Skip to content

Commit

Permalink
LinuxComponentPeer: Dismiss modals more proactively
Browse files Browse the repository at this point in the history
This patch should resolve an issue where the popupmenu for a combobox
could become 'stranded' if the plugin window was moved while the box was
open.
  • Loading branch information
reuk committed Jan 15, 2021
1 parent 838d555 commit 5490003
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 67 deletions.
12 changes: 8 additions & 4 deletions modules/juce_gui_basics/native/juce_linux_Windowing.cpp
Expand Up @@ -38,7 +38,7 @@ class LinuxComponentPeer : public ComponentPeer
: ComponentPeer (comp, windowStyleFlags),
isAlwaysOnTop (comp.isAlwaysOnTop())
{
// it's dangerous to create a window on a thread other than the message thread..
// it's dangerous to create a window on a thread other than the message thread.
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED

if (isAlwaysOnTop)
Expand All @@ -56,7 +56,7 @@ class LinuxComponentPeer : public ComponentPeer

~LinuxComponentPeer() override
{
// it's dangerous to delete a window on a thread other than the message thread..
// it's dangerous to delete a window on a thread other than the message thread.
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED

repainter = nullptr;
Expand All @@ -66,10 +66,15 @@ class LinuxComponentPeer : public ComponentPeer
--numAlwaysOnTopPeers;
}

::Window getWindowHandle() const noexcept
{
return windowH;
}

//==============================================================================
void* getNativeHandle() const override
{
return (void*) windowH;
return reinterpret_cast<void*> (getWindowHandle());
}

//==============================================================================
Expand Down Expand Up @@ -443,7 +448,6 @@ class LinuxComponentPeer : public ComponentPeer
scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); });
}
}

}

//==============================================================================
Expand Down
105 changes: 43 additions & 62 deletions modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
Expand Up @@ -163,12 +163,6 @@ XWindowSystemUtilities::GetXProperty::~GetXProperty()
}

//==============================================================================
using WindowMessageReceiveCallback = void (*) (XEvent&);
using SelectionRequestCallback = void (*) (XSelectionRequestEvent&);

static WindowMessageReceiveCallback dispatchWindowMessage = nullptr;
SelectionRequestCallback handleSelectionRequest = nullptr;

::Window juce_messageWindowHandle;
XContext windowHandleXContext;

Expand Down Expand Up @@ -1205,20 +1199,6 @@ namespace ClipboardHelpers
}
}

//==============================================================================
typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&);
extern SelectionRequestCallback handleSelectionRequest;

struct ClipboardCallbackInitialiser
{
ClipboardCallbackInitialiser()
{
handleSelectionRequest = ClipboardHelpers::handleSelection;
}
};

static ClipboardCallbackInitialiser clipboardInitialiser;

//==============================================================================
ComponentPeer* getPeerFor (::Window windowH)
{
Expand Down Expand Up @@ -2806,13 +2786,14 @@ bool XWindowSystem::initialiseXDisplay()
// Create a context to store user data associated with Windows we create
windowHandleXContext = (XContext) X11Symbols::getInstance()->xrmUniqueQuark();

// 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)
auto screen = X11Symbols::getInstance()->xDefaultScreen (display);
auto root = X11Symbols::getInstance()->xRootWindow (display, screen);
X11Symbols::getInstance()->xSelectInput (display, root, SubstructureNotifyMask);

// We're only interested in client messages for this window, which are always sent
XSetWindowAttributes swa;
swa.event_mask = NoEventMask;
juce_messageWindowHandle = X11Symbols::getInstance()->xCreateWindow (display, root,
0, 0, 1, 1, 0, 0, InputOnly,
X11Symbols::getInstance()->xDefaultVisual (display, screen),
Expand Down Expand Up @@ -2855,15 +2836,13 @@ bool XWindowSystem::initialiseXDisplay()
X11Symbols::getInstance()->xNextEvent (display, &evt);
}

if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle
&& handleSelectionRequest != nullptr)
if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle)
{
handleSelectionRequest (evt.xselectionrequest);
ClipboardHelpers::handleSelection (evt.xselectionrequest);
}
else if (evt.xany.window != juce_messageWindowHandle
&& dispatchWindowMessage != nullptr)
else if (evt.xany.window != juce_messageWindowHandle)
{
dispatchWindowMessage (evt);
windowMessageReceive (evt);
}

} while (display != nullptr);
Expand Down Expand Up @@ -3312,19 +3291,22 @@ void XWindowSystem::handleExposeEvent (LinuxComponentPeer* peer, XExposeEvent& e
}
}

void XWindowSystem::dismissBlockingModals (LinuxComponentPeer* peer) const
{
if (peer->getComponent().isCurrentlyBlockedByAnotherModalComponent())
if (auto* currentModalComp = Component::getCurrentlyModalComponent())
currentModalComp->inputAttemptWhenModal();
}

void XWindowSystem::handleConfigureNotifyEvent (LinuxComponentPeer* peer, XConfigureEvent& confEvent) const
{
peer->updateWindowBounds();
peer->updateBorderSize();
peer->handleMovedOrResized();

// if the native title bar is dragged, need to tell any active menus, etc.
if ((peer->getStyleFlags() & ComponentPeer::windowHasTitleBar) != 0
&& peer->getComponent().isCurrentlyBlockedByAnotherModalComponent())
{
if (auto* currentModalComp = Component::getCurrentlyModalComponent())
currentModalComp->inputAttemptWhenModal();
}
if ((peer->getStyleFlags() & ComponentPeer::windowHasTitleBar) != 0)
dismissBlockingModals (peer);

auto windowH = (::Window) peer->getNativeHandle();

Expand Down Expand Up @@ -3366,9 +3348,8 @@ void XWindowSystem::propertyNotifyEvent (LinuxComponentPeer* peer, const XProper
return std::find (data, end, atoms.windowStateHidden) != end;
};

if ((isStateChangeEvent() || isHidden()) && peer->getComponent().isCurrentlyBlockedByAnotherModalComponent())
if (auto* currentModalComp = Component::getCurrentlyModalComponent())
currentModalComp->inputAttemptWhenModal();
if (isStateChangeEvent() || isHidden())
dismissBlockingModals (peer);
}

void XWindowSystem::handleMappingNotify (XMappingEvent& mappingEvent) const
Expand Down Expand Up @@ -3476,39 +3457,39 @@ void XWindowSystem::handleXEmbedMessage (LinuxComponentPeer* peer, XClientMessag
}

//==============================================================================
namespace WindowingHelpers
void XWindowSystem::windowMessageReceive (XEvent& event)
{
static void windowMessageReceive (XEvent& event)
if (event.xany.window != None)
{
if (event.xany.window != None)
#if JUCE_X11_SUPPORTS_XEMBED
if (! juce_handleXEmbedEvent (nullptr, &event))
#endif
{
#if JUCE_X11_SUPPORTS_XEMBED
if (! juce_handleXEmbedEvent (nullptr, &event))
#endif
if (auto* peer = dynamic_cast<LinuxComponentPeer*> (getPeerFor (event.xany.window)))
{
if (auto* peer = dynamic_cast<LinuxComponentPeer*> (getPeerFor (event.xany.window)))
XWindowSystem::getInstance()->handleWindowMessage (peer, event);
XWindowSystem::getInstance()->handleWindowMessage (peer, event);
return;
}
}
else if (event.xany.type == KeymapNotify)
{
auto& keymapEvent = (const XKeymapEvent&) event.xkeymap;
memcpy (Keys::keyStates, keymapEvent.key_vector, 32);

if (event.type != ConfigureNotify)
return;

const auto* instance = XWindowSystem::getInstance();

for (auto i = ComponentPeer::getNumPeers(); --i >= 0;)
if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (ComponentPeer::getPeer (i)))
if (instance->isParentWindowOf (event.xconfigure.window, linuxPeer->getWindowHandle()))
instance->dismissBlockingModals (linuxPeer);
}
}
}

struct WindowingCallbackInitialiser
{
WindowingCallbackInitialiser()
else if (event.xany.type == KeymapNotify)
{
dispatchWindowMessage = WindowingHelpers::windowMessageReceive;
auto& keymapEvent = (const XKeymapEvent&) event.xkeymap;
memcpy (Keys::keyStates, keymapEvent.key_vector, 32);
}
};

static WindowingCallbackInitialiser windowingInitialiser;

}

//==============================================================================
JUCE_IMPLEMENT_SINGLETON (XWindowSystem)

} // namespace juce
Expand Up @@ -164,6 +164,7 @@ class XWindowSystem : public DeletedAtShutdown

//==============================================================================
void handleWindowMessage (LinuxComponentPeer* peer, XEvent& event) const;
bool isParentWindowOf (::Window windowH, ::Window possibleChild) const;

//==============================================================================
JUCE_DECLARE_SINGLETON (XWindowSystem, false)
Expand Down Expand Up @@ -197,7 +198,6 @@ class XWindowSystem : public DeletedAtShutdown
//==============================================================================
::Window getFocusWindow (::Window windowH) const;

bool isParentWindowOf (::Window windowH, ::Window possibleChild) const;
bool isFrontWindow (::Window windowH) const;

//==============================================================================
Expand Down Expand Up @@ -233,6 +233,10 @@ class XWindowSystem : public DeletedAtShutdown
void handleClientMessageEvent (LinuxComponentPeer*, XClientMessageEvent&, XEvent&) const;
void handleXEmbedMessage (LinuxComponentPeer*, XClientMessageEvent&) const;

void dismissBlockingModals (LinuxComponentPeer* peer) const;

static void windowMessageReceive (XEvent&);

//==============================================================================
bool xIsAvailable = false;

Expand Down

0 comments on commit 5490003

Please sign in to comment.