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