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.

313 lines
10KB

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