1 /*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with
6 * or without fee is hereby granted, provided that the above copyright notice and this
7 * permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "WindowPrivateData.hpp"
18 #include "TopLevelWidgetPrivateData.hpp"
19
20 #include "pugl.hpp"
21
22 #define DGL_DEBUG_EVENTS
23
24 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
25 # include <cinttypes>
26 #endif
27
28 START_NAMESPACE_DGL
29
30 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
31 # define DGL_DBG(msg) std::fprintf(stderr, "%s", msg);
32 # define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__);
33 # define DGL_DBGF std::fflush(stderr);
34 #else
35 # define DGL_DBG(msg)
36 # define DGL_DBGp(...)
37 # define DGL_DBGF
38 #endif
39
40 #define DEFAULT_WIDTH 640
41 #define DEFAULT_HEIGHT 480
42
43 // -----------------------------------------------------------------------
44
getDesktopScaleFactor()45 static double getDesktopScaleFactor()
46 {
47 if (const char* const scale = getenv("DPF_SCALE_FACTOR"))
48 return std::max(1.0, std::atof(scale));
49
50 return 1.0;
51 }
52
53 // -----------------------------------------------------------------------
54
PrivateData(Application & a,Window * const s)55 Window::PrivateData::PrivateData(Application& a, Window* const s)
56 : app(a),
57 appData(a.pData),
58 self(s),
59 view(puglNewView(appData->world)),
60 topLevelWidget(nullptr),
61 isClosed(true),
62 isVisible(false),
63 isEmbed(false),
64 scaleFactor(getDesktopScaleFactor()),
65 autoScaling(false),
66 autoScaleFactor(1.0),
67 minWidth(0),
68 minHeight(0),
69 modal()
70 {
71 init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
72 }
73
PrivateData(Application & a,Window * const s,PrivateData * const ppData)74 Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* const ppData)
75 : app(a),
76 appData(a.pData),
77 self(s),
78 view(puglNewView(appData->world)),
79 topLevelWidget(nullptr),
80 isClosed(true),
81 isVisible(false),
82 isEmbed(false),
83 scaleFactor(ppData->scaleFactor),
84 autoScaling(false),
85 autoScaleFactor(1.0),
86 minWidth(0),
87 minHeight(0),
88 modal(ppData)
89 {
90 init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
91
92 puglSetTransientFor(view, puglGetNativeWindow(ppData->view));
93 }
94
PrivateData(Application & a,Window * const s,const uintptr_t parentWindowHandle,const double scale,const bool resizable)95 Window::PrivateData::PrivateData(Application& a, Window* const s,
96 const uintptr_t parentWindowHandle,
97 const double scale, const bool resizable)
98 : app(a),
99 appData(a.pData),
100 self(s),
101 view(puglNewView(appData->world)),
102 topLevelWidget(nullptr),
103 isClosed(parentWindowHandle == 0),
104 isVisible(parentWindowHandle != 0),
105 isEmbed(parentWindowHandle != 0),
106 scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor()),
107 autoScaling(false),
108 autoScaleFactor(1.0),
109 minWidth(0),
110 minHeight(0),
111 modal()
112 {
113 if (isEmbed)
114 {
115 // puglSetDefaultSize(DEFAULT_WIDTH, DEFAULT_HEIGHT, height);
116 puglSetParentWindow(view, parentWindowHandle);
117 }
118
119 init(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable);
120
121 if (isEmbed)
122 {
123 appData->oneWindowShown();
124 puglShow(view);
125 }
126 }
127
PrivateData(Application & a,Window * const s,const uintptr_t parentWindowHandle,const uint width,const uint height,const double scale,const bool resizable)128 Window::PrivateData::PrivateData(Application& a, Window* const s,
129 const uintptr_t parentWindowHandle,
130 const uint width, const uint height,
131 const double scale, const bool resizable)
132 : app(a),
133 appData(a.pData),
134 self(s),
135 view(puglNewView(appData->world)),
136 topLevelWidget(nullptr),
137 isClosed(parentWindowHandle == 0),
138 isVisible(parentWindowHandle != 0),
139 isEmbed(parentWindowHandle != 0),
140 scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor()),
141 autoScaling(false),
142 autoScaleFactor(1.0),
143 minWidth(0),
144 minHeight(0),
145 modal()
146 {
147 if (isEmbed)
148 {
149 puglSetDefaultSize(view, static_cast<int>(width), static_cast<int>(height));
150 puglSetParentWindow(view, parentWindowHandle);
151 }
152
153 init(width, height, resizable);
154
155 if (isEmbed)
156 {
157 appData->oneWindowShown();
158 puglShow(view);
159 }
160 }
161
~PrivateData()162 Window::PrivateData::~PrivateData()
163 {
164 if (isEmbed)
165 {
166 puglHide(view);
167 appData->oneWindowClosed();
168 isClosed = true;
169 isVisible = false;
170 }
171
172 appData->idleCallbacks.remove(this);
173 appData->windows.remove(self);
174
175 if (view != nullptr)
176 puglFreeView(view);
177 }
178
179 // -----------------------------------------------------------------------
180
init(const uint width,const uint height,const bool resizable)181 void Window::PrivateData::init(const uint width, const uint height, const bool resizable)
182 {
183 appData->windows.push_back(self);
184 appData->idleCallbacks.push_back(this);
185 memset(graphicsContext, 0, sizeof(graphicsContext));
186
187 if (view == nullptr)
188 {
189 DGL_DBG("Failed to create Pugl view, everything will fail!\n");
190 return;
191 }
192
193 puglSetMatchingBackendForCurrentBuild(view);
194
195 puglSetHandle(view, this);
196 puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);
197 puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE);
198 puglSetViewHint(view, PUGL_DEPTH_BITS, 16);
199 puglSetViewHint(view, PUGL_STENCIL_BITS, 8);
200 // PUGL_SAMPLES ??
201 puglSetEventFunc(view, puglEventCallback);
202 // #ifndef DGL_FILE_BROWSER_DISABLED
203 // puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback);
204 // #endif
205
206 PuglRect rect = puglGetFrame(view);
207 rect.width = width;
208 rect.height = height;
209 puglSetFrame(view, rect);
210
211 // FIXME this is bad
212 puglRealize(view);
213 puglBackendEnter(view);
214 }
215
216 // -----------------------------------------------------------------------
217
show()218 void Window::PrivateData::show()
219 {
220 if (isVisible)
221 {
222 DGL_DBG("Window show matches current visible state, ignoring request\n");
223 return;
224 }
225 if (isEmbed)
226 {
227 DGL_DBG("Window show cannot be called when embedded\n");
228 return;
229 }
230
231 DGL_DBG("Window show called\n");
232
233 #if 0 && defined(DISTRHO_OS_MAC)
234 // if (mWindow != nullptr)
235 // {
236 // if (mParentWindow != nullptr)
237 // [mParentWindow addChildWindow:mWindow
238 // ordered:NSWindowAbove];
239 // }
240 #endif
241
242 if (isClosed)
243 {
244 isClosed = false;
245 appData->oneWindowShown();
246
247 // FIXME
248 PuglRect rect = puglGetFrame(view);
249 puglSetDefaultSize(view, static_cast<int>(rect.width), static_cast<int>(rect.height));
250 puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height));
251
252 #ifdef DISTRHO_OS_WINDOWS
253 puglWin32ShowWindowCentered(view);
254 #else
255 puglShow(view);
256 #endif
257 }
258 else
259 {
260 #ifdef DISTRHO_OS_WINDOWS
261 puglWin32RestoreWindow(view);
262 #else
263 puglShow(view);
264 #endif
265 }
266
267 isVisible = true;
268 }
269
hide()270 void Window::PrivateData::hide()
271 {
272 if (isEmbed)
273 {
274 DGL_DBG("Window hide cannot be called when embedded\n");
275 return;
276 }
277 if (! isVisible)
278 {
279 DGL_DBG("Window hide matches current visible state, ignoring request\n");
280 return;
281 }
282
283 DGL_DBG("Window hide called\n");
284
285 #if 0 && defined(DISTRHO_OS_MAC)
286 // if (mWindow != nullptr)
287 // {
288 // if (mParentWindow != nullptr)
289 // [mParentWindow removeChildWindow:mWindow];
290 // }
291 #endif
292
293 if (modal.enabled)
294 stopModal();
295
296 puglHide(view);
297
298 isVisible = false;
299 }
300
301 // -----------------------------------------------------------------------
302
close()303 void Window::PrivateData::close()
304 {
305 DGL_DBG("Window close\n");
306 // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData);
307
308 if (isEmbed || isClosed)
309 return;
310
311 isClosed = true;
312 hide();
313 appData->oneWindowClosed();
314 }
315
316 // -----------------------------------------------------------------------
317
focus()318 void Window::PrivateData::focus()
319 {
320 if (! isEmbed)
321 puglRaiseWindow(view);
322
323 puglGrabFocus(view);
324 }
325
326 // -----------------------------------------------------------------------
327
setResizable(const bool resizable)328 void Window::PrivateData::setResizable(const bool resizable)
329 {
330 DISTRHO_SAFE_ASSERT_RETURN(! isEmbed,);
331
332 DGL_DBG("Window setResizable called\n");
333
334 puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);
335 #ifdef DISTRHO_OS_WINDOWS
336 puglWin32SetWindowResizable(view, resizable);
337 #endif
338 }
339
340 // -----------------------------------------------------------------------
341
idleCallback()342 void Window::PrivateData::idleCallback()
343 {
344 // #if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED)
345 // if (fSelectedFile.isNotEmpty())
346 // {
347 // char* const buffer = fSelectedFile.getAndReleaseBuffer();
348 // fView->fileSelectedFunc(fView, buffer);
349 // std::free(buffer);
350 // }
351 // #endif
352
353 // if (modal.enabled && modal.parent != nullptr)
354 // modal.parent->idleCallback();
355 }
356
357 // -----------------------------------------------------------------------
358
addIdleCallback(IdleCallback * const callback,const uint timerFrequencyInMs)359 bool Window::PrivateData::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
360 {
361 if (timerFrequencyInMs == 0)
362 {
363 appData->idleCallbacks.push_back(callback);
364 return true;
365 }
366
367 return puglStartTimer(view, (uintptr_t)callback, static_cast<double>(timerFrequencyInMs) / 1000.0) == PUGL_SUCCESS;
368 }
369
removeIdleCallback(IdleCallback * const callback)370 bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback)
371 {
372 if (std::find(appData->idleCallbacks.begin(),
373 appData->idleCallbacks.end(), callback) != appData->idleCallbacks.end())
374 {
375 appData->idleCallbacks.remove(callback);
376 return true;
377 }
378
379 return puglStopTimer(view, (uintptr_t)callback) == PUGL_SUCCESS;
380 }
381
382 // -----------------------------------------------------------------------
383 // modal handling
384
startModal()385 void Window::PrivateData::startModal()
386 {
387 DGL_DBG("Window modal loop starting..."); DGL_DBGF;
388 DISTRHO_SAFE_ASSERT_RETURN(modal.parent != nullptr, show());
389
390 // activate modal mode for this window
391 modal.enabled = true;
392
393 // make parent give focus to us
394 modal.parent->modal.child = this;
395
396 // FIXME?
397 PuglRect rect = puglGetFrame(view);
398 puglSetDefaultSize(view, static_cast<int>(rect.width), static_cast<int>(rect.height));
399
400 // make sure both parent and ourselves are visible
401 modal.parent->show();
402 show();
403
404 DGL_DBG("Ok\n");
405 }
406
stopModal()407 void Window::PrivateData::stopModal()
408 {
409 DGL_DBG("Window modal loop stopping..."); DGL_DBGF;
410
411 // deactivate modal mode
412 modal.enabled = false;
413
414 // safety checks, make sure we have a parent and we are currently active as the child to give focus to
415 if (modal.parent == nullptr)
416 return;
417 if (modal.parent->modal.child != this)
418 return;
419
420 // stop parent from giving focus to us, so it behaves like normal
421 modal.parent->modal.child = nullptr;
422
423 // the mouse position probably changed since the modal appeared,
424 // so send a mouse motion event to the modal's parent window
425 #if 0
426 #if defined(DISTRHO_OS_HAIKU)
427 // TODO
428 #elif defined(DISTRHO_OS_MAC)
429 // TODO
430 #elif defined(DISTRHO_OS_WINDOWS)
431 // TODO
432 #else
433 int i, wx, wy;
434 uint u;
435 ::Window w;
436 if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True)
437 fModal.parent->onPuglMotion(wx, wy);
438 #endif
439 #endif
440
441 DGL_DBG("Ok\n");
442 }
443
runAsModal(const bool blockWait)444 void Window::PrivateData::runAsModal(const bool blockWait)
445 {
446 DGL_DBGp("Window::PrivateData::runAsModal %i\n", blockWait);
447 startModal();
448
449 if (blockWait)
450 {
451 DISTRHO_SAFE_ASSERT_RETURN(appData->isStandalone,);
452
453 while (isVisible && modal.enabled)
454 appData->idle(10);
455
456 stopModal();
457 }
458 else
459 {
460 appData->idle(0);
461 }
462 }
463
464 // -----------------------------------------------------------------------
465 // pugl events
466
onPuglConfigure(const double width,const double height)467 void Window::PrivateData::onPuglConfigure(const double width, const double height)
468 {
469 DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,);
470
471 DGL_DBGp("PUGL: onReshape : %i %i\n", width, height);
472
473 if (autoScaling)
474 {
475 const double scaleHorizontal = width / static_cast<double>(minWidth);
476 const double scaleVertical = height / static_cast<double>(minHeight);
477 autoScaleFactor = scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical;
478 }
479
480 const uint uwidth = static_cast<uint>(width + 0.5);
481 const uint uheight = static_cast<uint>(height + 0.5);
482 self->onReshape(uwidth, uheight);
483
484 #ifndef DPF_TEST_WINDOW_CPP
485 if (topLevelWidget != nullptr)
486 topLevelWidget->setSize(uwidth, uheight);
487 #endif
488
489 // always repaint after a resize
490 puglPostRedisplay(view);
491 }
492
onPuglExpose()493 void Window::PrivateData::onPuglExpose()
494 {
495 DGL_DBGp("PUGL: onPuglExpose : %p\n", topLevelWidget);
496
497 puglOnDisplayPrepare(view);
498
499 #ifndef DPF_TEST_WINDOW_CPP
500 if (topLevelWidget != nullptr)
501 topLevelWidget->pData->display();
502 #endif
503 }
504
onPuglClose()505 void Window::PrivateData::onPuglClose()
506 {
507 DGL_DBG("PUGL: onClose\n");
508
509 // if we have a parent or running as standalone we can prevent closing in certain conditions
510 if (modal.parent != nullptr || appData->isStandalone)
511 {
512 // parent gives focus to us as modal, prevent closing
513 if (modal.child != nullptr)
514 return modal.child->focus();
515
516 // ask window if we should close
517 if (! self->onClose())
518 return;
519 }
520
521 if (modal.enabled)
522 stopModal();
523
524 if (PrivateData* const child = modal.child)
525 {
526 modal.child = nullptr;
527 child->close();
528 }
529
530 close();
531 }
532
onPuglFocus(const bool focus,const CrossingMode mode)533 void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode)
534 {
535 DGL_DBGp("onPuglFocus : %i %i\n", focus, mode);
536
537 if (isClosed)
538 return;
539
540 if (modal.child != nullptr)
541 return modal.child->focus();
542
543 self->onFocus(focus, mode);
544 }
545
onPuglKey(const Widget::KeyboardEvent & ev)546 void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev)
547 {
548 DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode);
549
550 if (modal.child != nullptr)
551 return modal.child->focus();
552
553 #ifndef DPF_TEST_WINDOW_CPP
554 if (topLevelWidget != nullptr)
555 topLevelWidget->pData->keyboardEvent(ev);
556 #endif
557 }
558
onPuglSpecial(const Widget::SpecialEvent & ev)559 void Window::PrivateData::onPuglSpecial(const Widget::SpecialEvent& ev)
560 {
561 DGL_DBGp("onPuglSpecial : %i %u\n", ev.press, ev.key);
562
563 if (modal.child != nullptr)
564 return modal.child->focus();
565
566 #ifndef DPF_TEST_WINDOW_CPP
567 if (topLevelWidget != nullptr)
568 topLevelWidget->pData->specialEvent(ev);
569 #endif
570 }
571
onPuglText(const Widget::CharacterInputEvent & ev)572 void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev)
573 {
574 DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string);
575
576 if (modal.child != nullptr)
577 return modal.child->focus();
578
579 #ifndef DPF_TEST_WINDOW_CPP
580 if (topLevelWidget != nullptr)
581 topLevelWidget->pData->characterInputEvent(ev);
582 #endif
583 }
584
onPuglMouse(const Widget::MouseEvent & ev)585 void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev)
586 {
587 DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY());
588
589 if (modal.child != nullptr)
590 return modal.child->focus();
591
592 #ifndef DPF_TEST_WINDOW_CPP
593 if (topLevelWidget != nullptr)
594 topLevelWidget->pData->mouseEvent(ev);
595 #endif
596 }
597
onPuglMotion(const Widget::MotionEvent & ev)598 void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev)
599 {
600 DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY());
601
602 if (modal.child != nullptr)
603 return modal.child->focus();
604
605 #ifndef DPF_TEST_WINDOW_CPP
606 if (topLevelWidget != nullptr)
607 topLevelWidget->pData->motionEvent(ev);
608 #endif
609 }
610
onPuglScroll(const Widget::ScrollEvent & ev)611 void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev)
612 {
613 DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY());
614
615 if (modal.child != nullptr)
616 return modal.child->focus();
617
618 #ifndef DPF_TEST_WINDOW_CPP
619 if (topLevelWidget != nullptr)
620 topLevelWidget->pData->scrollEvent(ev);
621 #endif
622 }
623
624 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
625 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose);
626 #endif
627
puglEventCallback(PuglView * const view,const PuglEvent * const event)628 PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event)
629 {
630 Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view);
631 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
632 printEvent(event, "pugl event: ", true);
633 #endif
634
635 switch (event->type)
636 {
637 ///< No event
638 case PUGL_NOTHING:
639 break;
640
641 ///< View created, a #PuglEventCreate
642 case PUGL_CREATE:
643 #ifdef DGL_PUGL_USING_X11
644 if (! pData->isEmbed)
645 puglExtraSetWindowTypeAndPID(view);
646 #endif
647 break;
648
649 ///< View destroyed, a #PuglEventDestroy
650 case PUGL_DESTROY:
651 break;
652
653 ///< View moved/resized, a #PuglEventConfigure
654 case PUGL_CONFIGURE:
655 // unused x, y (double)
656 pData->onPuglConfigure(event->configure.width, event->configure.height);
657 break;
658
659 ///< View made visible, a #PuglEventMap
660 case PUGL_MAP:
661 break;
662
663 ///< View made invisible, a #PuglEventUnmap
664 case PUGL_UNMAP:
665 break;
666
667 ///< View ready to draw, a #PuglEventUpdate
668 case PUGL_UPDATE:
669 break;
670
671 ///< View must be drawn, a #PuglEventExpose
672 case PUGL_EXPOSE:
673 // unused x, y, width, height (double)
674 pData->onPuglExpose();
675 break;
676
677 ///< View will be closed, a #PuglEventClose
678 case PUGL_CLOSE:
679 pData->onPuglClose();
680 break;
681
682 ///< Keyboard focus entered view, a #PuglEventFocus
683 case PUGL_FOCUS_IN:
684 ///< Keyboard focus left view, a #PuglEventFocus
685 case PUGL_FOCUS_OUT:
686 pData->onPuglFocus(event->type == PUGL_FOCUS_IN,
687 static_cast<CrossingMode>(event->focus.mode));
688 break;
689
690 ///< Key pressed, a #PuglEventKey
691 case PUGL_KEY_PRESS:
692 ///< Key released, a #PuglEventKey
693 case PUGL_KEY_RELEASE:
694 {
695 // unused x, y, xRoot, yRoot (double)
696 // TODO special keys?
697 Widget::KeyboardEvent ev;
698 ev.mod = event->key.state;
699 ev.flags = event->key.flags;
700 ev.time = static_cast<uint>(event->key.time * 1000.0 + 0.5);
701 ev.press = event->type == PUGL_KEY_PRESS;
702 ev.key = event->key.key;
703 ev.keycode = event->key.keycode;
704 if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z')
705 ev.key -= 'a' - 'A'; // a-z -> A-Z
706 pData->onPuglKey(ev);
707 break;
708 }
709
710 ///< Character entered, a #PuglEventText
711 case PUGL_TEXT:
712 {
713 // unused x, y, xRoot, yRoot (double)
714 Widget::CharacterInputEvent ev;
715 ev.mod = event->text.state;
716 ev.flags = event->text.flags;
717 ev.time = static_cast<uint>(event->text.time * 1000.0 + 0.5);
718 ev.keycode = event->text.keycode;
719 ev.character = event->text.character;
720 std::strncpy(ev.string, event->text.string, sizeof(ev.string));
721 pData->onPuglText(ev);
722 break;
723 }
724
725 ///< Pointer entered view, a #PuglEventCrossing
726 case PUGL_POINTER_IN:
727 break;
728 ///< Pointer left view, a #PuglEventCrossing
729 case PUGL_POINTER_OUT:
730 break;
731
732 ///< Mouse button pressed, a #PuglEventButton
733 case PUGL_BUTTON_PRESS:
734 ///< Mouse button released, a #PuglEventButton
735 case PUGL_BUTTON_RELEASE:
736 {
737 Widget::MouseEvent ev;
738 ev.mod = event->button.state;
739 ev.flags = event->button.flags;
740 ev.time = static_cast<uint>(event->button.time * 1000.0 + 0.5);
741 ev.button = event->button.button;
742 ev.press = event->type == PUGL_BUTTON_PRESS;
743 ev.pos = Point<double>(event->button.x, event->button.y);
744 pData->onPuglMouse(ev);
745 break;
746 }
747
748 ///< Pointer moved, a #PuglEventMotion
749 case PUGL_MOTION:
750 {
751 Widget::MotionEvent ev;
752 ev.mod = event->motion.state;
753 ev.flags = event->motion.flags;
754 ev.time = static_cast<uint>(event->motion.time * 1000.0 + 0.5);
755 ev.pos = Point<double>(event->motion.x, event->motion.y);
756 pData->onPuglMotion(ev);
757 break;
758 }
759
760 ///< Scrolled, a #PuglEventScroll
761 case PUGL_SCROLL:
762 {
763 Widget::ScrollEvent ev;
764 ev.mod = event->scroll.state;
765 ev.flags = event->scroll.flags;
766 ev.time = static_cast<uint>(event->scroll.time * 1000.0 + 0.5);
767 ev.pos = Point<double>(event->scroll.x, event->scroll.y);
768 ev.delta = Point<double>(event->scroll.dx, event->scroll.dy);
769 ev.direction = static_cast<ScrollDirection>(event->scroll.direction);
770 pData->onPuglScroll(ev);
771 break;
772 }
773
774 ///< Custom client message, a #PuglEventClient
775 case PUGL_CLIENT:
776 break;
777
778 ///< Timer triggered, a #PuglEventTimer
779 case PUGL_TIMER:
780 if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id))
781 idleCallback->idleCallback();
782 break;
783
784 ///< Recursive loop entered, a #PuglEventLoopEnter
785 case PUGL_LOOP_ENTER:
786 break;
787
788 ///< Recursive loop left, a #PuglEventLoopLeave
789 case PUGL_LOOP_LEAVE:
790 break;
791 }
792
793 return PUGL_SUCCESS;
794 }
795
796 // -----------------------------------------------------------------------
797
798 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
printModifiers(const uint32_t mods)799 static int printModifiers(const uint32_t mods)
800 {
801 return fprintf(stderr, "Modifiers:%s%s%s%s\n",
802 (mods & PUGL_MOD_SHIFT) ? " Shift" : "",
803 (mods & PUGL_MOD_CTRL) ? " Ctrl" : "",
804 (mods & PUGL_MOD_ALT) ? " Alt" : "",
805 (mods & PUGL_MOD_SUPER) ? " Super" : "");
806 }
807
printEvent(const PuglEvent * event,const char * prefix,const bool verbose)808 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
809 {
810 #define FFMT "%6.1f"
811 #define PFMT FFMT " " FFMT
812 #define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
813
814 switch (event->type) {
815 case PUGL_NOTHING:
816 return 0;
817 case PUGL_KEY_PRESS:
818 return PRINT("%sKey press code %3u key U+%04X\n",
819 prefix,
820 event->key.keycode,
821 event->key.key);
822 case PUGL_KEY_RELEASE:
823 return PRINT("%sKey release code %3u key U+%04X\n",
824 prefix,
825 event->key.keycode,
826 event->key.key);
827 case PUGL_TEXT:
828 return PRINT("%sText entry code %3u char U+%04X (%s)\n",
829 prefix,
830 event->text.keycode,
831 event->text.character,
832 event->text.string);
833 case PUGL_BUTTON_PRESS:
834 case PUGL_BUTTON_RELEASE:
835 return (PRINT("%sMouse %u %s at " PFMT " ",
836 prefix,
837 event->button.button,
838 (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ",
839 event->button.x,
840 event->button.y) +
841 printModifiers(event->scroll.state));
842 case PUGL_SCROLL:
843 return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ",
844 prefix,
845 event->scroll.dx,
846 event->scroll.dy,
847 event->scroll.x,
848 event->scroll.y) +
849 printModifiers(event->scroll.state));
850 case PUGL_POINTER_IN:
851 return PRINT("%sMouse enter at " PFMT "\n",
852 prefix,
853 event->crossing.x,
854 event->crossing.y);
855 case PUGL_POINTER_OUT:
856 return PRINT("%sMouse leave at " PFMT "\n",
857 prefix,
858 event->crossing.x,
859 event->crossing.y);
860 case PUGL_FOCUS_IN:
861 return PRINT("%sFocus in %i\n",
862 prefix,
863 event->focus.mode);
864 case PUGL_FOCUS_OUT:
865 return PRINT("%sFocus out %i\n",
866 prefix,
867 event->focus.mode);
868 case PUGL_CLIENT:
869 return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
870 prefix,
871 event->client.data1,
872 event->client.data2);
873 case PUGL_TIMER:
874 return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id);
875 default:
876 break;
877 }
878
879 if (verbose) {
880 switch (event->type) {
881 case PUGL_CREATE:
882 return fprintf(stderr, "%sCreate\n", prefix);
883 case PUGL_DESTROY:
884 return fprintf(stderr, "%sDestroy\n", prefix);
885 case PUGL_MAP:
886 return fprintf(stderr, "%sMap\n", prefix);
887 case PUGL_UNMAP:
888 return fprintf(stderr, "%sUnmap\n", prefix);
889 case PUGL_UPDATE:
890 return 0; // fprintf(stderr, "%sUpdate\n", prefix);
891 case PUGL_CONFIGURE:
892 return PRINT("%sConfigure " PFMT " " PFMT "\n",
893 prefix,
894 event->configure.x,
895 event->configure.y,
896 event->configure.width,
897 event->configure.height);
898 case PUGL_EXPOSE:
899 return PRINT("%sExpose " PFMT " " PFMT "\n",
900 prefix,
901 event->expose.x,
902 event->expose.y,
903 event->expose.width,
904 event->expose.height);
905 case PUGL_CLOSE:
906 return PRINT("%sClose\n", prefix);
907 case PUGL_MOTION:
908 return PRINT("%sMouse motion at " PFMT "\n",
909 prefix,
910 event->motion.x,
911 event->motion.y);
912 default:
913 return PRINT("%sUnknown event type %d\n", prefix, (int)event->type);
914 }
915 }
916
917 #undef PRINT
918 #undef PFMT
919 #undef FFMT
920
921 return 0;
922 }
923 #endif
924
925 #undef DGL_DBG
926 #undef DGL_DBGF
927
928 // -----------------------------------------------------------------------
929
930 END_NAMESPACE_DGL
931