From d2403d9fa14b498263abb242324c21441a4246aa Mon Sep 17 00:00:00 2001 From: Cecilio Pardo Date: Mon, 28 Oct 2024 23:57:35 +0100 Subject: [PATCH] Add events for key press and key release on gui systems. And detection of double/triple taps on modifier keys. * lisp/low-level-key.el (llk-tap-timeout): (llk-tap-count): (llk-tap-keys): (llk-bindings): (llk-modifier-bindings): (llk-init): (llk-bind): (llk-modifier-bind): (llk-events): (llk-modifier-events): (llm-handle): * src/gtkutil.c (xg_create_frame_widgets): (xg_maybe_send_low_level_key_event): (xg_widget_key_press_event_cb): (xg_widget_key_release_event_cb): * src/xterm.c (x_maybe_send_physical_key_event): (x_filter_event): (handle_one_xevent): (syms_of_xterm): --- lisp/low-level-key.el | 141 +++++++++++++++++++++++++++++++++++++++++ src/gtkutil.c | 133 ++++++++++++++++++++++++++++++++++++++ src/keyboard.c | 38 +++++++++++ src/pgtkterm.c | 37 +++++++++++ src/termhooks.h | 2 + src/w32fns.c | 11 ++++ src/w32term.c | 69 ++++++++++++++++++++ src/w32term.h | 3 +- src/xterm.c | 144 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 lisp/low-level-key.el diff --git a/lisp/low-level-key.el b/lisp/low-level-key.el new file mode 100644 index 00000000000..639c082da15 --- /dev/null +++ b/lisp/low-level-key.el @@ -0,0 +1,141 @@ +;;; -*- lexical-binding: t -*- +(require 'cl-lib) + +;; User options +(defvar llk-tap-timeout 1000) +(defvar llk-tap-count 2) +(defvar llk-tap-keys + '(lshift rshift lctrl rctrl lalt ralt shift ctrl alt)) +(defvar llk-bindings nil) +(defvar llm-bindings nil) + +(defun llk-init () + (interactive) + (define-key special-event-map [low-level-key] 'llk-handle) + (define-key special-event-map [low-level-modifier] 'llm-handle) + + (setq llk-bindings nil) + (setq llm-bindings nil) + + ;; (llm-bind 'tap 'shift 'delete-other-windows) + ;; (llk-bind 'tap 'lctrl 'hyper) + (setq enable-low-level-key-events t)) + +;; For example: +;; (llk-add-binding 'tap 'lshift 'delete-other-windows) +;; Can bind to a command, a function or the symbol 'hyper. +(defun llk-bind (action key function) + (push (list action key function) llk-bindings)) + +(defun llm-bind (action key function) + (push (list action key function) llm-bindings)) + +;; We store the last events here to test for multitap. +(defvar llk-events nil) +(defvar llm-events nil) + +;; If positive, return key ('lshift, etc) else return nil. +(defun llk-detect-n-tap (n timeout) + ;; The physical-key event is like this: + ;; (physical-key t lshift 90196265 #) + ;; The second element is t for a key press, nil for a key release + ;; The fourth element is the time in milliseconds + ;; The fifth is the frame, we don't use it yet. + + (let ((key (cl-third last-input-event))) + (if (not (member key llk-tap-keys)) + ;; Key not in tap list, clear history + (setq llk-events nil) + ;; Clear it also if the first element is from a different key + (and llk-events + (not (equal (cl-third (car llk-events)) key)) + (setq llk-events nil)) + (push last-input-event llk-events) + ;; Only care about last 2xN events + (ntake (* 2 n) llk-events) + ;; If we have: + ;; - Exactly 2 * n events. + ;; - down, up, down, up, ... + ;; - not two much time between first and last + (and (eq (* 2 n) (length llk-events)) + (cl-every 'eq + (ntake (* 2 n) + (list nil t nil t nil t nil t + nil t nil t nil t nil t)) + (mapcar 'cl-second llk-events)) + (< (- (cl-fourth (cl-first llk-events)) + (cl-fourth (car (last llk-events)))) + timeout) + (progn + (setq llk-events nil) + key))))) + + +;; this function is a copy of llk-detect-n-tap, but for llm-events +(defun llm-detect-n-tap (n timeout) + (let ((key (cl-third last-input-event))) + (if (not (member key llk-tap-keys)) + (setq llm-events nil) + (and llm-events + (not (equal (cl-third (car llm-events)) key)) + (setq llm-events nil)) + (push last-input-event llm-events) + (ntake (* 2 n) llm-events) + (and (eq (* 2 n) (length llm-events)) + (cl-every 'eq + (ntake (* 2 n) + (list nil t nil t nil t nil t + nil t nil t nil t nil t)) + (mapcar 'cl-second llm-events)) + (< (- (cl-fourth (cl-first llm-events)) + (cl-fourth (car (last llm-events)))) + timeout) + (progn + (setq llm-events nil) + key))))) + +(defun llk-handle () + (interactive) + + (let ((tap-key (llk-detect-n-tap + llk-tap-count + llk-tap-timeout))) + (when tap-key + (let ((func (cl-third + (seq-find + (lambda (b) + (and (eq (cl-first b) 'tap) + (eq (cl-second b) tap-key))) + llk-bindings)))) + (cond + ((commandp func) (call-interactively func)) + ((functionp func) (funcall func)) + ((eq 'hyper func) + (message "H-...") + (let ((r (read-event))) + (setq unread-command-events + (list (event-apply-modifier + r 'hyper 24 "H-")))))))))) + +(defun llm-handle() + (interactive) + + (let ((tap-key (llm-detect-n-tap + llk-tap-count + llk-tap-timeout))) + (when tap-key + (let ((func (cl-third + (seq-find + (lambda (b) + (and (eq (cl-first b) 'tap) + (eq (cl-second b) tap-key))) + llm-bindings)))) + (cond + ((commandp func) (call-interactively func)) + ((functionp func) (funcall func)) + ((eq 'hyper func) + (message "H-...") + (let ((r (read-event))) + (setq unread-command-events + (list (event-apply-modifier + r 'hyper 24 "H-")))))))))) diff --git a/src/gtkutil.c b/src/gtkutil.c index d57627f152f..6ca4e313929 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -98,6 +98,7 @@ G_DEFINE_TYPE (EmacsMenuBar, emacs_menu_bar, GTK_TYPE_MENU_BAR) static void xg_im_context_preedit_changed (GtkIMContext *, gpointer); static void xg_im_context_preedit_end (GtkIMContext *, gpointer); static bool xg_widget_key_press_event_cb (GtkWidget *, GdkEvent *, gpointer); +static bool xg_widget_key_release_event_cb (GtkWidget *, GdkEvent *, gpointer); #endif #if GTK_CHECK_VERSION (3, 10, 0) @@ -1749,6 +1750,12 @@ xg_create_frame_widgets (struct frame *f) g_signal_connect (G_OBJECT (wfixed), "key-press-event", G_CALLBACK (xg_widget_key_press_event_cb), NULL); + + g_signal_connect (G_OBJECT (wfixed), "key-release-event", + G_CALLBACK (xg_widget_key_release_event_cb), + NULL); + + #endif { @@ -6376,6 +6383,105 @@ xg_im_context_preedit_end (GtkIMContext *imc, gpointer user_data) kbd_buffer_store_event (&inev); } +static void +xg_maybe_send_low_level_key_event (struct frame *f, + GdkEvent *xev) +{ + GdkEventKey xkey = xev->key; + bool is_press; + int keysym; + Lisp_Object key, modifier; + union buffered_input_event inev; + + if (!Venable_low_level_key_events) + return; + switch (xev->type) + { + case GDK_KEY_PRESS: + is_press = true; + break; + case GDK_KEY_RELEASE: + is_press = false; + break; + default: + return; + } + + keysym = xkey.keyval; + + switch (keysym) + { + case GDK_KEY_Shift_L: key = Qlshift; break; + case GDK_KEY_Shift_R: key = Qrshift; break; + case GDK_KEY_Control_L: key = Qlctrl; break; + case GDK_KEY_Control_R: key = Qrctrl; break; + case GDK_KEY_Alt_L: key = Qlalt; break; + case GDK_KEY_Alt_R: key = Qralt; break; + default: + key = Qnil; + } + + switch (keysym) + { + case GDK_KEY_Shift_L: + case GDK_KEY_Shift_R: + modifier = Qshift; + break; + case GDK_KEY_Control_L: + case GDK_KEY_Control_R: + modifier = Vx_ctrl_keysym; + if (NILP (modifier)) + modifier = Qctrl; + break; + case GDK_KEY_Alt_L: + case GDK_KEY_Alt_R: + modifier = Vx_meta_keysym; + if (NILP (modifier)) + modifier = Qalt; + break; + case GDK_KEY_Meta_L: + case GDK_KEY_Meta_R: + modifier = Vx_meta_keysym; + if (NILP (modifier)) + modifier = Qmeta; + break; + case GDK_KEY_Hyper_L: + case GDK_KEY_Hyper_R: + modifier = Vx_hyper_keysym; + if (NILP (modifier)) + modifier = Qhyper; + break; + case GDK_KEY_Super_L: + case GDK_KEY_Super_R: + modifier = Vx_super_keysym; + if (NILP (modifier)) + modifier = Qsuper; + break; + default: + modifier = Qnil; + } + + if (!NILP (key)) + { + EVENT_INIT (inev.ie); + XSETFRAME (inev.ie.frame_or_window, f); + inev.ie.kind = LOW_LEVEL_KEY_EVENT; + inev.ie.timestamp = xkey.time; + inev.ie.arg = list2 (is_press ? Qt : Qnil, key); + kbd_buffer_store_buffered_event (&inev, &xg_pending_quit_event); + } + + if (!NILP (modifier)) + { + EVENT_INIT (inev.ie); + XSETFRAME (inev.ie.frame_or_window, f); + inev.ie.kind = LOW_LEVEL_MODIFIER_KEY_EVENT; + inev.ie.timestamp = xkey.time; + inev.ie.arg = list2 (is_press ? Qt : Qnil, modifier); + kbd_buffer_store_buffered_event (&inev, &xg_pending_quit_event); + } +} + static bool xg_widget_key_press_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) @@ -6404,6 +6510,10 @@ xg_widget_key_press_event_cb (GtkWidget *widget, GdkEvent *event, if (!f) return true; +#ifndef HAVE_XINPUT2 + xg_maybe_send_low_level_key_event (f, event); +#endif + if (popup_activated ()) return true; @@ -6557,6 +6667,29 @@ xg_widget_key_press_event_cb (GtkWidget *widget, GdkEvent *event, return true; } +static bool +xg_widget_key_release_event_cb (GtkWidget *widget, GdkEvent *event, + gpointer user_data) +{ +#ifndef HAVE_XINPUT2 + Lisp_Object tail, tem; + struct frame *f = NULL; + + FOR_EACH_FRAME (tail, tem) + { + if (FRAME_X_P (XFRAME (tem)) + && (FRAME_GTK_WIDGET (XFRAME (tem)) == widget)) + { + f = XFRAME (tem); + break; + } + } + if (f) + xg_maybe_send_low_level_key_event (f, event); +#endif + return true; +} + bool xg_filter_key (struct frame *frame, XEvent *xkey) { diff --git a/src/keyboard.c b/src/keyboard.c index 6d28dca9aeb..532a1d5dbfe 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4274,6 +4274,8 @@ kbd_buffer_get_event (KBOARD **kbp, case CONFIG_CHANGED_EVENT: case FOCUS_OUT_EVENT: case SELECT_WINDOW_EVENT: + case LOW_LEVEL_KEY_EVENT: + case LOW_LEVEL_MODIFIER_KEY_EVENT: { obj = make_lispy_event (&event->ie); kbd_fetch_ptr = next_kbd_event (event); @@ -7118,6 +7120,22 @@ make_lispy_event (struct input_event *event) case PREEDIT_TEXT_EVENT: return list2 (Qpreedit_text, event->arg); + case LOW_LEVEL_KEY_EVENT: + return listn (5, + Qlow_level_key, + XCAR (event->arg), /* Press or release. */ + XCAR (XCDR (event->arg)), /* The key symbol. */ + make_fixnum (event->timestamp), + event->frame_or_window); + + case LOW_LEVEL_MODIFIER_KEY_EVENT: + return listn (5, + Qlow_level_modifier, + XCAR (event->arg), /* Press or release. */ + XCAR (XCDR (event->arg)), /* The key symbol. */ + make_fixnum (event->timestamp), + event->frame_or_window); + /* The 'kind' field of the event is something we don't recognize. */ default: emacs_abort (); @@ -12931,6 +12949,20 @@ syms_of_keyboard (void) DEFSYM (Qfile_notify, "file-notify"); #endif /* USE_FILE_NOTIFY */ + DEFVAR_LISP ("enable-low-level-key-events", Venable_low_level_key_events, + doc: /* Enabled the recepcion of low level key events. +This includes 'low-level-key' and 'low-level-modifier' events. */); + Venable_low_level_key_events = false; + + DEFSYM (Qlow_level_key, "low-level-key"); + DEFSYM (Qlow_level_modifier, "low-level-modifier"); + DEFSYM (Qlshift, "lshift"); + DEFSYM (Qrshift, "rshift"); + DEFSYM (Qlctrl, "lctrl"); + DEFSYM (Qrctrl, "rctrl"); + DEFSYM (Qlalt, "lalt"); + DEFSYM (Qralt, "ralt"); + DEFSYM (Qtouch_end, "touch-end"); /* Menu and tool bar item parts. */ @@ -14018,6 +14050,12 @@ keys_of_keyboard (void) "handle-focus-out"); initial_define_lispy_key (Vspecial_event_map, "move-frame", "handle-move-frame"); + initial_define_lispy_key (Vspecial_event_map, "low-level-key", + "ignore"); + initial_define_lispy_key (Vspecial_event_map, "low-level-modifier", + "ignore"); + + } /* Mark the pointers in the kboard objects. diff --git a/src/pgtkterm.c b/src/pgtkterm.c index 079945126e0..7091e9ef4c3 100644 --- a/src/pgtkterm.c +++ b/src/pgtkterm.c @@ -5201,6 +5201,39 @@ pgtk_enqueue_preedit (struct frame *f, Lisp_Object preedit) evq_enqueue (&inev); } +static void +pgtk_maybe_send_low_level_key_event (GdkEvent *event) +{ + if (!Venable_low_level_key_events) + return; + + Lisp_Object key; + switch (event->key.keyval) + { + case GDK_KEY_Shift_L: key = Qlshift; break; + case GDK_KEY_Shift_R: key = Qrshift; break; + case GDK_KEY_Control_L: key = Qlctrl; break; + case GDK_KEY_Control_R: key = Qrctrl; break; + case GDK_KEY_Alt_L: key = Qlalt; break; + case GDK_KEY_Alt_R: key = Qralt; break; + default: + return; + } + bool keypress = event->key.type == GDK_KEY_PRESS; + struct frame *f = pgtk_any_window_to_frame (event->key.window); + if (!f) + return; + + union buffered_input_event inev; + + EVENT_INIT (inev.ie); + XSETFRAME (inev.ie.frame_or_window, f); + inev.ie.kind = LOW_LEVEL_KEY_EVENT; + inev.ie.timestamp = event->key.time; + inev.ie.arg = list2 (keypress ? Qt : Qnil, key); + evq_enqueue (&inev); +} + static gboolean key_press_event (GtkWidget *widget, GdkEvent *event, gpointer *user_data) { @@ -5210,6 +5243,8 @@ key_press_event (GtkWidget *widget, GdkEvent *event, gpointer *user_data) struct frame *f; struct pgtk_display_info *dpyinfo; + pgtk_maybe_send_low_level_key_event(event); + f = pgtk_any_window_to_frame (gtk_widget_get_window (widget)); EVENT_INIT (inev.ie); hlinfo = MOUSE_HL_INFO (f); @@ -5454,6 +5489,8 @@ key_release_event (GtkWidget *widget, GdkDisplay *display; struct pgtk_display_info *dpyinfo; + pgtk_maybe_send_low_level_key_event(event); + display = gtk_widget_get_display (widget); dpyinfo = pgtk_display_info_for_display (display); diff --git a/src/termhooks.h b/src/termhooks.h index d6a9300bac9..006b74809a3 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -347,6 +347,8 @@ #define EMACS_TERMHOOKS_H /* In a NOTIFICATION_EVENT, .arg is a lambda to evaluate. */ , NOTIFICATION_EVENT #endif /* HAVE_ANDROID */ + , LOW_LEVEL_KEY_EVENT + , LOW_LEVEL_MODIFIER_KEY_EVENT }; /* Bit width of an enum event_kind tag at the start of structs and unions. */ diff --git a/src/w32fns.c b/src/w32fns.c index e2455b9271e..f1850a5f2f3 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -4669,6 +4669,11 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_KEYUP: case WM_SYSKEYUP: record_keyup (wParam, lParam); + if (Venable_low_level_key_events) + { + signal_user_input (); + my_post_msg( &wmsg, hwnd, WM_EMACS_LOW_LEVEL_KEY, wParam, lParam ); + } goto dflt; case WM_KEYDOWN: @@ -4695,6 +4700,12 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) if (w32_use_fallback_wm_chars_method) wParam = map_keypad_keys (wParam, (lParam & 0x1000000L) != 0); + if (Venable_low_level_key_events) + { + signal_user_input (); + my_post_msg( &wmsg, hwnd, WM_EMACS_LOW_LEVEL_KEY, wParam, lParam ); + } + windows_translate = 0; switch (wParam) diff --git a/src/w32term.c b/src/w32term.c index e18f39dd2a8..9fae9701daf 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -5270,6 +5270,75 @@ w32_read_socket (struct terminal *terminal, } break; + case WM_EMACS_LOW_LEVEL_KEY: + WORD key_flags = HIWORD (msg.msg.lParam); + BOOL is_wm_keyup = key_flags & KF_UP; + + if (is_wm_keyup || (key_flags & KF_REPEAT) == 0) /* WM_KEYDOWN, not repeating. */ + { + WORD scan_code = LOBYTE (key_flags); + if (key_flags & KF_EXTENDED) + scan_code = MAKEWORD (scan_code, 0xE0); + + UINT translated = MapVirtualKey (scan_code, MAPVK_VSC_TO_VK_EX); + WORD vk = LOWORD (msg.msg.wParam); + if (translated) + vk = LOWORD (translated); + + Lisp_Object key = Qnil; + Lisp_Object modifier = Qnil; + + switch (vk) + { + case VK_LSHIFT: key = Qlshift; break; + case VK_RSHIFT: key = Qrshift; break; + case VK_LCONTROL: key = Qlctrl; break; + case VK_RCONTROL: key = Qrctrl; break; + case VK_LMENU: key = Qlalt; break; + case VK_RMENU: key = Qralt; break; + } + + switch (vk) + { + case VK_LSHIFT: + case VK_RSHIFT: + modifier = Qshift; + break; + case VK_LCONTROL: + case VK_RCONTROL: + modifier = Qctrl; + break; + case VK_LMENU: + case VK_RMENU: + modifier = Qmeta; + break; + } + + if (!NILP (key)) + { + f = w32_window_to_frame (dpyinfo, msg.msg.hwnd); + inev.kind = LOW_LEVEL_KEY_EVENT; + XSETFRAME (inev.frame_or_window, f); + inev.timestamp = msg.msg.time; + inev.arg = list2 (is_wm_keyup ? Qnil : Qt, key); + kbd_buffer_store_event_hold (&inev, hold_quit); + + } + + if (!NILP (modifier)) + { + f = w32_window_to_frame (dpyinfo, msg.msg.hwnd); + inev.kind = LOW_LEVEL_MODIFIER_KEY_EVENT; + XSETFRAME (inev.frame_or_window, f); + inev.timestamp = msg.msg.time; + inev.arg = list2 (is_wm_keyup ? Qnil : Qt, modifier); + kbd_buffer_store_event_hold (&inev, hold_quit); + } + inev.kind = NO_EVENT; + + } + break; + case WM_UNICHAR: case WM_SYSCHAR: case WM_CHAR: diff --git a/src/w32term.h b/src/w32term.h index cad9fcf8cb1..88f7dfeef8b 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -713,7 +713,8 @@ #define WM_EMACS_FILENOTIFY (WM_EMACS_START + 25) #define WM_EMACS_IME_STATUS (WM_EMACS_START + 26) #define WM_EMACS_DRAGOVER (WM_EMACS_START + 27) #define WM_EMACS_DROP (WM_EMACS_START + 28) -#define WM_EMACS_END (WM_EMACS_START + 29) +#define WM_EMACS_LOW_LEVEL_KEY (WM_EMACS_START + 29) +#define WM_EMACS_END (WM_EMACS_START + 30) #define WND_FONTWIDTH_INDEX (0) #define WND_LINEHEIGHT_INDEX (4) diff --git a/src/xterm.c b/src/xterm.c index 0c20d38b0f7..97028c32336 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -17840,6 +17840,143 @@ #define STORE_KEYSYM_FOR_DEBUG(keysym) ((void)0) static struct x_display_info *next_noop_dpyinfo; +static void +x_maybe_send_low_level_key_event (struct x_display_info *dpyinfo, + const XEvent *xev) +{ + XKeyEvent xkey; + bool is_press; + KeySym keysym; + Lisp_Object key, modifier; + struct input_event ie; + + if (!Venable_low_level_key_events) + return; + + switch (xev->type) + { + case KeyPress: + is_press = true; + xkey = xev->xkey; + break; + case KeyRelease: + is_press = false; + xkey = xev->xkey; + break; +#ifdef HAVE_XINPUT2 + case GenericEvent: + XIDeviceEvent *xiev = xev->xcookie.data; + switch (xev->xgeneric.evtype) + { + case XI_KeyPress: + is_press = true; + break; + case XI_KeyRelease: + is_press = false; + break; + default: + return; + } + + xkey.serial = xiev->serial; + xkey.send_event = xiev->send_event; + xkey.display = xiev->display; + xkey.window = xiev->event; + xkey.root = xiev->root; + xkey.subwindow = xiev->child; + xkey.time = xiev->time; + xkey.x = xiev->event_x; + xkey.y = xiev->event_y; + xkey.x_root = xiev->root_x; + xkey.y_root = xiev->root_y; + xkey.state = xiev->mods.effective; + xkey.keycode = xiev->detail; + xkey.same_screen = 1; + break; +#endif + default: + return; + } + + struct frame *f = x_any_window_to_frame (dpyinfo, xkey.window); + if (!f) + return; + + XLookupString (&xkey, NULL, 0, &keysym, NULL); + + switch (keysym) + { + case XK_Shift_L: key = Qlshift; break; + case XK_Shift_R: key = Qrshift; break; + case XK_Control_L: key = Qlctrl; break; + case XK_Control_R: key = Qrctrl; break; + case XK_Alt_L: key = Qlalt; break; + case XK_Alt_R: key = Qralt; break; + default: + key = Qnil; + } + + switch (keysym) + { + case XK_Shift_L: + case XK_Shift_R: + modifier = Qshift; + break; + case XK_Control_L: + case XK_Control_R: + modifier = Vx_ctrl_keysym; + if (NILP (modifier)) + modifier = Qctrl; + break; + case XK_Alt_L: + case XK_Alt_R: + modifier = Vx_meta_keysym; + if (NILP (modifier)) + modifier = Qalt; + break; + case XK_Meta_L: + case XK_Meta_R: + modifier = Vx_meta_keysym; + if (NILP (modifier)) + modifier = Qmeta; + break; + case XK_Hyper_L: + case XK_Hyper_R: + modifier = Vx_hyper_keysym; + if (NILP (modifier)) + modifier = Qhyper; + break; + case XK_Super_L: + case XK_Super_R: + modifier = Vx_super_keysym; + if (NILP (modifier)) + modifier = Qsuper; + break; + default: + modifier = Qnil; + } + + if (!NILP (key)) + { + EVENT_INIT (ie); + XSETFRAME (ie.frame_or_window, f); + ie.kind = LOW_LEVEL_KEY_EVENT; + ie.timestamp = xkey.time; + ie.arg = list2 (is_press ? Qt : Qnil, key); + kbd_buffer_store_event (&ie); + } + + if (!NILP (modifier)) + { + EVENT_INIT (ie); + XSETFRAME (ie.frame_or_window, f); + ie.kind = LOW_LEVEL_MODIFIER_KEY_EVENT; + ie.timestamp = xkey.time; + ie.arg = list2 (is_press ? Qt : Qnil, key); + kbd_buffer_store_event (&ie); + } +} + /* Filter events for the current X input method. DPYINFO is the display this event is for. EVENT is the X event to filter. @@ -20206,6 +20343,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, goto OTHER; case KeyPress: + x_maybe_send_low_level_key_event (dpyinfo, event); x_display_set_last_user_time (dpyinfo, event->xkey.time, event->xkey.send_event, true); @@ -20715,6 +20853,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, #endif case KeyRelease: + x_maybe_send_low_level_key_event (dpyinfo, event); #ifdef HAVE_X_I18N /* Don't dispatch this event since XtDispatchEvent calls XFilterEvent, and two calls in a row may freeze the @@ -23970,6 +24109,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, struct xi_device_t *device, *source; XKeyPressedEvent xkey; + x_maybe_send_low_level_key_event (dpyinfo, event); + coding = Qlatin_1; /* The code under this label is quite desultory. There @@ -24586,6 +24727,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, #endif case XI_KeyRelease: + x_maybe_send_low_level_key_event (dpyinfo, event); + #if defined HAVE_X_I18N || defined USE_GTK || defined USE_LUCID { XKeyPressedEvent xkey; @@ -32662,6 +32805,7 @@ syms_of_xterm (void) Vx_toolkit_scroll_bars = Qnil; #endif + DEFSYM (Qshift, "shift"); DEFSYM (Qmodifier_value, "modifier-value"); DEFSYM (Qctrl, "ctrl"); Fput (Qctrl, Qmodifier_value, make_fixnum (ctrl_modifier)); -- 2.35.1.windows.2