Skip to content

Commit

Permalink
feat: DisplayInformationExtension per window
Browse files Browse the repository at this point in the history
  • Loading branch information
ramezgerges committed Feb 27, 2024
1 parent 4f9f27d commit 55b0d78
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Uno.UI.Runtime.Skia.Gtk.Helpers.Dpi;
using Uno.UI.Runtime.Skia.Gtk.UI.Controls;
using Windows.Graphics.Display;
using Microsoft.UI.Windowing;

namespace Uno.UI.Runtime.Skia.Gtk;

Expand All @@ -28,10 +29,22 @@ private void UnoGtkWindow_NativeWindowShown(object? sender, UnoGtkWindow e)
OnDpiChanged(null, EventArgs.Empty);
}

private Window? GetWindow()
private Window GetWindow()
{
_window ??= GtkHost.Current?.InitialWindow;
if (_window is { })
{
return _window;
}

// TODO: this is a ridiculous amount of indirection, find something better
if (AppWindow.GetFromWindowId(_displayInformation.WindowId) is not { } appWindow ||
Microsoft.UI.Xaml.Window.GetFromAppWindow(appWindow) is not { } window ||
UnoGtkWindow.GetGtkWindowFromWindow(window) is not { } gtkWindow)
{
throw new InvalidOperationException($"{nameof(GtkDisplayInformationExtension)} couldn't find a GTK window.");
}

_window = gtkWindow;
return _window;
}

Expand Down
9 changes: 7 additions & 2 deletions src/Uno.UI.Runtime.Skia.Gtk/UI/Controls/UnoGtkWindow.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable enable

using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.IO;
using Gtk;
Expand All @@ -17,12 +18,16 @@ namespace Uno.UI.Runtime.Skia.Gtk.UI.Controls;

internal class UnoGtkWindow : Window
{
private readonly WinUIWindow _winUIWindow;
private readonly ApplicationView _applicationView;
private static readonly ConcurrentDictionary<WinUIWindow, UnoGtkWindow> _windowToGtkWindow = new();

public static UnoGtkWindow? GetGtkWindowFromWindow(WinUIWindow window)
=> _windowToGtkWindow.TryGetValue(window, out var gtkWindow) ? gtkWindow : null;

public UnoGtkWindow(WinUIWindow winUIWindow, Microsoft.UI.Xaml.XamlRoot xamlRoot) : base(WindowType.Toplevel)
{
_winUIWindow = winUIWindow ?? throw new ArgumentNullException(nameof(winUIWindow));
_windowToGtkWindow[winUIWindow ?? throw new ArgumentNullException(nameof(winUIWindow))] = this;
winUIWindow.Closed += (_, _) => _windowToGtkWindow.TryRemove(winUIWindow, out _);

Size preferredWindowSize = ApplicationView.PreferredLaunchViewSize;
if (preferredWindowSize != Size.Empty)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,48 @@
using System.Windows;
using System.Windows.Media;
using Windows.Graphics.Display;
using Microsoft.UI.Windowing;
using Uno.UI.Runtime.Skia.Wpf.UI.Controls;
using WpfApplication = global::System.Windows.Application;
using WpfWindow = global::System.Windows.Window;

namespace Uno.UI.Runtime.Skia.Wpf
{
internal class WpfDisplayInformationExtension : IDisplayInformationExtension
{
private readonly DisplayInformation _displayInformation;

private WpfWindow? _window;
private float? _dpi;

public WpfDisplayInformationExtension(object owner)
{
_displayInformation = (DisplayInformation)owner;
WpfApplication.Current.Activated += Current_Activated;
GetWindow().Activated += Current_Activated;
}

private void Current_Activated(object? sender, EventArgs e)
{
WpfApplication.Current.MainWindow.DpiChanged += OnDpiChanged;
GetWindow().DpiChanged += OnDpiChanged;
}

private WpfWindow GetWindow()
{
if (_window is { })
{
return _window;
}

// TODO: this is a ridiculous amount of indirection, find something better
if (AppWindow.GetFromWindowId(_displayInformation.WindowId) is not { } appWindow ||
Microsoft.UI.Xaml.Window.GetFromAppWindow(appWindow) is not { } window ||
UnoWpfWindow.GetGtkWindowFromWindow(window) is not { } gtkWindow)
{
throw new InvalidOperationException($"{nameof(WpfDisplayInformationExtension)} couldn't find a GTK window.");
}

_window = gtkWindow;
return _window;
}

public DisplayOrientations CurrentOrientation => DisplayOrientations.Landscape;
Expand Down
10 changes: 8 additions & 2 deletions src/Uno.UI.Runtime.Skia.Wpf/UI/Controls/UnoWpfWindow.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable enable

using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.IO;
using Uno.Foundation.Logging;
Expand All @@ -14,14 +15,19 @@ namespace Uno.UI.Runtime.Skia.Wpf.UI.Controls;

internal class UnoWpfWindow : WpfWindow
{
private readonly WinUI.Window _winUIWindow;
private readonly ApplicationView _applicationView;

private bool _shown;

private static readonly ConcurrentDictionary<WinUI.Window, WpfWindow> _windowToWpfWindow = new();

public static WpfWindow? GetGtkWindowFromWindow(WinUI.Window window)
=> _windowToWpfWindow.TryGetValue(window, out var gtkWindow) ? gtkWindow : null;

public UnoWpfWindow(WinUI.Window winUIWindow, WinUI.XamlRoot xamlRoot)
{
_winUIWindow = winUIWindow ?? throw new ArgumentNullException(nameof(winUIWindow));
_windowToWpfWindow[winUIWindow ?? throw new ArgumentNullException(nameof(winUIWindow))] = this;
winUIWindow.Closed += (_, _) => _windowToWpfWindow.TryRemove(winUIWindow, out _);

Windows.Foundation.Size preferredWindowSize = ApplicationView.PreferredLaunchViewSize;
if (preferredWindowSize != Windows.Foundation.Size.Empty)
Expand Down
25 changes: 15 additions & 10 deletions src/Uno.UI.Runtime.Skia.X11/X11DisplayInformationExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ internal class X11DisplayInformationExtension : IDisplayInformationExtension
private const double InchesToMilliMeters = 25.4;

private readonly float? _scaleOverride;
private readonly DisplayInformation _owner;
private readonly X11XamlRootHost _host;
private DisplayInformationDetails _details;
private DisplayInformation _owner;

private record DisplayInformationDetails(
uint ScreenWidthInRawPixels,
Expand All @@ -114,19 +115,23 @@ public X11DisplayInformationExtension(object owner, float? scaleOverride)
// initialization is redundant, just keeping the compiler happy
_details = new DisplayInformationDetails(default, default, default, default, default, default);

var x11Window = X11XamlRootHost.GetWindow();
var host = X11XamlRootHost.GetXamlRootHostFromX11Window(x11Window);
host?.SetDisplayInformationExtension(this);
if (!X11Helper.XamlRootHostFromDisplayInformation(_owner, out var host))
{
throw new InvalidOperationException($"{nameof(X11DisplayInformationExtension)} couldn't find a {nameof(X11XamlRootHost)}.");
}
_host = host;
_host.SetDisplayInformationExtension(this);

UpdateDetails();
}

internal void UpdateDetails()
{
var x11Window = X11XamlRootHost.GetWindow();
var window = _host.X11Window.Window;
var display = _host.X11Window.Display;

var oldDetails = _details;
var xLock = X11Helper.XLock(x11Window.Display);
var xLock = X11Helper.XLock(display);
using var __ = Disposable.Create(() =>
{
// dispose lock before raising DpiChanged in case a user defined callback takes too long.
Expand All @@ -138,13 +143,13 @@ internal void UpdateDetails()
});

XWindowAttributes attributes = default;
var _1 = XLib.XGetWindowAttributes(x11Window.Display, x11Window.Window, ref attributes);
var _1 = XLib.XGetWindowAttributes(display, window, ref attributes);
var screen = attributes.screen;

if (XLib.XRRQueryExtension(x11Window.Display, out _, out _) != 0 &&
XLib.XRRQueryVersion(x11Window.Display, out var major, out var minor) != 0 &&
if (XLib.XRRQueryExtension(display, out _, out _) != 0 &&
XLib.XRRQueryVersion(display, out var major, out var minor) != 0 &&
(major > 1 || (major == 1 && minor >= 3)) &&
GetDisplayInformationXRandR1_3(x11Window.Display, x11Window.Window) is { } details)
GetDisplayInformationXRandR1_3(display, window) is { } details)
{
_details = details;
}
Expand Down
2 changes: 0 additions & 2 deletions src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ public X11PointerInputSource(IXamlRootHost host)
_host = (X11XamlRootHost)host;
_host.SetPointerSource(this);

DisplayInformation.GetForCurrentView();

// Set this on startup in case a different global default was set beforehand
PointerCursor = new(CoreCursorType.Arrow, 0);
_pointerCursor = PointerCursor; // initialization is not needed, we're just keeping the compiler happy
Expand Down
32 changes: 0 additions & 32 deletions src/Uno.UI.Runtime.Skia.X11/X11XamlRootHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,34 +200,6 @@ public static bool AllWindowsDone()
}
}

// TODO: return the first window and keep the order somehow
// This is just a terrible workaround to deal with DisplayInformation being a singleton instead
// of having a DisplayInformation instance per application view.
public static X11Window GetWindow()
{
lock (_x11WindowToXamlRootHost)
{
foreach (var pair in _x11WindowToXamlRootHost)
{
if (pair.Value._closed.Task.IsCompleted)
{
_x11WindowToXamlRootHost.Remove(pair.Key);
}
else
{
return pair.Key;
}
}
}

if (typeof(X11XamlRootHost).Log().IsEnabled(LogLevel.Error))
{
typeof(X11XamlRootHost).Log().Error($"{nameof(GetWindow)} didn't find any window.");
}

return default;
}

public static void Close(X11Window x11window)
{
lock (_x11WindowToXamlRootHostMutex)
Expand Down Expand Up @@ -313,10 +285,6 @@ private void Initialize()
{
_renderer = new X11SoftwareRenderer(this, _x11Window.Value);
}

// This is necessary to initialize the lazy-initialized instance as it will be needed before before the first measure cycle,
// which will need the DisplayInformation to calculate the bounds of the window (otherwise we get NaNxNaN).
Windows.Graphics.Display.DisplayInformation.GetForCurrentView();
}

// https://github.com/gamedevtech/X11OpenGLWindow/blob/4a3d55bb7aafd135670947f71bd2a3ee691d3fb3/README.md
Expand Down
15 changes: 15 additions & 0 deletions src/Uno.UI.Runtime.Skia.X11/X11_Bindings/X11Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ public static bool XamlRootHostFromApplicationView(ApplicationView view, [NotNul
return false;
}

public static bool XamlRootHostFromDisplayInformation(Windows.Graphics.Display.DisplayInformation displayInformation, [NotNullWhen(true)] out X11XamlRootHost? x11XamlRootHost)
{
// TODO: this is a ridiculous amount of indirection, find something better
if (AppWindow.GetFromWindowId(displayInformation.WindowId) is { } appWindow &&
Window.GetFromAppWindow(appWindow) is { } window &&
X11WindowWrapper.GetHostFromWindow(window) is { } host)
{
x11XamlRootHost = host;
return true;
}

x11XamlRootHost = null;
return false;
}

private static Func<IntPtr, string, bool, IntPtr> _getAtom = Funcs.CreateMemoized<IntPtr, string, bool, IntPtr>(XLib.XInternAtom);
public static IntPtr GetAtom(IntPtr display, string name, bool only_if_exists = false) => _getAtom(display, name, only_if_exists);

Expand Down
3 changes: 3 additions & 0 deletions src/Uno.UI/UI/Xaml/Window/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ internal Window(WindowType windowType)
{
Initialize();
}

// We set up the DisplayInformation instance after Initialize so that we have an actual window to bind to.
Windows.Graphics.Display.DisplayInformation.GetOrCreateForWindowId(AppWindow.Id);
}

private void OnWindowClosed(object sender, object e) => ApplicationHelper.RemoveWindow(this);
Expand Down
7 changes: 5 additions & 2 deletions src/Uno.UWP/Graphics/Display/DisplayInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@ public sealed partial class DisplayInformation
private TypedEventHandler<DisplayInformation, object> _orientationChanged;
private TypedEventHandler<DisplayInformation, object> _dpiChanged;

private DisplayInformation()
private DisplayInformation(WindowId windowId)
{
WindowId = windowId;
Initialize();
}

internal WindowId WindowId { get; }

public static DisplayInformation GetForCurrentView()
{
#if ANDROID
Expand Down Expand Up @@ -65,7 +68,7 @@ internal static DisplayInformation GetOrCreateForWindowId(WindowId windowId)
{
if (!_windowIdMap.TryGetValue(windowId, out var appView))
{
appView = new();
appView = new(windowId);
_windowIdMap[windowId] = appView;
}

Expand Down
1 change: 0 additions & 1 deletion src/Uno.UWP/Microsoft/UI/Windowing/AppWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ internal AppWindow()

_windowIdMap[Id] = this;
ApplicationView.GetOrCreateForWindowId(Id);
DisplayInformation.GetOrCreateForWindowId(Id);
}

public event TypedEventHandler<AppWindow, AppWindowClosingEventArgs> Closing;
Expand Down

0 comments on commit 55b0d78

Please sign in to comment.