unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#74423: Low level key events
       [not found]   ` <s1r8qtzsvbe.fsf@yahoo.com>
@ 2024-11-18 20:35     ` Cecilio Pardo
  2024-11-18 23:49       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
                         ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Cecilio Pardo @ 2024-11-18 20:35 UTC (permalink / raw)
  To: 74423

[-- Attachment #1: Type: text/plain, Size: 1798 bytes --]

Here is a still incomplete patch for this, renamed from "physical 
keyboard events", following up to
https://lists.gnu.org/archive/html/emacs-devel/2024-11/msg00085.html

It provides events for press/release of keys, independently of the 
normal keyboard events. These events are bound int the 
special-event-map. Some lisp is included to implement detection of 
multiple tapping on keys, and running commands or simulating modifiers.

Currently, only modifier keys are available, because other keys would 
send their own normal event. This could change when an use case for 
other keys appears.

To provide events for 'logical' modifiers Ctrl, Meta, etc., now there is 
a new event low-level-modifier, in addition to low-level-key. If you 
press Shift_L, two events will be generated, one for 'shift as 
low-level-modifier, and one for 'lshift as low-level-key. The lisp part 
handles those events separately.

I added a variable that enables/disables everything, 
enable-low-level-key-events.

This are some of Po Lu's notes on the previous patch:

> [...] Emacs must not depend on XKB for such a basic feature.

Fixed.

> Please find some means of generating such events by saving them into the
> keyboard event buffer defined in handle_ome_xevent.

As we may generate two events, that is not possible.

> x_filter_events is not the proper location for this call.  It is only
> invoked to filter an event through the active GTK or X input method
> context, and it is not invoked if Emacs is configured not to open input
> method contexts, or they are unavailable by happenstance.

Done. If XINPUT is enabled, the XI_KeyPress event used, over the gtk 
one. If not, the gtk event is used unless there is no input method. In 
this case KeyPress event is used.

The pgtk version is not updated.





[-- Attachment #2: 0001-Add-events-for-key-press-and-key-release-on-gui-syst.patch --]
[-- Type: text/plain, Size: 24075 bytes --]

From d2403d9fa14b498263abb242324c21441a4246aa Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
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 #<frame>)
+  ;; 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


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* bug#74423: Low level key events
  2024-11-18 20:35     ` bug#74423: Low level key events Cecilio Pardo
@ 2024-11-18 23:49       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-11-19 15:29       ` Eli Zaretskii
  2024-11-19 16:43       ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2 siblings, 0 replies; 6+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-11-18 23:49 UTC (permalink / raw)
  To: Cecilio Pardo; +Cc: 74423

Cecilio Pardo <cpardo@imayhem.com> writes:

> +   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;
> +    }

This is not the proper means of establishing the modifier bound to a key
symbol in GTK, but I cannot immediately tell you what the alternative is.

> +	  my_post_msg( &wmsg, hwnd, WM_EMACS_LOW_LEVEL_KEY, wParam, lParam );
> +	}

> +	  my_post_msg( &wmsg, hwnd, WM_EMACS_LOW_LEVEL_KEY, wParam, lParam );
> +	}

Stylistic issues.

> +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;
> +    }

This doesn't generate `ralt' events on my system, as that key produces
the keysym ISO_Level3_Shift/

> +   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;
> +    }

You are computing the modifier event to be generated incorrectly.  You
should search the modifier map (dpyinfo->modmap) for columns containing
received keysyms, and compare modifier bits derived from the rows where
they appear against meta_mod_mask, shift_lock_mask, alt_mod_mask,
super_mod_mask, and hyper_mod_mask in the display structure, or CtrlMask
and ShiftMask.

>      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);

Would you rearrange the locations of calls to x_any_window_to_frame in
handle_one_xevent so that you need not redundantly call the same
function in x_maybe_send_low_level_key_event?

Thanks for your interest in Emacs.





^ permalink raw reply	[flat|nested] 6+ messages in thread

* bug#74423: Low level key events
  2024-11-18 20:35     ` bug#74423: Low level key events Cecilio Pardo
  2024-11-18 23:49       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-11-19 15:29       ` Eli Zaretskii
  2024-11-19 16:43       ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2 siblings, 0 replies; 6+ messages in thread
From: Eli Zaretskii @ 2024-11-19 15:29 UTC (permalink / raw)
  To: Cecilio Pardo, Stefan Monnier; +Cc: 74423

> Date: Mon, 18 Nov 2024 21:35:40 +0100
> From: Cecilio Pardo <cpardo@imayhem.com>
> 
> Here is a still incomplete patch for this, renamed from "physical 
> keyboard events", following up to
> https://lists.gnu.org/archive/html/emacs-devel/2024-11/msg00085.html
> 
> It provides events for press/release of keys, independently of the 
> normal keyboard events. These events are bound int the 
> special-event-map. Some lisp is included to implement detection of 
> multiple tapping on keys, and running commands or simulating modifiers.

Thanks, I have some minor comments, and also added Stefan to the
discussion, in case he will also have comments.

> +  if (!Venable_low_level_key_events)

You treat this variable as a C boolean, i.e. assume it was defined
with DEFVAR_BOOL, but in that case our convention is to call the C
variables without the leading 'V'.  OTOH, if you envision that this
variable could be something other than nil or t in Lisp, then you need
to use NILP here.

> +  DEFVAR_LISP ("enable-low-level-key-events", Venable_low_level_key_events,
> +	       doc: /* Enabled the recepcion of low level key events.
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
"If non-nil, reception of low-level key events is enabled."

> +static void
> +pgtk_maybe_send_low_level_key_event (GdkEvent *event)
> +{
> +  if (!Venable_low_level_key_events)
> +    return;

Same problem with the test here.

> --- 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 );
> +	}

And here (and in several other places in the patch).





^ permalink raw reply	[flat|nested] 6+ messages in thread

* bug#74423: Low level key events
  2024-11-18 20:35     ` bug#74423: Low level key events Cecilio Pardo
  2024-11-18 23:49       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-11-19 15:29       ` Eli Zaretskii
@ 2024-11-19 16:43       ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-11-19 20:05         ` Cecilio Pardo
  2 siblings, 1 reply; 6+ messages in thread
From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-11-19 16:43 UTC (permalink / raw)
  To: Cecilio Pardo; +Cc: 74423

> It provides events for press/release of keys, independently of the normal
> keyboard events. These events are bound int the special-event-map. Some lisp
> is included to implement detection of multiple tapping on keys, and running
> commands or simulating modifiers.

I hadn't followed the discussion over at emacs-devel, but this is cool.
I used to have a local patch which generated extra events for all key
presses/releases.  I never wrote any useful Lisp-level code for it
(all I had were bindings to ignore those events so Emacs was still
usable 🙂), but I really think it would make for fun new hacks.

I haven't had time to look at your whole patch, but here are some comments.

> +  (let ((key (cl-third last-input-event)))

Please try and use `event-*` functions.  If none serve (as will often be
the case), define local "replacements" until they can be promoted to
`subr.el`.

> +         ((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-"))))))))))

Move that code to a function, so you can get rid of this `hyper`
special case.  BTW, any reason why you couldn't use
`event-apply-hyper-modifier`?

[ BTW, this becomes more ... interesting ... when you want to be able to
  cumulative that for several modifiers, in which case your `read-event`
  might return an event which is not the one to which you want to add
  `H-`.  ]

> +  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;
> +    }

I think the list of low-level keys handled here should not be hard-coded.
IOW, maybe `enable-low-level-keys` should not be a boolean but
a list/map/table indicating which keys to handle.

> +  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);
> +    }
> +}

So, IIUC you might generate 2 low-level events for a single key press?
Why?

Other note: in the distant past (back around Emacs-21) I seem to
remember Gerd making changes to the event structure so as to avoid
allocating Lisp objects for this code.  I think it was related to
problems due to running this low-level event-handler code from within
a C signal handler, which is a practice was have since stopped, luckily,
but maybe there are still good reasons to try and avoid involving
allocating objects into the Lisp heap in this low-level code.


        Stefan






^ permalink raw reply	[flat|nested] 6+ messages in thread

* bug#74423: Low level key events
  2024-11-19 16:43       ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-11-19 20:05         ` Cecilio Pardo
  2024-11-20  4:21           ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 6+ messages in thread
From: Cecilio Pardo @ 2024-11-19 20:05 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: 74423

On 19/11/2024 17:43, Stefan Monnier wrote:

>> +         ((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-"))))))))))
> 
> Move that code to a function, so you can get rid of this `hyper`
> special case.  BTW, any reason why you couldn't use
> `event-apply-hyper-modifier`?

No, no reason. I will change that.

> I think the list of low-level keys handled here should not be hard-coded.
> IOW, maybe `enable-low-level-keys` should not be a boolean but
> a list/map/table indicating which keys to handle.

I was sending the keys for which I have an immediate use case. Using a 
list looks got, adding some special symbols to choose groups, such as 
"all modifier keys".

> So, IIUC you might generate 2 low-level events for a single key press?
> Why?

We want to allow to detect modifier keys, regardless of the key that is 
used. For example, when you press Shift_L we generate an event for 
'lshift and other (of different type) for the modifier 'shift.

> Other note: in the distant past (back around Emacs-21) I seem to
> remember Gerd making changes to the event structure so as to avoid
> allocating Lisp objects for this code.  I think it was related to
> problems due to running this low-level event-handler code from within
> a C signal handler, which is a practice was have since stopped, luckily,
> but maybe there are still good reasons to try and avoid involving
> allocating objects into the Lisp heap in this low-level code.

I will look into this.

Thanks for your comments!







^ permalink raw reply	[flat|nested] 6+ messages in thread

* bug#74423: Low level key events
  2024-11-19 20:05         ` Cecilio Pardo
@ 2024-11-20  4:21           ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 0 replies; 6+ messages in thread
From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-11-20  4:21 UTC (permalink / raw)
  To: Cecilio Pardo; +Cc: 74423

>> Move that code to a function, so you can get rid of this `hyper`
>> special case.  BTW, any reason why you couldn't use
>> `event-apply-hyper-modifier`?
> No, no reason. I will change that.

To be clear: I don't know that it works here.

>> I think the list of low-level keys handled here should not be hard-coded.
>> IOW, maybe `enable-low-level-keys` should not be a boolean but
>> a list/map/table indicating which keys to handle.
> I was sending the keys for which I have an immediate use case. Using a list
> looks got, adding some special symbols to choose groups, such as "all
> modifier keys".

I was also thinking that maybe the list/table could be used to avoid
having to define in the C code what is a "modifier key" and things
like that.

>> So, IIUC you might generate 2 low-level events for a single key press?
>> Why?
> We want to allow to detect modifier keys, regardless of the key that is
> used.  For example, when you press Shift_L we generate an event for 'lshift
> and other (of different type) for the modifier 'shift.

But my question is about the fact that you generate two events, not
about the two pieces of information.
IOW you could also generate a single event with both pieces of
information (and if use the "normal" keymaps instead of
`special-event-map` then you might even have a `function-key-map` rule
which converts a `Shift_L` down to a `shift` if there was no binding for
the `Shift_L` event).


        Stefan






^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-11-20  4:21 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <fb50ec11-7aec-481e-8a3a-ecdcf22eb7c0@imayhem.com>
     [not found] ` <31bdc55d-8c13-4de0-9cef-bd6cc4fb033f@imayhem.com>
     [not found]   ` <s1r8qtzsvbe.fsf@yahoo.com>
2024-11-18 20:35     ` bug#74423: Low level key events Cecilio Pardo
2024-11-18 23:49       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-11-19 15:29       ` Eli Zaretskii
2024-11-19 16:43       ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-11-19 20:05         ` Cecilio Pardo
2024-11-20  4:21           ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).