mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Added several linux improvements to the event loop, MIDI and X11 (see commit messsage)
MIDI: Several ALSA Seq improvements =================================== Many things were done in this patch (I couldn't really split in several patches): * Only one ALSA Sequencer client per application * ALSA Sequencer client name is the application's name by default * Fixed a bug when getDeivces() would return devices created by the application itself * Only ports created with createNewDevice() are allowed to be subscribed, other ports (created by openDevice()) doesn't allow subscription * AlsaPort is now handled by AlsaClient, basically having the proper representation such as the ALSA Sequencer has. Files: Fix default directory paths ================================== Some information on: * Linux LSB FHS: http://www.linuxfoundation.org/collaborate/workgroups/lsb/fhs-30 * https://wiki.archlinux.org/index.php/Xdg_user_directories Refactor Event loop to remove X11 dependency ============================================ The goal of this refactor was to remove X11 dependencies on juce_events to enable non-gui applications (example, Embedded Linux apps) to have no libx11 dependency. The side-effect of this refactor is easy implementation of other Linux graphical back-end, cleanup some code, better handling of X displays and other benefits. I removed a lot of the code from juce_linux_Windowing to separate files for clarity. I also renamed all Linux X11 files to *linux_X11* instead of just *linux*. X11: Remove unnecessary XGrabButton call ======================================== This call is made unnecessary if used proper window flags when a window is created.
This commit is contained in:
parent
12b9c01ad6
commit
c2100022cc
22 changed files with 1480 additions and 1243 deletions
|
|
@ -31,118 +31,276 @@
|
|||
#if JUCE_ALSA
|
||||
|
||||
// You can define these strings in your app if you want to override the default names:
|
||||
#ifndef JUCE_ALSA_MIDI_INPUT_NAME
|
||||
#define JUCE_ALSA_MIDI_INPUT_NAME "Juce Midi Input"
|
||||
#endif
|
||||
|
||||
#ifndef JUCE_ALSA_MIDI_OUTPUT_NAME
|
||||
#define JUCE_ALSA_MIDI_OUTPUT_NAME "Juce Midi Output"
|
||||
#ifndef JUCE_ALSA_MIDI_NAME
|
||||
#define JUCE_ALSA_MIDI_NAME JUCEApplicationBase::getInstance()->getApplicationName().toUTF8()
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
namespace
|
||||
{
|
||||
|
||||
class AlsaPortAndCallback;
|
||||
|
||||
//==============================================================================
|
||||
class AlsaClient : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
typedef ReferenceCountedObjectPtr<AlsaClient> Ptr;
|
||||
|
||||
static Ptr getInstance (bool forInput)
|
||||
//==============================================================================
|
||||
// represents an input or output port of the supplied AlsaClient
|
||||
class Port
|
||||
{
|
||||
public:
|
||||
Port (AlsaClient& c, bool forInput) noexcept
|
||||
: portId (-1),
|
||||
callbackEnabled (false),
|
||||
client (c),
|
||||
isInput (forInput),
|
||||
callback (nullptr),
|
||||
maxEventSize (4 * 1024),
|
||||
midiInput (nullptr)
|
||||
{}
|
||||
|
||||
~Port()
|
||||
{
|
||||
if (isValid())
|
||||
{
|
||||
if (isInput)
|
||||
enableCallback (false);
|
||||
else
|
||||
snd_midi_event_free (midiParser);
|
||||
|
||||
snd_seq_delete_simple_port (client.get(), portId);
|
||||
}
|
||||
}
|
||||
|
||||
void connectWith (int sourceClient, int sourcePort) const noexcept
|
||||
{
|
||||
if (isInput)
|
||||
snd_seq_connect_from (client.get(), portId, sourceClient, sourcePort);
|
||||
else
|
||||
snd_seq_connect_to (client.get(), portId, sourceClient, sourcePort);
|
||||
}
|
||||
|
||||
bool isValid() const noexcept
|
||||
{
|
||||
return client.get() != nullptr && portId >= 0;
|
||||
}
|
||||
|
||||
void setupInput(MidiInput* input, MidiInputCallback* cb)
|
||||
{
|
||||
jassert (cb && input);
|
||||
|
||||
callback = cb;
|
||||
midiInput = input;
|
||||
}
|
||||
|
||||
void setupOutput()
|
||||
{
|
||||
jassert (! isInput);
|
||||
|
||||
snd_midi_event_new ((size_t) maxEventSize, &midiParser);
|
||||
}
|
||||
|
||||
void enableCallback (bool enable)
|
||||
{
|
||||
if (callbackEnabled != enable)
|
||||
{
|
||||
callbackEnabled = enable;
|
||||
|
||||
if (enable)
|
||||
client.registerCallback();
|
||||
else
|
||||
client.unregisterCallback();
|
||||
}
|
||||
}
|
||||
|
||||
bool sendMessageNow (const MidiMessage& message)
|
||||
{
|
||||
if (message.getRawDataSize() > maxEventSize)
|
||||
{
|
||||
maxEventSize = message.getRawDataSize();
|
||||
snd_midi_event_free (midiParser);
|
||||
snd_midi_event_new ((size_t) maxEventSize, &midiParser);
|
||||
}
|
||||
|
||||
snd_seq_event_t event;
|
||||
snd_seq_ev_clear (&event);
|
||||
|
||||
long numBytes = (long) message.getRawDataSize();
|
||||
const uint8* data = message.getRawData();
|
||||
|
||||
snd_seq_t* seqHandle = client.get();
|
||||
bool success = true;
|
||||
|
||||
while (numBytes > 0)
|
||||
{
|
||||
const long numSent = snd_midi_event_encode (midiParser, data, numBytes, &event);
|
||||
|
||||
if (numSent <= 0)
|
||||
{
|
||||
success = numSent == 0;
|
||||
break;
|
||||
}
|
||||
|
||||
numBytes -= numSent;
|
||||
data += numSent;
|
||||
|
||||
snd_seq_ev_set_source (&event, portId);
|
||||
snd_seq_ev_set_subs (&event);
|
||||
snd_seq_ev_set_direct (&event);
|
||||
|
||||
if (snd_seq_event_output_direct (seqHandle, &event) < 0)
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snd_midi_event_reset_encode (midiParser);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool operator== (const Port& lhs) const noexcept
|
||||
{
|
||||
return portId != -1 && portId == lhs.portId;
|
||||
}
|
||||
|
||||
int portId;
|
||||
bool callbackEnabled;
|
||||
|
||||
private:
|
||||
friend class AlsaClient;
|
||||
|
||||
AlsaClient& client;
|
||||
bool isInput;
|
||||
MidiInputCallback* callback;
|
||||
snd_midi_event_t* midiParser;
|
||||
int maxEventSize;
|
||||
MidiInput* midiInput;
|
||||
|
||||
void createPort (const String& name, bool enableSubscription)
|
||||
{
|
||||
if (snd_seq_t* seqHandle = client.get())
|
||||
{
|
||||
const unsigned int caps =
|
||||
isInput
|
||||
? (SND_SEQ_PORT_CAP_WRITE | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_WRITE : 0))
|
||||
: (SND_SEQ_PORT_CAP_WRITE | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_READ : 0));
|
||||
portId = snd_seq_create_simple_port (seqHandle, name.toUTF8(), caps,
|
||||
SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
||||
SND_SEQ_PORT_TYPE_APPLICATION);
|
||||
}
|
||||
}
|
||||
|
||||
void handleIncomingMidiMessage (const MidiMessage& message) const
|
||||
{
|
||||
callback->handleIncomingMidiMessage (midiInput, message);
|
||||
}
|
||||
|
||||
void handlePartialSysexMessage (const uint8* messageData, int numBytesSoFar, double timeStamp)
|
||||
{
|
||||
callback->handlePartialSysexMessage (midiInput, messageData, numBytesSoFar, timeStamp);
|
||||
}
|
||||
};
|
||||
|
||||
static Ptr getInstance()
|
||||
{
|
||||
AlsaClient*& instance = (forInput ? inInstance : outInstance);
|
||||
if (instance == nullptr)
|
||||
instance = new AlsaClient (forInput);
|
||||
instance = new AlsaClient();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool isInput() const noexcept { return input; }
|
||||
|
||||
void registerCallback (AlsaPortAndCallback* cb)
|
||||
void registerCallback ()
|
||||
{
|
||||
if (cb != nullptr)
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
activeCallbacks.add (cb);
|
||||
|
||||
if (inputThread == nullptr)
|
||||
inputThread = new MidiInputThread (*this);
|
||||
}
|
||||
if (inputThread == nullptr)
|
||||
inputThread = new MidiInputThread (*this);
|
||||
|
||||
if (++activeCallbacks - 1 == 0)
|
||||
inputThread->startThread();
|
||||
}
|
||||
}
|
||||
|
||||
void unregisterCallback (AlsaPortAndCallback* cb)
|
||||
void unregisterCallback ()
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
jassert (activeCallbacks.contains (cb));
|
||||
activeCallbacks.removeAllInstancesOf (cb);
|
||||
|
||||
if (activeCallbacks.size() == 0 && inputThread->isThreadRunning())
|
||||
jassert (activeCallbacks.get() > 0);
|
||||
if (--activeCallbacks == 0 && inputThread->isThreadRunning())
|
||||
inputThread->signalThreadShouldExit();
|
||||
}
|
||||
|
||||
void handleIncomingMidiMessage (snd_seq_event*, const MidiMessage&);
|
||||
void handlePartialSysexMessage (snd_seq_event*, const uint8*, int, double);
|
||||
void handleIncomingMidiMessage (snd_seq_event* event, const MidiMessage& message)
|
||||
{
|
||||
if (event->dest.port < ports.size()
|
||||
&& ports[event->dest.port]->callbackEnabled)
|
||||
ports[event->dest.port]->handleIncomingMidiMessage (message);
|
||||
}
|
||||
|
||||
void handlePartialSysexMessage (snd_seq_event* event, const uint8* messageData, int numBytesSoFar, double timeStamp)
|
||||
{
|
||||
if (event->dest.port < ports.size()
|
||||
&& ports[event->dest.port]->callbackEnabled)
|
||||
ports[event->dest.port]->handlePartialSysexMessage (messageData, numBytesSoFar, timeStamp);
|
||||
}
|
||||
|
||||
snd_seq_t* get() const noexcept { return handle; }
|
||||
int getId() const noexcept { return clientId; }
|
||||
|
||||
Port* createPort (const String& name, bool forInput, bool enableSubscription)
|
||||
{
|
||||
Port* port = new Port (*this, forInput);
|
||||
port->createPort (name, enableSubscription);
|
||||
ports.set (port->portId, port);
|
||||
incReferenceCount();
|
||||
return port;
|
||||
}
|
||||
|
||||
void deletePort (Port* port)
|
||||
{
|
||||
ports.remove (port->portId);
|
||||
decReferenceCount();
|
||||
}
|
||||
|
||||
private:
|
||||
bool input;
|
||||
snd_seq_t* handle;
|
||||
|
||||
Array<AlsaPortAndCallback*> activeCallbacks;
|
||||
int clientId;
|
||||
OwnedArray<Port> ports;
|
||||
Atomic<int> activeCallbacks;
|
||||
CriticalSection callbackLock;
|
||||
|
||||
static AlsaClient* inInstance;
|
||||
static AlsaClient* outInstance;
|
||||
static AlsaClient* instance;
|
||||
|
||||
//==============================================================================
|
||||
friend class ReferenceCountedObjectPtr<AlsaClient>;
|
||||
friend struct ContainerDeletePolicy<AlsaClient>;
|
||||
|
||||
AlsaClient (bool forInput)
|
||||
: input (forInput), handle (nullptr)
|
||||
AlsaClient ()
|
||||
: handle (nullptr),
|
||||
inputThread (nullptr)
|
||||
{
|
||||
AlsaClient*& instance = (input ? inInstance : outInstance);
|
||||
jassert (instance == nullptr);
|
||||
|
||||
instance = this;
|
||||
snd_seq_open (&handle, "default", SND_SEQ_OPEN_DUPLEX, 0);
|
||||
snd_seq_nonblock (handle, SND_SEQ_NONBLOCK);
|
||||
snd_seq_set_client_name (handle, JUCE_ALSA_MIDI_NAME);
|
||||
clientId = snd_seq_client_id(handle);
|
||||
|
||||
snd_seq_open (&handle, "default", forInput ? SND_SEQ_OPEN_INPUT
|
||||
: SND_SEQ_OPEN_OUTPUT, 0);
|
||||
|
||||
snd_seq_set_client_name (handle, forInput ? JUCE_ALSA_MIDI_INPUT_NAME
|
||||
: JUCE_ALSA_MIDI_OUTPUT_NAME);
|
||||
// It's good idea to pre-allocate a good number of elements
|
||||
ports.ensureStorageAllocated (32);
|
||||
}
|
||||
|
||||
~AlsaClient()
|
||||
{
|
||||
AlsaClient*& instance = (input ? inInstance : outInstance);
|
||||
jassert (instance != nullptr);
|
||||
|
||||
instance = nullptr;
|
||||
|
||||
if (handle != nullptr)
|
||||
{
|
||||
snd_seq_close (handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
|
||||
jassert (activeCallbacks.size() == 0);
|
||||
jassert (activeCallbacks.get() == 0);
|
||||
|
||||
if (inputThread)
|
||||
{
|
||||
inputThread->stopThread (3000);
|
||||
inputThread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -152,7 +310,7 @@ private:
|
|||
MidiInputThread (AlsaClient& c)
|
||||
: Thread ("Juce MIDI Input"), client (c), concatenator (2048)
|
||||
{
|
||||
jassert (client.input && client.get() != nullptr);
|
||||
jassert (client.get() != nullptr);
|
||||
}
|
||||
|
||||
void run() override
|
||||
|
|
@ -176,8 +334,6 @@ private:
|
|||
if (threadShouldExit())
|
||||
break;
|
||||
|
||||
snd_seq_nonblock (seqHandle, 1);
|
||||
|
||||
do
|
||||
{
|
||||
snd_seq_event_t* inputEvent = nullptr;
|
||||
|
|
@ -213,202 +369,91 @@ private:
|
|||
ScopedPointer<MidiInputThread> inputThread;
|
||||
};
|
||||
|
||||
AlsaClient* AlsaClient::inInstance = nullptr;
|
||||
AlsaClient* AlsaClient::outInstance = nullptr;
|
||||
AlsaClient* AlsaClient::instance = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
// represents an input or output port of the supplied AlsaClient
|
||||
class AlsaPort
|
||||
static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client,
|
||||
snd_seq_client_info_t* clientInfo,
|
||||
const bool forInput,
|
||||
StringArray& deviceNamesFound,
|
||||
const int deviceIndexToOpen)
|
||||
{
|
||||
public:
|
||||
AlsaPort() noexcept : portId (-1) {}
|
||||
AlsaPort (const AlsaClient::Ptr& c, int port) noexcept : client (c), portId (port) {}
|
||||
AlsaClient::Port* port = nullptr;
|
||||
|
||||
void createPort (const AlsaClient::Ptr& c, const String& name, bool forInput)
|
||||
{
|
||||
client = c;
|
||||
|
||||
if (snd_seq_t* handle = client->get())
|
||||
portId = snd_seq_create_simple_port (handle, name.toUTF8(),
|
||||
forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)
|
||||
: (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ),
|
||||
SND_SEQ_PORT_TYPE_MIDI_GENERIC);
|
||||
}
|
||||
|
||||
void deletePort()
|
||||
{
|
||||
if (isValid())
|
||||
{
|
||||
snd_seq_delete_simple_port (client->get(), portId);
|
||||
portId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void connectWith (int sourceClient, int sourcePort)
|
||||
{
|
||||
if (client->isInput())
|
||||
snd_seq_connect_from (client->get(), portId, sourceClient, sourcePort);
|
||||
else
|
||||
snd_seq_connect_to (client->get(), portId, sourceClient, sourcePort);
|
||||
}
|
||||
|
||||
bool isValid() const noexcept
|
||||
{
|
||||
return client != nullptr && client->get() != nullptr && portId >= 0;
|
||||
}
|
||||
|
||||
AlsaClient::Ptr client;
|
||||
int portId;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class AlsaPortAndCallback
|
||||
{
|
||||
public:
|
||||
AlsaPortAndCallback (AlsaPort p, MidiInput* in, MidiInputCallback* cb)
|
||||
: port (p), midiInput (in), callback (cb), callbackEnabled (false)
|
||||
{
|
||||
}
|
||||
|
||||
~AlsaPortAndCallback()
|
||||
{
|
||||
enableCallback (false);
|
||||
port.deletePort();
|
||||
}
|
||||
|
||||
void enableCallback (bool enable)
|
||||
{
|
||||
if (callbackEnabled != enable)
|
||||
{
|
||||
callbackEnabled = enable;
|
||||
|
||||
if (enable)
|
||||
port.client->registerCallback (this);
|
||||
else
|
||||
port.client->unregisterCallback (this);
|
||||
}
|
||||
}
|
||||
|
||||
void handleIncomingMidiMessage (const MidiMessage& message) const
|
||||
{
|
||||
callback->handleIncomingMidiMessage (midiInput, message);
|
||||
}
|
||||
|
||||
void handlePartialSysexMessage (const uint8* messageData, int numBytesSoFar, double timeStamp)
|
||||
{
|
||||
callback->handlePartialSysexMessage (midiInput, messageData, numBytesSoFar, timeStamp);
|
||||
}
|
||||
|
||||
private:
|
||||
AlsaPort port;
|
||||
MidiInput* midiInput;
|
||||
MidiInputCallback* callback;
|
||||
bool callbackEnabled;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlsaPortAndCallback)
|
||||
};
|
||||
|
||||
void AlsaClient::handleIncomingMidiMessage (snd_seq_event_t* event, const MidiMessage& message)
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (AlsaPortAndCallback* const cb = activeCallbacks[event->dest.port])
|
||||
cb->handleIncomingMidiMessage (message);
|
||||
}
|
||||
|
||||
void AlsaClient::handlePartialSysexMessage (snd_seq_event* event, const uint8* messageData, int numBytesSoFar, double timeStamp)
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (AlsaPortAndCallback* const cb = activeCallbacks[event->dest.port])
|
||||
cb->handlePartialSysexMessage (messageData, numBytesSoFar, timeStamp);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static AlsaPort iterateMidiClient (const AlsaClient::Ptr& seq,
|
||||
snd_seq_client_info_t* clientInfo,
|
||||
const bool forInput,
|
||||
StringArray& deviceNamesFound,
|
||||
const int deviceIndexToOpen)
|
||||
{
|
||||
AlsaPort port;
|
||||
|
||||
snd_seq_t* seqHandle = seq->get();
|
||||
snd_seq_t* seqHandle = client->get();
|
||||
snd_seq_port_info_t* portInfo = nullptr;
|
||||
|
||||
if (snd_seq_port_info_malloc (&portInfo) == 0)
|
||||
snd_seq_port_info_alloca (&portInfo);
|
||||
jassert (portInfo);
|
||||
int numPorts = snd_seq_client_info_get_num_ports (clientInfo);
|
||||
const int sourceClient = snd_seq_client_info_get_client (clientInfo);
|
||||
|
||||
snd_seq_port_info_set_client (portInfo, sourceClient);
|
||||
snd_seq_port_info_set_port (portInfo, -1);
|
||||
|
||||
while (--numPorts >= 0)
|
||||
{
|
||||
int numPorts = snd_seq_client_info_get_num_ports (clientInfo);
|
||||
const int client = snd_seq_client_info_get_client (clientInfo);
|
||||
|
||||
snd_seq_port_info_set_client (portInfo, client);
|
||||
snd_seq_port_info_set_port (portInfo, -1);
|
||||
|
||||
while (--numPorts >= 0)
|
||||
if (snd_seq_query_next_port (seqHandle, portInfo) == 0
|
||||
&& (snd_seq_port_info_get_capability (portInfo)
|
||||
& (forInput ? SND_SEQ_PORT_CAP_SUBS_WRITE : SND_SEQ_PORT_CAP_SUBS_READ)) != 0)
|
||||
{
|
||||
if (snd_seq_query_next_port (seqHandle, portInfo) == 0
|
||||
&& (snd_seq_port_info_get_capability (portInfo) & (forInput ? SND_SEQ_PORT_CAP_READ
|
||||
: SND_SEQ_PORT_CAP_WRITE)) != 0)
|
||||
const String portName = snd_seq_port_info_get_name(portInfo);
|
||||
|
||||
deviceNamesFound.add (portName);
|
||||
|
||||
if (deviceNamesFound.size() == deviceIndexToOpen + 1)
|
||||
{
|
||||
const String clientName = snd_seq_client_info_get_name (clientInfo);
|
||||
const String portName = snd_seq_port_info_get_name(portInfo);
|
||||
|
||||
if (clientName == portName)
|
||||
deviceNamesFound.add (clientName);
|
||||
else
|
||||
deviceNamesFound.add (clientName + ": " + portName);
|
||||
|
||||
if (deviceNamesFound.size() == deviceIndexToOpen + 1)
|
||||
const int sourcePort = snd_seq_port_info_get_port (portInfo);
|
||||
if (sourcePort != -1)
|
||||
{
|
||||
const int sourcePort = snd_seq_port_info_get_port (portInfo);
|
||||
|
||||
if (sourcePort != -1)
|
||||
{
|
||||
const int sourceClient = snd_seq_client_info_get_client (clientInfo);
|
||||
|
||||
port.createPort (seq, portName, forInput);
|
||||
port.connectWith (sourceClient, sourcePort);
|
||||
}
|
||||
port = client->createPort (portName, forInput, false);
|
||||
jassert (port->isValid());
|
||||
port->connectWith (sourceClient, sourcePort);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snd_seq_port_info_free (portInfo);
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
static AlsaPort iterateMidiDevices (const bool forInput,
|
||||
StringArray& deviceNamesFound,
|
||||
const int deviceIndexToOpen)
|
||||
static AlsaClient::Port* iterateMidiDevices (const bool forInput,
|
||||
StringArray& deviceNamesFound,
|
||||
const int deviceIndexToOpen)
|
||||
{
|
||||
AlsaPort port;
|
||||
const AlsaClient::Ptr client (AlsaClient::getInstance (forInput));
|
||||
AlsaClient::Port* port = nullptr;
|
||||
const AlsaClient::Ptr client (AlsaClient::getInstance());
|
||||
|
||||
if (snd_seq_t* const seqHandle = client->get())
|
||||
{
|
||||
snd_seq_system_info_t* systemInfo = nullptr;
|
||||
snd_seq_client_info_t* clientInfo = nullptr;
|
||||
|
||||
if (snd_seq_system_info_malloc (&systemInfo) == 0)
|
||||
snd_seq_system_info_alloca (&systemInfo);
|
||||
jassert(systemInfo);
|
||||
if (snd_seq_system_info (seqHandle, systemInfo) == 0)
|
||||
{
|
||||
if (snd_seq_system_info (seqHandle, systemInfo) == 0
|
||||
&& snd_seq_client_info_malloc (&clientInfo) == 0)
|
||||
{
|
||||
int numClients = snd_seq_system_info_get_cur_clients (systemInfo);
|
||||
snd_seq_client_info_alloca (&clientInfo);
|
||||
jassert(clientInfo);
|
||||
int numClients = snd_seq_system_info_get_cur_clients (systemInfo);
|
||||
|
||||
while (--numClients >= 0 && ! port.isValid())
|
||||
if (snd_seq_query_next_client (seqHandle, clientInfo) == 0)
|
||||
while (--numClients >= 0)
|
||||
{
|
||||
if (snd_seq_query_next_client (seqHandle, clientInfo) == 0)
|
||||
{
|
||||
const int sourceClient = snd_seq_client_info_get_client (clientInfo);
|
||||
if (sourceClient != client->getId()
|
||||
&& sourceClient != SND_SEQ_CLIENT_SYSTEM)
|
||||
{
|
||||
port = iterateMidiClient (client, clientInfo, forInput,
|
||||
deviceNamesFound, deviceIndexToOpen);
|
||||
|
||||
snd_seq_client_info_free (clientInfo);
|
||||
if (port)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snd_seq_system_info_free (systemInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
deviceNamesFound.appendNumbersToDuplicates (true, true);
|
||||
|
|
@ -416,80 +461,6 @@ static AlsaPort iterateMidiDevices (const bool forInput,
|
|||
return port;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class MidiOutputDevice
|
||||
{
|
||||
public:
|
||||
MidiOutputDevice (MidiOutput* const output, const AlsaPort& p)
|
||||
: midiOutput (output), port (p),
|
||||
maxEventSize (16 * 1024)
|
||||
{
|
||||
jassert (port.isValid() && midiOutput != nullptr);
|
||||
snd_midi_event_new ((size_t) maxEventSize, &midiParser);
|
||||
}
|
||||
|
||||
~MidiOutputDevice()
|
||||
{
|
||||
snd_midi_event_free (midiParser);
|
||||
port.deletePort();
|
||||
}
|
||||
|
||||
bool sendMessageNow (const MidiMessage& message)
|
||||
{
|
||||
if (message.getRawDataSize() > maxEventSize)
|
||||
{
|
||||
maxEventSize = message.getRawDataSize();
|
||||
snd_midi_event_free (midiParser);
|
||||
snd_midi_event_new ((size_t) maxEventSize, &midiParser);
|
||||
}
|
||||
|
||||
snd_seq_event_t event;
|
||||
snd_seq_ev_clear (&event);
|
||||
|
||||
long numBytes = (long) message.getRawDataSize();
|
||||
const uint8* data = message.getRawData();
|
||||
|
||||
snd_seq_t* seqHandle = port.client->get();
|
||||
bool success = true;
|
||||
|
||||
while (numBytes > 0)
|
||||
{
|
||||
const long numSent = snd_midi_event_encode (midiParser, data, numBytes, &event);
|
||||
|
||||
if (numSent <= 0)
|
||||
{
|
||||
success = numSent == 0;
|
||||
break;
|
||||
}
|
||||
|
||||
numBytes -= numSent;
|
||||
data += numSent;
|
||||
|
||||
snd_seq_ev_set_source (&event, port.portId);
|
||||
snd_seq_ev_set_subs (&event);
|
||||
snd_seq_ev_set_direct (&event);
|
||||
|
||||
if (snd_seq_event_output_direct (seqHandle, &event) < 0)
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snd_midi_event_reset_encode (midiParser);
|
||||
return success;
|
||||
}
|
||||
|
||||
private:
|
||||
MidiOutput* const midiOutput;
|
||||
AlsaPort port;
|
||||
snd_midi_event_t* midiParser;
|
||||
int maxEventSize;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutputDevice)
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
StringArray MidiOutput::getDevices()
|
||||
|
|
@ -509,13 +480,16 @@ MidiOutput* MidiOutput::openDevice (int deviceIndex)
|
|||
MidiOutput* newDevice = nullptr;
|
||||
|
||||
StringArray devices;
|
||||
AlsaPort port (iterateMidiDevices (false, devices, deviceIndex));
|
||||
AlsaClient::Port* port = iterateMidiDevices (false, devices, deviceIndex);
|
||||
|
||||
if (port.isValid())
|
||||
{
|
||||
newDevice = new MidiOutput (devices [deviceIndex]);
|
||||
newDevice->internal = new MidiOutputDevice (newDevice, port);
|
||||
}
|
||||
if (port == nullptr)
|
||||
return nullptr;
|
||||
|
||||
jassert (port->isValid());
|
||||
|
||||
newDevice = new MidiOutput (devices [deviceIndex]);
|
||||
port->setupOutput();
|
||||
newDevice->internal = port;
|
||||
|
||||
return newDevice;
|
||||
}
|
||||
|
|
@ -523,17 +497,16 @@ MidiOutput* MidiOutput::openDevice (int deviceIndex)
|
|||
MidiOutput* MidiOutput::createNewDevice (const String& deviceName)
|
||||
{
|
||||
MidiOutput* newDevice = nullptr;
|
||||
AlsaPort port;
|
||||
|
||||
const AlsaClient::Ptr client (AlsaClient::getInstance (false));
|
||||
const AlsaClient::Ptr client (AlsaClient::getInstance());
|
||||
|
||||
port.createPort (client, deviceName, false);
|
||||
AlsaClient::Port* port = client->createPort (deviceName, false, true);
|
||||
|
||||
if (port.isValid())
|
||||
{
|
||||
newDevice = new MidiOutput (deviceName);
|
||||
newDevice->internal = new MidiOutputDevice (newDevice, port);
|
||||
}
|
||||
jassert (port->isValid());
|
||||
|
||||
newDevice = new MidiOutput (deviceName);
|
||||
port->setupOutput();
|
||||
newDevice->internal = port;
|
||||
|
||||
return newDevice;
|
||||
}
|
||||
|
|
@ -542,12 +515,13 @@ MidiOutput::~MidiOutput()
|
|||
{
|
||||
stopBackgroundThread();
|
||||
|
||||
delete static_cast<MidiOutputDevice*> (internal);
|
||||
AlsaClient::Ptr client (AlsaClient::getInstance());
|
||||
client->deletePort (static_cast<AlsaClient::Port*> (internal));
|
||||
}
|
||||
|
||||
void MidiOutput::sendMessageNow (const MidiMessage& message)
|
||||
{
|
||||
static_cast<MidiOutputDevice*> (internal)->sendMessageNow (message);
|
||||
static_cast<AlsaClient::Port*> (internal)->sendMessageNow (message);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -559,17 +533,18 @@ MidiInput::MidiInput (const String& nm)
|
|||
MidiInput::~MidiInput()
|
||||
{
|
||||
stop();
|
||||
delete static_cast<AlsaPortAndCallback*> (internal);
|
||||
AlsaClient::Ptr client (AlsaClient::getInstance());
|
||||
client->deletePort (static_cast<AlsaClient::Port*> (internal));
|
||||
}
|
||||
|
||||
void MidiInput::start()
|
||||
{
|
||||
static_cast<AlsaPortAndCallback*> (internal)->enableCallback (true);
|
||||
static_cast<AlsaClient::Port*> (internal)->enableCallback (true);
|
||||
}
|
||||
|
||||
void MidiInput::stop()
|
||||
{
|
||||
static_cast<AlsaPortAndCallback*> (internal)->enableCallback (false);
|
||||
static_cast<AlsaClient::Port*> (internal)->enableCallback (false);
|
||||
}
|
||||
|
||||
int MidiInput::getDefaultDeviceIndex()
|
||||
|
|
@ -589,13 +564,16 @@ MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback)
|
|||
MidiInput* newDevice = nullptr;
|
||||
|
||||
StringArray devices;
|
||||
AlsaPort port (iterateMidiDevices (true, devices, deviceIndex));
|
||||
AlsaClient::Port* port = iterateMidiDevices (true, devices, deviceIndex);
|
||||
|
||||
if (port.isValid())
|
||||
{
|
||||
newDevice = new MidiInput (devices [deviceIndex]);
|
||||
newDevice->internal = new AlsaPortAndCallback (port, newDevice, callback);
|
||||
}
|
||||
if (port == nullptr)
|
||||
return nullptr;
|
||||
|
||||
jassert (port->isValid());
|
||||
|
||||
newDevice = new MidiInput (devices [deviceIndex]);
|
||||
port->setupInput (newDevice, callback);
|
||||
newDevice->internal = port;
|
||||
|
||||
return newDevice;
|
||||
}
|
||||
|
|
@ -603,17 +581,16 @@ MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback)
|
|||
MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
|
||||
{
|
||||
MidiInput* newDevice = nullptr;
|
||||
AlsaPort port;
|
||||
|
||||
const AlsaClient::Ptr client (AlsaClient::getInstance (true));
|
||||
AlsaClient::Ptr client (AlsaClient::getInstance());
|
||||
|
||||
port.createPort (client, deviceName, true);
|
||||
AlsaClient::Port* port = client->createPort (deviceName, true, true);
|
||||
|
||||
if (port.isValid())
|
||||
{
|
||||
newDevice = new MidiInput (deviceName);
|
||||
newDevice->internal = new AlsaPortAndCallback (port, newDevice, callback);
|
||||
}
|
||||
jassert (port->isValid());
|
||||
|
||||
newDevice = new MidiInput (deviceName);
|
||||
port->setupInput (newDevice, callback);
|
||||
newDevice->internal = port;
|
||||
|
||||
return newDevice;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,10 +101,6 @@ namespace juce
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if JUCE_LINUX
|
||||
extern Display* display;
|
||||
#endif
|
||||
|
||||
extern JUCE_API bool handleManufacturerSpecificVST2Opcode (int32, pointer_sized_int, void*, float);
|
||||
}
|
||||
|
||||
|
|
@ -332,6 +328,10 @@ public:
|
|||
|
||||
vstEffect.flags |= vstEffectFlagDataInChunks;
|
||||
|
||||
#if JUCE_LINUX
|
||||
display = XWindowSystem::getInstance()->displayRef();
|
||||
#endif
|
||||
|
||||
activePlugins.add (this);
|
||||
}
|
||||
|
||||
|
|
@ -370,6 +370,10 @@ public:
|
|||
messageThreadIsDefinitelyCorrect = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JUCE_LINUX
|
||||
display = XWindowSystem::getInstance()->displayUnref();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1343,7 +1347,11 @@ public:
|
|||
Rectangle<int> childBounds (child->getWidth(), child->getHeight());
|
||||
childBounds *= scale;
|
||||
|
||||
XResizeWindow (display, (Window) getWindowHandle(), childBounds.getWidth(), childBounds.getHeight());
|
||||
{
|
||||
ScopedXDisplay xDisplay;
|
||||
::Display* display = xDisplay.get();
|
||||
XResizeWindow (display, (Window) getWindowHandle(), childBounds.getWidth(), childBounds.getHeight());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JUCE_MAC
|
||||
|
|
@ -1404,6 +1412,7 @@ private:
|
|||
#if JUCE_MAC
|
||||
void* hostWindow;
|
||||
#elif JUCE_LINUX
|
||||
::Display* display;
|
||||
Window hostWindow;
|
||||
#else
|
||||
HWND hostWindow;
|
||||
|
|
|
|||
|
|
@ -224,14 +224,11 @@ static pointer_sized_int VSTINTERFACECALL audioMaster (VstEffectInterface* effec
|
|||
//==============================================================================
|
||||
#if JUCE_LINUX
|
||||
|
||||
extern Display* display;
|
||||
extern XContext windowHandleXContext;
|
||||
|
||||
namespace
|
||||
{
|
||||
static bool xErrorTriggered = false;
|
||||
|
||||
static int temporaryErrorHandler (Display*, XErrorEvent*)
|
||||
static int temporaryErrorHandler (::Display*, XErrorEvent*)
|
||||
{
|
||||
xErrorTriggered = true;
|
||||
return 0;
|
||||
|
|
@ -249,8 +246,13 @@ namespace
|
|||
unsigned char* data;
|
||||
Atom userType;
|
||||
|
||||
XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType,
|
||||
&userType, &userSize, &userCount, &bytes, &data);
|
||||
{
|
||||
ScopedXDisplay xDisplay;
|
||||
::Display* display = xDisplay.get();
|
||||
|
||||
XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType,
|
||||
&userType, &userSize, &userCount, &bytes, &data);
|
||||
}
|
||||
|
||||
XSetErrorHandler (oldErrorHandler);
|
||||
|
||||
|
|
@ -263,7 +265,12 @@ namespace
|
|||
Window* childWindows;
|
||||
unsigned int numChildren = 0;
|
||||
|
||||
XQueryTree (display, windowToCheck, &rootWindow, &parentWindow, &childWindows, &numChildren);
|
||||
{
|
||||
ScopedXDisplay xDisplay;
|
||||
::Display* display = xDisplay.get();
|
||||
|
||||
XQueryTree (display, windowToCheck, &rootWindow, &parentWindow, &childWindows, &numChildren);
|
||||
}
|
||||
|
||||
if (numChildren > 0)
|
||||
return childWindows [0];
|
||||
|
|
@ -2025,6 +2032,7 @@ public:
|
|||
#elif JUCE_LINUX
|
||||
pluginWindow = None;
|
||||
pluginProc = None;
|
||||
display = XWindowSystem::getInstance()->displayRef();
|
||||
|
||||
#elif JUCE_MAC
|
||||
ignoreUnused (recursiveResize, pluginRefusesToResize, alreadyInside);
|
||||
|
|
@ -2053,6 +2061,8 @@ public:
|
|||
carbonWrapper = nullptr;
|
||||
#endif
|
||||
cocoaWrapper = nullptr;
|
||||
#elif JUCE_LINUX
|
||||
display = XWindowSystem::getInstance()->displayUnref();
|
||||
#endif
|
||||
|
||||
activeVSTWindows.removeFirstMatchingValue (this);
|
||||
|
|
@ -2253,6 +2263,7 @@ private:
|
|||
void* originalWndProc;
|
||||
int sizeCheckCount;
|
||||
#elif JUCE_LINUX
|
||||
::Display* display;
|
||||
Window pluginWindow;
|
||||
EventProcPtr pluginProc;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -810,6 +810,7 @@ public:
|
|||
On Windows, this might be "\Documents and Settings\username\Application Data".
|
||||
On the Mac, it might be "~/Library". If you're going to store your settings in here,
|
||||
always create your own sub-folder to put them in, to avoid making a mess.
|
||||
On GNU/Linux it is "~/.config".
|
||||
*/
|
||||
userApplicationDataDirectory,
|
||||
|
||||
|
|
@ -819,6 +820,8 @@ public:
|
|||
On the Mac it'll be "/Library", on Windows, it could be something like
|
||||
"\Documents and Settings\All Users\Application Data".
|
||||
|
||||
On GNU/Linux it is "/opt".
|
||||
|
||||
Depending on the setup, this folder may be read-only.
|
||||
*/
|
||||
commonApplicationDataDirectory,
|
||||
|
|
|
|||
|
|
@ -120,29 +120,22 @@ File File::getSpecialLocation (const SpecialLocationType type)
|
|||
return File();
|
||||
}
|
||||
|
||||
case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~");
|
||||
case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~");
|
||||
case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~");
|
||||
case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~");
|
||||
case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~/Documents");
|
||||
case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~/Music");
|
||||
case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~/Videos");
|
||||
case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~/Pictures");
|
||||
case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop");
|
||||
case userApplicationDataDirectory: return resolveXDGFolder ("XDG_CONFIG_HOME", "~");
|
||||
case userApplicationDataDirectory: return resolveXDGFolder ("XDG_CONFIG_HOME", "~/.config");
|
||||
case commonDocumentsDirectory:
|
||||
case commonApplicationDataDirectory: return File ("/var");
|
||||
case commonApplicationDataDirectory: return File ("/opt");
|
||||
case globalApplicationsDirectory: return File ("/usr");
|
||||
|
||||
case tempDirectory:
|
||||
{
|
||||
File tmp ("/var/tmp");
|
||||
if (const char* tmpDir = getenv ("TMPDIR"))
|
||||
return File (CharPointer_UTF8 (tmpDir));
|
||||
|
||||
if (! tmp.isDirectory())
|
||||
{
|
||||
tmp = "/tmp";
|
||||
|
||||
if (! tmp.isDirectory())
|
||||
tmp = File::getCurrentWorkingDirectory();
|
||||
}
|
||||
|
||||
return tmp;
|
||||
return File ("/tmp");
|
||||
}
|
||||
|
||||
case invokedExecutableFile:
|
||||
|
|
|
|||
|
|
@ -53,10 +53,6 @@
|
|||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
#elif JUCE_LINUX
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xresource.h>
|
||||
#include <X11/Xutil.h>
|
||||
#undef KeyPress
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
|
@ -90,7 +86,6 @@ namespace juce
|
|||
#include "native/juce_win32_Messaging.cpp"
|
||||
|
||||
#elif JUCE_LINUX
|
||||
#include "native/juce_ScopedXLock.h"
|
||||
#include "native/juce_linux_Messaging.cpp"
|
||||
|
||||
#elif JUCE_ANDROID
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@
|
|||
license: ISC
|
||||
|
||||
dependencies: juce_core
|
||||
linuxPackages: x11
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
|
|
@ -80,7 +79,11 @@ namespace juce
|
|||
#include "interprocess/juce_InterprocessConnection.h"
|
||||
#include "interprocess/juce_InterprocessConnectionServer.h"
|
||||
#include "interprocess/juce_ConnectedChildProcess.h"
|
||||
#include "native/juce_ScopedXLock.h"
|
||||
|
||||
#if JUCE_LINUX
|
||||
#include "native/juce_linux_EventLoop.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW && JUCE_WINDOWS
|
||||
#include "native/juce_win32_HiddenMessageWindow.h"
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2016 - ROLI Ltd.
|
||||
|
||||
Permission is granted to use this software 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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
OF THIS SOFTWARE.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses other parts of JUCE not
|
||||
licensed under the ISC terms, commercial licenses are available: visit
|
||||
www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_LINUX || DOXYGEN
|
||||
|
||||
/** A handy class that uses XLockDisplay and XUnlockDisplay to lock the X server
|
||||
using RAII (Only available in Linux!).
|
||||
*/
|
||||
class ScopedXLock
|
||||
{
|
||||
public:
|
||||
/** Creating a ScopedXLock object locks the X display.
|
||||
This uses XLockDisplay() to grab the display that Juce is using.
|
||||
*/
|
||||
ScopedXLock();
|
||||
|
||||
/** Deleting a ScopedXLock object unlocks the X display.
|
||||
This calls XUnlockDisplay() to release the lock.
|
||||
*/
|
||||
~ScopedXLock();
|
||||
};
|
||||
|
||||
#endif
|
||||
33
modules/juce_events/native/juce_linux_EventLoop.h
Normal file
33
modules/juce_events/native/juce_linux_EventLoop.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef JUCE_LINUX_EVENTLOOP_H_INCLUDED
|
||||
#define JUCE_LINUX_EVENTLOOP_H_INCLUDED
|
||||
|
||||
namespace LinuxEventLoop
|
||||
{
|
||||
struct CallbackFunctionBase
|
||||
{
|
||||
virtual ~CallbackFunctionBase() {}
|
||||
virtual bool operator()(int fd) = 0;
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
template <typename FdCallbackFunction>
|
||||
struct CallbackFunction : public CallbackFunctionBase
|
||||
{
|
||||
FdCallbackFunction callback;
|
||||
|
||||
CallbackFunction (FdCallbackFunction c) : callback (c) {}
|
||||
|
||||
bool operator() (int fd) override { return callback (fd); }
|
||||
};
|
||||
|
||||
template <typename FdCallbackFunction>
|
||||
void setWindowSystemFd (int fd, FdCallbackFunction readCallback)
|
||||
{
|
||||
setWindowSystemFdInternal (fd, new CallbackFunction<FdCallbackFunction> (readCallback));
|
||||
}
|
||||
void removeWindowSystemFd() noexcept;
|
||||
|
||||
void setWindowSystemFdInternal (int fd, CallbackFunctionBase* readCallback) noexcept;
|
||||
}
|
||||
|
||||
#endif /* JUCE_LINUX_EVENTLOOP_H_INCLUDED */
|
||||
|
|
@ -28,112 +28,115 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_DEBUG && ! defined (JUCE_DEBUG_XERRORS)
|
||||
#define JUCE_DEBUG_XERRORS 1
|
||||
#endif
|
||||
#include <poll.h>
|
||||
|
||||
Display* display = nullptr;
|
||||
Window juce_messageWindowHandle = None;
|
||||
XContext windowHandleXContext; // This is referenced from Windowing.cpp
|
||||
|
||||
typedef bool (*WindowMessageReceiveCallback) (XEvent&);
|
||||
WindowMessageReceiveCallback dispatchWindowMessage = nullptr;
|
||||
|
||||
typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&);
|
||||
SelectionRequestCallback handleSelectionRequest = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
ScopedXLock::ScopedXLock() { if (display != nullptr) XLockDisplay (display); }
|
||||
ScopedXLock::~ScopedXLock() { if (display != nullptr) XUnlockDisplay (display); }
|
||||
enum FdType {
|
||||
INTERNAL_QUEUE_FD,
|
||||
WINDOW_SYSTEM_FD,
|
||||
FD_COUNT,
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class InternalMessageQueue
|
||||
{
|
||||
public:
|
||||
InternalMessageQueue()
|
||||
: bytesInSocket (0),
|
||||
totalEventCount (0)
|
||||
: fdCount (1),
|
||||
loopCount (0),
|
||||
bytesInSocket (0)
|
||||
{
|
||||
int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd);
|
||||
ignoreUnused (ret); jassert (ret == 0);
|
||||
|
||||
auto internalQueueCb = [this] (int _fd)
|
||||
{
|
||||
if (const MessageManager::MessageBase::Ptr msg = this->popNextMessage (_fd))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
msg->messageCallback();
|
||||
return true;
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
pfds[INTERNAL_QUEUE_FD].fd = getReadHandle();
|
||||
pfds[INTERNAL_QUEUE_FD].events = POLLIN;
|
||||
readCallback[INTERNAL_QUEUE_FD] = new LinuxEventLoop::CallbackFunction<decltype(internalQueueCb)> (internalQueueCb);
|
||||
}
|
||||
|
||||
~InternalMessageQueue()
|
||||
{
|
||||
close (fd[0]);
|
||||
close (fd[1]);
|
||||
close (getReadHandle());
|
||||
close (getWriteHandle());
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void postMessage (MessageManager::MessageBase* const msg)
|
||||
void postMessage (MessageManager::MessageBase* const msg) noexcept
|
||||
{
|
||||
const int maxBytesInSocketQueue = 128;
|
||||
|
||||
ScopedLock sl (lock);
|
||||
queue.add (msg);
|
||||
|
||||
const int maxBytesInSocketQueue = 128;
|
||||
|
||||
if (bytesInSocket < maxBytesInSocketQueue)
|
||||
{
|
||||
++bytesInSocket;
|
||||
bytesInSocket++;
|
||||
|
||||
ScopedUnlock ul (lock);
|
||||
const unsigned char x = 0xff;
|
||||
ssize_t bytesWritten = write (fd[0], &x, 1);
|
||||
ssize_t bytesWritten = write (getWriteHandle(), &x, 1);
|
||||
ignoreUnused (bytesWritten);
|
||||
}
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
void setWindowSystemFd (int _fd, LinuxEventLoop::CallbackFunctionBase* _readCallback)
|
||||
{
|
||||
jassert (fdCount == 1);
|
||||
|
||||
ScopedLock sl (lock);
|
||||
return queue.size() == 0;
|
||||
|
||||
fdCount = 2;
|
||||
pfds[WINDOW_SYSTEM_FD].fd = _fd;
|
||||
pfds[WINDOW_SYSTEM_FD].events = POLLIN;
|
||||
readCallback[WINDOW_SYSTEM_FD] = _readCallback;
|
||||
readCallback[WINDOW_SYSTEM_FD]->active = true;
|
||||
}
|
||||
|
||||
bool dispatchNextEvent()
|
||||
void removeWindowSystemFd ()
|
||||
{
|
||||
// This alternates between giving priority to XEvents or internal messages,
|
||||
// to keep everything running smoothly..
|
||||
if ((++totalEventCount & 1) != 0)
|
||||
return dispatchNextXEvent() || dispatchNextInternalMessage();
|
||||
jassert (fdCount == FD_COUNT);
|
||||
|
||||
return dispatchNextInternalMessage() || dispatchNextXEvent();
|
||||
ScopedLock sl (lock);
|
||||
|
||||
fdCount = 1;
|
||||
readCallback[WINDOW_SYSTEM_FD]->active = false;
|
||||
}
|
||||
|
||||
bool dispatchNextEvent() noexcept
|
||||
{
|
||||
for (int counter = 0; counter < fdCount; counter++)
|
||||
{
|
||||
const int i = loopCount++;
|
||||
loopCount %= fdCount;
|
||||
if (readCallback[i] != nullptr && readCallback[i]->active)
|
||||
{
|
||||
if ((*readCallback[i]) (pfds[i].fd))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for an event (either XEvent, or an internal Message)
|
||||
bool sleepUntilEvent (const int timeoutMs)
|
||||
{
|
||||
if (! isEmpty())
|
||||
return true;
|
||||
|
||||
if (display != nullptr)
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
if (XPending (display))
|
||||
return true;
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeoutMs * 1000;
|
||||
int fd0 = getWaitHandle();
|
||||
int fdmax = fd0;
|
||||
|
||||
fd_set readset;
|
||||
FD_ZERO (&readset);
|
||||
FD_SET (fd0, &readset);
|
||||
|
||||
if (display != nullptr)
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
int fd1 = XConnectionNumber (display);
|
||||
FD_SET (fd1, &readset);
|
||||
fdmax = jmax (fd0, fd1);
|
||||
}
|
||||
|
||||
const int ret = select (fdmax + 1, &readset, 0, 0, &tv);
|
||||
return (ret > 0); // ret <= 0 if error or timeout
|
||||
const int pnum = poll (pfds, static_cast<nfds_t> (fdCount), timeoutMs);
|
||||
return (pnum > 0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -143,46 +146,16 @@ private:
|
|||
CriticalSection lock;
|
||||
ReferenceCountedArray <MessageManager::MessageBase> queue;
|
||||
int fd[2];
|
||||
pollfd pfds[FD_COUNT];
|
||||
ScopedPointer<LinuxEventLoop::CallbackFunctionBase> readCallback[FD_COUNT];
|
||||
int fdCount;
|
||||
int loopCount;
|
||||
int bytesInSocket;
|
||||
int totalEventCount;
|
||||
|
||||
int getWaitHandle() const noexcept { return fd[1]; }
|
||||
int getWriteHandle() const noexcept { return fd[0]; }
|
||||
int getReadHandle() const noexcept { return fd[1]; }
|
||||
|
||||
static bool setNonBlocking (int handle)
|
||||
{
|
||||
int socketFlags = fcntl (handle, F_GETFL, 0);
|
||||
if (socketFlags == -1)
|
||||
return false;
|
||||
|
||||
socketFlags |= O_NONBLOCK;
|
||||
return fcntl (handle, F_SETFL, socketFlags) == 0;
|
||||
}
|
||||
|
||||
static bool dispatchNextXEvent()
|
||||
{
|
||||
if (display == nullptr)
|
||||
return false;
|
||||
|
||||
XEvent evt;
|
||||
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
if (! XPending (display))
|
||||
return false;
|
||||
|
||||
XNextEvent (display, &evt);
|
||||
}
|
||||
|
||||
if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle
|
||||
&& handleSelectionRequest != nullptr)
|
||||
handleSelectionRequest (evt.xselectionrequest);
|
||||
else if (evt.xany.window != juce_messageWindowHandle && dispatchWindowMessage != nullptr)
|
||||
dispatchWindowMessage (evt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MessageManager::MessageBase::Ptr popNextMessage()
|
||||
MessageManager::MessageBase::Ptr popNextMessage (int _fd) noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
|
|
@ -192,27 +165,12 @@ private:
|
|||
|
||||
const ScopedUnlock ul (lock);
|
||||
unsigned char x;
|
||||
ssize_t numBytes = read (fd[1], &x, 1);
|
||||
ssize_t numBytes = read (_fd, &x, 1);
|
||||
ignoreUnused (numBytes);
|
||||
}
|
||||
|
||||
return queue.removeAndReturn (0);
|
||||
}
|
||||
|
||||
bool dispatchNextInternalMessage()
|
||||
{
|
||||
if (const MessageManager::MessageBase::Ptr msg = popNextMessage())
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
msg->messageCallback();
|
||||
return true;
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
juce_ImplementSingleton_SingleThreaded (InternalMessageQueue)
|
||||
|
|
@ -221,57 +179,7 @@ juce_ImplementSingleton_SingleThreaded (InternalMessageQueue)
|
|||
//==============================================================================
|
||||
namespace LinuxErrorHandling
|
||||
{
|
||||
static bool errorOccurred = false;
|
||||
static bool keyboardBreakOccurred = false;
|
||||
static XErrorHandler oldErrorHandler = (XErrorHandler) 0;
|
||||
static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0;
|
||||
|
||||
//==============================================================================
|
||||
// Usually happens when client-server connection is broken
|
||||
int ioErrorHandler (Display*)
|
||||
{
|
||||
DBG ("ERROR: connection to X server broken.. terminating.");
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
MessageManager::getInstance()->stopDispatchLoop();
|
||||
|
||||
errorOccurred = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int errorHandler (Display* display, XErrorEvent* event)
|
||||
{
|
||||
ignoreUnused (display, event);
|
||||
|
||||
#if JUCE_DEBUG_XERRORS
|
||||
char errorStr[64] = { 0 };
|
||||
char requestStr[64] = { 0 };
|
||||
|
||||
XGetErrorText (display, event->error_code, errorStr, 64);
|
||||
XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64);
|
||||
DBG ("ERROR: X returned " << errorStr << " for operation " << requestStr);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void installXErrorHandlers()
|
||||
{
|
||||
oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler);
|
||||
oldErrorHandler = XSetErrorHandler (errorHandler);
|
||||
}
|
||||
|
||||
void removeXErrorHandlers()
|
||||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
XSetIOErrorHandler (oldIOErrorHandler);
|
||||
oldIOErrorHandler = 0;
|
||||
|
||||
XSetErrorHandler (oldErrorHandler);
|
||||
oldErrorHandler = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void keyboardBreakSignalHandler (int sig)
|
||||
|
|
@ -297,78 +205,25 @@ void MessageManager::doPlatformSpecificInitialisation()
|
|||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
// Initialise xlib for multiple thread support
|
||||
static bool initThreadCalled = false;
|
||||
|
||||
if (! initThreadCalled)
|
||||
{
|
||||
if (! XInitThreads())
|
||||
{
|
||||
// This is fatal! Print error and closedown
|
||||
Logger::outputDebugString ("Failed to initialise xlib thread support.");
|
||||
Process::terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
initThreadCalled = true;
|
||||
}
|
||||
|
||||
LinuxErrorHandling::installXErrorHandlers();
|
||||
LinuxErrorHandling::installKeyboardBreakHandler();
|
||||
}
|
||||
|
||||
// Create the internal message queue
|
||||
InternalMessageQueue::getInstance();
|
||||
|
||||
// Try to connect to a display
|
||||
String displayName (getenv ("DISPLAY"));
|
||||
if (displayName.isEmpty())
|
||||
displayName = ":0.0";
|
||||
|
||||
display = XOpenDisplay (displayName.toUTF8());
|
||||
|
||||
if (display != nullptr) // This is not fatal! we can run headless.
|
||||
{
|
||||
// Create a context to store user data associated with Windows we create
|
||||
windowHandleXContext = XUniqueContext();
|
||||
|
||||
// We're only interested in client messages for this window, which are always sent
|
||||
XSetWindowAttributes swa;
|
||||
swa.event_mask = NoEventMask;
|
||||
|
||||
// Create our message window (this will never be mapped)
|
||||
const int screen = DefaultScreen (display);
|
||||
juce_messageWindowHandle = XCreateWindow (display, RootWindow (display, screen),
|
||||
0, 0, 1, 1, 0, 0, InputOnly,
|
||||
DefaultVisual (display, screen),
|
||||
CWEventMask, &swa);
|
||||
}
|
||||
InternalMessageQueue* queue = InternalMessageQueue::getInstance();
|
||||
ignoreUnused (queue);
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
InternalMessageQueue::deleteInstance();
|
||||
|
||||
if (display != nullptr && ! LinuxErrorHandling::errorOccurred)
|
||||
{
|
||||
XDestroyWindow (display, juce_messageWindowHandle);
|
||||
|
||||
juce_messageWindowHandle = 0;
|
||||
display = nullptr;
|
||||
|
||||
LinuxErrorHandling::removeXErrorHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
if (! LinuxErrorHandling::errorOccurred)
|
||||
if (InternalMessageQueue* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
{
|
||||
if (InternalMessageQueue* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
{
|
||||
queue->postMessage (message);
|
||||
return true;
|
||||
}
|
||||
queue->postMessage (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -382,29 +237,38 @@ void MessageManager::broadcastMessage (const String& /* value */)
|
|||
// this function expects that it will NEVER be called simultaneously for two concurrent threads
|
||||
bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
{
|
||||
while (! LinuxErrorHandling::errorOccurred)
|
||||
for (;;)
|
||||
{
|
||||
if (LinuxErrorHandling::keyboardBreakOccurred)
|
||||
{
|
||||
LinuxErrorHandling::errorOccurred = true;
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
Process::terminate();
|
||||
|
||||
break;
|
||||
}
|
||||
JUCEApplicationBase::getInstance()->quit();
|
||||
|
||||
if (InternalMessageQueue* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
{
|
||||
if (queue->dispatchNextEvent())
|
||||
return true;
|
||||
|
||||
if (returnIfNoPendingMessages)
|
||||
break;
|
||||
|
||||
else if (returnIfNoPendingMessages)
|
||||
return false;
|
||||
|
||||
// wait for 2000ms for next events if necessary
|
||||
queue->sleepUntilEvent (2000);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
|
||||
void LinuxEventLoop::setWindowSystemFdInternal (int fd, LinuxEventLoop::CallbackFunctionBase* readCallback) noexcept
|
||||
{
|
||||
if (InternalMessageQueue* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
queue->setWindowSystemFd (fd, readCallback);
|
||||
}
|
||||
|
||||
void LinuxEventLoop::removeWindowSystemFd() noexcept
|
||||
{
|
||||
if (InternalMessageQueue* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
queue->removeWindowSystemFd();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ void MessageManager::broadcastMessage (const String& value)
|
|||
data.lpData = (void*) localCopy.toUTF32().getAddress();
|
||||
|
||||
DWORD_PTR result;
|
||||
SendMessageTimeout (windows.getUnchecked(i), WM_COPYDATA,
|
||||
SendMessageTimeout (windows.getUnchecked (i), WM_COPYDATA,
|
||||
(WPARAM) juce_messageWindowHandle,
|
||||
(LPARAM) &data,
|
||||
SMTO_BLOCK | SMTO_ABORTIFHUNG, 8000, &result);
|
||||
|
|
|
|||
|
|
@ -282,8 +282,9 @@ extern bool juce_areThereAnyAlwaysOnTopWindows();
|
|||
#include "native/juce_win32_FileChooser.cpp"
|
||||
|
||||
#elif JUCE_LINUX
|
||||
#include "native/juce_linux_Clipboard.cpp"
|
||||
#include "native/juce_linux_Windowing.cpp"
|
||||
#include "native/juce_linux_X11.cpp"
|
||||
#include "native/juce_linux_X11_Clipboard.cpp"
|
||||
#include "native/juce_linux_X11_Windowing.cpp"
|
||||
#include "native/juce_linux_FileChooser.cpp"
|
||||
|
||||
#elif JUCE_ANDROID
|
||||
|
|
|
|||
|
|
@ -281,6 +281,10 @@ class FlexBox;
|
|||
#include "lookandfeel/juce_LookAndFeel_V1.h"
|
||||
#include "lookandfeel/juce_LookAndFeel_V3.h"
|
||||
|
||||
#if JUCE_LINUX
|
||||
#include "native/juce_linux_X11.h"
|
||||
#endif
|
||||
|
||||
// these classes are C++11-only
|
||||
#if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS && JUCE_COMPILER_SUPPORTS_LAMBDAS
|
||||
#include "layout/juce_FlexItem.h"
|
||||
|
|
|
|||
312
modules/juce_gui_basics/native/juce_linux_X11.cpp
Normal file
312
modules/juce_gui_basics/native/juce_linux_X11.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
typedef void (*WindowMessageReceiveCallback) (XEvent&);
|
||||
WindowMessageReceiveCallback dispatchWindowMessage = nullptr;
|
||||
|
||||
typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&);
|
||||
SelectionRequestCallback handleSelectionRequest = nullptr;
|
||||
|
||||
::Window juce_messageWindowHandle;
|
||||
|
||||
XContext windowHandleXContext;
|
||||
|
||||
//==============================================================================
|
||||
namespace X11ErrorHandling
|
||||
{
|
||||
static XErrorHandler oldErrorHandler = (XErrorHandler) 0;
|
||||
static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0;
|
||||
|
||||
//==============================================================================
|
||||
// Usually happens when client-server connection is broken
|
||||
int ioErrorHandler (::Display*)
|
||||
{
|
||||
DBG ("ERROR: connection to X server broken.. terminating.");
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
MessageManager::getInstance()->stopDispatchLoop();
|
||||
|
||||
// set this somewhere
|
||||
// errorOccurred = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int errorHandler (::Display* display, XErrorEvent* event)
|
||||
{
|
||||
ignoreUnused (display, event);
|
||||
|
||||
#if JUCE_DEBUG_XERRORS
|
||||
char errorStr[64] = { 0 };
|
||||
char requestStr[64] = { 0 };
|
||||
|
||||
XGetErrorText (display, event->error_code, errorStr, 64);
|
||||
XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64);
|
||||
DBG ("ERROR: X returned " << errorStr << " for operation " << requestStr);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void installXErrorHandlers()
|
||||
{
|
||||
oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler);
|
||||
oldErrorHandler = XSetErrorHandler (errorHandler);
|
||||
}
|
||||
|
||||
void removeXErrorHandlers()
|
||||
{
|
||||
XSetIOErrorHandler (oldIOErrorHandler);
|
||||
oldIOErrorHandler = 0;
|
||||
|
||||
XSetErrorHandler (oldErrorHandler);
|
||||
oldErrorHandler = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
XWindowSystem::XWindowSystem() noexcept
|
||||
: display (nullptr)
|
||||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
// Initialise xlib for multiple thread support
|
||||
static bool initThreadCalled = false;
|
||||
|
||||
if (! initThreadCalled)
|
||||
{
|
||||
if (! XInitThreads())
|
||||
{
|
||||
// This is fatal! Print error and closedown
|
||||
Logger::outputDebugString ("Failed to initialise xlib thread support.");
|
||||
Process::terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
initThreadCalled = true;
|
||||
}
|
||||
|
||||
X11ErrorHandling::installXErrorHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
XWindowSystem::~XWindowSystem() noexcept
|
||||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
X11ErrorHandling::removeXErrorHandlers();
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
|
||||
::Display* XWindowSystem::displayRef() noexcept
|
||||
{
|
||||
if (++displayCount - 1 == 0)
|
||||
{
|
||||
String displayName (getenv ("DISPLAY"));
|
||||
if (displayName.isEmpty())
|
||||
displayName = ":0.0";
|
||||
|
||||
display = XOpenDisplay (displayName.toUTF8());
|
||||
|
||||
initialiseXDisplay();
|
||||
}
|
||||
|
||||
return this->display;
|
||||
}
|
||||
|
||||
::Display* XWindowSystem::displayUnref() noexcept
|
||||
{
|
||||
jassert (display != nullptr);
|
||||
jassert (displayCount.get() > 0);
|
||||
|
||||
if (--displayCount == 0)
|
||||
{
|
||||
destroyXDisplay();
|
||||
XCloseDisplay (display);
|
||||
display = nullptr;
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
void XWindowSystem::initialiseXDisplay() noexcept
|
||||
{
|
||||
// This is fatal! Print error and closedown
|
||||
if (display == nullptr)
|
||||
{
|
||||
Logger::outputDebugString ("Failed to connect to the X Server.");
|
||||
Process::terminate();
|
||||
}
|
||||
|
||||
// Create a context to store user data associated with Windows we create
|
||||
windowHandleXContext = XUniqueContext();
|
||||
|
||||
// We're only interested in client messages for this window, which are always sent
|
||||
XSetWindowAttributes swa;
|
||||
swa.event_mask = NoEventMask;
|
||||
|
||||
// Create our message window (this will never be mapped)
|
||||
const int screen = DefaultScreen (display);
|
||||
juce_messageWindowHandle = XCreateWindow (display, RootWindow (display, screen),
|
||||
0, 0, 1, 1, 0, 0, InputOnly,
|
||||
DefaultVisual (display, screen),
|
||||
CWEventMask, &swa);
|
||||
|
||||
XSync (display, False);
|
||||
|
||||
// Setup input event handler
|
||||
int fd = XConnectionNumber (display);
|
||||
LinuxEventLoop::setWindowSystemFd
|
||||
(fd,
|
||||
[this](int /*fd*/) {
|
||||
do
|
||||
{
|
||||
XEvent evt;
|
||||
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
|
||||
if (! XPending (display))
|
||||
return false;
|
||||
|
||||
XNextEvent (display, &evt);
|
||||
}
|
||||
|
||||
if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle
|
||||
&& handleSelectionRequest != nullptr)
|
||||
handleSelectionRequest (evt.xselectionrequest);
|
||||
else if (evt.xany.window != juce_messageWindowHandle
|
||||
&& dispatchWindowMessage != nullptr)
|
||||
dispatchWindowMessage (evt);
|
||||
|
||||
} while (display != nullptr);
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void XWindowSystem::destroyXDisplay() noexcept
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
|
||||
XDestroyWindow (display, juce_messageWindowHandle);
|
||||
|
||||
juce_messageWindowHandle = 0;
|
||||
|
||||
XSync (display, True);
|
||||
|
||||
LinuxEventLoop::removeWindowSystemFd();
|
||||
}
|
||||
|
||||
juce_ImplementSingleton (XWindowSystem)
|
||||
|
||||
//==============================================================================
|
||||
ScopedXDisplay::ScopedXDisplay()
|
||||
{
|
||||
display = XWindowSystem::getInstance()->displayRef();
|
||||
}
|
||||
ScopedXDisplay::~ScopedXDisplay()
|
||||
{
|
||||
XWindowSystem::getInstance()->displayUnref();
|
||||
}
|
||||
|
||||
::Display* ScopedXDisplay::get()
|
||||
{
|
||||
return display;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ScopedXLock::ScopedXLock(::Display* _display)
|
||||
: display (_display)
|
||||
{
|
||||
if (display != nullptr) XLockDisplay (display);
|
||||
}
|
||||
|
||||
ScopedXLock::~ScopedXLock()
|
||||
{
|
||||
if (display != nullptr) XUnlockDisplay (display);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Atoms::Atoms(::Display* display)
|
||||
{
|
||||
protocols = getIfExists (display, "WM_PROTOCOLS");
|
||||
protocolList [TAKE_FOCUS] = getIfExists (display, "WM_TAKE_FOCUS");
|
||||
protocolList [DELETE_WINDOW] = getIfExists (display, "WM_DELETE_WINDOW");
|
||||
protocolList [PING] = getIfExists (display, "_NET_WM_PING");
|
||||
changeState = getIfExists (display, "WM_CHANGE_STATE");
|
||||
state = getIfExists (display, "WM_STATE");
|
||||
userTime = getCreating (display, "_NET_WM_USER_TIME");
|
||||
activeWin = getCreating (display, "_NET_ACTIVE_WINDOW");
|
||||
pid = getCreating (display, "_NET_WM_PID");
|
||||
windowType = getIfExists (display, "_NET_WM_WINDOW_TYPE");
|
||||
windowState = getIfExists (display, "_NET_WM_STATE");
|
||||
|
||||
XdndAware = getCreating (display, "XdndAware");
|
||||
XdndEnter = getCreating (display, "XdndEnter");
|
||||
XdndLeave = getCreating (display, "XdndLeave");
|
||||
XdndPosition = getCreating (display, "XdndPosition");
|
||||
XdndStatus = getCreating (display, "XdndStatus");
|
||||
XdndDrop = getCreating (display, "XdndDrop");
|
||||
XdndFinished = getCreating (display, "XdndFinished");
|
||||
XdndSelection = getCreating (display, "XdndSelection");
|
||||
|
||||
XdndTypeList = getCreating (display, "XdndTypeList");
|
||||
XdndActionList = getCreating (display, "XdndActionList");
|
||||
XdndActionCopy = getCreating (display, "XdndActionCopy");
|
||||
XdndActionPrivate = getCreating (display, "XdndActionPrivate");
|
||||
XdndActionDescription = getCreating (display, "XdndActionDescription");
|
||||
|
||||
allowedMimeTypes[0] = getCreating (display, "UTF8_STRING");
|
||||
allowedMimeTypes[1] = getCreating (display, "text/plain;charset=utf-8");
|
||||
allowedMimeTypes[2] = getCreating (display, "text/plain");
|
||||
allowedMimeTypes[3] = getCreating (display, "text/uri-list");
|
||||
|
||||
allowedActions[0] = getCreating (display, "XdndActionMove");
|
||||
allowedActions[1] = XdndActionCopy;
|
||||
allowedActions[2] = getCreating (display, "XdndActionLink");
|
||||
allowedActions[3] = getCreating (display, "XdndActionAsk");
|
||||
allowedActions[4] = XdndActionPrivate;
|
||||
}
|
||||
|
||||
Atom Atoms::getIfExists (::Display* display, const char* name)
|
||||
{
|
||||
return XInternAtom (display, name, True);
|
||||
}
|
||||
|
||||
Atom Atoms::getCreating (::Display* display, const char* name)
|
||||
{
|
||||
return XInternAtom (display, name, False);
|
||||
}
|
||||
|
||||
String Atoms::getName (::Display* display, const Atom atom)
|
||||
{
|
||||
if (atom == None)
|
||||
return "None";
|
||||
|
||||
return String (XGetAtomName (display, atom));
|
||||
}
|
||||
|
||||
bool Atoms::isMimeTypeFile (::Display* display, const Atom atom)
|
||||
{
|
||||
return getName (display, atom).equalsIgnoreCase ("text/uri-list");
|
||||
}
|
||||
|
||||
|
||||
const unsigned long Atoms::DndVersion = 3;
|
||||
|
||||
//==============================================================================
|
||||
GetXProperty::GetXProperty (::Display* display, Window window, Atom atom,
|
||||
long offset, long length, bool shouldDelete,
|
||||
Atom requestedType)
|
||||
: data (nullptr)
|
||||
{
|
||||
success = (XGetWindowProperty (display, window, atom, offset, length,
|
||||
(Bool) shouldDelete, requestedType, &actualType,
|
||||
&actualFormat, &numItems, &bytesLeft, &data) == Success)
|
||||
&& data != nullptr;
|
||||
}
|
||||
|
||||
GetXProperty::~GetXProperty()
|
||||
{
|
||||
if (data != nullptr)
|
||||
XFree (data);
|
||||
}
|
||||
114
modules/juce_gui_basics/native/juce_linux_X11.h
Normal file
114
modules/juce_gui_basics/native/juce_linux_X11.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#ifndef JUCE_XWINDOWSYSTEM_H_INCLUDED
|
||||
#define JUCE_XWINDOWSYSTEM_H_INCLUDED
|
||||
|
||||
// Hack to forward declare _XDisplay outside the
|
||||
// juce namespace
|
||||
|
||||
}
|
||||
|
||||
struct _XDisplay;
|
||||
|
||||
#define ATOM_TYPE unsigned long
|
||||
#define WINDOW_TYPE unsigned long
|
||||
|
||||
namespace juce {
|
||||
|
||||
//==============================================================================
|
||||
class XWindowSystem
|
||||
{
|
||||
public:
|
||||
::_XDisplay* displayRef() noexcept;
|
||||
::_XDisplay* displayUnref() noexcept;
|
||||
juce_DeclareSingleton (XWindowSystem, false)
|
||||
|
||||
private:
|
||||
::_XDisplay* display;
|
||||
Atomic<int> displayCount;
|
||||
|
||||
XWindowSystem() noexcept;
|
||||
~XWindowSystem() noexcept;
|
||||
|
||||
void initialiseXDisplay() noexcept;
|
||||
void destroyXDisplay() noexcept;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ScopedXDisplay
|
||||
{
|
||||
public:
|
||||
ScopedXDisplay();
|
||||
~ScopedXDisplay();
|
||||
::_XDisplay* get();
|
||||
private:
|
||||
::_XDisplay* display;
|
||||
};
|
||||
|
||||
/** A handy class that uses XLockDisplay and XUnlockDisplay to lock the X server
|
||||
using RAII (Only available in Linux!).
|
||||
*/
|
||||
class ScopedXLock
|
||||
{
|
||||
public:
|
||||
/** Creating a ScopedXLock object locks the X display.
|
||||
This uses XLockDisplay() to grab the display that Juce is using.
|
||||
*/
|
||||
ScopedXLock (::_XDisplay* _display);
|
||||
|
||||
/** Deleting a ScopedXLock object unlocks the X display.
|
||||
This calls XUnlockDisplay() to release the lock.
|
||||
*/
|
||||
~ScopedXLock();
|
||||
private:
|
||||
// defined in juce_linux_X11.h
|
||||
::_XDisplay* display;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct Atoms
|
||||
{
|
||||
Atoms(::_XDisplay* display);
|
||||
|
||||
enum ProtocolItems
|
||||
{
|
||||
TAKE_FOCUS = 0,
|
||||
DELETE_WINDOW = 1,
|
||||
PING = 2
|
||||
};
|
||||
|
||||
ATOM_TYPE protocols, protocolList[3], changeState, state, userTime,
|
||||
activeWin, pid, windowType, windowState,
|
||||
XdndAware, XdndEnter, XdndLeave, XdndPosition, XdndStatus,
|
||||
XdndDrop, XdndFinished, XdndSelection, XdndTypeList, XdndActionList,
|
||||
XdndActionDescription, XdndActionCopy, XdndActionPrivate,
|
||||
allowedActions[5],
|
||||
allowedMimeTypes[4];
|
||||
|
||||
static const unsigned long DndVersion;
|
||||
|
||||
static ATOM_TYPE getIfExists (::_XDisplay* display, const char* name);
|
||||
static ATOM_TYPE getCreating (::_XDisplay* display, const char* name);
|
||||
|
||||
static String getName (::_XDisplay* display, const ATOM_TYPE atom);
|
||||
|
||||
static bool isMimeTypeFile (::_XDisplay* display, const ATOM_TYPE atom);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct GetXProperty
|
||||
{
|
||||
GetXProperty (::_XDisplay* display, WINDOW_TYPE window, ATOM_TYPE atom,
|
||||
long offset, long length, bool shouldDelete,
|
||||
ATOM_TYPE requestedType);
|
||||
|
||||
~GetXProperty();
|
||||
|
||||
bool success;
|
||||
unsigned char* data;
|
||||
unsigned long numItems, bytesLeft;
|
||||
ATOM_TYPE actualType;
|
||||
int actualFormat;
|
||||
};
|
||||
|
||||
#undef ATOM_TYPE
|
||||
|
||||
#endif // JUCE_XWINDOWSYSTEM_H_INCLUDED
|
||||
|
|
@ -22,7 +22,6 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
extern ::Display* display;
|
||||
extern ::Window juce_messageWindowHandle;
|
||||
|
||||
namespace ClipboardHelpers
|
||||
|
|
@ -33,7 +32,7 @@ namespace ClipboardHelpers
|
|||
static Atom atom_TARGETS;
|
||||
|
||||
//==============================================================================
|
||||
static void initSelectionAtoms()
|
||||
static void initSelectionAtoms (::Display* display)
|
||||
{
|
||||
static bool isInitialised = false;
|
||||
|
||||
|
|
@ -41,16 +40,16 @@ namespace ClipboardHelpers
|
|||
{
|
||||
isInitialised = true;
|
||||
|
||||
atom_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False);
|
||||
atom_CLIPBOARD = XInternAtom (display, "CLIPBOARD", False);
|
||||
atom_TARGETS = XInternAtom (display, "TARGETS", False);
|
||||
atom_UTF8_STRING = Atoms::getCreating (display, "UTF8_STRING");
|
||||
atom_CLIPBOARD = Atoms::getCreating (display, "CLIPBOARD");
|
||||
atom_TARGETS = Atoms::getCreating (display, "TARGETS");
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Read the content of a window property as either a locale-dependent string or an utf8 string
|
||||
// works only for strings shorter than 1000000 bytes
|
||||
static String readWindowProperty (Window window, Atom prop)
|
||||
static String readWindowProperty (::Display* display, Window window, Atom prop)
|
||||
{
|
||||
String returnData;
|
||||
|
||||
|
|
@ -86,7 +85,8 @@ namespace ClipboardHelpers
|
|||
|
||||
//==============================================================================
|
||||
// Send a SelectionRequest to the window owning the selection and waits for its answer (with a timeout) */
|
||||
static bool requestSelectionContent (String& selectionContent, Atom selection, Atom requestedFormat)
|
||||
static bool requestSelectionContent (::Display* display, String& selectionContent,
|
||||
Atom selection, Atom requestedFormat)
|
||||
{
|
||||
Atom property_name = XInternAtom (display, "JUCE_SEL", false);
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ namespace ClipboardHelpers
|
|||
{
|
||||
jassert (event.xselection.requestor == juce_messageWindowHandle);
|
||||
|
||||
selectionContent = readWindowProperty (event.xselection.requestor,
|
||||
selectionContent = readWindowProperty (display, event.xselection.requestor,
|
||||
event.xselection.property);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -128,69 +128,66 @@ namespace ClipboardHelpers
|
|||
// Called from the event loop in juce_linux_Messaging in response to SelectionRequest events
|
||||
static void handleSelection (XSelectionRequestEvent& evt)
|
||||
{
|
||||
if (display != nullptr)
|
||||
ClipboardHelpers::initSelectionAtoms (evt.display);
|
||||
|
||||
// the selection content is sent to the target window as a window property
|
||||
XSelectionEvent reply;
|
||||
reply.type = SelectionNotify;
|
||||
reply.display = evt.display;
|
||||
reply.requestor = evt.requestor;
|
||||
reply.selection = evt.selection;
|
||||
reply.target = evt.target;
|
||||
reply.property = None; // == "fail"
|
||||
reply.time = evt.time;
|
||||
|
||||
HeapBlock<char> data;
|
||||
int propertyFormat = 0;
|
||||
size_t numDataItems = 0;
|
||||
|
||||
if (evt.selection == XA_PRIMARY || evt.selection == ClipboardHelpers::atom_CLIPBOARD)
|
||||
{
|
||||
ClipboardHelpers::initSelectionAtoms();
|
||||
|
||||
// the selection content is sent to the target window as a window property
|
||||
XSelectionEvent reply;
|
||||
reply.type = SelectionNotify;
|
||||
reply.display = evt.display;
|
||||
reply.requestor = evt.requestor;
|
||||
reply.selection = evt.selection;
|
||||
reply.target = evt.target;
|
||||
reply.property = None; // == "fail"
|
||||
reply.time = evt.time;
|
||||
|
||||
HeapBlock<char> data;
|
||||
int propertyFormat = 0;
|
||||
size_t numDataItems = 0;
|
||||
|
||||
if (evt.selection == XA_PRIMARY || evt.selection == ClipboardHelpers::atom_CLIPBOARD)
|
||||
if (evt.target == XA_STRING || evt.target == ClipboardHelpers::atom_UTF8_STRING)
|
||||
{
|
||||
if (evt.target == XA_STRING || evt.target == ClipboardHelpers::atom_UTF8_STRING)
|
||||
{
|
||||
// translate to utf8
|
||||
numDataItems = ClipboardHelpers::localClipboardContent.getNumBytesAsUTF8() + 1;
|
||||
data.calloc (numDataItems + 1);
|
||||
ClipboardHelpers::localClipboardContent.copyToUTF8 (data, numDataItems);
|
||||
propertyFormat = 8; // bits/item
|
||||
}
|
||||
else if (evt.target == ClipboardHelpers::atom_TARGETS)
|
||||
{
|
||||
// another application wants to know what we are able to send
|
||||
numDataItems = 2;
|
||||
propertyFormat = 32; // atoms are 32-bit
|
||||
data.calloc (numDataItems * 4);
|
||||
Atom* atoms = reinterpret_cast<Atom*> (data.getData());
|
||||
atoms[0] = ClipboardHelpers::atom_UTF8_STRING;
|
||||
atoms[1] = XA_STRING;
|
||||
|
||||
evt.target = XA_ATOM;
|
||||
}
|
||||
// translate to utf8
|
||||
numDataItems = ClipboardHelpers::localClipboardContent.getNumBytesAsUTF8() + 1;
|
||||
data.calloc (numDataItems + 1);
|
||||
ClipboardHelpers::localClipboardContent.copyToUTF8 (data, numDataItems);
|
||||
propertyFormat = 8; // bits/item
|
||||
}
|
||||
else
|
||||
else if (evt.target == ClipboardHelpers::atom_TARGETS)
|
||||
{
|
||||
DBG ("requested unsupported clipboard");
|
||||
// another application wants to know what we are able to send
|
||||
numDataItems = 2;
|
||||
propertyFormat = 32; // atoms are 32-bit
|
||||
data.calloc (numDataItems * 4);
|
||||
Atom* atoms = reinterpret_cast<Atom*> (data.getData());
|
||||
atoms[0] = ClipboardHelpers::atom_UTF8_STRING;
|
||||
atoms[1] = XA_STRING;
|
||||
|
||||
evt.target = XA_ATOM;
|
||||
}
|
||||
|
||||
if (data != nullptr)
|
||||
{
|
||||
const size_t maxReasonableSelectionSize = 1000000;
|
||||
|
||||
// for very big chunks of data, we should use the "INCR" protocol , which is a pain in the *ss
|
||||
if (evt.property != None && numDataItems < maxReasonableSelectionSize)
|
||||
{
|
||||
XChangeProperty (evt.display, evt.requestor,
|
||||
evt.property, evt.target,
|
||||
propertyFormat /* 8 or 32 */, PropModeReplace,
|
||||
reinterpret_cast<const unsigned char*> (data.getData()), (int) numDataItems);
|
||||
reply.property = evt.property; // " == success"
|
||||
}
|
||||
}
|
||||
|
||||
XSendEvent (evt.display, evt.requestor, 0, NoEventMask, (XEvent*) &reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG ("requested unsupported clipboard");
|
||||
}
|
||||
|
||||
if (data != nullptr)
|
||||
{
|
||||
const size_t maxReasonableSelectionSize = 1000000;
|
||||
|
||||
// for very big chunks of data, we should use the "INCR" protocol , which is a pain in the *ss
|
||||
if (evt.property != None && numDataItems < maxReasonableSelectionSize)
|
||||
{
|
||||
XChangeProperty (evt.display, evt.requestor,
|
||||
evt.property, evt.target,
|
||||
propertyFormat /* 8 or 32 */, PropModeReplace,
|
||||
reinterpret_cast<const unsigned char*> (data.getData()), (int) numDataItems);
|
||||
reply.property = evt.property; // " == success"
|
||||
}
|
||||
}
|
||||
|
||||
XSendEvent (evt.display, evt.requestor, 0, NoEventMask, (XEvent*) &reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -211,9 +208,12 @@ static ClipboardCallbackInitialiser clipboardInitialiser;
|
|||
//==============================================================================
|
||||
void SystemClipboard::copyTextToClipboard (const String& clipText)
|
||||
{
|
||||
ScopedXDisplay xDisplay;
|
||||
::Display* display = xDisplay.get();
|
||||
|
||||
if (display != nullptr)
|
||||
{
|
||||
ClipboardHelpers::initSelectionAtoms();
|
||||
ClipboardHelpers::initSelectionAtoms (display);
|
||||
ClipboardHelpers::localClipboardContent = clipText;
|
||||
|
||||
XSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime);
|
||||
|
|
@ -224,10 +224,12 @@ void SystemClipboard::copyTextToClipboard (const String& clipText)
|
|||
String SystemClipboard::getTextFromClipboard()
|
||||
{
|
||||
String content;
|
||||
ScopedXDisplay xDisplay;
|
||||
::Display* display = xDisplay.get();
|
||||
|
||||
if (display != nullptr)
|
||||
{
|
||||
ClipboardHelpers::initSelectionAtoms();
|
||||
ClipboardHelpers::initSelectionAtoms (display);
|
||||
|
||||
/* 1) try to read from the "CLIPBOARD" selection first (the "high
|
||||
level" clipboard that is supposed to be filled by ctrl-C
|
||||
|
|
@ -256,12 +258,14 @@ String SystemClipboard::getTextFromClipboard()
|
|||
else
|
||||
{
|
||||
// first try: we want an utf8 string
|
||||
bool ok = ClipboardHelpers::requestSelectionContent (content, selection, ClipboardHelpers::atom_UTF8_STRING);
|
||||
bool ok = ClipboardHelpers::requestSelectionContent (display, content,
|
||||
selection, ClipboardHelpers::atom_UTF8_STRING);
|
||||
|
||||
if (! ok)
|
||||
{
|
||||
// second chance, ask for a good old locale-dependent string ..
|
||||
ok = ClipboardHelpers::requestSelectionContent (content, selection, XA_STRING);
|
||||
ok = ClipboardHelpers::requestSelectionContent (display, content,
|
||||
selection, XA_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -124,9 +124,9 @@ namespace juce
|
|||
//==============================================================================
|
||||
#elif JUCE_LINUX
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_linux_WebBrowserComponent.cpp"
|
||||
#include "native/juce_linux_X11_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
#include "native/juce_linux_SystemTrayIcon.cpp"
|
||||
#include "native/juce_linux_X11_SystemTrayIcon.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
|
|
|
|||
|
|
@ -22,22 +22,23 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
extern ::Display* display;
|
||||
|
||||
//==============================================================================
|
||||
class SystemTrayIconComponent::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (const Image& im, Window windowH) : image (im)
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
ScopedXDisplay xDisplay;
|
||||
::Display* display = xDisplay.get();
|
||||
|
||||
ScopedXLock xlock (display);
|
||||
|
||||
Screen* const screen = XDefaultScreenOfDisplay (display);
|
||||
const int screenNumber = XScreenNumberOfScreen (screen);
|
||||
|
||||
String screenAtom ("_NET_SYSTEM_TRAY_S");
|
||||
screenAtom << screenNumber;
|
||||
Atom selectionAtom = XInternAtom (display, screenAtom.toUTF8(), false);
|
||||
Atom selectionAtom = Atoms::getCreating (display, screenAtom.toUTF8());
|
||||
|
||||
XGrabServer (display);
|
||||
Window managerWin = XGetSelectionOwner (display, selectionAtom);
|
||||
|
|
@ -53,7 +54,7 @@ public:
|
|||
XEvent ev = { 0 };
|
||||
ev.xclient.type = ClientMessage;
|
||||
ev.xclient.window = managerWin;
|
||||
ev.xclient.message_type = XInternAtom (display, "_NET_SYSTEM_TRAY_OPCODE", False);
|
||||
ev.xclient.message_type = Atoms::getCreating (display, "_NET_SYSTEM_TRAY_OPCODE");
|
||||
ev.xclient.format = 32;
|
||||
ev.xclient.data.l[0] = CurrentTime;
|
||||
ev.xclient.data.l[1] = 0 /*SYSTEM_TRAY_REQUEST_DOCK*/;
|
||||
|
|
@ -67,11 +68,11 @@ public:
|
|||
|
||||
// For older KDE's ...
|
||||
long atomData = 1;
|
||||
Atom trayAtom = XInternAtom (display, "KWM_DOCKWINDOW", false);
|
||||
Atom trayAtom = Atoms::getCreating (display, "KWM_DOCKWINDOW");
|
||||
XChangeProperty (display, windowH, trayAtom, trayAtom, 32, PropModeReplace, (unsigned char*) &atomData, 1);
|
||||
|
||||
// For more recent KDE's...
|
||||
trayAtom = XInternAtom (display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", false);
|
||||
trayAtom = Atoms::getCreating (display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR");
|
||||
XChangeProperty (display, windowH, trayAtom, XA_WINDOW, 32, PropModeReplace, (unsigned char*) &windowH, 1);
|
||||
|
||||
// A minimum size must be specified for GNOME and Xfce, otherwise the icon is displayed with a width of 1
|
||||
|
|
@ -208,7 +208,7 @@ private:
|
|||
#include "native/juce_OpenGL_win32.h"
|
||||
|
||||
#elif JUCE_LINUX
|
||||
#include "native/juce_OpenGL_linux.h"
|
||||
#include "native/juce_OpenGL_linux_X11.h"
|
||||
|
||||
#elif JUCE_ANDROID
|
||||
#include "native/juce_OpenGL_android.h"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
extern ::Display* display;
|
||||
extern XContext windowHandleXContext;
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -61,7 +60,9 @@ public:
|
|||
: component (comp), renderContext (0), embeddedWindow (0), swapFrames (0), bestVisual (0),
|
||||
contextToShareWith (shareContext), context (nullptr), dummy (*this)
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
display = XWindowSystem::getInstance()->displayRef();
|
||||
|
||||
ScopedXLock xlock (display);
|
||||
XSync (display, False);
|
||||
|
||||
GLint attribs[] =
|
||||
|
|
@ -125,18 +126,20 @@ public:
|
|||
|
||||
if (embeddedWindow != 0)
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
ScopedXLock xlock (display);
|
||||
XUnmapWindow (display, embeddedWindow);
|
||||
XDestroyWindow (display, embeddedWindow);
|
||||
}
|
||||
|
||||
if (bestVisual != nullptr)
|
||||
XFree (bestVisual);
|
||||
|
||||
XWindowSystem::getInstance()->displayUnref();
|
||||
}
|
||||
|
||||
void initialiseOnRenderThread (OpenGLContext& c)
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
ScopedXLock xlock (display);
|
||||
renderContext = glXCreateContext (display, bestVisual, (GLXContext) contextToShareWith, GL_TRUE);
|
||||
c.makeActive();
|
||||
context = &c;
|
||||
|
|
@ -163,6 +166,8 @@ public:
|
|||
|
||||
static void deactivateCurrentContext()
|
||||
{
|
||||
ScopedXDisplay xDisplay;
|
||||
::Display* display = xDisplay.get();
|
||||
glXMakeCurrent (display, None, 0);
|
||||
}
|
||||
|
||||
|
|
@ -178,7 +183,7 @@ public:
|
|||
const Rectangle<int> physicalBounds =
|
||||
juce_LinuxScaledToPhysicalBounds (component.getPeer(), bounds);
|
||||
|
||||
ScopedXLock xlock;
|
||||
ScopedXLock xlock (display);
|
||||
XMoveResizeWindow (display, embeddedWindow,
|
||||
physicalBounds.getX(), physicalBounds.getY(),
|
||||
(unsigned int) jmax (1, physicalBounds.getWidth()),
|
||||
|
|
@ -229,12 +234,17 @@ private:
|
|||
OpenGLContext* context;
|
||||
DummyComponent dummy;
|
||||
|
||||
::Display* display;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
bool OpenGLHelpers::isContextActive()
|
||||
{
|
||||
ScopedXLock xlock;
|
||||
ScopedXDisplay xDisplay;
|
||||
::Display* display = xDisplay.get();
|
||||
|
||||
ScopedXLock xlock (display);
|
||||
return glXGetCurrentContext() != 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue