comparison DPF-Prymula-audioplugins/dpf/dgl/src/WindowPrivateData.cpp @ 3:84e66ea83026

DPF-Prymula-audioplugins-0.231015-2
author prymula <prymula76@outlook.com>
date Mon, 16 Oct 2023 21:53:34 +0200
parents
children
comparison
equal deleted inserted replaced
2:cf2cb71d31dd 3:84e66ea83026
1 /*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2022 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 # ifdef DISTRHO_PROPER_CPP11_SUPPORT
26 # include <cinttypes>
27 # else
28 # include <inttypes.h>
29 # endif
30 #endif
31
32 START_NAMESPACE_DGL
33
34 #ifdef 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
43
44 #define DEFAULT_WIDTH 640
45 #define DEFAULT_HEIGHT 480
46
47 #define FOR_EACH_TOP_LEVEL_WIDGET(it) \
48 for (std::list<TopLevelWidget*>::iterator it = topLevelWidgets.begin(); it != topLevelWidgets.end(); ++it)
49
50 #define FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) \
51 for (std::list<TopLevelWidget*>::reverse_iterator rit = topLevelWidgets.rbegin(); rit != topLevelWidgets.rend(); ++rit)
52
53 // -----------------------------------------------------------------------
54
55 static double getScaleFactorFromParent(const PuglView* const view)
56 {
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));
60
61 if (view != nullptr)
62 return puglGetScaleFactorFromParent(view);
63
64 return 1.0;
65 }
66
67 static PuglView* puglNewViewWithTransientParent(PuglWorld* const world, PuglView* const transientParentView)
68 {
69 DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, nullptr);
70
71 if (PuglView* const view = puglNewView(world))
72 {
73 puglSetTransientParent(view, puglGetNativeView(transientParentView));
74 return view;
75 }
76
77 return nullptr;
78 }
79
80 static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintptr_t parentWindowHandle)
81 {
82 DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, nullptr);
83
84 if (PuglView* const view = puglNewView(world))
85 {
86 puglSetParentWindow(view, parentWindowHandle);
87 return view;
88 }
89
90 return nullptr;
91 }
92
93 // -----------------------------------------------------------------------
94
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()
120 {
121 initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
122 }
123
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)
149 {
150 initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
151 }
152
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()
180 {
181 initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable);
182 }
183
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 usesSizeRequest_)
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(usesSizeRequest_),
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()
212 {
213 if (isEmbed)
214 puglSetParentWindow(view, parentWindowHandle);
215
216 initPre(width != 0 ? width : DEFAULT_WIDTH, height != 0 ? height : DEFAULT_HEIGHT, resizable);
217 }
218
219 Window::PrivateData::~PrivateData()
220 {
221 appData->idleCallbacks.remove(this);
222 appData->windows.remove(self);
223 std::free(filenameToRenderInto);
224
225 if (view == nullptr)
226 return;
227
228 if (isEmbed)
229 {
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;
238 }
239
240 puglFreeView(view);
241 }
242
243 // -----------------------------------------------------------------------
244
245 void Window::PrivateData::initPre(const uint width, const uint height, const bool resizable)
246 {
247 appData->windows.push_back(self);
248 appData->idleCallbacks.push_back(this);
249 memset(graphicsContext, 0, sizeof(graphicsContext));
250
251 if (view == nullptr)
252 {
253 d_stderr2("Failed to create Pugl view, everything will fail!");
254 return;
255 }
256
257 puglSetMatchingBackendForCurrentBuild(view);
258 puglSetHandle(view, this);
259
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);
268
269 // PUGL_SAMPLES ??
270 puglSetEventFunc(view, puglEventCallback);
271
272 // setting default size triggers system-level calls, do it last
273 puglSetSizeHint(view, PUGL_DEFAULT_SIZE, width, height);
274 }
275
276 bool Window::PrivateData::initPost()
277 {
278 if (view == nullptr)
279 return false;
280
281 // create view now, as a few methods we allow devs to use require it
282 if (puglRealize(view) != PUGL_SUCCESS)
283 {
284 view = nullptr;
285 d_stderr2("Failed to realize Pugl view, everything will fail!");
286 return false;
287 }
288
289 if (isEmbed)
290 {
291 appData->oneWindowShown();
292 puglShow(view);
293 }
294
295 return true;
296 }
297
298 // -----------------------------------------------------------------------
299
300 void Window::PrivateData::close()
301 {
302 DGL_DBG("Window close\n");
303 // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData);
304
305 if (isEmbed || isClosed)
306 return;
307
308 isClosed = true;
309 hide();
310 appData->oneWindowClosed();
311 }
312
313 // -----------------------------------------------------------------------
314
315 void Window::PrivateData::show()
316 {
317 if (isVisible)
318 {
319 DGL_DBG("Window show matches current visible state, ignoring request\n");
320 return;
321 }
322 if (isEmbed)
323 {
324 DGL_DBG("Window show cannot be called when embedded\n");
325 return;
326 }
327
328 DGL_DBG("Window show called\n");
329
330 if (view == nullptr)
331 return;
332
333 if (isClosed)
334 {
335 isClosed = false;
336 appData->oneWindowShown();
337
338 // FIXME
339 // PuglRect rect = puglGetFrame(view);
340 // puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height));
341
342 #if defined(DISTRHO_OS_WINDOWS)
343 puglWin32ShowCentered(view);
344 #elif defined(DISTRHO_OS_MAC)
345 puglMacOSShowCentered(view);
346 #else
347 puglShow(view);
348 #endif
349 }
350 else
351 {
352 #ifdef DISTRHO_OS_WINDOWS
353 puglWin32RestoreWindow(view);
354 #else
355 puglShow(view);
356 #endif
357 }
358
359 isVisible = true;
360 }
361
362 void Window::PrivateData::hide()
363 {
364 if (isEmbed)
365 {
366 DGL_DBG("Window hide cannot be called when embedded\n");
367 return;
368 }
369 if (! isVisible)
370 {
371 DGL_DBG("Window hide matches current visible state, ignoring request\n");
372 return;
373 }
374
375 DGL_DBG("Window hide called\n");
376
377 if (modal.enabled)
378 stopModal();
379
380 #ifndef DGL_FILE_BROWSER_DISABLED
381 if (fileBrowserHandle != nullptr)
382 {
383 fileBrowserClose(fileBrowserHandle);
384 fileBrowserHandle = nullptr;
385 }
386 #endif
387
388 puglHide(view);
389
390 isVisible = false;
391 }
392
393 // -----------------------------------------------------------------------
394
395 void Window::PrivateData::focus()
396 {
397 if (view == nullptr)
398 return;
399
400 if (! isEmbed)
401 puglRaiseWindow(view);
402
403 puglGrabFocus(view);
404 }
405
406 // -----------------------------------------------------------------------
407
408 void Window::PrivateData::setResizable(const bool resizable)
409 {
410 DISTRHO_SAFE_ASSERT_RETURN(! isEmbed,);
411
412 DGL_DBG("Window setResizable called\n");
413
414 puglSetResizable(view, resizable);
415 }
416
417 // -----------------------------------------------------------------------
418
419 void Window::PrivateData::idleCallback()
420 {
421 #ifndef DGL_FILE_BROWSER_DISABLED
422 if (fileBrowserHandle != nullptr && fileBrowserIdle(fileBrowserHandle))
423 {
424 self->onFileSelected(fileBrowserGetPath(fileBrowserHandle));
425 fileBrowserClose(fileBrowserHandle);
426 fileBrowserHandle = nullptr;
427 }
428 #endif
429 }
430
431 // -----------------------------------------------------------------------
432 // idle callback stuff
433
434 bool Window::PrivateData::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
435 {
436 if (ignoreIdleCallbacks)
437 return false;
438
439 if (timerFrequencyInMs == 0)
440 {
441 appData->idleCallbacks.push_back(callback);
442 return true;
443 }
444
445 return puglStartTimer(view, (uintptr_t)callback, static_cast<double>(timerFrequencyInMs) / 1000.0) == PUGL_SUCCESS;
446 }
447
448 bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback)
449 {
450 if (ignoreIdleCallbacks)
451 return false;
452
453 if (std::find(appData->idleCallbacks.begin(),
454 appData->idleCallbacks.end(), callback) != appData->idleCallbacks.end())
455 {
456 appData->idleCallbacks.remove(callback);
457 return true;
458 }
459
460 return puglStopTimer(view, (uintptr_t)callback) == PUGL_SUCCESS;
461 }
462
463 #ifndef DGL_FILE_BROWSER_DISABLED
464 // -----------------------------------------------------------------------
465 // file handling
466
467 bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options)
468 {
469 if (fileBrowserHandle != nullptr)
470 fileBrowserClose(fileBrowserHandle);
471
472 FileBrowserOptions options2 = options;
473
474 if (options2.title == nullptr)
475 options2.title = puglGetWindowTitle(view);
476
477 fileBrowserHandle = fileBrowserCreate(isEmbed,
478 puglGetNativeView(view),
479 autoScaling ? autoScaleFactor : scaleFactor,
480 options2);
481
482 return fileBrowserHandle != nullptr;
483 }
484 #endif // ! DGL_FILE_BROWSER_DISABLED
485
486 // -----------------------------------------------------------------------
487 // modal handling
488
489 void Window::PrivateData::startModal()
490 {
491 DGL_DBG("Window modal loop starting..."); DGL_DBGF;
492 DISTRHO_SAFE_ASSERT_RETURN(modal.parent != nullptr, show());
493
494 // activate modal mode for this window
495 modal.enabled = true;
496
497 // make parent give focus to us
498 modal.parent->modal.child = this;
499
500 // make sure both parent and ourselves are visible
501 modal.parent->show();
502 show();
503
504 #ifdef DISTRHO_OS_MAC
505 puglMacOSAddChildWindow(modal.parent->view, view);
506 #endif
507
508 DGL_DBG("Ok\n");
509 }
510
511 void Window::PrivateData::stopModal()
512 {
513 DGL_DBG("Window modal loop stopping..."); DGL_DBGF;
514
515 // deactivate modal mode
516 modal.enabled = false;
517
518 // safety checks, make sure we have a parent and we are currently active as the child to give focus to
519 if (modal.parent == nullptr)
520 return;
521 if (modal.parent->modal.child != this)
522 return;
523
524 #ifdef DISTRHO_OS_MAC
525 puglMacOSRemoveChildWindow(modal.parent->view, view);
526 #endif
527
528 // stop parent from giving focus to us, so it behaves like normal
529 modal.parent->modal.child = nullptr;
530
531 // refocus main window after closing child
532 if (! modal.parent->isClosed)
533 {
534 const Widget::MotionEvent ev;
535 modal.parent->onPuglMotion(ev);
536 modal.parent->focus();
537 }
538
539 DGL_DBG("Ok\n");
540 }
541
542 void Window::PrivateData::runAsModal(const bool blockWait)
543 {
544 DGL_DBGp("Window::PrivateData::runAsModal %i\n", blockWait);
545 startModal();
546
547 if (blockWait)
548 {
549 DISTRHO_SAFE_ASSERT_RETURN(appData->isStandalone,);
550
551 while (isVisible && modal.enabled)
552 appData->idle(10);
553
554 stopModal();
555 }
556 else
557 {
558 appData->idle(0);
559 }
560 }
561
562 // -----------------------------------------------------------------------
563 // pugl events
564
565 void Window::PrivateData::onPuglConfigure(const double width, const double height)
566 {
567 DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,);
568
569 DGL_DBGp("PUGL: onReshape : %f %f\n", width, height);
570
571 if (autoScaling)
572 {
573 const double scaleHorizontal = width / static_cast<double>(minWidth);
574 const double scaleVertical = height / static_cast<double>(minHeight);
575 autoScaleFactor = scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical;
576 }
577
578 const uint uwidth = static_cast<uint>(width + 0.5);
579 const uint uheight = static_cast<uint>(height + 0.5);
580
581 self->onReshape(uwidth, uheight);
582
583 #ifndef DPF_TEST_WINDOW_CPP
584 FOR_EACH_TOP_LEVEL_WIDGET(it)
585 {
586 TopLevelWidget* const widget(*it);
587
588 /* Some special care here, we call Widget::setSize instead of the TopLevelWidget one.
589 * This is because we want TopLevelWidget::setSize to handle both window and widget size,
590 * but we dont want to change window size here, because we are the window..
591 * So we just call the Widget specific method manually.
592 *
593 * Alternatively, we could expose a resize function on the pData, like done with the display function.
594 * But there is nothing extra we need to do in there, so this works fine.
595 */
596 ((Widget*)widget)->setSize(uwidth, uheight);
597 }
598 #endif
599
600 // always repaint after a resize
601 puglPostRedisplay(view);
602 }
603
604 void Window::PrivateData::onPuglExpose()
605 {
606 // DGL_DBG("PUGL: onPuglExpose\n");
607
608 puglOnDisplayPrepare(view);
609
610 #ifndef DPF_TEST_WINDOW_CPP
611 FOR_EACH_TOP_LEVEL_WIDGET(it)
612 {
613 TopLevelWidget* const widget(*it);
614
615 if (widget->isVisible())
616 widget->pData->display();
617 }
618
619 if (char* const filename = filenameToRenderInto)
620 {
621 const PuglRect rect = puglGetFrame(view);
622 filenameToRenderInto = nullptr;
623 renderToPicture(filename, getGraphicsContext(), static_cast<uint>(rect.width), static_cast<uint>(rect.height));
624 std::free(filename);
625 }
626 #endif
627 }
628
629 void Window::PrivateData::onPuglClose()
630 {
631 DGL_DBG("PUGL: onClose\n");
632
633 #ifndef DISTRHO_OS_MAC
634 // if we are running as standalone we can prevent closing in certain conditions
635 if (appData->isStandalone)
636 {
637 // a child window is active, gives focus to it
638 if (modal.child != nullptr)
639 return modal.child->focus();
640
641 // ask window if we should close
642 if (! self->onClose())
643 return;
644 }
645 #endif
646
647 if (modal.enabled)
648 stopModal();
649
650 if (modal.child != nullptr)
651 {
652 modal.child->close();
653 modal.child = nullptr;
654 }
655
656 close();
657 }
658
659 void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode)
660 {
661 DGL_DBGp("onPuglFocus : %i %i | %i\n", focus, mode, isClosed);
662
663 if (isClosed)
664 return;
665
666 if (modal.child != nullptr)
667 return modal.child->focus();
668
669 self->onFocus(focus, mode);
670 }
671
672 void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev)
673 {
674 DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode);
675
676 if (modal.child != nullptr)
677 return modal.child->focus();
678
679 #ifndef DPF_TEST_WINDOW_CPP
680 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
681 {
682 TopLevelWidget* const widget(*rit);
683
684 if (widget->isVisible() && widget->onKeyboard(ev))
685 break;
686 }
687 #endif
688 }
689
690 void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev)
691 {
692 DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string);
693
694 if (modal.child != nullptr)
695 return modal.child->focus();
696
697 #ifndef DPF_TEST_WINDOW_CPP
698 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
699 {
700 TopLevelWidget* const widget(*rit);
701
702 if (widget->isVisible() && widget->onCharacterInput(ev))
703 break;
704 }
705 #endif
706 }
707
708 void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev)
709 {
710 DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY());
711
712 if (modal.child != nullptr)
713 return modal.child->focus();
714
715 #ifndef DPF_TEST_WINDOW_CPP
716 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
717 {
718 TopLevelWidget* const widget(*rit);
719
720 if (widget->isVisible() && widget->onMouse(ev))
721 break;
722 }
723 #endif
724 }
725
726 void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev)
727 {
728 DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY());
729
730 if (modal.child != nullptr)
731 return modal.child->focus();
732
733 #ifndef DPF_TEST_WINDOW_CPP
734 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
735 {
736 TopLevelWidget* const widget(*rit);
737
738 if (widget->isVisible() && widget->onMotion(ev))
739 break;
740 }
741 #endif
742 }
743
744 void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev)
745 {
746 DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY());
747
748 if (modal.child != nullptr)
749 return modal.child->focus();
750
751 #ifndef DPF_TEST_WINDOW_CPP
752 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
753 {
754 TopLevelWidget* const widget(*rit);
755
756 if (widget->isVisible() && widget->onScroll(ev))
757 break;
758 }
759 #endif
760 }
761
762 const void* Window::PrivateData::getClipboard(size_t& dataSize)
763 {
764 clipboardTypeId = 0;
765 waitingForClipboardData = true,
766 waitingForClipboardEvents = true;
767
768 // begin clipboard dance here
769 if (puglPaste(view) != PUGL_SUCCESS)
770 {
771 dataSize = 0;
772 waitingForClipboardEvents = false;
773 return nullptr;
774 }
775
776 #ifdef DGL_USING_X11
777 // wait for type request, clipboardTypeId must be != 0 to be valid
778 int retry = static_cast<int>(2 / 0.03);
779 while (clipboardTypeId == 0 && waitingForClipboardData && --retry >= 0)
780 {
781 if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS)
782 break;
783 }
784 #endif
785
786 if (clipboardTypeId == 0)
787 {
788 dataSize = 0;
789 waitingForClipboardEvents = false;
790 return nullptr;
791 }
792
793 #ifdef DGL_USING_X11
794 // wait for actual data (assumes offer was accepted)
795 retry = static_cast<int>(2 / 0.03);
796 while (waitingForClipboardData && --retry >= 0)
797 {
798 if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS)
799 break;
800 }
801 #endif
802
803 if (clipboardTypeId == 0)
804 {
805 dataSize = 0;
806 waitingForClipboardEvents = false;
807 return nullptr;
808 }
809
810 waitingForClipboardEvents = false;
811 return puglGetClipboard(view, clipboardTypeId - 1, &dataSize);
812 }
813
814 uint32_t Window::PrivateData::onClipboardDataOffer()
815 {
816 DGL_DBG("onClipboardDataOffer\n");
817
818 if ((clipboardTypeId = self->onClipboardDataOffer()) != 0)
819 return clipboardTypeId;
820
821 // stop waiting for data, it was rejected
822 waitingForClipboardData = false;
823 return 0;
824 }
825
826 void Window::PrivateData::onClipboardData(const uint32_t typeId)
827 {
828 if (clipboardTypeId != typeId)
829 clipboardTypeId = 0;
830
831 waitingForClipboardData = false;
832 }
833
834 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
835 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose);
836 #endif
837
838 PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event)
839 {
840 Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view);
841 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
842 if (event->type != PUGL_TIMER) {
843 printEvent(event, "pugl event: ", true);
844 }
845 #endif
846
847 if (pData->waitingForClipboardEvents)
848 {
849 switch (event->type)
850 {
851 case PUGL_UPDATE:
852 case PUGL_EXPOSE:
853 case PUGL_FOCUS_IN:
854 case PUGL_FOCUS_OUT:
855 case PUGL_KEY_PRESS:
856 case PUGL_KEY_RELEASE:
857 case PUGL_TEXT:
858 case PUGL_POINTER_IN:
859 case PUGL_POINTER_OUT:
860 case PUGL_BUTTON_PRESS:
861 case PUGL_BUTTON_RELEASE:
862 case PUGL_MOTION:
863 case PUGL_SCROLL:
864 case PUGL_TIMER:
865 case PUGL_LOOP_ENTER:
866 case PUGL_LOOP_LEAVE:
867 return PUGL_SUCCESS;
868 case PUGL_DATA_OFFER:
869 case PUGL_DATA:
870 break;
871 default:
872 d_stdout("Got event %d while waitingForClipboardEvents", event->type);
873 break;
874 }
875 }
876
877 switch (event->type)
878 {
879 ///< No event
880 case PUGL_NOTHING:
881 break;
882
883 ///< View created, a #PuglEventCreate
884 case PUGL_CREATE:
885 #ifdef DGL_USING_X11
886 if (! pData->isEmbed)
887 puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone);
888 #endif
889 break;
890
891 ///< View destroyed, a #PuglEventDestroy
892 case PUGL_DESTROY:
893 break;
894
895 ///< View moved/resized, a #PuglEventConfigure
896 case PUGL_CONFIGURE:
897 // unused x, y (double)
898 pData->onPuglConfigure(event->configure.width, event->configure.height);
899 break;
900
901 ///< View made visible, a #PuglEventMap
902 case PUGL_MAP:
903 break;
904
905 ///< View made invisible, a #PuglEventUnmap
906 case PUGL_UNMAP:
907 break;
908
909 ///< View ready to draw, a #PuglEventUpdate
910 case PUGL_UPDATE:
911 break;
912
913 ///< View must be drawn, a #PuglEventExpose
914 case PUGL_EXPOSE:
915 // unused x, y, width, height (double)
916 pData->onPuglExpose();
917 break;
918
919 ///< View will be closed, a #PuglEventClose
920 case PUGL_CLOSE:
921 pData->onPuglClose();
922 break;
923
924 ///< Keyboard focus entered view, a #PuglEventFocus
925 case PUGL_FOCUS_IN:
926 ///< Keyboard focus left view, a #PuglEventFocus
927 case PUGL_FOCUS_OUT:
928 pData->onPuglFocus(event->type == PUGL_FOCUS_IN,
929 static_cast<CrossingMode>(event->focus.mode));
930 break;
931
932 ///< Key pressed, a #PuglEventKey
933 case PUGL_KEY_PRESS:
934 ///< Key released, a #PuglEventKey
935 case PUGL_KEY_RELEASE:
936 {
937 // unused x, y, xRoot, yRoot (double)
938 Widget::KeyboardEvent ev;
939 ev.mod = event->key.state;
940 ev.flags = event->key.flags;
941 ev.time = static_cast<uint>(event->key.time * 1000.0 + 0.5);
942 ev.press = event->type == PUGL_KEY_PRESS;
943 ev.key = event->key.key;
944 ev.keycode = event->key.keycode;
945
946 // keyboard events must always be lowercase
947 if (ev.key >= 'A' && ev.key <= 'Z')
948 {
949 ev.key += 'a' - 'A'; // A-Z -> a-z
950 ev.mod |= kModifierShift;
951 }
952
953 pData->onPuglKey(ev);
954 break;
955 }
956
957 ///< Character entered, a #PuglEventText
958 case PUGL_TEXT:
959 {
960 // unused x, y, xRoot, yRoot (double)
961 Widget::CharacterInputEvent ev;
962 ev.mod = event->text.state;
963 ev.flags = event->text.flags;
964 ev.time = static_cast<uint>(event->text.time * 1000.0 + 0.5);
965 ev.keycode = event->text.keycode;
966 ev.character = event->text.character;
967 std::strncpy(ev.string, event->text.string, sizeof(ev.string));
968 pData->onPuglText(ev);
969 break;
970 }
971
972 ///< Pointer entered view, a #PuglEventCrossing
973 case PUGL_POINTER_IN:
974 break;
975 ///< Pointer left view, a #PuglEventCrossing
976 case PUGL_POINTER_OUT:
977 break;
978
979 ///< Mouse button pressed, a #PuglEventButton
980 case PUGL_BUTTON_PRESS:
981 ///< Mouse button released, a #PuglEventButton
982 case PUGL_BUTTON_RELEASE:
983 {
984 Widget::MouseEvent ev;
985 ev.mod = event->button.state;
986 ev.flags = event->button.flags;
987 ev.time = static_cast<uint>(event->button.time * 1000.0 + 0.5);
988 ev.button = event->button.button + 1;
989 ev.press = event->type == PUGL_BUTTON_PRESS;
990 ev.pos = Point<double>(event->button.x, event->button.y);
991 ev.absolutePos = ev.pos;
992 pData->onPuglMouse(ev);
993 break;
994 }
995
996 ///< Pointer moved, a #PuglEventMotion
997 case PUGL_MOTION:
998 {
999 Widget::MotionEvent ev;
1000 ev.mod = event->motion.state;
1001 ev.flags = event->motion.flags;
1002 ev.time = static_cast<uint>(event->motion.time * 1000.0 + 0.5);
1003 ev.pos = Point<double>(event->motion.x, event->motion.y);
1004 ev.absolutePos = ev.pos;
1005 pData->onPuglMotion(ev);
1006 break;
1007 }
1008
1009 ///< Scrolled, a #PuglEventScroll
1010 case PUGL_SCROLL:
1011 {
1012 Widget::ScrollEvent ev;
1013 ev.mod = event->scroll.state;
1014 ev.flags = event->scroll.flags;
1015 ev.time = static_cast<uint>(event->scroll.time * 1000.0 + 0.5);
1016 ev.pos = Point<double>(event->scroll.x, event->scroll.y);
1017 ev.delta = Point<double>(event->scroll.dx, event->scroll.dy);
1018 ev.direction = static_cast<ScrollDirection>(event->scroll.direction);
1019 ev.absolutePos = ev.pos;
1020 pData->onPuglScroll(ev);
1021 break;
1022 }
1023
1024 ///< Custom client message, a #PuglEventClient
1025 case PUGL_CLIENT:
1026 break;
1027
1028 ///< Timer triggered, a #PuglEventTimer
1029 case PUGL_TIMER:
1030 if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id))
1031 idleCallback->idleCallback();
1032 break;
1033
1034 ///< Recursive loop entered, a #PuglEventLoopEnter
1035 case PUGL_LOOP_ENTER:
1036 break;
1037
1038 ///< Recursive loop left, a #PuglEventLoopLeave
1039 case PUGL_LOOP_LEAVE:
1040 break;
1041
1042 ///< Data offered from clipboard, a #PuglDataOfferEvent
1043 case PUGL_DATA_OFFER:
1044 if (const uint32_t offerTypeId = pData->onClipboardDataOffer())
1045 puglAcceptOffer(view, &event->offer, offerTypeId - 1);
1046 break;
1047
1048 ///< Data available from clipboard, a #PuglDataEvent
1049 case PUGL_DATA:
1050 pData->onClipboardData(event->data.typeIndex + 1);
1051 break;
1052 }
1053
1054 return PUGL_SUCCESS;
1055 }
1056
1057 // -----------------------------------------------------------------------
1058
1059 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
1060 static int printModifiers(const uint32_t mods)
1061 {
1062 return fprintf(stderr, "Modifiers:%s%s%s%s\n",
1063 (mods & PUGL_MOD_SHIFT) ? " Shift" : "",
1064 (mods & PUGL_MOD_CTRL) ? " Ctrl" : "",
1065 (mods & PUGL_MOD_ALT) ? " Alt" : "",
1066 (mods & PUGL_MOD_SUPER) ? " Super" : "");
1067 }
1068
1069 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
1070 {
1071 #define FFMT "%6.1f"
1072 #define PFMT FFMT " " FFMT
1073 #define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
1074
1075 switch (event->type) {
1076 case PUGL_NOTHING:
1077 return 0;
1078 case PUGL_KEY_PRESS:
1079 return PRINT("%sKey press code %3u key U+%04X\n",
1080 prefix,
1081 event->key.keycode,
1082 event->key.key);
1083 case PUGL_KEY_RELEASE:
1084 return PRINT("%sKey release code %3u key U+%04X\n",
1085 prefix,
1086 event->key.keycode,
1087 event->key.key);
1088 case PUGL_TEXT:
1089 return PRINT("%sText entry code %3u char U+%04X (%s)\n",
1090 prefix,
1091 event->text.keycode,
1092 event->text.character,
1093 event->text.string);
1094 case PUGL_BUTTON_PRESS:
1095 case PUGL_BUTTON_RELEASE:
1096 return (PRINT("%sMouse %u %s at " PFMT " ",
1097 prefix,
1098 event->button.button,
1099 (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ",
1100 event->button.x,
1101 event->button.y) +
1102 printModifiers(event->scroll.state));
1103 case PUGL_SCROLL:
1104 return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ",
1105 prefix,
1106 event->scroll.dx,
1107 event->scroll.dy,
1108 event->scroll.x,
1109 event->scroll.y) +
1110 printModifiers(event->scroll.state));
1111 case PUGL_POINTER_IN:
1112 return PRINT("%sMouse enter at " PFMT "\n",
1113 prefix,
1114 event->crossing.x,
1115 event->crossing.y);
1116 case PUGL_POINTER_OUT:
1117 return PRINT("%sMouse leave at " PFMT "\n",
1118 prefix,
1119 event->crossing.x,
1120 event->crossing.y);
1121 case PUGL_FOCUS_IN:
1122 return PRINT("%sFocus in %i\n",
1123 prefix,
1124 event->focus.mode);
1125 case PUGL_FOCUS_OUT:
1126 return PRINT("%sFocus out %i\n",
1127 prefix,
1128 event->focus.mode);
1129 case PUGL_CLIENT:
1130 return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
1131 prefix,
1132 event->client.data1,
1133 event->client.data2);
1134 case PUGL_TIMER:
1135 return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id);
1136 default:
1137 break;
1138 }
1139
1140 if (verbose) {
1141 switch (event->type) {
1142 case PUGL_CREATE:
1143 return fprintf(stderr, "%sCreate\n", prefix);
1144 case PUGL_DESTROY:
1145 return fprintf(stderr, "%sDestroy\n", prefix);
1146 case PUGL_MAP:
1147 return fprintf(stderr, "%sMap\n", prefix);
1148 case PUGL_UNMAP:
1149 return fprintf(stderr, "%sUnmap\n", prefix);
1150 case PUGL_UPDATE:
1151 return 0; // fprintf(stderr, "%sUpdate\n", prefix);
1152 case PUGL_CONFIGURE:
1153 return PRINT("%sConfigure " PFMT " " PFMT "\n",
1154 prefix,
1155 event->configure.x,
1156 event->configure.y,
1157 event->configure.width,
1158 event->configure.height);
1159 case PUGL_EXPOSE:
1160 return PRINT("%sExpose " PFMT " " PFMT "\n",
1161 prefix,
1162 event->expose.x,
1163 event->expose.y,
1164 event->expose.width,
1165 event->expose.height);
1166 case PUGL_CLOSE:
1167 return PRINT("%sClose\n", prefix);
1168 case PUGL_MOTION:
1169 return PRINT("%sMouse motion at " PFMT "\n",
1170 prefix,
1171 event->motion.x,
1172 event->motion.y);
1173 default:
1174 return PRINT("%sUnknown event type %d\n", prefix, (int)event->type);
1175 }
1176 }
1177
1178 #undef PRINT
1179 #undef PFMT
1180 #undef FFMT
1181
1182 return 0;
1183 }
1184 #endif
1185
1186 #undef DGL_DBG
1187 #undef DGL_DBGF
1188
1189 // -----------------------------------------------------------------------
1190
1191 END_NAMESPACE_DGL