Skip to content

feat(windowing): generic X11/EWMH window backend#41

Merged
avifenesh merged 5 commits into
agent-sh:mainfrom
devthejo:feat/x11-ewmh-backend
Jul 5, 2026
Merged

feat(windowing): generic X11/EWMH window backend#41
avifenesh merged 5 commits into
agent-sh:mainfrom
devthejo:feat/x11-ewmh-backend

Conversation

@devthejo

@devthejo devthejo commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

What

Adds a generic X11 / EWMH window backend so list_windows, focused_window, activate_window, move_window, and resize_window work on X11 window managers that don't have a dedicated backend — Cinnamon/Muffin, MATE/Marco, Xfce/xfwm4, Openbox, etc.

Today window targeting only covers GNOME Shell, KWin, Hyprland, i3, and COSMIC, so on e.g. Cinnamon doctor reports can_list_windows: false and targeted window ops are unavailable even though the session is fully scriptable over EWMH. The README already advertises "X11 best-effort", but there is currently no generic X11 backend.

How

  • New src/windowing/backends/x11.rs — talks plain EWMH/ICCCM via wmctrl + xprop (same shell-out style as the i3/hyprland backends):
    • list_windows: wmctrl -l -p -G -x → id / pid / geometry / WM_CLASS / title, focused flag from _NET_ACTIVE_WINDOW, terminal enrichment like the other backends.
    • activate_window: wmctrl -i -a (EWMH _NET_ACTIVE_WINDOW).
    • move_window / resize_window: wmctrl -i -r -e after dropping the maximized state.
    • Pure parse_wmctrl_windows / parse_active_window_id helpers with unit tests.
  • Registered last in BACKEND_ORDER / DESCRIPTORS / probe_backends, so any session-native backend still wins first; the probe is gated to real X11 sessions so it won't hijack XWayland under a Wayland compositor.
  • move_window / resize_window now dispatch by the resolved window's backend (registry::move_window / resize_window) instead of being hardcoded to the GNOME Shell extension, and window_geometry_op reports the actual backend.

Testing

  • cargo test green (adds x11 parser tests); cargo clippy clean.
  • Verified end-to-end on Linux Mint 22.3 (Cinnamon/Muffin, X11) through the MCP server:
    • doctorreadiness.blockers: [] (window listing/focus now available).
    • list_windows returns the live window list with the correct focused window.
    • activate_window flips _NET_ACTIVE_WINDOW to the target (verified exact_window_focused: true).
    • move_window / resize_window change the window geometry.

Notes / limitations

  • move / resize final position is best-effort — wmctrl -e is subject to WM frame-gravity offsets; the size is exact. Could be tightened later using _NET_FRAME_EXTENTS.
  • Requires wmctrl + xprop at runtime (the backend probe fails gracefully if they're absent), consistent with the i3 (i3-msg + xprop) and Hyprland (hyprctl) backends.

🤖 Generated with Claude Code

Generic wmctrl/xprop backend for list/focus/activate/move/resize on any EWMH-compliant X11 window manager without a dedicated backend (Cinnamon, MATE, Xfce, Openbox, …). Registered last in the backend order so a session-native backend always wins first.

Also dispatch move_window/resize_window by the resolved window's backend instead of hardcoding the GNOME Shell extension, so geometry ops work on X11 too. doctor now reports window listing/focus available on plain X11 sessions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a generic X11/EWMH window backend using wmctrl and xprop to support window listing, activation, moving, and resizing on standards-compliant X11 window managers. The move_window and resize_window tools are refactored to delegate to the registry, enabling support for both GNOME and X11 backends. Feedback suggests adding an X11 session check to list_windows to avoid executing commands on Wayland, handling the coordinate -1 in wmctrl move commands to prevent it from being ignored as a "preserve" instruction, and making the "N/A" check in the clean helper case-insensitive.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/windowing/backends/x11.rs
Comment thread src/windowing/backends/x11.rs Outdated
Comment thread src/windowing/backends/x11.rs
- list_windows: bail on non-X11 sessions so it never returns XWayland-only windows when a Wayland session falls through to this backend.

- move_window: map a -1 x/y target to -2, since wmctrl -e reads -1 as "preserve current value" and would otherwise drop the move.

- clean: compare "N/A" case-insensitively.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@devthejo

devthejo commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review — all three points addressed in 75a3e65:

  • X11 session check in list_windows: added an is_x11_session() guard that bails on non-X11 sessions. registry::list_windows() tries each backend directly (not gated on probe()), so without this a Wayland session with no native backend could fall through here and return XWayland-only windows; now it errors explicitly instead.
  • wmctrl -e and the -1 sentinel: move_window now maps a -1 x/y target to -2 (wmctrl reads -1 in any field as "preserve current value"), so the move is no longer silently dropped at that coordinate.
  • Case-insensitive N/A: clean() now uses eq_ignore_ascii_case("N/A").

Added unit tests for the coordinate mapping and the N/A/blank handling. cargo test (132) and cargo clippy are green.

@devthejo devthejo marked this pull request as ready for review July 5, 2026 11:26
@devthejo devthejo requested a review from avifenesh as a code owner July 5, 2026 11:26

@avifenesh avifenesh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is an auto review done by revuto.


I found two issues where the new X11 backend is advertised as available beyond what the surrounding diagnostics/focus paths can actually support.

Comment thread src/windowing/backends/x11.rs Outdated
Comment thread src/windowing/registry.rs
probe(): the focused flag (and thus focused_window/activate verification) comes from _NET_ACTIVE_WINDOW via xprop, so advertise can_focus_apps/can_focus_windows only when xprop is on PATH. Listing still works with wmctrl alone.

diagnostics: capabilities.window_control hard-coded the named backend fields and omitted i3 and x11, so it was empty on X11-only sessions even though the registry uses the x11 backend. Read i3/x11 from windowing.backends (tried last).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@avifenesh avifenesh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is an auto review done by revuto.


I found two remaining correctness issues in the X11 backend/diagnostics paths.

Comment thread src/windowing/backends/x11.rs
Comment thread src/diagnostics.rs
devthejo and others added 2 commits July 5, 2026 15:27
…focus unavailable

resize_window: reject width<=0 || height<=0 before wmctrl -e, where a non-positive value is the "preserve current value" sentinel and would report success while leaving a dimension unchanged.

windowing note: when a backend can list windows but cannot verify focus (e.g. wmctrl present but xprop missing on X11, so can_focus_windows=false), say listing-only instead of claiming focused_window and targeted-input verification are available.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@avifenesh avifenesh merged commit 68e33fb into agent-sh:main Jul 5, 2026
@avifenesh

Copy link
Copy Markdown
Collaborator

Thanks @devthejo, nice work. I pushed a small formatting-only commit on top and merged it.

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.

2 participants