summaryrefslogtreecommitdiff
path: root/src/xterm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/xterm.c')
-rw-r--r--src/xterm.c1778
1 files changed, 1750 insertions, 28 deletions
diff --git a/src/xterm.c b/src/xterm.c
index 89885e0d889..7456b3b6beb 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/>. */
#include <X11/extensions/Xdbe.h>
#endif
+#ifdef HAVE_XINPUT2
+#include <X11/extensions/XInput2.h>
+#endif
+
/* Load sys/types.h if not already loaded.
In some systems loading it twice is suicidal. */
#ifndef makedev
@@ -223,9 +227,15 @@ static bool x_handle_net_wm_state (struct frame *, const XPropertyEvent *);
static void x_check_fullscreen (struct frame *);
static void x_check_expected_move (struct frame *, int, int);
static void x_sync_with_move (struct frame *, int, int, bool);
+#ifndef HAVE_XINPUT2
static int handle_one_xevent (struct x_display_info *,
const XEvent *, int *,
struct input_event *);
+#else
+static int handle_one_xevent (struct x_display_info *,
+ XEvent *, int *,
+ struct input_event *);
+#endif
#if ! (defined USE_X_TOOLKIT || defined USE_MOTIF) && defined USE_GTK
static int x_dispatch_event (XEvent *, Display *);
#endif
@@ -335,6 +345,336 @@ x_extension_initialize (struct x_display_info *dpyinfo)
dpyinfo->ext_codes = ext_codes;
}
+#endif /* HAVE_CAIRO */
+
+#ifdef HAVE_XINPUT2
+
+/* Free all XI2 devices on dpyinfo. */
+static void
+x_free_xi_devices (struct x_display_info *dpyinfo)
+{
+ struct xi_touch_point_t *tem, *last;
+
+ block_input ();
+
+ if (dpyinfo->num_devices)
+ {
+ for (int i = 0; i < dpyinfo->num_devices; ++i)
+ {
+ XIUngrabDevice (dpyinfo->display, dpyinfo->devices[i].device_id,
+ CurrentTime);
+ xfree (dpyinfo->devices[i].valuators);
+
+ tem = dpyinfo->devices[i].touchpoints;
+ while (tem)
+ {
+ last = tem;
+ tem = tem->next;
+ xfree (last);
+ }
+ }
+
+ xfree (dpyinfo->devices);
+ dpyinfo->devices = NULL;
+ dpyinfo->num_devices = 0;
+ }
+
+ unblock_input ();
+}
+
+/* The code below handles the tracking of scroll valuators on XInput
+ 2, in order to support scroll wheels that report information more
+ granular than a screen line.
+
+ On X, when the XInput 2 extension is being utilized, the states of
+ the mouse wheels in each axis are stored as absolute values inside
+ "valuators" attached to each mouse device. To obtain the delta of
+ the scroll wheel from a motion event (which is used to report that
+ some valuator has changed), it is necessary to iterate over every
+ valuator that changed, and compare its previous value to the
+ current value of the valuator.
+
+ Each individual valuator also has an "interval", which is the
+ amount you must divide that delta by in order to obtain a delta in
+ the terms of scroll units.
+
+ This delta however is still intermediate, to make driver
+ implementations easier. The XInput developers recommend (and most
+ programs use) the following algorithm to convert from scroll unit
+ deltas to pixel deltas:
+
+ pixels_scrolled = pow (window_height, 2.0 / 3.0) * delta; */
+
+/* Setup valuator tracking for XI2 master devices on
+ DPYINFO->display. */
+
+static void
+x_init_master_valuators (struct x_display_info *dpyinfo)
+{
+ int ndevices;
+ XIDeviceInfo *infos;
+
+ block_input ();
+ x_free_xi_devices (dpyinfo);
+ infos = XIQueryDevice (dpyinfo->display,
+ XIAllDevices,
+ &ndevices);
+
+ if (!ndevices)
+ {
+ XIFreeDeviceInfo (infos);
+ unblock_input ();
+ return;
+ }
+
+ int actual_devices = 0;
+ dpyinfo->devices = xmalloc (sizeof *dpyinfo->devices * ndevices);
+
+ for (int i = 0; i < ndevices; ++i)
+ {
+ XIDeviceInfo *device = &infos[i];
+
+ if (device->enabled)
+ {
+ int actual_valuator_count = 0;
+ struct xi_device_t *xi_device = &dpyinfo->devices[actual_devices++];
+ xi_device->device_id = device->deviceid;
+ xi_device->grab = 0;
+ xi_device->valuators =
+ xmalloc (sizeof *xi_device->valuators * device->num_classes);
+ xi_device->touchpoints = NULL;
+ xi_device->master_p = (device->use == XIMasterKeyboard
+ || device->use == XIMasterPointer);
+ xi_device->direct_p = false;
+
+ for (int c = 0; c < device->num_classes; ++c)
+ {
+ switch (device->classes[c]->type)
+ {
+#ifdef XIScrollClass /* XInput 2.1 */
+ case XIScrollClass:
+ {
+ XIScrollClassInfo *info =
+ (XIScrollClassInfo *) device->classes[c];
+ struct xi_scroll_valuator_t *valuator;
+
+ if (xi_device->master_p)
+ {
+ valuator = &xi_device->valuators[actual_valuator_count++];
+ valuator->horizontal
+ = (info->scroll_type == XIScrollTypeHorizontal);
+ valuator->invalid_p = true;
+ valuator->emacs_value = DBL_MIN;
+ valuator->increment = info->increment;
+ valuator->number = info->number;
+ }
+
+ break;
+ }
+#endif
+#ifdef XITouchClass /* XInput 2.2 */
+ case XITouchClass:
+ {
+ XITouchClassInfo *info;
+
+ info = (XITouchClassInfo *) device->classes[c];
+ xi_device->direct_p = info->mode == XIDirectTouch;
+ }
+#endif
+ default:
+ break;
+ }
+ }
+
+ xi_device->scroll_valuator_count = actual_valuator_count;
+ }
+ }
+
+ dpyinfo->num_devices = actual_devices;
+ XIFreeDeviceInfo (infos);
+ unblock_input ();
+}
+
+/* Return the delta of the scroll valuator VALUATOR_NUMBER under
+ DEVICE_ID in the display DPYINFO with VALUE. The valuator's
+ valuator will be set to VALUE afterwards. In case no scroll
+ valuator is found, or if device_id is not known to Emacs, DBL_MAX
+ is returned. Otherwise, the valuator is returned in
+ VALUATOR_RETURN. */
+static double
+x_get_scroll_valuator_delta (struct x_display_info *dpyinfo, int device_id,
+ int valuator_number, double value,
+ struct xi_scroll_valuator_t **valuator_return)
+{
+ block_input ();
+
+ for (int i = 0; i < dpyinfo->num_devices; ++i)
+ {
+ struct xi_device_t *device = &dpyinfo->devices[i];
+
+ if (device->device_id == device_id && device->master_p)
+ {
+ for (int j = 0; j < device->scroll_valuator_count; ++j)
+ {
+ struct xi_scroll_valuator_t *sv = &device->valuators[j];
+
+ if (sv->number == valuator_number)
+ {
+ if (sv->invalid_p)
+ {
+ sv->current_value = value;
+ sv->invalid_p = false;
+ *valuator_return = sv;
+
+ unblock_input ();
+ return 0.0;
+ }
+ else
+ {
+ double delta = (sv->current_value - value) / sv->increment;
+ sv->current_value = value;
+ *valuator_return = sv;
+
+ unblock_input ();
+ return delta;
+ }
+ }
+ }
+
+ unblock_input ();
+ return DBL_MAX;
+ }
+ }
+
+ unblock_input ();
+ return DBL_MAX;
+}
+
+static struct xi_device_t *
+xi_device_from_id (struct x_display_info *dpyinfo, int deviceid)
+{
+ for (int i = 0; i < dpyinfo->num_devices; ++i)
+ {
+ if (dpyinfo->devices[i].device_id == deviceid)
+ return &dpyinfo->devices[i];
+ }
+
+ return NULL;
+}
+
+#ifdef XI_TouchBegin
+
+static void
+xi_link_touch_point (struct xi_device_t *device,
+ int detail, double x, double y)
+{
+ struct xi_touch_point_t *touchpoint;
+
+ touchpoint = xmalloc (sizeof *touchpoint);
+ touchpoint->next = device->touchpoints;
+ touchpoint->x = x;
+ touchpoint->y = y;
+ touchpoint->number = detail;
+
+ device->touchpoints = touchpoint;
+}
+
+static bool
+xi_unlink_touch_point (int detail,
+ struct xi_device_t *device)
+{
+ struct xi_touch_point_t *last, *tem;
+
+ for (last = NULL, tem = device->touchpoints; tem;
+ last = tem, tem = tem->next)
+ {
+ if (tem->number == detail)
+ {
+ if (!last)
+ device->touchpoints = tem->next;
+ else
+ last->next = tem->next;
+
+ xfree (tem);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static struct xi_touch_point_t *
+xi_find_touch_point (struct xi_device_t *device, int detail)
+{
+ struct xi_touch_point_t *point;
+
+ for (point = device->touchpoints; point; point = point->next)
+ {
+ if (point->number == detail)
+ return point;
+ }
+
+ return NULL;
+}
+
+#endif /* XI_TouchBegin */
+
+static void
+xi_grab_or_ungrab_device (struct xi_device_t *device,
+ struct x_display_info *dpyinfo,
+ Window window)
+{
+ XIEventMask mask;
+ ptrdiff_t l = XIMaskLen (XI_LASTEVENT);
+ unsigned char *m;
+ mask.mask = m = alloca (l);
+ memset (m, 0, l);
+ mask.mask_len = l;
+
+ XISetMask (m, XI_ButtonPress);
+ XISetMask (m, XI_ButtonRelease);
+ XISetMask (m, XI_Motion);
+ XISetMask (m, XI_Enter);
+ XISetMask (m, XI_Leave);
+
+ if (device->grab)
+ {
+ XIGrabDevice (dpyinfo->display, device->device_id, window,
+ CurrentTime, None, GrabModeAsync,
+ GrabModeAsync, True, &mask);
+ }
+ else
+ {
+ XIUngrabDevice (dpyinfo->display, device->device_id, CurrentTime);
+ }
+}
+
+static void
+xi_reset_scroll_valuators_for_device_id (struct x_display_info *dpyinfo, int id)
+{
+ struct xi_device_t *device = xi_device_from_id (dpyinfo, id);
+ struct xi_scroll_valuator_t *valuator;
+
+ if (!device || !device->master_p)
+ return;
+
+ if (!device->scroll_valuator_count)
+ return;
+
+ for (int i = 0; i < device->scroll_valuator_count; ++i)
+ {
+ valuator = &device->valuators[i];
+ valuator->invalid_p = true;
+ valuator->emacs_value = 0.0;
+ }
+
+ return;
+}
+
+#endif
+
+#ifdef USE_CAIRO
+
void
x_cr_destroy_frame_context (struct frame *f)
{
@@ -1563,22 +1903,6 @@ x_set_cursor_gc (struct glyph_string *s)
static void
x_set_mouse_face_gc (struct glyph_string *s)
{
- int face_id;
- struct face *face;
-
- /* What face has to be used last for the mouse face? */
- face_id = MOUSE_HL_INFO (s->f)->mouse_face_face_id;
- face = FACE_FROM_ID_OR_NULL (s->f, face_id);
- if (face == NULL)
- face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-
- if (s->first_glyph->type == CHAR_GLYPH)
- face_id = FACE_FOR_CHAR (s->f, face, s->first_glyph->u.ch, -1, Qnil);
- else
- face_id = FACE_FOR_CHAR (s->f, face, 0, -1, Qnil);
- s->face = FACE_FROM_ID (s->f, face_id);
- prepare_face_for_display (s->f, s->face);
-
if (s->font == s->face->font)
s->gc = s->face->gc;
else
@@ -3806,6 +4130,10 @@ x_draw_glyph_string (struct glyph_string *s)
if (!s->for_overlaps)
{
+ /* Draw relief if not yet drawn. */
+ if (!relief_drawn_p && s->face->box != FACE_NO_BOX)
+ x_draw_glyph_string_box (s);
+
/* Draw underline. */
if (s->face->underline)
{
@@ -3961,10 +4289,6 @@ x_draw_glyph_string (struct glyph_string *s)
}
}
- /* Draw relief if not yet drawn. */
- if (!relief_drawn_p && s->face->box != FACE_NO_BOX)
- x_draw_glyph_string_box (s);
-
if (s->prev)
{
struct glyph_string *prev;
@@ -4142,6 +4466,8 @@ x_show_hourglass (struct frame *f)
XMapRaised (dpy, x->hourglass_window);
XFlush (dpy);
+ /* Ensure that the spinning hourglass is shown. */
+ flush_frame (f);
}
}
}
@@ -4405,6 +4731,99 @@ x_scroll_run (struct window *w, struct run *run)
/* Cursor off. Will be switched on again in gui_update_window_end. */
gui_clear_cursor (w);
+#ifdef HAVE_XWIDGETS
+ /* "Copy" xwidget windows in the area that will be scrolled. */
+ Display *dpy = FRAME_X_DISPLAY (f);
+ Window window = FRAME_X_WINDOW (f);
+
+ Window root, parent, *children;
+ unsigned int nchildren;
+
+ if (XQueryTree (dpy, window, &root, &parent, &children, &nchildren))
+ {
+ /* Now find xwidget views situated between from_y and to_y, and
+ attached to w. */
+ for (unsigned int i = 0; i < nchildren; ++i)
+ {
+ Window child = children[i];
+ struct xwidget_view *view = xwidget_view_from_window (child);
+
+ if (view && !view->hidden)
+ {
+ int window_y = view->y + view->clip_top;
+ int window_height = view->clip_bottom - view->clip_top;
+
+ Emacs_Rectangle r1, r2, result;
+ r1.x = w->pixel_left;
+ r1.y = from_y;
+ r1.width = w->pixel_width;
+ r1.height = height;
+ r2 = r1;
+ r2.y = window_y;
+ r2.height = window_height;
+
+ /* The window is offscreen, just unmap it. */
+ if (window_height == 0)
+ {
+ view->hidden = true;
+ XUnmapWindow (dpy, child);
+ continue;
+ }
+
+ bool intersects_p =
+ gui_intersect_rectangles (&r1, &r2, &result);
+
+ if (XWINDOW (view->w) == w && intersects_p)
+ {
+ int y = view->y + (to_y - from_y);
+ int text_area_x, text_area_y, text_area_width, text_area_height;
+ int clip_top, clip_bottom;
+
+ window_box (w, view->area, &text_area_x, &text_area_y,
+ &text_area_width, &text_area_height);
+
+ view->y = y;
+
+ clip_top = 0;
+ clip_bottom = XXWIDGET (view->model)->height;
+
+ if (y < text_area_y)
+ clip_top = text_area_y - y;
+
+ if ((y + clip_bottom) > (text_area_y + text_area_height))
+ {
+ clip_bottom -= (y + clip_bottom) - (text_area_y + text_area_height);
+ }
+
+ view->clip_top = clip_top;
+ view->clip_bottom = clip_bottom;
+
+ /* This means the view has moved offscreen. Unmap
+ it and hide it here. */
+ if ((view->clip_bottom - view->clip_top) <= 0)
+ {
+ view->hidden = true;
+ XUnmapWindow (dpy, child);
+ }
+ else
+ {
+ XMoveResizeWindow (dpy, child, view->x + view->clip_left,
+ view->y + view->clip_top,
+ view->clip_right - view->clip_left,
+ view->clip_bottom - view->clip_top);
+ cairo_xlib_surface_set_size (view->cr_surface,
+ view->clip_right - view->clip_left,
+ view->clip_bottom - view->clip_top);
+ }
+ xwidget_expose (view);
+ XFlush (dpy);
+ }
+ }
+ }
+ XFree (children);
+ }
+#endif
+
#ifdef USE_CAIRO
if (FRAME_CR_CONTEXT (f))
{
@@ -4578,8 +4997,9 @@ x_focus_changed (int type, int state, struct x_display_info *dpyinfo, struct fra
}
}
-/* Return the Emacs frame-object corresponding to an X window.
- It could be the frame's main window or an icon window. */
+/* Return the Emacs frame-object corresponding to an X window. It
+ could be the frame's main window, an icon window, or an xwidget
+ window. */
static struct frame *
x_window_to_frame (struct x_display_info *dpyinfo, int wdesc)
@@ -4590,6 +5010,13 @@ x_window_to_frame (struct x_display_info *dpyinfo, int wdesc)
if (wdesc == None)
return NULL;
+#ifdef HAVE_XWIDGETS
+ struct xwidget_view *xvw = xwidget_view_from_window (wdesc);
+
+ if (xvw && xvw->frame)
+ return xvw->frame;
+#endif
+
FOR_EACH_FRAME (tail, frame)
{
f = XFRAME (frame);
@@ -4681,7 +5108,16 @@ static struct frame *
x_menubar_window_to_frame (struct x_display_info *dpyinfo,
const XEvent *event)
{
- Window wdesc = event->xany.window;
+ Window wdesc;
+#ifdef HAVE_XINPUT2
+ if (event->type == GenericEvent
+ && dpyinfo->supports_xi2
+ && (event->xcookie.evtype == XI_ButtonPress
+ || event->xcookie.evtype == XI_ButtonRelease))
+ wdesc = ((XIDeviceEvent *) event->xcookie.data)->event;
+ else
+#endif
+ wdesc = event->xany.window;
Lisp_Object tail, frame;
struct frame *f;
struct x_output *x;
@@ -4784,6 +5220,37 @@ x_detect_focus_change (struct x_display_info *dpyinfo, struct frame *frame,
}
break;
+#ifdef HAVE_XINPUT2
+ case GenericEvent:
+ {
+ XIEvent *xi_event = (XIEvent *) event;
+
+ struct frame *focus_frame = dpyinfo->x_focus_event_frame;
+ int focus_state
+ = focus_frame ? focus_frame->output_data.x->focus_state : 0;
+
+#ifdef USE_GTK
+ if (xi_event->evtype == XI_FocusIn
+ || xi_event->evtype == XI_FocusOut)
+ x_focus_changed ((xi_event->evtype == XI_FocusIn
+ ? FocusIn : FocusOut),
+ FOCUS_EXPLICIT,
+ dpyinfo, frame, bufp);
+ else
+#endif
+ if ((xi_event->evtype == XI_Enter
+ || xi_event->evtype == XI_Leave)
+ && (((XIEnterEvent *) xi_event)->detail
+ != XINotifyInferior)
+ && !(focus_state & FOCUS_EXPLICIT))
+ x_focus_changed ((xi_event->evtype == XI_Enter
+ ? FocusIn : FocusOut),
+ FOCUS_IMPLICIT,
+ dpyinfo, frame, bufp);
+ break;
+ }
+#endif
+
case FocusIn:
case FocusOut:
/* Ignore transient focus events from hotkeys, window manager
@@ -5012,7 +5479,7 @@ x_x_to_emacs_modifiers (struct x_display_info *dpyinfo, int state)
| ((state & dpyinfo->hyper_mod_mask) ? mod_hyper : 0));
}
-static int
+int
x_emacs_to_x_modifiers (struct x_display_info *dpyinfo, intmax_t state)
{
EMACS_INT mod_ctrl = ctrl_modifier;
@@ -7888,7 +8355,11 @@ mouse_or_wdesc_frame (struct x_display_info *dpyinfo, int wdesc)
static int
handle_one_xevent (struct x_display_info *dpyinfo,
+#ifndef HAVE_XINPUT2
const XEvent *event,
+#else
+ XEvent *event,
+#endif
int *finish, struct input_event *hold_quit)
{
union buffered_input_event inev;
@@ -7914,7 +8385,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
inev.ie.kind = NO_EVENT;
inev.ie.arg = Qnil;
- any = x_any_window_to_frame (dpyinfo, event->xany.window);
+#ifdef HAVE_XINPUT2
+ if (event->type != GenericEvent)
+#endif
+ any = x_any_window_to_frame (dpyinfo, event->xany.window);
+#ifdef HAVE_XINPUT2
+ else
+ any = NULL;
+#endif
if (any && any->wait_event_type == event->type)
any->wait_event_type = 0; /* Indicates we got it. */
@@ -8226,6 +8704,18 @@ handle_one_xevent (struct x_display_info *dpyinfo,
case Expose:
f = x_window_to_frame (dpyinfo, event->xexpose.window);
+#ifdef HAVE_XWIDGETS
+ {
+ struct xwidget_view *xv =
+ xwidget_view_from_window (event->xexpose.window);
+
+ if (xv)
+ {
+ xwidget_expose (xv);
+ goto OTHER;
+ }
+ }
+#endif
if (f)
{
if (!FRAME_VISIBLE_P (f))
@@ -8381,6 +8871,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
goto OTHER;
case MapNotify:
+#if defined HAVE_XINPUT2 && defined HAVE_GTK3
+ if (xg_is_menu_window (dpyinfo->display, event->xmap.window))
+ popup_activated_flag = 1;
+#endif
/* We use x_top_window_to_frame because map events can
come for sub-windows and they don't mean that the
frame is visible. */
@@ -8806,6 +9300,31 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_display_set_last_user_time (dpyinfo, event->xcrossing.time);
x_detect_focus_change (dpyinfo, any, event, &inev.ie);
+#ifdef HAVE_XWIDGETS
+ {
+ struct xwidget_view *xvw = xwidget_view_from_window (event->xcrossing.window);
+ Mouse_HLInfo *hlinfo;
+
+ if (xvw)
+ {
+ xwidget_motion_or_crossing (xvw, event);
+ hlinfo = MOUSE_HL_INFO (xvw->frame);
+
+ if (xvw->frame == hlinfo->mouse_face_mouse_frame)
+ {
+ clear_mouse_face (hlinfo);
+ hlinfo->mouse_face_mouse_frame = 0;
+ }
+
+ if (any_help_event_p)
+ {
+ do_help = -1;
+ }
+ goto OTHER;
+ }
+ }
+#endif
+
f = any;
if (f && x_mouse_click_focus_ignore_position)
@@ -8849,6 +9368,17 @@ handle_one_xevent (struct x_display_info *dpyinfo,
goto OTHER;
case LeaveNotify:
+#ifdef HAVE_XWIDGETS
+ {
+ struct xwidget_view *xvw = xwidget_view_from_window (event->xcrossing.window);
+
+ if (xvw)
+ {
+ xwidget_motion_or_crossing (xvw, event);
+ goto OTHER;
+ }
+ }
+#endif
x_display_set_last_user_time (dpyinfo, event->xcrossing.time);
x_detect_focus_change (dpyinfo, any, event, &inev.ie);
@@ -8899,6 +9429,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (f && xg_event_is_for_scrollbar (f, event))
f = 0;
#endif
+#ifdef HAVE_XWIDGETS
+ struct xwidget_view *xvw = xwidget_view_from_window (event->xmotion.window);
+
+ if (xvw)
+ xwidget_motion_or_crossing (xvw, event);
+#endif
if (f)
{
/* Maybe generate a SELECT_WINDOW_EVENT for
@@ -9153,6 +9689,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
case ButtonRelease:
case ButtonPress:
{
+#ifdef HAVE_XWIDGETS
+ struct xwidget_view *xvw = xwidget_view_from_window (event->xmotion.window);
+
+ if (xvw)
+ {
+ xwidget_button (xvw, event->type == ButtonPress,
+ event->xbutton.x, event->xbutton.y,
+ event->xbutton.button, event->xbutton.state,
+ event->xbutton.time);
+
+ if (!EQ (selected_window, xvw->w) && (event->xbutton.button < 4))
+ {
+ inev.ie.kind = SELECT_WINDOW_EVENT;
+ inev.ie.frame_or_window = xvw->w;
+ }
+ goto OTHER;
+ }
+#endif
/* 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;
@@ -9352,6 +9906,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_find_modifier_meanings (dpyinfo);
FALLTHROUGH;
case MappingKeyboard:
+#ifdef HAVE_XKB
+ if (dpyinfo->xkb_desc)
+ XkbGetUpdatedMap (dpyinfo->display, XkbAllComponentsMask,
+ dpyinfo->xkb_desc);
+#endif
XRefreshKeyboardMapping ((XMappingEvent *) &event->xmapping);
}
goto OTHER;
@@ -9359,6 +9918,1107 @@ 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;
+#ifdef HAVE_XWIDGETS
+ bool any_stop_p = false;
+#endif /* HAVE_XWIDGETS */
+
+ /* 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);
+
+ /* One problem behind the design of XInput 2 scrolling is
+ that valuators are not unique to each window, but only
+ the window that has grabbed the valuator's device or
+ the window that the device's pointer is on top of can
+ receive motion events. There is also no way to
+ retrieve the value of a valuator outside of each motion
+ event.
+
+ As such, to prevent wildly inaccurate results when the
+ valuators have changed outside Emacs, we reset our
+ records of each valuator's value whenever the pointer
+ re-enters a frame after its valuators have potentially
+ been changed elsewhere. */
+ if (enter->detail != XINotifyInferior
+ && enter->mode != XINotifyPassiveUngrab
+ && enter->mode != XINotifyUngrab && any)
+ xi_reset_scroll_valuators_for_device_id (dpyinfo, enter->deviceid);
+
+ 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:
+ {
+ struct xi_device_t *device;
+
+ states = &xev->valuators;
+ values = states->values;
+ device = xi_device_from_id (dpyinfo, xev->deviceid);
+
+ if (!device || !device->master_p)
+ goto XI_OTHER;
+
+#ifdef XI_TouchBegin
+ if (xev->flags & XIPointerEmulated
+ && dpyinfo->xi2_version >= 2)
+ goto XI_OTHER;
+#endif
+
+ x_display_set_last_user_time (dpyinfo, xi_event->time);
+
+#ifdef HAVE_XWIDGETS
+ struct xwidget_view *xv = xwidget_view_from_window (xev->event);
+ double xv_total_x = 0.0;
+ double xv_total_y = 0.0;
+#endif
+
+ for (int i = 0; i < states->mask_len * 8; i++)
+ {
+ if (XIMaskIsSet (states->mask, i))
+ {
+ struct xi_scroll_valuator_t *val;
+ double delta, scroll_unit;
+ int scroll_height;
+ Lisp_Object window;
+
+
+ /* See the comment on top of
+ x_init_master_valuators for more details on how
+ scroll wheel movement is reported on XInput 2. */
+ delta = x_get_scroll_valuator_delta (dpyinfo, xev->deviceid,
+ i, *values, &val);
+
+ if (delta != DBL_MAX)
+ {
+#ifdef HAVE_XWIDGETS
+ if (xv)
+ {
+ if (val->horizontal)
+ xv_total_x += delta;
+ else
+ xv_total_y += delta;
+
+ found_valuator = true;
+
+ if (delta == 0.0)
+ any_stop_p = true;
+
+ continue;
+ }
+#endif
+ if (!f)
+ {
+ f = x_any_window_to_frame (dpyinfo, xev->event);
+
+ if (!f)
+ goto XI_OTHER;
+ }
+
+ found_valuator = true;
+
+ if (signbit (delta) != signbit (val->emacs_value))
+ val->emacs_value = 0;
+
+ val->emacs_value += delta;
+
+ if (mwheel_coalesce_scroll_events
+ && (fabs (val->emacs_value) < 1)
+ && (fabs (delta) > 0))
+ continue;
+
+ bool s = signbit (val->emacs_value);
+ inev.ie.kind = (fabs (delta) > 0
+ ? (val->horizontal
+ ? HORIZ_WHEEL_EVENT
+ : WHEEL_EVENT)
+ : TOUCH_END_EVENT);
+ inev.ie.timestamp = xev->time;
+
+ XSETINT (inev.ie.x, lrint (xev->event_x));
+ XSETINT (inev.ie.y, lrint (xev->event_y));
+ XSETFRAME (inev.ie.frame_or_window, f);
+
+ if (fabs (delta) > 0)
+ {
+ inev.ie.modifiers = !s ? up_modifier : down_modifier;
+ inev.ie.modifiers
+ |= x_x_to_emacs_modifiers (dpyinfo,
+ xev->mods.effective);
+ }
+
+ window = window_from_coordinates (f, xev->event_x,
+ xev->event_y, NULL,
+ false, false);
+
+ if (WINDOWP (window))
+ scroll_height = XWINDOW (window)->pixel_height;
+ else
+ /* EVENT_X and EVENT_Y can be outside the
+ frame if F holds the input grab, so fall
+ back to the height of the frame instead. */
+ scroll_height = FRAME_PIXEL_HEIGHT (f);
+
+ scroll_unit = pow (scroll_height, 2.0 / 3.0);
+
+ if (NUMBERP (Vx_scroll_event_delta_factor))
+ scroll_unit *= XFLOATINT (Vx_scroll_event_delta_factor);
+
+ if (fabs (delta) > 0)
+ {
+ if (val->horizontal)
+ {
+ inev.ie.arg
+ = list3 (Qnil,
+ make_float (val->emacs_value
+ * scroll_unit),
+ make_float (0));
+ }
+ else
+ {
+ inev.ie.arg = list3 (Qnil, make_float (0),
+ make_float (val->emacs_value
+ * scroll_unit));
+ }
+ }
+ else
+ {
+ inev.ie.arg = Qnil;
+ }
+
+ kbd_buffer_store_event_hold (&inev.ie, hold_quit);
+
+ val->emacs_value = 0;
+ }
+ values++;
+ }
+
+ inev.ie.kind = NO_EVENT;
+ }
+
+#ifdef HAVE_XWIDGETS
+ if (xv)
+ {
+ if (found_valuator)
+ xwidget_scroll (xv, xev->event_x, xev->event_y,
+ xv_total_x, xv_total_y, xev->mods.effective,
+ xev->time, any_stop_p);
+ else
+ xwidget_motion_notify (xv, xev->event_x, xev->event_y,
+ xev->mods.effective, xev->time);
+
+ goto XI_OTHER;
+ }
+#endif
+ if (found_valuator)
+ goto XI_OTHER;
+
+ ev.x = lrint (xev->event_x);
+ ev.y = lrint (xev->event_y);
+ ev.window = xev->event;
+ ev.time = xev->time;
+
+ 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;
+
+#ifdef XIPointerEmulated
+ /* Ignore emulated scroll events when XI2 native
+ scroll events are present. */
+ if (dpyinfo->xi2_version >= 1
+ && xev->detail >= 4
+ && xev->detail <= 8
+ && xev->flags & XIPointerEmulated)
+ {
+ *finish = X_EVENT_DROP;
+ goto XI_OTHER;
+ }
+#endif
+
+ device = xi_device_from_id (dpyinfo, xev->deviceid);
+
+ if (!device || !device->master_p)
+ goto XI_OTHER;
+
+ 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.effective;
+ bv.time = xev->time;
+
+ 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.effective;
+ 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;
+ int copy_bufsiz = sizeof (copy_buffer);
+ ptrdiff_t i;
+ int nchars, len;
+ struct xi_device_t *device;
+
+ device = xi_device_from_id (dpyinfo, xev->deviceid);
+
+ if (!device || !device->master_p)
+ goto XI_OTHER;
+
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
+ /* Dispatch XI_KeyPress events when in menu. */
+ if (popup_activated ())
+ goto XI_OTHER;
+#endif
+
+#ifdef HAVE_X_I18N
+ XKeyPressedEvent xkey;
+
+ memset (&xkey, 0, sizeof xkey);
+
+ xkey.type = KeyPress;
+ xkey.serial = xev->serial;
+ xkey.send_event = xev->send_event;
+ xkey.display = xev->display;
+ xkey.window = xev->event;
+ xkey.root = xev->root;
+ xkey.subwindow = xev->child;
+ xkey.time = xev->time;
+ xkey.state = xev->mods.effective;
+ xkey.keycode = xev->detail;
+ xkey.same_screen = True;
+
+ if (x_filter_event (dpyinfo, (XEvent *) &xkey))
+ {
+ *finish = X_EVENT_DROP;
+ goto XI_OTHER;
+ }
+#endif
+
+#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;
+
+ 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;
+
+#ifdef HAVE_X_I18N
+ if (FRAME_XIC (f))
+ {
+ Status status_return;
+ nbytes = XmbLookupString (FRAME_XIC (f),
+ &xkey, (char *) copy_bufptr,
+ copy_bufsiz, &keysym,
+ &status_return);
+
+ if (status_return == XBufferOverflow)
+ {
+ copy_bufsiz = nbytes + 1;
+ copy_bufptr = alloca (copy_bufsiz);
+ nbytes = XmbLookupString (FRAME_XIC (f),
+ &xkey, (char *) copy_bufptr,
+ copy_bufsiz, &keysym,
+ &status_return);
+ }
+
+ if (status_return == XLookupNone)
+ goto xi_done_keysym;
+ else if (status_return == XLookupChars)
+ {
+ keysym = NoSymbol;
+ state = 0;
+ }
+ else if (status_return != XLookupKeySym
+ && status_return != XLookupBoth)
+ emacs_abort ();
+ }
+ else
+#endif
+ {
+#ifdef HAVE_XKB
+ int overflow = 0;
+ KeySym sym = keysym;
+
+ if (dpyinfo->xkb_desc)
+ {
+ nbytes = XkbTranslateKeySym (dpyinfo->display, &sym,
+ state & ~mods_rtrn, copy_bufptr,
+ copy_bufsiz, &overflow);
+ if (overflow)
+ {
+ copy_bufptr = alloca ((copy_bufsiz += overflow)
+ * sizeof *copy_bufptr);
+ overflow = 0;
+ nbytes = XkbTranslateKeySym (dpyinfo->display, &sym,
+ state & ~mods_rtrn, copy_bufptr,
+ copy_bufsiz, &overflow);
+
+ if (overflow)
+ nbytes = 0;
+ }
+ }
+ else
+#endif
+ {
+ nbytes = XLookupString (&xkey, copy_bufptr,
+ copy_bufsiz, &keysym,
+ &compose_status);
+ }
+ }
+
+ /* 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;
+ }
+
+ 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);
+#ifdef HAVE_X_I18N
+ XKeyPressedEvent xkey;
+
+ memset (&xkey, 0, sizeof xkey);
+
+ xkey.type = KeyRelease;
+ xkey.serial = xev->serial;
+ xkey.send_event = xev->send_event;
+ xkey.display = xev->display;
+ xkey.window = xev->event;
+ xkey.root = xev->root;
+ xkey.subwindow = xev->child;
+ xkey.time = xev->time;
+ xkey.state = xev->mods.effective;
+ xkey.keycode = xev->detail;
+ xkey.same_screen = True;
+
+ x_filter_event (dpyinfo, (XEvent *) &xkey);
+#endif
+ goto XI_OTHER;
+ case XI_PropertyEvent:
+ case XI_HierarchyChanged:
+ case XI_DeviceChanged:
+ x_init_master_valuators (dpyinfo);
+ goto XI_OTHER;
+#ifdef XI_TouchBegin
+ case XI_TouchBegin:
+ {
+ struct xi_device_t *device;
+ device = xi_device_from_id (dpyinfo, xev->deviceid);
+
+ if (!device)
+ goto XI_OTHER;
+
+ if (xi_find_touch_point (device, xev->detail))
+ emacs_abort ();
+
+ f = x_any_window_to_frame (dpyinfo, xev->event);
+
+ if (f && device->direct_p)
+ {
+ x_catch_errors (dpyinfo->display);
+ XIAllowTouchEvents (dpyinfo->display, xev->deviceid,
+ xev->detail, xev->event, XIAcceptTouch);
+ if (!x_had_errors_p (dpyinfo->display))
+ {
+ xi_link_touch_point (device, xev->detail, xev->event_x,
+ xev->event_y);
+
+#ifdef HAVE_GTK3
+ if (FRAME_X_OUTPUT (f)->menubar_widget
+ && xg_event_is_for_menubar (f, event))
+ {
+ 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
+
+ inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT;
+ inev.ie.timestamp = xev->time;
+ XSETFRAME (inev.ie.frame_or_window, f);
+ XSETINT (inev.ie.x, lrint (xev->event_x));
+ XSETINT (inev.ie.y, lrint (xev->event_y));
+ XSETINT (inev.ie.arg, xev->detail);
+ }
+ x_uncatch_errors_after_check ();
+ }
+ else
+ {
+ x_catch_errors (dpyinfo->display);
+ XIAllowTouchEvents (dpyinfo->display, xev->deviceid,
+ xev->detail, xev->event, XIRejectTouch);
+ x_uncatch_errors ();
+ }
+
+ goto XI_OTHER;
+ }
+ case XI_TouchUpdate:
+ {
+ struct xi_device_t *device;
+ struct xi_touch_point_t *touchpoint;
+ Lisp_Object arg = Qnil;
+
+ device = xi_device_from_id (dpyinfo, xev->deviceid);
+
+ if (!device)
+ goto XI_OTHER;
+
+ touchpoint = xi_find_touch_point (device, xev->detail);
+
+ if (!touchpoint)
+ goto XI_OTHER;
+
+ touchpoint->x = xev->event_x;
+ touchpoint->y = xev->event_y;
+
+ f = x_any_window_to_frame (dpyinfo, xev->event);
+
+ if (f && device->direct_p)
+ {
+ inev.ie.kind = TOUCHSCREEN_UPDATE_EVENT;
+ inev.ie.timestamp = xev->time;
+ XSETFRAME (inev.ie.frame_or_window, f);
+
+ for (touchpoint = device->touchpoints;
+ touchpoint; touchpoint = touchpoint->next)
+ {
+ arg = Fcons (list3i (lrint (touchpoint->x),
+ lrint (touchpoint->y),
+ lrint (touchpoint->number)),
+ arg);
+ }
+
+ inev.ie.arg = arg;
+ }
+
+ goto XI_OTHER;
+ }
+ case XI_TouchEnd:
+ {
+ struct xi_device_t *device;
+ bool unlinked_p;
+
+ device = xi_device_from_id (dpyinfo, xev->deviceid);
+
+ if (!device)
+ goto XI_OTHER;
+
+ unlinked_p = xi_unlink_touch_point (xev->detail, device);
+
+ if (unlinked_p)
+ {
+ f = x_any_window_to_frame (dpyinfo, xev->event);
+
+ if (f && device->direct_p)
+ {
+ inev.ie.kind = TOUCHSCREEN_END_EVENT;
+ inev.ie.timestamp = xev->time;
+ XSETFRAME (inev.ie.frame_or_window, f);
+ XSETINT (inev.ie.x, lrint (xev->event_x));
+ XSETINT (inev.ie.y, lrint (xev->event_y));
+ XSETINT (inev.ie.arg, xev->detail);
+ }
+ }
+
+ goto XI_OTHER;
+ }
+#endif
+ default:
+ goto XI_OTHER;
+ }
+ xi_done_keysym:
+#ifdef HAVE_X_I18N
+ if (FRAME_XIC (f) && (FRAME_XIC_STYLE (f) & XIMStatusArea))
+ xic_set_statusarea (f);
+#endif
+ 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
default:
OTHER:
@@ -11564,6 +13224,13 @@ x_lower_frame (struct frame *f)
XFlush (FRAME_X_DISPLAY (f));
unblock_input ();
}
+#ifdef HAVE_XWIDGETS
+ /* Make sure any X windows owned by xwidget views of the parent
+ still display below the lowered frame. */
+
+ if (FRAME_PARENT_FRAME (f))
+ lower_frame_xwidget_views (FRAME_PARENT_FRAME (f));
+#endif
}
static void
@@ -12123,6 +13790,10 @@ x_free_frame_resources (struct frame *f)
xfree (f->shell_position);
#else /* !USE_X_TOOLKIT */
+#ifdef HAVE_XWIDGETS
+ kill_frame_xwidget_views (f);
+#endif
+
#ifdef USE_GTK
xg_free_frame_widgets (f);
#endif /* USE_GTK */
@@ -13029,6 +14700,40 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
dpyinfo->supports_xdbe = true;
#endif
+#ifdef HAVE_XINPUT2
+ dpyinfo->supports_xi2 = false;
+ int rc;
+ int major = 2;
+#ifdef XI_BarrierHit /* XInput 2.3 */
+ int minor = 3;
+#elif defined XI_TouchBegin /* XInput 2.2 */
+ int minor = 2;
+#elif defined XIScrollClass /* XInput 2.1 */
+ int minor = 1;
+#else /* Some old version of XI2 we're not interested in. */
+ int minor = 0;
+#endif
+ int fer, fee;
+
+ if (XQueryExtension (dpyinfo->display, "XInputExtension",
+ &dpyinfo->xi2_opcode, &fer, &fee))
+ {
+ rc = XIQueryVersion (dpyinfo->display, &major, &minor);
+ if (rc == Success)
+ {
+ dpyinfo->supports_xi2 = true;
+ x_init_master_valuators (dpyinfo);
+ }
+ }
+ dpyinfo->xi2_version = minor;
+#endif
+
+#ifdef HAVE_XKB
+ dpyinfo->xkb_desc = XkbGetMap (dpyinfo->display,
+ XkbAllComponentsMask,
+ XkbUseCoreKbd);
+#endif
+
#if defined USE_CAIRO || defined HAVE_XFT
{
/* If we are using Xft, the following precautions should be made:
@@ -13461,6 +15166,14 @@ x_delete_terminal (struct terminal *terminal)
XrmDestroyDatabase (dpyinfo->rdb);
#endif
+#ifdef HAVE_XKB
+ if (dpyinfo->xkb_desc)
+ XkbFreeKeyboard (dpyinfo->xkb_desc, XkbAllComponentsMask, True);
+#endif
+#ifdef HAVE_XINPUT2
+ if (dpyinfo->supports_xi2)
+ x_free_xi_devices (dpyinfo);
+#endif
#ifdef USE_GTK
xg_display_close (dpyinfo->display);
#else
@@ -13620,9 +15333,12 @@ x_initialize (void)
void
init_xterm (void)
{
- /* Emacs can handle only core input events, so make sure
- Gtk doesn't use Xinput or Xinput2 extensions. */
+#ifndef HAVE_XINPUT2
+ /* Emacs can handle only core input events when built without XI2
+ support, so make sure Gtk doesn't use Xinput or Xinput2
+ extensions. */
xputenv ("GDK_CORE_DEVICE_EVENTS=1");
+#endif
}
#endif
@@ -13679,7 +15395,7 @@ selected window or cursor position is preserved. */);
A value of nil means Emacs doesn't use toolkit scroll bars.
With the X Window system, the value is a symbol describing the
X toolkit. Possible values are: gtk, motif, xaw, or xaw3d.
-With MS Windows or Nextstep, the value is t. */);
+With MS Windows, Haiku windowing or Nextstep, the value is t. */);
#ifdef USE_TOOLKIT_SCROLL_BARS
#ifdef USE_MOTIF
Vx_toolkit_scroll_bars = intern_c_string ("motif");
@@ -13779,4 +15495,10 @@ gtk_window_move to set or store frame positions and disables some time
consuming frame position adjustments. In newer versions of GTK, Emacs
always uses gtk_window_move and ignores the value of this variable. */);
x_gtk_use_window_move = true;
+
+ DEFVAR_LISP ("x-scroll-event-delta-factor", Vx_scroll_event_delta_factor,
+ doc: /* A scale to apply to pixel deltas reported in scroll events.
+This option is only effective when Emacs is built with XInput 2
+support. */);
+ Vx_scroll_event_delta_factor = make_float (1.0);
}