]> git.eshelyaron.com Git - emacs.git/commitdiff
Report touch events on MS-Windows
authorPo Lu <luangruo@yahoo.com>
Sun, 9 Jun 2024 09:44:25 +0000 (17:44 +0800)
committerEshel Yaron <me@eshelyaron.com>
Mon, 10 Jun 2024 07:26:06 +0000 (09:26 +0200)
* etc/NEWS: Announce change.

* src/w32fns.c (RegisterTouchWindow_fn): New function.
(w32_createwindow): Assign a base value for touch event
identifiers to the frame, and register for touch input.
(w32_wnd_proc): Forward WM_TOUCH/WM_TOUCHMOVE messages to the
main thread.
(globals_of_w32fns): Load RegisterTouchWindow from user32.dll.

* src/w32term.c (w32_read_socket) <WM_TOUCH>: Detect WM_TOUCH
events, compare and record their touch points with and into the
frame's record of their prior state, and report the same to Lisp.
(pfnCloseTouchInputHandle, pfnGetTouchInputInfo): New variables.
(w32_initialize): Load the above functions from user32.dll.

* src/w32term.h (MAX_TOUCH_POINTS): New definition.
(struct w32_output) <touch_ids, touch_x, touch_y, touch_base>:
New fields.

(cherry picked from commit 5eb729c0b36a31869cf4928bc7bf5111b6f59ebb)

etc/NEWS
src/w32fns.c
src/w32term.c
src/w32term.h

index 383dbf96e8c2c92ad85b89482e5acf691b17ba2e..a01f45f53594881fd2d1bc26cd3f715e81e68406 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -546,11 +546,12 @@ function call.
 
 +++
 ** Emacs now has better support for touchscreen devices.
-On systems that understand them (at present X, Android, and PGTK), 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 (at present X, Android, PGTK, and
+MS-Windows), 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 8b61b54bdc57a3f378f070607f276138451b44cc..4437c1fb2b524e914ce8d783b85f335505d78a28 100644 (file)
@@ -216,6 +216,8 @@ typedef BOOL (WINAPI * WTSRegisterSessionNotification_Proc)
   (HWND hwnd, DWORD dwFlags);
 typedef BOOL (WINAPI * WTSUnRegisterSessionNotification_Proc) (HWND hwnd);
 
+typedef BOOL (WINAPI * RegisterTouchWindow_proc) (HWND, ULONG);
+
 TrackMouseEvent_Proc track_mouse_event_fn = NULL;
 ImmGetCompositionString_Proc get_composition_string_fn = NULL;
 ImmGetContext_Proc get_ime_context_fn = NULL;
@@ -234,6 +236,7 @@ SetWindowTheme_Proc SetWindowTheme_fn = NULL;
 DwmSetWindowAttribute_Proc DwmSetWindowAttribute_fn = NULL;
 WTSUnRegisterSessionNotification_Proc WTSUnRegisterSessionNotification_fn = NULL;
 WTSRegisterSessionNotification_Proc WTSRegisterSessionNotification_fn = NULL;
+RegisterTouchWindow_proc RegisterTouchWindow_fn = NULL;
 
 extern AppendMenuW_Proc unicode_append_menu;
 
@@ -2455,6 +2458,7 @@ w32_createwindow (struct frame *f, int *coords)
   RECT rect;
   int top, left;
   Lisp_Object border_width = Fcdr (Fassq (Qborder_width, f->param_alist));
+  static EMACS_INT touch_base;
 
   if (FRAME_PARENT_FRAME (f) && FRAME_W32_P (FRAME_PARENT_FRAME (f)))
     {
@@ -2517,6 +2521,8 @@ w32_createwindow (struct frame *f, int *coords)
 
   if (hwnd)
     {
+      int i;
+
       if (FRAME_SKIP_TASKBAR (f))
        SetWindowLong (hwnd, GWL_EXSTYLE,
                       GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_NOACTIVATE);
@@ -2545,6 +2551,20 @@ w32_createwindow (struct frame *f, int *coords)
           parent.  */
        MapWindowPoints (HWND_DESKTOP, parent_hwnd, (LPPOINT) &rect, 2);
 
+      /* Enable touch-screen input.  */
+      if (RegisterTouchWindow_fn)
+       (*RegisterTouchWindow_fn) (hwnd, 0);
+
+      /* Reset F's touch point array.  */
+      for (i = 0; i < ARRAYELTS (f->output_data.w32->touch_ids); ++i)
+       f->output_data.w32->touch_ids[i] = -1;
+
+      /* Assign an offset for touch points reported to F.  */
+      if (FIXNUM_OVERFLOW_P (touch_base + MAX_TOUCH_POINTS - 1))
+       touch_base = 0;
+      f->output_data.w32->touch_base = touch_base;
+      touch_base += MAX_TOUCH_POINTS;
+
       f->left_pos = rect.left;
       f->top_pos = rect.top;
     }
@@ -5370,6 +5390,19 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
       goto dflt;
 
+#ifdef WM_TOUCHMOVE
+    case WM_TOUCHMOVE:
+#else /* not WM_TOUCHMOVE */
+#ifndef WM_TOUCH
+#define WM_TOUCH 576
+#endif /* WM_TOUCH */
+    case WM_TOUCH:
+#endif /* not WM_TOUCHMOVE */
+      my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
+      /* It is said that DefWindowProc will release the touch
+        information in the event.  */
+      return 0;
+
 #ifdef WINDOWSNT
     case WM_CREATE:
       setup_w32_kbdhook (hwnd);
@@ -11405,6 +11438,9 @@ globals_of_w32fns (void)
   system_parameters_info_w_fn = (SystemParametersInfoW_Proc)
     get_proc_addr (user32_lib, "SystemParametersInfoW");
 #endif
+  RegisterTouchWindow_fn
+    = (RegisterTouchWindow_proc) get_proc_addr (user32_lib,
+                                               "RegisterTouchWindow");
 
   {
     HMODULE imm32_lib = GetModuleHandle ("imm32.dll");
index 3ef6d0f11f14cdee51a10ac5964cef046ab41ebe..45f94ab76bdf5257027f0f5f5a36ba6cfa83bd1b 100644 (file)
@@ -120,6 +120,13 @@ BOOL (WINAPI *pfnSetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD);
 /* PlgBlt is available since Windows 2000.  */
 BOOL (WINAPI *pfnPlgBlt) (HDC, const POINT *, HDC, int, int, int, int, HBITMAP, int, int);
 
+/* Functions that extract data from touch-screen events.  */
+typedef BOOL (WINAPI * CloseTouchInputHandle_proc) (HANDLE);
+typedef BOOL (WINAPI * GetTouchInputInfo_proc) (HANDLE, UINT, PTOUCHINPUT, int);
+
+CloseTouchInputHandle_proc pfnCloseTouchInputHandle;
+GetTouchInputInfo_proc pfnGetTouchInputInfo;
+
 
 #ifndef LWA_ALPHA
 #define LWA_ALPHA 0x02
@@ -139,6 +146,35 @@ BOOL (WINAPI *pfnPlgBlt) (HDC, const POINT *, HDC, int, int, int, int, HBITMAP,
 #define SM_CYVIRTUALSCREEN 79
 #endif
 
+/* Define required types and constants on systems with older headers
+   lest they be absent.  */
+
+#if _WIN32_WINNT < 0x0601
+#define TOUCHEVENTF_DOWN                              0x0001
+#define TOUCHEVENTF_MOVE                              0x0002
+#define TOUCHEVENTF_UP                                0x0004
+
+#define TOUCHEVENTMASKF_CONTACTAREA                   0x0004
+#define TOUCHEVENTMASKF_EXTRAINFO                     0x0002
+#define TOUCHEVENTMASKF_TIMEFROMSYSTEM                0x0001
+
+#define WM_TOUCHMOVE                                     576
+
+typedef struct _TOUCHINPUT
+{
+  LONG x;
+  LONG y;
+  HANDLE hSource;
+  DWORD dwID;
+  DWORD dwFlags;
+  DWORD dwMask;
+  DWORD dwTime;
+  ULONG_PTR dwExtraInfo;
+  DWORD cxContact;
+  DWORD cyContact;
+} TOUCHINPUT, *PTOUCHINPUT;
+#endif /* _WIN32_WINNT < 0x0601 */
+
 /* The handle of the frame that currently owns the system caret.  */
 HWND w32_system_caret_hwnd;
 int w32_system_caret_height;
@@ -6056,6 +6092,137 @@ w32_read_socket (struct terminal *terminal,
          break;
 #endif
 
+#if 0
+         /* These messages existed in prerelease versions of Windows 7,
+            yet, though superseded by just WM_TOUCHMOVE (renamed
+            WM_TOUCH) in the release, are still defined by MinGW's
+            winuser.h.  */
+       case WM_TOUCHDOWN:
+       case WM_TOUCHUP:
+#endif /* 0 */
+#ifdef WM_TOUCHMOVE
+       case WM_TOUCHMOVE:
+#else /* not WM_TOUCHMOVE */
+       case WM_TOUCH:
+#endif /* not WM_TOUCHMOVE */
+         f = w32_window_to_frame (dpyinfo, msg.msg.hwnd);
+
+         /* WM_TOUCH should never be received when touch input
+            functions are unavailable.  */
+         if (!pfnGetTouchInputInfo)
+           break;
+
+         if (f)
+           {
+             TOUCHINPUT points[MAX_TOUCH_POINTS];
+             int i, x;
+
+             if ((*pfnGetTouchInputInfo) ((HANDLE) msg.msg.lParam,
+                                          MAX_TOUCH_POINTS,
+                                          points, sizeof (TOUCHINPUT)))
+               {
+                 bool movement_p = false;
+                 EMACS_INT base = FRAME_OUTPUT_DATA (f)->touch_base;
+
+                 /* Iterate over the list of touch points in the
+                    structure, and for each, enter or remove
+                    information into and from F->touch_ids, and
+                    generate events correspondingly.  */
+                 for (i = 0; i < MAX_TOUCH_POINTS; ++i)
+                   {
+                     if (!points[i].dwID)
+                       continue;
+
+                     /* Search for a slot in touch_ids that is either
+                        empty or matches dwID.  */
+                     for (x = 0; x < MAX_TOUCH_POINTS; x++)
+                       {
+                         if (FRAME_OUTPUT_DATA (f)->touch_ids[x] == -1
+                             || (FRAME_OUTPUT_DATA (f)->touch_ids[x]
+                                 == points[i].dwID))
+                           break;
+                       }
+                     if (x == MAX_TOUCH_POINTS)
+                       continue;
+
+                     if (points[i].dwFlags & TOUCHEVENTF_UP)
+                       {
+                         /* Clear the entry in touch_ids and report the
+                            change.  Unless, of course, the entry be
+                            empty.  */
+                         if (FRAME_OUTPUT_DATA (f)->touch_ids[x] == -1)
+                           continue;
+                         FRAME_OUTPUT_DATA (f)->touch_ids[x] = -1;
+
+                         inev.kind = TOUCHSCREEN_END_EVENT;
+                         inev.timestamp = msg.msg.time;
+                         XSETFRAME (inev.frame_or_window, f);
+                         XSETINT (inev.x, points[i].x);
+                         XSETINT (inev.y, points[i].y);
+                         XSETINT (inev.arg, x + base);
+                         kbd_buffer_store_event (&inev);
+                         EVENT_INIT (inev);
+                       }
+                     else if (points[i].dwFlags & TOUCHEVENTF_DOWN)
+                       {
+                         bool recorded_p
+                           = FRAME_OUTPUT_DATA (f)->touch_ids[x] != -1;
+
+                         /* Report and record (if not already recorded)
+                            the addition.  */
+                         FRAME_OUTPUT_DATA (f)->touch_ids[x] = points[i].dwID;
+                         FRAME_OUTPUT_DATA (f)->touch_x[x] = points[i].x;
+                         FRAME_OUTPUT_DATA (f)->touch_y[x] = points[i].y;
+
+                         if (recorded_p)
+                           movement_p = true;
+                         else
+                           {
+                             inev.kind = TOUCHSCREEN_BEGIN_EVENT;
+                             inev.timestamp = msg.msg.time;
+                             XSETFRAME (inev.frame_or_window, f);
+                             XSETINT (inev.x, points[i].x);
+                             XSETINT (inev.y, points[i].y);
+                             XSETINT (inev.arg, x + base);
+                             kbd_buffer_store_event (&inev);
+                             EVENT_INIT (inev);
+                           }
+                       }
+                     else
+                       {
+                         FRAME_OUTPUT_DATA (f)->touch_ids[x] = points[i].dwID;
+                         FRAME_OUTPUT_DATA (f)->touch_x[x] = points[i].x;
+                         FRAME_OUTPUT_DATA (f)->touch_y[x] = points[i].y;
+                         movement_p = true;
+                       }
+                   }
+
+                 /* Report updated positions of touchpoints if some
+                    changed.  */
+                 if (movement_p)
+                   {
+                     Lisp_Object arg;
+
+                     inev.kind = TOUCHSCREEN_UPDATE_EVENT;
+                     inev.timestamp = msg.msg.time;
+                     XSETFRAME (inev.frame_or_window, f);
+                     arg = Qnil;
+
+                     for (i = 0; i < MAX_TOUCH_POINTS; ++i)
+                       arg
+                         = Fcons (list3i (FRAME_OUTPUT_DATA (f)->touch_x[i],
+                                          FRAME_OUTPUT_DATA (f)->touch_y[i],
+                                          i + base),
+                                  arg);
+
+                     inev.arg = arg;
+                   }
+               }
+           }
+
+         (*CloseTouchInputHandle) ((HANDLE) msg.msg.lParam);
+         break;
+
        default:
          /* Check for messages registered at runtime.  */
          if (msg.msg.message == msh_mousewheel)
@@ -7893,12 +8060,13 @@ w32_initialize (void)
 #define LOAD_PROC(lib, fn) pfn##fn = (void *) GetProcAddress (lib, #fn)
 
     LOAD_PROC (user_lib, SetLayeredWindowAttributes);
+    LOAD_PROC (user_lib, CloseTouchInputHandle);
+    LOAD_PROC (user_lib, GetTouchInputInfo);
 
     /* PlgBlt is not available on Windows 9X.  */
     HMODULE hgdi = LoadLibrary ("gdi32.dll");
     if (hgdi)
       LOAD_PROC (hgdi, PlgBlt);
-
 #undef LOAD_PROC
 
     /* Ensure scrollbar handles are at least 5 pixels.  */
index 38eac4230dd5f73c4e847160763700f665263563..1cdb165d2eddc5aec4a05c4b07bbd9476e3c37f2 100644 (file)
@@ -434,6 +434,18 @@ struct w32_output
 
   /* Whether or not this frame should be double buffered.  */
   unsigned want_paint_buffer : 1;
+
+#define MAX_TOUCH_POINTS 10
+  /* Array of dwIDs of presently active touch points, or -1 when
+     unpopulated.  */
+  int touch_ids[MAX_TOUCH_POINTS];
+
+  /* X and Y coordinates of active touchpoints.  */
+  LONG touch_x[MAX_TOUCH_POINTS], touch_y[MAX_TOUCH_POINTS];
+
+  /* Base value for touch point identifiers registered by this
+     frame.  */
+  EMACS_INT touch_base;
 };
 
 extern struct w32_output w32term_display;