Browse Source

Screen information API implementation

pull/1159/head
Jurjen Biewenga 9 years ago
committed by Nikita Tsukanov
parent
commit
e25f949d98
  1. 3
      samples/ControlCatalog/ControlCatalog.csproj
  2. 11
      samples/ControlCatalog/MainView.xaml.cs
  3. 62
      samples/ControlCatalog/Pages/ScreenPage.cs
  4. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  5. 9
      src/Avalonia.Controls/Platform/IScreenImpl.cs
  6. 5
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  7. 18
      src/Avalonia.Controls/Platform/Screen.cs
  8. 54
      src/Avalonia.Controls/Screens.cs
  9. 3
      src/Avalonia.Controls/Window.cs
  10. 1
      src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj
  11. 42
      src/Gtk/Avalonia.Gtk/ScreenImpl.cs
  12. 3
      src/Gtk/Avalonia.Gtk/WindowImpl.cs
  13. 2
      src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
  14. 24
      src/Gtk/Avalonia.Gtk3/GtkScreen.cs
  15. 25
      src/Gtk/Avalonia.Gtk3/Interop/GObject.cs
  16. 32
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  17. 57
      src/Gtk/Avalonia.Gtk3/ScreenImpl.cs
  18. 2
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  19. 1
      src/Gtk/Avalonia.Gtk3/WindowImpl.cs
  20. 11
      src/OSX/Avalonia.MonoMac/Helpers.cs
  21. 25
      src/OSX/Avalonia.MonoMac/MacScreen.cs
  22. 51
      src/OSX/Avalonia.MonoMac/ScreenImpl.cs
  23. 2
      src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs
  24. 2
      src/Windows/Avalonia.Win32/Avalonia.Win32.Shared.projitems
  25. 17
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  26. 64
      src/Windows/Avalonia.Win32/ScreenImpl.cs
  27. 25
      src/Windows/Avalonia.Win32/WinScreen.cs
  28. 5
      src/Windows/Avalonia.Win32/WindowImpl.cs

3
samples/ControlCatalog/ControlCatalog.csproj

@ -136,7 +136,7 @@
</Compile>
<Compile Include="Pages\SliderPage.xaml.cs">
<DependentUpon>SliderPage.xaml</DependentUpon>
</Compile>
</Compile>
<Compile Include="Pages\TreeViewPage.xaml.cs">
<DependentUpon>TreeViewPage.xaml</DependentUpon>
</Compile>
@ -146,6 +146,7 @@
<Compile Include="Pages\ToolTipPage.xaml.cs">
<DependentUpon>ToolTipPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ScreenPage.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>

11
samples/ControlCatalog/MainView.xaml.cs

@ -13,11 +13,20 @@ namespace ControlCatalog
{
this.InitializeComponent();
if (AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().IsDesktop)
((IList) this.FindControl<TabControl>("Sidebar").Items).Add(new TabItem()
{
IList tabItems = ((IList)this.FindControl<TabControl>("Sidebar").Items);
tabItems.Add(new TabItem()
{
Header = "Dialogs",
Content = new DialogsPage()
});
tabItems.Add(new TabItem()
{
Header = "Screens",
Content = new ScreenPage()
});
}
}
private void InitializeComponent()

62
samples/ControlCatalog/Pages/ScreenPage.cs

@ -0,0 +1,62 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Platform;
namespace ControlCatalog.Pages
{
public class ScreenPage : UserControl
{
private double _leftMost;
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
Window w = (Window)VisualRoot;
w.PositionChanged += (sender, args) => InvalidateVisual();
}
public override void Render(DrawingContext context)
{
base.Render(context);
Window w = (Window)VisualRoot;
Screen[] screens = w.Screens.All;
Pen p = new Pen(Brushes.Black);
if (screens != null)
foreach (Screen screen in screens)
{
if (screen.Bounds.X / 10f < _leftMost)
{
_leftMost = screen.Bounds.X / 10f;
InvalidateVisual();
return;
}
Rect boundsRect = new Rect(screen.Bounds.X / 10f + Math.Abs(_leftMost), screen.Bounds.Y / 10f, screen.Bounds.Width / 10f,
screen.Bounds.Height / 10f);
Rect workingAreaRect = new Rect(screen.WorkingArea.X / 10f + Math.Abs(_leftMost), screen.WorkingArea.Y / 10f, screen.WorkingArea.Width / 10f,
screen.WorkingArea.Height / 10f);
context.DrawRectangle(p, boundsRect);
context.DrawRectangle(p, workingAreaRect);
FormattedText text = new FormattedText();
text.Text = $"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height), text);
text.Text = $"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text);
text.Text = $"Primary: {screen.Primary}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text);
text.Text = $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new Rect(w.Position, w.Bounds.Size)))}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text);
}
context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10, w.Bounds.Width / 10, w.Bounds.Height / 10));
}
}
}

2
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@ -36,6 +36,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_clientSize = value;
UpdateParams();
}
public IScreenImpl Screen { get; }
public Point Position
{

9
src/Avalonia.Controls/Platform/IScreenImpl.cs

@ -0,0 +1,9 @@
namespace Avalonia.Platform
{
public interface IScreenImpl
{
int ScreenCount { get; }
Screen[] AllScreens { get; }
}
}

5
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@ -65,5 +65,10 @@ namespace Avalonia.Platform
/// Sets the client size of the toplevel.
/// </summary>
void Resize(Size clientSize);
/// <summary>
/// Gets platform specific display information
/// </summary>
IScreenImpl Screen { get; }
}
}

18
src/Avalonia.Controls/Platform/Screen.cs

@ -0,0 +1,18 @@
namespace Avalonia.Platform
{
public class Screen
{
public Rect Bounds { get; }
public Rect WorkingArea { get; }
public bool Primary { get; }
public Screen(Rect bounds, Rect workingArea, bool primary)
{
this.Bounds = bounds;
this.WorkingArea = workingArea;
this.Primary = primary;
}
}
}

54
src/Avalonia.Controls/Screens.cs

@ -0,0 +1,54 @@
using System.Linq;
using Avalonia.Platform;
using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{
public class Screens
{
private readonly IScreenImpl _iScreenImpl;
public int ScreenCount => _iScreenImpl.ScreenCount;
public Screen[] All => _iScreenImpl?.AllScreens;
public Screen Primary => All.FirstOrDefault(x => x.Primary);
public Screens(IScreenImpl iScreenImpl)
{
_iScreenImpl = iScreenImpl;
}
public Screen ScreenFromBounds(Rect bounds){
Screen currMaxScreen = null;
double maxAreaSize = 0;
foreach (Screen screen in All)
{
double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width);
double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height);
double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width);
double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height);
double area = (right - left) * (bottom - top);
if (area > maxAreaSize)
{
maxAreaSize = area;
currMaxScreen = screen;
}
}
return currMaxScreen;
}
public Screen SceenFromPoint(Point point)
{
return All.FirstOrDefault(x=>x.Bounds.Contains(point));
}
public Screen ScreenFromVisual(IVisual visual)
{
Point tl = visual.PointToScreen(visual.Bounds.TopLeft);
Point br = visual.PointToScreen(visual.Bounds.BottomRight);
return ScreenFromBounds(new Rect(tl,br));
}
}
}

3
src/Avalonia.Controls/Window.cs

@ -119,6 +119,7 @@ namespace Avalonia.Controls
: base(impl)
{
_maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size);
Screens = new Screens(PlatformImpl?.Screen);
}
/// <inheritdoc/>
@ -135,6 +136,8 @@ namespace Avalonia.Controls
remove { _nameScope.Unregistered -= value; }
}
public Screens Screens { get; private set; }
/// <summary>
/// Gets the platform-specific window implementation.
/// </summary>

1
src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj

@ -48,6 +48,7 @@
<Compile Include="FramebufferManager.cs" />
<Compile Include="IconImpl.cs" />
<Compile Include="KeyTransform.cs" />
<Compile Include="ScreenImpl.cs" />
<Compile Include="SurfaceFramebuffer.cs" />
<Compile Include="SystemDialogImpl.cs" />
<Compile Include="CursorFactory.cs" />

42
src/Gtk/Avalonia.Gtk/ScreenImpl.cs

@ -0,0 +1,42 @@
using System;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Utilities;
using Gdk;
using Screen = Avalonia.Platform.Screen;
using Window = Gtk.Window;
namespace Avalonia.Gtk
{
internal class ScreenImpl : IScreenImpl
{
private Window window;
public int ScreenCount
{
get => window.Display.DefaultScreen.NMonitors;
}
public Screen[] AllScreens {
get
{
Screen[] screens = new Screen[ScreenCount];
var screen = window.Display.DefaultScreen;
for (short i = 0; i < screens.Length; i++)
{
Rectangle geometry = screen.GetMonitorGeometry(i);
Rect geometryRect = new Rect(geometry.X, geometry.Y, geometry.Width, geometry.Height);
Screen s = new Screen(geometryRect, geometryRect, false);
screens[i] = s;
}
return screens;
}
}
public ScreenImpl(Window window)
{
this.window = window;
}
}
}

3
src/Gtk/Avalonia.Gtk/WindowImpl.cs

@ -1,5 +1,6 @@
using System;
using System.Reactive.Disposables;
using Avalonia.Controls;
using Avalonia.Platform;
using Gdk;
@ -66,6 +67,8 @@ namespace Avalonia.Gtk
}
}
public IScreenImpl Screen => new ScreenImpl(Window);
public void Resize(Size value)
{
Window.Resize((int)value.Width, (int)value.Height);

2
src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj

@ -53,6 +53,8 @@
<Compile Include="WindowBaseImpl.cs" />
<Compile Include="Interop\Utf8Buffer.cs" />
<Compile Include="WindowImpl.cs" />
<Compile Include="ScreenImpl.cs" />
<Compile Include="GtkScreen.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />

24
src/Gtk/Avalonia.Gtk3/GtkScreen.cs

@ -0,0 +1,24 @@
using Avalonia.Platform;
namespace Avalonia.Gtk3
{
public class GtkScreen : Screen
{
private readonly int _screenId;
public GtkScreen(Rect bounds, Rect workingArea, bool primary, int screenId) : base(bounds, workingArea, primary)
{
this._screenId = screenId;
}
public override int GetHashCode()
{
return _screenId;
}
public override bool Equals(object obj)
{
return (obj is GtkScreen screen) ? this._screenId == screen._screenId : base.Equals(obj);
}
}
}

25
src/Gtk/Avalonia.Gtk3/Interop/GObject.cs

@ -46,7 +46,30 @@ namespace Avalonia.Gtk3.Interop
class GtkImContext : GObject
{
}
class GdkScreen : GObject
{
public GdkScreen() : base(IntPtr.Zero, false)
{
}
public GdkScreen(IntPtr handle, bool owned = true) : base(handle, owned)
{
this.handle = handle;
}
}
class UnownedGdkScreen : GdkScreen
{
public UnownedGdkScreen() : base(IntPtr.Zero, false)
{
}
public UnownedGdkScreen(IntPtr handle, bool owned = true) : base(IntPtr.Zero, false)
{
this.handle = handle;
}
}
class GtkDialog : GtkWindow

32
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@ -20,6 +20,27 @@ namespace Avalonia.Gtk3.Interop
{
public static class D
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate gint16 gdk_display_get_n_screens(IntPtr display);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate UnownedGdkScreen gdk_display_get_screen(IntPtr display, gint16 num);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate UnownedGdkScreen gdk_display_get_default_screen (IntPtr display);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate gint16 gdk_screen_get_n_monitors(GdkScreen screen);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate gint16 gdk_screen_get_primary_monitor(GdkScreen screen);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate void gdk_screen_get_monitor_geometry(GdkScreen screen, gint16 num, ref GdkRectangle rect);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate void gdk_screen_get_monitor_workarea(GdkScreen screen, gint16 num, ref GdkRectangle rect);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate IntPtr gtk_application_new(Utf8Buffer appId, int flags);
@ -288,6 +309,9 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate bool signal_onevent(IntPtr gtkWidget, IntPtr ev, IntPtr userData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void monitors_changed(IntPtr screen, IntPtr userData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate bool signal_commit(IntPtr gtkWidget, IntPtr utf8string, IntPtr userData);
@ -298,7 +322,13 @@ namespace Avalonia.Gtk3.Interop
public delegate void GtkClipboardTextReceivedFunc(IntPtr clipboard, IntPtr utf8string, IntPtr userdata);
}
public static D.gdk_display_get_n_screens GdkDisplayGetNScreens;
public static D.gdk_display_get_screen GdkDisplayGetScreen;
public static D.gdk_display_get_default_screen GdkDisplayGetDefaultScreen;
public static D.gdk_screen_get_n_monitors GdkScreenGetNMonitors;
public static D.gdk_screen_get_primary_monitor GdkScreenGetPrimaryMonitor;
public static D.gdk_screen_get_monitor_geometry GdkScreenGetMonitorGeometry;
public static D.gdk_screen_get_monitor_workarea GdkScreenGetMonitorWorkarea;
public static D.gtk_window_set_decorated GtkWindowSetDecorated;
public static D.gtk_window_set_skip_taskbar_hint GtkWindowSetSkipTaskbarHint;
public static D.gtk_window_get_skip_taskbar_hint GtkWindowGetSkipTaskbarHint;

57
src/Gtk/Avalonia.Gtk3/ScreenImpl.cs

@ -0,0 +1,57 @@
using System;
using Avalonia.Controls;
using Avalonia.Gtk3.Interop;
using Avalonia.Platform;
using Avalonia.Utilities;
namespace Avalonia.Gtk3
{
internal class ScreenImpl : IScreenImpl
{
public int ScreenCount
{
get => _allScreens.Length;
}
private Screen[] _allScreens;
public Screen[] AllScreens
{
get
{
if (_allScreens == null)
{
IntPtr display = Native.GdkGetDefaultDisplay();
GdkScreen screen = Native.GdkDisplayGetDefaultScreen(display);
short primary = Native.GdkScreenGetPrimaryMonitor(screen);
Screen[] screens = new Screen[ScreenCount];
for (short i = 0; i < screens.Length; i++)
{
GdkRectangle workArea = new GdkRectangle(), geometry = new GdkRectangle();
Native.GdkScreenGetMonitorGeometry(screen, i, ref geometry);
Native.GdkScreenGetMonitorWorkarea(screen, i, ref workArea);
Rect workAreaRect = new Rect(workArea.X, workArea.Y, workArea.Width, workArea.Height);
Rect geometryRect = new Rect(geometry.X, geometry.Y, geometry.Width, geometry.Height);
GtkScreen s = new GtkScreen(geometryRect, workAreaRect, i == primary, i);
screens[i] = s;
}
_allScreens = screens;
}
return _allScreens;
}
}
public ScreenImpl()
{
IntPtr display = Native.GdkGetDefaultDisplay();
GdkScreen screen = Native.GdkDisplayGetDefaultScreen(display);
Signal.Connect<Native.D.monitors_changed>(screen, "monitors-changed", MonitorsChanged);
}
private unsafe void MonitorsChanged(IntPtr screen, IntPtr userData)
{
_allScreens = null;
}
}
}

2
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -341,6 +341,8 @@ namespace Avalonia.Gtk3
return;
Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height);
}
public IScreenImpl Screen { get; } = new ScreenImpl();
public Point Position
{

1
src/Gtk/Avalonia.Gtk3/WindowImpl.cs

@ -1,4 +1,5 @@
using System;
using System.Linq.Expressions;
using Avalonia.Controls;
using Avalonia.Gtk3.Interop;
using Avalonia.Platform;

11
src/OSX/Avalonia.MonoMac/Helpers.cs

@ -1,6 +1,8 @@
using System;
using System.ComponentModel;
using MonoMac.AppKit;
using MonoMac.CoreGraphics;
using MonoMac.OpenGL;
namespace Avalonia.MonoMac
{
@ -27,5 +29,14 @@ namespace Avalonia.MonoMac
return new CGPoint(pt.X, t - pt.Y);
}
public static Rect ConvertRectY(this Rect rect)
{
return new Rect(rect.Position.WithY(rect.Y + rect.Height).ConvertPointY(), rect.Size);
}
public static CGRect ConvertRectY(this CGRect rect)
{
return new CGRect(new CGPoint(rect.X, rect.Y + rect.Height).ConvertPointY(), rect.Size);
}
}
}

25
src/OSX/Avalonia.MonoMac/MacScreen.cs

@ -0,0 +1,25 @@
using System;
using Avalonia.Platform;
namespace Avalonia.MonoMac
{
public class MacScreen : Screen
{
private readonly IntPtr handle;
public MacScreen(Rect bounds, Rect workingArea, bool primary, IntPtr handle) : base(bounds, workingArea, primary)
{
this.handle = handle;
}
public override int GetHashCode()
{
return (int)handle;
}
public override bool Equals(object obj)
{
return (obj is MacScreen screen) ? this.handle == screen.handle : base.Equals(obj);
}
}
}

51
src/OSX/Avalonia.MonoMac/ScreenImpl.cs

@ -0,0 +1,51 @@
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Utilities;
using MonoMac.AppKit;
using MonoMac.Foundation;
namespace Avalonia.MonoMac
{
public class ScreenImpl : IScreenImpl
{
private const string NSApplicationDidChangeScreenParametersNotification = "NSApplicationDidChangeScreenParametersNotification";
public int ScreenCount
{
get => NSScreen.Screens.Length;
}
private Screen[] _allScreens;
public Screen[] AllScreens
{
get
{
if (_allScreens == null)
{
NSScreen[] screens = NSScreen.Screens;
Screen[] s = new Screen[screens.Length];
NSScreen primary = NSScreen.MainScreen;
for (int i = 0; i < screens.Length; i++)
{
Rect bounds = screens[i].Frame.ToAvaloniaRect().ConvertRectY();
Rect workArea = screens[i].VisibleFrame.ToAvaloniaRect().ConvertRectY();
s[i] = new MacScreen(bounds, workArea, i == 0, screens[i].Handle);
}
_allScreens = s;
}
return _allScreens;
}
}
public ScreenImpl()
{
NSNotificationCenter.DefaultCenter.AddObserver(NSApplicationDidChangeScreenParametersNotification, MonitorsChanged);
}
private void MonitorsChanged(NSNotification notification)
{
_allScreens = null;
}
}
}

2
src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs

@ -153,6 +153,8 @@ namespace Avalonia.MonoMac
Position = pos;
}
public IScreenImpl Screen { get; } = new ScreenImpl();
public override Point PointToClient(Point point)
{
var cocoaScreenPoint = point.ToMonoMacPoint().ConvertPointY();

2
src/Windows/Avalonia.Win32/Avalonia.Win32.Shared.projitems

@ -20,10 +20,12 @@
<Compile Include="$(MSBuildThisFileDirectory)PopupImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Properties\AssemblyInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RenderLoop.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ScreenImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SystemDialogImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Win32Platform.cs" />
<Compile Include="$(MSBuildThisFileDirectory)WindowFramebuffer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)WindowImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)WinScreen.cs" />
<Compile Include="$(MSBuildThisFileDirectory)..\..\Shared\WindowResizeDragHelper.cs" />
<Compile Include="..\..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>

17
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -616,6 +616,12 @@ namespace Avalonia.Win32.Interop
public const int SizeOf_BITMAPINFOHEADER = 40;
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip,
MonitorEnumDelegate lpfnEnum, IntPtr dwData);
public delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
@ -903,6 +909,9 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromPoint(POINT pt, MONITOR dwFlags);
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromRect(RECT rect, MONITOR dwFlags);
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromWindow(IntPtr hwnd, MONITOR dwFlags);
@ -1021,6 +1030,14 @@ namespace Avalonia.Win32.Interop
public int top;
public int right;
public int bottom;
public RECT(Rect rect)
{
left = (int)rect.X;
top = (int)rect.Y;
right = (int)(rect.X + rect.Width);
bottom = (int)(rect.Y + rect.Height);
}
}
public struct TRACKMOUSEEVENT

64
src/Windows/Avalonia.Win32/ScreenImpl.cs

@ -0,0 +1,64 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Utilities;
using static Avalonia.Win32.Interop.UnmanagedMethods;
#if NETSTANDARD
using Win32Exception = Avalonia.Win32.NetStandard.AvaloniaWin32Exception;
#endif
namespace Avalonia.Win32
{
public class ScreenImpl : IScreenImpl
{
public int ScreenCount
{
get => GetSystemMetrics(SystemMetric.SM_CMONITORS);
}
private Screen[] _allScreens;
public Screen[] AllScreens
{
get
{
if (_allScreens == null)
{
int index = 0;
Screen[] screens = new Screen[ScreenCount];
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
(IntPtr monitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr data) =>
{
MONITORINFO monitorInfo = new MONITORINFO();
if (GetMonitorInfo(monitor, monitorInfo))
{
RECT bounds = monitorInfo.rcMonitor;
RECT workingArea = monitorInfo.rcWork;
Rect avaloniaBounds = new Rect(bounds.left, bounds.top, bounds.right - bounds.left,
bounds.bottom - bounds.top);
Rect avaloniaWorkArea =
new Rect(workingArea.left, workingArea.top, workingArea.right - bounds.left,
workingArea.bottom - bounds.top);
screens[index] =
new WinScreen(avaloniaBounds, avaloniaWorkArea, monitorInfo.dwFlags == 1,
monitor);
index++;
}
return true;
}, IntPtr.Zero);
_allScreens = screens;
}
return _allScreens;
}
}
public void InvalidateScreensCache()
{
_allScreens = null;
}
}
}

25
src/Windows/Avalonia.Win32/WinScreen.cs

@ -0,0 +1,25 @@
using System;
using Avalonia.Platform;
namespace Avalonia.Win32
{
public class WinScreen : Screen
{
private readonly IntPtr _hMonitor;
public WinScreen(Rect bounds, Rect workingArea, bool primary, IntPtr hMonitor) : base(bounds, workingArea, primary)
{
this._hMonitor = hMonitor;
}
public override int GetHashCode()
{
return (int)_hMonitor;
}
public override bool Equals(object obj)
{
return (obj is WinScreen screen) ? this._hMonitor == screen._hMonitor : base.Equals(obj);
}
}
}

5
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -103,6 +103,7 @@ namespace Avalonia.Win32
}
}
public IScreenImpl Screen => new ScreenImpl();
public IRenderer CreateRenderer(IRenderRoot root)
{
@ -598,6 +599,10 @@ namespace Avalonia.Win32
case UnmanagedMethods.WindowsMessage.WM_MOVE:
PositionChanged?.Invoke(new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)));
return IntPtr.Zero;
case UnmanagedMethods.WindowsMessage.WM_DISPLAYCHANGE:
(Screen as ScreenImpl)?.InvalidateScreensCache();
return IntPtr.Zero;
}
#if USE_MANAGED_DRAG

Loading…
Cancel
Save