# How to Handle Keyboard Input This how-to guide will show you the basics of handling keyboard input in Mir based compositors. Handling keyboard input allows for great flexibility in your compositor. For example, it's widely known that ALT + F4 closes the currently open application. Some window managers take this a step further by allowing all navigation between windows to be done via the keyboard. We'll implement a couple of shortcuts: - CTRL + ALT + T / CTRL + ALT + SHIFT + T: To launch a terminal emulator - CTRL + ALT + Backspace: To stop the compositor For ease of reading (and copying), we'll split the changes into three parts: headers, code, and options. **Note**: [Write Your First Wayland Compositor](../write-your-first-wayland-compositor.md) is a prerequisite for this how-to. ### Header changes Here we just include `append_event_filter.h` for the declaration of `AppendEventFilter` and `toolkit_event` to get definitions for event types and functions. We import everything from `miral::toolkit` to make the code a bit easier to read. ```diff @@ -1,10 +1,15 @@ #include +#include #include #include +#include using namespace miral; +using namespace miral::toolkit; ``` ### Code changes This big block of code can be broken down into three parts: 1. Declaring the name of the terminal we'll use and an external client launcher that we'll use to launch it. 2. Filtering Mir events until we obtain a keyboard key down event. 3. Handling the combinations we want. ```diff + std::string terminal_cmd{"kgx"}; + miral::ExternalClientLauncher external_client_launcher; + + auto const builtin_keybinds = [&](MirEvent const* event) + { + // Skip non-input events + if (mir_event_get_type(event) != mir_event_type_input) + return false; + + // Skip non-key input events + MirInputEvent const* input_event = mir_event_get_input_event(event); + if (mir_input_event_get_type(input_event) != mir_input_event_type_key) + return false; + + // Skip anything but down presses + MirKeyboardEvent const* kev = mir_input_event_get_keyboard_event(input_event); + if (mir_keyboard_event_action(kev) != mir_keyboard_action_down) + return false; + + // CTRL + ALT must be pressed + MirInputEventModifiers mods = mir_keyboard_event_modifiers(kev); + if (!(mods & mir_input_event_modifier_alt) || !(mods & mir_input_event_modifier_ctrl)) + return false; + + switch (mir_keyboard_event_keysym(kev)) + { + // Exit program + case XKB_KEY_BackSpace: + runner.stop(); + return true; + + // Launch terminal + case XKB_KEY_t: + case XKB_KEY_T: + external_client_launcher.launch({terminal_cmd}); + return false; + + // Do nothing + default: + return false; + }; + }; ``` ### Options changes Here we simply add the external client launcher we declared in the previous subsection, and an event filter that uses the code we specified in the previous subsection. ```diff return runner.run_with( { set_window_management_policy(), + external_client_launcher, + miral::AppendEventFilter{builtin_keybinds} }); } ``` This can be easily extended to read keybinds from config files or control various other aspects of the compositor. The only limit here is your imagination.