From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Stefan Monnier Newsgroups: gmane.emacs.devel Subject: Re: XInput 2 support (again) Date: Sat, 13 Nov 2021 10:04:04 -0500 Message-ID: References: <87v90wmh6m.fsf.ref@yahoo.com> <87v90wmh6m.fsf@yahoo.com> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="33528"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) Cc: Po Lu To: Po Lu via "Emacs development discussions." Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sat Nov 13 16:05:29 2021 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mluau-0008Tx-Cu for ged-emacs-devel@m.gmane-mx.org; Sat, 13 Nov 2021 16:05:28 +0100 Original-Received: from localhost ([::1]:36076 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mluat-0003nP-Am for ged-emacs-devel@m.gmane-mx.org; Sat, 13 Nov 2021 10:05:27 -0500 Original-Received: from eggs.gnu.org ([209.51.188.92]:44496) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mluZl-0002RZ-2J for emacs-devel@gnu.org; Sat, 13 Nov 2021 10:04:17 -0500 Original-Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]:3531) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mluZf-0007b2-Op for emacs-devel@gnu.org; Sat, 13 Nov 2021 10:04:16 -0500 Original-Received: from pmg1.iro.umontreal.ca (localhost.localdomain [127.0.0.1]) by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id 6E91E100355; Sat, 13 Nov 2021 10:04:09 -0500 (EST) Original-Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1]) by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id 3BCD710018A; Sat, 13 Nov 2021 10:04:05 -0500 (EST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca; s=mail; t=1636815845; bh=aGCqHL47l0C+iyj2yYXtvTfH4KAOAyTsGuILKiZfwck=; h=From:To:Cc:Subject:References:Date:In-Reply-To:From; b=OKCOcdxPioPE35NOIhp/NahhC4qBBGo1ypRnxRZNCjVDwMORvCvhiLF7uRkgk0xBY ERKbwFO0Hy1We9gRsWhCbugb3XcqepKAweDBH7Ro1u6+N+/91v/TOHQyMdF77pUd0Y ANFx1pzZPy/UOvVBPV5WVBDmfIqwbC83yhhxAz0OG/CU4+52z/dVp0PDBx/mn1S9Rf 0jwHaZxVRyN1DFrgVZuCYFMF0BryJeB3V7i6PmUrKhHNOxuXiB+t4ryPS80Cwoj5GQ /QcYy3OEd6d6TwKgCWFf1pL7+MUj68HuRWyMvvrrjqRBhzh28nlheDnSgEtvp9FnnL Wy5ASNnFk7S5Q== Original-Received: from ceviche (modemcable034.207-20-96.mc.videotron.ca [96.20.207.34]) by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id F056E120991; Sat, 13 Nov 2021 10:04:04 -0500 (EST) In-Reply-To: <87v90wmh6m.fsf@yahoo.com> (Po Lu via's message of "Sat, 13 Nov 2021 19:24:49 +0800") Received-SPF: pass client-ip=132.204.25.50; envelope-from=monnier@iro.umontreal.ca; helo=mailscanner.iro.umontreal.ca X-Spam_score_int: -42 X-Spam_score: -4.3 X-Spam_bar: ---- X-Spam_report: (-4.3 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:279325 Archived-At: > @@ -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