OKAY, FUCK C#, FUCK DOT NET - IM DONE. DIFFERENT APPROACH
This commit is contained in:
parent
87c66733eb
commit
9d28aadf26
12
Cargo.toml
12
Cargo.toml
|
|
@ -7,10 +7,7 @@ rust-version = "1.75"
|
||||||
[lib]
|
[lib]
|
||||||
name = "layers"
|
name = "layers"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
# staticlib: macOS Swift shell links it directly.
|
crate-type = ["staticlib", "rlib"]
|
||||||
# cdylib: Windows (layers.dll, loaded by WinUI 3 shell) and Linux (liblayers.so, loaded by GTK shell).
|
|
||||||
# rlib: in-tree bin target.
|
|
||||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "layers"
|
name = "layers"
|
||||||
|
|
@ -30,6 +27,13 @@ raw-window-handle = "0.6"
|
||||||
pollster = "0.4"
|
pollster = "0.4"
|
||||||
winit = "0.30"
|
winit = "0.30"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
|
windows = { version = "0.58", features = [
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_Graphics_Dwm",
|
||||||
|
"Win32_UI_WindowsAndMessaging",
|
||||||
|
] }
|
||||||
|
|
||||||
iced_wgpu = "0.14"
|
iced_wgpu = "0.14"
|
||||||
iced_graphics = "0.14"
|
iced_graphics = "0.14"
|
||||||
iced_runtime = "0.14"
|
iced_runtime = "0.14"
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,6 @@ int32_t layers_startup(void);
|
||||||
void layers_set_plugin_root(const char* path_utf8);
|
void layers_set_plugin_root(const char* path_utf8);
|
||||||
|
|
||||||
ViewportHandle* layers_create(void* nsview, float width, float height, float scale);
|
ViewportHandle* layers_create(void* nsview, float width, float height, float scale);
|
||||||
/// Windows / WinUI 3 — host a Rust-rendered viewport in a SwapChainPanel.
|
|
||||||
/// `swap_chain_panel` is the SwapChainPanel's native COM IInspectable pointer.
|
|
||||||
ViewportHandle* layers_create_from_swap_chain_panel(void* swap_chain_panel, float width, float height, float scale);
|
|
||||||
void layers_destroy(ViewportHandle* handle);
|
void layers_destroy(ViewportHandle* handle);
|
||||||
void layers_render(ViewportHandle* handle);
|
void layers_render(ViewportHandle* handle);
|
||||||
void layers_resize(ViewportHandle* handle, float width, float height, float scale);
|
void layers_resize(ViewportHandle* handle, float width, float height, float scale);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Application
|
|
||||||
x:Class="LayersShell.App"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
|
||||||
<Application.Resources>
|
|
||||||
<ResourceDictionary>
|
|
||||||
<ResourceDictionary.ThemeDictionaries>
|
|
||||||
<ResourceDictionary x:Key="Default">
|
|
||||||
<SolidColorBrush x:Key="AppBackground" Color="#0A0A0D"/>
|
|
||||||
</ResourceDictionary>
|
|
||||||
</ResourceDictionary.ThemeDictionaries>
|
|
||||||
</ResourceDictionary>
|
|
||||||
</Application.Resources>
|
|
||||||
</Application>
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
|
|
||||||
namespace LayersShell;
|
|
||||||
|
|
||||||
public partial class App : Application
|
|
||||||
{
|
|
||||||
private MainWindow? _window;
|
|
||||||
|
|
||||||
public App()
|
|
||||||
{
|
|
||||||
Log("App ctor enter");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
AppDomain.CurrentDomain.FirstChanceException += (_, e) =>
|
|
||||||
Log($"FirstChance: {e.Exception.GetType().Name}: {e.Exception.Message}");
|
|
||||||
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
|
|
||||||
Log($"UnhandledException: {e.ExceptionObject}");
|
|
||||||
UnhandledException += (_, e) =>
|
|
||||||
Log($"XAML UnhandledException: {e.Exception}");
|
|
||||||
InitializeComponent();
|
|
||||||
Log("App ctor ok");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log($"App ctor FAILED: {ex}");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
|
||||||
{
|
|
||||||
Log("OnLaunched enter");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_window = new MainWindow();
|
|
||||||
Log("MainWindow constructed");
|
|
||||||
_window.Activate();
|
|
||||||
Log("MainWindow activated");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log($"OnLaunched FAILED: {ex}");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void Log(string line)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var path = Path.Combine(Path.GetTempPath(), "layers-shell.log");
|
|
||||||
var text = DateTime.Now.ToString("HH:mm:ss.fff ") + line + Environment.NewLine;
|
|
||||||
using var fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read);
|
|
||||||
var bytes = System.Text.Encoding.UTF8.GetBytes(text);
|
|
||||||
fs.Write(bytes, 0, bytes.Length);
|
|
||||||
fs.Flush(flushToDisk: true);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace LayersShell;
|
|
||||||
|
|
||||||
internal static class LayersNative
|
|
||||||
{
|
|
||||||
private const string Dll = "layers_native";
|
|
||||||
|
|
||||||
private static bool _startedUp;
|
|
||||||
public static void StartupIfNeeded()
|
|
||||||
{
|
|
||||||
if (_startedUp) return;
|
|
||||||
_startedUp = true;
|
|
||||||
_ = layers_startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern int layers_startup();
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
|
||||||
public static extern void layers_set_plugin_root([MarshalAs(UnmanagedType.LPStr)] string path);
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern IntPtr layers_create_from_swap_chain_panel(IntPtr swapChainPanel, float width, float height, float scale);
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern void layers_destroy(IntPtr handle);
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern void layers_render(IntPtr handle);
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern void layers_resize(IntPtr handle, float width, float height, float scale);
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern void layers_mouse_move(IntPtr handle, float x, float y);
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern void layers_mouse_left(IntPtr handle);
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern void layers_mouse_button(IntPtr handle, float x, float y, uint button, [MarshalAs(UnmanagedType.I1)] bool pressed);
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern void layers_mouse_scroll(IntPtr handle, float x, float y, float dx, float dy);
|
|
||||||
|
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
|
||||||
public static extern void layers_key_event(IntPtr handle, uint named, [MarshalAs(UnmanagedType.LPStr)] string? utf8, uint mods, ushort nativeKeycode, [MarshalAs(UnmanagedType.I1)] bool pressed);
|
|
||||||
}
|
|
||||||
|
|
||||||
[System.Runtime.InteropServices.ComImport]
|
|
||||||
[System.Runtime.InteropServices.Guid("63aad0b8-7c24-40ff-85a8-640d944cc325")]
|
|
||||||
[System.Runtime.InteropServices.InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
||||||
public interface ISwapChainPanelNative
|
|
||||||
{
|
|
||||||
[PreserveSig]
|
|
||||||
int SetSwapChain(IntPtr swapChain);
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>WinExe</OutputType>
|
|
||||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
|
||||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
|
||||||
<RootNamespace>LayersShell</RootNamespace>
|
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
|
||||||
<Platforms>x64;ARM64</Platforms>
|
|
||||||
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
|
|
||||||
<UseWinUI>true</UseWinUI>
|
|
||||||
<EnableMsixTooling>true</EnableMsixTooling>
|
|
||||||
<WindowsPackageType>None</WindowsPackageType>
|
|
||||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
|
||||||
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<LangVersion>latest</LangVersion>
|
|
||||||
<PublishReadyToRun>true</PublishReadyToRun>
|
|
||||||
<AssemblyName>Layers</AssemblyName>
|
|
||||||
<EnableMrtResourceIndexer>false</EnableMrtResourceIndexer>
|
|
||||||
<GenerateAppxPriFile>false</GenerateAppxPriFile>
|
|
||||||
<ShouldComputeInputPris>false</ShouldComputeInputPris>
|
|
||||||
<ApplicationIcon Condition="Exists('Assets\app.ico')">Assets\app.ico</ApplicationIcon>
|
|
||||||
|
|
||||||
<LayersIconSvg>$(MSBuildProjectDirectory)\..\..\..\resources\Layers.svg</LayersIconSvg>
|
|
||||||
<LayersIconOut>$(MSBuildProjectDirectory)\Assets\app.ico</LayersIconOut>
|
|
||||||
<LayersIconTmp>$(IntermediateOutputPath)icon_tmp</LayersIconTmp>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.241114002" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<Target Name="GenerateAppIcon"
|
|
||||||
BeforeTargets="BeforeBuild"
|
|
||||||
Condition="Exists('$(LayersIconSvg)') and !Exists('$(LayersIconOut)')">
|
|
||||||
<MakeDir Directories="$(MSBuildProjectDirectory)\Assets;$(LayersIconTmp)" />
|
|
||||||
<Exec Command='rsvg-convert --width 16 --height 16 "$(LayersIconSvg)" -o "$(LayersIconTmp)\icon_16.png"' ContinueOnError="true" />
|
|
||||||
<Exec Command='rsvg-convert --width 24 --height 24 "$(LayersIconSvg)" -o "$(LayersIconTmp)\icon_24.png"' ContinueOnError="true" />
|
|
||||||
<Exec Command='rsvg-convert --width 32 --height 32 "$(LayersIconSvg)" -o "$(LayersIconTmp)\icon_32.png"' ContinueOnError="true" />
|
|
||||||
<Exec Command='rsvg-convert --width 48 --height 48 "$(LayersIconSvg)" -o "$(LayersIconTmp)\icon_48.png"' ContinueOnError="true" />
|
|
||||||
<Exec Command='rsvg-convert --width 64 --height 64 "$(LayersIconSvg)" -o "$(LayersIconTmp)\icon_64.png"' ContinueOnError="true" />
|
|
||||||
<Exec Command='rsvg-convert --width 128 --height 128 "$(LayersIconSvg)" -o "$(LayersIconTmp)\icon_128.png"' ContinueOnError="true" />
|
|
||||||
<Exec Command='rsvg-convert --width 256 --height 256 "$(LayersIconSvg)" -o "$(LayersIconTmp)\icon_256.png"' ContinueOnError="true" />
|
|
||||||
<Exec Command='magick "$(LayersIconTmp)\icon_16.png" "$(LayersIconTmp)\icon_24.png" "$(LayersIconTmp)\icon_32.png" "$(LayersIconTmp)\icon_48.png" "$(LayersIconTmp)\icon_64.png" "$(LayersIconTmp)\icon_128.png" "$(LayersIconTmp)\icon_256.png" "$(LayersIconOut)"'
|
|
||||||
ContinueOnError="true" />
|
|
||||||
<RemoveDir Directories="$(LayersIconTmp)" ContinueOnError="true" />
|
|
||||||
</Target>
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Window
|
|
||||||
x:Class="LayersShell.MainWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<SwapChainPanel
|
|
||||||
x:Name="RenderSurface"
|
|
||||||
SizeChanged="RenderSurface_SizeChanged"
|
|
||||||
PointerMoved="RenderSurface_PointerMoved"
|
|
||||||
PointerPressed="RenderSurface_PointerPressed"
|
|
||||||
PointerReleased="RenderSurface_PointerReleased"
|
|
||||||
PointerWheelChanged="RenderSurface_PointerWheel"
|
|
||||||
PointerEntered="RenderSurface_PointerEntered"
|
|
||||||
PointerExited="RenderSurface_PointerExited"
|
|
||||||
KeyDown="RenderSurface_KeyDown"
|
|
||||||
KeyUp="RenderSurface_KeyUp"/>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
|
|
@ -1,288 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Microsoft.UI;
|
|
||||||
using Microsoft.UI.Composition.SystemBackdrops;
|
|
||||||
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
|
|
||||||
{
|
|
||||||
if (MicaController.IsSupported())
|
|
||||||
{
|
|
||||||
SystemBackdrop = new MicaBackdrop { Kind = Microsoft.UI.Composition.SystemBackdrops.MicaKind.Base };
|
|
||||||
App.Log("SystemBackdrop assigned");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
App.Log("Mica not supported on this machine; skipping backdrop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) { App.Log($"SystemBackdrop FAILED: {ex}"); }
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using Microsoft.UI.Dispatching;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
|
|
||||||
namespace LayersShell;
|
|
||||||
|
|
||||||
public static class Program
|
|
||||||
{
|
|
||||||
[STAThread]
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
App.Log($"Main enter (args={args.Length})");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
WinRT.ComWrappersSupport.InitializeComWrappers();
|
|
||||||
App.Log("ComWrappers initialised");
|
|
||||||
Application.Start(p =>
|
|
||||||
{
|
|
||||||
App.Log("Application.Start callback enter");
|
|
||||||
var ctx = new DispatcherQueueSynchronizationContext(DispatcherQueue.GetForCurrentThread());
|
|
||||||
SynchronizationContext.SetSynchronizationContext(ctx);
|
|
||||||
App.Log("SyncContext set — creating App");
|
|
||||||
var app = new App();
|
|
||||||
App.Log($"App instance created ({app.GetType().Name})");
|
|
||||||
});
|
|
||||||
App.Log("Application.Start returned");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
App.Log($"Main FAILED: {ex}");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
using System;
|
|
||||||
using Microsoft.UI.Input;
|
|
||||||
using Windows.System;
|
|
||||||
|
|
||||||
namespace LayersShell;
|
|
||||||
|
|
||||||
internal static class WinKeyMap
|
|
||||||
{
|
|
||||||
public static uint MapVirtualKey(VirtualKey key) => key switch
|
|
||||||
{
|
|
||||||
VirtualKey.Enter => 1,
|
|
||||||
VirtualKey.Escape => 2,
|
|
||||||
VirtualKey.Back => 3,
|
|
||||||
VirtualKey.Tab => 4,
|
|
||||||
VirtualKey.Left => 5,
|
|
||||||
VirtualKey.Right => 6,
|
|
||||||
VirtualKey.Up => 7,
|
|
||||||
VirtualKey.Down => 8,
|
|
||||||
VirtualKey.Delete => 9,
|
|
||||||
VirtualKey.Home => 10,
|
|
||||||
VirtualKey.End => 11,
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static string? TextForKey(VirtualKey key)
|
|
||||||
{
|
|
||||||
if (key >= VirtualKey.A && key <= VirtualKey.Z)
|
|
||||||
{
|
|
||||||
char c = (char)('a' + (int)(key - VirtualKey.A));
|
|
||||||
return c.ToString();
|
|
||||||
}
|
|
||||||
if (key >= VirtualKey.Number0 && key <= VirtualKey.Number9)
|
|
||||||
{
|
|
||||||
char c = (char)('0' + (int)(key - VirtualKey.Number0));
|
|
||||||
return c.ToString();
|
|
||||||
}
|
|
||||||
return key switch
|
|
||||||
{
|
|
||||||
VirtualKey.Space => " ",
|
|
||||||
_ => null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint CurrentModifiers()
|
|
||||||
{
|
|
||||||
uint m = 0;
|
|
||||||
if ((InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift) & Windows.UI.Core.CoreVirtualKeyStates.Down) == Windows.UI.Core.CoreVirtualKeyStates.Down) m |= 1;
|
|
||||||
if ((InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control) & Windows.UI.Core.CoreVirtualKeyStates.Down) == Windows.UI.Core.CoreVirtualKeyStates.Down) m |= 2;
|
|
||||||
if ((InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Menu) & Windows.UI.Core.CoreVirtualKeyStates.Down) == Windows.UI.Core.CoreVirtualKeyStates.Down) m |= 4;
|
|
||||||
if ((InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.LeftWindows) & Windows.UI.Core.CoreVirtualKeyStates.Down) == Windows.UI.Core.CoreVirtualKeyStates.Down) m |= 8;
|
|
||||||
if ((InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.RightWindows) & Windows.UI.Core.CoreVirtualKeyStates.Down) == Windows.UI.Core.CoreVirtualKeyStates.Down) m |= 8;
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<assemblyIdentity version="0.1.0.0" name="Layers.app"/>
|
|
||||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
|
||||||
<windowsSettings>
|
|
||||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
|
||||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
|
||||||
</windowsSettings>
|
|
||||||
</application>
|
|
||||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
|
||||||
<application>
|
|
||||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
|
||||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
|
||||||
</application>
|
|
||||||
</compatibility>
|
|
||||||
</assembly>
|
|
||||||
|
|
@ -75,6 +75,9 @@ impl ApplicationHandler for ShellApp {
|
||||||
.window_handle()
|
.window_handle()
|
||||||
.expect("winit: window handle")
|
.expect("winit: window handle")
|
||||||
.as_raw();
|
.as_raw();
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
apply_win11_chrome(&raw_window);
|
||||||
let raw_display = window
|
let raw_display = window
|
||||||
.display_handle()
|
.display_handle()
|
||||||
.expect("winit: display handle")
|
.expect("winit: display handle")
|
||||||
|
|
@ -245,6 +248,36 @@ fn init_logging() {
|
||||||
.try_init();
|
.try_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn apply_win11_chrome(raw_window: &raw_window_handle::RawWindowHandle) {
|
||||||
|
use raw_window_handle::RawWindowHandle;
|
||||||
|
use windows::Win32::Foundation::{BOOL, HWND};
|
||||||
|
use windows::Win32::Graphics::Dwm::{
|
||||||
|
DwmSetWindowAttribute, DWMSBT_MAINWINDOW, DWMWA_SYSTEMBACKDROP_TYPE,
|
||||||
|
DWMWA_USE_IMMERSIVE_DARK_MODE, DWM_SYSTEMBACKDROP_TYPE,
|
||||||
|
};
|
||||||
|
|
||||||
|
let RawWindowHandle::Win32(handle) = raw_window else { return };
|
||||||
|
let hwnd = HWND(handle.hwnd.get() as *mut _);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let dark: BOOL = BOOL(1);
|
||||||
|
let _ = DwmSetWindowAttribute(
|
||||||
|
hwnd,
|
||||||
|
DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||||
|
&dark as *const _ as _,
|
||||||
|
std::mem::size_of::<BOOL>() as u32,
|
||||||
|
);
|
||||||
|
let backdrop: DWM_SYSTEMBACKDROP_TYPE = DWMSBT_MAINWINDOW;
|
||||||
|
let _ = DwmSetWindowAttribute(
|
||||||
|
hwnd,
|
||||||
|
DWMWA_SYSTEMBACKDROP_TYPE,
|
||||||
|
&backdrop as *const _ as _,
|
||||||
|
std::mem::size_of::<DWM_SYSTEMBACKDROP_TYPE>() as u32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn discover_plugin_root() -> Option<PathBuf> {
|
fn discover_plugin_root() -> Option<PathBuf> {
|
||||||
if let Ok(p) = std::env::var("LAYERS_PLUGIN_ROOT") {
|
if let Ok(p) = std::env::var("LAYERS_PLUGIN_ROOT") {
|
||||||
let pb = PathBuf::from(p);
|
let pb = PathBuf::from(p);
|
||||||
|
|
|
||||||
28
src/ffi.rs
28
src/ffi.rs
|
|
@ -160,19 +160,6 @@ pub extern "C" fn layers_create(
|
||||||
Box::into_raw(Box::new(handle))
|
Box::into_raw(Box::new(handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn layers_create_from_swap_chain_panel(
|
|
||||||
swap_chain_panel: *mut c_void,
|
|
||||||
width: f32,
|
|
||||||
height: f32,
|
|
||||||
scale: f32,
|
|
||||||
) -> *mut ViewportHandle {
|
|
||||||
let Some(handle) = create_handle_from_swap_chain_panel(swap_chain_panel, width, height, scale) else {
|
|
||||||
return std::ptr::null_mut();
|
|
||||||
};
|
|
||||||
Box::into_raw(Box::new(handle))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn layers_destroy(handle: *mut ViewportHandle) {
|
pub extern "C" fn layers_destroy(handle: *mut ViewportHandle) {
|
||||||
|
|
@ -395,21 +382,6 @@ fn create_handle_from_raw(
|
||||||
finalise_handle(instance, surface, width, height, scale)
|
finalise_handle(instance, surface, width, height, scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
fn create_handle_from_swap_chain_panel(
|
|
||||||
swap_chain_panel: *mut c_void,
|
|
||||||
width: f32,
|
|
||||||
height: f32,
|
|
||||||
scale: f32,
|
|
||||||
) -> Option<ViewportHandle> {
|
|
||||||
let ptr = NonNull::new(swap_chain_panel)?;
|
|
||||||
let (instance, surface) = create_instance_and_surface(|instance| {
|
|
||||||
let target = wgpu::SurfaceTargetUnsafe::SwapChainPanel(ptr.as_ptr());
|
|
||||||
unsafe { instance.create_surface_unsafe(target).ok() }
|
|
||||||
})?;
|
|
||||||
finalise_handle(instance, surface, width, height, scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_instance_and_surface<F>(
|
fn create_instance_and_surface<F>(
|
||||||
build_surface: F,
|
build_surface: F,
|
||||||
) -> Option<(wgpu::Instance, wgpu::Surface<'static>)>
|
) -> Option<(wgpu::Instance, wgpu::Surface<'static>)>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue