From 251beb34720d2e7d60ceaddc811a65f52f15bdbd Mon Sep 17 00:00:00 2001
From: HampusM <hampus@hampusmat.com>
Date: Wed, 6 Mar 2024 22:15:33 +0100
Subject: feat(glfw): add setting key callback

---
 glfw/src/util.rs   |  31 +++++
 glfw/src/window.rs | 346 +++++++++++++++++++++++++++++++++--------------------
 2 files changed, 246 insertions(+), 131 deletions(-)

(limited to 'glfw/src')

diff --git a/glfw/src/util.rs b/glfw/src/util.rs
index f77aaf8..98dcb9b 100644
--- a/glfw/src/util.rs
+++ b/glfw/src/util.rs
@@ -8,3 +8,34 @@ pub fn is_main_thread() -> bool
 
     ttid == pid
 }
+
+macro_rules! enum_from_repr {
+    (
+        #[repr($repr: ident)]
+        $(#[$attr: meta])*
+        $visibility: vis enum $name: ident
+        {
+            $($variant: ident = $raw: path,)*
+        }
+    ) => {
+        $(#[$attr])*
+        #[repr($repr)]
+        $visibility enum $name
+        {
+            $($variant = $raw,)*
+        }
+
+        impl $name
+        {
+            fn from_repr(repr: $repr) -> Option<Self>
+            {
+                match repr {
+                    $($raw => Some(Self::$variant),)*
+                    _ => None
+                }
+            }
+        }
+    };
+}
+
+pub(crate) use enum_from_repr;
diff --git a/glfw/src/window.rs b/glfw/src/window.rs
index 4c7652c..8059cea 100644
--- a/glfw/src/window.rs
+++ b/glfw/src/window.rs
@@ -1,10 +1,14 @@
 use std::cell::RefCell;
 use std::ffi::{c_int, CString};
 use std::hint::unreachable_unchecked;
+use std::io::{stdout, Write};
 use std::panic::catch_unwind;
 use std::ptr::null_mut;
 
+use bitflags::bitflags;
+
 use crate::init::{initialize, Glfw};
+use crate::util::enum_from_repr;
 use crate::{get_glfw_error, Error};
 
 #[derive(Debug)]
@@ -129,6 +133,22 @@ impl Window
         }
     }
 
+    pub fn set_key_callback(
+        &self,
+        callback: impl Fn(Key, i32, KeyState, KeyModifiers) + 'static,
+    )
+    {
+        KEY_CALLBACK.with_borrow_mut(|key_callback| {
+            *key_callback = Some(Box::new(callback));
+        });
+
+        // SAFETY: The initialize function (called when the window is created) makes sure
+        // the current thread is the main thread
+        unsafe {
+            crate::ffi::glfwSetKeyCallback(self.handle, Some(key_callback));
+        }
+    }
+
     /// Returns the last reported state of a keyboard key.
     ///
     /// # Errors
@@ -411,138 +431,156 @@ pub enum CursorMode
     Normal = crate::ffi::GLFW_CURSOR_NORMAL,
 }
 
-#[derive(Debug, Clone, Copy)]
-#[repr(i32)]
-pub enum Key
-{
-    // Unknown = crate::ffi::GLFW_KEY_UNKNOWN,
-    Space = crate::ffi::GLFW_KEY_SPACE,
-    Apostrophe = crate::ffi::GLFW_KEY_APOSTROPHE,
-    Comma = crate::ffi::GLFW_KEY_COMMA,
-    Minus = crate::ffi::GLFW_KEY_MINUS,
-    Period = crate::ffi::GLFW_KEY_PERIOD,
-    Slash = crate::ffi::GLFW_KEY_SLASH,
-    Digit0 = crate::ffi::GLFW_KEY_0,
-    Digit1 = crate::ffi::GLFW_KEY_1,
-    Digit2 = crate::ffi::GLFW_KEY_2,
-    Digit3 = crate::ffi::GLFW_KEY_3,
-    Digit4 = crate::ffi::GLFW_KEY_4,
-    Digit5 = crate::ffi::GLFW_KEY_5,
-    Digit6 = crate::ffi::GLFW_KEY_6,
-    Digit7 = crate::ffi::GLFW_KEY_7,
-    Digit8 = crate::ffi::GLFW_KEY_8,
-    Digit9 = crate::ffi::GLFW_KEY_9,
-    Semicolon = crate::ffi::GLFW_KEY_SEMICOLON,
-    Equal = crate::ffi::GLFW_KEY_EQUAL,
-    A = crate::ffi::GLFW_KEY_A,
-    B = crate::ffi::GLFW_KEY_B,
-    C = crate::ffi::GLFW_KEY_C,
-    D = crate::ffi::GLFW_KEY_D,
-    E = crate::ffi::GLFW_KEY_E,
-    F = crate::ffi::GLFW_KEY_F,
-    G = crate::ffi::GLFW_KEY_G,
-    H = crate::ffi::GLFW_KEY_H,
-    I = crate::ffi::GLFW_KEY_I,
-    J = crate::ffi::GLFW_KEY_J,
-    K = crate::ffi::GLFW_KEY_K,
-    L = crate::ffi::GLFW_KEY_L,
-    M = crate::ffi::GLFW_KEY_M,
-    N = crate::ffi::GLFW_KEY_N,
-    O = crate::ffi::GLFW_KEY_O,
-    P = crate::ffi::GLFW_KEY_P,
-    Q = crate::ffi::GLFW_KEY_Q,
-    R = crate::ffi::GLFW_KEY_R,
-    S = crate::ffi::GLFW_KEY_S,
-    T = crate::ffi::GLFW_KEY_T,
-    U = crate::ffi::GLFW_KEY_U,
-    V = crate::ffi::GLFW_KEY_V,
-    W = crate::ffi::GLFW_KEY_W,
-    X = crate::ffi::GLFW_KEY_X,
-    Y = crate::ffi::GLFW_KEY_Y,
-    Z = crate::ffi::GLFW_KEY_Z,
-    LeftBracket = crate::ffi::GLFW_KEY_LEFT_BRACKET,
-    Backslash = crate::ffi::GLFW_KEY_BACKSLASH,
-    RightBracket = crate::ffi::GLFW_KEY_RIGHT_BRACKET,
-    GraveAccent = crate::ffi::GLFW_KEY_GRAVE_ACCENT,
-    World1 = crate::ffi::GLFW_KEY_WORLD_1,
-    World2 = crate::ffi::GLFW_KEY_WORLD_2,
-    Escape = crate::ffi::GLFW_KEY_ESCAPE,
-    Enter = crate::ffi::GLFW_KEY_ENTER,
-    Tab = crate::ffi::GLFW_KEY_TAB,
-    Backspace = crate::ffi::GLFW_KEY_BACKSPACE,
-    Insert = crate::ffi::GLFW_KEY_INSERT,
-    Delete = crate::ffi::GLFW_KEY_DELETE,
-    Right = crate::ffi::GLFW_KEY_RIGHT,
-    Left = crate::ffi::GLFW_KEY_LEFT,
-    Down = crate::ffi::GLFW_KEY_DOWN,
-    Up = crate::ffi::GLFW_KEY_UP,
-    PageUp = crate::ffi::GLFW_KEY_PAGE_UP,
-    PageDown = crate::ffi::GLFW_KEY_PAGE_DOWN,
-    Home = crate::ffi::GLFW_KEY_HOME,
-    End = crate::ffi::GLFW_KEY_END,
-    CapsLock = crate::ffi::GLFW_KEY_CAPS_LOCK,
-    ScrollLock = crate::ffi::GLFW_KEY_SCROLL_LOCK,
-    NumLock = crate::ffi::GLFW_KEY_NUM_LOCK,
-    PrintScreen = crate::ffi::GLFW_KEY_PRINT_SCREEN,
-    Pause = crate::ffi::GLFW_KEY_PAUSE,
-    F1 = crate::ffi::GLFW_KEY_F1,
-    F2 = crate::ffi::GLFW_KEY_F2,
-    F3 = crate::ffi::GLFW_KEY_F3,
-    F4 = crate::ffi::GLFW_KEY_F4,
-    F5 = crate::ffi::GLFW_KEY_F5,
-    F6 = crate::ffi::GLFW_KEY_F6,
-    F7 = crate::ffi::GLFW_KEY_F7,
-    F8 = crate::ffi::GLFW_KEY_F8,
-    F9 = crate::ffi::GLFW_KEY_F9,
-    F10 = crate::ffi::GLFW_KEY_F10,
-    F11 = crate::ffi::GLFW_KEY_F11,
-    F12 = crate::ffi::GLFW_KEY_F12,
-    F13 = crate::ffi::GLFW_KEY_F13,
-    F14 = crate::ffi::GLFW_KEY_F14,
-    F15 = crate::ffi::GLFW_KEY_F15,
-    F16 = crate::ffi::GLFW_KEY_F16,
-    F17 = crate::ffi::GLFW_KEY_F17,
-    F18 = crate::ffi::GLFW_KEY_F18,
-    F19 = crate::ffi::GLFW_KEY_F19,
-    F20 = crate::ffi::GLFW_KEY_F20,
-    F21 = crate::ffi::GLFW_KEY_F21,
-    F22 = crate::ffi::GLFW_KEY_F22,
-    F23 = crate::ffi::GLFW_KEY_F23,
-    F24 = crate::ffi::GLFW_KEY_F24,
-    F25 = crate::ffi::GLFW_KEY_F25,
-    Kp0 = crate::ffi::GLFW_KEY_KP_0,
-    Kp1 = crate::ffi::GLFW_KEY_KP_1,
-    Kp2 = crate::ffi::GLFW_KEY_KP_2,
-    Kp3 = crate::ffi::GLFW_KEY_KP_3,
-    Kp4 = crate::ffi::GLFW_KEY_KP_4,
-    Kp5 = crate::ffi::GLFW_KEY_KP_5,
-    Kp6 = crate::ffi::GLFW_KEY_KP_6,
-    Kp7 = crate::ffi::GLFW_KEY_KP_7,
-    Kp8 = crate::ffi::GLFW_KEY_KP_8,
-    Kp9 = crate::ffi::GLFW_KEY_KP_9,
-    KpDecimal = crate::ffi::GLFW_KEY_KP_DECIMAL,
-    KpDivide = crate::ffi::GLFW_KEY_KP_DIVIDE,
-    KpMultiply = crate::ffi::GLFW_KEY_KP_MULTIPLY,
-    KpSubtract = crate::ffi::GLFW_KEY_KP_SUBTRACT,
-    KpAdd = crate::ffi::GLFW_KEY_KP_ADD,
-    KpEnter = crate::ffi::GLFW_KEY_KP_ENTER,
-    KpEqual = crate::ffi::GLFW_KEY_KP_EQUAL,
-    LeftShift = crate::ffi::GLFW_KEY_LEFT_SHIFT,
-    LeftControl = crate::ffi::GLFW_KEY_LEFT_CONTROL,
-    LeftAlt = crate::ffi::GLFW_KEY_LEFT_ALT,
-    LeftSuper = crate::ffi::GLFW_KEY_LEFT_SUPER,
-    RightShift = crate::ffi::GLFW_KEY_RIGHT_SHIFT,
-    RightControl = crate::ffi::GLFW_KEY_RIGHT_CONTROL,
-    RightAlt = crate::ffi::GLFW_KEY_RIGHT_ALT,
-    RightSuper = crate::ffi::GLFW_KEY_RIGHT_SUPER,
-    Menu = crate::ffi::GLFW_KEY_MENU,
-}
+enum_from_repr!(
+    #[repr(i32)]
+    #[derive(Debug, Clone, Copy)]
+    pub enum Key
+    {
+        // Unknown = crate::ffi::GLFW_KEY_UNKNOWN,
+        Space = crate::ffi::GLFW_KEY_SPACE,
+        Apostrophe = crate::ffi::GLFW_KEY_APOSTROPHE,
+        Comma = crate::ffi::GLFW_KEY_COMMA,
+        Minus = crate::ffi::GLFW_KEY_MINUS,
+        Period = crate::ffi::GLFW_KEY_PERIOD,
+        Slash = crate::ffi::GLFW_KEY_SLASH,
+        Digit0 = crate::ffi::GLFW_KEY_0,
+        Digit1 = crate::ffi::GLFW_KEY_1,
+        Digit2 = crate::ffi::GLFW_KEY_2,
+        Digit3 = crate::ffi::GLFW_KEY_3,
+        Digit4 = crate::ffi::GLFW_KEY_4,
+        Digit5 = crate::ffi::GLFW_KEY_5,
+        Digit6 = crate::ffi::GLFW_KEY_6,
+        Digit7 = crate::ffi::GLFW_KEY_7,
+        Digit8 = crate::ffi::GLFW_KEY_8,
+        Digit9 = crate::ffi::GLFW_KEY_9,
+        Semicolon = crate::ffi::GLFW_KEY_SEMICOLON,
+        Equal = crate::ffi::GLFW_KEY_EQUAL,
+        A = crate::ffi::GLFW_KEY_A,
+        B = crate::ffi::GLFW_KEY_B,
+        C = crate::ffi::GLFW_KEY_C,
+        D = crate::ffi::GLFW_KEY_D,
+        E = crate::ffi::GLFW_KEY_E,
+        F = crate::ffi::GLFW_KEY_F,
+        G = crate::ffi::GLFW_KEY_G,
+        H = crate::ffi::GLFW_KEY_H,
+        I = crate::ffi::GLFW_KEY_I,
+        J = crate::ffi::GLFW_KEY_J,
+        K = crate::ffi::GLFW_KEY_K,
+        L = crate::ffi::GLFW_KEY_L,
+        M = crate::ffi::GLFW_KEY_M,
+        N = crate::ffi::GLFW_KEY_N,
+        O = crate::ffi::GLFW_KEY_O,
+        P = crate::ffi::GLFW_KEY_P,
+        Q = crate::ffi::GLFW_KEY_Q,
+        R = crate::ffi::GLFW_KEY_R,
+        S = crate::ffi::GLFW_KEY_S,
+        T = crate::ffi::GLFW_KEY_T,
+        U = crate::ffi::GLFW_KEY_U,
+        V = crate::ffi::GLFW_KEY_V,
+        W = crate::ffi::GLFW_KEY_W,
+        X = crate::ffi::GLFW_KEY_X,
+        Y = crate::ffi::GLFW_KEY_Y,
+        Z = crate::ffi::GLFW_KEY_Z,
+        LeftBracket = crate::ffi::GLFW_KEY_LEFT_BRACKET,
+        Backslash = crate::ffi::GLFW_KEY_BACKSLASH,
+        RightBracket = crate::ffi::GLFW_KEY_RIGHT_BRACKET,
+        GraveAccent = crate::ffi::GLFW_KEY_GRAVE_ACCENT,
+        World1 = crate::ffi::GLFW_KEY_WORLD_1,
+        World2 = crate::ffi::GLFW_KEY_WORLD_2,
+        Escape = crate::ffi::GLFW_KEY_ESCAPE,
+        Enter = crate::ffi::GLFW_KEY_ENTER,
+        Tab = crate::ffi::GLFW_KEY_TAB,
+        Backspace = crate::ffi::GLFW_KEY_BACKSPACE,
+        Insert = crate::ffi::GLFW_KEY_INSERT,
+        Delete = crate::ffi::GLFW_KEY_DELETE,
+        Right = crate::ffi::GLFW_KEY_RIGHT,
+        Left = crate::ffi::GLFW_KEY_LEFT,
+        Down = crate::ffi::GLFW_KEY_DOWN,
+        Up = crate::ffi::GLFW_KEY_UP,
+        PageUp = crate::ffi::GLFW_KEY_PAGE_UP,
+        PageDown = crate::ffi::GLFW_KEY_PAGE_DOWN,
+        Home = crate::ffi::GLFW_KEY_HOME,
+        End = crate::ffi::GLFW_KEY_END,
+        CapsLock = crate::ffi::GLFW_KEY_CAPS_LOCK,
+        ScrollLock = crate::ffi::GLFW_KEY_SCROLL_LOCK,
+        NumLock = crate::ffi::GLFW_KEY_NUM_LOCK,
+        PrintScreen = crate::ffi::GLFW_KEY_PRINT_SCREEN,
+        Pause = crate::ffi::GLFW_KEY_PAUSE,
+        F1 = crate::ffi::GLFW_KEY_F1,
+        F2 = crate::ffi::GLFW_KEY_F2,
+        F3 = crate::ffi::GLFW_KEY_F3,
+        F4 = crate::ffi::GLFW_KEY_F4,
+        F5 = crate::ffi::GLFW_KEY_F5,
+        F6 = crate::ffi::GLFW_KEY_F6,
+        F7 = crate::ffi::GLFW_KEY_F7,
+        F8 = crate::ffi::GLFW_KEY_F8,
+        F9 = crate::ffi::GLFW_KEY_F9,
+        F10 = crate::ffi::GLFW_KEY_F10,
+        F11 = crate::ffi::GLFW_KEY_F11,
+        F12 = crate::ffi::GLFW_KEY_F12,
+        F13 = crate::ffi::GLFW_KEY_F13,
+        F14 = crate::ffi::GLFW_KEY_F14,
+        F15 = crate::ffi::GLFW_KEY_F15,
+        F16 = crate::ffi::GLFW_KEY_F16,
+        F17 = crate::ffi::GLFW_KEY_F17,
+        F18 = crate::ffi::GLFW_KEY_F18,
+        F19 = crate::ffi::GLFW_KEY_F19,
+        F20 = crate::ffi::GLFW_KEY_F20,
+        F21 = crate::ffi::GLFW_KEY_F21,
+        F22 = crate::ffi::GLFW_KEY_F22,
+        F23 = crate::ffi::GLFW_KEY_F23,
+        F24 = crate::ffi::GLFW_KEY_F24,
+        F25 = crate::ffi::GLFW_KEY_F25,
+        Kp0 = crate::ffi::GLFW_KEY_KP_0,
+        Kp1 = crate::ffi::GLFW_KEY_KP_1,
+        Kp2 = crate::ffi::GLFW_KEY_KP_2,
+        Kp3 = crate::ffi::GLFW_KEY_KP_3,
+        Kp4 = crate::ffi::GLFW_KEY_KP_4,
+        Kp5 = crate::ffi::GLFW_KEY_KP_5,
+        Kp6 = crate::ffi::GLFW_KEY_KP_6,
+        Kp7 = crate::ffi::GLFW_KEY_KP_7,
+        Kp8 = crate::ffi::GLFW_KEY_KP_8,
+        Kp9 = crate::ffi::GLFW_KEY_KP_9,
+        KpDecimal = crate::ffi::GLFW_KEY_KP_DECIMAL,
+        KpDivide = crate::ffi::GLFW_KEY_KP_DIVIDE,
+        KpMultiply = crate::ffi::GLFW_KEY_KP_MULTIPLY,
+        KpSubtract = crate::ffi::GLFW_KEY_KP_SUBTRACT,
+        KpAdd = crate::ffi::GLFW_KEY_KP_ADD,
+        KpEnter = crate::ffi::GLFW_KEY_KP_ENTER,
+        KpEqual = crate::ffi::GLFW_KEY_KP_EQUAL,
+        LeftShift = crate::ffi::GLFW_KEY_LEFT_SHIFT,
+        LeftControl = crate::ffi::GLFW_KEY_LEFT_CONTROL,
+        LeftAlt = crate::ffi::GLFW_KEY_LEFT_ALT,
+        LeftSuper = crate::ffi::GLFW_KEY_LEFT_SUPER,
+        RightShift = crate::ffi::GLFW_KEY_RIGHT_SHIFT,
+        RightControl = crate::ffi::GLFW_KEY_RIGHT_CONTROL,
+        RightAlt = crate::ffi::GLFW_KEY_RIGHT_ALT,
+        RightSuper = crate::ffi::GLFW_KEY_RIGHT_SUPER,
+        Menu = crate::ffi::GLFW_KEY_MENU,
+    }
+);
 
-#[derive(Debug, Clone, Copy)]
-pub enum KeyState
-{
-    Pressed,
-    Released,
+enum_from_repr!(
+    #[repr(i32)]
+    #[derive(Debug, Clone, Copy)]
+    pub enum KeyState
+    {
+        Pressed = crate::ffi::GLFW_PRESS,
+        Released = crate::ffi::GLFW_RELEASE,
+        Repeat = crate::ffi::GLFW_REPEAT,
+    }
+);
+
+bitflags! {
+    #[derive(Debug, Clone, Copy)]
+    pub struct KeyModifiers: i32 {
+        const SHIFT = crate::ffi::GLFW_MOD_SHIFT;
+        const CONTROL = crate::ffi::GLFW_MOD_CONTROL;
+        const ALT = crate::ffi::GLFW_MOD_ALT;
+        const SUPER = crate::ffi::GLFW_MOD_SUPER;
+        const CAPS_LOCK = crate::ffi::GLFW_MOD_CAPS_LOCK;
+        const NUM_LOCK = crate::ffi::GLFW_MOD_NUM_LOCK;
+    }
 }
 
 #[derive(Debug, Clone, Copy)]
@@ -594,9 +632,11 @@ pub struct CursorPosition
 }
 
 type FramebufferSizeCb = Box<dyn Fn(Size)>;
+type KeyCallback = Box<dyn Fn(Key, i32, KeyState, KeyModifiers)>;
 
 thread_local! {
 static FRAMEBUFFER_SIZE_CB: RefCell<Option<FramebufferSizeCb>> = RefCell::new(None);
+static KEY_CALLBACK: RefCell<Option<KeyCallback>> = RefCell::new(None);
 }
 
 extern "C" fn framebuffer_size_callback(
@@ -624,3 +664,47 @@ extern "C" fn framebuffer_size_callback(
         println!("ERROR: Panic in framebuffer size callback");
     }
 }
+
+extern "C" fn key_callback(
+    _window: *mut crate::ffi::GLFWwindow,
+    key_raw: c_int,
+    scancode: c_int,
+    action_raw: c_int,
+    mods: c_int,
+)
+{
+    let Some(key) = Key::from_repr(key_raw) else {
+        write!(stdout(), "Unknown key {key_raw}").ok();
+        return;
+    };
+
+    let Some(key_state) = KeyState::from_repr(action_raw) else {
+        write!(stdout(), "Unknown key state {action_raw}").ok();
+        return;
+    };
+
+    let Some(key_modifiers) = KeyModifiers::from_bits(mods) else {
+        write!(
+            stdout(),
+            "Key modifiers {action_raw} contain one or more unknown bit(s)"
+        )
+        .ok();
+
+        return;
+    };
+
+    // Unwinds are catched because unwinding from Rust code into foreign code is UB.
+    let res = catch_unwind(|| {
+        KEY_CALLBACK
+            .try_with(|key_callback| {
+                if let Some(cb) = key_callback.borrow().as_deref() {
+                    cb(key, scancode, key_state, key_modifiers);
+                }
+            })
+            .ok();
+    });
+
+    if res.is_err() {
+        write!(stdout(), "ERROR: Panic in key callback").ok();
+    }
+}
-- 
cgit v1.2.3-18-g5258