upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)
This commit is contained in:
@ -35,7 +35,7 @@ public:
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (const ActionBroadcaster* const b = broadcaster)
|
||||
if (auto b = broadcaster.get())
|
||||
if (b->actionListeners.contains (listener))
|
||||
listener->actionListenerCallback (message);
|
||||
}
|
||||
@ -52,13 +52,13 @@ private:
|
||||
ActionBroadcaster::ActionBroadcaster()
|
||||
{
|
||||
// are you trying to create this object before or after juce has been intialised??
|
||||
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
|
||||
}
|
||||
|
||||
ActionBroadcaster::~ActionBroadcaster()
|
||||
{
|
||||
// all event-based objects must be deleted BEFORE juce is shut down!
|
||||
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
|
||||
}
|
||||
|
||||
void ActionBroadcaster::addActionListener (ActionListener* const listener)
|
||||
|
@ -35,7 +35,7 @@ class JUCE_API ActionListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ActionListener() {}
|
||||
virtual ~ActionListener() = default;
|
||||
|
||||
/** Overridden by your subclass to receive the callback.
|
||||
|
||||
|
@ -43,7 +43,7 @@ public:
|
||||
//==============================================================================
|
||||
AsyncUpdater::AsyncUpdater()
|
||||
{
|
||||
activeMessage = new AsyncUpdaterMessage (*this);
|
||||
activeMessage = *new AsyncUpdaterMessage (*this);
|
||||
}
|
||||
|
||||
AsyncUpdater::~AsyncUpdater()
|
||||
@ -63,7 +63,7 @@ void AsyncUpdater::triggerAsyncUpdate()
|
||||
{
|
||||
// If you're calling this before (or after) the MessageManager is
|
||||
// running, then you're not going to get any callbacks!
|
||||
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
|
||||
|
||||
if (activeMessage->shouldDeliver.compareAndSetBool (1, 0))
|
||||
if (! activeMessage->post())
|
||||
@ -79,7 +79,7 @@ void AsyncUpdater::cancelPendingUpdate() noexcept
|
||||
void AsyncUpdater::handleUpdateNowIfNeeded()
|
||||
{
|
||||
// This can only be called by the event thread.
|
||||
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
if (activeMessage->shouldDeliver.exchange (0) != 0)
|
||||
handleAsyncUpdate();
|
||||
|
@ -36,7 +36,7 @@ void ChangeBroadcaster::addChangeListener (ChangeListener* const listener)
|
||||
{
|
||||
// Listeners can only be safely added when the event thread is locked
|
||||
// You can use a MessageManagerLock if you need to call this from another thread.
|
||||
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
changeListeners.add (listener);
|
||||
}
|
||||
@ -45,7 +45,7 @@ void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener)
|
||||
{
|
||||
// Listeners can only be safely removed when the event thread is locked
|
||||
// You can use a MessageManagerLock if you need to call this from another thread.
|
||||
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
changeListeners.remove (listener);
|
||||
}
|
||||
@ -54,7 +54,7 @@ void ChangeBroadcaster::removeAllChangeListeners()
|
||||
{
|
||||
// Listeners can only be safely removed when the event thread is locked
|
||||
// You can use a MessageManagerLock if you need to call this from another thread.
|
||||
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
changeListeners.clear();
|
||||
}
|
||||
@ -68,7 +68,7 @@ void ChangeBroadcaster::sendChangeMessage()
|
||||
void ChangeBroadcaster::sendSynchronousChangeMessage()
|
||||
{
|
||||
// This can only be called by the event thread.
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
broadcastCallback.cancelPendingUpdate();
|
||||
callListeners();
|
||||
|
@ -45,7 +45,7 @@ class JUCE_API ChangeListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ChangeListener() {}
|
||||
virtual ~ChangeListener() = default;
|
||||
|
||||
/** Your subclass should implement this method to receive the callback.
|
||||
@param source the ChangeBroadcaster that triggered the callback.
|
||||
|
@ -94,7 +94,7 @@ struct ChildProcessMaster::Connection : public InterprocessConnection,
|
||||
startThread (4);
|
||||
}
|
||||
|
||||
~Connection()
|
||||
~Connection() override
|
||||
{
|
||||
stopThread (10000);
|
||||
}
|
||||
@ -192,7 +192,7 @@ struct ChildProcessSlave::Connection : public InterprocessConnection,
|
||||
startThread (4);
|
||||
}
|
||||
|
||||
~Connection()
|
||||
~Connection() override
|
||||
{
|
||||
stopThread (10000);
|
||||
}
|
||||
|
@ -104,8 +104,6 @@ public:
|
||||
|
||||
private:
|
||||
struct Connection;
|
||||
friend struct Connection;
|
||||
friend struct ContainerDeletePolicy<Connection>;
|
||||
std::unique_ptr<Connection> connection;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave)
|
||||
@ -194,8 +192,6 @@ private:
|
||||
std::unique_ptr<ChildProcess> childProcess;
|
||||
|
||||
struct Connection;
|
||||
friend struct Connection;
|
||||
friend struct ContainerDeletePolicy<Connection>;
|
||||
std::unique_ptr<Connection> connection;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster)
|
||||
|
@ -59,6 +59,7 @@ bool InterprocessConnection::connectToSocket (const String& hostName,
|
||||
|
||||
if (socket->connect (hostName, portNumber, timeOutMillisecs))
|
||||
{
|
||||
threadIsRunning = true;
|
||||
connectionMadeInt();
|
||||
thread->startThread();
|
||||
return true;
|
||||
@ -130,7 +131,7 @@ bool InterprocessConnection::isConnected() const
|
||||
|
||||
return ((socket != nullptr && socket->isConnected())
|
||||
|| (pipe != nullptr && pipe->isOpen()))
|
||||
&& thread->isThreadRunning();
|
||||
&& threadIsRunning;
|
||||
}
|
||||
|
||||
String InterprocessConnection::getConnectedHostName() const
|
||||
@ -179,6 +180,8 @@ void InterprocessConnection::initialiseWithSocket (StreamingSocket* newSocket)
|
||||
{
|
||||
jassert (socket == nullptr && pipe == nullptr);
|
||||
socket.reset (newSocket);
|
||||
|
||||
threadIsRunning = true;
|
||||
connectionMadeInt();
|
||||
thread->startThread();
|
||||
}
|
||||
@ -187,6 +190,8 @@ void InterprocessConnection::initialiseWithPipe (NamedPipe* newPipe)
|
||||
{
|
||||
jassert (socket == nullptr && pipe == nullptr);
|
||||
pipe.reset (newPipe);
|
||||
|
||||
threadIsRunning = true;
|
||||
connectionMadeInt();
|
||||
thread->startThread();
|
||||
}
|
||||
@ -366,6 +371,8 @@ void InterprocessConnection::runThread()
|
||||
if (thread->threadShouldExit() || ! readNextMessage())
|
||||
break;
|
||||
}
|
||||
|
||||
threadIsRunning = false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -197,9 +197,9 @@ private:
|
||||
int readData (void*, int);
|
||||
|
||||
struct ConnectionThread;
|
||||
friend struct ConnectionThread;
|
||||
friend struct ContainerDeletePolicy<ConnectionThread>;
|
||||
std::unique_ptr<ConnectionThread> thread;
|
||||
std::atomic<bool> threadIsRunning { false };
|
||||
|
||||
void runThread();
|
||||
int writeData (void*, int);
|
||||
|
||||
|
@ -45,7 +45,7 @@ public:
|
||||
InterprocessConnectionServer();
|
||||
|
||||
/** Destructor. */
|
||||
~InterprocessConnectionServer();
|
||||
~InterprocessConnectionServer() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Starts an internal thread which listens on the given port number.
|
||||
|
@ -0,0 +1,190 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
NetworkServiceDiscovery::Advertiser::Advertiser (const String& serviceTypeUID,
|
||||
const String& serviceDescription,
|
||||
int broadcastPortToUse, int connectionPort,
|
||||
RelativeTime minTimeBetweenBroadcasts)
|
||||
: Thread ("Discovery_broadcast"),
|
||||
message (serviceTypeUID), broadcastPort (broadcastPortToUse),
|
||||
minInterval (minTimeBetweenBroadcasts)
|
||||
{
|
||||
message.setAttribute ("id", Uuid().toString());
|
||||
message.setAttribute ("name", serviceDescription);
|
||||
message.setAttribute ("address", String());
|
||||
message.setAttribute ("port", connectionPort);
|
||||
|
||||
startThread (2);
|
||||
}
|
||||
|
||||
NetworkServiceDiscovery::Advertiser::~Advertiser()
|
||||
{
|
||||
stopThread (2000);
|
||||
socket.shutdown();
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::Advertiser::run()
|
||||
{
|
||||
if (! socket.bindToPort (0))
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
sendBroadcast();
|
||||
wait ((int) minInterval.inMilliseconds());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::Advertiser::sendBroadcast()
|
||||
{
|
||||
auto localAddress = IPAddress::getLocalAddress();
|
||||
message.setAttribute ("address", localAddress.toString());
|
||||
auto broadcastAddress = IPAddress::getInterfaceBroadcastAddress (localAddress);
|
||||
auto data = message.createDocument ({}, true, false);
|
||||
socket.write (broadcastAddress.toString(), broadcastPort, data.toRawUTF8(), (int) data.getNumBytesAsUTF8());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
NetworkServiceDiscovery::AvailableServiceList::AvailableServiceList (const String& serviceType, int broadcastPort)
|
||||
: Thread ("Discovery_listen"), serviceTypeUID (serviceType)
|
||||
{
|
||||
socket.bindToPort (broadcastPort);
|
||||
startThread (2);
|
||||
}
|
||||
|
||||
NetworkServiceDiscovery::AvailableServiceList::~AvailableServiceList()
|
||||
{
|
||||
socket.shutdown();
|
||||
stopThread (2000);
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::AvailableServiceList::run()
|
||||
{
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
if (socket.waitUntilReady (true, 200) == 1)
|
||||
{
|
||||
char buffer[1024];
|
||||
auto bytesRead = socket.read (buffer, sizeof (buffer) - 1, false);
|
||||
|
||||
if (bytesRead > 10)
|
||||
if (auto xml = parseXML (String (CharPointer_UTF8 (buffer),
|
||||
CharPointer_UTF8 (buffer + bytesRead))))
|
||||
if (xml->hasTagName (serviceTypeUID))
|
||||
handleMessage (*xml);
|
||||
}
|
||||
|
||||
removeTimedOutServices();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<NetworkServiceDiscovery::Service> NetworkServiceDiscovery::AvailableServiceList::getServices() const
|
||||
{
|
||||
const ScopedLock sl (listLock);
|
||||
auto listCopy = services;
|
||||
return listCopy;
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::AvailableServiceList::handleAsyncUpdate()
|
||||
{
|
||||
if (onChange != nullptr)
|
||||
onChange();
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::AvailableServiceList::handleMessage (const XmlElement& xml)
|
||||
{
|
||||
Service service;
|
||||
service.instanceID = xml.getStringAttribute ("id");
|
||||
|
||||
if (service.instanceID.trim().isNotEmpty())
|
||||
{
|
||||
service.description = xml.getStringAttribute ("name");
|
||||
service.address = IPAddress (xml.getStringAttribute ("address"));
|
||||
service.port = xml.getIntAttribute ("port");
|
||||
service.lastSeen = Time::getCurrentTime();
|
||||
|
||||
handleMessage (service);
|
||||
}
|
||||
}
|
||||
|
||||
static void sortServiceList (std::vector<NetworkServiceDiscovery::Service>& services)
|
||||
{
|
||||
auto compareServices = [] (const NetworkServiceDiscovery::Service& s1,
|
||||
const NetworkServiceDiscovery::Service& s2)
|
||||
{
|
||||
return s1.instanceID < s2.instanceID;
|
||||
};
|
||||
|
||||
std::sort (services.begin(), services.end(), compareServices);
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::AvailableServiceList::handleMessage (const Service& service)
|
||||
{
|
||||
const ScopedLock sl (listLock);
|
||||
|
||||
for (auto& s : services)
|
||||
{
|
||||
if (s.instanceID == service.instanceID)
|
||||
{
|
||||
if (s.description != service.description
|
||||
|| s.address != service.address
|
||||
|| s.port != service.port)
|
||||
{
|
||||
s = service;
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
s.lastSeen = service.lastSeen;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
services.push_back (service);
|
||||
sortServiceList (services);
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::AvailableServiceList::removeTimedOutServices()
|
||||
{
|
||||
const double timeoutSeconds = 5.0;
|
||||
auto oldestAllowedTime = Time::getCurrentTime() - RelativeTime::seconds (timeoutSeconds);
|
||||
|
||||
const ScopedLock sl (listLock);
|
||||
|
||||
auto oldEnd = std::end (services);
|
||||
auto newEnd = std::remove_if (std::begin (services), oldEnd,
|
||||
[=] (const Service& s) { return s.lastSeen < oldestAllowedTime; });
|
||||
|
||||
if (newEnd != oldEnd)
|
||||
{
|
||||
services.erase (newEnd, oldEnd);
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
128
modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h
Normal file
128
modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains classes that implement a simple protocol for broadcasting the availability
|
||||
and location of a discoverable service on the local network, and for maintaining a
|
||||
list of known services.
|
||||
*/
|
||||
struct NetworkServiceDiscovery
|
||||
{
|
||||
/** An object which runs a thread to repeatedly broadcast the existence of a
|
||||
discoverable service.
|
||||
|
||||
To use, simply create an instance of an Advertiser and it'll broadcast until
|
||||
you delete it.
|
||||
*/
|
||||
struct Advertiser : private Thread
|
||||
{
|
||||
/** Creates and starts an Advertiser thread, broadcasting with the given properties.
|
||||
@param serviceTypeUID A user-supplied string to define the type of service this represents
|
||||
@param serviceDescription A description string that will appear in the Service::description field for clients
|
||||
@param broadcastPort The port number on which to broadcast the service discovery packets
|
||||
@param connectionPort The port number that will be sent to appear in the Service::port field
|
||||
@param minTimeBetweenBroadcasts The interval to wait between sending broadcast messages
|
||||
*/
|
||||
Advertiser (const String& serviceTypeUID,
|
||||
const String& serviceDescription,
|
||||
int broadcastPort,
|
||||
int connectionPort,
|
||||
RelativeTime minTimeBetweenBroadcasts = RelativeTime::seconds (1.5));
|
||||
|
||||
/** Destructor */
|
||||
~Advertiser() override;
|
||||
|
||||
private:
|
||||
XmlElement message;
|
||||
const int broadcastPort;
|
||||
const RelativeTime minInterval;
|
||||
DatagramSocket socket { true };
|
||||
|
||||
void run() override;
|
||||
void sendBroadcast();
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains information about a service that has been found on the network.
|
||||
@see AvailableServiceList, Advertiser
|
||||
*/
|
||||
struct Service
|
||||
{
|
||||
String instanceID; /**< A UUID that identifies the particular instance of the Advertiser class. */
|
||||
String description; /**< The service description as sent by the Advertiser */
|
||||
IPAddress address; /**< The IP address of the advertiser */
|
||||
int port; /**< The port number of the advertiser */
|
||||
Time lastSeen; /**< The time of the last ping received from the advertiser */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Watches the network for broadcasts from Advertiser objects, and keeps a list of
|
||||
all the currently active instances.
|
||||
|
||||
Just create an instance of AvailableServiceList and it will start listening - you
|
||||
can register a callback with its onChange member to find out when services
|
||||
appear/disappear, and you can call getServices() to find out the current list.
|
||||
@see Service, Advertiser
|
||||
*/
|
||||
struct AvailableServiceList : private Thread,
|
||||
private AsyncUpdater
|
||||
{
|
||||
/** Creates an AvailableServiceList that will bind to the given port number and watch
|
||||
the network for Advertisers broadcasting the given service type.
|
||||
|
||||
This will only detect broadcasts from an Advertiser object with a matching
|
||||
serviceTypeUID value, and where the broadcastPort matches.
|
||||
*/
|
||||
AvailableServiceList (const String& serviceTypeUID, int broadcastPort);
|
||||
|
||||
/** Destructor */
|
||||
~AvailableServiceList() override;
|
||||
|
||||
/** A lambda that can be set to recieve a callback when the list changes */
|
||||
std::function<void()> onChange;
|
||||
|
||||
/** Returns a list of the currently known services. */
|
||||
std::vector<Service> getServices() const;
|
||||
|
||||
private:
|
||||
DatagramSocket socket { true };
|
||||
String serviceTypeUID;
|
||||
CriticalSection listLock;
|
||||
std::vector<Service> services;
|
||||
|
||||
void run() override;
|
||||
void handleAsyncUpdate() override;
|
||||
void handleMessage (const XmlElement&);
|
||||
void handleMessage (const Service&);
|
||||
void removeTimedOutServices();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AvailableServiceList)
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace juce
|
@ -66,6 +66,7 @@
|
||||
#include "interprocess/juce_InterprocessConnection.cpp"
|
||||
#include "interprocess/juce_InterprocessConnectionServer.cpp"
|
||||
#include "interprocess/juce_ConnectedChildProcess.cpp"
|
||||
#include "interprocess/juce_NetworkServiceDiscovery.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
ID: juce_events
|
||||
vendor: juce
|
||||
version: 5.3.2
|
||||
version: 5.4.3
|
||||
name: JUCE message and event handling classes
|
||||
description: Classes for running an application's main event loop and sending/receiving messages, timers, etc.
|
||||
website: http://www.juce.com/juce
|
||||
@ -59,6 +59,12 @@
|
||||
#endif
|
||||
|
||||
#if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER && JUCE_WINDOWS
|
||||
// If this header file is missing then you are probably attempting to use WinRT
|
||||
// functionality without the WinRT libraries installed on your system. Try installing
|
||||
// the latest Windows Standalone SDK and maybe also adding the path to the WinRT
|
||||
// headers to your build system. This path should have the form
|
||||
// "C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
|
||||
#include <inspectable.h>
|
||||
#include <hstring.h>
|
||||
#endif
|
||||
|
||||
@ -81,6 +87,7 @@
|
||||
#include "interprocess/juce_InterprocessConnection.h"
|
||||
#include "interprocess/juce_InterprocessConnectionServer.h"
|
||||
#include "interprocess/juce_ConnectedChildProcess.h"
|
||||
#include "interprocess/juce_NetworkServiceDiscovery.h"
|
||||
|
||||
#if JUCE_LINUX
|
||||
#include "native/juce_linux_EventLoop.h"
|
||||
|
@ -23,7 +23,7 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
JUCEApplicationBase::CreateInstanceFunction JUCEApplicationBase::createInstance = 0;
|
||||
JUCEApplicationBase::CreateInstanceFunction JUCEApplicationBase::createInstance = nullptr;
|
||||
JUCEApplicationBase* JUCEApplicationBase::appInstance = nullptr;
|
||||
|
||||
#if JUCE_IOS
|
||||
|
@ -107,9 +107,11 @@ public:
|
||||
If your application class returns true for this, more than one instance is
|
||||
permitted to run (except on the Mac where this isn't possible).
|
||||
|
||||
If it's false, the second instance won't start, but it you will still get a
|
||||
If it's false, the second instance won't start, but you will still get a
|
||||
callback to anotherInstanceStarted() to tell you about this - which
|
||||
gives you a chance to react to what the user was trying to do.
|
||||
|
||||
@see anotherInstanceStarted
|
||||
*/
|
||||
virtual bool moreThanOneInstanceAllowed() = 0;
|
||||
|
||||
@ -151,6 +153,9 @@ public:
|
||||
/** Indicates that the user has tried to start up another instance of the app.
|
||||
|
||||
This will get called even if moreThanOneInstanceAllowed() is false.
|
||||
It is currently only implemented on Windows and Mac.
|
||||
|
||||
@see moreThanOneInstanceAllowed
|
||||
*/
|
||||
virtual void anotherInstanceStarted (const String& commandLine) = 0;
|
||||
|
||||
@ -270,7 +275,7 @@ public:
|
||||
static int main (int argc, const char* argv[]);
|
||||
|
||||
static void appWillTerminateByForce();
|
||||
typedef JUCEApplicationBase* (*CreateInstanceFunction)();
|
||||
using CreateInstanceFunction = JUCEApplicationBase* (*)();
|
||||
static CreateInstanceFunction createInstance;
|
||||
|
||||
#if JUCE_IOS
|
||||
@ -290,8 +295,6 @@ private:
|
||||
bool stillInitialising = true;
|
||||
|
||||
struct MultipleInstanceHandler;
|
||||
friend struct MultipleInstanceHandler;
|
||||
friend struct ContainerDeletePolicy<MultipleInstanceHandler>;
|
||||
std::unique_ptr<MultipleInstanceHandler> multipleInstanceHandler;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (JUCEApplicationBase)
|
||||
|
@ -49,10 +49,10 @@ class JUCE_API CallbackMessage : public MessageManager::MessageBase
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
CallbackMessage() noexcept {}
|
||||
CallbackMessage() = default;
|
||||
|
||||
/** Destructor. */
|
||||
~CallbackMessage() {}
|
||||
~CallbackMessage() override = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Called when the message is delivered.
|
||||
@ -63,7 +63,7 @@ public:
|
||||
Note that like all other messages, this object will be deleted immediately
|
||||
after this method has been invoked.
|
||||
*/
|
||||
virtual void messageCallback() = 0;
|
||||
virtual void messageCallback() override = 0;
|
||||
|
||||
private:
|
||||
// Avoid the leak-detector because for plugins, the host can unload our DLL with undelivered
|
||||
|
@ -116,7 +116,7 @@ public:
|
||||
#elif JUCE_ANDROID
|
||||
|
||||
#define JUCE_CREATE_APPLICATION_DEFINE(AppClass) \
|
||||
juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); }
|
||||
extern "C" juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); }
|
||||
|
||||
#define JUCE_MAIN_FUNCTION_DEFINITION
|
||||
|
||||
@ -155,7 +155,7 @@ public:
|
||||
|
||||
#if JUCE_IOS
|
||||
/**
|
||||
You can instruct JUCE to use a custom iOS app delegate class instaed of JUCE's default
|
||||
You can instruct JUCE to use a custom iOS app delegate class instead of JUCE's default
|
||||
app delegate. For JUCE to work you must pass all messages to JUCE's internal app delegate.
|
||||
Below is an example of minimal forwarding custom delegate. Note that you are at your own
|
||||
risk if you decide to use your own delegate and subtle, hard to debug bugs may occur.
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
//==============================================================================
|
||||
/** Creates an uninitialised message. */
|
||||
Message() noexcept;
|
||||
~Message();
|
||||
~Message() override;
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<Message>;
|
||||
|
||||
|
@ -35,7 +35,7 @@ void Message::messageCallback()
|
||||
MessageListener::MessageListener() noexcept
|
||||
{
|
||||
// Are you trying to create a messagelistener before or after juce has been intialised??
|
||||
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
|
||||
}
|
||||
|
||||
MessageListener::~MessageListener()
|
||||
|
@ -231,6 +231,22 @@ bool MessageManager::currentThreadHasLockedMessageManager() const noexcept
|
||||
return thisThread == messageThreadId || thisThread == threadWithLock.get();
|
||||
}
|
||||
|
||||
bool MessageManager::existsAndIsLockedByCurrentThread() noexcept
|
||||
{
|
||||
if (auto i = getInstanceWithoutCreating())
|
||||
return i->currentThreadHasLockedMessageManager();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageManager::existsAndIsCurrentThread() noexcept
|
||||
{
|
||||
if (auto i = getInstanceWithoutCreating())
|
||||
return i->isThisTheMessageThread();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
/* The only safe way to lock the message thread while another thread does
|
||||
@ -294,7 +310,7 @@ bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept
|
||||
|
||||
try
|
||||
{
|
||||
blockingMessage = new BlockingMessage (this);
|
||||
blockingMessage = *new BlockingMessage (this);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -349,7 +365,7 @@ void MessageManager::Lock::exit() const noexcept
|
||||
lockGained.set (0);
|
||||
|
||||
if (mm != nullptr)
|
||||
mm->threadWithLock = 0;
|
||||
mm->threadWithLock = {};
|
||||
|
||||
if (blockingMessage != nullptr)
|
||||
{
|
||||
@ -417,7 +433,7 @@ bool MessageManagerLock::attemptLock (Thread* threadToCheck, ThreadPoolJob* jobT
|
||||
return true;
|
||||
}
|
||||
|
||||
MessageManagerLock::~MessageManagerLock() noexcept { mmLock.exit(); }
|
||||
MessageManagerLock::~MessageManagerLock() { mmLock.exit(); }
|
||||
|
||||
void MessageManagerLock::exitSignalSent()
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ class OpenGLContext;
|
||||
|
||||
//==============================================================================
|
||||
/** See MessageManager::callFunctionOnMessageThread() for use of this function type. */
|
||||
typedef void* (MessageCallbackFunction) (void* userData);
|
||||
using MessageCallbackFunction = void* (void* userData);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
@ -147,6 +147,16 @@ public:
|
||||
*/
|
||||
bool currentThreadHasLockedMessageManager() const noexcept;
|
||||
|
||||
/** Returns true if there's an instance of the MessageManager, and if the current thread
|
||||
has the lock on it.
|
||||
*/
|
||||
static bool existsAndIsLockedByCurrentThread() noexcept;
|
||||
|
||||
/** Returns true if there's an instance of the MessageManager, and if the current thread
|
||||
is running it.
|
||||
*/
|
||||
static bool existsAndIsCurrentThread() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Sends a message to all other JUCE applications that are running.
|
||||
|
||||
@ -176,8 +186,8 @@ public:
|
||||
class JUCE_API MessageBase : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
MessageBase() noexcept {}
|
||||
virtual ~MessageBase() {}
|
||||
MessageBase() = default;
|
||||
~MessageBase() override = default;
|
||||
|
||||
virtual void messageCallback() = 0;
|
||||
bool post();
|
||||
@ -280,13 +290,13 @@ public:
|
||||
|
||||
//==============================================================================
|
||||
/** Provides the type of scoped lock to use with a CriticalSection. */
|
||||
typedef GenericScopedLock<Lock> ScopedLockType;
|
||||
using ScopedLockType = GenericScopedLock<Lock>;
|
||||
|
||||
/** Provides the type of scoped unlocker to use with a CriticalSection. */
|
||||
typedef GenericScopedUnlock<Lock> ScopedUnlockType;
|
||||
using ScopedUnlockType = GenericScopedUnlock<Lock>;
|
||||
|
||||
/** Provides the type of scoped try-locker to use with a CriticalSection. */
|
||||
typedef GenericScopedTryLock<Lock> ScopedTryLockType;
|
||||
using ScopedTryLockType = GenericScopedTryLock<Lock>;
|
||||
|
||||
private:
|
||||
struct BlockingMessage;
|
||||
@ -442,7 +452,7 @@ public:
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen!
|
||||
*/
|
||||
~MessageManagerLock() noexcept;
|
||||
~MessageManagerLock() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the lock was successfully acquired.
|
||||
@ -462,4 +472,28 @@ private:
|
||||
JUCE_DECLARE_NON_COPYABLE (MessageManagerLock)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This macro is used to catch unsafe use of functions which expect to only be called
|
||||
on the message thread, or when a MessageManagerLock is in place.
|
||||
It will also fail if you try to use the function before the message manager has been
|
||||
created, which could happen if you accidentally invoke it during a static constructor.
|
||||
*/
|
||||
#define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED \
|
||||
jassert (juce::MessageManager::existsAndIsLockedByCurrentThread());
|
||||
|
||||
/** This macro is used to catch unsafe use of functions which expect to only be called
|
||||
on the message thread.
|
||||
It will also fail if you try to use the function before the message manager has been
|
||||
created, which could happen if you accidentally invoke it during a static constructor.
|
||||
*/
|
||||
#define JUCE_ASSERT_MESSAGE_THREAD \
|
||||
jassert (juce::MessageManager::existsAndIsCurrentThread());
|
||||
|
||||
/** This macro is used to catch unsafe use of functions which expect to not be called
|
||||
outside the lifetime of the MessageManager.
|
||||
*/
|
||||
#define JUCE_ASSERT_MESSAGE_MANAGER_EXISTS \
|
||||
jassert (juce::MessageManager::getInstanceWithoutCreating() != nullptr);
|
||||
|
||||
|
||||
} // namespace juce
|
||||
|
@ -48,7 +48,6 @@ public:
|
||||
|
||||
private:
|
||||
JUCE_PUBLIC_IN_DLL_BUILD (struct Pimpl)
|
||||
friend struct ContainerDeletePolicy<Pimpl>;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MountedVolumeListChangeDetector)
|
||||
|
@ -50,7 +50,7 @@ namespace Android
|
||||
|
||||
struct Handler
|
||||
{
|
||||
Handler() : nativeHandler (getEnv()->NewObject (AndroidHandler, AndroidHandler.constructor)) {}
|
||||
Handler() : nativeHandler (LocalRef<jobject> (getEnv()->NewObject (AndroidHandler, AndroidHandler.constructor))) {}
|
||||
~Handler() { clearSingletonInstance(); }
|
||||
|
||||
JUCE_DECLARE_SINGLETON (Handler, false)
|
||||
@ -72,19 +72,19 @@ struct AndroidMessageQueue : private Android::Runnable
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED (AndroidMessageQueue, true)
|
||||
|
||||
AndroidMessageQueue()
|
||||
: self (CreateJavaInterface (this, "java/lang/Runnable").get())
|
||||
: self (CreateJavaInterface (this, "java/lang/Runnable"))
|
||||
{
|
||||
}
|
||||
|
||||
~AndroidMessageQueue()
|
||||
{
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
JUCE_ASSERT_MESSAGE_THREAD
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
bool post (MessageManager::MessageBase::Ptr&& message)
|
||||
{
|
||||
queue.add (static_cast<MessageManager::MessageBase::Ptr&& > (message));
|
||||
queue.add (std::move (message));
|
||||
|
||||
// this will call us on the message thread
|
||||
return handler.post (self.get());
|
||||
@ -94,7 +94,7 @@ private:
|
||||
|
||||
void run() override
|
||||
{
|
||||
while (true)
|
||||
for (;;)
|
||||
{
|
||||
MessageManager::MessageBase::Ptr message (queue.removeAndReturn (0));
|
||||
|
||||
@ -131,6 +131,7 @@ bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* cons
|
||||
{
|
||||
return AndroidMessageQueue::getInstance()->post (message);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::broadcastMessage (const String&)
|
||||
{
|
||||
@ -149,18 +150,26 @@ void MessageManager::stopDispatchLoop()
|
||||
void messageCallback() override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> activity (getCurrentActivity());
|
||||
|
||||
jmethodID quitMethod = env->GetMethodID (JuceAppActivity, "finishAndRemoveTask", "()V");
|
||||
|
||||
if (quitMethod != 0)
|
||||
if (activity != nullptr)
|
||||
{
|
||||
env->CallVoidMethod (android.activity, quitMethod);
|
||||
return;
|
||||
}
|
||||
jmethodID quitMethod = env->GetMethodID (AndroidActivity, "finishAndRemoveTask", "()V");
|
||||
|
||||
quitMethod = env->GetMethodID (JuceAppActivity, "finish", "()V");
|
||||
jassert (quitMethod != 0);
|
||||
env->CallVoidMethod (android.activity, quitMethod);
|
||||
if (quitMethod != 0)
|
||||
{
|
||||
env->CallVoidMethod (activity.get(), quitMethod);
|
||||
return;
|
||||
}
|
||||
|
||||
quitMethod = env->GetMethodID (AndroidActivity, "finish", "()V");
|
||||
jassert (quitMethod != 0);
|
||||
env->CallVoidMethod (activity.get(), quitMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -168,4 +177,134 @@ void MessageManager::stopDispatchLoop()
|
||||
quitMessagePosted = true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class JuceAppLifecycle : public ActivityLifecycleCallbacks
|
||||
{
|
||||
public:
|
||||
JuceAppLifecycle (juce::JUCEApplicationBase* (*initSymbolAddr)())
|
||||
: createApplicationSymbol (initSymbolAddr)
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
|
||||
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
|
||||
}
|
||||
}
|
||||
|
||||
~JuceAppLifecycle()
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr && myself != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
clear();
|
||||
env->CallVoidMethod (appContext.get(), AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
|
||||
myself.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void onActivityCreated (jobject, jobject) override
|
||||
{
|
||||
checkCreated();
|
||||
}
|
||||
|
||||
void onActivityDestroyed (jobject activity) override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
// if the main activity is being destroyed, only then tear-down JUCE
|
||||
if (env->IsSameObject (getMainActivity().get(), activity) != 0)
|
||||
{
|
||||
JUCEApplicationBase::appWillTerminateByForce();
|
||||
JNIClassBase::releaseAllClasses (env);
|
||||
|
||||
jclass systemClass = (jclass) env->FindClass ("java/lang/System");
|
||||
jmethodID exitMethod = env->GetStaticMethodID (systemClass, "exit", "(I)V");
|
||||
env->CallStaticVoidMethod (systemClass, exitMethod, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void onActivityStarted (jobject) override
|
||||
{
|
||||
checkCreated();
|
||||
}
|
||||
|
||||
void onActivityPaused (jobject) override
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
app->suspended();
|
||||
}
|
||||
|
||||
void onActivityResumed (jobject) override
|
||||
{
|
||||
checkInitialised();
|
||||
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
app->resumed();
|
||||
}
|
||||
|
||||
static JuceAppLifecycle& getInstance (juce::JUCEApplicationBase* (*initSymbolAddr)())
|
||||
{
|
||||
static JuceAppLifecycle juceAppLifecycle (initSymbolAddr);
|
||||
return juceAppLifecycle;
|
||||
}
|
||||
|
||||
private:
|
||||
void checkCreated()
|
||||
{
|
||||
if (JUCEApplicationBase::getInstance() == nullptr)
|
||||
{
|
||||
DBG (SystemStats::getJUCEVersion());
|
||||
|
||||
JUCEApplicationBase::createInstance = createApplicationSymbol;
|
||||
|
||||
initialiseJuce_GUI();
|
||||
|
||||
if (! JUCEApplicationBase::createInstance())
|
||||
jassertfalse; // you must supply an application object for an android app!
|
||||
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
}
|
||||
}
|
||||
|
||||
void checkInitialised()
|
||||
{
|
||||
checkCreated();
|
||||
|
||||
if (! hasBeenInitialised)
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
hasBeenInitialised = app->initialiseApp();
|
||||
|
||||
if (! hasBeenInitialised)
|
||||
exit (app->shutdownApp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobalRef myself;
|
||||
juce::JUCEApplicationBase* (*createApplicationSymbol)();
|
||||
bool hasBeenInitialised = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
File juce_getExecutableFile();
|
||||
|
||||
void juce_juceEventsAndroidStartApp()
|
||||
{
|
||||
auto dllPath = juce_getExecutableFile().getFullPathName();
|
||||
auto addr = reinterpret_cast<juce::JUCEApplicationBase*(*)()> (DynamicLibrary (dllPath)
|
||||
.getFunction ("juce_CreateApplication"));
|
||||
|
||||
if (addr != nullptr)
|
||||
JuceAppLifecycle::getInstance (addr);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -23,13 +23,13 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
typedef void (*AppFocusChangeCallback)();
|
||||
using AppFocusChangeCallback = void (*)();
|
||||
AppFocusChangeCallback appFocusChangeCallback = nullptr;
|
||||
|
||||
typedef bool (*CheckEventBlockedByModalComps) (NSEvent*);
|
||||
using CheckEventBlockedByModalComps = bool (*)(NSEvent*);
|
||||
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
|
||||
|
||||
typedef void (*MenuTrackingChangedCallback)(bool);
|
||||
using MenuTrackingChangedCallback = void (*)(bool);
|
||||
MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
@ -235,8 +235,10 @@ private:
|
||||
static String quotedIfContainsSpaces (NSString* file)
|
||||
{
|
||||
String s (nsStringToJuce (file));
|
||||
s = s.unquoted().replace ("\"", "\\\"");
|
||||
|
||||
if (s.containsChar (' '))
|
||||
s = s.quoted ('"');
|
||||
s = s.quoted();
|
||||
|
||||
return s;
|
||||
}
|
||||
@ -381,7 +383,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
|
||||
jassert (millisecondsToRunFor >= 0);
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
|
||||
uint32 endTime = Time::getMillisecondCounter() + (uint32) millisecondsToRunFor;
|
||||
auto endTime = Time::currentTimeMillis() + millisecondsToRunFor;
|
||||
|
||||
while (quitMessagePosted.get() == 0)
|
||||
{
|
||||
@ -397,7 +399,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
|
||||
if (e != nil && (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e)))
|
||||
[NSApp sendEvent: e];
|
||||
|
||||
if (Time::getMillisecondCounter() >= endTime)
|
||||
if (Time::currentTimeMillis() >= endTime)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -454,7 +456,7 @@ void __attribute__ ((visibility("default"))) repostCurrentNSEvent()
|
||||
struct EventReposter : public CallbackMessage
|
||||
{
|
||||
EventReposter() : e ([[NSApp currentEvent] retain]) {}
|
||||
~EventReposter() { [e release]; }
|
||||
~EventReposter() override { [e release]; }
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
|
@ -25,12 +25,16 @@ namespace juce
|
||||
|
||||
extern HWND juce_messageWindowHandle;
|
||||
|
||||
typedef bool (*CheckEventBlockedByModalComps) (const MSG&);
|
||||
using CheckEventBlockedByModalComps = bool (*)(const MSG&);
|
||||
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
|
||||
|
||||
typedef void (*SettingChangeCallbackFunc) (void);
|
||||
using SettingChangeCallbackFunc = void (*)(void);
|
||||
SettingChangeCallbackFunc settingChangeCallback = nullptr;
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity
|
||||
bool juce_isRunningInUnity();
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
namespace WindowsMessageHelpers
|
||||
{
|
||||
@ -42,7 +46,7 @@ namespace WindowsMessageHelpers
|
||||
|
||||
void dispatchMessageFromLParam (LPARAM lParam)
|
||||
{
|
||||
if (MessageManager::MessageBase* message = reinterpret_cast<MessageManager::MessageBase*> (lParam))
|
||||
if (auto message = reinterpret_cast<MessageManager::MessageBase*> (lParam))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
@ -68,7 +72,7 @@ namespace WindowsMessageHelpers
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void handleBroadcastMessage (const COPYDATASTRUCT* const data)
|
||||
void handleBroadcastMessage (const COPYDATASTRUCT* data)
|
||||
{
|
||||
if (data != nullptr && data->dwData == broadcastMessageMagicNumber)
|
||||
{
|
||||
@ -87,7 +91,7 @@ namespace WindowsMessageHelpers
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LRESULT CALLBACK messageWndProc (HWND h, const UINT message, const WPARAM wParam, const LPARAM lParam) noexcept
|
||||
LRESULT CALLBACK messageWndProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) noexcept
|
||||
{
|
||||
if (h == juce_messageWindowHandle)
|
||||
{
|
||||
@ -104,11 +108,10 @@ namespace WindowsMessageHelpers
|
||||
handleBroadcastMessage (reinterpret_cast<const COPYDATASTRUCT*> (lParam));
|
||||
return 0;
|
||||
}
|
||||
else if (message == WM_SETTINGCHANGE)
|
||||
{
|
||||
|
||||
if (message == WM_SETTINGCHANGE)
|
||||
if (settingChangeCallback != nullptr)
|
||||
settingChangeCallback();
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc (h, message, wParam, lParam);
|
||||
@ -120,7 +123,7 @@ LRESULT juce_offerEventToActiveXControl (::MSG&);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages)
|
||||
bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
{
|
||||
using namespace WindowsMessageHelpers;
|
||||
MSG m;
|
||||
@ -130,10 +133,10 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPend
|
||||
|
||||
if (GetMessage (&m, (HWND) 0, 0, 0) >= 0)
|
||||
{
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||
if (juce_offerEventToActiveXControl (m) != S_FALSE)
|
||||
return true;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (m.message == customMessageID && m.hwnd == juce_messageWindowHandle)
|
||||
{
|
||||
@ -151,7 +154,7 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPend
|
||||
{
|
||||
// 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();
|
||||
auto currentFocus = GetFocus();
|
||||
|
||||
if (currentFocus == 0 || JuceWindowIdentifier::isJUCEWindow (currentFocus))
|
||||
SetFocus (m.hwnd);
|
||||
@ -168,12 +171,18 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPend
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
message->incReferenceCount();
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity
|
||||
if (juce_isRunningInUnity())
|
||||
return SendNotifyMessage (juce_messageWindowHandle, WindowsMessageHelpers::customMessageID, 0, (LPARAM) message) != 0;
|
||||
#endif
|
||||
|
||||
return PostMessage (juce_messageWindowHandle, WindowsMessageHelpers::customMessageID, 0, (LPARAM) message) != 0;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String& value)
|
||||
{
|
||||
const String localCopy (value);
|
||||
auto localCopy = value;
|
||||
|
||||
Array<HWND> windows;
|
||||
EnumWindows (&WindowsMessageHelpers::broadcastEnumWindowProc, (LPARAM) &windows);
|
||||
|
@ -22,5 +22,60 @@
|
||||
|
||||
namespace juce
|
||||
{
|
||||
JUCE_IMPLEMENT_SINGLETON (WinRTWrapper)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
WinRTWrapper::~WinRTWrapper()
|
||||
{
|
||||
if (winRTHandle != nullptr)
|
||||
::FreeLibrary (winRTHandle);
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
String WinRTWrapper::hStringToString (HSTRING hstr)
|
||||
{
|
||||
if (isInitialised())
|
||||
if (const wchar_t* str = getHStringRawBuffer (hstr, nullptr))
|
||||
return String (str);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
WinRTWrapper::WinRTWrapper()
|
||||
{
|
||||
winRTHandle = ::LoadLibraryA ("api-ms-win-core-winrt-l1-1-0");
|
||||
|
||||
if (winRTHandle == nullptr)
|
||||
return;
|
||||
|
||||
roInitialize = (RoInitializeFuncPtr) ::GetProcAddress (winRTHandle, "RoInitialize");
|
||||
createHString = (WindowsCreateStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsCreateString");
|
||||
deleteHString = (WindowsDeleteStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsDeleteString");
|
||||
getHStringRawBuffer = (WindowsGetStringRawBufferFuncPtr) ::GetProcAddress (winRTHandle, "WindowsGetStringRawBuffer");
|
||||
roActivateInstance = (RoActivateInstanceFuncPtr) ::GetProcAddress (winRTHandle, "RoActivateInstance");
|
||||
roGetActivationFactory = (RoGetActivationFactoryFuncPtr) ::GetProcAddress (winRTHandle, "RoGetActivationFactory");
|
||||
|
||||
if (roInitialize == nullptr || createHString == nullptr || deleteHString == nullptr
|
||||
|| getHStringRawBuffer == nullptr || roActivateInstance == nullptr || roGetActivationFactory == nullptr)
|
||||
return;
|
||||
|
||||
HRESULT status = roInitialize (1);
|
||||
initialised = ! (status != S_OK && status != S_FALSE && status != 0x80010106L);
|
||||
}
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (WinRTWrapper)
|
||||
|
||||
}
|
||||
|
@ -26,29 +26,14 @@ 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 (String);
|
||||
|
||||
~ScopedHString()
|
||||
{
|
||||
if (WinRTWrapper::getInstance()->isInitialised() && hstr != nullptr)
|
||||
WinRTWrapper::getInstance()->deleteHString (hstr);
|
||||
}
|
||||
~ScopedHString();
|
||||
|
||||
HSTRING get() const noexcept
|
||||
{
|
||||
return hstr;
|
||||
}
|
||||
HSTRING get() const noexcept { return hstr; }
|
||||
|
||||
private:
|
||||
HSTRING hstr = nullptr;
|
||||
@ -56,36 +41,81 @@ public:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedHString)
|
||||
};
|
||||
|
||||
~WinRTWrapper()
|
||||
template <class ComClass>
|
||||
class ComPtr
|
||||
{
|
||||
if (winRTHandle != nullptr)
|
||||
::FreeLibrary (winRTHandle);
|
||||
public:
|
||||
ComPtr() noexcept {}
|
||||
ComPtr (ComClass* obj) : p (obj) { if (p) p->AddRef(); }
|
||||
ComPtr (const ComPtr& other) : p (other.p) { if (p) p->AddRef(); }
|
||||
~ComPtr() { release(); }
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
operator ComClass*() const noexcept { return p; }
|
||||
ComClass& operator*() const noexcept { return *p; }
|
||||
ComClass* operator->() const noexcept { return p; }
|
||||
|
||||
String hStringToString (HSTRING hstr)
|
||||
ComPtr& operator= (ComClass* const newP)
|
||||
{
|
||||
if (newP != nullptr)
|
||||
newP->AddRef();
|
||||
|
||||
release();
|
||||
p = newP;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComPtr& operator= (const ComPtr& newP) { return operator= (newP.p); }
|
||||
|
||||
ComClass** resetAndGetPointerAddress()
|
||||
{
|
||||
release();
|
||||
p = nullptr;
|
||||
return &p;
|
||||
}
|
||||
|
||||
private:
|
||||
ComClass* p = nullptr;
|
||||
|
||||
void release() { if (p != nullptr) p->Release(); }
|
||||
|
||||
ComClass** operator&() noexcept; // private to avoid it being used accidentally
|
||||
};
|
||||
|
||||
JUCE_DECLARE_SINGLETON (WinRTWrapper, true)
|
||||
|
||||
~WinRTWrapper();
|
||||
|
||||
String hStringToString (HSTRING);
|
||||
|
||||
bool isInitialised() const noexcept { return initialised; }
|
||||
|
||||
template <class ComClass>
|
||||
ComPtr<ComClass> activateInstance (const wchar_t* runtimeClassID, REFCLSID classUUID)
|
||||
{
|
||||
ComPtr<ComClass> result;
|
||||
|
||||
if (isInitialised())
|
||||
if (const wchar_t* str = getHStringRawBuffer (hstr, nullptr))
|
||||
return String (str);
|
||||
{
|
||||
ComPtr<IInspectable> inspectable;
|
||||
ScopedHString runtimeClass (runtimeClassID);
|
||||
auto hr = roActivateInstance (runtimeClass.get(), inspectable.resetAndGetPointerAddress());
|
||||
|
||||
return {};
|
||||
}
|
||||
if (SUCCEEDED (hr))
|
||||
inspectable->QueryInterface (classUUID, (void**) result.resetAndGetPointerAddress());
|
||||
}
|
||||
|
||||
bool isInitialised() const noexcept
|
||||
{
|
||||
return initialised;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class ComClass>
|
||||
ComSmartPtr<ComClass> getWRLFactory (const wchar_t* runtimeClassID)
|
||||
ComPtr<ComClass> getWRLFactory (const wchar_t* runtimeClassID)
|
||||
{
|
||||
ComSmartPtr<ComClass> comPtr;
|
||||
ComPtr<ComClass> comPtr;
|
||||
|
||||
if (isInitialised())
|
||||
{
|
||||
ScopedHString classID (runtimeClassID);
|
||||
|
||||
if (classID.get() != nullptr)
|
||||
roGetActivationFactory (classID.get(), __uuidof (ComClass), (void**) comPtr.resetAndGetPointerAddress());
|
||||
}
|
||||
@ -94,25 +124,7 @@ public:
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
WinRTWrapper();
|
||||
|
||||
HMODULE winRTHandle = nullptr;
|
||||
bool initialised = false;
|
||||
@ -121,12 +133,14 @@ private:
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -28,7 +28,7 @@ class Timer::TimerThread : private Thread,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
typedef CriticalSection LockType; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
|
||||
using LockType = CriticalSection; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
|
||||
|
||||
TimerThread() : Thread ("JUCE Timer")
|
||||
{
|
||||
@ -36,7 +36,7 @@ public:
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
~TimerThread() noexcept
|
||||
~TimerThread() override
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
callbackArrived.signal();
|
||||
@ -324,7 +324,7 @@ void Timer::startTimer (int interval) noexcept
|
||||
{
|
||||
// If you're calling this before (or after) the MessageManager is
|
||||
// running, then you're not going to get any timer callbacks!
|
||||
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
|
||||
|
||||
const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
|
||||
|
||||
|
Reference in New Issue
Block a user