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