]> git.eshelyaron.com Git - emacs.git/commitdiff
Implement touch screen events on PGTK
authorPo Lu <luangruo@yahoo.com>
Sat, 1 Jun 2024 07:41:54 +0000 (15:41 +0800)
committerEshel Yaron <me@eshelyaron.com>
Sat, 1 Jun 2024 17:04:59 +0000 (19:04 +0200)
* 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.

(cherry picked from commit 2b7056db424ab0f8bf9e96b5a3c6aa12a3debf48)

etc/NEWS
lisp/loadup.el
lisp/touch-screen.el
src/gtkutil.c
src/pgtkfns.c
src/pgtkterm.c
src/pgtkterm.h

index 2b28d3c12a5ce9c94be9a42e01ba8a53767ca8dc..b97f821931722215a56e31baba88b8ee4dfd5c1a 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -507,10 +507,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".
index b8844ea042c5448f08b1cbdb9779029c53d6cc4c..eea87d4b940677a33d140c05f455dbbcb545c518 100644 (file)
 (if (featurep 'pgtk)
     (progn
       (load "pgtk-dnd")
+      (load "touch-screen")
       (load "term/common-win")
       (load "term/pgtk-win")))
 (if (fboundp 'x-create-frame)
index 436b8d0954c8448240fdc4c58732e96d030cb754..b77f3a6e07da1b9a1c026388afbedce87136546b 100644 (file)
@@ -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.
index 7de8eba0aa198059bac4d15d2e54467235e80254..d57627f152fcbe622e2eaf4051fc59bfdf01dd8d 100644 (file)
@@ -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);
 
index 6a8efb6d0bfec99680d7e9aad9e03d4a4ca9a01c..bdc6c5836fa0a605dc376dc0a7a6b5463a4ef498 100644 (file)
@@ -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.  */
index 8d9a47b932f8b98b6c21087ed110b2194ff08352..886f115c391986298990c5c4c59d8222ee4f0c1c 100644 (file)
@@ -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;
 }
 
+\f
+
+/* 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;
+}
+
+\f
+
+/* 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);
 
+\f
+
 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);
 }
index 8072d9636915636bdce0dae20cdbb4747c0ad171..90ca2aa22d43579f72331f7f7045bd230bec148a 100644 (file)
@@ -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;