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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  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. {
  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 int 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. CARLA_SAFE_ASSERT_CONTINUE(nameSize != 0);
  541. if (std::strstr((const char*)nameData, uiTitle) != nullptr)
  542. {
  543. CARLA_SAFE_ASSERT_RETURN(lastGoodWindow == window || lastGoodWindow == 0, true);
  544. lastGoodWindow = window;
  545. carla_stdout("Match found using UTF-8 name");
  546. }
  547. }
  548. // ------------------------------------------------
  549. // try using name (simple)
  550. char* wmName = nullptr;
  551. status = XFetchName(sd.display, window, &wmName);
  552. if (wmName != nullptr)
  553. {
  554. const ScopedFreeData sfd2(wmName);
  555. CARLA_SAFE_ASSERT_CONTINUE(status != 0);
  556. if (std::strstr(wmName, uiTitle) != nullptr)
  557. {
  558. CARLA_SAFE_ASSERT_RETURN(lastGoodWindow == window || lastGoodWindow == 0, true);
  559. lastGoodWindow = window;
  560. carla_stdout("Match found using simple name");
  561. }
  562. }
  563. }
  564. if (lastGoodWindow == 0)
  565. return false;
  566. const Atom _nwt = XInternAtom(sd.display ,"_NET_WM_STATE", False);
  567. const Atom _nws[2] = {
  568. XInternAtom(sd.display, "_NET_WM_STATE_SKIP_TASKBAR", False),
  569. XInternAtom(sd.display, "_NET_WM_STATE_SKIP_PAGER", False)
  570. };
  571. XChangeProperty(sd.display, lastGoodWindow, _nwt, XA_ATOM, 32, PropModeAppend, (const uchar*)_nws, 2);
  572. const Atom _nwi = XInternAtom(sd.display, "_NET_WM_ICON", False);
  573. XChangeProperty(sd.display, lastGoodWindow, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
  574. const Window hostWinId((Window)winId);
  575. XSetTransientForHint(sd.display, lastGoodWindow, hostWinId);
  576. if (centerUI && false /* moving the window after being shown isn't pretty... */)
  577. {
  578. int hostX, hostY, pluginX, pluginY;
  579. uint hostWidth, hostHeight, pluginWidth, pluginHeight, border, depth;
  580. Window retWindow;
  581. if (XGetGeometry(sd.display, hostWinId, &retWindow, &hostX, &hostY, &hostWidth, &hostHeight, &border, &depth) != 0 &&
  582. XGetGeometry(sd.display, lastGoodWindow, &retWindow, &pluginX, &pluginY, &pluginWidth, &pluginHeight, &border, &depth) != 0)
  583. {
  584. if (XTranslateCoordinates(sd.display, hostWinId, rootWindow, hostX, hostY, &hostX, &hostY, &retWindow) == True &&
  585. XTranslateCoordinates(sd.display, lastGoodWindow, rootWindow, pluginX, pluginY, &pluginX, &pluginY, &retWindow) == True)
  586. {
  587. const int newX = hostX + int(hostWidth/2 - pluginWidth/2);
  588. const int newY = hostY + int(hostHeight/2 - pluginHeight/2);
  589. XMoveWindow(sd.display, lastGoodWindow, newX, newY);
  590. }
  591. }
  592. }
  593. // focusing the host UI and then the plugin UI forces the WM to repaint the plugin window icon
  594. XRaiseWindow(sd.display, hostWinId);
  595. XSetInputFocus(sd.display, hostWinId, RevertToPointerRoot, CurrentTime);
  596. XRaiseWindow(sd.display, lastGoodWindow);
  597. XSetInputFocus(sd.display, lastGoodWindow, RevertToPointerRoot, CurrentTime);
  598. XFlush(sd.display);
  599. return true;
  600. #else
  601. return true;
  602. (void)pid; (void)centerUI;
  603. #endif
  604. }
  605. // -----------------------------------------------------
  606. #ifdef CARLA_OS_MAC
  607. CarlaPluginUI* CarlaPluginUI::newCocoa(CloseCallback* cb, uintptr_t parentId, bool /*isResizable*/)
  608. {
  609. return new JucePluginUI(cb, parentId);
  610. }
  611. #endif
  612. #ifdef CARLA_OS_WIN
  613. CarlaPluginUI* CarlaPluginUI::newWindows(CloseCallback* cb, uintptr_t parentId, bool /*isResizable*/)
  614. {
  615. return new JucePluginUI(cb, parentId);
  616. }
  617. #endif
  618. #ifdef HAVE_X11
  619. CarlaPluginUI* CarlaPluginUI::newX11(CloseCallback* cb, uintptr_t parentId, bool isResizable)
  620. {
  621. return new X11PluginUI(cb, parentId, isResizable);
  622. }
  623. #endif
  624. // -----------------------------------------------------