max / alloy
| 1 | #!/bin/sh |
| 2 | # alloy-dim — warn that the lock is coming, by taking the backlight down. |
| 3 | # |
| 4 | # The sway config claimed "dim to lock at 5 minutes" for as long as it has |
| 5 | # existed and nothing dimmed: the screen went from full brightness straight to |
| 6 | # swaylock at 300 seconds. So the lock always arrived as an ambush, and the only |
| 7 | # way to prevent one was to have been touching the keyboard at the moment it |
| 8 | # fired. |
| 9 | # |
| 10 | # This is the thirty seconds of warning. swayidle calls `down` at 270 and `up` on |
| 11 | # the next input, so moving the mouse cancels a lock that has not happened yet. |
| 12 | # It is also the closest thing to a grace period available: upstream swaylock has |
| 13 | # no --grace (only the swaylock-effects fork does), and adopting a fork for one |
| 14 | # flag is a poor trade for a distro that prefers canonical packages. |
| 15 | # |
| 16 | # Nothing here may fail an idle chain. A non-zero exit from a swayidle timeout |
| 17 | # action is not the end of the world, but a machine with no backlight at all — a |
| 18 | # desktop, or the server profile — must not spend every five minutes reporting |
| 19 | # that it cannot dim a screen it does not have. So absence exits 0. |
| 20 | # |
| 21 | # Why sysfs and not brightnessctl: the image ships no brightness CLI. SwayOSD |
| 22 | # raises brightness by writing /sys/class/backlight/<dev>/brightness directly, |
| 23 | # and the Containerfile puts a udev rule on udev's search path that chgrps that |
| 24 | # file to `video` and adds group write. The installer puts the account in `video` |
| 25 | # for exactly this reason. So a session process can write it, and that is the |
| 26 | # whole mechanism the Fn keys already use. |
| 27 | |
| 28 | |
| 29 | |
| 30 | # The verb is checked before anything else, and before the backlight is looked |
| 31 | # for, so that a typo is an error on every machine rather than only on the ones |
| 32 | # with a panel. Ordered the other way — which is how this was first written — a |
| 33 | # misspelled verb exits 0 on a desktop, because the no-backlight case returns |
| 34 | # before the argument is ever examined. The Containerfile asserts the config and |
| 35 | # this script agree, so a typo should not reach here; making it loud anyway costs |
| 36 | # three lines and removes a way for that assertion to be wrong. |
| 37 | case "" in |
| 38 | down | up) ;; |
| 39 | *) |
| 40 | |
| 41 | |
| 42 | ;; |
| 43 | esac |
| 44 | |
| 45 | # The first backlight this session can actually write. Iterating rather than |
| 46 | # hardcoding intel_backlight: the name is the driver's, so it differs between |
| 47 | # Intel, AMD and a DDC-driven external panel, and a machine can carry more than |
| 48 | # one. Writability is the test that matters, because an unwritable device means |
| 49 | # the udev rule did not apply and dimming would fail anyway. |
| 50 | device="" |
| 51 | for; do |
| 52 | [ || continue |
| 53 | device="" |
| 54 | break |
| 55 | done |
| 56 | |
| 57 | # No panel, or no group write on it. Either way there is nothing to dim and |
| 58 | # saying so once per idle period would be noise, not information. |
| 59 | [ || |
| 60 | |
| 61 | # Where the pre-dim level is remembered. In the runtime directory because it is |
| 62 | # tmpfs cleared at logout: a saved level that outlived the session would restore |
| 63 | # a brightness from yesterday, and there is no cleanup path that would catch it. |
| 64 | # Keyed by device so two panels cannot overwrite each other's saved value. |
| 65 | state="/alloy-dim." |
| 66 | |
| 67 | case "" in |
| 68 | down) |
| 69 | # Already dimmed. Without this guard a second `down` would save the dimmed |
| 70 | # value as the level to restore, and the screen would never come back up — |
| 71 | # which is the one failure here that looks like broken hardware. |
| 72 | [ && |
| 73 | |
| 74 | current= |
| 75 | |
| 76 | |
| 77 | # A fifth of current, floored at 1. Not zero: zero is a dark panel, which is |
| 78 | # indistinguishable from the display power-off that follows at ten minutes, |
| 79 | # and this has to read as a warning rather than as the screen having gone. |
| 80 | # A fraction rather than a fixed value because the starting point is the |
| 81 | # user's own choice and a fixed target would brighten a dim screen. |
| 82 | dimmed= |
| 83 | [ && dimmed=1 |
| 84 | |
| 85 | ;; |
| 86 | up) |
| 87 | # Nothing saved means this session did not dim, so there is nothing to put |
| 88 | # back. Restoring unconditionally would invent a brightness on any resume |
| 89 | # that followed something other than a dim. |
| 90 | [ || |
| 91 | |
| 92 | |
| 93 | |
| 94 | ;; |
| 95 | esac |
| 96 |