diff --git a/SFMLRenderer/SFMLHwndHost.cs b/SFMLRenderer/SFMLHwndHost.cs index 975968b..999e681 100644 --- a/SFMLRenderer/SFMLHwndHost.cs +++ b/SFMLRenderer/SFMLHwndHost.cs @@ -64,10 +64,10 @@ namespace SFMLRenderer hs?.Dispose(); } - private nint HwndMessageHook(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) + private IntPtr HwndMessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { _renderWindow?.DispatchEvents(); - return nint.Zero; + return IntPtr.Zero; } } } diff --git a/SFMLRenderer/SFMLRenderWindow.cs b/SFMLRenderer/SFMLRenderWindow.cs new file mode 100644 index 0000000..ce31211 --- /dev/null +++ b/SFMLRenderer/SFMLRenderWindow.cs @@ -0,0 +1,171 @@ +using SFML.Graphics; +using SFML.System; +using SFML.Window; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace SFMLRenderer +{ + public class SFMLRenderWindow : RenderWindow, ISFMLRenderer + { + private readonly DispatcherTimer _timer = new() { Interval = TimeSpan.FromMilliseconds(10) }; + + public SFMLRenderWindow(VideoMode mode, string title, Styles style) : base(mode, title, style) + { + SetActive(false); + _timer.Tick += (s, e) => DispatchEvents(); + _timer.Start(); + RendererCreated?.Invoke(this, EventArgs.Empty); + } + + public event EventHandler? RendererCreated; + + public event EventHandler? RendererDisposing + { + add => throw new NotImplementedException(); + remove => throw new NotImplementedException(); + } + + public event EventHandler? CanvasMouseMove + { + add { MouseMoved += value; } + remove { MouseMoved -= value; } + } + + public event EventHandler? CanvasMouseButtonPressed + { + add { MouseButtonPressed += value; } + remove { MouseButtonPressed -= value; } + } + + public event EventHandler? CanvasMouseButtonReleased + { + add { MouseButtonReleased += value; } + remove { MouseButtonReleased -= value; } + } + + public event EventHandler? CanvasMouseWheelScrolled + { + add { MouseWheelScrolled += value; } + remove { MouseWheelScrolled -= value; } + } + + public Vector2u Resolution + { + get => Size; + set => Size = value; + } + + public Vector2f Center + { + get + { + using var view = GetView(); + return view.Center; + } + set + { + using var view = GetView(); + view.Center = value; + SetView(view); + } + } + + public float Zoom + { + get + { + using var view = GetView(); + return Math.Abs(Size.X / view.Size.X); // XXX: 仅使用宽度进行缩放计算 + } + set + { + value = Math.Abs(value); + if (value <= 0) return; + using var view = GetView(); + var signX = Math.Sign(view.Size.X); + var signY = Math.Sign(view.Size.Y); + var resolution = Size; + view.Size = new(resolution.X / value * signX, resolution.Y / value * signY); + SetView(view); + } + } + + public float Rotation + { + get + { + using var view = GetView(); + return view.Rotation; + } + set + { + using var view = GetView(); + view.Rotation = value; + SetView(view); + } + } + + public bool FlipX + { + get + { + using var view = GetView(); + return view.Size.X < 0; + } + set + { + using var view = GetView(); + var size = view.Size; + if (size.X > 0 && value || size.X < 0 && !value) + size.X *= -1; + view.Size = size; + SetView(view); + } + } + + public bool FlipY + { + get + { + using var view = GetView(); + return view.Size.Y < 0; + } + set + { + using var view = GetView(); + var size = view.Size; + if (size.Y > 0 && value || size.Y < 0 && !value) + size.Y *= -1; + view.Size = size; + SetView(view); + } + } + + public uint MaxFps + { + get => _maxFps; + set + { + SetFramerateLimit(value); + _maxFps = value; + } + } + private uint _maxFps = 0; + + public bool VerticalSync + { + get => _verticalSync; + set + { + SetVerticalSyncEnabled(value); + _verticalSync = value; + } + } + private bool _verticalSync = false; + } +} diff --git a/SpineViewer/Natives/Gdi32.cs b/SpineViewer/Natives/Gdi32.cs index 6e97ae2..5845cf9 100644 --- a/SpineViewer/Natives/Gdi32.cs +++ b/SpineViewer/Natives/Gdi32.cs @@ -15,15 +15,15 @@ namespace SpineViewer.Natives public static class Gdi32 { [DllImport("gdi32.dll", SetLastError = true)] - public static extern nint CreateCompatibleDC(nint hdc); + public static extern IntPtr CreateCompatibleDC(IntPtr hdc); [DllImport("gdi32.dll", SetLastError = true)] - public static extern bool DeleteDC(nint hdc); + public static extern bool DeleteDC(IntPtr hdc); [DllImport("gdi32.dll", SetLastError = true)] - public static extern nint SelectObject(nint hdc, nint hgdiobj); + public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); [DllImport("gdi32.dll", SetLastError = true)] - public static extern bool DeleteObject(nint hObject); + public static extern bool DeleteObject(IntPtr hObject); } } diff --git a/SpineViewer/Natives/User32.cs b/SpineViewer/Natives/User32.cs index 06d9714..4702bd6 100644 --- a/SpineViewer/Natives/User32.cs +++ b/SpineViewer/Natives/User32.cs @@ -15,22 +15,61 @@ namespace SpineViewer.Natives public static class User32 { public const int GWL_STYLE = -16; - public const int WS_SIZEBOX = 0x40000; - public const int WS_BORDER = 0x800000; - public const int WS_VISIBLE = 0x10000000; - public const int WS_CHILD = 0x40000000; + public const int WS_OVERLAPPED = 0x00000000; public const int WS_POPUP = unchecked((int)0x80000000); + public const int WS_CHILD = 0x40000000; + public const int WS_MINIMIZE = 0x20000000; + public const int WS_VISIBLE = 0x10000000; + public const int WS_DISABLED = 0x08000000; + public const int WS_CLIPSIBLINGS = 0x04000000; + public const int WS_CLIPCHILDREN = 0x02000000; + public const int WS_MAXIMIZE = 0x01000000; + public const int WS_BORDER = 0x00800000; + public const int WS_DLGFRAME = 0x00400000; + public const int WS_VSCROLL = 0x00200000; + public const int WS_HSCROLL = 0x00100000; + public const int WS_SYSMENU = 0x00080000; + public const int WS_THICKFRAME = 0x00040000; + public const int WS_GROUP = 0x00020000; + public const int WS_TABSTOP = 0x00010000; + public const int WS_MINIMIZEBOX = 0x00020000; + public const int WS_MAXIMIZEBOX = 0x00010000; + public const int WS_CHILDWINDOW = WS_CHILD; + public const int WS_CAPTION = WS_BORDER | WS_DLGFRAME; + public const int WS_TILED = WS_OVERLAPPED; + public const int WS_ICONIC = WS_MINIMIZE; + public const int WS_SIZEBOX = WS_THICKFRAME; + public const int WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW; + public const int WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + public const int WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU; public const int GWL_EXSTYLE = -20; - public const int WS_EX_TOPMOST = 0x8; - public const int WS_EX_TRANSPARENT = 0x20; - public const int WS_EX_TOOLWINDOW = 0x80; - public const int WS_EX_WINDOWEDGE = 0x100; - public const int WS_EX_CLIENTEDGE = 0x200; - public const int WS_EX_APPWINDOW = 0x40000; - public const int WS_EX_LAYERED = 0x80000; + public const int WS_EX_DLGMODALFRAME = 0x00000001; + public const int WS_EX_NOPARENTNOTIFY = 0x00000004; + public const int WS_EX_TOPMOST = 0x00000008; + public const int WS_EX_ACCEPTFILES = 0x00000010; + public const int WS_EX_TRANSPARENT = 0x00000020; + public const int WS_EX_MDICHILD = 0x00000040; + public const int WS_EX_TOOLWINDOW = 0x00000080; + public const int WS_EX_WINDOWEDGE = 0x00000100; + public const int WS_EX_CLIENTEDGE = 0x00000200; + public const int WS_EX_CONTEXTHELP = 0x00000400; + public const int WS_EX_RIGHT = 0x00001000; + public const int WS_EX_LEFT = 0x00000000; + public const int WS_EX_RTLREADING = 0x00002000; + public const int WS_EX_LTRREADING = 0x00000000; + public const int WS_EX_LEFTSCROLLBAR = 0x00004000; + public const int WS_EX_RIGHTSCROLLBAR = 0x00000000; + public const int WS_EX_CONTROLPARENT = 0x00010000; + public const int WS_EX_STATICEDGE = 0x00020000; + public const int WS_EX_APPWINDOW = 0x00040000; public const int WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE; - public const int WS_EX_NOACTIVATE = 0x8000000; + public const int WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST; + public const int WS_EX_LAYERED = 0x00080000; + public const int WS_EX_NOINHERITLAYOUT = 0x00100000; + public const int WS_EX_LAYOUTRTL = 0x00400000; + public const int WS_EX_COMPOSITED = 0x02000000; + public const int WS_EX_NOACTIVATE = 0x08000000; public const uint LWA_COLORKEY = 0x1; public const uint LWA_ALPHA = 0x2; @@ -42,12 +81,23 @@ namespace SpineViewer.Natives public const int ULW_ALPHA = 0x00000002; public const int ULW_OPAQUE = 0x00000004; - public const nint HWND_TOPMOST = -1; + public const IntPtr HWND_TOPMOST = -1; - public const uint SWP_NOSIZE = 0x0001; - public const uint SWP_NOMOVE = 0x0002; - public const uint SWP_NOZORDER = 0x0004; + public const uint SWP_ASYNCWINDOWPOS = 0x4000; + public const uint SWP_DEFERERASE = 0x2000; + public const uint SWP_NOSENDCHANGING = 0x0400; + public const uint SWP_NOOWNERZORDER = 0x0200; + public const uint SWP_NOREPOSITION = 0x0200; + public const uint SWP_NOCOPYBITS = 0x0100; + public const uint SWP_HIDEWINDOW = 0x0080; + public const uint SWP_SHOWWINDOW = 0x0040; + public const uint SWP_DRAWFRAME = 0x0020; public const uint SWP_FRAMECHANGED = 0x0020; + public const uint SWP_NOACTIVATE = 0x0010; + public const uint SWP_NOREDRAW = 0x0008; + public const uint SWP_NOZORDER = 0x0004; + public const uint SWP_NOMOVE = 0x0002; + public const uint SWP_NOSIZE = 0x0001; public const int WM_SPAWN_WORKER = 0x052C; // 一个未公开的神秘消息 @@ -72,6 +122,8 @@ namespace SpineViewer.Natives public const int SW_RESTORE = 9; public const int SW_SHOWDEFAULT = 10; + public const uint MONITOR_DEFAULTTONULL = 0; + public const uint MONITOR_DEFAULTTOPRIMARY = 1; public const uint MONITOR_DEFAULTTONEAREST = 2; [StructLayout(LayoutKind.Sequential)] @@ -124,29 +176,25 @@ namespace SpineViewer.Natives public string szDevice; } - [DllImport("user32.dll", SetLastError = true)] - public static extern nint GetDC(nint hWnd); + public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] - public static extern int ReleaseDC(nint hWnd, nint hDC); + public static extern IntPtr GetDC(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] - public static extern int SetWindowLong(nint hWnd, int nIndex, int dwNewLong); + public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); [DllImport("user32.dll", SetLastError = true)] - public static extern int GetWindowLong(nint hWnd, int nIndex); + public static extern bool GetLayeredWindowAttributes(IntPtr hWnd, ref uint crKey, ref byte bAlpha, ref uint dwFlags); [DllImport("user32.dll", SetLastError = true)] - public static extern bool GetLayeredWindowAttributes(nint hWnd, ref uint crKey, ref byte bAlpha, ref uint dwFlags); + public static extern bool SetLayeredWindowAttributes(IntPtr hWnd, uint pcrKey, byte pbAlpha, uint pdwFlags); [DllImport("user32.dll", SetLastError = true)] - public static extern bool SetLayeredWindowAttributes(nint hWnd, uint pcrKey, byte pbAlpha, uint pdwFlags); + public static extern bool UpdateLayeredWindow(IntPtr hWnd, IntPtr hdcDst, IntPtr pptDst, ref SIZE psize, IntPtr hdcSrc, ref POINT pptSrc, int crKey, ref BLENDFUNCTION pblend, int dwFlags); [DllImport("user32.dll", SetLastError = true)] - public static extern bool UpdateLayeredWindow(nint hWnd, nint hdcDst, nint pptDst, ref SIZE psize, nint hdcSrc, ref POINT pptSrc, int crKey, ref BLENDFUNCTION pblend, int dwFlags); - - [DllImport("user32.dll", SetLastError = true)] - public static extern bool SetWindowPos(nint hWnd, nint hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport("user32.dll", SetLastError = true)] public static extern uint GetDoubleClickTime(); @@ -155,28 +203,34 @@ namespace SpineViewer.Natives private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); [DllImport("user32.dll", SetLastError = true)] - public static extern nint FindWindow(string lpClassName, string lpWindowName); + public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll", SetLastError = true)] - public static extern nint SendMessageTimeout(nint hWnd, uint Msg, nint wParam, nint lParam, uint fuFlags, uint uTimeout, out nint lpdwResult); + public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam, uint fuFlags, uint uTimeout, out IntPtr lpdwResult); [DllImport("user32.dll", SetLastError = true)] - public static extern nint FindWindowEx(nint parentHandle, nint childAfter, string className, string windowTitle); + public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle); [DllImport("user32.dll", SetLastError = true)] - public static extern nint SetParent(nint hWndChild, nint hWndNewParent); + public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); [DllImport("user32.dll", SetLastError = true)] - public static extern nint GetParent(nint hWnd); + public static extern IntPtr GetParent(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] - public static extern nint GetAncestor(nint hWnd, uint gaFlags); + public static extern IntPtr GetAncestor(IntPtr hWnd, uint gaFlags); [DllImport("user32.dll", SetLastError = true)] - public static extern nint GetWindow(nint hWnd, uint uCmd); + public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); [DllImport("user32.dll", SetLastError = true)] - public static extern bool ShowWindow(nint hWnd, int nCmdShow); + public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + [DllImport("user32.dll")] + public static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam); + + [DllImport("User32.dll")] + public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern bool IsIconic(IntPtr hWnd); @@ -190,6 +244,12 @@ namespace SpineViewer.Natives [DllImport("user32.dll")] private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi); + [DllImport("user32.dll")] + public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll")] + public static extern int GetWindowLong(IntPtr hWnd, int nIndex); + public static TimeSpan GetLastInputElapsedTime() { LASTINPUTINFO lastInputInfo = new(); @@ -205,14 +265,59 @@ namespace SpineViewer.Natives return TimeSpan.FromMilliseconds(idleTimeMillis); } - public static nint GetWorkerW() + public static IntPtr GetWorkerW() { + // NOTE: Codes borrowed from @rocksdanister/lively + var progman = FindWindow("Progman", null); - if (progman == nint.Zero) - return nint.Zero; - nint hWnd = FindWindowEx(progman, 0, "WorkerW", null); - Debug.WriteLine($"HWND(Progman.WorkerW): {hWnd:x8}"); - return hWnd; + if (progman == IntPtr.Zero) + return IntPtr.Zero; + + // Send 0x052C to Progman. This message directs Progman to spawn a + // WorkerW behind the desktop icons. If it is already there, nothing + // happens. + SendMessageTimeout(progman, WM_SPAWN_WORKER, 0, 0, SMTO_NORMAL, 1000, out _); + + // Spy++ output + // ..... + // 0x00010190 "" WorkerW + // ... + // 0x000100EE "" SHELLDLL_DefView + // 0x000100F0 "FolderView" SysListView32 + // 0x00100B8A "" WorkerW <-- This is the WorkerW instance we are after! + // 0x000100EC "Program Manager" Progman + var workerw = IntPtr.Zero; + + // We enumerate all Windows, until we find one, that has the SHELLDLL_DefView + // as a child. + // If we found that window, we take its next sibling and assign it to workerw. + EnumWindows(new EnumWindowsProc((tophandle, topparamhandle) => + { + IntPtr p = FindWindowEx(tophandle, IntPtr.Zero, "SHELLDLL_DefView", null); + + if (p != IntPtr.Zero) + { + // Gets the WorkerW Window after the current one. + workerw = FindWindowEx(IntPtr.Zero, tophandle, "WorkerW", null); + } + + return true; + }), IntPtr.Zero); + + // Some Windows 11 builds have a different Progman window layout. + // If the above code failed to find WorkerW, we should try this. + // Spy++ output + // 0x000100EC "Program Manager" Progman + // 0x000100EE "" SHELLDLL_DefView + // 0x000100F0 "FolderView" SysListView32 + // 0x00100B8A "" WorkerW <-- This is the WorkerW instance we are after! + if (workerw == IntPtr.Zero) + { + workerw = FindWindowEx(progman, IntPtr.Zero, "WorkerW", null); + } + + Debug.WriteLine($"HWND(WorkerW): {workerw:x8}"); + return workerw; } public static bool GetScreenResolution(IntPtr hwnd, out uint width, out uint height) @@ -231,5 +336,22 @@ namespace SpineViewer.Natives width = height = 0; return false; } + + public static bool GetPrimaryScreenResolution(out uint width, out uint height) + { + IntPtr hMon = MonitorFromWindow(IntPtr.Zero, MONITOR_DEFAULTTOPRIMARY); + + var mi = new MONITORINFOEX { cbSize = (uint)Marshal.SizeOf() }; + if (GetMonitorInfo(hMon, ref mi)) + { + int widthPx = mi.rcMonitor.Right - mi.rcMonitor.Left; + int heightPx = mi.rcMonitor.Bottom - mi.rcMonitor.Top; + width = (uint)widthPx; + height = (uint)heightPx; + return true; + } + width = height = 0; + return false; + } } } diff --git a/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs index e844d31..656f36c 100644 --- a/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/MainWindowViewModel.cs @@ -17,9 +17,10 @@ namespace SpineViewer.ViewModels.MainWindow { private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); - public MainWindowViewModel(ISFMLRenderer sfmlRenderer) + public MainWindowViewModel(ISFMLRenderer sfmlRenderer, ISFMLRenderer wallpaperRenderer) { _sfmlRenderer = sfmlRenderer; + _wallpaperRenderer = wallpaperRenderer; _explorerListViewModel = new(this); _spineObjectListViewModel = new(this); _sfmlRendererViewModel = new(this); @@ -34,6 +35,9 @@ namespace SpineViewer.ViewModels.MainWindow public ISFMLRenderer SFMLRenderer => _sfmlRenderer; private readonly ISFMLRenderer _sfmlRenderer; + public ISFMLRenderer WallpaperRenderer => _wallpaperRenderer; + private readonly ISFMLRenderer _wallpaperRenderer; + public TaskbarItemProgressState ProgressState { get => _progressState; set => SetProperty(ref _progressState, value); } private TaskbarItemProgressState _progressState = TaskbarItemProgressState.None; @@ -133,19 +137,5 @@ namespace SpineViewer.ViewModels.MainWindow _spineObjectListViewModel.LoadedSpineObjects = value.LoadedSpineObjects; } } - - /// - /// 调试命令 - /// - public RelayCommand Cmd_Debug => _cmd_Debug ??= new(Debug_Execute); - private RelayCommand? _cmd_Debug; - - private void Debug_Execute() - { -#if DEBUG - - MessagePopupService.Quest("测试一下"); -#endif - } } } \ No newline at end of file diff --git a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs index ad7c4dd..cdfb2bb 100644 --- a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs @@ -35,6 +35,7 @@ namespace SpineViewer.ViewModels.MainWindow private readonly MainWindowViewModel _vmMain; private readonly ObservableCollectionWithLock _models; private readonly ISFMLRenderer _renderer; + private readonly ISFMLRenderer _wallpaperRenderer; /// /// 被选中对象的背景颜色 @@ -90,6 +91,7 @@ namespace SpineViewer.ViewModels.MainWindow _vmMain = vmMain; _models = _vmMain.SpineObjects; _renderer = _vmMain.SFMLRenderer; + _wallpaperRenderer = _vmMain.WallpaperRenderer; } /// @@ -443,6 +445,7 @@ namespace SpineViewer.ViewModels.MainWindow { try { + _wallpaperRenderer.SetActive(true); _renderer.SetActive(true); float delta; @@ -461,7 +464,11 @@ namespace SpineViewer.ViewModels.MainWindow _forwardDelta = 0; } + using var v = _renderer.GetView(); + _wallpaperRenderer.SetView(v); + _renderer.Clear(_backgroundColor); + _wallpaperRenderer.Clear(_backgroundColor); // 渲染背景 lock (_bgLock) @@ -492,6 +499,7 @@ namespace SpineViewer.ViewModels.MainWindow bg.Position = view.Center; bg.Rotation = view.Rotation; _renderer.Draw(bg); + _wallpaperRenderer.Draw(bg); } } @@ -531,10 +539,12 @@ namespace SpineViewer.ViewModels.MainWindow sp.EnableDebug = true; _renderer.Draw(sp); sp.EnableDebug = false; + _wallpaperRenderer.Draw(sp); } } _renderer.Display(); + _wallpaperRenderer.Display(); } } catch (Exception ex) @@ -546,6 +556,7 @@ namespace SpineViewer.ViewModels.MainWindow finally { _renderer.SetActive(false); + _wallpaperRenderer.SetActive(false); } } diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml index 965473a..d6d2e6d 100644 --- a/SpineViewer/Views/MainWindow.xaml +++ b/SpineViewer/Views/MainWindow.xaml @@ -69,7 +69,7 @@ - + diff --git a/SpineViewer/Views/MainWindow.xaml.cs b/SpineViewer/Views/MainWindow.xaml.cs index 44c5dd1..f749bf7 100644 --- a/SpineViewer/Views/MainWindow.xaml.cs +++ b/SpineViewer/Views/MainWindow.xaml.cs @@ -1,7 +1,7 @@ -using Microsoft.Win32; -using NLog; +using NLog; using NLog.Layouts; using NLog.Targets; +using SFMLRenderer; using Spine; using SpineViewer.Models; using SpineViewer.Natives; @@ -35,17 +35,24 @@ public partial class MainWindow : Window public static readonly string LastStateFilePath = Path.Combine(Path.GetDirectoryName(Environment.ProcessPath), "laststate.json"); private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private ListViewItem? _listViewDragSourceItem = null; private Point _listViewDragSourcePoint; + private readonly SFMLRenderWindow _wallpaperRenderWindow; private readonly MainWindowViewModel _vm; public MainWindow() { InitializeComponent(); InitializeLogConfiguration(); - DataContext = _vm = new(_renderPanel); - _notifyIcon.Text = _vm.Title; // XXX: hc 的 NotifyIcon 的 Text 似乎没法双向绑定 + + _wallpaperRenderWindow = new(new(500, 500), "SpineViewerWallpaper", SFML.Window.Styles.None); + _wallpaperRenderWindow.SetVisible(false); + DataContext = _vm = new(_renderPanel, _wallpaperRenderWindow); + + // XXX: hc 的 NotifyIcon 的 Text 似乎没法双向绑定 + _notifyIcon.Text = _vm.Title; _vm.SpineObjectListViewModel.RequestSelectionChanging += SpinesListView_RequestSelectionChanging; _vm.SFMLRendererViewModel.RequestSelectionChanging += SpinesListView_RequestSelectionChanging; @@ -333,7 +340,7 @@ public partial class MainWindow : Window private void _notifyIcon_Click(object sender, RoutedEventArgs e) { - + } private void _notifyIcon_MouseDoubleClick(object sender, RoutedEventArgs e) @@ -600,4 +607,33 @@ public partial class MainWindow : Window } #endregion + + private void DebugMenuItem_Click(object sender, RoutedEventArgs e) + { +#if DEBUG + var www = _wallpaperRenderWindow; + User32.GetPrimaryScreenResolution(out var sw, out var sh); + www.Position = new(0, 0); + www.Size = new(sw, sh); + + var handle = www.SystemHandle; + Debug.WriteLine($"Handle: {handle:x8}"); + + var style = User32.GetWindowLong(handle, User32.GWL_STYLE) | User32.WS_POPUP; + var exStyle = User32.GetWindowLong(handle, User32.GWL_EXSTYLE) | User32.WS_EX_LAYERED | User32.WS_EX_TOOLWINDOW | User32.WS_EX_TOPMOST; + User32.SetWindowLong(handle, User32.GWL_STYLE, style); + User32.SetWindowLong(handle, User32.GWL_EXSTYLE, exStyle); + User32.SetLayeredWindowAttributes(handle, 0, 200, User32.LWA_ALPHA); + + var workerw = User32.GetWorkerW(); + var res = User32.SetParent(handle, workerw); + Debug.WriteLine($"SetParent: {res:x8}"); + + User32.SetLayeredWindowAttributes(handle, 0, 255, User32.LWA_ALPHA); + var s = www.Size; + www.Size = new(s.X + 1, s.Y + 1); + www.Size = s; + www.SetVisible(true); +#endif + } } \ No newline at end of file