From 1baaa016bd2ecc20a21d569fb662d650adb5b353 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Sun, 30 May 2010 12:56:29 +0100 Subject: [PATCH] Tidied up some linux messaging code. Fixed a couple of small graphic bugs. --- .../utility/jucer_FillTypePropertyComponent.h | 38 +- extras/juce demo/Source/demos/WidgetsDemo.cpp | 4 +- juce_amalgamated.cpp | 503 ++++++++---------- .../mouse/juce_DragAndDropContainer.cpp | 15 +- .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 4 +- .../drawables/juce_DrawableComposite.cpp | 2 +- src/native/linux/juce_linux_Messaging.cpp | 492 ++++++++--------- 7 files changed, 493 insertions(+), 565 deletions(-) diff --git a/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h b/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h index 2d53638a5d..bb6ce907c2 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h +++ b/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h @@ -202,7 +202,8 @@ private: //============================================================================== class GradientDesigner : public Component, public ChangeBroadcaster, - private ChangeListener + public ChangeListener, + public ButtonListener { public: GradientDesigner (const ColourGradient& gradient_) @@ -213,6 +214,19 @@ private: draggingPos (0) { addChildComponent (&colourPicker); + + linearButton.setButtonText ("Linear"); + linearButton.setRadioGroupId (321); + linearButton.setConnectedEdges (TextButton::ConnectedOnRight | TextButton::ConnectedOnLeft); + radialButton.setButtonText ("Radial"); + radialButton.setRadioGroupId (321); + radialButton.setConnectedEdges (TextButton::ConnectedOnRight | TextButton::ConnectedOnLeft); + + addAndMakeVisible (&linearButton); + addAndMakeVisible (&radialButton); + + linearButton.addButtonListener (this); + radialButton.addButtonListener (this); colourPicker.addChangeListener (this); } @@ -256,9 +270,14 @@ private: void resized() { - previewArea.setBounds (7, 8, getWidth() - 14, 24); - colourPicker.setBounds (0, previewArea.getBottom() + 8, - getWidth(), getHeight() - previewArea.getBottom() - 8); + previewArea.setBounds (7, 35, getWidth() - 14, 24); + + const int w = 60; + linearButton.setBounds (getWidth() / 2 - w, 2, w, 20); + radialButton.setBounds (getWidth() / 2, 2, w, 20); + + colourPicker.setBounds (0, previewArea.getBottom() + 16, + getWidth(), getHeight() - previewArea.getBottom() - 16); } void mouseDown (const MouseEvent& e) @@ -338,6 +357,9 @@ private: if (selectedPoint < 0) selectedPoint = 0; + linearButton.setToggleState (! gradient.isRadial, false); + radialButton.setToggleState (gradient.isRadial, false); + updatePicker(); sendChangeMessage (this); repaint(); @@ -364,6 +386,13 @@ private: } } + void buttonClicked (Button* b) + { + ColourGradient g (gradient); + g.isRadial = (b == &radialButton); + setGradient (g); + } + private: StoredSettings::ColourSelectorWithSwatches colourPicker; ColourGradient gradient; @@ -374,6 +403,7 @@ private: ColourGradient preDragGradient; Rectangle previewArea; + TextButton linearButton, radialButton; void updatePicker() { diff --git a/extras/juce demo/Source/demos/WidgetsDemo.cpp b/extras/juce demo/Source/demos/WidgetsDemo.cpp index 2ddbaef797..aa5a7653e4 100644 --- a/extras/juce demo/Source/demos/WidgetsDemo.cpp +++ b/extras/juce demo/Source/demos/WidgetsDemo.cpp @@ -863,14 +863,12 @@ private: for (int i = 0; i < icons.getNumEntries(); ++i) { - InputStream* svgFileStream = icons.createStreamForEntry (i); + ScopedPointer svgFileStream (icons.createStreamForEntry (i)); if (svgFileStream != 0) { iconNames.add (icons.getEntry(i)->filename); iconsFromZipFile.add (Drawable::createFromImageDataStream (*svgFileStream)); - - delete svgFileStream; } } } diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index fd169a6eed..480693849d 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -69610,12 +69610,11 @@ public: ddt->itemDragEnter (dragDescLocal, source, relPos.getX(), relPos.getY()); } - if (getCurrentlyOver() != 0 && getCurrentlyOver()->isInterestedInDragSource (dragDescLocal, source)) - getCurrentlyOver()->itemDragMove (dragDescLocal, source, relPos.getX(), relPos.getY()); + DragAndDropTarget* target = getCurrentlyOver(); + if (target != 0 && target->isInterestedInDragSource (dragDescLocal, source)) + target->itemDragMove (dragDescLocal, source, relPos.getX(), relPos.getY()); - if (getCurrentlyOver() == 0 - && canDoExternalDrag - && ! hasCheckedForExternalDrag) + if (getCurrentlyOver() == 0 && canDoExternalDrag && ! hasCheckedForExternalDrag) { if (Desktop::getInstance().findComponentAt (screenPos) == 0) { @@ -69807,11 +69806,7 @@ const String DragAndDropContainer::getCurrentDragDescription() const DragAndDropContainer* DragAndDropContainer::findParentDragContainerFor (Component* c) { - if (c == 0) - return 0; - - // (unable to use the syntax findParentComponentOfClass () because of a VC6 compiler bug) - return c->findParentComponentOfClass ((DragAndDropContainer*) 0); + return c == 0 ? 0 : c->findParentComponentOfClass ((DragAndDropContainer*) 0); } bool DragAndDropContainer::shouldDropFilesWhenDraggedExternally (const String&, Component*, StringArray&, bool&) @@ -83332,13 +83327,13 @@ public: else { Path p; - p.addRectangle (0.0f, 0.0f, (float) srcClip.getWidth(), (float) srcClip.getHeight()); + p.addRectangle (srcClip); SoftwareRendererClasses::ClipRegionBase::Ptr c (clip->clone()); c = c->clipToPath (p, transform); if (c != 0) - c->renderImageTransformed (destData, srcData, alpha, transform, betterQuality, true); + c->renderImageTransformed (destData, srcData, alpha, transform, betterQuality, false); } } @@ -84009,7 +84004,7 @@ DrawableComposite::DrawableComposite (const DrawableComposite& other) for (i = 0; i < 3; ++i) controlPoints[i] = other.controlPoints[i]; - for (i = 0; i < drawables.size(); ++i) + for (i = 0; i < other.drawables.size(); ++i) drawables.add (other.drawables.getUnchecked(i)->createCopy()); markersX.addCopiesOf (other.markersX); @@ -253994,7 +253989,8 @@ class InternalMessageQueue { public: InternalMessageQueue() - : bytesInSocket (0) + : bytesInSocket (0), + totalEventCount (0) { int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd); (void) ret; jassert (ret == 0); @@ -254007,6 +254003,8 @@ public: { close (fd[0]); close (fd[1]); + + clearSingletonInstance(); } void postMessage (Message* msg) @@ -254033,6 +254031,106 @@ public: return queue.size() == 0; } + bool dispatchNextEvent() + { + // This alternates between giving priority to XEvents or internal messages, + // to keep everything running smoothly.. + if ((++totalEventCount & 1) != 0) + return dispatchNextXEvent() || dispatchNextInternalMessage(); + else + return dispatchNextInternalMessage() || dispatchNextXEvent(); + } + + // Wait for an event (either XEvent, or an internal Message) + bool sleepUntilEvent (const int timeoutMs) + { + if (! isEmpty()) + return true; + + if (display != 0) + { + ScopedXLock xlock; + if (XPending (display)) + return true; + } + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = timeoutMs * 1000; + int fd0 = getWaitHandle(); + int fdmax = fd0; + + fd_set readset; + FD_ZERO (&readset); + FD_SET (fd0, &readset); + + if (display != 0) + { + ScopedXLock xlock; + int fd1 = XConnectionNumber (display); + FD_SET (fd1, &readset); + fdmax = jmax (fd0, fd1); + } + + const int ret = select (fdmax + 1, &readset, 0, 0, &tv); + return (ret > 0); // ret <= 0 if error or timeout + } + + struct MessageThreadFuncCall + { + enum { uniqueID = 0x73774623 }; + + MessageCallbackFunction* func; + void* parameter; + void* result; + CriticalSection lock; + WaitableEvent event; + }; + + juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue); + +private: + CriticalSection lock; + OwnedArray queue; + int fd[2]; + int bytesInSocket; + int totalEventCount; + + int getWaitHandle() const throw() { return fd[1]; } + + static bool setNonBlocking (int handle) + { + int socketFlags = fcntl (handle, F_GETFL, 0); + if (socketFlags == -1) + return false; + + socketFlags |= O_NONBLOCK; + return fcntl (handle, F_SETFL, socketFlags) == 0; + } + + static bool dispatchNextXEvent() + { + if (display == 0) + return false; + + XEvent evt; + + { + ScopedXLock xlock; + if (! XPending (display)) + return false; + + XNextEvent (display, &evt); + } + + if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle) + juce_handleSelectionRequest (evt.xselectionrequest); + else if (evt.xany.window != juce_messageWindowHandle) + juce_windowMessageReceive (&evt); + + return true; + } + Message* popNextMessage() { ScopedLock sl (lock); @@ -254052,94 +254150,99 @@ public: return m; } - int getWaitHandle() const { return fd[1]; } - -private: - CriticalSection lock; - OwnedArray queue; - int fd[2]; - int bytesInSocket; - - static bool setNonBlocking (int handle) + bool dispatchNextInternalMessage() { - int socketFlags = fcntl (handle, F_GETFL, 0); + ScopedPointer msg (popNextMessage()); - if (socketFlags == -1) + if (msg == 0) return false; - socketFlags |= O_NONBLOCK; + if (msg->intParameter1 == MessageThreadFuncCall::uniqueID) + { + // Handle callback message + MessageThreadFuncCall* const call = (MessageThreadFuncCall*) msg->pointerParameter; - return fcntl (handle, F_SETFL, socketFlags) == 0; + call->result = (*(call->func)) (call->parameter); + call->event.signal(); + } + else + { + // Handle "normal" messages + MessageManager::getInstance()->deliverMessage (msg.release()); + } + + return true; } }; -static InternalMessageQueue* juce_internalMessageQueue = 0; +juce_ImplementSingleton_SingleThreaded (InternalMessageQueue); -// error handling in X11 -static bool errorOccurred = false; -static bool keyboardBreakOccurred = false; -static XErrorHandler oldErrorHandler = (XErrorHandler) 0; -static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0; - -// Usually happens when client-server connection is broken -static int ioErrorHandler (Display* display) +namespace LinuxErrorHandling { - DBG ("ERROR: connection to X server broken.. terminating."); - errorOccurred = true; + static bool errorOccurred = false; + static bool keyboardBreakOccurred = false; + static XErrorHandler oldErrorHandler = (XErrorHandler) 0; + static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0; - if (JUCEApplication::getInstance() != 0) - Process::terminate(); - - return 0; -} - -// A protocol error has occurred -static int errorHandler (Display* display, XErrorEvent* event) -{ -#if JUCE_DEBUG_XERRORS - char errorStr[64] = { 0 }; - char requestStr[64] = { 0 }; - - XGetErrorText (display, event->error_code, errorStr, 64); - - XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toCString(), - "Unknown", requestStr, 64); - - DBG ("ERROR: X returned " + String (errorStr) + " for operation " + String (requestStr)); -#endif - - return 0; -} - -// Breakin from keyboard -static void signalHandler (int sig) -{ - if (sig == SIGINT) + // Usually happens when client-server connection is broken + static int ioErrorHandler (Display* display) { - keyboardBreakOccurred = true; - return; - } - - static bool reentrant = false; - - if (! reentrant) - { - reentrant = true; - - // Illegal instruction - fflush (stdout); - Logger::outputDebugString ("ERROR: Program executed illegal instruction.. terminating"); + DBG ("ERROR: connection to X server broken.. terminating."); errorOccurred = true; if (JUCEApplication::getInstance() != 0) Process::terminate(); + + return 0; } - else + + // A protocol error has occurred + static int juce_XErrorHandler (Display* display, XErrorEvent* event) { - if (JUCEApplication::getInstance() != 0) - exit(0); + #if JUCE_DEBUG_XERRORS + char errorStr[64] = { 0 }; + char requestStr[64] = { 0 }; + + XGetErrorText (display, event->error_code, errorStr, 64); + XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toCString(), "Unknown", requestStr, 64); + DBG ("ERROR: X returned " + String (errorStr) + " for operation " + String (requestStr)); + #endif + + return 0; + } + + static void installXErrorHandlers() + { + oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler); + oldErrorHandler = XSetErrorHandler (juce_XErrorHandler); + } + + static void removeXErrorHandlers() + { + XSetIOErrorHandler (oldIOErrorHandler); + oldIOErrorHandler = 0; + + XSetErrorHandler (oldErrorHandler); + oldErrorHandler = 0; + } + + static void keyboardBreakSignalHandler (int sig) + { + if (sig == SIGINT) + keyboardBreakOccurred = true; + } + + static void installKeyboardBreakHandler() + { + struct sigaction saction; + sigset_t maskSet; + sigemptyset (&maskSet); + saction.sa_handler = keyboardBreakSignalHandler; + saction.sa_mask = maskSet; + saction.sa_flags = 0; + sigaction (SIGINT, &saction, 0); } } @@ -254164,32 +254267,11 @@ void MessageManager::doPlatformSpecificInitialisation() initThreadCalled = true; } - // This is called if the client/server connection is broken - oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler); - - // This is called if a protocol error occurs - oldErrorHandler = XSetErrorHandler (errorHandler); - - // Install signal handler for break-in - struct sigaction saction; - sigset_t maskSet; - sigemptyset (&maskSet); - saction.sa_handler = signalHandler; - saction.sa_mask = maskSet; - saction.sa_flags = 0; - sigaction (SIGINT, &saction, 0); - -#ifndef _DEBUG - // Setup signal handlers for various fatal errors - sigaction (SIGILL, &saction, 0); - sigaction (SIGBUS, &saction, 0); - sigaction (SIGFPE, &saction, 0); - sigaction (SIGSEGV, &saction, 0); - sigaction (SIGSYS, &saction, 0); -#endif + LinuxErrorHandling::installXErrorHandlers(); + LinuxErrorHandling::installKeyboardBreakHandler(); // Create the internal message queue - juce_internalMessageQueue = new InternalMessageQueue(); + InternalMessageQueue::getInstance(); // Try to connect to a display String displayName (getenv ("DISPLAY")); @@ -254198,59 +254280,46 @@ void MessageManager::doPlatformSpecificInitialisation() display = XOpenDisplay (displayName.toCString()); - if (display == 0) + if (display != 0) // This is not fatal! we can run headless. { - // This is not fatal! we can run headless. - return; + // Create a context to store user data associated with Windows we create in WindowDriver + windowHandleXContext = XUniqueContext(); + + // We're only interested in client messages for this window, which are always sent + XSetWindowAttributes swa; + swa.event_mask = NoEventMask; + + // Create our message window (this will never be mapped) + const int screen = DefaultScreen (display); + juce_messageWindowHandle = XCreateWindow (display, RootWindow (display, screen), + 0, 0, 1, 1, 0, 0, InputOnly, + DefaultVisual (display, screen), + CWEventMask, &swa); } - - // Get defaults for various properties - int screen = DefaultScreen (display); - Window root = RootWindow (display, screen); - Visual* visual = DefaultVisual (display, screen); - - // Create a context to store user data associated with Windows we - // create in WindowDriver - windowHandleXContext = XUniqueContext(); - - // We're only interested in client messages for this window - // which are always sent - XSetWindowAttributes swa; - swa.event_mask = NoEventMask; - - // Create our message window (this will never be mapped) - juce_messageWindowHandle = XCreateWindow (display, root, - 0, 0, 1, 1, 0, 0, InputOnly, - visual, CWEventMask, &swa); } void MessageManager::doPlatformSpecificShutdown() { - deleteAndZero (juce_internalMessageQueue); + InternalMessageQueue::deleteInstance(); - if (display != 0 && ! errorOccurred) + if (display != 0 && ! LinuxErrorHandling::errorOccurred) { XDestroyWindow (display, juce_messageWindowHandle); XCloseDisplay (display); - // reset pointers juce_messageWindowHandle = 0; display = 0; - // Restore original error handlers - XSetIOErrorHandler (oldIOErrorHandler); - oldIOErrorHandler = 0; - XSetErrorHandler (oldErrorHandler); - oldErrorHandler = 0; + LinuxErrorHandling::removeXErrorHandlers(); } } bool juce_postMessageToSystemQueue (void* message) { - if (errorOccurred) + if (LinuxErrorHandling::errorOccurred) return false; - juce_internalMessageQueue->postMessage ((Message*) message); + InternalMessageQueue::getInstanceWithoutCreating()->postMessage ((Message*) message); return true; } @@ -254259,144 +254328,37 @@ void MessageManager::broadcastMessage (const String& value) throw() /* TODO */ } -struct MessageThreadFuncCall -{ - enum { uniqueID = 0x73774623 }; - - MessageCallbackFunction* func; - void* parameter; - void* result; - CriticalSection lock; - WaitableEvent event; -}; - void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, void* parameter) { - if (errorOccurred) + if (LinuxErrorHandling::errorOccurred) return 0; - if (! isThisTheMessageThread()) - { - MessageThreadFuncCall messageCallContext; - messageCallContext.func = func; - messageCallContext.parameter = parameter; - - juce_internalMessageQueue->postMessage (new Message (MessageThreadFuncCall::uniqueID, - 0, 0, &messageCallContext)); - - // Wait for it to complete before continuing - messageCallContext.event.wait(); - - return messageCallContext.result; - } - else - { - // Just call the function directly + if (isThisTheMessageThread()) return func (parameter); - } -} -// Wait for an event (either XEvent, or an internal Message) -static bool juce_sleepUntilEvent (const int timeoutMs) -{ - if (! juce_internalMessageQueue->isEmpty()) - return true; + InternalMessageQueue::MessageThreadFuncCall messageCallContext; + messageCallContext.func = func; + messageCallContext.parameter = parameter; - if (display != 0) - { - ScopedXLock xlock; - if (XPending (display)) - return true; - } + InternalMessageQueue::getInstanceWithoutCreating() + ->postMessage (new Message (InternalMessageQueue::MessageThreadFuncCall::uniqueID, + 0, 0, &messageCallContext)); - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = timeoutMs * 1000; - int fd0 = juce_internalMessageQueue->getWaitHandle(); - int fdmax = fd0; + // Wait for it to complete before continuing + messageCallContext.event.wait(); - fd_set readset; - FD_ZERO (&readset); - FD_SET (fd0, &readset); - - if (display != 0) - { - ScopedXLock xlock; - int fd1 = XConnectionNumber (display); - FD_SET (fd1, &readset); - fdmax = jmax (fd0, fd1); - } - - const int ret = select (fdmax + 1, &readset, 0, 0, &tv); - return (ret > 0); // ret <= 0 if error or timeout -} - -// Handle next XEvent (if any) -static bool juce_dispatchNextXEvent() -{ - if (display == 0) - return false; - - XEvent evt; - - { - ScopedXLock xlock; - - if (! XPending (display)) - return false; - - XNextEvent (display, &evt); - } - - if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle) - { - juce_handleSelectionRequest (evt.xselectionrequest); - } - else if (evt.xany.window != juce_messageWindowHandle) - { - juce_windowMessageReceive (&evt); - } - - return true; -} - -// Handle next internal Message (if any) -static bool juce_dispatchNextInternalMessage() -{ - ScopedPointer msg (juce_internalMessageQueue->popNextMessage()); - - if (msg == 0) - return false; - - if (msg->intParameter1 == MessageThreadFuncCall::uniqueID) - { - // Handle callback message - MessageThreadFuncCall* const call = (MessageThreadFuncCall*) msg->pointerParameter; - - call->result = (*(call->func)) (call->parameter); - call->event.signal(); - } - else - { - // Handle "normal" messages - MessageManager::getInstance()->deliverMessage (msg.release()); - } - - return true; + return messageCallContext.result; } // this function expects that it will NEVER be called simultaneously for two concurrent threads bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) { - for (;;) + while (! LinuxErrorHandling::errorOccurred) { - if (errorOccurred) - break; - - if (keyboardBreakOccurred) + if (LinuxErrorHandling::keyboardBreakOccurred) { - errorOccurred = true; + LinuxErrorHandling::errorOccurred = true; if (JUCEApplication::getInstance() != 0) Process::terminate(); @@ -254404,28 +254366,13 @@ bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) break; } - static int totalEventCount = 0; - ++totalEventCount; + if (InternalMessageQueue::getInstanceWithoutCreating()->dispatchNextEvent()) + return true; - // The purpose here is to give either priority to XEvents or - // to internal messages This is necessary to keep a "good" - // behaviour when the cpu is overloaded - if (totalEventCount & 1) - { - if (juce_dispatchNextXEvent() || juce_dispatchNextInternalMessage()) - return true; - } - else - { - if (juce_dispatchNextInternalMessage() || juce_dispatchNextXEvent()) - return true; - } - - if (returnIfNoPendingMessages) // early exit + if (returnIfNoPendingMessages) break; - // the timeout is to be on the safe side, but it does not seem to be useful - juce_sleepUntilEvent (2000); + InternalMessageQueue::getInstanceWithoutCreating()->sleepUntilEvent (2000); } return false; diff --git a/src/gui/components/mouse/juce_DragAndDropContainer.cpp b/src/gui/components/mouse/juce_DragAndDropContainer.cpp index a51e2f8c1f..88a8542a86 100644 --- a/src/gui/components/mouse/juce_DragAndDropContainer.cpp +++ b/src/gui/components/mouse/juce_DragAndDropContainer.cpp @@ -227,12 +227,11 @@ public: ddt->itemDragEnter (dragDescLocal, source, relPos.getX(), relPos.getY()); } - if (getCurrentlyOver() != 0 && getCurrentlyOver()->isInterestedInDragSource (dragDescLocal, source)) - getCurrentlyOver()->itemDragMove (dragDescLocal, source, relPos.getX(), relPos.getY()); + DragAndDropTarget* target = getCurrentlyOver(); + if (target != 0 && target->isInterestedInDragSource (dragDescLocal, source)) + target->itemDragMove (dragDescLocal, source, relPos.getX(), relPos.getY()); - if (getCurrentlyOver() == 0 - && canDoExternalDrag - && ! hasCheckedForExternalDrag) + if (getCurrentlyOver() == 0 && canDoExternalDrag && ! hasCheckedForExternalDrag) { if (Desktop::getInstance().findComponentAt (screenPos) == 0) { @@ -426,11 +425,7 @@ const String DragAndDropContainer::getCurrentDragDescription() const DragAndDropContainer* DragAndDropContainer::findParentDragContainerFor (Component* c) { - if (c == 0) - return 0; - - // (unable to use the syntax findParentComponentOfClass () because of a VC6 compiler bug) - return c->findParentComponentOfClass ((DragAndDropContainer*) 0); + return c == 0 ? 0 : c->findParentComponentOfClass ((DragAndDropContainer*) 0); } bool DragAndDropContainer::shouldDropFilesWhenDraggedExternally (const String&, Component*, StringArray&, bool&) diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index 25937be6d4..ad6c62938e 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -1983,13 +1983,13 @@ public: else { Path p; - p.addRectangle (0.0f, 0.0f, (float) srcClip.getWidth(), (float) srcClip.getHeight()); + p.addRectangle (srcClip); SoftwareRendererClasses::ClipRegionBase::Ptr c (clip->clone()); c = c->clipToPath (p, transform); if (c != 0) - c->renderImageTransformed (destData, srcData, alpha, transform, betterQuality, true); + c->renderImageTransformed (destData, srcData, alpha, transform, betterQuality, false); } } diff --git a/src/gui/graphics/drawables/juce_DrawableComposite.cpp b/src/gui/graphics/drawables/juce_DrawableComposite.cpp index 061ce52a67..ebf6eb5e71 100644 --- a/src/gui/graphics/drawables/juce_DrawableComposite.cpp +++ b/src/gui/graphics/drawables/juce_DrawableComposite.cpp @@ -47,7 +47,7 @@ DrawableComposite::DrawableComposite (const DrawableComposite& other) for (i = 0; i < 3; ++i) controlPoints[i] = other.controlPoints[i]; - for (i = 0; i < drawables.size(); ++i) + for (i = 0; i < other.drawables.size(); ++i) drawables.add (other.drawables.getUnchecked(i)->createCopy()); markersX.addCopiesOf (other.markersX); diff --git a/src/native/linux/juce_linux_Messaging.cpp b/src/native/linux/juce_linux_Messaging.cpp index ed75dfc232..dc518e893e 100644 --- a/src/native/linux/juce_linux_Messaging.cpp +++ b/src/native/linux/juce_linux_Messaging.cpp @@ -48,7 +48,8 @@ class InternalMessageQueue { public: InternalMessageQueue() - : bytesInSocket (0) + : bytesInSocket (0), + totalEventCount (0) { int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd); (void) ret; jassert (ret == 0); @@ -61,8 +62,11 @@ public: { close (fd[0]); close (fd[1]); + + clearSingletonInstance(); } + //============================================================================== void postMessage (Message* msg) { const int maxBytesInSocketQueue = 128; @@ -87,6 +91,108 @@ public: return queue.size() == 0; } + bool dispatchNextEvent() + { + // This alternates between giving priority to XEvents or internal messages, + // to keep everything running smoothly.. + if ((++totalEventCount & 1) != 0) + return dispatchNextXEvent() || dispatchNextInternalMessage(); + else + return dispatchNextInternalMessage() || dispatchNextXEvent(); + } + + // Wait for an event (either XEvent, or an internal Message) + bool sleepUntilEvent (const int timeoutMs) + { + if (! isEmpty()) + return true; + + if (display != 0) + { + ScopedXLock xlock; + if (XPending (display)) + return true; + } + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = timeoutMs * 1000; + int fd0 = getWaitHandle(); + int fdmax = fd0; + + fd_set readset; + FD_ZERO (&readset); + FD_SET (fd0, &readset); + + if (display != 0) + { + ScopedXLock xlock; + int fd1 = XConnectionNumber (display); + FD_SET (fd1, &readset); + fdmax = jmax (fd0, fd1); + } + + const int ret = select (fdmax + 1, &readset, 0, 0, &tv); + return (ret > 0); // ret <= 0 if error or timeout + } + + //============================================================================== + struct MessageThreadFuncCall + { + enum { uniqueID = 0x73774623 }; + + MessageCallbackFunction* func; + void* parameter; + void* result; + CriticalSection lock; + WaitableEvent event; + }; + + //============================================================================== + juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue); + +private: + CriticalSection lock; + OwnedArray queue; + int fd[2]; + int bytesInSocket; + int totalEventCount; + + int getWaitHandle() const throw() { return fd[1]; } + + static bool setNonBlocking (int handle) + { + int socketFlags = fcntl (handle, F_GETFL, 0); + if (socketFlags == -1) + return false; + + socketFlags |= O_NONBLOCK; + return fcntl (handle, F_SETFL, socketFlags) == 0; + } + + static bool dispatchNextXEvent() + { + if (display == 0) + return false; + + XEvent evt; + + { + ScopedXLock xlock; + if (! XPending (display)) + return false; + + XNextEvent (display, &evt); + } + + if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle) + juce_handleSelectionRequest (evt.xselectionrequest); + else if (evt.xany.window != juce_messageWindowHandle) + juce_windowMessageReceive (&evt); + + return true; + } + Message* popNextMessage() { ScopedLock sl (lock); @@ -106,95 +212,103 @@ public: return m; } - int getWaitHandle() const { return fd[1]; } - -private: - CriticalSection lock; - OwnedArray queue; - int fd[2]; - int bytesInSocket; - - static bool setNonBlocking (int handle) + bool dispatchNextInternalMessage() { - int socketFlags = fcntl (handle, F_GETFL, 0); + ScopedPointer msg (popNextMessage()); - if (socketFlags == -1) + if (msg == 0) return false; - socketFlags |= O_NONBLOCK; + if (msg->intParameter1 == MessageThreadFuncCall::uniqueID) + { + // Handle callback message + MessageThreadFuncCall* const call = (MessageThreadFuncCall*) msg->pointerParameter; - return fcntl (handle, F_SETFL, socketFlags) == 0; + call->result = (*(call->func)) (call->parameter); + call->event.signal(); + } + else + { + // Handle "normal" messages + MessageManager::getInstance()->deliverMessage (msg.release()); + } + + return true; } }; +juce_ImplementSingleton_SingleThreaded (InternalMessageQueue); + + //============================================================================== -static InternalMessageQueue* juce_internalMessageQueue = 0; - -// error handling in X11 -static bool errorOccurred = false; -static bool keyboardBreakOccurred = false; -static XErrorHandler oldErrorHandler = (XErrorHandler) 0; -static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0; - -// Usually happens when client-server connection is broken -static int ioErrorHandler (Display* display) +namespace LinuxErrorHandling { - DBG ("ERROR: connection to X server broken.. terminating."); + //============================================================================== + static bool errorOccurred = false; + static bool keyboardBreakOccurred = false; + static XErrorHandler oldErrorHandler = (XErrorHandler) 0; + static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0; - errorOccurred = true; - - if (JUCEApplication::getInstance() != 0) - Process::terminate(); - - return 0; -} - -// A protocol error has occurred -static int errorHandler (Display* display, XErrorEvent* event) -{ -#if JUCE_DEBUG_XERRORS - char errorStr[64] = { 0 }; - char requestStr[64] = { 0 }; - - XGetErrorText (display, event->error_code, errorStr, 64); - - XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toCString(), - "Unknown", requestStr, 64); - - DBG ("ERROR: X returned " + String (errorStr) + " for operation " + String (requestStr)); -#endif - - return 0; -} - -// Breakin from keyboard -static void signalHandler (int sig) -{ - if (sig == SIGINT) + //============================================================================== + // Usually happens when client-server connection is broken + static int ioErrorHandler (Display* display) { - keyboardBreakOccurred = true; - return; - } - - static bool reentrant = false; - - if (! reentrant) - { - reentrant = true; - - // Illegal instruction - fflush (stdout); - Logger::outputDebugString ("ERROR: Program executed illegal instruction.. terminating"); + DBG ("ERROR: connection to X server broken.. terminating."); errorOccurred = true; if (JUCEApplication::getInstance() != 0) Process::terminate(); + + return 0; } - else + + // A protocol error has occurred + static int juce_XErrorHandler (Display* display, XErrorEvent* event) { - if (JUCEApplication::getInstance() != 0) - exit(0); + #if JUCE_DEBUG_XERRORS + char errorStr[64] = { 0 }; + char requestStr[64] = { 0 }; + + XGetErrorText (display, event->error_code, errorStr, 64); + XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toCString(), "Unknown", requestStr, 64); + DBG ("ERROR: X returned " + String (errorStr) + " for operation " + String (requestStr)); + #endif + + return 0; + } + + static void installXErrorHandlers() + { + oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler); + oldErrorHandler = XSetErrorHandler (juce_XErrorHandler); + } + + static void removeXErrorHandlers() + { + XSetIOErrorHandler (oldIOErrorHandler); + oldIOErrorHandler = 0; + + XSetErrorHandler (oldErrorHandler); + oldErrorHandler = 0; + } + + //============================================================================== + static void keyboardBreakSignalHandler (int sig) + { + if (sig == SIGINT) + keyboardBreakOccurred = true; + } + + static void installKeyboardBreakHandler() + { + struct sigaction saction; + sigset_t maskSet; + sigemptyset (&maskSet); + saction.sa_handler = keyboardBreakSignalHandler; + saction.sa_mask = maskSet; + saction.sa_flags = 0; + sigaction (SIGINT, &saction, 0); } } @@ -220,32 +334,11 @@ void MessageManager::doPlatformSpecificInitialisation() initThreadCalled = true; } - // This is called if the client/server connection is broken - oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler); - - // This is called if a protocol error occurs - oldErrorHandler = XSetErrorHandler (errorHandler); - - // Install signal handler for break-in - struct sigaction saction; - sigset_t maskSet; - sigemptyset (&maskSet); - saction.sa_handler = signalHandler; - saction.sa_mask = maskSet; - saction.sa_flags = 0; - sigaction (SIGINT, &saction, 0); - -#ifndef _DEBUG - // Setup signal handlers for various fatal errors - sigaction (SIGILL, &saction, 0); - sigaction (SIGBUS, &saction, 0); - sigaction (SIGFPE, &saction, 0); - sigaction (SIGSEGV, &saction, 0); - sigaction (SIGSYS, &saction, 0); -#endif + LinuxErrorHandling::installXErrorHandlers(); + LinuxErrorHandling::installKeyboardBreakHandler(); // Create the internal message queue - juce_internalMessageQueue = new InternalMessageQueue(); + InternalMessageQueue::getInstance(); // Try to connect to a display String displayName (getenv ("DISPLAY")); @@ -254,59 +347,46 @@ void MessageManager::doPlatformSpecificInitialisation() display = XOpenDisplay (displayName.toCString()); - if (display == 0) + if (display != 0) // This is not fatal! we can run headless. { - // This is not fatal! we can run headless. - return; + // Create a context to store user data associated with Windows we create in WindowDriver + windowHandleXContext = XUniqueContext(); + + // We're only interested in client messages for this window, which are always sent + XSetWindowAttributes swa; + swa.event_mask = NoEventMask; + + // Create our message window (this will never be mapped) + const int screen = DefaultScreen (display); + juce_messageWindowHandle = XCreateWindow (display, RootWindow (display, screen), + 0, 0, 1, 1, 0, 0, InputOnly, + DefaultVisual (display, screen), + CWEventMask, &swa); } - - // Get defaults for various properties - int screen = DefaultScreen (display); - Window root = RootWindow (display, screen); - Visual* visual = DefaultVisual (display, screen); - - // Create a context to store user data associated with Windows we - // create in WindowDriver - windowHandleXContext = XUniqueContext(); - - // We're only interested in client messages for this window - // which are always sent - XSetWindowAttributes swa; - swa.event_mask = NoEventMask; - - // Create our message window (this will never be mapped) - juce_messageWindowHandle = XCreateWindow (display, root, - 0, 0, 1, 1, 0, 0, InputOnly, - visual, CWEventMask, &swa); } void MessageManager::doPlatformSpecificShutdown() { - deleteAndZero (juce_internalMessageQueue); + InternalMessageQueue::deleteInstance(); - if (display != 0 && ! errorOccurred) + if (display != 0 && ! LinuxErrorHandling::errorOccurred) { XDestroyWindow (display, juce_messageWindowHandle); XCloseDisplay (display); - // reset pointers juce_messageWindowHandle = 0; display = 0; - // Restore original error handlers - XSetIOErrorHandler (oldIOErrorHandler); - oldIOErrorHandler = 0; - XSetErrorHandler (oldErrorHandler); - oldErrorHandler = 0; + LinuxErrorHandling::removeXErrorHandlers(); } } bool juce_postMessageToSystemQueue (void* message) { - if (errorOccurred) + if (LinuxErrorHandling::errorOccurred) return false; - juce_internalMessageQueue->postMessage ((Message*) message); + InternalMessageQueue::getInstanceWithoutCreating()->postMessage ((Message*) message); return true; } @@ -315,144 +395,37 @@ void MessageManager::broadcastMessage (const String& value) throw() /* TODO */ } -struct MessageThreadFuncCall -{ - enum { uniqueID = 0x73774623 }; - - MessageCallbackFunction* func; - void* parameter; - void* result; - CriticalSection lock; - WaitableEvent event; -}; - void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, void* parameter) { - if (errorOccurred) + if (LinuxErrorHandling::errorOccurred) return 0; - if (! isThisTheMessageThread()) - { - MessageThreadFuncCall messageCallContext; - messageCallContext.func = func; - messageCallContext.parameter = parameter; - - juce_internalMessageQueue->postMessage (new Message (MessageThreadFuncCall::uniqueID, - 0, 0, &messageCallContext)); - - // Wait for it to complete before continuing - messageCallContext.event.wait(); - - return messageCallContext.result; - } - else - { - // Just call the function directly + if (isThisTheMessageThread()) return func (parameter); - } -} -// Wait for an event (either XEvent, or an internal Message) -static bool juce_sleepUntilEvent (const int timeoutMs) -{ - if (! juce_internalMessageQueue->isEmpty()) - return true; + InternalMessageQueue::MessageThreadFuncCall messageCallContext; + messageCallContext.func = func; + messageCallContext.parameter = parameter; - if (display != 0) - { - ScopedXLock xlock; - if (XPending (display)) - return true; - } + InternalMessageQueue::getInstanceWithoutCreating() + ->postMessage (new Message (InternalMessageQueue::MessageThreadFuncCall::uniqueID, + 0, 0, &messageCallContext)); - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = timeoutMs * 1000; - int fd0 = juce_internalMessageQueue->getWaitHandle(); - int fdmax = fd0; + // Wait for it to complete before continuing + messageCallContext.event.wait(); - fd_set readset; - FD_ZERO (&readset); - FD_SET (fd0, &readset); - - if (display != 0) - { - ScopedXLock xlock; - int fd1 = XConnectionNumber (display); - FD_SET (fd1, &readset); - fdmax = jmax (fd0, fd1); - } - - const int ret = select (fdmax + 1, &readset, 0, 0, &tv); - return (ret > 0); // ret <= 0 if error or timeout -} - -// Handle next XEvent (if any) -static bool juce_dispatchNextXEvent() -{ - if (display == 0) - return false; - - XEvent evt; - - { - ScopedXLock xlock; - - if (! XPending (display)) - return false; - - XNextEvent (display, &evt); - } - - if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle) - { - juce_handleSelectionRequest (evt.xselectionrequest); - } - else if (evt.xany.window != juce_messageWindowHandle) - { - juce_windowMessageReceive (&evt); - } - - return true; -} - -// Handle next internal Message (if any) -static bool juce_dispatchNextInternalMessage() -{ - ScopedPointer msg (juce_internalMessageQueue->popNextMessage()); - - if (msg == 0) - return false; - - if (msg->intParameter1 == MessageThreadFuncCall::uniqueID) - { - // Handle callback message - MessageThreadFuncCall* const call = (MessageThreadFuncCall*) msg->pointerParameter; - - call->result = (*(call->func)) (call->parameter); - call->event.signal(); - } - else - { - // Handle "normal" messages - MessageManager::getInstance()->deliverMessage (msg.release()); - } - - return true; + return messageCallContext.result; } // this function expects that it will NEVER be called simultaneously for two concurrent threads bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) { - for (;;) + while (! LinuxErrorHandling::errorOccurred) { - if (errorOccurred) - break; - - if (keyboardBreakOccurred) + if (LinuxErrorHandling::keyboardBreakOccurred) { - errorOccurred = true; + LinuxErrorHandling::errorOccurred = true; if (JUCEApplication::getInstance() != 0) Process::terminate(); @@ -460,28 +433,13 @@ bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) break; } - static int totalEventCount = 0; - ++totalEventCount; + if (InternalMessageQueue::getInstanceWithoutCreating()->dispatchNextEvent()) + return true; - // The purpose here is to give either priority to XEvents or - // to internal messages This is necessary to keep a "good" - // behaviour when the cpu is overloaded - if (totalEventCount & 1) - { - if (juce_dispatchNextXEvent() || juce_dispatchNextInternalMessage()) - return true; - } - else - { - if (juce_dispatchNextInternalMessage() || juce_dispatchNextXEvent()) - return true; - } - - if (returnIfNoPendingMessages) // early exit + if (returnIfNoPendingMessages) break; - // the timeout is to be on the safe side, but it does not seem to be useful - juce_sleepUntilEvent (2000); + InternalMessageQueue::getInstanceWithoutCreating()->sleepUntilEvent (2000); } return false;