Browse Source

Merge pull request #5178 from robinxan/master

More reliable pixendensitydetection under Xorg/Randr
pull/5283/head
Dan Walmsley 6 years ago
committed by GitHub
parent
commit
13e0e302fe
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/Avalonia.X11/X11Atoms.cs
  2. 94
      src/Avalonia.X11/X11Screens.cs
  3. 4
      src/Avalonia.X11/X11Structs.cs
  4. 7
      src/Avalonia.X11/XLib.cs

2
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;

94
src/Avalonia.X11/X11Screens.cs

@ -66,7 +66,8 @@ namespace Avalonia.X11
private X11Screen[] _cache;
private X11Info _x11;
private IntPtr _window;
const int EDIDStructureLength = 32; // Length of a EDID-Block-Length(128 bytes), XRRGetOutputProperty multiplies offset and length by 4
public Randr15ScreensImpl(AvaloniaX11Platform platform, X11ScreensUserSettings settings)
{
_settings = settings;
@ -82,6 +83,38 @@ namespace Avalonia.X11
_cache = null;
}
private unsafe Size? GetPhysicalMonitorSizeFromEDID(IntPtr rrOutput)
{
if(rrOutput == IntPtr.Zero)
return null;
var properties = XRRListOutputProperties(_x11.Display,rrOutput, 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, rrOutput, _x11.Atoms.RR_PROPERTY_RANDR_EDID, 0, EDIDStructureLength, false, false, _x11.Atoms.AnyPropertyType, out IntPtr actualType, out int actualFormat, out int bytesAfter, out _, out IntPtr prop);
if(actualType != _x11.Atoms.XA_INTEGER)
return null;
if(actualFormat != 8) // Expecting an byte array
return null;
var edid = new byte[bytesAfter];
Marshal.Copy(prop,edid,0,bytesAfter);
XFree(prop);
XFree(new IntPtr(properties));
if(edid.Length < 22)
return null;
var width = edid[21]; // 0x15 1 Max. Horizontal Image Size cm.
var height = edid[22]; // 0x16 1 Max. Vertical Image Size cm.
if(width == 0 && height == 0)
return null;
return new Size(width * 10, height * 10);
}
public unsafe X11Screen[] Screens
{
get
@ -97,24 +130,28 @@ namespace Avalonia.X11
var namePtr = XGetAtomName(_x11.Display, mon.Name);
var name = Marshal.PtrToStringAnsi(namePtr);
XFree(namePtr);
var density = 1d;
var bounds = new PixelRect(mon.X, mon.Y, mon.Width, mon.Height);
Size? pSize = null;
double density = 0;
if (_settings.NamedScaleFactors?.TryGetValue(name, out density) != true)
{
if (mon.MWidth == 0)
density = 1;
else
density = X11Screen.GuessPixelDensity(mon.Width, mon.MWidth);
for(int o = 0; o < mon.NOutput; o++)
{
var outputSize = GetPhysicalMonitorSizeFromEDID(mon.Outputs[o]);
var outputDensity = 1d;
if(outputSize != null)
outputDensity = X11Screen.GuessPixelDensity(bounds, outputSize.Value);
if(density == 0 || density > outputDensity)
{
density = outputDensity;
pSize = outputSize;
}
}
}
if(density == 0)
density = 1;
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 +200,6 @@ namespace Avalonia.X11
}
public int ScreenCount => _impl.Screens.Length;
public IReadOnlyList<Screen> AllScreens =>
@ -229,6 +265,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 +285,7 @@ namespace Avalonia.X11
}
else if (pixelDensity == null)
{
PixelDensity = GuessPixelDensity(bounds.Width, physicalSize.Value.Width);
PixelDensity = GuessPixelDensity(bounds, physicalSize.Value);
}
else
{
@ -257,7 +294,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();
}
}
}
}

4
src/Avalonia.X11/X11Structs.cs

@ -1870,7 +1870,7 @@ namespace Avalonia.X11 {
public const string XNFontSet = "fontSet";
}
struct XRRMonitorInfo {
unsafe struct XRRMonitorInfo {
public IntPtr Name;
public int Primary;
public int Automatic;
@ -1881,6 +1881,6 @@ namespace Avalonia.X11 {
public int Height;
public int MWidth;
public int MHeight;
public IntPtr Outputs;
public IntPtr* Outputs;
}
}

7
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, IntPtr output, out int count);
[DllImport(libX11Randr)]
public static extern int XRRGetOutputProperty(IntPtr dpy, IntPtr output, IntPtr atom, int offset, int length, bool _delete, bool pending, IntPtr req_type, out IntPtr 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);

Loading…
Cancel
Save