Skip to content

Commit dabc93a

Browse files
committed
pen: Send virtual mouse and touch events for pen input.
Fixes #11948.
1 parent 169c8d5 commit dabc93a

File tree

8 files changed

+168
-32
lines changed

8 files changed

+168
-32
lines changed

include/SDL3/SDL_hints.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4185,6 +4185,36 @@ extern "C" {
41854185
*/
41864186
#define SDL_HINT_ASSERT "SDL_ASSERT"
41874187

4188+
/**
4189+
* A variable controlling whether pen events should generate synthetic mouse
4190+
* events.
4191+
*
4192+
* The variable can be set to the following values:
4193+
*
4194+
* - "0": Pen events will not generate mouse events.
4195+
* - "1": Pen events will generate mouse events. (default)
4196+
*
4197+
* This hint can be set anytime.
4198+
*
4199+
* \since This hint is available since SDL 3.2.0.
4200+
*/
4201+
#define SDL_HINT_PEN_MOUSE_EVENTS "SDL_PEN_MOUSE_EVENTS"
4202+
4203+
/**
4204+
* A variable controlling whether pen events should generate synthetic touch
4205+
* events.
4206+
*
4207+
* The variable can be set to the following values:
4208+
*
4209+
* - "0": Pen events will not generate touch events.
4210+
* - "1": Pen events will generate touch events. (default)
4211+
*
4212+
* This hint can be set anytime.
4213+
*
4214+
* \since This hint is available since SDL 3.2.0.
4215+
*/
4216+
#define SDL_HINT_PEN_TOUCH_EVENTS "SDL_PEN_TOUCH_EVENTS"
4217+
41884218

41894219
/**
41904220
* An enumeration of hint priorities.

include/SDL3/SDL_pen.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
#define SDL_pen_h_
4141

4242
#include <SDL3/SDL_stdinc.h>
43+
#include <SDL3/SDL_mouse.h>
44+
#include <SDL3/SDL_touch.h>
4345

4446
/* Set up for C function definitions, even when using C++ */
4547
#ifdef __cplusplus
@@ -59,6 +61,20 @@ extern "C" {
5961
*/
6062
typedef Uint32 SDL_PenID;
6163

64+
/**
65+
* The SDL_MouseID for mouse events simulated with pen input.
66+
*
67+
* \since This macro is available since SDL 3.1.3.
68+
*/
69+
#define SDL_PEN_MOUSEID ((SDL_MouseID)-2)
70+
71+
/**
72+
* The SDL_TouchID for touch events simulated with pen input.
73+
*
74+
* \since This macro is available since SDL 3.1.3.
75+
*/
76+
#define SDL_PEN_TOUCHID ((SDL_TouchID)-2)
77+
6278

6379
/**
6480
* Pen input flags, as reported by various pen events' `pen_state` field.

src/events/SDL_mouse.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,24 @@ static void SDLCALL SDL_MouseTouchEventsChanged(void *userdata, const char *name
173173
}
174174
}
175175

176+
static void SDLCALL SDL_PenMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
177+
{
178+
SDL_Mouse *mouse = (SDL_Mouse *)userdata;
179+
180+
mouse->pen_mouse_events = SDL_GetStringBoolean(hint, true);
181+
}
182+
183+
static void SDLCALL SDL_PenTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
184+
{
185+
SDL_Mouse *mouse = (SDL_Mouse *)userdata;
186+
187+
mouse->pen_touch_events = SDL_GetStringBoolean(hint, true);
188+
189+
if (mouse->pen_touch_events) {
190+
SDL_AddTouch(SDL_PEN_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "pen_input");
191+
}
192+
}
193+
176194
static void SDLCALL SDL_MouseAutoCaptureChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
177195
{
178196
SDL_Mouse *mouse = (SDL_Mouse *)userdata;
@@ -239,6 +257,12 @@ bool SDL_PreInitMouse(void)
239257
SDL_AddHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS,
240258
SDL_MouseTouchEventsChanged, mouse);
241259

260+
SDL_AddHintCallback(SDL_HINT_PEN_MOUSE_EVENTS,
261+
SDL_PenMouseEventsChanged, mouse);
262+
263+
SDL_AddHintCallback(SDL_HINT_PEN_TOUCH_EVENTS,
264+
SDL_PenTouchEventsChanged, mouse);
265+
242266
SDL_AddHintCallback(SDL_HINT_MOUSE_AUTO_CAPTURE,
243267
SDL_MouseAutoCaptureChanged, mouse);
244268

@@ -1043,6 +1067,12 @@ void SDL_QuitMouse(void)
10431067
SDL_RemoveHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS,
10441068
SDL_MouseTouchEventsChanged, mouse);
10451069

1070+
SDL_RemoveHintCallback(SDL_HINT_PEN_MOUSE_EVENTS,
1071+
SDL_PenMouseEventsChanged, mouse);
1072+
1073+
SDL_RemoveHintCallback(SDL_HINT_PEN_TOUCH_EVENTS,
1074+
SDL_PenTouchEventsChanged, mouse);
1075+
10461076
SDL_RemoveHintCallback(SDL_HINT_MOUSE_AUTO_CAPTURE,
10471077
SDL_MouseAutoCaptureChanged, mouse);
10481078

src/events/SDL_mouse_c.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ typedef struct
118118
int double_click_radius;
119119
bool touch_mouse_events;
120120
bool mouse_touch_events;
121+
bool pen_mouse_events;
122+
bool pen_touch_events;
121123
bool was_touch_mouse_events; // Was a touch-mouse event pending?
122124
#ifdef SDL_PLATFORM_VITA
123125
Uint8 vita_touch_mouse_device;

src/events/SDL_pen.c

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "SDL_events_c.h"
2727
#include "SDL_pen_c.h"
2828

29+
static SDL_PenID pen_touching = 0; // used for synthetic mouse/touch events.
30+
2931
typedef struct SDL_Pen
3032
{
3133
SDL_PenID instance_id;
@@ -111,6 +113,7 @@ void SDL_QuitPen(void)
111113
SDL_free(pen_devices);
112114
pen_devices = NULL;
113115
pen_device_count = 0;
116+
pen_touching = 0;
114117
}
115118

116119
#if 0 // not a public API at the moment.
@@ -309,7 +312,7 @@ void SDL_RemoveAllPenDevices(void (*callback)(SDL_PenID instance_id, void *handl
309312
SDL_UnlockRWLock(pen_device_rwlock);
310313
}
311314

312-
void SDL_SendPenTouch(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, bool eraser, bool down)
315+
void SDL_SendPenTouch(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window, bool eraser, bool down)
313316
{
314317
bool send_event = false;
315318
SDL_PenInputFlags input_state = 0;
@@ -363,10 +366,45 @@ void SDL_SendPenTouch(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window
363366
event.ptouch.down = down;
364367
SDL_PushEvent(&event);
365368
}
369+
370+
SDL_Mouse *mouse = SDL_GetMouse();
371+
if (mouse && window) {
372+
if (mouse->pen_mouse_events) {
373+
if (down) {
374+
if (!pen_touching) {
375+
SDL_SendMouseMotion(timestamp, window, SDL_PEN_MOUSEID, false, x, y);
376+
SDL_SendMouseButton(timestamp, window, SDL_PEN_MOUSEID, SDL_BUTTON_LEFT, true);
377+
}
378+
} else {
379+
if (pen_touching == instance_id) {
380+
SDL_SendMouseButton(timestamp, window, SDL_PEN_MOUSEID, SDL_BUTTON_LEFT, false);
381+
}
382+
}
383+
}
384+
385+
if (mouse->pen_touch_events) {
386+
const SDL_EventType touchtype = down ? SDL_EVENT_FINGER_DOWN : SDL_EVENT_FINGER_UP;
387+
const float normalized_x = x / (float)window->w;
388+
const float normalized_y = y / (float)window->h;
389+
if (!pen_touching || (pen_touching == instance_id)) {
390+
SDL_SendTouch(timestamp, SDL_PEN_TOUCHID, SDL_BUTTON_LEFT, window, touchtype, normalized_x, normalized_y, pen->axes[SDL_PEN_AXIS_PRESSURE]);
391+
}
392+
}
393+
}
394+
395+
if (down) {
396+
if (!pen_touching) {
397+
pen_touching = instance_id;
398+
}
399+
} else {
400+
if (pen_touching == instance_id) {
401+
pen_touching = 0;
402+
}
403+
}
366404
}
367405
}
368406

369-
void SDL_SendPenAxis(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, SDL_PenAxis axis, float value)
407+
void SDL_SendPenAxis(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window, SDL_PenAxis axis, float value)
370408
{
371409
SDL_assert((axis >= 0) && (axis < SDL_PEN_AXIS_COUNT)); // fix the backend if this triggers.
372410

@@ -405,10 +443,19 @@ void SDL_SendPenAxis(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *
405443
event.paxis.axis = axis;
406444
event.paxis.value = value;
407445
SDL_PushEvent(&event);
446+
447+
if (window && (axis == SDL_PEN_AXIS_PRESSURE) && (pen_touching == instance_id)) {
448+
SDL_Mouse *mouse = SDL_GetMouse();
449+
if (mouse && mouse->pen_touch_events) {
450+
const float normalized_x = x / (float)window->w;
451+
const float normalized_y = y / (float)window->h;
452+
SDL_SendTouchMotion(timestamp, SDL_PEN_TOUCHID, SDL_BUTTON_LEFT, window, normalized_x, normalized_y, value);
453+
}
454+
}
408455
}
409456
}
410457

411-
void SDL_SendPenMotion(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, float x, float y)
458+
void SDL_SendPenMotion(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window, float x, float y)
412459
{
413460
bool send_event = false;
414461
SDL_PenInputFlags input_state = 0;
@@ -440,10 +487,25 @@ void SDL_SendPenMotion(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window
440487
event.pmotion.x = x;
441488
event.pmotion.y = y;
442489
SDL_PushEvent(&event);
490+
491+
if (window && (pen_touching == instance_id)) {
492+
SDL_Mouse *mouse = SDL_GetMouse();
493+
if (mouse) {
494+
if (mouse->pen_mouse_events) {
495+
SDL_SendMouseMotion(timestamp, window, SDL_PEN_MOUSEID, false, x, y);
496+
}
497+
498+
if (mouse->pen_touch_events) {
499+
const float normalized_x = x / (float)window->w;
500+
const float normalized_y = y / (float)window->h;
501+
SDL_SendTouchMotion(timestamp, SDL_PEN_TOUCHID, SDL_BUTTON_LEFT, window, normalized_x, normalized_y, pen->axes[SDL_PEN_AXIS_PRESSURE]);
502+
}
503+
}
504+
}
443505
}
444506
}
445507

446-
void SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, Uint8 button, bool down)
508+
void SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window, Uint8 button, bool down)
447509
{
448510
bool send_event = false;
449511
SDL_PenInputFlags input_state = 0;
@@ -492,6 +554,13 @@ void SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window
492554
event.pbutton.button = button;
493555
event.pbutton.down = down;
494556
SDL_PushEvent(&event);
557+
558+
if (window && (pen_touching == instance_id)) {
559+
SDL_Mouse *mouse = SDL_GetMouse();
560+
if (mouse && mouse->pen_mouse_events) {
561+
SDL_SendMouseButton(timestamp, window, SDL_PEN_MOUSEID, button + 1, down);
562+
}
563+
}
495564
}
496565
}
497566
}

src/events/SDL_pen_c.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,16 @@ extern void SDL_RemovePenDevice(Uint64 timestamp, SDL_PenID instance_id);
6767
extern void SDL_RemoveAllPenDevices(void (*callback)(SDL_PenID instance_id, void *handle, void *userdata), void *userdata);
6868

6969
// Backend calls this when a pen's button changes, to generate events and update state.
70-
extern void SDL_SendPenTouch(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, bool eraser, bool down);
70+
extern void SDL_SendPenTouch(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window, bool eraser, bool down);
7171

7272
// Backend calls this when a pen moves on the tablet, to generate events and update state.
73-
extern void SDL_SendPenMotion(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, float x, float y);
73+
extern void SDL_SendPenMotion(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window, float x, float y);
7474

7575
// Backend calls this when a pen's axis changes, to generate events and update state.
76-
extern void SDL_SendPenAxis(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, SDL_PenAxis axis, float value);
76+
extern void SDL_SendPenAxis(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window, SDL_PenAxis axis, float value);
7777

7878
// Backend calls this when a pen's button changes, to generate events and update state.
79-
extern void SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, const SDL_Window *window, Uint8 button, bool down);
79+
extern void SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window, Uint8 button, bool down);
8080

8181
// Backend can optionally use this to find the SDL_PenID for the `handle` that was passed to SDL_AddPenDevice.
8282
extern SDL_PenID SDL_FindPenByHandle(void *handle);

src/events/SDL_touch.c

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,9 @@ static int SDL_num_touch = 0;
2929
static SDL_Touch **SDL_touchDevices = NULL;
3030

3131
// for mapping touch events to mice
32-
33-
#define SYNTHESIZE_TOUCH_TO_MOUSE 1
34-
35-
#if SYNTHESIZE_TOUCH_TO_MOUSE
3632
static bool finger_touching = false;
3733
static SDL_FingerID track_fingerid;
3834
static SDL_TouchID track_touchid;
39-
#endif
4035

4136
// Public functions
4237
bool SDL_InitTouch(void)
@@ -257,27 +252,25 @@ static void SDL_DelFinger(SDL_Touch *touch, SDL_FingerID fingerid)
257252
void SDL_SendTouch(Uint64 timestamp, SDL_TouchID id, SDL_FingerID fingerid, SDL_Window *window, SDL_EventType type, float x, float y, float pressure)
258253
{
259254
SDL_Finger *finger;
260-
SDL_Mouse *mouse;
261255
bool down = (type == SDL_EVENT_FINGER_DOWN);
262256

263257
SDL_Touch *touch = SDL_GetTouch(id);
264258
if (!touch) {
265259
return;
266260
}
267261

268-
mouse = SDL_GetMouse();
262+
SDL_Mouse *mouse = SDL_GetMouse();
269263

270-
#if SYNTHESIZE_TOUCH_TO_MOUSE
271264
// SDL_HINT_TOUCH_MOUSE_EVENTS: controlling whether touch events should generate synthetic mouse events
272265
// SDL_HINT_VITA_TOUCH_MOUSE_DEVICE: controlling which touchpad should generate synthetic mouse events, PSVita-only
273266
{
267+
// FIXME: maybe we should only restrict to a few SDL_TouchDeviceType
268+
if ((id != SDL_MOUSE_TOUCHID) && (id != SDL_PEN_TOUCHID)) {
274269
#ifdef SDL_PLATFORM_VITA
275-
if (mouse->touch_mouse_events && ((mouse->vita_touch_mouse_device == id) || (mouse->vita_touch_mouse_device == 2))) {
270+
if (mouse->touch_mouse_events && ((mouse->vita_touch_mouse_device == id) || (mouse->vita_touch_mouse_device == 2))) {
276271
#else
277-
if (mouse->touch_mouse_events) {
272+
if (mouse->touch_mouse_events) {
278273
#endif
279-
// FIXME: maybe we should only restrict to a few SDL_TouchDeviceType
280-
if (id != SDL_MOUSE_TOUCHID) {
281274
if (window) {
282275
if (down) {
283276
if (finger_touching == false) {
@@ -318,13 +311,12 @@ void SDL_SendTouch(Uint64 timestamp, SDL_TouchID id, SDL_FingerID fingerid, SDL_
318311
}
319312
}
320313
}
321-
#endif
322314

323315
// SDL_HINT_MOUSE_TOUCH_EVENTS: if not set, discard synthetic touch events coming from platform layer
324-
if (mouse->mouse_touch_events == 0) {
325-
if (id == SDL_MOUSE_TOUCHID) {
326-
return;
327-
}
316+
if (!mouse->mouse_touch_events && (id == SDL_MOUSE_TOUCHID)) {
317+
return;
318+
} else if (!mouse->pen_touch_events && (id == SDL_PEN_TOUCHID)) {
319+
return;
328320
}
329321

330322
finger = SDL_GetFinger(touch, fingerid);
@@ -384,21 +376,19 @@ void SDL_SendTouchMotion(Uint64 timestamp, SDL_TouchID id, SDL_FingerID fingerid
384376
{
385377
SDL_Touch *touch;
386378
SDL_Finger *finger;
387-
SDL_Mouse *mouse;
388379
float xrel, yrel, prel;
389380

390381
touch = SDL_GetTouch(id);
391382
if (!touch) {
392383
return;
393384
}
394385

395-
mouse = SDL_GetMouse();
386+
SDL_Mouse *mouse = SDL_GetMouse();
396387

397-
#if SYNTHESIZE_TOUCH_TO_MOUSE
398388
// SDL_HINT_TOUCH_MOUSE_EVENTS: controlling whether touch events should generate synthetic mouse events
399389
{
400-
if (mouse->touch_mouse_events) {
401-
if (id != SDL_MOUSE_TOUCHID) {
390+
if ((id != SDL_MOUSE_TOUCHID) && (id != SDL_PEN_TOUCHID)) {
391+
if (mouse->touch_mouse_events) {
402392
if (window) {
403393
if (finger_touching == true && track_touchid == id && track_fingerid == fingerid) {
404394
float pos_x = (x * (float)window->w);
@@ -421,7 +411,6 @@ void SDL_SendTouchMotion(Uint64 timestamp, SDL_TouchID id, SDL_FingerID fingerid
421411
}
422412
}
423413
}
424-
#endif
425414

426415
// SDL_HINT_MOUSE_TOUCH_EVENTS: if not set, discard synthetic touch events coming from platform layer
427416
if (mouse->mouse_touch_events == 0) {

src/video/wayland/SDL_waylandevents.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2957,7 +2957,7 @@ static void tablet_tool_handle_frame(void *data, struct zwp_tablet_tool_v2 *tool
29572957

29582958
const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_MS_TO_NS(time));
29592959
const SDL_PenID instance_id = sdltool->instance_id;
2960-
const SDL_Window *window = sdltool->tool_focus;
2960+
SDL_Window *window = sdltool->tool_focus;
29612961

29622962
// I don't know if this is necessary (or makes sense), but send motion before pen downs, but after pen ups, so you don't get unexpected lines drawn.
29632963
if (sdltool->frame_motion_set && (sdltool->frame_pen_down != -1)) {

0 commit comments

Comments
 (0)