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 const ANCHORED_TOUCH = sys::MaaWin32InputMethod_AnchoredTouch as u64;
535 }
536}
537
538#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
540pub struct RecognitionDetail {
541 pub node_name: String,
543 pub algorithm: AlgorithmEnum,
545 pub hit: bool,
547 pub box_rect: Rect,
549 pub detail: serde_json::Value,
551 #[serde(skip)]
553 pub raw_image: Option<Vec<u8>>,
554 #[serde(skip)]
556 pub draw_images: Vec<Vec<u8>>,
557 #[serde(default)]
559 pub sub_details: Vec<RecognitionDetail>,
560}
561
562impl RecognitionDetail {
563 pub fn as_template_match_result(&self) -> Option<TemplateMatchResult> {
564 serde_json::from_value(self.detail.clone()).ok()
565 }
566
567 pub fn as_feature_match_result(&self) -> Option<FeatureMatchResult> {
568 serde_json::from_value(self.detail.clone()).ok()
569 }
570
571 pub fn as_color_match_result(&self) -> Option<ColorMatchResult> {
572 serde_json::from_value(self.detail.clone()).ok()
573 }
574
575 pub fn as_ocr_result(&self) -> Option<OCRResult> {
576 serde_json::from_value(self.detail.clone()).ok()
577 }
578
579 pub fn as_neural_network_result(&self) -> Option<NeuralNetworkResult> {
580 serde_json::from_value(self.detail.clone()).ok()
581 }
582
583 pub fn as_custom_result(&self) -> Option<CustomRecognitionResult> {
584 serde_json::from_value(self.detail.clone()).ok()
585 }
586}
587
588#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
590pub struct ActionDetail {
591 pub node_name: String,
593 pub action: ActionEnum,
595 pub box_rect: Rect,
597 pub success: bool,
599 pub detail: serde_json::Value,
601}
602
603#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
604pub struct WaitFreezesDetail {
605 pub wf_id: MaaId,
606 pub name: String,
607 pub phase: String,
608 pub success: bool,
609 pub elapsed_ms: u64,
610 #[serde(default)]
611 pub reco_id_list: Vec<MaaId>,
612 pub roi: Rect,
613}
614
615impl ActionDetail {
616 pub fn as_click_result(&self) -> Option<ClickActionResult> {
617 serde_json::from_value(self.detail.clone()).ok()
618 }
619
620 pub fn as_long_press_result(&self) -> Option<LongPressActionResult> {
621 serde_json::from_value(self.detail.clone()).ok()
622 }
623
624 pub fn as_swipe_result(&self) -> Option<SwipeActionResult> {
625 serde_json::from_value(self.detail.clone()).ok()
626 }
627
628 pub fn as_multi_swipe_result(&self) -> Option<MultiSwipeActionResult> {
629 serde_json::from_value(self.detail.clone()).ok()
630 }
631
632 pub fn as_click_key_result(&self) -> Option<ClickKeyActionResult> {
633 serde_json::from_value(self.detail.clone()).ok()
634 }
635
636 pub fn as_input_text_result(&self) -> Option<InputTextActionResult> {
637 serde_json::from_value(self.detail.clone()).ok()
638 }
639
640 pub fn as_app_result(&self) -> Option<AppActionResult> {
641 serde_json::from_value(self.detail.clone()).ok()
642 }
643
644 pub fn as_scroll_result(&self) -> Option<ScrollActionResult> {
645 serde_json::from_value(self.detail.clone()).ok()
646 }
647
648 pub fn as_touch_result(&self) -> Option<TouchActionResult> {
649 serde_json::from_value(self.detail.clone()).ok()
650 }
651
652 pub fn as_shell_result(&self) -> Option<ShellActionResult> {
653 serde_json::from_value(self.detail.clone()).ok()
654 }
655}
656
657#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
663pub struct ClickActionResult {
664 pub point: Point,
665 pub contact: i32,
666 #[serde(default)]
667 pub pressure: i32,
668}
669
670#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
672pub struct LongPressActionResult {
673 pub point: Point,
674 pub duration: i32,
675 pub contact: i32,
676 #[serde(default)]
677 pub pressure: i32,
678}
679
680#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
682pub struct SwipeActionResult {
683 pub begin: Point,
684 pub end: Vec<Point>,
685 #[serde(default)]
686 pub end_hold: Vec<i32>,
687 #[serde(default)]
688 pub duration: Vec<i32>,
689 #[serde(default)]
690 pub only_hover: bool,
691 #[serde(default)]
692 pub starting: i32,
693 pub contact: i32,
694 #[serde(default)]
695 pub pressure: i32,
696}
697
698#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
700pub struct MultiSwipeActionResult {
701 pub swipes: Vec<SwipeActionResult>,
702}
703
704#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
706pub struct ClickKeyActionResult {
707 pub keycode: Vec<i32>,
708}
709
710#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
712pub struct LongPressKeyActionResult {
713 pub keycode: Vec<i32>,
714 pub duration: i32,
715}
716
717#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
719pub struct InputTextActionResult {
720 pub text: String,
721}
722
723#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
725pub struct AppActionResult {
726 pub package: String,
727}
728
729#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
731pub struct ScrollActionResult {
732 #[serde(default)]
733 pub point: Point,
734 pub dx: i32,
735 pub dy: i32,
736}
737
738#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
740pub struct TouchActionResult {
741 pub contact: i32,
742 pub point: Point,
743 #[serde(default)]
744 pub pressure: i32,
745}
746
747#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
749pub struct ShellActionResult {
750 pub cmd: String,
751 pub shell_timeout: i32,
752 pub success: bool,
753 pub output: String,
754}
755
756#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
758pub struct NodeDetail {
759 pub node_name: String,
760 pub reco_id: MaaId,
762 pub act_id: MaaId,
764 #[serde(default)]
766 pub recognition: Option<RecognitionDetail>,
767 #[serde(default)]
769 pub action: Option<ActionDetail>,
770 pub completed: bool,
772}
773
774#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
776pub struct TaskDetail {
777 pub entry: String,
779 pub node_id_list: Vec<MaaId>,
781 pub status: MaaStatus,
783 #[serde(default)]
785 pub nodes: Vec<Option<NodeDetail>>,
786}
787
788#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
790#[serde(into = "String", from = "String")]
791pub enum AlgorithmEnum {
792 DirectHit,
793 TemplateMatch,
794 FeatureMatch,
795 ColorMatch,
796 OCR,
797 NeuralNetworkClassify,
798 NeuralNetworkDetect,
799 And,
800 Or,
801 Custom,
802 Other(String),
803}
804
805impl From<String> for AlgorithmEnum {
806 fn from(s: String) -> Self {
807 match s.as_str() {
808 "DirectHit" => Self::DirectHit,
809 "TemplateMatch" => Self::TemplateMatch,
810 "FeatureMatch" => Self::FeatureMatch,
811 "ColorMatch" => Self::ColorMatch,
812 "OCR" => Self::OCR,
813 "NeuralNetworkClassify" => Self::NeuralNetworkClassify,
814 "NeuralNetworkDetect" => Self::NeuralNetworkDetect,
815 "And" => Self::And,
816 "Or" => Self::Or,
817 "Custom" => Self::Custom,
818 _ => Self::Other(s),
819 }
820 }
821}
822
823impl From<AlgorithmEnum> for String {
824 fn from(algo: AlgorithmEnum) -> Self {
825 match algo {
826 AlgorithmEnum::DirectHit => "DirectHit".to_string(),
827 AlgorithmEnum::TemplateMatch => "TemplateMatch".to_string(),
828 AlgorithmEnum::FeatureMatch => "FeatureMatch".to_string(),
829 AlgorithmEnum::ColorMatch => "ColorMatch".to_string(),
830 AlgorithmEnum::OCR => "OCR".to_string(),
831 AlgorithmEnum::NeuralNetworkClassify => "NeuralNetworkClassify".to_string(),
832 AlgorithmEnum::NeuralNetworkDetect => "NeuralNetworkDetect".to_string(),
833 AlgorithmEnum::And => "And".to_string(),
834 AlgorithmEnum::Or => "Or".to_string(),
835 AlgorithmEnum::Custom => "Custom".to_string(),
836 AlgorithmEnum::Other(s) => s,
837 }
838 }
839}
840
841impl AlgorithmEnum {
842 pub fn as_str(&self) -> &str {
843 match self {
844 Self::DirectHit => "DirectHit",
845 Self::TemplateMatch => "TemplateMatch",
846 Self::FeatureMatch => "FeatureMatch",
847 Self::ColorMatch => "ColorMatch",
848 Self::OCR => "OCR",
849 Self::NeuralNetworkClassify => "NeuralNetworkClassify",
850 Self::NeuralNetworkDetect => "NeuralNetworkDetect",
851 Self::And => "And",
852 Self::Or => "Or",
853 Self::Custom => "Custom",
854 Self::Other(s) => s.as_str(),
855 }
856 }
857}
858
859#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
861#[serde(into = "String", from = "String")]
862pub enum ActionEnum {
863 DoNothing,
864 Click,
865 LongPress,
866 Swipe,
867 MultiSwipe,
868 TouchDown,
869 TouchMove,
870 TouchUp,
871 ClickKey,
872 LongPressKey,
873 KeyDown,
874 KeyUp,
875 InputText,
876 StartApp,
877 StopApp,
878 StopTask,
879 Scroll,
880 Command,
881 Shell,
882 Custom,
883 Other(String),
884}
885
886impl From<String> for ActionEnum {
887 fn from(s: String) -> Self {
888 match s.as_str() {
889 "DoNothing" => Self::DoNothing,
890 "Click" => Self::Click,
891 "LongPress" => Self::LongPress,
892 "Swipe" => Self::Swipe,
893 "MultiSwipe" => Self::MultiSwipe,
894 "TouchDown" => Self::TouchDown,
895 "TouchMove" => Self::TouchMove,
896 "TouchUp" => Self::TouchUp,
897 "ClickKey" => Self::ClickKey,
898 "LongPressKey" => Self::LongPressKey,
899 "KeyDown" => Self::KeyDown,
900 "KeyUp" => Self::KeyUp,
901 "InputText" => Self::InputText,
902 "StartApp" => Self::StartApp,
903 "StopApp" => Self::StopApp,
904 "StopTask" => Self::StopTask,
905 "Scroll" => Self::Scroll,
906 "Command" => Self::Command,
907 "Shell" => Self::Shell,
908 "Custom" => Self::Custom,
909 _ => Self::Other(s),
910 }
911 }
912}
913
914impl From<ActionEnum> for String {
915 fn from(act: ActionEnum) -> Self {
916 match act {
917 ActionEnum::DoNothing => "DoNothing".to_string(),
918 ActionEnum::Click => "Click".to_string(),
919 ActionEnum::LongPress => "LongPress".to_string(),
920 ActionEnum::Swipe => "Swipe".to_string(),
921 ActionEnum::MultiSwipe => "MultiSwipe".to_string(),
922 ActionEnum::TouchDown => "TouchDown".to_string(),
923 ActionEnum::TouchMove => "TouchMove".to_string(),
924 ActionEnum::TouchUp => "TouchUp".to_string(),
925 ActionEnum::ClickKey => "ClickKey".to_string(),
926 ActionEnum::LongPressKey => "LongPressKey".to_string(),
927 ActionEnum::KeyDown => "KeyDown".to_string(),
928 ActionEnum::KeyUp => "KeyUp".to_string(),
929 ActionEnum::InputText => "InputText".to_string(),
930 ActionEnum::StartApp => "StartApp".to_string(),
931 ActionEnum::StopApp => "StopApp".to_string(),
932 ActionEnum::StopTask => "StopTask".to_string(),
933 ActionEnum::Scroll => "Scroll".to_string(),
934 ActionEnum::Command => "Command".to_string(),
935 ActionEnum::Shell => "Shell".to_string(),
936 ActionEnum::Custom => "Custom".to_string(),
937 ActionEnum::Other(s) => s,
938 }
939 }
940}
941
942impl ActionEnum {
943 pub fn as_str(&self) -> &str {
944 match self {
945 Self::DoNothing => "DoNothing",
946 Self::Click => "Click",
947 Self::LongPress => "LongPress",
948 Self::Swipe => "Swipe",
949 Self::MultiSwipe => "MultiSwipe",
950 Self::TouchDown => "TouchDown",
951 Self::TouchMove => "TouchMove",
952 Self::TouchUp => "TouchUp",
953 Self::ClickKey => "ClickKey",
954 Self::LongPressKey => "LongPressKey",
955 Self::KeyDown => "KeyDown",
956 Self::KeyUp => "KeyUp",
957 Self::InputText => "InputText",
958 Self::StartApp => "StartApp",
959 Self::StopApp => "StopApp",
960 Self::StopTask => "StopTask",
961 Self::Scroll => "Scroll",
962 Self::Command => "Command",
963 Self::Shell => "Shell",
964 Self::Custom => "Custom",
965 Self::Other(s) => s.as_str(),
966 }
967 }
968}
969
970#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
972#[non_exhaustive]
973pub enum NotificationType {
974 Starting,
975 Succeeded,
976 Failed,
977 Unknown,
978}
979
980impl NotificationType {
981 pub fn from_message(msg: &str) -> Self {
982 if msg.ends_with(".Starting") {
983 Self::Starting
984 } else if msg.ends_with(".Succeeded") {
985 Self::Succeeded
986 } else if msg.ends_with(".Failed") {
987 Self::Failed
988 } else {
989 Self::Unknown
990 }
991 }
992}
993
994#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
997pub struct BoxAndScore {
998 #[serde(rename = "box")]
999 pub box_rect: (i32, i32, i32, i32),
1000 pub score: f64,
1001}
1002
1003#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1004pub struct BoxAndCount {
1005 #[serde(rename = "box")]
1006 pub box_rect: (i32, i32, i32, i32),
1007 pub count: i32,
1008}
1009
1010pub type TemplateMatchResult = BoxAndScore;
1011pub type FeatureMatchResult = BoxAndCount;
1012pub type ColorMatchResult = BoxAndCount;
1013
1014#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1015pub struct OCRResult {
1016 #[serde(flatten)]
1017 pub base: BoxAndScore,
1018 pub text: String,
1019}
1020
1021#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1022pub struct NeuralNetworkResult {
1023 #[serde(flatten)]
1024 pub base: BoxAndScore,
1025 pub cls_index: i32,
1026 pub label: String,
1027}
1028
1029pub type NeuralNetworkClassifyResult = NeuralNetworkResult;
1030pub type NeuralNetworkDetectResult = NeuralNetworkResult;
1031
1032#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1033pub struct CustomRecognitionResult {
1034 #[serde(rename = "box")]
1035 pub box_rect: (i32, i32, i32, i32),
1036 pub detail: serde_json::Value,
1037}
1038
1039#[cfg(test)]
1040mod tests {
1041 use super::{
1042 AndroidNativeControllerConfig, AndroidScreenResolution, LinuxControllerConfig,
1043 LinuxInputMethod, LinuxScreencapMethod,
1044 };
1045 use serde_json::json;
1046
1047 #[test]
1048 fn android_native_controller_config_serializes_expected_shape() {
1049 let config = AndroidNativeControllerConfig {
1050 library_path: "/data/local/tmp/libmaa_unit.so".to_string(),
1051 screen_resolution: AndroidScreenResolution {
1052 width: 1920,
1053 height: 1080,
1054 },
1055 display_id: Some(1),
1056 force_stop: Some(true),
1057 };
1058
1059 let value = serde_json::to_value(config).unwrap();
1060
1061 assert_eq!(
1062 value,
1063 json!({
1064 "library_path": "/data/local/tmp/libmaa_unit.so",
1065 "screen_resolution": {
1066 "width": 1920,
1067 "height": 1080
1068 },
1069 "display_id": 1,
1070 "force_stop": true
1071 })
1072 );
1073 }
1074
1075 #[test]
1076 fn linux_controller_config_serializes_expected_shape() {
1077 let config = LinuxControllerConfig {
1078 screencap_method: LinuxScreencapMethod::PIPEWIRE.bits(),
1079 input_method: LinuxInputMethod::UINPUT.bits(),
1080 wlr_socket_path: None,
1081 pw_socket_fd: Some(42),
1082 pw_node_id: Some(7),
1083 uinput_screen_width: Some(1920),
1084 uinput_screen_height: Some(1080),
1085 uinput_path: None,
1086 eis_socket_path: Some("/run/user/1000/gamescope-0-ei".into()),
1087 use_win32_vk_code: Some(true),
1088 };
1089
1090 let value = serde_json::to_value(config).unwrap();
1091
1092 assert_eq!(
1093 value,
1094 json!({
1095 "screencap_method": 4,
1096 "input_method": 2,
1097 "pw_socket_fd": 42,
1098 "pw_node_id": 7,
1099 "uinput_screen_width": 1920,
1100 "uinput_screen_height": 1080,
1101 "eis_socket_path": "/run/user/1000/gamescope-0-ei",
1102 "use_win32_vk_code": true
1103 })
1104 );
1105 }
1106
1107 #[test]
1108 fn linux_controller_config_accepts_legacy_size_keys() {
1109 let config: LinuxControllerConfig = serde_json::from_value(json!({
1110 "screencap_method": 4,
1111 "input_method": 2,
1112 "pw_screen_width": 1920,
1113 "pw_screen_height": 1080
1114 }))
1115 .unwrap();
1116
1117 assert_eq!(config.uinput_screen_width, Some(1920));
1118 assert_eq!(config.uinput_screen_height, Some(1080));
1119 }
1120}