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
43KB

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