Improve xwidget event handling on XI2

* src/xterm.c (handle_one_xevent): Pass XI2 entry and leave
events to xwidgets and fix scroll valuator reset logic for
xwidgets.

* src/xwidget.c (xwidget_button_1): Stop ungrabbing all devices
XI2 builds.
(xwidget_motion_or_crossing): Learn to pass through XI2 crossing
events.
(x_draw_xwidget_glyph_string): Add crossing events to the XI2
event mask.
This commit is contained in:
Po Lu 2021-12-29 14:37:49 +08:00
parent 22a9a20513
commit 2b7d33e30b
2 changed files with 114 additions and 66 deletions

View file

@ -9928,9 +9928,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
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;
@ -9981,28 +9978,46 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_display_set_last_user_time (dpyinfo, xi_event->time);
x_detect_focus_change (dpyinfo, any, event, &inev.ie);
{
#ifdef HAVE_XWIDGETS
struct xwidget_view *xwidget_view = xwidget_view_from_window (enter->event);
#else
bool xwidget_view = false;
#endif
/* 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.
/* 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
/* See the comment under FocusIn in
`x_detect_focus_change'. The main relevant culprit
these days seems to be XFCE. */
&& enter->mode != XINotifyUngrab
&& any && enter->event == FRAME_X_WINDOW (any))
xi_reset_scroll_valuators_for_device_id (dpyinfo, enter->deviceid);
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
/* See the comment under FocusIn in
`x_detect_focus_change'. The main relevant culprit
these days seems to be XFCE. */
&& enter->mode != XINotifyUngrab
&& (xwidget_view
|| (any && enter->event == FRAME_X_WINDOW (any))))
xi_reset_scroll_valuators_for_device_id (dpyinfo, enter->deviceid);
#ifdef HAVE_XWIDGETS
if (xwidget_view)
{
*finish = X_EVENT_DROP;
xwidget_motion_or_crossing (xwidget_view, event);
goto XI_OTHER;
}
#endif
}
f = any;
@ -10026,6 +10041,21 @@ handle_one_xevent (struct x_display_info *dpyinfo,
ev.window = leave->event;
any = x_any_window_to_frame (dpyinfo, leave->event);
#ifdef HAVE_XWIDGETS
{
struct xwidget_view *xvw
= xwidget_view_from_window (leave->event);
if (xvw)
{
*finish = X_EVENT_DROP;
xwidget_motion_or_crossing (xvw, event);
goto XI_OTHER;
}
}
#endif
x_display_set_last_user_time (dpyinfo, xi_event->time);
x_detect_focus_change (dpyinfo, any, event, &inev.ie);
@ -10105,16 +10135,19 @@ handle_one_xevent (struct x_display_info *dpyinfo,
#ifdef HAVE_XWIDGETS
if (xv)
{
/* FIXME: figure out what in GTK is
causing interval values to jump by
>100 at the end of a touch sequence
when an xwidget gets a scroll event
where is_stop is TRUE. */
if (fabs (delta) > 100)
continue;
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
@ -10224,8 +10257,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (found_valuator)
xwidget_scroll (xv, xev->event_x, xev->event_y,
xv_total_x, xv_total_y, state,
xev->time, any_stop_p);
-xv_total_x, -xv_total_y, state,
xev->time, (xv_total_x == 0.0
&& xv_total_y == 0.0));
else
xwidget_motion_notify (xv, xev->event_x, xev->event_y,
state, xev->time);

View file

@ -1052,12 +1052,6 @@ xwidget_button_1 (struct xwidget_view *view,
GdkEvent *xg_event = gdk_event_new (down_p ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE);
struct xwidget *model = XXWIDGET (view->model);
GtkWidget *target;
#ifdef HAVE_XINPUT2
struct x_display_info *dpyinfo;
struct xi_device_t *xi_device;
GdkSeat *seat;
GdkDevice *device;
#endif
/* X and Y should be relative to the origin of view->wdesc. */
x += view->clip_left;
@ -1081,24 +1075,6 @@ xwidget_button_1 (struct xwidget_view *view,
xg_event->button.time = time;
xg_event->button.device = find_suitable_pointer (view->frame);
#ifdef HAVE_XINPUT2
dpyinfo = FRAME_DISPLAY_INFO (view->frame);
device = xg_event->button.device;
for (int idx = 0; idx < dpyinfo->num_devices; ++idx)
{
xi_device = &dpyinfo->devices[idx];
XIUngrabDevice (view->dpy, xi_device->device_id, CurrentTime);
}
if (device)
{
seat = gdk_device_get_seat (device);
gdk_seat_ungrab (seat);
}
#endif
gtk_main_do_event (xg_event);
gdk_event_free (xg_event);
}
@ -1260,24 +1236,47 @@ xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event)
int x;
int y;
GtkWidget *target;
#ifdef HAVE_XINPUT2
XIEnterEvent *xev = NULL;
#endif
if (NILP (model->buffer))
return;
xg_event = gdk_event_new (event->type == MotionNotify
? GDK_MOTION_NOTIFY
: (event->type == LeaveNotify
? GDK_LEAVE_NOTIFY
: GDK_ENTER_NOTIFY));
#ifdef HAVE_XINPUT2
if (event->type != GenericEvent)
#endif
{
xg_event = gdk_event_new (event->type == MotionNotify
? GDK_MOTION_NOTIFY
: (event->type == LeaveNotify
? GDK_LEAVE_NOTIFY
: GDK_ENTER_NOTIFY));
target = find_widget_at_pos (model->widgetwindow_osr,
(event->type == MotionNotify
? event->xmotion.x + view->clip_left
: event->xcrossing.x + view->clip_left),
(event->type == MotionNotify
? event->xmotion.y + view->clip_top
: event->xcrossing.y + view->clip_top),
&x, &y);
}
#ifdef HAVE_XINPUT2
else
{
eassert (event->xcookie.evtype == XI_Enter
|| event->xcookie.evtype == XI_Leave);
target = find_widget_at_pos (model->widgetwindow_osr,
(event->type == MotionNotify
? event->xmotion.x + view->clip_left
: event->xcrossing.x + view->clip_left),
(event->type == MotionNotify
? event->xmotion.y + view->clip_top
: event->xcrossing.y + view->clip_top),
&x, &y);
xev = (XIEnterEvent *) event->xcookie.data;
xg_event = gdk_event_new (event->type == XI_Enter
? GDK_ENTER_NOTIFY
: GDK_LEAVE_NOTIFY);
target = find_widget_at_pos (model->widgetwindow_osr,
lrint (xev->event_x + view->clip_left),
lrint (xev->event_y + view->clip_top),
&x, &y);
}
#endif
if (!target)
target = model->widget_osr;
@ -1297,6 +1296,18 @@ xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event)
xg_event->motion.state = event->xmotion.state;
xg_event->motion.device = find_suitable_pointer (view->frame);
}
#ifdef HAVE_XINPUT2
else if (event->type == GenericEvent)
{
xg_event->crossing.x = (gdouble) xev->event_x;
xg_event->crossing.y = (gdouble) xev->event_y;
xg_event->crossing.x_root = (gdouble) xev->root_x;
xg_event->crossing.y_root = (gdouble) xev->root_y;
xg_event->crossing.time = xev->time;
xg_event->crossing.focus = xev->focus;
gdk_event_set_device (xg_event, find_suitable_pointer (view->frame));
}
#endif
else
{
xg_event->crossing.detail = min (5, event->xcrossing.detail);
@ -1305,6 +1316,7 @@ xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event)
xg_event->crossing.y = y;
xg_event->crossing.x_root = event->xcrossing.x_root;
xg_event->crossing.y_root = event->xcrossing.y_root;
xg_event->crossing.focus = event->xcrossing.focus;
gdk_event_set_device (xg_event, find_suitable_pointer (view->frame));
}
@ -2108,6 +2120,8 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
XISetMask (m, XI_Motion);
XISetMask (m, XI_ButtonPress);
XISetMask (m, XI_ButtonRelease);
XISetMask (m, XI_Enter);
XISetMask (m, XI_Leave);
XISelectEvents (xv->dpy, xv->wdesc, &mask, 1);
}
#endif