summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoapen.m
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoapen.m
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoapen.m')
-rw-r--r--contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoapen.m178
1 files changed, 178 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoapen.m b/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoapen.m
new file mode 100644
index 0000000..6c30bfb
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoapen.m
@@ -0,0 +1,178 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_COCOA
24
25#include "SDL_cocoapen.h"
26#include "SDL_cocoavideo.h"
27
28#include "../../events/SDL_pen_c.h"
29
30bool Cocoa_InitPen(SDL_VideoDevice *_this)
31{
32 return true;
33}
34
35typedef struct Cocoa_PenHandle
36{
37 NSUInteger deviceid;
38 NSUInteger toolid;
39 SDL_PenID pen;
40 bool is_eraser;
41} Cocoa_PenHandle;
42
43typedef struct FindPenByDeviceAndToolIDData
44{
45 NSUInteger deviceid;
46 NSUInteger toolid;
47 void *handle;
48} FindPenByDeviceAndToolIDData;
49
50static bool FindPenByDeviceAndToolID(void *handle, void *userdata)
51{
52 const Cocoa_PenHandle *cocoa_handle = (const Cocoa_PenHandle *) handle;
53 FindPenByDeviceAndToolIDData *data = (FindPenByDeviceAndToolIDData *) userdata;
54
55 if (cocoa_handle->deviceid != data->deviceid) {
56 return false;
57 } else if (cocoa_handle->toolid != data->toolid) {
58 return false;
59 }
60 data->handle = handle;
61 return true;
62}
63
64static Cocoa_PenHandle *Cocoa_FindPenByDeviceID(NSUInteger deviceid, NSUInteger toolid)
65{
66 FindPenByDeviceAndToolIDData data;
67 data.deviceid = deviceid;
68 data.toolid = toolid;
69 data.handle = NULL;
70 SDL_FindPenByCallback(FindPenByDeviceAndToolID, &data);
71 return (Cocoa_PenHandle *) data.handle;
72}
73
74static void Cocoa_HandlePenProximityEvent(SDL_CocoaWindowData *_data, NSEvent *event)
75{
76 const NSUInteger devid = [event deviceID];
77 const NSUInteger toolid = [event pointingDeviceID];
78
79 if (event.enteringProximity) { // new pen coming!
80 const NSPointingDeviceType devtype = [event pointingDeviceType];
81 const bool is_eraser = (devtype == NSPointingDeviceTypeEraser);
82 const bool is_pen = (devtype == NSPointingDeviceTypePen);
83 if (!is_eraser && !is_pen) {
84 return; // we ignore other things, which hopefully is right.
85 }
86
87 Cocoa_PenHandle *handle = (Cocoa_PenHandle *) SDL_calloc(1, sizeof (*handle));
88 if (!handle) {
89 return; // oh well.
90 }
91
92 // Cocoa offers almost none of this information as specifics, but can without warning offer any of these specific things.
93 SDL_PenInfo peninfo;
94 SDL_zero(peninfo);
95 peninfo.capabilities = SDL_PEN_CAPABILITY_PRESSURE | SDL_PEN_CAPABILITY_ROTATION | SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT | SDL_PEN_CAPABILITY_TANGENTIAL_PRESSURE | (is_eraser ? SDL_PEN_CAPABILITY_ERASER : 0);
96 peninfo.max_tilt = 90.0f;
97 peninfo.num_buttons = 2;
98 peninfo.subtype = is_eraser ? SDL_PEN_TYPE_ERASER : SDL_PEN_TYPE_PEN;
99
100 handle->deviceid = devid;
101 handle->toolid = toolid;
102 handle->is_eraser = is_eraser;
103 handle->pen = SDL_AddPenDevice(Cocoa_GetEventTimestamp([event timestamp]), NULL, &peninfo, handle);
104 if (!handle->pen) {
105 SDL_free(handle); // oh well.
106 }
107 } else { // old pen leaving!
108 Cocoa_PenHandle *handle = Cocoa_FindPenByDeviceID(devid, toolid);
109 if (handle) {
110 SDL_RemovePenDevice(Cocoa_GetEventTimestamp([event timestamp]), handle->pen);
111 SDL_free(handle);
112 }
113 }
114}
115
116static void Cocoa_HandlePenPointEvent(SDL_CocoaWindowData *_data, NSEvent *event)
117{
118 const Uint64 timestamp = Cocoa_GetEventTimestamp([event timestamp]);
119 Cocoa_PenHandle *handle = Cocoa_FindPenByDeviceID([event deviceID], [event pointingDeviceID]);
120 if (!handle) {
121 return;
122 }
123
124 const SDL_PenID pen = handle->pen;
125 const NSEventButtonMask buttons = [event buttonMask];
126 const NSPoint tilt = [event tilt];
127 const NSPoint point = [event locationInWindow];
128 const bool is_touching = (buttons & NSEventButtonMaskPenTip) != 0;
129 SDL_Window *window = _data.window;
130
131 SDL_SendPenTouch(timestamp, pen, window, handle->is_eraser, is_touching);
132 SDL_SendPenMotion(timestamp, pen, window, (float) point.x, (float) (window->h - point.y));
133 SDL_SendPenButton(timestamp, pen, window, 1, ((buttons & NSEventButtonMaskPenLowerSide) != 0));
134 SDL_SendPenButton(timestamp, pen, window, 2, ((buttons & NSEventButtonMaskPenUpperSide) != 0));
135 SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_PRESSURE, [event pressure]);
136 SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_ROTATION, [event rotation]);
137 SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_XTILT, ((float) tilt.x) * 90.0f);
138 SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_YTILT, ((float) -tilt.y) * 90.0f);
139 SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_TANGENTIAL_PRESSURE, event.tangentialPressure);
140}
141
142bool Cocoa_HandlePenEvent(SDL_CocoaWindowData *_data, NSEvent *event)
143{
144 NSEventType type = [event type];
145
146 if ((type != NSEventTypeTabletPoint) && (type != NSEventTypeTabletProximity)) {
147 const NSEventSubtype subtype = [event subtype];
148 if (subtype == NSEventSubtypeTabletPoint) {
149 type = NSEventTypeTabletPoint;
150 } else if (subtype == NSEventSubtypeTabletProximity) {
151 type = NSEventTypeTabletProximity;
152 } else {
153 return false; // not a tablet event.
154 }
155 }
156
157 if (type == NSEventTypeTabletPoint) {
158 Cocoa_HandlePenPointEvent(_data, event);
159 } else if (type == NSEventTypeTabletProximity) {
160 Cocoa_HandlePenProximityEvent(_data, event);
161 } else {
162 return false; // not a tablet event.
163 }
164
165 return true;
166}
167
168static void Cocoa_FreePenHandle(SDL_PenID instance_id, void *handle, void *userdata)
169{
170 SDL_free(handle);
171}
172
173void Cocoa_QuitPen(SDL_VideoDevice *_this)
174{
175 SDL_RemoveAllPenDevices(Cocoa_FreePenHandle, NULL);
176}
177
178#endif // SDL_VIDEO_DRIVER_COCOA