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