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.

787 lines
23KB

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