godosa-engine

git clone https://git.godosa.eu/godosa-engine

master

raw · 2603 bytes

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);
}