WindowImpl.cs 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.ComponentModel;
  6. using System.Diagnostics.CodeAnalysis;
  7. using System.Runtime.InteropServices;
  8. using Avalonia.Controls;
  9. using Avalonia.Input;
  10. using Avalonia.Input.Raw;
  11. using Avalonia.OpenGL;
  12. using Avalonia.Platform;
  13. using Avalonia.Rendering;
  14. using Avalonia.Win32.Input;
  15. using Avalonia.Win32.Interop;
  16. using static Avalonia.Win32.Interop.UnmanagedMethods;
  17. namespace Avalonia.Win32
  18. {
  19. public class WindowImpl : IWindowImpl, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
  20. {
  21. private static readonly List<WindowImpl> s_instances = new List<WindowImpl>();
  22. private static readonly IntPtr DefaultCursor = UnmanagedMethods.LoadCursor(
  23. IntPtr.Zero, new IntPtr((int)UnmanagedMethods.Cursor.IDC_ARROW));
  24. private UnmanagedMethods.WndProc _wndProcDelegate;
  25. private string _className;
  26. private IntPtr _hwnd;
  27. private bool _multitouch;
  28. private TouchDevice _touchDevice = new TouchDevice();
  29. private MouseDevice _mouseDevice = new WindowsMouseDevice();
  30. private IInputRoot _owner;
  31. private ManagedDeferredRendererLock _rendererLock = new ManagedDeferredRendererLock();
  32. private bool _trackingMouse;
  33. private bool _decorated = true;
  34. private bool _resizable = true;
  35. private bool _topmost = false;
  36. private bool _taskbarIcon = true;
  37. private double _scaling = 1;
  38. private WindowState _showWindowState;
  39. private WindowState _lastWindowState;
  40. private FramebufferManager _framebuffer;
  41. private IGlPlatformSurface _gl;
  42. private OleDropTarget _dropTarget;
  43. private Size _minSize;
  44. private Size _maxSize;
  45. private WindowImpl _parent;
  46. private readonly List<WindowImpl> _disabledBy = new List<WindowImpl>();
  47. #if USE_MANAGED_DRAG
  48. private readonly ManagedWindowResizeDragHelper _managedDrag;
  49. #endif
  50. public WindowImpl()
  51. {
  52. #if USE_MANAGED_DRAG
  53. _managedDrag = new ManagedWindowResizeDragHelper(this, capture =>
  54. {
  55. if (capture)
  56. UnmanagedMethods.SetCapture(Handle.Handle);
  57. else
  58. UnmanagedMethods.ReleaseCapture();
  59. });
  60. #endif
  61. CreateWindow();
  62. _framebuffer = new FramebufferManager(_hwnd);
  63. if (Win32GlManager.EglFeature != null)
  64. _gl = new EglGlPlatformSurface((EglDisplay)Win32GlManager.EglFeature.Display,
  65. Win32GlManager.EglFeature.DeferredContext, this);
  66. s_instances.Add(this);
  67. }
  68. public Action Activated { get; set; }
  69. public Func<bool> Closing { get; set; }
  70. public Action Closed { get; set; }
  71. public Action Deactivated { get; set; }
  72. public Action<RawInputEventArgs> Input { get; set; }
  73. public Action<Rect> Paint { get; set; }
  74. public Action<Size> Resized { get; set; }
  75. public Action<double> ScalingChanged { get; set; }
  76. public Action<PixelPoint> PositionChanged { get; set; }
  77. public Action<WindowState> WindowStateChanged { get; set; }
  78. public Thickness BorderThickness
  79. {
  80. get
  81. {
  82. if (_decorated)
  83. {
  84. var style = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE);
  85. var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE);
  86. var padding = new RECT();
  87. if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle))
  88. {
  89. return new Thickness(-padding.left, -padding.top, padding.right, padding.bottom);
  90. }
  91. else
  92. {
  93. throw new Win32Exception();
  94. }
  95. }
  96. else
  97. {
  98. return new Thickness();
  99. }
  100. }
  101. }
  102. public Size ClientSize
  103. {
  104. get
  105. {
  106. UnmanagedMethods.RECT rect;
  107. UnmanagedMethods.GetClientRect(_hwnd, out rect);
  108. return new Size(rect.right, rect.bottom) / Scaling;
  109. }
  110. }
  111. public void Move(PixelPoint point) => Position = point;
  112. public void SetMinMaxSize(Size minSize, Size maxSize)
  113. {
  114. _minSize = minSize;
  115. _maxSize = maxSize;
  116. }
  117. public IScreenImpl Screen
  118. {
  119. get;
  120. } = new ScreenImpl();
  121. public IRenderer CreateRenderer(IRenderRoot root)
  122. {
  123. var loop = AvaloniaLocator.Current.GetService<IRenderLoop>();
  124. var customRendererFactory = AvaloniaLocator.Current.GetService<IRendererFactory>();
  125. if (customRendererFactory != null)
  126. return customRendererFactory.Create(root, loop);
  127. return Win32Platform.UseDeferredRendering ?
  128. (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock) :
  129. new ImmediateRenderer(root);
  130. }
  131. public void Resize(Size value)
  132. {
  133. int requestedClientWidth = (int)(value.Width * Scaling);
  134. int requestedClientHeight = (int)(value.Height * Scaling);
  135. UnmanagedMethods.RECT clientRect;
  136. UnmanagedMethods.GetClientRect(_hwnd, out clientRect);
  137. // do comparison after scaling to avoid rounding issues
  138. if (requestedClientWidth != clientRect.Width || requestedClientHeight != clientRect.Height)
  139. {
  140. UnmanagedMethods.RECT windowRect;
  141. UnmanagedMethods.GetWindowRect(_hwnd, out windowRect);
  142. UnmanagedMethods.SetWindowPos(
  143. _hwnd,
  144. IntPtr.Zero,
  145. 0,
  146. 0,
  147. requestedClientWidth + (windowRect.Width - clientRect.Width),
  148. requestedClientHeight + (windowRect.Height - clientRect.Height),
  149. UnmanagedMethods.SetWindowPosFlags.SWP_RESIZE);
  150. }
  151. }
  152. public double Scaling => _scaling;
  153. public IPlatformHandle Handle
  154. {
  155. get;
  156. private set;
  157. }
  158. void UpdateEnabled()
  159. {
  160. EnableWindow(_hwnd, _disabledBy.Count == 0);
  161. }
  162. public Size MaxClientSize
  163. {
  164. get
  165. {
  166. return (new Size(
  167. UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CXMAXTRACK),
  168. UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CYMAXTRACK))
  169. - BorderThickness) / Scaling;
  170. }
  171. }
  172. public IMouseDevice MouseDevice => _mouseDevice;
  173. public WindowState WindowState
  174. {
  175. get
  176. {
  177. var placement = default(UnmanagedMethods.WINDOWPLACEMENT);
  178. UnmanagedMethods.GetWindowPlacement(_hwnd, ref placement);
  179. switch (placement.ShowCmd)
  180. {
  181. case UnmanagedMethods.ShowWindowCommand.Maximize:
  182. return WindowState.Maximized;
  183. case UnmanagedMethods.ShowWindowCommand.Minimize:
  184. return WindowState.Minimized;
  185. default:
  186. return WindowState.Normal;
  187. }
  188. }
  189. set
  190. {
  191. if (UnmanagedMethods.IsWindowVisible(_hwnd))
  192. {
  193. ShowWindow(value);
  194. }
  195. else
  196. {
  197. _showWindowState = value;
  198. }
  199. }
  200. }
  201. public IEnumerable<object> Surfaces => new object[]
  202. {
  203. Handle, _gl, _framebuffer
  204. };
  205. public void Activate()
  206. {
  207. UnmanagedMethods.SetActiveWindow(_hwnd);
  208. }
  209. public IPopupImpl CreatePopup() => Win32Platform.UseOverlayPopups ? null : new PopupImpl(this);
  210. public void Dispose()
  211. {
  212. if (_dropTarget != null)
  213. {
  214. OleContext.Current?.UnregisterDragDrop(Handle);
  215. _dropTarget = null;
  216. }
  217. if (_hwnd != IntPtr.Zero)
  218. {
  219. UnmanagedMethods.DestroyWindow(_hwnd);
  220. _hwnd = IntPtr.Zero;
  221. }
  222. if (_className != null)
  223. {
  224. UnmanagedMethods.UnregisterClass(_className, UnmanagedMethods.GetModuleHandle(null));
  225. _className = null;
  226. }
  227. }
  228. public void Hide()
  229. {
  230. if (_parent != null)
  231. {
  232. _parent._disabledBy.Remove(this);
  233. _parent.UpdateEnabled();
  234. _parent = null;
  235. }
  236. UnmanagedMethods.ShowWindow(_hwnd, UnmanagedMethods.ShowWindowCommand.Hide);
  237. }
  238. public void SetSystemDecorations(bool value)
  239. {
  240. if (value == _decorated)
  241. {
  242. return;
  243. }
  244. UpdateWMStyles(()=> _decorated = value);
  245. }
  246. public void Invalidate(Rect rect)
  247. {
  248. var f = Scaling;
  249. var r = new UnmanagedMethods.RECT
  250. {
  251. left = (int)Math.Floor(rect.X * f),
  252. top = (int)Math.Floor(rect.Y * f),
  253. right = (int)Math.Ceiling(rect.Right * f),
  254. bottom = (int)Math.Ceiling(rect.Bottom * f),
  255. };
  256. UnmanagedMethods.InvalidateRect(_hwnd, ref r, false);
  257. }
  258. public Point PointToClient(PixelPoint point)
  259. {
  260. var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
  261. UnmanagedMethods.ScreenToClient(_hwnd, ref p);
  262. return new Point(p.X, p.Y) / Scaling;
  263. }
  264. public PixelPoint PointToScreen(Point point)
  265. {
  266. point *= Scaling;
  267. var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
  268. UnmanagedMethods.ClientToScreen(_hwnd, ref p);
  269. return new PixelPoint(p.X, p.Y);
  270. }
  271. public void SetInputRoot(IInputRoot inputRoot)
  272. {
  273. _owner = inputRoot;
  274. CreateDropTarget();
  275. }
  276. public void SetTitle(string title)
  277. {
  278. UnmanagedMethods.SetWindowText(_hwnd, title);
  279. }
  280. public virtual void Show()
  281. {
  282. SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, IntPtr.Zero);
  283. ShowWindow(_showWindowState);
  284. }
  285. public void BeginMoveDrag(PointerPressedEventArgs e)
  286. {
  287. _mouseDevice.Capture(null);
  288. UnmanagedMethods.DefWindowProc(_hwnd, (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN,
  289. new IntPtr((int)UnmanagedMethods.HitTestValues.HTCAPTION), IntPtr.Zero);
  290. e.Pointer.Capture(null);
  291. }
  292. static readonly Dictionary<WindowEdge, UnmanagedMethods.HitTestValues> EdgeDic = new Dictionary<WindowEdge, UnmanagedMethods.HitTestValues>
  293. {
  294. {WindowEdge.East, UnmanagedMethods.HitTestValues.HTRIGHT},
  295. {WindowEdge.North, UnmanagedMethods.HitTestValues.HTTOP },
  296. {WindowEdge.NorthEast, UnmanagedMethods.HitTestValues.HTTOPRIGHT },
  297. {WindowEdge.NorthWest, UnmanagedMethods.HitTestValues.HTTOPLEFT },
  298. {WindowEdge.South, UnmanagedMethods.HitTestValues.HTBOTTOM },
  299. {WindowEdge.SouthEast, UnmanagedMethods.HitTestValues.HTBOTTOMRIGHT },
  300. {WindowEdge.SouthWest, UnmanagedMethods.HitTestValues.HTBOTTOMLEFT },
  301. {WindowEdge.West, UnmanagedMethods.HitTestValues.HTLEFT}
  302. };
  303. public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e)
  304. {
  305. #if USE_MANAGED_DRAG
  306. _managedDrag.BeginResizeDrag(edge, ScreenToClient(MouseDevice.Position));
  307. #else
  308. _mouseDevice.Capture(null);
  309. UnmanagedMethods.DefWindowProc(_hwnd, (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN,
  310. new IntPtr((int)EdgeDic[edge]), IntPtr.Zero);
  311. #endif
  312. }
  313. public PixelPoint Position
  314. {
  315. get
  316. {
  317. UnmanagedMethods.RECT rc;
  318. UnmanagedMethods.GetWindowRect(_hwnd, out rc);
  319. return new PixelPoint(rc.left, rc.top);
  320. }
  321. set
  322. {
  323. UnmanagedMethods.SetWindowPos(
  324. Handle.Handle,
  325. IntPtr.Zero,
  326. value.X,
  327. value.Y,
  328. 0,
  329. 0,
  330. UnmanagedMethods.SetWindowPosFlags.SWP_NOSIZE | UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE);
  331. }
  332. }
  333. public void ShowDialog(IWindowImpl parent)
  334. {
  335. _parent = (WindowImpl)parent;
  336. _parent._disabledBy.Add(this);
  337. _parent.UpdateEnabled();
  338. SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, ((WindowImpl)parent)._hwnd);
  339. ShowWindow(_showWindowState);
  340. }
  341. public void SetCursor(IPlatformHandle cursor)
  342. {
  343. var hCursor = cursor?.Handle ?? DefaultCursor;
  344. UnmanagedMethods.SetClassLong(_hwnd, UnmanagedMethods.ClassLongIndex.GCLP_HCURSOR, hCursor);
  345. if (_owner.IsPointerOver)
  346. UnmanagedMethods.SetCursor(hCursor);
  347. }
  348. protected virtual IntPtr CreateWindowOverride(ushort atom)
  349. {
  350. return UnmanagedMethods.CreateWindowEx(
  351. 0,
  352. atom,
  353. null,
  354. (int)UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW,
  355. UnmanagedMethods.CW_USEDEFAULT,
  356. UnmanagedMethods.CW_USEDEFAULT,
  357. UnmanagedMethods.CW_USEDEFAULT,
  358. UnmanagedMethods.CW_USEDEFAULT,
  359. IntPtr.Zero,
  360. IntPtr.Zero,
  361. IntPtr.Zero,
  362. IntPtr.Zero);
  363. }
  364. bool ShouldIgnoreTouchEmulatedMessage()
  365. {
  366. if (!_multitouch)
  367. return false;
  368. var marker = 0xFF515700L;
  369. var info = GetMessageExtraInfo().ToInt64();
  370. return (info & marker) == marker;
  371. }
  372. [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")]
  373. protected virtual unsafe IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
  374. {
  375. bool unicode = UnmanagedMethods.IsWindowUnicode(hWnd);
  376. const double wheelDelta = 120.0;
  377. uint timestamp = unchecked((uint)UnmanagedMethods.GetMessageTime());
  378. RawInputEventArgs e = null;
  379. switch ((UnmanagedMethods.WindowsMessage)msg)
  380. {
  381. case UnmanagedMethods.WindowsMessage.WM_ACTIVATE:
  382. var wa = (UnmanagedMethods.WindowActivate)(ToInt32(wParam) & 0xffff);
  383. switch (wa)
  384. {
  385. case UnmanagedMethods.WindowActivate.WA_ACTIVE:
  386. case UnmanagedMethods.WindowActivate.WA_CLICKACTIVE:
  387. Activated?.Invoke();
  388. break;
  389. case UnmanagedMethods.WindowActivate.WA_INACTIVE:
  390. Deactivated?.Invoke();
  391. break;
  392. }
  393. return IntPtr.Zero;
  394. case WindowsMessage.WM_NCCALCSIZE:
  395. if (ToInt32(wParam) == 1 && !_decorated)
  396. {
  397. return IntPtr.Zero;
  398. }
  399. break;
  400. case UnmanagedMethods.WindowsMessage.WM_CLOSE:
  401. bool? preventClosing = Closing?.Invoke();
  402. if (preventClosing == true)
  403. {
  404. return IntPtr.Zero;
  405. }
  406. break;
  407. case UnmanagedMethods.WindowsMessage.WM_DESTROY:
  408. //Window doesn't exist anymore
  409. _hwnd = IntPtr.Zero;
  410. //Remove root reference to this class, so unmanaged delegate can be collected
  411. s_instances.Remove(this);
  412. Closed?.Invoke();
  413. if (_parent != null)
  414. {
  415. _parent._disabledBy.Remove(this);
  416. _parent.UpdateEnabled();
  417. }
  418. _mouseDevice.Dispose();
  419. _touchDevice?.Dispose();
  420. //Free other resources
  421. Dispose();
  422. return IntPtr.Zero;
  423. case UnmanagedMethods.WindowsMessage.WM_DPICHANGED:
  424. var dpi = ToInt32(wParam) & 0xffff;
  425. var newDisplayRect = Marshal.PtrToStructure<UnmanagedMethods.RECT>(lParam);
  426. _scaling = dpi / 96.0;
  427. ScalingChanged?.Invoke(_scaling);
  428. SetWindowPos(hWnd,
  429. IntPtr.Zero,
  430. newDisplayRect.left,
  431. newDisplayRect.top,
  432. newDisplayRect.right - newDisplayRect.left,
  433. newDisplayRect.bottom - newDisplayRect.top,
  434. SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE);
  435. return IntPtr.Zero;
  436. case UnmanagedMethods.WindowsMessage.WM_KEYDOWN:
  437. case UnmanagedMethods.WindowsMessage.WM_SYSKEYDOWN:
  438. e = new RawKeyEventArgs(
  439. WindowsKeyboardDevice.Instance,
  440. timestamp,
  441. _owner,
  442. RawKeyEventType.KeyDown,
  443. KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam)), WindowsKeyboardDevice.Instance.Modifiers);
  444. break;
  445. case UnmanagedMethods.WindowsMessage.WM_MENUCHAR:
  446. // mute the system beep
  447. return (IntPtr)((Int32)UnmanagedMethods.MenuCharParam.MNC_CLOSE << 16);
  448. case UnmanagedMethods.WindowsMessage.WM_KEYUP:
  449. case UnmanagedMethods.WindowsMessage.WM_SYSKEYUP:
  450. e = new RawKeyEventArgs(
  451. WindowsKeyboardDevice.Instance,
  452. timestamp,
  453. _owner,
  454. RawKeyEventType.KeyUp,
  455. KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam)), WindowsKeyboardDevice.Instance.Modifiers);
  456. break;
  457. case UnmanagedMethods.WindowsMessage.WM_CHAR:
  458. // Ignore control chars
  459. if (ToInt32(wParam) >= 32)
  460. {
  461. e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp, _owner,
  462. new string((char)ToInt32(wParam), 1));
  463. }
  464. break;
  465. case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN:
  466. case UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN:
  467. case UnmanagedMethods.WindowsMessage.WM_MBUTTONDOWN:
  468. case UnmanagedMethods.WindowsMessage.WM_XBUTTONDOWN:
  469. if (ShouldIgnoreTouchEmulatedMessage())
  470. break;
  471. e = new RawPointerEventArgs(
  472. _mouseDevice,
  473. timestamp,
  474. _owner,
  475. (UnmanagedMethods.WindowsMessage)msg switch
  476. {
  477. UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN => RawPointerEventType.LeftButtonDown,
  478. UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN => RawPointerEventType.RightButtonDown,
  479. UnmanagedMethods.WindowsMessage.WM_MBUTTONDOWN => RawPointerEventType.MiddleButtonDown,
  480. UnmanagedMethods.WindowsMessage.WM_XBUTTONDOWN =>
  481. HighWord(ToInt32(wParam)) == 1 ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton2Down
  482. },
  483. DipFromLParam(lParam), GetMouseModifiers(wParam));
  484. break;
  485. case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP:
  486. case UnmanagedMethods.WindowsMessage.WM_RBUTTONUP:
  487. case UnmanagedMethods.WindowsMessage.WM_MBUTTONUP:
  488. case UnmanagedMethods.WindowsMessage.WM_XBUTTONUP:
  489. if (ShouldIgnoreTouchEmulatedMessage())
  490. break;
  491. e = new RawPointerEventArgs(
  492. _mouseDevice,
  493. timestamp,
  494. _owner,
  495. (UnmanagedMethods.WindowsMessage)msg switch
  496. {
  497. UnmanagedMethods.WindowsMessage.WM_LBUTTONUP => RawPointerEventType.LeftButtonUp,
  498. UnmanagedMethods.WindowsMessage.WM_RBUTTONUP => RawPointerEventType.RightButtonUp,
  499. UnmanagedMethods.WindowsMessage.WM_MBUTTONUP => RawPointerEventType.MiddleButtonUp,
  500. UnmanagedMethods.WindowsMessage.WM_XBUTTONUP =>
  501. HighWord(ToInt32(wParam)) == 1 ? RawPointerEventType.XButton1Up : RawPointerEventType.XButton2Up,
  502. },
  503. DipFromLParam(lParam), GetMouseModifiers(wParam));
  504. break;
  505. case UnmanagedMethods.WindowsMessage.WM_MOUSEMOVE:
  506. if(ShouldIgnoreTouchEmulatedMessage())
  507. break;
  508. if (!_trackingMouse)
  509. {
  510. var tm = new UnmanagedMethods.TRACKMOUSEEVENT
  511. {
  512. cbSize = Marshal.SizeOf<UnmanagedMethods.TRACKMOUSEEVENT>(),
  513. dwFlags = 2,
  514. hwndTrack = _hwnd,
  515. dwHoverTime = 0,
  516. };
  517. UnmanagedMethods.TrackMouseEvent(ref tm);
  518. }
  519. e = new RawPointerEventArgs(
  520. _mouseDevice,
  521. timestamp,
  522. _owner,
  523. RawPointerEventType.Move,
  524. DipFromLParam(lParam), GetMouseModifiers(wParam));
  525. break;
  526. case UnmanagedMethods.WindowsMessage.WM_MOUSEWHEEL:
  527. e = new RawMouseWheelEventArgs(
  528. _mouseDevice,
  529. timestamp,
  530. _owner,
  531. PointToClient(PointFromLParam(lParam)),
  532. new Vector(0, (ToInt32(wParam) >> 16) / wheelDelta), GetMouseModifiers(wParam));
  533. break;
  534. case UnmanagedMethods.WindowsMessage.WM_MOUSEHWHEEL:
  535. e = new RawMouseWheelEventArgs(
  536. _mouseDevice,
  537. timestamp,
  538. _owner,
  539. PointToClient(PointFromLParam(lParam)),
  540. new Vector(-(ToInt32(wParam) >> 16) / wheelDelta, 0), GetMouseModifiers(wParam));
  541. break;
  542. case UnmanagedMethods.WindowsMessage.WM_MOUSELEAVE:
  543. _trackingMouse = false;
  544. e = new RawPointerEventArgs(
  545. _mouseDevice,
  546. timestamp,
  547. _owner,
  548. RawPointerEventType.LeaveWindow,
  549. new Point(-1,-1), WindowsKeyboardDevice.Instance.Modifiers);
  550. break;
  551. case UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN:
  552. case UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN:
  553. case UnmanagedMethods.WindowsMessage.WM_NCMBUTTONDOWN:
  554. case UnmanagedMethods.WindowsMessage.WM_NCXBUTTONDOWN:
  555. e = new RawPointerEventArgs(
  556. _mouseDevice,
  557. timestamp,
  558. _owner,
  559. (UnmanagedMethods.WindowsMessage)msg switch
  560. {
  561. UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType.NonClientLeftButtonDown,
  562. UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN => RawPointerEventType.RightButtonDown,
  563. UnmanagedMethods.WindowsMessage.WM_NCMBUTTONDOWN => RawPointerEventType.MiddleButtonDown,
  564. UnmanagedMethods.WindowsMessage.WM_NCXBUTTONDOWN =>
  565. HighWord(ToInt32(wParam)) == 1 ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton2Down,
  566. },
  567. PointToClient(PointFromLParam(lParam)), GetMouseModifiers(wParam));
  568. break;
  569. case WindowsMessage.WM_TOUCH:
  570. var touchInputCount = wParam.ToInt32();
  571. var pTouchInputs = stackalloc TOUCHINPUT[touchInputCount];
  572. var touchInputs = new Span<TOUCHINPUT>(pTouchInputs, touchInputCount);
  573. if (GetTouchInputInfo(lParam, (uint)touchInputCount, pTouchInputs, Marshal.SizeOf<TOUCHINPUT>()))
  574. {
  575. foreach (var touchInput in touchInputs)
  576. {
  577. Input?.Invoke(new RawTouchEventArgs(_touchDevice, touchInput.Time,
  578. _owner,
  579. touchInput.Flags.HasFlagCustom(TouchInputFlags.TOUCHEVENTF_UP) ?
  580. RawPointerEventType.TouchEnd :
  581. touchInput.Flags.HasFlagCustom(TouchInputFlags.TOUCHEVENTF_DOWN) ?
  582. RawPointerEventType.TouchBegin :
  583. RawPointerEventType.TouchUpdate,
  584. PointToClient(new PixelPoint(touchInput.X / 100, touchInput.Y / 100)),
  585. WindowsKeyboardDevice.Instance.Modifiers,
  586. touchInput.Id));
  587. }
  588. CloseTouchInputHandle(lParam);
  589. return IntPtr.Zero;
  590. }
  591. break;
  592. case WindowsMessage.WM_NCPAINT:
  593. if (!_decorated)
  594. {
  595. return IntPtr.Zero;
  596. }
  597. break;
  598. case WindowsMessage.WM_NCACTIVATE:
  599. if (!_decorated)
  600. {
  601. return new IntPtr(1);
  602. }
  603. break;
  604. case UnmanagedMethods.WindowsMessage.WM_PAINT:
  605. using (_rendererLock.Lock())
  606. {
  607. UnmanagedMethods.PAINTSTRUCT ps;
  608. if (UnmanagedMethods.BeginPaint(_hwnd, out ps) != IntPtr.Zero)
  609. {
  610. var f = Scaling;
  611. var r = ps.rcPaint;
  612. Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f,
  613. (r.bottom - r.top) / f));
  614. UnmanagedMethods.EndPaint(_hwnd, ref ps);
  615. }
  616. }
  617. return IntPtr.Zero;
  618. case UnmanagedMethods.WindowsMessage.WM_SIZE:
  619. using (_rendererLock.Lock())
  620. {
  621. // Do nothing here, just block until the pending frame render is completed on the render thread
  622. }
  623. var size = (UnmanagedMethods.SizeCommand)wParam;
  624. if (Resized != null &&
  625. (size == UnmanagedMethods.SizeCommand.Restored ||
  626. size == UnmanagedMethods.SizeCommand.Maximized))
  627. {
  628. var clientSize = new Size(ToInt32(lParam) & 0xffff, ToInt32(lParam) >> 16);
  629. Resized(clientSize / Scaling);
  630. }
  631. var windowState = size == SizeCommand.Maximized ? WindowState.Maximized
  632. : (size == SizeCommand.Minimized ? WindowState.Minimized : WindowState.Normal);
  633. if (windowState != _lastWindowState)
  634. {
  635. _lastWindowState = windowState;
  636. WindowStateChanged?.Invoke(windowState);
  637. }
  638. return IntPtr.Zero;
  639. case UnmanagedMethods.WindowsMessage.WM_MOVE:
  640. PositionChanged?.Invoke(new PixelPoint((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)));
  641. return IntPtr.Zero;
  642. case UnmanagedMethods.WindowsMessage.WM_GETMINMAXINFO:
  643. MINMAXINFO mmi = Marshal.PtrToStructure<UnmanagedMethods.MINMAXINFO>(lParam);
  644. if (_minSize.Width > 0)
  645. mmi.ptMinTrackSize.X = (int)((_minSize.Width * Scaling) + BorderThickness.Left + BorderThickness.Right);
  646. if (_minSize.Height > 0)
  647. mmi.ptMinTrackSize.Y = (int)((_minSize.Height * Scaling) + BorderThickness.Top + BorderThickness.Bottom);
  648. if (!Double.IsInfinity(_maxSize.Width) && _maxSize.Width > 0)
  649. mmi.ptMaxTrackSize.X = (int)((_maxSize.Width * Scaling) + BorderThickness.Left + BorderThickness.Right);
  650. if (!Double.IsInfinity(_maxSize.Height) && _maxSize.Height > 0)
  651. mmi.ptMaxTrackSize.Y = (int)((_maxSize.Height * Scaling) + BorderThickness.Top + BorderThickness.Bottom);
  652. Marshal.StructureToPtr(mmi, lParam, true);
  653. return IntPtr.Zero;
  654. case UnmanagedMethods.WindowsMessage.WM_DISPLAYCHANGE:
  655. (Screen as ScreenImpl)?.InvalidateScreensCache();
  656. return IntPtr.Zero;
  657. }
  658. #if USE_MANAGED_DRAG
  659. if (_managedDrag.PreprocessInputEvent(ref e))
  660. return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
  661. #endif
  662. if (e != null && Input != null)
  663. {
  664. Input(e);
  665. if (e.Handled)
  666. {
  667. return IntPtr.Zero;
  668. }
  669. }
  670. using (_rendererLock.Lock())
  671. return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
  672. }
  673. static RawInputModifiers GetMouseModifiers(IntPtr wParam)
  674. {
  675. var keys = (UnmanagedMethods.ModifierKeys)ToInt32(wParam);
  676. var modifiers = WindowsKeyboardDevice.Instance.Modifiers;
  677. if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_LBUTTON))
  678. modifiers |= RawInputModifiers.LeftMouseButton;
  679. if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_RBUTTON))
  680. modifiers |= RawInputModifiers.RightMouseButton;
  681. if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_MBUTTON))
  682. modifiers |= RawInputModifiers.MiddleMouseButton;
  683. if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_XBUTTON1))
  684. modifiers |= RawInputModifiers.XButton1MouseButton;
  685. if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_XBUTTON2))
  686. modifiers |= RawInputModifiers.XButton2MouseButton;
  687. return modifiers;
  688. }
  689. private void CreateWindow()
  690. {
  691. // Ensure that the delegate doesn't get garbage collected by storing it as a field.
  692. _wndProcDelegate = new UnmanagedMethods.WndProc(WndProc);
  693. _className = $"Avalonia-{Guid.NewGuid().ToString()}";
  694. UnmanagedMethods.WNDCLASSEX wndClassEx = new UnmanagedMethods.WNDCLASSEX
  695. {
  696. cbSize = Marshal.SizeOf<UnmanagedMethods.WNDCLASSEX>(),
  697. style = (int)(ClassStyles.CS_OWNDC | ClassStyles.CS_HREDRAW | ClassStyles.CS_VREDRAW), // Unique DC helps with performance when using Gpu based rendering
  698. lpfnWndProc = _wndProcDelegate,
  699. hInstance = UnmanagedMethods.GetModuleHandle(null),
  700. hCursor = DefaultCursor,
  701. hbrBackground = IntPtr.Zero,
  702. lpszClassName = _className
  703. };
  704. ushort atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx);
  705. if (atom == 0)
  706. {
  707. throw new Win32Exception();
  708. }
  709. _hwnd = CreateWindowOverride(atom);
  710. if (_hwnd == IntPtr.Zero)
  711. {
  712. throw new Win32Exception();
  713. }
  714. Handle = new PlatformHandle(_hwnd, PlatformConstants.WindowHandleType);
  715. _multitouch = Win32Platform.Options.EnableMultitouch ?? false;
  716. if (_multitouch)
  717. RegisterTouchWindow(_hwnd, 0);
  718. if (UnmanagedMethods.ShCoreAvailable)
  719. {
  720. uint dpix, dpiy;
  721. var monitor = UnmanagedMethods.MonitorFromWindow(
  722. _hwnd,
  723. UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
  724. if (UnmanagedMethods.GetDpiForMonitor(
  725. monitor,
  726. UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
  727. out dpix,
  728. out dpiy) == 0)
  729. {
  730. _scaling = dpix / 96.0;
  731. }
  732. }
  733. }
  734. private void CreateDropTarget()
  735. {
  736. OleDropTarget odt = new OleDropTarget(this, _owner);
  737. if (OleContext.Current?.RegisterDragDrop(Handle, odt) ?? false)
  738. _dropTarget = odt;
  739. }
  740. private Point DipFromLParam(IntPtr lParam)
  741. {
  742. return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / Scaling;
  743. }
  744. private PixelPoint PointFromLParam(IntPtr lParam)
  745. {
  746. return new PixelPoint((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16));
  747. }
  748. private Point ScreenToClient(Point point)
  749. {
  750. var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
  751. UnmanagedMethods.ScreenToClient(_hwnd, ref p);
  752. return new Point(p.X, p.Y);
  753. }
  754. private void ShowWindow(WindowState state)
  755. {
  756. UnmanagedMethods.ShowWindowCommand command;
  757. switch (state)
  758. {
  759. case WindowState.Minimized:
  760. command = ShowWindowCommand.Minimize;
  761. break;
  762. case WindowState.Maximized:
  763. command = ShowWindowCommand.Maximize;
  764. break;
  765. case WindowState.Normal:
  766. command = ShowWindowCommand.Restore;
  767. break;
  768. default:
  769. throw new ArgumentException("Invalid WindowState.");
  770. }
  771. UnmanagedMethods.ShowWindow(_hwnd, command);
  772. if (state == WindowState.Maximized)
  773. {
  774. MaximizeWithoutCoveringTaskbar();
  775. }
  776. if (!Design.IsDesignMode)
  777. {
  778. SetFocus(_hwnd);
  779. }
  780. }
  781. private void MaximizeWithoutCoveringTaskbar()
  782. {
  783. IntPtr monitor = MonitorFromWindow(_hwnd, MONITOR.MONITOR_DEFAULTTONEAREST);
  784. if (monitor != IntPtr.Zero)
  785. {
  786. MONITORINFO monitorInfo = MONITORINFO.Create();
  787. if (GetMonitorInfo(monitor, ref monitorInfo))
  788. {
  789. RECT rcMonitorArea = monitorInfo.rcMonitor;
  790. var x = monitorInfo.rcWork.left;
  791. var y = monitorInfo.rcWork.top;
  792. var cx = Math.Abs(monitorInfo.rcWork.right - x);
  793. var cy = Math.Abs(monitorInfo.rcWork.bottom - y);
  794. SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW);
  795. }
  796. }
  797. }
  798. public void SetIcon(IWindowIconImpl icon)
  799. {
  800. var impl = (IconImpl)icon;
  801. var hIcon = impl?.HIcon ?? IntPtr.Zero;
  802. UnmanagedMethods.PostMessage(_hwnd, (int)UnmanagedMethods.WindowsMessage.WM_SETICON,
  803. new IntPtr((int)UnmanagedMethods.Icons.ICON_BIG), hIcon);
  804. }
  805. private static int ToInt32(IntPtr ptr)
  806. {
  807. if (IntPtr.Size == 4) return ptr.ToInt32();
  808. return (int)(ptr.ToInt64() & 0xffffffff);
  809. }
  810. public void ShowTaskbarIcon(bool value)
  811. {
  812. if (_taskbarIcon == value)
  813. {
  814. return;
  815. }
  816. _taskbarIcon = value;
  817. var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE);
  818. style &= ~(UnmanagedMethods.WindowStyles.WS_VISIBLE);
  819. style |= UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW;
  820. if (value)
  821. style |= UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW;
  822. else
  823. style &= ~(UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW);
  824. WINDOWPLACEMENT windowPlacement = UnmanagedMethods.WINDOWPLACEMENT.Default;
  825. if (UnmanagedMethods.GetWindowPlacement(_hwnd, ref windowPlacement))
  826. {
  827. //Toggle to make the styles stick
  828. UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide);
  829. UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE, (uint)style);
  830. UnmanagedMethods.ShowWindow(_hwnd, windowPlacement.ShowCmd);
  831. }
  832. }
  833. private void UpdateWMStyles(Action change)
  834. {
  835. var oldDecorated = _decorated;
  836. var oldThickness = BorderThickness;
  837. change();
  838. var style = (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE);
  839. const WindowStyles controlledFlags = WindowStyles.WS_OVERLAPPEDWINDOW;
  840. style = style | controlledFlags ^ controlledFlags;
  841. style |= WindowStyles.WS_OVERLAPPEDWINDOW;
  842. if (!_decorated)
  843. {
  844. style ^= (WindowStyles.WS_CAPTION | WindowStyles.WS_SYSMENU);
  845. }
  846. if (!_resizable)
  847. {
  848. style ^= (WindowStyles.WS_SIZEFRAME);
  849. }
  850. GetClientRect(_hwnd, out var oldClientRect);
  851. var oldClientRectOrigin = new UnmanagedMethods.POINT();
  852. ClientToScreen(_hwnd, ref oldClientRectOrigin);
  853. oldClientRect.Offset(oldClientRectOrigin);
  854. SetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE, (uint)style);
  855. UnmanagedMethods.GetWindowRect(_hwnd, out var windowRect);
  856. bool frameUpdated = false;
  857. if (oldDecorated != _decorated)
  858. {
  859. var newRect = oldClientRect;
  860. if (_decorated)
  861. AdjustWindowRectEx(ref newRect, (uint)style, false,
  862. GetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE));
  863. SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height,
  864. SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED);
  865. frameUpdated = true;
  866. }
  867. if (!frameUpdated)
  868. SetWindowPos(_hwnd, IntPtr.Zero, 0, 0, 0, 0,
  869. SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOZORDER |
  870. SetWindowPosFlags.SWP_NOACTIVATE
  871. | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE);
  872. }
  873. public void CanResize(bool value)
  874. {
  875. if (value == _resizable)
  876. {
  877. return;
  878. }
  879. UpdateWMStyles(()=> _resizable = value);
  880. }
  881. public void SetTopmost(bool value)
  882. {
  883. if (value == _topmost)
  884. {
  885. return;
  886. }
  887. IntPtr hWndInsertAfter = value ? WindowPosZOrder.HWND_TOPMOST : WindowPosZOrder.HWND_NOTOPMOST;
  888. UnmanagedMethods.SetWindowPos(_hwnd,
  889. hWndInsertAfter,
  890. 0, 0, 0, 0,
  891. SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
  892. _topmost = value;
  893. }
  894. PixelSize EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Size
  895. {
  896. get
  897. {
  898. RECT rect;
  899. GetClientRect(_hwnd, out rect);
  900. return new PixelSize(
  901. Math.Max(1, rect.right - rect.left),
  902. Math.Max(1, rect.bottom - rect.top));
  903. }
  904. }
  905. IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle;
  906. private static int HighWord(int param) => param >> 16;
  907. }
  908. }