From 81e39f9895ce87fe26cba4a9aad7d83d246f285a Mon Sep 17 00:00:00 2001 From: Dan Clark Date: Sat, 2 Nov 2024 20:57:13 +0000 Subject: [PATCH] More WIndows specific parts moved to this project --- .../ConfigurationValueViewModel.cs | 4 +- Toolkit.Foundation/Navigation.cs | 2 +- .../ContentCropper/ContentCropper.cs | 4 +- Toolkit.Windows/ITaskbar.cs | 12 ++ Toolkit.Windows/ITaskbarButton.cs | 9 ++ Toolkit.Windows/PointerLocationExtensions.cs | 9 ++ Toolkit.Windows/PointerMonitor.cs | 61 +--------- Toolkit.Windows/RectExtensions.cs | 11 ++ Toolkit.Windows/Screen.cs | 113 +++++++++++++++++ Toolkit.Windows/SystemInformationHelper.cs | 33 +++++ Toolkit.Windows/Taskbar.cs | 114 ++++++++++++++++++ Toolkit.Windows/TaskbarButton.cs | 99 +++++++++++++++ .../TaskbarButtonDragEnterEventArgs.cs | 3 + .../TaskbarButtonDragOverEventArgs.cs | 3 + .../TaskbarButtonEnteredEventArgs.cs | 3 + .../TaskbarButtonInvokedEventArgs.cs | 3 + Toolkit.Windows/TaskbarChangedEventArgs.cs | 3 + Toolkit.Windows/TaskbarDragEnterEventArgs.cs | 3 + Toolkit.Windows/TaskbarDragOverEventArgs.cs | 3 + Toolkit.Windows/TaskbarEnteredEventArgs.cs | 3 + Toolkit.Windows/TaskbarList.cs | 12 ++ Toolkit.Windows/TaskbarPlacement.cs | 9 ++ Toolkit.Windows/TaskbarState.cs | 8 ++ Toolkit.Windows/Toolkit.Windows.csproj | 7 +- Toolkit.Windows/WndProcMonitor.cs | 65 ++++++++++ 25 files changed, 528 insertions(+), 68 deletions(-) create mode 100644 Toolkit.Windows/ITaskbar.cs create mode 100644 Toolkit.Windows/ITaskbarButton.cs create mode 100644 Toolkit.Windows/PointerLocationExtensions.cs create mode 100644 Toolkit.Windows/RectExtensions.cs create mode 100644 Toolkit.Windows/Screen.cs create mode 100644 Toolkit.Windows/SystemInformationHelper.cs create mode 100644 Toolkit.Windows/Taskbar.cs create mode 100644 Toolkit.Windows/TaskbarButton.cs create mode 100644 Toolkit.Windows/TaskbarButtonDragEnterEventArgs.cs create mode 100644 Toolkit.Windows/TaskbarButtonDragOverEventArgs.cs create mode 100644 Toolkit.Windows/TaskbarButtonEnteredEventArgs.cs create mode 100644 Toolkit.Windows/TaskbarButtonInvokedEventArgs.cs create mode 100644 Toolkit.Windows/TaskbarChangedEventArgs.cs create mode 100644 Toolkit.Windows/TaskbarDragEnterEventArgs.cs create mode 100644 Toolkit.Windows/TaskbarDragOverEventArgs.cs create mode 100644 Toolkit.Windows/TaskbarEnteredEventArgs.cs create mode 100644 Toolkit.Windows/TaskbarList.cs create mode 100644 Toolkit.Windows/TaskbarPlacement.cs create mode 100644 Toolkit.Windows/TaskbarState.cs create mode 100644 Toolkit.Windows/WndProcMonitor.cs diff --git a/Toolkit.Foundation/ConfigurationValueViewModel.cs b/Toolkit.Foundation/ConfigurationValueViewModel.cs index d95ef04..1935d47 100644 --- a/Toolkit.Foundation/ConfigurationValueViewModel.cs +++ b/Toolkit.Foundation/ConfigurationValueViewModel.cs @@ -19,7 +19,7 @@ public partial class ConfigurationValueViewModel(IServic { if (args.Sender is TConfiguration configuration) { - // await Task.Run(() => Value = read(configuration)); + await Task.Run(() => Value = read(configuration)); } } @@ -96,7 +96,7 @@ public partial class ConfigurationValueViewModel { if (args.Sender is TConfiguration configuration) { - // await Task.Run(() => Value = read(configuration)); + await Task.Run(() => Value = read(configuration)); } } diff --git a/Toolkit.Foundation/Navigation.cs b/Toolkit.Foundation/Navigation.cs index 02a3c24..fb473e8 100644 --- a/Toolkit.Foundation/Navigation.cs +++ b/Toolkit.Foundation/Navigation.cs @@ -8,7 +8,7 @@ public class Navigation(IServiceProvider provider, IPublisher publisher) : INavigation { - public async void Navigate(string route, + public void Navigate(string route, object? sender = null, object? region = null, EventHandler? navigated = null, diff --git a/Toolkit.UI.Controls.Avalonia/ContentCropper/ContentCropper.cs b/Toolkit.UI.Controls.Avalonia/ContentCropper/ContentCropper.cs index 6696440..9a3c26d 100644 --- a/Toolkit.UI.Controls.Avalonia/ContentCropper/ContentCropper.cs +++ b/Toolkit.UI.Controls.Avalonia/ContentCropper/ContentCropper.cs @@ -533,8 +533,8 @@ public class ContentCropper : ContentControl double centreX, double centreY) { - int width = (int)border.Width; - int height = (int)border.Height; + int width = (int?)border?.Width ?? 0; + int height = (int?)border?.Height ?? 0; double x = Math.Max(centreX - width / 2, 0); double y = Math.Max(centreY - height / 2, 0); diff --git a/Toolkit.Windows/ITaskbar.cs b/Toolkit.Windows/ITaskbar.cs new file mode 100644 index 0000000..093cc45 --- /dev/null +++ b/Toolkit.Windows/ITaskbar.cs @@ -0,0 +1,12 @@ +using Toolkit.Foundation; + +namespace Toolkit.Windows; + +public interface ITaskbar : + IInitialization, + IDisposable +{ + TaskbarState GetCurrentState(); + + IntPtr GetHandle(); +} diff --git a/Toolkit.Windows/ITaskbarButton.cs b/Toolkit.Windows/ITaskbarButton.cs new file mode 100644 index 0000000..e7dc1e4 --- /dev/null +++ b/Toolkit.Windows/ITaskbarButton.cs @@ -0,0 +1,9 @@ +namespace Toolkit.Windows; + +public interface ITaskbarButton : + IDisposable +{ + Rect Rect { get; } + + string Name { get; } +} diff --git a/Toolkit.Windows/PointerLocationExtensions.cs b/Toolkit.Windows/PointerLocationExtensions.cs new file mode 100644 index 0000000..0d4be6a --- /dev/null +++ b/Toolkit.Windows/PointerLocationExtensions.cs @@ -0,0 +1,9 @@ +namespace Toolkit.Windows; + +public static class PointerLocationExtensions +{ + public static bool IsWithinBounds(this PointerLocation args, Rect bounds) => args.X >= bounds.X + && args.X <= bounds.X + bounds.Width + && args.Y >= bounds.Y + && args.Y <= bounds.Y + bounds.Height; +} diff --git a/Toolkit.Windows/PointerMonitor.cs b/Toolkit.Windows/PointerMonitor.cs index 48101d3..b2ec022 100644 --- a/Toolkit.Windows/PointerMonitor.cs +++ b/Toolkit.Windows/PointerMonitor.cs @@ -3,70 +3,10 @@ using System.Drawing; using Toolkit.Foundation; using Windows.Win32; using Windows.Win32.Foundation; -using Windows.Win32.Graphics.Gdi; using Windows.Win32.UI.WindowsAndMessaging; namespace Toolkit.Windows; -public class WndProcMonitor(IPublisher publisher) : - IWndProcMonitor -{ - private WNDPROC? handler; - private readonly IPublisher publisher = publisher; - - public IntPtr Handle { get; private set; } - - public void Dispose() - { - PInvoke.DestroyWindow((HWND)Handle); - } - - private unsafe void InitializeWndProc() - { - var windowName = Guid.NewGuid().ToString(); - handler = Wndproc; - - WNDCLASSW wndProcWindow; - - wndProcWindow.style = 0; - wndProcWindow.lpfnWndProc = handler; - wndProcWindow.cbClsExtra = 0; - wndProcWindow.cbWndExtra = 0; - wndProcWindow.hInstance = new HINSTANCE(); - wndProcWindow.hIcon = new HICON(); - wndProcWindow.hCursor = new HCURSOR(); - wndProcWindow.hbrBackground = new HBRUSH(); - - fixed (char* menuName = "") - { - wndProcWindow.lpszMenuName = new PCWSTR(menuName); - } - - fixed (char* className = windowName) - { - wndProcWindow.lpszClassName = new PCWSTR(className); - } - - PInvoke.RegisterClass(wndProcWindow); - Handle = PInvoke.CreateWindowEx(0, wndProcWindow.lpszClassName, - new PCWSTR(), 0, 0, 0, 0, 0, new HWND(), - new HMENU(), - new HINSTANCE()); - } - - private LRESULT Wndproc(HWND param0, uint param1, WPARAM param2, LPARAM param3) - { - publisher.Publish(new WndProcEventArgs(param1, (uint)param2.Value, (uint)param3.Value)); - return PInvoke.DefWindowProc(param0, param1, param2, param3); - } - - public void Initialize() - { - InitializeWndProc(); - } -} - - public class PointerMonitor(IPublisher publisher) : IPointerMonitor { @@ -75,6 +15,7 @@ public class PointerMonitor(IPublisher publisher) : private bool isPointerPressed; private HOOKPROC? mouseEventDelegate; private UnhookWindowsHookExSafeHandle? mouseHandle; + ~PointerMonitor() { Dispose(false); diff --git a/Toolkit.Windows/RectExtensions.cs b/Toolkit.Windows/RectExtensions.cs new file mode 100644 index 0000000..47a0bff --- /dev/null +++ b/Toolkit.Windows/RectExtensions.cs @@ -0,0 +1,11 @@ +using Windows.Win32.Foundation; + +namespace Toolkit.Windows; + +internal static class RectExtensions +{ + internal static Rect ToRect(this RECT rect) => + rect.right - rect.left < 0 || rect.bottom - rect.top < 0 + ? new Rect(rect.left, rect.top, 0, 0) + : new Rect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); +} diff --git a/Toolkit.Windows/Screen.cs b/Toolkit.Windows/Screen.cs new file mode 100644 index 0000000..39cc5f4 --- /dev/null +++ b/Toolkit.Windows/Screen.cs @@ -0,0 +1,113 @@ +using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; +using Windows.Win32.Graphics.Gdi; + +namespace Toolkit.Windows; + +public class Screen +{ + private const int CCHDEVICENAME = 32; + private const int PRIMARY_MONITOR = unchecked((int)0xBAADF00D); + private static readonly bool _multiMonitorSupport; + + private readonly IntPtr _monitorHandle; + + static Screen() + { + _multiMonitorSupport = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS) != 0; + } + + internal Screen(IntPtr monitorHandle) + { + if (!_multiMonitorSupport || monitorHandle == (IntPtr)PRIMARY_MONITOR) + { + Bounds = SystemInformationHelper.VirtualScreen; + Primary = true; + DeviceName = "DISPLAY"; + } + else + { + var monitorData = GetMonitorData(monitorHandle); + + Bounds = new Rect(monitorData.MonitorRect.left, monitorData.MonitorRect.top, + monitorData.MonitorRect.right - monitorData.MonitorRect.left, + monitorData.MonitorRect.bottom - monitorData.MonitorRect.top); + + Primary = (monitorData.Flags & (int)MonitorFlag.MONITOR_DEFAULTTOPRIMARY) != 0; + DeviceName = monitorData.DeviceName; + } + + _monitorHandle = monitorHandle; + } + + private enum MonitorFlag : uint + { + MONITOR_DEFAULTTONULL = 0, + MONITOR_DEFAULTTOPRIMARY = 1, + MONITOR_DEFAULTTONEAREST = 2 + } + + public Rect Bounds { get; } + + public string DeviceName { get; } + + public bool Primary { get; } + + public Rect WorkingArea => GetWorkingArea(); + + public static Screen FromHandle(IntPtr handle) => _multiMonitorSupport ? + new Screen(PInvoke.MonitorFromWindow((HWND)handle, + MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST)) : + new Screen(PRIMARY_MONITOR); + + public override bool Equals(object? obj) + { + if (obj is not Screen monitor) return false; + return _monitorHandle == monitor._monitorHandle; + } + + public override int GetHashCode() + { + checked + { + return (int)_monitorHandle; + } + } + + [DllImport("user32.dll", EntryPoint = "GetMonitorInfo", CharSet = CharSet.Auto, SetLastError = true)] + private static extern bool GetMonitorInfoEx(IntPtr hMonitor, ref MonitorData lpmi); + + private MonitorData GetMonitorData(IntPtr monitorHandle) + { + MonitorData monitorData = new(); + monitorData.Size = Marshal.SizeOf(monitorData); + GetMonitorInfoEx(monitorHandle, ref monitorData); + + return monitorData; + } + + private Rect GetWorkingArea() + { + if (!_multiMonitorSupport || _monitorHandle == PRIMARY_MONITOR) + { + return SystemInformationHelper.WorkingArea; + } + + var monitorData = GetMonitorData(_monitorHandle); + return new Rect(monitorData.WorkAreaRect.left, monitorData.WorkAreaRect.top, monitorData.WorkAreaRect.right - monitorData.WorkAreaRect.left, monitorData.WorkAreaRect.bottom - monitorData.WorkAreaRect.top); + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct MonitorData + { + public int Size; + public RECT MonitorRect; + public RECT WorkAreaRect; + public uint Flags; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)] + public string DeviceName; + } +} diff --git a/Toolkit.Windows/SystemInformationHelper.cs b/Toolkit.Windows/SystemInformationHelper.cs new file mode 100644 index 0000000..60dd73c --- /dev/null +++ b/Toolkit.Windows/SystemInformationHelper.cs @@ -0,0 +1,33 @@ +using System.Runtime.InteropServices; +using Windows.Foundation; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace Toolkit.Windows; + +internal static class SystemInformationHelper +{ + private const int SPI_GETWORKAREA = 48; + + public static Rect VirtualScreen => GetVirtualScreen(); + public static Rect WorkingArea => GetWorkingArea(); + + private static Rect GetVirtualScreen() + { + Size size = new(PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSCREEN), + PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSCREEN)); + return new Rect(0, 0, (int)size.Width, (int)size.Height); + } + + private static Rect GetWorkingArea() + { + var rect = new RECT(); + + SystemParametersInfo(SPI_GETWORKAREA, 0, ref rect, 0); + return new Rect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + } + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern bool SystemParametersInfo(int nAction, int nParam, ref RECT rc, int nUpdate); +} diff --git a/Toolkit.Windows/Taskbar.cs b/Toolkit.Windows/Taskbar.cs new file mode 100644 index 0000000..d2d3c0a --- /dev/null +++ b/Toolkit.Windows/Taskbar.cs @@ -0,0 +1,114 @@ +using Toolkit.Foundation; +using Windows.Win32; + +namespace Toolkit.Windows; + +public class Taskbar(ISubscriber subscriber, + IPublisher publisher, + IDisposer disposer) : + ITaskbar, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler +{ + private bool isDrag; + private bool isWithinBounds; + + public void Dispose() + { + disposer.Dispose(this); + GC.SuppressFinalize(this); + } + + public TaskbarState GetCurrentState() + { + var handle = GetHandle(); + var state = new TaskbarState + { + Screen = Screen.FromHandle(handle) + }; + + var appBarData = PInvoke.GetAppBarData(handle); + PInvoke.GetAppBarPosition(ref appBarData); + + state.Rect = appBarData.rect.ToRect(); + state.Placement = (TaskbarPlacement)appBarData.uEdge; + + return state; + } + + public IntPtr GetHandle() => WindowHelper.Find("Shell_TrayWnd"); + + public Task Handle(WndProcEventArgs args) + { + if (args.Message == PInvoke.WM_TASKBARCREATED || + args.Message == (int)WndProcMessages.WM_SETTINGCHANGE && + (int)args.WParam == PInvoke.SPI_SETWORKAREA) + { + publisher.Publish(); + } + + return Task.CompletedTask; + } + + public Task Handle(PointerReleasedEventArgs args) + { + if (isDrag) + { + isDrag = false; + } + + return Task.CompletedTask; + } + + public Task Handle(PointerMovedEventArgs args) + { + nint taskbarHandle = GetHandle(); + if (WindowHelper.TryGetBounds(taskbarHandle, out var rect)) + { + if (args.Location.IsWithinBounds(rect)) + { + if (isWithinBounds) + { + return Task.CompletedTask; + } + + isWithinBounds = true; + publisher.Publish(); + } + else + { + isDrag = false; + isWithinBounds = false; + } + } + + return Task.CompletedTask; + } + + public Task Handle(PointerDragEventArgs args) + { + if (isWithinBounds) + { + if (isDrag) + { + publisher.Publish(); + } + else + { + publisher.Publish(); + } + + isDrag = true; + } + else + { + isDrag = false; + } + + return Task.CompletedTask; + } + + public void Initialize() => subscriber.Subscribe(this); +} diff --git a/Toolkit.Windows/TaskbarButton.cs b/Toolkit.Windows/TaskbarButton.cs new file mode 100644 index 0000000..80192e8 --- /dev/null +++ b/Toolkit.Windows/TaskbarButton.cs @@ -0,0 +1,99 @@ +using Toolkit.Foundation; + +namespace Toolkit.Windows; + +public class TaskbarButton : + ITaskbarButton, + INotificationHandler, + INotificationHandler, + INotificationHandler +{ + private readonly IPublisher publisher; + private readonly IDisposer disposer; + private bool isWithinBounds; + private bool isDrag; + + public TaskbarButton(string name, + Rect rect, + IPublisher publisher, + ISubscriber subscriber, + IDisposer disposer) + { + this.publisher = publisher; + this.disposer = disposer; + + Name = name; + Rect = rect; + + subscriber.Subscribe(this); + } + + public Rect Rect { get; internal set; } + + public string Name { get; internal set; } + + public void Dispose() + { + disposer.Dispose(this); + GC.SuppressFinalize(this); + } + + public Task Handle(PointerReleasedEventArgs args) + { + if (!isDrag && isWithinBounds) + { + publisher.Publish(new TaskbarButtonInvokedEventArgs(this)); + } + + if (isDrag) + { + isDrag = false; + } + + return Task.CompletedTask; + } + + public Task Handle(PointerDragEventArgs args) + { + if (isWithinBounds) + { + if (isDrag) + { + publisher.Publish(new TaskbarButtonDragOverEventArgs(this)); + } + else + { + publisher.Publish(new TaskbarButtonDragEnterEventArgs(this)); + } + + isDrag = true; + } + else + { + isDrag = false; + } + + return Task.CompletedTask; + } + + public Task Handle(PointerMovedEventArgs args) + { + if (args.Location.IsWithinBounds(Rect)) + { + if (isWithinBounds) + { + return Task.CompletedTask; + } + + isWithinBounds = true; + publisher.Publish(new TaskbarButtonEnteredEventArgs(this)); + } + else + { + isDrag = false; + isWithinBounds = false; + } + + return Task.CompletedTask; + } +} diff --git a/Toolkit.Windows/TaskbarButtonDragEnterEventArgs.cs b/Toolkit.Windows/TaskbarButtonDragEnterEventArgs.cs new file mode 100644 index 0000000..7747bb7 --- /dev/null +++ b/Toolkit.Windows/TaskbarButtonDragEnterEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarButtonDragEnterEventArgs(TaskbarButton Button); diff --git a/Toolkit.Windows/TaskbarButtonDragOverEventArgs.cs b/Toolkit.Windows/TaskbarButtonDragOverEventArgs.cs new file mode 100644 index 0000000..34bc29e --- /dev/null +++ b/Toolkit.Windows/TaskbarButtonDragOverEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarButtonDragOverEventArgs(TaskbarButton Button); diff --git a/Toolkit.Windows/TaskbarButtonEnteredEventArgs.cs b/Toolkit.Windows/TaskbarButtonEnteredEventArgs.cs new file mode 100644 index 0000000..ec94eb6 --- /dev/null +++ b/Toolkit.Windows/TaskbarButtonEnteredEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarButtonEnteredEventArgs(TaskbarButton Button); diff --git a/Toolkit.Windows/TaskbarButtonInvokedEventArgs.cs b/Toolkit.Windows/TaskbarButtonInvokedEventArgs.cs new file mode 100644 index 0000000..6f88146 --- /dev/null +++ b/Toolkit.Windows/TaskbarButtonInvokedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarButtonInvokedEventArgs(TaskbarButton Button); diff --git a/Toolkit.Windows/TaskbarChangedEventArgs.cs b/Toolkit.Windows/TaskbarChangedEventArgs.cs new file mode 100644 index 0000000..aa27c42 --- /dev/null +++ b/Toolkit.Windows/TaskbarChangedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarChangedEventArgs; diff --git a/Toolkit.Windows/TaskbarDragEnterEventArgs.cs b/Toolkit.Windows/TaskbarDragEnterEventArgs.cs new file mode 100644 index 0000000..7929ade --- /dev/null +++ b/Toolkit.Windows/TaskbarDragEnterEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarDragEnterEventArgs; diff --git a/Toolkit.Windows/TaskbarDragOverEventArgs.cs b/Toolkit.Windows/TaskbarDragOverEventArgs.cs new file mode 100644 index 0000000..15066ea --- /dev/null +++ b/Toolkit.Windows/TaskbarDragOverEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarDragOverEventArgs; diff --git a/Toolkit.Windows/TaskbarEnteredEventArgs.cs b/Toolkit.Windows/TaskbarEnteredEventArgs.cs new file mode 100644 index 0000000..701fa10 --- /dev/null +++ b/Toolkit.Windows/TaskbarEnteredEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarEnteredEventArgs; diff --git a/Toolkit.Windows/TaskbarList.cs b/Toolkit.Windows/TaskbarList.cs new file mode 100644 index 0000000..2fad053 --- /dev/null +++ b/Toolkit.Windows/TaskbarList.cs @@ -0,0 +1,12 @@ +namespace Toolkit.Windows; + +public class TaskbarList(ITaskbar taskbar) : + ITaskbarList +{ + public IntPtr GetHandle() + { + nint rebarHandle = WindowHelper.Find("ReBarWindow32", taskbar.GetHandle()); + nint taskHandle = WindowHelper.Find("MSTaskSwWClass", rebarHandle); + return WindowHelper.Find("MSTaskListWClass", taskHandle); + } +} diff --git a/Toolkit.Windows/TaskbarPlacement.cs b/Toolkit.Windows/TaskbarPlacement.cs new file mode 100644 index 0000000..24e5bc2 --- /dev/null +++ b/Toolkit.Windows/TaskbarPlacement.cs @@ -0,0 +1,9 @@ +namespace Toolkit.Windows; + +public enum TaskbarPlacement +{ + Left = 0, + Top = 1, + Right = 2, + Bottom = 3 +} diff --git a/Toolkit.Windows/TaskbarState.cs b/Toolkit.Windows/TaskbarState.cs new file mode 100644 index 0000000..a52a6be --- /dev/null +++ b/Toolkit.Windows/TaskbarState.cs @@ -0,0 +1,8 @@ +namespace Toolkit.Windows; + +public struct TaskbarState +{ + public TaskbarPlacement Placement; + public Rect Rect; + public Screen Screen; +} diff --git a/Toolkit.Windows/Toolkit.Windows.csproj b/Toolkit.Windows/Toolkit.Windows.csproj index ae4c7d3..b784cf4 100644 --- a/Toolkit.Windows/Toolkit.Windows.csproj +++ b/Toolkit.Windows/Toolkit.Windows.csproj @@ -1,12 +1,14 @@  - net9.0-windows + net9.0-windows10.0.19041.0 enable enable True True - x64;x86 + AnyCPU;x64;x86 + 10.0.19041.41 + @@ -14,7 +16,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/Toolkit.Windows/WndProcMonitor.cs b/Toolkit.Windows/WndProcMonitor.cs new file mode 100644 index 0000000..c3b69af --- /dev/null +++ b/Toolkit.Windows/WndProcMonitor.cs @@ -0,0 +1,65 @@ +using Toolkit.Foundation; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Graphics.Gdi; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace Toolkit.Windows; + +public class WndProcMonitor(IPublisher publisher) : + IWndProcMonitor +{ + private WNDPROC? handler; + private readonly IPublisher publisher = publisher; + + public IntPtr Handle { get; private set; } + + public void Dispose() + { + PInvoke.DestroyWindow((HWND)Handle); + } + + private unsafe void InitializeWndProc() + { + var windowName = Guid.NewGuid().ToString(); + handler = Wndproc; + + WNDCLASSW wndProcWindow; + + wndProcWindow.style = 0; + wndProcWindow.lpfnWndProc = handler; + wndProcWindow.cbClsExtra = 0; + wndProcWindow.cbWndExtra = 0; + wndProcWindow.hInstance = new HINSTANCE(); + wndProcWindow.hIcon = new HICON(); + wndProcWindow.hCursor = new HCURSOR(); + wndProcWindow.hbrBackground = new HBRUSH(); + + fixed (char* menuName = "") + { + wndProcWindow.lpszMenuName = new PCWSTR(menuName); + } + + fixed (char* className = windowName) + { + wndProcWindow.lpszClassName = new PCWSTR(className); + } + + PInvoke.RegisterClass(wndProcWindow); + Handle = PInvoke.CreateWindowEx(0, wndProcWindow.lpszClassName, + new PCWSTR(), 0, 0, 0, 0, 0, new HWND(), + new HMENU(), + new HINSTANCE()); + } + + private LRESULT Wndproc(HWND param0, uint param1, WPARAM param2, LPARAM param3) + { + publisher.Publish(new WndProcEventArgs(param1, (uint)param2.Value, (uint)param3.Value)); + return PInvoke.DefWindowProc(param0, param1, param2, param3); + } + + public void Initialize() + { + InitializeWndProc(); + } +}