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 12KB

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
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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 "CarlaPluginUi.hpp"
  18. #ifdef HAVE_X11
  19. # include <sys/types.h>
  20. # include <X11/Xatom.h>
  21. # include <X11/Xlib.h>
  22. # include <X11/Xutil.h>
  23. #endif
  24. #ifdef HAVE_X11
  25. # include "CarlaPluginUi_X11Icon.hpp"
  26. // -----------------------------------------------------
  27. // X11
  28. static const int X11Key_Escape = 9;
  29. class X11PluginUi : public CarlaPluginUi
  30. {
  31. public:
  32. X11PluginUi(CloseCallback* const cb, const uintptr_t parentId) noexcept
  33. : CarlaPluginUi(cb),
  34. fDisplay(nullptr),
  35. fWindow(0),
  36. fIsVisible(false)
  37. {
  38. fDisplay = XOpenDisplay(nullptr);
  39. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  40. const int screen = DefaultScreen(fDisplay);
  41. XSetWindowAttributes attr;
  42. carla_zeroStruct<XSetWindowAttributes>(attr);
  43. attr.border_pixel = 0;
  44. attr.event_mask = KeyPressMask|KeyReleaseMask;
  45. fWindow = XCreateWindow(fDisplay, RootWindow(fDisplay, screen),
  46. 0, 0, 300, 300, 0,
  47. DefaultDepth(fDisplay, screen),
  48. InputOutput,
  49. DefaultVisual(fDisplay, screen),
  50. CWBorderPixel|CWEventMask, &attr);
  51. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  52. XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fWindow, 1, GrabModeAsync, GrabModeAsync);
  53. Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True);
  54. XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1);
  55. pid_t pid = getpid();
  56. Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False);
  57. XChangeProperty(fDisplay, fWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  58. Atom _nwi = XInternAtom(fDisplay, "_NET_WM_ICON", False);
  59. XChangeProperty(fDisplay, fWindow, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
  60. if (parentId != 0)
  61. setTransientWinId(parentId);
  62. }
  63. ~X11PluginUi() override
  64. {
  65. CARLA_SAFE_ASSERT(! fIsVisible);
  66. if (fIsVisible)
  67. {
  68. XUnmapWindow(fDisplay, fWindow);
  69. fIsVisible = false;
  70. }
  71. if (fWindow != 0)
  72. {
  73. XDestroyWindow(fDisplay, fWindow);
  74. fWindow = 0;
  75. }
  76. if (fDisplay != nullptr)
  77. {
  78. XCloseDisplay(fDisplay);
  79. fDisplay = nullptr;
  80. }
  81. }
  82. void show() override
  83. {
  84. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  85. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  86. fIsVisible = true;
  87. XMapRaised(fDisplay, fWindow);
  88. XFlush(fDisplay);
  89. }
  90. void hide() override
  91. {
  92. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  93. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  94. fIsVisible = false;
  95. XUnmapWindow(fDisplay, fWindow);
  96. XFlush(fDisplay);
  97. }
  98. void idle() override
  99. {
  100. for (XEvent event; XPending(fDisplay) > 0;)
  101. {
  102. XNextEvent(fDisplay, &event);
  103. if (! fIsVisible)
  104. continue;
  105. char* type = nullptr;
  106. switch (event.type)
  107. {
  108. case ClientMessage:
  109. type = XGetAtomName(fDisplay, event.xclient.message_type);
  110. CARLA_SAFE_ASSERT_CONTINUE(type != nullptr);
  111. if (std::strcmp(type, "WM_PROTOCOLS") == 0)
  112. {
  113. fIsVisible = false;
  114. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  115. fCallback->handlePluginUiClosed();
  116. }
  117. break;
  118. case KeyRelease:
  119. if (event.xkey.keycode == X11Key_Escape)
  120. {
  121. fIsVisible = false;
  122. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  123. fCallback->handlePluginUiClosed();
  124. }
  125. break;
  126. }
  127. if (type != nullptr)
  128. XFree(type);
  129. }
  130. }
  131. void focus() override
  132. {
  133. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  134. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  135. XRaiseWindow(fDisplay, fWindow);
  136. XSetInputFocus(fDisplay, fWindow, RevertToPointerRoot, CurrentTime);
  137. XFlush(fDisplay);
  138. }
  139. void setSize(const uint width, const uint height, const bool forceUpdate) override
  140. {
  141. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  142. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  143. XResizeWindow(fDisplay, fWindow, width, height);
  144. XSizeHints sizeHints;
  145. carla_zeroStruct<XSizeHints>(sizeHints);
  146. sizeHints.flags = PSize|PMinSize|PMaxSize;
  147. sizeHints.width = static_cast<int>(width);
  148. sizeHints.height = static_cast<int>(height);
  149. sizeHints.min_width = static_cast<int>(width);
  150. sizeHints.min_height = static_cast<int>(height);
  151. sizeHints.max_width = static_cast<int>(width);
  152. sizeHints.max_height = static_cast<int>(height);
  153. XSetNormalHints(fDisplay, fWindow, &sizeHints);
  154. if (forceUpdate)
  155. XFlush(fDisplay);
  156. }
  157. void setTitle(const char* const title) override
  158. {
  159. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  160. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  161. XStoreName(fDisplay, fWindow, title);
  162. }
  163. void setTransientWinId(const uintptr_t winId) override
  164. {
  165. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  166. CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
  167. XSetTransientForHint(fDisplay, fWindow, static_cast<Window>(winId));
  168. }
  169. void* getPtr() const noexcept
  170. {
  171. return (void*)fWindow;
  172. }
  173. private:
  174. Display* fDisplay;
  175. Window fWindow;
  176. bool fIsVisible;
  177. };
  178. #endif
  179. // -----------------------------------------------------
  180. bool CarlaPluginUi::tryTransientWinIdMatch(const uintptr_t pid, const char* const uiTitle, const uintptr_t winId)
  181. {
  182. CARLA_SAFE_ASSERT_RETURN(uiTitle != nullptr && uiTitle[0] != '\0', true);
  183. CARLA_SAFE_ASSERT_RETURN(winId != 0, true);
  184. #if defined(CARLA_OS_MAC)
  185. return true;
  186. (void)pid;
  187. #elif defined(CARLA_OS_WIN)
  188. return true;
  189. (void)pid;
  190. #elif defined(HAVE_X11)
  191. struct ScopedDisplay {
  192. Display* display;
  193. ScopedDisplay() : display(XOpenDisplay(nullptr)) {}
  194. ~ScopedDisplay() { if (display!=nullptr) XCloseDisplay(display); }
  195. };
  196. struct ScopedFreeData {
  197. union {
  198. char* data;
  199. uchar* udata;
  200. };
  201. ScopedFreeData(char* d) : data(d) {}
  202. ScopedFreeData(uchar* d) : udata(d) {}
  203. ~ScopedFreeData() { XFree(data); }
  204. };
  205. const ScopedDisplay sd;
  206. CARLA_SAFE_ASSERT_RETURN(sd.display != nullptr, true);
  207. Atom _ncl = XInternAtom(sd.display, "_NET_CLIENT_LIST" , False);
  208. Atom _nwn = XInternAtom(sd.display, "_NET_WM_NAME", False);
  209. Atom _nwp = XInternAtom(sd.display, "_NET_WM_PID", False);
  210. Atom utf8 = XInternAtom(sd.display, "UTF8_STRING", True);
  211. Atom actualType;
  212. int actualFormat;
  213. ulong numWindows, bytesAfter;
  214. uchar* data = nullptr;
  215. int status = XGetWindowProperty(sd.display, DefaultRootWindow(sd.display), _ncl, 0L, (~0L), False, AnyPropertyType, &actualType, &actualFormat, &numWindows, &bytesAfter, &data);
  216. CARLA_SAFE_ASSERT_RETURN(data != nullptr, true);
  217. const ScopedFreeData sfd(data);
  218. CARLA_SAFE_ASSERT_RETURN(status == Success, true);
  219. CARLA_SAFE_ASSERT_RETURN(actualFormat == 32, true);
  220. CARLA_SAFE_ASSERT_RETURN(numWindows != 0, true);
  221. Window* windows = (Window*)data;
  222. Window lastGoodWindow = 0;
  223. for (ulong i = 0; i < numWindows; i++)
  224. {
  225. const Window window(windows[i]);
  226. CARLA_SAFE_ASSERT_CONTINUE(window != 0);
  227. // ------------------------------------------------
  228. // try using pid
  229. if (pid != 0)
  230. {
  231. ulong pidSize;
  232. uchar* pidData = nullptr;
  233. status = XGetWindowProperty(sd.display, window, _nwp, 0L, (~0L), False, XA_CARDINAL, &actualType, &actualFormat, &pidSize, &bytesAfter, &pidData);
  234. if (pidData != nullptr)
  235. {
  236. const ScopedFreeData sfd2(pidData);
  237. CARLA_SAFE_ASSERT_CONTINUE(status == Success);
  238. CARLA_SAFE_ASSERT_CONTINUE(pidSize != 0);
  239. if (*(ulong*)pidData == static_cast<ulong>(pid))
  240. {
  241. CARLA_SAFE_ASSERT_RETURN(lastGoodWindow == window || lastGoodWindow == 0, true);
  242. lastGoodWindow = window;
  243. carla_stdout("Match found using pid");
  244. break;
  245. }
  246. }
  247. }
  248. // ------------------------------------------------
  249. // try using name (UTF-8)
  250. ulong nameSize;
  251. uchar* nameData = nullptr;
  252. status = XGetWindowProperty(sd.display, window, _nwn, 0L, (~0L), False, utf8, &actualType, &actualFormat, &nameSize, &bytesAfter, &nameData);
  253. if (nameData != nullptr)
  254. {
  255. const ScopedFreeData sfd2(nameData);
  256. CARLA_SAFE_ASSERT_CONTINUE(status == Success);
  257. CARLA_SAFE_ASSERT_CONTINUE(nameSize != 0);
  258. if (std::strstr((const char*)nameData, uiTitle) != nullptr)
  259. {
  260. CARLA_SAFE_ASSERT_RETURN(lastGoodWindow == window || lastGoodWindow == 0, true);
  261. lastGoodWindow = window;
  262. carla_stdout("Match found using UTF-8 name");
  263. }
  264. }
  265. // ------------------------------------------------
  266. // try using name (simple)
  267. char* wmName = nullptr;
  268. status = XFetchName(sd.display, window, &wmName);
  269. if (wmName != nullptr)
  270. {
  271. const ScopedFreeData sfd2(wmName);
  272. CARLA_SAFE_ASSERT_CONTINUE(status != 0);
  273. if (std::strstr(wmName, uiTitle) != nullptr)
  274. {
  275. CARLA_SAFE_ASSERT_RETURN(lastGoodWindow == window || lastGoodWindow == 0, true);
  276. lastGoodWindow = window;
  277. carla_stdout("Match found using simple name");
  278. }
  279. }
  280. }
  281. if (lastGoodWindow == 0)
  282. return false;
  283. Atom _nwt = XInternAtom(sd.display ,"_NET_WM_STATE", False);
  284. Atom _nws[2];
  285. _nws[0] = XInternAtom(sd.display, "_NET_WM_STATE_SKIP_TASKBAR", False);
  286. _nws[1] = XInternAtom(sd.display, "_NET_WM_STATE_SKIP_PAGER", False);
  287. XChangeProperty(sd.display, lastGoodWindow, _nwt, XA_ATOM, 32, PropModeAppend, (const uchar*)_nws, 2);
  288. Atom _nwi = XInternAtom(sd.display, "_NET_WM_ICON", False);
  289. XChangeProperty(sd.display, lastGoodWindow, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
  290. XSetTransientForHint(sd.display, lastGoodWindow, (Window)winId);
  291. XRaiseWindow(sd.display, lastGoodWindow);
  292. XSetInputFocus(sd.display, lastGoodWindow, RevertToPointerRoot, CurrentTime);
  293. XFlush(sd.display);
  294. return true;
  295. #else
  296. return true;
  297. (void)pid;
  298. #endif
  299. }
  300. // -----------------------------------------------------
  301. #ifdef CARLA_OS_MAC
  302. CarlaPluginUi* CarlaPluginUi::newCocoa(CloseCallback*, uintptr_t)
  303. {
  304. //return new CocoaPluginUi(cb, parentId);
  305. return nullptr;
  306. }
  307. #endif
  308. #ifdef CARLA_OS_WIN
  309. CarlaPluginUi* CarlaPluginUi::newWindows(CloseCallback*, uintptr_t)
  310. {
  311. //return new WindowsPluginUi(cb, parentId);
  312. return nullptr;
  313. }
  314. #endif
  315. #ifdef HAVE_X11
  316. CarlaPluginUi* CarlaPluginUi::newX11(CloseCallback* cb, uintptr_t parentId)
  317. {
  318. return new X11PluginUi(cb, parentId);
  319. }
  320. #endif
  321. // -----------------------------------------------------