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.

juce_linux_X11.cpp 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. typedef void (*WindowMessageReceiveCallback) (XEvent&);
  20. WindowMessageReceiveCallback dispatchWindowMessage = nullptr;
  21. typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&);
  22. SelectionRequestCallback handleSelectionRequest = nullptr;
  23. ::Window juce_messageWindowHandle;
  24. XContext windowHandleXContext;
  25. //==============================================================================
  26. namespace X11ErrorHandling
  27. {
  28. static XErrorHandler oldErrorHandler = (XErrorHandler) 0;
  29. static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0;
  30. //==============================================================================
  31. // Usually happens when client-server connection is broken
  32. int ioErrorHandler (::Display*)
  33. {
  34. DBG ("ERROR: connection to X server broken.. terminating.");
  35. if (JUCEApplicationBase::isStandaloneApp())
  36. MessageManager::getInstance()->stopDispatchLoop();
  37. // set this somewhere
  38. // errorOccurred = true;
  39. return 0;
  40. }
  41. int errorHandler (::Display* display, XErrorEvent* event)
  42. {
  43. ignoreUnused (display, event);
  44. #if JUCE_DEBUG_XERRORS
  45. char errorStr[64] = { 0 };
  46. char requestStr[64] = { 0 };
  47. XGetErrorText (display, event->error_code, errorStr, 64);
  48. XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64);
  49. DBG ("ERROR: X returned " << errorStr << " for operation " << requestStr);
  50. #endif
  51. return 0;
  52. }
  53. void installXErrorHandlers()
  54. {
  55. oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler);
  56. oldErrorHandler = XSetErrorHandler (errorHandler);
  57. }
  58. void removeXErrorHandlers()
  59. {
  60. XSetIOErrorHandler (oldIOErrorHandler);
  61. oldIOErrorHandler = 0;
  62. XSetErrorHandler (oldErrorHandler);
  63. oldErrorHandler = 0;
  64. }
  65. }
  66. //==============================================================================
  67. XWindowSystem::XWindowSystem() noexcept
  68. : display (nullptr)
  69. {
  70. if (JUCEApplicationBase::isStandaloneApp())
  71. {
  72. // Initialise xlib for multiple thread support
  73. static bool initThreadCalled = false;
  74. if (! initThreadCalled)
  75. {
  76. if (! XInitThreads())
  77. {
  78. // This is fatal! Print error and closedown
  79. Logger::outputDebugString ("Failed to initialise xlib thread support.");
  80. Process::terminate();
  81. return;
  82. }
  83. initThreadCalled = true;
  84. }
  85. X11ErrorHandling::installXErrorHandlers();
  86. }
  87. }
  88. XWindowSystem::~XWindowSystem() noexcept
  89. {
  90. if (JUCEApplicationBase::isStandaloneApp())
  91. X11ErrorHandling::removeXErrorHandlers();
  92. clearSingletonInstance();
  93. }
  94. ::Display* XWindowSystem::displayRef() noexcept
  95. {
  96. if (++displayCount - 1 == 0)
  97. {
  98. String displayName (getenv ("DISPLAY"));
  99. if (displayName.isEmpty())
  100. displayName = ":0.0";
  101. display = XOpenDisplay (displayName.toUTF8());
  102. initialiseXDisplay();
  103. }
  104. return this->display;
  105. }
  106. ::Display* XWindowSystem::displayUnref() noexcept
  107. {
  108. jassert (display != nullptr);
  109. jassert (displayCount.get() > 0);
  110. if (--displayCount == 0)
  111. {
  112. destroyXDisplay();
  113. XCloseDisplay (display);
  114. display = nullptr;
  115. }
  116. return display;
  117. }
  118. void XWindowSystem::initialiseXDisplay() noexcept
  119. {
  120. // This is fatal! Print error and closedown
  121. if (display == nullptr)
  122. {
  123. Logger::outputDebugString ("Failed to connect to the X Server.");
  124. Process::terminate();
  125. }
  126. // Create a context to store user data associated with Windows we create
  127. windowHandleXContext = XUniqueContext();
  128. // We're only interested in client messages for this window, which are always sent
  129. XSetWindowAttributes swa;
  130. swa.event_mask = NoEventMask;
  131. // Create our message window (this will never be mapped)
  132. const int screen = DefaultScreen (display);
  133. juce_messageWindowHandle = XCreateWindow (display, RootWindow (display, screen),
  134. 0, 0, 1, 1, 0, 0, InputOnly,
  135. DefaultVisual (display, screen),
  136. CWEventMask, &swa);
  137. XSync (display, False);
  138. // Setup input event handler
  139. int fd = XConnectionNumber (display);
  140. LinuxEventLoop::setWindowSystemFd
  141. (fd,
  142. [this](int /*fd*/) {
  143. do
  144. {
  145. XEvent evt;
  146. {
  147. ScopedXLock xlock (display);
  148. if (! XPending (display))
  149. return false;
  150. XNextEvent (display, &evt);
  151. }
  152. if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle
  153. && handleSelectionRequest != nullptr)
  154. handleSelectionRequest (evt.xselectionrequest);
  155. else if (evt.xany.window != juce_messageWindowHandle
  156. && dispatchWindowMessage != nullptr)
  157. dispatchWindowMessage (evt);
  158. } while (display != nullptr);
  159. return false;
  160. });
  161. }
  162. void XWindowSystem::destroyXDisplay() noexcept
  163. {
  164. ScopedXLock xlock (display);
  165. XDestroyWindow (display, juce_messageWindowHandle);
  166. juce_messageWindowHandle = 0;
  167. XSync (display, True);
  168. LinuxEventLoop::removeWindowSystemFd();
  169. }
  170. juce_ImplementSingleton (XWindowSystem)
  171. //==============================================================================
  172. ScopedXDisplay::ScopedXDisplay() : display (XWindowSystem::getInstance()->displayRef())
  173. {
  174. }
  175. ScopedXDisplay::~ScopedXDisplay()
  176. {
  177. XWindowSystem::getInstance()->displayUnref();
  178. }
  179. //==============================================================================
  180. ScopedXLock::ScopedXLock(::Display* d) : display (d)
  181. {
  182. if (display != nullptr) XLockDisplay (display);
  183. }
  184. ScopedXLock::~ScopedXLock()
  185. {
  186. if (display != nullptr) XUnlockDisplay (display);
  187. }
  188. //==============================================================================
  189. Atoms::Atoms(::Display* display)
  190. {
  191. protocols = getIfExists (display, "WM_PROTOCOLS");
  192. protocolList [TAKE_FOCUS] = getIfExists (display, "WM_TAKE_FOCUS");
  193. protocolList [DELETE_WINDOW] = getIfExists (display, "WM_DELETE_WINDOW");
  194. protocolList [PING] = getIfExists (display, "_NET_WM_PING");
  195. changeState = getIfExists (display, "WM_CHANGE_STATE");
  196. state = getIfExists (display, "WM_STATE");
  197. userTime = getCreating (display, "_NET_WM_USER_TIME");
  198. activeWin = getCreating (display, "_NET_ACTIVE_WINDOW");
  199. pid = getCreating (display, "_NET_WM_PID");
  200. windowType = getIfExists (display, "_NET_WM_WINDOW_TYPE");
  201. windowState = getIfExists (display, "_NET_WM_STATE");
  202. XdndAware = getCreating (display, "XdndAware");
  203. XdndEnter = getCreating (display, "XdndEnter");
  204. XdndLeave = getCreating (display, "XdndLeave");
  205. XdndPosition = getCreating (display, "XdndPosition");
  206. XdndStatus = getCreating (display, "XdndStatus");
  207. XdndDrop = getCreating (display, "XdndDrop");
  208. XdndFinished = getCreating (display, "XdndFinished");
  209. XdndSelection = getCreating (display, "XdndSelection");
  210. XdndTypeList = getCreating (display, "XdndTypeList");
  211. XdndActionList = getCreating (display, "XdndActionList");
  212. XdndActionCopy = getCreating (display, "XdndActionCopy");
  213. XdndActionPrivate = getCreating (display, "XdndActionPrivate");
  214. XdndActionDescription = getCreating (display, "XdndActionDescription");
  215. XembedMsgType = getCreating (display, "_XEMBED");
  216. XembedInfo = getCreating (display, "_XEMBED_INFO");
  217. allowedMimeTypes[0] = getCreating (display, "UTF8_STRING");
  218. allowedMimeTypes[1] = getCreating (display, "text/plain;charset=utf-8");
  219. allowedMimeTypes[2] = getCreating (display, "text/plain");
  220. allowedMimeTypes[3] = getCreating (display, "text/uri-list");
  221. allowedActions[0] = getCreating (display, "XdndActionMove");
  222. allowedActions[1] = XdndActionCopy;
  223. allowedActions[2] = getCreating (display, "XdndActionLink");
  224. allowedActions[3] = getCreating (display, "XdndActionAsk");
  225. allowedActions[4] = XdndActionPrivate;
  226. }
  227. Atom Atoms::getIfExists (::Display* display, const char* name)
  228. {
  229. return XInternAtom (display, name, True);
  230. }
  231. Atom Atoms::getCreating (::Display* display, const char* name)
  232. {
  233. return XInternAtom (display, name, False);
  234. }
  235. String Atoms::getName (::Display* display, const Atom atom)
  236. {
  237. if (atom == None)
  238. return "None";
  239. return String (XGetAtomName (display, atom));
  240. }
  241. bool Atoms::isMimeTypeFile (::Display* display, const Atom atom)
  242. {
  243. return getName (display, atom).equalsIgnoreCase ("text/uri-list");
  244. }
  245. const unsigned long Atoms::DndVersion = 3;
  246. //==============================================================================
  247. GetXProperty::GetXProperty (::Display* display, Window window, Atom atom,
  248. long offset, long length, bool shouldDelete,
  249. Atom requestedType)
  250. : data (nullptr)
  251. {
  252. success = (XGetWindowProperty (display, window, atom, offset, length,
  253. (Bool) shouldDelete, requestedType, &actualType,
  254. &actualFormat, &numItems, &bytesLeft, &data) == Success)
  255. && data != nullptr;
  256. }
  257. GetXProperty::~GetXProperty()
  258. {
  259. if (data != nullptr)
  260. XFree (data);
  261. }