Correctly handle touches on the tool bar

* src/xterm.c (xi_link_touch_point): New argument `frame'.  Set
new field `touchpoint->frame' to it.
(xi_unlink_touch_points): New function.
(xi_disable_devices): Clear the tool bar device on frames whose
tool bar device matches this field.
(handle_one_xevent): If an XI_TouchBegin event lands on the tool
bar, then simulate a pointer click.  Ignore future events from
that touchpoint from there onwards.
(x_make_frame_invisible, x_free_frame_resources): Unlink touch
points associated with the frame.

* src/xterm.h (struct xi_touch_point_t): New field `frame'.
(struct x_output): New fields for keeping track of tool bar
touches.
This commit is contained in:
Po Lu 2023-01-17 18:28:37 +08:00
parent bb383a5491
commit b9025c507a
2 changed files with 163 additions and 9 deletions

View file

@ -5750,7 +5750,8 @@ xi_device_from_id (struct x_display_info *dpyinfo, int deviceid)
static void
xi_link_touch_point (struct xi_device_t *device,
int detail, double x, double y)
int detail, double x, double y,
struct frame *frame)
{
struct xi_touch_point_t *touchpoint;
@ -5759,6 +5760,7 @@ xi_link_touch_point (struct xi_device_t *device,
touchpoint->x = x;
touchpoint->y = y;
touchpoint->number = detail;
touchpoint->frame = frame;
device->touchpoints = touchpoint;
}
@ -5787,6 +5789,36 @@ xi_unlink_touch_point (int detail,
return false;
}
/* Unlink all touch points associated with the frame F.
This is done upon unmapping or destroying F's window, because
touch point delivery after that point is undefined. */
static void
xi_unlink_touch_points (struct frame *f)
{
struct xi_device_t *device;
struct xi_touch_point_t **next, *last;
int i;
for (i = 0; i < FRAME_DISPLAY_INFO (f)->num_devices; ++i)
{
device = &FRAME_DISPLAY_INFO (f)->devices[i];
/* Now unlink all touch points on DEVICE matching F. */
for (next = &device->touchpoints; (last = *next);)
{
if (last->frame == f)
{
*next = last->next;
xfree (last);
}
else
next = &last->next;
}
}
}
static struct xi_touch_point_t *
xi_find_touch_point (struct xi_device_t *device, int detail)
{
@ -13535,6 +13567,10 @@ xi_disable_devices (struct x_display_info *dpyinfo,
#ifdef HAVE_XINPUT2_2
struct xi_touch_point_t *tem, *last;
#endif
#if defined HAVE_XINPUT2_2 && !defined HAVE_EXT_TOOL_BAR
struct x_output *output;
Lisp_Object tail, frame;
#endif
/* Don't pointlessly copy dpyinfo->devices if there are no devices
to disable. */
@ -13577,6 +13613,34 @@ xi_disable_devices (struct x_display_info *dpyinfo,
tem = tem->next;
xfree (last);
}
#ifndef HAVE_EXT_TOOL_BAR
/* Now look through each frame on DPYINFO. If it has an
outstanding tool bar press for this device, release
the tool bar. */
FOR_EACH_FRAME (tail, frame)
{
if (!FRAME_X_P (XFRAME (frame))
|| (FRAME_DISPLAY_INFO (XFRAME (frame))
!= dpyinfo))
continue;
output = FRAME_OUTPUT_DATA (XFRAME (frame));
if (output->tool_bar_touch_device
== dpyinfo->devices[i].device_id)
{
if (XFRAME (frame)->last_tool_bar_item != -1
&& WINDOWP (XFRAME (frame)->tool_bar_window))
handle_tool_bar_click (XFRAME (frame), 0, 0,
false, 0);
output->tool_bar_touch_device = 0;
}
}
#endif
#endif
goto out;
@ -24209,6 +24273,48 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
#endif
#ifndef HAVE_EXT_TOOL_BAR
/* Is this a touch from a direct touch device that is in
the tool-bar? */
if (device->direct_p
&& WINDOWP (f->tool_bar_window)
&& WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window)))
{
Lisp_Object window;
int x = xev->event_x;
int y = xev->event_y;
window = window_from_coordinates (f, x, y, 0, true, true);
/* Ignore button release events if the mouse
wasn't previously pressed on the tool bar.
We do this because otherwise selecting some
text with the mouse and then releasing it on
the tool bar doesn't stop selecting text,
since the tool bar eats the button up
event. */
tool_bar_p = EQ (window, f->tool_bar_window);
/* If this touch has started in the tool bar, do not
send it to Lisp. Instead, simulate a tool bar
click, releasing it once it goes away. */
if (tool_bar_p)
{
handle_tool_bar_click (f, x, y, true, 0);
/* Record the device and the touch ID on the
frame. That way, Emacs knows when to dismiss
the tool bar click later. */
FRAME_OUTPUT_DATA (f)->tool_bar_touch_device
= device->device_id;
FRAME_OUTPUT_DATA (f)->tool_bar_touch_id = xev->detail;
goto XI_OTHER;
}
}
#endif
if (!menu_bar_p && !tool_bar_p)
{
if (f && device->direct_p)
@ -24218,13 +24324,16 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_catch_errors (dpyinfo->display);
if (x_input_grab_touch_events)
XIAllowTouchEvents (dpyinfo->display, xev->deviceid,
xev->detail, xev->event, XIAcceptTouch);
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);
xi_link_touch_point (device, xev->detail,
xev->event_x,
xev->event_y, f);
inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT;
inev.ie.timestamp = xev->time;
@ -24299,10 +24408,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
for (touchpoint = device->touchpoints;
touchpoint; touchpoint = touchpoint->next)
{
arg = Fcons (list3i (lrint (touchpoint->x),
lrint (touchpoint->y),
lrint (touchpoint->number)),
arg);
if (touchpoint->frame == f)
arg = Fcons (list3i (lrint (touchpoint->x),
lrint (touchpoint->y),
lrint (touchpoint->number)),
arg);
}
if (source)
@ -24348,6 +24458,23 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
}
/* Now see if the touchpoint was previously on the tool bar.
If it was, release the tool bar. */
if (!f)
f = x_window_to_frame (dpyinfo, xev->event);
if (f && (FRAME_OUTPUT_DATA (f)->tool_bar_touch_id
== xev->detail))
{
if (f->last_tool_bar_item != -1)
handle_tool_bar_click (f, xev->event_x, xev->event_y,
false, 0);
/* Now clear the tool bar device. */
FRAME_OUTPUT_DATA (f)->tool_bar_touch_device = 0;
}
goto XI_OTHER;
}
@ -28453,6 +28580,11 @@ x_make_frame_invisible (struct frame *f)
block_input ();
#ifdef HAVE_XINPUT2_2
/* Remove any touch points associated with F. */
xi_unlink_touch_points (f);
#endif
/* Before unmapping the window, update the WM_SIZE_HINTS property to claim
that the current position of the window is user-specified, rather than
program-specified, so that when the window is mapped again, it will be
@ -28658,6 +28790,11 @@ x_free_frame_resources (struct frame *f)
xi_handle_delete_frame (dpyinfo, f);
#endif
#ifdef HAVE_XINPUT2_2
/* Remove any touch points associated with F. */
xi_unlink_touch_points (f);
#endif
/* If a display connection is dead, don't try sending more
commands to the X server. */
if (dpyinfo->display)

View file

@ -257,10 +257,17 @@ struct xi_scroll_valuator_t
struct xi_touch_point_t
{
/* The next touch point in this list. */
struct xi_touch_point_t *next;
/* The touchpoint detail. */
int number;
/* The last known X and Y position of the touchpoint. */
double x, y;
/* The frame associated with this touch point. */
struct frame *frame;
};
#endif
@ -1295,6 +1302,16 @@ struct x_output
VisibilityFullyObscured, but is set to something else in
handle_one_xevent. */
int visibility_state;
#ifdef HAVE_XINPUT2_2
/* The touch ID of the last touch point to have touched the tool
bar. */
int tool_bar_touch_id;
/* The device that last touched the tool bar. 0 if no device
touched the tool bar. */
int tool_bar_touch_device;
#endif
};
enum