using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Media;
namespace DodoSSH.Client.App.Views;
///
/// Paints the frame Windows still draws around a BorderOnly window in the application's own
/// colour, so the top edge stops reading as a leftover system titlebar.
///
///
///
/// The symptom this exists for: a pale strip across the very top of the window, a few pixels
/// tall and plainly not part of the application — most obvious on a machine with "show accent colour
/// on title bars and window borders" turned on, where it comes out blue against a near-black shell.
///
///
/// It is not TitleBar.axaml leaking and it is not a margin. It is DWM, and the reason it is
/// there is visible in Avalonia's own Win32 backend: WindowImpl.UpdateWindowProperties gives a
/// window WS_BORDER | WS_THICKFRAME and then calls
/// DwmExtendFrameIntoClientArea with one-pixel margins on all four sides. So the compositor
/// owns a hairline of every edge of this window, and it fills that hairline with the system's caption
/// and border colours — which are chosen by the user's personalisation settings and have no reason to
/// resemble CanvasColor. The window is the wrong place to look for the pixels; they were never
/// painted by anything in this tree.
///
///
/// The fix is to tell DWM what colour to use rather than to try to cover it. DWMWA_BORDER_COLOR
/// and DWMWA_CAPTION_COLOR arrived in Windows 11 21H2 and are exactly that; both are set to the
/// window's own background, so the hairline still exists — the resize grip is on it, and the drop
/// shadow hangs off it — and simply cannot be seen. Deliberately not DWMWA_COLOR_NONE,
/// which removes the border outright: on a dark desktop that leaves a near-black window with no edge
/// at all, which trades one visual defect for another.
///
///
/// Windows 10 gets the dark-mode attribute and nothing else, and that is the whole of what is
/// available there: the two colour attributes are unsupported, DwmSetWindowAttribute answers
/// E_INVALIDARG, and the calls do nothing. Hence the ignored return values — every attribute
/// here is an improvement where it lands and a no-op where it does not, so there is nothing for a
/// caller to handle and nothing worth logging on a path that runs once at startup.
///
///
internal static class NativeWindowFrame
{
/// Windows 11 21H2 and later: the colour of the frame border.
private const int BorderColorAttribute = 34;
/// Windows 11 21H2 and later: the colour of the caption, including the extended frame.
private const int CaptionColorAttribute = 35;
///
/// Windows 10 1903 and later: draw the frame in the dark palette.
///
///
/// Redundant on Windows 11, where the two colour attributes above name the colours outright, and it
/// is set anyway because it is the only one of the three that Windows 10 honours. The build before
/// 1903 used attribute 19 for this; that is not chased here, because a border on an OS release that
/// left support in 2020 is not worth a second interop call.
///
private const int DarkModeAttribute = 20;
///
/// Matches 's system-drawn frame to the colour it paints itself.
///
///
/// Call once the window has a handle — OnOpened is the first such moment. Calling earlier
/// finds no platform handle and silently does nothing, which is the defect this replaced: the strip
/// is only visible once the window is on screen, so a call that ran too early looks like a fix that
/// does not work rather than a fix that never ran.
///
internal static void MatchTo(Window window)
{
// Every attribute below is a DWM one, and DWM is Windows. Elsewhere the frame is drawn by the
// platform's own compositor and there is nothing here to say to it.
if (!OperatingSystem.IsWindows())
{
return;
}
if (window.TryGetPlatformHandle()?.Handle is not { } handle || handle == IntPtr.Zero)
{
return;
}
Set(handle, DarkModeAttribute, 1);
// The window's own Background rather than a named resource, so the frame cannot drift from the
// canvas when the palette moves. A brush that is not solid — a gradient, or nothing set at all —
// has no single colour to match, and leaving the system's own is better than inventing one.
if (window.Background is not ISolidColorBrush { Color: var canvas })
{
return;
}
var reference = ColorRef(canvas);
Set(handle, BorderColorAttribute, reference);
Set(handle, CaptionColorAttribute, reference);
}
///
/// Sets one integer-valued DWM attribute, and discards the answer.
///
///
/// The discard is the point of this method existing rather than being three call sites. Every
/// attribute here is unsupported on some Windows this application runs on, and unsupported means
/// E_INVALIDARG and no change — which is the intended outcome on that OS, not a failure, so
/// there is nothing for the caller to do with the HRESULT and nothing worth logging once at
/// startup. Written once, with the reasoning, rather than left implicit at each call.
///
private static void Set(IntPtr window, int attribute, int value) =>
_ = DwmSetWindowAttribute(window, attribute, ref value, sizeof(int));
///
/// Packs into a Win32 COLORREF.
///
///
/// 0x00BBGGRR — blue in the high byte, not red, and the alpha byte must be zero. Getting the
/// order wrong produces a plausible-looking wrong colour rather than an error, which is the kind of
/// bug that survives a glance at the window.
///
private static int ColorRef(Color color) => color.R | (color.G << 8) | (color.B << 16);
///
/// DllImport rather than LibraryImport, for the reason
/// gives at its own P/Invoke: the generated form needs
/// AllowUnsafeBlocks across a project that handles key material, and this signature is
/// blittable, so there is no marshalling for it to improve.
///
#pragma warning disable SYSLIB1054
[DllImport("dwmapi.dll")]
private static extern int DwmSetWindowAttribute(IntPtr window, int attribute, ref int value, int size);
#pragma warning restore SYSLIB1054
}