X11 CSD: double-click on ElementRole="TitleBar" does not maximize/restore the window
On Linux (X11) with client-side decorations enabled (X11PlatformOptions.EnableDrawnDecorations = true and Window.ExtendClientAreaToDecorationsHint = true), double-clicking the title bar does not toggle WindowState between Normal and Maximized.
The same setup works on Windows and macOS.
The maximize button (PART_MaximizeButton / ElementRole="MaximizeButton") still works. Only the title-bar double-click gesture is missing.
Avalonia docs say that a control marked with WindowDecorationProperties.ElementRole="TitleBar" should get native drag and double-click-to-maximize automatically:
Mark an element as a title bar drag region using the WindowDecorationProperties.ElementRole attached property. The operating system handles drag and double-click-to-maximize behavior automatically.
On X11 CSD this second part is not implemented.
To Reproduce
- Create a desktop Avalonia app (12.1.0 or current master).
- Enable X11 drawn decorations:
AppBuilder.Configure<App>()
.UsePlatformDetect()
.With(new X11PlatformOptions
{
EnableDrawnDecorations = true,
});- Use a window with extended client area and a title-bar role, for example:
<Window ExtendClientAreaToDecorationsHint="True">
<Border Height="32"
Background="SteelBlue"
WindowDecorationProperties.ElementRole="TitleBar" />
</Window>(Default WindowDrawnDecorations with PART_TitleBar / ElementRole="TitleBar" reproduces it as well.) 4. Run on Linux under X11 (not WSL/WSLg). 5. Restore the window to WindowState.Normal. 6. Double-click the title bar (not the maximize button).
Avalonia version 12.1.0 (also present on current master in src/Avalonia.X11/X11Window.cs)
OS Linux / Debian / X11 (reproduced with EnableDrawnDecorations CSD). Windows and macOS are fine.
Expected behavior
Double-click on the title bar while Normal → WindowState.Maximized Double-click while Maximized → WindowState.Normal Single-click + drag still moves the window This is how Win32 (HTCAPTION) and macOS (DoubleClickHelper in Avalonia.Native.WindowImpl.ChromeHitTest) already behave.
Actual behavior
The window is dragged (or a drag is initiated). WindowState does not change.
Avalonia version
12.1.0
OS
Linux
Additional context
Root cause: in X11Window.ScheduleInput, a LeftButtonDown on chrome TitleBar immediately sends _NET_WM_MOVERESIZE with direction _NET_WM_MOVERESIZE_MOVE and returns, without checking for a double-click:
// Chrome hit-test for drawn decorations
if (NeedsDrawnDecorations
&& mouse.Type == RawPointerEventType.LeftButtonDown
&& _inputRoot is { } inputRoot)
{
var chromeRole = inputRoot.HitTestChromeElement(mouse.Position);
if (chromeRole is { } role)
{
var moveResizeSide = role switch
{
WindowDecorationsElementRole.TitleBar => NetWmMoveResize._NET_WM_MOVERESIZE_MOVE,
// ... resize edges ...
};
if (moveResizeSide.HasValue)
{
SendNetWMMessage(_x11.Atoms._NET_WM_MOVERESIZE, ...);
return; // event never reaches Avalonia input / DoubleTapped
}
}
}There is no DoubleClickHelper usage in this file.
Why this breaks double-click
On Linux the application does not move its own window by changing X/Y. For CSD, the toolkit asks the window manager (GNOME/Mutter, KDE/KWin, etc.) to take over the pointer and move the window. That request is the EWMH client message _NET_WM_MOVERESIZE with direction MOVE.
Avalonia sends that message on the first click. After that:
the pointer is handed to the WM for a move operation; the second click of a double-click is no longer a title-bar double-click inside Avalonia; EWMH has no “double-click = maximize” message — the toolkit must detect the double-click itself and set _NET_WM_STATE / WindowState. macOS already does the right split in Avalonia.Native/WindowImpl.cs:
if (_doubleClickHelper.IsDoubleClick(e.Timestamp, e.Position))
{
switch (WindowState)
{
case WindowState.Maximized or WindowState.FullScreen when _canResize:
WindowState = WindowState.Normal;
break;
case WindowState.Normal when _canMaximize:
WindowState = WindowState.Maximized;
break;
}
}
else
{
_native.BeginMoveDrag();
}Win32 maps TitleBar to HTCAPTION; DefWindowProc handles WM_NCLBUTTONDBLCLK.
Why an app-level DoubleTapped workaround does not work
Because ScheduleInput returns before _rawEventGrouper.HandleEvent, the click never enters Avalonia’s input pipeline. DoubleTapped on PART_TitleBar never fires.
The maximize button is unaffected: MaximizeButton is not mapped to NET_WM_MOVERESIZE*, so WindowDrawnDecorations.OnMaximizeButtonClick still toggles WindowState.
Suggested fix
Mirror the macOS ChromeHitTest logic in the X11 CSD path: on TitleBar + LeftButtonDown, if DoubleClickHelper.IsDoubleClick(...) then toggle WindowState (respecting CanMaximize / CanResize); otherwise send _NET_WM_MOVERESIZE_MOVE.
Source: AvaloniaUI/Avalonia