WindowImpl.cs 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Runtime.InteropServices;
  5. using Avalonia.Controls;
  6. using Avalonia.Controls.Platform;
  7. using Avalonia.Input;
  8. using Avalonia.Input.Raw;
  9. using Avalonia.OpenGL;
  10. using Avalonia.Platform;
  11. using Avalonia.Rendering;
  12. using Avalonia.Win32.Input;
  13. using Avalonia.Win32.Interop;
  14. using static Avalonia.Win32.Interop.UnmanagedMethods;
  15. namespace Avalonia.Win32
  16. {
  17. /// <summary>
  18. /// Window implementation for Win32 platform.
  19. /// </summary>
  20. public partial class WindowImpl : IWindowImpl, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo,
  21. ITopLevelImplWithNativeControlHost
  22. {
  23. private static readonly List<WindowImpl> s_instances = new List<WindowImpl>();
  24. private static readonly IntPtr DefaultCursor = LoadCursor(
  25. IntPtr.Zero, new IntPtr((int)UnmanagedMethods.Cursor.IDC_ARROW));
  26. private static readonly Dictionary<WindowEdge, HitTestValues> s_edgeLookup =
  27. new Dictionary<WindowEdge, HitTestValues>
  28. {
  29. { WindowEdge.East, HitTestValues.HTRIGHT },
  30. { WindowEdge.North, HitTestValues.HTTOP },
  31. { WindowEdge.NorthEast, HitTestValues.HTTOPRIGHT },
  32. { WindowEdge.NorthWest, HitTestValues.HTTOPLEFT },
  33. { WindowEdge.South, HitTestValues.HTBOTTOM },
  34. { WindowEdge.SouthEast, HitTestValues.HTBOTTOMRIGHT },
  35. { WindowEdge.SouthWest, HitTestValues.HTBOTTOMLEFT },
  36. { WindowEdge.West, HitTestValues.HTLEFT }
  37. };
  38. private SavedWindowInfo _savedWindowInfo;
  39. private bool _isFullScreenActive;
  40. private bool _isClientAreaExtended;
  41. private Thickness _extendedMargins;
  42. private Thickness _offScreenMargin;
  43. private double _extendTitleBarHint = -1;
  44. #if USE_MANAGED_DRAG
  45. private readonly ManagedWindowResizeDragHelper _managedDrag;
  46. #endif
  47. private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE);
  48. private readonly TouchDevice _touchDevice;
  49. private readonly MouseDevice _mouseDevice;
  50. private readonly ManagedDeferredRendererLock _rendererLock;
  51. private readonly FramebufferManager _framebuffer;
  52. private readonly IGlPlatformSurface _gl;
  53. private Win32NativeControlHost _nativeControlHost;
  54. private WndProc _wndProcDelegate;
  55. private string _className;
  56. private IntPtr _hwnd;
  57. private bool _multitouch;
  58. private IInputRoot _owner;
  59. private WindowProperties _windowProperties;
  60. private bool _trackingMouse;
  61. private bool _topmost;
  62. private double _scaling = 1;
  63. private WindowState _showWindowState;
  64. private WindowState _lastWindowState;
  65. private OleDropTarget _dropTarget;
  66. private Size _minSize;
  67. private Size _maxSize;
  68. private POINT _maxTrackSize;
  69. private WindowImpl _parent;
  70. private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default;
  71. public WindowImpl()
  72. {
  73. _touchDevice = new TouchDevice();
  74. _mouseDevice = new WindowsMouseDevice();
  75. #if USE_MANAGED_DRAG
  76. _managedDrag = new ManagedWindowResizeDragHelper(this, capture =>
  77. {
  78. if (capture)
  79. UnmanagedMethods.SetCapture(Handle.Handle);
  80. else
  81. UnmanagedMethods.ReleaseCapture();
  82. });
  83. #endif
  84. _windowProperties = new WindowProperties
  85. {
  86. ShowInTaskbar = false,
  87. IsResizable = true,
  88. Decorations = SystemDecorations.Full
  89. };
  90. _rendererLock = new ManagedDeferredRendererLock();
  91. CreateWindow();
  92. _framebuffer = new FramebufferManager(_hwnd);
  93. if (Win32GlManager.EglFeature != null)
  94. _gl = new EglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this);
  95. Screen = new ScreenImpl();
  96. _nativeControlHost = new Win32NativeControlHost(this);
  97. s_instances.Add(this);
  98. }
  99. public Action Activated { get; set; }
  100. public Func<bool> Closing { get; set; }
  101. public Action Closed { get; set; }
  102. public Action Deactivated { get; set; }
  103. public Action<RawInputEventArgs> Input { get; set; }
  104. public Action<Rect> Paint { get; set; }
  105. public Action<Size> Resized { get; set; }
  106. public Action<double> ScalingChanged { get; set; }
  107. public Action<PixelPoint> PositionChanged { get; set; }
  108. public Action<WindowState> WindowStateChanged { get; set; }
  109. public Action LostFocus { get; set; }
  110. public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
  111. public Thickness BorderThickness
  112. {
  113. get
  114. {
  115. if (HasFullDecorations)
  116. {
  117. var style = GetStyle();
  118. var exStyle = GetExtendedStyle();
  119. var padding = new RECT();
  120. if (AdjustWindowRectEx(ref padding, (uint)style, false, (uint)exStyle))
  121. {
  122. return new Thickness(-padding.left, -padding.top, padding.right, padding.bottom);
  123. }
  124. else
  125. {
  126. throw new Win32Exception();
  127. }
  128. }
  129. else
  130. {
  131. return new Thickness();
  132. }
  133. }
  134. }
  135. public double RenderScaling => _scaling;
  136. public double DesktopScaling => RenderScaling;
  137. public Size ClientSize
  138. {
  139. get
  140. {
  141. GetClientRect(_hwnd, out var rect);
  142. return new Size(rect.right, rect.bottom) / RenderScaling;
  143. }
  144. }
  145. public IScreenImpl Screen { get; }
  146. public IPlatformHandle Handle { get; private set; }
  147. public virtual Size MaxAutoSizeHint => new Size(_maxTrackSize.X / RenderScaling, _maxTrackSize.Y / RenderScaling);
  148. public IMouseDevice MouseDevice => _mouseDevice;
  149. public WindowState WindowState
  150. {
  151. get
  152. {
  153. if(_isFullScreenActive)
  154. {
  155. return WindowState.FullScreen;
  156. }
  157. var placement = default(WINDOWPLACEMENT);
  158. GetWindowPlacement(_hwnd, ref placement);
  159. return placement.ShowCmd switch
  160. {
  161. ShowWindowCommand.Maximize => WindowState.Maximized,
  162. ShowWindowCommand.Minimize => WindowState.Minimized,
  163. _ => WindowState.Normal
  164. };
  165. }
  166. set
  167. {
  168. if (IsWindowVisible(_hwnd))
  169. {
  170. ShowWindow(value);
  171. }
  172. else
  173. {
  174. _showWindowState = value;
  175. }
  176. }
  177. }
  178. public WindowTransparencyLevel TransparencyLevel { get; private set; }
  179. protected IntPtr Hwnd => _hwnd;
  180. public void SetTransparencyLevelHint (WindowTransparencyLevel transparencyLevel)
  181. {
  182. TransparencyLevel = EnableBlur(transparencyLevel);
  183. }
  184. private WindowTransparencyLevel EnableBlur(WindowTransparencyLevel transparencyLevel)
  185. {
  186. if (Win32Platform.WindowsVersion.Major >= 6)
  187. {
  188. if (DwmIsCompositionEnabled(out var compositionEnabled) != 0 || !compositionEnabled)
  189. {
  190. return WindowTransparencyLevel.None;
  191. }
  192. else if (Win32Platform.WindowsVersion.Major >= 10)
  193. {
  194. return Win10EnableBlur(transparencyLevel);
  195. }
  196. else if (Win32Platform.WindowsVersion.Minor >= 2)
  197. {
  198. return Win8xEnableBlur(transparencyLevel);
  199. }
  200. else
  201. {
  202. return Win7EnableBlur(transparencyLevel);
  203. }
  204. }
  205. else
  206. {
  207. return WindowTransparencyLevel.None;
  208. }
  209. }
  210. private WindowTransparencyLevel Win7EnableBlur(WindowTransparencyLevel transparencyLevel)
  211. {
  212. if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur)
  213. {
  214. transparencyLevel = WindowTransparencyLevel.Blur;
  215. }
  216. var blurInfo = new DWM_BLURBEHIND(false);
  217. if (transparencyLevel == WindowTransparencyLevel.Blur)
  218. {
  219. blurInfo = new DWM_BLURBEHIND(true);
  220. }
  221. DwmEnableBlurBehindWindow(_hwnd, ref blurInfo);
  222. if (transparencyLevel == WindowTransparencyLevel.Transparent)
  223. {
  224. return WindowTransparencyLevel.None;
  225. }
  226. else
  227. {
  228. return transparencyLevel;
  229. }
  230. }
  231. private WindowTransparencyLevel Win8xEnableBlur(WindowTransparencyLevel transparencyLevel)
  232. {
  233. var accent = new AccentPolicy();
  234. var accentStructSize = Marshal.SizeOf(accent);
  235. if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur)
  236. {
  237. transparencyLevel = WindowTransparencyLevel.Blur;
  238. }
  239. if (transparencyLevel == WindowTransparencyLevel.Transparent)
  240. {
  241. accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
  242. }
  243. else
  244. {
  245. accent.AccentState = AccentState.ACCENT_DISABLED;
  246. }
  247. var accentPtr = Marshal.AllocHGlobal(accentStructSize);
  248. Marshal.StructureToPtr(accent, accentPtr, false);
  249. var data = new WindowCompositionAttributeData();
  250. data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
  251. data.SizeOfData = accentStructSize;
  252. data.Data = accentPtr;
  253. SetWindowCompositionAttribute(_hwnd, ref data);
  254. Marshal.FreeHGlobal(accentPtr);
  255. if (transparencyLevel >= WindowTransparencyLevel.Blur)
  256. {
  257. Win7EnableBlur(transparencyLevel);
  258. }
  259. return transparencyLevel;
  260. }
  261. private WindowTransparencyLevel Win10EnableBlur(WindowTransparencyLevel transparencyLevel)
  262. {
  263. bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628;
  264. var accent = new AccentPolicy();
  265. var accentStructSize = Marshal.SizeOf(accent);
  266. if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic)
  267. {
  268. transparencyLevel = WindowTransparencyLevel.Blur;
  269. }
  270. switch (transparencyLevel)
  271. {
  272. default:
  273. case WindowTransparencyLevel.None:
  274. accent.AccentState = AccentState.ACCENT_DISABLED;
  275. break;
  276. case WindowTransparencyLevel.Transparent:
  277. accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT;
  278. break;
  279. case WindowTransparencyLevel.Blur:
  280. accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
  281. break;
  282. case WindowTransparencyLevel.AcrylicBlur:
  283. case (WindowTransparencyLevel.AcrylicBlur + 1): // hack-force acrylic.
  284. accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC;
  285. transparencyLevel = WindowTransparencyLevel.AcrylicBlur;
  286. break;
  287. }
  288. accent.AccentFlags = 2;
  289. accent.GradientColor = 0x01000000;
  290. var accentPtr = Marshal.AllocHGlobal(accentStructSize);
  291. Marshal.StructureToPtr(accent, accentPtr, false);
  292. var data = new WindowCompositionAttributeData();
  293. data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
  294. data.SizeOfData = accentStructSize;
  295. data.Data = accentPtr;
  296. SetWindowCompositionAttribute(_hwnd, ref data);
  297. Marshal.FreeHGlobal(accentPtr);
  298. return transparencyLevel;
  299. }
  300. public IEnumerable<object> Surfaces => new object[] { Handle, _gl, _framebuffer };
  301. public PixelPoint Position
  302. {
  303. get
  304. {
  305. GetWindowRect(_hwnd, out var rc);
  306. return new PixelPoint(rc.left, rc.top);
  307. }
  308. set
  309. {
  310. SetWindowPos(
  311. Handle.Handle,
  312. IntPtr.Zero,
  313. value.X,
  314. value.Y,
  315. 0,
  316. 0,
  317. SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
  318. }
  319. }
  320. private bool HasFullDecorations => _windowProperties.Decorations == SystemDecorations.Full;
  321. public void Move(PixelPoint point) => Position = point;
  322. public void SetMinMaxSize(Size minSize, Size maxSize)
  323. {
  324. _minSize = minSize;
  325. _maxSize = maxSize;
  326. }
  327. public IRenderer CreateRenderer(IRenderRoot root)
  328. {
  329. var loop = AvaloniaLocator.Current.GetService<IRenderLoop>();
  330. var customRendererFactory = AvaloniaLocator.Current.GetService<IRendererFactory>();
  331. if (customRendererFactory != null)
  332. return customRendererFactory.Create(root, loop);
  333. return Win32Platform.UseDeferredRendering ?
  334. (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock) :
  335. new ImmediateRenderer(root);
  336. }
  337. public void Resize(Size value)
  338. {
  339. int requestedClientWidth = (int)(value.Width * RenderScaling);
  340. int requestedClientHeight = (int)(value.Height * RenderScaling);
  341. GetClientRect(_hwnd, out var clientRect);
  342. // do comparison after scaling to avoid rounding issues
  343. if (requestedClientWidth != clientRect.Width || requestedClientHeight != clientRect.Height)
  344. {
  345. GetWindowRect(_hwnd, out var windowRect);
  346. SetWindowPos(
  347. _hwnd,
  348. IntPtr.Zero,
  349. 0,
  350. 0,
  351. requestedClientWidth + (windowRect.Width - clientRect.Width),
  352. requestedClientHeight + (windowRect.Height - clientRect.Height),
  353. SetWindowPosFlags.SWP_RESIZE);
  354. }
  355. }
  356. public void Activate()
  357. {
  358. SetActiveWindow(_hwnd);
  359. }
  360. public IPopupImpl CreatePopup() => Win32Platform.UseOverlayPopups ? null : new PopupImpl(this);
  361. public void Dispose()
  362. {
  363. if (_dropTarget != null)
  364. {
  365. OleContext.Current?.UnregisterDragDrop(Handle);
  366. _dropTarget = null;
  367. }
  368. if (_hwnd != IntPtr.Zero)
  369. {
  370. DestroyWindow(_hwnd);
  371. _hwnd = IntPtr.Zero;
  372. }
  373. if (_className != null)
  374. {
  375. UnregisterClass(_className, GetModuleHandle(null));
  376. _className = null;
  377. }
  378. _framebuffer.Dispose();
  379. }
  380. public void Invalidate(Rect rect)
  381. {
  382. var scaling = RenderScaling;
  383. var r = new RECT
  384. {
  385. left = (int)Math.Floor(rect.X * scaling),
  386. top = (int)Math.Floor(rect.Y * scaling),
  387. right = (int)Math.Ceiling(rect.Right * scaling),
  388. bottom = (int)Math.Ceiling(rect.Bottom * scaling),
  389. };
  390. InvalidateRect(_hwnd, ref r, false);
  391. }
  392. public Point PointToClient(PixelPoint point)
  393. {
  394. var p = new POINT { X = point.X, Y = point.Y };
  395. UnmanagedMethods.ScreenToClient(_hwnd, ref p);
  396. return new Point(p.X, p.Y) / RenderScaling;
  397. }
  398. public PixelPoint PointToScreen(Point point)
  399. {
  400. point *= RenderScaling;
  401. var p = new POINT { X = (int)point.X, Y = (int)point.Y };
  402. ClientToScreen(_hwnd, ref p);
  403. return new PixelPoint(p.X, p.Y);
  404. }
  405. public void SetInputRoot(IInputRoot inputRoot)
  406. {
  407. _owner = inputRoot;
  408. CreateDropTarget();
  409. }
  410. public void Hide()
  411. {
  412. UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide);
  413. }
  414. public virtual void Show()
  415. {
  416. SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, _parent != null ? _parent._hwnd : IntPtr.Zero);
  417. ShowWindow(_showWindowState);
  418. }
  419. public Action GotInputWhenDisabled { get; set; }
  420. public void SetParent(IWindowImpl parent)
  421. {
  422. _parent = (WindowImpl)parent;
  423. SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, _parent._hwnd);
  424. }
  425. public void SetEnabled(bool enable) => EnableWindow(_hwnd, enable);
  426. public void BeginMoveDrag(PointerPressedEventArgs e)
  427. {
  428. _mouseDevice.Capture(null);
  429. DefWindowProc(_hwnd, (int)WindowsMessage.WM_NCLBUTTONDOWN,
  430. new IntPtr((int)HitTestValues.HTCAPTION), IntPtr.Zero);
  431. e.Pointer.Capture(null);
  432. }
  433. public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e)
  434. {
  435. #if USE_MANAGED_DRAG
  436. _managedDrag.BeginResizeDrag(edge, ScreenToClient(MouseDevice.Position.ToPoint(_scaling)));
  437. #else
  438. _mouseDevice.Capture(null);
  439. DefWindowProc(_hwnd, (int)WindowsMessage.WM_NCLBUTTONDOWN,
  440. new IntPtr((int)s_edgeLookup[edge]), IntPtr.Zero);
  441. #endif
  442. }
  443. public void SetTitle(string title)
  444. {
  445. SetWindowText(_hwnd, title);
  446. }
  447. public void SetCursor(IPlatformHandle cursor)
  448. {
  449. var hCursor = cursor?.Handle ?? DefaultCursor;
  450. SetClassLong(_hwnd, ClassLongIndex.GCLP_HCURSOR, hCursor);
  451. if (_owner.IsPointerOver)
  452. {
  453. UnmanagedMethods.SetCursor(hCursor);
  454. }
  455. }
  456. public void SetIcon(IWindowIconImpl icon)
  457. {
  458. var impl = (IconImpl)icon;
  459. var hIcon = impl?.HIcon ?? IntPtr.Zero;
  460. PostMessage(_hwnd, (int)WindowsMessage.WM_SETICON,
  461. new IntPtr((int)Icons.ICON_BIG), hIcon);
  462. }
  463. public void ShowTaskbarIcon(bool value)
  464. {
  465. var newWindowProperties = _windowProperties;
  466. newWindowProperties.ShowInTaskbar = value;
  467. UpdateWindowProperties(newWindowProperties);
  468. }
  469. public void CanResize(bool value)
  470. {
  471. var newWindowProperties = _windowProperties;
  472. newWindowProperties.IsResizable = value;
  473. UpdateWindowProperties(newWindowProperties);
  474. }
  475. public void SetSystemDecorations(SystemDecorations value)
  476. {
  477. var newWindowProperties = _windowProperties;
  478. newWindowProperties.Decorations = value;
  479. UpdateWindowProperties(newWindowProperties);
  480. }
  481. public void SetTopmost(bool value)
  482. {
  483. if (value == _topmost)
  484. {
  485. return;
  486. }
  487. IntPtr hWndInsertAfter = value ? WindowPosZOrder.HWND_TOPMOST : WindowPosZOrder.HWND_NOTOPMOST;
  488. SetWindowPos(_hwnd,
  489. hWndInsertAfter,
  490. 0, 0, 0, 0,
  491. SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
  492. _topmost = value;
  493. }
  494. protected virtual IntPtr CreateWindowOverride(ushort atom)
  495. {
  496. return CreateWindowEx(
  497. 0,
  498. atom,
  499. null,
  500. (int)WindowStyles.WS_OVERLAPPEDWINDOW | (int) WindowStyles.WS_CLIPCHILDREN,
  501. CW_USEDEFAULT,
  502. CW_USEDEFAULT,
  503. CW_USEDEFAULT,
  504. CW_USEDEFAULT,
  505. IntPtr.Zero,
  506. IntPtr.Zero,
  507. IntPtr.Zero,
  508. IntPtr.Zero);
  509. }
  510. private void CreateWindow()
  511. {
  512. // Ensure that the delegate doesn't get garbage collected by storing it as a field.
  513. _wndProcDelegate = WndProc;
  514. _className = $"Avalonia-{Guid.NewGuid().ToString()}";
  515. // Unique DC helps with performance when using Gpu based rendering
  516. const ClassStyles windowClassStyle = ClassStyles.CS_OWNDC | ClassStyles.CS_HREDRAW | ClassStyles.CS_VREDRAW;
  517. var wndClassEx = new WNDCLASSEX
  518. {
  519. cbSize = Marshal.SizeOf<WNDCLASSEX>(),
  520. style = (int)windowClassStyle,
  521. lpfnWndProc = _wndProcDelegate,
  522. hInstance = GetModuleHandle(null),
  523. hCursor = DefaultCursor,
  524. hbrBackground = IntPtr.Zero,
  525. lpszClassName = _className
  526. };
  527. ushort atom = RegisterClassEx(ref wndClassEx);
  528. if (atom == 0)
  529. {
  530. throw new Win32Exception();
  531. }
  532. _hwnd = CreateWindowOverride(atom);
  533. if (_hwnd == IntPtr.Zero)
  534. {
  535. throw new Win32Exception();
  536. }
  537. Handle = new PlatformHandle(_hwnd, PlatformConstants.WindowHandleType);
  538. _multitouch = Win32Platform.Options.EnableMultitouch ?? true;
  539. if (_multitouch)
  540. {
  541. RegisterTouchWindow(_hwnd, 0);
  542. }
  543. if (ShCoreAvailable)
  544. {
  545. var monitor = MonitorFromWindow(
  546. _hwnd,
  547. MONITOR.MONITOR_DEFAULTTONEAREST);
  548. if (GetDpiForMonitor(
  549. monitor,
  550. MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
  551. out var dpix,
  552. out var dpiy) == 0)
  553. {
  554. _scaling = dpix / 96.0;
  555. }
  556. }
  557. }
  558. private void CreateDropTarget()
  559. {
  560. var odt = new OleDropTarget(this, _owner);
  561. if (OleContext.Current?.RegisterDragDrop(Handle, odt) ?? false)
  562. {
  563. _dropTarget = odt;
  564. }
  565. }
  566. /// <summary>
  567. /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc
  568. /// Method must only be called from inside UpdateWindowProperties.
  569. /// </summary>
  570. /// <param name="fullscreen"></param>
  571. private void SetFullScreen(bool fullscreen)
  572. {
  573. if (fullscreen)
  574. {
  575. GetWindowRect(_hwnd, out var windowRect);
  576. _savedWindowInfo.WindowRect = windowRect;
  577. var current = GetStyle();
  578. var currentEx = GetExtendedStyle();
  579. _savedWindowInfo.Style = current;
  580. _savedWindowInfo.ExStyle = currentEx;
  581. // Set new window style and size.
  582. SetStyle(current & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME), false);
  583. SetExtendedStyle(currentEx & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE), false);
  584. // On expand, if we're given a window_rect, grow to it, otherwise do
  585. // not resize.
  586. MONITORINFO monitor_info = MONITORINFO.Create();
  587. GetMonitorInfo(MonitorFromWindow(_hwnd, MONITOR.MONITOR_DEFAULTTONEAREST), ref monitor_info);
  588. var window_rect = monitor_info.rcMonitor.ToPixelRect();
  589. SetWindowPos(_hwnd, IntPtr.Zero, window_rect.X, window_rect.Y,
  590. window_rect.Width, window_rect.Height,
  591. SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED);
  592. _isFullScreenActive = true;
  593. }
  594. else
  595. {
  596. // Reset original window style and size. The multiple window size/moves
  597. // here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
  598. // repainted. Better-looking methods welcome.
  599. _isFullScreenActive = false;
  600. var windowStates = GetWindowStateStyles();
  601. SetStyle((_savedWindowInfo.Style & ~WindowStateMask) | windowStates, false);
  602. SetExtendedStyle(_savedWindowInfo.ExStyle, false);
  603. // On restore, resize to the previous saved rect size.
  604. var new_rect = _savedWindowInfo.WindowRect.ToPixelRect();
  605. SetWindowPos(_hwnd, IntPtr.Zero, new_rect.X, new_rect.Y, new_rect.Width,
  606. new_rect.Height,
  607. SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED);
  608. UpdateWindowProperties(_windowProperties, true);
  609. }
  610. TaskBarList.MarkFullscreen(_hwnd, fullscreen);
  611. ExtendClientArea();
  612. }
  613. private MARGINS UpdateExtendMargins()
  614. {
  615. RECT borderThickness = new RECT();
  616. RECT borderCaptionThickness = new RECT();
  617. AdjustWindowRectEx(ref borderCaptionThickness, (uint)(GetStyle()), false, 0);
  618. AdjustWindowRectEx(ref borderThickness, (uint)(GetStyle() & ~WindowStyles.WS_CAPTION), false, 0);
  619. borderThickness.left *= -1;
  620. borderThickness.top *= -1;
  621. borderCaptionThickness.left *= -1;
  622. borderCaptionThickness.top *= -1;
  623. bool wantsTitleBar = _extendChromeHints.HasFlag(ExtendClientAreaChromeHints.SystemChrome) || _extendTitleBarHint == -1;
  624. if (!wantsTitleBar)
  625. {
  626. borderCaptionThickness.top = 1;
  627. }
  628. MARGINS margins = new MARGINS();
  629. margins.cxLeftWidth = 1;
  630. margins.cxRightWidth = 1;
  631. margins.cyBottomHeight = 1;
  632. if (_extendTitleBarHint != -1)
  633. {
  634. borderCaptionThickness.top = (int)(_extendTitleBarHint * RenderScaling);
  635. }
  636. margins.cyTopHeight = _extendChromeHints.HasFlag(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasFlag(ExtendClientAreaChromeHints.PreferSystemChrome) ? borderCaptionThickness.top : 1;
  637. if (WindowState == WindowState.Maximized)
  638. {
  639. _extendedMargins = new Thickness(0, (borderCaptionThickness.top - borderThickness.top) / RenderScaling, 0, 0);
  640. _offScreenMargin = new Thickness(borderThickness.left / RenderScaling, borderThickness.top / RenderScaling, borderThickness.right / RenderScaling, borderThickness.bottom / RenderScaling);
  641. }
  642. else
  643. {
  644. _extendedMargins = new Thickness(0, (borderCaptionThickness.top) / RenderScaling, 0, 0);
  645. _offScreenMargin = new Thickness();
  646. }
  647. return margins;
  648. }
  649. private void ExtendClientArea()
  650. {
  651. if (DwmIsCompositionEnabled(out bool compositionEnabled) < 0 || !compositionEnabled)
  652. {
  653. _isClientAreaExtended = false;
  654. return;
  655. }
  656. GetWindowRect(_hwnd, out var rcClient);
  657. // Inform the application of the frame change.
  658. SetWindowPos(_hwnd,
  659. IntPtr.Zero,
  660. rcClient.left, rcClient.top,
  661. rcClient.Width, rcClient.Height,
  662. SetWindowPosFlags.SWP_FRAMECHANGED);
  663. if (_isClientAreaExtended && WindowState != WindowState.FullScreen)
  664. {
  665. var margins = UpdateExtendMargins();
  666. DwmExtendFrameIntoClientArea(_hwnd, ref margins);
  667. }
  668. else
  669. {
  670. var margins = new MARGINS();
  671. DwmExtendFrameIntoClientArea(_hwnd, ref margins);
  672. _offScreenMargin = new Thickness();
  673. _extendedMargins = new Thickness();
  674. }
  675. if(!_isClientAreaExtended || (_extendChromeHints.HasFlag(ExtendClientAreaChromeHints.SystemChrome) &&
  676. !_extendChromeHints.HasFlag(ExtendClientAreaChromeHints.PreferSystemChrome)))
  677. {
  678. EnableCloseButton(_hwnd);
  679. }
  680. else
  681. {
  682. DisableCloseButton(_hwnd);
  683. }
  684. ExtendClientAreaToDecorationsChanged?.Invoke(_isClientAreaExtended);
  685. }
  686. private void ShowWindow(WindowState state)
  687. {
  688. ShowWindowCommand? command;
  689. var newWindowProperties = _windowProperties;
  690. switch (state)
  691. {
  692. case WindowState.Minimized:
  693. newWindowProperties.IsFullScreen = false;
  694. command = ShowWindowCommand.Minimize;
  695. break;
  696. case WindowState.Maximized:
  697. newWindowProperties.IsFullScreen = false;
  698. command = ShowWindowCommand.Maximize;
  699. break;
  700. case WindowState.Normal:
  701. newWindowProperties.IsFullScreen = false;
  702. command = ShowWindowCommand.Restore;
  703. break;
  704. case WindowState.FullScreen:
  705. newWindowProperties.IsFullScreen = true;
  706. command = IsWindowVisible(_hwnd) ? (ShowWindowCommand?)null : ShowWindowCommand.Restore;
  707. break;
  708. default:
  709. throw new ArgumentException("Invalid WindowState.");
  710. }
  711. UpdateWindowProperties(newWindowProperties);
  712. if (command.HasValue)
  713. {
  714. UnmanagedMethods.ShowWindow(_hwnd, command.Value);
  715. }
  716. if (state == WindowState.Maximized)
  717. {
  718. MaximizeWithoutCoveringTaskbar();
  719. }
  720. if (!Design.IsDesignMode)
  721. {
  722. SetFocus(_hwnd);
  723. }
  724. }
  725. private void MaximizeWithoutCoveringTaskbar()
  726. {
  727. IntPtr monitor = MonitorFromWindow(_hwnd, MONITOR.MONITOR_DEFAULTTONEAREST);
  728. if (monitor != IntPtr.Zero)
  729. {
  730. var monitorInfo = MONITORINFO.Create();
  731. if (GetMonitorInfo(monitor, ref monitorInfo))
  732. {
  733. var x = monitorInfo.rcWork.left;
  734. var y = monitorInfo.rcWork.top;
  735. var cx = Math.Abs(monitorInfo.rcWork.right - x);
  736. var cy = Math.Abs(monitorInfo.rcWork.bottom - y);
  737. SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW);
  738. }
  739. }
  740. }
  741. private WindowStyles GetWindowStateStyles()
  742. {
  743. return GetStyle() & WindowStateMask;
  744. }
  745. private WindowStyles GetStyle()
  746. {
  747. if (_isFullScreenActive)
  748. {
  749. return _savedWindowInfo.Style;
  750. }
  751. else
  752. {
  753. return (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE);
  754. }
  755. }
  756. private WindowStyles GetExtendedStyle()
  757. {
  758. if (_isFullScreenActive)
  759. {
  760. return _savedWindowInfo.ExStyle;
  761. }
  762. else
  763. {
  764. return (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE);
  765. }
  766. }
  767. private void SetStyle(WindowStyles style, bool save = true)
  768. {
  769. if (save)
  770. {
  771. _savedWindowInfo.Style = style;
  772. }
  773. if (!_isFullScreenActive)
  774. {
  775. SetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE, (uint)style);
  776. }
  777. }
  778. private void SetExtendedStyle(WindowStyles style, bool save = true)
  779. {
  780. if (save)
  781. {
  782. _savedWindowInfo.ExStyle = style;
  783. }
  784. if (!_isFullScreenActive)
  785. {
  786. SetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE, (uint)style);
  787. }
  788. }
  789. private void UpdateWindowProperties(WindowProperties newProperties, bool forceChanges = false)
  790. {
  791. var oldProperties = _windowProperties;
  792. // Calling SetWindowPos will cause events to be sent and we need to respond
  793. // according to the new values already.
  794. _windowProperties = newProperties;
  795. if ((oldProperties.ShowInTaskbar != newProperties.ShowInTaskbar) || forceChanges)
  796. {
  797. var exStyle = GetExtendedStyle();
  798. if (newProperties.ShowInTaskbar)
  799. {
  800. exStyle |= WindowStyles.WS_EX_APPWINDOW;
  801. }
  802. else
  803. {
  804. exStyle &= ~WindowStyles.WS_EX_APPWINDOW;
  805. }
  806. SetExtendedStyle(exStyle);
  807. // TODO: To hide non-owned window from taskbar we need to parent it to a hidden window.
  808. // Otherwise it will still show in the taskbar.
  809. }
  810. WindowStyles style;
  811. if ((oldProperties.IsResizable != newProperties.IsResizable) || forceChanges)
  812. {
  813. style = GetStyle();
  814. if (newProperties.IsResizable)
  815. {
  816. style |= WindowStyles.WS_SIZEFRAME;
  817. style |= WindowStyles.WS_MAXIMIZEBOX;
  818. }
  819. else
  820. {
  821. style &= ~WindowStyles.WS_SIZEFRAME;
  822. style &= ~WindowStyles.WS_MAXIMIZEBOX;
  823. }
  824. SetStyle(style);
  825. }
  826. if (oldProperties.IsFullScreen != newProperties.IsFullScreen)
  827. {
  828. SetFullScreen(newProperties.IsFullScreen);
  829. }
  830. if ((oldProperties.Decorations != newProperties.Decorations) || forceChanges)
  831. {
  832. style = GetStyle();
  833. const WindowStyles fullDecorationFlags = WindowStyles.WS_CAPTION | WindowStyles.WS_SYSMENU;
  834. if (newProperties.Decorations == SystemDecorations.Full)
  835. {
  836. style |= fullDecorationFlags;
  837. }
  838. else
  839. {
  840. style &= ~fullDecorationFlags;
  841. }
  842. SetStyle(style);
  843. if (!_isFullScreenActive)
  844. {
  845. var margin = newProperties.Decorations == SystemDecorations.BorderOnly ? 1 : 0;
  846. var margins = new MARGINS
  847. {
  848. cyBottomHeight = margin,
  849. cxRightWidth = margin,
  850. cxLeftWidth = margin,
  851. cyTopHeight = margin
  852. };
  853. DwmExtendFrameIntoClientArea(_hwnd, ref margins);
  854. GetClientRect(_hwnd, out var oldClientRect);
  855. var oldClientRectOrigin = new POINT();
  856. ClientToScreen(_hwnd, ref oldClientRectOrigin);
  857. oldClientRect.Offset(oldClientRectOrigin);
  858. var newRect = oldClientRect;
  859. if (newProperties.Decorations == SystemDecorations.Full)
  860. {
  861. AdjustWindowRectEx(ref newRect, (uint)style, false, (uint)GetExtendedStyle());
  862. }
  863. SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height,
  864. SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE |
  865. SetWindowPosFlags.SWP_FRAMECHANGED);
  866. }
  867. }
  868. }
  869. private const int MF_BYCOMMAND = 0x0;
  870. private const int MF_BYPOSITION = 0x400;
  871. private const int MF_REMOVE = 0x1000;
  872. private const int MF_ENABLED = 0x0;
  873. private const int MF_GRAYED = 0x1;
  874. private const int MF_DISABLED = 0x2;
  875. private const int SC_CLOSE = 0xF060;
  876. void DisableCloseButton(IntPtr hwnd)
  877. {
  878. EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE,
  879. MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
  880. }
  881. void EnableCloseButton(IntPtr hwnd)
  882. {
  883. EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE,
  884. MF_BYCOMMAND | MF_ENABLED);
  885. }
  886. #if USE_MANAGED_DRAG
  887. private Point ScreenToClient(Point point)
  888. {
  889. var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
  890. UnmanagedMethods.ScreenToClient(_hwnd, ref p);
  891. return new Point(p.X, p.Y);
  892. }
  893. #endif
  894. PixelSize EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Size
  895. {
  896. get
  897. {
  898. GetClientRect(_hwnd, out var rect);
  899. return new PixelSize(
  900. Math.Max(1, rect.right - rect.left),
  901. Math.Max(1, rect.bottom - rect.top));
  902. }
  903. }
  904. double EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Scaling => RenderScaling;
  905. IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle;
  906. public void SetExtendClientAreaToDecorationsHint(bool hint)
  907. {
  908. _isClientAreaExtended = hint;
  909. ExtendClientArea();
  910. }
  911. public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints)
  912. {
  913. _extendChromeHints = hints;
  914. ExtendClientArea();
  915. }
  916. /// <inheritdoc/>
  917. public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight)
  918. {
  919. _extendTitleBarHint = titleBarHeight;
  920. ExtendClientArea();
  921. }
  922. /// <inheritdoc/>
  923. public bool IsClientAreaExtendedToDecorations => _isClientAreaExtended;
  924. /// <inheritdoc/>
  925. public Action<bool> ExtendClientAreaToDecorationsChanged { get; set; }
  926. /// <inheritdoc/>
  927. public bool NeedsManagedDecorations => _isClientAreaExtended && _extendChromeHints.HasFlag(ExtendClientAreaChromeHints.PreferSystemChrome);
  928. /// <inheritdoc/>
  929. public Thickness ExtendedMargins => _extendedMargins;
  930. /// <inheritdoc/>
  931. public Thickness OffScreenMargin => _offScreenMargin;
  932. /// <inheritdoc/>
  933. public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0);
  934. private struct SavedWindowInfo
  935. {
  936. public WindowStyles Style { get; set; }
  937. public WindowStyles ExStyle { get; set; }
  938. public RECT WindowRect { get; set; }
  939. };
  940. private struct WindowProperties
  941. {
  942. public bool ShowInTaskbar;
  943. public bool IsResizable;
  944. public SystemDecorations Decorations;
  945. public bool IsFullScreen;
  946. }
  947. }
  948. }