diff options
Diffstat (limited to 'src/Godosa.Desktop/CursorWarp.cs')
| -rw-r--r-- | src/Godosa.Desktop/CursorWarp.cs | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/src/Godosa.Desktop/CursorWarp.cs b/src/Godosa.Desktop/CursorWarp.cs new file mode 100644 index 0000000..c44342d --- /dev/null +++ b/src/Godosa.Desktop/CursorWarp.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Controls; +using Godosa.Core.Diagnostics; + +namespace Godosa.Desktop; + +/// <summary> +/// Moves the OS pointer to a point in a visual (free look recentering, camera +/// spec ยง3: Avalonia 12.1.2 has no pointer lock). X11/XWayland via libX11 +/// (XWayland turns a warp under a hidden cursor into a pointer lock), Windows +/// via user32. Returns false when unsupported or on any failure. +/// </summary> +public static partial class CursorWarp +{ + private static IntPtr _display; + + public static bool TryWarp(Visual visual, Point viewPoint) + { + try + { + var topLevel = TopLevel.GetTopLevel(visual); + var handle = topLevel?.TryGetPlatformHandle(); + if (topLevel is null || handle is null) return false; + + if (OperatingSystem.IsWindows()) + { + var screen = visual.PointToScreen(viewPoint); + return SetCursorPos(screen.X, screen.Y); + } + if (OperatingSystem.IsLinux() && handle.HandleDescriptor == "XID") + { + if (visual.TranslatePoint(viewPoint, topLevel) is not { } p) return false; + if (_display == IntPtr.Zero) _display = XOpenDisplay(IntPtr.Zero); + if (_display == IntPtr.Zero) return false; + double scaling = topLevel.RenderScaling; + XWarpPointer(_display, 0, (nuint)handle.Handle, 0, 0, 0, 0, + (int)Math.Round(p.X * scaling), (int)Math.Round(p.Y * scaling)); + XFlush(_display); + return true; + } + if (EngineDebug.Input) EngineDebug.Write("input", $"cursor warp unsupported (handle {handle.HandleDescriptor})"); + return false; + } + catch (Exception ex) when (ex is DllNotFoundException or EntryPointNotFoundException) + { + if (EngineDebug.Input) EngineDebug.Write("input", $"cursor warp failed: {ex.Message}"); + return false; + } + } + + [LibraryImport("libX11.so.6")] + private static partial IntPtr XOpenDisplay(IntPtr displayName); + + [LibraryImport("libX11.so.6")] + private static partial int XWarpPointer(IntPtr display, nuint srcWindow, nuint destWindow, + int srcX, int srcY, uint srcWidth, uint srcHeight, int destX, int destY); + + [LibraryImport("libX11.so.6")] + private static partial int XFlush(IntPtr display); + + [LibraryImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool SetCursorPos(int x, int y); +} |
