Layers/shell/windows/LayersShell/MainWindow.xaml.cs

281 lines
9.4 KiB
C#

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Windows.Graphics;
using WinRT.Interop;
namespace LayersShell;
public sealed partial class MainWindow : Window
{
private IntPtr _handle = IntPtr.Zero;
private IntPtr _hwnd = IntPtr.Zero;
private const double DefaultLogicalWidth = 480;
private const double DefaultLogicalHeight = 640;
private bool _isFocused = true;
private bool _isHovered = false;
public MainWindow()
{
App.Log("MainWindow ctor enter");
try
{
InitializeComponent();
}
catch (Exception ex) { App.Log($"InitializeComponent FAILED: {ex}"); throw; }
Title = "Layers";
_hwnd = WindowNative.GetWindowHandle(this);
App.Log($"hwnd = {_hwnd:X}");
try
{
var appWindow = AppWindow.GetFromWindowId(Win32Interop.GetWindowIdFromWindow(_hwnd));
App.Log($"appWindow null? {appWindow is null}");
if (appWindow is not null)
{
appWindow.SetPresenter(AppWindowPresenterKind.Overlapped);
App.Log($"presenter kind: {appWindow.Presenter?.Kind}");
if (appWindow.Presenter is OverlappedPresenter op)
{
op.IsAlwaysOnTop = true;
op.IsResizable = true;
op.IsMaximizable = false;
op.IsMinimizable = true;
App.Log("OverlappedPresenter configured");
}
App.Log("before ResizeClient");
appWindow.ResizeClient(new SizeInt32((int)DefaultLogicalWidth, (int)DefaultLogicalHeight));
App.Log("ResizeClient ok");
}
}
catch (Exception ex) { App.Log($"appWindow setup FAILED: {ex}"); throw; }
try
{
SystemBackdrop = new MicaBackdrop { Kind = Microsoft.UI.Composition.SystemBackdrops.MicaKind.Base };
App.Log("SystemBackdrop assigned");
}
catch (Exception ex) { App.Log($"SystemBackdrop FAILED: {ex}"); throw; }
Activated += OnActivated;
Closed += OnClosed;
App.Log("events wired");
DispatcherQueue.TryEnqueue(() =>
{
try
{
App.Log("deferred init: PointToRust");
PointToRust();
App.Log("deferred init: StartupIfNeeded");
LayersNative.StartupIfNeeded();
App.Log("deferred init: CreateNativeHandle");
CreateNativeHandle();
App.Log($"deferred init: _handle = {_handle:X}");
StartRenderLoop();
App.Log("deferred init: render loop started");
}
catch (Exception ex)
{
App.Log($"deferred init FAILED: {ex}");
throw;
}
});
}
private void OnActivated(object sender, WindowActivatedEventArgs e)
{
_isFocused = e.WindowActivationState != WindowActivationState.Deactivated;
ApplyFade();
}
private void OnClosed(object sender, WindowEventArgs args)
{
_renderTimer?.Stop();
_renderTimer = null;
if (_handle != IntPtr.Zero)
{
LayersNative.layers_destroy(_handle);
_handle = IntPtr.Zero;
}
}
private void PointToRust()
{
App.Log("PointToRust enter");
var exe = System.Reflection.Assembly.GetEntryAssembly()?.Location;
App.Log($"exe = {exe}");
if (string.IsNullOrEmpty(exe)) { App.Log("PointToRust: empty exe path"); return; }
var binDir = Path.GetDirectoryName(exe) ?? string.Empty;
var pluginRoot = Path.GetDirectoryName(binDir) ?? string.Empty;
App.Log($"pluginRoot = {pluginRoot}");
var dllPath = Path.Combine(binDir, "layers_native.dll");
App.Log($"layers_native.dll present: {File.Exists(dllPath)}; path: {dllPath}");
try
{
var h = System.Runtime.InteropServices.NativeLibrary.Load(dllPath);
App.Log($"NativeLibrary.Load ok -> 0x{h.ToInt64():X}");
}
catch (Exception e)
{
App.Log($"NativeLibrary.Load FAILED: {e.GetType().Name}: {e.Message}");
throw;
}
App.Log("calling layers_set_plugin_root…");
LayersNative.layers_set_plugin_root(pluginRoot);
App.Log("layers_set_plugin_root returned");
}
private void CreateNativeHandle()
{
if (_handle != IntPtr.Zero) return;
var scale = (float)RenderSurface.CompositionScaleX;
var width = (float)RenderSurface.ActualWidth;
var height = (float)RenderSurface.ActualHeight;
App.Log($"CreateNativeHandle: w={width} h={height} scale={scale}");
if (width <= 0 || height <= 0 || scale <= 0)
{
App.Log("CreateNativeHandle: skipped (panel not laid out yet)");
return;
}
var iid = typeof(ISwapChainPanelNative).GUID;
var unknown = Marshal.GetIUnknownForObject(RenderSurface);
IntPtr native = IntPtr.Zero;
try
{
int hr = Marshal.QueryInterface(unknown, in iid, out native);
App.Log($"QueryInterface hr=0x{hr:X} native=0x{native.ToInt64():X}");
if (hr < 0 || native == IntPtr.Zero) return;
_handle = LayersNative.layers_create_from_swap_chain_panel(native, width, height, scale);
App.Log($"layers_create_from_swap_chain_panel -> 0x{_handle.ToInt64():X}");
}
catch (Exception ex)
{
App.Log($"CreateNativeHandle threw: {ex}");
throw;
}
finally
{
if (native != IntPtr.Zero) Marshal.Release(native);
Marshal.Release(unknown);
}
}
private DispatcherTimer? _renderTimer;
private void StartRenderLoop()
{
_renderTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(1000.0 / 60.0),
};
_renderTimer.Tick += (_, __) =>
{
if (_handle != IntPtr.Zero)
{
LayersNative.layers_render(_handle);
}
};
_renderTimer.Start();
}
private void ApplyFade()
{
double target = (_isFocused && _isHovered) ? 1.0
: (_isFocused || _isHovered) ? 0.5
: 0.1;
if (Content is FrameworkElement root)
{
root.Opacity = target;
}
}
private void RenderSurface_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (_handle == IntPtr.Zero)
{
CreateNativeHandle();
return;
}
var scale = (float)RenderSurface.CompositionScaleX;
LayersNative.layers_resize(_handle, (float)e.NewSize.Width, (float)e.NewSize.Height, scale);
}
private void RenderSurface_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (_handle == IntPtr.Zero) return;
var p = e.GetCurrentPoint(RenderSurface).Position;
LayersNative.layers_mouse_move(_handle, (float)p.X, (float)p.Y);
}
private void RenderSurface_PointerPressed(object sender, PointerRoutedEventArgs e)
{
RenderSurface.Focus(FocusState.Programmatic);
PushMouseButton(e, pressed: true);
}
private void RenderSurface_PointerReleased(object sender, PointerRoutedEventArgs e)
{
PushMouseButton(e, pressed: false);
}
private void PushMouseButton(PointerRoutedEventArgs e, bool pressed)
{
if (_handle == IntPtr.Zero) return;
var p = e.GetCurrentPoint(RenderSurface);
uint btn;
var props = p.Properties;
if (props.IsLeftButtonPressed || (!pressed && !props.IsRightButtonPressed && !props.IsMiddleButtonPressed)) btn = 0;
else if (props.IsRightButtonPressed) btn = 1;
else if (props.IsMiddleButtonPressed) btn = 2;
else btn = 0;
LayersNative.layers_mouse_button(_handle, (float)p.Position.X, (float)p.Position.Y, btn, pressed);
}
private void RenderSurface_PointerWheel(object sender, PointerRoutedEventArgs e)
{
if (_handle == IntPtr.Zero) return;
var p = e.GetCurrentPoint(RenderSurface);
var dy = (float)p.Properties.MouseWheelDelta / 3.0f;
LayersNative.layers_mouse_scroll(_handle, (float)p.Position.X, (float)p.Position.Y, 0.0f, dy);
}
private void RenderSurface_PointerEntered(object sender, PointerRoutedEventArgs e)
{
_isHovered = true;
ApplyFade();
}
private void RenderSurface_PointerExited(object sender, PointerRoutedEventArgs e)
{
_isHovered = false;
if (_handle != IntPtr.Zero)
{
LayersNative.layers_mouse_left(_handle);
}
ApplyFade();
}
private void RenderSurface_KeyDown(object sender, KeyRoutedEventArgs e) => DispatchKey(e, pressed: true);
private void RenderSurface_KeyUp(object sender, KeyRoutedEventArgs e) => DispatchKey(e, pressed: false);
private void DispatchKey(KeyRoutedEventArgs e, bool pressed)
{
if (_handle == IntPtr.Zero) return;
uint named = WinKeyMap.MapVirtualKey(e.Key);
string? text = named == 0 ? WinKeyMap.TextForKey(e.Key) : null;
uint mods = WinKeyMap.CurrentModifiers();
LayersNative.layers_key_event(_handle, named, text, mods, (ushort)e.Key, pressed);
}
}