2 Commits
Author SHA1 Message Date
Daniele Fucini 0234456af8 Log SimulateUserActivity once per burst
Steam calls it thousands of times per hour during controller play, which made up
~98% of the journal. Log the first call and a summary with the count after 5
minutes of quiet, when the client leaves, or on shutdown.
2026-10-15 23:38:52 +02:00
Daniele Fucini 51e18766a4 Update PKGBUILD 2026-10-14 13:32:58 +02:00
3 changed files with 51 additions and 6 deletions
+2 -2
View File
@@ -3,7 +3,7 @@
# Maintainer: Daniele Fucini <dfucini [at] gmail [dot] com>
pkgname=xautolock-inhibit
pkgver=0.1.1
pkgver=0.1.2
pkgrel=1
pkgdesc='Honor org.freedesktop.ScreenSaver inhibit requests by disabling xautolock'
arch=('any')
@@ -17,7 +17,7 @@ depends=('libx11'
makedepends=('git')
source=("git+$url?signed#tag=$pkgver")
validpgpkeys=('7E12BC1FF3B6EDB2CD8053EB981A2B2A3BBF5514')
sha256sums=('SKIP')
sha256sums=('6713c706e422cea0ae346fc7bd16611dd0e32487acf4d15ec3a4db325eaaa0c7')
package()
{
+1 -1
View File
@@ -85,7 +85,7 @@ journalctl --user -u xautolock-inhibit
## Limitations
- Restarting the daemon loses the requests it holds; apps don't send them again. Avoid restarting it while a video or game is running.
- `SimulateUserActivity` is accepted and logged, but does nothing.
- `SimulateUserActivity` is accepted but does nothing. Calls are logged once when a client starts sending them and once, with a count, after 5 minutes without any.
- `GetActive` always returns `false`.
## License
+48 -3
View File
@@ -10,6 +10,9 @@ import itertools
import signal
import subprocess
import sys
import time
from dataclasses import dataclass
from datetime import timedelta
from gi.repository import Gio, GLib
@@ -22,6 +25,7 @@ except ImportError:
BUS_NAME = "org.freedesktop.ScreenSaver"
OBJECT_PATHS = ("/org/freedesktop/ScreenSaver", "/ScreenSaver") # Vivaldi uses the first, Zen the second
EXIT_NAME_TAKEN = 2
ACTIVITY_QUIET = 300 # seconds without SimulateUserActivity that end a logged burst
INTROSPECTION = """
<node>
@@ -105,6 +109,14 @@ class XBlanking:
log(f"X blanking {'paused' if on else 'resumed'}")
@dataclass
class ActivityBurst:
first: float
last: float
calls: int = 0
timer: int = 0
class Inhibitor:
def __init__(self, ignored_reasons, release_delay, blanking):
self.ignored_reasons = ignored_reasons
@@ -113,6 +125,7 @@ class Inhibitor:
self.cookies = {} # cookie -> (sender, application_name, reason, counts)
self.next_cookie = itertools.count(1)
self.proc_names = {} # sender -> "comm[pid]", for logging only
self.activity: dict[str, ActivityBurst] = {} # sender -> SimulateUserActivity burst
self.disabled = False
self.release_timer = 0
self.conn = None
@@ -150,7 +163,9 @@ class Inhibitor:
self.blanking.suspend(False)
self.disabled = False
def shutdown(self):
def shutdown(self) -> None:
for sender in list(self.activity):
self.end_activity(sender)
if self.release_timer:
GLib.source_remove(self.release_timer)
self.release_timer = 0
@@ -192,11 +207,41 @@ class Inhibitor:
log(f"UnInhibit {cookie}: '{app}' '{reason}'")
self.update()
def release_sender(self, sender):
def simulate_activity(self, sender: str) -> None:
"""Log a sender's calls once per burst: Steam sends thousands per hour."""
now = time.monotonic()
burst = self.activity.get(sender)
if burst is None:
log(f"SimulateUserActivity from {self.proc_name(sender)} (not implemented)")
burst = self.activity[sender] = ActivityBurst(first=now, last=now)
else:
GLib.source_remove(burst.timer)
burst.calls += 1
burst.last = now
burst.timer = GLib.timeout_add_seconds(ACTIVITY_QUIET, self.on_activity_quiet, sender)
def on_activity_quiet(self, sender: str) -> bool:
self.activity[sender].timer = 0
self.end_activity(sender)
return GLib.SOURCE_REMOVE
def end_activity(self, sender: str) -> None:
burst = self.activity.pop(sender, None)
if burst is None:
return
if burst.timer:
GLib.source_remove(burst.timer)
if burst.calls == 1:
return
span = timedelta(seconds=round(burst.last - burst.first))
log(f"SimulateUserActivity from {self.proc_name(sender)}: {burst.calls} calls over {span}")
def release_sender(self, sender: str) -> None:
gone = [c for c, (s, *_) in self.cookies.items() if s == sender]
for c in gone:
_, app, reason, _ = self.cookies.pop(c)
log(f"Released {c}: '{app}' '{reason}' ({sender} left the bus)")
self.end_activity(sender)
self.proc_names.pop(sender, None)
if gone:
self.update()
@@ -209,7 +254,7 @@ class Inhibitor:
self.uninhibit(sender, params.unpack()[0])
invocation.return_value(None)
elif method == "SimulateUserActivity":
log(f"SimulateUserActivity from {self.proc_name(sender)} (not implemented)")
self.simulate_activity(sender)
invocation.return_value(None)
elif method == "Lock":
log(f"Lock from {self.proc_name(sender)}")