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 #[serde(default)]
718 pub auto_up: bool,
719}
720
721#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
723pub struct LongPressKeyActionResult {
724 pub keycode: Vec<i32>,
725 pub duration: i32,
726}
727
728#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
730pub struct InputTextActionResult {
731 pub text: String,
732}
733
734#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
736pub struct AppActionResult {
737 pub package: String,
738}
739
740#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
742pub struct ScrollActionResult {
743 #[serde(default)]
744 pub point: Point,
745 pub dx: i32,
746 pub dy: i32,
747}
748
749#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
751pub struct TouchActionResult {
752 pub contact: i32,
753 pub point: Point,
754 #[serde(default)]
755 pub pressure: i32,
756 #[serde(default)]
758 pub auto_up: bool,
759}
760
761#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
763pub struct ShellActionResult {
764 pub cmd: String,
765 pub shell_timeout: i32,
766 pub success: bool,
767 pub output: String,
768}
769
770#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
772pub struct NodeDetail {
773 pub node_name: String,
774 pub reco_id: MaaId,
776 pub act_id: MaaId,
778 #[serde(default)]
780 pub recognition: Option<RecognitionDetail>,
781 #[serde(default)]
783 pub action: Option<ActionDetail>,
784 pub completed: bool,
786}
787
788#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
790pub struct TaskDetail {
791 pub entry: String,
793 pub node_id_list: Vec<MaaId>,
795 pub status: MaaStatus,
797 #[serde(default)]
799 pub nodes: Vec<Option<NodeDetail>>,
800}
801
802#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
804#[serde(into = "String", from = "String")]
805pub enum AlgorithmEnum {
806 DirectHit,
807 TemplateMatch,
808 FeatureMatch,
809 ColorMatch,
810 OCR,
811 NeuralNetworkClassify,
812 NeuralNetworkDetect,
813 And,
814 Or,
815 Custom,
816 Other(String),
817}
818
819impl From<String> for AlgorithmEnum {
820 fn from(s: String) -> Self {
821 match s.as_str() {
822 "DirectHit" => Self::DirectHit,
823 "TemplateMatch" => Self::TemplateMatch,
824 "FeatureMatch" => Self::FeatureMatch,
825 "ColorMatch" => Self::ColorMatch,
826 "OCR" => Self::OCR,
827 "NeuralNetworkClassify" => Self::NeuralNetworkClassify,
828 "NeuralNetworkDetect" => Self::NeuralNetworkDetect,
829 "And" => Self::And,
830 "Or" => Self::Or,
831 "Custom" => Self::Custom,
832 _ => Self::Other(s),
833 }
834 }
835}
836
837impl From<AlgorithmEnum> for String {
838 fn from(algo: AlgorithmEnum) -> Self {
839 match algo {
840 AlgorithmEnum::DirectHit => "DirectHit".to_string(),
841 AlgorithmEnum::TemplateMatch => "TemplateMatch".to_string(),
842 AlgorithmEnum::FeatureMatch => "FeatureMatch".to_string(),
843 AlgorithmEnum::ColorMatch => "ColorMatch".to_string(),
844 AlgorithmEnum::OCR => "OCR".to_string(),
845 AlgorithmEnum::NeuralNetworkClassify => "NeuralNetworkClassify".to_string(),
846 AlgorithmEnum::NeuralNetworkDetect => "NeuralNetworkDetect".to_string(),
847 AlgorithmEnum::And => "And".to_string(),
848 AlgorithmEnum::Or => "Or".to_string(),
849 AlgorithmEnum::Custom => "Custom".to_string(),
850 AlgorithmEnum::Other(s) => s,
851 }
852 }
853}
854
855impl AlgorithmEnum {
856 pub fn as_str(&self) -> &str {
857 match self {
858 Self::DirectHit => "DirectHit",
859 Self::TemplateMatch => "TemplateMatch",
860 Self::FeatureMatch => "FeatureMatch",
861 Self::ColorMatch => "ColorMatch",
862 Self::OCR => "OCR",
863 Self::NeuralNetworkClassify => "NeuralNetworkClassify",
864 Self::NeuralNetworkDetect => "NeuralNetworkDetect",
865 Self::And => "And",
866 Self::Or => "Or",
867 Self::Custom => "Custom",
868 Self::Other(s) => s.as_str(),
869 }
870 }
871}
872
873#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
875#[serde(into = "String", from = "String")]
876pub enum ActionEnum {
877 DoNothing,
878 Click,
879 LongPress,
880 Swipe,
881 MultiSwipe,
882 TouchDown,
883 TouchMove,
884 TouchUp,
885 ClickKey,
886 LongPressKey,
887 KeyDown,
888 KeyUp,
889 InputText,
890 StartApp,
891 StopApp,
892 StopTask,
893 Scroll,
894 Command,
895 Shell,
896 Custom,
897 Other(String),
898}
899
900impl From<String> for ActionEnum {
901 fn from(s: String) -> Self {
902 match s.as_str() {
903 "DoNothing" => Self::DoNothing,
904 "Click" => Self::Click,
905 "LongPress" => Self::LongPress,
906 "Swipe" => Self::Swipe,
907 "MultiSwipe" => Self::MultiSwipe,
908 "TouchDown" => Self::TouchDown,
909 "TouchMove" => Self::TouchMove,
910 "TouchUp" => Self::TouchUp,
911 "ClickKey" => Self::ClickKey,
912 "LongPressKey" => Self::LongPressKey,
913 "KeyDown" => Self::KeyDown,
914 "KeyUp" => Self::KeyUp,
915 "InputText" => Self::InputText,
916 "StartApp" => Self::StartApp,
917 "StopApp" => Self::StopApp,
918 "StopTask" => Self::StopTask,
919 "Scroll" => Self::Scroll,
920 "Command" => Self::Command,
921 "Shell" => Self::Shell,
922 "Custom" => Self::Custom,
923 _ => Self::Other(s),
924 }
925 }
926}
927
928impl From<ActionEnum> for String {
929 fn from(act: ActionEnum) -> Self {
930 match act {
931 ActionEnum::DoNothing => "DoNothing".to_string(),
932 ActionEnum::Click => "Click".to_string(),
933 ActionEnum::LongPress => "LongPress".to_string(),
934 ActionEnum::Swipe => "Swipe".to_string(),
935 ActionEnum::MultiSwipe => "MultiSwipe".to_string(),
936 ActionEnum::TouchDown => "TouchDown".to_string(),
937 ActionEnum::TouchMove => "TouchMove".to_string(),
938 ActionEnum::TouchUp => "TouchUp".to_string(),
939 ActionEnum::ClickKey => "ClickKey".to_string(),
940 ActionEnum::LongPressKey => "LongPressKey".to_string(),
941 ActionEnum::KeyDown => "KeyDown".to_string(),
942 ActionEnum::KeyUp => "KeyUp".to_string(),
943 ActionEnum::InputText => "InputText".to_string(),
944 ActionEnum::StartApp => "StartApp".to_string(),
945 ActionEnum::StopApp => "StopApp".to_string(),
946 ActionEnum::StopTask => "StopTask".to_string(),
947 ActionEnum::Scroll => "Scroll".to_string(),
948 ActionEnum::Command => "Command".to_string(),
949 ActionEnum::Shell => "Shell".to_string(),
950 ActionEnum::Custom => "Custom".to_string(),
951 ActionEnum::Other(s) => s,
952 }
953 }
954}
955
956impl ActionEnum {
957 pub fn as_str(&self) -> &str {
958 match self {
959 Self::DoNothing => "DoNothing",
960 Self::Click => "Click",
961 Self::LongPress => "LongPress",
962 Self::Swipe => "Swipe",
963 Self::MultiSwipe => "MultiSwipe",
964 Self::TouchDown => "TouchDown",
965 Self::TouchMove => "TouchMove",
966 Self::TouchUp => "TouchUp",
967 Self::ClickKey => "ClickKey",
968 Self::LongPressKey => "LongPressKey",
969 Self::KeyDown => "KeyDown",
970 Self::KeyUp => "KeyUp",
971 Self::InputText => "InputText",
972 Self::StartApp => "StartApp",
973 Self::StopApp => "StopApp",
974 Self::StopTask => "StopTask",
975 Self::Scroll => "Scroll",
976 Self::Command => "Command",
977 Self::Shell => "Shell",
978 Self::Custom => "Custom",
979 Self::Other(s) => s.as_str(),
980 }
981 }
982}
983
984#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
986#[non_exhaustive]
987pub enum NotificationType {
988 Starting,
989 Succeeded,
990 Failed,
991 Unknown,
992}
993
994impl NotificationType {
995 pub fn from_message(msg: &str) -> Self {
996 if msg.ends_with(".Starting") {
997 Self::Starting
998 } else if msg.ends_with(".Succeeded") {
999 Self::Succeeded
1000 } else if msg.ends_with(".Failed") {
1001 Self::Failed
1002 } else {
1003 Self::Unknown
1004 }
1005 }
1006}
1007
1008#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1011pub struct BoxAndScore {
1012 #[serde(rename = "box")]
1013 pub box_rect: (i32, i32, i32, i32),
1014 pub score: f64,
1015}
1016
1017#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1018pub struct BoxAndCount {
1019 #[serde(rename = "box")]
1020 pub box_rect: (i32, i32, i32, i32),
1021 pub count: i32,
1022}
1023
1024pub type TemplateMatchResult = BoxAndScore;
1025pub type FeatureMatchResult = BoxAndCount;
1026pub type ColorMatchResult = BoxAndCount;
1027
1028#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1029pub struct OCRResult {
1030 #[serde(flatten)]
1031 pub base: BoxAndScore,
1032 pub text: String,
1033}
1034
1035#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1036pub struct NeuralNetworkResult {
1037 #[serde(flatten)]
1038 pub base: BoxAndScore,
1039 pub cls_index: i32,
1040 pub label: String,
1041}
1042
1043pub type NeuralNetworkClassifyResult = NeuralNetworkResult;
1044pub type NeuralNetworkDetectResult = NeuralNetworkResult;
1045
1046#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1047pub struct CustomRecognitionResult {
1048 #[serde(rename = "box")]
1049 pub box_rect: (i32, i32, i32, i32),
1050 pub detail: serde_json::Value,
1051}
1052
1053#[cfg(test)]
1054mod tests {
1055 use super::{
1056 AndroidNativeControllerConfig, AndroidScreenResolution, ClickKeyActionResult,
1057 LinuxControllerConfig, LinuxInputMethod, LinuxScreencapMethod, TouchActionResult,
1058 };
1059 use serde_json::json;
1060
1061 #[test]
1062 fn input_action_results_preserve_auto_up() {
1063 for auto_up in [None, Some(false), Some(true)] {
1064 let mut key = json!({"keycode": [65]});
1065 let mut touch = json!({"contact": 1, "point": [100, 200], "pressure": 2});
1066 if let Some(value) = auto_up {
1067 key["auto_up"] = json!(value);
1068 touch["auto_up"] = json!(value);
1069 }
1070 let key: ClickKeyActionResult = serde_json::from_value(key).unwrap();
1071 let touch: TouchActionResult = serde_json::from_value(touch).unwrap();
1072 assert_eq!(key.auto_up, auto_up.unwrap_or(false));
1073 assert_eq!(touch.auto_up, auto_up.unwrap_or(false));
1074 assert_eq!(
1075 serde_json::to_value(key).unwrap()["auto_up"],
1076 auto_up.unwrap_or(false)
1077 );
1078 assert_eq!(
1079 serde_json::to_value(touch).unwrap()["auto_up"],
1080 auto_up.unwrap_or(false)
1081 );
1082 }
1083 }
1084
1085 #[test]
1086 fn android_native_controller_config_serializes_expected_shape() {
1087 let config = AndroidNativeControllerConfig {
1088 library_path: "/data/local/tmp/libmaa_unit.so".to_string(),
1089 screen_resolution: AndroidScreenResolution {
1090 width: 1920,
1091 height: 1080,
1092 },
1093 display_id: Some(1),
1094 force_stop: Some(true),
1095 };
1096
1097 let value = serde_json::to_value(config).unwrap();
1098
1099 assert_eq!(
1100 value,
1101 json!({
1102 "library_path": "/data/local/tmp/libmaa_unit.so",
1103 "screen_resolution": {
1104 "width": 1920,
1105 "height": 1080
1106 },
1107 "display_id": 1,
1108 "force_stop": true
1109 })
1110 );
1111 }
1112
1113 #[test]
1114 fn linux_controller_config_serializes_expected_shape() {
1115 let config = LinuxControllerConfig {
1116 screencap_method: LinuxScreencapMethod::PIPEWIRE.bits(),
1117 input_method: LinuxInputMethod::UINPUT.bits(),
1118 wlr_socket_path: None,
1119 pw_socket_fd: Some(42),
1120 pw_node_id: Some(7),
1121 uinput_screen_width: Some(1920),
1122 uinput_screen_height: Some(1080),
1123 uinput_path: None,
1124 eis_socket_path: Some("/run/user/1000/gamescope-0-ei".into()),
1125 use_win32_vk_code: Some(true),
1126 };
1127
1128 let value = serde_json::to_value(config).unwrap();
1129
1130 assert_eq!(
1131 value,
1132 json!({
1133 "screencap_method": 4,
1134 "input_method": 2,
1135 "pw_socket_fd": 42,
1136 "pw_node_id": 7,
1137 "uinput_screen_width": 1920,
1138 "uinput_screen_height": 1080,
1139 "eis_socket_path": "/run/user/1000/gamescope-0-ei",
1140 "use_win32_vk_code": true
1141 })
1142 );
1143 }
1144
1145 #[test]
1146 fn linux_controller_config_accepts_legacy_size_keys() {
1147 let config: LinuxControllerConfig = serde_json::from_value(json!({
1148 "screencap_method": 4,
1149 "input_method": 2,
1150 "pw_screen_width": 1920,
1151 "pw_screen_height": 1080
1152 }))
1153 .unwrap();
1154
1155 assert_eq!(config.uinput_screen_width, Some(1920));
1156 assert_eq!(config.uinput_screen_height, Some(1080));
1157 }
1158}