Crafter.Graphics/interfaces/Crafter.Graphics-Keys.cppm

478 lines
25 KiB
C++

/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
Catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
#include <linux/input-event-codes.h>
#endif
export module Crafter.Graphics:Keys;
import std;
import :Types;
// Compile-time translation from the abstract `CrafterKeys` enum to the raw
// platform key code that the runtime actually stores and compares. The whole
// point: bindings, events, and InputField never carry the abstract enum at
// runtime — they carry the raw integer the platform delivers. `Key(...)` is
// `consteval` so every call from source folds to an immediate operand; user
// code stays cross-platform (`Key(CrafterKeys::Space)`) without any `#ifdef`
// at the call site.
//
// Domain of the returned KeyCode:
// Win32: bits 0..7 = PS/2 set-1 scancode byte. Bit 8 = extended-key flag
// (the 0xE0-prefixed variants — RightCtrl, RightAlt, the keypad's
// Enter and /, the cursor cluster's arrows/Home/End/etc., the
// Windows keys). At runtime, WndProc OR-s in 0x100 when lParam
// bit 24 is set, so a saved binding for "RightCtrl" (0x11D) survives
// round-trip with the actual hardware event.
// Wayland/Linux: kernel input-event-codes (KEY_*) directly. The +8 X11
// offset is stripped at the wl_keyboard.key boundary so the
// runtime values match the table here.
//
// Keys with no clean scancode encoding (Pause, some keys without standard
// physical position) return 0. 0 is documented as "unmapped"; binding to it
// will never match a real keypress.
export namespace Crafter {
using KeyCode = std::uint32_t;
consteval KeyCode Key(CrafterKeys k) {
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
// PS/2 set-1 scancodes. Extended keys carry 0x100 so the runtime
// path can disambiguate by OR-ing in 0x100 when lParam bit 24 is set.
switch (k) {
// Alphabetic
case CrafterKeys::A: return 0x1E;
case CrafterKeys::B: return 0x30;
case CrafterKeys::C: return 0x2E;
case CrafterKeys::D: return 0x20;
case CrafterKeys::E: return 0x12;
case CrafterKeys::F: return 0x21;
case CrafterKeys::G: return 0x22;
case CrafterKeys::H: return 0x23;
case CrafterKeys::I: return 0x17;
case CrafterKeys::J: return 0x24;
case CrafterKeys::K: return 0x25;
case CrafterKeys::L: return 0x26;
case CrafterKeys::M: return 0x32;
case CrafterKeys::N: return 0x31;
case CrafterKeys::O: return 0x18;
case CrafterKeys::P: return 0x19;
case CrafterKeys::Q: return 0x10;
case CrafterKeys::R: return 0x13;
case CrafterKeys::S: return 0x1F;
case CrafterKeys::T: return 0x14;
case CrafterKeys::U: return 0x16;
case CrafterKeys::V: return 0x2F;
case CrafterKeys::W: return 0x11;
case CrafterKeys::X: return 0x2D;
case CrafterKeys::Y: return 0x15;
case CrafterKeys::Z: return 0x2C;
// Numeric (top row)
case CrafterKeys::_1: return 0x02;
case CrafterKeys::_2: return 0x03;
case CrafterKeys::_3: return 0x04;
case CrafterKeys::_4: return 0x05;
case CrafterKeys::_5: return 0x06;
case CrafterKeys::_6: return 0x07;
case CrafterKeys::_7: return 0x08;
case CrafterKeys::_8: return 0x09;
case CrafterKeys::_9: return 0x0A;
case CrafterKeys::_0: return 0x0B;
// Function keys
case CrafterKeys::F1: return 0x3B;
case CrafterKeys::F2: return 0x3C;
case CrafterKeys::F3: return 0x3D;
case CrafterKeys::F4: return 0x3E;
case CrafterKeys::F5: return 0x3F;
case CrafterKeys::F6: return 0x40;
case CrafterKeys::F7: return 0x41;
case CrafterKeys::F8: return 0x42;
case CrafterKeys::F9: return 0x43;
case CrafterKeys::F10: return 0x44;
case CrafterKeys::F11: return 0x57;
case CrafterKeys::F12: return 0x58;
// Control keys
case CrafterKeys::Escape: return 0x01;
case CrafterKeys::Tab: return 0x0F;
case CrafterKeys::Enter: return 0x1C;
case CrafterKeys::Space: return 0x39;
case CrafterKeys::Backspace: return 0x0E;
case CrafterKeys::Delete: return 0x153; // extended
case CrafterKeys::Insert: return 0x152; // extended
case CrafterKeys::Home: return 0x147; // extended
case CrafterKeys::End: return 0x14F; // extended
case CrafterKeys::PageUp: return 0x149; // extended
case CrafterKeys::PageDown: return 0x151; // extended
case CrafterKeys::CapsLock: return 0x3A;
case CrafterKeys::NumLock: return 0x45;
case CrafterKeys::ScrollLock: return 0x46;
// Modifiers
case CrafterKeys::LeftShift: return 0x2A;
case CrafterKeys::RightShift: return 0x36;
case CrafterKeys::LeftCtrl: return 0x1D;
case CrafterKeys::RightCtrl: return 0x11D; // extended
case CrafterKeys::LeftAlt: return 0x38;
case CrafterKeys::RightAlt: return 0x138; // extended
case CrafterKeys::LeftSuper: return 0x15B; // extended
case CrafterKeys::RightSuper: return 0x15C; // extended
// Arrows (all extended on Win32 — the cursor cluster, not the keypad)
case CrafterKeys::Up: return 0x148;
case CrafterKeys::Down: return 0x150;
case CrafterKeys::Left: return 0x14B;
case CrafterKeys::Right: return 0x14D;
// Keypad
case CrafterKeys::keypad_0: return 0x52;
case CrafterKeys::keypad_1: return 0x4F;
case CrafterKeys::keypad_2: return 0x50;
case CrafterKeys::keypad_3: return 0x51;
case CrafterKeys::keypad_4: return 0x4B;
case CrafterKeys::keypad_5: return 0x4C;
case CrafterKeys::keypad_6: return 0x4D;
case CrafterKeys::keypad_7: return 0x47;
case CrafterKeys::keypad_8: return 0x48;
case CrafterKeys::keypad_9: return 0x49;
case CrafterKeys::keypad_enter: return 0x11C; // extended
case CrafterKeys::keypad_plus: return 0x4E;
case CrafterKeys::keypad_minus: return 0x4A;
case CrafterKeys::keypad_multiply: return 0x37;
case CrafterKeys::keypad_divide: return 0x135; // extended
case CrafterKeys::keypad_decimal: return 0x53;
// Punctuation
case CrafterKeys::grave: return 0x29;
case CrafterKeys::minus: return 0x0C;
case CrafterKeys::equal: return 0x0D;
case CrafterKeys::bracket_left: return 0x1A;
case CrafterKeys::bracket_right: return 0x1B;
case CrafterKeys::backslash: return 0x2B;
case CrafterKeys::semicolon: return 0x27;
case CrafterKeys::quote: return 0x28;
case CrafterKeys::comma: return 0x33;
case CrafterKeys::period: return 0x34;
case CrafterKeys::slash: return 0x35;
case CrafterKeys::print_screen: return 0x137; // extended
case CrafterKeys::pause: return 0; // unmapped — multi-byte sequence
case CrafterKeys::menu: return 0x15D; // extended (App key)
// Multimedia / browser / launch keys (all extended on Win32)
case CrafterKeys::volume_up: return 0x130;
case CrafterKeys::volume_down: return 0x12E;
case CrafterKeys::volume_mute: return 0x120;
case CrafterKeys::media_play: return 0x122;
case CrafterKeys::media_stop: return 0x124;
case CrafterKeys::media_prev: return 0x110;
case CrafterKeys::media_next: return 0x119;
case CrafterKeys::browser_back: return 0x16A;
case CrafterKeys::browser_forward: return 0x169;
case CrafterKeys::browser_refresh: return 0x167;
case CrafterKeys::browser_stop: return 0x168;
case CrafterKeys::browser_search: return 0x165;
case CrafterKeys::browser_home: return 0x132;
case CrafterKeys::launch_mail: return 0x16C;
case CrafterKeys::launch_calculator: return 0x121;
case CrafterKeys::launch_media_player: return 0x16D;
case CrafterKeys::CrafterKeysMax: return 0;
}
#endif
#ifdef CRAFTER_GRAPHICS_WINDOW_DOM
// DOM mode: `KeyboardEvent.code` (a layout-independent physical-key
// string like "KeyA" or "Space") is hashed to a 32-bit KeyCode in
// dom-env.js and again here at compile time. FNV-1a was picked for
// its trivial implementation — collision chance over the ~110-entry
// key set is negligible.
constexpr auto fnv = [](const char* s) constexpr -> KeyCode {
std::uint32_t h = 2166136261u;
while (*s) {
h ^= static_cast<std::uint8_t>(*s++);
h *= 16777619u;
}
return h;
};
switch (k) {
// Alphabetic — KeyboardEvent.code uses "Key" prefix.
case CrafterKeys::A: return fnv("KeyA");
case CrafterKeys::B: return fnv("KeyB");
case CrafterKeys::C: return fnv("KeyC");
case CrafterKeys::D: return fnv("KeyD");
case CrafterKeys::E: return fnv("KeyE");
case CrafterKeys::F: return fnv("KeyF");
case CrafterKeys::G: return fnv("KeyG");
case CrafterKeys::H: return fnv("KeyH");
case CrafterKeys::I: return fnv("KeyI");
case CrafterKeys::J: return fnv("KeyJ");
case CrafterKeys::K: return fnv("KeyK");
case CrafterKeys::L: return fnv("KeyL");
case CrafterKeys::M: return fnv("KeyM");
case CrafterKeys::N: return fnv("KeyN");
case CrafterKeys::O: return fnv("KeyO");
case CrafterKeys::P: return fnv("KeyP");
case CrafterKeys::Q: return fnv("KeyQ");
case CrafterKeys::R: return fnv("KeyR");
case CrafterKeys::S: return fnv("KeyS");
case CrafterKeys::T: return fnv("KeyT");
case CrafterKeys::U: return fnv("KeyU");
case CrafterKeys::V: return fnv("KeyV");
case CrafterKeys::W: return fnv("KeyW");
case CrafterKeys::X: return fnv("KeyX");
case CrafterKeys::Y: return fnv("KeyY");
case CrafterKeys::Z: return fnv("KeyZ");
// Numeric (top row) — DigitN.
case CrafterKeys::_0: return fnv("Digit0");
case CrafterKeys::_1: return fnv("Digit1");
case CrafterKeys::_2: return fnv("Digit2");
case CrafterKeys::_3: return fnv("Digit3");
case CrafterKeys::_4: return fnv("Digit4");
case CrafterKeys::_5: return fnv("Digit5");
case CrafterKeys::_6: return fnv("Digit6");
case CrafterKeys::_7: return fnv("Digit7");
case CrafterKeys::_8: return fnv("Digit8");
case CrafterKeys::_9: return fnv("Digit9");
// Function keys.
case CrafterKeys::F1: return fnv("F1");
case CrafterKeys::F2: return fnv("F2");
case CrafterKeys::F3: return fnv("F3");
case CrafterKeys::F4: return fnv("F4");
case CrafterKeys::F5: return fnv("F5");
case CrafterKeys::F6: return fnv("F6");
case CrafterKeys::F7: return fnv("F7");
case CrafterKeys::F8: return fnv("F8");
case CrafterKeys::F9: return fnv("F9");
case CrafterKeys::F10: return fnv("F10");
case CrafterKeys::F11: return fnv("F11");
case CrafterKeys::F12: return fnv("F12");
// Control keys.
case CrafterKeys::Escape: return fnv("Escape");
case CrafterKeys::Tab: return fnv("Tab");
case CrafterKeys::Enter: return fnv("Enter");
case CrafterKeys::Space: return fnv("Space");
case CrafterKeys::Backspace: return fnv("Backspace");
case CrafterKeys::Delete: return fnv("Delete");
case CrafterKeys::Insert: return fnv("Insert");
case CrafterKeys::Home: return fnv("Home");
case CrafterKeys::End: return fnv("End");
case CrafterKeys::PageUp: return fnv("PageUp");
case CrafterKeys::PageDown: return fnv("PageDown");
case CrafterKeys::CapsLock: return fnv("CapsLock");
case CrafterKeys::NumLock: return fnv("NumLock");
case CrafterKeys::ScrollLock: return fnv("ScrollLock");
// Modifiers.
case CrafterKeys::LeftShift: return fnv("ShiftLeft");
case CrafterKeys::RightShift: return fnv("ShiftRight");
case CrafterKeys::LeftCtrl: return fnv("ControlLeft");
case CrafterKeys::RightCtrl: return fnv("ControlRight");
case CrafterKeys::LeftAlt: return fnv("AltLeft");
case CrafterKeys::RightAlt: return fnv("AltRight");
case CrafterKeys::LeftSuper: return fnv("MetaLeft");
case CrafterKeys::RightSuper: return fnv("MetaRight");
// Arrows.
case CrafterKeys::Up: return fnv("ArrowUp");
case CrafterKeys::Down: return fnv("ArrowDown");
case CrafterKeys::Left: return fnv("ArrowLeft");
case CrafterKeys::Right: return fnv("ArrowRight");
// Keypad.
case CrafterKeys::keypad_0: return fnv("Numpad0");
case CrafterKeys::keypad_1: return fnv("Numpad1");
case CrafterKeys::keypad_2: return fnv("Numpad2");
case CrafterKeys::keypad_3: return fnv("Numpad3");
case CrafterKeys::keypad_4: return fnv("Numpad4");
case CrafterKeys::keypad_5: return fnv("Numpad5");
case CrafterKeys::keypad_6: return fnv("Numpad6");
case CrafterKeys::keypad_7: return fnv("Numpad7");
case CrafterKeys::keypad_8: return fnv("Numpad8");
case CrafterKeys::keypad_9: return fnv("Numpad9");
case CrafterKeys::keypad_enter: return fnv("NumpadEnter");
case CrafterKeys::keypad_plus: return fnv("NumpadAdd");
case CrafterKeys::keypad_minus: return fnv("NumpadSubtract");
case CrafterKeys::keypad_multiply: return fnv("NumpadMultiply");
case CrafterKeys::keypad_divide: return fnv("NumpadDivide");
case CrafterKeys::keypad_decimal: return fnv("NumpadDecimal");
// Punctuation.
case CrafterKeys::grave: return fnv("Backquote");
case CrafterKeys::minus: return fnv("Minus");
case CrafterKeys::equal: return fnv("Equal");
case CrafterKeys::bracket_left: return fnv("BracketLeft");
case CrafterKeys::bracket_right: return fnv("BracketRight");
case CrafterKeys::backslash: return fnv("Backslash");
case CrafterKeys::semicolon: return fnv("Semicolon");
case CrafterKeys::quote: return fnv("Quote");
case CrafterKeys::comma: return fnv("Comma");
case CrafterKeys::period: return fnv("Period");
case CrafterKeys::slash: return fnv("Slash");
case CrafterKeys::print_screen: return fnv("PrintScreen");
case CrafterKeys::pause: return fnv("Pause");
case CrafterKeys::menu: return fnv("ContextMenu");
// Multimedia / browser / launch.
case CrafterKeys::volume_up: return fnv("AudioVolumeUp");
case CrafterKeys::volume_down: return fnv("AudioVolumeDown");
case CrafterKeys::volume_mute: return fnv("AudioVolumeMute");
case CrafterKeys::media_play: return fnv("MediaPlayPause");
case CrafterKeys::media_stop: return fnv("MediaStop");
case CrafterKeys::media_prev: return fnv("MediaTrackPrevious");
case CrafterKeys::media_next: return fnv("MediaTrackNext");
case CrafterKeys::browser_back: return fnv("BrowserBack");
case CrafterKeys::browser_forward: return fnv("BrowserForward");
case CrafterKeys::browser_refresh: return fnv("BrowserRefresh");
case CrafterKeys::browser_stop: return fnv("BrowserStop");
case CrafterKeys::browser_search: return fnv("BrowserSearch");
case CrafterKeys::browser_home: return fnv("BrowserHome");
case CrafterKeys::launch_mail: return fnv("LaunchMail");
case CrafterKeys::launch_calculator: return fnv("LaunchApp2");
case CrafterKeys::launch_media_player: return fnv("LaunchApp1");
case CrafterKeys::CrafterKeysMax: return 0;
}
#endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
// Linux kernel input-event-codes. The Wayland keyboard handler strips
// the +8 X11 offset before delivery so these match wl_keyboard.key
// values directly.
switch (k) {
// Alphabetic
case CrafterKeys::A: return KEY_A;
case CrafterKeys::B: return KEY_B;
case CrafterKeys::C: return KEY_C;
case CrafterKeys::D: return KEY_D;
case CrafterKeys::E: return KEY_E;
case CrafterKeys::F: return KEY_F;
case CrafterKeys::G: return KEY_G;
case CrafterKeys::H: return KEY_H;
case CrafterKeys::I: return KEY_I;
case CrafterKeys::J: return KEY_J;
case CrafterKeys::K: return KEY_K;
case CrafterKeys::L: return KEY_L;
case CrafterKeys::M: return KEY_M;
case CrafterKeys::N: return KEY_N;
case CrafterKeys::O: return KEY_O;
case CrafterKeys::P: return KEY_P;
case CrafterKeys::Q: return KEY_Q;
case CrafterKeys::R: return KEY_R;
case CrafterKeys::S: return KEY_S;
case CrafterKeys::T: return KEY_T;
case CrafterKeys::U: return KEY_U;
case CrafterKeys::V: return KEY_V;
case CrafterKeys::W: return KEY_W;
case CrafterKeys::X: return KEY_X;
case CrafterKeys::Y: return KEY_Y;
case CrafterKeys::Z: return KEY_Z;
// Numeric
case CrafterKeys::_0: return KEY_0;
case CrafterKeys::_1: return KEY_1;
case CrafterKeys::_2: return KEY_2;
case CrafterKeys::_3: return KEY_3;
case CrafterKeys::_4: return KEY_4;
case CrafterKeys::_5: return KEY_5;
case CrafterKeys::_6: return KEY_6;
case CrafterKeys::_7: return KEY_7;
case CrafterKeys::_8: return KEY_8;
case CrafterKeys::_9: return KEY_9;
// Function keys
case CrafterKeys::F1: return KEY_F1;
case CrafterKeys::F2: return KEY_F2;
case CrafterKeys::F3: return KEY_F3;
case CrafterKeys::F4: return KEY_F4;
case CrafterKeys::F5: return KEY_F5;
case CrafterKeys::F6: return KEY_F6;
case CrafterKeys::F7: return KEY_F7;
case CrafterKeys::F8: return KEY_F8;
case CrafterKeys::F9: return KEY_F9;
case CrafterKeys::F10: return KEY_F10;
case CrafterKeys::F11: return KEY_F11;
case CrafterKeys::F12: return KEY_F12;
// Control keys
case CrafterKeys::Escape: return KEY_ESC;
case CrafterKeys::Tab: return KEY_TAB;
case CrafterKeys::Enter: return KEY_ENTER;
case CrafterKeys::Space: return KEY_SPACE;
case CrafterKeys::Backspace: return KEY_BACKSPACE;
case CrafterKeys::Delete: return KEY_DELETE;
case CrafterKeys::Insert: return KEY_INSERT;
case CrafterKeys::Home: return KEY_HOME;
case CrafterKeys::End: return KEY_END;
case CrafterKeys::PageUp: return KEY_PAGEUP;
case CrafterKeys::PageDown: return KEY_PAGEDOWN;
case CrafterKeys::CapsLock: return KEY_CAPSLOCK;
case CrafterKeys::NumLock: return KEY_NUMLOCK;
case CrafterKeys::ScrollLock: return KEY_SCROLLLOCK;
// Modifiers
case CrafterKeys::LeftShift: return KEY_LEFTSHIFT;
case CrafterKeys::RightShift: return KEY_RIGHTSHIFT;
case CrafterKeys::LeftCtrl: return KEY_LEFTCTRL;
case CrafterKeys::RightCtrl: return KEY_RIGHTCTRL;
case CrafterKeys::LeftAlt: return KEY_LEFTALT;
case CrafterKeys::RightAlt: return KEY_RIGHTALT;
case CrafterKeys::LeftSuper: return KEY_LEFTMETA;
case CrafterKeys::RightSuper: return KEY_RIGHTMETA;
// Arrows
case CrafterKeys::Up: return KEY_UP;
case CrafterKeys::Down: return KEY_DOWN;
case CrafterKeys::Left: return KEY_LEFT;
case CrafterKeys::Right: return KEY_RIGHT;
// Keypad
case CrafterKeys::keypad_0: return KEY_KP0;
case CrafterKeys::keypad_1: return KEY_KP1;
case CrafterKeys::keypad_2: return KEY_KP2;
case CrafterKeys::keypad_3: return KEY_KP3;
case CrafterKeys::keypad_4: return KEY_KP4;
case CrafterKeys::keypad_5: return KEY_KP5;
case CrafterKeys::keypad_6: return KEY_KP6;
case CrafterKeys::keypad_7: return KEY_KP7;
case CrafterKeys::keypad_8: return KEY_KP8;
case CrafterKeys::keypad_9: return KEY_KP9;
case CrafterKeys::keypad_enter: return KEY_KPENTER;
case CrafterKeys::keypad_plus: return KEY_KPPLUS;
case CrafterKeys::keypad_minus: return KEY_KPMINUS;
case CrafterKeys::keypad_multiply: return KEY_KPASTERISK;
case CrafterKeys::keypad_divide: return KEY_KPSLASH;
case CrafterKeys::keypad_decimal: return KEY_KPDOT;
// Punctuation
case CrafterKeys::grave: return KEY_GRAVE;
case CrafterKeys::minus: return KEY_MINUS;
case CrafterKeys::equal: return KEY_EQUAL;
case CrafterKeys::bracket_left: return KEY_LEFTBRACE;
case CrafterKeys::bracket_right: return KEY_RIGHTBRACE;
case CrafterKeys::backslash: return KEY_BACKSLASH;
case CrafterKeys::semicolon: return KEY_SEMICOLON;
case CrafterKeys::quote: return KEY_APOSTROPHE;
case CrafterKeys::comma: return KEY_COMMA;
case CrafterKeys::period: return KEY_DOT;
case CrafterKeys::slash: return KEY_SLASH;
case CrafterKeys::print_screen: return KEY_SYSRQ;
case CrafterKeys::pause: return KEY_PAUSE;
case CrafterKeys::menu: return KEY_COMPOSE;
// Multimedia / browser / launch keys
case CrafterKeys::volume_up: return KEY_VOLUMEUP;
case CrafterKeys::volume_down: return KEY_VOLUMEDOWN;
case CrafterKeys::volume_mute: return KEY_MUTE;
case CrafterKeys::media_play: return KEY_PLAYPAUSE;
case CrafterKeys::media_stop: return KEY_STOPCD;
case CrafterKeys::media_prev: return KEY_PREVIOUSSONG;
case CrafterKeys::media_next: return KEY_NEXTSONG;
case CrafterKeys::browser_back: return KEY_BACK;
case CrafterKeys::browser_forward: return KEY_FORWARD;
case CrafterKeys::browser_refresh: return KEY_REFRESH;
case CrafterKeys::browser_stop: return KEY_STOP;
case CrafterKeys::browser_search: return KEY_SEARCH;
case CrafterKeys::browser_home: return KEY_HOMEPAGE;
case CrafterKeys::launch_mail: return KEY_MAIL;
case CrafterKeys::launch_calculator: return KEY_CALC;
case CrafterKeys::launch_media_player: return KEY_MEDIA;
case CrafterKeys::CrafterKeysMax: return 0;
}
#endif
return 0;
}
}