mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-18 00:54:19 +00:00
Added Linux contributions for headless messaging, clipboard and better shm support.
This commit is contained in:
parent
d7cbfe93c5
commit
2343dcdcee
8 changed files with 1344 additions and 670 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -252666,7 +252666,7 @@ void* PlatformUtilities::getProcedureEntryPoint (void* libraryHandle, const Stri
|
|||
|
||||
#if ! JUCE_ONLY_BUILD_CORE_LIBRARY
|
||||
|
||||
/********* Start of inlined file: juce_linux_Messaging.cpp *********/
|
||||
/********* Start of inlined file: juce_linux_Clipboard.cpp *********/
|
||||
// (This file gets included by juce_linux_NativeCode.cpp, rather than being
|
||||
// compiled on its own).
|
||||
#if JUCE_INCLUDED_FILE
|
||||
|
|
@ -252675,8 +252675,246 @@ void* PlatformUtilities::getProcedureEntryPoint (void* libraryHandle, const Stri
|
|||
#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
|
||||
/********* End of inlined file: juce_linux_Clipboard.cpp *********/
|
||||
|
||||
/********* Start of inlined file: juce_linux_Messaging.cpp *********/
|
||||
// (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
|
||||
|
||||
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"
|
||||
|
|
@ -252692,8 +252930,68 @@ 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;
|
||||
|
|
@ -252701,21 +252999,22 @@ 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;
|
||||
|
|
@ -252743,20 +253042,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;
|
||||
|
||||
|
|
@ -252764,14 +253061,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);
|
||||
}
|
||||
}
|
||||
|
|
@ -252788,7 +253085,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;
|
||||
|
|
@ -252807,7 +253104,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);
|
||||
|
|
@ -252821,6 +253118,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");
|
||||
|
|
@ -252829,12 +253130,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;
|
||||
}
|
||||
|
||||
|
|
@ -252865,7 +253161,9 @@ void MessageManager::doPlatformSpecificInitialisation()
|
|||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
if (errorCondition == false)
|
||||
deleteAndZero (juce_internalMessageQueue);
|
||||
|
||||
if (display != 0 && ! errorOccurred)
|
||||
{
|
||||
XDestroyWindow (display, juce_messageWindowHandle);
|
||||
XCloseDisplay (display);
|
||||
|
|
@ -252884,9 +253182,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;
|
||||
|
|
@ -252906,131 +253210,79 @@ 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;
|
||||
|
||||
if (XSendEvent (display, juce_messageWindowHandle, false, NoEventMask, (XEvent*) &clientMsg) == 0)
|
||||
return 0;
|
||||
|
||||
XFlush (display); // This is necessary to ensure the event is delivered
|
||||
|
||||
// Wait for it to complete before continuing
|
||||
messageFuncCallContext.event.wait();
|
||||
|
||||
retVal = messageFuncCallContext.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just call the function directly
|
||||
retVal = func (parameter);
|
||||
}
|
||||
return messageCallContext.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just call the function directly
|
||||
return func (parameter);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
// Wait for an event (either XEvent, or an internal Message)
|
||||
static bool juce_sleepUntilEvent (const int timeoutMs)
|
||||
{
|
||||
if (errorCondition)
|
||||
return false;
|
||||
if ((display != 0 && XPending (display)) || ! juce_internalMessageQueue->isEmpty())
|
||||
return true;
|
||||
|
||||
if (breakIn)
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeoutMs * 1000;
|
||||
int fd0 = juce_internalMessageQueue->getWaitHandle();
|
||||
int fdmax = fd0;
|
||||
|
||||
fd_set readset;
|
||||
FD_ZERO (&readset);
|
||||
FD_SET (fd0, &readset);
|
||||
|
||||
if (display != 0)
|
||||
{
|
||||
errorCondition = true;
|
||||
|
||||
if (juce_isRunningAsApplication())
|
||||
Process::terminate();
|
||||
|
||||
return false;
|
||||
int fd1 = XConnectionNumber (display);
|
||||
FD_SET (fd1, &readset);
|
||||
fdmax = jmax (fd0, fd1);
|
||||
}
|
||||
|
||||
if (returnIfNoPendingMessages && ! XPending (display))
|
||||
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 || ! XPending (display))
|
||||
return false;
|
||||
|
||||
XEvent evt;
|
||||
XNextEvent (display, &evt);
|
||||
|
||||
if (evt.type == ClientMessage && evt.xany.window == juce_messageWindowHandle)
|
||||
if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle)
|
||||
{
|
||||
XClientMessageEvent* const clientMsg = (XClientMessageEvent*) &evt;
|
||||
|
||||
if (clientMsg->format != 32)
|
||||
{
|
||||
jassertfalse
|
||||
DBG ("Error: juce_dispatchNextMessageOnSystemQueue received malformed client message.");
|
||||
}
|
||||
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
|
||||
}
|
||||
juce_handleSelectionRequest (evt.xselectionrequest);
|
||||
}
|
||||
else if (evt.xany.window != juce_messageWindowHandle)
|
||||
{
|
||||
|
|
@ -253040,6 +253292,76 @@ bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Handle next internal Message (if any)
|
||||
static bool juce_dispatchNextInternalMessage()
|
||||
{
|
||||
if (juce_internalMessageQueue->isEmpty())
|
||||
return false;
|
||||
|
||||
ScopedPointer <Message> msg (juce_internalMessageQueue->popMessage());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// this function expects that it will NEVER be called simultaneously for two concurrent threads
|
||||
bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (errorOccurred)
|
||||
break;
|
||||
|
||||
if (keyboardBreakOccurred)
|
||||
{
|
||||
errorOccurred = true;
|
||||
|
||||
if (JUCEApplication::getInstance() != 0)
|
||||
Process::terminate();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (juce_dispatchNextXEvent() || juce_dispatchNextInternalMessage())
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (juce_dispatchNextInternalMessage() || juce_dispatchNextXEvent())
|
||||
return true;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
#endif
|
||||
/********* End of inlined file: juce_linux_Messaging.cpp *********/
|
||||
|
||||
|
|
@ -253667,12 +253989,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;
|
||||
extern XContext improbableNumber;
|
||||
|
|
@ -254170,7 +254486,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);
|
||||
|
|
@ -255182,6 +255498,10 @@ public:
|
|||
break;
|
||||
|
||||
default:
|
||||
#if JUCE_USE_XSHM
|
||||
if (event->xany.type == XShmGetEventBase (display))
|
||||
repainter->notifyPaintCompleted();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -255269,6 +255589,8 @@ private:
|
|||
lastTimeImageUsed (0)
|
||||
{
|
||||
#if JUCE_USE_XSHM
|
||||
shmCompletedDrawing = true;
|
||||
|
||||
useARGBImagesForRendering = isShmAvailable();
|
||||
|
||||
if (useARGBImagesForRendering)
|
||||
|
|
@ -255291,7 +255613,7 @@ private:
|
|||
|
||||
void timerCallback()
|
||||
{
|
||||
if (! regionsNeedingRepaint.isEmpty())
|
||||
if (! regionsNeedingRepaint.isEmpty() && shmCompletedDrawing)
|
||||
{
|
||||
stopTimer();
|
||||
performAnyPendingRepaintsNow();
|
||||
|
|
@ -255319,6 +255641,8 @@ private:
|
|||
|
||||
if (! totalArea.isEmpty())
|
||||
{
|
||||
shmCompletedDrawing = false;
|
||||
|
||||
if (image == 0 || image->getWidth() < totalArea.getWidth()
|
||||
|| image->getHeight() < totalArea.getHeight())
|
||||
{
|
||||
|
|
@ -255362,6 +255686,13 @@ private:
|
|||
startTimer (repaintTimerPeriod);
|
||||
}
|
||||
|
||||
#if JUCE_USE_XSHM
|
||||
void notifyPaintCompleted()
|
||||
{
|
||||
shmCompletedDrawing = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
LinuxComponentPeer* const peer;
|
||||
ScopedPointer <XBitmapImage> image;
|
||||
|
|
@ -255370,6 +255701,7 @@ private:
|
|||
|
||||
#if JUCE_USE_XSHM
|
||||
bool useARGBImagesForRendering;
|
||||
bool shmCompletedDrawing;
|
||||
#endif
|
||||
LinuxRepaintManager (const LinuxRepaintManager&);
|
||||
const LinuxRepaintManager& operator= (const LinuxRepaintManager&);
|
||||
|
|
@ -256101,6 +256433,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;
|
||||
|
||||
|
|
@ -256629,84 +256964,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)
|
||||
{
|
||||
jassertfalse // not implemented!
|
||||
|
|
@ -258888,6 +259145,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
|
||||
}
|
||||
|
|
@ -273681,7 +273994,6 @@ END_JUCE_NAMESPACE
|
|||
- (void) captureOutput: (QTCaptureFileOutput*) captureOutput
|
||||
didOutputSampleBuffer: (QTSampleBuffer*) sampleBuffer
|
||||
fromConnection: (QTCaptureConnection*) connection;
|
||||
|
||||
@end
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
|
@ -273747,6 +274059,7 @@ public:
|
|||
|
||||
void resetFile()
|
||||
{
|
||||
[fileOutput recordToOutputFileURL: nil];
|
||||
[session removeOutput: fileOutput];
|
||||
[fileOutput release];
|
||||
fileOutput = [[QTCaptureMovieFileOutput alloc] init];
|
||||
|
|
@ -273826,11 +274139,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
|
||||
|
|
@ -273962,12 +274278,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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
266
src/native/linux/juce_linux_Clipboard.cpp
Normal file
266
src/native/linux/juce_linux_Clipboard.cpp
Normal file
|
|
@ -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,131 +333,79 @@ 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;
|
||||
|
||||
if (XSendEvent (display, juce_messageWindowHandle, false, NoEventMask, (XEvent*) &clientMsg) == 0)
|
||||
return 0;
|
||||
|
||||
XFlush (display); // This is necessary to ensure the event is delivered
|
||||
|
||||
// Wait for it to complete before continuing
|
||||
messageFuncCallContext.event.wait();
|
||||
|
||||
retVal = messageFuncCallContext.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just call the function directly
|
||||
retVal = func (parameter);
|
||||
}
|
||||
return messageCallContext.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just call the function directly
|
||||
return func (parameter);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
// Wait for an event (either XEvent, or an internal Message)
|
||||
static bool juce_sleepUntilEvent (const int timeoutMs)
|
||||
{
|
||||
if (errorCondition)
|
||||
return false;
|
||||
if ((display != 0 && XPending (display)) || ! juce_internalMessageQueue->isEmpty())
|
||||
return true;
|
||||
|
||||
if (breakIn)
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeoutMs * 1000;
|
||||
int fd0 = juce_internalMessageQueue->getWaitHandle();
|
||||
int fdmax = fd0;
|
||||
|
||||
fd_set readset;
|
||||
FD_ZERO (&readset);
|
||||
FD_SET (fd0, &readset);
|
||||
|
||||
if (display != 0)
|
||||
{
|
||||
errorCondition = true;
|
||||
|
||||
if (juce_isRunningAsApplication())
|
||||
Process::terminate();
|
||||
|
||||
return false;
|
||||
int fd1 = XConnectionNumber (display);
|
||||
FD_SET (fd1, &readset);
|
||||
fdmax = jmax (fd0, fd1);
|
||||
}
|
||||
|
||||
if (returnIfNoPendingMessages && ! XPending (display))
|
||||
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 || ! XPending (display))
|
||||
return false;
|
||||
|
||||
XEvent evt;
|
||||
XNextEvent (display, &evt);
|
||||
|
||||
if (evt.type == ClientMessage && evt.xany.window == juce_messageWindowHandle)
|
||||
if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle)
|
||||
{
|
||||
XClientMessageEvent* const clientMsg = (XClientMessageEvent*) &evt;
|
||||
|
||||
if (clientMsg->format != 32)
|
||||
{
|
||||
jassertfalse
|
||||
DBG ("Error: juce_dispatchNextMessageOnSystemQueue received malformed client message.");
|
||||
}
|
||||
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
|
||||
}
|
||||
juce_handleSelectionRequest (evt.xselectionrequest);
|
||||
}
|
||||
else if (evt.xany.window != juce_messageWindowHandle)
|
||||
{
|
||||
|
|
@ -398,4 +415,74 @@ bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Handle next internal Message (if any)
|
||||
static bool juce_dispatchNextInternalMessage()
|
||||
{
|
||||
if (juce_internalMessageQueue->isEmpty())
|
||||
return false;
|
||||
|
||||
ScopedPointer <Message> msg (juce_internalMessageQueue->popMessage());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// this function expects that it will NEVER be called simultaneously for two concurrent threads
|
||||
bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (errorOccurred)
|
||||
break;
|
||||
|
||||
if (keyboardBreakOccurred)
|
||||
{
|
||||
errorOccurred = true;
|
||||
|
||||
if (JUCEApplication::getInstance() != 0)
|
||||
Process::terminate();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (juce_dispatchNextXEvent() || juce_dispatchNextInternalMessage())
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (juce_dispatchNextInternalMessage() || juce_dispatchNextXEvent())
|
||||
return true;
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue