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.

401 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #if JUCE_DEBUG && ! defined (JUCE_DEBUG_XERRORS)
  19. #define JUCE_DEBUG_XERRORS 1
  20. #endif
  21. Display* display = nullptr;
  22. Window juce_messageWindowHandle = None;
  23. XContext windowHandleXContext; // This is referenced from Windowing.cpp
  24. typedef bool (*WindowMessageReceiveCallback) (XEvent&);
  25. WindowMessageReceiveCallback dispatchWindowMessage = nullptr;
  26. typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&);
  27. SelectionRequestCallback handleSelectionRequest = nullptr;
  28. //==============================================================================
  29. ScopedXLock::ScopedXLock() { XLockDisplay (display); }
  30. ScopedXLock::~ScopedXLock() { XUnlockDisplay (display); }
  31. //==============================================================================
  32. class InternalMessageQueue
  33. {
  34. public:
  35. InternalMessageQueue()
  36. : bytesInSocket (0),
  37. totalEventCount (0)
  38. {
  39. int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd);
  40. (void) ret; jassert (ret == 0);
  41. }
  42. ~InternalMessageQueue()
  43. {
  44. close (fd[0]);
  45. close (fd[1]);
  46. clearSingletonInstance();
  47. }
  48. //==============================================================================
  49. void postMessage (MessageManager::MessageBase* const msg)
  50. {
  51. const int maxBytesInSocketQueue = 128;
  52. ScopedLock sl (lock);
  53. queue.add (msg);
  54. if (bytesInSocket < maxBytesInSocketQueue)
  55. {
  56. ++bytesInSocket;
  57. ScopedUnlock ul (lock);
  58. const unsigned char x = 0xff;
  59. size_t bytesWritten = write (fd[0], &x, 1);
  60. (void) bytesWritten;
  61. }
  62. }
  63. bool isEmpty() const
  64. {
  65. ScopedLock sl (lock);
  66. return queue.size() == 0;
  67. }
  68. bool dispatchNextEvent()
  69. {
  70. // This alternates between giving priority to XEvents or internal messages,
  71. // to keep everything running smoothly..
  72. if ((++totalEventCount & 1) != 0)
  73. return dispatchNextXEvent() || dispatchNextInternalMessage();
  74. return dispatchNextInternalMessage() || dispatchNextXEvent();
  75. }
  76. // Wait for an event (either XEvent, or an internal Message)
  77. bool sleepUntilEvent (const int timeoutMs)
  78. {
  79. if (! isEmpty())
  80. return true;
  81. if (display != 0)
  82. {
  83. ScopedXLock xlock;
  84. if (XPending (display))
  85. return true;
  86. }
  87. struct timeval tv;
  88. tv.tv_sec = 0;
  89. tv.tv_usec = timeoutMs * 1000;
  90. int fd0 = getWaitHandle();
  91. int fdmax = fd0;
  92. fd_set readset;
  93. FD_ZERO (&readset);
  94. FD_SET (fd0, &readset);
  95. if (display != 0)
  96. {
  97. ScopedXLock xlock;
  98. int fd1 = XConnectionNumber (display);
  99. FD_SET (fd1, &readset);
  100. fdmax = jmax (fd0, fd1);
  101. }
  102. const int ret = select (fdmax + 1, &readset, 0, 0, &tv);
  103. return (ret > 0); // ret <= 0 if error or timeout
  104. }
  105. //==============================================================================
  106. juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue);
  107. private:
  108. CriticalSection lock;
  109. ReferenceCountedArray <MessageManager::MessageBase> queue;
  110. int fd[2];
  111. int bytesInSocket;
  112. int totalEventCount;
  113. int getWaitHandle() const noexcept { return fd[1]; }
  114. static bool setNonBlocking (int handle)
  115. {
  116. int socketFlags = fcntl (handle, F_GETFL, 0);
  117. if (socketFlags == -1)
  118. return false;
  119. socketFlags |= O_NONBLOCK;
  120. return fcntl (handle, F_SETFL, socketFlags) == 0;
  121. }
  122. static bool dispatchNextXEvent()
  123. {
  124. if (display == 0)
  125. return false;
  126. XEvent evt;
  127. {
  128. ScopedXLock xlock;
  129. if (! XPending (display))
  130. return false;
  131. XNextEvent (display, &evt);
  132. }
  133. if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle
  134. && handleSelectionRequest != nullptr)
  135. handleSelectionRequest (evt.xselectionrequest);
  136. else if (evt.xany.window != juce_messageWindowHandle && dispatchWindowMessage != nullptr)
  137. dispatchWindowMessage (evt);
  138. return true;
  139. }
  140. MessageManager::MessageBase::Ptr popNextMessage()
  141. {
  142. const ScopedLock sl (lock);
  143. if (bytesInSocket > 0)
  144. {
  145. --bytesInSocket;
  146. const ScopedUnlock ul (lock);
  147. unsigned char x;
  148. size_t numBytes = read (fd[1], &x, 1);
  149. (void) numBytes;
  150. }
  151. return queue.removeAndReturn (0);
  152. }
  153. bool dispatchNextInternalMessage()
  154. {
  155. const MessageManager::MessageBase::Ptr msg (popNextMessage());
  156. if (msg == nullptr)
  157. return false;
  158. JUCE_TRY
  159. {
  160. msg->messageCallback();
  161. }
  162. JUCE_CATCH_EXCEPTION
  163. return true;
  164. }
  165. };
  166. juce_ImplementSingleton_SingleThreaded (InternalMessageQueue);
  167. //==============================================================================
  168. namespace LinuxErrorHandling
  169. {
  170. static bool errorOccurred = false;
  171. static bool keyboardBreakOccurred = false;
  172. static XErrorHandler oldErrorHandler = (XErrorHandler) 0;
  173. static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0;
  174. //==============================================================================
  175. // Usually happens when client-server connection is broken
  176. int ioErrorHandler (Display*)
  177. {
  178. DBG ("ERROR: connection to X server broken.. terminating.");
  179. if (JUCEApplicationBase::isStandaloneApp())
  180. MessageManager::getInstance()->stopDispatchLoop();
  181. errorOccurred = true;
  182. return 0;
  183. }
  184. int errorHandler (Display* display, XErrorEvent* event)
  185. {
  186. #if JUCE_DEBUG_XERRORS
  187. char errorStr[64] = { 0 };
  188. char requestStr[64] = { 0 };
  189. XGetErrorText (display, event->error_code, errorStr, 64);
  190. XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64);
  191. DBG ("ERROR: X returned " << errorStr << " for operation " << requestStr);
  192. #endif
  193. return 0;
  194. }
  195. void installXErrorHandlers()
  196. {
  197. oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler);
  198. oldErrorHandler = XSetErrorHandler (errorHandler);
  199. }
  200. void removeXErrorHandlers()
  201. {
  202. if (JUCEApplicationBase::isStandaloneApp())
  203. {
  204. XSetIOErrorHandler (oldIOErrorHandler);
  205. oldIOErrorHandler = 0;
  206. XSetErrorHandler (oldErrorHandler);
  207. oldErrorHandler = 0;
  208. }
  209. }
  210. //==============================================================================
  211. void keyboardBreakSignalHandler (int sig)
  212. {
  213. if (sig == SIGINT)
  214. keyboardBreakOccurred = true;
  215. }
  216. void installKeyboardBreakHandler()
  217. {
  218. struct sigaction saction;
  219. sigset_t maskSet;
  220. sigemptyset (&maskSet);
  221. saction.sa_handler = keyboardBreakSignalHandler;
  222. saction.sa_mask = maskSet;
  223. saction.sa_flags = 0;
  224. sigaction (SIGINT, &saction, 0);
  225. }
  226. }
  227. //==============================================================================
  228. void MessageManager::doPlatformSpecificInitialisation()
  229. {
  230. if (JUCEApplicationBase::isStandaloneApp())
  231. {
  232. // Initialise xlib for multiple thread support
  233. static bool initThreadCalled = false;
  234. if (! initThreadCalled)
  235. {
  236. if (! XInitThreads())
  237. {
  238. // This is fatal! Print error and closedown
  239. Logger::outputDebugString ("Failed to initialise xlib thread support.");
  240. Process::terminate();
  241. return;
  242. }
  243. initThreadCalled = true;
  244. }
  245. LinuxErrorHandling::installXErrorHandlers();
  246. LinuxErrorHandling::installKeyboardBreakHandler();
  247. }
  248. // Create the internal message queue
  249. InternalMessageQueue::getInstance();
  250. // Try to connect to a display
  251. String displayName (getenv ("DISPLAY"));
  252. if (displayName.isEmpty())
  253. displayName = ":0.0";
  254. display = XOpenDisplay (displayName.toUTF8());
  255. if (display != 0) // This is not fatal! we can run headless.
  256. {
  257. // Create a context to store user data associated with Windows we create
  258. windowHandleXContext = XUniqueContext();
  259. // We're only interested in client messages for this window, which are always sent
  260. XSetWindowAttributes swa;
  261. swa.event_mask = NoEventMask;
  262. // Create our message window (this will never be mapped)
  263. const int screen = DefaultScreen (display);
  264. juce_messageWindowHandle = XCreateWindow (display, RootWindow (display, screen),
  265. 0, 0, 1, 1, 0, 0, InputOnly,
  266. DefaultVisual (display, screen),
  267. CWEventMask, &swa);
  268. }
  269. }
  270. void MessageManager::doPlatformSpecificShutdown()
  271. {
  272. InternalMessageQueue::deleteInstance();
  273. if (display != 0 && ! LinuxErrorHandling::errorOccurred)
  274. {
  275. XDestroyWindow (display, juce_messageWindowHandle);
  276. XCloseDisplay (display);
  277. juce_messageWindowHandle = 0;
  278. display = nullptr;
  279. LinuxErrorHandling::removeXErrorHandlers();
  280. }
  281. }
  282. bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
  283. {
  284. if (LinuxErrorHandling::errorOccurred)
  285. return false;
  286. InternalMessageQueue::getInstanceWithoutCreating()->postMessage (message);
  287. return true;
  288. }
  289. void MessageManager::broadcastMessage (const String& /* value */)
  290. {
  291. /* TODO */
  292. }
  293. // this function expects that it will NEVER be called simultaneously for two concurrent threads
  294. bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
  295. {
  296. while (! LinuxErrorHandling::errorOccurred)
  297. {
  298. if (LinuxErrorHandling::keyboardBreakOccurred)
  299. {
  300. LinuxErrorHandling::errorOccurred = true;
  301. if (JUCEApplicationBase::isStandaloneApp())
  302. Process::terminate();
  303. break;
  304. }
  305. InternalMessageQueue* const queue = InternalMessageQueue::getInstanceWithoutCreating();
  306. jassert (queue != nullptr);
  307. if (queue->dispatchNextEvent())
  308. return true;
  309. if (returnIfNoPendingMessages)
  310. break;
  311. queue->sleepUntilEvent (2000);
  312. }
  313. return false;
  314. }