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.

778 lines
22KB

  1. /*
  2. * Carla Plugin UI
  3. * Copyright (C) 2014 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. #if defined(CARLA_OS_WIN) || defined(CARLA_OS_MAC)
  20. # include "juce_gui_basics.h"
  21. using juce::Colour;
  22. using juce::Colours;
  23. using juce::ComponentPeer;
  24. using juce::DocumentWindow;
  25. using juce::Graphics;
  26. #endif
  27. #ifdef HAVE_X11
  28. # include <sys/types.h>
  29. # include <X11/Xatom.h>
  30. # include <X11/Xlib.h>
  31. # include <X11/Xutil.h>
  32. #endif
  33. // -----------------------------------------------------
  34. // AutoResizingNSViewComponentWithParent, see juce_audio_processors.cpp
  35. #ifdef CARLA_OS_MAC
  36. # include "juce_gui_extra.h"
  37. # ifdef CARLA_PLUGIN_UI_WITHOUT_JUCE_PROCESSORS
  38. # include "juce_core/native/juce_BasicNativeHeaders.h"
  39. # else
  40. struct NSView;
  41. # endif
  42. namespace juce {
  43. //==============================================================================
  44. struct AutoResizingNSViewComponent : public NSViewComponent,
  45. private AsyncUpdater {
  46. AutoResizingNSViewComponent();
  47. void childBoundsChanged(Component*) override;
  48. void handleAsyncUpdate() override;
  49. bool recursive;
  50. };
  51. struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent,
  52. private Timer {
  53. AutoResizingNSViewComponentWithParent();
  54. NSView* getChildView() const;
  55. void timerCallback() override;
  56. };
  57. //==============================================================================
  58. # ifdef CARLA_PLUGIN_UI_WITHOUT_JUCE_PROCESSORS
  59. # include "juce_core/native/juce_BasicNativeHeaders.h"
  60. AutoResizingNSViewComponent::AutoResizingNSViewComponent()
  61. : recursive (false) {}
  62. void AutoResizingNSViewComponent::childBoundsChanged(Component*) override
  63. {
  64. if (recursive)
  65. {
  66. triggerAsyncUpdate();
  67. }
  68. else
  69. {
  70. recursive = true;
  71. resizeToFitView();
  72. recursive = true;
  73. }
  74. }
  75. void AutoResizingNSViewComponent::handleAsyncUpdate() override
  76. {
  77. resizeToFitView();
  78. }
  79. AutoResizingNSViewComponentWithParent::AutoResizingNSViewComponentWithParent()
  80. {
  81. NSView* v = [[NSView alloc] init];
  82. setView (v);
  83. [v release];
  84. startTimer(500);
  85. }
  86. NSView* AutoResizingNSViewComponentWithParent::getChildView() const
  87. {
  88. if (NSView* parent = (NSView*)getView())
  89. if ([[parent subviews] count] > 0)
  90. return [[parent subviews] objectAtIndex: 0];
  91. return nil;
  92. }
  93. void AutoResizingNSViewComponentWithParent::timerCallback() override
  94. {
  95. if (NSView* child = getChildView())
  96. {
  97. stopTimer();
  98. setView(child);
  99. }
  100. }
  101. #endif
  102. } // namespace juce
  103. using juce::AutoResizingNSViewComponentWithParent;
  104. #endif
  105. // -----------------------------------------------------
  106. // JUCE
  107. #if defined(CARLA_OS_WIN) || defined(CARLA_OS_MAC)
  108. class JucePluginUI : public CarlaPluginUI,
  109. public DocumentWindow
  110. {
  111. public:
  112. JucePluginUI(CloseCallback* const cb, const uintptr_t /*parentId*/)
  113. : CarlaPluginUI(cb, false),
  114. DocumentWindow("JucePluginUI", Colour(50, 50, 200), DocumentWindow::closeButton, false),
  115. fClosed(false),
  116. #ifdef CARLA_OS_MAC
  117. fCocoaWrapper(),
  118. #endif
  119. leakDetector_JucePluginUI()
  120. {
  121. setVisible(false);
  122. //setAlwaysOnTop(true);
  123. setOpaque(true);
  124. setResizable(false, false);
  125. setUsingNativeTitleBar(true);
  126. #ifdef CARLA_OS_MAC
  127. addAndMakeVisible(fCocoaWrapper = new AutoResizingNSViewComponentWithParent());
  128. #endif
  129. addToDesktop();
  130. }
  131. ~JucePluginUI() override
  132. {
  133. #ifdef CARLA_OS_MAC
  134. // deleted before window
  135. fCocoaWrapper = nullptr;
  136. #endif
  137. }
  138. protected:
  139. // CarlaPluginUI calls
  140. void closeButtonPressed() override
  141. {
  142. fClosed = true;
  143. }
  144. void show() override
  145. {
  146. fClosed = false;
  147. DocumentWindow::setVisible(true);
  148. }
  149. void hide() override
  150. {
  151. DocumentWindow::setVisible(false);
  152. }
  153. void focus() override
  154. {
  155. DocumentWindow::toFront(true);
  156. }
  157. void idle() override
  158. {
  159. if (fClosed)
  160. {
  161. fClosed = false;
  162. CARLA_SAFE_ASSERT_RETURN(fCallback != nullptr,);
  163. fCallback->handlePluginUIClosed();
  164. }
  165. }
  166. void setSize(const uint width, const uint height, const bool /*forceUpdate*/) override
  167. {
  168. DocumentWindow::setSize(static_cast<int>(width), static_cast<int>(height));
  169. }
  170. void setTitle(const char* const title) override
  171. {
  172. DocumentWindow::setName(title);
  173. }
  174. void setTransientWinId(const uintptr_t /*winId*/) override
  175. {
  176. }
  177. void* getPtr() const noexcept override
  178. {
  179. #ifdef CARLA_OS_MAC
  180. return fCocoaWrapper->getView();
  181. #else
  182. if (ComponentPeer* const peer = getPeer())
  183. return peer->getNativeHandle();
  184. carla_stdout("getPtr() failed");
  185. return nullptr;
  186. #endif
  187. }
  188. #ifdef CARLA_OS_MAC
  189. // JUCE MacOS calls
  190. void childBoundsChanged(Component*) override
  191. {
  192. if (fCocoaWrapper != nullptr)
  193. {
  194. const int w = fCocoaWrapper->getWidth();
  195. const int h = fCocoaWrapper->getHeight();
  196. if (w != DocumentWindow::getWidth() || h != DocumentWindow::getHeight())
  197. DocumentWindow::setSize(w, h);
  198. }
  199. }
  200. void resized() override
  201. {
  202. if (fCocoaWrapper != nullptr)
  203. fCocoaWrapper->setSize(DocumentWindow::getWidth(), DocumentWindow::getHeight());
  204. }
  205. #endif
  206. #ifdef CARLA_OS_WINDOWS
  207. // JUCE Windows calls
  208. void mouseDown(const MouseEvent&) override
  209. {
  210. DocumentWindow::toFront(true);
  211. }
  212. #endif
  213. void paint(Graphics& g) override
  214. {
  215. g.fillAll(Colours::black);
  216. }
  217. #if 0
  218. //==============================================================================
  219. bool keyStateChanged (bool) override { return pluginWantsKeys; }
  220. bool keyPressed (const juce::KeyPress&) override { return pluginWantsKeys; }
  221. #endif
  222. private:
  223. volatile bool fClosed;
  224. #ifdef CARLA_OS_MAC
  225. juce::ScopedPointer<AutoResizingNSViewComponentWithParent> fCocoaWrapper;
  226. #endif
  227. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(JucePluginUI)
  228. };
  229. #endif
  230. // -----------------------------------------------------
  231. // X11
  232. #ifdef HAVE_X11
  233. # include "CarlaPluginUI_X11Icon.hpp"
  234. typedef void (*EventProcPtr)(XEvent* ev);
  235. static const int X11Key_Escape = 9;
  236. static bool gErrorTriggered = false;
  237. static int temporaryErrorHandler(Display*, XErrorEvent*)
  238. {
  239. gErrorTriggered = true;
  240. return 0;
  241. }
  242. class X11PluginUI : public CarlaPluginUI
  243. {
  244. public:
  245. X11PluginUI(CloseCallback* const cb, const uintptr_t parentId, const bool isResizable) noexcept
  246. : CarlaPluginUI(cb, isResizable),
  247. fDisplay(nullptr),
  248. fWindow(0),
  249. fIsVisible(false),
  250. fFirstShow(true),
  251. fEventProc(nullptr),
  252. leakDetector_X11PluginUI()
  253. {
  254. fDisplay = XOpenDisplay(nullptr);
  255. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  256. const int screen = DefaultScreen(fDisplay);
  257. XSetWindowAttributes attr;
  258. carla_zeroStruct<XSetWindowAttributes>(attr);
  259. attr.border_pixel = 0;
  260. attr.event_mask = KeyPressMask|KeyReleaseMask;
  261. if (fIsResizable)
  262. attr.event_mask |= StructureNotifyMask;
  263. fWindow = XCreateWindow(fDisplay, RootWindow(fDisplay, screen),
  264. 0, 0, 300, 300, 0,
  265. DefaultDepth(fDisplay, screen),
  266. InputOutput,
  267. DefaultVisual(fDisplay, screen),
  268. CWBorderPixel|CWEventMask, &attr);
  269. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  270. XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fWindow, 1, GrabModeAsync, GrabModeAsync);
  271. Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True);
  272. XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1);
  273. const pid_t pid = getpid();
  274. const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False);
  275. XChangeProperty(fDisplay, fWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  276. const Atom _nwi = XInternAtom(fDisplay, "_NET_WM_ICON", False);
  277. XChangeProperty(fDisplay, fWindow, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
  278. if (parentId != 0)
  279. setTransientWinId(parentId);
  280. }
  281. ~X11PluginUI() override
  282. {
  283. CARLA_SAFE_ASSERT(! fIsVisible);
  284. if (fIsVisible)
  285. {
  286. XUnmapWindow(fDisplay, fWindow);
  287. fIsVisible = false;
  288. }
  289. if (fWindow != 0)
  290. {
  291. XDestroyWindow(fDisplay, fWindow);
  292. fWindow = 0;
  293. }
  294. if (fDisplay != nullptr)
  295. {
  296. XCloseDisplay(fDisplay);
  297. fDisplay = nullptr;
  298. }
  299. }
  300. void show() override
  301. {
  302. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  303. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  304. if (fFirstShow)
  305. {
  306. if (const Window childWindow = getChildWindow())
  307. {
  308. const Atom _xevp = XInternAtom(fDisplay, "_XEventProc", False);
  309. gErrorTriggered = false;
  310. const XErrorHandler oldErrorHandler(XSetErrorHandler(temporaryErrorHandler));
  311. Atom actualType;
  312. int actualFormat;
  313. ulong nitems, bytesAfter;
  314. uchar* data = nullptr;
  315. XGetWindowProperty(fDisplay, childWindow, _xevp, 0, 1, False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesAfter, &data);
  316. XSetErrorHandler(oldErrorHandler);
  317. if (nitems == 1 && ! gErrorTriggered)
  318. {
  319. fEventProc = *reinterpret_cast<EventProcPtr*>(data);
  320. XMapRaised(fDisplay, childWindow);
  321. }
  322. }
  323. }
  324. fIsVisible = true;
  325. fFirstShow = false;
  326. XMapRaised(fDisplay, fWindow);
  327. XFlush(fDisplay);
  328. }
  329. void hide() override
  330. {
  331. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  332. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  333. fIsVisible = false;
  334. XUnmapWindow(fDisplay, fWindow);
  335. XFlush(fDisplay);
  336. }
  337. void idle() override
  338. {
  339. // prevent recursion
  340. if (fIsIdling) return;
  341. fIsIdling = true;
  342. for (XEvent event; XPending(fDisplay) > 0;)
  343. {
  344. XNextEvent(fDisplay, &event);
  345. if (! fIsVisible)
  346. continue;
  347. char* type = nullptr;
  348. switch (event.type)
  349. {
  350. case ConfigureNotify:
  351. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  352. CARLA_SAFE_ASSERT_CONTINUE(event.xconfigure.width > 0);
  353. CARLA_SAFE_ASSERT_CONTINUE(event.xconfigure.height > 0);
  354. fCallback->handlePluginUIResized(static_cast<uint>(event.xconfigure.width),
  355. static_cast<uint>(event.xconfigure.height));
  356. break;
  357. case ClientMessage:
  358. type = XGetAtomName(fDisplay, event.xclient.message_type);
  359. CARLA_SAFE_ASSERT_CONTINUE(type != nullptr);
  360. if (std::strcmp(type, "WM_PROTOCOLS") == 0)
  361. {
  362. fIsVisible = false;
  363. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  364. fCallback->handlePluginUIClosed();
  365. }
  366. break;
  367. case KeyRelease:
  368. if (event.xkey.keycode == X11Key_Escape)
  369. {
  370. fIsVisible = false;
  371. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  372. fCallback->handlePluginUIClosed();
  373. }
  374. break;
  375. }
  376. if (type != nullptr)
  377. XFree(type);
  378. else if (fEventProc != nullptr)
  379. fEventProc(&event);
  380. }
  381. fIsIdling = false;
  382. }
  383. void focus() override
  384. {
  385. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  386. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  387. XRaiseWindow(fDisplay, fWindow);
  388. XSetInputFocus(fDisplay, fWindow, RevertToPointerRoot, CurrentTime);
  389. XFlush(fDisplay);
  390. }
  391. void setSize(const uint width, const uint height, const bool forceUpdate) override
  392. {
  393. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  394. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  395. XResizeWindow(fDisplay, fWindow, width, height);
  396. if (! fIsResizable)
  397. {
  398. XSizeHints sizeHints;
  399. carla_zeroStruct<XSizeHints>(sizeHints);
  400. sizeHints.flags = PSize|PMinSize|PMaxSize;
  401. sizeHints.width = static_cast<int>(width);
  402. sizeHints.height = static_cast<int>(height);
  403. sizeHints.min_width = static_cast<int>(width);
  404. sizeHints.min_height = static_cast<int>(height);
  405. sizeHints.max_width = static_cast<int>(width);
  406. sizeHints.max_height = static_cast<int>(height);
  407. XSetNormalHints(fDisplay, fWindow, &sizeHints);
  408. }
  409. if (forceUpdate)
  410. XFlush(fDisplay);
  411. }
  412. void setTitle(const char* const title) override
  413. {
  414. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  415. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  416. XStoreName(fDisplay, fWindow, title);
  417. }
  418. void setTransientWinId(const uintptr_t winId) override
  419. {
  420. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  421. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  422. XSetTransientForHint(fDisplay, fWindow, static_cast<Window>(winId));
  423. }
  424. void* getPtr() const noexcept
  425. {
  426. return (void*)fWindow;
  427. }
  428. void* getDisplay() const noexcept
  429. {
  430. return fDisplay;
  431. }
  432. protected:
  433. Window getChildWindow() const
  434. {
  435. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr, 0);
  436. CARLA_SAFE_ASSERT_RETURN(fWindow != 0, 0);
  437. Window rootWindow, parentWindow, ret = 0;
  438. Window* childWindows = nullptr;
  439. uint numChildren = 0;
  440. XQueryTree(fDisplay, fWindow, &rootWindow, &parentWindow, &childWindows, &numChildren);
  441. if (numChildren > 0 && childWindows != nullptr)
  442. {
  443. ret = childWindows[0];
  444. XFree(childWindows);
  445. }
  446. return ret;
  447. }
  448. private:
  449. Display* fDisplay;
  450. Window fWindow;
  451. bool fIsVisible;
  452. bool fFirstShow;
  453. EventProcPtr fEventProc;
  454. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(X11PluginUI)
  455. };
  456. #endif // HAVE_X11
  457. // -----------------------------------------------------
  458. bool CarlaPluginUI::tryTransientWinIdMatch(const uintptr_t pid, const char* const uiTitle, const uintptr_t winId, const bool centerUI)
  459. {
  460. CARLA_SAFE_ASSERT_RETURN(uiTitle != nullptr && uiTitle[0] != '\0', true);
  461. CARLA_SAFE_ASSERT_RETURN(winId != 0, true);
  462. #if defined(CARLA_OS_MAC)
  463. return true;
  464. (void)pid; (void)centerUI;
  465. #elif defined(CARLA_OS_WIN)
  466. return true;
  467. (void)pid; (void)centerUI;
  468. #elif defined(HAVE_X11)
  469. struct ScopedDisplay {
  470. Display* display;
  471. ScopedDisplay() : display(XOpenDisplay(nullptr)) {}
  472. ~ScopedDisplay() { if (display!=nullptr) XCloseDisplay(display); }
  473. // c++ compat stuff
  474. CARLA_PREVENT_HEAP_ALLOCATION
  475. CARLA_DECLARE_NON_COPY_CLASS(ScopedDisplay)
  476. };
  477. struct ScopedFreeData {
  478. union {
  479. char* data;
  480. uchar* udata;
  481. };
  482. ScopedFreeData(char* d) : data(d) {}
  483. ScopedFreeData(uchar* d) : udata(d) {}
  484. ~ScopedFreeData() { XFree(data); }
  485. // c++ compat stuff
  486. CARLA_PREVENT_HEAP_ALLOCATION
  487. CARLA_DECLARE_NON_COPY_CLASS(ScopedFreeData)
  488. };
  489. const ScopedDisplay sd;
  490. CARLA_SAFE_ASSERT_RETURN(sd.display != nullptr, true);
  491. const Window rootWindow(DefaultRootWindow(sd.display));
  492. const Atom _ncl = XInternAtom(sd.display, "_NET_CLIENT_LIST" , False);
  493. const Atom _nwn = XInternAtom(sd.display, "_NET_WM_NAME", False);
  494. const Atom _nwp = XInternAtom(sd.display, "_NET_WM_PID", False);
  495. const Atom utf8 = XInternAtom(sd.display, "UTF8_STRING", True);
  496. Atom actualType;
  497. int actualFormat;
  498. ulong numWindows, bytesAfter;
  499. uchar* data = nullptr;
  500. int status = XGetWindowProperty(sd.display, rootWindow, _ncl, 0L, (~0L), False, AnyPropertyType, &actualType, &actualFormat, &numWindows, &bytesAfter, &data);
  501. CARLA_SAFE_ASSERT_RETURN(data != nullptr, true);
  502. const ScopedFreeData sfd(data);
  503. CARLA_SAFE_ASSERT_RETURN(status == Success, true);
  504. CARLA_SAFE_ASSERT_RETURN(actualFormat == 32, true);
  505. CARLA_SAFE_ASSERT_RETURN(numWindows != 0, true);
  506. Window* windows = (Window*)data;
  507. Window lastGoodWindow = 0;
  508. for (ulong i = 0; i < numWindows; i++)
  509. {
  510. const Window window(windows[i]);
  511. CARLA_SAFE_ASSERT_CONTINUE(window != 0);
  512. // ------------------------------------------------
  513. // try using pid
  514. if (pid != 0)
  515. {
  516. ulong pidSize;
  517. uchar* pidData = nullptr;
  518. status = XGetWindowProperty(sd.display, window, _nwp, 0L, (~0L), False, XA_CARDINAL, &actualType, &actualFormat, &pidSize, &bytesAfter, &pidData);
  519. if (pidData != nullptr)
  520. {
  521. const ScopedFreeData sfd2(pidData);
  522. CARLA_SAFE_ASSERT_CONTINUE(status == Success);
  523. CARLA_SAFE_ASSERT_CONTINUE(pidSize != 0);
  524. if (*(ulong*)pidData == static_cast<ulong>(pid))
  525. {
  526. CARLA_SAFE_ASSERT_RETURN(lastGoodWindow == window || lastGoodWindow == 0, true);
  527. lastGoodWindow = window;
  528. carla_stdout("Match found using pid");
  529. break;
  530. }
  531. }
  532. }
  533. // ------------------------------------------------
  534. // try using name (UTF-8)
  535. ulong nameSize;
  536. uchar* nameData = nullptr;
  537. status = XGetWindowProperty(sd.display, window, _nwn, 0L, (~0L), False, utf8, &actualType, &actualFormat, &nameSize, &bytesAfter, &nameData);
  538. if (nameData != nullptr)
  539. {
  540. const ScopedFreeData sfd2(nameData);
  541. CARLA_SAFE_ASSERT_CONTINUE(status == Success);
  542. CARLA_SAFE_ASSERT_CONTINUE(nameSize != 0);
  543. if (std::strstr((const char*)nameData, uiTitle) != nullptr)
  544. {
  545. CARLA_SAFE_ASSERT_RETURN(lastGoodWindow == window || lastGoodWindow == 0, true);
  546. lastGoodWindow = window;
  547. carla_stdout("Match found using UTF-8 name");
  548. }
  549. }
  550. // ------------------------------------------------
  551. // try using name (simple)
  552. char* wmName = nullptr;
  553. status = XFetchName(sd.display, window, &wmName);
  554. if (wmName != nullptr)
  555. {
  556. const ScopedFreeData sfd2(wmName);
  557. CARLA_SAFE_ASSERT_CONTINUE(status != 0);
  558. if (std::strstr(wmName, uiTitle) != nullptr)
  559. {
  560. CARLA_SAFE_ASSERT_RETURN(lastGoodWindow == window || lastGoodWindow == 0, true);
  561. lastGoodWindow = window;
  562. carla_stdout("Match found using simple name");
  563. }
  564. }
  565. }
  566. if (lastGoodWindow == 0)
  567. return false;
  568. const Atom _nwt = XInternAtom(sd.display ,"_NET_WM_STATE", False);
  569. const Atom _nws[2] = {
  570. XInternAtom(sd.display, "_NET_WM_STATE_SKIP_TASKBAR", False),
  571. XInternAtom(sd.display, "_NET_WM_STATE_SKIP_PAGER", False)
  572. };
  573. XChangeProperty(sd.display, lastGoodWindow, _nwt, XA_ATOM, 32, PropModeAppend, (const uchar*)_nws, 2);
  574. const Atom _nwi = XInternAtom(sd.display, "_NET_WM_ICON", False);
  575. XChangeProperty(sd.display, lastGoodWindow, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
  576. const Window hostWinId((Window)winId);
  577. XSetTransientForHint(sd.display, lastGoodWindow, hostWinId);
  578. if (centerUI && false /* moving the window after being shown isn't pretty... */)
  579. {
  580. int hostX, hostY, pluginX, pluginY;
  581. uint hostWidth, hostHeight, pluginWidth, pluginHeight, border, depth;
  582. Window retWindow;
  583. if (XGetGeometry(sd.display, hostWinId, &retWindow, &hostX, &hostY, &hostWidth, &hostHeight, &border, &depth) != 0 &&
  584. XGetGeometry(sd.display, lastGoodWindow, &retWindow, &pluginX, &pluginY, &pluginWidth, &pluginHeight, &border, &depth) != 0)
  585. {
  586. if (XTranslateCoordinates(sd.display, hostWinId, rootWindow, hostX, hostY, &hostX, &hostY, &retWindow) == True &&
  587. XTranslateCoordinates(sd.display, lastGoodWindow, rootWindow, pluginX, pluginY, &pluginX, &pluginY, &retWindow) == True)
  588. {
  589. const int newX = hostX + int(hostWidth/2 - pluginWidth/2);
  590. const int newY = hostY + int(hostHeight/2 - pluginHeight/2);
  591. XMoveWindow(sd.display, lastGoodWindow, newX, newY);
  592. }
  593. }
  594. }
  595. // focusing the host UI and then the plugin UI forces the WM to repaint the plugin window icon
  596. XRaiseWindow(sd.display, hostWinId);
  597. XSetInputFocus(sd.display, hostWinId, RevertToPointerRoot, CurrentTime);
  598. XRaiseWindow(sd.display, lastGoodWindow);
  599. XSetInputFocus(sd.display, lastGoodWindow, RevertToPointerRoot, CurrentTime);
  600. XFlush(sd.display);
  601. return true;
  602. #else
  603. return true;
  604. (void)pid; (void)centerUI;
  605. #endif
  606. }
  607. // -----------------------------------------------------
  608. #ifdef CARLA_OS_MAC
  609. CarlaPluginUI* CarlaPluginUI::newCocoa(CloseCallback* cb, uintptr_t parentId, bool /*isResizable*/)
  610. {
  611. return new JucePluginUI(cb, parentId);
  612. }
  613. #endif
  614. #ifdef CARLA_OS_WIN
  615. CarlaPluginUI* CarlaPluginUI::newWindows(CloseCallback* cb, uintptr_t parentId, bool /*isResizable*/)
  616. {
  617. return new JucePluginUI(cb, parentId);
  618. }
  619. #endif
  620. #ifdef HAVE_X11
  621. CarlaPluginUI* CarlaPluginUI::newX11(CloseCallback* cb, uintptr_t parentId, bool isResizable)
  622. {
  623. return new X11PluginUI(cb, parentId, isResizable);
  624. }
  625. #endif
  626. // -----------------------------------------------------