Register global shortcuts - fakeivchenko/lwjwae GitHub Wiki

A global shortcut reaches the application whichever application has the keyboard, such as the keys that bring a quick note window to the front. In this guide, Ctrl+Shift+Space brings the window back and tells the page when.

A quick note window that two presses of the shortcut brought back, on Linux and on Windows 11

Register a shortcut

public class Shortcuts {
  private static final DateTimeFormatter TIME = DateTimeFormatter.ofPattern("HH:mm:ss");

  public static void main(String[] args) {
    Application.launch(
        WindowParameters.of("Quick note", 560, 380),
        window -> {
          window
              .application()
              .globalShortcut(
                  "CmdOrCtrl+Shift+Space",
                  () -> {
                    window.focus();
                    window.emit("summoned", LocalTime.now().format(TIME));
                  });
          window.loadResource("shortcuts/index.html");
        });
  }
}

The page hears the event and puts the cursor into the note:

lwjwae.listen("summoned", ({ payload }) => {
  document.querySelector("#log").prepend(Object.assign(document.createElement("li"), { textContent: `Summoned at ${payload}` }));
  document.querySelector("#note").focus();
});

globalShortcut returns an EventSubscription. Call close() on it to give the shortcut back; otherwise, the shortcut stays with the application until it closes. The handler runs off the UI thread, so it can block or call back into a window.

Write a shortcut

A shortcut is the modifiers and then the key, joined with +, in any case:

Text Keys
Ctrl+Shift+K Ctrl, Shift, and K
CmdOrCtrl+Alt+Space Command on macOS or Ctrl elsewhere, Alt, and Space
F13 F13 alone

The modifiers are Ctrl or Control, Alt or Option, Shift, Meta, Super, Win, Cmd or Command, and CmdOrCtrl or CommandOrControl. A key other than a function key needs a modifier: a shortcut takes its key away from every other application, so a letter alone could no longer be typed. Shortcut.parse reads the text, and Shortcut.of(key, modifiers...) builds a shortcut without one.

When the keys are taken

If another application already holds the shortcut, globalShortcut throws ShortcutUnavailableException. Catch it and offer the user other keys:

try {
  application.globalShortcut("CmdOrCtrl+Shift+Space", window::focus);
} catch (ShortcutUnavailableException e) {
  window.emit("shortcutTaken", e.getMessage());
}

Platforms

Platform How the shortcut is registered
Windows RegisterHotKey. A shortcut that Windows keeps, such as Win+L, throws too. Holding the keys down runs the handler once.
macOS RegisterEventHotKey of Carbon, which needs no permission of the user. A shortcut of the system, such as Command+Space for Spotlight, can still go to the system first.
Linux, X11 A grab of the key on the root window.
Linux, Wayland The GlobalShortcuts portal of KDE Plasma, and of GNOME 48 and later. The desktop asks the user to confirm the shortcuts the first time, and the user can pick other keys. Without the portal, the call throws UnsupportedOperationException.

Good to know

  • On Wayland, globalShortcut returns before the user confirms, and a shortcut that the user turns down never runs.
  • window.focus() from the handler brings the window to the front where the portal hands on the activation token of the key press, as GNOME does. KDE Plasma hands none on, so there the window only asks for attention in the taskbar.
  • Keys that only matter while a window of the application has the keyboard belong in a menu entry with an accelerator; see Add menus.
⚠️ **GitHub.com Fallback** ⚠️