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