From 29f7b5ca60820c21e2c92eaa03f7864b893a5879 Mon Sep 17 00:00:00 2001 From: Jon Ringle Date: Wed, 4 Oct 2023 13:20:19 -0400 Subject: [PATCH] cairo-gtk: Add support for key input --- c_src/device/cairo/cairo_gtk.c | 27 +++- c_src/device/nvg/glfw.c | 6 +- c_src/scenic/comms.c | 14 ++- c_src/scenic/comms.h | 15 ++- lib/from_port.ex | 224 +++++++++++++++++++++++++++++++-- 5 files changed, 256 insertions(+), 30 deletions(-) diff --git a/c_src/device/cairo/cairo_gtk.c b/c_src/device/cairo/cairo_gtk.c index 0415392..bc5a045 100644 --- a/c_src/device/cairo/cairo_gtk.c +++ b/c_src/device/cairo/cairo_gtk.c @@ -100,12 +100,25 @@ static gboolean on_button_event(GtkWidget* widget, return FALSE; } - int mods = 0; // TODO: decipher event->state (GdkModifierType) + send_mouse_button(KEYMAP_GDK, + event->button, + action, + event->state, + event->x, event->y); - guint button = event->button - 1; - - send_mouse_button(button, action, mods, event->x, event->y); + return TRUE; +} +static gboolean on_key_event(GtkWidget* widget, + GdkEventKey* event, + gpointer data) +{ + int action = (event->type == GDK_KEY_PRESS) ? 1 : 0; + uint32_t unicode = gdk_keyval_to_unicode(event->keyval); + send_key(KEYMAP_GDK, event->keyval, event->hardware_keycode, action, event->state); + if (!(event->keyval & 0xF000) && event->type == GDK_KEY_PRESS) { + send_codepoint(KEYMAP_GDK, unicode, event->state); + } return TRUE; } @@ -134,11 +147,15 @@ int device_init(const device_opts_t* p_opts, gtk_widget_set_events(g_cairo_gtk.window, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK); + GDK_BUTTON_RELEASE_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK); g_signal_connect(G_OBJECT(g_cairo_gtk.window), "motion-notify-event", G_CALLBACK(on_motion_event), NULL); g_signal_connect(G_OBJECT(g_cairo_gtk.window), "button-press-event", G_CALLBACK(on_button_event), NULL); g_signal_connect(G_OBJECT(g_cairo_gtk.window), "button-release-event", G_CALLBACK(on_button_event), NULL); + g_signal_connect(G_OBJECT(g_cairo_gtk.window), "key-press-event", G_CALLBACK(on_key_event), NULL); + g_signal_connect(G_OBJECT(g_cairo_gtk.window), "key-release-event", G_CALLBACK(on_key_event), NULL); GtkDrawingArea* drawing_area = (GtkDrawingArea*)gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(g_cairo_gtk.window), (GtkWidget*)drawing_area); diff --git a/c_src/device/nvg/glfw.c b/c_src/device/nvg/glfw.c index 6c24955..0f3ca28 100644 --- a/c_src/device/nvg/glfw.c +++ b/c_src/device/nvg/glfw.c @@ -94,13 +94,13 @@ void reshape_window(GLFWwindow* window, int w, int h) //--------------------------------------------------------- void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { - send_key(key, scancode, action, mods); + send_key(KEYMAP_GLFW, key, scancode, action, mods); } //--------------------------------------------------------- void charmods_callback(GLFWwindow* window, unsigned int codepoint, int mods) { - send_codepoint(codepoint, mods); + send_codepoint(KEYMAP_GLFW, codepoint, mods); } //--------------------------------------------------------- @@ -123,7 +123,7 @@ void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { double x, y; glfwGetCursorPos(window, &x, &y); - send_mouse_button(button, action, mods, x, y); + send_mouse_button(KEYMAP_GLFW, button, action, mods, x, y); } //--------------------------------------------------------- diff --git a/c_src/scenic/comms.c b/c_src/scenic/comms.c index 6f70f15..7c698bd 100644 --- a/c_src/scenic/comms.c +++ b/c_src/scenic/comms.c @@ -241,15 +241,16 @@ void send_reshape( int window_width, int window_height ) PACK(typedef struct msg_key_t { uint32_t msg_id; + uint32_t keymap; uint32_t key; uint32_t scancode; uint32_t action; uint32_t mods; }) msg_key_t; -void send_key(int key, int scancode, int action, int mods) +void send_key(keymap_t keymap, int key, int scancode, int action, int mods) { - msg_key_t msg = { MSG_OUT_KEY, key, scancode, action, mods }; + msg_key_t msg = { MSG_OUT_KEY, keymap, key, scancode, action, mods }; write_cmd((byte*) &msg, sizeof(msg_key_t)); } @@ -257,13 +258,14 @@ void send_key(int key, int scancode, int action, int mods) PACK(typedef struct msg_codepoint_t { uint32_t msg_id; + uint32_t keymap; uint32_t codepoint; uint32_t mods; }) msg_codepoint_t; -void send_codepoint(unsigned int codepoint, int mods) +void send_codepoint(keymap_t keymap, unsigned int codepoint, int mods) { - msg_codepoint_t msg = { MSG_OUT_CODEPOINT, codepoint, mods }; + msg_codepoint_t msg = { MSG_OUT_CODEPOINT, keymap, codepoint, mods }; write_cmd((byte*) &msg, sizeof(msg_codepoint_t)); } @@ -285,6 +287,7 @@ void send_cursor_pos(float xpos, float ypos) PACK(typedef struct msg_mouse_button_t { uint32_t msg_id; + uint32_t keymap; uint32_t button; uint32_t action; uint32_t mods; @@ -292,10 +295,11 @@ PACK(typedef struct msg_mouse_button_t float ypos; }) msg_mouse_button_t; -void send_mouse_button(int button, int action, int mods, float xpos, float ypos) +void send_mouse_button(keymap_t keymap, int button, int action, int mods, float xpos, float ypos) { msg_mouse_button_t msg = { MSG_OUT_MOUSE_BUTTON, + keymap, button, action, mods, diff --git a/c_src/scenic/comms.h b/c_src/scenic/comms.h index 0f705aa..d2cca80 100644 --- a/c_src/scenic/comms.h +++ b/c_src/scenic/comms.h @@ -91,6 +91,13 @@ typedef enum { _MSG_OUT_SIZE_ = 0XFFFFFFFF, } msg_out_t; +typedef enum { + KEYMAP_GLFW = 0x01, + KEYMAP_GDK = 0x02, + + _KEYMAP_SIZE_ = 0xFFFFFFFF, +} keymap_t; + int read_exact(byte* buf, int len); int write_exact(byte* buf, int len); int read_msg_length(struct timeval * ptv); @@ -127,11 +134,11 @@ void render(driver_data_t* p_data); void send_image_miss(unsigned int img_id); -void send_reshape( int window_width, int window_height ); -void send_key(int key, int scancode, int action, int mods); -void send_codepoint(unsigned int codepoint, int mods); +void send_reshape(int window_width, int window_height); +void send_key(keymap_t keymap, int key, int scancode, int action, int mods); +void send_codepoint(keymap_t keymap, unsigned int codepoint, int mods); void send_cursor_pos(float xpos, float ypos); -void send_mouse_button(int button, int action, int mods, float xpos, +void send_mouse_button(keymap_t keymap, int button, int action, int mods, float xpos, float ypos); void send_scroll(float xoffset, float yoffset, float xpos, float ypos); void send_cursor_enter(int entered, float xpos, float ypos); diff --git a/lib/from_port.ex b/lib/from_port.ex index 51c8a3c..7f75258 100644 --- a/lib/from_port.ex +++ b/lib/from_port.ex @@ -13,6 +13,8 @@ defmodule Scenic.Driver.Local.FromPort do require Logger + import Bitwise + import Scenic.Driver, only: [ assign: 3, @@ -49,6 +51,9 @@ defmodule Scenic.Driver.Local.FromPort do # @msg_font_miss 0x22 # @msg_texture_miss 0x23 + @keymap_glfw 0x01 + @keymap_gdk 0x02 + # ============================================================================ @doc false @@ -186,6 +191,7 @@ defmodule Scenic.Driver.Local.FromPort do def handle_port_message( << @msg_key_id::unsigned-integer-size(32)-native, + keymap::unsigned-integer-native-size(32), key::unsigned-integer-native-size(32), _scancode::unsigned-integer-native-size(32), action::integer-native-size(32), @@ -193,8 +199,19 @@ defmodule Scenic.Driver.Local.FromPort do >>, driver ) do - key = key_to_atom(key) - mods = prep_mods(mods) + key = + case keymap do + @keymap_glfw -> glfw_key_to_atom(key) + @keymap_gdk -> gdk_key_to_atom(key) + _ -> :key_unknown + end + + mods = + case keymap do + @keymap_glfw -> glfw_prep_mods(mods) + @keymap_gdk -> gdk_prep_mods(mods) + _ -> [] + end send_input(driver, {:key, {key, action, mods}}) @@ -205,13 +222,21 @@ defmodule Scenic.Driver.Local.FromPort do def handle_port_message( << @msg_char_id::unsigned-integer-size(32)-native, + keymap::unsigned-integer-native-size(32), codepoint::unsigned-integer-native-size(32), mods::unsigned-integer-native-size(32) >>, driver ) do codepoint = codepoint_to_char(codepoint) - mods = prep_mods(mods) + + mods = + case keymap do + @keymap_glfw -> glfw_prep_mods(mods) + @keymap_gdk -> gdk_prep_mods(mods) + _ -> [] + end + send_input(driver, {:codepoint, {codepoint, mods}}) {:noreply, driver} @@ -236,6 +261,7 @@ defmodule Scenic.Driver.Local.FromPort do def handle_port_message( << @msg_mouse_button_id::unsigned-integer-size(32)-native, + keymap::unsigned-integer-native-size(32), button::unsigned-integer-native-size(32), action::unsigned-integer-native-size(32), mods::unsigned-integer-native-size(32), @@ -245,8 +271,20 @@ defmodule Scenic.Driver.Local.FromPort do driver ) do # action = action_to_atom(action) - button = button_to_atom(button) - mods = prep_mods(mods) + button = + case keymap do + @keymap_glfw -> glfw_button_to_atom(button) + @keymap_gdk -> gdk_button_to_atom(button) + _ -> :unknown + end + + mods = + case keymap do + @keymap_glfw -> glfw_prep_mods(mods) + @keymap_gdk -> gdk_prep_mods(mods) + _ -> [] + end + pos = scene_coords({x, y}, driver) send_input(driver, {:cursor_button, {button, action, mods, pos}}) {:noreply, driver} @@ -309,7 +347,170 @@ defmodule Scenic.Driver.Local.FromPort do end # ============================================================================ - # utilities to translate Glfw input to standardized input + # utilities to translate GDK input to standardized input + + @gdk_button_atoms %{ + 1 => :btn_left, + 2 => :btn_middle, + 3 => :btn_right + } + defp gdk_button_to_atom(code), do: Map.get(@gdk_button_atoms, code, :unknown) + + # ============================================================================ + # keyboard input helpers + # these are for reading the keyboard directly. If you are trying to do text input + # use the text/char helpers instead + + # -------------------------------------------------------- + + # key codes use the standards defined by gdk, which generates them with + # https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/gdkkeysyms-update.pl + # which sources it's values from + # https://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h + + # -------------------------------------------------------- + @gdk_key_atoms %{ + 32 => :key_space, + 39 => :key_apostrophe, + 44 => :key_comma, + 45 => :key_minus, + 46 => :key_dot, + 47 => :key_slash, + 48 => :key_0, + 49 => :key_1, + 50 => :key_2, + 51 => :key_3, + 52 => :key_4, + 53 => :key_5, + 54 => :key_6, + 55 => :key_7, + 56 => :key_8, + 57 => :key_9, + 59 => :key_semicolon, + 61 => :key_equal, + 65 => :key_a, + 66 => :key_b, + 67 => :key_c, + 68 => :key_d, + 69 => :key_e, + 70 => :key_f, + 71 => :key_g, + 72 => :key_h, + 73 => :key_i, + 74 => :key_j, + 75 => :key_k, + 76 => :key_l, + 77 => :key_m, + 78 => :key_n, + 79 => :key_o, + 80 => :key_p, + 81 => :key_q, + 82 => :key_r, + 83 => :key_s, + 84 => :key_t, + 85 => :key_u, + 86 => :key_v, + 87 => :key_w, + 88 => :key_x, + 89 => :key_y, + 90 => :key_z, + 91 => :key_leftbrace, + 92 => :key_backslash, + 93 => :key_rightbrace, + 96 => :key_grave, + 0xFF1B => :key_esc, + 0xFF0D => :key_enter, + 0xFF09 => :key_tab, + 0xFF08 => :key_backspace, + 0xFF63 => :key_insert, + 0xFFFF => :key_delete, + 0xFF53 => :key_right, + 0xFF51 => :key_left, + 0xFF54 => :key_down, + 0xFF52 => :key_up, + 0xFF55 => :key_pageup, + 0xFF56 => :key_pagedown, + 0xFF50 => :key_home, + 0xFF57 => :key_end, + 0xFFE5 => :key_capslock, + 0xFF14 => :key_scrolllock, + 0xFF7F => :key_numlock, + 0xFF61 => :key_screen, + 0xFF13 => :key_pause, + 0xFFBE => :key_f1, + 0xFFBF => :key_f2, + 0xFFC0 => :key_f3, + 0xFFC1 => :key_f4, + 0xFFC2 => :key_f5, + 0xFFC3 => :key_f6, + 0xFFC4 => :key_f7, + 0xFFC5 => :key_f8, + 0xFFC6 => :key_f9, + 0xFFC7 => :key_f10, + 0xFFC8 => :key_f11, + 0xFFC9 => :key_f12, + 0xFFCA => :key_f13, + 0xFFCB => :key_f14, + 0xFFCC => :key_f15, + 0xFFCD => :key_f16, + 0xFFCE => :key_f17, + 0xFFCF => :key_f18, + 0xFFD0 => :key_f19, + 0xFFD1 => :key_f20, + 0xFFD2 => :key_f21, + 0xFFD3 => :key_f22, + 0xFFD4 => :key_f23, + 0xFFD5 => :key_f24, + 0xFFD6 => :key_f25, + 0xFFB0 => :key_kp0, + 0xFFB1 => :key_kp1, + 0xFFB2 => :key_kp2, + 0xFFB3 => :key_kp3, + 0xFFB4 => :key_kp4, + 0xFFB5 => :key_kp5, + 0xFFB6 => :key_kp6, + 0xFFB7 => :key_kp7, + 0xFFB8 => :key_kp8, + 0xFFB9 => :key_kp9, + 0xFFAE => :key_kpdot, + 0xFFAF => :key_kpslash, + 0xFFAA => :key_kpasterisk, + 0xFFAD => :key_kpminus, + 0xFFAB => :key_kpplus, + 0xFF8D => :key_kpenter, + 0xFFBD => :key_kpequal, + 0xFFE1 => :key_leftshift, + 0xFFE3 => :key_leftctrl, + 0xFFE9 => :key_leftalt, + # 0xffeb => "left_super" + + 0xFFE2 => :key_rightshift, + 0xFFE4 => :key_rightctrl, + 0xFFEA => :key_rightalt, + # 0xffec => "right_super" + + 0xFF67 => :key_menu + } + defp gdk_key_to_atom(code), do: Map.get(@gdk_key_atoms, code, :key_unknown) + + # -------------------------------------------------------- + + @gdk_mod_shift 1 <<< 0 + @gdk_mod_ctrl 1 <<< 2 + @gdk_mod_alt 1 <<< 3 + @gdk_mod_super 1 <<< 26 + @gdk_mod_caps_lock 1 <<< 1 + # @gdk_mod_num_lock 0x020 + defp gdk_prep_mods(mods) do + [] + |> add_if_masked(mods, @gdk_mod_shift, :shift) + |> add_if_masked(mods, @gdk_mod_ctrl, :ctrl) + |> add_if_masked(mods, @gdk_mod_alt, :alt) + |> add_if_masked(mods, @gdk_mod_super, :meta) + |> add_if_masked(mods, @gdk_mod_caps_lock, :caps_lock) + + # |> add_if_masked(mods, @gdk_mod_num_lock, :num_lock) + end # ============================================================================ # utilities to translate Glfw input to standardized input @@ -319,12 +520,9 @@ defmodule Scenic.Driver.Local.FromPort do 1 => :btn_right, 2 => :btn_middle } - defp button_to_atom(code), do: Map.get(@glfw_button_atoms, code, :unknown) + defp glfw_button_to_atom(code), do: Map.get(@glfw_button_atoms, code, :unknown) - # ============================================================================ - # keyboard input helpers - # these are for reading the keyboard directly. If you are trying to do text input - # use the text/char helpers instead + # -------------------------------------------------------- # key codes use the standards defined by Glfw # http://www.Glfw.org/docs/latest/group__keys.html @@ -452,7 +650,7 @@ defmodule Scenic.Driver.Local.FromPort do 348 => :key_menu } - defp key_to_atom(code), do: Map.get(@glfw_key_atoms, code, :key_unknown) + defp glfw_key_to_atom(code), do: Map.get(@glfw_key_atoms, code, :key_unknown) # -------------------------------------------------------- @glfw_mod_shift 0x001 @@ -461,7 +659,7 @@ defmodule Scenic.Driver.Local.FromPort do @glfw_mod_super 0x008 @glfw_mod_caps_lock 0x010 @glfw_mod_num_lock 0x020 - defp prep_mods(mods) do + defp glfw_prep_mods(mods) do [] |> add_if_masked(mods, @glfw_mod_shift, :shift) |> add_if_masked(mods, @glfw_mod_ctrl, :ctrl)