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.

782 lines
22KB

  1. /*
  2. * Carla Plugin UI
  3. * Copyright (C) 2014-2017 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. #include "AppConfig.h"
  20. #if defined(CARLA_OS_WIN) || defined(CARLA_OS_MAC)
  21. # include "juce_gui_basics/juce_gui_basics.h"
  22. using juce::Colour;
  23. using juce::Colours;
  24. using juce::ComponentPeer;
  25. using juce::DocumentWindow;
  26. using juce::Graphics;
  27. #endif
  28. #ifdef HAVE_X11
  29. # include <sys/types.h>
  30. # include <X11/Xatom.h>
  31. # include <X11/Xlib.h>
  32. # include <X11/Xutil.h>
  33. #endif
  34. // -----------------------------------------------------
  35. // AutoResizingNSViewComponentWithParent, see juce_audio_processors.cpp
  36. #ifdef CARLA_OS_MAC
  37. # include "juce_gui_extra/juce_gui_extra.h"
  38. # ifdef CARLA_PLUGIN_UI_WITHOUT_JUCE_PROCESSORS
  39. # include "juce_core/native/juce_BasicNativeHeaders.h"
  40. # else
  41. struct NSView;
  42. # endif
  43. namespace juce {
  44. //==============================================================================
  45. struct AutoResizingNSViewComponent : public NSViewComponent,
  46. private AsyncUpdater {
  47. AutoResizingNSViewComponent();
  48. void childBoundsChanged(Component*) override;
  49. void handleAsyncUpdate() override;
  50. bool recursive;
  51. };
  52. struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent,
  53. private Timer {
  54. AutoResizingNSViewComponentWithParent();
  55. NSView* getChildView() const;
  56. void timerCallback() override;
  57. };
  58. //==============================================================================
  59. # ifdef CARLA_PLUGIN_UI_WITHOUT_JUCE_PROCESSORS
  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. {
  120. setVisible(false);
  121. //setAlwaysOnTop(true);
  122. setOpaque(true);
  123. setResizable(false, false);
  124. setUsingNativeTitleBar(true);
  125. #ifdef CARLA_OS_MAC
  126. addAndMakeVisible(fCocoaWrapper = new AutoResizingNSViewComponentWithParent());
  127. #endif
  128. addToDesktop();
  129. }
  130. ~JucePluginUI() override
  131. {
  132. #ifdef CARLA_OS_MAC
  133. // deleted before window
  134. fCocoaWrapper = nullptr;
  135. #endif
  136. }
  137. protected:
  138. // CarlaPluginUI calls
  139. void closeButtonPressed() override
  140. {
  141. fClosed = true;
  142. }
  143. void show() override
  144. {
  145. fClosed = false;
  146. DocumentWindow::setVisible(true);
  147. }
  148. void hide() override
  149. {
  150. DocumentWindow::setVisible(false);
  151. }
  152. void focus() override
  153. {
  154. DocumentWindow::toFront(true);
  155. }
  156. void idle() override
  157. {
  158. if (fClosed)
  159. {
  160. fClosed = false;
  161. CARLA_SAFE_ASSERT_RETURN(fCallback != nullptr,);
  162. fCallback->handlePluginUIClosed();
  163. }
  164. }
  165. void setSize(const uint width, const uint height, const bool /*forceUpdate*/) override
  166. {
  167. DocumentWindow::setSize(static_cast<int>(width), static_cast<int>(height));
  168. }
  169. void setTitle(const char* const title) override
  170. {
  171. DocumentWindow::setName(title);
  172. }
  173. void setTransientWinId(const uintptr_t /*winId*/) override
  174. {
  175. }
  176. void* getPtr() const noexcept override
  177. {
  178. #ifdef CARLA_OS_MAC
  179. return fCocoaWrapper->getView();
  180. #else
  181. if (ComponentPeer* const peer = getPeer())
  182. return peer->getNativeHandle();
  183. carla_stdout("getPtr() failed");
  184. return nullptr;
  185. #endif
  186. }
  187. #ifdef CARLA_OS_MAC
  188. // JUCE MacOS calls
  189. void childBoundsChanged(Component*) override
  190. {
  191. if (fCocoaWrapper != nullptr)
  192. {
  193. const int w = fCocoaWrapper->getWidth();
  194. const int h = fCocoaWrapper->getHeight();
  195. if (w != DocumentWindow::getWidth() || h != DocumentWindow::getHeight())
  196. DocumentWindow::setSize(w, h);
  197. }
  198. }
  199. void resized() override
  200. {
  201. if (fCocoaWrapper != nullptr)
  202. fCocoaWrapper->setSize(DocumentWindow::getWidth(), DocumentWindow::getHeight());
  203. }
  204. #endif
  205. #ifdef CARLA_OS_WINDOWS
  206. // JUCE Windows calls
  207. void mouseDown(const MouseEvent&) override
  208. {
  209. DocumentWindow::toFront(true);
  210. }
  211. #endif
  212. void paint(Graphics& g) override
  213. {
  214. g.fillAll(Colours::black);
  215. }
  216. #if 0
  217. //==============================================================================
  218. bool keyStateChanged (bool) override { return pluginWantsKeys; }
  219. bool keyPressed (const juce::KeyPress&) override { return pluginWantsKeys; }
  220. #endif
  221. private:
  222. volatile bool fClosed;
  223. #ifdef CARLA_OS_MAC
  224. juce::ScopedPointer<AutoResizingNSViewComponentWithParent> fCocoaWrapper;
  225. #endif
  226. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(JucePluginUI)
  227. };
  228. #endif
  229. // -----------------------------------------------------
  230. // X11
  231. #ifdef HAVE_X11
  232. # include "CarlaPluginUI_X11Icon.hpp"
  233. typedef void (*EventProcPtr)(XEvent* ev);
  234. static const uint X11Key_Escape = 9;
  235. static bool gErrorTriggered = false;
  236. static int temporaryErrorHandler(Display*, XErrorEvent*)
  237. {
  238. gErrorTriggered = true;
  239. return 0;
  240. }
  241. class X11PluginUI : public CarlaPluginUI
  242. {
  243. public:
  244. X11PluginUI(CloseCallback* const cb, const uintptr_t parentId, const bool isResizable) noexcept
  245. : CarlaPluginUI(cb, isResizable),
  246. fDisplay(nullptr),
  247. fWindow(0),
  248. fIsVisible(false),
  249. fFirstShow(true),
  250. fEventProc(nullptr)
  251. {
  252. fDisplay = XOpenDisplay(nullptr);
  253. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  254. const int screen = DefaultScreen(fDisplay);
  255. XSetWindowAttributes attr;
  256. carla_zeroStruct(attr);
  257. attr.border_pixel = 0;
  258. attr.event_mask = KeyPressMask|KeyReleaseMask;
  259. if (fIsResizable)
  260. attr.event_mask |= StructureNotifyMask;
  261. fWindow = XCreateWindow(fDisplay, RootWindow(fDisplay, screen),
  262. 0, 0, 300, 300, 0,
  263. DefaultDepth(fDisplay, screen),
  264. InputOutput,
  265. DefaultVisual(fDisplay, screen),
  266. CWBorderPixel|CWEventMask, &attr);
  267. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  268. XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fWindow, 1, GrabModeAsync, GrabModeAsync);
  269. Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True);
  270. XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1);
  271. const pid_t pid = getpid();
  272. const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False);
  273. XChangeProperty(fDisplay, fWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  274. const Atom _nwi = XInternAtom(fDisplay, "_NET_WM_ICON", False);
  275. XChangeProperty(fDisplay, fWindow, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
  276. if (parentId != 0)
  277. setTransientWinId(parentId);
  278. }
  279. ~X11PluginUI() override
  280. {
  281. CARLA_SAFE_ASSERT(! fIsVisible);
  282. if (fIsVisible)
  283. {
  284. XUnmapWindow(fDisplay, fWindow);
  285. fIsVisible = false;
  286. }
  287. if (fWindow != 0)
  288. {
  289. XDestroyWindow(fDisplay, fWindow);
  290. fWindow = 0;
  291. }
  292. if (fDisplay != nullptr)
  293. {
  294. XCloseDisplay(fDisplay);
  295. fDisplay = nullptr;
  296. }
  297. }
  298. void show() override
  299. {
  300. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  301. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  302. if (fFirstShow)
  303. {
  304. if (const Window childWindow = getChildWindow())
  305. {
  306. const Atom _xevp = XInternAtom(fDisplay, "_XEventProc", False);
  307. gErrorTriggered = false;
  308. const XErrorHandler oldErrorHandler(XSetErrorHandler(temporaryErrorHandler));
  309. Atom actualType;
  310. int actualFormat;
  311. ulong nitems, bytesAfter;
  312. uchar* data = nullptr;
  313. XGetWindowProperty(fDisplay, childWindow, _xevp, 0, 1, False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesAfter, &data);
  314. XSetErrorHandler(oldErrorHandler);
  315. if (nitems == 1 && ! gErrorTriggered)
  316. {
  317. fEventProc = *reinterpret_cast<EventProcPtr*>(data);
  318. XMapRaised(fDisplay, childWindow);
  319. }
  320. }
  321. }
  322. fIsVisible = true;
  323. fFirstShow = false;
  324. XMapRaised(fDisplay, fWindow);
  325. XFlush(fDisplay);
  326. }
  327. void hide() override
  328. {
  329. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  330. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  331. fIsVisible = false;
  332. XUnmapWindow(fDisplay, fWindow);
  333. XFlush(fDisplay);
  334. }
  335. void idle() override
  336. {
  337. // prevent recursion
  338. if (fIsIdling) return;
  339. fIsIdling = true;
  340. for (XEvent event; XPending(fDisplay) > 0;)
  341. {
  342. XNextEvent(fDisplay, &event);
  343. if (! fIsVisible)
  344. continue;
  345. char* type = nullptr;
  346. switch (event.type)
  347. {
  348. case ConfigureNotify:
  349. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  350. CARLA_SAFE_ASSERT_CONTINUE(event.xconfigure.width > 0);
  351. CARLA_SAFE_ASSERT_CONTINUE(event.xconfigure.height > 0);
  352. fCallback->handlePluginUIResized(static_cast<uint>(event.xconfigure.width),
  353. static_cast<uint>(event.xconfigure.height));
  354. break;
  355. case ClientMessage:
  356. type = XGetAtomName(fDisplay, event.xclient.message_type);
  357. CARLA_SAFE_ASSERT_CONTINUE(type != nullptr);
  358. if (std::strcmp(type, "WM_PROTOCOLS") == 0)
  359. {
  360. fIsVisible = false;
  361. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  362. fCallback->handlePluginUIClosed();
  363. }
  364. break;
  365. case KeyRelease:
  366. if (event.xkey.keycode == X11Key_Escape)
  367. {
  368. fIsVisible = false;
  369. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  370. fCallback->handlePluginUIClosed();
  371. }
  372. break;
  373. }
  374. if (type != nullptr)
  375. XFree(type);
  376. else if (fEventProc != nullptr)
  377. fEventProc(&event);
  378. }
  379. fIsIdling = false;
  380. }
  381. void focus() override
  382. {
  383. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  384. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  385. XRaiseWindow(fDisplay, fWindow);
  386. XSetInputFocus(fDisplay, fWindow, RevertToPointerRoot, CurrentTime);
  387. XFlush(fDisplay);
  388. }
  389. void setSize(const uint width, const uint height, const bool forceUpdate) override
  390. {
  391. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  392. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  393. XResizeWindow(fDisplay, fWindow, width, height);
  394. if (! fIsResizable)
  395. {
  396. XSizeHints sizeHints;
  397. carla_zeroStruct(sizeHints);
  398. sizeHints.flags = PSize|PMinSize|PMaxSize;
  399. sizeHints.width = static_cast<int>(width);
  400. sizeHints.height = static_cast<int>(height);
  401. sizeHints.min_width = static_cast<int>(width);
  402. sizeHints.min_height = static_cast<int>(height);
  403. sizeHints.max_width = static_cast<int>(width);
  404. sizeHints.max_height = static_cast<int>(height);
  405. XSetNormalHints(fDisplay, fWindow, &sizeHints);
  406. }
  407. if (forceUpdate)
  408. XFlush(fDisplay);
  409. }
  410. void setTitle(const char* const title) override
  411. {
  412. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  413. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  414. XStoreName(fDisplay, fWindow, title);
  415. }
  416. void setTransientWinId(const uintptr_t winId) override
  417. {
  418. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  419. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  420. XSetTransientForHint(fDisplay, fWindow, static_cast<Window>(winId));
  421. }
  422. void* getPtr() const noexcept
  423. {
  424. return (void*)fWindow;
  425. }
  426. void* getDisplay() const noexcept
  427. {
  428. return fDisplay;
  429. }
  430. protected:
  431. Window getChildWindow() const
  432. {
  433. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr, 0);
  434. CARLA_SAFE_ASSERT_RETURN(fWindow != 0, 0);
  435. Window rootWindow, parentWindow, ret = 0;
  436. Window* childWindows = nullptr;
  437. uint numChildren = 0;
  438. XQueryTree(fDisplay, fWindow, &rootWindow, &parentWindow, &childWindows, &numChildren);
  439. if (numChildren > 0 && childWindows != nullptr)
  440. {
  441. ret = childWindows[0];
  442. XFree(childWindows);
  443. }
  444. return ret;
  445. }
  446. private:
  447. Display* fDisplay;
  448. Window fWindow;
  449. bool fIsVisible;
  450. bool fFirstShow;
  451. EventProcPtr fEventProc;
  452. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(X11PluginUI)
  453. };
  454. #endif // HAVE_X11
  455. // -----------------------------------------------------
  456. bool CarlaPluginUI::tryTransientWinIdMatch(const uintptr_t pid, const char* const uiTitle, const uintptr_t winId, const bool centerUI)
  457. {
  458. CARLA_SAFE_ASSERT_RETURN(uiTitle != nullptr && uiTitle[0] != '\0', true);
  459. CARLA_SAFE_ASSERT_RETURN(winId != 0, true);
  460. #if defined(CARLA_OS_MAC)
  461. return true;
  462. (void)pid; (void)centerUI;
  463. #elif defined(CARLA_OS_WIN)
  464. return true;
  465. (void)pid; (void)centerUI;
  466. #elif defined(HAVE_X11)
  467. struct ScopedDisplay {
  468. Display* display;
  469. ScopedDisplay() : display(XOpenDisplay(nullptr)) {}
  470. ~ScopedDisplay() { if (display!=nullptr) XCloseDisplay(display); }
  471. // c++ compat stuff
  472. CARLA_PREVENT_HEAP_ALLOCATION
  473. CARLA_DECLARE_NON_COPY_CLASS(ScopedDisplay)
  474. };
  475. struct ScopedFreeData {
  476. union {
  477. char* data;
  478. uchar* udata;
  479. };
  480. ScopedFreeData(char* d) : data(d) {}
  481. ScopedFreeData(uchar* d) : udata(d) {}
  482. ~ScopedFreeData() { XFree(data); }
  483. // c++ compat stuff
  484. CARLA_PREVENT_HEAP_ALLOCATION
  485. CARLA_DECLARE_NON_COPY_CLASS(ScopedFreeData)
  486. };
  487. const ScopedDisplay sd;
  488. CARLA_SAFE_ASSERT_RETURN(sd.display != nullptr, true);
  489. const Window rootWindow(DefaultRootWindow(sd.display));
  490. const Atom _ncl = XInternAtom(sd.display, "_NET_CLIENT_LIST" , False);
  491. const Atom _nwn = XInternAtom(sd.display, "_NET_WM_NAME", False);
  492. const Atom _nwp = XInternAtom(sd.display, "_NET_WM_PID", False);
  493. const Atom utf8 = XInternAtom(sd.display, "UTF8_STRING", True);
  494. Atom actualType;
  495. int actualFormat;
  496. ulong numWindows, bytesAfter;
  497. uchar* data = nullptr;
  498. int status = XGetWindowProperty(sd.display, rootWindow, _ncl, 0L, (~0L), False, AnyPropertyType, &actualType, &actualFormat, &numWindows, &bytesAfter, &data);
  499. CARLA_SAFE_ASSERT_RETURN(data != nullptr, true);
  500. const ScopedFreeData sfd(data);
  501. CARLA_SAFE_ASSERT_RETURN(status == Success, true);
  502. CARLA_SAFE_ASSERT_RETURN(actualFormat == 32, true);
  503. CARLA_SAFE_ASSERT_RETURN(numWindows != 0, true);
  504. Window* windows = (Window*)data;
  505. Window lastGoodWindow = 0;
  506. for (ulong i = 0; i < numWindows; i++)
  507. {
  508. const Window window(windows[i]);
  509. CARLA_SAFE_ASSERT_CONTINUE(window != 0);
  510. // ------------------------------------------------
  511. // try using pid
  512. if (pid != 0)
  513. {
  514. ulong pidSize;
  515. uchar* pidData = nullptr;
  516. status = XGetWindowProperty(sd.display, window, _nwp, 0L, (~0L), False, XA_CARDINAL, &actualType, &actualFormat, &pidSize, &bytesAfter, &pidData);
  517. if (pidData != nullptr)
  518. {
  519. const ScopedFreeData sfd2(pidData);
  520. CARLA_SAFE_ASSERT_CONTINUE(status == Success);
  521. CARLA_SAFE_ASSERT_CONTINUE(pidSize != 0);
  522. if (*(ulong*)pidData == static_cast<ulong>(pid))
  523. {
  524. CARLA_SAFE_ASSERT_RETURN(lastGoodWindow == window || lastGoodWindow == 0, true);
  525. lastGoodWindow = window;
  526. carla_stdout("Match found using pid");
  527. break;
  528. }
  529. }
  530. }
  531. // ------------------------------------------------
  532. // try using name (UTF-8)
  533. ulong nameSize;
  534. uchar* nameData = nullptr;
  535. status = XGetWindowProperty(sd.display, window, _nwn, 0L, (~0L), False, utf8, &actualType, &actualFormat, &nameSize, &bytesAfter, &nameData);
  536. if (nameData != nullptr)
  537. {
  538. const ScopedFreeData sfd2(nameData);
  539. CARLA_SAFE_ASSERT_CONTINUE(status == Success);
  540. if (nameSize == 0)
  541. // nameless window
  542. continue;
  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. // -----------------------------------------------------