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.

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