MaaFw 2.1.0__py3-none-manylinux2014_x86_64.whl → 5.4.0b1__py3-none-manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
maa/controller.py CHANGED
@@ -1,12 +1,13 @@
1
1
  import json
2
+ import os
3
+ import numpy
2
4
  from abc import abstractmethod
3
5
  from ctypes import c_int32
4
- import os
5
6
  from pathlib import Path
6
- from typing import Any, Dict, Optional, Union
7
+ from typing import Any, Dict, Optional, Tuple, Union
7
8
 
8
9
  from .buffer import ImageBuffer, StringBuffer
9
- from .notification_handler import NotificationHandler
10
+ from .event_sink import EventSink, NotificationType
10
11
  from .define import *
11
12
  from .job import Job, JobWithResult
12
13
  from .library import Library
@@ -14,25 +15,21 @@ from .library import Library
14
15
  __all__ = [
15
16
  "AdbController",
16
17
  "DbgController",
18
+ "PlayCoverController",
17
19
  "Win32Controller",
20
+ "GamepadController",
18
21
  "CustomController",
19
22
  ]
20
23
 
21
24
 
22
25
  class Controller:
23
- _notification_handler: Optional[NotificationHandler]
24
26
  _handle: MaaControllerHandle
25
- _own: bool = False
27
+ _own: bool
26
28
 
27
29
  def __init__(
28
30
  self,
29
31
  handle: Optional[MaaControllerHandle] = None,
30
32
  ):
31
- if not Library.initialized:
32
- raise RuntimeError(
33
- "Library not initialized, please call `library.open()` first."
34
- )
35
-
36
33
  self._set_api_properties()
37
34
 
38
35
  if handle:
@@ -44,40 +41,149 @@ class Controller:
44
41
 
45
42
  def __del__(self):
46
43
  if self._handle and self._own:
47
- Library.framework.MaaControllerDestroy(self._handle)
44
+ Library.framework().MaaControllerDestroy(self._handle)
48
45
 
49
46
  def post_connection(self) -> Job:
50
- ctrl_id = Library.framework.MaaControllerPostConnection(self._handle)
47
+ """异步连接设备 / Asynchronously connect device
48
+
49
+ 这是一个异步操作,会立即返回一个 Job 对象
50
+ This is an asynchronous operation that immediately returns a Job object
51
+
52
+ Returns:
53
+ Job: 作业对象,可通过 status/wait 查询状态 / Job object, can query status via status/wait
54
+ """
55
+ ctrl_id = Library.framework().MaaControllerPostConnection(self._handle)
51
56
  return self._gen_ctrl_job(ctrl_id)
52
57
 
53
- def post_click(self, x: int, y: int) -> Job:
54
- ctrl_id = Library.framework.MaaControllerPostClick(self._handle, x, y)
58
+ def post_click(self, x: int, y: int, contact: int = 0, pressure: int = 1) -> Job:
59
+ """异步点击 / Asynchronously click
60
+
61
+ 这是一个异步操作,会立即返回一个 Job 对象
62
+ This is an asynchronous operation that immediately returns a Job object
63
+
64
+ Args:
65
+ x: x 坐标 / x coordinate
66
+ y: y 坐标 / y coordinate
67
+ contact: 触点编号 (Adb 控制器: 手指编号; Win32 控制器: 鼠标按键 0:左键, 1:右键, 2:中键) / Contact number (Adb controller: finger number; Win32 controller: mouse button 0:left, 1:right, 2:middle)
68
+ pressure: 触点力度 / Contact pressure
69
+
70
+ Returns:
71
+ Job: 作业对象,可通过 status/wait 查询状态 / Job object, can query status via status/wait
72
+ """
73
+ ctrl_id = Library.framework().MaaControllerPostClickV2(
74
+ self._handle, x, y, contact, pressure
75
+ )
55
76
  return self._gen_ctrl_job(ctrl_id)
56
77
 
57
- def post_swipe(self, x1: int, y1: int, x2: int, y2: int, duration: int) -> Job:
58
- ctrl_id = Library.framework.MaaControllerPostSwipe(
59
- self._handle, x1, y1, x2, y2, duration
78
+ def post_swipe(
79
+ self,
80
+ x1: int,
81
+ y1: int,
82
+ x2: int,
83
+ y2: int,
84
+ duration: int,
85
+ contact: int = 0,
86
+ pressure: int = 1,
87
+ ) -> Job:
88
+ """滑动 / Swipe
89
+
90
+ Args:
91
+ x1: 起点 x 坐标 / Start x coordinate
92
+ y1: 起点 y 坐标 / Start y coordinate
93
+ x2: 终点 x 坐标 / End x coordinate
94
+ y2: 终点 y 坐标 / End y coordinate
95
+ duration: 滑动时长(毫秒) / Swipe duration in milliseconds
96
+ contact: 触点编号 (Adb 控制器: 手指编号; Win32 控制器: 鼠标按键 0:左键, 1:右键, 2:中键) / Contact number (Adb controller: finger number; Win32 controller: mouse button 0:left, 1:right, 2:middle)
97
+ pressure: 触点力度 / Contact pressure
98
+
99
+ Returns:
100
+ Job: 作业对象 / Job object
101
+ """
102
+ ctrl_id = Library.framework().MaaControllerPostSwipeV2(
103
+ self._handle, x1, y1, x2, y2, duration, contact, pressure
60
104
  )
61
105
  return self._gen_ctrl_job(ctrl_id)
62
106
 
63
107
  def post_press_key(self, key: int) -> Job:
64
- ctrl_id = Library.framework.MaaControllerPostPressKey(self._handle, key)
108
+ """
109
+ Deprecated: Use post_click_key instead.
110
+ """
111
+ return self.post_click_key(key)
112
+
113
+ def post_click_key(self, key: int) -> Job:
114
+ """单击按键 / Click key
115
+
116
+ Args:
117
+ key: 虚拟键码 / Virtual key code
118
+
119
+ Returns:
120
+ Job: 作业对象 / Job object
121
+ """
122
+ ctrl_id = Library.framework().MaaControllerPostClickKey(self._handle, key)
123
+ return self._gen_ctrl_job(ctrl_id)
124
+
125
+ def post_key_down(self, key: int) -> Job:
126
+ """按下键 / Key down
127
+
128
+ Args:
129
+ key: 虚拟键码 / Virtual key code
130
+
131
+ Returns:
132
+ Job: 作业对象 / Job object
133
+ """
134
+ ctrl_id = Library.framework().MaaControllerPostKeyDown(self._handle, key)
135
+ return self._gen_ctrl_job(ctrl_id)
136
+
137
+ def post_key_up(self, key: int) -> Job:
138
+ """抬起键 / Key up
139
+
140
+ Args:
141
+ key: 虚拟键码 / Virtual key code
142
+
143
+ Returns:
144
+ Job: 作业对象 / Job object
145
+ """
146
+ ctrl_id = Library.framework().MaaControllerPostKeyUp(self._handle, key)
65
147
  return self._gen_ctrl_job(ctrl_id)
66
148
 
67
149
  def post_input_text(self, text: str) -> Job:
68
- ctrl_id = Library.framework.MaaControllerPostInputText(
150
+ """输入文本 / Input text
151
+
152
+ Args:
153
+ text: 要输入的文本 / Text to input
154
+
155
+ Returns:
156
+ Job: 作业对象 / Job object
157
+ """
158
+ ctrl_id = Library.framework().MaaControllerPostInputText(
69
159
  self._handle, text.encode()
70
160
  )
71
161
  return self._gen_ctrl_job(ctrl_id)
72
162
 
73
163
  def post_start_app(self, intent: str) -> Job:
74
- ctrl_id = Library.framework.MaaControllerPostStartApp(
164
+ """启动应用 / Start app
165
+
166
+ Args:
167
+ intent: 目标应用 (Adb 控制器: package name 或 activity) / Target app (Adb controller: package name or activity)
168
+
169
+ Returns:
170
+ Job: 作业对象 / Job object
171
+ """
172
+ ctrl_id = Library.framework().MaaControllerPostStartApp(
75
173
  self._handle, intent.encode()
76
174
  )
77
175
  return self._gen_ctrl_job(ctrl_id)
78
176
 
79
177
  def post_stop_app(self, intent: str) -> Job:
80
- ctrl_id = Library.framework.MaaControllerPostStopApp(
178
+ """关闭应用 / Stop app
179
+
180
+ Args:
181
+ intent: 目标应用 (Adb 控制器: package name) / Target app (Adb controller: package name)
182
+
183
+ Returns:
184
+ Job: 作业对象 / Job object
185
+ """
186
+ ctrl_id = Library.framework().MaaControllerPostStopApp(
81
187
  self._handle, intent.encode()
82
188
  )
83
189
  return self._gen_ctrl_job(ctrl_id)
@@ -85,7 +191,18 @@ class Controller:
85
191
  def post_touch_down(
86
192
  self, x: int, y: int, contact: int = 0, pressure: int = 1
87
193
  ) -> Job:
88
- ctrl_id = Library.framework.MaaControllerPostTouchDown(
194
+ """按下 / Touch down
195
+
196
+ Args:
197
+ x: x 坐标 / x coordinate
198
+ y: y 坐标 / y coordinate
199
+ contact: 触点编号 (Adb 控制器: 手指编号; Win32 控制器: 鼠标按键 0:左键, 1:右键, 2:中键) / Contact number (Adb controller: finger number; Win32 controller: mouse button 0:left, 1:right, 2:middle)
200
+ pressure: 触点力度 / Contact pressure
201
+
202
+ Returns:
203
+ Job: 作业对象 / Job object
204
+ """
205
+ ctrl_id = Library.framework().MaaControllerPostTouchDown(
89
206
  self._handle, contact, x, y, pressure
90
207
  )
91
208
  return self._gen_ctrl_job(ctrl_id)
@@ -93,17 +210,58 @@ class Controller:
93
210
  def post_touch_move(
94
211
  self, x: int, y: int, contact: int = 0, pressure: int = 1
95
212
  ) -> Job:
96
- ctrl_id = Library.framework.MaaControllerPostTouchMove(
213
+ """移动 / Move
214
+
215
+ Args:
216
+ x: x 坐标 / x coordinate
217
+ y: y 坐标 / y coordinate
218
+ contact: 触点编号 (Adb 控制器: 手指编号; Win32 控制器: 鼠标按键 0:左键, 1:右键, 2:中键) / Contact number (Adb controller: finger number; Win32 controller: mouse button 0:left, 1:right, 2:middle)
219
+ pressure: 触点力度 / Contact pressure
220
+
221
+ Returns:
222
+ Job: 作业对象 / Job object
223
+ """
224
+ ctrl_id = Library.framework().MaaControllerPostTouchMove(
97
225
  self._handle, contact, x, y, pressure
98
226
  )
99
227
  return self._gen_ctrl_job(ctrl_id)
100
228
 
101
229
  def post_touch_up(self, contact: int = 0) -> Job:
102
- ctrl_id = Library.framework.MaaControllerPostTouchUp(self._handle, contact)
230
+ """抬起 / Touch up
231
+
232
+ Args:
233
+ contact: 触点编号 (Adb 控制器: 手指编号; Win32 控制器: 鼠标按键 0:左键, 1:右键, 2:中键) / Contact number (Adb controller: finger number; Win32 controller: mouse button 0:left, 1:right, 2:middle)
234
+
235
+ Returns:
236
+ Job: 作业对象 / Job object
237
+ """
238
+ ctrl_id = Library.framework().MaaControllerPostTouchUp(self._handle, contact)
239
+ return self._gen_ctrl_job(ctrl_id)
240
+
241
+ def post_scroll(self, dx: int, dy: int) -> Job:
242
+ """滚动 / Scroll
243
+
244
+ Args:
245
+ dx: 水平滚动距离,正值向右滚动,负值向左滚动 / Horizontal scroll distance, positive for right, negative for left
246
+ dy: 垂直滚动距离,正值向上滚动,负值向下滚动 / Vertical scroll distance, positive for up, negative for down
247
+
248
+ Returns:
249
+ Job: 作业对象 / Job object
250
+
251
+ Note:
252
+ 不是所有控制器都支持滚动操作 / Not all controllers support scroll operation
253
+ 建议使用 120 的整数倍(WHEEL_DELTA)以获得最佳兼容性 / Using multiples of 120 (WHEEL_DELTA) is recommended
254
+ """
255
+ ctrl_id = Library.framework().MaaControllerPostScroll(self._handle, dx, dy)
103
256
  return self._gen_ctrl_job(ctrl_id)
104
257
 
105
258
  def post_screencap(self) -> JobWithResult:
106
- ctrl_id = Library.framework.MaaControllerPostScreencap(self._handle)
259
+ """截图 / Screenshot
260
+
261
+ Returns:
262
+ JobWithResult: 作业对象,可通过 result 获取截图 / Job object, can get screenshot via result
263
+ """
264
+ ctrl_id = Library.framework().MaaControllerPostScreencap(self._handle)
107
265
  return JobWithResult(
108
266
  ctrl_id,
109
267
  self._status,
@@ -113,28 +271,127 @@ class Controller:
113
271
 
114
272
  @property
115
273
  def cached_image(self) -> numpy.ndarray:
274
+ """获取最新一次截图 / Get the latest screenshot
275
+
276
+ Returns:
277
+ numpy.ndarray: 截图图像 / Screenshot image
278
+
279
+ Raises:
280
+ RuntimeError: 如果获取失败
281
+
282
+ Note:
283
+ 返回的图像是经过缩放的,尺寸根据截图目标尺寸设置(长边/短边)而定,可能与设备原始分辨率不同。
284
+ 使用 resolution 属性可获取设备的原始(未缩放)分辨率。
285
+
286
+ The returned image is scaled according to the screenshot target size settings (long side / short side).
287
+ The image dimensions may differ from the raw device resolution.
288
+ Use the resolution property to get the raw (unscaled) device resolution.
289
+ """
116
290
  image_buffer = ImageBuffer()
117
- if not Library.framework.MaaControllerCachedImage(
291
+ if not Library.framework().MaaControllerCachedImage(
118
292
  self._handle, image_buffer._handle
119
293
  ):
120
294
  raise RuntimeError("Failed to get cached image.")
121
295
  return image_buffer.get()
122
296
 
297
+ def post_shell(self, cmd: str, timeout: int = 20000) -> JobWithResult:
298
+ """执行 shell 命令 (仅 ADB 控制器) / Execute shell command (ADB only)
299
+
300
+ Args:
301
+ cmd: shell 命令 / shell command
302
+ timeout: 超时时间(毫秒),默认 20000 / Timeout in milliseconds, default 20000
303
+
304
+ Returns:
305
+ JobWithResult: 作业对象,可通过 result 获取命令输出 / Job object, can get output via result
306
+ """
307
+ ctrl_id = Library.framework().MaaControllerPostShell(
308
+ self._handle, cmd.encode("utf-8"), timeout
309
+ )
310
+ return JobWithResult(
311
+ ctrl_id,
312
+ self._status,
313
+ self._wait,
314
+ self._get_shell_output,
315
+ )
316
+
317
+ @property
318
+ def shell_output(self) -> str:
319
+ """获取最近一次 shell 命令输出 / Get the latest shell command output
320
+
321
+ Returns:
322
+ str: shell 命令输出 / shell command output
323
+
324
+ Raises:
325
+ RuntimeError: 如果获取失败
326
+ """
327
+ string_buffer = StringBuffer()
328
+ if not Library.framework().MaaControllerGetShellOutput(
329
+ self._handle, string_buffer._handle
330
+ ):
331
+ raise RuntimeError("Failed to get shell output.")
332
+ return string_buffer.get()
333
+
123
334
  @property
124
335
  def connected(self) -> bool:
125
- return bool(Library.framework.MaaControllerConnected(self._handle))
336
+ """判断是否已连接 / Check if connected
337
+
338
+ Returns:
339
+ bool: 是否已连接 / Whether connected
340
+ """
341
+ return bool(Library.framework().MaaControllerConnected(self._handle))
126
342
 
127
343
  @property
128
344
  def uuid(self) -> str:
345
+ """获取设备 uuid / Get device uuid
346
+
347
+ Returns:
348
+ str: 设备 uuid / Device uuid
349
+
350
+ Raises:
351
+ RuntimeError: 如果获取失败
352
+ """
129
353
  buffer = StringBuffer()
130
- if not Library.framework.MaaControllerGetUuid(self._handle, buffer._handle):
354
+ if not Library.framework().MaaControllerGetUuid(self._handle, buffer._handle):
131
355
  raise RuntimeError("Failed to get UUID.")
132
356
  return buffer.get()
133
357
 
358
+ @property
359
+ def resolution(self) -> Tuple[int, int]:
360
+ """获取设备原始(未缩放)分辨率 / Get the raw (unscaled) device resolution
361
+
362
+ Returns:
363
+ Tuple[int, int]: (宽度, 高度),获取失败时返回 (0, 0) / (width, height), returns (0, 0) on failure
364
+
365
+ Note:
366
+ 返回的是设备屏幕的实际分辨率,未经任何缩放处理。
367
+ 而通过 cached_image 获取的截图是经过缩放的,其尺寸可能与此原始分辨率不同。
368
+ 需要在首次截图后才能获取到有效值,否则返回 (0, 0)。
369
+
370
+ This returns the actual device screen resolution before any scaling.
371
+ The screenshot obtained via cached_image is scaled according to the screenshot target size settings,
372
+ so its dimensions may differ from this raw resolution.
373
+ Valid values are only available after the first screenshot is taken, otherwise returns (0, 0).
374
+ """
375
+ width = ctypes.c_int32()
376
+ height = ctypes.c_int32()
377
+ if not Library.framework().MaaControllerGetResolution(
378
+ self._handle, ctypes.byref(width), ctypes.byref(height)
379
+ ):
380
+ return (0, 0)
381
+ return (width.value, height.value)
382
+
134
383
  def set_screenshot_target_long_side(self, long_side: int) -> bool:
384
+ """设置截图缩放长边到指定长度 / Set screenshot scaling long side to specified length
385
+
386
+ Args:
387
+ long_side: 长边长度 / Long side length
388
+
389
+ Returns:
390
+ bool: 是否成功 / Whether successful
391
+ """
135
392
  cint = ctypes.c_int32(long_side)
136
393
  return bool(
137
- Library.framework.MaaControllerSetOption(
394
+ Library.framework().MaaControllerSetOption(
138
395
  self._handle,
139
396
  MaaOption(MaaCtrlOptionEnum.ScreenshotTargetLongSide),
140
397
  ctypes.pointer(cint),
@@ -143,9 +400,17 @@ class Controller:
143
400
  )
144
401
 
145
402
  def set_screenshot_target_short_side(self, short_side: int) -> bool:
403
+ """设置截图缩放短边到指定长度 / Set screenshot scaling short side to specified length
404
+
405
+ Args:
406
+ short_side: 短边长度 / Short side length
407
+
408
+ Returns:
409
+ bool: 是否成功 / Whether successful
410
+ """
146
411
  cint = ctypes.c_int32(short_side)
147
412
  return bool(
148
- Library.framework.MaaControllerSetOption(
413
+ Library.framework().MaaControllerSetOption(
149
414
  self._handle,
150
415
  MaaOption(MaaCtrlOptionEnum.ScreenshotTargetShortSide),
151
416
  ctypes.pointer(cint),
@@ -153,17 +418,77 @@ class Controller:
153
418
  )
154
419
  )
155
420
 
421
+ def set_screenshot_use_raw_size(self, enable: bool) -> bool:
422
+ """设置截图不缩放 / Set screenshot use raw size without scaling
423
+
424
+ 注意:此选项可能导致在不同分辨率的设备上坐标不正确
425
+ Note: This option may cause incorrect coordinates on devices with different resolutions
426
+
427
+ Args:
428
+ enable: 是否启用 / Whether to enable
429
+
430
+ Returns:
431
+ bool: 是否成功 / Whether successful
432
+ """
433
+ cbool = MaaBool(enable)
434
+ return bool(
435
+ Library.framework().MaaControllerSetOption(
436
+ self._handle,
437
+ MaaOption(MaaCtrlOptionEnum.ScreenshotUseRawSize),
438
+ ctypes.pointer(cbool),
439
+ ctypes.sizeof(MaaBool),
440
+ )
441
+ )
442
+
443
+ _sink_holder: Dict[int, "ControllerEventSink"] = {}
444
+
445
+ def add_sink(self, sink: "ControllerEventSink") -> Optional[int]:
446
+ """添加控制器事件监听器 / Add controller event listener
447
+
448
+ Args:
449
+ sink: 事件监听器 / Event sink
450
+
451
+ Returns:
452
+ Optional[int]: 监听器 id,失败返回 None / Listener id, or None if failed
453
+ """
454
+ sink_id = int(
455
+ Library.framework().MaaControllerAddSink(
456
+ self._handle, *EventSink._gen_c_param(sink)
457
+ )
458
+ )
459
+ if sink_id == MaaInvalidId:
460
+ return None
461
+
462
+ self._sink_holder[sink_id] = sink
463
+ return sink_id
464
+
465
+ def remove_sink(self, sink_id: int) -> None:
466
+ """移除控制器事件监听器 / Remove controller event listener
467
+
468
+ Args:
469
+ sink_id: 监听器 id / Listener id
470
+ """
471
+ Library.framework().MaaControllerRemoveSink(self._handle, sink_id)
472
+ self._sink_holder.pop(sink_id)
473
+
474
+ def clear_sinks(self) -> None:
475
+ """清除所有控制器事件监听器 / Clear all controller event listeners"""
476
+ Library.framework().MaaControllerClearSinks(self._handle)
477
+
156
478
  ### private ###
157
479
 
158
480
  def _status(self, maaid: int) -> MaaStatus:
159
- return Library.framework.MaaControllerStatus(self._handle, maaid)
481
+ return Library.framework().MaaControllerStatus(self._handle, maaid)
160
482
 
161
483
  def _wait(self, maaid: int) -> MaaStatus:
162
- return Library.framework.MaaControllerWait(self._handle, maaid)
484
+ return Library.framework().MaaControllerWait(self._handle, maaid)
163
485
 
164
486
  def _get_screencap(self, _: int) -> numpy.ndarray:
165
487
  return self.cached_image
166
488
 
489
+ def _get_shell_output(self, _: int) -> str:
490
+ return self.shell_output
491
+
167
492
  def _gen_ctrl_job(self, ctrlid: MaaCtrlId) -> Job:
168
493
  return Job(
169
494
  ctrlid,
@@ -179,68 +504,99 @@ class Controller:
179
504
  return
180
505
  Controller._api_properties_initialized = True
181
506
 
182
- Library.framework.MaaControllerDestroy.restype = None
183
- Library.framework.MaaControllerDestroy.argtypes = [MaaControllerHandle]
507
+ Library.framework().MaaControllerDestroy.restype = None
508
+ Library.framework().MaaControllerDestroy.argtypes = [MaaControllerHandle]
184
509
 
185
- Library.framework.MaaControllerSetOption.restype = MaaBool
186
- Library.framework.MaaControllerSetOption.argtypes = [
510
+ Library.framework().MaaControllerSetOption.restype = MaaBool
511
+ Library.framework().MaaControllerSetOption.argtypes = [
187
512
  MaaControllerHandle,
188
513
  MaaCtrlOption,
189
514
  MaaOptionValue,
190
515
  MaaOptionValueSize,
191
516
  ]
192
517
 
193
- Library.framework.MaaControllerPostConnection.restype = MaaCtrlId
194
- Library.framework.MaaControllerPostConnection.argtypes = [MaaControllerHandle]
518
+ Library.framework().MaaControllerPostConnection.restype = MaaCtrlId
519
+ Library.framework().MaaControllerPostConnection.argtypes = [MaaControllerHandle]
195
520
 
196
- Library.framework.MaaControllerPostClick.restype = MaaCtrlId
197
- Library.framework.MaaControllerPostClick.argtypes = [
521
+ Library.framework().MaaControllerPostClick.restype = MaaCtrlId
522
+ Library.framework().MaaControllerPostClick.argtypes = [
198
523
  MaaControllerHandle,
199
524
  c_int32,
200
525
  c_int32,
201
526
  ]
202
527
 
203
- Library.framework.MaaControllerPostSwipe.restype = MaaCtrlId
204
- Library.framework.MaaControllerPostSwipe.argtypes = [
528
+ Library.framework().MaaControllerPostClickV2.restype = MaaCtrlId
529
+ Library.framework().MaaControllerPostClickV2.argtypes = [
205
530
  MaaControllerHandle,
206
531
  c_int32,
207
532
  c_int32,
208
533
  c_int32,
209
534
  c_int32,
535
+ ]
536
+
537
+ Library.framework().MaaControllerPostSwipe.restype = MaaCtrlId
538
+ Library.framework().MaaControllerPostSwipe.argtypes = [
539
+ MaaControllerHandle,
540
+ c_int32,
541
+ c_int32,
542
+ c_int32,
543
+ c_int32,
544
+ c_int32,
545
+ ]
546
+
547
+ Library.framework().MaaControllerPostSwipeV2.restype = MaaCtrlId
548
+ Library.framework().MaaControllerPostSwipeV2.argtypes = [
549
+ MaaControllerHandle,
550
+ c_int32,
551
+ c_int32,
552
+ c_int32,
553
+ c_int32,
554
+ c_int32,
555
+ c_int32,
210
556
  c_int32,
211
557
  ]
212
558
 
213
- Library.framework.MaaControllerPostPressKey.restype = MaaCtrlId
214
- Library.framework.MaaControllerPostPressKey.argtypes = [
559
+ Library.framework().MaaControllerPostClickKey.restype = MaaCtrlId
560
+ Library.framework().MaaControllerPostClickKey.argtypes = [
215
561
  MaaControllerHandle,
216
562
  c_int32,
217
563
  ]
218
564
 
219
- Library.framework.MaaControllerPostInputText.restype = MaaCtrlId
220
- Library.framework.MaaControllerPostInputText.argtypes = [
565
+ Library.framework().MaaControllerPostKeyDown.restype = MaaCtrlId
566
+ Library.framework().MaaControllerPostKeyDown.argtypes = [
567
+ MaaControllerHandle,
568
+ c_int32,
569
+ ]
570
+ Library.framework().MaaControllerPostKeyUp.restype = MaaCtrlId
571
+ Library.framework().MaaControllerPostKeyUp.argtypes = [
572
+ MaaControllerHandle,
573
+ c_int32,
574
+ ]
575
+ Library.framework().MaaControllerPostInputText.restype = MaaCtrlId
576
+ Library.framework().MaaControllerPostInputText.argtypes = [
221
577
  MaaControllerHandle,
222
578
  ctypes.c_char_p,
223
579
  ]
224
580
 
225
- Library.framework.MaaControllerPostScreencap.restype = MaaCtrlId
226
- Library.framework.MaaControllerPostScreencap.argtypes = [
581
+ Library.framework().MaaControllerPostScreencap.restype = MaaCtrlId
582
+ Library.framework().MaaControllerPostScreencap.argtypes = [
227
583
  MaaControllerHandle,
228
584
  ]
229
585
 
230
- Library.framework.MaaControllerPostStartApp.restype = MaaCtrlId
231
- Library.framework.MaaControllerPostStartApp.argtypes = [
586
+ Library.framework().MaaControllerPostStartApp.restype = MaaCtrlId
587
+ Library.framework().MaaControllerPostStartApp.argtypes = [
232
588
  MaaControllerHandle,
233
589
  ctypes.c_char_p,
234
590
  ]
235
591
 
236
- Library.framework.MaaControllerPostStopApp.restype = MaaCtrlId
237
- Library.framework.MaaControllerPostStopApp.argtypes = [
592
+ Library.framework().MaaControllerPostStopApp.restype = MaaCtrlId
593
+ Library.framework().MaaControllerPostStopApp.argtypes = [
238
594
  MaaControllerHandle,
239
595
  ctypes.c_char_p,
240
596
  ]
241
597
 
242
- Library.framework.MaaControllerPostTouchDown.restype = MaaCtrlId
243
- Library.framework.MaaControllerPostTouchDown.argtypes = [
598
+ Library.framework().MaaControllerPostTouchDown.restype = MaaCtrlId
599
+ Library.framework().MaaControllerPostTouchDown.argtypes = [
244
600
  MaaControllerHandle,
245
601
  c_int32,
246
602
  c_int32,
@@ -248,8 +604,8 @@ class Controller:
248
604
  c_int32,
249
605
  ]
250
606
 
251
- Library.framework.MaaControllerPostTouchMove.restype = MaaCtrlId
252
- Library.framework.MaaControllerPostTouchMove.argtypes = [
607
+ Library.framework().MaaControllerPostTouchMove.restype = MaaCtrlId
608
+ Library.framework().MaaControllerPostTouchMove.argtypes = [
253
609
  MaaControllerHandle,
254
610
  c_int32,
255
611
  c_int32,
@@ -257,40 +613,77 @@ class Controller:
257
613
  c_int32,
258
614
  ]
259
615
 
260
- Library.framework.MaaControllerPostTouchUp.restype = MaaCtrlId
261
- Library.framework.MaaControllerPostTouchUp.argtypes = [
616
+ Library.framework().MaaControllerPostTouchUp.restype = MaaCtrlId
617
+ Library.framework().MaaControllerPostTouchUp.argtypes = [
262
618
  MaaControllerHandle,
263
619
  c_int32,
264
620
  ]
265
- Library.framework.MaaControllerStatus.restype = MaaStatus
266
- Library.framework.MaaControllerStatus.argtypes = [
621
+
622
+ Library.framework().MaaControllerPostScroll.restype = MaaCtrlId
623
+ Library.framework().MaaControllerPostScroll.argtypes = [
624
+ MaaControllerHandle,
625
+ c_int32,
626
+ c_int32,
627
+ ]
628
+
629
+ Library.framework().MaaControllerStatus.restype = MaaStatus
630
+ Library.framework().MaaControllerStatus.argtypes = [
267
631
  MaaControllerHandle,
268
632
  MaaCtrlId,
269
633
  ]
270
634
 
271
- Library.framework.MaaControllerWait.restype = MaaStatus
272
- Library.framework.MaaControllerWait.argtypes = [
635
+ Library.framework().MaaControllerWait.restype = MaaStatus
636
+ Library.framework().MaaControllerWait.argtypes = [
273
637
  MaaControllerHandle,
274
638
  MaaCtrlId,
275
639
  ]
276
640
 
277
- Library.framework.MaaControllerConnected.restype = MaaBool
278
- Library.framework.MaaControllerConnected.argtypes = [MaaControllerHandle]
641
+ Library.framework().MaaControllerConnected.restype = MaaBool
642
+ Library.framework().MaaControllerConnected.argtypes = [MaaControllerHandle]
279
643
 
280
- Library.framework.MaaControllerCachedImage.restype = MaaBool
281
- Library.framework.MaaControllerCachedImage.argtypes = [
644
+ Library.framework().MaaControllerCachedImage.restype = MaaBool
645
+ Library.framework().MaaControllerCachedImage.argtypes = [
282
646
  MaaControllerHandle,
283
647
  MaaImageBufferHandle,
284
648
  ]
285
649
 
286
- Library.framework.MaaControllerGetUuid.restype = MaaBool
287
- Library.framework.MaaControllerGetUuid.argtypes = [
650
+ Library.framework().MaaControllerGetUuid.restype = MaaBool
651
+ Library.framework().MaaControllerGetUuid.argtypes = [
288
652
  MaaControllerHandle,
289
653
  MaaStringBufferHandle,
290
654
  ]
291
655
 
656
+ Library.framework().MaaControllerGetResolution.restype = MaaBool
657
+ Library.framework().MaaControllerGetResolution.argtypes = [
658
+ MaaControllerHandle,
659
+ ctypes.POINTER(ctypes.c_int32),
660
+ ctypes.POINTER(ctypes.c_int32),
661
+ ]
662
+
663
+ Library.framework().MaaControllerAddSink.restype = MaaSinkId
664
+ Library.framework().MaaControllerAddSink.argtypes = [
665
+ MaaControllerHandle,
666
+ MaaEventCallback,
667
+ ctypes.c_void_p,
668
+ ]
669
+
670
+ Library.framework().MaaControllerRemoveSink.restype = None
671
+ Library.framework().MaaControllerRemoveSink.argtypes = [
672
+ MaaControllerHandle,
673
+ MaaSinkId,
674
+ ]
675
+
676
+ Library.framework().MaaControllerClearSinks.restype = None
677
+ Library.framework().MaaControllerClearSinks.argtypes = [MaaControllerHandle]
678
+
292
679
 
293
680
  class AdbController(Controller):
681
+ """Adb 控制器 / Adb controller
682
+
683
+ 截图方式和输入方式会在启动时进行测速, 选择最快的方案
684
+ Screenshot and input methods will be speed tested at startup, selecting the fastest option
685
+ """
686
+
294
687
  AGENT_BINARY_PATH = os.path.join(
295
688
  os.path.dirname(__file__),
296
689
  "../MaaAgentBinary",
@@ -304,21 +697,30 @@ class AdbController(Controller):
304
697
  input_methods: int = MaaAdbInputMethodEnum.Default,
305
698
  config: Dict[str, Any] = {},
306
699
  agent_path: Union[str, Path] = AGENT_BINARY_PATH,
307
- notification_handler: Optional[NotificationHandler] = None,
308
700
  ):
701
+ """创建 Adb 控制器 / Create Adb controller
702
+
703
+ Args:
704
+ adb_path: adb 路径 / adb path
705
+ address: 连接地址 / connection address
706
+ screencap_methods: 所有可使用的截图方式 / all available screenshot methods
707
+ input_methods: 所有可使用的输入方式 / all available input methods
708
+ config: 额外配置 / extra config
709
+ agent_path: MaaAgentBinary 路径 / MaaAgentBinary path
710
+
711
+ Raises:
712
+ RuntimeError: 如果创建失败
713
+ """
309
714
  super().__init__()
310
715
  self._set_adb_api_properties()
311
716
 
312
- self._notification_handler = notification_handler
313
-
314
- self._handle = Library.framework.MaaAdbControllerCreate(
717
+ self._handle = Library.framework().MaaAdbControllerCreate(
315
718
  str(adb_path).encode(),
316
719
  address.encode(),
317
720
  MaaAdbScreencapMethod(screencap_methods),
318
721
  MaaAdbInputMethod(input_methods),
319
722
  json.dumps(config, ensure_ascii=False).encode(),
320
723
  str(agent_path).encode(),
321
- *NotificationHandler._gen_c_param(self._notification_handler)
322
724
  )
323
725
 
324
726
  if not self._handle:
@@ -326,54 +728,103 @@ class AdbController(Controller):
326
728
 
327
729
  def _set_adb_api_properties(self):
328
730
 
329
- Library.framework.MaaAdbControllerCreate.restype = MaaControllerHandle
330
- Library.framework.MaaAdbControllerCreate.argtypes = [
731
+ Library.framework().MaaAdbControllerCreate.restype = MaaControllerHandle
732
+ Library.framework().MaaAdbControllerCreate.argtypes = [
331
733
  ctypes.c_char_p,
332
734
  ctypes.c_char_p,
333
735
  MaaAdbScreencapMethod,
334
736
  MaaAdbInputMethod,
335
737
  ctypes.c_char_p,
336
738
  ctypes.c_char_p,
337
- MaaNotificationCallback,
338
- ctypes.c_void_p,
339
739
  ]
340
740
 
341
741
 
342
742
  class Win32Controller(Controller):
743
+ """Win32 控制器 / Win32 controller"""
343
744
 
344
745
  def __init__(
345
746
  self,
346
- hWnd: ctypes.c_void_p,
347
- screencap_method: int = MaaWin32ScreencapMethodEnum.DXGI_DesktopDup,
348
- input_method: int = MaaWin32InputMethodEnum.Seize,
349
- notification_handler: Optional[NotificationHandler] = None,
747
+ hWnd: Union[ctypes.c_void_p, int, None],
748
+ screencap_method: int = MaaWin32ScreencapMethodEnum.PrintWindow,
749
+ mouse_method: int = MaaWin32InputMethodEnum.Seize,
750
+ keyboard_method: int = MaaWin32InputMethodEnum.Seize,
350
751
  ):
752
+ """创建 Win32 控制器 / Create Win32 controller
753
+
754
+ Args:
755
+ hWnd: 窗口句柄 / window handle
756
+ screencap_method: 使用的截图方式 / screenshot method used
757
+ mouse_method: 使用的鼠标输入方式 / mouse input method used
758
+ keyboard_method: 使用的键盘输入方式 / keyboard input method used
759
+
760
+ Raises:
761
+ RuntimeError: 如果创建失败
762
+ """
351
763
  super().__init__()
352
764
  self._set_win32_api_properties()
353
765
 
354
- self._notification_handler = notification_handler
355
- self._handle = Library.framework.MaaWin32ControllerCreate(
766
+ self._handle = Library.framework().MaaWin32ControllerCreate(
356
767
  hWnd,
357
768
  MaaWin32ScreencapMethod(screencap_method),
358
- MaaWin32InputMethod(input_method),
359
- *NotificationHandler._gen_c_param(self._notification_handler)
769
+ MaaWin32InputMethod(mouse_method),
770
+ MaaWin32InputMethod(keyboard_method),
360
771
  )
361
772
 
362
773
  if not self._handle:
363
774
  raise RuntimeError("Failed to create Win32 controller.")
364
775
 
365
776
  def _set_win32_api_properties(self):
366
- Library.framework.MaaWin32ControllerCreate.restype = MaaControllerHandle
367
- Library.framework.MaaWin32ControllerCreate.argtypes = [
777
+ Library.framework().MaaWin32ControllerCreate.restype = MaaControllerHandle
778
+ Library.framework().MaaWin32ControllerCreate.argtypes = [
368
779
  ctypes.c_void_p,
369
780
  MaaWin32ScreencapMethod,
370
781
  MaaWin32InputMethod,
371
- MaaNotificationCallback,
372
- ctypes.c_void_p,
782
+ MaaWin32InputMethod,
783
+ ]
784
+
785
+
786
+ class PlayCoverController(Controller):
787
+ """PlayCover 控制器 / PlayCover controller
788
+
789
+ 用于在 macOS 上控制通过 PlayCover 运行的 iOS 应用
790
+ For controlling iOS apps running via PlayCover on macOS
791
+ """
792
+
793
+ def __init__(
794
+ self,
795
+ address: str,
796
+ uuid: str,
797
+ ):
798
+ """创建 PlayCover 控制器 / Create PlayCover controller
799
+
800
+ Args:
801
+ address: PlayTools 服务地址 (host:port) / PlayTools service endpoint (host:port)
802
+ uuid: 目标应用 bundle identifier / Target app bundle identifier
803
+
804
+ Raises:
805
+ RuntimeError: 如果创建失败
806
+ """
807
+ super().__init__()
808
+ self._set_playcover_api_properties()
809
+
810
+ self._handle = Library.framework().MaaPlayCoverControllerCreate(
811
+ address.encode(),
812
+ uuid.encode(),
813
+ )
814
+
815
+ if not self._handle:
816
+ raise RuntimeError("Failed to create PlayCover controller.")
817
+
818
+ def _set_playcover_api_properties(self):
819
+ Library.framework().MaaPlayCoverControllerCreate.restype = MaaControllerHandle
820
+ Library.framework().MaaPlayCoverControllerCreate.argtypes = [
821
+ ctypes.c_char_p,
822
+ ctypes.c_char_p,
373
823
  ]
374
824
 
375
825
 
376
826
  class DbgController(Controller):
827
+ """调试控制器 / Debug controller"""
377
828
 
378
829
  def __init__(
379
830
  self,
@@ -381,51 +832,109 @@ class DbgController(Controller):
381
832
  write_path: Union[str, Path],
382
833
  dbg_type: int,
383
834
  config: Dict[str, Any] = {},
384
- notification_handler: Optional[NotificationHandler] = None,
385
835
  ):
836
+ """创建调试控制器 / Create debug controller
837
+
838
+ Args:
839
+ read_path: 输入路径, 包含通过 Recording 选项记录的操作 / Input path, includes operations recorded via Recording option
840
+ write_path: 输出路径, 包含执行结果 / Output path, includes execution results
841
+ dbg_type: 控制器模式 / Controller mode
842
+ config: 额外配置 / Extra config
843
+
844
+ Raises:
845
+ RuntimeError: 如果创建失败
846
+ """
386
847
  super().__init__()
387
848
  self._set_dbg_api_properties()
388
849
 
389
- self._notification_handler = notification_handler
390
- self._handle = Library.framework.MaaDbgControllerCreate(
850
+ self._handle = Library.framework().MaaDbgControllerCreate(
391
851
  str(read_path).encode(),
392
852
  str(write_path).encode(),
393
853
  MaaDbgControllerType(dbg_type),
394
854
  json.dumps(config, ensure_ascii=False).encode(),
395
- *NotificationHandler._gen_c_param(self._notification_handler)
396
855
  )
397
856
 
398
857
  if not self._handle:
399
858
  raise RuntimeError("Failed to create DBG controller.")
400
859
 
401
860
  def _set_dbg_api_properties(self):
402
- Library.framework.MaaDbgControllerCreate.restype = MaaControllerHandle
403
- Library.framework.MaaDbgControllerCreate.argtypes = [
861
+ Library.framework().MaaDbgControllerCreate.restype = MaaControllerHandle
862
+ Library.framework().MaaDbgControllerCreate.argtypes = [
404
863
  ctypes.c_char_p,
405
864
  ctypes.c_char_p,
406
865
  MaaDbgControllerType,
407
866
  ctypes.c_char_p,
408
- MaaNotificationCallback,
409
- ctypes.c_void_p,
410
867
  ]
411
868
 
412
869
 
413
- class CustomController(Controller):
870
+ class GamepadController(Controller):
871
+ """虚拟手柄控制器 (仅 Windows) / Virtual gamepad controller (Windows only)
414
872
 
415
- _callbacks: MaaCustomControllerCallbacks
873
+ 通过 ViGEm 模拟 Xbox 360 或 DualShock 4 手柄,用于控制需要手柄输入的游戏。
874
+ Emulates Xbox 360 or DualShock 4 gamepad via ViGEm for controlling games that require gamepad input.
875
+
876
+ 需要安装 ViGEm Bus Driver: https://github.com/ViGEm/ViGEmBus/releases
877
+ Requires ViGEm Bus Driver: https://github.com/ViGEm/ViGEmBus/releases
878
+
879
+ 手柄操作映射:
880
+ - click_key/key_down/key_up: 数字按键 (使用 MaaGamepadButtonEnum)
881
+ - touch_down/touch_move/touch_up: 摇杆和扳机 (contact 使用 MaaGamepadContactEnum)
882
+ - contact 0: 左摇杆 (x, y: -32768~32767)
883
+ - contact 1: 右摇杆 (x, y: -32768~32767)
884
+ - contact 2: 左扳机 (pressure: 0~255)
885
+ - contact 3: 右扳机 (pressure: 0~255)
886
+ """
416
887
 
417
888
  def __init__(
418
889
  self,
419
- notification_handler: Optional[NotificationHandler] = None,
890
+ hWnd: Union[ctypes.c_void_p, int, None],
891
+ gamepad_type: int = MaaGamepadTypeEnum.Xbox360,
892
+ screencap_method: int = MaaWin32ScreencapMethodEnum.PrintWindow,
420
893
  ):
894
+ """创建虚拟手柄控制器 / Create virtual gamepad controller
895
+
896
+ Args:
897
+ hWnd: 窗口句柄,用于截图 (可为 None,不需要截图时) / Window handle for screencap (can be None if screencap not needed)
898
+ gamepad_type: 手柄类型 (MaaGamepadTypeEnum.Xbox360 或 MaaGamepadTypeEnum.DualShock4) / Gamepad type
899
+ screencap_method: 截图方式 (当 hWnd 不为 None 时使用) / Screencap method (used when hWnd is not None)
900
+
901
+ Raises:
902
+ RuntimeError: 如果创建失败
903
+ """
421
904
  super().__init__()
422
- self._set_custom_api_properties()
905
+ self._set_gamepad_api_properties()
906
+
907
+ self._handle = Library.framework().MaaGamepadControllerCreate(
908
+ hWnd,
909
+ MaaGamepadType(gamepad_type),
910
+ MaaWin32ScreencapMethod(screencap_method),
911
+ )
912
+
913
+ if not self._handle:
914
+ raise RuntimeError("Failed to create Gamepad controller.")
915
+
916
+ def _set_gamepad_api_properties(self):
917
+ Library.framework().MaaGamepadControllerCreate.restype = MaaControllerHandle
918
+ Library.framework().MaaGamepadControllerCreate.argtypes = [
919
+ ctypes.c_void_p,
920
+ MaaGamepadType,
921
+ MaaWin32ScreencapMethod,
922
+ ]
423
923
 
424
- self._notification_handler = notification_handler
924
+
925
+ class CustomController(Controller):
926
+
927
+ _callbacks: MaaCustomControllerCallbacks
928
+
929
+ def __init__(self):
930
+ super().__init__()
931
+ self._set_custom_api_properties()
425
932
 
426
933
  self._callbacks = MaaCustomControllerCallbacks(
427
934
  CustomController._c_connect_agent,
935
+ CustomController._c_connected_agent,
428
936
  CustomController._c_request_uuid_agent,
937
+ CustomController._c_get_features_agent,
429
938
  CustomController._c_start_app_agent,
430
939
  CustomController._c_stop_app_agent,
431
940
  CustomController._c_screencap_agent,
@@ -434,14 +943,16 @@ class CustomController(Controller):
434
943
  CustomController._c_touch_down_agent,
435
944
  CustomController._c_touch_move_agent,
436
945
  CustomController._c_touch_up_agent,
437
- CustomController._c_press_key_agent,
946
+ CustomController._c_click_key_agent,
438
947
  CustomController._c_input_text_agent,
948
+ CustomController._c_key_down_agent,
949
+ CustomController._c_key_up_agent,
950
+ CustomController._c_scroll_agent,
439
951
  )
440
952
 
441
- self._handle = Library.framework.MaaCustomControllerCreate(
953
+ self._handle = Library.framework().MaaCustomControllerCreate(
442
954
  self.c_handle,
443
955
  self.c_arg,
444
- *NotificationHandler._gen_c_param(self._notification_handler)
445
956
  )
446
957
 
447
958
  if not self._handle:
@@ -459,10 +970,20 @@ class CustomController(Controller):
459
970
  def connect(self) -> bool:
460
971
  raise NotImplementedError
461
972
 
973
+ def connected(self) -> bool:
974
+ """检查是否已连接(可选实现,默认返回 True)"""
975
+ return True
976
+
462
977
  @abstractmethod
463
978
  def request_uuid(self) -> str:
464
979
  raise NotImplementedError
465
980
 
981
+ def get_features(self) -> int:
982
+ return (
983
+ MaaControllerFeatureEnum.UseMouseDownAndUpInsteadOfClick
984
+ | MaaControllerFeatureEnum.UseKeyboardDownAndUpInsteadOfClick
985
+ )
986
+
466
987
  @abstractmethod
467
988
  def start_app(self, intent: str) -> bool:
468
989
  raise NotImplementedError
@@ -508,13 +1029,25 @@ class CustomController(Controller):
508
1029
  raise NotImplementedError
509
1030
 
510
1031
  @abstractmethod
511
- def press_key(self, keycode: int) -> bool:
1032
+ def click_key(self, keycode: int) -> bool:
512
1033
  raise NotImplementedError
513
1034
 
514
1035
  @abstractmethod
515
1036
  def input_text(self, text: str) -> bool:
516
1037
  raise NotImplementedError
517
1038
 
1039
+ @abstractmethod
1040
+ def key_down(self, keycode: int) -> bool:
1041
+ raise NotImplementedError
1042
+
1043
+ @abstractmethod
1044
+ def key_up(self, keycode: int) -> bool:
1045
+ raise NotImplementedError
1046
+
1047
+ @abstractmethod
1048
+ def scroll(self, dx: int, dy: int) -> bool:
1049
+ raise NotImplementedError
1050
+
518
1051
  @staticmethod
519
1052
  @MaaCustomControllerCallbacks.ConnectFunc
520
1053
  def _c_connect_agent(
@@ -530,6 +1063,21 @@ class CustomController(Controller):
530
1063
 
531
1064
  return int(self.connect())
532
1065
 
1066
+ @staticmethod
1067
+ @MaaCustomControllerCallbacks.ConnectedFunc
1068
+ def _c_connected_agent(
1069
+ trans_arg: ctypes.c_void_p,
1070
+ ) -> int:
1071
+ if not trans_arg:
1072
+ return int(False)
1073
+
1074
+ self: CustomController = ctypes.cast(
1075
+ trans_arg,
1076
+ ctypes.py_object,
1077
+ ).value
1078
+
1079
+ return int(self.connected())
1080
+
533
1081
  @staticmethod
534
1082
  @MaaCustomControllerCallbacks.RequestUuidFunc
535
1083
  def _c_request_uuid_agent(
@@ -550,6 +1098,19 @@ class CustomController(Controller):
550
1098
  uuid_buffer.set(uuid)
551
1099
  return int(True)
552
1100
 
1101
+ @staticmethod
1102
+ @MaaCustomControllerCallbacks.GetFeaturesFunc
1103
+ def _c_get_features_agent(trans_arg: ctypes.c_void_p) -> int:
1104
+ if not trans_arg:
1105
+ return int(MaaControllerFeatureEnum.Null)
1106
+
1107
+ self: CustomController = ctypes.cast(
1108
+ trans_arg,
1109
+ ctypes.py_object,
1110
+ ).value
1111
+
1112
+ return int(self.get_features())
1113
+
553
1114
  @staticmethod
554
1115
  @MaaCustomControllerCallbacks.StartAppFunc
555
1116
  def _c_start_app_agent(
@@ -697,8 +1258,8 @@ class CustomController(Controller):
697
1258
  return int(self.touch_up(int(c_contact)))
698
1259
 
699
1260
  @staticmethod
700
- @MaaCustomControllerCallbacks.PressKeyFunc
701
- def _c_press_key_agent(
1261
+ @MaaCustomControllerCallbacks.ClickKeyFunc
1262
+ def _c_click_key_agent(
702
1263
  c_keycode: ctypes.c_int32,
703
1264
  trans_arg: ctypes.c_void_p,
704
1265
  ) -> int:
@@ -710,7 +1271,39 @@ class CustomController(Controller):
710
1271
  ctypes.py_object,
711
1272
  ).value
712
1273
 
713
- return int(self.press_key(int(c_keycode)))
1274
+ return int(self.click_key(int(c_keycode)))
1275
+
1276
+ @staticmethod
1277
+ @MaaCustomControllerCallbacks.KeyDownFunc
1278
+ def _c_key_down_agent(
1279
+ c_keycode: ctypes.c_int32,
1280
+ trans_arg: ctypes.c_void_p,
1281
+ ) -> int:
1282
+ if not trans_arg:
1283
+ return int(False)
1284
+
1285
+ self: CustomController = ctypes.cast(
1286
+ trans_arg,
1287
+ ctypes.py_object,
1288
+ ).value
1289
+
1290
+ return int(self.key_down(int(c_keycode)))
1291
+
1292
+ @staticmethod
1293
+ @MaaCustomControllerCallbacks.KeyUpFunc
1294
+ def _c_key_up_agent(
1295
+ c_keycode: ctypes.c_int32,
1296
+ trans_arg: ctypes.c_void_p,
1297
+ ) -> int:
1298
+ if not trans_arg:
1299
+ return int(False)
1300
+
1301
+ self: CustomController = ctypes.cast(
1302
+ trans_arg,
1303
+ ctypes.py_object,
1304
+ ).value
1305
+
1306
+ return int(self.key_up(int(c_keycode)))
714
1307
 
715
1308
  @staticmethod
716
1309
  @MaaCustomControllerCallbacks.InputTextFunc
@@ -728,11 +1321,65 @@ class CustomController(Controller):
728
1321
 
729
1322
  return int(self.input_text(c_text.decode()))
730
1323
 
1324
+ @staticmethod
1325
+ @MaaCustomControllerCallbacks.ScrollFunc
1326
+ def _c_scroll_agent(
1327
+ c_dx: ctypes.c_int32,
1328
+ c_dy: ctypes.c_int32,
1329
+ trans_arg: ctypes.c_void_p,
1330
+ ) -> int:
1331
+ if not trans_arg:
1332
+ return int(False)
1333
+
1334
+ self: CustomController = ctypes.cast(
1335
+ trans_arg,
1336
+ ctypes.py_object,
1337
+ ).value
1338
+
1339
+ return int(self.scroll(int(c_dx), int(c_dy)))
1340
+
731
1341
  def _set_custom_api_properties(self):
732
- Library.framework.MaaCustomControllerCreate.restype = MaaControllerHandle
733
- Library.framework.MaaCustomControllerCreate.argtypes = [
1342
+ Library.framework().MaaCustomControllerCreate.restype = MaaControllerHandle
1343
+ Library.framework().MaaCustomControllerCreate.argtypes = [
734
1344
  ctypes.POINTER(MaaCustomControllerCallbacks),
735
1345
  ctypes.c_void_p,
736
- MaaNotificationCallback,
737
- ctypes.c_void_p,
738
1346
  ]
1347
+
1348
+
1349
+ class ControllerEventSink(EventSink):
1350
+
1351
+ @dataclass
1352
+ class ControllerActionDetail:
1353
+ ctrl_id: int
1354
+ uuid: str
1355
+ action: str
1356
+ param: dict
1357
+
1358
+ def on_controller_action(
1359
+ self,
1360
+ controller: Controller,
1361
+ noti_type: NotificationType,
1362
+ detail: ControllerActionDetail,
1363
+ ):
1364
+ pass
1365
+
1366
+ def on_raw_notification(self, controller: Controller, msg: str, details: dict):
1367
+ pass
1368
+
1369
+ def _on_raw_notification(self, handle: ctypes.c_void_p, msg: str, details: dict):
1370
+
1371
+ controller = Controller(handle=handle)
1372
+ self.on_raw_notification(controller, msg, details)
1373
+
1374
+ noti_type = EventSink._notification_type(msg)
1375
+ if msg.startswith("Controller.Action"):
1376
+ detail = self.ControllerActionDetail(
1377
+ ctrl_id=details["ctrl_id"],
1378
+ uuid=details["uuid"],
1379
+ action=details["action"],
1380
+ param=details["param"],
1381
+ )
1382
+ self.on_controller_action(controller, noti_type, detail)
1383
+
1384
+ else:
1385
+ self.on_unknown_notification(controller, msg, details)