Skip to main content

global-shortcut-portal

A pure-Python library for the Wayland Global Shortcut Portal (org.freedesktop.portal.GlobalShortcuts).
Lets any application register and receive global keyboard shortcuts on Wayland, without X11 key grabbing.

AI was used heavily during development, with human review and testing of all code.
This is a personal library I wanted and I'm sharing it in case it's useful to others.

Requirements

  • Python >= 3.10
  • dbus-next (pure Python, no C extensions)
  • A Wayland compositor with a Global Shortcuts portal backend (KDE Plasma 6+, GNOME 48+, Hyprland, etc.)

Installation

pip install global-shortcut-portal

On systems with an externally managed environment (e.g. recent Debian/Ubuntu, Fedora, Arch Linux with system Python) use pip install --user or a virtual environment. Alternatively, install with uv:

uv pip install global-shortcut-portal

For development, clone the repo and run:

uv sync --group dev

Reference Example

The repository includes a fully-commented reference app at examples/reference_example_app.py that demonstrates the complete session lifecycle with interactive controls:

Key Action
b Bind example shortcuts with default triggers
a Grow the list: bind a third shortcut (resets session)
f Force empty: two reset+bind rounds that remove shortcuts
e Register shortcuts without triggers
l List bound shortcuts
c Open the native config dialog
r Reset the session (needed before re-binding)
q Quit
python examples/reference_example_app.py

Flatpak

xdg-desktop-portal was built for sandboxed apps, so this library runs unchanged inside a Flatpak sandbox — portal access is granted by default, no D-Bus permissions needed (see docs/flatpak.md). The only difference is the app ID: the portal attributes shortcuts to the sandbox app ID ($FLATPAK_ID). The library ships small detection helpers (is_flatpak(), flatpak_id(), portal_app_id(), session_type()) for apps that need to know.

A ready-to-build Flatpak for the reference example lives in flatpak/:

flatpak-builder --force-clean --user --install flatpak/build \
  flatpak/page.codeberg.marvin1099.GlobalShortcutPortalExample.json
flatpak run page.codeberg.marvin1099.GlobalShortcutPortalExample

Documentation

Quick Start

import asyncio
from global_shortcut_portal import (
    GlobalShortcutsSession,
    Portal,
    Shortcut,
    SessionCallback,
)


class MyCallback(SessionCallback):
    def on_activated(self, event):
        print(f"Shortcut activated: {event.shortcut_id}")

    def on_deactivated(self, event):
        print(f"Shortcut deactivated: {event.shortcut_id}")


async def main():
    portal = Portal()
    await portal.connect()

    session = GlobalShortcutsSession(
        portal,
        app_id="org.example.MyApp",
        callback=MyCallback(),
    )
    await session.connect()

    shortcuts = [
        Shortcut(
            id="toggle-overlay",
            description="Toggle overlay window",
            preferred_trigger="CTRL+ALT+SPACE",
        ),
    ]
    bound = await session.bind(shortcuts)
    for s in bound:
        print(f"Bound: {s.id} -> {s.trigger_description}")

    await asyncio.Event().wait()


asyncio.run(main())

Features

  • Async API via dbus-next (pure Python asyncio D-Bus library)
  • Session life-cycle management (create, bind, list, configure, close)
  • Supports Registry.Register for xdg-desktop-portal >= 1.20
  • Full signal handling (Activated, Deactivated, ShortcutsChanged)
  • Shortcut trigger parsing and formatting (XDG shortcuts specification)
  • Version 2 portal features (ConfigureShortcuts)

Notes

  • BindShortcuts is only allowed once per session. There is no portal method to unbind or update a bound shortcut; use the native config dialog or create a new session to change the set.
  • Desktop environment persistence: Some DEs (notably Plasma/KDE) persist shortcut triggers per app_id. A reset session + rebind works per spec: the new bind set replaces the old one, so a shortcut missing from the new set is removed. But a shortcut that is still bound (same ID) keeps its stored trigger — to change one, first bind a set without it, then reset again and rebind the full set with the new trigger.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

global_shortcut_portal-0.2.0.tar.gz (28.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

global_shortcut_portal-0.2.0-py3-none-any.whl (24.5 kB view details)

Uploaded Python 3

File details

Details for the file global_shortcut_portal-0.2.0.tar.gz.

File metadata

  • Download URL: global_shortcut_portal-0.2.0.tar.gz
  • Upload date:
  • Size: 28.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Garuda Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for global_shortcut_portal-0.2.0.tar.gz
Algorithm Hash digest
SHA256 2bc4e79d5130c68de9f59e115fb6c660ca9e9bf2a9b61042c09e9f075e8ec5ac
MD5 75d65148cf26ad40b6374013fee48b77
BLAKE2b-256 451a211be4e6f0cabdbd68c7895b018682f203f924ab646e99450412c3ad6269

See more details on using hashes here.

File details

Details for the file global_shortcut_portal-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: global_shortcut_portal-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 24.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Garuda Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for global_shortcut_portal-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 50c779d9bc846046256b323e58aab314cc97d5891f2f97cc201c311d37d579cc
MD5 715d256ceff5ab330f5c5e31ecaedb7e
BLAKE2b-256 db74385820f855cbf6f95b134020eabc6cc0a109e6b336d68c0df82b2761e938

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.1

2 files

This release

0.2.0 This release

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page