1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 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
22
23 #include "../../SDL_internal.h"
24
25 #if SDL_VIDEO_DRIVER_EMSCRIPTEN
26
27 #include <emscripten/emscripten.h>
28 #include <emscripten/html5.h>
29
30 #include "SDL_emscriptenmouse.h"
31 #include "SDL_emscriptenvideo.h"
32
33 #include "../../events/SDL_mouse_c.h"
34 #include "SDL_assert.h"
35
36 static SDL_Cursor*
Emscripten_CreateCursorFromString(const char * cursor_str,SDL_bool is_custom)37 Emscripten_CreateCursorFromString(const char* cursor_str, SDL_bool is_custom)
38 {
39 SDL_Cursor* cursor;
40 Emscripten_CursorData *curdata;
41
42 cursor = SDL_calloc(1, sizeof(SDL_Cursor));
43 if (cursor) {
44 curdata = (Emscripten_CursorData *) SDL_calloc(1, sizeof(*curdata));
45 if (!curdata) {
46 SDL_OutOfMemory();
47 SDL_free(cursor);
48 return NULL;
49 }
50
51 curdata->system_cursor = cursor_str;
52 curdata->is_custom = is_custom;
53 cursor->driverdata = curdata;
54 }
55 else {
56 SDL_OutOfMemory();
57 }
58
59 return cursor;
60 }
61
62 static SDL_Cursor*
Emscripten_CreateDefaultCursor()63 Emscripten_CreateDefaultCursor()
64 {
65 return Emscripten_CreateCursorFromString("default", SDL_FALSE);
66 }
67
68 static SDL_Cursor*
Emscripten_CreateCursor(SDL_Surface * surface,int hot_x,int hot_y)69 Emscripten_CreateCursor(SDL_Surface* surface, int hot_x, int hot_y)
70 {
71 const char *cursor_url = NULL;
72 SDL_Surface *conv_surf;
73
74 conv_surf = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ABGR8888, 0);
75
76 if (!conv_surf) {
77 return NULL;
78 }
79
80 cursor_url = (const char *)EM_ASM_INT({
81 var w = $0;
82 var h = $1;
83 var hot_x = $2;
84 var hot_y = $3;
85 var pixels = $4;
86
87 var canvas = document.createElement("canvas");
88 canvas.width = w;
89 canvas.height = h;
90
91 var ctx = canvas.getContext("2d");
92
93 var image = ctx.createImageData(w, h);
94 var data = image.data;
95 var src = pixels >> 2;
96 var dst = 0;
97 var num;
98 if (typeof CanvasPixelArray !== 'undefined' && data instanceof CanvasPixelArray) {
99 // IE10/IE11: ImageData objects are backed by the deprecated CanvasPixelArray,
100 // not UInt8ClampedArray. These don't have buffers, so we need to revert
101 // to copying a byte at a time. We do the undefined check because modern
102 // browsers do not define CanvasPixelArray anymore.
103 num = data.length;
104 while (dst < num) {
105 var val = HEAP32[src]; // This is optimized. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
106 data[dst ] = val & 0xff;
107 data[dst+1] = (val >> 8) & 0xff;
108 data[dst+2] = (val >> 16) & 0xff;
109 data[dst+3] = (val >> 24) & 0xff;
110 src++;
111 dst += 4;
112 }
113 } else {
114 var data32 = new Int32Array(data.buffer);
115 num = data32.length;
116 data32.set(HEAP32.subarray(src, src + num));
117 }
118
119 ctx.putImageData(image, 0, 0);
120 var url = hot_x === 0 && hot_y === 0
121 ? "url(" + canvas.toDataURL() + "), auto"
122 : "url(" + canvas.toDataURL() + ") " + hot_x + " " + hot_y + ", auto";
123
124 var urlBuf = _malloc(url.length + 1);
125 stringToUTF8(url, urlBuf, url.length + 1);
126
127 return urlBuf;
128 }, surface->w, surface->h, hot_x, hot_y, conv_surf->pixels);
129
130 SDL_FreeSurface(conv_surf);
131
132 return Emscripten_CreateCursorFromString(cursor_url, SDL_TRUE);
133 }
134
135 static SDL_Cursor*
Emscripten_CreateSystemCursor(SDL_SystemCursor id)136 Emscripten_CreateSystemCursor(SDL_SystemCursor id)
137 {
138 const char *cursor_name = NULL;
139
140 switch(id) {
141 case SDL_SYSTEM_CURSOR_ARROW:
142 cursor_name = "default";
143 break;
144 case SDL_SYSTEM_CURSOR_IBEAM:
145 cursor_name = "text";
146 break;
147 case SDL_SYSTEM_CURSOR_WAIT:
148 cursor_name = "wait";
149 break;
150 case SDL_SYSTEM_CURSOR_CROSSHAIR:
151 cursor_name = "crosshair";
152 break;
153 case SDL_SYSTEM_CURSOR_WAITARROW:
154 cursor_name = "progress";
155 break;
156 case SDL_SYSTEM_CURSOR_SIZENWSE:
157 cursor_name = "nwse-resize";
158 break;
159 case SDL_SYSTEM_CURSOR_SIZENESW:
160 cursor_name = "nesw-resize";
161 break;
162 case SDL_SYSTEM_CURSOR_SIZEWE:
163 cursor_name = "ew-resize";
164 break;
165 case SDL_SYSTEM_CURSOR_SIZENS:
166 cursor_name = "ns-resize";
167 break;
168 case SDL_SYSTEM_CURSOR_SIZEALL:
169 cursor_name = "move";
170 break;
171 case SDL_SYSTEM_CURSOR_NO:
172 cursor_name = "not-allowed";
173 break;
174 case SDL_SYSTEM_CURSOR_HAND:
175 cursor_name = "pointer";
176 break;
177 default:
178 SDL_assert(0);
179 return NULL;
180 }
181
182 return Emscripten_CreateCursorFromString(cursor_name, SDL_FALSE);
183 }
184
185 static void
Emscripten_FreeCursor(SDL_Cursor * cursor)186 Emscripten_FreeCursor(SDL_Cursor* cursor)
187 {
188 Emscripten_CursorData *curdata;
189 if (cursor) {
190 curdata = (Emscripten_CursorData *) cursor->driverdata;
191
192 if (curdata != NULL) {
193 if (curdata->is_custom) {
194 SDL_free((char *)curdata->system_cursor);
195 }
196 SDL_free(cursor->driverdata);
197 }
198
199 SDL_free(cursor);
200 }
201 }
202
203 static int
Emscripten_ShowCursor(SDL_Cursor * cursor)204 Emscripten_ShowCursor(SDL_Cursor* cursor)
205 {
206 Emscripten_CursorData *curdata;
207 if (SDL_GetMouseFocus() != NULL) {
208 if(cursor && cursor->driverdata) {
209 curdata = (Emscripten_CursorData *) cursor->driverdata;
210
211 if(curdata->system_cursor) {
212 EM_ASM_INT({
213 if (Module['canvas']) {
214 Module['canvas'].style['cursor'] = UTF8ToString($0);
215 }
216 return 0;
217 }, curdata->system_cursor);
218 }
219 }
220 else {
221 EM_ASM(
222 if (Module['canvas']) {
223 Module['canvas'].style['cursor'] = 'none';
224 }
225 );
226 }
227 }
228 return 0;
229 }
230
231 static void
Emscripten_WarpMouse(SDL_Window * window,int x,int y)232 Emscripten_WarpMouse(SDL_Window* window, int x, int y)
233 {
234 SDL_Unsupported();
235 }
236
237 static int
Emscripten_SetRelativeMouseMode(SDL_bool enabled)238 Emscripten_SetRelativeMouseMode(SDL_bool enabled)
239 {
240 SDL_Window *window;
241 SDL_WindowData *window_data;
242
243 /* TODO: pointer lock isn't actually enabled yet */
244 if(enabled) {
245 window = SDL_GetMouseFocus();
246 if (window == NULL) {
247 return -1;
248 }
249
250 window_data = (SDL_WindowData *) window->driverdata;
251
252 if(emscripten_request_pointerlock(window_data->canvas_id, 1) >= EMSCRIPTEN_RESULT_SUCCESS) {
253 return 0;
254 }
255 } else {
256 if(emscripten_exit_pointerlock() >= EMSCRIPTEN_RESULT_SUCCESS) {
257 return 0;
258 }
259 }
260 return -1;
261 }
262
263 void
Emscripten_InitMouse()264 Emscripten_InitMouse()
265 {
266 SDL_Mouse* mouse = SDL_GetMouse();
267
268 mouse->CreateCursor = Emscripten_CreateCursor;
269 mouse->ShowCursor = Emscripten_ShowCursor;
270 mouse->FreeCursor = Emscripten_FreeCursor;
271 mouse->WarpMouse = Emscripten_WarpMouse;
272 mouse->CreateSystemCursor = Emscripten_CreateSystemCursor;
273 mouse->SetRelativeMouseMode = Emscripten_SetRelativeMouseMode;
274
275 SDL_SetDefaultCursor(Emscripten_CreateDefaultCursor());
276 }
277
278 void
Emscripten_FiniMouse()279 Emscripten_FiniMouse()
280 {
281 }
282
283 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
284
285 /* vi: set ts=4 sw=4 expandtab: */
286
287