From: Stefan Monnier <monnier@iro.umontreal.ca>
To: Po Lu via "Emacs development discussions." <emacs-devel@gnu.org>
Cc: Po Lu <luangruo@yahoo.com>
Subject: Re: XInput 2 support (again)
Date: Sat, 13 Nov 2021 10:04:04 -0500 [thread overview]
Message-ID: <jwv8rxs6r8m.fsf-monnier+emacs@gnu.org> (raw)
In-Reply-To: <87v90wmh6m.fsf@yahoo.com> (Po Lu via's message of "Sat, 13 Nov 2021 19:24:49 +0800")
> @@ -3074,6 +3078,35 @@ 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)
> + {
> + 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 HAVE_X_I18N
> FRAME_XIC (f) = NULL;
> if (use_xim)
> @@ -3254,6 +3287,35 @@ x_window (struct frame *f)
> }
> #endif /* HAVE_X_I18N */
>
> +#ifdef HAVE_XINPUT2
> + if (FRAME_DISPLAY_INFO (f)->supports_xi2)
> + {
> + 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
> +
Any reason this isn't consolidated into a separate function to avoid the
code duplication?
> + struct xi_device_t *xi_device =
> + &dpyinfo->devices[actual_devices++];
The GNU coding standard encourages to cut lines just *before* infix
operators rather after. E.g.:
struct xi_device_t *xi_device
= &dpyinfo->devices[actual_devices++];
> @@ -9518,6 +9772,833 @@ handle_one_xevent (struct x_display_info *dpyinfo,
> case DestroyNotify:
> xft_settings_event (dpyinfo, event);
> break;
> +#ifdef HAVE_XINPUT2
> + case GenericEvent:
> + {
> + if (!dpyinfo->supports_xi2)
> + goto OTHER;
> + if (event->xgeneric.extension != dpyinfo->xi2_opcode)
> + /* Not an XI2 event. */
> + goto OTHER;
> + bool must_free_data = false;
> + XIEvent *xi_event = (XIEvent *) event->xcookie.data;
> + /* Sometimes the event is already claimed by GTK, which
> + will free its data in due course. */
> + if (!xi_event && XGetEventData (dpyinfo->display, &event->xcookie))
> + {
> + must_free_data = true;
> + xi_event = (XIEvent *) event->xcookie.data;
> + }
> +
> + XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
> + XILeaveEvent *leave = (XILeaveEvent *) xi_event;
> + XIEnterEvent *enter = (XIEnterEvent *) xi_event;
> + XIFocusInEvent *focusin = (XIFocusInEvent *) xi_event;
> + XIFocusOutEvent *focusout = (XIFocusOutEvent *) xi_event;
> + XIValuatorState *states;
> + double *values;
> + bool found_valuator = false;
> +
> + /* A fake XMotionEvent for x_note_mouse_movement. */
> + XMotionEvent ev;
> + /* A fake XButtonEvent for x_construct_mouse_click. */
> + XButtonEvent bv;
> +
> + if (!xi_event)
> + {
> + eassert (!must_free_data);
> + goto OTHER;
> + }
> +
> + switch (event->xcookie.evtype)
> + {
> + case XI_FocusIn:
> + any = x_any_window_to_frame (dpyinfo, focusin->event);
> +#ifndef USE_GTK
> + /* Some WMs (e.g. Mutter in Gnome Shell), don't unmap
> + minimized/iconified windows; thus, for those WMs we won't get
> + a MapNotify when unminimizing/deconifying. Check here if we
> + are deiconizing a window (Bug42655).
> +
> + But don't do that on GTK since it may cause a plain invisible
> + frame get reported as iconified, compare
> + https://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00133.html.
> + That is fixed above but bites us here again. */
> + f = any;
> + if (f && FRAME_ICONIFIED_P (f))
> + {
> + SET_FRAME_VISIBLE (f, 1);
> + SET_FRAME_ICONIFIED (f, false);
> + f->output_data.x->has_been_visible = true;
> + inev.ie.kind = DEICONIFY_EVENT;
> + XSETFRAME (inev.ie.frame_or_window, f);
> + }
> +#endif /* USE_GTK */
> + x_detect_focus_change (dpyinfo, any, event, &inev.ie);
> + goto XI_OTHER;
> + case XI_FocusOut:
> + any = x_any_window_to_frame (dpyinfo, focusout->event);
> + x_detect_focus_change (dpyinfo, any, event, &inev.ie);
> + goto XI_OTHER;
> + case XI_Enter:
> + any = x_any_window_to_frame (dpyinfo, enter->event);
> + ev.x = lrint (enter->event_x);
> + ev.y = lrint (enter->event_y);
> + ev.window = leave->event;
> +
> + x_display_set_last_user_time (dpyinfo, xi_event->time);
> + x_detect_focus_change (dpyinfo, any, event, &inev.ie);
> + f = any;
> +
> + if (f && x_mouse_click_focus_ignore_position)
> + ignore_next_mouse_click_timeout = xi_event->time + 200;
> +
> + /* EnterNotify counts as mouse movement,
> + so update things that depend on mouse position. */
> + if (f && !f->output_data.x->hourglass_p)
> + x_note_mouse_movement (f, &ev);
> +#ifdef USE_GTK
> + /* We may get an EnterNotify on the buttons in the toolbar. In that
> + case we moved out of any highlighted area and need to note this. */
> + if (!f && dpyinfo->last_mouse_glyph_frame)
> + x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &ev);
> +#endif
> + goto XI_OTHER;
> + case XI_Leave:
> + ev.x = lrint (leave->event_x);
> + ev.y = lrint (leave->event_y);
> + ev.window = leave->event;
> + any = x_any_window_to_frame (dpyinfo, leave->event);
> +
> + x_display_set_last_user_time (dpyinfo, xi_event->time);
> + x_detect_focus_change (dpyinfo, any, event, &inev.ie);
> +
> + f = x_top_window_to_frame (dpyinfo, leave->event);
> + if (f)
> + {
> + if (f == hlinfo->mouse_face_mouse_frame)
> + {
> + /* If we move outside the frame, then we're
> + certainly no longer on any text in the frame. */
> + clear_mouse_face (hlinfo);
> + hlinfo->mouse_face_mouse_frame = 0;
> + }
> +
> + /* Generate a nil HELP_EVENT to cancel a help-echo.
> + Do it only if there's something to cancel.
> + Otherwise, the startup message is cleared when
> + the mouse leaves the frame. */
> + if (any_help_event_p)
> + do_help = -1;
> + }
> +#ifdef USE_GTK
> + /* See comment in EnterNotify above */
> + else if (dpyinfo->last_mouse_glyph_frame)
> + x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &ev);
> +#endif
> + goto XI_OTHER;
> + case XI_Motion:
> + /* First test if there is some kind of scroll event
> + here! */
> + states = &xev->valuators;
> + values = states->values;
> +
> + x_display_set_last_user_time (dpyinfo, xi_event->time);
> +
> + for (int i = 0; i < states->mask_len * 8; i++)
> + {
> + if (XIMaskIsSet (states->mask, i))
> + {
> + block_input ();
> +
> + struct xi_scroll_valuator_t *val;
> + double delta =
> + x_get_scroll_valuator_delta (dpyinfo, xev->deviceid,
> + i, *values, &val);
> +
> + if (delta != DBL_MAX)
> + {
> + f = mouse_or_wdesc_frame (dpyinfo, xev->event);
> + found_valuator = true;
> + if (signbit (delta) != signbit (val->emacs_value))
> + val->emacs_value = DBL_MIN;
> +
> + val->emacs_value += delta;
> +
> + if (!f)
> + {
> + f = x_any_window_to_frame (dpyinfo, xev->event);
> +
> + if (!f)
> + {
> + unblock_input ();
> + goto XI_OTHER;
> + }
> + }
> +
> + if ((val->horizontal
> + && fabs (val->emacs_value / val->increment) >= 1)
> + || (!val->horizontal
> + && fabs (val->emacs_value / val->increment) >= 1))
> + {
> + Lisp_Object tab_bar_arg = Qnil;
> + bool tab_bar_p = false;
> + bool tool_bar_p = false;
> + bool s = signbit (val->emacs_value);
> +
> + bv.button = !val->horizontal ? (s ? 5 : 4) : (s ? 7 : 6);
> + bv.type = ButtonPress;
> +
> + bv.x = lrint (xev->event_x);
> + bv.y = lrint (xev->event_y);
> + bv.window = xev->event;
> + bv.state = xev->mods.base
> + | xev->mods.effective
> + | xev->mods.latched
> + | xev->mods.locked;
> +
> + /* Is this in the tab-bar? */
> + if (WINDOWP (f->tab_bar_window)
> + && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
> + {
> + Lisp_Object window;
> + int x = bv.x;
> + int y = bv.y;
> +
> + window = window_from_coordinates (f, x, y, 0, true, true);
> + tab_bar_p = EQ (window, f->tab_bar_window);
> +
> + if (tab_bar_p)
> + {
> + tab_bar_arg = handle_tab_bar_click
> + (f, x, y, true, x_x_to_emacs_modifiers (dpyinfo, bv.state));
> + tab_bar_arg = handle_tab_bar_click
> + (f, x, y, false, x_x_to_emacs_modifiers (dpyinfo, bv.state));
> + }
> + }
> +
> + if (!NILP (tab_bar_arg))
> + inev.ie.arg = tab_bar_arg;
> +
> + if (!tool_bar_p && !(NILP (tab_bar_arg) && tab_bar_p))
> + {
> + if (ignore_next_mouse_click_timeout)
> + {
> + if (xev->time > ignore_next_mouse_click_timeout)
> + {
> + x_construct_mouse_click (&inev.ie, &bv, f);
> + if (!NILP (tab_bar_arg))
> + inev.ie.arg = tab_bar_arg;
> + kbd_buffer_store_event (&inev.ie);
> + inev.ie.modifiers &= ~down_modifier;
> + inev.ie.modifiers |= up_modifier;
> + kbd_buffer_store_event (&inev.ie);
> + }
> + ignore_next_mouse_click_timeout = 0;
> + }
> + else
> + {
> + x_construct_mouse_click (&inev.ie, &bv, f);
> + kbd_buffer_store_event (&inev.ie);
> + inev.ie.modifiers &= ~down_modifier;
> + inev.ie.modifiers |= up_modifier;
> + kbd_buffer_store_event (&inev.ie);
> + }
> + }
> +
> + val->emacs_value = DBL_MIN;
> + }
> + }
> + unblock_input ();
> + values++;
> + }
> +
> + inev.ie.kind = NO_EVENT;
> + }
> +
> + if (found_valuator)
> + goto XI_OTHER;
> +
> + ev.x = lrint (xev->event_x);
> + ev.y = lrint (xev->event_y);
> + ev.window = xev->event;
> +
> + previous_help_echo_string = help_echo_string;
> + help_echo_string = Qnil;
> +
> + if (hlinfo->mouse_face_hidden)
> + {
> + hlinfo->mouse_face_hidden = false;
> + clear_mouse_face (hlinfo);
> + }
> +
> + f = mouse_or_wdesc_frame (dpyinfo, xev->event);
> +
> +#ifdef USE_GTK
> + if (f && xg_event_is_for_scrollbar (f, event))
> + f = 0;
> +#endif
> + if (f)
> + {
> + /* Maybe generate a SELECT_WINDOW_EVENT for
> + `mouse-autoselect-window' but don't let popup menus
> + interfere with this (Bug#1261). */
> + if (!NILP (Vmouse_autoselect_window)
> + && !popup_activated ()
> + /* Don't switch if we're currently in the minibuffer.
> + This tries to work around problems where the
> + minibuffer gets unselected unexpectedly, and where
> + you then have to move your mouse all the way down to
> + the minibuffer to select it. */
> + && !MINI_WINDOW_P (XWINDOW (selected_window))
> + /* With `focus-follows-mouse' non-nil create an event
> + also when the target window is on another frame. */
> + && (f == XFRAME (selected_frame)
> + || !NILP (focus_follows_mouse)))
> + {
> + static Lisp_Object last_mouse_window;
> + Lisp_Object window = window_from_coordinates (f, ev.x, ev.y, 0, false, false);
> +
> + /* A window will be autoselected only when it is not
> + selected now and the last mouse movement event was
> + not in it. The remainder of the code is a bit vague
> + wrt what a "window" is. For immediate autoselection,
> + the window is usually the entire window but for GTK
> + where the scroll bars don't count. For delayed
> + autoselection the window is usually the window's text
> + area including the margins. */
> + if (WINDOWP (window)
> + && !EQ (window, last_mouse_window)
> + && !EQ (window, selected_window))
> + {
> + inev.ie.kind = SELECT_WINDOW_EVENT;
> + inev.ie.frame_or_window = window;
> + }
> +
> + /* Remember the last window where we saw the mouse. */
> + last_mouse_window = window;
> + }
> +
> + if (!x_note_mouse_movement (f, &ev))
> + help_echo_string = previous_help_echo_string;
> + }
> + else
> + {
> +#ifndef USE_TOOLKIT_SCROLL_BARS
> + struct scroll_bar *bar
> + = x_window_to_scroll_bar (xi_event->display, xev->event, 2);
> +
> + if (bar)
> + x_scroll_bar_note_movement (bar, &ev);
> +#endif /* USE_TOOLKIT_SCROLL_BARS */
> +
> + /* If we move outside the frame, then we're
> + certainly no longer on any text in the frame. */
> + clear_mouse_face (hlinfo);
> + }
> +
> + /* If the contents of the global variable help_echo_string
> + has changed, generate a HELP_EVENT. */
> + if (!NILP (help_echo_string)
> + || !NILP (previous_help_echo_string))
> + do_help = 1;
> + goto XI_OTHER;
> + case XI_ButtonRelease:
> + case XI_ButtonPress:
> + {
> + /* If we decide we want to generate an event to be seen
> + by the rest of Emacs, we put it here. */
> + Lisp_Object tab_bar_arg = Qnil;
> + bool tab_bar_p = false;
> + bool tool_bar_p = false;
> + struct xi_device_t *device;
> +
> + /* Ignore emulated scroll events when XI2 native
> + scroll events are present. */
> + if (dpyinfo->xi2_version >= 1 && xev->detail >= 4
> + && xev->detail <= 8)
> + goto XI_OTHER;
> +
> + device = xi_device_from_id (dpyinfo, xev->deviceid);
> +
> + bv.button = xev->detail;
> + bv.type = xev->evtype == XI_ButtonPress ? ButtonPress : ButtonRelease;
> + bv.x = lrint (xev->event_x);
> + bv.y = lrint (xev->event_y);
> + bv.window = xev->event;
> + bv.state = xev->mods.base
> + | xev->mods.effective
> + | xev->mods.latched
> + | xev->mods.locked;
> +
> + memset (&compose_status, 0, sizeof (compose_status));
> + dpyinfo->last_mouse_glyph_frame = NULL;
> + x_display_set_last_user_time (dpyinfo, xev->time);
> +
> + f = mouse_or_wdesc_frame (dpyinfo, xev->event);
> +
> + if (f && xev->evtype == XI_ButtonPress
> + && !popup_activated ()
> + && !x_window_to_scroll_bar (xev->display, xev->event, 2)
> + && !FRAME_NO_ACCEPT_FOCUS (f))
> + {
> + /* When clicking into a child frame or when clicking
> + into a parent frame with the child frame selected and
> + `no-accept-focus' is not set, select the clicked
> + frame. */
> + struct frame *hf = dpyinfo->highlight_frame;
> +
> + if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
> + {
> + block_input ();
> + XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
> + RevertToParent, CurrentTime);
> + if (FRAME_PARENT_FRAME (f))
> + XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f));
> + unblock_input ();
> + }
> + }
> +
> +#ifdef USE_GTK
> + if (f && xg_event_is_for_scrollbar (f, event))
> + f = 0;
> +#endif
> +
> + if (f)
> + {
> + /* Is this in the tab-bar? */
> + if (WINDOWP (f->tab_bar_window)
> + && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
> + {
> + Lisp_Object window;
> + int x = bv.x;
> + int y = bv.y;
> +
> + window = window_from_coordinates (f, x, y, 0, true, true);
> + tab_bar_p = EQ (window, f->tab_bar_window);
> +
> + if (tab_bar_p)
> + tab_bar_arg = handle_tab_bar_click
> + (f, x, y, xev->evtype == XI_ButtonPress,
> + x_x_to_emacs_modifiers (dpyinfo, bv.state));
> + }
> +
> +#if ! defined (USE_GTK)
> + /* Is this in the tool-bar? */
> + if (WINDOWP (f->tool_bar_window)
> + && WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window)))
> + {
> + Lisp_Object window;
> + int x = bv.x;
> + int y = bv.y;
> +
> + window = window_from_coordinates (f, x, y, 0, true, true);
> + tool_bar_p = EQ (window, f->tool_bar_window);
> +
> + if (tool_bar_p && xev->detail < 4)
> + handle_tool_bar_click
> + (f, x, y, xev->evtype == XI_ButtonPress,
> + x_x_to_emacs_modifiers (dpyinfo, bv.state));
> + }
> +#endif /* !USE_GTK */
> +
> + if (!(tab_bar_p && NILP (tab_bar_arg)) && !tool_bar_p)
> +#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
> + if (! popup_activated ())
> +#endif
> + {
> + if (ignore_next_mouse_click_timeout)
> + {
> + if (xev->evtype == XI_ButtonPress
> + && xev->time > ignore_next_mouse_click_timeout)
> + {
> + ignore_next_mouse_click_timeout = 0;
> + x_construct_mouse_click (&inev.ie, &bv, f);
> + }
> + if (xev->evtype == XI_ButtonRelease)
> + ignore_next_mouse_click_timeout = 0;
> + }
> + else
> + x_construct_mouse_click (&inev.ie, &bv, f);
> +
> + if (!NILP (tab_bar_arg))
> + inev.ie.arg = tab_bar_arg;
> + }
> + if (FRAME_X_EMBEDDED_P (f))
> + xembed_send_message (f, xev->time,
> + XEMBED_REQUEST_FOCUS, 0, 0, 0);
> + }
> +
> + if (xev->evtype == XI_ButtonPress)
> + {
> + dpyinfo->grabbed |= (1 << xev->detail);
> + device->grab |= (1 << xev->detail);
> + dpyinfo->last_mouse_frame = f;
> + if (f && !tab_bar_p)
> + f->last_tab_bar_item = -1;
> +#if ! defined (USE_GTK)
> + if (f && !tool_bar_p)
> + f->last_tool_bar_item = -1;
> +#endif /* not USE_GTK */
> +
> + }
> + else
> + {
> + dpyinfo->grabbed &= ~(1 << xev->detail);
> + device->grab &= ~(1 << xev->detail);
> + }
> +
> + xi_grab_or_ungrab_device (device, dpyinfo, xev->event);
> +
> + if (f)
> + f->mouse_moved = false;
> +
> +#if defined (USE_GTK)
> + /* No Xt toolkit currently available has support for XI2.
> + So the code here assumes use of GTK. */
> + f = x_menubar_window_to_frame (dpyinfo, event);
> + if (f /* Gtk+ menus only react to the first three buttons. */
> + && xev->detail < 3)
> + {
> + /* What is done with Core Input ButtonPressed is not
> + possible here, because GenericEvents cannot be saved. */
> + bool was_waiting_for_input = waiting_for_input;
> + /* This hack was adopted from the NS port. Whether
> + or not it is actually safe is a different story
> + altogether. */
> + if (waiting_for_input)
> + waiting_for_input = 0;
> + set_frame_menubar (f, true);
> + waiting_for_input = was_waiting_for_input;
> + }
> +#endif
> + goto XI_OTHER;
> + }
> + case XI_KeyPress:
> + {
> + int state = xev->mods.base
> + | xev->mods.effective
> + | xev->mods.latched
> + | xev->mods.locked;
> + Lisp_Object c;
> +#ifdef HAVE_XKB
> + unsigned int mods_rtrn;
> +#endif
> + int keycode = xev->detail;
> + KeySym keysym;
> + char copy_buffer[81];
> + char *copy_bufptr = copy_buffer;
> + unsigned char *copy_ubufptr;
> +#ifdef HAVE_XKB
> + int copy_bufsiz = sizeof (copy_buffer);
> +#endif
> + ptrdiff_t i;
> + int nchars, len;
> +
> +#ifdef HAVE_XKB
> + if (dpyinfo->xkb_desc)
> + {
> + if (!XkbTranslateKeyCode (dpyinfo->xkb_desc, keycode,
> + state, &mods_rtrn, &keysym))
> + goto XI_OTHER;
> + }
> + else
> + {
> +#endif
> + int keysyms_per_keycode_return;
> + KeySym *ksms = XGetKeyboardMapping (dpyinfo->display, keycode, 1,
> + &keysyms_per_keycode_return);
> + if (!(keysym = ksms[0]))
> + {
> + XFree (ksms);
> + goto XI_OTHER;
> + }
> + XFree (ksms);
> +#ifdef HAVE_XKB
> + }
> +#endif
> +
> + if (keysym == NoSymbol)
> + goto XI_OTHER;
> +
> + x_display_set_last_user_time (dpyinfo, xev->time);
> + ignore_next_mouse_click_timeout = 0;
> +
> +#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
> + /* Dispatch XI_KeyPress events when in menu. */
> + if (popup_activated ())
> + goto XI_OTHER;
> +#endif
> +
> + f = x_any_window_to_frame (dpyinfo, xev->event);
> +
> + /* If mouse-highlight is an integer, input clears out
> + mouse highlighting. */
> + if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
> + && (f == 0
> +#if ! defined (USE_GTK)
> + || !EQ (f->tool_bar_window, hlinfo->mouse_face_window)
> +#endif
> + || !EQ (f->tab_bar_window, hlinfo->mouse_face_window))
> + )
> + {
> + clear_mouse_face (hlinfo);
> + hlinfo->mouse_face_hidden = true;
> + }
> +
> + if (f != 0)
> + {
> +#ifdef USE_GTK
> + /* Don't pass keys to GTK. A Tab will shift focus to the
> + tool bar in GTK 2.4. Keys will still go to menus and
> + dialogs because in that case popup_activated is nonzero
> + (see above). */
> + *finish = X_EVENT_DROP;
> +#endif
> + /* If not using XIM/XIC, and a compose sequence is in progress,
> + we break here. Otherwise, chars_matched is always 0. */
> + if (compose_status.chars_matched > 0 && nbytes == 0)
> + goto XI_OTHER;
> +
> + memset (&compose_status, 0, sizeof (compose_status));
> +
> + XSETFRAME (inev.ie.frame_or_window, f);
> + inev.ie.modifiers
> + = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), state);
> + inev.ie.timestamp = xev->time;
> +
> + /* First deal with keysyms which have defined
> + translations to characters. */
> + if (keysym >= 32 && keysym < 128)
> + /* Avoid explicitly decoding each ASCII character. */
> + {
> + inev.ie.kind = ASCII_KEYSTROKE_EVENT;
> + inev.ie.code = keysym;
> +
> + goto xi_done_keysym;
> + }
> +
> + /* Keysyms directly mapped to Unicode characters. */
> + if (keysym >= 0x01000000 && keysym <= 0x0110FFFF)
> + {
> + if (keysym < 0x01000080)
> + inev.ie.kind = ASCII_KEYSTROKE_EVENT;
> + else
> + inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
> + inev.ie.code = keysym & 0xFFFFFF;
> + goto xi_done_keysym;
> + }
> +
> + /* Now non-ASCII. */
> + if (HASH_TABLE_P (Vx_keysym_table)
> + && (c = Fgethash (make_fixnum (keysym),
> + Vx_keysym_table,
> + Qnil),
> + FIXNATP (c)))
> + {
> + inev.ie.kind = (SINGLE_BYTE_CHAR_P (XFIXNAT (c))
> + ? ASCII_KEYSTROKE_EVENT
> + : MULTIBYTE_CHAR_KEYSTROKE_EVENT);
> + inev.ie.code = XFIXNAT (c);
> + goto xi_done_keysym;
> + }
> +
> + /* Random non-modifier sorts of keysyms. */
> + if (((keysym >= XK_BackSpace && keysym <= XK_Escape)
> + || keysym == XK_Delete
> +#ifdef XK_ISO_Left_Tab
> + || (keysym >= XK_ISO_Left_Tab
> + && keysym <= XK_ISO_Enter)
> +#endif
> + || IsCursorKey (keysym) /* 0xff50 <= x < 0xff60 */
> + || IsMiscFunctionKey (keysym) /* 0xff60 <= x < VARIES */
> +#ifdef HPUX
> + /* This recognizes the "extended function
> + keys". It seems there's no cleaner way.
> + Test IsModifierKey to avoid handling
> + mode_switch incorrectly. */
> + || (XK_Select <= keysym && keysym < XK_KP_Space)
> +#endif
> +#ifdef XK_dead_circumflex
> + || keysym == XK_dead_circumflex
> +#endif
> +#ifdef XK_dead_grave
> + || keysym == XK_dead_grave
> +#endif
> +#ifdef XK_dead_tilde
> + || keysym == XK_dead_tilde
> +#endif
> +#ifdef XK_dead_diaeresis
> + || keysym == XK_dead_diaeresis
> +#endif
> +#ifdef XK_dead_macron
> + || keysym == XK_dead_macron
> +#endif
> +#ifdef XK_dead_degree
> + || keysym == XK_dead_degree
> +#endif
> +#ifdef XK_dead_acute
> + || keysym == XK_dead_acute
> +#endif
> +#ifdef XK_dead_cedilla
> + || keysym == XK_dead_cedilla
> +#endif
> +#ifdef XK_dead_breve
> + || keysym == XK_dead_breve
> +#endif
> +#ifdef XK_dead_ogonek
> + || keysym == XK_dead_ogonek
> +#endif
> +#ifdef XK_dead_caron
> + || keysym == XK_dead_caron
> +#endif
> +#ifdef XK_dead_doubleacute
> + || keysym == XK_dead_doubleacute
> +#endif
> +#ifdef XK_dead_abovedot
> + || keysym == XK_dead_abovedot
> +#endif
> + || IsKeypadKey (keysym) /* 0xff80 <= x < 0xffbe */
> + || IsFunctionKey (keysym) /* 0xffbe <= x < 0xffe1 */
> + /* Any "vendor-specific" key is ok. */
> + || (keysym & (1 << 28))
> + || (keysym != NoSymbol && nbytes == 0))
> + && ! (IsModifierKey (keysym)
> + /* The symbols from XK_ISO_Lock
> + to XK_ISO_Last_Group_Lock
> + don't have real modifiers but
> + should be treated similarly to
> + Mode_switch by Emacs. */
> +#if defined XK_ISO_Lock && defined XK_ISO_Last_Group_Lock
> + || (XK_ISO_Lock <= keysym
> + && keysym <= XK_ISO_Last_Group_Lock)
> +#endif
> + ))
> + {
> + STORE_KEYSYM_FOR_DEBUG (keysym);
> + /* make_lispy_event will convert this to a symbolic
> + key. */
> + inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
> + inev.ie.code = keysym;
> + goto xi_done_keysym;
> + }
> +
> +#ifdef HAVE_XKB
> + int overflow = 0;
> + KeySym sym = keysym;
> +
> + if (dpyinfo->xkb_desc)
> + {
> + if (!(nbytes = XkbTranslateKeySym (dpyinfo->display, &sym,
> + state & ~mods_rtrn, copy_bufptr,
> + copy_bufsiz, &overflow)))
> + goto XI_OTHER;
> + }
> + else
> +#else
> + {
> + block_input ();
> + char *str = XKeysymToString (keysym);
> + if (!str)
> + {
> + unblock_input ();
> + goto XI_OTHER;
> + }
> + nbytes = strlen (str) + 1;
> + copy_bufptr = alloca (nbytes);
> + strcpy (copy_bufptr, str);
> + unblock_input ();
> + }
> +#endif
> +#ifdef HAVE_XKB
> + if (overflow)
> + {
> + overflow = 0;
> + copy_bufptr = alloca (copy_bufsiz + overflow);
> + keysym = sym;
> + if (!(nbytes = XkbTranslateKeySym (dpyinfo->display, &sym,
> + state & ~mods_rtrn, copy_bufptr,
> + copy_bufsiz + overflow, &overflow)))
> + goto XI_OTHER;
> +
> + if (overflow)
> + goto XI_OTHER;
> + }
> +#endif
> +
> + for (i = 0, nchars = 0; i < nbytes; i++)
> + {
> + if (ASCII_CHAR_P (copy_bufptr[i]))
> + nchars++;
> + STORE_KEYSYM_FOR_DEBUG (copy_bufptr[i]);
> + }
> +
> + if (nchars < nbytes)
> + {
> + /* Decode the input data. */
> +
> + setup_coding_system (Vlocale_coding_system, &coding);
> + coding.src_multibyte = false;
> + coding.dst_multibyte = true;
> + /* The input is converted to events, thus we can't
> + handle composition. Anyway, there's no XIM that
> + gives us composition information. */
> + coding.common_flags &= ~CODING_ANNOTATION_MASK;
> +
> + SAFE_NALLOCA (coding.destination, MAX_MULTIBYTE_LENGTH,
> + nbytes);
> + coding.dst_bytes = MAX_MULTIBYTE_LENGTH * nbytes;
> + coding.mode |= CODING_MODE_LAST_BLOCK;
> + decode_coding_c_string (&coding, (unsigned char *) copy_bufptr, nbytes, Qnil);
> + nbytes = coding.produced;
> + nchars = coding.produced_char;
> + copy_bufptr = (char *) coding.destination;
> + }
> +
> + copy_ubufptr = (unsigned char *) copy_bufptr;
> +
> + /* Convert the input data to a sequence of
> + character events. */
> + for (i = 0; i < nbytes; i += len)
> + {
> + int ch;
> + if (nchars == nbytes)
> + ch = copy_ubufptr[i], len = 1;
> + else
> + ch = string_char_and_length (copy_ubufptr + i, &len);
> + inev.ie.kind = (SINGLE_BYTE_CHAR_P (ch)
> + ? ASCII_KEYSTROKE_EVENT
> + : MULTIBYTE_CHAR_KEYSTROKE_EVENT);
> + inev.ie.code = ch;
> + kbd_buffer_store_buffered_event (&inev, hold_quit);
> + }
> +
> + inev.ie.kind = NO_EVENT;
> + goto xi_done_keysym;
> + }
> + goto XI_OTHER;
> + }
> + case XI_KeyRelease:
> + x_display_set_last_user_time (dpyinfo, xev->time);
> + goto XI_OTHER;
> + case XI_PropertyEvent:
> + case XI_HierarchyChanged:
> + case XI_DeviceChanged:
> + x_init_master_valuators (dpyinfo);
> + goto XI_OTHER;
> + default:
> + goto XI_OTHER;
> + }
> + xi_done_keysym:
> + if (must_free_data)
> + XFreeEventData (dpyinfo->display, &event->xcookie);
> + goto done_keysym;
> + XI_OTHER:
> + if (must_free_data)
> + XFreeEventData (dpyinfo->display, &event->xcookie);
> + goto OTHER;
> + }
> +#endif
That's a damn large chunk of code to add into a function.
A lot of it is copy&pasted from other parts of `handle_one_xevent`.
Please avoid such code duplication (`handle_one_xevent` is already bad
enough from this point of view).
Also the code uses more than 80 columns at various places for no good
reason; that needs to be fixed.
Stefan
next prev parent reply other threads:[~2021-11-13 15:04 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <87v90wmh6m.fsf.ref@yahoo.com>
2021-11-13 11:24 ` XInput 2 support (again) Po Lu via Emacs development discussions.
2021-11-13 15:04 ` Stefan Monnier [this message]
2021-11-14 1:29 ` Po Lu via Emacs development discussions.
2021-11-14 15:29 ` Stefan Monnier
2021-11-14 16:12 ` Eli Zaretskii
2021-11-14 18:11 ` Stefan Monnier
2021-11-14 18:51 ` Eli Zaretskii
2021-11-15 0:26 ` Po Lu
2021-11-15 2:48 ` Stefan Monnier
2021-11-15 2:54 ` Po Lu
2021-11-15 3:18 ` Stefan Monnier
2021-11-15 4:50 ` Po Lu
2021-12-07 17:23 ` Possible XInput2 cursor bug ? (Was: Re: XInput 2 support (again)) Madhu
2021-12-08 1:27 ` Possible XInput2 cursor bug ? Po Lu
2021-12-08 2:33 ` Madhu
2021-12-08 2:39 ` Po Lu
2021-12-12 5:11 ` Madhu
2021-12-12 5:27 ` Po Lu
2021-12-12 6:02 ` Po Lu
2021-12-12 14:45 ` Madhu
2021-12-13 1:21 ` Po Lu
2021-12-13 4:18 ` Madhu
2021-12-12 7:26 ` Eli Zaretskii
2021-12-12 14:55 ` Madhu
2021-12-12 15:08 ` Eli Zaretskii
2021-12-12 18:15 ` Madhu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=jwv8rxs6r8m.fsf-monnier+emacs@gnu.org \
--to=monnier@iro.umontreal.ca \
--cc=emacs-devel@gnu.org \
--cc=luangruo@yahoo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.