From e4eb62defa01588877049402c44e010f569508e2 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sun, 9 Jun 2024 17:44:25 +0800 Subject: [PATCH] Report touch events on MS-Windows * 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) : 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) : New fields. (cherry picked from commit 5eb729c0b36a31869cf4928bc7bf5111b6f59ebb) --- etc/NEWS | 11 ++-- src/w32fns.c | 36 +++++++++++ src/w32term.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++- src/w32term.h | 12 ++++ 4 files changed, 223 insertions(+), 6 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 383dbf96e8c..a01f45f5359 100644 --- 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". diff --git a/src/w32fns.c b/src/w32fns.c index 8b61b54bdc5..4437c1fb2b5 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -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"); diff --git a/src/w32term.c b/src/w32term.c index 3ef6d0f11f1..45f94ab76bd 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -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. */ diff --git a/src/w32term.h b/src/w32term.h index 38eac4230dd..1cdb165d2ed 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -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; -- 2.39.5