Files
mesh-catalog/modules/asus-zephyrus-g14
jochen 1059f12ee0 asus-zephyrus-g14: the laptop's keys, scripts and i3 lines are the module's, with a resume backstop
The i3 and laptop READMEs each pointed at the other for 10-asus.conf and 20-g14.conf,
so nobody owned them. The module now writes both (adopted paths, so no duplicate
binding breaks i3's config check), ships the scripts they call, runs the media keys
with notifications again, and brings back the touchpad reset after resume as a unit
the sleep services want. zephyrus_keys answers what each custom key runs; the check
flags as-user, thd's account and the resume unit. xorg-xinput is the xorg module's.
2026-10-04 15:49:46 +02:00
..

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/*.ron belong 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.conf and /etc/modprobe.d/supergfxd.conf belong 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 (the xorg module).
  • 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 (its laptop flavor). 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's as-user.
  • The screenshot script (~/.config/i3/scripts/screenshot.sh) is any machine's. The i3 module 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-rust README). 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 to Balanced after 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 online file reading 1 as mains, and on this model the USB-C ports report online. Batteries of scope=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_profile holds 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 with zephyrus_charge_limit stands until the bundle next starts. For a one-off full charge, use its oneshot.
  • 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 --user changes 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 connected eDP output. The predecessor named eDP-1 and used jq. 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 its exec lines 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 registers nvidia_0, which moves nothing. The predecessor named amdgpu_bl1 literally.
  • zephyrus-notify ID TEXT: one replacing notification, through busctl (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.ron has ac_command: "supergfxctl -m Hybrid" and bat_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_check reports both. The fix is the operator's, in asusd's file: clear both commands, or update supergfxctl once it can be packaged.
  • brightness.conf did nothing. HandleBrightnessKey is 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-profile is stopped (below).
  • asusctl is a local build (6.4.0, Unknown Packager) beside a foreign asusctl-debug.

When assigned to the laptop: what changes

  1. /usr/local/lib/asus-zephyrus-g14/ appears (seven scripts).
  2. Written over found files (each original kept once by the host): g14-nvidia-power.conf and video-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's 10-asus.conf and 20-g14.conf (the module's lines; the i3 module's watcher checks and reloads them).
  3. 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.
  4. daemon-reload and a triggerhappy restart. thd now runs as the account and the keys run the module's scripts. upower restarts.
  5. Packages, asusd and supergfxd: already as declared, so nothing changes. asusctl stays the local 6.4.0 until the next upgrade.
  6. 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:

  1. systemctl --user disable --now auto-profile.service, then delete ~/.config/systemd/user/auto-profile.service and ~/scripts/auto-profile. Do this right after the push, or two switchers run at once.
  2. Before the push, keep the place layouts. The module writes 20-g14.conf over the found file, and the found file carries the seven $mod+Alt+1…7 layout bindings, which name places. Move them, and the exec … ~/.screenlayout/@default.sh line, 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 found 20-g14.conf once in any case.
  3. ~/scripts/asus-bright, ~/scripts/asusctl-kbd-bright, ~/scripts/xrandr-bright, ~/scripts/media-control, ~/scripts/xinput-reset-touchpad, ~/scripts/.screenlayouts/orden-workspaces.sh and ~/.config/i3/scripts/kbd-brightness-notify.sh: no trigger and no line of the module's uses them any more. Keep ~/scripts/as-user while /etc/udev/rules.d/91-monitor-hotplug.rules exists: that rule still runs the monitor wizard through it. zephyrus_check names every place that still calls it.
  4. /etc/systemd/system/triggerhappy.service: the predecessor's replacement of the packaged unit. The module's drop-in works over either, so delete it and systemctl daemon-reload to return to the packaged unit (Type=notify, socket).
  5. /etc/systemd/logind.conf.d/brightness.conf: the unknown key, which does nothing.
  6. /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.)
  7. /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 (rm it, then pacman -S upower).
  8. Optional: systemctl disable nvidia-suspend nvidia-resume nvidia-hibernate (the drop-ins carry them now), /etc/asusd/*.ron-old and fan_curves.ron.bak, the foreign asusctl-debug package, and pacman -S asusctl for 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_check and zephyrus_keys read 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_check reads 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 xinput settings 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_keys over 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.