Skip to content

Commit

Permalink
X11: Ask the host to manage client-initiated resizes
Browse files Browse the repository at this point in the history
  • Loading branch information
reuk committed Jul 24, 2023
1 parent f8d38ed commit a8fa44e
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 3 deletions.
Expand Up @@ -125,6 +125,10 @@ void ResizableBorderComponent::mouseDown (const MouseEvent& e)

originalBounds = component->getBounds();

if (auto* peer = component->getPeer())
if (&peer->getComponent() == component)
peer->startHostManagedResize (peer->globalToLocal (localPointToGlobal (e.getPosition())), mouseZone);

if (constrainer != nullptr)
constrainer->resizeStart();
}
Expand Down
Expand Up @@ -125,7 +125,7 @@ class JUCE_API ResizableBorderComponent : public Component
/** Returns an appropriate mouse-cursor for this resize zone. */
MouseCursor getMouseCursor() const noexcept;

/** Returns true if dragging this zone will move the enire object without resizing it. */
/** Returns true if dragging this zone will move the entire object without resizing it. */
bool isDraggingWholeObject() const noexcept { return zone == centre; }
/** Returns true if dragging this zone will move the object's left edge. */
bool isDraggingLeftEdge() const noexcept { return (zone & left) != 0; }
Expand Down
Expand Up @@ -45,7 +45,7 @@ void ResizableCornerComponent::paint (Graphics& g)
isMouseButtonDown());
}

void ResizableCornerComponent::mouseDown (const MouseEvent&)
void ResizableCornerComponent::mouseDown (const MouseEvent& e)
{
if (component == nullptr)
{
Expand All @@ -55,6 +55,13 @@ void ResizableCornerComponent::mouseDown (const MouseEvent&)

originalBounds = component->getBounds();

using Zone = ResizableBorderComponent::Zone;
const Zone zone { Zone::bottom | Zone::right };

if (auto* peer = component->getPeer())
if (&peer->getComponent() == component)
peer->startHostManagedResize (peer->globalToLocal (localPointToGlobal (e.getPosition())), zone);

if (constrainer != nullptr)
constrainer->resizeStart();
}
Expand Down
21 changes: 20 additions & 1 deletion modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp
Expand Up @@ -52,7 +52,7 @@ void ResizableEdgeComponent::paint (Graphics& g)
isMouseOver(), isMouseButtonDown());
}

void ResizableEdgeComponent::mouseDown (const MouseEvent&)
void ResizableEdgeComponent::mouseDown (const MouseEvent& e)
{
if (component == nullptr)
{
Expand All @@ -62,6 +62,25 @@ void ResizableEdgeComponent::mouseDown (const MouseEvent&)

originalBounds = component->getBounds();

using Zone = ResizableBorderComponent::Zone;

const Zone zone { [&]
{
switch (edge)
{
case Edge::leftEdge: return Zone::left;
case Edge::rightEdge: return Zone::right;
case Edge::topEdge: return Zone::top;
case Edge::bottomEdge: return Zone::bottom;
}

return Zone::centre;
}() };

if (auto* peer = component->getPeer())
if (&peer->getComponent() == component)
peer->startHostManagedResize (peer->globalToLocal (localPointToGlobal (e.getPosition())), zone);

if (constrainer != nullptr)
constrainer->resizeStart();
}
Expand Down
6 changes: 6 additions & 0 deletions modules/juce_gui_basics/native/juce_Windowing_linux.cpp
Expand Up @@ -399,6 +399,12 @@ class LinuxComponentPeer : public ComponentPeer,

void clearWindowAssociation() { association = {}; }

void startHostManagedResize (Point<int> mouseDownPosition,
ResizableBorderComponent::Zone zone) override
{
XWindowSystem::getInstance()->startHostManagedResize (windowH, mouseDownPosition, zone);
}

//==============================================================================
static bool isActiveApplication;
bool focused = false;
Expand Down
62 changes: 62 additions & 0 deletions modules/juce_gui_basics/native/juce_XWindowSystem_linux.cpp
Expand Up @@ -1789,6 +1789,68 @@ void XWindowSystem::setBounds (::Window windowH, Rectangle<int> newBounds, bool
}
}

void XWindowSystem::startHostManagedResize (::Window windowH,
Point<int> mouseDown,
ResizableBorderComponent::Zone zone)
{
const auto moveResize = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_MOVERESIZE");

if (moveResize == None)
return;

XWindowSystemUtilities::ScopedXLock xLock;

X11Symbols::getInstance()->xUngrabPointer (display, CurrentTime);

const auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display));

XClientMessageEvent clientMsg;
clientMsg.display = display;
clientMsg.window = windowH;
clientMsg.type = ClientMessage;
clientMsg.format = 32;
clientMsg.message_type = moveResize;
clientMsg.data.l[0] = mouseDown.getX();
clientMsg.data.l[1] = mouseDown.getY();
clientMsg.data.l[2] = [&]
{
// It's unclear which header is supposed to contain these
static constexpr auto _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0;
static constexpr auto _NET_WM_MOVERESIZE_SIZE_TOP = 1;
static constexpr auto _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2;
static constexpr auto _NET_WM_MOVERESIZE_SIZE_RIGHT = 3;
static constexpr auto _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4;
static constexpr auto _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5;
static constexpr auto _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6;
static constexpr auto _NET_WM_MOVERESIZE_SIZE_LEFT = 7;
static constexpr auto _NET_WM_MOVERESIZE_MOVE = 8;

using F = ResizableBorderComponent::Zone::Zones;

switch (zone.getZoneFlags())
{
case F::top | F::left: return _NET_WM_MOVERESIZE_SIZE_TOPLEFT;
case F::top: return _NET_WM_MOVERESIZE_SIZE_TOP;
case F::top | F::right: return _NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
case F::right: return _NET_WM_MOVERESIZE_SIZE_RIGHT;
case F::bottom | F::right: return _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
case F::bottom: return _NET_WM_MOVERESIZE_SIZE_BOTTOM;
case F::bottom | F::left: return _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
case F::left: return _NET_WM_MOVERESIZE_SIZE_LEFT;
}

return _NET_WM_MOVERESIZE_MOVE;
}();
clientMsg.data.l[3] = 0;
clientMsg.data.l[4] = 1;

X11Symbols::getInstance()->xSendEvent (display,
root,
false,
SubstructureRedirectMask | SubstructureNotifyMask,
unalignedPointerCast<XEvent*> (&clientMsg));
}

void XWindowSystem::updateConstraints (::Window windowH) const
{
if (auto* peer = getPeerFor (windowH))
Expand Down
4 changes: 4 additions & 0 deletions modules/juce_gui_basics/native/juce_XWindowSystem_linux.h
Expand Up @@ -241,6 +241,10 @@ class XWindowSystem : public DeletedAtShutdown

bool isX11Available() const noexcept { return xIsAvailable; }

void startHostManagedResize (::Window window,
Point<int> mouseDown,
ResizableBorderComponent::Zone zone);

static String getWindowScalingFactorSettingName() { return "Gdk/WindowScalingFactor"; }
static String getThemeNameSettingName() { return "Net/ThemeName"; }

Expand Down
10 changes: 10 additions & 0 deletions modules/juce_gui_basics/windows/juce_ComponentPeer.h
Expand Up @@ -258,6 +258,16 @@ class JUCE_API ComponentPeer : private FocusChangeListener
*/
void setConstrainer (ComponentBoundsConstrainer* newConstrainer) noexcept;

/** Asks the window-manager to begin resizing this window, on platforms where this is useful
(currently just Linux/X11).
@param mouseDownPosition The position of the mouse event that started the resize in
unscaled peer coordinates
@param zone The edges of the window that may be moved during the resize
*/
virtual void startHostManagedResize ([[maybe_unused]] Point<int> mouseDownPosition,
[[maybe_unused]] ResizableBorderComponent::Zone zone) {}

/** Returns the current constrainer, if one has been set. */
ComponentBoundsConstrainer* getConstrainer() const noexcept { return constrainer; }

Expand Down

0 comments on commit a8fa44e

Please sign in to comment.