Browse Source

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.
tags/2021-05-28
Felipe F. Tonello hogliux 8 years ago
parent
commit
c2100022cc
22 changed files with 1468 additions and 1231 deletions
  1. +301
    -324
      modules/juce_audio_devices/native/juce_linux_Midi.cpp
  2. +14
    -5
      modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
  3. +18
    -7
      modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  4. +3
    -0
      modules/juce_core/files/juce_File.h
  5. +9
    -16
      modules/juce_core/native/juce_linux_Files.cpp
  6. +0
    -5
      modules/juce_events/juce_events.cpp
  7. +5
    -2
      modules/juce_events/juce_events.h
  8. +0
    -54
      modules/juce_events/native/juce_ScopedXLock.h
  9. +33
    -0
      modules/juce_events/native/juce_linux_EventLoop.h
  10. +99
    -235
      modules/juce_events/native/juce_linux_Messaging.cpp
  11. +1
    -1
      modules/juce_events/native/juce_win32_Messaging.cpp
  12. +3
    -2
      modules/juce_gui_basics/juce_gui_basics.cpp
  13. +4
    -0
      modules/juce_gui_basics/juce_gui_basics.h
  14. +312
    -0
      modules/juce_gui_basics/native/juce_linux_X11.cpp
  15. +114
    -0
      modules/juce_gui_basics/native/juce_linux_X11.h
  16. +70
    -66
      modules/juce_gui_basics/native/juce_linux_X11_Clipboard.cpp
  17. +455
    -498
      modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp
  18. +2
    -2
      modules/juce_gui_extra/juce_gui_extra.cpp
  19. +8
    -7
      modules/juce_gui_extra/native/juce_linux_X11_SystemTrayIcon.cpp
  20. +0
    -0
      modules/juce_gui_extra/native/juce_linux_X11_WebBrowserComponent.cpp
  21. +1
    -1
      modules/juce_opengl/juce_opengl.cpp
  22. +16
    -6
      modules/juce_opengl/native/juce_OpenGL_linux_X11.h

+ 301
- 324
modules/juce_audio_devices/native/juce_linux_Midi.cpp View File

@@ -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
{
AlsaClient*& instance = (forInput ? inInstance : outInstance);
if (instance == nullptr)
instance = new AlsaClient (forInput);
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);
return instance;
}
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 isInput() const noexcept { return input; }
bool isValid() const noexcept
{
return client.get() != nullptr && portId >= 0;
}
void registerCallback (AlsaPortAndCallback* cb)
{
if (cb != nullptr)
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)
{
const ScopedLock sl (callbackLock);
activeCallbacks.add (cb);
callbackEnabled = enable;
if (inputThread == nullptr)
inputThread = new MidiInputThread (*this);
if (enable)
client.registerCallback();
else
client.unregisterCallback();
}
}
inputThread->startThread();
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()
{
if (instance == nullptr)
instance = new AlsaClient();
return instance;
}
void unregisterCallback (AlsaPortAndCallback* cb)
void registerCallback ()
{
const ScopedLock sl (callbackLock);
if (inputThread == nullptr)
inputThread = new MidiInputThread (*this);
jassert (activeCallbacks.contains (cb));
activeCallbacks.removeAllInstancesOf (cb);
if (++activeCallbacks - 1 == 0)
inputThread->startThread();
}
if (activeCallbacks.size() == 0 && inputThread->isThreadRunning())
void unregisterCallback ()
{
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) {}
void createPort (const AlsaClient::Ptr& c, const String& name, bool forInput)
{
client = c;
AlsaClient::Port* port = nullptr;
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;
};
snd_seq_t* seqHandle = client->get();
snd_seq_port_info_t* portInfo = nullptr;
//==============================================================================
class AlsaPortAndCallback
{
public:
AlsaPortAndCallback (AlsaPort p, MidiInput* in, MidiInputCallback* cb)
: port (p), midiInput (in), callback (cb), callbackEnabled (false)
{
}
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);
~AlsaPortAndCallback()
{
enableCallback (false);
port.deletePort();
}
snd_seq_port_info_set_client (portInfo, sourceClient);
snd_seq_port_info_set_port (portInfo, -1);
void enableCallback (bool enable)
while (--numPorts >= 0)
{
if (callbackEnabled != enable)
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)
{
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;
const String portName = snd_seq_port_info_get_name(portInfo);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlsaPortAndCallback)
};
void AlsaClient::handleIncomingMidiMessage (snd_seq_event_t* event, const MidiMessage& message)
{
const ScopedLock sl (callbackLock);
deviceNamesFound.add (portName);
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_port_info_t* portInfo = nullptr;
if (snd_seq_port_info_malloc (&portInfo) == 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_READ
: SND_SEQ_PORT_CAP_WRITE)) != 0)
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;
}


+ 14
- 5
modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp View File

@@ -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;


+ 18
- 7
modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp View File

@@ -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


+ 3
- 0
modules/juce_core/files/juce_File.h View File

@@ -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,


+ 9
- 16
modules/juce_core/native/juce_linux_Files.cpp View File

@@ -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:


+ 0
- 5
modules/juce_events/juce_events.cpp View File

@@ -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


+ 5
- 2
modules/juce_events/juce_events.h View File

@@ -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"


+ 0
- 54
modules/juce_events/native/juce_ScopedXLock.h View File

@@ -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
- 0
modules/juce_events/native/juce_linux_EventLoop.h View 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 */

+ 99
- 235
modules/juce_events/native/juce_linux_Messaging.cpp View File

@@ -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);
ScopedLock sl (lock);
return dispatchNextInternalMessage() || dispatchNextXEvent();
fdCount = 1;
readCallback[WINDOW_SYSTEM_FD]->active = false;
}
// Wait for an event (either XEvent, or an internal Message)
bool sleepUntilEvent (const int timeoutMs)
bool dispatchNextEvent() noexcept
{
if (! isEmpty())
return true;
if (display != nullptr)
for (int counter = 0; counter < fdCount; counter++)
{
ScopedXLock xlock;
if (XPending (display))
return true;
const int i = loopCount++;
loopCount %= fdCount;
if (readCallback[i] != nullptr && readCallback[i]->active)
{
if ((*readCallback[i]) (pfds[i].fd))
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);
}
return false;
}
const int ret = select (fdmax + 1, &readset, 0, 0, &tv);
return (ret > 0); // ret <= 0 if error or timeout
bool sleepUntilEvent (const int timeoutMs)
{
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]; }
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;
}
int getWriteHandle() const noexcept { return fd[0]; }
int getReadHandle() const noexcept { return fd[1]; }
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();
}

+ 1
- 1
modules/juce_events/native/juce_win32_Messaging.cpp View File

@@ -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);


+ 3
- 2
modules/juce_gui_basics/juce_gui_basics.cpp View File

@@ -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


+ 4
- 0
modules/juce_gui_basics/juce_gui_basics.h View File

@@ -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
- 0
modules/juce_gui_basics/native/juce_linux_X11.cpp View 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
- 0
modules/juce_gui_basics/native/juce_linux_X11.h View 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

modules/juce_gui_basics/native/juce_linux_Clipboard.cpp → modules/juce_gui_basics/native/juce_linux_X11_Clipboard.cpp View File

@@ -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;
}
}
else
{
DBG ("requested unsupported clipboard");
}
if (data != nullptr)
{
const size_t maxReasonableSelectionSize = 1000000;
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"
}
// 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);
}
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);
}
}
}

modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp
File diff suppressed because it is too large
View File


+ 2
- 2
modules/juce_gui_extra/juce_gui_extra.cpp View File

@@ -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


modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp → modules/juce_gui_extra/native/juce_linux_X11_SystemTrayIcon.cpp View File

@@ -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

modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp → modules/juce_gui_extra/native/juce_linux_X11_WebBrowserComponent.cpp View File


+ 1
- 1
modules/juce_opengl/juce_opengl.cpp View File

@@ -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"


modules/juce_opengl/native/juce_OpenGL_linux.h → modules/juce_opengl/native/juce_OpenGL_linux_X11.h View File

@@ -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…
Cancel
Save