fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).
This commit is contained in:
171
modules/juce_events/native/juce_android_Messaging.cpp
Normal file
171
modules/juce_events/native/juce_android_Messaging.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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 (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").get())
|
||||
{
|
||||
}
|
||||
|
||||
~AndroidMessageQueue()
|
||||
{
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
bool post (MessageManager::MessageBase::Ptr&& message)
|
||||
{
|
||||
queue.add (static_cast<MessageManager::MessageBase::Ptr&& > (message));
|
||||
|
||||
// this will call us on the message thread
|
||||
return handler.post (self.get());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void run() override
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
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::dispatchNextMessageOnSystemQueue (const bool)
|
||||
{
|
||||
Logger::outputDebugString ("*** Modal loops are not possible in Android!! Exiting...");
|
||||
exit (1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
jmethodID quitMethod = env->GetMethodID (JuceAppActivity, "finishAndRemoveTask", "()V");
|
||||
|
||||
if (quitMethod != 0)
|
||||
{
|
||||
env->CallVoidMethod (android.activity, quitMethod);
|
||||
return;
|
||||
}
|
||||
|
||||
quitMethod = env->GetMethodID (JuceAppActivity, "finish", "()V");
|
||||
jassert (quitMethod != 0);
|
||||
env->CallVoidMethod (android.activity, quitMethod);
|
||||
}
|
||||
};
|
||||
|
||||
(new QuitCallback())->post();
|
||||
quitMessagePosted = true;
|
||||
}
|
||||
|
||||
} // namespace juce
|
103
modules/juce_events/native/juce_ios_MessageManager.mm
Normal file
103
modules/juce_events/native/juce_ios_MessageManager.mm
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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
|
55
modules/juce_events/native/juce_linux_EventLoop.h
Normal file
55
modules/juce_events/native/juce_linux_EventLoop.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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
|
||||
{
|
||||
struct CallbackFunctionBase
|
||||
{
|
||||
virtual ~CallbackFunctionBase() {}
|
||||
virtual bool operator()(int fd) = 0;
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
template <typename FdCallbackFunction>
|
||||
struct CallbackFunction : public CallbackFunctionBase
|
||||
{
|
||||
FdCallbackFunction callback;
|
||||
|
||||
CallbackFunction (FdCallbackFunction c) : callback (c) {}
|
||||
|
||||
bool operator() (int fd) override { return callback (fd); }
|
||||
};
|
||||
|
||||
template <typename FdCallbackFunction>
|
||||
void setWindowSystemFd (int fd, FdCallbackFunction readCallback)
|
||||
{
|
||||
setWindowSystemFdInternal (fd, new CallbackFunction<FdCallbackFunction> (readCallback));
|
||||
}
|
||||
void removeWindowSystemFd() noexcept;
|
||||
|
||||
void setWindowSystemFdInternal (int fd, CallbackFunctionBase* readCallback) noexcept;
|
||||
}
|
||||
|
||||
} // namespace juce
|
265
modules/juce_events/native/juce_linux_Messaging.cpp
Normal file
265
modules/juce_events/native/juce_linux_Messaging.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
enum FdType
|
||||
{
|
||||
INTERNAL_QUEUE_FD,
|
||||
WINDOW_SYSTEM_FD,
|
||||
FD_COUNT,
|
||||
};
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class InternalMessageQueue
|
||||
{
|
||||
public:
|
||||
InternalMessageQueue()
|
||||
{
|
||||
auto ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd);
|
||||
ignoreUnused (ret); jassert (ret == 0);
|
||||
|
||||
auto internalQueueCb = [this] (int _fd)
|
||||
{
|
||||
if (const MessageManager::MessageBase::Ptr msg = this->popNextMessage (_fd))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
msg->messageCallback();
|
||||
return true;
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
pfds[INTERNAL_QUEUE_FD].fd = getReadHandle();
|
||||
pfds[INTERNAL_QUEUE_FD].events = POLLIN;
|
||||
readCallback[INTERNAL_QUEUE_FD].reset (new LinuxEventLoop::CallbackFunction<decltype(internalQueueCb)> (internalQueueCb));
|
||||
}
|
||||
|
||||
~InternalMessageQueue()
|
||||
{
|
||||
close (getReadHandle());
|
||||
close (getWriteHandle());
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void postMessage (MessageManager::MessageBase* const msg) noexcept
|
||||
{
|
||||
ScopedLock sl (lock);
|
||||
queue.add (msg);
|
||||
|
||||
const int maxBytesInSocketQueue = 128;
|
||||
|
||||
if (bytesInSocket < maxBytesInSocketQueue)
|
||||
{
|
||||
bytesInSocket++;
|
||||
|
||||
ScopedUnlock ul (lock);
|
||||
const unsigned char x = 0xff;
|
||||
ssize_t bytesWritten = write (getWriteHandle(), &x, 1);
|
||||
ignoreUnused (bytesWritten);
|
||||
}
|
||||
}
|
||||
|
||||
void setWindowSystemFd (int _fd, LinuxEventLoop::CallbackFunctionBase* _readCallback)
|
||||
{
|
||||
jassert (fdCount == 1);
|
||||
|
||||
ScopedLock sl (lock);
|
||||
|
||||
fdCount = 2;
|
||||
pfds[WINDOW_SYSTEM_FD].fd = _fd;
|
||||
pfds[WINDOW_SYSTEM_FD].events = POLLIN;
|
||||
readCallback[WINDOW_SYSTEM_FD].reset (_readCallback);
|
||||
readCallback[WINDOW_SYSTEM_FD]->active = true;
|
||||
}
|
||||
|
||||
void removeWindowSystemFd()
|
||||
{
|
||||
jassert (fdCount == FD_COUNT);
|
||||
|
||||
ScopedLock sl (lock);
|
||||
|
||||
fdCount = 1;
|
||||
readCallback[WINDOW_SYSTEM_FD]->active = false;
|
||||
}
|
||||
|
||||
bool dispatchNextEvent() noexcept
|
||||
{
|
||||
for (int counter = 0; counter < fdCount; counter++)
|
||||
{
|
||||
const int i = loopCount++;
|
||||
loopCount %= fdCount;
|
||||
|
||||
if (readCallback[i] != nullptr && readCallback[i]->active)
|
||||
if ((*readCallback[i]) (pfds[i].fd))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sleepUntilEvent (const int timeoutMs)
|
||||
{
|
||||
const int pnum = poll (pfds, static_cast<nfds_t> (fdCount), timeoutMs);
|
||||
return (pnum > 0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (InternalMessageQueue)
|
||||
|
||||
private:
|
||||
CriticalSection lock;
|
||||
ReferenceCountedArray <MessageManager::MessageBase> queue;
|
||||
int fd[2];
|
||||
pollfd pfds[FD_COUNT];
|
||||
std::unique_ptr<LinuxEventLoop::CallbackFunctionBase> readCallback[FD_COUNT];
|
||||
int fdCount = 1;
|
||||
int loopCount = 0;
|
||||
int bytesInSocket = 0;
|
||||
|
||||
int getWriteHandle() const noexcept { return fd[0]; }
|
||||
int getReadHandle() const noexcept { return fd[1]; }
|
||||
|
||||
MessageManager::MessageBase::Ptr popNextMessage (int _fd) noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (bytesInSocket > 0)
|
||||
{
|
||||
--bytesInSocket;
|
||||
|
||||
const ScopedUnlock ul (lock);
|
||||
unsigned char x;
|
||||
ssize_t numBytes = read (_fd, &x, 1);
|
||||
ignoreUnused (numBytes);
|
||||
}
|
||||
|
||||
return queue.removeAndReturn (0);
|
||||
}
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue)
|
||||
|
||||
|
||||
//==============================================================================
|
||||
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, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
LinuxErrorHandling::installKeyboardBreakHandler();
|
||||
|
||||
// Create the internal message queue
|
||||
auto* queue = InternalMessageQueue::getInstance();
|
||||
ignoreUnused (queue);
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
InternalMessageQueue::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 MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (LinuxErrorHandling::keyboardBreakOccurred)
|
||||
JUCEApplicationBase::getInstance()->quit();
|
||||
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
{
|
||||
if (queue->dispatchNextEvent())
|
||||
break;
|
||||
|
||||
if (returnIfNoPendingMessages)
|
||||
return false;
|
||||
|
||||
// wait for 2000ms for next events if necessary
|
||||
queue->sleepUntilEvent (2000);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void LinuxEventLoop::setWindowSystemFdInternal (int fd, LinuxEventLoop::CallbackFunctionBase* readCallback) noexcept
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
queue->setWindowSystemFd (fd, readCallback);
|
||||
}
|
||||
|
||||
void LinuxEventLoop::removeWindowSystemFd() noexcept
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
queue->removeWindowSystemFd();
|
||||
}
|
||||
|
||||
|
||||
} // namespace juce
|
521
modules/juce_events/native/juce_mac_MessageManager.mm
Normal file
521
modules/juce_events/native/juce_mac_MessageManager.mm
Normal file
@ -0,0 +1,521 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
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: getBroadcastEventName()
|
||||
object: nil
|
||||
suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately];
|
||||
}
|
||||
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;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct AppDelegateClass : public ObjCClass<NSObject>
|
||||
{
|
||||
AppDelegateClass() : ObjCClass<NSObject> ("JUCEAppDelegate_")
|
||||
{
|
||||
addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@");
|
||||
addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "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@:@");
|
||||
addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@");
|
||||
addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@");
|
||||
addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@");
|
||||
addMethod (@selector (dummyMethod), dummyMethod, "v@:");
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
//==============================================================================
|
||||
addIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("pushNotificationsDelegate");
|
||||
|
||||
addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@");
|
||||
addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate, "v@:@");
|
||||
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*)
|
||||
{
|
||||
[[NSAppleEventManager sharedAppleEventManager] setEventHandler: self
|
||||
andSelector: @selector (getUrl:withReplyEvent:)
|
||||
forEventClass: kInternetEventClass
|
||||
andEventID: kAEGetURL];
|
||||
}
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
static void applicationDidFinishLaunching (id self, SEL, NSNotification* notification)
|
||||
{
|
||||
if (notification.userInfo != nil)
|
||||
{
|
||||
NSUserNotification* userNotification = [notification.userInfo objectForKey: nsStringLiteral ("NSApplicationLaunchUserNotificationKey")];
|
||||
|
||||
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));
|
||||
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 = NSSelectorFromString (@"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 = NSSelectorFromString (@"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 = NSSelectorFromString (@"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
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
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_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]);
|
||||
JUCEApplicationBase::sendUnhandledException (&ex, __FILE__, __LINE__);
|
||||
}
|
||||
@finally
|
||||
{
|
||||
}
|
||||
#else
|
||||
[NSApp run];
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdownNSApp()
|
||||
{
|
||||
[NSApp stop: nil];
|
||||
[NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1];
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
#if JUCE_PROJUCER_LIVE_BUILD
|
||||
quitMessagePosted = true;
|
||||
#else
|
||||
|
||||
if (isThisTheMessageThread())
|
||||
{
|
||||
quitMessagePosted = true;
|
||||
shutdownNSApp();
|
||||
}
|
||||
else
|
||||
{
|
||||
struct QuitCallback : public CallbackMessage
|
||||
{
|
||||
QuitCallback() {}
|
||||
void messageCallback() override { MessageManager::getInstance()->stopDispatchLoop(); }
|
||||
};
|
||||
|
||||
(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.get() == 0)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
CFRunLoopRunInMode (kCFRunLoopDefaultMode, 0.001, true);
|
||||
|
||||
NSEvent* e = [NSApp nextEventMatchingMask: NSEventMaskAny
|
||||
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.get() == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
void initialiseNSApplication();
|
||||
void initialiseNSApplication()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[NSApplication sharedApplication];
|
||||
}
|
||||
}
|
||||
|
||||
static AppDelegate* appDelegate = nullptr;
|
||||
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (appDelegate == nil)
|
||||
appDelegate = new AppDelegate();
|
||||
}
|
||||
|
||||
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::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() { [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.reset (new Pimpl (*this)); }
|
||||
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
105
modules/juce_events/native/juce_osx_MessageQueue.h
Normal file
105
modules/juce_events/native/juce_osx_MessageQueue.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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 = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext);
|
||||
CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
~MessageQueue() noexcept
|
||||
{
|
||||
CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
|
||||
CFRunLoopSourceInvalidate (runLoopSource);
|
||||
CFRelease (runLoopSource);
|
||||
}
|
||||
|
||||
void post (MessageManager::MessageBase* const message)
|
||||
{
|
||||
messages.add (message);
|
||||
wakeUp();
|
||||
}
|
||||
|
||||
private:
|
||||
ReferenceCountedArray<MessageManager::MessageBase, CriticalSection> messages;
|
||||
CFRunLoopRef runLoop;
|
||||
CFRunLoopSourceRef runLoopSource;
|
||||
|
||||
void wakeUp() noexcept
|
||||
{
|
||||
CFRunLoopSourceSignal (runLoopSource);
|
||||
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
|
135
modules/juce_events/native/juce_win32_HiddenMessageWindow.h
Normal file
135
modules/juce_events/native/juce_win32_HiddenMessageWindow.h
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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 = { 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) (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 ~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();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
240
modules/juce_events/native/juce_win32_Messaging.cpp
Normal file
240
modules/juce_events/native/juce_win32_Messaging.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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;
|
||||
|
||||
typedef bool (*CheckEventBlockedByModalComps) (const MSG&);
|
||||
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
|
||||
|
||||
typedef void (*SettingChangeCallbackFunc) (void);
|
||||
SettingChangeCallbackFunc settingChangeCallback = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
namespace WindowsMessageHelpers
|
||||
{
|
||||
const unsigned int customMessageID = WM_USER + 123;
|
||||
const unsigned int broadcastMessageMagicNumber = 0xc403;
|
||||
|
||||
const TCHAR messageWindowName[] = _T("JUCEWindow");
|
||||
std::unique_ptr<HiddenMessageWindow> messageWindow;
|
||||
|
||||
void dispatchMessageFromLParam (LPARAM lParam)
|
||||
{
|
||||
if (MessageManager::MessageBase* message = reinterpret_cast<MessageManager::MessageBase*> (lParam))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
message->messageCallback();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
message->decReferenceCount();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void handleBroadcastMessage (const COPYDATASTRUCT* const 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();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LRESULT CALLBACK messageWndProc (HWND h, const UINT message, const WPARAM wParam, const LPARAM lParam) noexcept
|
||||
{
|
||||
if (h == juce_messageWindowHandle)
|
||||
{
|
||||
if (message == customMessageID)
|
||||
{
|
||||
// (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;
|
||||
}
|
||||
|
||||
if (message == WM_COPYDATA)
|
||||
{
|
||||
handleBroadcastMessage (reinterpret_cast<const COPYDATASTRUCT*> (lParam));
|
||||
return 0;
|
||||
}
|
||||
else if (message == WM_SETTINGCHANGE)
|
||||
{
|
||||
if (settingChangeCallback != nullptr)
|
||||
settingChangeCallback();
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc (h, message, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||
LRESULT juce_offerEventToActiveXControl (::MSG&);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
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 JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||
if (juce_offerEventToActiveXControl (m) != S_FALSE)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
if (m.message == customMessageID && m.hwnd == juce_messageWindowHandle)
|
||||
{
|
||||
dispatchMessageFromLParam (m.lParam);
|
||||
}
|
||||
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..
|
||||
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::customMessageID, 0, (LPARAM) message) != 0;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String& value)
|
||||
{
|
||||
const String localCopy (value);
|
||||
|
||||
Array<HWND> windows;
|
||||
EnumWindows (&WindowsMessageHelpers::broadcastEnumWindowProc, (LPARAM) &windows);
|
||||
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
{
|
||||
COPYDATASTRUCT data;
|
||||
data.dwData = WindowsMessageHelpers::broadcastMessageMagicNumber;
|
||||
data.cbData = (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 MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
OleInitialize (0);
|
||||
|
||||
using namespace WindowsMessageHelpers;
|
||||
messageWindow.reset (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.reset (new Pimpl (*this)); }
|
||||
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
|
||||
|
||||
} // namespace juce
|
26
modules/juce_events/native/juce_win32_WinRTWrapper.cpp
Normal file
26
modules/juce_events/native/juce_win32_WinRTWrapper.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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
|
||||
{
|
||||
JUCE_IMPLEMENT_SINGLETON (WinRTWrapper)
|
||||
}
|
133
modules/juce_events/native/juce_win32_WinRTWrapper.h
Normal file
133
modules/juce_events/native/juce_win32_WinRTWrapper.h
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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:
|
||||
JUCE_DECLARE_SINGLETON (WinRTWrapper, true)
|
||||
|
||||
class ScopedHString
|
||||
{
|
||||
public:
|
||||
ScopedHString (String str)
|
||||
{
|
||||
if (WinRTWrapper::getInstance()->isInitialised())
|
||||
WinRTWrapper::getInstance()->createHString (str.toWideCharPointer(),
|
||||
static_cast<uint32_t> (str.length()),
|
||||
&hstr);
|
||||
}
|
||||
|
||||
~ScopedHString()
|
||||
{
|
||||
if (WinRTWrapper::getInstance()->isInitialised() && hstr != nullptr)
|
||||
WinRTWrapper::getInstance()->deleteHString (hstr);
|
||||
}
|
||||
|
||||
HSTRING get() const noexcept
|
||||
{
|
||||
return hstr;
|
||||
}
|
||||
|
||||
private:
|
||||
HSTRING hstr = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedHString)
|
||||
};
|
||||
|
||||
~WinRTWrapper()
|
||||
{
|
||||
if (winRTHandle != nullptr)
|
||||
::FreeLibrary (winRTHandle);
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
String hStringToString (HSTRING hstr)
|
||||
{
|
||||
if (isInitialised())
|
||||
if (const wchar_t* str = getHStringRawBuffer (hstr, nullptr))
|
||||
return String (str);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool isInitialised() const noexcept
|
||||
{
|
||||
return initialised;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
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");
|
||||
roGetActivationFactory = (RoGetActivationFactoryFuncPtr) ::GetProcAddress (winRTHandle, "RoGetActivationFactory");
|
||||
|
||||
if (roInitialize == nullptr || createHString == nullptr || deleteHString == nullptr
|
||||
|| getHStringRawBuffer == nullptr || roGetActivationFactory == nullptr)
|
||||
return;
|
||||
|
||||
HRESULT status = roInitialize (1);
|
||||
initialised = ! (status != S_OK && status != S_FALSE && status != 0x80010106L);
|
||||
}
|
||||
|
||||
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* RoGetActivationFactoryFuncPtr) (HSTRING, REFIID, void**);
|
||||
|
||||
RoInitializeFuncPtr roInitialize = nullptr;
|
||||
WindowsCreateStringFuncPtr createHString = nullptr;
|
||||
WindowsDeleteStringFuncPtr deleteHString = nullptr;
|
||||
WindowsGetStringRawBufferFuncPtr getHStringRawBuffer = nullptr;
|
||||
RoGetActivationFactoryFuncPtr roGetActivationFactory = nullptr;
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user