| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237 |
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations;
- using System.Diagnostics;
- using System.Linq;
- using System.Reactive.Disposables;
- using System.Text;
- using System.Threading.Tasks;
- using System.Threading;
- using Avalonia.Controls;
- using Avalonia.Controls.Platform;
- using Avalonia.Controls.Primitives.PopupPositioning;
- using Avalonia.FreeDesktop;
- using Avalonia.Input;
- using Avalonia.Input.Raw;
- using Avalonia.Input.TextInput;
- using Avalonia.OpenGL;
- using Avalonia.OpenGL.Egl;
- using Avalonia.Platform;
- using Avalonia.Platform.Storage;
- using Avalonia.Rendering;
- using Avalonia.Rendering.Composition;
- using Avalonia.Threading;
- using Avalonia.X11.Glx;
- using Avalonia.X11.NativeDialogs;
- using static Avalonia.X11.XLib;
- // ReSharper disable IdentifierTypo
- // ReSharper disable StringLiteralTypo
- namespace Avalonia.X11
- {
- unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client,
- ITopLevelImplWithNativeMenuExporter,
- ITopLevelImplWithNativeControlHost,
- ITopLevelImplWithTextInputMethod,
- ITopLevelImplWithStorageProvider
- {
- private readonly AvaloniaX11Platform _platform;
- private readonly bool _popup;
- private readonly X11Info _x11;
- private XConfigureEvent? _configure;
- private PixelPoint? _configurePoint;
- private bool _triggeredExpose;
- private IInputRoot _inputRoot;
- private readonly MouseDevice _mouse;
- private readonly TouchDevice _touch;
- private readonly IKeyboardDevice _keyboard;
- private PixelPoint? _position;
- private PixelSize _realSize;
- private IntPtr _handle;
- private IntPtr _xic;
- private IntPtr _renderHandle;
- private IntPtr _xSyncCounter;
- private XSyncValue _xSyncValue;
- private XSyncState _xSyncState = 0;
- private bool _mapped;
- private bool _wasMappedAtLeastOnce = false;
- private double? _scalingOverride;
- private bool _disabled;
- private TransparencyHelper _transparencyHelper;
- private RawEventGrouper _rawEventGrouper;
- private bool _useRenderWindow = false;
- enum XSyncState
- {
- None,
- WaitConfigure,
- WaitPaint
- }
-
- public X11Window(AvaloniaX11Platform platform, IWindowImpl popupParent)
- {
- _platform = platform;
- _popup = popupParent != null;
- _x11 = platform.Info;
- _mouse = new MouseDevice();
- _touch = new TouchDevice();
- _keyboard = platform.KeyboardDevice;
- var glfeature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
- XSetWindowAttributes attr = new XSetWindowAttributes();
- var valueMask = default(SetWindowValuemask);
- attr.backing_store = 1;
- attr.bit_gravity = Gravity.NorthWestGravity;
- attr.win_gravity = Gravity.NorthWestGravity;
- valueMask |= SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel
- | SetWindowValuemask.BackPixmap | SetWindowValuemask.BackingStore
- | SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity;
- if (_popup)
- {
- attr.override_redirect = 1;
- valueMask |= SetWindowValuemask.OverrideRedirect;
- }
- XVisualInfo? visualInfo = null;
- // OpenGL seems to be do weird things to it's current window which breaks resize sometimes
- _useRenderWindow = glfeature != null;
-
- var glx = glfeature as GlxPlatformOpenGlInterface;
- if (glx != null)
- visualInfo = *glx.Display.VisualInfo;
- else if (glfeature == null)
- visualInfo = _x11.TransparentVisualInfo;
- var egl = glfeature as EglPlatformOpenGlInterface;
-
- var visual = IntPtr.Zero;
- var depth = 24;
- if (visualInfo != null)
- {
- visual = visualInfo.Value.visual;
- depth = (int)visualInfo.Value.depth;
- attr.colormap = XCreateColormap(_x11.Display, _x11.RootWindow, visualInfo.Value.visual, 0);
- valueMask |= SetWindowValuemask.ColorMap;
- }
- int defaultWidth = 0, defaultHeight = 0;
- if (!_popup && Screen != null)
- {
- var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
- .FirstOrDefault(m => m.Bounds.Contains(Position));
- if (monitor != null)
- {
- // Emulate Window 7+'s default window size behavior.
- defaultWidth = (int)(monitor.WorkingArea.Width * 0.75d);
- defaultHeight = (int)(monitor.WorkingArea.Height * 0.7d);
- }
- }
- // check if the calculated size is zero then compensate to hardcoded resolution
- defaultWidth = Math.Max(defaultWidth, 300);
- defaultHeight = Math.Max(defaultHeight, 200);
- _handle = XCreateWindow(_x11.Display, _x11.RootWindow, 10, 10, defaultWidth, defaultHeight, 0,
- depth,
- (int)CreateWindowArgs.InputOutput,
- visual,
- new UIntPtr((uint)valueMask), ref attr);
- if (_useRenderWindow)
- _renderHandle = XCreateWindow(_x11.Display, _handle, 0, 0, defaultWidth, defaultHeight, 0, depth,
- (int)CreateWindowArgs.InputOutput,
- visual,
- new UIntPtr((uint)(SetWindowValuemask.BorderPixel | SetWindowValuemask.BitGravity |
- SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr);
- else
- _renderHandle = _handle;
- Handle = new SurfacePlatformHandle(this);
- _realSize = new PixelSize(defaultWidth, defaultHeight);
- platform.Windows[_handle] = OnEvent;
- XEventMask ignoredMask = XEventMask.SubstructureRedirectMask
- | XEventMask.ResizeRedirectMask
- | XEventMask.PointerMotionHintMask;
- if (platform.XI2 != null)
- ignoredMask |= platform.XI2.AddWindow(_handle, this);
- var mask = new IntPtr(0xffffff ^ (int)ignoredMask);
- XSelectInput(_x11.Display, _handle, mask);
- var protocols = new[]
- {
- _x11.Atoms.WM_DELETE_WINDOW
- };
- XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length);
- XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_WINDOW_TYPE, _x11.Atoms.XA_ATOM,
- 32, PropertyMode.Replace, new[] {_x11.Atoms._NET_WM_WINDOW_TYPE_NORMAL}, 1);
- SetWmClass(_platform.Options.WmClass);
- var surfaces = new List<object>
- {
- new X11FramebufferSurface(_x11.DeferredDisplay, _renderHandle,
- depth, () => RenderScaling)
- };
-
- if (egl != null)
- surfaces.Insert(0,
- new EglGlPlatformSurface(egl,
- new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
- if (glx != null)
- surfaces.Insert(0, new GlxGlPlatformSurface(glx.Display, glx.DeferredContext,
- new SurfaceInfo(this, _x11.Display, _handle, _renderHandle)));
- surfaces.Add(Handle);
- Surfaces = surfaces.ToArray();
- UpdateMotifHints();
- UpdateSizeHints(null);
- _rawEventGrouper = new RawEventGrouper(e => Input?.Invoke(e));
-
- _transparencyHelper = new TransparencyHelper(_x11, _handle, platform.Globals);
- _transparencyHelper.SetTransparencyRequest(WindowTransparencyLevel.None);
- CreateIC();
- XFlush(_x11.Display);
- if(_popup)
- PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(popupParent, MoveResize));
- if (platform.Options.UseDBusMenu)
- NativeMenuExporter = DBusMenuExporter.TryCreateTopLevelNativeMenu(_handle);
- NativeControlHost = new X11NativeControlHost(_platform, this);
- InitializeIme();
-
- XChangeProperty(_x11.Display, _handle, _x11.Atoms.WM_PROTOCOLS, _x11.Atoms.XA_ATOM, 32,
- PropertyMode.Replace, new[] { _x11.Atoms.WM_DELETE_WINDOW, _x11.Atoms._NET_WM_SYNC_REQUEST }, 2);
- if (_x11.HasXSync)
- {
- _xSyncCounter = XSyncCreateCounter(_x11.Display, _xSyncValue);
- XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_SYNC_REQUEST_COUNTER,
- _x11.Atoms.XA_CARDINAL, 32, PropertyMode.Replace, ref _xSyncCounter, 1);
- }
- StorageProvider = new CompositeStorageProvider(new Func<Task<IStorageProvider>>[]
- {
- () => _platform.Options.UseDBusFilePicker ? DBusSystemDialog.TryCreate(Handle) : Task.FromResult<IStorageProvider>(null),
- () => GtkSystemDialog.TryCreate(this),
- });
- }
- class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
- {
- private readonly X11Window _window;
- private readonly IntPtr _display;
- private readonly IntPtr _parent;
- public SurfaceInfo(X11Window window, IntPtr display, IntPtr parent, IntPtr xid)
- {
- _window = window;
- _display = display;
- _parent = parent;
- Handle = xid;
- }
- public IntPtr Handle { get; }
- public PixelSize Size
- {
- get
- {
- XLockDisplay(_display);
- XGetGeometry(_display, _parent, out var geo);
- XResizeWindow(_display, Handle, geo.width, geo.height);
- XUnlockDisplay(_display);
- return new PixelSize(geo.width, geo.height);
- }
- }
- public double Scaling => _window.RenderScaling;
- }
- void UpdateMotifHints()
- {
- var functions = MotifFunctions.Move | MotifFunctions.Close | MotifFunctions.Resize |
- MotifFunctions.Minimize | MotifFunctions.Maximize;
- var decorations = MotifDecorations.Menu | MotifDecorations.Title | MotifDecorations.Border |
- MotifDecorations.Maximize | MotifDecorations.Minimize | MotifDecorations.ResizeH;
- if (_popup
- || _systemDecorations == SystemDecorations.None)
- decorations = 0;
- if (!_canResize)
- {
- functions &= ~(MotifFunctions.Resize | MotifFunctions.Maximize);
- decorations &= ~(MotifDecorations.Maximize | MotifDecorations.ResizeH);
- }
- var hints = new MotifWmHints
- {
- flags = new IntPtr((int)(MotifFlags.Decorations | MotifFlags.Functions)),
- decorations = new IntPtr((int)decorations),
- functions = new IntPtr((int)functions)
- };
- XChangeProperty(_x11.Display, _handle,
- _x11.Atoms._MOTIF_WM_HINTS, _x11.Atoms._MOTIF_WM_HINTS, 32,
- PropertyMode.Replace, ref hints, 5);
- }
- void UpdateSizeHints(PixelSize? preResize)
- {
- var min = _minMaxSize.minSize;
- var max = _minMaxSize.maxSize;
- if (!_canResize)
- max = min = _realSize;
-
- if (preResize.HasValue)
- {
- var desired = preResize.Value;
- max = new PixelSize(Math.Max(desired.Width, max.Width), Math.Max(desired.Height, max.Height));
- min = new PixelSize(Math.Min(desired.Width, min.Width), Math.Min(desired.Height, min.Height));
- }
- var hints = new XSizeHints
- {
- min_width = min.Width,
- min_height = min.Height
- };
- hints.height_inc = hints.width_inc = 1;
- var flags = XSizeHintsFlags.PMinSize | XSizeHintsFlags.PResizeInc | XSizeHintsFlags.PPosition | XSizeHintsFlags.PSize;
- // People might be passing double.MaxValue
- if (max.Width < 100000 && max.Height < 100000)
- {
- hints.max_width = max.Width;
- hints.max_height = max.Height;
- flags |= XSizeHintsFlags.PMaxSize;
- }
- hints.flags = (IntPtr)flags;
- XSetWMNormalHints(_x11.Display, _handle, ref hints);
- }
- public Size ClientSize => new Size(_realSize.Width / RenderScaling, _realSize.Height / RenderScaling);
- public Size? FrameSize
- {
- get
- {
- XGetWindowProperty(_x11.Display, _handle, _x11.Atoms._NET_FRAME_EXTENTS, IntPtr.Zero,
- new IntPtr(4), false, (IntPtr)Atom.AnyPropertyType, out var _,
- out var _, out var nitems, out var _, out var prop);
- if (nitems.ToInt64() != 4)
- {
- // Window hasn't been mapped by the WM yet, so can't get the extents.
- return null;
- }
- var data = (IntPtr*)prop.ToPointer();
- var extents = new Thickness(data[0].ToInt32(), data[2].ToInt32(), data[1].ToInt32(), data[3].ToInt32());
- XFree(prop);
-
- return new Size(
- (_realSize.Width + extents.Left + extents.Right) / RenderScaling,
- (_realSize.Height + extents.Top + extents.Bottom) / RenderScaling);
- }
- }
- public double RenderScaling
- {
- get => Interlocked.CompareExchange(ref _scaling, 0.0, 0.0);
- private set => Interlocked.Exchange(ref _scaling, value);
- }
-
- public double DesktopScaling => RenderScaling;
- public IEnumerable<object> Surfaces { get; }
- public Action<RawInputEventArgs> Input { get; set; }
- public Action<Rect> Paint { get; set; }
- public Action<Size, PlatformResizeReason> Resized { get; set; }
- //TODO
- public Action<double> ScalingChanged { get; set; }
- public Action Deactivated { get; set; }
- public Action Activated { get; set; }
- public Func<bool> Closing { get; set; }
- public Action<WindowState> WindowStateChanged { get; set; }
- public Action<WindowTransparencyLevel> TransparencyLevelChanged
- {
- get => _transparencyHelper?.TransparencyLevelChanged;
- set
- {
- if (_transparencyHelper != null)
- _transparencyHelper.TransparencyLevelChanged = value;
- }
- }
- public Action<bool> ExtendClientAreaToDecorationsChanged { get; set; }
- public Thickness ExtendedMargins { get; } = new Thickness();
- public Thickness OffScreenMargin { get; } = new Thickness();
- public bool IsClientAreaExtendedToDecorations { get; }
- public Action Closed { get; set; }
- public Action<PixelPoint> PositionChanged { get; set; }
- public Action LostFocus { get; set; }
- public IRenderer CreateRenderer(IRenderRoot root)
- {
- var loop = AvaloniaLocator.Current.GetService<IRenderLoop>();
- var customRendererFactory = AvaloniaLocator.Current.GetService<IRendererFactory>();
- if (customRendererFactory != null)
- return customRendererFactory.Create(root, loop);
- return _platform.Options.UseDeferredRendering
- ? _platform.Options.UseCompositor
- ? new CompositingRenderer(root, this._platform.Compositor)
- : new DeferredRenderer(root, loop)
- {
- RenderOnlyOnRenderThread = true
- }
- : (IRenderer)new X11ImmediateRendererProxy(root, loop);
- }
- void OnEvent(ref XEvent ev)
- {
- if (ev.type == XEventName.MapNotify)
- {
- _mapped = true;
- if (_useRenderWindow)
- XMapWindow(_x11.Display, _renderHandle);
- }
- else if (ev.type == XEventName.UnmapNotify)
- _mapped = false;
- else if (ev.type == XEventName.Expose ||
- (ev.type == XEventName.VisibilityNotify &&
- ev.VisibilityEvent.state < 2))
- {
- EnqueuePaint();
- }
- else if (ev.type == XEventName.FocusIn)
- {
- if (ActivateTransientChildIfNeeded())
- return;
- Activated?.Invoke();
- _imeControl?.SetWindowActive(true);
- }
- else if (ev.type == XEventName.FocusOut)
- {
- _imeControl?.SetWindowActive(false);
- Deactivated?.Invoke();
- }
- else if (ev.type == XEventName.MotionNotify)
- MouseEvent(RawPointerEventType.Move, ref ev, ev.MotionEvent.state);
- else if (ev.type == XEventName.LeaveNotify)
- MouseEvent(RawPointerEventType.LeaveWindow, ref ev, ev.CrossingEvent.state);
- else if (ev.type == XEventName.PropertyNotify)
- {
- OnPropertyChange(ev.PropertyEvent.atom, ev.PropertyEvent.state == 0);
- }
- else if (ev.type == XEventName.ButtonPress)
- {
- if (ActivateTransientChildIfNeeded())
- return;
- if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9)
- MouseEvent(
- ev.ButtonEvent.button switch
- {
- 1 => RawPointerEventType.LeftButtonDown,
- 2 => RawPointerEventType.MiddleButtonDown,
- 3 => RawPointerEventType.RightButtonDown,
- 8 => RawPointerEventType.XButton1Down,
- 9 => RawPointerEventType.XButton2Down
- },
- ref ev, ev.ButtonEvent.state);
- else
- {
- var delta = ev.ButtonEvent.button == 4
- ? new Vector(0, 1)
- : ev.ButtonEvent.button == 5
- ? new Vector(0, -1)
- : ev.ButtonEvent.button == 6
- ? new Vector(1, 0)
- : new Vector(-1, 0);
- ScheduleInput(new RawMouseWheelEventArgs(_mouse, (ulong)ev.ButtonEvent.time.ToInt64(),
- _inputRoot, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), delta,
- TranslateModifiers(ev.ButtonEvent.state)), ref ev);
- }
-
- }
- else if (ev.type == XEventName.ButtonRelease)
- {
- if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9)
- MouseEvent(
- ev.ButtonEvent.button switch
- {
- 1 => RawPointerEventType.LeftButtonUp,
- 2 => RawPointerEventType.MiddleButtonUp,
- 3 => RawPointerEventType.RightButtonUp,
- 8 => RawPointerEventType.XButton1Up,
- 9 => RawPointerEventType.XButton2Up
- },
- ref ev, ev.ButtonEvent.state);
- }
- else if (ev.type == XEventName.ConfigureNotify)
- {
- if (ev.ConfigureEvent.window != _handle)
- return;
- var needEnqueue = (_configure == null);
- _configure = ev.ConfigureEvent;
- if (ev.ConfigureEvent.override_redirect != 0 || ev.ConfigureEvent.send_event != 0)
- _configurePoint = new PixelPoint(ev.ConfigureEvent.x, ev.ConfigureEvent.y);
- else
- {
- XTranslateCoordinates(_x11.Display, _handle, _x11.RootWindow,
- 0, 0,
- out var tx, out var ty, out _);
- _configurePoint = new PixelPoint(tx, ty);
- }
- if (needEnqueue)
- Dispatcher.UIThread.Post(() =>
- {
- if (_configure == null)
- return;
- var cev = _configure.Value;
- var npos = _configurePoint.Value;
- _configure = null;
- _configurePoint = null;
-
- var nsize = new PixelSize(cev.width, cev.height);
- var changedSize = _realSize != nsize;
- var changedPos = _position == null || npos != _position;
- _realSize = nsize;
- _position = npos;
- bool updatedSizeViaScaling = false;
- if (changedPos)
- {
- PositionChanged?.Invoke(npos);
- updatedSizeViaScaling = UpdateScaling();
- }
- UpdateImePosition();
- if (changedSize && !updatedSizeViaScaling && !_popup)
- Resized?.Invoke(ClientSize, PlatformResizeReason.Unspecified);
- Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout);
- }, DispatcherPriority.Layout);
- if (_useRenderWindow)
- XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width,
- ev.ConfigureEvent.height);
- if (_xSyncState == XSyncState.WaitConfigure)
- {
- _xSyncState = XSyncState.WaitPaint;
- EnqueuePaint();
- }
- }
- else if (ev.type == XEventName.DestroyNotify
- && ev.DestroyWindowEvent.window == _handle)
- {
- Cleanup();
- }
- else if (ev.type == XEventName.ClientMessage)
- {
- if (ev.ClientMessageEvent.message_type == _x11.Atoms.WM_PROTOCOLS)
- {
- if (ev.ClientMessageEvent.ptr1 == _x11.Atoms.WM_DELETE_WINDOW)
- {
- if (Closing?.Invoke() != true)
- Dispose();
- }
- else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST)
- {
- _xSyncValue.Lo = new UIntPtr(ev.ClientMessageEvent.ptr3.ToPointer()).ToUInt32();
- _xSyncValue.Hi = ev.ClientMessageEvent.ptr4.ToInt32();
- _xSyncState = XSyncState.WaitConfigure;
- }
- }
- }
- else if (ev.type == XEventName.KeyPress || ev.type == XEventName.KeyRelease)
- {
- if (ActivateTransientChildIfNeeded())
- return;
- HandleKeyEvent(ref ev);
- }
- }
- private bool UpdateScaling(bool skipResize = false)
- {
- double newScaling;
- if (_scalingOverride.HasValue)
- newScaling = _scalingOverride.Value;
- else
- {
- var monitor = _platform.X11Screens.Screens.OrderBy(x => x.PixelDensity)
- .FirstOrDefault(m => m.Bounds.Contains(Position));
- newScaling = monitor?.PixelDensity ?? RenderScaling;
- }
- if (RenderScaling != newScaling)
- {
- var oldScaledSize = ClientSize;
- RenderScaling = newScaling;
- ScalingChanged?.Invoke(RenderScaling);
- UpdateImePosition();
- SetMinMaxSize(_scaledMinMaxSize.minSize, _scaledMinMaxSize.maxSize);
- if(!skipResize)
- Resize(oldScaledSize, true, PlatformResizeReason.DpiChange);
- return true;
- }
-
- return false;
- }
- private WindowState _lastWindowState;
- public WindowState WindowState
- {
- get => _lastWindowState;
- set
- {
- if(_lastWindowState == value)
- return;
- _lastWindowState = value;
- if (value == WindowState.Minimized)
- {
- XIconifyWindow(_x11.Display, _handle, _x11.DefaultScreen);
- }
- else if (value == WindowState.Maximized)
- {
- ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN);
- ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_FULLSCREEN);
- ChangeWMAtoms(true, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT,
- _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ);
- }
- else if (value == WindowState.FullScreen)
- {
- ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN);
- ChangeWMAtoms(true, _x11.Atoms._NET_WM_STATE_FULLSCREEN);
- ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT,
- _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ);
- }
- else
- {
- ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN);
- ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_FULLSCREEN);
- ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT,
- _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ);
- }
- }
- }
- private void OnPropertyChange(IntPtr atom, bool hasValue)
- {
- if (atom == _x11.Atoms._NET_FRAME_EXTENTS)
- {
- // Occurs once the window has been mapped, which is the earliest the extents
- // can be retrieved, so invoke event to force update of TopLevel.FrameSize.
- Resized.Invoke(ClientSize, PlatformResizeReason.Unspecified);
- }
- if (atom == _x11.Atoms._NET_WM_STATE)
- {
- WindowState state = WindowState.Normal;
- if(hasValue)
- {
- XGetWindowProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_STATE, IntPtr.Zero, new IntPtr(256),
- false, (IntPtr)Atom.XA_ATOM, out _, out _, out var nitems, out _,
- out var prop);
- int maximized = 0;
- var pitems = (IntPtr*)prop.ToPointer();
- for (var c = 0; c < nitems.ToInt32(); c++)
- {
- if (pitems[c] == _x11.Atoms._NET_WM_STATE_HIDDEN)
- {
- state = WindowState.Minimized;
- break;
- }
- if(pitems[c] == _x11.Atoms._NET_WM_STATE_FULLSCREEN)
- {
- state = WindowState.FullScreen;
- break;
- }
- if (pitems[c] == _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ ||
- pitems[c] == _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT)
- {
- maximized++;
- if (maximized == 2)
- {
- state = WindowState.Maximized;
- break;
- }
- }
- }
- XFree(prop);
- }
- if (_lastWindowState != state)
- {
- _lastWindowState = state;
- WindowStateChanged?.Invoke(state);
- }
- }
- }
- RawInputModifiers TranslateModifiers(XModifierMask state)
- {
- var rv = default(RawInputModifiers);
- if (state.HasAllFlags(XModifierMask.Button1Mask))
- rv |= RawInputModifiers.LeftMouseButton;
- if (state.HasAllFlags(XModifierMask.Button2Mask))
- rv |= RawInputModifiers.RightMouseButton;
- if (state.HasAllFlags(XModifierMask.Button3Mask))
- rv |= RawInputModifiers.MiddleMouseButton;
- if (state.HasAllFlags(XModifierMask.Button4Mask))
- rv |= RawInputModifiers.XButton1MouseButton;
- if (state.HasAllFlags(XModifierMask.Button5Mask))
- rv |= RawInputModifiers.XButton2MouseButton;
- if (state.HasAllFlags(XModifierMask.ShiftMask))
- rv |= RawInputModifiers.Shift;
- if (state.HasAllFlags(XModifierMask.ControlMask))
- rv |= RawInputModifiers.Control;
- if (state.HasAllFlags(XModifierMask.Mod1Mask))
- rv |= RawInputModifiers.Alt;
- if (state.HasAllFlags(XModifierMask.Mod4Mask))
- rv |= RawInputModifiers.Meta;
- return rv;
- }
-
- private SystemDecorations _systemDecorations = SystemDecorations.Full;
- private bool _canResize = true;
- private const int MaxWindowDimension = 100000;
- private (Size minSize, Size maxSize) _scaledMinMaxSize =
- (new Size(1, 1), new Size(double.PositiveInfinity, double.PositiveInfinity));
- private (PixelSize minSize, PixelSize maxSize) _minMaxSize = (new PixelSize(1, 1),
- new PixelSize(MaxWindowDimension, MaxWindowDimension));
-
- private double _scaling = 1;
- void ScheduleInput(RawInputEventArgs args, ref XEvent xev)
- {
- _x11.LastActivityTimestamp = xev.ButtonEvent.time;
- ScheduleInput(args);
- }
-
- public void ScheduleXI2Input(RawInputEventArgs args)
- {
- if (args is RawPointerEventArgs pargs)
- {
- if ((pargs.Type == RawPointerEventType.TouchBegin
- || pargs.Type == RawPointerEventType.TouchUpdate
- || pargs.Type == RawPointerEventType.LeftButtonDown
- || pargs.Type == RawPointerEventType.RightButtonDown
- || pargs.Type == RawPointerEventType.MiddleButtonDown
- || pargs.Type == RawPointerEventType.NonClientLeftButtonDown)
- && ActivateTransientChildIfNeeded())
- return;
- if (pargs.Type == RawPointerEventType.TouchEnd
- && ActivateTransientChildIfNeeded())
- pargs.Type = RawPointerEventType.TouchCancel;
- }
- ScheduleInput(args);
- }
-
- private void ScheduleInput(RawInputEventArgs args)
- {
- if (args is RawPointerEventArgs mouse)
- mouse.Position = mouse.Position / RenderScaling;
- if (args is RawDragEvent drag)
- drag.Location = drag.Location / RenderScaling;
-
- _rawEventGrouper.HandleEvent(args);
- }
-
- void MouseEvent(RawPointerEventType type, ref XEvent ev, XModifierMask mods)
- {
- var mev = new RawPointerEventArgs(
- _mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot,
- type, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), TranslateModifiers(mods));
- ScheduleInput(mev, ref ev);
- }
- void EnqueuePaint()
- {
- if (!_triggeredExpose)
- {
- _triggeredExpose = true;
- Dispatcher.UIThread.Post(() =>
- {
- _triggeredExpose = false;
- DoPaint();
- }, DispatcherPriority.Render);
- }
- }
-
- void DoPaint()
- {
- Paint?.Invoke(new Rect());
- if (_xSyncCounter != IntPtr.Zero && _xSyncState == XSyncState.WaitPaint)
- {
- _xSyncState = XSyncState.None;
- XSyncSetCounter(_x11.Display, _xSyncCounter, _xSyncValue);
- }
- }
-
- public void Invalidate(Rect rect)
- {
- }
- public IInputRoot InputRoot => _inputRoot;
-
- public void SetInputRoot(IInputRoot inputRoot)
- {
- _inputRoot = inputRoot;
- }
- public void Dispose()
- {
- Cleanup();
- }
- void Cleanup()
- {
- if (_rawEventGrouper != null)
- {
- _rawEventGrouper.Dispose();
- _rawEventGrouper = null;
- }
-
- if (_transparencyHelper != null)
- {
- _transparencyHelper.Dispose();
- _transparencyHelper = null;
- }
-
- if (_imeControl != null)
- {
- _imeControl.Dispose();
- _imeControl = null;
- _ime = null;
- }
-
- if (_xic != IntPtr.Zero)
- {
- XDestroyIC(_xic);
- _xic = IntPtr.Zero;
- }
- if (_xSyncCounter != IntPtr.Zero)
- {
- XSyncDestroyCounter(_x11.Display, _xSyncCounter);
- _xSyncCounter = IntPtr.Zero;
- }
-
- if (_handle != IntPtr.Zero)
- {
- _platform.Windows.Remove(_handle);
- _platform.XI2?.OnWindowDestroyed(_handle);
- var handle = _handle;
- _handle = IntPtr.Zero;
- Closed?.Invoke();
- _mouse.Dispose();
- _touch.Dispose();
- XDestroyWindow(_x11.Display, handle);
- }
-
- if (_useRenderWindow && _renderHandle != IntPtr.Zero)
- {
- _renderHandle = IntPtr.Zero;
- }
- }
- bool ActivateTransientChildIfNeeded()
- {
- if (_disabled)
- {
- GotInputWhenDisabled?.Invoke();
- return true;
- }
- return false;
- }
- public void SetParent(IWindowImpl parent)
- {
- if (parent == null || parent.Handle == null || parent.Handle.Handle == IntPtr.Zero)
- XDeleteProperty(_x11.Display, _handle, _x11.Atoms.XA_WM_TRANSIENT_FOR);
- else
- XSetTransientForHint(_x11.Display, _handle, parent.Handle.Handle);
- }
- public void Show(bool activate, bool isDialog)
- {
- _wasMappedAtLeastOnce = true;
- XMapWindow(_x11.Display, _handle);
- XFlush(_x11.Display);
- }
- public void Hide() => XUnmapWindow(_x11.Display, _handle);
-
- public Point PointToClient(PixelPoint point) => new Point((point.X - Position.X) / RenderScaling, (point.Y - Position.Y) / RenderScaling);
- public PixelPoint PointToScreen(Point point) => new PixelPoint(
- (int)(point.X * RenderScaling + Position.X),
- (int)(point.Y * RenderScaling + Position.Y));
-
- public void SetSystemDecorations(SystemDecorations enabled)
- {
- _systemDecorations = enabled == SystemDecorations.Full ? SystemDecorations.Full : SystemDecorations.None;
- UpdateMotifHints();
- UpdateSizeHints(null);
- }
- public void Resize(Size clientSize, PlatformResizeReason reason) => Resize(clientSize, false, reason);
- public void Move(PixelPoint point) => Position = point;
- private void MoveResize(PixelPoint position, Size size, double scaling)
- {
- Move(position);
- _scalingOverride = scaling;
- UpdateScaling(true);
- Resize(size, true, PlatformResizeReason.Layout);
- }
- PixelSize ToPixelSize(Size size) => new PixelSize((int)(size.Width * RenderScaling), (int)(size.Height * RenderScaling));
-
- void Resize(Size clientSize, bool force, PlatformResizeReason reason)
- {
- if (!force && clientSize == ClientSize)
- return;
-
- var needImmediatePopupResize = clientSize != ClientSize;
- var pixelSize = ToPixelSize(clientSize);
- UpdateSizeHints(pixelSize);
- XConfigureResizeWindow(_x11.Display, _handle, pixelSize);
- if (_useRenderWindow)
- XConfigureResizeWindow(_x11.Display, _renderHandle, pixelSize);
- XFlush(_x11.Display);
- if (force || !_wasMappedAtLeastOnce || (_popup && needImmediatePopupResize))
- {
- _realSize = pixelSize;
- Resized?.Invoke(ClientSize, reason);
- }
- }
-
- public void CanResize(bool value)
- {
- _canResize = value;
- UpdateMotifHints();
- UpdateSizeHints(null);
- }
- public void SetCursor(ICursorImpl cursor)
- {
- if (cursor == null)
- XDefineCursor(_x11.Display, _handle, _x11.DefaultCursor);
- else if (cursor is CursorImpl impl)
- {
- XDefineCursor(_x11.Display, _handle, impl.Handle);
- }
- }
- public IPlatformHandle Handle { get; }
-
- public PixelPoint Position
- {
- get => _position ?? default;
- set
- {
- var changes = new XWindowChanges
- {
- x = (int)value.X,
- y = (int)value.Y
- };
- XConfigureWindow(_x11.Display, _handle, ChangeWindowFlags.CWX | ChangeWindowFlags.CWY,
- ref changes);
- XFlush(_x11.Display);
- if (!_wasMappedAtLeastOnce)
- {
- _position = value;
- PositionChanged?.Invoke(value);
- }
- }
- }
- public IMouseDevice MouseDevice => _mouse;
- public TouchDevice TouchDevice => _touch;
- public IPopupImpl CreatePopup()
- => _platform.Options.OverlayPopups ? null : new X11Window(_platform, this);
- public void Activate()
- {
- if (_x11.Atoms._NET_ACTIVE_WINDOW != IntPtr.Zero)
- {
- SendNetWMMessage(_x11.Atoms._NET_ACTIVE_WINDOW, (IntPtr)1, _x11.LastActivityTimestamp,
- IntPtr.Zero);
- }
- else
- {
- XRaiseWindow(_x11.Display, _handle);
- XSetInputFocus(_x11.Display, _handle, 0, IntPtr.Zero);
- }
- }
- public IScreenImpl Screen => _platform.Screens;
- public Size MaxAutoSizeHint => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.PixelDensity))
- .OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
- void SendNetWMMessage(IntPtr message_type, IntPtr l0,
- IntPtr? l1 = null, IntPtr? l2 = null, IntPtr? l3 = null, IntPtr? l4 = null)
- {
- var xev = new XEvent
- {
- ClientMessageEvent =
- {
- type = XEventName.ClientMessage,
- send_event = 1,
- window = _handle,
- message_type = message_type,
- format = 32,
- ptr1 = l0,
- ptr2 = l1 ?? IntPtr.Zero,
- ptr3 = l2 ?? IntPtr.Zero,
- ptr4 = l3 ?? IntPtr.Zero
- }
- };
- xev.ClientMessageEvent.ptr4 = l4 ?? IntPtr.Zero;
- XSendEvent(_x11.Display, _x11.RootWindow, false,
- new IntPtr((int)(EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
- }
- void BeginMoveResize(NetWmMoveResize side, PointerPressedEventArgs e)
- {
- var pos = GetCursorPos(_x11);
- XUngrabPointer(_x11.Display, new IntPtr(0));
- SendNetWMMessage (_x11.Atoms._NET_WM_MOVERESIZE, (IntPtr) pos.x, (IntPtr) pos.y,
- (IntPtr) side,
- (IntPtr) 1, (IntPtr)1); // left button
-
- e.Pointer.Capture(null);
- }
- public void BeginMoveDrag(PointerPressedEventArgs e)
- {
- BeginMoveResize(NetWmMoveResize._NET_WM_MOVERESIZE_MOVE, e);
- }
- public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e)
- {
- var side = NetWmMoveResize._NET_WM_MOVERESIZE_CANCEL;
- if (edge == WindowEdge.East)
- side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_RIGHT;
- if (edge == WindowEdge.North)
- side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_TOP;
- if (edge == WindowEdge.South)
- side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOM;
- if (edge == WindowEdge.West)
- side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_LEFT;
- if (edge == WindowEdge.NorthEast)
- side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
- if (edge == WindowEdge.NorthWest)
- side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_TOPLEFT;
- if (edge == WindowEdge.SouthEast)
- side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
- if (edge == WindowEdge.SouthWest)
- side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
- BeginMoveResize(side, e);
- }
- public void SetTitle(string title)
- {
- if (string.IsNullOrEmpty(title))
- {
- XDeleteProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_NAME);
- XDeleteProperty(_x11.Display, _handle, _x11.Atoms.XA_WM_NAME);
- }
- else
- {
- var data = Encoding.UTF8.GetBytes(title);
- fixed (void* pdata = data)
- {
- XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_NAME, _x11.Atoms.UTF8_STRING, 8,
- PropertyMode.Replace, pdata, data.Length);
- XStoreName(_x11.Display, _handle, title);
- }
- }
- }
- public void SetWmClass(string wmClass)
- {
- // See https://tronche.com/gui/x/icccm/sec-4.html#WM_CLASS
- // We don't actually parse the application's command line, so we only use RESOURCE_NAME and argv[0]
- var appId = Environment.GetEnvironmentVariable("RESOURCE_NAME")
- ?? Process.GetCurrentProcess().ProcessName;
-
- var encodedAppId = Encoding.ASCII.GetBytes(appId);
- var encodedWmClass = Encoding.ASCII.GetBytes(wmClass ?? appId);
- var hint = XAllocClassHint();
- fixed(byte* pAppId = encodedAppId)
- fixed (byte* pWmClass = encodedWmClass)
- {
- hint->res_name = pAppId;
- hint->res_class = pWmClass;
- XSetClassHint(_x11.Display, _handle, hint);
- }
- XFree(hint);
- }
- public void SetMinMaxSize(Size minSize, Size maxSize)
- {
- _scaledMinMaxSize = (minSize, maxSize);
- var min = new PixelSize(
- (int)(minSize.Width < 1 ? 1 : minSize.Width * RenderScaling),
- (int)(minSize.Height < 1 ? 1 : minSize.Height * RenderScaling));
- const int maxDim = MaxWindowDimension;
- var max = new PixelSize(
- (int)(maxSize.Width > maxDim ? maxDim : Math.Max(min.Width, maxSize.Width * RenderScaling)),
- (int)(maxSize.Height > maxDim ? maxDim : Math.Max(min.Height, maxSize.Height * RenderScaling)));
-
- _minMaxSize = (min, max);
- UpdateSizeHints(null);
- }
- public void SetTopmost(bool value)
- {
- ChangeWMAtoms(value, _x11.Atoms._NET_WM_STATE_ABOVE);
- }
-
- public void SetEnabled(bool enable)
- {
- _disabled = !enable;
- }
- public void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint)
- {
- }
- public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints)
- {
- }
- public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight)
- {
- }
- public Action GotInputWhenDisabled { get; set; }
- public void SetIcon(IWindowIconImpl icon)
- {
- if (icon != null)
- {
- var data = ((X11IconData)icon).Data;
- fixed (void* pdata = data)
- XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_ICON,
- new IntPtr((int)Atom.XA_CARDINAL), 32, PropertyMode.Replace,
- pdata, data.Length);
- }
- else
- {
- XDeleteProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_ICON);
- }
- }
- public void ShowTaskbarIcon(bool value)
- {
- ChangeWMAtoms(!value, _x11.Atoms._NET_WM_STATE_SKIP_TASKBAR);
- }
- void ChangeWMAtoms(bool enable, params IntPtr[] atoms)
- {
- if (atoms.Length != 1 && atoms.Length != 2)
- throw new ArgumentException();
- if (!_mapped)
- {
- XGetWindowProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_STATE, IntPtr.Zero, new IntPtr(256),
- false, (IntPtr)Atom.XA_ATOM, out _, out _, out var nitems, out _,
- out var prop);
- var ptr = (IntPtr*)prop.ToPointer();
- var newAtoms = new HashSet<IntPtr>();
- for (var c = 0; c < nitems.ToInt64(); c++)
- newAtoms.Add(*ptr);
- XFree(prop);
- foreach(var atom in atoms)
- if (enable)
- newAtoms.Add(atom);
- else
- newAtoms.Remove(atom);
- XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_STATE, (IntPtr)Atom.XA_ATOM, 32,
- PropertyMode.Replace, newAtoms.ToArray(), newAtoms.Count);
- }
-
- SendNetWMMessage(_x11.Atoms._NET_WM_STATE,
- (IntPtr)(enable ? 1 : 0),
- atoms[0],
- atoms.Length > 1 ? atoms[1] : IntPtr.Zero,
- atoms.Length > 2 ? atoms[2] : IntPtr.Zero,
- atoms.Length > 3 ? atoms[3] : IntPtr.Zero
- );
- }
- public IPopupPositioner PopupPositioner { get; }
- public ITopLevelNativeMenuExporter NativeMenuExporter { get; }
- public INativeControlHostImpl NativeControlHost { get; }
- public ITextInputMethodImpl TextInputMethod => _ime;
- public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) =>
- _transparencyHelper?.SetTransparencyRequest(transparencyLevel);
- public void SetWindowManagerAddShadowHint(bool enabled)
- {
- }
- public WindowTransparencyLevel TransparencyLevel =>
- _transparencyHelper?.CurrentLevel ?? WindowTransparencyLevel.None;
- public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0.8);
- public bool NeedsManagedDecorations => false;
- public IStorageProvider StorageProvider { get; }
- public class SurfacePlatformHandle : IPlatformNativeSurfaceHandle
- {
- private readonly X11Window _owner;
- public PixelSize Size => _owner.ToPixelSize(_owner.ClientSize);
- public double Scaling => _owner.RenderScaling;
- public SurfacePlatformHandle(X11Window owner)
- {
- _owner = owner;
- }
- public IntPtr Handle => _owner._renderHandle;
- public string HandleDescriptor => "XID";
- }
- }
- }
|