Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
441b86448e
|
||
|
|
6548bbb232
|
@@ -2,22 +2,25 @@
|
|||||||
|
|
||||||
# Maintainer: Daniele Fucini <dfucini [at] gmail [dot] com>
|
# Maintainer: Daniele Fucini <dfucini [at] gmail [dot] com>
|
||||||
|
|
||||||
pkgname=xautolock-inhibit
|
pkgname=xidle-inhibit
|
||||||
pkgver=0.2
|
pkgver=0.3
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc='Honor org.freedesktop.ScreenSaver inhibit requests by disabling xautolock'
|
pkgdesc='Honor org.freedesktop.ScreenSaver inhibit requests by blocking xautolock or xss-lock'
|
||||||
arch=('any')
|
arch=('any')
|
||||||
url="https://git.shouldnt.work/fuxino/$pkgname"
|
url="https://git.shouldnt.work/fuxino/$pkgname"
|
||||||
license=('GPL-3.0-or-later')
|
license=('GPL-3.0-or-later')
|
||||||
depends=('libx11'
|
depends=('libx11'
|
||||||
'libxss'
|
'libxss'
|
||||||
'python'
|
'python'
|
||||||
'python-gobject'
|
'python-gobject')
|
||||||
'xautolock')
|
optdepends=('xautolock: xautolock backend'
|
||||||
|
'xss-lock: xss-lock backend')
|
||||||
makedepends=('git')
|
makedepends=('git')
|
||||||
|
conflicts=('xautolock-inhibit')
|
||||||
|
replaces=('xautolock-inhibit')
|
||||||
source=("git+$url?signed#tag=$pkgver")
|
source=("git+$url?signed#tag=$pkgver")
|
||||||
validpgpkeys=('7E12BC1FF3B6EDB2CD8053EB981A2B2A3BBF5514')
|
validpgpkeys=('7E12BC1FF3B6EDB2CD8053EB981A2B2A3BBF5514')
|
||||||
sha256sums=('c9d4bf5de7a1bd3f8ddf0cd2103e01891726877cfe12cb0f545c880b0df6f44f')
|
sha256sums=('13390705ad8e0abea5136dbe57f21b7d9509681ee8628109dd0a302497188fbf')
|
||||||
|
|
||||||
package()
|
package()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ Formerly `xautolock-inhibit`, which supported only xautolock.
|
|||||||
- `xautolock` or `xss-lock`, already running in the session
|
- `xautolock` or `xss-lock`, already running in the session
|
||||||
- `python`, `python-gobject`
|
- `python`, `python-gobject`
|
||||||
- `libx11`, `libxss`
|
- `libx11`, `libxss`
|
||||||
|
- For `xidle-dim`: a compositor, such as picom
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -46,14 +47,16 @@ spawnOnce "xautolock -time 10 -locker slock -detectsleep"
|
|||||||
spawnOnce "systemctl --user start xidle-inhibit"
|
spawnOnce "systemctl --user start xidle-inhibit"
|
||||||
```
|
```
|
||||||
|
|
||||||
With xss-lock, the lock timeout is the X screensaver timeout (`xset s`):
|
With xss-lock, the lock timeout is the X screensaver timeout (`xset s TIMEOUT CYCLE`). This dims the screen with `xidle-dim` at 9:50 and locks at 10:00:
|
||||||
|
|
||||||
```haskell
|
```haskell
|
||||||
spawnOnce "xset s 600"
|
spawnOnce "xset s 590 10"
|
||||||
spawnOnce "xss-lock -- slock"
|
spawnOnce "xss-lock -n xidle-dim -- slock"
|
||||||
spawnOnce "systemctl --user start xidle-inhibit"
|
spawnOnce "systemctl --user start xidle-inhibit"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Without dimming, use `xset s 600 0` and `xss-lock -- slock`. Start xss-lock from the window manager, not from a terminal: terminals like kitty run commands outside the login session, and xss-lock then ignores `loginctl lock-session`.
|
||||||
|
|
||||||
The unit has no `[Install]` section on purpose. Enabling it at login would also start it in a desktop environment, or before X is up; starting it from the window manager limits it to that session. The user service manager needs `DISPLAY` (and `XAUTHORITY`) in its environment, which `/etc/X11/xinit/xinitrc.d/50-systemd-user.sh` sets up for `startx` sessions.
|
The unit has no `[Install]` section on purpose. Enabling it at login would also start it in a desktop environment, or before X is up; starting it from the window manager limits it to that session. The user service manager needs `DISPLAY` (and `XAUTHORITY`) in its environment, which `/etc/X11/xinit/xinitrc.d/50-systemd-user.sh` sets up for `startx` sessions.
|
||||||
|
|
||||||
### Upgrading from xautolock-inhibit
|
### Upgrading from xautolock-inhibit
|
||||||
@@ -107,6 +110,19 @@ Every request, release and locker command is logged to the journal:
|
|||||||
journalctl --user -u xidle-inhibit
|
journalctl --user -u xidle-inhibit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Dimming before the lock
|
||||||
|
|
||||||
|
`xidle-dim` is an xss-lock notifier (`-n`). It fades a black window over the screen until xss-lock stops it, on user activity or when the locker starts. It needs a compositor such as picom and does nothing without one. Because it doesn't change gamma, it works alongside redshift.
|
||||||
|
|
||||||
|
```
|
||||||
|
xidle-dim [--opacity 0-1] [--fade SECONDS]
|
||||||
|
```
|
||||||
|
|
||||||
|
- `--opacity`: final darkness (default 0.6).
|
||||||
|
- `--fade`: fade-in time (default 1.5).
|
||||||
|
|
||||||
|
Options go inside the notifier command: `xss-lock -n "xidle-dim --opacity 0.8" -- slock`.
|
||||||
|
|
||||||
## App notes
|
## App notes
|
||||||
|
|
||||||
- **Sandboxed apps** may be blocked from reaching the service. For a firejail profile that filters D-Bus (`dbus-user filter`, as the Firefox-based profiles do), allow it in the app's `.local` file, e.g. `zen-browser.local`:
|
- **Sandboxed apps** may be blocked from reaching the service. For a firejail profile that filters D-Bus (`dbus-user filter`, as the Firefox-based profiles do), allow it in the app's `.local` file, e.g. `zen-browser.local`:
|
||||||
|
|||||||
+2
-1
@@ -1,10 +1,11 @@
|
|||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
line-length = 120
|
line-length = 120
|
||||||
extend-include = ["xidle-inhibit"]
|
extend-include = ["xidle-inhibit", "xidle-dim"]
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
select = ["E", "F", "W", "I", "B", "UP"]
|
select = ["E", "F", "W", "I", "B", "UP"]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
|
# Scripts both map to __main__, so check xidle-dim separately: mypy xidle-dim
|
||||||
files = ["xidle-inhibit"]
|
files = ["xidle-inhibit"]
|
||||||
strict = true
|
strict = true
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Dim the screen until killed, as an xss-lock notifier: xss-lock -n xidle-dim -- slock.
|
||||||
|
|
||||||
|
Maps a black window over the whole screen and fades in its opacity through the compositor; gamma is left alone,
|
||||||
|
so it doesn't clash with redshift. Without a compositor it does nothing, as the window would be solid black.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import ctypes
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from types import FrameType
|
||||||
|
|
||||||
|
CW_BACK_PIXEL = 1 << 1
|
||||||
|
CW_OVERRIDE_REDIRECT = 1 << 9
|
||||||
|
INPUT_OUTPUT = 1
|
||||||
|
XA_CARDINAL = 6
|
||||||
|
PROP_MODE_REPLACE = 0
|
||||||
|
OPACITY_MAX = 0xFFFFFFFF
|
||||||
|
|
||||||
|
|
||||||
|
class XSetWindowAttributes(ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("background_pixmap", ctypes.c_ulong),
|
||||||
|
("background_pixel", ctypes.c_ulong),
|
||||||
|
("border_pixmap", ctypes.c_ulong),
|
||||||
|
("border_pixel", ctypes.c_ulong),
|
||||||
|
("bit_gravity", ctypes.c_int),
|
||||||
|
("win_gravity", ctypes.c_int),
|
||||||
|
("backing_store", ctypes.c_int),
|
||||||
|
("backing_planes", ctypes.c_ulong),
|
||||||
|
("backing_pixel", ctypes.c_ulong),
|
||||||
|
("save_under", ctypes.c_int),
|
||||||
|
("event_mask", ctypes.c_long),
|
||||||
|
("do_not_propagate_mask", ctypes.c_long),
|
||||||
|
("override_redirect", ctypes.c_int),
|
||||||
|
("colormap", ctypes.c_ulong),
|
||||||
|
("cursor", ctypes.c_ulong),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def load_x11() -> ctypes.CDLL:
|
||||||
|
x11 = ctypes.CDLL("libX11.so.6")
|
||||||
|
p, ul, i = ctypes.c_void_p, ctypes.c_ulong, ctypes.c_int
|
||||||
|
x11.XOpenDisplay.argtypes = [ctypes.c_char_p]
|
||||||
|
x11.XOpenDisplay.restype = p
|
||||||
|
for name in ("XDefaultScreen",):
|
||||||
|
getattr(x11, name).argtypes = [p]
|
||||||
|
for name in ("XRootWindow", "XBlackPixel"):
|
||||||
|
getattr(x11, name).argtypes = [p, i]
|
||||||
|
getattr(x11, name).restype = ul
|
||||||
|
for name in ("XDisplayWidth", "XDisplayHeight"):
|
||||||
|
getattr(x11, name).argtypes = [p, i]
|
||||||
|
x11.XInternAtom.argtypes = [p, ctypes.c_char_p, i]
|
||||||
|
x11.XInternAtom.restype = ul
|
||||||
|
x11.XGetSelectionOwner.argtypes = [p, ul]
|
||||||
|
x11.XGetSelectionOwner.restype = ul
|
||||||
|
x11.XCreateWindow.argtypes = [p, ul, i, i, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, i, ctypes.c_uint, p, ul,
|
||||||
|
ctypes.POINTER(XSetWindowAttributes)]
|
||||||
|
x11.XCreateWindow.restype = ul
|
||||||
|
x11.XChangeProperty.argtypes = [p, ul, ul, ul, i, i, p, i]
|
||||||
|
x11.XMapRaised.argtypes = [p, ul]
|
||||||
|
x11.XFlush.argtypes = [p]
|
||||||
|
return x11
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
parser.add_argument("--opacity", type=float, default=0.6, help="final darkness, 0 to 1 (default: %(default)s)")
|
||||||
|
parser.add_argument("--fade", type=float, default=1.5, help="fade-in time in seconds (default: %(default)s)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
if not 0 <= args.opacity <= 1 or args.fade < 0:
|
||||||
|
parser.error("--opacity must be between 0 and 1 and --fade not negative")
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def exit_on_signal(_signum: int, _frame: FrameType | None) -> None:
|
||||||
|
sys.exit(0) # the X server removes the window when the connection closes
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
args = parse_args()
|
||||||
|
# xss-lock sends SIGHUP on user activity and SIGTERM when the locker starts.
|
||||||
|
for sig in (signal.SIGHUP, signal.SIGTERM, signal.SIGINT):
|
||||||
|
signal.signal(sig, exit_on_signal)
|
||||||
|
|
||||||
|
x11 = load_x11()
|
||||||
|
dpy = x11.XOpenDisplay(None)
|
||||||
|
if not dpy:
|
||||||
|
sys.exit("xidle-dim: cannot open display")
|
||||||
|
screen = x11.XDefaultScreen(dpy)
|
||||||
|
if not x11.XGetSelectionOwner(dpy, x11.XInternAtom(dpy, f"_NET_WM_CM_S{screen}".encode(), False)):
|
||||||
|
print("xidle-dim: no compositor running, not dimming", file=sys.stderr)
|
||||||
|
signal.pause() # stay alive like a notifier should, until xss-lock kills us
|
||||||
|
|
||||||
|
attrs = XSetWindowAttributes(background_pixel=x11.XBlackPixel(dpy, screen), override_redirect=True)
|
||||||
|
win = x11.XCreateWindow(dpy, x11.XRootWindow(dpy, screen), 0, 0, x11.XDisplayWidth(dpy, screen),
|
||||||
|
x11.XDisplayHeight(dpy, screen), 0, 0, INPUT_OUTPUT, None,
|
||||||
|
CW_BACK_PIXEL | CW_OVERRIDE_REDIRECT, ctypes.byref(attrs))
|
||||||
|
opacity_atom = x11.XInternAtom(dpy, b"_NET_WM_WINDOW_OPACITY", False)
|
||||||
|
|
||||||
|
def set_opacity(level: float) -> None:
|
||||||
|
value = ctypes.c_ulong(int(level * OPACITY_MAX)) # format 32 properties are passed as longs
|
||||||
|
x11.XChangeProperty(dpy, win, opacity_atom, XA_CARDINAL, 32, PROP_MODE_REPLACE, ctypes.byref(value), 1)
|
||||||
|
x11.XFlush(dpy)
|
||||||
|
|
||||||
|
set_opacity(0)
|
||||||
|
x11.XMapRaised(dpy, win)
|
||||||
|
steps = max(1, int(args.fade * 30))
|
||||||
|
for n in range(1, steps + 1):
|
||||||
|
set_opacity(args.opacity * n / steps)
|
||||||
|
time.sleep(args.fade / steps)
|
||||||
|
while True:
|
||||||
|
signal.pause()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user