From d5a8468469b9cb64c03303426fbe9be7e2c1f222 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:15:15 +0000 Subject: [PATCH 01/15] Add missing C# bindings for MaaFramework C API - Add MaaMacOSControllerCreate, MaaAndroidNativeControllerCreate, MaaReplayControllerCreate, MaaRecordControllerCreate P/Invoke - Add MaaControllerPostRelativeMove P/Invoke - Add MaaTaskerGetWaitFreezesDetail P/Invoke - Add MaaToolkitMacOS P/Invoke (CheckPermission, RequestPermission, RevealPermissionSettings) - Update MaaDbgControllerCreate to new 1-param signature - Add MaaMacOSController, MaaAndroidNativeController, MaaReplayController, MaaRecordController wrapper classes - Update MaaDbgController wrapper for new API - Add RelativeMove to IMaaController/MaaController - Add GetWaitFreezesDetail to IMaaTasker/MaaTasker - Add MacOSScreencapMethod, MacOSInputMethod, MacOSPermission enums - Add MaaWfId type alias - Add IMaaToolkitMacOS interface and implementation - Add Node.WaitFreezes messages to MaaMsg Agent-Logs-Url: https://github.com/MaaXYZ/MaaFramework.Binding.CSharp/sessions/201d767e-cec0-4888-b09f-8b7a74837c9a Co-authored-by: MistEO <18511905+MistEO@users.noreply.github.com> --- .../Framework/Instance/MaaController.cs | 45 +++++++++++++++- .../Interop/Framework/Instance/MaaTasker.cs | 4 ++ .../Interop/Framework/MaaDef.cs | 5 ++ .../Interop/Toolkit/MaaToolkitDef.cs | 1 + .../Interop/Toolkit/MacOS/MaaToolkitMacOS.cs | 30 +++++++++++ .../MaaController.cs | 7 +++ .../MaaAndroidNativeController.cs | 46 ++++++++++++++++ .../MaaController/MaaDbgController.cs | 24 +++------ .../MaaController/MaaMacOSController.cs | 53 +++++++++++++++++++ .../MaaController/MaaRecordController.cs | 51 ++++++++++++++++++ .../MaaController/MaaReplayController.cs | 46 ++++++++++++++++ src/MaaFramework.Binding.Native/MaaTasker.cs | 30 +++++++++++ src/MaaFramework.Binding.Native/MaaToolkit.cs | 28 ++++++++++ .../Enums/Controllers/MacOSInputMethod.cs | 24 +++++++++ .../Enums/Controllers/MacOSPermission.cs | 22 ++++++++ .../Enums/Controllers/MacOSScreencapMethod.cs | 22 ++++++++ src/MaaFramework.Binding/IMaaController.cs | 8 +++ src/MaaFramework.Binding/IMaaTasker.cs | 13 +++++ src/MaaFramework.Binding/IMaaToolkit.cs | 32 +++++++++++ src/MaaFramework.Binding/MaaDef.cs | 1 + src/MaaFramework.Binding/MaaMsg.cs | 35 ++++++++++++ 21 files changed, 509 insertions(+), 18 deletions(-) create mode 100644 src/MaaFramework.Binding.Native/Interop/Toolkit/MacOS/MaaToolkitMacOS.cs create mode 100644 src/MaaFramework.Binding.Native/MaaController/MaaAndroidNativeController.cs create mode 100644 src/MaaFramework.Binding.Native/MaaController/MaaMacOSController.cs create mode 100644 src/MaaFramework.Binding.Native/MaaController/MaaRecordController.cs create mode 100644 src/MaaFramework.Binding.Native/MaaController/MaaReplayController.cs create mode 100644 src/MaaFramework.Binding/Enums/Controllers/MacOSInputMethod.cs create mode 100644 src/MaaFramework.Binding/Enums/Controllers/MacOSPermission.cs create mode 100644 src/MaaFramework.Binding/Enums/Controllers/MacOSScreencapMethod.cs diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs index e128ac7..23090fd 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs @@ -28,8 +28,48 @@ public static partial class MaaController [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaControllerHandle MaaCustomControllerCreate([MarshalUsing(typeof(MaaMarshaller))] Custom.IMaaCustomController controller, nint controllerArg); + /// + /// Create a debug controller that serves images from a directory. + /// + /// Path to a directory of images (or a single image file). + /// The controller handle, or nint.Zero on failure. + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] + public static partial MaaControllerHandle MaaDbgControllerCreate(string readPath); + + /// + /// Create a macOS controller for native macOS applications. + /// + /// The CGWindowID of the target window (0 for desktop). + /// macOS screencap method to use. + /// macOS input method to use. + /// The controller handle, or nint.Zero on failure. + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] + public static partial MaaControllerHandle MaaMacOSControllerCreate(uint windowId, MaaMacOSScreencapMethod screencapMethod, MaaMacOSInputMethod inputMethod); + + /// + /// Create an Android native controller. + /// + /// JSON config for the control unit. + /// The controller handle, or nint.Zero on failure. + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] + public static partial MaaControllerHandle MaaAndroidNativeControllerCreate(string configJson); + + /// + /// Create a replay controller that replays recorded operations. + /// + /// Path to the recording JSONL file. + /// The controller handle, or nint.Zero on failure. [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] - public static partial MaaControllerHandle MaaDbgControllerCreate(string readPath, string writePath, MaaDbgControllerType type, string config); + public static partial MaaControllerHandle MaaReplayControllerCreate(string recordingPath); + + /// + /// Create a record controller that wraps an existing controller and records all operations. + /// + /// The inner controller to forward all operations to. + /// Path to the recording JSONL file to write. + /// The record controller handle, or nint.Zero on failure. + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] + public static partial MaaControllerHandle MaaRecordControllerCreate(MaaControllerHandle inner, string recordingPath); [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial void MaaControllerDestroy(MaaControllerHandle ctrl); @@ -87,6 +127,9 @@ public static partial class MaaController [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaCtrlId MaaControllerPostTouchUp(MaaControllerHandle ctrl, int contact); + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] + public static partial MaaCtrlId MaaControllerPostRelativeMove(MaaControllerHandle ctrl, int dx, int dy); + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaCtrlId MaaControllerPostKeyDown(MaaControllerHandle ctrl, int keycode); diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaTasker.cs b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaTasker.cs index 0046b3e..90ae9b2 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaTasker.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaTasker.cs @@ -109,6 +109,10 @@ public static partial class MaaTasker [return: MarshalAs(UnmanagedType.U1)] public static partial bool MaaTaskerGetNodeDetail(MaaTaskerHandle tasker, MaaNodeId nodeId, MaaStringBufferHandle nodeName, out MaaRecoId recoId, out MaaActId actionId, [MarshalAs(UnmanagedType.U1)] out bool completed); + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaTaskerGetWaitFreezesDetail(MaaTaskerHandle tasker, MaaWfId wfId, MaaStringBufferHandle nodeName, MaaStringBufferHandle phase, [MarshalAs(UnmanagedType.U1)] out bool success, out MaaSize elapsedMs, MaaRecoId[]? recoIdList, ref MaaSize recoIdListSize, MaaRectHandle roi); + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] [return: MarshalAs(UnmanagedType.U1)] public static partial bool MaaTaskerGetTaskDetail(MaaTaskerHandle tasker, MaaTaskId taskId, MaaStringBufferHandle entry, MaaNodeId[] nodeIdList, ref MaaSize nodeIdListSize, out MaaStatus status); diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs b/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs index 34a5784..0d8fadf 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs @@ -50,10 +50,15 @@ // No bitwise OR, just set it global using MaaDbgControllerType = System.UInt64; global using MaaControllerFeature = System.UInt64; +// macOS controller types +global using MaaMacOSScreencapMethod = System.UInt64; +global using MaaMacOSInputMethod = System.UInt64; // Gamepad types global using MaaGamepadType = System.UInt64; global using MaaGamepadButton = System.UInt64; global using MaaGamepadTouch = System.UInt64; +// WaitFreezes id +global using MaaWfId = System.Int64; global using MaaRectHandle = nint; using System.Runtime.InteropServices; diff --git a/src/MaaFramework.Binding.Native/Interop/Toolkit/MaaToolkitDef.cs b/src/MaaFramework.Binding.Native/Interop/Toolkit/MaaToolkitDef.cs index bc13a1a..0261a5c 100644 --- a/src/MaaFramework.Binding.Native/Interop/Toolkit/MaaToolkitDef.cs +++ b/src/MaaFramework.Binding.Native/Interop/Toolkit/MaaToolkitDef.cs @@ -14,6 +14,7 @@ global using MaaToolkitAdbDeviceListHandle = nint; global using MaaToolkitDesktopWindowHandle = nint; global using MaaToolkitDesktopWindowListHandle = nint; +global using MaaMacOSPermission = System.Int32; namespace MaaFramework.Binding.Interop.Native; diff --git a/src/MaaFramework.Binding.Native/Interop/Toolkit/MacOS/MaaToolkitMacOS.cs b/src/MaaFramework.Binding.Native/Interop/Toolkit/MacOS/MaaToolkitMacOS.cs new file mode 100644 index 0000000..ffc34e1 --- /dev/null +++ b/src/MaaFramework.Binding.Native/Interop/Toolkit/MacOS/MaaToolkitMacOS.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记 +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + +using System.Runtime.InteropServices; + +namespace MaaFramework.Binding.Interop.Native; + +public static partial class MaaToolkitMacOS +{ + [LibraryImport("MaaToolkit")] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaToolkitMacOSCheckPermission(MaaMacOSPermission perm); + + [LibraryImport("MaaToolkit")] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaToolkitMacOSRequestPermission(MaaMacOSPermission perm); + + [LibraryImport("MaaToolkit")] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaToolkitMacOSRevealPermissionSettings(MaaMacOSPermission perm); +} diff --git a/src/MaaFramework.Binding.Native/MaaController.cs b/src/MaaFramework.Binding.Native/MaaController.cs index 47766e2..752e6ce 100644 --- a/src/MaaFramework.Binding.Native/MaaController.cs +++ b/src/MaaFramework.Binding.Native/MaaController.cs @@ -168,6 +168,13 @@ public MaaJob TouchMove(int contact, int x, int y, int pressure = 1) public MaaJob TouchUp(int contact) => CreateJob(MaaControllerPostTouchUp(Handle, contact)); + /// + /// + /// Wrapper of . + /// + public MaaJob RelativeMove(int dx, int dy) + => CreateJob(MaaControllerPostRelativeMove(Handle, dx, dy)); + /// /// /// Wrapper of . diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaAndroidNativeController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaAndroidNativeController.cs new file mode 100644 index 0000000..728d087 --- /dev/null +++ b/src/MaaFramework.Binding.Native/MaaController/MaaAndroidNativeController.cs @@ -0,0 +1,46 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using static MaaFramework.Binding.Interop.Native.MaaController; + +namespace MaaFramework.Binding; + +/// +/// A wrapper class providing a reference implementation for . +/// +[DebuggerDisplay("{DebuggerDisplay,nq}")] +public class MaaAndroidNativeController : MaaController +{ + private readonly string _debugConfigJson; + + [ExcludeFromCodeCoverage(Justification = "Debugger display.")] + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => IsInvalid + ? $"Invalid {GetType().Name}" + : $"{GetType().Name} {{ ConfigJson = {_debugConfigJson} }}"; + + /// + /// Creates a instance. + /// + /// JSON config for the control unit. + /// Executes if ; otherwise, not link. + /// Checks LinkStart().Wait() status if ; otherwise, not check. + /// + /// Wrapper of . + /// This controller is only available on Android. + /// + /// + /// + public MaaAndroidNativeController([StringSyntax("Json")] string configJson, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded) + { + ArgumentException.ThrowIfNullOrEmpty(configJson); + + var handle = MaaAndroidNativeControllerCreate(configJson); + _ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller); + SetHandle(handle, needReleased: true); + + _debugConfigJson = configJson; + + if (link == LinkOption.Start) + LinkStartOnConstructed(check, configJson); + } +} diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaDbgController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaDbgController.cs index 274910a..41e91a2 100644 --- a/src/MaaFramework.Binding.Native/MaaController/MaaDbgController.cs +++ b/src/MaaFramework.Binding.Native/MaaController/MaaDbgController.cs @@ -11,47 +11,37 @@ namespace MaaFramework.Binding; public class MaaDbgController : MaaController { private readonly string _debugReadPath; - private readonly string _debugWritePath; - private readonly DbgControllerType _debugType; - private readonly string _debugConfig; [ExcludeFromCodeCoverage(Justification = "Debugger display.")] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => IsInvalid ? $"Invalid {GetType().Name}" - : $"{GetType().Name} {{ ReadFrom = {_debugReadPath}, WriteTo = {_debugWritePath}, Type = {_debugType}, Config = {_debugConfig} }}"; + : $"{GetType().Name} {{ ReadFrom = {_debugReadPath} }}"; /// /// Creates a instance. /// - /// The read path. - /// The write path. - /// The DebugControllerType. - /// The config. + /// Path to a directory of images (or a single image file). /// Executes if ; otherwise, not link. /// Checks LinkStart().Wait() status if ; otherwise, not check. /// /// Wrapper of . + /// Images are loaded on connect and cycled through on each screencap request. + /// All input operations (click, swipe, etc.) are no-ops that return success. /// /// /// - public MaaDbgController(string readPath, string writePath, DbgControllerType type, [StringSyntax("Json")] string config = "{}", LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded) + public MaaDbgController(string readPath, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded) { ArgumentException.ThrowIfNullOrEmpty(readPath); - ArgumentException.ThrowIfNullOrEmpty(writePath); - if (type == DbgControllerType.None) throw new ArgumentException($"Value cannot be {DbgControllerType.None}.", nameof(type)); - ArgumentException.ThrowIfNullOrEmpty(config); - var handle = MaaDbgControllerCreate(readPath, writePath, (MaaDbgControllerType)type, config); + var handle = MaaDbgControllerCreate(readPath); _ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller); SetHandle(handle, needReleased: true); _debugReadPath = readPath; - _debugWritePath = writePath; - _debugType = type; - _debugConfig = config; if (link == LinkOption.Start) - LinkStartOnConstructed(check, readPath, writePath, type, config); + LinkStartOnConstructed(check, readPath); } } diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaMacOSController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaMacOSController.cs new file mode 100644 index 0000000..0ecc125 --- /dev/null +++ b/src/MaaFramework.Binding.Native/MaaController/MaaMacOSController.cs @@ -0,0 +1,53 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using static MaaFramework.Binding.Interop.Native.MaaController; + +namespace MaaFramework.Binding; + +/// +/// A wrapper class providing a reference implementation for . +/// +[DebuggerDisplay("{DebuggerDisplay,nq}")] +public class MaaMacOSController : MaaController +{ + private readonly uint _debugWindowId; + private readonly MacOSScreencapMethod _debugScreencapMethod; + private readonly MacOSInputMethod _debugInputMethod; + + [ExcludeFromCodeCoverage(Justification = "Debugger display.")] + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => IsInvalid + ? $"Invalid {GetType().Name}" + : $"{GetType().Name} {{ WindowId = {_debugWindowId}, ScreencapMethod = {_debugScreencapMethod}, InputMethod = {_debugInputMethod} }}"; + + /// + /// Creates a instance. + /// + /// The CGWindowID of the target window (0 for desktop). + /// macOS screencap method to use. + /// macOS input method to use. + /// Executes if ; otherwise, not link. + /// Checks LinkStart().Wait() status if ; otherwise, not check. + /// + /// Wrapper of . + /// This controller is designed for native macOS applications. + /// Requires Screen Recording permission for screencap. + /// Input simulation requires Accessibility permission. + /// Some features are not supported: start_app, stop_app, scroll. + /// Only single touch is supported (contact must be 0). + /// + /// + public MaaMacOSController(uint windowId, MacOSScreencapMethod screencapMethod, MacOSInputMethod inputMethod, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded) + { + var handle = MaaMacOSControllerCreate(windowId, (MaaMacOSScreencapMethod)screencapMethod, (MaaMacOSInputMethod)inputMethod); + _ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller); + SetHandle(handle, needReleased: true); + + _debugWindowId = windowId; + _debugScreencapMethod = screencapMethod; + _debugInputMethod = inputMethod; + + if (link == LinkOption.Start) + LinkStartOnConstructed(check, windowId, screencapMethod, inputMethod); + } +} diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaRecordController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaRecordController.cs new file mode 100644 index 0000000..9e38ecd --- /dev/null +++ b/src/MaaFramework.Binding.Native/MaaController/MaaRecordController.cs @@ -0,0 +1,51 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using static MaaFramework.Binding.Interop.Native.MaaController; + +namespace MaaFramework.Binding; + +/// +/// A wrapper class providing a reference implementation for . +/// +[DebuggerDisplay("{DebuggerDisplay,nq}")] +public class MaaRecordController : MaaController +{ + private readonly string _debugRecordingPath; + + [ExcludeFromCodeCoverage(Justification = "Debugger display.")] + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => IsInvalid + ? $"Invalid {GetType().Name}" + : $"{GetType().Name} {{ RecordingPath = {_debugRecordingPath} }}"; + + /// + /// Creates a instance. + /// + /// The inner controller to forward all operations to. Must not be null. + /// Path to the recording JSONL file to write. + /// Executes if ; otherwise, not link. + /// Checks LinkStart().Wait() status if ; otherwise, not check. + /// + /// Wrapper of . + /// The record controller does NOT take ownership of the inner controller. + /// Screenshot images will be saved to a "{stem}-Screenshot" folder in the same directory as this file. + /// The recorded file can be replayed using . + /// + /// + /// + /// + public MaaRecordController(MaaController inner, string recordingPath, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded) + { + ArgumentNullException.ThrowIfNull(inner); + ArgumentException.ThrowIfNullOrEmpty(recordingPath); + + var handle = MaaRecordControllerCreate(inner.Handle, recordingPath); + _ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller); + SetHandle(handle, needReleased: true); + + _debugRecordingPath = recordingPath; + + if (link == LinkOption.Start) + LinkStartOnConstructed(check, inner, recordingPath); + } +} diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaReplayController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaReplayController.cs new file mode 100644 index 0000000..4755ed7 --- /dev/null +++ b/src/MaaFramework.Binding.Native/MaaController/MaaReplayController.cs @@ -0,0 +1,46 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using static MaaFramework.Binding.Interop.Native.MaaController; + +namespace MaaFramework.Binding; + +/// +/// A wrapper class providing a reference implementation for . +/// +[DebuggerDisplay("{DebuggerDisplay,nq}")] +public class MaaReplayController : MaaController +{ + private readonly string _debugRecordingPath; + + [ExcludeFromCodeCoverage(Justification = "Debugger display.")] + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => IsInvalid + ? $"Invalid {GetType().Name}" + : $"{GetType().Name} {{ RecordingPath = {_debugRecordingPath} }}"; + + /// + /// Creates a instance. + /// + /// Path to the recording JSONL file written by . + /// Executes if ; otherwise, not link. + /// Checks LinkStart().Wait() status if ; otherwise, not check. + /// + /// Wrapper of . + /// Screenshot image paths in the file are resolved relative to this file's parent directory. + /// + /// + /// + public MaaReplayController(string recordingPath, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded) + { + ArgumentException.ThrowIfNullOrEmpty(recordingPath); + + var handle = MaaReplayControllerCreate(recordingPath); + _ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller); + SetHandle(handle, needReleased: true); + + _debugRecordingPath = recordingPath; + + if (link == LinkOption.Start) + LinkStartOnConstructed(check, recordingPath); + } +} diff --git a/src/MaaFramework.Binding.Native/MaaTasker.cs b/src/MaaFramework.Binding.Native/MaaTasker.cs index b6e11e9..532851e 100644 --- a/src/MaaFramework.Binding.Native/MaaTasker.cs +++ b/src/MaaFramework.Binding.Native/MaaTasker.cs @@ -407,6 +407,36 @@ public bool GetActionDetail(MaaActId actionId, out string nodeName, out string a return ret; } + /// + public bool GetWaitFreezesDetail(MaaWfId wfId, out string nodeName, out string phase, out bool isSucceeded, out ulong elapsedMs, out MaaRecoId[] recoIdList, IMaaRectBuffer? roi) + => GetWaitFreezesDetail(wfId, out nodeName, out phase, out isSucceeded, out elapsedMs, out recoIdList, (MaaRectBuffer?)roi); + + /// + /// + /// Wrapper of . + /// + public bool GetWaitFreezesDetail(MaaWfId wfId, out string nodeName, out string phase, out bool isSucceeded, out ulong elapsedMs, out MaaRecoId[] recoIdList, MaaRectBuffer? roi) + { + var roiHandle = roi?.Handle ?? MaaRectHandle.Zero; + MaaSize recoIdListSize = 0; + + using var nodeNameBuffer = new MaaStringBuffer(); + using var phaseBuffer = new MaaStringBuffer(); + + if (!MaaTaskerGetWaitFreezesDetail(Handle, wfId, nodeNameBuffer.Handle, phaseBuffer.Handle, out isSucceeded, out elapsedMs, null, ref recoIdListSize, roiHandle)) + { + nodeName = string.Empty; + phase = string.Empty; + recoIdList = []; + return false; + } + + nodeName = nodeNameBuffer.ToString(); + phase = phaseBuffer.ToString(); + recoIdList = recoIdListSize == 0 ? [] : new MaaRecoId[recoIdListSize]; + return MaaTaskerGetWaitFreezesDetail(Handle, wfId, nodeNameBuffer.Handle, phaseBuffer.Handle, out isSucceeded, out elapsedMs, recoIdList, ref recoIdListSize, roiHandle); + } + /// /// /// Wrapper of . diff --git a/src/MaaFramework.Binding.Native/MaaToolkit.cs b/src/MaaFramework.Binding.Native/MaaToolkit.cs index 74c0c80..734c452 100644 --- a/src/MaaFramework.Binding.Native/MaaToolkit.cs +++ b/src/MaaFramework.Binding.Native/MaaToolkit.cs @@ -38,6 +38,9 @@ public MaaToolkit(bool init = false, string userPath = nameof(Environment.Curren /// public IMaaToolkitDesktop Desktop { get; } = new DesktopClass(); + /// + public IMaaToolkitMacOS MacOS { get; } = new MacOSClass(); + /// protected internal class ConfigClass : IMaaToolkitConfig { @@ -101,4 +104,29 @@ public IMaaListBuffer Find() return list; } } + + /// + protected internal class MacOSClass : IMaaToolkitMacOS + { + /// + /// + /// Wrapper of . + /// + public bool CheckPermission(MacOSPermission permission) + => Interop.Native.MaaToolkitMacOS.MaaToolkitMacOSCheckPermission((MaaMacOSPermission)permission); + + /// + /// + /// Wrapper of . + /// + public bool RequestPermission(MacOSPermission permission) + => Interop.Native.MaaToolkitMacOS.MaaToolkitMacOSRequestPermission((MaaMacOSPermission)permission); + + /// + /// + /// Wrapper of . + /// + public bool RevealPermissionSettings(MacOSPermission permission) + => Interop.Native.MaaToolkitMacOS.MaaToolkitMacOSRevealPermissionSettings((MaaMacOSPermission)permission); + } } diff --git a/src/MaaFramework.Binding/Enums/Controllers/MacOSInputMethod.cs b/src/MaaFramework.Binding/Enums/Controllers/MacOSInputMethod.cs new file mode 100644 index 0000000..6687e16 --- /dev/null +++ b/src/MaaFramework.Binding/Enums/Controllers/MacOSInputMethod.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记 +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + +namespace MaaFramework.Binding; + +/// +/// macOS input method. +/// +[System.Flags] +public enum MacOSInputMethod : System.UInt64 +{ + None = 0, + GlobalEvent = 1, + PostToPid = (1 << 1), +} diff --git a/src/MaaFramework.Binding/Enums/Controllers/MacOSPermission.cs b/src/MaaFramework.Binding/Enums/Controllers/MacOSPermission.cs new file mode 100644 index 0000000..6c6010c --- /dev/null +++ b/src/MaaFramework.Binding/Enums/Controllers/MacOSPermission.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记 +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + +namespace MaaFramework.Binding; + +/// +/// macOS permission types. +/// +public enum MacOSPermission : System.Int32 +{ + ScreenCapture = 1, + Accessibility = 2, +} diff --git a/src/MaaFramework.Binding/Enums/Controllers/MacOSScreencapMethod.cs b/src/MaaFramework.Binding/Enums/Controllers/MacOSScreencapMethod.cs new file mode 100644 index 0000000..9764168 --- /dev/null +++ b/src/MaaFramework.Binding/Enums/Controllers/MacOSScreencapMethod.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记 +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + +namespace MaaFramework.Binding; + +/// +/// macOS screencap method. +/// +public enum MacOSScreencapMethod : System.UInt64 +{ + None = 0, + ScreenCaptureKit = 1, +} diff --git a/src/MaaFramework.Binding/IMaaController.cs b/src/MaaFramework.Binding/IMaaController.cs index 0f11c6b..57ca357 100644 --- a/src/MaaFramework.Binding/IMaaController.cs +++ b/src/MaaFramework.Binding/IMaaController.cs @@ -120,6 +120,14 @@ public interface IMaaController : IMaaCommon, IMaaOption, IMaa /// MaaJob TouchUp(int contact); + /// + /// Moves the mouse/pointer relative to its current position. + /// + /// The relative horizontal offset. + /// The relative vertical offset. + /// A relative move . + MaaJob RelativeMove(int dx, int dy); + /// /// Usage: KeyDown -> KeyUp. /// For adb controller, is from android key event. diff --git a/src/MaaFramework.Binding/IMaaTasker.cs b/src/MaaFramework.Binding/IMaaTasker.cs index 4b282b7..fd03497 100644 --- a/src/MaaFramework.Binding/IMaaTasker.cs +++ b/src/MaaFramework.Binding/IMaaTasker.cs @@ -154,6 +154,19 @@ bool GetRecognitionDetail(MaaRecoId recognitionId, out string nodeName, out s /// if query was successful; otherwise, . bool GetActionDetail(MaaActId actionId, out string nodeName, out string action, IMaaRectBuffer? box, out bool isSucceeded, out string detailJson); + /// + /// Gets the wait freezes detail. + /// + /// The wait freezes id. + /// The node name. + /// The phase (e.g. "pre", "post", "repeat", "context"). + /// A value indicating whether the wait freezes is succeeded. + /// The elapsed time in milliseconds. + /// The recognition id list. + /// The region of interest. + /// if query was successful; otherwise, . + bool GetWaitFreezesDetail(MaaWfId wfId, out string nodeName, out string phase, out bool isSucceeded, out ulong elapsedMs, out MaaRecoId[] recoIdList, IMaaRectBuffer? roi); + /// /// Gets the node detail. /// diff --git a/src/MaaFramework.Binding/IMaaToolkit.cs b/src/MaaFramework.Binding/IMaaToolkit.cs index 4ba0ecd..6130b85 100644 --- a/src/MaaFramework.Binding/IMaaToolkit.cs +++ b/src/MaaFramework.Binding/IMaaToolkit.cs @@ -22,6 +22,11 @@ public interface IMaaToolkit /// Gets the MaaToolkit Desktop. /// IMaaToolkitDesktop Desktop { get; } + + /// + /// Gets the MaaToolkit macOS helper. + /// + IMaaToolkitMacOS MacOS { get; } } /// @@ -89,3 +94,30 @@ public interface IMaaToolkitDesktopWindow /// IMaaListBuffer Find(); } + +/// +/// An interface defining wrapped members for MaaToolkit macOS. +/// +public interface IMaaToolkitMacOS +{ + /// + /// Checks if the specified macOS permission is granted. + /// + /// The permission to check. + /// if the permission is granted; otherwise, . + bool CheckPermission(MacOSPermission permission); + + /// + /// Requests the specified macOS permission. + /// + /// The permission to request. + /// if the permission was granted; otherwise, . + bool RequestPermission(MacOSPermission permission); + + /// + /// Reveals the system permission settings for the specified macOS permission. + /// + /// The permission to reveal settings for. + /// if the settings were revealed successfully; otherwise, . + bool RevealPermissionSettings(MacOSPermission permission); +} diff --git a/src/MaaFramework.Binding/MaaDef.cs b/src/MaaFramework.Binding/MaaDef.cs index 3883e8b..f6e38b4 100644 --- a/src/MaaFramework.Binding/MaaDef.cs +++ b/src/MaaFramework.Binding/MaaDef.cs @@ -4,6 +4,7 @@ global using MaaNodeId = System.Int64; global using MaaRecoId = System.Int64; global using MaaActId = System.Int64; +global using MaaWfId = System.Int64; global using MaaSize = System.UInt64; using System.Diagnostics.CodeAnalysis; diff --git a/src/MaaFramework.Binding/MaaMsg.cs b/src/MaaFramework.Binding/MaaMsg.cs index d1af735..824bbe1 100644 --- a/src/MaaFramework.Binding/MaaMsg.cs +++ b/src/MaaFramework.Binding/MaaMsg.cs @@ -346,5 +346,40 @@ public static class Action public const string Failed = "Node.Action.Failed"; } + public static class WaitFreezes + { + /// + /// Message for wait freezes. + /// + /// + /// details_json: { task_id: number, wf_id: number, name: string, phase: string, roi: [number, number, number, number], param: { time: number, threshold: number, method: number, rate_limit: number, timeout: number }, reco_ids?: number[], elapsed?: number, focus: any, } + /// + public const string Starting = "Node.WaitFreezes.Starting"; + + /// + /// Message for wait freezes. + /// + /// + /// details_json: { task_id: number, wf_id: number, name: string, phase: string, roi: [number, number, number, number], param: { time: number, threshold: number, method: number, rate_limit: number, timeout: number }, reco_ids?: number[], elapsed?: number, focus: any, } + /// + public const string Prefix = "Node.WaitFreezes"; + + /// + /// Message for wait freezes. + /// + /// + /// details_json: { task_id: number, wf_id: number, name: string, phase: string, roi: [number, number, number, number], param: { time: number, threshold: number, method: number, rate_limit: number, timeout: number }, reco_ids?: number[], elapsed?: number, focus: any, } + /// + public const string Succeeded = "Node.WaitFreezes.Succeeded"; + + /// + /// Message for wait freezes. + /// + /// + /// details_json: { task_id: number, wf_id: number, name: string, phase: string, roi: [number, number, number, number], param: { time: number, threshold: number, method: number, rate_limit: number, timeout: number }, reco_ids?: number[], elapsed?: number, focus: any, } + /// + public const string Failed = "Node.WaitFreezes.Failed"; + + } } } From 30cba442086603adccb956d24174d2804f4fc110 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Sat, 4 Jul 2026 17:33:26 +0800 Subject: [PATCH 02/15] feat(controller): add MaaControllerPostMouseMoveRelative (#1189) --- docs/articles/overview-of-wrapper-and-api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/articles/overview-of-wrapper-and-api.md b/docs/articles/overview-of-wrapper-and-api.md index bb65979..6ece007 100644 --- a/docs/articles/overview-of-wrapper-and-api.md +++ b/docs/articles/overview-of-wrapper-and-api.md @@ -104,6 +104,7 @@ IMaaDisposable Derived: | IMaaController.TouchDown() | `MaaControllerPostTouchDown` | | IMaaController.TouchMove() | `MaaControllerPostTouchMove` | | IMaaController.TouchUp() | `MaaControllerPostTouchUp` | +| IMaaController.RelativeMove() | `MaaControllerPostRelativeMove` | | IMaaController.Screencap() | `MaaControllerPostScreencap` | | IMaaController.Scroll() | `MaaControllerPostScroll` | | IMaaController.Inactive() | `MaaControllerPostInactive` | From cfb6610b6e1fd1223356b57a60c9d8768e8da2cf Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Sat, 4 Jul 2026 18:36:57 +0800 Subject: [PATCH 03/15] =?UTF-8?q?feat(win32):=20=E5=BA=9F=E5=BC=83=20`Post?= =?UTF-8?q?ThreadMessage`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaFramework.Binding/Enums/Controllers/Win32InputMethod.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/MaaFramework.Binding/Enums/Controllers/Win32InputMethod.cs b/src/MaaFramework.Binding/Enums/Controllers/Win32InputMethod.cs index 33cbfb5..22b8571 100644 --- a/src/MaaFramework.Binding/Enums/Controllers/Win32InputMethod.cs +++ b/src/MaaFramework.Binding/Enums/Controllers/Win32InputMethod.cs @@ -43,6 +43,7 @@ public enum Win32InputMethod : System.UInt64 SendMessage = (1 << 1), PostMessage = (1 << 2), LegacyEvent = (1 << 3), + [Obsolete("Deprecated from MaaFramework v5.9.0.")] PostThreadMessage = (1 << 4), SendMessageWithCursorPos = (1 << 5), PostMessageWithCursorPos = (1 << 6), From dd056005d21d610020b35c1b44462ca3b0e828ce Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Sat, 4 Jul 2026 18:44:33 +0800 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20=E5=AE=8C=E6=95=B4=E7=9A=84=20mac?= =?UTF-8?q?OS=20=E5=8E=9F=E7=94=9F=E6=88=AA=E5=9B=BE=E5=92=8C=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E4=BB=A5=E5=8F=8A=20MaaToolkit=20=E7=9A=84=20DesktopW?= =?UTF-8?q?indow=20=E6=94=AF=E6=8C=81=20(#1116)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/articles/overview-of-wrapper-and-api.md | 3 +++ .../Interop/Framework/Instance/MaaController.cs | 7 +++++++ .../MaaController/MaaMacOSController.cs | 4 ++-- .../Enums/Controllers/MacOSInputMethod.cs | 12 ++++++++++-- .../Enums/Controllers/MacOSScreencapMethod.cs | 10 +++++++++- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/docs/articles/overview-of-wrapper-and-api.md b/docs/articles/overview-of-wrapper-and-api.md index 6ece007..e317de4 100644 --- a/docs/articles/overview-of-wrapper-and-api.md +++ b/docs/articles/overview-of-wrapper-and-api.md @@ -26,6 +26,8 @@ | AdbInputMethods | `MaaAdbInputMethod` | | Win32ScreencapMethod | `MaaWin32ScreencapMethod` | | Win32InputMethod | `MaaWin32InputMethod` | +| MacOSScreencapMethod | `MaaMacOSScreencapMethod` | +| MacOSInputMethod | `MaaMacOSInputMethod` | | DbgControllerType | `MaaDbgControllerType` | | GamepadType | `MaaGamepadType` | | GamepadButton | `MaaGamepadButton` | @@ -87,6 +89,7 @@ IMaaDisposable Derived: | --- | --- | | MaaAdbController.ctor() | `MaaAdbControllerCreate`
`MaaControllerAddSink` | | MaaWin32Controller.ctor() | `MaaWin32ControllerCreate`
`MaaControllerAddSink` | +| MaaMacOSController.ctor() | `MaaMacOSControllerCreate`
`MaaControllerAddSink` | | MaaCustomController.ctor() | `MaaCustomControllerCreate`
`MaaControllerAddSink` | | MaaDbgController.ctor() | `MaaDbgControllerCreate`
`MaaControllerAddSink` | | MaaPlayCoverController.ctor() | `MaaPlayCoverControllerCreate`
`MaaControllerAddSink` | diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs index 23090fd..21aa2f5 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs @@ -43,6 +43,13 @@ public static partial class MaaController /// macOS screencap method to use. /// macOS input method to use. /// The controller handle, or nint.Zero on failure. + /// + ///
This controller is designed for native macOS applications. + ///
Requires Screen Recording permission for screencap. + ///
Input simulation requires Accessibility permission. + ///
Some features are not supported: start_app, stop_app, scroll. + ///
Only single touch is supported (contact must be 0). + ///
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaControllerHandle MaaMacOSControllerCreate(uint windowId, MaaMacOSScreencapMethod screencapMethod, MaaMacOSInputMethod inputMethod); diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaMacOSController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaMacOSController.cs index 0ecc125..09e147c 100644 --- a/src/MaaFramework.Binding.Native/MaaController/MaaMacOSController.cs +++ b/src/MaaFramework.Binding.Native/MaaController/MaaMacOSController.cs @@ -24,8 +24,8 @@ public class MaaMacOSController : MaaController /// Creates a instance. ///
/// The CGWindowID of the target window (0 for desktop). - /// macOS screencap method to use. - /// macOS input method to use. + /// The macOS screencap method. + /// The macOS input method. /// Executes if ; otherwise, not link. /// Checks LinkStart().Wait() status if ; otherwise, not check. /// diff --git a/src/MaaFramework.Binding/Enums/Controllers/MacOSInputMethod.cs b/src/MaaFramework.Binding/Enums/Controllers/MacOSInputMethod.cs index 6687e16..7e419aa 100644 --- a/src/MaaFramework.Binding/Enums/Controllers/MacOSInputMethod.cs +++ b/src/MaaFramework.Binding/Enums/Controllers/MacOSInputMethod.cs @@ -13,9 +13,17 @@ namespace MaaFramework.Binding; /// -/// macOS input method. +/// MacOS input method. +/// +/// Select ONE method only. +/// +/// | Method | Description | +///
|-----------------|--------------------------------------------------------------------------------------------------------------------| +///
| GlobalEvent | Injects into the global HID event stream via CGEventPost(kCGHIDEventTap), dispatched by the OS to the front window | +///
| PostToPid | Directly send to target process using CGEventPostToPid | +///
+/// ///
-[System.Flags] public enum MacOSInputMethod : System.UInt64 { None = 0, diff --git a/src/MaaFramework.Binding/Enums/Controllers/MacOSScreencapMethod.cs b/src/MaaFramework.Binding/Enums/Controllers/MacOSScreencapMethod.cs index 9764168..ff02e26 100644 --- a/src/MaaFramework.Binding/Enums/Controllers/MacOSScreencapMethod.cs +++ b/src/MaaFramework.Binding/Enums/Controllers/MacOSScreencapMethod.cs @@ -13,7 +13,15 @@ namespace MaaFramework.Binding; /// -/// macOS screencap method. +/// MacOS screencap method. +/// +/// Select ONE method only. +/// +/// | Method | Description | +///
|-----------------|------------------------------------------------| +///
| ScreenCaptureKit| Modern macOS screencap using ScreenCaptureKit | +///
+/// ///
public enum MacOSScreencapMethod : System.UInt64 { From 335c2c8693160a4d52d0ba667665bbcb9dcb5df4 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Sat, 4 Jul 2026 22:23:12 +0800 Subject: [PATCH 05/15] feat: macos utils (#1214) --- docs/articles/overview-of-wrapper-and-api.md | 4 ++++ src/MaaFramework.Binding.Native/MaaToolkit.cs | 13 +++++++------ .../Enums/Controllers/MacOSPermission.cs | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/articles/overview-of-wrapper-and-api.md b/docs/articles/overview-of-wrapper-and-api.md index e317de4..30f6282 100644 --- a/docs/articles/overview-of-wrapper-and-api.md +++ b/docs/articles/overview-of-wrapper-and-api.md @@ -28,6 +28,7 @@ | Win32InputMethod | `MaaWin32InputMethod` | | MacOSScreencapMethod | `MaaMacOSScreencapMethod` | | MacOSInputMethod | `MaaMacOSInputMethod` | +| MacOSPermission | `MaaMacOSPermissionEnum` | | DbgControllerType | `MaaDbgControllerType` | | GamepadType | `MaaGamepadType` | | GamepadButton | `MaaGamepadButton` | @@ -316,6 +317,9 @@ IMaaDisposable Derived: | MaaToolkit.ctor()
IMaaToolkit.Config.InitOption() | `MaaToolkitConfigInitOption` | | IMaaToolkit.AdbDevice.Find()
IMaaToolkit.AdbDevice.FindAsync() | `MaaToolkitAdbDeviceFind`
`MaaToolkitAdbDeviceFindSpecified` | | IMaaToolkit.Desktop.Window.Find() | `MaaToolkitDesktopWindowFindAll` | +| IMaaToolkit.MacOS.CheckPermission() | `MaaToolkitMacOSCheckPermission` | +| IMaaToolkit.MacOS.RequestPermission() | `MaaToolkitMacOSRequestPermission` | +| IMaaToolkit.MacOS.RevealPermissionSettings() | `MaaToolkitMacOSRevealPermissionSettings` | ## Buffers.AdbDeviceListBuffer : Buffers.MaaListBuffer : Buffers.IMaaListBuffer diff --git a/src/MaaFramework.Binding.Native/MaaToolkit.cs b/src/MaaFramework.Binding.Native/MaaToolkit.cs index 734c452..65c0448 100644 --- a/src/MaaFramework.Binding.Native/MaaToolkit.cs +++ b/src/MaaFramework.Binding.Native/MaaToolkit.cs @@ -2,6 +2,7 @@ using MaaFramework.Binding.Interop.Native; using System.Diagnostics.CodeAnalysis; using static MaaFramework.Binding.Interop.Native.MaaToolkit; +using static MaaFramework.Binding.Interop.Native.MaaToolkitMacOS; namespace MaaFramework.Binding; @@ -110,23 +111,23 @@ protected internal class MacOSClass : IMaaToolkitMacOS { /// /// - /// Wrapper of . + /// Wrapper of . /// public bool CheckPermission(MacOSPermission permission) - => Interop.Native.MaaToolkitMacOS.MaaToolkitMacOSCheckPermission((MaaMacOSPermission)permission); + => MaaToolkitMacOSCheckPermission((MaaMacOSPermission)permission); /// /// - /// Wrapper of . + /// Wrapper of . /// public bool RequestPermission(MacOSPermission permission) - => Interop.Native.MaaToolkitMacOS.MaaToolkitMacOSRequestPermission((MaaMacOSPermission)permission); + => MaaToolkitMacOSRequestPermission((MaaMacOSPermission)permission); /// /// - /// Wrapper of . + /// Wrapper of . /// public bool RevealPermissionSettings(MacOSPermission permission) - => Interop.Native.MaaToolkitMacOS.MaaToolkitMacOSRevealPermissionSettings((MaaMacOSPermission)permission); + => MaaToolkitMacOSRevealPermissionSettings((MaaMacOSPermission)permission); } } diff --git a/src/MaaFramework.Binding/Enums/Controllers/MacOSPermission.cs b/src/MaaFramework.Binding/Enums/Controllers/MacOSPermission.cs index 6c6010c..7d48bf1 100644 --- a/src/MaaFramework.Binding/Enums/Controllers/MacOSPermission.cs +++ b/src/MaaFramework.Binding/Enums/Controllers/MacOSPermission.cs @@ -13,7 +13,7 @@ namespace MaaFramework.Binding; /// -/// macOS permission types. +/// MacOS permission types. /// public enum MacOSPermission : System.Int32 { From b07824d070d4793ec9267693fa5ffd087ab9ea4d Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Sat, 4 Jul 2026 22:35:05 +0800 Subject: [PATCH 06/15] feat: Record & Replay Controller (#1146) --- docs/articles/overview-of-wrapper-and-api.md | 3 +- .../Framework/Instance/MaaController.cs | 43 +++++++++++++------ .../Framework/Instance/MaaCustomController.cs | 15 +++++++ .../Interop/Framework/MaaDef.cs | 2 - .../MaaAgentClient.cs | 12 +++--- .../MaaController.cs | 4 +- .../MaaController/MaaDbgController.cs | 10 +++-- .../MaaController/MaaRecordController.cs | 18 +++++--- .../MaaController/MaaReplayController.cs | 8 ++-- .../Test_Custom.cs | 7 +++ .../Test_IMaaController.cs | 4 +- .../Custom/IMaaCustomController.cs | 6 +++ src/MaaFramework.Binding/IMaaController.cs | 6 +-- 13 files changed, 96 insertions(+), 42 deletions(-) diff --git a/docs/articles/overview-of-wrapper-and-api.md b/docs/articles/overview-of-wrapper-and-api.md index 30f6282..a11201b 100644 --- a/docs/articles/overview-of-wrapper-and-api.md +++ b/docs/articles/overview-of-wrapper-and-api.md @@ -29,7 +29,6 @@ | MacOSScreencapMethod | `MaaMacOSScreencapMethod` | | MacOSInputMethod | `MaaMacOSInputMethod` | | MacOSPermission | `MaaMacOSPermissionEnum` | -| DbgControllerType | `MaaDbgControllerType` | | GamepadType | `MaaGamepadType` | | GamepadButton | `MaaGamepadButton` | | GamepadTouch | `MaaGamepadTouch` | @@ -93,6 +92,8 @@ IMaaDisposable Derived: | MaaMacOSController.ctor() | `MaaMacOSControllerCreate`
`MaaControllerAddSink` | | MaaCustomController.ctor() | `MaaCustomControllerCreate`
`MaaControllerAddSink` | | MaaDbgController.ctor() | `MaaDbgControllerCreate`
`MaaControllerAddSink` | +| MaaReplayController.ctor() | `MaaReplayControllerCreate`
`MaaControllerAddSink` | +| MaaRecordController.ctor() | `MaaRecordControllerCreate`
`MaaControllerAddSink` | | MaaPlayCoverController.ctor() | `MaaPlayCoverControllerCreate`
`MaaControllerAddSink` | | MaaGamepadController.ctor() | `MaaGamepadControllerCreate`
`MaaControllerAddSink` | | MaaWlRootsController.ctor() | `MaaWlRootsControllerCreate`
`MaaControllerAddSink` | diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs index 21aa2f5..1d268f7 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs @@ -25,17 +25,6 @@ public static partial class MaaController [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaControllerHandle MaaWin32ControllerCreate(nint hWnd, MaaWin32ScreencapMethod screencapMethod, MaaWin32InputMethod mouseMethod, MaaWin32InputMethod keyboardMethod); - [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] - public static partial MaaControllerHandle MaaCustomControllerCreate([MarshalUsing(typeof(MaaMarshaller))] Custom.IMaaCustomController controller, nint controllerArg); - - /// - /// Create a debug controller that serves images from a directory. - /// - /// Path to a directory of images (or a single image file). - /// The controller handle, or nint.Zero on failure. - [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] - public static partial MaaControllerHandle MaaDbgControllerCreate(string readPath); - /// /// Create a macOS controller for native macOS applications. /// @@ -61,10 +50,28 @@ public static partial class MaaController [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaControllerHandle MaaAndroidNativeControllerCreate(string configJson); + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] + public static partial MaaControllerHandle MaaCustomControllerCreate([MarshalUsing(typeof(MaaMarshaller))] Custom.IMaaCustomController controller, nint controllerArg); + + /// + /// Create a debug controller that serves images from a directory. + /// + /// + /// Path to a directory of images (or a single image file). + /// Images are loaded on connect and cycled through on each screencap request. + /// All input operations (click, swipe, etc.) are no-ops that return success. + /// + /// The controller handle, or nint.Zero on failure. + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] + public static partial MaaControllerHandle MaaDbgControllerCreate(string readPath); + /// /// Create a replay controller that replays recorded operations. /// - /// Path to the recording JSONL file. + /// + /// Path to the recording JSONL file written by MaaRecordControllerCreate. + /// Screenshot image paths in the file are resolved relative to this file's parent directory. + /// /// The controller handle, or nint.Zero on failure. [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaControllerHandle MaaReplayControllerCreate(string recordingPath); @@ -72,8 +79,16 @@ public static partial class MaaController /// /// Create a record controller that wraps an existing controller and records all operations. /// - /// The inner controller to forward all operations to. - /// Path to the recording JSONL file to write. + /// + /// The inner controller to forward all operations to. Must not be null. + /// The record controller does NOT take ownership of the inner controller. + /// + /// + /// Path to the recording JSONL file to write. + /// Screenshot images will be saved to a "{stem}-Screenshot" folder + /// in the same directory as this file. + /// The recorded file can be replayed using MaaReplayControllerCreate. + /// /// The record controller handle, or nint.Zero on failure. [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaControllerHandle MaaRecordControllerCreate(MaaControllerHandle inner, string recordingPath); diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaCustomController.cs b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaCustomController.cs index db9981e..8b4c3c9 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaCustomController.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaCustomController.cs @@ -110,6 +110,8 @@ private sealed class Delegates(IMaaCustomController managed) public KeyDownDelegate KeyDown = (int keycode, nint transArg) => managed.KeyDown(keycode); public KeyUpDelegate KeyUp = (int keycode, nint transArg) => managed.KeyUp(keycode); public ScrollDelegate Scroll = (int dx, int dy, nint transArg) => managed.Scroll(dx, dy); + public RelativeMoveDelegate RelativeMove = (int dx, int dy, nint transArg) => managed.RelativeMove(dx, dy); + public ShellDelegate Shell = (string cmd, long timeout, nint transArg, MaaStringBufferHandle buffer) => managed.Shell(cmd, timeout, new MaaStringBuffer(buffer)); public InactiveDelegate Inactive = (nint transArg) => managed.Inactive(); public GetInfoDelegate GetInfo = (nint transArg, MaaStringBufferHandle buffer) => managed.GetInfo(new MaaStringBuffer(buffer)); }; @@ -141,6 +143,8 @@ private sealed class Unmanaged(Delegates delegates) public nint KeyDown = Marshal.GetFunctionPointerForDelegate(delegates.KeyDown); public nint KeyUp = Marshal.GetFunctionPointerForDelegate(delegates.KeyUp); public nint Scroll = Marshal.GetFunctionPointerForDelegate(delegates.Scroll); + public nint RelativeMove = Marshal.GetFunctionPointerForDelegate(delegates.RelativeMove); + public nint Shell = Marshal.GetFunctionPointerForDelegate(delegates.Shell); public nint Inactive = Marshal.GetFunctionPointerForDelegate(delegates.Inactive); public nint GetInfo = Marshal.GetFunctionPointerForDelegate(delegates.GetInfo); } @@ -218,6 +222,17 @@ private sealed class Unmanaged(Delegates delegates) [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool ScrollDelegate(int dx, int dy, nint transArg); + [return: MarshalAs(UnmanagedType.U1)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool RelativeMoveDelegate(int dx, int dy, nint transArg); + + /// + /// Write result to buffer. + /// + [return: MarshalAs(UnmanagedType.U1)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool ShellDelegate(string cmd, long timeout, nint transArg, MaaStringBufferHandle buffer); + [return: MarshalAs(UnmanagedType.U1)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool InactiveDelegate(nint transArg); diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs b/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs index 0d8fadf..54818ba 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs @@ -47,8 +47,6 @@ global using MaaWin32ScreencapMethod = System.UInt64; // No bitwise OR, just set it global using MaaWin32InputMethod = System.UInt64; -// No bitwise OR, just set it -global using MaaDbgControllerType = System.UInt64; global using MaaControllerFeature = System.UInt64; // macOS controller types global using MaaMacOSScreencapMethod = System.UInt64; diff --git a/src/MaaFramework.Binding.Native/MaaAgentClient.cs b/src/MaaFramework.Binding.Native/MaaAgentClient.cs index 878272e..c6ab526 100644 --- a/src/MaaFramework.Binding.Native/MaaAgentClient.cs +++ b/src/MaaFramework.Binding.Native/MaaAgentClient.cs @@ -13,14 +13,14 @@ namespace MaaFramework.Binding; [DebuggerDisplay("{DebuggerDisplay,nq}")] public class MaaAgentClient : MaaDisposableHandle, IMaaAgentClient { - private long _timeout = -1; + private long _millisecondsTimeout = -1; private Process? _agentServerProcess; [ExcludeFromCodeCoverage(Justification = "Debugger display.")] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => IsInvalid ? $"Invalid {GetType().Name}" - : $"{GetType().Name} {{ {nameof(Id)} = {Id ?? ""}, {nameof(IsConnected)} = {IsConnected}, {nameof(IsAlive)} = {IsAlive}, Timeout = {_timeout} }}"; + : $"{GetType().Name} {{ {nameof(Id)} = {Id ?? ""}, {nameof(IsConnected)} = {IsConnected}, {nameof(IsAlive)} = {IsAlive}, Timeout = {_millisecondsTimeout} }}"; internal sealed class NullAgentClient : MaaAgentClient { internal NullAgentClient() : base(MaaAgentClientHandle.Zero) { } } @@ -387,8 +387,8 @@ public bool LinkStop() ///
public bool SetTimeout(long millisecondsDelay) { - _timeout = millisecondsDelay; - return MaaAgentClientSetTimeout(Handle, _timeout); + _millisecondsTimeout = millisecondsDelay; + return MaaAgentClientSetTimeout(Handle, _millisecondsTimeout); } /// @@ -439,7 +439,7 @@ public bool CancelWith(CancellationToken cancellationToken, Func? waitFunc finally { ctr.Dispose(); - _ = SetTimeout(_timeout).ThrowIfFalse(); + _ = SetTimeout(_millisecondsTimeout).ThrowIfFalse(); } } @@ -459,7 +459,7 @@ public bool Cancel(Func? waitFunc = null, Task? waitTask = null, Maa } finally { - _ = SetTimeout(_timeout).ThrowIfFalse(); + _ = SetTimeout(_millisecondsTimeout).ThrowIfFalse(); } } diff --git a/src/MaaFramework.Binding.Native/MaaController.cs b/src/MaaFramework.Binding.Native/MaaController.cs index 752e6ce..8a6abe9 100644 --- a/src/MaaFramework.Binding.Native/MaaController.cs +++ b/src/MaaFramework.Binding.Native/MaaController.cs @@ -214,8 +214,8 @@ public MaaJob Inactive() /// /// Wrapper of . /// - public MaaJob Shell(string cmd, long timeout = 20000) - => CreateJob(MaaControllerPostShell(Handle, cmd, timeout)); + public MaaJob Shell(string cmd, long millisecondsTimeout = 20000) + => CreateJob(MaaControllerPostShell(Handle, cmd, millisecondsTimeout)); /// /// diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaDbgController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaDbgController.cs index 41e91a2..1a074df 100644 --- a/src/MaaFramework.Binding.Native/MaaController/MaaDbgController.cs +++ b/src/MaaFramework.Binding.Native/MaaController/MaaDbgController.cs @@ -19,15 +19,17 @@ public class MaaDbgController : MaaController : $"{GetType().Name} {{ ReadFrom = {_debugReadPath} }}"; /// - /// Creates a instance. + /// Creates a instance that serves images from a directory. /// - /// Path to a directory of images (or a single image file). + /// + /// Path to a directory of images (or a single image file). + /// Images are loaded on connect and cycled through on each screencap request. + /// All input operations (click, swipe, etc.) are no-ops that return success. + /// /// Executes if ; otherwise, not link. /// Checks LinkStart().Wait() status if ; otherwise, not check. /// /// Wrapper of . - /// Images are loaded on connect and cycled through on each screencap request. - /// All input operations (click, swipe, etc.) are no-ops that return success. /// /// /// diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaRecordController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaRecordController.cs index 9e38ecd..d6eac6d 100644 --- a/src/MaaFramework.Binding.Native/MaaController/MaaRecordController.cs +++ b/src/MaaFramework.Binding.Native/MaaController/MaaRecordController.cs @@ -10,7 +10,10 @@ namespace MaaFramework.Binding; [DebuggerDisplay("{DebuggerDisplay,nq}")] public class MaaRecordController : MaaController { +#pragma warning disable // 删除未读的私有成员 + private readonly MaaController _debugInner; private readonly string _debugRecordingPath; +#pragma warning restore // 删除未读的私有成员 [ExcludeFromCodeCoverage(Justification = "Debugger display.")] [DebuggerBrowsable(DebuggerBrowsableState.Never)] @@ -21,15 +24,19 @@ public class MaaRecordController : MaaController /// /// Creates a instance. /// - /// The inner controller to forward all operations to. Must not be null. - /// Path to the recording JSONL file to write. + /// + /// The inner controller to forward all operations to. Must not be null. + /// The record controller does NOT take ownership of the inner controller. + /// + /// + /// Path to the recording JSONL file to write. + /// Screenshot images will be saved to a "{stem}-Screenshot" folder in the same directory as this file. + /// The recorded file can be replayed using . + /// /// Executes if ; otherwise, not link. /// Checks LinkStart().Wait() status if ; otherwise, not check. /// /// Wrapper of . - /// The record controller does NOT take ownership of the inner controller. - /// Screenshot images will be saved to a "{stem}-Screenshot" folder in the same directory as this file. - /// The recorded file can be replayed using . /// /// /// @@ -43,6 +50,7 @@ public MaaRecordController(MaaController inner, string recordingPath, LinkOption _ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller); SetHandle(handle, needReleased: true); + _debugInner = inner; _debugRecordingPath = recordingPath; if (link == LinkOption.Start) diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaReplayController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaReplayController.cs index 4755ed7..86aa213 100644 --- a/src/MaaFramework.Binding.Native/MaaController/MaaReplayController.cs +++ b/src/MaaFramework.Binding.Native/MaaController/MaaReplayController.cs @@ -19,14 +19,16 @@ public class MaaReplayController : MaaController : $"{GetType().Name} {{ RecordingPath = {_debugRecordingPath} }}"; /// - /// Creates a instance. + /// Creates a instance that replays recorded operations. /// - /// Path to the recording JSONL file written by . + /// + /// Path to the recording JSONL file written by . + /// Screenshot image paths in the file are resolved relative to this file's parent directory. + /// /// Executes if ; otherwise, not link. /// Checks LinkStart().Wait() status if ; otherwise, not check. /// /// Wrapper of . - /// Screenshot image paths in the file are resolved relative to this file's parent directory. /// /// /// diff --git a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs index fa9569e..2cc9296 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs @@ -301,6 +301,13 @@ public bool KeyUp(int keycode) => c.KeyUp(keycode).Wait().IsSucceeded(); public bool Scroll(int dx, int dy) => c.Scroll(dx, dy).Wait().IsSucceeded(); + + public bool RelativeMove(int dx, int dy) + => c.RelativeMove(dx, dy).Wait().IsSucceeded(); + + public bool Shell(string cmd, long millisecondsTimeout, IMaaStringBuffer buffer) + => c.Shell(cmd, millisecondsTimeout).Wait().IsSucceeded() && c.GetShellOutput(out _); + public bool Inactive() => c.Inactive().Wait().IsSucceeded(); public bool GetInfo(IMaaStringBuffer buffer) diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs index 32171cd..fcd972d 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs @@ -365,11 +365,11 @@ public void Interface_Uuid(MaaTypes type, IMaaController maaController) [TestMethod] [MaaData(MaaTypes.All, nameof(Data), "echo hello", 20000)] - public void Interface_Shell_ShellOutput(MaaTypes type, IMaaController maaController, string cmd, long timeout) + public void Interface_Shell_ShellOutput(MaaTypes type, IMaaController maaController, string cmd, long millisecondsTimeout) { Assert.IsNotNull(maaController); - var job = maaController.Shell(cmd, timeout); + var job = maaController.Shell(cmd, millisecondsTimeout); if (maaController is MaaAdbController) { Interface_IMaaPost_Success(job); diff --git a/src/MaaFramework.Binding/Custom/IMaaCustomController.cs b/src/MaaFramework.Binding/Custom/IMaaCustomController.cs index 1948fee..c58e27a 100644 --- a/src/MaaFramework.Binding/Custom/IMaaCustomController.cs +++ b/src/MaaFramework.Binding/Custom/IMaaCustomController.cs @@ -35,6 +35,12 @@ public interface IMaaCustomController : IMaaCustom, IDisposable bool KeyDown(int keycode); bool KeyUp(int keycode); bool Scroll(int dx, int dy); + bool RelativeMove(int dx, int dy); + + /// + /// Write result to buffer. + /// + bool Shell(string cmd, long millisecondsTimeout, IMaaStringBuffer buffer); bool Inactive(); /// diff --git a/src/MaaFramework.Binding/IMaaController.cs b/src/MaaFramework.Binding/IMaaController.cs index 57ca357..3cbed50 100644 --- a/src/MaaFramework.Binding/IMaaController.cs +++ b/src/MaaFramework.Binding/IMaaController.cs @@ -177,13 +177,13 @@ public interface IMaaController : IMaaCommon, IMaaOption, IMaa /// Appends a job for shell command action. /// /// The shell command to execute. - /// The timeout in milliseconds. Default is 20000 (20 seconds). + /// The timeout in milliseconds. Default is 20000 (20 seconds). /// A shell . /// - /// This is only valid for ADB controllers. If the controller is not an ADB controller, the action will fail. + /// Supported by ADB controllers and custom controllers that implement the shell callback. /// See also . /// - MaaJob Shell(string cmd, long timeout = 20000); + MaaJob Shell(string cmd, long millisecondsTimeout = 20000); /// /// Gets the cached shell command output. From 2d85bf7899843afa888efe8f80905fc979ee8f76 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Mon, 6 Jul 2026 16:38:39 +0800 Subject: [PATCH 07/15] feat: android native support (#1222) --- docs/articles/overview-of-wrapper-and-api.md | 1 + .../Interop/Framework/Instance/MaaController.cs | 13 ++++++++++++- .../MaaController/MaaAndroidNativeController.cs | 11 ++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/articles/overview-of-wrapper-and-api.md b/docs/articles/overview-of-wrapper-and-api.md index a11201b..c1954aa 100644 --- a/docs/articles/overview-of-wrapper-and-api.md +++ b/docs/articles/overview-of-wrapper-and-api.md @@ -90,6 +90,7 @@ IMaaDisposable Derived: | MaaAdbController.ctor() | `MaaAdbControllerCreate`
`MaaControllerAddSink` | | MaaWin32Controller.ctor() | `MaaWin32ControllerCreate`
`MaaControllerAddSink` | | MaaMacOSController.ctor() | `MaaMacOSControllerCreate`
`MaaControllerAddSink` | +| MaaAndroidNativeController.ctor() | `MaaAndroidNativeControllerCreate`
`MaaControllerAddSink` | | MaaCustomController.ctor() | `MaaCustomControllerCreate`
`MaaControllerAddSink` | | MaaDbgController.ctor() | `MaaDbgControllerCreate`
`MaaControllerAddSink` | | MaaReplayController.ctor() | `MaaReplayControllerCreate`
`MaaControllerAddSink` | diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs index 1d268f7..f1454d4 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs @@ -45,8 +45,19 @@ public static partial class MaaController /// /// Create an Android native controller. /// - /// JSON config for the control unit. + /// + /// JSON config for the control unit. Required fields: + /// - library_path: path to the Android native control unit library + /// - screen_resolution.width / screen_resolution.height: raw screenshot and touch resolution + /// Optional fields: + /// - display_id: target display id, defaults to 0 + /// - force_stop: whether to force stop before start_app, defaults to false + /// /// The controller handle, or nint.Zero on failure. + /// + /// This controller is only available on Android. + /// The configured screen_resolution must match the control unit's raw screenshot/touch coordinate space. + /// [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaControllerHandle MaaAndroidNativeControllerCreate(string configJson); diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaAndroidNativeController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaAndroidNativeController.cs index 728d087..e437ecd 100644 --- a/src/MaaFramework.Binding.Native/MaaController/MaaAndroidNativeController.cs +++ b/src/MaaFramework.Binding.Native/MaaController/MaaAndroidNativeController.cs @@ -21,12 +21,21 @@ public class MaaAndroidNativeController : MaaController /// /// Creates a instance. /// - /// JSON config for the control unit. + /// + /// JSON config for the control unit. + /// Required fields: + ///
- library_path: path to the Android native control unit library + ///
- screen_resolution.width / screen_resolution.height: raw screenshot and touch resolution
+ /// Optional fields: + ///
- display_id: target display id, defaults to 0 + ///
- force_stop: whether to force stop before start_app, defaults to false
+ /// /// Executes if ; otherwise, not link. /// Checks LinkStart().Wait() status if ; otherwise, not check. /// /// Wrapper of . /// This controller is only available on Android. + /// The configured screen_resolution must match the control unit's raw screenshot/touch coordinate space. /// /// /// From 2503d2cae93f048c795c4c695797cb7691e1d737 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Mon, 6 Jul 2026 17:03:21 +0800 Subject: [PATCH 08/15] =?UTF-8?q?feat(win32):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=BC=A0=E6=A0=87=E9=94=81=E5=AE=9A=E8=B7=9F=E9=9A=8F=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E6=94=AF=E6=8C=81=E5=90=8E=E5=8F=B0=20TPS/FP?= =?UTF-8?q?S=20=E8=A7=86=E8=A7=92=E6=8E=A7=E5=88=B6=20(#1232)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaFramework.Binding.Native/MaaController.cs | 3 ++- .../Test_IMaaController.cs | 2 ++ .../Enums/Options/ControllerOption.cs | 10 ++++++++++ .../Extensions/MaaOptionExtensions.cs | 5 +++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/MaaFramework.Binding.Native/MaaController.cs b/src/MaaFramework.Binding.Native/MaaController.cs index 8a6abe9..804fd75 100644 --- a/src/MaaFramework.Binding.Native/MaaController.cs +++ b/src/MaaFramework.Binding.Native/MaaController.cs @@ -82,7 +82,8 @@ public bool SetOption(ControllerOption opt, T value) { (int vvvv, ControllerOption.ScreenshotTargetLongSide or ControllerOption.ScreenshotTargetShortSide) => vvvv.ToMaaOptionValue(), - (bool vvv, ControllerOption.ScreenshotUseRawSize) => vvv.ToMaaOptionValue(), + (bool vvv, ControllerOption.ScreenshotUseRawSize + or ControllerOption.MouseLockFollow) => vvv.ToMaaOptionValue(), _ => throw new NotSupportedException($"'{nameof(ControllerOption)}.{opt}' or type '{typeof(T)}' is not supported."), }; diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs index fcd972d..4743a56 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs @@ -144,6 +144,7 @@ public void CreateInstances() [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotTargetLongSide, 1280)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotTargetShortSide, 720)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotUseRawSize, false)] + [MaaData(MaaTypes.All, nameof(Data), ControllerOption.MouseLockFollow, false)] public void Interface_SetOption(MaaTypes type, IMaaController maaController, ControllerOption opt, object arg) { Assert.IsNotNull(maaController); @@ -400,6 +401,7 @@ public void Interface_Shell_ShellOutput(MaaTypes type, IMaaController maaControl [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotTargetLongSide, 0.0)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotTargetShortSide, 0.0)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotUseRawSize, 0.0)] + [MaaData(MaaTypes.All, nameof(Data), ControllerOption.MouseLockFollow, 0.0)] public void Interface_SetOption_InvalidData(MaaTypes type, IMaaController maaController, ControllerOption opt, object arg) { Assert.IsNotNull(maaController); diff --git a/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs b/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs index 20a0fea..2232c5c 100644 --- a/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs +++ b/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs @@ -39,5 +39,15 @@ public enum ControllerOption : System.Int32 /// value: bool, eg: true; val_size: sizeof(bool) /// ScreenshotUseRawSize = 3, + + /// + /// Enable or disable mouse-lock-follow mode for Win32 controllers. + /// + /// + /// This is designed for TPS/FPS games that lock the mouse to their window in the background. + /// Only valid for Win32 controllers using message-based input methods. + /// value: bool, eg: true; val_size: sizeof(bool) + /// + MouseLockFollow = 4, } diff --git a/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs b/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs index 28e4d28..b7d1aa6 100644 --- a/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs +++ b/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs @@ -25,6 +25,11 @@ public static bool SetOption_ScreenshotTargetShortSide(this IMaaOption? opt, bool value) => opt?.SetOption(ControllerOption.ScreenshotUseRawSize, value) ?? throw new ArgumentNullException(nameof(opt)); + /// + /// + public static bool SetOption_MouseLockFollow(this IMaaOption? opt, bool value) + => opt?.SetOption(ControllerOption.MouseLockFollow, value) ?? throw new ArgumentNullException(nameof(opt)); + /// /// public static bool SetOption_LogDir(this IMaaOption? opt, string value) From 89d64c7225626404242d03c7791c9562a2445abc Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Mon, 6 Jul 2026 17:17:10 +0800 Subject: [PATCH 09/15] feat: add optional anchor info for reco sink (#1187) --- .../Notification/NotificationDetail.cs | 13 +++++++------ src/MaaFramework.Binding/MaaMsg.cs | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/MaaFramework.Binding.Extensions/Notification/NotificationDetail.cs b/src/MaaFramework.Binding.Extensions/Notification/NotificationDetail.cs index eee2e60..5b2b491 100644 --- a/src/MaaFramework.Binding.Extensions/Notification/NotificationDetail.cs +++ b/src/MaaFramework.Binding.Extensions/Notification/NotificationDetail.cs @@ -33,7 +33,7 @@ public record PipelineNodeDetail( [property: JsonPropertyName("task_id")] int TaskId, [property: JsonPropertyName("node_id")] int NodeId, [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("focus")] JsonElement? Focus + [property: JsonPropertyName("focus")] JsonElement Focus ); /// @@ -41,7 +41,7 @@ public record RecognitionNodeDetail( [property: JsonPropertyName("task_id")] int TaskId, [property: JsonPropertyName("node_id")] int NodeId, [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("focus")] JsonElement? Focus + [property: JsonPropertyName("focus")] JsonElement Focus ); /// @@ -49,7 +49,7 @@ public record ActionNodeDetail( [property: JsonPropertyName("task_id")] int TaskId, [property: JsonPropertyName("node_id")] int NodeId, [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("focus")] JsonElement? Focus + [property: JsonPropertyName("focus")] JsonElement Focus ); /// @@ -66,7 +66,7 @@ public record NodeNextListDetail( [property: JsonPropertyName("task_id")] int TaskId, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("list")] IReadOnlyList NextList, - [property: JsonPropertyName("focus")] JsonElement? Focus + [property: JsonPropertyName("focus")] JsonElement Focus ); /// @@ -74,7 +74,8 @@ public record NodeRecognitionDetail( [property: JsonPropertyName("task_id")] int TaskId, [property: JsonPropertyName("reco_id")] int RecognitionId, [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("focus")] JsonElement? Focus + [property: JsonPropertyName("focus")] JsonElement Focus, + [property: JsonPropertyName("anchor")] string? Anchor ); /// @@ -82,5 +83,5 @@ public record NodeActionDetail( [property: JsonPropertyName("task_id")] int TaskId, [property: JsonPropertyName("action_id")] int ActionId, [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("focus")] JsonElement? Focus + [property: JsonPropertyName("focus")] JsonElement Focus ); diff --git a/src/MaaFramework.Binding/MaaMsg.cs b/src/MaaFramework.Binding/MaaMsg.cs index 824bbe1..2d877c5 100644 --- a/src/MaaFramework.Binding/MaaMsg.cs +++ b/src/MaaFramework.Binding/MaaMsg.cs @@ -282,7 +282,7 @@ public static class Recognition /// Message for the recognition list of node. /// /// - /// details_json: { task_id: number, reco_id: number, name: string, focus: any, } + /// details_json: { task_id: number, reco_id: number, name: string, focus: any, anchor?: string, } /// public const string Starting = "Node.Recognition.Starting"; @@ -290,7 +290,7 @@ public static class Recognition /// Message for the recognition list of node. ///
/// - /// details_json: { task_id: number, reco_id: number, name: string, focus: any, } + /// details_json: { task_id: number, reco_id: number, name: string, focus: any, anchor?: string, } /// public const string Prefix = "Node.Recognition"; @@ -298,7 +298,7 @@ public static class Recognition /// Message for the recognition list of node. /// /// - /// details_json: { task_id: number, reco_id: number, name: string, focus: any, } + /// details_json: { task_id: number, reco_id: number, name: string, focus: any, anchor?: string, } /// public const string Succeeded = "Node.Recognition.Succeeded"; @@ -306,7 +306,7 @@ public static class Recognition /// Message for the recognition list of node. /// /// - /// details_json: { task_id: number, reco_id: number, name: string, focus: any, } + /// details_json: { task_id: number, reco_id: number, name: string, focus: any, anchor?: string, } /// public const string Failed = "Node.Recognition.Failed"; From 068d9141506160f3db2aeefd57df219002826cca Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Mon, 6 Jul 2026 19:53:10 +0800 Subject: [PATCH 10/15] feat: controller ScreenshotResizeMethod (#1243) --- src/MaaFramework.Binding.Native/MaaController.cs | 3 ++- .../Test_IMaaController.cs | 2 ++ .../Enums/Options/ControllerOption.cs | 11 +++++++++++ .../Extensions/MaaOptionExtensions.cs | 5 +++++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/MaaFramework.Binding.Native/MaaController.cs b/src/MaaFramework.Binding.Native/MaaController.cs index 804fd75..4a7ea9b 100644 --- a/src/MaaFramework.Binding.Native/MaaController.cs +++ b/src/MaaFramework.Binding.Native/MaaController.cs @@ -81,7 +81,8 @@ public bool SetOption(ControllerOption opt, T value) var optValue = (value, opt) switch { (int vvvv, ControllerOption.ScreenshotTargetLongSide - or ControllerOption.ScreenshotTargetShortSide) => vvvv.ToMaaOptionValue(), + or ControllerOption.ScreenshotTargetShortSide + or ControllerOption.ScreenshotResizeMethod) => vvvv.ToMaaOptionValue(), (bool vvv, ControllerOption.ScreenshotUseRawSize or ControllerOption.MouseLockFollow) => vvv.ToMaaOptionValue(), diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs index 4743a56..42471b8 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs @@ -145,6 +145,7 @@ public void CreateInstances() [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotTargetShortSide, 720)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotUseRawSize, false)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.MouseLockFollow, false)] + [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotResizeMethod, 0)] public void Interface_SetOption(MaaTypes type, IMaaController maaController, ControllerOption opt, object arg) { Assert.IsNotNull(maaController); @@ -402,6 +403,7 @@ public void Interface_Shell_ShellOutput(MaaTypes type, IMaaController maaControl [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotTargetShortSide, 0.0)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotUseRawSize, 0.0)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.MouseLockFollow, 0.0)] + [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotResizeMethod, 0.0)] public void Interface_SetOption_InvalidData(MaaTypes type, IMaaController maaController, ControllerOption opt, object arg) { Assert.IsNotNull(maaController); diff --git a/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs b/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs index 2232c5c..950ee48 100644 --- a/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs +++ b/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs @@ -49,5 +49,16 @@ public enum ControllerOption : System.Int32 /// value: bool, eg: true; val_size: sizeof(bool) ///
MouseLockFollow = 4, + + /// + /// Set the interpolation method used when resizing screenshots. + /// + /// + /// Value corresponds to cv::InterpolationFlags: + /// INTER_NEAREST=0, INTER_LINEAR=1, INTER_CUBIC=2, INTER_AREA=3, INTER_LANCZOS4=4 + /// Default is INTER_AREA (3). + /// value: int, eg: 3; val_size: sizeof(int) + /// + ScreenshotResizeMethod = 6, } diff --git a/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs b/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs index b7d1aa6..1f81570 100644 --- a/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs +++ b/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs @@ -30,6 +30,11 @@ public static bool SetOption_ScreenshotUseRawSize(this IMaaOption? opt, bool value) => opt?.SetOption(ControllerOption.MouseLockFollow, value) ?? throw new ArgumentNullException(nameof(opt)); + /// + /// + public static bool SetOption_ScreenshotResizeMethod(this IMaaOption? opt, int value) + => opt?.SetOption(ControllerOption.ScreenshotResizeMethod, value) ?? throw new ArgumentNullException(nameof(opt)); + /// /// public static bool SetOption_LogDir(this IMaaOption? opt, string value) From 984211804be602f365febdedaef8736d8e6ace7c Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Mon, 6 Jul 2026 21:23:09 +0800 Subject: [PATCH 11/15] feat: wait_freezes msg (#1178) --- .../Notification/NotificationDetail.cs | 96 +++- .../Notification/NotificationDetailContext.cs | 3 +- .../Notification/NotificationHandler.cs | 24 +- .../NotificationHandlerRegistry.cs | 16 +- src/MaaFramework.Binding.Extensions/README.md | 6 +- .../README.zh_cn.md | 4 + .../Interop/Framework/MaaDef.cs | 3 +- src/MaaFramework.Binding.Native/MaaTasker.cs | 12 +- .../Test_Common.cs | 1 + src/MaaFramework.Binding/IMaaTasker.cs | 14 +- src/MaaFramework.Binding/MaaMsg.cs | 446 ++++++++---------- 11 files changed, 325 insertions(+), 300 deletions(-) diff --git a/src/MaaFramework.Binding.Extensions/Notification/NotificationDetail.cs b/src/MaaFramework.Binding.Extensions/Notification/NotificationDetail.cs index 5b2b491..32753bf 100644 --- a/src/MaaFramework.Binding.Extensions/Notification/NotificationDetail.cs +++ b/src/MaaFramework.Binding.Extensions/Notification/NotificationDetail.cs @@ -3,51 +3,69 @@ namespace MaaFramework.Binding.Notification; -/// +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + +/// +public enum ResourcePathType +{ + Bundle, + OcrModel, + Pipeline, + Image, + + Unknown = -1 +} + +/// public record ResourceLoadingDetail( - [property: JsonPropertyName("res_id")] int ResourceId, + [property: JsonPropertyName("res_id")] long ResourceId, [property: JsonPropertyName("path")] string Path, [property: JsonPropertyName("type")] string Type, [property: JsonPropertyName("hash")] string Hash -); +) +{ + // 1. .NET 7 没有支持 aot 的 JsonStringEnumConverter + // 2. Unknown 向前兼容,使用 JsonStringEnumConverter 还需要设置 option 并且将 Unknown 设置为默认值 0 + [JsonIgnore] public ResourcePathType ParsedType => Enum.TryParse(Type, true, out var result) ? result : ResourcePathType.Unknown; +}; -/// +/// public record ControllerActionDetail( - [property: JsonPropertyName("ctrl_id")] int ControllerId, + [property: JsonPropertyName("ctrl_id")] long ControllerId, [property: JsonPropertyName("uuid")] string Uuid, [property: JsonPropertyName("action")] string Action, [property: JsonPropertyName("param")] JsonElement Param, [property: JsonPropertyName("info")] JsonElement Info ); -/// +/// public record TaskerTaskDetail( - [property: JsonPropertyName("task_id")] int TaskId, + [property: JsonPropertyName("task_id")] long TaskId, [property: JsonPropertyName("entry")] string Entry, [property: JsonPropertyName("uuid")] string Uuid, [property: JsonPropertyName("hash")] string Hash ); -/// +/// public record PipelineNodeDetail( - [property: JsonPropertyName("task_id")] int TaskId, - [property: JsonPropertyName("node_id")] int NodeId, + [property: JsonPropertyName("task_id")] long TaskId, + [property: JsonPropertyName("node_id")] long NodeId, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("focus")] JsonElement Focus ); -/// +/// public record RecognitionNodeDetail( - [property: JsonPropertyName("task_id")] int TaskId, - [property: JsonPropertyName("node_id")] int NodeId, + [property: JsonPropertyName("task_id")] long TaskId, + [property: JsonPropertyName("node_id")] long NodeId, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("focus")] JsonElement Focus ); -/// +/// public record ActionNodeDetail( - [property: JsonPropertyName("task_id")] int TaskId, - [property: JsonPropertyName("node_id")] int NodeId, + [property: JsonPropertyName("task_id")] long TaskId, + [property: JsonPropertyName("node_id")] long NodeId, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("focus")] JsonElement Focus ); @@ -61,27 +79,57 @@ public record NodeAttr( [property: JsonPropertyName("anchor")] bool Anchor ); -/// +/// public record NodeNextListDetail( - [property: JsonPropertyName("task_id")] int TaskId, + [property: JsonPropertyName("task_id")] long TaskId, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("list")] IReadOnlyList NextList, [property: JsonPropertyName("focus")] JsonElement Focus ); -/// +/// public record NodeRecognitionDetail( - [property: JsonPropertyName("task_id")] int TaskId, - [property: JsonPropertyName("reco_id")] int RecognitionId, + [property: JsonPropertyName("task_id")] long TaskId, + [property: JsonPropertyName("reco_id")] long RecognitionId, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("focus")] JsonElement Focus, [property: JsonPropertyName("anchor")] string? Anchor ); -/// +/// public record NodeActionDetail( - [property: JsonPropertyName("task_id")] int TaskId, - [property: JsonPropertyName("action_id")] int ActionId, + [property: JsonPropertyName("task_id")] long TaskId, + [property: JsonPropertyName("action_id")] long ActionId, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("focus")] JsonElement Focus ); + +public record WaitFreezesParam( + [property: JsonPropertyName("time")] long MillisecondsTime, + [property: JsonPropertyName("threshold")] double Threshold, + [property: JsonPropertyName("method")] int Method, + [property: JsonPropertyName("rate_limit")] long MillisecondsRateLimit, + [property: JsonPropertyName("timeout")] long MillisecondsTimeout +) +{ + [JsonIgnore] public TimeSpan Time => TimeSpan.FromMilliseconds(MillisecondsTime); + [JsonIgnore] public TimeSpan RateLimit => TimeSpan.FromMilliseconds(MillisecondsRateLimit); + [JsonIgnore] public TimeSpan Timeout => TimeSpan.FromMilliseconds(MillisecondsTimeout); +} + +/// +public record NodeWaitFreezesDetail( + [property: JsonPropertyName("task_id")] long TaskId, + [property: JsonPropertyName("wf_id")] long WaitFreezesId, + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("phase")] string Phase, + [property: JsonPropertyName("roi")] IReadOnlyList Roi, + [property: JsonPropertyName("param")] WaitFreezesParam Param, + [property: JsonPropertyName("reco_ids")] IReadOnlyList? RecognitionIds, + [property: JsonPropertyName("elapsed")] long? MillisecondsElapsed, + [property: JsonPropertyName("focus")] JsonElement Focus +// https://github.com/MaaXYZ/MaaFramework/blob/v5.10.5/source/MaaFramework/Task/Component/ActionHelper.cpp#L46 +) +{ + [JsonIgnore] public TimeSpan? Elapsed => MillisecondsElapsed.HasValue ? TimeSpan.FromMilliseconds(MillisecondsElapsed.Value) : null; +} diff --git a/src/MaaFramework.Binding.Extensions/Notification/NotificationDetailContext.cs b/src/MaaFramework.Binding.Extensions/Notification/NotificationDetailContext.cs index 6708a23..694e3d2 100644 --- a/src/MaaFramework.Binding.Extensions/Notification/NotificationDetailContext.cs +++ b/src/MaaFramework.Binding.Extensions/Notification/NotificationDetailContext.cs @@ -9,8 +9,9 @@ namespace MaaFramework.Binding.Notification; [JsonSerializable(typeof(PipelineNodeDetail))] [JsonSerializable(typeof(RecognitionNodeDetail))] [JsonSerializable(typeof(ActionNodeDetail))] -[JsonSerializable(typeof(NodeAttr))] [JsonSerializable(typeof(NodeNextListDetail))] [JsonSerializable(typeof(NodeRecognitionDetail))] [JsonSerializable(typeof(NodeActionDetail))] +[JsonSerializable(typeof(NodeWaitFreezesDetail))] +[JsonSerializable(typeof(string))] public partial class NotificationDetailContext : JsonSerializerContext; diff --git a/src/MaaFramework.Binding.Extensions/Notification/NotificationHandler.cs b/src/MaaFramework.Binding.Extensions/Notification/NotificationHandler.cs index fb5135f..6073d80 100644 --- a/src/MaaFramework.Binding.Extensions/Notification/NotificationHandler.cs +++ b/src/MaaFramework.Binding.Extensions/Notification/NotificationHandler.cs @@ -100,6 +100,18 @@ public static EventHandler ToCallback(this Notif public static EventHandler ToCallback(this NotificationHandler notify) where TContext : IMaaTasker => notify.ToCallback(MaaMsg.Tasker.Task.Prefix); + /// + public static EventHandler ToCallback(this NotificationHandler notify) where TContext : IMaaContext + => notify.ToCallback(MaaMsg.Node.PipelineNode.Prefix); + + /// + public static EventHandler ToCallback(this NotificationHandler notify) where TContext : IMaaContext + => notify.ToCallback(MaaMsg.Node.RecognitionNode.Prefix); + + /// + public static EventHandler ToCallback(this NotificationHandler notify) where TContext : IMaaContext + => notify.ToCallback(MaaMsg.Node.ActionNode.Prefix); + /// public static EventHandler ToCallback(this NotificationHandler notify) where TContext : IMaaContext => notify.ToCallback(MaaMsg.Node.NextList.Prefix); @@ -113,15 +125,7 @@ public static EventHandler ToCallback(this Notif => notify.ToCallback(MaaMsg.Node.Action.Prefix); /// - public static EventHandler ToCallback(this NotificationHandler notify) where TContext : IMaaContext - => notify.ToCallback(MaaMsg.Node.PipelineNode.Prefix); - - /// - public static EventHandler ToCallback(this NotificationHandler notify) where TContext : IMaaContext - => notify.ToCallback(MaaMsg.Node.RecognitionNode.Prefix); - - /// - public static EventHandler ToCallback(this NotificationHandler notify) where TContext : IMaaContext - => notify.ToCallback(MaaMsg.Node.ActionNode.Prefix); + public static EventHandler ToCallback(this NotificationHandler notify) where TContext : IMaaContext + => notify.ToCallback(MaaMsg.Node.WaitFreezes.Prefix); } diff --git a/src/MaaFramework.Binding.Extensions/Notification/NotificationHandlerRegistry.cs b/src/MaaFramework.Binding.Extensions/Notification/NotificationHandlerRegistry.cs index 055329a..e5ccd0f 100644 --- a/src/MaaFramework.Binding.Extensions/Notification/NotificationHandlerRegistry.cs +++ b/src/MaaFramework.Binding.Extensions/Notification/NotificationHandlerRegistry.cs @@ -232,6 +232,20 @@ internal void OnFailed(object? sender, string details) => Failed?.Invoke(sender, NotificationDetailContext.Default.NodeActionDetail) ?? throw new InvalidCastException()); } + public WaitFreezesRegistry WaitFreezes { get; } = new(); + public sealed class WaitFreezesRegistry + { + public event EventHandler? Starting; + internal void OnStarting(object? sender, string details) => Starting?.Invoke(sender, JsonSerializer.Deserialize(details, + NotificationDetailContext.Default.NodeWaitFreezesDetail) ?? throw new InvalidCastException()); + public event EventHandler? Succeeded; + internal void OnSucceeded(object? sender, string details) => Succeeded?.Invoke(sender, JsonSerializer.Deserialize(details, + NotificationDetailContext.Default.NodeWaitFreezesDetail) ?? throw new InvalidCastException()); + public event EventHandler? Failed; + internal void OnFailed(object? sender, string details) => Failed?.Invoke(sender, JsonSerializer.Deserialize(details, + NotificationDetailContext.Default.NodeWaitFreezesDetail) ?? throw new InvalidCastException()); + } + } -} \ No newline at end of file +} diff --git a/src/MaaFramework.Binding.Extensions/README.md b/src/MaaFramework.Binding.Extensions/README.md index 3d48478..5f705c6 100644 --- a/src/MaaFramework.Binding.Extensions/README.md +++ b/src/MaaFramework.Binding.Extensions/README.md @@ -56,9 +56,13 @@ Supported TDetail types: - ResourceLoadingDetail - ControllerActionDetail - TaskerTaskDetail +- PipelineNodeDetail +- RecognitionNodeDetail +- ActionNodeDetail - NodeNextListDetail - NodeRecognitionDetail - NodeActionDetail +- NodeWaitFreezesDetail - string Usage example: @@ -82,4 +86,4 @@ NotificationHandler OnResourceLoading = (type, detail) => } }; maa.Callback += OnResourceLoading.ToCallback(); -``` \ No newline at end of file +``` diff --git a/src/MaaFramework.Binding.Extensions/README.zh_cn.md b/src/MaaFramework.Binding.Extensions/README.zh_cn.md index 22a71b6..c151721 100644 --- a/src/MaaFramework.Binding.Extensions/README.zh_cn.md +++ b/src/MaaFramework.Binding.Extensions/README.zh_cn.md @@ -56,9 +56,13 @@ TDetail 为以下类型: - ResourceLoadingDetail - ControllerActionDetail - TaskerTaskDetail +- PipelineNodeDetail +- RecognitionNodeDetail +- ActionNodeDetail - NodeNextListDetail - NodeRecognitionDetail - NodeActionDetail +- NodeWaitFreezesDetail - string 使用示例: diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs b/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs index 54818ba..8e9075e 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs @@ -19,6 +19,7 @@ global using MaaRecoId = System.Int64; global using MaaActId = System.Int64; global using MaaNodeId = System.Int64; +global using MaaWfId = System.Int64; global using MaaSinkId = System.Int64; global using MaaStringBufferHandle = nint; global using MaaImageBufferHandle = nint; @@ -55,8 +56,6 @@ global using MaaGamepadType = System.UInt64; global using MaaGamepadButton = System.UInt64; global using MaaGamepadTouch = System.UInt64; -// WaitFreezes id -global using MaaWfId = System.Int64; global using MaaRectHandle = nint; using System.Runtime.InteropServices; diff --git a/src/MaaFramework.Binding.Native/MaaTasker.cs b/src/MaaFramework.Binding.Native/MaaTasker.cs index 532851e..c5be649 100644 --- a/src/MaaFramework.Binding.Native/MaaTasker.cs +++ b/src/MaaFramework.Binding.Native/MaaTasker.cs @@ -408,14 +408,14 @@ public bool GetActionDetail(MaaActId actionId, out string nodeName, out string a } /// - public bool GetWaitFreezesDetail(MaaWfId wfId, out string nodeName, out string phase, out bool isSucceeded, out ulong elapsedMs, out MaaRecoId[] recoIdList, IMaaRectBuffer? roi) - => GetWaitFreezesDetail(wfId, out nodeName, out phase, out isSucceeded, out elapsedMs, out recoIdList, (MaaRectBuffer?)roi); + public bool GetWaitFreezesDetail(MaaWfId waitFreezesId, out string nodeName, out string phase, out bool isSucceeded, out MaaSize millisecondsElapsed, out MaaRecoId[] recoIdList, IMaaRectBuffer? roi) + => GetWaitFreezesDetail(waitFreezesId, out nodeName, out phase, out isSucceeded, out millisecondsElapsed, out recoIdList, (MaaRectBuffer?)roi); - /// + /// /// /// Wrapper of . /// - public bool GetWaitFreezesDetail(MaaWfId wfId, out string nodeName, out string phase, out bool isSucceeded, out ulong elapsedMs, out MaaRecoId[] recoIdList, MaaRectBuffer? roi) + public bool GetWaitFreezesDetail(MaaWfId waitFreezesId, out string nodeName, out string phase, out bool isSucceeded, out MaaSize millisecondsElapsed, out MaaRecoId[] recoIdList, MaaRectBuffer? roi) { var roiHandle = roi?.Handle ?? MaaRectHandle.Zero; MaaSize recoIdListSize = 0; @@ -423,7 +423,7 @@ public bool GetWaitFreezesDetail(MaaWfId wfId, out string nodeName, out string p using var nodeNameBuffer = new MaaStringBuffer(); using var phaseBuffer = new MaaStringBuffer(); - if (!MaaTaskerGetWaitFreezesDetail(Handle, wfId, nodeNameBuffer.Handle, phaseBuffer.Handle, out isSucceeded, out elapsedMs, null, ref recoIdListSize, roiHandle)) + if (!MaaTaskerGetWaitFreezesDetail(Handle, waitFreezesId, nodeNameBuffer.Handle, phaseBuffer.Handle, out isSucceeded, out millisecondsElapsed, null, ref recoIdListSize, roiHandle)) { nodeName = string.Empty; phase = string.Empty; @@ -434,7 +434,7 @@ public bool GetWaitFreezesDetail(MaaWfId wfId, out string nodeName, out string p nodeName = nodeNameBuffer.ToString(); phase = phaseBuffer.ToString(); recoIdList = recoIdListSize == 0 ? [] : new MaaRecoId[recoIdListSize]; - return MaaTaskerGetWaitFreezesDetail(Handle, wfId, nodeNameBuffer.Handle, phaseBuffer.Handle, out isSucceeded, out elapsedMs, recoIdList, ref recoIdListSize, roiHandle); + return MaaTaskerGetWaitFreezesDetail(Handle, waitFreezesId, nodeNameBuffer.Handle, phaseBuffer.Handle, out isSucceeded, out millisecondsElapsed, recoIdList, ref recoIdListSize, roiHandle); } /// diff --git a/src/MaaFramework.Binding.UnitTests/Test_Common.cs b/src/MaaFramework.Binding.UnitTests/Test_Common.cs index d0cbbd2..33b243a 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_Common.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_Common.cs @@ -95,4 +95,5 @@ internal static void OnCallback(object? sender, MaaCallbackEventArgs e) internal static NotificationHandler OnNodeNextList = (type, detail, context) => Assert.IsNotNull(context); internal static NotificationHandler OnNodeRecognition = (type, detail, context) => Assert.IsNotNull(context); internal static NotificationHandler OnNodeAction = (type, detail, context) => Assert.IsNotNull(context); + internal static NotificationHandler OnNodeWaitFreezes = (type, detail, context) => Assert.IsNotNull(context); } diff --git a/src/MaaFramework.Binding/IMaaTasker.cs b/src/MaaFramework.Binding/IMaaTasker.cs index fd03497..4cbd7bb 100644 --- a/src/MaaFramework.Binding/IMaaTasker.cs +++ b/src/MaaFramework.Binding/IMaaTasker.cs @@ -157,15 +157,19 @@ bool GetRecognitionDetail(MaaRecoId recognitionId, out string nodeName, out s /// /// Gets the wait freezes detail. /// - /// The wait freezes id. + /// The wait freezes id. /// The node name. /// The phase (e.g. "pre", "post", "repeat", "context"). /// A value indicating whether the wait freezes is succeeded. - /// The elapsed time in milliseconds. - /// The recognition id list. - /// The region of interest. + /// The total elapsed time in milliseconds. + /// The recognition id array for each frame-to-frame comparison. + /// The actual region of interest used for comparison. /// if query was successful; otherwise, . - bool GetWaitFreezesDetail(MaaWfId wfId, out string nodeName, out string phase, out bool isSucceeded, out ulong elapsedMs, out MaaRecoId[] recoIdList, IMaaRectBuffer? roi); + /// + /// Obtained via `wf_id` from the callback. + /// Each `reco_id` can be queried via `MaaTaskerGetRecognitionDetail` to get the drawn images. + /// + bool GetWaitFreezesDetail(MaaWfId waitFreezesId, out string nodeName, out string phase, out bool isSucceeded, out MaaSize millisecondsElapsed, out MaaRecoId[] recoIdList, IMaaRectBuffer? roi); /// /// Gets the node detail. diff --git a/src/MaaFramework.Binding/MaaMsg.cs b/src/MaaFramework.Binding/MaaMsg.cs index 2d877c5..debb5f8 100644 --- a/src/MaaFramework.Binding/MaaMsg.cs +++ b/src/MaaFramework.Binding/MaaMsg.cs @@ -22,364 +22,310 @@ public static class MaaMsg { public static class Resource { + /// + /// The message for the resource loading. + /// + /// + /// + /// details_json: { + /// res_id: number, + /// path: string, + /// type: string, // "Bundle" | "OcrModel" | "Pipeline" | "Image" + /// hash: string, + /// } + /// + /// public static class Loading { - /// - /// The message for the resource loading. - /// - /// - /// details_json: { res_id: number, path: string, type: string // "Bundle" | "OcrModel" | "Pipeline" | "Image", hash: string, } - /// + /// public const string Starting = "Resource.Loading.Starting"; - /// - /// The message for the resource loading. - /// - /// - /// details_json: { res_id: number, path: string, type: string // "Bundle" | "OcrModel" | "Pipeline" | "Image", hash: string, } - /// + /// public const string Prefix = "Resource.Loading"; - /// - /// The message for the resource loading. - /// - /// - /// details_json: { res_id: number, path: string, type: string // "Bundle" | "OcrModel" | "Pipeline" | "Image", hash: string, } - /// + /// public const string Succeeded = "Resource.Loading.Succeeded"; - /// - /// The message for the resource loading. - /// - /// - /// details_json: { res_id: number, path: string, type: string // "Bundle" | "OcrModel" | "Pipeline" | "Image", hash: string, } - /// + /// public const string Failed = "Resource.Loading.Failed"; - } } public static class Controller { + /// + /// Message for the controller actions. + /// + /// + /// + /// details_json: { + /// ctrl_id: number, + /// uuid: string, + /// action: string, + /// param: object, + /// info: object, + /// } + /// + /// public static class Action { - /// - /// Message for the controller actions. - /// - /// - /// details_json: { ctrl_id: number, uuid: string, action: string, param: object, info: object, } - /// + /// public const string Starting = "Controller.Action.Starting"; - /// - /// Message for the controller actions. - /// - /// - /// details_json: { ctrl_id: number, uuid: string, action: string, param: object, info: object, } - /// + /// public const string Prefix = "Controller.Action"; - /// - /// Message for the controller actions. - /// - /// - /// details_json: { ctrl_id: number, uuid: string, action: string, param: object, info: object, } - /// + /// public const string Succeeded = "Controller.Action.Succeeded"; - /// - /// Message for the controller actions. - /// - /// - /// details_json: { ctrl_id: number, uuid: string, action: string, param: object, info: object, } - /// + /// public const string Failed = "Controller.Action.Failed"; - } } public static class Tasker { + /// + /// Message for the task. + /// + /// + /// + /// details_json: { + /// task_id: number, + /// entry: string, + /// uuid: string, + /// hash: string + /// } + /// + /// public static class Task { - /// - /// Message for the task. - /// - /// - /// details_json: { task_id: number, entry: string, uuid: string, hash: string } - /// + /// public const string Starting = "Tasker.Task.Starting"; - /// - /// Message for the task. - /// - /// - /// details_json: { task_id: number, entry: string, uuid: string, hash: string } - /// + /// public const string Prefix = "Tasker.Task"; - /// - /// Message for the task. - /// - /// - /// details_json: { task_id: number, entry: string, uuid: string, hash: string } - /// + /// public const string Succeeded = "Tasker.Task.Succeeded"; - /// - /// Message for the task. - /// - /// - /// details_json: { task_id: number, entry: string, uuid: string, hash: string } - /// + /// public const string Failed = "Tasker.Task.Failed"; - } } public static class Node { + /// + /// Message for the node. + /// + /// + /// + /// details_json: { + /// task_id: number, + /// node_id: number, + /// name: string, + /// focus: any, + /// } + /// + /// public static class PipelineNode { - /// - /// Message for the pipeline node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Starting = "Node.PipelineNode.Starting"; - /// - /// Message for the pipeline node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Prefix = "Node.PipelineNode"; - /// - /// Message for the pipeline node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Succeeded = "Node.PipelineNode.Succeeded"; - /// - /// Message for the pipeline node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Failed = "Node.PipelineNode.Failed"; - } + + /// + /// Message for the node. + /// + /// + /// + /// details_json: { + /// task_id: number, + /// node_id: number, + /// name: string, + /// focus: any, + /// } + /// + /// public static class RecognitionNode { - /// - /// Message for the recognition node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Starting = "Node.RecognitionNode.Starting"; - /// - /// Message for the recognition node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Prefix = "Node.RecognitionNode"; - /// - /// Message for the recognition node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Succeeded = "Node.RecognitionNode.Succeeded"; - /// - /// Message for the recognition node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Failed = "Node.RecognitionNode.Failed"; - } + + /// + /// Message for the node. + /// + /// + /// + /// details_json: { + /// task_id: number, + /// node_id: number, + /// name: string, + /// focus: any, + /// } + /// + /// public static class ActionNode { - /// - /// Message for the action node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Starting = "Node.ActionNode.Starting"; - /// - /// Message for the action node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Prefix = "Node.ActionNode"; - /// - /// Message for the action node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Succeeded = "Node.ActionNode.Succeeded"; - /// - /// Message for the action node. - /// - /// - /// details_json: { task_id: number, node_id: number, name: string, focus: any, } - /// + /// public const string Failed = "Node.ActionNode.Failed"; - } + + /// + /// Message for the next list of node. + /// + /// + /// + /// details_json: { + /// task_id: number, + /// name: string, + /// list: [ + /// { + /// "name": string, + /// "jump_back": boolean, + /// "anchor": boolean, + /// }, + /// ... + /// ], + /// focus: any, + /// } + /// + /// public static class NextList { - /// - /// Message for the next list of node. - /// - /// - /// details_json: { task_id: number, name: string, list: [{ name: string, jump_back: boolean, anchor: boolean }, ...], focus: any, } - /// + /// public const string Starting = "Node.NextList.Starting"; - /// - /// Message for the next list of node. - /// - /// - /// details_json: { task_id: number, name: string, list: [{ name: string, jump_back: boolean, anchor: boolean }, ...], focus: any, } - /// + /// public const string Prefix = "Node.NextList"; - /// - /// Message for the next list of node. - /// - /// - /// details_json: { task_id: number, name: string, list: [{ name: string, jump_back: boolean, anchor: boolean }, ...], focus: any, } - /// + /// public const string Succeeded = "Node.NextList.Succeeded"; - /// - /// Message for the next list of node. - /// - /// - /// details_json: { task_id: number, name: string, list: [{ name: string, jump_back: boolean, anchor: boolean }, ...], focus: any, } - /// + /// public const string Failed = "Node.NextList.Failed"; - } + + /// + /// Message for the recognition list of node. + /// + /// + /// + /// details_json: { + /// task_id: number, + /// reco_id: number, + /// name: string, + /// focus: any, + /// anchor?: string, + /// } + /// + /// public static class Recognition { - /// - /// Message for the recognition list of node. - /// - /// - /// details_json: { task_id: number, reco_id: number, name: string, focus: any, anchor?: string, } - /// + /// public const string Starting = "Node.Recognition.Starting"; - /// - /// Message for the recognition list of node. - /// - /// - /// details_json: { task_id: number, reco_id: number, name: string, focus: any, anchor?: string, } - /// + /// public const string Prefix = "Node.Recognition"; - /// - /// Message for the recognition list of node. - /// - /// - /// details_json: { task_id: number, reco_id: number, name: string, focus: any, anchor?: string, } - /// + /// public const string Succeeded = "Node.Recognition.Succeeded"; - /// - /// Message for the recognition list of node. - /// - /// - /// details_json: { task_id: number, reco_id: number, name: string, focus: any, anchor?: string, } - /// + /// public const string Failed = "Node.Recognition.Failed"; - } + + /// + /// Message for the action of node. + /// + /// + /// + /// details_json: { + /// task_id: number, + /// action_id: number, + /// name: string, + /// focus: any, + /// } + /// + /// public static class Action { - /// - /// Message for the action of node. - /// - /// - /// details_json: { task_id: number, action_id: number, name: string, focus: any, } - /// + /// public const string Starting = "Node.Action.Starting"; - /// - /// Message for the action of node. - /// - /// - /// details_json: { task_id: number, action_id: number, name: string, focus: any, } - /// + /// public const string Prefix = "Node.Action"; - /// - /// Message for the action of node. - /// - /// - /// details_json: { task_id: number, action_id: number, name: string, focus: any, } - /// + /// public const string Succeeded = "Node.Action.Succeeded"; - /// - /// Message for the action of node. - /// - /// - /// details_json: { task_id: number, action_id: number, name: string, focus: any, } - /// + /// public const string Failed = "Node.Action.Failed"; - } + + /// + /// Message for wait freezes. + /// + /// + /// + /// details_json: { + /// task_id: number, + /// wf_id: number, + /// name: string, + /// phase: string, // "pre" | "post" | "repeat" | "context" + /// roi: [number, number, number, number], + /// param: { + /// time: number, + /// threshold: number, + /// method: number, + /// rate_limit: number, + /// timeout: number, + /// }, + /// reco_ids?: number[], // Succeeded / Failed only + /// elapsed?: number, // Succeeded / Failed only + /// focus: any, + /// } + /// + /// public static class WaitFreezes { - /// - /// Message for wait freezes. - /// - /// - /// details_json: { task_id: number, wf_id: number, name: string, phase: string, roi: [number, number, number, number], param: { time: number, threshold: number, method: number, rate_limit: number, timeout: number }, reco_ids?: number[], elapsed?: number, focus: any, } - /// + /// public const string Starting = "Node.WaitFreezes.Starting"; - /// - /// Message for wait freezes. - /// - /// - /// details_json: { task_id: number, wf_id: number, name: string, phase: string, roi: [number, number, number, number], param: { time: number, threshold: number, method: number, rate_limit: number, timeout: number }, reco_ids?: number[], elapsed?: number, focus: any, } - /// + /// public const string Prefix = "Node.WaitFreezes"; - /// - /// Message for wait freezes. - /// - /// - /// details_json: { task_id: number, wf_id: number, name: string, phase: string, roi: [number, number, number, number], param: { time: number, threshold: number, method: number, rate_limit: number, timeout: number }, reco_ids?: number[], elapsed?: number, focus: any, } - /// + /// public const string Succeeded = "Node.WaitFreezes.Succeeded"; - /// - /// Message for wait freezes. - /// - /// - /// details_json: { task_id: number, wf_id: number, name: string, phase: string, roi: [number, number, number, number], param: { time: number, threshold: number, method: number, rate_limit: number, timeout: number }, reco_ids?: number[], elapsed?: number, focus: any, } - /// + /// public const string Failed = "Node.WaitFreezes.Failed"; - } } } From 5900a8cb5144b305ed15d08b1ecc44364892f1d1 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:48:35 +0800 Subject: [PATCH 12/15] =?UTF-8?q?feat(wlroots):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=B0=86=E6=8C=89=E9=94=AE=E8=A7=86=E4=B8=BA=20Win32=20VK=20?= =?UTF-8?q?=E9=94=AE=E7=A0=81=20(#1283)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Framework/Instance/MaaController.cs | 34 +++++++++++++++---- .../MaaController/MaaWlRootsController.cs | 9 +++-- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs index f1454d4..79b8af6 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs @@ -104,6 +104,34 @@ public static partial class MaaController [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaControllerHandle MaaRecordControllerCreate(MaaControllerHandle inner, string recordingPath); + /// + /// Create a PlayCover controller for macOS. + /// + /// The PlayTools service address in "host:port" format. + /// The application bundle identifier (e.g., "com.hypergryph.arknights"). + /// The controller handle, or nullptr on failure. + /// + /// This controller is designed for PlayCover on macOS. + /// Some features are not supported: start_app, input_text, click_key, key_down, key_up, scroll. + /// Only single touch is supported (contact must be 0). + /// + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] + public static partial MaaControllerHandle MaaPlayCoverControllerCreate(string address, string uuid); + + /// + /// Create a wlroots controller for Linux. + /// + /// The wayland socket path (e.g., "/run/user/1000/wayland-0"). + /// + ///
If true, key codes passed to click_key / key_down / key_up are + ///
interpreted as Win32 Virtual-Key codes (VK_*) and translated to Linux evdev codes + ///
internally. If false, key codes are passed through as raw evdev codes. + /// + /// The controller handle, or nullptr on failure. + /// This controller is designed for wlroots on Linux. + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] + public static partial MaaControllerHandle MaaWlRootsControllerCreate(string wlrSocketPath, [MarshalAs(UnmanagedType.U1)] bool useWin32VkCode); + [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial void MaaControllerDestroy(MaaControllerHandle ctrl); @@ -185,12 +213,6 @@ public static partial class MaaController [return: MarshalAs(UnmanagedType.U1)] public static partial bool MaaControllerGetShellOutput(MaaControllerHandle ctrl, MaaStringBufferHandle buffer); - [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] - public static partial MaaControllerHandle MaaPlayCoverControllerCreate(string address, string uuid); - - [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] - public static partial MaaControllerHandle MaaWlRootsControllerCreate(string wlr_socket_path); - /// /// Create a virtual gamepad controller for Windows. /// diff --git a/src/MaaFramework.Binding.Native/MaaController/MaaWlRootsController.cs b/src/MaaFramework.Binding.Native/MaaController/MaaWlRootsController.cs index 881234b..d3d3ad0 100644 --- a/src/MaaFramework.Binding.Native/MaaController/MaaWlRootsController.cs +++ b/src/MaaFramework.Binding.Native/MaaController/MaaWlRootsController.cs @@ -24,6 +24,11 @@ public class MaaWlRootsController : MaaController /// Creates a instance. ///
/// The wayland socket path (e.g., "/run/user/1000/wayland-0"). + /// + /// If true, key codes passed to / / + ///
interpreted as Win32 Virtual-Key codes (VK_*) and translated to Linux evdev codes internally.
+ /// If false, key codes are passed through as raw evdev codes. + /// /// Executes if ; otherwise, not link. /// Checks LinkStart().Wait() status if ; otherwise, not check. /// @@ -32,11 +37,11 @@ public class MaaWlRootsController : MaaController /// /// /// - public MaaWlRootsController(string wlrSocketPath, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded) + public MaaWlRootsController(string wlrSocketPath, bool useWin32VirtualKeyCodes = false, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded) { ArgumentException.ThrowIfNullOrEmpty(wlrSocketPath); - var handle = MaaWlRootsControllerCreate(wlrSocketPath); + var handle = MaaWlRootsControllerCreate(wlrSocketPath, useWin32VirtualKeyCodes); _ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller); SetHandle(handle, needReleased: true); From 050be48303892d89f645af3878c189398f33ea25 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Wed, 8 Jul 2026 01:40:41 +0800 Subject: [PATCH 13/15] =?UTF-8?q?feat(win32):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E6=89=98=E7=AE=A1=E6=8C=89=E9=94=AE=E5=AE=88?= =?UTF-8?q?=E6=8A=A4=EF=BC=8C=E6=94=AF=E6=8C=81=E5=90=8E=E5=8F=B0=E9=95=BF?= =?UTF-8?q?=E6=8C=89=E7=A7=BB=E5=8A=A8=20(#1231)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Interop/MaaMarshaller.cs | 43 ++++++++++++++++++- .../Interop/MaaMarshallingExtensions.cs | 4 ++ .../MaaController.cs | 2 + .../Test_IMaaController.cs | 2 + .../Enums/Options/ControllerOption.cs | 11 +++++ .../Extensions/MaaOptionExtensions.cs | 5 +++ 6 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/MaaFramework.Binding.Native/Interop/MaaMarshaller.cs b/src/MaaFramework.Binding.Native/Interop/MaaMarshaller.cs index 8c9fbbe..50c788e 100644 --- a/src/MaaFramework.Binding.Native/Interop/MaaMarshaller.cs +++ b/src/MaaFramework.Binding.Native/Interop/MaaMarshaller.cs @@ -1,7 +1,8 @@ -using System.Runtime.InteropServices; +using MaaFramework.Binding.Custom; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using System.Text; -using MaaFramework.Binding.Custom; namespace MaaFramework.Binding.Interop.Native; @@ -46,6 +47,44 @@ public static byte[] ConvertToMaaOptionValue(bool value) /// public static byte[] ConvertToMaaOptionValue(string value) => Encoding.UTF8.GetBytes(value); + + /// + /// Converts a to a MaaOptionValue ([]). + /// + public static byte[] ConvertToMaaOptionValue(IEnumerable value) + { + ArgumentNullException.ThrowIfNull(value); + + if (value is int[] array) + { + var result = new byte[array.Length * 4]; + Span byteSpan = result; + MemoryMarshal.Cast(array).CopyTo(byteSpan); + return result; + } + + if (value is List list) + { + var result = new byte[list.Count * 4]; + Span byteSpan = result; + MemoryMarshal.Cast(CollectionsMarshal.AsSpan(list)).CopyTo(byteSpan); + return result; + } + + if (value.TryGetNonEnumeratedCount(out var count)) + { + var result = new byte[count * 4]; + var i = 0; + foreach (var item in value) + { + // 边界检查 MemoryMarshal.Cast(new ReadOnlySpan(in item)).CopyTo(new Span(result, i++ * 4, 4)); + Unsafe.WriteUnaligned(ref result[i++ * 4], item); + } + return result; + } + + return [.. value.SelectMany(BitConverter.GetBytes)]; + } } /// diff --git a/src/MaaFramework.Binding.Native/Interop/MaaMarshallingExtensions.cs b/src/MaaFramework.Binding.Native/Interop/MaaMarshallingExtensions.cs index 65f602c..d659ae5 100644 --- a/src/MaaFramework.Binding.Native/Interop/MaaMarshallingExtensions.cs +++ b/src/MaaFramework.Binding.Native/Interop/MaaMarshallingExtensions.cs @@ -67,4 +67,8 @@ internal static byte[] ToMaaOptionValue(this string value) /// internal static byte[] ToMaaOptionValue(this nuint value) => MaaMarshaller.ConvertToMaaOptionValue(value); + + /// + internal static byte[] ToMaaOptionValue(this IEnumerable value) + => MaaMarshaller.ConvertToMaaOptionValue(value); } diff --git a/src/MaaFramework.Binding.Native/MaaController.cs b/src/MaaFramework.Binding.Native/MaaController.cs index 4a7ea9b..6036d4c 100644 --- a/src/MaaFramework.Binding.Native/MaaController.cs +++ b/src/MaaFramework.Binding.Native/MaaController.cs @@ -86,6 +86,8 @@ or ControllerOption.ScreenshotTargetShortSide (bool vvv, ControllerOption.ScreenshotUseRawSize or ControllerOption.MouseLockFollow) => vvv.ToMaaOptionValue(), + (IEnumerable ints, ControllerOption.BackgroundManagedKeys) => ints.ToMaaOptionValue(), + _ => throw new NotSupportedException($"'{nameof(ControllerOption)}.{opt}' or type '{typeof(T)}' is not supported."), }; diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs index 42471b8..0c755a4 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs @@ -146,6 +146,7 @@ public void CreateInstances() [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotUseRawSize, false)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.MouseLockFollow, false)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotResizeMethod, 0)] + [MaaData(MaaTypes.All, nameof(Data), ControllerOption.BackgroundManagedKeys, new int[] { 0x57, 0x41, 0x53, 0x44 })] public void Interface_SetOption(MaaTypes type, IMaaController maaController, ControllerOption opt, object arg) { Assert.IsNotNull(maaController); @@ -404,6 +405,7 @@ public void Interface_Shell_ShellOutput(MaaTypes type, IMaaController maaControl [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotUseRawSize, 0.0)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.MouseLockFollow, 0.0)] [MaaData(MaaTypes.All, nameof(Data), ControllerOption.ScreenshotResizeMethod, 0.0)] + [MaaData(MaaTypes.All, nameof(Data), ControllerOption.BackgroundManagedKeys, 0.0)] public void Interface_SetOption_InvalidData(MaaTypes type, IMaaController maaController, ControllerOption opt, object arg) { Assert.IsNotNull(maaController); diff --git a/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs b/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs index 950ee48..6185ad0 100644 --- a/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs +++ b/src/MaaFramework.Binding/Enums/Options/ControllerOption.cs @@ -60,5 +60,16 @@ public enum ControllerOption : System.Int32 /// value: int, eg: 3; val_size: sizeof(int) ///
ScreenshotResizeMethod = 6, + + /// + /// Configure background managed key domain for Win32 controllers. + /// + /// + /// Must be set before connection. After setting, matching ClickKey / LongPressKey / KeyDown / KeyUp + ///
operations automatically route through the background guardian path.
+ /// Only supported by Win32 controllers; other controllers will fail. + /// value: int32_t array of virtual key codes; val_size: sizeof(int32_t) * count + ///
+ BackgroundManagedKeys = 7, } diff --git a/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs b/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs index 1f81570..56f40eb 100644 --- a/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs +++ b/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs @@ -35,6 +35,11 @@ public static bool SetOption_MouseLockFollow(this IMaaOption? public static bool SetOption_ScreenshotResizeMethod(this IMaaOption? opt, int value) => opt?.SetOption(ControllerOption.ScreenshotResizeMethod, value) ?? throw new ArgumentNullException(nameof(opt)); + /// + /// + public static bool SetOption_BackgroundManagedKeys(this IMaaOption? opt, IEnumerable value) + => opt?.SetOption(ControllerOption.BackgroundManagedKeys, value) ?? throw new ArgumentNullException(nameof(opt)); + /// /// public static bool SetOption_LogDir(this IMaaOption? opt, string value) From c25d3147512080086a2f8bd1e6d57021085187a7 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Wed, 8 Jul 2026 15:03:09 +0800 Subject: [PATCH 14/15] fix: resolve compilation errors --- .../Interop/Framework/Instance/MaaTasker.cs | 4 +++- src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaTasker.cs b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaTasker.cs index 90ae9b2..c076fe8 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaTasker.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaTasker.cs @@ -7,6 +7,8 @@ // //------------------------------------------------------------------------------ +#nullable enable + #pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记 #pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 @@ -115,7 +117,7 @@ public static partial class MaaTasker [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] [return: MarshalAs(UnmanagedType.U1)] - public static partial bool MaaTaskerGetTaskDetail(MaaTaskerHandle tasker, MaaTaskId taskId, MaaStringBufferHandle entry, MaaNodeId[] nodeIdList, ref MaaSize nodeIdListSize, out MaaStatus status); + public static partial bool MaaTaskerGetTaskDetail(MaaTaskerHandle tasker, MaaTaskId taskId, MaaStringBufferHandle entry, MaaNodeId[]? nodeIdList, ref MaaSize nodeIdListSize, out MaaStatus status); [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] [return: MarshalAs(UnmanagedType.U1)] diff --git a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs index 8f646df..f666420 100644 --- a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs +++ b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs @@ -91,7 +91,7 @@ private static IEnumerable GetLibraryPaths(string libraryFullName) // to AppContext.BaseDirectory which is the directory containing the single-file executable. var assemblyDirectory = string.IsNullOrEmpty(s_assembly.Location) ? AppContext.BaseDirectory - : Path.GetDirectoryName(s_assembly.Location)!; + : Path.GetDirectoryName(s_assembly.Location); string?[] basePaths = [ From d5096ca1855c0c1512255780294e0e7cd918065e Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Wed, 8 Jul 2026 15:07:26 +0800 Subject: [PATCH 15/15] chore: update documentation comments --- .../Framework/Instance/MaaController.cs | 24 ++++++ .../Enums/Controllers/AdbInputMethods.cs | 22 +++-- .../Enums/Controllers/AdbScreencapMethods.cs | 32 +++++--- .../Enums/Controllers/GamepadButton.cs | 80 ++++++++++--------- .../Enums/Controllers/GamepadTouch.cs | 36 +++++---- .../Enums/Controllers/GamepadType.cs | 14 ++-- .../Enums/Controllers/Win32InputMethod.cs | 43 +++++----- 7 files changed, 156 insertions(+), 95 deletions(-) diff --git a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs index 79b8af6..7ddff03 100644 --- a/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs +++ b/src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs @@ -200,12 +200,36 @@ public static partial class MaaController [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaCtrlId MaaControllerPostScreencap(MaaControllerHandle ctrl); + /// + /// Post a scroll action to the controller. + /// + /// The controller handle. + /// The horizontal scroll delta. Positive values scroll right, negative values scroll left. + /// The vertical scroll delta. Positive values scroll up, negative values scroll down. + /// The control id of the scroll action. + /// + /// Not all controllers support scroll. If not supported, the action will fail. + /// Scroll is supported by Win32 controllers and custom controllers that implement scroll. + /// If the controller does not support scroll, the action will fail. Use MaaControllerStatus or
+ /// MaaControllerWait to check the result.
+ /// The dx/dy values are sent directly as scroll increments. Using multiples of 120 (WHEEL_DELTA) is
+ /// recommended for best compatibility.
+ ///
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaCtrlId MaaControllerPostScroll(MaaControllerHandle ctrl, int dx, int dy); [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaCtrlId MaaControllerPostInactive(MaaControllerHandle ctrl); + /// Post a shell command to the controller. + /// The controller handle. + /// The shell command to execute. + /// Timeout in milliseconds. Default is 20000 (20 seconds). + /// The control id of the shell action. + /// + /// This is only valid for ADB controllers. If the controller is not an ADB controller, the action will fail. + /// Supported by ADB controllers and custom controllers that implement the shell callback. + /// [LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)] public static partial MaaCtrlId MaaControllerPostShell(MaaControllerHandle ctrl, string cmd, long timeout); diff --git a/src/MaaFramework.Binding/Enums/Controllers/AdbInputMethods.cs b/src/MaaFramework.Binding/Enums/Controllers/AdbInputMethods.cs index 06a2c51..50b6a8f 100644 --- a/src/MaaFramework.Binding/Enums/Controllers/AdbInputMethods.cs +++ b/src/MaaFramework.Binding/Enums/Controllers/AdbInputMethods.cs @@ -14,16 +14,22 @@ namespace MaaFramework.Binding; /// /// Adb input method flags -/// Use bitwise OR to set the methods you need. -/// MaaFramework will select the first available method according to priority. +/// +/// Use bitwise OR to set the methods you need. +///
MaaFramework will select the first available method according to priority.
+/// /// Priority (high to low): EmulatorExtras > Maatouch > MinitouchAndAdbKey > AdbShell +/// /// Default: All methods except EmulatorExtras -/// | Method | Speed | Compatibility | Notes | -/// |----------------------|-------|---------------|---------------------------------------| -/// | AdbShell | Slow | High | | -/// | MinitouchAndAdbKey | Fast | Medium | Key press still uses AdbShell | -/// | Maatouch | Fast | Medium | | -/// | EmulatorExtras | Fast | Low | Emulators only: MuMu 12 | +/// +/// | Method | Speed | Compatibility | Notes | +///
|----------------------|-------|---------------|---------------------------------------| +///
| AdbShell | Slow | High | | +///
| MinitouchAndAdbKey | Fast | Medium | Key press still uses AdbShell | +///
| Maatouch | Fast | Medium | | +///
| EmulatorExtras | Fast | Low | Emulators only: MuMu 12 | +///
+/// ///
[Flags] public enum AdbInputMethods : System.UInt64 diff --git a/src/MaaFramework.Binding/Enums/Controllers/AdbScreencapMethods.cs b/src/MaaFramework.Binding/Enums/Controllers/AdbScreencapMethods.cs index 2a5fc68..6b8aaad 100644 --- a/src/MaaFramework.Binding/Enums/Controllers/AdbScreencapMethods.cs +++ b/src/MaaFramework.Binding/Enums/Controllers/AdbScreencapMethods.cs @@ -14,20 +14,26 @@ namespace MaaFramework.Binding; /// /// Adb screencap method flags. -/// Use bitwise OR to set the methods you need. -/// MaaFramework will test all provided methods and use the fastest available one. +/// +/// Use bitwise OR to set the methods you need. +///
MaaFramework will test all provided methods and use the fastest available one.
+/// /// Default: All methods except RawByNetcat, MinicapDirect, MinicapStream -/// Note: MinicapDirect and MinicapStream use lossy JPEG encoding, which may -/// significantly reduce template matching accuracy. Not recommended. -/// | Method | Speed | Compatibility | Encoding | Notes | -/// |-----------------------|------------|---------------|----------|-------------------------------------| -/// | EncodeToFileAndPull | Slow | High | Lossless | | -/// | Encode | Slow | High | Lossless | | -/// | RawWithGzip | Medium | High | Lossless | | -/// | RawByNetcat | Fast | Low | Lossless | | -/// | MinicapDirect | Fast | Low | Lossy | | -/// | MinicapStream | Very Fast | Low | Lossy | | -/// | EmulatorExtras | Very Fast | Low | Lossless | Emulators only: MuMu 12, LDPlayer 9 | +/// +/// Note: MinicapDirect and MinicapStream use lossy JPEG encoding, which may +///
significantly reduce template matching accuracy. Not recommended.
+/// +/// | Method | Speed | Compatibility | Encoding | Notes | +///
|-----------------------|------------|---------------|----------|-------------------------------------| +///
| EncodeToFileAndPull | Slow | High | Lossless | | +///
| Encode | Slow | High | Lossless | | +///
| RawWithGzip | Medium | High | Lossless | | +///
| RawByNetcat | Fast | Low | Lossless | | +///
| MinicapDirect | Fast | Low | Lossy | | +///
| MinicapStream | Very Fast | Low | Lossy | | +///
| EmulatorExtras | Very Fast | Low | Lossless | Emulators only: MuMu 12, LDPlayer 9 | +///
+/// ///
[Flags] public enum AdbScreencapMethods : System.UInt64 diff --git a/src/MaaFramework.Binding/Enums/Controllers/GamepadButton.cs b/src/MaaFramework.Binding/Enums/Controllers/GamepadButton.cs index 4a39fb3..8125f7b 100644 --- a/src/MaaFramework.Binding/Enums/Controllers/GamepadButton.cs +++ b/src/MaaFramework.Binding/Enums/Controllers/GamepadButton.cs @@ -13,42 +13,50 @@ namespace MaaFramework.Binding; /// -/// Virtual gamepad button codes for click_key/key_down/key_up -/// Use these values with MaaControllerPostClickKey, MaaControllerPostKeyDown, MaaControllerPostKeyUp. -/// Values are based on XUSB (Xbox 360) button flags. DS4 face buttons are mapped to Xbox equivalents. -/// Xbox 360 buttons: -/// | Value | Button | Description | -/// |---------|---------------------|------------------------| -/// | 0x1000 | A | A button | -/// | 0x2000 | B | B button | -/// | 0x4000 | X | X button | -/// | 0x8000 | Y | Y button | -/// | 0x0100 | LB (Left Shoulder) | Left bumper | -/// | 0x0200 | RB (Right Shoulder) | Right bumper | -/// | 0x0040 | L_THUMB | Left stick click | -/// | 0x0080 | R_THUMB | Right stick click | -/// | 0x0010 | START | Start button | -/// | 0x0020 | BACK | Back button | -/// | 0x0400 | GUIDE | Guide/Home button | -/// | 0x0001 | DPAD_UP | D-pad up | -/// | 0x0002 | DPAD_DOWN | D-pad down | -/// | 0x0004 | DPAD_LEFT | D-pad left | -/// | 0x0008 | DPAD_RIGHT | D-pad right | -/// DualShock 4 buttons (aliases to Xbox buttons): -/// | Value | Button | Xbox Equivalent | Description | -/// |---------|-----------|-----------------|---------------------------| -/// | 0x1000 | CROSS | A | Cross (X) button | -/// | 0x2000 | CIRCLE | B | Circle button | -/// | 0x4000 | SQUARE | X | Square button | -/// | 0x8000 | TRIANGLE | Y | Triangle button | -/// | 0x0100 | L1 | LB | L1 button | -/// | 0x0200 | R1 | RB | R1 button | -/// | 0x0040 | L3 | L_THUMB | Left stick click | -/// | 0x0080 | R3 | R_THUMB | Right stick click | -/// | 0x0010 | OPTIONS | START | Options button | -/// | 0x0020 | SHARE | BACK | Share button | -/// | 0x10000 | PS | - | PS button (DS4 special) | -/// | 0x20000 | TOUCHPAD | - | Touchpad click (DS4 only) | +/// Virtual gamepad button codes for / / +/// +/// Use these values with MaaControllerPostClickKey, MaaControllerPostKeyDown, MaaControllerPostKeyUp. +///
Values are based on XUSB (Xbox 360) button flags. DS4 face buttons are mapped to Xbox equivalents.
+/// +/// Xbox 360 buttons: +/// +/// | Value | Button | Description | +///
|---------|---------------------|------------------------| +///
| 0x1000 | A | A button | +///
| 0x2000 | B | B button | +///
| 0x4000 | X | X button | +///
| 0x8000 | Y | Y button | +///
| 0x0100 | LB (Left Shoulder) | Left bumper | +///
| 0x0200 | RB (Right Shoulder) | Right bumper | +///
| 0x0040 | L_THUMB | Left stick click | +///
| 0x0080 | R_THUMB | Right stick click | +///
| 0x0010 | START | Start button | +///
| 0x0020 | BACK | Back button | +///
| 0x0400 | GUIDE | Guide/Home button | +///
| 0x0001 | DPAD_UP | D-pad up | +///
| 0x0002 | DPAD_DOWN | D-pad down | +///
| 0x0004 | DPAD_LEFT | D-pad left | +///
| 0x0008 | DPAD_RIGHT | D-pad right | +///
+/// +/// DualShock 4 buttons (aliases to Xbox buttons): +/// +/// | Value | Button | Xbox Equivalent | Description | +///
|---------|-----------|-----------------|---------------------------| +///
| 0x1000 | CROSS | A | Cross (X) button | +///
| 0x2000 | CIRCLE | B | Circle button | +///
| 0x4000 | SQUARE | X | Square button | +///
| 0x8000 | TRIANGLE | Y | Triangle button | +///
| 0x0100 | L1 | LB | L1 button | +///
| 0x0200 | R1 | RB | R1 button | +///
| 0x0040 | L3 | L_THUMB | Left stick click | +///
| 0x0080 | R3 | R_THUMB | Right stick click | +///
| 0x0010 | OPTIONS | START | Options button | +///
| 0x0020 | SHARE | BACK | Share button | +///
| 0x10000 | PS | - | PS button (DS4 special) | +///
| 0x20000 | TOUCHPAD | - | Touchpad click (DS4 only) | +///
+/// ///
[Flags] public enum GamepadButton : System.UInt64 diff --git a/src/MaaFramework.Binding/Enums/Controllers/GamepadTouch.cs b/src/MaaFramework.Binding/Enums/Controllers/GamepadTouch.cs index af7722b..46af7b8 100644 --- a/src/MaaFramework.Binding/Enums/Controllers/GamepadTouch.cs +++ b/src/MaaFramework.Binding/Enums/Controllers/GamepadTouch.cs @@ -13,21 +13,27 @@ namespace MaaFramework.Binding; /// -/// Virtual gamepad touch contact definitions for touch_down/touch_move/touch_up -/// For gamepad controller, the touch functions are repurposed for analog inputs: -/// - x, y: Analog stick position -/// - pressure: Trigger value (0~255) -/// Contact mapping: -/// | Contact | Input | x range | y range | pressure | Description | -/// |---------------|-----------------|---------------|---------------|------------|----------------------------------| -/// | LeftStick | Left Stick | -32768~32767 | -32768~32767 | ignored | Left analog stick X/Y position | -/// | RightStick | Right Stick | -32768~32767 | -32768~32767 | ignored | Right analog stick X/Y position | -/// | LeftTrigger | Left Trigger | ignored | ignored | 0~255 | Left trigger (LT/L2) value | -/// | RightTrigger | Right Trigger | ignored | ignored | 0~255 | Right trigger (RT/R2) value | -/// Usage: -/// - touch_down(contact, x, y, pressure): Start analog input -/// - touch_move(contact, x, y, pressure): Update analog input position/value -/// - touch_up(contact): Release/reset analog input to center/zero +/// Virtual gamepad touch contact definitions for / / +/// +/// For gamepad controller, the touch functions are repurposed for analog inputs: +///
- x, y: Analog stick position +///
- pressure: Trigger value (0~255)
+/// +/// Contact mapping: +/// +/// | Contact | Input | x range | y range | pressure | Description | +///
|---------------|-----------------|---------------|---------------|------------|----------------------------------| +///
| LeftStick | Left Stick | -32768~32767 | -32768~32767 | ignored | Left analog stick X/Y position | +///
| RightStick | Right Stick | -32768~32767 | -32768~32767 | ignored | Right analog stick X/Y position | +///
| LeftTrigger | Left Trigger | ignored | ignored | 0~255 | Left trigger (LT/L2) value | +///
| RightTrigger | Right Trigger | ignored | ignored | 0~255 | Right trigger (RT/R2) value | +///
+/// +/// Usage: +///
- touch_down(contact, x, y, pressure): Start analog input +///
- touch_move(contact, x, y, pressure): Update analog input position/value +///
- touch_up(contact): Release/reset analog input to center/zero
+/// ///
public enum GamepadTouch : System.UInt64 { diff --git a/src/MaaFramework.Binding/Enums/Controllers/GamepadType.cs b/src/MaaFramework.Binding/Enums/Controllers/GamepadType.cs index 267482f..e219fe3 100644 --- a/src/MaaFramework.Binding/Enums/Controllers/GamepadType.cs +++ b/src/MaaFramework.Binding/Enums/Controllers/GamepadType.cs @@ -14,11 +14,15 @@ namespace MaaFramework.Binding; /// /// Virtual gamepad type. -/// Select ONE type only. -/// | Type | Description | -/// |---------------|------------------------------------------------| -/// | Xbox360 | Microsoft Xbox 360 Controller (wired) | -/// | DualShock4 | Sony DualShock 4 Controller (wired) | +/// +/// Select ONE type only. +/// +/// | Type | Description | +///
|---------------|------------------------------------------------| +///
| Xbox360 | Microsoft Xbox 360 Controller (wired) | +///
| DualShock4 | Sony DualShock 4 Controller (wired) | +///
+/// ///
public enum GamepadType : System.UInt64 { diff --git a/src/MaaFramework.Binding/Enums/Controllers/Win32InputMethod.cs b/src/MaaFramework.Binding/Enums/Controllers/Win32InputMethod.cs index 22b8571..182e239 100644 --- a/src/MaaFramework.Binding/Enums/Controllers/Win32InputMethod.cs +++ b/src/MaaFramework.Binding/Enums/Controllers/Win32InputMethod.cs @@ -14,27 +14,34 @@ namespace MaaFramework.Binding; /// /// Win32 input method +/// /// No bitwise OR, select ONE method only. +/// /// No default value. Client should choose one as default. +/// /// Different applications process input differently, there is no universal solution. -/// | Method | Compatibility | Require Admin | Seize Mouse | Background Support | Notes | -/// |--------------------------|---------------|---------------|--------------|--------------------|------------------------------------------| -/// | Seize | High | No | Yes | No | | -/// | SendMessage | Medium | Maybe | No | Yes | | -/// | PostMessage | Medium | Maybe | No | Yes | | -/// | LegacyEvent | Low | No | Yes | No | | -/// | PostThreadMessage | Low | Maybe | No | Yes | | -/// | SendMessageWithCursorPos | Medium | Maybe | Briefly | Yes | Moves cursor to target position, then restores | -/// | PostMessageWithCursorPos | Medium | Maybe | Briefly | Yes | Moves cursor to target position, then restores | -/// | SendMessageWithWindowPos | Medium | Maybe | No | Yes | Moves window to align target with cursor, then restores | -/// | PostMessageWithWindowPos | Medium | Maybe | No | Yes | Moves window to align target with cursor, then restores | -/// Note: -/// - Admin rights mainly depend on the target application's privilege level. -/// If the target runs as admin, MaaFramework should also run as admin for compatibility. -/// - "WithCursorPos" methods briefly move the cursor to target position, send message, -/// then restore cursor position. This "briefly" seizes the mouse but won't block user operations. -/// - "WithWindowPos" methods briefly move the window so the target aligns with the current cursor -/// position, send message, then restore the window position. The cursor is not moved. +/// +/// | Method | Compatibility | Require Admin | Seize Mouse | Background Support | Notes | +///
|--------------------------|---------------|---------------|--------------|--------------------|------------------------------------------| +///
| Seize | High | No | Yes | No | | +///
| SendMessage | Medium | Maybe | No | Yes | | +///
| PostMessage | Medium | Maybe | No | Yes | | +///
| LegacyEvent | Low | No | Yes | No | | +///
| PostThreadMessage | Low | Maybe | No | Yes | | +///
| SendMessageWithCursorPos | Medium | Maybe | Briefly | Yes | Moves cursor to target position, then restores | +///
| PostMessageWithCursorPos | Medium | Maybe | Briefly | Yes | Moves cursor to target position, then restores | +///
| SendMessageWithWindowPos | Medium | Maybe | No | Yes | Moves window to align target with cursor, then restores | +///
| PostMessageWithWindowPos | Medium | Maybe | No | Yes | Moves window to align target with cursor, then restores | +///
+/// +/// Note: +///
- Admin rights mainly depend on the target application's privilege level. +///
If the target runs as admin, MaaFramework should also run as admin for compatibility. +///
- "WithCursorPos" methods briefly move the cursor to target position, send message, +///
then restore cursor position. This "briefly" seizes the mouse but won't block user operations. +///
- "WithWindowPos" methods briefly move the window so the target aligns with the current cursor +///
position, send message, then restore the window position. The cursor is not moved.
+/// ///
public enum Win32InputMethod : System.UInt64 {