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.

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