|
@@ -27,13 +27,15 @@ namespace Avalonia.X11
|
|
|
private IInputRoot _inputRoot;
|
|
|
private readonly IMouseDevice _mouse;
|
|
|
private readonly IKeyboardDevice _keyboard;
|
|
|
- private Point _position;
|
|
|
+ private Point? _position;
|
|
|
+ private PixelSize _realSize;
|
|
|
private IntPtr _handle;
|
|
|
private IntPtr _xic;
|
|
|
private IntPtr _renderHandle;
|
|
|
private bool _mapped;
|
|
|
private HashSet<X11Window> _transientChildren = new HashSet<X11Window>();
|
|
|
private X11Window _transientParent;
|
|
|
+ public object SyncRoot { get; } = new object();
|
|
|
|
|
|
class InputEventContainer
|
|
|
{
|
|
@@ -79,7 +81,7 @@ namespace Avalonia.X11
|
|
|
SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr);
|
|
|
|
|
|
Handle = new PlatformHandle(_handle, "XID");
|
|
|
- ClientSize = new Size(400, 400);
|
|
|
+ _realSize = new PixelSize(300, 200);
|
|
|
platform.Windows[_handle] = OnEvent;
|
|
|
XEventMask ignoredMask = XEventMask.SubstructureRedirectMask
|
|
|
| XEventMask.ResizeRedirectMask
|
|
@@ -96,12 +98,12 @@ namespace Avalonia.X11
|
|
|
var feature = (EglGlPlatformFeature)AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
|
|
|
var surfaces = new List<object>
|
|
|
{
|
|
|
- new X11FramebufferSurface(_x11.DeferredDisplay, _handle)
|
|
|
+ new X11FramebufferSurface(_x11.DeferredDisplay, _renderHandle, () => Scaling)
|
|
|
};
|
|
|
if (feature != null)
|
|
|
surfaces.Insert(0,
|
|
|
new EglGlPlatformSurface((EglDisplay)feature.Display, feature.DeferredContext,
|
|
|
- new SurfaceInfo(_x11.DeferredDisplay, _handle, _renderHandle)));
|
|
|
+ new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
|
|
|
Surfaces = surfaces.ToArray();
|
|
|
UpdateMotifHits();
|
|
|
XFlush(_x11.Display);
|
|
@@ -109,11 +111,13 @@ namespace Avalonia.X11
|
|
|
|
|
|
class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
|
|
|
{
|
|
|
+ private readonly X11Window _window;
|
|
|
private readonly IntPtr _display;
|
|
|
private readonly IntPtr _parent;
|
|
|
|
|
|
- public SurfaceInfo(IntPtr display, IntPtr parent, IntPtr xid)
|
|
|
+ public SurfaceInfo(X11Window window, IntPtr display, IntPtr parent, IntPtr xid)
|
|
|
{
|
|
|
+ _window = window;
|
|
|
_display = display;
|
|
|
_parent = parent;
|
|
|
Handle = xid;
|
|
@@ -134,7 +138,7 @@ namespace Avalonia.X11
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public double Scaling { get; } = 1;
|
|
|
+ public double Scaling => _window.Scaling;
|
|
|
}
|
|
|
|
|
|
void UpdateMotifHits()
|
|
@@ -188,10 +192,20 @@ namespace Avalonia.X11
|
|
|
|
|
|
XSetWMNormalHints(_x11.Display, _handle, ref hints);
|
|
|
}
|
|
|
-
|
|
|
- public Size ClientSize { get; private set; }
|
|
|
- //TODO
|
|
|
- public double Scaling { get; } = 1;
|
|
|
+
|
|
|
+ public Size ClientSize => new Size(_realSize.Width / Scaling, _realSize.Height / Scaling);
|
|
|
+
|
|
|
+ public double Scaling
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ lock (SyncRoot)
|
|
|
+ return _scaling;
|
|
|
+
|
|
|
+ }
|
|
|
+ private set => _scaling = value;
|
|
|
+ }
|
|
|
+
|
|
|
public IEnumerable<object> Surfaces { get; }
|
|
|
public Action<RawInputEventArgs> Input { get; set; }
|
|
|
public Action<Rect> Paint { get; set; }
|
|
@@ -209,6 +223,11 @@ namespace Avalonia.X11
|
|
|
new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>());
|
|
|
|
|
|
void OnEvent(XEvent ev)
|
|
|
+ {
|
|
|
+ lock (SyncRoot)
|
|
|
+ OnEventSync(ev);
|
|
|
+ }
|
|
|
+ void OnEventSync(XEvent ev)
|
|
|
{
|
|
|
if(XFilterEvent(ref ev, _handle))
|
|
|
return;
|
|
@@ -269,6 +288,8 @@ namespace Avalonia.X11
|
|
|
}
|
|
|
else if (ev.type == XEventName.ConfigureNotify)
|
|
|
{
|
|
|
+ if (ev.ConfigureEvent.window != _handle)
|
|
|
+ return;
|
|
|
var needEnqueue = (_configure == null);
|
|
|
_configure = ev.ConfigureEvent;
|
|
|
if (needEnqueue)
|
|
@@ -278,22 +299,27 @@ namespace Avalonia.X11
|
|
|
return;
|
|
|
var cev = _configure.Value;
|
|
|
_configure = null;
|
|
|
- var nsize = new Size(cev.width, cev.height);
|
|
|
- XTranslateCoordinates(_x11.Display, _handle, _x11.DefaultRootWindow, 0, 0, out var xret,
|
|
|
- out var yret, out var _);
|
|
|
- var npos = new Point(xret, yret);
|
|
|
- var changedSize = ClientSize != nsize;
|
|
|
- var changedPos = npos != _position;
|
|
|
- ClientSize = nsize;
|
|
|
+ var nsize = new PixelSize(cev.width, cev.height);
|
|
|
+ var npos = new Point(cev.x, cev.y);
|
|
|
+ var changedSize = _realSize != nsize;
|
|
|
+ var changedPos = _position == null || npos != _position;
|
|
|
+ _realSize = nsize;
|
|
|
_position = npos;
|
|
|
- if (changedSize)
|
|
|
- Resized?.Invoke(nsize);
|
|
|
+ bool updatedSizeViaScaling = false;
|
|
|
if (changedPos)
|
|
|
+ {
|
|
|
PositionChanged?.Invoke(npos);
|
|
|
+ updatedSizeViaScaling = UpdateScaling();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (changedSize && !updatedSizeViaScaling)
|
|
|
+ Resized?.Invoke(ClientSize);
|
|
|
+
|
|
|
Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout);
|
|
|
}, DispatcherPriority.Layout);
|
|
|
+ XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width, ev.ConfigureEvent.height);
|
|
|
}
|
|
|
- else if (ev.type == XEventName.DestroyNotify)
|
|
|
+ else if (ev.type == XEventName.DestroyNotify && ev.AnyEvent.window == _handle)
|
|
|
{
|
|
|
Cleanup();
|
|
|
}
|
|
@@ -338,6 +364,28 @@ namespace Avalonia.X11
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private bool UpdateScaling()
|
|
|
+ {
|
|
|
+ lock (SyncRoot)
|
|
|
+ {
|
|
|
+ var monitor = _platform.X11Screens.Screens.OrderBy(x => x.PixelDensity)
|
|
|
+ .FirstOrDefault(m => m.Bounds.Contains(Position));
|
|
|
+ var newScaling = monitor?.PixelDensity ?? Scaling;
|
|
|
+ if (Scaling != newScaling)
|
|
|
+ {
|
|
|
+ Console.WriteLine(
|
|
|
+ $"Updating scaling from {Scaling} to {newScaling} as a response to position change to {Position}");
|
|
|
+ var oldScaledSize = ClientSize;
|
|
|
+ Scaling = newScaling;
|
|
|
+ ScalingChanged?.Invoke(Scaling);
|
|
|
+ Resize(oldScaledSize, true);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private WindowState _lastWindowState;
|
|
|
public WindowState WindowState
|
|
|
{
|
|
@@ -431,6 +479,7 @@ namespace Avalonia.X11
|
|
|
private bool _systemDecorations = true;
|
|
|
private bool _canResize = true;
|
|
|
private (Size minSize, Size maxSize) _minMaxSize;
|
|
|
+ private double _scaling = 1;
|
|
|
|
|
|
void ScheduleInput(RawInputEventArgs args, ref XEvent xev)
|
|
|
{
|
|
@@ -440,6 +489,10 @@ namespace Avalonia.X11
|
|
|
|
|
|
public void ScheduleInput(RawInputEventArgs args)
|
|
|
{
|
|
|
+ if (args is RawMouseEventArgs mouse)
|
|
|
+ mouse.Position = mouse.Position / Scaling;
|
|
|
+ if (args is RawDragEvent drag)
|
|
|
+ drag.Location = drag.Location / Scaling;
|
|
|
|
|
|
_lastEvent = new InputEventContainer() {Event = args};
|
|
|
_inputQueue.Enqueue(_lastEvent);
|
|
@@ -564,35 +617,35 @@ namespace Avalonia.X11
|
|
|
public void Hide() => XUnmapWindow(_x11.Display, _handle);
|
|
|
|
|
|
|
|
|
- public Point PointToClient(Point point) => new Point(point.X - _position.X, point.Y - _position.Y);
|
|
|
+ public Point PointToClient(Point point) => new Point((point.X - Position.X) / Scaling, (point.Y - Position.Y) / Scaling);
|
|
|
|
|
|
- public Point PointToScreen(Point point) => new Point(point.X + _position.X, point.Y + _position.Y);
|
|
|
+ public Point PointToScreen(Point point) => new Point(point.X * Scaling + Position.X, point.Y * Scaling + Position.Y);
|
|
|
|
|
|
public void SetSystemDecorations(bool enabled)
|
|
|
{
|
|
|
_systemDecorations = enabled;
|
|
|
UpdateMotifHits();
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ public void Resize(Size clientSize) => Resize(clientSize, false);
|
|
|
|
|
|
-
|
|
|
- public void Resize(Size clientSize)
|
|
|
+ void Resize(Size clientSize, bool force)
|
|
|
{
|
|
|
- if (clientSize == ClientSize)
|
|
|
+ if (!force && clientSize == ClientSize)
|
|
|
return;
|
|
|
- var changes = new XWindowChanges
|
|
|
- {
|
|
|
- width = (int)clientSize.Width,
|
|
|
- height = (int)clientSize.Height
|
|
|
- };
|
|
|
- var needResize = clientSize != ClientSize;
|
|
|
- XConfigureWindow(_x11.Display, _handle, ChangeWindowFlags.CWHeight | ChangeWindowFlags.CWWidth,
|
|
|
- ref changes);
|
|
|
+
|
|
|
+ var needImmediatePopupResize = clientSize != ClientSize;
|
|
|
+
|
|
|
+ var pixelSize = new PixelSize((int)(clientSize.Width * Scaling), (int)(clientSize.Height * Scaling));
|
|
|
+ XConfigureResizeWindow(_x11.Display, _handle, pixelSize);
|
|
|
+ XConfigureResizeWindow(_x11.Display, _renderHandle, pixelSize);
|
|
|
XFlush(_x11.Display);
|
|
|
|
|
|
- if (_popup && needResize)
|
|
|
+ if (force || (_popup && needImmediatePopupResize))
|
|
|
{
|
|
|
- ClientSize = clientSize;
|
|
|
- Resized?.Invoke(clientSize);
|
|
|
+ _realSize = pixelSize;
|
|
|
+ Resized?.Invoke(ClientSize);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -618,7 +671,7 @@ namespace Avalonia.X11
|
|
|
|
|
|
public Point Position
|
|
|
{
|
|
|
- get => _position;
|
|
|
+ get => _position ?? default;
|
|
|
set
|
|
|
{
|
|
|
var changes = new XWindowChanges
|
|
@@ -650,9 +703,10 @@ namespace Avalonia.X11
|
|
|
}
|
|
|
|
|
|
|
|
|
- public IScreenImpl Screen { get; } = AvaloniaLocator.CurrentMutable.GetService<IScreenImpl>();
|
|
|
- public Size MaxClientSize { get; } = new Size(1920, 1280);
|
|
|
+ public IScreenImpl Screen => _platform.Screens;
|
|
|
|
|
|
+ public Size MaxClientSize => _platform.X11Screens.Screens.Select(s => s.Bounds.Size / s.PixelDensity)
|
|
|
+ .OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
|
|
|
|
|
|
|
|
|
void SendNetWMMessage(IntPtr message_type, IntPtr l0,
|