X11Window.cs 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Reactive.Disposables;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Threading;
  10. using Avalonia.Controls;
  11. using Avalonia.Controls.Platform;
  12. using Avalonia.Controls.Primitives.PopupPositioning;
  13. using Avalonia.FreeDesktop;
  14. using Avalonia.Input;
  15. using Avalonia.Input.Raw;
  16. using Avalonia.Input.TextInput;
  17. using Avalonia.OpenGL;
  18. using Avalonia.OpenGL.Egl;
  19. using Avalonia.Platform;
  20. using Avalonia.Platform.Storage;
  21. using Avalonia.Rendering;
  22. using Avalonia.Rendering.Composition;
  23. using Avalonia.Threading;
  24. using Avalonia.X11.Glx;
  25. using Avalonia.X11.NativeDialogs;
  26. using static Avalonia.X11.XLib;
  27. // ReSharper disable IdentifierTypo
  28. // ReSharper disable StringLiteralTypo
  29. namespace Avalonia.X11
  30. {
  31. unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client,
  32. ITopLevelImplWithNativeMenuExporter,
  33. ITopLevelImplWithNativeControlHost,
  34. ITopLevelImplWithTextInputMethod,
  35. ITopLevelImplWithStorageProvider
  36. {
  37. private readonly AvaloniaX11Platform _platform;
  38. private readonly bool _popup;
  39. private readonly X11Info _x11;
  40. private XConfigureEvent? _configure;
  41. private PixelPoint? _configurePoint;
  42. private bool _triggeredExpose;
  43. private IInputRoot _inputRoot;
  44. private readonly MouseDevice _mouse;
  45. private readonly TouchDevice _touch;
  46. private readonly IKeyboardDevice _keyboard;
  47. private PixelPoint? _position;
  48. private PixelSize _realSize;
  49. private IntPtr _handle;
  50. private IntPtr _xic;
  51. private IntPtr _renderHandle;
  52. private IntPtr _xSyncCounter;
  53. private XSyncValue _xSyncValue;
  54. private XSyncState _xSyncState = 0;
  55. private bool _mapped;
  56. private bool _wasMappedAtLeastOnce = false;
  57. private double? _scalingOverride;
  58. private bool _disabled;
  59. private TransparencyHelper _transparencyHelper;
  60. private RawEventGrouper _rawEventGrouper;
  61. private bool _useRenderWindow = false;
  62. enum XSyncState
  63. {
  64. None,
  65. WaitConfigure,
  66. WaitPaint
  67. }
  68. public X11Window(AvaloniaX11Platform platform, IWindowImpl popupParent)
  69. {
  70. _platform = platform;
  71. _popup = popupParent != null;
  72. _x11 = platform.Info;
  73. _mouse = new MouseDevice();
  74. _touch = new TouchDevice();
  75. _keyboard = platform.KeyboardDevice;
  76. var glfeature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
  77. XSetWindowAttributes attr = new XSetWindowAttributes();
  78. var valueMask = default(SetWindowValuemask);
  79. attr.backing_store = 1;
  80. attr.bit_gravity = Gravity.NorthWestGravity;
  81. attr.win_gravity = Gravity.NorthWestGravity;
  82. valueMask |= SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel
  83. | SetWindowValuemask.BackPixmap | SetWindowValuemask.BackingStore
  84. | SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity;
  85. if (_popup)
  86. {
  87. attr.override_redirect = 1;
  88. valueMask |= SetWindowValuemask.OverrideRedirect;
  89. }
  90. XVisualInfo? visualInfo = null;
  91. // OpenGL seems to be do weird things to it's current window which breaks resize sometimes
  92. _useRenderWindow = glfeature != null;
  93. var glx = glfeature as GlxPlatformOpenGlInterface;
  94. if (glx != null)
  95. visualInfo = *glx.Display.VisualInfo;
  96. else if (glfeature == null)
  97. visualInfo = _x11.TransparentVisualInfo;
  98. var egl = glfeature as EglPlatformOpenGlInterface;
  99. var visual = IntPtr.Zero;
  100. var depth = 24;
  101. if (visualInfo != null)
  102. {
  103. visual = visualInfo.Value.visual;
  104. depth = (int)visualInfo.Value.depth;
  105. attr.colormap = XCreateColormap(_x11.Display, _x11.RootWindow, visualInfo.Value.visual, 0);
  106. valueMask |= SetWindowValuemask.ColorMap;
  107. }
  108. int defaultWidth = 0, defaultHeight = 0;
  109. if (!_popup && Screen != null)
  110. {
  111. var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
  112. .FirstOrDefault(m => m.Bounds.Contains(Position));
  113. if (monitor != null)
  114. {
  115. // Emulate Window 7+'s default window size behavior.
  116. defaultWidth = (int)(monitor.WorkingArea.Width * 0.75d);
  117. defaultHeight = (int)(monitor.WorkingArea.Height * 0.7d);
  118. }
  119. }
  120. // check if the calculated size is zero then compensate to hardcoded resolution
  121. defaultWidth = Math.Max(defaultWidth, 300);
  122. defaultHeight = Math.Max(defaultHeight, 200);
  123. _handle = XCreateWindow(_x11.Display, _x11.RootWindow, 10, 10, defaultWidth, defaultHeight, 0,
  124. depth,
  125. (int)CreateWindowArgs.InputOutput,
  126. visual,
  127. new UIntPtr((uint)valueMask), ref attr);
  128. if (_useRenderWindow)
  129. _renderHandle = XCreateWindow(_x11.Display, _handle, 0, 0, defaultWidth, defaultHeight, 0, depth,
  130. (int)CreateWindowArgs.InputOutput,
  131. visual,
  132. new UIntPtr((uint)(SetWindowValuemask.BorderPixel | SetWindowValuemask.BitGravity |
  133. SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr);
  134. else
  135. _renderHandle = _handle;
  136. Handle = new SurfacePlatformHandle(this);
  137. _realSize = new PixelSize(defaultWidth, defaultHeight);
  138. platform.Windows[_handle] = OnEvent;
  139. XEventMask ignoredMask = XEventMask.SubstructureRedirectMask
  140. | XEventMask.ResizeRedirectMask
  141. | XEventMask.PointerMotionHintMask;
  142. if (platform.XI2 != null)
  143. ignoredMask |= platform.XI2.AddWindow(_handle, this);
  144. var mask = new IntPtr(0xffffff ^ (int)ignoredMask);
  145. XSelectInput(_x11.Display, _handle, mask);
  146. var protocols = new[]
  147. {
  148. _x11.Atoms.WM_DELETE_WINDOW
  149. };
  150. XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length);
  151. XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_WINDOW_TYPE, _x11.Atoms.XA_ATOM,
  152. 32, PropertyMode.Replace, new[] {_x11.Atoms._NET_WM_WINDOW_TYPE_NORMAL}, 1);
  153. SetWmClass(_platform.Options.WmClass);
  154. var surfaces = new List<object>
  155. {
  156. new X11FramebufferSurface(_x11.DeferredDisplay, _renderHandle,
  157. depth, () => RenderScaling)
  158. };
  159. if (egl != null)
  160. surfaces.Insert(0,
  161. new EglGlPlatformSurface(egl,
  162. new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
  163. if (glx != null)
  164. surfaces.Insert(0, new GlxGlPlatformSurface(glx.Display, glx.DeferredContext,
  165. new SurfaceInfo(this, _x11.Display, _handle, _renderHandle)));
  166. surfaces.Add(Handle);
  167. Surfaces = surfaces.ToArray();
  168. UpdateMotifHints();
  169. UpdateSizeHints(null);
  170. _rawEventGrouper = new RawEventGrouper(e => Input?.Invoke(e));
  171. _transparencyHelper = new TransparencyHelper(_x11, _handle, platform.Globals);
  172. _transparencyHelper.SetTransparencyRequest(WindowTransparencyLevel.None);
  173. CreateIC();
  174. XFlush(_x11.Display);
  175. if(_popup)
  176. PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(popupParent, MoveResize));
  177. if (platform.Options.UseDBusMenu)
  178. NativeMenuExporter = DBusMenuExporter.TryCreateTopLevelNativeMenu(_handle);
  179. NativeControlHost = new X11NativeControlHost(_platform, this);
  180. InitializeIme();
  181. XChangeProperty(_x11.Display, _handle, _x11.Atoms.WM_PROTOCOLS, _x11.Atoms.XA_ATOM, 32,
  182. PropertyMode.Replace, new[] { _x11.Atoms.WM_DELETE_WINDOW, _x11.Atoms._NET_WM_SYNC_REQUEST }, 2);
  183. if (_x11.HasXSync)
  184. {
  185. _xSyncCounter = XSyncCreateCounter(_x11.Display, _xSyncValue);
  186. XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_SYNC_REQUEST_COUNTER,
  187. _x11.Atoms.XA_CARDINAL, 32, PropertyMode.Replace, ref _xSyncCounter, 1);
  188. }
  189. StorageProvider = new CompositeStorageProvider(new Func<Task<IStorageProvider>>[]
  190. {
  191. () => _platform.Options.UseDBusFilePicker ? DBusSystemDialog.TryCreate(Handle) : Task.FromResult<IStorageProvider>(null),
  192. () => GtkSystemDialog.TryCreate(this),
  193. });
  194. }
  195. class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
  196. {
  197. private readonly X11Window _window;
  198. private readonly IntPtr _display;
  199. private readonly IntPtr _parent;
  200. public SurfaceInfo(X11Window window, IntPtr display, IntPtr parent, IntPtr xid)
  201. {
  202. _window = window;
  203. _display = display;
  204. _parent = parent;
  205. Handle = xid;
  206. }
  207. public IntPtr Handle { get; }
  208. public PixelSize Size
  209. {
  210. get
  211. {
  212. XLockDisplay(_display);
  213. XGetGeometry(_display, _parent, out var geo);
  214. XResizeWindow(_display, Handle, geo.width, geo.height);
  215. XUnlockDisplay(_display);
  216. return new PixelSize(geo.width, geo.height);
  217. }
  218. }
  219. public double Scaling => _window.RenderScaling;
  220. }
  221. void UpdateMotifHints()
  222. {
  223. var functions = MotifFunctions.Move | MotifFunctions.Close | MotifFunctions.Resize |
  224. MotifFunctions.Minimize | MotifFunctions.Maximize;
  225. var decorations = MotifDecorations.Menu | MotifDecorations.Title | MotifDecorations.Border |
  226. MotifDecorations.Maximize | MotifDecorations.Minimize | MotifDecorations.ResizeH;
  227. if (_popup
  228. || _systemDecorations == SystemDecorations.None)
  229. decorations = 0;
  230. if (!_canResize)
  231. {
  232. functions &= ~(MotifFunctions.Resize | MotifFunctions.Maximize);
  233. decorations &= ~(MotifDecorations.Maximize | MotifDecorations.ResizeH);
  234. }
  235. var hints = new MotifWmHints
  236. {
  237. flags = new IntPtr((int)(MotifFlags.Decorations | MotifFlags.Functions)),
  238. decorations = new IntPtr((int)decorations),
  239. functions = new IntPtr((int)functions)
  240. };
  241. XChangeProperty(_x11.Display, _handle,
  242. _x11.Atoms._MOTIF_WM_HINTS, _x11.Atoms._MOTIF_WM_HINTS, 32,
  243. PropertyMode.Replace, ref hints, 5);
  244. }
  245. void UpdateSizeHints(PixelSize? preResize)
  246. {
  247. var min = _minMaxSize.minSize;
  248. var max = _minMaxSize.maxSize;
  249. if (!_canResize)
  250. max = min = _realSize;
  251. if (preResize.HasValue)
  252. {
  253. var desired = preResize.Value;
  254. max = new PixelSize(Math.Max(desired.Width, max.Width), Math.Max(desired.Height, max.Height));
  255. min = new PixelSize(Math.Min(desired.Width, min.Width), Math.Min(desired.Height, min.Height));
  256. }
  257. var hints = new XSizeHints
  258. {
  259. min_width = min.Width,
  260. min_height = min.Height
  261. };
  262. hints.height_inc = hints.width_inc = 1;
  263. var flags = XSizeHintsFlags.PMinSize | XSizeHintsFlags.PResizeInc | XSizeHintsFlags.PPosition | XSizeHintsFlags.PSize;
  264. // People might be passing double.MaxValue
  265. if (max.Width < 100000 && max.Height < 100000)
  266. {
  267. hints.max_width = max.Width;
  268. hints.max_height = max.Height;
  269. flags |= XSizeHintsFlags.PMaxSize;
  270. }
  271. hints.flags = (IntPtr)flags;
  272. XSetWMNormalHints(_x11.Display, _handle, ref hints);
  273. }
  274. public Size ClientSize => new Size(_realSize.Width / RenderScaling, _realSize.Height / RenderScaling);
  275. public Size? FrameSize
  276. {
  277. get
  278. {
  279. XGetWindowProperty(_x11.Display, _handle, _x11.Atoms._NET_FRAME_EXTENTS, IntPtr.Zero,
  280. new IntPtr(4), false, (IntPtr)Atom.AnyPropertyType, out var _,
  281. out var _, out var nitems, out var _, out var prop);
  282. if (nitems.ToInt64() != 4)
  283. {
  284. // Window hasn't been mapped by the WM yet, so can't get the extents.
  285. return null;
  286. }
  287. var data = (IntPtr*)prop.ToPointer();
  288. var extents = new Thickness(data[0].ToInt32(), data[2].ToInt32(), data[1].ToInt32(), data[3].ToInt32());
  289. XFree(prop);
  290. return new Size(
  291. (_realSize.Width + extents.Left + extents.Right) / RenderScaling,
  292. (_realSize.Height + extents.Top + extents.Bottom) / RenderScaling);
  293. }
  294. }
  295. public double RenderScaling
  296. {
  297. get => Interlocked.CompareExchange(ref _scaling, 0.0, 0.0);
  298. private set => Interlocked.Exchange(ref _scaling, value);
  299. }
  300. public double DesktopScaling => RenderScaling;
  301. public IEnumerable<object> Surfaces { get; }
  302. public Action<RawInputEventArgs> Input { get; set; }
  303. public Action<Rect> Paint { get; set; }
  304. public Action<Size, PlatformResizeReason> Resized { get; set; }
  305. //TODO
  306. public Action<double> ScalingChanged { get; set; }
  307. public Action Deactivated { get; set; }
  308. public Action Activated { get; set; }
  309. public Func<bool> Closing { get; set; }
  310. public Action<WindowState> WindowStateChanged { get; set; }
  311. public Action<WindowTransparencyLevel> TransparencyLevelChanged
  312. {
  313. get => _transparencyHelper?.TransparencyLevelChanged;
  314. set
  315. {
  316. if (_transparencyHelper != null)
  317. _transparencyHelper.TransparencyLevelChanged = value;
  318. }
  319. }
  320. public Action<bool> ExtendClientAreaToDecorationsChanged { get; set; }
  321. public Thickness ExtendedMargins { get; } = new Thickness();
  322. public Thickness OffScreenMargin { get; } = new Thickness();
  323. public bool IsClientAreaExtendedToDecorations { get; }
  324. public Action Closed { get; set; }
  325. public Action<PixelPoint> PositionChanged { get; set; }
  326. public Action LostFocus { get; set; }
  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 _platform.Options.UseDeferredRendering
  334. ? _platform.Options.UseCompositor
  335. ? new CompositingRenderer(root, this._platform.Compositor)
  336. : new DeferredRenderer(root, loop)
  337. {
  338. RenderOnlyOnRenderThread = true
  339. }
  340. : (IRenderer)new X11ImmediateRendererProxy(root, loop);
  341. }
  342. void OnEvent(ref XEvent ev)
  343. {
  344. if (ev.type == XEventName.MapNotify)
  345. {
  346. _mapped = true;
  347. if (_useRenderWindow)
  348. XMapWindow(_x11.Display, _renderHandle);
  349. }
  350. else if (ev.type == XEventName.UnmapNotify)
  351. _mapped = false;
  352. else if (ev.type == XEventName.Expose ||
  353. (ev.type == XEventName.VisibilityNotify &&
  354. ev.VisibilityEvent.state < 2))
  355. {
  356. EnqueuePaint();
  357. }
  358. else if (ev.type == XEventName.FocusIn)
  359. {
  360. if (ActivateTransientChildIfNeeded())
  361. return;
  362. Activated?.Invoke();
  363. _imeControl?.SetWindowActive(true);
  364. }
  365. else if (ev.type == XEventName.FocusOut)
  366. {
  367. _imeControl?.SetWindowActive(false);
  368. Deactivated?.Invoke();
  369. }
  370. else if (ev.type == XEventName.MotionNotify)
  371. MouseEvent(RawPointerEventType.Move, ref ev, ev.MotionEvent.state);
  372. else if (ev.type == XEventName.LeaveNotify)
  373. MouseEvent(RawPointerEventType.LeaveWindow, ref ev, ev.CrossingEvent.state);
  374. else if (ev.type == XEventName.PropertyNotify)
  375. {
  376. OnPropertyChange(ev.PropertyEvent.atom, ev.PropertyEvent.state == 0);
  377. }
  378. else if (ev.type == XEventName.ButtonPress)
  379. {
  380. if (ActivateTransientChildIfNeeded())
  381. return;
  382. if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9)
  383. MouseEvent(
  384. ev.ButtonEvent.button switch
  385. {
  386. 1 => RawPointerEventType.LeftButtonDown,
  387. 2 => RawPointerEventType.MiddleButtonDown,
  388. 3 => RawPointerEventType.RightButtonDown,
  389. 8 => RawPointerEventType.XButton1Down,
  390. 9 => RawPointerEventType.XButton2Down
  391. },
  392. ref ev, ev.ButtonEvent.state);
  393. else
  394. {
  395. var delta = ev.ButtonEvent.button == 4
  396. ? new Vector(0, 1)
  397. : ev.ButtonEvent.button == 5
  398. ? new Vector(0, -1)
  399. : ev.ButtonEvent.button == 6
  400. ? new Vector(1, 0)
  401. : new Vector(-1, 0);
  402. ScheduleInput(new RawMouseWheelEventArgs(_mouse, (ulong)ev.ButtonEvent.time.ToInt64(),
  403. _inputRoot, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), delta,
  404. TranslateModifiers(ev.ButtonEvent.state)), ref ev);
  405. }
  406. }
  407. else if (ev.type == XEventName.ButtonRelease)
  408. {
  409. if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9)
  410. MouseEvent(
  411. ev.ButtonEvent.button switch
  412. {
  413. 1 => RawPointerEventType.LeftButtonUp,
  414. 2 => RawPointerEventType.MiddleButtonUp,
  415. 3 => RawPointerEventType.RightButtonUp,
  416. 8 => RawPointerEventType.XButton1Up,
  417. 9 => RawPointerEventType.XButton2Up
  418. },
  419. ref ev, ev.ButtonEvent.state);
  420. }
  421. else if (ev.type == XEventName.ConfigureNotify)
  422. {
  423. if (ev.ConfigureEvent.window != _handle)
  424. return;
  425. var needEnqueue = (_configure == null);
  426. _configure = ev.ConfigureEvent;
  427. if (ev.ConfigureEvent.override_redirect != 0 || ev.ConfigureEvent.send_event != 0)
  428. _configurePoint = new PixelPoint(ev.ConfigureEvent.x, ev.ConfigureEvent.y);
  429. else
  430. {
  431. XTranslateCoordinates(_x11.Display, _handle, _x11.RootWindow,
  432. 0, 0,
  433. out var tx, out var ty, out _);
  434. _configurePoint = new PixelPoint(tx, ty);
  435. }
  436. if (needEnqueue)
  437. Dispatcher.UIThread.Post(() =>
  438. {
  439. if (_configure == null)
  440. return;
  441. var cev = _configure.Value;
  442. var npos = _configurePoint.Value;
  443. _configure = null;
  444. _configurePoint = null;
  445. var nsize = new PixelSize(cev.width, cev.height);
  446. var changedSize = _realSize != nsize;
  447. var changedPos = _position == null || npos != _position;
  448. _realSize = nsize;
  449. _position = npos;
  450. bool updatedSizeViaScaling = false;
  451. if (changedPos)
  452. {
  453. PositionChanged?.Invoke(npos);
  454. updatedSizeViaScaling = UpdateScaling();
  455. }
  456. UpdateImePosition();
  457. if (changedSize && !updatedSizeViaScaling && !_popup)
  458. Resized?.Invoke(ClientSize, PlatformResizeReason.Unspecified);
  459. Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout);
  460. }, DispatcherPriority.Layout);
  461. if (_useRenderWindow)
  462. XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width,
  463. ev.ConfigureEvent.height);
  464. if (_xSyncState == XSyncState.WaitConfigure)
  465. {
  466. _xSyncState = XSyncState.WaitPaint;
  467. EnqueuePaint();
  468. }
  469. }
  470. else if (ev.type == XEventName.DestroyNotify
  471. && ev.DestroyWindowEvent.window == _handle)
  472. {
  473. Cleanup();
  474. }
  475. else if (ev.type == XEventName.ClientMessage)
  476. {
  477. if (ev.ClientMessageEvent.message_type == _x11.Atoms.WM_PROTOCOLS)
  478. {
  479. if (ev.ClientMessageEvent.ptr1 == _x11.Atoms.WM_DELETE_WINDOW)
  480. {
  481. if (Closing?.Invoke() != true)
  482. Dispose();
  483. }
  484. else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST)
  485. {
  486. _xSyncValue.Lo = new UIntPtr(ev.ClientMessageEvent.ptr3.ToPointer()).ToUInt32();
  487. _xSyncValue.Hi = ev.ClientMessageEvent.ptr4.ToInt32();
  488. _xSyncState = XSyncState.WaitConfigure;
  489. }
  490. }
  491. }
  492. else if (ev.type == XEventName.KeyPress || ev.type == XEventName.KeyRelease)
  493. {
  494. if (ActivateTransientChildIfNeeded())
  495. return;
  496. HandleKeyEvent(ref ev);
  497. }
  498. }
  499. private bool UpdateScaling(bool skipResize = false)
  500. {
  501. double newScaling;
  502. if (_scalingOverride.HasValue)
  503. newScaling = _scalingOverride.Value;
  504. else
  505. {
  506. var monitor = _platform.X11Screens.Screens.OrderBy(x => x.PixelDensity)
  507. .FirstOrDefault(m => m.Bounds.Contains(Position));
  508. newScaling = monitor?.PixelDensity ?? RenderScaling;
  509. }
  510. if (RenderScaling != newScaling)
  511. {
  512. var oldScaledSize = ClientSize;
  513. RenderScaling = newScaling;
  514. ScalingChanged?.Invoke(RenderScaling);
  515. UpdateImePosition();
  516. SetMinMaxSize(_scaledMinMaxSize.minSize, _scaledMinMaxSize.maxSize);
  517. if(!skipResize)
  518. Resize(oldScaledSize, true, PlatformResizeReason.DpiChange);
  519. return true;
  520. }
  521. return false;
  522. }
  523. private WindowState _lastWindowState;
  524. public WindowState WindowState
  525. {
  526. get => _lastWindowState;
  527. set
  528. {
  529. if(_lastWindowState == value)
  530. return;
  531. _lastWindowState = value;
  532. if (value == WindowState.Minimized)
  533. {
  534. XIconifyWindow(_x11.Display, _handle, _x11.DefaultScreen);
  535. }
  536. else if (value == WindowState.Maximized)
  537. {
  538. ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN);
  539. ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_FULLSCREEN);
  540. ChangeWMAtoms(true, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT,
  541. _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ);
  542. }
  543. else if (value == WindowState.FullScreen)
  544. {
  545. ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN);
  546. ChangeWMAtoms(true, _x11.Atoms._NET_WM_STATE_FULLSCREEN);
  547. ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT,
  548. _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ);
  549. }
  550. else
  551. {
  552. ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN);
  553. ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_FULLSCREEN);
  554. ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT,
  555. _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ);
  556. }
  557. }
  558. }
  559. private void OnPropertyChange(IntPtr atom, bool hasValue)
  560. {
  561. if (atom == _x11.Atoms._NET_FRAME_EXTENTS)
  562. {
  563. // Occurs once the window has been mapped, which is the earliest the extents
  564. // can be retrieved, so invoke event to force update of TopLevel.FrameSize.
  565. Resized.Invoke(ClientSize, PlatformResizeReason.Unspecified);
  566. }
  567. if (atom == _x11.Atoms._NET_WM_STATE)
  568. {
  569. WindowState state = WindowState.Normal;
  570. if(hasValue)
  571. {
  572. XGetWindowProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_STATE, IntPtr.Zero, new IntPtr(256),
  573. false, (IntPtr)Atom.XA_ATOM, out _, out _, out var nitems, out _,
  574. out var prop);
  575. int maximized = 0;
  576. var pitems = (IntPtr*)prop.ToPointer();
  577. for (var c = 0; c < nitems.ToInt32(); c++)
  578. {
  579. if (pitems[c] == _x11.Atoms._NET_WM_STATE_HIDDEN)
  580. {
  581. state = WindowState.Minimized;
  582. break;
  583. }
  584. if(pitems[c] == _x11.Atoms._NET_WM_STATE_FULLSCREEN)
  585. {
  586. state = WindowState.FullScreen;
  587. break;
  588. }
  589. if (pitems[c] == _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ ||
  590. pitems[c] == _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT)
  591. {
  592. maximized++;
  593. if (maximized == 2)
  594. {
  595. state = WindowState.Maximized;
  596. break;
  597. }
  598. }
  599. }
  600. XFree(prop);
  601. }
  602. if (_lastWindowState != state)
  603. {
  604. _lastWindowState = state;
  605. WindowStateChanged?.Invoke(state);
  606. }
  607. }
  608. }
  609. RawInputModifiers TranslateModifiers(XModifierMask state)
  610. {
  611. var rv = default(RawInputModifiers);
  612. if (state.HasAllFlags(XModifierMask.Button1Mask))
  613. rv |= RawInputModifiers.LeftMouseButton;
  614. if (state.HasAllFlags(XModifierMask.Button2Mask))
  615. rv |= RawInputModifiers.RightMouseButton;
  616. if (state.HasAllFlags(XModifierMask.Button3Mask))
  617. rv |= RawInputModifiers.MiddleMouseButton;
  618. if (state.HasAllFlags(XModifierMask.Button4Mask))
  619. rv |= RawInputModifiers.XButton1MouseButton;
  620. if (state.HasAllFlags(XModifierMask.Button5Mask))
  621. rv |= RawInputModifiers.XButton2MouseButton;
  622. if (state.HasAllFlags(XModifierMask.ShiftMask))
  623. rv |= RawInputModifiers.Shift;
  624. if (state.HasAllFlags(XModifierMask.ControlMask))
  625. rv |= RawInputModifiers.Control;
  626. if (state.HasAllFlags(XModifierMask.Mod1Mask))
  627. rv |= RawInputModifiers.Alt;
  628. if (state.HasAllFlags(XModifierMask.Mod4Mask))
  629. rv |= RawInputModifiers.Meta;
  630. return rv;
  631. }
  632. private SystemDecorations _systemDecorations = SystemDecorations.Full;
  633. private bool _canResize = true;
  634. private const int MaxWindowDimension = 100000;
  635. private (Size minSize, Size maxSize) _scaledMinMaxSize =
  636. (new Size(1, 1), new Size(double.PositiveInfinity, double.PositiveInfinity));
  637. private (PixelSize minSize, PixelSize maxSize) _minMaxSize = (new PixelSize(1, 1),
  638. new PixelSize(MaxWindowDimension, MaxWindowDimension));
  639. private double _scaling = 1;
  640. void ScheduleInput(RawInputEventArgs args, ref XEvent xev)
  641. {
  642. _x11.LastActivityTimestamp = xev.ButtonEvent.time;
  643. ScheduleInput(args);
  644. }
  645. public void ScheduleXI2Input(RawInputEventArgs args)
  646. {
  647. if (args is RawPointerEventArgs pargs)
  648. {
  649. if ((pargs.Type == RawPointerEventType.TouchBegin
  650. || pargs.Type == RawPointerEventType.TouchUpdate
  651. || pargs.Type == RawPointerEventType.LeftButtonDown
  652. || pargs.Type == RawPointerEventType.RightButtonDown
  653. || pargs.Type == RawPointerEventType.MiddleButtonDown
  654. || pargs.Type == RawPointerEventType.NonClientLeftButtonDown)
  655. && ActivateTransientChildIfNeeded())
  656. return;
  657. if (pargs.Type == RawPointerEventType.TouchEnd
  658. && ActivateTransientChildIfNeeded())
  659. pargs.Type = RawPointerEventType.TouchCancel;
  660. }
  661. ScheduleInput(args);
  662. }
  663. private void ScheduleInput(RawInputEventArgs args)
  664. {
  665. if (args is RawPointerEventArgs mouse)
  666. mouse.Position = mouse.Position / RenderScaling;
  667. if (args is RawDragEvent drag)
  668. drag.Location = drag.Location / RenderScaling;
  669. _rawEventGrouper.HandleEvent(args);
  670. }
  671. void MouseEvent(RawPointerEventType type, ref XEvent ev, XModifierMask mods)
  672. {
  673. var mev = new RawPointerEventArgs(
  674. _mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot,
  675. type, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), TranslateModifiers(mods));
  676. ScheduleInput(mev, ref ev);
  677. }
  678. void EnqueuePaint()
  679. {
  680. if (!_triggeredExpose)
  681. {
  682. _triggeredExpose = true;
  683. Dispatcher.UIThread.Post(() =>
  684. {
  685. _triggeredExpose = false;
  686. DoPaint();
  687. }, DispatcherPriority.Render);
  688. }
  689. }
  690. void DoPaint()
  691. {
  692. Paint?.Invoke(new Rect());
  693. if (_xSyncCounter != IntPtr.Zero && _xSyncState == XSyncState.WaitPaint)
  694. {
  695. _xSyncState = XSyncState.None;
  696. XSyncSetCounter(_x11.Display, _xSyncCounter, _xSyncValue);
  697. }
  698. }
  699. public void Invalidate(Rect rect)
  700. {
  701. }
  702. public IInputRoot InputRoot => _inputRoot;
  703. public void SetInputRoot(IInputRoot inputRoot)
  704. {
  705. _inputRoot = inputRoot;
  706. }
  707. public void Dispose()
  708. {
  709. Cleanup();
  710. }
  711. void Cleanup()
  712. {
  713. if (_rawEventGrouper != null)
  714. {
  715. _rawEventGrouper.Dispose();
  716. _rawEventGrouper = null;
  717. }
  718. if (_transparencyHelper != null)
  719. {
  720. _transparencyHelper.Dispose();
  721. _transparencyHelper = null;
  722. }
  723. if (_imeControl != null)
  724. {
  725. _imeControl.Dispose();
  726. _imeControl = null;
  727. _ime = null;
  728. }
  729. if (_xic != IntPtr.Zero)
  730. {
  731. XDestroyIC(_xic);
  732. _xic = IntPtr.Zero;
  733. }
  734. if (_xSyncCounter != IntPtr.Zero)
  735. {
  736. XSyncDestroyCounter(_x11.Display, _xSyncCounter);
  737. _xSyncCounter = IntPtr.Zero;
  738. }
  739. if (_handle != IntPtr.Zero)
  740. {
  741. _platform.Windows.Remove(_handle);
  742. _platform.XI2?.OnWindowDestroyed(_handle);
  743. var handle = _handle;
  744. _handle = IntPtr.Zero;
  745. Closed?.Invoke();
  746. _mouse.Dispose();
  747. _touch.Dispose();
  748. XDestroyWindow(_x11.Display, handle);
  749. }
  750. if (_useRenderWindow && _renderHandle != IntPtr.Zero)
  751. {
  752. _renderHandle = IntPtr.Zero;
  753. }
  754. }
  755. bool ActivateTransientChildIfNeeded()
  756. {
  757. if (_disabled)
  758. {
  759. GotInputWhenDisabled?.Invoke();
  760. return true;
  761. }
  762. return false;
  763. }
  764. public void SetParent(IWindowImpl parent)
  765. {
  766. if (parent == null || parent.Handle == null || parent.Handle.Handle == IntPtr.Zero)
  767. XDeleteProperty(_x11.Display, _handle, _x11.Atoms.XA_WM_TRANSIENT_FOR);
  768. else
  769. XSetTransientForHint(_x11.Display, _handle, parent.Handle.Handle);
  770. }
  771. public void Show(bool activate, bool isDialog)
  772. {
  773. _wasMappedAtLeastOnce = true;
  774. XMapWindow(_x11.Display, _handle);
  775. XFlush(_x11.Display);
  776. }
  777. public void Hide() => XUnmapWindow(_x11.Display, _handle);
  778. public Point PointToClient(PixelPoint point) => new Point((point.X - Position.X) / RenderScaling, (point.Y - Position.Y) / RenderScaling);
  779. public PixelPoint PointToScreen(Point point) => new PixelPoint(
  780. (int)(point.X * RenderScaling + Position.X),
  781. (int)(point.Y * RenderScaling + Position.Y));
  782. public void SetSystemDecorations(SystemDecorations enabled)
  783. {
  784. _systemDecorations = enabled == SystemDecorations.Full ? SystemDecorations.Full : SystemDecorations.None;
  785. UpdateMotifHints();
  786. UpdateSizeHints(null);
  787. }
  788. public void Resize(Size clientSize, PlatformResizeReason reason) => Resize(clientSize, false, reason);
  789. public void Move(PixelPoint point) => Position = point;
  790. private void MoveResize(PixelPoint position, Size size, double scaling)
  791. {
  792. Move(position);
  793. _scalingOverride = scaling;
  794. UpdateScaling(true);
  795. Resize(size, true, PlatformResizeReason.Layout);
  796. }
  797. PixelSize ToPixelSize(Size size) => new PixelSize((int)(size.Width * RenderScaling), (int)(size.Height * RenderScaling));
  798. void Resize(Size clientSize, bool force, PlatformResizeReason reason)
  799. {
  800. if (!force && clientSize == ClientSize)
  801. return;
  802. var needImmediatePopupResize = clientSize != ClientSize;
  803. var pixelSize = ToPixelSize(clientSize);
  804. UpdateSizeHints(pixelSize);
  805. XConfigureResizeWindow(_x11.Display, _handle, pixelSize);
  806. if (_useRenderWindow)
  807. XConfigureResizeWindow(_x11.Display, _renderHandle, pixelSize);
  808. XFlush(_x11.Display);
  809. if (force || !_wasMappedAtLeastOnce || (_popup && needImmediatePopupResize))
  810. {
  811. _realSize = pixelSize;
  812. Resized?.Invoke(ClientSize, reason);
  813. }
  814. }
  815. public void CanResize(bool value)
  816. {
  817. _canResize = value;
  818. UpdateMotifHints();
  819. UpdateSizeHints(null);
  820. }
  821. public void SetCursor(ICursorImpl cursor)
  822. {
  823. if (cursor == null)
  824. XDefineCursor(_x11.Display, _handle, _x11.DefaultCursor);
  825. else if (cursor is CursorImpl impl)
  826. {
  827. XDefineCursor(_x11.Display, _handle, impl.Handle);
  828. }
  829. }
  830. public IPlatformHandle Handle { get; }
  831. public PixelPoint Position
  832. {
  833. get => _position ?? default;
  834. set
  835. {
  836. var changes = new XWindowChanges
  837. {
  838. x = (int)value.X,
  839. y = (int)value.Y
  840. };
  841. XConfigureWindow(_x11.Display, _handle, ChangeWindowFlags.CWX | ChangeWindowFlags.CWY,
  842. ref changes);
  843. XFlush(_x11.Display);
  844. if (!_wasMappedAtLeastOnce)
  845. {
  846. _position = value;
  847. PositionChanged?.Invoke(value);
  848. }
  849. }
  850. }
  851. public IMouseDevice MouseDevice => _mouse;
  852. public TouchDevice TouchDevice => _touch;
  853. public IPopupImpl CreatePopup()
  854. => _platform.Options.OverlayPopups ? null : new X11Window(_platform, this);
  855. public void Activate()
  856. {
  857. if (_x11.Atoms._NET_ACTIVE_WINDOW != IntPtr.Zero)
  858. {
  859. SendNetWMMessage(_x11.Atoms._NET_ACTIVE_WINDOW, (IntPtr)1, _x11.LastActivityTimestamp,
  860. IntPtr.Zero);
  861. }
  862. else
  863. {
  864. XRaiseWindow(_x11.Display, _handle);
  865. XSetInputFocus(_x11.Display, _handle, 0, IntPtr.Zero);
  866. }
  867. }
  868. public IScreenImpl Screen => _platform.Screens;
  869. public Size MaxAutoSizeHint => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.PixelDensity))
  870. .OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
  871. void SendNetWMMessage(IntPtr message_type, IntPtr l0,
  872. IntPtr? l1 = null, IntPtr? l2 = null, IntPtr? l3 = null, IntPtr? l4 = null)
  873. {
  874. var xev = new XEvent
  875. {
  876. ClientMessageEvent =
  877. {
  878. type = XEventName.ClientMessage,
  879. send_event = 1,
  880. window = _handle,
  881. message_type = message_type,
  882. format = 32,
  883. ptr1 = l0,
  884. ptr2 = l1 ?? IntPtr.Zero,
  885. ptr3 = l2 ?? IntPtr.Zero,
  886. ptr4 = l3 ?? IntPtr.Zero
  887. }
  888. };
  889. xev.ClientMessageEvent.ptr4 = l4 ?? IntPtr.Zero;
  890. XSendEvent(_x11.Display, _x11.RootWindow, false,
  891. new IntPtr((int)(EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
  892. }
  893. void BeginMoveResize(NetWmMoveResize side, PointerPressedEventArgs e)
  894. {
  895. var pos = GetCursorPos(_x11);
  896. XUngrabPointer(_x11.Display, new IntPtr(0));
  897. SendNetWMMessage (_x11.Atoms._NET_WM_MOVERESIZE, (IntPtr) pos.x, (IntPtr) pos.y,
  898. (IntPtr) side,
  899. (IntPtr) 1, (IntPtr)1); // left button
  900. e.Pointer.Capture(null);
  901. }
  902. public void BeginMoveDrag(PointerPressedEventArgs e)
  903. {
  904. BeginMoveResize(NetWmMoveResize._NET_WM_MOVERESIZE_MOVE, e);
  905. }
  906. public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e)
  907. {
  908. var side = NetWmMoveResize._NET_WM_MOVERESIZE_CANCEL;
  909. if (edge == WindowEdge.East)
  910. side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_RIGHT;
  911. if (edge == WindowEdge.North)
  912. side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_TOP;
  913. if (edge == WindowEdge.South)
  914. side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOM;
  915. if (edge == WindowEdge.West)
  916. side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_LEFT;
  917. if (edge == WindowEdge.NorthEast)
  918. side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
  919. if (edge == WindowEdge.NorthWest)
  920. side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_TOPLEFT;
  921. if (edge == WindowEdge.SouthEast)
  922. side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
  923. if (edge == WindowEdge.SouthWest)
  924. side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
  925. BeginMoveResize(side, e);
  926. }
  927. public void SetTitle(string title)
  928. {
  929. if (string.IsNullOrEmpty(title))
  930. {
  931. XDeleteProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_NAME);
  932. XDeleteProperty(_x11.Display, _handle, _x11.Atoms.XA_WM_NAME);
  933. }
  934. else
  935. {
  936. var data = Encoding.UTF8.GetBytes(title);
  937. fixed (void* pdata = data)
  938. {
  939. XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_NAME, _x11.Atoms.UTF8_STRING, 8,
  940. PropertyMode.Replace, pdata, data.Length);
  941. XStoreName(_x11.Display, _handle, title);
  942. }
  943. }
  944. }
  945. public void SetWmClass(string wmClass)
  946. {
  947. // See https://tronche.com/gui/x/icccm/sec-4.html#WM_CLASS
  948. // We don't actually parse the application's command line, so we only use RESOURCE_NAME and argv[0]
  949. var appId = Environment.GetEnvironmentVariable("RESOURCE_NAME")
  950. ?? Process.GetCurrentProcess().ProcessName;
  951. var encodedAppId = Encoding.ASCII.GetBytes(appId);
  952. var encodedWmClass = Encoding.ASCII.GetBytes(wmClass ?? appId);
  953. var hint = XAllocClassHint();
  954. fixed(byte* pAppId = encodedAppId)
  955. fixed (byte* pWmClass = encodedWmClass)
  956. {
  957. hint->res_name = pAppId;
  958. hint->res_class = pWmClass;
  959. XSetClassHint(_x11.Display, _handle, hint);
  960. }
  961. XFree(hint);
  962. }
  963. public void SetMinMaxSize(Size minSize, Size maxSize)
  964. {
  965. _scaledMinMaxSize = (minSize, maxSize);
  966. var min = new PixelSize(
  967. (int)(minSize.Width < 1 ? 1 : minSize.Width * RenderScaling),
  968. (int)(minSize.Height < 1 ? 1 : minSize.Height * RenderScaling));
  969. const int maxDim = MaxWindowDimension;
  970. var max = new PixelSize(
  971. (int)(maxSize.Width > maxDim ? maxDim : Math.Max(min.Width, maxSize.Width * RenderScaling)),
  972. (int)(maxSize.Height > maxDim ? maxDim : Math.Max(min.Height, maxSize.Height * RenderScaling)));
  973. _minMaxSize = (min, max);
  974. UpdateSizeHints(null);
  975. }
  976. public void SetTopmost(bool value)
  977. {
  978. ChangeWMAtoms(value, _x11.Atoms._NET_WM_STATE_ABOVE);
  979. }
  980. public void SetEnabled(bool enable)
  981. {
  982. _disabled = !enable;
  983. }
  984. public void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint)
  985. {
  986. }
  987. public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints)
  988. {
  989. }
  990. public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight)
  991. {
  992. }
  993. public Action GotInputWhenDisabled { get; set; }
  994. public void SetIcon(IWindowIconImpl icon)
  995. {
  996. if (icon != null)
  997. {
  998. var data = ((X11IconData)icon).Data;
  999. fixed (void* pdata = data)
  1000. XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_ICON,
  1001. new IntPtr((int)Atom.XA_CARDINAL), 32, PropertyMode.Replace,
  1002. pdata, data.Length);
  1003. }
  1004. else
  1005. {
  1006. XDeleteProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_ICON);
  1007. }
  1008. }
  1009. public void ShowTaskbarIcon(bool value)
  1010. {
  1011. ChangeWMAtoms(!value, _x11.Atoms._NET_WM_STATE_SKIP_TASKBAR);
  1012. }
  1013. void ChangeWMAtoms(bool enable, params IntPtr[] atoms)
  1014. {
  1015. if (atoms.Length != 1 && atoms.Length != 2)
  1016. throw new ArgumentException();
  1017. if (!_mapped)
  1018. {
  1019. XGetWindowProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_STATE, IntPtr.Zero, new IntPtr(256),
  1020. false, (IntPtr)Atom.XA_ATOM, out _, out _, out var nitems, out _,
  1021. out var prop);
  1022. var ptr = (IntPtr*)prop.ToPointer();
  1023. var newAtoms = new HashSet<IntPtr>();
  1024. for (var c = 0; c < nitems.ToInt64(); c++)
  1025. newAtoms.Add(*ptr);
  1026. XFree(prop);
  1027. foreach(var atom in atoms)
  1028. if (enable)
  1029. newAtoms.Add(atom);
  1030. else
  1031. newAtoms.Remove(atom);
  1032. XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_STATE, (IntPtr)Atom.XA_ATOM, 32,
  1033. PropertyMode.Replace, newAtoms.ToArray(), newAtoms.Count);
  1034. }
  1035. SendNetWMMessage(_x11.Atoms._NET_WM_STATE,
  1036. (IntPtr)(enable ? 1 : 0),
  1037. atoms[0],
  1038. atoms.Length > 1 ? atoms[1] : IntPtr.Zero,
  1039. atoms.Length > 2 ? atoms[2] : IntPtr.Zero,
  1040. atoms.Length > 3 ? atoms[3] : IntPtr.Zero
  1041. );
  1042. }
  1043. public IPopupPositioner PopupPositioner { get; }
  1044. public ITopLevelNativeMenuExporter NativeMenuExporter { get; }
  1045. public INativeControlHostImpl NativeControlHost { get; }
  1046. public ITextInputMethodImpl TextInputMethod => _ime;
  1047. public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) =>
  1048. _transparencyHelper?.SetTransparencyRequest(transparencyLevel);
  1049. public void SetWindowManagerAddShadowHint(bool enabled)
  1050. {
  1051. }
  1052. public WindowTransparencyLevel TransparencyLevel =>
  1053. _transparencyHelper?.CurrentLevel ?? WindowTransparencyLevel.None;
  1054. public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0.8);
  1055. public bool NeedsManagedDecorations => false;
  1056. public IStorageProvider StorageProvider { get; }
  1057. public class SurfacePlatformHandle : IPlatformNativeSurfaceHandle
  1058. {
  1059. private readonly X11Window _owner;
  1060. public PixelSize Size => _owner.ToPixelSize(_owner.ClientSize);
  1061. public double Scaling => _owner.RenderScaling;
  1062. public SurfacePlatformHandle(X11Window owner)
  1063. {
  1064. _owner = owner;
  1065. }
  1066. public IntPtr Handle => _owner._renderHandle;
  1067. public string HandleDescriptor => "XID";
  1068. }
  1069. }
  1070. }