emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: master 3b41141708: Expose the name of an event's input device to Lis


From: Po Lu
Subject: Re: master 3b41141708: Expose the name of an event's input device to Lisp
Date: Sat, 09 Apr 2022 19:44:57 +0800
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.91 (gnu/linux)

Eli Zaretskii <eliz@gnu.org> writes:

> No, only for events that are specific to the device and not available
> in other devices.

But the point is that they aren't specific to the device.  The device
will in the general case behave like any other device (a touchpad will
scroll just like a mouse, and an eraser will move the pointer like any
other stylus), but inside specific use cases, such as precision
scrolling and artist-mode, they will have to behave differently.

> So, with your current design and implementation, what level will deal
> with the device type and decide how to handle each event with the
> device type in mind?  Events are bound to commands in Emacs, so does
> this mean each command will need to know about the device to DTRT?

The default events (i.e. button and motion events from an eraser) is
usually enough to DTRT, but a command can also chose to treat it as an
eraser instead of an ordinary pointing device.

The individual command will have to know about the device to treat it
correctly, just as elsewhere: web browsers, programs using GTK, Qt, etc.

> And what would happen instead under your design and implementation?
> won't we need to add code to recognize the new device?

We would not.  The user could write code to recognize the device based
on its name (or other identifiers.)

> I don't see how can you so sure about this.  A name is just a string.
> With devices development these days, chances are what is a unique name
> today will cease to be that tomorrow.

The X server and Wayland compositor guarantee that the name is unique to
each device.  The developers of the low-level libraries and drivers used
on those display servers also treat the names of devices as something
permanent that should not be changed, since not only applications and
toolkits rely on them, but also many kinds of hardware database files
and "quirks".

> In general, I have hard time imagining that modern systems let you
> access the precise and detailed device information, because (AFAIK)
> modern systems typically hide this information from applications.

IME, that trend has been reversed as of late.  Major toolkits which
previously preferred to define special event types ("scroll" events for
wheel mice, and "smooth scroll" events for touchpads) are now exposing
the device directly.  I hear that a similar thing is happening to web
browsers, but I didn't look at the web browser APIs in that much detail.
Especially on GNU/Linux, where the input drivers and window server shove
many details of relevance into the device itself.

Some other Emacs APIs will probably have to change also:
`set-mouse-position' will have to be changed to accept a device, so that
multi-pointer X can work correctly.  Otherwise, the fallback "client
pointer" will be moved, which is not always the pointer that the user is
using to interact with Emacs.

> How will this different behavior implemented, under your current
> proposals?  Which code will deal with the device information, and how
> will that eventually affect behavior?  Please show code examples if
> possible.

My idea is that the commands that want device-specific behavior will
behave specially.  For example, the following change to artist-mode
causes erasers to erase characters instead of drawing them:

diff --git a/lisp/textmodes/artist.el b/lisp/textmodes/artist.el
index e37b0d988a..47dd70e4f1 100644
--- a/lisp/textmodes/artist.el
+++ b/lisp/textmodes/artist.el
@@ -4786,7 +4786,11 @@ artist-down-mouse-1
          (artist-mode-line-show-curr-operation t)
 
          (cond ((eq draw-how 'artist-do-continously)
-                (artist-mouse-draw-continously ev))
+                (artist-mouse-draw-continously ev
+                                                (when (eq (device-class 
last-event-frame
+                                                                        
last-event-device)
+                                                          'eraser)
+                                                  'erase-char)))
                ((eq draw-how 'artist-do-poly)
                 (artist-mouse-draw-poly ev))
                ((and (numberp draw-how) (= draw-how 1))
@@ -4897,14 +4901,16 @@ artist--adjust-x
         x
       (- x adjust 2))))
 
-(defun artist-mouse-draw-continously (ev)
+(defun artist-mouse-draw-continously (ev &optional override-op)
   "Generic function for shapes that require 1 point as input.
-Operation is done continuously while the mouse button is hold down.
-The event, EV, is the mouse event."
+Operation is done continuously while the mouse button is hold
+down.  The event, EV, is the mouse event.  OVERRIDE-OP is another
+operation to use, or nil."
   (let* ((unshifted    (artist-go-get-symbol-shift artist-curr-go nil))
         (shifted      (artist-go-get-symbol-shift artist-curr-go t))
         (shift-state  (artist-event-is-shifted ev))
-        (op           (if shift-state shifted unshifted))
+        (op           (or override-op
+                           (if shift-state shifted unshifted)))
         (draw-how     (artist-go-get-draw-how-from-symbol op))
         (init-fn      (artist-go-get-init-fn-from-symbol op))
         (prep-fill-fn (artist-go-get-prep-fill-fn-from-symbol op))

And this change would allow clicking the first button on a graphics
tablet (or a user-specified mouse) to select the "circle" drawing
operation instead of drawing something underneath the mouse pointer:

diff --git a/lisp/textmodes/artist.el b/lisp/textmodes/artist.el
index e37b0d988a..a8f5586ddf 100644
--- a/lisp/textmodes/artist.el
+++ b/lisp/textmodes/artist.el
@@ -4741,75 +4741,84 @@ artist-compute-up-event-key
 (defun artist-down-mouse-1 (ev)
   "Perform drawing action for event EV."
   (interactive "@e")
-  (let* ((real (artist-go-get-symbol-shift
-               artist-curr-go (artist-event-is-shifted ev)))
-        (draw-how (artist-go-get-draw-how-from-symbol real))
-        ;; Remember original values for draw-region-min-y and max-y
-        ;; in case we are interrupting a key-draw operation.
-        (orig-draw-region-min-y artist-draw-region-min-y)
-        (orig-draw-region-max-y artist-draw-region-max-y)
-        (orig-pointer-shape (if (eq window-system 'x) x-pointer-shape nil))
-        (echo-keystrokes 0)    ; Don't echo unfinished commands.
-        ;; Remember original binding for the button-up event to this
-        ;; button-down event.
-        (key (artist-compute-up-event-key ev))
-        (orig-button-up-binding (lookup-key (current-global-map) key)))
+  (if (or (eq (device-class last-event-frame
+                            last-event-device)
+              'pad)
+          (equal last-event-device ; Or in the future, (device-name
+                                   ; last-event-device)
+                 ;; A mouse the user wants to treat specially.  In the
+                 ;; real code this would be customizable, of course.
+                 "USB Optical Mouse (2)"))
+      (artist-select-op-circle)
+    (let* ((real (artist-go-get-symbol-shift
+                 artist-curr-go (artist-event-is-shifted ev)))
+          (draw-how (artist-go-get-draw-how-from-symbol real))
+          ;; Remember original values for draw-region-min-y and max-y
+          ;; in case we are interrupting a key-draw operation.
+          (orig-draw-region-min-y artist-draw-region-min-y)
+          (orig-draw-region-max-y artist-draw-region-max-y)
+          (orig-pointer-shape (if (eq window-system 'x) x-pointer-shape nil))
+          (echo-keystrokes 0)  ; Don't echo unfinished commands.
+          ;; Remember original binding for the button-up event to this
+          ;; button-down event.
+          (key (artist-compute-up-event-key ev))
+          (orig-button-up-binding (lookup-key (current-global-map) key)))
 
-    (unwind-protect
-       (progn
-         (if (eq window-system 'x)
-             (artist-set-pointer-shape artist-pointer-shape))
-
-         ;; Redefine the button-up binding temporarily (the original
-         ;; binding is restored in the unwind-forms below). This is to
-         ;; avoid the phenomenon outlined in this scenario:
-         ;;
-         ;; 1. A routine which reads something from the mini-buffer (such
-         ;;    as the text renderer) is called from below.
-         ;; 2. Meanwhile, the users releases the mouse button.
-         ;; 3. As a (funny :-) coincidence, the binding for the
-         ;;    button-up event is often mouse-set-point, so Emacs
-         ;;    sets the point to where the button was released, which is
-         ;;    in the buffer where the user wants to place the text.
-         ;; 4. The user types C-x o (or uses the mouse once again)
-         ;;    until he reaches the mini-buffer which is still prompting
-         ;;    for some text to render.
-         ;;
-         ;; To do this foolproof, all local and minor-mode maps should
-         ;; be searched and temporarily changed as well, since they
-         ;; too might have some binding for the button-up event,
-         ;; but I hope dealing with the global map will suffice.
-         (define-key (current-global-map) key 'artist-do-nothing)
+      (unwind-protect
+         (progn
+           (if (eq window-system 'x)
+               (artist-set-pointer-shape artist-pointer-shape))
+
+           ;; Redefine the button-up binding temporarily (the original
+           ;; binding is restored in the unwind-forms below). This is to
+           ;; avoid the phenomenon outlined in this scenario:
+           ;;
+           ;; 1. A routine which reads something from the mini-buffer (such
+           ;;    as the text renderer) is called from below.
+           ;; 2. Meanwhile, the users releases the mouse button.
+           ;; 3. As a (funny :-) coincidence, the binding for the
+           ;;    button-up event is often mouse-set-point, so Emacs
+           ;;    sets the point to where the button was released, which is
+           ;;    in the buffer where the user wants to place the text.
+           ;; 4. The user types C-x o (or uses the mouse once again)
+           ;;    until he reaches the mini-buffer which is still prompting
+           ;;    for some text to render.
+           ;;
+           ;; To do this foolproof, all local and minor-mode maps should
+           ;; be searched and temporarily changed as well, since they
+           ;; too might have some binding for the button-up event,
+           ;; but I hope dealing with the global map will suffice.
+           (define-key (current-global-map) key 'artist-do-nothing)
 
-         (artist-draw-region-reset)
+           (artist-draw-region-reset)
 
-         (artist-mode-line-show-curr-operation t)
+           (artist-mode-line-show-curr-operation t)
 
-         (cond ((eq draw-how 'artist-do-continously)
-                (artist-mouse-draw-continously ev))
-               ((eq draw-how 'artist-do-poly)
-                (artist-mouse-draw-poly ev))
-               ((and (numberp draw-how) (= draw-how 1))
-                (artist-mouse-draw-1point ev))
-               ((and (numberp draw-how) (= draw-how 2))
-                (artist-mouse-draw-2points ev))
-               (t (message "Drawing \"%s\"s is not yet implemented"
-                           draw-how)))
+           (cond ((eq draw-how 'artist-do-continously)
+                  (artist-mouse-draw-continously ev))
+                 ((eq draw-how 'artist-do-poly)
+                  (artist-mouse-draw-poly ev))
+                 ((and (numberp draw-how) (= draw-how 1))
+                  (artist-mouse-draw-1point ev))
+                 ((and (numberp draw-how) (= draw-how 2))
+                  (artist-mouse-draw-2points ev))
+                 (t (message "Drawing \"%s\"s is not yet implemented"
+                             draw-how)))
 
-         (if artist-trim-line-endings
-             (artist-draw-region-trim-line-endings artist-draw-region-min-y
-                                                   artist-draw-region-max-y))
-         (setq artist-draw-region-min-y orig-draw-region-min-y)
-         (setq artist-draw-region-max-y orig-draw-region-max-y))
+           (if artist-trim-line-endings
+               (artist-draw-region-trim-line-endings artist-draw-region-min-y
+                                                     artist-draw-region-max-y))
+           (setq artist-draw-region-min-y orig-draw-region-min-y)
+           (setq artist-draw-region-max-y orig-draw-region-max-y))
 
-      ; This is protected
-      (if (eq window-system 'x)
-         (artist-set-pointer-shape orig-pointer-shape))
+                                        ; This is protected
+        (if (eq window-system 'x)
+           (artist-set-pointer-shape orig-pointer-shape))
 
-      (if orig-button-up-binding
-         (define-key (current-global-map) key orig-button-up-binding))
+        (if orig-button-up-binding
+           (define-key (current-global-map) key orig-button-up-binding))
 
-      (artist-mode-line-show-curr-operation artist-key-is-drawing))))
+        (artist-mode-line-show-curr-operation artist-key-is-drawing)))))
 

>> >> And what if the user has two ordinary mice connected, and wants one to
>> >> behave differently from the other, in effect giving him an extra set of
>> >> mouse buttons?
>> >
>> > What about it?  Why does this require to know about the device, and
>> > cannot be expressed as special events?
>> 
>> How would you decide from which mice ordinary button events should come,
>> and what others without knowing their names?
>
> By appropriate numbering of their buttons, for example?

How would you number the buttons of a specific device without knowing
its name?  Besides, window systems have a limit on the largest button
that can be reported.

> Instead of doing this on the level of input methods, why not do this
> where we produce keyboard key events, by converting the keys to what
> they are supposed to mean from the user's POV?  Then none of the
> higher-level code will need to change, and moreover, users will be
> able to use these different keyboards with input methods unrelated to
> the keyboard type handling, whereas if this is done in an input
> method, one cannot have another input method active at the same time
> to use the characters that come from the "translation" input method.

Actually, I think it would be even better to have the existing input
method system work well with multiple devices.  IOW, we could add a new
variable `input-method-functions', an alist of (DEVICE
. ACTUAL-FUNCTION), where ACTUAL-FUNCTION is called on keyboard events
from DEVICE, and only if none of that is found do we default to
`input-method-function'.

But we will still need to expose a way to identify individual input
devices, which on GNU/Linux happens to be the name of the device.

> No, we just need a mapping from this event to an Emacs command that
> will do whatever is needed for the event.  And users can customize
> that command if they need.

But if I press the key labeled "я", and the system keyboard layout is on
US International, the event Emacs gets is "z", and vice versa.  The only
difference between that and pressing "z" on the US International
keyboard is that the device reported as the source of the event is
different, and those separate devices can be identified by their names.

Thanks.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]