Implement touch screen events on PGTK
* etc/NEWS: Better qualify entry for touch screen events. * lisp/loadup.el (featurep 'pgtk): Load touch-screen.el. * lisp/touch-screen.el: Revise list of systems where touch screen events are reported. * src/gtkutil.c (xg_create_frame_widgets): Request GDK_TOUCH_MASK. * src/pgtkfns.c (pgtk_frame_parm_handlers, tip_window): Pacify compiler warning. * src/pgtkterm.c (pgtk_free_frame_resources): Free touch points linked to this frame. (pgtk_link_touch_point, pgtk_unlink_touch_point) (pgtk_unlink_touch_points, pgtk_find_touch_point): New functions, ported from X. (touch_event_cb): New event callback. (pgtk_set_event_handler): Register `touch_event_cb' as handler for `touch-event'. (pgtk_delete_display): Free residual touch points on this display. * src/pgtkterm.h (struct pgtk_touch_point): New structure. (struct pgtk_display_info) <touchpoints>: New field.
This commit is contained in:
parent
b1692e23ed
commit
2b7056db42
7 changed files with 282 additions and 9 deletions
9
etc/NEWS
9
etc/NEWS
|
@ -520,10 +520,11 @@ function call.
|
|||
|
||||
+++
|
||||
** Emacs now has better support for touchscreen devices.
|
||||
Many touch screen gestures are now implemented and translated into
|
||||
mouse or gesture events, and support for tapping tool bar buttons and
|
||||
opening menus has been written. Countless packages, such as Dired and
|
||||
Custom have been adjusted to better understand touch screen input.
|
||||
On systems that understand them, many touch screen gestures are now
|
||||
implemented and translated into mouse or gesture events, and support for
|
||||
tapping tool bar buttons and opening menus has been written. Countless
|
||||
packages, such as Dired and Custom have been adjusted to better
|
||||
understand touch screen input.
|
||||
|
||||
---
|
||||
** On X, Emacs now supports input methods which perform "string conversion".
|
||||
|
|
|
@ -369,6 +369,7 @@
|
|||
(if (featurep 'pgtk)
|
||||
(progn
|
||||
(load "pgtk-dnd")
|
||||
(load "touch-screen")
|
||||
(load "term/common-win")
|
||||
(load "term/pgtk-win")))
|
||||
(if (fboundp 'x-create-frame)
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
;;; Commentary:
|
||||
|
||||
;; This file provides code to recognize simple touch screen gestures.
|
||||
;; It is used on X and Android, currently the only systems where Emacs
|
||||
;; supports touch input.
|
||||
;; It is used on X, PGTK, and Android, currently the only systems where
|
||||
;; Emacs supports touch input.
|
||||
;;
|
||||
;; See (elisp)Touchscreen Events for a description of the details of
|
||||
;; touch events.
|
||||
|
|
|
@ -1669,6 +1669,7 @@ xg_create_frame_widgets (struct frame *f)
|
|||
#ifdef HAVE_PGTK
|
||||
| GDK_SCROLL_MASK
|
||||
| GDK_SMOOTH_SCROLL_MASK
|
||||
| GDK_TOUCH_MASK
|
||||
#endif
|
||||
| GDK_VISIBILITY_NOTIFY_MASK);
|
||||
|
||||
|
|
|
@ -945,6 +945,7 @@ unless TYPE is `png'. */)
|
|||
return pgtk_cr_export_frames (frames, surface_type);
|
||||
}
|
||||
|
||||
extern frame_parm_handler pgtk_frame_parm_handlers[];
|
||||
frame_parm_handler pgtk_frame_parm_handlers[] =
|
||||
{
|
||||
gui_set_autoraise, /* generic OK */
|
||||
|
@ -2619,7 +2620,7 @@ static Lisp_Object tip_frame;
|
|||
|
||||
/* The window-system window corresponding to the frame of the
|
||||
currently visible tooltip. */
|
||||
GtkWidget *tip_window;
|
||||
static GtkWidget *tip_window;
|
||||
|
||||
/* A timer that hides or deletes the currently visible tooltip when it
|
||||
fires. */
|
||||
|
|
241
src/pgtkterm.c
241
src/pgtkterm.c
|
@ -450,6 +450,8 @@ pgtk_frame_raise_lower (struct frame *f, bool raise_flag)
|
|||
|
||||
/* Free X resources of frame F. */
|
||||
|
||||
static void pgtk_unlink_touch_points (struct frame *);
|
||||
|
||||
void
|
||||
pgtk_free_frame_resources (struct frame *f)
|
||||
{
|
||||
|
@ -462,6 +464,7 @@ pgtk_free_frame_resources (struct frame *f)
|
|||
|
||||
block_input ();
|
||||
|
||||
pgtk_unlink_touch_points (f);
|
||||
#ifdef HAVE_XWIDGETS
|
||||
kill_frame_xwidget_views (f);
|
||||
#endif
|
||||
|
@ -6524,6 +6527,230 @@ drag_drop (GtkWidget *widget, GdkDragContext *context,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Touch screen events. */
|
||||
|
||||
/* Record a touch sequence with the identifier DETAIL from the given
|
||||
FRAME on the specified DPYINFO. Round X and Y and record them as its
|
||||
current position, assign an identifier to the touch sequence suitable
|
||||
for reporting to Lisp, and return the same. */
|
||||
|
||||
static EMACS_INT
|
||||
pgtk_link_touch_point (struct pgtk_display_info *dpyinfo,
|
||||
GdkEventSequence *detail, gdouble x,
|
||||
gdouble y, struct frame *frame)
|
||||
{
|
||||
struct pgtk_touch_point *touchpoint;
|
||||
static EMACS_INT local_detail;
|
||||
|
||||
/* Assign an identifier suitable for reporting to Lisp. On builds
|
||||
with 64-bit Lisp_Object, this is largely a theoretical problem, but
|
||||
CARD32s easily overflow 32-bit systems, as they are not specific to
|
||||
X clients (e.g. Emacs) but grow uniformly across all of them. */
|
||||
|
||||
if (FIXNUM_OVERFLOW_P (local_detail))
|
||||
local_detail = 0;
|
||||
|
||||
touchpoint = xmalloc (sizeof *touchpoint);
|
||||
touchpoint->next = dpyinfo->touchpoints;
|
||||
touchpoint->x = lrint (x);
|
||||
touchpoint->y = lrint (y);
|
||||
touchpoint->number = detail;
|
||||
touchpoint->local_detail = local_detail++;
|
||||
touchpoint->frame = frame;
|
||||
dpyinfo->touchpoints = touchpoint;
|
||||
return touchpoint->local_detail;
|
||||
}
|
||||
|
||||
/* Free and remove the touch sequence with the identifier DETAIL.
|
||||
DPYINFO is the display in which the touch sequence should be
|
||||
recorded. If such a touch sequence exists, return its local
|
||||
identifier in *LOCAL_DETAIL.
|
||||
|
||||
Value is 0 if no touch sequence by that identifier exists inside
|
||||
DPYINFO, or 1 if a touch sequence has been found. */
|
||||
|
||||
static int
|
||||
pgtk_unlink_touch_point (GdkEventSequence *detail,
|
||||
struct pgtk_display_info *dpyinfo,
|
||||
EMACS_INT *local_detail)
|
||||
{
|
||||
struct pgtk_touch_point *last, *tem;
|
||||
|
||||
for (last = NULL, tem = dpyinfo->touchpoints; tem;
|
||||
last = tem, tem = tem->next)
|
||||
{
|
||||
if (tem->number == detail)
|
||||
{
|
||||
if (!last)
|
||||
dpyinfo->touchpoints = tem->next;
|
||||
else
|
||||
last->next = tem->next;
|
||||
|
||||
*local_detail = tem->local_detail;
|
||||
xfree (tem);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlink all touch points associated with the frame F. This is done
|
||||
upon destroying F's window (or its being destroyed), because touch
|
||||
point delivery after that point is undefined. */
|
||||
|
||||
static void
|
||||
pgtk_unlink_touch_points (struct frame *f)
|
||||
{
|
||||
struct pgtk_touch_point **next, *last;
|
||||
struct pgtk_display_info *dpyinfo;
|
||||
|
||||
/* Now unlink all touch points on F's display matching F. */
|
||||
|
||||
dpyinfo = FRAME_DISPLAY_INFO (f);
|
||||
for (next = &dpyinfo->touchpoints; (last = *next);)
|
||||
{
|
||||
if (last->frame == f)
|
||||
{
|
||||
*next = last->next;
|
||||
xfree (last);
|
||||
}
|
||||
else
|
||||
next = &last->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the data associated with a touch sequence DETAIL recorded by
|
||||
`pgtk_link_touch_point' from DPYINFO, or NULL if it can't be
|
||||
found. */
|
||||
|
||||
static struct pgtk_touch_point *
|
||||
pgtk_find_touch_point (struct pgtk_display_info *dpyinfo,
|
||||
GdkEventSequence *detail)
|
||||
{
|
||||
struct pgtk_touch_point *point;
|
||||
|
||||
for (point = dpyinfo->touchpoints; point; point = point->next)
|
||||
{
|
||||
if (point->number == detail)
|
||||
return point;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
touch_event_cb (GtkWidget *self, GdkEvent *event, gpointer user_data)
|
||||
{
|
||||
struct pgtk_display_info *dpyinfo;
|
||||
struct frame *f;
|
||||
EMACS_INT local_detail;
|
||||
union buffered_input_event inev;
|
||||
struct pgtk_touch_point *touchpoint;
|
||||
Lisp_Object arg = Qnil;
|
||||
int state;
|
||||
|
||||
EVENT_INIT (inev.ie);
|
||||
|
||||
f = pgtk_any_window_to_frame (gtk_widget_get_window (self));
|
||||
eassert (f);
|
||||
dpyinfo = FRAME_DISPLAY_INFO (f);
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_TOUCH_BEGIN:
|
||||
|
||||
/* Verify that no touch point with this identifier is already at
|
||||
large. */
|
||||
if (pgtk_find_touch_point (dpyinfo, event->touch.sequence))
|
||||
break;
|
||||
|
||||
/* Record this in the display structure. */
|
||||
local_detail = pgtk_link_touch_point (dpyinfo, event->touch.sequence,
|
||||
event->touch.x, event->touch.y,
|
||||
f);
|
||||
/* Generate the input event. */
|
||||
inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT;
|
||||
inev.ie.timestamp = event->touch.time;
|
||||
XSETFRAME (inev.ie.frame_or_window, f);
|
||||
XSETINT (inev.ie.x, lrint (event->touch.x));
|
||||
XSETINT (inev.ie.y, lrint (event->touch.y));
|
||||
XSETINT (inev.ie.arg, local_detail);
|
||||
break;
|
||||
|
||||
case GDK_TOUCH_UPDATE:
|
||||
touchpoint = pgtk_find_touch_point (dpyinfo,
|
||||
event->touch.sequence);
|
||||
|
||||
if (!touchpoint
|
||||
/* Don't send this event if nothing has changed
|
||||
either. */
|
||||
|| (touchpoint->x == lrint (event->touch.x)
|
||||
&& touchpoint->y == lrint (event->touch.y)))
|
||||
break;
|
||||
|
||||
/* Construct the input event. */
|
||||
touchpoint->x = lrint (event->touch.x);
|
||||
touchpoint->y = lrint (event->touch.y);
|
||||
inev.ie.kind = TOUCHSCREEN_UPDATE_EVENT;
|
||||
inev.ie.timestamp = event->touch.time;
|
||||
XSETFRAME (inev.ie.frame_or_window, f);
|
||||
|
||||
for (touchpoint = dpyinfo->touchpoints;
|
||||
touchpoint; touchpoint = touchpoint->next)
|
||||
{
|
||||
if (touchpoint->frame == f)
|
||||
arg = Fcons (list3i (touchpoint->x, touchpoint->y,
|
||||
touchpoint->local_detail),
|
||||
arg);
|
||||
}
|
||||
|
||||
inev.ie.arg = arg;
|
||||
break;
|
||||
|
||||
case GDK_TOUCH_END:
|
||||
case GDK_TOUCH_CANCEL:
|
||||
/* Remove this touch point's record, also establishing its
|
||||
existence. */
|
||||
state = pgtk_unlink_touch_point (event->touch.sequence,
|
||||
dpyinfo, &local_detail);
|
||||
/* If it did exist... */
|
||||
if (state)
|
||||
{
|
||||
/* ... generate a suitable event. */
|
||||
inev.ie.kind = TOUCHSCREEN_END_EVENT;
|
||||
inev.ie.timestamp = event->touch.time;
|
||||
inev.ie.modifiers = (event->type != GDK_TOUCH_END);
|
||||
|
||||
XSETFRAME (inev.ie.frame_or_window, f);
|
||||
XSETINT (inev.ie.x, lrint (event->touch.x));
|
||||
XSETINT (inev.ie.y, lrint (event->touch.y));
|
||||
XSETINT (inev.ie.arg, local_detail);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the above produced a workable event, report the name of the
|
||||
device that gave rise to it. */
|
||||
|
||||
if (inev.ie.kind != NO_EVENT)
|
||||
{
|
||||
inev.ie.device = pgtk_get_device_for_event (dpyinfo, event);
|
||||
evq_enqueue (&inev);
|
||||
}
|
||||
|
||||
return inev.ie.kind != NO_EVENT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Callbacks for sundries. */
|
||||
|
||||
static void
|
||||
pgtk_monitors_changed_cb (GdkScreen *screen, gpointer user_data)
|
||||
{
|
||||
|
@ -6540,6 +6767,8 @@ pgtk_monitors_changed_cb (GdkScreen *screen, gpointer user_data)
|
|||
|
||||
static gboolean pgtk_selection_event (GtkWidget *, GdkEvent *, gpointer);
|
||||
|
||||
|
||||
|
||||
void
|
||||
pgtk_set_event_handler (struct frame *f)
|
||||
{
|
||||
|
@ -6609,6 +6838,8 @@ pgtk_set_event_handler (struct frame *f)
|
|||
G_CALLBACK (pgtk_selection_event), NULL);
|
||||
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "selection-notify-event",
|
||||
G_CALLBACK (pgtk_selection_event), NULL);
|
||||
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "touch-event",
|
||||
G_CALLBACK (touch_event_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "event",
|
||||
G_CALLBACK (pgtk_handle_event), NULL);
|
||||
}
|
||||
|
@ -7028,6 +7259,7 @@ static void
|
|||
pgtk_delete_display (struct pgtk_display_info *dpyinfo)
|
||||
{
|
||||
struct terminal *t;
|
||||
struct pgtk_touch_point *last, *tem;
|
||||
|
||||
/* Close all frames and delete the generic struct terminal for this
|
||||
X display. */
|
||||
|
@ -7049,6 +7281,15 @@ pgtk_delete_display (struct pgtk_display_info *dpyinfo)
|
|||
tail->next = tail->next->next;
|
||||
}
|
||||
|
||||
/* Free remaining touchpoints. */
|
||||
tem = dpyinfo->touchpoints;
|
||||
while (tem)
|
||||
{
|
||||
last = tem;
|
||||
tem = tem->next;
|
||||
xfree (last);
|
||||
}
|
||||
|
||||
pgtk_free_devices (dpyinfo);
|
||||
xfree (dpyinfo);
|
||||
}
|
||||
|
|
|
@ -50,13 +50,38 @@ struct pgtk_bitmap_record
|
|||
|
||||
struct pgtk_device_t
|
||||
{
|
||||
/* Lisp name of the device. */
|
||||
Lisp_Object name;
|
||||
|
||||
/* Seat to which this device appertains. */
|
||||
GdkSeat *seat;
|
||||
|
||||
/* Pointer to this device's GdkDevice object. */
|
||||
GdkDevice *device;
|
||||
|
||||
Lisp_Object name;
|
||||
/* Next device in this chain. */
|
||||
struct pgtk_device_t *next;
|
||||
};
|
||||
|
||||
struct pgtk_touch_point
|
||||
{
|
||||
/* The detail code reported to Lisp. */
|
||||
EMACS_INT local_detail;
|
||||
|
||||
/* The frame associated with this touch point. */
|
||||
struct frame *frame;
|
||||
|
||||
/* The next touch point in this list. */
|
||||
struct pgtk_touch_point *next;
|
||||
|
||||
/* The touchpoint detail. This purports to be a pointer, but is a
|
||||
number. */
|
||||
GdkEventSequence *number;
|
||||
|
||||
/* The last known rounded X and Y positions of the touchpoint. */
|
||||
int x, y;
|
||||
};
|
||||
|
||||
#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
|
||||
#define ARGB_TO_ULONG(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
|
||||
|
||||
|
@ -131,10 +156,13 @@ struct pgtk_display_info
|
|||
/* This says how to access this display through GDK. */
|
||||
GdkDisplay *gdpy;
|
||||
|
||||
/* An alias defined to make porting X code easier. */
|
||||
/* An alias defined to facilitate porting X code. */
|
||||
GdkDisplay *display;
|
||||
};
|
||||
|
||||
/* List of active touch-points. */
|
||||
struct pgtk_touch_point *touchpoints;
|
||||
|
||||
/* This is a cons cell of the form (NAME . FONT-LIST-CACHE). */
|
||||
Lisp_Object name_list_element;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue