mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-13 00:04:19 +00:00
Added Animated App template and examples
This commit is contained in:
parent
fefcf7aca6
commit
ff6520a89a
1141 changed files with 438491 additions and 94 deletions
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_SCOPEDXLOCK_H_INCLUDED
|
||||
#define JUCE_SCOPEDXLOCK_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_LINUX || DOXYGEN
|
||||
|
||||
/** A handy class that uses XLockDisplay and XUnlockDisplay to lock the X server
|
||||
using RAII (Only available in Linux!).
|
||||
*/
|
||||
class ScopedXLock
|
||||
{
|
||||
public:
|
||||
/** Creating a ScopedXLock object locks the X display.
|
||||
This uses XLockDisplay() to grab the display that Juce is using.
|
||||
*/
|
||||
ScopedXLock();
|
||||
|
||||
/** Deleting a ScopedXLock object unlocks the X display.
|
||||
This calls XUnlockDisplay() to release the lock.
|
||||
*/
|
||||
~ScopedXLock();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // JUCE_SCOPEDXLOCK_H_INCLUDED
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
void MessageManager::doPlatformSpecificInitialisation() {}
|
||||
void MessageManager::doPlatformSpecificShutdown() {}
|
||||
|
||||
//==============================================================================
|
||||
bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages)
|
||||
{
|
||||
Logger::outputDebugString ("*** Modal loops are not possible in Android!! Exiting...");
|
||||
exit (1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
message->incReferenceCount();
|
||||
android.activity.callVoidMethod (JuceAppActivity.postMessage, (jlong) (pointer_sized_uint) message);
|
||||
return true;
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, deliverMessage, void, (jobject activity, jlong value))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
MessageManager::MessageBase* const message = (MessageManager::MessageBase*) (pointer_sized_uint) value;
|
||||
message->messageCallback();
|
||||
message->decReferenceCount();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::broadcastMessage (const String&)
|
||||
{
|
||||
}
|
||||
|
||||
void MessageManager::runDispatchLoop()
|
||||
{
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
struct QuitCallback : public CallbackMessage
|
||||
{
|
||||
QuitCallback() {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
android.activity.callVoidMethod (JuceAppActivity.finish);
|
||||
}
|
||||
};
|
||||
|
||||
(new QuitCallback())->post();
|
||||
quitMessagePosted = true;
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
void MessageManager::runDispatchLoop()
|
||||
{
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
runDispatchLoopUntil (-1);
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
[[[UIApplication sharedApplication] delegate] applicationWillTerminate: [UIApplication sharedApplication]];
|
||||
exit (0); // iOS apps get no mercy..
|
||||
}
|
||||
|
||||
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
|
||||
uint32 startTime = Time::getMillisecondCounter();
|
||||
NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001];
|
||||
|
||||
while (! quitMessagePosted)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
|
||||
beforeDate: endDate];
|
||||
|
||||
if (millisecondsToRunFor >= 0
|
||||
&& Time::getMillisecondCounter() >= startTime + (uint32) millisecondsToRunFor)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ! quitMessagePosted;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static ScopedPointer<MessageQueue> messageQueue;
|
||||
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (messageQueue == nullptr)
|
||||
messageQueue = new MessageQueue();
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
messageQueue = nullptr;
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
if (messageQueue != nullptr)
|
||||
messageQueue->post (message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String&)
|
||||
{
|
||||
// N/A on current iOS
|
||||
}
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_DEBUG && ! defined (JUCE_DEBUG_XERRORS)
|
||||
#define JUCE_DEBUG_XERRORS 1
|
||||
#endif
|
||||
|
||||
Display* display = nullptr;
|
||||
Window juce_messageWindowHandle = None;
|
||||
XContext windowHandleXContext; // This is referenced from Windowing.cpp
|
||||
|
||||
typedef bool (*WindowMessageReceiveCallback) (XEvent&);
|
||||
WindowMessageReceiveCallback dispatchWindowMessage = nullptr;
|
||||
|
||||
typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&);
|
||||
SelectionRequestCallback handleSelectionRequest = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
ScopedXLock::ScopedXLock() { XLockDisplay (display); }
|
||||
ScopedXLock::~ScopedXLock() { XUnlockDisplay (display); }
|
||||
|
||||
//==============================================================================
|
||||
class InternalMessageQueue
|
||||
{
|
||||
public:
|
||||
InternalMessageQueue()
|
||||
: bytesInSocket (0),
|
||||
totalEventCount (0)
|
||||
{
|
||||
int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd);
|
||||
(void) ret; jassert (ret == 0);
|
||||
}
|
||||
|
||||
~InternalMessageQueue()
|
||||
{
|
||||
close (fd[0]);
|
||||
close (fd[1]);
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void postMessage (MessageManager::MessageBase* const msg)
|
||||
{
|
||||
const int maxBytesInSocketQueue = 128;
|
||||
|
||||
ScopedLock sl (lock);
|
||||
queue.add (msg);
|
||||
|
||||
if (bytesInSocket < maxBytesInSocketQueue)
|
||||
{
|
||||
++bytesInSocket;
|
||||
|
||||
ScopedUnlock ul (lock);
|
||||
const unsigned char x = 0xff;
|
||||
size_t bytesWritten = write (fd[0], &x, 1);
|
||||
(void) bytesWritten;
|
||||
}
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
ScopedLock sl (lock);
|
||||
return queue.size() == 0;
|
||||
}
|
||||
|
||||
bool dispatchNextEvent()
|
||||
{
|
||||
// This alternates between giving priority to XEvents or internal messages,
|
||||
// to keep everything running smoothly..
|
||||
if ((++totalEventCount & 1) != 0)
|
||||
return dispatchNextXEvent() || dispatchNextInternalMessage();
|
||||
|
||||
return dispatchNextInternalMessage() || dispatchNextXEvent();
|
||||
}
|
||||
|
||||
// Wait for an event (either XEvent, or an internal Message)
|
||||
bool sleepUntilEvent (const int timeoutMs)
|
||||
{
|
||||
if (! isEmpty())
|
||||
return true;
|
||||
|
||||
if (display != 0)
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
if (XPending (display))
|
||||
return true;
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeoutMs * 1000;
|
||||
int fd0 = getWaitHandle();
|
||||
int fdmax = fd0;
|
||||
|
||||
fd_set readset;
|
||||
FD_ZERO (&readset);
|
||||
FD_SET (fd0, &readset);
|
||||
|
||||
if (display != 0)
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
int fd1 = XConnectionNumber (display);
|
||||
FD_SET (fd1, &readset);
|
||||
fdmax = jmax (fd0, fd1);
|
||||
}
|
||||
|
||||
const int ret = select (fdmax + 1, &readset, 0, 0, &tv);
|
||||
return (ret > 0); // ret <= 0 if error or timeout
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue);
|
||||
|
||||
private:
|
||||
CriticalSection lock;
|
||||
ReferenceCountedArray <MessageManager::MessageBase> queue;
|
||||
int fd[2];
|
||||
int bytesInSocket;
|
||||
int totalEventCount;
|
||||
|
||||
int getWaitHandle() const noexcept { return fd[1]; }
|
||||
|
||||
static bool setNonBlocking (int handle)
|
||||
{
|
||||
int socketFlags = fcntl (handle, F_GETFL, 0);
|
||||
if (socketFlags == -1)
|
||||
return false;
|
||||
|
||||
socketFlags |= O_NONBLOCK;
|
||||
return fcntl (handle, F_SETFL, socketFlags) == 0;
|
||||
}
|
||||
|
||||
static bool dispatchNextXEvent()
|
||||
{
|
||||
if (display == 0)
|
||||
return false;
|
||||
|
||||
XEvent evt;
|
||||
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
if (! XPending (display))
|
||||
return false;
|
||||
|
||||
XNextEvent (display, &evt);
|
||||
}
|
||||
|
||||
if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle
|
||||
&& handleSelectionRequest != nullptr)
|
||||
handleSelectionRequest (evt.xselectionrequest);
|
||||
else if (evt.xany.window != juce_messageWindowHandle && dispatchWindowMessage != nullptr)
|
||||
dispatchWindowMessage (evt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MessageManager::MessageBase::Ptr popNextMessage()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (bytesInSocket > 0)
|
||||
{
|
||||
--bytesInSocket;
|
||||
|
||||
const ScopedUnlock ul (lock);
|
||||
unsigned char x;
|
||||
size_t numBytes = read (fd[1], &x, 1);
|
||||
(void) numBytes;
|
||||
}
|
||||
|
||||
return queue.removeAndReturn (0);
|
||||
}
|
||||
|
||||
bool dispatchNextInternalMessage()
|
||||
{
|
||||
if (const MessageManager::MessageBase::Ptr msg = popNextMessage())
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
msg->messageCallback();
|
||||
return true;
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
juce_ImplementSingleton_SingleThreaded (InternalMessageQueue);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
namespace LinuxErrorHandling
|
||||
{
|
||||
static bool errorOccurred = false;
|
||||
static bool keyboardBreakOccurred = false;
|
||||
static XErrorHandler oldErrorHandler = (XErrorHandler) 0;
|
||||
static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0;
|
||||
|
||||
//==============================================================================
|
||||
// Usually happens when client-server connection is broken
|
||||
int ioErrorHandler (Display*)
|
||||
{
|
||||
DBG ("ERROR: connection to X server broken.. terminating.");
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
MessageManager::getInstance()->stopDispatchLoop();
|
||||
|
||||
errorOccurred = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int errorHandler (Display* display, XErrorEvent* event)
|
||||
{
|
||||
#if JUCE_DEBUG_XERRORS
|
||||
char errorStr[64] = { 0 };
|
||||
char requestStr[64] = { 0 };
|
||||
|
||||
XGetErrorText (display, event->error_code, errorStr, 64);
|
||||
XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64);
|
||||
DBG ("ERROR: X returned " << errorStr << " for operation " << requestStr);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void installXErrorHandlers()
|
||||
{
|
||||
oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler);
|
||||
oldErrorHandler = XSetErrorHandler (errorHandler);
|
||||
}
|
||||
|
||||
void removeXErrorHandlers()
|
||||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
XSetIOErrorHandler (oldIOErrorHandler);
|
||||
oldIOErrorHandler = 0;
|
||||
|
||||
XSetErrorHandler (oldErrorHandler);
|
||||
oldErrorHandler = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void keyboardBreakSignalHandler (int sig)
|
||||
{
|
||||
if (sig == SIGINT)
|
||||
keyboardBreakOccurred = true;
|
||||
}
|
||||
|
||||
void installKeyboardBreakHandler()
|
||||
{
|
||||
struct sigaction saction;
|
||||
sigset_t maskSet;
|
||||
sigemptyset (&maskSet);
|
||||
saction.sa_handler = keyboardBreakSignalHandler;
|
||||
saction.sa_mask = maskSet;
|
||||
saction.sa_flags = 0;
|
||||
sigaction (SIGINT, &saction, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
// Initialise xlib for multiple thread support
|
||||
static bool initThreadCalled = false;
|
||||
|
||||
if (! initThreadCalled)
|
||||
{
|
||||
if (! XInitThreads())
|
||||
{
|
||||
// This is fatal! Print error and closedown
|
||||
Logger::outputDebugString ("Failed to initialise xlib thread support.");
|
||||
Process::terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
initThreadCalled = true;
|
||||
}
|
||||
|
||||
LinuxErrorHandling::installXErrorHandlers();
|
||||
LinuxErrorHandling::installKeyboardBreakHandler();
|
||||
}
|
||||
|
||||
// Create the internal message queue
|
||||
InternalMessageQueue::getInstance();
|
||||
|
||||
// Try to connect to a display
|
||||
String displayName (getenv ("DISPLAY"));
|
||||
if (displayName.isEmpty())
|
||||
displayName = ":0.0";
|
||||
|
||||
display = XOpenDisplay (displayName.toUTF8());
|
||||
|
||||
if (display != 0) // This is not fatal! we can run headless.
|
||||
{
|
||||
// Create a context to store user data associated with Windows we create
|
||||
windowHandleXContext = XUniqueContext();
|
||||
|
||||
// We're only interested in client messages for this window, which are always sent
|
||||
XSetWindowAttributes swa;
|
||||
swa.event_mask = NoEventMask;
|
||||
|
||||
// Create our message window (this will never be mapped)
|
||||
const int screen = DefaultScreen (display);
|
||||
juce_messageWindowHandle = XCreateWindow (display, RootWindow (display, screen),
|
||||
0, 0, 1, 1, 0, 0, InputOnly,
|
||||
DefaultVisual (display, screen),
|
||||
CWEventMask, &swa);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
InternalMessageQueue::deleteInstance();
|
||||
|
||||
if (display != 0 && ! LinuxErrorHandling::errorOccurred)
|
||||
{
|
||||
XDestroyWindow (display, juce_messageWindowHandle);
|
||||
XCloseDisplay (display);
|
||||
|
||||
juce_messageWindowHandle = 0;
|
||||
display = nullptr;
|
||||
|
||||
LinuxErrorHandling::removeXErrorHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
if (LinuxErrorHandling::errorOccurred)
|
||||
return false;
|
||||
|
||||
InternalMessageQueue::getInstanceWithoutCreating()->postMessage (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String& /* value */)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
// this function expects that it will NEVER be called simultaneously for two concurrent threads
|
||||
bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
{
|
||||
while (! LinuxErrorHandling::errorOccurred)
|
||||
{
|
||||
if (LinuxErrorHandling::keyboardBreakOccurred)
|
||||
{
|
||||
LinuxErrorHandling::errorOccurred = true;
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
Process::terminate();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
InternalMessageQueue* const queue = InternalMessageQueue::getInstanceWithoutCreating();
|
||||
jassert (queue != nullptr);
|
||||
|
||||
if (queue->dispatchNextEvent())
|
||||
return true;
|
||||
|
||||
if (returnIfNoPendingMessages)
|
||||
break;
|
||||
|
||||
queue->sleepUntilEvent (2000);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
typedef void (*AppFocusChangeCallback)();
|
||||
AppFocusChangeCallback appFocusChangeCallback = nullptr;
|
||||
|
||||
typedef bool (*CheckEventBlockedByModalComps) (NSEvent*);
|
||||
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
|
||||
|
||||
typedef void (*MenuTrackingChangedCallback)(bool);
|
||||
MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
struct AppDelegate
|
||||
{
|
||||
public:
|
||||
AppDelegate()
|
||||
{
|
||||
static AppDelegateClass cls;
|
||||
delegate = [cls.createInstance() init];
|
||||
|
||||
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
[center addObserver: delegate selector: @selector (mainMenuTrackingBegan:)
|
||||
name: NSMenuDidBeginTrackingNotification object: nil];
|
||||
[center addObserver: delegate selector: @selector (mainMenuTrackingEnded:)
|
||||
name: NSMenuDidEndTrackingNotification object: nil];
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
[NSApp setDelegate: delegate];
|
||||
|
||||
[[NSDistributedNotificationCenter defaultCenter] addObserver: delegate
|
||||
selector: @selector (broadcastMessageCallback:)
|
||||
name: getBroacastEventName()
|
||||
object: nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[center addObserver: delegate selector: @selector (applicationDidResignActive:)
|
||||
name: NSApplicationDidResignActiveNotification object: NSApp];
|
||||
|
||||
[center addObserver: delegate selector: @selector (applicationDidBecomeActive:)
|
||||
name: NSApplicationDidBecomeActiveNotification object: NSApp];
|
||||
|
||||
[center addObserver: delegate selector: @selector (applicationWillUnhide:)
|
||||
name: NSApplicationWillUnhideNotification object: NSApp];
|
||||
}
|
||||
}
|
||||
|
||||
~AppDelegate()
|
||||
{
|
||||
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: delegate];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: delegate];
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
[NSApp setDelegate: nil];
|
||||
|
||||
[[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate
|
||||
name: getBroacastEventName()
|
||||
object: nil];
|
||||
}
|
||||
|
||||
[delegate release];
|
||||
}
|
||||
|
||||
static NSString* getBroacastEventName()
|
||||
{
|
||||
return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64()));
|
||||
}
|
||||
|
||||
MessageQueue messageQueue;
|
||||
id delegate;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct AppDelegateClass : public ObjCClass <NSObject>
|
||||
{
|
||||
AppDelegateClass() : ObjCClass <NSObject> ("JUCEAppDelegate_")
|
||||
{
|
||||
addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@");
|
||||
addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@");
|
||||
addMethod (@selector (application:openFile:), application_openFile, "c@:@@");
|
||||
addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@");
|
||||
addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@");
|
||||
addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@");
|
||||
addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@");
|
||||
addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@");
|
||||
addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@");
|
||||
addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@");
|
||||
addMethod (@selector (dummyMethod), dummyMethod, "v@:");
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
private:
|
||||
static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*)
|
||||
{
|
||||
if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
app->systemRequestedQuit();
|
||||
|
||||
if (! MessageManager::getInstance()->hasStopMessageBeenSent())
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
static void applicationWillTerminate (id /*self*/, SEL, NSNotification*)
|
||||
{
|
||||
JUCEApplicationBase::appWillTerminateByForce();
|
||||
}
|
||||
|
||||
static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename)
|
||||
{
|
||||
if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
app->anotherInstanceStarted (quotedIfContainsSpaces (filename));
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames)
|
||||
{
|
||||
if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
StringArray files;
|
||||
|
||||
for (NSString* f in filenames)
|
||||
files.add (quotedIfContainsSpaces (f));
|
||||
|
||||
if (files.size() > 0)
|
||||
app->anotherInstanceStarted (files.joinIntoString (" "));
|
||||
}
|
||||
}
|
||||
|
||||
static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
||||
static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
||||
static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
||||
|
||||
static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n)
|
||||
{
|
||||
NSDictionary* dict = (NSDictionary*) [n userInfo];
|
||||
const String messageString (nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]));
|
||||
MessageManager::getInstance()->deliverBroadcastMessage (messageString);
|
||||
}
|
||||
|
||||
static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*)
|
||||
{
|
||||
if (menuTrackingChangedCallback != nullptr)
|
||||
(*menuTrackingChangedCallback) (true);
|
||||
}
|
||||
|
||||
static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*)
|
||||
{
|
||||
if (menuTrackingChangedCallback != nullptr)
|
||||
(*menuTrackingChangedCallback) (false);
|
||||
}
|
||||
|
||||
static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread)
|
||||
|
||||
private:
|
||||
static void focusChanged()
|
||||
{
|
||||
if (appFocusChangeCallback != nullptr)
|
||||
(*appFocusChangeCallback)();
|
||||
}
|
||||
|
||||
static String quotedIfContainsSpaces (NSString* file)
|
||||
{
|
||||
String s (nsStringToJuce (file));
|
||||
if (s.containsChar (' '))
|
||||
s = s.quoted ('"');
|
||||
|
||||
return s;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::runDispatchLoop()
|
||||
{
|
||||
if (! quitMessagePosted) // check that the quit message wasn't already posted..
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
// must only be called by the message thread!
|
||||
jassert (isThisTheMessageThread());
|
||||
|
||||
#if JUCE_PROJUCER_LIVE_BUILD
|
||||
runDispatchLoopUntil (std::numeric_limits<int>::max());
|
||||
#else
|
||||
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
|
||||
@try
|
||||
{
|
||||
[NSApp run];
|
||||
}
|
||||
@catch (NSException* e)
|
||||
{
|
||||
// An AppKit exception will kill the app, but at least this provides a chance to log it.,
|
||||
std::runtime_error ex (std::string ("NSException: ") + [[e name] UTF8String] + ", Reason:" + [[e reason] UTF8String]);
|
||||
JUCEApplication::sendUnhandledException (&ex, __FILE__, __LINE__);
|
||||
}
|
||||
@finally
|
||||
{
|
||||
}
|
||||
#else
|
||||
[NSApp run];
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdownNSApp()
|
||||
{
|
||||
[NSApp stop: nil];
|
||||
[NSApp activateIgnoringOtherApps: YES]; // (if the app is inactive, it sits there and ignores the quit request until the next time it gets activated)
|
||||
[NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1];
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
quitMessagePosted = true;
|
||||
|
||||
#if ! JUCE_PROJUCER_LIVE_BUILD
|
||||
if (isThisTheMessageThread())
|
||||
{
|
||||
shutdownNSApp();
|
||||
}
|
||||
else
|
||||
{
|
||||
struct QuitCallback : public CallbackMessage
|
||||
{
|
||||
QuitCallback() {}
|
||||
void messageCallback() override { shutdownNSApp(); }
|
||||
};
|
||||
|
||||
(new QuitCallback())->post();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
|
||||
{
|
||||
jassert (millisecondsToRunFor >= 0);
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
|
||||
uint32 endTime = Time::getMillisecondCounter() + (uint32) millisecondsToRunFor;
|
||||
|
||||
while (! quitMessagePosted)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
CFRunLoopRunInMode (kCFRunLoopDefaultMode, 0.001, true);
|
||||
|
||||
NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask
|
||||
untilDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]
|
||||
inMode: NSDefaultRunLoopMode
|
||||
dequeue: YES];
|
||||
|
||||
if (e != nil && (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e)))
|
||||
[NSApp sendEvent: e];
|
||||
|
||||
if (Time::getMillisecondCounter() >= endTime)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ! quitMessagePosted;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
void initialiseNSApplication();
|
||||
void initialiseNSApplication()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[NSApplication sharedApplication];
|
||||
}
|
||||
}
|
||||
|
||||
static AppDelegate* appDelegate = nullptr;
|
||||
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (appDelegate == nil)
|
||||
appDelegate = new AppDelegate();
|
||||
|
||||
#if ! (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
|
||||
// This launches a dummy thread, which forces Cocoa to initialise NSThreads correctly (needed prior to 10.5)
|
||||
if (! [NSThread isMultiThreaded])
|
||||
[NSThread detachNewThreadSelector: @selector (dummyMethod)
|
||||
toTarget: appDelegate->delegate
|
||||
withObject: nil];
|
||||
#endif
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
delete appDelegate;
|
||||
appDelegate = nullptr;
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageBase* message)
|
||||
{
|
||||
jassert (appDelegate != nil);
|
||||
appDelegate->messageQueue.post (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String& message)
|
||||
{
|
||||
NSDictionary* info = [NSDictionary dictionaryWithObject: juceStringToNS (message)
|
||||
forKey: nsStringLiteral ("message")];
|
||||
|
||||
[[NSDistributedNotificationCenter defaultCenter] postNotificationName: AppDelegate::getBroacastEventName()
|
||||
object: nil
|
||||
userInfo: info];
|
||||
}
|
||||
|
||||
// Special function used by some plugin classes to re-post carbon events
|
||||
void repostCurrentNSEvent();
|
||||
void repostCurrentNSEvent()
|
||||
{
|
||||
struct EventReposter : public CallbackMessage
|
||||
{
|
||||
EventReposter() : e ([[NSApp currentEvent] retain]) {}
|
||||
~EventReposter() { [e release]; }
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
[NSApp postEvent: e atStart: YES];
|
||||
}
|
||||
|
||||
NSEvent* e;
|
||||
};
|
||||
|
||||
(new EventReposter())->post();
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
struct MountedVolumeListChangeDetector::Pimpl
|
||||
{
|
||||
Pimpl (MountedVolumeListChangeDetector& d) : owner (d)
|
||||
{
|
||||
static ObserverClass cls;
|
||||
delegate = [cls.createInstance() init];
|
||||
ObserverClass::setOwner (delegate, this);
|
||||
|
||||
NSNotificationCenter* nc = [[NSWorkspace sharedWorkspace] notificationCenter];
|
||||
|
||||
[nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidMountNotification object: nil];
|
||||
[nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidUnmountNotification object: nil];
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver: delegate];
|
||||
[delegate release];
|
||||
}
|
||||
|
||||
private:
|
||||
MountedVolumeListChangeDetector& owner;
|
||||
id delegate;
|
||||
|
||||
struct ObserverClass : public ObjCClass<NSObject>
|
||||
{
|
||||
ObserverClass() : ObjCClass<NSObject> ("JUCEDriveObserver_")
|
||||
{
|
||||
addIvar<Pimpl*> ("owner");
|
||||
addMethod (@selector (changed:), changed, "v@:@");
|
||||
addProtocol (@protocol (NSTextInput));
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static Pimpl* getOwner (id self) { return getIvar<Pimpl*> (self, "owner"); }
|
||||
static void setOwner (id self, Pimpl* owner) { object_setInstanceVariable (self, "owner", owner); }
|
||||
|
||||
static void changed (id self, SEL, NSNotification*)
|
||||
{
|
||||
getOwner (self)->owner.mountedVolumeListChanged();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl = new Pimpl (*this); }
|
||||
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_OSX_MESSAGEQUEUE_H_INCLUDED
|
||||
#define JUCE_OSX_MESSAGEQUEUE_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/* An internal message pump class used in OSX and iOS. */
|
||||
class MessageQueue
|
||||
{
|
||||
public:
|
||||
MessageQueue()
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 && ! JUCE_IOS
|
||||
runLoop = CFRunLoopGetMain();
|
||||
#else
|
||||
runLoop = CFRunLoopGetCurrent();
|
||||
#endif
|
||||
|
||||
CFRunLoopSourceContext sourceContext;
|
||||
zerostruct (sourceContext); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
|
||||
sourceContext.info = this;
|
||||
sourceContext.perform = runLoopSourceCallback;
|
||||
runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext);
|
||||
CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
~MessageQueue()
|
||||
{
|
||||
CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
|
||||
CFRunLoopSourceInvalidate (runLoopSource);
|
||||
CFRelease (runLoopSource);
|
||||
}
|
||||
|
||||
void post (MessageManager::MessageBase* const message)
|
||||
{
|
||||
messages.add (message);
|
||||
CFRunLoopSourceSignal (runLoopSource);
|
||||
CFRunLoopWakeUp (runLoop);
|
||||
}
|
||||
|
||||
private:
|
||||
ReferenceCountedArray <MessageManager::MessageBase, CriticalSection> messages;
|
||||
CFRunLoopRef runLoop;
|
||||
CFRunLoopSourceRef runLoopSource;
|
||||
|
||||
bool deliverNextMessage()
|
||||
{
|
||||
const MessageManager::MessageBase::Ptr nextMessage (messages.removeAndReturn (0));
|
||||
|
||||
if (nextMessage == nullptr)
|
||||
return false;
|
||||
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
nextMessage->messageCallback();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void runLoopCallback()
|
||||
{
|
||||
for (int i = 4; --i >= 0;)
|
||||
if (! deliverNextMessage())
|
||||
return;
|
||||
|
||||
CFRunLoopSourceSignal (runLoopSource);
|
||||
CFRunLoopWakeUp (runLoop);
|
||||
}
|
||||
|
||||
static void runLoopSourceCallback (void* info)
|
||||
{
|
||||
static_cast <MessageQueue*> (info)->runLoopCallback();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // JUCE_OSX_MESSAGEQUEUE_H_INCLUDED
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_WIN32_HIDDENMESSAGEWINDOW_H_INCLUDED
|
||||
#define JUCE_WIN32_HIDDENMESSAGEWINDOW_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
class HiddenMessageWindow
|
||||
{
|
||||
public:
|
||||
HiddenMessageWindow (const TCHAR* const messageWindowName, WNDPROC wndProc)
|
||||
{
|
||||
String className ("JUCE_");
|
||||
className << String::toHexString (Time::getHighResolutionTicks());
|
||||
|
||||
HMODULE moduleHandle = (HMODULE) Process::getCurrentModuleInstanceHandle();
|
||||
|
||||
WNDCLASSEX wc = { 0 };
|
||||
wc.cbSize = sizeof (wc);
|
||||
wc.lpfnWndProc = wndProc;
|
||||
wc.cbWndExtra = 4;
|
||||
wc.hInstance = moduleHandle;
|
||||
wc.lpszClassName = className.toWideCharPointer();
|
||||
|
||||
atom = RegisterClassEx (&wc);
|
||||
jassert (atom != 0);
|
||||
|
||||
hwnd = CreateWindow (getClassNameFromAtom(), messageWindowName,
|
||||
0, 0, 0, 0, 0, 0, 0, moduleHandle, 0);
|
||||
jassert (hwnd != 0);
|
||||
}
|
||||
|
||||
~HiddenMessageWindow()
|
||||
{
|
||||
DestroyWindow (hwnd);
|
||||
UnregisterClass (getClassNameFromAtom(), 0);
|
||||
}
|
||||
|
||||
inline HWND getHWND() const noexcept { return hwnd; }
|
||||
|
||||
private:
|
||||
ATOM atom;
|
||||
HWND hwnd;
|
||||
|
||||
LPCTSTR getClassNameFromAtom() noexcept { return (LPCTSTR) MAKELONG (atom, 0); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class JuceWindowIdentifier
|
||||
{
|
||||
public:
|
||||
static bool isJUCEWindow (HWND hwnd) noexcept
|
||||
{
|
||||
return GetWindowLongPtr (hwnd, GWLP_USERDATA) == getImprobableWindowNumber();
|
||||
}
|
||||
|
||||
static void setAsJUCEWindow (HWND hwnd, bool isJuceWindow) noexcept
|
||||
{
|
||||
SetWindowLongPtr (hwnd, GWLP_USERDATA, isJuceWindow ? getImprobableWindowNumber() : 0);
|
||||
}
|
||||
|
||||
private:
|
||||
static LONG_PTR getImprobableWindowNumber() noexcept
|
||||
{
|
||||
static LONG_PTR number = (LONG_PTR) Random::getSystemRandom().nextInt64();
|
||||
return number;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class DeviceChangeDetector : private Timer
|
||||
{
|
||||
public:
|
||||
DeviceChangeDetector (const wchar_t* const name)
|
||||
: messageWindow (name, (WNDPROC) deviceChangeEventCallback)
|
||||
{
|
||||
SetWindowLongPtr (messageWindow.getHWND(), GWLP_USERDATA, (LONG_PTR) this);
|
||||
}
|
||||
|
||||
virtual ~DeviceChangeDetector() {}
|
||||
|
||||
virtual void systemDeviceChanged() = 0;
|
||||
|
||||
void triggerAsyncDeviceChangeCallback()
|
||||
{
|
||||
// We'll pause before sending a message, because on device removal, the OS hasn't always updated
|
||||
// its device lists correctly at this point. This also helps avoid repeated callbacks.
|
||||
startTimer (500);
|
||||
}
|
||||
|
||||
private:
|
||||
HiddenMessageWindow messageWindow;
|
||||
|
||||
static LRESULT CALLBACK deviceChangeEventCallback (HWND h, const UINT message,
|
||||
const WPARAM wParam, const LPARAM lParam)
|
||||
{
|
||||
if (message == WM_DEVICECHANGE
|
||||
&& (wParam == 0x8000 /*DBT_DEVICEARRIVAL*/
|
||||
|| wParam == 0x8004 /*DBT_DEVICEREMOVECOMPLETE*/
|
||||
|| wParam == 0x0007 /*DBT_DEVNODES_CHANGED*/))
|
||||
{
|
||||
((DeviceChangeDetector*) GetWindowLongPtr (h, GWLP_USERDATA))
|
||||
->triggerAsyncDeviceChangeCallback();
|
||||
}
|
||||
|
||||
return DefWindowProc (h, message, wParam, lParam);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
systemDeviceChanged();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // JUCE_WIN32_HIDDENMESSAGEWINDOW_H_INCLUDED
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
extern HWND juce_messageWindowHandle;
|
||||
|
||||
typedef bool (*CheckEventBlockedByModalComps) (const MSG&);
|
||||
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
namespace WindowsMessageHelpers
|
||||
{
|
||||
const unsigned int specialId = WM_APP + 0x4400;
|
||||
const unsigned int broadcastId = WM_APP + 0x4403;
|
||||
|
||||
const TCHAR messageWindowName[] = _T("JUCEWindow");
|
||||
ScopedPointer<HiddenMessageWindow> messageWindow;
|
||||
|
||||
void dispatchMessageFromLParam (LPARAM lParam)
|
||||
{
|
||||
MessageManager::MessageBase* const message = reinterpret_cast <MessageManager::MessageBase*> (lParam);
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
message->messageCallback();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
message->decReferenceCount();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LRESULT CALLBACK messageWndProc (HWND h, const UINT message, const WPARAM wParam, const LPARAM lParam) noexcept
|
||||
{
|
||||
if (h == juce_messageWindowHandle)
|
||||
{
|
||||
if (message == specialId)
|
||||
{
|
||||
// (These are trapped early in our dispatch loop, but must also be checked
|
||||
// here in case some 3rd-party code is running the dispatch loop).
|
||||
dispatchMessageFromLParam (lParam);
|
||||
return 0;
|
||||
}
|
||||
else if (message == broadcastId)
|
||||
{
|
||||
const ScopedPointer<String> messageString ((String*) lParam);
|
||||
MessageManager::getInstance()->deliverBroadcastMessage (*messageString);
|
||||
return 0;
|
||||
}
|
||||
else if (message == WM_COPYDATA)
|
||||
{
|
||||
const COPYDATASTRUCT* const data = reinterpret_cast <const COPYDATASTRUCT*> (lParam);
|
||||
|
||||
if (data->dwData == broadcastId)
|
||||
{
|
||||
const String messageString (CharPointer_UTF32 ((const CharPointer_UTF32::CharType*) data->lpData),
|
||||
data->cbData / sizeof (CharPointer_UTF32::CharType));
|
||||
|
||||
PostMessage (juce_messageWindowHandle, broadcastId, 0, (LPARAM) new String (messageString));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc (h, message, wParam, lParam);
|
||||
}
|
||||
|
||||
BOOL CALLBACK broadcastEnumWindowProc (HWND hwnd, LPARAM lParam)
|
||||
{
|
||||
if (hwnd != juce_messageWindowHandle)
|
||||
reinterpret_cast <Array<HWND>*> (lParam)->add (hwnd);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages)
|
||||
{
|
||||
using namespace WindowsMessageHelpers;
|
||||
MSG m;
|
||||
|
||||
if (returnIfNoPendingMessages && ! PeekMessage (&m, (HWND) 0, 0, 0, PM_NOREMOVE))
|
||||
return false;
|
||||
|
||||
if (GetMessage (&m, (HWND) 0, 0, 0) >= 0)
|
||||
{
|
||||
if (m.message == specialId && m.hwnd == juce_messageWindowHandle)
|
||||
{
|
||||
dispatchMessageFromLParam (m.lParam);
|
||||
}
|
||||
else if (m.message == WM_QUIT)
|
||||
{
|
||||
if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
|
||||
app->systemRequestedQuit();
|
||||
}
|
||||
else if (isEventBlockedByModalComps == nullptr || ! isEventBlockedByModalComps (m))
|
||||
{
|
||||
if ((m.message == WM_LBUTTONDOWN || m.message == WM_RBUTTONDOWN)
|
||||
&& ! JuceWindowIdentifier::isJUCEWindow (m.hwnd))
|
||||
{
|
||||
// if it's someone else's window being clicked on, and the focus is
|
||||
// currently on a juce window, pass the kb focus over..
|
||||
HWND currentFocus = GetFocus();
|
||||
|
||||
if (currentFocus == 0 || JuceWindowIdentifier::isJUCEWindow (currentFocus))
|
||||
SetFocus (m.hwnd);
|
||||
}
|
||||
|
||||
TranslateMessage (&m);
|
||||
DispatchMessage (&m);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
message->incReferenceCount();
|
||||
return PostMessage (juce_messageWindowHandle, WindowsMessageHelpers::specialId, 0, (LPARAM) message) != 0;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String& value)
|
||||
{
|
||||
Array<HWND> windows;
|
||||
EnumWindows (&WindowsMessageHelpers::broadcastEnumWindowProc, (LPARAM) &windows);
|
||||
|
||||
const String localCopy (value);
|
||||
|
||||
COPYDATASTRUCT data;
|
||||
data.dwData = WindowsMessageHelpers::broadcastId;
|
||||
data.cbData = (localCopy.length() + 1) * sizeof (CharPointer_UTF32::CharType);
|
||||
data.lpData = (void*) localCopy.toUTF32().getAddress();
|
||||
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
{
|
||||
HWND hwnd = windows.getUnchecked(i);
|
||||
|
||||
TCHAR windowName [64]; // no need to read longer strings than this
|
||||
GetWindowText (hwnd, windowName, 64);
|
||||
windowName [63] = 0;
|
||||
|
||||
if (String (windowName) == WindowsMessageHelpers::messageWindowName)
|
||||
{
|
||||
DWORD_PTR result;
|
||||
SendMessageTimeout (hwnd, WM_COPYDATA,
|
||||
(WPARAM) juce_messageWindowHandle,
|
||||
(LPARAM) &data,
|
||||
SMTO_BLOCK | SMTO_ABORTIFHUNG, 8000, &result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
OleInitialize (0);
|
||||
|
||||
using namespace WindowsMessageHelpers;
|
||||
messageWindow = new HiddenMessageWindow (messageWindowName, (WNDPROC) messageWndProc);
|
||||
juce_messageWindowHandle = messageWindow->getHWND();
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
WindowsMessageHelpers::messageWindow = nullptr;
|
||||
|
||||
OleUninitialize();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct MountedVolumeListChangeDetector::Pimpl : private DeviceChangeDetector
|
||||
{
|
||||
Pimpl (MountedVolumeListChangeDetector& d) : DeviceChangeDetector (L"MountedVolumeList"), owner (d)
|
||||
{
|
||||
File::findFileSystemRoots (lastVolumeList);
|
||||
}
|
||||
|
||||
void systemDeviceChanged() override
|
||||
{
|
||||
Array<File> newList;
|
||||
File::findFileSystemRoots (newList);
|
||||
|
||||
if (lastVolumeList != newList)
|
||||
{
|
||||
lastVolumeList = newList;
|
||||
owner.mountedVolumeListChanged();
|
||||
}
|
||||
}
|
||||
|
||||
MountedVolumeListChangeDetector& owner;
|
||||
Array<File> lastVolumeList;
|
||||
};
|
||||
|
||||
MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl = new Pimpl (*this); }
|
||||
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue