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.

1452 lines
45KB

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