Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1445 lines
44KB

  1. /*
  2. * Carla Plugin UI
  3. * Copyright (C) 2014-2021 Filipe Coelho <falktx@falktx.com>
  4. *
  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.
  9. *
  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.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "CarlaJuceUtils.hpp"
  18. #include "CarlaPluginUI.hpp"
  19. #ifdef HAVE_X11
  20. # include <sys/types.h>
  21. # include <X11/Xatom.h>
  22. # include <X11/Xlib.h>
  23. # include <X11/Xutil.h>
  24. # include "CarlaPluginUI_X11Icon.hpp"
  25. #endif
  26. #ifdef CARLA_OS_MAC
  27. # include "CarlaMacUtils.hpp"
  28. # import <Cocoa/Cocoa.h>
  29. #endif
  30. #ifdef CARLA_OS_WIN
  31. # include <ctime>
  32. # include "water/common.hpp"
  33. #endif
  34. #ifndef CARLA_PLUGIN_UI_CLASS_PREFIX
  35. # error CARLA_PLUGIN_UI_CLASS_PREFIX undefined
  36. #endif
  37. // ---------------------------------------------------------------------------------------------------------------------
  38. // X11
  39. #ifdef HAVE_X11
  40. typedef void (*EventProcPtr)(XEvent* ev);
  41. static const uint X11Key_Escape = 9;
  42. static bool gErrorTriggered = false;
  43. static pthread_mutex_t gErrorMutex = PTHREAD_MUTEX_INITIALIZER;
  44. static int temporaryErrorHandler(Display*, XErrorEvent*)
  45. {
  46. gErrorTriggered = true;
  47. return 0;
  48. }
  49. class X11PluginUI : public CarlaPluginUI
  50. {
  51. public:
  52. X11PluginUI(Callback* const cb, const uintptr_t parentId,
  53. const bool isStandalone, const bool isResizable, const bool canMonitorChildren) noexcept
  54. : CarlaPluginUI(cb, isStandalone, isResizable),
  55. fDisplay(nullptr),
  56. fHostWindow(0),
  57. fChildWindow(0),
  58. fChildWindowConfigured(false),
  59. fChildWindowMonitoring(isResizable || canMonitorChildren),
  60. fIsVisible(false),
  61. fFirstShow(true),
  62. fSetSizeCalledAtLeastOnce(false),
  63. fEventProc(nullptr)
  64. {
  65. fDisplay = XOpenDisplay(nullptr);
  66. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  67. const int screen = DefaultScreen(fDisplay);
  68. XSetWindowAttributes attr;
  69. carla_zeroStruct(attr);
  70. attr.event_mask = KeyPressMask|KeyReleaseMask|FocusChangeMask;
  71. if (fChildWindowMonitoring)
  72. attr.event_mask |= StructureNotifyMask|SubstructureNotifyMask;
  73. fHostWindow = XCreateWindow(fDisplay, RootWindow(fDisplay, screen),
  74. 0, 0, 300, 300, 0,
  75. DefaultDepth(fDisplay, screen),
  76. InputOutput,
  77. DefaultVisual(fDisplay, screen),
  78. CWBorderPixel|CWEventMask, &attr);
  79. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  80. XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fHostWindow, 1, GrabModeAsync, GrabModeAsync);
  81. Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True);
  82. XSetWMProtocols(fDisplay, fHostWindow, &wmDelete, 1);
  83. const pid_t pid = getpid();
  84. const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False);
  85. XChangeProperty(fDisplay, fHostWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  86. const Atom _nwi = XInternAtom(fDisplay, "_NET_WM_ICON", False);
  87. XChangeProperty(fDisplay, fHostWindow, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
  88. const Atom _wt = XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE", False);
  89. // Setting the window to both dialog and normal will produce a decorated floating dialog
  90. // Order is important: DIALOG needs to come before NORMAL
  91. const Atom _wts[2] = {
  92. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
  93. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
  94. };
  95. XChangeProperty(fDisplay, fHostWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
  96. if (parentId != 0)
  97. setTransientWinId(parentId);
  98. }
  99. ~X11PluginUI() override
  100. {
  101. CARLA_SAFE_ASSERT(! fIsVisible);
  102. if (fDisplay == nullptr)
  103. return;
  104. if (fIsVisible)
  105. {
  106. XUnmapWindow(fDisplay, fHostWindow);
  107. fIsVisible = false;
  108. }
  109. if (fHostWindow != 0)
  110. {
  111. XDestroyWindow(fDisplay, fHostWindow);
  112. fHostWindow = 0;
  113. }
  114. XCloseDisplay(fDisplay);
  115. fDisplay = nullptr;
  116. }
  117. void show() override
  118. {
  119. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  120. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  121. if (fFirstShow)
  122. {
  123. if (const Window childWindow = getChildWindow())
  124. {
  125. if (! fSetSizeCalledAtLeastOnce)
  126. {
  127. int width = 0;
  128. int height = 0;
  129. XWindowAttributes attrs;
  130. carla_zeroStruct(attrs);
  131. pthread_mutex_lock(&gErrorMutex);
  132. const XErrorHandler oldErrorHandler = XSetErrorHandler(temporaryErrorHandler);
  133. gErrorTriggered = false;
  134. if (XGetWindowAttributes(fDisplay, childWindow, &attrs))
  135. {
  136. width = attrs.width;
  137. height = attrs.height;
  138. }
  139. XSetErrorHandler(oldErrorHandler);
  140. pthread_mutex_unlock(&gErrorMutex);
  141. if (width == 0 && height == 0)
  142. {
  143. XSizeHints sizeHints;
  144. carla_zeroStruct(sizeHints);
  145. if (XGetNormalHints(fDisplay, childWindow, &sizeHints))
  146. {
  147. if (sizeHints.flags & PSize)
  148. {
  149. width = sizeHints.width;
  150. height = sizeHints.height;
  151. }
  152. else if (sizeHints.flags & PBaseSize)
  153. {
  154. width = sizeHints.base_width;
  155. height = sizeHints.base_height;
  156. }
  157. }
  158. }
  159. if (width > 1 && height > 1)
  160. setSize(static_cast<uint>(width), static_cast<uint>(height), false);
  161. }
  162. const Atom _xevp = XInternAtom(fDisplay, "_XEventProc", False);
  163. pthread_mutex_lock(&gErrorMutex);
  164. const XErrorHandler oldErrorHandler(XSetErrorHandler(temporaryErrorHandler));
  165. gErrorTriggered = false;
  166. Atom actualType;
  167. int actualFormat;
  168. ulong nitems, bytesAfter;
  169. uchar* data = nullptr;
  170. XGetWindowProperty(fDisplay, childWindow, _xevp, 0, 1, False, AnyPropertyType,
  171. &actualType, &actualFormat, &nitems, &bytesAfter, &data);
  172. XSetErrorHandler(oldErrorHandler);
  173. pthread_mutex_unlock(&gErrorMutex);
  174. if (nitems == 1 && ! gErrorTriggered)
  175. {
  176. fEventProc = *reinterpret_cast<EventProcPtr*>(data);
  177. XMapRaised(fDisplay, childWindow);
  178. }
  179. }
  180. }
  181. fIsVisible = true;
  182. fFirstShow = false;
  183. XMapRaised(fDisplay, fHostWindow);
  184. XSync(fDisplay, False);
  185. }
  186. void hide() override
  187. {
  188. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  189. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  190. fIsVisible = false;
  191. XUnmapWindow(fDisplay, fHostWindow);
  192. XFlush(fDisplay);
  193. }
  194. void idle() override
  195. {
  196. // prevent recursion
  197. if (fIsIdling) return;
  198. int nextWidth = 0;
  199. int nextHeight = 0;
  200. fIsIdling = true;
  201. for (XEvent event; XPending(fDisplay) > 0;)
  202. {
  203. XNextEvent(fDisplay, &event);
  204. if (! fIsVisible)
  205. continue;
  206. char* type = nullptr;
  207. switch (event.type)
  208. {
  209. case ConfigureNotify:
  210. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  211. CARLA_SAFE_ASSERT_CONTINUE(event.xconfigure.width > 0);
  212. CARLA_SAFE_ASSERT_CONTINUE(event.xconfigure.height > 0);
  213. if (event.xconfigure.window == fHostWindow)
  214. {
  215. const uint width = static_cast<uint>(event.xconfigure.width);
  216. const uint height = static_cast<uint>(event.xconfigure.height);
  217. if (fChildWindow != 0)
  218. {
  219. if (! fChildWindowConfigured)
  220. {
  221. pthread_mutex_lock(&gErrorMutex);
  222. const XErrorHandler oldErrorHandler = XSetErrorHandler(temporaryErrorHandler);
  223. gErrorTriggered = false;
  224. XSizeHints sizeHints;
  225. carla_zeroStruct(sizeHints);
  226. if (XGetNormalHints(fDisplay, fChildWindow, &sizeHints) && !gErrorTriggered)
  227. {
  228. XSetNormalHints(fDisplay, fHostWindow, &sizeHints);
  229. }
  230. else
  231. {
  232. carla_stdout("Caught errors while accessing child window");
  233. fChildWindow = 0;
  234. }
  235. fChildWindowConfigured = true;
  236. XSetErrorHandler(oldErrorHandler);
  237. pthread_mutex_unlock(&gErrorMutex);
  238. }
  239. if (fChildWindow != 0)
  240. XResizeWindow(fDisplay, fChildWindow, width, height);
  241. }
  242. fCallback->handlePluginUIResized(width, height);
  243. }
  244. else if (fChildWindowMonitoring && event.xconfigure.window == fChildWindow && fChildWindow != 0)
  245. {
  246. nextWidth = event.xconfigure.width;
  247. nextHeight = event.xconfigure.height;
  248. }
  249. break;
  250. case ClientMessage:
  251. type = XGetAtomName(fDisplay, event.xclient.message_type);
  252. CARLA_SAFE_ASSERT_CONTINUE(type != nullptr);
  253. if (std::strcmp(type, "WM_PROTOCOLS") == 0)
  254. {
  255. fIsVisible = false;
  256. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  257. fCallback->handlePluginUIClosed();
  258. }
  259. break;
  260. case KeyRelease:
  261. if (event.xkey.keycode == X11Key_Escape)
  262. {
  263. fIsVisible = false;
  264. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  265. fCallback->handlePluginUIClosed();
  266. }
  267. break;
  268. case FocusIn:
  269. if (fChildWindow == 0)
  270. fChildWindow = getChildWindow();
  271. if (fChildWindow != 0)
  272. XSetInputFocus(fDisplay, fChildWindow, RevertToPointerRoot, CurrentTime);
  273. break;
  274. }
  275. if (type != nullptr)
  276. XFree(type);
  277. else if (fEventProc != nullptr && event.type != FocusIn && event.type != FocusOut)
  278. fEventProc(&event);
  279. }
  280. if (nextWidth != 0 && nextHeight != 0)
  281. {
  282. XSizeHints sizeHints;
  283. carla_zeroStruct(sizeHints);
  284. if (XGetNormalHints(fDisplay, fChildWindow, &sizeHints))
  285. XSetNormalHints(fDisplay, fHostWindow, &sizeHints);
  286. XResizeWindow(fDisplay, fHostWindow, static_cast<uint>(nextWidth), static_cast<uint>(nextHeight));
  287. XFlush(fDisplay);
  288. }
  289. fIsIdling = false;
  290. }
  291. void focus() override
  292. {
  293. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  294. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  295. XWindowAttributes wa;
  296. carla_zeroStruct(wa);
  297. CARLA_SAFE_ASSERT_RETURN(XGetWindowAttributes(fDisplay, fHostWindow, &wa),);
  298. if (wa.map_state == IsViewable)
  299. {
  300. XRaiseWindow(fDisplay, fHostWindow);
  301. XSetInputFocus(fDisplay, fHostWindow, RevertToPointerRoot, CurrentTime);
  302. XSync(fDisplay, False);
  303. }
  304. }
  305. void setSize(const uint width, const uint height, const bool forceUpdate) override
  306. {
  307. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  308. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  309. fSetSizeCalledAtLeastOnce = true;
  310. XResizeWindow(fDisplay, fHostWindow, width, height);
  311. if (fChildWindow != 0)
  312. XResizeWindow(fDisplay, fChildWindow, width, height);
  313. if (! fIsResizable)
  314. {
  315. XSizeHints sizeHints;
  316. carla_zeroStruct(sizeHints);
  317. sizeHints.flags = PSize|PMinSize|PMaxSize;
  318. sizeHints.width = static_cast<int>(width);
  319. sizeHints.height = static_cast<int>(height);
  320. sizeHints.min_width = static_cast<int>(width);
  321. sizeHints.min_height = static_cast<int>(height);
  322. sizeHints.max_width = static_cast<int>(width);
  323. sizeHints.max_height = static_cast<int>(height);
  324. XSetNormalHints(fDisplay, fHostWindow, &sizeHints);
  325. }
  326. if (forceUpdate)
  327. XSync(fDisplay, False);
  328. }
  329. void setTitle(const char* const title) override
  330. {
  331. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  332. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  333. XStoreName(fDisplay, fHostWindow, title);
  334. }
  335. void setTransientWinId(const uintptr_t winId) override
  336. {
  337. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  338. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  339. XSetTransientForHint(fDisplay, fHostWindow, static_cast<Window>(winId));
  340. }
  341. void setChildWindow(void* const winId) override
  342. {
  343. CARLA_SAFE_ASSERT_RETURN(winId != nullptr,);
  344. fChildWindow = (Window)winId;
  345. }
  346. void* getPtr() const noexcept override
  347. {
  348. return (void*)fHostWindow;
  349. }
  350. void* getDisplay() const noexcept override
  351. {
  352. return fDisplay;
  353. }
  354. protected:
  355. Window getChildWindow() const
  356. {
  357. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr, 0);
  358. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0, 0);
  359. Window rootWindow, parentWindow, ret = 0;
  360. Window* childWindows = nullptr;
  361. uint numChildren = 0;
  362. XQueryTree(fDisplay, fHostWindow, &rootWindow, &parentWindow, &childWindows, &numChildren);
  363. if (numChildren > 0 && childWindows != nullptr)
  364. {
  365. ret = childWindows[0];
  366. XFree(childWindows);
  367. }
  368. return ret;
  369. }
  370. private:
  371. Display* fDisplay;
  372. Window fHostWindow;
  373. Window fChildWindow;
  374. bool fChildWindowConfigured;
  375. bool fChildWindowMonitoring;
  376. bool fIsVisible;
  377. bool fFirstShow;
  378. bool fSetSizeCalledAtLeastOnce;
  379. EventProcPtr fEventProc;
  380. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(X11PluginUI)
  381. };
  382. #endif // HAVE_X11
  383. // ---------------------------------------------------------------------------------------------------------------------
  384. // MacOS / Cocoa
  385. #ifdef CARLA_OS_MAC
  386. #if defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
  387. # define CarlaPluginWindow CARLA_JOIN_MACRO(CarlaPluginWindowBridgedArch, CARLA_PLUGIN_UI_CLASS_PREFIX)
  388. #elif defined(BUILD_BRIDGE)
  389. # define CarlaPluginWindow CARLA_JOIN_MACRO(CarlaPluginWindowBridged, CARLA_PLUGIN_UI_CLASS_PREFIX)
  390. #else
  391. # define CarlaPluginWindow CARLA_JOIN_MACRO(CarlaPluginWindow, CARLA_PLUGIN_UI_CLASS_PREFIX)
  392. #endif
  393. @interface CarlaPluginWindow : NSWindow
  394. - (id) initWithContentRect:(NSRect)contentRect
  395. styleMask:(unsigned int)aStyle
  396. backing:(NSBackingStoreType)bufferingType
  397. defer:(BOOL)flag;
  398. - (BOOL) canBecomeKeyWindow;
  399. - (BOOL) canBecomeMainWindow;
  400. @end
  401. @implementation CarlaPluginWindow
  402. - (id)initWithContentRect:(NSRect)contentRect
  403. styleMask:(unsigned int)aStyle
  404. backing:(NSBackingStoreType)bufferingType
  405. defer:(BOOL)flag
  406. {
  407. NSWindow* result = [super initWithContentRect:contentRect
  408. styleMask:aStyle
  409. backing:bufferingType
  410. defer:flag];
  411. [result setAcceptsMouseMovedEvents:YES];
  412. return (CarlaPluginWindow*)result;
  413. // unused
  414. (void)flag;
  415. }
  416. - (BOOL)canBecomeKeyWindow
  417. {
  418. return YES;
  419. }
  420. - (BOOL)canBecomeMainWindow
  421. {
  422. return NO;
  423. }
  424. @end
  425. @interface CarlaPluginWindowDelegate : NSObject<NSWindowDelegate>
  426. {
  427. CarlaPluginUI::Callback* callback;
  428. CarlaPluginWindow* window;
  429. }
  430. - (instancetype)initWithWindowAndCallback:(CarlaPluginWindow*)window
  431. callback:(CarlaPluginUI::Callback*)callback2;
  432. - (BOOL)windowShouldClose:(id)sender;
  433. @end
  434. @implementation CarlaPluginWindowDelegate
  435. - (instancetype)initWithWindowAndCallback:(CarlaPluginWindow*)window2
  436. callback:(CarlaPluginUI::Callback*)callback2
  437. {
  438. if ((self = [super init]))
  439. {
  440. callback = callback2;
  441. window = window2;
  442. }
  443. return self;
  444. }
  445. - (BOOL)windowShouldClose:(id)sender
  446. {
  447. if (callback != nil)
  448. callback->handlePluginUIClosed();
  449. return NO;
  450. // unused
  451. (void)sender;
  452. }
  453. @end
  454. @interface CarlaPluginView : NSView
  455. - (void)resizeWithOldSuperviewSize:(NSSize)oldSize;
  456. @end
  457. @implementation CarlaPluginView
  458. - (void)resizeWithOldSuperviewSize:(NSSize)oldSize
  459. {
  460. [super resizeWithOldSuperviewSize:oldSize];
  461. /*
  462. for (NSView* subview in [self subviews])
  463. {
  464. [subview setFrame:NSMakeRect(0, 0, oldSize.width, oldSize.height)];
  465. break;
  466. }
  467. */
  468. }
  469. @end
  470. class CocoaPluginUI : public CarlaPluginUI
  471. {
  472. public:
  473. CocoaPluginUI(Callback* const callback, const uintptr_t parentId, const bool isStandalone, const bool isResizable) noexcept
  474. : CarlaPluginUI(callback, isStandalone, isResizable),
  475. fView(nullptr),
  476. fParentWindow(nullptr),
  477. fWindow(nullptr)
  478. {
  479. carla_debug("CocoaPluginUI::CocoaPluginUI(%p, " P_UINTPTR, "%s)", callback, parentId, bool2str(isResizable));
  480. const CarlaBackend::AutoNSAutoreleasePool arp;
  481. [NSApplication sharedApplication];
  482. fView = [[CarlaPluginView new]retain];
  483. CARLA_SAFE_ASSERT_RETURN(fView != nullptr,)
  484. uint style = NSClosableWindowMask | NSTitledWindowMask;
  485. /*
  486. if (isResizable)
  487. style |= NSResizableWindowMask;
  488. */
  489. const NSRect frame = NSMakeRect(0, 0, 100, 100);
  490. fWindow = [[[CarlaPluginWindow alloc]
  491. initWithContentRect:frame
  492. styleMask:style
  493. backing:NSBackingStoreBuffered
  494. defer:NO
  495. ] retain];
  496. if (fWindow == nullptr)
  497. {
  498. [fView release];
  499. fView = nullptr;
  500. return;
  501. }
  502. ((NSWindow*)fWindow).delegate = [[[CarlaPluginWindowDelegate alloc]
  503. initWithWindowAndCallback:fWindow
  504. callback:callback] retain];
  505. // if (! isResizable)
  506. {
  507. [fView setAutoresizingMask:NSViewNotSizable];
  508. [[fWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
  509. }
  510. [fWindow setContentView:fView];
  511. [fWindow makeFirstResponder:fView];
  512. [fView setHidden:NO];
  513. if (parentId != 0)
  514. setTransientWinId(parentId);
  515. }
  516. ~CocoaPluginUI() override
  517. {
  518. carla_debug("CocoaPluginUI::~CocoaPluginUI()");
  519. if (fView == nullptr)
  520. return;
  521. [fView setHidden:YES];
  522. [fView removeFromSuperview];
  523. [fWindow close];
  524. [fView release];
  525. [fWindow release];
  526. }
  527. void show() override
  528. {
  529. carla_debug("CocoaPluginUI::show()");
  530. CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
  531. if (fParentWindow != nullptr)
  532. {
  533. [fParentWindow addChildWindow:fWindow
  534. ordered:NSWindowAbove];
  535. }
  536. else
  537. {
  538. [fWindow setIsVisible:YES];
  539. }
  540. }
  541. void hide() override
  542. {
  543. carla_debug("CocoaPluginUI::hide()");
  544. CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
  545. [fWindow setIsVisible:NO];
  546. if (fParentWindow != nullptr)
  547. [fParentWindow removeChildWindow:fWindow];
  548. }
  549. void idle() override
  550. {
  551. // carla_debug("CocoaPluginUI::idle()");
  552. }
  553. void focus() override
  554. {
  555. carla_debug("CocoaPluginUI::focus()");
  556. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  557. [fWindow makeKeyAndOrderFront:fWindow];
  558. [fWindow orderFrontRegardless];
  559. [NSApp activateIgnoringOtherApps:YES];
  560. }
  561. void setSize(const uint width, const uint height, const bool forceUpdate) override
  562. {
  563. carla_debug("CocoaPluginUI::setSize(%u, %u, %s)", width, height, bool2str(forceUpdate));
  564. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  565. CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
  566. [fView setFrame:NSMakeRect(0, 0, width, height)];
  567. for (NSView* subview in [fView subviews])
  568. {
  569. [subview setFrame:NSMakeRect(0, 0, width, height)];
  570. break;
  571. }
  572. const NSSize size = NSMakeSize(width, height);
  573. [fWindow setContentSize:size];
  574. /*
  575. if (fIsResizable)
  576. {
  577. [fWindow setContentMinSize:NSMakeSize(1, 1)];
  578. [fWindow setContentMaxSize:NSMakeSize(99999, 99999)];
  579. }
  580. else
  581. {
  582. [fWindow setContentMinSize:size];
  583. [fWindow setContentMaxSize:size];
  584. }
  585. */
  586. if (forceUpdate)
  587. {
  588. // FIXME, not enough
  589. [fView setNeedsDisplay:YES];
  590. }
  591. }
  592. void setTitle(const char* const title) override
  593. {
  594. carla_debug("CocoaPluginUI::setTitle(\"%s\")", title);
  595. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  596. NSString* titleString = [[NSString alloc]
  597. initWithBytes:title
  598. length:strlen(title)
  599. encoding:NSUTF8StringEncoding];
  600. [fWindow setTitle:titleString];
  601. }
  602. void setTransientWinId(const uintptr_t winId) override
  603. {
  604. carla_debug("CocoaPluginUI::setTransientWinId(" P_UINTPTR ")", winId);
  605. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  606. NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
  607. CARLA_SAFE_ASSERT_RETURN(parentWindow != nullptr,);
  608. fParentWindow = parentWindow;
  609. if ([fWindow isVisible])
  610. [fParentWindow addChildWindow:fWindow
  611. ordered:NSWindowAbove];
  612. }
  613. void setChildWindow(void* const childWindow) override
  614. {
  615. carla_debug("CocoaPluginUI::setChildWindow(%p)", childWindow);
  616. CARLA_SAFE_ASSERT_RETURN(childWindow != nullptr,);
  617. NSView* const view = (NSView*)childWindow;
  618. const NSRect frame = [view frame];
  619. [fWindow setContentSize:frame.size];
  620. [fView setFrame:frame];
  621. [fView setNeedsDisplay:YES];
  622. }
  623. void* getPtr() const noexcept override
  624. {
  625. carla_debug("CocoaPluginUI::getPtr()");
  626. return (void*)fView;
  627. }
  628. void* getDisplay() const noexcept
  629. {
  630. carla_debug("CocoaPluginUI::getDisplay()");
  631. return (void*)fWindow;
  632. }
  633. private:
  634. NSView* fView;
  635. NSWindow* fParentWindow;
  636. CarlaPluginWindow* fWindow;
  637. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CocoaPluginUI)
  638. };
  639. #endif // CARLA_OS_MAC
  640. // ---------------------------------------------------------------------------------------------------------------------
  641. // Windows
  642. #ifdef CARLA_OS_WIN
  643. #define CARLA_LOCAL_CLOSE_MSG (WM_USER + 50)
  644. static LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
  645. class WindowsPluginUI : public CarlaPluginUI
  646. {
  647. public:
  648. WindowsPluginUI(Callback* const cb, const uintptr_t parentId, const bool isStandalone, const bool isResizable) noexcept
  649. : CarlaPluginUI(cb, isStandalone, isResizable),
  650. fWindow(nullptr),
  651. fChildWindow(nullptr),
  652. fParentWindow(nullptr),
  653. fIsVisible(false),
  654. fFirstShow(true)
  655. {
  656. // FIXME
  657. static int wc_count = 0;
  658. char classNameBuf[32];
  659. std::srand((std::time(nullptr)));
  660. std::snprintf(classNameBuf, 32, "CarlaWin-%d-%d", ++wc_count, std::rand());
  661. classNameBuf[31] = '\0';
  662. const HINSTANCE hInstance = water::getCurrentModuleInstanceHandle();
  663. carla_zeroStruct(fWindowClass);
  664. fWindowClass.style = CS_OWNDC;
  665. fWindowClass.lpfnWndProc = wndProc;
  666. fWindowClass.hInstance = hInstance;
  667. fWindowClass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
  668. fWindowClass.hCursor = LoadCursor(hInstance, IDC_ARROW);
  669. fWindowClass.lpszClassName = strdup(classNameBuf);
  670. if (!RegisterClass(&fWindowClass)) {
  671. free((void*)fWindowClass.lpszClassName);
  672. return;
  673. }
  674. int winFlags = WS_POPUPWINDOW | WS_CAPTION;
  675. if (isResizable)
  676. winFlags |= WS_SIZEBOX;
  677. #ifdef BUILDING_CARLA_FOR_WINE
  678. const uint winType = WS_EX_DLGMODALFRAME;
  679. const HWND parent = nullptr;
  680. #else
  681. const uint winType = WS_EX_TOOLWINDOW;
  682. const HWND parent = (HWND)parentId;
  683. #endif
  684. fWindow = CreateWindowEx(winType,
  685. classNameBuf, "Carla Plugin UI", winFlags,
  686. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  687. parent, nullptr,
  688. hInstance, nullptr);
  689. if (fWindow == nullptr)
  690. {
  691. const DWORD errorCode = ::GetLastError();
  692. carla_stderr2("CreateWindowEx failed with error code 0x%x, class name was '%s'",
  693. errorCode, fWindowClass.lpszClassName);
  694. UnregisterClass(fWindowClass.lpszClassName, nullptr);
  695. free((void*)fWindowClass.lpszClassName);
  696. return;
  697. }
  698. SetWindowLongPtr(fWindow, GWLP_USERDATA, (LONG_PTR)this);
  699. #ifndef BUILDING_CARLA_FOR_WINE
  700. if (parentId != 0)
  701. setTransientWinId(parentId);
  702. #endif
  703. return;
  704. // maybe unused
  705. (void)parentId;
  706. }
  707. ~WindowsPluginUI() override
  708. {
  709. CARLA_SAFE_ASSERT(! fIsVisible);
  710. if (fWindow != 0)
  711. {
  712. if (fIsVisible)
  713. ShowWindow(fWindow, SW_HIDE);
  714. DestroyWindow(fWindow);
  715. fWindow = 0;
  716. }
  717. // FIXME
  718. UnregisterClass(fWindowClass.lpszClassName, nullptr);
  719. free((void*)fWindowClass.lpszClassName);
  720. }
  721. void show() override
  722. {
  723. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  724. if (fFirstShow)
  725. {
  726. fFirstShow = false;
  727. RECT rectChild, rectParent;
  728. if (fChildWindow != nullptr && GetWindowRect(fChildWindow, &rectChild))
  729. setSize(rectChild.right - rectChild.left, rectChild.bottom - rectChild.top, false);
  730. if (fParentWindow != nullptr &&
  731. GetWindowRect(fWindow, &rectChild) &&
  732. GetWindowRect(fParentWindow, &rectParent))
  733. {
  734. SetWindowPos(fWindow, fParentWindow,
  735. rectParent.left + (rectChild.right-rectChild.left)/2,
  736. rectParent.top + (rectChild.bottom-rectChild.top)/2,
  737. 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
  738. }
  739. else
  740. {
  741. ShowWindow(fWindow, SW_SHOWNORMAL);
  742. }
  743. }
  744. else
  745. {
  746. ShowWindow(fWindow, SW_RESTORE);
  747. }
  748. fIsVisible = true;
  749. UpdateWindow(fWindow);
  750. }
  751. void hide() override
  752. {
  753. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  754. ShowWindow(fWindow, SW_HIDE);
  755. fIsVisible = false;
  756. UpdateWindow(fWindow);
  757. }
  758. void idle() override
  759. {
  760. if (fIsIdling || fWindow == nullptr)
  761. return;
  762. MSG msg;
  763. fIsIdling = true;
  764. while (::PeekMessage(&msg, fWindow, 0, 0, PM_REMOVE))
  765. {
  766. switch (msg.message)
  767. {
  768. case WM_QUIT:
  769. case CARLA_LOCAL_CLOSE_MSG:
  770. fIsVisible = false;
  771. CARLA_SAFE_ASSERT_BREAK(fCallback != nullptr);
  772. fCallback->handlePluginUIClosed();
  773. break;
  774. }
  775. DispatchMessageA(&msg);
  776. }
  777. fIsIdling = false;
  778. }
  779. LRESULT checkAndHandleMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  780. {
  781. if (fWindow == hwnd)
  782. {
  783. switch (message)
  784. {
  785. case WM_SIZE:
  786. if (fChildWindow != nullptr)
  787. {
  788. RECT rect;
  789. GetClientRect(fWindow, &rect);
  790. SetWindowPos(fChildWindow, 0, 0, 0, rect.right, rect.bottom,
  791. SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  792. }
  793. break;
  794. case WM_QUIT:
  795. case CARLA_LOCAL_CLOSE_MSG:
  796. fIsVisible = false;
  797. CARLA_SAFE_ASSERT_BREAK(fCallback != nullptr);
  798. fCallback->handlePluginUIClosed();
  799. break;
  800. }
  801. }
  802. return DefWindowProcA(hwnd, message, wParam, lParam);
  803. }
  804. void focus() override
  805. {
  806. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  807. SetForegroundWindow(fWindow);
  808. SetActiveWindow(fWindow);
  809. SetFocus(fWindow);
  810. }
  811. void setSize(const uint width, const uint height, const bool forceUpdate) override
  812. {
  813. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  814. const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fIsResizable ? WS_SIZEBOX : 0x0);
  815. RECT wr = { 0, 0, static_cast<long>(width), static_cast<long>(height) };
  816. AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST);
  817. SetWindowPos(fWindow, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top,
  818. SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  819. if (forceUpdate)
  820. UpdateWindow(fWindow);
  821. }
  822. void setTitle(const char* const title) override
  823. {
  824. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  825. SetWindowTextA(fWindow, title);
  826. }
  827. void setTransientWinId(const uintptr_t winId) override
  828. {
  829. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  830. fParentWindow = (HWND)winId;
  831. SetWindowLongPtr(fWindow, GWLP_HWNDPARENT, (LONG_PTR)winId);
  832. }
  833. void setChildWindow(void* const winId) override
  834. {
  835. CARLA_SAFE_ASSERT_RETURN(winId != nullptr,);
  836. fChildWindow = (HWND)winId;
  837. }
  838. void* getPtr() const noexcept override
  839. {
  840. return (void*)fWindow;
  841. }
  842. void* getDisplay() const noexcept
  843. {
  844. return nullptr;
  845. }
  846. private:
  847. HWND fWindow;
  848. HWND fChildWindow;
  849. HWND fParentWindow;
  850. WNDCLASS fWindowClass;
  851. bool fIsVisible;
  852. bool fFirstShow;
  853. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WindowsPluginUI)
  854. };
  855. LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  856. {
  857. switch (message)
  858. {
  859. case WM_CLOSE:
  860. PostMessage(hwnd, CARLA_LOCAL_CLOSE_MSG, wParam, lParam);
  861. return 0;
  862. #if 0
  863. case WM_CREATE:
  864. PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0);
  865. return 0;
  866. case WM_DESTROY:
  867. return 0;
  868. #endif
  869. default:
  870. if (WindowsPluginUI* const ui = (WindowsPluginUI*)GetWindowLongPtr(hwnd, GWLP_USERDATA))
  871. return ui->checkAndHandleMessage(hwnd, message, wParam, lParam);
  872. return DefWindowProcA(hwnd, message, wParam, lParam);
  873. }
  874. }
  875. #endif // CARLA_OS_WIN
  876. // -----------------------------------------------------
  877. #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
  878. bool CarlaPluginUI::tryTransientWinIdMatch(const uintptr_t pid, const char* const uiTitle, const uintptr_t winId, const bool centerUI)
  879. {
  880. CARLA_SAFE_ASSERT_RETURN(uiTitle != nullptr && uiTitle[0] != '\0', true);
  881. CARLA_SAFE_ASSERT_RETURN(winId != 0, true);
  882. #if defined(HAVE_X11)
  883. struct ScopedDisplay {
  884. Display* display;
  885. ScopedDisplay() : display(XOpenDisplay(nullptr)) {}
  886. ~ScopedDisplay() { if (display!=nullptr) XCloseDisplay(display); }
  887. // c++ compat stuff
  888. CARLA_PREVENT_HEAP_ALLOCATION
  889. CARLA_DECLARE_NON_COPY_CLASS(ScopedDisplay)
  890. };
  891. struct ScopedFreeData {
  892. union {
  893. char* data;
  894. uchar* udata;
  895. };
  896. ScopedFreeData(char* d) : data(d) {}
  897. ScopedFreeData(uchar* d) : udata(d) {}
  898. ~ScopedFreeData() { XFree(data); }
  899. // c++ compat stuff
  900. CARLA_PREVENT_HEAP_ALLOCATION
  901. CARLA_DECLARE_NON_COPY_CLASS(ScopedFreeData)
  902. };
  903. const ScopedDisplay sd;
  904. CARLA_SAFE_ASSERT_RETURN(sd.display != nullptr, true);
  905. const Window rootWindow(DefaultRootWindow(sd.display));
  906. const Atom _ncl = XInternAtom(sd.display, "_NET_CLIENT_LIST" , False);
  907. const Atom _nwn = XInternAtom(sd.display, "_NET_WM_NAME", False);
  908. const Atom _nwp = XInternAtom(sd.display, "_NET_WM_PID", False);
  909. const Atom utf8 = XInternAtom(sd.display, "UTF8_STRING", True);
  910. Atom actualType;
  911. int actualFormat;
  912. ulong numWindows, bytesAfter;
  913. uchar* data = nullptr;
  914. int status = XGetWindowProperty(sd.display, rootWindow, _ncl, 0L, (~0L), False, AnyPropertyType, &actualType, &actualFormat, &numWindows, &bytesAfter, &data);
  915. CARLA_SAFE_ASSERT_RETURN(data != nullptr, true);
  916. const ScopedFreeData sfd(data);
  917. CARLA_SAFE_ASSERT_RETURN(status == Success, true);
  918. CARLA_SAFE_ASSERT_RETURN(actualFormat == 32, true);
  919. CARLA_SAFE_ASSERT_RETURN(numWindows != 0, true);
  920. Window* windows = (Window*)data;
  921. Window lastGoodWindowPID = 0, lastGoodWindowNameSimple = 0, lastGoodWindowNameUTF8 = 0;
  922. for (ulong i = 0; i < numWindows; i++)
  923. {
  924. const Window window(windows[i]);
  925. CARLA_SAFE_ASSERT_CONTINUE(window != 0);
  926. // ------------------------------------------------
  927. // try using pid
  928. if (pid != 0)
  929. {
  930. ulong pidSize;
  931. uchar* pidData = nullptr;
  932. status = XGetWindowProperty(sd.display, window, _nwp, 0L, (~0L), False, XA_CARDINAL, &actualType, &actualFormat, &pidSize, &bytesAfter, &pidData);
  933. if (pidData != nullptr)
  934. {
  935. const ScopedFreeData sfd2(pidData);
  936. CARLA_SAFE_ASSERT_CONTINUE(status == Success);
  937. CARLA_SAFE_ASSERT_CONTINUE(pidSize != 0);
  938. if (*(ulong*)pidData == static_cast<ulong>(pid))
  939. lastGoodWindowPID = window;
  940. }
  941. }
  942. // ------------------------------------------------
  943. // try using name (UTF-8)
  944. ulong nameSize;
  945. uchar* nameData = nullptr;
  946. status = XGetWindowProperty(sd.display, window, _nwn, 0L, (~0L), False, utf8, &actualType, &actualFormat, &nameSize, &bytesAfter, &nameData);
  947. if (nameData != nullptr)
  948. {
  949. const ScopedFreeData sfd2(nameData);
  950. CARLA_SAFE_ASSERT_CONTINUE(status == Success);
  951. if (nameSize != 0 && std::strstr((const char*)nameData, uiTitle) != nullptr)
  952. lastGoodWindowNameUTF8 = window;
  953. }
  954. // ------------------------------------------------
  955. // try using name (simple)
  956. char* wmName = nullptr;
  957. status = XFetchName(sd.display, window, &wmName);
  958. if (wmName != nullptr)
  959. {
  960. const ScopedFreeData sfd2(wmName);
  961. CARLA_SAFE_ASSERT_CONTINUE(status != 0);
  962. if (std::strstr(wmName, uiTitle) != nullptr)
  963. lastGoodWindowNameSimple = window;
  964. }
  965. }
  966. if (lastGoodWindowPID == 0 && lastGoodWindowNameSimple == 0 && lastGoodWindowNameUTF8 == 0)
  967. return false;
  968. Window windowToMap;
  969. if (lastGoodWindowPID != 0)
  970. {
  971. if (lastGoodWindowPID == lastGoodWindowNameSimple && lastGoodWindowPID == lastGoodWindowNameUTF8)
  972. {
  973. carla_stdout("Match found using pid, simple and UTF-8 name all at once, nice!");
  974. windowToMap = lastGoodWindowPID;
  975. }
  976. else if (lastGoodWindowPID == lastGoodWindowNameUTF8)
  977. {
  978. carla_stdout("Match found using pid and UTF-8 name");
  979. windowToMap = lastGoodWindowPID;
  980. }
  981. else if (lastGoodWindowPID == lastGoodWindowNameSimple)
  982. {
  983. carla_stdout("Match found using pid and simple name");
  984. windowToMap = lastGoodWindowPID;
  985. }
  986. else if (lastGoodWindowNameUTF8 != 0)
  987. {
  988. if (lastGoodWindowNameUTF8 == lastGoodWindowNameSimple)
  989. {
  990. carla_stdout("Match found using simple and UTF-8 name (ignoring pid)");
  991. windowToMap = lastGoodWindowNameUTF8;
  992. }
  993. else
  994. {
  995. carla_stdout("Match found using UTF-8 name (ignoring pid)");
  996. windowToMap = lastGoodWindowNameUTF8;
  997. }
  998. }
  999. else
  1000. {
  1001. carla_stdout("Match found using pid");
  1002. windowToMap = lastGoodWindowPID;
  1003. }
  1004. }
  1005. else if (lastGoodWindowNameUTF8 != 0)
  1006. {
  1007. if (lastGoodWindowNameUTF8 == lastGoodWindowNameSimple)
  1008. {
  1009. carla_stdout("Match found using simple and UTF-8 name");
  1010. windowToMap = lastGoodWindowNameUTF8;
  1011. }
  1012. else
  1013. {
  1014. carla_stdout("Match found using UTF-8 name");
  1015. windowToMap = lastGoodWindowNameUTF8;
  1016. }
  1017. }
  1018. else
  1019. {
  1020. carla_stdout("Match found using simple name");
  1021. windowToMap = lastGoodWindowNameSimple;
  1022. }
  1023. const Atom _nwt = XInternAtom(sd.display ,"_NET_WM_STATE", False);
  1024. const Atom _nws[2] = {
  1025. XInternAtom(sd.display, "_NET_WM_STATE_SKIP_TASKBAR", False),
  1026. XInternAtom(sd.display, "_NET_WM_STATE_SKIP_PAGER", False)
  1027. };
  1028. XChangeProperty(sd.display, windowToMap, _nwt, XA_ATOM, 32, PropModeAppend, (const uchar*)_nws, 2);
  1029. const Atom _nwi = XInternAtom(sd.display, "_NET_WM_ICON", False);
  1030. XChangeProperty(sd.display, windowToMap, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
  1031. const Window hostWinId((Window)winId);
  1032. XSetTransientForHint(sd.display, windowToMap, hostWinId);
  1033. if (centerUI && false /* moving the window after being shown isn't pretty... */)
  1034. {
  1035. int hostX, hostY, pluginX, pluginY;
  1036. uint hostWidth, hostHeight, pluginWidth, pluginHeight, border, depth;
  1037. Window retWindow;
  1038. if (XGetGeometry(sd.display, hostWinId, &retWindow, &hostX, &hostY, &hostWidth, &hostHeight, &border, &depth) != 0 &&
  1039. XGetGeometry(sd.display, windowToMap, &retWindow, &pluginX, &pluginY, &pluginWidth, &pluginHeight, &border, &depth) != 0)
  1040. {
  1041. if (XTranslateCoordinates(sd.display, hostWinId, rootWindow, hostX, hostY, &hostX, &hostY, &retWindow) == True &&
  1042. XTranslateCoordinates(sd.display, windowToMap, rootWindow, pluginX, pluginY, &pluginX, &pluginY, &retWindow) == True)
  1043. {
  1044. const int newX = hostX + int(hostWidth/2 - pluginWidth/2);
  1045. const int newY = hostY + int(hostHeight/2 - pluginHeight/2);
  1046. XMoveWindow(sd.display, windowToMap, newX, newY);
  1047. }
  1048. }
  1049. }
  1050. // focusing the host UI and then the plugin UI forces the WM to repaint the plugin window icon
  1051. XRaiseWindow(sd.display, hostWinId);
  1052. XSetInputFocus(sd.display, hostWinId, RevertToPointerRoot, CurrentTime);
  1053. XRaiseWindow(sd.display, windowToMap);
  1054. XSetInputFocus(sd.display, windowToMap, RevertToPointerRoot, CurrentTime);
  1055. XFlush(sd.display);
  1056. return true;
  1057. #endif
  1058. #ifdef CARLA_OS_MAC
  1059. uint const hints = kCGWindowListOptionOnScreenOnly|kCGWindowListExcludeDesktopElements;
  1060. CFArrayRef const windowListRef = CGWindowListCopyWindowInfo(hints, kCGNullWindowID);
  1061. const NSArray* const windowList = (const NSArray*)windowListRef;
  1062. int windowToMap, windowWithPID = 0, windowWithNameAndPID = 0;
  1063. const NSDictionary* entry;
  1064. for (entry in windowList)
  1065. {
  1066. // FIXME: is this needed? is old version safe?
  1067. #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
  1068. if ([entry[(id)kCGWindowSharingState] intValue] == kCGWindowSharingNone)
  1069. continue;
  1070. NSString* const windowName = entry[(id)kCGWindowName];
  1071. int const windowNumber = [entry[(id)kCGWindowNumber] intValue];
  1072. uintptr_t const windowPID = [entry[(id)kCGWindowOwnerPID] intValue];
  1073. #else
  1074. if ([[entry objectForKey:(id)kCGWindowSharingState] intValue] == kCGWindowSharingNone)
  1075. continue;
  1076. NSString* const windowName = [entry objectForKey:(id)kCGWindowName];
  1077. int const windowNumber = [[entry objectForKey:(id)kCGWindowNumber] intValue];
  1078. uintptr_t const windowPID = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
  1079. #endif
  1080. if (windowPID != pid)
  1081. continue;
  1082. windowWithPID = windowNumber;
  1083. if (windowName != nullptr && std::strcmp([windowName UTF8String], uiTitle) == 0)
  1084. windowWithNameAndPID = windowNumber;
  1085. }
  1086. CFRelease(windowListRef);
  1087. if (windowWithNameAndPID != 0)
  1088. {
  1089. carla_stdout("Match found using pid and name");
  1090. windowToMap = windowWithNameAndPID;
  1091. }
  1092. else if (windowWithPID != 0)
  1093. {
  1094. carla_stdout("Match found using pid");
  1095. windowToMap = windowWithPID;
  1096. }
  1097. else
  1098. {
  1099. return false;
  1100. }
  1101. NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
  1102. CARLA_SAFE_ASSERT_RETURN(parentWindow != nullptr, false);
  1103. [parentWindow orderWindow:NSWindowBelow
  1104. relativeTo:windowToMap];
  1105. return true;
  1106. #endif
  1107. #ifdef CARLA_OS_WIN
  1108. if (HWND const childWindow = FindWindowA(nullptr, uiTitle))
  1109. {
  1110. HWND const parentWindow = (HWND)winId;
  1111. SetWindowLongPtr(childWindow, GWLP_HWNDPARENT, (LONG_PTR)parentWindow);
  1112. if (centerUI)
  1113. {
  1114. RECT rectChild, rectParent;
  1115. if (GetWindowRect(childWindow, &rectChild) && GetWindowRect(parentWindow, &rectParent))
  1116. {
  1117. SetWindowPos(childWindow, parentWindow,
  1118. rectParent.left + (rectChild.right-rectChild.left)/2,
  1119. rectParent.top + (rectChild.bottom-rectChild.top)/2,
  1120. 0, 0, SWP_NOSIZE);
  1121. }
  1122. }
  1123. carla_stdout("Match found using window name");
  1124. return true;
  1125. }
  1126. return false;
  1127. #endif
  1128. // fallback, may be unused
  1129. return true;
  1130. (void)pid; (void)centerUI;
  1131. }
  1132. #endif // BUILD_BRIDGE_ALTERNATIVE_ARCH
  1133. // -----------------------------------------------------
  1134. #ifdef HAVE_X11
  1135. CarlaPluginUI* CarlaPluginUI::newX11(Callback* const cb,
  1136. const uintptr_t parentId,
  1137. const bool isStandalone,
  1138. const bool isResizable,
  1139. const bool isLV2)
  1140. {
  1141. return new X11PluginUI(cb, parentId, isStandalone, isResizable, isLV2);
  1142. }
  1143. #endif
  1144. #ifdef CARLA_OS_MAC
  1145. CarlaPluginUI* CarlaPluginUI::newCocoa(Callback* const cb,
  1146. const uintptr_t parentId,
  1147. const bool isStandalone,
  1148. const bool isResizable)
  1149. {
  1150. return new CocoaPluginUI(cb, parentId, isStandalone, isResizable);
  1151. }
  1152. #endif
  1153. #ifdef CARLA_OS_WIN
  1154. CarlaPluginUI* CarlaPluginUI::newWindows(Callback* const cb,
  1155. const uintptr_t parentId,
  1156. const bool isStandalone,
  1157. const bool isResizable)
  1158. {
  1159. return new WindowsPluginUI(cb, parentId, isStandalone, isResizable);
  1160. }
  1161. #endif
  1162. // -----------------------------------------------------