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.

1502 lines
47KB

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