Cleanup
[carla.git] / source / modules / dgl / src / WindowPrivateData.cpp
blob60b81957b6388ad024b311acbccc2e90e696cd23
1 /*
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"
20 #include "pugl.hpp"
22 // #define DGL_DEBUG_EVENTS
24 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
25 # ifdef DISTRHO_PROPER_CPP11_SUPPORT
26 # include <cinttypes>
27 # else
28 # include <inttypes.h>
29 # endif
30 #endif
32 START_NAMESPACE_DGL
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);
38 #else
39 # define DGL_DBG(msg)
40 # define DGL_DBGp(...)
41 # define DGL_DBGF
42 #endif
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));
61 if (view != nullptr)
62 return puglGetScaleFactorFromParent(view);
64 return 1.0;
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));
74 return view;
77 return nullptr;
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);
87 return view;
90 return nullptr;
93 // -----------------------------------------------------------------------
95 Window::PrivateData::PrivateData(Application& a, Window* const s)
96 : app(a),
97 appData(a.pData),
98 self(s),
99 view(appData->world != nullptr ? puglNewView(appData->world) : nullptr),
100 topLevelWidgets(),
101 isClosed(true),
102 isVisible(false),
103 isEmbed(false),
104 usesSizeRequest(false),
105 scaleFactor(getScaleFactorFromParent(view)),
106 autoScaling(false),
107 autoScaleFactor(1.0),
108 minWidth(0),
109 minHeight(0),
110 keepAspectRatio(false),
111 ignoreIdleCallbacks(false),
112 waitingForClipboardData(false),
113 waitingForClipboardEvents(false),
114 clipboardTypeId(0),
115 filenameToRenderInto(nullptr),
116 #ifndef DGL_FILE_BROWSER_DISABLED
117 fileBrowserHandle(nullptr),
118 #endif
119 modal()
121 initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
124 Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* const ppData)
125 : app(a),
126 appData(a.pData),
127 self(s),
128 view(puglNewViewWithTransientParent(appData->world, ppData->view)),
129 topLevelWidgets(),
130 isClosed(true),
131 isVisible(false),
132 isEmbed(false),
133 usesSizeRequest(false),
134 scaleFactor(ppData->scaleFactor),
135 autoScaling(false),
136 autoScaleFactor(1.0),
137 minWidth(0),
138 minHeight(0),
139 keepAspectRatio(false),
140 ignoreIdleCallbacks(false),
141 waitingForClipboardData(false),
142 waitingForClipboardEvents(false),
143 clipboardTypeId(0),
144 filenameToRenderInto(nullptr),
145 #ifndef DGL_FILE_BROWSER_DISABLED
146 fileBrowserHandle(nullptr),
147 #endif
148 modal(ppData)
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)
156 : app(a),
157 appData(a.pData),
158 self(s),
159 view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)),
160 topLevelWidgets(),
161 isClosed(parentWindowHandle == 0),
162 isVisible(parentWindowHandle != 0),
163 isEmbed(parentWindowHandle != 0),
164 usesSizeRequest(false),
165 scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)),
166 autoScaling(false),
167 autoScaleFactor(1.0),
168 minWidth(0),
169 minHeight(0),
170 keepAspectRatio(false),
171 ignoreIdleCallbacks(false),
172 waitingForClipboardData(false),
173 waitingForClipboardEvents(false),
174 clipboardTypeId(0),
175 filenameToRenderInto(nullptr),
176 #ifndef DGL_FILE_BROWSER_DISABLED
177 fileBrowserHandle(nullptr),
178 #endif
179 modal()
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)
188 : app(a),
189 appData(a.pData),
190 self(s),
191 view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)),
192 topLevelWidgets(),
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)),
198 autoScaling(false),
199 autoScaleFactor(1.0),
200 minWidth(0),
201 minHeight(0),
202 keepAspectRatio(false),
203 ignoreIdleCallbacks(false),
204 waitingForClipboardData(false),
205 waitingForClipboardEvents(false),
206 clipboardTypeId(0),
207 filenameToRenderInto(nullptr),
208 #ifndef DGL_FILE_BROWSER_DISABLED
209 fileBrowserHandle(nullptr),
210 #endif
211 modal()
213 if (isEmbed)
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);
225 if (view == nullptr)
226 return;
228 if (isEmbed)
230 #ifndef DGL_FILE_BROWSER_DISABLED
231 if (fileBrowserHandle != nullptr)
232 fileBrowserClose(fileBrowserHandle);
233 #endif
234 puglHide(view);
235 appData->oneWindowClosed();
236 isClosed = true;
237 isVisible = false;
240 puglFreeView(view);
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));
251 if (view == nullptr)
253 d_stderr2("Failed to create Pugl view, everything will fail!");
254 return;
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);
262 #if DGL_USE_RGBA
263 puglSetViewHint(view, PUGL_DEPTH_BITS, 24);
264 #else
265 puglSetViewHint(view, PUGL_DEPTH_BITS, 16);
266 #endif
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);
275 #else
276 puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_TRUE);
277 puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2);
278 #endif
280 // PUGL_SAMPLES ??
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()
289 if (view == nullptr)
290 return false;
292 // create view now, as a few methods we allow devs to use require it
293 if (puglRealize(view) != PUGL_SUCCESS)
295 view = nullptr;
296 d_stderr2("Failed to realize Pugl view, everything will fail!");
297 return false;
300 if (isEmbed)
302 appData->oneWindowShown();
303 puglShow(view);
306 return true;
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)
317 return;
319 isClosed = true;
320 hide();
321 appData->oneWindowClosed();
324 // -----------------------------------------------------------------------
326 void Window::PrivateData::show()
328 if (isVisible)
330 DGL_DBG("Window show matches current visible state, ignoring request\n");
331 return;
333 if (isEmbed)
335 DGL_DBG("Window show cannot be called when embedded\n");
336 return;
339 DGL_DBG("Window show called\n");
341 if (view == nullptr)
342 return;
344 if (isClosed)
346 isClosed = false;
347 appData->oneWindowShown();
349 // FIXME
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);
357 #else
358 puglShow(view);
359 #endif
361 else
363 #ifdef DISTRHO_OS_WINDOWS
364 puglWin32RestoreWindow(view);
365 #else
366 puglShow(view);
367 #endif
370 isVisible = true;
373 void Window::PrivateData::hide()
375 if (isEmbed)
377 DGL_DBG("Window hide cannot be called when embedded\n");
378 return;
380 if (! isVisible)
382 DGL_DBG("Window hide matches current visible state, ignoring request\n");
383 return;
386 DGL_DBG("Window hide called\n");
388 if (modal.enabled)
389 stopModal();
391 #ifndef DGL_FILE_BROWSER_DISABLED
392 if (fileBrowserHandle != nullptr)
394 fileBrowserClose(fileBrowserHandle);
395 fileBrowserHandle = nullptr;
397 #endif
399 puglHide(view);
401 isVisible = false;
404 // -----------------------------------------------------------------------
406 void Window::PrivateData::focus()
408 if (view == nullptr)
409 return;
411 if (! isEmbed)
412 puglRaiseWindow(view);
414 puglGrabFocus(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;
439 #endif
442 // -----------------------------------------------------------------------
443 // idle callback stuff
445 bool Window::PrivateData::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
447 if (ignoreIdleCallbacks)
448 return false;
450 if (timerFrequencyInMs == 0)
452 appData->idleCallbacks.push_back(callback);
453 return true;
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)
462 return false;
464 if (std::find(appData->idleCallbacks.begin(),
465 appData->idleCallbacks.end(), callback) != appData->idleCallbacks.end())
467 appData->idleCallbacks.remove(callback);
468 return true;
471 return puglStopTimer(view, (uintptr_t)callback) == PUGL_SUCCESS;
474 #ifndef DGL_FILE_BROWSER_DISABLED
475 // -----------------------------------------------------------------------
476 // file handling
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,
491 options2);
493 return fileBrowserHandle != nullptr;
495 #endif // ! DGL_FILE_BROWSER_DISABLED
497 // -----------------------------------------------------------------------
498 // modal handling
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();
513 show();
515 #ifdef DISTRHO_OS_MAC
516 puglMacOSAddChildWindow(modal.parent->view, view);
517 #endif
519 DGL_DBG("Ok\n");
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)
531 return;
532 if (modal.parent->modal.child != this)
533 return;
535 #ifdef DISTRHO_OS_MAC
536 puglMacOSRemoveChildWindow(modal.parent->view, view);
537 #endif
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();
550 DGL_DBG("Ok\n");
553 void Window::PrivateData::runAsModal(const bool blockWait)
555 DGL_DBGp("Window::PrivateData::runAsModal %i\n", blockWait);
556 startModal();
558 if (blockWait)
560 DISTRHO_SAFE_ASSERT_RETURN(appData->isStandalone,);
562 while (isVisible && modal.enabled)
563 appData->idle(10);
565 stopModal();
567 else
569 appData->idle(0);
573 // -----------------------------------------------------------------------
574 // pugl events
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);
582 if (autoScaling)
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);
609 #endif
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));
635 std::free(filename);
637 #endif
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())
654 return;
656 #endif
658 if (modal.enabled)
659 stopModal();
661 if (modal.child != nullptr)
663 modal.child->close();
664 modal.child = nullptr;
667 close();
670 void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode)
672 DGL_DBGp("onPuglFocus : %i %i | %i\n", focus, mode, isClosed);
674 if (isClosed)
675 return;
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))
696 break;
698 #endif
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))
714 break;
716 #endif
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))
732 break;
734 #endif
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))
750 break;
752 #endif
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))
768 break;
770 #endif
773 const void* Window::PrivateData::getClipboard(size_t& dataSize)
775 clipboardTypeId = 0;
776 waitingForClipboardData = true,
777 waitingForClipboardEvents = true;
779 // begin clipboard dance here
780 if (puglPaste(view) != PUGL_SUCCESS)
782 dataSize = 0;
783 waitingForClipboardEvents = false;
784 return nullptr;
787 #ifdef DGL_USING_X11
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)
793 break;
795 #endif
797 if (clipboardTypeId == 0)
799 dataSize = 0;
800 waitingForClipboardEvents = false;
801 return nullptr;
804 #ifdef DGL_USING_X11
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)
810 break;
812 #endif
814 if (clipboardTypeId == 0)
816 dataSize = 0;
817 waitingForClipboardEvents = false;
818 return nullptr;
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;
834 return 0;
837 void Window::PrivateData::onClipboardData(const uint32_t typeId)
839 if (clipboardTypeId != typeId)
840 clipboardTypeId = 0;
842 waitingForClipboardData = false;
845 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
846 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose);
847 #endif
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);
856 #endif
858 if (pData->waitingForClipboardEvents)
860 switch (event->type)
862 case PUGL_UPDATE:
863 case PUGL_EXPOSE:
864 case PUGL_FOCUS_IN:
865 case PUGL_FOCUS_OUT:
866 case PUGL_KEY_PRESS:
867 case PUGL_KEY_RELEASE:
868 case PUGL_TEXT:
869 case PUGL_POINTER_IN:
870 case PUGL_POINTER_OUT:
871 case PUGL_BUTTON_PRESS:
872 case PUGL_BUTTON_RELEASE:
873 case PUGL_MOTION:
874 case PUGL_SCROLL:
875 case PUGL_TIMER:
876 case PUGL_LOOP_ENTER:
877 case PUGL_LOOP_LEAVE:
878 return PUGL_SUCCESS;
879 case PUGL_DATA_OFFER:
880 case PUGL_DATA:
881 break;
882 default:
883 d_stdout("Got event %d while waitingForClipboardEvents", event->type);
884 break;
888 switch (event->type)
890 ///< No event
891 case PUGL_NOTHING:
892 break;
894 ///< View created, a #PuglEventCreate
895 case PUGL_CREATE:
896 #ifdef DGL_USING_X11
897 if (! pData->isEmbed)
898 puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone);
899 #endif
900 break;
902 ///< View destroyed, a #PuglEventDestroy
903 case PUGL_DESTROY:
904 break;
906 ///< View moved/resized, a #PuglEventConfigure
907 case PUGL_CONFIGURE:
908 // unused x, y (double)
909 pData->onPuglConfigure(event->configure.width, event->configure.height);
910 break;
912 ///< View made visible, a #PuglEventMap
913 case PUGL_MAP:
914 break;
916 ///< View made invisible, a #PuglEventUnmap
917 case PUGL_UNMAP:
918 break;
920 ///< View ready to draw, a #PuglEventUpdate
921 case PUGL_UPDATE:
922 break;
924 ///< View must be drawn, a #PuglEventExpose
925 case PUGL_EXPOSE:
926 // unused x, y, width, height (double)
927 pData->onPuglExpose();
928 break;
930 ///< View will be closed, a #PuglEventClose
931 case PUGL_CLOSE:
932 pData->onPuglClose();
933 break;
935 ///< Keyboard focus entered view, a #PuglEventFocus
936 case PUGL_FOCUS_IN:
937 ///< Keyboard focus left view, a #PuglEventFocus
938 case PUGL_FOCUS_OUT:
939 pData->onPuglFocus(event->type == PUGL_FOCUS_IN,
940 static_cast<CrossingMode>(event->focus.mode));
941 break;
943 ///< Key pressed, a #PuglEventKey
944 case PUGL_KEY_PRESS:
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);
965 break;
968 ///< Character entered, a #PuglEventText
969 case PUGL_TEXT:
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);
980 break;
983 ///< Pointer entered view, a #PuglEventCrossing
984 case PUGL_POINTER_IN:
985 break;
986 ///< Pointer left view, a #PuglEventCrossing
987 case PUGL_POINTER_OUT:
988 break;
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);
1004 break;
1007 ///< Pointer moved, a #PuglEventMotion
1008 case PUGL_MOTION:
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);
1017 break;
1020 ///< Scrolled, a #PuglEventScroll
1021 case PUGL_SCROLL:
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);
1032 break;
1035 ///< Custom client message, a #PuglEventClient
1036 case PUGL_CLIENT:
1037 break;
1039 ///< Timer triggered, a #PuglEventTimer
1040 case PUGL_TIMER:
1041 if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id))
1042 idleCallback->idleCallback();
1043 break;
1045 ///< Recursive loop entered, a #PuglEventLoopEnter
1046 case PUGL_LOOP_ENTER:
1047 break;
1049 ///< Recursive loop left, a #PuglEventLoopLeave
1050 case PUGL_LOOP_LEAVE:
1051 break;
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);
1057 break;
1059 ///< Data available from clipboard, a #PuglDataEvent
1060 case PUGL_DATA:
1061 pData->onClipboardData(event->data.typeIndex + 1);
1062 break;
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) {
1087 case PUGL_NOTHING:
1088 return 0;
1089 case PUGL_KEY_PRESS:
1090 return PRINT("%sKey press code %3u key U+%04X\n",
1091 prefix,
1092 event->key.keycode,
1093 event->key.key);
1094 case PUGL_KEY_RELEASE:
1095 return PRINT("%sKey release code %3u key U+%04X\n",
1096 prefix,
1097 event->key.keycode,
1098 event->key.key);
1099 case PUGL_TEXT:
1100 return PRINT("%sText entry code %3u char U+%04X (%s)\n",
1101 prefix,
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 " ",
1108 prefix,
1109 event->button.button,
1110 (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ",
1111 event->button.x,
1112 event->button.y) +
1113 printModifiers(event->scroll.state));
1114 case PUGL_SCROLL:
1115 return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ",
1116 prefix,
1117 event->scroll.dx,
1118 event->scroll.dy,
1119 event->scroll.x,
1120 event->scroll.y) +
1121 printModifiers(event->scroll.state));
1122 case PUGL_POINTER_IN:
1123 return PRINT("%sMouse enter at " PFMT "\n",
1124 prefix,
1125 event->crossing.x,
1126 event->crossing.y);
1127 case PUGL_POINTER_OUT:
1128 return PRINT("%sMouse leave at " PFMT "\n",
1129 prefix,
1130 event->crossing.x,
1131 event->crossing.y);
1132 case PUGL_FOCUS_IN:
1133 return PRINT("%sFocus in %i\n",
1134 prefix,
1135 event->focus.mode);
1136 case PUGL_FOCUS_OUT:
1137 return PRINT("%sFocus out %i\n",
1138 prefix,
1139 event->focus.mode);
1140 case PUGL_CLIENT:
1141 return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
1142 prefix,
1143 event->client.data1,
1144 event->client.data2);
1145 case PUGL_TIMER:
1146 return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id);
1147 default:
1148 break;
1151 if (verbose) {
1152 switch (event->type) {
1153 case PUGL_CREATE:
1154 return fprintf(stderr, "%sCreate\n", prefix);
1155 case PUGL_DESTROY:
1156 return fprintf(stderr, "%sDestroy\n", prefix);
1157 case PUGL_MAP:
1158 return fprintf(stderr, "%sMap\n", prefix);
1159 case PUGL_UNMAP:
1160 return fprintf(stderr, "%sUnmap\n", prefix);
1161 case PUGL_UPDATE:
1162 return 0; // fprintf(stderr, "%sUpdate\n", prefix);
1163 case PUGL_CONFIGURE:
1164 return PRINT("%sConfigure " PFMT " " PFMT "\n",
1165 prefix,
1166 event->configure.x,
1167 event->configure.y,
1168 event->configure.width,
1169 event->configure.height);
1170 case PUGL_EXPOSE:
1171 return PRINT("%sExpose " PFMT " " PFMT "\n",
1172 prefix,
1173 event->expose.x,
1174 event->expose.y,
1175 event->expose.width,
1176 event->expose.height);
1177 case PUGL_CLOSE:
1178 return PRINT("%sClose\n", prefix);
1179 case PUGL_MOTION:
1180 return PRINT("%sMouse motion at " PFMT "\n",
1181 prefix,
1182 event->motion.x,
1183 event->motion.y);
1184 default:
1185 return PRINT("%sUnknown event type %d\n", prefix, (int)event->type);
1189 #undef PRINT
1190 #undef PFMT
1191 #undef FFMT
1193 return 0;
1195 #endif
1197 #undef DGL_DBG
1198 #undef DGL_DBGF
1200 // -----------------------------------------------------------------------
1202 END_NAMESPACE_DGL