The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

337 lines
11KB

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