Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Screens API refactor #16295

Merged
merged 20 commits into from
Jul 16, 2024
Merged

Screens API refactor #16295

merged 20 commits into from
Jul 16, 2024

Conversation

maxkatz6
Copy link
Member

@maxkatz6 maxkatz6 commented Jul 12, 2024

What does the pull request do?

  1. Screens API now consistently returns the same instances of Screen class for the same physical screen. Previously it would return new instances each time.
  2. On physical screen changes, previously retrieved Screen instances now also get updated. No need to re-read all screens each time.
  3. New event added that is raised on any screen changed or added (no fine tuning here, sorry, maybe in the future).
  4. Adds an API to retrieve screen platform handle, where possible.
  5. +DisplayName and +CurrentOrientation properties on screen.
  6. Screen is accessible from the TopLevel, not only Window.
  7. Adds implementations for all platforms (in this PR - only Windows and MacOS to keep it simpler, Linux is skipped for now)

I skipped Linux because I have no idea how it works and can't properly test it locally.
Which also means, Linux still returns new Screen instance on each read, basically an old behavior.

See #15901 for details

Updated API:

public class WindowBase : TopLevel
{
-    public Screens Screens { get; }
+    public new Screens Screens => base.Screens!;
}
public class TopLevel
{
+    public Screens? Screens { get; }
}

public class Screen
{
+    public string? DisplayName { get; }
+    public ScreenOrientation CurrentOrientation { get; }

+    public IPlatformHandle? TryGetPlatformHandle();
}

public class Screens
{
+    public event EventHandler? Changed;
+    public Screen? ScreenFromTopLevel(TopLevel topLevel);
+    Task<bool> RequestScreenDetails();
}

+public enum ScreenOrientation
+{
+    None,
+    Landscape,
+    Portrait,
+    LandscapeFlipped,
+    PortraitFlipped
+}

Checklist

Breaking changes

ctor.Screens(IScreenImpl) is now PrivateAPI. It should not break any app, as IScreenImpl by itself was a private API since 11.0.

Fixed issues

#15382
#11512
#15637
#15901

@maxkatz6
Copy link
Member Author

One breaking change that I would love to change, but can't - make Bounds and WorkArea nullable, and instead add non-nullable Size. Because we can get Screen.Size pretty much on any platform, but screen position and work area are only available on desktop + desktop chromium.

public class Screen
{
+     public PixelSize Size { get; }
-     public PixelRect Bounds { get; }
+     public PixelRect? Bounds { get; }
-     public PixelRect WorkArea { get; }
+     public PixelRect? WorkArea { get; }
}

We might move this specific change to 12.0, cc @kekekeks

namespace System.Runtime.InteropServices
{
[global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false)]
internal sealed class UnmanagedCallersOnlyAttribute : global::System.Attribute
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: I used function pointers in netstandard2.0, and it even works on net framework.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you checked with x86 arch? It's the one with weird calling conventions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC one of the reasons for that attribute was to ensure that the function has a proper native->managed transition and is even callable with the corresponding calling convention.

.NET Framework isn't aware of the attribute and isn't likely to setup a proper native->managed transition.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I believe that it's not safe to do.

@@ -43,14 +66,44 @@ virtual HRESULT GetScreen (int index, AvnScreen* ret) override

ret->Scaling = 1;
Copy link
Member Author

@maxkatz6 maxkatz6 Jul 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does anybody remember why we set scaling to 1? Instead of using an actual value (more likely between 2 and 3 for most main displays). MacOS.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our Screen API reports layout scaling, not DPI. macOS always uses logical coordinates for window positioning, so the reported value is 1.
We could extend the API to report both.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our Screen API reports layout scaling

Is it used anywhere like that? It's also not intuitive.
Normally people would use Window.DesktopScaling for that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other implementations like qtbase and chromium just return dpi scaling, not layout.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will keep it as is for now

@maxkatz6
Copy link
Member Author

Added some screen integration tests as well. PR is ready.

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0050061-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0050079-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

native/Avalonia.Native/src/OSX/common.h Show resolved Hide resolved
native/Avalonia.Native/src/OSX/Screens.mm Outdated Show resolved Hide resolved
@@ -43,14 +66,44 @@ virtual HRESULT GetScreen (int index, AvnScreen* ret) override

ret->Scaling = 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our Screen API reports layout scaling, not DPI. macOS always uses logical coordinates for window positioning, so the reported value is 1.
We could extend the API to report both.

native/Avalonia.Native/src/OSX/Screens.mm Outdated Show resolved Hide resolved
namespace System.Runtime.InteropServices
{
[global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false)]
internal sealed class UnmanagedCallersOnlyAttribute : global::System.Attribute
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you checked with x86 arch? It's the one with weird calling conventions.

namespace System.Runtime.InteropServices
{
[global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false)]
internal sealed class UnmanagedCallersOnlyAttribute : global::System.Attribute
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I believe that it's not safe to do.

src/Avalonia.Controls/Platform/Screen.cs Outdated Show resolved Hide resolved
src/Avalonia.Native/ScreenImpl.cs Outdated Show resolved Hide resolved
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs Outdated Show resolved Hide resolved
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0050132-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@maxkatz6 maxkatz6 requested a review from kekekeks July 16, 2024 06:06
@maxkatz6
Copy link
Member Author

@kekekeks removed incorrect UnmanagedCallersOnly usage, changed macOS impl to use com pointer, and moved GetHashCode/Equals impl to another class that inherits Screen.

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0050150-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0050178-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@maxkatz6 maxkatz6 merged commit 05ac6d2 into master Jul 16, 2024
9 of 11 checks passed
@maxkatz6 maxkatz6 deleted the screens-api-refactor branch July 16, 2024 22:48
Gillibald pushed a commit to Gillibald/Avalonia that referenced this pull request Jul 29, 2024
* Draft new API

* Push reusable ScreensBaseImpl implementation

* Fix tests and stubs

* Update ScreensPage sample to work on mobile + show new APIs

* Reimplement Windows ScreensImpl, reuse existing screens in other places of backend, use Microsoft.Windows.CsWin32 for interop

* Make X11 project buildable, don't utilize new APIs yet

* Reimplement macOS Screens API, differenciate screens by CGDirectDisplayID

* Fix build

* Adjust breaking changes file (none affect users)

* Fix missing macOS Screen.DisplayName

* Add more tests + fix screen removal

* Add screens integration tests

* Use hash set with comparer when removing screens

* Make screenimpl safer on macOS as per review

* Replace UnmanagedCallersOnly usage with source generated EnumDisplayMonitors

* Remove unused dllimport

* Only implement GetHashCode and Equals on PlatformScreen subclass, without changing base Screen
# Conflicts:
#	api/Avalonia.nupkg.xml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants