1use crate::sys;
4use serde::{Deserialize, Serialize};
5use std::fmt;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
12pub struct MaaStatus(pub i32);
13
14pub type MaaId = i64;
16
17impl MaaStatus {
18 pub const INVALID: Self = Self(sys::MaaStatusEnum_MaaStatus_Invalid as i32);
19 pub const PENDING: Self = Self(sys::MaaStatusEnum_MaaStatus_Pending as i32);
20 pub const RUNNING: Self = Self(sys::MaaStatusEnum_MaaStatus_Running as i32);
21 pub const SUCCEEDED: Self = Self(sys::MaaStatusEnum_MaaStatus_Succeeded as i32);
22 pub const FAILED: Self = Self(sys::MaaStatusEnum_MaaStatus_Failed as i32);
23
24 pub fn is_success(&self) -> bool {
26 *self == Self::SUCCEEDED
27 }
28
29 pub fn succeeded(&self) -> bool {
31 *self == Self::SUCCEEDED
32 }
33
34 pub fn is_failed(&self) -> bool {
36 *self == Self::FAILED
37 }
38
39 pub fn failed(&self) -> bool {
41 *self == Self::FAILED
42 }
43
44 pub fn done(&self) -> bool {
46 *self == Self::SUCCEEDED || *self == Self::FAILED
47 }
48
49 pub fn pending(&self) -> bool {
51 *self == Self::PENDING
52 }
53
54 pub fn running(&self) -> bool {
56 *self == Self::RUNNING
57 }
58}
59
60impl fmt::Display for MaaStatus {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 match *self {
63 Self::INVALID => write!(f, "Invalid"),
64 Self::PENDING => write!(f, "Pending"),
65 Self::RUNNING => write!(f, "Running"),
66 Self::SUCCEEDED => write!(f, "Succeeded"),
67 Self::FAILED => write!(f, "Failed"),
68 _ => write!(f, "Unknown({})", self.0),
69 }
70 }
71}
72
73pub fn check_bool(ret: sys::MaaBool) -> crate::MaaResult<()> {
74 if ret != 0 {
75 Ok(())
76 } else {
77 Err(crate::MaaError::FrameworkError(0))
78 }
79}
80
81impl From<i32> for MaaStatus {
82 fn from(value: i32) -> Self {
83 Self(value)
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Default)]
94#[serde(from = "RectDef")]
95pub struct Rect {
96 pub x: i32,
97 pub y: i32,
98 pub width: i32,
99 pub height: i32,
100}
101
102impl Serialize for Rect {
103 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
104 where
105 S: serde::Serializer,
106 {
107 (self.x, self.y, self.width, self.height).serialize(serializer)
108 }
109}
110
111#[derive(Deserialize)]
113#[serde(untagged)]
114enum RectDef {
115 Map {
116 x: i32,
117 y: i32,
118 #[serde(alias = "w")]
119 width: i32,
120 #[serde(alias = "h")]
121 height: i32,
122 },
123 Array(i32, i32, i32, i32),
124}
125
126impl From<RectDef> for Rect {
127 fn from(def: RectDef) -> Self {
128 match def {
129 RectDef::Map {
130 x,
131 y,
132 width,
133 height,
134 } => Rect {
135 x,
136 y,
137 width,
138 height,
139 },
140 RectDef::Array(x, y, w, h) => Rect {
141 x,
142 y,
143 width: w,
144 height: h,
145 },
146 }
147 }
148}
149
150impl From<(i32, i32, i32, i32)> for Rect {
151 fn from(tuple: (i32, i32, i32, i32)) -> Self {
152 Self {
153 x: tuple.0,
154 y: tuple.1,
155 width: tuple.2,
156 height: tuple.3,
157 }
158 }
159}
160
161impl From<sys::MaaRect> for Rect {
162 fn from(r: sys::MaaRect) -> Self {
163 Self {
164 x: r.x,
165 y: r.y,
166 width: r.width,
167 height: r.height,
168 }
169 }
170}
171
172impl Rect {
173 pub fn to_tuple(&self) -> (i32, i32, i32, i32) {
174 (self.x, self.y, self.width, self.height)
175 }
176}
177
178impl PartialEq<(i32, i32, i32, i32)> for Rect {
179 fn eq(&self, other: &(i32, i32, i32, i32)) -> bool {
180 self.x == other.0 && self.y == other.1 && self.width == other.2 && self.height == other.3
181 }
182}
183
184impl PartialEq<Rect> for (i32, i32, i32, i32) {
185 fn eq(&self, other: &Rect) -> bool {
186 self.0 == other.x && self.1 == other.y && self.2 == other.width && self.3 == other.height
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
192pub struct Point {
193 pub x: i32,
194 pub y: i32,
195}
196
197impl Point {
198 pub fn new(x: i32, y: i32) -> Self {
199 Self { x, y }
200 }
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
209#[repr(u64)]
210#[non_exhaustive]
211pub enum GamepadType {
212 Xbox360 = 0,
214 DualShock4 = 1,
216}
217
218#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
220#[repr(i32)]
221#[non_exhaustive]
222pub enum GamepadContact {
223 LeftStick = 0,
225 RightStick = 1,
227 LeftTrigger = 2,
229 RightTrigger = 3,
231}
232
233bitflags::bitflags! {
234 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
238 pub struct GamepadButton: u32 {
239 const DPAD_UP = 0x0001;
241 const DPAD_DOWN = 0x0002;
242 const DPAD_LEFT = 0x0004;
243 const DPAD_RIGHT = 0x0008;
244
245 const START = 0x0010;
247 const BACK = 0x0020;
248 const LEFT_THUMB = 0x0040; const RIGHT_THUMB = 0x0080; const LB = 0x0100; const RB = 0x0200; const GUIDE = 0x0400;
257
258 const A = 0x1000;
260 const B = 0x2000;
261 const X = 0x4000;
262 const Y = 0x8000;
263
264 const PS = 0x10000;
266 const TOUCHPAD = 0x20000;
267 }
268}
269
270impl GamepadButton {
271 pub const CROSS: Self = Self::A;
273 pub const CIRCLE: Self = Self::B;
274 pub const SQUARE: Self = Self::X;
275 pub const TRIANGLE: Self = Self::Y;
276 pub const L1: Self = Self::LB;
277 pub const R1: Self = Self::RB;
278 pub const L3: Self = Self::LEFT_THUMB;
279 pub const R3: Self = Self::RIGHT_THUMB;
280 pub const OPTIONS: Self = Self::START;
281 pub const SHARE: Self = Self::BACK;
282}
283
284bitflags::bitflags! {
289 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
293 pub struct ControllerFeature: u64 {
294 const USE_MOUSE_DOWN_UP_INSTEAD_OF_CLICK = 1;
298 const USE_KEY_DOWN_UP_INSTEAD_OF_CLICK = 1 << 1;
301 const NO_SCALING_TOUCH_POINTS = 1 << 2;
304 }
305}
306
307#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
313pub struct AndroidScreenResolution {
314 pub width: i32,
316 pub height: i32,
318}
319
320#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
322pub struct AndroidNativeControllerConfig {
323 pub library_path: String,
325 pub screen_resolution: AndroidScreenResolution,
327 #[serde(default, skip_serializing_if = "Option::is_none")]
329 pub display_id: Option<u32>,
330 #[serde(default, skip_serializing_if = "Option::is_none")]
332 pub force_stop: Option<bool>,
333}
334
335bitflags::bitflags! {
336 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
341 pub struct AdbScreencapMethod: u64 {
342 const ENCODE_TO_FILE_AND_PULL = 1;
343 const ENCODE = 1 << 1;
344 const RAW_WITH_GZIP = 1 << 2;
345 const RAW_BY_NETCAT = 1 << 3;
346 const MINICAP_DIRECT = 1 << 4;
347 const MINICAP_STREAM = 1 << 5;
348 const EMULATOR_EXTRAS = 1 << 6;
349 const ALL = !0;
350 }
351}
352
353impl AdbScreencapMethod {
354 pub const DEFAULT: Self = Self::from_bits_truncate(
356 Self::ALL.bits()
357 & !Self::RAW_BY_NETCAT.bits()
358 & !Self::MINICAP_DIRECT.bits()
359 & !Self::MINICAP_STREAM.bits(),
360 );
361}
362
363impl Default for AdbScreencapMethod {
364 fn default() -> Self {
365 Self::DEFAULT
366 }
367}
368
369bitflags::bitflags! {
370 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
375 pub struct AdbInputMethod: u64 {
376 const ADB_SHELL = 1;
377 const MINITOUCH_AND_ADB_KEY = 1 << 1;
378 const MAATOUCH = 1 << 2;
379 const EMULATOR_EXTRAS = 1 << 3;
380 const ALL = !0;
381 }
382}
383
384impl AdbInputMethod {
385 pub const DEFAULT: Self =
387 Self::from_bits_truncate(Self::ALL.bits() & !Self::EMULATOR_EXTRAS.bits());
388}
389
390impl Default for AdbInputMethod {
391 fn default() -> Self {
392 Self::DEFAULT
393 }
394}
395
396bitflags::bitflags! {
401 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
403 pub struct LinuxScreencapMethod: u64 {
404 const NONE = sys::MaaLinuxScreencapMethod_None as u64;
405 const WLR = sys::MaaLinuxScreencapMethod_Wlr as u64;
407 const PIPEWIRE = sys::MaaLinuxScreencapMethod_PipeWire as u64;
409 }
410}
411
412bitflags::bitflags! {
413 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
415 pub struct LinuxInputMethod: u64 {
416 const NONE = sys::MaaLinuxInputMethod_None as u64;
417 const WLR = sys::MaaLinuxInputMethod_Wlr as u64;
419 const UINPUT = sys::MaaLinuxInputMethod_UInput as u64;
421 const LIBEI = sys::MaaLinuxInputMethod_Libei as u64;
423 }
424}
425
426#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
428pub struct LinuxControllerConfig {
429 pub screencap_method: sys::MaaLinuxScreencapMethod,
431 pub input_method: sys::MaaLinuxInputMethod,
433 #[serde(default, skip_serializing_if = "Option::is_none")]
435 pub wlr_socket_path: Option<String>,
436 #[serde(default, skip_serializing_if = "Option::is_none")]
442 pub pw_socket_fd: Option<i32>,
443 #[serde(default, skip_serializing_if = "Option::is_none")]
449 pub pw_node_id: Option<u32>,
450 #[serde(
452 default,
453 alias = "pw_screen_width",
454 skip_serializing_if = "Option::is_none"
455 )]
456 pub uinput_screen_width: Option<i32>,
457 #[serde(
459 default,
460 alias = "pw_screen_height",
461 skip_serializing_if = "Option::is_none"
462 )]
463 pub uinput_screen_height: Option<i32>,
464 #[serde(default, skip_serializing_if = "Option::is_none")]
466 pub uinput_path: Option<String>,
467 #[serde(default, skip_serializing_if = "Option::is_none")]
476 pub eis_socket_path: Option<String>,
477 #[serde(default, skip_serializing_if = "Option::is_none")]
479 pub use_win32_vk_code: Option<bool>,
480}
481
482bitflags::bitflags! {
487 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
499 pub struct Win32ScreencapMethod: u64 {
500 const GDI = 1;
501 const FRAME_POOL = 1 << 1;
502 const DXGI_DESKTOP_DUP = 1 << 2;
503 const DXGI_DESKTOP_DUP_WINDOW = 1 << 3;
504 const PRINT_WINDOW = 1 << 4;
505 const SCREEN_DC = 1 << 5;
506 const ALL = !0;
507 const FOREGROUND = Self::DXGI_DESKTOP_DUP_WINDOW.bits() | Self::SCREEN_DC.bits();
508 const BACKGROUND = Self::FRAME_POOL.bits() | Self::PRINT_WINDOW.bits();
509 }
510}
511
512bitflags::bitflags! {
513 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
515 pub struct Win32InputMethod: u64 {
516 const SEIZE = 1;
517 const SEND_MESSAGE = 1 << 1;
518 const POST_MESSAGE = 1 << 2;
519 const LEGACY_EVENT = 1 << 3;
520 const POST_THREAD_MESSAGE = 1 << 4;
522 const SEND_MESSAGE_WITH_CURSOR_POS = 1 << 5;
523 const POST_MESSAGE_WITH_CURSOR_POS = 1 << 6;
524 const SEND_MESSAGE_WITH_WINDOW_POS = 1 << 7;
525 const POST_MESSAGE_WITH_WINDOW_POS = 1 << 8;
526 const INTERCEPTION = sys::MaaWin32InputMethod_Interception as u64;
529 }
530}
531
532#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
534pub struct RecognitionDetail {
535 pub node_name: String,
537 pub algorithm: AlgorithmEnum,
539 pub hit: bool,
541 pub box_rect: Rect,
543 pub detail: serde_json::Value,
545 #[serde(skip)]
547 pub raw_image: Option<Vec<u8>>,
548 #[serde(skip)]
550 pub draw_images: Vec<Vec<u8>>,
551 #[serde(default)]
553 pub sub_details: Vec<RecognitionDetail>,
554}
555
556impl RecognitionDetail {
557 pub fn as_template_match_result(&self) -> Option<TemplateMatchResult> {
558 serde_json::from_value(self.detail.clone()).ok()
559 }
560
561 pub fn as_feature_match_result(&self) -> Option<FeatureMatchResult> {
562 serde_json::from_value(self.detail.clone()).ok()
563 }
564
565 pub fn as_color_match_result(&self) -> Option<ColorMatchResult> {
566 serde_json::from_value(self.detail.clone()).ok()
567 }
568
569 pub fn as_ocr_result(&self) -> Option<OCRResult> {
570 serde_json::from_value(self.detail.clone()).ok()
571 }
572
573 pub fn as_neural_network_result(&self) -> Option<NeuralNetworkResult> {
574 serde_json::from_value(self.detail.clone()).ok()
575 }
576
577 pub fn as_custom_result(&self) -> Option<CustomRecognitionResult> {
578 serde_json::from_value(self.detail.clone()).ok()
579 }
580}
581
582#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
584pub struct ActionDetail {
585 pub node_name: String,
587 pub action: ActionEnum,
589 pub box_rect: Rect,
591 pub success: bool,
593 pub detail: serde_json::Value,
595}
596
597#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
598pub struct WaitFreezesDetail {
599 pub wf_id: MaaId,
600 pub name: String,
601 pub phase: String,
602 pub success: bool,
603 pub elapsed_ms: u64,
604 #[serde(default)]
605 pub reco_id_list: Vec<MaaId>,
606 pub roi: Rect,
607}
608
609impl ActionDetail {
610 pub fn as_click_result(&self) -> Option<ClickActionResult> {
611 serde_json::from_value(self.detail.clone()).ok()
612 }
613
614 pub fn as_long_press_result(&self) -> Option<LongPressActionResult> {
615 serde_json::from_value(self.detail.clone()).ok()
616 }
617
618 pub fn as_swipe_result(&self) -> Option<SwipeActionResult> {
619 serde_json::from_value(self.detail.clone()).ok()
620 }
621
622 pub fn as_multi_swipe_result(&self) -> Option<MultiSwipeActionResult> {
623 serde_json::from_value(self.detail.clone()).ok()
624 }
625
626 pub fn as_click_key_result(&self) -> Option<ClickKeyActionResult> {
627 serde_json::from_value(self.detail.clone()).ok()
628 }
629
630 pub fn as_input_text_result(&self) -> Option<InputTextActionResult> {
631 serde_json::from_value(self.detail.clone()).ok()
632 }
633
634 pub fn as_app_result(&self) -> Option<AppActionResult> {
635 serde_json::from_value(self.detail.clone()).ok()
636 }
637
638 pub fn as_scroll_result(&self) -> Option<ScrollActionResult> {
639 serde_json::from_value(self.detail.clone()).ok()
640 }
641
642 pub fn as_touch_result(&self) -> Option<TouchActionResult> {
643 serde_json::from_value(self.detail.clone()).ok()
644 }
645
646 pub fn as_shell_result(&self) -> Option<ShellActionResult> {
647 serde_json::from_value(self.detail.clone()).ok()
648 }
649}
650
651#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
657pub struct ClickActionResult {
658 pub point: Point,
659 pub contact: i32,
660 #[serde(default)]
661 pub pressure: i32,
662}
663
664#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
666pub struct LongPressActionResult {
667 pub point: Point,
668 pub duration: i32,
669 pub contact: i32,
670 #[serde(default)]
671 pub pressure: i32,
672}
673
674#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
676pub struct SwipeActionResult {
677 pub begin: Point,
678 pub end: Vec<Point>,
679 #[serde(default)]
680 pub end_hold: Vec<i32>,
681 #[serde(default)]
682 pub duration: Vec<i32>,
683 #[serde(default)]
684 pub only_hover: bool,
685 #[serde(default)]
686 pub starting: i32,
687 pub contact: i32,
688 #[serde(default)]
689 pub pressure: i32,
690}
691
692#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
694pub struct MultiSwipeActionResult {
695 pub swipes: Vec<SwipeActionResult>,
696}
697
698#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
700pub struct ClickKeyActionResult {
701 pub keycode: Vec<i32>,
702}
703
704#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
706pub struct LongPressKeyActionResult {
707 pub keycode: Vec<i32>,
708 pub duration: i32,
709}
710
711#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
713pub struct InputTextActionResult {
714 pub text: String,
715}
716
717#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
719pub struct AppActionResult {
720 pub package: String,
721}
722
723#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
725pub struct ScrollActionResult {
726 #[serde(default)]
727 pub point: Point,
728 pub dx: i32,
729 pub dy: i32,
730}
731
732#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
734pub struct TouchActionResult {
735 pub contact: i32,
736 pub point: Point,
737 #[serde(default)]
738 pub pressure: i32,
739}
740
741#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
743pub struct ShellActionResult {
744 pub cmd: String,
745 pub shell_timeout: i32,
746 pub success: bool,
747 pub output: String,
748}
749
750#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
752pub struct NodeDetail {
753 pub node_name: String,
754 pub reco_id: MaaId,
756 pub act_id: MaaId,
758 #[serde(default)]
760 pub recognition: Option<RecognitionDetail>,
761 #[serde(default)]
763 pub action: Option<ActionDetail>,
764 pub completed: bool,
766}
767
768#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
770pub struct TaskDetail {
771 pub entry: String,
773 pub node_id_list: Vec<MaaId>,
775 pub status: MaaStatus,
777 #[serde(default)]
779 pub nodes: Vec<Option<NodeDetail>>,
780}
781
782#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
784#[serde(into = "String", from = "String")]
785pub enum AlgorithmEnum {
786 DirectHit,
787 TemplateMatch,
788 FeatureMatch,
789 ColorMatch,
790 OCR,
791 NeuralNetworkClassify,
792 NeuralNetworkDetect,
793 And,
794 Or,
795 Custom,
796 Other(String),
797}
798
799impl From<String> for AlgorithmEnum {
800 fn from(s: String) -> Self {
801 match s.as_str() {
802 "DirectHit" => Self::DirectHit,
803 "TemplateMatch" => Self::TemplateMatch,
804 "FeatureMatch" => Self::FeatureMatch,
805 "ColorMatch" => Self::ColorMatch,
806 "OCR" => Self::OCR,
807 "NeuralNetworkClassify" => Self::NeuralNetworkClassify,
808 "NeuralNetworkDetect" => Self::NeuralNetworkDetect,
809 "And" => Self::And,
810 "Or" => Self::Or,
811 "Custom" => Self::Custom,
812 _ => Self::Other(s),
813 }
814 }
815}
816
817impl From<AlgorithmEnum> for String {
818 fn from(algo: AlgorithmEnum) -> Self {
819 match algo {
820 AlgorithmEnum::DirectHit => "DirectHit".to_string(),
821 AlgorithmEnum::TemplateMatch => "TemplateMatch".to_string(),
822 AlgorithmEnum::FeatureMatch => "FeatureMatch".to_string(),
823 AlgorithmEnum::ColorMatch => "ColorMatch".to_string(),
824 AlgorithmEnum::OCR => "OCR".to_string(),
825 AlgorithmEnum::NeuralNetworkClassify => "NeuralNetworkClassify".to_string(),
826 AlgorithmEnum::NeuralNetworkDetect => "NeuralNetworkDetect".to_string(),
827 AlgorithmEnum::And => "And".to_string(),
828 AlgorithmEnum::Or => "Or".to_string(),
829 AlgorithmEnum::Custom => "Custom".to_string(),
830 AlgorithmEnum::Other(s) => s,
831 }
832 }
833}
834
835impl AlgorithmEnum {
836 pub fn as_str(&self) -> &str {
837 match self {
838 Self::DirectHit => "DirectHit",
839 Self::TemplateMatch => "TemplateMatch",
840 Self::FeatureMatch => "FeatureMatch",
841 Self::ColorMatch => "ColorMatch",
842 Self::OCR => "OCR",
843 Self::NeuralNetworkClassify => "NeuralNetworkClassify",
844 Self::NeuralNetworkDetect => "NeuralNetworkDetect",
845 Self::And => "And",
846 Self::Or => "Or",
847 Self::Custom => "Custom",
848 Self::Other(s) => s.as_str(),
849 }
850 }
851}
852
853#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
855#[serde(into = "String", from = "String")]
856pub enum ActionEnum {
857 DoNothing,
858 Click,
859 LongPress,
860 Swipe,
861 MultiSwipe,
862 TouchDown,
863 TouchMove,
864 TouchUp,
865 ClickKey,
866 LongPressKey,
867 KeyDown,
868 KeyUp,
869 InputText,
870 StartApp,
871 StopApp,
872 StopTask,
873 Scroll,
874 Command,
875 Shell,
876 Custom,
877 Other(String),
878}
879
880impl From<String> for ActionEnum {
881 fn from(s: String) -> Self {
882 match s.as_str() {
883 "DoNothing" => Self::DoNothing,
884 "Click" => Self::Click,
885 "LongPress" => Self::LongPress,
886 "Swipe" => Self::Swipe,
887 "MultiSwipe" => Self::MultiSwipe,
888 "TouchDown" => Self::TouchDown,
889 "TouchMove" => Self::TouchMove,
890 "TouchUp" => Self::TouchUp,
891 "ClickKey" => Self::ClickKey,
892 "LongPressKey" => Self::LongPressKey,
893 "KeyDown" => Self::KeyDown,
894 "KeyUp" => Self::KeyUp,
895 "InputText" => Self::InputText,
896 "StartApp" => Self::StartApp,
897 "StopApp" => Self::StopApp,
898 "StopTask" => Self::StopTask,
899 "Scroll" => Self::Scroll,
900 "Command" => Self::Command,
901 "Shell" => Self::Shell,
902 "Custom" => Self::Custom,
903 _ => Self::Other(s),
904 }
905 }
906}
907
908impl From<ActionEnum> for String {
909 fn from(act: ActionEnum) -> Self {
910 match act {
911 ActionEnum::DoNothing => "DoNothing".to_string(),
912 ActionEnum::Click => "Click".to_string(),
913 ActionEnum::LongPress => "LongPress".to_string(),
914 ActionEnum::Swipe => "Swipe".to_string(),
915 ActionEnum::MultiSwipe => "MultiSwipe".to_string(),
916 ActionEnum::TouchDown => "TouchDown".to_string(),
917 ActionEnum::TouchMove => "TouchMove".to_string(),
918 ActionEnum::TouchUp => "TouchUp".to_string(),
919 ActionEnum::ClickKey => "ClickKey".to_string(),
920 ActionEnum::LongPressKey => "LongPressKey".to_string(),
921 ActionEnum::KeyDown => "KeyDown".to_string(),
922 ActionEnum::KeyUp => "KeyUp".to_string(),
923 ActionEnum::InputText => "InputText".to_string(),
924 ActionEnum::StartApp => "StartApp".to_string(),
925 ActionEnum::StopApp => "StopApp".to_string(),
926 ActionEnum::StopTask => "StopTask".to_string(),
927 ActionEnum::Scroll => "Scroll".to_string(),
928 ActionEnum::Command => "Command".to_string(),
929 ActionEnum::Shell => "Shell".to_string(),
930 ActionEnum::Custom => "Custom".to_string(),
931 ActionEnum::Other(s) => s,
932 }
933 }
934}
935
936impl ActionEnum {
937 pub fn as_str(&self) -> &str {
938 match self {
939 Self::DoNothing => "DoNothing",
940 Self::Click => "Click",
941 Self::LongPress => "LongPress",
942 Self::Swipe => "Swipe",
943 Self::MultiSwipe => "MultiSwipe",
944 Self::TouchDown => "TouchDown",
945 Self::TouchMove => "TouchMove",
946 Self::TouchUp => "TouchUp",
947 Self::ClickKey => "ClickKey",
948 Self::LongPressKey => "LongPressKey",
949 Self::KeyDown => "KeyDown",
950 Self::KeyUp => "KeyUp",
951 Self::InputText => "InputText",
952 Self::StartApp => "StartApp",
953 Self::StopApp => "StopApp",
954 Self::StopTask => "StopTask",
955 Self::Scroll => "Scroll",
956 Self::Command => "Command",
957 Self::Shell => "Shell",
958 Self::Custom => "Custom",
959 Self::Other(s) => s.as_str(),
960 }
961 }
962}
963
964#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
966#[non_exhaustive]
967pub enum NotificationType {
968 Starting,
969 Succeeded,
970 Failed,
971 Unknown,
972}
973
974impl NotificationType {
975 pub fn from_message(msg: &str) -> Self {
976 if msg.ends_with(".Starting") {
977 Self::Starting
978 } else if msg.ends_with(".Succeeded") {
979 Self::Succeeded
980 } else if msg.ends_with(".Failed") {
981 Self::Failed
982 } else {
983 Self::Unknown
984 }
985 }
986}
987
988#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
991pub struct BoxAndScore {
992 #[serde(rename = "box")]
993 pub box_rect: (i32, i32, i32, i32),
994 pub score: f64,
995}
996
997#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
998pub struct BoxAndCount {
999 #[serde(rename = "box")]
1000 pub box_rect: (i32, i32, i32, i32),
1001 pub count: i32,
1002}
1003
1004pub type TemplateMatchResult = BoxAndScore;
1005pub type FeatureMatchResult = BoxAndCount;
1006pub type ColorMatchResult = BoxAndCount;
1007
1008#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1009pub struct OCRResult {
1010 #[serde(flatten)]
1011 pub base: BoxAndScore,
1012 pub text: String,
1013}
1014
1015#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1016pub struct NeuralNetworkResult {
1017 #[serde(flatten)]
1018 pub base: BoxAndScore,
1019 pub cls_index: i32,
1020 pub label: String,
1021}
1022
1023pub type NeuralNetworkClassifyResult = NeuralNetworkResult;
1024pub type NeuralNetworkDetectResult = NeuralNetworkResult;
1025
1026#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1027pub struct CustomRecognitionResult {
1028 #[serde(rename = "box")]
1029 pub box_rect: (i32, i32, i32, i32),
1030 pub detail: serde_json::Value,
1031}
1032
1033#[cfg(test)]
1034mod tests {
1035 use super::{
1036 AndroidNativeControllerConfig, AndroidScreenResolution, LinuxControllerConfig,
1037 LinuxInputMethod, LinuxScreencapMethod,
1038 };
1039 use serde_json::json;
1040
1041 #[test]
1042 fn android_native_controller_config_serializes_expected_shape() {
1043 let config = AndroidNativeControllerConfig {
1044 library_path: "/data/local/tmp/libmaa_unit.so".to_string(),
1045 screen_resolution: AndroidScreenResolution {
1046 width: 1920,
1047 height: 1080,
1048 },
1049 display_id: Some(1),
1050 force_stop: Some(true),
1051 };
1052
1053 let value = serde_json::to_value(config).unwrap();
1054
1055 assert_eq!(
1056 value,
1057 json!({
1058 "library_path": "/data/local/tmp/libmaa_unit.so",
1059 "screen_resolution": {
1060 "width": 1920,
1061 "height": 1080
1062 },
1063 "display_id": 1,
1064 "force_stop": true
1065 })
1066 );
1067 }
1068
1069 #[test]
1070 fn linux_controller_config_serializes_expected_shape() {
1071 let config = LinuxControllerConfig {
1072 screencap_method: LinuxScreencapMethod::PIPEWIRE.bits(),
1073 input_method: LinuxInputMethod::UINPUT.bits(),
1074 wlr_socket_path: None,
1075 pw_socket_fd: Some(42),
1076 pw_node_id: Some(7),
1077 uinput_screen_width: Some(1920),
1078 uinput_screen_height: Some(1080),
1079 uinput_path: None,
1080 eis_socket_path: Some("/run/user/1000/gamescope-0-ei".into()),
1081 use_win32_vk_code: Some(true),
1082 };
1083
1084 let value = serde_json::to_value(config).unwrap();
1085
1086 assert_eq!(
1087 value,
1088 json!({
1089 "screencap_method": 4,
1090 "input_method": 2,
1091 "pw_socket_fd": 42,
1092 "pw_node_id": 7,
1093 "uinput_screen_width": 1920,
1094 "uinput_screen_height": 1080,
1095 "eis_socket_path": "/run/user/1000/gamescope-0-ei",
1096 "use_win32_vk_code": true
1097 })
1098 );
1099 }
1100
1101 #[test]
1102 fn linux_controller_config_accepts_legacy_size_keys() {
1103 let config: LinuxControllerConfig = serde_json::from_value(json!({
1104 "screencap_method": 4,
1105 "input_method": 2,
1106 "pw_screen_width": 1920,
1107 "pw_screen_height": 1080
1108 }))
1109 .unwrap();
1110
1111 assert_eq!(config.uinput_screen_width, Some(1920));
1112 assert_eq!(config.uinput_screen_height, Some(1080));
1113 }
1114}