build(deps): vendor JUCE 7.0.12

This commit is contained in:
2026-09-08 14:55:21 +02:00
parent 5b99f51bd1
commit d6705ab29f
3591 changed files with 1218267 additions and 0 deletions
@@ -0,0 +1,55 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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
{
struct LinuxEventLoopInternal
{
/**
@internal
Receives notifications when a fd callback is added or removed.
This is useful for VST3 plugins that host other VST3 plugins. In this scenario, the 'inner'
plugin may want to register additional file descriptors on top of those registered by the
'outer' plugin. In order for this to work, the outer plugin must in turn pass this request
on to the host, to indicate that the outer plugin wants to receive FD callbacks for both the
inner and outer plugin.
*/
struct Listener
{
virtual ~Listener() = default;
virtual void fdCallbacksChanged() = 0;
};
/** @internal */
static void registerLinuxEventLoopListener (Listener&);
/** @internal */
static void deregisterLinuxEventLoopListener (Listener&);
/** @internal */
static void invokeEventLoopCallbackForFd (int);
/** @internal */
static std::vector<int> getRegisteredFds();
};
} // namespace juce
@@ -0,0 +1,47 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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::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::LinuxEventLoop
@@ -0,0 +1,134 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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, std::function<void()> onChangeIn)
: messageWindow (name, (WNDPROC) deviceChangeEventCallback),
onChange (std::move (onChangeIn))
{
SetWindowLongPtr (messageWindow.getHWND(), GWLP_USERDATA, (LONG_PTR) this);
}
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;
std::function<void()> onChange;
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();
NullCheckedInvocation::invoke (onChange);
}
};
} // namespace juce
@@ -0,0 +1,103 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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
@@ -0,0 +1,494 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 final : public ObjCClass<NSObject>
{
AppDelegateClass() : ObjCClass ("JUCEAppDelegate_")
{
addMethod (@selector (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
});
addMethod (@selector (applicationShouldTerminate:), [] (id /*self*/, SEL, NSApplication*)
{
if (auto* app = JUCEApplicationBase::getInstance())
{
app->systemRequestedQuit();
if (! MessageManager::getInstance()->hasStopMessageBeenSent())
return NSTerminateCancel;
}
return NSTerminateNow;
});
addMethod (@selector (applicationWillTerminate:), [] (id /*self*/, SEL, NSNotification*)
{
JUCEApplicationBase::appWillTerminateByForce();
});
addMethod (@selector (application:openFile:), [] (id /*self*/, SEL, NSApplication*, NSString* filename)
{
if (auto* app = JUCEApplicationBase::getInstance())
{
app->anotherInstanceStarted (quotedIfContainsSpaces (filename));
return YES;
}
return NO;
});
addMethod (@selector (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 (" "));
}
});
addMethod (@selector (applicationDidBecomeActive:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); });
addMethod (@selector (applicationDidResignActive:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); });
addMethod (@selector (applicationWillUnhide:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); });
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
addMethod (@selector (getUrl:withReplyEvent:), [] (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*)
{
if (auto* app = JUCEApplicationBase::getInstance())
app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue]));
});
addMethod (@selector (broadcastMessageCallback:), [] (id /*self*/, SEL, NSNotification* n)
{
NSDictionary* dict = (NSDictionary*) [n userInfo];
auto messageString = nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]);
MessageManager::getInstance()->deliverBroadcastMessage (messageString);
});
addMethod (@selector (mainMenuTrackingBegan:), [] (id /*self*/, SEL, NSNotification*)
{
NullCheckedInvocation::invoke (menuTrackingChangedCallback, true);
});
addMethod (@selector (mainMenuTrackingEnded:), [] (id /*self*/, SEL, NSNotification*)
{
NullCheckedInvocation::invoke (menuTrackingChangedCallback, false);
});
// (used as a way of running a dummy thread)
addMethod (@selector (dummyMethod), [] (id /*self*/, SEL) {});
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#if JUCE_PUSH_NOTIFICATIONS
//==============================================================================
addIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("pushNotificationsDelegate");
addMethod (@selector (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
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullable-to-nonnull-conversion")
if (userNotification != nil && userNotification.userInfo != nil)
[self application: [NSApplication sharedApplication] didReceiveRemoteNotification: userNotification.userInfo];
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
}
});
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
addMethod (@selector (setPushNotificationsDelegate:), [] (id self, SEL, NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* delegate)
{
object_setInstanceVariable (self, "pushNotificationsDelegate", delegate);
});
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), [] (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];
}
});
addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), [] (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];
}
});
addMethod (@selector (application: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
registerClass();
}
private:
static void focusChanged()
{
if (appFocusChangeCallback != nullptr)
(*appFocusChangeCallback)();
}
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 NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* getPushNotificationsDelegate (id self)
{
return getIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> (self, "pushNotificationsDelegate");
}
#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 final : 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];
}
//==============================================================================
#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 final : public ObjCClass<NSObject>
{
ObserverClass() : ObjCClass<NSObject> ("JUCEDriveObserver_")
{
addIvar<Pimpl*> ("owner");
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
addMethod (@selector (changed:), changed);
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
@@ -0,0 +1,104 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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
@@ -0,0 +1,302 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 final : 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 final : 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 final : 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();
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
@@ -0,0 +1,396 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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()
{
[[maybe_unused]] auto err = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, msgpipe);
jassert (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;
[[maybe_unused]] auto numBytes = write (getWriteHandle(), &x, 1);
}
}
//==============================================================================
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;
[[maybe_unused]] auto numBytes = read (fd, &x, 1);
}
return queue.removeAndReturn (0);
}
};
JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue)
//==============================================================================
/*
Stores callbacks associated with file descriptors (FD).
The callback for a particular FD should be called whenever that file has data to read.
For standalone apps, the main thread will call poll to wait for new data on any FD, and then
call the associated callbacks for any FDs that changed.
For plugins, the host (generally) provides some kind of run loop mechanism instead.
- In VST2 plugins, the host should call effEditIdle at regular intervals, and plugins can
dispatch all pending events inside this callback. The host doesn't know about any of the
plugin's FDs, so it's possible there will be a bit of latency between an FD becoming ready,
and its associated callback being called.
- In VST3 plugins, it's possible to register each FD individually with the host. In this case,
the facilities in LinuxEventLoopInternal can be used to observe added/removed FD callbacks,
and the host can be notified whenever the set of FDs changes. The host will call onFDIsSet
whenever a particular FD has data ready. This call should be forwarded through to
InternalRunLoop::dispatchEvent.
*/
struct InternalRunLoop
{
public:
InternalRunLoop() = default;
void registerFdCallback (int fd, std::function<void()>&& cb, short eventMask)
{
{
const ScopedLock sl (lock);
callbacks.emplace (fd, std::make_shared<std::function<void()>> (std::move (cb)));
const auto iter = getPollfd (fd);
if (iter == pfds.end() || iter->fd != fd)
pfds.insert (iter, { fd, eventMask, 0 });
else
jassertfalse;
jassert (pfdsAreSorted());
}
listeners.call ([] (auto& l) { l.fdCallbacksChanged(); });
}
void unregisterFdCallback (int fd)
{
{
const ScopedLock sl (lock);
callbacks.erase (fd);
const auto iter = getPollfd (fd);
if (iter != pfds.end() && iter->fd == fd)
pfds.erase (iter);
else
jassertfalse;
jassert (pfdsAreSorted());
}
listeners.call ([] (auto& l) { l.fdCallbacksChanged(); });
}
bool dispatchPendingEvents()
{
callbackStorage.clear();
getFunctionsToCallThisTime (callbackStorage);
// CriticalSection should be available during the callback
for (auto& fn : callbackStorage)
(*fn)();
return ! callbackStorage.empty();
}
void dispatchEvent (int fd) const
{
const auto fn = [&]
{
const ScopedLock sl (lock);
const auto iter = callbacks.find (fd);
return iter != callbacks.end() ? iter->second : nullptr;
}();
// CriticalSection should be available during the callback
if (auto* callback = fn.get())
(*callback)();
}
bool sleepUntilNextEvent (int timeoutMs)
{
const ScopedLock sl (lock);
return poll (pfds.data(), static_cast<nfds_t> (pfds.size()), timeoutMs) != 0;
}
std::vector<int> getRegisteredFds()
{
const ScopedLock sl (lock);
std::vector<int> result;
result.reserve (callbacks.size());
std::transform (callbacks.begin(),
callbacks.end(),
std::back_inserter (result),
[] (const auto& pair) { return pair.first; });
return result;
}
void addListener (LinuxEventLoopInternal::Listener& listener) { listeners.add (&listener); }
void removeListener (LinuxEventLoopInternal::Listener& listener) { listeners.remove (&listener); }
//==============================================================================
JUCE_DECLARE_SINGLETON (InternalRunLoop, false)
private:
using SharedCallback = std::shared_ptr<std::function<void()>>;
/* Appends any functions that need to be called to the passed-in vector.
We take a copy of each shared function so that the functions can be called without
locking or racing in the event that the function attempts to register/deregister a
new FD callback.
*/
void getFunctionsToCallThisTime (std::vector<SharedCallback>& functions)
{
const ScopedLock sl (lock);
if (! sleepUntilNextEvent (0))
return;
for (auto& pfd : pfds)
{
if (std::exchange (pfd.revents, 0) != 0)
{
const auto iter = callbacks.find (pfd.fd);
if (iter != callbacks.end())
functions.emplace_back (iter->second);
}
}
}
std::vector<pollfd>::iterator getPollfd (int fd)
{
return std::lower_bound (pfds.begin(), pfds.end(), fd, [] (auto descriptor, auto toFind)
{
return descriptor.fd < toFind;
});
}
bool pfdsAreSorted() const
{
return std::is_sorted (pfds.begin(), pfds.end(), [] (auto a, auto b) { return a.fd < b.fd; });
}
CriticalSection lock;
std::map<int, SharedCallback> callbacks;
std::vector<SharedCallback> callbackStorage;
std::vector<pollfd> pfds;
ListenerList<LinuxEventLoopInternal::Listener> listeners;
};
JUCE_IMPLEMENT_SINGLETON (InternalRunLoop)
//==============================================================================
namespace LinuxErrorHandling
{
static bool keyboardBreakOccurred = false;
static void keyboardBreakSignalHandler (int sig)
{
if (sig == SIGINT)
keyboardBreakOccurred = true;
}
static 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
}
namespace detail
{
// 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;
}
} // namespace detail
//==============================================================================
void LinuxEventLoop::registerFdCallback (int fd, std::function<void (int)> readCallback, short eventMask)
{
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
runLoop->registerFdCallback (fd, [cb = std::move (readCallback), fd] { cb (fd); }, eventMask);
}
void LinuxEventLoop::unregisterFdCallback (int fd)
{
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
runLoop->unregisterFdCallback (fd);
}
//==============================================================================
void LinuxEventLoopInternal::registerLinuxEventLoopListener (LinuxEventLoopInternal::Listener& listener)
{
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
runLoop->addListener (listener);
}
void LinuxEventLoopInternal::deregisterLinuxEventLoopListener (LinuxEventLoopInternal::Listener& listener)
{
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
runLoop->removeListener (listener);
}
void LinuxEventLoopInternal::invokeEventLoopCallbackForFd (int fd)
{
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
runLoop->dispatchEvent (fd);
}
std::vector<int> LinuxEventLoopInternal::getRegisteredFds()
{
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
return runLoop->getRegisteredFds();
return {};
}
} // namespace juce
@@ -0,0 +1,325 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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_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 (detail::RunningInUnity::state)
{
SendNotifyMessage (juce_messageWindowHandle, customMessageID, 0, 0);
return;
}
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)
NullCheckedInvocation::invoke (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 final : 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");
//==============================================================================
namespace detail
{
bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
{
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
return queue->dispatchNextMessage (returnIfNoPendingMessages);
return false;
}
} // namespace detail
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()
{
[[maybe_unused]] const auto result = OleInitialize (nullptr);
InternalMessageQueue::getInstance();
}
void MessageManager::doPlatformSpecificShutdown()
{
InternalMessageQueue::deleteInstance();
OleUninitialize();
}
//==============================================================================
struct MountedVolumeListChangeDetector::Pimpl
{
explicit Pimpl (MountedVolumeListChangeDetector& d)
: owner (d)
{
File::findFileSystemRoots (lastVolumeList);
}
void systemDeviceChanged()
{
Array<File> newList;
File::findFileSystemRoots (newList);
if (std::exchange (lastVolumeList, newList) != newList)
owner.mountedVolumeListChanged();
}
DeviceChangeDetector detector { L"MountedVolumeList", [this] { systemDeviceChanged(); } };
MountedVolumeListChangeDetector& owner;
Array<File> lastVolumeList;
};
MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl.reset (new Pimpl (*this)); }
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
} // namespace juce
@@ -0,0 +1,35 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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.
==============================================================================
*/
#pragma once
namespace juce::detail
{
class RunningInUnity
{
public:
/* @internal */
static inline bool state = false;
};
} // namespace juce::detail
@@ -0,0 +1,63 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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()
{
if (@available (macOS 10.9, *))
activity = [[NSProcessInfo processInfo] beginActivityWithOptions: NSActivityUserInitiatedAllowingIdleSystemSleep
reason: @"App must remain in high-power mode"];
}
~Pimpl()
{
if (@available (macOS 10.9, *))
[[NSProcessInfo processInfo] endActivity: activity];
}
private:
id activity;
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
@@ -0,0 +1,49 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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
@@ -0,0 +1,82 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 != (HRESULT) 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)
}
@@ -0,0 +1,112 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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, false)
//==============================================================================
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