git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
54
deps/juce/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.cpp
vendored
Normal file
54
deps/juce/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.cpp
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_MAC
|
||||
|
||||
class ScopedLowPowerModeDisabler::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl() = default;
|
||||
~Pimpl() { [[NSProcessInfo processInfo] endActivity: activity]; }
|
||||
|
||||
private:
|
||||
id activity { [[NSProcessInfo processInfo] beginActivityWithOptions: NSActivityUserInitiatedAllowingIdleSystemSleep
|
||||
reason: @"App must remain in high-power mode"] };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Pimpl)
|
||||
JUCE_DECLARE_NON_MOVEABLE (Pimpl)
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class ScopedLowPowerModeDisabler::Pimpl {};
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
ScopedLowPowerModeDisabler::ScopedLowPowerModeDisabler()
|
||||
: pimpl (std::make_unique<Pimpl>()) {}
|
||||
|
||||
ScopedLowPowerModeDisabler::~ScopedLowPowerModeDisabler() = default;
|
||||
|
||||
} // namespace juce
|
49
deps/juce/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.h
vendored
Normal file
49
deps/juce/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.h
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Disables low-power-mode for the duration of an instance's lifetime.
|
||||
|
||||
Currently this is only implemented on macOS, where it will disable the
|
||||
"App Nap" power-saving feature.
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class ScopedLowPowerModeDisabler
|
||||
{
|
||||
public:
|
||||
ScopedLowPowerModeDisabler();
|
||||
~ScopedLowPowerModeDisabler();
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ScopedLowPowerModeDisabler)
|
||||
JUCE_DECLARE_NON_MOVEABLE (ScopedLowPowerModeDisabler)
|
||||
};
|
||||
|
||||
} // namespace juce
|
301
deps/juce/modules/juce_events/native/juce_android_Messaging.cpp
vendored
Normal file
301
deps/juce/modules/juce_events/native/juce_android_Messaging.cpp
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
namespace Android
|
||||
{
|
||||
class Runnable : public juce::AndroidInterfaceImplementer
|
||||
{
|
||||
public:
|
||||
virtual void run() = 0;
|
||||
|
||||
private:
|
||||
jobject invoke (jobject proxy, jobject method, jobjectArray args) override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
auto methodName = juce::juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
|
||||
|
||||
if (methodName == "run")
|
||||
{
|
||||
run();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// invoke base class
|
||||
return AndroidInterfaceImplementer::invoke (proxy, method, args);
|
||||
}
|
||||
};
|
||||
|
||||
struct Handler
|
||||
{
|
||||
Handler() : nativeHandler (LocalRef<jobject> (getEnv()->NewObject (AndroidHandler, AndroidHandler.constructor))) {}
|
||||
~Handler() { clearSingletonInstance(); }
|
||||
|
||||
JUCE_DECLARE_SINGLETON (Handler, false)
|
||||
|
||||
bool post (jobject runnable)
|
||||
{
|
||||
return (getEnv()->CallBooleanMethod (nativeHandler.get(), AndroidHandler.post, runnable) != 0);
|
||||
}
|
||||
|
||||
GlobalRef nativeHandler;
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (Handler)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct AndroidMessageQueue : private Android::Runnable
|
||||
{
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED (AndroidMessageQueue, true)
|
||||
|
||||
AndroidMessageQueue()
|
||||
: self (CreateJavaInterface (this, "java/lang/Runnable"))
|
||||
{
|
||||
}
|
||||
|
||||
~AndroidMessageQueue() override
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_THREAD
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
bool post (MessageManager::MessageBase::Ptr&& message)
|
||||
{
|
||||
queue.add (std::move (message));
|
||||
|
||||
// this will call us on the message thread
|
||||
return handler.post (self.get());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void run() override
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
MessageManager::MessageBase::Ptr message (queue.removeAndReturn (0));
|
||||
|
||||
if (message == nullptr)
|
||||
break;
|
||||
|
||||
message->messageCallback();
|
||||
}
|
||||
}
|
||||
|
||||
// the this pointer to this class in Java land
|
||||
GlobalRef self;
|
||||
|
||||
ReferenceCountedArray<MessageManager::MessageBase, CriticalSection> queue;
|
||||
Android::Handler handler;
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (AndroidMessageQueue)
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::doPlatformSpecificInitialisation() { AndroidMessageQueue::getInstance(); }
|
||||
void MessageManager::doPlatformSpecificShutdown() { AndroidMessageQueue::deleteInstance(); }
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
return AndroidMessageQueue::getInstance()->post (message);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::broadcastMessage (const String&)
|
||||
{
|
||||
}
|
||||
|
||||
void MessageManager::runDispatchLoop()
|
||||
{
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
struct QuitCallback : public CallbackMessage
|
||||
{
|
||||
QuitCallback() {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> activity (getCurrentActivity());
|
||||
|
||||
if (activity != nullptr)
|
||||
{
|
||||
jmethodID quitMethod = env->GetMethodID (AndroidActivity, "finishAndRemoveTask", "()V");
|
||||
|
||||
if (quitMethod != nullptr)
|
||||
{
|
||||
env->CallVoidMethod (activity.get(), quitMethod);
|
||||
return;
|
||||
}
|
||||
|
||||
quitMethod = env->GetMethodID (AndroidActivity, "finish", "()V");
|
||||
jassert (quitMethod != nullptr);
|
||||
env->CallVoidMethod (activity.get(), quitMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(new QuitCallback())->post();
|
||||
quitMessagePosted = true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class JuceAppLifecycle : public ActivityLifecycleCallbacks
|
||||
{
|
||||
public:
|
||||
JuceAppLifecycle (juce::JUCEApplicationBase* (*initSymbolAddr)())
|
||||
: createApplicationSymbol (initSymbolAddr)
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
|
||||
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
|
||||
}
|
||||
}
|
||||
|
||||
~JuceAppLifecycle() override
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr && myself != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
clear();
|
||||
env->CallVoidMethod (appContext.get(), AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
|
||||
myself.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void onActivityCreated (jobject, jobject) override
|
||||
{
|
||||
checkCreated();
|
||||
}
|
||||
|
||||
void onActivityDestroyed (jobject activity) override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
// if the main activity is being destroyed, only then tear-down JUCE
|
||||
if (env->IsSameObject (getMainActivity().get(), activity) != 0)
|
||||
{
|
||||
JUCEApplicationBase::appWillTerminateByForce();
|
||||
JNIClassBase::releaseAllClasses (env);
|
||||
|
||||
jclass systemClass = (jclass) env->FindClass ("java/lang/System");
|
||||
jmethodID exitMethod = env->GetStaticMethodID (systemClass, "exit", "(I)V");
|
||||
env->CallStaticVoidMethod (systemClass, exitMethod, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void onActivityStarted (jobject) override
|
||||
{
|
||||
checkCreated();
|
||||
}
|
||||
|
||||
void onActivityPaused (jobject) override
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
app->suspended();
|
||||
}
|
||||
|
||||
void onActivityResumed (jobject) override
|
||||
{
|
||||
checkInitialised();
|
||||
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
app->resumed();
|
||||
}
|
||||
|
||||
static JuceAppLifecycle& getInstance (juce::JUCEApplicationBase* (*initSymbolAddr)())
|
||||
{
|
||||
static JuceAppLifecycle juceAppLifecycle (initSymbolAddr);
|
||||
return juceAppLifecycle;
|
||||
}
|
||||
|
||||
private:
|
||||
void checkCreated()
|
||||
{
|
||||
if (JUCEApplicationBase::getInstance() == nullptr)
|
||||
{
|
||||
DBG (SystemStats::getJUCEVersion());
|
||||
|
||||
JUCEApplicationBase::createInstance = createApplicationSymbol;
|
||||
|
||||
initialiseJuce_GUI();
|
||||
|
||||
if (! JUCEApplicationBase::createInstance())
|
||||
jassertfalse; // you must supply an application object for an android app!
|
||||
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
}
|
||||
}
|
||||
|
||||
void checkInitialised()
|
||||
{
|
||||
checkCreated();
|
||||
|
||||
if (! hasBeenInitialised)
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
hasBeenInitialised = app->initialiseApp();
|
||||
|
||||
if (! hasBeenInitialised)
|
||||
exit (app->shutdownApp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobalRef myself;
|
||||
juce::JUCEApplicationBase* (*createApplicationSymbol)();
|
||||
bool hasBeenInitialised = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
File juce_getExecutableFile();
|
||||
|
||||
void juce_juceEventsAndroidStartApp()
|
||||
{
|
||||
auto dllPath = juce_getExecutableFile().getFullPathName();
|
||||
auto addr = reinterpret_cast<juce::JUCEApplicationBase*(*)()> (DynamicLibrary (dllPath)
|
||||
.getFunction ("juce_CreateApplication"));
|
||||
|
||||
if (addr != nullptr)
|
||||
JuceAppLifecycle::getInstance (addr);
|
||||
}
|
||||
|
||||
} // namespace juce
|
103
deps/juce/modules/juce_events/native/juce_ios_MessageManager.mm
vendored
Normal file
103
deps/juce/modules/juce_events/native/juce_ios_MessageManager.mm
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
void MessageManager::runDispatchLoop()
|
||||
{
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
|
||||
while (quitMessagePosted.get() == 0)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
|
||||
beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
if (! SystemStats::isRunningInAppExtensionSandbox())
|
||||
[[[UIApplication sharedApplication] delegate] applicationWillTerminate: [UIApplication sharedApplication]];
|
||||
|
||||
exit (0); // iOS apps get no mercy..
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
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.get() == 0)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
|
||||
beforeDate: endDate];
|
||||
|
||||
if (millisecondsToRunFor >= 0
|
||||
&& Time::getMillisecondCounter() >= startTime + (uint32) millisecondsToRunFor)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return quitMessagePosted.get() == 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
static std::unique_ptr<MessageQueue> messageQueue;
|
||||
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (messageQueue == nullptr)
|
||||
messageQueue.reset (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
|
||||
}
|
||||
|
||||
} // namespace juce
|
50
deps/juce/modules/juce_events/native/juce_linux_EventLoop.h
vendored
Normal file
50
deps/juce/modules/juce_events/native/juce_linux_EventLoop.h
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace LinuxEventLoop
|
||||
{
|
||||
/** Registers a callback that will be called when a file descriptor is ready for I/O.
|
||||
|
||||
This will add the given file descriptor to the internal set of file descriptors
|
||||
that will be passed to the poll() call. When this file descriptor has data to read
|
||||
the readCallback will be called.
|
||||
|
||||
@param fd the file descriptor to be monitored
|
||||
@param readCallback a callback that will be called when the file descriptor has
|
||||
data to read. The file descriptor will be passed as an argument
|
||||
@param eventMask a bit mask specifying the events you are interested in for the
|
||||
file descriptor. The possible values for this are defined in
|
||||
<poll.h>
|
||||
*/
|
||||
void registerFdCallback (int fd, std::function<void (int)> readCallback, short eventMask = 1 /*POLLIN*/);
|
||||
|
||||
/** Unregisters a previously registered file descriptor.
|
||||
|
||||
@see registerFdCallback
|
||||
*/
|
||||
void unregisterFdCallback (int fd);
|
||||
}
|
||||
|
||||
} // namespace juce
|
336
deps/juce/modules/juce_events/native/juce_linux_Messaging.cpp
vendored
Normal file
336
deps/juce/modules/juce_events/native/juce_linux_Messaging.cpp
vendored
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class InternalMessageQueue
|
||||
{
|
||||
public:
|
||||
InternalMessageQueue()
|
||||
{
|
||||
auto err = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, msgpipe);
|
||||
jassertquiet (err == 0);
|
||||
|
||||
LinuxEventLoop::registerFdCallback (getReadHandle(),
|
||||
[this] (int fd)
|
||||
{
|
||||
while (auto msg = popNextMessage (fd))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
msg->messageCallback();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
~InternalMessageQueue()
|
||||
{
|
||||
LinuxEventLoop::unregisterFdCallback (getReadHandle());
|
||||
|
||||
close (getReadHandle());
|
||||
close (getWriteHandle());
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void postMessage (MessageManager::MessageBase* const msg) noexcept
|
||||
{
|
||||
ScopedLock sl (lock);
|
||||
queue.add (msg);
|
||||
|
||||
if (bytesInSocket < maxBytesInSocketQueue)
|
||||
{
|
||||
bytesInSocket++;
|
||||
|
||||
ScopedUnlock ul (lock);
|
||||
unsigned char x = 0xff;
|
||||
auto numBytes = write (getWriteHandle(), &x, 1);
|
||||
ignoreUnused (numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_SINGLETON (InternalMessageQueue, false)
|
||||
|
||||
private:
|
||||
CriticalSection lock;
|
||||
ReferenceCountedArray <MessageManager::MessageBase> queue;
|
||||
|
||||
int msgpipe[2];
|
||||
int bytesInSocket = 0;
|
||||
static constexpr int maxBytesInSocketQueue = 128;
|
||||
|
||||
int getWriteHandle() const noexcept { return msgpipe[0]; }
|
||||
int getReadHandle() const noexcept { return msgpipe[1]; }
|
||||
|
||||
MessageManager::MessageBase::Ptr popNextMessage (int fd) noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (bytesInSocket > 0)
|
||||
{
|
||||
--bytesInSocket;
|
||||
|
||||
ScopedUnlock ul (lock);
|
||||
unsigned char x;
|
||||
auto numBytes = read (fd, &x, 1);
|
||||
ignoreUnused (numBytes);
|
||||
}
|
||||
|
||||
return queue.removeAndReturn (0);
|
||||
}
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue)
|
||||
|
||||
//==============================================================================
|
||||
struct InternalRunLoop
|
||||
{
|
||||
public:
|
||||
InternalRunLoop()
|
||||
{
|
||||
fdReadCallbacks.reserve (16);
|
||||
}
|
||||
|
||||
void registerFdCallback (int fd, std::function<void (int)>&& cb, short eventMask)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (shouldDeferModifyingReadCallbacks)
|
||||
{
|
||||
deferredReadCallbackModifications.emplace_back ([this, fd, cb, eventMask]() mutable
|
||||
{
|
||||
registerFdCallback (fd, std::move (cb), eventMask);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
fdReadCallbacks.push_back ({ fd, std::move (cb) });
|
||||
pfds.push_back ({ fd, eventMask, 0 });
|
||||
}
|
||||
|
||||
void unregisterFdCallback (int fd)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (shouldDeferModifyingReadCallbacks)
|
||||
{
|
||||
deferredReadCallbackModifications.emplace_back ([this, fd] { unregisterFdCallback (fd); });
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto removePredicate = [=] (const std::pair<int, std::function<void (int)>>& cb) { return cb.first == fd; };
|
||||
|
||||
fdReadCallbacks.erase (std::remove_if (std::begin (fdReadCallbacks), std::end (fdReadCallbacks), removePredicate),
|
||||
std::end (fdReadCallbacks));
|
||||
}
|
||||
|
||||
{
|
||||
auto removePredicate = [=] (const pollfd& pfd) { return pfd.fd == fd; };
|
||||
|
||||
pfds.erase (std::remove_if (std::begin (pfds), std::end (pfds), removePredicate),
|
||||
std::end (pfds));
|
||||
}
|
||||
}
|
||||
|
||||
bool dispatchPendingEvents()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (poll (&pfds.front(), static_cast<nfds_t> (pfds.size()), 0) == 0)
|
||||
return false;
|
||||
|
||||
bool eventWasSent = false;
|
||||
|
||||
for (auto& pfd : pfds)
|
||||
{
|
||||
if (pfd.revents == 0)
|
||||
continue;
|
||||
|
||||
pfd.revents = 0;
|
||||
|
||||
auto fd = pfd.fd;
|
||||
|
||||
for (auto& fdAndCallback : fdReadCallbacks)
|
||||
{
|
||||
if (fdAndCallback.first == fd)
|
||||
{
|
||||
{
|
||||
ScopedValueSetter<bool> insideFdReadCallback (shouldDeferModifyingReadCallbacks, true);
|
||||
fdAndCallback.second (fd);
|
||||
}
|
||||
|
||||
if (! deferredReadCallbackModifications.empty())
|
||||
{
|
||||
for (auto& deferredRegisterEvent : deferredReadCallbackModifications)
|
||||
deferredRegisterEvent();
|
||||
|
||||
deferredReadCallbackModifications.clear();
|
||||
|
||||
// elements may have been removed from the fdReadCallbacks/pfds array so we really need
|
||||
// to call poll again
|
||||
return true;
|
||||
}
|
||||
|
||||
eventWasSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return eventWasSent;
|
||||
}
|
||||
|
||||
void sleepUntilNextEvent (int timeoutMs)
|
||||
{
|
||||
poll (&pfds.front(), static_cast<nfds_t> (pfds.size()), timeoutMs);
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, std::function<void (int)>>> getFdReadCallbacks()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return fdReadCallbacks;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_SINGLETON (InternalRunLoop, false)
|
||||
|
||||
private:
|
||||
CriticalSection lock;
|
||||
|
||||
std::vector<std::pair<int, std::function<void (int)>>> fdReadCallbacks;
|
||||
std::vector<pollfd> pfds;
|
||||
|
||||
bool shouldDeferModifyingReadCallbacks = false;
|
||||
std::vector<std::function<void()>> deferredReadCallbackModifications;
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (InternalRunLoop)
|
||||
|
||||
//==============================================================================
|
||||
namespace LinuxErrorHandling
|
||||
{
|
||||
static bool keyboardBreakOccurred = false;
|
||||
|
||||
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, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
LinuxErrorHandling::installKeyboardBreakHandler();
|
||||
|
||||
InternalRunLoop::getInstance();
|
||||
InternalMessageQueue::getInstance();
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
InternalMessageQueue::deleteInstance();
|
||||
InternalRunLoop::deleteInstance();
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
{
|
||||
queue->postMessage (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String&)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
// this function expects that it will NEVER be called simultaneously for two concurrent threads
|
||||
bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (LinuxErrorHandling::keyboardBreakOccurred)
|
||||
JUCEApplicationBase::quit();
|
||||
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
{
|
||||
if (runLoop->dispatchPendingEvents())
|
||||
break;
|
||||
|
||||
if (returnIfNoPendingMessages)
|
||||
return false;
|
||||
|
||||
runLoop->sleepUntilNextEvent (2000);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void LinuxEventLoop::registerFdCallback (int fd, std::function<void (int)> readCallback, short eventMask)
|
||||
{
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
runLoop->registerFdCallback (fd, std::move (readCallback), eventMask);
|
||||
}
|
||||
|
||||
void LinuxEventLoop::unregisterFdCallback (int fd)
|
||||
{
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
runLoop->unregisterFdCallback (fd);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
||||
JUCE_API std::vector<std::pair<int, std::function<void (int)>>> getFdReadCallbacks()
|
||||
{
|
||||
using namespace juce;
|
||||
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
return runLoop->getFdReadCallbacks();
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
539
deps/juce/modules/juce_events/native/juce_mac_MessageManager.mm
vendored
Normal file
539
deps/juce/modules/juce_events/native/juce_mac_MessageManager.mm
vendored
Normal file
@@ -0,0 +1,539 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
using AppFocusChangeCallback = void (*)();
|
||||
AppFocusChangeCallback appFocusChangeCallback = nullptr;
|
||||
|
||||
using CheckEventBlockedByModalComps = bool (*)(NSEvent*);
|
||||
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
|
||||
|
||||
using MenuTrackingChangedCallback = void (*)(bool);
|
||||
MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
struct AppDelegateClass : public ObjCClass<NSObject>
|
||||
{
|
||||
AppDelegateClass() : ObjCClass<NSObject> ("JUCEAppDelegate_")
|
||||
{
|
||||
addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@");
|
||||
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@:@");
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@");
|
||||
addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@");
|
||||
addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@");
|
||||
addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@");
|
||||
addMethod (@selector (dummyMethod), dummyMethod, "v@:");
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
//==============================================================================
|
||||
addIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("pushNotificationsDelegate");
|
||||
|
||||
addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@");
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate, "v@:@");
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications, "v@:@@");
|
||||
addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications, "v@:@@");
|
||||
addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification, "v@:@@");
|
||||
#endif
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
private:
|
||||
static void applicationWillFinishLaunching (id self, SEL, NSNotification*)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
[[NSAppleEventManager sharedAppleEventManager] setEventHandler: self
|
||||
andSelector: @selector (getUrl:withReplyEvent:)
|
||||
forEventClass: kInternetEventClass
|
||||
andEventID: kAEGetURL];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
static void applicationDidFinishLaunching (id self, SEL, NSNotification* notification)
|
||||
{
|
||||
if (notification.userInfo != nil)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
||||
// NSUserNotification is deprecated from macOS 11, but there doesn't seem to be a
|
||||
// replacement for NSApplicationLaunchUserNotificationKey returning a non-deprecated type
|
||||
NSUserNotification* userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
if (userNotification != nil && userNotification.userInfo != nil)
|
||||
didReceiveRemoteNotification (self, nil, [NSApplication sharedApplication], userNotification.userInfo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*)
|
||||
{
|
||||
if (auto* 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 (auto* app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
app->anotherInstanceStarted (quotedIfContainsSpaces (filename));
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames)
|
||||
{
|
||||
if (auto* 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];
|
||||
auto 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)
|
||||
|
||||
static void focusChanged()
|
||||
{
|
||||
if (appFocusChangeCallback != nullptr)
|
||||
(*appFocusChangeCallback)();
|
||||
}
|
||||
|
||||
static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*)
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue]));
|
||||
}
|
||||
|
||||
static String quotedIfContainsSpaces (NSString* file)
|
||||
{
|
||||
String s (nsStringToJuce (file));
|
||||
s = s.unquoted().replace ("\"", "\\\"");
|
||||
|
||||
if (s.containsChar (' '))
|
||||
s = s.quoted();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
//==============================================================================
|
||||
static void setPushNotificationsDelegate (id self, SEL, NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* delegate)
|
||||
{
|
||||
object_setInstanceVariable (self, "pushNotificationsDelegate", delegate);
|
||||
}
|
||||
|
||||
static NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* getPushNotificationsDelegate (id self)
|
||||
{
|
||||
return getIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> (self, "pushNotificationsDelegate");
|
||||
}
|
||||
|
||||
static void registeredForRemoteNotifications (id self, SEL, NSApplication* application, NSData* deviceToken)
|
||||
{
|
||||
auto* delegate = getPushNotificationsDelegate (self);
|
||||
|
||||
SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:);
|
||||
|
||||
if (delegate != nil && [delegate respondsToSelector: selector])
|
||||
{
|
||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
||||
[invocation setSelector: selector];
|
||||
[invocation setTarget: delegate];
|
||||
[invocation setArgument: &application atIndex:2];
|
||||
[invocation setArgument: &deviceToken atIndex:3];
|
||||
|
||||
[invocation invoke];
|
||||
}
|
||||
}
|
||||
|
||||
static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* application, NSError* error)
|
||||
{
|
||||
auto* delegate = getPushNotificationsDelegate (self);
|
||||
|
||||
SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:);
|
||||
|
||||
if (delegate != nil && [delegate respondsToSelector: selector])
|
||||
{
|
||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
||||
[invocation setSelector: selector];
|
||||
[invocation setTarget: delegate];
|
||||
[invocation setArgument: &application atIndex:2];
|
||||
[invocation setArgument: &error atIndex:3];
|
||||
|
||||
[invocation invoke];
|
||||
}
|
||||
}
|
||||
|
||||
static void didReceiveRemoteNotification (id self, SEL, NSApplication* application, NSDictionary* userInfo)
|
||||
{
|
||||
auto* delegate = getPushNotificationsDelegate (self);
|
||||
|
||||
SEL selector = @selector (application:didReceiveRemoteNotification:);
|
||||
|
||||
if (delegate != nil && [delegate respondsToSelector: selector])
|
||||
{
|
||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
||||
[invocation setSelector: selector];
|
||||
[invocation setTarget: delegate];
|
||||
[invocation setArgument: &application atIndex:2];
|
||||
[invocation setArgument: &userInfo atIndex:3];
|
||||
|
||||
[invocation invoke];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// This is declared at file scope, so that it's guaranteed to be
|
||||
// constructed before and destructed after `appDelegate` (below)
|
||||
static AppDelegateClass appDelegateClass;
|
||||
|
||||
//==============================================================================
|
||||
struct AppDelegate
|
||||
{
|
||||
public:
|
||||
AppDelegate()
|
||||
{
|
||||
delegate = [appDelegateClass.createInstance() init];
|
||||
|
||||
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
[center addObserver: delegate selector: @selector (mainMenuTrackingBegan:)
|
||||
name: NSMenuDidBeginTrackingNotification object: nil];
|
||||
[center addObserver: delegate selector: @selector (mainMenuTrackingEnded:)
|
||||
name: NSMenuDidEndTrackingNotification object: nil];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
[NSApp setDelegate: delegate];
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
[[NSDistributedNotificationCenter defaultCenter] addObserver: delegate
|
||||
selector: @selector (broadcastMessageCallback:)
|
||||
name: getBroadcastEventName()
|
||||
object: nil
|
||||
suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
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: getBroadcastEventName()
|
||||
object: nil];
|
||||
}
|
||||
|
||||
[delegate release];
|
||||
}
|
||||
|
||||
static NSString* getBroadcastEventName()
|
||||
{
|
||||
return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64()));
|
||||
}
|
||||
|
||||
MessageQueue messageQueue;
|
||||
id delegate;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::runDispatchLoop()
|
||||
{
|
||||
if (quitMessagePosted.get() == 0) // check that the quit message wasn't already posted..
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
// must only be called by the message thread!
|
||||
jassert (isThisTheMessageThread());
|
||||
|
||||
#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]);
|
||||
JUCEApplicationBase::sendUnhandledException (&ex, __FILE__, __LINE__);
|
||||
}
|
||||
@finally
|
||||
{
|
||||
}
|
||||
#else
|
||||
[NSApp run];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdownNSApp()
|
||||
{
|
||||
[NSApp stop: nil];
|
||||
[NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1];
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
if (isThisTheMessageThread())
|
||||
{
|
||||
quitMessagePosted = true;
|
||||
shutdownNSApp();
|
||||
}
|
||||
else
|
||||
{
|
||||
struct QuitCallback : public CallbackMessage
|
||||
{
|
||||
QuitCallback() {}
|
||||
void messageCallback() override { MessageManager::getInstance()->stopDispatchLoop(); }
|
||||
};
|
||||
|
||||
(new QuitCallback())->post();
|
||||
}
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
|
||||
{
|
||||
jassert (millisecondsToRunFor >= 0);
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
|
||||
auto endTime = Time::currentTimeMillis() + millisecondsToRunFor;
|
||||
|
||||
while (quitMessagePosted.get() == 0)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
auto msRemaining = endTime - Time::currentTimeMillis();
|
||||
|
||||
if (msRemaining <= 0)
|
||||
break;
|
||||
|
||||
CFRunLoopRunInMode (kCFRunLoopDefaultMode, jmin (1.0, msRemaining * 0.001), true);
|
||||
|
||||
if (NSEvent* e = [NSApp nextEventMatchingMask: NSEventMaskAny
|
||||
untilDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]
|
||||
inMode: NSDefaultRunLoopMode
|
||||
dequeue: YES])
|
||||
if (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e))
|
||||
[NSApp sendEvent: e];
|
||||
}
|
||||
}
|
||||
|
||||
return quitMessagePosted.get() == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
void initialiseNSApplication();
|
||||
void initialiseNSApplication()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[NSApplication sharedApplication];
|
||||
}
|
||||
}
|
||||
|
||||
static std::unique_ptr<AppDelegate> appDelegate;
|
||||
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (appDelegate == nil)
|
||||
appDelegate.reset (new AppDelegate());
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
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::getBroadcastEventName()
|
||||
object: nil
|
||||
userInfo: info];
|
||||
}
|
||||
|
||||
// Special function used by some plugin classes to re-post carbon events
|
||||
void __attribute__ ((visibility("default"))) repostCurrentNSEvent();
|
||||
void __attribute__ ((visibility("default"))) repostCurrentNSEvent()
|
||||
{
|
||||
struct EventReposter : public CallbackMessage
|
||||
{
|
||||
EventReposter() : e ([[NSApp currentEvent] retain]) {}
|
||||
~EventReposter() override { [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];
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
[nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidMountNotification object: nil];
|
||||
[nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidUnmountNotification object: nil];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
~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");
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
addMethod (@selector (changed:), changed, "v@:@");
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
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.reset (new Pimpl (*this)); }
|
||||
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
104
deps/juce/modules/juce_events/native/juce_osx_MessageQueue.h
vendored
Normal file
104
deps/juce/modules/juce_events/native/juce_osx_MessageQueue.h
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/* An internal message pump class used in OSX and iOS. */
|
||||
class MessageQueue
|
||||
{
|
||||
public:
|
||||
MessageQueue()
|
||||
{
|
||||
#if JUCE_IOS
|
||||
runLoop = CFRunLoopGetCurrent();
|
||||
#else
|
||||
runLoop = CFRunLoopGetMain();
|
||||
#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.reset (CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext));
|
||||
CFRunLoopAddSource (runLoop, runLoopSource.get(), kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
~MessageQueue() noexcept
|
||||
{
|
||||
CFRunLoopRemoveSource (runLoop, runLoopSource.get(), kCFRunLoopCommonModes);
|
||||
CFRunLoopSourceInvalidate (runLoopSource.get());
|
||||
}
|
||||
|
||||
void post (MessageManager::MessageBase* const message)
|
||||
{
|
||||
messages.add (message);
|
||||
wakeUp();
|
||||
}
|
||||
|
||||
private:
|
||||
ReferenceCountedArray<MessageManager::MessageBase, CriticalSection> messages;
|
||||
CFRunLoopRef runLoop;
|
||||
CFUniquePtr<CFRunLoopSourceRef> runLoopSource;
|
||||
|
||||
void wakeUp() noexcept
|
||||
{
|
||||
CFRunLoopSourceSignal (runLoopSource.get());
|
||||
CFRunLoopWakeUp (runLoop);
|
||||
}
|
||||
|
||||
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() noexcept
|
||||
{
|
||||
for (int i = 4; --i >= 0;)
|
||||
if (! deliverNextMessage())
|
||||
return;
|
||||
|
||||
wakeUp();
|
||||
}
|
||||
|
||||
static void runLoopSourceCallback (void* info) noexcept
|
||||
{
|
||||
static_cast<MessageQueue*> (info)->runLoopCallback();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
134
deps/juce/modules/juce_events/native/juce_win32_HiddenMessageWindow.h
vendored
Normal file
134
deps/juce/modules/juce_events/native/juce_win32_HiddenMessageWindow.h
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
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 = {};
|
||||
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,
|
||||
nullptr, nullptr, moduleHandle, nullptr);
|
||||
jassert (hwnd != nullptr);
|
||||
}
|
||||
|
||||
~HiddenMessageWindow()
|
||||
{
|
||||
DestroyWindow (hwnd);
|
||||
UnregisterClass (getClassNameFromAtom(), nullptr);
|
||||
}
|
||||
|
||||
inline HWND getHWND() const noexcept { return hwnd; }
|
||||
|
||||
private:
|
||||
ATOM atom;
|
||||
HWND hwnd;
|
||||
|
||||
LPCTSTR getClassNameFromAtom() noexcept { return (LPCTSTR) (pointer_sized_uint) atom; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
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 auto number = (LONG_PTR) Random().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 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();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
328
deps/juce/modules/juce_events/native/juce_win32_Messaging.cpp
vendored
Normal file
328
deps/juce/modules/juce_events/native/juce_win32_Messaging.cpp
vendored
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
extern HWND juce_messageWindowHandle;
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity
|
||||
bool juce_isRunningInUnity();
|
||||
#endif
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||
LRESULT juce_offerEventToActiveXControl (::MSG&);
|
||||
#endif
|
||||
|
||||
using CheckEventBlockedByModalComps = bool (*)(const MSG&);
|
||||
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
|
||||
|
||||
using SettingChangeCallbackFunc = void (*)(void);
|
||||
SettingChangeCallbackFunc settingChangeCallback = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
class InternalMessageQueue
|
||||
{
|
||||
public:
|
||||
InternalMessageQueue()
|
||||
{
|
||||
messageWindow = std::make_unique<HiddenMessageWindow> (messageWindowName, (WNDPROC) messageWndProc);
|
||||
juce_messageWindowHandle = messageWindow->getHWND();
|
||||
}
|
||||
|
||||
~InternalMessageQueue()
|
||||
{
|
||||
juce_messageWindowHandle = nullptr;
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_SINGLETON (InternalMessageQueue, false)
|
||||
|
||||
//==============================================================================
|
||||
void broadcastMessage (const String& message)
|
||||
{
|
||||
auto localCopy = message;
|
||||
|
||||
Array<HWND> windows;
|
||||
EnumWindows (&broadcastEnumWindowProc, (LPARAM) &windows);
|
||||
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
{
|
||||
COPYDATASTRUCT data;
|
||||
data.dwData = broadcastMessageMagicNumber;
|
||||
data.cbData = (DWORD) (((size_t) localCopy.length() + 1) * sizeof (CharPointer_UTF32::CharType));
|
||||
data.lpData = (void*) localCopy.toUTF32().getAddress();
|
||||
|
||||
DWORD_PTR result;
|
||||
SendMessageTimeout (windows.getUnchecked (i), WM_COPYDATA,
|
||||
(WPARAM) juce_messageWindowHandle,
|
||||
(LPARAM) &data,
|
||||
SMTO_BLOCK | SMTO_ABORTIFHUNG, 8000, &result);
|
||||
}
|
||||
}
|
||||
|
||||
void postMessage (MessageManager::MessageBase* message)
|
||||
{
|
||||
bool shouldTriggerMessageQueueDispatch = false;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
shouldTriggerMessageQueueDispatch = messageQueue.isEmpty();
|
||||
messageQueue.add (message);
|
||||
}
|
||||
|
||||
if (! shouldTriggerMessageQueueDispatch)
|
||||
return;
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity
|
||||
if (juce_isRunningInUnity())
|
||||
{
|
||||
SendNotifyMessage (juce_messageWindowHandle, customMessageID, 0, 0);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
PostMessage (juce_messageWindowHandle, customMessageID, 0, 0);
|
||||
}
|
||||
|
||||
bool dispatchNextMessage (bool returnIfNoPendingMessages)
|
||||
{
|
||||
MSG m;
|
||||
|
||||
if (returnIfNoPendingMessages && ! PeekMessage (&m, nullptr, 0, 0, PM_NOREMOVE))
|
||||
return false;
|
||||
|
||||
if (GetMessage (&m, nullptr, 0, 0) >= 0)
|
||||
{
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||
if (juce_offerEventToActiveXControl (m) != S_FALSE)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
if (m.message == customMessageID && m.hwnd == juce_messageWindowHandle)
|
||||
{
|
||||
dispatchMessages();
|
||||
}
|
||||
else if (m.message == WM_QUIT)
|
||||
{
|
||||
if (auto* 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..
|
||||
auto currentFocus = GetFocus();
|
||||
|
||||
if (currentFocus == nullptr || JuceWindowIdentifier::isJUCEWindow (currentFocus))
|
||||
SetFocus (m.hwnd);
|
||||
}
|
||||
|
||||
TranslateMessage (&m);
|
||||
DispatchMessage (&m);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static LRESULT CALLBACK messageWndProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) noexcept
|
||||
{
|
||||
if (h == juce_messageWindowHandle)
|
||||
{
|
||||
if (message == customMessageID)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
queue->dispatchMessages();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (message == WM_COPYDATA)
|
||||
{
|
||||
handleBroadcastMessage (reinterpret_cast<const COPYDATASTRUCT*> (lParam));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (message == WM_SETTINGCHANGE)
|
||||
if (settingChangeCallback != nullptr)
|
||||
settingChangeCallback();
|
||||
}
|
||||
|
||||
return DefWindowProc (h, message, wParam, lParam);
|
||||
}
|
||||
|
||||
static BOOL CALLBACK broadcastEnumWindowProc (HWND hwnd, LPARAM lParam)
|
||||
{
|
||||
if (hwnd != juce_messageWindowHandle)
|
||||
{
|
||||
TCHAR windowName[64] = { 0 }; // no need to read longer strings than this
|
||||
GetWindowText (hwnd, windowName, 63);
|
||||
|
||||
if (String (windowName) == messageWindowName)
|
||||
reinterpret_cast<Array<HWND>*> (lParam)->add (hwnd);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void dispatchMessage (MessageManager::MessageBase* message)
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
message->messageCallback();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
message->decReferenceCount();
|
||||
}
|
||||
|
||||
static void handleBroadcastMessage (const COPYDATASTRUCT* data)
|
||||
{
|
||||
if (data != nullptr && data->dwData == broadcastMessageMagicNumber)
|
||||
{
|
||||
struct BroadcastMessage : public CallbackMessage
|
||||
{
|
||||
BroadcastMessage (CharPointer_UTF32 text, size_t length) : message (text, length) {}
|
||||
void messageCallback() override { MessageManager::getInstance()->deliverBroadcastMessage (message); }
|
||||
|
||||
String message;
|
||||
};
|
||||
|
||||
(new BroadcastMessage (CharPointer_UTF32 ((const CharPointer_UTF32::CharType*) data->lpData),
|
||||
data->cbData / sizeof (CharPointer_UTF32::CharType)))
|
||||
->post();
|
||||
}
|
||||
}
|
||||
|
||||
void dispatchMessages()
|
||||
{
|
||||
ReferenceCountedArray<MessageManager::MessageBase> messagesToDispatch;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (messageQueue.isEmpty())
|
||||
return;
|
||||
|
||||
messagesToDispatch.swapWith (messageQueue);
|
||||
}
|
||||
|
||||
for (int i = 0; i < messagesToDispatch.size(); ++i)
|
||||
{
|
||||
auto message = messagesToDispatch.getUnchecked (i);
|
||||
message->incReferenceCount();
|
||||
dispatchMessage (message.get());
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static constexpr unsigned int customMessageID = WM_USER + 123;
|
||||
static constexpr unsigned int broadcastMessageMagicNumber = 0xc403;
|
||||
static const TCHAR messageWindowName[];
|
||||
|
||||
std::unique_ptr<HiddenMessageWindow> messageWindow;
|
||||
|
||||
CriticalSection lock;
|
||||
ReferenceCountedArray<MessageManager::MessageBase> messageQueue;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternalMessageQueue)
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue)
|
||||
|
||||
const TCHAR InternalMessageQueue::messageWindowName[] = _T("JUCEWindow");
|
||||
|
||||
//==============================================================================
|
||||
bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
return queue->dispatchNextMessage (returnIfNoPendingMessages);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
{
|
||||
queue->postMessage (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String& value)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
queue->broadcastMessage (value);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
ignoreUnused (OleInitialize (nullptr));
|
||||
InternalMessageQueue::getInstance();
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
InternalMessageQueue::deleteInstance();
|
||||
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.reset (new Pimpl (*this)); }
|
||||
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
|
||||
|
||||
} // namespace juce
|
82
deps/juce/modules/juce_events/native/juce_win32_WinRTWrapper.cpp
vendored
Normal file
82
deps/juce/modules/juce_events/native/juce_win32_WinRTWrapper.cpp
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
WinRTWrapper::WinRTWrapper()
|
||||
{
|
||||
winRTHandle = ::LoadLibraryA ("api-ms-win-core-winrt-l1-1-0");
|
||||
|
||||
if (winRTHandle == nullptr)
|
||||
return;
|
||||
|
||||
roInitialize = (RoInitializeFuncPtr) ::GetProcAddress (winRTHandle, "RoInitialize");
|
||||
createHString = (WindowsCreateStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsCreateString");
|
||||
deleteHString = (WindowsDeleteStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsDeleteString");
|
||||
getHStringRawBuffer = (WindowsGetStringRawBufferFuncPtr) ::GetProcAddress (winRTHandle, "WindowsGetStringRawBuffer");
|
||||
roActivateInstance = (RoActivateInstanceFuncPtr) ::GetProcAddress (winRTHandle, "RoActivateInstance");
|
||||
roGetActivationFactory = (RoGetActivationFactoryFuncPtr) ::GetProcAddress (winRTHandle, "RoGetActivationFactory");
|
||||
|
||||
if (roInitialize == nullptr || createHString == nullptr || deleteHString == nullptr
|
||||
|| getHStringRawBuffer == nullptr || roActivateInstance == nullptr || roGetActivationFactory == nullptr)
|
||||
return;
|
||||
|
||||
HRESULT status = roInitialize (1);
|
||||
initialised = ! (status != S_OK && status != S_FALSE && status != 0x80010106L);
|
||||
}
|
||||
|
||||
WinRTWrapper::~WinRTWrapper()
|
||||
{
|
||||
if (winRTHandle != nullptr)
|
||||
::FreeLibrary (winRTHandle);
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
WinRTWrapper::ScopedHString::ScopedHString (String str)
|
||||
{
|
||||
if (WinRTWrapper::getInstance()->isInitialised())
|
||||
WinRTWrapper::getInstance()->createHString (str.toWideCharPointer(),
|
||||
static_cast<uint32_t> (str.length()),
|
||||
&hstr);
|
||||
}
|
||||
|
||||
WinRTWrapper::ScopedHString::~ScopedHString()
|
||||
{
|
||||
if (WinRTWrapper::getInstance()->isInitialised() && hstr != nullptr)
|
||||
WinRTWrapper::getInstance()->deleteHString (hstr);
|
||||
}
|
||||
|
||||
String WinRTWrapper::hStringToString (HSTRING hstr)
|
||||
{
|
||||
if (isInitialised())
|
||||
if (const wchar_t* str = getHStringRawBuffer (hstr, nullptr))
|
||||
return String (str);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (WinRTWrapper)
|
||||
|
||||
}
|
112
deps/juce/modules/juce_events/native/juce_win32_WinRTWrapper.h
vendored
Normal file
112
deps/juce/modules/juce_events/native/juce_win32_WinRTWrapper.h
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class WinRTWrapper : public DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
~WinRTWrapper();
|
||||
bool isInitialised() const noexcept { return initialised; }
|
||||
|
||||
JUCE_DECLARE_SINGLETON (WinRTWrapper, true)
|
||||
|
||||
//==============================================================================
|
||||
template <class ComClass>
|
||||
ComSmartPtr<ComClass> activateInstance (const wchar_t* runtimeClassID, REFCLSID classUUID)
|
||||
{
|
||||
ComSmartPtr<ComClass> result;
|
||||
|
||||
if (isInitialised())
|
||||
{
|
||||
ComSmartPtr<IInspectable> inspectable;
|
||||
ScopedHString runtimeClass (runtimeClassID);
|
||||
auto hr = roActivateInstance (runtimeClass.get(), inspectable.resetAndGetPointerAddress());
|
||||
|
||||
if (SUCCEEDED (hr))
|
||||
inspectable->QueryInterface (classUUID, (void**) result.resetAndGetPointerAddress());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class ComClass>
|
||||
ComSmartPtr<ComClass> getWRLFactory (const wchar_t* runtimeClassID)
|
||||
{
|
||||
ComSmartPtr<ComClass> comPtr;
|
||||
|
||||
if (isInitialised())
|
||||
{
|
||||
ScopedHString classID (runtimeClassID);
|
||||
|
||||
if (classID.get() != nullptr)
|
||||
roGetActivationFactory (classID.get(), __uuidof (ComClass), (void**) comPtr.resetAndGetPointerAddress());
|
||||
}
|
||||
|
||||
return comPtr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ScopedHString
|
||||
{
|
||||
public:
|
||||
ScopedHString (String);
|
||||
~ScopedHString();
|
||||
|
||||
HSTRING get() const noexcept { return hstr; }
|
||||
|
||||
private:
|
||||
HSTRING hstr = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedHString)
|
||||
};
|
||||
|
||||
String hStringToString (HSTRING);
|
||||
|
||||
private:
|
||||
WinRTWrapper();
|
||||
|
||||
//==============================================================================
|
||||
HMODULE winRTHandle = nullptr;
|
||||
bool initialised = false;
|
||||
|
||||
typedef HRESULT (WINAPI* RoInitializeFuncPtr) (int);
|
||||
typedef HRESULT (WINAPI* WindowsCreateStringFuncPtr) (LPCWSTR, UINT32, HSTRING*);
|
||||
typedef HRESULT (WINAPI* WindowsDeleteStringFuncPtr) (HSTRING);
|
||||
typedef PCWSTR (WINAPI* WindowsGetStringRawBufferFuncPtr) (HSTRING, UINT32*);
|
||||
typedef HRESULT (WINAPI* RoActivateInstanceFuncPtr) (HSTRING, IInspectable**);
|
||||
typedef HRESULT (WINAPI* RoGetActivationFactoryFuncPtr) (HSTRING, REFIID, void**);
|
||||
|
||||
RoInitializeFuncPtr roInitialize = nullptr;
|
||||
WindowsCreateStringFuncPtr createHString = nullptr;
|
||||
WindowsDeleteStringFuncPtr deleteHString = nullptr;
|
||||
WindowsGetStringRawBufferFuncPtr getHStringRawBuffer = nullptr;
|
||||
RoActivateInstanceFuncPtr roActivateInstance = nullptr;
|
||||
RoGetActivationFactoryFuncPtr roGetActivationFactory = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WinRTWrapper)
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user