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.

1408 lines
43KB

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