From e7c1c3dbecb85ac86af782677c62db99309bf34a Mon Sep 17 00:00:00 2001 From: Robin Dostal Date: Mon, 14 Dec 2020 18:00:05 +0100 Subject: [PATCH 01/16] More reliable pixendensitydetection under Xorg/Randr Physical dimensions are parsed from the monitors EDID instead of Randr. GuessPixelDensity locks to fixed values, caps at 2 and returns 1 if the supplied dimensions are unreasonable. --- src/Avalonia.X11/X11Atoms.cs | 2 + src/Avalonia.X11/X11Screens.cs | 81 +++++++++++++++++++++++++--------- src/Avalonia.X11/XLib.cs | 7 +++ 3 files changed, 70 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.X11/X11Atoms.cs b/src/Avalonia.X11/X11Atoms.cs index 32f1822e51..137757b9cf 100644 --- a/src/Avalonia.X11/X11Atoms.cs +++ b/src/Avalonia.X11/X11Atoms.cs @@ -114,6 +114,8 @@ namespace Avalonia.X11 public readonly IntPtr XA_WM_CLASS = (IntPtr)67; public readonly IntPtr XA_WM_TRANSIENT_FOR = (IntPtr)68; + public readonly IntPtr RR_PROPERTY_RANDR_EDID = (IntPtr)82; + public readonly IntPtr WM_PROTOCOLS; public readonly IntPtr WM_DELETE_WINDOW; public readonly IntPtr WM_TAKE_FOCUS; diff --git a/src/Avalonia.X11/X11Screens.cs b/src/Avalonia.X11/X11Screens.cs index af6d2674b6..df1b2b7312 100644 --- a/src/Avalonia.X11/X11Screens.cs +++ b/src/Avalonia.X11/X11Screens.cs @@ -82,6 +82,39 @@ namespace Avalonia.X11 _cache = null; } + private unsafe Size? GetPhysicalMonitorSizeFromEDID(IntPtr rrOutput) + { + if(rrOutput == IntPtr.Zero) + return null; + var output = Marshal.ReadInt64(rrOutput); + var properties = XRRListOutputProperties(_x11.Display,output, out int propertyCount); + var hasEDID = false; + for(var pc = 0; pc < propertyCount; pc++) + { + if(properties[pc] == _x11.Atoms.RR_PROPERTY_RANDR_EDID) + hasEDID = true; + } + if(!hasEDID) + return null; + XRRGetOutputProperty(_x11.Display, output, _x11.Atoms.RR_PROPERTY_RANDR_EDID, 0, 128, false, false, _x11.Atoms.AnyPropertyType, out int actualType, out int actualFormat, out int nItems, out _, out IntPtr prop); + if(actualType != 19) // XA_INTEGER + return null; + if(actualFormat != 8) + return null; + + var edid = new byte[nItems]; + Marshal.Copy(prop,edid,0,nItems); + XFree(prop); + XFree(new IntPtr(properties)); + if(edid.Length < 22) + return null; + var width = edid[21]; + var height = edid[22]; + if(width == 0 && height == 0) + return null; + return new Size(width * 10, height * 10); + } + public unsafe X11Screen[] Screens { get @@ -97,24 +130,13 @@ namespace Avalonia.X11 var namePtr = XGetAtomName(_x11.Display, mon.Name); var name = Marshal.PtrToStringAnsi(namePtr); XFree(namePtr); - + var bounds = new PixelRect(mon.X, mon.Y, mon.Width, mon.Height); var density = 1d; - if (_settings.NamedScaleFactors?.TryGetValue(name, out density) != true) - { - if (mon.MWidth == 0) - density = 1; - else - density = X11Screen.GuessPixelDensity(mon.Width, mon.MWidth); - } - + var pSize = GetPhysicalMonitorSizeFromEDID(mon.Outputs); + if (_settings.NamedScaleFactors?.TryGetValue(name, out density) != true && pSize != null) + density = X11Screen.GuessPixelDensity(bounds, pSize.Value); density *= _settings.GlobalScaleFactor; - - var bounds = new PixelRect(mon.X, mon.Y, mon.Width, mon.Height); - screens[c] = new X11Screen(bounds, - mon.Primary != 0, - name, - (mon.MWidth == 0 || mon.MHeight == 0) ? (Size?)null : new Size(mon.MWidth, mon.MHeight), - density); + screens[c] = new X11Screen(bounds, mon.Primary != 0, name, pSize, density); } XFree(new IntPtr(monitors)); @@ -163,7 +185,6 @@ namespace Avalonia.X11 } - public int ScreenCount => _impl.Screens.Length; public IReadOnlyList AllScreens => @@ -229,6 +250,7 @@ namespace Avalonia.X11 class X11Screen { private const int FullHDWidth = 1920; + private const int FullHDHeight = 1080; public bool Primary { get; } public string Name { get; set; } public PixelRect Bounds { get; set; } @@ -248,7 +270,7 @@ namespace Avalonia.X11 } else if (pixelDensity == null) { - PixelDensity = GuessPixelDensity(bounds.Width, physicalSize.Value.Width); + PixelDensity = GuessPixelDensity(bounds, physicalSize.Value); } else { @@ -257,7 +279,26 @@ namespace Avalonia.X11 } } - public static double GuessPixelDensity(double pixelWidth, double mmWidth) - => pixelWidth <= FullHDWidth ? 1 : Math.Max(1, Math.Round(pixelWidth / mmWidth * 25.4 / 96)); + public static double GuessPixelDensity(PixelRect pixel, Size physical) + { + var calculatedDensity = 1d; + if(physical.Width > 0) + calculatedDensity = pixel.Width <= FullHDWidth ? 1 : Math.Max(1, pixel.Width / physical.Width * 25.4 / 96); + else if(physical.Height > 0) + calculatedDensity = pixel.Height <= FullHDHeight ? 1 : Math.Max(1, pixel.Height / physical.Height * 25.4 / 96); + + if(calculatedDensity > 3) + return 1; + else + { + var sanePixelDensities = new double[] { 1, 1.25, 1.50, 1.75, 2 }; + foreach(var saneDensity in sanePixelDensities) + { + if(calculatedDensity <= saneDensity + 0.20) + return saneDensity; + } + return sanePixelDensities.Last(); + } + } } } diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs index 85aa4862b7..92308cb7a1 100644 --- a/src/Avalonia.X11/XLib.cs +++ b/src/Avalonia.X11/XLib.cs @@ -500,6 +500,13 @@ namespace Avalonia.X11 [DllImport(libX11Randr)] public static extern XRRMonitorInfo* XRRGetMonitors(IntPtr dpy, IntPtr window, bool get_active, out int nmonitors); + + [DllImport(libX11Randr)] + public static extern IntPtr* XRRListOutputProperties(IntPtr dpy, long output, out int count); + + [DllImport(libX11Randr)] + public static extern int XRRGetOutputProperty(IntPtr dpy, long output, IntPtr atom, long offset, long length, bool _delete, bool pending, IntPtr req_type, out int actual_type, out int actual_format, out int nitems, out long bytes_after, out IntPtr prop); + [DllImport(libX11Randr)] public static extern void XRRSelectInput(IntPtr dpy, IntPtr window, RandrEventMask mask); From c2b405b45c15cf102e3ae354a0ca1ef3bccf801c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 16 Dec 2020 09:04:56 +0000 Subject: [PATCH 02/16] Set correct pointerover brush for expander control --- src/Avalonia.Themes.Fluent/Controls/Expander.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml index 97ca62c493..ad0fd18ea5 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml @@ -108,7 +108,7 @@ + + ToolTip A control which pops up a hint when a control is hovered - @@ -38,6 +38,31 @@ ToolTip bottom placement + + + + + Moving offset + From ca947cc912288c464ec5e30e7c21d7b94328dac8 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 3 Jan 2021 04:04:19 -0500 Subject: [PATCH 12/16] Use Bounds instead of TransformedBoundsProperty to recalculate ToolTip position --- src/Avalonia.Controls/ToolTipService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ToolTipService.cs b/src/Avalonia.Controls/ToolTipService.cs index cd0fad1997..d40a116dd5 100644 --- a/src/Avalonia.Controls/ToolTipService.cs +++ b/src/Avalonia.Controls/ToolTipService.cs @@ -103,7 +103,7 @@ namespace Avalonia.Controls { var control = (Control)sender; - if (e.Property == Visual.TransformedBoundsProperty) + if (e.Property == Visual.BoundsProperty) { var toolTip = control.GetValue(ToolTip.ToolTipProperty); toolTip?.RecalculatePosition(control); From ee717223f002832853d93e5d7d2b734e9431ca4d Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 3 Jan 2021 16:18:31 -0500 Subject: [PATCH 13/16] Use EffectiveViewportChanged instead of subscribing on PropertyChanged --- src/Avalonia.Controls/ToolTipService.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/ToolTipService.cs b/src/Avalonia.Controls/ToolTipService.cs index d40a116dd5..9e0bf60f42 100644 --- a/src/Avalonia.Controls/ToolTipService.cs +++ b/src/Avalonia.Controls/ToolTipService.cs @@ -28,14 +28,12 @@ namespace Avalonia.Controls { control.PointerEnter -= ControlPointerEnter; control.PointerLeave -= ControlPointerLeave; - control.PropertyChanged -= Control_PropertyChanged; } if (e.NewValue != null) { control.PointerEnter += ControlPointerEnter; control.PointerLeave += ControlPointerLeave; - control.PropertyChanged += Control_PropertyChanged; } if (ToolTip.GetIsOpen(control) && e.NewValue != e.OldValue && !(e.NewValue is ToolTip)) @@ -53,10 +51,12 @@ namespace Avalonia.Controls if (e.OldValue is false && e.NewValue is true) { control.DetachedFromVisualTree += ControlDetaching; + control.EffectiveViewportChanged += ControlEffectiveViewportChanged; } else if(e.OldValue is true && e.NewValue is false) { control.DetachedFromVisualTree -= ControlDetaching; + control.EffectiveViewportChanged -= ControlEffectiveViewportChanged; } } @@ -64,6 +64,7 @@ namespace Avalonia.Controls { var control = (Control)sender; control.DetachedFromVisualTree -= ControlDetaching; + control.EffectiveViewportChanged -= ControlEffectiveViewportChanged; Close(control); } @@ -99,15 +100,11 @@ namespace Avalonia.Controls Close(control); } - private void Control_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + private void ControlEffectiveViewportChanged(object sender, Layout.EffectiveViewportChangedEventArgs e) { var control = (Control)sender; - - if (e.Property == Visual.BoundsProperty) - { - var toolTip = control.GetValue(ToolTip.ToolTipProperty); - toolTip?.RecalculatePosition(control); - } + var toolTip = control.GetValue(ToolTip.ToolTipProperty); + toolTip?.RecalculatePosition(control); } private void StartShowTimer(int showDelay, Control control) From 90344d4c03fd0b983fd0de59f9a4c4905dfc5057 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 4 Jan 2021 11:39:11 +0200 Subject: [PATCH 14/16] fix #5217 native immediate crash on 0,0 rendering popup/window macos --- native/Avalonia.Native/src/OSX/rendertarget.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index 00b6dab219..9ec08853e1 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -111,7 +111,11 @@ if(_renderbuffer != 0) glDeleteRenderbuffers(1, &_renderbuffer); } - CFRelease(surface); + + if(surface != nullptr) + { + CFRelease(surface); + } } @end From 78c2415462c98ae4c3fbef100a005e067fe13920 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 4 Jan 2021 11:39:11 +0200 Subject: [PATCH 15/16] ensure we have valid surface with min size (1,1) --- native/Avalonia.Native/src/OSX/rendertarget.mm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index 9ec08853e1..b2d4341bb9 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -149,6 +149,12 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta } - (void)resize:(AvnPixelSize)size withScale: (float) scale{ + + if(size.Height <= 0) + size.Height = 1; + if(size.Width <= 0) + size.Width = 1; + @synchronized (lock) { if(surface == nil || surface->size.Width != size.Width From 5db58e766061afc737c3be5e3e6477e04c32a220 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Tue, 5 Jan 2021 13:48:37 +0100 Subject: [PATCH 16/16] Use correct sdk --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 15392278c9..fbd8507193 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -51,7 +51,7 @@ jobs: inputs: actions: 'build' scheme: '' - sdk: 'macosx11.0' + sdk: 'macosx11.1' configuration: 'Release' xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace' xcodeVersion: '12' # Options: 8, 9, default, specifyPath