2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with
6 * or without fee is hereby granted, provided that the above copyright notice and this
7 * permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 // --------------------------------------------------------------------------------------------------------------------
20 // include base headers
26 # include "../OpenGL-include.hpp"
29 # include <vulkan/vulkan_core.h>
32 /* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */
40 #if defined(DISTRHO_OS_MAC)
41 # import <Cocoa/Cocoa.h>
43 # include <mach/mach_time.h>
45 # include <cairo-quartz.h>
48 # include <OpenGL/gl.h>
51 # import <QuartzCore/CAMetalLayer.h>
52 # include <vulkan/vulkan_macos.h>
54 #elif defined(DISTRHO_OS_WASM)
55 # include <emscripten/emscripten.h>
56 # include <emscripten/html5.h>
60 #elif defined(DISTRHO_OS_WINDOWS)
62 # include <winsock2.h>
64 # include <windowsx.h>
66 # include <cairo-win32.h>
72 # include <vulkan/vulkan.h>
73 # include <vulkan/vulkan_win32.h>
75 #elif defined(HAVE_X11)
79 # include <sys/select.h>
80 // # include <sys/time.h>
82 # include <X11/Xatom.h>
83 # include <X11/Xlib.h>
84 # include <X11/Xresource.h>
85 # include <X11/Xutil.h>
86 # include <X11/keysym.h>
88 # include <X11/Xcursor/Xcursor.h>
89 // # include <X11/cursorfont.h>
92 # include <X11/extensions/Xrandr.h>
95 # include <X11/extensions/sync.h>
96 # include <X11/extensions/syncconst.h>
99 # include <cairo-xlib.h>
105 # include <vulkan/vulkan_xlib.h>
109 #ifndef DGL_FILE_BROWSER_DISABLED
110 # define FILE_BROWSER_DIALOG_DGL_NAMESPACE
111 # define FILE_BROWSER_DIALOG_NAMESPACE DGL_NAMESPACE
112 # define DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED
114 # include "../../distrho/extra/FileBrowserDialogImpl.hpp"
116 # include "../../distrho/extra/FileBrowserDialogImpl.cpp"
119 #ifndef DISTRHO_OS_MAC
123 // --------------------------------------------------------------------------------------------------------------------
125 #if defined(DISTRHO_OS_MAC)
126 # ifndef DISTRHO_MACOS_NAMESPACE_MACRO
127 # define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE
128 # define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE)
129 # define PuglCairoView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglCairoView)
130 # define PuglOpenGLView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglOpenGLView)
131 # define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglStubView)
132 # define PuglVulkanView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglVulkanView)
133 # define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindow)
134 # define PuglWindowDelegate DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindowDelegate)
135 # define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWrapperView)
137 # pragma clang diagnostic push
138 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
139 # import "pugl-upstream/src/mac.m"
140 # import "pugl-upstream/src/mac_stub.m"
142 # import "pugl-upstream/src/mac_cairo.m"
145 # import "pugl-upstream/src/mac_gl.m"
148 # import "pugl-upstream/src/mac_vulkan.m"
150 # pragma clang diagnostic pop
151 #elif defined(DISTRHO_OS_WASM)
152 # include "pugl-upstream/src/wasm.c"
153 # include "pugl-upstream/src/wasm_stub.c"
155 # include "pugl-upstream/src/wasm_gl.c"
157 #elif defined(DISTRHO_OS_WINDOWS)
158 # include "pugl-upstream/src/win.c"
159 # include "pugl-upstream/src/win_stub.c"
161 # include "pugl-upstream/src/win_cairo.c"
164 # include "pugl-upstream/src/win_gl.c"
167 # include "pugl-upstream/src/win_vulkan.c"
169 #elif defined(HAVE_X11)
170 # include "pugl-upstream/src/x11.c"
171 # include "pugl-upstream/src/x11_stub.c"
173 # include "pugl-upstream/src/x11_cairo.c"
176 # include "pugl-upstream/src/x11_gl.c"
179 # include "pugl-upstream/src/x11_vulkan.c"
183 #include "pugl-upstream/src/common.c"
184 #include "pugl-upstream/src/internal.c"
186 // --------------------------------------------------------------------------------------------------------------------
187 // DGL specific, expose backend enter
189 bool puglBackendEnter(PuglView
* const view
)
191 return view
->backend
->enter(view
, nullptr) == PUGL_SUCCESS
;
194 // --------------------------------------------------------------------------------------------------------------------
195 // DGL specific, expose backend leave
197 bool puglBackendLeave(PuglView
* const view
)
199 return view
->backend
->leave(view
, nullptr) == PUGL_SUCCESS
;
202 // --------------------------------------------------------------------------------------------------------------------
203 // DGL specific, assigns backend that matches current DGL build
205 void puglSetMatchingBackendForCurrentBuild(PuglView
* const view
)
208 puglSetBackend(view
, puglCairoBackend());
211 puglSetBackend(view
, puglGlBackend());
214 puglSetBackend(view
, puglVulkanBackend());
216 if (view
->backend
== nullptr)
217 puglSetBackend(view
, puglStubBackend());
220 // --------------------------------------------------------------------------------------------------------------------
221 // bring view window into the foreground, aka "raise" window
223 void puglRaiseWindow(PuglView
* const view
)
225 #if defined(DISTRHO_OS_MAC)
226 if (NSWindow
* const window
= view
->impl
->window
? view
->impl
->window
227 : [view
->impl
->wrapperView window
])
228 [window orderFrontRegardless
];
229 #elif defined(DISTRHO_OS_WASM)
231 #elif defined(DISTRHO_OS_WINDOWS)
232 SetForegroundWindow(view
->impl
->hwnd
);
233 SetActiveWindow(view
->impl
->hwnd
);
234 #elif defined(HAVE_X11)
235 XRaiseWindow(view
->world
->impl
->display
, view
->impl
->win
);
239 // --------------------------------------------------------------------------------------------------------------------
240 // get scale factor from parent window if possible, fallback to puglGetScaleFactor
242 double puglGetScaleFactorFromParent(const PuglView
* const view
)
244 const PuglNativeView parent
= view
->parent
? view
->parent
: view
->transientParent
? view
->transientParent
: 0;
245 #if defined(DISTRHO_OS_MAC)
246 // some of these can return 0 as backingScaleFactor, pick the most relevant valid one
247 const NSWindow
* possibleWindows
[] = {
248 parent
!= 0 ? [(NSView
*)parent window
] : nullptr,
250 [view
->impl
->wrapperView window
]
252 for (size_t i
=0; i
<ARRAY_SIZE(possibleWindows
); ++i
)
254 if (possibleWindows
[i
] == nullptr)
256 if (const double scaleFactor
= [[possibleWindows
[i
] screen
] backingScaleFactor
])
259 return [[NSScreen mainScreen
] backingScaleFactor
];
260 #elif defined(DISTRHO_OS_WINDOWS)
261 const HWND hwnd
= parent
!= 0 ? (HWND
)parent
: view
->impl
->hwnd
;
262 return puglWinGetViewScaleFactor(hwnd
);
264 return puglGetScaleFactor(view
);
270 // --------------------------------------------------------------------------------------------------------------------
271 // Combined puglSetSizeHint using PUGL_MIN_SIZE and PUGL_FIXED_ASPECT
273 PuglStatus
puglSetGeometryConstraints(PuglView
* const view
, const uint width
, const uint height
, const bool aspect
)
275 view
->sizeHints
[PUGL_MIN_SIZE
].width
= width
;
276 view
->sizeHints
[PUGL_MIN_SIZE
].height
= height
;
280 view
->sizeHints
[PUGL_FIXED_ASPECT
].width
= width
;
281 view
->sizeHints
[PUGL_FIXED_ASPECT
].height
= height
;
284 #if defined(DISTRHO_OS_MAC)
285 if (view
->impl
->window
)
289 if ((status
= updateSizeHint(view
, PUGL_MIN_SIZE
)) != PUGL_SUCCESS
)
292 if (aspect
&& (status
= updateSizeHint(view
, PUGL_FIXED_ASPECT
)) != PUGL_SUCCESS
)
295 #elif defined(DISTRHO_OS_WASM)
297 #elif defined(DISTRHO_OS_WINDOWS)
299 #elif defined(HAVE_X11)
300 if (const PuglStatus status
= updateSizeHints(view
))
303 XFlush(view
->world
->impl
->display
);
309 // --------------------------------------------------------------------------------------------------------------------
310 // set view as resizable (or not) during runtime
312 void puglSetResizable(PuglView
* const view
, const bool resizable
)
314 puglSetViewHint(view
, PUGL_RESIZABLE
, resizable
? PUGL_TRUE
: PUGL_FALSE
);
316 #if defined(DISTRHO_OS_MAC)
317 if (PuglWindow
* const window
= view
->impl
->window
)
319 const uint style
= (NSClosableWindowMask
| NSTitledWindowMask
| NSMiniaturizableWindowMask
)
320 | (resizable
? NSResizableWindowMask
: 0x0);
321 [window setStyleMask
:style
];
323 // FIXME use [view setAutoresizingMask:NSViewNotSizable] ?
324 #elif defined(DISTRHO_OS_WASM)
326 #elif defined(DISTRHO_OS_WINDOWS)
327 if (const HWND hwnd
= view
->impl
->hwnd
)
329 const uint winFlags
= resizable
? GetWindowLong(hwnd
, GWL_STYLE
) | (WS_SIZEBOX
| WS_MAXIMIZEBOX
)
330 : GetWindowLong(hwnd
, GWL_STYLE
) & ~(WS_SIZEBOX
| WS_MAXIMIZEBOX
);
331 SetWindowLong(hwnd
, GWL_STYLE
, winFlags
);
333 #elif defined(HAVE_X11)
334 updateSizeHints(view
);
338 // --------------------------------------------------------------------------------------------------------------------
339 // set window size while also changing default
341 PuglStatus
puglSetSizeAndDefault(PuglView
* view
, uint width
, uint height
)
343 if (width
> INT16_MAX
|| height
> INT16_MAX
)
344 return PUGL_BAD_PARAMETER
;
346 view
->sizeHints
[PUGL_DEFAULT_SIZE
].width
= view
->frame
.width
= static_cast<PuglSpan
>(width
);
347 view
->sizeHints
[PUGL_DEFAULT_SIZE
].height
= view
->frame
.height
= static_cast<PuglSpan
>(height
);
349 #if defined(DISTRHO_OS_MAC)
350 // mostly matches upstream pugl, simplified
351 PuglInternals
* const impl
= view
->impl
;
353 const PuglRect frame
= view
->frame
;
354 const NSRect framePx
= rectToNsRect(frame
);
355 const NSRect framePt
= nsRectToPoints(view
, framePx
);
357 if (PuglWindow
* const window
= view
->impl
->window
)
359 const NSRect screenPt
= rectToScreen(viewScreen(view
), framePt
);
360 const NSRect winFrame
= [window frameRectForContentRect
:screenPt
];
361 [window setFrame
:winFrame display
:NO
];
364 const NSSize sizePx
= NSMakeSize(frame
.width
, frame
.height
);
365 const NSSize sizePt
= [impl
->drawView convertSizeFromBacking
:sizePx
];
366 [impl
->wrapperView setFrameSize
:sizePt
];
367 [impl
->drawView setFrameSize
:sizePt
];
368 #elif defined(DISTRHO_OS_WASM)
369 d_stdout("className is %s", view
->world
->className
);
370 emscripten_set_canvas_element_size(view
->world
->className
, width
, height
);
371 #elif defined(DISTRHO_OS_WINDOWS)
372 // matches upstream pugl, except we re-enter context after resize
373 if (const HWND hwnd
= view
->impl
->hwnd
)
375 const RECT rect
= adjustedWindowRect(view
, view
->frame
.x
, view
->frame
.y
,
376 static_cast<long>(width
), static_cast<long>(height
));
378 if (!SetWindowPos(hwnd
, HWND_TOP
, 0, 0, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
,
379 SWP_NOACTIVATE
| SWP_NOOWNERZORDER
| SWP_NOZORDER
| SWP_NOMOVE
))
380 return PUGL_UNKNOWN_ERROR
;
382 // make sure to return context back to ourselves
383 puglBackendEnter(view
);
385 #elif defined(HAVE_X11)
386 // matches upstream pugl, all in one
387 if (const Window window
= view
->impl
->win
)
389 Display
* const display
= view
->world
->impl
->display
;
391 if (! XResizeWindow(display
, window
, width
, height
))
392 return PUGL_UNKNOWN_ERROR
;
394 if (const PuglStatus status
= updateSizeHints(view
))
404 // --------------------------------------------------------------------------------------------------------------------
405 // DGL specific, build-specific drawing prepare
407 void puglOnDisplayPrepare(PuglView
*)
410 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
411 # ifndef DGL_USE_GLES
417 // --------------------------------------------------------------------------------------------------------------------
418 // DGL specific, build-specific fallback resize
420 void puglFallbackOnResize(PuglView
* const view
)
424 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
425 # ifndef DGL_USE_GLES
426 glMatrixMode(GL_PROJECTION
);
428 glOrtho(0.0, static_cast<GLdouble
>(view
->frame
.width
), static_cast<GLdouble
>(view
->frame
.height
), 0.0, 0.0, 1.0);
429 glViewport(0, 0, static_cast<GLsizei
>(view
->frame
.width
), static_cast<GLsizei
>(view
->frame
.height
));
430 glMatrixMode(GL_MODELVIEW
);
433 glViewport(0, 0, static_cast<GLsizei
>(view
->frame
.width
), static_cast<GLsizei
>(view
->frame
.height
));
442 // --------------------------------------------------------------------------------------------------------------------
444 #if defined(DISTRHO_OS_MAC)
446 // --------------------------------------------------------------------------------------------------------------------
447 // macOS specific, add another view's window as child
450 puglMacOSAddChildWindow(PuglView
* const view
, PuglView
* const child
)
452 if (NSWindow
* const viewWindow
= view
->impl
->window
? view
->impl
->window
453 : [view
->impl
->wrapperView window
])
455 if (NSWindow
* const childWindow
= child
->impl
->window
? child
->impl
->window
456 : [child
->impl
->wrapperView window
])
458 [viewWindow addChildWindow
:childWindow ordered
:NSWindowAbove
];
466 // --------------------------------------------------------------------------------------------------------------------
467 // macOS specific, remove another view's window as child
470 puglMacOSRemoveChildWindow(PuglView
* const view
, PuglView
* const child
)
472 if (NSWindow
* const viewWindow
= view
->impl
->window
? view
->impl
->window
473 : [view
->impl
->wrapperView window
])
475 if (NSWindow
* const childWindow
= child
->impl
->window
? child
->impl
->window
476 : [child
->impl
->wrapperView window
])
478 [viewWindow removeChildWindow
:childWindow
];
486 // --------------------------------------------------------------------------------------------------------------------
487 // macOS specific, center view based on parent coordinates (if there is one)
489 void puglMacOSShowCentered(PuglView
* const view
)
491 if (puglShow(view
) != PUGL_SUCCESS
)
494 if (view
->transientParent
!= 0)
496 NSWindow
* const transientWindow
= [(NSView
*)view
->transientParent window
];
497 DISTRHO_SAFE_ASSERT_RETURN(transientWindow
!= nullptr,);
499 const NSRect ourFrame
= [view
->impl
->window frame
];
500 const NSRect transientFrame
= [transientWindow frame
];
502 const int x
= transientFrame
.origin
.x
+ (transientFrame
.size
.width
- ourFrame
.size
.width
) / 2;
503 const int y
= transientFrame
.origin
.y
+ (transientFrame
.size
.height
- ourFrame
.size
.height
) / 2;
505 [view
->impl
->window setFrameTopLeftPoint
:NSMakePoint(x
, y
)];
509 [view
->impl
->window center
];
513 // --------------------------------------------------------------------------------------------------------------------
515 #elif defined(DISTRHO_OS_WINDOWS)
517 // --------------------------------------------------------------------------------------------------------------------
518 // win32 specific, call ShowWindow with SW_RESTORE
520 void puglWin32RestoreWindow(PuglView
* const view
)
522 PuglInternals
* impl
= view
->impl
;
523 DISTRHO_SAFE_ASSERT_RETURN(impl
->hwnd
!= nullptr,);
525 ShowWindow(impl
->hwnd
, SW_RESTORE
);
526 SetFocus(impl
->hwnd
);
529 // --------------------------------------------------------------------------------------------------------------------
530 // win32 specific, center view based on parent coordinates (if there is one)
532 void puglWin32ShowCentered(PuglView
* const view
)
534 PuglInternals
* impl
= view
->impl
;
535 DISTRHO_SAFE_ASSERT_RETURN(impl
->hwnd
!= nullptr,);
537 RECT rectChild
, rectParent
;
539 if (view
->transientParent
!= 0 &&
540 GetWindowRect(impl
->hwnd
, &rectChild
) &&
541 GetWindowRect((HWND
)view
->transientParent
, &rectParent
))
543 SetWindowPos(impl
->hwnd
, (HWND
)view
->transientParent
,
544 rectParent
.left
+ (rectChild
.right
-rectChild
.left
)/2,
545 rectParent
.top
+ (rectChild
.bottom
-rectChild
.top
)/2,
546 0, 0, SWP_SHOWWINDOW
|SWP_NOSIZE
);
550 #ifdef DGL_WINDOWS_ICON_ID
552 std::memset(&wClass
, 0, sizeof(wClass
));
554 const HINSTANCE hInstance
= GetModuleHandle(nullptr);
556 if (GetClassInfoEx(hInstance
, view
->world
->className
, &wClass
))
557 wClass
.hIcon
= LoadIcon(nullptr, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID
));
559 SetClassLongPtr(impl
->hwnd
, GCLP_HICON
, (LONG_PTR
) LoadIcon(hInstance
, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID
)));
563 std::memset(&mInfo
, 0, sizeof(mInfo
));
564 mInfo
.cbSize
= sizeof(mInfo
);
566 if (GetMonitorInfo(MonitorFromWindow(impl
->hwnd
, MONITOR_DEFAULTTOPRIMARY
), &mInfo
))
567 SetWindowPos(impl
->hwnd
,
569 mInfo
.rcWork
.left
+ (mInfo
.rcWork
.right
- mInfo
.rcWork
.left
- view
->frame
.width
) / 2,
570 mInfo
.rcWork
.top
+ (mInfo
.rcWork
.bottom
- mInfo
.rcWork
.top
- view
->frame
.height
) / 2,
571 0, 0, SWP_SHOWWINDOW
|SWP_NOSIZE
);
573 ShowWindow(impl
->hwnd
, SW_NORMAL
);
576 SetFocus(impl
->hwnd
);
579 // --------------------------------------------------------------------------------------------------------------------
581 #elif defined(DISTRHO_OS_WASM)
585 // --------------------------------------------------------------------------------------------------------------------
587 #elif defined(HAVE_X11)
589 PuglStatus
puglX11UpdateWithoutExposures(PuglWorld
* const world
)
591 const bool wasDispatchingEvents
= world
->impl
->dispatchingEvents
;
592 world
->impl
->dispatchingEvents
= true;
593 PuglStatus st
= PUGL_SUCCESS
;
595 const double startTime
= puglGetTime(world
);
596 const double endTime
= startTime
+ 0.03;
598 for (double t
= startTime
; !st
&& t
< endTime
; t
= puglGetTime(world
))
600 pollX11Socket(world
, endTime
- t
);
601 st
= dispatchX11Events(world
);
604 world
->impl
->dispatchingEvents
= wasDispatchingEvents
;
608 // --------------------------------------------------------------------------------------------------------------------
609 // X11 specific, set dialog window type and pid hints
611 void puglX11SetWindowTypeAndPID(const PuglView
* const view
, const bool isStandalone
)
613 const PuglInternals
* const impl
= view
->impl
;
614 Display
* const display
= view
->world
->impl
->display
;
616 const pid_t pid
= getpid();
617 const Atom _nwp
= XInternAtom(display
, "_NET_WM_PID", False
);
618 XChangeProperty(display
, impl
->win
, _nwp
, XA_CARDINAL
, 32, PropModeReplace
, (const uchar
*)&pid
, 1);
620 const Atom _wt
= XInternAtom(display
, "_NET_WM_WINDOW_TYPE", False
);
626 _wts
[numAtoms
++] = XInternAtom(display
, "_NET_WM_WINDOW_TYPE_DIALOG", False
);
628 _wts
[numAtoms
++] = XInternAtom(display
, "_NET_WM_WINDOW_TYPE_NORMAL", False
);
630 XChangeProperty(display
, impl
->win
, _wt
, XA_ATOM
, 32, PropModeReplace
, (const uchar
*)&_wts
, numAtoms
);
633 // --------------------------------------------------------------------------------------------------------------------
637 #ifndef DISTRHO_OS_MAC