1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
4  * Copyright (C) 2019 Jean Pierre Cimalando <jp-dev@inbox.ru>
5  * Copyright (C) 2019 Robin Gareus <robin@gareus.org>
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any purpose with
8  * or without fee is hereby granted, provided that the above copyright notice and this
9  * permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
12  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
13  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 // we need this for now
20 //#define PUGL_GRAB_FOCUS 1
21 
22 #include "../Base.hpp"
23 
24 #ifdef DGL_CAIRO
25 # define PUGL_CAIRO
26 # include "../Cairo.hpp"
27 #endif
28 #ifdef DGL_OPENGL
29 # define PUGL_OPENGL
30 # include "../OpenGL.hpp"
31 #endif
32 
33 #include "pugl/pugl.h"
34 
35 #if defined(__GNUC__) && (__GNUC__ >= 7)
36 # pragma GCC diagnostic push
37 # pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
38 #endif
39 
40 #if defined(DISTRHO_OS_HAIKU)
41 # define DGL_DEBUG_EVENTS
42 # include "pugl/pugl_haiku.cpp"
43 #elif defined(DISTRHO_OS_MAC)
44 # define PuglWindow     DISTRHO_JOIN_MACRO(PuglWindow,     DGL_NAMESPACE)
45 # define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE)
46 # include "pugl/pugl_osx.m"
47 #elif defined(DISTRHO_OS_WINDOWS)
48 # include "pugl/pugl_win.cpp"
49 # undef max
50 # undef min
51 #else
52 # include <sys/types.h>
53 # include <unistd.h>
54 extern "C" {
55 # include "pugl/pugl_x11.c"
56 }
57 #endif
58 
59 #if defined(__GNUC__) && (__GNUC__ >= 7)
60 # pragma GCC diagnostic pop
61 #endif
62 
63 #include "ApplicationPrivateData.hpp"
64 #include "WidgetPrivateData.hpp"
65 #include "../StandaloneWindow.hpp"
66 #include "../../distrho/extra/String.hpp"
67 
68 #define FOR_EACH_WIDGET(it) \
69   for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it)
70 
71 #define FOR_EACH_WIDGET_INV(rit) \
72   for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit)
73 
74 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
75 # define DBG(msg)  std::fprintf(stderr, "%s", msg);
76 # define DBGp(...) std::fprintf(stderr, __VA_ARGS__);
77 # define DBGF      std::fflush(stderr);
78 #else
79 # define DBG(msg)
80 # define DBGp(...)
81 # define DBGF
82 #endif
83 
84 START_NAMESPACE_DGL
85 
86 // -----------------------------------------------------------------------
87 // Window Private
88 
89 struct Window::PrivateData {
PrivateDataWindow::PrivateData90     PrivateData(Application& app, Window* const self)
91         : fApp(app),
92           fSelf(self),
93           fView(puglInit()),
94           fFirstInit(true),
95           fVisible(false),
96           fResizable(true),
97           fUsingEmbed(false),
98           fWidth(1),
99           fHeight(1),
100           fScaling(1.0),
101           fAutoScaling(1.0),
102           fTitle(nullptr),
103           fWidgets(),
104           fModal(),
105 #if defined(DISTRHO_OS_HAIKU)
106           bApplication(nullptr),
107           bView(nullptr),
108           bWindow(nullptr)
109 #elif defined(DISTRHO_OS_MAC)
110           fNeedsIdle(true),
111           mView(nullptr),
112           mWindow(nullptr),
113           mParentWindow(nullptr)
114 # ifndef DGL_FILE_BROWSER_DISABLED
115         , fOpenFilePanel(nullptr),
116           fFilePanelDelegate(nullptr)
117 # endif
118 #elif defined(DISTRHO_OS_WINDOWS)
119           hwnd(nullptr),
120           hwndParent(nullptr)
121 #else
122           xDisplay(nullptr),
123           xWindow(0)
124 #endif
125     {
126         DBG("Creating window without parent..."); DBGF;
127         init();
128     }
129 
PrivateDataWindow::PrivateData130     PrivateData(Application& app, Window* const self, Window& parent)
131         : fApp(app),
132           fSelf(self),
133           fView(puglInit()),
134           fFirstInit(true),
135           fVisible(false),
136           fResizable(true),
137           fUsingEmbed(false),
138           fWidth(1),
139           fHeight(1),
140           fScaling(1.0),
141           fAutoScaling(1.0),
142           fTitle(nullptr),
143           fWidgets(),
144           fModal(parent.pData),
145 #if defined(DISTRHO_OS_HAIKU)
146           bApplication(nullptr),
147           bView(nullptr),
148           bWindow(nullptr)
149 #elif defined(DISTRHO_OS_MAC)
150           fNeedsIdle(false),
151           mView(nullptr),
152           mWindow(nullptr),
153           mParentWindow(nullptr)
154 # ifndef DGL_FILE_BROWSER_DISABLED
155         , fOpenFilePanel(nullptr),
156           fFilePanelDelegate(nullptr)
157 # endif
158 #elif defined(DISTRHO_OS_WINDOWS)
159           hwnd(nullptr),
160           hwndParent(nullptr)
161 #else
162           xDisplay(nullptr),
163           xWindow(0)
164 #endif
165     {
166         DBG("Creating window with parent..."); DBGF;
167         init();
168 
169         const PuglInternals* const parentImpl(parent.pData->fView->impl);
170 
171         // NOTE: almost a 1:1 copy of setTransientWinId()
172 #if defined(DISTRHO_OS_HAIKU)
173         // TODO
174 #elif defined(DISTRHO_OS_MAC)
175         mParentWindow = parentImpl->window;
176 #elif defined(DISTRHO_OS_WINDOWS)
177         hwndParent = parentImpl->hwnd;
178         SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwndParent);
179 #else
180         XSetTransientForHint(xDisplay, xWindow, parentImpl->win);
181 #endif
182     }
183 
PrivateDataWindow::PrivateData184     PrivateData(Application& app, Window* const self, const intptr_t parentId, const double scaling, const bool resizable)
185         : fApp(app),
186           fSelf(self),
187           fView(puglInit()),
188           fFirstInit(true),
189           fVisible(parentId != 0),
190           fResizable(resizable),
191           fUsingEmbed(parentId != 0),
192           fWidth(1),
193           fHeight(1),
194           fScaling(scaling),
195           fAutoScaling(1.0),
196           fTitle(nullptr),
197           fWidgets(),
198           fModal(),
199 #if defined(DISTRHO_OS_HAIKU)
200           bApplication(nullptr),
201           bView(nullptr),
202           bWindow(nullptr)
203 #elif defined(DISTRHO_OS_MAC)
204           fNeedsIdle(parentId == 0),
205           mView(nullptr),
206           mWindow(nullptr),
207           mParentWindow(nullptr)
208 # ifndef DGL_FILE_BROWSER_DISABLED
209         , fOpenFilePanel(nullptr),
210           fFilePanelDelegate(nullptr)
211 # endif
212 #elif defined(DISTRHO_OS_WINDOWS)
213           hwnd(nullptr),
214           hwndParent(nullptr)
215 #else
216           xDisplay(nullptr),
217           xWindow(0)
218 #endif
219     {
220         if (fUsingEmbed)
221         {
222             DBG("Creating embedded window..."); DBGF;
223             puglInitWindowParent(fView, parentId);
224         }
225         else
226         {
227             DBG("Creating window without parent..."); DBGF;
228         }
229 
230         init();
231 
232         if (fUsingEmbed)
233         {
234             DBG("NOTE: Embed window is always visible and non-resizable\n");
235             puglShowWindow(fView);
236             fApp.pData->oneShown();
237             fFirstInit = false;
238         }
239     }
240 
initWindow::PrivateData241     void init()
242     {
243         if (fSelf == nullptr || fView == nullptr)
244         {
245             DBG("Failed!\n");
246             return;
247         }
248 
249         puglInitUserResizable(fView, fResizable);
250         puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight));
251 
252         puglSetHandle(fView, this);
253         puglSetDisplayFunc(fView, onDisplayCallback);
254         puglSetKeyboardFunc(fView, onKeyboardCallback);
255         puglSetMotionFunc(fView, onMotionCallback);
256         puglSetMouseFunc(fView, onMouseCallback);
257         puglSetScrollFunc(fView, onScrollCallback);
258         puglSetSpecialFunc(fView, onSpecialCallback);
259         puglSetReshapeFunc(fView, onReshapeCallback);
260         puglSetCloseFunc(fView, onCloseCallback);
261 #ifndef DGL_FILE_BROWSER_DISABLED
262         puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback);
263 #endif
264 
265         puglCreateWindow(fView, nullptr);
266 
267         PuglInternals* impl = fView->impl;
268 
269 #if defined(DISTRHO_OS_HAIKU)
270         bApplication = impl->app;
271         bView        = impl->view;
272         bWindow      = impl->window;
273 #elif defined(DISTRHO_OS_MAC)
274         mView   = impl->view;
275         mWindow = impl->window;
276         DISTRHO_SAFE_ASSERT(mView != nullptr);
277         if (fUsingEmbed) {
278             DISTRHO_SAFE_ASSERT(mWindow == nullptr);
279         } else {
280             DISTRHO_SAFE_ASSERT(mWindow != nullptr);
281         }
282 #elif defined(DISTRHO_OS_WINDOWS)
283         hwnd = impl->hwnd;
284         DISTRHO_SAFE_ASSERT(hwnd != 0);
285 #else
286         xDisplay = impl->display;
287         xWindow  = impl->win;
288         DISTRHO_SAFE_ASSERT(xWindow != 0);
289 
290         if (! fUsingEmbed)
291         {
292             const pid_t pid = getpid();
293             const Atom _nwp = XInternAtom(xDisplay, "_NET_WM_PID", False);
294             XChangeProperty(xDisplay, xWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
295 
296             const Atom _wt = XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE", False);
297 
298             // Setting the window to both dialog and normal will produce a decorated floating dialog
299             // Order is important: DIALOG needs to come before NORMAL
300             const Atom _wts[2] = {
301                 XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
302                 XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
303             };
304             XChangeProperty(xDisplay, xWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
305         }
306 #endif
307         puglEnterContext(fView);
308 
309         fApp.pData->windows.push_back(fSelf);
310 
311         DBG("Success!\n");
312     }
313 
~PrivateDataWindow::PrivateData314     ~PrivateData()
315     {
316         DBG("Destroying window..."); DBGF;
317 
318         if (fModal.enabled)
319         {
320             exec_fini();
321             close();
322         }
323 
324         fWidgets.clear();
325 
326         if (fUsingEmbed)
327         {
328             puglHideWindow(fView);
329             fApp.pData->oneHidden();
330         }
331 
332         if (fSelf != nullptr)
333         {
334             fApp.pData->windows.remove(fSelf);
335             fSelf = nullptr;
336         }
337 
338         if (fView != nullptr)
339         {
340             puglDestroy(fView);
341             fView = nullptr;
342         }
343 
344         if (fTitle != nullptr)
345         {
346             std::free(fTitle);
347             fTitle = nullptr;
348         }
349 
350 #if defined(DISTRHO_OS_HAIKU)
351         bApplication = nullptr;
352         bView        = nullptr;
353         bWindow      = nullptr;
354 #elif defined(DISTRHO_OS_MAC)
355         mView   = nullptr;
356         mWindow = nullptr;
357 #elif defined(DISTRHO_OS_WINDOWS)
358         hwnd = 0;
359 #else
360         xDisplay = nullptr;
361         xWindow  = 0;
362 #endif
363 
364 #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED)
365         if (fOpenFilePanel)
366         {
367             [fOpenFilePanel release];
368             fOpenFilePanel = nullptr;
369         }
370         if (fFilePanelDelegate)
371         {
372             [fFilePanelDelegate release];
373             fFilePanelDelegate = nullptr;
374         }
375 #endif
376 
377         DBG("Success!\n");
378     }
379 
380     // -------------------------------------------------------------------
381 
closeWindow::PrivateData382     void close()
383     {
384         DBG("Window close\n");
385 
386         if (fUsingEmbed)
387             return;
388 
389         setVisible(false);
390 
391         if (! fFirstInit)
392         {
393             fApp.pData->oneHidden();
394             fFirstInit = true;
395         }
396     }
397 
execWindow::PrivateData398     void exec(const bool lockWait)
399     {
400         DBG("Window exec\n");
401         exec_init();
402 
403         if (lockWait)
404         {
405             for (; fVisible && fModal.enabled;)
406             {
407                 idle();
408                 d_msleep(10);
409             }
410 
411             exec_fini();
412         }
413         else
414         {
415             idle();
416         }
417     }
418 
419     // -------------------------------------------------------------------
420 
exec_initWindow::PrivateData421     void exec_init()
422     {
423         DBG("Window modal loop starting..."); DBGF;
424         DISTRHO_SAFE_ASSERT_RETURN(fModal.parent != nullptr, setVisible(true));
425 
426         fModal.enabled = true;
427         fModal.parent->fModal.childFocus = this;
428 
429         fModal.parent->setVisible(true);
430         setVisible(true);
431 
432         DBG("Ok\n");
433     }
434 
exec_finiWindow::PrivateData435     void exec_fini()
436     {
437         DBG("Window modal loop stopping..."); DBGF;
438         fModal.enabled = false;
439 
440         if (fModal.parent != nullptr)
441         {
442             fModal.parent->fModal.childFocus = nullptr;
443 
444             // the mouse position probably changed since the modal appeared,
445             // so send a mouse motion event to the modal's parent window
446 #if defined(DISTRHO_OS_HAIKU)
447             // TODO
448 #elif defined(DISTRHO_OS_MAC)
449             // TODO
450 #elif defined(DISTRHO_OS_WINDOWS)
451             // TODO
452 #else
453             int i, wx, wy;
454             uint u;
455             ::Window w;
456             if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True)
457                 fModal.parent->onPuglMotion(wx, wy);
458 #endif
459         }
460 
461         DBG("Ok\n");
462     }
463 
464     // -------------------------------------------------------------------
465 
focusWindow::PrivateData466     void focus()
467     {
468         DBG("Window focus\n");
469 #if defined(DISTRHO_OS_HAIKU)
470         if (bWindow != nullptr)
471         {
472             if (bWindow->LockLooper())
473             {
474                 bWindow->Activate(true);
475                 bWindow->UnlockLooper();
476             }
477         }
478         else
479         {
480             bView->MakeFocus(true);
481         }
482 #elif defined(DISTRHO_OS_MAC)
483         if (mWindow != nullptr)
484             [mWindow makeKeyWindow];
485 #elif defined(DISTRHO_OS_WINDOWS)
486         SetForegroundWindow(hwnd);
487         SetActiveWindow(hwnd);
488         SetFocus(hwnd);
489 #else
490         XRaiseWindow(xDisplay, xWindow);
491         XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime);
492         XFlush(xDisplay);
493 #endif
494     }
495 
496     // -------------------------------------------------------------------
497 
setVisibleWindow::PrivateData498     void setVisible(const bool yesNo)
499     {
500         if (fVisible == yesNo)
501         {
502             DBG("Window setVisible matches current state, ignoring request\n");
503             return;
504         }
505         if (fUsingEmbed)
506         {
507             DBG("Window setVisible cannot be called when embedded\n");
508             return;
509         }
510 
511         DBG("Window setVisible called\n");
512 
513         fVisible = yesNo;
514 
515         if (yesNo && fFirstInit)
516             setSize(fWidth, fHeight, true);
517 
518 #if defined(DISTRHO_OS_HAIKU)
519         if (bWindow != nullptr)
520         {
521             if (bWindow->LockLooper())
522             {
523                 if (yesNo)
524                     bWindow->Show();
525                 else
526                     bWindow->Hide();
527 
528                 // TODO use flush?
529                 bWindow->Sync();
530                 bWindow->UnlockLooper();
531             }
532         }
533         else
534         {
535             if (yesNo)
536                 bView->Show();
537             else
538                 bView->Hide();
539         }
540 #elif defined(DISTRHO_OS_MAC)
541         if (yesNo)
542         {
543             if (mWindow != nullptr)
544             {
545                 if (mParentWindow != nullptr)
546                     [mParentWindow addChildWindow:mWindow
547                                           ordered:NSWindowAbove];
548 
549                 [mWindow setIsVisible:YES];
550             }
551             else
552             {
553                 [mView setHidden:NO];
554             }
555         }
556         else
557         {
558             if (mWindow != nullptr)
559             {
560                 if (mParentWindow != nullptr)
561                     [mParentWindow removeChildWindow:mWindow];
562 
563                 [mWindow setIsVisible:NO];
564             }
565             else
566             {
567                 [mView setHidden:YES];
568             }
569         }
570 #elif defined(DISTRHO_OS_WINDOWS)
571         if (yesNo)
572         {
573             if (fFirstInit)
574             {
575                 RECT rectChild, rectParent;
576 
577                 if (hwndParent != nullptr &&
578                     GetWindowRect(hwnd, &rectChild) &&
579                     GetWindowRect(hwndParent, &rectParent))
580                 {
581                     SetWindowPos(hwnd, hwndParent,
582                                  rectParent.left + (rectChild.right-rectChild.left)/2,
583                                  rectParent.top + (rectChild.bottom-rectChild.top)/2,
584                                  0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
585                 }
586                 else
587                 {
588                     ShowWindow(hwnd, SW_SHOWNORMAL);
589                 }
590             }
591             else
592             {
593                 ShowWindow(hwnd, SW_RESTORE);
594             }
595         }
596         else
597         {
598             ShowWindow(hwnd, SW_HIDE);
599         }
600 
601         UpdateWindow(hwnd);
602 #else
603         if (yesNo)
604             XMapRaised(xDisplay, xWindow);
605         else
606             XUnmapWindow(xDisplay, xWindow);
607 
608         XFlush(xDisplay);
609 #endif
610 
611         if (yesNo)
612         {
613             if (fFirstInit)
614             {
615                 fApp.pData->oneShown();
616                 fFirstInit = false;
617             }
618         }
619         else if (fModal.enabled)
620             exec_fini();
621     }
622 
623     // -------------------------------------------------------------------
624 
setResizableWindow::PrivateData625     void setResizable(const bool yesNo)
626     {
627         if (fResizable == yesNo)
628         {
629             DBG("Window setResizable matches current state, ignoring request\n");
630             return;
631         }
632         if (fUsingEmbed)
633         {
634             DBG("Window setResizable cannot be called when embedded\n");
635             return;
636         }
637 
638         DBG("Window setResizable called\n");
639 
640         fResizable = yesNo;
641         fView->user_resizable = yesNo;
642 
643 #if defined(DISTRHO_OS_HAIKU)
644         // TODO
645         // B_NO_BORDER
646         // B_TITLED_WINDOW_LOOK
647         // bWindow->SetFlags();
648 #elif defined(DISTRHO_OS_MAC)
649         const uint flags = yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0;
650         [mView setAutoresizingMask:flags];
651 #elif defined(DISTRHO_OS_WINDOWS)
652         const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) |  WS_SIZEBOX
653                                         : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX;
654         SetWindowLong(hwnd, GWL_STYLE, winFlags);
655 #endif
656 
657         setSize(fWidth, fHeight, true);
658     }
659 
660     // -------------------------------------------------------------------
661 
setGeometryConstraintsWindow::PrivateData662     void setGeometryConstraints(uint width, uint height, bool aspect)
663     {
664         // Did you forget to set DISTRHO_UI_USER_RESIZABLE ?
665         DISTRHO_SAFE_ASSERT_RETURN(fResizable,);
666 
667         fView->min_width  = width;
668         fView->min_height = height;
669         puglUpdateGeometryConstraints(fView, width, height, aspect);
670     }
671 
672     // -------------------------------------------------------------------
673 
setSizeWindow::PrivateData674     void setSize(uint width, uint height, const bool forced = false)
675     {
676         if (width <= 1 || height <= 1)
677         {
678             DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height);
679             return;
680         }
681 
682         if (fWidth == width && fHeight == height && ! forced)
683         {
684             DBGp("Window setSize matches current size, ignoring request (%i %i)\n", width, height);
685             return;
686         }
687 
688         fWidth  = width;
689         fHeight = height;
690 
691         DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false");
692 
693 #if defined(DISTRHO_OS_HAIKU)
694         bView->ResizeTo(width, height);
695 
696         if (bWindow != nullptr && bWindow->LockLooper())
697         {
698             bWindow->MoveTo(50, 50);
699             bWindow->ResizeTo(width, height);
700 
701             if (! forced)
702                 bWindow->Flush();
703 
704             bWindow->UnlockLooper();
705         }
706         // TODO resizable
707 #elif defined(DISTRHO_OS_MAC)
708         [mView setFrame:NSMakeRect(0, 0, width, height)];
709 
710         if (mWindow != nullptr)
711         {
712             const NSSize size = NSMakeSize(width, height);
713             [mWindow setContentSize:size];
714 
715             if (fResizable)
716             {
717                 [mWindow setContentMinSize:NSMakeSize(1, 1)];
718                 [mWindow setContentMaxSize:NSMakeSize(99999, 99999)];
719                 [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:NO];
720             }
721             else
722             {
723                 [mWindow setContentMinSize:size];
724                 [mWindow setContentMaxSize:size];
725                 [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
726             }
727         }
728 #elif defined(DISTRHO_OS_WINDOWS)
729         const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0);
730         RECT wr = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) };
731         AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST);
732 
733         SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top,
734                      SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
735 
736         if (! forced)
737             UpdateWindow(hwnd);
738 #else
739 
740         if (! fResizable)
741         {
742             XSizeHints sizeHints;
743             memset(&sizeHints, 0, sizeof(sizeHints));
744 
745             sizeHints.flags      = PSize|PMinSize|PMaxSize;
746             sizeHints.width      = static_cast<int>(width);
747             sizeHints.height     = static_cast<int>(height);
748             sizeHints.min_width  = static_cast<int>(width);
749             sizeHints.min_height = static_cast<int>(height);
750             sizeHints.max_width  = static_cast<int>(width);
751             sizeHints.max_height = static_cast<int>(height);
752 
753             XSetWMNormalHints(xDisplay, xWindow, &sizeHints);
754         }
755 
756         XResizeWindow(xDisplay, xWindow, width, height);
757 
758         if (! forced)
759             XFlush(xDisplay);
760 #endif
761 
762         puglPostRedisplay(fView);
763     }
764 
765     // -------------------------------------------------------------------
766 
getTitleWindow::PrivateData767     const char* getTitle() const noexcept
768     {
769         static const char* const kFallback = "";
770 
771         return fTitle != nullptr ? fTitle : kFallback;
772     }
773 
setTitleWindow::PrivateData774     void setTitle(const char* const title)
775     {
776         DBGp("Window setTitle \"%s\"\n", title);
777 
778         if (fTitle != nullptr)
779             std::free(fTitle);
780 
781         fTitle = strdup(title);
782 
783 #if defined(DISTRHO_OS_HAIKU)
784         if (bWindow != nullptr&& bWindow->LockLooper())
785         {
786             bWindow->SetTitle(title);
787             bWindow->UnlockLooper();
788         }
789 #elif defined(DISTRHO_OS_MAC)
790         if (mWindow != nullptr)
791         {
792             NSString* titleString = [[NSString alloc]
793                                       initWithBytes:title
794                                              length:strlen(title)
795                                           encoding:NSUTF8StringEncoding];
796 
797             [mWindow setTitle:titleString];
798         }
799 #elif defined(DISTRHO_OS_WINDOWS)
800         SetWindowTextA(hwnd, title);
801 #else
802         XStoreName(xDisplay, xWindow, title);
803         Atom netWmName = XInternAtom(xDisplay, "_NET_WM_NAME", False);
804         Atom utf8String = XInternAtom(xDisplay, "UTF8_STRING", False);
805         XChangeProperty(xDisplay, xWindow, netWmName, utf8String, 8, PropModeReplace, (unsigned char *)title, strlen(title));
806 #endif
807     }
808 
setTransientWinIdWindow::PrivateData809     void setTransientWinId(const uintptr_t winId)
810     {
811         DISTRHO_SAFE_ASSERT_RETURN(winId != 0,);
812 
813 #if defined(DISTRHO_OS_HAIKU)
814         // TODO
815 #elif defined(DISTRHO_OS_MAC)
816         NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
817         DISTRHO_SAFE_ASSERT_RETURN(parentWindow != nullptr,);
818 
819         [parentWindow addChildWindow:mWindow
820                              ordered:NSWindowAbove];
821 #elif defined(DISTRHO_OS_WINDOWS)
822         hwndParent = (HWND)winId;
823         SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)winId);
824 #else
825         XSetTransientForHint(xDisplay, xWindow, static_cast< ::Window>(winId));
826 #endif
827     }
828 
829     // -------------------------------------------------------------------
830 
getScalingWindow::PrivateData831     double getScaling() const noexcept
832     {
833         return fScaling;
834     }
835 
setAutoScalingWindow::PrivateData836     void setAutoScaling(const double scaling) noexcept
837     {
838         DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,);
839 
840         fAutoScaling = scaling;
841     }
842 
843     // -------------------------------------------------------------------
844 
getIgnoringKeyRepeatWindow::PrivateData845     bool getIgnoringKeyRepeat() const noexcept
846     {
847         return fView->ignoreKeyRepeat;
848     }
849 
setIgnoringKeyRepeatWindow::PrivateData850     void setIgnoringKeyRepeat(bool ignore) noexcept
851     {
852         puglIgnoreKeyRepeat(fView, ignore);
853     }
854 
855     // -------------------------------------------------------------------
856 
addWidgetWindow::PrivateData857     void addWidget(Widget* const widget)
858     {
859         fWidgets.push_back(widget);
860     }
861 
removeWidgetWindow::PrivateData862     void removeWidget(Widget* const widget)
863     {
864         fWidgets.remove(widget);
865     }
866 
idleWindow::PrivateData867     void idle()
868     {
869         puglProcessEvents(fView);
870 
871 #ifdef DISTRHO_OS_HAIKU
872         if (bApplication != nullptr)
873         {
874             // bApplication->Lock();
875             // bApplication->Loop();
876             // bApplication->Unlock();
877         }
878 #endif
879 
880 #ifdef DISTRHO_OS_MAC
881         if (fNeedsIdle)
882         {
883             NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
884             NSEvent* event;
885 
886             for (;;)
887             {
888                 event = [NSApp
889                          nextEventMatchingMask:NSAnyEventMask
890                                      untilDate:[NSDate distantPast]
891                                         inMode:NSDefaultRunLoopMode
892                                        dequeue:YES];
893 
894                 if (event == nil)
895                     break;
896 
897                 [NSApp sendEvent: event];
898             }
899 
900             [pool release];
901         }
902 #endif
903 
904 #if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED)
905         if (fSelectedFile.isNotEmpty())
906         {
907             char* const buffer = fSelectedFile.getAndReleaseBuffer();
908             fView->fileSelectedFunc(fView, buffer);
909             std::free(buffer);
910         }
911 #endif
912 
913         if (fModal.enabled && fModal.parent != nullptr)
914             fModal.parent->idle();
915     }
916 
917     // -------------------------------------------------------------------
918 
onPuglDisplayWindow::PrivateData919     void onPuglDisplay()
920     {
921         fSelf->onDisplayBefore();
922 
923         FOR_EACH_WIDGET(it)
924         {
925             Widget* const widget(*it);
926             widget->pData->display(fWidth, fHeight, fAutoScaling, false);
927         }
928 
929         fSelf->onDisplayAfter();
930     }
931 
onPuglKeyboardWindow::PrivateData932     int onPuglKeyboard(const bool press, const uint key)
933     {
934         DBGp("PUGL: onKeyboard : %i %i\n", press, key);
935 
936         if (fModal.childFocus != nullptr)
937         {
938             fModal.childFocus->focus();
939             return 0;
940         }
941 
942         Widget::KeyboardEvent ev;
943         ev.press = press;
944         ev.key  = key;
945         ev.mod  = static_cast<Modifier>(puglGetModifiers(fView));
946         ev.time = puglGetEventTimestamp(fView);
947 
948         FOR_EACH_WIDGET_INV(rit)
949         {
950             Widget* const widget(*rit);
951 
952             if (widget->isVisible() && widget->onKeyboard(ev))
953                 return 0;
954         }
955 
956         return 1;
957     }
958 
onPuglSpecialWindow::PrivateData959     int onPuglSpecial(const bool press, const Key key)
960     {
961         DBGp("PUGL: onSpecial : %i %i\n", press, key);
962 
963         if (fModal.childFocus != nullptr)
964         {
965             fModal.childFocus->focus();
966             return 0;
967         }
968 
969         Widget::SpecialEvent ev;
970         ev.press = press;
971         ev.key   = key;
972         ev.mod   = static_cast<Modifier>(puglGetModifiers(fView));
973         ev.time  = puglGetEventTimestamp(fView);
974 
975         FOR_EACH_WIDGET_INV(rit)
976         {
977             Widget* const widget(*rit);
978 
979             if (widget->isVisible() && widget->onSpecial(ev))
980                 return 0;
981         }
982 
983         return 1;
984     }
985 
onPuglMouseWindow::PrivateData986     void onPuglMouse(const int button, const bool press, int x, int y)
987     {
988         DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y);
989 
990         // FIXME - pugl sends 2 of these for each window on init, don't ask me why. we'll ignore it
991         if (press && button == 0 && x == 0 && y == 0) return;
992 
993         if (fModal.childFocus != nullptr)
994             return fModal.childFocus->focus();
995 
996         x /= fAutoScaling;
997         y /= fAutoScaling;
998 
999         Widget::MouseEvent ev;
1000         ev.button = button;
1001         ev.press  = press;
1002         ev.mod    = static_cast<Modifier>(puglGetModifiers(fView));
1003         ev.time   = puglGetEventTimestamp(fView);
1004 
1005         FOR_EACH_WIDGET_INV(rit)
1006         {
1007             Widget* const widget(*rit);
1008 
1009             ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
1010 
1011             if (widget->isVisible() && widget->onMouse(ev))
1012                 break;
1013         }
1014     }
1015 
onPuglMotionWindow::PrivateData1016     void onPuglMotion(int x, int y)
1017     {
1018         // DBGp("PUGL: onMotion : %i %i\n", x, y);
1019 
1020         if (fModal.childFocus != nullptr)
1021             return;
1022 
1023         x /= fAutoScaling;
1024         y /= fAutoScaling;
1025 
1026         Widget::MotionEvent ev;
1027         ev.mod  = static_cast<Modifier>(puglGetModifiers(fView));
1028         ev.time = puglGetEventTimestamp(fView);
1029 
1030         FOR_EACH_WIDGET_INV(rit)
1031         {
1032             Widget* const widget(*rit);
1033 
1034             ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
1035 
1036             if (widget->isVisible() && widget->onMotion(ev))
1037                 break;
1038         }
1039     }
1040 
onPuglScrollWindow::PrivateData1041     void onPuglScroll(int x, int y, float dx, float dy)
1042     {
1043         DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy);
1044 
1045         if (fModal.childFocus != nullptr)
1046             return;
1047 
1048         x /= fAutoScaling;
1049         y /= fAutoScaling;
1050         dx /= fAutoScaling;
1051         dy /= fAutoScaling;
1052 
1053         Widget::ScrollEvent ev;
1054         ev.delta = Point<float>(dx, dy);
1055         ev.mod   = static_cast<Modifier>(puglGetModifiers(fView));
1056         ev.time  = puglGetEventTimestamp(fView);
1057 
1058         FOR_EACH_WIDGET_INV(rit)
1059         {
1060             Widget* const widget(*rit);
1061 
1062             ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
1063 
1064             if (widget->isVisible() && widget->onScroll(ev))
1065                 break;
1066         }
1067     }
1068 
onPuglReshapeWindow::PrivateData1069     void onPuglReshape(const int width, const int height)
1070     {
1071         DBGp("PUGL: onReshape : %i %i\n", width, height);
1072 
1073         if (width <= 1 && height <= 1)
1074             return;
1075 
1076         fWidth  = static_cast<uint>(width);
1077         fHeight = static_cast<uint>(height);
1078 
1079         fSelf->onReshape(fWidth, fHeight);
1080 
1081         FOR_EACH_WIDGET(it)
1082         {
1083             Widget* const widget(*it);
1084 
1085             if (widget->pData->needsFullViewport)
1086                 widget->setSize(fWidth, fHeight);
1087         }
1088     }
1089 
onPuglCloseWindow::PrivateData1090     void onPuglClose()
1091     {
1092         DBG("PUGL: onClose\n");
1093 
1094         if (fModal.enabled)
1095             exec_fini();
1096 
1097         fSelf->onClose();
1098 
1099         if (fModal.childFocus != nullptr)
1100             fModal.childFocus->fSelf->onClose();
1101 
1102         close();
1103     }
1104 
1105     // -------------------------------------------------------------------
1106 
handlePluginKeyboardWindow::PrivateData1107     bool handlePluginKeyboard(const bool press, const uint key)
1108     {
1109         DBGp("PUGL: handlePluginKeyboard : %i %i\n", press, key);
1110 
1111         if (fModal.childFocus != nullptr)
1112         {
1113             fModal.childFocus->focus();
1114             return true;
1115         }
1116 
1117         Widget::KeyboardEvent ev;
1118         ev.press = press;
1119         ev.key   = key;
1120         ev.mod   = static_cast<Modifier>(fView->mods);
1121         ev.time  = 0;
1122 
1123         if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z')
1124             ev.key -= 'a' - 'A'; // a-z -> A-Z
1125 
1126         FOR_EACH_WIDGET_INV(rit)
1127         {
1128             Widget* const widget(*rit);
1129 
1130             if (widget->isVisible() && widget->onKeyboard(ev))
1131                 return true;
1132         }
1133 
1134         return false;
1135     }
1136 
handlePluginSpecialWindow::PrivateData1137     bool handlePluginSpecial(const bool press, const Key key)
1138     {
1139         DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key);
1140 
1141         if (fModal.childFocus != nullptr)
1142         {
1143             fModal.childFocus->focus();
1144             return true;
1145         }
1146 
1147         int mods = 0x0;
1148 
1149         switch (key)
1150         {
1151         case kKeyShift:
1152             mods |= kModifierShift;
1153             break;
1154         case kKeyControl:
1155             mods |= kModifierControl;
1156             break;
1157         case kKeyAlt:
1158             mods |= kModifierAlt;
1159             break;
1160         default:
1161             break;
1162         }
1163 
1164         if (mods != 0x0)
1165         {
1166             if (press)
1167                 fView->mods |= mods;
1168             else
1169                 fView->mods &= ~(mods);
1170         }
1171 
1172         Widget::SpecialEvent ev;
1173         ev.press = press;
1174         ev.key   = key;
1175         ev.mod   = static_cast<Modifier>(fView->mods);
1176         ev.time  = 0;
1177 
1178         FOR_EACH_WIDGET_INV(rit)
1179         {
1180             Widget* const widget(*rit);
1181 
1182             if (widget->isVisible() && widget->onSpecial(ev))
1183                 return true;
1184         }
1185 
1186         return false;
1187     }
1188 
1189 #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED)
openPanelDidEndWindow::PrivateData1190     static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData)
1191     {
1192         PrivateData* pData = (PrivateData*)userData;
1193 
1194         if (returnCode == NSOKButton)
1195         {
1196             NSArray* urls = [panel URLs];
1197             NSURL* fileUrl = nullptr;
1198 
1199             for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i)
1200             {
1201                 NSURL* url = (NSURL*)[urls objectAtIndex:i];
1202                 if ([url isFileURL])
1203                     fileUrl = url;
1204             }
1205 
1206             if (fileUrl)
1207             {
1208                 PuglView* view = pData->fView;
1209                 if (view->fileSelectedFunc)
1210                 {
1211                     const char* fileName = [fileUrl.path UTF8String];
1212                     view->fileSelectedFunc(view, fileName);
1213                 }
1214             }
1215         }
1216 
1217         [pData->fOpenFilePanel release];
1218         pData->fOpenFilePanel = nullptr;
1219     }
1220 #endif
1221 
1222     // -------------------------------------------------------------------
1223 
1224     Application&    fApp;
1225     Window*         fSelf;
1226     GraphicsContext fContext;
1227     PuglView*       fView;
1228 
1229     bool fFirstInit;
1230     bool fVisible;
1231     bool fResizable;
1232     bool fUsingEmbed;
1233     uint fWidth;
1234     uint fHeight;
1235     double fScaling;
1236     double fAutoScaling;
1237     char* fTitle;
1238     std::list<Widget*> fWidgets;
1239 
1240     struct Modal {
1241         bool enabled;
1242         PrivateData* parent;
1243         PrivateData* childFocus;
1244 
ModalWindow::PrivateData::Modal1245         Modal()
1246             : enabled(false),
1247               parent(nullptr),
1248               childFocus(nullptr) {}
1249 
ModalWindow::PrivateData::Modal1250         Modal(PrivateData* const p)
1251             : enabled(false),
1252               parent(p),
1253               childFocus(nullptr) {}
1254 
~ModalWindow::PrivateData::Modal1255         ~Modal()
1256         {
1257             DISTRHO_SAFE_ASSERT(! enabled);
1258             DISTRHO_SAFE_ASSERT(childFocus == nullptr);
1259         }
1260 
1261         DISTRHO_DECLARE_NON_COPY_STRUCT(Modal)
1262     } fModal;
1263 
1264 #if defined(DISTRHO_OS_HAIKU)
1265     BApplication* bApplication;
1266     BView*        bView;
1267     BWindow*      bWindow;
1268 #elif defined(DISTRHO_OS_MAC)
1269     bool            fNeedsIdle;
1270     NSView<PuglGenericView>* mView;
1271     id              mWindow;
1272     id              mParentWindow;
1273 # ifndef DGL_FILE_BROWSER_DISABLED
1274     NSOpenPanel*    fOpenFilePanel;
1275     id              fFilePanelDelegate;
1276 # endif
1277 #elif defined(DISTRHO_OS_WINDOWS)
1278     HWND hwnd;
1279     HWND hwndParent;
1280 # ifndef DGL_FILE_BROWSER_DISABLED
1281     String fSelectedFile;
1282 # endif
1283 #else
1284     Display* xDisplay;
1285     ::Window xWindow;
1286 #endif
1287 
1288     // -------------------------------------------------------------------
1289     // Callbacks
1290 
1291     #define handlePtr ((PrivateData*)puglGetHandle(view))
1292 
onDisplayCallbackWindow::PrivateData1293     static void onDisplayCallback(PuglView* view)
1294     {
1295         handlePtr->onPuglDisplay();
1296     }
1297 
onKeyboardCallbackWindow::PrivateData1298     static int onKeyboardCallback(PuglView* view, bool press, uint32_t key)
1299     {
1300         return handlePtr->onPuglKeyboard(press, key);
1301     }
1302 
onSpecialCallbackWindow::PrivateData1303     static int onSpecialCallback(PuglView* view, bool press, PuglKey key)
1304     {
1305         return handlePtr->onPuglSpecial(press, static_cast<Key>(key));
1306     }
1307 
onMouseCallbackWindow::PrivateData1308     static void onMouseCallback(PuglView* view, int button, bool press, int x, int y)
1309     {
1310         handlePtr->onPuglMouse(button, press, x, y);
1311     }
1312 
onMotionCallbackWindow::PrivateData1313     static void onMotionCallback(PuglView* view, int x, int y)
1314     {
1315         handlePtr->onPuglMotion(x, y);
1316     }
1317 
onScrollCallbackWindow::PrivateData1318     static void onScrollCallback(PuglView* view, int x, int y, float dx, float dy)
1319     {
1320         handlePtr->onPuglScroll(x, y, dx, dy);
1321     }
1322 
onReshapeCallbackWindow::PrivateData1323     static void onReshapeCallback(PuglView* view, int width, int height)
1324     {
1325         handlePtr->onPuglReshape(width, height);
1326     }
1327 
onCloseCallbackWindow::PrivateData1328     static void onCloseCallback(PuglView* view)
1329     {
1330         handlePtr->onPuglClose();
1331     }
1332 
1333 #ifndef DGL_FILE_BROWSER_DISABLED
fileBrowserSelectedCallbackWindow::PrivateData1334     static void fileBrowserSelectedCallback(PuglView* view, const char* filename)
1335     {
1336         handlePtr->fSelf->fileBrowserSelected(filename);
1337     }
1338 #endif
1339 
1340     #undef handlePtr
1341 
1342     DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
1343 };
1344 
1345 // -----------------------------------------------------------------------
1346 // Window
1347 
Window(Application & app)1348 Window::Window(Application& app)
1349     : pData(new PrivateData(app, this)) {}
1350 
Window(Application & app,Window & parent)1351 Window::Window(Application& app, Window& parent)
1352     : pData(new PrivateData(app, this, parent)) {}
1353 
Window(Application & app,intptr_t parentId,double scaling,bool resizable)1354 Window::Window(Application& app, intptr_t parentId, double scaling, bool resizable)
1355     : pData(new PrivateData(app, this, parentId, scaling, resizable)) {}
1356 
~Window()1357 Window::~Window()
1358 {
1359     delete pData;
1360 }
1361 
show()1362 void Window::show()
1363 {
1364     pData->setVisible(true);
1365 }
1366 
hide()1367 void Window::hide()
1368 {
1369     pData->setVisible(false);
1370 }
1371 
close()1372 void Window::close()
1373 {
1374     pData->close();
1375 }
1376 
exec(bool lockWait)1377 void Window::exec(bool lockWait)
1378 {
1379     pData->exec(lockWait);
1380 }
1381 
focus()1382 void Window::focus()
1383 {
1384     pData->focus();
1385 }
1386 
repaint()1387 void Window::repaint() noexcept
1388 {
1389     puglPostRedisplay(pData->fView);
1390 }
1391 
1392 // static int fib_filter_filename_filter(const char* const name)
1393 // {
1394 //     return 1;
1395 //     (void)name;
1396 // }
1397 
1398 #ifndef DGL_FILE_BROWSER_DISABLED
1399 
1400 #ifdef DISTRHO_OS_MAC
1401 END_NAMESPACE_DGL
1402 @interface FilePanelDelegate : NSObject
1403 {
1404     void (*fCallback)(NSOpenPanel*, int, void*);
1405     void* fUserData;
1406 }
1407 -(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void*)userData;
1408 -(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
1409 @end
1410 START_NAMESPACE_DGL
1411 #endif
1412 
openFileBrowser(const FileBrowserOptions & options)1413 bool Window::openFileBrowser(const FileBrowserOptions& options)
1414 {
1415 # ifdef SOFD_HAVE_X11
1416     using DISTRHO_NAMESPACE::String;
1417 
1418     // --------------------------------------------------------------------------
1419     // configure start dir
1420 
1421     // TODO: get abspath if needed
1422     // TODO: cross-platform
1423 
1424     String startDir(options.startDir);
1425 
1426 #  ifdef DISTRHO_OS_LINUX
1427     if (startDir.isEmpty())
1428     {
1429         if (char* const dir_name = get_current_dir_name())
1430         {
1431             startDir = dir_name;
1432             std::free(dir_name);
1433         }
1434     }
1435 #  endif
1436 
1437     DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false);
1438 
1439     if (! startDir.endsWith('/'))
1440         startDir += "/";
1441 
1442     DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false);
1443 
1444     // --------------------------------------------------------------------------
1445     // configure title
1446 
1447     String title(options.title);
1448 
1449     if (title.isEmpty())
1450     {
1451         title = pData->getTitle();
1452 
1453         if (title.isEmpty())
1454             title = "FileBrowser";
1455     }
1456 
1457     DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false);
1458 
1459     // --------------------------------------------------------------------------
1460     // configure filters
1461 
1462     x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter);
1463 
1464     // --------------------------------------------------------------------------
1465     // configure buttons
1466 
1467     x_fib_cfg_buttons(3, options.buttons.listAllFiles-1);
1468     x_fib_cfg_buttons(1, options.buttons.showHidden-1);
1469     x_fib_cfg_buttons(2, options.buttons.showPlaces-1);
1470 
1471     // --------------------------------------------------------------------------
1472     // show
1473 
1474     return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0);
1475 # elif defined(DISTRHO_OS_WINDOWS)
1476     // the old and compatible dialog API
1477     OPENFILENAMEW ofn;
1478     memset(&ofn, 0, sizeof(ofn));
1479 
1480     ofn.lStructSize = sizeof(ofn);
1481     ofn.hwndOwner = pData->hwnd;
1482 
1483     // set initial directory in UTF-16 coding
1484     std::vector<WCHAR> startDirW;
1485     if (options.startDir)
1486     {
1487         startDirW.resize(strlen(options.startDir) + 1);
1488         if (MultiByteToWideChar(CP_UTF8, 0, options.startDir, -1, startDirW.data(), startDirW.size()))
1489             ofn.lpstrInitialDir = startDirW.data();
1490     }
1491 
1492     // set title in UTF-16 coding
1493     std::vector<WCHAR> titleW;
1494     if (options.title)
1495     {
1496         titleW.resize(strlen(options.title) + 1);
1497         if (MultiByteToWideChar(CP_UTF8, 0, options.title, -1, titleW.data(), titleW.size()))
1498             ofn.lpstrTitle = titleW.data();
1499     }
1500 
1501     // prepare a buffer to receive the result
1502     std::vector<WCHAR> fileNameW(32768); // the Unicode maximum
1503     ofn.lpstrFile = fileNameW.data();
1504     ofn.nMaxFile = (DWORD)fileNameW.size();
1505 
1506     // TODO synchronous only, can't do better with WinAPI native dialogs.
1507     // threading might work, if someone is motivated to risk it.
1508     if (GetOpenFileNameW(&ofn))
1509     {
1510         // back to UTF-8
1511         std::vector<char> fileNameA(4 * 32768);
1512         if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, fileNameA.data(), (int)fileNameA.size(), nullptr, nullptr))
1513         {
1514             // handle it during the next idle cycle (fake async)
1515             pData->fSelectedFile = fileNameA.data();
1516         }
1517     }
1518 
1519     return true;
1520 # elif defined(DISTRHO_OS_MAC)
1521     if (pData->fOpenFilePanel) // permit one dialog at most
1522     {
1523         [pData->fOpenFilePanel makeKeyAndOrderFront:nil];
1524         return false;
1525     }
1526 
1527     NSOpenPanel* panel = [NSOpenPanel openPanel];
1528     pData->fOpenFilePanel = [panel retain];
1529 
1530     [panel setCanChooseFiles:YES];
1531     [panel setCanChooseDirectories:NO];
1532     [panel setAllowsMultipleSelection:NO];
1533 
1534     if (options.startDir)
1535         [panel setDirectory:[NSString stringWithUTF8String:options.startDir]];
1536 
1537     if (options.title)
1538     {
1539         NSString* titleString = [[NSString alloc]
1540             initWithBytes:options.title
1541                    length:strlen(options.title)
1542                  encoding:NSUTF8StringEncoding];
1543         [panel setTitle:titleString];
1544     }
1545 
1546     id delegate = pData->fFilePanelDelegate;
1547     if (!delegate)
1548     {
1549         delegate = [[FilePanelDelegate alloc] initWithCallback:&PrivateData::openPanelDidEnd
1550                                                       userData:pData];
1551         pData->fFilePanelDelegate = [delegate retain];
1552     }
1553 
1554     [panel beginSheetForDirectory:nullptr
1555                              file:nullptr
1556                    modalForWindow:nullptr
1557                     modalDelegate:delegate
1558                    didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
1559                       contextInfo:nullptr];
1560 
1561     return true;
1562 # else
1563     // not implemented
1564     return false;
1565 
1566     // unused
1567     (void)options;
1568 # endif
1569 }
1570 
1571 #ifdef DISTRHO_OS_MAC
1572 END_NAMESPACE_DGL
1573 @implementation FilePanelDelegate
1574 -(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void *)userData
1575 {
1576     [super init];
1577     self->fCallback = callback;
1578     self->fUserData = userData;
1579     return self;
1580 }
1581 
1582 -(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
1583 {
1584     self->fCallback(sheet, returnCode, self->fUserData);
1585     (void)contextInfo;
1586 }
1587 @end
1588 START_NAMESPACE_DGL
1589 #endif
1590 
1591 #endif // !defined(DGL_FILE_BROWSER_DISABLED)
1592 
isEmbed() const1593 bool Window::isEmbed() const noexcept
1594 {
1595     return pData->fUsingEmbed;
1596 }
1597 
isVisible() const1598 bool Window::isVisible() const noexcept
1599 {
1600     return pData->fVisible;
1601 }
1602 
setVisible(bool yesNo)1603 void Window::setVisible(bool yesNo)
1604 {
1605     pData->setVisible(yesNo);
1606 }
1607 
isResizable() const1608 bool Window::isResizable() const noexcept
1609 {
1610     return pData->fResizable;
1611 }
1612 
setResizable(bool yesNo)1613 void Window::setResizable(bool yesNo)
1614 {
1615     pData->setResizable(yesNo);
1616 }
1617 
setGeometryConstraints(uint width,uint height,bool aspect)1618 void Window::setGeometryConstraints(uint width, uint height, bool aspect)
1619 {
1620     pData->setGeometryConstraints(width, height, aspect);
1621 }
1622 
getWidth() const1623 uint Window::getWidth() const noexcept
1624 {
1625     return pData->fWidth;
1626 }
1627 
getHeight() const1628 uint Window::getHeight() const noexcept
1629 {
1630     return pData->fHeight;
1631 }
1632 
getSize() const1633 Size<uint> Window::getSize() const noexcept
1634 {
1635     return Size<uint>(pData->fWidth, pData->fHeight);
1636 }
1637 
setSize(uint width,uint height)1638 void Window::setSize(uint width, uint height)
1639 {
1640     pData->setSize(width, height);
1641 }
1642 
setSize(Size<uint> size)1643 void Window::setSize(Size<uint> size)
1644 {
1645     pData->setSize(size.getWidth(), size.getHeight());
1646 }
1647 
getTitle() const1648 const char* Window::getTitle() const noexcept
1649 {
1650     return pData->getTitle();
1651 }
1652 
setTitle(const char * title)1653 void Window::setTitle(const char* title)
1654 {
1655     pData->setTitle(title);
1656 }
1657 
setTransientWinId(uintptr_t winId)1658 void Window::setTransientWinId(uintptr_t winId)
1659 {
1660     pData->setTransientWinId(winId);
1661 }
1662 
getScaling() const1663 double Window::getScaling() const noexcept
1664 {
1665     return pData->getScaling();
1666 }
1667 
getIgnoringKeyRepeat() const1668 bool Window::getIgnoringKeyRepeat() const noexcept
1669 {
1670     return pData->getIgnoringKeyRepeat();
1671 }
1672 
setIgnoringKeyRepeat(bool ignore)1673 void Window::setIgnoringKeyRepeat(bool ignore) noexcept
1674 {
1675     pData->setIgnoringKeyRepeat(ignore);
1676 }
1677 
getApp() const1678 Application& Window::getApp() const noexcept
1679 {
1680     return pData->fApp;
1681 }
1682 
getWindowId() const1683 intptr_t Window::getWindowId() const noexcept
1684 {
1685     return puglGetNativeWindow(pData->fView);
1686 }
1687 
getGraphicsContext() const1688 const GraphicsContext& Window::getGraphicsContext() const noexcept
1689 {
1690     GraphicsContext& context = pData->fContext;
1691 #ifdef DGL_CAIRO
1692     context.cairo = (cairo_t*)puglGetContext(pData->fView);
1693 #endif
1694     return context;
1695 }
1696 
_setAutoScaling(double scaling)1697 void Window::_setAutoScaling(double scaling) noexcept
1698 {
1699     pData->setAutoScaling(scaling);
1700 }
1701 
_addWidget(Widget * const widget)1702 void Window::_addWidget(Widget* const widget)
1703 {
1704     pData->addWidget(widget);
1705 }
1706 
_removeWidget(Widget * const widget)1707 void Window::_removeWidget(Widget* const widget)
1708 {
1709     pData->removeWidget(widget);
1710 }
1711 
_idle()1712 void Window::_idle()
1713 {
1714     pData->idle();
1715 }
1716 
1717 // -----------------------------------------------------------------------
1718 
addIdleCallback(IdleCallback * const callback)1719 void Window::addIdleCallback(IdleCallback* const callback)
1720 {
1721     DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)
1722 
1723     pData->fApp.pData->idleCallbacks.push_back(callback);
1724 }
1725 
removeIdleCallback(IdleCallback * const callback)1726 void Window::removeIdleCallback(IdleCallback* const callback)
1727 {
1728     DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)
1729 
1730     pData->fApp.pData->idleCallbacks.remove(callback);
1731 }
1732 
1733 // -----------------------------------------------------------------------
1734 
onDisplayBefore()1735 void Window::onDisplayBefore()
1736 {
1737 #ifdef DGL_OPENGL
1738     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1739     glLoadIdentity();
1740 #endif
1741 }
1742 
onDisplayAfter()1743 void Window::onDisplayAfter()
1744 {
1745 }
1746 
onReshape(uint width,uint height)1747 void Window::onReshape(uint width, uint height)
1748 {
1749 #ifdef DGL_OPENGL
1750     glEnable(GL_BLEND);
1751     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1752     glMatrixMode(GL_PROJECTION);
1753     glLoadIdentity();
1754     glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0);
1755     glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
1756     glMatrixMode(GL_MODELVIEW);
1757     glLoadIdentity();
1758 #endif
1759 }
1760 
onClose()1761 void Window::onClose()
1762 {
1763 }
1764 
1765 #ifndef DGL_FILE_BROWSER_DISABLED
fileBrowserSelected(const char *)1766 void Window::fileBrowserSelected(const char*)
1767 {
1768 }
1769 #endif
1770 
handlePluginKeyboard(const bool press,const uint key)1771 bool Window::handlePluginKeyboard(const bool press, const uint key)
1772 {
1773     return pData->handlePluginKeyboard(press, key);
1774 }
1775 
handlePluginSpecial(const bool press,const Key key)1776 bool Window::handlePluginSpecial(const bool press, const Key key)
1777 {
1778     return pData->handlePluginSpecial(press, key);
1779 }
1780 
1781 // -----------------------------------------------------------------------
1782 
StandaloneWindow()1783 StandaloneWindow::StandaloneWindow()
1784     : Application(),
1785       Window((Application&)*this),
1786       fWidget(nullptr) {}
1787 
exec()1788 void StandaloneWindow::exec()
1789 {
1790     Window::show();
1791     Application::exec();
1792 }
1793 
onReshape(uint width,uint height)1794 void StandaloneWindow::onReshape(uint width, uint height)
1795 {
1796     if (fWidget != nullptr)
1797         fWidget->setSize(width, height);
1798     Window::onReshape(width, height);
1799 }
1800 
_addWidget(Widget * widget)1801 void StandaloneWindow::_addWidget(Widget* widget)
1802 {
1803     if (fWidget == nullptr)
1804     {
1805         fWidget = widget;
1806         fWidget->pData->needsFullViewport = true;
1807     }
1808     Window::_addWidget(widget);
1809 }
1810 
_removeWidget(Widget * widget)1811 void StandaloneWindow::_removeWidget(Widget* widget)
1812 {
1813     if (fWidget == widget)
1814     {
1815         fWidget->pData->needsFullViewport = false;
1816         fWidget = nullptr;
1817     }
1818     Window::_removeWidget(widget);
1819 }
1820 
1821 // -----------------------------------------------------------------------
1822 
1823 END_NAMESPACE_DGL
1824 
1825 #undef DBG
1826 #undef DBGF
1827