Skip to content

feat: add globalShortcut.listShortcuts()#52239

Draft
codebytere wants to merge 3 commits into
mainfrom
feat/global-shortcuts-portal-list
Draft

feat: add globalShortcut.listShortcuts()#52239
codebytere wants to merge 3 commits into
mainfrom
feat/global-shortcuts-portal-list

Conversation

@codebytere

@codebytere codebytere commented Jul 2, 2026

Copy link
Copy Markdown
Member

Description of Change

On the GlobalShortcuts portal path an app can't tell whether globalShortcut.register() will interrupt the user — compositors surface approval as a dialog, or on KDE Plasma 6 by auto-approving and raising System Settings on every new bind. The portal exposes ListShortcuts for exactly this, but nothing reaches it from app code. Add globalShortcut.listShortcuts(), resolving with the shortcuts currently registered for the app — including approvals from previous sessions — so apps can check before registering. Rejects when registration isn't portal-handled (Windows/macOS/X11/GNOME) or the portal is unavailable.

Queries run through a dedicated portal session built on the public components/dbus helpers rather than through ui::GlobalAcceleratorListener. Two compositor behaviors, both confirmed on a Plasma 6.3 rig with dbus-monitor and portal journal captures, dictate the session's shape. xdg-desktop-portal resolves the caller's app id from the systemd scope cgroup and caches it per connection at first contact; Chromium's cgroup migration is async, so a bare service check races it and KDE then keys shortcuts by session token instead of app id — the lister gates on dbus_xdg::RequestXdgDesktopPortal(), which sequences the scope setup before first contact. And all of an app's portal sessions share one kglobalaccel component whose actions xdg-desktop-portal-kde deactivates when any of them closes, so a throwaway query session's Close would permanently kill the app's registered shortcuts — the query session is created lazily, reused for the process lifetime, and never explicitly closed. Concurrent callers coalesce into one in-flight query, bounded by a watchdog since dbus_xdg::Request has no timeout on its Response-signal wait.

This surfaced a bind-state hole in GlobalAcceleratorListenerLinux, kept as the sole (upstreamable) chromium patch: when ListShortcuts shows every command already registered, bind_state_ stays kNotBound, and OnCommandsChanged has no branch for that state with a live session — a later register() returns true but no bind pass ever runs and the shortcut never fires. Mark such sessions bound so the next commands change re-runs the bind flow.

cc @VerteDinde

Checklist

Release Notes

Notes: Added globalShortcut.listShortcuts() to query shortcuts registered with the GlobalShortcuts desktop portal on Linux.

Allows apps on Linux to query the shortcuts already registered with the
GlobalShortcuts desktop portal, including ones approved in previous
sessions, so they can tell whether registering a shortcut will interrupt
the user before calling register().

Queries go through a dedicated long-lived portal session built on the
public components/dbus helpers: created lazily after
dbus_xdg::RequestXdgDesktopPortal() resolves the app identity (contacting
the portal earlier caches an empty app id per connection, fragmenting
approvals on KDE), never explicitly closed (xdg-desktop-portal-kde
deactivates all of an app's shortcuts when any of its sessions closes),
coalescing concurrent callers into one in-flight query, with a watchdog
bounding each step of the cycle.

Also adds a 6-line chromium patch fixing GlobalAcceleratorListenerLinux:
sessions whose commands are all already registered with the portal are
marked bound so a later registration re-runs the bind pass instead of
silently never binding.
@electron-cation electron-cation Bot added the new-pr 🌱 PR opened recently label Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new-pr 🌱 PR opened recently

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant