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.

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