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.

CarlaPluginUI.cpp 22KB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  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. // -----------------------------------------------------