mirror of
https://github.com/gwdevhub/Daybreak.git
synced 2026-07-15 15:19:57 +00:00
Co-authored-by: Alexandru Macocian <amacocian@microsoft.com>
This commit is contained in:
@@ -11,4 +11,6 @@ public sealed class ScreenManagerOptions
|
||||
public double Y { get; set; }
|
||||
public double Width { get; set; }
|
||||
public double Height { get; set; }
|
||||
public int DpiX { get; set; } = 96;
|
||||
public int DpiY { get; set; } = 96;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ internal sealed class ScreenManager(
|
||||
ILogger<ScreenManager> logger
|
||||
) : IScreenManager, IHostedService
|
||||
{
|
||||
private const int DefaultDpi = 96;
|
||||
|
||||
private readonly PhotinoWindow photinoWindow = photinoWindow.ThrowIfNull();
|
||||
private readonly IOptionsProvider optionsProvider = optionsProvider.ThrowIfNull();
|
||||
private readonly IOptionsMonitor<ScreenManagerOptions> liveUpdateableOptions =
|
||||
@@ -70,11 +72,15 @@ internal sealed class ScreenManager(
|
||||
this.photinoWindow.Height
|
||||
);
|
||||
|
||||
var currentDpi = GetDpiForPosition(position.Left, position.Top);
|
||||
|
||||
var options = this.liveUpdateableOptions.CurrentValue;
|
||||
options.X = position.Left;
|
||||
options.Y = position.Top;
|
||||
options.Width = position.Width;
|
||||
options.Height = position.Height;
|
||||
options.DpiX = currentDpi.X;
|
||||
options.DpiY = currentDpi.Y;
|
||||
this.optionsProvider.SaveOption(options);
|
||||
}
|
||||
|
||||
@@ -85,6 +91,8 @@ internal sealed class ScreenManager(
|
||||
options.Y = 0;
|
||||
options.Width = 0;
|
||||
options.Height = 0;
|
||||
options.DpiX = DefaultDpi;
|
||||
options.DpiY = DefaultDpi;
|
||||
this.optionsProvider.SaveOption(options);
|
||||
}
|
||||
|
||||
@@ -112,11 +120,12 @@ internal sealed class ScreenManager(
|
||||
|
||||
public Rectangle GetSavedPosition()
|
||||
{
|
||||
var savedOptions = this.liveUpdateableOptions.CurrentValue;
|
||||
var savedPosition = new Rectangle(
|
||||
(int)this.liveUpdateableOptions.CurrentValue.X,
|
||||
(int)this.liveUpdateableOptions.CurrentValue.Y,
|
||||
(int)this.liveUpdateableOptions.CurrentValue.Width,
|
||||
(int)this.liveUpdateableOptions.CurrentValue.Height
|
||||
(int)savedOptions.X,
|
||||
(int)savedOptions.Y,
|
||||
(int)savedOptions.Width,
|
||||
(int)savedOptions.Height
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -139,9 +148,74 @@ internal sealed class ScreenManager(
|
||||
);
|
||||
}
|
||||
|
||||
// Adjust position based on DPI scaling changes
|
||||
var savedDpiX = savedOptions.DpiX > 0 ? savedOptions.DpiX : DefaultDpi;
|
||||
var savedDpiY = savedOptions.DpiY > 0 ? savedOptions.DpiY : DefaultDpi;
|
||||
var currentDpi = GetDpiForPosition(savedPosition.Left, savedPosition.Top);
|
||||
|
||||
if (savedDpiX != currentDpi.X || savedDpiY != currentDpi.Y)
|
||||
{
|
||||
var scaleX = (double)currentDpi.X / savedDpiX;
|
||||
var scaleY = (double)currentDpi.Y / savedDpiY;
|
||||
savedPosition = new Rectangle(
|
||||
(int)(savedPosition.X * scaleX),
|
||||
(int)(savedPosition.Y * scaleY),
|
||||
(int)(savedPosition.Width * scaleX),
|
||||
(int)(savedPosition.Height * scaleY)
|
||||
);
|
||||
}
|
||||
|
||||
// Validate that the position is within visible screen bounds
|
||||
savedPosition = this.EnsurePositionIsOnScreen(savedPosition);
|
||||
|
||||
return savedPosition;
|
||||
}
|
||||
|
||||
private Rectangle EnsurePositionIsOnScreen(Rectangle position)
|
||||
{
|
||||
var screens = this.Screens.ToList();
|
||||
if (screens.Count == 0)
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
// Check if the window center is within any screen
|
||||
var centerX = position.Left + (position.Width / 2);
|
||||
var centerY = position.Top + (position.Height / 2);
|
||||
|
||||
var isOnScreen = screens.Any(s =>
|
||||
centerX >= s.Size.Left && centerX <= s.Size.Right &&
|
||||
centerY >= s.Size.Top && centerY <= s.Size.Bottom);
|
||||
|
||||
if (isOnScreen)
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
// Window is off-screen, move to the first available screen
|
||||
var firstScreen = screens.First();
|
||||
return new Rectangle(
|
||||
firstScreen.Size.X + (firstScreen.Size.Width / 4),
|
||||
firstScreen.Size.Y + (firstScreen.Size.Height / 4),
|
||||
Math.Min(position.Width, firstScreen.Size.Width / 2),
|
||||
Math.Min(position.Height, firstScreen.Size.Height / 2)
|
||||
);
|
||||
}
|
||||
|
||||
private static Point GetDpiForPosition(int x, int y)
|
||||
{
|
||||
var point = new NativeMethods.POINT { X = x, Y = y };
|
||||
var monitor = NativeMethods.MonitorFromPoint(point, NativeMethods.MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
if (monitor != IntPtr.Zero &&
|
||||
NativeMethods.GetDpiForMonitor(monitor, NativeMethods.MonitorDpiType.MDT_EFFECTIVE_DPI, out var dpiX, out var dpiY) == 0)
|
||||
{
|
||||
return new Point(dpiX, dpiY);
|
||||
}
|
||||
|
||||
return new Point(DefaultDpi, DefaultDpi);
|
||||
}
|
||||
|
||||
private static IntPtr? GetMainWindowHandle()
|
||||
{
|
||||
var process = Process.GetProcessesByName("gw").FirstOrDefault();
|
||||
|
||||
@@ -87,6 +87,14 @@ public static class NativeMethods
|
||||
DWMWCP_ROUNDSMALL = 3
|
||||
}
|
||||
|
||||
public enum MonitorDpiType
|
||||
{
|
||||
MDT_EFFECTIVE_DPI = 0,
|
||||
MDT_ANGULAR_DPI = 1,
|
||||
MDT_RAW_DPI = 2,
|
||||
MDT_DEFAULT = MDT_EFFECTIVE_DPI
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ThreadAccess : uint
|
||||
{
|
||||
@@ -703,4 +711,12 @@ public static class NativeMethods
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool GetMonitorInfo(nint hMonitor, ref MONITORINFO lpmi);
|
||||
|
||||
[DllImport("shcore.dll")]
|
||||
public static extern int GetDpiForMonitor(IntPtr hMonitor, MonitorDpiType dpiType, out int dpiX, out int dpiY);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr MonitorFromPoint(POINT pt, uint dwFlags);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user