mirror of
https://github.com/lahfir/agent-desktop.git
synced 2026-07-26 17:12:15 +00:00
refactor!: modernize toolchain to Rust 2024 edition
- Bump workspace edition 2021 → 2024 and MSRV 1.78 → 1.85. - Add [workspace.lints] block with project-wide rust/clippy policy: unsafe_op_in_unsafe_fn warn, unused_must_use deny, dbg_macro deny. - Migrate all extern "C" blocks to unsafe extern "C" (Edition 2024 req). - Migrate all #[no_mangle] to #[unsafe(no_mangle)] (Edition 2024 req). - Wrap unsafe operations inside unsafe fn bodies in explicit unsafe blocks via cargo fix. - Switch RefEntry::path from Vec<usize> to SmallVec<[usize; 8]> with serde+union features. Eliminates per-ref heap allocation for typical accessibility tree depths (<8) while serializing identically to the prior Vec form. - Strip non-doc-comments from workspace Cargo.toml. BREAKING CHANGE: minimum supported Rust version is now 1.85. Downstream consumers building from source must upgrade their toolchain.
This commit is contained in:
parent
1f1d8d1b53
commit
11427b271a
147 changed files with 661 additions and 548 deletions
30
Cargo.toml
30
Cargo.toml
|
|
@ -3,8 +3,8 @@ members = ["crates/core", "crates/macos", "crates/windows", "crates/linux", "cr
|
|||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.78"
|
||||
edition = "2024"
|
||||
rust-version = "1.85"
|
||||
license = "Apache-2.0"
|
||||
version = "0.1.14" # x-release-please-version
|
||||
|
||||
|
|
@ -18,8 +18,29 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|||
base64 = "0.22"
|
||||
rustc-hash = "2.1"
|
||||
libc = "0.2"
|
||||
smallvec = { version = "1.13", features = ["serde", "union"] }
|
||||
agent-desktop-core = { path = "crates/core" }
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_op_in_unsafe_fn = "warn"
|
||||
unreachable_pub = "allow"
|
||||
unused_must_use = "deny"
|
||||
let_underscore_drop = "allow"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
dbg_macro = "deny"
|
||||
todo = "warn"
|
||||
needless_collect = "warn"
|
||||
redundant_clone = "allow"
|
||||
unwrap_used = "allow"
|
||||
expect_used = "allow"
|
||||
needless_pass_by_value = "allow"
|
||||
manual_let_else = "allow"
|
||||
items_after_statements = "allow"
|
||||
collapsible_if = "allow"
|
||||
print_stdout = "allow"
|
||||
print_stderr = "allow"
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
|
|
@ -27,8 +48,6 @@ codegen-units = 1
|
|||
strip = true
|
||||
panic = "abort"
|
||||
|
||||
# Fast build for CI: skip LTO and size optimisations, keep debug info for
|
||||
# better test failure messages. Inherits release settings not listed here.
|
||||
[profile.ci]
|
||||
inherits = "release"
|
||||
opt-level = 1
|
||||
|
|
@ -37,9 +56,6 @@ codegen-units = 16
|
|||
strip = false
|
||||
debug = 1
|
||||
|
||||
# Dedicated profile for the FFI cdylib. Overrides panic = "abort" so
|
||||
# catch_unwind actually catches at the extern "C" boundary — the CLI
|
||||
# release profile stays abort for size reasons.
|
||||
[profile.release-ffi]
|
||||
inherits = "release"
|
||||
panic = "unwind"
|
||||
|
|
|
|||
|
|
@ -12,3 +12,7 @@ tracing.workspace = true
|
|||
rustc-hash.workspace = true
|
||||
base64.workspace = true
|
||||
libc.workspace = true
|
||||
smallvec.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{
|
||||
PermissionReport, PermissionState,
|
||||
action::{
|
||||
ActionRequest, ActionResult, DragParams, ElementState, KeyCombo, MouseEvent, WindowOp,
|
||||
},
|
||||
|
|
@ -6,7 +7,6 @@ use crate::{
|
|||
node::{AccessibilityNode, AppInfo, Rect, SurfaceInfo, WindowInfo},
|
||||
notification::{NotificationFilter, NotificationIdentity, NotificationInfo},
|
||||
refs::RefEntry,
|
||||
PermissionReport, PermissionState,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub fn execute(adapter: &dyn PlatformAdapter) -> Result<Value, AppError> {
|
||||
adapter.clear_clipboard()?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub fn execute(adapter: &dyn PlatformAdapter) -> Result<Value, AppError> {
|
||||
let text = adapter.get_clipboard()?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub fn execute(text: String, adapter: &dyn PlatformAdapter) -> Result<Value, AppError> {
|
||||
adapter.set_clipboard(&text)?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const PROTECTED_PROCESSES: &[&str] = &["loginwindow", "windowserver", "dock", "launchd", "finder"];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct DismissAllNotificationsArgs {
|
||||
pub app: Option<String>,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct DismissNotificationArgs {
|
||||
pub index: usize,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
commands::helpers::resolve_ref,
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct DragArgs {
|
||||
pub from_ref: Option<String>,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
adapter::PlatformAdapter, commands::search_text, error::AppError, node::AccessibilityNode,
|
||||
snapshot,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const DEFAULT_LIMIT: usize = 50;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
error::{AdapterError, AppError, ErrorCode},
|
||||
node::WindowInfo,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(not(test))]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, commands::helpers::resolve_ref, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct GetArgs {
|
||||
pub ref_id: String,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
refs_store::RefStore,
|
||||
window_lookup,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct AppArgs {
|
||||
pub app: Option<String>,
|
||||
|
|
@ -137,8 +137,8 @@ mod tests {
|
|||
use crate::node::AppInfo;
|
||||
use crate::refs::RefMap;
|
||||
use crate::refs_test_support::HomeGuard;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
struct ReleaseCountingAdapter {
|
||||
releases: AtomicU32,
|
||||
|
|
@ -215,7 +215,7 @@ mod tests {
|
|||
source_app: None,
|
||||
source_window_title: None,
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
commands::helpers::resolve_ref,
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct HoverArgs {
|
||||
pub ref_id: Option<String>,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
action::ElementState, adapter::PlatformAdapter, commands::helpers::resolve_ref,
|
||||
error::AppError, refs::RefEntry,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct IsArgs {
|
||||
pub ref_id: String,
|
||||
|
|
@ -130,7 +130,7 @@ mod tests {
|
|||
source_app: None,
|
||||
source_window_title: None,
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,7 +252,7 @@ mod tests {
|
|||
source_app: None,
|
||||
source_window_title: None,
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
});
|
||||
let adapter = LiveStateAdapter {
|
||||
state: Mutex::new(None),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, commands::press::parse_combo, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct KeyDownArgs {
|
||||
pub combo: String,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, commands::press::parse_combo, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct KeyUpArgs {
|
||||
pub combo: String,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, commands::search_text, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct ListAppsArgs {
|
||||
pub app: Option<String>,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, error::AppError, notification::NotificationFilter};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct ListNotificationsArgs {
|
||||
pub app: Option<String>,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, commands::helpers::resolve_app_pid, error::AppError};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct ListSurfacesArgs {
|
||||
pub app: Option<String>,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::WindowOp,
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{window_op_command, AppArgs},
|
||||
commands::helpers::{AppArgs, window_op_command},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::WindowOp,
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{window_op_command, AppArgs},
|
||||
commands::helpers::{AppArgs, window_op_command},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
adapter::PlatformAdapter,
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct MouseClickArgs {
|
||||
pub x: f64,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
adapter::PlatformAdapter,
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct MouseDownArgs {
|
||||
pub x: f64,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
adapter::PlatformAdapter,
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct MouseMoveArgs {
|
||||
pub x: f64,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
adapter::PlatformAdapter,
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct MouseUpArgs {
|
||||
pub x: f64,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
action::WindowOp, adapter::PlatformAdapter, commands::helpers::resolve_window_for_app,
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct MoveWindowArgs {
|
||||
pub app: Option<String>,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, error::AppError, notification::NotificationIdentity};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct NotificationActionArgs {
|
||||
pub index: usize,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{adapter::PlatformAdapter, error::AppError, PermissionReport};
|
||||
use serde_json::{json, Value};
|
||||
use crate::{PermissionReport, adapter::PlatformAdapter, error::AppError};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct PermissionsArgs {
|
||||
pub request: bool,
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ pub fn parse_combo(s: &str) -> Result<KeyCombo, AppError> {
|
|||
other => {
|
||||
return Err(AppError::invalid_input(format!(
|
||||
"Unknown modifier: '{other}'"
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
modifiers.push(modifier);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ fn snapshot_id() -> String {
|
|||
source_app: None,
|
||||
source_window_title: None,
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
});
|
||||
RefStore::new().unwrap().save_new_snapshot(&refmap).unwrap()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
action::WindowOp, adapter::PlatformAdapter, commands::helpers::resolve_window_for_app,
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct ResizeWindowArgs {
|
||||
pub app: Option<String>,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::WindowOp,
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{window_op_command, AppArgs},
|
||||
commands::helpers::{AppArgs, window_op_command},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::{PlatformAdapter, SnapshotSurface, TreeOptions, WindowFilter},
|
||||
commands::helpers::{resolve_ref, RefArgs},
|
||||
commands::helpers::{RefArgs, resolve_ref},
|
||||
error::AppError,
|
||||
refs::RefEntry,
|
||||
snapshot,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub fn execute(args: RefArgs, adapter: &dyn PlatformAdapter) -> Result<Value, AppError> {
|
||||
let (entry, handle) = resolve_ref(&args.ref_id, args.snapshot_id.as_deref(), adapter)?;
|
||||
|
|
@ -180,7 +180,7 @@ mod tests {
|
|||
source_app,
|
||||
source_window_title: None,
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
});
|
||||
RefStore::new().unwrap().save_new_snapshot(&refmap).unwrap()
|
||||
}
|
||||
|
|
@ -223,9 +223,11 @@ mod tests {
|
|||
assert_eq!(value["action"], "right_click");
|
||||
assert_eq!(value["menu_probe"]["ok"], false);
|
||||
assert_eq!(value["menu_probe"]["error"]["code"], "ELEMENT_NOT_FOUND");
|
||||
assert!(value["menu_probe"]["error"]["suggestion"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.contains("snapshot --surface menu"));
|
||||
assert!(
|
||||
value["menu_probe"]["error"]["suggestion"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.contains("snapshot --surface menu")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
error::AppError,
|
||||
};
|
||||
use base64::Engine;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct ScreenshotArgs {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::error::AppError;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const SKILL_DESKTOP_MAIN: &str = include_str!("../../../../skills/agent-desktop/SKILL.md");
|
||||
const SKILL_DESKTOP_REF_OBSERVATION: &str =
|
||||
|
|
@ -43,11 +43,26 @@ const SKILLS: &[Skill] = &[
|
|||
summary: "Primary guide. Snapshot/ref loop, JSON envelope, 54 commands across observation, interaction, keyboard/mouse, app lifecycle, notifications, clipboard, wait.",
|
||||
main: SKILL_DESKTOP_MAIN,
|
||||
refs: &[
|
||||
SkillRef { rel_path: "references/commands-observation.md", body: SKILL_DESKTOP_REF_OBSERVATION },
|
||||
SkillRef { rel_path: "references/commands-interaction.md", body: SKILL_DESKTOP_REF_INTERACTION },
|
||||
SkillRef { rel_path: "references/commands-system.md", body: SKILL_DESKTOP_REF_SYSTEM },
|
||||
SkillRef { rel_path: "references/workflows.md", body: SKILL_DESKTOP_REF_WORKFLOWS },
|
||||
SkillRef { rel_path: "references/macos.md", body: SKILL_DESKTOP_REF_MACOS },
|
||||
SkillRef {
|
||||
rel_path: "references/commands-observation.md",
|
||||
body: SKILL_DESKTOP_REF_OBSERVATION,
|
||||
},
|
||||
SkillRef {
|
||||
rel_path: "references/commands-interaction.md",
|
||||
body: SKILL_DESKTOP_REF_INTERACTION,
|
||||
},
|
||||
SkillRef {
|
||||
rel_path: "references/commands-system.md",
|
||||
body: SKILL_DESKTOP_REF_SYSTEM,
|
||||
},
|
||||
SkillRef {
|
||||
rel_path: "references/workflows.md",
|
||||
body: SKILL_DESKTOP_REF_WORKFLOWS,
|
||||
},
|
||||
SkillRef {
|
||||
rel_path: "references/macos.md",
|
||||
body: SKILL_DESKTOP_REF_MACOS,
|
||||
},
|
||||
],
|
||||
},
|
||||
Skill {
|
||||
|
|
@ -56,10 +71,22 @@ const SKILLS: &[Skill] = &[
|
|||
summary: "Embedding agent-desktop in another process via the C ABI. Build/link, error propagation, handle ownership, threading rules.",
|
||||
main: SKILL_FFI_MAIN,
|
||||
refs: &[
|
||||
SkillRef { rel_path: "references/build-and-link.md", body: SKILL_FFI_REF_BUILD },
|
||||
SkillRef { rel_path: "references/error-handling.md", body: SKILL_FFI_REF_ERRORS },
|
||||
SkillRef { rel_path: "references/ownership.md", body: SKILL_FFI_REF_OWNERSHIP },
|
||||
SkillRef { rel_path: "references/threading.md", body: SKILL_FFI_REF_THREADING },
|
||||
SkillRef {
|
||||
rel_path: "references/build-and-link.md",
|
||||
body: SKILL_FFI_REF_BUILD,
|
||||
},
|
||||
SkillRef {
|
||||
rel_path: "references/error-handling.md",
|
||||
body: SKILL_FFI_REF_ERRORS,
|
||||
},
|
||||
SkillRef {
|
||||
rel_path: "references/ownership.md",
|
||||
body: SKILL_FFI_REF_OWNERSHIP,
|
||||
},
|
||||
SkillRef {
|
||||
rel_path: "references/threading.md",
|
||||
body: SKILL_FFI_REF_THREADING,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
error::AppError,
|
||||
snapshot, snapshot_ref,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct SnapshotArgs {
|
||||
pub app: Option<String>,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use crate::{
|
||||
PermissionReport,
|
||||
adapter::PlatformAdapter,
|
||||
commands::permissions::{self, PermissionsArgs},
|
||||
error::AppError,
|
||||
refs_store::RefStore,
|
||||
PermissionReport,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub fn execute(adapter: &dyn PlatformAdapter) -> Result<Value, AppError> {
|
||||
let report = adapter.permission_report();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{Action, ActionRequest},
|
||||
adapter::PlatformAdapter,
|
||||
commands::helpers::{execute_ref_action, RefArgs},
|
||||
commands::helpers::{RefArgs, execute_ref_action},
|
||||
error::AppError,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::error::AppError;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct VersionArgs {
|
||||
pub json: bool,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
refs_store::RefStore,
|
||||
snapshot,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub struct WaitArgs {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ fn snapshot_with_one_ref() -> String {
|
|||
source_app: None,
|
||||
source_window_title: None,
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
});
|
||||
RefStore::new().unwrap().save_new_snapshot(&refmap).unwrap()
|
||||
}
|
||||
|
|
@ -142,7 +142,7 @@ fn latest_ref_cache_picks_up_newer_snapshot_after_refresh() {
|
|||
source_app: None,
|
||||
source_window_title: None,
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
});
|
||||
let second_id = store.save_new_snapshot(&second).unwrap();
|
||||
assert_ne!(first_id, second_id);
|
||||
|
|
@ -178,7 +178,7 @@ fn latest_ref_cache_debounces_consecutive_refreshes() {
|
|||
source_app: None,
|
||||
source_window_title: None,
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
});
|
||||
let _ = store.save_new_snapshot(&other).unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ pub(crate) fn ref_entry_from_node(
|
|||
source_app: source_app.map(str::to_string),
|
||||
source_window_title: source_window_title.map(str::to_string),
|
||||
root_ref,
|
||||
path: path.to_vec(),
|
||||
path: smallvec::SmallVec::from_slice(path),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +122,6 @@ fn allocate_refs_at_path(
|
|||
config: &RefAllocConfig,
|
||||
path: &mut Vec<usize>,
|
||||
) -> AccessibilityNode {
|
||||
let root_ref_owned = config.root_ref_id.map(str::to_string);
|
||||
let is_interactive = INTERACTIVE_ROLES.contains(&node.role.as_str());
|
||||
|
||||
if is_interactive {
|
||||
|
|
@ -131,7 +130,7 @@ fn allocate_refs_at_path(
|
|||
config.pid,
|
||||
config.source_app,
|
||||
config.source_window_title,
|
||||
root_ref_owned.clone(),
|
||||
config.root_ref_id.map(str::to_string),
|
||||
path,
|
||||
);
|
||||
node.ref_id = Some(refmap.allocate(entry));
|
||||
|
|
@ -304,7 +303,7 @@ mod tests {
|
|||
|
||||
let save_ref = out.children[0].ref_id.as_deref().unwrap();
|
||||
let open_ref = out.children[1].children[0].ref_id.as_deref().unwrap();
|
||||
assert_eq!(refmap.get(save_ref).unwrap().path, vec![0]);
|
||||
assert_eq!(refmap.get(open_ref).unwrap().path, vec![1, 0]);
|
||||
assert_eq!(refmap.get(save_ref).unwrap().path.as_slice(), [0]);
|
||||
assert_eq!(refmap.get(open_ref).unwrap().path.as_slice(), [1, 0]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
use crate::error::AppError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smallvec::SmallVec;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::hash_map::RandomState;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::RandomState;
|
||||
use std::hash::BuildHasher;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
pub type RefPath = SmallVec<[usize; 8]>;
|
||||
|
||||
pub(crate) const MAX_REFMAP_BYTES: u64 = 1_048_576;
|
||||
static SNAPSHOT_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
|
|
@ -33,8 +36,8 @@ pub struct RefEntry {
|
|||
pub source_window_title: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub root_ref: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub path: Vec<usize>,
|
||||
#[serde(default, skip_serializing_if = "SmallVec::is_empty")]
|
||||
pub path: RefPath,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
error::{AdapterError, AppError},
|
||||
refs::{home_dir, new_snapshot_id, validate_snapshot_id, write_private_file, RefMap},
|
||||
refs::{RefMap, home_dir, new_snapshot_id, validate_snapshot_id, write_private_file},
|
||||
refs_lock::RefStoreLock,
|
||||
};
|
||||
use std::io::Read;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ fn entry(role: &str, name: Option<&str>) -> RefEntry {
|
|||
source_app: None,
|
||||
source_window_title: None,
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ fn test_get_existing() {
|
|||
source_app: Some("Finder".into()),
|
||||
source_window_title: Some("Documents".into()),
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
});
|
||||
let retrieved = map.get(&ref_id).unwrap();
|
||||
assert_eq!(retrieved.pid, 42);
|
||||
|
|
@ -167,7 +167,7 @@ fn test_save_load_roundtrip_with_home_override() {
|
|||
source_app: Some("TestApp".into()),
|
||||
source_window_title: Some("Test Window".into()),
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
});
|
||||
map.save().expect("save should succeed under HomeGuard");
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ fn test_refstore_snapshot_roundtrip_and_latest_pointer() {
|
|||
source_app: Some("TestApp".into()),
|
||||
source_window_title: Some("Test Window".into()),
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
});
|
||||
|
||||
let snapshot_id = store.save_new_snapshot(&map).unwrap();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ publish = false
|
|||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
smallvec.workspace = true
|
||||
agent-desktop-core.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
|
|
@ -23,3 +24,6 @@ agent-desktop-linux = { path = "../linux" }
|
|||
|
||||
[build-dependencies]
|
||||
cbindgen = "= 0.27.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
use std::panic::AssertUnwindSafe;
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn spike_panicking_entry() -> i32 {
|
||||
std::panic::catch_unwind(AssertUnwindSafe(|| -> i32 {
|
||||
panic!("synthetic panic inside extern C fn");
|
||||
|
|
|
|||
|
|
@ -20,95 +20,99 @@ fn direction_from_c(d: AdDirection) -> Direction {
|
|||
const MAX_MODIFIERS_PER_COMBO: u32 = 4;
|
||||
|
||||
pub(crate) unsafe fn key_combo_from_c(k: &AdKeyCombo) -> Result<CoreKeyCombo, &'static str> {
|
||||
let key = c_to_string(k.key).ok_or("key is null or invalid UTF-8")?;
|
||||
unsafe {
|
||||
let key = c_to_string(k.key).ok_or("key is null or invalid UTF-8")?;
|
||||
|
||||
if k.modifier_count > MAX_MODIFIERS_PER_COMBO {
|
||||
return Err("modifier_count exceeds MAX_MODIFIERS_PER_COMBO (4)");
|
||||
}
|
||||
if k.modifier_count > 0 && k.modifiers.is_null() {
|
||||
return Err("modifier_count > 0 but modifiers pointer is null");
|
||||
}
|
||||
|
||||
let mut modifiers = Vec::with_capacity(k.modifier_count as usize);
|
||||
if k.modifier_count > 0 {
|
||||
let slice = std::slice::from_raw_parts(k.modifiers, k.modifier_count as usize);
|
||||
for raw_modifier in slice {
|
||||
let m = AdModifier::from_c(*raw_modifier).ok_or("invalid modifier discriminant")?;
|
||||
let modifier = match m {
|
||||
AdModifier::Cmd => Modifier::Cmd,
|
||||
AdModifier::Ctrl => Modifier::Ctrl,
|
||||
AdModifier::Alt => Modifier::Alt,
|
||||
AdModifier::Shift => Modifier::Shift,
|
||||
};
|
||||
modifiers.push(modifier);
|
||||
if k.modifier_count > MAX_MODIFIERS_PER_COMBO {
|
||||
return Err("modifier_count exceeds MAX_MODIFIERS_PER_COMBO (4)");
|
||||
}
|
||||
if k.modifier_count > 0 && k.modifiers.is_null() {
|
||||
return Err("modifier_count > 0 but modifiers pointer is null");
|
||||
}
|
||||
|
||||
let mut modifiers = Vec::with_capacity(k.modifier_count as usize);
|
||||
if k.modifier_count > 0 {
|
||||
let slice = std::slice::from_raw_parts(k.modifiers, k.modifier_count as usize);
|
||||
for raw_modifier in slice {
|
||||
let m = AdModifier::from_c(*raw_modifier).ok_or("invalid modifier discriminant")?;
|
||||
let modifier = match m {
|
||||
AdModifier::Cmd => Modifier::Cmd,
|
||||
AdModifier::Ctrl => Modifier::Ctrl,
|
||||
AdModifier::Alt => Modifier::Alt,
|
||||
AdModifier::Shift => Modifier::Shift,
|
||||
};
|
||||
modifiers.push(modifier);
|
||||
}
|
||||
}
|
||||
Ok(CoreKeyCombo { key, modifiers })
|
||||
}
|
||||
Ok(CoreKeyCombo { key, modifiers })
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn action_from_c(action: &AdAction) -> Result<Action, &'static str> {
|
||||
let kind = AdActionKind::from_c(action.kind).ok_or("invalid action kind discriminant")?;
|
||||
match kind {
|
||||
AdActionKind::Click => Ok(Action::Click),
|
||||
AdActionKind::DoubleClick => Ok(Action::DoubleClick),
|
||||
AdActionKind::RightClick => Ok(Action::RightClick),
|
||||
AdActionKind::TripleClick => Ok(Action::TripleClick),
|
||||
AdActionKind::SetFocus => Ok(Action::SetFocus),
|
||||
AdActionKind::Expand => Ok(Action::Expand),
|
||||
AdActionKind::Collapse => Ok(Action::Collapse),
|
||||
AdActionKind::Toggle => Ok(Action::Toggle),
|
||||
AdActionKind::Check => Ok(Action::Check),
|
||||
AdActionKind::Uncheck => Ok(Action::Uncheck),
|
||||
AdActionKind::ScrollTo => Ok(Action::ScrollTo),
|
||||
AdActionKind::Clear => Ok(Action::Clear),
|
||||
AdActionKind::Hover => Ok(Action::Hover),
|
||||
AdActionKind::SetValue => {
|
||||
let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?;
|
||||
Ok(Action::SetValue(text))
|
||||
}
|
||||
AdActionKind::Select => {
|
||||
let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?;
|
||||
Ok(Action::Select(text))
|
||||
}
|
||||
AdActionKind::TypeText => {
|
||||
let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?;
|
||||
Ok(Action::TypeText(text))
|
||||
}
|
||||
AdActionKind::Scroll => {
|
||||
let raw_dir = AdDirection::from_c(action.scroll.direction)
|
||||
.ok_or("invalid scroll direction discriminant")?;
|
||||
let dir = direction_from_c(raw_dir);
|
||||
Ok(Action::Scroll(dir, action.scroll.amount))
|
||||
}
|
||||
AdActionKind::PressKey => {
|
||||
let combo = key_combo_from_c(&action.key)?;
|
||||
Ok(Action::PressKey(combo))
|
||||
}
|
||||
AdActionKind::KeyDown => {
|
||||
let combo = key_combo_from_c(&action.key)?;
|
||||
Ok(Action::KeyDown(combo))
|
||||
}
|
||||
AdActionKind::KeyUp => {
|
||||
let combo = key_combo_from_c(&action.key)?;
|
||||
Ok(Action::KeyUp(combo))
|
||||
}
|
||||
AdActionKind::Drag => {
|
||||
let params = CoreDragParams {
|
||||
from: CorePoint {
|
||||
x: action.drag.from.x,
|
||||
y: action.drag.from.y,
|
||||
},
|
||||
to: CorePoint {
|
||||
x: action.drag.to.x,
|
||||
y: action.drag.to.y,
|
||||
},
|
||||
duration_ms: if action.drag.duration_ms == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(action.drag.duration_ms)
|
||||
},
|
||||
};
|
||||
Ok(Action::Drag(params))
|
||||
unsafe {
|
||||
let kind = AdActionKind::from_c(action.kind).ok_or("invalid action kind discriminant")?;
|
||||
match kind {
|
||||
AdActionKind::Click => Ok(Action::Click),
|
||||
AdActionKind::DoubleClick => Ok(Action::DoubleClick),
|
||||
AdActionKind::RightClick => Ok(Action::RightClick),
|
||||
AdActionKind::TripleClick => Ok(Action::TripleClick),
|
||||
AdActionKind::SetFocus => Ok(Action::SetFocus),
|
||||
AdActionKind::Expand => Ok(Action::Expand),
|
||||
AdActionKind::Collapse => Ok(Action::Collapse),
|
||||
AdActionKind::Toggle => Ok(Action::Toggle),
|
||||
AdActionKind::Check => Ok(Action::Check),
|
||||
AdActionKind::Uncheck => Ok(Action::Uncheck),
|
||||
AdActionKind::ScrollTo => Ok(Action::ScrollTo),
|
||||
AdActionKind::Clear => Ok(Action::Clear),
|
||||
AdActionKind::Hover => Ok(Action::Hover),
|
||||
AdActionKind::SetValue => {
|
||||
let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?;
|
||||
Ok(Action::SetValue(text))
|
||||
}
|
||||
AdActionKind::Select => {
|
||||
let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?;
|
||||
Ok(Action::Select(text))
|
||||
}
|
||||
AdActionKind::TypeText => {
|
||||
let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?;
|
||||
Ok(Action::TypeText(text))
|
||||
}
|
||||
AdActionKind::Scroll => {
|
||||
let raw_dir = AdDirection::from_c(action.scroll.direction)
|
||||
.ok_or("invalid scroll direction discriminant")?;
|
||||
let dir = direction_from_c(raw_dir);
|
||||
Ok(Action::Scroll(dir, action.scroll.amount))
|
||||
}
|
||||
AdActionKind::PressKey => {
|
||||
let combo = key_combo_from_c(&action.key)?;
|
||||
Ok(Action::PressKey(combo))
|
||||
}
|
||||
AdActionKind::KeyDown => {
|
||||
let combo = key_combo_from_c(&action.key)?;
|
||||
Ok(Action::KeyDown(combo))
|
||||
}
|
||||
AdActionKind::KeyUp => {
|
||||
let combo = key_combo_from_c(&action.key)?;
|
||||
Ok(Action::KeyUp(combo))
|
||||
}
|
||||
AdActionKind::Drag => {
|
||||
let params = CoreDragParams {
|
||||
from: CorePoint {
|
||||
x: action.drag.from.x,
|
||||
y: action.drag.from.y,
|
||||
},
|
||||
to: CorePoint {
|
||||
x: action.drag.to.x,
|
||||
y: action.drag.to.y,
|
||||
},
|
||||
duration_ms: if action.drag.duration_ms == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(action.drag.duration_ms)
|
||||
},
|
||||
};
|
||||
Ok(Action::Drag(params))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::actions::conversion::action_from_c;
|
||||
use crate::actions::result::action_result_to_c;
|
||||
use crate::error::{self, AdResult};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::{AdAction, AdActionResult, AdNativeHandle, AdPolicyKind};
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::{action::ActionRequest, adapter::NativeHandle};
|
||||
|
||||
/// # Safety
|
||||
|
|
@ -12,7 +12,7 @@ use agent_desktop_core::{action::ActionRequest, adapter::NativeHandle};
|
|||
/// `handle` must be a non-null pointer to a valid `AdNativeHandle`.
|
||||
/// `action` must be a non-null pointer to a valid `AdAction`.
|
||||
/// `out` must be a non-null pointer to an `AdActionResult` to write the result into.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_execute_action(
|
||||
adapter: *const AdAdapter,
|
||||
handle: *const AdNativeHandle,
|
||||
|
|
@ -30,7 +30,7 @@ pub unsafe extern "C" fn ad_execute_action(
|
|||
/// `handle` must be a non-null pointer to a valid `AdNativeHandle`.
|
||||
/// `action` must be a non-null pointer to a valid `AdAction`.
|
||||
/// `out` must be a non-null pointer to an `AdActionResult` to write the result into.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_execute_action_with_policy(
|
||||
adapter: *const AdAdapter,
|
||||
handle: *const AdNativeHandle,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::AdAdapter;
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::AdNativeHandle;
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::adapter::NativeHandle;
|
||||
|
||||
/// Releases a handle previously returned by `ad_resolve_element` and
|
||||
|
|
@ -28,7 +28,7 @@ use agent_desktop_core::adapter::NativeHandle;
|
|||
/// `handle` must be null or a `*mut AdNativeHandle` previously
|
||||
/// populated by `ad_resolve_element`. On return `(*handle).ptr` is
|
||||
/// `NULL` so a double-call is a no-op instead of a double-free.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_free_handle(
|
||||
adapter: *const AdAdapter,
|
||||
handle: *mut AdNativeHandle,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::string::{c_to_string, try_c_to_string};
|
||||
use crate::error::{self, AdResult};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::{AdNativeHandle, AdRefEntry};
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::refs::RefEntry as CoreRefEntry;
|
||||
|
||||
/// # Safety
|
||||
|
|
@ -10,7 +10,7 @@ use agent_desktop_core::refs::RefEntry as CoreRefEntry;
|
|||
/// `adapter` must be a non-null pointer returned by `ad_adapter_create`.
|
||||
/// `entry` must be a non-null pointer to a valid `AdRefEntry`.
|
||||
/// `out` must be a non-null pointer to an `AdNativeHandle` to write the result into.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_resolve_element(
|
||||
adapter: *const AdAdapter,
|
||||
entry: *const AdRefEntry,
|
||||
|
|
@ -68,7 +68,7 @@ pub unsafe extern "C" fn ad_resolve_element(
|
|||
source_app: None,
|
||||
source_window_title: None,
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
};
|
||||
match adapter.inner.resolve_element(&core_entry) {
|
||||
Ok(handle) => {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ pub(crate) fn action_result_to_c(r: &CoreActionResult) -> AdActionResult {
|
|||
///
|
||||
/// `result` must be a pointer to an `AdActionResult` previously written by `ad_execute_action`,
|
||||
/// or null. After this call all pointers inside the struct are invalid.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_free_action_result(result: *mut AdActionResult) {
|
||||
crate::ffi_try::trap_panic_void(|| unsafe {
|
||||
if result.is_null() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::error::{self, AdResult};
|
||||
use crate::ffi_try::{trap_panic, trap_panic_ptr, trap_panic_void};
|
||||
use agent_desktop_core::{adapter::PlatformAdapter, PermissionState};
|
||||
use agent_desktop_core::{PermissionState, adapter::PlatformAdapter};
|
||||
|
||||
pub struct AdAdapter {
|
||||
pub(crate) inner: Box<dyn PlatformAdapter>,
|
||||
|
|
@ -33,7 +33,7 @@ fn build_adapter() -> Box<dyn PlatformAdapter> {
|
|||
/// The returned pointer is owned by the caller and must be released with
|
||||
/// `ad_adapter_destroy`. Creating and destroying adapters is cheap; the
|
||||
/// common pattern is one adapter per process lifetime.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn ad_adapter_create() -> *mut AdAdapter {
|
||||
trap_panic_ptr(|| {
|
||||
let adapter = AdAdapter {
|
||||
|
|
@ -47,7 +47,7 @@ pub extern "C" fn ad_adapter_create() -> *mut AdAdapter {
|
|||
///
|
||||
/// `adapter` must be a pointer returned by `ad_adapter_create`, or null.
|
||||
/// After this call the pointer is invalid and must not be used.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_adapter_destroy(adapter: *mut AdAdapter) {
|
||||
trap_panic_void(|| {
|
||||
if !adapter.is_null() {
|
||||
|
|
@ -60,7 +60,7 @@ pub unsafe extern "C" fn ad_adapter_destroy(adapter: *mut AdAdapter) {
|
|||
///
|
||||
/// `adapter` must be a non-null pointer returned by `ad_adapter_create` that
|
||||
/// has not yet been destroyed.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_check_permissions(adapter: *const AdAdapter) -> AdResult {
|
||||
trap_panic(|| {
|
||||
crate::pointer_guard::guard_non_null!(adapter, c"adapter is null");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::convert::string::c_to_string;
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::AdAdapter;
|
||||
use crate::convert::string::c_to_string;
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Closes the application identified by `id` (bundle id on macOS,
|
||||
|
|
@ -10,7 +10,7 @@ use std::os::raw::c_char;
|
|||
///
|
||||
/// # Safety
|
||||
/// `adapter` must be non-null. `id` must be a non-null UTF-8 C string.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_close_app(
|
||||
adapter: *const AdAdapter,
|
||||
id: *const c_char,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::string::c_to_string;
|
||||
use crate::convert::window::window_info_to_c;
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::AdWindowInfo;
|
||||
use crate::AdAdapter;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Launches the application identified by `id` (bundle id on macOS,
|
||||
|
|
@ -19,7 +19,7 @@ use std::os::raw::c_char;
|
|||
/// # Safety
|
||||
/// `adapter` must be non-null. `id` must be a non-null UTF-8 C string.
|
||||
/// `out` must be a non-null writable `*mut AdWindowInfo`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_launch_app(
|
||||
adapter: *const AdAdapter,
|
||||
id: *const c_char,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::app::{app_info_to_c, free_app_info_fields};
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::{trap_panic, trap_panic_void};
|
||||
use crate::types::{AdAppInfo, AdAppList};
|
||||
use crate::AdAdapter;
|
||||
use std::ptr;
|
||||
|
||||
/// # Safety
|
||||
|
|
@ -10,7 +10,7 @@ use std::ptr;
|
|||
/// `out` must be a valid writable `*mut *mut AdAppList`.
|
||||
/// On success, `*out` is a newly-allocated opaque list freed with
|
||||
/// `ad_app_list_free`. On error, `*out` is null and last-error is set.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_list_apps(
|
||||
adapter: *const AdAdapter,
|
||||
out: *mut *mut AdAppList,
|
||||
|
|
@ -42,7 +42,7 @@ pub unsafe extern "C" fn ad_list_apps(
|
|||
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_apps`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_app_list_count(list: *const AdAppList) -> u32 {
|
||||
if list.is_null() {
|
||||
return 0;
|
||||
|
|
@ -56,7 +56,7 @@ pub unsafe extern "C" fn ad_app_list_count(list: *const AdAppList) -> u32 {
|
|||
///
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_apps`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_app_list_get(list: *const AdAppList, index: u32) -> *const AdAppInfo {
|
||||
if list.is_null() {
|
||||
return ptr::null();
|
||||
|
|
@ -73,7 +73,7 @@ pub unsafe extern "C" fn ad_app_list_get(list: *const AdAppList, index: u32) ->
|
|||
///
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_apps`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_app_list_free(list: *mut AdAppList) {
|
||||
trap_panic_void(|| unsafe {
|
||||
if list.is_null() {
|
||||
|
|
|
|||
|
|
@ -13,10 +13,12 @@ pub(crate) fn app_info_to_c(a: &AppInfo) -> AdAppInfo {
|
|||
}
|
||||
|
||||
pub(crate) unsafe fn free_app_info_fields(a: &mut AdAppInfo) {
|
||||
free_c_string(a.name as *mut c_char);
|
||||
free_c_string(a.bundle_id as *mut c_char);
|
||||
a.name = ptr::null();
|
||||
a.bundle_id = ptr::null();
|
||||
unsafe {
|
||||
free_c_string(a.name as *mut c_char);
|
||||
free_c_string(a.bundle_id as *mut c_char);
|
||||
a.name = ptr::null();
|
||||
a.bundle_id = ptr::null();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -17,15 +17,17 @@ pub(crate) fn notification_info_to_c(info: &NotificationInfo) -> AdNotificationI
|
|||
}
|
||||
|
||||
pub(crate) unsafe fn free_notification_info_fields(info: &mut AdNotificationInfo) {
|
||||
free_c_string(info.app_name as *mut c_char);
|
||||
free_c_string(info.title as *mut c_char);
|
||||
free_c_string(info.body as *mut c_char);
|
||||
free_c_string_array(info.actions, info.action_count);
|
||||
info.app_name = ptr::null();
|
||||
info.title = ptr::null();
|
||||
info.body = ptr::null();
|
||||
info.actions = ptr::null_mut();
|
||||
info.action_count = 0;
|
||||
unsafe {
|
||||
free_c_string(info.app_name as *mut c_char);
|
||||
free_c_string(info.title as *mut c_char);
|
||||
free_c_string(info.body as *mut c_char);
|
||||
free_c_string_array(info.actions, info.action_count);
|
||||
info.app_name = ptr::null();
|
||||
info.title = ptr::null();
|
||||
info.body = ptr::null();
|
||||
info.actions = ptr::null_mut();
|
||||
info.action_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn strings_to_c_array(strings: &[String]) -> (*mut *mut c_char, u32) {
|
||||
|
|
@ -41,15 +43,17 @@ fn strings_to_c_array(strings: &[String]) -> (*mut *mut c_char, u32) {
|
|||
}
|
||||
|
||||
unsafe fn free_c_string_array(arr: *mut *mut c_char, count: u32) {
|
||||
if arr.is_null() {
|
||||
return;
|
||||
unsafe {
|
||||
if arr.is_null() {
|
||||
return;
|
||||
}
|
||||
let slice = std::slice::from_raw_parts_mut(arr, count as usize);
|
||||
for p in slice.iter_mut() {
|
||||
free_c_string(*p);
|
||||
}
|
||||
drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut(
|
||||
arr,
|
||||
count as usize,
|
||||
)));
|
||||
}
|
||||
let slice = std::slice::from_raw_parts_mut(arr, count as usize);
|
||||
for p in slice.iter_mut() {
|
||||
free_c_string(*p);
|
||||
}
|
||||
drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut(
|
||||
arr,
|
||||
count as usize,
|
||||
)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,16 +36,20 @@ pub(crate) fn opt_string_to_c(s: Option<&str>) -> *mut c_char {
|
|||
}
|
||||
|
||||
pub(crate) unsafe fn free_c_string(ptr: *mut c_char) {
|
||||
if !ptr.is_null() {
|
||||
drop(CString::from_raw(ptr));
|
||||
unsafe {
|
||||
if !ptr.is_null() {
|
||||
drop(CString::from_raw(ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn c_to_string(ptr: *const c_char) -> Option<String> {
|
||||
if ptr.is_null() {
|
||||
return None;
|
||||
unsafe {
|
||||
if ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
CStr::from_ptr(ptr).to_str().ok().map(str::to_owned)
|
||||
}
|
||||
CStr::from_ptr(ptr).to_str().ok().map(str::to_owned)
|
||||
}
|
||||
|
||||
/// Tri-state decode of a foreign C string used for optional filter
|
||||
|
|
@ -62,12 +66,14 @@ pub(crate) unsafe fn c_to_string(ptr: *const c_char) -> Option<String> {
|
|||
/// # Safety
|
||||
/// `ptr` must be null or a NUL-terminated C string.
|
||||
pub(crate) unsafe fn try_c_to_string(ptr: *const c_char) -> Result<Option<String>, ()> {
|
||||
if ptr.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
match CStr::from_ptr(ptr).to_str() {
|
||||
Ok(s) => Ok(Some(s.to_owned())),
|
||||
Err(_) => Err(()),
|
||||
unsafe {
|
||||
if ptr.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
match CStr::from_ptr(ptr).to_str() {
|
||||
Ok(s) => Ok(Some(s.to_owned())),
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,12 @@ pub(crate) fn surface_info_to_c(s: &SurfaceInfo) -> AdSurfaceInfo {
|
|||
}
|
||||
|
||||
pub(crate) unsafe fn free_surface_info_fields(s: &mut AdSurfaceInfo) {
|
||||
free_c_string(s.kind as *mut c_char);
|
||||
free_c_string(s.title as *mut c_char);
|
||||
s.kind = ptr::null();
|
||||
s.title = ptr::null();
|
||||
unsafe {
|
||||
free_c_string(s.kind as *mut c_char);
|
||||
free_c_string(s.title as *mut c_char);
|
||||
s.kind = ptr::null();
|
||||
s.title = ptr::null();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -30,12 +30,14 @@ pub(crate) fn window_info_to_c(w: &WindowInfo) -> AdWindowInfo {
|
|||
}
|
||||
|
||||
pub(crate) unsafe fn free_window_info_fields(w: &mut AdWindowInfo) {
|
||||
free_c_string(w.id as *mut c_char);
|
||||
free_c_string(w.title as *mut c_char);
|
||||
free_c_string(w.app_name as *mut c_char);
|
||||
w.id = ptr::null();
|
||||
w.title = ptr::null();
|
||||
w.app_name = ptr::null();
|
||||
unsafe {
|
||||
free_c_string(w.id as *mut c_char);
|
||||
free_c_string(w.title as *mut c_char);
|
||||
free_c_string(w.app_name as *mut c_char);
|
||||
w.id = ptr::null();
|
||||
w.title = ptr::null();
|
||||
w.app_name = ptr::null();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use agent_desktop_core::error::{AdapterError, ErrorCode};
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::{c_char, CStr, CString};
|
||||
use std::ffi::{CStr, CString, c_char};
|
||||
|
||||
const fn error_code_variant_count() -> usize {
|
||||
let mut count = 0;
|
||||
|
|
@ -215,7 +215,7 @@ pub(crate) fn last_error_code() -> AdResult {
|
|||
/// leaks to Thread B.
|
||||
/// Returns the `AdResult` code of the last error on the calling thread,
|
||||
/// or `AD_RESULT_OK` if no error has been recorded.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn ad_last_error_code() -> AdResult {
|
||||
crate::ffi_try::trap_panic(last_error_code)
|
||||
}
|
||||
|
|
@ -224,7 +224,7 @@ pub extern "C" fn ad_last_error_code() -> AdResult {
|
|||
/// error has been recorded on the calling thread. The pointer remains
|
||||
/// valid across any number of subsequent *successful* FFI calls; only
|
||||
/// the next failing call overwrites it.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn ad_last_error_message() -> *const c_char {
|
||||
crate::ffi_try::trap_panic_const_ptr(|| {
|
||||
LAST_ERROR.with(|cell| {
|
||||
|
|
@ -239,7 +239,7 @@ pub extern "C" fn ad_last_error_message() -> *const c_char {
|
|||
/// Returns a borrowed C string with a human-readable suggestion for how
|
||||
/// to recover from the last error, or null if the adapter didn't emit
|
||||
/// one. Same lifetime rules as `ad_last_error_message`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn ad_last_error_suggestion() -> *const c_char {
|
||||
crate::ffi_try::trap_panic_const_ptr(|| {
|
||||
LAST_ERROR.with(|cell| {
|
||||
|
|
@ -255,7 +255,7 @@ pub extern "C" fn ad_last_error_suggestion() -> *const c_char {
|
|||
/// for the last error (AX error codes, COM HRESULTs, AT-SPI messages,
|
||||
/// etc.), or null if the adapter didn't supply one. Same lifetime rules
|
||||
/// as `ad_last_error_message`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn ad_last_error_platform_detail() -> *const c_char {
|
||||
crate::ffi_try::trap_panic_const_ptr(|| {
|
||||
LAST_ERROR.with(|cell| {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::error::{set_last_error_static, AdResult};
|
||||
use crate::error::{AdResult, set_last_error_static};
|
||||
use std::ffi::CStr;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
|
||||
static PANIC_MESSAGE: &CStr = c"rust panic in FFI boundary";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::string::{c_to_string, free_c_string, string_to_c};
|
||||
use crate::error::{self, AdResult};
|
||||
use crate::ffi_try::{trap_panic, trap_panic_void};
|
||||
use crate::AdAdapter;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Reads the current clipboard text and writes an owned C string into
|
||||
|
|
@ -11,7 +11,7 @@ use std::os::raw::c_char;
|
|||
/// # Safety
|
||||
/// `adapter` must be a non-null pointer returned by `ad_adapter_create`.
|
||||
/// `out` must be a non-null writable `*mut *mut c_char`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_get_clipboard(
|
||||
adapter: *const AdAdapter,
|
||||
out: *mut *mut c_char,
|
||||
|
|
@ -28,12 +28,10 @@ pub unsafe extern "C" fn ad_get_clipboard(
|
|||
Ok(text) => {
|
||||
let c = string_to_c(&text);
|
||||
if c.is_null() {
|
||||
error::set_last_error(
|
||||
&agent_desktop_core::error::AdapterError::new(
|
||||
agent_desktop_core::error::ErrorCode::Internal,
|
||||
"clipboard text contains an interior NUL and cannot be represented as a C string",
|
||||
),
|
||||
);
|
||||
error::set_last_error(&agent_desktop_core::error::AdapterError::new(
|
||||
agent_desktop_core::error::ErrorCode::Internal,
|
||||
"clipboard text contains an interior NUL and cannot be represented as a C string",
|
||||
));
|
||||
return AdResult::ErrInternal;
|
||||
}
|
||||
*out = c;
|
||||
|
|
@ -53,7 +51,7 @@ pub unsafe extern "C" fn ad_get_clipboard(
|
|||
/// # Safety
|
||||
/// `adapter` must be a non-null pointer returned by `ad_adapter_create`.
|
||||
/// `text` must be a non-null, NUL-terminated UTF-8 C string.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_set_clipboard(
|
||||
adapter: *const AdAdapter,
|
||||
text: *const c_char,
|
||||
|
|
@ -88,7 +86,7 @@ pub unsafe extern "C" fn ad_set_clipboard(
|
|||
///
|
||||
/// # Safety
|
||||
/// `adapter` must be a non-null pointer returned by `ad_adapter_create`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_clear_clipboard(adapter: *const AdAdapter) -> AdResult {
|
||||
trap_panic(|| unsafe {
|
||||
if let Err(rc) = crate::main_thread::require_main_thread() {
|
||||
|
|
@ -113,7 +111,7 @@ pub unsafe extern "C" fn ad_clear_clipboard(adapter: *const AdAdapter) -> AdResu
|
|||
/// # Safety
|
||||
/// `s` must be null or a pointer previously handed out by this crate.
|
||||
/// After this call the pointer is invalid and must not be used.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_free_string(s: *mut c_char) {
|
||||
trap_panic_void(|| unsafe { free_c_string(s) })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::error::{self, AdResult};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::AdDragParams;
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::action::{DragParams as CoreDragParams, Point as CorePoint};
|
||||
|
||||
/// Synthesizes a mouse drag from `params.from` to `params.to`. When
|
||||
|
|
@ -11,7 +11,7 @@ use agent_desktop_core::action::{DragParams as CoreDragParams, Point as CorePoin
|
|||
/// # Safety
|
||||
/// `adapter` must be a non-null pointer returned by `ad_adapter_create`.
|
||||
/// `params` must be a non-null pointer to a valid `AdDragParams`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_drag(
|
||||
adapter: *const AdAdapter,
|
||||
params: *const AdDragParams,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::error::{self, AdResult};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::{AdMouseButton, AdMouseEvent, AdMouseEventKind};
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::action::{
|
||||
MouseButton as CoreMouseButton, MouseEvent as CoreMouseEvent,
|
||||
MouseEventKind as CoreMouseEventKind, Point as CorePoint,
|
||||
|
|
@ -22,7 +22,7 @@ pub(crate) fn mouse_button_from_c(b: AdMouseButton) -> CoreMouseButton {
|
|||
/// # Safety
|
||||
/// `adapter` must be a non-null pointer returned by `ad_adapter_create`.
|
||||
/// `event` must be a non-null pointer to a valid `AdMouseEvent`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_mouse_event(
|
||||
adapter: *const AdAdapter,
|
||||
event: *const AdMouseEvent,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
//! / `ad_free_string` / `ad_free_action_result` / `ad_free_tree` family.
|
||||
//! Those paths touch no AX/Cocoa state and are safe from any thread.
|
||||
|
||||
use crate::error::{set_last_error_static, AdResult};
|
||||
use crate::error::{AdResult, set_last_error_static};
|
||||
use std::ffi::CStr;
|
||||
|
||||
static OFF_MAIN_THREAD_MESSAGE: &CStr =
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::actions::result::action_result_to_c;
|
||||
use crate::convert::string::{c_to_string, decode_optional_filter};
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::AdActionResult;
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::notification::NotificationIdentity;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ use std::os::raw::c_char;
|
|||
/// is rejected with `AD_RESULT_ERR_INVALID_ARGS` rather than silently
|
||||
/// treated as "no fingerprint". `out` must be a valid writable
|
||||
/// `*mut AdActionResult`; on error it is zero-initialized.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_notification_action(
|
||||
adapter: *const AdAdapter,
|
||||
index: u32,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::convert::string::decode_optional_filter;
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::AdAdapter;
|
||||
use crate::convert::string::decode_optional_filter;
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Dismisses the notification at `index`. Indexes are only valid within
|
||||
|
|
@ -11,7 +11,7 @@ use std::os::raw::c_char;
|
|||
///
|
||||
/// # Safety
|
||||
/// `adapter` must be valid. `app_filter` may be null.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_dismiss_notification(
|
||||
adapter: *const AdAdapter,
|
||||
index: u32,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::notification::notification_info_to_c;
|
||||
use crate::convert::string::decode_optional_filter;
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::notifications::list::ad_notification_list_free;
|
||||
use crate::types::{AdNotificationInfo, AdNotificationList};
|
||||
use crate::AdAdapter;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr;
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ use std::ptr;
|
|||
/// # Safety
|
||||
/// `adapter` must be valid. `app_filter` may be null. `dismissed_out`
|
||||
/// and `failed_out` must both be valid writable `*mut *mut AdNotificationList`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_dismiss_all_notifications(
|
||||
adapter: *const AdAdapter,
|
||||
app_filter: *const c_char,
|
||||
|
|
@ -86,7 +86,7 @@ pub unsafe extern "C" fn ad_dismiss_all_notifications(
|
|||
/// # Safety
|
||||
/// Both arguments must be null or pointers from
|
||||
/// `ad_dismiss_all_notifications`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_dismiss_all_notifications_free(
|
||||
dismissed: *mut AdNotificationList,
|
||||
failed: *mut AdNotificationList,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::notification::{free_notification_info_fields, notification_info_to_c};
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::{trap_panic, trap_panic_void};
|
||||
use crate::notifications::filter::filter_from_c;
|
||||
use crate::types::{AdNotificationFilter, AdNotificationInfo, AdNotificationList};
|
||||
use crate::AdAdapter;
|
||||
use std::ptr;
|
||||
|
||||
/// Lists the notifications currently on-screen.
|
||||
|
|
@ -17,7 +17,7 @@ use std::ptr;
|
|||
/// `adapter` must be valid. `filter` may be null. `out` must be a valid
|
||||
/// writable `*mut *mut AdNotificationList`. On success `*out` is a
|
||||
/// non-null handle freed with `ad_notification_list_free`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_list_notifications(
|
||||
adapter: *const AdAdapter,
|
||||
filter: *const AdNotificationFilter,
|
||||
|
|
@ -58,7 +58,7 @@ pub unsafe extern "C" fn ad_list_notifications(
|
|||
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_notifications`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_notification_list_count(list: *const AdNotificationList) -> u32 {
|
||||
if list.is_null() {
|
||||
return 0;
|
||||
|
|
@ -71,7 +71,7 @@ pub unsafe extern "C" fn ad_notification_list_count(list: *const AdNotificationL
|
|||
///
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_notifications`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_notification_list_get(
|
||||
list: *const AdNotificationList,
|
||||
index: u32,
|
||||
|
|
@ -90,7 +90,7 @@ pub unsafe extern "C" fn ad_notification_list_get(
|
|||
///
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_notifications`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_notification_list_free(list: *mut AdNotificationList) {
|
||||
trap_panic_void(|| unsafe {
|
||||
if list.is_null() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::string::decode_optional_filter;
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::observation::walk::find_first_match;
|
||||
use crate::types::{AdFindQuery, AdNativeHandle, AdWindowInfo};
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::adapter::{SnapshotSurface, TreeOptions};
|
||||
use agent_desktop_core::refs::RefEntry;
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ use agent_desktop_core::refs::RefEntry;
|
|||
/// `adapter`, `win`, and `query` must be valid pointers. `out_handle`
|
||||
/// must be a valid writable `*mut AdNativeHandle`. On
|
||||
/// `AD_RESULT_ERR_ELEMENT_NOT_FOUND` the out-handle is zero-initialized.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_find(
|
||||
adapter: *const AdAdapter,
|
||||
win: *const AdWindowInfo,
|
||||
|
|
@ -102,7 +102,7 @@ pub unsafe extern "C" fn ad_find(
|
|||
source_app: None,
|
||||
source_window_title: Some(core_win.title.clone()),
|
||||
root_ref: None,
|
||||
path: Vec::new(),
|
||||
path: smallvec::SmallVec::new(),
|
||||
};
|
||||
match adapter.inner.resolve_element(&ref_entry) {
|
||||
Ok(handle) => {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::string::{c_to_string, string_to_c_lossy};
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::AdNativeHandle;
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::adapter::NativeHandle;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ use std::os::raw::c_char;
|
|||
/// `adapter` must be valid. `handle` must be a non-null `AdNativeHandle`.
|
||||
/// `property` must be a non-null UTF-8 C string. `out` must be a valid
|
||||
/// writable `*mut *mut c_char`; it is null-initialized on entry.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_get(
|
||||
adapter: *const AdAdapter,
|
||||
handle: *const AdNativeHandle,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::string::{c_to_string, decode_optional_filter};
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::{AdFindQuery, AdWindowInfo};
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::adapter::{SnapshotSurface, TreeOptions};
|
||||
use agent_desktop_core::node::AccessibilityNode;
|
||||
use std::os::raw::c_char;
|
||||
|
|
@ -36,7 +36,7 @@ use std::os::raw::c_char;
|
|||
/// # Safety
|
||||
/// All pointers must be valid. `property` must be a non-null UTF-8
|
||||
/// C string. `out` must be a valid writable `*mut bool`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_is(
|
||||
adapter: *const AdAdapter,
|
||||
win: *const AdWindowInfo,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use std::ptr;
|
|||
///
|
||||
/// # Safety
|
||||
/// `buf` must be null or returned by `ad_screenshot`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_image_buffer_data(buf: *const AdImageBuffer) -> *const u8 {
|
||||
if buf.is_null() {
|
||||
return ptr::null();
|
||||
|
|
@ -20,7 +20,7 @@ pub unsafe extern "C" fn ad_image_buffer_data(buf: *const AdImageBuffer) -> *con
|
|||
///
|
||||
/// # Safety
|
||||
/// `buf` must be null or returned by `ad_screenshot`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_image_buffer_size(buf: *const AdImageBuffer) -> u64 {
|
||||
if buf.is_null() {
|
||||
return 0;
|
||||
|
|
@ -33,7 +33,7 @@ pub unsafe extern "C" fn ad_image_buffer_size(buf: *const AdImageBuffer) -> u64
|
|||
///
|
||||
/// # Safety
|
||||
/// `buf` must be null or returned by `ad_screenshot`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_image_buffer_width(buf: *const AdImageBuffer) -> u32 {
|
||||
if buf.is_null() {
|
||||
return 0;
|
||||
|
|
@ -46,7 +46,7 @@ pub unsafe extern "C" fn ad_image_buffer_width(buf: *const AdImageBuffer) -> u32
|
|||
///
|
||||
/// # Safety
|
||||
/// `buf` must be null or returned by `ad_screenshot`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_image_buffer_height(buf: *const AdImageBuffer) -> u32 {
|
||||
if buf.is_null() {
|
||||
return 0;
|
||||
|
|
@ -60,7 +60,7 @@ pub unsafe extern "C" fn ad_image_buffer_height(buf: *const AdImageBuffer) -> u3
|
|||
///
|
||||
/// # Safety
|
||||
/// `buf` must be null or returned by `ad_screenshot`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_image_buffer_format(buf: *const AdImageBuffer) -> AdImageFormat {
|
||||
if buf.is_null() {
|
||||
return AdImageFormat::Png;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::AdAdapter;
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::{AdImageBuffer, AdImageFormat, AdScreenshotKind, AdScreenshotTarget};
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::adapter::{ImageFormat, ScreenshotTarget as CoreScreenshotTarget};
|
||||
use std::ptr;
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ use std::ptr;
|
|||
/// `adapter` and `target` must be valid pointers. `out` must be a valid
|
||||
/// writable `*mut *mut AdImageBuffer`. On error `*out` is null and
|
||||
/// last-error is set.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_screenshot(
|
||||
adapter: *const AdAdapter,
|
||||
target: *const AdScreenshotTarget,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::types::AdImageBuffer;
|
|||
/// # Safety
|
||||
/// `buf` must be null or a pointer previously returned by `ad_screenshot`.
|
||||
/// Double-free is undefined behavior.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_image_buffer_free(buf: *mut AdImageBuffer) {
|
||||
trap_panic_void(|| unsafe {
|
||||
if buf.is_null() {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::surface::{free_surface_info_fields, surface_info_to_c};
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::{trap_panic, trap_panic_void};
|
||||
use crate::types::{AdSurfaceInfo, AdSurfaceList};
|
||||
use crate::AdAdapter;
|
||||
use std::ptr;
|
||||
|
||||
/// # Safety
|
||||
/// `adapter` must be valid. `out` must be a valid writable
|
||||
/// `*mut *mut AdSurfaceList`. Success produces a list handle freed via
|
||||
/// `ad_surface_list_free`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_list_surfaces(
|
||||
adapter: *const AdAdapter,
|
||||
pid: i32,
|
||||
|
|
@ -42,7 +42,7 @@ pub unsafe extern "C" fn ad_list_surfaces(
|
|||
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_surfaces`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_surface_list_count(list: *const AdSurfaceList) -> u32 {
|
||||
if list.is_null() {
|
||||
return 0;
|
||||
|
|
@ -55,7 +55,7 @@ pub unsafe extern "C" fn ad_surface_list_count(list: *const AdSurfaceList) -> u3
|
|||
///
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_surfaces`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_surface_list_get(
|
||||
list: *const AdSurfaceList,
|
||||
index: u32,
|
||||
|
|
@ -74,7 +74,7 @@ pub unsafe extern "C" fn ad_surface_list_get(
|
|||
///
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_surfaces`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_surface_list_free(list: *mut AdSurfaceList) {
|
||||
trap_panic_void(|| unsafe {
|
||||
if list.is_null() {
|
||||
|
|
|
|||
|
|
@ -4,41 +4,45 @@ use std::os::raw::c_char;
|
|||
use std::ptr;
|
||||
|
||||
unsafe fn free_c_string_array(arr: *mut *mut c_char, count: u32) {
|
||||
if arr.is_null() {
|
||||
return;
|
||||
unsafe {
|
||||
if arr.is_null() {
|
||||
return;
|
||||
}
|
||||
let slice = std::slice::from_raw_parts_mut(arr, count as usize);
|
||||
for p in slice.iter_mut() {
|
||||
free_c_string(*p);
|
||||
}
|
||||
drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut(
|
||||
arr,
|
||||
count as usize,
|
||||
)));
|
||||
}
|
||||
let slice = std::slice::from_raw_parts_mut(arr, count as usize);
|
||||
for p in slice.iter_mut() {
|
||||
free_c_string(*p);
|
||||
}
|
||||
drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut(
|
||||
arr,
|
||||
count as usize,
|
||||
)));
|
||||
}
|
||||
|
||||
unsafe fn free_node_fields(node: &mut AdNode) {
|
||||
free_c_string(node.ref_id as *mut c_char);
|
||||
free_c_string(node.role as *mut c_char);
|
||||
free_c_string(node.name as *mut c_char);
|
||||
free_c_string(node.value as *mut c_char);
|
||||
free_c_string(node.description as *mut c_char);
|
||||
free_c_string(node.hint as *mut c_char);
|
||||
free_c_string_array(node.states, node.state_count);
|
||||
node.ref_id = ptr::null();
|
||||
node.role = ptr::null();
|
||||
node.name = ptr::null();
|
||||
node.value = ptr::null();
|
||||
node.description = ptr::null();
|
||||
node.hint = ptr::null();
|
||||
node.states = ptr::null_mut();
|
||||
node.state_count = 0;
|
||||
unsafe {
|
||||
free_c_string(node.ref_id as *mut c_char);
|
||||
free_c_string(node.role as *mut c_char);
|
||||
free_c_string(node.name as *mut c_char);
|
||||
free_c_string(node.value as *mut c_char);
|
||||
free_c_string(node.description as *mut c_char);
|
||||
free_c_string(node.hint as *mut c_char);
|
||||
free_c_string_array(node.states, node.state_count);
|
||||
node.ref_id = ptr::null();
|
||||
node.role = ptr::null();
|
||||
node.name = ptr::null();
|
||||
node.value = ptr::null();
|
||||
node.description = ptr::null();
|
||||
node.hint = ptr::null();
|
||||
node.states = ptr::null_mut();
|
||||
node.state_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// `tree` must be null or point to a valid `AdNodeTree` previously returned
|
||||
/// by `flatten_tree` or `ad_get_tree`. After this call the tree is zeroed.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_free_tree(tree: *mut AdNodeTree) {
|
||||
crate::ffi_try::trap_panic_void(|| unsafe {
|
||||
if tree.is_null() {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::AdAdapter;
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::tree::flatten::flatten_tree;
|
||||
use crate::types::{AdNodeTree, AdSnapshotSurface, AdTreeOptions, AdWindowInfo};
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::adapter::SnapshotSurface;
|
||||
use std::ptr;
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ fn core_surface(s: AdSnapshotSurface) -> SnapshotSurface {
|
|||
/// # Safety
|
||||
/// All pointers must be non-null. `win.id` and `win.title` must be
|
||||
/// valid UTF-8 C strings. `out` must be writable.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_get_tree(
|
||||
adapter: *const AdAdapter,
|
||||
win: *const AdWindowInfo,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::AdAdapter;
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::AdWindowInfo;
|
||||
use crate::windows::to_core::ad_window_to_core;
|
||||
use crate::AdAdapter;
|
||||
|
||||
/// Brings `win` to the foreground on the current space. Returns
|
||||
/// `AD_RESULT_ERR_WINDOW_NOT_FOUND` when the referenced window no longer
|
||||
|
|
@ -12,7 +12,7 @@ use crate::AdAdapter;
|
|||
/// `adapter` must be a non-null pointer from `ad_adapter_create`. `win`
|
||||
/// must be a non-null pointer to an `AdWindowInfo` whose `id` and
|
||||
/// `title` fields are non-null, valid UTF-8 C strings.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_focus_window(
|
||||
adapter: *const AdAdapter,
|
||||
win: *const AdWindowInfo,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use crate::types::AdWindowInfo;
|
|||
/// `win` must be null or point to a valid `AdWindowInfo` whose string
|
||||
/// fields were allocated by this crate. Do not call on pointers inside
|
||||
/// an `AdWindowList` — free the list instead.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_release_window_fields(win: *mut AdWindowInfo) {
|
||||
trap_panic_void(|| unsafe {
|
||||
if win.is_null() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::AdAdapter;
|
||||
use crate::convert::string::decode_optional_filter;
|
||||
use crate::convert::window::{free_window_info_fields, window_info_to_c};
|
||||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::{trap_panic, trap_panic_void};
|
||||
use crate::types::{AdWindowInfo, AdWindowList};
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::adapter::WindowFilter;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr;
|
||||
|
|
@ -12,7 +12,7 @@ use std::ptr;
|
|||
/// `adapter` must be valid. `out` must be a valid writable
|
||||
/// `*mut *mut AdWindowList`. `app_filter` may be null or a C string.
|
||||
/// Success produces a list handle freed via `ad_window_list_free`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_list_windows(
|
||||
adapter: *const AdAdapter,
|
||||
app_filter: *const c_char,
|
||||
|
|
@ -50,7 +50,7 @@ pub unsafe extern "C" fn ad_list_windows(
|
|||
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_windows`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_window_list_count(list: *const AdWindowList) -> u32 {
|
||||
if list.is_null() {
|
||||
return 0;
|
||||
|
|
@ -63,7 +63,7 @@ pub unsafe extern "C" fn ad_window_list_count(list: *const AdWindowList) -> u32
|
|||
///
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_windows`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_window_list_get(
|
||||
list: *const AdWindowList,
|
||||
index: u32,
|
||||
|
|
@ -82,7 +82,7 @@ pub unsafe extern "C" fn ad_window_list_get(
|
|||
///
|
||||
/// # Safety
|
||||
/// `list` must be null or a pointer returned by `ad_list_windows`.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_window_list_free(list: *mut AdWindowList) {
|
||||
trap_panic_void(|| unsafe {
|
||||
if list.is_null() {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::error::{set_last_error, AdResult};
|
||||
use crate::AdAdapter;
|
||||
use crate::error::{AdResult, set_last_error};
|
||||
use crate::ffi_try::trap_panic;
|
||||
use crate::types::{AdWindowInfo, AdWindowOp, AdWindowOpKind};
|
||||
use crate::windows::to_core::ad_window_to_core;
|
||||
use crate::AdAdapter;
|
||||
use agent_desktop_core::action::WindowOp;
|
||||
|
||||
/// Performs a window-manager operation (`Resize`, `Move`, `Minimize`,
|
||||
|
|
@ -15,7 +15,7 @@ use agent_desktop_core::action::WindowOp;
|
|||
/// # Safety
|
||||
/// `adapter` and `win` must be non-null pointers. `win.id` and
|
||||
/// `win.title` must be non-null valid UTF-8 C strings.
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ad_window_op(
|
||||
adapter: *const AdAdapter,
|
||||
win: *const AdWindowInfo,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
mod common;
|
||||
|
||||
use common::{
|
||||
ad_execute_action, ad_execute_action_with_policy, default_action, with_adapter, AdActionResult,
|
||||
AdNativeHandle, AdPolicyKind, AdResult,
|
||||
AdActionResult, AdNativeHandle, AdPolicyKind, AdResult, ad_execute_action,
|
||||
ad_execute_action_with_policy, default_action, with_adapter,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
mod common;
|
||||
|
||||
use common::{
|
||||
ad_launch_app, ad_list_windows, ad_resolve_element, c_char, with_adapter, AdNativeHandle,
|
||||
AdRefEntry, AdResult, AdWindowInfo, AdWindowList,
|
||||
AdNativeHandle, AdRefEntry, AdResult, AdWindowInfo, AdWindowList, ad_launch_app,
|
||||
ad_list_windows, ad_resolve_element, c_char, with_adapter,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
mod common;
|
||||
|
||||
use common::{
|
||||
AdAppList, AdFindQuery, AdNativeHandle, AdResult, AdWindowInfo, AdWindowList, CStr,
|
||||
ad_adapter_create, ad_adapter_destroy, ad_app_list_count, ad_app_list_free, ad_app_list_get,
|
||||
ad_check_permissions, ad_find, ad_free_handle, ad_last_error_code, ad_last_error_message,
|
||||
ad_list_apps, ad_list_windows, ad_window_list_count, ad_window_list_free, with_adapter,
|
||||
AdAppList, AdFindQuery, AdNativeHandle, AdResult, AdWindowInfo, AdWindowList, CStr,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub use agent_desktop_ffi::{
|
|||
pub use std::ffi::CStr;
|
||||
pub use std::os::raw::c_char;
|
||||
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
pub fn ad_adapter_create() -> *mut AdAdapter;
|
||||
pub fn ad_adapter_destroy(adapter: *mut AdAdapter);
|
||||
pub fn ad_check_permissions(adapter: *const AdAdapter) -> AdResult;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue