atspi-connection-0.13.0/.cargo_vcs_info.json0000644000000001560000000000100143740ustar { "git": { "sha1": "f486755b9bf993613f63a779133aafc54925004a" }, "path_in_vcs": "atspi-connection" }atspi-connection-0.13.0/Cargo.lock0000644000000766770000000000100123740ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "async-broadcast" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-channel" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", "pin-project-lite", "slab", ] [[package]] name = "async-io" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ "autocfg", "cfg-if", "concurrent-queue", "futures-io", "futures-lite", "parking", "polling", "rustix", "slab", "windows-sys 0.61.0", ] [[package]] name = "async-lock" version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ "event-listener", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-process" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" dependencies = [ "async-channel", "async-io", "async-lock", "async-signal", "async-task", "blocking", "cfg-if", "event-listener", "futures-lite", "rustix", ] [[package]] name = "async-recursion" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "async-signal" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" dependencies = [ "async-io", "async-lock", "atomic-waker", "cfg-if", "futures-core", "futures-io", "rustix", "signal-hook-registry", "slab", "windows-sys 0.61.0", ] [[package]] name = "async-stream" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", "pin-project-lite", ] [[package]] name = "async-stream-impl" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "async-task" version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atspi-common" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d" dependencies = [ "enumflags2", "serde", "static_assertions", "zbus", "zbus-lockstep", "zbus-lockstep-macros", "zbus_names", "zvariant", ] [[package]] name = "atspi-connection" version = "0.13.0" dependencies = [ "atspi-common", "atspi-proxies", "enumflags2", "futures-lite", "tokio-test", "tracing", "zbus", ] [[package]] name = "atspi-proxies" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc" dependencies = [ "atspi-common", "serde", "zbus", ] [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets 0.52.6", ] [[package]] name = "bitflags" version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "blocking" version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ "async-channel", "async-task", "futures-io", "futures-lite", "piper", ] [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cfg-if" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "concurrent-queue" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "endi" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", ] [[package]] name = "enumflags2_derive" version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", "windows-sys 0.61.0", ] [[package]] name = "event-listener" version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] [[package]] name = "event-listener-strategy" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ "event-listener", "pin-project-lite", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", "futures-io", "parking", "pin-project-lite", ] [[package]] name = "getrandom" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", "r-efi", "wasi 0.14.7+wasi-0.2.4", ] [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "hermit-abi" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "indexmap" version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92119844f513ffa41556430369ab02c295a3578af21cf945caa3e9e0c2481ac3" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "io-uring" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ "bitflags", "cfg-if", "libc", ] [[package]] name = "libc" version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "linux-raw-sys" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "miniz_oxide" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] [[package]] name = "nix" version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags", "cfg-if", "cfg_aliases", "libc", "memoffset", ] [[package]] name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "ordered-stream" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" dependencies = [ "futures-core", "pin-project-lite", ] [[package]] name = "parking" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "piper" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", "fastrand", "futures-io", ] [[package]] name = "polling" version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", "rustix", "windows-sys 0.61.0", ] [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro-crate" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quick-xml" version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ "memchr", "serde", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom", ] [[package]] name = "rustc-demangle" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustix" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.61.0", ] [[package]] name = "serde" version = "1.0.225" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_core" version = "1.0.225" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.225" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_repr" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "signal-hook-registry" version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", "windows-sys 0.61.0", ] [[package]] name = "tokio" version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "io-uring", "libc", "mio", "pin-project-lite", "slab", ] [[package]] name = "tokio-stream" version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", "tokio", ] [[package]] name = "tokio-test" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" dependencies = [ "async-stream", "bytes", "futures-core", "tokio", "tokio-stream", ] [[package]] name = "toml_datetime" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a197c0ec7d131bfc6f7e82c8442ba1595aeab35da7adbf05b6b73cd06a16b6be" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2ad0b7ae9cfeef5605163839cb9221f453399f15cfb5c10be9885fcf56611f9" dependencies = [ "indexmap", "toml_datetime", "toml_parser", "winnow", ] [[package]] name = "toml_parser" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" dependencies = [ "winnow", ] [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-core" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] [[package]] name = "uds_windows" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ "memoffset", "tempfile", "winapi", ] [[package]] name = "unicode-ident" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" version = "0.14.7+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" dependencies = [ "wasip2", ] [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ "wit-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-link" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-link" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ "windows-targets 0.53.3", ] [[package]] name = "windows-sys" version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" dependencies = [ "windows-link 0.2.0", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows-targets" version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", "windows_i686_gnullvm 0.53.0", "windows_i686_msvc 0.53.0", "windows_x86_64_gnu 0.53.0", "windows_x86_64_gnullvm 0.53.0", "windows_x86_64_msvc 0.53.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "zbus" version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7" dependencies = [ "async-broadcast", "async-executor", "async-io", "async-lock", "async-process", "async-recursion", "async-task", "async-trait", "blocking", "enumflags2", "event-listener", "futures-core", "futures-lite", "hex", "nix", "ordered-stream", "rand", "serde", "serde_repr", "tracing", "uds_windows", "windows-sys 0.60.2", "winnow", "zbus_macros", "zbus_names", "zvariant", ] [[package]] name = "zbus-lockstep" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29e96e38ded30eeab90b6ba88cb888d70aef4e7489b6cd212c5e5b5ec38045b6" dependencies = [ "zbus_xml", "zvariant", ] [[package]] name = "zbus-lockstep-macros" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc6821851fa840b708b4cbbaf6241868cabc85a2dc22f426361b0292bfc0b836" dependencies = [ "proc-macro2", "quote", "syn", "zbus-lockstep", "zbus_xml", "zvariant", ] [[package]] name = "zbus_macros" version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn", "zbus_names", "zvariant", "zvariant_utils", ] [[package]] name = "zbus_names" version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", "winnow", "zvariant", ] [[package]] name = "zbus_xml" version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589e9a02bfafb9754bb2340a9e3b38f389772684c63d9637e76b1870377bec29" dependencies = [ "quick-xml", "serde", "static_assertions", "zbus_names", "zvariant", ] [[package]] name = "zerocopy" version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zvariant" version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" dependencies = [ "endi", "enumflags2", "serde", "winnow", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn", "zvariant_utils", ] [[package]] name = "zvariant_utils" version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" dependencies = [ "proc-macro2", "quote", "serde", "syn", "winnow", ] atspi-connection-0.13.0/Cargo.toml0000644000000033350000000000100123740ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.77.2" name = "atspi-connection" version = "0.13.0" build = false include = [ "LICENSE-*", "README.md", "src/**/*", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A method of connecting, querying, sending and receiving over AT-SPI." readme = "README.md" keywords = [ "a11y", "accessibility", "linux", "screen-reader", ] categories = ["accessibility"] license = "Apache-2.0 OR MIT" repository = "https://github.com/odilia-app/atspi/" [features] default = [ "p2p", "wrappers", ] p2p = [ "dep:futures-lite", "zbus/p2p", ] tracing = ["dep:tracing"] wrappers = [ "atspi-common/wrappers", "dep:futures-lite", ] [lib] name = "atspi_connection" path = "src/lib.rs" [dependencies.atspi-common] version = "0.13.0" features = ["zbus"] default-features = false [dependencies.atspi-proxies] version = "0.13.0" [dependencies.futures-lite] version = "2.6.0" optional = true default-features = false [dependencies.tracing] version = "0.1.40" optional = true [dependencies.zbus] version = "5.5" features = ["async-io"] default-features = false [dev-dependencies.enumflags2] version = "0.7.9" [dev-dependencies.tokio-test] version = "0.4.2" atspi-connection-0.13.0/Cargo.toml.orig000064400000000000000000000024011046102023000160460ustar 00000000000000[package] categories = ["accessibility"] description = "A method of connecting, querying, sending and receiving over AT-SPI." edition = "2021" include = ["LICENSE-*", "README.md", "src/**/*"] keywords = ["a11y", "accessibility", "linux", "screen-reader"] license = "Apache-2.0 OR MIT" name = "atspi-connection" readme = "README.md" repository = "https://github.com/odilia-app/atspi/" rust-version.workspace = true version = "0.13.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["p2p", "wrappers"] p2p = ["dep:futures-lite", "zbus/p2p"] tracing = ["dep:tracing"] wrappers = ["atspi-common/wrappers", "dep:futures-lite"] [dependencies] atspi-common = { path = "../atspi-common/", version = "0.13.0", default-features = false, features = ["zbus"] } atspi-proxies = { path = "../atspi-proxies/", version = "0.13.0" } futures-lite = { version = "2.6.0", default-features = false, optional = true } tracing = { optional = true, workspace = true } zbus.workspace = true [dev-dependencies] enumflags2.workspace = true tokio-test = "0.4.2" atspi-connection-0.13.0/LICENSE-APACHE2.txt000064400000000000000000000260741046102023000160170ustar 00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. atspi-connection-0.13.0/LICENSE-MIT.txt000064400000000000000000000020571046102023000154400ustar 00000000000000Copyright (c) 2022 Tait Hoyem Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. atspi-connection-0.13.0/README.md000064400000000000000000000021221046102023000144360ustar 00000000000000# `atspi-connection` This crate uses the the following crates to support its various functions: * `atspi-common`: to receive events from AT-SPI. * `atspi-proxies`: to send events and query live information over AT-SPI. This is essentially a client-facing library where you can use the `AccessibilityConnection` structure to get streams of AT-SPI events coming from a Linux system. See the examples folder and documentation on how to use this library. ## Feature Flags * `default`: `wrappers`, `p2p` * `p2p`: dependencies `async-executor" and enables`zbus/p2p` * `tracing`: enable support for the `tracing` crate * `wrappers`: enable support for `atspi-common` wrapper types that categorize events by interface, as well as the all-encompassing `Event` enum that can store any event type. * This also enables the `event_stream` function that allows you to receive a stream of `Event`s instead of specific events. ## P2P Peer-to-Peer (P2P) support in atspi enables direct connections to applications, bypassing the central accessibility bus when possible. See: [introduction to `atspi` p2p](p2p.md) atspi-connection-0.13.0/src/lib.rs000064400000000000000000000375431046102023000151010ustar 00000000000000//! A connection to AT-SPI. //! This connection may receive any [`atspi_common::events::Event`] structures. #![deny(clippy::all, clippy::pedantic, clippy::cargo, unsafe_code, rustdoc::all, missing_docs)] #![allow(clippy::multiple_crate_versions)] pub use atspi_common as common; use common::{ error::AtspiError, events::{DBusInterface, DBusMatchRule, DBusMember, MessageConversion, RegistryEventString}, EventProperties, Result as AtspiResult, }; #[cfg(feature = "wrappers")] use atspi_common::events::Event; #[cfg(feature = "p2p")] mod p2p; #[cfg(feature = "p2p")] pub use p2p::{Peer, P2P}; use atspi_proxies::{ accessible::AccessibleProxy, bus::{BusProxy, StatusProxy}, registry::RegistryProxy, }; #[cfg(feature = "wrappers")] use futures_lite::stream::{Stream, StreamExt}; use std::ops::Deref; use zbus::{ fdo::DBusProxy, proxy::{CacheProperties, Defaults}, Address, MatchRule, }; #[cfg(feature = "wrappers")] use zbus::{message::Type as MessageType, MessageStream}; #[cfg(feature = "p2p")] use crate::p2p::Peers; /// A connection to the at-spi bus #[derive(Clone, Debug)] pub struct AccessibilityConnection { registry: RegistryProxy<'static>, dbus_proxy: DBusProxy<'static>, #[cfg(feature = "p2p")] peers: Peers, } impl AccessibilityConnection { /// Open a new connection to the bus /// # Errors /// May error when a bus is not available, /// or when the accessibility bus (AT-SPI) can not be found. #[cfg_attr(feature = "tracing", tracing::instrument)] pub async fn new() -> AtspiResult { // Grab the a11y bus address from the session bus let a11y_bus_addr = { #[cfg(feature = "tracing")] tracing::debug!("Connecting to session bus"); let session_bus = Box::pin(zbus::Connection::session()).await?; #[cfg(feature = "tracing")] tracing::debug!( name = session_bus.unique_name().map(|n| n.as_str()), "Connected to session bus" ); let proxy = BusProxy::new(&session_bus).await?; #[cfg(feature = "tracing")] tracing::debug!("Getting a11y bus address from session bus"); proxy.get_address().await? }; #[cfg(feature = "tracing")] tracing::debug!(address = %a11y_bus_addr, "Got a11y bus address"); let addr: Address = a11y_bus_addr.parse()?; let accessibility_conn = Self::from_address(addr).await?; #[cfg(feature = "p2p")] accessibility_conn .peers .spawn_peer_listener_task(accessibility_conn.connection()); Ok(accessibility_conn) } /// Returns an [`AccessibilityConnection`], a wrapper for the [`RegistryProxy`]; a handle for the registry provider /// on the accessibility bus. /// /// You may want to call this if you have the accessibility bus address and want a connection with /// a convenient async event stream provisioning. /// /// Without address, you will want to call `open`, which tries to obtain the accessibility bus' address /// on your behalf. /// /// # Errors /// /// `RegistryProxy` is configured with invalid path, interface or destination pub async fn from_address(bus_addr: Address) -> AtspiResult { #[cfg(feature = "tracing")] tracing::info!("Connecting to a11y bus"); let bus = Box::pin(zbus::connection::Builder::address(bus_addr)?.build()).await?; #[cfg(feature = "tracing")] tracing::info!(name = bus.unique_name().map(|n| n.as_str()), "Connected to a11y bus"); // The Proxy holds a strong reference to a Connection, so we only need to store the proxy let registry = RegistryProxy::new(&bus).await?; let dbus_proxy = DBusProxy::new(&bus).await?; #[cfg(not(feature = "p2p"))] return Ok(Self { registry, dbus_proxy }); #[cfg(feature = "p2p")] let peers = Peers::initialize_peers(&bus).await?; #[cfg(feature = "p2p")] return Ok(Self { registry, dbus_proxy, peers }); } /// Stream yielding all `Event` types. /// /// Monitor this stream to be notified and receive events on the a11y bus. /// /// # Example /// Basic use: /// /// ```rust /// use atspi_connection::AccessibilityConnection; /// use enumflags2::BitFlag; /// use atspi_connection::common::events::{ObjectEvents, object::StateChangedEvent}; /// use zbus::{fdo::DBusProxy, MatchRule, message::Type as MessageType}; /// use atspi_connection::common::events::Event; /// # use futures_lite::StreamExt; /// # use std::error::Error; /// /// # fn main() { /// # assert!(tokio_test::block_on(example()).is_ok()); /// # } /// /// # async fn example() -> Result<(), Box> { /// let atspi = AccessibilityConnection::new().await?; /// atspi.register_event::().await?; /// /// let mut events = atspi.event_stream(); /// std::pin::pin!(&mut events); /// # let output = std::process::Command::new("busctl") /// # .arg("--user") /// # .arg("call") /// # .arg("org.a11y.Bus") /// # .arg("/org/a11y/bus") /// # .arg("org.a11y.Bus") /// # .arg("GetAddress") /// # .output() /// # .unwrap(); /// # let addr_string = String::from_utf8(output.stdout).unwrap(); /// # let addr_str = addr_string /// # .strip_prefix("s \"") /// # .unwrap() /// # .trim() /// # .strip_suffix('"') /// # .unwrap(); /// # let mut base_cmd = std::process::Command::new("busctl"); /// # let thing = base_cmd /// # .arg("--address") /// # .arg(addr_str) /// # .arg("emit") /// # .arg("/org/a11y/atspi/null") /// # .arg("org.a11y.atspi.Event.Object") /// # .arg("StateChanged") /// # .arg("siiva{sv}") /// # .arg("") /// # .arg("0") /// # .arg("0") /// # .arg("i") /// # .arg("0") /// # .arg("0") /// # .output() /// # .unwrap(); /// /// while let Some(Ok(ev)) = events.next().await { /// // Handle Object events /// if let Ok(event) = StateChangedEvent::try_from(ev) { /// # break; /// // do something else here /// } else { continue } /// } /// # Ok(()) /// # } /// ``` #[cfg(feature = "wrappers")] pub fn event_stream(&self) -> impl Stream> { MessageStream::from(self.registry.inner().connection()).filter_map(|res| { let msg = match res { Ok(m) => m, Err(e) => return Some(Err(e.into())), }; let msg_header = msg.header(); let Some(msg_interface) = msg_header.interface() else { return Some(Err(AtspiError::MissingInterface)); }; // Most events we encounter are sent on an "org.a11y.atspi.Event.*" interface, // but there are exceptions like "org.a11y.atspi.Cache" or "org.a11y.atspi.Registry". // So only signals with a suitable interface name will be parsed. if msg_interface.starts_with("org.a11y.atspi") && msg.message_type() == MessageType::Signal { Some(Event::try_from(&msg)) } else { None } }) } /// Registers an events as defined in [`atspi_common::events`].\ /// This function registers a single event, like so: /// /// # Example /// ```rust /// use atspi_connection::common::events::object::StateChangedEvent; /// # tokio_test::block_on(async { /// let connection = atspi_connection::AccessibilityConnection::new().await.unwrap(); /// connection.register_event::().await.unwrap(); /// # }) /// ``` /// /// # Errors /// This function may return an error if a [`zbus::Error`] is caused by all the various calls to [`zbus::fdo::DBusProxy`] and [`zbus::MatchRule::try_from`]. pub async fn add_match_rule(&self) -> Result<(), AtspiError> { let match_rule = MatchRule::try_from(::MATCH_RULE_STRING)?; self.dbus_proxy.add_match_rule(match_rule).await?; Ok(()) } /// Deregisters an event as defined in [`atspi_common::events`].\ /// This function registers a single event, like so: /// /// # Example /// ```rust /// use atspi_connection::common::events::object::StateChangedEvent; /// # tokio_test::block_on(async { /// let connection = atspi_connection::AccessibilityConnection::new().await.unwrap(); /// connection.add_match_rule::().await.unwrap(); /// connection.remove_match_rule::().await.unwrap(); /// # }) /// ``` /// /// # Errors /// This function may return an error if a [`zbus::Error`] is caused by all the various calls to [`zbus::fdo::DBusProxy`] and [`zbus::MatchRule::try_from`]. pub async fn remove_match_rule(&self) -> Result<(), AtspiError> { let match_rule = MatchRule::try_from(::MATCH_RULE_STRING)?; self.dbus_proxy.add_match_rule(match_rule).await?; Ok(()) } /// Add a registry event. /// This tells accessible applications which events should be forwarded to the accessibility bus. /// This is called by [`Self::register_event`]. /// /// # Example /// ```rust /// use atspi_connection::common::events::object::StateChangedEvent; /// # tokio_test::block_on(async { /// let connection = atspi_connection::AccessibilityConnection::new().await.unwrap(); /// connection.add_registry_event::().await.unwrap(); /// connection.remove_registry_event::().await.unwrap(); /// # }) /// ``` /// /// # Errors /// May cause an error if the `DBus` method [`atspi_proxies::registry::RegistryProxy::register_event`] fails. pub async fn add_registry_event(&self) -> Result<(), AtspiError> { self.registry .register_event(::REGISTRY_EVENT_STRING) .await?; Ok(()) } /// Remove a registry event. /// This tells accessible applications which events should be forwarded to the accessibility bus. /// This is called by [`Self::deregister_event`]. /// It may be called like so: /// /// # Example /// ```rust /// use atspi_connection::common::events::object::StateChangedEvent; /// # tokio_test::block_on(async { /// let connection = atspi_connection::AccessibilityConnection::new().await.unwrap(); /// connection.add_registry_event::().await.unwrap(); /// connection.remove_registry_event::().await.unwrap(); /// # }) /// ``` /// /// # Errors /// May cause an error if the `DBus` method [`RegistryProxy::deregister_event`] fails. pub async fn remove_registry_event(&self) -> Result<(), AtspiError> { self.registry .deregister_event(::REGISTRY_EVENT_STRING) .await?; Ok(()) } /// This calls [`Self::add_registry_event`] and [`Self::add_match_rule`], two components necessary to receive accessibility events. /// # Errors /// This will only fail if [`Self::add_registry_event`[ or [`Self::add_match_rule`] fails. pub async fn register_event( &self, ) -> Result<(), AtspiError> { self.add_registry_event::().await?; self.add_match_rule::().await?; Ok(()) } /// This calls [`Self::remove_registry_event`] and [`Self::remove_match_rule`], two components necessary to receive accessibility events. /// # Errors /// This will only fail if [`Self::remove_registry_event`] or [`Self::remove_match_rule`] fails. pub async fn deregister_event( &self, ) -> Result<(), AtspiError> { self.remove_registry_event::().await?; self.remove_match_rule::().await?; Ok(()) } /// Shorthand for a reference to the underlying [`zbus::Connection`] #[must_use = "The reference to the underlying zbus::Connection must be used"] pub fn connection(&self) -> &zbus::Connection { self.registry.inner().connection() } /// Send an event over the accessibility bus. /// This converts the event into a [`zbus::Message`] using the [`DBusMember`] + [`DBusInterface`] trait. /// /// # Errors /// This will only fail if: /// 1. [`zbus::Message`] fails at any point, or /// 2. sending the event fails for some reason. /// /// Both of these conditions should never happen as long as you have a valid event. pub async fn send_event<'a, T>(&self, event: T) -> Result<(), AtspiError> where T: DBusMember + DBusInterface + EventProperties + MessageConversion<'a>, { let conn = self.connection(); let new_message = zbus::Message::signal( event.path(), ::DBUS_INTERFACE, ::DBUS_MEMBER, )? .sender(conn.unique_name().ok_or(AtspiError::MissingName)?)? // this re-encodes the entire body; it's not great..., but you can't replace a sender once a message a created. .build(&event.body())?; Ok(conn.send(&new_message).await?) } /// Get the root accessible object from the atspi registry; /// This semantically represents the root of the accessibility tree /// and can be used for tree traversals. /// /// It may be called like so: /// /// # Example /// ```rust /// use atspi_connection::AccessibilityConnection; /// use zbus::proxy::CacheProperties; /// # tokio_test::block_on(async { /// let connection = atspi_connection::AccessibilityConnection::new().await.unwrap(); /// let root = connection.root_accessible_on_registry().await.unwrap(); /// let children = root.get_children().await; /// assert!(children.is_ok()); /// # }); /// ``` /// # Errors /// This will fail if a dbus connection cannot be established when trying to /// connect to the registry /// /// # Panics /// This will panic if the `default_service` is not set on the `RegistryProxy`, which should never happen. pub async fn root_accessible_on_registry(&self) -> Result, AtspiError> { let registry_well_known_name = RegistryProxy::DESTINATION .as_ref() .expect("Registry default_service is set"); let registry = AccessibleProxy::builder(self.connection()) .destination(registry_well_known_name)? // registry has an incomplete implementation of the DBus interface // and therefore cannot be cached; thus we always disable caching it .cache_properties(CacheProperties::No) .build() .await?; Ok(registry) } } impl Deref for AccessibilityConnection { type Target = RegistryProxy<'static>; fn deref(&self) -> &Self::Target { &self.registry } } /// Set the `IsEnabled` property in the session bus. /// /// Assistive Technology provider applications (ATs) should set the accessibility /// `IsEnabled` status on the users session bus on startup as applications may monitor this property /// to enable their accessibility support dynamically. /// /// See: The [freedesktop - AT-SPI2 wiki](https://www.freedesktop.org/wiki/Accessibility/AT-SPI2/) /// /// # Example /// ```rust /// let result = tokio_test::block_on( atspi_connection::set_session_accessibility(true) ); /// assert!(result.is_ok()); /// ``` /// # Errors /// /// 1. when no connection with the session bus can be established, /// 2. if creation of a [`atspi_proxies::bus::StatusProxy`] fails /// 3. if the `IsEnabled` property cannot be read /// 4. the `IsEnabled` property cannot be set. pub async fn set_session_accessibility(status: bool) -> std::result::Result<(), AtspiError> { // Get a connection to the session bus. let session = Box::pin(zbus::Connection::session()).await?; // Acquire a `StatusProxy` for the session bus. let status_proxy = StatusProxy::new(&session).await?; if status_proxy.is_enabled().await? != status { status_proxy.set_is_enabled(status).await?; } Ok(()) } /// Read the `IsEnabled` accessibility status property on the session bus. /// /// # Examples /// ```rust /// # tokio_test::block_on( async { /// let status = atspi_connection::read_session_accessibility().await; /// /// // The status is either true or false /// assert!(status.is_ok()); /// # }); /// ``` /// /// # Errors /// /// - If no connection with the session bus could be established. /// - If creation of a [`atspi_proxies::bus::StatusProxy`] fails. /// - If the `IsEnabled` property cannot be read. pub async fn read_session_accessibility() -> AtspiResult { // Get a connection to the session bus. let session = Box::pin(zbus::Connection::session()).await?; // Acquire a `StatusProxy` for the session bus. let status_proxy = StatusProxy::new(&session).await?; // Read the `IsEnabled` property. status_proxy.is_enabled().await.map_err(Into::into) } atspi-connection-0.13.0/src/p2p.rs000064400000000000000000000702421046102023000150250ustar 00000000000000//! Extends the `AccessibilityConnection` with P2P capabilities. //! //! # Considerations on using executors and P2P //! //! Every connection has a zbus `Executor` instance, on which tasks can be launched. Internally, zbus uses this executor for all sorts of tasks: listening for D-Bus signals, //! keeping property caches up to date, handling timeout errors, etc. //! //! When zbus users use `tokio` (zbus feature "tokio" set), zbus will latch onto the tokio runtime. //! The `Executor` instance will be empty and all zbus tasks are run on the user's tokio runtime. //! However, when using any other executor (smol, glommio, etc.), each `Connection` will spin up a thread with an `async_executor::Executor`. //! //! Typically an application will have a single connection, but with P2P, your application will have a connection with each application that supports it. //! Consequently, on anything but tokio, applications will get an extra thread with an `async_executor` for each connection! //! (So picking smol won't necessarily make your application small in the context of P2P.) use atspi_common::{object_ref::ObjectRefOwned, AtspiError}; use atspi_proxies::{ accessible::{AccessibleProxy, ObjectRefExt}, application::{self, ApplicationProxy}, proxy_ext::ProxyExt, registry::RegistryProxy, }; use futures_lite::stream::StreamExt; use std::sync::{Arc, Mutex}; use zbus::{ conn::Builder, fdo::DBusProxy, names::{ BusName, OwnedBusName, OwnedUniqueName, OwnedWellKnownName, UniqueName, WellKnownName, }, proxy::{CacheProperties, Defaults}, zvariant::ObjectPath, Address, }; #[cfg(feature = "tracing")] use tracing::{debug, info, warn}; use crate::AtspiResult; /// Represents a peer with the name, path and connection for the P2P peer. #[derive(Clone, Debug)] pub struct Peer { unique_name: OwnedUniqueName, well_known_name: Option, socket_address: Address, p2p_connection: zbus::Connection, } impl Peer { /// Creates a new `Peer` with the given bus name and socket path. /// /// # Note /// This function is intended for use in building the initial list of peers. /// /// If given a `UniqueName`, it will check if the peer also owns a well-known name. /// If given a `WellKnownName`, it will query the D-Bus for the unique name of the peer. /// /// # Errors /// - `DBusProxy` cannot be created. /// - The socket address cannot be parsed. /// pub(crate) async fn try_new( bus_name: B, socket: S, conn: &zbus::Connection, ) -> Result where B: Into, S: TryInto
, { let dbus_proxy = DBusProxy::new(conn).await?; let owned_bus_name: OwnedBusName = bus_name.into(); let socket_address = socket .try_into() .map_err(|_| AtspiError::ParseError("Invalid address string"))?; // Because D-Bus does not let us query whether a unique name is the owner of a well-known name, // we need to query all well-known names and their owners, and then check if the unique name is one of them. // Get all well-known names from D-Bus let well_known_names: Vec = dbus_proxy .list_names() .await? .into_iter() .filter_map(|name| { if let BusName::WellKnown(well_nown_name) = name.clone().inner() { Some(OwnedWellKnownName::from(well_nown_name.clone())) } else { None } }) .collect(); // We are creating a mapping of unique names to well-known names. let mut unique_to_well_known: Vec<(OwnedUniqueName, OwnedWellKnownName)> = Vec::new(); // For each well-known name, we get the unique name of the owner. // Note: not all well-known names on the bus will have an accessible owner. // For instance, the `org.freedesktop.DBus` well-known name does not have an accessible connection. for well_known_name in &well_known_names { let bus_name = BusName::from(well_known_name.clone()); if let Ok(unique_name) = dbus_proxy.get_name_owner(bus_name).await { unique_to_well_known.push((unique_name, well_known_name.clone())); } } // Now we have a mapping of unique names to well-known names. // // A `Peer` instance requires a unique name which may or may not have a well-known name. // We can build a `Peer` instance from either a unique name or a well-known name. // If the argument name is a unique name, we look up whether this peer also owns a well-known name in our mapping. // If the argument name is a well-known name, we _must_ get its unique owner too to create a `Peer`. let (unique_name, well_known_name) = match owned_bus_name.inner() { BusName::Unique(name) => { // The argument name is the mandatory `UniqueName`, we do want check whether this peer also owns a well-known name. let owned_well_known_name = unique_to_well_known.iter().find_map(|(u, w)| { if u == name { Some(w.clone()) } else { None } }); let owned_unique_name = OwnedUniqueName::from(name.clone()); (owned_unique_name, owned_well_known_name) } BusName::WellKnown(well_known_name) => { let bus_name = BusName::from(well_known_name.clone()); let owned_unique_name = dbus_proxy.get_name_owner(bus_name).await?; let owned_well_known_name = OwnedWellKnownName::from(well_known_name.clone()); (owned_unique_name, Some(owned_well_known_name)) } }; let p2p_connection = Builder::address(socket_address.clone())?.p2p().build().await?; Ok(Peer { unique_name, well_known_name, socket_address, p2p_connection }) } /// Returns the bus name of the peer. #[must_use] pub fn unique_name(&self) -> &OwnedUniqueName { &self.unique_name } /// Returns the well-known bus name of the peer, if it has one. #[must_use] pub fn well_known_name(&self) -> Option<&OwnedWellKnownName> { self.well_known_name.as_ref() } /// Returns the socket [`Address`] of the peer. #[must_use] pub fn socket_address(&self) -> &Address { &self.socket_address } /// Returns the p2p [`Connection`][zbus::Connection] of the peer. pub fn connection(&self) -> &zbus::Connection { &self.p2p_connection } /// Try to create a new `Peer` from a bus name. /// /// # Errors /// Returns an error if the application proxy cannot be created or if it does not support `get_application_bus_address`.\ /// A non-existent bus name will also return an error. pub async fn try_from_bus_name( bus_name: BusName<'_>, conn: &zbus::Connection, ) -> AtspiResult { // Get the application proxy for the bus name let application_proxy = ApplicationProxy::builder(conn) .destination(&bus_name)? .cache_properties(CacheProperties::No) .build() .await?; let socket_path = application_proxy.get_application_bus_address().await?; Self::try_new(bus_name, socket_path.as_str(), conn).await } /// Returns a [`Proxies`][atspi_proxies::proxy_ext::Proxies] object for the given object path.\ /// A `Proxies` object is used to obtain any of the proxies the object supports. /// /// # Errors /// On invalid object path. pub async fn proxies( &'_ self, path: &ObjectPath<'_>, ) -> AtspiResult> { let accessible_proxy = AccessibleProxy::builder(&self.p2p_connection) .path(path.to_owned())? .cache_properties(CacheProperties::No) .build() .await?; accessible_proxy.proxies().await } /// Returns an `AccessibleProxy` for the root accessible object of the peer. /// /// # Errors /// In case of an invalid connection. pub async fn as_root_accessible_proxy(&self) -> AtspiResult> { AccessibleProxy::builder(&self.p2p_connection) .cache_properties(CacheProperties::No) .build() .await .map_err(AtspiError::from) } /// Returns an [`AccessibleProxy`] for the accessible object of the peer. /// /// # Errors /// In case of an invalid connection or object path. pub async fn as_accessible_proxy( &self, obj: &ObjectRefOwned, ) -> AtspiResult> { let path = obj.path(); AccessibleProxy::builder(&self.p2p_connection) .path(path)? .cache_properties(CacheProperties::No) .build() .await .map_err(AtspiError::from) } } // A trait is needed to extend functionality on `BusName` for P2P address lookup. pub(crate) trait BusNameExt { /// Looks up a `BusName`'s P2P address, if available. async fn get_p2p_address(&self, conn: &zbus::Connection) -> AtspiResult
; } impl BusNameExt for BusName<'_> { async fn get_p2p_address(&self, conn: &zbus::Connection) -> AtspiResult
{ let application_proxy = application::ApplicationProxy::builder(conn) .destination(self)? .cache_properties(CacheProperties::No) .build() .await?; application_proxy .get_application_bus_address() .await .map_err(|e| { AtspiError::Owned(format!( "Failed to get application bus address for {}: {e}", &self )) }) .and_then(|address| { Address::try_from(address.as_str()) .map_err(|_| AtspiError::ParseError("Invalid address string")) }) } } #[derive(Clone, Debug)] pub(crate) struct Peers { peers: Arc>>, } impl Peers { /// Returns a `Peers` containing the initial peers that support P2P connections. /// /// # Note /// Intended for internal use with `AccessibilityConnection::new()`. /// /// # Errors /// This function can return an error in the following cases: /// - the `AccessibleProxy` to the registry cannot be created. /// - the registry returns an error when querying for children. /// - for any child, the `AccessibleProxy` cannot be created or the `ApplicationProxy` cannot be created. pub(crate) async fn initialize_peers(conn: &zbus::Connection) -> AtspiResult { let registry_well_known_name = RegistryProxy::DESTINATION .as_ref() .expect("RegistryProxy `default_destination` is not set"); let reg_accessible = AccessibleProxy::builder(conn) .destination(registry_well_known_name)? .cache_properties(CacheProperties::No) .build() .await?; let accessible_applications = reg_accessible.get_children().await?; let mut peers = Vec::with_capacity(accessible_applications.len()); for app in accessible_applications { let accessible_proxy = app.as_accessible_proxy(conn).await?; let proxies = accessible_proxy.proxies().await?; let application_proxy = proxies.application().await?; // Get the application bus address // aka: Does the application support P2P connections? if let Ok(address) = application_proxy.get_application_bus_address().await { let name = app.name().ok_or(AtspiError::MissingName)?; let bus_name = BusName::from(name.clone()); match Peer::try_new(bus_name, address.as_str(), conn).await { Ok(peer) => peers.push(peer), #[cfg(feature = "tracing")] Err(e) => { tracing::warn!("Failed to create peer for {:?}: {}", app.name_as_str(), e); } #[cfg(all(debug_assertions, not(feature = "tracing")))] Err(e) => { eprintln!("Failed to create peer for {:?}: {}", app.name_as_str(), e); } #[cfg(not(any(feature = "tracing", debug_assertions)))] Err(_) => { // Ignore error creating peer } } } } Ok(Peers { peers: Arc::new(Mutex::new(peers)) }) } /// Returns a [`Peer`] by its bus name. fn get_peer(&self, bus_name: &BusName<'_>) -> Option { let peers = self.peers.lock().expect("already locked by current thread"); let matched = match bus_name { BusName::Unique(unique_name) => { peers.iter().find(|peer| peer.unique_name() == unique_name) } BusName::WellKnown(well_known_name) => { let owned_well_known_name = OwnedWellKnownName::from(well_known_name.clone()); peers .iter() .find(|peer| peer.well_known_name() == Some(&owned_well_known_name)) } }; matched.cloned() } /// Returns the inner `Arc>>`. fn inner(&self) -> Arc>> { Arc::clone(&self.peers) } /// Inserts a new `Peer` into the list of peers. async fn insert_unique( &self, unique_name: &zbus::names::UniqueName<'_>, conn: &zbus::Connection, ) -> AtspiResult<()> { let bus_name = BusName::Unique(unique_name.as_ref()); let address = bus_name.get_p2p_address(conn).await?; let p2p_connection = Builder::address(address.clone())?.p2p().build().await?; let unique_name = OwnedUniqueName::from(unique_name.clone()); let peer = Peer { unique_name, well_known_name: None, socket_address: address, p2p_connection }; let mut guard = self.peers.lock().expect("lock already held by current thread"); guard.push(peer); Ok(()) } /// Removes a `Peer` from the list of peers by its unique name. fn remove_unique(&self, unique_name: &zbus::names::UniqueName<'_>) { let mut peers = self.peers.lock().expect("lock already held by current thread"); peers.retain(|peer| peer.unique_name() != unique_name); } /// Inserts a new `Peer` with a well-known name into the list of peers. async fn insert_well_known( &self, well_known_name: &WellKnownName<'_>, name_owner: &UniqueName<'_>, conn: &zbus::Connection, ) -> AtspiResult<()> { let bus_name = BusName::WellKnown(well_known_name.clone()); let address = bus_name.get_p2p_address(conn).await?; let p2p_connection = Builder::address(address.clone())?.p2p().build().await?; let well_known_name = OwnedWellKnownName::from(well_known_name.clone()); let unique_name = OwnedUniqueName::from(name_owner.clone()); let peer = Peer { unique_name, well_known_name: Some(well_known_name), socket_address: address, p2p_connection, }; let mut guard = self.peers.lock().expect("lock already held by current thread"); guard.push(peer); Ok(()) } /// Removes a `Peer` with a well-known name from the list of peers. fn remove_well_known(&self, well_known_name: &WellKnownName<'_>, name_owner: &UniqueName<'_>) { let mut peers = self.peers.lock().expect("lock already held by current thread"); let owned_well_known_name = OwnedWellKnownName::from(well_known_name.clone()); peers.retain(|peer| { (peer.well_known_name() != Some(&owned_well_known_name)) && peer.unique_name() == name_owner }); } /// Update a `Peer` with a new owner of it's well-known name in the list of peers. async fn update_well_known_owner( &self, well_known_name: &WellKnownName<'_>, old_name_owner: &UniqueName<'_>, new_name_owner: &UniqueName<'_>, conn: &zbus::Connection, ) -> AtspiResult<()> { let socket_address = BusName::from(new_name_owner.clone()).get_p2p_address(conn).await?; let p2p_connection = Builder::address(socket_address.clone())?.p2p().build().await?; let well_known_name = Some(OwnedWellKnownName::from(well_known_name.clone())); let old_name_owner = OwnedUniqueName::from(old_name_owner.clone()); let unique_name = OwnedUniqueName::from(new_name_owner.clone()); let peer = Peer { unique_name, well_known_name: well_known_name.clone(), socket_address, p2p_connection, }; let mut peers = self.peers.lock().expect("lock already held by current thread"); if let Some(existing_peer) = peers.iter_mut().find(|p| { p.well_known_name() == well_known_name.as_ref() && p.unique_name() == &old_name_owner }) { *existing_peer = peer; } else { return Err(AtspiError::Owned(format!( "Owner swap failed: well-known name {well_known_name:?} with owner: {old_name_owner} not found" ))); } Ok(()) } /// Spawns a task which listens for peer mutations. /// /// This task listens for `NameOwnerChanged` signals and updates the list of peers accordingly. /// /// # executor /// The task is spawned on the executor of the `zbus::Connection`. /// /// # Note /// This function is called internally by `AccessibilityConnection::new()`. pub(crate) fn spawn_peer_listener_task(&self, conn: &zbus::Connection) { // Clone the `Peers` and `Connection` to move them into the async task. // This is necessary because the async task needs to own these values. let peers = self.clone(); let conn = conn.clone(); let dbus_proxy = futures_lite::future::block_on(DBusProxy::new(&conn)) .expect("Failed to create DBusProxy"); let executor = conn.executor().clone(); executor.spawn(async move { let Ok(mut name_owner_changed_stream) = dbus_proxy.receive_name_owner_changed().await.inspect_err(|#[allow(unused_variables)] err| { #[cfg(feature = "tracing")] debug!("Failed to receive `NameOwnerChanged` stream: {err}"); }) else { return; }; while let Some(name_owner_event) = name_owner_changed_stream.next().await { let Ok(args) = name_owner_event.args() else { #[cfg(feature = "tracing")] tracing::debug!("Received name owner changed event without args, skipping."); continue; }; let name = args.name().clone(); let new = args.new_owner().clone(); let old = args.old_owner().clone(); // `NameOwnerChanged` table (U = Unique, W = Well-Known): // | Name | Old Owner | New Owner | Operation | // |------|-----------|-----------|----------| // | U | None | Some(U) | Add | // | U | Some(U) | None | Remove | // | W | None | Some(U) | Add | // | W | Some(U) | None | Remove | // | W | Some(U) | Some(U) | Replace | match name { BusName::Unique(unique_name) => { // `zvariant:Optional` has deref target `Option`. match (&*old, &*new) { // Application appeared on the bus. (None, Some(new_owner)) => { debug_assert_eq!(new_owner, &unique_name, "When a name appears on the bus, the new owner must be the unique name itself."); if let Ok(()) = peers.insert_unique(&unique_name, &conn).await.inspect_err(|#[allow(unused_variables)] err| { #[cfg(feature = "tracing")] warn!("Failed to insert unique name: {unique_name}: {err}"); }) { #[cfg(feature = "tracing")] info!("Inserted unique name: {unique_name} into the peer list."); }; } // Unique name left the bus. (Some(old), None) => { debug_assert!(old == &unique_name, "When a unique name is removed from the bus, the old owner must be the unique name itself."); peers.remove_unique(&unique_name); #[cfg(feature = "tracing")] info!("Peer with unique name: {unique_name} left the bus - removed from peer list."); } // Unknown combination. (_, _) => { #[cfg(feature = "tracing")] debug!("NameOwnerChanged` with unique name: {unique_name} has unknown argument combination ({old:?}, {new:?})."); } } } BusName::WellKnown(well_known_name) => { match (&*old, &*new) { // Unknown mutatuion. Well-known names should always have at least a new or old owner. (None, None) => { #[cfg(feature = "tracing")] debug!("Received `NameOwnerChanged` event with no old or new owner for well-known name: {}", well_known_name); } // Well-known name appeared on the bus. (None, Some(new_owner_unique_name)) => { if let Ok(()) = peers.insert_well_known( &well_known_name, new_owner_unique_name, &conn, ).await.inspect_err(|#[allow(unused_variables)] err| { #[cfg(feature = "tracing")] warn!("Failed to insert well-known name: {} with owner: {} - {}", &well_known_name, &new_owner_unique_name, err); }) { #[cfg(feature = "tracing")] info!("Well-known name: {} with owner: {} inserted into the peer list.", &well_known_name, &new_owner_unique_name); } } // Well-known name left the bus. (Some(old_owner_unique_name), None) => { peers.remove_well_known( &well_known_name, old_owner_unique_name, ); #[cfg(feature = "tracing")] info!( "Well-known name: {} with owner: {} removed from the peer list.", &well_known_name, &old_owner_unique_name ); }, // Well-known name received a new owner on the bus. (Some(old_owner_unique_name), Some(new_owner_unique_name)) => { if let Ok(()) = peers.update_well_known_owner(&well_known_name, old_owner_unique_name, new_owner_unique_name, &conn).await.inspect_err(|#[allow(unused_variables)] err| { #[cfg(feature = "tracing")] warn!("Failed to update well-known name: {} owner from: {} to: {} - {}", &well_known_name, &old_owner_unique_name, &new_owner_unique_name, err); }) { #[cfg(feature = "tracing")] info!("Well-known name: {} updated owner from: {} to: {}", &well_known_name, &old_owner_unique_name, &new_owner_unique_name); }; } } } } // End of match on `name` } // End of while loop #[cfg(feature = "tracing")] tracing::warn!("Peer listener task stopped, clearing peers list."); peers.clear(); }, "PeerListenerTask") .detach(); } /// Clears the list of peers. /// /// # Note /// This is used to reset the list of peers when the D-Bus connection is lost. fn clear(&self) { let mut peers = self.peers.lock().expect("lock already held by current thread"); peers.clear(); } } /// Trait for P2P connection handling. pub trait P2P { /// Returns a P2P connected `AccessibleProxy`for object, _if available_.\ /// If the application does not support P2P, this returns an `AccessibleProxy` for the object with a bus connection. fn object_as_accessible( &'_ self, obj: &ObjectRefOwned, ) -> impl std::future::Future>>; /// Returns a P2P connected `AccessibleProxy` to the root accessible object for the given bus name, _if available_.\ /// If the P2P connection is not available, it returns an `AccessibleProxy` with a bus connection. fn bus_name_as_root_accessible( &'_ self, name: &BusName, ) -> impl std::future::Future>>; /// Return a list of peers that are currently connected. fn peers(&self) -> Arc>>; /// Returns a [`Peer`] by its bus name. fn get_peer(&self, bus_name: &BusName<'_>) -> Option; } impl P2P for crate::AccessibilityConnection { /// Returns a P2P connected `AccessibleProxy` for the object, _if available_.\ /// If the application does not support P2P, an `AccessibleProxy` with a bus connection is returned. /// /// # Examples /// ```rust /// # use tokio_test::block_on; /// use zbus::names::UniqueName; /// use zbus::zvariant::ObjectPath; /// use atspi_proxies::accessible::AccessibleProxy; /// use atspi_common::ObjectRef; /// use atspi_connection::{P2P, Peer}; /// use atspi_connection::AccessibilityConnection; /// /// # block_on(async { /// let conn = AccessibilityConnection::new().await.unwrap(); /// /// let name = UniqueName::from_static_str_unchecked(":1.1"); /// let path = ObjectPath::from_static_str_unchecked("/org/freedesktop/accessible/root"); /// /// let object_ref = ObjectRef::new_owned(name, path); /// let accessible_proxy = conn.object_as_accessible(&object_ref).await; /// assert!( /// accessible_proxy.is_ok(), /// "Failed to get accessible proxy: {:?}", /// accessible_proxy.err() /// ); /// # }); /// ``` /// /// Handling `ObjectRef::Null` case: /// /// ```rust /// # use tokio_test::block_on; /// use atspi_proxies::accessible::AccessibleProxy; /// use atspi_common::{AtspiError, ObjectRef, ObjectRefOwned}; /// use atspi_connection::P2P; /// use atspi_connection::AccessibilityConnection; /// /// # block_on(async { /// let conn = AccessibilityConnection::new().await.unwrap(); /// let object_ref = ObjectRef::Null; /// let object_ref = ObjectRefOwned::new(object_ref); // Assume we received this from `Accessible.Parent` /// /// let res = conn.object_as_accessible(&object_ref).await; /// match res { /// Ok(proxy) => { /// // Use the proxy /// let _proxy: AccessibleProxy<'_> = proxy; /// } /// Err(AtspiError::NullRef(_msg)) => { /// // Handle null-reference case /// } /// Err(_other) => { /// // Handle other error types /// } /// } /// # }); /// ``` /// /// # Errors /// If the method is called with a null-reference `ObjectRef`, it will return an `AtspiError::NullRef`. /// Users should ensure that the `ObjectRef` is non-null before calling this method or handle the result. /// If the `AccessibleProxy` cannot be created, or if the object path is invalid. /// /// # Note /// This function will first try to find a [`Peer`] with a P2P connection async fn object_as_accessible(&self, obj: &ObjectRefOwned) -> AtspiResult> { if obj.is_null() { return Err(AtspiError::NullRef( "`p2p::object_as_accessible` called with null-reference ObjectRef", )); } let name = obj.name().ok_or(AtspiError::MissingName)?.to_owned(); let name = OwnedUniqueName::from(name); let path = obj.path(); let lookup = self .peers .peers .lock() .expect("lock already held by current thread") .iter() .find(|peer| &name == peer.unique_name()) .cloned(); if let Some(peer) = lookup { // If a peer is found, create an `AccessibleProxy` with a P2P connection AccessibleProxy::builder(peer.connection()) .path(path)? .cache_properties(CacheProperties::No) .build() .await .map_err(Into::into) } else { // If _no_ peer was found, fall back to the bus connection let conn = self.connection(); AccessibleProxy::builder(conn) .path(path)? .cache_properties(CacheProperties::No) .build() .await .map_err(Into::into) } } /// Returns a P2P connected [`AccessibleProxy`] to the root accessible object for the given bus name _if available_.\ /// If the P2P connection is not available, it returns an `AccessibleProxy` with a bus connection. /// /// # Examples /// /// ```rust /// # use tokio_test::block_on; /// use zbus::names::BusName; /// use atspi_proxies::accessible::AccessibleProxy; /// use atspi_common::ObjectRef; /// use atspi_connection::{AccessibilityConnection, P2P}; /// /// # block_on(async { /// let conn = AccessibilityConnection::new().await.unwrap(); /// let bus_name = BusName::from_static_str("org.a11y.atspi.Registry").unwrap(); /// let _accessible_proxy = conn.bus_name_as_root_accessible(&bus_name).await.unwrap(); /// // Use the accessible proxy as needed /// # }); /// ``` /// /// # Errors /// In case of an invalid connection or object path. async fn bus_name_as_root_accessible( &'_ self, name: &BusName<'_>, ) -> AtspiResult> { // Look up peer by bus name let lookup = self .peers .peers .lock() .expect("lock already held by current thread") .iter() .find(|peer| { // Check if the peer's unique name matches the bus name match name { BusName::Unique(unique_name) => peer.unique_name() == unique_name, BusName::WellKnown(well_known_name) => { peer.well_known_name().is_some_and(|w| w == well_known_name) } } }) .cloned(); if let Some(peer) = lookup { // If a peer is found, create an AccessibleProxy with a P2P connection AccessibleProxy::builder(peer.connection()) .cache_properties(CacheProperties::No) .build() .await .map_err(Into::into) } else { // If no peer is found, fall back to the bus connection let conn = self.connection(); AccessibleProxy::builder(conn) .cache_properties(CacheProperties::No) .build() .await .map_err(Into::into) } } /// Get the currently connected P2P capable peers. /// /// # Examples /// ```rust /// # use tokio_test::block_on; /// use atspi_connection::AccessibilityConnection; /// use atspi_connection::{P2P, Peer}; /// /// # block_on(async { /// let conn = AccessibilityConnection::new().await.unwrap(); /// let locked_peers = conn.peers(); /// let peers = locked_peers.lock().expect("lock already held by current thread"); /// for peer in &*peers { /// println!("Peer: {} at {}", peer.unique_name(), peer.socket_address()); /// } /// # }); /// ``` fn peers(&self) -> Arc>> { self.peers.inner() } /// Returns a [`Peer`] by its bus name. /// /// # Examples /// ```rust /// # use tokio_test::block_on; /// use atspi_connection::{AccessibilityConnection, P2P, Peer}; /// use zbus::names::BusName; /// /// # block_on(async { /// let a11y = AccessibilityConnection::new().await.unwrap(); /// let bus_name = BusName::from_static_str(":1.42").unwrap(); /// let peer: Option = a11y.get_peer(&bus_name); /// # }); /// ``` fn get_peer(&self, bus_name: &BusName<'_>) -> Option { self.peers.get_peer(bus_name) } }