diff --git a/modules/juce_audio_devices/native/juce_linux_Midi.cpp b/modules/juce_audio_devices/native/juce_linux_Midi.cpp index ea0f838a32..c027786193 100644 --- a/modules/juce_audio_devices/native/juce_linux_Midi.cpp +++ b/modules/juce_audio_devices/native/juce_linux_Midi.cpp @@ -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 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 activeCallbacks; + int clientId; + OwnedArray ports; + Atomic activeCallbacks; CriticalSection callbackLock; - static AlsaClient* inInstance; - static AlsaClient* outInstance; + static AlsaClient* instance; //============================================================================== friend class ReferenceCountedObjectPtr; friend struct ContainerDeletePolicy; - 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 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 (internal); + AlsaClient::Ptr client (AlsaClient::getInstance()); + client->deletePort (static_cast (internal)); } void MidiOutput::sendMessageNow (const MidiMessage& message) { - static_cast (internal)->sendMessageNow (message); + static_cast (internal)->sendMessageNow (message); } //============================================================================== @@ -559,17 +533,18 @@ MidiInput::MidiInput (const String& nm) MidiInput::~MidiInput() { stop(); - delete static_cast (internal); + AlsaClient::Ptr client (AlsaClient::getInstance()); + client->deletePort (static_cast (internal)); } void MidiInput::start() { - static_cast (internal)->enableCallback (true); + static_cast (internal)->enableCallback (true); } void MidiInput::stop() { - static_cast (internal)->enableCallback (false); + static_cast (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; } diff --git a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp index 068c34bb19..40c048e91a 100644 --- a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp @@ -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 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; diff --git a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index 5367d9b343..97f61c4fca 100644 --- a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -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 diff --git a/modules/juce_core/files/juce_File.h b/modules/juce_core/files/juce_File.h index a71db2b9a5..c21a5970f2 100644 --- a/modules/juce_core/files/juce_File.h +++ b/modules/juce_core/files/juce_File.h @@ -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, diff --git a/modules/juce_core/native/juce_linux_Files.cpp b/modules/juce_core/native/juce_linux_Files.cpp index ce6a764c1a..b285eae09d 100644 --- a/modules/juce_core/native/juce_linux_Files.cpp +++ b/modules/juce_core/native/juce_linux_Files.cpp @@ -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: diff --git a/modules/juce_events/juce_events.cpp b/modules/juce_events/juce_events.cpp index 48edf2b675..f3c328096c 100644 --- a/modules/juce_events/juce_events.cpp +++ b/modules/juce_events/juce_events.cpp @@ -53,10 +53,6 @@ #import #elif JUCE_LINUX - #include - #include - #include - #undef KeyPress #include #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 diff --git a/modules/juce_events/juce_events.h b/modules/juce_events/juce_events.h index 68edca4e6c..f7105c37bd 100644 --- a/modules/juce_events/juce_events.h +++ b/modules/juce_events/juce_events.h @@ -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" diff --git a/modules/juce_events/native/juce_ScopedXLock.h b/modules/juce_events/native/juce_ScopedXLock.h deleted file mode 100644 index 418ca1047b..0000000000 --- a/modules/juce_events/native/juce_ScopedXLock.h +++ /dev/null @@ -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 diff --git a/modules/juce_events/native/juce_linux_EventLoop.h b/modules/juce_events/native/juce_linux_EventLoop.h new file mode 100644 index 0000000000..2b552a49b3 --- /dev/null +++ b/modules/juce_events/native/juce_linux_EventLoop.h @@ -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 + struct CallbackFunction : public CallbackFunctionBase + { + FdCallbackFunction callback; + + CallbackFunction (FdCallbackFunction c) : callback (c) {} + + bool operator() (int fd) override { return callback (fd); } + }; + + template + void setWindowSystemFd (int fd, FdCallbackFunction readCallback) + { + setWindowSystemFdInternal (fd, new CallbackFunction (readCallback)); + } + void removeWindowSystemFd() noexcept; + + void setWindowSystemFdInternal (int fd, CallbackFunctionBase* readCallback) noexcept; +} + +#endif /* JUCE_LINUX_EVENTLOOP_H_INCLUDED */ diff --git a/modules/juce_events/native/juce_linux_Messaging.cpp b/modules/juce_events/native/juce_linux_Messaging.cpp index ad18da3916..aa9117cd4f 100644 --- a/modules/juce_events/native/juce_linux_Messaging.cpp +++ b/modules/juce_events/native/juce_linux_Messaging.cpp @@ -28,112 +28,115 @@ ============================================================================== */ -#if JUCE_DEBUG && ! defined (JUCE_DEBUG_XERRORS) - #define JUCE_DEBUG_XERRORS 1 -#endif +#include -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 (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 (fdCount), timeoutMs); + return (pnum > 0); } //============================================================================== @@ -143,46 +146,16 @@ private: CriticalSection lock; ReferenceCountedArray queue; int fd[2]; + pollfd pfds[FD_COUNT]; + ScopedPointer 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(); } diff --git a/modules/juce_events/native/juce_win32_Messaging.cpp b/modules/juce_events/native/juce_win32_Messaging.cpp index f7e8a05767..a40bac41f9 100644 --- a/modules/juce_events/native/juce_win32_Messaging.cpp +++ b/modules/juce_events/native/juce_win32_Messaging.cpp @@ -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); diff --git a/modules/juce_gui_basics/juce_gui_basics.cpp b/modules/juce_gui_basics/juce_gui_basics.cpp index 878bb56628..72223c99ac 100644 --- a/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/modules/juce_gui_basics/juce_gui_basics.cpp @@ -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 diff --git a/modules/juce_gui_basics/juce_gui_basics.h b/modules/juce_gui_basics/juce_gui_basics.h index 1130a26ddb..3b43c14dae 100644 --- a/modules/juce_gui_basics/juce_gui_basics.h +++ b/modules/juce_gui_basics/juce_gui_basics.h @@ -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" diff --git a/modules/juce_gui_basics/native/juce_linux_X11.cpp b/modules/juce_gui_basics/native/juce_linux_X11.cpp new file mode 100644 index 0000000000..2f9c440669 --- /dev/null +++ b/modules/juce_gui_basics/native/juce_linux_X11.cpp @@ -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); +} diff --git a/modules/juce_gui_basics/native/juce_linux_X11.h b/modules/juce_gui_basics/native/juce_linux_X11.h new file mode 100644 index 0000000000..3a31d2351e --- /dev/null +++ b/modules/juce_gui_basics/native/juce_linux_X11.h @@ -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 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 diff --git a/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp b/modules/juce_gui_basics/native/juce_linux_X11_Clipboard.cpp similarity index 64% rename from modules/juce_gui_basics/native/juce_linux_Clipboard.cpp rename to modules/juce_gui_basics/native/juce_linux_X11_Clipboard.cpp index 0427e9458d..04dbc6e0a8 100644 --- a/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp +++ b/modules/juce_gui_basics/native/juce_linux_X11_Clipboard.cpp @@ -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 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 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 (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 (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 (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 (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); } } } diff --git a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp similarity index 85% rename from modules/juce_gui_basics/native/juce_linux_Windowing.cpp rename to modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp index 891f4d5bfb..406e85173d 100644 --- a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp @@ -22,115 +22,16 @@ ============================================================================== */ -extern ::Display* display; -extern XContext windowHandleXContext; -typedef void (*WindowMessageReceiveCallback) (XEvent&); -extern WindowMessageReceiveCallback dispatchWindowMessage; - - -//============================================================================== -struct Atoms -{ - Atoms() - { - protocols = getIfExists ("WM_PROTOCOLS"); - protocolList [TAKE_FOCUS] = getIfExists ("WM_TAKE_FOCUS"); - protocolList [DELETE_WINDOW] = getIfExists ("WM_DELETE_WINDOW"); - protocolList [PING] = getIfExists ("_NET_WM_PING"); - changeState = getIfExists ("WM_CHANGE_STATE"); - state = getIfExists ("WM_STATE"); - userTime = getCreating ("_NET_WM_USER_TIME"); - activeWin = getCreating ("_NET_ACTIVE_WINDOW"); - pid = getCreating ("_NET_WM_PID"); - windowType = getIfExists ("_NET_WM_WINDOW_TYPE"); - windowState = getIfExists ("_NET_WM_STATE"); - - XdndAware = getCreating ("XdndAware"); - XdndEnter = getCreating ("XdndEnter"); - XdndLeave = getCreating ("XdndLeave"); - XdndPosition = getCreating ("XdndPosition"); - XdndStatus = getCreating ("XdndStatus"); - XdndDrop = getCreating ("XdndDrop"); - XdndFinished = getCreating ("XdndFinished"); - XdndSelection = getCreating ("XdndSelection"); - - XdndTypeList = getCreating ("XdndTypeList"); - XdndActionList = getCreating ("XdndActionList"); - XdndActionCopy = getCreating ("XdndActionCopy"); - XdndActionPrivate = getCreating ("XdndActionPrivate"); - XdndActionDescription = getCreating ("XdndActionDescription"); - - allowedMimeTypes[0] = getCreating ("UTF8_STRING"); - allowedMimeTypes[1] = getCreating ("text/plain;charset=utf-8"); - allowedMimeTypes[2] = getCreating ("text/plain"); - allowedMimeTypes[3] = getCreating ("text/uri-list"); - - allowedActions[0] = getCreating ("XdndActionMove"); - allowedActions[1] = XdndActionCopy; - allowedActions[2] = getCreating ("XdndActionLink"); - allowedActions[3] = getCreating ("XdndActionAsk"); - allowedActions[4] = XdndActionPrivate; - } - - enum ProtocolItems - { - TAKE_FOCUS = 0, - DELETE_WINDOW = 1, - PING = 2 - }; - - Atom 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 getIfExists (const char* name) { return XInternAtom (display, name, True); } - static Atom getCreating (const char* name) { return XInternAtom (display, name, False); } - - static String getName (const Atom atom) - { - if (atom == None) - return "None"; - - return String (XGetAtomName (display, atom)); - } - - static bool isMimeTypeFile (const Atom atom) { return getName (atom).equalsIgnoreCase ("text/uri-list"); } -}; - -const unsigned long Atoms::DndVersion = 3; +#if JUCE_DEBUG && ! defined (JUCE_DEBUG_XERRORS) + #define JUCE_DEBUG_XERRORS 1 +#endif -//============================================================================== -struct GetXProperty -{ - GetXProperty (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; - } +extern WindowMessageReceiveCallback dispatchWindowMessage; - ~GetXProperty() - { - if (data != nullptr) - XFree (data); - } +extern XContext windowHandleXContext; - bool success; - unsigned char* data; - unsigned long numItems, bytesLeft; - Atom actualType; - int actualFormat; -}; +//=============================== X11 - Keys =================================== -//============================================================================== namespace Keys { enum MouseButtons @@ -153,6 +54,9 @@ namespace Keys bool KeyPress::isKeyCurrentlyDown (const int keyCode) { + ScopedXDisplay xDisplay; + ::Display* display = xDisplay.get(); + if (display == nullptr) return false; @@ -175,7 +79,7 @@ bool KeyPress::isKeyCurrentlyDown (const int keyCode) } } - ScopedXLock xlock; + ScopedXLock xlock (display); const int keycode = XKeysymToKeycode (display, (KeySym) keysym); @@ -185,6 +89,63 @@ bool KeyPress::isKeyCurrentlyDown (const int keyCode) } //============================================================================== + +const int KeyPress::spaceKey = XK_space & 0xff; +const int KeyPress::returnKey = XK_Return & 0xff; +const int KeyPress::escapeKey = XK_Escape & 0xff; +const int KeyPress::backspaceKey = XK_BackSpace & 0xff; +const int KeyPress::leftKey = (XK_Left & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::rightKey = (XK_Right & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::upKey = (XK_Up & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::downKey = (XK_Down & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::pageUpKey = (XK_Page_Up & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::pageDownKey = (XK_Page_Down & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::endKey = (XK_End & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::homeKey = (XK_Home & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::insertKey = (XK_Insert & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::deleteKey = (XK_Delete & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::tabKey = XK_Tab & 0xff; +const int KeyPress::F1Key = (XK_F1 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F2Key = (XK_F2 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F3Key = (XK_F3 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F4Key = (XK_F4 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F5Key = (XK_F5 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F6Key = (XK_F6 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F7Key = (XK_F7 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F8Key = (XK_F8 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F9Key = (XK_F9 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F10Key = (XK_F10 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F11Key = (XK_F11 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F12Key = (XK_F12 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F13Key = (XK_F13 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F14Key = (XK_F14 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F15Key = (XK_F15 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::F16Key = (XK_F16 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::numberPad0 = (XK_KP_0 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::numberPad1 = (XK_KP_1 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::numberPad2 = (XK_KP_2 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::numberPad3 = (XK_KP_3 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::numberPad4 = (XK_KP_4 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::numberPad5 = (XK_KP_5 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::numberPad6 = (XK_KP_6 & 0xff) | Keys::extendedKeyModifier; +const int KeyPress::numberPad7 = (XK_KP_7 & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::numberPad8 = (XK_KP_8 & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::numberPad9 = (XK_KP_9 & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::numberPadAdd = (XK_KP_Add & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::numberPadSubtract = (XK_KP_Subtract & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::numberPadMultiply = (XK_KP_Multiply & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::numberPadDivide = (XK_KP_Divide & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::numberPadSeparator = (XK_KP_Separator & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::numberPadDecimalPoint = (XK_KP_Decimal & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::numberPadEquals = (XK_KP_Equal & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::numberPadDelete = (XK_KP_Delete & 0xff)| Keys::extendedKeyModifier; +const int KeyPress::playKey = ((int) 0xffeeff00) | Keys::extendedKeyModifier; +const int KeyPress::stopKey = ((int) 0xffeeff01) | Keys::extendedKeyModifier; +const int KeyPress::fastForwardKey = ((int) 0xffeeff02) | Keys::extendedKeyModifier; +const int KeyPress::rewindKey = ((int) 0xffeeff03) | Keys::extendedKeyModifier; + +//================================== X11 - Shm ================================= + #if JUCE_USE_XSHM namespace XSHMHelpers { @@ -195,7 +156,7 @@ namespace XSHMHelpers return 0; } - static bool isShmAvailable() noexcept + static bool isShmAvailable(::Display* display) noexcept { static bool isChecked = false; static bool isAvailable = false; @@ -209,7 +170,7 @@ namespace XSHMHelpers int major, minor; Bool pixmaps; - ScopedXLock xlock; + ScopedXLock xlock (display); if (XShmQueryVersion (display, &major, &minor, &pixmaps)) { @@ -264,7 +225,8 @@ namespace XSHMHelpers } #endif -//============================================================================== +//=============================== X11 - Render ================================= + #if JUCE_USE_XRENDER namespace XRender { @@ -278,7 +240,7 @@ namespace XRender static tXRenderFindFormat xRenderFindFormat = nullptr; static tXRenderFindVisualFormat xRenderFindVisualFormat = nullptr; - static bool isAvailable() + static bool isAvailable(::Display* display) { static bool hasLoaded = false; @@ -288,7 +250,7 @@ namespace XRender { hasLoaded = true; - ScopedXLock xlock; + ScopedXLock xlock (display); if (void* h = dlopen ("libXrender.so", RTLD_GLOBAL | RTLD_NOW)) { @@ -315,15 +277,15 @@ namespace XRender return xRenderQueryVersion != nullptr; } - static bool hasCompositingWindowManager() noexcept + static bool hasCompositingWindowManager(::Display* display) noexcept { return display != nullptr && XGetSelectionOwner (display, Atoms::getCreating ("_NET_WM_CM_S0")) != 0; } - static XRenderPictFormat* findPictureFormat() + static XRenderPictFormat* findPictureFormat(::Display* display) { - ScopedXLock xlock; + ScopedXLock xlock (display); XRenderPictFormat* pictFormat = nullptr; if (isAvailable()) @@ -362,12 +324,13 @@ namespace XRender } #endif -//============================================================================== +//================================ X11 - Visuals =============================== + namespace Visuals { - static Visual* findVisualWithDepth (const int desiredDepth) noexcept + static Visual* findVisualWithDepth (::Display* display, const int desiredDepth) noexcept { - ScopedXLock xlock; + ScopedXLock xlock (display); Visual* visual = nullptr; int numVisuals = 0; @@ -414,19 +377,19 @@ namespace Visuals return visual; } - static Visual* findVisualFormat (const int desiredDepth, int& matchedDepth) noexcept + static Visual* findVisualFormat (::Display* display, const int desiredDepth, int& matchedDepth) noexcept { Visual* visual = nullptr; if (desiredDepth == 32) { #if JUCE_USE_XSHM - if (XSHMHelpers::isShmAvailable()) + if (XSHMHelpers::isShmAvailable (display)) { #if JUCE_USE_XRENDER - if (XRender::isAvailable()) + if (XRender::isAvailable (display)) { - if (XRenderPictFormat* pictFormat = XRender::findPictureFormat()) + if (XRenderPictFormat* pictFormat = XRender::findPictureFormat (display)) { int numVisuals = 0; XVisualInfo desiredVisual; @@ -459,7 +422,7 @@ namespace Visuals #endif if (visual == nullptr) { - visual = findVisualWithDepth (32); + visual = findVisualWithDepth (display, 32); if (visual != nullptr) matchedDepth = 32; } @@ -469,14 +432,14 @@ namespace Visuals if (visual == nullptr && desiredDepth >= 24) { - visual = findVisualWithDepth (24); + visual = findVisualWithDepth (display, 24); if (visual != nullptr) matchedDepth = 24; } if (visual == nullptr && desiredDepth >= 16) { - visual = findVisualWithDepth (16); + visual = findVisualWithDepth (display, 16); if (visual != nullptr) matchedDepth = 16; } @@ -485,27 +448,29 @@ namespace Visuals } } -//============================================================================== +//================================= X11 - Bitmap =============================== + class XBitmapImage : public ImagePixelData { public: - XBitmapImage (const Image::PixelFormat format, const int w, const int h, + XBitmapImage (::Display* _display, const Image::PixelFormat format, const int w, const int h, const bool clearImage, const unsigned int imageDepth_, Visual* visual) : ImagePixelData (format, w, h), imageDepth (imageDepth_), - gc (None) + gc (None), + display (_display) { jassert (format == Image::RGB || format == Image::ARGB); pixelStride = (format == Image::RGB) ? 3 : 4; lineStride = ((w * pixelStride + 3) & ~3); - ScopedXLock xlock; + ScopedXLock xlock (display); #if JUCE_USE_XSHM usingXShm = false; - if ((imageDepth > 16) && XSHMHelpers::isShmAvailable()) + if ((imageDepth > 16) && XSHMHelpers::isShmAvailable (display)) { zerostruct (segmentInfo); @@ -594,7 +559,7 @@ public: ~XBitmapImage() { - ScopedXLock xlock; + ScopedXLock xlock (display); if (gc != None) XFreeGC (display, gc); @@ -645,7 +610,7 @@ public: void blitToWindow (Window window, int dx, int dy, unsigned int dw, unsigned int dh, int sx, int sy) { - ScopedXLock xlock; + ScopedXLock xlock (display); if (gc == None) { @@ -715,6 +680,7 @@ private: int pixelStride, lineStride; uint8* imageData; GC gc; + ::Display* display; #if JUCE_USE_XSHM XShmSegmentInfo segmentInfo; @@ -735,6 +701,7 @@ private: }; //============================================================================== + #if JUCE_USE_XRANDR template <> struct ContainerDeletePolicy @@ -755,17 +722,18 @@ struct ContainerDeletePolicy }; #endif -//============================================================================== +//================================ X11 - DisplayGeometry ======================= + class DisplayGeometry { private: //============================================================================== - DisplayGeometry (::Display* dpy, double masterScale) + DisplayGeometry (::Display* display, double masterScale) { jassert (instance == nullptr); instance = this; - queryDisplayInfos (dpy, masterScale); + queryDisplayInfos (display, masterScale); updatePositions(); } @@ -903,10 +871,10 @@ public: return *instance; } - static DisplayGeometry& getOrCreateInstance (::Display* dpy, double masterScale) + static DisplayGeometry& getOrCreateInstance (::Display* display, double masterScale) { if (instance == nullptr) - new DisplayGeometry (dpy, masterScale); + new DisplayGeometry (display, masterScale); return getInstance(); } @@ -917,14 +885,14 @@ private: //============================================================================== #if JUCE_USE_XINERAMA - static Array XineramaQueryDisplays (::Display* dpy) + static Array XineramaQueryDisplays (::Display* display) { typedef Bool (*tXineramaIsActive) (::Display*); typedef XineramaScreenInfo* (*tXineramaQueryScreens) (::Display*, int*); int major_opcode, first_event, first_error; - if (XQueryExtension (dpy, "XINERAMA", &major_opcode, &first_event, &first_error)) + if (XQueryExtension (display, "XINERAMA", &major_opcode, &first_event, &first_error)) { static void* libXinerama = nullptr; static tXineramaIsActive isActiveFuncPtr = nullptr; @@ -944,10 +912,10 @@ private: } } - if (isActiveFuncPtr != nullptr && xineramaQueryScreens != nullptr && isActiveFuncPtr (dpy) != 0) + if (isActiveFuncPtr != nullptr && xineramaQueryScreens != nullptr && isActiveFuncPtr (display) != 0) { int numScreens; - if (XineramaScreenInfo* xinfo = xineramaQueryScreens (dpy, &numScreens)) + if (XineramaScreenInfo* xinfo = xineramaQueryScreens (display, &numScreens)) { Array infos (xinfo, numScreens); XFree (xinfo); @@ -1013,34 +981,34 @@ private: } //============================================================================== - XRRScreenResources* getScreenResources (::Display* dpy, ::Window window) + XRRScreenResources* getScreenResources (::Display* display, ::Window window) { if (getScreenResourcesPtr != nullptr) - return getScreenResourcesPtr (dpy, window); + return getScreenResourcesPtr (display, window); return nullptr; } - XRROutputInfo* getOutputInfo (::Display* dpy, XRRScreenResources* resources, RROutput output) + XRROutputInfo* getOutputInfo (::Display* display, XRRScreenResources* resources, RROutput output) { if (getOutputInfoPtr != nullptr) - return getOutputInfoPtr (dpy, resources, output); + return getOutputInfoPtr (display, resources, output); return nullptr; } - XRRCrtcInfo* getCrtcInfo (::Display* dpy, XRRScreenResources* resources, RRCrtc crtc) + XRRCrtcInfo* getCrtcInfo (::Display* display, XRRScreenResources* resources, RRCrtc crtc) { if (getCrtcInfoPtr != nullptr) - return getCrtcInfoPtr (dpy, resources, crtc); + return getCrtcInfoPtr (display, resources, crtc); return nullptr; } - RROutput getOutputPrimary (::Display* dpy, ::Window window) + RROutput getOutputPrimary (::Display* display, ::Window window) { if (getOutputPrimaryPtr != nullptr) - return getOutputPrimaryPtr (dpy, window); + return getOutputPrimaryPtr (display, window); return 0; } @@ -1091,7 +1059,7 @@ private: #endif - static double getDisplayDPI (int index) + static double getDisplayDPI (::Display* display, int index) { double dpiX = (DisplayWidth (display, index) * 25.4) / DisplayWidthMM (display, index); double dpiY = (DisplayHeight (display, index) * 25.4) / DisplayHeightMM (display, index); @@ -1159,26 +1127,26 @@ private: } //============================================================================== - void queryDisplayInfos (::Display* dpy, double masterScale) noexcept + void queryDisplayInfos (::Display* display, double masterScale) noexcept { - ScopedXLock xlock; + ScopedXLock xlock (display); #if JUCE_USE_XRANDR { int major_opcode, first_event, first_error; - if (XQueryExtension (dpy, "RANDR", &major_opcode, &first_event, &first_error)) + if (XQueryExtension (display, "RANDR", &major_opcode, &first_event, &first_error)) { XRandrWrapper& xrandr = XRandrWrapper::getInstance(); ScopedPointer screens; - const int numMonitors = ScreenCount (dpy); - RROutput mainDisplay = xrandr.getOutputPrimary (dpy, RootWindow (dpy, 0)); + const int numMonitors = ScreenCount (display); + RROutput mainDisplay = xrandr.getOutputPrimary (display, RootWindow (display, 0)); for (int i = 0; i < numMonitors; ++i) { - if ((screens = xrandr.getScreenResources (dpy, RootWindow (dpy, i))).get()) + if ((screens = xrandr.getScreenResources (display, RootWindow (display, i))).get()) { for (int j = 0; j < screens->noutput; ++j) { @@ -1192,14 +1160,14 @@ private: ScopedPointer output; - if ((output = xrandr.getOutputInfo (dpy, screens.get(), screens->outputs[j])).get()) + if ((output = xrandr.getOutputInfo (display, screens.get(), screens->outputs[j])).get()) { if (! output->crtc) continue; ScopedPointer crtc; - if ((crtc = xrandr.getCrtcInfo (dpy, screens.get(), output->crtc)).get()) + if ((crtc = xrandr.getCrtcInfo (display, screens.get(), output->crtc)).get()) { ExtendedInfo e; e.totalBounds = Rectangle (crtc->x, crtc->y, @@ -1207,7 +1175,7 @@ private: e.usableBounds = e.totalBounds.withZeroOrigin(); // Support for usable area is not implemented in JUCE yet e.topLeftScaled = e.totalBounds.getTopLeft(); e.isMain = (mainDisplay == screens->outputs[j]) && (i == 0); - e.dpi = getDisplayDPI (0); + e.dpi = getDisplayDPI (display, 0); // The raspberry pi returns a zero sized display, so we need to guard for divide-by-zero if (output->mm_width > 0 && output->mm_height > 0) @@ -1231,7 +1199,7 @@ private: #endif #if JUCE_USE_XINERAMA { - Array screens = XineramaQueryDisplays (dpy); + Array screens = XineramaQueryDisplays (display); int numMonitors = screens.size(); for (int index = 0; index < numMonitors; ++index) @@ -1249,7 +1217,7 @@ private: e.topLeftScaled = e.totalBounds.getTopLeft(); // this will be overwritten by updatePositions later e.isMain = (index == 0); e.scale = masterScale; - e.dpi = getDisplayDPI (0); // (all screens share the same DPI) + e.dpi = getDisplayDPI (display, 0); // (all screens share the same DPI) infos.add (e); } @@ -1260,15 +1228,15 @@ private: if (infos.size() == 0) #endif { - Atom hints = Atoms::getIfExists ("_NET_WORKAREA"); + Atom hints = Atoms::getIfExists (display, "_NET_WORKAREA"); if (hints != None) { - const int numMonitors = ScreenCount (dpy); + const int numMonitors = ScreenCount (display); for (int i = 0; i < numMonitors; ++i) { - GetXProperty prop (RootWindow (dpy, i), hints, 0, 4, false, XA_CARDINAL); + GetXProperty prop (display, RootWindow (display, i), hints, 0, 4, false, XA_CARDINAL); if (prop.success && prop.actualType == XA_CARDINAL && prop.actualFormat == 32 && prop.numItems == 4) { @@ -1281,7 +1249,7 @@ private: e.topLeftScaled = e.totalBounds.getTopLeft(); // this will be overwritten by updatePositions later e.isMain = (infos.size() == 0); e.scale = masterScale; - e.dpi = getDisplayDPI (i); + e.dpi = getDisplayDPI (display, i); infos.add (e); } @@ -1291,13 +1259,13 @@ private: if (infos.size() == 0) { ExtendedInfo e; - e.totalBounds = Rectangle (DisplayWidth (dpy, DefaultScreen (dpy)), - DisplayHeight (dpy, DefaultScreen (dpy))); + e.totalBounds = Rectangle (DisplayWidth (display, DefaultScreen (display)), + DisplayHeight (display, DefaultScreen (display))); e.usableBounds = e.totalBounds; // Support for usable area is not implemented in JUCE yet e.topLeftScaled = e.totalBounds.getTopLeft(); // this will be overwritten by updatePositions later e.isMain = true; e.scale = masterScale; - e.dpi = getDisplayDPI (0); + e.dpi = getDisplayDPI (display, 0); infos.add (e); } @@ -1334,7 +1302,7 @@ private: }; //============================================================================== - void updateScaledDisplayCoordinate(bool updateYCoordinates) + void updateScaledDisplayCoordinate (bool updateYCoordinates) { if (infos.size() < 2) return; @@ -1405,12 +1373,13 @@ void ContainerDeletePolicy::destroy (XRRCrtcInfo* ptr) } #endif -//============================================================================== +//=============================== X11 - Pixmap ================================= + namespace PixmapHelpers { - Pixmap createColourPixmapFromImage (Display* display, const Image& image) + Pixmap createColourPixmapFromImage (::Display* display, const Image& image) { - ScopedXLock xlock; + ScopedXLock xlock (display); const unsigned int width = (unsigned int) image.getWidth(); const unsigned int height = (unsigned int) image.getHeight(); @@ -1435,9 +1404,9 @@ namespace PixmapHelpers return pixmap; } - Pixmap createMaskPixmapFromImage (Display* display, const Image& image) + Pixmap createMaskPixmapFromImage (::Display* display, const Image& image) { - ScopedXLock xlock; + ScopedXLock xlock (display); const unsigned int width = (unsigned int) image.getWidth(); const unsigned int height = (unsigned int) image.getHeight(); @@ -1496,8 +1465,11 @@ public: // it's dangerous to create a window on a thread other than the message thread.. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); - dispatchWindowMessage = windowMessageReceive; - repainter = new LinuxRepaintManager (*this); + display = XWindowSystem::getInstance()->displayRef(); + + atoms = new Atoms (display); + dragState = new DragState (display); + repainter = new LinuxRepaintManager (*this, display); if (isAlwaysOnTop) ++numAlwaysOnTopPeers; @@ -1518,21 +1490,11 @@ public: if (isAlwaysOnTop) --numAlwaysOnTopPeers; - } - // (this callback is hooked up in the messaging code) - static void windowMessageReceive (XEvent& event) - { - if (event.xany.window != None) - { - if (LinuxComponentPeer* const peer = getPeerFor (event.xany.window)) - peer->handleWindowMessage (event); - } - else if (event.xany.type == KeymapNotify) - { - const XKeymapEvent& keymapEvent = (const XKeymapEvent&) event.xkeymap; - memcpy (Keys::keyStates, keymapEvent.key_vector, 32); - } + // delete before display + repainter = nullptr; + + display = XWindowSystem::getInstance()->displayUnref(); } //============================================================================== @@ -1547,7 +1509,7 @@ public: if (display != nullptr) { - ScopedXLock xlock; + ScopedXLock xlock (display); if (! XFindContext (display, (XID) windowHandle, windowHandleXContext, &peer)) if (peer != nullptr && ! ComponentPeer::isValidPeer (reinterpret_cast (peer))) @@ -1559,7 +1521,7 @@ public: void setVisible (bool shouldBeVisible) override { - ScopedXLock xlock; + ScopedXLock xlock (display); if (shouldBeVisible) XMapWindow (display, windowH); @@ -1571,7 +1533,7 @@ public: { XTextProperty nameProperty; char* strings[] = { const_cast (title.toRawUTF8()) }; - ScopedXLock xlock; + ScopedXLock xlock (display); if (XStringListToTextProperty (strings, 1, &nameProperty)) { @@ -1588,7 +1550,7 @@ public: { // When transitioning back from fullscreen, we might need to remove // the FULLSCREEN window property - Atom fs = Atoms::getIfExists ("_NET_WM_STATE_FULLSCREEN"); + Atom fs = Atoms::getIfExists (display, "_NET_WM_STATE_FULLSCREEN"); if (fs != None) { @@ -1599,13 +1561,13 @@ public: clientMsg.window = windowH; clientMsg.type = ClientMessage; clientMsg.format = 32; - clientMsg.message_type = atoms.windowState; + clientMsg.message_type = atoms->windowState; clientMsg.data.l[0] = 0; // Remove clientMsg.data.l[1] = (long) fs; clientMsg.data.l[2] = 0; clientMsg.data.l[3] = 1; // Normal Source - ScopedXLock xlock; + ScopedXLock xlock (display); XSendEvent (display, root, false, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*) &clientMsg); @@ -1625,7 +1587,7 @@ public: DisplayGeometry::scaledToPhysical (bounds); WeakReference deletionChecker (&component); - ScopedXLock xlock; + ScopedXLock xlock (display); XSizeHints* const hints = XAllocSizeHints(); hints->flags = USSize | USPosition; @@ -1691,10 +1653,10 @@ public: clientMsg.window = windowH; clientMsg.type = ClientMessage; clientMsg.format = 32; - clientMsg.message_type = atoms.changeState; + clientMsg.message_type = atoms->changeState; clientMsg.data.l[0] = IconicState; - ScopedXLock xlock; + ScopedXLock xlock (display); XSendEvent (display, root, false, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*) &clientMsg); } else @@ -1705,11 +1667,11 @@ public: bool isMinimised() const override { - ScopedXLock xlock; - GetXProperty prop (windowH, atoms.state, 0, 64, false, atoms.state); + ScopedXLock xlock (display); + GetXProperty prop (display, windowH, atoms->state, 0, 64, false, atoms->state); return prop.success - && prop.actualType == atoms.state + && prop.actualType == atoms->state && prop.actualFormat == 32 && prop.numItems > 0 && ((unsigned long*) prop.data)[0] == IconicState; @@ -1744,7 +1706,7 @@ public: uint32 windowListSize = 0; Window parent, root; - ScopedXLock xlock; + ScopedXLock xlock (display); if (XQueryTree (display, windowH, &root, &parent, &windowList, &windowListSize) != 0) { if (windowList != nullptr) @@ -1762,7 +1724,7 @@ public: uint32 windowListSize = 0; bool result = false; - ScopedXLock xlock; + ScopedXLock xlock (display); Window parent, root = RootWindow (display, DefaultScreen (display)); if (XQueryTree (display, root, &root, &parent, &windowList, &windowListSize) != 0) @@ -1807,7 +1769,7 @@ public: int wx, wy; unsigned int ww, wh, bw, bitDepth; - ScopedXLock xlock; + ScopedXLock xlock (display); localPos *= currentScaleFactor; @@ -1835,12 +1797,12 @@ public: } { - ScopedXLock xlock; + ScopedXLock xlock (display); XEvent ev; ev.xclient.type = ClientMessage; ev.xclient.serial = 0; ev.xclient.send_event = True; - ev.xclient.message_type = atoms.activeWin; + ev.xclient.message_type = atoms->activeWin; ev.xclient.window = windowH; ev.xclient.format = 32; ev.xclient.data.l[0] = 2; @@ -1869,7 +1831,7 @@ public: Window newStack[] = { otherPeer->windowH, windowH }; - ScopedXLock xlock; + ScopedXLock xlock (display); XRestackWindows (display, newStack, 2); } else @@ -1880,7 +1842,7 @@ public: { int revert = 0; Window focusedWindow = 0; - ScopedXLock xlock; + ScopedXLock xlock (display); XGetInputFocus (display, &focusedWindow, &revert); return focusedWindow == windowH; @@ -1889,7 +1851,7 @@ public: void grabFocus() override { XWindowAttributes atts; - ScopedXLock xlock; + ScopedXLock xlock (display); if (windowH != 0 && XGetWindowAttributes (display, windowH, &atts) @@ -1926,8 +1888,8 @@ public: for (int x = 0; x < newIcon.getWidth(); ++x) data[index++] = (unsigned long) newIcon.getPixelAt (x, y).getARGB(); - ScopedXLock xlock; - xchangeProperty (windowH, Atoms::getCreating ("_NET_WM_ICON"), XA_CARDINAL, 32, data.getData(), dataSize); + ScopedXLock xlock (display); + xchangeProperty (windowH, Atoms::getCreating (display, "_NET_WM_ICON"), XA_CARDINAL, 32, data.getData(), dataSize); deleteIconPixmaps(); @@ -1948,7 +1910,7 @@ public: void deleteIconPixmaps() { - ScopedXLock xlock; + ScopedXLock xlock (display); XWMHints* wmHints = XGetWMHints (display, windowH); if (wmHints != nullptr) @@ -2011,9 +1973,9 @@ public: default: #if JUCE_USE_XSHM - if (XSHMHelpers::isShmAvailable()) + if (XSHMHelpers::isShmAvailable (display)) { - ScopedXLock xlock; + ScopedXLock xlock (display); if (event.xany.type == XShmGetEventBase (display)) repainter->notifyPaintCompleted(); } @@ -2033,7 +1995,7 @@ public: KeySym sym; { - ScopedXLock xlock; + ScopedXLock xlock (display); updateKeyStates ((int) keyEvent.keycode, true); String oldLocale (::setlocale (LC_ALL, 0)); @@ -2165,7 +2127,7 @@ public: KeySym sym; { - ScopedXLock xlock; + ScopedXLock xlock (display); sym = XkbKeycodeToKeysym (display, (::KeyCode) keyEvent.keycode, 0, 0); } @@ -2238,7 +2200,7 @@ public: default: break; } - if (dragState.dragging) + if (dragState->dragging) handleExternalDragButtonReleaseEvent(); handleMouseEvent (0, getMousePos (buttonRelEvent), currentModifiers, @@ -2253,7 +2215,7 @@ public: lastMousePos = Point (movedEvent.x_root, movedEvent.y_root); - if (dragState.dragging) + if (dragState->dragging) handleExternalDragMotionNotify(); handleMouseEvent (0, getMousePos (movedEvent), currentModifiers, @@ -2307,7 +2269,7 @@ public: { // Batch together all pending expose events XEvent nextEvent; - ScopedXLock xlock; + ScopedXLock xlock (display); // if we have opengl contexts then just repaint them all // regardless if this is really necessary @@ -2369,7 +2331,7 @@ public: unsigned int numChildren; { - ScopedXLock xlock; + ScopedXLock xlock (display); XQueryTree (display, windowH, &wRoot, &parentWindow, &wChild, &numChildren); } @@ -2391,7 +2353,7 @@ public: if (mappingEvent.request != MappingPointer) { // Deal with modifier/keyboard mapping - ScopedXLock xlock; + ScopedXLock xlock (display); XRefreshKeyboardMapping (&mappingEvent); updateModifierMappings(); } @@ -2399,11 +2361,11 @@ public: void handleClientMessageEvent (XClientMessageEvent& clientMsg, XEvent& event) { - if (clientMsg.message_type == atoms.protocols && clientMsg.format == 32) + if (clientMsg.message_type == atoms->protocols && clientMsg.format == 32) { const Atom atom = (Atom) clientMsg.data.l[0]; - if (atom == atoms.protocolList [Atoms::PING]) + if (atom == atoms->protocolList [Atoms::PING]) { Window root = RootWindow (display, DefaultScreen (display)); @@ -2412,13 +2374,13 @@ public: XSendEvent (display, root, False, NoEventMask, &event); XFlush (display); } - else if (atom == atoms.protocolList [Atoms::TAKE_FOCUS]) + else if (atom == atoms->protocolList [Atoms::TAKE_FOCUS]) { if ((getStyleFlags() & juce::ComponentPeer::windowIgnoresKeyPresses) == 0) { XWindowAttributes atts; - ScopedXLock xlock; + ScopedXLock xlock (display); if (clientMsg.window != 0 && XGetWindowAttributes (display, clientMsg.window, &atts)) { @@ -2427,33 +2389,33 @@ public: } } } - else if (atom == atoms.protocolList [Atoms::DELETE_WINDOW]) + else if (atom == atoms->protocolList [Atoms::DELETE_WINDOW]) { handleUserClosingWindow(); } } - else if (clientMsg.message_type == atoms.XdndEnter) + else if (clientMsg.message_type == atoms->XdndEnter) { handleDragAndDropEnter (clientMsg); } - else if (clientMsg.message_type == atoms.XdndLeave) + else if (clientMsg.message_type == atoms->XdndLeave) { handleDragExit (dragInfo); resetDragAndDrop(); } - else if (clientMsg.message_type == atoms.XdndPosition) + else if (clientMsg.message_type == atoms->XdndPosition) { handleDragAndDropPosition (clientMsg); } - else if (clientMsg.message_type == atoms.XdndDrop) + else if (clientMsg.message_type == atoms->XdndDrop) { handleDragAndDropDrop (clientMsg); } - else if (clientMsg.message_type == atoms.XdndStatus) + else if (clientMsg.message_type == atoms->XdndStatus) { handleExternalDragAndDropStatus (clientMsg); } - else if (clientMsg.message_type == atoms.XdndFinished) + else if (clientMsg.message_type == atoms->XdndFinished) { externalResetDragAndDrop(); } @@ -2461,7 +2423,7 @@ public: bool externalDragTextInit (const String& text) { - if (dragState.dragging) + if (dragState->dragging) return false; return externalDragInit (true, text); @@ -2469,7 +2431,7 @@ public: bool externalDragFileInit (const StringArray& files, bool /*canMoveFiles*/) { - if (dragState.dragging) + if (dragState->dragging) return false; StringArray uriList; @@ -2490,7 +2452,7 @@ public: //============================================================================== void showMouseCursor (Cursor cursor) noexcept { - ScopedXLock xlock; + ScopedXLock xlock (display); XDefineCursor (display, windowH, cursor); } @@ -2533,17 +2495,18 @@ private: class LinuxRepaintManager : public Timer { public: - LinuxRepaintManager (LinuxComponentPeer& p) - : peer (p), lastTimeImageUsed (0) + LinuxRepaintManager (LinuxComponentPeer& p, ::Display* _display) + : peer (p), lastTimeImageUsed (0), + display (_display) { #if JUCE_USE_XSHM shmPaintsPending = 0; - useARGBImagesForRendering = XSHMHelpers::isShmAvailable(); + useARGBImagesForRendering = XSHMHelpers::isShmAvailable (display); if (useARGBImagesForRendering) { - ScopedXLock xlock; + ScopedXLock xlock (display); XShmSegmentInfo segmentinfo; XImage* const testImage @@ -2603,10 +2566,10 @@ private: || image.getHeight() < totalArea.getHeight()) { #if JUCE_USE_XSHM - image = Image (new XBitmapImage (useARGBImagesForRendering ? Image::ARGB + image = Image (new XBitmapImage (display, useARGBImagesForRendering ? Image::ARGB : Image::RGB, #else - image = Image (new XBitmapImage (Image::RGB, + image = Image (new XBitmapImage (display, Image::RGB, #endif (totalArea.getWidth() + 31) & ~31, (totalArea.getHeight() + 31) & ~31, @@ -2661,6 +2624,7 @@ private: Image image; uint32 lastTimeImageUsed; RectangleList regionsNeedingRepaint; + ::Display* display; #if JUCE_USE_XSHM bool useARGBImagesForRendering; @@ -2669,7 +2633,7 @@ private: JUCE_DECLARE_NON_COPYABLE (LinuxRepaintManager) }; - const Atoms atoms; + ScopedPointer atoms; ScopedPointer repainter; friend class LinuxRepaintManager; @@ -2684,6 +2648,7 @@ private: double currentScaleFactor; Array glRepaintListeners; enum { KeyPressEventType = 2 }; + static ::Display* display; struct MotifWmHints { @@ -2765,7 +2730,7 @@ private: // modifier constants: check what they're mapped to static void updateModifierMappings() noexcept { - ScopedXLock xlock; + ScopedXLock xlock (display); const int altLeftCode = XKeysymToKeycode (display, XK_Alt_L); const int numLockCode = XKeysymToKeycode (display, XK_Num_Lock); @@ -2774,18 +2739,12 @@ private: if (XModifierKeymap* const mapping = XGetModifierMapping (display)) { - const int numMods = 8; - const int maxKeysPerMod = mapping->max_keypermod; - - for (int i = 0; i < numMods; i++) + for (int i = 0; i < 8; i++) { - for (int j = 0; j < maxKeysPerMod; ++j) - { - const int index = (i * maxKeysPerMod) + j; - - if (mapping->modifiermap[index] == altLeftCode) Keys::AltMask = 1 << i; - else if (mapping->modifiermap[index] == numLockCode) Keys::NumLockMask = 1 << i; - } + if (mapping->modifiermap [i << 1] == altLeftCode) + Keys::AltMask = 1 << i; + else if (mapping->modifiermap [i << 1] == numLockCode) + Keys::NumLockMask = 1 << i; } XFreeModifiermap (mapping); @@ -2800,7 +2759,7 @@ private: void removeWindowDecorations (Window wndH) { - Atom hints = Atoms::getIfExists ("_MOTIF_WM_HINTS"); + Atom hints = Atoms::getIfExists (display, "_MOTIF_WM_HINTS"); if (hints != None) { @@ -2810,43 +2769,43 @@ private: motifHints.flags = 2; /* MWM_HINTS_DECORATIONS */ motifHints.decorations = 0; - ScopedXLock xlock; + ScopedXLock xlock (display); xchangeProperty (wndH, hints, hints, 32, &motifHints, 4); } - hints = Atoms::getIfExists ("_WIN_HINTS"); + hints = Atoms::getIfExists (display, "_WIN_HINTS"); if (hints != None) { long gnomeHints = 0; - ScopedXLock xlock; + ScopedXLock xlock (display); xchangeProperty (wndH, hints, hints, 32, &gnomeHints, 1); } - hints = Atoms::getIfExists ("KWM_WIN_DECORATION"); + hints = Atoms::getIfExists (display, "KWM_WIN_DECORATION"); if (hints != None) { long kwmHints = 2; /*KDE_tinyDecoration*/ - ScopedXLock xlock; + ScopedXLock xlock (display); xchangeProperty (wndH, hints, hints, 32, &kwmHints, 1); } - hints = Atoms::getIfExists ("_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"); + hints = Atoms::getIfExists (display, "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"); if (hints != None) { - ScopedXLock xlock; - xchangeProperty (wndH, atoms.windowType, XA_ATOM, 32, &hints, 1); + ScopedXLock xlock (display); + xchangeProperty (wndH, atoms->windowType, XA_ATOM, 32, &hints, 1); } } void addWindowButtons (Window wndH) { - ScopedXLock xlock; - Atom hints = Atoms::getIfExists ("_MOTIF_WM_HINTS"); + ScopedXLock xlock (display); + Atom hints = Atoms::getIfExists (display, "_MOTIF_WM_HINTS"); if (hints != None) { @@ -2882,7 +2841,7 @@ private: xchangeProperty (wndH, hints, hints, 32, &motifHints, 5); } - hints = Atoms::getIfExists ("_NET_WM_ALLOWED_ACTIONS"); + hints = Atoms::getIfExists (display, "_NET_WM_ALLOWED_ACTIONS"); if (hints != None) { @@ -2890,16 +2849,16 @@ private: int num = 0; if ((styleFlags & windowIsResizable) != 0) - netHints [num++] = Atoms::getIfExists ("_NET_WM_ACTION_RESIZE"); + netHints [num++] = Atoms::getIfExists (display, "_NET_WM_ACTION_RESIZE"); if ((styleFlags & windowHasMaximiseButton) != 0) - netHints [num++] = Atoms::getIfExists ("_NET_WM_ACTION_FULLSCREEN"); + netHints [num++] = Atoms::getIfExists (display, "_NET_WM_ACTION_FULLSCREEN"); if ((styleFlags & windowHasMinimiseButton) != 0) - netHints [num++] = Atoms::getIfExists ("_NET_WM_ACTION_MINIMIZE"); + netHints [num++] = Atoms::getIfExists (display, "_NET_WM_ACTION_MINIMIZE"); if ((styleFlags & windowHasCloseButton) != 0) - netHints [num++] = Atoms::getIfExists ("_NET_WM_ACTION_CLOSE"); + netHints [num++] = Atoms::getIfExists (display, "_NET_WM_ACTION_CLOSE"); xchangeProperty (wndH, hints, XA_ATOM, 32, &netHints, num); } @@ -2911,27 +2870,27 @@ private: if ((styleFlags & windowIsTemporary) != 0 || ((styleFlags & windowHasDropShadow) == 0 && Desktop::canUseSemiTransparentWindows())) - netHints [0] = Atoms::getIfExists ("_NET_WM_WINDOW_TYPE_COMBO"); + netHints [0] = Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_COMBO"); else - netHints [0] = Atoms::getIfExists ("_NET_WM_WINDOW_TYPE_NORMAL"); + netHints [0] = Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_NORMAL"); - xchangeProperty (windowH, atoms.windowType, XA_ATOM, 32, &netHints, 1); + xchangeProperty (windowH, atoms->windowType, XA_ATOM, 32, &netHints, 1); int numHints = 0; if ((styleFlags & windowAppearsOnTaskbar) == 0) - netHints [numHints++] = Atoms::getIfExists ("_NET_WM_STATE_SKIP_TASKBAR"); + netHints [numHints++] = Atoms::getIfExists (display, "_NET_WM_STATE_SKIP_TASKBAR"); if (component.isAlwaysOnTop()) - netHints [numHints++] = Atoms::getIfExists ("_NET_WM_STATE_ABOVE"); + netHints [numHints++] = Atoms::getIfExists (display, "_NET_WM_STATE_ABOVE"); if (numHints > 0) - xchangeProperty (windowH, atoms.windowState, XA_ATOM, 32, &netHints, numHints); + xchangeProperty (windowH, atoms->windowState, XA_ATOM, 32, &netHints, numHints); } void createWindow (Window parentToAddTo) { - ScopedXLock xlock; + ScopedXLock xlock (display); resetDragAndDrop(); // Get defaults for various properties @@ -2941,7 +2900,7 @@ private: parentWindow = parentToAddTo; // Try to obtain a 32-bit visual or fallback to 24 or 16 - visual = Visuals::findVisualFormat ((styleFlags & windowIsSemiTransparent) ? 32 : 24, depth); + visual = Visuals::findVisualFormat (display, (styleFlags & windowIsSemiTransparent) ? 32 : 24, depth); if (visual == nullptr) { @@ -3004,16 +2963,16 @@ private: // Associate the PID, allowing to be shut down when something goes wrong unsigned long pid = (unsigned long) getpid(); - xchangeProperty (windowH, atoms.pid, XA_CARDINAL, 32, &pid, 1); + xchangeProperty (windowH, atoms->pid, XA_CARDINAL, 32, &pid, 1); // Set window manager protocols - xchangeProperty (windowH, atoms.protocols, XA_ATOM, 32, atoms.protocolList, 2); + xchangeProperty (windowH, atoms->protocols, XA_ATOM, 32, atoms->protocolList, 2); // Set drag and drop flags - xchangeProperty (windowH, atoms.XdndTypeList, XA_ATOM, 32, atoms.allowedMimeTypes, numElementsInArray (atoms.allowedMimeTypes)); - xchangeProperty (windowH, atoms.XdndActionList, XA_ATOM, 32, atoms.allowedActions, numElementsInArray (atoms.allowedActions)); - xchangeProperty (windowH, atoms.XdndActionDescription, XA_STRING, 8, "", 0); - xchangeProperty (windowH, atoms.XdndAware, XA_ATOM, 32, &Atoms::DndVersion, 1); + xchangeProperty (windowH, atoms->XdndTypeList, XA_ATOM, 32, atoms->allowedMimeTypes, numElementsInArray (atoms->allowedMimeTypes)); + xchangeProperty (windowH, atoms->XdndActionList, XA_ATOM, 32, atoms->allowedActions, numElementsInArray (atoms->allowedActions)); + xchangeProperty (windowH, atoms->XdndActionDescription, XA_STRING, 8, "", 0); + xchangeProperty (windowH, atoms->XdndAware, XA_ATOM, 32, &atoms->DndVersion, 1); initialisePointerMap(); updateModifierMappings(); @@ -3021,7 +2980,7 @@ private: void destroyWindow() { - ScopedXLock xlock; + ScopedXLock xlock (display); XPointer handlePointer; if (! XFindContext (display, (XID) windowH, windowHandleXContext, &handlePointer)) @@ -3043,7 +3002,7 @@ private: return NoEventMask | KeyPressMask | KeyReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | KeymapStateMask | ExposureMask | StructureNotifyMask | FocusChangeMask - | ((styleFlags & windowIgnoresMouseClicks) != 0 ? 0 : (ButtonPressMask | ButtonReleaseMask)); + | ((styleFlags & windowIgnoresMouseClicks) != 0 ? 0 : (ButtonPressMask | ButtonReleaseMask)); } template @@ -3065,7 +3024,7 @@ private: long getUserTime() const { - GetXProperty prop (windowH, atoms.userTime, 0, 65536, false, XA_CARDINAL); + GetXProperty prop (display, windowH, atoms->userTime, 0, 65536, false, XA_CARDINAL); return prop.success ? *(long*) prop.data : 0; } @@ -3077,12 +3036,12 @@ private: } else if (windowBorder.getTopAndBottom() == 0 && windowBorder.getLeftAndRight() == 0) { - ScopedXLock xlock; - Atom hints = Atoms::getIfExists ("_NET_FRAME_EXTENTS"); + ScopedXLock xlock (display); + Atom hints = Atoms::getIfExists (display, "_NET_FRAME_EXTENTS"); if (hints != None) { - GetXProperty prop (windowH, hints, 0, 4, false, XA_CARDINAL); + GetXProperty prop (display, windowH, hints, 0, 4, false, XA_CARDINAL); if (prop.success && prop.actualFormat == 32) { @@ -3104,7 +3063,7 @@ private: int wx = 0, wy = 0; unsigned int ww = 0, wh = 0, bw, bitDepth; - ScopedXLock xlock; + ScopedXLock xlock (display); if (XGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth)) if (! XTranslateCoordinates (display, windowH, root, 0, 0, &wx, &wy, &child)) @@ -3122,14 +3081,14 @@ private: //============================================================================== struct DragState { - DragState() + DragState(::Display* _display) : isText (false), dragging (false), expectingStatus (false), canDrop (false), targetWindow (None), xdndVersion (-1) { if (isText) - allowedTypes.add (Atoms::getCreating ("text/plain")); + allowedTypes.add (Atoms::getCreating (_display, "text/plain")); else - allowedTypes.add (Atoms::getCreating ("text/uri-list")); + allowedTypes.add (Atoms::getCreating (_display, "text/uri-list")); } bool isText; @@ -3156,7 +3115,7 @@ private: void resetExternalDragState() { - dragState = DragState(); + dragState = new DragState (display); } void sendDragAndDropMessage (XClientMessageEvent& msg) @@ -3167,7 +3126,7 @@ private: msg.format = 32; msg.data.l[0] = (long) windowH; - ScopedXLock xlock; + ScopedXLock xlock (display); XSendEvent (display, dragAndDropSourceWindow, False, 0, (XEvent*) &msg); } @@ -3179,7 +3138,7 @@ private: msg.format = 32; msg.data.l[0] = (long) windowH; - ScopedXLock xlock; + ScopedXLock xlock (display); return XSendEvent (display, targetWindow, False, 0, (XEvent*) &msg) != 0; } @@ -3188,7 +3147,7 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = atoms.XdndDrop; + msg.message_type = atoms->XdndDrop; msg.data.l[2] = CurrentTime; sendExternalDragAndDropMessage (msg, targetWindow); @@ -3199,11 +3158,11 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = atoms.XdndEnter; - msg.data.l[1] = (dragState.xdndVersion << 24); + msg.message_type = atoms->XdndEnter; + msg.data.l[1] = (dragState->xdndVersion << 24); for (int i = 0; i < 3; ++i) - msg.data.l[i + 2] = (long) dragState.allowedTypes[i]; + msg.data.l[i + 2] = (long) dragState->allowedTypes[i]; sendExternalDragAndDropMessage (msg, targetWindow); } @@ -3213,20 +3172,20 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = atoms.XdndPosition; + msg.message_type = atoms->XdndPosition; Point mousePos (Desktop::getInstance().getMousePosition()); - if (dragState.silentRect.contains (mousePos)) // we've been asked to keep silent + if (dragState->silentRect.contains (mousePos)) // we've been asked to keep silent return; mousePos = DisplayGeometry::scaledToPhysical (mousePos); msg.data.l[1] = 0; msg.data.l[2] = (mousePos.x << 16) | mousePos.y; msg.data.l[3] = CurrentTime; - msg.data.l[4] = (long) atoms.XdndActionCopy; // this is all JUCE currently supports + msg.data.l[4] = (long) atoms->XdndActionCopy; // this is all JUCE currently supports - dragState.expectingStatus = sendExternalDragAndDropMessage (msg, targetWindow); + dragState->expectingStatus = sendExternalDragAndDropMessage (msg, targetWindow); } void sendDragAndDropStatus (const bool acceptDrop, Atom dropAction) @@ -3234,7 +3193,7 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = atoms.XdndStatus; + msg.message_type = atoms->XdndStatus; msg.data.l[1] = (acceptDrop ? 1 : 0) | 2; // 2 indicates that we want to receive position messages msg.data.l[4] = (long) dropAction; @@ -3246,7 +3205,7 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = atoms.XdndLeave; + msg.message_type = atoms->XdndLeave; sendExternalDragAndDropMessage (msg, targetWindow); } @@ -3255,13 +3214,13 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = atoms.XdndFinished; + msg.message_type = atoms->XdndFinished; sendDragAndDropMessage (msg); } void handleExternalSelectionClear() { - if (dragState.dragging) + if (dragState->dragging) externalResetDragAndDrop(); } @@ -3277,15 +3236,15 @@ private: s.xselection.property = None; s.xselection.time = evt.xselectionrequest.time; - if (dragState.allowedTypes.contains (targetType)) + if (dragState->allowedTypes.contains (targetType)) { s.xselection.property = evt.xselectionrequest.property; xchangeProperty (evt.xselectionrequest.requestor, evt.xselectionrequest.property, targetType, 8, - dragState.textOrFiles.toRawUTF8(), - (int) dragState.textOrFiles.getNumBytesAsUTF8()); + dragState->textOrFiles.toRawUTF8(), + (int) dragState->textOrFiles.getNumBytesAsUTF8()); } XSendEvent (display, evt.xselectionrequest.requestor, True, 0, &s); @@ -3293,39 +3252,39 @@ private: void handleExternalDragAndDropStatus (const XClientMessageEvent& clientMsg) { - if (dragState.expectingStatus) + if (dragState->expectingStatus) { - dragState.expectingStatus = false; - dragState.canDrop = false; - dragState.silentRect = Rectangle(); + dragState->expectingStatus = false; + dragState->canDrop = false; + dragState->silentRect = Rectangle(); if ((clientMsg.data.l[1] & 1) != 0 - && ((Atom) clientMsg.data.l[4] == atoms.XdndActionCopy - || (Atom) clientMsg.data.l[4] == atoms.XdndActionPrivate)) + && ((Atom) clientMsg.data.l[4] == atoms->XdndActionCopy + || (Atom) clientMsg.data.l[4] == atoms->XdndActionPrivate)) { if ((clientMsg.data.l[1] & 2) == 0) // target requests silent rectangle - dragState.silentRect.setBounds ((int) clientMsg.data.l[2] >> 16, + dragState->silentRect.setBounds ((int) clientMsg.data.l[2] >> 16, (int) clientMsg.data.l[2] & 0xffff, (int) clientMsg.data.l[3] >> 16, (int) clientMsg.data.l[3] & 0xffff); - dragState.canDrop = true; + dragState->canDrop = true; } } } void handleExternalDragButtonReleaseEvent() { - if (dragState.dragging) + if (dragState->dragging) XUngrabPointer (display, CurrentTime); - if (dragState.canDrop) + if (dragState->canDrop) { - sendExternalDragAndDropDrop (dragState.targetWindow); + sendExternalDragAndDropDrop (dragState->targetWindow); } else { - sendExternalDragAndDropLeave (dragState.targetWindow); + sendExternalDragAndDropLeave (dragState->targetWindow); externalResetDragAndDrop(); } } @@ -3334,18 +3293,18 @@ private: { Window targetWindow = externalFindDragTargetWindow (RootWindow (display, DefaultScreen (display))); - if (dragState.targetWindow != targetWindow) + if (dragState->targetWindow != targetWindow) { - if (dragState.targetWindow != None) - sendExternalDragAndDropLeave (dragState.targetWindow); + if (dragState->targetWindow != None) + sendExternalDragAndDropLeave (dragState->targetWindow); - dragState.canDrop = false; - dragState.silentRect = Rectangle(); + dragState->canDrop = false; + dragState->silentRect = Rectangle(); if (targetWindow == None) return; - GetXProperty prop (targetWindow, atoms.XdndAware, + GetXProperty prop (display, targetWindow, atoms->XdndAware, 0, 2, false, AnyPropertyType); if (prop.success @@ -3353,19 +3312,19 @@ private: && prop.actualFormat == 32 && prop.numItems == 1) { - dragState.xdndVersion = jmin ((int) prop.data[0], (int) Atoms::DndVersion); + dragState->xdndVersion = jmin ((int) prop.data[0], (int) atoms->DndVersion); } else { - dragState.xdndVersion = -1; + dragState->xdndVersion = -1; return; } sendExternalDragAndDropEnter (targetWindow); - dragState.targetWindow = targetWindow; + dragState->targetWindow = targetWindow; } - if (! dragState.expectingStatus) + if (! dragState->expectingStatus) sendExternalDragAndDropPosition (targetWindow); } @@ -3380,13 +3339,13 @@ private: (int) clientMsg.data.l[2] & 0xffff); dropPos -= bounds.getPosition(); - Atom targetAction = atoms.XdndActionCopy; + Atom targetAction = atoms->XdndActionCopy; - for (int i = numElementsInArray (atoms.allowedActions); --i >= 0;) + for (int i = numElementsInArray (atoms->allowedActions); --i >= 0;) { - if ((Atom) clientMsg.data.l[4] == atoms.allowedActions[i]) + if ((Atom) clientMsg.data.l[4] == atoms->allowedActions[i]) { - targetAction = atoms.allowedActions[i]; + targetAction = atoms->allowedActions[i]; break; } } @@ -3448,8 +3407,8 @@ private: if ((clientMsg.data.l[1] & 1) != 0) { - ScopedXLock xlock; - GetXProperty prop (dragAndDropSourceWindow, atoms.XdndTypeList, 0, 0x8000000L, false, XA_ATOM); + ScopedXLock xlock (display); + GetXProperty prop (display, dragAndDropSourceWindow, atoms->XdndTypeList, 0, 0x8000000L, false, XA_ATOM); if (prop.success && prop.actualType == XA_ATOM @@ -3478,9 +3437,9 @@ private: } for (int i = 0; i < srcMimeTypeAtomList.size() && dragAndDropCurrentMimeType == 0; ++i) - for (int j = 0; j < numElementsInArray (atoms.allowedMimeTypes); ++j) - if (srcMimeTypeAtomList[i] == atoms.allowedMimeTypes[j]) - dragAndDropCurrentMimeType = atoms.allowedMimeTypes[j]; + for (int j = 0; j < numElementsInArray (atoms->allowedMimeTypes); ++j) + if (srcMimeTypeAtomList[i] == atoms->allowedMimeTypes[j]) + dragAndDropCurrentMimeType = atoms->allowedMimeTypes[j]; handleDragAndDropPosition (clientMsg); } @@ -3498,7 +3457,7 @@ private: for (;;) { - GetXProperty prop (evt.xany.window, evt.xselection.property, + GetXProperty prop (display, evt.xany.window, evt.xselection.property, dropData.getSize() / 4, 65536, false, AnyPropertyType); if (! prop.success) @@ -3513,7 +3472,7 @@ private: lines.addLines (dropData.toString()); } - if (Atoms::isMimeTypeFile (dragAndDropCurrentMimeType)) + if (Atoms::isMimeTypeFile (display, dragAndDropCurrentMimeType)) { for (int i = 0; i < lines.size(); ++i) dragInfo.files.add (URL::removeEscapeChars (lines[i].replace ("file://", String(), true))); @@ -3538,11 +3497,11 @@ private: if (dragAndDropSourceWindow != None && dragAndDropCurrentMimeType != None) { - ScopedXLock xlock; + ScopedXLock xlock (display); XConvertSelection (display, - atoms.XdndSelection, + atoms->XdndSelection, dragAndDropCurrentMimeType, - Atoms::getCreating ("JXSelectionWindowProperty"), + Atoms::getCreating (display, "JXSelectionWindowProperty"), windowH, (::Time) clientMsg.data.l[2]); } @@ -3555,7 +3514,7 @@ private: bool dndAwarePropFound = false; for (int i = 0; i < numProperties; ++i) - if (properties[i] == atoms.XdndAware) + if (properties[i] == atoms->XdndAware) dndAwarePropFound = true; if (properties != nullptr) @@ -3584,12 +3543,12 @@ private: bool externalDragInit (bool isText, const String& textOrFiles) { - ScopedXLock xlock; + ScopedXLock xlock (display); resetExternalDragState(); - dragState.isText = isText; - dragState.textOrFiles = textOrFiles; - dragState.targetWindow = windowH; + dragState->isText = isText; + dragState->textOrFiles = textOrFiles; + dragState->targetWindow = windowH; const int pointerGrabMask = Button1MotionMask | ButtonReleaseMask; @@ -3599,14 +3558,14 @@ private: // No other method of changing the pointer seems to work, this call is needed from this very context XChangeActivePointerGrab (display, pointerGrabMask, (Cursor) createDraggingHandCursor(), CurrentTime); - XSetSelectionOwner (display, atoms.XdndSelection, windowH, CurrentTime); + XSetSelectionOwner (display, atoms->XdndSelection, windowH, CurrentTime); // save the available types to XdndTypeList - xchangeProperty (windowH, atoms.XdndTypeList, XA_ATOM, 32, - dragState.allowedTypes.getRawDataPointer(), - dragState.allowedTypes.size()); + xchangeProperty (windowH, atoms->XdndTypeList, XA_ATOM, 32, + dragState->allowedTypes.getRawDataPointer(), + dragState->allowedTypes.size()); - dragState.dragging = true; + dragState->dragging = true; handleExternalDragMotionNotify(); return true; } @@ -3616,16 +3575,16 @@ private: void externalResetDragAndDrop() { - if (dragState.dragging) + if (dragState->dragging) { - ScopedXLock xlock; + ScopedXLock xlock (display); XUngrabPointer (display, CurrentTime); } resetExternalDragState(); } - DragState dragState; + ScopedPointer dragState; DragInfo dragInfo; Atom dragAndDropCurrentMimeType; Window dragAndDropSourceWindow; @@ -3672,6 +3631,34 @@ private: ModifierKeys LinuxComponentPeer::currentModifiers; bool LinuxComponentPeer::isActiveApplication = false; Point LinuxComponentPeer::lastMousePos; +::Display* LinuxComponentPeer::display = nullptr; + +//============================================================================== +namespace WindowingHelpers { + static void windowMessageReceive (XEvent& event) + { + if (event.xany.window != None) + { + if (LinuxComponentPeer* const peer = LinuxComponentPeer::getPeerFor (event.xany.window)) + peer->handleWindowMessage (event); + } + else if (event.xany.type == KeymapNotify) + { + const XKeymapEvent& keymapEvent = (const XKeymapEvent&) event.xkeymap; + memcpy (Keys::keyStates, keymapEvent.key_vector, 32); + } + } +} + +struct WindowingCallbackInitialiser +{ + WindowingCallbackInitialiser() + { + dispatchWindowMessage = WindowingHelpers::windowMessageReceive; + } +}; + +static WindowingCallbackInitialiser windowingInitialiser; //============================================================================== JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() @@ -3691,6 +3678,9 @@ void ModifierKeys::updateCurrentModifiers() noexcept ModifierKeys ModifierKeys::getCurrentModifiersRealtime() noexcept { + ScopedXDisplay xDisplay; + ::Display* display = xDisplay.get(); + if (display != nullptr) { Window root, child; @@ -3698,7 +3688,7 @@ ModifierKeys ModifierKeys::getCurrentModifiersRealtime() noexcept unsigned int mask; int mouseMods = 0; - ScopedXLock xlock; + ScopedXLock xlock (display); if (XQueryPointer (display, RootWindow (display, DefaultScreen (display)), &root, &child, &x, &y, &winx, &winy, &mask) != False) @@ -3733,6 +3723,9 @@ ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAtt //============================================================================== void Desktop::Displays::findDisplays (float masterScale) { + ScopedXDisplay xDisplay; + ::Display* display = xDisplay.get(); + DisplayGeometry& geometry = DisplayGeometry::getOrCreateInstance (display, masterScale); // add the main display first @@ -3803,7 +3796,7 @@ bool Desktop::canUseSemiTransparentWindows() noexcept { int matchedDepth = 0, desiredDepth = 32; - return Visuals::findVisualFormat (desiredDepth, matchedDepth) != 0 + return Visuals::findVisualFormat (display, desiredDepth, matchedDepth) != 0 && matchedDepth == desiredDepth; } #endif @@ -3813,6 +3806,9 @@ bool Desktop::canUseSemiTransparentWindows() noexcept Point MouseInputSource::getCurrentRawMousePosition() { + ScopedXDisplay xDisplay; + ::Display* display = xDisplay.get(); + if (display == nullptr) return Point(); @@ -3820,7 +3816,7 @@ Point MouseInputSource::getCurrentRawMousePosition() int x, y, winx, winy; unsigned int mask; - ScopedXLock xlock; + ScopedXLock xlock (display); if (XQueryPointer (display, RootWindow (display, DefaultScreen (display)), @@ -3836,9 +3832,12 @@ Point MouseInputSource::getCurrentRawMousePosition() void MouseInputSource::setRawMousePosition (Point newPosition) { + ScopedXDisplay xDisplay; + ::Display* display = xDisplay.get(); + if (display != nullptr) { - ScopedXLock xlock; + ScopedXLock xlock (display); Window root = RootWindow (display, DefaultScreen (display)); newPosition = DisplayGeometry::scaledToPhysical (newPosition); XWarpPointer (display, None, root, 0, 0, 0, 0, roundToInt (newPosition.getX()), roundToInt (newPosition.getY())); @@ -3864,6 +3863,9 @@ void Desktop::setScreenSaverEnabled (const bool isEnabled) { screenSaverAllowed = isEnabled; + ScopedXDisplay xDisplay; + ::Display* display = xDisplay.get(); + if (display != nullptr) { typedef void (*tXScreenSaverSuspend) (Display*, Bool); @@ -3873,7 +3875,7 @@ void Desktop::setScreenSaverEnabled (const bool isEnabled) if (void* h = dlopen ("libXss.so", RTLD_GLOBAL | RTLD_NOW)) xScreenSaverSuspend = (tXScreenSaverSuspend) dlsym (h, "XScreenSaverSuspend"); - ScopedXLock xlock; + ScopedXLock xlock (display); if (xScreenSaverSuspend != nullptr) xScreenSaverSuspend (display, ! isEnabled); } @@ -3886,12 +3888,87 @@ bool Desktop::isScreenSaverEnabled() } //============================================================================== +Image juce_createIconForFile (const File& /* file */) +{ + return Image(); +} + +//============================================================================== +void LookAndFeel::playAlertSound() +{ + std::cout << "\a" << std::flush; +} +//============================================================================== +Rectangle juce_LinuxScaledToPhysicalBounds (ComponentPeer* peer, const Rectangle& bounds) +{ + Rectangle retval = bounds; + + if (LinuxComponentPeer* linuxPeer = dynamic_cast (peer)) + retval *= linuxPeer->getCurrentScale(); + + return retval; +} + +void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy) +{ + if (LinuxComponentPeer* linuxPeer = dynamic_cast (peer)) + linuxPeer->addOpenGLRepaintListener (dummy); +} + +void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy) +{ + if (LinuxComponentPeer* linuxPeer = dynamic_cast (peer)) + linuxPeer->removeOpenGLRepaintListener (dummy); +} + +//============================================================================== +#if JUCE_MODAL_LOOPS_PERMITTED +void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType, + const String& title, const String& message, + Component* /* associatedComponent */) +{ + AlertWindow::showMessageBox (iconType, title, message); +} +#endif + +void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType, + const String& title, const String& message, + Component* associatedComponent, + ModalComponentManager::Callback* callback) +{ + AlertWindow::showMessageBoxAsync (iconType, title, message, String(), associatedComponent, callback); +} + +bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType, + const String& title, const String& message, + Component* associatedComponent, + ModalComponentManager::Callback* callback) +{ + return AlertWindow::showOkCancelBox (iconType, title, message, String(), String(), + associatedComponent, callback); +} + +int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType, + const String& title, const String& message, + Component* associatedComponent, + ModalComponentManager::Callback* callback) +{ + return AlertWindow::showYesNoCancelBox (iconType, title, message, + String(), String(), String(), + associatedComponent, callback); +} + +//============================== X11 - MouseCursor ============================= + void* CustomMouseCursorInfo::create() const { + ScopedXDisplay xDisplay; + ::Display* display = xDisplay.get(); + if (display == nullptr) return nullptr; - ScopedXLock xlock; + ScopedXLock xlock (display); const unsigned int imageW = (unsigned int) image.getWidth(); const unsigned int imageH = (unsigned int) image.getHeight(); int hotspotX = hotspot.x; @@ -4013,15 +4090,21 @@ void* CustomMouseCursorInfo::create() const void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool) { + ScopedXDisplay xDisplay; + ::Display* display = xDisplay.get(); + if (cursorHandle != nullptr && display != nullptr) { - ScopedXLock xlock; + ScopedXLock xlock (display); XFreeCursor (display, (Cursor) cursorHandle); } } void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type) { + ScopedXDisplay xDisplay; + ::Display* display = xDisplay.get(); + if (display == nullptr) return None; @@ -4066,7 +4149,7 @@ void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType ty return None; } - ScopedXLock xlock; + ScopedXLock xlock (display); return (void*) XCreateFontCursor (display, shape); } @@ -4082,19 +4165,14 @@ void MouseCursor::showInAllWindows() const showInWindow (ComponentPeer::getPeer (i)); } -//============================================================================== -Image juce_createIconForFile (const File& /* file */) -{ - return Image(); -} +//=================================== X11 - DND ================================ -//============================================================================== bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) { if (files.size() == 0) return false; - if (MouseInputSource* draggingSource = Desktop::getInstance().getDraggingMouseSource(0)) + if (MouseInputSource* draggingSource = Desktop::getInstance().getDraggingMouseSource (0)) if (Component* sourceComp = draggingSource->getComponentUnderMouse()) if (LinuxComponentPeer* const lp = dynamic_cast (sourceComp->getPeer())) return lp->externalDragFileInit (files, canMoveFiles); @@ -4109,7 +4187,7 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text) if (text.isEmpty()) return false; - if (MouseInputSource* draggingSource = Desktop::getInstance().getDraggingMouseSource(0)) + if (MouseInputSource* draggingSource = Desktop::getInstance().getDraggingMouseSource (0)) if (Component* sourceComp = draggingSource->getComponentUnderMouse()) if (LinuxComponentPeer* const lp = dynamic_cast (sourceComp->getPeer())) return lp->externalDragTextInit (text); @@ -4118,124 +4196,3 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text) jassertfalse; return false; } - -//============================================================================== -void LookAndFeel::playAlertSound() -{ - std::cout << "\a" << std::flush; -} -//============================================================================== -Rectangle juce_LinuxScaledToPhysicalBounds(ComponentPeer* peer, const Rectangle& bounds) -{ - Rectangle retval = bounds; - - if (LinuxComponentPeer* linuxPeer = dynamic_cast (peer)) - retval *= linuxPeer->getCurrentScale(); - - return retval; -} - -void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy) -{ - if (LinuxComponentPeer* linuxPeer = dynamic_cast (peer)) - linuxPeer->addOpenGLRepaintListener (dummy); -} - -void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy) -{ - if (LinuxComponentPeer* linuxPeer = dynamic_cast (peer)) - linuxPeer->removeOpenGLRepaintListener (dummy); -} - -//============================================================================== -#if JUCE_MODAL_LOOPS_PERMITTED -void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType, - const String& title, const String& message, - Component* /* associatedComponent */) -{ - AlertWindow::showMessageBox (iconType, title, message); -} -#endif - -void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType, - const String& title, const String& message, - Component* associatedComponent, - ModalComponentManager::Callback* callback) -{ - AlertWindow::showMessageBoxAsync (iconType, title, message, String(), associatedComponent, callback); -} - -bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType, - const String& title, const String& message, - Component* associatedComponent, - ModalComponentManager::Callback* callback) -{ - return AlertWindow::showOkCancelBox (iconType, title, message, String(), String(), - associatedComponent, callback); -} - -int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType, - const String& title, const String& message, - Component* associatedComponent, - ModalComponentManager::Callback* callback) -{ - return AlertWindow::showYesNoCancelBox (iconType, title, message, - String(), String(), String(), - associatedComponent, callback); -} - - -//============================================================================== -const int KeyPress::spaceKey = XK_space & 0xff; -const int KeyPress::returnKey = XK_Return & 0xff; -const int KeyPress::escapeKey = XK_Escape & 0xff; -const int KeyPress::backspaceKey = XK_BackSpace & 0xff; -const int KeyPress::leftKey = (XK_Left & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::rightKey = (XK_Right & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::upKey = (XK_Up & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::downKey = (XK_Down & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::pageUpKey = (XK_Page_Up & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::pageDownKey = (XK_Page_Down & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::endKey = (XK_End & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::homeKey = (XK_Home & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::insertKey = (XK_Insert & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::deleteKey = (XK_Delete & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::tabKey = XK_Tab & 0xff; -const int KeyPress::F1Key = (XK_F1 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F2Key = (XK_F2 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F3Key = (XK_F3 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F4Key = (XK_F4 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F5Key = (XK_F5 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F6Key = (XK_F6 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F7Key = (XK_F7 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F8Key = (XK_F8 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F9Key = (XK_F9 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F10Key = (XK_F10 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F11Key = (XK_F11 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F12Key = (XK_F12 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F13Key = (XK_F13 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F14Key = (XK_F14 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F15Key = (XK_F15 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::F16Key = (XK_F16 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::numberPad0 = (XK_KP_0 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::numberPad1 = (XK_KP_1 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::numberPad2 = (XK_KP_2 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::numberPad3 = (XK_KP_3 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::numberPad4 = (XK_KP_4 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::numberPad5 = (XK_KP_5 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::numberPad6 = (XK_KP_6 & 0xff) | Keys::extendedKeyModifier; -const int KeyPress::numberPad7 = (XK_KP_7 & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::numberPad8 = (XK_KP_8 & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::numberPad9 = (XK_KP_9 & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::numberPadAdd = (XK_KP_Add & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::numberPadSubtract = (XK_KP_Subtract & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::numberPadMultiply = (XK_KP_Multiply & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::numberPadDivide = (XK_KP_Divide & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::numberPadSeparator = (XK_KP_Separator & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::numberPadDecimalPoint = (XK_KP_Decimal & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::numberPadEquals = (XK_KP_Equal & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::numberPadDelete = (XK_KP_Delete & 0xff)| Keys::extendedKeyModifier; -const int KeyPress::playKey = ((int) 0xffeeff00) | Keys::extendedKeyModifier; -const int KeyPress::stopKey = ((int) 0xffeeff01) | Keys::extendedKeyModifier; -const int KeyPress::fastForwardKey = ((int) 0xffeeff02) | Keys::extendedKeyModifier; -const int KeyPress::rewindKey = ((int) 0xffeeff03) | Keys::extendedKeyModifier; diff --git a/modules/juce_gui_extra/juce_gui_extra.cpp b/modules/juce_gui_extra/juce_gui_extra.cpp index 89c7397031..ebcfed767f 100644 --- a/modules/juce_gui_extra/juce_gui_extra.cpp +++ b/modules/juce_gui_extra/juce_gui_extra.cpp @@ -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 diff --git a/modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp b/modules/juce_gui_extra/native/juce_linux_X11_SystemTrayIcon.cpp similarity index 87% rename from modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp rename to modules/juce_gui_extra/native/juce_linux_X11_SystemTrayIcon.cpp index a5224830c8..cfe2be17a9 100644 --- a/modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp +++ b/modules/juce_gui_extra/native/juce_linux_X11_SystemTrayIcon.cpp @@ -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 diff --git a/modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp b/modules/juce_gui_extra/native/juce_linux_X11_WebBrowserComponent.cpp similarity index 100% rename from modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp rename to modules/juce_gui_extra/native/juce_linux_X11_WebBrowserComponent.cpp diff --git a/modules/juce_opengl/juce_opengl.cpp b/modules/juce_opengl/juce_opengl.cpp index 1c8994fd47..ab5da84617 100644 --- a/modules/juce_opengl/juce_opengl.cpp +++ b/modules/juce_opengl/juce_opengl.cpp @@ -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" diff --git a/modules/juce_opengl/native/juce_OpenGL_linux.h b/modules/juce_opengl/native/juce_OpenGL_linux_X11.h similarity index 91% rename from modules/juce_opengl/native/juce_OpenGL_linux.h rename to modules/juce_opengl/native/juce_OpenGL_linux_X11.h index 34684dc3c4..a597149fe3 100644 --- a/modules/juce_opengl/native/juce_OpenGL_linux.h +++ b/modules/juce_opengl/native/juce_OpenGL_linux_X11.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 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; }