Compare commits
2
Commits
d963224cb7
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c862e5dd78 | ||
|
|
34bae045c3 |
No files matched your search
@@ -2,6 +2,24 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented here.
|
All notable changes to this project will be documented here.
|
||||||
|
|
||||||
|
## 0.2.1 - 2026-09-23
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Detect the active Hyprland config provider and use Lua DPMS/focus dispatchers
|
||||||
|
for Lua sessions. Legacy hyprlang sessions retain their original commands.
|
||||||
|
- Log the failing refresh operation, exit status and Hyprland error response;
|
||||||
|
reject error responses even when hyprctl exits successfully.
|
||||||
|
- Add regression coverage for Lua, older Hyprland without `status`, and refresh
|
||||||
|
failures before and after the VT switch. Document the Lua startup hook.
|
||||||
|
|
||||||
|
### Validated
|
||||||
|
|
||||||
|
- 30 behavioral tests, Bash syntax checks and ShellCheck 0.11.0 passed.
|
||||||
|
- Full manual recovery completed on the desktop; the operator then confirmed
|
||||||
|
physical HDMI input switching and recovery on Hyprland 0.56.2 with Lua and
|
||||||
|
an iiyama PL3288UH on `HDMI-A-2`. See [validation details](docs/hardware-validation.md).
|
||||||
|
|
||||||
## 0.2.0 - 2026-09-23
|
## 0.2.0 - 2026-09-23
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -5,9 +5,13 @@ an Arch Linux / Hyprland workstation and back. A DRM hotplug event triggers a
|
|||||||
short switch away from the active graphical VT and back. There is no periodic
|
short switch away from the active graphical VT and back. There is no periodic
|
||||||
DPMS toggle or polling timer.
|
DPMS toggle or polling timer.
|
||||||
|
|
||||||
**Version 0.2.0.** The initial implementation and the review fixes are separate
|
**Version 0.2.1.** The initial implementation and the review fixes are separate
|
||||||
commits. See [the code review](docs/code-review.md) and [changelog](CHANGELOG.md).
|
commits. See [the code review](docs/code-review.md) and [changelog](CHANGELOG.md).
|
||||||
|
|
||||||
|
Physical HDMI input switching and recovery were confirmed working on
|
||||||
|
2026-09-23 with Hyprland 0.56.2 (Lua configuration) and an iiyama PL3288UH on
|
||||||
|
`HDMI-A-2`. See [validation results and remaining checks](docs/hardware-validation.md).
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
A systemd-based Linux desktop with Hyprland, a local `seat0` Wayland session,
|
A systemd-based Linux desktop with Hyprland, a local `seat0` Wayland session,
|
||||||
@@ -24,14 +28,16 @@ recovery. Hyprland must still expose the configured monitor over its IPC socket.
|
|||||||
|
|
||||||
## Install from the package
|
## Install from the package
|
||||||
|
|
||||||
Download `hyprland-hdmi-revive-0.2.0.tar.gz` and `SHA256SUMS` from the
|
Use `hyprland-hdmi-revive-0.2.1.tar.gz` and `SHA256SUMS` from a published
|
||||||
[Gitea release](https://gitea.jr-it-services.de/johannes/hyprland-hdmi-revive/releases).
|
[Gitea release](https://gitea.jr-it-services.de/johannes/hyprland-hdmi-revive/releases)
|
||||||
|
or [build the bundle from source](#build-and-validate). A Git push alone does not
|
||||||
|
publish installer bundles.
|
||||||
Run as your desktop user, **without sudo**:
|
Run as your desktop user, **without sudo**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sha256sum --ignore-missing -c SHA256SUMS
|
sha256sum --ignore-missing -c SHA256SUMS
|
||||||
tar -xzf hyprland-hdmi-revive-0.2.0.tar.gz
|
tar -xzf hyprland-hdmi-revive-0.2.1.tar.gz
|
||||||
cd hyprland-hdmi-revive-0.2.0
|
cd hyprland-hdmi-revive-0.2.1
|
||||||
./scripts/install.sh
|
./scripts/install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -92,7 +98,19 @@ uses `sudo -n` with command timeouts. A return failure gets one emergency retry.
|
|||||||
### Connect the Hyprland session
|
### Connect the Hyprland session
|
||||||
|
|
||||||
The user service manager does not automatically inherit a terminal's Hyprland
|
The user service manager does not automatically inherit a terminal's Hyprland
|
||||||
environment. Add this line to your Hyprland configuration (commonly
|
environment. For a Lua configuration, add this hook to your loaded Lua config:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
hl.on("hyprland.start", function()
|
||||||
|
hl.exec_cmd([[sh -c '
|
||||||
|
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||||
|
systemctl --user import-environment HYPRLAND_INSTANCE_SIGNATURE XDG_CONFIG_HOME &&
|
||||||
|
systemctl --user restart revive-monitor-drm-watch.service
|
||||||
|
']])
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
For a legacy hyprlang configuration, use this line instead (commonly in
|
||||||
`~/.config/hypr/hyprland.conf`):
|
`~/.config/hypr/hyprland.conf`):
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
@@ -124,6 +142,9 @@ and IPC guards prevent pre-login VT switching.
|
|||||||
A reported non-Hyprland desktop is excluded.
|
A reported non-Hyprland desktop is excluded.
|
||||||
- The helper independently verifies the requested VT and rechecks the session
|
- The helper independently verifies the requested VT and rechecks the session
|
||||||
after settling and permission checks.
|
after settling and permission checks.
|
||||||
|
- DPMS/focus commands follow the active config provider reported by
|
||||||
|
`hyprctl -j status`: Lua or legacy hyprlang. Older versions without this query
|
||||||
|
use legacy commands. A refresh error is logged before aborting recovery.
|
||||||
- A private `XDG_RUNTIME_DIR` is required. Locks prevent duplicate watchers and
|
- A private `XDG_RUNTIME_DIR` is required. Locks prevent duplicate watchers and
|
||||||
overlapping recoveries; there is no shared `/tmp` fallback.
|
overlapping recoveries; there is no shared `/tmp` fallback.
|
||||||
- Missing IPC, invalid monitor names, command failures and lost event streams
|
- Missing IPC, invalid monitor names, command failures and lost event streams
|
||||||
@@ -166,7 +187,9 @@ It creates a `.tar.gz` installer bundle, source ZIP, Base64 ZIP and `SHA256SUMS`
|
|||||||
be decoded on Windows with `certutil -decode <file.zip.b64> <file.zip>`.
|
be decoded on Windows with `certutil -decode <file.zip.b64> <file.zip>`.
|
||||||
|
|
||||||
Tests mock loginctl, Hyprland IPC, udev, sudo and systemd. They never switch the
|
Tests mock loginctl, Hyprland IPC, udev, sudo and systemd. They never switch the
|
||||||
real display. A hardware input-switch test after installation remains necessary.
|
real display. Version 0.2.1 passed 30 behavioral tests and a physical input-switch
|
||||||
|
test on the [documented setup](docs/hardware-validation.md). Test input switching
|
||||||
|
after installation on other hardware as well.
|
||||||
CI runs the shell checks and behavioral tests on a configured Gitea Actions runner.
|
CI runs the shell checks and behavioral tests on a configured Gitea Actions runner.
|
||||||
|
|
||||||
See [architecture](docs/architecture.md), [troubleshooting](docs/troubleshooting.md)
|
See [architecture](docs/architecture.md), [troubleshooting](docs/troubleshooting.md)
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ active graphical VT. It is intended for one local Hyprland session on `seat0`.
|
|||||||
timestamps are not read from shared files or affected by wall-clock changes.
|
timestamps are not read from shared files or affected by wall-clock changes.
|
||||||
4. The synchronous recovery helper locks, independently identifies the active
|
4. The synchronous recovery helper locks, independently identifies the active
|
||||||
session, waits for HDMI settling, checks the Hyprland IPC output and refreshes
|
session, waits for HDMI settling, checks the Hyprland IPC output and refreshes
|
||||||
DPMS/focus. The instance signature comes from the Hyprland startup hook.
|
DPMS/focus. `hyprctl -j status` selects Lua or legacy hyprlang dispatch syntax;
|
||||||
|
versions without the query use legacy commands. Dispatches must return `ok`
|
||||||
|
with exit status zero, otherwise their error is logged and recovery aborts.
|
||||||
|
The instance signature comes from the Hyprland startup hook.
|
||||||
5. Before switching, the helper rechecks session identity and VT and checks sudo
|
5. Before switching, the helper rechecks session identity and VT and checks sudo
|
||||||
permission for both directions. It rechecks session identity after those calls.
|
permission for both directions. It rechecks session identity after those calls.
|
||||||
6. The helper sets an EXIT/signal trap, switches to VT2 (when graphical VT=1) or
|
6. The helper sets an EXIT/signal trap, switches to VT2 (when graphical VT=1) or
|
||||||
|
|||||||
+21
-1
@@ -41,4 +41,24 @@ kann einen echten schnellen Rückwechsel unterdrücken. Das bleibt eine bewusste
|
|||||||
Einschränkung dieses hardwareabhängigen Workarounds und ist in README beschrieben.
|
Einschränkung dieses hardwareabhängigen Workarounds und ist in README beschrieben.
|
||||||
|
|
||||||
Ein realer Test von HDMI-Eingangswechsel, Ab-/Anmeldung und Boot-Verhalten wurde
|
Ein realer Test von HDMI-Eingangswechsel, Ab-/Anmeldung und Boot-Verhalten wurde
|
||||||
in diesem Review nicht ausgeführt. Er bleibt nach Installation erforderlich.
|
im ursprünglichen Review für 0.2.0 nicht ausgeführt. Der anschließende Praxistest
|
||||||
|
für 0.2.1 ist im folgenden Nachtrag dokumentiert.
|
||||||
|
|
||||||
|
## Nachtrag: Lua-Korrektur und Praxistest für 0.2.1
|
||||||
|
|
||||||
|
Die Prüfung am Desktop zeigte, dass Hyprland 0.56.2 mit Lua-Konfiguration die
|
||||||
|
alten `dispatch dpms on`-/`dispatch focusmonitor`-Befehle ablehnt. Version 0.2.0
|
||||||
|
brach dadurch vor dem VT-Wechsel ab. Die bisherigen Mocks hatten beliebige
|
||||||
|
Dispatch-Aufrufe akzeptiert und diesen Fehler nicht erkannt.
|
||||||
|
|
||||||
|
Version 0.2.1 erkennt den aktiven Konfigurationsanbieter über `hyprctl -j status`
|
||||||
|
und verwendet die passenden Lua- oder hyprlang-Befehle. Die Mocks prüfen nun die
|
||||||
|
Befehlssyntax; fehlgeschlagene Refresh-Aufrufe protokollieren Operation,
|
||||||
|
Exitstatus und Fehlerantwort. Alle 30 Verhaltenstests sowie Bash-Syntaxprüfung
|
||||||
|
und ShellCheck 0.11.0 waren erfolgreich.
|
||||||
|
|
||||||
|
Der vollständige manuelle Recovery-Lauf am Desktop kehrte auf die grafische VT1
|
||||||
|
zurück und meldete `Recovery completed for HDMI-A-2`. Anschließend bestätigte
|
||||||
|
der Betreiber am 23.09.2026 den erfolgreichen physischen HDMI-Eingangswechsel.
|
||||||
|
Testumgebung und noch nicht separat bestätigte Prüfungen sind unter
|
||||||
|
[Hardware validation](hardware-validation.md) festgehalten.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# Hardware validation
|
||||||
|
|
||||||
|
## Confirmed on 2026-09-23
|
||||||
|
|
||||||
|
Version 0.2.1 was installed and the systemd user watcher restarted on the
|
||||||
|
following setup:
|
||||||
|
|
||||||
|
| Component | Tested configuration |
|
||||||
|
| --- | --- |
|
||||||
|
| Desktop | Arch Linux, Hyprland 0.56.2 |
|
||||||
|
| Configuration provider | Lua (`hyprctl -j status`) |
|
||||||
|
| Session | Local Wayland session on seat0, graphical VT1 |
|
||||||
|
| Display | iiyama PL3288UH, 3840 × 2160 at 60 Hz |
|
||||||
|
| Output | `HDMI-A-2` |
|
||||||
|
| Timing | 5 s HDMI settle, 1 s temporary VT, 2 s return settle, 30 s cooldown |
|
||||||
|
|
||||||
|
The original failure was reproduced: legacy DPMS/focus commands returned Lua
|
||||||
|
syntax errors and stopped recovery before the VT switch. Both corrected Lua
|
||||||
|
commands returned `ok` on the running compositor.
|
||||||
|
|
||||||
|
After installation, a complete manual recovery returned to graphical VT1 and
|
||||||
|
reported:
|
||||||
|
|
||||||
|
```text
|
||||||
|
revive-monitor-fix: Recovery started for HDMI-A-2 on VT 1
|
||||||
|
revive-monitor-fix: Using lua dispatch syntax
|
||||||
|
revive-monitor-fix: VT refresh: 2 -> 1
|
||||||
|
revive-monitor-fix: Recovery completed for HDMI-A-2
|
||||||
|
```
|
||||||
|
|
||||||
|
The watcher remained active. The operator then tested physical HDMI input
|
||||||
|
switching and confirmed that recovery worked successfully.
|
||||||
|
|
||||||
|
## Automated validation
|
||||||
|
|
||||||
|
- All 30 behavioral tests passed, including Lua and legacy dispatch syntax,
|
||||||
|
refresh failures, session guards, VT return handling and installer behavior.
|
||||||
|
- Bash syntax checks and ShellCheck 0.11.0 passed.
|
||||||
|
- Installer archive and ZIP contents matched; ZIP integrity, Base64 round-trip
|
||||||
|
and release checksums were verified.
|
||||||
|
|
||||||
|
## Scope and remaining checks
|
||||||
|
|
||||||
|
This confirms the normal HDMI input-switch path on the setup above. It does
|
||||||
|
not establish compatibility with every monitor or GPU.
|
||||||
|
|
||||||
|
The operator had reported that the earlier unwanted tty switching was gone.
|
||||||
|
A dedicated cold-boot/pre-login check and logout/login cycle after the 0.2.1
|
||||||
|
update have not been separately confirmed. Rapid away/back switching inside
|
||||||
|
the cooldown also remains a separate test; such events can be suppressed by
|
||||||
|
design. See [the hardware acceptance procedure](troubleshooting.md#hardware-acceptance).
|
||||||
@@ -36,6 +36,22 @@ Expected: your numeric UID, `Type=wayland`, `Active=yes`, `Seat=seat0`, a VT fro
|
|||||||
1 to 63, and either a Hyprland desktop or an empty Desktop property. The helper
|
1 to 63, and either a Hyprland desktop or an empty Desktop property. The helper
|
||||||
also checks these properties when invoked manually.
|
also checks these properties when invoked manually.
|
||||||
|
|
||||||
|
## Hyprland refresh failed / Lua syntax error
|
||||||
|
|
||||||
|
Version 0.2.0 sent legacy dispatch commands even to Lua-configured sessions.
|
||||||
|
On Hyprland 0.56.2 this produced `')' expected near 'on'` and aborted recovery
|
||||||
|
before the VT switch. Update to 0.2.1 with `./scripts/install.sh --start`.
|
||||||
|
|
||||||
|
The helper now reads `hyprctl -j status` to choose the active config provider.
|
||||||
|
For Lua it sends `hl.dsp.dpms({ action = "on", monitor = "HDMI-A-2" })` and
|
||||||
|
`hl.dsp.focus({ monitor = "HDMI-A-2" })` through `hyprctl dispatch`. The configured
|
||||||
|
output replaces `HDMI-A-2`. Legacy hyprlang and older versions remain supported.
|
||||||
|
See the official [hyprctl documentation](https://wiki.hypr.land/configuring/core/advanced-configuration/using-hyprctl/)
|
||||||
|
and [dispatchers](https://wiki.hypr.land/configuring/core/dispatchers/).
|
||||||
|
|
||||||
|
The journal identifies `DPMS` or `focus`, the command's exit status and its error
|
||||||
|
response. A zero exit status with an error response is also treated as failure.
|
||||||
|
|
||||||
## Invalid runtime directory
|
## Invalid runtime directory
|
||||||
|
|
||||||
`XDG_RUNTIME_DIR` must be your login-managed, user-owned directory with mode 0700
|
`XDG_RUNTIME_DIR` must be your login-managed, user-owned directory with mode 0700
|
||||||
@@ -84,6 +100,15 @@ hook manually as documented in README.
|
|||||||
|
|
||||||
## Hardware acceptance
|
## Hardware acceptance
|
||||||
|
|
||||||
|
The operator confirmed successful physical HDMI input switching and recovery
|
||||||
|
with version 0.2.1 on 2026-09-23. The tested setup and the scope of that result
|
||||||
|
are recorded in [hardware validation](hardware-validation.md).
|
||||||
|
|
||||||
After installation, check a normal input change, a rapid away/back change and a
|
After installation, check a normal input change, a rapid away/back change and a
|
||||||
logout/login cycle. Before Hyprland login there must be no chvt invocation from
|
logout/login cycle. Before Hyprland login there must be no chvt invocation from
|
||||||
this service. Automated tests use mocks and do not replace this hardware check.
|
this service. Automated tests use mocks and do not replace this hardware check.
|
||||||
|
|
||||||
|
For a first normal input-switch test with the default timing, stay on the other
|
||||||
|
input for about 40 seconds before returning. This allows the initial attempt
|
||||||
|
and its 30-second cooldown to finish. Then test rapid switching separately;
|
||||||
|
returns inside the cooldown can still be suppressed.
|
||||||
@@ -44,9 +44,44 @@ monitor_present() {
|
|||||||
jq -e --arg name "$MONITOR" 'any(.[]; .name == $name)' >/dev/null 2>&1
|
jq -e --arg name "$MONITOR" 'any(.[]; .name == $name)' >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select_dispatch_syntax() {
|
||||||
|
local status provider
|
||||||
|
DISPATCH_SYNTAX=hyprlang
|
||||||
|
# The active config provider, not the version, determines dispatch syntax.
|
||||||
|
# Older Hyprland versions do not expose `status`; their dispatch is legacy.
|
||||||
|
if status="$(timeout 5 hyprctl -j status 2>/dev/null)" &&
|
||||||
|
provider="$(jq -er '.configProvider | select(type == "string")' <<< "$status" 2>/dev/null)"; then
|
||||||
|
case "$provider" in
|
||||||
|
lua|hyprlang) DISPATCH_SYNTAX="$provider" ;;
|
||||||
|
*) log "Unsupported Hyprland config provider: $provider"; return 1 ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
log 'Hyprland config provider unavailable; trying legacy dispatch syntax'
|
||||||
|
fi
|
||||||
|
log "Using $DISPATCH_SYNTAX dispatch syntax"
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_checked() {
|
||||||
|
local action="$1" reply status
|
||||||
|
shift
|
||||||
|
reply="$(timeout 5 hyprctl dispatch "$@" 2>&1)"
|
||||||
|
status=$?
|
||||||
|
# Some hyprctl versions return exit 0 even for an IPC error response.
|
||||||
|
if (( status != 0 )) || [[ "$reply" != ok ]]; then
|
||||||
|
log "Hyprland $action failed (exit $status): ${reply:-no response}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
refresh_monitor() {
|
refresh_monitor() {
|
||||||
timeout 5 hyprctl dispatch dpms on "$MONITOR" >/dev/null 2>&1 &&
|
if [[ "$DISPATCH_SYNTAX" == lua ]]; then
|
||||||
timeout 5 hyprctl dispatch focusmonitor "$MONITOR" >/dev/null 2>&1
|
# MONITOR is restricted to alphanumeric characters, underscores and '-'.
|
||||||
|
dispatch_checked DPMS "hl.dsp.dpms({ action = \"on\", monitor = \"$MONITOR\" })" &&
|
||||||
|
dispatch_checked focus "hl.dsp.focus({ monitor = \"$MONITOR\" })"
|
||||||
|
else
|
||||||
|
dispatch_checked DPMS dpms on "$MONITOR" &&
|
||||||
|
dispatch_checked focus focusmonitor "$MONITOR"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
log "Recovery started for $MONITOR on VT $GRAPHICAL_VT"
|
log "Recovery started for $MONITOR on VT $GRAPHICAL_VT"
|
||||||
@@ -55,6 +90,7 @@ if ! monitor_present; then
|
|||||||
log "Hyprland IPC unavailable or configured output $MONITOR absent - recovery aborted"
|
log "Hyprland IPC unavailable or configured output $MONITOR absent - recovery aborted"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
select_dispatch_syntax || exit 1
|
||||||
if ! refresh_monitor; then
|
if ! refresh_monitor; then
|
||||||
log 'Hyprland refresh failed - recovery aborted'
|
log 'Hyprland refresh failed - recovery aborted'
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -32,8 +32,35 @@ elif name == 'hyprctl':
|
|||||||
print('invalid')
|
print('invalid')
|
||||||
else:
|
else:
|
||||||
print(json.dumps([{'name': os.environ.get('OUTPUT', 'HDMI-A-2')}]))
|
print(json.dumps([{'name': os.environ.get('OUTPUT', 'HDMI-A-2')}]))
|
||||||
|
elif args == ['-j', 'status']:
|
||||||
|
if os.environ.get('OLD_HYPRLAND'):
|
||||||
|
print('unknown request')
|
||||||
|
sys.exit(int(os.environ.get('STATUS_EXIT', '0')))
|
||||||
|
print(json.dumps({'configProvider': os.environ.get('CONFIG_PROVIDER', 'hyprlang')}))
|
||||||
|
elif args[0] == 'dispatch':
|
||||||
|
monitor = os.environ.get('OUTPUT', 'HDMI-A-2')
|
||||||
|
if os.environ.get('CONFIG_PROVIDER') == 'lua':
|
||||||
|
commands = {
|
||||||
|
('dispatch', f'hl.dsp.dpms({{ action = "on", monitor = "{monitor}" }})'): 'DPMS',
|
||||||
|
('dispatch', f'hl.dsp.focus({{ monitor = "{monitor}" }})'): 'focus',
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
|
commands = {
|
||||||
|
('dispatch', 'dpms', 'on', monitor): 'DPMS',
|
||||||
|
('dispatch', 'focusmonitor', monitor): 'focus',
|
||||||
|
}
|
||||||
|
action = commands.get(tuple(args))
|
||||||
|
if action is None:
|
||||||
|
print('error: invalid dispatch syntax')
|
||||||
|
sys.exit(7)
|
||||||
|
if os.environ.get('DISPATCH_FAIL') == action and (
|
||||||
|
not os.environ.get('FINAL_REFRESH_FAIL') or (state / 'away').exists()):
|
||||||
|
print(f'error: {action} rejected for {monitor}')
|
||||||
|
sys.exit(int(os.environ.get('DISPATCH_EXIT', '7')))
|
||||||
print('ok')
|
print('ok')
|
||||||
|
else:
|
||||||
|
print('unexpected hyprctl call', file=sys.stderr)
|
||||||
|
sys.exit(99)
|
||||||
elif name == 'sleep':
|
elif name == 'sleep':
|
||||||
if os.environ.get('CHANGE_SESSION'):
|
if os.environ.get('CHANGE_SESSION'):
|
||||||
(state / 'inactive').touch()
|
(state / 'inactive').touch()
|
||||||
@@ -105,6 +132,51 @@ class RecoveryTests(unittest.TestCase):
|
|||||||
self.assertEqual(self.switches(), ['2', '1'])
|
self.assertEqual(self.switches(), ['2', '1'])
|
||||||
self.assertIn('Recovery completed', r.stderr)
|
self.assertIn('Recovery completed', r.stderr)
|
||||||
|
|
||||||
|
def test_lua_session_refreshes_before_and_after_vt_switch(self):
|
||||||
|
r = self.run_script(CONFIG_PROVIDER='lua')
|
||||||
|
self.assertEqual(r.returncode, 0, r.stderr)
|
||||||
|
self.assertEqual(self.switches(), ['2', '1'])
|
||||||
|
self.assertIn('Using lua dispatch syntax', r.stderr)
|
||||||
|
dispatches = [a for a in self.calls('hyprctl') if a[0] == 'dispatch']
|
||||||
|
self.assertEqual(dispatches, [
|
||||||
|
['dispatch', 'hl.dsp.dpms({ action = "on", monitor = "HDMI-A-2" })'],
|
||||||
|
['dispatch', 'hl.dsp.focus({ monitor = "HDMI-A-2" })'],
|
||||||
|
] * 2)
|
||||||
|
|
||||||
|
def test_legacy_without_status_command(self):
|
||||||
|
for code in ('0', '1'):
|
||||||
|
with self.subTest(code=code):
|
||||||
|
r = self.run_script(OLD_HYPRLAND='1', STATUS_EXIT=code)
|
||||||
|
self.assertEqual(r.returncode, 0, r.stderr)
|
||||||
|
self.assertIn('Using hyprlang dispatch syntax', r.stderr)
|
||||||
|
|
||||||
|
def test_unknown_config_provider_aborts(self):
|
||||||
|
r = self.run_script(CONFIG_PROVIDER='unknown')
|
||||||
|
self.assertNotEqual(r.returncode, 0)
|
||||||
|
self.assertIn('Unsupported Hyprland config provider', r.stderr)
|
||||||
|
self.assertEqual(self.switches(), [])
|
||||||
|
|
||||||
|
def test_dispatch_errors_reported_before_switch(self):
|
||||||
|
for provider in ('lua', 'hyprlang'):
|
||||||
|
for action in ('DPMS', 'focus'):
|
||||||
|
for code in ('0', '7', '124'):
|
||||||
|
with self.subTest(provider=provider, action=action, code=code):
|
||||||
|
r = self.run_script(CONFIG_PROVIDER=provider,
|
||||||
|
DISPATCH_FAIL=action, DISPATCH_EXIT=code)
|
||||||
|
self.assertNotEqual(r.returncode, 0)
|
||||||
|
self.assertIn(f'Hyprland {action} failed (exit {code})', r.stderr)
|
||||||
|
self.assertIn(f'error: {action} rejected for HDMI-A-2', r.stderr)
|
||||||
|
self.assertNotIn('Recovery completed', r.stderr)
|
||||||
|
self.assertEqual(self.switches(), [])
|
||||||
|
|
||||||
|
def test_final_lua_refresh_failure_not_reported_as_success(self):
|
||||||
|
r = self.run_script(CONFIG_PROVIDER='lua', DISPATCH_FAIL='focus', FINAL_REFRESH_FAIL='1')
|
||||||
|
self.assertNotEqual(r.returncode, 0)
|
||||||
|
self.assertEqual(self.switches(), ['2', '1'])
|
||||||
|
self.assertIn('Final Hyprland refresh failed', r.stderr)
|
||||||
|
self.assertIn('error: focus rejected for HDMI-A-2', r.stderr)
|
||||||
|
self.assertNotIn('Recovery completed', r.stderr)
|
||||||
|
|
||||||
def test_invalid_vt(self):
|
def test_invalid_vt(self):
|
||||||
for vt in ('', '0', '64', '01', 'word', '99999999999999999'):
|
for vt in ('', '0', '64', '01', 'word', '99999999999999999'):
|
||||||
with self.subTest(vt=vt):
|
with self.subTest(vt=vt):
|
||||||
|
|||||||
Reference in new issue
Block a user