use serde::{Deserialize, Serialize};
pub type NodeRef = u64;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct SemanticNode {
pub id: NodeRef,
pub role: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub toggled: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expanded: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub selected: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub level: Option<usize>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub disabled: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub focused: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub live: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub numeric_value: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bounds: Option<NodeBounds>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub actions: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub children: Vec<NodeRef>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
pub struct NodeBounds {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct LayoutNode {
pub id: NodeRef,
#[serde(rename = "type")]
pub type_name: String,
pub bounds: NodeBounds,
pub active: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub clips_children: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent: Option<NodeRef>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub children: Vec<NodeRef>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub debug: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct WindowInfo {
pub id: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub focused: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct AnnouncementDto {
pub seq: u64,
pub text: String,
pub politeness: String,
}
impl From<teksilo_core::Announcement> for AnnouncementDto {
fn from(a: teksilo_core::Announcement) -> Self {
Self {
seq: a.seq,
text: a.text,
politeness: if a.assertive { "assertive" } else { "polite" }.to_string(),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ShortcutInfo {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub primary: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub secondary: Option<String>,
pub enabled: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
#[serde(rename_all = "snake_case")]
pub enum PointerAction {
#[default]
Click,
DoubleClick,
Down,
Up,
Move,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
#[serde(rename_all = "snake_case")]
pub enum PointerButtonDto {
#[default]
Primary,
Secondary,
Middle,
Back,
Forward,
}
impl PointerButtonDto {
pub fn to_core(self) -> teksilo_core::PointerButton {
use teksilo_core::PointerButton as B;
match self {
PointerButtonDto::Primary => B::Primary,
PointerButtonDto::Secondary => B::Secondary,
PointerButtonDto::Middle => B::Middle,
PointerButtonDto::Back => B::Back,
PointerButtonDto::Forward => B::Forward,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Assertion {
Exists,
Focused,
RoleEquals { value: String },
LabelEquals { value: String },
LabelContains { value: String },
ValueEquals { value: String },
Toggled { value: bool },
Expanded { value: bool },
Selected { value: bool },
Disabled { value: bool },
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct AssertionResult {
pub passed: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
}
impl AssertionResult {
pub fn passed() -> Self {
Self {
passed: true,
detail: None,
}
}
pub fn failed(detail: impl Into<String>) -> Self {
Self {
passed: false,
detail: Some(detail.into()),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum WaitCondition {
NodeExists {
#[serde(default, skip_serializing_if = "Option::is_none")]
role: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
label: Option<String>,
},
NodeValue { node: NodeRef, expected: String },
NodeGone { node: NodeRef },
AtVersionAtLeast { version: u64 },
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SettleSpec {
#[serde(default)]
pub clock_millis: u64,
#[serde(default = "default_max_anim_frames")]
pub max_anim_frames: u32,
#[serde(default = "default_true")]
pub layout_after: bool,
#[serde(default = "default_settle_timeout")]
pub settle_timeout_ms: u64,
}
fn default_max_anim_frames() -> u32 {
60
}
fn default_true() -> bool {
true
}
fn default_settle_timeout() -> u64 {
500
}
impl Default for SettleSpec {
fn default() -> Self {
Self {
clock_millis: 0,
max_anim_frames: default_max_anim_frames(),
layout_after: true,
settle_timeout_ms: default_settle_timeout(),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(deny_unknown_fields)]
pub enum AutomationOp {
SnapshotTree {
#[serde(default)]
max_depth: Option<usize>,
},
ReadNode {
node: NodeRef,
},
LayoutTree {
#[serde(default)]
max_depth: Option<usize>,
#[serde(default)]
include_debug: bool,
},
InspectNode {
node: NodeRef,
},
FindNode {
#[serde(default)]
role: Option<String>,
#[serde(default)]
label: Option<String>,
},
AssertNode {
node: NodeRef,
assertion: Assertion,
},
ListWindows,
InvokeAction {
node: NodeRef,
action: String,
},
FocusNode {
node: NodeRef,
},
SetValue {
node: NodeRef,
value: String,
},
Expand {
node: NodeRef,
},
Collapse {
node: NodeRef,
},
Scroll {
node: NodeRef,
#[serde(default)]
dx: f32,
#[serde(default)]
dy: f32,
#[serde(default)]
ctrl: bool,
#[serde(default)]
shift: bool,
#[serde(default)]
alt: bool,
#[serde(default)]
meta: bool,
},
InjectPointer {
x: f32,
y: f32,
#[serde(default)]
action: PointerAction,
#[serde(default)]
button: PointerButtonDto,
#[serde(default)]
ctrl: bool,
#[serde(default)]
shift: bool,
#[serde(default)]
alt: bool,
#[serde(default)]
meta: bool,
},
RightClick {
node: NodeRef,
},
InjectKey {
key: String,
#[serde(default)]
ctrl: bool,
#[serde(default)]
shift: bool,
#[serde(default)]
alt: bool,
#[serde(default)]
meta: bool,
},
TypeText {
node: NodeRef,
text: String,
},
TypeIme {
node: NodeRef,
#[serde(default)]
preedit: Option<String>,
#[serde(default)]
commit: Option<String>,
},
DragNode {
node: NodeRef,
#[serde(default)]
to_node: Option<NodeRef>,
#[serde(default)]
to_x: Option<f32>,
#[serde(default)]
to_y: Option<f32>,
},
GetOverlays,
GetShortcuts,
ListLiveRegions,
PullAnnouncements {
#[serde(default)]
since_seq: u64,
},
AdvanceClock {
millis: u64,
},
Settle,
WaitForCondition {
condition: WaitCondition,
},
Screenshot {
#[serde(default)]
node: Option<NodeRef>,
},
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "status", rename_all = "snake_case")]
pub enum AutomationReply {
Ok {
#[serde(default)]
data: serde_json::Value,
},
Err {
code: String,
message: String,
},
}
impl AutomationReply {
pub fn ok(data: serde_json::Value) -> Self {
AutomationReply::Ok { data }
}
pub fn ok_unit() -> Self {
AutomationReply::Ok {
data: serde_json::Value::Null,
}
}
pub fn ok_json<T: Serialize>(value: &T) -> Self {
match serde_json::to_value(value) {
Ok(data) => AutomationReply::Ok { data },
Err(e) => AutomationReply::err("SERIALIZE_FAILED", e.to_string()),
}
}
pub fn err(code: impl Into<String>, message: impl Into<String>) -> Self {
AutomationReply::Err {
code: code.into(),
message: message.into(),
}
}
pub fn is_ok(&self) -> bool {
matches!(self, AutomationReply::Ok { .. })
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(deny_unknown_fields)]
pub struct AutomationRequest {
#[serde(default)]
pub window_id: Option<u64>,
pub op: AutomationOp,
#[serde(default)]
pub settle: SettleSpec,
}
pub mod codes {
pub const NOT_FOUND: &str = "NOT_FOUND";
pub const BAD_ARGUMENT: &str = "BAD_ARGUMENT";
pub const UNKNOWN_NAME: &str = "UNKNOWN_NAME";
pub const UNHANDLED_ACTION: &str = "UNHANDLED_ACTION";
pub const WAIT_TIMEOUT: &str = "WAIT_TIMEOUT";
pub const SETTLE_TIMEOUT: &str = "SETTLE_TIMEOUT";
pub const GPU_UNAVAILABLE: &str = "GPU_UNAVAILABLE";
pub const HOST_REQUIRED: &str = "HOST_REQUIRED";
pub const ASSERTION_FAILED: &str = "ASSERTION_FAILED";
}