281 lines
9.4 KiB
C#
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);
|
|
}
|
|
}
|