comparison Framework/Deprecated/GuiAdapter.cpp @ 1502:e5729dab3f67

moving Deprecated/Applications/Generic/GuiAdapter.[cpp|h] to Framework/Deprecated/
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 30 Jun 2020 11:37:34 +0200
parents Deprecated/Applications/Generic/GuiAdapter.cpp@615035c2f3ba
children
comparison
equal deleted inserted replaced
1501:1e381f2596d3 1502:e5729dab3f67
1 /**
2 * Stone of Orthanc
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21 #include "GuiAdapter.h"
22
23 #if ORTHANC_ENABLE_OPENGL == 1
24 # include "../OpenGL/OpenGLIncludes.h"
25 #endif
26
27 #if ORTHANC_ENABLE_SDL == 1
28 # include <SDL_video.h>
29 # include <SDL_render.h>
30 # include <SDL.h>
31 #endif
32
33 #include <Compatibility.h>
34
35 namespace OrthancStone
36 {
37 std::ostream& operator<<(
38 std::ostream& os, const GuiAdapterKeyboardEvent& event)
39 {
40 os << "sym: " << event.sym << " (" << (int)(event.sym[0]) << ") ctrl: " << event.ctrlKey << ", " <<
41 "shift: " << event.shiftKey << ", " <<
42 "alt: " << event.altKey;
43 return os;
44 }
45
46 std::ostream& operator<<(
47 std::ostream& os, const GuiAdapterMouseEvent& event)
48 {
49 os << "targetX: " << event.targetX << " targetY: " << event.targetY << " button: " << event.button
50 << "ctrlKey: " << event.ctrlKey << "shiftKey: " << event.shiftKey << "altKey: " << event.altKey;
51
52 return os;
53 }
54
55 int GuiAdapter::s_instanceCount = 0;
56
57 #if ORTHANC_ENABLE_WASM == 1
58 void GuiAdapter::Run(GuiAdapterRunFunc /*func*/, void* /*cookie*/)
59 {
60 }
61
62 void ConvertFromPlatform(
63 GuiAdapterUiEvent& dest,
64 int eventType,
65 const EmscriptenUiEvent& src)
66 {
67 // no data for now
68 }
69
70 void ConvertFromPlatform(
71 GuiAdapterMouseEvent& dest,
72 int eventType,
73 const EmscriptenMouseEvent& src)
74 {
75 memset(&dest, 0, sizeof(GuiAdapterMouseEvent));
76 switch (eventType)
77 {
78 case EMSCRIPTEN_EVENT_CLICK:
79 LOG(ERROR) << "Emscripten EMSCRIPTEN_EVENT_CLICK is not supported";
80 ORTHANC_ASSERT(false, "Not supported");
81 break;
82 case EMSCRIPTEN_EVENT_MOUSEDOWN:
83 dest.type = GUIADAPTER_EVENT_MOUSEDOWN;
84 break;
85 case EMSCRIPTEN_EVENT_DBLCLICK:
86 dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK;
87 break;
88 case EMSCRIPTEN_EVENT_MOUSEMOVE:
89 dest.type = GUIADAPTER_EVENT_MOUSEMOVE;
90 break;
91 case EMSCRIPTEN_EVENT_MOUSEUP:
92 dest.type = GUIADAPTER_EVENT_MOUSEUP;
93 break;
94 case EMSCRIPTEN_EVENT_WHEEL:
95 dest.type = GUIADAPTER_EVENT_WHEEL;
96 break;
97
98 default:
99 LOG(ERROR) << "Emscripten event: " << eventType << " is not supported";
100 ORTHANC_ASSERT(false, "Not supported");
101 }
102 //dest.timestamp = src.timestamp;
103 //dest.screenX = src.screenX;
104 //dest.screenY = src.screenY;
105 //dest.clientX = src.clientX;
106 //dest.clientY = src.clientY;
107 dest.ctrlKey = src.ctrlKey;
108 dest.shiftKey = src.shiftKey;
109 dest.altKey = src.altKey;
110 //dest.metaKey = src.metaKey;
111 dest.button = src.button;
112 //dest.buttons = src.buttons;
113 //dest.movementX = src.movementX;
114 //dest.movementY = src.movementY;
115 dest.targetX = src.targetX;
116 dest.targetY = src.targetY;
117 //dest.canvasX = src.canvasX;
118 //dest.canvasY = src.canvasY;
119 //dest.padding = src.padding;
120 }
121
122 void ConvertFromPlatform( GuiAdapterWheelEvent& dest, int eventType, const EmscriptenWheelEvent& src)
123 {
124 ConvertFromPlatform(dest.mouse, eventType, src.mouse);
125 dest.deltaX = src.deltaX;
126 dest.deltaY = src.deltaY;
127 switch (src.deltaMode)
128 {
129 case DOM_DELTA_PIXEL:
130 dest.deltaMode = GUIADAPTER_DELTA_PIXEL;
131 break;
132 case DOM_DELTA_LINE:
133 dest.deltaMode = GUIADAPTER_DELTA_LINE;
134 break;
135 case DOM_DELTA_PAGE:
136 dest.deltaMode = GUIADAPTER_DELTA_PAGE;
137 break;
138 default:
139 ORTHANC_ASSERT(false, "Unknown deltaMode: " << src.deltaMode <<
140 " in wheel event...");
141 }
142 dest.deltaMode = src.deltaMode;
143 }
144
145 void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const EmscriptenKeyboardEvent& src)
146 {
147 dest.sym[0] = src.key[0];
148 dest.sym[1] = 0;
149 dest.ctrlKey = src.ctrlKey;
150 dest.shiftKey = src.shiftKey;
151 dest.altKey = src.altKey;
152 }
153
154 template<typename GenericFunc>
155 struct FuncAdapterPayload
156 {
157 std::string canvasCssSelector;
158 void* userData;
159 GenericFunc callback;
160 };
161
162 template<typename GenericFunc,
163 typename GuiAdapterEvent,
164 typename EmscriptenEvent>
165 EM_BOOL OnEventAdapterFunc(
166 int eventType, const EmscriptenEvent* emEvent, void* userData)
167 {
168 // userData is OnMouseWheelFuncAdapterPayload
169 FuncAdapterPayload<GenericFunc>* payload =
170 reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData);
171 // LOG(INFO) << "OnEventAdapterFunc";
172 // LOG(INFO) << "------------------";
173 // LOG(INFO) << "eventType: " << eventType << " wheelEvent: " <<
174 // (int)wheelEvent << " userData: " << userData <<
175 // " payload->userData: " << payload->userData;
176
177 GuiAdapterEvent guiEvent;
178 ConvertFromPlatform(guiEvent, eventType, *emEvent);
179 bool ret = (*(payload->callback))(payload->canvasCssSelector, &guiEvent, payload->userData);
180 return static_cast<EM_BOOL>(ret);
181 }
182
183 template<typename GenericFunc,
184 typename GuiAdapterEvent,
185 typename EmscriptenEvent>
186 EM_BOOL OnEventAdapterFunc2(
187 int /*eventType*/, const EmscriptenEvent* wheelEvent, void* userData)
188 {
189 // userData is OnMouseWheelFuncAdapterPayload
190 FuncAdapterPayload<GenericFunc>* payload =
191 reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData);
192
193 GuiAdapterEvent guiEvent;
194 ConvertFromPlatform(guiEvent, *wheelEvent);
195 bool ret = (*(payload->callback))(payload->canvasCssSelector, &guiEvent, payload->userData);
196 return static_cast<EM_BOOL>(ret);
197 }
198
199 template<typename GenericFunc>
200 EM_BOOL OnEventAdapterFunc3(
201 double time, void* userData)
202 {
203 // userData is OnMouseWheelFuncAdapterPayload
204 FuncAdapterPayload<GenericFunc>* payload =
205 reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData);
206 //std::unique_ptr< FuncAdapterPayload<GenericFunc> > deleter(payload);
207 bool ret = (*(payload->callback))(time, payload->userData);
208 return static_cast<EM_BOOL>(ret);
209 }
210
211 /*
212
213 Explanation
214 ===========
215
216 - in "older" Emscripten, where DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR doesn't exist or is set to 0,
217 the following strings need to be used to register events:
218 - for canvas, the canvas DOM id. In case of <canvas id="mycanvas1" width='640' ...></canvas>", the string needs
219 to be "mycanvas"
220 - for the window (for key events), the string needs to be "#window"
221 - in newer Emscripten where DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR==1 (or maybe is not there anymore, in the
222 future as of 2020-04-20)
223 - for canvas, the canvas DOM id. In case of <canvas id="mycanvas1" width='640' ...></canvas>", the string needs
224 to be "#mycanvas" (notice the "number sign", aka "hash", NOT AKA "sharp", as can be read on https://en.wikipedia.org/wiki/Number_sign)
225 - for the window (for key events), the string needs to be EMSCRIPTEN_EVENT_TARGET_WINDOW. I do not mean
226 "EMSCRIPTEN_EVENT_TARGET_WINDOW", but the #define EMSCRIPTEN_EVENT_TARGET_WINDOW ((const char*)2) that
227 can be found in emscripten/html5.h
228
229 The code below converts the input canvasId (as in the old emscripten) to the emscripten-compliant one, with the
230 following compile condition : #if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR == 1
231
232 If the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR build parameter disappears, you might want to refactor this code
233 or continue to pass the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR compile macro (which is different from the CMake
234 variable)
235
236 What we are doing below:
237 - in older Emscripten, the registration functions will receive "mycanvas" and "#window" and the callbacks will receive
238 the same std::string in their payload ("mycanvas" and "#window")
239
240 - in newer Emscripten, the registration functions will receive "#mycanvas" and EMSCRIPTEN_EVENT_TARGET_WINDOW, but
241 the callbacks will receive "#mycanvas" and "#window" (since it is not possible to store the EMSCRIPTEN_EVENT_TARGET_WINDOW
242 magic value in an std::string, while we still want the callback to be able to change its behavior according to the
243 target element.
244
245 */
246
247 void convertElementTarget(const char*& outCanvasCssSelectorSz, std::string& outCanvasCssSelector, const std::string& canvasId)
248 {
249 // only "#window" can start with a #
250 if (canvasId[0] == '#')
251 {
252 ORTHANC_ASSERT(canvasId == "#window");
253 }
254 #if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR == 1
255 if (canvasId == "#window")
256 {
257 // we store this in the payload so that the callback can
258 outCanvasCssSelector = "#window";
259 outCanvasCssSelectorSz = EMSCRIPTEN_EVENT_TARGET_WINDOW;
260 }
261 else
262 {
263 outCanvasCssSelector = "#" + canvasId;
264 outCanvasCssSelectorSz = outCanvasCssSelector.c_str();
265 }
266 #else
267 if (canvasId == "#window")
268 {
269 // we store this in the payload so that the callback can
270 outCanvasCssSelector = "#window";
271 outCanvasCssSelectorSz = outCanvasCssSelector.c_str();;
272 }
273 else
274 {
275 outCanvasCssSelector = canvasId;
276 outCanvasCssSelectorSz = outCanvasCssSelector.c_str();;
277 }
278 #endif
279 }
280
281 // resize: (const char* target, void* userData, EM_BOOL useCapture, em_ui_callback_func callback)
282 template<
283 typename GenericFunc,
284 typename GuiAdapterEvent,
285 typename EmscriptenEvent,
286 typename EmscriptenSetCallbackFunc>
287 static void SetCallback(
288 EmscriptenSetCallbackFunc emFunc,
289 std::string canvasId, void* userData, bool capture, GenericFunc func)
290 {
291 std::string canvasCssSelector;
292 const char* canvasCssSelectorSz = NULL;
293 convertElementTarget(canvasCssSelectorSz, canvasCssSelector, canvasId);
294
295 // TODO: write RemoveCallback with an int id that gets returned from here
296
297 // create userdata payload
298 std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(new FuncAdapterPayload<GenericFunc>());
299 payload->canvasCssSelector = canvasCssSelector;
300 payload->callback = func;
301 payload->userData = userData;
302 void* userDataRaw = reinterpret_cast<void*>(payload.release());
303
304 // call the registration function
305 (*emFunc)(
306 canvasCssSelectorSz,
307 userDataRaw,
308 static_cast<EM_BOOL>(capture),
309 &OnEventAdapterFunc<GenericFunc, GuiAdapterEvent, EmscriptenEvent>,
310 EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD);
311 }
312
313 template<
314 typename GenericFunc,
315 typename GuiAdapterEvent,
316 typename EmscriptenEvent,
317 typename EmscriptenSetCallbackFunc>
318 static void SetCallback2(
319 EmscriptenSetCallbackFunc emFunc,
320 std::string canvasId, void* userData, bool capture, GenericFunc func)
321 {
322 std::string canvasCssSelector;
323 const char* canvasCssSelectorSz = NULL;
324 convertElementTarget(canvasCssSelectorSz, canvasCssSelector, canvasId);
325
326 // TODO: write RemoveCallback with an int id that gets returned from here
327
328 // create userdata payload
329 std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(new FuncAdapterPayload<GenericFunc>());
330 payload->canvasCssSelector = canvasCssSelector;
331 payload->callback = func;
332 payload->userData = userData;
333 void* userDataRaw = reinterpret_cast<void*>(payload.release());
334
335 // call the registration function
336 (*emFunc)(
337 canvasCssSelectorSz,
338 userDataRaw,
339 static_cast<EM_BOOL>(capture),
340 &OnEventAdapterFunc2<GenericFunc, GuiAdapterEvent, EmscriptenEvent>,
341 EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD);
342 }
343
344 template<
345 typename GenericFunc,
346 typename EmscriptenSetCallbackFunc>
347 static void SetAnimationFrameCallback(
348 EmscriptenSetCallbackFunc emFunc,
349 void* userData, GenericFunc func)
350 {
351 std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(
352 new FuncAdapterPayload<GenericFunc>()
353 );
354 payload->canvasCssSelector = "UNDEFINED";
355 payload->callback = func;
356 payload->userData = userData;
357 void* userDataRaw = reinterpret_cast<void*>(payload.release());
358 (*emFunc)(
359 &OnEventAdapterFunc3<GenericFunc>,
360 userDataRaw);
361 }
362
363 void GuiAdapter::SetWheelCallback(
364 std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func)
365 {
366 SetCallback<OnMouseWheelFunc, GuiAdapterWheelEvent, EmscriptenWheelEvent>(
367 &emscripten_set_wheel_callback_on_thread,
368 canvasId,
369 userData,
370 capture,
371 func);
372 }
373
374
375 void GuiAdapter::SetMouseDblClickCallback(
376 std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
377 {
378 SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
379 &emscripten_set_dblclick_callback_on_thread,
380 canvasId,
381 userData,
382 capture,
383 func);
384 }
385
386
387 void GuiAdapter::SetMouseDownCallback(
388 std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
389 {
390 SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
391 &emscripten_set_mousedown_callback_on_thread,
392 canvasId,
393 userData,
394 capture,
395 func);
396 }
397
398 void GuiAdapter::SetMouseMoveCallback(
399 std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
400 {
401 // LOG(INFO) << "SetMouseMoveCallback -- " << "supplied userData: " <<
402 // userData;
403
404 SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
405 &emscripten_set_mousemove_callback_on_thread,
406 canvasId,
407 userData,
408 capture,
409 func);
410 }
411
412 void GuiAdapter::SetMouseUpCallback(
413 std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
414 {
415 SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
416 &emscripten_set_mouseup_callback_on_thread,
417 canvasId,
418 userData,
419 capture,
420 func);
421 }
422
423 void GuiAdapter::SetKeyDownCallback(
424 std::string canvasId, void* userData, bool capture, OnKeyDownFunc func)
425 {
426 SetCallback2<OnKeyDownFunc, GuiAdapterKeyboardEvent, EmscriptenKeyboardEvent>(
427 &emscripten_set_keydown_callback_on_thread,
428 canvasId,
429 userData,
430 capture,
431 func);
432 }
433
434 void GuiAdapter::SetKeyUpCallback(
435 std::string canvasId, void* userData, bool capture, OnKeyUpFunc func)
436 {
437 SetCallback2<OnKeyUpFunc, GuiAdapterKeyboardEvent, EmscriptenKeyboardEvent>(
438 &emscripten_set_keyup_callback_on_thread,
439 canvasId,
440 userData,
441 capture,
442 func);
443 }
444
445 #if 0
446 // useless under Wasm where canvas resize is handled automatically
447 void GuiAdapter::SetResizeCallback(
448 std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func)
449 {
450 SetCallback<OnWindowResizeFunc, GuiAdapterUiEvent, EmscriptenUiEvent>(
451 &emscripten_set_resize_callback_on_thread,
452 canvasId,
453 userData,
454 capture,
455 func);
456 }
457 #endif
458
459 void GuiAdapter::RequestAnimationFrame(
460 OnAnimationFrameFunc func, void* userData)
461 {
462 SetAnimationFrameCallback<OnAnimationFrameFunc>(
463 &emscripten_request_animation_frame_loop,
464 userData,
465 func);
466 }
467
468 #if 0
469 void GuiAdapter::SetKeyDownCallback(
470 std::string canvasId, void* userData, bool capture, OnKeyDownFunc func)
471 {
472 emscripten_set_keydown_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func);
473 }
474 void GuiAdapter::SetKeyUpCallback(
475 std::string canvasId, void* userData, bool capture, OnKeyUpFunc func)
476 {
477 emscripten_set_keyup_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func);
478 }
479
480 // handled from within WebAssemblyViewport
481 //void GuiAdapter::SetResizeCallback(std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func)
482 //{
483 // emscripten_set_resize_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func);
484 //}
485
486 void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData)
487 {
488 emscripten_request_animation_frame_loop(func, userData);
489 }
490 #endif
491
492
493 #else
494
495 // SDL ONLY
496 void ConvertFromPlatform(GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source)
497 {
498 memset(&dest, 0, sizeof(GuiAdapterMouseEvent));
499 switch (source.type)
500 {
501 case SDL_MOUSEBUTTONDOWN:
502 if (source.button.clicks == 1) {
503 dest.type = GUIADAPTER_EVENT_MOUSEDOWN;
504 } else if (source.button.clicks == 2) {
505 dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK;
506 } else {
507 dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK;
508 LOG(WARNING) << "Multiple-click ignored.";
509 }
510 break;
511 case SDL_MOUSEMOTION:
512 dest.type = GUIADAPTER_EVENT_MOUSEMOVE;
513 break;
514 case SDL_MOUSEBUTTONUP:
515 dest.type = GUIADAPTER_EVENT_MOUSEUP;
516 break;
517 case SDL_MOUSEWHEEL:
518 dest.type = GUIADAPTER_EVENT_WHEEL;
519 break;
520 default:
521 LOG(ERROR) << "SDL event: " << source.type << " is not supported";
522 ORTHANC_ASSERT(false, "Not supported");
523 }
524 //dest.timestamp = src.timestamp;
525 //dest.screenX = src.screenX;
526 //dest.screenY = src.screenY;
527 //dest.clientX = src.clientX;
528 //dest.clientY = src.clientY;
529 dest.ctrlKey = ctrlPressed;
530 dest.shiftKey = shiftPressed;
531 dest.altKey = altPressed;
532 //dest.metaKey = src.metaKey;
533 switch (source.button.button)
534 {
535 case SDL_BUTTON_MIDDLE:
536 dest.button =GUIADAPTER_MOUSEBUTTON_MIDDLE;
537 break;
538
539 case SDL_BUTTON_RIGHT:
540 dest.button = GUIADAPTER_MOUSEBUTTON_RIGHT;
541 break;
542
543 case SDL_BUTTON_LEFT:
544 dest.button = GUIADAPTER_MOUSEBUTTON_LEFT;
545 break;
546
547 default:
548 break;
549 }
550 //dest.buttons = src.buttons;
551 //dest.movementX = src.movementX;
552 //dest.movementY = src.movementY;
553 dest.targetX = source.button.x;
554 dest.targetY = source.button.y;
555 //dest.canvasX = src.canvasX;
556 //dest.canvasY = src.canvasY;
557 //dest.padding = src.padding;
558 }
559
560 void ConvertFromPlatform(
561 GuiAdapterWheelEvent& dest,
562 bool ctrlPressed, bool shiftPressed, bool altPressed,
563 const SDL_Event& source)
564 {
565 ConvertFromPlatform(dest.mouse, ctrlPressed, shiftPressed, altPressed, source);
566 dest.deltaX = source.wheel.x;
567 dest.deltaY = source.wheel.y;
568 }
569
570 void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const SDL_Event& src)
571 {
572 memset(&dest, 0, sizeof(GuiAdapterMouseEvent));
573 switch (src.type)
574 {
575 case SDL_KEYDOWN:
576 dest.type = GUIADAPTER_EVENT_KEYDOWN;
577 break;
578 case SDL_KEYUP:
579 dest.type = GUIADAPTER_EVENT_KEYUP;
580 break;
581 default:
582 LOG(ERROR) << "SDL event: " << src.type << " is not supported";
583 ORTHANC_ASSERT(false, "Not supported");
584 }
585 dest.sym[0] = src.key.keysym.sym;
586 dest.sym[1] = 0;
587
588 if (src.key.keysym.mod & KMOD_CTRL)
589 dest.ctrlKey = true;
590 else
591 dest.ctrlKey = false;
592
593 if (src.key.keysym.mod & KMOD_SHIFT)
594 dest.shiftKey = true;
595 else
596 dest.shiftKey = false;
597
598 if (src.key.keysym.mod & KMOD_ALT)
599 dest.altKey = true;
600 else
601 dest.altKey = false;
602 }
603
604 // SDL ONLY
605 void GuiAdapter::SetSdlResizeCallback(
606 std::string canvasId, void* userData, bool capture, OnSdlWindowResizeFunc func)
607 {
608 resizeHandlers_.push_back(EventHandlerData<OnSdlWindowResizeFunc>(canvasId, func, userData));
609 }
610
611 // SDL ONLY
612 void GuiAdapter::SetMouseDownCallback(
613 std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
614 {
615 mouseDownHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
616 }
617
618 // SDL ONLY
619 void GuiAdapter::SetMouseDblClickCallback(
620 std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
621 {
622 mouseDblCickHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
623 }
624
625 // SDL ONLY
626 void GuiAdapter::SetMouseMoveCallback(
627 std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
628 {
629 mouseMoveHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
630 }
631
632 // SDL ONLY
633 void GuiAdapter::SetMouseUpCallback(
634 std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
635 {
636 mouseUpHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
637 }
638
639 // SDL ONLY
640 void GuiAdapter::SetWheelCallback(
641 std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func)
642 {
643 mouseWheelHandlers_.push_back(EventHandlerData<OnMouseWheelFunc>(canvasId, func, userData));
644 }
645
646 // SDL ONLY
647 void GuiAdapter::SetKeyDownCallback(
648 std::string canvasId, void* userData, bool capture, OnKeyDownFunc func)
649 {
650 keyDownHandlers_.push_back(EventHandlerData<OnKeyDownFunc>(canvasId, func, userData));
651 }
652
653 // SDL ONLY
654 void GuiAdapter::SetKeyUpCallback(
655 std::string canvasId, void* userData, bool capture, OnKeyUpFunc func)
656 {
657 keyUpHandlers_.push_back(EventHandlerData<OnKeyUpFunc>(canvasId, func, userData));
658 }
659
660 // SDL ONLY
661 void GuiAdapter::SetGenericSdlEventCallback(
662 std::string canvasId, void* userData, bool capture, OnSdlEventCallback func)
663 {
664 sdlEventHandlers_.push_back(EventHandlerData<OnSdlEventCallback>(canvasId, func, userData));
665 }
666
667 // SDL ONLY
668 void GuiAdapter::OnAnimationFrame()
669 {
670 std::vector<size_t> disabledAnimationHandlers;
671 for (size_t i = 0; i < animationFrameHandlers_.size(); i++)
672 {
673 // TODO: fix time
674 bool goOn = (*(animationFrameHandlers_[i].first))(0, animationFrameHandlers_[i].second);
675
676 // If the function returns false, we need to emulate what happens in Web
677 // and remove the function from the handlers...
678 if (!goOn)
679 disabledAnimationHandlers.push_back(i);
680 }
681 for (size_t i = 0; i < disabledAnimationHandlers.size(); i++)
682 {
683 ORTHANC_ASSERT(animationFrameHandlers_.begin() + disabledAnimationHandlers[i] < animationFrameHandlers_.end());
684 animationFrameHandlers_.erase(animationFrameHandlers_.begin() + disabledAnimationHandlers[i]);
685 }
686 }
687
688 // SDL ONLY
689 void GuiAdapter::OnResize(unsigned int width, unsigned int height)
690 {
691 for (size_t i = 0; i < resizeHandlers_.size(); i++)
692 {
693 (*(resizeHandlers_[i].func))(
694 resizeHandlers_[i].canvasName, NULL, width, height, resizeHandlers_[i].userData);
695 }
696 }
697
698
699
700 void GuiAdapter::OnSdlGenericEvent(const SDL_Event& sdlEvent)
701 {
702 // Events related to a window are only sent to the related canvas
703 // User events are sent to everyone (we can't filter them here)
704
705 /*
706 SDL_WindowEvent SDL_WINDOWEVENT
707 SDL_KeyboardEvent SDL_KEYDOWN
708 SDL_KEYUP
709 SDL_TextEditingEvent SDL_TEXTEDITING
710 SDL_TextInputEvent SDL_TEXTINPUT
711 SDL_MouseMotionEvent SDL_MOUSEMOTION
712 SDL_MouseButtonEvent SDL_MOUSEBUTTONDOWN
713 SDL_MOUSEBUTTONUP
714 SDL_MouseWheelEvent SDL_MOUSEWHEEL
715 SDL_UserEvent SDL_USEREVENT through ::SDL_LASTEVENT-1
716 */
717
718 // if this string is left empty, it means the message will be sent to
719 // all widgets.
720 // otherwise, it contains the originating message window title
721
722 std::string windowTitle;
723 uint32_t windowId = 0;
724
725 if (sdlEvent.type == SDL_WINDOWEVENT)
726 windowId = sdlEvent.window.windowID;
727 else if (sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP)
728 windowId = sdlEvent.key.windowID;
729 else if (sdlEvent.type == SDL_TEXTEDITING)
730 windowId = sdlEvent.edit.windowID;
731 else if (sdlEvent.type == SDL_TEXTINPUT)
732 windowId = sdlEvent.text.windowID;
733 else if (sdlEvent.type == SDL_MOUSEMOTION)
734 windowId = sdlEvent.motion.windowID;
735 else if (sdlEvent.type == SDL_MOUSEBUTTONDOWN || sdlEvent.type == SDL_MOUSEBUTTONUP)
736 windowId = sdlEvent.button.windowID;
737 else if (sdlEvent.type == SDL_MOUSEWHEEL)
738 windowId = sdlEvent.wheel.windowID;
739 else if (sdlEvent.type >= SDL_USEREVENT && sdlEvent.type <= (SDL_LASTEVENT-1))
740 windowId = sdlEvent.user.windowID;
741
742 if (windowId != 0)
743 {
744 SDL_Window* sdlWindow = SDL_GetWindowFromID(windowId);
745 ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowId << "\" is not a valid SDL window ID!");
746 const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
747 ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowId << "\" has a NULL window title!");
748 windowTitle = windowTitleSz;
749 ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowId << "\" has an empty window title!");
750 }
751
752 for (size_t i = 0; i < sdlEventHandlers_.size(); i++)
753 {
754 // normally, the handlers return a bool indicating whether they
755 // have handled the event or not, but we don't really care about this
756 std::string& canvasName = sdlEventHandlers_[i].canvasName;
757
758 bool sendEvent = true;
759
760 if (windowTitle != "" && (canvasName != windowTitle))
761 sendEvent = false;
762
763 if (sendEvent)
764 {
765 OnSdlEventCallback func = sdlEventHandlers_[i].func;
766 (*func)(canvasName, sdlEvent, sdlEventHandlers_[i].userData);
767 }
768 }
769 }
770
771 // SDL ONLY
772 void GuiAdapter::OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event)
773 {
774 // the SDL window name IS the canvas name ("canvas" is used because this lib
775 // is designed for Wasm
776 SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID);
777 ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!");
778
779 const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
780 ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!");
781
782 std::string windowTitle(windowTitleSz);
783 ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!");
784
785 switch (event.mouse.type)
786 {
787 case GUIADAPTER_EVENT_WHEEL:
788 for (size_t i = 0; i < mouseWheelHandlers_.size(); i++)
789 {
790 if (mouseWheelHandlers_[i].canvasName == windowTitle)
791 (*(mouseWheelHandlers_[i].func))(windowTitle, &event, mouseWheelHandlers_[i].userData);
792 }
793 break;
794 default:
795 ORTHANC_ASSERT(false, "Wrong event.type: " << event.mouse.type << " in GuiAdapter::OnMouseWheelEvent(...)");
796 break;
797 }
798 }
799
800
801 void GuiAdapter::OnKeyboardEvent(uint32_t windowID, const GuiAdapterKeyboardEvent& event)
802 {
803 // only one-letter (ascii) keyboard events supported for now
804 ORTHANC_ASSERT(event.sym[0] != 0);
805 ORTHANC_ASSERT(event.sym[1] == 0);
806
807 SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID);
808 ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!");
809
810 const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
811 ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!");
812
813 std::string windowTitle(windowTitleSz);
814 ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!");
815
816 switch (event.type)
817 {
818 case GUIADAPTER_EVENT_KEYDOWN:
819 for (size_t i = 0; i < keyDownHandlers_.size(); i++)
820 {
821 (*(keyDownHandlers_[i].func))(windowTitle, &event, keyDownHandlers_[i].userData);
822 }
823 break;
824 case GUIADAPTER_EVENT_KEYUP:
825 for (size_t i = 0; i < keyUpHandlers_.size(); i++)
826 {
827 (*(keyUpHandlers_[i].func))(windowTitle, &event, keyUpHandlers_[i].userData);
828 }
829 break;
830 default:
831 ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnKeyboardEvent(...)");
832 break;
833 }
834 }
835
836 // SDL ONLY
837 void GuiAdapter::OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event)
838 {
839 if (windowID == 0)
840 {
841 LOG(WARNING) << "GuiAdapter::OnMouseEvent -- windowID == 0 and event won't be routed!";
842 }
843 else
844 {
845 // the SDL window name IS the canvas name ("canvas" is used because this lib
846 // is designed for Wasm
847 SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID);
848
849 ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!");
850
851 const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
852 ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!");
853
854 std::string windowTitle(windowTitleSz);
855 ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!");
856
857 switch (event.type)
858 {
859 case GUIADAPTER_EVENT_MOUSEDOWN:
860 for (size_t i = 0; i < mouseDownHandlers_.size(); i++)
861 {
862 if (mouseDownHandlers_[i].canvasName == windowTitle)
863 (*(mouseDownHandlers_[i].func))(windowTitle, &event, mouseDownHandlers_[i].userData);
864 }
865 break;
866 case GUIADAPTER_EVENT_MOUSEDBLCLICK:
867 for (size_t i = 0; i < mouseDblCickHandlers_.size(); i++)
868 {
869 if (mouseDblCickHandlers_[i].canvasName == windowTitle)
870 (*(mouseDblCickHandlers_[i].func))(windowTitle, &event, mouseDblCickHandlers_[i].userData);
871 }
872 break;
873 case GUIADAPTER_EVENT_MOUSEMOVE:
874 for (size_t i = 0; i < mouseMoveHandlers_.size(); i++)
875 {
876 if (mouseMoveHandlers_[i].canvasName == windowTitle)
877 (*(mouseMoveHandlers_[i].func))(windowTitle, &event, mouseMoveHandlers_[i].userData);
878 }
879 break;
880 case GUIADAPTER_EVENT_MOUSEUP:
881 for (size_t i = 0; i < mouseUpHandlers_.size(); i++)
882 {
883 if (mouseUpHandlers_[i].canvasName == windowTitle)
884 (*(mouseUpHandlers_[i].func))(windowTitle, &event, mouseUpHandlers_[i].userData);
885 }
886 break;
887 default:
888 ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnMouseEvent(...)");
889 break;
890 }
891 }
892 }
893
894
895 // extern void Debug_SetContextToBeKilled(std::string title);
896 // extern void Debug_SetContextToBeRestored(std::string title);
897
898 // SDL ONLY
899 void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData)
900 {
901 animationFrameHandlers_.push_back(std::make_pair(func, userData));
902 }
903
904 # if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__) /* OpenGL debug is not available on OS X */
905
906 // SDL ONLY
907 static void GLAPIENTRY
908 OpenGLMessageCallback(GLenum source,
909 GLenum type,
910 GLuint id,
911 GLenum severity,
912 GLsizei length,
913 const GLchar * message,
914 const void* userParam)
915 {
916 if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
917 {
918 fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
919 (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
920 type, severity, message);
921 }
922 }
923 # endif
924
925 #if 0
926 // TODO: remove this when generic sdl event handlers are implemented in
927 // the DoseView
928 // SDL ONLY
929 bool GuiAdapter::IsSdlViewPortRefreshEvent(const SDL_Event& event) const
930 {
931 SDL_Window* sdlWindow = SDL_GetWindowFromID(event.window.windowID);
932
933 ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << event.window.windowID << "\" is not a valid SDL window ID!");
934
935 const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
936
937 // now we need to find the DoseView from from the canvas name!
938 // (and retrieve the SdlViewport)
939 boost::shared_ptr<IGuiAdapterWidget> foundWidget;
940 VisitWidgets([&foundWidget, windowTitleSz](auto widget)
941 {
942 if (widget->GetCanvasIdentifier() == std::string(windowTitleSz))
943 foundWidget = widget;
944 });
945 ORTHANC_ASSERT(foundWidget, "The window named: \"" << windowTitleSz << "\" was not found in the registered widgets!");
946 return foundWidget->GetSdlViewport().IsRefreshEvent(event);
947 }
948 #endif
949
950 // SDL ONLY
951 void GuiAdapter::Run(GuiAdapterRunFunc func, void* cookie)
952 {
953 #if 1
954 // TODO: MAKE THIS DYNAMIC !!! See SdlOpenGLViewport vs Cairo in ViewportWrapper
955 # if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__)
956 glEnable(GL_DEBUG_OUTPUT);
957 glDebugMessageCallback(OpenGLMessageCallback, 0);
958 # endif
959 #endif
960
961 // Uint32 SDL_GetWindowID(SDL_Window* window)
962 // SDL_Window* SDL_GetWindowFromID(Uint32 id) // may return NULL
963
964 bool stop = false;
965 while (!stop)
966 {
967 {
968 // TODO: lock all viewports here! (use a scoped object)
969 if(func != NULL)
970 (*func)(cookie);
971 OnAnimationFrame(); // in SDL we must call it
972 }
973
974 while (!stop)
975 {
976 std::vector<SDL_Event> sdlEvents;
977 std::map<Uint32,SDL_Event> userEventsMap;
978
979 SDL_Event sdlEvent;
980
981 // FIRST: collect all pending events
982 while (SDL_PollEvent(&sdlEvent) != 0)
983 {
984 if ( (sdlEvent.type >= SDL_USEREVENT) &&
985 (sdlEvent.type < SDL_LASTEVENT) )
986 {
987 // we don't want to have multiple events with the same event.type
988 userEventsMap[sdlEvent.type] = sdlEvent;
989 }
990 else
991 {
992 sdlEvents.push_back(sdlEvent);
993 }
994 }
995
996 // SECOND: collect all user events
997 for (std::map<Uint32,SDL_Event>::const_iterator it = userEventsMap.begin(); it != userEventsMap.end(); ++it)
998 sdlEvents.push_back(it->second);
999
1000 // now process the events
1001 for (std::vector<SDL_Event>::const_iterator it = sdlEvents.begin(); it != sdlEvents.end(); ++it)
1002 {
1003 const SDL_Event& sdlEvent = *it;
1004 // TODO: lock all viewports here! (use a scoped object)
1005
1006 if (sdlEvent.type == SDL_QUIT)
1007 {
1008 // TODO: call exit callbacks here
1009 stop = true;
1010 break;
1011 }
1012 else if ((sdlEvent.type == SDL_MOUSEMOTION) ||
1013 (sdlEvent.type == SDL_MOUSEBUTTONDOWN) ||
1014 (sdlEvent.type == SDL_MOUSEBUTTONUP))
1015 {
1016 int scancodeCount = 0;
1017 const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
1018 bool ctrlPressed(false);
1019 bool shiftPressed(false);
1020 bool altPressed(false);
1021
1022 if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL])
1023 ctrlPressed = true;
1024 if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL])
1025 ctrlPressed = true;
1026 if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT])
1027 shiftPressed = true;
1028 if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT])
1029 shiftPressed = true;
1030 if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT])
1031 altPressed = true;
1032
1033 GuiAdapterMouseEvent dest;
1034 ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, sdlEvent);
1035 OnMouseEvent(sdlEvent.window.windowID, dest);
1036 #if 0
1037 // for reference, how to create trackers
1038 if (tracker)
1039 {
1040 PointerEvent e;
1041 e.AddPosition(compositor.GetPixelCenterCoordinates(
1042 sdlEvent.button.x, sdlEvent.button.y));
1043 tracker->PointerMove(e);
1044 }
1045 #endif
1046 }
1047 else if (sdlEvent.type == SDL_MOUSEWHEEL)
1048 {
1049
1050 int scancodeCount = 0;
1051 const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
1052 bool ctrlPressed(false);
1053 bool shiftPressed(false);
1054 bool altPressed(false);
1055
1056 if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL])
1057 ctrlPressed = true;
1058 if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL])
1059 ctrlPressed = true;
1060 if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT])
1061 shiftPressed = true;
1062 if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT])
1063 shiftPressed = true;
1064 if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT])
1065 altPressed = true;
1066
1067 GuiAdapterWheelEvent dest;
1068 ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, sdlEvent);
1069 OnMouseWheelEvent(sdlEvent.window.windowID, dest);
1070
1071 //KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount);
1072
1073 //int x, y;
1074 //SDL_GetMouseState(&x, &y);
1075
1076 //if (sdlEvent.wheel.y > 0)
1077 //{
1078 // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers);
1079 //}
1080 //else if (sdlEvent.wheel.y < 0)
1081 //{
1082 // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers);
1083 //}
1084 }
1085 else if (sdlEvent.type == SDL_WINDOWEVENT &&
1086 (sdlEvent.window.event == SDL_WINDOWEVENT_RESIZED ||
1087 sdlEvent.window.event == SDL_WINDOWEVENT_SIZE_CHANGED))
1088 {
1089 #if 0
1090 tracker.reset();
1091 #endif
1092 OnResize(sdlEvent.window.data1, sdlEvent.window.data2);
1093 }
1094 else if (sdlEvent.type == SDL_KEYDOWN && sdlEvent.key.repeat == 0 /* Ignore key bounce */)
1095 {
1096 switch (sdlEvent.key.keysym.sym)
1097 {
1098 case SDLK_f:
1099 // window.GetWindow().ToggleMaximize(); //TODO: move to particular handler
1100 break;
1101
1102 // This commented out code was used to debug the context
1103 // loss/restoring code (2019-08-10)
1104 // case SDLK_k:
1105 // {
1106 // SDL_Window* window = SDL_GetWindowFromID(sdlEvent.window.windowID);
1107 // std::string windowTitle(SDL_GetWindowTitle(window));
1108 // Debug_SetContextToBeKilled(windowTitle);
1109 // }
1110 // break;
1111 // case SDLK_l:
1112 // {
1113 // SDL_Window* window = SDL_GetWindowFromID(sdlEvent.window.windowID);
1114 // std::string windowTitle(SDL_GetWindowTitle(window));
1115 // Debug_SetContextToBeRestored(windowTitle);
1116 // }
1117 // break;
1118
1119 case SDLK_q:
1120 stop = true;
1121 break;
1122
1123 default:
1124 GuiAdapterKeyboardEvent dest;
1125 ConvertFromPlatform(dest, sdlEvent);
1126 OnKeyboardEvent(sdlEvent.window.windowID, dest);
1127 break;
1128 }
1129 }
1130
1131 OnSdlGenericEvent(sdlEvent);
1132 }
1133 SDL_Delay(1);
1134 }
1135 }
1136 }
1137 #endif
1138 }
1139