Hotkeys API

From vice-emu
Jump to navigation Jump to search

The "new" hotkeys system as used in the Gtk3 UI is described here. The plan is to make this the only API for hotkeys in VICE, porting SDL over to this system and having any future UIs/ports use this as well.


What is referred to here as hotkeys are keyboard shortcuts to activate UI elements such as dialogs and control emulator behavior (such as Warp mode, Pause, Reset, Quit). Another term for these is "(keyboard) accelerators" as used by Gtk+. I'll try to use "hotkeys" for VICE's API and "accelerators" for the Gtk+ implementation, but don't shoot me if I mix them up somewhere down the line.

The name VICE in the context of the hotkeys API means the generic code in src/arch/shared/ while the name arch is used for architecture/OS/UI-specific code.

A keysym is an unsigned 32-bit integer value identifying a single key, for example VHK_KEY_Return (0xff0d). A key name is a string identifying a key, such as "a" or "Adiaeresis", these are used in the .vhk files.

A modifier is an unsigned 32-bit integer value identifying a single modifier key, such as VHK_MOD_ALT (0x0001), a modifier mask is a bitwise-ORed combination of modifiers, for example VHK_MOD_CONTROL|VHK_MOD_ALT (0x0005). A modifier name is a string used in the .vhk files to refer to modifiers, such as "<Alt>" or "<Command>".

Basic concepts

The hotkeys system is closely linked with the UI Actions system. In the Gtk3 UI the hotkeys are mapped to UI action IDs when parsing hotkey (.vhk) files. For any menu item that triggers a UI action the hotkey is used to set an accelerator label on the item (e.g. "Alt+Z"), but hotkeys can also be assigned to UI actions that do not have a corresponding menu item.

In the Gtk3 UI the accelerator labels on the menu items are cosmetic, we use GClosures for hotkey signal handlers so they also work in fullscreen mode and without a corresponding menu item.

A hotkey is in essence a simple key with optional modifier keys that maps to a UI action. Here's a snippet of a hotkey (data/C64/gtk3-hotkeys.vhk) file:

# "File" - monitor, reset, quit
monitor-open        <Alt>h
reset-soft          <Alt>F9
reset-hard          <Alt>F12
quit                <Alt>q

(todo: add some fake syntax highlighting with CSS to the example)

The first column contains UI action names, as declared in uiactions.c, and the second column contains key names with (optional) modifiers. So the "reset-soft" action can be triggered with Alt+F9. Modifier keys are referred to with angular brackets and key names are simple strings, taken from X11's keysymdef.h header, with the XK_ prefix stripped.


The VICE src/arch/shared/hotkeys/vhkkeysyms.h header contains copies of some of the X11 defines -- which are referred to in the code as "keysyms -- prefixed with VHK_ instead of XK_, as seen in this small excerpt:

#define VHK_KEY_KP_Separator            0xffac  /* Separator, often comma */
#define VHK_KEY_KP_Subtract             0xffad
#define VHK_KEY_KP_Decimal              0xffae
#define VHK_KEY_KP_Divide               0xffaf


Modifiers are special keys like Alt, Control and Shift. The defines for the modifiers are also present in the vhkkeysyms.h header:

#define VHK_MOD_NONE    0x0000
#define VHK_MOD_ALT     0x0001
#define VHK_MOD_COMMAND 0x0002
#define VHK_MOD_CONTROL 0x0004
#define VHK_MOD_HYPER   0x0008
#define VHK_MOD_META    0x0010
#define VHK_MOD_OPTION  0x0020
#define VHK_MOD_SHIFT   0x0040
#define VHK_MOD_SUPER   0x0080

Combining these defines with OR results in a modmask.

Code using the hotkeys API can translate between modifier values and strings with the functions declared in the vhkkeysyms.h header:

uint32_t    vhk_keysym_from_name  (const char *name);
const char *vhk_keysym_name       (uint32_t vice_keysym);
uint32_t    vhk_modifier_from_name(const char *name, const char **endptr);
char       *vhk_modmask_name      (uint32_t vice_modmask);


The hotkeys system is split into a few distinct components:

First there's the basic framework already in place that does most of the heavy lifting, that code resides in src/arch/shared/hotkeys/ and is UI toolkit and OS/architecture-agnostic. This will be referred to as the VICE side of the code, the API refers to VICE-specific defines and functions with a vice affix, for example a function argument called vice_keysym or the function ui_hotkeys_vhk_filename_vice().

Second there's the arch-specific code that needs to be implemented for each arch/UI using the hotkeys API. This code should be placed in src/arch/ui-name, for example the Gtk3-specific hotkeys code resides in src/arch/gtk3/. The API refers to the UI/arch/OS-specific bits with an arch affix.

The arch-specific code is required to implement a few "virtual" functions for VICE to use, the prototypes for these are available in src/uiapi.h.
At present the required functions are:

void ui_hotkeys_arch_init(void);
void ui_hotkeys_arch_shutdown(void);

void ui_hotkeys_arch_install_by_map(vhk_map_t *map);
void ui_hotkeys_arch_update_by_map (vhk_map_t *map,
                                    uint32_t vice_keysym,
                                    uint32_t vice_modmask);
void ui_hotkeys_arch_remove_by_map (vhk_map_t *map);

uint32_t ui_hotkeys_arch_keysym_from_arch  (uint32_t arch_keysym);
uint32_t ui_hotkeys_arch_keysym_to_arch    (uint32_t vice_keysym);
uint32_t ui_hotkeys_arch_modifier_from_arch(uint32_t arch_mod);
uint32_t ui_hotkeys_arch_modifier_to_arch  (uint32_t vice_mod);
uint32_t ui_hotkeys_arch_modmask_from_arch (uint32_t arch_modmask);
uint32_t ui_hotkeys_arch_modmask_to_arch   (uint32_t vice_modmask);

More on these later.