2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
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.
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.
17 #include "WindowPrivateData.hpp"
18 #include "TopLevelWidgetPrivateData.hpp"
22 // #define DGL_DEBUG_EVENTS
24 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
25 # ifdef DISTRHO_PROPER_CPP11_SUPPORT
28 # include <inttypes.h>
34 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
35 # define DGL_DBG(msg) std::fprintf(stderr, "%s", msg);
36 # define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__);
37 # define DGL_DBGF std::fflush(stderr);
40 # define DGL_DBGp(...)
44 #define DEFAULT_WIDTH 640
45 #define DEFAULT_HEIGHT 480
47 #define FOR_EACH_TOP_LEVEL_WIDGET(it) \
48 for (std::list<TopLevelWidget*>::iterator it = topLevelWidgets.begin(); it != topLevelWidgets.end(); ++it)
50 #define FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) \
51 for (std::list<TopLevelWidget*>::reverse_iterator rit = topLevelWidgets.rbegin(); rit != topLevelWidgets.rend(); ++rit)
53 // -----------------------------------------------------------------------
55 static double getScaleFactorFromParent(const PuglView
* const view
)
57 // allow custom scale for testing
58 if (const char* const scale
= getenv("DPF_SCALE_FACTOR"))
59 return std::max(1.0, std::atof(scale
));
62 return puglGetScaleFactorFromParent(view
);
67 static PuglView
* puglNewViewWithTransientParent(PuglWorld
* const world
, PuglView
* const transientParentView
)
69 DISTRHO_SAFE_ASSERT_RETURN(world
!= nullptr, nullptr);
71 if (PuglView
* const view
= puglNewView(world
))
73 puglSetTransientParent(view
, puglGetNativeView(transientParentView
));
80 static PuglView
* puglNewViewWithParentWindow(PuglWorld
* const world
, const uintptr_t parentWindowHandle
)
82 DISTRHO_SAFE_ASSERT_RETURN(world
!= nullptr, nullptr);
84 if (PuglView
* const view
= puglNewView(world
))
86 puglSetParentWindow(view
, parentWindowHandle
);
93 // -----------------------------------------------------------------------
95 Window::PrivateData::PrivateData(Application
& a
, Window
* const s
)
99 view(appData
->world
!= nullptr ? puglNewView(appData
->world
) : nullptr),
104 usesSizeRequest(false),
105 scaleFactor(getScaleFactorFromParent(view
)),
107 autoScaleFactor(1.0),
110 keepAspectRatio(false),
111 ignoreIdleCallbacks(false),
112 waitingForClipboardData(false),
113 waitingForClipboardEvents(false),
115 filenameToRenderInto(nullptr),
116 #ifndef DGL_FILE_BROWSER_DISABLED
117 fileBrowserHandle(nullptr),
121 initPre(DEFAULT_WIDTH
, DEFAULT_HEIGHT
, false);
124 Window::PrivateData::PrivateData(Application
& a
, Window
* const s
, PrivateData
* const ppData
)
128 view(puglNewViewWithTransientParent(appData
->world
, ppData
->view
)),
133 usesSizeRequest(false),
134 scaleFactor(ppData
->scaleFactor
),
136 autoScaleFactor(1.0),
139 keepAspectRatio(false),
140 ignoreIdleCallbacks(false),
141 waitingForClipboardData(false),
142 waitingForClipboardEvents(false),
144 filenameToRenderInto(nullptr),
145 #ifndef DGL_FILE_BROWSER_DISABLED
146 fileBrowserHandle(nullptr),
150 initPre(DEFAULT_WIDTH
, DEFAULT_HEIGHT
, false);
153 Window::PrivateData::PrivateData(Application
& a
, Window
* const s
,
154 const uintptr_t parentWindowHandle
,
155 const double scale
, const bool resizable
)
159 view(puglNewViewWithParentWindow(appData
->world
, parentWindowHandle
)),
161 isClosed(parentWindowHandle
== 0),
162 isVisible(parentWindowHandle
!= 0),
163 isEmbed(parentWindowHandle
!= 0),
164 usesSizeRequest(false),
165 scaleFactor(scale
!= 0.0 ? scale
: getScaleFactorFromParent(view
)),
167 autoScaleFactor(1.0),
170 keepAspectRatio(false),
171 ignoreIdleCallbacks(false),
172 waitingForClipboardData(false),
173 waitingForClipboardEvents(false),
175 filenameToRenderInto(nullptr),
176 #ifndef DGL_FILE_BROWSER_DISABLED
177 fileBrowserHandle(nullptr),
181 initPre(DEFAULT_WIDTH
, DEFAULT_HEIGHT
, resizable
);
184 Window::PrivateData::PrivateData(Application
& a
, Window
* const s
,
185 const uintptr_t parentWindowHandle
,
186 const uint width
, const uint height
,
187 const double scale
, const bool resizable
, const bool isVST3
)
191 view(puglNewViewWithParentWindow(appData
->world
, parentWindowHandle
)),
193 isClosed(parentWindowHandle
== 0),
194 isVisible(parentWindowHandle
!= 0 && view
!= nullptr),
195 isEmbed(parentWindowHandle
!= 0),
196 usesSizeRequest(isVST3
),
197 scaleFactor(scale
!= 0.0 ? scale
: getScaleFactorFromParent(view
)),
199 autoScaleFactor(1.0),
202 keepAspectRatio(false),
203 ignoreIdleCallbacks(false),
204 waitingForClipboardData(false),
205 waitingForClipboardEvents(false),
207 filenameToRenderInto(nullptr),
208 #ifndef DGL_FILE_BROWSER_DISABLED
209 fileBrowserHandle(nullptr),
214 puglSetParentWindow(view
, parentWindowHandle
);
216 initPre(width
!= 0 ? width
: DEFAULT_WIDTH
, height
!= 0 ? height
: DEFAULT_HEIGHT
, resizable
);
219 Window::PrivateData::~PrivateData()
221 appData
->idleCallbacks
.remove(this);
222 appData
->windows
.remove(self
);
223 std::free(filenameToRenderInto
);
230 #ifndef DGL_FILE_BROWSER_DISABLED
231 if (fileBrowserHandle
!= nullptr)
232 fileBrowserClose(fileBrowserHandle
);
235 appData
->oneWindowClosed();
243 // -----------------------------------------------------------------------
245 void Window::PrivateData::initPre(const uint width
, const uint height
, const bool resizable
)
247 appData
->windows
.push_back(self
);
248 appData
->idleCallbacks
.push_back(this);
249 memset(graphicsContext
, 0, sizeof(graphicsContext
));
253 d_stderr2("Failed to create Pugl view, everything will fail!");
257 puglSetMatchingBackendForCurrentBuild(view
);
258 puglSetHandle(view
, this);
260 puglSetViewHint(view
, PUGL_RESIZABLE
, resizable
? PUGL_TRUE
: PUGL_FALSE
);
261 puglSetViewHint(view
, PUGL_IGNORE_KEY_REPEAT
, PUGL_FALSE
);
263 puglSetViewHint(view
, PUGL_DEPTH_BITS
, 24);
265 puglSetViewHint(view
, PUGL_DEPTH_BITS
, 16);
267 puglSetViewHint(view
, PUGL_STENCIL_BITS
, 8);
269 #if defined(DGL_USE_OPENGL3) || defined(DGL_USE_GLES3)
270 puglSetViewHint(view
, PUGL_USE_COMPAT_PROFILE
, PUGL_FALSE
);
271 puglSetViewHint(view
, PUGL_CONTEXT_VERSION_MAJOR
, 3);
272 #elif defined(DGL_USE_GLES2)
273 puglSetViewHint(view
, PUGL_USE_COMPAT_PROFILE
, PUGL_FALSE
);
274 puglSetViewHint(view
, PUGL_CONTEXT_VERSION_MAJOR
, 2);
276 puglSetViewHint(view
, PUGL_USE_COMPAT_PROFILE
, PUGL_TRUE
);
277 puglSetViewHint(view
, PUGL_CONTEXT_VERSION_MAJOR
, 2);
281 puglSetEventFunc(view
, puglEventCallback
);
283 // setting default size triggers system-level calls, do it last
284 puglSetSizeHint(view
, PUGL_DEFAULT_SIZE
, width
, height
);
287 bool Window::PrivateData::initPost()
292 // create view now, as a few methods we allow devs to use require it
293 if (puglRealize(view
) != PUGL_SUCCESS
)
296 d_stderr2("Failed to realize Pugl view, everything will fail!");
302 appData
->oneWindowShown();
309 // -----------------------------------------------------------------------
311 void Window::PrivateData::close()
313 DGL_DBG("Window close\n");
314 // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData);
316 if (isEmbed
|| isClosed
)
321 appData
->oneWindowClosed();
324 // -----------------------------------------------------------------------
326 void Window::PrivateData::show()
330 DGL_DBG("Window show matches current visible state, ignoring request\n");
335 DGL_DBG("Window show cannot be called when embedded\n");
339 DGL_DBG("Window show called\n");
347 appData
->oneWindowShown();
350 // PuglRect rect = puglGetFrame(view);
351 // puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height));
353 #if defined(DISTRHO_OS_WINDOWS)
354 puglWin32ShowCentered(view
);
355 #elif defined(DISTRHO_OS_MAC)
356 puglMacOSShowCentered(view
);
363 #ifdef DISTRHO_OS_WINDOWS
364 puglWin32RestoreWindow(view
);
373 void Window::PrivateData::hide()
377 DGL_DBG("Window hide cannot be called when embedded\n");
382 DGL_DBG("Window hide matches current visible state, ignoring request\n");
386 DGL_DBG("Window hide called\n");
391 #ifndef DGL_FILE_BROWSER_DISABLED
392 if (fileBrowserHandle
!= nullptr)
394 fileBrowserClose(fileBrowserHandle
);
395 fileBrowserHandle
= nullptr;
404 // -----------------------------------------------------------------------
406 void Window::PrivateData::focus()
412 puglRaiseWindow(view
);
417 // -----------------------------------------------------------------------
419 void Window::PrivateData::setResizable(const bool resizable
)
421 DISTRHO_SAFE_ASSERT_RETURN(! isEmbed
,);
423 DGL_DBG("Window setResizable called\n");
425 puglSetResizable(view
, resizable
);
428 // -----------------------------------------------------------------------
430 void Window::PrivateData::idleCallback()
432 #ifndef DGL_FILE_BROWSER_DISABLED
433 if (fileBrowserHandle
!= nullptr && fileBrowserIdle(fileBrowserHandle
))
435 self
->onFileSelected(fileBrowserGetPath(fileBrowserHandle
));
436 fileBrowserClose(fileBrowserHandle
);
437 fileBrowserHandle
= nullptr;
442 // -----------------------------------------------------------------------
443 // idle callback stuff
445 bool Window::PrivateData::addIdleCallback(IdleCallback
* const callback
, const uint timerFrequencyInMs
)
447 if (ignoreIdleCallbacks
)
450 if (timerFrequencyInMs
== 0)
452 appData
->idleCallbacks
.push_back(callback
);
456 return puglStartTimer(view
, (uintptr_t)callback
, static_cast<double>(timerFrequencyInMs
) / 1000.0) == PUGL_SUCCESS
;
459 bool Window::PrivateData::removeIdleCallback(IdleCallback
* const callback
)
461 if (ignoreIdleCallbacks
)
464 if (std::find(appData
->idleCallbacks
.begin(),
465 appData
->idleCallbacks
.end(), callback
) != appData
->idleCallbacks
.end())
467 appData
->idleCallbacks
.remove(callback
);
471 return puglStopTimer(view
, (uintptr_t)callback
) == PUGL_SUCCESS
;
474 #ifndef DGL_FILE_BROWSER_DISABLED
475 // -----------------------------------------------------------------------
478 bool Window::PrivateData::openFileBrowser(const FileBrowserOptions
& options
)
480 if (fileBrowserHandle
!= nullptr)
481 fileBrowserClose(fileBrowserHandle
);
483 FileBrowserOptions options2
= options
;
485 if (options2
.title
== nullptr)
486 options2
.title
= puglGetWindowTitle(view
);
488 fileBrowserHandle
= fileBrowserCreate(isEmbed
,
489 puglGetNativeView(view
),
490 autoScaling
? autoScaleFactor
: scaleFactor
,
493 return fileBrowserHandle
!= nullptr;
495 #endif // ! DGL_FILE_BROWSER_DISABLED
497 // -----------------------------------------------------------------------
500 void Window::PrivateData::startModal()
502 DGL_DBG("Window modal loop starting..."); DGL_DBGF
;
503 DISTRHO_SAFE_ASSERT_RETURN(modal
.parent
!= nullptr, show());
505 // activate modal mode for this window
506 modal
.enabled
= true;
508 // make parent give focus to us
509 modal
.parent
->modal
.child
= this;
511 // make sure both parent and ourselves are visible
512 modal
.parent
->show();
515 #ifdef DISTRHO_OS_MAC
516 puglMacOSAddChildWindow(modal
.parent
->view
, view
);
522 void Window::PrivateData::stopModal()
524 DGL_DBG("Window modal loop stopping..."); DGL_DBGF
;
526 // deactivate modal mode
527 modal
.enabled
= false;
529 // safety checks, make sure we have a parent and we are currently active as the child to give focus to
530 if (modal
.parent
== nullptr)
532 if (modal
.parent
->modal
.child
!= this)
535 #ifdef DISTRHO_OS_MAC
536 puglMacOSRemoveChildWindow(modal
.parent
->view
, view
);
539 // stop parent from giving focus to us, so it behaves like normal
540 modal
.parent
->modal
.child
= nullptr;
542 // refocus main window after closing child
543 if (! modal
.parent
->isClosed
)
545 const Widget::MotionEvent ev
;
546 modal
.parent
->onPuglMotion(ev
);
547 modal
.parent
->focus();
553 void Window::PrivateData::runAsModal(const bool blockWait
)
555 DGL_DBGp("Window::PrivateData::runAsModal %i\n", blockWait
);
560 DISTRHO_SAFE_ASSERT_RETURN(appData
->isStandalone
,);
562 while (isVisible
&& modal
.enabled
)
573 // -----------------------------------------------------------------------
576 void Window::PrivateData::onPuglConfigure(const double width
, const double height
)
578 DISTRHO_SAFE_ASSERT_INT2_RETURN(width
> 1 && height
> 1, width
, height
,);
580 DGL_DBGp("PUGL: onReshape : %f %f\n", width
, height
);
584 const double scaleHorizontal
= width
/ static_cast<double>(minWidth
);
585 const double scaleVertical
= height
/ static_cast<double>(minHeight
);
586 autoScaleFactor
= scaleHorizontal
< scaleVertical
? scaleHorizontal
: scaleVertical
;
589 const uint uwidth
= static_cast<uint
>(width
+ 0.5);
590 const uint uheight
= static_cast<uint
>(height
+ 0.5);
592 self
->onReshape(uwidth
, uheight
);
594 #ifndef DPF_TEST_WINDOW_CPP
595 FOR_EACH_TOP_LEVEL_WIDGET(it
)
597 TopLevelWidget
* const widget(*it
);
599 /* Some special care here, we call Widget::setSize instead of the TopLevelWidget one.
600 * This is because we want TopLevelWidget::setSize to handle both window and widget size,
601 * but we dont want to change window size here, because we are the window..
602 * So we just call the Widget specific method manually.
604 * Alternatively, we could expose a resize function on the pData, like done with the display function.
605 * But there is nothing extra we need to do in there, so this works fine.
607 ((Widget
*)widget
)->setSize(uwidth
, uheight
);
611 // always repaint after a resize
612 puglPostRedisplay(view
);
615 void Window::PrivateData::onPuglExpose()
617 DGL_DBG("PUGL: onPuglExpose\n");
619 puglOnDisplayPrepare(view
);
621 #ifndef DPF_TEST_WINDOW_CPP
622 FOR_EACH_TOP_LEVEL_WIDGET(it
)
624 TopLevelWidget
* const widget(*it
);
626 if (widget
->isVisible())
627 widget
->pData
->display();
630 if (char* const filename
= filenameToRenderInto
)
632 const PuglRect rect
= puglGetFrame(view
);
633 filenameToRenderInto
= nullptr;
634 renderToPicture(filename
, getGraphicsContext(), static_cast<uint
>(rect
.width
), static_cast<uint
>(rect
.height
));
640 void Window::PrivateData::onPuglClose()
642 DGL_DBG("PUGL: onClose\n");
644 #ifndef DISTRHO_OS_MAC
645 // if we are running as standalone we can prevent closing in certain conditions
646 if (appData
->isStandalone
)
648 // a child window is active, gives focus to it
649 if (modal
.child
!= nullptr)
650 return modal
.child
->focus();
652 // ask window if we should close
653 if (! self
->onClose())
661 if (modal
.child
!= nullptr)
663 modal
.child
->close();
664 modal
.child
= nullptr;
670 void Window::PrivateData::onPuglFocus(const bool focus
, const CrossingMode mode
)
672 DGL_DBGp("onPuglFocus : %i %i | %i\n", focus
, mode
, isClosed
);
677 if (modal
.child
!= nullptr)
678 return modal
.child
->focus();
680 self
->onFocus(focus
, mode
);
683 void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent
& ev
)
685 DGL_DBGp("onPuglKey : %i %u %u\n", ev
.press
, ev
.key
, ev
.keycode
);
687 if (modal
.child
!= nullptr)
688 return modal
.child
->focus();
690 #ifndef DPF_TEST_WINDOW_CPP
691 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit
)
693 TopLevelWidget
* const widget(*rit
);
695 if (widget
->isVisible() && widget
->onKeyboard(ev
))
701 void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent
& ev
)
703 DGL_DBGp("onPuglText : %u %u %s\n", ev
.keycode
, ev
.character
, ev
.string
);
705 if (modal
.child
!= nullptr)
706 return modal
.child
->focus();
708 #ifndef DPF_TEST_WINDOW_CPP
709 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit
)
711 TopLevelWidget
* const widget(*rit
);
713 if (widget
->isVisible() && widget
->onCharacterInput(ev
))
719 void Window::PrivateData::onPuglMouse(const Widget::MouseEvent
& ev
)
721 DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev
.button
, ev
.press
, ev
.pos
.getX(), ev
.pos
.getY());
723 if (modal
.child
!= nullptr)
724 return modal
.child
->focus();
726 #ifndef DPF_TEST_WINDOW_CPP
727 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit
)
729 TopLevelWidget
* const widget(*rit
);
731 if (widget
->isVisible() && widget
->onMouse(ev
))
737 void Window::PrivateData::onPuglMotion(const Widget::MotionEvent
& ev
)
739 DGL_DBGp("onPuglMotion : %f %f\n", ev
.pos
.getX(), ev
.pos
.getY());
741 if (modal
.child
!= nullptr)
742 return modal
.child
->focus();
744 #ifndef DPF_TEST_WINDOW_CPP
745 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit
)
747 TopLevelWidget
* const widget(*rit
);
749 if (widget
->isVisible() && widget
->onMotion(ev
))
755 void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent
& ev
)
757 DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev
.pos
.getX(), ev
.pos
.getY(), ev
.delta
.getX(), ev
.delta
.getY());
759 if (modal
.child
!= nullptr)
760 return modal
.child
->focus();
762 #ifndef DPF_TEST_WINDOW_CPP
763 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit
)
765 TopLevelWidget
* const widget(*rit
);
767 if (widget
->isVisible() && widget
->onScroll(ev
))
773 const void* Window::PrivateData::getClipboard(size_t& dataSize
)
776 waitingForClipboardData
= true,
777 waitingForClipboardEvents
= true;
779 // begin clipboard dance here
780 if (puglPaste(view
) != PUGL_SUCCESS
)
783 waitingForClipboardEvents
= false;
788 // wait for type request, clipboardTypeId must be != 0 to be valid
789 int retry
= static_cast<int>(2 / 0.03);
790 while (clipboardTypeId
== 0 && waitingForClipboardData
&& --retry
>= 0)
792 if (puglX11UpdateWithoutExposures(appData
->world
) != PUGL_SUCCESS
)
797 if (clipboardTypeId
== 0)
800 waitingForClipboardEvents
= false;
805 // wait for actual data (assumes offer was accepted)
806 retry
= static_cast<int>(2 / 0.03);
807 while (waitingForClipboardData
&& --retry
>= 0)
809 if (puglX11UpdateWithoutExposures(appData
->world
) != PUGL_SUCCESS
)
814 if (clipboardTypeId
== 0)
817 waitingForClipboardEvents
= false;
821 waitingForClipboardEvents
= false;
822 return puglGetClipboard(view
, clipboardTypeId
- 1, &dataSize
);
825 uint32_t Window::PrivateData::onClipboardDataOffer()
827 DGL_DBG("onClipboardDataOffer\n");
829 if ((clipboardTypeId
= self
->onClipboardDataOffer()) != 0)
830 return clipboardTypeId
;
832 // stop waiting for data, it was rejected
833 waitingForClipboardData
= false;
837 void Window::PrivateData::onClipboardData(const uint32_t typeId
)
839 if (clipboardTypeId
!= typeId
)
842 waitingForClipboardData
= false;
845 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
846 static int printEvent(const PuglEvent
* event
, const char* prefix
, const bool verbose
);
849 PuglStatus
Window::PrivateData::puglEventCallback(PuglView
* const view
, const PuglEvent
* const event
)
851 Window::PrivateData
* const pData
= (Window::PrivateData
*)puglGetHandle(view
);
852 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
853 if (event
->type
!= PUGL_TIMER
) {
854 printEvent(event
, "pugl event: ", true);
858 if (pData
->waitingForClipboardEvents
)
867 case PUGL_KEY_RELEASE
:
869 case PUGL_POINTER_IN
:
870 case PUGL_POINTER_OUT
:
871 case PUGL_BUTTON_PRESS
:
872 case PUGL_BUTTON_RELEASE
:
876 case PUGL_LOOP_ENTER
:
877 case PUGL_LOOP_LEAVE
:
879 case PUGL_DATA_OFFER
:
883 d_stdout("Got event %d while waitingForClipboardEvents", event
->type
);
894 ///< View created, a #PuglEventCreate
897 if (! pData
->isEmbed
)
898 puglX11SetWindowTypeAndPID(view
, pData
->appData
->isStandalone
);
902 ///< View destroyed, a #PuglEventDestroy
906 ///< View moved/resized, a #PuglEventConfigure
908 // unused x, y (double)
909 pData
->onPuglConfigure(event
->configure
.width
, event
->configure
.height
);
912 ///< View made visible, a #PuglEventMap
916 ///< View made invisible, a #PuglEventUnmap
920 ///< View ready to draw, a #PuglEventUpdate
924 ///< View must be drawn, a #PuglEventExpose
926 // unused x, y, width, height (double)
927 pData
->onPuglExpose();
930 ///< View will be closed, a #PuglEventClose
932 pData
->onPuglClose();
935 ///< Keyboard focus entered view, a #PuglEventFocus
937 ///< Keyboard focus left view, a #PuglEventFocus
939 pData
->onPuglFocus(event
->type
== PUGL_FOCUS_IN
,
940 static_cast<CrossingMode
>(event
->focus
.mode
));
943 ///< Key pressed, a #PuglEventKey
945 ///< Key released, a #PuglEventKey
946 case PUGL_KEY_RELEASE
:
948 // unused x, y, xRoot, yRoot (double)
949 Widget::KeyboardEvent ev
;
950 ev
.mod
= event
->key
.state
;
951 ev
.flags
= event
->key
.flags
;
952 ev
.time
= static_cast<uint
>(event
->key
.time
* 1000.0 + 0.5);
953 ev
.press
= event
->type
== PUGL_KEY_PRESS
;
954 ev
.key
= event
->key
.key
;
955 ev
.keycode
= event
->key
.keycode
;
957 // keyboard events must always be lowercase
958 if (ev
.key
>= 'A' && ev
.key
<= 'Z')
960 ev
.key
+= 'a' - 'A'; // A-Z -> a-z
961 ev
.mod
|= kModifierShift
;
964 pData
->onPuglKey(ev
);
968 ///< Character entered, a #PuglEventText
971 // unused x, y, xRoot, yRoot (double)
972 Widget::CharacterInputEvent ev
;
973 ev
.mod
= event
->text
.state
;
974 ev
.flags
= event
->text
.flags
;
975 ev
.time
= static_cast<uint
>(event
->text
.time
* 1000.0 + 0.5);
976 ev
.keycode
= event
->text
.keycode
;
977 ev
.character
= event
->text
.character
;
978 std::strncpy(ev
.string
, event
->text
.string
, sizeof(ev
.string
));
979 pData
->onPuglText(ev
);
983 ///< Pointer entered view, a #PuglEventCrossing
984 case PUGL_POINTER_IN
:
986 ///< Pointer left view, a #PuglEventCrossing
987 case PUGL_POINTER_OUT
:
990 ///< Mouse button pressed, a #PuglEventButton
991 case PUGL_BUTTON_PRESS
:
992 ///< Mouse button released, a #PuglEventButton
993 case PUGL_BUTTON_RELEASE
:
995 Widget::MouseEvent ev
;
996 ev
.mod
= event
->button
.state
;
997 ev
.flags
= event
->button
.flags
;
998 ev
.time
= static_cast<uint
>(event
->button
.time
* 1000.0 + 0.5);
999 ev
.button
= event
->button
.button
+ 1;
1000 ev
.press
= event
->type
== PUGL_BUTTON_PRESS
;
1001 ev
.pos
= Point
<double>(event
->button
.x
, event
->button
.y
);
1002 ev
.absolutePos
= ev
.pos
;
1003 pData
->onPuglMouse(ev
);
1007 ///< Pointer moved, a #PuglEventMotion
1010 Widget::MotionEvent ev
;
1011 ev
.mod
= event
->motion
.state
;
1012 ev
.flags
= event
->motion
.flags
;
1013 ev
.time
= static_cast<uint
>(event
->motion
.time
* 1000.0 + 0.5);
1014 ev
.pos
= Point
<double>(event
->motion
.x
, event
->motion
.y
);
1015 ev
.absolutePos
= ev
.pos
;
1016 pData
->onPuglMotion(ev
);
1020 ///< Scrolled, a #PuglEventScroll
1023 Widget::ScrollEvent ev
;
1024 ev
.mod
= event
->scroll
.state
;
1025 ev
.flags
= event
->scroll
.flags
;
1026 ev
.time
= static_cast<uint
>(event
->scroll
.time
* 1000.0 + 0.5);
1027 ev
.pos
= Point
<double>(event
->scroll
.x
, event
->scroll
.y
);
1028 ev
.delta
= Point
<double>(event
->scroll
.dx
, event
->scroll
.dy
);
1029 ev
.direction
= static_cast<ScrollDirection
>(event
->scroll
.direction
);
1030 ev
.absolutePos
= ev
.pos
;
1031 pData
->onPuglScroll(ev
);
1035 ///< Custom client message, a #PuglEventClient
1039 ///< Timer triggered, a #PuglEventTimer
1041 if (IdleCallback
* const idleCallback
= reinterpret_cast<IdleCallback
*>(event
->timer
.id
))
1042 idleCallback
->idleCallback();
1045 ///< Recursive loop entered, a #PuglEventLoopEnter
1046 case PUGL_LOOP_ENTER
:
1049 ///< Recursive loop left, a #PuglEventLoopLeave
1050 case PUGL_LOOP_LEAVE
:
1053 ///< Data offered from clipboard, a #PuglDataOfferEvent
1054 case PUGL_DATA_OFFER
:
1055 if (const uint32_t offerTypeId
= pData
->onClipboardDataOffer())
1056 puglAcceptOffer(view
, &event
->offer
, offerTypeId
- 1);
1059 ///< Data available from clipboard, a #PuglDataEvent
1061 pData
->onClipboardData(event
->data
.typeIndex
+ 1);
1065 return PUGL_SUCCESS
;
1068 // -----------------------------------------------------------------------
1070 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
1071 static int printModifiers(const uint32_t mods
)
1073 return fprintf(stderr
, "Modifiers:%s%s%s%s\n",
1074 (mods
& PUGL_MOD_SHIFT
) ? " Shift" : "",
1075 (mods
& PUGL_MOD_CTRL
) ? " Ctrl" : "",
1076 (mods
& PUGL_MOD_ALT
) ? " Alt" : "",
1077 (mods
& PUGL_MOD_SUPER
) ? " Super" : "");
1080 static int printEvent(const PuglEvent
* event
, const char* prefix
, const bool verbose
)
1082 #define FFMT "%6.1f"
1083 #define PFMT FFMT " " FFMT
1084 #define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
1086 switch (event
->type
) {
1089 case PUGL_KEY_PRESS
:
1090 return PRINT("%sKey press code %3u key U+%04X\n",
1094 case PUGL_KEY_RELEASE
:
1095 return PRINT("%sKey release code %3u key U+%04X\n",
1100 return PRINT("%sText entry code %3u char U+%04X (%s)\n",
1102 event
->text
.keycode
,
1103 event
->text
.character
,
1104 event
->text
.string
);
1105 case PUGL_BUTTON_PRESS
:
1106 case PUGL_BUTTON_RELEASE
:
1107 return (PRINT("%sMouse %u %s at " PFMT
" ",
1109 event
->button
.button
,
1110 (event
->type
== PUGL_BUTTON_PRESS
) ? "down" : "up ",
1113 printModifiers(event
->scroll
.state
));
1115 return (PRINT("%sScroll %5.1f %5.1f at " PFMT
" ",
1121 printModifiers(event
->scroll
.state
));
1122 case PUGL_POINTER_IN
:
1123 return PRINT("%sMouse enter at " PFMT
"\n",
1127 case PUGL_POINTER_OUT
:
1128 return PRINT("%sMouse leave at " PFMT
"\n",
1133 return PRINT("%sFocus in %i\n",
1136 case PUGL_FOCUS_OUT
:
1137 return PRINT("%sFocus out %i\n",
1141 return PRINT("%sClient %" PRIXPTR
" %" PRIXPTR
"\n",
1143 event
->client
.data1
,
1144 event
->client
.data2
);
1146 return PRINT("%sTimer %" PRIuPTR
"\n", prefix
, event
->timer
.id
);
1152 switch (event
->type
) {
1154 return fprintf(stderr
, "%sCreate\n", prefix
);
1156 return fprintf(stderr
, "%sDestroy\n", prefix
);
1158 return fprintf(stderr
, "%sMap\n", prefix
);
1160 return fprintf(stderr
, "%sUnmap\n", prefix
);
1162 return 0; // fprintf(stderr, "%sUpdate\n", prefix);
1163 case PUGL_CONFIGURE
:
1164 return PRINT("%sConfigure " PFMT
" " PFMT
"\n",
1168 event
->configure
.width
,
1169 event
->configure
.height
);
1171 return PRINT("%sExpose " PFMT
" " PFMT
"\n",
1175 event
->expose
.width
,
1176 event
->expose
.height
);
1178 return PRINT("%sClose\n", prefix
);
1180 return PRINT("%sMouse motion at " PFMT
"\n",
1185 return PRINT("%sUnknown event type %d\n", prefix
, (int)event
->type
);
1200 // -----------------------------------------------------------------------