Skip to content

Commit d5a8468

Browse files
CopilotMistEO
andauthored
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>
1 parent 41f49ad commit d5a8468

21 files changed

Lines changed: 509 additions & 18 deletions

src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaController.cs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,48 @@ public static partial class MaaController
2828
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
2929
public static partial MaaControllerHandle MaaCustomControllerCreate([MarshalUsing(typeof(MaaMarshaller))] Custom.IMaaCustomController controller, nint controllerArg);
3030

31+
/// <summary>
32+
/// Create a debug controller that serves images from a directory.
33+
/// </summary>
34+
/// <param name="readPath">Path to a directory of images (or a single image file).</param>
35+
/// <returns>The controller handle, or nint.Zero on failure.</returns>
36+
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
37+
public static partial MaaControllerHandle MaaDbgControllerCreate(string readPath);
38+
39+
/// <summary>
40+
/// Create a macOS controller for native macOS applications.
41+
/// </summary>
42+
/// <param name="windowId">The CGWindowID of the target window (0 for desktop).</param>
43+
/// <param name="screencapMethod">macOS screencap method to use.</param>
44+
/// <param name="inputMethod">macOS input method to use.</param>
45+
/// <returns>The controller handle, or nint.Zero on failure.</returns>
46+
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
47+
public static partial MaaControllerHandle MaaMacOSControllerCreate(uint windowId, MaaMacOSScreencapMethod screencapMethod, MaaMacOSInputMethod inputMethod);
48+
49+
/// <summary>
50+
/// Create an Android native controller.
51+
/// </summary>
52+
/// <param name="configJson">JSON config for the control unit.</param>
53+
/// <returns>The controller handle, or nint.Zero on failure.</returns>
54+
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
55+
public static partial MaaControllerHandle MaaAndroidNativeControllerCreate(string configJson);
56+
57+
/// <summary>
58+
/// Create a replay controller that replays recorded operations.
59+
/// </summary>
60+
/// <param name="recordingPath">Path to the recording JSONL file.</param>
61+
/// <returns>The controller handle, or nint.Zero on failure.</returns>
3162
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
32-
public static partial MaaControllerHandle MaaDbgControllerCreate(string readPath, string writePath, MaaDbgControllerType type, string config);
63+
public static partial MaaControllerHandle MaaReplayControllerCreate(string recordingPath);
64+
65+
/// <summary>
66+
/// Create a record controller that wraps an existing controller and records all operations.
67+
/// </summary>
68+
/// <param name="inner">The inner controller to forward all operations to.</param>
69+
/// <param name="recordingPath">Path to the recording JSONL file to write.</param>
70+
/// <returns>The record controller handle, or nint.Zero on failure.</returns>
71+
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
72+
public static partial MaaControllerHandle MaaRecordControllerCreate(MaaControllerHandle inner, string recordingPath);
3373

3474
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
3575
public static partial void MaaControllerDestroy(MaaControllerHandle ctrl);
@@ -87,6 +127,9 @@ public static partial class MaaController
87127
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
88128
public static partial MaaCtrlId MaaControllerPostTouchUp(MaaControllerHandle ctrl, int contact);
89129

130+
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
131+
public static partial MaaCtrlId MaaControllerPostRelativeMove(MaaControllerHandle ctrl, int dx, int dy);
132+
90133
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
91134
public static partial MaaCtrlId MaaControllerPostKeyDown(MaaControllerHandle ctrl, int keycode);
92135

src/MaaFramework.Binding.Native/Interop/Framework/Instance/MaaTasker.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ public static partial class MaaTasker
109109
[return: MarshalAs(UnmanagedType.U1)]
110110
public static partial bool MaaTaskerGetNodeDetail(MaaTaskerHandle tasker, MaaNodeId nodeId, MaaStringBufferHandle nodeName, out MaaRecoId recoId, out MaaActId actionId, [MarshalAs(UnmanagedType.U1)] out bool completed);
111111

112+
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
113+
[return: MarshalAs(UnmanagedType.U1)]
114+
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);
115+
112116
[LibraryImport("MaaFramework", StringMarshalling = StringMarshalling.Utf8)]
113117
[return: MarshalAs(UnmanagedType.U1)]
114118
public static partial bool MaaTaskerGetTaskDetail(MaaTaskerHandle tasker, MaaTaskId taskId, MaaStringBufferHandle entry, MaaNodeId[] nodeIdList, ref MaaSize nodeIdListSize, out MaaStatus status);

src/MaaFramework.Binding.Native/Interop/Framework/MaaDef.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,15 @@
5050
// No bitwise OR, just set it
5151
global using MaaDbgControllerType = System.UInt64;
5252
global using MaaControllerFeature = System.UInt64;
53+
// macOS controller types
54+
global using MaaMacOSScreencapMethod = System.UInt64;
55+
global using MaaMacOSInputMethod = System.UInt64;
5356
// Gamepad types
5457
global using MaaGamepadType = System.UInt64;
5558
global using MaaGamepadButton = System.UInt64;
5659
global using MaaGamepadTouch = System.UInt64;
60+
// WaitFreezes id
61+
global using MaaWfId = System.Int64;
5762
global using MaaRectHandle = nint;
5863

5964
using System.Runtime.InteropServices;

src/MaaFramework.Binding.Native/Interop/Toolkit/MaaToolkitDef.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
global using MaaToolkitAdbDeviceListHandle = nint;
1515
global using MaaToolkitDesktopWindowHandle = nint;
1616
global using MaaToolkitDesktopWindowListHandle = nint;
17+
global using MaaMacOSPermission = System.Int32;
1718

1819
namespace MaaFramework.Binding.Interop.Native;
1920

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by a tool.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记
11+
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
12+
13+
using System.Runtime.InteropServices;
14+
15+
namespace MaaFramework.Binding.Interop.Native;
16+
17+
public static partial class MaaToolkitMacOS
18+
{
19+
[LibraryImport("MaaToolkit")]
20+
[return: MarshalAs(UnmanagedType.U1)]
21+
public static partial bool MaaToolkitMacOSCheckPermission(MaaMacOSPermission perm);
22+
23+
[LibraryImport("MaaToolkit")]
24+
[return: MarshalAs(UnmanagedType.U1)]
25+
public static partial bool MaaToolkitMacOSRequestPermission(MaaMacOSPermission perm);
26+
27+
[LibraryImport("MaaToolkit")]
28+
[return: MarshalAs(UnmanagedType.U1)]
29+
public static partial bool MaaToolkitMacOSRevealPermissionSettings(MaaMacOSPermission perm);
30+
}

src/MaaFramework.Binding.Native/MaaController.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ public MaaJob TouchMove(int contact, int x, int y, int pressure = 1)
168168
public MaaJob TouchUp(int contact)
169169
=> CreateJob(MaaControllerPostTouchUp(Handle, contact));
170170

171+
/// <inheritdoc/>
172+
/// <remarks>
173+
/// Wrapper of <see cref="MaaControllerPostRelativeMove"/>.
174+
/// </remarks>
175+
public MaaJob RelativeMove(int dx, int dy)
176+
=> CreateJob(MaaControllerPostRelativeMove(Handle, dx, dy));
177+
171178
/// <inheritdoc/>
172179
/// <remarks>
173180
/// Wrapper of <see cref="MaaControllerPostKeyDown"/>.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Diagnostics;
2+
using System.Diagnostics.CodeAnalysis;
3+
using static MaaFramework.Binding.Interop.Native.MaaController;
4+
5+
namespace MaaFramework.Binding;
6+
7+
/// <summary>
8+
/// A wrapper class providing a reference implementation for <see cref="MaaAndroidNativeControllerCreate"/>.
9+
/// </summary>
10+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
11+
public class MaaAndroidNativeController : MaaController
12+
{
13+
private readonly string _debugConfigJson;
14+
15+
[ExcludeFromCodeCoverage(Justification = "Debugger display.")]
16+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
17+
private string DebuggerDisplay => IsInvalid
18+
? $"Invalid {GetType().Name}"
19+
: $"{GetType().Name} {{ ConfigJson = {_debugConfigJson} }}";
20+
21+
/// <summary>
22+
/// Creates a <see cref="MaaAndroidNativeController"/> instance.
23+
/// </summary>
24+
/// <param name="configJson">JSON config for the control unit.</param>
25+
/// <param name="link">Executes <see cref="IMaaController.LinkStart"/> if <see cref="LinkOption.Start"/>; otherwise, not link.</param>
26+
/// <param name="check">Checks LinkStart().Wait() status if <see cref="CheckStatusOption.ThrowIfNotSucceeded"/>; otherwise, not check.</param>
27+
/// <remarks>
28+
/// Wrapper of <see cref="MaaAndroidNativeControllerCreate"/>.
29+
/// <para>This controller is only available on Android.</para>
30+
/// </remarks>
31+
/// <exception cref="ArgumentException"/>
32+
/// <exception cref="MaaJobStatusException"/>
33+
public MaaAndroidNativeController([StringSyntax("Json")] string configJson, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded)
34+
{
35+
ArgumentException.ThrowIfNullOrEmpty(configJson);
36+
37+
var handle = MaaAndroidNativeControllerCreate(configJson);
38+
_ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller);
39+
SetHandle(handle, needReleased: true);
40+
41+
_debugConfigJson = configJson;
42+
43+
if (link == LinkOption.Start)
44+
LinkStartOnConstructed(check, configJson);
45+
}
46+
}

src/MaaFramework.Binding.Native/MaaController/MaaDbgController.cs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,47 +11,37 @@ namespace MaaFramework.Binding;
1111
public class MaaDbgController : MaaController
1212
{
1313
private readonly string _debugReadPath;
14-
private readonly string _debugWritePath;
15-
private readonly DbgControllerType _debugType;
16-
private readonly string _debugConfig;
1714

1815
[ExcludeFromCodeCoverage(Justification = "Debugger display.")]
1916
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
2017
private string DebuggerDisplay => IsInvalid
2118
? $"Invalid {GetType().Name}"
22-
: $"{GetType().Name} {{ ReadFrom = {_debugReadPath}, WriteTo = {_debugWritePath}, Type = {_debugType}, Config = {_debugConfig} }}";
19+
: $"{GetType().Name} {{ ReadFrom = {_debugReadPath} }}";
2320

2421
/// <summary>
2522
/// Creates a <see cref="MaaDbgController"/> instance.
2623
/// </summary>
27-
/// <param name="readPath">The read path.</param>
28-
/// <param name="writePath">The write path.</param>
29-
/// <param name="type">The DebugControllerType.</param>
30-
/// <param name="config">The config.</param>
24+
/// <param name="readPath">Path to a directory of images (or a single image file).</param>
3125
/// <param name="link">Executes <see cref="IMaaController.LinkStart"/> if <see cref="LinkOption.Start"/>; otherwise, not link.</param>
3226
/// <param name="check">Checks LinkStart().Wait() status if <see cref="CheckStatusOption.ThrowIfNotSucceeded"/>; otherwise, not check.</param>
3327
/// <remarks>
3428
/// Wrapper of <see cref="MaaDbgControllerCreate"/>.
29+
/// <para>Images are loaded on connect and cycled through on each screencap request.</para>
30+
/// <para>All input operations (click, swipe, etc.) are no-ops that return success.</para>
3531
/// </remarks>
3632
/// <exception cref="ArgumentException"/>
3733
/// <exception cref="MaaJobStatusException"/>
38-
public MaaDbgController(string readPath, string writePath, DbgControllerType type, [StringSyntax("Json")] string config = "{}", LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded)
34+
public MaaDbgController(string readPath, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded)
3935
{
4036
ArgumentException.ThrowIfNullOrEmpty(readPath);
41-
ArgumentException.ThrowIfNullOrEmpty(writePath);
42-
if (type == DbgControllerType.None) throw new ArgumentException($"Value cannot be {DbgControllerType.None}.", nameof(type));
43-
ArgumentException.ThrowIfNullOrEmpty(config);
4437

45-
var handle = MaaDbgControllerCreate(readPath, writePath, (MaaDbgControllerType)type, config);
38+
var handle = MaaDbgControllerCreate(readPath);
4639
_ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller);
4740
SetHandle(handle, needReleased: true);
4841

4942
_debugReadPath = readPath;
50-
_debugWritePath = writePath;
51-
_debugType = type;
52-
_debugConfig = config;
5343

5444
if (link == LinkOption.Start)
55-
LinkStartOnConstructed(check, readPath, writePath, type, config);
45+
LinkStartOnConstructed(check, readPath);
5646
}
5747
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Diagnostics;
2+
using System.Diagnostics.CodeAnalysis;
3+
using static MaaFramework.Binding.Interop.Native.MaaController;
4+
5+
namespace MaaFramework.Binding;
6+
7+
/// <summary>
8+
/// A wrapper class providing a reference implementation for <see cref="MaaMacOSControllerCreate"/>.
9+
/// </summary>
10+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
11+
public class MaaMacOSController : MaaController
12+
{
13+
private readonly uint _debugWindowId;
14+
private readonly MacOSScreencapMethod _debugScreencapMethod;
15+
private readonly MacOSInputMethod _debugInputMethod;
16+
17+
[ExcludeFromCodeCoverage(Justification = "Debugger display.")]
18+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
19+
private string DebuggerDisplay => IsInvalid
20+
? $"Invalid {GetType().Name}"
21+
: $"{GetType().Name} {{ WindowId = {_debugWindowId}, ScreencapMethod = {_debugScreencapMethod}, InputMethod = {_debugInputMethod} }}";
22+
23+
/// <summary>
24+
/// Creates a <see cref="MaaMacOSController"/> instance.
25+
/// </summary>
26+
/// <param name="windowId">The CGWindowID of the target window (0 for desktop).</param>
27+
/// <param name="screencapMethod">macOS screencap method to use.</param>
28+
/// <param name="inputMethod">macOS input method to use.</param>
29+
/// <param name="link">Executes <see cref="IMaaController.LinkStart"/> if <see cref="LinkOption.Start"/>; otherwise, not link.</param>
30+
/// <param name="check">Checks LinkStart().Wait() status if <see cref="CheckStatusOption.ThrowIfNotSucceeded"/>; otherwise, not check.</param>
31+
/// <remarks>
32+
/// Wrapper of <see cref="MaaMacOSControllerCreate"/>.
33+
/// <para>This controller is designed for native macOS applications.</para>
34+
/// <para>Requires Screen Recording permission for screencap.</para>
35+
/// <para>Input simulation requires Accessibility permission.</para>
36+
/// <para>Some features are not supported: start_app, stop_app, scroll.</para>
37+
/// <para>Only single touch is supported (contact must be 0).</para>
38+
/// </remarks>
39+
/// <exception cref="MaaJobStatusException"/>
40+
public MaaMacOSController(uint windowId, MacOSScreencapMethod screencapMethod, MacOSInputMethod inputMethod, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded)
41+
{
42+
var handle = MaaMacOSControllerCreate(windowId, (MaaMacOSScreencapMethod)screencapMethod, (MaaMacOSInputMethod)inputMethod);
43+
_ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller);
44+
SetHandle(handle, needReleased: true);
45+
46+
_debugWindowId = windowId;
47+
_debugScreencapMethod = screencapMethod;
48+
_debugInputMethod = inputMethod;
49+
50+
if (link == LinkOption.Start)
51+
LinkStartOnConstructed(check, windowId, screencapMethod, inputMethod);
52+
}
53+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System.Diagnostics;
2+
using System.Diagnostics.CodeAnalysis;
3+
using static MaaFramework.Binding.Interop.Native.MaaController;
4+
5+
namespace MaaFramework.Binding;
6+
7+
/// <summary>
8+
/// A wrapper class providing a reference implementation for <see cref="MaaRecordControllerCreate"/>.
9+
/// </summary>
10+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
11+
public class MaaRecordController : MaaController
12+
{
13+
private readonly string _debugRecordingPath;
14+
15+
[ExcludeFromCodeCoverage(Justification = "Debugger display.")]
16+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
17+
private string DebuggerDisplay => IsInvalid
18+
? $"Invalid {GetType().Name}"
19+
: $"{GetType().Name} {{ RecordingPath = {_debugRecordingPath} }}";
20+
21+
/// <summary>
22+
/// Creates a <see cref="MaaRecordController"/> instance.
23+
/// </summary>
24+
/// <param name="inner">The inner controller to forward all operations to. Must not be null.</param>
25+
/// <param name="recordingPath">Path to the recording JSONL file to write.</param>
26+
/// <param name="link">Executes <see cref="IMaaController.LinkStart"/> if <see cref="LinkOption.Start"/>; otherwise, not link.</param>
27+
/// <param name="check">Checks LinkStart().Wait() status if <see cref="CheckStatusOption.ThrowIfNotSucceeded"/>; otherwise, not check.</param>
28+
/// <remarks>
29+
/// Wrapper of <see cref="MaaRecordControllerCreate"/>.
30+
/// <para>The record controller does NOT take ownership of the inner controller.</para>
31+
/// <para>Screenshot images will be saved to a "{stem}-Screenshot" folder in the same directory as this file.</para>
32+
/// <para>The recorded file can be replayed using <see cref="MaaReplayController"/>.</para>
33+
/// </remarks>
34+
/// <exception cref="ArgumentNullException"/>
35+
/// <exception cref="ArgumentException"/>
36+
/// <exception cref="MaaJobStatusException"/>
37+
public MaaRecordController(MaaController inner, string recordingPath, LinkOption link = LinkOption.Start, CheckStatusOption check = CheckStatusOption.ThrowIfNotSucceeded)
38+
{
39+
ArgumentNullException.ThrowIfNull(inner);
40+
ArgumentException.ThrowIfNullOrEmpty(recordingPath);
41+
42+
var handle = MaaRecordControllerCreate(inner.Handle, recordingPath);
43+
_ = MaaControllerAddSink(handle, MaaEventCallback, (nint)MaaHandleType.Controller);
44+
SetHandle(handle, needReleased: true);
45+
46+
_debugRecordingPath = recordingPath;
47+
48+
if (link == LinkOption.Start)
49+
LinkStartOnConstructed(check, inner, recordingPath);
50+
}
51+
}

0 commit comments

Comments
 (0)