diff options
| author | Po Lu | 2021-10-16 13:15:36 +0800 |
|---|---|---|
| committer | Po Lu | 2021-11-20 18:25:09 +0800 |
| commit | 487ec3cf2a34496866153474507ab741d8dfea63 (patch) | |
| tree | f8a04b4204418d06b1308d9186f32728b770bb13 /src | |
| parent | fbf361f593df52ff414a4483f105e2e4c1a921e2 (diff) | |
| download | emacs-487ec3cf2a34496866153474507ab741d8dfea63.tar.gz emacs-487ec3cf2a34496866153474507ab741d8dfea63.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.in | 7 | ||||
| -rw-r--r-- | src/gtkutil.c | 72 | ||||
| -rw-r--r-- | src/gtkutil.h | 4 | ||||
| -rw-r--r-- | src/xfns.c | 50 | ||||
| -rw-r--r-- | src/xmenu.c | 4 | ||||
| -rw-r--r-- | src/xterm.c | 1107 | ||||
| -rw-r--r-- | src/xterm.h | 44 |
7 files changed, 1281 insertions, 7 deletions
diff --git a/src/Makefile.in b/src/Makefile.in index 4c5535f8ad9..0aaaf91d392 100644 --- a/src/Makefile.in +++ b/src/Makefile.in | |||
| @@ -258,6 +258,9 @@ XINERAMA_CFLAGS = @XINERAMA_CFLAGS@ | |||
| 258 | XFIXES_LIBS = @XFIXES_LIBS@ | 258 | XFIXES_LIBS = @XFIXES_LIBS@ |
| 259 | XFIXES_CFLAGS = @XFIXES_CFLAGS@ | 259 | XFIXES_CFLAGS = @XFIXES_CFLAGS@ |
| 260 | 260 | ||
| 261 | XINPUT_LIBS = @XINPUT_LIBS@ | ||
| 262 | XINPUT_CFLAGS = @XINPUT_CFLAGS@ | ||
| 263 | |||
| 261 | XDBE_LIBS = @XDBE_LIBS@ | 264 | XDBE_LIBS = @XDBE_LIBS@ |
| 262 | XDBE_CFLAGS = @XDBE_CFLAGS@ | 265 | XDBE_CFLAGS = @XDBE_CFLAGS@ |
| 263 | 266 | ||
| @@ -374,7 +377,7 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \ | |||
| 374 | $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \ | 377 | $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \ |
| 375 | $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(LIBGCCJIT_CFLAGS) $(DBUS_CFLAGS) \ | 378 | $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(LIBGCCJIT_CFLAGS) $(DBUS_CFLAGS) \ |
| 376 | $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \ | 379 | $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \ |
| 377 | $(WEBKIT_CFLAGS) $(WEBP_CFLAGS) $(LCMS2_CFLAGS) \ | 380 | $(XINPUT_CFLAGS) $(WEBP_CFLAGS) $(WEBKIT_CFLAGS) $(LCMS2_CFLAGS) \ |
| 378 | $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ | 381 | $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ |
| 379 | $(HARFBUZZ_CFLAGS) $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ | 382 | $(HARFBUZZ_CFLAGS) $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ |
| 380 | $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) \ | 383 | $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) \ |
| @@ -524,7 +527,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \ | |||
| 524 | $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(HARFBUZZ_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ | 527 | $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(HARFBUZZ_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ |
| 525 | $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \ | 528 | $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \ |
| 526 | $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \ | 529 | $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \ |
| 527 | $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) | 530 | $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) $(XINPUT_LIBS) |
| 528 | 531 | ||
| 529 | ## FORCE it so that admin/unidata can decide whether this file is | 532 | ## FORCE it so that admin/unidata can decide whether this file is |
| 530 | ## up-to-date. Although since charprop depends on bootstrap-emacs, | 533 | ## up-to-date. Although since charprop depends on bootstrap-emacs, |
diff --git a/src/gtkutil.c b/src/gtkutil.c index a9eabf47d8f..9e676cd025b 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c | |||
| @@ -47,6 +47,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 47 | 47 | ||
| 48 | #include <gdk/gdkkeysyms.h> | 48 | #include <gdk/gdkkeysyms.h> |
| 49 | 49 | ||
| 50 | #ifdef HAVE_XINPUT2 | ||
| 51 | #include <X11/extensions/XInput2.h> | ||
| 52 | #endif | ||
| 53 | |||
| 50 | #ifdef HAVE_XFT | 54 | #ifdef HAVE_XFT |
| 51 | #include <X11/Xft/Xft.h> | 55 | #include <X11/Xft/Xft.h> |
| 52 | #endif | 56 | #endif |
| @@ -839,6 +843,23 @@ my_log_handler (const gchar *log_domain, GLogLevelFlags log_level, | |||
| 839 | } | 843 | } |
| 840 | #endif | 844 | #endif |
| 841 | 845 | ||
| 846 | #if defined HAVE_GTK3 && defined HAVE_XINPUT2 | ||
| 847 | bool | ||
| 848 | xg_is_menu_window (Display *dpy, Window wdesc) | ||
| 849 | { | ||
| 850 | GtkWidget *gwdesc = xg_win_to_widget (dpy, wdesc); | ||
| 851 | |||
| 852 | if (GTK_IS_WINDOW (gwdesc)) | ||
| 853 | { | ||
| 854 | GtkWidget *fw = gtk_bin_get_child (GTK_BIN (gwdesc)); | ||
| 855 | if (GTK_IS_MENU (fw)) | ||
| 856 | return true; | ||
| 857 | } | ||
| 858 | |||
| 859 | return false; | ||
| 860 | } | ||
| 861 | #endif | ||
| 862 | |||
| 842 | /* Make a geometry string and pass that to GTK. It seems this is the | 863 | /* Make a geometry string and pass that to GTK. It seems this is the |
| 843 | only way to get geometry position right if the user explicitly | 864 | only way to get geometry position right if the user explicitly |
| 844 | asked for a position when starting Emacs. | 865 | asked for a position when starting Emacs. |
| @@ -3589,6 +3610,18 @@ xg_event_is_for_menubar (struct frame *f, const XEvent *event) | |||
| 3589 | 3610 | ||
| 3590 | if (! x->menubar_widget) return 0; | 3611 | if (! x->menubar_widget) return 0; |
| 3591 | 3612 | ||
| 3613 | #ifdef HAVE_XINPUT2 | ||
| 3614 | XIDeviceEvent *xev = (XIDeviceEvent *) event->xcookie.data; | ||
| 3615 | if (event->type == GenericEvent) /* XI_ButtonPress or XI_ButtonRelease */ | ||
| 3616 | { | ||
| 3617 | if (! (xev->event_x >= 0 | ||
| 3618 | && xev->event_x < FRAME_PIXEL_WIDTH (f) | ||
| 3619 | && xev->event_y >= 0 | ||
| 3620 | && xev->event_y < FRAME_MENUBAR_HEIGHT (f))) | ||
| 3621 | return 0; | ||
| 3622 | } | ||
| 3623 | else | ||
| 3624 | #endif | ||
| 3592 | if (! (event->xbutton.x >= 0 | 3625 | if (! (event->xbutton.x >= 0 |
| 3593 | && event->xbutton.x < FRAME_PIXEL_WIDTH (f) | 3626 | && event->xbutton.x < FRAME_PIXEL_WIDTH (f) |
| 3594 | && event->xbutton.y >= 0 | 3627 | && event->xbutton.y >= 0 |
| @@ -3597,7 +3630,12 @@ xg_event_is_for_menubar (struct frame *f, const XEvent *event) | |||
| 3597 | return 0; | 3630 | return 0; |
| 3598 | 3631 | ||
| 3599 | gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); | 3632 | gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); |
| 3600 | gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window); | 3633 | #ifdef HAVE_XINPUT2 |
| 3634 | if (event->type == GenericEvent) | ||
| 3635 | gw = gdk_x11_window_lookup_for_display (gdpy, xev->event); | ||
| 3636 | else | ||
| 3637 | #endif | ||
| 3638 | gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window); | ||
| 3601 | if (! gw) return 0; | 3639 | if (! gw) return 0; |
| 3602 | gevent.any.window = gw; | 3640 | gevent.any.window = gw; |
| 3603 | gevent.any.type = GDK_NOTHING; | 3641 | gevent.any.type = GDK_NOTHING; |
| @@ -4244,7 +4282,20 @@ xg_event_is_for_scrollbar (struct frame *f, const XEvent *event) | |||
| 4244 | { | 4282 | { |
| 4245 | bool retval = 0; | 4283 | bool retval = 0; |
| 4246 | 4284 | ||
| 4285 | #ifdef HAVE_XINPUT2 | ||
| 4286 | XIDeviceEvent *xev = (XIDeviceEvent *) event->xcookie.data; | ||
| 4287 | if (f && ((FRAME_DISPLAY_INFO (f)->supports_xi2 | ||
| 4288 | && event->type == GenericEvent | ||
| 4289 | && (event->xgeneric.extension | ||
| 4290 | == FRAME_DISPLAY_INFO (f)->xi2_opcode) | ||
| 4291 | && ((event->xgeneric.evtype == XI_ButtonPress | ||
| 4292 | && xev->detail < 4) | ||
| 4293 | || (event->xgeneric.evtype == XI_Motion))) | ||
| 4294 | || (event->type == ButtonPress | ||
| 4295 | && event->xbutton.button < 4))) | ||
| 4296 | #else | ||
| 4247 | if (f && event->type == ButtonPress && event->xbutton.button < 4) | 4297 | if (f && event->type == ButtonPress && event->xbutton.button < 4) |
| 4298 | #endif /* HAVE_XINPUT2 */ | ||
| 4248 | { | 4299 | { |
| 4249 | /* Check if press occurred outside the edit widget. */ | 4300 | /* Check if press occurred outside the edit widget. */ |
| 4250 | GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); | 4301 | 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) | |||
| 4262 | gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL); | 4313 | gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL); |
| 4263 | #endif | 4314 | #endif |
| 4264 | retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget); | 4315 | retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget); |
| 4316 | #ifdef HAVE_XINPUT2 | ||
| 4317 | GtkWidget *grab = gtk_grab_get_current (); | ||
| 4318 | if (event->type == GenericEvent | ||
| 4319 | && event->xgeneric.evtype == XI_Motion) | ||
| 4320 | retval = retval || (grab && GTK_IS_SCROLLBAR (grab)); | ||
| 4321 | #endif | ||
| 4265 | } | 4322 | } |
| 4323 | #ifdef HAVE_XINPUT2 | ||
| 4324 | else if (f && ((FRAME_DISPLAY_INFO (f)->supports_xi2 | ||
| 4325 | && event->type == GenericEvent | ||
| 4326 | && (event->xgeneric.extension | ||
| 4327 | == FRAME_DISPLAY_INFO (f)->xi2_opcode) | ||
| 4328 | && ((event->xgeneric.evtype == XI_ButtonRelease | ||
| 4329 | && xev->detail < 4) | ||
| 4330 | || (event->xgeneric.evtype == XI_Motion))) | ||
| 4331 | || ((event->type == ButtonRelease | ||
| 4332 | && event->xbutton.button < 4) | ||
| 4333 | || event->type == MotionNotify))) | ||
| 4334 | #else | ||
| 4266 | else if (f | 4335 | else if (f |
| 4267 | && ((event->type == ButtonRelease && event->xbutton.button < 4) | 4336 | && ((event->type == ButtonRelease && event->xbutton.button < 4) |
| 4268 | || event->type == MotionNotify)) | 4337 | || event->type == MotionNotify)) |
| 4338 | #endif /* HAVE_XINPUT2 */ | ||
| 4269 | { | 4339 | { |
| 4270 | /* If we are releasing or moving the scroll bar, it has the grab. */ | 4340 | /* If we are releasing or moving the scroll bar, it has the grab. */ |
| 4271 | GtkWidget *w = gtk_grab_get_current (); | 4341 | GtkWidget *w = gtk_grab_get_current (); |
diff --git a/src/gtkutil.h b/src/gtkutil.h index 31a12cd5d3c..95dd75b7fad 100644 --- a/src/gtkutil.h +++ b/src/gtkutil.h | |||
| @@ -192,6 +192,10 @@ extern Lisp_Object xg_get_page_setup (void); | |||
| 192 | extern void xg_print_frames_dialog (Lisp_Object); | 192 | extern void xg_print_frames_dialog (Lisp_Object); |
| 193 | #endif | 193 | #endif |
| 194 | 194 | ||
| 195 | #if defined HAVE_GTK3 && defined HAVE_XINPUT2 | ||
| 196 | extern bool xg_is_menu_window (Display *dpy, Window); | ||
| 197 | #endif | ||
| 198 | |||
| 195 | /* Mark all callback data that are Lisp_object:s during GC. */ | 199 | /* Mark all callback data that are Lisp_object:s during GC. */ |
| 196 | extern void xg_mark_data (void); | 200 | extern void xg_mark_data (void); |
| 197 | 201 | ||
diff --git a/src/xfns.c b/src/xfns.c index 785ae3baca5..b33b40b330b 100644 --- a/src/xfns.c +++ b/src/xfns.c | |||
| @@ -57,6 +57,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 57 | #include <X11/extensions/Xdbe.h> | 57 | #include <X11/extensions/Xdbe.h> |
| 58 | #endif | 58 | #endif |
| 59 | 59 | ||
| 60 | #ifdef HAVE_XINPUT2 | ||
| 61 | #include <X11/extensions/XInput2.h> | ||
| 62 | #endif | ||
| 63 | |||
| 60 | #ifdef USE_X_TOOLKIT | 64 | #ifdef USE_X_TOOLKIT |
| 61 | #include <X11/Shell.h> | 65 | #include <X11/Shell.h> |
| 62 | 66 | ||
| @@ -2912,6 +2916,37 @@ initial_set_up_x_back_buffer (struct frame *f) | |||
| 2912 | unblock_input (); | 2916 | unblock_input (); |
| 2913 | } | 2917 | } |
| 2914 | 2918 | ||
| 2919 | #if defined HAVE_XINPUT2 && !defined USE_GTK | ||
| 2920 | static void | ||
| 2921 | setup_xi_event_mask (struct frame *f) | ||
| 2922 | { | ||
| 2923 | XIEventMask mask; | ||
| 2924 | ptrdiff_t l = XIMaskLen (XI_LASTEVENT); | ||
| 2925 | unsigned char *m; | ||
| 2926 | |||
| 2927 | mask.mask = m = alloca (l); | ||
| 2928 | memset (m, 0, l); | ||
| 2929 | mask.mask_len = l; | ||
| 2930 | mask.deviceid = XIAllMasterDevices; | ||
| 2931 | |||
| 2932 | XISetMask (m, XI_ButtonPress); | ||
| 2933 | XISetMask (m, XI_ButtonRelease); | ||
| 2934 | XISetMask (m, XI_KeyPress); | ||
| 2935 | XISetMask (m, XI_KeyRelease); | ||
| 2936 | XISetMask (m, XI_Motion); | ||
| 2937 | XISetMask (m, XI_Enter); | ||
| 2938 | XISetMask (m, XI_Leave); | ||
| 2939 | XISetMask (m, XI_FocusIn); | ||
| 2940 | XISetMask (m, XI_FocusOut); | ||
| 2941 | XISetMask (m, XI_PropertyEvent); | ||
| 2942 | XISetMask (m, XI_HierarchyChanged); | ||
| 2943 | XISetMask (m, XI_DeviceChanged); | ||
| 2944 | XISelectEvents (FRAME_X_DISPLAY (f), | ||
| 2945 | FRAME_X_WINDOW (f), | ||
| 2946 | &mask, 1); | ||
| 2947 | } | ||
| 2948 | #endif | ||
| 2949 | |||
| 2915 | #ifdef USE_X_TOOLKIT | 2950 | #ifdef USE_X_TOOLKIT |
| 2916 | 2951 | ||
| 2917 | /* Create and set up the X widget for frame F. */ | 2952 | /* Create and set up the X widget for frame F. */ |
| @@ -3074,6 +3109,11 @@ x_window (struct frame *f, long window_prompting) | |||
| 3074 | class_hints.res_class = SSDATA (Vx_resource_class); | 3109 | class_hints.res_class = SSDATA (Vx_resource_class); |
| 3075 | XSetClassHint (FRAME_X_DISPLAY (f), XtWindow (shell_widget), &class_hints); | 3110 | XSetClassHint (FRAME_X_DISPLAY (f), XtWindow (shell_widget), &class_hints); |
| 3076 | 3111 | ||
| 3112 | #ifdef HAVE_XINPUT2 | ||
| 3113 | if (FRAME_DISPLAY_INFO (f)->supports_xi2) | ||
| 3114 | setup_xi_event_mask (f); | ||
| 3115 | #endif | ||
| 3116 | |||
| 3077 | #ifdef HAVE_X_I18N | 3117 | #ifdef HAVE_X_I18N |
| 3078 | FRAME_XIC (f) = NULL; | 3118 | FRAME_XIC (f) = NULL; |
| 3079 | if (use_xim) | 3119 | if (use_xim) |
| @@ -3254,6 +3294,11 @@ x_window (struct frame *f) | |||
| 3254 | } | 3294 | } |
| 3255 | #endif /* HAVE_X_I18N */ | 3295 | #endif /* HAVE_X_I18N */ |
| 3256 | 3296 | ||
| 3297 | #ifdef HAVE_XINPUT2 | ||
| 3298 | if (FRAME_DISPLAY_INFO (f)->supports_xi2) | ||
| 3299 | setup_xi_event_mask (f); | ||
| 3300 | #endif | ||
| 3301 | |||
| 3257 | validate_x_resource_name (); | 3302 | validate_x_resource_name (); |
| 3258 | 3303 | ||
| 3259 | class_hints.res_name = SSDATA (Vx_resource_name); | 3304 | class_hints.res_name = SSDATA (Vx_resource_name); |
| @@ -8038,6 +8083,11 @@ eliminated in future versions of Emacs. */); | |||
| 8038 | /* Tell Emacs about this window system. */ | 8083 | /* Tell Emacs about this window system. */ |
| 8039 | Fprovide (Qx, Qnil); | 8084 | Fprovide (Qx, Qnil); |
| 8040 | 8085 | ||
| 8086 | #ifdef HAVE_XINPUT2 | ||
| 8087 | DEFSYM (Qxinput2, "xinput2"); | ||
| 8088 | Fprovide (Qxinput2, Qnil); | ||
| 8089 | #endif | ||
| 8090 | |||
| 8041 | #ifdef USE_X_TOOLKIT | 8091 | #ifdef USE_X_TOOLKIT |
| 8042 | Fprovide (intern_c_string ("x-toolkit"), Qnil); | 8092 | Fprovide (intern_c_string ("x-toolkit"), Qnil); |
| 8043 | #ifdef USE_MOTIF | 8093 | #ifdef USE_MOTIF |
diff --git a/src/xmenu.c b/src/xmenu.c index ea2cbab2030..07255911f97 100644 --- a/src/xmenu.c +++ b/src/xmenu.c | |||
| @@ -105,7 +105,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 105 | 105 | ||
| 106 | /* Flag which when set indicates a dialog or menu has been posted by | 106 | /* Flag which when set indicates a dialog or menu has been posted by |
| 107 | Xt on behalf of one of the widget sets. */ | 107 | Xt on behalf of one of the widget sets. */ |
| 108 | #ifndef HAVE_XINPUT2 | ||
| 108 | static int popup_activated_flag; | 109 | static int popup_activated_flag; |
| 110 | #else | ||
| 111 | int popup_activated_flag; | ||
| 112 | #endif | ||
| 109 | 113 | ||
| 110 | 114 | ||
| 111 | #ifdef USE_X_TOOLKIT | 115 | #ifdef USE_X_TOOLKIT |
diff --git a/src/xterm.c b/src/xterm.c index 816b6dc5a8b..63754a2cb61 100644 --- a/src/xterm.c +++ b/src/xterm.c | |||
| @@ -42,6 +42,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 42 | #include <X11/extensions/Xdbe.h> | 42 | #include <X11/extensions/Xdbe.h> |
| 43 | #endif | 43 | #endif |
| 44 | 44 | ||
| 45 | #ifdef HAVE_XINPUT2 | ||
| 46 | #include <X11/extensions/XInput2.h> | ||
| 47 | #endif | ||
| 48 | |||
| 45 | /* Load sys/types.h if not already loaded. | 49 | /* Load sys/types.h if not already loaded. |
| 46 | In some systems loading it twice is suicidal. */ | 50 | In some systems loading it twice is suicidal. */ |
| 47 | #ifndef makedev | 51 | #ifndef makedev |
| @@ -223,9 +227,15 @@ static bool x_handle_net_wm_state (struct frame *, const XPropertyEvent *); | |||
| 223 | static void x_check_fullscreen (struct frame *); | 227 | static void x_check_fullscreen (struct frame *); |
| 224 | static void x_check_expected_move (struct frame *, int, int); | 228 | static void x_check_expected_move (struct frame *, int, int); |
| 225 | static void x_sync_with_move (struct frame *, int, int, bool); | 229 | static void x_sync_with_move (struct frame *, int, int, bool); |
| 230 | #ifndef HAVE_XINPUT2 | ||
| 226 | static int handle_one_xevent (struct x_display_info *, | 231 | static int handle_one_xevent (struct x_display_info *, |
| 227 | const XEvent *, int *, | 232 | const XEvent *, int *, |
| 228 | struct input_event *); | 233 | struct input_event *); |
| 234 | #else | ||
| 235 | static int handle_one_xevent (struct x_display_info *, | ||
| 236 | XEvent *, int *, | ||
| 237 | struct input_event *); | ||
| 238 | #endif | ||
| 229 | #if ! (defined USE_X_TOOLKIT || defined USE_MOTIF) && defined USE_GTK | 239 | #if ! (defined USE_X_TOOLKIT || defined USE_MOTIF) && defined USE_GTK |
| 230 | static int x_dispatch_event (XEvent *, Display *); | 240 | static int x_dispatch_event (XEvent *, Display *); |
| 231 | #endif | 241 | #endif |
| @@ -335,6 +345,224 @@ x_extension_initialize (struct x_display_info *dpyinfo) | |||
| 335 | dpyinfo->ext_codes = ext_codes; | 345 | dpyinfo->ext_codes = ext_codes; |
| 336 | } | 346 | } |
| 337 | 347 | ||
| 348 | |||
| 349 | #ifdef HAVE_XINPUT2 | ||
| 350 | |||
| 351 | /* Free all XI2 devices on dpyinfo. */ | ||
| 352 | static void | ||
| 353 | x_free_xi_devices (struct x_display_info *dpyinfo) | ||
| 354 | { | ||
| 355 | block_input (); | ||
| 356 | |||
| 357 | if (dpyinfo->num_devices) | ||
| 358 | { | ||
| 359 | for (int i = 0; i < dpyinfo->num_devices; ++i) | ||
| 360 | { | ||
| 361 | XIUngrabDevice (dpyinfo->display, dpyinfo->devices[i].device_id, | ||
| 362 | CurrentTime); | ||
| 363 | xfree (dpyinfo->devices[i].valuators); | ||
| 364 | } | ||
| 365 | |||
| 366 | xfree (dpyinfo->devices); | ||
| 367 | dpyinfo->devices = NULL; | ||
| 368 | dpyinfo->num_devices = 0; | ||
| 369 | } | ||
| 370 | |||
| 371 | unblock_input (); | ||
| 372 | } | ||
| 373 | |||
| 374 | /* Setup valuator tracking for XI2 master devices on | ||
| 375 | DPYINFO->display. */ | ||
| 376 | |||
| 377 | static void | ||
| 378 | x_init_master_valuators (struct x_display_info *dpyinfo) | ||
| 379 | { | ||
| 380 | int ndevices; | ||
| 381 | XIDeviceInfo *infos; | ||
| 382 | |||
| 383 | block_input (); | ||
| 384 | x_free_xi_devices (dpyinfo); | ||
| 385 | infos = XIQueryDevice (dpyinfo->display, | ||
| 386 | XIAllMasterDevices, | ||
| 387 | &ndevices); | ||
| 388 | |||
| 389 | if (!ndevices) | ||
| 390 | { | ||
| 391 | XIFreeDeviceInfo (infos); | ||
| 392 | unblock_input (); | ||
| 393 | return; | ||
| 394 | } | ||
| 395 | |||
| 396 | int actual_devices = 0; | ||
| 397 | dpyinfo->devices = xmalloc (sizeof *dpyinfo->devices * ndevices); | ||
| 398 | |||
| 399 | for (int i = 0; i < ndevices; ++i) | ||
| 400 | { | ||
| 401 | XIDeviceInfo *device = &infos[i]; | ||
| 402 | |||
| 403 | if (device->enabled) | ||
| 404 | { | ||
| 405 | int actual_valuator_count = 0; | ||
| 406 | struct xi_device_t *xi_device = &dpyinfo->devices[actual_devices++]; | ||
| 407 | xi_device->device_id = device->deviceid; | ||
| 408 | xi_device->grab = 0; | ||
| 409 | xi_device->valuators = | ||
| 410 | xmalloc (sizeof *xi_device->valuators * device->num_classes); | ||
| 411 | |||
| 412 | for (int c = 0; c < device->num_classes; ++c) | ||
| 413 | { | ||
| 414 | switch (device->classes[c]->type) | ||
| 415 | { | ||
| 416 | #ifdef XIScrollClass /* XInput 2.1 */ | ||
| 417 | case XIScrollClass: | ||
| 418 | { | ||
| 419 | XIScrollClassInfo *info = | ||
| 420 | (XIScrollClassInfo *) device->classes[c]; | ||
| 421 | struct xi_scroll_valuator_t *valuator = | ||
| 422 | &xi_device->valuators[actual_valuator_count++]; | ||
| 423 | |||
| 424 | valuator->horizontal | ||
| 425 | = (info->scroll_type == XIScrollTypeHorizontal); | ||
| 426 | valuator->invalid_p = true; | ||
| 427 | valuator->emacs_value = DBL_MIN; | ||
| 428 | valuator->increment = info->increment; | ||
| 429 | valuator->number = info->number; | ||
| 430 | break; | ||
| 431 | } | ||
| 432 | #endif | ||
| 433 | default: | ||
| 434 | break; | ||
| 435 | } | ||
| 436 | } | ||
| 437 | xi_device->scroll_valuator_count = actual_valuator_count; | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | dpyinfo->num_devices = actual_devices; | ||
| 442 | XIFreeDeviceInfo (infos); | ||
| 443 | unblock_input (); | ||
| 444 | } | ||
| 445 | |||
| 446 | /* Return the delta of the scroll valuator VALUATOR_NUMBER under | ||
| 447 | DEVICE_ID in the display DPYINFO with VALUE. The valuator's | ||
| 448 | valuator will be set to VALUE afterwards. In case no scroll | ||
| 449 | valuator is found, or if device_id is not known to Emacs, DBL_MAX | ||
| 450 | is returned. Otherwise, the valuator is returned in | ||
| 451 | VALUATOR_RETURN. */ | ||
| 452 | static double | ||
| 453 | x_get_scroll_valuator_delta (struct x_display_info *dpyinfo, int device_id, | ||
| 454 | int valuator_number, double value, | ||
| 455 | struct xi_scroll_valuator_t **valuator_return) | ||
| 456 | { | ||
| 457 | block_input (); | ||
| 458 | |||
| 459 | for (int i = 0; i < dpyinfo->num_devices; ++i) | ||
| 460 | { | ||
| 461 | struct xi_device_t *device = &dpyinfo->devices[i]; | ||
| 462 | |||
| 463 | if (device->device_id == device_id) | ||
| 464 | { | ||
| 465 | for (int j = 0; j < device->scroll_valuator_count; ++j) | ||
| 466 | { | ||
| 467 | struct xi_scroll_valuator_t *sv = &device->valuators[j]; | ||
| 468 | |||
| 469 | if (sv->number == valuator_number) | ||
| 470 | { | ||
| 471 | if (sv->invalid_p) | ||
| 472 | { | ||
| 473 | sv->current_value = value; | ||
| 474 | sv->invalid_p = false; | ||
| 475 | *valuator_return = sv; | ||
| 476 | |||
| 477 | unblock_input (); | ||
| 478 | return 0.0; | ||
| 479 | } | ||
| 480 | else | ||
| 481 | { | ||
| 482 | double delta = (sv->current_value - value) / sv->increment; | ||
| 483 | sv->current_value = value; | ||
| 484 | *valuator_return = sv; | ||
| 485 | |||
| 486 | unblock_input (); | ||
| 487 | return delta; | ||
| 488 | } | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | unblock_input (); | ||
| 493 | return DBL_MAX; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | |||
| 497 | unblock_input (); | ||
| 498 | return DBL_MAX; | ||
| 499 | } | ||
| 500 | |||
| 501 | static struct xi_device_t * | ||
| 502 | xi_device_from_id (struct x_display_info *dpyinfo, int deviceid) | ||
| 503 | { | ||
| 504 | for (int i = 0; i < dpyinfo->num_devices; ++i) | ||
| 505 | { | ||
| 506 | if (dpyinfo->devices[i].device_id == deviceid) | ||
| 507 | return &dpyinfo->devices[i]; | ||
| 508 | } | ||
| 509 | |||
| 510 | return NULL; | ||
| 511 | } | ||
| 512 | |||
| 513 | static void | ||
| 514 | xi_grab_or_ungrab_device (struct xi_device_t *device, | ||
| 515 | struct x_display_info *dpyinfo, | ||
| 516 | Window window) | ||
| 517 | { | ||
| 518 | XIEventMask mask; | ||
| 519 | ptrdiff_t l = XIMaskLen (XI_LASTEVENT); | ||
| 520 | unsigned char *m; | ||
| 521 | mask.mask = m = alloca (l); | ||
| 522 | memset (m, 0, l); | ||
| 523 | mask.mask_len = l; | ||
| 524 | |||
| 525 | XISetMask (m, XI_ButtonPress); | ||
| 526 | XISetMask (m, XI_ButtonRelease); | ||
| 527 | XISetMask (m, XI_Motion); | ||
| 528 | XISetMask (m, XI_Enter); | ||
| 529 | XISetMask (m, XI_Leave); | ||
| 530 | |||
| 531 | if (device->grab) | ||
| 532 | { | ||
| 533 | XIGrabDevice (dpyinfo->display, device->device_id, window, | ||
| 534 | CurrentTime, None, GrabModeAsync, | ||
| 535 | GrabModeAsync, True, &mask); | ||
| 536 | } | ||
| 537 | else | ||
| 538 | { | ||
| 539 | XIUngrabDevice (dpyinfo->display, device->device_id, CurrentTime); | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | static void | ||
| 544 | xi_reset_scroll_valuators_for_device_id (struct x_display_info *dpyinfo, int id) | ||
| 545 | { | ||
| 546 | struct xi_device_t *device = xi_device_from_id (dpyinfo, id); | ||
| 547 | struct xi_scroll_valuator_t *valuator; | ||
| 548 | |||
| 549 | if (!device) | ||
| 550 | return; | ||
| 551 | |||
| 552 | if (!device->scroll_valuator_count) | ||
| 553 | return; | ||
| 554 | |||
| 555 | for (int i = 0; i < device->scroll_valuator_count; ++i) | ||
| 556 | { | ||
| 557 | valuator = &device->valuators[i]; | ||
| 558 | valuator->invalid_p = true; | ||
| 559 | } | ||
| 560 | |||
| 561 | return; | ||
| 562 | } | ||
| 563 | |||
| 564 | #endif | ||
| 565 | |||
| 338 | void | 566 | void |
| 339 | x_cr_destroy_frame_context (struct frame *f) | 567 | x_cr_destroy_frame_context (struct frame *f) |
| 340 | { | 568 | { |
| @@ -4768,7 +4996,16 @@ static struct frame * | |||
| 4768 | x_menubar_window_to_frame (struct x_display_info *dpyinfo, | 4996 | x_menubar_window_to_frame (struct x_display_info *dpyinfo, |
| 4769 | const XEvent *event) | 4997 | const XEvent *event) |
| 4770 | { | 4998 | { |
| 4771 | Window wdesc = event->xany.window; | 4999 | Window wdesc; |
| 5000 | #ifdef HAVE_XINPUT2 | ||
| 5001 | if (event->type == GenericEvent | ||
| 5002 | && dpyinfo->supports_xi2 | ||
| 5003 | && (event->xcookie.evtype == XI_ButtonPress | ||
| 5004 | || event->xcookie.evtype == XI_ButtonRelease)) | ||
| 5005 | wdesc = ((XIDeviceEvent *) event->xcookie.data)->event; | ||
| 5006 | else | ||
| 5007 | #endif | ||
| 5008 | wdesc = event->xany.window; | ||
| 4772 | Lisp_Object tail, frame; | 5009 | Lisp_Object tail, frame; |
| 4773 | struct frame *f; | 5010 | struct frame *f; |
| 4774 | struct x_output *x; | 5011 | struct x_output *x; |
| @@ -4871,6 +5108,29 @@ x_detect_focus_change (struct x_display_info *dpyinfo, struct frame *frame, | |||
| 4871 | } | 5108 | } |
| 4872 | break; | 5109 | break; |
| 4873 | 5110 | ||
| 5111 | #ifdef HAVE_XINPUT2 | ||
| 5112 | case GenericEvent: | ||
| 5113 | { | ||
| 5114 | XIEvent *xi_event = (XIEvent *) event; | ||
| 5115 | |||
| 5116 | struct frame *focus_frame = dpyinfo->x_focus_event_frame; | ||
| 5117 | int focus_state | ||
| 5118 | = focus_frame ? focus_frame->output_data.x->focus_state : 0; | ||
| 5119 | |||
| 5120 | if (!((xi_event->evtype == XI_Enter | ||
| 5121 | || xi_event->evtype == XI_Leave) | ||
| 5122 | && (focus_state & FOCUS_EXPLICIT))) | ||
| 5123 | x_focus_changed ((xi_event->evtype == XI_Enter | ||
| 5124 | || xi_event->evtype == XI_FocusIn | ||
| 5125 | ? FocusIn : FocusOut), | ||
| 5126 | (xi_event->evtype == XI_Enter | ||
| 5127 | || xi_event->evtype == XI_Leave | ||
| 5128 | ? FOCUS_IMPLICIT : FOCUS_EXPLICIT), | ||
| 5129 | dpyinfo, frame, bufp); | ||
| 5130 | break; | ||
| 5131 | } | ||
| 5132 | #endif | ||
| 5133 | |||
| 4874 | case FocusIn: | 5134 | case FocusIn: |
| 4875 | case FocusOut: | 5135 | case FocusOut: |
| 4876 | /* Ignore transient focus events from hotkeys, window manager | 5136 | /* Ignore transient focus events from hotkeys, window manager |
| @@ -7975,7 +8235,11 @@ mouse_or_wdesc_frame (struct x_display_info *dpyinfo, int wdesc) | |||
| 7975 | 8235 | ||
| 7976 | static int | 8236 | static int |
| 7977 | handle_one_xevent (struct x_display_info *dpyinfo, | 8237 | handle_one_xevent (struct x_display_info *dpyinfo, |
| 8238 | #ifndef HAVE_XINPUT2 | ||
| 7978 | const XEvent *event, | 8239 | const XEvent *event, |
| 8240 | #else | ||
| 8241 | XEvent *event, | ||
| 8242 | #endif | ||
| 7979 | int *finish, struct input_event *hold_quit) | 8243 | int *finish, struct input_event *hold_quit) |
| 7980 | { | 8244 | { |
| 7981 | union buffered_input_event inev; | 8245 | union buffered_input_event inev; |
| @@ -8001,7 +8265,14 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 8001 | inev.ie.kind = NO_EVENT; | 8265 | inev.ie.kind = NO_EVENT; |
| 8002 | inev.ie.arg = Qnil; | 8266 | inev.ie.arg = Qnil; |
| 8003 | 8267 | ||
| 8004 | any = x_any_window_to_frame (dpyinfo, event->xany.window); | 8268 | #ifdef HAVE_XINPUT2 |
| 8269 | if (event->type != GenericEvent) | ||
| 8270 | #endif | ||
| 8271 | any = x_any_window_to_frame (dpyinfo, event->xany.window); | ||
| 8272 | #ifdef HAVE_XINPUT2 | ||
| 8273 | else | ||
| 8274 | any = NULL; | ||
| 8275 | #endif | ||
| 8005 | 8276 | ||
| 8006 | if (any && any->wait_event_type == event->type) | 8277 | if (any && any->wait_event_type == event->type) |
| 8007 | any->wait_event_type = 0; /* Indicates we got it. */ | 8278 | any->wait_event_type = 0; /* Indicates we got it. */ |
| @@ -8480,6 +8751,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 8480 | goto OTHER; | 8751 | goto OTHER; |
| 8481 | 8752 | ||
| 8482 | case MapNotify: | 8753 | case MapNotify: |
| 8754 | #if defined HAVE_XINPUT2 && defined HAVE_GTK3 | ||
| 8755 | if (xg_is_menu_window (dpyinfo->display, event->xmap.window)) | ||
| 8756 | popup_activated_flag = 1; | ||
| 8757 | #endif | ||
| 8483 | /* We use x_top_window_to_frame because map events can | 8758 | /* We use x_top_window_to_frame because map events can |
| 8484 | come for sub-windows and they don't mean that the | 8759 | come for sub-windows and they don't mean that the |
| 8485 | frame is visible. */ | 8760 | frame is visible. */ |
| @@ -9518,6 +9793,785 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 9518 | case DestroyNotify: | 9793 | case DestroyNotify: |
| 9519 | xft_settings_event (dpyinfo, event); | 9794 | xft_settings_event (dpyinfo, event); |
| 9520 | break; | 9795 | break; |
| 9796 | #ifdef HAVE_XINPUT2 | ||
| 9797 | case GenericEvent: | ||
| 9798 | { | ||
| 9799 | if (!dpyinfo->supports_xi2) | ||
| 9800 | goto OTHER; | ||
| 9801 | if (event->xgeneric.extension != dpyinfo->xi2_opcode) | ||
| 9802 | /* Not an XI2 event. */ | ||
| 9803 | goto OTHER; | ||
| 9804 | bool must_free_data = false; | ||
| 9805 | XIEvent *xi_event = (XIEvent *) event->xcookie.data; | ||
| 9806 | /* Sometimes the event is already claimed by GTK, which | ||
| 9807 | will free its data in due course. */ | ||
| 9808 | if (!xi_event && XGetEventData (dpyinfo->display, &event->xcookie)) | ||
| 9809 | { | ||
| 9810 | must_free_data = true; | ||
| 9811 | xi_event = (XIEvent *) event->xcookie.data; | ||
| 9812 | } | ||
| 9813 | |||
| 9814 | XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; | ||
| 9815 | XILeaveEvent *leave = (XILeaveEvent *) xi_event; | ||
| 9816 | XIEnterEvent *enter = (XIEnterEvent *) xi_event; | ||
| 9817 | XIFocusInEvent *focusin = (XIFocusInEvent *) xi_event; | ||
| 9818 | XIFocusOutEvent *focusout = (XIFocusOutEvent *) xi_event; | ||
| 9819 | XIValuatorState *states; | ||
| 9820 | double *values; | ||
| 9821 | bool found_valuator = false; | ||
| 9822 | |||
| 9823 | /* A fake XMotionEvent for x_note_mouse_movement. */ | ||
| 9824 | XMotionEvent ev; | ||
| 9825 | /* A fake XButtonEvent for x_construct_mouse_click. */ | ||
| 9826 | XButtonEvent bv; | ||
| 9827 | |||
| 9828 | if (!xi_event) | ||
| 9829 | { | ||
| 9830 | eassert (!must_free_data); | ||
| 9831 | goto OTHER; | ||
| 9832 | } | ||
| 9833 | |||
| 9834 | switch (event->xcookie.evtype) | ||
| 9835 | { | ||
| 9836 | case XI_FocusIn: | ||
| 9837 | any = x_any_window_to_frame (dpyinfo, focusin->event); | ||
| 9838 | #ifndef USE_GTK | ||
| 9839 | /* Some WMs (e.g. Mutter in Gnome Shell), don't unmap | ||
| 9840 | minimized/iconified windows; thus, for those WMs we won't get | ||
| 9841 | a MapNotify when unminimizing/deconifying. Check here if we | ||
| 9842 | are deiconizing a window (Bug42655). | ||
| 9843 | |||
| 9844 | But don't do that on GTK since it may cause a plain invisible | ||
| 9845 | frame get reported as iconified, compare | ||
| 9846 | https://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00133.html. | ||
| 9847 | That is fixed above but bites us here again. */ | ||
| 9848 | f = any; | ||
| 9849 | if (f && FRAME_ICONIFIED_P (f)) | ||
| 9850 | { | ||
| 9851 | SET_FRAME_VISIBLE (f, 1); | ||
| 9852 | SET_FRAME_ICONIFIED (f, false); | ||
| 9853 | f->output_data.x->has_been_visible = true; | ||
| 9854 | inev.ie.kind = DEICONIFY_EVENT; | ||
| 9855 | XSETFRAME (inev.ie.frame_or_window, f); | ||
| 9856 | } | ||
| 9857 | #endif /* USE_GTK */ | ||
| 9858 | x_detect_focus_change (dpyinfo, any, event, &inev.ie); | ||
| 9859 | goto XI_OTHER; | ||
| 9860 | case XI_FocusOut: | ||
| 9861 | any = x_any_window_to_frame (dpyinfo, focusout->event); | ||
| 9862 | x_detect_focus_change (dpyinfo, any, event, &inev.ie); | ||
| 9863 | goto XI_OTHER; | ||
| 9864 | case XI_Enter: | ||
| 9865 | any = x_any_window_to_frame (dpyinfo, enter->event); | ||
| 9866 | ev.x = lrint (enter->event_x); | ||
| 9867 | ev.y = lrint (enter->event_y); | ||
| 9868 | ev.window = leave->event; | ||
| 9869 | |||
| 9870 | x_display_set_last_user_time (dpyinfo, xi_event->time); | ||
| 9871 | x_detect_focus_change (dpyinfo, any, event, &inev.ie); | ||
| 9872 | xi_reset_scroll_valuators_for_device_id (dpyinfo, enter->deviceid); | ||
| 9873 | f = any; | ||
| 9874 | |||
| 9875 | if (f && x_mouse_click_focus_ignore_position) | ||
| 9876 | ignore_next_mouse_click_timeout = xi_event->time + 200; | ||
| 9877 | |||
| 9878 | /* EnterNotify counts as mouse movement, | ||
| 9879 | so update things that depend on mouse position. */ | ||
| 9880 | if (f && !f->output_data.x->hourglass_p) | ||
| 9881 | x_note_mouse_movement (f, &ev); | ||
| 9882 | #ifdef USE_GTK | ||
| 9883 | /* We may get an EnterNotify on the buttons in the toolbar. In that | ||
| 9884 | case we moved out of any highlighted area and need to note this. */ | ||
| 9885 | if (!f && dpyinfo->last_mouse_glyph_frame) | ||
| 9886 | x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &ev); | ||
| 9887 | #endif | ||
| 9888 | goto XI_OTHER; | ||
| 9889 | case XI_Leave: | ||
| 9890 | ev.x = lrint (leave->event_x); | ||
| 9891 | ev.y = lrint (leave->event_y); | ||
| 9892 | ev.window = leave->event; | ||
| 9893 | any = x_any_window_to_frame (dpyinfo, leave->event); | ||
| 9894 | |||
| 9895 | x_display_set_last_user_time (dpyinfo, xi_event->time); | ||
| 9896 | x_detect_focus_change (dpyinfo, any, event, &inev.ie); | ||
| 9897 | xi_reset_scroll_valuators_for_device_id (dpyinfo, leave->deviceid); | ||
| 9898 | |||
| 9899 | f = x_top_window_to_frame (dpyinfo, leave->event); | ||
| 9900 | if (f) | ||
| 9901 | { | ||
| 9902 | if (f == hlinfo->mouse_face_mouse_frame) | ||
| 9903 | { | ||
| 9904 | /* If we move outside the frame, then we're | ||
| 9905 | certainly no longer on any text in the frame. */ | ||
| 9906 | clear_mouse_face (hlinfo); | ||
| 9907 | hlinfo->mouse_face_mouse_frame = 0; | ||
| 9908 | } | ||
| 9909 | |||
| 9910 | /* Generate a nil HELP_EVENT to cancel a help-echo. | ||
| 9911 | Do it only if there's something to cancel. | ||
| 9912 | Otherwise, the startup message is cleared when | ||
| 9913 | the mouse leaves the frame. */ | ||
| 9914 | if (any_help_event_p) | ||
| 9915 | do_help = -1; | ||
| 9916 | } | ||
| 9917 | #ifdef USE_GTK | ||
| 9918 | /* See comment in EnterNotify above */ | ||
| 9919 | else if (dpyinfo->last_mouse_glyph_frame) | ||
| 9920 | x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &ev); | ||
| 9921 | #endif | ||
| 9922 | goto XI_OTHER; | ||
| 9923 | case XI_Motion: | ||
| 9924 | /* First test if there is some kind of scroll event | ||
| 9925 | here! */ | ||
| 9926 | states = &xev->valuators; | ||
| 9927 | values = states->values; | ||
| 9928 | |||
| 9929 | x_display_set_last_user_time (dpyinfo, xi_event->time); | ||
| 9930 | |||
| 9931 | for (int i = 0; i < states->mask_len * 8; i++) | ||
| 9932 | { | ||
| 9933 | if (XIMaskIsSet (states->mask, i)) | ||
| 9934 | { | ||
| 9935 | block_input (); | ||
| 9936 | |||
| 9937 | struct xi_scroll_valuator_t *val; | ||
| 9938 | double delta; | ||
| 9939 | |||
| 9940 | delta = x_get_scroll_valuator_delta (dpyinfo, xev->deviceid, | ||
| 9941 | i, *values, &val); | ||
| 9942 | |||
| 9943 | if (delta != DBL_MAX) | ||
| 9944 | { | ||
| 9945 | f = mouse_or_wdesc_frame (dpyinfo, xev->event); | ||
| 9946 | found_valuator = true; | ||
| 9947 | |||
| 9948 | if (signbit (delta) != signbit (val->emacs_value)) | ||
| 9949 | val->emacs_value = 0; | ||
| 9950 | |||
| 9951 | val->emacs_value += delta; | ||
| 9952 | |||
| 9953 | if (!f) | ||
| 9954 | { | ||
| 9955 | f = x_any_window_to_frame (dpyinfo, xev->event); | ||
| 9956 | |||
| 9957 | if (!f) | ||
| 9958 | { | ||
| 9959 | unblock_input (); | ||
| 9960 | goto XI_OTHER; | ||
| 9961 | } | ||
| 9962 | } | ||
| 9963 | |||
| 9964 | bool s = signbit (val->emacs_value); | ||
| 9965 | inev.ie.kind = (val->horizontal | ||
| 9966 | ? HORIZ_WHEEL_EVENT | ||
| 9967 | : WHEEL_EVENT); | ||
| 9968 | inev.ie.timestamp = xev->time; | ||
| 9969 | |||
| 9970 | XSETINT (inev.ie.x, lrint (xev->event_x)); | ||
| 9971 | XSETINT (inev.ie.y, lrint (xev->event_y)); | ||
| 9972 | XSETFRAME (inev.ie.frame_or_window, f); | ||
| 9973 | |||
| 9974 | inev.ie.modifiers = !s ? up_modifier : down_modifier; | ||
| 9975 | inev.ie.modifiers | ||
| 9976 | |= x_x_to_emacs_modifiers (dpyinfo, | ||
| 9977 | xev->mods.effective); | ||
| 9978 | inev.ie.arg = Qnil; | ||
| 9979 | |||
| 9980 | kbd_buffer_store_event_hold (&inev.ie, hold_quit); | ||
| 9981 | |||
| 9982 | val->emacs_value = 0; | ||
| 9983 | } | ||
| 9984 | unblock_input (); | ||
| 9985 | values++; | ||
| 9986 | } | ||
| 9987 | |||
| 9988 | inev.ie.kind = NO_EVENT; | ||
| 9989 | } | ||
| 9990 | |||
| 9991 | if (found_valuator) | ||
| 9992 | goto XI_OTHER; | ||
| 9993 | |||
| 9994 | ev.x = lrint (xev->event_x); | ||
| 9995 | ev.y = lrint (xev->event_y); | ||
| 9996 | ev.window = xev->event; | ||
| 9997 | |||
| 9998 | previous_help_echo_string = help_echo_string; | ||
| 9999 | help_echo_string = Qnil; | ||
| 10000 | |||
| 10001 | if (hlinfo->mouse_face_hidden) | ||
| 10002 | { | ||
| 10003 | hlinfo->mouse_face_hidden = false; | ||
| 10004 | clear_mouse_face (hlinfo); | ||
| 10005 | } | ||
| 10006 | |||
| 10007 | f = mouse_or_wdesc_frame (dpyinfo, xev->event); | ||
| 10008 | |||
| 10009 | #ifdef USE_GTK | ||
| 10010 | if (f && xg_event_is_for_scrollbar (f, event)) | ||
| 10011 | f = 0; | ||
| 10012 | #endif | ||
| 10013 | if (f) | ||
| 10014 | { | ||
| 10015 | /* Maybe generate a SELECT_WINDOW_EVENT for | ||
| 10016 | `mouse-autoselect-window' but don't let popup menus | ||
| 10017 | interfere with this (Bug#1261). */ | ||
| 10018 | if (!NILP (Vmouse_autoselect_window) | ||
| 10019 | && !popup_activated () | ||
| 10020 | /* Don't switch if we're currently in the minibuffer. | ||
| 10021 | This tries to work around problems where the | ||
| 10022 | minibuffer gets unselected unexpectedly, and where | ||
| 10023 | you then have to move your mouse all the way down to | ||
| 10024 | the minibuffer to select it. */ | ||
| 10025 | && !MINI_WINDOW_P (XWINDOW (selected_window)) | ||
| 10026 | /* With `focus-follows-mouse' non-nil create an event | ||
| 10027 | also when the target window is on another frame. */ | ||
| 10028 | && (f == XFRAME (selected_frame) | ||
| 10029 | || !NILP (focus_follows_mouse))) | ||
| 10030 | { | ||
| 10031 | static Lisp_Object last_mouse_window; | ||
| 10032 | Lisp_Object window = window_from_coordinates (f, ev.x, ev.y, 0, false, false); | ||
| 10033 | |||
| 10034 | /* A window will be autoselected only when it is not | ||
| 10035 | selected now and the last mouse movement event was | ||
| 10036 | not in it. The remainder of the code is a bit vague | ||
| 10037 | wrt what a "window" is. For immediate autoselection, | ||
| 10038 | the window is usually the entire window but for GTK | ||
| 10039 | where the scroll bars don't count. For delayed | ||
| 10040 | autoselection the window is usually the window's text | ||
| 10041 | area including the margins. */ | ||
| 10042 | if (WINDOWP (window) | ||
| 10043 | && !EQ (window, last_mouse_window) | ||
| 10044 | && !EQ (window, selected_window)) | ||
| 10045 | { | ||
| 10046 | inev.ie.kind = SELECT_WINDOW_EVENT; | ||
| 10047 | inev.ie.frame_or_window = window; | ||
| 10048 | } | ||
| 10049 | |||
| 10050 | /* Remember the last window where we saw the mouse. */ | ||
| 10051 | last_mouse_window = window; | ||
| 10052 | } | ||
| 10053 | |||
| 10054 | if (!x_note_mouse_movement (f, &ev)) | ||
| 10055 | help_echo_string = previous_help_echo_string; | ||
| 10056 | } | ||
| 10057 | else | ||
| 10058 | { | ||
| 10059 | #ifndef USE_TOOLKIT_SCROLL_BARS | ||
| 10060 | struct scroll_bar *bar | ||
| 10061 | = x_window_to_scroll_bar (xi_event->display, xev->event, 2); | ||
| 10062 | |||
| 10063 | if (bar) | ||
| 10064 | x_scroll_bar_note_movement (bar, &ev); | ||
| 10065 | #endif /* USE_TOOLKIT_SCROLL_BARS */ | ||
| 10066 | |||
| 10067 | /* If we move outside the frame, then we're | ||
| 10068 | certainly no longer on any text in the frame. */ | ||
| 10069 | clear_mouse_face (hlinfo); | ||
| 10070 | } | ||
| 10071 | |||
| 10072 | /* If the contents of the global variable help_echo_string | ||
| 10073 | has changed, generate a HELP_EVENT. */ | ||
| 10074 | if (!NILP (help_echo_string) | ||
| 10075 | || !NILP (previous_help_echo_string)) | ||
| 10076 | do_help = 1; | ||
| 10077 | goto XI_OTHER; | ||
| 10078 | case XI_ButtonRelease: | ||
| 10079 | case XI_ButtonPress: | ||
| 10080 | { | ||
| 10081 | /* If we decide we want to generate an event to be seen | ||
| 10082 | by the rest of Emacs, we put it here. */ | ||
| 10083 | Lisp_Object tab_bar_arg = Qnil; | ||
| 10084 | bool tab_bar_p = false; | ||
| 10085 | bool tool_bar_p = false; | ||
| 10086 | struct xi_device_t *device; | ||
| 10087 | |||
| 10088 | /* Ignore emulated scroll events when XI2 native | ||
| 10089 | scroll events are present. */ | ||
| 10090 | if (dpyinfo->xi2_version >= 1 && xev->detail >= 4 | ||
| 10091 | && xev->detail <= 8) | ||
| 10092 | goto XI_OTHER; | ||
| 10093 | |||
| 10094 | device = xi_device_from_id (dpyinfo, xev->deviceid); | ||
| 10095 | |||
| 10096 | bv.button = xev->detail; | ||
| 10097 | bv.type = xev->evtype == XI_ButtonPress ? ButtonPress : ButtonRelease; | ||
| 10098 | bv.x = lrint (xev->event_x); | ||
| 10099 | bv.y = lrint (xev->event_y); | ||
| 10100 | bv.window = xev->event; | ||
| 10101 | bv.state = xev->mods.base | ||
| 10102 | | xev->mods.effective | ||
| 10103 | | xev->mods.latched | ||
| 10104 | | xev->mods.locked; | ||
| 10105 | |||
| 10106 | memset (&compose_status, 0, sizeof (compose_status)); | ||
| 10107 | dpyinfo->last_mouse_glyph_frame = NULL; | ||
| 10108 | x_display_set_last_user_time (dpyinfo, xev->time); | ||
| 10109 | |||
| 10110 | f = mouse_or_wdesc_frame (dpyinfo, xev->event); | ||
| 10111 | |||
| 10112 | if (f && xev->evtype == XI_ButtonPress | ||
| 10113 | && !popup_activated () | ||
| 10114 | && !x_window_to_scroll_bar (xev->display, xev->event, 2) | ||
| 10115 | && !FRAME_NO_ACCEPT_FOCUS (f)) | ||
| 10116 | { | ||
| 10117 | /* When clicking into a child frame or when clicking | ||
| 10118 | into a parent frame with the child frame selected and | ||
| 10119 | `no-accept-focus' is not set, select the clicked | ||
| 10120 | frame. */ | ||
| 10121 | struct frame *hf = dpyinfo->highlight_frame; | ||
| 10122 | |||
| 10123 | if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf))) | ||
| 10124 | { | ||
| 10125 | block_input (); | ||
| 10126 | XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), | ||
| 10127 | RevertToParent, CurrentTime); | ||
| 10128 | if (FRAME_PARENT_FRAME (f)) | ||
| 10129 | XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f)); | ||
| 10130 | unblock_input (); | ||
| 10131 | } | ||
| 10132 | } | ||
| 10133 | |||
| 10134 | #ifdef USE_GTK | ||
| 10135 | if (f && xg_event_is_for_scrollbar (f, event)) | ||
| 10136 | f = 0; | ||
| 10137 | #endif | ||
| 10138 | |||
| 10139 | if (f) | ||
| 10140 | { | ||
| 10141 | /* Is this in the tab-bar? */ | ||
| 10142 | if (WINDOWP (f->tab_bar_window) | ||
| 10143 | && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window))) | ||
| 10144 | { | ||
| 10145 | Lisp_Object window; | ||
| 10146 | int x = bv.x; | ||
| 10147 | int y = bv.y; | ||
| 10148 | |||
| 10149 | window = window_from_coordinates (f, x, y, 0, true, true); | ||
| 10150 | tab_bar_p = EQ (window, f->tab_bar_window); | ||
| 10151 | |||
| 10152 | if (tab_bar_p) | ||
| 10153 | tab_bar_arg = handle_tab_bar_click | ||
| 10154 | (f, x, y, xev->evtype == XI_ButtonPress, | ||
| 10155 | x_x_to_emacs_modifiers (dpyinfo, bv.state)); | ||
| 10156 | } | ||
| 10157 | |||
| 10158 | #if ! defined (USE_GTK) | ||
| 10159 | /* Is this in the tool-bar? */ | ||
| 10160 | if (WINDOWP (f->tool_bar_window) | ||
| 10161 | && WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window))) | ||
| 10162 | { | ||
| 10163 | Lisp_Object window; | ||
| 10164 | int x = bv.x; | ||
| 10165 | int y = bv.y; | ||
| 10166 | |||
| 10167 | window = window_from_coordinates (f, x, y, 0, true, true); | ||
| 10168 | tool_bar_p = EQ (window, f->tool_bar_window); | ||
| 10169 | |||
| 10170 | if (tool_bar_p && xev->detail < 4) | ||
| 10171 | handle_tool_bar_click | ||
| 10172 | (f, x, y, xev->evtype == XI_ButtonPress, | ||
| 10173 | x_x_to_emacs_modifiers (dpyinfo, bv.state)); | ||
| 10174 | } | ||
| 10175 | #endif /* !USE_GTK */ | ||
| 10176 | |||
| 10177 | if (!(tab_bar_p && NILP (tab_bar_arg)) && !tool_bar_p) | ||
| 10178 | #if defined (USE_X_TOOLKIT) || defined (USE_GTK) | ||
| 10179 | if (! popup_activated ()) | ||
| 10180 | #endif | ||
| 10181 | { | ||
| 10182 | if (ignore_next_mouse_click_timeout) | ||
| 10183 | { | ||
| 10184 | if (xev->evtype == XI_ButtonPress | ||
| 10185 | && xev->time > ignore_next_mouse_click_timeout) | ||
| 10186 | { | ||
| 10187 | ignore_next_mouse_click_timeout = 0; | ||
| 10188 | x_construct_mouse_click (&inev.ie, &bv, f); | ||
| 10189 | } | ||
| 10190 | if (xev->evtype == XI_ButtonRelease) | ||
| 10191 | ignore_next_mouse_click_timeout = 0; | ||
| 10192 | } | ||
| 10193 | else | ||
| 10194 | x_construct_mouse_click (&inev.ie, &bv, f); | ||
| 10195 | |||
| 10196 | if (!NILP (tab_bar_arg)) | ||
| 10197 | inev.ie.arg = tab_bar_arg; | ||
| 10198 | } | ||
| 10199 | if (FRAME_X_EMBEDDED_P (f)) | ||
| 10200 | xembed_send_message (f, xev->time, | ||
| 10201 | XEMBED_REQUEST_FOCUS, 0, 0, 0); | ||
| 10202 | } | ||
| 10203 | |||
| 10204 | if (xev->evtype == XI_ButtonPress) | ||
| 10205 | { | ||
| 10206 | dpyinfo->grabbed |= (1 << xev->detail); | ||
| 10207 | device->grab |= (1 << xev->detail); | ||
| 10208 | dpyinfo->last_mouse_frame = f; | ||
| 10209 | if (f && !tab_bar_p) | ||
| 10210 | f->last_tab_bar_item = -1; | ||
| 10211 | #if ! defined (USE_GTK) | ||
| 10212 | if (f && !tool_bar_p) | ||
| 10213 | f->last_tool_bar_item = -1; | ||
| 10214 | #endif /* not USE_GTK */ | ||
| 10215 | |||
| 10216 | } | ||
| 10217 | else | ||
| 10218 | { | ||
| 10219 | dpyinfo->grabbed &= ~(1 << xev->detail); | ||
| 10220 | device->grab &= ~(1 << xev->detail); | ||
| 10221 | } | ||
| 10222 | |||
| 10223 | xi_grab_or_ungrab_device (device, dpyinfo, xev->event); | ||
| 10224 | |||
| 10225 | if (f) | ||
| 10226 | f->mouse_moved = false; | ||
| 10227 | |||
| 10228 | #if defined (USE_GTK) | ||
| 10229 | /* No Xt toolkit currently available has support for XI2. | ||
| 10230 | So the code here assumes use of GTK. */ | ||
| 10231 | f = x_menubar_window_to_frame (dpyinfo, event); | ||
| 10232 | if (f /* Gtk+ menus only react to the first three buttons. */ | ||
| 10233 | && xev->detail < 3) | ||
| 10234 | { | ||
| 10235 | /* What is done with Core Input ButtonPressed is not | ||
| 10236 | possible here, because GenericEvents cannot be saved. */ | ||
| 10237 | bool was_waiting_for_input = waiting_for_input; | ||
| 10238 | /* This hack was adopted from the NS port. Whether | ||
| 10239 | or not it is actually safe is a different story | ||
| 10240 | altogether. */ | ||
| 10241 | if (waiting_for_input) | ||
| 10242 | waiting_for_input = 0; | ||
| 10243 | set_frame_menubar (f, true); | ||
| 10244 | waiting_for_input = was_waiting_for_input; | ||
| 10245 | } | ||
| 10246 | #endif | ||
| 10247 | goto XI_OTHER; | ||
| 10248 | } | ||
| 10249 | case XI_KeyPress: | ||
| 10250 | { | ||
| 10251 | int state = xev->mods.base | ||
| 10252 | | xev->mods.effective | ||
| 10253 | | xev->mods.latched | ||
| 10254 | | xev->mods.locked; | ||
| 10255 | Lisp_Object c; | ||
| 10256 | #ifdef HAVE_XKB | ||
| 10257 | unsigned int mods_rtrn; | ||
| 10258 | #endif | ||
| 10259 | int keycode = xev->detail; | ||
| 10260 | KeySym keysym; | ||
| 10261 | char copy_buffer[81]; | ||
| 10262 | char *copy_bufptr = copy_buffer; | ||
| 10263 | unsigned char *copy_ubufptr; | ||
| 10264 | #ifdef HAVE_XKB | ||
| 10265 | int copy_bufsiz = sizeof (copy_buffer); | ||
| 10266 | #endif | ||
| 10267 | ptrdiff_t i; | ||
| 10268 | int nchars, len; | ||
| 10269 | |||
| 10270 | #ifdef HAVE_XKB | ||
| 10271 | if (dpyinfo->xkb_desc) | ||
| 10272 | { | ||
| 10273 | if (!XkbTranslateKeyCode (dpyinfo->xkb_desc, keycode, | ||
| 10274 | state, &mods_rtrn, &keysym)) | ||
| 10275 | goto XI_OTHER; | ||
| 10276 | } | ||
| 10277 | else | ||
| 10278 | { | ||
| 10279 | #endif | ||
| 10280 | int keysyms_per_keycode_return; | ||
| 10281 | KeySym *ksms = XGetKeyboardMapping (dpyinfo->display, keycode, 1, | ||
| 10282 | &keysyms_per_keycode_return); | ||
| 10283 | if (!(keysym = ksms[0])) | ||
| 10284 | { | ||
| 10285 | XFree (ksms); | ||
| 10286 | goto XI_OTHER; | ||
| 10287 | } | ||
| 10288 | XFree (ksms); | ||
| 10289 | #ifdef HAVE_XKB | ||
| 10290 | } | ||
| 10291 | #endif | ||
| 10292 | |||
| 10293 | if (keysym == NoSymbol) | ||
| 10294 | goto XI_OTHER; | ||
| 10295 | |||
| 10296 | x_display_set_last_user_time (dpyinfo, xev->time); | ||
| 10297 | ignore_next_mouse_click_timeout = 0; | ||
| 10298 | |||
| 10299 | #if defined (USE_X_TOOLKIT) || defined (USE_GTK) | ||
| 10300 | /* Dispatch XI_KeyPress events when in menu. */ | ||
| 10301 | if (popup_activated ()) | ||
| 10302 | goto XI_OTHER; | ||
| 10303 | #endif | ||
| 10304 | |||
| 10305 | f = x_any_window_to_frame (dpyinfo, xev->event); | ||
| 10306 | |||
| 10307 | /* If mouse-highlight is an integer, input clears out | ||
| 10308 | mouse highlighting. */ | ||
| 10309 | if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight) | ||
| 10310 | && (f == 0 | ||
| 10311 | #if ! defined (USE_GTK) | ||
| 10312 | || !EQ (f->tool_bar_window, hlinfo->mouse_face_window) | ||
| 10313 | #endif | ||
| 10314 | || !EQ (f->tab_bar_window, hlinfo->mouse_face_window)) | ||
| 10315 | ) | ||
| 10316 | { | ||
| 10317 | clear_mouse_face (hlinfo); | ||
| 10318 | hlinfo->mouse_face_hidden = true; | ||
| 10319 | } | ||
| 10320 | |||
| 10321 | if (f != 0) | ||
| 10322 | { | ||
| 10323 | #ifdef USE_GTK | ||
| 10324 | /* Don't pass keys to GTK. A Tab will shift focus to the | ||
| 10325 | tool bar in GTK 2.4. Keys will still go to menus and | ||
| 10326 | dialogs because in that case popup_activated is nonzero | ||
| 10327 | (see above). */ | ||
| 10328 | *finish = X_EVENT_DROP; | ||
| 10329 | #endif | ||
| 10330 | /* If not using XIM/XIC, and a compose sequence is in progress, | ||
| 10331 | we break here. Otherwise, chars_matched is always 0. */ | ||
| 10332 | if (compose_status.chars_matched > 0 && nbytes == 0) | ||
| 10333 | goto XI_OTHER; | ||
| 10334 | |||
| 10335 | memset (&compose_status, 0, sizeof (compose_status)); | ||
| 10336 | |||
| 10337 | XSETFRAME (inev.ie.frame_or_window, f); | ||
| 10338 | inev.ie.modifiers | ||
| 10339 | = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), state); | ||
| 10340 | inev.ie.timestamp = xev->time; | ||
| 10341 | |||
| 10342 | /* First deal with keysyms which have defined | ||
| 10343 | translations to characters. */ | ||
| 10344 | if (keysym >= 32 && keysym < 128) | ||
| 10345 | /* Avoid explicitly decoding each ASCII character. */ | ||
| 10346 | { | ||
| 10347 | inev.ie.kind = ASCII_KEYSTROKE_EVENT; | ||
| 10348 | inev.ie.code = keysym; | ||
| 10349 | |||
| 10350 | goto xi_done_keysym; | ||
| 10351 | } | ||
| 10352 | |||
| 10353 | /* Keysyms directly mapped to Unicode characters. */ | ||
| 10354 | if (keysym >= 0x01000000 && keysym <= 0x0110FFFF) | ||
| 10355 | { | ||
| 10356 | if (keysym < 0x01000080) | ||
| 10357 | inev.ie.kind = ASCII_KEYSTROKE_EVENT; | ||
| 10358 | else | ||
| 10359 | inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT; | ||
| 10360 | inev.ie.code = keysym & 0xFFFFFF; | ||
| 10361 | goto xi_done_keysym; | ||
| 10362 | } | ||
| 10363 | |||
| 10364 | /* Now non-ASCII. */ | ||
| 10365 | if (HASH_TABLE_P (Vx_keysym_table) | ||
| 10366 | && (c = Fgethash (make_fixnum (keysym), | ||
| 10367 | Vx_keysym_table, | ||
| 10368 | Qnil), | ||
| 10369 | FIXNATP (c))) | ||
| 10370 | { | ||
| 10371 | inev.ie.kind = (SINGLE_BYTE_CHAR_P (XFIXNAT (c)) | ||
| 10372 | ? ASCII_KEYSTROKE_EVENT | ||
| 10373 | : MULTIBYTE_CHAR_KEYSTROKE_EVENT); | ||
| 10374 | inev.ie.code = XFIXNAT (c); | ||
| 10375 | goto xi_done_keysym; | ||
| 10376 | } | ||
| 10377 | |||
| 10378 | /* Random non-modifier sorts of keysyms. */ | ||
| 10379 | if (((keysym >= XK_BackSpace && keysym <= XK_Escape) | ||
| 10380 | || keysym == XK_Delete | ||
| 10381 | #ifdef XK_ISO_Left_Tab | ||
| 10382 | || (keysym >= XK_ISO_Left_Tab | ||
| 10383 | && keysym <= XK_ISO_Enter) | ||
| 10384 | #endif | ||
| 10385 | || IsCursorKey (keysym) /* 0xff50 <= x < 0xff60 */ | ||
| 10386 | || IsMiscFunctionKey (keysym) /* 0xff60 <= x < VARIES */ | ||
| 10387 | #ifdef HPUX | ||
| 10388 | /* This recognizes the "extended function | ||
| 10389 | keys". It seems there's no cleaner way. | ||
| 10390 | Test IsModifierKey to avoid handling | ||
| 10391 | mode_switch incorrectly. */ | ||
| 10392 | || (XK_Select <= keysym && keysym < XK_KP_Space) | ||
| 10393 | #endif | ||
| 10394 | #ifdef XK_dead_circumflex | ||
| 10395 | || keysym == XK_dead_circumflex | ||
| 10396 | #endif | ||
| 10397 | #ifdef XK_dead_grave | ||
| 10398 | || keysym == XK_dead_grave | ||
| 10399 | #endif | ||
| 10400 | #ifdef XK_dead_tilde | ||
| 10401 | || keysym == XK_dead_tilde | ||
| 10402 | #endif | ||
| 10403 | #ifdef XK_dead_diaeresis | ||
| 10404 | || keysym == XK_dead_diaeresis | ||
| 10405 | #endif | ||
| 10406 | #ifdef XK_dead_macron | ||
| 10407 | || keysym == XK_dead_macron | ||
| 10408 | #endif | ||
| 10409 | #ifdef XK_dead_degree | ||
| 10410 | || keysym == XK_dead_degree | ||
| 10411 | #endif | ||
| 10412 | #ifdef XK_dead_acute | ||
| 10413 | || keysym == XK_dead_acute | ||
| 10414 | #endif | ||
| 10415 | #ifdef XK_dead_cedilla | ||
| 10416 | || keysym == XK_dead_cedilla | ||
| 10417 | #endif | ||
| 10418 | #ifdef XK_dead_breve | ||
| 10419 | || keysym == XK_dead_breve | ||
| 10420 | #endif | ||
| 10421 | #ifdef XK_dead_ogonek | ||
| 10422 | || keysym == XK_dead_ogonek | ||
| 10423 | #endif | ||
| 10424 | #ifdef XK_dead_caron | ||
| 10425 | || keysym == XK_dead_caron | ||
| 10426 | #endif | ||
| 10427 | #ifdef XK_dead_doubleacute | ||
| 10428 | || keysym == XK_dead_doubleacute | ||
| 10429 | #endif | ||
| 10430 | #ifdef XK_dead_abovedot | ||
| 10431 | || keysym == XK_dead_abovedot | ||
| 10432 | #endif | ||
| 10433 | || IsKeypadKey (keysym) /* 0xff80 <= x < 0xffbe */ | ||
| 10434 | || IsFunctionKey (keysym) /* 0xffbe <= x < 0xffe1 */ | ||
| 10435 | /* Any "vendor-specific" key is ok. */ | ||
| 10436 | || (keysym & (1 << 28)) | ||
| 10437 | || (keysym != NoSymbol && nbytes == 0)) | ||
| 10438 | && ! (IsModifierKey (keysym) | ||
| 10439 | /* The symbols from XK_ISO_Lock | ||
| 10440 | to XK_ISO_Last_Group_Lock | ||
| 10441 | don't have real modifiers but | ||
| 10442 | should be treated similarly to | ||
| 10443 | Mode_switch by Emacs. */ | ||
| 10444 | #if defined XK_ISO_Lock && defined XK_ISO_Last_Group_Lock | ||
| 10445 | || (XK_ISO_Lock <= keysym | ||
| 10446 | && keysym <= XK_ISO_Last_Group_Lock) | ||
| 10447 | #endif | ||
| 10448 | )) | ||
| 10449 | { | ||
| 10450 | STORE_KEYSYM_FOR_DEBUG (keysym); | ||
| 10451 | /* make_lispy_event will convert this to a symbolic | ||
| 10452 | key. */ | ||
| 10453 | inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT; | ||
| 10454 | inev.ie.code = keysym; | ||
| 10455 | goto xi_done_keysym; | ||
| 10456 | } | ||
| 10457 | |||
| 10458 | #ifdef HAVE_XKB | ||
| 10459 | int overflow = 0; | ||
| 10460 | KeySym sym = keysym; | ||
| 10461 | |||
| 10462 | if (dpyinfo->xkb_desc) | ||
| 10463 | { | ||
| 10464 | if (!(nbytes = XkbTranslateKeySym (dpyinfo->display, &sym, | ||
| 10465 | state & ~mods_rtrn, copy_bufptr, | ||
| 10466 | copy_bufsiz, &overflow))) | ||
| 10467 | goto XI_OTHER; | ||
| 10468 | } | ||
| 10469 | else | ||
| 10470 | #else | ||
| 10471 | { | ||
| 10472 | block_input (); | ||
| 10473 | char *str = XKeysymToString (keysym); | ||
| 10474 | if (!str) | ||
| 10475 | { | ||
| 10476 | unblock_input (); | ||
| 10477 | goto XI_OTHER; | ||
| 10478 | } | ||
| 10479 | nbytes = strlen (str) + 1; | ||
| 10480 | copy_bufptr = alloca (nbytes); | ||
| 10481 | strcpy (copy_bufptr, str); | ||
| 10482 | unblock_input (); | ||
| 10483 | } | ||
| 10484 | #endif | ||
| 10485 | #ifdef HAVE_XKB | ||
| 10486 | if (overflow) | ||
| 10487 | { | ||
| 10488 | overflow = 0; | ||
| 10489 | copy_bufptr = alloca (copy_bufsiz + overflow); | ||
| 10490 | keysym = sym; | ||
| 10491 | if (!(nbytes = XkbTranslateKeySym (dpyinfo->display, &sym, | ||
| 10492 | state & ~mods_rtrn, copy_bufptr, | ||
| 10493 | copy_bufsiz + overflow, &overflow))) | ||
| 10494 | goto XI_OTHER; | ||
| 10495 | |||
| 10496 | if (overflow) | ||
| 10497 | goto XI_OTHER; | ||
| 10498 | } | ||
| 10499 | #endif | ||
| 10500 | |||
| 10501 | for (i = 0, nchars = 0; i < nbytes; i++) | ||
| 10502 | { | ||
| 10503 | if (ASCII_CHAR_P (copy_bufptr[i])) | ||
| 10504 | nchars++; | ||
| 10505 | STORE_KEYSYM_FOR_DEBUG (copy_bufptr[i]); | ||
| 10506 | } | ||
| 10507 | |||
| 10508 | if (nchars < nbytes) | ||
| 10509 | { | ||
| 10510 | /* Decode the input data. */ | ||
| 10511 | |||
| 10512 | setup_coding_system (Vlocale_coding_system, &coding); | ||
| 10513 | coding.src_multibyte = false; | ||
| 10514 | coding.dst_multibyte = true; | ||
| 10515 | /* The input is converted to events, thus we can't | ||
| 10516 | handle composition. Anyway, there's no XIM that | ||
| 10517 | gives us composition information. */ | ||
| 10518 | coding.common_flags &= ~CODING_ANNOTATION_MASK; | ||
| 10519 | |||
| 10520 | SAFE_NALLOCA (coding.destination, MAX_MULTIBYTE_LENGTH, | ||
| 10521 | nbytes); | ||
| 10522 | coding.dst_bytes = MAX_MULTIBYTE_LENGTH * nbytes; | ||
| 10523 | coding.mode |= CODING_MODE_LAST_BLOCK; | ||
| 10524 | decode_coding_c_string (&coding, (unsigned char *) copy_bufptr, | ||
| 10525 | nbytes, Qnil); | ||
| 10526 | nbytes = coding.produced; | ||
| 10527 | nchars = coding.produced_char; | ||
| 10528 | copy_bufptr = (char *) coding.destination; | ||
| 10529 | } | ||
| 10530 | |||
| 10531 | copy_ubufptr = (unsigned char *) copy_bufptr; | ||
| 10532 | |||
| 10533 | /* Convert the input data to a sequence of | ||
| 10534 | character events. */ | ||
| 10535 | for (i = 0; i < nbytes; i += len) | ||
| 10536 | { | ||
| 10537 | int ch; | ||
| 10538 | if (nchars == nbytes) | ||
| 10539 | ch = copy_ubufptr[i], len = 1; | ||
| 10540 | else | ||
| 10541 | ch = string_char_and_length (copy_ubufptr + i, &len); | ||
| 10542 | inev.ie.kind = (SINGLE_BYTE_CHAR_P (ch) | ||
| 10543 | ? ASCII_KEYSTROKE_EVENT | ||
| 10544 | : MULTIBYTE_CHAR_KEYSTROKE_EVENT); | ||
| 10545 | inev.ie.code = ch; | ||
| 10546 | kbd_buffer_store_buffered_event (&inev, hold_quit); | ||
| 10547 | } | ||
| 10548 | |||
| 10549 | inev.ie.kind = NO_EVENT; | ||
| 10550 | goto xi_done_keysym; | ||
| 10551 | } | ||
| 10552 | goto XI_OTHER; | ||
| 10553 | } | ||
| 10554 | case XI_KeyRelease: | ||
| 10555 | x_display_set_last_user_time (dpyinfo, xev->time); | ||
| 10556 | goto XI_OTHER; | ||
| 10557 | case XI_PropertyEvent: | ||
| 10558 | case XI_HierarchyChanged: | ||
| 10559 | case XI_DeviceChanged: | ||
| 10560 | x_init_master_valuators (dpyinfo); | ||
| 10561 | goto XI_OTHER; | ||
| 10562 | default: | ||
| 10563 | goto XI_OTHER; | ||
| 10564 | } | ||
| 10565 | xi_done_keysym: | ||
| 10566 | if (must_free_data) | ||
| 10567 | XFreeEventData (dpyinfo->display, &event->xcookie); | ||
| 10568 | goto done_keysym; | ||
| 10569 | XI_OTHER: | ||
| 10570 | if (must_free_data) | ||
| 10571 | XFreeEventData (dpyinfo->display, &event->xcookie); | ||
| 10572 | goto OTHER; | ||
| 10573 | } | ||
| 10574 | #endif | ||
| 9521 | 10575 | ||
| 9522 | default: | 10576 | default: |
| 9523 | OTHER: | 10577 | OTHER: |
| @@ -13199,6 +14253,40 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) | |||
| 13199 | dpyinfo->supports_xdbe = true; | 14253 | dpyinfo->supports_xdbe = true; |
| 13200 | #endif | 14254 | #endif |
| 13201 | 14255 | ||
| 14256 | #ifdef HAVE_XINPUT2 | ||
| 14257 | dpyinfo->supports_xi2 = false; | ||
| 14258 | int rc; | ||
| 14259 | int major = 2; | ||
| 14260 | #ifdef XI_BarrierHit /* XInput 2.3 */ | ||
| 14261 | int minor = 3; | ||
| 14262 | #elif defined XI_TouchBegin /* XInput 2.2 */ | ||
| 14263 | int minor = 2; | ||
| 14264 | #elif defined XIScrollClass /* XInput 1.1 */ | ||
| 14265 | int minor = 1; | ||
| 14266 | #else /* Some old version of XI2 we're not interested in. */ | ||
| 14267 | int minor = 0; | ||
| 14268 | #endif | ||
| 14269 | int fer, fee; | ||
| 14270 | |||
| 14271 | if (XQueryExtension (dpyinfo->display, "XInputExtension", | ||
| 14272 | &dpyinfo->xi2_opcode, &fer, &fee)) | ||
| 14273 | { | ||
| 14274 | rc = XIQueryVersion (dpyinfo->display, &major, &minor); | ||
| 14275 | if (rc == Success) | ||
| 14276 | { | ||
| 14277 | dpyinfo->supports_xi2 = true; | ||
| 14278 | x_init_master_valuators (dpyinfo); | ||
| 14279 | } | ||
| 14280 | } | ||
| 14281 | dpyinfo->xi2_version = minor; | ||
| 14282 | #endif | ||
| 14283 | |||
| 14284 | #ifdef HAVE_XKB | ||
| 14285 | dpyinfo->xkb_desc = XkbGetMap (dpyinfo->display, | ||
| 14286 | XkbAllComponentsMask, | ||
| 14287 | XkbUseCoreKbd); | ||
| 14288 | #endif | ||
| 14289 | |||
| 13202 | #if defined USE_CAIRO || defined HAVE_XFT | 14290 | #if defined USE_CAIRO || defined HAVE_XFT |
| 13203 | { | 14291 | { |
| 13204 | /* If we are using Xft, the following precautions should be made: | 14292 | /* If we are using Xft, the following precautions should be made: |
| @@ -13631,6 +14719,14 @@ x_delete_terminal (struct terminal *terminal) | |||
| 13631 | XrmDestroyDatabase (dpyinfo->rdb); | 14719 | XrmDestroyDatabase (dpyinfo->rdb); |
| 13632 | #endif | 14720 | #endif |
| 13633 | 14721 | ||
| 14722 | #ifdef HAVE_XKB | ||
| 14723 | if (dpyinfo->xkb_desc) | ||
| 14724 | XkbFreeKeyboard (dpyinfo->xkb_desc, XkbAllComponentsMask, True); | ||
| 14725 | #endif | ||
| 14726 | #ifdef HAVE_XINPUT2 | ||
| 14727 | if (dpyinfo->supports_xi2) | ||
| 14728 | x_free_xi_devices (dpyinfo); | ||
| 14729 | #endif | ||
| 13634 | #ifdef USE_GTK | 14730 | #ifdef USE_GTK |
| 13635 | xg_display_close (dpyinfo->display); | 14731 | xg_display_close (dpyinfo->display); |
| 13636 | #else | 14732 | #else |
| @@ -13790,9 +14886,12 @@ x_initialize (void) | |||
| 13790 | void | 14886 | void |
| 13791 | init_xterm (void) | 14887 | init_xterm (void) |
| 13792 | { | 14888 | { |
| 13793 | /* Emacs can handle only core input events, so make sure | 14889 | #ifndef HAVE_XINPUT2 |
| 13794 | Gtk doesn't use Xinput or Xinput2 extensions. */ | 14890 | /* Emacs can handle only core input events when built without XI2 |
| 14891 | support, so make sure Gtk doesn't use Xinput or Xinput2 | ||
| 14892 | extensions. */ | ||
| 13795 | xputenv ("GDK_CORE_DEVICE_EVENTS=1"); | 14893 | xputenv ("GDK_CORE_DEVICE_EVENTS=1"); |
| 14894 | #endif | ||
| 13796 | } | 14895 | } |
| 13797 | #endif | 14896 | #endif |
| 13798 | 14897 | ||
diff --git a/src/xterm.h b/src/xterm.h index 9d9534dd629..7abe168bc6f 100644 --- a/src/xterm.h +++ b/src/xterm.h | |||
| @@ -88,6 +88,10 @@ typedef GtkWidget *xt_or_gtk_widget; | |||
| 88 | #include <X11/Xlib-xcb.h> | 88 | #include <X11/Xlib-xcb.h> |
| 89 | #endif | 89 | #endif |
| 90 | 90 | ||
| 91 | #ifdef HAVE_XKB | ||
| 92 | #include <X11/XKBlib.h> | ||
| 93 | #endif | ||
| 94 | |||
| 91 | #include "dispextern.h" | 95 | #include "dispextern.h" |
| 92 | #include "termhooks.h" | 96 | #include "termhooks.h" |
| 93 | 97 | ||
| @@ -163,6 +167,28 @@ struct color_name_cache_entry | |||
| 163 | char *name; | 167 | char *name; |
| 164 | }; | 168 | }; |
| 165 | 169 | ||
| 170 | #ifdef HAVE_XINPUT2 | ||
| 171 | struct xi_scroll_valuator_t | ||
| 172 | { | ||
| 173 | bool invalid_p; | ||
| 174 | double current_value; | ||
| 175 | double emacs_value; | ||
| 176 | double increment; | ||
| 177 | |||
| 178 | int number; | ||
| 179 | int horizontal; | ||
| 180 | }; | ||
| 181 | |||
| 182 | struct xi_device_t | ||
| 183 | { | ||
| 184 | int device_id; | ||
| 185 | int scroll_valuator_count; | ||
| 186 | int grab; | ||
| 187 | |||
| 188 | struct xi_scroll_valuator_t *valuators; | ||
| 189 | }; | ||
| 190 | #endif | ||
| 191 | |||
| 166 | Status x_parse_color (struct frame *f, const char *color_name, | 192 | Status x_parse_color (struct frame *f, const char *color_name, |
| 167 | XColor *color); | 193 | XColor *color); |
| 168 | 194 | ||
| @@ -474,6 +500,19 @@ struct x_display_info | |||
| 474 | #ifdef HAVE_XDBE | 500 | #ifdef HAVE_XDBE |
| 475 | bool supports_xdbe; | 501 | bool supports_xdbe; |
| 476 | #endif | 502 | #endif |
| 503 | |||
| 504 | #ifdef HAVE_XINPUT2 | ||
| 505 | bool supports_xi2; | ||
| 506 | int xi2_version; | ||
| 507 | int xi2_opcode; | ||
| 508 | |||
| 509 | int num_devices; | ||
| 510 | struct xi_device_t *devices; | ||
| 511 | #endif | ||
| 512 | |||
| 513 | #ifdef HAVE_XKB | ||
| 514 | XkbDescPtr xkb_desc; | ||
| 515 | #endif | ||
| 477 | }; | 516 | }; |
| 478 | 517 | ||
| 479 | #ifdef HAVE_X_I18N | 518 | #ifdef HAVE_X_I18N |
| @@ -481,6 +520,11 @@ struct x_display_info | |||
| 481 | extern bool use_xim; | 520 | extern bool use_xim; |
| 482 | #endif | 521 | #endif |
| 483 | 522 | ||
| 523 | #ifdef HAVE_XINPUT2 | ||
| 524 | /* Defined in xmenu.c. */ | ||
| 525 | extern int popup_activated_flag; | ||
| 526 | #endif | ||
| 527 | |||
| 484 | /* This is a chain of structures for all the X displays currently in use. */ | 528 | /* This is a chain of structures for all the X displays currently in use. */ |
| 485 | extern struct x_display_info *x_display_list; | 529 | extern struct x_display_info *x_display_list; |
| 486 | 530 | ||