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