Skip to content

Commit

Permalink
OpenGL: Avoid dispatching events to the OpenGL component peer after i…
Browse files Browse the repository at this point in the history
…t is destroyed

The significant change here is that the XSaveContext call in the OpenGL
NativeContext now has a matching XDeleteContext call. This breaks the
connection between the X window and the ComponentPeer, so that we avoid
dereferencing a dangling ComponentPeer pointer if the window receives
events after the peer is destroyed.
  • Loading branch information
reuk committed Dec 14, 2022
1 parent e8d694d commit bac51ab
Showing 1 changed file with 103 additions and 1 deletion.
104 changes: 103 additions & 1 deletion modules/juce_opengl/native/juce_OpenGL_linux_X11.h
Expand Up @@ -45,6 +45,106 @@ std::unique_ptr<Data, XFreeDeleter> makeXFreePtr (Data* raw) { return std::uniqu
void juce_LinuxAddRepaintListener (ComponentPeer*, Component* dummy);
void juce_LinuxRemoveRepaintListener (ComponentPeer*, Component* dummy);

/* Attaches a peer to a given window, so that it can be retrieved with XFindContext on
the windowHandleXContext.
*/
class ScopedPeerAssociation
{
public:
static ScopedPeerAssociation make (ComponentPeer* peerIn, Window windowIn)
{
if (peerIn == nullptr)
return { nullptr, windowIn };

const auto display = XWindowSystem::getInstance()->getDisplay();

if (X11Symbols::getInstance()->xSaveContext (display,
static_cast<XID> (windowIn),
windowHandleXContext,
unalignedPointerCast<XPointer> (peerIn)) != 0)
{
jassertfalse;
return { nullptr, windowIn };
}

return { peerIn, windowIn };
}

ScopedPeerAssociation (const ScopedPeerAssociation&) = delete;
ScopedPeerAssociation& operator= (const ScopedPeerAssociation&) = delete;

ScopedPeerAssociation (ScopedPeerAssociation&& other) noexcept
: peer (std::exchange (other.peer, nullptr)), window (other.window) {}

ScopedPeerAssociation& operator= (ScopedPeerAssociation&& other) noexcept
{
ScopedPeerAssociation { std::move (other) }.swap (*this);
return *this;
}

~ScopedPeerAssociation() noexcept
{
if (peer == nullptr)
return;

const auto display = XWindowSystem::getInstance()->getDisplay();
XPointer ptr{};

if (X11Symbols::getInstance()->xFindContext (display, window, windowHandleXContext, &ptr) != 0)
{
jassertfalse;
return;
}

jassert (unalignedPointerCast<XPointer> (peer) == ptr);

if (X11Symbols::getInstance()->xDeleteContext (display, window, windowHandleXContext) != 0)
jassertfalse;
}

private:
ScopedPeerAssociation (ComponentPeer* componentPeer, Window embeddedWindow)
: peer (componentPeer), window (static_cast<XID> (embeddedWindow)) {}

void swap (ScopedPeerAssociation& other) noexcept
{
std::swap (other.peer, peer);
std::swap (other.window, window);
}

ComponentPeer* peer = nullptr;
XID window{};
};

class PeerListener : private ComponentMovementWatcher
{
public:
PeerListener (Component& comp, Window embeddedWindow)
: ComponentMovementWatcher (&comp),
window (embeddedWindow),
association (ScopedPeerAssociation::make (comp.getPeer(), window)) {}

private:
using ComponentMovementWatcher::componentMovedOrResized,
ComponentMovementWatcher::componentVisibilityChanged;

void componentMovedOrResized (bool, bool) override {}
void componentVisibilityChanged() override {}

void componentPeerChanged() override
{
// This should not be rewritten as a ternary expression or similar.
// The old association must be destroyed before the new one is created.
association = ScopedPeerAssociation::make (nullptr, window);

if (auto* comp = getComponent())
association = ScopedPeerAssociation::make (comp->getPeer(), window);
}

Window window{};
ScopedPeerAssociation association;
};

//==============================================================================
class OpenGLContext::NativeContext
{
Expand Down Expand Up @@ -114,7 +214,7 @@ class OpenGLContext::NativeContext
CWBorderPixel | CWColormap | CWEventMask,
&swa);

X11Symbols::getInstance()->xSaveContext (display, (XID) embeddedWindow, windowHandleXContext, (XPointer) peer);
peerListener.emplace (component, embeddedWindow);

X11Symbols::getInstance()->xMapWindow (display, embeddedWindow);
X11Symbols::getInstance()->xFreeColormap (display, colourMap);
Expand Down Expand Up @@ -322,6 +422,8 @@ class OpenGLContext::NativeContext
GLXContext renderContext = {};
Window embeddedWindow = {};

std::optional<PeerListener> peerListener;

int swapFrames = 1;
Rectangle<int> bounds;
std::unique_ptr<GLXFBConfig, XFreeDeleter> bestConfig;
Expand Down

0 comments on commit bac51ab

Please sign in to comment.