| @@ -41,6 +41,7 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "../core/juce_Random.h" | |||
| #include "../io/network/juce_URL.h" | |||
| #include "../io/files/juce_NamedPipe.h" | |||
| #include "../io/streams/juce_MemoryOutputStream.h" | |||
| #include "../threads/juce_InterProcessLock.h" | |||
| #include "../audio/devices/juce_AudioIODeviceType.h" | |||
| #include "../threads/juce_Thread.h" | |||
| @@ -100,6 +101,7 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "linux/juce_linux_Threads.cpp" | |||
| #if ! JUCE_ONLY_BUILD_CORE_LIBRARY | |||
| #include "linux/juce_linux_Clipboard.cpp" | |||
| #include "linux/juce_linux_Messaging.cpp" | |||
| #include "linux/juce_linux_Fonts.cpp" | |||
| #include "linux/juce_linux_Windowing.cpp" | |||
| @@ -0,0 +1,266 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 by Raw Material Software Ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the GNU General | |||
| Public License (Version 2), as published by the Free Software Foundation. | |||
| A copy of the license is included in the JUCE distribution, or can be found | |||
| online at www.gnu.org/licenses. | |||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.rawmaterialsoftware.com/juce for more information. | |||
| ============================================================================== | |||
| */ | |||
| // (This file gets included by juce_linux_NativeCode.cpp, rather than being | |||
| // compiled on its own). | |||
| #if JUCE_INCLUDED_FILE | |||
| //============================================================================== | |||
| #ifdef JUCE_DEBUG | |||
| #define JUCE_DEBUG_XERRORS 1 | |||
| #endif | |||
| extern Display* display; | |||
| extern Window juce_messageWindowHandle; | |||
| static String localClipboardContent; | |||
| static Atom atom_UTF8_STRING; | |||
| static Atom atom_CLIPBOARD; | |||
| static Atom atom_TARGETS; | |||
| //============================================================================== | |||
| static void initSelectionAtoms() | |||
| { | |||
| static bool isInitialised = false; | |||
| if (! isInitialised) | |||
| { | |||
| atom_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False); | |||
| atom_CLIPBOARD = XInternAtom (display, "CLIPBOARD", False); | |||
| atom_TARGETS = XInternAtom (display, "TARGETS", False); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| // Read the content of a window property as either a locale-dependent string or an utf8 string | |||
| // works only for strings shorter than 1000000 bytes | |||
| static String juce_readWindowProperty (Window window, Atom prop, | |||
| Atom fmt, // XA_STRING or UTF8_STRING | |||
| bool deleteAfterReading) | |||
| { | |||
| String returnData; | |||
| uint8 *clipData; | |||
| Atom actualType; | |||
| int actualFormat; | |||
| unsigned long numItems, bytesLeft; | |||
| if (XGetWindowProperty (display, window, prop, | |||
| 0L /* offset */, 1000000 /* length (max) */, False, | |||
| AnyPropertyType /* format */, | |||
| &actualType, &actualFormat, &numItems, &bytesLeft, | |||
| &clipData) == Success) | |||
| { | |||
| if (actualType == atom_UTF8_STRING && actualFormat == 8) | |||
| { | |||
| returnData = String::fromUTF8 (clipData, numItems); | |||
| } | |||
| else if (actualType == XA_STRING && actualFormat == 8) | |||
| { | |||
| returnData = String ((const char*) clipData, numItems); | |||
| } | |||
| if (clipData != 0) | |||
| XFree (clipData); | |||
| jassert (bytesLeft == 0 || numItems == 1000000); | |||
| } | |||
| if (deleteAfterReading) | |||
| XDeleteProperty (display, window, prop); | |||
| return returnData; | |||
| } | |||
| //============================================================================== | |||
| // Send a SelectionRequest to the window owning the selection and waits for its answer (with a timeout) */ | |||
| static bool juce_requestSelectionContent (String &selection_content, Atom selection, Atom requested_format) | |||
| { | |||
| Atom property_name = XInternAtom (display, "JUCE_SEL", false); | |||
| // The selection owner will be asked to set the JUCE_SEL property on the | |||
| // juce_messageWindowHandle with the selection content | |||
| XConvertSelection (display, selection, requested_format, property_name, | |||
| juce_messageWindowHandle, CurrentTime); | |||
| int timeoutMs = 200; // will wait at most for 200 ms | |||
| do | |||
| { | |||
| XEvent event; | |||
| if (XCheckTypedWindowEvent (display, juce_messageWindowHandle, SelectionNotify, &event)) | |||
| { | |||
| if (event.xselection.property == property_name) | |||
| { | |||
| jassert (event.xselection.requestor == juce_messageWindowHandle); | |||
| selection_content = juce_readWindowProperty (event.xselection.requestor, | |||
| event.xselection.property, | |||
| requested_format, true); | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| return false; // the format we asked for was denied.. (event.xselection.property == None) | |||
| } | |||
| } | |||
| // not very elegant.. we could do a select() or something like that... | |||
| // however clipboard content requesting is inherently slow on x11, it | |||
| // often takes 50ms or more so... | |||
| Thread::sleep (4); | |||
| timeoutMs -= 4; | |||
| } | |||
| while (timeoutMs > 0); | |||
| DBG("timeout for juce_requestSelectionContent"); | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| // Called from the event loop in juce_linux_Messaging in response to SelectionRequest events | |||
| void juce_handleSelectionRequest (XSelectionRequestEvent &evt) | |||
| { | |||
| initSelectionAtoms(); | |||
| // the selection content is sent to the target window as a window property | |||
| XSelectionEvent reply; | |||
| reply.type = SelectionNotify; | |||
| reply.display = evt.display; | |||
| reply.requestor = evt.requestor; | |||
| reply.selection = evt.selection; | |||
| reply.target = evt.target; | |||
| reply.property = None; // == "fail" | |||
| reply.time = evt.time; | |||
| HeapBlock <char> data; | |||
| int propertyFormat = 0, numDataItems = 0; | |||
| if (evt.selection == XA_PRIMARY || evt.selection == atom_CLIPBOARD) | |||
| { | |||
| if (evt.target == XA_STRING) | |||
| { | |||
| // format data according to system locale | |||
| numDataItems = localClipboardContent.length(); | |||
| data.calloc (numDataItems + 2); | |||
| localClipboardContent.copyToBuffer ((char*) data, numDataItems + 1); | |||
| propertyFormat = 8; // bits/item | |||
| } | |||
| else if (evt.target == atom_UTF8_STRING) | |||
| { | |||
| // translate to utf8 | |||
| numDataItems = localClipboardContent.copyToUTF8 (0); | |||
| data.calloc (numDataItems + 2); | |||
| localClipboardContent.copyToUTF8 (data, numDataItems + 1); | |||
| propertyFormat = 8; // bits/item | |||
| } | |||
| else if (evt.target == atom_TARGETS) | |||
| { | |||
| // another application wants to know what we are able to send | |||
| numDataItems = 2; | |||
| propertyFormat = 32; // atoms are 32-bit | |||
| data.calloc (numDataItems * 4); | |||
| ((Atom*) data)[0] = atom_UTF8_STRING; | |||
| ((Atom*) data)[1] = XA_STRING; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| DBG ("requested unsupported clipboard"); | |||
| } | |||
| if (data != 0) | |||
| { | |||
| const int maxReasonableSelectionSize = 1000000; | |||
| // for very big chunks of data, we should use the "INCR" protocol , which is a pain in the *ss | |||
| if (evt.property != None && numDataItems < maxReasonableSelectionSize) | |||
| { | |||
| XChangeProperty (evt.display, evt.requestor, | |||
| evt.property, evt.target, | |||
| propertyFormat /* 8 or 32 */, PropModeReplace, | |||
| (const unsigned char*) data, numDataItems); | |||
| reply.property = evt.property; // " == success" | |||
| } | |||
| } | |||
| XSendEvent (evt.display, evt.requestor, 0, NoEventMask, (XEvent*) &reply); | |||
| } | |||
| //============================================================================== | |||
| void SystemClipboard::copyTextToClipboard (const String& clipText) throw() | |||
| { | |||
| initSelectionAtoms(); | |||
| localClipboardContent = clipText; | |||
| XSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime); | |||
| XSetSelectionOwner (display, atom_CLIPBOARD, juce_messageWindowHandle, CurrentTime); | |||
| } | |||
| const String SystemClipboard::getTextFromClipboard() throw() | |||
| { | |||
| initSelectionAtoms(); | |||
| /* 1) try to read from the "CLIPBOARD" selection first (the "high | |||
| level" clipboard that is supposed to be filled by ctrl-C | |||
| etc). When a clipboard manager is running, the content of this | |||
| selection is preserved even when the original selection owner | |||
| exits. | |||
| 2) and then try to read from "PRIMARY" selection (the "legacy" selection | |||
| filled by good old x11 apps such as xterm) | |||
| */ | |||
| String content; | |||
| Atom selection = XA_PRIMARY; | |||
| Window selectionOwner = None; | |||
| if ((selectionOwner = XGetSelectionOwner (display, selection)) == None) | |||
| { | |||
| selection = atom_CLIPBOARD; | |||
| selectionOwner = XGetSelectionOwner (display, selection); | |||
| } | |||
| if (selectionOwner != None) | |||
| { | |||
| if (selectionOwner == juce_messageWindowHandle) | |||
| { | |||
| content = localClipboardContent; | |||
| } | |||
| else | |||
| { | |||
| // first try: we want an utf8 string | |||
| bool ok = juce_requestSelectionContent (content, selection, atom_UTF8_STRING); | |||
| if (! ok) | |||
| { | |||
| // second chance, ask for a good old locale-dependent string .. | |||
| ok = juce_requestSelectionContent (content, selection, XA_STRING); | |||
| } | |||
| } | |||
| } | |||
| return content; | |||
| } | |||
| #endif | |||
| @@ -39,6 +39,62 @@ void FileChooser::showPlatformDialog (OwnedArray<File>& results, | |||
| bool selectMultipleFiles, | |||
| FilePreviewComponent* previewComponent) | |||
| { | |||
| const tchar* const separator = T(":"); | |||
| String command ("zenity --file-selection"); | |||
| if (title.isNotEmpty()) | |||
| command << " --title=\"" << title << "\""; | |||
| if (file != File::nonexistent) | |||
| command << " --filename=\"" << file.getFullPathName () << "\""; | |||
| if (isDirectory) | |||
| command << " --directory"; | |||
| if (isSave) | |||
| command << " --save"; | |||
| if (selectMultipleFiles) | |||
| command << " --multiple --separator=\"" << separator << "\""; | |||
| command << " 2>&1"; | |||
| MemoryOutputStream result; | |||
| int status = -1; | |||
| FILE* stream = popen ((const char*) command.toUTF8(), "r"); | |||
| if (stream != 0) | |||
| { | |||
| for (;;) | |||
| { | |||
| char buffer [1024]; | |||
| const int bytesRead = fread (buffer, 1, sizeof (buffer), stream); | |||
| if (bytesRead <= 0) | |||
| break; | |||
| result.write (buffer, bytesRead); | |||
| } | |||
| status = pclose (stream); | |||
| } | |||
| if (status == 0) | |||
| { | |||
| String resultString (String::fromUTF8 ((const uint8*) result.getData(), result.getDataSize())); | |||
| StringArray tokens; | |||
| if (selectMultipleFiles) | |||
| tokens.addTokens (resultString, separator, 0); | |||
| else | |||
| tokens.add (resultString); | |||
| for (int i = 0; i < tokens.size(); i++) | |||
| results.add (new File (tokens[i])); | |||
| return; | |||
| } | |||
| //xxx ain't got one! | |||
| jassertfalse | |||
| } | |||
| @@ -29,11 +29,11 @@ | |||
| //============================================================================== | |||
| #ifdef JUCE_DEBUG | |||
| #define JUCE_DEBUG_XERRORS 1 | |||
| #define JUCE_DEBUG_XERRORS 1 | |||
| #endif | |||
| Display* display = 0; // This is also referenced from WindowDriver.cpp | |||
| static Window juce_messageWindowHandle = None; | |||
| Window juce_messageWindowHandle = None; | |||
| #define SpecialAtom "JUCESpecialAtom" | |||
| #define BroadcastAtom "JUCEBroadcastAtom" | |||
| @@ -49,8 +49,70 @@ XContext improbableNumber; | |||
| // Defined in WindowDriver.cpp | |||
| extern void juce_windowMessageReceive (XEvent* event); | |||
| // Defined in ClipboardDriver.cpp | |||
| extern void juce_handleSelectionRequest (XSelectionRequestEvent &evt); | |||
| //============================================================================== | |||
| class InternalMessageQueue | |||
| { | |||
| public: | |||
| InternalMessageQueue() | |||
| { | |||
| int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd); | |||
| (void) ret; jassert (ret == 0); | |||
| } | |||
| ~InternalMessageQueue() | |||
| { | |||
| close (fd[0]); | |||
| close (fd[1]); | |||
| } | |||
| void postMessage (Message* msg) | |||
| { | |||
| ScopedLock sl (lock); | |||
| queue.add (msg); | |||
| const unsigned char x = 0xff; | |||
| write (fd[0], &x, 1); | |||
| } | |||
| bool isEmpty() const | |||
| { | |||
| ScopedLock sl (lock); | |||
| return queue.size() == 0; | |||
| } | |||
| Message* popMessage() | |||
| { | |||
| Message* m = 0; | |||
| ScopedLock sl (lock); | |||
| if (queue.size() != 0) | |||
| { | |||
| unsigned char x; | |||
| read (fd[1], &x, 1); | |||
| m = queue.getUnchecked(0); | |||
| queue.remove (0, false /* deleteObject */); | |||
| } | |||
| return m; | |||
| } | |||
| int getWaitHandle() const { return fd[1]; } | |||
| private: | |||
| CriticalSection lock; | |||
| OwnedArray <Message> queue; | |||
| int fd[2]; | |||
| }; | |||
| //============================================================================== | |||
| struct MessageThreadFuncCall | |||
| { | |||
| enum { uniqueID = 0x73774623 }; | |||
| MessageCallbackFunction* func; | |||
| void* parameter; | |||
| void* result; | |||
| @@ -58,21 +120,23 @@ struct MessageThreadFuncCall | |||
| WaitableEvent event; | |||
| }; | |||
| static bool errorCondition = false; | |||
| //============================================================================== | |||
| 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; | |||
| // (defined in another file to avoid problems including certain headers in this one) | |||
| extern bool juce_isRunningAsApplication(); | |||
| // Usually happens when client-server connection is broken | |||
| static int ioErrorHandler (Display* display) | |||
| { | |||
| DBG (T("ERROR: connection to X server broken.. terminating.")); | |||
| errorCondition = true; | |||
| errorOccurred = true; | |||
| if (juce_isRunningAsApplication()) | |||
| if (JUCEApplication::getInstance() != 0) | |||
| Process::terminate(); | |||
| return 0; | |||
| @@ -100,20 +164,18 @@ static int errorHandler (Display* display, XErrorEvent* event) | |||
| return 0; | |||
| } | |||
| static bool breakIn = false; | |||
| // Breakin from keyboard | |||
| static void sig_handler (int sig) | |||
| static void signalHandler (int sig) | |||
| { | |||
| if (sig == SIGINT) | |||
| { | |||
| breakIn = true; | |||
| keyboardBreakOccurred = true; | |||
| return; | |||
| } | |||
| static bool reentrant = false; | |||
| if (reentrant == false) | |||
| if (! reentrant) | |||
| { | |||
| reentrant = true; | |||
| @@ -121,14 +183,14 @@ static void sig_handler (int sig) | |||
| fflush (stdout); | |||
| Logger::outputDebugString ("ERROR: Program executed illegal instruction.. terminating"); | |||
| errorCondition = true; | |||
| errorOccurred = true; | |||
| if (juce_isRunningAsApplication()) | |||
| if (JUCEApplication::getInstance() != 0) | |||
| Process::terminate(); | |||
| } | |||
| else | |||
| { | |||
| if (juce_isRunningAsApplication()) | |||
| if (JUCEApplication::getInstance() != 0) | |||
| exit(0); | |||
| } | |||
| } | |||
| @@ -146,7 +208,7 @@ void MessageManager::doPlatformSpecificInitialisation() | |||
| // This is fatal! Print error and closedown | |||
| Logger::outputDebugString ("Failed to initialise xlib thread support."); | |||
| if (juce_isRunningAsApplication()) | |||
| if (JUCEApplication::getInstance() != 0) | |||
| Process::terminate(); | |||
| return; | |||
| @@ -165,7 +227,7 @@ void MessageManager::doPlatformSpecificInitialisation() | |||
| struct sigaction saction; | |||
| sigset_t maskSet; | |||
| sigemptyset (&maskSet); | |||
| saction.sa_handler = sig_handler; | |||
| saction.sa_handler = signalHandler; | |||
| saction.sa_mask = maskSet; | |||
| saction.sa_flags = 0; | |||
| sigaction (SIGINT, &saction, NULL); | |||
| @@ -179,6 +241,10 @@ void MessageManager::doPlatformSpecificInitialisation() | |||
| sigaction (SIGSYS, &saction, NULL); | |||
| #endif | |||
| // Create the internal message queue | |||
| juce_internalMessageQueue = new InternalMessageQueue(); | |||
| // Try to connect to a display | |||
| String displayName (getenv ("DISPLAY")); | |||
| if (displayName.isEmpty()) | |||
| displayName = T(":0.0"); | |||
| @@ -187,12 +253,7 @@ void MessageManager::doPlatformSpecificInitialisation() | |||
| if (display == 0) | |||
| { | |||
| // This is fatal! Print error and closedown | |||
| Logger::outputDebugString ("Failed to open the X display."); | |||
| if (juce_isRunningAsApplication()) | |||
| Process::terminate(); | |||
| // This is not fatal! we can run headless. | |||
| return; | |||
| } | |||
| @@ -223,7 +284,9 @@ void MessageManager::doPlatformSpecificInitialisation() | |||
| void MessageManager::doPlatformSpecificShutdown() | |||
| { | |||
| if (errorCondition == false) | |||
| deleteAndZero (juce_internalMessageQueue); | |||
| if (display != 0 && ! errorOccurred) | |||
| { | |||
| XDestroyWindow (display, juce_messageWindowHandle); | |||
| XCloseDisplay (display); | |||
| @@ -242,9 +305,15 @@ void MessageManager::doPlatformSpecificShutdown() | |||
| bool juce_postMessageToSystemQueue (void* message) | |||
| { | |||
| if (errorCondition) | |||
| if (errorOccurred) | |||
| return false; | |||
| juce_internalMessageQueue->postMessage ((Message*) message); | |||
| return true; | |||
| } | |||
| /*bool juce_postMessageToX11Queue (void *message) | |||
| { | |||
| XClientMessageEvent clientMsg; | |||
| clientMsg.display = display; | |||
| clientMsg.window = juce_messageWindowHandle; | |||
| @@ -264,138 +333,156 @@ bool juce_postMessageToSystemQueue (void* message) | |||
| XFlush (display); // This is necessary to ensure the event is delivered | |||
| return true; | |||
| } | |||
| }*/ | |||
| void MessageManager::broadcastMessage (const String& value) throw() | |||
| { | |||
| /* TODO */ | |||
| } | |||
| void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, | |||
| void* parameter) | |||
| { | |||
| void* retVal = 0; | |||
| if (errorOccurred) | |||
| return 0; | |||
| if (! errorCondition) | |||
| if (! isThisTheMessageThread()) | |||
| { | |||
| if (! isThisTheMessageThread()) | |||
| { | |||
| static MessageThreadFuncCall messageFuncCallContext; | |||
| MessageThreadFuncCall messageCallContext; | |||
| messageCallContext.func = func; | |||
| messageCallContext.parameter = parameter; | |||
| const ScopedLock sl (messageFuncCallContext.lock); | |||
| juce_internalMessageQueue->postMessage (new Message (MessageThreadFuncCall::uniqueID, | |||
| 0, 0, &messageCallContext)); | |||
| XClientMessageEvent clientMsg; | |||
| clientMsg.display = display; | |||
| clientMsg.window = juce_messageWindowHandle; | |||
| clientMsg.type = ClientMessage; | |||
| clientMsg.format = 32; | |||
| clientMsg.message_type = specialCallbackId; | |||
| #if JUCE_64BIT | |||
| clientMsg.data.l[0] = (long) (0x00000000ffffffff & (((uint64) &messageFuncCallContext) >> 32)); | |||
| clientMsg.data.l[1] = (long) (0x00000000ffffffff & (uint64) &messageFuncCallContext); | |||
| #else | |||
| clientMsg.data.l[0] = (long) &messageFuncCallContext; | |||
| #endif | |||
| // Wait for it to complete before continuing | |||
| messageCallContext.event.wait(); | |||
| messageFuncCallContext.func = func; | |||
| messageFuncCallContext.parameter = parameter; | |||
| return messageCallContext.result; | |||
| } | |||
| else | |||
| { | |||
| // Just call the function directly | |||
| return func (parameter); | |||
| } | |||
| } | |||
| if (XSendEvent (display, juce_messageWindowHandle, false, NoEventMask, (XEvent*) &clientMsg) == 0) | |||
| return 0; | |||
| // Wait for an event (either XEvent, or an internal Message) | |||
| static bool juce_sleepUntilEvent (const int timeoutMs) | |||
| { | |||
| if ((display != 0 && XPending (display)) || ! juce_internalMessageQueue->isEmpty()) | |||
| return true; | |||
| XFlush (display); // This is necessary to ensure the event is delivered | |||
| 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 | |||
| messageFuncCallContext.event.wait(); | |||
| fd_set readset; | |||
| FD_ZERO (&readset); | |||
| FD_SET (fd0, &readset); | |||
| retVal = messageFuncCallContext.result; | |||
| } | |||
| else | |||
| { | |||
| // Just call the function directly | |||
| retVal = func (parameter); | |||
| } | |||
| if (display != 0) | |||
| { | |||
| int fd1 = XConnectionNumber (display); | |||
| FD_SET (fd1, &readset); | |||
| fdmax = jmax (fd0, fd1); | |||
| } | |||
| return retVal; | |||
| int ret = select (fdmax + 1, &readset, 0, 0, &tv); | |||
| return (ret > 0); // ret <= 0 if error or timeout | |||
| } | |||
| bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) | |||
| // Handle next XEvent (if any) | |||
| static bool juce_dispatchNextXEvent() | |||
| { | |||
| if (errorCondition) | |||
| if (display == 0 || ! XPending (display)) | |||
| return false; | |||
| if (breakIn) | |||
| XEvent evt; | |||
| XNextEvent (display, &evt); | |||
| if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle) | |||
| { | |||
| juce_handleSelectionRequest (evt.xselectionrequest); | |||
| } | |||
| else if (evt.xany.window != juce_messageWindowHandle) | |||
| { | |||
| errorCondition = true; | |||
| juce_windowMessageReceive (&evt); | |||
| } | |||
| if (juce_isRunningAsApplication()) | |||
| Process::terminate(); | |||
| return true; | |||
| } | |||
| // Handle next internal Message (if any) | |||
| static bool juce_dispatchNextInternalMessage() | |||
| { | |||
| if (juce_internalMessageQueue->isEmpty()) | |||
| return false; | |||
| } | |||
| if (returnIfNoPendingMessages && ! XPending (display)) | |||
| return false; | |||
| ScopedPointer <Message> msg (juce_internalMessageQueue->popMessage()); | |||
| XEvent evt; | |||
| XNextEvent (display, &evt); | |||
| 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()); | |||
| } | |||
| if (evt.type == ClientMessage && evt.xany.window == juce_messageWindowHandle) | |||
| return true; | |||
| } | |||
| // this function expects that it will NEVER be called simultaneously for two concurrent threads | |||
| bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) | |||
| { | |||
| for (;;) | |||
| { | |||
| XClientMessageEvent* const clientMsg = (XClientMessageEvent*) &evt; | |||
| if (errorOccurred) | |||
| break; | |||
| if (keyboardBreakOccurred) | |||
| { | |||
| errorOccurred = true; | |||
| if (JUCEApplication::getInstance() != 0) | |||
| Process::terminate(); | |||
| break; | |||
| } | |||
| if (clientMsg->format != 32) | |||
| static int totalEventCount = 0; | |||
| ++totalEventCount; | |||
| // 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) | |||
| { | |||
| jassertfalse | |||
| DBG ("Error: juce_dispatchNextMessageOnSystemQueue received malformed client message."); | |||
| if (juce_dispatchNextXEvent() || juce_dispatchNextInternalMessage()) | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| JUCE_TRY | |||
| { | |||
| #if JUCE_64BIT | |||
| void* const messagePtr | |||
| = (void*) ((0xffffffff00000000 & (((uint64) clientMsg->data.l[0]) << 32)) | |||
| | (clientMsg->data.l[1] & 0x00000000ffffffff)); | |||
| #else | |||
| void* const messagePtr = (void*) (clientMsg->data.l[0]); | |||
| #endif | |||
| if (clientMsg->message_type == specialId) | |||
| { | |||
| MessageManager::getInstance()->deliverMessage (messagePtr); | |||
| } | |||
| else if (clientMsg->message_type == specialCallbackId) | |||
| { | |||
| MessageThreadFuncCall* const call = (MessageThreadFuncCall*) messagePtr; | |||
| MessageCallbackFunction* func = call->func; | |||
| call->result = (*func) (call->parameter); | |||
| call->event.signal(); | |||
| } | |||
| else if (clientMsg->message_type == broadcastId) | |||
| { | |||
| #if 0 | |||
| TCHAR buffer[8192]; | |||
| zeromem (buffer, sizeof (buffer)); | |||
| if (GlobalGetAtomName ((ATOM) lParam, buffer, 8192) != 0) | |||
| mq->deliverBroadcastMessage (String (buffer)); | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| DBG ("Error: juce_dispatchNextMessageOnSystemQueue received unknown client message."); | |||
| } | |||
| } | |||
| JUCE_CATCH_ALL | |||
| if (juce_dispatchNextInternalMessage() || juce_dispatchNextXEvent()) | |||
| return true; | |||
| } | |||
| } | |||
| else if (evt.xany.window != juce_messageWindowHandle) | |||
| { | |||
| juce_windowMessageReceive (&evt); | |||
| if (returnIfNoPendingMessages) // early exit | |||
| break; | |||
| // the timeout is to be on the safe side, but it does not seem to be useful | |||
| juce_sleepUntilEvent (2000); | |||
| } | |||
| return true; | |||
| return false; | |||
| } | |||
| #endif | |||
| @@ -100,12 +100,6 @@ bool Process::isForegroundProcess() | |||
| return isActiveApplication; | |||
| } | |||
| // (used in the messaging code, declared here for build reasons) | |||
| bool juce_isRunningAsApplication() | |||
| { | |||
| return JUCEApplication::getInstance() != 0; | |||
| } | |||
| //============================================================================== | |||
| // These are defined in juce_linux_Messaging.cpp | |||
| extern Display* display; | |||
| @@ -611,7 +605,7 @@ public: | |||
| // blit results to screen. | |||
| #if JUCE_USE_XSHM | |||
| if (usingXShm) | |||
| XShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, False); | |||
| XShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, True); | |||
| else | |||
| #endif | |||
| XPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh); | |||
| @@ -1627,6 +1621,10 @@ public: | |||
| break; | |||
| default: | |||
| #if JUCE_USE_XSHM | |||
| if (event->xany.type == XShmGetEventBase (display)) | |||
| repainter->notifyPaintCompleted(); | |||
| #endif | |||
| break; | |||
| } | |||
| } | |||
| @@ -1716,6 +1714,8 @@ private: | |||
| lastTimeImageUsed (0) | |||
| { | |||
| #if JUCE_USE_XSHM | |||
| shmCompletedDrawing = true; | |||
| useARGBImagesForRendering = isShmAvailable(); | |||
| if (useARGBImagesForRendering) | |||
| @@ -1738,7 +1738,7 @@ private: | |||
| void timerCallback() | |||
| { | |||
| if (! regionsNeedingRepaint.isEmpty()) | |||
| if (! regionsNeedingRepaint.isEmpty() && shmCompletedDrawing) | |||
| { | |||
| stopTimer(); | |||
| performAnyPendingRepaintsNow(); | |||
| @@ -1766,6 +1766,8 @@ private: | |||
| if (! totalArea.isEmpty()) | |||
| { | |||
| shmCompletedDrawing = false; | |||
| if (image == 0 || image->getWidth() < totalArea.getWidth() | |||
| || image->getHeight() < totalArea.getHeight()) | |||
| { | |||
| @@ -1809,6 +1811,13 @@ private: | |||
| startTimer (repaintTimerPeriod); | |||
| } | |||
| #if JUCE_USE_XSHM | |||
| void notifyPaintCompleted() | |||
| { | |||
| shmCompletedDrawing = true; | |||
| } | |||
| #endif | |||
| private: | |||
| LinuxComponentPeer* const peer; | |||
| ScopedPointer <XBitmapImage> image; | |||
| @@ -1817,6 +1826,7 @@ private: | |||
| #if JUCE_USE_XSHM | |||
| bool useARGBImagesForRendering; | |||
| bool shmCompletedDrawing; | |||
| #endif | |||
| LinuxRepaintManager (const LinuxRepaintManager&); | |||
| const LinuxRepaintManager& operator= (const LinuxRepaintManager&); | |||
| @@ -2555,6 +2565,9 @@ void juce_windowMessageReceive (XEvent* event) | |||
| //============================================================================== | |||
| void juce_updateMultiMonitorInfo (Array <Rectangle>& monitorCoords, const bool /*clipToWorkArea*/) throw() | |||
| { | |||
| if (display == 0) | |||
| return; | |||
| #if JUCE_USE_XINERAMA | |||
| int major_opcode, first_event, first_error; | |||
| @@ -3096,85 +3109,6 @@ void OpenGLPixelFormat::getAvailablePixelFormats (Component* component, | |||
| #endif | |||
| //============================================================================== | |||
| static void initClipboard (Window root, Atom* cutBuffers) throw() | |||
| { | |||
| static bool init = false; | |||
| if (! init) | |||
| { | |||
| init = true; | |||
| // Make sure all cut buffers exist before use | |||
| for (int i = 0; i < 8; i++) | |||
| { | |||
| XChangeProperty (display, root, cutBuffers[i], | |||
| XA_STRING, 8, PropModeAppend, NULL, 0); | |||
| } | |||
| } | |||
| } | |||
| // Clipboard implemented currently using cut buffers | |||
| // rather than the more powerful selection method | |||
| void SystemClipboard::copyTextToClipboard (const String& clipText) throw() | |||
| { | |||
| Window root = RootWindow (display, DefaultScreen (display)); | |||
| Atom cutBuffers[8] = { XA_CUT_BUFFER0, XA_CUT_BUFFER1, XA_CUT_BUFFER2, XA_CUT_BUFFER3, | |||
| XA_CUT_BUFFER4, XA_CUT_BUFFER5, XA_CUT_BUFFER6, XA_CUT_BUFFER7 }; | |||
| initClipboard (root, cutBuffers); | |||
| XRotateWindowProperties (display, root, cutBuffers, 8, 1); | |||
| XChangeProperty (display, root, cutBuffers[0], | |||
| XA_STRING, 8, PropModeReplace, (const unsigned char*) (const char*) clipText, | |||
| clipText.length()); | |||
| } | |||
| const String SystemClipboard::getTextFromClipboard() throw() | |||
| { | |||
| const int bufSize = 64; // in words | |||
| String returnData; | |||
| int byteOffset = 0; | |||
| Window root = RootWindow (display, DefaultScreen (display)); | |||
| Atom cutBuffers[8] = { XA_CUT_BUFFER0, XA_CUT_BUFFER1, XA_CUT_BUFFER2, XA_CUT_BUFFER3, | |||
| XA_CUT_BUFFER4, XA_CUT_BUFFER5, XA_CUT_BUFFER6, XA_CUT_BUFFER7 }; | |||
| initClipboard (root, cutBuffers); | |||
| for (;;) | |||
| { | |||
| unsigned long bytesLeft = 0, nitems = 0; | |||
| unsigned char* clipData = 0; | |||
| int actualFormat = 0; | |||
| Atom actualType; | |||
| if (XGetWindowProperty (display, root, cutBuffers[0], byteOffset >> 2, bufSize, | |||
| False, XA_STRING, &actualType, &actualFormat, &nitems, &bytesLeft, | |||
| &clipData) == Success) | |||
| { | |||
| if (actualType == XA_STRING && actualFormat == 8) | |||
| { | |||
| byteOffset += nitems; | |||
| returnData += String ((const char*) clipData, nitems); | |||
| } | |||
| else | |||
| { | |||
| bytesLeft = 0; | |||
| } | |||
| if (clipData != 0) | |||
| XFree (clipData); | |||
| } | |||
| if (bytesLeft == 0) | |||
| break; | |||
| } | |||
| return returnData; | |||
| } | |||
| //============================================================================== | |||
| bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) | |||
| { | |||
| @@ -53,7 +53,6 @@ END_JUCE_NAMESPACE | |||
| - (void) captureOutput: (QTCaptureFileOutput*) captureOutput | |||
| didOutputSampleBuffer: (QTSampleBuffer*) sampleBuffer | |||
| fromConnection: (QTCaptureConnection*) connection; | |||
| @end | |||
| BEGIN_JUCE_NAMESPACE | |||
| @@ -120,6 +119,7 @@ public: | |||
| void resetFile() | |||
| { | |||
| [fileOutput recordToOutputFileURL: nil]; | |||
| [session removeOutput: fileOutput]; | |||
| [fileOutput release]; | |||
| fileOutput = [[QTCaptureMovieFileOutput alloc] init]; | |||
| @@ -199,11 +199,14 @@ END_JUCE_NAMESPACE | |||
| withSampleBuffer: (QTSampleBuffer*) sampleBuffer | |||
| fromConnection: (QTCaptureConnection*) connection | |||
| { | |||
| const ScopedAutoReleasePool pool; | |||
| if (internal->listeners.size() > 0) | |||
| { | |||
| const ScopedAutoReleasePool pool; | |||
| internal->callListeners ([CIImage imageWithCVImageBuffer: videoFrame], | |||
| CVPixelBufferGetWidth (videoFrame), | |||
| CVPixelBufferGetHeight (videoFrame)); | |||
| internal->callListeners ([CIImage imageWithCVImageBuffer: videoFrame], | |||
| CVPixelBufferGetWidth (videoFrame), | |||
| CVPixelBufferGetHeight (videoFrame)); | |||
| } | |||
| } | |||
| - (void) captureOutput: (QTCaptureFileOutput*) captureOutput | |||
| @@ -338,12 +341,11 @@ CameraDevice* CameraDevice::openDevice (int index, | |||
| int minWidth, int minHeight, | |||
| int maxWidth, int maxHeight) | |||
| { | |||
| CameraDevice* d = new CameraDevice (getAvailableDevices() [index], index); | |||
| ScopedPointer <CameraDevice> d (new CameraDevice (getAvailableDevices() [index], index)); | |||
| if (((QTCameraDeviceInteral*) (d->internal))->openingError.isEmpty()) | |||
| return d; | |||
| return d.release(); | |||
| delete d; | |||
| return 0; | |||
| } | |||