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