using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Win32.Input;
using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia.Win32
{
///
/// Window implementation for Win32 platform.
///
public partial class WindowImpl : IWindowImpl, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo,
ITopLevelImplWithNativeControlHost
{
private static readonly List s_instances = new List();
private static readonly IntPtr DefaultCursor = LoadCursor(
IntPtr.Zero, new IntPtr((int)UnmanagedMethods.Cursor.IDC_ARROW));
private static readonly Dictionary s_edgeLookup =
new Dictionary
{
{ WindowEdge.East, HitTestValues.HTRIGHT },
{ WindowEdge.North, HitTestValues.HTTOP },
{ WindowEdge.NorthEast, HitTestValues.HTTOPRIGHT },
{ WindowEdge.NorthWest, HitTestValues.HTTOPLEFT },
{ WindowEdge.South, HitTestValues.HTBOTTOM },
{ WindowEdge.SouthEast, HitTestValues.HTBOTTOMRIGHT },
{ WindowEdge.SouthWest, HitTestValues.HTBOTTOMLEFT },
{ WindowEdge.West, HitTestValues.HTLEFT }
};
private SavedWindowInfo _savedWindowInfo;
private bool _isFullScreenActive;
private bool _isClientAreaExtended;
private Thickness _extendedMargins;
private Thickness _offScreenMargin;
private double _extendTitleBarHint = -1;
#if USE_MANAGED_DRAG
private readonly ManagedWindowResizeDragHelper _managedDrag;
#endif
private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE);
private readonly TouchDevice _touchDevice;
private readonly MouseDevice _mouseDevice;
private readonly ManagedDeferredRendererLock _rendererLock;
private readonly FramebufferManager _framebuffer;
private readonly IGlPlatformSurface _gl;
private Win32NativeControlHost _nativeControlHost;
private WndProc _wndProcDelegate;
private string _className;
private IntPtr _hwnd;
private bool _multitouch;
private IInputRoot _owner;
private WindowProperties _windowProperties;
private bool _trackingMouse;
private bool _topmost;
private double _scaling = 1;
private WindowState _showWindowState;
private WindowState _lastWindowState;
private OleDropTarget _dropTarget;
private Size _minSize;
private Size _maxSize;
private POINT _maxTrackSize;
private WindowImpl _parent;
private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default;
public WindowImpl()
{
_touchDevice = new TouchDevice();
_mouseDevice = new WindowsMouseDevice();
#if USE_MANAGED_DRAG
_managedDrag = new ManagedWindowResizeDragHelper(this, capture =>
{
if (capture)
UnmanagedMethods.SetCapture(Handle.Handle);
else
UnmanagedMethods.ReleaseCapture();
});
#endif
_windowProperties = new WindowProperties
{
ShowInTaskbar = false,
IsResizable = true,
Decorations = SystemDecorations.Full
};
_rendererLock = new ManagedDeferredRendererLock();
CreateWindow();
_framebuffer = new FramebufferManager(_hwnd);
if (Win32GlManager.EglFeature != null)
_gl = new EglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this);
Screen = new ScreenImpl();
_nativeControlHost = new Win32NativeControlHost(this);
s_instances.Add(this);
}
public Action Activated { get; set; }
public Func Closing { get; set; }
public Action Closed { get; set; }
public Action Deactivated { get; set; }
public Action Input { get; set; }
public Action Paint { get; set; }
public Action Resized { get; set; }
public Action ScalingChanged { get; set; }
public Action PositionChanged { get; set; }
public Action WindowStateChanged { get; set; }
public Action LostFocus { get; set; }
public Action TransparencyLevelChanged { get; set; }
public Thickness BorderThickness
{
get
{
if (HasFullDecorations)
{
var style = GetStyle();
var exStyle = GetExtendedStyle();
var padding = new RECT();
if (AdjustWindowRectEx(ref padding, (uint)style, false, (uint)exStyle))
{
return new Thickness(-padding.left, -padding.top, padding.right, padding.bottom);
}
else
{
throw new Win32Exception();
}
}
else
{
return new Thickness();
}
}
}
public double RenderScaling => _scaling;
public double DesktopScaling => RenderScaling;
public Size ClientSize
{
get
{
GetClientRect(_hwnd, out var rect);
return new Size(rect.right, rect.bottom) / RenderScaling;
}
}
public IScreenImpl Screen { get; }
public IPlatformHandle Handle { get; private set; }
public virtual Size MaxAutoSizeHint => new Size(_maxTrackSize.X / RenderScaling, _maxTrackSize.Y / RenderScaling);
public IMouseDevice MouseDevice => _mouseDevice;
public WindowState WindowState
{
get
{
if(_isFullScreenActive)
{
return WindowState.FullScreen;
}
var placement = default(WINDOWPLACEMENT);
GetWindowPlacement(_hwnd, ref placement);
return placement.ShowCmd switch
{
ShowWindowCommand.Maximize => WindowState.Maximized,
ShowWindowCommand.Minimize => WindowState.Minimized,
_ => WindowState.Normal
};
}
set
{
if (IsWindowVisible(_hwnd))
{
ShowWindow(value);
}
else
{
_showWindowState = value;
}
}
}
public WindowTransparencyLevel TransparencyLevel { get; private set; }
protected IntPtr Hwnd => _hwnd;
public void SetTransparencyLevelHint (WindowTransparencyLevel transparencyLevel)
{
TransparencyLevel = EnableBlur(transparencyLevel);
}
private WindowTransparencyLevel EnableBlur(WindowTransparencyLevel transparencyLevel)
{
if (Win32Platform.WindowsVersion.Major >= 6)
{
if (DwmIsCompositionEnabled(out var compositionEnabled) != 0 || !compositionEnabled)
{
return WindowTransparencyLevel.None;
}
else if (Win32Platform.WindowsVersion.Major >= 10)
{
return Win10EnableBlur(transparencyLevel);
}
else if (Win32Platform.WindowsVersion.Minor >= 2)
{
return Win8xEnableBlur(transparencyLevel);
}
else
{
return Win7EnableBlur(transparencyLevel);
}
}
else
{
return WindowTransparencyLevel.None;
}
}
private WindowTransparencyLevel Win7EnableBlur(WindowTransparencyLevel transparencyLevel)
{
if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur)
{
transparencyLevel = WindowTransparencyLevel.Blur;
}
var blurInfo = new DWM_BLURBEHIND(false);
if (transparencyLevel == WindowTransparencyLevel.Blur)
{
blurInfo = new DWM_BLURBEHIND(true);
}
DwmEnableBlurBehindWindow(_hwnd, ref blurInfo);
if (transparencyLevel == WindowTransparencyLevel.Transparent)
{
return WindowTransparencyLevel.None;
}
else
{
return transparencyLevel;
}
}
private WindowTransparencyLevel Win8xEnableBlur(WindowTransparencyLevel transparencyLevel)
{
var accent = new AccentPolicy();
var accentStructSize = Marshal.SizeOf(accent);
if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur)
{
transparencyLevel = WindowTransparencyLevel.Blur;
}
if (transparencyLevel == WindowTransparencyLevel.Transparent)
{
accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
}
else
{
accent.AccentState = AccentState.ACCENT_DISABLED;
}
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accent, accentPtr, false);
var data = new WindowCompositionAttributeData();
data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
data.SizeOfData = accentStructSize;
data.Data = accentPtr;
SetWindowCompositionAttribute(_hwnd, ref data);
Marshal.FreeHGlobal(accentPtr);
if (transparencyLevel >= WindowTransparencyLevel.Blur)
{
Win7EnableBlur(transparencyLevel);
}
return transparencyLevel;
}
private WindowTransparencyLevel Win10EnableBlur(WindowTransparencyLevel transparencyLevel)
{
bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628;
var accent = new AccentPolicy();
var accentStructSize = Marshal.SizeOf(accent);
if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic)
{
transparencyLevel = WindowTransparencyLevel.Blur;
}
switch (transparencyLevel)
{
default:
case WindowTransparencyLevel.None:
accent.AccentState = AccentState.ACCENT_DISABLED;
break;
case WindowTransparencyLevel.Transparent:
accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT;
break;
case WindowTransparencyLevel.Blur:
accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
break;
case WindowTransparencyLevel.AcrylicBlur:
case (WindowTransparencyLevel.AcrylicBlur + 1): // hack-force acrylic.
accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC;
transparencyLevel = WindowTransparencyLevel.AcrylicBlur;
break;
}
accent.AccentFlags = 2;
accent.GradientColor = 0x01000000;
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accent, accentPtr, false);
var data = new WindowCompositionAttributeData();
data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
data.SizeOfData = accentStructSize;
data.Data = accentPtr;
SetWindowCompositionAttribute(_hwnd, ref data);
Marshal.FreeHGlobal(accentPtr);
return transparencyLevel;
}
public IEnumerable