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) <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.
This commit is contained in:
Po Lu 2024-06-09 17:44:25 +08:00
parent dcd95d7a77
commit 5eb729c0b3
4 changed files with 223 additions and 6 deletions

View file

@ -559,11 +559,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".

View 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");

View 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. */

View 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;