atspi-common-0.13.0/.cargo_vcs_info.json0000644000000001520000000000100135210ustar { "git": { "sha1": "f486755b9bf993613f63a779133aafc54925004a" }, "path_in_vcs": "atspi-common" }atspi-common-0.13.0/Cargo.lock0000644000000775220000000000100115130ustar # 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 = "assert_matches" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[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" dependencies = [ "assert_matches", "enumflags2", "rename-item", "serde", "serde_plain", "static_assertions", "tokio", "tokio-stream", "tokio-test", "zbus", "zbus-lockstep", "zbus-lockstep-macros", "zbus_names", "zbus_xml", "zvariant", ] [[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 = "darling" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", ] [[package]] name = "darling_core" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "syn", ] [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", "syn", ] [[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 = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[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 = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[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 = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[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 = "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 = "rename-item" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f80b251729e529bbe8893766fb6e20594f6e8dc1e495b8d795a98beb609666a1" dependencies = [ "darling", "heck", "proc-macro2", "quote", "syn", ] [[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_plain" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" dependencies = [ "serde", ] [[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", "tokio-macros", ] [[package]] name = "tokio-macros" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", "syn", ] [[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", "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 = "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-common-0.13.0/Cargo.toml0000644000000043100000000000100115170ustar # 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-common" version = "0.13.0" build = false include = [ "LICENSE-*", "README.md", "src/**/*", "xml/*", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Primitive types used for sending and receiving Linux accessibility events." readme = "README.md" keywords = [ "Accessibility", "Macros", ] categories = [ "accessibility", "api-bindings", ] license = "Apache-2.0 OR MIT" repository = "https://github.com/odilia-app/atspi" [features] default = [ "wrappers", "zbus", ] wrappers = [] zbus = ["dep:zbus"] [lib] name = "atspi_common" path = "src/lib.rs" [dependencies.enumflags2] version = "0.7.9" [dependencies.serde] version = "1.0.200" [dependencies.static_assertions] version = "1.1.0" [dependencies.zbus] version = "5.5" features = ["async-io"] optional = true default-features = false [dependencies.zbus-lockstep] version = "0.5.1" [dependencies.zbus-lockstep-macros] version = "0.5.1" [dependencies.zbus_names] version = "4.1.1" [dependencies.zvariant] version = "5.2" default-features = false [dev-dependencies.assert_matches] version = "1.5.0" [dev-dependencies.rename-item] version = "0.1.0" [dev-dependencies.serde_plain] version = "1.0.1" [dev-dependencies.static_assertions] version = "1.1.0" [dev-dependencies.tokio] version = "1.38.2" features = [ "macros", "rt-multi-thread", ] default-features = false [dev-dependencies.tokio-stream] version = "0.1.1" features = ["time"] default-features = false [dev-dependencies.tokio-test] version = "0.4.2" [dev-dependencies.zbus] version = "5.5" features = ["async-io"] default-features = false [dev-dependencies.zbus_xml] version = "5.0.2" atspi-common-0.13.0/Cargo.toml.orig000064400000000000000000000031701046102023000152030ustar 00000000000000[package] categories = ["accessibility", "api-bindings"] description = "Primitive types used for sending and receiving Linux accessibility events." edition = "2021" include = ["LICENSE-*", "README.md", "src/**/*", "xml/*"] keywords = ["Accessibility", "Macros"] license = "Apache-2.0 OR MIT" name = "atspi-common" 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] zbus = ["dep:zbus"] default = ["wrappers", "zbus"] wrappers = [] [dependencies] enumflags2 = "0.7.9" serde = "1.0.200" static_assertions = "1.1.0" zbus = { workspace = true, optional = true } zbus-lockstep = { version = "0.5.1" } zbus-lockstep-macros = { version = "0.5.1" } zbus_names = "4.1.1" zvariant = { version = "5.2", default-features = false } [dev-dependencies] assert_matches = "1.5.0" atspi-connection = { path = "../atspi-connection" } atspi-proxies = { path = "../atspi-proxies" } rename-item = "0.1.0" serde_plain = "1.0.1" static_assertions = "1.1.0" tokio = { version = "1.38.2", default-features = false, features = [ "macros", "rt-multi-thread", ] } tokio-stream = { version = "0.1.1", default-features = false, features = ["time"] } tokio-test = "0.4.2" zbus_xml = "5.0.2" zbus = { workspace = true } atspi-common-0.13.0/LICENSE-APACHE2.txt000064400000000000000000000260741046102023000151500ustar 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-common-0.13.0/LICENSE-MIT.txt000064400000000000000000000020571046102023000145710ustar 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-common-0.13.0/README.md000064400000000000000000000012141046102023000135700ustar 00000000000000# `atspi-common` Common data structures for the `atspi` crate. This crate is meant to only contain the absolute lowest common denominator for libraries to talk to each other about `atspi` information; therefore, it attempts to be able to compile on basically any architecture (including WASM). Please use the internal documentation to learn how to use these data structures. ## Feature Flags - `default`: `zbus`, `wrappers` - `zbus`: include support for serializing/deserializing from `zbus::Message`s over DBus. - `wrappers`: container enum types that group events by AT-SPI interface, as well as `Event` wrapper that contains any possible event. atspi-common-0.13.0/src/action.rs000064400000000000000000000015751046102023000147350ustar 00000000000000use serde::{Deserialize, Serialize}; use zvariant::Type; /// An action which may be triggered through the accessibility API. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] pub struct Action { /// The name of the action pub name: String, /// Description of the action pub description: String, // TODO: should be an enum/stricter type; this is why it's in its own file. /// The keybinding(s) used to trigger it (without the AT). pub keybinding: String, } #[cfg(test)] mod test { use super::Action; use zbus_lockstep::method_return_signature; use zvariant::Type; #[test] fn validate_action_signature() { // signature is of type `a(sss)`, where `(sss)` is the type we're validating. let action_signature = method_return_signature!(member: "GetActions", interface: "org.a11y.atspi.Action"); assert_eq!(Vec::::SIGNATURE, &action_signature); } } atspi-common-0.13.0/src/cache.rs000064400000000000000000000064101046102023000145140ustar 00000000000000//! Common types for `org.a11y.atspi.Cache` events. //! use crate::{object_ref::ObjectRefOwned, InterfaceSet, ObjectRef, Role, StateSet}; use serde::{Deserialize, Serialize}; use zbus_lockstep_macros::validate; use zvariant::Type; /// The item type provided by `Cache:Add` signals #[allow(clippy::module_name_repetitions)] #[derive(Clone, Debug, Serialize, Deserialize, Type, PartialEq, Eq, Hash)] #[validate(signal: "AddAccessible")] pub struct CacheItem { /// The accessible object (within the application) (so) pub object: ObjectRefOwned, /// The application (root object(?) (so) pub app: ObjectRefOwned, /// The parent object. (so) pub parent: ObjectRefOwned, /// The accessbile index in parent. i pub index: i32, /// Child count of the accessible i pub children: i32, /// The exposed interface(s) set. as pub ifaces: InterfaceSet, /// The short localized name. s pub short_name: String, /// `ObjectRef` role. u pub role: Role, /// More detailed localized name. pub name: String, /// The states applicable to the accessible. au pub states: StateSet, } impl Default for CacheItem { fn default() -> Self { Self { object: ObjectRefOwned::from_static_str_unchecked( ":0.0", "/org/a11y/atspi/accessible/object", ), app: ObjectRefOwned::from_static_str_unchecked( ":0.0", "/org/a11y/atspi/accessible/application", ), parent: ObjectRefOwned::from_static_str_unchecked( ":0.0", "/org/a11y/atspi/accessible/parent", ), index: 0, children: 0, ifaces: InterfaceSet::empty(), short_name: String::default(), role: Role::Invalid, name: String::default(), states: StateSet::empty(), } } } /// The item type provided by `Cache:Add` signals #[allow(clippy::module_name_repetitions)] #[derive(Clone, Debug, Serialize, Deserialize, Type, PartialEq, Eq, Hash)] pub struct LegacyCacheItem { /// The accessible object (within the application) (so) pub object: ObjectRefOwned, /// The application (root object(?) (so) pub app: ObjectRefOwned, /// The parent object. (so) pub parent: ObjectRefOwned, /// List of references to the accessible's children. a(so) pub children: Vec, /// The exposed interface(s) set. as pub ifaces: InterfaceSet, /// The short localized name. s pub short_name: String, /// `ObjectRef` role. u pub role: Role, /// More detailed localized name. pub name: String, /// The states applicable to the accessible. au pub states: StateSet, } impl Default for LegacyCacheItem { fn default() -> Self { Self { object: ObjectRef::from_static_str_unchecked( ":0.0", "/org/a11y/atspi/accessible/object", ) .into(), app: ObjectRef::from_static_str_unchecked( ":0.0", "/org/a11y/atspi/accessible/application", ) .into(), parent: ObjectRef::from_static_str_unchecked( ":0.0", "/org/a11y/atspi/accessible/parent", ) .into(), children: Vec::new(), ifaces: InterfaceSet::empty(), short_name: String::default(), role: Role::Invalid, name: String::default(), states: StateSet::empty(), } } } #[cfg(test)] #[test] fn zvariant_type_signature_of_legacy_cache_item() { use std::str::FromStr; assert_eq!( *::SIGNATURE, zbus::zvariant::Signature::from_str("((so)(so)(so)a(so)assusau)").unwrap() ); } atspi-common-0.13.0/src/error.rs000064400000000000000000000132531046102023000146050ustar 00000000000000#[allow(clippy::module_name_repetitions)] #[derive(Debug)] #[non_exhaustive] /// An error type that can describe atspi and `std` and different `zbus` errors. pub enum AtspiError { /// Converting one type into another failure Conversion(&'static str), /// When testing on either variant, we might find the we are not interested in. CacheVariantMismatch, /// On specific types, if the event / message member does not match the Event's name. MemberMatch(String), /// On specific types, if the event / message member does not match the Event's name. InterfaceMatch(String), /// On specific types, if the kind (string variant) does not match the Event's kind. KindMatch(String), /// When an interface is not available. InterfaceNotAvailable(&'static str), /// To indicate a match or equality test on a signal body signature failed. SignatureMatch(String), /// When matching on an unknown interface UnknownInterface, /// No interface on event. MissingInterface, /// No member on event. MissingMember, /// No path on event. MissingPath, /// When matching on an unknown role UnknownRole(u32), /// No name on bus. MissingName, /// The signal that was encountered is unknown. UnknownSignal, /// Other errors. Owned(String), /// Null-reference error. This is used when an `ObjectRef` is expected to be non-null, but it is null. NullRef(&'static str), /// A `zbus` or `zbus::Fdo` error. variant. Zbus(String), /// A `zbus_names` error variant ZBusNames(zbus_names::Error), /// A `zbus_names` error variant Zvariant(zvariant::Error), /// Failed to parse a string into an enum variant ParseError(&'static str), /// Failed to get the ID of a path. PathConversionError(ObjectPathConversionError), /// Std i/o error variant. IO(std::io::Error), /// Failed to convert an integer into another type of integer (usually i32 -> usize). IntConversionError(std::num::TryFromIntError), /// An infallible error; this is just something to satisfy the compiler. Infallible, } impl std::error::Error for AtspiError {} impl std::fmt::Display for AtspiError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Conversion(e) => f.write_str(&format!("atspi: conversion failure: {e}")), Self::MemberMatch(e) => { f.write_str("atspi: member mismatch in conversion: ")?; e.fmt(f) } Self::InterfaceMatch(e) => { f.write_str("atspi: interface mismatch in conversion: ")?; e.fmt(f) } Self::KindMatch(e) => { f.write_str(format!("atspi: kind mismatch in conversion: {e}").as_str()) } Self::SignatureMatch(e) => { f.write_str(format!("atspi: body signature mismatch in conversion: {e:?}").as_str()) } Self::InterfaceNotAvailable(e) => { f.write_str(format!("atspi: interface not available: {e}").as_str()) } Self::UnknownInterface => f.write_str("Unknown interface."), Self::MissingInterface => f.write_str("Missing interface."), Self::MissingMember => f.write_str("Missing member."), Self::MissingPath => f.write_str("Missing path."), Self::UnknownRole(e) => { f.write_str("atspi: Unknown role: ")?; e.fmt(f) } Self::UnknownSignal => f.write_str("atspi: Unknown signal"), Self::CacheVariantMismatch => f.write_str("atspi: Cache variant mismatch"), Self::Owned(e) => { f.write_str("atspi: other error: ")?; e.fmt(f) } Self::NullRef(e) => { f.write_str("atspi: null reference: ")?; f.write_str(e) } Self::Zbus(e) => { f.write_str("ZBus Error: ")?; e.fmt(f) } Self::Zvariant(e) => { f.write_str("Zvariant error: ")?; e.fmt(f) } Self::ZBusNames(e) => { f.write_str("ZBus_names Error: ")?; e.fmt(f) } Self::ParseError(e) => f.write_str(e), Self::PathConversionError(e) => { f.write_str("ID cannot be extracted from the path: ")?; e.fmt(f) } Self::IO(e) => { f.write_str("std IO Error: ")?; e.fmt(f) } Self::IntConversionError(e) => { f.write_str("Integer conversion error: ")?; e.fmt(f) } Self::MissingName => f.write_str("Missing name for a bus."), Self::Infallible => { f.write_str("Infallible; only to trick the compiler. This should never happen.") } } } } impl From for AtspiError { fn from(_e: std::convert::Infallible) -> Self { Self::Infallible } } impl From for AtspiError { fn from(e: std::num::TryFromIntError) -> Self { Self::IntConversionError(e) } } #[cfg(feature = "zbus")] impl From for AtspiError { fn from(e: zbus::fdo::Error) -> Self { Self::Zbus(format!("{e:?}")) } } #[cfg(feature = "zbus")] impl From for AtspiError { fn from(e: zbus::Error) -> Self { Self::Zbus(format!("{e:?}")) } } impl From for AtspiError { fn from(e: zbus_names::Error) -> Self { Self::ZBusNames(e) } } impl From for AtspiError { fn from(e: zvariant::Error) -> Self { Self::Zvariant(e) } } impl From for AtspiError { fn from(e: std::io::Error) -> Self { Self::IO(e) } } impl From for AtspiError { fn from(e: ObjectPathConversionError) -> AtspiError { Self::PathConversionError(e) } } #[allow(clippy::module_name_repetitions)] #[derive(Clone, Debug)] pub enum ObjectPathConversionError { NoIdAvailable, ParseError(::Err), } impl std::fmt::Display for ObjectPathConversionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::NoIdAvailable => f.write_str("No ID available in the path."), Self::ParseError(e) => { f.write_str("Failure to parse: ")?; e.fmt(f) } } } } impl std::error::Error for ObjectPathConversionError {} atspi-common-0.13.0/src/events/cache.rs000064400000000000000000000131531046102023000160220ustar 00000000000000use crate::cache::{CacheItem, LegacyCacheItem}; #[cfg(feature = "zbus")] use crate::error::AtspiError; #[cfg(feature = "zbus")] use crate::object_ref::ObjectRef; #[cfg(feature = "zbus")] use crate::EventProperties; use crate::{ events::{DBusInterface, DBusMatchRule, DBusMember, RegistryEventString}, object_ref::ObjectRefOwned, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "zbus")] use zbus::message::{Body as DbusBody, Header}; #[cfg(feature = "zbus")] use super::{MessageConversion, MessageConversionExt}; /// Type that contains the `zbus::Message` for meta information and /// the [`crate::cache::LegacyCacheItem`] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)] pub struct LegacyAddAccessibleEvent { /// The [`ObjectRef`] the event applies to. pub item: ObjectRefOwned, /// A cache item to add to the internal cache. pub node_added: LegacyCacheItem, } impl_event_type_properties_for_event!(LegacyAddAccessibleEvent); event_test_cases!(LegacyAddAccessibleEvent, Explicit); impl_from_dbus_message!(LegacyAddAccessibleEvent, Explicit); impl_event_properties!(LegacyAddAccessibleEvent); impl_to_dbus_message!(LegacyAddAccessibleEvent); impl_member_interface_registry_string_and_match_rule_for_event!( LegacyAddAccessibleEvent, "AddAccessible", "org.a11y.atspi.Cache", "cache:add", "type='signal',interface='org.a11y.atspi.Cache',member='AddAccessible'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for LegacyAddAccessibleEvent { type Body<'msg> = LegacyCacheItem; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { Ok(Self { item: item.into(), node_added: body.deserialize_unchecked::>()? }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { self.node_added.clone() } } /// Type that contains the `zbus::Message` for meta information and /// the [`crate::cache::CacheItem`] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)] pub struct AddAccessibleEvent { /// The [`ObjectRef`] the event applies to. pub item: ObjectRefOwned, /// A cache item to add to the internal cache. pub node_added: CacheItem, } impl_event_type_properties_for_event!(AddAccessibleEvent); event_test_cases!(AddAccessibleEvent, Explicit); impl_member_interface_registry_string_and_match_rule_for_event!( AddAccessibleEvent, "AddAccessible", "org.a11y.atspi.Cache", "cache:add", "type='signal',interface='org.a11y.atspi.Cache',member='AddAccessible'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for AddAccessibleEvent { type Body<'msg> = CacheItem; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { Ok(Self { item: item.into(), node_added: body.deserialize_unchecked::>()? }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { self.node_added.clone() } } impl_msg_conversion_ext_for_target_type_with_specified_body_type!(target: AddAccessibleEvent, body: CacheItem); impl_from_dbus_message!(AddAccessibleEvent, Explicit); impl_event_properties!(AddAccessibleEvent); impl_to_dbus_message!(AddAccessibleEvent); /// `Cache::RemoveAccessible` signal event type. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)] pub struct RemoveAccessibleEvent { /// The application that emitted the signal TODO Check Me /// The [`ObjectRef`] the event applies to. pub item: ObjectRefOwned, /// The node that was removed from the application tree TODO Check Me pub node_removed: ObjectRefOwned, } impl_event_type_properties_for_event!(RemoveAccessibleEvent); event_test_cases!(RemoveAccessibleEvent, Explicit); impl_member_interface_registry_string_and_match_rule_for_event!( RemoveAccessibleEvent, "RemoveAccessible", "org.a11y.atspi.Cache", "cache:remove", "type='signal',interface='org.a11y.atspi.Cache',member='RemoveAccessible'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for RemoveAccessibleEvent { type Body<'msg> = ObjectRefOwned; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { Ok(Self { item: item.into(), node_removed: body.deserialize_unchecked::>()?, }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { self.node_removed.clone() } } #[cfg(feature = "zbus")] impl MessageConversionExt<'_, LegacyCacheItem> for LegacyAddAccessibleEvent { fn try_from_message(msg: &zbus::Message, hdr: &Header) -> Result { >::validate_interface(hdr)?; >::validate_member(hdr)?; >::validate_body( msg, )?; ::from_message_unchecked(msg, hdr) } } impl_msg_conversion_ext_for_target_type_with_specified_body_type!(target: RemoveAccessibleEvent, body: ObjectRefOwned); impl_from_dbus_message!(RemoveAccessibleEvent, Explicit); impl_event_properties!(RemoveAccessibleEvent); impl_to_dbus_message!(RemoveAccessibleEvent); atspi-common-0.13.0/src/events/document.rs000064400000000000000000000143671046102023000166050ustar 00000000000000use crate::events::{DBusInterface, DBusMatchRule, DBusMember, RegistryEventString}; use crate::object_ref::ObjectRefOwned; #[cfg(feature = "zbus")] use crate::EventProperties; #[cfg(feature = "zbus")] use crate::AtspiError; #[cfg(feature = "zbus")] use zbus::message::Header; /// An event triggered by the completion of a document load action. /// For example: a web page has finished loading its initial payload, or /// `LibreOffice` has loaded a document from disk. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct LoadCompleteEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } /// An event triggered by a reloading of a document. /// For example: pressing F5, or `Control + r` will reload a page in a web browser. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ReloadEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } /// An event triggered by the cancelling of a document load. /// For example: during the loading of a large web page, a user may press `Escape` to stop loading the page. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct LoadStoppedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ContentChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct AttributesChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } /// The focused page has changed. /// /// This event is usually sent only by document readers, signaling /// that the _physical page equivalent is now different. /// This event does not encode _which_ page is the new one, only that a new page is now the primary /// one. /// /// See `atspi_proxies::document::DocumentProxy::current_page_number` to actively find the /// page number. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct PageChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_member_interface_registry_string_and_match_rule_for_event!( LoadCompleteEvent, "LoadComplete", "org.a11y.atspi.Event.Document", "document:load-complete", "type='signal',interface='org.a11y.atspi.Event.Document',member='LoadComplete'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ReloadEvent, "Reload", "org.a11y.atspi.Event.Document", "document:reload", "type='signal',interface='org.a11y.atspi.Event.Document',member='LoadStopped'" ); impl_member_interface_registry_string_and_match_rule_for_event!( LoadStoppedEvent, "LoadStopped", "org.a11y.atspi.Event.Document", "document:load-stopped", "type='signal',interface='org.a11y.atspi.Event.Document',member='LoadStopped'" ); // TODO confirm registry event string, not found in grep at at-spi2-core impl_member_interface_registry_string_and_match_rule_for_event!( ContentChangedEvent, "ContentChanged", "org.a11y.atspi.Event.Document", "document:content-changed", "type='signal',interface='org.a11y.atspi.Event.Document',member='ContentChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( AttributesChangedEvent, "AttributesChanged", "org.a11y.atspi.Event.Document", "document:attributes-changed", "type='signal',interface='org.a11y.atspi.Event.Document',member='AttributesChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( PageChangedEvent, "PageChanged", "org.a11y.atspi.Event.Document", "document:page-changed", "type='signal',interface='org.a11y.atspi.Event.Document',member='PageChanged'" ); impl_event_type_properties_for_event!(LoadCompleteEvent); event_test_cases!(LoadCompleteEvent); impl_to_dbus_message!(LoadCompleteEvent); impl_from_dbus_message!(LoadCompleteEvent); impl_event_properties!(LoadCompleteEvent); impl_from_object_ref!(LoadCompleteEvent); impl_event_type_properties_for_event!(ReloadEvent); event_test_cases!(ReloadEvent); impl_to_dbus_message!(ReloadEvent); impl_from_dbus_message!(ReloadEvent); impl_event_properties!(ReloadEvent); impl_from_object_ref!(ReloadEvent); impl_event_type_properties_for_event!(LoadStoppedEvent); event_test_cases!(LoadStoppedEvent); impl_to_dbus_message!(LoadStoppedEvent); impl_from_dbus_message!(LoadStoppedEvent); impl_event_properties!(LoadStoppedEvent); impl_from_object_ref!(LoadStoppedEvent); impl_event_type_properties_for_event!(ContentChangedEvent); event_test_cases!(ContentChangedEvent); impl_to_dbus_message!(ContentChangedEvent); impl_from_dbus_message!(ContentChangedEvent); impl_event_properties!(ContentChangedEvent); impl_from_object_ref!(ContentChangedEvent); impl_event_type_properties_for_event!(AttributesChangedEvent); event_test_cases!(AttributesChangedEvent); impl_to_dbus_message!(AttributesChangedEvent); impl_from_dbus_message!(AttributesChangedEvent); impl_event_properties!(AttributesChangedEvent); impl_from_object_ref!(AttributesChangedEvent); impl_event_type_properties_for_event!(PageChangedEvent); event_test_cases!(PageChangedEvent); impl_to_dbus_message!(PageChangedEvent); impl_from_dbus_message!(PageChangedEvent); impl_event_properties!(PageChangedEvent); impl_from_object_ref!(PageChangedEvent); impl_msg_conversion_ext_for_target_type!(LoadCompleteEvent); impl_msg_conversion_ext_for_target_type!(ReloadEvent); impl_msg_conversion_ext_for_target_type!(LoadStoppedEvent); impl_msg_conversion_ext_for_target_type!(ContentChangedEvent); impl_msg_conversion_ext_for_target_type!(AttributesChangedEvent); impl_msg_conversion_ext_for_target_type!(PageChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(LoadCompleteEvent); impl_msg_conversion_for_types_built_from_object_ref!(ReloadEvent); impl_msg_conversion_for_types_built_from_object_ref!(LoadStoppedEvent); impl_msg_conversion_for_types_built_from_object_ref!(ContentChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(AttributesChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(PageChangedEvent); atspi-common-0.13.0/src/events/event_body.rs000064400000000000000000000743051046102023000171230ustar 00000000000000use crate::AtspiError; use serde::{ ser::{SerializeMap, SerializeTuple}, Deserialize, Serialize, }; use zbus_lockstep_macros::validate; use zvariant::{ObjectPath, OwnedValue, Type, Value}; /// Event body as used exclusively by 'Qt' toolkit. /// /// Signature: "siiv(so)" #[derive(Debug, Serialize, Deserialize, PartialEq, Type)] pub struct EventBodyQtOwned { /// kind variant, used for specifying an event triple "object:state-changed:focused", /// the "focus" part of this event is what is contained within the kind. #[serde(rename = "type")] pub kind: String, /// Generic detail1 value described by AT-SPI. pub detail1: i32, /// Generic detail2 value described by AT-SPI. pub detail2: i32, /// Generic `any_data` value described by AT-SPI. /// This can be any type. pub any_data: OwnedValue, /// Not in use. /// See: [`QtProperties`]. #[serde(skip_deserializing)] pub(crate) properties: QtProperties, } impl Clone for EventBodyQtOwned { /// # Safety /// /// This implementation of [`Clone`] *can panic!* although chances are slim. /// /// If the following conditions are met: /// 1. the `any_data` field contains an [`std::os::fd::OwnedFd`] type, and /// 2. the maximum number of open files for the process is exceeded. /// /// Then this function panic. /// None of the types in [`crate::events`] use [`std::os::fd::OwnedFd`]. /// Events on the AT-SPI bus *could, theoretically* send a file descriptor, but nothing in the current /// specification describes that. /// See [`zvariant::Value::try_clone`] for more information. fn clone(&self) -> Self { let cloned_any_data = self.any_data.try_clone().unwrap_or_else(|err| { panic!("Failure cloning 'any_data' field: {err:?}"); }); Self { kind: self.kind.clone(), detail1: self.detail1, detail2: self.detail2, any_data: cloned_any_data, properties: QtProperties, } } } /// Unit struct placeholder for `EventBodyQtOwned.properties` /// /// AT-SPI2 never reads or writes to `EventBodyQT.properties`. /// `QtProperties` has the appropriate implementations for `Serialize` and `Deserialize` /// to make it serialize as an a valid tuple and valid bytes deserialize as placeholder. #[derive(Debug, Copy, Clone, Deserialize, Type, Default, PartialEq)] #[zvariant(signature = "(so)")] pub(crate) struct QtProperties; impl Serialize for QtProperties { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { let mut tup = serializer.serialize_tuple(2)?; tup.serialize_element(":0.0")?; tup.serialize_element(&ObjectPath::from_static_str_unchecked("/"))?; tup.end() } } impl Default for EventBodyQtOwned { fn default() -> Self { Self { kind: String::new(), detail1: 0, detail2: 0, any_data: 0_u32.into(), properties: QtProperties, } } } /// Unit struct placeholder for `EventBody.properties` /// /// AT-SPI2 never reads or writes to `EventBody.properties`. /// `Properties` has the appropriate implementations for `Serialize` and `Deserialize` /// to make it serialize as an a valid dictionary and valid bytes deserialize as placeholder. #[derive(Debug, Copy, Clone, Type, Default, Deserialize, PartialEq)] #[zvariant(signature = "a{sv}")] pub(crate) struct Properties; impl Serialize for Properties { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { serializer.serialize_map(Some(0))?.end() } } /// AT-SPI2 protocol native event body type. /// /// All of the various signals in the AT-SPI2 protocol share this shape. /// Most toolkits and implementors emit this type, except for `Qt`, which has has its /// own type: [`EventBodyQtOwned`]. /// /// Signature `(siiva{sv})`, #[validate(signal: "PropertyChange")] #[derive(Debug, Serialize, Deserialize, PartialEq, Type)] pub struct EventBodyOwned { /// kind variant, used for specifying an event triple "object:state-changed:focused", /// the "focus" part of this event is what is contained within the kind. #[serde(rename = "type")] pub kind: String, /// Generic detail1 value described by AT-SPI. pub detail1: i32, /// Generic detail2 value described by AT-SPI. pub detail2: i32, /// Generic `any_data` value described by AT-SPI. /// This can be any type. /// pub any_data: OwnedValue, /// Not in use. /// See: [`Properties`]. pub(crate) properties: Properties, } impl Default for EventBodyOwned { fn default() -> Self { Self { kind: String::new(), detail1: 0, detail2: 0, any_data: 0_u32.into(), properties: Properties, } } } impl Clone for EventBodyOwned { /// # Safety /// /// This implementation of [`Clone`] *can panic!* although chances are slim. /// /// If the following conditions are met: /// 1. the `any_data` field contains an [`std::os::fd::OwnedFd`] type, and /// 2. the maximum number of open files for the process is exceeded. /// /// Then this function panic. /// None of the types in [`crate::events`] use [`std::os::fd::OwnedFd`]. /// Events on the AT-SPI bus *could, theoretically* send a file descriptor, but nothing in the current /// specification describes that. /// See [`zvariant::Value::try_clone`] for more information. fn clone(&self) -> Self { let cloned_any_data = self.any_data.try_clone().unwrap_or_else(|err| { panic!("Failure cloning 'any_data' field: {err:?}"); }); Self { kind: self.kind.clone(), detail1: self.detail1, detail2: self.detail2, any_data: cloned_any_data, properties: Properties, } } } #[derive(Debug, Serialize, Deserialize, PartialEq, Type)] pub struct EventBodyBorrowed<'a> { /// kind variant, used for specifying an event triple "object:state-changed:focused", /// the "focus" part of this event is what is contained within the kind. #[serde(rename = "type")] #[serde(borrow)] pub kind: &'a str, /// Generic detail1 value described by AT-SPI. pub detail1: i32, /// Generic detail2 value described by AT-SPI. pub detail2: i32, /// Generic `any_data` value described by AT-SPI. /// This can be any type. #[serde(borrow)] pub any_data: Value<'a>, /// Not in use. /// See: [`Properties`]. #[serde(skip_deserializing)] pub(crate) properties: Properties, } impl Default for EventBodyBorrowed<'_> { fn default() -> Self { Self { kind: "", detail1: 0, detail2: 0, any_data: Value::new(0_u32), properties: Properties, } } } impl EventBodyBorrowed<'_> { /// Convert this borrowed event body to an owned event body. /// /// # Errors /// /// This will error if the following conditions are met: /// 1. the `any_data` field contains an [`std::os::fd::OwnedFd`] type, and /// 2. the maximum number of open files for the process is exceeded. /// /// Chances are slim because none of the types in [`crate::events`] use [`std::os::fd::OwnedFd`]. /// See [`zvariant::Value::try_clone`] for more information. pub fn to_fully_owned(&self) -> Result { let owned_any_data = self.any_data.try_to_owned()?; Ok(EventBodyOwned { kind: self.kind.into(), detail1: self.detail1, detail2: self.detail2, any_data: owned_any_data, properties: Properties, }) } } impl Clone for EventBodyBorrowed<'_> { /// # Safety /// /// This implementation of [`Clone`] *can panic!* although chances are slim. /// /// If the following conditions are met: /// 1. the `any_data` field contains an [`std::os::fd::OwnedFd`] type, and /// 2. the maximum number of open files for the process is exceeded. /// /// Then this function panic. /// None of the types in [`crate::events`] use [`std::os::fd::OwnedFd`]. /// Events on the AT-SPI bus *could, theoretically* send a file descriptor, but nothing in the current /// specification describes that. /// See [`zvariant::Value::try_clone`] for more information. fn clone(&self) -> Self { let cloned_any_data = self.any_data.try_clone().unwrap_or_else(|err| { panic!("Failure cloning 'any_data' field: {err:?}"); }); Self { kind: self.kind, detail1: self.detail1, detail2: self.detail2, any_data: cloned_any_data, properties: Properties, } } } #[derive(Debug, Type, Deserialize, PartialEq)] pub struct EventBodyQtBorrowed<'m> { /// kind variant, used for specifying an event triple "object:state-changed:focused", /// the "focus" part of this event is what is contained within the kind. #[serde(rename = "type")] pub kind: &'m str, /// Generic detail1 value described by AT-SPI. pub detail1: i32, /// Generic detail2 value described by AT-SPI. pub detail2: i32, /// Generic `any_data` value described by AT-SPI. /// This can be any type. #[serde(borrow)] pub any_data: Value<'m>, /// Not in use. /// See: [`QtProperties`]. #[serde(skip_deserializing)] pub(crate) properties: QtProperties, } impl Default for EventBodyQtBorrowed<'_> { fn default() -> Self { Self { kind: "", detail1: 0, detail2: 0, any_data: Value::new(0_u32), properties: QtProperties, } } } impl Clone for EventBodyQtBorrowed<'_> { /// # Safety /// /// This implementation of [`Clone`] *can panic!* although chances are slim. /// /// If the following conditions are met: /// 1. the `any_data` field contains an [`std::os::fd::OwnedFd`] type, and /// 2. the maximum number of open files for the process is exceeded. /// /// Then this function panics. /// None of the types in [`crate::events`] use [`std::os::fd::OwnedFd`]. /// Events on the AT-SPI bus *could, theoretically* send a file descriptor, but nothing in the current /// specification describes that. /// See [`zvariant::Value::try_clone`] for more information. fn clone(&self) -> Self { let cloned_any_data = self.any_data.try_clone().unwrap_or_else(|err| { panic!("Failure cloning 'any_data' field: {err:?}"); }); Self { kind: self.kind, detail1: self.detail1, detail2: self.detail2, any_data: cloned_any_data, properties: QtProperties, } } } impl EventBodyQtBorrowed<'_> { /// Convert partially borrowed Qt event body to an owned event body. /// /// # Errors /// /// This will error if the following conditions are met: /// 1. the `any_data` field contains an [`std::os::fd::OwnedFd`] type, and /// 2. the maximum number of open files for the process is exceeded. pub fn try_to_owned(&self) -> Result { let any_data = self.any_data.try_to_owned()?; Ok(EventBodyQtOwned { kind: self.kind.to_owned(), detail1: self.detail1, detail2: self.detail2, any_data, properties: self.properties, }) } } impl<'de> From> for EventBodyBorrowed<'de> { fn from(borrow: EventBodyQtBorrowed<'de>) -> Self { let EventBodyQtBorrowed { kind, detail1, detail2, any_data, properties: _ } = borrow; Self { kind, detail1, detail2, any_data, properties: Properties } } } impl From for EventBodyOwned { fn from(body: EventBodyQtOwned) -> Self { Self { kind: body.kind, detail1: body.detail1, detail2: body.detail2, any_data: body.any_data, properties: Properties, } } } /// Common event body that can be either owned or borrowed. /// /// This is useful for APIs that can return either owned or borrowed event bodies. /// Having this type allows to be generic over the event body type. #[derive(Debug, Clone, PartialEq)] pub enum EventBody<'a> { Owned(EventBodyOwned), Borrowed(EventBodyBorrowed<'a>), } impl Default for EventBody<'_> { fn default() -> Self { Self::Borrowed(EventBodyBorrowed::default()) } } impl<'a> EventBody<'_> { /// Non-consuming conversion to an owned event body. /// /// Does cloning. /// /// # Errors /// The borrowed variant will error if the following conditions are met: /// 1. the `any_data` field contains an [`std::os::fd::OwnedFd`] type, and /// 2. the maximum number of open files for the process is exceeded. pub fn as_owned(&self) -> Result { match self { Self::Owned(owned) => Ok(owned.clone()), Self::Borrowed(borrowed) => borrowed.to_fully_owned(), } } /// Consuming conversion to an owned event body. /// /// Does cloning. /// /// # Errors /// The borrowed variant will error if the following conditions are met: /// 1. the `any_data` field contains an [`std::os::fd::OwnedFd`] type, and /// 2. the maximum number of open files for the process is exceeded. pub fn into_owned(self) -> Result { match self { Self::Owned(owned) => Ok(owned), Self::Borrowed(borrowed) => borrowed.to_fully_owned(), } } /// The `kind` field as `&str`. /// /// With both variants, this method returns a reference to the `kind` field. #[must_use] pub fn kind(&'a self) -> &'a str { match self { Self::Owned(owned) => owned.kind.as_str(), Self::Borrowed(borrowed) => borrowed.kind, } } /// Take or convert the `kind` field as `String`. /// /// With the owned variant, this method takes the `kind` field and replaces it with an empty string. /// With the borrowed variant, this method clones and allocates the `kind` field. pub fn take_kind(&mut self) -> String { match self { Self::Owned(owned) => std::mem::take(&mut owned.kind), Self::Borrowed(borrowed) => borrowed.kind.to_owned(), } } /// The `detail1` field. #[must_use] pub fn detail1(&self) -> i32 { match self { Self::Owned(owned) => owned.detail1, Self::Borrowed(borrowed) => borrowed.detail1, } } /// The `detail2` field. #[must_use] pub fn detail2(&self) -> i32 { match self { Self::Owned(owned) => owned.detail2, Self::Borrowed(borrowed) => borrowed.detail2, } } /// The `any_data` field as `&Value`. /// With both variants, this method returns a reference to the `any_data` field. #[must_use] pub fn any_data(&'a self) -> &'a Value<'a> { match self { Self::Owned(owned) => &owned.any_data, Self::Borrowed(borrowed) => &borrowed.any_data, } } /// Take or convert the `any_data` field as `OwnedValue`. /// With the owned variant, this method takes the `any_data` field and replaces it with a default value. /// As `Value` does not have a default value, we will replace with `0_u32`, a nbon-allocating value. /// /// With the borrowed variant, this method clones and allocates the `any_data` field. /// /// # Panics /// This method will panic if the `any_data` field contains an [`std::os::fd::OwnedFd`] type, and /// the maximum number of open files for the process is exceeded. /// /// None of the types in [`crate::events`] use [`std::os::fd::OwnedFd`]. /// Events on the AT-SPI bus *could, theoretically* send a file descriptor, but nothing in the current /// specification describes that. pub fn take_any_data(&mut self) -> OwnedValue { match self { Self::Owned(owned) => std::mem::replace(&mut owned.any_data, 0_u32.into()), Self::Borrowed(borrowed) => borrowed.any_data.try_to_owned().expect("cloning 'any_data' field should not fail because we do not expect it to hold an fd"), } } } impl Type for EventBody<'_> { const SIGNATURE: &'static zvariant::Signature = EventBodyOwned::SIGNATURE; } impl<'de> Deserialize<'de> for EventBody<'de> { fn deserialize(deserializer: D) -> Result, D::Error> where D: serde::de::Deserializer<'de>, { let borrowed = EventBodyBorrowed::deserialize(deserializer)?; Ok(borrowed.into()) } } impl Serialize for EventBody<'_> { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { match self { EventBody::Owned(owned) => owned.serialize(serializer), EventBody::Borrowed(borrowed) => borrowed.serialize(serializer), } } } impl From for EventBody<'_> { fn from(owned: EventBodyOwned) -> Self { EventBody::Owned(owned) } } impl<'b> From> for EventBody<'b> { fn from(borrowed: EventBodyBorrowed<'b>) -> Self { EventBody::Borrowed(borrowed) } } impl From for EventBody<'_> { fn from(qt_owned: EventBodyQtOwned) -> Self { EventBody::Owned(qt_owned.into()) } } impl<'a> From> for EventBody<'a> { fn from(qt_borrowed: EventBodyQtBorrowed<'a>) -> Self { EventBody::Borrowed(qt_borrowed.into()) } } impl From for EventBodyQtOwned { fn from(owned: EventBodyOwned) -> Self { Self { kind: owned.kind, detail1: owned.detail1, detail2: owned.detail2, any_data: owned.any_data, properties: QtProperties, } } } impl<'a> From> for EventBodyQtOwned { fn from(borrowed: EventBodyBorrowed<'a>) -> Self { Self { kind: borrowed.kind.to_owned(), detail1: borrowed.detail1, detail2: borrowed.detail2, any_data: borrowed .any_data .try_to_owned() .expect("converting borrowed to owned should not fail"), properties: QtProperties, } } } impl From> for EventBodyQtOwned { fn from(event: EventBody) -> Self { match event { EventBody::Owned(owned) => owned.into(), EventBody::Borrowed(borrowed) => borrowed.into(), } } } impl PartialEq for EventBodyQtOwned { fn eq(&self, other: &EventBodyOwned) -> bool { self.kind == other.kind && self.detail1 == other.detail1 && self.detail2 == other.detail2 && self.any_data == other.any_data } } impl PartialEq for EventBodyOwned { fn eq(&self, other: &EventBodyQtOwned) -> bool { self.kind == other.kind && self.detail1 == other.detail1 && self.detail2 == other.detail2 && self.any_data == other.any_data } } impl PartialEq> for EventBodyQtBorrowed<'_> { fn eq(&self, other: &EventBodyBorrowed<'_>) -> bool { self.kind == other.kind && self.detail1 == other.detail1 && self.detail2 == other.detail2 && self.any_data == other.any_data } } impl PartialEq> for EventBodyBorrowed<'_> { fn eq(&self, other: &EventBodyQtBorrowed<'_>) -> bool { self.kind == other.kind && self.detail1 == other.detail1 && self.detail2 == other.detail2 && self.any_data == other.any_data } } #[cfg(test)] mod test { use super::*; use crate::ObjectRef; use std::collections::HashMap; use zvariant::{serialized::Context, LE}; use zvariant::{Array, ObjectPath, Value}; #[test] fn owned_event_body_clone() { let event = EventBodyOwned::default(); let cloned = event.clone(); assert_eq!(event, cloned); } #[test] fn event_body_qt_clone() { let event = EventBodyQtOwned::default(); let cloned = event.clone(); assert_eq!(event, cloned); } #[test] fn event_body_borrowed_clone() { let event = EventBodyBorrowed::default(); let cloned = event.clone(); assert_eq!(event, cloned); } #[test] fn event_body_qt_borrowed_clone() { let event = EventBodyQtBorrowed::default(); let cloned = event.clone(); assert_eq!(event, cloned); } #[test] fn owned_event_body_default() { let event = EventBodyOwned::default(); assert_eq!(event.kind, ""); assert_eq!(event.detail1, 0); assert_eq!(event.detail2, 0); assert_eq!(event.any_data, 0_u32.into()); } #[test] fn qt_event_body_default() { let event = EventBodyQtOwned::default(); assert_eq!(event.kind, ""); assert_eq!(event.detail1, 0); assert_eq!(event.detail2, 0); assert_eq!(event.any_data, 0_u32.into()); assert_eq!(event.properties, QtProperties); } #[test] fn event_body_borrowed_default() { let event = EventBodyBorrowed::default(); assert_eq!(event.kind, ""); assert_eq!(event.detail1, 0); assert_eq!(event.detail2, 0); assert_eq!(event.any_data, Value::new(0_u32)); } #[test] fn qt_event_body_borrowed_default() { let event = EventBodyQtBorrowed::default(); assert_eq!(event.kind, ""); assert_eq!(event.detail1, 0); assert_eq!(event.detail2, 0); assert_eq!(event.any_data, Value::new(0_u32)); assert_eq!(event.properties, QtProperties); } #[test] fn event_body_default() { let event = EventBody::default(); assert_eq!(event, EventBody::Borrowed(EventBodyBorrowed::default())); } #[test] fn qt_to_owned() { let qt = EventBodyQtOwned::default(); let owned: EventBodyOwned = EventBodyQtOwned::default().into(); assert_eq!(owned, qt); } #[test] fn borrowed_to_qt() { let borrowed: EventBodyBorrowed = EventBodyQtBorrowed::default().into(); assert_eq!(borrowed, EventBodyBorrowed::default()); } #[test] fn event_body_deserialize_as_owned() { let event = EventBodyOwned::default(); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::(ctxt, &event).unwrap(); let (deserialized, _) = bytes.deserialize::().unwrap(); assert_eq!(deserialized, event); } #[test] fn owned_event_body_deserialize_as_borrowed() { let event = EventBodyOwned::default(); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::(ctxt, &event).unwrap(); let (deserialized, _) = bytes.deserialize::().unwrap(); assert_eq!(deserialized, EventBodyBorrowed::default()); assert_eq!(deserialized.kind, event.kind.as_str()); assert_eq!(deserialized.detail1, event.detail1); assert_eq!(deserialized.detail2, event.detail2); assert_eq!(deserialized.any_data, *event.any_data); } #[test] fn qt_owned_event_body_deserialize_as_borrowed() { let event = EventBodyQtOwned::default(); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::(ctxt, &event).unwrap(); let (deserialized, _) = bytes.deserialize::().unwrap(); assert_eq!(deserialized, EventBodyBorrowed::default()); assert_eq!(deserialized.kind, event.kind.as_str()); assert_eq!(deserialized.detail1, event.detail1); assert_eq!(deserialized.detail2, event.detail2); assert_eq!(deserialized.any_data, *event.any_data); } #[test] fn event_body_default_deserialize_as_event_body() { let event = EventBody::default(); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::(ctxt, &event).unwrap(); let (deserialized, _) = bytes.deserialize::().unwrap(); assert_eq!(deserialized, event); } #[test] fn event_body_owned_default_deserialize_as_event_body() { let event = EventBodyOwned::default(); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::(ctxt, &event).unwrap(); let (deserialized, _) = bytes.deserialize::().unwrap(); assert_eq!(deserialized.kind(), event.kind.as_str()); assert_eq!(deserialized.detail1(), event.detail1); assert_eq!(deserialized.detail2(), event.detail2); assert_eq!(*deserialized.any_data(), *event.any_data); } #[test] fn complex_body_deserialize_as_event_body() { let boots = Array::from(vec!["these", "boots", "are", "made", "for", "walking"]); let boots = Value::from(boots); let event = ( "object:state-changed:focused", 1, 2, boots.clone(), HashMap::from([("key", Value::from(55_u32)), ("key2", Value::from(56_u32))]), ); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::<(&str, i32, i32, Value, HashMap<&str, Value>)>(ctxt, &event) .unwrap(); let (deserialized, _) = bytes.deserialize::().unwrap(); assert_eq!(deserialized.kind(), "object:state-changed:focused"); assert_eq!(deserialized.detail1(), 1); assert_eq!(deserialized.detail2(), 2); assert_eq!(*deserialized.any_data(), boots); } #[test] fn complex_body_deserialize_as_owned_event_body() { let boots = Array::from(vec!["these", "boots", "are", "made", "for", "walking"]); let boots = Value::from(boots); let event = ( "object:state-changed:focused", 1, 2, boots.clone(), HashMap::from([("key", Value::from(55_u32)), ("key2", Value::from(56_u32))]), ); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::<(&str, i32, i32, Value, HashMap<&str, Value>)>(ctxt, &event) .unwrap(); let (deserialized, _) = bytes.deserialize::().unwrap(); assert_eq!(deserialized.kind, "object:state-changed:focused"); assert_eq!(deserialized.detail1, 1); assert_eq!(deserialized.detail2, 2); assert_eq!(*deserialized.any_data, boots); } #[test] fn complex_qt_body_as_bytes_deserialize_as_event_body() { let boots = Array::from(vec!["these", "boots", "are", "made", "for", "walking"]); let boots = Value::from(boots); let event = ( "and that is what they'll do", 1, 2, boots.clone(), (":0.0", ObjectPath::from_static_str_unchecked("/")), ); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::<(&str, i32, i32, Value, (&str, ObjectPath))>(ctxt, &event) .unwrap(); let (deserialized, _) = bytes.deserialize::().unwrap(); assert_eq!(deserialized.kind(), "and that is what they'll do"); assert_eq!(deserialized.detail1(), 1); assert_eq!(deserialized.detail2(), 2); assert_eq!(*deserialized.any_data(), boots); } #[test] fn complex_qt_body_as_message_deserialize_as_event_body() { let boots = Array::from(vec!["these", "boots", "are", "made", "for", "walking"]); let boots = Value::from(boots); let event = ( "and that is what they'll do", 1, 2, boots.clone(), (":0.0", ObjectPath::from_static_str_unchecked("/")), ); let msg = zbus::Message::signal("/", "org.a11y.atspi.Object", "StateChange") .unwrap() .build(&event) .unwrap(); let body = msg.body(); let deserialized = body.deserialize_unchecked::().unwrap(); assert_eq!(deserialized.kind(), "and that is what they'll do"); assert_eq!(deserialized.detail1(), 1); assert_eq!(deserialized.detail2(), 2); assert_eq!(*deserialized.any_data(), boots); } #[test] fn illegal_body_as_bytes_deserialize_as_event_body() { let boots = Array::from(vec!["these", "boots", "are", "made", "for", "walking"]); let boots = Value::from(boots); let event = ( "and that is what they'll do", 1, 2, 4_u32, boots.clone(), (":0.0", ObjectPath::from_static_str_unchecked("/")), ); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::<(&str, i32, i32, u32, Value<'_>, (&str, ObjectPath<'_>))>( ctxt, &event, ) .unwrap(); assert!(bytes.deserialize::().is_err()); } #[test] fn complex_body_deserialize_as_borrowed_event_body() { let boots = Array::from(vec!["these", "boots", "are", "made", "for", "walking"]); let boots = Value::from(boots); let event = ( "object:state-changed:focused", 1, 2, boots.clone(), HashMap::from([("key", Value::from(55_u32)), ("key2", Value::from(56_u32))]), ); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::<(&str, i32, i32, Value, HashMap<&str, Value>)>(ctxt, &event) .unwrap(); let (deserialized, _) = bytes.deserialize::().unwrap(); assert_eq!(deserialized.kind, "object:state-changed:focused"); assert_eq!(deserialized.detail1, 1); assert_eq!(deserialized.detail2, 2); assert_eq!(deserialized.any_data, boots); } #[test] fn deserialize_message_from_complex_message_data() { let boots = Array::from(vec!["these", "boots", "are", "made", "for", "walking"]); let boots = Value::from(boots); let body = ( "object:state-changed:focused", 1, 2, boots.clone(), HashMap::from([("key", Value::from(55_u32)), ("key2", Value::from(56_u32))]), ); let message = zbus::Message::signal("/", "org.a11y.atspi.Object", "StateChange") .unwrap() .build(&body) .unwrap(); let msg_body = message.body(); let deserialized = msg_body.deserialize::().unwrap(); assert_eq!(deserialized.kind, "object:state-changed:focused"); assert_eq!(deserialized.detail1, 1); assert_eq!(deserialized.detail2, 2); assert_eq!(*deserialized.any_data, boots); } #[test] fn simple_data_deserialization() { let body = "hello"; let message = zbus::Message::signal("/bus/driver/zeenix", "org.Zbus", "TicketCheck") .unwrap() .build(&body) .unwrap(); let msg_body = message.body(); let deserialized = msg_body.deserialize::<&str>().unwrap(); assert_eq!(deserialized, body); } #[test] fn test_valid_hashmap_of_string_value_deserializes_as_properties() { let val = Value::from(0_u32); let key = "test"; let map = HashMap::from([(key, val)]); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::>(ctxt, &map).unwrap(); let (properties, _) = bytes.deserialize::().unwrap(); assert_eq!(properties, Properties); } #[test] fn test_object_ref_deserializes_as_qt_properties() { let object_ref = ObjectRef::default(); let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::(ctxt, &object_ref).unwrap(); let (qt_props, _) = bytes.deserialize::().unwrap(); assert_eq!(qt_props, QtProperties); } #[test] fn test_properties_serializes_as_valid_hashmap() { let properties = Properties; let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::(ctxt, &properties).unwrap(); let (map, _) = bytes.deserialize::>().unwrap(); assert_eq!(map, HashMap::new()); } #[test] fn test_qt_properties_serializes_as_valid_string_objpath_tuple() { let qt_properties = QtProperties; let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::(ctxt, &qt_properties).unwrap(); let (tuple, _) = bytes.deserialize::<(&str, ObjectPath)>().unwrap(); assert_eq!(tuple, (":0.0", ObjectPath::from_static_str_unchecked("/"))); } #[test] fn test_qt_properties_serializes_as_valid_object_ref() { let qt_properties = QtProperties; let ctxt = Context::new_dbus(LE, 0); let bytes = zvariant::to_bytes::(ctxt, &qt_properties).unwrap(); let (objectref, _) = bytes.deserialize::().unwrap(); assert_eq!(objectref.name().unwrap().as_str(), ":0.0"); assert_eq!(objectref.path(), &ObjectPath::from_static_str_unchecked("/")); } #[cfg(test)] mod signatures { use crate::events::EventBodyQtOwned; use zvariant::{signature::Fields, Signature, Type}; #[test] fn test_event_body_signature_equals_borrowed_event_body_signature() { use super::*; use zvariant::Type; let borrowed = EventBodyBorrowed::SIGNATURE; let owned = EventBodyOwned::SIGNATURE; assert_eq!(borrowed, owned); } // We have no definition of the Qt event body, so we can't compare the type // to a signature definition in XML. // That is why we keep a copy here. const QSPI_EVENT_SIGNATURE: &Signature = &Signature::static_structure(&[ &Signature::Str, &Signature::I32, &Signature::I32, &Signature::Variant, &Signature::Structure(Fields::Static { fields: &[&Signature::Str, &Signature::ObjectPath], }), ]); #[test] fn check_event_body_qt_signature() { assert_eq!(::SIGNATURE, QSPI_EVENT_SIGNATURE); } } } atspi-common-0.13.0/src/events/event_wrappers.rs000064400000000000000000002231131046102023000200220ustar 00000000000000use crate::events::registry::socket::AvailableEvent; use crate::events::registry::{EventListenerDeregisteredEvent, EventListenerRegisteredEvent}; #[cfg(feature = "zbus")] use crate::events::traits::{EventWrapperMessageConversion, TryFromMessage}; #[cfg(feature = "zbus")] use crate::events::MessageConversion; use crate::{ error::AtspiError, events::{ cache::{AddAccessibleEvent, LegacyAddAccessibleEvent, RemoveAccessibleEvent}, document::{ AttributesChangedEvent as DocumentAttributesChangedEvent, ContentChangedEvent, LoadCompleteEvent, LoadStoppedEvent, PageChangedEvent, ReloadEvent, }, focus::FocusEvent, keyboard::ModifiersEvent, mouse::{AbsEvent, ButtonEvent, RelEvent}, object::{ ActiveDescendantChangedEvent, AnnouncementEvent, AttributesChangedEvent as ObjectAttributesChangedEvent, BoundsChangedEvent, ChildrenChangedEvent, ColumnDeletedEvent, ColumnInsertedEvent, ColumnReorderedEvent, LinkSelectedEvent, ModelChangedEvent, PropertyChangeEvent as ObjectPropertyChangeEvent, RowDeletedEvent, RowInsertedEvent, RowReorderedEvent, SelectionChangedEvent, StateChangedEvent, TextAttributesChangedEvent, TextBoundsChangedEvent, TextCaretMovedEvent, TextChangedEvent, TextSelectionChangedEvent, VisibleDataChangedEvent, }, terminal::{ ApplicationChangedEvent, CharWidthChangedEvent, ColumnCountChangedEvent, LineChangedEvent, LineCountChangedEvent, }, window::{ ActivateEvent, CloseEvent, CreateEvent, DeactivateEvent, DesktopCreateEvent, DesktopDestroyEvent, DestroyEvent, LowerEvent, MaximizeEvent, MinimizeEvent, MoveEvent, PropertyChangeEvent as WindowPropertyChangeEvent, RaiseEvent, ReparentEvent, ResizeEvent, RestoreEvent, RestyleEvent, ShadeEvent, UUshadeEvent, }, DBusInterface, DBusMatchRule, EventTypeProperties, RegistryEventString, }, EventProperties, }; #[cfg(feature = "zbus")] use crate::{events::DBusMember, CacheItem, LegacyCacheItem}; use serde::{Deserialize, Serialize}; #[cfg(feature = "zbus")] use zbus::message::Header; use zbus_names::UniqueName; use zvariant::ObjectPath; #[cfg(feature = "zbus")] use zvariant::Type; impl_from_user_facing_event_for_interface_event_enum!( EventListenerRegisteredEvent, EventListenerEvents, EventListenerEvents::Registered ); impl_from_user_facing_type_for_event_enum!(EventListenerRegisteredEvent, Event::Listener); impl_try_from_event_for_user_facing_type!( EventListenerRegisteredEvent, EventListenerEvents::Registered, Event::Listener ); impl_from_user_facing_event_for_interface_event_enum!( EventListenerDeregisteredEvent, EventListenerEvents, EventListenerEvents::Deregistered ); impl_from_user_facing_type_for_event_enum!(EventListenerDeregisteredEvent, Event::Listener); impl_try_from_event_for_user_facing_type!( EventListenerDeregisteredEvent, EventListenerEvents::Deregistered, Event::Listener ); #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] pub enum KeyboardEvents { /// See: [`ModifiersEvent`]. Modifiers(ModifiersEvent), } impl_tryfrommessage_for_event_wrapper!(KeyboardEvents); impl_try_from_event_for_interface_enum!(KeyboardEvents, Event::Keyboard); impl_from_interface_event_enum_for_event!(KeyboardEvents, Event::Keyboard); impl_from_user_facing_event_for_interface_event_enum!( ModifiersEvent, KeyboardEvents, KeyboardEvents::Modifiers ); impl EventTypeProperties for KeyboardEvents { fn member(&self) -> &'static str { match self { Self::Modifiers(inner) => inner.member(), } } fn match_rule(&self) -> &'static str { match self { Self::Modifiers(inner) => inner.match_rule(), } } fn interface(&self) -> &'static str { match self { Self::Modifiers(inner) => inner.interface(), } } fn registry_string(&self) -> &'static str { match self { Self::Modifiers(inner) => inner.registry_string(), } } } impl EventProperties for KeyboardEvents { fn path(&self) -> ObjectPath<'_> { match self { Self::Modifiers(inner) => inner.path(), } } fn sender(&self) -> UniqueName<'_> { match self { Self::Modifiers(inner) => inner.sender(), } } } event_wrapper_test_cases!(KeyboardEvents, ModifiersEvent); impl DBusMatchRule for KeyboardEvents { const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Event.Keyboard'"; } impl DBusInterface for KeyboardEvents { const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Event.Keyboard"; } impl RegistryEventString for KeyboardEvents { const REGISTRY_EVENT_STRING: &'static str = "keyboard:"; } impl_from_user_facing_type_for_event_enum!(ModifiersEvent, Event::Keyboard); #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for KeyboardEvents { type Error = AtspiError; fn try_from(msg: &zbus::Message) -> Result { Self::try_from_message(msg) } } impl_try_from_event_for_user_facing_type!( ModifiersEvent, KeyboardEvents::Modifiers, Event::Keyboard ); #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] pub enum MouseEvents { /// See: [`AbsEvent`]. Abs(AbsEvent), /// See: [`RelEvent`]. Rel(RelEvent), /// See: [`ButtonEvent`]. Button(ButtonEvent), } impl DBusMatchRule for MouseEvents { const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Event.Mouse'"; } impl DBusInterface for MouseEvents { const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Event.Mouse"; } impl RegistryEventString for MouseEvents { const REGISTRY_EVENT_STRING: &'static str = "mouse:"; } impl_tryfrommessage_for_event_wrapper!(MouseEvents); #[cfg(feature = "zbus")] impl EventWrapperMessageConversion for KeyboardEvents { fn try_from_message_interface_checked( msg: &zbus::Message, hdr: &Header, ) -> Result { let member = hdr .member() .ok_or(AtspiError::MemberMatch("Event without member".into()))?; match member.as_str() { ModifiersEvent::DBUS_MEMBER => { Ok(KeyboardEvents::Modifiers(ModifiersEvent::from_message_unchecked(msg, hdr)?)) } _ => Err(AtspiError::MemberMatch("No matching member for Keyboard".into())), } } } impl EventProperties for MouseEvents { fn path(&self) -> ObjectPath<'_> { match self { Self::Abs(inner) => inner.path(), Self::Rel(inner) => inner.path(), Self::Button(inner) => inner.path(), } } fn sender(&self) -> UniqueName<'_> { match self { Self::Abs(inner) => inner.sender(), Self::Rel(inner) => inner.sender(), Self::Button(inner) => inner.sender(), } } } impl EventTypeProperties for MouseEvents { fn member(&self) -> &'static str { match self { Self::Abs(inner) => inner.member(), Self::Rel(inner) => inner.member(), Self::Button(inner) => inner.member(), } } fn interface(&self) -> &'static str { match self { Self::Abs(inner) => inner.interface(), Self::Rel(inner) => inner.interface(), Self::Button(inner) => inner.interface(), } } fn match_rule(&self) -> &'static str { match self { Self::Abs(inner) => inner.match_rule(), Self::Rel(inner) => inner.match_rule(), Self::Button(inner) => inner.match_rule(), } } fn registry_string(&self) -> &'static str { match self { Self::Abs(inner) => inner.registry_string(), Self::Rel(inner) => inner.registry_string(), Self::Button(inner) => inner.registry_string(), } } } event_wrapper_test_cases!(MouseEvents, AbsEvent); impl_try_from_event_for_interface_enum!(MouseEvents, Event::Mouse); impl_from_interface_event_enum_for_event!(MouseEvents, Event::Mouse); impl_from_user_facing_event_for_interface_event_enum!(RelEvent, MouseEvents, MouseEvents::Rel); impl_try_from_event_for_user_facing_type!(RelEvent, MouseEvents::Rel, Event::Mouse); impl_from_user_facing_event_for_interface_event_enum!( ButtonEvent, MouseEvents, MouseEvents::Button ); impl_try_from_event_for_user_facing_type!(ButtonEvent, MouseEvents::Button, Event::Mouse); #[cfg(feature = "zbus")] impl EventWrapperMessageConversion for MouseEvents { fn try_from_message_interface_checked( msg: &zbus::Message, hdr: &Header, ) -> Result { let member = hdr.member().ok_or(AtspiError::MissingMember)?; match member.as_str() { AbsEvent::DBUS_MEMBER => { Ok(MouseEvents::Abs(AbsEvent::from_message_unchecked(msg, hdr)?)) } RelEvent::DBUS_MEMBER => { Ok(MouseEvents::Rel(RelEvent::from_message_unchecked(msg, hdr)?)) } ButtonEvent::DBUS_MEMBER => { Ok(MouseEvents::Button(ButtonEvent::from_message_unchecked(msg, hdr)?)) } _ => Err(AtspiError::MemberMatch("No matching member for Mouse".into())), } } } #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for MouseEvents { type Error = AtspiError; fn try_from(msg: &zbus::Message) -> Result { Self::try_from_message(msg) } } impl_from_user_facing_type_for_event_enum!(ButtonEvent, Event::Mouse); impl_from_user_facing_type_for_event_enum!(RelEvent, Event::Mouse); impl_from_user_facing_type_for_event_enum!(AbsEvent, Event::Mouse); impl_from_user_facing_event_for_interface_event_enum!(AbsEvent, MouseEvents, MouseEvents::Abs); impl_try_from_event_for_user_facing_type!(AbsEvent, MouseEvents::Abs, Event::Mouse); #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] pub enum ObjectEvents { /// See: [`ObjectPropertyChangeEvent`]. PropertyChange(ObjectPropertyChangeEvent), /// See: [`BoundsChangedEvent`]. BoundsChanged(BoundsChangedEvent), /// See: [`LinkSelectedEvent`]. LinkSelected(LinkSelectedEvent), /// See: [`StateChangedEvent`]. StateChanged(StateChangedEvent), /// See: [`ChildrenChangedEvent`]. ChildrenChanged(ChildrenChangedEvent), /// See: [`VisibleDataChangedEvent`]. VisibleDataChanged(VisibleDataChangedEvent), /// See: [`SelectionChangedEvent`]. SelectionChanged(SelectionChangedEvent), /// See: [`ModelChangedEvent`]. ModelChanged(ModelChangedEvent), /// See: [`ActiveDescendantChangedEvent`]. ActiveDescendantChanged(ActiveDescendantChangedEvent), /// See: [`AnnouncementEvent`]. Announcement(AnnouncementEvent), /// See: [`ObjectAttributesChangedEvent`]. AttributesChanged(ObjectAttributesChangedEvent), /// See: [`RowInsertedEvent`]. RowInserted(RowInsertedEvent), /// See: [`RowReorderedEvent`]. RowReordered(RowReorderedEvent), /// See: [`RowDeletedEvent`]. RowDeleted(RowDeletedEvent), /// See: [`ColumnInsertedEvent`]. ColumnInserted(ColumnInsertedEvent), /// See: [`ColumnReorderedEvent`]. ColumnReordered(ColumnReorderedEvent), /// See: [`ColumnDeletedEvent`]. ColumnDeleted(ColumnDeletedEvent), /// See: [`TextBoundsChangedEvent`]. TextBoundsChanged(TextBoundsChangedEvent), /// See: [`TextSelectionChangedEvent`]. TextSelectionChanged(TextSelectionChangedEvent), /// See: [`TextChangedEvent`]. TextChanged(TextChangedEvent), /// See: [`TextAttributesChangedEvent`]. TextAttributesChanged(TextAttributesChangedEvent), /// See: [`TextCaretMovedEvent`]. TextCaretMoved(TextCaretMovedEvent), } impl_tryfrommessage_for_event_wrapper!(ObjectEvents); impl EventTypeProperties for ObjectEvents { fn member(&self) -> &'static str { match self { Self::PropertyChange(inner) => inner.member(), Self::BoundsChanged(inner) => inner.member(), Self::LinkSelected(inner) => inner.member(), Self::StateChanged(inner) => inner.member(), Self::ChildrenChanged(inner) => inner.member(), Self::VisibleDataChanged(inner) => inner.member(), Self::SelectionChanged(inner) => inner.member(), Self::ModelChanged(inner) => inner.member(), Self::ActiveDescendantChanged(inner) => inner.member(), Self::Announcement(inner) => inner.member(), Self::AttributesChanged(inner) => inner.member(), Self::RowInserted(inner) => inner.member(), Self::RowReordered(inner) => inner.member(), Self::RowDeleted(inner) => inner.member(), Self::ColumnInserted(inner) => inner.member(), Self::ColumnReordered(inner) => inner.member(), Self::ColumnDeleted(inner) => inner.member(), Self::TextBoundsChanged(inner) => inner.member(), Self::TextSelectionChanged(inner) => inner.member(), Self::TextChanged(inner) => inner.member(), Self::TextAttributesChanged(inner) => inner.member(), Self::TextCaretMoved(inner) => inner.member(), } } fn interface(&self) -> &'static str { match self { Self::PropertyChange(inner) => inner.interface(), Self::BoundsChanged(inner) => inner.interface(), Self::LinkSelected(inner) => inner.interface(), Self::StateChanged(inner) => inner.interface(), Self::ChildrenChanged(inner) => inner.interface(), Self::VisibleDataChanged(inner) => inner.interface(), Self::SelectionChanged(inner) => inner.interface(), Self::ModelChanged(inner) => inner.interface(), Self::ActiveDescendantChanged(inner) => inner.interface(), Self::Announcement(inner) => inner.interface(), Self::AttributesChanged(inner) => inner.interface(), Self::RowInserted(inner) => inner.interface(), Self::RowReordered(inner) => inner.interface(), Self::RowDeleted(inner) => inner.interface(), Self::ColumnInserted(inner) => inner.interface(), Self::ColumnReordered(inner) => inner.interface(), Self::ColumnDeleted(inner) => inner.interface(), Self::TextBoundsChanged(inner) => inner.interface(), Self::TextSelectionChanged(inner) => inner.interface(), Self::TextChanged(inner) => inner.interface(), Self::TextAttributesChanged(inner) => inner.interface(), Self::TextCaretMoved(inner) => inner.interface(), } } fn match_rule(&self) -> &'static str { match self { Self::PropertyChange(inner) => inner.match_rule(), Self::BoundsChanged(inner) => inner.match_rule(), Self::LinkSelected(inner) => inner.match_rule(), Self::StateChanged(inner) => inner.match_rule(), Self::ChildrenChanged(inner) => inner.match_rule(), Self::VisibleDataChanged(inner) => inner.match_rule(), Self::SelectionChanged(inner) => inner.match_rule(), Self::ModelChanged(inner) => inner.match_rule(), Self::ActiveDescendantChanged(inner) => inner.match_rule(), Self::Announcement(inner) => inner.match_rule(), Self::AttributesChanged(inner) => inner.match_rule(), Self::RowInserted(inner) => inner.match_rule(), Self::RowReordered(inner) => inner.match_rule(), Self::RowDeleted(inner) => inner.match_rule(), Self::ColumnInserted(inner) => inner.match_rule(), Self::ColumnReordered(inner) => inner.match_rule(), Self::ColumnDeleted(inner) => inner.match_rule(), Self::TextBoundsChanged(inner) => inner.match_rule(), Self::TextSelectionChanged(inner) => inner.match_rule(), Self::TextChanged(inner) => inner.match_rule(), Self::TextAttributesChanged(inner) => inner.match_rule(), Self::TextCaretMoved(inner) => inner.match_rule(), } } fn registry_string(&self) -> &'static str { match self { Self::PropertyChange(inner) => inner.registry_string(), Self::BoundsChanged(inner) => inner.registry_string(), Self::LinkSelected(inner) => inner.registry_string(), Self::StateChanged(inner) => inner.registry_string(), Self::ChildrenChanged(inner) => inner.registry_string(), Self::VisibleDataChanged(inner) => inner.registry_string(), Self::SelectionChanged(inner) => inner.registry_string(), Self::ModelChanged(inner) => inner.registry_string(), Self::ActiveDescendantChanged(inner) => inner.registry_string(), Self::Announcement(inner) => inner.registry_string(), Self::AttributesChanged(inner) => inner.registry_string(), Self::RowInserted(inner) => inner.registry_string(), Self::RowReordered(inner) => inner.registry_string(), Self::RowDeleted(inner) => inner.registry_string(), Self::ColumnInserted(inner) => inner.registry_string(), Self::ColumnReordered(inner) => inner.registry_string(), Self::ColumnDeleted(inner) => inner.registry_string(), Self::TextBoundsChanged(inner) => inner.registry_string(), Self::TextSelectionChanged(inner) => inner.registry_string(), Self::TextChanged(inner) => inner.registry_string(), Self::TextAttributesChanged(inner) => inner.registry_string(), Self::TextCaretMoved(inner) => inner.registry_string(), } } } impl EventProperties for ObjectEvents { fn path(&self) -> ObjectPath<'_> { match self { Self::PropertyChange(inner) => inner.path(), Self::BoundsChanged(inner) => inner.path(), Self::LinkSelected(inner) => inner.path(), Self::StateChanged(inner) => inner.path(), Self::ChildrenChanged(inner) => inner.path(), Self::VisibleDataChanged(inner) => inner.path(), Self::SelectionChanged(inner) => inner.path(), Self::ModelChanged(inner) => inner.path(), Self::ActiveDescendantChanged(inner) => inner.path(), Self::Announcement(inner) => inner.path(), Self::AttributesChanged(inner) => inner.path(), Self::RowInserted(inner) => inner.path(), Self::RowReordered(inner) => inner.path(), Self::RowDeleted(inner) => inner.path(), Self::ColumnInserted(inner) => inner.path(), Self::ColumnReordered(inner) => inner.path(), Self::ColumnDeleted(inner) => inner.path(), Self::TextBoundsChanged(inner) => inner.path(), Self::TextSelectionChanged(inner) => inner.path(), Self::TextChanged(inner) => inner.path(), Self::TextAttributesChanged(inner) => inner.path(), Self::TextCaretMoved(inner) => inner.path(), } } fn sender(&self) -> UniqueName<'_> { match self { Self::PropertyChange(inner) => inner.sender(), Self::BoundsChanged(inner) => inner.sender(), Self::LinkSelected(inner) => inner.sender(), Self::StateChanged(inner) => inner.sender(), Self::ChildrenChanged(inner) => inner.sender(), Self::VisibleDataChanged(inner) => inner.sender(), Self::SelectionChanged(inner) => inner.sender(), Self::ModelChanged(inner) => inner.sender(), Self::ActiveDescendantChanged(inner) => inner.sender(), Self::Announcement(inner) => inner.sender(), Self::AttributesChanged(inner) => inner.sender(), Self::RowInserted(inner) => inner.sender(), Self::RowReordered(inner) => inner.sender(), Self::RowDeleted(inner) => inner.sender(), Self::ColumnInserted(inner) => inner.sender(), Self::ColumnReordered(inner) => inner.sender(), Self::ColumnDeleted(inner) => inner.sender(), Self::TextBoundsChanged(inner) => inner.sender(), Self::TextSelectionChanged(inner) => inner.sender(), Self::TextChanged(inner) => inner.sender(), Self::TextAttributesChanged(inner) => inner.sender(), Self::TextCaretMoved(inner) => inner.sender(), } } } impl_try_from_event_for_interface_enum!(ObjectEvents, Event::Object); impl_from_interface_event_enum_for_event!(ObjectEvents, Event::Object); event_wrapper_test_cases!(ObjectEvents, ObjectPropertyChangeEvent); impl_from_user_facing_event_for_interface_event_enum!( TextChangedEvent, ObjectEvents, ObjectEvents::TextChanged ); impl_try_from_event_for_user_facing_type!( TextChangedEvent, ObjectEvents::TextChanged, Event::Object ); #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] pub enum DocumentEvents { /// See: [`LoadCompleteEvent`]. LoadComplete(LoadCompleteEvent), /// See: [`ReloadEvent`]. Reload(ReloadEvent), /// See: [`LoadStoppedEvent`]. LoadStopped(LoadStoppedEvent), /// See: [`ContentChangedEvent`]. ContentChanged(ContentChangedEvent), /// See: [`DocumentAttributesChangedEvent`]. AttributesChanged(DocumentAttributesChangedEvent), /// See: [`PageChangedEvent`]. PageChanged(PageChangedEvent), } impl_tryfrommessage_for_event_wrapper!(DocumentEvents); impl DBusInterface for DocumentEvents { const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Event.Document"; } impl DBusMatchRule for DocumentEvents { const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Event.Document'"; } impl RegistryEventString for DocumentEvents { const REGISTRY_EVENT_STRING: &'static str = "Document:"; } impl EventTypeProperties for DocumentEvents { fn member(&self) -> &'static str { match self { Self::LoadComplete(inner) => inner.member(), Self::Reload(inner) => inner.member(), Self::LoadStopped(inner) => inner.member(), Self::ContentChanged(inner) => inner.member(), Self::AttributesChanged(inner) => inner.member(), Self::PageChanged(inner) => inner.member(), } } fn interface(&self) -> &'static str { match self { Self::LoadComplete(inner) => inner.interface(), Self::Reload(inner) => inner.interface(), Self::LoadStopped(inner) => inner.interface(), Self::ContentChanged(inner) => inner.interface(), Self::AttributesChanged(inner) => inner.interface(), Self::PageChanged(inner) => inner.interface(), } } fn match_rule(&self) -> &'static str { match self { Self::LoadComplete(inner) => inner.match_rule(), Self::Reload(inner) => inner.match_rule(), Self::LoadStopped(inner) => inner.match_rule(), Self::ContentChanged(inner) => inner.match_rule(), Self::AttributesChanged(inner) => inner.match_rule(), Self::PageChanged(inner) => inner.match_rule(), } } fn registry_string(&self) -> &'static str { match self { Self::LoadComplete(inner) => inner.registry_string(), Self::Reload(inner) => inner.registry_string(), Self::LoadStopped(inner) => inner.registry_string(), Self::ContentChanged(inner) => inner.registry_string(), Self::AttributesChanged(inner) => inner.registry_string(), Self::PageChanged(inner) => inner.registry_string(), } } } impl EventProperties for DocumentEvents { fn path(&self) -> ObjectPath<'_> { match self { Self::LoadComplete(inner) => inner.path(), Self::Reload(inner) => inner.path(), Self::LoadStopped(inner) => inner.path(), Self::ContentChanged(inner) => inner.path(), Self::AttributesChanged(inner) => inner.path(), Self::PageChanged(inner) => inner.path(), } } fn sender(&self) -> UniqueName<'_> { match self { Self::LoadComplete(inner) => inner.sender(), Self::Reload(inner) => inner.sender(), Self::LoadStopped(inner) => inner.sender(), Self::ContentChanged(inner) => inner.sender(), Self::AttributesChanged(inner) => inner.sender(), Self::PageChanged(inner) => inner.sender(), } } } impl_from_user_facing_type_for_event_enum!(PageChangedEvent, Event::Document); impl_from_user_facing_type_for_event_enum!(DocumentAttributesChangedEvent, Event::Document); impl_from_user_facing_type_for_event_enum!(ContentChangedEvent, Event::Document); impl_from_user_facing_type_for_event_enum!(LoadStoppedEvent, Event::Document); impl_from_user_facing_type_for_event_enum!(ReloadEvent, Event::Document); impl_from_user_facing_type_for_event_enum!(LoadCompleteEvent, Event::Document); impl_try_from_event_for_interface_enum!(DocumentEvents, Event::Document); impl_from_interface_event_enum_for_event!(DocumentEvents, Event::Document); event_wrapper_test_cases!(DocumentEvents, LoadCompleteEvent); #[cfg(feature = "zbus")] impl EventWrapperMessageConversion for DocumentEvents { fn try_from_message_interface_checked( msg: &zbus::Message, hdr: &Header, ) -> Result { let member = hdr.member().ok_or(AtspiError::MissingMember)?; match member.as_str() { LoadCompleteEvent::DBUS_MEMBER => Ok(DocumentEvents::LoadComplete( LoadCompleteEvent::from_message_unchecked(msg, hdr)?, )), ReloadEvent::DBUS_MEMBER => { Ok(DocumentEvents::Reload(ReloadEvent::from_message_unchecked(msg, hdr)?)) } LoadStoppedEvent::DBUS_MEMBER => { Ok(DocumentEvents::LoadStopped(LoadStoppedEvent::from_message_unchecked(msg, hdr)?)) } ContentChangedEvent::DBUS_MEMBER => Ok(DocumentEvents::ContentChanged( ContentChangedEvent::from_message_unchecked(msg, hdr)?, )), DocumentAttributesChangedEvent::DBUS_MEMBER => Ok(DocumentEvents::AttributesChanged( DocumentAttributesChangedEvent::from_message_unchecked(msg, hdr)?, )), PageChangedEvent::DBUS_MEMBER => { Ok(DocumentEvents::PageChanged(PageChangedEvent::from_message_unchecked(msg, hdr)?)) } _ => Err(AtspiError::MemberMatch("No matching member for Document".into())), } } } #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for DocumentEvents { type Error = AtspiError; fn try_from(msg: &zbus::Message) -> Result { Self::try_from_message(msg) } } impl_from_user_facing_event_for_interface_event_enum!( LoadCompleteEvent, DocumentEvents, DocumentEvents::LoadComplete ); impl_try_from_event_for_user_facing_type!( LoadCompleteEvent, DocumentEvents::LoadComplete, Event::Document ); impl_from_user_facing_event_for_interface_event_enum!( ReloadEvent, DocumentEvents, DocumentEvents::Reload ); impl_try_from_event_for_user_facing_type!(ReloadEvent, DocumentEvents::Reload, Event::Document); impl_from_user_facing_event_for_interface_event_enum!( LoadStoppedEvent, DocumentEvents, DocumentEvents::LoadStopped ); impl_try_from_event_for_user_facing_type!( LoadStoppedEvent, DocumentEvents::LoadStopped, Event::Document ); impl_from_user_facing_event_for_interface_event_enum!( ContentChangedEvent, DocumentEvents, DocumentEvents::ContentChanged ); impl_try_from_event_for_user_facing_type!( ContentChangedEvent, DocumentEvents::ContentChanged, Event::Document ); impl_from_user_facing_event_for_interface_event_enum!( DocumentAttributesChangedEvent, DocumentEvents, DocumentEvents::AttributesChanged ); impl_try_from_event_for_user_facing_type!( DocumentAttributesChangedEvent, DocumentEvents::AttributesChanged, Event::Document ); impl_from_user_facing_event_for_interface_event_enum!( PageChangedEvent, DocumentEvents, DocumentEvents::PageChanged ); impl_try_from_event_for_user_facing_type!( PageChangedEvent, DocumentEvents::PageChanged, Event::Document ); /// Encapsulates the various different accessibility bus signal types. /// /// Assumes being non exhaustive to allow for future- or custom signals. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum Event { /// See: [`DocumentEvents`]. Document(DocumentEvents), /// See: [`FocusEvents`]. Focus(FocusEvents), /// See: [`KeyboardEvents`]. Keyboard(KeyboardEvents), /// See: [`MouseEvents`]. Mouse(MouseEvents), /// See: [`ObjectEvents`]. Object(ObjectEvents), /// See: [`TerminalEvents`]. Terminal(TerminalEvents), /// See: [`WindowEvents`]. Window(WindowEvents), /// See: [`AvailableEvent`]. Available(AvailableEvent), /// See: [`CacheEvents`]. Cache(CacheEvents), /// See: [`EventListenerEvents`]. Listener(EventListenerEvents), } impl EventTypeProperties for Event { fn member(&self) -> &'static str { match self { Self::Document(inner) => inner.member(), Self::Focus(inner) => inner.member(), Self::Keyboard(inner) => inner.member(), Self::Mouse(inner) => inner.member(), Self::Object(inner) => inner.member(), Self::Terminal(inner) => inner.member(), Self::Window(inner) => inner.member(), Self::Available(inner) => inner.member(), Self::Cache(inner) => inner.member(), Self::Listener(inner) => inner.member(), } } fn interface(&self) -> &'static str { match self { Self::Document(inner) => inner.interface(), Self::Focus(inner) => inner.interface(), Self::Keyboard(inner) => inner.interface(), Self::Mouse(inner) => inner.interface(), Self::Object(inner) => inner.interface(), Self::Terminal(inner) => inner.interface(), Self::Window(inner) => inner.interface(), Self::Available(inner) => inner.interface(), Self::Cache(inner) => inner.interface(), Self::Listener(inner) => inner.interface(), } } fn match_rule(&self) -> &'static str { match self { Self::Document(inner) => inner.match_rule(), Self::Focus(inner) => inner.match_rule(), Self::Keyboard(inner) => inner.match_rule(), Self::Mouse(inner) => inner.match_rule(), Self::Object(inner) => inner.match_rule(), Self::Terminal(inner) => inner.match_rule(), Self::Window(inner) => inner.match_rule(), Self::Available(inner) => inner.match_rule(), Self::Cache(inner) => inner.match_rule(), Self::Listener(inner) => inner.match_rule(), } } fn registry_string(&self) -> &'static str { match self { Self::Document(inner) => inner.registry_string(), Self::Focus(inner) => inner.registry_string(), Self::Keyboard(inner) => inner.registry_string(), Self::Mouse(inner) => inner.registry_string(), Self::Object(inner) => inner.registry_string(), Self::Terminal(inner) => inner.registry_string(), Self::Window(inner) => inner.registry_string(), Self::Available(inner) => inner.registry_string(), Self::Cache(inner) => inner.registry_string(), Self::Listener(inner) => inner.registry_string(), } } } impl EventProperties for Event { fn path(&self) -> ObjectPath<'_> { match self { Self::Document(inner) => inner.path(), Self::Focus(inner) => inner.path(), Self::Keyboard(inner) => inner.path(), Self::Mouse(inner) => inner.path(), Self::Object(inner) => inner.path(), Self::Terminal(inner) => inner.path(), Self::Window(inner) => inner.path(), Self::Available(inner) => inner.path(), Self::Cache(inner) => inner.path(), Self::Listener(inner) => inner.path(), } } fn sender(&self) -> UniqueName<'_> { match self { Self::Document(inner) => inner.sender(), Self::Focus(inner) => inner.sender(), Self::Keyboard(inner) => inner.sender(), Self::Mouse(inner) => inner.sender(), Self::Object(inner) => inner.sender(), Self::Terminal(inner) => inner.sender(), Self::Window(inner) => inner.sender(), Self::Available(inner) => inner.sender(), Self::Cache(inner) => inner.sender(), Self::Listener(inner) => inner.sender(), } } } #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for Event { type Error = AtspiError; fn try_from(msg: &zbus::Message) -> Result { let header = msg.header(); let interface = header.interface().ok_or(AtspiError::MissingInterface)?; let interface_str = interface.as_str(); match interface_str { ::DBUS_INTERFACE => { Ok(Event::Object(ObjectEvents::try_from_message_interface_checked(msg, &header)?)) } ::DBUS_INTERFACE => { Ok(Event::Focus(FocusEvents::try_from_message_interface_checked(msg, &header)?)) } ::DBUS_INTERFACE => { Ok(Event::Cache(CacheEvents::try_from_message_interface_checked(msg, &header)?)) } ::DBUS_INTERFACE => { Ok(Event::Window(WindowEvents::try_from_message_interface_checked(msg, &header)?)) } ::DBUS_INTERFACE => { Ok(Event::Mouse(MouseEvents::try_from_message_interface_checked(msg, &header)?)) } ::DBUS_INTERFACE => Ok(Event::Terminal( TerminalEvents::try_from_message_interface_checked(msg, &header)?, )), ::DBUS_INTERFACE => Ok(Event::Document( DocumentEvents::try_from_message_interface_checked(msg, &header)?, )), ::DBUS_INTERFACE => Ok(Event::Keyboard( KeyboardEvents::try_from_message_interface_checked(msg, &header)?, )), ::DBUS_INTERFACE => Ok(Event::Listener( EventListenerEvents::try_from_message_interface_checked(msg, &header)?, )), ::DBUS_INTERFACE => { Ok(AvailableEvent::try_from(msg)?.into()) } _ => Err(AtspiError::InterfaceMatch(format!( "No events found with interface {interface_str}" ))), } } } impl_from_user_facing_type_for_event_enum!(TextCaretMovedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(TextAttributesChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(TextChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(TextSelectionChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(TextBoundsChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(ColumnDeletedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(ColumnReorderedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(ColumnInsertedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(RowDeletedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(RowReorderedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(RowInsertedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(ObjectAttributesChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(AnnouncementEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(ActiveDescendantChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(ModelChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(SelectionChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(VisibleDataChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(ChildrenChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(StateChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(LinkSelectedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(BoundsChangedEvent, Event::Object); impl_from_user_facing_type_for_event_enum!(ObjectPropertyChangeEvent, Event::Object); impl_from_user_facing_event_for_interface_event_enum!( ObjectPropertyChangeEvent, ObjectEvents, ObjectEvents::PropertyChange ); impl_try_from_event_for_user_facing_type!( ObjectPropertyChangeEvent, ObjectEvents::PropertyChange, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( BoundsChangedEvent, ObjectEvents, ObjectEvents::BoundsChanged ); impl_try_from_event_for_user_facing_type!( BoundsChangedEvent, ObjectEvents::BoundsChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( LinkSelectedEvent, ObjectEvents, ObjectEvents::LinkSelected ); impl_try_from_event_for_user_facing_type!( LinkSelectedEvent, ObjectEvents::LinkSelected, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( StateChangedEvent, ObjectEvents, ObjectEvents::StateChanged ); impl_try_from_event_for_user_facing_type!( StateChangedEvent, ObjectEvents::StateChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( ChildrenChangedEvent, ObjectEvents, ObjectEvents::ChildrenChanged ); impl_try_from_event_for_user_facing_type!( ChildrenChangedEvent, ObjectEvents::ChildrenChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( VisibleDataChangedEvent, ObjectEvents, ObjectEvents::VisibleDataChanged ); impl_try_from_event_for_user_facing_type!( VisibleDataChangedEvent, ObjectEvents::VisibleDataChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( SelectionChangedEvent, ObjectEvents, ObjectEvents::SelectionChanged ); impl_try_from_event_for_user_facing_type!( SelectionChangedEvent, ObjectEvents::SelectionChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( ModelChangedEvent, ObjectEvents, ObjectEvents::ModelChanged ); impl_try_from_event_for_user_facing_type!( ModelChangedEvent, ObjectEvents::ModelChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( ActiveDescendantChangedEvent, ObjectEvents, ObjectEvents::ActiveDescendantChanged ); impl_try_from_event_for_user_facing_type!( ActiveDescendantChangedEvent, ObjectEvents::ActiveDescendantChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( AnnouncementEvent, ObjectEvents, ObjectEvents::Announcement ); impl_try_from_event_for_user_facing_type!( AnnouncementEvent, ObjectEvents::Announcement, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( ObjectAttributesChangedEvent, ObjectEvents, ObjectEvents::AttributesChanged ); impl_try_from_event_for_user_facing_type!( ObjectAttributesChangedEvent, ObjectEvents::AttributesChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( RowInsertedEvent, ObjectEvents, ObjectEvents::RowInserted ); impl_try_from_event_for_user_facing_type!( RowInsertedEvent, ObjectEvents::RowInserted, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( RowReorderedEvent, ObjectEvents, ObjectEvents::RowReordered ); impl_try_from_event_for_user_facing_type!( RowReorderedEvent, ObjectEvents::RowReordered, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( RowDeletedEvent, ObjectEvents, ObjectEvents::RowDeleted ); impl_try_from_event_for_user_facing_type!(RowDeletedEvent, ObjectEvents::RowDeleted, Event::Object); impl_from_user_facing_event_for_interface_event_enum!( ColumnInsertedEvent, ObjectEvents, ObjectEvents::ColumnInserted ); impl_try_from_event_for_user_facing_type!( ColumnInsertedEvent, ObjectEvents::ColumnInserted, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( ColumnReorderedEvent, ObjectEvents, ObjectEvents::ColumnReordered ); impl_try_from_event_for_user_facing_type!( ColumnReorderedEvent, ObjectEvents::ColumnReordered, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( ColumnDeletedEvent, ObjectEvents, ObjectEvents::ColumnDeleted ); impl_try_from_event_for_user_facing_type!( ColumnDeletedEvent, ObjectEvents::ColumnDeleted, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( TextBoundsChangedEvent, ObjectEvents, ObjectEvents::TextBoundsChanged ); impl_try_from_event_for_user_facing_type!( TextBoundsChangedEvent, ObjectEvents::TextBoundsChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( TextSelectionChangedEvent, ObjectEvents, ObjectEvents::TextSelectionChanged ); impl_try_from_event_for_user_facing_type!( TextSelectionChangedEvent, ObjectEvents::TextSelectionChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( TextAttributesChangedEvent, ObjectEvents, ObjectEvents::TextAttributesChanged ); impl_try_from_event_for_user_facing_type!( TextAttributesChangedEvent, ObjectEvents::TextAttributesChanged, Event::Object ); impl_from_user_facing_event_for_interface_event_enum!( TextCaretMovedEvent, ObjectEvents, ObjectEvents::TextCaretMoved ); impl_try_from_event_for_user_facing_type!( TextCaretMovedEvent, ObjectEvents::TextCaretMoved, Event::Object ); impl DBusMatchRule for ObjectEvents { const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Event.Object'"; } impl DBusInterface for ObjectEvents { const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Event.Object"; } impl RegistryEventString for ObjectEvents { const REGISTRY_EVENT_STRING: &'static str = "object:"; } #[cfg(feature = "zbus")] impl EventWrapperMessageConversion for ObjectEvents { fn try_from_message_interface_checked( msg: &zbus::Message, hdr: &Header, ) -> Result { let member = hdr.member().ok_or(AtspiError::MissingMember)?; match member.as_str() { ObjectPropertyChangeEvent::DBUS_MEMBER => Ok(ObjectEvents::PropertyChange( ObjectPropertyChangeEvent::from_message_unchecked(msg, hdr)?, )), BoundsChangedEvent::DBUS_MEMBER => Ok(ObjectEvents::BoundsChanged( BoundsChangedEvent::from_message_unchecked(msg, hdr)?, )), LinkSelectedEvent::DBUS_MEMBER => { Ok(ObjectEvents::LinkSelected(LinkSelectedEvent::from_message_unchecked(msg, hdr)?)) } StateChangedEvent::DBUS_MEMBER => { Ok(ObjectEvents::StateChanged(StateChangedEvent::from_message_unchecked(msg, hdr)?)) } ChildrenChangedEvent::DBUS_MEMBER => Ok(ObjectEvents::ChildrenChanged( ChildrenChangedEvent::from_message_unchecked(msg, hdr)?, )), VisibleDataChangedEvent::DBUS_MEMBER => Ok(ObjectEvents::VisibleDataChanged( VisibleDataChangedEvent::from_message_unchecked(msg, hdr)?, )), SelectionChangedEvent::DBUS_MEMBER => Ok(ObjectEvents::SelectionChanged( SelectionChangedEvent::from_message_unchecked(msg, hdr)?, )), ModelChangedEvent::DBUS_MEMBER => { Ok(ObjectEvents::ModelChanged(ModelChangedEvent::from_message_unchecked(msg, hdr)?)) } ActiveDescendantChangedEvent::DBUS_MEMBER => Ok(ObjectEvents::ActiveDescendantChanged( ActiveDescendantChangedEvent::from_message_unchecked(msg, hdr)?, )), AnnouncementEvent::DBUS_MEMBER => { Ok(ObjectEvents::Announcement(AnnouncementEvent::from_message_unchecked(msg, hdr)?)) } ObjectAttributesChangedEvent::DBUS_MEMBER => Ok(ObjectEvents::AttributesChanged( ObjectAttributesChangedEvent::from_message_unchecked(msg, hdr)?, )), RowInsertedEvent::DBUS_MEMBER => { Ok(ObjectEvents::RowInserted(RowInsertedEvent::from_message_unchecked(msg, hdr)?)) } RowReorderedEvent::DBUS_MEMBER => { Ok(ObjectEvents::RowReordered(RowReorderedEvent::from_message_unchecked(msg, hdr)?)) } RowDeletedEvent::DBUS_MEMBER => { Ok(ObjectEvents::RowDeleted(RowDeletedEvent::from_message_unchecked(msg, hdr)?)) } ColumnInsertedEvent::DBUS_MEMBER => Ok(ObjectEvents::ColumnInserted( ColumnInsertedEvent::from_message_unchecked(msg, hdr)?, )), ColumnReorderedEvent::DBUS_MEMBER => Ok(ObjectEvents::ColumnReordered( ColumnReorderedEvent::from_message_unchecked(msg, hdr)?, )), ColumnDeletedEvent::DBUS_MEMBER => Ok(ObjectEvents::ColumnDeleted( ColumnDeletedEvent::from_message_unchecked(msg, hdr)?, )), TextBoundsChangedEvent::DBUS_MEMBER => Ok(ObjectEvents::TextBoundsChanged( TextBoundsChangedEvent::from_message_unchecked(msg, hdr)?, )), TextSelectionChangedEvent::DBUS_MEMBER => Ok(ObjectEvents::TextSelectionChanged( TextSelectionChangedEvent::from_message_unchecked(msg, hdr)?, )), TextChangedEvent::DBUS_MEMBER => { Ok(ObjectEvents::TextChanged(TextChangedEvent::from_message_unchecked(msg, hdr)?)) } TextAttributesChangedEvent::DBUS_MEMBER => Ok(ObjectEvents::TextAttributesChanged( TextAttributesChangedEvent::from_message_unchecked(msg, hdr)?, )), TextCaretMovedEvent::DBUS_MEMBER => Ok(ObjectEvents::TextCaretMoved( TextCaretMovedEvent::from_message_unchecked(msg, hdr)?, )), _ => Err(AtspiError::MemberMatch(format!( "No matching member {member} for interface {}", Self::DBUS_INTERFACE, ))), } } } #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for ObjectEvents { type Error = AtspiError; fn try_from(msg: &zbus::Message) -> Result { Self::try_from_message(msg) } } /// All events related to the `org.a11y.atspi.Cache` interface. /// Note that these are not telling the client that an item *has been added* to a cache. /// It is telling the client "here is a bunch of information to store it in your cache". #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)] #[allow(clippy::module_name_repetitions)] pub enum CacheEvents { /// See: [`AddAccessibleEvent`]. Add(AddAccessibleEvent), /// See: [`LegacyAddAccessibleEvent`]. LegacyAdd(LegacyAddAccessibleEvent), /// See: [`RemoveAccessibleEvent`]. Remove(RemoveAccessibleEvent), } impl_from_user_facing_type_for_event_enum!(RemoveAccessibleEvent, Event::Cache); impl_from_user_facing_type_for_event_enum!(AddAccessibleEvent, Event::Cache); impl_from_user_facing_type_for_event_enum!(LegacyAddAccessibleEvent, Event::Cache); impl DBusMatchRule for CacheEvents { const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Cache'"; } impl RegistryEventString for CacheEvents { const REGISTRY_EVENT_STRING: &'static str = "Cache"; } impl DBusInterface for CacheEvents { const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Cache"; } impl EventTypeProperties for CacheEvents { fn member(&self) -> &'static str { match self { Self::Add(inner) => inner.member(), Self::LegacyAdd(inner) => inner.member(), Self::Remove(inner) => inner.member(), } } fn interface(&self) -> &'static str { match self { Self::Add(inner) => inner.interface(), Self::LegacyAdd(inner) => inner.interface(), Self::Remove(inner) => inner.interface(), } } fn match_rule(&self) -> &'static str { match self { Self::Add(inner) => inner.match_rule(), Self::LegacyAdd(inner) => inner.match_rule(), Self::Remove(inner) => inner.match_rule(), } } fn registry_string(&self) -> &'static str { match self { Self::Add(inner) => inner.registry_string(), Self::LegacyAdd(inner) => inner.registry_string(), Self::Remove(inner) => inner.registry_string(), } } } impl EventProperties for CacheEvents { fn path(&self) -> ObjectPath<'_> { match self { Self::Add(inner) => inner.path(), Self::LegacyAdd(inner) => inner.path(), Self::Remove(inner) => inner.path(), } } fn sender(&self) -> UniqueName<'_> { match self { Self::Add(inner) => inner.sender(), Self::LegacyAdd(inner) => inner.sender(), Self::Remove(inner) => inner.sender(), } } } #[cfg(feature = "zbus")] impl EventWrapperMessageConversion for CacheEvents { fn try_from_message_interface_checked( msg: &zbus::Message, hdr: &Header, ) -> Result { let member = hdr.member().ok_or(AtspiError::MissingMember)?; match member.as_str() { AddAccessibleEvent::DBUS_MEMBER => { let body = msg.body(); let sig = body.signature(); if sig == CacheItem::SIGNATURE { Ok(CacheEvents::Add(AddAccessibleEvent::from_message_unchecked(msg, hdr)?)) } else if sig == LegacyCacheItem::SIGNATURE { Ok(CacheEvents::LegacyAdd(LegacyAddAccessibleEvent::from_message_unchecked( msg, hdr, )?)) } else { Err(AtspiError::SignatureMatch(format!( "No matching event for signature {} in interface {}", &sig.to_string(), Self::DBUS_INTERFACE ))) } } RemoveAccessibleEvent::DBUS_MEMBER => { Ok(CacheEvents::Remove(RemoveAccessibleEvent::from_message_unchecked(msg, hdr)?)) } _ => Err(AtspiError::MemberMatch(format!( "No member {} in {}", member.as_str(), Self::DBUS_INTERFACE ))), } } } impl_tryfrommessage_for_event_wrapper!(CacheEvents); #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for CacheEvents { type Error = AtspiError; fn try_from(msg: &zbus::Message) -> Result { Self::try_from_message(msg) } } impl_from_user_facing_event_for_interface_event_enum!( LegacyAddAccessibleEvent, CacheEvents, CacheEvents::LegacyAdd ); impl_try_from_event_for_user_facing_type!( LegacyAddAccessibleEvent, CacheEvents::LegacyAdd, Event::Cache ); impl_from_user_facing_event_for_interface_event_enum!( AddAccessibleEvent, CacheEvents, CacheEvents::Add ); impl_try_from_event_for_user_facing_type!(AddAccessibleEvent, CacheEvents::Add, Event::Cache); impl_from_user_facing_event_for_interface_event_enum!( RemoveAccessibleEvent, CacheEvents, CacheEvents::Remove ); impl_try_from_event_for_user_facing_type!(RemoveAccessibleEvent, CacheEvents::Remove, Event::Cache); impl_try_from_event_for_interface_enum!(CacheEvents, Event::Cache); #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] pub enum FocusEvents { /// See: [`FocusEvent`]. Focus(FocusEvent), } impl_tryfrommessage_for_event_wrapper!(FocusEvents); #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for FocusEvents { type Error = AtspiError; fn try_from(msg: &zbus::Message) -> Result { Self::try_from_message(msg) } } impl_from_interface_event_enum_for_event!(FocusEvents, Event::Focus); impl_try_from_event_for_user_facing_type!(FocusEvent, FocusEvents::Focus, Event::Focus); impl EventTypeProperties for FocusEvents { fn member(&self) -> &'static str { match self { Self::Focus(inner) => inner.member(), } } fn match_rule(&self) -> &'static str { match self { Self::Focus(inner) => inner.match_rule(), } } fn interface(&self) -> &'static str { match self { Self::Focus(inner) => inner.interface(), } } fn registry_string(&self) -> &'static str { match self { Self::Focus(inner) => inner.registry_string(), } } } impl EventProperties for FocusEvents { fn path(&self) -> ObjectPath<'_> { match self { Self::Focus(inner) => inner.path(), } } fn sender(&self) -> UniqueName<'_> { match self { Self::Focus(inner) => inner.sender(), } } } impl_try_from_event_for_interface_enum!(FocusEvents, Event::Focus); impl_from_user_facing_event_for_interface_event_enum!(FocusEvent, FocusEvents, FocusEvents::Focus); impl_from_user_facing_type_for_event_enum!(FocusEvent, Event::Focus); event_wrapper_test_cases!(FocusEvents, FocusEvent); #[cfg(feature = "zbus")] impl EventWrapperMessageConversion for FocusEvents { fn try_from_message_interface_checked( msg: &zbus::Message, hdr: &Header, ) -> Result { let member = hdr.member().ok_or(AtspiError::MissingMember)?; match member.as_str() { FocusEvent::DBUS_MEMBER => { Ok(FocusEvents::Focus(FocusEvent::from_message_unchecked(msg, hdr)?)) } _ => Err(AtspiError::MemberMatch(format!( "No matching member {member} for interface {}", Self::DBUS_INTERFACE, ))), } } } impl DBusMatchRule for FocusEvents { const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Event.Focus'"; } impl RegistryEventString for FocusEvents { const REGISTRY_EVENT_STRING: &'static str = "focus:"; } impl DBusInterface for FocusEvents { const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Event.Focus"; } /// All events related to the `org.a11y.atspi.Event.Terminal` interface. #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] pub enum TerminalEvents { /// See: [`LineChangedEvent`]. LineChanged(LineChangedEvent), /// See: [`ColumnCountChangedEvent`]. ColumnCountChanged(ColumnCountChangedEvent), /// See: [`LineCountChangedEvent`]. LineCountChanged(LineCountChangedEvent), /// See: [`ApplicationChangedEvent`]. ApplicationChanged(ApplicationChangedEvent), /// See: [`CharWidthChangedEvent`]. CharWidthChanged(CharWidthChangedEvent), } impl_tryfrommessage_for_event_wrapper!(TerminalEvents); impl EventTypeProperties for TerminalEvents { fn member(&self) -> &'static str { match self { Self::LineChanged(inner) => inner.member(), Self::ColumnCountChanged(inner) => inner.member(), Self::LineCountChanged(inner) => inner.member(), Self::ApplicationChanged(inner) => inner.member(), Self::CharWidthChanged(inner) => inner.member(), } } fn interface(&self) -> &'static str { match self { Self::LineChanged(inner) => inner.interface(), Self::ColumnCountChanged(inner) => inner.interface(), Self::LineCountChanged(inner) => inner.interface(), Self::ApplicationChanged(inner) => inner.interface(), Self::CharWidthChanged(inner) => inner.interface(), } } fn match_rule(&self) -> &'static str { match self { Self::LineChanged(inner) => inner.match_rule(), Self::ColumnCountChanged(inner) => inner.match_rule(), Self::LineCountChanged(inner) => inner.match_rule(), Self::ApplicationChanged(inner) => inner.match_rule(), Self::CharWidthChanged(inner) => inner.match_rule(), } } fn registry_string(&self) -> &'static str { match self { Self::LineChanged(inner) => inner.registry_string(), Self::ColumnCountChanged(inner) => inner.registry_string(), Self::LineCountChanged(inner) => inner.registry_string(), Self::ApplicationChanged(inner) => inner.registry_string(), Self::CharWidthChanged(inner) => inner.registry_string(), } } } impl EventProperties for TerminalEvents { fn path(&self) -> ObjectPath<'_> { match self { Self::LineChanged(inner) => inner.path(), Self::ColumnCountChanged(inner) => inner.path(), Self::LineCountChanged(inner) => inner.path(), Self::ApplicationChanged(inner) => inner.path(), Self::CharWidthChanged(inner) => inner.path(), } } fn sender(&self) -> UniqueName<'_> { match self { Self::LineChanged(inner) => inner.sender(), Self::ColumnCountChanged(inner) => inner.sender(), Self::LineCountChanged(inner) => inner.sender(), Self::ApplicationChanged(inner) => inner.sender(), Self::CharWidthChanged(inner) => inner.sender(), } } } impl_from_user_facing_type_for_event_enum!(CharWidthChangedEvent, Event::Terminal); impl_from_user_facing_type_for_event_enum!(ApplicationChangedEvent, Event::Terminal); impl_from_user_facing_type_for_event_enum!(LineCountChangedEvent, Event::Terminal); impl_from_user_facing_type_for_event_enum!(ColumnCountChangedEvent, Event::Terminal); impl_from_user_facing_type_for_event_enum!(LineChangedEvent, Event::Terminal); impl_try_from_event_for_interface_enum!(TerminalEvents, Event::Terminal); impl_from_interface_event_enum_for_event!(TerminalEvents, Event::Terminal); event_wrapper_test_cases!(TerminalEvents, LineChangedEvent); impl DBusMatchRule for TerminalEvents { const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Event.Terminal'"; } impl RegistryEventString for TerminalEvents { const REGISTRY_EVENT_STRING: &'static str = "terminal:"; } impl DBusInterface for TerminalEvents { const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Event.Terminal"; } #[cfg(feature = "zbus")] impl EventWrapperMessageConversion for TerminalEvents { fn try_from_message_interface_checked( msg: &zbus::Message, hdr: &Header, ) -> Result { let member = hdr .member() .ok_or(AtspiError::MemberMatch("Event without member".into()))?; match member.as_str() { LineChangedEvent::DBUS_MEMBER => { Ok(TerminalEvents::LineChanged(LineChangedEvent::from_message_unchecked(msg, hdr)?)) } ColumnCountChangedEvent::DBUS_MEMBER => Ok(TerminalEvents::ColumnCountChanged( ColumnCountChangedEvent::from_message_unchecked(msg, hdr)?, )), LineCountChangedEvent::DBUS_MEMBER => Ok(TerminalEvents::LineCountChanged( LineCountChangedEvent::from_message_unchecked(msg, hdr)?, )), ApplicationChangedEvent::DBUS_MEMBER => Ok(TerminalEvents::ApplicationChanged( ApplicationChangedEvent::from_message_unchecked(msg, hdr)?, )), CharWidthChangedEvent::DBUS_MEMBER => Ok(TerminalEvents::CharWidthChanged( CharWidthChangedEvent::from_message_unchecked(msg, hdr)?, )), _ => Err(AtspiError::MemberMatch("No matching member for Terminal".into())), } } } #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for TerminalEvents { type Error = AtspiError; fn try_from(msg: &zbus::Message) -> Result { Self::try_from_message(msg) } } impl_from_user_facing_event_for_interface_event_enum!( LineChangedEvent, TerminalEvents, TerminalEvents::LineChanged ); impl_try_from_event_for_user_facing_type!( LineChangedEvent, TerminalEvents::LineChanged, Event::Terminal ); impl_from_user_facing_event_for_interface_event_enum!( ColumnCountChangedEvent, TerminalEvents, TerminalEvents::ColumnCountChanged ); impl_try_from_event_for_user_facing_type!( ColumnCountChangedEvent, TerminalEvents::ColumnCountChanged, Event::Terminal ); impl_from_user_facing_event_for_interface_event_enum!( LineCountChangedEvent, TerminalEvents, TerminalEvents::LineCountChanged ); impl_try_from_event_for_user_facing_type!( LineCountChangedEvent, TerminalEvents::LineCountChanged, Event::Terminal ); impl_from_user_facing_event_for_interface_event_enum!( ApplicationChangedEvent, TerminalEvents, TerminalEvents::ApplicationChanged ); impl_try_from_event_for_user_facing_type!( ApplicationChangedEvent, TerminalEvents::ApplicationChanged, Event::Terminal ); impl_from_user_facing_event_for_interface_event_enum!( CharWidthChangedEvent, TerminalEvents, TerminalEvents::CharWidthChanged ); impl_try_from_event_for_user_facing_type!( CharWidthChangedEvent, TerminalEvents::CharWidthChanged, Event::Terminal ); /// All events on the `org.a11y.atspi.Event.Window` interface. #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] pub enum WindowEvents { /// See: [`WindowPropertyChangeEvent`]. PropertyChange(WindowPropertyChangeEvent), /// See: [`MinimizeEvent`]. Minimize(MinimizeEvent), /// See: [`MaximizeEvent`]. Maximize(MaximizeEvent), /// See: [`RestoreEvent`]. Restore(RestoreEvent), /// See: [`CloseEvent`]. Close(CloseEvent), /// See: [`CreateEvent`]. Create(CreateEvent), /// See: [`ReparentEvent`]. Reparent(ReparentEvent), /// See: [`DesktopCreateEvent`]. DesktopCreate(DesktopCreateEvent), /// See: [`DesktopDestroyEvent`]. DesktopDestroy(DesktopDestroyEvent), /// See: [`DestroyEvent`]. Destroy(DestroyEvent), /// See: [`ActivateEvent`]. Activate(ActivateEvent), /// See: [`DeactivateEvent`]. Deactivate(DeactivateEvent), /// See: [`RaiseEvent`]. Raise(RaiseEvent), /// See: [`LowerEvent`]. Lower(LowerEvent), /// See: [`MoveEvent`]. Move(MoveEvent), /// See: [`ResizeEvent`]. Resize(ResizeEvent), /// See: [`ShadeEvent`]. Shade(ShadeEvent), /// See: [`UUshadeEvent`]. UUshade(UUshadeEvent), /// See: [`RestyleEvent`]. Restyle(RestyleEvent), } impl_tryfrommessage_for_event_wrapper!(WindowEvents); impl EventTypeProperties for WindowEvents { fn member(&self) -> &'static str { match self { Self::PropertyChange(inner) => inner.member(), Self::Minimize(inner) => inner.member(), Self::Maximize(inner) => inner.member(), Self::Restore(inner) => inner.member(), Self::Close(inner) => inner.member(), Self::Create(inner) => inner.member(), Self::Reparent(inner) => inner.member(), Self::DesktopCreate(inner) => inner.member(), Self::DesktopDestroy(inner) => inner.member(), Self::Destroy(inner) => inner.member(), Self::Activate(inner) => inner.member(), Self::Deactivate(inner) => inner.member(), Self::Raise(inner) => inner.member(), Self::Lower(inner) => inner.member(), Self::Move(inner) => inner.member(), Self::Resize(inner) => inner.member(), Self::Shade(inner) => inner.member(), Self::UUshade(inner) => inner.member(), Self::Restyle(inner) => inner.member(), } } fn interface(&self) -> &'static str { match self { Self::PropertyChange(inner) => inner.interface(), Self::Minimize(inner) => inner.interface(), Self::Maximize(inner) => inner.interface(), Self::Restore(inner) => inner.interface(), Self::Close(inner) => inner.interface(), Self::Create(inner) => inner.interface(), Self::Reparent(inner) => inner.interface(), Self::DesktopCreate(inner) => inner.interface(), Self::DesktopDestroy(inner) => inner.interface(), Self::Destroy(inner) => inner.interface(), Self::Activate(inner) => inner.interface(), Self::Deactivate(inner) => inner.interface(), Self::Raise(inner) => inner.interface(), Self::Lower(inner) => inner.interface(), Self::Move(inner) => inner.interface(), Self::Resize(inner) => inner.interface(), Self::Shade(inner) => inner.interface(), Self::UUshade(inner) => inner.interface(), Self::Restyle(inner) => inner.interface(), } } fn match_rule(&self) -> &'static str { match self { Self::PropertyChange(inner) => inner.match_rule(), Self::Minimize(inner) => inner.match_rule(), Self::Maximize(inner) => inner.match_rule(), Self::Restore(inner) => inner.match_rule(), Self::Close(inner) => inner.match_rule(), Self::Create(inner) => inner.match_rule(), Self::Reparent(inner) => inner.match_rule(), Self::DesktopCreate(inner) => inner.match_rule(), Self::DesktopDestroy(inner) => inner.match_rule(), Self::Destroy(inner) => inner.match_rule(), Self::Activate(inner) => inner.match_rule(), Self::Deactivate(inner) => inner.match_rule(), Self::Raise(inner) => inner.match_rule(), Self::Lower(inner) => inner.match_rule(), Self::Move(inner) => inner.match_rule(), Self::Resize(inner) => inner.match_rule(), Self::Shade(inner) => inner.match_rule(), Self::UUshade(inner) => inner.match_rule(), Self::Restyle(inner) => inner.match_rule(), } } fn registry_string(&self) -> &'static str { match self { Self::PropertyChange(inner) => inner.registry_string(), Self::Minimize(inner) => inner.registry_string(), Self::Maximize(inner) => inner.registry_string(), Self::Restore(inner) => inner.registry_string(), Self::Close(inner) => inner.registry_string(), Self::Create(inner) => inner.registry_string(), Self::Reparent(inner) => inner.registry_string(), Self::DesktopCreate(inner) => inner.registry_string(), Self::DesktopDestroy(inner) => inner.registry_string(), Self::Destroy(inner) => inner.registry_string(), Self::Activate(inner) => inner.registry_string(), Self::Deactivate(inner) => inner.registry_string(), Self::Raise(inner) => inner.registry_string(), Self::Lower(inner) => inner.registry_string(), Self::Move(inner) => inner.registry_string(), Self::Resize(inner) => inner.registry_string(), Self::Shade(inner) => inner.registry_string(), Self::UUshade(inner) => inner.registry_string(), Self::Restyle(inner) => inner.registry_string(), } } } impl EventProperties for WindowEvents { fn path(&self) -> ObjectPath<'_> { match self { Self::PropertyChange(inner) => inner.path(), Self::Minimize(inner) => inner.path(), Self::Maximize(inner) => inner.path(), Self::Restore(inner) => inner.path(), Self::Close(inner) => inner.path(), Self::Create(inner) => inner.path(), Self::Reparent(inner) => inner.path(), Self::DesktopCreate(inner) => inner.path(), Self::DesktopDestroy(inner) => inner.path(), Self::Destroy(inner) => inner.path(), Self::Activate(inner) => inner.path(), Self::Deactivate(inner) => inner.path(), Self::Raise(inner) => inner.path(), Self::Lower(inner) => inner.path(), Self::Move(inner) => inner.path(), Self::Resize(inner) => inner.path(), Self::Shade(inner) => inner.path(), Self::UUshade(inner) => inner.path(), Self::Restyle(inner) => inner.path(), } } fn sender(&self) -> UniqueName<'_> { match self { Self::PropertyChange(inner) => inner.sender(), Self::Minimize(inner) => inner.sender(), Self::Maximize(inner) => inner.sender(), Self::Restore(inner) => inner.sender(), Self::Close(inner) => inner.sender(), Self::Create(inner) => inner.sender(), Self::Reparent(inner) => inner.sender(), Self::DesktopCreate(inner) => inner.sender(), Self::DesktopDestroy(inner) => inner.sender(), Self::Destroy(inner) => inner.sender(), Self::Activate(inner) => inner.sender(), Self::Deactivate(inner) => inner.sender(), Self::Raise(inner) => inner.sender(), Self::Lower(inner) => inner.sender(), Self::Move(inner) => inner.sender(), Self::Resize(inner) => inner.sender(), Self::Shade(inner) => inner.sender(), Self::UUshade(inner) => inner.sender(), Self::Restyle(inner) => inner.sender(), } } } impl_from_user_facing_type_for_event_enum!(ReparentEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(CloseEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(RestoreEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(MaximizeEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(MinimizeEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(WindowPropertyChangeEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(RestyleEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(UUshadeEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(ShadeEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(ResizeEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(MoveEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(LowerEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(RaiseEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(DeactivateEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(ActivateEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(DestroyEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(DesktopDestroyEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(DesktopCreateEvent, Event::Window); impl_from_user_facing_type_for_event_enum!(CreateEvent, Event::Window); impl_try_from_event_for_interface_enum!(WindowEvents, Event::Window); impl_from_interface_event_enum_for_event!(WindowEvents, Event::Window); event_wrapper_test_cases!(WindowEvents, MoveEvent); impl DBusMatchRule for WindowEvents { const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Event.Window'"; } impl DBusInterface for WindowEvents { const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Event.Window"; } impl RegistryEventString for WindowEvents { const REGISTRY_EVENT_STRING: &'static str = "window:"; } #[cfg(feature = "zbus")] impl EventWrapperMessageConversion for WindowEvents { fn try_from_message_interface_checked( msg: &zbus::Message, hdr: &Header, ) -> Result { let member = hdr.member().ok_or(AtspiError::MissingMember)?; match member.as_str() { WindowPropertyChangeEvent::DBUS_MEMBER => Ok(WindowEvents::PropertyChange( WindowPropertyChangeEvent::from_message_unchecked(msg, hdr)?, )), MinimizeEvent::DBUS_MEMBER => { Ok(WindowEvents::Minimize(MinimizeEvent::from_message_unchecked(msg, hdr)?)) } MaximizeEvent::DBUS_MEMBER => { Ok(WindowEvents::Maximize(MaximizeEvent::from_message_unchecked(msg, hdr)?)) } RestoreEvent::DBUS_MEMBER => { Ok(WindowEvents::Restore(RestoreEvent::from_message_unchecked(msg, hdr)?)) } CloseEvent::DBUS_MEMBER => { Ok(WindowEvents::Close(CloseEvent::from_message_unchecked(msg, hdr)?)) } CreateEvent::DBUS_MEMBER => { Ok(WindowEvents::Create(CreateEvent::from_message_unchecked(msg, hdr)?)) } ReparentEvent::DBUS_MEMBER => { Ok(WindowEvents::Reparent(ReparentEvent::from_message_unchecked(msg, hdr)?)) } DesktopCreateEvent::DBUS_MEMBER => Ok(WindowEvents::DesktopCreate( DesktopCreateEvent::from_message_unchecked(msg, hdr)?, )), DesktopDestroyEvent::DBUS_MEMBER => Ok(WindowEvents::DesktopDestroy( DesktopDestroyEvent::from_message_unchecked(msg, hdr)?, )), DestroyEvent::DBUS_MEMBER => { Ok(WindowEvents::Destroy(DestroyEvent::from_message_unchecked(msg, hdr)?)) } ActivateEvent::DBUS_MEMBER => { Ok(WindowEvents::Activate(ActivateEvent::from_message_unchecked(msg, hdr)?)) } DeactivateEvent::DBUS_MEMBER => { Ok(WindowEvents::Deactivate(DeactivateEvent::from_message_unchecked(msg, hdr)?)) } RaiseEvent::DBUS_MEMBER => { Ok(WindowEvents::Raise(RaiseEvent::from_message_unchecked(msg, hdr)?)) } LowerEvent::DBUS_MEMBER => { Ok(WindowEvents::Lower(LowerEvent::from_message_unchecked(msg, hdr)?)) } MoveEvent::DBUS_MEMBER => { Ok(WindowEvents::Move(MoveEvent::from_message_unchecked(msg, hdr)?)) } ResizeEvent::DBUS_MEMBER => { Ok(WindowEvents::Resize(ResizeEvent::from_message_unchecked(msg, hdr)?)) } ShadeEvent::DBUS_MEMBER => { Ok(WindowEvents::Shade(ShadeEvent::from_message_unchecked(msg, hdr)?)) } UUshadeEvent::DBUS_MEMBER => { Ok(WindowEvents::UUshade(UUshadeEvent::from_message_unchecked(msg, hdr)?)) } RestyleEvent::DBUS_MEMBER => { Ok(WindowEvents::Restyle(RestyleEvent::from_message_unchecked(msg, hdr)?)) } _ => Err(AtspiError::MemberMatch("No matching member for Window".into())), } } } #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for WindowEvents { type Error = AtspiError; fn try_from(msg: &zbus::Message) -> Result { Self::try_from_message(msg) } } impl_from_user_facing_event_for_interface_event_enum!( WindowPropertyChangeEvent, WindowEvents, WindowEvents::PropertyChange ); impl_try_from_event_for_user_facing_type!( WindowPropertyChangeEvent, WindowEvents::PropertyChange, Event::Window ); impl_from_user_facing_event_for_interface_event_enum!( MinimizeEvent, WindowEvents, WindowEvents::Minimize ); impl_try_from_event_for_user_facing_type!(MinimizeEvent, WindowEvents::Minimize, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( MaximizeEvent, WindowEvents, WindowEvents::Maximize ); impl_try_from_event_for_user_facing_type!(MaximizeEvent, WindowEvents::Maximize, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( RestoreEvent, WindowEvents, WindowEvents::Restore ); impl_try_from_event_for_user_facing_type!(RestoreEvent, WindowEvents::Restore, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( CloseEvent, WindowEvents, WindowEvents::Close ); impl_try_from_event_for_user_facing_type!(CloseEvent, WindowEvents::Close, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( CreateEvent, WindowEvents, WindowEvents::Create ); impl_try_from_event_for_user_facing_type!(CreateEvent, WindowEvents::Create, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( ReparentEvent, WindowEvents, WindowEvents::Reparent ); impl_try_from_event_for_user_facing_type!(ReparentEvent, WindowEvents::Reparent, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( DesktopCreateEvent, WindowEvents, WindowEvents::DesktopCreate ); impl_try_from_event_for_user_facing_type!( DesktopCreateEvent, WindowEvents::DesktopCreate, Event::Window ); impl_from_user_facing_event_for_interface_event_enum!( DesktopDestroyEvent, WindowEvents, WindowEvents::DesktopDestroy ); impl_try_from_event_for_user_facing_type!( DesktopDestroyEvent, WindowEvents::DesktopDestroy, Event::Window ); impl_from_user_facing_event_for_interface_event_enum!( DestroyEvent, WindowEvents, WindowEvents::Destroy ); impl_try_from_event_for_user_facing_type!(DestroyEvent, WindowEvents::Destroy, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( ActivateEvent, WindowEvents, WindowEvents::Activate ); impl_try_from_event_for_user_facing_type!(ActivateEvent, WindowEvents::Activate, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( DeactivateEvent, WindowEvents, WindowEvents::Deactivate ); impl_try_from_event_for_user_facing_type!(DeactivateEvent, WindowEvents::Deactivate, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( RaiseEvent, WindowEvents, WindowEvents::Raise ); impl_try_from_event_for_user_facing_type!(RaiseEvent, WindowEvents::Raise, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( LowerEvent, WindowEvents, WindowEvents::Lower ); impl_try_from_event_for_user_facing_type!(LowerEvent, WindowEvents::Lower, Event::Window); impl_from_user_facing_event_for_interface_event_enum!(MoveEvent, WindowEvents, WindowEvents::Move); impl_try_from_event_for_user_facing_type!(MoveEvent, WindowEvents::Move, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( ResizeEvent, WindowEvents, WindowEvents::Resize ); impl_try_from_event_for_user_facing_type!(ResizeEvent, WindowEvents::Resize, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( ShadeEvent, WindowEvents, WindowEvents::Shade ); impl_try_from_event_for_user_facing_type!(ShadeEvent, WindowEvents::Shade, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( UUshadeEvent, WindowEvents, WindowEvents::UUshade ); impl_try_from_event_for_user_facing_type!(UUshadeEvent, WindowEvents::UUshade, Event::Window); impl_from_user_facing_event_for_interface_event_enum!( RestyleEvent, WindowEvents, WindowEvents::Restyle ); impl_try_from_event_for_user_facing_type!(RestyleEvent, WindowEvents::Restyle, Event::Window); /// The events that can be emitted by the registry daemon. /// This enum is used to wrap the events that are emitted by the registry daemon. /// The events are [`EventListenerRegisteredEvent`] and [`EventListenerDeregisteredEvent`]. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] #[allow(clippy::module_name_repetitions)] pub enum EventListenerEvents { /// See: [`EventListenerRegisteredEvent`]. Registered(EventListenerRegisteredEvent), /// See: [`EventListenerDeregisteredEvent`]. Deregistered(EventListenerDeregisteredEvent), } impl_tryfrommessage_for_event_wrapper!(EventListenerEvents); impl DBusInterface for EventListenerEvents { const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Registry"; } impl DBusMatchRule for EventListenerEvents { const MATCH_RULE_STRING: &'static str = "type='signal',interface='org.a11y.atspi.Registry'"; } impl RegistryEventString for EventListenerEvents { const REGISTRY_EVENT_STRING: &'static str = "Event"; } impl EventTypeProperties for EventListenerEvents { fn member(&self) -> &'static str { match self { Self::Registered(inner) => inner.member(), Self::Deregistered(inner) => inner.member(), } } fn match_rule(&self) -> &'static str { match self { Self::Registered(inner) => inner.match_rule(), Self::Deregistered(inner) => inner.match_rule(), } } fn interface(&self) -> &'static str { match self { Self::Registered(inner) => inner.interface(), Self::Deregistered(inner) => inner.interface(), } } fn registry_string(&self) -> &'static str { match self { Self::Registered(inner) => inner.registry_string(), Self::Deregistered(inner) => inner.registry_string(), } } } impl EventProperties for EventListenerEvents { fn path(&self) -> ObjectPath<'_> { match self { Self::Registered(inner) => inner.path(), Self::Deregistered(inner) => inner.path(), } } fn sender(&self) -> UniqueName<'_> { match self { Self::Registered(inner) => inner.sender(), Self::Deregistered(inner) => inner.sender(), } } } #[cfg(feature = "zbus")] impl crate::events::traits::EventWrapperMessageConversion for EventListenerEvents { fn try_from_message_interface_checked( msg: &zbus::Message, hdr: &zbus::message::Header, ) -> Result { let member = hdr.member().ok_or(AtspiError::MissingMember)?; match member.as_str() { EventListenerRegisteredEvent::DBUS_MEMBER => Ok(EventListenerEvents::Registered( EventListenerRegisteredEvent::from_message_unchecked(msg, hdr)?, )), EventListenerDeregisteredEvent::DBUS_MEMBER => Ok(EventListenerEvents::Deregistered( EventListenerDeregisteredEvent::from_message_unchecked(msg, hdr)?, )), _ => Err(AtspiError::MemberMatch(format!( "No member {} in {}", member.as_str(), Self::DBUS_INTERFACE ))), } } } #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for EventListenerEvents { type Error = AtspiError; fn try_from(msg: &zbus::Message) -> Result { ::try_from_message(msg) } } impl_try_from_event_for_interface_enum!(EventListenerEvents, Event::Listener); impl_from_interface_event_enum_for_event!(EventListenerEvents, Event::Listener); impl From for Event { fn from(ev: AvailableEvent) -> Event { Event::Available(ev) } } #[cfg(feature = "zbus")] impl TryFrom for AvailableEvent { type Error = AtspiError; fn try_from(generic_event: Event) -> Result { if let Event::Available(specific_event) = generic_event { Ok(specific_event) } else { Err(AtspiError::Conversion("Invalid type")) } } } event_wrapper_test_cases!(EventListenerEvents, EventListenerRegisteredEvent); atspi-common-0.13.0/src/events/focus.rs000064400000000000000000000020151046102023000160710ustar 00000000000000#[cfg(feature = "zbus")] use crate::error::AtspiError; #[cfg(feature = "zbus")] use crate::EventProperties; use crate::{ events::{DBusInterface, DBusMatchRule, DBusMember, RegistryEventString}, object_ref::ObjectRefOwned, }; #[cfg(feature = "zbus")] use zbus::message::Header; #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct FocusEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(FocusEvent); impl_member_interface_registry_string_and_match_rule_for_event! { FocusEvent, "Focus", "org.a11y.atspi.Event.Focus", "focus:", "type='signal',interface='org.a11y.atspi.Event.Focus',member='Focus'" } impl_msg_conversion_ext_for_target_type!(FocusEvent); impl_msg_conversion_for_types_built_from_object_ref!(FocusEvent); event_test_cases!(FocusEvent); impl_to_dbus_message!(FocusEvent); impl_from_dbus_message!(FocusEvent); impl_event_properties!(FocusEvent); impl_from_object_ref!(FocusEvent); atspi-common-0.13.0/src/events/keyboard.rs000064400000000000000000000041731046102023000165610ustar 00000000000000#[cfg(feature = "zbus")] use super::event_body::EventBody; #[cfg(feature = "zbus")] use crate::error::AtspiError; use crate::{ events::{DBusInterface, DBusMatchRule, DBusMember, EventBodyOwned, RegistryEventString}, object_ref::ObjectRefOwned, }; #[cfg(feature = "zbus")] use crate::{events::MessageConversion, EventProperties, ObjectRef}; #[cfg(feature = "zbus")] use zbus::message::{Body as DbusBody, Header}; #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ModifiersEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, pub previous_modifiers: i32, pub current_modifiers: i32, } impl_event_type_properties_for_event!(ModifiersEvent); impl_member_interface_registry_string_and_match_rule_for_event! { ModifiersEvent, "Modifiers", "org.a11y.atspi.Event.Keyboard", "keyboard:modifiers", "type='signal',interface='org.a11y.atspi.Event.Keyboard',member='Modifiers'" } #[cfg(feature = "zbus")] impl MessageConversion<'_> for ModifiersEvent { type Body<'msg> = EventBody<'msg>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let body = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), previous_modifiers: body.detail1(), current_modifiers: body.detail2(), }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { EventBodyOwned { detail1: self.previous_modifiers, detail2: self.current_modifiers, ..Default::default() } .into() } } impl_msg_conversion_ext_for_target_type!(ModifiersEvent); event_test_cases!(ModifiersEvent); impl_to_dbus_message!(ModifiersEvent); impl_from_dbus_message!(ModifiersEvent); impl_event_properties!(ModifiersEvent); impl From for EventBodyOwned { fn from(event: ModifiersEvent) -> Self { EventBodyOwned { detail1: event.previous_modifiers, detail2: event.current_modifiers, ..Default::default() } } } atspi-common-0.13.0/src/events/mod.rs000064400000000000000000000014101046102023000155270ustar 00000000000000pub mod cache; pub mod document; pub mod event_body; #[cfg(feature = "wrappers")] pub mod event_wrappers; pub mod focus; pub mod keyboard; pub mod mouse; pub mod object; pub mod registry; pub mod terminal; pub mod traits; pub mod window; pub use event_body::{ EventBody, EventBodyBorrowed, EventBodyOwned, EventBodyQtBorrowed, EventBodyQtOwned, }; #[cfg(feature = "wrappers")] pub use event_wrappers::{ CacheEvents, DocumentEvents, Event, EventListenerEvents, FocusEvents, KeyboardEvents, MouseEvents, ObjectEvents, TerminalEvents, WindowEvents, }; pub use traits::{ DBusInterface, DBusMatchRule, DBusMember, DBusProperties, EventProperties, EventTypeProperties, RegistryEventString, }; #[cfg(feature = "zbus")] pub use traits::{MessageConversion, MessageConversionExt}; atspi-common-0.13.0/src/events/mouse.rs000064400000000000000000000134371046102023000161140ustar 00000000000000#[cfg(feature = "zbus")] use crate::events::MessageConversion; use crate::events::{ DBusInterface, DBusMatchRule, DBusMember, EventBody, EventBodyOwned, RegistryEventString, }; use crate::object_ref::ObjectRefOwned; #[cfg(feature = "zbus")] use crate::EventProperties; #[cfg(feature = "zbus")] use crate::{error::AtspiError, ObjectRef}; #[cfg(feature = "zbus")] use zbus::message::{Body as DbusBody, Header}; #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct AbsEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, pub x: i32, pub y: i32, } impl_event_type_properties_for_event!(AbsEvent); #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct RelEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, pub x: i32, pub y: i32, } impl_event_type_properties_for_event!(RelEvent); #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ButtonEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, pub detail: String, pub mouse_x: i32, pub mouse_y: i32, } impl_event_type_properties_for_event!(ButtonEvent); impl_member_interface_registry_string_and_match_rule_for_event! { AbsEvent, "Abs", "org.a11y.atspi.Event.Mouse", "mouse:abs", "type='signal',interface='org.a11y.atspi.Event.Mouse',member='Abs'" } impl_member_interface_registry_string_and_match_rule_for_event! { RelEvent, "Rel", "org.a11y.atspi.Event.Mouse", "mouse:rel", "type='signal',interface='org.a11y.atspi.Event.Mouse',member='Rel'" } impl_member_interface_registry_string_and_match_rule_for_event! { ButtonEvent, "Button", "org.a11y.atspi.Event.Mouse", "mouse:button", "type='signal',interface='org.a11y.atspi.Event.Mouse',member='Button'" } #[cfg(feature = "zbus")] impl MessageConversion<'_> for AbsEvent { type Body<'a> = EventBody<'a>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let body = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), x: body.detail1(), y: body.detail2() }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { EventBodyOwned { detail1: self.x, detail2: self.y, ..Default::default() }.into() } } #[cfg(feature = "zbus")] impl MessageConversion<'_> for RelEvent { type Body<'a> = EventBody<'a>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let body = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), x: body.detail1(), y: body.detail2() }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { EventBodyOwned { detail1: self.x, detail2: self.y, ..Default::default() }.into() } } #[cfg(feature = "zbus")] impl MessageConversion<'_> for ButtonEvent { type Body<'a> = EventBody<'a>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let mut body = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), detail: body.take_kind(), mouse_x: body.detail1(), mouse_y: body.detail2(), }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { EventBodyOwned::from(self).into() } } event_test_cases!(AbsEvent); impl_to_dbus_message!(AbsEvent); impl_from_dbus_message!(AbsEvent); impl_event_properties!(AbsEvent); impl From for EventBodyOwned { fn from(event: AbsEvent) -> Self { EventBodyOwned { detail1: event.x, detail2: event.y, ..Default::default() } } } impl From<&AbsEvent> for EventBodyOwned { fn from(event: &AbsEvent) -> Self { EventBodyOwned { detail1: event.x, detail2: event.y, ..Default::default() } } } impl From for EventBody<'_> { fn from(event: AbsEvent) -> Self { EventBodyOwned::from(event).into() } } event_test_cases!(RelEvent); impl_to_dbus_message!(RelEvent); impl_from_dbus_message!(RelEvent); impl_event_properties!(RelEvent); impl From for EventBodyOwned { fn from(event: RelEvent) -> Self { EventBodyOwned { detail1: event.x, detail2: event.y, ..Default::default() } } } impl From<&RelEvent> for EventBodyOwned { fn from(event: &RelEvent) -> Self { EventBodyOwned { detail1: event.x, detail2: event.y, ..Default::default() } } } impl From for EventBody<'_> { fn from(event: RelEvent) -> Self { EventBodyOwned::from(event).into() } } event_test_cases!(ButtonEvent); impl_to_dbus_message!(ButtonEvent); impl_from_dbus_message!(ButtonEvent); impl_event_properties!(ButtonEvent); impl From for EventBodyOwned { fn from(event: ButtonEvent) -> Self { EventBodyOwned { kind: event.detail, detail1: event.mouse_x, detail2: event.mouse_y, ..Default::default() } } } impl From for EventBody<'_> { fn from(event: ButtonEvent) -> Self { EventBodyOwned::from(event).into() } } impl From<&ButtonEvent> for EventBodyOwned { fn from(event: &ButtonEvent) -> Self { EventBodyOwned { kind: event.detail.clone(), detail1: event.mouse_x, detail2: event.mouse_y, ..Default::default() } } } impl_msg_conversion_ext_for_target_type!(AbsEvent); impl_msg_conversion_ext_for_target_type!(RelEvent); impl_msg_conversion_ext_for_target_type!(ButtonEvent); atspi-common-0.13.0/src/events/object.rs000064400000000000000000001267071046102023000162370ustar 00000000000000//! All events which can be received by the `org.a11y.atspi.Object` interface. #![deny(missing_docs)] #[cfg(feature = "zbus")] use crate::events::MessageConversion; #[cfg(feature = "zbus")] use crate::object_ref::ObjectRef; #[cfg(feature = "zbus")] use crate::EventProperties; use crate::{ error::AtspiError, events::{ DBusInterface, DBusMatchRule, DBusMember, EventBody, EventBodyOwned, RegistryEventString, }, object_ref::ObjectRefOwned, State, }; use std::hash::Hash; #[cfg(feature = "zbus")] use zbus::message::{Body as DbusBody, Header}; use zvariant::{OwnedValue, Value}; const ACCESSIBLE_NAME_PROPERTY_NAME: &str = "accessible-name"; const ACCESSIBLE_DESCRIPTION_PROPERTY_NAME: &str = "accessible-description"; const ACCESSIBLE_HELP_TEXT_PROPERTY_NAME: &str = "accessible-help-text"; const ACCESSIBLE_PARENT_PROPERTY_NAME: &str = "accessible-parent"; const ACCESSIBLE_ROLE_PROPERTY_NAME: &str = "accessible-role"; const ACCESSIBLE_TABLE_CAPTION_PROPERTY_NAME: &str = "accessible-table-caption"; const ACCESSIBLE_TABLE_COLUMN_DESCRIPTION_PROPERTY_NAME: &str = "accessible-table-column-description"; const ACCESSIBLE_TABLE_COLUMN_HEADER_PROPERTY_NAME: &str = "accessible-table-column-header"; const ACCESSIBLE_TABLE_ROW_DESCRIPTION_PROPERTY_NAME: &str = "accessible-table-row-description"; const ACCESSIBLE_TABLE_ROW_HEADER_PROPERTY_NAME: &str = "accessible-table-row-header"; const ACCESSIBLE_TABLE_SUMMARY_PROPERTY_NAME: &str = "accessible-table-summary"; /// An event representing a property change on UI item `item` with new value `value`. #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub struct PropertyChangeEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, /// The name of the property. // TODO: this is not necessary since the string is encoded in the `Property` type. pub property: String, /// The value of the property. pub value: Property, } impl_event_type_properties_for_event!(PropertyChangeEvent); impl Hash for PropertyChangeEvent { fn hash(&self, state: &mut H) { self.item.hash(state); self.property.hash(state); } } // Do not derive Eq if not all fields implement Eq impl Eq for PropertyChangeEvent {} // TODO: Looks like a false positive Clippy lint // Derive me. #[allow(clippy::derivable_impls)] impl Default for PropertyChangeEvent { fn default() -> Self { Self { item: ObjectRefOwned::default(), property: String::default(), value: Property::default(), } } } /// Any accessibility related property on an [`crate::ObjectRef`]. /// This is used only in the [`PropertyChangeEvent`]; this event gets triggered if a role or accessible /// description of an item changes. #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] #[non_exhaustive] pub enum Property { /// Name of the element; this can either be the text of a simple UI element like a [`crate::Role::Button`], but it could also be alternative text via [`aria-label`](https://www.w3.org/TR/wai-aria/#aria-label). Name(String), /// The extended description of an item (usually via [`aria-describedby`](https://www.w3.org/TR/wai-aria/#aria-describedby)). Description(String), /// The [ARIA role](https://www.w3.org/TR/wai-aria/#roles) of a given item. Role(crate::Role), /// Parent of the item in a hierarchical tree. Parent(ObjectRefOwned), /// A description of the table as a whole: in HTML this is achieved via the /// `...
VALUE_HERE
` pattern TableCaption(String), /// Similar to [`Self::TableColumnHeader`] except it's the attached description instead of the /// data in the header. TableColumnDescription(String), /// A column header: in HTML this is accomplished with the use of `` in an aligned column with a given `` cell element TableColumnHeader(String), /// Similar to [`Self::TableRowHeader`] except it's the attached description instead of the /// data in the header. TableRowDescription(String), /// Row header: in HTML this is accomplished with the use of `` at the beginning of a `` TableRowHeader(String), /// The table summary is a shorter description of the table. In HTML this would be accomplished /// with the [figure/figcaption pattern](https://www.w3.org/WAI/tutorials/tables/caption-summary/#using-the-figure-element-to-mark-up-a-table-summary) TableSummary(String), /// The attached help text of the item. HelpText(String), /// Any other attribute not explicitly laid out above. Other((String, OwnedValue)), } impl Clone for Property { fn clone(&self) -> Self { match self { Property::Name(name) => Self::Name(name.clone()), Property::Description(description) => Self::Description(description.clone()), Property::Role(role) => Self::Role(*role), Property::Parent(parent) => Self::Parent(parent.clone()), Property::TableCaption(table_caption) => Self::TableCaption(table_caption.clone()), Property::TableColumnDescription(table_column_description) => { Self::TableColumnDescription(table_column_description.clone()) } Property::TableColumnHeader(table_column_header) => { Self::TableColumnHeader(table_column_header.clone()) } Property::TableRowDescription(table_row_description) => { Self::TableRowDescription(table_row_description.clone()) } Property::TableRowHeader(table_row_header) => { Self::TableRowHeader(table_row_header.clone()) } Property::TableSummary(table_summary) => Self::TableSummary(table_summary.clone()), Property::HelpText(help_text) => Self::HelpText(help_text.clone()), Property::Other((property, value)) => Self::Other(( property.clone(), value .try_clone() .expect("Could not clone 'value'. Since properties are not known to carry files, we do not expect to exceed open file limit."), )), } } } impl Default for Property { fn default() -> Self { Self::Other((String::default(), u64::default().into())) } } impl TryFrom> for Property { type Error = AtspiError; fn try_from(mut body: EventBody<'_>) -> Result { let property = body.kind(); match property { ACCESSIBLE_NAME_PROPERTY_NAME => Ok(Self::Name( body.take_any_data() .try_into() .map_err(|_| AtspiError::ParseError(ACCESSIBLE_NAME_PROPERTY_NAME))?, )), ACCESSIBLE_DESCRIPTION_PROPERTY_NAME => Ok(Self::Description( body.take_any_data() .try_into() .map_err(|_| AtspiError::ParseError(ACCESSIBLE_DESCRIPTION_PROPERTY_NAME))?, )), ACCESSIBLE_ROLE_PROPERTY_NAME => Ok(Self::Role({ let role_int: u32 = body .any_data() .try_into() .map_err(|_| AtspiError::ParseError(ACCESSIBLE_ROLE_PROPERTY_NAME))?; let role: crate::Role = crate::Role::try_from(role_int) .map_err(|_| AtspiError::ParseError("accessible-role"))?; role })), ACCESSIBLE_PARENT_PROPERTY_NAME => Ok(Self::Parent( body.take_any_data() .try_into() .map_err(|_| AtspiError::ParseError(ACCESSIBLE_PARENT_PROPERTY_NAME))?, )), ACCESSIBLE_TABLE_CAPTION_PROPERTY_NAME => Ok(Self::TableCaption( body.take_any_data() .try_into() .map_err(|_| AtspiError::ParseError(ACCESSIBLE_TABLE_CAPTION_PROPERTY_NAME))?, )), ACCESSIBLE_TABLE_COLUMN_DESCRIPTION_PROPERTY_NAME => { Ok(Self::TableColumnDescription(body.take_any_data().try_into().map_err(|_| { AtspiError::ParseError(ACCESSIBLE_TABLE_COLUMN_DESCRIPTION_PROPERTY_NAME) })?)) } ACCESSIBLE_TABLE_COLUMN_HEADER_PROPERTY_NAME => { Ok(Self::TableColumnHeader(body.take_any_data().try_into().map_err(|_| { AtspiError::ParseError(ACCESSIBLE_TABLE_COLUMN_HEADER_PROPERTY_NAME) })?)) } ACCESSIBLE_TABLE_ROW_DESCRIPTION_PROPERTY_NAME => { Ok(Self::TableRowDescription(body.take_any_data().try_into().map_err(|_| { AtspiError::ParseError(ACCESSIBLE_TABLE_ROW_DESCRIPTION_PROPERTY_NAME) })?)) } ACCESSIBLE_TABLE_ROW_HEADER_PROPERTY_NAME => { Ok(Self::TableRowHeader(body.take_any_data().try_into().map_err(|_| { AtspiError::ParseError(ACCESSIBLE_TABLE_ROW_HEADER_PROPERTY_NAME) })?)) } ACCESSIBLE_TABLE_SUMMARY_PROPERTY_NAME => Ok(Self::TableSummary( body.take_any_data() .try_into() .map_err(|_| AtspiError::ParseError(ACCESSIBLE_TABLE_SUMMARY_PROPERTY_NAME))?, )), ACCESSIBLE_HELP_TEXT_PROPERTY_NAME => Ok(Self::HelpText( body.take_any_data() .try_into() .map_err(|_| AtspiError::ParseError(ACCESSIBLE_HELP_TEXT_PROPERTY_NAME))?, )), _ => Ok(Self::Other((property.to_string(), body.take_any_data()))), } } } impl From for OwnedValue { fn from(property: Property) -> Self { let value = match property { Property::Name(name) => Value::from(name), Property::Description(description) => Value::from(description), Property::Role(role) => Value::from(role as u32), Property::Parent(parent) => Value::from(parent), Property::TableCaption(table_caption) => Value::from(table_caption), Property::TableColumnDescription(table_column_description) => { Value::from(table_column_description) } Property::TableColumnHeader(table_column_header) => Value::from(table_column_header), Property::TableRowDescription(table_row_description) => { Value::from(table_row_description) } Property::TableRowHeader(table_row_header) => Value::from(table_row_header), Property::TableSummary(table_summary) => Value::from(table_summary), Property::HelpText(help_text) => Value::from(help_text), Property::Other((_, value)) => value.into(), }; value.try_into().expect("Should succeed as there are no borrowed file descriptors involved that could, potentially, exceed the open file limit when converted to OwnedValue") } } #[cfg(test)] mod test_property { use crate::events::object::{Property, PropertyChangeEvent}; use crate::events::{EventBody, EventBodyOwned}; use crate::{ObjectRef, Role}; static TEST_OBJECT_REF: &ObjectRef = &ObjectRef::from_static_str_unchecked(":0.0", "/org/a11y/atspi/test/path"); macro_rules! property_subtype_test { ($name:ident, $key:expr, $prop:path, $val:expr) => { #[test] fn $name() { let prop = $prop($val); let prop_ev = PropertyChangeEvent { item: TEST_OBJECT_REF.clone().into(), property: $key.to_string(), value: prop.clone(), }; let ev_body: EventBodyOwned = prop_ev.try_into().expect("Valid event body!"); let ev: EventBody<'_> = ev_body.into(); let prop2: Property = ev.try_into().expect("Valid Property value"); assert_eq!(prop, prop2); } }; } property_subtype_test!( test_prop_type_desc, "accessible-description", Property::Description, "Accessible description text here!".to_string() ); property_subtype_test!( test_prop_type_name, "accessible-name", Property::Name, "Accessible name here!".to_string() ); property_subtype_test!(test_prop_type_role, "accessible-role", Property::Role, Role::Invalid); property_subtype_test!( test_prop_type_parent, "accessible-parent", Property::Parent, ObjectRef::from_static_str_unchecked(":420.69", "/fake/a11y/addr").into() ); property_subtype_test!( test_prop_type_table_caption, "accessible-table-caption", Property::TableCaption, "Accessible table description here".to_string() ); property_subtype_test!( test_prop_type_table_cd, "accessible-table-column-description", Property::TableColumnDescription, "Accessible table column description here!".to_string() ); property_subtype_test!( test_prop_type_table_ch, "accessible-table-column-header", Property::TableColumnHeader, "Accessible table column header here!".to_string() ); property_subtype_test!( test_prop_type_table_rd, "accessible-table-row-description", Property::TableRowDescription, "Accessible table row description here!".to_string() ); property_subtype_test!( test_prop_type_table_rh, "accessible-table-row-header", Property::TableRowHeader, "Accessible table row header here!".to_string() ); property_subtype_test!( test_prop_help_text, "accessible-help-text", Property::HelpText, "Accessible help text here!".to_string() ); } /// An event triggered when the visual bounds for an item have changed. /// This usually happens either: /// /// 1. due to a re-draw on a window whose size has changed and dynamically adjusted said item's visual size, or /// 2. content within the bounds of said item has changed to change its size. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct BoundsChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(BoundsChangedEvent); /// A link has been selected. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct LinkSelectedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(LinkSelectedEvent); /// A state of an object has been modified. /// A [`State`] can be added or removed from any [`crate::ObjectRef`]. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct StateChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, /// The state to be enabled/disabled. pub state: State, /// Whether the state was enabled or disabled. #[serde(with = "i32_bool_conversion")] pub enabled: bool, } impl_event_type_properties_for_event!(StateChangedEvent); mod i32_bool_conversion { use serde::{Deserialize, Deserializer, Serializer}; /// Convert an integer flag to a boolean. /// returns true if value is more than 0, otherwise false pub fn deserialize<'de, D>(de: D) -> Result where D: Deserializer<'de>, { let int: i32 = Deserialize::deserialize(de)?; Ok(int > 0) } /// Convert a boolean flag to an integer. /// returns 0 if false and 1 if true // TODO: Will the world REALLY fall apart if we were not to use a reference here? // In other words, see if &bool can be replaced with bool. #[allow(clippy::trivially_copy_pass_by_ref)] pub fn serialize(b: &bool, ser: S) -> Result where S: Serializer, { let val: i32 = (*b).into(); ser.serialize_i32(val) } } /// A child of `item` has been added or removed. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ChildrenChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, /// The [`crate::Operation`] being performed. pub operation: crate::Operation, /// Index to remove from/add to. pub index_in_parent: i32, /// A reference to the new child. pub child: ObjectRefOwned, } impl_event_type_properties_for_event!(ChildrenChangedEvent); /// A change in whether a particular item is visible or invisible (but still present). #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct VisibleDataChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(VisibleDataChangedEvent); /// The selection of this item has changed. /// For example: when a selection from a series of checkboxes is changed, this will change the state of the child, _and_ cause a [`SelectionChangedEvent`] on the parent. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct SelectionChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(SelectionChangedEvent); /// An event sent when the method of selecting items in a list/set of options changes. /// Also see: #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ModelChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(ModelChangedEvent); /// An event fired when the focus has moved within a tree. /// The parent: `item` and descendant (may not be a direct child): `descebdant` are both referenced for convenience. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ActiveDescendantChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, /// The descendant which is now the active one. pub descendant: ObjectRefOwned, } impl_event_type_properties_for_event!(ActiveDescendantChangedEvent); /// An announcement with a defined text string and an [ARIA politeness level](https://www.w3.org/TR/2009/WD-wai-aria-20091215/states_and_properties#aria-live). #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct AnnouncementEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, /// Text of the announcement. pub text: String, /// Politeness level. pub live: crate::Politeness, } impl_event_type_properties_for_event!(AnnouncementEvent); /// Signal that some attribute of an object (usually styling) has changed. /// This event does not encode _what_ has changed about the attributes, merely that they have /// changed. /// /// To query the updated information, use `atspi_proxies::AccessibleProxy`'s `get_attribute` method. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct AttributesChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(AttributesChangedEvent); /// A row has been added to a table. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct RowInsertedEvent { /// The table which has had a row inserted. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(RowInsertedEvent); /// A row has been moved within a table. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct RowReorderedEvent { /// The table which has had a row re-ordered. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(RowReorderedEvent); /// A row has been deleted from a table. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct RowDeletedEvent { /// The table which has had a row removed. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(RowDeletedEvent); /// A column has been added to a table. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ColumnInsertedEvent { /// The table which has had a column inserted. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(ColumnInsertedEvent); /// A column has been re-ordered within a table. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ColumnReorderedEvent { /// The table which has had a column re-ordered. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(ColumnReorderedEvent); /// A column has been removed from a table. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ColumnDeletedEvent { /// The table which has had a column removed. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(ColumnDeletedEvent); /// The bounds of a piece of text have changed. /// This event does _not_ specify what the new bounds are; it is only to notify an AT that the bounds have changed. /// To query information about the new state of the selection, use `atspi_proxies::TextProxy`'s `get_bounded_ranges` function. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct TextBoundsChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(TextBoundsChangedEvent); /// The user's selection of a piece of text has changed. /// This event does _not_ specify what the new selection is, nor its indecies; it is only to notify an AT that the selection has changed. /// To query information about the new state of the selection, use `atspi_proxies::TextProxy`'s methods. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct TextSelectionChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(TextSelectionChangedEvent); /// Text has changed within the UI element `item`. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct TextChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, /// The [`crate::Operation`] being performed. pub operation: crate::Operation, /// starting index of the insertion/deletion /// /// NOTE: This gives the Unicode index (not the byte index). I.e., it groups unicode sequences /// into one character. /// Always use the appropriate insertion methods to deal with this, i.e., do not use /// [`String::insert_str`]. pub start_pos: i32, /// length of the insertion/deletion /// /// NOTE: This gives the unicode length (not the byte length). pub length: i32, /// the text being inserted/deleted pub text: String, } impl_event_type_properties_for_event!(TextChangedEvent); /// Signal that some attributes about the text (usually styling) have changed. /// This event does not encode _what_ has changed about the attributes, merely that they have /// changed. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct TextAttributesChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(TextAttributesChangedEvent); /// The caret of the user also known as a cursor (not to be confused with mouse pointer) has changed position. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct TextCaretMovedEvent { /// The object on which the caret has been moved on. pub item: ObjectRefOwned, /// New position of the caret. /// NOTE: this provide the Unicode index (not the byte index) and therefore when referencing /// locations in a string, you should be using the [`std::str::Chars`] iterator, and not use /// anything like [`str::get`] (as this uses the byte index). /// /// See also: [`TextChangedEvent`]. pub position: i32, } impl_event_type_properties_for_event!(TextCaretMovedEvent); impl_member_interface_registry_string_and_match_rule_for_event!( PropertyChangeEvent, "PropertyChange", "org.a11y.atspi.Event.Object", "object:property-change", "type='signal',interface='org.a11y.atspi.Event.Object',member='PropertyChange'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for PropertyChangeEvent { type Body<'b> = EventBody<'b>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let mut body = body.deserialize_unchecked::>()?; let property: String = body.take_kind(); let value: Property = body.try_into()?; Ok(Self { item: item.into(), property, value }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { let copy = self.clone(); EventBodyOwned::from(copy).into() } } impl_member_interface_registry_string_and_match_rule_for_event!( BoundsChangedEvent, "BoundsChanged", "org.a11y.atspi.Event.Object", "object:bounds-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='BoundsChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( LinkSelectedEvent, "LinkSelected", "org.a11y.atspi.Event.Object", "object:link-selected", "type='signal',interface='org.a11y.atspi.Event.Object',member='LinkSelected'" ); impl_member_interface_registry_string_and_match_rule_for_event!( StateChangedEvent, "StateChanged", "org.a11y.atspi.Event.Object", "object:state-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='StateChanged'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for StateChangedEvent { type Body<'a> = EventBody<'a>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let body: Self::Body<'_> = body.deserialize_unchecked()?; Ok(Self { item: item.into(), state: body.kind().into(), enabled: body.detail1() > 0 }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { let copy = self.clone(); copy.into() } } impl_member_interface_registry_string_and_match_rule_for_event!( ChildrenChangedEvent, "ChildrenChanged", "org.a11y.atspi.Event.Object", "object:children-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='ChildrenChanged'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for ChildrenChangedEvent { type Body<'a> = EventBody<'a>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let mut body = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), operation: body.kind().parse()?, index_in_parent: body.detail1(), child: body.take_any_data().try_into()?, }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { EventBodyOwned::from(self.clone()).into() } } impl_member_interface_registry_string_and_match_rule_for_event!( VisibleDataChangedEvent, "VisibleDataChanged", "org.a11y.atspi.Event.Object", "object:visible-data-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='VisibleDataChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( SelectionChangedEvent, "SelectionChanged", "org.a11y.atspi.Event.Object", "object:selection-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='SelectionChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ModelChangedEvent, "ModelChanged", "org.a11y.atspi.Event.Object", "object:model-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='ModelChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ActiveDescendantChangedEvent, "ActiveDescendantChanged", "org.a11y.atspi.Event.Object", "object:active-descendant-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='ActiveDescendantChanged'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for ActiveDescendantChangedEvent { type Body<'a> = EventBody<'a>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let mut body = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), descendant: body.take_any_data().try_into()? }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { EventBodyOwned::from(self.clone()).into() } } impl_member_interface_registry_string_and_match_rule_for_event!( AnnouncementEvent, "Announcement", "org.a11y.atspi.Event.Object", "object:announcement", "type='signal',interface='org.a11y.atspi.Event.Object',member='Announcement'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for AnnouncementEvent { type Body<'a> = EventBody<'a>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let mut body = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), text: body .take_any_data() .try_into() .map_err(|_| AtspiError::Conversion("text"))?, live: body.detail1().try_into()?, }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { EventBodyOwned::from(self.clone()).into() } } impl_member_interface_registry_string_and_match_rule_for_event!( AttributesChangedEvent, "AttributesChanged", "org.a11y.atspi.Event.Object", "object:attributes-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='AttributesChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( RowInsertedEvent, "RowInserted", "org.a11y.atspi.Event.Object", "object:row-inserted", "type='signal',interface='org.a11y.atspi.Event.Object',member='RowInserted'" ); impl_member_interface_registry_string_and_match_rule_for_event!( RowReorderedEvent, "RowReordered", "org.a11y.atspi.Event.Object", "object:row-reordered", "type='signal',interface='org.a11y.atspi.Event.Object',member='RowReordered'" ); impl_member_interface_registry_string_and_match_rule_for_event!( RowDeletedEvent, "RowDeleted", "org.a11y.atspi.Event.Object", "object:row-deleted", "type='signal',interface='org.a11y.atspi.Event.Object',member='RowDeleted'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ColumnInsertedEvent, "ColumnInserted", "org.a11y.atspi.Event.Object", "object:column-inserted", "type='signal',interface='org.a11y.atspi.Event.Object',member='ColumnInserted'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ColumnReorderedEvent, "ColumnReordered", "org.a11y.atspi.Event.Object", "object:column-reordered", "type='signal',interface='org.a11y.atspi.Event.Object',member='ColumnReordered'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ColumnDeletedEvent, "ColumnDeleted", "org.a11y.atspi.Event.Object", "object:column-deleted", "type='signal',interface='org.a11y.atspi.Event.Object',member='ColumnDeleted'" ); impl_member_interface_registry_string_and_match_rule_for_event!( TextBoundsChangedEvent, "TextBoundsChanged", "org.a11y.atspi.Event.Object", "object:text-bounds-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='TextBoundsChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( TextSelectionChangedEvent, "TextSelectionChanged", "org.a11y.atspi.Event.Object", "object:text-selection-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='TextSelectionChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( TextChangedEvent, "TextChanged", "org.a11y.atspi.Event.Object", "object:text-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='TextChanged'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for TextChangedEvent { type Body<'a> = EventBody<'a>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let mut body = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), operation: body.kind().parse()?, start_pos: body.detail1(), length: body.detail2(), text: body.take_any_data().try_into()?, }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { EventBodyOwned::from(self.clone()).into() } } impl_member_interface_registry_string_and_match_rule_for_event!( TextAttributesChangedEvent, "TextAttributesChanged", "org.a11y.atspi.Event.Object", "object:text-attributes-changed", "type='signal',interface='org.a11y.atspi.Event.Object',member='TextAttributesChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( TextCaretMovedEvent, "TextCaretMoved", "org.a11y.atspi.Event.Object", "object:text-caret-moved", "type='signal',interface='org.a11y.atspi.Event.Object',member='TextCaretMoved'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for TextCaretMovedEvent { type Body<'a> = EventBody<'a>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let body = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), position: body.detail1() }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { EventBodyOwned::from(self.clone()).into() } } event_test_cases!(PropertyChangeEvent); impl_to_dbus_message!(PropertyChangeEvent); impl_from_dbus_message!(PropertyChangeEvent); impl_event_properties!(PropertyChangeEvent); impl From for EventBodyOwned { fn from(event: PropertyChangeEvent) -> Self { EventBodyOwned { kind: event.property, any_data: event.value.into(), ..Default::default() } } } impl From<&PropertyChangeEvent> for EventBodyOwned { fn from(event: &PropertyChangeEvent) -> Self { EventBodyOwned { kind: event.property.clone(), any_data: event.value.clone().into(), ..Default::default() } } } impl From for EventBody<'_> { fn from(event: PropertyChangeEvent) -> Self { EventBodyOwned::from(event).into() } } event_test_cases!(BoundsChangedEvent); impl_to_dbus_message!(BoundsChangedEvent); impl_from_dbus_message!(BoundsChangedEvent); impl_event_properties!(BoundsChangedEvent); impl_from_object_ref!(BoundsChangedEvent); event_test_cases!(LinkSelectedEvent); impl_to_dbus_message!(LinkSelectedEvent); impl_from_dbus_message!(LinkSelectedEvent); impl_event_properties!(LinkSelectedEvent); impl_from_object_ref!(LinkSelectedEvent); event_test_cases!(StateChangedEvent); impl_to_dbus_message!(StateChangedEvent); impl_from_dbus_message!(StateChangedEvent); impl_event_properties!(StateChangedEvent); impl From for EventBodyOwned { fn from(event: StateChangedEvent) -> Self { EventBodyOwned { kind: event.state.to_string(), detail1: event.enabled.into(), ..Default::default() } } } impl From<&StateChangedEvent> for EventBodyOwned { fn from(event: &StateChangedEvent) -> Self { EventBodyOwned { kind: event.state.to_string(), detail1: event.enabled.into(), ..Default::default() } } } impl From for EventBody<'_> { fn from(event: StateChangedEvent) -> Self { EventBodyOwned::from(event).into() } } event_test_cases!(ChildrenChangedEvent); impl_to_dbus_message!(ChildrenChangedEvent); impl_from_dbus_message!(ChildrenChangedEvent); impl_event_properties!(ChildrenChangedEvent); impl From for EventBodyOwned { fn from(event: ChildrenChangedEvent) -> Self { EventBodyOwned { kind: event.operation.to_string(), detail1: event.index_in_parent, // `OwnedValue` is constructed from the `crate::ObjectRef` // Only way to fail is to convert a `Fd` into an `OwnedValue`. // Therefore, this is safe. any_data: Value::from(event.child) .try_into() .expect("Failed to convert child to OwnedValue"), ..Default::default() } } } impl From<&ChildrenChangedEvent> for EventBodyOwned { fn from(event: &ChildrenChangedEvent) -> Self { EventBodyOwned { kind: event.operation.to_string(), detail1: event.index_in_parent, detail2: i32::default(), // `OwnedValue` is constructed from the `crate::ObjectRef` // Only path to fail is to convert a `Fd` into an `OwnedValue`. // Therefore, this is safe. any_data: Value::from(event.child.clone()) .try_into() .expect("ObjectRef should convert to OwnedValue without error"), properties: super::event_body::Properties, } } } impl From for EventBody<'_> { fn from(event: ChildrenChangedEvent) -> Self { EventBodyOwned::from(event).into() } } event_test_cases!(VisibleDataChangedEvent); impl_to_dbus_message!(VisibleDataChangedEvent); impl_from_dbus_message!(VisibleDataChangedEvent); impl_event_properties!(VisibleDataChangedEvent); impl_from_object_ref!(VisibleDataChangedEvent); event_test_cases!(SelectionChangedEvent); impl_to_dbus_message!(SelectionChangedEvent); impl_from_dbus_message!(SelectionChangedEvent); impl_event_properties!(SelectionChangedEvent); impl_from_object_ref!(SelectionChangedEvent); event_test_cases!(ModelChangedEvent); impl_to_dbus_message!(ModelChangedEvent); impl_from_dbus_message!(ModelChangedEvent); impl_event_properties!(ModelChangedEvent); impl_from_object_ref!(ModelChangedEvent); event_test_cases!(ActiveDescendantChangedEvent); impl_to_dbus_message!(ActiveDescendantChangedEvent); impl_from_dbus_message!(ActiveDescendantChangedEvent); impl_event_properties!(ActiveDescendantChangedEvent); impl From for EventBodyOwned { fn from(event: ActiveDescendantChangedEvent) -> Self { EventBodyOwned { // `OwnedValue` is constructed from the `crate::ObjectRef` // Only way to fail is to convert a Fd into an `OwnedValue`. // Therefore, this is safe. any_data: Value::from(event.descendant) .try_to_owned() .expect("Failed to convert descendant to OwnedValue"), ..Default::default() } } } event_test_cases!(AnnouncementEvent); impl_to_dbus_message!(AnnouncementEvent); impl_from_dbus_message!(AnnouncementEvent); impl_event_properties!(AnnouncementEvent); impl From for EventBodyOwned { fn from(event: AnnouncementEvent) -> Self { EventBodyOwned { detail1: event.live as i32, // `OwnedValue` is constructed from `String` // Therefore, this is safe. any_data: Value::from(event.text) .try_to_owned() .expect("Failed to convert text to OwnedValue"), ..Default::default() } } } event_test_cases!(AttributesChangedEvent); impl_to_dbus_message!(AttributesChangedEvent); impl_from_dbus_message!(AttributesChangedEvent); impl_event_properties!(AttributesChangedEvent); impl_from_object_ref!(AttributesChangedEvent); event_test_cases!(RowInsertedEvent); impl_to_dbus_message!(RowInsertedEvent); impl_from_dbus_message!(RowInsertedEvent); impl_event_properties!(RowInsertedEvent); impl_from_object_ref!(RowInsertedEvent); event_test_cases!(RowReorderedEvent); impl_to_dbus_message!(RowReorderedEvent); impl_from_dbus_message!(RowReorderedEvent); impl_event_properties!(RowReorderedEvent); impl_from_object_ref!(RowReorderedEvent); event_test_cases!(RowDeletedEvent); impl_to_dbus_message!(RowDeletedEvent); impl_from_dbus_message!(RowDeletedEvent); impl_event_properties!(RowDeletedEvent); impl_from_object_ref!(RowDeletedEvent); event_test_cases!(ColumnInsertedEvent); impl_to_dbus_message!(ColumnInsertedEvent); impl_from_dbus_message!(ColumnInsertedEvent); impl_event_properties!(ColumnInsertedEvent); impl_from_object_ref!(ColumnInsertedEvent); event_test_cases!(ColumnReorderedEvent); impl_to_dbus_message!(ColumnReorderedEvent); impl_from_dbus_message!(ColumnReorderedEvent); impl_event_properties!(ColumnReorderedEvent); impl_from_object_ref!(ColumnReorderedEvent); event_test_cases!(ColumnDeletedEvent); impl_to_dbus_message!(ColumnDeletedEvent); impl_from_dbus_message!(ColumnDeletedEvent); impl_event_properties!(ColumnDeletedEvent); impl_from_object_ref!(ColumnDeletedEvent); event_test_cases!(TextBoundsChangedEvent); impl_to_dbus_message!(TextBoundsChangedEvent); impl_from_dbus_message!(TextBoundsChangedEvent); impl_event_properties!(TextBoundsChangedEvent); impl_from_object_ref!(TextBoundsChangedEvent); event_test_cases!(TextSelectionChangedEvent); impl_to_dbus_message!(TextSelectionChangedEvent); impl_from_dbus_message!(TextSelectionChangedEvent); impl_event_properties!(TextSelectionChangedEvent); impl_from_object_ref!(TextSelectionChangedEvent); event_test_cases!(TextChangedEvent); assert_impl_all!(TextChangedEvent:Clone,std::fmt::Debug,serde::Serialize,serde::Deserialize<'static>,Default,PartialEq,Eq,std::hash::Hash,crate::EventProperties,crate::EventTypeProperties); #[cfg(feature = "zbus")] assert_impl_all!(zbus::Message:TryFrom); impl_to_dbus_message!(TextChangedEvent); impl_from_dbus_message!(TextChangedEvent); impl_event_properties!(TextChangedEvent); impl From for EventBodyOwned { fn from(event: TextChangedEvent) -> Self { EventBodyOwned { kind: event.operation.to_string(), detail1: event.start_pos, detail2: event.length, // `OwnedValue` is constructed from a `String` // Therefore, this is safe. any_data: Value::from(event.text) .try_to_owned() .expect("Failed to convert child to OwnedValue"), ..Default::default() } } } event_test_cases!(TextAttributesChangedEvent); impl_to_dbus_message!(TextAttributesChangedEvent); impl_from_dbus_message!(TextAttributesChangedEvent); impl_event_properties!(TextAttributesChangedEvent); impl_from_object_ref!(TextAttributesChangedEvent); event_test_cases!(TextCaretMovedEvent); impl_to_dbus_message!(TextCaretMovedEvent); impl_from_dbus_message!(TextCaretMovedEvent); impl_event_properties!(TextCaretMovedEvent); impl From for EventBodyOwned { fn from(event: TextCaretMovedEvent) -> Self { EventBodyOwned { detail1: event.position, ..Default::default() } } } impl_msg_conversion_ext_for_target_type!(PropertyChangeEvent); impl_msg_conversion_ext_for_target_type!(BoundsChangedEvent); impl_msg_conversion_ext_for_target_type!(LinkSelectedEvent); impl_msg_conversion_ext_for_target_type!(StateChangedEvent); impl_msg_conversion_ext_for_target_type!(ChildrenChangedEvent); impl_msg_conversion_ext_for_target_type!(VisibleDataChangedEvent); impl_msg_conversion_ext_for_target_type!(SelectionChangedEvent); impl_msg_conversion_ext_for_target_type!(ModelChangedEvent); impl_msg_conversion_ext_for_target_type!(ActiveDescendantChangedEvent); impl_msg_conversion_ext_for_target_type!(AnnouncementEvent); impl_msg_conversion_ext_for_target_type!(AttributesChangedEvent); impl_msg_conversion_ext_for_target_type!(RowInsertedEvent); impl_msg_conversion_ext_for_target_type!(RowReorderedEvent); impl_msg_conversion_ext_for_target_type!(RowDeletedEvent); impl_msg_conversion_ext_for_target_type!(ColumnInsertedEvent); impl_msg_conversion_ext_for_target_type!(ColumnReorderedEvent); impl_msg_conversion_ext_for_target_type!(ColumnDeletedEvent); impl_msg_conversion_ext_for_target_type!(TextBoundsChangedEvent); impl_msg_conversion_ext_for_target_type!(TextSelectionChangedEvent); impl_msg_conversion_ext_for_target_type!(TextChangedEvent); impl_msg_conversion_ext_for_target_type!(TextAttributesChangedEvent); impl_msg_conversion_ext_for_target_type!(TextCaretMovedEvent); impl_msg_conversion_for_types_built_from_object_ref!(BoundsChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(LinkSelectedEvent); impl_msg_conversion_for_types_built_from_object_ref!(VisibleDataChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(SelectionChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(ModelChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(AttributesChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(RowInsertedEvent); impl_msg_conversion_for_types_built_from_object_ref!(RowReorderedEvent); impl_msg_conversion_for_types_built_from_object_ref!(RowDeletedEvent); impl_msg_conversion_for_types_built_from_object_ref!(ColumnInsertedEvent); impl_msg_conversion_for_types_built_from_object_ref!(ColumnReorderedEvent); impl_msg_conversion_for_types_built_from_object_ref!(ColumnDeletedEvent); impl_msg_conversion_for_types_built_from_object_ref!(TextBoundsChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(TextSelectionChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(TextAttributesChangedEvent); atspi-common-0.13.0/src/events/registry.rs000064400000000000000000000176271046102023000166410ustar 00000000000000//! This module contains the events that are emitted by the registry daemon. //! The events are [`EventListenerRegisteredEvent`] and [`EventListenerDeregisteredEvent`]. #[cfg(feature = "zbus")] use crate::object_ref::ObjectRef; #[cfg(feature = "zbus")] use crate::{error::AtspiError, events::MessageConversion, EventProperties}; use serde::{Deserialize, Serialize}; #[cfg(feature = "zbus")] use zbus::message::{Body as DbusBody, Header}; use zbus_lockstep_macros::validate; use zbus_names::{OwnedUniqueName, UniqueName}; use zvariant::Type; use crate::{ events::{DBusInterface, DBusMatchRule, DBusMember, RegistryEventString}, object_ref::ObjectRefOwned, }; /// An event that is emitted by the registry daemon, to inform that an event has been deregistered /// to no longer listen for. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)] pub struct EventListenerDeregisteredEvent { /// The [`ObjectRef`] the event applies to. pub item: ObjectRefOwned, /// A list of events that have been deregistered via the registry interface. /// See `atspi-connection`. pub deregistered_event: EventListeners, } impl_event_type_properties_for_event!(EventListenerDeregisteredEvent); event_test_cases!(EventListenerDeregisteredEvent, Explicit); // The registry string cannot be found in upstrream at-spi2-core. impl_member_interface_registry_string_and_match_rule_for_event!( EventListenerDeregisteredEvent, "EventListenerDeregistered", "org.a11y.atspi.Registry", "registry:event-listener-deregistered", "type='signal',interface='org.a11y.atspi.Registry',member='EventListenerDeregistered'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for EventListenerDeregisteredEvent { type Body<'a> = EventListeners; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let deregistered_event = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), deregistered_event }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { self.deregistered_event.clone() } } impl_msg_conversion_ext_for_target_type_with_specified_body_type!(target: EventListenerDeregisteredEvent, body: EventListeners); impl_from_dbus_message!(EventListenerDeregisteredEvent, Explicit); impl_event_properties!(EventListenerDeregisteredEvent); impl_to_dbus_message!(EventListenerDeregisteredEvent); /// An event that is emitted by the regostry daemon to signal that an event has been registered to listen for. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq, Hash)] pub struct EventListenerRegisteredEvent { /// The [`ObjectRef`] the event applies to. pub item: ObjectRefOwned, /// A list of events that have been registered via the registry interface. /// See `atspi-connection`. pub registered_event: EventListeners, } impl_event_type_properties_for_event!(EventListenerRegisteredEvent); #[cfg(feature = "zbus")] impl MessageConversion<'_> for EventListenerRegisteredEvent { type Body<'a> = EventListeners; fn from_message_unchecked_parts( item: ObjectRef, registered_event: DbusBody, ) -> Result { let registered_event = registered_event.deserialize_unchecked()?; Ok(Self { item: item.into(), registered_event }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { self.registered_event.clone() } } impl_msg_conversion_ext_for_target_type_with_specified_body_type!(target: EventListenerRegisteredEvent, body: EventListeners); impl_from_dbus_message!(EventListenerRegisteredEvent, Explicit); impl_event_properties!(EventListenerRegisteredEvent); impl_to_dbus_message!(EventListenerRegisteredEvent); event_test_cases!(EventListenerRegisteredEvent, Explicit); // The registry string cannot be found in upstrream at-spi2-core. impl_member_interface_registry_string_and_match_rule_for_event!( EventListenerRegisteredEvent, "EventListenerRegistered", "org.a11y.atspi.Registry", "registry:event-listener-registered", "type='signal',interface='org.a11y.atspi.Registry',member='EventListenerRegistered'" ); /// Signal type emitted by `EventListenerRegistered` and `EventListenerDeregistered` signals, /// which belong to the `Registry` interface, implemented by the registry-daemon. #[validate(signal: "EventListenerRegistered")] #[derive(Debug, Clone, Serialize, Deserialize, Type, PartialEq, Eq, Hash)] pub struct EventListeners { pub bus_name: OwnedUniqueName, // TODO: `path` should be a `zvariant::ObjectPath` but that requires changing the signature with an attribute // and `Serialize`/`Deserialize` impls. pub path: String, } impl Default for EventListeners { fn default() -> Self { Self { bus_name: UniqueName::from_static_str_unchecked(":0.0").into(), path: String::from("/org/a11y/atspi/null"), } } } #[cfg(test)] mod event_listener_tests { use super::*; #[test] fn test_event_listener_default_no_panic() { let el = EventListeners::default(); assert_eq!(el.bus_name.as_str(), ":0.0"); assert_eq!(el.path.as_str(), "/org/a11y/atspi/null"); } } pub mod socket { //! This module contains the event that is emitted by the registry daemon's `Socket` interface. #[cfg(feature = "zbus")] use crate::events::MessageConversion; use crate::events::{DBusInterface, DBusMatchRule, DBusMember, RegistryEventString}; use crate::object_ref::ObjectRefOwned; #[cfg(feature = "zbus")] use crate::AtspiError; #[cfg(feature = "zbus")] use crate::EventProperties; #[cfg(feature = "zbus")] use crate::ObjectRef; #[cfg(feature = "zbus")] use zbus::message::{Body as DbusBody, Header}; use serde::{Deserialize, Serialize}; /// An event that is emitted when the registry daemon has started. /// /// The accessibility registry emits this signal early during startup, /// when it has registered with the `DBus` daemon and is available for /// calls from applications. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Eq, Hash)] pub struct AvailableEvent { /// The emitting [`ObjectRef`]. pub item: ObjectRefOwned, /// The [`ObjectRef`] for the Registry's root object. pub socket: ObjectRefOwned, } impl_event_type_properties_for_event!(AvailableEvent); event_test_cases!(AvailableEvent, Explicit); // We cannot register at Registry for this event, as it is emitted by the Registry itself at early startup. // So, we do not have a registry event string for this event. // The `Available` event is unconditionally emitted: // [at-spi2-core/registryd/registry.c:1437](https://github.com/GNOME/at-spi2-core/blob/019d1a4013216d7d01040cf4eb3b8647bffc0dc9/registryd/registry.c#L1437) impl_member_interface_registry_string_and_match_rule_for_event!( AvailableEvent, "Available", "org.a11y.atspi.Socket", "", "type='signal',interface='org.a11y.atspi.Socket',member='Available'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for AvailableEvent { type Body<'a> = ObjectRef<'a>; fn from_message_unchecked_parts( item: ObjectRef<'_>, body: DbusBody, ) -> Result { let socket = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), socket: socket.into() }) } fn from_message_unchecked( msg: &zbus::Message, header: &Header, ) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { self.socket.clone().into_inner() } } impl_msg_conversion_ext_for_target_type_with_specified_body_type!(target: AvailableEvent, body: ObjectRef<'a>); impl_from_dbus_message!(AvailableEvent, Explicit); impl_event_properties!(AvailableEvent); impl_to_dbus_message!(AvailableEvent); } atspi-common-0.13.0/src/events/terminal.rs000064400000000000000000000123001046102023000165630ustar 00000000000000#[cfg(feature = "zbus")] use crate::error::AtspiError; #[cfg(feature = "zbus")] use crate::EventProperties; use crate::{ events::{DBusInterface, DBusMatchRule, DBusMember, RegistryEventString}, object_ref::ObjectRefOwned, }; #[cfg(feature = "zbus")] use zbus::message::Header; /// A line of text has been changed. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct LineChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(LineChangedEvent); /// The width of a terminal emulator has changed sufficiently such that the number of characters /// able to fit on one *visual* line has changed. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ColumnCountChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(ColumnCountChangedEvent); /// The height of a terminal emulator has changed sufficiently such that the number of lines /// able to fit within the terminal has changed. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct LineCountChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(LineCountChangedEvent); #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ApplicationChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(ApplicationChangedEvent); /// The width of a terminal emulator has changed sufficiently such that the number of characters /// able to fit on one *visual* line has changed. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct CharWidthChangedEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(CharWidthChangedEvent); impl_member_interface_registry_string_and_match_rule_for_event!( LineChangedEvent, "LineChanged", "org.a11y.atspi.Event.Terminal", "terminal:line-changed", "type='signal',interface='org.a11y.atspi.Event.Terminal',member='LineChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ColumnCountChangedEvent, "ColumncountChanged", "org.a11y.atspi.Event.Terminal", "terminal:columncount-changed", "type='signal',interface='org.a11y.atspi.Event.Terminal',member='ColumncountChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( LineCountChangedEvent, "LinecountChanged", "org.a11y.atspi.Event.Terminal", "terminal:linecount-changed", "type='signal',interface='org.a11y.atspi.Event.Terminal',member='LinecountChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ApplicationChangedEvent, "ApplicationChanged", "org.a11y.atspi.Event.Terminal", "terminal:application-changed", "type='signal',interface='org.a11y.atspi.Event.Terminal',member='ApplicationChanged'" ); impl_member_interface_registry_string_and_match_rule_for_event!( CharWidthChangedEvent, "CharwidthChanged", "org.a11y.atspi.Event.Terminal", "terminal:char-width-changed", "type='signal',interface='org.a11y.atspi.Event.Terminal',member='CharwidthChanged'" ); event_test_cases!(LineChangedEvent); impl_to_dbus_message!(LineChangedEvent); impl_from_dbus_message!(LineChangedEvent); impl_event_properties!(LineChangedEvent); impl_from_object_ref!(LineChangedEvent); event_test_cases!(ColumnCountChangedEvent); impl_to_dbus_message!(ColumnCountChangedEvent); impl_from_dbus_message!(ColumnCountChangedEvent); impl_event_properties!(ColumnCountChangedEvent); impl_from_object_ref!(ColumnCountChangedEvent); event_test_cases!(LineCountChangedEvent); impl_to_dbus_message!(LineCountChangedEvent); impl_from_dbus_message!(LineCountChangedEvent); impl_event_properties!(LineCountChangedEvent); impl_from_object_ref!(LineCountChangedEvent); event_test_cases!(ApplicationChangedEvent); impl_to_dbus_message!(ApplicationChangedEvent); impl_from_dbus_message!(ApplicationChangedEvent); impl_event_properties!(ApplicationChangedEvent); impl_from_object_ref!(ApplicationChangedEvent); event_test_cases!(CharWidthChangedEvent); impl_to_dbus_message!(CharWidthChangedEvent); impl_from_dbus_message!(CharWidthChangedEvent); impl_event_properties!(CharWidthChangedEvent); impl_from_object_ref!(CharWidthChangedEvent); impl_msg_conversion_ext_for_target_type!(LineChangedEvent); impl_msg_conversion_ext_for_target_type!(ColumnCountChangedEvent); impl_msg_conversion_ext_for_target_type!(LineCountChangedEvent); impl_msg_conversion_ext_for_target_type!(ApplicationChangedEvent); impl_msg_conversion_ext_for_target_type!(CharWidthChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(LineChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(ColumnCountChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(LineCountChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(ApplicationChangedEvent); impl_msg_conversion_for_types_built_from_object_ref!(CharWidthChangedEvent); atspi-common-0.13.0/src/events/traits.rs000064400000000000000000000221671046102023000162720ustar 00000000000000#[cfg(feature = "zbus")] use crate::AtspiError; use crate::ObjectRef; #[cfg(feature = "zbus")] use serde::{Deserialize, Serialize}; #[cfg(feature = "zbus")] use zbus::message::{Body as DbusBody, Header}; use zbus_names::UniqueName; use zvariant::ObjectPath; #[cfg(feature = "zbus")] use zvariant::Type; /// Describes properties of a specific event _type_. /// /// - `DBus` member name /// - `DBus` interface name /// /// Together, the member and interface name can describe a specific event _type_. /// Likewise, the path and sender bus name collectively make up an [`ObjectRef`], which is a way to uniquely identify an individual accessible item available to `atspi`. /// The latter is available via the [`EventProperties`] trait. /// /// This can also be generalized, for example this is implemented for [`crate::Event`] by dispatching to the matching variants. /// NOTE: to use `EventProperties` on wrapper types, like `Event`, you must enable the "enum-dispatch" feature. /// /// This trait *is* object-safe. pub trait EventTypeProperties { fn member(&self) -> &'static str; fn interface(&self) -> &'static str; fn match_rule(&self) -> &'static str; fn registry_string(&self) -> &'static str; } /// `EventProperties` allows access to the internals of an event, specifically: /// /// - The `DBUs` name which sent the event. /// - The `ObjectPath`, a unique id for a given application. /// - Collectively, this is called an [`ObjectRef`]. /// /// This trait *is* object-safe. pub trait EventProperties { fn sender(&self) -> UniqueName<'_>; fn path(&self) -> ObjectPath<'_>; fn object_ref(&self) -> ObjectRef<'_> { ObjectRef::new(self.sender(), self.path()) } } assert_obj_safe!(EventTypeProperties); assert_obj_safe!(EventProperties); /// A way to convert a [`zbus::Message`] without checking its interface. #[cfg(all(feature = "zbus", feature = "wrappers"))] pub(crate) trait EventWrapperMessageConversion { /// # Errors /// Will fail if no matching member or body signature is found. fn try_from_message_interface_checked( msg: &zbus::Message, hdr: &Header, ) -> Result where Self: Sized; } // TODO: Document why this can't be `TryFrom<&zbus::Message>`. #[cfg(all(feature = "zbus", feature = "wrappers"))] pub(crate) trait TryFromMessage { fn try_from_message(msg: &zbus::Message) -> Result where Self: Sized; } /// The `DBus` member for the event. /// For example, for an [`crate::events::object::TextChangedEvent`] this should be `"TextChanged"` pub trait DBusMember { /// The event's `DBus` member. const DBUS_MEMBER: &'static str; } /// The `DBus` interface name for an event - or a wrapper type. /// For example, for any event within the "Object" interface, this should be "org.a11y.atspi.Event.Object". pub trait DBusInterface { /// A static interface string for `DBus`. /// This should usually be a string that looks like this: `"org.a11y.atspi.Event.*"`; const DBUS_INTERFACE: &'static str; } /// A static `DBus` match rule string. /// This should usually be a string that looks like this: /// `"type='signal',interface='org.a11y.atspi.Event.Object',member='PropertyChange'"`; // We cannot concat! consts, so we (at time of writing) need to have a separate trait for this. // Otherwise composing from `DBusMember` and `DBusInterface` would be preferred. pub trait DBusMatchRule { /// A static match rule string for `DBus`. const MATCH_RULE_STRING: &'static str; } /// A static `Registry` event string for registering with the `RegistryProxy` for receiving events. pub trait RegistryEventString { /// A registry event string for registering for event receiving via the `RegistryProxy`. /// This should be deprecated in favour of composing the string from [`DBusMember::DBUS_MEMBER`] and [`DBusInterface::DBUS_INTERFACE`]. const REGISTRY_EVENT_STRING: &'static str; } /// A 'alias'-trait that combines all the `DBus` related traits. pub trait DBusProperties: DBusMember + DBusInterface + DBusMatchRule + RegistryEventString {} #[cfg(feature = "zbus")] pub trait MessageConversionExt<'a, B>: 'a + MessageConversion<'a, Body<'a> = B> where B: Type + Serialize + Deserialize<'a>, { /// Convert a [`zbus::Message`] into this event type. /// Does all the validation for you. /// /// # Errors /// /// - The message does not have an interface: [`type@AtspiError::MissingInterface`] /// - The message interface does not match the one for the event: [`type@AtspiError::InterfaceMatch`] /// - The message does not have an member: [`type@AtspiError::MissingMember`] /// - The message member does not match the one for the event: [`type@AtspiError::MemberMatch`] /// - The message signature does not match the one for the event: [`type@AtspiError::SignatureMatch`] /// /// See [`MessageConversion::from_message_unchecked`] for info on panic condition that should never /// happen. fn try_from_message(msg: &'a zbus::Message, hdr: &Header) -> Result where Self: Sized + 'a; /// Validate the interface string via [`zbus::message::Header::interface`] against `Self`'s assignment of [`DBusInterface::DBUS_INTERFACE`] /// /// # Errors /// /// - [`type@AtspiError::MissingInterface`] if there is no interface /// - [`type@AtspiError::InterfaceMatch`] if the interfaces do not match fn validate_interface(header: &Header) -> Result<(), AtspiError> { let interface = header.interface().ok_or(AtspiError::MissingInterface)?; if interface != Self::DBUS_INTERFACE { return Err(AtspiError::InterfaceMatch(format!( "The interface {} does not match the signal's interface: {}", interface, Self::DBUS_INTERFACE, ))); } Ok(()) } /// Validate the member string via [`zbus::message::Header::member`] against `Self`'s assignment of [`DBusMember::DBUS_MEMBER`] /// /// # Errors /// /// - [`type@AtspiError::MissingMember`] if there is no member /// - [`type@AtspiError::MemberMatch`] if the members do not match fn validate_member(hdr: &Header) -> Result<(), AtspiError> { let member = hdr.member().ok_or(AtspiError::MissingMember)?; if member != Self::DBUS_MEMBER { return Err(AtspiError::MemberMatch(format!( "The member {} does not match the signal's member: {}", // unwrap is safe here because of guard above member, Self::DBUS_MEMBER, ))); } Ok(()) } /// Validate the body signature against the [`zvariant::Signature`] of [`MessageConversion::Body`] /// /// # Errors /// /// - [`type@AtspiError::SignatureMatch`] if the signatures do not match fn validate_body(msg: &zbus::Message) -> Result<(), AtspiError> { let body = msg.body(); let body_signature = body.signature(); let expected_signature = B::SIGNATURE; if body_signature != expected_signature { return Err(AtspiError::SignatureMatch(format!( "The message signature {} does not match the signal's body signature: {}", body_signature, &expected_signature.to_string(), ))); } Ok(()) } } #[cfg(feature = "zbus")] pub trait MessageConversion<'a>: DBusProperties { /// What is the body type of this event. type Body<'msg>: Type + Deserialize<'msg> + Serialize where Self: 'msg; /// Build an event from a [`zbus::Message`] reference. /// This function will not check for any of the following error conditions: /// /// - That the message has an interface: [`type@AtspiError::MissingInterface`] /// - That the message interface matches the one for the event: [`type@AtspiError::InterfaceMatch`] /// - That the message has an member: [`type@AtspiError::MissingMember`] /// - That the message member matches the one for the event: [`type@AtspiError::MemberMatch`] /// - That the message signature matches the one for the event: [`type@AtspiError::SignatureMatch`] /// /// Therefore, this should only be used when one has checked the above conditions. /// These must be checked manually. /// Alternatively, there is the [`MessageConversionExt::try_from_message`] that will check these /// conditions for you. /// /// This type also implements `TryFrom<&zbus::Message>`; consider using this if you are not an /// internal developer. /// /// # Errors /// /// It is possible to get a [`type@AtspiError::Zvariant`] error if you do not check the proper /// conditions before calling this. fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result where Self: Sized + 'a; /// Build an event from an [`ObjectRef`] and [`Self::Body`]. /// This function will not check for any of the following error conditions: /// /// - That the message has an interface: [`type@AtspiError::MissingInterface`] /// - That the message interface matches the one for the event: [`type@AtspiError::InterfaceMatch`] /// - That the message has an member: [`type@AtspiError::MissingMember`] /// - That the message member matches the one for the event: [`type@AtspiError::MemberMatch`] /// /// Therefore, this should only be used when one has checked the above conditions. /// /// # Errors /// /// Some [`Self::Body`] types may fallibly convert data fields contained in the body. /// If this happens, then the function will return an error. fn from_message_unchecked_parts(obj_ref: ObjectRef, body: DbusBody) -> Result where Self: Sized; /// The body of the object. fn body(&self) -> Self::Body<'_>; } atspi-common-0.13.0/src/events/window.rs000064400000000000000000000400641046102023000162670ustar 00000000000000#[cfg(feature = "zbus")] use crate::error::AtspiError; #[cfg(feature = "zbus")] use crate::events::EventBody; #[cfg(feature = "zbus")] use crate::events::MessageConversion; use crate::events::{ DBusInterface, DBusMatchRule, DBusMember, EventBodyOwned, RegistryEventString, }; use crate::object_ref::ObjectRefOwned; #[cfg(feature = "zbus")] use crate::{EventProperties, ObjectRef}; use serde::Deserialize; use serde::Serialize; #[cfg(feature = "zbus")] use zbus::message::{Body as DbusBody, Header}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct PropertyChangeEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, pub property: String, } impl_event_type_properties_for_event!(PropertyChangeEvent); /// The window has been minimized. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct MinimizeEvent { /// The application which has been minimized. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(MinimizeEvent); /// The window has been maximized. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct MaximizeEvent { /// The application which has been maximized. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(MaximizeEvent); #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct RestoreEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(RestoreEvent); /// A window has been closed. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct CloseEvent { /// The application which has been closed. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(CloseEvent); /// A new window has been created. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct CreateEvent { /// An application to query for additional events from. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(CreateEvent); #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct ReparentEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(ReparentEvent); /// A new virtual desktop has been created. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub struct DesktopCreateEvent { /// A reference to a new desktop pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(DesktopCreateEvent); /// A virtual desktop has been deleted. #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct DesktopDestroyEvent { /// A reference to the destroyed desktop. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(DesktopDestroyEvent); #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct DestroyEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(DestroyEvent); #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct ActivateEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(ActivateEvent); #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct DeactivateEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(DeactivateEvent); #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct RaiseEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(RaiseEvent); #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct LowerEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(LowerEvent); #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct MoveEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(MoveEvent); /// A window has been resized. #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct ResizeEvent { /// The application which has been resized. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(ResizeEvent); #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct ShadeEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(ShadeEvent); #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct UUshadeEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(UUshadeEvent); #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Hash, Default)] pub struct RestyleEvent { /// The [`crate::ObjectRef`] which the event applies to. pub item: ObjectRefOwned, } impl_event_type_properties_for_event!(RestyleEvent); impl_member_interface_registry_string_and_match_rule_for_event!( PropertyChangeEvent, "PropertyChange", "org.a11y.atspi.Event.Window", "window:property-change", "type='signal',interface='org.a11y.atspi.Event.Window',member='PropertyChange'" ); #[cfg(feature = "zbus")] impl MessageConversion<'_> for PropertyChangeEvent { type Body<'a> = EventBody<'a>; fn from_message_unchecked_parts(item: ObjectRef, body: DbusBody) -> Result { let mut body = body.deserialize_unchecked::>()?; Ok(Self { item: item.into(), property: body.take_kind() }) } fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result { let item = header.try_into()?; let body = msg.body(); Self::from_message_unchecked_parts(item, body) } fn body(&self) -> Self::Body<'_> { EventBody::Owned(EventBodyOwned { kind: self.property.clone(), ..Default::default() }) } } impl_member_interface_registry_string_and_match_rule_for_event!( MinimizeEvent, "Minimize", "org.a11y.atspi.Event.Window", "window:minimize", "type='signal',interface='org.a11y.atspi.Event.Window',member='Minimize'" ); impl_member_interface_registry_string_and_match_rule_for_event!( MaximizeEvent, "Maximize", "org.a11y.atspi.Event.Window", "window:maximize", "type='signal',interface='org.a11y.atspi.Event.Window',member='Maximize'" ); impl_member_interface_registry_string_and_match_rule_for_event!( RestoreEvent, "Restore", "org.a11y.atspi.Event.Window", "window:restore", "type='signal',interface='org.a11y.atspi.Event.Window',member='Restore'" ); impl_member_interface_registry_string_and_match_rule_for_event!( CloseEvent, "Close", "org.a11y.atspi.Event.Window", "window:close", "type='signal',interface='org.a11y.atspi.Event.Window',member='Close'" ); impl_member_interface_registry_string_and_match_rule_for_event!( CreateEvent, "Create", "org.a11y.atspi.Event.Window", "window:create", "type='signal',interface='org.a11y.atspi.Event.Window',member='Create'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ReparentEvent, "Reparent", "org.a11y.atspi.Event.Window", "window:reparent", "type='signal',interface='org.a11y.atspi.Event.Window',member='Reparent'" ); impl_member_interface_registry_string_and_match_rule_for_event!( DesktopCreateEvent, "DesktopCreate", "org.a11y.atspi.Event.Window", "window:desktop-create", "type='signal',interface='org.a11y.atspi.Event.Window',member='DesktopCreate'" ); impl_member_interface_registry_string_and_match_rule_for_event!( DesktopDestroyEvent, "DesktopDestroy", "org.a11y.atspi.Event.Window", "window:desktop-destroy", "type='signal',interface='org.a11y.atspi.Event.Window',member='DesktopDestroy'" ); impl_member_interface_registry_string_and_match_rule_for_event!( DestroyEvent, "Destroy", "org.a11y.atspi.Event.Window", "window:destroy", "type='signal',interface='org.a11y.atspi.Event.Window',member='Destroy'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ActivateEvent, "Activate", "org.a11y.atspi.Event.Window", "window:activate", "type='signal',interface='org.a11y.atspi.Event.Window',member='Activate'" ); impl_member_interface_registry_string_and_match_rule_for_event!( DeactivateEvent, "Deactivate", "org.a11y.atspi.Event.Window", "window:deactivate", "type='signal',interface='org.a11y.atspi.Event.Window',member='Deactivate'" ); impl_member_interface_registry_string_and_match_rule_for_event!( RaiseEvent, "Raise", "org.a11y.atspi.Event.Window", "window:raise", "type='signal',interface='org.a11y.atspi.Event.Window',member='Raise'" ); impl_member_interface_registry_string_and_match_rule_for_event!( LowerEvent, "Lower", "org.a11y.atspi.Event.Window", "window:lower", "type='signal',interface='org.a11y.atspi.Event.Window',member='Lower'" ); impl_member_interface_registry_string_and_match_rule_for_event!( MoveEvent, "Move", "org.a11y.atspi.Event.Window", "window:move", "type='signal',interface='org.a11y.atspi.Event.Window',member='Move'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ResizeEvent, "Resize", "org.a11y.atspi.Event.Window", "window:resize", "type='signal',interface='org.a11y.atspi.Event.Window',member='Resize'" ); impl_member_interface_registry_string_and_match_rule_for_event!( ShadeEvent, "Shade", "org.a11y.atspi.Event.Window", "window:shade", "type='signal',interface='org.a11y.atspi.Event.Window',member='Shade'" ); impl_member_interface_registry_string_and_match_rule_for_event!( UUshadeEvent, "uUshade", "org.a11y.atspi.Event.Window", "window:uushade", "type='signal',interface='org.a11y.atspi.Event.Window',member='uUshade'" ); impl_member_interface_registry_string_and_match_rule_for_event!( RestyleEvent, "Restyle", "org.a11y.atspi.Event.Window", "window:restyle", "type='signal',interface='org.a11y.atspi.Event.Window',member='Restyle'" ); event_test_cases!(PropertyChangeEvent); impl_to_dbus_message!(PropertyChangeEvent); impl_from_dbus_message!(PropertyChangeEvent); impl_event_properties!(PropertyChangeEvent); impl From for EventBodyOwned { fn from(event: PropertyChangeEvent) -> Self { EventBodyOwned { kind: event.property, ..Default::default() } } } event_test_cases!(MinimizeEvent); impl_to_dbus_message!(MinimizeEvent); impl_from_dbus_message!(MinimizeEvent); impl_event_properties!(MinimizeEvent); impl_from_object_ref!(MinimizeEvent); event_test_cases!(MaximizeEvent); impl_to_dbus_message!(MaximizeEvent); impl_from_dbus_message!(MaximizeEvent); impl_event_properties!(MaximizeEvent); impl_from_object_ref!(MaximizeEvent); event_test_cases!(RestoreEvent); impl_to_dbus_message!(RestoreEvent); impl_from_dbus_message!(RestoreEvent); impl_event_properties!(RestoreEvent); impl_from_object_ref!(RestoreEvent); event_test_cases!(CloseEvent); impl_to_dbus_message!(CloseEvent); impl_from_dbus_message!(CloseEvent); impl_event_properties!(CloseEvent); impl_from_object_ref!(CloseEvent); event_test_cases!(CreateEvent); impl_to_dbus_message!(CreateEvent); impl_from_dbus_message!(CreateEvent); impl_event_properties!(CreateEvent); impl_from_object_ref!(CreateEvent); event_test_cases!(ReparentEvent); impl_to_dbus_message!(ReparentEvent); impl_from_dbus_message!(ReparentEvent); impl_event_properties!(ReparentEvent); impl_from_object_ref!(ReparentEvent); event_test_cases!(DesktopCreateEvent); impl_to_dbus_message!(DesktopCreateEvent); impl_from_dbus_message!(DesktopCreateEvent); impl_event_properties!(DesktopCreateEvent); impl_from_object_ref!(DesktopCreateEvent); event_test_cases!(DesktopDestroyEvent); impl_to_dbus_message!(DesktopDestroyEvent); impl_from_dbus_message!(DesktopDestroyEvent); impl_event_properties!(DesktopDestroyEvent); impl_from_object_ref!(DesktopDestroyEvent); event_test_cases!(DestroyEvent); impl_to_dbus_message!(DestroyEvent); impl_from_dbus_message!(DestroyEvent); impl_event_properties!(DestroyEvent); impl_from_object_ref!(DestroyEvent); event_test_cases!(ActivateEvent); impl_to_dbus_message!(ActivateEvent); impl_from_dbus_message!(ActivateEvent); impl_event_properties!(ActivateEvent); impl_from_object_ref!(ActivateEvent); event_test_cases!(DeactivateEvent); impl_to_dbus_message!(DeactivateEvent); impl_from_dbus_message!(DeactivateEvent); impl_event_properties!(DeactivateEvent); impl_from_object_ref!(DeactivateEvent); event_test_cases!(RaiseEvent); impl_to_dbus_message!(RaiseEvent); impl_from_dbus_message!(RaiseEvent); impl_event_properties!(RaiseEvent); impl_from_object_ref!(RaiseEvent); event_test_cases!(LowerEvent); impl_to_dbus_message!(LowerEvent); impl_from_dbus_message!(LowerEvent); impl_event_properties!(LowerEvent); impl_from_object_ref!(LowerEvent); event_test_cases!(MoveEvent); impl_to_dbus_message!(MoveEvent); impl_from_dbus_message!(MoveEvent); impl_event_properties!(MoveEvent); impl_from_object_ref!(MoveEvent); event_test_cases!(ResizeEvent); impl_to_dbus_message!(ResizeEvent); impl_from_dbus_message!(ResizeEvent); impl_event_properties!(ResizeEvent); impl_from_object_ref!(ResizeEvent); event_test_cases!(ShadeEvent); impl_to_dbus_message!(ShadeEvent); impl_from_dbus_message!(ShadeEvent); impl_event_properties!(ShadeEvent); impl_from_object_ref!(ShadeEvent); event_test_cases!(UUshadeEvent); impl_to_dbus_message!(UUshadeEvent); impl_from_dbus_message!(UUshadeEvent); impl_event_properties!(UUshadeEvent); impl_from_object_ref!(UUshadeEvent); event_test_cases!(RestyleEvent); impl_to_dbus_message!(RestyleEvent); impl_from_dbus_message!(RestyleEvent); impl_event_properties!(RestyleEvent); impl_from_object_ref!(RestyleEvent); impl_msg_conversion_ext_for_target_type!(PropertyChangeEvent); impl_msg_conversion_ext_for_target_type!(MinimizeEvent); impl_msg_conversion_ext_for_target_type!(MaximizeEvent); impl_msg_conversion_ext_for_target_type!(RestoreEvent); impl_msg_conversion_ext_for_target_type!(CloseEvent); impl_msg_conversion_ext_for_target_type!(CreateEvent); impl_msg_conversion_ext_for_target_type!(ReparentEvent); impl_msg_conversion_ext_for_target_type!(DesktopCreateEvent); impl_msg_conversion_ext_for_target_type!(DesktopDestroyEvent); impl_msg_conversion_ext_for_target_type!(DestroyEvent); impl_msg_conversion_ext_for_target_type!(ActivateEvent); impl_msg_conversion_ext_for_target_type!(DeactivateEvent); impl_msg_conversion_ext_for_target_type!(RaiseEvent); impl_msg_conversion_ext_for_target_type!(LowerEvent); impl_msg_conversion_ext_for_target_type!(MoveEvent); impl_msg_conversion_ext_for_target_type!(ResizeEvent); impl_msg_conversion_ext_for_target_type!(ShadeEvent); impl_msg_conversion_ext_for_target_type!(UUshadeEvent); impl_msg_conversion_ext_for_target_type!(RestyleEvent); impl_msg_conversion_for_types_built_from_object_ref!(MinimizeEvent); impl_msg_conversion_for_types_built_from_object_ref!(MaximizeEvent); impl_msg_conversion_for_types_built_from_object_ref!(RestoreEvent); impl_msg_conversion_for_types_built_from_object_ref!(CloseEvent); impl_msg_conversion_for_types_built_from_object_ref!(CreateEvent); impl_msg_conversion_for_types_built_from_object_ref!(ReparentEvent); impl_msg_conversion_for_types_built_from_object_ref!(DesktopCreateEvent); impl_msg_conversion_for_types_built_from_object_ref!(DesktopDestroyEvent); impl_msg_conversion_for_types_built_from_object_ref!(DestroyEvent); impl_msg_conversion_for_types_built_from_object_ref!(ActivateEvent); impl_msg_conversion_for_types_built_from_object_ref!(DeactivateEvent); impl_msg_conversion_for_types_built_from_object_ref!(RaiseEvent); impl_msg_conversion_for_types_built_from_object_ref!(LowerEvent); impl_msg_conversion_for_types_built_from_object_ref!(MoveEvent); impl_msg_conversion_for_types_built_from_object_ref!(ResizeEvent); impl_msg_conversion_for_types_built_from_object_ref!(ShadeEvent); impl_msg_conversion_for_types_built_from_object_ref!(UUshadeEvent); impl_msg_conversion_for_types_built_from_object_ref!(RestyleEvent); atspi-common-0.13.0/src/interface.rs000064400000000000000000000213031046102023000154070ustar 00000000000000//! Conversion functions and types representing a set of [`Interface`]s. //! //! Each `AccessibleProxy` will implement some set of these interfaces, //! represented by a [`InterfaceSet`]. use enumflags2::{bitflags, BitFlag, BitFlags}; use serde::{ de::{self, Deserializer, Visitor}, ser::{self, Serializer}, Deserialize, Serialize, }; use std::fmt; use zvariant::{Signature, Type}; /// AT-SPI interfaces an accessible object can implement. #[bitflags] #[repr(u32)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Interface { /// Interface to indicate implementation of `AccessibleProxy`. #[serde(rename = "org.a11y.atspi.Accessible")] Accessible, /// Interface to indicate implementation of `ActionProxy`. #[serde(rename = "org.a11y.atspi.Action")] Action, /// Interface to indicate implementation of `ApplicationProxy`. #[serde(rename = "org.a11y.atspi.Application")] Application, /// Interface to indicate implementation of `CacheProxy`. #[serde(rename = "org.a11y.atspi.Cache")] Cache, /// Interface to indicate implementation of `CollectionProxy`. #[serde(rename = "org.a11y.atspi.Collection")] Collection, /// Interface to indicate implementation of `ComponentProxy`. #[serde(rename = "org.a11y.atspi.Component")] Component, /// Interface to indicate implementation of `DocumentProxy`. #[serde(rename = "org.a11y.atspi.Document")] Document, /// Interface to indicate implementation of `DeviceEventControllerProxy`. #[serde(rename = "org.a11y.atspi.DeviceEventController")] DeviceEventController, /// Interface to indicate implementation of `DeviceEventListenerProxy`. #[serde(rename = "org.a11y.atspi.DeviceEventListener")] DeviceEventListener, /// Interface to indicate implementation of `EditableTextProxy`. #[serde(rename = "org.a11y.atspi.EditableText")] EditableText, /// Interface to indicate implementation of `HyperlinkProxy`. #[serde(rename = "org.a11y.atspi.Hyperlink")] Hyperlink, /// Interface to indicate implementation of `HypertextProxy`. #[serde(rename = "org.a11y.atspi.Hypertext")] Hypertext, /// Interface to indicate implementation of `ImageProxy`. #[serde(rename = "org.a11y.atspi.Image")] Image, /// Interface to indicate implementation of `RegistryProxy`. #[serde(rename = "org.a11y.atspi.Registry")] Registry, /// Interface to indicate implementation of `SelectionProxy`. #[serde(rename = "org.a11y.atspi.Selection")] Selection, /// Interface to indicate implementation of `SocketProxy`. #[serde(rename = "org.a11y.atspi.Socket")] Socket, /// Interface to indicate implementation of `TableProxy`. #[serde(rename = "org.a11y.atspi.Table")] Table, /// Interface to indicate implementation of `TableCellProxy`. #[serde(rename = "org.a11y.atspi.TableCell")] TableCell, /// Interface to indicate implementation of `TextProxy`. #[serde(rename = "org.a11y.atspi.Text")] Text, /// Interface to indicate implementation of `ValueProxy`. #[serde(rename = "org.a11y.atspi.Value")] Value, } /// A collection type which encodes the AT-SPI interfaces an accessible object has implemented. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct InterfaceSet(BitFlags); impl InterfaceSet { pub fn new>>(value: B) -> Self { Self(value.into()) } #[must_use] pub fn empty() -> InterfaceSet { InterfaceSet(Interface::empty()) } #[must_use] pub fn bits(&self) -> u32 { self.0.bits() } #[must_use] pub fn all() -> InterfaceSet { InterfaceSet(Interface::all()) } pub fn contains>>(self, other: B) -> bool { self.0.contains(other) } pub fn insert>>(&mut self, other: B) { self.0.insert(other); } #[must_use] pub fn iter(&self) -> enumflags2::Iter { self.0.iter() } } impl IntoIterator for InterfaceSet { type IntoIter = enumflags2::Iter; type Item = Interface; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl IntoIterator for &InterfaceSet { type IntoIter = enumflags2::Iter; type Item = Interface; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl Default for InterfaceSet { fn default() -> Self { Self::empty() } } impl<'de> de::Deserialize<'de> for InterfaceSet { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct InterfaceSetVisitor; impl<'de> Visitor<'de> for InterfaceSetVisitor { type Value = InterfaceSet; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a sequence comprised of valid AT-SPI interface names") } fn visit_newtype_struct(self, deserializer: D) -> Result where D: Deserializer<'de>, { match as Deserialize>::deserialize(deserializer) { Ok(interfaces) => Ok(InterfaceSet(BitFlags::from_iter(interfaces))), Err(e) => Err(e), } } } deserializer.deserialize_newtype_struct("InterfaceSet", InterfaceSetVisitor) } } impl ser::Serialize for InterfaceSet { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer .serialize_newtype_struct("InterfaceSet", &self.0.iter().collect::>()) } } impl Type for InterfaceSet { const SIGNATURE: &'static Signature = as Type>::SIGNATURE; } impl FromIterator for InterfaceSet { fn from_iter>(iter: T) -> Self { Self(BitFlags::from_iter(iter)) } } impl<'a> FromIterator<&'a Interface> for InterfaceSet { fn from_iter>(iter: I) -> Self { InterfaceSet(iter.into_iter().copied().collect()) } } impl From for InterfaceSet { fn from(value: Interface) -> Self { Self(value.into()) } } impl std::ops::BitAnd for InterfaceSet { type Output = InterfaceSet; fn bitand(self, other: Self) -> Self::Output { InterfaceSet(self.0 & other.0) } } impl std::ops::BitXor for InterfaceSet { type Output = InterfaceSet; fn bitxor(self, other: Self) -> Self::Output { InterfaceSet(self.0 ^ other.0) } } impl std::ops::BitOr for InterfaceSet { type Output = InterfaceSet; fn bitor(self, other: Self) -> Self::Output { InterfaceSet(self.0 | other.0) } } #[cfg(test)] mod tests { use super::{Interface, InterfaceSet}; use zvariant::serialized::Data; use zvariant::{serialized::Context, to_bytes, LE}; #[test] fn serialize_empty_interface_set() { let ctxt = Context::new_dbus(LE, 0); let encoded = to_bytes(ctxt, &InterfaceSet::empty()).unwrap(); assert_eq!(encoded.bytes(), &[0, 0, 0, 0]); } #[test] fn deserialize_empty_interface_set() { let ctxt = Context::new_dbus(LE, 0); let encoded = to_bytes(ctxt, &InterfaceSet::empty()).unwrap(); let (decoded, _) = encoded.deserialize::().unwrap(); assert_eq!(decoded, InterfaceSet::empty()); } #[test] fn serialize_interface_set_accessible() { let ctxt = Context::new_dbus(LE, 0); let encoded = to_bytes(ctxt, &InterfaceSet::new(Interface::Accessible)).unwrap(); assert_eq!( encoded.bytes(), &[ 30, 0, 0, 0, 25, 0, 0, 0, 111, 114, 103, 46, 97, 49, 49, 121, 46, 97, 116, 115, 112, 105, 46, 65, 99, 99, 101, 115, 115, 105, 98, 108, 101, 0 ] ); } #[test] fn deserialize_interface_set_accessible() { let ctxt = Context::new_dbus(LE, 0); let data = Data::new::<&[u8]>( &[ 30, 0, 0, 0, 25, 0, 0, 0, 111, 114, 103, 46, 97, 49, 49, 121, 46, 97, 116, 115, 112, 105, 46, 65, 99, 99, 101, 115, 115, 105, 98, 108, 101, 0, ], ctxt, ); let (ifaceset, _) = data.deserialize::().unwrap(); assert_eq!(ifaceset, InterfaceSet::new(Interface::Accessible)); } #[test] fn can_handle_multiple_interfaces() { let ctxt = Context::new_dbus(LE, 0); let object = InterfaceSet::new(Interface::Accessible | Interface::Action | Interface::Component); let encoded = to_bytes(ctxt, &object).unwrap(); let (decoded, _) = encoded.deserialize::().unwrap(); assert!(object == decoded); } // The order of appearance of the interfaces is equal to the order in the enum. #[test] fn iterator_on_interface_set() { let set = InterfaceSet::new(Interface::Accessible | Interface::Action | Interface::Component); let mut iter = set.into_iter(); assert_eq!(iter.next(), Some(Interface::Accessible)); assert_eq!(iter.next(), Some(Interface::Action)); assert_eq!(iter.next(), Some(Interface::Component)); assert_eq!(iter.next(), None); } #[test] fn iterator_on_interface_set_ref() { let set = InterfaceSet::new(Interface::Text | Interface::Collection | Interface::Component); let mut iter = (&set).into_iter(); assert_eq!(iter.next(), Some(Interface::Collection)); assert_eq!(iter.next(), Some(Interface::Component)); assert_eq!(iter.next(), Some(Interface::Text)); } } atspi-common-0.13.0/src/lib.rs000064400000000000000000000246251046102023000142270ustar 00000000000000#![deny(clippy::all, clippy::pedantic, clippy::cargo, unsafe_code, rustdoc::all)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::multiple_crate_versions)] //! # atspi-common //! //! Defines all common types, events, and data structures for `atspi-proxies` and `atspi-connection`. //! Since `atspi-proxies` and `atspi-connection` are downstream crates, the documentation can not link to it directly. //! Any type ending in `*Proxy` is in `atspi-proxies`. //! #[macro_use] extern crate static_assertions; #[macro_use] pub(crate) mod macros; pub mod action; #[cfg(feature = "wrappers")] pub use crate::events::event_wrappers::{ CacheEvents, DocumentEvents, Event, EventListenerEvents, FocusEvents, KeyboardEvents, MouseEvents, ObjectEvents, TerminalEvents, WindowEvents, }; pub use action::Action; pub mod object_match; pub use object_match::{MatchType, ObjectMatchRule, SortOrder, TreeTraversalType}; pub mod object_ref; pub use object_ref::{ObjectRef, ObjectRefOwned}; pub mod operation; pub use operation::Operation; pub mod interface; pub use interface::{Interface, InterfaceSet}; pub mod state; pub use state::{State, StateSet}; pub mod cache; pub use cache::{CacheItem, LegacyCacheItem}; pub mod error; pub use error::AtspiError; pub mod events; pub use events::{EventProperties, EventTypeProperties}; mod role; pub use role::Role; mod relation_type; pub use relation_type::RelationType; use serde::{Deserialize, Serialize}; use zvariant::Type; pub type Result = std::result::Result; /// Describes a selection of text, including selections across object boundaries. /// /// For example, selecting from the beginning of a paragraph to half way through a link would cause /// the start and end object references to be different. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] pub struct TextSelection { /// starting object reference start_obj: ObjectRefOwned, /// text offset within `start_obj` start_idx: i32, /// ending object reference end_obj: ObjectRefOwned, /// text offset within `end_obj` end_idx: i32, /// is the `start_obj` active; /// /// This is the same as querying for the [`StateSet`], then checking if [`State::Active`] is contained. /// See `atspi_proxies::accessible::AccessibleProxy` for more information on checking state. start_is_active: bool, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] #[repr(u32)] /// The coordinate type encodes the frame of reference. pub enum CoordType { /// In relation to the entire screen. Screen, /// In relation to only the window. Window, /// In relation to the parent of the element being checked. Parent, } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, Type)] #[repr(u32)] /// Enumeration used by `TextProxy` to indicate how to treat characters intersecting bounding boxes. pub enum ClipType { /// No characters/glyphs are omitted. Neither, /// Characters/glyphs clipped by the minimum coordinate are omitted. Min, /// Characters/glyphs which intersect the maximum coordinate are omitted. Max, /// Only glyphs falling entirely within the region bounded by min and max are retained. Both, } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, Type)] #[repr(u32)] /// Level of granularity to get text of, in relation to a cursor position. pub enum Granularity { /// Gives the character at the index of the cursor. With a line-style cursor (which is standard) this will get the character that appears after the cursor. Char, /// Gives the entire word in front of, or which contains, the cursor. TODO: confirm that it always chooses the word in front of the cursor. Word, /// Gives entire sentence in front of, or which contains, the cursor. TODO: confirm that it always chooses the sentence after the cursor. Sentence, /// Gives the line, as seen visually of which the cursor is situated within. Line, /// Gives the entire block of text, regardless of where the cursor lies within it. Paragraph, } /// Indicates relative stacking order of a `atspi_proxies::component::ComponentProxy` with respect to the /// onscreen visual representation of the UI. /// /// The layer index, in combination with the component's extents, /// can be used to compute the visibility of all or part of a component. /// This is important in programmatic determination of region-of-interest for magnification, /// and in flat screen review models of the screen, as well as for other uses. /// Objects residing in two of the `Layer` categories support further z-ordering information, /// with respect to their peers in the same layer: /// namely, [`Layer::Window`] and [`Layer::Mdi`]. /// Relative stacking order for other objects within the same layer is not available; /// the recommended heuristic is first child paints first. In other words, /// assume that the first siblings in the child list are subject to being /// overpainted by later siblings if their bounds intersect. #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] pub enum Layer { /// Indicates an error condition or uninitialized value. Invalid, /// Reserved for the desktop background; this is the bottom-most layer, /// over which everything else is painted. Background, /// The 'background' layer for most content renderers and /// UI `atspi_proxies::component::ComponentProxy` containers. Canvas, /// The layer in which the majority of ordinary 'foreground' widgets reside. Widget, /// A special layer between [`Layer::Canvas`] and [`Layer::Widget`], in which the /// 'pseudo windows' (e.g. the Multiple-Document Interface frames) reside. /// /// See `atspi_proxies::component::ComponentProxy::get_mdizorder`. Mdi, /// A layer for popup window content, above [`Layer::Widget`]. Popup, /// The topmost layer. Overlay, /// The layer in which a toplevel window background usually resides. Window, } /// Enumeration used by interface the [`crate::interface::Interface::Accessible`] to specify where an object should be placed on the screen when using `scroll_to`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] pub enum ScrollType { /// Scroll the object to the top left corner of the window. TopLeft, /// Scroll the object to the bottom right corner of the window. BottomRight, /// Scroll the object to the top edge of the window. TopEdge, /// Scroll the object to the bottom edge of the window. BottomEdge, /// Scroll the object to the left edge of the window. LeftEdge, /// Scroll the object to the right edge of the window. RightEdge, /// Scroll the object to application-dependent position on the window. Anywhere, } /// Enumeration used to indicate a type of live region and how assertive it /// should be in terms of speaking notifications. /// /// Currently, this is only used /// for `Announcement` events, but it may be used for additional purposes /// in the future. /// The argument in the `Announcement` event is named `politeness`. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize, Type)] #[repr(i32)] pub enum Politeness { /// No live region. #[default] None = 0, /// This live region should be considered polite. Polite = 1, /// This live region should be considered assertive. Assertive = 2, } impl TryFrom for Politeness { type Error = AtspiError; fn try_from(value: i32) -> std::result::Result { match value { 0 => Ok(Politeness::None), 1 => Ok(Politeness::Polite), 2 => Ok(Politeness::Assertive), _ => Err(AtspiError::Conversion("Unknown Politeness variant")), } } } #[cfg(test)] mod tests { use super::*; use std::str::FromStr; use zbus_lockstep::{ method_args_signature, method_return_signature, signal_body_type_signature, }; use zvariant::Signature; #[test] fn convert_i32_to_live() { assert_eq!(Politeness::None, Politeness::try_from(0).unwrap()); assert_eq!(Politeness::Polite, Politeness::try_from(1).unwrap()); assert_eq!(Politeness::Assertive, Politeness::try_from(2).unwrap()); assert!(Politeness::try_from(3).is_err()); assert!(Politeness::try_from(-1).is_err()); } #[test] fn validate_live_signature() { let signature = signal_body_type_signature!("Announcement"); let politeness_signature_str = &signature.to_string_no_parens(); let politeness_signature = Signature::from_str(&politeness_signature_str.as_str()[1..2]) .expect("Valid signature pattern"); assert_eq!(*::SIGNATURE, politeness_signature); } #[test] fn validate_scroll_type_signature() { let signature = method_args_signature!(member: "ScrollTo", interface: "org.a11y.atspi.Component", argument: "type"); assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_layer_signature() { let signature = method_return_signature!("GetLayer"); assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_granularity_signature() { let signature = method_args_signature!(member: "GetStringAtOffset", interface: "org.a11y.atspi.Text", argument: "granularity"); assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_clip_type_signature() { let signature = method_args_signature!(member: "GetTextAtOffset", interface: "org.a11y.atspi.Text", argument: "type"); assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_coord_type_signature() { let signature = method_args_signature!(member: "GetImagePosition", interface: "org.a11y.atspi.Image", argument: "coordType"); assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_match_type_signature() { let rule_signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "rule"); let match_type_signature_str = rule_signature.to_string(); let match_type_signature = Signature::from_str(&match_type_signature_str.as_str()[3..4]) .expect("Valid signature pattern"); assert_eq!(*::SIGNATURE, match_type_signature); } #[test] fn validate_text_selection_signature() { let selection_signature = method_args_signature!(member: "GetTextSelections", interface: "org.a11y.atspi.Document", argument: "selections"); let selection_signature_str = selection_signature.to_string(); let selection_signature = Signature::from_str(&selection_signature_str.as_str()[1..]) .expect("Valid signature pattern"); // this signature is written: `a(...)`, where `(...)` is the signature we want to compare against assert_eq!(*::SIGNATURE, selection_signature); } } atspi-common-0.13.0/src/macros.rs000064400000000000000000001157771046102023000147560ustar 00000000000000/// Expands to implement the required methods for the [`crate::EventProperties`] trait. /// This depends on the struct to have an `item` field of type [`crate::ObjectRef`]. /// /// ```ignore /// impl_from_interface_event_enum_for_event!(TextCaretMovedEvent); /// ``` /// /// Expands to: /// /// ```ignore /// impl EventProperties for TextCaretMovedEvent { /// fn sender(&self) -> UniqueName<'_> { /// self.item /// .name() /// .expect("Events are constructed with valid ObjetRef") /// .as_ref() /// } /// fn path(&self) -> ObjectPath<'_> { /// self.item.path().as_ref() /// } /// } /// ``` macro_rules! impl_event_properties { ($type:ty) => { impl crate::EventProperties for $type { fn sender(&self) -> zbus_names::UniqueName<'_> { self.item .name() .expect("Events are received with valid ObjetRef") .as_ref() } fn path(&self) -> zvariant::ObjectPath<'_> { self.item.path().as_ref() } } }; } /// Expands to implement From for [`crate::ObjectRef`]. /// This depends on the struct to have an `item` field of type [`crate::ObjectRef`]. /// /// ```ignore /// impl_from_object_ref!(TextAttributesChangedEvent); /// ``` /// /// Exapnds to: /// /// ```ignore /// impl From for TextAttributesChangedEvent { /// fn from(obj_ref: ObjectRef) -> Self { /// Self { item: obj_ref.into() } /// } /// } /// ``` macro_rules! impl_from_object_ref { ($type:ty) => { impl From> for $type { fn from(obj_ref: crate::ObjectRef) -> Self { Self { item: obj_ref.into() } } } }; } #[cfg(feature = "wrappers")] /// Expands to a conversion given the enclosed event type and outer `Event` variant. /// /// eg /// ```ignore /// impl_from_interface_event_enum_for_event!(ObjectEvents, Event::Object); /// ``` /// expands to: /// /// ```ignore /// impl From for Event { /// fn from(event_variant: ObjectEvents) -> Event { /// Event::Object(event_variant.into()) /// } /// } /// ``` macro_rules! impl_from_interface_event_enum_for_event { ($outer_type:ty, $outer_variant:path) => { #[cfg(feature = "wrappers")] impl From<$outer_type> for Event { fn from(event_variant: $outer_type) -> Event { $outer_variant(event_variant.into()) } } }; } #[cfg(feature = "wrappers")] /// Expands to a conversion given the enclosed event enum type and outer `Event` variant. /// /// eg /// ```ignore /// impl_try_from_event_for_interface_enum!(ObjectEvents, Event::Object); /// ``` /// expands to: /// /// ```ignore /// impl TryFrom for ObjectEvents { /// type Error = AtspiError; /// fn try_from(generic_event: Event) -> Result { /// if let Event::Object(event_type) = generic_event { /// Ok(event_type) /// } else { /// Err(AtspiError::Conversion("Invalid type")) /// } /// } /// } /// ``` macro_rules! impl_try_from_event_for_interface_enum { ($outer_type:ty, $outer_variant:path) => { impl TryFrom for $outer_type { type Error = AtspiError; fn try_from(generic_event: Event) -> Result<$outer_type, Self::Error> { if let $outer_variant(event_type) = generic_event { Ok(event_type) } else { Err(AtspiError::Conversion("Invalid type")) } } } }; } #[cfg(feature = "wrappers")] /// Expands to a conversion given the user facing event type, /// the wrapping interface enum variant, and the outer `Event` variant. /// /// ```ignore /// impl_from_user_facing_event_for_interface_event_enum!(StateChangedEvent, ObjectEvents, ObjectEvents::StateChanged); /// ``` /// /// expands to: /// /// ```ignore /// impl From for ObjectEvents { /// fn from(specific_event: StateChangedEvent) -> ObjectEvents { /// ObjectEvents::StateChanged(specific_event) /// } /// } /// ``` macro_rules! impl_from_user_facing_event_for_interface_event_enum { ($inner_type:ty, $outer_type:ty, $inner_variant:path) => { impl From<$inner_type> for $outer_type { fn from(specific_event: $inner_type) -> $outer_type { $inner_variant(specific_event) } } }; } #[cfg(feature = "wrappers")] /// Expands to a conversion given two arguments, /// 1. the user facing event type `(inner_type)` /// which relies on a conversion to its interface variant enum type variant. /// 2. the outer `Event::)>` wrapper., /// the enum type and outtermost variant. /// /// ```ignore user facing type, outer event variant /// impl_from_user_facing_type_for_event_enum!(StateChangedEvent, Event::Object); /// ``` /// /// expands to: /// /// ```ignore /// impl From for Event { /// fn from(event_variant: StateChangedEvent) -> Event { /// Event::Object(ObjectEvents::StateChanged(event_variant)) /// } /// } /// ``` macro_rules! impl_from_user_facing_type_for_event_enum { ($inner_type:ty, $outer_variant:path) => { #[cfg(feature = "wrappers")] impl From<$inner_type> for Event { fn from(event_variant: $inner_type) -> Event { $outer_variant(event_variant.into()) } } }; } #[cfg(feature = "wrappers")] /// Expands to a `TryFrom for T` where T is the user facing type. /// The macro takes three arguments: /// /// 1. The user facing type. /// 2. The inner variant of the user facing type. /// 3. The outer variant of the `Event` enum. /// /// ```ignore /// impl_try_from_event_for_user_facing_type!( /// StateChangedEvent, /// ObjectEvents::StateChanged, /// Event::Object /// ); /// ``` /// expands to: /// /// ```ignore /// impl TryFrom for StateChangedEvent { /// type Error = AtspiError; /// fn try_from(generic_event: Event) -> Result { /// if let Event::Object(ObjectEvents::StateChanged(specific_event)) = generic_event { /// Ok(specific_event) /// } else { /// Err(AtspiError::Conversion("Invalid type")) /// } /// } /// } /// ``` /// macro_rules! impl_try_from_event_for_user_facing_type { ($inner_type:ty, $inner_variant:path, $outer_variant:path) => { #[cfg(feature = "wrappers")] impl TryFrom for $inner_type { type Error = AtspiError; fn try_from(generic_event: crate::Event) -> Result<$inner_type, Self::Error> { if let $outer_variant($inner_variant(specific_event)) = generic_event { Ok(specific_event) } else { Err(AtspiError::Conversion("Invalid type")) } } } }; } /// Implements the `TryFrom` trait for a given event type. /// Converts a user facing event type into a `zbus::Message`. /// /// # Example /// ```ignore /// impl_to_dbus_message!(StateChangedEvent); /// ``` /// expands to: /// /// ```ignore /// impl TryFrom for zbus::Message { /// type Error = AtspiError; /// fn try_from(event: StateChangedEvent) -> Result { /// Ok(zbus::Message::signal( /// event.path(), /// StateChangedEvent::DBUS_INTERFACE, /// StateChangedEvent::DBUS_MEMBER, /// )? /// .sender(event.sender())? /// .build(&event.body())?) /// } /// } /// macro_rules! impl_to_dbus_message { ($type:ty) => { #[cfg(feature = "zbus")] impl TryFrom<$type> for zbus::Message { type Error = AtspiError; fn try_from(event: $type) -> Result { use crate::events::{DBusInterface, DBusMember, MessageConversion}; Ok(zbus::Message::signal( event.path(), <$type as DBusInterface>::DBUS_INTERFACE, <$type as DBusMember>::DBUS_MEMBER, )? .sender(event.sender().to_string())? .build(&event.body())?) } } }; } /// Implements the `TryFrom` trait for a given event type. /// Converts a `zbus::Message` into a user facing event type. /// /// See [`crate::events::MessageConversion`] for details on implementation. /// /// # Example /// ```ignore /// impl_from_dbus_message!(StateChangedEvent); /// ``` /// expands to: /// /// ```ignore /// impl TryFrom<&zbus::Message> for StateChangedEvents { /// type Error = AtspiError; /// fn try_from(msg: &zbus::Message) -> Result { /// let hdr = msg.header(); /// <$type as MessageConversion>::try_from_message(msg, hdr) /// } /// } /// ``` /// /// There is also a variant that can be used for events whose [`crate::events::MessageConversion::Body`] is not /// [`crate::events::event_body::EventBodyOwned`]. You can call this by setting the second parameter to `Explicit`. macro_rules! impl_from_dbus_message { ($type:ty) => { impl_from_dbus_message!($type, Auto); }; ($type:ty, Auto) => { #[cfg(feature = "zbus")] impl<'msg> TryFrom<&'msg zbus::Message> for $type { type Error = AtspiError; fn try_from(msg: &'msg zbus::Message) -> Result { use crate::events::{EventBody, EventBodyQtBorrowed}; use crate::events::traits::{MessageConversion, MessageConversionExt}; use zvariant::Type; use crate::ObjectRef; let hdr = msg.header(); ::Body<'_>>>::validate_interface(&hdr)?; ::Body<'_>>>::validate_member(&hdr)?; let item = ObjectRef::try_from(&hdr)?; let body = msg.body(); let signature = body.signature(); if signature == EventBody::SIGNATURE || signature == EventBodyQtBorrowed::SIGNATURE { Ok(Self::from_message_unchecked_parts(item, body)?) } else { Err(AtspiError::SignatureMatch(format!( "signature mismatch: expected: {}, signal body: {}", msg.body().signature(), ::Body::SIGNATURE, ))) } } } }; ($type:ty, Explicit) => { #[cfg(feature = "zbus")] impl TryFrom<&zbus::Message> for $type { type Error = crate::AtspiError; fn try_from(msg: &zbus::Message) -> Result { let hdr = msg.header(); <$type as crate::events::MessageConversionExt<<$type as MessageConversion>::Body<'_>>>::try_from_message(msg, &hdr) } } }; } // We decorate the macro with a `#[cfg(test)]` attribute. // This prevents Clippy from complaining about the macro not being used. // It is being used, but only in test mode. // /// Tests `Default` and `BusProperties::from_message_unchecked` for a given event struct. /// /// Obtains a default for the given event struct. /// Asserts that the path and sender are the default. /// /// Breaks the struct down into item (the associated object) and body. /// Then tests `BusProperties::from_message_unchecked` with the item and body. #[cfg(test)] macro_rules! generic_event_test_case { ($type:ty) => { #[test] fn generic_event_uses() { use crate::events::traits::MessageConversion; let event_struct = <$type>::default(); assert_eq!(event_struct.path().as_str(), crate::object_ref::TEST_OBJECT_PATH_STR); assert_eq!(event_struct.sender().as_str(), crate::object_ref::TEST_OBJECT_BUS_NAME); let body = event_struct.body(); let body2 = Message::method_call( event_struct.path().as_str(), <$type as crate::events::DBusMember>::DBUS_MEMBER, ) .unwrap() .sender(event_struct.sender().as_str()) .unwrap() .build(&(body,)) .unwrap(); let header = body2.header(); let build_struct = <$type>::from_message_unchecked(&body2, &header) .expect("<$type as Default>'s parts should build a valid ObjectRef"); assert_eq!(event_struct, build_struct); } }; } // We decorate the macro with a `#[cfg(test)]` attribute. // This prevents Clippy from complaining about the macro not being used. // It is being used, but only in test mode. // /// Tests conversion to and from the `Event` enum. /// /// Obtains a default for the given event struct. /// Converts the struct into the `Event` enum, wrapping the struct. /// Converts the `Event` enum into the given event struct. /// Asserts that the original struct and the converted struct are equal. #[cfg(test)] macro_rules! event_has_matching_xml_definition { ($type:ty) => { #[test] fn event_has_matching_xml_definition() { use zbus_xml; use crate::events::{DBusInterface, DBusMember}; let fname = match <$type>::DBUS_INTERFACE.split(".").last().expect("Has last section") { "Cache" => "xml/Cache.xml", "Socket" => "xml/Socket.xml", "Registry" => "xml/Registry.xml", _ => "xml/Event.xml", }; let reader = std::fs::File::open(fname).expect("Valid file path!"); let xml = zbus_xml::Node::from_reader(reader).expect("Valid DBus XML file!"); let Some(interface) = xml.interfaces().iter().find(|int| int.name() == <$type>::DBUS_INTERFACE) else { let possible_names: Vec = xml.interfaces().iter().map(|int| int.name().as_str().to_string()).collect(); panic!("{} has interface name {}, but it was not found in the list of interfaces defined in the XML: {:?}", std::any::type_name::<$type>(), <$type>::DBUS_INTERFACE, possible_names); }; let Some(_member) = interface.signals().iter().find(|mem| mem.name() == <$type>::DBUS_MEMBER) else { let possible_names: Vec = interface.signals().iter().map(|mem| mem.name().as_str().to_string()).collect(); panic!("{} has interface name {} and member name {}, but it was not found in the list of members defined in the corresponding interface in the XML: {:?}", std::any::type_name::<$type>(), <$type>::DBUS_INTERFACE, <$type>::DBUS_MEMBER, possible_names); }; } }; } #[cfg(test)] macro_rules! zbus_message_qtspi_test_case { ($type:ty, Auto) => { #[cfg(feature = "zbus")] #[test] fn zbus_message_conversion_qtspi() { use crate::events::EventTypeProperties; use crate::events::MessageConversion; // in the case that the body type is EventBodyOwned, we need to also check successful // conversion from a QSPI-style body. let ev = <$type>::default(); let qt: crate::events::EventBodyQtOwned = ev.body().into(); let msg = zbus::Message::signal( ev.path(), ev.interface(), ev.member(), ) .unwrap() .sender(":0.0") .unwrap() .build(&(qt,)) .unwrap(); <$type>::try_from(&msg).expect("Should be able to use an EventBodyQtOwned for any type whose BusProperties::Body = EventBodyOwned"); } #[cfg(feature = "zbus")] #[test] fn zbus_message_conversion_qtspi_event_enum() { use crate::events::EventTypeProperties; use crate::events::MessageConversion; use crate::Event; // in the case that the body type is EventBodyOwned, we need to also check successful // conversion from a QSPI-style body. let ev = <$type>::default(); let qt: crate::events::EventBodyQtOwned = ev.body().into(); let msg = zbus::Message::signal( ev.path(), ev.interface(), ev.member(), ) .unwrap() .sender(":0.0") .unwrap() .build(&(qt,)) .unwrap(); assert_matches!(Event::try_from(&msg), Ok(_)); } }; ($type:ty, Explicit) => {}; } // We decorate the macro with a `#[cfg(test)]` attribute. // This prevents Clippy from complaining about the macro not being used. // It is being used, but only in test mode. // /// As of writing, this macro is expanded only once: in the `event_test_cases!` macro. #[cfg(test)] macro_rules! zbus_message_test_case { ($type:ty) => { zbus_message_test_case!($type, Auto); }; ($type:ty, $extra:tt) => { zbus_message_qtspi_test_case!($type, $extra); #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion_to_specific_event_type() { let struct_event = <$type>::default(); let msg: zbus::Message = zbus::Message::try_from(<$type>::default()).expect("Should convert a `$type::default()` into a message. Check the `impl_to_dbus_message` macro ."); let struct_event_back = <$type>::try_from(&msg).expect("Should convert from `$type::default()` originated `Message` back into a specific event type. Check the `impl_from_dbus_message` macro."); assert_eq!(struct_event, struct_event_back, "Events converted into a message and back must be the same"); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion_to_event_enum_type() { let struct_event = <$type>::default(); let msg: zbus::Message = zbus::Message::try_from(struct_event.clone()).expect("Should convert a `$type::default()` into a message. Check the `impl_to_dbus_message` macro."); let event_enum_back = crate::Event::try_from(&msg).expect("Should convert a from `$type::default()` built `Message` into an event enum. Check the `impl_from_dbus_message` macro."); let event_enum: crate::Event = struct_event.into(); assert_eq!(event_enum, event_enum_back); } // may want to consider parameterized tests here, no need for fuzz testing, but one level lower than that may be nice // try having a matching member, matching interface, path, or body type, but that has some other piece which is not right #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion_failure_fake_msg() -> () { let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", "org.a11y.atspi.technically.valid", "MadeUpMember", ) .unwrap() .sender(":0.0") .unwrap() .build(&()) .unwrap(); let event = <$type>::try_from(&fake_msg); assert_matches!(event, Err(_), "This conversion should fail"); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion_validated_message_with_body() -> () { use crate::events::MessageConversion; let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", "org.a11y.atspi.technically.valid", "MadeUpMember", ) .unwrap() .sender(":0.0") .unwrap() .build(&<$type>::default().body()) .unwrap(); let hdr = fake_msg.header(); let event = <$type>::from_message_unchecked(&fake_msg, &hdr); event.expect("The from_message_unchecked function should work, despite mismatching interface and member"); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion_failure_correct_interface() -> () { let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", <$type as crate::events::DBusInterface>::DBUS_INTERFACE, "MadeUpMember", ) .unwrap() .sender(":0.0") .unwrap() .build(&()) .unwrap(); let event = <$type>::try_from(&fake_msg); assert_matches!(event, Err(AtspiError::MemberMatch(_)), "Wrong kind of error"); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion_failure_correct_interface_and_member() -> () { let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", <$type as crate::events::DBusInterface>::DBUS_INTERFACE, <$type as crate::events::DBusMember>::DBUS_MEMBER, ) .unwrap() .sender(":0.0") .unwrap() .build(&()) .unwrap(); let event = <$type>::try_from(&fake_msg); assert_matches!(event, Err(AtspiError::SignatureMatch(_)), "Wrong kind of error"); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion_failure_correct_interface_and_member_invalid_body() -> () { // known invalid body for AT-SPI events let invalid_body: (i32, u64, String, String) = (0, 0, String::new(), String::new()); let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", <$type as crate::events::DBusInterface>::DBUS_INTERFACE, <$type as crate::events::DBusMember>::DBUS_MEMBER, ) .unwrap() .sender(":0.0") .unwrap() .build(&invalid_body) .unwrap(); let event = <$type>::try_from(&fake_msg); assert_matches!(event, Err(AtspiError::SignatureMatch(_)), "Wrong kind of error"); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion_failure_correct_body() -> () { use crate::events::MessageConversion; let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", "org.a11y.atspi.accessible.technically.valid", "FakeMember", ) .unwrap() .sender(":0.0") .unwrap() .build(&<$type>::default().body()) .unwrap(); let event = <$type>::try_from(&fake_msg); assert_matches!(event, Err(_)); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion_failure_correct_body_and_member() -> () { use crate::events::MessageConversion; let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", "org.a11y.atspi.accessible.technically.valid", <$type as crate::events::DBusMember>::DBUS_MEMBER, ) .unwrap() .sender(":0.0") .unwrap() .build(&<$type>::default().body()) .unwrap(); let event = <$type>::try_from(&fake_msg); assert_matches!(event, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error"); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion_failure_correct_body_and_interface() -> () { use crate::events::MessageConversion; let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", <$type as crate::events::DBusInterface>::DBUS_INTERFACE, "MadeUpMember", ) .unwrap() .sender(":0.0") .unwrap() .build(&<$type>::default().body()) .unwrap(); let event = <$type>::try_from(&fake_msg); assert_matches!(event, Err(AtspiError::MemberMatch(_)), "Wrong kind of error"); } }; } #[cfg(feature = "wrappers")] /// Expands to five tests: /// /// 1. `into_and_try_from_event` /// 2. `zbus_msg_invalid_interface` /// 3. `zbus_msg_invalid_member` /// 4. `zbus_msg_invalid_member_and_interface` /// 5. `zbus_msg_conversion` /// /// The macro takes two arguments: /// 1. The event's interface enum type. /// 2. Any user facing event type that is wrapped by the interface enum. /// /// # Examples /// /// ```ignore /// event_wrapper_test_cases!(MouseEvents, AbsEvent); /// ``` /// /// For each of the types, the macro will create a module with the name `events_tests_{foo}` /// where `{foo}` is the snake case of the 'interface enum' name. macro_rules! event_wrapper_test_cases { // The macro takes two arguments: the interface enum type and the user facing event type (ufet). ($iface_enum:ty, $ufet:ty) => { #[cfg(test)] #[rename_item::rename(name($iface_enum), prefix = "events_tests_", case = "snake")] mod foo { use super::{$ufet, $iface_enum, AtspiError, Event, MessageConversion}; // TODO: replace with [`std::assert_matches::assert_matches`] when stabilized use assert_matches::assert_matches; #[test] fn into_and_try_from_user_facing_event() { // Create a default event struct from its type's `Default::default()` impl. let sub_type = <$ufet>::default(); // Wrap the event struct in the event enum let mod_type = <$iface_enum>::from(sub_type); let hint_iface = "Check macro `impl_from_user_facing_event_for_interface_event_enum!`"; // Wrap the inner event enum into the `Event` enum. let event = Event::from(mod_type.clone()); let hint_event = "Check macro `impl_from_interface_event_enum_for_event!`"; // Unwrap the `Event` enum into the inner event enum. let hint_event_try = "Check macro `impl_try_from_event_for_interface_enum!`"; let mod_type2 = <$iface_enum>::try_from(event.clone()) .expect("Should convert outer `Event` enum into interface enum. hints: {hint_event} and {hint_event_try}"); assert_eq!( mod_type, mod_type2, "Interface enums should match. hint: {hint_iface}, {hint_event} and {hint_event_try}" ); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_invalid_interface() { let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", "org.a11y.atspi.technically.valid.lol", <$ufet as crate::events::DBusMember>::DBUS_MEMBER, ) .unwrap() .sender(":0.0") .unwrap() .build(&<$ufet>::default().body()) .unwrap(); // It is hard to see what eventually is tested here. Let's unravel it: // // Below we call `TryFrom<&zbus::Message> for $type` where `$type` the interface enum name. (eg. `MouseEvents`, `ObjectEvents`, etc.) and // `mod_type` is an 'interface enum' variant (eg. `MouseEvents::Abs(AbsEvent)`). // This conversion is found in the `/src/events/{iface_name}.rs`` file. // This conversion in turn leans on the `impl_from_dbus_message` macro. // In `MouseEvents::Abs(msg.try_into()?)`, it is the `msg.try_into()?` that should fail. // The `msg.try_into()?` is provided through the `impl_from_dbus_message` macro. // Additioanlly, we check against the same method in `Event`; the overarchive enum that // contains all other events as variants. let mod_type = <$iface_enum>::try_from(&fake_msg); let event_type = Event::try_from(&fake_msg); assert_matches!(mod_type, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error"); assert_matches!(event_type, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error"); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_invalid_member() { let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", <$ufet as crate::events::DBusInterface>::DBUS_INTERFACE, "FakeFunctionLol", ) .unwrap() .sender(":0.0") .unwrap() .build(&<$ufet>::default().body()) .unwrap(); // As above, the `msg.try_into()?` is provided through the `impl_from_dbus_message` macro. let mod_type = <$iface_enum>::try_from(&fake_msg); assert_matches!(mod_type, Err(AtspiError::MemberMatch(_)), "Wrong kind of error"); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_invalid_member_and_interface() { let fake_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", "org.a11y.atspi.technically.allowed", "FakeFunctionLol", ) .unwrap() .sender(":0.0") .unwrap() .build(&<$ufet>::default().body()) .unwrap(); // As above, the `msg.try_into()?` is provided through the `impl_from_dbus_message` macro. let mod_type = <$iface_enum>::try_from(&fake_msg); // Note that the non-matching interface is the first error, so the member match error is not reached. assert_matches!(mod_type, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error"); } #[cfg(feature = "zbus")] #[test] fn zbus_msg_conversion() { let valid_msg = zbus::Message::signal( "/org/a11y/sixtynine/fourtwenty", <$ufet as crate::events::DBusInterface>::DBUS_INTERFACE, <$ufet as crate::events::DBusMember>::DBUS_MEMBER, ) .unwrap() .sender(":0.0") .unwrap() .build(&<$ufet>::default().body()) .unwrap(); // As above, the `msg.try_into()?` is provided through the `impl_from_dbus_message` macro. let mod_type = <$iface_enum>::try_from(&valid_msg); mod_type.expect("Should convert from `$ufet::default()` built `Message` back into a interface event enum variant wrapping an inner type. Check the `impl_from_dbus_message` macro."); } } }; } macro_rules! event_test_cases { ($ufet:ty) => { event_test_cases!($ufet, Auto); }; ($ufet:ty, $qt:tt) => { #[cfg(test)] #[rename_item::rename(name($ufet), prefix = "event_tests_", case = "snake")] mod foo { use super::{$ufet, AtspiError, EventProperties }; use crate::events::traits::EventTypeProperties; #[cfg(feature = "zbus")] use zbus::Message; use crate::Event; // TODO: use [`std::assert_matches::assert_matches`] when stabilized use assert_matches::assert_matches; #[test] #[cfg(feature = "wrappers")] fn event_enum_conversion() { let event_struct = <$ufet>::default(); let event_enum = Event::from(event_struct.clone()); let event_struct_back = <$ufet>::try_from(event_enum) .expect("Should convert event enum into specific event type because it was created from it. Check the `impl_from_interface_event_enum_for_event` macro"); assert_eq!(event_struct, event_struct_back); } #[test] #[cfg(feature = "wrappers")] fn event_enum_transparency_test_case() { let specific_event = <$ufet>::default(); let generic_event = Event::from(specific_event.clone()); let hint = "Check macro `impl_from_user_facing_type_for_event_enum!`."; assert_eq!( specific_event.member(), generic_event.member(), "DBus members do not match. hint: {hint}" ); assert_eq!( specific_event.interface(), generic_event.interface(), "DBus interfaces do not match. hint: {hint}" ); assert_eq!( specific_event.registry_string(), generic_event.registry_string(), "Registry strings do not match. hint: {hint}" ); assert_eq!( specific_event.match_rule(), generic_event.match_rule(), "Match rule strings do not match. hint: {hint}" ); assert_eq!(specific_event.path(), generic_event.path(), "Paths do not match. hint: {hint}"); assert_eq!(specific_event.sender(), generic_event.sender(), "Senders do not match. hint: {hint}"); } zbus_message_test_case!($ufet, $qt); event_has_matching_xml_definition!($ufet); generic_event_test_case!($ufet); } assert_impl_all!( $ufet: Clone, std::fmt::Debug, serde::Serialize, serde::Deserialize<'static>, Default, PartialEq, Eq, std::hash::Hash, crate::events::traits::EventProperties, crate::events::traits::EventTypeProperties, crate::events::traits::DBusInterface, crate::events::traits::DBusMember, crate::events::traits::DBusMatchRule, crate::events::traits::RegistryEventString ); #[cfg(feature = "zbus")] assert_impl_all!(zbus::Message: TryFrom<$ufet>); }; } /// Implements `MessageConversionExt` for a given target event type with a given body type. /// /// # Example /// ```ignore /// # // 'ignore'd for bevity's sake because `impl`s require that the *example crate* defines traits `MessageConversionExt`, /// # // `MessageConversion` as well as the body and target types. /// /// impl_msg_conversion_ext_for_target_type_with_specified_body_type!(target: RemoveAccessibleEvent, body: ObjectRef); /// ``` /// expands to: /// /// ```ignore /// #[cfg(feature = "zbus")] /// impl<'a> MessageConversionExt<'_, ObjectRef> for RemoveAccessibleEvent { /// fn try_from_message(msg: &zbus::Message, hdr: &Header) -> Result { /// >::validate_interface(hdr)?; /// >::validate_member(hdr)?; /// >::validate_body(msg)?; /// >::from_message_unchecked(msg, hdr) /// } /// } /// ``` macro_rules! impl_msg_conversion_ext_for_target_type_with_specified_body_type { (target: $target_type:ty, body: $body_type:ty) => { #[cfg(feature = "zbus")] impl<'a> crate::events::MessageConversionExt<'a, $body_type> for $target_type { fn try_from_message(msg: &zbus::Message, hdr: &Header) -> Result { use crate::events::MessageConversionExt; >::validate_interface(hdr)?; >::validate_member(hdr)?; >::validate_body(msg)?; >::from_message_unchecked(msg, hdr) } } }; } /// Implements `MessageConversionExt` for a given target event type. /// /// # Example /// /// ```ignore /// impl_msg_conversion_ext_for_target_type!(LoadCompleteEvent); /// ``` /// expands to: /// /// ```ignore /// #[cfg(feature = "zbus")] /// impl<'msg> MessageConversionExt<'msg, EventBody<'msg>> for LoadCompleteEvent { /// fn try_from_message(msg: &'msg zbus::Message, header: &Header) -> Result { /// Self::validate_interface(header)?; /// Self::validate_member(header)?; /// /// let item = ObjectRefOwned::try_from(header)?; /// let msg_body = msg.body(); /// let signature = msg_body.signature(); /// /// if signature == crate::events::EventBodyOwned::SIGNATURE /// || signature == crate::events::EventBodyQtOwned::SIGNATURE /// { /// Self::from_message_unchecked_parts(item, msg_body) /// } else { /// Err(AtspiError::SignatureMatch(format!( /// "The message signature {} does not match a valid signal body signature: {} or {}", /// msg.body().signature(), /// crate::events::EventBodyOwned::SIGNATURE, /// crate::events::EventBodyQtOwned::SIGNATURE, /// ))) /// } /// } /// } /// ``` macro_rules! impl_msg_conversion_ext_for_target_type { ($target_type:ty) => { #[cfg(feature = "zbus")] impl<'msg> crate::events::MessageConversionExt<'msg, crate::events::EventBody<'msg>> for $target_type { fn try_from_message(msg: &'msg zbus::Message, header: &Header) -> Result { use zvariant::Type; use crate::events::traits::MessageConversion; Self::validate_interface(header)?; Self::validate_member(header)?; let item = ObjectRefOwned::try_from(header)?; let msg_body = msg.body(); let signature = msg_body.signature(); if signature == crate::events::EventBodyOwned::SIGNATURE || signature == crate::events::EventBodyQtOwned::SIGNATURE { Self::from_message_unchecked_parts(item.into_inner(), msg_body) } else { Err(AtspiError::SignatureMatch(format!( "The message signature {} does not match a valid signal body signature: {} or {}", msg.body().signature(), crate::events::EventBodyOwned::SIGNATURE, crate::events::EventBodyQtOwned::SIGNATURE, ))) } } } }; } #[cfg(feature = "wrappers")] /// Implements `TryFromMessage` for a given event wrapper type. /// /// # Example /// ```ignore /// impl_tryfrommessage_for_event_wrapper!(StateChangedEvent); /// ``` /// expands to: /// /// ```ignore /// #[cfg(feature = "zbus")] /// impl TryFromMessage for StateChangedEvent { /// fn try_from_message(msg: &zbus::Message) -> Result { /// let header = msg.header(); /// let interface = header.interface().ok_or(AtspiError::MissingInterface)?; /// if interface != Self::DBUS_INTERFACE { /// return Err(AtspiError::InterfaceMatch(format!( /// "Interface {} does not match require interface for event: {}", /// interface, /// Self::DBUS_INTERFACE /// ))); /// } /// Self::try_from_message_interface_checked(msg, &header) /// } /// } /// ``` macro_rules! impl_tryfrommessage_for_event_wrapper { ($wrapper:ty) => { #[cfg(feature = "zbus")] impl crate::events::traits::TryFromMessage for $wrapper { fn try_from_message(msg: &zbus::Message) -> Result<$wrapper, AtspiError> { use crate::events::traits::EventWrapperMessageConversion; let header = msg.header(); let interface = header.interface().ok_or(AtspiError::MissingInterface)?; if interface != Self::DBUS_INTERFACE { return Err(AtspiError::InterfaceMatch(format!( "Interface {} does not match require interface for event: {}", interface, Self::DBUS_INTERFACE ))); } Self::try_from_message_interface_checked(msg, &header) } } }; } /// Implement the `MessageConversion` trait for the given types. /// /// This macro is used to implement the `MessageConversion` trait for types that are built from an /// `ObjectRef` and a `zbus::message::Body` only - no `EventBody` needed. /// /// # Example /// /// ```ignore /// impl_msg_conversion_for_types_built_from_object_ref!(FocusEvent, FocusEvents); /// ``` /// /// This will generate the following implementations: /// /// ```ignore /// #[cfg(feature = "zbus")] /// impl MessageConversion<'_> for FocusEvent { /// type Body<'msg> = crate::events::EventBody<'msg>; /// /// fn from_message_unchecked_parts( /// obj_ref: ObjectRefOwned, /// _body: zbus::message::Body, /// ) -> Result { /// Ok(obj_ref.into()) /// } /// /// fn from_message_unchecked(_: &zbus::Message, header: &Header) -> Result { /// let obj_ref: ObjectRefOwned = header.try_into()?; /// Ok(obj_ref.into()) /// } /// /// fn body(&self) -> Self::Body<'_> { /// crate::events::EventBodyOwned::default().into() /// } /// } /// ``` macro_rules! impl_msg_conversion_for_types_built_from_object_ref { ($($type:ty),*) => { $( #[cfg(feature = "zbus")] impl crate::events::MessageConversion<'_> for $type { type Body<'msg> = crate::events::EventBody<'msg>; fn from_message_unchecked_parts( obj_ref: crate::object_ref::ObjectRef<'_>, _body: zbus::message::Body, ) -> Result { Ok(obj_ref.into()) } fn from_message_unchecked(_: &zbus::Message, header: &Header) -> Result { let obj_ref: ObjectRefOwned = header.try_into()?; Ok( Self { item: obj_ref }) } fn body(&self) -> Self::Body<'_> { crate::events::EventBodyOwned::default().into() } } )* }; } /// Implement `DBusMember`, `DBusInterface`, `DBusMatchRule`, and `RegistryEventString` /// for a given event type. /// /// This macro takes 5 arguments in the order: /// - The target type /// - The member string /// - The interface string /// - The registry string /// - The match rule string /// /// # Example /// ```ignore /// impl_member_interface_registry_string_and_match_rule_for_event!( /// FocusEvent, "Focus", "org.a11y.atspi.Event.Focus", "focus", /// "type='signal',interface='org.a11y.atspi.Event.Focus'"); /// ``` /// expands to: /// /// ```ignore /// impl DBusMember for FocusEvent { /// const DBUS_MEMBER: &'static str = "Focus"; /// } /// impl DBusInterface for FocusEvent { /// const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Event.Focus"; /// } /// impl MatchRule for FocusEvent { /// const MATCH_RULE: &'static str = "type='signal',interface='org.a11y.atspi.Event.Focus'"; /// } /// impl RegistryEventString for FocusEvent { /// const REGISTRY_STRING: &'static str = "focus"; /// } /// impl DBusProperties for FocusEvent {} /// ``` macro_rules! impl_member_interface_registry_string_and_match_rule_for_event { ($target_type:ty, $member_str:literal, $interface_str:literal, $registry_str:literal, $match_rule_str:literal) => { impl crate::events::DBusMember for $target_type { const DBUS_MEMBER: &'static str = $member_str; } impl crate::events::DBusInterface for $target_type { const DBUS_INTERFACE: &'static str = $interface_str; } impl crate::events::DBusMatchRule for $target_type { const MATCH_RULE_STRING: &'static str = $match_rule_str; } impl crate::events::RegistryEventString for $target_type { const REGISTRY_EVENT_STRING: &'static str = $registry_str; } impl crate::events::DBusProperties for $target_type {} }; } /// Implement `EventTypeProperties` for a given event type. /// /// This macro takes one argument: the target type. /// /// # Example /// ```ignore /// impl_event_type_properties_for_event!(FocusEvent); /// ``` /// expands to: /// /// ```ignore /// impl EventTypeProperties for FocusEvent { /// fn member(&self) -> &'static str { /// Self::DBUS_MEMBER /// } /// fn interface(&self) -> &'static str { /// Self::DBUS_INTERFACE /// } /// fn registry_string(&self) -> &'static str { /// Self::REGISTRY_EVENT_STRING /// } /// fn match_rule(&self) -> &'static str { /// Self::MATCH_RULE_STRING /// } /// } /// macro_rules! impl_event_type_properties_for_event { ($target_type:ty) => { impl crate::events::EventTypeProperties for $target_type { fn member(&self) -> &'static str { Self::DBUS_MEMBER } fn interface(&self) -> &'static str { Self::DBUS_INTERFACE } fn registry_string(&self) -> &'static str { Self::REGISTRY_EVENT_STRING } fn match_rule(&self) -> &'static str { Self::MATCH_RULE_STRING } } }; } atspi-common-0.13.0/src/object_match.rs000064400000000000000000000203331046102023000160730ustar 00000000000000use std::{borrow::Borrow, collections::HashMap, marker::PhantomData}; use serde::{Deserialize, Serialize}; use zvariant::{Signature, Type}; use crate::{Interface, InterfaceSet, Role, State, StateSet}; /// Defines how an object-tree is to be traversed. /// Used in `CollectionProxy`. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] #[repr(u32)] pub enum TreeTraversalType { /// When traversing the tree, restrict to children of the current object. RestrictChildren, /// When traversing the tree, restrict to siblings of the current object. RestrictSibling, /// Traverse the tree in order of appearance. #[default] Inorder, } /// Definition of match rules for accessible objects. /// Rule(s) against which we can match an object or a collection thereof. /// /// # Examples /// ```rust /// # use zbus::MatchRule; /// let builder = MatchRule::builder(); /// ``` /// #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct ObjectMatchRule { pub states: StateSet, pub states_mt: MatchType, pub attr: HashMap, pub attr_mt: MatchType, pub roles: Vec, pub roles_mt: MatchType, pub ifaces: InterfaceSet, pub ifaces_mt: MatchType, pub invert: bool, // Private phantom, gets compiled away. // Here to ensure the builder is the only route to obtain a `MatchRule` #[serde(skip)] _marker: std::marker::PhantomData<()>, } /// !!! WARNING !!! : /// /// State and Role are defined as u32 in Accessible.xml but as i32 in Collection.xml /// /// The signature on `StateSet` is defined inconsistently in the XMLs: /// /// - `Accessible.xml`: `GetState type="au"` /// - `Collection.xml`: `GetMatches` argument `name="rule" type="(aiia{ss}iaiiasib)"` /// /// The latter starts with ai, which is a signature for an array of signed ints i32. /// /// impl Type for ObjectMatchRule { const SIGNATURE: &'static Signature = &Signature::static_structure(&[ >::SIGNATURE, &Signature::I32, >::SIGNATURE, &Signature::I32, >::SIGNATURE, &Signature::I32, >::SIGNATURE, &Signature::I32, &Signature::Bool, ]); } impl ObjectMatchRule { /// Create a new `MatchRuleBuilder` #[must_use] pub fn builder() -> ObjectMatchRuleBuilder { ObjectMatchRuleBuilder::default() } } /// The 'builder' type for `MatchRule`. /// Use its methods to set match criteria. #[derive(Debug, Clone, Default)] pub struct ObjectMatchRuleBuilder { states: StateSet, states_mt: MatchType, attr: HashMap, attr_mt: MatchType, roles: Vec, roles_mt: MatchType, ifaces: InterfaceSet, ifaces_mt: MatchType, invert: bool, } impl ObjectMatchRuleBuilder { /// Insert a `StateSet` to the builder #[must_use] pub fn states(mut self, states: I, mt: MatchType) -> Self where I: IntoIterator, I::Item: Borrow, { self.states = states.into_iter().map(|state| *state.borrow()).collect(); self.states_mt = mt; self } /// Insert a map of attributes #[must_use] pub fn attributes(mut self, attributes: HashMap, mt: MatchType) -> Self { self.attr = attributes; self.attr_mt = mt; self } /// Insert a slice of `Role`s #[must_use] pub fn roles(mut self, roles: &[Role], mt: MatchType) -> Self { self.roles = roles.into(); self.roles_mt = mt; self } /// Insert an `InterfaceSet` from a collection of `Interface`s #[must_use] pub fn interfaces(mut self, interfaces: I, mt: MatchType) -> Self where I: IntoIterator, I::Item: Borrow, { self.ifaces = interfaces.into_iter().map(|iface| *iface.borrow()).collect(); self.ifaces_mt = mt; self } /// Sets the inversion of the `MatchRule`, defaults to `false`, no inversion. #[must_use] pub fn invert(mut self, invert: bool) -> Self { self.invert = invert; self } /// Builds the [`ObjectMatchRule`] /// /// [`ObjectMatchRule``]: crate::object_match::ObjectMatchRule #[must_use] pub fn build(self) -> ObjectMatchRule { ObjectMatchRule { states: self.states, states_mt: self.states_mt, attr: self.attr, attr_mt: self.attr_mt, roles: self.roles, roles_mt: self.roles_mt, ifaces: self.ifaces, ifaces_mt: self.ifaces_mt, invert: self.invert, _marker: PhantomData, } } } /// Enumeration used by [`ObjectMatchRule`] to specify how to interpret [`ObjectRef`] objects. /// /// [`ObjectRef`]: crate::object_ref::ObjectRef #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type, Default)] #[repr(i32)] pub enum MatchType { #[default] /// Invalidates match criterion. Meanting: the search of this property will not be performed. Invalid, /// All of the criteria must be met. All, /// Any of the criteria must criteria must be met. Any, /// None of the criteria must be met. NA, /// Same as [`Self::All`] if the criterion item is non-empty - All of the criteria must be met. /// For empty criteria this rule requires the returned value to also have empty set. Empty, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] #[repr(u32)] /// Enumeration used by interface `CollectionProxy` to specify the way [`ObjectRef`] /// objects should be sorted. /// /// # Note /// /// Only `Canonical` and `ReverseCanonical` are currently implemented in the known implementation of `Collection`. /// /// see: [issue 140. "Are all the AtspiCollectionSortOrder types really a thing?"](https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues/140) /// /// [`ObjectRef`]: crate::object_ref::ObjectRef pub enum SortOrder { /// Invalid sort order /// /// Unimplemented Invalid, /// Canonical sort order Canonical, /// Flow sort order /// /// Unimplemented Flow, /// Tab sort order /// /// Unimplemented Tab, /// Reverse canonical sort order ReverseCanonical, /// Reverse flow sort order /// /// Unimplemented ReverseFlow, /// Reverse tab sort order /// /// Unimplemented ReverseTab, } #[cfg(test)] mod tests { use super::*; use crate::{SortOrder, State}; use std::str::FromStr; use zbus_lockstep::method_args_signature; #[test] fn validate_match_rule_signature() { let signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "rule"); assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_match_type_signature() { let rule_signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "rule"); // The match type signature is the fourth element in the signature let match_type_signature_str = rule_signature.to_string(); let match_type_signature = Signature::from_str(&match_type_signature_str.as_str()[3..4]) .expect("Valid signature pattern"); assert_eq!(*::SIGNATURE, match_type_signature); } #[test] fn validate_tree_traversal_type_signature() { let signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "tree"); assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_sort_order_signature() { let signature = method_args_signature!(member: "GetMatches", interface: "org.a11y.atspi.Collection", argument: "sortby"); assert_eq!(*::SIGNATURE, signature); } #[test] fn create_empty_object_match_rule() { let rule = ObjectMatchRule::builder().build(); assert_eq!(rule.states, StateSet::default()); assert_eq!(rule.attr, HashMap::new()); assert_eq!(rule.roles, Vec::new()); assert_eq!(rule.ifaces, InterfaceSet::default()); assert!(!rule.invert); } #[test] fn create_object_match_rule() { let rule = ObjectMatchRule::builder() .states(StateSet::new(State::Active), MatchType::All) .attributes( [("name".to_string(), "value".to_string())].iter().cloned().collect(), MatchType::Any, ) .roles(&[Role::Alert], MatchType::All) .interfaces([Interface::Action], MatchType::Any) .invert(true) .build(); assert_eq!(rule.states, StateSet::new(State::Active)); assert_eq!( rule.attr, [("name".to_string(), "value".to_string())].iter().cloned().collect() ); assert_eq!(rule.roles, vec![Role::Alert]); assert_eq!(rule.ifaces, InterfaceSet::new(Interface::Action)); assert!(rule.invert); } } atspi-common-0.13.0/src/object_ref.rs000064400000000000000000000704201046102023000155550ustar 00000000000000use crate::AtspiError; use serde::{Deserialize, Serialize}; use std::hash::{Hash, Hasher}; use zbus_lockstep_macros::validate; use zbus_names::{BusName, UniqueName}; use zvariant::{ObjectPath, Structure, Type}; const NULL_PATH_STR: &str = "/org/a11y/atspi/null"; const NULL_OBJECT_PATH: &ObjectPath<'static> = &ObjectPath::from_static_str_unchecked(NULL_PATH_STR); #[cfg(test)] pub(crate) const TEST_OBJECT_BUS_NAME: &str = ":0.0"; #[cfg(test)] pub(crate) const TEST_OBJECT_PATH_STR: &str = "/org/a11y/atspi/test/default"; #[cfg(test)] pub(crate) const TEST_DEFAULT_OBJECT_REF: ObjectRef<'static> = ObjectRef::from_static_str_unchecked(TEST_OBJECT_BUS_NAME, TEST_OBJECT_PATH_STR); /// A unique identifier for an object in the accessibility tree. /// /// A ubiquitous type used to refer to an object in the accessibility tree. /// /// In AT-SPI2, objects in the applications' UI object tree are uniquely identified /// using an application's bus name and object path. "(so)" /// /// Emitted by `RemoveAccessible` and `Available` #[validate(signal: "Available")] #[derive(Clone, Debug, Eq, Type)] #[zvariant(signature = "(so)")] pub enum ObjectRef<'o> { Null, Owned { name: UniqueName<'static>, path: ObjectPath<'static> }, Borrowed { name: UniqueName<'o>, path: ObjectPath<'o> }, } impl<'o> ObjectRef<'o> { /// Create a new `ObjectRef::Borrowed` from a `UniqueName` and `ObjectPath`. #[must_use] pub fn new(name: UniqueName<'o>, path: ObjectPath<'o>) -> Self { Self::new_borrowed(name, path) } /// Create a new, owned `ObjectRef`. /// /// # Example /// ```rust /// use zbus::names::UniqueName; /// use zbus::zvariant::ObjectPath; /// use atspi_common::ObjectRef; /// /// let name = UniqueName::from_static_str_unchecked(":1.23"); /// let path = ObjectPath::from_static_str_unchecked("/org/a11y/example/path/007"); /// /// let object_ref = ObjectRef::new_owned(name, path); /// # assert_eq!(object_ref.name_as_str(), Some(":1.23")); /// # assert_eq!(object_ref.path_as_str(), "/org/a11y/example/path/007"); /// ``` pub fn new_owned(name: N, path: P) -> ObjectRefOwned where N: Into>, P: Into>, { let name: UniqueName<'static> = name.into(); let path: ObjectPath<'static> = path.into(); ObjectRefOwned(ObjectRef::Owned { name, path }) } /// Create a new, borrowed `ObjectRef`. /// /// # Example /// ```rust /// use zbus::names::UniqueName; /// use zbus::zvariant::ObjectPath; /// use atspi_common::ObjectRef; /// /// let name = UniqueName::from_static_str_unchecked(":1.23"); /// let path = ObjectPath::from_static_str_unchecked("/org/a11y/example/path/007"); /// /// let object_ref = ObjectRef::new_borrowed(name, path); /// # assert_eq!(object_ref.name_as_str(), Some(":1.23")); /// # assert_eq!(object_ref.path_as_str(), "/org/a11y/example/path/007"); /// ``` pub fn new_borrowed(name: N, path: P) -> ObjectRef<'o> where N: Into>, P: Into>, { let name: UniqueName<'o> = name.into(); let path: ObjectPath<'o> = path.into(); ObjectRef::Borrowed { name, path } } /// Create a new `ObjectRef`, from `BusName` and `ObjectPath`. /// /// # Errors /// Will fail if the `sender` is not a `UniqueName`. pub fn try_from_bus_name_and_path( sender: BusName<'o>, path: ObjectPath<'o>, ) -> Result { // Check whether `BusName` matches `UniqueName` if let BusName::Unique(unique_sender) = sender { Ok(ObjectRef::new(unique_sender, path)) } else { Err(AtspiError::ParseError("Expected UniqueName")) } } /// Create a new `ObjectRef`, unchecked. /// /// # Safety /// The caller must ensure that the strings are valid. #[must_use] pub const fn from_static_str_unchecked(name: &'static str, path: &'static str) -> Self { let name = UniqueName::from_static_str_unchecked(name); let path = ObjectPath::from_static_str_unchecked(path); ObjectRef::Owned { name, path } } /// Returns `true` if the object reference is `Null`, otherwise returns `false`. /// /// Toolkits may use the `Null` object reference to indicate that an object is not available or does not exist. /// For example, when calling `Accessible::get_parent` on an object that has no parent, /// it may return a `Null` object reference. #[must_use] pub fn is_null(&self) -> bool { matches!(self, Self::Null) } /// Returns the name of the object reference. /// If the object reference is `Null`, it returns `None`. /// If the object reference is `Owned` or `Borrowed`, it returns the name. /// /// # Example /// ```rust /// use zbus::names::UniqueName; /// use zbus::zvariant::ObjectPath; /// use atspi_common::ObjectRef; /// /// let name = UniqueName::from_static_str_unchecked(":1.23"); /// let path = ObjectPath::from_static_str_unchecked("/org/a11y/example/path/007"); /// let object_ref = ObjectRef::new_borrowed(name, path); /// /// // Check the name of the object reference /// assert!(object_ref.name().is_some()); /// assert_eq!(object_ref.name().unwrap().as_str(), ":1.23"); /// ``` #[must_use] pub fn name(&self) -> Option<&UniqueName<'o>> { match self { Self::Owned { name: own_name, .. } => Some(own_name), Self::Borrowed { name: borrow_name, .. } => Some(borrow_name), Self::Null => None, } } /// Returns the path of the object reference.\ /// /// # Example /// ```rust /// use zbus::names::UniqueName; /// use zbus::zvariant::ObjectPath; /// use atspi_common::ObjectRef; /// /// let name = UniqueName::from_static_str_unchecked(":1.23"); /// let path = ObjectPath::from_static_str_unchecked("/org/a11y/example/path/007"); /// let object_ref = ObjectRef::new_borrowed(name, path); /// /// // Check the path of the object reference /// assert_eq!(object_ref.path().as_str(), "/org/a11y/example/path/007"); /// ``` #[must_use] pub fn path(&self) -> &ObjectPath<'o> { match self { Self::Owned { path: own_path, .. } => own_path, Self::Borrowed { path: borrow_path, .. } => borrow_path, Self::Null => NULL_OBJECT_PATH, } } /// Converts the `ObjectRef` into an owned instance, consuming `self`.\ /// If the object reference is `Null`, it returns `ObjectRef::Null`.\ /// If the object reference is `Owned`, it returns the same `ObjectRef::Owned`.\ /// If the object reference is `Borrowed`, it converts the name and path to owned versions and returns `ObjectRef::Owned`. /// /// # Extending lifetime 'magic' (from 'o -> 'static') /// /// `ObjectRef<'_>` leans on the implementation of `UniqueName` and `ObjectPath` to /// convert the inner types to `'static`. /// These types have an `Inner` enum that can contain an `Owned`, `Borrowed`, or `Static` `Str` type. /// The `Str`type is either a `&'static str` (static), `&str` (borrowed), or an `Arc` (owned). /// /// # Example /// ```rust /// use zbus::names::UniqueName; /// use zbus::zvariant::ObjectPath; /// use atspi_common::ObjectRef; /// /// let name = UniqueName::from_static_str_unchecked(":1.23"); /// let path = ObjectPath::from_static_str_unchecked("/org/a11y/example/path/007"); /// let object_ref = ObjectRef::new_borrowed(name, path); /// /// // Check whether the object reference can be converted to an owned version /// assert!(!object_ref.is_null()); /// let object_ref = object_ref.into_owned(); /// assert!(matches!(object_ref, ObjectRef::Owned { .. })); /// ``` #[must_use] pub fn into_owned(self) -> ObjectRef<'static> { match self { Self::Null => ObjectRef::Null, Self::Owned { name, path } => ObjectRef::Owned { name, path }, Self::Borrowed { name, path } => { ObjectRef::Owned { name: name.to_owned(), path: path.to_owned() } } } } /// Returns the name of the object reference as a string slice. #[must_use] pub fn name_as_str(&self) -> Option<&str> { match self { ObjectRef::Null => None, ObjectRef::Owned { name, .. } | ObjectRef::Borrowed { name, .. } => Some(name.as_str()), } } /// Returns the path of the object reference as a string slice. #[must_use] pub fn path_as_str(&self) -> &str { match self { ObjectRef::Null => NULL_PATH_STR, ObjectRef::Owned { path, .. } | ObjectRef::Borrowed { path, .. } => path.as_str(), } } } // Event tests lean on the `Default` implementation of `ObjectRef`. // This is a workaround for the fact that `ObjectRef::Null` in // `#[cfg(test)]` context is inconvenient. // Events are guaranteed to have a non-null `ObjectRef` on their `item` field, because we receive signals over // regular (non-p2p) DBus. Which means the `Message` `Header` has valid `Sender` and `Path` fields which // are used to construct the `ObjectRef` from a `Message`. #[cfg(test)] impl Default for ObjectRef<'_> { /// Returns a non-Null object reference. (test implementation) fn default() -> Self { TEST_DEFAULT_OBJECT_REF } } #[cfg(not(test))] impl Default for ObjectRef<'_> { /// Returns a `Null` object reference. fn default() -> Self { ObjectRef::Null } } /// A wrapper around the static variant of `ObjectRef`. /// This is guaranteed to have a `'static` lifetime. #[validate(signal: "Available")] #[derive(Clone, Debug, Default, Eq, Type)] pub struct ObjectRefOwned(pub(crate) ObjectRef<'static>); impl From> for ObjectRefOwned { /// Convert an `ObjectRef<'_>` into an `ObjectRefOwned`. /// /// # Extending lifetime 'magic' (from 'o -> 'static') /// /// `ObjectRef<'_>` leans on the implementation of `UniqueName` and `ObjectPath` to /// convert the inner types to `'static`. /// These types have an `Inner` enum that can contain an `Owned`, `Borrowed`, or `Static` `Str` type. /// The `Str`type is either a `&'static str` (static), `&str` (borrowed), or an `Arc` (owned). fn from(object_ref: ObjectRef<'_>) -> Self { ObjectRefOwned(object_ref.into_owned()) } } impl ObjectRefOwned { /// Create a new `ObjectRefOwned` from an `ObjectRef<'static>`. #[must_use] pub const fn new(object_ref: ObjectRef<'static>) -> Self { Self(object_ref) } /// Create a new `ObjectRefOwned` from `&'static str` unchecked. /// /// # Safety /// The caller must ensure that the strings are valid. #[must_use] pub const fn from_static_str_unchecked(name: &'static str, path: &'static str) -> Self { let name = UniqueName::from_static_str_unchecked(name); let path = ObjectPath::from_static_str_unchecked(path); ObjectRefOwned(ObjectRef::Owned { name, path }) } /// Returns `true` if the object reference is `Null`, otherwise returns `false`. #[must_use] pub fn is_null(&self) -> bool { matches!(self.0, ObjectRef::Null) } /// Returns the inner `ObjectRef`, consuming `self`. #[must_use] pub fn into_inner(self) -> ObjectRef<'static> { self.0 } /// Returns the name of the object reference. /// If the object reference is `Null`, it returns `None`. /// If the object reference is `Owned` or `Borrowed`, it returns the name. /// /// # Example /// ```rust /// use zbus::names::UniqueName; /// use zbus::zvariant::ObjectPath; /// use atspi_common::ObjectRef; /// /// let name = UniqueName::from_static_str_unchecked(":1.23"); /// let path = ObjectPath::from_static_str_unchecked("/org/a11y/example/path/007"); /// let object_ref = ObjectRef::new_borrowed(name, path); /// /// // Check the name of the object reference /// assert!(object_ref.name().is_some()); /// assert_eq!(object_ref.name_as_str().unwrap(), ":1.23"); /// ``` #[must_use] pub fn name(&self) -> Option<&UniqueName<'static>> { match &self.0 { ObjectRef::Owned { name, .. } | ObjectRef::Borrowed { name, .. } => Some(name), ObjectRef::Null => None, } } /// Returns the path of the object reference.\ /// If the object reference is `Null`, it returns the null-path. /// /// # Example /// ```rust /// use zbus::names::UniqueName; /// use zbus::zvariant::ObjectPath; /// use atspi_common::ObjectRef; /// /// let name = UniqueName::from_static_str_unchecked(":1.23"); /// let path = ObjectPath::from_static_str_unchecked("/org/a11y/example/path/007"); /// let object_ref = ObjectRef::new_borrowed(name, path); /// /// assert_eq!(object_ref.path_as_str(), "/org/a11y/example/path/007"); /// ``` #[must_use] pub fn path(&self) -> &ObjectPath<'static> { match &self.0 { ObjectRef::Owned { path, .. } | ObjectRef::Borrowed { path, .. } => path, ObjectRef::Null => NULL_OBJECT_PATH, } } /// Returns the name of the object reference as a string slice. #[must_use] pub fn name_as_str(&self) -> Option<&str> { match &self.0 { ObjectRef::Null => None, ObjectRef::Owned { name, .. } | ObjectRef::Borrowed { name, .. } => Some(name.as_str()), } } /// Returns the path of the object reference as a string slice. #[must_use] pub fn path_as_str(&self) -> &str { match &self.0 { ObjectRef::Null => NULL_PATH_STR, ObjectRef::Owned { path, .. } | ObjectRef::Borrowed { path, .. } => path.as_str(), } } } impl Serialize for ObjectRef<'_> { /// `ObjectRef`'s wire format is `(&str, ObjectPath)`. /// The `Null` variant, the "Null object", is serialized as `("", ObjectPath("/org/a11y/atspi/null"))`. /// Both `Owned` and `Borrowed` variants are serialized as `(&str, ObjectPath)` with the object's\ /// unique name and path. fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { match &self { ObjectRef::Null => ("", NULL_OBJECT_PATH).serialize(serializer), ObjectRef::Owned { name, path } | ObjectRef::Borrowed { name, path } => { (name.as_str(), path).serialize(serializer) } } } } impl Serialize for ObjectRefOwned { /// `ObjectRefOwned` is serialized as the inner `ObjectRef`. fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { self.0.serialize(serializer) } } impl<'de: 'o, 'o> Deserialize<'de> for ObjectRef<'o> { /// `ObjectRef`'s wire format is `(&str, ObjectPath)`. /// An empty `&str` with a "/org/a11y/atspi/null" path is considered a `Null` object, /// this is deserialized as `ObjectRef::Null`.\ /// Any other valid `(&str, ObjectPath)` will deserialize into `ObjectRef::Borrowed`. fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct ObjectRefVisitor; impl<'de> serde::de::Visitor<'de> for ObjectRefVisitor { type Value = ObjectRef<'de>; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("a tuple of (&str, ObjectPath)") } fn visit_seq(self, mut seq: A) -> Result where A: serde::de::SeqAccess<'de>, { let name: &str = seq .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; let path: ObjectPath<'de> = seq .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?; if path == *NULL_OBJECT_PATH { Ok(ObjectRef::Null) } else { assert!( !name.is_empty(), "A non-null ObjectRef requires a name and a path but got: (\"\", {path})" ); Ok(ObjectRef::Borrowed { name: UniqueName::try_from(name).map_err(serde::de::Error::custom)?, path, }) } } } deserializer.deserialize_tuple(2, ObjectRefVisitor) } } impl<'de> Deserialize<'de> for ObjectRefOwned { /// `ObjectRefOwned` is deserialized as "Owned" variant `ObjectRef<'static>`. fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let object_ref: ObjectRef<'_> = Deserialize::deserialize(deserializer)?; Ok(object_ref.into()) } } impl PartialEq for ObjectRef<'_> { fn eq(&self, other: &Self) -> bool { match (self, other) { // Match order is relevant here. Null == Null, but Null != any other object. (ObjectRef::Null, ObjectRef::Null) => true, (ObjectRef::Null, _) | (_, ObjectRef::Null) => false, _ => self.name() == other.name() && self.path() == other.path(), } } } // `Hash` requires that hashes are equal if values are equal. // If a == b, then a.hash() == b.hash() must hold true. // // Because PartialEq treats Owned and Borrowed variants with identical (name, path) as equal, // we must implement Hash manually to ignore the variant discriminant and preserve hash/equality // consistency. impl Hash for ObjectRef<'_> { fn hash(&self, state: &mut H) { match self { ObjectRef::Null => { // Hashing a Null object reference. "Null".hash(state); } ObjectRef::Owned { name, path } | ObjectRef::Borrowed { name, path } => { name.as_str().hash(state); path.as_str().hash(state); } } } } impl Hash for ObjectRefOwned { fn hash(&self, state: &mut H) { self.0.hash(state); } } impl PartialEq for ObjectRefOwned { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl PartialEq> for ObjectRefOwned { fn eq(&self, other: &ObjectRef<'_>) -> bool { self.0 == *other } } impl PartialEq for ObjectRef<'_> { fn eq(&self, other: &ObjectRefOwned) -> bool { *self == other.0 } } #[cfg(feature = "zbus")] impl<'m: 'o, 'o> TryFrom<&'m zbus::message::Header<'_>> for ObjectRef<'o> { type Error = crate::AtspiError; // Construct an ObjectRef<'o> by reborrowing from the Header’s data. // 'm: 'o, 'm outlives 'o, so the references returned by this function // are guaranteed to be valid for the lifetime of the header. /// Construct an `ObjectRef` from a `zbus::message::Header`. /// /// # Header fields /// /// `Path` is a mandatory field on method calls and signals, /// `Sender` is an optional field, see: /// [DBus specification - header fields]())., /// /// ```quote /// On a message bus, this header field is controlled by the message bus, /// so it is as reliable and trustworthy as the message bus itself. /// Otherwise, (eg. P2P) this header field is controlled by the message sender, /// unless there is out-of-band information that indicates otherwise. /// ``` /// /// While unlikely, it is possible that `Sender` or `Path` are not set on the header. /// This could happen if the server implementation does not set these fields for any reason. /// /// # Errors /// Will return an `AtspiError::ParseError` if the header does not contain a valid path or sender. fn try_from(header: &'m zbus::message::Header) -> Result { let path = header.path().ok_or(crate::AtspiError::MissingPath)?; let name = header.sender().ok_or(crate::AtspiError::MissingName)?; Ok(ObjectRef::Borrowed { name: name.clone(), path: path.clone() }) } } #[cfg(feature = "zbus")] impl<'m> TryFrom<&'m zbus::message::Header<'_>> for ObjectRefOwned { type Error = crate::AtspiError; /// Construct an `ObjectRefOwned` from a `zbus::message::Header`. fn try_from(header: &'m zbus::message::Header) -> Result { let path = header.path().ok_or(crate::AtspiError::MissingPath)?; let name = header.sender().ok_or(crate::AtspiError::MissingName)?; let object_ref = ObjectRef::Owned { name: name.clone().into_owned(), path: path.clone().into_owned() }; Ok(ObjectRefOwned(object_ref)) } } impl<'v> TryFrom> for ObjectRef<'v> { type Error = zvariant::Error; fn try_from(value: zvariant::Value<'v>) -> Result { // Relies on the generic `Value` to tuple conversion `(UniqueName, ObjectPath)`. let (name, path): (UniqueName, ObjectPath) = value.try_into()?; Ok(ObjectRef::new_borrowed(name, path)) } } impl<'v> TryFrom> for ObjectRefOwned { type Error = zvariant::Error; fn try_from(value: zvariant::Value<'v>) -> Result { // Relies on the generic `Value` to tuple conversion `(UniqueName, ObjectPath)`. let (name, path): (UniqueName, ObjectPath) = value.try_into()?; Ok(ObjectRef::new_borrowed(name, path).into()) } } impl TryFrom for ObjectRef<'static> { type Error = zvariant::Error; fn try_from(value: zvariant::OwnedValue) -> Result { let (name, path): (UniqueName<'static>, ObjectPath<'static>) = value.try_into()?; Ok(ObjectRef::Owned { name, path }) } } impl TryFrom for ObjectRefOwned { type Error = zvariant::Error; fn try_from(value: zvariant::OwnedValue) -> Result { let (name, path): (UniqueName<'static>, ObjectPath<'static>) = value.try_into()?; let obj = ObjectRef::Owned { name, path }; Ok(ObjectRefOwned(obj)) } } impl<'reference: 'structure, 'object: 'structure, 'structure> From<&'reference ObjectRef<'object>> for zvariant::Structure<'structure> { fn from(obj: &'reference ObjectRef<'object>) -> Self { match obj { ObjectRef::Null => ("", NULL_OBJECT_PATH).into(), ObjectRef::Borrowed { name, path } => Structure::from((name.clone(), path)), ObjectRef::Owned { name, path } => Structure::from((name.as_str(), path.as_ref())), } } } impl<'o> From> for zvariant::Structure<'o> { fn from(obj: ObjectRef<'o>) -> Self { match obj { ObjectRef::Null => Structure::from(("", NULL_OBJECT_PATH)), ObjectRef::Borrowed { name, path } | ObjectRef::Owned { name, path } => { Structure::from((name, path)) } } } } impl From for zvariant::Structure<'_> { fn from(obj: ObjectRefOwned) -> Self { let object_ref = obj.into_inner(); object_ref.into() } } #[cfg(test)] mod tests { use std::hash::{DefaultHasher, Hash, Hasher}; use super::ObjectRef; use crate::object_ref::{NULL_OBJECT_PATH, NULL_PATH_STR}; use zbus::zvariant; use zbus::{names::UniqueName, zvariant::ObjectPath}; use zvariant::{serialized::Context, to_bytes, OwnedValue, Value, LE}; const TEST_OBJECT_PATH: &str = "/org/a11y/atspi/path/007"; #[test] fn owned_object_ref_creation() { let name = UniqueName::from_static_str_unchecked(":1.23"); let path = ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH); let object_ref = ObjectRef::new_owned(name, path); assert_eq!(object_ref.name_as_str(), Some(":1.23")); assert_eq!(object_ref.path_as_str(), TEST_OBJECT_PATH); } #[test] fn borrowed_object_ref_creation() { let object_ref = ObjectRef::new_borrowed( UniqueName::from_static_str(":1.23").unwrap(), ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH), ); assert_eq!(object_ref.name_as_str(), Some(":1.23")); assert_eq!(object_ref.path_as_str(), TEST_OBJECT_PATH); } #[test] fn null_object_ref() { let null_object_ref: ObjectRef = ObjectRef::Null; assert!(null_object_ref.is_null()); assert!(null_object_ref.name().is_none()); assert_eq!(null_object_ref.path(), NULL_OBJECT_PATH); } #[test] fn object_ref_into_owned() { let borrowed_object_ref = ObjectRef::new_borrowed( UniqueName::from_static_str(":1.23").unwrap(), ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH), ); let owned_object_ref = borrowed_object_ref.into_owned(); assert!(matches!(owned_object_ref, ObjectRef::Owned { .. })); assert_eq!(owned_object_ref.name_as_str(), Some(":1.23")); assert_eq!(owned_object_ref.path_as_str(), TEST_OBJECT_PATH); } #[test] fn object_ref_into_name_and_path() { let object_ref = ObjectRef::new_borrowed( UniqueName::from_static_str(":1.23").unwrap(), ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH), ); let name = object_ref.name().unwrap(); let path = object_ref.path(); assert_eq!(name.as_str(), ":1.23"); assert_eq!(path.as_str(), TEST_OBJECT_PATH); } #[test] fn serialization_null_object_ref() { let null_object_ref: ObjectRef = ObjectRef::Null; assert!(null_object_ref.is_null()); let ctxt = Context::new_dbus(LE, 0); let encoded = to_bytes(ctxt, &null_object_ref).unwrap(); let (obj, _) = encoded.deserialize::().unwrap(); assert!(obj.is_null()); assert!(obj.name().is_none()); assert_eq!(obj.path(), NULL_OBJECT_PATH); } #[test] fn serialization_owned_object_ref() { let name = UniqueName::from_static_str_unchecked(":1.23"); let path = ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH); let object_ref = ObjectRef::new_owned(name, path); let ctxt = Context::new_dbus(LE, 0); let encoded = to_bytes(ctxt, &object_ref).unwrap(); let (obj, _) = encoded.deserialize::().unwrap(); // Deserialization alwayys results in a borrowed object reference. // On the wire the distinction between owned and borrowed is not preserved. // As borrowed is the cheaper option, we always deserialize to that. assert!(matches!(obj, ObjectRef::Borrowed { .. })); assert_eq!(obj.name().unwrap().as_str(), ":1.23"); assert_eq!(obj.path_as_str(), TEST_OBJECT_PATH); } #[test] fn serialization_borrowed_object_ref() { let object_ref = ObjectRef::new_borrowed( UniqueName::from_static_str(":1.23").unwrap(), ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH), ); let ctxt = Context::new_dbus(LE, 0); let encoded = to_bytes(ctxt, &object_ref).unwrap(); let (obj, _) = encoded.deserialize::().unwrap(); assert!(matches!(obj, ObjectRef::Borrowed { .. })); assert_eq!(obj.name().unwrap().as_str(), ":1.23"); assert_eq!(obj.path_as_str(), TEST_OBJECT_PATH); } #[test] fn object_ref_equality() { let object_ref1 = ObjectRef::new_borrowed( UniqueName::from_static_str(":1.23").unwrap(), ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH), ); let object_ref2 = ObjectRef::new_borrowed( UniqueName::from_static_str(":1.23").unwrap(), ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH), ); assert_eq!(object_ref1, object_ref2); let object_ref3 = ObjectRef::new_borrowed( UniqueName::from_static_str(":1.24").unwrap(), ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH), ); assert_ne!(object_ref1, object_ref3); let object_ref4 = ObjectRef::new_owned( UniqueName::from_static_str_unchecked(":1.23"), ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH), ); assert_eq!(object_ref1, object_ref4); let null_object_ref: ObjectRef = ObjectRef::Null; assert_ne!(object_ref1, null_object_ref); assert_ne!(null_object_ref, object_ref1); let null_object_ref2: ObjectRef = ObjectRef::Null; assert_eq!(null_object_ref, null_object_ref2); } #[test] fn try_from_value_for_objectref() { let name = UniqueName::from_static_str_unchecked(":0.0"); let path = ObjectPath::from_static_str_unchecked("/org/a11y/atspi/testpath"); let objref = ObjectRef::new_borrowed(name, path); let value: Value = objref.into(); let objref_2: ObjectRef = value.try_into().unwrap(); assert_eq!(objref_2.name().unwrap().as_str(), ":0.0"); assert_eq!(objref_2.path_as_str(), "/org/a11y/atspi/testpath"); } #[test] fn try_from_owned_value_for_objectref() { let name = UniqueName::from_static_str_unchecked(":0.0"); let path = ObjectPath::from_static_str_unchecked("/org/a11y/atspi/testpath"); let objref = ObjectRef::new_borrowed(name, path); let value: Value = objref.into(); let value: OwnedValue = value.try_into().unwrap(); let objref_2: ObjectRef = value.try_into().unwrap(); assert_eq!(objref_2.name_as_str(), Some(":0.0")); assert_eq!(objref_2.path_as_str(), "/org/a11y/atspi/testpath"); } // Must fail test: #[test] fn must_fail_test_try_from_invalid_value_for_object_ref() { let value = zvariant::Value::from((42, true)); let obj: Result = value.try_into(); assert!(obj.is_err()); } #[test] fn hash_and_object_coherence() { let name = UniqueName::from_static_str_unchecked(":1.23"); let path = ObjectPath::from_static_str_unchecked(TEST_OBJECT_PATH); let object_ref1 = ObjectRef::new_borrowed(&name, &path); let object_ref2 = ObjectRef::new_borrowed(name, path); let mut hasher1 = DefaultHasher::new(); let mut hasher2 = DefaultHasher::new(); assert_eq!(object_ref1, object_ref2); object_ref1.hash(&mut hasher1); object_ref2.hash(&mut hasher2); assert_eq!(hasher1.finish(), hasher2.finish()); } #[test] #[should_panic(expected = "assertion failed: matches!(obj, ObjectRef::Borrowed { .. })")] fn valid_name_null_path_object_ref() { let object_ref = ObjectRef::from_static_str_unchecked("1.23", NULL_PATH_STR); let ctxt = Context::new_dbus(LE, 0); let encoded = to_bytes(ctxt, &object_ref).unwrap(); let (obj, _) = encoded.deserialize::().unwrap(); assert!(matches!(obj, ObjectRef::Borrowed { .. })); } // Check that the Deserialize implementation correctly panics #[test] #[should_panic( expected = r#"A non-null ObjectRef requires a name and a path but got: ("", /org/a11y/atspi/path/007)"# )] fn empty_name_valid_path_object_ref() { let object_ref = ObjectRef::from_static_str_unchecked("", TEST_OBJECT_PATH); let ctxt = Context::new_dbus(LE, 0); let encoded = to_bytes(ctxt, &object_ref).unwrap(); let (_obj, _) = encoded.deserialize::().unwrap(); } } atspi-common-0.13.0/src/operation.rs000064400000000000000000000021701046102023000154500ustar 00000000000000use crate::AtspiError; use std::{fmt, str::FromStr}; /// An operation can either be [`Self::Insert`] or [`Self::Delete`]. /// These correspond to methods available on [`Vec`]. #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, Eq, Hash, Default)] pub enum Operation { #[default] #[serde(rename = "add")] #[serde(alias = "add/system")] #[serde(alias = "insert")] #[serde(alias = "insert/system")] Insert, #[serde(rename = "delete")] #[serde(alias = "delete/system")] #[serde(alias = "remove")] #[serde(alias = "remove/system")] Delete, } impl FromStr for Operation { type Err = AtspiError; fn from_str(s: &str) -> Result { match s { "add" | "add/system" | "insert" | "insert/system" => Ok(Operation::Insert), "delete" | "delete/system" | "remove" | "remove/system" => Ok(Operation::Delete), _ => Err(AtspiError::KindMatch(format!("{s} is not a type of Operation"))), } } } impl fmt::Display for Operation { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Operation::Insert => write!(f, "insert"), Operation::Delete => write!(f, "delete"), } } } atspi-common-0.13.0/src/relation_type.rs000064400000000000000000000123221046102023000163260ustar 00000000000000use serde::{Deserialize, Serialize}; use zvariant::Type; /// Describes a relationship between one object and another. #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type, Hash)] pub enum RelationType { /// Not a meaningful relationship; clients should not normally encounter this value. Null = 0, /// Object is a label for one or more other objects. LabelFor, /// Object is labelled by one or more other objects. LabelledBy, /// Object is an interactive object which modifies the state, /// onscreen location, or other attributes of one or more target objects. ControllerFor, /// Object state, position, etc. is modified/controlled by user interaction /// with one or more other objects. /// For instance a viewport or scroll pane may be `ControlledBy` scrollbars. ControlledBy, /// Object has a grouping relationship (e.g. 'same group as') to one or more other objects. MemberOf, /// Object is a tooltip associated with another object. TooltipFor, /// Object is a child of the target. NodeChildOf, /// Object is a parent of the target. NodeParentOf, /// Used to indicate that a relationship exists, but its type is not /// specified in the enumeration. Extended, /// Object renders content which flows logically to another object. /// For instance, text in a paragraph may flow to another object /// which is not the 'next sibling' in the accessibility hierarchy. FlowsTo, /// Reciprocal of `FlowsTo`. FlowsFrom, /// Object is visually and semantically considered a subwindow of another object, /// even though it is not the object's child. /// Useful when dealing with embedded applications and other cases where the /// widget hierarchy does not map cleanly to the onscreen presentation. SubwindowOf, /// Similar to `SubwindowOf`, but specifically used for cross-process embedding. Embeds, /// Reciprocal of `Embeds`. Used to denote content rendered by embedded renderers /// that live in a separate process space from the embedding context. EmbeddedBy, ///Denotes that the object is a transient window or frame associated with another /// onscreen object. Similar to `TooltipFor`, but more general. /// Useful for windows which are technically toplevels but which, for one or more reasons, /// do not explicitly cause their associated window to lose 'window focus'. /// Creation of an [`crate::Role::Window`] object with the `PopupFor` relation usually requires /// some presentation action on the part of assistive technology clients, /// even though the previous toplevel [`crate::Role::Frame`] object may still be the active window. PopupFor, /// This is the reciprocal relation to `PopupFor`. ParentWindowOf, /// Reciprocal of `DescribedBy`. Indicates that this object provides descriptive information /// about the target object(s). See also `DetailsFor` and `ErrorFor`. DescriptionFor, /// Reciprocal of `DescriptionFor`. /// Indicates that one or more target objects provide descriptive information /// about this object. This relation type is most appropriate for information /// that is not essential as its presentation may be user-configurable and/or /// limited to an on-demand mechanism such as an assistive technology command. /// For brief, essential information such as can be found in a widget's on-screen /// label, use `LabelledBy`. For an on-screen error message, use `ErrorMessage`. /// For lengthy extended descriptive information contained in an on-screen object, /// consider using `Details` as assistive technologies may provide a means /// for the user to navigate to objects containing detailed descriptions so that /// their content can be more closely reviewed. DescribedBy, /// Reciprocal of `DetailsFor`. /// Indicates that this object has a detailed or extended description, /// the contents of which can be found in the target object(s). /// This relation type is most appropriate for information that is sufficiently lengthy /// as to make navigation to the container of that information desirable. /// For less verbose information suitable for announcement only, see `DescribedBy`. /// If the detailed information describes an error condition, `ErrorFor` should be used instead. /// Included in upstream [AT-SPI2-CORE](https://gitlab.gnome.org/GNOME/at-spi2-core) since 2.26. Details, /// Reciprocal of `Details`. /// Indicates that this object provides a detailed or extended description about the target /// object(s). See also `DescriptionFor` and `ErrorFor`. /// Included in upstream [AT-SPI2-CORE](https://gitlab.gnome.org/GNOME/at-spi2-core) since 2.26. DetailsFor, /// Reciprocal of `ErrorFor`. /// Indicates that this object has one or more errors, the nature of which is /// described in the contents of the target object(s). Objects that have this /// relation type should also contain [`crate::state::State::InvalidEntry`] when their /// `GetState` method is called. /// /// Included in upstream [AT-SPI2-CORE](https://gitlab.gnome.org/GNOME/at-spi2-core) since 2.26. ErrorMessage, /// Reciprocal of `ErrorMessage`. /// Indicates that this object contains an error message describing an invalid condition /// in the target object(s). /// Included in upstream [AT-SPI2-CORE](https://gitlab.gnome.org/GNOME/at-spi2-core) since 2.26. ErrorFor, } atspi-common-0.13.0/src/role.rs000064400000000000000000000624511046102023000144210ustar 00000000000000use serde::{Deserialize, Serialize}; use zvariant::Type; use crate::AtspiError; #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type, Hash)] /// An accessible object role. /// /// To think of it in terms of HTML, any semantic element likely has a corollary in this enum. /// For example: `