3 * Copyright (C) 2014-2022 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #include "CarlaJuceUtils.hpp"
19 #include "CarlaPluginUI.hpp"
23 # include <sys/types.h>
24 # include <X11/Xatom.h>
25 # include <X11/Xlib.h>
26 # include <X11/Xutil.h>
27 # include "CarlaPluginUI_X11Icon.hpp"
31 # include "CarlaMacUtils.hpp"
32 # import <Cocoa/Cocoa.h>
37 # include "water/common.hpp"
40 #ifndef CARLA_PLUGIN_UI_CLASS_PREFIX
41 # error CARLA_PLUGIN_UI_CLASS_PREFIX undefined
44 // ---------------------------------------------------------------------------------------------------------------------
48 static constexpr const uint X11Key_Escape
= 9;
50 typedef void (*EventProcPtr
)(XEvent
* ev
);
52 // FIXME put all this inside a scoped class
53 static bool gErrorTriggered
= false;
54 # if defined(__GNUC__) && (__GNUC__ >= 5) && ! defined(__clang__)
55 # pragma GCC diagnostic push
56 # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
58 static pthread_mutex_t gErrorMutex
= PTHREAD_MUTEX_INITIALIZER
;
59 # if defined(__GNUC__) && (__GNUC__ >= 5) && ! defined(__clang__)
60 # pragma GCC diagnostic pop
63 static int temporaryErrorHandler(Display
*, XErrorEvent
*)
65 gErrorTriggered
= true;
69 class X11PluginUI
: public CarlaPluginUI
72 X11PluginUI(Callback
* const cb
, const uintptr_t parentId
,
73 const bool isStandalone
, const bool isResizable
, const bool canMonitorChildren
) noexcept
74 : CarlaPluginUI(cb
, isStandalone
, isResizable
),
78 fChildWindowConfigured(false),
79 fChildWindowMonitoring(isResizable
|| canMonitorChildren
),
82 fSetSizeCalledAtLeastOnce(false),
87 fDisplay
= XOpenDisplay(nullptr);
88 CARLA_SAFE_ASSERT_RETURN(fDisplay
!= nullptr,);
90 const int screen
= DefaultScreen(fDisplay
);
92 XSetWindowAttributes attr
;
93 carla_zeroStruct(attr
);
95 attr
.event_mask
= KeyPressMask
|KeyReleaseMask
|FocusChangeMask
;
97 if (fChildWindowMonitoring
)
98 attr
.event_mask
|= StructureNotifyMask
|SubstructureNotifyMask
;
100 fHostWindow
= XCreateWindow(fDisplay
, RootWindow(fDisplay
, screen
),
102 DefaultDepth(fDisplay
, screen
),
104 DefaultVisual(fDisplay
, screen
),
105 CWBorderPixel
|CWEventMask
, &attr
);
107 CARLA_SAFE_ASSERT_RETURN(fHostWindow
!= 0,);
109 XGrabKey(fDisplay
, X11Key_Escape
, AnyModifier
, fHostWindow
, 1, GrabModeAsync
, GrabModeAsync
);
111 Atom wmDelete
= XInternAtom(fDisplay
, "WM_DELETE_WINDOW", True
);
112 XSetWMProtocols(fDisplay
, fHostWindow
, &wmDelete
, 1);
114 const pid_t pid
= getpid();
115 const Atom _nwp
= XInternAtom(fDisplay
, "_NET_WM_PID", False
);
116 XChangeProperty(fDisplay
, fHostWindow
, _nwp
, XA_CARDINAL
, 32, PropModeReplace
, (const uchar
*)&pid
, 1);
118 const Atom _nwi
= XInternAtom(fDisplay
, "_NET_WM_ICON", False
);
119 XChangeProperty(fDisplay
, fHostWindow
, _nwi
, XA_CARDINAL
, 32, PropModeReplace
, (const uchar
*)sCarlaX11Icon
, sCarlaX11IconSize
);
121 const Atom _wt
= XInternAtom(fDisplay
, "_NET_WM_WINDOW_TYPE", False
);
123 // Setting the window to both dialog and normal will produce a decorated floating dialog
124 // Order is important: DIALOG needs to come before NORMAL
125 const Atom _wts
[2] = {
126 XInternAtom(fDisplay
, "_NET_WM_WINDOW_TYPE_DIALOG", False
),
127 XInternAtom(fDisplay
, "_NET_WM_WINDOW_TYPE_NORMAL", False
)
129 XChangeProperty(fDisplay
, fHostWindow
, _wt
, XA_ATOM
, 32, PropModeReplace
, (const uchar
*)&_wts
, 2);
132 setTransientWinId(parentId
);
135 ~X11PluginUI() override
137 CARLA_SAFE_ASSERT(! fIsVisible
);
139 if (fDisplay
== nullptr)
144 XUnmapWindow(fDisplay
, fHostWindow
);
148 if (fHostWindow
!= 0)
150 XDestroyWindow(fDisplay
, fHostWindow
);
154 XCloseDisplay(fDisplay
);
160 CARLA_SAFE_ASSERT_RETURN(fDisplay
!= nullptr,);
161 CARLA_SAFE_ASSERT_RETURN(fHostWindow
!= 0,);
165 if (const Window childWindow
= getChildWindow())
167 if (! fSetSizeCalledAtLeastOnce
)
172 XWindowAttributes attrs
= {};
174 pthread_mutex_lock(&gErrorMutex
);
175 const XErrorHandler oldErrorHandler
= XSetErrorHandler(temporaryErrorHandler
);
176 gErrorTriggered
= false;
178 if (XGetWindowAttributes(fDisplay
, childWindow
, &attrs
))
181 height
= attrs
.height
;
184 XSetErrorHandler(oldErrorHandler
);
185 pthread_mutex_unlock(&gErrorMutex
);
187 if (width
== 0 && height
== 0)
189 XSizeHints sizeHints
= {};
191 if (XGetNormalHints(fDisplay
, childWindow
, &sizeHints
))
193 if (sizeHints
.flags
& PSize
)
195 width
= sizeHints
.width
;
196 height
= sizeHints
.height
;
198 else if (sizeHints
.flags
& PBaseSize
)
200 width
= sizeHints
.base_width
;
201 height
= sizeHints
.base_height
;
206 if (width
> 1 && height
> 1)
207 setSize(static_cast<uint
>(width
), static_cast<uint
>(height
), false, false);
210 const Atom _xevp
= XInternAtom(fDisplay
, "_XEventProc", False
);
212 pthread_mutex_lock(&gErrorMutex
);
213 const XErrorHandler
oldErrorHandler(XSetErrorHandler(temporaryErrorHandler
));
214 gErrorTriggered
= false;
218 ulong nitems
, bytesAfter
;
219 uchar
* data
= nullptr;
221 XGetWindowProperty(fDisplay
, childWindow
, _xevp
, 0, 1, False
, AnyPropertyType
,
222 &actualType
, &actualFormat
, &nitems
, &bytesAfter
, &data
);
224 XSetErrorHandler(oldErrorHandler
);
225 pthread_mutex_unlock(&gErrorMutex
);
227 if (nitems
== 1 && ! gErrorTriggered
)
229 fEventProc
= *reinterpret_cast<EventProcPtr
*>(data
);
230 XMapRaised(fDisplay
, childWindow
);
238 XMapRaised(fDisplay
, fHostWindow
);
239 XSync(fDisplay
, False
);
244 CARLA_SAFE_ASSERT_RETURN(fDisplay
!= nullptr,);
245 CARLA_SAFE_ASSERT_RETURN(fHostWindow
!= 0,);
248 XUnmapWindow(fDisplay
, fHostWindow
);
255 if (fIsIdling
) return;
257 uint nextChildWidth
= 0;
258 uint nextChildHeight
= 0;
260 uint nextHostWidth
= 0;
261 uint nextHostHeight
= 0;
265 for (XEvent event
; XPending(fDisplay
) > 0;)
267 XNextEvent(fDisplay
, &event
);
272 char* type
= nullptr;
276 case ConfigureNotify
:
277 CARLA_SAFE_ASSERT_CONTINUE(fCallback
!= nullptr);
278 CARLA_SAFE_ASSERT_CONTINUE(event
.xconfigure
.width
> 0);
279 CARLA_SAFE_ASSERT_CONTINUE(event
.xconfigure
.height
> 0);
281 if (event
.xconfigure
.window
== fHostWindow
&& fHostWindow
!= 0)
283 nextHostWidth
= static_cast<uint
>(event
.xconfigure
.width
);
284 nextHostHeight
= static_cast<uint
>(event
.xconfigure
.height
);
286 else if (event
.xconfigure
.window
== fChildWindow
&& fChildWindow
!= 0)
288 nextChildWidth
= static_cast<uint
>(event
.xconfigure
.width
);
289 nextChildHeight
= static_cast<uint
>(event
.xconfigure
.height
);
294 type
= XGetAtomName(fDisplay
, event
.xclient
.message_type
);
295 CARLA_SAFE_ASSERT_CONTINUE(type
!= nullptr);
297 if (std::strcmp(type
, "WM_PROTOCOLS") == 0)
300 CARLA_SAFE_ASSERT_CONTINUE(fCallback
!= nullptr);
301 fCallback
->handlePluginUIClosed();
306 if (event
.xkey
.keycode
== X11Key_Escape
)
309 CARLA_SAFE_ASSERT_CONTINUE(fCallback
!= nullptr);
310 fCallback
->handlePluginUIClosed();
315 if (fChildWindow
== 0)
316 fChildWindow
= getChildWindow();
318 if (fChildWindow
!= 0)
320 XWindowAttributes wa
;
321 carla_zeroStruct(wa
);
323 if (XGetWindowAttributes(fDisplay
, fChildWindow
, &wa
) && wa
.map_state
== IsViewable
)
324 XSetInputFocus(fDisplay
, fChildWindow
, RevertToPointerRoot
, CurrentTime
);
331 else if (fEventProc
!= nullptr && event
.type
!= FocusIn
&& event
.type
!= FocusOut
)
335 if (nextChildWidth
!= 0 && nextChildHeight
!= 0 && fChildWindow
!= 0)
337 applyHintsFromChildWindow();
338 XResizeWindow(fDisplay
, fHostWindow
, nextChildWidth
, nextChildHeight
);
341 else if (nextHostWidth
!= 0 && nextHostHeight
!= 0)
343 if (fChildWindow
!= 0 && ! fChildWindowConfigured
)
345 applyHintsFromChildWindow();
346 fChildWindowConfigured
= true;
349 if (fChildWindow
!= 0)
350 XResizeWindow(fDisplay
, fChildWindow
, nextHostWidth
, nextHostHeight
);
352 fCallback
->handlePluginUIResized(nextHostWidth
, nextHostHeight
);
358 void focus() override
360 CARLA_SAFE_ASSERT_RETURN(fDisplay
!= nullptr,);
361 CARLA_SAFE_ASSERT_RETURN(fHostWindow
!= 0,);
363 XWindowAttributes wa
;
364 carla_zeroStruct(wa
);
366 CARLA_SAFE_ASSERT_RETURN(XGetWindowAttributes(fDisplay
, fHostWindow
, &wa
),);
368 if (wa
.map_state
== IsViewable
)
370 XRaiseWindow(fDisplay
, fHostWindow
);
371 XSetInputFocus(fDisplay
, fHostWindow
, RevertToPointerRoot
, CurrentTime
);
372 XSync(fDisplay
, False
);
376 void setMinimumSize(const uint width
, const uint height
) override
378 CARLA_SAFE_ASSERT_RETURN(fDisplay
!= nullptr,);
379 CARLA_SAFE_ASSERT_RETURN(fHostWindow
!= 0,);
381 fMinimumWidth
= width
;
382 fMinimumHeight
= height
;
384 XSizeHints sizeHints
= {};
385 if (XGetNormalHints(fDisplay
, fHostWindow
, &sizeHints
))
387 sizeHints
.flags
|= PMinSize
;
388 sizeHints
.min_width
= static_cast<int>(width
);
389 sizeHints
.min_height
= static_cast<int>(height
);
390 XSetNormalHints(fDisplay
, fHostWindow
, &sizeHints
);
394 void setSize(const uint width
, const uint height
, const bool forceUpdate
, const bool resizeChild
) override
396 CARLA_SAFE_ASSERT_RETURN(fDisplay
!= nullptr,);
397 CARLA_SAFE_ASSERT_RETURN(fHostWindow
!= 0,);
399 fSetSizeCalledAtLeastOnce
= true;
400 XResizeWindow(fDisplay
, fHostWindow
, width
, height
);
402 if (fChildWindow
!= 0 && resizeChild
)
403 XResizeWindow(fDisplay
, fChildWindow
, width
, height
);
407 XSizeHints sizeHints
= {};
408 sizeHints
.flags
= PSize
|PMinSize
|PMaxSize
;
409 sizeHints
.width
= static_cast<int>(width
);
410 sizeHints
.height
= static_cast<int>(height
);
411 sizeHints
.min_width
= static_cast<int>(width
);
412 sizeHints
.min_height
= static_cast<int>(height
);
413 sizeHints
.max_width
= static_cast<int>(width
);
414 sizeHints
.max_height
= static_cast<int>(height
);
416 XSetNormalHints(fDisplay
, fHostWindow
, &sizeHints
);
420 XSync(fDisplay
, False
);
423 void setTitle(const char* const title
) override
425 CARLA_SAFE_ASSERT_RETURN(fDisplay
!= nullptr,);
426 CARLA_SAFE_ASSERT_RETURN(fHostWindow
!= 0,);
428 XStoreName(fDisplay
, fHostWindow
, title
);
430 const Atom _nwn
= XInternAtom(fDisplay
, "_NET_WM_NAME", False
);
431 const Atom utf8
= XInternAtom(fDisplay
, "UTF8_STRING", True
);
433 XChangeProperty(fDisplay
, fHostWindow
, _nwn
, utf8
, 8,
435 (const uchar
*)(title
),
439 void setTransientWinId(const uintptr_t winId
) override
441 CARLA_SAFE_ASSERT_RETURN(fDisplay
!= nullptr,);
442 CARLA_SAFE_ASSERT_RETURN(fHostWindow
!= 0,);
444 XSetTransientForHint(fDisplay
, fHostWindow
, static_cast<Window
>(winId
));
447 void setChildWindow(void* const winId
) override
449 CARLA_SAFE_ASSERT_RETURN(winId
!= nullptr,);
451 fChildWindow
= (Window
)winId
;
454 void* getPtr() const noexcept override
456 return (void*)fHostWindow
;
459 void* getDisplay() const noexcept override
465 void applyHintsFromChildWindow()
467 pthread_mutex_lock(&gErrorMutex
);
468 const XErrorHandler oldErrorHandler
= XSetErrorHandler(temporaryErrorHandler
);
469 gErrorTriggered
= false;
471 XSizeHints sizeHints
= {};
472 if (XGetNormalHints(fDisplay
, fChildWindow
, &sizeHints
) && !gErrorTriggered
)
474 if (fMinimumWidth
!= 0 && fMinimumHeight
!= 0)
476 sizeHints
.flags
|= PMinSize
;
477 sizeHints
.min_width
= fMinimumWidth
;
478 sizeHints
.min_height
= fMinimumHeight
;
481 XSetNormalHints(fDisplay
, fHostWindow
, &sizeHints
);
486 carla_stdout("Caught errors while accessing child window");
490 XSetErrorHandler(oldErrorHandler
);
491 pthread_mutex_unlock(&gErrorMutex
);
494 Window
getChildWindow() const
496 CARLA_SAFE_ASSERT_RETURN(fDisplay
!= nullptr, 0);
497 CARLA_SAFE_ASSERT_RETURN(fHostWindow
!= 0, 0);
499 Window rootWindow
, parentWindow
, ret
= 0;
500 Window
* childWindows
= nullptr;
501 uint numChildren
= 0;
503 XQueryTree(fDisplay
, fHostWindow
, &rootWindow
, &parentWindow
, &childWindows
, &numChildren
);
505 if (numChildren
> 0 && childWindows
!= nullptr)
507 ret
= childWindows
[0];
518 bool fChildWindowConfigured
;
519 bool fChildWindowMonitoring
;
522 bool fSetSizeCalledAtLeastOnce
;
525 EventProcPtr fEventProc
;
527 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(X11PluginUI
)
531 // ---------------------------------------------------------------------------------------------------------------------
536 #if defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
537 # define CarlaPluginWindow CARLA_JOIN_MACRO3(CarlaPluginWindowBridgedArch, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
538 # define CarlaPluginWindowDelegate CARLA_JOIN_MACRO3(CarlaPluginWindowDelegateBridgedArch, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
539 #elif defined(BUILD_BRIDGE)
540 # define CarlaPluginWindow CARLA_JOIN_MACRO3(CarlaPluginWindowBridged, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
541 # define CarlaPluginWindowDelegate CARLA_JOIN_MACRO3(CarlaPluginWindowDelegateBridged, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
543 # define CarlaPluginWindow CARLA_JOIN_MACRO3(CarlaPluginWindow, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
544 # define CarlaPluginWindowDelegate CARLA_JOIN_MACRO3(CarlaPluginWindowDelegate, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
547 @interface CarlaPluginWindow
: NSWindow
548 - (id
) initWithContentRect
:(NSRect
)contentRect
549 styleMask
:(unsigned int)aStyle
550 backing
:(NSBackingStoreType
)bufferingType
552 - (BOOL
) canBecomeKeyWindow
;
553 - (BOOL
) canBecomeMainWindow
;
556 @implementation CarlaPluginWindow
558 - (id
)initWithContentRect
:(NSRect
)contentRect
559 styleMask
:(unsigned int)aStyle
560 backing
:(NSBackingStoreType
)bufferingType
563 NSWindow
* result
= [super initWithContentRect
:contentRect
565 backing
:bufferingType
568 [result setAcceptsMouseMovedEvents
:YES
];
570 return (CarlaPluginWindow
*)result
;
576 - (BOOL
)canBecomeKeyWindow
581 - (BOOL
)canBecomeMainWindow
588 @interface CarlaPluginWindowDelegate
: NSObject
<NSWindowDelegate
>
590 CarlaPluginUI::Callback
* callback
;
591 CarlaPluginWindow
* window
;
593 - (instancetype
)initWithWindowAndCallback
:(CarlaPluginWindow
*)window
594 callback
:(CarlaPluginUI::Callback
*)callback2
;
595 - (BOOL
)windowShouldClose
:(id
)sender
;
596 - (NSSize
)windowWillResize
:(NSWindow
*)sender toSize
:(NSSize
)frameSize
;
599 @implementation CarlaPluginWindowDelegate
601 - (instancetype
)initWithWindowAndCallback
:(CarlaPluginWindow
*)window2
602 callback
:(CarlaPluginUI::Callback
*)callback2
604 if ((self
= [super init
]))
606 callback
= callback2
;
613 - (BOOL
)windowShouldClose
:(id
)sender
616 callback
->handlePluginUIClosed();
624 - (NSSize
)windowWillResize
:(NSWindow
*)sender toSize
:(NSSize
)frameSize
626 for (NSView
* subview in
[[window contentView
] subviews
])
628 const NSSize minSize
= [subview fittingSize
];
629 if (frameSize
.width
< minSize
.width
)
630 frameSize
.width
= minSize
.width
;
631 if (frameSize
.height
< minSize
.height
)
632 frameSize
.height
= minSize
.height
;
640 - (void)windowDidResize:(NSWindow*)sender
642 carla_stdout("window did resize %p %f %f", sender, [window frame].size.width, [window frame].size.height);
644 const NSSize size = [window frame].size;
645 NSView* const view = [window contentView];
647 for (NSView* subview in [view subviews])
649 [subview setFrameSize:size];
657 class CocoaPluginUI
: public CarlaPluginUI
660 CocoaPluginUI(Callback
* const callback
, const uintptr_t parentId
, const bool isStandalone
, const bool isResizable
) noexcept
661 : CarlaPluginUI(callback
, isStandalone
, isResizable
),
663 fParentWindow(nullptr),
666 carla_debug("CocoaPluginUI::CocoaPluginUI(%p, " P_UINTPTR
, "%s)", callback
, parentId
, bool2str(isResizable
));
667 const CARLA_BACKEND_NAMESPACE::AutoNSAutoreleasePool arp
;
668 [NSApplication sharedApplication
];
670 fView
= [[NSView
new]retain
];
671 CARLA_SAFE_ASSERT_RETURN(fView
!= nullptr,)
674 uint style
= NSWindowStyleMaskClosable
| NSWindowStyleMaskTitled
;
676 uint style
= NSClosableWindowMask
| NSTitledWindowMask
;
680 style |= NSResizableWindowMask;
683 const NSRect frame
= NSMakeRect(0, 0, 100, 100);
685 fWindow
= [[[CarlaPluginWindow alloc
]
686 initWithContentRect
:frame
688 backing
:NSBackingStoreBuffered
692 if (fWindow
== nullptr)
699 ((NSWindow
*)fWindow
).delegate
= [[[CarlaPluginWindowDelegate alloc
]
700 initWithWindowAndCallback
:fWindow
701 callback
:callback
] retain
];
706 [fView setAutoresizingMask:(NSViewWidthSizable |
707 NSViewHeightSizable |
712 [fView setAutoresizesSubviews:YES];
717 [fView setAutoresizingMask
:NSViewNotSizable
];
718 [fView setAutoresizesSubviews
:NO
];
719 [[fWindow standardWindowButton
:NSWindowZoomButton
] setHidden
:YES
];
722 [fWindow setContentView
:fView
];
723 [fWindow makeFirstResponder
:fView
];
725 [fView setHidden
:NO
];
728 setTransientWinId(parentId
);
731 ~CocoaPluginUI() override
733 carla_debug("CocoaPluginUI::~CocoaPluginUI()");
734 if (fView
== nullptr)
737 [fView setHidden
:YES
];
738 [fView removeFromSuperview
];
746 carla_debug("CocoaPluginUI::show()");
747 CARLA_SAFE_ASSERT_RETURN(fView
!= nullptr,);
749 if (fParentWindow
!= nullptr)
751 [fParentWindow addChildWindow
:fWindow
752 ordered
:NSWindowAbove
];
756 [fWindow setIsVisible
:YES
];
762 carla_debug("CocoaPluginUI::hide()");
763 CARLA_SAFE_ASSERT_RETURN(fView
!= nullptr,);
765 [fWindow setIsVisible
:NO
];
767 if (fParentWindow
!= nullptr)
768 [fParentWindow removeChildWindow
:fWindow
];
773 // carla_debug("CocoaPluginUI::idle()");
775 for (NSView
* subview in
[fView subviews
])
777 const NSSize viewSize
= [fView frame
].size
;
778 const NSSize subviewSize
= [subview frame
].size
;
780 if (viewSize
.width
!= subviewSize
.width
|| viewSize
.height
!= subviewSize
.height
)
782 [fView setFrameSize
:subviewSize
];
783 [fWindow setContentSize
:subviewSize
];
789 void focus() override
791 carla_debug("CocoaPluginUI::focus()");
792 CARLA_SAFE_ASSERT_RETURN(fWindow
!= nullptr,);
794 [fWindow makeKeyAndOrderFront
:fWindow
];
795 [fWindow orderFrontRegardless
];
796 [NSApp activateIgnoringOtherApps
:YES
];
799 void setMinimumSize(uint
, uint
) override
804 void setSize(const uint width
, const uint height
, const bool forceUpdate
, const bool resizeChild
) override
806 carla_debug("CocoaPluginUI::setSize(%u, %u, %s)", width
, height
, bool2str(forceUpdate
));
807 CARLA_SAFE_ASSERT_RETURN(fWindow
!= nullptr,);
808 CARLA_SAFE_ASSERT_RETURN(fView
!= nullptr,);
810 const NSSize size
= NSMakeSize(width
, height
);
812 [fView setFrameSize
:size
];
813 [fWindow setContentSize
:size
];
815 // this is needed for a few plugins
816 if (forceUpdate
&& resizeChild
)
818 for (NSView
* subview in
[fView subviews
])
820 [subview setFrame
:[fView frame
]];
828 [fWindow setContentMinSize:NSMakeSize(1, 1)];
829 [fWindow setContentMaxSize:NSMakeSize(99999, 99999)];
833 [fWindow setContentMinSize:size];
834 [fWindow setContentMaxSize:size];
841 [fView setNeedsDisplay
:YES
];
845 void setTitle(const char* const title
) override
847 carla_debug("CocoaPluginUI::setTitle(\"%s\")", title
);
848 CARLA_SAFE_ASSERT_RETURN(fWindow
!= nullptr,);
850 NSString
* titleString
= [[NSString alloc
]
853 encoding
:NSUTF8StringEncoding
];
855 [fWindow setTitle
:titleString
];
858 void setTransientWinId(const uintptr_t winId
) override
860 carla_debug("CocoaPluginUI::setTransientWinId(" P_UINTPTR
")", winId
);
861 CARLA_SAFE_ASSERT_RETURN(fWindow
!= nullptr,);
863 NSWindow
* const parentWindow
= [NSApp windowWithWindowNumber
:winId
];
864 CARLA_SAFE_ASSERT_RETURN(parentWindow
!= nullptr,);
866 fParentWindow
= parentWindow
;
868 if ([fWindow isVisible
])
869 [fParentWindow addChildWindow
:fWindow
870 ordered
:NSWindowAbove
];
873 void setChildWindow(void* const childWindow
) override
875 carla_debug("CocoaPluginUI::setChildWindow(%p)", childWindow
);
876 CARLA_SAFE_ASSERT_RETURN(childWindow
!= nullptr,);
878 NSView
* const view
= (NSView
*)childWindow
;
879 const NSRect frame
= [view frame
];
881 [fWindow setContentSize
:frame
.size
];
882 [fView setFrame
:frame
];
883 [fView setNeedsDisplay
:YES
];
886 void* getPtr() const noexcept override
888 carla_debug("CocoaPluginUI::getPtr()");
892 void* getDisplay() const noexcept
894 carla_debug("CocoaPluginUI::getDisplay()");
895 return (void*)fWindow
;
900 NSWindow
* fParentWindow
;
901 CarlaPluginWindow
* fWindow
;
903 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CocoaPluginUI
)
906 #endif // CARLA_OS_MAC
908 // ---------------------------------------------------------------------------------------------------------------------
913 #define CARLA_LOCAL_CLOSE_MSG (WM_USER + 50)
915 static LRESULT CALLBACK
wndProc(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
917 class WindowsPluginUI
: public CarlaPluginUI
920 WindowsPluginUI(Callback
* const cb
, const uintptr_t parentId
, const bool isStandalone
, const bool isResizable
) noexcept
921 : CarlaPluginUI(cb
, isStandalone
, isResizable
),
923 fChildWindow(nullptr),
924 fParentWindow(nullptr),
929 static int wc_count
= 0;
930 char classNameBuf
[32];
931 std::srand((std::time(nullptr)));
932 std::snprintf(classNameBuf
, 32, "CarlaWin-%d-%d", ++wc_count
, std::rand());
933 classNameBuf
[31] = '\0';
935 const HINSTANCE hInstance
= water::getCurrentModuleInstanceHandle();
937 carla_zeroStruct(fWindowClass
);
938 fWindowClass
.style
= CS_OWNDC
;
939 fWindowClass
.lpfnWndProc
= wndProc
;
940 fWindowClass
.hInstance
= hInstance
;
941 fWindowClass
.hIcon
= LoadIcon(hInstance
, IDI_APPLICATION
);
942 fWindowClass
.hCursor
= LoadCursor(hInstance
, IDC_ARROW
);
943 fWindowClass
.lpszClassName
= strdup(classNameBuf
);
945 if (!RegisterClassA(&fWindowClass
)) {
946 free((void*)fWindowClass
.lpszClassName
);
950 int winFlags
= WS_POPUPWINDOW
| WS_CAPTION
;
953 winFlags
|= WS_SIZEBOX
;
955 #ifdef BUILDING_CARLA_FOR_WINE
956 const uint winType
= WS_EX_DLGMODALFRAME
;
957 const HWND parent
= nullptr;
959 const uint winType
= WS_EX_TOOLWINDOW
;
960 const HWND parent
= (HWND
)parentId
;
963 fWindow
= CreateWindowExA(winType
,
964 classNameBuf
, "Carla Plugin UI", winFlags
,
965 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
969 if (fWindow
== nullptr)
971 const DWORD errorCode
= ::GetLastError();
972 carla_stderr2("CreateWindowEx failed with error code 0x%x, class name was '%s'",
973 errorCode
, fWindowClass
.lpszClassName
);
974 UnregisterClassA(fWindowClass
.lpszClassName
, nullptr);
975 free((void*)fWindowClass
.lpszClassName
);
979 SetWindowLongPtr(fWindow
, GWLP_USERDATA
, (LONG_PTR
)this);
981 #ifndef BUILDING_CARLA_FOR_WINE
983 setTransientWinId(parentId
);
991 ~WindowsPluginUI() override
993 CARLA_SAFE_ASSERT(! fIsVisible
);
998 ShowWindow(fWindow
, SW_HIDE
);
1000 DestroyWindow(fWindow
);
1005 UnregisterClassA(fWindowClass
.lpszClassName
, nullptr);
1006 free((void*)fWindowClass
.lpszClassName
);
1009 void show() override
1011 CARLA_SAFE_ASSERT_RETURN(fWindow
!= nullptr,);
1016 RECT rectChild
, rectParent
;
1018 if (fChildWindow
!= nullptr && GetWindowRect(fChildWindow
, &rectChild
))
1019 setSize(rectChild
.right
- rectChild
.left
, rectChild
.bottom
- rectChild
.top
, false, false);
1021 if (fParentWindow
!= nullptr &&
1022 GetWindowRect(fWindow
, &rectChild
) &&
1023 GetWindowRect(fParentWindow
, &rectParent
))
1025 SetWindowPos(fWindow
, fParentWindow
,
1026 rectParent
.left
+ (rectChild
.right
-rectChild
.left
)/2,
1027 rectParent
.top
+ (rectChild
.bottom
-rectChild
.top
)/2,
1028 0, 0, SWP_SHOWWINDOW
|SWP_NOSIZE
);
1032 ShowWindow(fWindow
, SW_SHOWNORMAL
);
1037 ShowWindow(fWindow
, SW_RESTORE
);
1041 UpdateWindow(fWindow
);
1044 void hide() override
1046 CARLA_SAFE_ASSERT_RETURN(fWindow
!= nullptr,);
1048 ShowWindow(fWindow
, SW_HIDE
);
1050 UpdateWindow(fWindow
);
1053 void idle() override
1055 if (fIsIdling
|| fWindow
== nullptr)
1061 while (::PeekMessage(&msg
, fWindow
, 0, 0, PM_REMOVE
))
1063 switch (msg
.message
)
1066 case CARLA_LOCAL_CLOSE_MSG
:
1068 CARLA_SAFE_ASSERT_BREAK(fCallback
!= nullptr);
1069 fCallback
->handlePluginUIClosed();
1073 DispatchMessageA(&msg
);
1079 LRESULT
checkAndHandleMessage(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
1081 if (fWindow
== hwnd
)
1086 if (fChildWindow
!= nullptr)
1089 GetClientRect(fWindow
, &rect
);
1090 SetWindowPos(fChildWindow
, 0, 0, 0, rect
.right
, rect
.bottom
,
1091 SWP_NOACTIVATE
|SWP_NOMOVE
|SWP_NOOWNERZORDER
|SWP_NOZORDER
);
1095 case CARLA_LOCAL_CLOSE_MSG
:
1097 CARLA_SAFE_ASSERT_BREAK(fCallback
!= nullptr);
1098 fCallback
->handlePluginUIClosed();
1103 return DefWindowProcA(hwnd
, message
, wParam
, lParam
);
1106 void focus() override
1108 CARLA_SAFE_ASSERT_RETURN(fWindow
!= nullptr,);
1110 SetForegroundWindow(fWindow
);
1111 SetActiveWindow(fWindow
);
1115 void setMinimumSize(uint
, uint
) override
1120 void setSize(const uint width
, const uint height
, const bool forceUpdate
, bool) override
1122 CARLA_SAFE_ASSERT_RETURN(fWindow
!= nullptr,);
1124 const int winFlags
= WS_POPUPWINDOW
| WS_CAPTION
| (fIsResizable
? WS_SIZEBOX
: 0x0);
1125 RECT wr
= { 0, 0, static_cast<long>(width
), static_cast<long>(height
) };
1126 AdjustWindowRectEx(&wr
, winFlags
, FALSE
, WS_EX_TOPMOST
);
1128 SetWindowPos(fWindow
, 0, 0, 0, wr
.right
-wr
.left
, wr
.bottom
-wr
.top
,
1129 SWP_NOACTIVATE
|SWP_NOMOVE
|SWP_NOOWNERZORDER
|SWP_NOZORDER
);
1132 UpdateWindow(fWindow
);
1135 void setTitle(const char* const title
) override
1137 CARLA_SAFE_ASSERT_RETURN(fWindow
!= nullptr,);
1139 SetWindowTextA(fWindow
, title
);
1142 void setTransientWinId(const uintptr_t winId
) override
1144 CARLA_SAFE_ASSERT_RETURN(fWindow
!= nullptr,);
1146 fParentWindow
= (HWND
)winId
;
1147 SetWindowLongPtr(fWindow
, GWLP_HWNDPARENT
, (LONG_PTR
)winId
);
1150 void setChildWindow(void* const winId
) override
1152 CARLA_SAFE_ASSERT_RETURN(winId
!= nullptr,);
1154 fChildWindow
= (HWND
)winId
;
1157 void* getPtr() const noexcept override
1159 return (void*)fWindow
;
1162 void* getDisplay() const noexcept
1171 WNDCLASSA fWindowClass
;
1176 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WindowsPluginUI
)
1179 LRESULT CALLBACK
wndProc(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
1184 PostMessage(hwnd
, CARLA_LOCAL_CLOSE_MSG
, wParam
, lParam
);
1189 PostMessage(hwnd
, WM_SHOWWINDOW
, TRUE
, 0);
1197 if (WindowsPluginUI
* const ui
= (WindowsPluginUI
*)GetWindowLongPtr(hwnd
, GWLP_USERDATA
))
1198 return ui
->checkAndHandleMessage(hwnd
, message
, wParam
, lParam
);
1199 return DefWindowProcA(hwnd
, message
, wParam
, lParam
);
1202 #endif // CARLA_OS_WIN
1204 // -----------------------------------------------------
1206 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1207 bool CarlaPluginUI::tryTransientWinIdMatch(const uintptr_t pid
, const char* const uiTitle
, const uintptr_t winId
, const bool centerUI
)
1209 CARLA_SAFE_ASSERT_RETURN(uiTitle
!= nullptr && uiTitle
[0] != '\0', true);
1210 CARLA_SAFE_ASSERT_RETURN(winId
!= 0, true);
1212 #if defined(HAVE_X11)
1213 struct ScopedDisplay
{
1215 ScopedDisplay() : display(XOpenDisplay(nullptr)) {}
1216 ~ScopedDisplay() { if (display
!=nullptr) XCloseDisplay(display
); }
1218 CARLA_PREVENT_HEAP_ALLOCATION
1219 CARLA_DECLARE_NON_COPYABLE(ScopedDisplay
)
1221 struct ScopedFreeData
{
1226 ScopedFreeData(char* d
) : data(d
) {}
1227 ScopedFreeData(uchar
* d
) : udata(d
) {}
1228 ~ScopedFreeData() { XFree(data
); }
1230 CARLA_PREVENT_HEAP_ALLOCATION
1231 CARLA_DECLARE_NON_COPYABLE(ScopedFreeData
)
1234 const ScopedDisplay sd
;
1235 CARLA_SAFE_ASSERT_RETURN(sd
.display
!= nullptr, true);
1237 const Window
rootWindow(DefaultRootWindow(sd
.display
));
1239 const Atom _ncl
= XInternAtom(sd
.display
, "_NET_CLIENT_LIST" , False
);
1240 const Atom _nwn
= XInternAtom(sd
.display
, "_NET_WM_NAME", False
);
1241 const Atom _nwp
= XInternAtom(sd
.display
, "_NET_WM_PID", False
);
1242 const Atom utf8
= XInternAtom(sd
.display
, "UTF8_STRING", True
);
1246 ulong numWindows
, bytesAfter
;
1247 uchar
* data
= nullptr;
1249 int status
= XGetWindowProperty(sd
.display
, rootWindow
, _ncl
, 0L, (~0L), False
, AnyPropertyType
, &actualType
, &actualFormat
, &numWindows
, &bytesAfter
, &data
);
1251 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr, true);
1252 const ScopedFreeData
sfd(data
);
1254 CARLA_SAFE_ASSERT_RETURN(status
== Success
, true);
1255 CARLA_SAFE_ASSERT_RETURN(actualFormat
== 32, true);
1256 CARLA_SAFE_ASSERT_RETURN(numWindows
!= 0, true);
1258 Window
* windows
= (Window
*)data
;
1259 Window lastGoodWindowPID
= 0, lastGoodWindowNameSimple
= 0, lastGoodWindowNameUTF8
= 0;
1261 for (ulong i
= 0; i
< numWindows
; i
++)
1263 const Window
window(windows
[i
]);
1264 CARLA_SAFE_ASSERT_CONTINUE(window
!= 0);
1266 // ------------------------------------------------
1272 uchar
* pidData
= nullptr;
1274 status
= XGetWindowProperty(sd
.display
, window
, _nwp
, 0L, (~0L), False
, XA_CARDINAL
, &actualType
, &actualFormat
, &pidSize
, &bytesAfter
, &pidData
);
1276 if (pidData
!= nullptr)
1278 const ScopedFreeData
sfd2(pidData
);
1279 CARLA_SAFE_ASSERT_CONTINUE(status
== Success
);
1280 CARLA_SAFE_ASSERT_CONTINUE(pidSize
!= 0);
1282 if (*(ulong
*)pidData
== static_cast<ulong
>(pid
))
1283 lastGoodWindowPID
= window
;
1287 // ------------------------------------------------
1288 // try using name (UTF-8)
1291 uchar
* nameData
= nullptr;
1293 status
= XGetWindowProperty(sd
.display
, window
, _nwn
, 0L, (~0L), False
, utf8
, &actualType
, &actualFormat
, &nameSize
, &bytesAfter
, &nameData
);
1295 if (nameData
!= nullptr)
1297 const ScopedFreeData
sfd2(nameData
);
1298 CARLA_SAFE_ASSERT_CONTINUE(status
== Success
);
1300 if (nameSize
!= 0 && std::strstr((const char*)nameData
, uiTitle
) != nullptr)
1301 lastGoodWindowNameUTF8
= window
;
1304 // ------------------------------------------------
1305 // try using name (simple)
1307 char* wmName
= nullptr;
1309 status
= XFetchName(sd
.display
, window
, &wmName
);
1311 if (wmName
!= nullptr)
1313 const ScopedFreeData
sfd2(wmName
);
1314 CARLA_SAFE_ASSERT_CONTINUE(status
!= 0);
1316 if (std::strstr(wmName
, uiTitle
) != nullptr)
1317 lastGoodWindowNameSimple
= window
;
1321 if (lastGoodWindowPID
== 0 && lastGoodWindowNameSimple
== 0 && lastGoodWindowNameUTF8
== 0)
1326 if (lastGoodWindowPID
!= 0)
1328 if (lastGoodWindowPID
== lastGoodWindowNameSimple
&& lastGoodWindowPID
== lastGoodWindowNameUTF8
)
1330 carla_stdout("Match found using pid, simple and UTF-8 name all at once, nice!");
1331 windowToMap
= lastGoodWindowPID
;
1333 else if (lastGoodWindowPID
== lastGoodWindowNameUTF8
)
1335 carla_stdout("Match found using pid and UTF-8 name");
1336 windowToMap
= lastGoodWindowPID
;
1338 else if (lastGoodWindowPID
== lastGoodWindowNameSimple
)
1340 carla_stdout("Match found using pid and simple name");
1341 windowToMap
= lastGoodWindowPID
;
1343 else if (lastGoodWindowNameUTF8
!= 0)
1345 if (lastGoodWindowNameUTF8
== lastGoodWindowNameSimple
)
1347 carla_stdout("Match found using simple and UTF-8 name (ignoring pid)");
1348 windowToMap
= lastGoodWindowNameUTF8
;
1352 carla_stdout("Match found using UTF-8 name (ignoring pid)");
1353 windowToMap
= lastGoodWindowNameUTF8
;
1358 carla_stdout("Match found using pid");
1359 windowToMap
= lastGoodWindowPID
;
1362 else if (lastGoodWindowNameUTF8
!= 0)
1364 if (lastGoodWindowNameUTF8
== lastGoodWindowNameSimple
)
1366 carla_stdout("Match found using simple and UTF-8 name");
1367 windowToMap
= lastGoodWindowNameUTF8
;
1371 carla_stdout("Match found using UTF-8 name");
1372 windowToMap
= lastGoodWindowNameUTF8
;
1377 carla_stdout("Match found using simple name");
1378 windowToMap
= lastGoodWindowNameSimple
;
1381 const Atom _nwt
= XInternAtom(sd
.display
,"_NET_WM_STATE", False
);
1382 const Atom _nws
[2] = {
1383 XInternAtom(sd
.display
, "_NET_WM_STATE_SKIP_TASKBAR", False
),
1384 XInternAtom(sd
.display
, "_NET_WM_STATE_SKIP_PAGER", False
)
1386 XChangeProperty(sd
.display
, windowToMap
, _nwt
, XA_ATOM
, 32, PropModeAppend
, (const uchar
*)_nws
, 2);
1388 const Atom _nwi
= XInternAtom(sd
.display
, "_NET_WM_ICON", False
);
1389 XChangeProperty(sd
.display
, windowToMap
, _nwi
, XA_CARDINAL
, 32, PropModeReplace
, (const uchar
*)sCarlaX11Icon
, sCarlaX11IconSize
);
1391 const Window
hostWinId((Window
)winId
);
1393 XSetTransientForHint(sd
.display
, windowToMap
, hostWinId
);
1395 if (centerUI
&& false /* moving the window after being shown isn't pretty... */)
1397 int hostX
, hostY
, pluginX
, pluginY
;
1398 uint hostWidth
, hostHeight
, pluginWidth
, pluginHeight
, border
, depth
;
1401 if (XGetGeometry(sd
.display
, hostWinId
, &retWindow
, &hostX
, &hostY
, &hostWidth
, &hostHeight
, &border
, &depth
) != 0 &&
1402 XGetGeometry(sd
.display
, windowToMap
, &retWindow
, &pluginX
, &pluginY
, &pluginWidth
, &pluginHeight
, &border
, &depth
) != 0)
1404 if (XTranslateCoordinates(sd
.display
, hostWinId
, rootWindow
, hostX
, hostY
, &hostX
, &hostY
, &retWindow
) == True
&&
1405 XTranslateCoordinates(sd
.display
, windowToMap
, rootWindow
, pluginX
, pluginY
, &pluginX
, &pluginY
, &retWindow
) == True
)
1407 const int newX
= hostX
+ int(hostWidth
/2 - pluginWidth
/2);
1408 const int newY
= hostY
+ int(hostHeight
/2 - pluginHeight
/2);
1410 XMoveWindow(sd
.display
, windowToMap
, newX
, newY
);
1415 // focusing the host UI and then the plugin UI forces the WM to repaint the plugin window icon
1416 XRaiseWindow(sd
.display
, hostWinId
);
1417 XSetInputFocus(sd
.display
, hostWinId
, RevertToPointerRoot
, CurrentTime
);
1419 XRaiseWindow(sd
.display
, windowToMap
);
1420 XSetInputFocus(sd
.display
, windowToMap
, RevertToPointerRoot
, CurrentTime
);
1427 uint
const hints
= kCGWindowListOptionOnScreenOnly
|kCGWindowListExcludeDesktopElements
;
1429 CFArrayRef
const windowListRef
= CGWindowListCopyWindowInfo(hints
, kCGNullWindowID
);
1430 const NSArray
* const windowList
= (const NSArray
*)windowListRef
;
1432 int windowToMap
, windowWithPID
= 0, windowWithNameAndPID
= 0;
1434 const NSDictionary
* entry
;
1435 for (entry in windowList
)
1437 // FIXME: is this needed? is old version safe?
1438 #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
1439 if ([entry
[(id
)kCGWindowSharingState
] intValue
] == kCGWindowSharingNone
)
1442 NSString
* const windowName
= entry
[(id
)kCGWindowName
];
1443 int const windowNumber
= [entry
[(id
)kCGWindowNumber
] intValue
];
1444 uintptr_t const windowPID
= [entry
[(id
)kCGWindowOwnerPID
] intValue
];
1446 if ([[entry objectForKey
:(id
)kCGWindowSharingState
] intValue
] == kCGWindowSharingNone
)
1449 NSString
* const windowName
= [entry objectForKey
:(id
)kCGWindowName
];
1450 int const windowNumber
= [[entry objectForKey
:(id
)kCGWindowNumber
] intValue
];
1451 uintptr_t const windowPID
= [[entry objectForKey
:(id
)kCGWindowOwnerPID
] intValue
];
1454 if (windowPID
!= pid
)
1457 windowWithPID
= windowNumber
;
1459 if (windowName
!= nullptr && std::strcmp([windowName UTF8String
], uiTitle
) == 0)
1460 windowWithNameAndPID
= windowNumber
;
1463 CFRelease(windowListRef
);
1465 if (windowWithNameAndPID
!= 0)
1467 carla_stdout("Match found using pid and name");
1468 windowToMap
= windowWithNameAndPID
;
1470 else if (windowWithPID
!= 0)
1472 carla_stdout("Match found using pid");
1473 windowToMap
= windowWithPID
;
1480 NSWindow
* const parentWindow
= [NSApp windowWithWindowNumber
:winId
];
1481 CARLA_SAFE_ASSERT_RETURN(parentWindow
!= nullptr, false);
1483 [parentWindow orderWindow
:NSWindowBelow
1484 relativeTo
:windowToMap
];
1489 if (HWND
const childWindow
= FindWindowA(nullptr, uiTitle
))
1491 HWND
const parentWindow
= (HWND
)winId
;
1492 SetWindowLongPtr(childWindow
, GWLP_HWNDPARENT
, (LONG_PTR
)parentWindow
);
1496 RECT rectChild
, rectParent
;
1498 if (GetWindowRect(childWindow
, &rectChild
) && GetWindowRect(parentWindow
, &rectParent
))
1500 SetWindowPos(childWindow
, parentWindow
,
1501 rectParent
.left
+ (rectChild
.right
-rectChild
.left
)/2,
1502 rectParent
.top
+ (rectChild
.bottom
-rectChild
.top
)/2,
1507 carla_stdout("Match found using window name");
1514 // fallback, may be unused
1516 (void)pid
; (void)centerUI
;
1518 #endif // BUILD_BRIDGE_ALTERNATIVE_ARCH
1520 // -----------------------------------------------------
1523 CarlaPluginUI
* CarlaPluginUI::newX11(Callback
* const cb
,
1524 const uintptr_t parentId
,
1525 const bool isStandalone
,
1526 const bool isResizable
,
1529 return new X11PluginUI(cb
, parentId
, isStandalone
, isResizable
, isLV2
);
1534 CarlaPluginUI
* CarlaPluginUI::newCocoa(Callback
* const cb
,
1535 const uintptr_t parentId
,
1536 const bool isStandalone
,
1537 const bool isResizable
)
1539 return new CocoaPluginUI(cb
, parentId
, isStandalone
, isResizable
);
1544 CarlaPluginUI
* CarlaPluginUI::newWindows(Callback
* const cb
,
1545 const uintptr_t parentId
,
1546 const bool isStandalone
,
1547 const bool isResizable
)
1549 return new WindowsPluginUI(cb
, parentId
, isStandalone
, isResizable
);
1553 // -----------------------------------------------------