Code around sleep was written into the service manager's sleep units by the module that needed it, and the mesh could not tell a sleeping machine from a lost one. power runs every module's code for the six moments, each piece bounded, owns logind's power handling from its settings, and says booted, sleeping, woke, shutting-down and the power source on the bus, sleeping under logind's delay lock before the machine sleeps.
29 KiB
asus-zephyrus-g14
The hardware module for the ASUS ROG Zephyrus G14 laptop: its vendor daemon and platform profiles, the hybrid GPU's mode and driver options, suspend, the lid and power key, low battery, the backlights, the vendor keys and the touchpad (novox/hq research 027/03 Power management on the laptop, research 026/05, to-be 42 phase 3).
Why this name
A module is named after the hardware model, never the node (novox/hq ADR 0112; research 026/03:
no flavors, no machine names). asus-zephyrus-g14 is the model family exactly as the firmware
reports it (/sys/class/dmi/id/product_family = ROG Zephyrus G14). The module's code checks that
value and its switcher does nothing on any other model, and zephyrus_check reports it.
A wider name such as asus-rog-laptop would promise what this module cannot keep. Its contents
belong to this family: the vendor-key scan codes, the eDP panel beside an NVIDIA dGPU, and the NVIDIA
D3 workaround. A second G14 is assigned the same module. Another ROG model gets its own.
Written against the GA403 (2024, Ryzen 8945HS, RTX 4070 Laptop, hybrid). Older G14 years have the same daemons and probably the same keys. Their GPU options are unverified.
What it owns
| what | how | |
|---|---|---|
| package | asusctl (asusd + client) |
the distribution's package (extra). The machine was found with a local build of 6.4.0. The host only asserts present, so the switch to 6.5.0 from extra happens at the next pacman -Syu (or pacman -S asusctl). zephyrus_check flags a local build |
| package | upower, playerctl |
what the low-battery drop-in and the media keys use. xinput is the xorg module's (one package, one module on a node) |
| service | asusd running (static unit: no boot state to declare), supergfxd running and enabled |
|
| archive | /usr/local/lib/asus-zephyrus-g14/bin/ |
the module's scripts, from files/bin (below) |
| file ×2 | ~/.config/i3/config.d/10-asus.conf, 20-g14.conf |
the laptop's lines in i3: the keys the firmware sends as ordinary presses (Fn+F6, Fn+F9), the keyboard-backlight notifier, the panel as primary, the touchpad key. The paths are adopted, because a second file binding the same keys makes i3's configuration check fail, and the i3 module's watcher then reloads nothing |
| file ×4 | asus-zephyrus-g14-touchpad-resume.service, and a drop-in asus-zephyrus-g14-touchpad.conf on each sleep service |
the touchpad's settings once more after a resume (below) |
| file | /etc/modprobe.d/g14-nvidia-power.conf |
NVreg_DynamicPowerManagement=0x00 (runtime D3 off: the ACPI D-Notifier hang) and NVreg_PreserveVideoMemoryAllocations=1. The path is adopted (ADR 0182) |
| file | /etc/modprobe.d/video-brightness-switch.conf |
video.brightness_switch_enabled=0, so the ACPI video driver does not also move a backlight on the keys. The file was on the machine and owned by nothing |
| file ×3 | systemd-{suspend,hibernate,suspend-then-hibernate}.service.d/asus-zephyrus-g14-nvidia.conf |
Wants= the matching nvidia-* sleep units and nvidia-resume (see suspend units below) |
| file | nvidia-powerd.service.d/asus-zephyrus-g14.conf |
ConditionKernelCommandLine=zephyrus.nvidia-powerd: Dynamic Boost runs only when the operator opts in at boot |
| file | /etc/systemd/logind.conf.d/power.conf |
the power key and the lid suspend, on battery, on mains and docked. systemd-logind is reloaded, never restarted |
| file | /etc/udev/rules.d/90-backlight.rules |
backlights writable by the video group. systemd-udevd is reloaded |
| file | triggerhappy.service.d/asus-zephyrus-g14.conf |
thd … --user ${machine:account}: the triggers run as the operator's account (below) |
| file | /etc/triggerhappy/triggers.d/asus-g14.conf |
the vendor keys: media (KEY_PROG1/3/4), panel brightness, touchpad (KEY_F21). The path is adopted, because two trigger files would fire every key twice |
| file | /etc/UPower/UPower.conf.d/50-asus-zephyrus-g14.conf |
low battery at 15/10/7 %; at 7 % suspend, not power off. A drop-in over the package's own file |
| file | /etc/X11/xorg.conf.d/30-asus-zephyrus-g14-touchpad.conf |
tap to click, natural scrolling, acceleration 0.15, as an X input class |
What it does not own, on purpose:
/etc/asusd/*.ronbelong to asusd, which rewrites them whenever a setting changes. RON is not a format the host writes into (ADR 0102 speaks JSON and marked blocks). Owning the file whole would repeat the predecessor's freeze: the measured file already differs from the one the predecessor shipped. The settings the module needs are set through asusd, by its code (below)./etc/supergfxd.confand/etc/modprobe.d/supergfxd.confbelong to supergfxd, which writes both.- The swap file, its unit and the swap partition are the machine's swap layout (research 027,
question 3). They are not this module's, nor
memory-pressure's. - Places. The screen layouts for named places (
$mod+Alt+1…7,~/.screenlayout/,~/scripts/.screenlayouts/@*.sh) name where the operator works, which no module may (ADR 0112). They are the operator's own lines until autorandr profiles replace them (thexorgmodule). - The monitor-hotplug wizard (
/etc/udev/rules.d/91-monitor-hotplug.rules,~/scripts/.screenlayouts/monitor-wizard.sh) is any laptop's, not this model's. The predecessor said so itself (itslaptopflavor). It is the display server's to replace with autorandr's own hotplug handling. Until then it stays as found, and it still calls the predecessor'sas-user. - The screenshot script (
~/.config/i3/scripts/screenshot.sh) is any machine's. Thei3module binds it too. This module only binds the key the firmware sends for it. - The bar's battery block. It belongs here (a block that follows this model's hardware), but the
bar has no way in yet (
i3status-rustREADME). Once ADR 0210's contributions reach the bar seat, this module contributes it.
Requires x11-display. The i3 lines and the session scripts need a display, so the module is
assigned where the display server is.
Software outside the distribution (ADR 0205, research 027 question 1)
supergfxctl (5.2.7, from the asus-linux repository, which is no longer configured) and
triggerhappy (AUR) are kept as found, and depended on. The module declares no package for
either, because the host installs from the official repositories only. It declares their services
(supergfxd running, triggerhappy running), so on a machine without them the host refuses the
service by name: does not exist on this machine. The refusal is loud, never a silent pass.
zephyrus_check names both as foreign.
This module does not choose between the options of research 027 question 1. Under the starting
position (P2: the build machine builds AUR packages into a repository the mesh serves), both become
package resources here, and a fresh G14 installs them. Until P2 exists, a fresh G14 is blocked
on installing these two by hand. ADR 0205's vendored archive (P1) does not fit: supergfxctl is a
daemon with a system-bus policy and udev rules, and triggerhappy is C.
A later option for the keys: the module's own Go code could read the vendor keys from evdev, which
the operator's account may do through the input group. That would retire triggerhappy entirely.
It is not done here, because it would put the keys behind the node's runtime, and the runtime
restarts a bundle that dies only on its next call (below).
The long-running code: the profile switcher (ADR 0198)
The module's Go bundle serves the tools and runs the platform-profile switcher in the same process.
The node's runtime launches the bundle at the runtime's start. It replaces the predecessor's
auto-profile, a user unit that woke every five seconds, on battery too.
- Policy (constants until settings exist, issue 168): battery →
Quiet; mains →Balanced; mains with the CPU at or above 50 % for 3 samples of 10 s →Performance, back toBalancedafter 3 samples at or below 20 %. Between the lines nothing moves (hysteresis). iowait counts as idle. - Woken by events, not a poll. The kernel's power-supply uevents (netlink, group 1) wake the
switcher. Any account may listen on that group, and it needs no daemon, bus client or dependency;
upower re-announces the same changes but would need a D-Bus client in the bundle. The CPU is
sampled only on mains, every 10 s, because only there does the answer depend on it. On battery,
a safety re-read every 5 minutes covers an event lost across a suspend. If the uevent socket
cannot be opened, the switcher polls every 10 s and says so in
zephyrus_profile_policy. - The battery decides the source. A battery that is discharging means battery, whatever any
adapter says. The predecessor took any
onlinefile reading 1 as mains, and on this model the USB-C ports reportonline. Batteries ofscope=Device(a mouse, a headset) are ignored. - It acts on a change of its decision, never to restore one. A profile someone chose by hand (the
profile key, asusctl,
zephyrus_profile) stays until the power source changes or the load crosses a line. The predecessor re-asserted its choice every five seconds, which made the profile key useless. Starting is not a decision: the runtime restarts the bundle on every push that changes one, and a push must not reset the operator's profile. - A hold.
zephyrus_profileholds the profile it sets for 60 min (hold_minutes). A change of power source ends the hold. - One assertion at start: through asusctl, the charge limit (80 %) and asusd's own on-mains and
on-battery profiles (
Balanced,Quiet), each read first and set only if it differs. asusd's own switching on a change of power source then agrees with the switcher's. A limit set later withzephyrus_charge_limitstands until the bundle next starts. For a one-off full charge, use itsoneshot. - Events:
profile.switched(profile,from,reason,source), published through the runtime.
No root is involved. asusd's and supergfxd's bus policies admit the users and wheel groups, and the
runtime runs as the operator's account. The one write that may escalate is the panel's backlight,
when the udev rule has not run yet. It uses sudo -n and never prompts. Every command is bounded at
20 s.
Known limit. The runtime restarts a launched bundle that exits on its next tool call, not at
once (mesh-tools launch.ts), so a crashed switcher stays down until a tool is called. ADR 0198 §1
says started again when it exits. The switcher recovers from a panic and reports it in
zephyrus_profile_policy and zephyrus_check, but a crash of the process is the runtime's to restart.
The vendor keys and the scripts
triggerhappy opens the input devices as root, then drops to the operator's account with its groups
(initgroups: input, video). The packaged unit already drops to nobody, and the module's drop-in
names the account instead. The predecessor replaced the packaged unit with one that ran every trigger
as root, then su-ed to a named person with a hard-coded uid and display, and sourced a file of
secrets on the way (research 027 question 2). Now:
zephyrus-session CMD…: runs a command in the account's graphical session. It sets the account's own bus (/run/user/<uid>/bus) and takes the display and its authority from the session's window manager's own environment, as the desktop modules' session finder does. If i3 is not running, it asks logind, and then any process of the account that has a display. Nothing is sourced. triggerhappy's--userchanges the user and its groups and nothing else: the triggers start with the service's bare environment, which is why every key that needs the session goes through this.zephyrus-media play-pause | next | previous: the media keys through MPRIS, with a notification of what happened and a lock against the key's own repeat.zephyrus-display primary | order: the internal panel as the primary output, and the display key's workspace split (odd workspaces on the panel, even ones on the first external output). The panel is found as the connectedeDPoutput. The predecessor namedeDP-1and usedjq. This reads i3's answer without it.zephyrus-kbd-notify: the keyboard backlight's level, shown when UPower says it changed. One instance per session, because i3 runs itsexeclines again on an in-place restart.zephyrus-backlight + | - | N: the panel in 5 % steps, never below 1 %. The panel is the backlight under the eDP connector, because this model also registersnvidia_0, which moves nothing. The predecessor namedamdgpu_bl1literally.zephyrus-notify ID TEXT: one replacing notification, throughbusctl(the service manager's client, so no libnotify).zephyrus-touchpad reset | toggle: bound to the touchpad key (KEY_F21).
Media keys go to MPRIS through playerctl. The predecessor's fallback to a media server's local
API needed a token from the secrets file, and is dropped until a module can be handed a secret
(research 027 question 2). A player that does not speak MPRIS is told as "Media: no player".
The touchpad: an input class instead of a sleep hook
The predecessor re-ran xinput from /etc/systemd/system-sleep/ after every resume, as a named person
on a guessed display, because settings made with xinput are lost when the device initialises again.
An X input class is applied by X every time the device appears: at login, on hotplug and after a
resume. So the cause is fixed. The class matches any touchpad on the machine, which is the model's, so it
holds across G14 years whose touchpads differ. It takes effect at the next X start.
zephyrus-touchpad reset stays as the manual form, on the touchpad key and $mod+Shift+x.
And a backstop after resume. A resume that does not initialise the device again does not make X
apply the class either, and the predecessor's i3 file says the touchpad "sometimes needs re-init after
sleep". So asus-zephyrus-g14-touchpad-resume.service runs zephyrus-touchpad reset as the account,
two seconds after the machine is awake. It is never enabled. Each sleep service Wants= it through a
drop-in, and it is ordered After= them, which is how nvidia-resume is started too.
zephyrus_check says whether the sleep wants it.
Suspend units without enabling them
nvidia-suspend, -hibernate, -suspend-then-hibernate and -resume are enabled with links in the
sleep services' .wants directories. The mesh makes no links (ADR 0012). The host's service shape
cannot declare them either: it may only say running or stopped, and running on a one-shot that
last failed would start nvidia-sleep.sh suspend with the machine awake. So the module asks for them
from the other side: a drop-in on each sleep service that Wants= them. The units' own
Before=/After= order them. The found links stay and are harmless.
suspend-then-hibernate now also gets nvidia-suspend-then-hibernate, which the machine lacked.
The drop-ins take effect at the service manager's next daemon-reload. In the same apply, the restart
of triggerhappy (whose drop-in changes) performs one.
Tools
| tool | r/a | what |
|---|---|---|
zephyrus_brightness |
r/a | panel (percent or ±step, floor 1 %) and keyboard (off/low/med/high, 0-3, ±) through asusd |
zephyrus_battery |
r | charge, energy in Wh, health (full ÷ design), cycles (the firmware reports 0, and this is said), limit, watts, hours left |
zephyrus_charge_limit |
r/a | 20-100 through asusd; oneshot |
zephyrus_gpu_mode |
r/a | mode, supported modes, dGPU power, the pending mode and action; says that asusd switches the mode on every change of power source |
zephyrus_profile |
r/a | active, on-mains and on-battery profile, kernel platform profile; set with a hold |
zephyrus_thermals |
r | every hwmon temperature and fan, the hottest, the dGPU's temperature only when it is awake (nvidia-smi wakes a suspended GPU) |
zephyrus_power_draw |
r | battery flow, APU package power (PPT), dGPU draw when awake, power source and why |
zephyrus_profile_policy |
r | what the switcher would choose now and why: source, recent load against the thresholds, decision, hold, last switch, what woke it, what it asserted at start, and whether the predecessor's switcher still runs |
zephyrus_fan_curves |
r | asusd's curves per profile and fan |
zephyrus_keys |
r | every custom key: each triggerhappy trigger, the module's i3 lines and the keys the firmware handles; what runs and the file it is in. Warns when one key is in two trigger files, which fires it twice |
zephyrus_check |
r | every expectation: model, packages (local or foreign), daemons, nvidia-powerd, sleep units, the NVIDIA options in force (/proc/driver/nvidia/params), charge limit, one authority each over the profile and the GPU mode, predecessor leftovers; the touchpad resume unit wanted by the sleep; triggerhappy running as the operator's account; any trigger, udev rule or sleep hook still naming as-user. It also lists what it did not check |
profile is a candidate verb for a future node-power-profile seat (research 027/03). That seat has
no record yet, so this is the module's own tool.
Found on the laptop, 2026-10-04 (read-only)
- Two authorities over the GPU mode.
asusd.ronhasac_command: "supergfxctl -m Hybrid"andbat_command: "supergfxctl -m Integrated", so asusd switches the GPU mode on every change of power source. supergfxd 5.2.7 cannot read logind's sessions (manager is an invalid variant, every boot), so a switch that needs a logout times out.zephyrus_checkreports both. The fix is the operator's, in asusd's file: clear both commands, or update supergfxctl once it can be packaged. brightness.confdid nothing.HandleBrightnessKeyis not a logind key, and logind logs Unknown key … ignoring at every start. The module does not carry it. The brightness keys were always triggerhappy's, with the ACPI video switch off.- Two profile switchers would run at once until
auto-profileis stopped (below). - asusctl is a local build (6.4.0, Unknown Packager) beside a foreign
asusctl-debug.
When assigned to the laptop: what changes
/usr/local/lib/asus-zephyrus-g14/appears (seven scripts).- Written over found files (each original kept once by the host):
g14-nvidia-power.confandvideo-brightness-switch.conf(same options, so no change until the next boot either),logind.conf.d/power.conf(same keys; logind reloaded),90-backlight.rules(same effect; udevd reloaded),triggers.d/asus-g14.conf(now the module's scripts), and i3's10-asus.confand20-g14.conf(the module's lines; thei3module's watcher checks and reloads them). - New: the three sleep drop-ins (behaviour gained:
nvidia-suspend-then-hibernate), the nvidia-powerd drop-in (no effect while it is masked), the triggerhappy drop-in, the UPower drop-in (same values as today), the touchpad input class (at the next X start), and the resume unit with its three drop-ins. daemon-reloadand atriggerhappyrestart. thd now runs as the account and the keys run the module's scripts.upowerrestarts.- Packages, asusd and supergfxd: already as declared, so nothing changes. asusctl stays the local 6.4.0 until the next upgrade.
- The node runtime restarts with the new bundle. The switcher asserts the limit (80, already) and asusd's profiles (Balanced and Quiet, already), so it sets nothing. It takes the current decision as applied and acts from the first event on.
Predecessor files this module makes redundant — the operator removes them once (ADR 0182)
On the laptop:
systemctl --user disable --now auto-profile.service, then delete~/.config/systemd/user/auto-profile.serviceand~/scripts/auto-profile. Do this right after the push, or two switchers run at once.- Before the push, keep the place layouts. The module writes
20-g14.confover the found file, and the found file carries the seven$mod+Alt+1…7layout bindings, which name places. Move them, and theexec … ~/.screenlayout/@default.shline, to a file of your own in the same directory, e.g.~/.config/i3/config.d/90-layouts.conf. i3 reads it the same way, and it stays yours until autorandr profiles replace it. The host keeps the found20-g14.confonce in any case. ~/scripts/asus-bright,~/scripts/asusctl-kbd-bright,~/scripts/xrandr-bright,~/scripts/media-control,~/scripts/xinput-reset-touchpad,~/scripts/.screenlayouts/orden-workspaces.shand~/.config/i3/scripts/kbd-brightness-notify.sh: no trigger and no line of the module's uses them any more. Keep~/scripts/as-userwhile/etc/udev/rules.d/91-monitor-hotplug.rulesexists: that rule still runs the monitor wizard through it.zephyrus_checknames every place that still calls it./etc/systemd/system/triggerhappy.service: the predecessor's replacement of the packaged unit. The module's drop-in works over either, so delete it andsystemctl daemon-reloadto return to the packaged unit (Type=notify, socket)./etc/systemd/logind.conf.d/brightness.conf: the unknown key, which does nothing./etc/systemd/system-sleep/xinput-reset-touchpad.sh, if it is still there: replaced by the input class and the resume unit. (It was already gone on 2026-10-04.)/etc/UPower/UPower.conf: the predecessor's replacement of the package's file. Its values are now the module's drop-in. Restore the package's copy (rmit, thenpacman -S upower).- Optional:
systemctl disable nvidia-suspend nvidia-resume nvidia-hibernate(the drop-ins carry them now),/etc/asusd/*.ron-oldandfan_curves.ron.bak, the foreignasusctl-debugpackage, andpacman -S asusctlfor the distribution's build.
Stays the machine's: /swapfile and /etc/systemd/system/swapfile.swap (the swap layout),
/etc/udev/rules.d/91-monitor-hotplug.rules (the display's, phase 2), and the place layouts.
On the desktop (the predecessor's G14 flavor reached it; part was removed on 2026-10-04): none of
this module applies there. Still present and to be deleted:
/etc/systemd/logind.conf.d/brightness.conf, /etc/systemd/system-sleep/xinput-reset-touchpad.sh,
~/scripts/xinput-reset-touchpad, ~/scripts/xrandr-bright and
~/.config/i3/scripts/kbd-brightness-notify.sh.
The predecessor, file by file
The predecessor carried this model in its desktop module's g14 and laptop flavors, and in a
g14-power module. Every model-specific file of the desktop module, and where it is now:
| predecessor file | now |
|---|---|
i3-asus.conf → config.d/10-asus.conf |
this module, the same path; the scripts it calls are the module's |
i3-g14.conf → config.d/20-g14.conf |
this module, the same path. The place layouts in it are the operator's own file (migration, step 2) |
g14-triggerhappy-asus-g14.conf |
this module, the same path; the triggers run the module's scripts |
g14-triggerhappy.service (a replacement of the packaged unit) |
retired: a drop-in over the packaged unit (--user) |
as-user |
retired: thd runs as the account, and zephyrus-session finds the session. Kept on the machine while the monitor wizard's udev rule calls it |
asus-bright |
this module: zephyrus-backlight, the panel found by its connector |
media-control (bound by the g14 triggers) |
this module: zephyrus-media, without the fallback that needed a secret |
xinput-reset-touchpad |
this module: the input class, zephyrus-touchpad, and the resume unit |
g14-system-sleep-xinput-reset-touchpad.sh |
this module: the input class, and the resume unit that the sleep services want |
kbd-brightness-notify.sh |
this module: zephyrus-kbd-notify, one instance per session |
asusctl-kbd-bright |
retired: the keyboard keys are the firmware's, and zephyrus_brightness sets it by hand |
xrandr-bright |
retired: a software dimming; the panel's backlight is zephyrus-backlight |
screenlayout-orden-workspaces.sh |
this module: zephyrus-display order, on Fn+F9 |
screenlayout-*.sh (six named places) |
the operator's, until autorandr profiles (the xorg module) replace them |
g14-90-backlight.rules |
this module, the same path |
g14-logind-brightness.conf |
retired: an unknown logind key that did nothing |
laptop-monitor-wizard.sh, laptop-91-monitor-hotplug.rules |
any laptop's, not this module's: kept as found, for the xorg module's autorandr to replace |
bottom-bar.g14.toml (the battery block) |
waits: this module's, once the bar takes contributions (ADR 0210) |
razer-basilisk-battery-percentage |
not this model's: a mouse. No module carries it (i3status-rust README) |
screenshot.sh |
not this model's: any machine's script, which the i3 module binds too; this module binds Fn+F6 to it |
package triggerhappy |
depended on, kept as found (outside the distribution, above) |
g14-power's pieces (the NVIDIA options, logind, UPower, the sleep units, auto-profile) are in
What it owns and the switcher above. Its memory guard is the memory-pressure module's.
What the predecessor paid for, and what this module does about it
- One file for every machine overwrote what each machine needed (the predecessor's split of its i3 configuration into a base, an ASUS and a G14 layer, 2026-03). Here the model's lines are this module's files, and nothing else writes them.
- The layers silently stopped applying. The predecessor's chain laptop → g14 was stated in two
places, and the one its installer read lacked it. For weeks the G14 received only its 21
G14-tagged files, and the 49 base and laptop files were never seeded. Validation and the pipeline
both reported success (2026-08, found at a cutover). Here a module is one manifest. Its tests assert
that every trigger runs a script it ships, and
zephyrus_checkandzephyrus_keysread back what is on the machine. - A hook wrote asusd's own files with sudo, because configuration sync might not run on install (2026-04), and froze them. asusd rewrites those files whenever a setting changes. Here the module sets asusd through its client and never writes the RON files.
- A profile daemon fought the profile key. The predecessor turned asusd's own switching off and re-asserted its choice every five seconds (2026-03). Here the switcher acts on a change of its decision, never to restore one.
- The overnight freeze was an ACPI power-source event hanging the NVIDIA GPU in runtime D3
(2026-06). It is fixed by the driver options, which
zephyrus_checkreads from the running driver rather than from the file. - A unit restarted about 73,000 times, unnoticed (2026-06). Here every expectation is in
zephyrus_check, and so is a list of what it did not check. - The vendor keys ran as root and
su-ed to a named person on a guessed display, sourcing a file of secrets. Here thd drops to the account, and nothing is sourced. - The touchpad lost its settings after a resume, because
xinputsettings vanish when the device initialises again. Here an input class re-applies them, with the resume unit as a backstop.
Tests
go test ./... in this directory. Every tool runs against a tree standing in for /sys, /proc and
/etc, and an injected runner answering with what asusctl 6.4 and supergfxctl 5.2 said on the laptop.
The tests cover:
- the power-source rule;
- battery arithmetic from
charge_*; - the eDP panel choice;
- brightness bounds;
- the policy's sustain, relax and hysteresis, with iowait counted as idle;
- the switcher: no act at start, one switch per change of source, a published event, boost from samples, holds, retry after failure, start-up assertions only where they differ, inert on another model;
- the uevent filter;
- the manifest: tools listed equal tools served, no machine named, triggers exist, every key runs a
shipped executable script, every script passes
bash -n; zephyrus_keysover the module's own triggers and i3 lines, and a key in two trigger files;- the checks for
as-user, triggerhappy's account and the resume unit.
The vendor keys are a contribution (changed 2026-10-04, novox/hq ADR 0212)
The trigger file and triggerhappy's service drop-in are no longer this module's. The triggerhappy
module holds node-hotkeys, owns the daemon, and reads only the mesh's trigger file. This module
contributes its eight trigger lines (media, panel brightness, touchpad) to that seat, so it depends
on a hotkey holder being assigned beside it. Its keys still run this module's own scripts.
zephyrus_keys reads the trigger directory as before.
The touchpad after waking, and the lid, move to the power module (changed 2026-10-04, novox/hq ADR 0211)
The touchpad resume unit and its three drop-ins on the sleep services are gone. The reset is now this
module's contribution to node-power's after-wake moment, so the module depends on the power
module. logind.conf.d/power.conf is the power module's. This laptop's values (suspend on the power
key and on the lid in every case) are that module's settings for this machine. The NVIDIA driver's
sleep drop-ins stay here: they must run inside the sleep transaction, which a contribution cannot.