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.

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