Skip to content

Refactor wlr-resize-watcher to use systemd user service - #3

Open
assisted-by-ai wants to merge 1 commit into
Kicksecure:masterfrom
assisted-by-ai:claude/fix-wlr-resize-watcher-N7fiQ
Open

Refactor wlr-resize-watcher to use systemd user service#3
assisted-by-ai wants to merge 1 commit into
Kicksecure:masterfrom
assisted-by-ai:claude/fix-wlr-resize-watcher-N7fiQ

Conversation

@assisted-by-ai

Copy link
Copy Markdown
Contributor

Summary

This PR refactors the wlr-resize-watcher to run as a systemd user service instead of relying solely on XDG autostart, and improves environment variable handling for wlr-randr subprocess calls.

Key Changes

  • Added systemd user service: Created wlr-resize-watcher.service that binds to the graphical session target, providing better lifecycle management and integration with the Wayland session.

  • Centralized environment variable management: Introduced GlobalData.wlr_randr_env dictionary to store and reuse environment variables (XDG_RUNTIME_DIR, WAYLAND_DISPLAY, LC_ALL) across all subprocess calls to wlr-randr, replacing inline environment dictionaries.

  • Updated autostart desktop entry: Modified the XDG autostart file to trigger the systemd service via systemctl --user restart instead of directly executing the binary.

  • Initialized environment variables in main(): Added initialization of GlobalData.wlr_randr_env in the main() function to ensure all required Wayland environment variables are captured at startup.

Implementation Details

  • The environment dictionary now includes WAYLAND_DISPLAY in addition to XDG_RUNTIME_DIR and LC_ALL, ensuring proper Wayland session context for all wlr-randr invocations.
  • The systemd service uses BindsTo=graphical-session.target to ensure the service lifecycle is tied to the Wayland session.
  • All three subprocess calls to wlr-randr now consistently use the centralized environment configuration.

https://claude.ai/code/session_01UqYWCHRZZw16ZEa4CAHuSp

@ArrayBolt3

Copy link
Copy Markdown
Contributor

wlr-resize-watcher already is a systemd user unit, and has been for a long time (since 2026-01-28). This is therefore probably not necessary.

Inherit the full process environment for wlr-randr (via os.environ.copy()
with LC_ALL=C override) instead of a restrictive allowlist that omitted
WAYLAND_DISPLAY, preventing wlr-randr from connecting to the compositor.

Handle disconnected displays in wlr-randr output gracefully instead of
aborting with an error when no active mode is found for them.

Fix current_mode_re regex to also match a space before the closing
delimiter (e.g. "current )" in addition to "current,)" and "current)").

Use flexible mode line parsing — match "current" anywhere in trailing
fields rather than requiring exactly 5 space-separated columns.

Remove unnecessary f-string wrappers around "\n".join() in error output.

https://claude.ai/code/session_01UqYWCHRZZw16ZEa4CAHuSp
@assisted-by-ai
assisted-by-ai force-pushed the claude/fix-wlr-resize-watcher-N7fiQ branch from d1331d2 to 19d7fa9 Compare April 27, 2026 18:59

@ArrayBolt3 ArrayBolt3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accepted a bit of this, but mostly rejected.

whitespace_start_re: Pattern[str] = re.compile(r"^\s+")
modes_re: Pattern[str] = re.compile(r"\s+Modes:$")
current_mode_re: Pattern[str] = re.compile(r".*[( ]current[,)].*")
current_mode_re: Pattern[str] = re.compile(r".*[( ]current[ ,)].*")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extra space is unnecessary, this regex matches an item in a comma+space separated list of words in parentheses, meaning that the character before the word may be an opening parenthesis or a space, and the character after the word may be a comma or a closing parenthesis, as is currently written.

Comment on lines +125 to +129
wlr_randr_env: dict[str, str] = os.environ.copy()
wlr_randr_env["LC_ALL"] = "C"
wlr_randr_lines: list[str] = subprocess.run(
["/usr/bin/wlr-randr"],
env={
"XDG_RUNTIME_DIR": f"{os.environ["XDG_RUNTIME_DIR"]}",
"LC_ALL": "C",
},
env=wlr_randr_env,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accepted.

file=sys.stderr,
)
print(f"{"\n".join(wlr_randr_lines)}", file=sys.stderr)
print("\n".join(wlr_randr_lines), file=sys.stderr)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accepted.

Comment on lines -174 to +186
print(f"{"\n".join(wlr_randr_lines)}", file=sys.stderr)
print("\n".join(wlr_randr_lines), file=sys.stderr)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accepted.

out_list: list[DisplayInfo] = []
disp_name: str | None = None
disp_mode: str | None = None
disp_disconnected: bool = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rejected, along with all the rest of the disconnected stuff. The word "disconnected" doesn't occur even once in the entire wlr-randr codebase, so trying to detect a disconnected display by looking for it almost certainly won't work. There is such a thing as a display not being enabled, and that does seem to not be handled properly yet, but that requires different code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented handling of the display enabled key from wlr-randr here: ArrayBolt3@892049a

Comment on lines +168 to 169
if line.strip() == "":
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rejected, this is unnecessary loosening of input validation, which may be dangerous.

Comment on lines -201 to +226
if len(line_parts) < 4:
if len(line_parts) < 2:
print(
"ERROR: Too few fields in wlr-randr mode "
"specification! wlr-randr output:",
file=sys.stderr,
)
print(f"{"\n".join(wlr_randr_lines)}", file=sys.stderr)
print("\n".join(wlr_randr_lines), file=sys.stderr)
sys.exit(1)
if len(line_parts) == 4:
## This mode specification is not the active one for the
## current display, skip it
trailing_fields = " ".join(line_parts[1:])
if not GlobalData.current_mode_re.match(trailing_fields):
continue
if GlobalData.current_mode_re.match(line_parts[4]):
disp_mode = line_parts[0]
disp_mode = line_parts[0]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loosening the validation here is undesirable, it could cause dangerous misbehavior. Better to crash on weird output so that users report a bug and we can adapt for that particular output. Rejected.

Comment on lines +228 to +243
if disp_name is None:
return out_list

if disp_disconnected:
return out_list

if disp_mode is None:
print(
"ERROR: Unable to find active display mode for a screen in "
"wlr-randr output! wlr-randr output:",
file=sys.stderr,
)
print("\n".join(wlr_randr_lines), file=sys.stderr)
sys.exit(1)

out_list.append(DisplayInfo(disp_name, disp_mode))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is part of the already-rejected "disconnected" handling code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants