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