VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / utils / CarlaPluginUI.cpp
blobbddb2c8e573cc2cba84731c011d9229a34a9a5c3
1 /*
2 * Carla Plugin UI
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"
21 #ifdef HAVE_X11
22 # include <pthread.h>
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"
28 #endif
30 #ifdef CARLA_OS_MAC
31 # include "CarlaMacUtils.hpp"
32 # import <Cocoa/Cocoa.h>
33 #endif
35 #ifdef CARLA_OS_WIN
36 # include <ctime>
37 # include "water/common.hpp"
38 #endif
40 #ifndef CARLA_PLUGIN_UI_CLASS_PREFIX
41 # error CARLA_PLUGIN_UI_CLASS_PREFIX undefined
42 #endif
44 // ---------------------------------------------------------------------------------------------------------------------
45 // X11
47 #ifdef HAVE_X11
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"
57 # endif
58 static pthread_mutex_t gErrorMutex = PTHREAD_MUTEX_INITIALIZER;
59 # if defined(__GNUC__) && (__GNUC__ >= 5) && ! defined(__clang__)
60 # pragma GCC diagnostic pop
61 # endif
63 static int temporaryErrorHandler(Display*, XErrorEvent*)
65 gErrorTriggered = true;
66 return 0;
69 class X11PluginUI : public CarlaPluginUI
71 public:
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),
75 fDisplay(nullptr),
76 fHostWindow(0),
77 fChildWindow(0),
78 fChildWindowConfigured(false),
79 fChildWindowMonitoring(isResizable || canMonitorChildren),
80 fIsVisible(false),
81 fFirstShow(true),
82 fSetSizeCalledAtLeastOnce(false),
83 fMinimumWidth(0),
84 fMinimumHeight(0),
85 fEventProc(nullptr)
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),
101 0, 0, 300, 300, 0,
102 DefaultDepth(fDisplay, screen),
103 InputOutput,
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);
131 if (parentId != 0)
132 setTransientWinId(parentId);
135 ~X11PluginUI() override
137 CARLA_SAFE_ASSERT(! fIsVisible);
139 if (fDisplay == nullptr)
140 return;
142 if (fIsVisible)
144 XUnmapWindow(fDisplay, fHostWindow);
145 fIsVisible = false;
148 if (fHostWindow != 0)
150 XDestroyWindow(fDisplay, fHostWindow);
151 fHostWindow = 0;
154 XCloseDisplay(fDisplay);
155 fDisplay = nullptr;
158 void show() override
160 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
161 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
163 if (fFirstShow)
165 if (const Window childWindow = getChildWindow())
167 if (! fSetSizeCalledAtLeastOnce)
169 int width = 0;
170 int height = 0;
172 XWindowAttributes attrs = {};
174 pthread_mutex_lock(&gErrorMutex);
175 const XErrorHandler oldErrorHandler = XSetErrorHandler(temporaryErrorHandler);
176 gErrorTriggered = false;
178 if (XGetWindowAttributes(fDisplay, childWindow, &attrs))
180 width = attrs.width;
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;
216 Atom actualType;
217 int actualFormat;
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);
235 fIsVisible = true;
236 fFirstShow = false;
238 XMapRaised(fDisplay, fHostWindow);
239 XSync(fDisplay, False);
242 void hide() override
244 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
245 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
247 fIsVisible = false;
248 XUnmapWindow(fDisplay, fHostWindow);
249 XFlush(fDisplay);
252 void idle() override
254 // prevent recursion
255 if (fIsIdling) return;
257 uint nextChildWidth = 0;
258 uint nextChildHeight = 0;
260 uint nextHostWidth = 0;
261 uint nextHostHeight = 0;
263 fIsIdling = true;
265 for (XEvent event; XPending(fDisplay) > 0;)
267 XNextEvent(fDisplay, &event);
269 if (! fIsVisible)
270 continue;
272 char* type = nullptr;
274 switch (event.type)
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);
291 break;
293 case ClientMessage:
294 type = XGetAtomName(fDisplay, event.xclient.message_type);
295 CARLA_SAFE_ASSERT_CONTINUE(type != nullptr);
297 if (std::strcmp(type, "WM_PROTOCOLS") == 0)
299 fIsVisible = false;
300 CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
301 fCallback->handlePluginUIClosed();
303 break;
305 case KeyRelease:
306 if (event.xkey.keycode == X11Key_Escape)
308 fIsVisible = false;
309 CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
310 fCallback->handlePluginUIClosed();
312 break;
314 case FocusIn:
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);
326 break;
329 if (type != nullptr)
330 XFree(type);
331 else if (fEventProc != nullptr && event.type != FocusIn && event.type != FocusOut)
332 fEventProc(&event);
335 if (nextChildWidth != 0 && nextChildHeight != 0 && fChildWindow != 0)
337 applyHintsFromChildWindow();
338 XResizeWindow(fDisplay, fHostWindow, nextChildWidth, nextChildHeight);
339 // XFlush(fDisplay);
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);
355 fIsIdling = false;
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);
405 if (! fIsResizable)
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);
419 if (forceUpdate)
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,
434 PropModeReplace,
435 (const uchar*)(title),
436 (int)strlen(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
461 return fDisplay;
464 protected:
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);
484 if (gErrorTriggered)
486 carla_stdout("Caught errors while accessing child window");
487 fChildWindow = 0;
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];
508 XFree(childWindows);
511 return ret;
514 private:
515 Display* fDisplay;
516 Window fHostWindow;
517 Window fChildWindow;
518 bool fChildWindowConfigured;
519 bool fChildWindowMonitoring;
520 bool fIsVisible;
521 bool fFirstShow;
522 bool fSetSizeCalledAtLeastOnce;
523 uint fMinimumWidth;
524 uint fMinimumHeight;
525 EventProcPtr fEventProc;
527 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(X11PluginUI)
529 #endif // HAVE_X11
531 // ---------------------------------------------------------------------------------------------------------------------
532 // MacOS / Cocoa
534 #ifdef CARLA_OS_MAC
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)
542 #else
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)
545 #endif
547 @interface CarlaPluginWindow : NSWindow
548 - (id) initWithContentRect:(NSRect)contentRect
549 styleMask:(unsigned int)aStyle
550 backing:(NSBackingStoreType)bufferingType
551 defer:(BOOL)flag;
552 - (BOOL) canBecomeKeyWindow;
553 - (BOOL) canBecomeMainWindow;
554 @end
556 @implementation CarlaPluginWindow
558 - (id)initWithContentRect:(NSRect)contentRect
559 styleMask:(unsigned int)aStyle
560 backing:(NSBackingStoreType)bufferingType
561 defer:(BOOL)flag
563 NSWindow* result = [super initWithContentRect:contentRect
564 styleMask:aStyle
565 backing:bufferingType
566 defer:flag];
568 [result setAcceptsMouseMovedEvents:YES];
570 return (CarlaPluginWindow*)result;
572 // unused
573 (void)flag;
576 - (BOOL)canBecomeKeyWindow
578 return YES;
581 - (BOOL)canBecomeMainWindow
583 return NO;
586 @end
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;
597 @end
599 @implementation CarlaPluginWindowDelegate
601 - (instancetype)initWithWindowAndCallback:(CarlaPluginWindow*)window2
602 callback:(CarlaPluginUI::Callback*)callback2
604 if ((self = [super init]))
606 callback = callback2;
607 window = window2;
610 return self;
613 - (BOOL)windowShouldClose:(id)sender
615 if (callback != nil)
616 callback->handlePluginUIClosed();
618 return NO;
620 // unused
621 (void)sender;
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;
633 break;
636 return frameSize;
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];
650 break;
655 @end
657 class CocoaPluginUI : public CarlaPluginUI
659 public:
660 CocoaPluginUI(Callback* const callback, const uintptr_t parentId, const bool isStandalone, const bool isResizable) noexcept
661 : CarlaPluginUI(callback, isStandalone, isResizable),
662 fView(nullptr),
663 fParentWindow(nullptr),
664 fWindow(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,)
673 #ifdef __MAC_10_12
674 uint style = NSWindowStyleMaskClosable | NSWindowStyleMaskTitled;
675 #else
676 uint style = NSClosableWindowMask | NSTitledWindowMask;
677 #endif
679 if (isResizable)
680 style |= NSResizableWindowMask;
683 const NSRect frame = NSMakeRect(0, 0, 100, 100);
685 fWindow = [[[CarlaPluginWindow alloc]
686 initWithContentRect:frame
687 styleMask:style
688 backing:NSBackingStoreBuffered
689 defer:NO
690 ] retain];
692 if (fWindow == nullptr)
694 [fView release];
695 fView = nullptr;
696 return;
699 ((NSWindow*)fWindow).delegate = [[[CarlaPluginWindowDelegate alloc]
700 initWithWindowAndCallback:fWindow
701 callback:callback] retain];
704 if (isResizable)
706 [fView setAutoresizingMask:(NSViewWidthSizable |
707 NSViewHeightSizable |
708 NSViewMinXMargin |
709 NSViewMaxXMargin |
710 NSViewMinYMargin |
711 NSViewMaxYMargin)];
712 [fView setAutoresizesSubviews:YES];
714 else
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];
727 if (parentId != 0)
728 setTransientWinId(parentId);
731 ~CocoaPluginUI() override
733 carla_debug("CocoaPluginUI::~CocoaPluginUI()");
734 if (fView == nullptr)
735 return;
737 [fView setHidden:YES];
738 [fView removeFromSuperview];
739 [fWindow close];
740 [fView release];
741 [fWindow release];
744 void show() override
746 carla_debug("CocoaPluginUI::show()");
747 CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
749 if (fParentWindow != nullptr)
751 [fParentWindow addChildWindow:fWindow
752 ordered:NSWindowAbove];
754 else
756 [fWindow setIsVisible:YES];
760 void hide() override
762 carla_debug("CocoaPluginUI::hide()");
763 CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
765 [fWindow setIsVisible:NO];
767 if (fParentWindow != nullptr)
768 [fParentWindow removeChildWindow:fWindow];
771 void idle() override
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];
785 break;
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
801 // TODO
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]];
821 break;
826 if (fIsResizable)
828 [fWindow setContentMinSize:NSMakeSize(1, 1)];
829 [fWindow setContentMaxSize:NSMakeSize(99999, 99999)];
831 else
833 [fWindow setContentMinSize:size];
834 [fWindow setContentMaxSize:size];
838 if (forceUpdate)
840 // FIXME, not enough
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]
851 initWithBytes:title
852 length:strlen(title)
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()");
889 return (void*)fView;
892 void* getDisplay() const noexcept
894 carla_debug("CocoaPluginUI::getDisplay()");
895 return (void*)fWindow;
898 private:
899 NSView* fView;
900 NSWindow* fParentWindow;
901 CarlaPluginWindow* fWindow;
903 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CocoaPluginUI)
906 #endif // CARLA_OS_MAC
908 // ---------------------------------------------------------------------------------------------------------------------
909 // Windows
911 #ifdef CARLA_OS_WIN
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
919 public:
920 WindowsPluginUI(Callback* const cb, const uintptr_t parentId, const bool isStandalone, const bool isResizable) noexcept
921 : CarlaPluginUI(cb, isStandalone, isResizable),
922 fWindow(nullptr),
923 fChildWindow(nullptr),
924 fParentWindow(nullptr),
925 fIsVisible(false),
926 fFirstShow(true)
928 // FIXME
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);
947 return;
950 int winFlags = WS_POPUPWINDOW | WS_CAPTION;
952 if (isResizable)
953 winFlags |= WS_SIZEBOX;
955 #ifdef BUILDING_CARLA_FOR_WINE
956 const uint winType = WS_EX_DLGMODALFRAME;
957 const HWND parent = nullptr;
958 #else
959 const uint winType = WS_EX_TOOLWINDOW;
960 const HWND parent = (HWND)parentId;
961 #endif
963 fWindow = CreateWindowExA(winType,
964 classNameBuf, "Carla Plugin UI", winFlags,
965 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
966 parent, nullptr,
967 hInstance, nullptr);
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);
976 return;
979 SetWindowLongPtr(fWindow, GWLP_USERDATA, (LONG_PTR)this);
981 #ifndef BUILDING_CARLA_FOR_WINE
982 if (parentId != 0)
983 setTransientWinId(parentId);
984 #endif
985 return;
987 // maybe unused
988 (void)parentId;
991 ~WindowsPluginUI() override
993 CARLA_SAFE_ASSERT(! fIsVisible);
995 if (fWindow != 0)
997 if (fIsVisible)
998 ShowWindow(fWindow, SW_HIDE);
1000 DestroyWindow(fWindow);
1001 fWindow = 0;
1004 // FIXME
1005 UnregisterClassA(fWindowClass.lpszClassName, nullptr);
1006 free((void*)fWindowClass.lpszClassName);
1009 void show() override
1011 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
1013 if (fFirstShow)
1015 fFirstShow = false;
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);
1030 else
1032 ShowWindow(fWindow, SW_SHOWNORMAL);
1035 else
1037 ShowWindow(fWindow, SW_RESTORE);
1040 fIsVisible = true;
1041 UpdateWindow(fWindow);
1044 void hide() override
1046 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
1048 ShowWindow(fWindow, SW_HIDE);
1049 fIsVisible = false;
1050 UpdateWindow(fWindow);
1053 void idle() override
1055 if (fIsIdling || fWindow == nullptr)
1056 return;
1058 MSG msg;
1059 fIsIdling = true;
1061 while (::PeekMessage(&msg, fWindow, 0, 0, PM_REMOVE))
1063 switch (msg.message)
1065 case WM_QUIT:
1066 case CARLA_LOCAL_CLOSE_MSG:
1067 fIsVisible = false;
1068 CARLA_SAFE_ASSERT_BREAK(fCallback != nullptr);
1069 fCallback->handlePluginUIClosed();
1070 break;
1073 DispatchMessageA(&msg);
1076 fIsIdling = false;
1079 LRESULT checkAndHandleMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1081 if (fWindow == hwnd)
1083 switch (message)
1085 case WM_SIZE:
1086 if (fChildWindow != nullptr)
1088 RECT rect;
1089 GetClientRect(fWindow, &rect);
1090 SetWindowPos(fChildWindow, 0, 0, 0, rect.right, rect.bottom,
1091 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
1093 break;
1094 case WM_QUIT:
1095 case CARLA_LOCAL_CLOSE_MSG:
1096 fIsVisible = false;
1097 CARLA_SAFE_ASSERT_BREAK(fCallback != nullptr);
1098 fCallback->handlePluginUIClosed();
1099 break;
1103 return DefWindowProcA(hwnd, message, wParam, lParam);
1106 void focus() override
1108 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
1110 SetForegroundWindow(fWindow);
1111 SetActiveWindow(fWindow);
1112 SetFocus(fWindow);
1115 void setMinimumSize(uint, uint) override
1117 // TODO
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);
1131 if (forceUpdate)
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
1164 return nullptr;
1167 private:
1168 HWND fWindow;
1169 HWND fChildWindow;
1170 HWND fParentWindow;
1171 WNDCLASSA fWindowClass;
1173 bool fIsVisible;
1174 bool fFirstShow;
1176 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WindowsPluginUI)
1179 LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1181 switch (message)
1183 case WM_CLOSE:
1184 PostMessage(hwnd, CARLA_LOCAL_CLOSE_MSG, wParam, lParam);
1185 return 0;
1187 #if 0
1188 case WM_CREATE:
1189 PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0);
1190 return 0;
1192 case WM_DESTROY:
1193 return 0;
1194 #endif
1196 default:
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 {
1214 Display* display;
1215 ScopedDisplay() : display(XOpenDisplay(nullptr)) {}
1216 ~ScopedDisplay() { if (display!=nullptr) XCloseDisplay(display); }
1217 // c++ compat stuff
1218 CARLA_PREVENT_HEAP_ALLOCATION
1219 CARLA_DECLARE_NON_COPYABLE(ScopedDisplay)
1221 struct ScopedFreeData {
1222 union {
1223 char* data;
1224 uchar* udata;
1226 ScopedFreeData(char* d) : data(d) {}
1227 ScopedFreeData(uchar* d) : udata(d) {}
1228 ~ScopedFreeData() { XFree(data); }
1229 // c++ compat stuff
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);
1244 Atom actualType;
1245 int actualFormat;
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 // ------------------------------------------------
1267 // try using pid
1269 if (pid != 0)
1271 ulong pidSize;
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)
1290 ulong nameSize;
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)
1322 return false;
1324 Window windowToMap;
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;
1350 else
1352 carla_stdout("Match found using UTF-8 name (ignoring pid)");
1353 windowToMap = lastGoodWindowNameUTF8;
1356 else
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;
1369 else
1371 carla_stdout("Match found using UTF-8 name");
1372 windowToMap = lastGoodWindowNameUTF8;
1375 else
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;
1399 Window retWindow;
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);
1422 XFlush(sd.display);
1423 return true;
1424 #endif
1426 #ifdef CARLA_OS_MAC
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)
1440 continue;
1442 NSString* const windowName = entry[(id)kCGWindowName];
1443 int const windowNumber = [entry[(id)kCGWindowNumber] intValue];
1444 uintptr_t const windowPID = [entry[(id)kCGWindowOwnerPID] intValue];
1445 #else
1446 if ([[entry objectForKey:(id)kCGWindowSharingState] intValue] == kCGWindowSharingNone)
1447 continue;
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];
1452 #endif
1454 if (windowPID != pid)
1455 continue;
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;
1475 else
1477 return false;
1480 NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
1481 CARLA_SAFE_ASSERT_RETURN(parentWindow != nullptr, false);
1483 [parentWindow orderWindow:NSWindowBelow
1484 relativeTo:windowToMap];
1485 return true;
1486 #endif
1488 #ifdef CARLA_OS_WIN
1489 if (HWND const childWindow = FindWindowA(nullptr, uiTitle))
1491 HWND const parentWindow = (HWND)winId;
1492 SetWindowLongPtr(childWindow, GWLP_HWNDPARENT, (LONG_PTR)parentWindow);
1494 if (centerUI)
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,
1503 0, 0, SWP_NOSIZE);
1507 carla_stdout("Match found using window name");
1508 return true;
1511 return false;
1512 #endif
1514 // fallback, may be unused
1515 return true;
1516 (void)pid; (void)centerUI;
1518 #endif // BUILD_BRIDGE_ALTERNATIVE_ARCH
1520 // -----------------------------------------------------
1522 #ifdef HAVE_X11
1523 CarlaPluginUI* CarlaPluginUI::newX11(Callback* const cb,
1524 const uintptr_t parentId,
1525 const bool isStandalone,
1526 const bool isResizable,
1527 const bool isLV2)
1529 return new X11PluginUI(cb, parentId, isStandalone, isResizable, isLV2);
1531 #endif
1533 #ifdef CARLA_OS_MAC
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);
1541 #endif
1543 #ifdef CARLA_OS_WIN
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);
1551 #endif
1553 // -----------------------------------------------------