Add support for event processing via XInput 2

* configure.ac: Add an option to use XInput 2 if available.
* src/Makefile.in (XINPUT_LIBS, XINPUT_CFLAGS): New variables.
(EMACS_CFLAGS): Add Xinput CFLAGS.
(LIBES): Add XInput libs.
* src/xmenu.c (popup_activated_flag): Expose flag if
XInput 2 is available.
* src/xfns.c (x_window): Set XInput 2 event mask.
(setup_xi_event_mask): New function.
(syms_of_xfns): Provide XInput 2 feature.
* src/xterm.c (x_detect_focus_change): Handle XInput 2
GenericEvents.
(handle_one_xevent): Handle XInput 2 events.
(x_term_init): Ask the server for XInput 2 support and set
xkb_desc if available.
(x_delete_terminal): Free XKB kb desc if it exists, and free
XI2 devices if they exist.
(xi_grab_or_ungrab_device)
(xi_reset_scroll_valuators_for_device_id)
(x_free_xi_devices, x_init_master_valuators): New functions.
(x_get_scroll_valuator_delta): New function.
(init_xterm): Don't tell GTK to only use Core Input when built
with XInput 2 support.
* src/xterm.h (struct x_display_info): Add fields for XKB
and XI2 support.
* src/gtkutil.c (xg_event_is_for_menubar): Handle
XIDeviceEvents.
(xg_is_menu_window): New function.
(xg_event_is_for_scrollbar): Handle XIDeviceEvents.
* etc/NEWS: Document changes.

* lisp/mwheel.el (mouse-wheel-down-alternate-event)
(mouse-wheel-up-alternate-event)
(mouse-wheel-left-alternate-event)
(mouse-wheel-right-alternate-event): New user options.

(mouse-wheel-text-scale)
(mwheel-scroll): Test for alternate events.
(mouse-wheel--setup-bindings): Set up bindings for alternate
buttons.
This commit is contained in:
Po Lu 2021-10-16 13:15:36 +08:00
parent fbf361f593
commit 487ec3cf2a
10 changed files with 1372 additions and 21 deletions

View file

@ -487,6 +487,7 @@ OPTION_DEFAULT_ON([modules],[don't compile with dynamic modules support])
OPTION_DEFAULT_ON([threads],[don't compile with elisp threading support])
OPTION_DEFAULT_OFF([native-compilation],[compile with Emacs Lisp native compiler support])
OPTION_DEFAULT_OFF([cygwin32-native-compilation],[use native compilation on 32-bit Cygwin])
OPTION_DEFAULT_OFF([xinput2],[use version 2.0 the X Input Extension for input])
AC_ARG_WITH([file-notification],[AS_HELP_STRING([--with-file-notification=LIB],
[use a file notification library (LIB one of: yes, inotify, kqueue, gfile, w32, no)])],
@ -4237,6 +4238,26 @@ fi
AC_SUBST(XFIXES_CFLAGS)
AC_SUBST(XFIXES_LIBS)
## Use XInput 2.0 if available
HAVE_XINPUT2=no
if test "${HAVE_X11}" = "yes" && test "${with_xinput2}" != "no"; then
EMACS_CHECK_MODULES([XINPUT], [xi])
if test $HAVE_XINPUT = yes; then
# Now check for XInput2.h
AC_CHECK_HEADER(X11/extensions/XInput2.h,
[AC_CHECK_LIB(Xi, XIGrabButton, HAVE_XINPUT2=yes)])
fi
if test $HAVE_XINPUT2 = yes; then
AC_DEFINE(HAVE_XINPUT2, 1, [Define to 1 if the X Input Extension version 2.0 is present.])
if test "$USE_GTK_TOOLKIT" = "GTK2"; then
AC_MSG_WARN([You are building Emacs with GTK+ 2 and the X Input Extension version 2.
This might lead to problems if your version of GTK+ is not built with support for XInput 2.])
fi
fi
fi
AC_SUBST(XINPUT_CFLAGS)
AC_SUBST(XINPUT_LIBS)
### Use Xdbe (-lXdbe) if available
HAVE_XDBE=no
if test "${HAVE_X11}" = "yes"; then
@ -6011,6 +6032,7 @@ AS_ECHO([" Does Emacs use -lXaw3d? ${HAVE_XAW3D
Does Emacs support legacy unexec dumping? ${with_unexec}
Which dumping strategy does Emacs use? ${with_dumping}
Does Emacs have native lisp compiler? ${HAVE_NATIVE_COMP}
Does Emacs use version 2 of the the X Input Extension? ${HAVE_XINPUT2}
"])
if test -n "${EMACSDATA}"; then

View file

@ -30,6 +30,14 @@ The file is typically installed using a file name akin to
If a constant file name is required, the file can be renamed to
"emacs.pdmp", and Emacs will find it during startup anyway.
** Emacs now supports use of XInput 2 for input events.
If your X server has support and you have the XInput 2 development headers
installed, you can configure Emacs with the option '--with-xinput2' to enable
this support.
The named feature `xinput2' can be used to test for the presence of
XInput 2 support from Lisp programs.
* Startup Changes in Emacs 29.1
@ -224,6 +232,15 @@ The user option 'comint-terminfo-terminal' and variable
'system-uses-terminfo' can now be set as connection-local variables to
change the terminal used on a remote host.
** Mwheel
---
*** New user options for alternate wheel events.
The options 'mouse-wheel-down-alternate-event', 'mouse-wheel-up-alternate-event',
'mouse-wheel-left-alternate-event', and 'mouse-wheel-right-alternate-event' have
been added to better support systems where two kinds of wheel events can be
received.
* Changes in Specialized Modes and Packages in Emacs 29.1

View file

@ -63,6 +63,13 @@
:type 'symbol
:set 'mouse-wheel-change-button)
(defcustom mouse-wheel-down-alternate-event
(when (featurep 'xinput2) 'wheel-up)
"Alternative wheel down event to consider."
:group 'mouse
:type 'symbol
:set 'mouse-wheel-change-button)
(defcustom mouse-wheel-up-event
(if (or (featurep 'w32-win) (featurep 'ns-win))
'wheel-down
@ -72,6 +79,13 @@
:type 'symbol
:set 'mouse-wheel-change-button)
(defcustom mouse-wheel-up-alternate-event
(when (featurep 'xinput2) 'wheel-down)
"Alternative wheel up event to consider."
:group 'mouse
:type 'symbol
:set 'mouse-wheel-change-button)
(defcustom mouse-wheel-click-event 'mouse-2
"Event that should be temporarily inhibited after mouse scrolling.
The mouse wheel is typically on the mouse-2 button, so it may easily
@ -226,12 +240,20 @@ Also see `mouse-wheel-tilt-scroll'."
'mouse-6)
"Event used for scrolling left.")
(defvar mouse-wheel-left-alternate-event
(when (featurep 'xinput2) 'wheel-left)
"Alternative wheel left event to consider.")
(defvar mouse-wheel-right-event
(if (or (featurep 'w32-win) (featurep 'ns-win))
'wheel-right
'mouse-7)
"Event used for scrolling right.")
(defvar mouse-wheel-right-alternate-event
(when (featurep 'xinput2) 'wheel-right)
"Alternative wheel right event to consider.")
(defun mouse-wheel--get-scroll-window (event)
"Return window for mouse wheel event EVENT.
If `mouse-wheel-follow-mouse' is non-nil, return the window that
@ -296,14 +318,16 @@ value of ARG, and the command uses it in subsequent scrolls."
(condition-case nil
(unwind-protect
(let ((button (mwheel-event-button event)))
(cond ((and (eq amt 'hscroll) (eq button mouse-wheel-down-event))
(cond ((and (eq amt 'hscroll) (memq button (list mouse-wheel-down-event
mouse-wheel-down-alternate-event)))
(when (and (natnump arg) (> arg 0))
(setq mouse-wheel-scroll-amount-horizontal arg))
(funcall (if mouse-wheel-flip-direction
mwheel-scroll-left-function
mwheel-scroll-right-function)
mouse-wheel-scroll-amount-horizontal))
((eq button mouse-wheel-down-event)
((memq button (list mouse-wheel-down-event
mouse-wheel-down-alternate-event))
(condition-case nil (funcall mwheel-scroll-down-function amt)
;; Make sure we do indeed scroll to the beginning of
;; the buffer.
@ -318,23 +342,27 @@ value of ARG, and the command uses it in subsequent scrolls."
;; for a reason that escapes me. This problem seems
;; to only affect scroll-down. --Stef
(set-window-start (selected-window) (point-min))))))
((and (eq amt 'hscroll) (eq button mouse-wheel-up-event))
((and (eq amt 'hscroll) (memq button (list mouse-wheel-up-event
mouse-wheel-up-alternate-event)))
(when (and (natnump arg) (> arg 0))
(setq mouse-wheel-scroll-amount-horizontal arg))
(funcall (if mouse-wheel-flip-direction
mwheel-scroll-right-function
mwheel-scroll-left-function)
mouse-wheel-scroll-amount-horizontal))
((eq button mouse-wheel-up-event)
((memq button (list mouse-wheel-up-event
mouse-wheel-up-alternate-event))
(condition-case nil (funcall mwheel-scroll-up-function amt)
;; Make sure we do indeed scroll to the end of the buffer.
(end-of-buffer (while t (funcall mwheel-scroll-up-function)))))
((eq button mouse-wheel-left-event) ; for tilt scroll
((memq button (list mouse-wheel-left-event
mouse-wheel-left-alternate-event)) ; for tilt scroll
(when mouse-wheel-tilt-scroll
(funcall (if mouse-wheel-flip-direction
mwheel-scroll-right-function
mwheel-scroll-left-function) amt)))
((eq button mouse-wheel-right-event) ; for tilt scroll
((memq button (list mouse-wheel-right-event
mouse-wheel-right-alternate-event)) ; for tilt scroll
(when mouse-wheel-tilt-scroll
(funcall (if mouse-wheel-flip-direction
mwheel-scroll-left-function
@ -378,9 +406,11 @@ value of ARG, and the command uses it in subsequent scrolls."
(button (mwheel-event-button event)))
(select-window scroll-window 'mark-for-redisplay)
(unwind-protect
(cond ((eq button mouse-wheel-down-event)
(cond ((memq button (list mouse-wheel-down-event
mouse-wheel-down-alternate-event))
(text-scale-increase 1))
((eq button mouse-wheel-up-event)
((eq button (list mouse-wheel-up-event
mouse-wheel-up-alternate-event))
(text-scale-decrease 1)))
(select-window selected-window))))
@ -432,15 +462,23 @@ an event used for scrolling, such as `mouse-wheel-down-event'."
(cond
;; Bindings for changing font size.
((and (consp binding) (eq (cdr binding) 'text-scale))
(dolist (event (list mouse-wheel-down-event mouse-wheel-up-event))
(mouse-wheel--add-binding `[,(list (caar binding) event)]
'mouse-wheel-text-scale)))
(dolist (event (list mouse-wheel-down-event mouse-wheel-up-event
mouse-wheel-down-alternate-event
mouse-wheel-up-alternate-event))
(when event
(mouse-wheel--add-binding `[,(list (caar binding) event)]
'mouse-wheel-text-scale))))
;; Bindings for scrolling.
(t
(dolist (event (list mouse-wheel-down-event mouse-wheel-up-event
mouse-wheel-left-event mouse-wheel-right-event))
(dolist (key (mouse-wheel--create-scroll-keys binding event))
(mouse-wheel--add-binding key 'mwheel-scroll)))))))
mouse-wheel-left-event mouse-wheel-right-event
mouse-wheel-down-alternate-event
mouse-wheel-up-alternate-event
mouse-wheel-left-alternate-event
mouse-wheel-right-alternate-event))
(when event
(dolist (key (mouse-wheel--create-scroll-keys binding event))
(mouse-wheel--add-binding key 'mwheel-scroll))))))))
(when mouse-wheel-mode
(mouse-wheel--setup-bindings))

View file

@ -258,6 +258,9 @@ XINERAMA_CFLAGS = @XINERAMA_CFLAGS@
XFIXES_LIBS = @XFIXES_LIBS@
XFIXES_CFLAGS = @XFIXES_CFLAGS@
XINPUT_LIBS = @XINPUT_LIBS@
XINPUT_CFLAGS = @XINPUT_CFLAGS@
XDBE_LIBS = @XDBE_LIBS@
XDBE_CFLAGS = @XDBE_CFLAGS@
@ -374,7 +377,7 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
$(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \
$(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(LIBGCCJIT_CFLAGS) $(DBUS_CFLAGS) \
$(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \
$(WEBKIT_CFLAGS) $(WEBP_CFLAGS) $(LCMS2_CFLAGS) \
$(XINPUT_CFLAGS) $(WEBP_CFLAGS) $(WEBKIT_CFLAGS) $(LCMS2_CFLAGS) \
$(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
$(HARFBUZZ_CFLAGS) $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
$(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) \
@ -524,7 +527,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \
$(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(HARFBUZZ_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
$(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \
$(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \
$(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS)
$(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) $(XINPUT_LIBS)
## FORCE it so that admin/unidata can decide whether this file is
## up-to-date. Although since charprop depends on bootstrap-emacs,

View file

@ -47,6 +47,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <gdk/gdkkeysyms.h>
#ifdef HAVE_XINPUT2
#include <X11/extensions/XInput2.h>
#endif
#ifdef HAVE_XFT
#include <X11/Xft/Xft.h>
#endif
@ -839,6 +843,23 @@ my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
}
#endif
#if defined HAVE_GTK3 && defined HAVE_XINPUT2
bool
xg_is_menu_window (Display *dpy, Window wdesc)
{
GtkWidget *gwdesc = xg_win_to_widget (dpy, wdesc);
if (GTK_IS_WINDOW (gwdesc))
{
GtkWidget *fw = gtk_bin_get_child (GTK_BIN (gwdesc));
if (GTK_IS_MENU (fw))
return true;
}
return false;
}
#endif
/* Make a geometry string and pass that to GTK. It seems this is the
only way to get geometry position right if the user explicitly
asked for a position when starting Emacs.
@ -3589,6 +3610,18 @@ xg_event_is_for_menubar (struct frame *f, const XEvent *event)
if (! x->menubar_widget) return 0;
#ifdef HAVE_XINPUT2
XIDeviceEvent *xev = (XIDeviceEvent *) event->xcookie.data;
if (event->type == GenericEvent) /* XI_ButtonPress or XI_ButtonRelease */
{
if (! (xev->event_x >= 0
&& xev->event_x < FRAME_PIXEL_WIDTH (f)
&& xev->event_y >= 0
&& xev->event_y < FRAME_MENUBAR_HEIGHT (f)))
return 0;
}
else
#endif
if (! (event->xbutton.x >= 0
&& event->xbutton.x < FRAME_PIXEL_WIDTH (f)
&& event->xbutton.y >= 0
@ -3597,7 +3630,12 @@ xg_event_is_for_menubar (struct frame *f, const XEvent *event)
return 0;
gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
#ifdef HAVE_XINPUT2
if (event->type == GenericEvent)
gw = gdk_x11_window_lookup_for_display (gdpy, xev->event);
else
#endif
gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
if (! gw) return 0;
gevent.any.window = gw;
gevent.any.type = GDK_NOTHING;
@ -4244,7 +4282,20 @@ xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
{
bool retval = 0;
#ifdef HAVE_XINPUT2
XIDeviceEvent *xev = (XIDeviceEvent *) event->xcookie.data;
if (f && ((FRAME_DISPLAY_INFO (f)->supports_xi2
&& event->type == GenericEvent
&& (event->xgeneric.extension
== FRAME_DISPLAY_INFO (f)->xi2_opcode)
&& ((event->xgeneric.evtype == XI_ButtonPress
&& xev->detail < 4)
|| (event->xgeneric.evtype == XI_Motion)))
|| (event->type == ButtonPress
&& event->xbutton.button < 4)))
#else
if (f && event->type == ButtonPress && event->xbutton.button < 4)
#endif /* HAVE_XINPUT2 */
{
/* Check if press occurred outside the edit widget. */
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
@ -4262,10 +4313,29 @@ xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
#endif
retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
#ifdef HAVE_XINPUT2
GtkWidget *grab = gtk_grab_get_current ();
if (event->type == GenericEvent
&& event->xgeneric.evtype == XI_Motion)
retval = retval || (grab && GTK_IS_SCROLLBAR (grab));
#endif
}
#ifdef HAVE_XINPUT2
else if (f && ((FRAME_DISPLAY_INFO (f)->supports_xi2
&& event->type == GenericEvent
&& (event->xgeneric.extension
== FRAME_DISPLAY_INFO (f)->xi2_opcode)
&& ((event->xgeneric.evtype == XI_ButtonRelease
&& xev->detail < 4)
|| (event->xgeneric.evtype == XI_Motion)))
|| ((event->type == ButtonRelease
&& event->xbutton.button < 4)
|| event->type == MotionNotify)))
#else
else if (f
&& ((event->type == ButtonRelease && event->xbutton.button < 4)
|| event->type == MotionNotify))
#endif /* HAVE_XINPUT2 */
{
/* If we are releasing or moving the scroll bar, it has the grab. */
GtkWidget *w = gtk_grab_get_current ();

View file

@ -192,6 +192,10 @@ extern Lisp_Object xg_get_page_setup (void);
extern void xg_print_frames_dialog (Lisp_Object);
#endif
#if defined HAVE_GTK3 && defined HAVE_XINPUT2
extern bool xg_is_menu_window (Display *dpy, Window);
#endif
/* Mark all callback data that are Lisp_object:s during GC. */
extern void xg_mark_data (void);

View file

@ -57,6 +57,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <X11/extensions/Xdbe.h>
#endif
#ifdef HAVE_XINPUT2
#include <X11/extensions/XInput2.h>
#endif
#ifdef USE_X_TOOLKIT
#include <X11/Shell.h>
@ -2912,6 +2916,37 @@ initial_set_up_x_back_buffer (struct frame *f)
unblock_input ();
}
#if defined HAVE_XINPUT2 && !defined USE_GTK
static void
setup_xi_event_mask (struct frame *f)
{
XIEventMask mask;
ptrdiff_t l = XIMaskLen (XI_LASTEVENT);
unsigned char *m;
mask.mask = m = alloca (l);
memset (m, 0, l);
mask.mask_len = l;
mask.deviceid = XIAllMasterDevices;
XISetMask (m, XI_ButtonPress);
XISetMask (m, XI_ButtonRelease);
XISetMask (m, XI_KeyPress);
XISetMask (m, XI_KeyRelease);
XISetMask (m, XI_Motion);
XISetMask (m, XI_Enter);
XISetMask (m, XI_Leave);
XISetMask (m, XI_FocusIn);
XISetMask (m, XI_FocusOut);
XISetMask (m, XI_PropertyEvent);
XISetMask (m, XI_HierarchyChanged);
XISetMask (m, XI_DeviceChanged);
XISelectEvents (FRAME_X_DISPLAY (f),
FRAME_X_WINDOW (f),
&mask, 1);
}
#endif
#ifdef USE_X_TOOLKIT
/* Create and set up the X widget for frame F. */
@ -3074,6 +3109,11 @@ x_window (struct frame *f, long window_prompting)
class_hints.res_class = SSDATA (Vx_resource_class);
XSetClassHint (FRAME_X_DISPLAY (f), XtWindow (shell_widget), &class_hints);
#ifdef HAVE_XINPUT2
if (FRAME_DISPLAY_INFO (f)->supports_xi2)
setup_xi_event_mask (f);
#endif
#ifdef HAVE_X_I18N
FRAME_XIC (f) = NULL;
if (use_xim)
@ -3254,6 +3294,11 @@ x_window (struct frame *f)
}
#endif /* HAVE_X_I18N */
#ifdef HAVE_XINPUT2
if (FRAME_DISPLAY_INFO (f)->supports_xi2)
setup_xi_event_mask (f);
#endif
validate_x_resource_name ();
class_hints.res_name = SSDATA (Vx_resource_name);
@ -8038,6 +8083,11 @@ eliminated in future versions of Emacs. */);
/* Tell Emacs about this window system. */
Fprovide (Qx, Qnil);
#ifdef HAVE_XINPUT2
DEFSYM (Qxinput2, "xinput2");
Fprovide (Qxinput2, Qnil);
#endif
#ifdef USE_X_TOOLKIT
Fprovide (intern_c_string ("x-toolkit"), Qnil);
#ifdef USE_MOTIF

View file

@ -105,7 +105,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
/* Flag which when set indicates a dialog or menu has been posted by
Xt on behalf of one of the widget sets. */
#ifndef HAVE_XINPUT2
static int popup_activated_flag;
#else
int popup_activated_flag;
#endif
#ifdef USE_X_TOOLKIT

File diff suppressed because it is too large Load diff

View file

@ -88,6 +88,10 @@ typedef GtkWidget *xt_or_gtk_widget;
#include <X11/Xlib-xcb.h>
#endif
#ifdef HAVE_XKB
#include <X11/XKBlib.h>
#endif
#include "dispextern.h"
#include "termhooks.h"
@ -163,6 +167,28 @@ struct color_name_cache_entry
char *name;
};
#ifdef HAVE_XINPUT2
struct xi_scroll_valuator_t
{
bool invalid_p;
double current_value;
double emacs_value;
double increment;
int number;
int horizontal;
};
struct xi_device_t
{
int device_id;
int scroll_valuator_count;
int grab;
struct xi_scroll_valuator_t *valuators;
};
#endif
Status x_parse_color (struct frame *f, const char *color_name,
XColor *color);
@ -474,6 +500,19 @@ struct x_display_info
#ifdef HAVE_XDBE
bool supports_xdbe;
#endif
#ifdef HAVE_XINPUT2
bool supports_xi2;
int xi2_version;
int xi2_opcode;
int num_devices;
struct xi_device_t *devices;
#endif
#ifdef HAVE_XKB
XkbDescPtr xkb_desc;
#endif
};
#ifdef HAVE_X_I18N
@ -481,6 +520,11 @@ struct x_display_info
extern bool use_xim;
#endif
#ifdef HAVE_XINPUT2
/* Defined in xmenu.c. */
extern int popup_activated_flag;
#endif
/* This is a chain of structures for all the X displays currently in use. */
extern struct x_display_info *x_display_list;