Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions sources/engine/Stride.Games/Desktop/GameWindowWinforms.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
#if (STRIDE_GRAPHICS_API_DIRECT3D || STRIDE_GRAPHICS_API_VULKAN) && (STRIDE_UI_WINFORMS || STRIDE_UI_WPF)
#if STRIDE_PLATFORM_WINDOWS && (STRIDE_UI_WINFORMS || STRIDE_UI_WPF)
using System;
using System.Diagnostics;
using System.Drawing;
Expand Down Expand Up @@ -159,6 +159,7 @@ protected override void Initialize(GameContext<Control> gameContext)
gameForm.FullscreenToggle += OnFullscreenToggle;
gameForm.DisableFullScreen += OnDisableFullScreen;
gameForm.FormClosing += OnClosing;
gameForm.DpiChanged += OnDpiChanged;
}
else
{
Expand Down Expand Up @@ -273,12 +274,12 @@ public override bool Visible
}

/// <inheritdoc />
public override double Opacity
public override double Opacity
{
get
{
return form?.Opacity ?? 1.0d;
}
}
set
{
if (form != null)
Expand All @@ -287,7 +288,7 @@ public override double Opacity
}
}
}

public override Int2 Position
{
get
Expand Down Expand Up @@ -409,6 +410,14 @@ public override bool Focused
}
}

private void OnDpiChanged(object sender, DpiChangedEventArgs e)
{
Dpi = new Int2(e.DeviceDpiNew);
DpiScale = e.DeviceDpiNew / 96.0f;

base.OnDpiChanged(sender, e);
}

protected override void Destroy()
{
if (Control != null)
Expand Down
39 changes: 31 additions & 8 deletions sources/engine/Stride.Games/GameWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
//
// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand Down Expand Up @@ -72,6 +72,11 @@ public abstract class GameWindow : ComponentBase
/// </summary>
public event EventHandler<EventArgs> FullscreenChanged;

/// <summary>
/// Occurs when the DPI configuration of this window has changed.
/// </summary>
public event EventHandler<EventArgs> DpiChanged;

/// <summary>
/// Occurs before the window gets destroyed.
/// </summary>
Expand Down Expand Up @@ -127,7 +132,7 @@ public abstract class GameWindow : ComponentBase
/// </summary>
/// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
public abstract bool Visible { get; set; }

/// <summary>
/// Gets or sets the opacity of the window.
/// </summary>
Expand Down Expand Up @@ -174,7 +179,7 @@ public string Title
/// <summary>
/// The size the window should have when switching from fullscreen to windowed mode.
/// To get the current actual size use <see cref="ClientBounds"/>.
/// This gets overwritten when the user resizes the window.
/// This gets overwritten when the user resizes the window.
/// </summary>
public Int2 PreferredWindowedSize { get; set; } = new Int2(768, 432);

Expand Down Expand Up @@ -215,6 +220,18 @@ internal void SetIsReallyFullscreen(bool isReallyFullscreen)
isFullscreen = isReallyFullscreen;
}

/// <summary>
/// Gets the DPI scale factor of the display where this window is currently displayed,
/// which is used to convert between physical pixels and device-independent pixels (DIPs).
/// </summary>
public float DpiScale { get; protected set; } = 1.0f; // 100 % = 96 DPI

/// <summary>
/// Gets the dots per inch (DPI) of the display where this window is currently displayed
/// in the horizontal and vertical directions.
/// </summary>
public Int2 Dpi { get; protected set; } = new Int2(96); // 96 DPI (baseline DPI for Windows and many other platforms)

#endregion

#region Public Methods and Operators
Expand All @@ -241,7 +258,7 @@ public void EndScreenDeviceChange()
internal Action RunCallback;

internal Action ExitCallback;

private bool isFullscreen;

internal abstract void Run();
Expand Down Expand Up @@ -284,9 +301,9 @@ protected void OnClientSizeChanged(object source, EventArgs e)
{
if (!isFullscreen)
{
// Update preferred windowed size in windowed mode
// Update preferred windowed size in windowed mode
var resizeSize = ClientBounds.Size;
PreferredWindowedSize = new Int2(resizeSize.Width, resizeSize.Height);
PreferredWindowedSize = new Int2(resizeSize.Width, resizeSize.Height);
}
var handler = ClientSizeChanged;
handler?.Invoke(this, e);
Expand Down Expand Up @@ -316,6 +333,12 @@ protected void OnDisableFullScreen(object source, EventArgs e)
IsFullscreen = false;
}

protected void OnDpiChanged(object source, EventArgs e)
{
var handler = DpiChanged;
handler?.Invoke(this, e);
}

protected void OnClosing(object source, EventArgs e)
{
var handler = Closing;
Expand Down
6 changes: 5 additions & 1 deletion sources/engine/Stride.Games/SDL/GameFormSDL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ namespace Stride.Games
/// </summary>
public class GameFormSDL : Window
{
#region Initialization
#region Initialization

/// <summary>
/// Initializes a new instance of the 'GameForm' class.
/// </summary>
Expand All @@ -39,9 +40,11 @@ public GameFormSDL(string text) : base(text)
RestoredActions += GameForm_RestoredActions;
KeyDownActions += GameFormSDL_KeyDownActions;
}

#endregion

#region Events

/// <summary>
/// Occurs when [app activated].
/// </summary>
Expand Down Expand Up @@ -75,6 +78,7 @@ public GameFormSDL(string text) : base(text)
#endregion

#region Implementation

// TODO: The code below is taken from GameForm.cs of the Windows Desktop implementation. This needs reviewing
private FormWindowState previousWindowState;

Expand Down
12 changes: 10 additions & 2 deletions sources/engine/Stride.Games/SDL/GameWindowSDL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ protected override void Initialize(GameContext<Window> gameContext)

window.ClientSize = new Size2(width, height);

window.MouseEnterActions += WindowOnMouseEnterActions;
window.MouseEnterActions += WindowOnMouseEnterActions;
window.MouseLeaveActions += WindowOnMouseLeaveActions;

var gameForm = window as GameFormSDL;
Expand All @@ -120,7 +120,7 @@ protected override void Initialize(GameContext<Window> gameContext)
gameForm.SizeChanged += OnClientSizeChanged;
gameForm.CloseActions += GameForm_CloseActions;
gameForm.FullscreenToggle += OnFullscreenToggle;

gameForm.DisplayChangedActions += WindowOnDisplayChangedActions;
}
else
{
Expand Down Expand Up @@ -380,6 +380,14 @@ public override bool Focused
}
}

private void WindowOnDisplayChangedActions(WindowEvent sdlWindowEvent)
{
Dpi = new Int2((int) window.Dpi);
DpiScale = window.DpiScale;

OnDpiChanged(this, EventArgs.Empty);
}

protected override void Destroy()
{
if (window != null)
Expand Down
41 changes: 41 additions & 0 deletions sources/engine/Stride.Graphics/Direct3D/GraphicsOutput.Direct3D.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,18 @@ public sealed unsafe partial class GraphicsOutput
/// </summary>
public Vector2 WhitePoint { get; }

/// <summary>
/// Gets the DPI scale factor of the display attached to this output, which is used
/// to convert between physical pixels and device-independent pixels (DIPs).
/// </summary>
public float DpiScale { get; }

/// <summary>
/// Gets the dots per inch (DPI) of the display attached to this output
/// in the horizontal and vertical directions.
/// </summary>
public Int2 Dpi { get; }


/// <summary>
/// Initializes a new instance of <see cref="GraphicsOutput"/>.
Expand Down Expand Up @@ -221,6 +233,10 @@ internal GraphicsOutput(GraphicsAdapter adapter, ComPtr<IDXGIOutput> nativeOutpu
_ => DisplayRotation.Default
};

GetDpiForMonitor(MonitorHandle, out int dpiX, out int dpiY);
Dpi = new Int2(dpiX, dpiY);
DpiScale = dpiX / 96.0f;

if (dxgiOutputVersion >= 6)
{
var nativeOutput6 = nativeOutput.AsComPtrUnsafe<IDXGIOutput, IDXGIOutput6>();
Expand Down Expand Up @@ -312,6 +328,31 @@ static string GetFriendlyName(char* deviceName)
}
return null;
}

//
// Attempts to get the DPI for the monitor associated with this output using Win32 API.
//
static void GetDpiForMonitor(nint hMonitor, out int dpiX, out int dpiY)
{
uint x, y;

// Windows 8.1+: shcore!GetDpiForMonitor
// TODO: Consider using GetDpiForWindow (Windows 10, version 1607+) as a fallback, which can be more accurate in multi-monitor setups with different DPI settings.
// TODO: Consider a fallback to GetDeviceCaps(LOGPIXELSX/Y) via a DC if GetDpiForMonitor is not available, which is the traditional way to get DPI on older Windows versions.
HResult result = Win32.GetDpiForMonitor(hMonitor, Win32.MDT_EFFECTIVE_DPI, &x, &y);

if (result.IsSuccess)
{
dpiX = (int) x;
dpiY = (int) y;
}
else
{
// Defaults to 96 dpi, which corresponds to 100% scaling
dpiX = 96;
dpiY = 96;
}
}
}

/// <inheritdoc/>
Expand Down
Loading