Skip to content

Add Wayland-compatible active workspace detection - #9

Merged
seb4stien merged 13 commits into
mainfrom
feat/native-wayland
Sep 8, 2026
Merged

seb4stien merged 13 commits into
mainfrom
feat/native-wayland

Conversation

@seb4stien

Copy link
Copy Markdown
Owner

Summary

et.workspaces.get_active_workspace_index() shelled out to wmctrl, which relies on the X11 EWMH window-manager protocol and no longer reliably reports the active workspace under GNOME's Wayland session (the default since Ubuntu 26.04).

There's no safe built-in D-Bus API for this on stock GNOME Shell:

  • org.gnome.Shell.Eval can run arbitrary JS but is gated by unsafe_mode, off by default.
  • org.gnome.Shell.Introspect.GetWindows exposes per-window info but no workspace index field.
  • Third-party window-based extensions (e.g. "Window Calls") can only infer the active workspace from a focused window, which fails for empty workspaces — a normal state for et's task workspaces.

What changed

  • New companion GNOME Shell extension, gnome-extension/et@seb4stien.github.com/, exposing GetActiveWorkspaceIndex() over D-Bus (/org/gnome/Shell/Extensions/Et), backed by global.workspace_manager — gives parity with wmctrl -d, including on empty workspaces. Intentionally generic so it can later absorb Tracker's time-tracking role too.
  • scripts/install-gnome-shell-extension.sh symlinks + enables the extension; wired into just install-requirements. Also added the standard [private] default Justfile recipe.
  • src/et/workspaces.py: detects the session type (XDG_SESSION_TYPE/WAYLAND_DISPLAY) and dispatches get_active_workspace_index() to the new D-Bus call on Wayland, keeping the existing wmctrl -d path unchanged on X11.
  • switch_to_workspace/move_active_window_to_workspace are intentionally left on wmctrl for now (still X11-only; noted as a follow-up).
  • README/CHANGELOG updated with the new Wayland requirement and rationale.
  • Unit tests for session detection and both dispatch/error paths; integration test simulating a Wayland session.

Testing

  • uv run pytest — 443 passed
  • uv run ruff check . — clean
  • uv run mypy src — clean
  • Attempted live verification against a real running GNOME Shell 46.3.1/Wayland session in the dev sandbox; confirmed Eval is disabled as expected, but the sandbox's org.gnome.Shell.Extensions D-Bus service isn't wired to the live compositor there, so full end-to-end confirmation needs a real desktop session (log out/in after running the install script).

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

seb4stien and others added 13 commits September 2, 2026 18:48
wmctrl relies on the X11 EWMH window-manager protocol and no longer
reliably reports the active workspace under GNOME's Wayland session
(the default since Ubuntu 26.04). There is no safe built-in D-Bus API
for this: org.gnome.Shell.Eval is gated by unsafe_mode (off by
default), and org.gnome.Shell.Introspect.GetWindows exposes no
workspace field, while third-party window-based extensions can't
detect an empty active workspace.

Ship a small companion GNOME Shell extension
(gnome-extension/et@seb4stien.github.com) that exposes
GetActiveWorkspaceIndex() over D-Bus, backed by
global.workspace_manager, giving parity with `wmctrl -d` including on
empty workspaces. It's intentionally generic so it can later absorb
Tracker's time-tracking role too.

- scripts/install-gnome-shell-extension.sh symlinks and enables the
  extension; wired into `just install-requirements`. Also add the
  standard `[private] default` Justfile recipe.
- workspaces.py: detect the session type (XDG_SESSION_TYPE /
  WAYLAND_DISPLAY) and dispatch get_active_workspace_index() to the new
  D-Bus call on Wayland, keeping the existing wmctrl -d path on X11.
  switch_to_workspace/move_active_window_to_workspace are unchanged
  (still wmctrl-only; follow-up noted in the plan).
- Update README/CHANGELOG with the new requirement.
- Add unit tests for session detection and both dispatch/error paths,
  and an integration test simulating a Wayland session.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A running GNOME Shell only scans
~/.local/share/gnome-shell/extensions for new UUIDs at startup, so
`gnome-extensions enable` (which talks to the live shell process)
fails with "Extension ... does not exist" for a brand-new extension
until the shell restarts/relogs in — even though the directory and
metadata.json are perfectly valid. Confirmed this against a live
GNOME Shell 46.3.1/Wayland session: already-known extensions (e.g.
Tracker) resolve fine via the same D-Bus call, but ours didn't until
after being registered as enabled directly.

scripts/install-gnome-shell-extension.sh now falls back to writing
the same `org.gnome.shell enabled-extensions` gsettings key `enable`
would set when the live call fails, so the extension starts enabled
the next time GNOME Shell (re)loads, and always exits 0 with a clear
next-step message instead of aborting hard on a misleading error.
Idempotent (no duplicate UUID) whichever path is taken.

Also clarifies this behavior in the README.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Symlinking races with GNOME Shell's one-time startup scan of the
extensions directory: if the checkout is re-provisioned on a timeline
independent of the GNOME session, a symlink whose target doesn't yet
exist at scan time gets silently skipped until an unrelated restart.
Copying avoids the race; --dev/--symlink opts back in for iterating
on extension.js. Also distinguish "never scanned" from "scanned but
errored" via a GetExtensionInfo D-Bus query.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the legacy tracker integration with persistent GNOME Shell counters, display preferences, isolated manual testing, and automated GNOME 46/50 coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prepare feat/native-wayland as a release candidate for v1.0.0: retitle
the CHANGELOG's Unreleased section to [1.0.0-rc.1] and bump
pyproject.toml/uv.lock accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… config

test_create_task_workspace_wraps_extension_error and
test_complete_task_wraps_extension_error_freeing_slot exercised failure
paths that call the real et.task.save_config before the mocked extension
call raises. Without ET_CONFIG_DIR overridden, this wrote fake test data
over the user's real ~/.config/et/config.yaml. Add the missing
@patch("et.task.save_config") to both tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add "et config" interactive wizard to populate ~/.config/et/config.yaml
  (Jira block + workspaces add/edit/remove loop, with credential check
  and retry/keep/discard on failure). New workspace entries default to
  static type and skip the Jira ref prompt when static.
- Rename jira._check_credentials to public check_credentials for reuse.
- Split README.md: extract developer workflow into CONTRIBUTING.md and
  design/architecture rationale into docs/ARCHITECTURE.md.
- Add a "Getting started" section to README with a step-by-step
  onboarding flow using explicit apt install commands (instead of
  `just install-requirements`, which also pulls in dev/test tooling),
  installing uv via its official installer, running the extension
  install script directly, and a final `uv tool install .` step.
- Remove wmctrl/X11 references from README (support to be deprecated).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the long per-command prose paragraphs under "Tasks" and "Git"
with a single intro line plus the already-informative example command
list (comments document what each does), pointing to --help for full
details. Cuts README.md from 449 to 317 lines with no loss of substance,
since --help already documents each subcommand in full.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the now-redundant standalone "Installation"/"Usage" sections
(covered by "Getting started"), the extension --dev/--symlink aside
(covered in CONTRIBUTING.md/ARCHITECTURE.md), and shorten the
Configuration/Workspaces intros, since `et config`/`et --help`/`et ws
--help` already document that ground. Rename subsection headers for
consistency ("Workspaces features", "Jira features", "Git features").

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Newer shellcheck versions (as used by CI, 0.11.0) added SC2329
("function is never invoked") as a separate check from SC2317. The
existing `# shellcheck disable=SC2317` comments on the `cleanup()`
functions invoked only via `trap cleanup EXIT` no longer suffice, so
CI's "Verify scripts" step started failing. Add SC2329 to the same
disable directives.

Verified with shellcheck 0.11.0 (matching CI): scripts/*.sh
scripts/lib/*.sh now pass with exit code 0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@seb4stien
seb4stien merged commit 503f462 into main Sep 8, 2026
3 checks passed
@seb4stien
seb4stien deleted the feat/native-wayland branch September 8, 2026 14:21
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.

1 participant