Skip to content

Commit

Permalink
Merge pull request #1832 from cwensley/curtis/wpf-more-system-dpi-fixes
Browse files Browse the repository at this point in the history
Wpf: More fixes getting proper screen bounds and image when using system dpi
  • Loading branch information
cwensley authored Nov 26, 2020
2 parents c3d43f2 + 88c4493 commit 2b9dc7c
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 29 deletions.
1 change: 1 addition & 0 deletions src/Eto.WinForms/Win32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public struct RECT
public int height => bottom - top;

public System.Drawing.Rectangle ToSD() => new System.Drawing.Rectangle(left, top, width, height);
public Eto.Drawing.Rectangle ToEto() => new Eto.Drawing.Rectangle(left, top, width, height);
}

[StructLayout(LayoutKind.Sequential)]
Expand Down
44 changes: 21 additions & 23 deletions src/Eto.WinForms/Win32.dpi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,39 +127,24 @@ public override sd.Rectangle GetBounds(swf.Screen screen)
if (!PerMonitorDpiSupported)
return screen.Bounds;

var oldDpiAwareness = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT.PER_MONITOR_AWARE_v2);

var hmonitor = MonitorFromPoint(screen.Bounds.Location, 0);
var info = new MONITORINFOEX();
GetMonitorInfo(new HandleRef(null, hmonitor), info);

//var bounds = screen.Bounds;
var bounds = info.rcMonitor.ToSD();
GetMonitorInfo(screen, ref info);

if (oldDpiAwareness != DPI_AWARENESS_CONTEXT.NONE)
SetThreadDpiAwarenessContext(oldDpiAwareness);
return bounds;
return info.rcMonitor.ToSD();
}

public sd.Rectangle GetWorkingArea(swf.Screen screen)
{
if (!PerMonitorDpiSupported)
return screen.WorkingArea;

var oldDpiAwareness = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT.PER_MONITOR_AWARE_v2);

var hmonitor = MonitorFromPoint(screen.Bounds.Location, 0);
var info = new MONITORINFOEX();
GetMonitorInfo(new HandleRef(null, hmonitor), info);

// var bounds = screen.WorkingArea;
var bounds = info.rcWork.ToSD();

if (oldDpiAwareness != DPI_AWARENESS_CONTEXT.NONE)
SetThreadDpiAwarenessContext(oldDpiAwareness);
return bounds;
GetMonitorInfo(screen, ref info);

return info.rcWork.ToSD();
}


DPI_AWARENESS_CONTEXT? processDpiAwareness;

public DPI_AWARENESS_CONTEXT ProcessDpiAwareness
Expand All @@ -183,11 +168,11 @@ public override float GetLogicalPixelSize(swf.Screen screen)
return (uint)graphics.DpiY / 96f;
}
}
var mon = MonitorFromPoint(screen.Bounds.Location, MONITOR.DEFAULTTONEAREST);

// use per-monitor aware dpi awareness to get ACTUAL dpi here
var oldDpiAwareness = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT.PER_MONITOR_AWARE_v2);

var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, MONITOR.DEFAULTTONEAREST);
uint dpiX, dpiY;
GetDpiForMonitor(mon, MDT.EFFECTIVE_DPI, out dpiX, out dpiY);

Expand All @@ -210,6 +195,19 @@ public override float GetLogicalPixelSize(swf.Screen screen)

public static float GetLogicalPixelSize(this swf.Screen screen) => locationHelper.GetLogicalPixelSize(screen);

public static void GetMonitorInfo(this swf.Screen screen, ref MONITORINFOEX info)
{
var hmonitor = MonitorFromPoint(screen.Bounds.Location, 0);

var oldDpiAwareness = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT.PER_MONITOR_AWARE_v2);

GetMonitorInfo(new HandleRef(null, hmonitor), info);

if (oldDpiAwareness != DPI_AWARENESS_CONTEXT.NONE)
SetThreadDpiAwarenessContext(oldDpiAwareness);
}


[DllImport("User32.dll")]
public static extern IntPtr MonitorFromPoint(System.Drawing.Point pt, MONITOR dwFlags);

Expand Down
7 changes: 4 additions & 3 deletions src/Eto.Wpf/Forms/MouseHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ public PointF Position
get
{
var oldDpiAwareness = Win32.PerMonitorDpiSupported ? Win32.SetThreadDpiAwarenessContext(Win32.DPI_AWARENESS_CONTEXT.PER_MONITOR_AWARE_v2) : Win32.DPI_AWARENESS_CONTEXT.NONE;
var result = swf.Control.MousePosition.ScreenToLogical();
var result = swf.Control.MousePosition;
if (oldDpiAwareness != Win32.DPI_AWARENESS_CONTEXT.NONE)
Win32.SetThreadDpiAwarenessContext(oldDpiAwareness);
return result;
return result.ScreenToLogical();
}
set
{
var pos = value.LogicalToScreen();
var oldDpiAwareness = Win32.PerMonitorDpiSupported ? Win32.SetThreadDpiAwarenessContext(Win32.DPI_AWARENESS_CONTEXT.PER_MONITOR_AWARE_v2) : Win32.DPI_AWARENESS_CONTEXT.NONE;
swf.Cursor.Position = Point.Round(value.LogicalToScreen()).ToSD();
swf.Cursor.Position = Point.Round(pos).ToSD();
if (oldDpiAwareness != Win32.DPI_AWARENESS_CONTEXT.NONE)
Win32.SetThreadDpiAwarenessContext(oldDpiAwareness);
}
Expand Down
11 changes: 10 additions & 1 deletion src/Eto.Wpf/Forms/ScreenHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ float GetRealScale()
public Image GetImage(RectangleF rect)
{
var adjustedRect = rect * Widget.LogicalPixelSize;
adjustedRect.Location += Control.Bounds.Location.ToEto();
//adjustedRect.Location += Control.Bounds.Location.ToEto();

var info = new Win32.MONITORINFOEX();
Win32.GetMonitorInfo(Control, ref info);
adjustedRect.Location += info.rcMonitor.ToEto().Location;

var oldDpiAwareness = Win32.PerMonitorDpiSupported ? Win32.SetThreadDpiAwarenessContext(Win32.DPI_AWARENESS_CONTEXT.PER_MONITOR_AWARE_v2) : Win32.DPI_AWARENESS_CONTEXT.NONE;
var realRect = Rectangle.Ceiling(adjustedRect);
using (var screenBmp = new sd.Bitmap(realRect.Width, realRect.Height, sd.Imaging.PixelFormat.Format32bppRgb))
{
Expand All @@ -64,6 +70,9 @@ public Image GetImage(RectangleF rect)
sw.Int32Rect.Empty,
sw.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

if (oldDpiAwareness != Win32.DPI_AWARENESS_CONTEXT.NONE)
Win32.SetThreadDpiAwarenessContext(oldDpiAwareness);

return new Bitmap(new BitmapHandler(bitmapSource));
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/Eto.Wpf/Forms/WpfWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -594,20 +594,23 @@ System.Windows.Forms.Screen SwfScreen
var handle = WindowHandle;
if (handle != IntPtr.Zero)
{
Point? location = null;
// Left/Top doesn't always report correct location when maximized, so use Win32 when we can.
var oldDpiAwareness = Win32.PerMonitorDpiSupported ? Win32.SetThreadDpiAwarenessContext(Win32.DPI_AWARENESS_CONTEXT.PER_MONITOR_AWARE_v2) : Win32.DPI_AWARENESS_CONTEXT.NONE;
try
{

Win32.RECT rect;
if (Win32.GetWindowRect(handle, out rect))
return Point.Round(new Point(rect.left, rect.top).ScreenToLogical(SwfScreen));
location = new Point(rect.left, rect.top);
}
finally
{
if (oldDpiAwareness != Win32.DPI_AWARENESS_CONTEXT.NONE)
Win32.SetThreadDpiAwarenessContext(oldDpiAwareness);
}

if (location != null)
return Point.Round(location.Value.ScreenToLogical(SwfScreen));
}
// in WPF, left/top of a window is transformed by the (current) screen dpi, which makes absolutely no sense.
var left = Control.Left;
Expand Down

0 comments on commit 2b9dc7c

Please sign in to comment.