Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/dgl/src/pugl-extra/wasm.c @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 // Copyright 2012-2022 David Robillard <d@drobilla.net> | |
2 // Copyright 2021-2022 Filipe Coelho <falktx@falktx.com> | |
3 // SPDX-License-Identifier: ISC | |
4 | |
5 #include "wasm.h" | |
6 | |
7 #include "../pugl-upstream/src/internal.h" | |
8 | |
9 #include <stdio.h> | |
10 | |
11 #include <emscripten/html5.h> | |
12 | |
13 #ifdef __cplusplus | |
14 # define PUGL_INIT_STRUCT \ | |
15 {} | |
16 #else | |
17 # define PUGL_INIT_STRUCT \ | |
18 { \ | |
19 0 \ | |
20 } | |
21 #endif | |
22 | |
23 #ifdef __MOD_DEVICES__ | |
24 # define MOD_SCALE_FACTOR_MULT 1 | |
25 #endif | |
26 | |
27 // #define PUGL_WASM_AUTO_POINTER_LOCK | |
28 // #define PUGL_WASM_NO_KEYBOARD_INPUT | |
29 // #define PUGL_WASM_NO_MOUSEWHEEL_INPUT | |
30 | |
31 PuglWorldInternals* | |
32 puglInitWorldInternals(const PuglWorldType type, const PuglWorldFlags flags) | |
33 { | |
34 PuglWorldInternals* impl = | |
35 (PuglWorldInternals*)calloc(1, sizeof(PuglWorldInternals)); | |
36 | |
37 impl->scaleFactor = emscripten_get_device_pixel_ratio(); | |
38 #ifdef __MOD_DEVICES__ | |
39 impl->scaleFactor *= MOD_SCALE_FACTOR_MULT; | |
40 #endif | |
41 | |
42 printf("DONE: %s %d | -> %f\n", __func__, __LINE__, impl->scaleFactor); | |
43 | |
44 return impl; | |
45 } | |
46 | |
47 void* | |
48 puglGetNativeWorld(PuglWorld*) | |
49 { | |
50 printf("DONE: %s %d\n", __func__, __LINE__); | |
51 return NULL; | |
52 } | |
53 | |
54 PuglInternals* | |
55 puglInitViewInternals(PuglWorld* const world) | |
56 { | |
57 printf("DONE: %s %d\n", __func__, __LINE__); | |
58 PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |
59 | |
60 impl->buttonPressTimeout = -1; | |
61 impl->supportsTouch = PUGL_DONT_CARE; // not yet known | |
62 | |
63 #ifdef PUGL_WASM_ASYNC_CLIPBOARD | |
64 impl->supportsClipboardRead = (PuglViewHintValue)EM_ASM_INT({ | |
65 if (typeof(navigator.clipboard) !== 'undefined' && typeof(navigator.clipboard.readText) === 'function' && window.isSecureContext) { | |
66 return 1; // PUGL_TRUE | |
67 } | |
68 return 0; // PUGL_FALSE | |
69 }); | |
70 | |
71 impl->supportsClipboardWrite = (PuglViewHintValue)EM_ASM_INT({ | |
72 if (typeof(navigator.clipboard) !== 'undefined' && typeof(navigator.clipboard.writeText) === 'function' && window.isSecureContext) { | |
73 return 1; // PUGL_TRUE | |
74 } | |
75 if (typeof(document.queryCommandSupported) !== 'undefined' && document.queryCommandSupported("copy")) { | |
76 return 1; // PUGL_TRUE | |
77 } | |
78 return 0; // PUGL_FALSE | |
79 }); | |
80 #endif | |
81 | |
82 return impl; | |
83 } | |
84 | |
85 static PuglStatus | |
86 puglDispatchEventWithContext(PuglView* const view, const PuglEvent* event) | |
87 { | |
88 PuglStatus st0 = PUGL_SUCCESS; | |
89 PuglStatus st1 = PUGL_SUCCESS; | |
90 | |
91 if (!(st0 = view->backend->enter(view, NULL))) { | |
92 st0 = view->eventFunc(view, event); | |
93 st1 = view->backend->leave(view, NULL); | |
94 } | |
95 | |
96 return st0 ? st0 : st1; | |
97 } | |
98 | |
99 static PuglMods | |
100 translateModifiers(const EM_BOOL ctrlKey, | |
101 const EM_BOOL shiftKey, | |
102 const EM_BOOL altKey, | |
103 const EM_BOOL metaKey) | |
104 { | |
105 return (ctrlKey ? PUGL_MOD_CTRL : 0u) | | |
106 (shiftKey ? PUGL_MOD_SHIFT : 0u) | | |
107 (altKey ? PUGL_MOD_ALT : 0u) | | |
108 (metaKey ? PUGL_MOD_SUPER : 0u); | |
109 } | |
110 | |
111 #ifndef PUGL_WASM_NO_KEYBOARD_INPUT | |
112 static PuglKey | |
113 keyCodeToSpecial(const unsigned long code, const unsigned long location) | |
114 { | |
115 switch (code) { | |
116 case 0x08: return PUGL_KEY_BACKSPACE; | |
117 case 0x1B: return PUGL_KEY_ESCAPE; | |
118 case 0x2E: return PUGL_KEY_DELETE; | |
119 case 0x70: return PUGL_KEY_F1; | |
120 case 0x71: return PUGL_KEY_F2; | |
121 case 0x72: return PUGL_KEY_F3; | |
122 case 0x73: return PUGL_KEY_F4; | |
123 case 0x74: return PUGL_KEY_F5; | |
124 case 0x75: return PUGL_KEY_F6; | |
125 case 0x76: return PUGL_KEY_F7; | |
126 case 0x77: return PUGL_KEY_F8; | |
127 case 0x78: return PUGL_KEY_F9; | |
128 case 0x79: return PUGL_KEY_F10; | |
129 case 0x7A: return PUGL_KEY_F11; | |
130 case 0x7B: return PUGL_KEY_F12; | |
131 case 0x25: return PUGL_KEY_LEFT; | |
132 case 0x26: return PUGL_KEY_UP; | |
133 case 0x27: return PUGL_KEY_RIGHT; | |
134 case 0x28: return PUGL_KEY_DOWN; | |
135 case 0x21: return PUGL_KEY_PAGE_UP; | |
136 case 0x22: return PUGL_KEY_PAGE_DOWN; | |
137 case 0x24: return PUGL_KEY_HOME; | |
138 case 0x23: return PUGL_KEY_END; | |
139 case 0x2D: return PUGL_KEY_INSERT; | |
140 case 0x10: return location == DOM_KEY_LOCATION_RIGHT ? PUGL_KEY_SHIFT_R : PUGL_KEY_SHIFT_L; | |
141 case 0x11: return location == DOM_KEY_LOCATION_RIGHT ? PUGL_KEY_CTRL_R : PUGL_KEY_CTRL_L; | |
142 case 0x12: return location == DOM_KEY_LOCATION_RIGHT ? PUGL_KEY_ALT_R : PUGL_KEY_ALT_L; | |
143 case 0xE0: return location == DOM_KEY_LOCATION_RIGHT ? PUGL_KEY_SUPER_R : PUGL_KEY_SUPER_L; | |
144 case 0x5D: return PUGL_KEY_MENU; | |
145 case 0x14: return PUGL_KEY_CAPS_LOCK; | |
146 case 0x91: return PUGL_KEY_SCROLL_LOCK; | |
147 case 0x90: return PUGL_KEY_NUM_LOCK; | |
148 case 0x2C: return PUGL_KEY_PRINT_SCREEN; | |
149 case 0x13: return PUGL_KEY_PAUSE; | |
150 case '\r': return (PuglKey)'\r'; | |
151 default: break; | |
152 } | |
153 | |
154 return (PuglKey)0; | |
155 } | |
156 | |
157 static bool | |
158 decodeCharacterString(const unsigned long keyCode, | |
159 const EM_UTF8 key[EM_HTML5_SHORT_STRING_LEN_BYTES], | |
160 char str[8]) | |
161 { | |
162 if (key[1] == 0) | |
163 { | |
164 str[0] = key[0]; | |
165 return true; | |
166 } | |
167 | |
168 return false; | |
169 } | |
170 | |
171 static EM_BOOL | |
172 puglKeyCallback(const int eventType, const EmscriptenKeyboardEvent* const keyEvent, void* const userData) | |
173 { | |
174 PuglView* const view = (PuglView*)userData; | |
175 | |
176 if (!view->visible) { | |
177 return EM_FALSE; | |
178 } | |
179 | |
180 if (keyEvent->repeat && view->hints[PUGL_IGNORE_KEY_REPEAT]) | |
181 return EM_TRUE; | |
182 | |
183 PuglStatus st0 = PUGL_SUCCESS; | |
184 PuglStatus st1 = PUGL_SUCCESS; | |
185 | |
186 const uint state = translateModifiers(keyEvent->ctrlKey, | |
187 keyEvent->shiftKey, | |
188 keyEvent->altKey, | |
189 keyEvent->metaKey); | |
190 | |
191 const PuglKey special = keyCodeToSpecial(keyEvent->keyCode, keyEvent->location); | |
192 | |
193 uint key = keyEvent->key[0] >= ' ' && keyEvent->key[0] <= '~' && keyEvent->key[1] == '\0' | |
194 ? keyEvent->key[0] | |
195 : keyEvent->keyCode; | |
196 | |
197 if (key >= 'A' && key <= 'Z' && !keyEvent->shiftKey) | |
198 key += 'a' - 'A'; | |
199 | |
200 PuglEvent event = {{PUGL_NOTHING, 0}}; | |
201 event.key.type = eventType == EMSCRIPTEN_EVENT_KEYDOWN ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; | |
202 event.key.time = keyEvent->timestamp / 1e3; | |
203 // event.key.x = xevent.xkey.x; | |
204 // event.key.y = xevent.xkey.y; | |
205 // event.key.xRoot = xevent.xkey.x_root; | |
206 // event.key.yRoot = xevent.xkey.y_root; | |
207 event.key.key = special ? special : key; | |
208 event.key.keycode = keyEvent->keyCode; | |
209 event.key.state = state; | |
210 st0 = puglDispatchEventWithContext(view, &event); | |
211 | |
212 d_debug("key event \n" | |
213 "\tdown: %d\n" | |
214 "\trepeat: %d\n" | |
215 "\tlocation: %d\n" | |
216 "\tstate: 0x%x\n" | |
217 "\tkey[]: '%s'\n" | |
218 "\tcode[]: '%s'\n" | |
219 "\tlocale[]: '%s'\n" | |
220 "\tkeyCode: 0x%lx:'%c' [deprecated, use key]\n" | |
221 "\twhich: 0x%lx:'%c' [deprecated, use key, same as keycode?]\n" | |
222 "\tspecial: 0x%x", | |
223 eventType == EMSCRIPTEN_EVENT_KEYDOWN, | |
224 keyEvent->repeat, | |
225 keyEvent->location, | |
226 state, | |
227 keyEvent->key, | |
228 keyEvent->code, | |
229 keyEvent->locale, | |
230 keyEvent->keyCode, keyEvent->keyCode >= ' ' && keyEvent->keyCode <= '~' ? keyEvent->keyCode : 0, | |
231 keyEvent->which, keyEvent->which >= ' ' && keyEvent->which <= '~' ? keyEvent->which : 0, | |
232 special); | |
233 | |
234 if (event.type == PUGL_KEY_PRESS && !special && !(keyEvent->ctrlKey|keyEvent->altKey|keyEvent->metaKey)) { | |
235 char str[8] = PUGL_INIT_STRUCT; | |
236 | |
237 if (decodeCharacterString(keyEvent->keyCode, keyEvent->key, str)) { | |
238 d_debug("resulting string is '%s'", str); | |
239 | |
240 event.text.type = PUGL_TEXT; | |
241 event.text.character = event.key.key; | |
242 memcpy(event.text.string, str, sizeof(event.text.string)); | |
243 st1 = puglDispatchEventWithContext(view, &event); | |
244 } | |
245 } | |
246 | |
247 return (st0 ? st0 : st1) == PUGL_SUCCESS ? EM_TRUE : EM_FALSE; | |
248 } | |
249 #endif | |
250 | |
251 static EM_BOOL | |
252 puglMouseCallback(const int eventType, const EmscriptenMouseEvent* const mouseEvent, void* const userData) | |
253 { | |
254 PuglView* const view = (PuglView*)userData; | |
255 | |
256 if (!view->visible) { | |
257 return EM_FALSE; | |
258 } | |
259 | |
260 PuglEvent event = {{PUGL_NOTHING, 0}}; | |
261 | |
262 const double time = mouseEvent->timestamp / 1e3; | |
263 const PuglMods state = translateModifiers(mouseEvent->ctrlKey, | |
264 mouseEvent->shiftKey, | |
265 mouseEvent->altKey, | |
266 mouseEvent->metaKey); | |
267 | |
268 double scaleFactor = view->world->impl->scaleFactor; | |
269 #ifdef __MOD_DEVICES__ | |
270 if (!view->impl->isFullscreen) { | |
271 scaleFactor /= EM_ASM_DOUBLE({ | |
272 return parseFloat( | |
273 RegExp('^scale\\\((.*)\\\)$') | |
274 .exec(document.getElementById("pedalboard-dashboard").style.transform)[1] | |
275 ); | |
276 }) * MOD_SCALE_FACTOR_MULT; | |
277 } | |
278 #endif | |
279 | |
280 // workaround missing pointer lock callback, see https://github.com/emscripten-core/emscripten/issues/9681 | |
281 EmscriptenPointerlockChangeEvent e; | |
282 if (emscripten_get_pointerlock_status(&e) == EMSCRIPTEN_RESULT_SUCCESS) | |
283 view->impl->pointerLocked = e.isActive; | |
284 | |
285 #ifdef __MOD_DEVICES__ | |
286 const long canvasX = mouseEvent->canvasX; | |
287 const long canvasY = mouseEvent->canvasY; | |
288 #else | |
289 const char* const className = view->world->className; | |
290 const double canvasX = mouseEvent->clientX - EM_ASM_DOUBLE({ | |
291 var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |
292 return canvasWrapper.getBoundingClientRect().x; | |
293 }, className); | |
294 const double canvasY = mouseEvent->clientY - EM_ASM_DOUBLE({ | |
295 var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |
296 return canvasWrapper.getBoundingClientRect().y; | |
297 }, className); | |
298 #endif | |
299 | |
300 switch (eventType) { | |
301 case EMSCRIPTEN_EVENT_MOUSEDOWN: | |
302 case EMSCRIPTEN_EVENT_MOUSEUP: | |
303 event.button.type = eventType == EMSCRIPTEN_EVENT_MOUSEDOWN ? PUGL_BUTTON_PRESS : PUGL_BUTTON_RELEASE; | |
304 event.button.time = time; | |
305 event.button.x = canvasX * scaleFactor; | |
306 event.button.y = canvasY * scaleFactor; | |
307 event.button.xRoot = mouseEvent->screenX * scaleFactor; | |
308 event.button.yRoot = mouseEvent->screenY * scaleFactor; | |
309 event.button.state = state; | |
310 switch (mouseEvent->button) { | |
311 case 1: | |
312 event.button.button = 2; | |
313 break; | |
314 case 2: | |
315 event.button.button = 1; | |
316 break; | |
317 default: | |
318 event.button.button = mouseEvent->button; | |
319 break; | |
320 } | |
321 break; | |
322 case EMSCRIPTEN_EVENT_MOUSEMOVE: | |
323 event.motion.type = PUGL_MOTION; | |
324 event.motion.time = time; | |
325 if (view->impl->pointerLocked) { | |
326 // adjust local values for delta | |
327 const double movementX = mouseEvent->movementX * scaleFactor; | |
328 const double movementY = mouseEvent->movementY * scaleFactor; | |
329 view->impl->lastMotion.x += movementX; | |
330 view->impl->lastMotion.y += movementY; | |
331 view->impl->lastMotion.xRoot += movementX; | |
332 view->impl->lastMotion.yRoot += movementY; | |
333 // now set x, y, xRoot and yRoot | |
334 event.motion.x = view->impl->lastMotion.x; | |
335 event.motion.y = view->impl->lastMotion.y; | |
336 event.motion.xRoot = view->impl->lastMotion.xRoot; | |
337 event.motion.yRoot = view->impl->lastMotion.yRoot; | |
338 } else { | |
339 // cache values for possible pointer lock movement later | |
340 view->impl->lastMotion.x = event.motion.x = canvasX * scaleFactor; | |
341 view->impl->lastMotion.y = event.motion.y = canvasY * scaleFactor; | |
342 view->impl->lastMotion.xRoot = event.motion.xRoot = mouseEvent->screenX * scaleFactor; | |
343 view->impl->lastMotion.yRoot = event.motion.yRoot = mouseEvent->screenY * scaleFactor; | |
344 } | |
345 event.motion.state = state; | |
346 break; | |
347 case EMSCRIPTEN_EVENT_MOUSEENTER: | |
348 case EMSCRIPTEN_EVENT_MOUSELEAVE: | |
349 event.crossing.type = eventType == EMSCRIPTEN_EVENT_MOUSEENTER ? PUGL_POINTER_IN : PUGL_POINTER_OUT; | |
350 event.crossing.time = time; | |
351 event.crossing.x = canvasX * scaleFactor; | |
352 event.crossing.y = canvasY * scaleFactor; | |
353 event.crossing.xRoot = mouseEvent->screenX * scaleFactor; | |
354 event.crossing.yRoot = mouseEvent->screenY * scaleFactor; | |
355 event.crossing.state = state; | |
356 event.crossing.mode = PUGL_CROSSING_NORMAL; | |
357 break; | |
358 } | |
359 | |
360 if (event.type == PUGL_NOTHING) | |
361 return EM_FALSE; | |
362 | |
363 puglDispatchEventWithContext(view, &event); | |
364 | |
365 #ifdef PUGL_WASM_AUTO_POINTER_LOCK | |
366 switch (eventType) { | |
367 case EMSCRIPTEN_EVENT_MOUSEDOWN: | |
368 emscripten_request_pointerlock(view->world->className, false); | |
369 break; | |
370 case EMSCRIPTEN_EVENT_MOUSEUP: | |
371 emscripten_exit_pointerlock(); | |
372 break; | |
373 } | |
374 #endif | |
375 | |
376 // note: we must always return false, otherwise canvas never gets keyboard input | |
377 return EM_FALSE; | |
378 } | |
379 | |
380 static void | |
381 puglTouchStartDelay(void* const userData) | |
382 { | |
383 PuglView* const view = (PuglView*)userData; | |
384 PuglInternals* const impl = view->impl; | |
385 | |
386 impl->buttonPressTimeout = -1; | |
387 impl->nextButtonEvent.button.time += 2000; | |
388 puglDispatchEventWithContext(view, &impl->nextButtonEvent); | |
389 } | |
390 | |
391 static EM_BOOL | |
392 puglTouchCallback(const int eventType, const EmscriptenTouchEvent* const touchEvent, void* const userData) | |
393 { | |
394 if (touchEvent->numTouches <= 0) { | |
395 return EM_FALSE; | |
396 } | |
397 | |
398 PuglView* const view = (PuglView*)userData; | |
399 PuglInternals* const impl = view->impl; | |
400 const char* const className = view->world->className; | |
401 | |
402 if (impl->supportsTouch == PUGL_DONT_CARE) { | |
403 impl->supportsTouch = PUGL_TRUE; | |
404 | |
405 // stop using mouse press events which conflict with touch | |
406 emscripten_set_mousedown_callback(className, view, false, NULL); | |
407 emscripten_set_mouseup_callback(className, view, false, NULL); | |
408 } | |
409 | |
410 if (!view->visible) { | |
411 return EM_FALSE; | |
412 } | |
413 | |
414 PuglEvent event = {{PUGL_NOTHING, 0}}; | |
415 | |
416 const double time = touchEvent->timestamp / 1e3; | |
417 const PuglMods state = translateModifiers(touchEvent->ctrlKey, | |
418 touchEvent->shiftKey, | |
419 touchEvent->altKey, | |
420 touchEvent->metaKey); | |
421 | |
422 double scaleFactor = view->world->impl->scaleFactor; | |
423 #ifdef __MOD_DEVICES__ | |
424 if (!view->impl->isFullscreen) { | |
425 scaleFactor /= EM_ASM_DOUBLE({ | |
426 return parseFloat( | |
427 RegExp('^scale\\\((.*)\\\)$') | |
428 .exec(document.getElementById("pedalboard-dashboard").style.transform)[1] | |
429 ); | |
430 }) * MOD_SCALE_FACTOR_MULT; | |
431 } | |
432 #endif | |
433 | |
434 d_debug("touch %d|%s %d || %ld", | |
435 eventType, | |
436 eventType == EMSCRIPTEN_EVENT_TOUCHSTART ? "start" : | |
437 eventType == EMSCRIPTEN_EVENT_TOUCHEND ? "end" : "cancel", | |
438 touchEvent->numTouches, | |
439 impl->buttonPressTimeout); | |
440 | |
441 const EmscriptenTouchPoint* point = &touchEvent->touches[0]; | |
442 | |
443 if (impl->buttonPressTimeout != -1 || eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) { | |
444 // if we received an event while touch is active, trigger initial click now | |
445 if (impl->buttonPressTimeout != -1) { | |
446 emscripten_clear_timeout(impl->buttonPressTimeout); | |
447 impl->buttonPressTimeout = -1; | |
448 if (eventType != EMSCRIPTEN_EVENT_TOUCHCANCEL) { | |
449 impl->nextButtonEvent.button.button = 0; | |
450 } | |
451 } | |
452 impl->nextButtonEvent.button.time = time; | |
453 puglDispatchEventWithContext(view, &impl->nextButtonEvent); | |
454 } | |
455 | |
456 #ifdef __MOD_DEVICES__ | |
457 const long canvasX = point->canvasX; | |
458 const long canvasY = point->canvasY; | |
459 #else | |
460 const double canvasX = point->clientX - EM_ASM_DOUBLE({ | |
461 var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |
462 return canvasWrapper.getBoundingClientRect().x; | |
463 }, className); | |
464 const double canvasY = point->clientY - EM_ASM_DOUBLE({ | |
465 var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |
466 return canvasWrapper.getBoundingClientRect().y; | |
467 }, className); | |
468 #endif | |
469 | |
470 switch (eventType) { | |
471 case EMSCRIPTEN_EVENT_TOUCHEND: | |
472 case EMSCRIPTEN_EVENT_TOUCHCANCEL: | |
473 event.button.type = PUGL_BUTTON_RELEASE; | |
474 event.button.time = time; | |
475 event.button.button = eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL ? 1 : 0; | |
476 event.button.x = canvasX * scaleFactor; | |
477 event.button.y = canvasY * scaleFactor; | |
478 event.button.xRoot = point->screenX * scaleFactor; | |
479 event.button.yRoot = point->screenY * scaleFactor; | |
480 event.button.state = state; | |
481 break; | |
482 | |
483 case EMSCRIPTEN_EVENT_TOUCHSTART: | |
484 // this event can be used for a couple of things, store it until we know more | |
485 event.button.type = PUGL_BUTTON_PRESS; | |
486 event.button.time = time; | |
487 event.button.button = 1; // if no other event occurs soon, treat it as right-click | |
488 event.button.x = canvasX * scaleFactor; | |
489 event.button.y = canvasY * scaleFactor; | |
490 event.button.xRoot = point->screenX * scaleFactor; | |
491 event.button.yRoot = point->screenY * scaleFactor; | |
492 event.button.state = state; | |
493 memcpy(&impl->nextButtonEvent, &event, sizeof(PuglEvent)); | |
494 impl->buttonPressTimeout = emscripten_set_timeout(puglTouchStartDelay, 2000, view); | |
495 // fall through, moving "mouse" to touch position | |
496 | |
497 case EMSCRIPTEN_EVENT_TOUCHMOVE: | |
498 event.motion.type = PUGL_MOTION; | |
499 event.motion.time = time; | |
500 event.motion.x = canvasX * scaleFactor; | |
501 event.motion.y = canvasY * scaleFactor; | |
502 event.motion.xRoot = point->screenX * scaleFactor; | |
503 event.motion.yRoot = point->screenY * scaleFactor; | |
504 event.motion.state = state; | |
505 break; | |
506 } | |
507 | |
508 if (event.type == PUGL_NOTHING) | |
509 return EM_FALSE; | |
510 | |
511 puglDispatchEventWithContext(view, &event); | |
512 | |
513 // FIXME we must always return false?? | |
514 return EM_FALSE; | |
515 } | |
516 | |
517 static EM_BOOL | |
518 puglFocusCallback(const int eventType, const EmscriptenFocusEvent* /*const focusEvent*/, void* const userData) | |
519 { | |
520 PuglView* const view = (PuglView*)userData; | |
521 | |
522 if (!view->visible) { | |
523 return EM_FALSE; | |
524 } | |
525 | |
526 d_debug("focus %d|%s", eventType, eventType == EMSCRIPTEN_EVENT_FOCUSIN ? "focus-in" : "focus-out"); | |
527 | |
528 PuglEvent event = {{eventType == EMSCRIPTEN_EVENT_FOCUSIN ? PUGL_FOCUS_IN : PUGL_FOCUS_OUT, 0}}; | |
529 event.focus.mode = PUGL_CROSSING_NORMAL; | |
530 | |
531 puglDispatchEventWithContext(view, &event); | |
532 | |
533 // note: we must always return false, otherwise canvas never gets proper focus | |
534 return EM_FALSE; | |
535 } | |
536 | |
537 static EM_BOOL | |
538 puglPointerLockChangeCallback(const int eventType, const EmscriptenPointerlockChangeEvent* event, void* const userData) | |
539 { | |
540 PuglView* const view = (PuglView*)userData; | |
541 | |
542 view->impl->pointerLocked = event->isActive; | |
543 return EM_TRUE; | |
544 } | |
545 | |
546 #ifndef PUGL_WASM_NO_MOUSEWHEEL_INPUT | |
547 static EM_BOOL | |
548 puglWheelCallback(const int eventType, const EmscriptenWheelEvent* const wheelEvent, void* const userData) | |
549 { | |
550 PuglView* const view = (PuglView*)userData; | |
551 | |
552 if (!view->visible) { | |
553 return EM_FALSE; | |
554 } | |
555 | |
556 double scaleFactor = view->world->impl->scaleFactor; | |
557 #ifdef __MOD_DEVICES__ | |
558 if (!view->impl->isFullscreen) { | |
559 scaleFactor /= EM_ASM_DOUBLE({ | |
560 return parseFloat( | |
561 RegExp('^scale\\\((.*)\\\)$') | |
562 .exec(document.getElementById("pedalboard-dashboard").style.transform)[1] | |
563 ); | |
564 }) * MOD_SCALE_FACTOR_MULT; | |
565 } | |
566 #endif | |
567 | |
568 #ifdef __MOD_DEVICES__ | |
569 const long canvasX = wheelEvent->mouse.canvasX; | |
570 const long canvasY = wheelEvent->mouse.canvasY; | |
571 #else | |
572 const char* const className = view->world->className; | |
573 const double canvasX = wheelEvent->mouse.canvasX - EM_ASM_INT({ | |
574 var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |
575 return canvasWrapper.getBoundingClientRect().x; | |
576 }, className); | |
577 const double canvasY = wheelEvent->mouse.canvasY - EM_ASM_INT({ | |
578 var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |
579 return canvasWrapper.getBoundingClientRect().y; | |
580 }, className); | |
581 #endif | |
582 | |
583 PuglEvent event = {{PUGL_SCROLL, 0}}; | |
584 event.scroll.time = wheelEvent->mouse.timestamp / 1e3; | |
585 event.scroll.x = canvasX; | |
586 event.scroll.y = canvasY; | |
587 event.scroll.xRoot = wheelEvent->mouse.screenX; | |
588 event.scroll.yRoot = wheelEvent->mouse.screenY; | |
589 event.scroll.state = translateModifiers(wheelEvent->mouse.ctrlKey, | |
590 wheelEvent->mouse.shiftKey, | |
591 wheelEvent->mouse.altKey, | |
592 wheelEvent->mouse.metaKey); | |
593 event.scroll.direction = PUGL_SCROLL_SMOOTH; | |
594 // FIXME handle wheelEvent->deltaMode | |
595 event.scroll.dx = wheelEvent->deltaX * 0.01 * scaleFactor; | |
596 event.scroll.dy = -wheelEvent->deltaY * 0.01 * scaleFactor; | |
597 | |
598 return puglDispatchEventWithContext(view, &event) == PUGL_SUCCESS ? EM_TRUE : EM_FALSE; | |
599 } | |
600 #endif | |
601 | |
602 static EM_BOOL | |
603 puglUiCallback(const int eventType, const EmscriptenUiEvent* const uiEvent, void* const userData) | |
604 { | |
605 PuglView* const view = (PuglView*)userData; | |
606 const char* const className = view->world->className; | |
607 | |
608 // FIXME | |
609 const int width = EM_ASM_INT({ | |
610 var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |
611 canvasWrapper.style.setProperty("--device-pixel-ratio", window.devicePixelRatio); | |
612 return canvasWrapper.clientWidth; | |
613 }, className); | |
614 | |
615 const int height = EM_ASM_INT({ | |
616 var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |
617 return canvasWrapper.clientHeight; | |
618 }, className); | |
619 | |
620 if (!width || !height) | |
621 return EM_FALSE; | |
622 | |
623 double scaleFactor = emscripten_get_device_pixel_ratio(); | |
624 #ifdef __MOD_DEVICES__ | |
625 scaleFactor *= MOD_SCALE_FACTOR_MULT; | |
626 #endif | |
627 view->world->impl->scaleFactor = scaleFactor; | |
628 | |
629 PuglEvent event = {{PUGL_CONFIGURE, 0}}; | |
630 event.configure.x = view->frame.x; | |
631 event.configure.y = view->frame.y; | |
632 event.configure.width = width * scaleFactor; | |
633 event.configure.height = height * scaleFactor; | |
634 puglDispatchEvent(view, &event); | |
635 | |
636 emscripten_set_canvas_element_size(view->world->className, width * scaleFactor, height * scaleFactor); | |
637 return EM_TRUE; | |
638 } | |
639 | |
640 static EM_BOOL | |
641 puglFullscreenChangeCallback(const int eventType, const EmscriptenFullscreenChangeEvent* const fscEvent, void* const userData) | |
642 { | |
643 PuglView* const view = (PuglView*)userData; | |
644 | |
645 view->impl->isFullscreen = fscEvent->isFullscreen; | |
646 | |
647 double scaleFactor = emscripten_get_device_pixel_ratio(); | |
648 #ifdef __MOD_DEVICES__ | |
649 scaleFactor *= MOD_SCALE_FACTOR_MULT; | |
650 #endif | |
651 view->world->impl->scaleFactor = scaleFactor; | |
652 | |
653 if (fscEvent->isFullscreen) { | |
654 PuglEvent event = {{PUGL_CONFIGURE, 0}}; | |
655 event.configure.x = 0; | |
656 event.configure.y = 0; | |
657 event.configure.width = fscEvent->elementWidth * scaleFactor; | |
658 event.configure.height = fscEvent->elementHeight * scaleFactor; | |
659 puglDispatchEvent(view, &event); | |
660 | |
661 emscripten_set_canvas_element_size(view->world->className, | |
662 fscEvent->elementWidth * scaleFactor, | |
663 fscEvent->elementHeight * scaleFactor); | |
664 | |
665 #ifdef __MOD_DEVICES__ | |
666 EM_ASM({ | |
667 document.getElementById("pedalboard-dashboard").style.transform = "scale(1.0)"; | |
668 }); | |
669 #endif | |
670 return EM_TRUE; | |
671 } | |
672 | |
673 return puglUiCallback(0, NULL, userData); | |
674 } | |
675 | |
676 static EM_BOOL | |
677 puglVisibilityChangeCallback(const int eventType, const EmscriptenVisibilityChangeEvent* const visibilityChangeEvent, void* const userData) | |
678 { | |
679 PuglView* const view = (PuglView*)userData; | |
680 | |
681 view->visible = visibilityChangeEvent->hidden == EM_FALSE; | |
682 PuglEvent event = {{ view->visible ? PUGL_MAP : PUGL_UNMAP, 0}}; | |
683 puglDispatchEvent(view, &event); | |
684 return EM_FALSE; | |
685 } | |
686 | |
687 PuglStatus | |
688 puglRealize(PuglView* const view) | |
689 { | |
690 printf("TODO: %s %d\n", __func__, __LINE__); | |
691 PuglStatus st = PUGL_SUCCESS; | |
692 | |
693 // Ensure that we do not have a parent | |
694 if (view->parent) { | |
695 printf("TODO: %s %d\n", __func__, __LINE__); | |
696 return PUGL_FAILURE; | |
697 } | |
698 | |
699 if (!view->backend || !view->backend->configure) { | |
700 printf("TODO: %s %d\n", __func__, __LINE__); | |
701 return PUGL_BAD_BACKEND; | |
702 } | |
703 | |
704 const char* const className = view->world->className; | |
705 d_stdout("className is %s", className); | |
706 | |
707 // Set the size to the default if it has not already been set | |
708 if (view->frame.width <= 0.0 && view->frame.height <= 0.0) { | |
709 PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |
710 if (!defaultSize.width || !defaultSize.height) { | |
711 return PUGL_BAD_CONFIGURATION; | |
712 } | |
713 | |
714 view->frame.width = defaultSize.width; | |
715 view->frame.height = defaultSize.height; | |
716 } | |
717 | |
718 // Configure and create the backend | |
719 if ((st = view->backend->configure(view)) || (st = view->backend->create(view))) { | |
720 view->backend->destroy(view); | |
721 return st; | |
722 } | |
723 | |
724 if (view->title) { | |
725 puglSetWindowTitle(view, view->title); | |
726 } | |
727 | |
728 puglDispatchSimpleEvent(view, PUGL_CREATE); | |
729 | |
730 PuglEvent event = {{PUGL_CONFIGURE, 0}}; | |
731 event.configure.x = view->frame.x; | |
732 event.configure.y = view->frame.y; | |
733 event.configure.width = view->frame.width; | |
734 event.configure.height = view->frame.height; | |
735 puglDispatchEvent(view, &event); | |
736 | |
737 EM_ASM({ | |
738 var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |
739 canvasWrapper.style.setProperty("--device-pixel-ratio", window.devicePixelRatio); | |
740 }, className); | |
741 | |
742 emscripten_set_canvas_element_size(className, view->frame.width, view->frame.height); | |
743 #ifndef PUGL_WASM_NO_KEYBOARD_INPUT | |
744 // emscripten_set_keypress_callback(className, view, false, puglKeyCallback); | |
745 emscripten_set_keydown_callback(className, view, false, puglKeyCallback); | |
746 emscripten_set_keyup_callback(className, view, false, puglKeyCallback); | |
747 #endif | |
748 emscripten_set_touchstart_callback(className, view, false, puglTouchCallback); | |
749 emscripten_set_touchend_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglTouchCallback); | |
750 emscripten_set_touchmove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglTouchCallback); | |
751 emscripten_set_touchcancel_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglTouchCallback); | |
752 emscripten_set_mousedown_callback(className, view, false, puglMouseCallback); | |
753 emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglMouseCallback); | |
754 emscripten_set_mousemove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglMouseCallback); | |
755 emscripten_set_mouseenter_callback(className, view, false, puglMouseCallback); | |
756 emscripten_set_mouseleave_callback(className, view, false, puglMouseCallback); | |
757 emscripten_set_focusin_callback(className, view, false, puglFocusCallback); | |
758 emscripten_set_focusout_callback(className, view, false, puglFocusCallback); | |
759 #ifndef PUGL_WASM_NO_MOUSEWHEEL_INPUT | |
760 emscripten_set_wheel_callback(className, view, false, puglWheelCallback); | |
761 #endif | |
762 emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglPointerLockChangeCallback); | |
763 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglUiCallback); | |
764 emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglFullscreenChangeCallback); | |
765 emscripten_set_visibilitychange_callback(view, false, puglVisibilityChangeCallback); | |
766 | |
767 printf("TODO: %s %d\n", __func__, __LINE__); | |
768 return PUGL_SUCCESS; | |
769 } | |
770 | |
771 PuglStatus | |
772 puglShow(PuglView* const view) | |
773 { | |
774 view->visible = true; | |
775 view->impl->needsRepaint = true; | |
776 return puglPostRedisplay(view); | |
777 } | |
778 | |
779 PuglStatus | |
780 puglHide(PuglView* const view) | |
781 { | |
782 view->visible = false; | |
783 return PUGL_FAILURE; | |
784 } | |
785 | |
786 void | |
787 puglFreeViewInternals(PuglView* const view) | |
788 { | |
789 printf("DONE: %s %d\n", __func__, __LINE__); | |
790 if (view && view->impl) { | |
791 if (view->backend) { | |
792 // unregister the window events, to make sure no callbacks to old views are triggered | |
793 emscripten_set_touchend_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); | |
794 emscripten_set_touchmove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); | |
795 emscripten_set_touchcancel_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); | |
796 emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); | |
797 emscripten_set_mousemove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); | |
798 emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); | |
799 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); | |
800 emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); | |
801 emscripten_set_visibilitychange_callback(NULL, false, NULL); | |
802 view->backend->destroy(view); | |
803 } | |
804 free(view->impl->clipboardData); | |
805 free(view->impl->timers); | |
806 free(view->impl); | |
807 } | |
808 } | |
809 | |
810 void | |
811 puglFreeWorldInternals(PuglWorld* const world) | |
812 { | |
813 printf("DONE: %s %d\n", __func__, __LINE__); | |
814 free(world->impl); | |
815 } | |
816 | |
817 PuglStatus | |
818 puglGrabFocus(PuglView*) | |
819 { | |
820 return PUGL_FAILURE; | |
821 } | |
822 | |
823 double | |
824 puglGetScaleFactor(const PuglView* const view) | |
825 { | |
826 printf("DONE: %s %d\n", __func__, __LINE__); | |
827 return view->world->impl->scaleFactor; | |
828 } | |
829 | |
830 double | |
831 puglGetTime(const PuglWorld*) | |
832 { | |
833 return emscripten_get_now() / 1e3; | |
834 } | |
835 | |
836 PuglStatus | |
837 puglUpdate(PuglWorld* const world, const double timeout) | |
838 { | |
839 for (size_t i = 0; i < world->numViews; ++i) { | |
840 PuglView* const view = world->views[i]; | |
841 | |
842 if (!view->visible) { | |
843 continue; | |
844 } | |
845 | |
846 puglDispatchSimpleEvent(view, PUGL_UPDATE); | |
847 | |
848 if (!view->impl->needsRepaint) { | |
849 continue; | |
850 } | |
851 | |
852 view->impl->needsRepaint = false; | |
853 | |
854 PuglEvent event = {{PUGL_EXPOSE, 0}}; | |
855 event.expose.x = view->frame.x; | |
856 event.expose.y = view->frame.y; | |
857 event.expose.width = view->frame.width; | |
858 event.expose.height = view->frame.height; | |
859 puglDispatchEvent(view, &event); | |
860 } | |
861 | |
862 return PUGL_SUCCESS; | |
863 } | |
864 | |
865 PuglStatus | |
866 puglPostRedisplay(PuglView* const view) | |
867 { | |
868 view->impl->needsRepaint = true; | |
869 return PUGL_SUCCESS; | |
870 } | |
871 | |
872 PuglStatus | |
873 puglPostRedisplayRect(PuglView* const view, const PuglRect rect) | |
874 { | |
875 view->impl->needsRepaint = true; | |
876 return PUGL_FAILURE; | |
877 } | |
878 | |
879 PuglNativeView | |
880 puglGetNativeView(PuglView* const view) | |
881 { | |
882 return 0; | |
883 } | |
884 | |
885 PuglStatus | |
886 puglSetWindowTitle(PuglView* const view, const char* const title) | |
887 { | |
888 puglSetString(&view->title, title); | |
889 emscripten_set_window_title(title); | |
890 return PUGL_SUCCESS; | |
891 } | |
892 | |
893 PuglStatus | |
894 puglSetSizeHint(PuglView* const view, | |
895 const PuglSizeHint hint, | |
896 const PuglSpan width, | |
897 const PuglSpan height) | |
898 { | |
899 view->sizeHints[hint].width = width; | |
900 view->sizeHints[hint].height = height; | |
901 return PUGL_SUCCESS; | |
902 } | |
903 | |
904 static EM_BOOL | |
905 puglTimerLoopCallback(double timeout, void* const arg) | |
906 { | |
907 PuglTimer* const timer = (PuglTimer*)arg; | |
908 PuglInternals* const impl = timer->view->impl; | |
909 | |
910 // only handle active timers | |
911 for (uint32_t i=0; i<impl->numTimers; ++i) | |
912 { | |
913 if (impl->timers[i].id == timer->id) | |
914 { | |
915 PuglEvent event = {{PUGL_TIMER, 0}}; | |
916 event.timer.id = timer->id; | |
917 puglDispatchEventWithContext(timer->view, &event); | |
918 return EM_TRUE; | |
919 } | |
920 } | |
921 | |
922 return EM_FALSE; | |
923 | |
924 // unused | |
925 (void)timeout; | |
926 } | |
927 | |
928 PuglStatus | |
929 puglStartTimer(PuglView* const view, const uintptr_t id, const double timeout) | |
930 { | |
931 printf("DONE: %s %d\n", __func__, __LINE__); | |
932 PuglInternals* const impl = view->impl; | |
933 const uint32_t timerIndex = impl->numTimers++; | |
934 | |
935 if (impl->timers == NULL) | |
936 impl->timers = (PuglTimer*)malloc(sizeof(PuglTimer)); | |
937 else | |
938 impl->timers = (PuglTimer*)realloc(impl->timers, sizeof(PuglTimer) * timerIndex); | |
939 | |
940 PuglTimer* const timer = &impl->timers[timerIndex]; | |
941 timer->view = view; | |
942 timer->id = id; | |
943 | |
944 emscripten_set_timeout_loop(puglTimerLoopCallback, timeout * 1e3, timer); | |
945 return PUGL_SUCCESS; | |
946 } | |
947 | |
948 PuglStatus | |
949 puglStopTimer(PuglView* const view, const uintptr_t id) | |
950 { | |
951 printf("DONE: %s %d\n", __func__, __LINE__); | |
952 PuglInternals* const impl = view->impl; | |
953 | |
954 if (impl->timers == NULL || impl->numTimers == 0) | |
955 return PUGL_FAILURE; | |
956 | |
957 for (uint32_t i=0; i<impl->numTimers; ++i) | |
958 { | |
959 if (impl->timers[i].id == id) | |
960 { | |
961 memmove(impl->timers + i, impl->timers + (i + 1), sizeof(PuglTimer) * (impl->numTimers - 1)); | |
962 --impl->numTimers; | |
963 return PUGL_SUCCESS; | |
964 } | |
965 } | |
966 | |
967 return PUGL_FAILURE; | |
968 } | |
969 | |
970 #ifdef PUGL_WASM_ASYNC_CLIPBOARD | |
971 EM_JS(char*, puglGetAsyncClipboardData, (), { | |
972 var text = Asyncify.handleSleep(function(wakeUp) { | |
973 navigator.clipboard.readText() | |
974 .then(function(text) { | |
975 wakeUp(text); | |
976 }) | |
977 .catch(function() { | |
978 wakeUp(""); | |
979 }); | |
980 }); | |
981 if (!text.length) { | |
982 return null; | |
983 } | |
984 var length = lengthBytesUTF8(text) + 1; | |
985 var str = _malloc(length); | |
986 stringToUTF8(text, str, length); | |
987 return str; | |
988 }); | |
989 #endif | |
990 | |
991 PuglStatus | |
992 puglPaste(PuglView* const view) | |
993 { | |
994 #ifdef PUGL_WASM_ASYNC_CLIPBOARD | |
995 // abort early if we already know it is not supported | |
996 if (view->impl->supportsClipboardRead == PUGL_FALSE) { | |
997 return PUGL_UNSUPPORTED; | |
998 } | |
999 | |
1000 free(view->impl->clipboardData); | |
1001 view->impl->clipboardData = puglGetAsyncClipboardData(); | |
1002 #endif | |
1003 | |
1004 if (view->impl->clipboardData == NULL) { | |
1005 return PUGL_FAILURE; | |
1006 } | |
1007 | |
1008 const PuglDataOfferEvent offer = { | |
1009 PUGL_DATA_OFFER, | |
1010 0, | |
1011 emscripten_get_now() / 1e3, | |
1012 }; | |
1013 | |
1014 PuglEvent offerEvent; | |
1015 offerEvent.offer = offer; | |
1016 puglDispatchEvent(view, &offerEvent); | |
1017 return PUGL_SUCCESS; | |
1018 } | |
1019 | |
1020 PuglStatus | |
1021 puglAcceptOffer(PuglView* const view, | |
1022 const PuglDataOfferEvent* const offer, | |
1023 const uint32_t typeIndex) | |
1024 { | |
1025 if (typeIndex != 0) { | |
1026 return PUGL_UNSUPPORTED; | |
1027 } | |
1028 | |
1029 const PuglDataEvent data = { | |
1030 PUGL_DATA, | |
1031 0, | |
1032 emscripten_get_now() / 1e3, | |
1033 0, | |
1034 }; | |
1035 | |
1036 PuglEvent dataEvent; | |
1037 dataEvent.data = data; | |
1038 puglDispatchEvent(view, &dataEvent); | |
1039 return PUGL_SUCCESS; | |
1040 } | |
1041 | |
1042 uint32_t | |
1043 puglGetNumClipboardTypes(const PuglView* const view) | |
1044 { | |
1045 return view->impl->clipboardData != NULL ? 1u : 0u; | |
1046 } | |
1047 | |
1048 const char* | |
1049 puglGetClipboardType(const PuglView* const view, const uint32_t typeIndex) | |
1050 { | |
1051 return (typeIndex == 0 && view->impl->clipboardData != NULL) | |
1052 ? "text/plain" | |
1053 : NULL; | |
1054 } | |
1055 | |
1056 const void* | |
1057 puglGetClipboard(PuglView* const view, | |
1058 const uint32_t typeIndex, | |
1059 size_t* const len) | |
1060 { | |
1061 return view->impl->clipboardData; | |
1062 } | |
1063 | |
1064 PuglStatus | |
1065 puglSetClipboard(PuglView* const view, | |
1066 const char* const type, | |
1067 const void* const data, | |
1068 const size_t len) | |
1069 { | |
1070 // only utf8 text supported for now | |
1071 if (type != NULL && strcmp(type, "text/plain") != 0) { | |
1072 return PUGL_UNSUPPORTED; | |
1073 } | |
1074 | |
1075 const char* const className = view->world->className; | |
1076 const char* const text = (const char*)data; | |
1077 | |
1078 #ifdef PUGL_WASM_ASYNC_CLIPBOARD | |
1079 // abort early if we already know it is not supported | |
1080 if (view->impl->supportsClipboardWrite == PUGL_FALSE) { | |
1081 return PUGL_UNSUPPORTED; | |
1082 } | |
1083 #else | |
1084 puglSetString(&view->impl->clipboardData, text); | |
1085 #endif | |
1086 | |
1087 EM_ASM({ | |
1088 if (typeof(navigator.clipboard) !== 'undefined' && typeof(navigator.clipboard.writeText) === 'function' && window.isSecureContext) { | |
1089 navigator.clipboard.writeText(UTF8ToString($1)); | |
1090 } else { | |
1091 var canvasClipboardObjName = UTF8ToString($0) + "_clipboard"; | |
1092 var canvasClipboardElem = document.getElementById(canvasClipboardObjName); | |
1093 | |
1094 if (!canvasClipboardElem) { | |
1095 canvasClipboardElem = document.createElement('textarea'); | |
1096 canvasClipboardElem.id = canvasClipboardObjName; | |
1097 canvasClipboardElem.style.position = 'fixed'; | |
1098 canvasClipboardElem.style.whiteSpace = 'pre'; | |
1099 canvasClipboardElem.style.zIndex = '-1'; | |
1100 canvasClipboardElem.setAttribute('readonly', true); | |
1101 document.body.appendChild(canvasClipboardElem); | |
1102 } | |
1103 | |
1104 canvasClipboardElem.textContent = UTF8ToString($1); | |
1105 canvasClipboardElem.select(); | |
1106 document.execCommand("copy"); | |
1107 } | |
1108 }, className, text); | |
1109 | |
1110 // FIXME proper return status | |
1111 return PUGL_SUCCESS; | |
1112 } | |
1113 | |
1114 PuglStatus | |
1115 puglSetCursor(PuglView* const view, const PuglCursor cursor) | |
1116 { | |
1117 printf("TODO: %s %d\n", __func__, __LINE__); | |
1118 return PUGL_FAILURE; | |
1119 } | |
1120 | |
1121 PuglStatus | |
1122 puglSetTransientParent(PuglView* const view, const PuglNativeView parent) | |
1123 { | |
1124 printf("TODO: %s %d\n", __func__, __LINE__); | |
1125 view->transientParent = parent; | |
1126 return PUGL_FAILURE; | |
1127 } | |
1128 | |
1129 PuglStatus | |
1130 puglSetPosition(PuglView* const view, const int x, const int y) | |
1131 { | |
1132 printf("TODO: %s %d\n", __func__, __LINE__); | |
1133 | |
1134 if (x > INT16_MAX || y > INT16_MAX) { | |
1135 return PUGL_BAD_PARAMETER; | |
1136 } | |
1137 | |
1138 view->frame.x = (PuglCoord)x; | |
1139 view->frame.y = (PuglCoord)y; | |
1140 return PUGL_FAILURE; | |
1141 } |