wayland-backend-0.3.8/.cargo_vcs_info.json0000644000000001550000000000100140710ustar { "git": { "sha1": "48b04f19ecf747104533f536ddf626e561346d19" }, "path_in_vcs": "wayland-backend" }wayland-backend-0.3.8/CHANGELOG.md000064400000000000000000000142671046102023000145030ustar 00000000000000# CHANGELOG: wayland-backend ## Unreleased ## 0.3.8 -- 2025-01-31 ### Bugfixes - backend/rs: Prevent a potential deadlock during client cleanup ## 0.3.7 -- 2024-09-04 ### Bugfixes - backend/sys: Fix importing external objects with `Backend::manage_object` by associating the proxy with the correct event queue ## 0.3.6 -- 2024-07-16 ### Bugfixes - backend/rs: server: Fixed potential deadlock on object destruction ## 0.3.5 -- 2024-07-03 #### Additions - `Backend::manage_object` for handling foreign proxies with the sys backend #### Bugfixes - backend/rs: server: Fixed potential deadlock caused by dead clients - backend/sys: client dispatching now always uses distinct event queue ## 0.3.4 -- 2024-05-30 #### Additions - Add `rwh_06` feature for `raw-window-handle` 0.6 #### Bugfixes - backend/rs: `WAYLAND_DEBUG` now displays `fixed` as decimal, not integer. * Matches libwayland ## 0.3.3 -- 2024-01-29 #### Additions - client: Implement `Eq` for `Backend` #### Bugfixes - backend/sys: Fix error/segfault if object argument is no longer alive - backend/rs: Retry send/recv on `EINTR` * Matches the behavior of libwayland ## 0.3.2 -- 2023-09-25 #### Bugfixes - server/sys: Fix an issue where the backend could deadlock in some cases when sending an event that destroys an object. ## 0.3.1 -- 2023-09-19 #### Additions - client: Add `Backend::poll_fd` to return fd for polling ## 0.3.0 -- 2023-09-02 #### Breaking change - MSRV bumped to 1.65 - `io-lifetimes` is no longer a (public) dependency - The `Backend::prepare_read()` method now returns `None` if the inner queue of the backend needs to be dispatched using `Backend::dispatch_inner_queue()`, instead of trying to dispatch it by itself. This can only happen when using the `sys` backend, and allows the crate to behave properly when multiple threads try to read the socket using the libwayland API. - server: `ObjectData::destroyed` function signature has changed to pass the `Handle` and `self` as `Arc`. #### Additions - Add `flush` method to server `Handle`. #### Bugfixes - Setting `WAYLAND_DEBUG` server-side now properly prints incoming requests ## 0.2.0 -- 2023-07-13 #### Breaking changes - Update wayland-sys to 0.31 ## 0.1.2 -- 19/04/2023 #### Bugfixes - In the rust server backend, don't send `delete_id` messages for server-created objects. - In the system server backend, wakeup the event loop if there are pending destructors waiting to be precessed. ## 0.1.1 -- 16/02/2023 #### Bugfixes - In sys backend, fix global data not being cleaned up on display drop. ## 0.1.0 -- 27/12/2022 ## 0.1.0-beta.14 #### Bugfixes - In rust backend, retry read if message is incomplete, instead of `Malformed` error. ## 0.1.0-beta.10 #### Breaking changes - `Message` is now also generic on the `Fd` type, and `io_lifetimes::OwnedFd` is used instead of `RawFd` when appropriate. #### Bugfixes - The rust backend no longer ever does a blocking flush. - Server-side sys backend is now able to track liveness of external objects. ## 0.1.0-beta.8 #### Breaking changes - all backends: creating null `ObjectId` is now done through the `ObjectId::null()` method, and the `null_id()` methods on the backends are removed. - `Argument::Str` now contains an `Option`, which is correctly mapped to nullable strings. This fixes segfaults that previously occurred dereferencing the null pointer in the system backend. - server: `disable_global` and `remove_global` now require the state type parameter, like `create_global`. This is required for compatibility with libwayland 1.21. - `ArgumentType::Array` and `ArgumentType::NewId` no longer take a `AllowNull` and are never nullable. #### Additions - client: introduce `Backend::dispatch_inner_queue()` meant for ensuring a system backend in guest mode can still process events event it does not control reading the socket. - introduce the `log` cargo feature to control logging behavior - A dummy implementation of ClientData is now provided through `()` and all trait methods are optional ## 0.1.0-beta.7 #### Bugfixes - backend/sys: the inner lock is no longer held when destructors are invoked - backend/sys: sys backend does not abort process when `Backend::disable_global` is invoked more than once ## 0.1.0-beta.6 #### Additions - client: `ObjectId` now implements the `Hash` trait - server: `ObjectId`, `ClientId` and `GlobalId` now implement the `Hash` trait #### Bugfixes - backend/sys: the inner lock is no longer held when destructors are invoked ## 0.1.0-beta.5 #### Additions - client/sys: introduce `Backend::from_foreign_display` #### Bugfixes - The server backend now correctly associates interfaces with its object arguments when parsing messages with nullable object arguments. ## 0.1.0-beta.4 #### Bugfixes - server-rs: the inner lock is no longer help when destructors are invoked ## 0.1.0-beta.3 #### Bugfixes - server-sys: Skip unmanaged globals in the global filter (caused a segfault) ## 0.1.0-beta.2 #### Breaking changes - server-sys: move `display_ptr()` from `Backend` to `Handle`. ## 0.1.0-beta.1 #### Breaking changes - Both client and server APIs have been profoundly reworked. The backend now has internal locking mechanism allowing handles to it to be cloned and shared accross the application. #### Bugfixes - Fix a crash when exactly filling the internal buffers. ## 0.1.0-alpha7 #### Bugfixes - Client-side with the rust backend, `wl_display` events are now properly printed with other events when `WAYLAND_DEBUG=1` is set. ## 0.1.0-alpha6 #### Changes - Server-side, request callbacks are now allowed to omit providing an `ObjectData` for newly created objects if they triggered a protocol error #### Bugfixes - Fix display leaks on system server backend. - Fix a panic when trying to send a message with a null object argument followed by a non-null object argument - Fix various memory leaks ## 0.1.0-alpha5 - Expose `wl_display` pointer on system server backend ## 0.1.0-alpha4 #### Breaking changes - Server-side `ObjectData::destroyed()` now has access to the `&mut D` server-wide data. ## 0.1.0-alpha3 #### Additions - Introduce `WEnum::into_result` as a convenience method. ## 0.1.0-alpha2 ## 0.1.0-alpha1 Initial pre-release of the crate. wayland-backend-0.3.8/Cargo.lock0000644000000247310000000000100120520ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "cc" version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "shlex", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "concat-idents" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d" dependencies = [ "quote", "syn", ] [[package]] name = "dlib" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ "libloading", ] [[package]] name = "downcast-rs" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "env_logger" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", "log", "regex", "termcolor", ] [[package]] name = "errno" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys", ] [[package]] name = "hermit-abi" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "is-terminal" version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi", "libc", "windows-sys", ] [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets", ] [[package]] name = "linux-raw-sys" version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "log" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "pkg-config" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "raw-window-handle" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "raw-window-handle" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "wayland-backend" version = "0.3.8" dependencies = [ "cc", "concat-idents", "downcast-rs", "env_logger", "log", "raw-window-handle 0.5.2", "raw-window-handle 0.6.2", "rustix", "scoped-tls", "smallvec", "wayland-sys", ] [[package]] name = "wayland-sys" version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" dependencies = [ "dlib", "libc", "log", "memoffset", "once_cell", "pkg-config", ] [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[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_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[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_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[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_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" wayland-backend-0.3.8/Cargo.toml0000644000000040720000000000100120710ustar # 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.65" name = "wayland-backend" version = "0.3.8" authors = ["Elinor Berger "] build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Low-level bindings to the Wayland protocol" documentation = "https://docs.rs/wayland-backend/" readme = "README.md" keywords = ["wayland"] categories = [ "gui", "api-bindings", ] license = "MIT" repository = "https://github.com/smithay/wayland-rs" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [lib] name = "wayland_backend" path = "src/lib.rs" [[test]] name = "rs_sys_impls" path = "tests/rs_sys_impls.rs" [dependencies.downcast-rs] version = "1.2" [dependencies.log] version = "0.4" optional = true [dependencies.raw-window-handle] version = "0.5.0" optional = true [dependencies.rustix] version = "0.38.42" features = [ "event", "fs", "net", "process", ] [dependencies.rwh_06] version = "0.6.0" optional = true package = "raw-window-handle" [dependencies.scoped-tls] version = "1.0" optional = true [dependencies.smallvec] version = "1.9" features = [ "union", "const_generics", "const_new", ] [dependencies.wayland-sys] version = "0.31.6" features = [] [dev-dependencies.concat-idents] version = "1.1" [dev-dependencies.env_logger] version = "0.10" [build-dependencies.cc] version = "1.0" [features] client_system = [ "wayland-sys/client", "dep:scoped-tls", ] dlopen = ["wayland-sys/dlopen"] server_system = [ "wayland-sys/server", "dep:scoped-tls", ] wayland-backend-0.3.8/Cargo.toml.orig000064400000000000000000000030151046102023000155460ustar 00000000000000[package] name = "wayland-backend" version = "0.3.8" authors = ["Elinor Berger "] edition = "2021" rust-version = "1.65" repository = "https://github.com/smithay/wayland-rs" documentation = "https://docs.rs/wayland-backend/" license = "MIT" categories = ["gui", "api-bindings"] keywords = ["wayland"] description = "Low-level bindings to the Wayland protocol" readme = "README.md" build = "build.rs" [dependencies] wayland-sys = { version = "0.31.6", path = "../wayland-sys", features = [] } log = { version = "0.4", optional = true } scoped-tls = { version = "1.0", optional = true } downcast-rs = "1.2" raw-window-handle = { version = "0.5.0", optional = true } rwh_06 = { package = "raw-window-handle", version = "0.6.0", optional = true } [dependencies.smallvec] version = "1.9" # Some additional features can be enabled since wayland-rs requires at least Rust 1.65 features = [ "union", # 1.49 "const_generics", # 1.51 "const_new", # 1.51 ] [dependencies.rustix] version = "0.38.42" features = [ "event", "fs", "net", "process", ] [build-dependencies] cc = "1.0" [dev-dependencies] wayland-scanner = { path = "../wayland-scanner"} wayland-sys = { path = "../wayland-sys", features = ["client", "server"] } concat-idents = "1.1" env_logger = "0.10" [features] client_system = ["wayland-sys/client", "dep:scoped-tls"] server_system = ["wayland-sys/server", "dep:scoped-tls"] dlopen = ["wayland-sys/dlopen"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] wayland-backend-0.3.8/LICENSE.txt000064400000000000000000000020411046102023000145000ustar 00000000000000Copyright (c) 2015 Elinor Berger 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. wayland-backend-0.3.8/README.md000064400000000000000000000034121046102023000141370ustar 00000000000000[![crates.io](https://img.shields.io/crates/v/wayland-backend.svg)](https://crates.io/crates/wayland-backend) [![docs.rs](https://docs.rs/wayland-backend/badge.svg)](https://docs.rs/wayland-backend) [![Continuous Integration](https://github.com/Smithay/wayland-rs/workflows/Continuous%20Integration/badge.svg)](https://github.com/Smithay/wayland-rs/actions?query=workflow%3A%22Continuous+Integration%22) [![codecov](https://codecov.io/gh/Smithay/wayland-rs/branch/master/graph/badge.svg)](https://codecov.io/gh/Smithay/wayland-rs) # wayland-backend Backend API for wayland crates This crate provide low-level APIs for interacting with the Wayland protocol, both client-side and server-side. For higher-level interfaces, see the `wayland-client` and `wayland-server` crates. Two possible backends are provided by this crate: the system backend ([`sys`] module) which relies on the system-provided wayland libraries, and the rust backend ([`rs`] module) which is an alternative rust implementation of the protocol. The rust backend is always available, and the system backend is controlled by the `client_system` and `server_system` cargo features. The `dlopen` cargo feature ensures that the system wayland libraries are loaded dynamically at runtime, so that your executable does not link them and can gracefully handle their absence (for example by falling back to X11). Additionnaly the default backends are reexported as toplevel `client` and `server` modules in this crate. For both client and server, the default backend is the system one if the associated cargo feature is enabled, and the rust one otherwise. Using these reexports is the recommended way to use the crate. Both backends have the exact same API, except that the system backend additionnaly provides functions related to FFI.wayland-backend-0.3.8/build.rs000064400000000000000000000010251046102023000143230ustar 00000000000000fn main() { println!("cargo:rustc-check-cfg=cfg(coverage)"); if std::env::var("CARGO_FEATURE_LOG").ok().is_some() { // build the client shim cc::Build::new().file("src/sys/client_impl/log_shim.c").compile("log_shim_client"); println!("cargo:rerun-if-changed=src/sys/client_impl/log_shim.c"); // build the server shim cc::Build::new().file("src/sys/server_impl/log_shim.c").compile("log_shim_server"); println!("cargo:rerun-if-changed=src/sys/server_impl/log_shim.c"); } } wayland-backend-0.3.8/src/client_api.rs000064400000000000000000000331171046102023000161310ustar 00000000000000use std::{ any::Any, fmt, os::unix::{ io::{BorrowedFd, OwnedFd, RawFd}, net::UnixStream, }, sync::Arc, }; #[cfg(doc)] use std::io::ErrorKind::WouldBlock; use crate::protocol::{Interface, Message, ObjectInfo}; use super::client_impl; pub use crate::types::client::{InvalidId, NoWaylandLib, WaylandError}; /// A trait representing your data associated to an object /// /// You will only be given access to it as a `&` reference, so you /// need to handle interior mutability by yourself. /// /// The methods of this trait will be invoked internally every time a /// new object is created to initialize its data. pub trait ObjectData: downcast_rs::DowncastSync { /// Dispatch an event for the associated object /// /// If the event has a `NewId` argument, the callback must return the object data /// for the newly created object fn event( self: Arc, backend: &Backend, msg: Message, ) -> Option>; /// Notification that the object has been destroyed and is no longer active fn destroyed(&self, object_id: ObjectId); /// Helper for forwarding a Debug implementation of your `ObjectData` type /// /// By default will just print `ObjectData { ... }` #[cfg_attr(coverage, coverage(off))] fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ObjectData").finish_non_exhaustive() } /// Helper for accessing user data /// /// This function is used to back the `Proxy::data()` function in `wayland_client`. By default, /// it returns `self` (via [`Downcast`][downcast_rs::DowncastSync]), but this may be overridden to allow downcasting user data /// without needing to have access to the full type. fn data_as_any(&self) -> &dyn Any { self.as_any() } } impl std::fmt::Debug for dyn ObjectData { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.debug(f) } } downcast_rs::impl_downcast!(sync ObjectData); /// An ID representing a Wayland object /// /// The backend internally tracks which IDs are still valid, invalidates them when the protocol object they /// represent is destroyed. As such even though the Wayland protocol reuses IDs, you can confidently compare /// two `ObjectId` for equality, they will only compare as equal if they both represent the same protocol /// object. #[derive(Clone, PartialEq, Eq, Hash)] pub struct ObjectId { pub(crate) id: client_impl::InnerObjectId, } impl fmt::Display for ObjectId { #[cfg_attr(coverage, coverage(off))] #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.id.fmt(f) } } impl fmt::Debug for ObjectId { #[cfg_attr(coverage, coverage(off))] #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.id.fmt(f) } } impl ObjectId { /// Check if this is a null ID /// /// **Note:** This is not the same as checking if the ID is still valid, which cannot be done without the /// [`Backend`]. A null ID is the ID equivalent of a null pointer: it never has been valid and never will /// be. #[inline] pub fn is_null(&self) -> bool { self.id.is_null() } /// Create a null object ID /// /// This object ID is always invalid, and should be used as placeholder in requests that create objects, /// or for request with an optional `Object` argument. /// /// See [`Backend::send_request()`] for details. #[inline] pub fn null() -> ObjectId { client_impl::InnerBackend::null_id() } /// Interface of the represented object #[inline] pub fn interface(&self) -> &'static Interface { self.id.interface() } /// Return the protocol-level numerical ID of this object /// /// Protocol IDs are reused after object destruction, so this should not be used as a unique identifier, /// instead use the [`ObjectId`] directly, it implements [`Clone`], [`PartialEq`], [`Eq`] and [`Hash`]. #[inline] pub fn protocol_id(&self) -> u32 { self.id.protocol_id() } } /// A Wayland client backend /// /// This type hosts all the interface for interacting with the wayland protocol. It can be /// cloned, all clones refer to the same underlying connection. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Backend { pub(crate) backend: client_impl::InnerBackend, } /// A weak handle to a [`Backend`] /// /// This handle behaves similarly to [`Weak`][std::sync::Weak], and can be used to keep access to /// the backend without actually preventing it from being dropped. #[derive(Clone, Debug)] pub struct WeakBackend { inner: client_impl::WeakInnerBackend, } impl WeakBackend { /// Try to upgrade this weak handle to a [`Backend`] /// /// Returns [`None`] if the associated backend was already dropped. pub fn upgrade(&self) -> Option { self.inner.upgrade().map(|backend| Backend { backend }) } } impl Backend { /// Try to initialize a Wayland backend on the provided unix stream /// /// The provided stream should correspond to an already established unix connection with /// the Wayland server. /// /// This method can only fail on the `sys` backend if the `dlopen` cargo feature was enabled /// and the system wayland library could not be found. pub fn connect(stream: UnixStream) -> Result { client_impl::InnerBackend::connect(stream).map(|backend| Self { backend }) } /// Get a [`WeakBackend`] from this backend pub fn downgrade(&self) -> WeakBackend { WeakBackend { inner: self.backend.downgrade() } } /// Flush all pending outgoing requests to the server /// /// Most errors on this method mean that the Wayland connection is no longer valid, the only /// exception being an IO [`WouldBlock`] error. In that case it means that you should try flushing again /// later. /// /// You can however expect this method returning [`WouldBlock`] to be very rare: it can only occur if /// either your client sent a lot of big messages at once, or the server is very laggy. pub fn flush(&self) -> Result<(), WaylandError> { self.backend.flush() } /// Access the Wayland socket FD for polling #[inline] pub fn poll_fd(&self) -> BorrowedFd { self.backend.poll_fd() } /// Get the object ID for the `wl_display` #[inline] pub fn display_id(&self) -> ObjectId { self.backend.display_id() } /// Get the last error that occurred on this backend /// /// If this returns [`Some`], your Wayland connection is already dead. #[inline] pub fn last_error(&self) -> Option { self.backend.last_error() } /// Get the detailed protocol information about a wayland object /// /// Returns an error if the provided object ID is no longer valid. #[inline] pub fn info(&self, id: ObjectId) -> Result { self.backend.info(id) } /// Sends a request to the server /// /// Returns an error if the sender ID of the provided message is no longer valid. /// /// **Panic:** /// /// Several checks against the protocol specification are done, and this method will panic if they do /// not pass: /// /// - the message opcode must be valid for the sender interface /// - the argument list must match the prototype for the message associated with this opcode /// - if the method creates a new object, a [`ObjectId::null()`] must be given /// in the argument list at the appropriate place, and a `child_spec` (interface and version) /// can be provided. If one is provided, it'll be checked against the protocol spec. If the /// protocol specification does not define the interface of the created object (notable example /// is `wl_registry.bind`), the `child_spec` must be provided. pub fn send_request( &self, msg: Message, data: Option>, child_spec: Option<(&'static Interface, u32)>, ) -> Result { self.backend.send_request(msg, data, child_spec) } /// Access the object data associated with a given object ID /// /// Returns an error if the object ID is not longer valid or if it corresponds to a Wayland /// object that is not managed by this backend (when multiple libraries share the same Wayland /// socket via `libwayland` if using the system backend). pub fn get_data(&self, id: ObjectId) -> Result, InvalidId> { self.backend.get_data(id) } /// Set the object data associated with a given object ID /// /// Returns an error if the object ID is not longer valid or if it corresponds to a Wayland /// object that is not managed by this backend (when multiple libraries share the same Wayland /// socket via `libwayland` if using the system backend). pub fn set_data(&self, id: ObjectId, data: Arc) -> Result<(), InvalidId> { self.backend.set_data(id, data) } /// Create a new reading guard /// /// This is the first step for actually reading events from the Wayland socket. See /// [`ReadEventsGuard`] for how to use it. /// /// This call will not block, but may return [`None`] if the inner queue of the backend needs to /// be dispatched. In which case you should invoke /// [`dispatch_inner_queue()`][Self::dispatch_inner_queue()]. #[inline] #[must_use] pub fn prepare_read(&self) -> Option { client_impl::InnerReadEventsGuard::try_new(self.backend.clone()) .map(|guard| ReadEventsGuard { guard }) } /// Dispatches the inner queue of this backend if necessary /// /// This function actually only does something when using the system backend. It dispaches an inner /// queue that the backend uses to wrap `libwayland`. While this dispatching is generally done in /// [`ReadEventsGuard::read()`], if multiple threads are interacting with the /// Wayland socket it can happen that this queue was filled by another thread. In that case /// [`prepare_read()`][Self::prepare_read()] will return [`None`], and you should invoke /// this function instead of using the [`ReadEventsGuard`] /// /// Returns the number of messages that were dispatched to their [`ObjectData`] callbacks. #[inline] pub fn dispatch_inner_queue(&self) -> Result { self.backend.dispatch_inner_queue() } } /// Guard for synchronizing event reading across multiple threads /// /// If multiple threads need to read events from the Wayland socket concurrently, /// it is necessary to synchronize their access. Failing to do so may cause some of the /// threads to not be notified of new events, and sleep much longer than appropriate. /// /// This guard is provided to ensure the proper synchronization is done. The guard is created using /// the [`Backend::prepare_read()`] method. And the event reading is /// triggered by consuming the guard using the [`ReadEventsGuard::read()`] method, synchronizing /// with other threads as necessary so that only one of the threads will actually perform the socket read. /// /// If you plan to poll the Wayland socket for readiness, the file descriptor can be retrieved via /// the [`ReadEventsGuard::connection_fd()`] method. Note that for the synchronization to /// correctly occur, you must *always* create the `ReadEventsGuard` *before* polling the socket. /// /// Dropping the guard is valid and will cancel the prepared read. #[derive(Debug)] pub struct ReadEventsGuard { pub(crate) guard: client_impl::InnerReadEventsGuard, } impl ReadEventsGuard { /// Access the Wayland socket FD for polling #[inline] pub fn connection_fd(&self) -> BorrowedFd { self.guard.connection_fd() } /// Attempt to read events from the Wayland socket /// /// If multiple threads have a live reading guard, this method will block until all of them /// are either dropped or have their `read()` method invoked, at which point one of the threads /// will read events from the socket and invoke the callbacks for the received events. All /// threads will then resume their execution. /// /// This returns the number of dispatched events, or `0` if an other thread handled the dispatching. /// If no events are available to read from the socket, this returns a [`WouldBlock`] IO error. #[inline] pub fn read(self) -> Result { self.guard.read() } } pub(crate) struct DumbObjectData; impl ObjectData for DumbObjectData { #[cfg_attr(coverage, coverage(off))] fn event( self: Arc, _handle: &Backend, _msg: Message, ) -> Option> { unreachable!() } #[cfg_attr(coverage, coverage(off))] fn destroyed(&self, _object_id: ObjectId) { unreachable!() } } pub(crate) struct UninitObjectData; impl ObjectData for UninitObjectData { #[cfg_attr(coverage, coverage(off))] fn event( self: Arc, _handle: &Backend, msg: Message, ) -> Option> { panic!("Received a message on an uninitialized object: {:?}", msg); } #[cfg_attr(coverage, coverage(off))] fn destroyed(&self, _object_id: ObjectId) {} #[cfg_attr(coverage, coverage(off))] fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("UninitObjectData").finish() } } wayland-backend-0.3.8/src/core_interfaces.rs000064400000000000000000000060651046102023000171570ustar 00000000000000//! Core interfaces of the protocol //! //! This module contains hard-coded interfaces for `wl_display`, `wl_registry` and `wl_callback`. //! These interfaces are frozen in the protocol and can never change. They are the only interfaces //! which the backends need to be aware of in particular. use crate::protocol::{AllowNull, ArgumentType, Interface, MessageDesc, ANONYMOUS_INTERFACE}; /// Interface `wl_display` pub static WL_DISPLAY_INTERFACE: Interface = Interface { name: "wl_display", version: 1, requests: &[ MessageDesc { name: "sync", since: 1, is_destructor: false, signature: &[ArgumentType::NewId], child_interface: Some(&WL_CALLBACK_INTERFACE), arg_interfaces: &[], }, MessageDesc { name: "get_registry", since: 1, is_destructor: false, signature: &[ArgumentType::NewId], child_interface: Some(&WL_REGISTRY_INTERFACE), arg_interfaces: &[], }, ], events: &[ MessageDesc { name: "error", since: 1, is_destructor: false, signature: &[ ArgumentType::Object(AllowNull::No), ArgumentType::Uint, ArgumentType::Str(AllowNull::No), ], child_interface: None, arg_interfaces: &[&ANONYMOUS_INTERFACE], }, MessageDesc { name: "delete_id", since: 1, is_destructor: false, signature: &[ArgumentType::Uint], child_interface: None, arg_interfaces: &[], }, ], c_ptr: None, }; /// Interface `wl_registry` pub static WL_REGISTRY_INTERFACE: Interface = Interface { name: "wl_registry", version: 1, requests: &[MessageDesc { name: "bind", since: 1, is_destructor: false, signature: &[ ArgumentType::Uint, ArgumentType::Str(AllowNull::No), ArgumentType::Uint, ArgumentType::NewId, ], child_interface: None, arg_interfaces: &[], }], events: &[ MessageDesc { name: "global", since: 1, is_destructor: false, signature: &[ArgumentType::Uint, ArgumentType::Str(AllowNull::No), ArgumentType::Uint], child_interface: None, arg_interfaces: &[], }, MessageDesc { name: "global_remove", since: 1, is_destructor: false, signature: &[ArgumentType::Uint], child_interface: None, arg_interfaces: &[], }, ], c_ptr: None, }; /// Interface `wl_callback` pub static WL_CALLBACK_INTERFACE: Interface = Interface { name: "wl_callback", version: 1, requests: &[], events: &[MessageDesc { name: "done", since: 1, is_destructor: true, signature: &[ArgumentType::Uint], child_interface: None, arg_interfaces: &[], }], c_ptr: None, }; wayland-backend-0.3.8/src/debug.rs000064400000000000000000000046221046102023000151070ustar 00000000000000//! Debugging helpers to handle `WAYLAND_DEBUG` env variable. use std::{ fmt::Display, os::unix::io::AsRawFd, time::{SystemTime, UNIX_EPOCH}, }; use crate::protocol::Argument; /// The `WAYLAND_DEBUG` env variable is set to debug client. pub fn has_debug_client_env() -> bool { matches!(std::env::var_os("WAYLAND_DEBUG"), Some(str) if str == "1" || str == "client") } /// Print the dispatched message to stderr in a following format: /// /// `[timestamp] <- interface@id.msg_name(args)` #[cfg_attr(coverage, coverage(off))] pub fn print_dispatched_message( interface: &str, id: u32, msg_name: &str, args: &[Argument], ) { // Add timestamp to output. print_timestamp(); eprint!(" <- {}@{}.{}, ({})", interface, id, msg_name, DisplaySlice(args)); // Add a new line. eprintln!(); } /// Print the send message to stderr in a following format: /// /// `[timestamp] -> interface@id.msg_name(args)` #[cfg_attr(coverage, coverage(off))] pub fn print_send_message( interface: &str, id: u32, msg_name: &str, args: &[Argument], discarded: bool, ) { // Add timestamp to output. print_timestamp(); if discarded { eprint!("[discarded]"); } eprint!(" -> {}@{}.{}({})", interface, id, msg_name, DisplaySlice(args)); // Add a new line. eprintln!(); } pub(crate) struct DisplaySlice<'a, D>(pub &'a [D]); impl Display for DisplaySlice<'_, D> { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut it = self.0.iter(); if let Some(val) = it.next() { write!(f, "{}", val)?; } for val in it { write!(f, ", {}", val)?; } Ok(()) } } /// Print timestamp in seconds.microseconds format. #[cfg_attr(coverage, coverage(off))] fn print_timestamp() { if let Ok(timestamp) = SystemTime::now().duration_since(UNIX_EPOCH) { // NOTE this is all to make timestamps the same with libwayland, so the log doesn't look // out of place when sys tries to log on their own. let time = (timestamp.as_secs() * 1000000 + timestamp.subsec_nanos() as u64 / 1000) as u32; // NOTE annotate timestamp so we know which library emmited the log entry. eprint!("[{:7}.{:03}][rs]", time / 1000, time % 1000); } } wayland-backend-0.3.8/src/lib.rs000064400000000000000000000114131046102023000145630ustar 00000000000000//! Backend API for wayland crates //! //! This crate provide low-level APIs for interacting with the Wayland protocol, //! both client-side and server-side. //! //! Two possible backends are provided by this crate: the system backend ([`sys`] module) //! which relies on the system-provided wayland libraries, and the rust backend ([`rs`] module) //! which is an alternative rust implementation of the protocol. The rust backend is always //! available, and the system backend is controlled by the `client_system` and `server_system` //! cargo features. The `dlopen` cargo feature ensures that the system wayland //! libraries are loaded dynamically at runtime, so that your executable does not link them and //! can gracefully handle their absence (for example by falling back to X11). //! //! Additionally the default backends are reexported as toplevel `client` and `server` modules //! in this crate. For both client and server, the default backend is the system one if the //! associated cargo feature is enabled, and the rust one otherwise. //! //! Using these reexports is the recommended way to use the crate: //! - If you don't need the `*_system` features, an other crate enabling them will not impact your code in //! any way as both backends have the same API (the system backend only has more methods) //! - If your code needs to do FFI, you just need to directly depend on `wayland-backend` with the //! appropriate `*_system` feature enabled, and all the other crates in your dependency tree will //! automatically use the `sys` backend. //! //! Both the `wayland-client` and `wayland-server` crates follow this principle, so everything will "Just //! Work" when using them. //! //! ## Logging //! //! This crate can generate some runtime error message (notably when a protocol error occurs). By default //! those messages are printed to stderr. If you activate the `log` cargo feature, they will instead be //! piped through the `log` crate. //! //! ## raw-window-handle integration //! //! The `rwh_06` feature activates the [`HasDisplayHandle`][raw_window_handle::HasDisplayHandle] implementation //! for the client module [`Backend`][client::Backend]. //! //! ### Deprecated raw-window-handle versions //! //! While raw-window-handle 0.5 is supported via the `raw-window-handle` feature, it is deprecated and will be removed in the future. //! //! Note that the `client_system` feature must also be enabled for the implementation to be activated. #![forbid(improper_ctypes)] #![deny(unsafe_op_in_unsafe_fn)] #![warn(missing_docs, missing_debug_implementations)] // The api modules are imported two times each, this is not accidental #![allow(clippy::duplicate_mod)] #![cfg_attr(coverage, feature(coverage_attribute))] // Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc -p #![cfg_attr(docsrs, feature(doc_auto_cfg))] /// Reexport of the `smallvec` crate, which is part of `wayland-backend`'s public API. pub extern crate smallvec; /// Helper macro for quickly making a [`Message`][crate::protocol::Message] #[macro_export] macro_rules! message { ($sender_id: expr, $opcode: expr, [$($args: expr),* $(,)?] $(,)?) => { $crate::protocol::Message { sender_id: $sender_id, opcode: $opcode, args: $crate::smallvec::smallvec![$($args),*], } } } // internal imports for dispatching logging depending on the `log` feature #[cfg(feature = "log")] #[allow(unused_imports)] use log::{debug as log_debug, error as log_error, info as log_info, warn as log_warn}; #[cfg(not(feature = "log"))] #[allow(unused_imports)] use std::{ eprintln as log_error, eprintln as log_warn, eprintln as log_info, eprintln as log_debug, }; #[cfg(any(test, feature = "client_system", feature = "server_system"))] pub mod sys; pub mod rs; #[cfg(not(feature = "client_system"))] pub use rs::client; #[cfg(feature = "client_system")] pub use sys::client; #[cfg(not(feature = "server_system"))] pub use rs::server; #[cfg(feature = "server_system")] pub use sys::server; #[cfg(test)] mod test; mod core_interfaces; mod debug; pub mod protocol; mod types; /* * These trampoline functions need to always be here because the build script cannot * conditionally build their C counterparts on whether the crate is tested or not... * They'll be optimized out when unused. */ #[cfg(feature = "log")] #[no_mangle] extern "C" fn wl_log_rust_logger_client(msg: *const std::os::raw::c_char) { let cstr = unsafe { std::ffi::CStr::from_ptr(msg) }; let text = cstr.to_string_lossy(); log::error!("{}", text); } #[cfg(feature = "log")] #[no_mangle] extern "C" fn wl_log_rust_logger_server(msg: *const std::os::raw::c_char) { let cstr = unsafe { std::ffi::CStr::from_ptr(msg) }; let text = cstr.to_string_lossy(); log::error!("{}", text); } wayland-backend-0.3.8/src/protocol.rs000064400000000000000000000272631046102023000156700ustar 00000000000000//! Types and utilities for manipulating the Wayland protocol use std::{ffi::CString, os::unix::io::AsRawFd}; pub use wayland_sys::common::{wl_argument, wl_interface, wl_message}; /// Describes whether an argument may have a null value. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum AllowNull { /// Null values are allowed. Yes, /// Null values are forbidden. No, } /// Enum of possible argument types as recognized by the wire #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum ArgumentType { /// An integer argument. Represented by a [`i32`]. Int, /// An unsigned integer argument. Represented by a [`u32`]. Uint, /// A signed fixed point number with 1/256 precision Fixed, /// A string. This is represented as a [`CString`] in a message. Str(AllowNull), /// Id of a wayland object Object(AllowNull), /// Id of a newly created wayland object NewId, /// `Vec` Array, /// A file descriptor argument. Represented by a [`RawFd`]. /// /// [`RawFd`]: std::os::fd::RawFd Fd, } impl ArgumentType { /// Returns true if the type of the argument is the same. pub fn same_type(self, other: Self) -> bool { std::mem::discriminant(&self) == std::mem::discriminant(&other) } } /// Enum of possible argument of the protocol #[derive(Debug, Clone)] #[allow(clippy::box_collection)] pub enum Argument { /// An integer argument. Represented by a [`i32`]. Int(i32), /// An unsigned integer argument. Represented by a [`u32`]. Uint(u32), /// A signed fixed point number with 1/256 precision Fixed(i32), /// CString /// /// The value is boxed to reduce the stack size of Argument. The performance /// impact is negligible as `string` arguments are pretty rare in the protocol. Str(Option>), /// Id of a wayland object Object(Id), /// Id of a newly created wayland object NewId(Id), /// `Vec` /// /// The value is boxed to reduce the stack size of Argument. The performance /// impact is negligible as `array` arguments are pretty rare in the protocol. Array(Box>), /// A file descriptor argument. Represented by a [`RawFd`]. /// /// [`RawFd`]: std::os::fd::RawFd Fd(Fd), } impl Argument { /// Retrieve the type of a given argument instance pub fn get_type(&self) -> ArgumentType { match *self { Self::Int(_) => ArgumentType::Int, Self::Uint(_) => ArgumentType::Uint, Self::Fixed(_) => ArgumentType::Fixed, Self::Str(_) => ArgumentType::Str(AllowNull::Yes), Self::Object(_) => ArgumentType::Object(AllowNull::Yes), Self::NewId(_) => ArgumentType::NewId, Self::Array(_) => ArgumentType::Array, Self::Fd(_) => ArgumentType::Fd, } } fn map_fd(self, f: &mut impl FnMut(Fd) -> T) -> Argument { match self { Self::Int(val) => Argument::Int(val), Self::Uint(val) => Argument::Uint(val), Self::Fixed(val) => Argument::Fixed(val), Self::Str(val) => Argument::Str(val), Self::Object(val) => Argument::Object(val), Self::NewId(val) => Argument::NewId(val), Self::Array(val) => Argument::Array(val), Self::Fd(val) => Argument::Fd(f(val)), } } } impl PartialEq for Argument { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Int(a), Self::Int(b)) => a == b, (Self::Uint(a), Self::Uint(b)) => a == b, (Self::Fixed(a), Self::Fixed(b)) => a == b, (Self::Str(a), Self::Str(b)) => a == b, (Self::Object(a), Self::Object(b)) => a == b, (Self::NewId(a), Self::NewId(b)) => a == b, (Self::Array(a), Self::Array(b)) => a == b, (Self::Fd(a), Self::Fd(b)) => a.as_raw_fd() == b.as_raw_fd(), _ => false, } } } impl Eq for Argument {} impl std::fmt::Display for Argument { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Int(value) => write!(f, "{}", value), Self::Uint(value) => write!(f, "{}", value), Self::Fixed(value) => write!(f, "{:.4}", *value as f64 / 256.0), Self::Str(value) => write!(f, "{:?}", value), Self::Object(value) => write!(f, "{}", value), Self::NewId(value) => write!(f, "{}", value), Self::Array(value) => write!(f, "{:?}", value), Self::Fd(value) => write!(f, "{}", value.as_raw_fd()), } } } /// Description of wayland interface. /// /// An interface describes the possible requests and events that a wayland client and compositor use to /// communicate. #[derive(Debug)] pub struct Interface { /// The name of the interface. pub name: &'static str, /// The maximum supported version of the interface. pub version: u32, /// A list that describes every request this interface supports. pub requests: &'static [MessageDesc], /// A list that describes every event this interface supports. pub events: &'static [MessageDesc], /// A C representation of this interface that may be used to interoperate with libwayland. pub c_ptr: Option<&'static wayland_sys::common::wl_interface>, } impl std::fmt::Display for Interface { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.name) } } /// Wire metadata of a given message #[derive(Copy, Clone, Debug)] pub struct MessageDesc { /// Name of this message pub name: &'static str, /// Signature of the message pub signature: &'static [ArgumentType], /// Minimum required version of the interface pub since: u32, /// Whether this message is a destructor pub is_destructor: bool, /// The child interface created from this message. /// /// In the wayland xml format, this corresponds to the `new_id` type. pub child_interface: Option<&'static Interface>, /// The interfaces passed into this message as arguments. pub arg_interfaces: &'static [&'static Interface], } /// Special interface representing an anonymous object pub static ANONYMOUS_INTERFACE: Interface = Interface { name: "", version: 0, requests: &[], events: &[], c_ptr: None }; /// Description of the protocol-level information of an object #[derive(Copy, Clone, Debug)] pub struct ObjectInfo { /// The protocol ID pub id: u32, /// The interface pub interface: &'static Interface, /// The version pub version: u32, } /// A protocol error /// /// This kind of error is generated by the server if your client didn't respect /// the protocol, after which the server will kill your connection. #[derive(Clone, Debug)] pub struct ProtocolError { /// The error code associated with the error /// /// It should be interpreted as an instance of the `Error` enum of the /// associated interface. pub code: u32, /// The id of the object that caused the error pub object_id: u32, /// The interface of the object that caused the error pub object_interface: String, /// The message sent by the server describing the error pub message: String, } /// Number of arguments that are stocked inline in a `Message` before allocating /// /// This is a ad-hoc number trying to reach a good balance between avoiding too many allocations /// and keeping the stack size of `Message` small. // Note: Keep in sync with `wayland_scanner::common::gen_write_body`. pub const INLINE_ARGS: usize = 4; /// Represents a message that has been sent from some object. #[derive(Clone, Debug)] pub struct Message { /// The id of the object that sent the message. pub sender_id: Id, /// The opcode of the message. pub opcode: u16, /// The arguments of the message. pub args: smallvec::SmallVec<[Argument; INLINE_ARGS]>, } impl Message { /// Map some closure on all Fd contained in this message, to change the Fd generic parameter. pub fn map_fd(self, mut f: impl FnMut(Fd) -> T) -> Message { Message { sender_id: self.sender_id, opcode: self.opcode, args: self.args.into_iter().map(move |arg| arg.map_fd(&mut f)).collect(), } } } impl PartialEq for Message { fn eq(&self, other: &Self) -> bool { self.sender_id == other.sender_id && self.opcode == other.opcode && self.args == other.args } } impl Eq for Message {} impl std::error::Error for ProtocolError {} impl std::fmt::Display for ProtocolError { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { write!( f, "Protocol error {} on object {}@{}: {}", self.code, self.object_interface, self.object_id, self.message ) } } /// Returns true if the two interfaces are the same. #[inline] pub fn same_interface(a: &'static Interface, b: &'static Interface) -> bool { std::ptr::eq(a, b) || a.name == b.name } pub(crate) fn check_for_signature( signature: &[ArgumentType], args: &[Argument], ) -> bool { if signature.len() != args.len() { return false; } for (typ, arg) in signature.iter().copied().zip(args.iter()) { if !arg.get_type().same_type(typ) { return false; } } true } #[inline] #[allow(dead_code)] pub(crate) fn same_interface_or_anonymous(a: &'static Interface, b: &'static Interface) -> bool { same_interface(a, b) || same_interface(a, &ANONYMOUS_INTERFACE) } /// An enum value in the protocol. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum WEnum { /// The interpreted value Value(T), /// The stored value does not match one defined by the protocol file Unknown(u32), } /// Error representing an unknown numeric variant for a [`WEnum`] #[derive(Debug, Copy, Clone)] pub struct WEnumError { typ: &'static str, value: u32, } impl std::error::Error for WEnumError {} impl std::fmt::Display for WEnumError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Unknown numeric value {} for enum {}", self.value, self.typ) } } impl WEnum { /// Convert this [`WEnum`] into a result /// /// This can be used to take advantage of the numerous helper methods on [`Result`] if you /// don't plan to handle the unknown case of this enum. /// /// You can also use the [`From`] and [`Into`] traits to perform the same conversion. #[inline] pub fn into_result(self) -> Result { match self { Self::Value(v) => Ok(v), Self::Unknown(value) => Err(WEnumError { typ: std::any::type_name::(), value }), } } } impl From> for Result { fn from(me: WEnum) -> Self { me.into_result() } } impl> From for WEnum { /// Constructs an enum from the integer format used by the wayland protocol. fn from(v: u32) -> Self { match T::try_from(v) { Ok(t) => Self::Value(t), Err(_) => Self::Unknown(v), } } } impl> From> for u32 { /// Converts an enum into a numerical form used by the wayland protocol. fn from(enu: WEnum) -> u32 { match enu { WEnum::Unknown(u) => u, WEnum::Value(t) => t.into(), } } } wayland-backend-0.3.8/src/rs/client_impl/mod.rs000064400000000000000000000720111046102023000175200ustar 00000000000000//! Client-side rust implementation of a Wayland protocol backend use std::{ fmt, os::unix::{ io::{AsRawFd, BorrowedFd, OwnedFd, RawFd}, net::UnixStream, }, sync::{Arc, Condvar, Mutex, MutexGuard, Weak}, }; use crate::{ core_interfaces::WL_DISPLAY_INTERFACE, debug, protocol::{ check_for_signature, same_interface, same_interface_or_anonymous, AllowNull, Argument, ArgumentType, Interface, Message, ObjectInfo, ProtocolError, ANONYMOUS_INTERFACE, INLINE_ARGS, }, }; use smallvec::SmallVec; use super::{ client::*, map::{Object, ObjectMap, SERVER_ID_LIMIT}, socket::{BufferedSocket, Socket}, wire::MessageParseError, }; #[derive(Debug, Clone)] struct Data { client_destroyed: bool, server_destroyed: bool, user_data: Arc, serial: u32, } /// An ID representing a Wayland object #[derive(Clone)] pub struct InnerObjectId { serial: u32, id: u32, interface: &'static Interface, } impl std::cmp::PartialEq for InnerObjectId { fn eq(&self, other: &Self) -> bool { self.id == other.id && self.serial == other.serial && same_interface(self.interface, other.interface) } } impl std::cmp::Eq for InnerObjectId {} impl std::hash::Hash for InnerObjectId { fn hash(&self, state: &mut H) { self.serial.hash(state); self.id.hash(state); } } impl fmt::Display for InnerObjectId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}@{}", self.interface.name, self.id) } } impl fmt::Debug for InnerObjectId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ObjectId({}, {})", self, self.serial) } } impl InnerObjectId { pub fn is_null(&self) -> bool { self.id == 0 } pub fn interface(&self) -> &'static Interface { self.interface } pub fn protocol_id(&self) -> u32 { self.id } } #[derive(Debug)] struct ProtocolState { socket: BufferedSocket, map: ObjectMap, last_error: Option, last_serial: u32, debug: bool, } #[derive(Debug)] struct ReadingState { prepared_reads: usize, read_condvar: Arc, read_serial: usize, } #[derive(Debug)] pub struct ConnectionState { protocol: Mutex, read: Mutex, } impl ConnectionState { fn lock_protocol(&self) -> MutexGuard { self.protocol.lock().unwrap() } fn lock_read(&self) -> MutexGuard { self.read.lock().unwrap() } } #[derive(Clone, Debug)] pub struct InnerBackend { state: Arc, } #[derive(Clone, Debug)] pub struct WeakInnerBackend { state: Weak, } impl WeakInnerBackend { pub fn upgrade(&self) -> Option { Weak::upgrade(&self.state).map(|state| InnerBackend { state }) } } impl PartialEq for InnerBackend { fn eq(&self, rhs: &Self) -> bool { Arc::ptr_eq(&self.state, &rhs.state) } } impl Eq for InnerBackend {} impl InnerBackend { pub fn downgrade(&self) -> WeakInnerBackend { WeakInnerBackend { state: Arc::downgrade(&self.state) } } pub fn connect(stream: UnixStream) -> Result { let socket = BufferedSocket::new(Socket::from(stream)); let mut map = ObjectMap::new(); map.insert_at( 1, Object { interface: &WL_DISPLAY_INTERFACE, version: 1, data: Data { client_destroyed: false, server_destroyed: false, user_data: Arc::new(DumbObjectData), serial: 0, }, }, ) .unwrap(); let debug = debug::has_debug_client_env(); Ok(Self { state: Arc::new(ConnectionState { protocol: Mutex::new(ProtocolState { socket, map, last_error: None, last_serial: 0, debug, }), read: Mutex::new(ReadingState { prepared_reads: 0, read_condvar: Arc::new(Condvar::new()), read_serial: 0, }), }), }) } /// Flush all pending outgoing requests to the server pub fn flush(&self) -> Result<(), WaylandError> { let mut guard = self.state.lock_protocol(); guard.no_last_error()?; if let Err(e) = guard.socket.flush() { return Err(guard.store_if_not_wouldblock_and_return_error(e)); } Ok(()) } pub fn poll_fd(&self) -> BorrowedFd { let raw_fd = self.state.lock_protocol().socket.as_raw_fd(); // This allows the lifetime of the BorrowedFd to be tied to &self rather than the lock guard, // which is the real safety concern unsafe { BorrowedFd::borrow_raw(raw_fd) } } } #[derive(Debug)] pub struct InnerReadEventsGuard { state: Arc, done: bool, } impl InnerReadEventsGuard { /// Create a new reading guard /// /// This call will not block, but event callbacks may be invoked in the process /// of preparing the guard. pub fn try_new(backend: InnerBackend) -> Option { backend.state.lock_read().prepared_reads += 1; Some(Self { state: backend.state, done: false }) } /// Access the Wayland socket FD for polling pub fn connection_fd(&self) -> BorrowedFd { let raw_fd = self.state.lock_protocol().socket.as_raw_fd(); // This allows the lifetime of the BorrowedFd to be tied to &self rather than the lock guard, // which is the real safety concern unsafe { BorrowedFd::borrow_raw(raw_fd) } } /// Attempt to read events from the Wayland socket /// /// If multiple threads have a live reading guard, this method will block until all of them /// are either dropped or have their `read()` method invoked, at which point on of the threads /// will read events from the socket and invoke the callbacks for the received events. All /// threads will then resume their execution. /// /// This returns the number of dispatched events, or `0` if an other thread handled the dispatching. /// If no events are available to read from the socket, this returns a `WouldBlock` IO error. pub fn read(mut self) -> Result { let mut guard = self.state.lock_read(); guard.prepared_reads -= 1; self.done = true; if guard.prepared_reads == 0 { // We should be the one reading let ret = dispatch_events(self.state.clone()); // wake up other threads guard.read_serial = guard.read_serial.wrapping_add(1); guard.read_condvar.notify_all(); // forward the return value ret } else { // We should wait for an other thread to read (or cancel) let serial = guard.read_serial; let condvar = guard.read_condvar.clone(); let _guard = condvar.wait_while(guard, |backend| serial == backend.read_serial).unwrap(); self.state.lock_protocol().no_last_error()?; Ok(0) } } } impl Drop for InnerReadEventsGuard { fn drop(&mut self) { if !self.done { let mut guard = self.state.lock_read(); guard.prepared_reads -= 1; if guard.prepared_reads == 0 { // Cancel the read guard.read_serial = guard.read_serial.wrapping_add(1); guard.read_condvar.notify_all(); } } } } impl InnerBackend { pub fn display_id(&self) -> ObjectId { ObjectId { id: InnerObjectId { serial: 0, id: 1, interface: &WL_DISPLAY_INTERFACE } } } pub fn last_error(&self) -> Option { self.state.lock_protocol().last_error.clone() } pub fn info(&self, id: ObjectId) -> Result { let object = self.state.lock_protocol().get_object(id.id.clone())?; if object.data.client_destroyed { Err(InvalidId) } else { Ok(ObjectInfo { id: id.id.id, interface: object.interface, version: object.version }) } } pub fn null_id() -> ObjectId { ObjectId { id: InnerObjectId { serial: 0, id: 0, interface: &ANONYMOUS_INTERFACE } } } pub fn send_request( &self, Message { sender_id: ObjectId { id }, opcode, args }: Message, data: Option>, child_spec: Option<(&'static Interface, u32)>, ) -> Result { let mut guard = self.state.lock_protocol(); let object = guard.get_object(id.clone())?; let message_desc = match object.interface.requests.get(opcode as usize) { Some(msg) => msg, None => { panic!("Unknown opcode {} for object {}@{}.", opcode, object.interface.name, id.id); } }; if object.data.client_destroyed { if guard.debug { debug::print_send_message(id.interface.name, id.id, message_desc.name, &args, true); } return Err(InvalidId); } if !check_for_signature(message_desc.signature, &args) { panic!( "Unexpected signature for request {}@{}.{}: expected {:?}, got {:?}.", object.interface.name, id.id, message_desc.name, message_desc.signature, args ); } // Prepare the child object let child_spec = if message_desc .signature .iter() .any(|arg| matches!(arg, ArgumentType::NewId)) { if let Some((iface, version)) = child_spec { if let Some(child_interface) = message_desc.child_interface { if !same_interface(child_interface, iface) { panic!( "Error when sending request {}@{}.{}: expected interface {} but got {}", object.interface.name, id.id, message_desc.name, child_interface.name, iface.name ); } if version != object.version { panic!( "Error when sending request {}@{}.{}: expected version {} but got {}", object.interface.name, id.id, message_desc.name, object.version, version ); } } Some((iface, version)) } else if let Some(child_interface) = message_desc.child_interface { Some((child_interface, object.version)) } else { panic!( "Error when sending request {}@{}.{}: target interface must be specified for a generic constructor.", object.interface.name, id.id, message_desc.name ); } } else { None }; let child = if let Some((child_interface, child_version)) = child_spec { let child_serial = guard.next_serial(); let child = Object { interface: child_interface, version: child_version, data: Data { client_destroyed: false, server_destroyed: false, user_data: Arc::new(DumbObjectData), serial: child_serial, }, }; let child_id = guard.map.client_insert_new(child); guard .map .with(child_id, |obj| { obj.data.user_data = data.expect( "Sending a request creating an object without providing an object data.", ); }) .unwrap(); Some((child_id, child_serial, child_interface)) } else { None }; // Prepare the message in a debug-compatible way let args = args.into_iter().map(|arg| { if let Argument::NewId(ObjectId { id: p }) = arg { if p.id != 0 { panic!("The newid provided when sending request {}@{}.{} is not a placeholder.", object.interface.name, id.id, message_desc.name); } if let Some((child_id, child_serial, child_interface)) = child { Argument::NewId(ObjectId { id: InnerObjectId { id: child_id, serial: child_serial, interface: child_interface}}) } else { unreachable!(); } } else { arg } }).collect::>(); if guard.debug { debug::print_send_message( object.interface.name, id.id, message_desc.name, &args, false, ); } #[cfg(feature = "log")] crate::log_debug!("Sending {}.{} ({})", id, message_desc.name, debug::DisplaySlice(&args)); // Send the message let mut msg_args = SmallVec::with_capacity(args.len()); let mut arg_interfaces = message_desc.arg_interfaces.iter(); for (i, arg) in args.into_iter().enumerate() { msg_args.push(match arg { Argument::Array(a) => Argument::Array(a), Argument::Int(i) => Argument::Int(i), Argument::Uint(u) => Argument::Uint(u), Argument::Str(s) => Argument::Str(s), Argument::Fixed(f) => Argument::Fixed(f), Argument::NewId(nid) => Argument::NewId(nid.id.id), Argument::Fd(f) => Argument::Fd(f), Argument::Object(o) => { let next_interface = arg_interfaces.next().unwrap(); if o.id.id != 0 { let arg_object = guard.get_object(o.id.clone())?; if !same_interface_or_anonymous(next_interface, arg_object.interface) { panic!("Request {}@{}.{} expects an argument of interface {} but {} was provided instead.", object.interface.name, id.id, message_desc.name, next_interface.name, arg_object.interface.name); } } else if !matches!(message_desc.signature[i], ArgumentType::Object(AllowNull::Yes)) { panic!("Request {}@{}.{} expects an non-null object argument.", object.interface.name, id.id, message_desc.name); } Argument::Object(o.id.id) } }); } let msg = Message { sender_id: id.id, opcode, args: msg_args }; if let Err(err) = guard.socket.write_message(&msg) { guard.last_error = Some(WaylandError::Io(err)); } // Handle destruction if relevant if message_desc.is_destructor { guard .map .with(id.id, |obj| { obj.data.client_destroyed = true; }) .unwrap(); object.data.user_data.destroyed(ObjectId { id }); } if let Some((child_id, child_serial, child_interface)) = child { Ok(ObjectId { id: InnerObjectId { id: child_id, serial: child_serial, interface: child_interface, }, }) } else { Ok(Self::null_id()) } } pub fn get_data(&self, id: ObjectId) -> Result, InvalidId> { let object = self.state.lock_protocol().get_object(id.id)?; Ok(object.data.user_data) } pub fn set_data(&self, id: ObjectId, data: Arc) -> Result<(), InvalidId> { self.state .lock_protocol() .map .with(id.id.id, move |objdata| { if objdata.data.serial != id.id.serial { Err(InvalidId) } else { objdata.data.user_data = data; Ok(()) } }) .unwrap_or(Err(InvalidId)) } // Nothing to do here, we don't have an inner queue pub fn dispatch_inner_queue(&self) -> Result { Ok(0) } } impl ProtocolState { fn next_serial(&mut self) -> u32 { self.last_serial = self.last_serial.wrapping_add(1); self.last_serial } #[inline] fn no_last_error(&self) -> Result<(), WaylandError> { if let Some(ref err) = self.last_error { Err(err.clone()) } else { Ok(()) } } #[inline] fn store_and_return_error(&mut self, err: impl Into) -> WaylandError { let err = err.into(); crate::log_error!("{}", err); self.last_error = Some(err.clone()); err } #[inline] fn store_if_not_wouldblock_and_return_error(&mut self, e: std::io::Error) -> WaylandError { if e.kind() != std::io::ErrorKind::WouldBlock { self.store_and_return_error(e) } else { e.into() } } fn get_object(&self, id: InnerObjectId) -> Result, InvalidId> { let object = self.map.find(id.id).ok_or(InvalidId)?; if object.data.serial != id.serial { return Err(InvalidId); } Ok(object) } fn handle_display_event(&mut self, message: Message) -> Result<(), WaylandError> { if self.debug { debug::print_dispatched_message( "wl_display", message.sender_id, if message.opcode == 0 { "error" } else { "delete_id" }, &message.args, ); } match message.opcode { 0 => { // wl_display.error if let [Argument::Object(obj), Argument::Uint(code), Argument::Str(Some(ref message))] = message.args[..] { let object = self.map.find(obj); let err = WaylandError::Protocol(ProtocolError { code, object_id: obj, object_interface: object .map(|obj| obj.interface.name) .unwrap_or("") .into(), message: message.to_string_lossy().into(), }); return Err(self.store_and_return_error(err)); } else { unreachable!() } } 1 => { // wl_display.delete_id if let [Argument::Uint(id)] = message.args[..] { let client_destroyed = self .map .with(id, |obj| { obj.data.server_destroyed = true; obj.data.client_destroyed }) .unwrap_or(false); if client_destroyed { self.map.remove(id); } } else { unreachable!() } } _ => unreachable!(), } Ok(()) } } fn dispatch_events(state: Arc) -> Result { let backend = Backend { backend: InnerBackend { state } }; let mut guard = backend.backend.state.lock_protocol(); guard.no_last_error()?; let mut dispatched = 0; loop { // Attempt to read a message let ProtocolState { ref mut socket, ref map, .. } = *guard; let message = match socket.read_one_message(|id, opcode| { map.find(id) .and_then(|o| o.interface.events.get(opcode as usize)) .map(|desc| desc.signature) }) { Ok(msg) => msg, Err(MessageParseError::MissingData) | Err(MessageParseError::MissingFD) => { // need to read more data if let Err(e) = guard.socket.fill_incoming_buffers() { if e.kind() != std::io::ErrorKind::WouldBlock { return Err(guard.store_and_return_error(e)); } else if dispatched == 0 { return Err(e.into()); } else { break; } } continue; } Err(MessageParseError::Malformed) => { // malformed error, protocol error let err = WaylandError::Protocol(ProtocolError { code: 0, object_id: 0, object_interface: "".into(), message: "Malformed Wayland message.".into(), }); return Err(guard.store_and_return_error(err)); } }; // We got a message, retrieve its associated object & details // These lookups must succeed otherwise we would not have been able to parse this message let receiver = guard.map.find(message.sender_id).unwrap(); let message_desc = receiver.interface.events.get(message.opcode as usize).unwrap(); // Short-circuit display-associated events if message.sender_id == 1 { guard.handle_display_event(message)?; continue; } let mut created_id = None; // Convert the arguments and create the new object if applicable let mut args = SmallVec::with_capacity(message.args.len()); let mut arg_interfaces = message_desc.arg_interfaces.iter(); for arg in message.args.into_iter() { args.push(match arg { Argument::Array(a) => Argument::Array(a), Argument::Int(i) => Argument::Int(i), Argument::Uint(u) => Argument::Uint(u), Argument::Str(s) => Argument::Str(s), Argument::Fixed(f) => Argument::Fixed(f), Argument::Fd(f) => Argument::Fd(f), Argument::Object(o) => { if o != 0 { // Lookup the object to make the appropriate Id let obj = match guard.map.find(o) { Some(o) => o, None => { let err = WaylandError::Protocol(ProtocolError { code: 0, object_id: 0, object_interface: "".into(), message: format!("Unknown object {}.", o), }); return Err(guard.store_and_return_error(err)); } }; if let Some(next_interface) = arg_interfaces.next() { if !same_interface_or_anonymous(next_interface, obj.interface) { let err = WaylandError::Protocol(ProtocolError { code: 0, object_id: 0, object_interface: "".into(), message: format!( "Protocol error: server sent object {} for interface {}, but it has interface {}.", o, next_interface.name, obj.interface.name ), }); return Err(guard.store_and_return_error(err)); } } Argument::Object(ObjectId { id: InnerObjectId { id: o, serial: obj.data.serial, interface: obj.interface }}) } else { Argument::Object(ObjectId { id: InnerObjectId { id: 0, serial: 0, interface: &ANONYMOUS_INTERFACE }}) } } Argument::NewId(new_id) => { // An object should be created let child_interface = match message_desc.child_interface { Some(iface) => iface, None => panic!("Received event {}@{}.{} which creates an object without specifying its interface, this is unsupported.", receiver.interface.name, message.sender_id, message_desc.name), }; let child_udata = Arc::new(UninitObjectData); // if this ID belonged to a now destroyed server object, we can replace it if new_id >= SERVER_ID_LIMIT && guard.map.with(new_id, |obj| obj.data.client_destroyed).unwrap_or(false) { guard.map.remove(new_id); } let child_obj = Object { interface: child_interface, version: receiver.version, data: Data { client_destroyed: receiver.data.client_destroyed, server_destroyed: false, user_data: child_udata, serial: guard.next_serial(), } }; let child_id = InnerObjectId { id: new_id, serial: child_obj.data.serial, interface: child_obj.interface }; created_id = Some(child_id.clone()); if let Err(()) = guard.map.insert_at(new_id, child_obj) { // abort parsing, this is an unrecoverable error let err = WaylandError::Protocol(ProtocolError { code: 0, object_id: 0, object_interface: "".into(), message: format!( "Protocol error: server tried to create \ an object \"{}\" with invalid id {}.", child_interface.name, new_id ), }); return Err(guard.store_and_return_error(err)); } Argument::NewId(ObjectId { id: child_id }) } }); } if guard.debug { debug::print_dispatched_message( receiver.interface.name, message.sender_id, message_desc.name, &args, ); } // If this event is send to an already destroyed object (by the client), swallow it if receiver.data.client_destroyed { continue; } // Invoke the user callback let id = InnerObjectId { id: message.sender_id, serial: receiver.data.serial, interface: receiver.interface, }; // unlock the mutex while we invoke the user callback std::mem::drop(guard); #[cfg(feature = "log")] crate::log_debug!( "Dispatching {}.{} ({})", id, receiver.version, debug::DisplaySlice(&args) ); let ret = receiver .data .user_data .clone() .event(&backend, Message { sender_id: ObjectId { id }, opcode: message.opcode, args }); // lock it again to resume dispatching guard = backend.backend.state.lock_protocol(); // If this event is a destructor, destroy the object if message_desc.is_destructor { guard .map .with(message.sender_id, |obj| { obj.data.server_destroyed = true; obj.data.client_destroyed = true; }) .unwrap(); receiver.data.user_data.destroyed(ObjectId { id: InnerObjectId { id: message.sender_id, serial: receiver.data.serial, interface: receiver.interface, }, }); } match (created_id, ret) { (Some(child_id), Some(child_data)) => { guard.map.with(child_id.id, |obj| obj.data.user_data = child_data).unwrap(); } (None, None) => {} (Some(child_id), None) => { panic!("Callback creating object {} did not provide any object data.", child_id); } (None, Some(_)) => { panic!("An object data was returned from a callback not creating any object"); } } dispatched += 1; } Ok(dispatched) } wayland-backend-0.3.8/src/rs/map.rs000064400000000000000000000116411046102023000152210ustar 00000000000000//! Wayland objects map use crate::protocol::Interface; use std::cmp::Ordering; /// Limit separating server-created from client-created objects IDs in the namespace pub const SERVER_ID_LIMIT: u32 = 0xFF00_0000; /// The representation of a protocol object #[derive(Debug, Clone)] pub struct Object { /// Interface name of this object pub interface: &'static Interface, /// Version of this object pub version: u32, /// ObjectData associated to this object (ex: its event queue client side) pub data: Data, } /// A holder for the object store of a connection /// /// Keeps track of which object id is associated to which /// interface object, and which is currently unused. #[derive(Debug, Default)] pub struct ObjectMap { client_objects: Vec>>, server_objects: Vec>>, } impl ObjectMap { /// Create a new empty object map pub fn new() -> Self { Self { client_objects: Vec::new(), server_objects: Vec::new() } } /// Find an object in the store pub fn find(&self, id: u32) -> Option> { if id == 0 { None } else if id >= SERVER_ID_LIMIT { self.server_objects.get((id - SERVER_ID_LIMIT) as usize).and_then(Clone::clone) } else { self.client_objects.get((id - 1) as usize).and_then(Clone::clone) } } /// Remove an object from the store /// /// Does nothing if the object didn't previously exists pub fn remove(&mut self, id: u32) { if id == 0 { // nothing } else if id >= SERVER_ID_LIMIT { if let Some(place) = self.server_objects.get_mut((id - SERVER_ID_LIMIT) as usize) { *place = None; } } else if let Some(place) = self.client_objects.get_mut((id - 1) as usize) { *place = None; } } /// Insert given object for given id /// /// Can fail if the requested id is not the next free id of this store. /// (In which case this is a protocol error) pub fn insert_at(&mut self, id: u32, object: Object) -> Result<(), ()> { if id == 0 { Err(()) } else if id >= SERVER_ID_LIMIT { insert_in_at(&mut self.server_objects, (id - SERVER_ID_LIMIT) as usize, object) } else { insert_in_at(&mut self.client_objects, (id - 1) as usize, object) } } /// Allocate a new id for an object in the client namespace pub fn client_insert_new(&mut self, object: Object) -> u32 { insert_in(&mut self.client_objects, object) + 1 } /// Allocate a new id for an object in the server namespace pub fn server_insert_new(&mut self, object: Object) -> u32 { insert_in(&mut self.server_objects, object) + SERVER_ID_LIMIT } /// Mutably access an object of the map pub fn with) -> T>(&mut self, id: u32, f: F) -> Result { if id == 0 { Err(()) } else if id >= SERVER_ID_LIMIT { if let Some(&mut Some(ref mut obj)) = self.server_objects.get_mut((id - SERVER_ID_LIMIT) as usize) { Ok(f(obj)) } else { Err(()) } } else if let Some(&mut Some(ref mut obj)) = self.client_objects.get_mut((id - 1) as usize) { Ok(f(obj)) } else { Err(()) } } pub fn all_objects(&self) -> impl Iterator)> { let client_side_iter = self .client_objects .iter() .enumerate() .flat_map(|(idx, obj)| obj.as_ref().map(|obj| (idx as u32 + 1, obj))); let server_side_iter = self .server_objects .iter() .enumerate() .flat_map(|(idx, obj)| obj.as_ref().map(|obj| (idx as u32 + SERVER_ID_LIMIT, obj))); client_side_iter.chain(server_side_iter) } } // insert a new object in a store at the first free place fn insert_in(store: &mut Vec>>, object: Object) -> u32 { match store.iter().position(Option::is_none) { Some(id) => { store[id] = Some(object); id as u32 } None => { store.push(Some(object)); (store.len() - 1) as u32 } } } // insert an object at a given place in a store fn insert_in_at( store: &mut Vec>>, id: usize, object: Object, ) -> Result<(), ()> { match id.cmp(&store.len()) { Ordering::Greater => Err(()), Ordering::Equal => { store.push(Some(object)); Ok(()) } Ordering::Less => { let previous = &mut store[id]; if !previous.is_none() { return Err(()); } *previous = Some(object); Ok(()) } } } wayland-backend-0.3.8/src/rs/mod.rs000064400000000000000000000010231046102023000152140ustar 00000000000000//! Rust implementations of the Wayland backends mod client_impl; mod server_impl; mod map; pub(crate) mod socket; mod wire; /// Client-side rust implementation of a Wayland protocol backend /// /// The main entrypoint is the [`Backend::connect()`][client::Backend::connect()] method. #[path = "../client_api.rs"] pub mod client; /// Server-side rust implementation of a Wayland protocol backend /// /// The main entrypoint is the [`Backend::new()`][server::Backend::new()] method. #[path = "../server_api.rs"] pub mod server; wayland-backend-0.3.8/src/rs/server_impl/client.rs000064400000000000000000000705431046102023000202570ustar 00000000000000use std::{ ffi::CString, os::unix::{ io::{AsFd, BorrowedFd, OwnedFd, RawFd}, net::UnixStream, }, sync::Arc, }; use crate::{ core_interfaces::{WL_CALLBACK_INTERFACE, WL_DISPLAY_INTERFACE, WL_REGISTRY_INTERFACE}, debug, protocol::{ check_for_signature, same_interface, same_interface_or_anonymous, AllowNull, Argument, ArgumentType, Interface, Message, ObjectInfo, ProtocolError, ANONYMOUS_INTERFACE, INLINE_ARGS, }, rs::map::SERVER_ID_LIMIT, types::server::{DisconnectReason, InvalidId}, }; use smallvec::SmallVec; use crate::rs::{ map::{Object, ObjectMap}, socket::{BufferedSocket, Socket}, wire::MessageParseError, }; use super::{ handle::PendingDestructor, registry::Registry, ClientData, ClientId, Credentials, Data, DumbObjectData, GlobalHandler, InnerClientId, InnerGlobalId, InnerObjectId, ObjectData, ObjectId, UninitObjectData, }; type ArgSmallVec = SmallVec<[Argument; INLINE_ARGS]>; #[repr(u32)] #[allow(dead_code)] pub(crate) enum DisplayError { InvalidObject = 0, InvalidMethod = 1, NoMemory = 2, Implementation = 3, } #[derive(Debug)] pub(crate) struct Client { socket: BufferedSocket, pub(crate) map: ObjectMap>, debug: bool, last_serial: u32, pub(crate) id: InnerClientId, pub(crate) killed: bool, pub(crate) data: Arc, } impl Client { fn next_serial(&mut self) -> u32 { self.last_serial = self.last_serial.wrapping_add(1); self.last_serial } } impl Client { pub(crate) fn new( stream: UnixStream, id: InnerClientId, debug: bool, data: Arc, ) -> Self { let socket = BufferedSocket::new(Socket::from(stream)); let mut map = ObjectMap::new(); map.insert_at( 1, Object { interface: &WL_DISPLAY_INTERFACE, version: 1, data: Data { user_data: Arc::new(DumbObjectData), serial: 0 }, }, ) .unwrap(); data.initialized(ClientId { id: id.clone() }); Self { socket, map, debug, id, killed: false, last_serial: 0, data } } pub(crate) fn create_object( &mut self, interface: &'static Interface, version: u32, user_data: Arc>, ) -> InnerObjectId { let serial = self.next_serial(); let id = self.map.server_insert_new(Object { interface, version, data: Data { serial, user_data }, }); InnerObjectId { id, serial, client_id: self.id.clone(), interface } } pub(crate) fn object_info(&self, id: InnerObjectId) -> Result { let object = self.get_object(id.clone())?; Ok(ObjectInfo { id: id.id, interface: object.interface, version: object.version }) } pub(crate) fn send_event( &mut self, Message { sender_id: object_id, opcode, args }: Message, pending_destructors: Option<&mut Vec>>, ) -> Result<(), InvalidId> { if self.killed { return Ok(()); } let object = self.get_object(object_id.id.clone())?; let message_desc = match object.interface.events.get(opcode as usize) { Some(msg) => msg, None => { panic!( "Unknown opcode {} for object {}@{}.", opcode, object.interface.name, object_id.id ); } }; if !check_for_signature(message_desc.signature, &args) { panic!( "Unexpected signature for event {}@{}.{}: expected {:?}, got {:?}.", object.interface.name, object_id.id, message_desc.name, message_desc.signature, args ); } if self.debug { debug::print_send_message( object.interface.name, object_id.id.id, message_desc.name, &args, false, ); } let mut msg_args = SmallVec::with_capacity(args.len()); let mut arg_interfaces = message_desc.arg_interfaces.iter(); for (i, arg) in args.into_iter().enumerate() { msg_args.push(match arg { Argument::Array(a) => Argument::Array(a), Argument::Int(i) => Argument::Int(i), Argument::Uint(u) => Argument::Uint(u), Argument::Str(s) => Argument::Str(s), Argument::Fixed(f) => Argument::Fixed(f), Argument::Fd(f) => Argument::Fd(f), Argument::NewId(o) => { if o.id.id != 0 { if o.id.client_id != self.id { panic!("Attempting to send an event with objects from wrong client.") } let object = self.get_object(o.id.clone())?; let child_interface = match message_desc.child_interface { Some(iface) => iface, None => panic!("Trying to send event {}@{}.{} which creates an object without specifying its interface, this is unsupported.", object_id.id.interface.name, object_id.id, message_desc.name), }; if !same_interface(child_interface, object.interface) { panic!("Event {}@{}.{} expects a newid argument of interface {} but {} was provided instead.", object.interface.name, object_id.id, message_desc.name, child_interface.name, object.interface.name); } } else if !matches!(message_desc.signature[i], ArgumentType::NewId) { panic!("Request {}@{}.{} expects an non-null newid argument.", object.interface.name, object_id.id, message_desc.name); } Argument::Object(o.id.id) }, Argument::Object(o) => { let next_interface = arg_interfaces.next().unwrap(); if o.id.id != 0 { if o.id.client_id != self.id { panic!("Attempting to send an event with objects from wrong client.") } let arg_object = self.get_object(o.id.clone())?; if !same_interface_or_anonymous(next_interface, arg_object.interface) { panic!("Event {}@{}.{} expects an object argument of interface {} but {} was provided instead.", object.interface.name, object_id.id, message_desc.name, next_interface.name, arg_object.interface.name); } } else if !matches!(message_desc.signature[i], ArgumentType::Object(AllowNull::Yes)) { panic!("Request {}@{}.{} expects an non-null object argument.", object.interface.name, object_id.id, message_desc.name); } Argument::Object(o.id.id) } }); } let msg = Message { sender_id: object_id.id.id, opcode, args: msg_args }; if self.socket.write_message(&msg).is_err() { self.kill(DisconnectReason::ConnectionClosed); } // Handle destruction if relevant if message_desc.is_destructor { self.map.remove(object_id.id.id); if let Some(vec) = pending_destructors { vec.push((object.data.user_data.clone(), self.id.clone(), object_id.id.clone())); } self.send_delete_id(object_id.id); } Ok(()) } pub(crate) fn send_delete_id(&mut self, object_id: InnerObjectId) { // We should only send delete_id for objects in the client ID space if object_id.id < SERVER_ID_LIMIT { let msg = message!(1, 1, [Argument::Uint(object_id.id)]); if self.socket.write_message(&msg).is_err() { self.kill(DisconnectReason::ConnectionClosed); } } self.map.remove(object_id.id); } pub(crate) fn get_object_data( &self, id: InnerObjectId, ) -> Result>, InvalidId> { let object = self.get_object(id)?; Ok(object.data.user_data) } pub(crate) fn set_object_data( &mut self, id: InnerObjectId, data: Arc>, ) -> Result<(), InvalidId> { self.map .with(id.id, |objdata| { if objdata.data.serial != id.serial { Err(InvalidId) } else { objdata.data.user_data = data; Ok(()) } }) .unwrap_or(Err(InvalidId)) } pub(crate) fn post_display_error(&mut self, code: DisplayError, message: CString) { self.post_error( InnerObjectId { id: 1, interface: &WL_DISPLAY_INTERFACE, client_id: self.id.clone(), serial: 0, }, code as u32, message, ) } pub(crate) fn post_error( &mut self, object_id: InnerObjectId, error_code: u32, message: CString, ) { let converted_message = message.to_string_lossy().into(); // errors are ignored, as the client will be killed anyway let _ = self.send_event( message!( ObjectId { id: InnerObjectId { id: 1, interface: &WL_DISPLAY_INTERFACE, client_id: self.id.clone(), serial: 0 } }, 0, // wl_display.error [ Argument::Object(ObjectId { id: object_id.clone() }), Argument::Uint(error_code), Argument::Str(Some(Box::new(message))), ], ), // wl_display.error is not a destructor, this argument will not be used None, ); let _ = self.flush(); self.kill(DisconnectReason::ProtocolError(ProtocolError { code: error_code, object_id: object_id.id, object_interface: object_id.interface.name.into(), message: converted_message, })); } #[cfg(any(target_os = "linux", target_os = "android"))] pub(crate) fn get_credentials(&self) -> Credentials { let creds = rustix::net::sockopt::get_socket_peercred(&self.socket).expect("getsockopt failed!?"); let pid = rustix::process::Pid::as_raw(Some(creds.pid)); Credentials { pid, uid: creds.uid.as_raw(), gid: creds.gid.as_raw() } } #[cfg(not(any(target_os = "linux", target_os = "android")))] // for now this only works on linux pub(crate) fn get_credentials(&self) -> Credentials { Credentials { pid: 0, uid: 0, gid: 0 } } pub(crate) fn kill(&mut self, reason: DisconnectReason) { self.killed = true; self.data.disconnected(ClientId { id: self.id.clone() }, reason); } pub(crate) fn flush(&mut self) -> std::io::Result<()> { self.socket.flush() } pub(crate) fn all_objects(&self) -> impl Iterator + '_ { let client_id = self.id.clone(); self.map.all_objects().map(move |(id, obj)| ObjectId { id: InnerObjectId { id, client_id: client_id.clone(), interface: obj.interface, serial: obj.data.serial, }, }) } #[allow(clippy::type_complexity)] pub(crate) fn next_request( &mut self, ) -> std::io::Result<(Message, Object>)> { if self.killed { return Err(rustix::io::Errno::PIPE.into()); } loop { let map = &self.map; let msg = match self.socket.read_one_message(|id, opcode| { map.find(id) .and_then(|o| o.interface.requests.get(opcode as usize)) .map(|desc| desc.signature) }) { Ok(msg) => msg, Err(MessageParseError::MissingData) | Err(MessageParseError::MissingFD) => { // need to read more data if let Err(e) = self.socket.fill_incoming_buffers() { if e.kind() != std::io::ErrorKind::WouldBlock { self.kill(DisconnectReason::ConnectionClosed); } return Err(e); } continue; } Err(MessageParseError::Malformed) => { self.kill(DisconnectReason::ConnectionClosed); return Err(rustix::io::Errno::PROTO.into()); } }; let obj = self.map.find(msg.sender_id).unwrap(); if self.debug { debug::print_dispatched_message( obj.interface.name, msg.sender_id, obj.interface.requests.get(msg.opcode as usize).unwrap().name, &msg.args, ); } return Ok((msg, obj)); } } fn get_object(&self, id: InnerObjectId) -> Result>, InvalidId> { let object = self.map.find(id.id).ok_or(InvalidId)?; if object.data.serial != id.serial { return Err(InvalidId); } Ok(object) } pub(crate) fn object_for_protocol_id(&self, pid: u32) -> Result { let object = self.map.find(pid).ok_or(InvalidId)?; Ok(InnerObjectId { id: pid, client_id: self.id.clone(), serial: object.data.serial, interface: object.interface, }) } fn queue_all_destructors(&mut self, pending_destructors: &mut Vec>) { pending_destructors.extend(self.map.all_objects().map(|(id, obj)| { ( obj.data.user_data.clone(), self.id.clone(), InnerObjectId { id, serial: obj.data.serial, client_id: self.id.clone(), interface: obj.interface, }, ) })); } pub(crate) fn handle_display_request( &mut self, message: Message, registry: &mut Registry, ) { match message.opcode { // wl_display.sync(new id wl_callback) 0 => { if let [Argument::NewId(new_id)] = message.args[..] { let serial = self.next_serial(); let callback_obj = Object { interface: &WL_CALLBACK_INTERFACE, version: 1, data: Data { user_data: Arc::new(DumbObjectData), serial }, }; if let Err(()) = self.map.insert_at(new_id, callback_obj) { self.post_display_error( DisplayError::InvalidObject, CString::new(format!("Invalid new_id: {}.", new_id)).unwrap(), ); return; } let cb_id = ObjectId { id: InnerObjectId { id: new_id, client_id: self.id.clone(), serial, interface: &WL_CALLBACK_INTERFACE, }, }; // send wl_callback.done(0) this callback does not have any meaningful destructor to run, we can ignore it self.send_event(message!(cb_id, 0, [Argument::Uint(0)]), None).unwrap(); } else { unreachable!() } } // wl_display.get_registry(new id wl_registry) 1 => { if let [Argument::NewId(new_id)] = message.args[..] { let serial = self.next_serial(); let registry_obj = Object { interface: &WL_REGISTRY_INTERFACE, version: 1, data: Data { user_data: Arc::new(DumbObjectData), serial }, }; let registry_id = InnerObjectId { id: new_id, serial, client_id: self.id.clone(), interface: &WL_REGISTRY_INTERFACE, }; if let Err(()) = self.map.insert_at(new_id, registry_obj) { self.post_display_error( DisplayError::InvalidObject, CString::new(format!("Invalid new_id: {}.", new_id)).unwrap(), ); return; } let _ = registry.new_registry(registry_id, self); } else { unreachable!() } } _ => { // unkown opcode, kill the client self.post_display_error( DisplayError::InvalidMethod, CString::new(format!( "Unknown opcode {} for interface wl_display.", message.opcode )) .unwrap(), ); } } } #[allow(clippy::type_complexity)] pub(crate) fn handle_registry_request( &mut self, message: Message, registry: &mut Registry, ) -> Option<(InnerClientId, InnerGlobalId, InnerObjectId, Arc>)> { match message.opcode { // wl_registry.bind(uint name, str interface, uint version, new id) 0 => { if let [Argument::Uint(name), Argument::Str(Some(ref interface_name)), Argument::Uint(version), Argument::NewId(new_id)] = message.args[..] { if let Some((interface, global_id, handler)) = registry.check_bind(self, name, interface_name, version) { let serial = self.next_serial(); let object = Object { interface, version, data: Data { serial, user_data: Arc::new(UninitObjectData) }, }; if let Err(()) = self.map.insert_at(new_id, object) { self.post_display_error( DisplayError::InvalidObject, CString::new(format!("Invalid new_id: {}.", new_id)).unwrap(), ); return None; } Some(( self.id.clone(), global_id, InnerObjectId { id: new_id, client_id: self.id.clone(), interface, serial, }, handler.clone(), )) } else { self.post_display_error( DisplayError::InvalidObject, CString::new(format!( "Invalid binding of {} version {} for global {}.", interface_name.to_string_lossy(), version, name )) .unwrap(), ); None } } else { unreachable!() } } _ => { // unkown opcode, kill the client self.post_display_error( DisplayError::InvalidMethod, CString::new(format!( "Unknown opcode {} for interface wl_registry.", message.opcode )) .unwrap(), ); None } } } pub(crate) fn process_request( &mut self, object: &Object>, message: Message, ) -> Option<(ArgSmallVec, bool, Option)> { let message_desc = object.interface.requests.get(message.opcode as usize).unwrap(); // Convert the arguments and create the new object if applicable let mut new_args = SmallVec::with_capacity(message.args.len()); let mut arg_interfaces = message_desc.arg_interfaces.iter(); let mut created_id = None; for (i, arg) in message.args.into_iter().enumerate() { new_args.push(match arg { Argument::Array(a) => Argument::Array(a), Argument::Int(i) => Argument::Int(i), Argument::Uint(u) => Argument::Uint(u), Argument::Str(s) => Argument::Str(s), Argument::Fixed(f) => Argument::Fixed(f), Argument::Fd(f) => Argument::Fd(f), Argument::Object(o) => { let next_interface = arg_interfaces.next(); if o != 0 { // Lookup the object to make the appropriate Id let obj = match self.map.find(o) { Some(o) => o, None => { self.post_display_error( DisplayError::InvalidObject, CString::new(format!("Unknown id: {}.", o)).unwrap() ); return None; } }; if let Some(next_interface) = next_interface { if !same_interface_or_anonymous(next_interface, obj.interface) { self.post_display_error( DisplayError::InvalidObject, CString::new(format!( "Invalid object {} in request {}.{}: expected {} but got {}.", o, object.interface.name, message_desc.name, next_interface.name, obj.interface.name, )).unwrap() ); return None; } } Argument::Object(ObjectId { id: InnerObjectId { id: o, client_id: self.id.clone(), serial: obj.data.serial, interface: obj.interface }}) } else if matches!(message_desc.signature[i], ArgumentType::Object(AllowNull::Yes)) { Argument::Object(ObjectId { id: InnerObjectId { id: 0, client_id: self.id.clone(), serial: 0, interface: &ANONYMOUS_INTERFACE }}) } else { self.post_display_error( DisplayError::InvalidObject, CString::new(format!( "Invalid null object in request {}.{}.", object.interface.name, message_desc.name, )).unwrap() ); return None; } } Argument::NewId(new_id) => { // An object should be created let child_interface = match message_desc.child_interface { Some(iface) => iface, None => panic!("Received request {}@{}.{} which creates an object without specifying its interface, this is unsupported.", object.interface.name, message.sender_id, message_desc.name), }; let child_udata = Arc::new(UninitObjectData); let child_obj = Object { interface: child_interface, version: object.version, data: Data { user_data: child_udata, serial: self.next_serial(), } }; let child_id = InnerObjectId { id: new_id, client_id: self.id.clone(), serial: child_obj.data.serial, interface: child_obj.interface }; created_id = Some(child_id.clone()); if let Err(()) = self.map.insert_at(new_id, child_obj) { // abort parsing, this is an unrecoverable error self.post_display_error( DisplayError::InvalidObject, CString::new(format!("Invalid new_id: {}.", new_id)).unwrap() ); return None; } Argument::NewId(ObjectId { id: child_id }) } }); } Some((new_args, message_desc.is_destructor, created_id)) } } impl AsFd for Client { fn as_fd(&self) -> BorrowedFd<'_> { self.socket.as_fd() } } #[derive(Debug)] pub(crate) struct ClientStore { clients: Vec>>, last_serial: u32, debug: bool, } impl ClientStore { pub(crate) fn new(debug: bool) -> Self { Self { clients: Vec::new(), last_serial: 0, debug } } pub(crate) fn create_client( &mut self, stream: UnixStream, data: Arc, ) -> InnerClientId { let serial = self.next_serial(); // Find the next free place let (id, place) = match self.clients.iter_mut().enumerate().find(|(_, c)| c.is_none()) { Some((id, place)) => (id, place), None => { self.clients.push(None); (self.clients.len() - 1, self.clients.last_mut().unwrap()) } }; let id = InnerClientId { id: id as u32, serial }; *place = Some(Client::new(stream, id.clone(), self.debug, data)); id } pub(crate) fn get_client(&self, id: InnerClientId) -> Result<&Client, InvalidId> { match self.clients.get(id.id as usize) { Some(Some(client)) if client.id == id => Ok(client), _ => Err(InvalidId), } } pub(crate) fn get_client_mut( &mut self, id: InnerClientId, ) -> Result<&mut Client, InvalidId> { match self.clients.get_mut(id.id as usize) { Some(&mut Some(ref mut client)) if client.id == id => Ok(client), _ => Err(InvalidId), } } pub(crate) fn cleanup( &mut self, pending_destructors: &mut Vec>, ) -> SmallVec<[Client; 1]> { let mut cleaned = SmallVec::new(); for place in &mut self.clients { if place.as_ref().map(|client| client.killed).unwrap_or(false) { // Remove the client from the store and flush it one last time before dropping it let mut client = place.take().unwrap(); client.queue_all_destructors(pending_destructors); let _ = client.flush(); cleaned.push(client); } } cleaned } fn next_serial(&mut self) -> u32 { self.last_serial = self.last_serial.wrapping_add(1); self.last_serial } pub(crate) fn clients_mut(&mut self) -> impl Iterator> { self.clients.iter_mut().flat_map(|o| o.as_mut()).filter(|c| !c.killed) } pub(crate) fn all_clients_id(&self) -> impl Iterator + '_ { self.clients.iter().flat_map(|opt| { opt.as_ref().filter(|c| !c.killed).map(|client| ClientId { id: client.id.clone() }) }) } } wayland-backend-0.3.8/src/rs/server_impl/common_poll.rs000064400000000000000000000321771046102023000213200ustar 00000000000000use std::{ os::unix::io::{AsRawFd, BorrowedFd, OwnedFd}, sync::{Arc, Mutex}, }; use super::{ handle::State, ClientId, Data, GlobalHandler, GlobalId, Handle, InnerClientId, InnerGlobalId, InnerHandle, InnerObjectId, ObjectId, }; use crate::{ core_interfaces::{WL_DISPLAY_INTERFACE, WL_REGISTRY_INTERFACE}, protocol::{same_interface, Argument, Message}, rs::map::Object, types::server::InitError, }; #[cfg(any(target_os = "linux", target_os = "android"))] use rustix::event::epoll; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "macos" ))] use rustix::event::kqueue::*; use smallvec::SmallVec; #[derive(Debug)] pub struct InnerBackend { state: Arc>>, } impl InnerBackend { pub fn new() -> Result { #[cfg(any(target_os = "linux", target_os = "android"))] let poll_fd = epoll::create(epoll::CreateFlags::CLOEXEC) .map_err(Into::into) .map_err(InitError::Io)?; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "macos" ))] let poll_fd = kqueue().map_err(Into::into).map_err(InitError::Io)?; Ok(Self { state: Arc::new(Mutex::new(State::new(poll_fd))) }) } pub fn flush(&self, client: Option) -> std::io::Result<()> { self.state.lock().unwrap().flush(client) } pub fn handle(&self) -> Handle { Handle { handle: InnerHandle { state: self.state.clone() as Arc<_> } } } pub fn poll_fd(&self) -> BorrowedFd { let raw_fd = self.state.lock().unwrap().poll_fd.as_raw_fd(); // This allows the lifetime of the BorrowedFd to be tied to &self rather than the lock guard, // which is the real safety concern unsafe { BorrowedFd::borrow_raw(raw_fd) } } pub fn dispatch_client( &self, data: &mut D, client_id: InnerClientId, ) -> std::io::Result { let ret = self.dispatch_events_for(data, client_id); let cleanup = self.state.lock().unwrap().cleanup(); cleanup(&self.handle(), data); ret } #[cfg(any(target_os = "linux", target_os = "android"))] pub fn dispatch_all_clients(&self, data: &mut D) -> std::io::Result { use std::os::unix::io::AsFd; let poll_fd = self.poll_fd(); let mut dispatched = 0; loop { let mut events = epoll::EventVec::with_capacity(32); epoll::wait(poll_fd.as_fd(), &mut events, 0)?; if events.is_empty() { break; } for event in events.iter() { let id = InnerClientId::from_u64(event.data.u64()); // remove the cb while we call it, to gracefully handle reentrancy if let Ok(count) = self.dispatch_events_for(data, id) { dispatched += count; } } let cleanup = self.state.lock().unwrap().cleanup(); cleanup(&self.handle(), data); } Ok(dispatched) } #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "macos" ))] pub fn dispatch_all_clients(&self, data: &mut D) -> std::io::Result { use std::time::Duration; let poll_fd = self.poll_fd(); let mut dispatched = 0; loop { let mut events = Vec::with_capacity(32); let nevents = unsafe { kevent(&poll_fd, &[], &mut events, Some(Duration::ZERO))? }; if nevents == 0 { break; } for event in events.iter().take(nevents) { let id = InnerClientId::from_u64(event.udata() as u64); // remove the cb while we call it, to gracefully handle reentrancy if let Ok(count) = self.dispatch_events_for(data, id) { dispatched += count; } } let cleanup = self.state.lock().unwrap().cleanup(); cleanup(&self.handle(), data); } Ok(dispatched) } pub(crate) fn dispatch_events_for( &self, data: &mut D, client_id: InnerClientId, ) -> std::io::Result { let mut dispatched = 0; let handle = self.handle(); let mut state = self.state.lock().unwrap(); loop { let action = { let state = &mut *state; if let Ok(client) = state.clients.get_client_mut(client_id.clone()) { let (message, object) = match client.next_request() { Ok(v) => v, Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { if dispatched > 0 { break; } else { return Err(e); } } Err(e) => { #[cfg(any(target_os = "linux", target_os = "android"))] { epoll::delete(&state.poll_fd, client)?; } #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "macos" ))] { use rustix::event::kqueue::*; use std::os::unix::io::{AsFd, AsRawFd}; let evt = Event::new( EventFilter::Read(client.as_fd().as_raw_fd()), EventFlags::DELETE, client_id.as_u64() as isize, ); let mut events = Vec::new(); unsafe { kevent(&state.poll_fd, &[evt], &mut events, None) .map(|_| ())?; } } return Err(e); } }; dispatched += 1; if same_interface(object.interface, &WL_DISPLAY_INTERFACE) { client.handle_display_request(message, &mut state.registry); continue; } else if same_interface(object.interface, &WL_REGISTRY_INTERFACE) { if let Some((client, global, object, handler)) = client.handle_registry_request(message, &mut state.registry) { DispatchAction::Bind { client, global, object, handler } } else { continue; } } else { let object_id = InnerObjectId { id: message.sender_id, serial: object.data.serial, interface: object.interface, client_id: client.id.clone(), }; let opcode = message.opcode; let (arguments, is_destructor, created_id) = match client.process_request(&object, message) { Some(args) => args, None => continue, }; // Return the whole set to invoke the callback while handle is not borrower via client DispatchAction::Request { object, object_id, opcode, arguments, is_destructor, created_id, } } } else { return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, "Invalid client ID", )); } }; match action { DispatchAction::Request { object, object_id, opcode, arguments, is_destructor, created_id, } => { // temporarily unlock the state Mutex while this request is dispatched std::mem::drop(state); let ret = object.data.user_data.clone().request( &handle.clone(), data, ClientId { id: client_id.clone() }, Message { sender_id: ObjectId { id: object_id.clone() }, opcode, args: arguments, }, ); if is_destructor { object.data.user_data.clone().destroyed( &handle.clone(), data, ClientId { id: client_id.clone() }, ObjectId { id: object_id.clone() }, ); } // acquire the lock again and continue state = self.state.lock().unwrap(); if is_destructor { if let Ok(client) = state.clients.get_client_mut(client_id.clone()) { client.send_delete_id(object_id); } } match (created_id, ret) { (Some(child_id), Some(child_data)) => { if let Ok(client) = state.clients.get_client_mut(client_id.clone()) { client .map .with(child_id.id, |obj| obj.data.user_data = child_data) .unwrap(); } } (None, None) => {} (Some(child_id), None) => { // Allow the callback to not return any data if the client is already dead (typically // if the callback provoked a protocol error) if let Ok(client) = state.clients.get_client(client_id.clone()) { if !client.killed { panic!( "Callback creating object {} did not provide any object data.", child_id ); } } } (None, Some(_)) => { panic!("An object data was returned from a callback not creating any object"); } } // dropping the object calls destructors from which users could call into wayland-backend again. // so lets release and relock the state again, to avoid a deadlock std::mem::drop(state); std::mem::drop(object); state = self.state.lock().unwrap(); } DispatchAction::Bind { object, client, global, handler } => { // temporarily unlock the state Mutex while this request is dispatched std::mem::drop(state); let child_data = handler.bind( &handle.clone(), data, ClientId { id: client.clone() }, GlobalId { id: global }, ObjectId { id: object.clone() }, ); // acquire the lock again and continue state = self.state.lock().unwrap(); if let Ok(client) = state.clients.get_client_mut(client.clone()) { client.map.with(object.id, |obj| obj.data.user_data = child_data).unwrap(); } } } } Ok(dispatched) } } enum DispatchAction { Request { object: Object>, object_id: InnerObjectId, opcode: u16, arguments: SmallVec<[Argument; 4]>, is_destructor: bool, created_id: Option, }, Bind { object: InnerObjectId, client: InnerClientId, global: InnerGlobalId, handler: Arc>, }, } wayland-backend-0.3.8/src/rs/server_impl/handle.rs000064400000000000000000000355421046102023000202340ustar 00000000000000use std::{ ffi::CString, os::unix::{ io::{OwnedFd, RawFd}, net::UnixStream, }, sync::{Arc, Mutex, Weak}, }; use crate::{ protocol::{same_interface, Interface, Message, ObjectInfo, ANONYMOUS_INTERFACE}, types::server::{DisconnectReason, GlobalInfo, InvalidId}, }; use super::{ client::ClientStore, registry::Registry, ClientData, ClientId, Credentials, GlobalHandler, InnerClientId, InnerGlobalId, InnerObjectId, ObjectData, ObjectId, }; pub(crate) type PendingDestructor = (Arc>, InnerClientId, InnerObjectId); #[derive(Debug)] pub struct State { pub(crate) clients: ClientStore, pub(crate) registry: Registry, pub(crate) pending_destructors: Vec>, pub(crate) poll_fd: OwnedFd, } impl State { pub(crate) fn new(poll_fd: OwnedFd) -> Self { let debug = matches!(std::env::var_os("WAYLAND_DEBUG"), Some(str) if str == "1" || str == "server"); Self { clients: ClientStore::new(debug), registry: Registry::new(), pending_destructors: Vec::new(), poll_fd, } } pub(crate) fn cleanup<'a>(&mut self) -> impl FnOnce(&super::Handle, &mut D) + 'a { let dead_clients = self.clients.cleanup(&mut self.pending_destructors); self.registry.cleanup(&dead_clients); // return a closure that will do the cleanup once invoked let pending_destructors = std::mem::take(&mut self.pending_destructors); move |handle, data| { for (object_data, client_id, object_id) in pending_destructors { object_data.clone().destroyed( handle, data, ClientId { id: client_id }, ObjectId { id: object_id }, ); } std::mem::drop(dead_clients); } } pub(crate) fn flush(&mut self, client: Option) -> std::io::Result<()> { if let Some(ClientId { id: client }) = client { match self.clients.get_client_mut(client) { Ok(client) => client.flush(), Err(InvalidId) => Ok(()), } } else { for client in self.clients.clients_mut() { let _ = client.flush(); } Ok(()) } } } #[derive(Clone)] pub struct InnerHandle { pub(crate) state: Arc>, } impl std::fmt::Debug for InnerHandle { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fmt.debug_struct("InnerHandle[rs]").finish_non_exhaustive() } } #[derive(Clone)] pub struct WeakInnerHandle { pub(crate) state: Weak>, } impl std::fmt::Debug for WeakInnerHandle { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fmt.debug_struct("WeakInnerHandle[rs]").finish_non_exhaustive() } } impl WeakInnerHandle { pub fn upgrade(&self) -> Option { self.state.upgrade().map(|state| InnerHandle { state }) } } impl InnerHandle { pub fn downgrade(&self) -> WeakInnerHandle { WeakInnerHandle { state: Arc::downgrade(&self.state) } } pub fn object_info(&self, id: InnerObjectId) -> Result { self.state.lock().unwrap().object_info(id) } pub fn insert_client( &self, stream: UnixStream, data: Arc, ) -> std::io::Result { self.state.lock().unwrap().insert_client(stream, data) } pub fn get_client(&self, id: InnerObjectId) -> Result { self.state.lock().unwrap().get_client(id) } pub fn get_client_data(&self, id: InnerClientId) -> Result, InvalidId> { self.state.lock().unwrap().get_client_data(id) } pub fn get_client_credentials(&self, id: InnerClientId) -> Result { self.state.lock().unwrap().get_client_credentials(id) } pub fn with_all_clients(&self, mut f: impl FnMut(ClientId)) { self.state.lock().unwrap().with_all_clients(&mut f) } pub fn with_all_objects_for( &self, client_id: InnerClientId, mut f: impl FnMut(ObjectId), ) -> Result<(), InvalidId> { self.state.lock().unwrap().with_all_objects_for(client_id, &mut f) } pub fn object_for_protocol_id( &self, client_id: InnerClientId, interface: &'static Interface, protocol_id: u32, ) -> Result { self.state.lock().unwrap().object_for_protocol_id(client_id, interface, protocol_id) } pub fn create_object( &self, client_id: InnerClientId, interface: &'static Interface, version: u32, data: Arc>, ) -> Result { let mut state = self.state.lock().unwrap(); let state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::create_object()."); let client = state.clients.get_client_mut(client_id)?; Ok(ObjectId { id: client.create_object(interface, version, data) }) } pub fn null_id() -> ObjectId { ObjectId { id: InnerObjectId { id: 0, serial: 0, client_id: InnerClientId { id: 0, serial: 0 }, interface: &ANONYMOUS_INTERFACE, }, } } pub fn send_event(&self, msg: Message) -> Result<(), InvalidId> { self.state.lock().unwrap().send_event(msg) } pub fn get_object_data( &self, id: InnerObjectId, ) -> Result>, InvalidId> { let mut state = self.state.lock().unwrap(); let state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::get_object_data()."); state.clients.get_client(id.client_id.clone())?.get_object_data(id) } pub fn get_object_data_any( &self, id: InnerObjectId, ) -> Result, InvalidId> { self.state.lock().unwrap().get_object_data_any(id) } pub fn set_object_data( &self, id: InnerObjectId, data: Arc>, ) -> Result<(), InvalidId> { let mut state = self.state.lock().unwrap(); let state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::set_object_data()."); state.clients.get_client_mut(id.client_id.clone())?.set_object_data(id, data) } pub fn post_error(&self, object_id: InnerObjectId, error_code: u32, message: CString) { self.state.lock().unwrap().post_error(object_id, error_code, message) } pub fn kill_client(&self, client_id: InnerClientId, reason: DisconnectReason) { self.state.lock().unwrap().kill_client(client_id, reason) } pub fn create_global( &self, interface: &'static Interface, version: u32, handler: Arc>, ) -> InnerGlobalId { let mut state = self.state.lock().unwrap(); let state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::create_global()."); state.registry.create_global(interface, version, handler, &mut state.clients) } pub fn disable_global(&self, id: InnerGlobalId) { let mut state = self.state.lock().unwrap(); let state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::create_global()."); state.registry.disable_global(id, &mut state.clients) } pub fn remove_global(&self, id: InnerGlobalId) { let mut state = self.state.lock().unwrap(); let state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::create_global()."); state.registry.remove_global(id, &mut state.clients) } pub fn global_info(&self, id: InnerGlobalId) -> Result { self.state.lock().unwrap().global_info(id) } pub fn get_global_handler( &self, id: InnerGlobalId, ) -> Result>, InvalidId> { let mut state = self.state.lock().unwrap(); let state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::get_global_handler()."); state.registry.get_handler(id) } pub fn flush(&mut self, client: Option) -> std::io::Result<()> { self.state.lock().unwrap().flush(client) } } pub(crate) trait ErasedState: downcast_rs::Downcast { fn object_info(&self, id: InnerObjectId) -> Result; fn insert_client( &mut self, stream: UnixStream, data: Arc, ) -> std::io::Result; fn get_client(&self, id: InnerObjectId) -> Result; fn get_client_data(&self, id: InnerClientId) -> Result, InvalidId>; fn get_client_credentials(&self, id: InnerClientId) -> Result; fn with_all_clients(&self, f: &mut dyn FnMut(ClientId)); fn with_all_objects_for( &self, client_id: InnerClientId, f: &mut dyn FnMut(ObjectId), ) -> Result<(), InvalidId>; fn object_for_protocol_id( &self, client_id: InnerClientId, interface: &'static Interface, protocol_id: u32, ) -> Result; fn get_object_data_any( &self, id: InnerObjectId, ) -> Result, InvalidId>; fn send_event(&mut self, msg: Message) -> Result<(), InvalidId>; fn post_error(&mut self, object_id: InnerObjectId, error_code: u32, message: CString); fn kill_client(&mut self, client_id: InnerClientId, reason: DisconnectReason); fn global_info(&self, id: InnerGlobalId) -> Result; fn flush(&mut self, client: Option) -> std::io::Result<()>; } downcast_rs::impl_downcast!(ErasedState); impl ErasedState for State { fn object_info(&self, id: InnerObjectId) -> Result { self.clients.get_client(id.client_id.clone())?.object_info(id) } fn insert_client( &mut self, stream: UnixStream, data: Arc, ) -> std::io::Result { let id = self.clients.create_client(stream, data); let client = self.clients.get_client(id.clone()).unwrap(); // register the client to the internal epoll #[cfg(any(target_os = "linux", target_os = "android"))] let ret = { use rustix::event::epoll; epoll::add( &self.poll_fd, client, epoll::EventData::new_u64(id.as_u64()), epoll::EventFlags::IN, ) }; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "macos" ))] let ret = { use rustix::event::kqueue::*; use std::os::unix::io::{AsFd, AsRawFd}; let evt = Event::new( EventFilter::Read(client.as_fd().as_raw_fd()), EventFlags::ADD | EventFlags::RECEIPT, id.as_u64() as isize, ); let mut events = Vec::new(); unsafe { kevent(&self.poll_fd, &[evt], &mut events, None).map(|_| ()) } }; match ret { Ok(()) => Ok(id), Err(e) => { self.kill_client(id, DisconnectReason::ConnectionClosed); Err(e.into()) } } } fn get_client(&self, id: InnerObjectId) -> Result { if self.clients.get_client(id.client_id.clone()).is_ok() { Ok(ClientId { id: id.client_id }) } else { Err(InvalidId) } } fn get_client_data(&self, id: InnerClientId) -> Result, InvalidId> { let client = self.clients.get_client(id)?; Ok(client.data.clone()) } fn get_client_credentials(&self, id: InnerClientId) -> Result { let client = self.clients.get_client(id)?; Ok(client.get_credentials()) } fn with_all_clients(&self, f: &mut dyn FnMut(ClientId)) { for client in self.clients.all_clients_id() { f(client) } } fn with_all_objects_for( &self, client_id: InnerClientId, f: &mut dyn FnMut(ObjectId), ) -> Result<(), InvalidId> { let client = self.clients.get_client(client_id)?; for object in client.all_objects() { f(object) } Ok(()) } fn object_for_protocol_id( &self, client_id: InnerClientId, interface: &'static Interface, protocol_id: u32, ) -> Result { let client = self.clients.get_client(client_id)?; let object = client.object_for_protocol_id(protocol_id)?; if same_interface(interface, object.interface) { Ok(ObjectId { id: object }) } else { Err(InvalidId) } } fn get_object_data_any( &self, id: InnerObjectId, ) -> Result, InvalidId> { self.clients .get_client(id.client_id.clone())? .get_object_data(id) .map(|arc| arc.into_any_arc()) } fn send_event(&mut self, msg: Message) -> Result<(), InvalidId> { self.clients .get_client_mut(msg.sender_id.id.client_id.clone())? .send_event(msg, Some(&mut self.pending_destructors)) } fn post_error(&mut self, object_id: InnerObjectId, error_code: u32, message: CString) { if let Ok(client) = self.clients.get_client_mut(object_id.client_id.clone()) { client.post_error(object_id, error_code, message) } } fn kill_client(&mut self, client_id: InnerClientId, reason: DisconnectReason) { if let Ok(client) = self.clients.get_client_mut(client_id) { client.kill(reason) } } fn global_info(&self, id: InnerGlobalId) -> Result { self.registry.get_info(id) } fn flush(&mut self, client: Option) -> std::io::Result<()> { self.flush(client) } } wayland-backend-0.3.8/src/rs/server_impl/mod.rs000064400000000000000000000063321046102023000175530ustar 00000000000000//! Server-side rust implementation of a Wayland protocol backend use std::os::unix::io::OwnedFd; use std::{fmt, sync::Arc}; use crate::protocol::{same_interface, Interface, Message}; mod client; mod common_poll; mod handle; mod registry; pub use crate::types::server::Credentials; pub use common_poll::InnerBackend; pub use handle::{InnerHandle, WeakInnerHandle}; use super::server::*; #[derive(Clone)] pub struct InnerObjectId { id: u32, serial: u32, client_id: InnerClientId, interface: &'static Interface, } impl InnerObjectId { pub fn is_null(&self) -> bool { self.id == 0 } pub fn interface(&self) -> &'static Interface { self.interface } pub fn same_client_as(&self, other: &Self) -> bool { self.client_id == other.client_id } pub fn protocol_id(&self) -> u32 { self.id } } impl fmt::Display for InnerObjectId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}@{}[{}]", self.interface.name, self.id, self.client_id.id) } } impl fmt::Debug for InnerObjectId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ObjectId({}, {})", self, self.serial) } } impl PartialEq for InnerObjectId { fn eq(&self, other: &Self) -> bool { self.id == other.id && self.serial == other.serial && self.client_id == other.client_id && same_interface(self.interface, other.interface) } } impl std::cmp::Eq for InnerObjectId {} impl std::hash::Hash for InnerObjectId { fn hash(&self, state: &mut H) { self.id.hash(state); self.serial.hash(state); self.client_id.hash(state); } } /// An id of a client connected to the server. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct InnerClientId { id: u32, serial: u32, } impl InnerClientId { fn as_u64(&self) -> u64 { ((self.id as u64) << 32) + self.serial as u64 } fn from_u64(t: u64) -> Self { Self { id: (t >> 32) as u32, serial: t as u32 } } } /// The ID of a global #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct InnerGlobalId { id: u32, serial: u32, } #[derive(Debug)] pub(crate) struct Data { user_data: Arc>, serial: u32, } impl Clone for Data { #[cfg_attr(coverage, coverage(off))] fn clone(&self) -> Self { Self { user_data: self.user_data.clone(), serial: self.serial } } } struct UninitObjectData; impl ObjectData for UninitObjectData { #[cfg_attr(coverage, coverage(off))] fn request( self: Arc, _: &Handle, _: &mut D, _: ClientId, msg: Message, ) -> Option>> { panic!("Received a message on an uninitialized object: {:?}", msg); } #[cfg_attr(coverage, coverage(off))] fn destroyed(self: Arc, _: &Handle, _: &mut D, _: ClientId, _: ObjectId) {} #[cfg_attr(coverage, coverage(off))] fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("UninitObjectData").finish() } } wayland-backend-0.3.8/src/rs/server_impl/registry.rs000064400000000000000000000174521046102023000206510ustar 00000000000000use std::{ ffi::{CStr, CString}, sync::Arc, }; use crate::protocol::{Argument, Interface}; use crate::types::server::{GlobalInfo, InvalidId}; use super::{ client::{Client, ClientStore}, ClientId, GlobalHandler, GlobalId, InnerGlobalId, InnerObjectId, ObjectId, }; /* GlobalId.id is the global protocol name (starting at 1), hence we must subtract 1 to it before indexing the vec */ #[derive(Debug)] struct Global { id: InnerGlobalId, interface: &'static Interface, version: u32, handler: Arc>, disabled: bool, } #[derive(Debug)] pub struct Registry { globals: Vec>>, known_registries: Vec, last_serial: u32, } impl Registry { pub(crate) fn new() -> Self { Self { globals: Vec::new(), known_registries: Vec::new(), last_serial: 0 } } fn next_serial(&mut self) -> u32 { self.last_serial = self.last_serial.wrapping_add(1); self.last_serial } pub(crate) fn create_global( &mut self, interface: &'static Interface, version: u32, handler: Arc>, clients: &mut ClientStore, ) -> InnerGlobalId { if version > interface.version { panic!( "Cannot create global {} version {}: maximum supported version is {}", interface.name, version, interface.version ); } let serial = self.next_serial(); let (id, place) = match self.globals.iter_mut().enumerate().find(|(_, g)| g.is_none()) { Some((id, place)) => (id, place), None => { self.globals.push(None); (self.globals.len() - 1, self.globals.last_mut().unwrap()) } }; let id = InnerGlobalId { id: id as u32 + 1, serial }; *place = Some(Global { id: id.clone(), interface, version, handler, disabled: false }); self.send_global_to_all(id.clone(), clients).unwrap(); id } fn get_global(&self, id: InnerGlobalId) -> Result<&Global, InvalidId> { self.globals .get(id.id as usize - 1) .and_then(|o| o.as_ref()) .filter(|o| o.id == id) .ok_or(InvalidId) } pub(crate) fn get_info(&self, id: InnerGlobalId) -> Result { let global = self.get_global(id)?; Ok(GlobalInfo { interface: global.interface, version: global.version, disabled: global.disabled, }) } pub(crate) fn get_handler( &self, id: InnerGlobalId, ) -> Result>, InvalidId> { let global = self.get_global(id)?; Ok(global.handler.clone()) } pub(crate) fn check_bind( &self, client: &Client, name: u32, interface_name: &CStr, version: u32, ) -> Option<(&'static Interface, InnerGlobalId, Arc>)> { if name == 0 || version == 0 { return None; } let target_global = self.globals.get((name - 1) as usize).and_then(|o| o.as_ref())?; if target_global.interface.name.as_bytes() != interface_name.to_bytes() { return None; } if target_global.version < version { return None; } if !target_global.handler.can_view( ClientId { id: client.id.clone() }, &client.data, GlobalId { id: target_global.id.clone() }, ) { return None; } Some((target_global.interface, target_global.id.clone(), target_global.handler.clone())) } pub(crate) fn cleanup(&mut self, dead_clients: &[Client]) { self.known_registries .retain(|obj_id| !dead_clients.iter().any(|cid| cid.id == obj_id.client_id)) } pub(crate) fn disable_global(&mut self, id: InnerGlobalId, clients: &mut ClientStore) { let global = match self.globals.get_mut(id.id as usize - 1) { Some(&mut Some(ref mut g)) if g.id == id => g, _ => return, }; // Do nothing if the global is already disabled if !global.disabled { global.disabled = true; // send the global_remove for registry in self.known_registries.iter().cloned() { if let Ok(client) = clients.get_client_mut(registry.client_id.clone()) { let _ = send_global_remove_to(client, global, ObjectId { id: registry.clone() }); } } } } pub(crate) fn remove_global(&mut self, id: InnerGlobalId, clients: &mut ClientStore) { // disable the global if not already disabled self.disable_global(id.clone(), clients); // now remove it if the id is still valid if let Some(place) = self.globals.get_mut(id.id as usize - 1) { if place.as_ref().map(|g| g.id == id).unwrap_or(false) { *place = None; } } } pub(crate) fn new_registry( &mut self, registry: InnerObjectId, client: &mut Client, ) -> Result<(), InvalidId> { self.send_all_globals_to(registry.clone(), client)?; self.known_registries.push(registry); Ok(()) } pub(crate) fn send_all_globals_to( &self, registry: InnerObjectId, client: &mut Client, ) -> Result<(), InvalidId> { for global in self.globals.iter().flat_map(|opt| opt.as_ref()) { if !global.disabled && global.handler.can_view( ClientId { id: client.id.clone() }, &client.data, GlobalId { id: global.id.clone() }, ) { // fail the whole send on error, there is no point in trying further on a failing client send_global_to(client, global, ObjectId { id: registry.clone() })?; } } Ok(()) } pub(crate) fn send_global_to_all( &self, global_id: InnerGlobalId, clients: &mut ClientStore, ) -> Result<(), InvalidId> { let global = self.get_global(global_id)?; if global.disabled { return Err(InvalidId); } for registry in self.known_registries.iter().cloned() { if let Ok(client) = clients.get_client_mut(registry.client_id.clone()) { if !global.disabled && global.handler.can_view( ClientId { id: client.id.clone() }, &client.data, GlobalId { id: global.id.clone() }, ) { // don't fail the whole send for a single erroring client let _ = send_global_to(client, global, ObjectId { id: registry.clone() }); } } } Ok(()) } } #[inline] fn send_global_to( client: &mut Client, global: &Global, registry: ObjectId, ) -> Result<(), InvalidId> { client.send_event( message!( registry, 0, // wl_registry.global [ Argument::Uint(global.id.id), Argument::Str(Some(Box::new(CString::new(global.interface.name).unwrap()))), Argument::Uint(global.version), ], ), // This is not a destructor event None, ) } #[inline] fn send_global_remove_to( client: &mut Client, global: &Global, registry: ObjectId, ) -> Result<(), InvalidId> { client.send_event( message!( registry, 1, // wl_registry.global_remove [Argument::Uint(global.id.id)], ), // This is not a destructor event None, ) } wayland-backend-0.3.8/src/rs/socket.rs000064400000000000000000000433301046102023000157340ustar 00000000000000//! Wayland socket manipulation use std::collections::VecDeque; use std::io::{ErrorKind, IoSlice, IoSliceMut, Result as IoResult}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; use std::os::unix::net::UnixStream; use std::slice; use rustix::io::retry_on_intr; use rustix::net::{ recvmsg, send, sendmsg, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, SendAncillaryBuffer, SendAncillaryMessage, SendFlags, }; use crate::protocol::{ArgumentType, Message}; use super::wire::{parse_message, write_to_buffers, MessageParseError, MessageWriteError}; /// Maximum number of FD that can be sent in a single socket message pub const MAX_FDS_OUT: usize = 28; /// Maximum number of bytes that can be sent in a single socket message pub const MAX_BYTES_OUT: usize = 4096; /* * Socket */ /// A wayland socket #[derive(Debug)] pub struct Socket { stream: UnixStream, } impl Socket { /// Send a single message to the socket /// /// A single socket message can contain several wayland messages /// /// The `fds` slice should not be longer than `MAX_FDS_OUT`, and the `bytes` /// slice should not be longer than `MAX_BYTES_OUT` otherwise the receiving /// end may lose some data. pub fn send_msg(&self, bytes: &[u8], fds: &[OwnedFd]) -> IoResult { #[cfg(not(target_os = "macos"))] let flags = SendFlags::DONTWAIT | SendFlags::NOSIGNAL; #[cfg(target_os = "macos")] let flags = SendFlags::DONTWAIT; if !fds.is_empty() { let iov = [IoSlice::new(bytes)]; let mut cmsg_space = vec![0; rustix::cmsg_space!(ScmRights(fds.len()))]; let mut cmsg_buffer = SendAncillaryBuffer::new(&mut cmsg_space); let fds = unsafe { slice::from_raw_parts(fds.as_ptr() as *const BorrowedFd, fds.len()) }; cmsg_buffer.push(SendAncillaryMessage::ScmRights(fds)); Ok(retry_on_intr(|| sendmsg(self, &iov, &mut cmsg_buffer, flags))?) } else { Ok(retry_on_intr(|| send(self, bytes, flags))?) } } /// Receive a single message from the socket /// /// Return the number of bytes received and the number of Fds received. /// /// Errors with `WouldBlock` is no message is available. /// /// A single socket message can contain several wayland messages. /// /// The `buffer` slice should be at least `MAX_BYTES_OUT` long and the `fds` /// slice `MAX_FDS_OUT` long, otherwise some data of the received message may /// be lost. pub fn rcv_msg(&self, buffer: &mut [u8], fds: &mut VecDeque) -> IoResult { #[cfg(not(target_os = "macos"))] let flags = RecvFlags::DONTWAIT | RecvFlags::CMSG_CLOEXEC; #[cfg(target_os = "macos")] let flags = RecvFlags::DONTWAIT; let mut cmsg_space = [0; rustix::cmsg_space!(ScmRights(MAX_FDS_OUT))]; let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg_space); let mut iov = [IoSliceMut::new(buffer)]; let msg = retry_on_intr(|| recvmsg(&self.stream, &mut iov[..], &mut cmsg_buffer, flags))?; let received_fds = cmsg_buffer .drain() .filter_map(|cmsg| match cmsg { RecvAncillaryMessage::ScmRights(fds) => Some(fds), _ => None, }) .flatten(); fds.extend(received_fds); #[cfg(target_os = "macos")] for fd in fds.iter() { if let Ok(flags) = rustix::io::fcntl_getfd(fd) { let _ = rustix::io::fcntl_setfd(fd, flags | rustix::io::FdFlags::CLOEXEC); } } Ok(msg.bytes) } } impl From for Socket { fn from(stream: UnixStream) -> Self { // macOS doesn't have MSG_NOSIGNAL, but has SO_NOSIGPIPE instead #[cfg(target_os = "macos")] let _ = rustix::net::sockopt::set_socket_nosigpipe(&stream, true); Self { stream } } } impl AsFd for Socket { fn as_fd(&self) -> BorrowedFd<'_> { self.stream.as_fd() } } impl AsRawFd for Socket { fn as_raw_fd(&self) -> RawFd { self.stream.as_raw_fd() } } /* * BufferedSocket */ /// An adapter around a raw Socket that directly handles buffering and /// conversion from/to wayland messages #[derive(Debug)] pub struct BufferedSocket { socket: Socket, in_data: Buffer, in_fds: VecDeque, out_data: Buffer, out_fds: Vec, } impl BufferedSocket { /// Wrap a Socket into a Buffered Socket pub fn new(socket: Socket) -> Self { Self { socket, in_data: Buffer::new(2 * MAX_BYTES_OUT), // Incoming buffers are twice as big in order to be in_fds: VecDeque::new(), // able to store leftover data if needed out_data: Buffer::new(MAX_BYTES_OUT), out_fds: Vec::new(), } } /// Flush the contents of the outgoing buffer into the socket pub fn flush(&mut self) -> IoResult<()> { let written = { let bytes = self.out_data.get_contents(); if bytes.is_empty() { return Ok(()); } self.socket.send_msg(bytes, &self.out_fds)? }; self.out_data.offset(written); self.out_data.move_to_front(); self.out_fds.clear(); Ok(()) } // internal method // // attempts to write a message in the internal out buffers, // returns true if successful // // if false is returned, it means there is not enough space // in the buffer fn attempt_write_message(&mut self, msg: &Message) -> IoResult { match write_to_buffers(msg, self.out_data.get_writable_storage(), &mut self.out_fds) { Ok(bytes_out) => { self.out_data.advance(bytes_out); Ok(true) } Err(MessageWriteError::BufferTooSmall) => Ok(false), Err(MessageWriteError::DupFdFailed(e)) => Err(e), } } /// Write a message to the outgoing buffer /// /// This method may flush the internal buffer if necessary (if it is full). /// /// If the message is too big to fit in the buffer, the error `Error::Sys(E2BIG)` /// will be returned. pub fn write_message(&mut self, msg: &Message) -> IoResult<()> { if !self.attempt_write_message(msg)? { // the attempt failed, there is not enough space in the buffer // we need to flush it if let Err(e) = self.flush() { if e.kind() != ErrorKind::WouldBlock { return Err(e); } } if !self.attempt_write_message(msg)? { // If this fails again, this means the message is too big // to be transmitted at all return Err(rustix::io::Errno::TOOBIG.into()); } } Ok(()) } /// Try to fill the incoming buffers of this socket, to prepare /// a new round of parsing. pub fn fill_incoming_buffers(&mut self) -> IoResult<()> { // reorganize the buffers self.in_data.move_to_front(); // receive a message let in_bytes = { let bytes = self.in_data.get_writable_storage(); self.socket.rcv_msg(bytes, &mut self.in_fds)? }; if in_bytes == 0 { // the other end of the socket was closed return Err(rustix::io::Errno::PIPE.into()); } // advance the storage self.in_data.advance(in_bytes); Ok(()) } /// Read and deserialize a single message from the incoming buffers socket /// /// This method requires one closure that given an object id and an opcode, /// must provide the signature of the associated request/event, in the form of /// a `&'static [ArgumentType]`. pub fn read_one_message( &mut self, mut signature: F, ) -> Result, MessageParseError> where F: FnMut(u32, u16) -> Option<&'static [ArgumentType]>, { let (msg, read_data) = { let data = self.in_data.get_contents(); if data.len() < 2 * 4 { return Err(MessageParseError::MissingData); } let object_id = u32::from_ne_bytes([data[0], data[1], data[2], data[3]]); let word_2 = u32::from_ne_bytes([data[4], data[5], data[6], data[7]]); let opcode = (word_2 & 0x0000_FFFF) as u16; if let Some(sig) = signature(object_id, opcode) { match parse_message(data, sig, &mut self.in_fds) { Ok((msg, rest_data)) => (msg, data.len() - rest_data.len()), Err(e) => return Err(e), } } else { // no signature found ? return Err(MessageParseError::Malformed); } }; self.in_data.offset(read_data); Ok(msg) } } impl AsRawFd for BufferedSocket { fn as_raw_fd(&self) -> RawFd { self.socket.as_raw_fd() } } impl AsFd for BufferedSocket { fn as_fd(&self) -> BorrowedFd<'_> { self.socket.as_fd() } } /* * Buffer */ #[derive(Debug)] struct Buffer { storage: Vec, occupied: usize, offset: usize, } impl Buffer { fn new(size: usize) -> Self { Self { storage: vec![T::default(); size], occupied: 0, offset: 0 } } /// Advance the internal counter of occupied space fn advance(&mut self, bytes: usize) { self.occupied += bytes; } /// Advance the read offset of current occupied space fn offset(&mut self, bytes: usize) { self.offset += bytes; } /// Clears the contents of the buffer /// /// This only sets the counter of occupied space back to zero, /// allowing previous content to be overwritten. #[allow(unused)] fn clear(&mut self) { self.occupied = 0; self.offset = 0; } /// Get the current contents of the occupied space of the buffer fn get_contents(&self) -> &[T] { &self.storage[(self.offset)..(self.occupied)] } /// Get mutable access to the unoccupied space of the buffer fn get_writable_storage(&mut self) -> &mut [T] { &mut self.storage[(self.occupied)..] } /// Move the unread contents of the buffer to the front, to ensure /// maximal write space availability fn move_to_front(&mut self) { if self.occupied > self.offset { self.storage.copy_within((self.offset)..(self.occupied), 0) } self.occupied -= self.offset; self.offset = 0; } } #[cfg(test)] mod tests { use super::*; use crate::protocol::{AllowNull, Argument, ArgumentType, Message}; use std::ffi::CString; use std::os::unix::io::IntoRawFd; use smallvec::smallvec; fn same_file(a: BorrowedFd, b: BorrowedFd) -> bool { let stat1 = rustix::fs::fstat(a).unwrap(); let stat2 = rustix::fs::fstat(b).unwrap(); stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino } // check if two messages are equal // // if arguments contain FDs, check that the fd point to // the same file, rather than are the same number. fn assert_eq_msgs( msg1: &Message, msg2: &Message, ) { assert_eq!(msg1.sender_id, msg2.sender_id); assert_eq!(msg1.opcode, msg2.opcode); assert_eq!(msg1.args.len(), msg2.args.len()); for (arg1, arg2) in msg1.args.iter().zip(msg2.args.iter()) { if let (Argument::Fd(fd1), Argument::Fd(fd2)) = (arg1, arg2) { let fd1 = unsafe { BorrowedFd::borrow_raw(fd1.as_raw_fd()) }; let fd2 = unsafe { BorrowedFd::borrow_raw(fd2.as_raw_fd()) }; assert!(same_file(fd1, fd2)); } else { assert_eq!(arg1, arg2); } } } #[test] fn write_read_cycle() { let msg = Message { sender_id: 42, opcode: 7, args: smallvec![ Argument::Uint(3), Argument::Fixed(-89), Argument::Str(Some(Box::new(CString::new(&b"I like trains!"[..]).unwrap()))), Argument::Array(vec![1, 2, 3, 4, 5, 6, 7, 8, 9].into()), Argument::Object(88), Argument::NewId(56), Argument::Int(-25), ], }; let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap(); let mut client = BufferedSocket::new(Socket::from(client)); let mut server = BufferedSocket::new(Socket::from(server)); client.write_message(&msg).unwrap(); client.flush().unwrap(); static SIGNATURE: &[ArgumentType] = &[ ArgumentType::Uint, ArgumentType::Fixed, ArgumentType::Str(AllowNull::No), ArgumentType::Array, ArgumentType::Object(AllowNull::No), ArgumentType::NewId, ArgumentType::Int, ]; server.fill_incoming_buffers().unwrap(); let ret_msg = server .read_one_message(|sender_id, opcode| { if sender_id == 42 && opcode == 7 { Some(SIGNATURE) } else { None } }) .unwrap(); assert_eq_msgs(&msg.map_fd(|fd| fd.as_raw_fd()), &ret_msg.map_fd(IntoRawFd::into_raw_fd)); } #[test] fn write_read_cycle_fd() { let msg = Message { sender_id: 42, opcode: 7, args: smallvec![ Argument::Fd(1), // stdin Argument::Fd(0), // stdout ], }; let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap(); let mut client = BufferedSocket::new(Socket::from(client)); let mut server = BufferedSocket::new(Socket::from(server)); client.write_message(&msg).unwrap(); client.flush().unwrap(); static SIGNATURE: &[ArgumentType] = &[ArgumentType::Fd, ArgumentType::Fd]; server.fill_incoming_buffers().unwrap(); let ret_msg = server .read_one_message(|sender_id, opcode| { if sender_id == 42 && opcode == 7 { Some(SIGNATURE) } else { None } }) .unwrap(); assert_eq_msgs(&msg.map_fd(|fd| fd.as_raw_fd()), &ret_msg.map_fd(IntoRawFd::into_raw_fd)); } #[test] fn write_read_cycle_multiple() { let messages = vec![ Message { sender_id: 42, opcode: 0, args: smallvec![ Argument::Int(42), Argument::Str(Some(Box::new(CString::new(&b"I like trains"[..]).unwrap()))), ], }, Message { sender_id: 42, opcode: 1, args: smallvec![ Argument::Fd(1), // stdin Argument::Fd(0), // stdout ], }, Message { sender_id: 42, opcode: 2, args: smallvec![ Argument::Uint(3), Argument::Fd(2), // stderr ], }, ]; static SIGNATURES: &[&[ArgumentType]] = &[ &[ArgumentType::Int, ArgumentType::Str(AllowNull::No)], &[ArgumentType::Fd, ArgumentType::Fd], &[ArgumentType::Uint, ArgumentType::Fd], ]; let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap(); let mut client = BufferedSocket::new(Socket::from(client)); let mut server = BufferedSocket::new(Socket::from(server)); for msg in &messages { client.write_message(msg).unwrap(); } client.flush().unwrap(); server.fill_incoming_buffers().unwrap(); let mut recv_msgs = Vec::new(); while let Ok(message) = server.read_one_message(|sender_id, opcode| { if sender_id == 42 { Some(SIGNATURES[opcode as usize]) } else { None } }) { recv_msgs.push(message); } assert_eq!(recv_msgs.len(), 3); for (msg1, msg2) in messages.into_iter().zip(recv_msgs.into_iter()) { assert_eq_msgs(&msg1.map_fd(|fd| fd.as_raw_fd()), &msg2.map_fd(IntoRawFd::into_raw_fd)); } } #[test] fn parse_with_string_len_multiple_of_4() { let msg = Message { sender_id: 2, opcode: 0, args: smallvec![ Argument::Uint(18), Argument::Str(Some(Box::new(CString::new(&b"wl_shell"[..]).unwrap()))), Argument::Uint(1), ], }; let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap(); let mut client = BufferedSocket::new(Socket::from(client)); let mut server = BufferedSocket::new(Socket::from(server)); client.write_message(&msg).unwrap(); client.flush().unwrap(); static SIGNATURE: &[ArgumentType] = &[ArgumentType::Uint, ArgumentType::Str(AllowNull::No), ArgumentType::Uint]; server.fill_incoming_buffers().unwrap(); let ret_msg = server .read_one_message(|sender_id, opcode| { if sender_id == 2 && opcode == 0 { Some(SIGNATURE) } else { None } }) .unwrap(); assert_eq_msgs(&msg.map_fd(|fd| fd.as_raw_fd()), &ret_msg.map_fd(IntoRawFd::into_raw_fd)); } } wayland-backend-0.3.8/src/rs/wire.rs000064400000000000000000000244101046102023000154100ustar 00000000000000//! Types and routines used to manipulate arguments from the wire format use std::collections::VecDeque; use std::ffi::CStr; use std::os::unix::io::{BorrowedFd, OwnedFd, RawFd}; use crate::protocol::{Argument, ArgumentType, Message}; use smallvec::SmallVec; /// Error generated when trying to serialize a message into buffers #[derive(Debug)] pub enum MessageWriteError { /// The buffer is too small to hold the message contents BufferTooSmall, /// The message contains a FD that could not be dup-ed DupFdFailed(std::io::Error), } impl std::error::Error for MessageWriteError {} impl std::fmt::Display for MessageWriteError { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { match self { Self::BufferTooSmall => { f.write_str("The provided buffer is too small to hold message content.") } Self::DupFdFailed(e) => { write!( f, "The message contains a file descriptor that could not be dup()-ed ({}).", e ) } } } } /// Error generated when trying to deserialize a message from buffers #[derive(Debug, Clone)] pub enum MessageParseError { /// The message references a FD but the buffer FD is empty MissingFD, /// More data is needed to deserialize the message MissingData, /// The message is malformed and cannot be parsed Malformed, } impl std::error::Error for MessageParseError {} impl std::fmt::Display for MessageParseError { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { match *self { Self::MissingFD => { f.write_str("The message references a FD but the buffer FD is empty.") } Self::MissingData => f.write_str("More data is needed to deserialize the message"), Self::Malformed => f.write_str("The message is malformed and cannot be parsed"), } } } /// Serialize the contents of this message into provided buffers /// /// Returns the number of elements written in each buffer /// /// Any serialized Fd will be `dup()`-ed in the process pub fn write_to_buffers( msg: &Message, payload: &mut [u8], fds: &mut Vec, ) -> Result { let orig_payload_len = payload.len(); // Helper function to write a u32 or a RawFd to its buffer fn write_buf(u: u32, payload: &mut [u8]) -> Result<&mut [u8], MessageWriteError> { if payload.len() >= 4 { let (head, tail) = payload.split_at_mut(4); head.copy_from_slice(&u.to_ne_bytes()); Ok(tail) } else { Err(MessageWriteError::BufferTooSmall) } } // Helper function to write byte arrays in payload fn write_array_to_payload<'a>( array: &[u8], payload: &'a mut [u8], ) -> Result<&'a mut [u8], MessageWriteError> { // size header let payload = write_buf(array.len() as u32, payload)?; // Handle padding let len = next_multiple_of(array.len(), 4); if payload.len() < len { return Err(MessageWriteError::BufferTooSmall); } let (buffer_slice, rest) = payload.split_at_mut(len); buffer_slice[..array.len()].copy_from_slice(array); Ok(rest) } let free_size = payload.len(); if free_size < 2 * 4 { return Err(MessageWriteError::BufferTooSmall); } let (header, mut payload) = payload.split_at_mut(2 * 4); // write the contents in the buffer for arg in &msg.args { payload = match *arg { Argument::Int(i) => write_buf(i as u32, payload)?, Argument::Uint(u) => write_buf(u, payload)?, Argument::Fixed(f) => write_buf(f as u32, payload)?, Argument::Str(Some(ref s)) => write_array_to_payload(s.as_bytes_with_nul(), payload)?, Argument::Str(None) => write_array_to_payload(&[], payload)?, Argument::Object(o) => write_buf(o, payload)?, Argument::NewId(n) => write_buf(n, payload)?, Argument::Array(ref a) => write_array_to_payload(a, payload)?, Argument::Fd(fd) => { let dup_fd = unsafe { BorrowedFd::borrow_raw(fd) } .try_clone_to_owned() .map_err(MessageWriteError::DupFdFailed)?; fds.push(dup_fd); payload } }; } let wrote_size = free_size - payload.len(); header[..4].copy_from_slice(&msg.sender_id.to_ne_bytes()); header[4..] .copy_from_slice(&(((wrote_size as u32) << 16) | u32::from(msg.opcode)).to_ne_bytes()); Ok(orig_payload_len - payload.len()) } /// Attempts to parse a single wayland message with the given signature. /// /// If the buffers contains several messages, only the first one will be parsed, /// and the unused tail of the buffers is returned. If a single message was present, /// the returned slices should thus be empty. /// /// Errors if the message is malformed. #[allow(clippy::type_complexity)] pub fn parse_message<'a>( raw: &'a [u8], signature: &[ArgumentType], fds: &mut VecDeque, ) -> Result<(Message, &'a [u8]), MessageParseError> { // helper function to read arrays fn read_array_from_payload( array_len: usize, payload: &[u8], ) -> Result<(&[u8], &[u8]), MessageParseError> { let len = next_multiple_of(array_len, 4); if len > payload.len() { return Err(MessageParseError::MissingData); } Ok((&payload[..array_len], &payload[len..])) } if raw.len() < 2 * 4 { return Err(MessageParseError::MissingData); } let sender_id = u32::from_ne_bytes([raw[0], raw[1], raw[2], raw[3]]); let word_2 = u32::from_ne_bytes([raw[4], raw[5], raw[6], raw[7]]); let opcode = (word_2 & 0x0000_FFFF) as u16; let len = (word_2 >> 16) as usize; if len < 2 * 4 { return Err(MessageParseError::Malformed); } else if len > raw.len() { return Err(MessageParseError::MissingData); } let fd_len = signature.iter().filter(|x| matches!(x, ArgumentType::Fd)).count(); if fd_len > fds.len() { return Err(MessageParseError::MissingFD); } let (mut payload, rest) = raw.split_at(len); payload = &payload[2 * 4..]; let arguments = signature .iter() .map(|argtype| { if let ArgumentType::Fd = *argtype { // don't consume input but fd if let Some(front) = fds.pop_front() { Ok(Argument::Fd(front)) } else { Err(MessageParseError::MissingFD) } } else if payload.len() >= 4 { let (front, mut tail) = payload.split_at(4); let front = u32::from_ne_bytes(front.try_into().unwrap()); let arg = match *argtype { ArgumentType::Int => Ok(Argument::Int(front as i32)), ArgumentType::Uint => Ok(Argument::Uint(front)), ArgumentType::Fixed => Ok(Argument::Fixed(front as i32)), ArgumentType::Str(_) => { read_array_from_payload(front as usize, tail).and_then(|(v, rest)| { tail = rest; if !v.is_empty() { match CStr::from_bytes_with_nul(v) { Ok(s) => Ok(Argument::Str(Some(Box::new(s.into())))), Err(_) => Err(MessageParseError::Malformed), } } else { Ok(Argument::Str(None)) } }) } ArgumentType::Object(_) => Ok(Argument::Object(front)), ArgumentType::NewId => Ok(Argument::NewId(front)), ArgumentType::Array => { read_array_from_payload(front as usize, tail).map(|(v, rest)| { tail = rest; Argument::Array(Box::new(v.into())) }) } ArgumentType::Fd => unreachable!(), }; payload = tail; arg } else { Err(MessageParseError::MissingData) } }) .collect::, MessageParseError>>()?; let msg = Message { sender_id, opcode, args: arguments }; Ok((msg, rest)) } // Stabalized in Rust 1.73 fn next_multiple_of(lhs: usize, rhs: usize) -> usize { match lhs % rhs { 0 => lhs, r => lhs + (rhs - r), } } #[cfg(test)] mod tests { use super::*; use crate::protocol::AllowNull; use smallvec::smallvec; use std::{ffi::CString, os::unix::io::IntoRawFd}; #[test] fn into_from_raw_cycle() { let mut bytes_buffer = vec![0; 1024]; let mut fd_buffer = Vec::new(); let msg = Message { sender_id: 42, opcode: 7, args: smallvec![ Argument::Uint(3), Argument::Fixed(-89), Argument::Str(Some(Box::new(CString::new(&b"I like trains!"[..]).unwrap()))), Argument::Array(vec![1, 2, 3, 4, 5, 6, 7, 8, 9].into()), Argument::Object(88), Argument::NewId(56), Argument::Int(-25), ], }; // write the message to the buffers write_to_buffers(&msg, &mut bytes_buffer[..], &mut fd_buffer).unwrap(); // read them back let mut fd_buffer = VecDeque::from(fd_buffer); let (rebuilt, _) = parse_message( &bytes_buffer[..], &[ ArgumentType::Uint, ArgumentType::Fixed, ArgumentType::Str(AllowNull::No), ArgumentType::Array, ArgumentType::Object(AllowNull::No), ArgumentType::NewId, ArgumentType::Int, ], &mut fd_buffer, ) .unwrap(); assert_eq!(rebuilt.map_fd(IntoRawFd::into_raw_fd), msg); } } wayland-backend-0.3.8/src/server_api.rs000064400000000000000000000530451046102023000161630ustar 00000000000000use std::{ ffi::CString, fmt, os::unix::{ io::{BorrowedFd, OwnedFd, RawFd}, net::UnixStream, }, sync::Arc, }; use crate::protocol::{Interface, Message, ObjectInfo}; pub use crate::types::server::{Credentials, DisconnectReason, GlobalInfo, InitError, InvalidId}; use super::server_impl; /// A trait representing your data associated to an object /// /// You will only be given access to it as a `&` reference, so you /// need to handle interior mutability by yourself. /// /// The methods of this trait will be invoked internally every time a /// new object is created to initialize its data. pub trait ObjectData: downcast_rs::DowncastSync { /// Dispatch a request for the associated object /// /// If the request has a `NewId` argument, the callback must return the object data /// for the newly created object fn request( self: Arc, handle: &Handle, data: &mut D, client_id: ClientId, msg: Message, ) -> Option>>; /// Notification that the object has been destroyed and is no longer active fn destroyed( self: Arc, handle: &Handle, data: &mut D, client_id: ClientId, object_id: ObjectId, ); /// Helper for forwarding a Debug implementation of your `ObjectData` type /// /// By default will just print `ObjectData { ... }` #[cfg_attr(coverage, coverage(off))] fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ObjectData").finish_non_exhaustive() } } downcast_rs::impl_downcast!(sync ObjectData); impl std::fmt::Debug for dyn ObjectData { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.debug(f) } } /// A trait representing the handling of new bound globals pub trait GlobalHandler: downcast_rs::DowncastSync { /// Check if given client is allowed to interact with given global /// /// If this function returns false, the client will not be notified of the existence /// of this global, and any attempt to bind it will result in a protocol error as if /// the global did not exist. /// /// Default implementation always return true. fn can_view( &self, _client_id: ClientId, _client_data: &Arc, _global_id: GlobalId, ) -> bool { true } /// A global has been bound /// /// Given client bound given global, creating given object. /// /// The method must return the object data for the newly created object. fn bind( self: Arc, handle: &Handle, data: &mut D, client_id: ClientId, global_id: GlobalId, object_id: ObjectId, ) -> Arc>; /// Helper for forwarding a Debug implementation of your `GlobalHandler` type /// /// By default will just print `GlobalHandler { ... }` #[cfg_attr(coverage, coverage(off))] fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("GlobalHandler").finish_non_exhaustive() } } impl std::fmt::Debug for dyn GlobalHandler { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.debug(f) } } downcast_rs::impl_downcast!(sync GlobalHandler); /// A trait representing your data associated to a client pub trait ClientData: downcast_rs::DowncastSync { /// Notification that the client was initialized fn initialized(&self, _client_id: ClientId) {} /// Notification that the client is disconnected fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {} /// Helper for forwarding a Debug implementation of your `ClientData` type /// /// By default will just print `GlobalHandler { ... }` #[cfg_attr(coverage, coverage(off))] fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ClientData").finish_non_exhaustive() } } impl std::fmt::Debug for dyn ClientData { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.debug(f) } } impl ClientData for () {} downcast_rs::impl_downcast!(sync ClientData); /// An ID representing a Wayland object /// /// The backend internally tracks which IDs are still valid, invalidates them when the protocol object they /// represent is destroyed. As such even though the Wayland protocol reuses IDs, you still confidently compare /// two `ObjectId` for equality, they will only compare as equal if they both represent the same protocol /// object from the same client. #[derive(Clone, PartialEq, Eq, Hash)] pub struct ObjectId { pub(crate) id: server_impl::InnerObjectId, } impl ObjectId { /// Returns whether this object is a null object. /// /// **Note:** This is not the same as checking if the ID is still valid, which cannot be done without the /// [`Backend`]. A null ID is the ID equivalent of a null pointer: it never has been valid and never will /// be. pub fn is_null(&self) -> bool { self.id.is_null() } /// Returns an object id that represents a null object. /// /// This object ID is always invalid, and should be used for events with an optional `Object` argument. #[inline] pub fn null() -> ObjectId { server_impl::InnerHandle::null_id() } /// Returns the interface of this object. pub fn interface(&self) -> &'static Interface { self.id.interface() } /// Check if two object IDs are associated with the same client /// /// *Note:* This may spuriously return `false` if one (or both) of the objects to compare /// is no longer valid. pub fn same_client_as(&self, other: &Self) -> bool { self.id.same_client_as(&other.id) } /// Return the protocol-level numerical ID of this object /// /// Protocol IDs are reused after object destruction and each client has its own ID space, so this should /// not be used as a unique identifier, instead use the `ObjectId` directly, it implements `Clone`, /// `PartialEq`, `Eq` and `Hash`. pub fn protocol_id(&self) -> u32 { self.id.protocol_id() } } impl fmt::Display for ObjectId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.id.fmt(f) } } impl fmt::Debug for ObjectId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.id.fmt(f) } } /// An ID representing a Wayland client /// /// The backend internally tracks which IDs are still valid, invalidates them when the client they represent /// is disconnected. As such you can confidently compare two `ClientId` for equality, they will only compare /// as equal if they both represent the same client. #[derive(Clone, PartialEq, Eq, Hash)] pub struct ClientId { pub(crate) id: server_impl::InnerClientId, } impl fmt::Debug for ClientId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.id.fmt(f) } } /// An Id representing a global #[derive(Clone, PartialEq, Eq, Hash)] pub struct GlobalId { pub(crate) id: server_impl::InnerGlobalId, } impl fmt::Debug for GlobalId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.id.fmt(f) } } /// Main handle of a backend to the Wayland protocol /// /// This type hosts most of the protocol-related functionality of the backend, and is the /// main entry point for manipulating Wayland objects. It can be retrieved from the backend via /// [`Backend::handle()`] and cloned, and is given to you as argument in many callbacks. #[derive(Clone, Debug)] pub struct Handle { pub(crate) handle: server_impl::InnerHandle, } /// A weak reference to a [`Handle`] /// /// This handle behaves similarly to [`Weak`][std::sync::Weak], and can be used to keep access to /// the handle without actually preventing it from being dropped. #[derive(Clone, Debug)] pub struct WeakHandle { pub(crate) handle: server_impl::WeakInnerHandle, } impl WeakHandle { /// Try to upgrade this weak handle to a [`Handle`] /// /// Returns [`None`] if the associated backend was already dropped. #[inline] pub fn upgrade(&self) -> Option { self.handle.upgrade().map(|handle| Handle { handle }) } } impl Handle { /// Get a [`WeakHandle`] from this handle #[inline] pub fn downgrade(&self) -> WeakHandle { WeakHandle { handle: self.handle.downgrade() } } /// Get the detailed protocol information about a wayland object /// /// Returns an error if the provided object ID is no longer valid. #[inline] pub fn object_info(&self, id: ObjectId) -> Result { self.handle.object_info(id.id) } /// Initializes a connection with a client. /// /// The `data` parameter contains data that will be associated with the client. #[inline] pub fn insert_client( &mut self, stream: UnixStream, data: Arc, ) -> std::io::Result { Ok(ClientId { id: self.handle.insert_client(stream, data)? }) } /// Returns the id of the client which owns the object. #[inline] pub fn get_client(&self, id: ObjectId) -> Result { self.handle.get_client(id.id) } /// Returns the data associated with a client. #[inline] pub fn get_client_data(&self, id: ClientId) -> Result, InvalidId> { self.handle.get_client_data(id.id) } /// Retrive the [`Credentials`] of a client #[inline] pub fn get_client_credentials(&self, id: ClientId) -> Result { self.handle.get_client_credentials(id.id) } /// Invokes a closure for all clients connected to this server /// /// Note that while this method is running, an internal lock of the backend is held, /// as a result invoking other methods of the `Handle` within the closure will deadlock. /// You should thus store the relevant `ClientId` in a container of your choice and process /// them after this method has returned. #[inline] pub fn with_all_clients(&self, f: impl FnMut(ClientId)) { self.handle.with_all_clients(f) } /// Invokes a closure for all objects owned by a client. /// /// Note that while this method is running, an internal lock of the backend is held, /// as a result invoking other methods of the `Handle` within the closure will deadlock. /// You should thus store the relevant `ObjectId` in a container of your choice and process /// them after this method has returned. #[inline] pub fn with_all_objects_for( &self, client_id: ClientId, f: impl FnMut(ObjectId), ) -> Result<(), InvalidId> { self.handle.with_all_objects_for(client_id.id, f) } /// Retrieve the `ObjectId` for a wayland object given its protocol numerical ID #[inline] pub fn object_for_protocol_id( &self, client_id: ClientId, interface: &'static Interface, protocol_id: u32, ) -> Result { self.handle.object_for_protocol_id(client_id.id, interface, protocol_id) } /// Create a new object for given client /// /// To ensure state coherence of the protocol, the created object should be immediately /// sent as a "New ID" argument in an event to the client. /// /// # Panics /// /// This method will panic if the type parameter `D` is not same to the same type as the /// one the backend was initialized with. #[inline] pub fn create_object( &self, client_id: ClientId, interface: &'static Interface, version: u32, data: Arc>, ) -> Result { self.handle.create_object(client_id.id, interface, version, data) } /// Send an event to the client /// /// Returns an error if the sender ID of the provided message is no longer valid. /// /// # Panics /// /// Checks against the protocol specification are done, and this method will panic if they do /// not pass: /// /// - the message opcode must be valid for the sender interface /// - the argument list must match the prototype for the message associated with this opcode #[inline] pub fn send_event(&self, msg: Message) -> Result<(), InvalidId> { self.handle.send_event(msg) } /// Returns the data associated with an object. /// /// **Panic:** This method will panic if the type parameter `D` is not same to the same type as the /// one the backend was initialized with. #[inline] pub fn get_object_data( &self, id: ObjectId, ) -> Result>, InvalidId> { self.handle.get_object_data(id.id) } /// Returns the data associated with an object as a `dyn Any` #[inline] pub fn get_object_data_any( &self, id: ObjectId, ) -> Result, InvalidId> { self.handle.get_object_data_any(id.id) } /// Sets the data associated with some object. /// /// **Panic:** This method will panic if the type parameter `D` is not same to the same type as the /// one the backend was initialized with. #[inline] pub fn set_object_data( &self, id: ObjectId, data: Arc>, ) -> Result<(), InvalidId> { self.handle.set_object_data(id.id, data) } /// Posts a protocol error on an object. This will also disconnect the client which created the object. #[inline] pub fn post_error(&self, object_id: ObjectId, error_code: u32, message: CString) { self.handle.post_error(object_id.id, error_code, message) } /// Kills the connection to a client. /// /// The disconnection reason determines the error message that is sent to the client (if any). #[inline] pub fn kill_client(&self, client_id: ClientId, reason: DisconnectReason) { self.handle.kill_client(client_id.id, reason) } /// Creates a global of the specified interface and version and then advertises it to clients. /// /// The clients which the global is advertised to is determined by the implementation of the [`GlobalHandler`]. /// /// **Panic:** This method will panic if the type parameter `D` is not same to the same type as the /// one the backend was initialized with. #[inline] pub fn create_global( &self, interface: &'static Interface, version: u32, handler: Arc>, ) -> GlobalId { GlobalId { id: self.handle.create_global(interface, version, handler) } } /// Disables a global object that is currently active. /// /// The global removal will be signaled to all currently connected clients. New clients will not know of /// the global, but the associated state and callbacks will not be freed. As such, clients that still try /// to bind the global afterwards (because they have not yet realized it was removed) will succeed. /// /// Invoking this method on an already disabled or removed global does nothing. It is not possible to /// re-enable a disabled global, this method is meant to be invoked some time before actually removing /// the global, to avoid killing clients because of a race. /// /// **Panic:** This method will panic if the type parameter `D` is not same to the same type as the /// one the backend was initialized with. #[inline] pub fn disable_global(&self, id: GlobalId) { self.handle.disable_global::(id.id) } /// Removes a global object and free its ressources. /// /// The global object will no longer be considered valid by the server, clients trying to bind it will be /// killed, and the global ID is freed for re-use. /// /// It is advised to first disable a global and wait some amount of time before removing it, to ensure all /// clients are correctly aware of its removal. Note that clients will generally not expect globals that /// represent a capability of the server to be removed, as opposed to globals representing peripherals /// (like `wl_output` or `wl_seat`). /// /// This methods does nothing if the provided `GlobalId` corresponds to an already removed global. /// /// **Panic:** This method will panic if the type parameter `D` is not same to the same type as the /// one the backend was initialized with. #[inline] pub fn remove_global(&self, id: GlobalId) { self.handle.remove_global::(id.id) } /// Returns information about a global. #[inline] pub fn global_info(&self, id: GlobalId) -> Result { self.handle.global_info(id.id) } /// Returns the handler which manages the visibility and notifies when a client has bound the global. #[inline] pub fn get_global_handler( &self, id: GlobalId, ) -> Result>, InvalidId> { self.handle.get_global_handler(id.id) } /// Flushes pending events destined for a client. /// /// If no client is specified, all pending events are flushed to all clients. pub fn flush(&mut self, client: Option) -> std::io::Result<()> { self.handle.flush(client) } } /// A backend object that represents the state of a wayland server. /// /// A backend is used to drive a wayland server by receiving requests, dispatching messages to the appropriate /// handlers and flushes requests to be sent back to the client. #[derive(Debug)] pub struct Backend { pub(crate) backend: server_impl::InnerBackend, } impl Backend { /// Initialize a new Wayland backend #[inline] pub fn new() -> Result { Ok(Self { backend: server_impl::InnerBackend::new()? }) } /// Flushes pending events destined for a client. /// /// If no client is specified, all pending events are flushed to all clients. #[inline] pub fn flush(&mut self, client: Option) -> std::io::Result<()> { self.backend.flush(client) } /// Returns a handle which represents the server side state of the backend. /// /// The handle provides a variety of functionality, such as querying information about wayland objects, /// obtaining data associated with a client and it's objects, and creating globals. #[inline] pub fn handle(&self) -> Handle { self.backend.handle() } /// Returns the underlying file descriptor. /// /// The file descriptor may be monitored for activity with a polling mechanism such as epoll or kqueue. /// When it becomes readable, this means there are pending messages that would be dispatched if you call /// [`Backend::dispatch_all_clients`]. /// /// The file descriptor should not be used for any other purpose than monitoring it. #[inline] pub fn poll_fd(&self) -> BorrowedFd { self.backend.poll_fd() } /// Dispatches all pending messages from the specified client. /// /// This method will not block if there are no pending messages. /// /// The provided `data` will be provided to the handler of messages received from the client. /// /// For performance reasons, use of this function should be integrated with an event loop, monitoring /// the file descriptor associated with the client and only calling this method when messages are /// available. /// /// **Note:** This functionality is currently only available on the rust backend, invoking this method on /// the system backend will do the same as invoking /// [`Backend::dispatch_all_clients()`]. #[inline] pub fn dispatch_single_client( &mut self, data: &mut D, client_id: ClientId, ) -> std::io::Result { self.backend.dispatch_client(data, client_id.id) } /// Dispatches all pending messages from all clients. /// /// This method will not block if there are no pending messages. /// /// The provided `data` will be provided to the handler of messages received from the clients. /// /// For performance reasons, use of this function should be integrated with an event loop, monitoring the /// file descriptor retrieved by [`Backend::poll_fd`] and only calling this method when messages are /// available. #[inline] pub fn dispatch_all_clients(&mut self, data: &mut D) -> std::io::Result { self.backend.dispatch_all_clients(data) } } // Workaround: Some versions of rustc throw a `struct is never constructed`-warning here, // if the `server_system`-feature is enabled, even though the `rs`-module makes use if it. #[allow(dead_code)] pub(crate) struct DumbObjectData; #[allow(dead_code)] impl ObjectData for DumbObjectData { #[cfg_attr(coverage, coverage(off))] fn request( self: Arc, _handle: &Handle, _data: &mut D, _client_id: ClientId, _msg: Message, ) -> Option>> { unreachable!() } #[cfg_attr(coverage, coverage(off))] fn destroyed( self: Arc, _handle: &Handle, _: &mut D, _client_id: ClientId, _object_id: ObjectId, ) { } } wayland-backend-0.3.8/src/sys/client_impl/log_shim.c000064400000000000000000000010451046102023000205310ustar 00000000000000#include #include typedef void (wl_log_func_t)(const char *, va_list); void wl_log_trampoline_to_rust_client(char const *fmt, va_list list); void wl_log_rust_logger_client(char const *msg); void wl_log_trampoline_to_rust_client(char const *fmt, va_list list) { char buffer[256]; int ret = vsnprintf(buffer, 256, fmt, list); if (ret <= 0) { // forward the unformatted message, in a best-effort attempt wl_log_rust_logger_client(fmt); } else { wl_log_rust_logger_client(buffer); } }wayland-backend-0.3.8/src/sys/client_impl/mod.rs000064400000000000000000001105651046102023000177210ustar 00000000000000//! Client-side implementation of a Wayland protocol backend using `libwayland` use std::{ collections::HashSet, ffi::CStr, os::raw::{c_int, c_void}, os::unix::{ io::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}, net::UnixStream, }, sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, MutexGuard, Weak, }, }; use crate::{ core_interfaces::WL_DISPLAY_INTERFACE, debug, debug::has_debug_client_env, protocol::{ check_for_signature, same_interface, AllowNull, Argument, ArgumentType, Interface, Message, ObjectInfo, ProtocolError, ANONYMOUS_INTERFACE, }, }; use scoped_tls::scoped_thread_local; use smallvec::SmallVec; use wayland_sys::{client::*, common::*, ffi_dispatch}; use super::{free_arrays, RUST_MANAGED}; use super::client::*; scoped_thread_local! { // scoped_tls does not allow unsafe_op_in_unsafe_fn internally #[allow(unsafe_op_in_unsafe_fn)] static BACKEND: Backend } /// An ID representing a Wayland object #[derive(Clone)] pub struct InnerObjectId { id: u32, ptr: *mut wl_proxy, alive: Option>, interface: &'static Interface, } unsafe impl Send for InnerObjectId {} unsafe impl Sync for InnerObjectId {} impl std::cmp::PartialEq for InnerObjectId { fn eq(&self, other: &Self) -> bool { match (&self.alive, &other.alive) { (Some(ref a), Some(ref b)) => { // this is an object we manage Arc::ptr_eq(a, b) } (None, None) => { // this is an external (un-managed) object self.ptr == other.ptr && self.id == other.id && same_interface(self.interface, other.interface) } _ => false, } } } impl std::cmp::Eq for InnerObjectId {} impl std::hash::Hash for InnerObjectId { fn hash(&self, state: &mut H) { self.id.hash(state); self.ptr.hash(state); self.alive .as_ref() .map(|arc| &**arc as *const AtomicBool) .unwrap_or(std::ptr::null()) .hash(state); } } impl InnerObjectId { pub fn is_null(&self) -> bool { self.ptr.is_null() } pub fn interface(&self) -> &'static Interface { self.interface } pub fn protocol_id(&self) -> u32 { self.id } pub unsafe fn from_ptr( interface: &'static Interface, ptr: *mut wl_proxy, ) -> Result { // Safety: the provided pointer must be a valid wayland object let ptr_iface_name = unsafe { CStr::from_ptr(ffi_dispatch!(wayland_client_handle(), wl_proxy_get_class, ptr)) }; // Safety: the code generated by wayland-scanner is valid let provided_iface_name = unsafe { CStr::from_ptr( interface .c_ptr .expect("[wayland-backend-sys] Cannot use Interface without c_ptr!") .name, ) }; if ptr_iface_name != provided_iface_name { return Err(InvalidId); } let id = ffi_dispatch!(wayland_client_handle(), wl_proxy_get_id, ptr); // Test if the proxy is managed by us. let is_rust_managed = ffi_dispatch!(wayland_client_handle(), wl_proxy_get_listener, ptr) == &RUST_MANAGED as *const u8 as *const _; let alive = if is_rust_managed { // Safety: the object is rust_managed, so its user-data pointer must be valid let udata = unsafe { &*(ffi_dispatch!(wayland_client_handle(), wl_proxy_get_user_data, ptr) as *mut ProxyUserData) }; Some(udata.alive.clone()) } else { None }; Ok(Self { id, ptr, alive, interface }) } pub fn as_ptr(&self) -> *mut wl_proxy { if self.alive.as_ref().map(|alive| alive.load(Ordering::Acquire)).unwrap_or(true) { self.ptr } else { std::ptr::null_mut() } } } impl std::fmt::Display for InnerObjectId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}@{}", self.interface.name, self.id) } } impl std::fmt::Debug for InnerObjectId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "ObjectId({})", self) } } struct ProxyUserData { alive: Arc, data: Arc, interface: &'static Interface, } #[derive(Debug)] struct ConnectionState { display: *mut wl_display, owns_display: bool, evq: *mut wl_event_queue, display_id: InnerObjectId, last_error: Option, known_proxies: HashSet<*mut wl_proxy>, } unsafe impl Send for ConnectionState {} #[derive(Debug)] struct Dispatcher; #[derive(Debug)] struct Inner { state: Mutex, dispatch_lock: Mutex, debug: bool, } #[derive(Clone, Debug)] pub struct InnerBackend { inner: Arc, } #[derive(Clone, Debug)] pub struct WeakInnerBackend { inner: Weak, } impl InnerBackend { fn lock_state(&self) -> MutexGuard { self.inner.state.lock().unwrap() } pub fn downgrade(&self) -> WeakInnerBackend { WeakInnerBackend { inner: Arc::downgrade(&self.inner) } } pub fn display_ptr(&self) -> *mut wl_display { self.inner.state.lock().unwrap().display } } impl WeakInnerBackend { pub fn upgrade(&self) -> Option { Weak::upgrade(&self.inner).map(|inner| InnerBackend { inner }) } } impl PartialEq for InnerBackend { fn eq(&self, rhs: &Self) -> bool { Arc::ptr_eq(&self.inner, &rhs.inner) } } impl Eq for InnerBackend {} unsafe impl Send for InnerBackend {} unsafe impl Sync for InnerBackend {} impl InnerBackend { pub fn connect(stream: UnixStream) -> Result { if !is_lib_available() { return Err(NoWaylandLib); } let display = unsafe { ffi_dispatch!(wayland_client_handle(), wl_display_connect_to_fd, stream.into_raw_fd()) }; if display.is_null() { panic!("[wayland-backend-sys] libwayland reported an allocation failure."); } // set the log trampoline #[cfg(feature = "log")] unsafe { ffi_dispatch!( wayland_client_handle(), wl_log_set_handler_client, wl_log_trampoline_to_rust_client ); } Ok(Self::from_display(display, true)) } pub unsafe fn from_foreign_display(display: *mut wl_display) -> Self { Self::from_display(display, false) } fn from_display(display: *mut wl_display, owned: bool) -> Self { let evq = unsafe { ffi_dispatch!(wayland_client_handle(), wl_display_create_queue, display) }; let display_alive = owned.then(|| Arc::new(AtomicBool::new(true))); Self { inner: Arc::new(Inner { state: Mutex::new(ConnectionState { display, evq, display_id: InnerObjectId { id: 1, ptr: display as *mut wl_proxy, alive: display_alive, interface: &WL_DISPLAY_INTERFACE, }, owns_display: owned, last_error: None, known_proxies: HashSet::new(), }), debug: has_debug_client_env(), dispatch_lock: Mutex::new(Dispatcher), }), } } pub fn flush(&self) -> Result<(), WaylandError> { let mut guard = self.lock_state(); guard.no_last_error()?; let ret = unsafe { ffi_dispatch!(wayland_client_handle(), wl_display_flush, guard.display) }; if ret < 0 { Err(guard.store_if_not_wouldblock_and_return_error(std::io::Error::last_os_error())) } else { Ok(()) } } pub fn poll_fd(&self) -> BorrowedFd { let guard = self.lock_state(); unsafe { BorrowedFd::borrow_raw(ffi_dispatch!( wayland_client_handle(), wl_display_get_fd, guard.display )) } } pub fn dispatch_inner_queue(&self) -> Result { self.inner.dispatch_lock.lock().unwrap().dispatch_pending(self.inner.clone()) } } impl ConnectionState { #[inline] fn no_last_error(&self) -> Result<(), WaylandError> { if let Some(ref err) = self.last_error { Err(err.clone()) } else { Ok(()) } } #[inline] fn store_and_return_error(&mut self, err: std::io::Error) -> WaylandError { // check if it was actually a protocol error let err = if err.raw_os_error() == Some(rustix::io::Errno::PROTO.raw_os_error()) { let mut object_id = 0; let mut interface = std::ptr::null(); let code = unsafe { ffi_dispatch!( wayland_client_handle(), wl_display_get_protocol_error, self.display, &mut interface, &mut object_id ) }; let object_interface = unsafe { if interface.is_null() { String::new() } else { let cstr = std::ffi::CStr::from_ptr((*interface).name); cstr.to_string_lossy().into() } }; WaylandError::Protocol(ProtocolError { code, object_id, object_interface, message: String::new(), }) } else { WaylandError::Io(err) }; crate::log_error!("{}", err); self.last_error = Some(err.clone()); err } #[inline] fn store_if_not_wouldblock_and_return_error(&mut self, e: std::io::Error) -> WaylandError { if e.kind() != std::io::ErrorKind::WouldBlock { self.store_and_return_error(e) } else { e.into() } } } impl Dispatcher { fn dispatch_pending(&self, inner: Arc) -> Result { let (display, evq) = { let guard = inner.state.lock().unwrap(); (guard.display, guard.evq) }; let backend = Backend { backend: InnerBackend { inner } }; // We erase the lifetime of the Handle to be able to store it in the tls, // it's safe as it'll only last until the end of this function call anyway let ret = BACKEND.set(&backend, || unsafe { ffi_dispatch!(wayland_client_handle(), wl_display_dispatch_queue_pending, display, evq) }); if ret < 0 { Err(backend .backend .inner .state .lock() .unwrap() .store_if_not_wouldblock_and_return_error(std::io::Error::last_os_error())) } else { Ok(ret as usize) } } } #[derive(Debug)] pub struct InnerReadEventsGuard { inner: Arc, display: *mut wl_display, done: bool, } impl InnerReadEventsGuard { pub fn try_new(backend: InnerBackend) -> Option { let (display, evq) = { let guard = backend.lock_state(); (guard.display, guard.evq) }; let ret = unsafe { ffi_dispatch!(wayland_client_handle(), wl_display_prepare_read_queue, display, evq) }; if ret < 0 { None } else { Some(Self { inner: backend.inner, display, done: false }) } } pub fn connection_fd(&self) -> BorrowedFd { unsafe { BorrowedFd::borrow_raw(ffi_dispatch!( wayland_client_handle(), wl_display_get_fd, self.display )) } } pub fn read(mut self) -> Result { self.done = true; let ret = unsafe { ffi_dispatch!(wayland_client_handle(), wl_display_read_events, self.display) }; if ret < 0 { // we have done the reading, and there is an error Err(self .inner .state .lock() .unwrap() .store_if_not_wouldblock_and_return_error(std::io::Error::last_os_error())) } else { // the read occured, dispatch pending events self.inner.dispatch_lock.lock().unwrap().dispatch_pending(self.inner.clone()) } } } impl Drop for InnerReadEventsGuard { fn drop(&mut self) { if !self.done { unsafe { ffi_dispatch!(wayland_client_handle(), wl_display_cancel_read, self.display); } } } } impl InnerBackend { pub fn display_id(&self) -> ObjectId { ObjectId { id: self.lock_state().display_id.clone() } } pub fn last_error(&self) -> Option { self.lock_state().last_error.clone() } pub fn info(&self, ObjectId { id }: ObjectId) -> Result { if !id.alive.as_ref().map(|a| a.load(Ordering::Acquire)).unwrap_or(true) || id.ptr.is_null() { return Err(InvalidId); } let version = if id.id == 1 { // special case the display, because libwayland returns a version of 0 for it 1 } else { unsafe { ffi_dispatch!(wayland_client_handle(), wl_proxy_get_version, id.ptr) } }; Ok(ObjectInfo { id: id.id, interface: id.interface, version }) } pub fn null_id() -> ObjectId { ObjectId { id: InnerObjectId { ptr: std::ptr::null_mut(), interface: &ANONYMOUS_INTERFACE, id: 0, alive: None, }, } } pub fn send_request( &self, Message { sender_id: ObjectId { id }, opcode, args }: Message, data: Option>, child_spec: Option<(&'static Interface, u32)>, ) -> Result { let mut guard = self.lock_state(); // check that the argument list is valid let message_desc = match id.interface.requests.get(opcode as usize) { Some(msg) => msg, None => { panic!("Unknown opcode {} for object {}@{}.", opcode, id.interface.name, id.id); } }; if !id.alive.as_ref().map(|a| a.load(Ordering::Acquire)).unwrap_or(true) || id.ptr.is_null() { if self.inner.debug { debug::print_send_message(id.interface.name, id.id, message_desc.name, &args, true); } return Err(InvalidId); } let parent_version = if id.id == 1 { 1 } else { unsafe { ffi_dispatch!(wayland_client_handle(), wl_proxy_get_version, id.ptr) } }; if !check_for_signature(message_desc.signature, &args) { panic!( "Unexpected signature for request {}@{}.{}: expected {:?}, got {:?}.", id.interface.name, id.id, message_desc.name, message_desc.signature, args ); } // Prepare the child object data let child_spec = if message_desc .signature .iter() .any(|arg| matches!(arg, ArgumentType::NewId)) { if let Some((iface, version)) = child_spec { if let Some(child_interface) = message_desc.child_interface { if !same_interface(child_interface, iface) { panic!( "Wrong placeholder used when sending request {}@{}.{}: expected interface {} but got {}", id.interface.name, id.id, message_desc.name, child_interface.name, iface.name ); } if version != parent_version { panic!( "Wrong placeholder used when sending request {}@{}.{}: expected version {} but got {}", id.interface.name, id.id, message_desc.name, parent_version, version ); } } Some((iface, version)) } else if let Some(child_interface) = message_desc.child_interface { Some((child_interface, parent_version)) } else { panic!( "Wrong placeholder used when sending request {}@{}.{}: target interface must be specified for a generic constructor.", id.interface.name, id.id, message_desc.name ); } } else { None }; let child_interface_ptr = child_spec .as_ref() .map(|(i, _)| { i.c_ptr.expect("[wayland-backend-sys] Cannot use Interface without c_ptr!") as *const _ }) .unwrap_or(std::ptr::null()); let child_version = child_spec.as_ref().map(|(_, v)| *v).unwrap_or(parent_version); // check that all input objects are valid and create the [wl_argument] let mut argument_list = SmallVec::<[wl_argument; 4]>::with_capacity(args.len()); let mut arg_interfaces = message_desc.arg_interfaces.iter(); for (i, arg) in args.iter().enumerate() { match *arg { Argument::Uint(u) => argument_list.push(wl_argument { u }), Argument::Int(i) => argument_list.push(wl_argument { i }), Argument::Fixed(f) => argument_list.push(wl_argument { f }), Argument::Fd(h) => argument_list.push(wl_argument { h }), Argument::Array(ref a) => { let a = Box::new(wl_array { size: a.len(), alloc: a.len(), data: a.as_ptr() as *mut _, }); argument_list.push(wl_argument { a: Box::into_raw(a) }) } Argument::Str(Some(ref s)) => argument_list.push(wl_argument { s: s.as_ptr() }), Argument::Str(None) => argument_list.push(wl_argument { s: std::ptr::null() }), Argument::Object(ref o) => { let next_interface = arg_interfaces.next().unwrap(); if !o.id.ptr.is_null() { if !o.id.alive.as_ref().map(|a| a.load(Ordering::Acquire)).unwrap_or(true) { unsafe { free_arrays(message_desc.signature, &argument_list) }; return Err(InvalidId); } if !same_interface(next_interface, o.id.interface) { panic!("Request {}@{}.{} expects an argument of interface {} but {} was provided instead.", id.interface.name, id.id, message_desc.name, next_interface.name, o.id.interface.name); } } else if !matches!( message_desc.signature[i], ArgumentType::Object(AllowNull::Yes) ) { panic!( "Request {}@{}.{} expects an non-null object argument.", id.interface.name, id.id, message_desc.name ); } argument_list.push(wl_argument { o: o.id.ptr as *const _ }) } Argument::NewId(_) => argument_list.push(wl_argument { n: 0 }), } } let ret = if child_spec.is_none() { unsafe { ffi_dispatch!( wayland_client_handle(), wl_proxy_marshal_array, id.ptr, opcode as u32, argument_list.as_mut_ptr(), ) } std::ptr::null_mut() } else { // We are a guest Backend, need to use a wrapper unsafe { let wrapped_ptr = ffi_dispatch!(wayland_client_handle(), wl_proxy_create_wrapper, id.ptr); ffi_dispatch!(wayland_client_handle(), wl_proxy_set_queue, wrapped_ptr, guard.evq); let ret = ffi_dispatch!( wayland_client_handle(), wl_proxy_marshal_array_constructor_versioned, wrapped_ptr, opcode as u32, argument_list.as_mut_ptr(), child_interface_ptr, child_version ); ffi_dispatch!(wayland_client_handle(), wl_proxy_wrapper_destroy, wrapped_ptr); ret } }; unsafe { free_arrays(message_desc.signature, &argument_list); } if ret.is_null() && child_spec.is_some() { panic!("[wayland-backend-sys] libwayland reported an allocation failure."); } // initialize the proxy let child_id = if let Some((child_interface, _)) = child_spec { let data = match data { Some(data) => data, None => { // we destroy this proxy before panicking to avoid a leak, as it cannot be destroyed by the // main destructor given it does not yet have a proper user-data unsafe { ffi_dispatch!(wayland_client_handle(), wl_proxy_destroy, ret); } panic!( "Sending a request creating an object without providing an object data." ); } }; unsafe { self.manage_object_internal(child_interface, ret, data, &mut guard) } } else { Self::null_id() }; if message_desc.is_destructor { if let Some(ref alive) = id.alive { let udata = unsafe { Box::from_raw(ffi_dispatch!( wayland_client_handle(), wl_proxy_get_user_data, id.ptr ) as *mut ProxyUserData) }; unsafe { ffi_dispatch!( wayland_client_handle(), wl_proxy_set_user_data, id.ptr, std::ptr::null_mut() ); } alive.store(false, Ordering::Release); udata.data.destroyed(ObjectId { id: id.clone() }); } guard.known_proxies.remove(&id.ptr); unsafe { ffi_dispatch!(wayland_client_handle(), wl_proxy_destroy, id.ptr); } } Ok(child_id) } pub fn get_data(&self, ObjectId { id }: ObjectId) -> Result, InvalidId> { if !id.alive.as_ref().map(|a| a.load(Ordering::Acquire)).unwrap_or(false) { return Err(InvalidId); } if id.id == 1 { // special case the display whose object data is not accessible return Ok(Arc::new(DumbObjectData)); } let udata = unsafe { &*(ffi_dispatch!(wayland_client_handle(), wl_proxy_get_user_data, id.ptr) as *mut ProxyUserData) }; Ok(udata.data.clone()) } pub fn set_data( &self, ObjectId { id }: ObjectId, data: Arc, ) -> Result<(), InvalidId> { if !id.alive.as_ref().map(|a| a.load(Ordering::Acquire)).unwrap_or(false) { return Err(InvalidId); } // Cannot touch the user_data of the display if id.id == 1 { return Err(InvalidId); } let udata = unsafe { &mut *(ffi_dispatch!(wayland_client_handle(), wl_proxy_get_user_data, id.ptr) as *mut ProxyUserData) }; udata.data = data; Ok(()) } /// Start managing a Wayland object. /// /// Safety: This will change the event queue the proxy is associated with. /// Changing the event queue of an existing proxy is not thread-safe. /// If another thread is concurrently reading the wayland socket and the /// proxy already received an event it might get enqueued on the old event queue. pub unsafe fn manage_object( &self, interface: &'static Interface, proxy: *mut wl_proxy, data: Arc, ) -> ObjectId { let mut guard = self.lock_state(); unsafe { ffi_dispatch!(wayland_client_handle(), wl_proxy_set_queue, proxy, guard.evq); self.manage_object_internal(interface, proxy, data, &mut guard) } } /// Start managing a Wayland object. /// /// Opposed to [`Self::manage_object`], this does not acquire any guards. unsafe fn manage_object_internal( &self, interface: &'static Interface, proxy: *mut wl_proxy, data: Arc, guard: &mut MutexGuard, ) -> ObjectId { let alive = Arc::new(AtomicBool::new(true)); let object_id = ObjectId { id: InnerObjectId { ptr: proxy, alive: Some(alive.clone()), id: unsafe { ffi_dispatch!(wayland_client_handle(), wl_proxy_get_id, proxy) }, interface, }, }; guard.known_proxies.insert(proxy); let udata = Box::new(ProxyUserData { alive, data, interface }); unsafe { ffi_dispatch!( wayland_client_handle(), wl_proxy_add_dispatcher, proxy, dispatcher_func, &RUST_MANAGED as *const u8 as *const c_void, Box::into_raw(udata) as *mut c_void ); } object_id } } unsafe extern "C" fn dispatcher_func( _: *const c_void, proxy: *mut c_void, opcode: u32, _: *const wl_message, args: *const wl_argument, ) -> c_int { let proxy = proxy as *mut wl_proxy; // Safety: if our dispatcher fun is called, then the associated proxy must be rust_managed and have a valid user_data let udata_ptr = unsafe { ffi_dispatch!(wayland_client_handle(), wl_proxy_get_user_data, proxy) as *mut ProxyUserData }; let udata = unsafe { &mut *udata_ptr }; let interface = udata.interface; let message_desc = match interface.events.get(opcode as usize) { Some(desc) => desc, None => { crate::log_error!("Unknown event opcode {} for interface {}.", opcode, interface.name); return -1; } }; let mut parsed_args = SmallVec::<[Argument; 4]>::with_capacity(message_desc.signature.len()); let mut arg_interfaces = message_desc.arg_interfaces.iter().copied(); let mut created = None; // Safety (args deference): the args array provided by libwayland is well-formed for (i, typ) in message_desc.signature.iter().enumerate() { match typ { ArgumentType::Uint => parsed_args.push(Argument::Uint(unsafe { (*args.add(i)).u })), ArgumentType::Int => parsed_args.push(Argument::Int(unsafe { (*args.add(i)).i })), ArgumentType::Fixed => parsed_args.push(Argument::Fixed(unsafe { (*args.add(i)).f })), ArgumentType::Fd => { parsed_args.push(Argument::Fd(unsafe { OwnedFd::from_raw_fd((*args.add(i)).h) })) } ArgumentType::Array => { let array = unsafe { &*((*args.add(i)).a) }; // Safety: the array provided by libwayland must be valid let content = unsafe { std::slice::from_raw_parts(array.data as *mut u8, array.size) }; parsed_args.push(Argument::Array(Box::new(content.into()))); } ArgumentType::Str(_) => { let ptr = unsafe { (*args.add(i)).s }; // Safety: the c-string provided by libwayland must be valid if !ptr.is_null() { let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) }; parsed_args.push(Argument::Str(Some(Box::new(cstr.into())))); } else { parsed_args.push(Argument::Str(None)); } } ArgumentType::Object(_) => { let obj = unsafe { (*args.add(i)).o as *mut wl_proxy }; if !obj.is_null() { // retrieve the object relevant info let obj_id = ffi_dispatch!(wayland_client_handle(), wl_proxy_get_id, obj); // check if this is a local or distant proxy let next_interface = arg_interfaces.next().unwrap_or(&ANONYMOUS_INTERFACE); let listener = ffi_dispatch!(wayland_client_handle(), wl_proxy_get_listener, obj); if listener == &RUST_MANAGED as *const u8 as *const c_void { // Safety: the object is rust-managed, its user-data must be valid let obj_udata = unsafe { &*(ffi_dispatch!(wayland_client_handle(), wl_proxy_get_user_data, obj) as *mut ProxyUserData) }; if !same_interface(next_interface, obj_udata.interface) { crate::log_error!( "Received object {}@{} in {}.{} but expected interface {}.", obj_udata.interface.name, obj_id, interface.name, message_desc.name, next_interface.name, ); return -1; } parsed_args.push(Argument::Object(ObjectId { id: InnerObjectId { alive: Some(obj_udata.alive.clone()), ptr: obj, id: obj_id, interface: obj_udata.interface, }, })); } else { parsed_args.push(Argument::Object(ObjectId { id: InnerObjectId { alive: None, id: obj_id, ptr: obj, interface: next_interface, }, })); } } else { // libwayland-client.so checks nulls for us parsed_args.push(Argument::Object(ObjectId { id: InnerObjectId { alive: None, id: 0, ptr: std::ptr::null_mut(), interface: &ANONYMOUS_INTERFACE, }, })) } } ArgumentType::NewId => { let obj = unsafe { (*args.add(i)).o as *mut wl_proxy }; // this is a newid, it needs to be initialized if !obj.is_null() { let child_interface = message_desc.child_interface.unwrap_or_else(|| { crate::log_warn!( "Event {}.{} creates an anonymous object.", interface.name, opcode ); &ANONYMOUS_INTERFACE }); let child_alive = Arc::new(AtomicBool::new(true)); let child_id = InnerObjectId { ptr: obj, alive: Some(child_alive.clone()), id: ffi_dispatch!(wayland_client_handle(), wl_proxy_get_id, obj), interface: child_interface, }; let child_udata = Box::into_raw(Box::new(ProxyUserData { alive: child_alive, data: Arc::new(UninitObjectData), interface: child_interface, })); created = Some((child_id.clone(), child_udata)); ffi_dispatch!( wayland_client_handle(), wl_proxy_add_dispatcher, obj, dispatcher_func, &RUST_MANAGED as *const u8 as *const c_void, child_udata as *mut c_void ); parsed_args.push(Argument::NewId(ObjectId { id: child_id })); } else { parsed_args.push(Argument::NewId(ObjectId { id: InnerObjectId { id: 0, ptr: std::ptr::null_mut(), alive: None, interface: &ANONYMOUS_INTERFACE, }, })) } } } } let proxy_id = ffi_dispatch!(wayland_client_handle(), wl_proxy_get_id, proxy); let id = ObjectId { id: InnerObjectId { alive: Some(udata.alive.clone()), ptr: proxy, id: proxy_id, interface: udata.interface, }, }; let ret = BACKEND.with(|backend| { let mut guard = backend.backend.lock_state(); if let Some((ref new_id, _)) = created { guard.known_proxies.insert(new_id.ptr); } if message_desc.is_destructor { guard.known_proxies.remove(&proxy); } std::mem::drop(guard); udata.data.clone().event( backend, Message { sender_id: id.clone(), opcode: opcode as u16, args: parsed_args }, ) }); if message_desc.is_destructor { // Safety: the udata_ptr must be valid as we are in a rust-managed object, and we are done with using udata let udata = unsafe { Box::from_raw(udata_ptr) }; ffi_dispatch!(wayland_client_handle(), wl_proxy_set_user_data, proxy, std::ptr::null_mut()); udata.alive.store(false, Ordering::Release); udata.data.destroyed(id); ffi_dispatch!(wayland_client_handle(), wl_proxy_destroy, proxy); } match (created, ret) { (Some((_, child_udata_ptr)), Some(child_data)) => { // Safety: child_udata_ptr is valid, we created it earlier unsafe { (*child_udata_ptr).data = child_data; } } (Some((child_id, _)), None) => { panic!("Callback creating object {} did not provide any object data.", child_id); } (None, Some(_)) => { panic!("An object data was returned from a callback not creating any object"); } (None, None) => {} } 0 } #[cfg(feature = "log")] extern "C" { fn wl_log_trampoline_to_rust_client(fmt: *const std::os::raw::c_char, list: *const c_void); } impl Drop for ConnectionState { fn drop(&mut self) { // Cleanup the objects we know about, libwayland will discard any future message // they receive. for proxy_ptr in self.known_proxies.drain() { let _ = unsafe { Box::from_raw(ffi_dispatch!( wayland_client_handle(), wl_proxy_get_user_data, proxy_ptr ) as *mut ProxyUserData) }; unsafe { ffi_dispatch!(wayland_client_handle(), wl_proxy_destroy, proxy_ptr); } } unsafe { ffi_dispatch!(wayland_client_handle(), wl_event_queue_destroy, self.evq) } if self.owns_display { // we own the connection, close it unsafe { ffi_dispatch!(wayland_client_handle(), wl_display_disconnect, self.display) } } } } wayland-backend-0.3.8/src/sys/mod.rs000064400000000000000000000173171046102023000154230ustar 00000000000000//! Implementations of the Wayland backends using the system `libwayland` use std::sync::Arc; use wayland_sys::client::wl_proxy; use wayland_sys::common::{wl_argument, wl_array}; use crate::client::{ObjectData, ObjectId}; use crate::protocol::{ArgumentType, Interface}; #[cfg(any(test, feature = "client_system"))] mod client_impl; #[cfg(any(test, feature = "server_system"))] mod server_impl; /// Magic static for wayland objects managed by wayland-client or wayland-server /// /// This static serves no purpose other than existing at a stable address. static RUST_MANAGED: u8 = 42; unsafe fn free_arrays(signature: &[ArgumentType], arglist: &[wl_argument]) { for (typ, arg) in signature.iter().zip(arglist.iter()) { if let ArgumentType::Array = typ { // Safety: the arglist provided arglist must be valid for associated signature // and contains pointers to boxed arrays as appropriate let _ = unsafe { Box::from_raw(arg.a as *mut wl_array) }; } } } /// Client-side implementation of a Wayland protocol backend using `libwayland` /// /// Entrypoints are: /// - [`Backend::connect()`][client::Backend::connect()] method if you're creating the Wayland connection /// - [`Backend::from_foreign_display()`][client::Backend::from_foreign_display()] if you're interacting with an /// already existing Wayland connection through FFI. #[cfg(any(test, feature = "client_system"))] #[path = "../client_api.rs"] pub mod client; // API complements for FFI #[cfg(any(test, feature = "client_system"))] impl client::ObjectId { /// Creates an object id from a libwayland-client pointer. /// /// # Errors /// /// This function returns an [`InvalidId`][client::InvalidId] error if the interface of the proxy does /// not match the provided interface. /// /// # Safety /// /// The provided pointer must be a valid pointer to a `wl_resource` and remain valid for as /// long as the retrieved `ObjectId` is used. pub unsafe fn from_ptr( interface: &'static crate::protocol::Interface, ptr: *mut wayland_sys::client::wl_proxy, ) -> Result { Ok(Self { id: unsafe { client_impl::InnerObjectId::from_ptr(interface, ptr) }? }) } /// Get the underlying libwayland pointer for this object pub fn as_ptr(&self) -> *mut wayland_sys::client::wl_proxy { self.id.as_ptr() } } #[cfg(any(test, feature = "client_system"))] impl client::Backend { /// Creates a Backend from a foreign `*mut wl_display`. /// /// This is useful if you are writing a library that is expected to plug itself into an existing /// Wayland connection. /// /// This will initialize the [`Backend`][Self] in "guest" mode, meaning it will not close the /// connection on drop. After the [`Backend`][Self] is dropped, if the server sends an event /// to an object that was created from it, that event will be silently discarded. This may lead to /// protocol errors if the server expects an answer to that event, as such you should make sure to /// cleanup your Wayland state before dropping the [`Backend`][Self]. /// /// # Safety /// /// You need to ensure the `*mut wl_display` remains live as long as the [`Backend`][Self] /// (or its clones) exist. pub unsafe fn from_foreign_display(display: *mut wayland_sys::client::wl_display) -> Self { Self { backend: unsafe { client_impl::InnerBackend::from_foreign_display(display) } } } /// Returns the underlying `wl_display` pointer to this backend. /// /// This pointer is needed to interface with EGL, Vulkan and other C libraries. /// /// This pointer is only valid for the lifetime of the backend. pub fn display_ptr(&self) -> *mut wayland_sys::client::wl_display { self.backend.display_ptr() } /// Take over handling for a proxy created by a third party. /// /// # Safety /// /// There must never be more than one party managing an object. This is only /// safe to call when a third party gave you ownership of an unmanaged proxy. /// /// The caller is also responsible for making sure the passed interface matches /// the proxy. #[inline] pub unsafe fn manage_object( &self, interface: &'static Interface, proxy: *mut wl_proxy, data: Arc, ) -> ObjectId { unsafe { self.backend.manage_object(interface, proxy, data) } } } // SAFETY: // - The display_ptr will not change for the lifetime of the backend. // - The display_ptr will be valid, either because we have created the pointer or the caller which created the // backend has ensured the pointer is valid when `Backend::from_foreign_display` was called. #[cfg(feature = "raw-window-handle")] unsafe impl raw_window_handle::HasRawDisplayHandle for client::Backend { fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle { let mut handle = raw_window_handle::WaylandDisplayHandle::empty(); handle.display = self.display_ptr().cast(); raw_window_handle::RawDisplayHandle::Wayland(handle) } } #[cfg(all(feature = "rwh_06", feature = "client_system"))] impl rwh_06::HasDisplayHandle for client::Backend { fn display_handle(&self) -> Result, rwh_06::HandleError> { use std::ptr::NonNull; // SAFETY: // - The display_ptr will be valid, either because we have created the pointer or the caller which created the // backend has ensured the pointer is valid when `Backend::from_foreign_display` was called. let ptr = unsafe { NonNull::new_unchecked(self.display_ptr().cast()) }; let handle = rwh_06::WaylandDisplayHandle::new(ptr); let raw = rwh_06::RawDisplayHandle::Wayland(handle); // SAFETY: // - The display_ptr will be valid, either because we have created the pointer or the caller which created the // backend has ensured the pointer is valid when `Backend::from_foreign_display` was called. // - The lifetime assigned to the DisplayHandle borrows the Backend, ensuring the display pointer // is valid.. // - The display_ptr will not change for the lifetime of the backend. Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw) }) } } /// Server-side implementation of a Wayland protocol backend using `libwayland` /// /// The main entrypoint is the [`Backend::new()`][server::Backend::new()] method. #[cfg(any(test, feature = "server_system"))] #[path = "../server_api.rs"] pub mod server; #[cfg(any(test, feature = "server_system"))] impl server::ObjectId { /// Creates an object from a C pointer. /// /// # Errors /// /// This function returns an [`InvalidId`][server::InvalidId] error if the interface of the /// resource does not match the provided interface. /// /// # Safety /// /// The provided pointer must be a valid pointer to a `wl_resource` and remain valid for as /// long as the retrieved `ObjectId` is used. pub unsafe fn from_ptr( interface: &'static crate::protocol::Interface, ptr: *mut wayland_sys::server::wl_resource, ) -> Result { Ok(Self { id: unsafe { server_impl::InnerObjectId::from_ptr(Some(interface), ptr) }? }) } /// Returns the pointer that represents this object. /// /// The pointer may be used to interoperate with libwayland. pub fn as_ptr(&self) -> *mut wayland_sys::server::wl_resource { self.id.as_ptr() } } #[cfg(any(test, feature = "server_system"))] impl server::Handle { /// Access the underlying `*mut wl_display` pointer pub fn display_ptr(&self) -> *mut wayland_sys::server::wl_display { self.handle.display_ptr() } } wayland-backend-0.3.8/src/sys/server_impl/log_shim.c000064400000000000000000000010451046102023000205610ustar 00000000000000#include #include typedef void (wl_log_func_t)(const char *, va_list); void wl_log_trampoline_to_rust_server(char const *fmt, va_list list); void wl_log_rust_logger_server(char const *msg); void wl_log_trampoline_to_rust_server(char const *fmt, va_list list) { char buffer[256]; int ret = vsnprintf(buffer, 256, fmt, list); if (ret <= 0) { // forward the unformatted message, in a best-effort attempt wl_log_rust_logger_server(fmt); } else { wl_log_rust_logger_server(buffer); } }wayland-backend-0.3.8/src/sys/server_impl/mod.rs000064400000000000000000001640721046102023000177530ustar 00000000000000//! Server-side implementation of a Wayland protocol backend using `libwayland` use std::{ ffi::{CStr, CString}, os::raw::{c_int, c_void}, os::unix::{ io::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}, net::UnixStream, }, sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, Weak, }, }; use crate::protocol::{ check_for_signature, same_interface, AllowNull, Argument, ArgumentType, Interface, Message, ObjectInfo, ANONYMOUS_INTERFACE, }; use scoped_tls::scoped_thread_local; use smallvec::SmallVec; use wayland_sys::{common::*, ffi_dispatch, server::*}; use super::{free_arrays, server::*, RUST_MANAGED}; #[allow(unused_imports)] pub use crate::types::server::{Credentials, DisconnectReason, GlobalInfo, InitError, InvalidId}; scoped_thread_local! { // scoped_tls does not allow unsafe_op_in_unsafe_fn internally #[allow(unsafe_op_in_unsafe_fn)] static HANDLE: (Arc>, *mut c_void) } type PendingDestructor = (Arc>, ClientId, ObjectId); // Pointer is &mut Vec> scoped_thread_local! { // scoped_tls does not allow unsafe_op_in_unsafe_fn internally #[allow(unsafe_op_in_unsafe_fn)] static PENDING_DESTRUCTORS: *mut c_void } /// An id of an object on a wayland server. #[derive(Clone)] pub struct InnerObjectId { id: u32, ptr: *mut wl_resource, alive: Arc, interface: &'static Interface, } unsafe impl Send for InnerObjectId {} unsafe impl Sync for InnerObjectId {} impl std::cmp::PartialEq for InnerObjectId { fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.alive, &other.alive) } } impl std::cmp::Eq for InnerObjectId {} impl std::hash::Hash for InnerObjectId { fn hash(&self, state: &mut H) { self.id.hash(state); self.ptr.hash(state); (&*self.alive as *const AtomicBool).hash(state); } } impl std::fmt::Display for InnerObjectId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}@{}", self.interface.name, self.id) } } impl std::fmt::Debug for InnerObjectId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "ObjectId({})", self) } } impl InnerObjectId { pub fn is_null(&self) -> bool { self.ptr.is_null() } pub fn interface(&self) -> &'static Interface { self.interface } pub fn same_client_as(&self, other: &Self) -> bool { if !self.alive.load(Ordering::Acquire) || !other.alive.load(Ordering::Acquire) { return false; } let my_client_ptr = unsafe { ffi_dispatch!(wayland_server_handle(), wl_resource_get_client, self.ptr) }; let other_client_ptr = unsafe { ffi_dispatch!(wayland_server_handle(), wl_resource_get_client, other.ptr) }; my_client_ptr == other_client_ptr } pub fn protocol_id(&self) -> u32 { self.id } pub unsafe fn from_ptr( interface: Option<&'static Interface>, ptr: *mut wl_resource, ) -> Result { let id = ffi_dispatch!(wayland_server_handle(), wl_resource_get_id, ptr); // This is a horrible back to work around the fact that it is not possible to check if the // resource implementation is RUST_MANAGED without knowing the interface of that object... let is_rust_managed = { // wl_resource_instance_of uses wl_interface_equals, which only checks that the interface name // is correct, so we create a temporary dummy wl_interface with the correct name so that the // check only actually verifies that the object is RUST_MANAGED let iface_name = ffi_dispatch!(wayland_server_handle(), wl_resource_get_class, ptr); let dummy_iface = wl_interface { name: iface_name, version: 0, request_count: 0, event_count: 0, requests: std::ptr::null(), events: std::ptr::null(), }; ffi_dispatch!( wayland_server_handle(), wl_resource_instance_of, ptr, &dummy_iface, &RUST_MANAGED as *const u8 as *const _ ) != 0 }; if is_rust_managed { // Using () instead of the type parameter here is safe, because: // 1) ResourceUserData is #[repr(C)], so its layout does not depend on D // 2) we are only accessing the field `.alive`, which type is independent of D // let udata = ffi_dispatch!(wayland_server_handle(), wl_resource_get_user_data, ptr) as *mut ResourceUserData<()>; let alive = unsafe { (*udata).alive.clone() }; let udata_iface = unsafe { (*udata).interface }; if let Some(iface) = interface { if !same_interface(iface, udata_iface) { return Err(InvalidId); } } Ok(InnerObjectId { id, ptr, alive, interface: udata_iface }) } else if let Some(interface) = interface { let iface_c_ptr = interface.c_ptr.expect("[wayland-backend-sys] Cannot use Interface without c_ptr!"); // Safety: the provided pointer must be a valid wayland object let ptr_iface_name = unsafe { CStr::from_ptr(ffi_dispatch!(wayland_server_handle(), wl_resource_get_class, ptr)) }; // Safety: the code generated by wayland-scanner is valid let provided_iface_name = unsafe { CStr::from_ptr(iface_c_ptr.name) }; if ptr_iface_name != provided_iface_name { return Err(InvalidId); } // On external resource, we use the wl_resource_add_destroy_listener mechanism to track their liveness let listener = ffi_dispatch!( wayland_server_handle(), wl_resource_get_destroy_listener, ptr, external_resource_destroy_notify ); let alive = if listener.is_null() { // The listener needs to be initialized let alive = Arc::new(AtomicBool::new(true)); let data = Box::into_raw(Box::new(ExternalResourceData { alive: alive.clone() })); let listener = signal::rust_listener_create(external_resource_destroy_notify); // Safety: we just created listener and client_data, they are valid unsafe { signal::rust_listener_set_user_data(listener, data as *mut c_void); } ffi_dispatch!( wayland_server_handle(), wl_resource_add_destroy_listener, ptr, listener ); alive } else { unsafe { let data = signal::rust_listener_get_user_data(listener) as *mut ExternalResourceData; (*data).alive.clone() } }; Ok(InnerObjectId { id, ptr, alive, interface }) } else { Err(InvalidId) } } pub fn as_ptr(&self) -> *mut wl_resource { if self.alive.load(Ordering::Acquire) { self.ptr } else { std::ptr::null_mut() } } } struct ExternalResourceData { alive: Arc, } unsafe extern "C" fn external_resource_destroy_notify(listener: *mut wl_listener, _: *mut c_void) { // Safety: if this function is invoked by libwayland its arguments must be valid let data = unsafe { Box::from_raw(signal::rust_listener_get_user_data(listener) as *mut ExternalResourceData) }; unsafe { signal::rust_listener_destroy(listener); } data.alive.store(false, Ordering::Release); } /// An id of a client connected to the server. #[derive(Debug, Clone)] pub struct InnerClientId { ptr: *mut wl_client, alive: Arc, } unsafe impl Send for InnerClientId {} unsafe impl Sync for InnerClientId {} impl std::cmp::PartialEq for InnerClientId { fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.alive, &other.alive) } } impl std::cmp::Eq for InnerClientId {} impl std::hash::Hash for InnerClientId { fn hash(&self, state: &mut H) { (&*self.alive as *const AtomicBool).hash(state) } } /// The ID of a global #[derive(Debug, Clone)] pub struct InnerGlobalId { ptr: *mut wl_global, alive: Arc, } unsafe impl Send for InnerGlobalId {} unsafe impl Sync for InnerGlobalId {} impl std::cmp::PartialEq for InnerGlobalId { fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.alive, &other.alive) } } impl std::cmp::Eq for InnerGlobalId {} impl std::hash::Hash for InnerGlobalId { fn hash(&self, state: &mut H) { (&*self.alive as *const AtomicBool).hash(state) } } #[repr(C)] struct ResourceUserData { alive: Arc, data: Arc>, interface: &'static Interface, } struct ClientUserData { data: Arc, alive: Arc, } struct GlobalUserData { handler: Arc>, interface: &'static Interface, version: u32, disabled: bool, alive: Arc, ptr: *mut wl_global, } #[derive(Debug)] pub struct State { display: *mut wl_display, pending_destructors: Vec>, timer_source: *mut wl_event_source, _data: std::marker::PhantomData, known_globals: Vec, } unsafe impl Send for State {} #[derive(Debug)] pub struct InnerBackend { state: Arc>>, display_ptr: *mut wl_display, } unsafe impl Send for InnerBackend {} unsafe impl Sync for InnerBackend {} impl InnerBackend { pub fn new() -> Result { if !is_lib_available() { return Err(InitError::NoWaylandLib); } let display = unsafe { ffi_dispatch!(wayland_server_handle(), wl_display_create) }; if display.is_null() { panic!("[wayland-backend-sys] libwayland reported an allocation failure."); } #[cfg(feature = "log")] unsafe { ffi_dispatch!( wayland_server_handle(), wl_log_set_handler_server, wl_log_trampoline_to_rust_server ) }; unsafe { ffi_dispatch!( wayland_server_handle(), wl_display_set_global_filter, display, global_filter::, std::ptr::null_mut() ); } // Insert a timer we can use to force wakeups of the libwayland inner event loop extern "C" fn timer_noop_cb(_: *mut c_void) -> i32 { 0 } let timer_source = unsafe { let evl = ffi_dispatch!(wayland_server_handle(), wl_display_get_event_loop, display); ffi_dispatch!( wayland_server_handle(), wl_event_loop_add_timer, evl, timer_noop_cb, std::ptr::null_mut() ) }; Ok(Self { state: Arc::new(Mutex::new(State { display, pending_destructors: Vec::new(), timer_source, _data: std::marker::PhantomData, known_globals: Vec::new(), })), display_ptr: display, }) } pub fn flush(&mut self, client: Option) -> std::io::Result<()> { self.state.lock().unwrap().flush(client) } pub fn handle(&self) -> Handle { Handle { handle: InnerHandle { state: self.state.clone() as Arc<_> } } } pub fn poll_fd(&self) -> BorrowedFd { unsafe { let evl_ptr = ffi_dispatch!(wayland_server_handle(), wl_display_get_event_loop, self.display_ptr); BorrowedFd::borrow_raw(ffi_dispatch!( wayland_server_handle(), wl_event_loop_get_fd, evl_ptr )) } } pub fn dispatch_client( &mut self, data: &mut D, _client_id: InnerClientId, ) -> std::io::Result { self.dispatch_all_clients(data) } pub fn dispatch_all_clients(&mut self, data: &mut D) -> std::io::Result { let state = self.state.clone() as Arc>; let display = self.display_ptr; let ret = HANDLE.set(&(state, data as *mut _ as *mut c_void), || unsafe { let evl_ptr = ffi_dispatch!(wayland_server_handle(), wl_display_get_event_loop, display); ffi_dispatch!(wayland_server_handle(), wl_event_loop_dispatch, evl_ptr, 0) }); let pending_destructors = std::mem::take(&mut self.state.lock().unwrap().pending_destructors); for (object, client_id, object_id) in pending_destructors { let handle = self.handle(); object.clone().destroyed(&handle, data, client_id, object_id); } if ret < 0 { Err(std::io::Error::last_os_error()) } else { Ok(ret as usize) } } } impl Drop for State { fn drop(&mut self) { // wl_display_destroy_clients may result in the destruction of some wayland objects. Pending // destructors are queued up inside the PENDING_DESTRUCTORS scoped global. We need to set the scoped // global in order for destructors to be queued up properly. PENDING_DESTRUCTORS.set(&(&mut self.pending_destructors as *mut _ as *mut _), || unsafe { ffi_dispatch!(wayland_server_handle(), wl_display_destroy_clients, self.display); }); let known_globals = std::mem::take(&mut self.known_globals); for global in known_globals { unsafe { let _ = Box::from_raw(ffi_dispatch!( wayland_server_handle(), wl_global_get_user_data, global.ptr ) as *mut GlobalUserData); ffi_dispatch!(wayland_server_handle(), wl_global_destroy, global.ptr); } } unsafe { ffi_dispatch!(wayland_server_handle(), wl_display_destroy, self.display); } } } #[derive(Clone)] pub struct InnerHandle { state: Arc>, } impl std::fmt::Debug for InnerHandle { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fmt.debug_struct("InnerHandle[sys]").finish_non_exhaustive() } } #[derive(Clone)] pub struct WeakInnerHandle { pub(crate) state: Weak>, } impl std::fmt::Debug for WeakInnerHandle { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fmt.debug_struct("WeakInnerHandle[sys]").finish_non_exhaustive() } } impl WeakInnerHandle { pub fn upgrade(&self) -> Option { self.state.upgrade().map(|state| InnerHandle { state }) } } impl InnerHandle { pub fn downgrade(&self) -> WeakInnerHandle { WeakInnerHandle { state: Arc::downgrade(&self.state) } } pub fn object_info(&self, id: InnerObjectId) -> Result { self.state.lock().unwrap().object_info(id) } pub fn insert_client( &self, stream: UnixStream, data: Arc, ) -> std::io::Result { self.state.lock().unwrap().insert_client(stream, data) } pub fn get_client(&self, id: InnerObjectId) -> Result { self.state.lock().unwrap().get_client(id) } pub fn get_client_data(&self, id: InnerClientId) -> Result, InvalidId> { self.state.lock().unwrap().get_client_data(id) } pub fn get_client_credentials(&self, id: InnerClientId) -> Result { self.state.lock().unwrap().get_client_credentials(id) } pub fn with_all_clients(&self, mut f: impl FnMut(ClientId)) { self.state.lock().unwrap().with_all_clients(&mut f) } pub fn with_all_objects_for( &self, client_id: InnerClientId, mut f: impl FnMut(ObjectId), ) -> Result<(), InvalidId> { self.state.lock().unwrap().with_all_objects_for(client_id, &mut f) } pub fn object_for_protocol_id( &self, client_id: InnerClientId, interface: &'static Interface, protocol_id: u32, ) -> Result { self.state.lock().unwrap().object_for_protocol_id(client_id, interface, protocol_id) } pub fn create_object( &self, client: InnerClientId, interface: &'static Interface, version: u32, data: Arc>, ) -> Result { let mut state = self.state.lock().unwrap(); // Keep this guard alive while the code is run to protect the C state let _state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::create_object()."); if !client.alive.load(Ordering::Acquire) { return Err(InvalidId); } let interface_ptr = interface.c_ptr.expect("Interface without c_ptr are unsupported by the sys backend."); let resource = unsafe { ffi_dispatch!( wayland_server_handle(), wl_resource_create, client.ptr, interface_ptr, version as i32, 0 ) }; Ok(ObjectId { id: unsafe { init_resource(resource, interface, Some(data)).0 } }) } pub fn null_id() -> ObjectId { ObjectId { id: InnerObjectId { ptr: std::ptr::null_mut(), id: 0, alive: Arc::new(AtomicBool::new(false)), interface: &ANONYMOUS_INTERFACE, }, } } pub fn send_event(&self, msg: Message) -> Result<(), InvalidId> { self.state.lock().unwrap().send_event(msg) } pub fn get_object_data( &self, id: InnerObjectId, ) -> Result>, InvalidId> { let mut state = self.state.lock().unwrap(); // Keep this guard alive while the code is run to protect the C state let _state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::get_object_data()."); if !id.alive.load(Ordering::Acquire) { return Err(InvalidId); } let iface_c_ptr = id.interface.c_ptr.expect("[wayland-backend-sys] Cannot use Interface without c_ptr!"); let is_managed = unsafe { ffi_dispatch!( wayland_server_handle(), wl_resource_instance_of, id.ptr, iface_c_ptr, &RUST_MANAGED as *const u8 as *const _ ) != 0 }; if !is_managed { return Err(InvalidId); } let udata = unsafe { &*(ffi_dispatch!(wayland_server_handle(), wl_resource_get_user_data, id.ptr) as *mut ResourceUserData) }; Ok(udata.data.clone()) } pub fn get_object_data_any( &self, id: InnerObjectId, ) -> Result, InvalidId> { self.state.lock().unwrap().get_object_data_any(id) } pub fn set_object_data( &self, id: InnerObjectId, data: Arc>, ) -> Result<(), InvalidId> { let mut state = self.state.lock().unwrap(); // Keep this guard alive while the code is run to protect the C state let _state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::set_object_data()."); if !id.alive.load(Ordering::Acquire) { return Err(InvalidId); } let iface_c_ptr = id.interface.c_ptr.expect("[wayland-backend-sys] Cannot use Interface without c_ptr!"); let is_managed = unsafe { ffi_dispatch!( wayland_server_handle(), wl_resource_instance_of, id.ptr, iface_c_ptr, &RUST_MANAGED as *const u8 as *const _ ) != 0 }; if !is_managed { return Err(InvalidId); } let udata = unsafe { &mut *(ffi_dispatch!(wayland_server_handle(), wl_resource_get_user_data, id.ptr) as *mut ResourceUserData) }; udata.data = data; Ok(()) } pub fn post_error(&self, object_id: InnerObjectId, error_code: u32, message: CString) { self.state.lock().unwrap().post_error(object_id, error_code, message) } pub fn kill_client(&self, client_id: InnerClientId, reason: DisconnectReason) { self.state.lock().unwrap().kill_client(client_id, reason) } pub fn create_global( &self, interface: &'static Interface, version: u32, handler: Arc>, ) -> InnerGlobalId { let display = { let mut state = self.state.lock().unwrap(); let state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::create_global()."); state.display }; let alive = Arc::new(AtomicBool::new(true)); let interface_ptr = interface.c_ptr.expect("Interface without c_ptr are unsupported by the sys backend."); let udata = Box::into_raw(Box::new(GlobalUserData { handler, alive: alive.clone(), interface, version, disabled: false, ptr: std::ptr::null_mut(), })); let ret = HANDLE.set(&(self.state.clone(), std::ptr::null_mut()), || unsafe { ffi_dispatch!( wayland_server_handle(), wl_global_create, display, interface_ptr, version as i32, udata as *mut c_void, global_bind:: ) }); if ret.is_null() { // free the user data as global creation failed let _ = unsafe { Box::from_raw(udata) }; panic!( "[wayland-backend-sys] Invalid global specification or memory allocation failure." ); } unsafe { (*udata).ptr = ret; } let mut state = self.state.lock().unwrap(); let state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::create_global()."); let id = InnerGlobalId { ptr: ret, alive }; state.known_globals.push(id.clone()); id } pub fn disable_global(&self, id: InnerGlobalId) { // check that `D` is correct { let mut state = self.state.lock().unwrap(); let _state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::disable_global()."); } if !id.alive.load(Ordering::Acquire) { return; } let udata = unsafe { &mut *(ffi_dispatch!(wayland_server_handle(), wl_global_get_user_data, id.ptr) as *mut GlobalUserData) }; // libwayland will abort if wl_global_remove is called more than once. // This means we do nothing if the global is already disabled if !udata.disabled { udata.disabled = true; // send the global_remove HANDLE.set(&(self.state.clone(), std::ptr::null_mut()), || unsafe { ffi_dispatch!(wayland_server_handle(), wl_global_remove, id.ptr); }); } } pub fn remove_global(&self, id: InnerGlobalId) { { let mut state = self.state.lock().unwrap(); let state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::remove_global()."); state.known_globals.retain(|g| g != &id); } if !id.alive.load(Ordering::Acquire) { return; } let udata = unsafe { Box::from_raw(ffi_dispatch!(wayland_server_handle(), wl_global_get_user_data, id.ptr) as *mut GlobalUserData) }; udata.alive.store(false, Ordering::Release); HANDLE.set(&(self.state.clone(), std::ptr::null_mut()), || unsafe { ffi_dispatch!(wayland_server_handle(), wl_global_destroy, id.ptr); }); } pub fn global_info(&self, id: InnerGlobalId) -> Result { self.state.lock().unwrap().global_info(id) } /// Returns the handler which manages the visibility and notifies when a client has bound the global. pub fn get_global_handler( &self, id: InnerGlobalId, ) -> Result>, InvalidId> { let mut state = self.state.lock().unwrap(); // Keep this guard alive while the code is run to protect the C state let _state = (&mut *state as &mut dyn ErasedState) .downcast_mut::>() .expect("Wrong type parameter passed to Handle::set_object_data()."); if !id.alive.load(Ordering::Acquire) { return Err(InvalidId); } let udata = unsafe { Box::from_raw(ffi_dispatch!(wayland_server_handle(), wl_global_get_user_data, id.ptr) as *mut GlobalUserData) }; Ok(udata.handler.clone()) } pub fn flush(&mut self, client: Option) -> std::io::Result<()> { self.state.lock().unwrap().flush(client) } pub fn display_ptr(&self) -> *mut wl_display { self.state.lock().unwrap().display_ptr() } } pub(crate) trait ErasedState: downcast_rs::Downcast { fn object_info(&self, id: InnerObjectId) -> Result; fn insert_client( &self, stream: UnixStream, data: Arc, ) -> std::io::Result; fn get_client(&self, id: InnerObjectId) -> Result; fn get_client_credentials(&self, id: InnerClientId) -> Result; fn get_client_data(&self, id: InnerClientId) -> Result, InvalidId>; fn with_all_clients(&self, f: &mut dyn FnMut(ClientId)); fn with_all_objects_for( &self, client_id: InnerClientId, f: &mut dyn FnMut(ObjectId), ) -> Result<(), InvalidId>; fn object_for_protocol_id( &self, client_id: InnerClientId, interface: &'static Interface, protocol_id: u32, ) -> Result; fn get_object_data_any( &self, id: InnerObjectId, ) -> Result, InvalidId>; fn send_event(&mut self, msg: Message) -> Result<(), InvalidId>; fn post_error(&mut self, object_id: InnerObjectId, error_code: u32, message: CString); fn kill_client(&mut self, client_id: InnerClientId, reason: DisconnectReason); fn global_info(&self, id: InnerGlobalId) -> Result; fn is_known_global(&self, global_ptr: *const wl_global) -> bool; fn flush(&mut self, client: Option) -> std::io::Result<()>; fn display_ptr(&self) -> *mut wl_display; } downcast_rs::impl_downcast!(ErasedState); impl ErasedState for State { fn object_info(&self, id: InnerObjectId) -> Result { if !id.alive.load(Ordering::Acquire) { return Err(InvalidId); } let version = unsafe { ffi_dispatch!(wayland_server_handle(), wl_resource_get_version, id.ptr) } as u32; Ok(ObjectInfo { id: id.id, version, interface: id.interface }) } fn insert_client( &self, stream: UnixStream, data: Arc, ) -> std::io::Result { let ret = unsafe { ffi_dispatch!( wayland_server_handle(), wl_client_create, self.display, stream.into_raw_fd() ) }; if ret.is_null() { return Err(std::io::Error::last_os_error()); } Ok(unsafe { init_client(ret, data) }) } fn get_client(&self, id: InnerObjectId) -> Result { if !id.alive.load(Ordering::Acquire) { return Err(InvalidId); } unsafe { let client_ptr = ffi_dispatch!(wayland_server_handle(), wl_resource_get_client, id.ptr); client_id_from_ptr(client_ptr).ok_or(InvalidId).map(|id| ClientId { id }) } } fn get_client_data(&self, id: InnerClientId) -> Result, InvalidId> { if !id.alive.load(Ordering::Acquire) { return Err(InvalidId); } let data = unsafe { match client_user_data(id.ptr) { Some(ptr) => &mut *ptr, None => return Err(InvalidId), } }; Ok(data.data.clone()) } fn get_client_credentials(&self, id: InnerClientId) -> Result { if !id.alive.load(Ordering::Acquire) { return Err(InvalidId); } let mut creds = Credentials { pid: 0, uid: 0, gid: 0 }; unsafe { ffi_dispatch!( wayland_server_handle(), wl_client_get_credentials, id.ptr, &mut creds.pid, &mut creds.uid, &mut creds.gid ); } Ok(creds) } fn with_all_clients(&self, f: &mut dyn FnMut(ClientId)) { let mut client_list = unsafe { ffi_dispatch!(wayland_server_handle(), wl_display_get_client_list, self.display) }; unsafe { while (*client_list).next != client_list { let client = ffi_dispatch!(wayland_server_handle(), wl_client_from_link, client_list); if let Some(id) = client_id_from_ptr(client) { f(ClientId { id }) } client_list = (*client_list).next; } } } fn with_all_objects_for( &self, client_id: InnerClientId, mut f: &mut dyn FnMut(ObjectId), ) -> Result<(), InvalidId> { if !client_id.alive.load(Ordering::Acquire) { return Err(InvalidId); } unsafe extern "C" fn iterator_func( resource: *mut wl_resource, user_data: *mut c_void, ) -> c_int { // the closure is passed by double-reference because we only have 1 pointer of user data let closure = unsafe { &mut *(user_data as *mut &mut dyn FnMut(ObjectId)) }; // Only process objects that are RUST_MANAGED (we cannot guess the Interface for the others, // and thus cannot create the ObjectId). if let Ok(id) = unsafe { InnerObjectId::from_ptr(None, resource) } { closure(ObjectId { id }) } // return WL_ITERATOR_CONTINUE 1 } unsafe { ffi_dispatch!( wayland_server_handle(), wl_client_for_each_resource, client_id.ptr, iterator_func, &mut f as *mut _ as *mut c_void, ) } Ok(()) } fn object_for_protocol_id( &self, client_id: InnerClientId, interface: &'static Interface, protocol_id: u32, ) -> Result { if !client_id.alive.load(Ordering::Acquire) { return Err(InvalidId); } let resource = unsafe { ffi_dispatch!(wayland_server_handle(), wl_client_get_object, client_id.ptr, protocol_id) }; if resource.is_null() { Err(InvalidId) } else { unsafe { ObjectId::from_ptr(interface, resource) } } } fn get_object_data_any( &self, id: InnerObjectId, ) -> Result, InvalidId> { if !id.alive.load(Ordering::Acquire) { return Err(InvalidId); } let iface_c_ptr = id.interface.c_ptr.expect("[wayland-backend-sys] Cannot use Interface without c_ptr!"); let is_managed = unsafe { ffi_dispatch!( wayland_server_handle(), wl_resource_instance_of, id.ptr, iface_c_ptr, &RUST_MANAGED as *const u8 as *const _ ) != 0 }; if !is_managed { return Err(InvalidId); } let udata = unsafe { &*(ffi_dispatch!(wayland_server_handle(), wl_resource_get_user_data, id.ptr) as *mut ResourceUserData) }; Ok(udata.data.clone().into_any_arc()) } fn send_event( &mut self, Message { sender_id: ObjectId { id }, opcode, args }: Message, ) -> Result<(), InvalidId> { if !id.alive.load(Ordering::Acquire) || id.ptr.is_null() { return Err(InvalidId); } // check that the argument list is valid let message_desc = match id.interface.events.get(opcode as usize) { Some(msg) => msg, None => { panic!("Unknown opcode {} for object {}@{}.", opcode, id.interface.name, id.id); } }; if !check_for_signature(message_desc.signature, &args) { panic!( "Unexpected signature for request {}@{}.{}: expected {:?}, got {:?}.", id.interface.name, id.id, message_desc.name, message_desc.signature, args ); } let mut argument_list = SmallVec::<[wl_argument; 4]>::with_capacity(args.len()); let mut arg_interfaces = message_desc.arg_interfaces.iter(); for (i, arg) in args.iter().enumerate() { match *arg { Argument::Uint(u) => argument_list.push(wl_argument { u }), Argument::Int(i) => argument_list.push(wl_argument { i }), Argument::Fixed(f) => argument_list.push(wl_argument { f }), Argument::Fd(h) => argument_list.push(wl_argument { h }), Argument::Array(ref a) => { let a = Box::new(wl_array { size: a.len(), alloc: a.len(), data: a.as_ptr() as *mut _, }); argument_list.push(wl_argument { a: Box::into_raw(a) }) } Argument::Str(Some(ref s)) => argument_list.push(wl_argument { s: s.as_ptr() }), Argument::Str(None) => argument_list.push(wl_argument { s: std::ptr::null() }), Argument::Object(ref o) => { let next_interface = arg_interfaces.next().unwrap(); if !o.id.ptr.is_null() { if !o.id.alive.load(Ordering::Acquire) { unsafe { free_arrays(message_desc.signature, &argument_list) }; return Err(InvalidId); } // check that the object belongs to the right client if self.get_client(id.clone()).unwrap().id.ptr != self.get_client(o.id.clone()).unwrap().id.ptr { panic!("Attempting to send an event with objects from wrong client."); } if !same_interface(next_interface, o.id.interface) { panic!("Event {}@{}.{} expects an argument of interface {} but {} was provided instead.", id.interface.name, id.id, message_desc.name, next_interface.name, o.id.interface.name); } } else if !matches!( message_desc.signature[i], ArgumentType::Object(AllowNull::Yes) ) { panic!( "Event {}@{}.{} expects an non-null object argument.", id.interface.name, id.id, message_desc.name ); } argument_list.push(wl_argument { o: o.id.ptr as *const _ }) } Argument::NewId(ref o) => { if !o.id.ptr.is_null() { if !id.alive.load(Ordering::Acquire) { unsafe { free_arrays(message_desc.signature, &argument_list) }; return Err(InvalidId); } // check that the object belongs to the right client if self.get_client(id.clone()).unwrap().id.ptr != self.get_client(o.id.clone()).unwrap().id.ptr { panic!("Attempting to send an event with objects from wrong client."); } let child_interface = match message_desc.child_interface { Some(iface) => iface, None => panic!("Trying to send event {}@{}.{} which creates an object without specifying its interface, this is unsupported.", id.interface.name, id.id, message_desc.name), }; if !same_interface(child_interface, o.id.interface) { panic!("Event {}@{}.{} expects an argument of interface {} but {} was provided instead.", id.interface.name, id.id, message_desc.name, child_interface.name, o.id.interface.name); } } else if !matches!(message_desc.signature[i], ArgumentType::NewId) { panic!( "Event {}@{}.{} expects an non-null object argument.", id.interface.name, id.id, message_desc.name ); } argument_list.push(wl_argument { o: o.id.ptr as *const _ }) } } } unsafe { ffi_dispatch!( wayland_server_handle(), wl_resource_post_event_array, id.ptr, opcode as u32, argument_list.as_mut_ptr() ); } unsafe { free_arrays(message_desc.signature, &argument_list); } if message_desc.is_destructor { // wl_resource_destroy invokes a destructor PENDING_DESTRUCTORS.set( &(&mut self.pending_destructors as *mut _ as *mut _), || unsafe { ffi_dispatch!(wayland_server_handle(), wl_resource_destroy, id.ptr); }, ); } Ok(()) } fn post_error(&mut self, id: InnerObjectId, error_code: u32, message: CString) { if !id.alive.load(Ordering::Acquire) { return; } // Safety: at this point we already checked that the pointer is valid let client = unsafe { ffi_dispatch!(wayland_server_handle(), wl_resource_get_client, id.ptr) }; let client_id = unsafe { client_id_from_ptr(client) }.unwrap(); // mark the client as dead client_id.alive.store(false, Ordering::Release); unsafe { ffi_dispatch!( wayland_server_handle(), wl_resource_post_error, id.ptr, error_code, message.as_ptr() ) } } fn kill_client(&mut self, client_id: InnerClientId, reason: DisconnectReason) { if !client_id.alive.load(Ordering::Acquire) { return; } if let Some(udata) = unsafe { client_user_data(client_id.ptr) } { let udata = unsafe { &*udata }; udata.alive.store(false, Ordering::Release); udata.data.disconnected(ClientId { id: client_id.clone() }, reason); } // wl_client_destroy invokes destructors PENDING_DESTRUCTORS.set(&(&mut self.pending_destructors as *mut _ as *mut _), || unsafe { ffi_dispatch!(wayland_server_handle(), wl_client_destroy, client_id.ptr); }); } fn global_info(&self, id: InnerGlobalId) -> Result { if !id.alive.load(Ordering::Acquire) { return Err(InvalidId); } let udata = unsafe { &*(ffi_dispatch!(wayland_server_handle(), wl_global_get_user_data, id.ptr) as *mut GlobalUserData) }; Ok(GlobalInfo { interface: udata.interface, version: udata.version, disabled: udata.disabled, }) } fn is_known_global(&self, global_ptr: *const wl_global) -> bool { self.known_globals.iter().any(|ginfo| (ginfo.ptr as *const wl_global) == global_ptr) } fn flush(&mut self, client: Option) -> std::io::Result<()> { if let Some(ClientId { id: client_id }) = client { if client_id.alive.load(Ordering::Acquire) { unsafe { ffi_dispatch!(wayland_server_handle(), wl_client_flush, client_id.ptr) } } } else { // wl_display_flush_clients might invoke destructors PENDING_DESTRUCTORS.set( &(&mut self.pending_destructors as *mut _ as *mut _), || unsafe { ffi_dispatch!(wayland_server_handle(), wl_display_flush_clients, self.display); }, ); } if !self.pending_destructors.is_empty() { // Arm the timer to trigger a wakeup of the inner event loop in 1ms, so that the user // is indicated to call dispatch_clients() and have the destructors run unsafe { ffi_dispatch!( wayland_server_handle(), wl_event_source_timer_update, self.timer_source, 1 ) }; } Ok(()) } fn display_ptr(&self) -> *mut wl_display { self.display } } unsafe fn init_client(client: *mut wl_client, data: Arc) -> InnerClientId { let alive = Arc::new(AtomicBool::new(true)); let client_data = Box::into_raw(Box::new(ClientUserData { alive: alive.clone(), data })); let listener = signal::rust_listener_create(client_destroy_notify); // Safety: we just created listener and client_data, they are valid unsafe { signal::rust_listener_set_user_data(listener, client_data as *mut c_void); } ffi_dispatch!(wayland_server_handle(), wl_client_add_destroy_listener, client, listener); InnerClientId { ptr: client, alive } } unsafe fn client_id_from_ptr(client: *mut wl_client) -> Option { // Safety: the provided pointer is a valid and initialized wl_client for type parameter D unsafe { client_user_data(client) .map(|udata| InnerClientId { ptr: client, alive: (*udata).alive.clone() }) } } unsafe fn client_user_data(client: *mut wl_client) -> Option<*mut ClientUserData> { if client.is_null() { return None; } let listener = ffi_dispatch!( wayland_server_handle(), wl_client_get_destroy_listener, client, client_destroy_notify ); if !listener.is_null() { // Safety: the pointer we got must be valid if the client is still alive unsafe { Some(signal::rust_listener_get_user_data(listener) as *mut ClientUserData) } } else { None } } unsafe extern "C" fn client_destroy_notify(listener: *mut wl_listener, client_ptr: *mut c_void) { // Safety: if this function is invoked by libwayland its arguments must be valid let data = unsafe { Box::from_raw(signal::rust_listener_get_user_data(listener) as *mut ClientUserData) }; unsafe { signal::rust_listener_destroy(listener); } // only notify the killing if it was not already if data.alive.load(Ordering::Acquire) { data.alive.store(false, Ordering::Release); data.data.disconnected( ClientId { id: InnerClientId { ptr: client_ptr as *mut wl_client, alive: data.alive.clone() }, }, DisconnectReason::ConnectionClosed, ); } } unsafe extern "C" fn global_bind( client: *mut wl_client, data: *mut c_void, version: u32, id: u32, ) { // Safety: when this function is invoked, the data pointer provided by libwayland is the data we previously put there let global_udata = unsafe { &mut *(data as *mut GlobalUserData) }; let global_id = InnerGlobalId { alive: global_udata.alive.clone(), ptr: global_udata.ptr }; // Safety: libwayland invoked us with a valid wl_client let client_id = match unsafe { client_id_from_ptr(client) } { Some(id) => id, None => return, }; // this must be Some(), checked at creation of the global let interface_ptr = global_udata.interface.c_ptr.unwrap(); HANDLE.with(|&(ref state_arc, data_ptr)| { // Safety: the data_ptr is a valid pointer that live outside code put there let data = unsafe { &mut *(data_ptr as *mut D) }; // create the object let resource = ffi_dispatch!( wayland_server_handle(), wl_resource_create, client, interface_ptr, version as i32, id ); // Safety: resource was just created, it must be valid let (object_id, udata) = unsafe { init_resource(resource, global_udata.interface, None) }; let obj_data = global_udata.handler.clone().bind( &Handle { handle: InnerHandle { state: state_arc.clone() } }, data, ClientId { id: client_id }, GlobalId { id: global_id }, ObjectId { id: object_id }, ); // Safety: udata was just created, it is valid unsafe { (*udata).data = obj_data }; }) } unsafe extern "C" fn global_filter( client: *const wl_client, global: *const wl_global, _: *mut c_void, ) -> bool { if !HANDLE.is_set() { // This happens if we are invoked during the global shutdown // at this point, we really don't care what we send to clients, // the compositor is shutting down anyway so they're all going to be killed // thus return false, so that nothing is sent to anybody return false; } // Safety: skip processing globals that do not belong to us let is_known_global = HANDLE.with(|(state_arc, _)| { let guard = state_arc.lock().unwrap(); guard.is_known_global(global) }); if !is_known_global { return true; } // Safety: if we are invoked here, the client is a valid client initialized by us let client_udata = match unsafe { client_user_data(client as *mut _) } { Some(id) => unsafe { &*id }, None => return false, }; let client_id = InnerClientId { ptr: client as *mut _, alive: client_udata.alive.clone() }; // Safety: if we are invoked here, the global is a global client initialized by us let global_udata = unsafe { &*(ffi_dispatch!(wayland_server_handle(), wl_global_get_user_data, global) as *mut GlobalUserData) }; let global_id = InnerGlobalId { ptr: global as *mut wl_global, alive: global_udata.alive.clone() }; global_udata.handler.can_view( ClientId { id: client_id }, &client_udata.data, GlobalId { id: global_id }, ) } unsafe fn init_resource( resource: *mut wl_resource, interface: &'static Interface, data: Option>>, ) -> (InnerObjectId, *mut ResourceUserData) { let alive = Arc::new(AtomicBool::new(true)); let udata = Box::into_raw(Box::new(ResourceUserData { data: data.unwrap_or_else(|| Arc::new(UninitObjectData)), interface, alive: alive.clone(), })); let id = ffi_dispatch!(wayland_server_handle(), wl_resource_get_id, resource); ffi_dispatch!( wayland_server_handle(), wl_resource_set_dispatcher, resource, resource_dispatcher::, &RUST_MANAGED as *const u8 as *const c_void, udata as *mut c_void, Some(resource_destructor::) ); (InnerObjectId { interface, alive, id, ptr: resource }, udata) } unsafe extern "C" fn resource_dispatcher( _: *const c_void, resource: *mut c_void, opcode: u32, _: *const wl_message, args: *const wl_argument, ) -> i32 { let resource = resource as *mut wl_resource; let udata_ptr = ffi_dispatch!(wayland_server_handle(), wl_resource_get_user_data, resource) as *mut ResourceUserData; // Safety: if we are invoked, the resource is valid and rust-managed, so its user data is valid let udata = unsafe { &mut *udata_ptr }; let client = ffi_dispatch!(wayland_server_handle(), wl_resource_get_client, resource); let resource_id = ffi_dispatch!(wayland_server_handle(), wl_resource_get_id, resource); let version = ffi_dispatch!(wayland_server_handle(), wl_resource_get_version, resource); let interface = udata.interface; let message_desc = match interface.requests.get(opcode as usize) { Some(desc) => desc, None => { crate::log_error!("Unknown event opcode {} for interface {}.", opcode, interface.name); return -1; } }; let mut parsed_args = SmallVec::<[Argument; 4]>::with_capacity(message_desc.signature.len()); let mut arg_interfaces = message_desc.arg_interfaces.iter().copied(); let mut created = None; // Safety (args deference): the args array provided by libwayland is well-formed for (i, typ) in message_desc.signature.iter().enumerate() { match typ { ArgumentType::Uint => parsed_args.push(Argument::Uint(unsafe { (*args.add(i)).u })), ArgumentType::Int => parsed_args.push(Argument::Int(unsafe { (*args.add(i)).i })), ArgumentType::Fixed => parsed_args.push(Argument::Fixed(unsafe { (*args.add(i)).f })), ArgumentType::Fd => { parsed_args.push(Argument::Fd(unsafe { OwnedFd::from_raw_fd((*args.add(i)).h) })) } ArgumentType::Array => { let array = unsafe { &*((*args.add(i)).a) }; // Safety: the wl_array provided by libwayland is valid let content = unsafe { std::slice::from_raw_parts(array.data as *mut u8, array.size) }; parsed_args.push(Argument::Array(Box::new(content.into()))); } ArgumentType::Str(_) => { let ptr = unsafe { (*args.add(i)).s }; // Safety: the c-string provided by libwayland is valid if !ptr.is_null() { let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) }; parsed_args.push(Argument::Str(Some(Box::new(cstr.into())))); } else { parsed_args.push(Argument::Str(None)); } } ArgumentType::Object(_) => { let obj = unsafe { (*args.add(i)).o as *mut wl_resource }; let next_interface = arg_interfaces.next().unwrap_or(&ANONYMOUS_INTERFACE); let id = if !obj.is_null() { ObjectId { id: unsafe { InnerObjectId::from_ptr(Some(next_interface), obj) } .expect("Received an invalid ID when parsing a message."), } } else { // libwayland-server.so checks nulls for us InnerHandle::null_id() }; parsed_args.push(Argument::Object(id)) } ArgumentType::NewId => { let new_id = unsafe { (*args.add(i)).n }; // create the object if new_id != 0 { let child_interface = match message_desc.child_interface { Some(iface) => iface, None => panic!("Received request {}@{}.{} which creates an object without specifying its interface, this is unsupported.", udata.interface.name, resource_id, message_desc.name), }; // create the object let resource = ffi_dispatch!( wayland_server_handle(), wl_resource_create, client, child_interface.c_ptr.unwrap(), version, new_id ); // Safety: the resource has just been created and is valid let (child_id, child_data_ptr) = unsafe { init_resource::(resource, child_interface, None) }; created = Some((child_id.clone(), child_data_ptr)); parsed_args.push(Argument::NewId(ObjectId { id: child_id })); } else { parsed_args.push(Argument::NewId(InnerHandle::null_id())) } } } } let object_id = ObjectId { id: InnerObjectId { ptr: resource, id: resource_id, interface: udata.interface, alive: udata.alive.clone(), }, }; // Safety: the client ptr is valid and provided by libwayland let client_id = unsafe { client_id_from_ptr(client) }.unwrap(); let ret = HANDLE.with(|&(ref state_arc, data_ptr)| { // Safety: the data pointer has been set by outside code and is valid let data = unsafe { &mut *(data_ptr as *mut D) }; udata.data.clone().request( &Handle { handle: InnerHandle { state: state_arc.clone() } }, data, ClientId { id: client_id.clone() }, Message { sender_id: object_id.clone(), opcode: opcode as u16, args: parsed_args }, ) }); if message_desc.is_destructor { ffi_dispatch!(wayland_server_handle(), wl_resource_destroy, resource); } match (created, ret) { (Some((_, child_udata_ptr)), Some(child_data)) => unsafe { (*child_udata_ptr).data = child_data; }, (Some((child_id, _)), None) => { // Accept a missing object data if a protocol error occurred (and the object is already dead) if client_id.alive.load(Ordering::Acquire) { panic!("Callback creating object {} did not provide any object data.", child_id); } } (None, Some(_)) => { panic!("An object data was returned from a callback not creating any object"); } (None, None) => {} } 0 } unsafe extern "C" fn resource_destructor(resource: *mut wl_resource) { // Safety: if this destructor is called resource is valid and initialized by us let udata = unsafe { Box::from_raw(ffi_dispatch!(wayland_server_handle(), wl_resource_get_user_data, resource) as *mut ResourceUserData) }; let id = ffi_dispatch!(wayland_server_handle(), wl_resource_get_id, resource); let client = ffi_dispatch!(wayland_server_handle(), wl_resource_get_client, resource); // if this destructor is invoked during cleanup, the client ptr is no longer valid and it'll return None let client_id = unsafe { client_id_from_ptr(client) }.unwrap_or(InnerClientId { ptr: std::ptr::null_mut(), alive: Arc::new(AtomicBool::new(false)), }); udata.alive.store(false, Ordering::Release); let object_id = InnerObjectId { interface: udata.interface, ptr: resource, alive: udata.alive.clone(), id }; // Due to reentrancy, it is possible that both HANDLE and PENDING_DESTRUCTORS are set at the same time // If this is the case, PENDING_DESTRUCTORS should have priority if !PENDING_DESTRUCTORS.is_set() { HANDLE.with(|&(ref state_arc, data_ptr)| { // Safety: the data pointer have been set by outside code and are valid let data = unsafe { &mut *(data_ptr as *mut D) }; udata.data.destroyed( &Handle { handle: InnerHandle { state: state_arc.clone() } }, data, ClientId { id: client_id }, ObjectId { id: object_id }, ); }); } else { PENDING_DESTRUCTORS.with(|&pending_ptr| { // Safety: the pending pointer have been set by outside code and are valid let pending = unsafe { &mut *(pending_ptr as *mut Vec>) }; pending.push(( udata.data.clone(), ClientId { id: client_id }, ObjectId { id: object_id }, )); }) } } #[cfg(feature = "log")] extern "C" { fn wl_log_trampoline_to_rust_server(fmt: *const std::os::raw::c_char, list: *const c_void); } struct UninitObjectData; impl ObjectData for UninitObjectData { #[cfg_attr(coverage, coverage(off))] fn request( self: Arc, _: &Handle, _: &mut D, _: ClientId, msg: Message, ) -> Option>> { panic!("Received a message on an uninitialized object: {:?}", msg); } #[cfg_attr(coverage, coverage(off))] fn destroyed(self: Arc, _: &Handle, _: &mut D, _: ClientId, _: ObjectId) {} #[cfg_attr(coverage, coverage(off))] fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("UninitObjectData").finish() } } wayland-backend-0.3.8/src/test/destructors.rs000064400000000000000000000156131046102023000173630ustar 00000000000000use std::{ ffi::CString, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, }; use super::*; struct ServerData(AtomicBool); macro_rules! impl_server_objectdata { ($server_backend:tt) => { impl $server_backend::ObjectData<()> for ServerData { fn request( self: Arc, _: &$server_backend::Handle, _: &mut (), _: $server_backend::ClientId, _: Message<$server_backend::ObjectId, OwnedFd>, ) -> Option>> { None } fn destroyed( self: Arc, _: &$server_backend::Handle, _: &mut (), _: $server_backend::ClientId, _: $server_backend::ObjectId, ) { self.0.store(true, Ordering::Release); } } impl $server_backend::GlobalHandler<()> for ServerData { fn bind( self: Arc, _: &$server_backend::Handle, _: &mut (), _: $server_backend::ClientId, _: $server_backend::GlobalId, _: $server_backend::ObjectId, ) -> Arc> { self } } }; } impl_server_objectdata!(server_rs); impl_server_objectdata!(server_sys); struct ClientData(AtomicBool); macro_rules! impl_client_objectdata { ($client_backend:tt) => { impl $client_backend::ObjectData for ClientData { fn event( self: Arc, _: &$client_backend::Backend, _: Message<$client_backend::ObjectId, OwnedFd>, ) -> Option> { None } fn destroyed(&self, _object_id: $client_backend::ObjectId) { self.0.store(true, Ordering::Release); } } }; } impl_client_objectdata!(client_rs); impl_client_objectdata!(client_sys); expand_test!(destructor_request, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); let server_data = Arc::new(ServerData(AtomicBool::new(false))); let client_data = Arc::new(ClientData(AtomicBool::new(false))); server.handle().create_global(&interfaces::TEST_GLOBAL_INTERFACE, 3, server_data.clone()); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)), ) .unwrap(); // create the test global let test_global_id = client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(3), Argument::NewId(client_backend::ObjectId::null()), ], ), Some(client_data.clone()), Some((&interfaces::TEST_GLOBAL_INTERFACE, 3)), ) .unwrap(); client .send_request( message!( test_global_id, 4, // destroy [] ), None, None, ) .unwrap(); assert!(client_data.0.load(Ordering::Acquire)); client.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); assert!(server_data.0.load(Ordering::Acquire)); }); expand_test!(destructor_cleanup, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); let server_data = Arc::new(ServerData(AtomicBool::new(false))); let client_data = Arc::new(ClientData(AtomicBool::new(false))); server.handle().create_global(&interfaces::TEST_GLOBAL_INTERFACE, 3, server_data.clone()); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)), ) .unwrap(); // create the test global client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(3), Argument::NewId(client_backend::ObjectId::null()), ], ), Some(client_data), Some((&interfaces::TEST_GLOBAL_INTERFACE, 3)), ) .unwrap(); client.flush().unwrap(); // dispatch once to ensure objects are created server-side as well server.dispatch_all_clients(&mut ()).unwrap(); // then drop the client std::mem::drop(client); // now destructors should be called server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); assert!(server_data.0.load(Ordering::Acquire)); }); struct ServerClientData(AtomicBool); macro_rules! impl_server_clientdata { ($server_backend:tt) => { impl $server_backend::ClientData for ServerClientData { fn initialized(&self, _: $server_backend::ClientId) {} fn disconnected( &self, _: $server_backend::ClientId, _: crate::types::server::DisconnectReason, ) { self.0.store(true, Ordering::Release); } } }; } impl_server_clientdata!(server_rs); impl_server_clientdata!(server_sys); expand_test!(destructor_client_cleanup, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let client_data = Arc::new(ServerClientData(AtomicBool::new(false))); let _client_id = server.handle().insert_client(rx, client_data.clone()).unwrap(); std::mem::drop(tx); server.dispatch_all_clients(&mut ()).unwrap(); assert!(client_data.0.load(Ordering::Acquire)); }); wayland-backend-0.3.8/src/test/many_args.rs000064400000000000000000000161671046102023000167670ustar 00000000000000use std::{ ffi::{CStr, CString}, sync::atomic::{AtomicBool, Ordering}, }; use crate::protocol::Message; use super::*; struct ServerData(AtomicBool); macro_rules! serverdata_impls { ($server_backend:tt) => { impl $server_backend::ObjectData<()> for ServerData { fn request( self: Arc, _: &$server_backend::Handle, _: &mut (), _: $server_backend::ClientId, msg: Message<$server_backend::ObjectId, OwnedFd> ) -> Option>> { assert_eq!(msg.opcode, 0); if let [Argument::Uint(u), Argument::Int(i), Argument::Fixed(f), Argument::Array(ref a), Argument::Str(Some(ref s)), Argument::Fd(fd)] = &msg.args[..] { assert_eq!(*u, 42); assert_eq!(*i, -13); assert_eq!(*f, 4589); assert_eq!(&**a, &[1, 2, 3, 4, 5, 6, 7, 8, 9]); assert_eq!(&***s, CStr::from_bytes_with_nul(b"I like trains\0").unwrap()); // compare the fd to stdin let stat1 = rustix::fs::fstat(&fd).unwrap(); let stat2 = rustix::fs::fstat(std::io::stdin()).unwrap(); assert_eq!(stat1.st_dev, stat2.st_dev); assert_eq!(stat1.st_ino, stat2.st_ino); } else { panic!("Bad argument list !") } self.0.store(true, Ordering::SeqCst); None } fn destroyed( self: Arc, _: &$server_backend::Handle, _: &mut (), _: $server_backend::ClientId, _: $server_backend::ObjectId ) { } } impl $server_backend::GlobalHandler<()> for ServerData { fn bind( self: Arc, handle: &$server_backend::Handle, _: &mut (), _: $server_backend::ClientId, _: $server_backend::GlobalId, object_id: $server_backend::ObjectId, ) -> Arc> { handle .send_event(message!( object_id, 0, [ Argument::Uint(1337), Argument::Int(-53), Argument::Fixed(9823), Argument::Array(Box::new(vec![10, 20, 30, 40, 50, 60, 70, 80, 90])), Argument::Str(Some(Box::new(CString::new("I want cake".as_bytes()).unwrap()))), Argument::Fd(1), // stdout ], )) .unwrap(); self } } } } serverdata_impls!(server_rs); serverdata_impls!(server_sys); struct ClientData(AtomicBool); macro_rules! clientdata_impls { ($client_backend:tt) => { impl $client_backend::ObjectData for ClientData { fn event( self: Arc, _handle: & $client_backend::Backend, msg: Message<$client_backend::ObjectId, OwnedFd> ) -> Option> { assert_eq!(msg.opcode, 0); if let [Argument::Uint(u), Argument::Int(i), Argument::Fixed(f), Argument::Array(ref a), Argument::Str(Some(ref s)), Argument::Fd(fd)] = &msg.args[..] { assert_eq!(*u, 1337); assert_eq!(*i, -53); assert_eq!(*f, 9823); assert_eq!(&**a, &[10, 20, 30, 40, 50, 60, 70, 80, 90]); assert_eq!(&***s, CStr::from_bytes_with_nul(b"I want cake\0").unwrap()); // compare the fd to stdout let stat1 = rustix::fs::fstat(&fd).unwrap(); let stat2 = rustix::fs::fstat(std::io::stdout()).unwrap(); assert_eq!(stat1.st_dev, stat2.st_dev); assert_eq!(stat1.st_ino, stat2.st_ino); } else { panic!("Bad argument list !") } self.0.store(true, Ordering::SeqCst); None } fn destroyed(&self, _object_id: $client_backend::ObjectId) {} } } } clientdata_impls!(client_rs); clientdata_impls!(client_sys); // create a global and send the many_args method expand_test!(many_args, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); let server_data = Arc::new(ServerData(AtomicBool::new(false))); let client_data = Arc::new(ClientData(AtomicBool::new(false))); // Prepare a global server.handle().create_global(&interfaces::TEST_GLOBAL_INTERFACE, 1, server_data.clone()); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)), ) .unwrap(); // create the test global let test_global_id = client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(1), Argument::NewId(client_backend::ObjectId::null()), ], ), Some(client_data.clone()), Some((&interfaces::TEST_GLOBAL_INTERFACE, 1)), ) .unwrap(); client.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); client.prepare_read().unwrap().read().unwrap(); assert!(client_data.0.load(Ordering::SeqCst)); // send the many_args request client .send_request( message!( test_global_id, 0, [ Argument::Uint(42), Argument::Int(-13), Argument::Fixed(4589), Argument::Array(Box::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9])), Argument::Str(Some(Box::new( CString::new("I like trains".as_bytes()).unwrap() ))), Argument::Fd(0), // stdin ], ), None, None, ) .unwrap(); client.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); assert!(server_data.0.load(Ordering::SeqCst)); }); wayland-backend-0.3.8/src/test/mod.rs000064400000000000000000000125271046102023000155620ustar 00000000000000#![allow(dead_code, non_snake_case)] use std::os::fd::OwnedFd; use std::sync::Arc; use crate::protocol::{Argument, Message}; use crate::rs::{client as client_rs, server as server_rs}; use crate::sys::{client as client_sys, server as server_sys}; macro_rules! expand_test { ($test_name:ident, $test_body:tt) => { expand_test!(__list_expand, __no_panic, $test_name, $test_body); }; (panic $test_name:ident, $test_body:tt) => { expand_test!(__list_expand, __panic, $test_name, $test_body); }; (__list_expand, $panic:tt, $test_name:ident, $test_body:tt) => { expand_test!(__expand, $panic, $test_name, client_rs, server_rs, $test_body); expand_test!(__expand, $panic, $test_name, client_sys, server_rs, $test_body); expand_test!(__expand, $panic, $test_name, client_rs, server_sys, $test_body); expand_test!(__expand, $panic, $test_name, client_sys, server_sys, $test_body); }; (__expand, __panic, $test_name: ident, $client_backend: ty, $server_backend: ident, $test_body: tt) => { concat_idents::concat_idents!(fn_name = $test_name, __, $client_backend, __, $server_backend { #[test] #[should_panic] #[allow(unused_imports)] fn fn_name() { let _ = env_logger::builder().is_test(true).try_init(); use $client_backend as client_backend; use $server_backend as server_backend; $test_body } }); }; (__expand, __no_panic, $test_name: ident, $client_backend: ty, $server_backend: ident, $test_body: tt) => { concat_idents::concat_idents!(fn_name = $test_name, __, $client_backend, __, $server_backend { #[test] #[allow(unused_imports)] fn fn_name() { let _ = env_logger::builder().is_test(true).try_init(); use $client_backend as client_backend; use $server_backend as server_backend; $test_body } }); }; } mod interfaces { use crate as wayland_backend; wayland_scanner::generate_interfaces!( "../wayland-scanner/tests/scanner_assets/test-protocol.xml" ); } mod destructors; mod many_args; mod object_args; mod protocol_error; mod server_created_objects; mod sync; /* * Assertion of Send/Sync for all relevant objects */ fn ensure_both() {} #[allow(dead_code)] fn send_sync_client_rs() { ensure_both::(); ensure_both::(); } #[allow(dead_code)] fn send_sync_client_sys() { ensure_both::(); ensure_both::(); } #[allow(dead_code)] fn send_sync_server_rs() { ensure_both::>(); ensure_both::(); ensure_both::(); ensure_both::(); } #[allow(dead_code)] fn send_sync_server_sys() { ensure_both::>(); ensure_both::(); ensure_both::(); ensure_both::(); } /* * A "do nothing" data as a helper */ struct DoNothingData; // Server Global Handler impl server_rs::GlobalHandler for DoNothingData { fn bind( self: Arc, _: &server_rs::Handle, _: &mut D, _: server_rs::ClientId, _: server_rs::GlobalId, _: server_rs::ObjectId, ) -> Arc> { self } } impl server_sys::GlobalHandler for DoNothingData { fn bind( self: Arc, _: &server_sys::Handle, _: &mut D, _: server_sys::ClientId, _: server_sys::GlobalId, _: server_sys::ObjectId, ) -> Arc> { self } } // Server Object Data impl server_rs::ObjectData for DoNothingData { fn request( self: Arc, _: &server_rs::Handle, _: &mut D, _: server_rs::ClientId, _: Message, ) -> Option>> { None } fn destroyed( self: Arc, _handle: &server_rs::Handle, _: &mut D, _: server_rs::ClientId, _: server_rs::ObjectId, ) { } } impl server_sys::ObjectData for DoNothingData { fn request( self: Arc, _: &server_sys::Handle, _: &mut D, _: server_sys::ClientId, _: Message, ) -> Option>> { None } fn destroyed( self: Arc, _handle: &server_sys::Handle, _: &mut D, _: server_sys::ClientId, _: server_sys::ObjectId, ) { } } // Client Object Data impl client_rs::ObjectData for DoNothingData { fn event( self: Arc, _: &client_rs::Backend, _: Message, ) -> Option> { None } fn destroyed(&self, _: client_rs::ObjectId) {} } impl client_sys::ObjectData for DoNothingData { fn event( self: Arc, _: &client_sys::Backend, _: Message, ) -> Option> { None } fn destroyed(&self, _: client_sys::ObjectId) {} } wayland-backend-0.3.8/src/test/object_args.rs000064400000000000000000000413561046102023000172670ustar 00000000000000use std::{ ffi::CString, sync::atomic::{AtomicBool, Ordering}, }; use crate::protocol::Message; use super::*; struct ServerData(AtomicBool); macro_rules! impl_server_objectdata { ($server_backend:tt) => { impl $server_backend::ObjectData<()> for ServerData { fn request( self: Arc, handle: &$server_backend::Handle, _: &mut (), _: $server_backend::ClientId, msg: Message<$server_backend::ObjectId, OwnedFd>, ) -> Option>> { if msg.opcode == 1 { assert_eq!( handle.object_info(msg.sender_id.clone()).unwrap().interface.name, "test_global" ); if let [Argument::NewId(secondary)] = &msg.args[..] { handle .send_event(message!(msg.sender_id, 1, [Argument::Object(secondary.clone())])) .unwrap(); return Some(self); } else { panic!("Bad argument list!"); } } else if msg.opcode == 2{ return Some(self); } else if msg.opcode == 3 { assert_eq!(handle.object_info(msg.sender_id).unwrap().interface.name, "test_global"); if let [Argument::Object(secondary), Argument::Object(tertiary), Argument::Uint(u)] = &msg.args[..] { assert_eq!( handle.object_info(secondary.clone()).unwrap().interface.name, "secondary" ); if *u == 1 { assert!(tertiary.is_null()); } else if *u == 2 { assert_eq!( handle.object_info(tertiary.clone()).unwrap().interface.name, "tertiary" ); self.0.store(true, Ordering::SeqCst); } } else { panic!("Bad argument list!"); } } else if msg.opcode == 6 { if let [Argument::NewId(_), Argument::Object(sec), Argument::Object(ter)] = &msg.args[..] { assert!(sec.is_null()); assert!(&ter.interface().name == &interfaces::TERTIARY_INTERFACE.name); } else { panic!("Bad argument list!"); } self.0.store(true, Ordering::SeqCst); return Some(self) } None } fn destroyed( self: Arc, _: &$server_backend::Handle, _: &mut (), _: $server_backend::ClientId, _: $server_backend::ObjectId ) { } } impl $server_backend::GlobalHandler<()> for ServerData { fn bind( self: Arc, _: &$server_backend::Handle, _: &mut (), _: $server_backend::ClientId, _: $server_backend::GlobalId, _: $server_backend::ObjectId ) -> Arc> { self } } } } impl_server_objectdata!(server_rs); impl_server_objectdata!(server_sys); struct ClientData(AtomicBool); macro_rules! impl_client_objectdata { ($client_backend:tt) => { impl $client_backend::ObjectData for ClientData { fn event( self: Arc, handle: &$client_backend::Backend, msg: Message<$client_backend::ObjectId, OwnedFd>, ) -> Option> { assert_eq!(msg.opcode, 1); if let [Argument::Object(secondary)] = &msg.args[..] { let info = handle.info(secondary.clone()).unwrap(); assert_eq!(info.id, 4); assert_eq!(info.interface.name, "secondary"); } else { panic!("Bad argument list!"); } self.0.store(true, Ordering::SeqCst); None } fn destroyed(&self, _object_id: $client_backend::ObjectId) {} } }; } impl_client_objectdata!(client_rs); impl_client_objectdata!(client_sys); // create a global and create objects expand_test!(create_objects, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); let server_data = Arc::new(ServerData(AtomicBool::new(false))); let client_data = Arc::new(ClientData(AtomicBool::new(false))); // Prepare a global server.handle().create_global(&interfaces::TEST_GLOBAL_INTERFACE, 3, server_data.clone()); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)), ) .unwrap(); // create the test global let test_global_id = client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(3), Argument::NewId(client_backend::ObjectId::null()), ], ), Some(client_data.clone()), Some((&interfaces::TEST_GLOBAL_INTERFACE, 3)), ) .unwrap(); // create the two objects let secondary_id = client .send_request( message!( test_global_id.clone(), 1, [Argument::NewId(client_backend::ObjectId::null())] ), Some(client_data.clone()), None, ) .unwrap(); let tertiary_id = client .send_request( message!( test_global_id.clone(), 2, [Argument::NewId(client_backend::ObjectId::null())] ), Some(client_data.clone()), None, ) .unwrap(); // link them client .send_request( message!( test_global_id.clone(), 3, [ Argument::Object(secondary_id.clone()), Argument::Object(client_backend::ObjectId::null()), Argument::Uint(1), ], ), None, None, ) .unwrap(); client .send_request( message!( test_global_id, 3, [Argument::Object(secondary_id), Argument::Object(tertiary_id), Argument::Uint(2)], ), None, None, ) .unwrap(); client.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); client.prepare_read().unwrap().read().unwrap(); assert!(server_data.0.load(Ordering::SeqCst)); assert!(client_data.0.load(Ordering::SeqCst)); }); expand_test!(panic bad_interface, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let server = server_backend::Backend::<()>::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); let server_data = Arc::new(ServerData(AtomicBool::new(false))); // Prepare a global server.handle().create_global(&interfaces::TEST_GLOBAL_INTERFACE, 3, server_data); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)) ) .unwrap(); // create the test global let test_global_id = client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(3), Argument::NewId(client_backend::ObjectId::null()), ], ), None, Some((&interfaces::TEST_GLOBAL_INTERFACE, 3)) ) .unwrap(); // create the two objects let secondary_id = client .send_request(message!(test_global_id.clone(), 1, [Argument::NewId(client_backend::ObjectId::null())]), Some(Arc::new(DoNothingData)), None) .unwrap(); let tertiary_id = client .send_request(message!(test_global_id.clone(), 2, [Argument::NewId(client_backend::ObjectId::null())]), Some(Arc::new(DoNothingData)), None) .unwrap(); // link them, argument order is wrong, should panic client .send_request( message!( test_global_id, 3, [Argument::Object(tertiary_id), Argument::Object(secondary_id), Argument::Uint(42)], ), None, None, ) .unwrap(); }); expand_test!(panic double_null, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let server = server_backend::Backend::<()>::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); let server_data = Arc::new(ServerData(AtomicBool::new(false))); // Prepare a global server.handle().create_global(&interfaces::TEST_GLOBAL_INTERFACE, 3, server_data); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)) ) .unwrap(); // create the test global let test_global_id = client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(3), Argument::NewId(client_backend::ObjectId::null()), ], ), Some(Arc::new(DoNothingData)), Some((&interfaces::TEST_GLOBAL_INTERFACE, 3)) ) .unwrap(); // link two objects, first object cannot be null, should panic client .send_request( message!( test_global_id, 3, [ Argument::Object(client_backend::ObjectId::null()), Argument::Object(client_backend::ObjectId::null()), Argument::Uint(42) ], ), None, None, ) .unwrap(); }); expand_test!(null_obj_followed_by_interface, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let server = server_backend::Backend::<()>::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); let server_data = Arc::new(ServerData(AtomicBool::new(false))); // Prepare a global server.handle().create_global(&interfaces::TEST_GLOBAL_INTERFACE, 3, server_data); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)), ) .unwrap(); // create the test global let test_global_id = client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(3), Argument::NewId(client_backend::ObjectId::null()), ], ), Some(Arc::new(DoNothingData)), Some((&interfaces::TEST_GLOBAL_INTERFACE, 3)), ) .unwrap(); // create the an object let tertiary_id = client .send_request( message!( test_global_id.clone(), 2, [Argument::NewId(client_backend::ObjectId::null())] ), Some(Arc::new(DoNothingData)), None, ) .unwrap(); // link it, first is null but the second is not, this should work fine client .send_request( message!( test_global_id, 5, [ Argument::Object(client_backend::ObjectId::null()), Argument::Object(tertiary_id), ], ), None, None, ) .unwrap(); }); expand_test!(new_id_null_and_non_null, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::<()>::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); let server_data = Arc::new(ServerData(AtomicBool::new(false))); // Prepare a global server.handle().create_global(&interfaces::TEST_GLOBAL_INTERFACE, 5, server_data.clone()); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)), ) .unwrap(); // create the test global let test_global_id = client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(5), Argument::NewId(client_backend::ObjectId::null()), ], ), Some(Arc::new(DoNothingData)), Some((&interfaces::TEST_GLOBAL_INTERFACE, 3)), ) .unwrap(); // create the an object let tertiary_id = client .send_request( message!( test_global_id.clone(), 2, [Argument::NewId(client_backend::ObjectId::null())] ), Some(Arc::new(DoNothingData)), None, ) .unwrap(); // link it, first is null but the second is not, this should work fine let _quad_id = client .send_request( message!( test_global_id, 6, // newid_and_allow_null [ Argument::NewId(client_backend::ObjectId::null()), Argument::Object(client_backend::ObjectId::null()), Argument::Object(tertiary_id), ], ), Some(Arc::new(DoNothingData)), None, ) .unwrap(); client.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); client.prepare_read().unwrap().read().unwrap(); assert!(server_data.0.load(Ordering::SeqCst)); }); wayland-backend-0.3.8/src/test/protocol_error.rs000064400000000000000000000237641046102023000200620ustar 00000000000000use std::{ ffi::CString, sync::{Arc, Mutex}, }; use crate::rs::socket::{BufferedSocket, Socket}; use super::*; struct ServerData(Arc>>); impl server_rs::GlobalHandler<()> for ServerData { fn bind( self: Arc, _: &server_rs::Handle, _: &mut (), _: server_rs::ClientId, _: server_rs::GlobalId, object_id: server_rs::ObjectId, ) -> Arc> { *(self.0.lock().unwrap()) = Some(object_id); Arc::new(DoNothingData) } } impl server_sys::GlobalHandler<()> for ServerData { fn bind( self: Arc, _: &server_sys::Handle, _: &mut (), _: server_sys::ClientId, _: server_sys::GlobalId, object_id: server_sys::ObjectId, ) -> Arc> { *(self.0.lock().unwrap()) = Some(object_id); Arc::new(DoNothingData) } } expand_test!(protocol_error, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); let object_id = Arc::new(Mutex::new(None)); // Prepare a global server.handle().create_global( &interfaces::TEST_GLOBAL_INTERFACE, 3, Arc::new(ServerData(object_id.clone())), ); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)), ) .unwrap(); // create the test global client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(3), Argument::NewId(client_backend::ObjectId::null()), ], ), Some(Arc::new(DoNothingData)), Some((&interfaces::TEST_GLOBAL_INTERFACE, 3)), ) .unwrap(); client.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); // get the object_id for the global let oid = object_id.lock().unwrap().clone().unwrap(); // post the error server.handle().post_error(oid, 42, CString::new("I don't like you.".as_bytes()).unwrap()); server.flush(None).unwrap(); let ret = client.prepare_read().unwrap().read(); match ret { Err(client_backend::WaylandError::Protocol(err)) => { assert_eq!(err.code, 42); assert_eq!(err.object_id, 3); assert_eq!(err.object_interface, "test_global"); if std::any::TypeId::of::() == std::any::TypeId::of::() { // only the RS client backed can retrieve the error message assert_eq!(err.message, "I don't like you."); } } _ => panic!("Bad ret: {:?}", ret), } }); expand_test!(client_wrong_id, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::<()>::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let mut socket = BufferedSocket::new(Socket::from(tx)); socket .write_message(&Message { sender_id: 1, // wl_display opcode: 1, // wl_registry args: smallvec::smallvec![ Argument::NewId(3), // should be 2 ], }) .unwrap(); socket.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); // server should have killed us due to the error, but it might send us that error first let ret = socket.fill_incoming_buffers().and_then(|_| socket.fill_incoming_buffers()); assert!(ret.is_err()); }); expand_test!(client_wrong_opcode, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::<()>::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let mut socket = BufferedSocket::new(Socket::from(tx)); socket .write_message(&Message { sender_id: 1, // wl_display opcode: 42, // inexistant args: smallvec::smallvec![], }) .unwrap(); socket.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); // server should have killed us due to the error, but it might send us that error first let ret = socket.fill_incoming_buffers().and_then(|_| socket.fill_incoming_buffers()); assert!(ret.is_err()); }); expand_test!(client_wrong_sender, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::<()>::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let mut socket = BufferedSocket::new(Socket::from(tx)); socket .write_message(&Message { sender_id: 2, // inexistant opcode: 0, // args: smallvec::smallvec![], }) .unwrap(); socket.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); // server should have killed us due to the error, but it might send us that error first let ret = socket.fill_incoming_buffers().and_then(|_| socket.fill_incoming_buffers()); assert!(ret.is_err()); }); struct ProtocolErrorServerData; impl server_rs::GlobalHandler<()> for ProtocolErrorServerData { fn bind( self: Arc, _: &server_rs::Handle, _: &mut (), _: server_rs::ClientId, _: server_rs::GlobalId, _: server_rs::ObjectId, ) -> Arc> { Arc::new(ProtocolErrorServerData) } } impl server_sys::GlobalHandler<()> for ProtocolErrorServerData { fn bind( self: Arc, _: &server_sys::Handle, _: &mut (), _: server_sys::ClientId, _: server_sys::GlobalId, _: server_sys::ObjectId, ) -> Arc> { Arc::new(ProtocolErrorServerData) } } impl server_rs::ObjectData for ProtocolErrorServerData { fn request( self: Arc, handle: &server_rs::Handle, _: &mut D, _: server_rs::ClientId, msg: Message, ) -> Option>> { handle.post_error(msg.sender_id, 0, CString::new("I don't like you.".as_bytes()).unwrap()); None } fn destroyed( self: Arc, _handle: &server_rs::Handle, _: &mut D, _: server_rs::ClientId, _: server_rs::ObjectId, ) { } } impl server_sys::ObjectData for ProtocolErrorServerData { fn request( self: Arc, handle: &server_sys::Handle, _: &mut D, _: server_sys::ClientId, msg: Message, ) -> Option>> { handle.post_error(msg.sender_id, 0, CString::new("I don't like you.".as_bytes()).unwrap()); None } fn destroyed( self: Arc, _handle: &server_sys::Handle, _: &mut D, _: server_sys::ClientId, _: server_sys::ObjectId, ) { } } expand_test!(protocol_error_in_request_without_object_init, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); // Prepare a global server.handle().create_global( &interfaces::TEST_GLOBAL_INTERFACE, 3, // The user code will provide an user data even if it triggers a protocol error // (and thus destroys the object) Arc::new(ProtocolErrorServerData), ); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)), ) .unwrap(); // create the test global let test_global_id = client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(3), Argument::NewId(client_backend::ObjectId::null()), ], ), Some(Arc::new(DoNothingData)), Some((&interfaces::TEST_GLOBAL_INTERFACE, 3)), ) .unwrap(); client.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); // Now, the client sends a request, which will trigger a protocol error client .send_request( message!(test_global_id, 1, [Argument::NewId(client_backend::ObjectId::null())]), Some(Arc::new(DoNothingData)), None, ) .unwrap(); client.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); // the server should not panic, and gracefull accept that the user did not provide any object data for // the already destroyed object }); wayland-backend-0.3.8/src/test/server_created_objects.rs000064400000000000000000000131761046102023000215120ustar 00000000000000use std::{ ffi::CString, sync::atomic::{AtomicU32, Ordering}, }; use crate::protocol::Message; use super::*; struct ServerData; macro_rules! impl_globalhandler { ($server_backend:tt) => { impl $server_backend::GlobalHandler<()> for ServerData { fn bind( self: Arc, handle: &$server_backend::Handle, _: &mut (), client: $server_backend::ClientId, _: $server_backend::GlobalId, object_id: $server_backend::ObjectId, ) -> Arc> { // send the first event with a newid & a null object let obj_1 = handle .create_object::<()>( client.clone(), &interfaces::QUAD_INTERFACE, 3, Arc::new(DoNothingData), ) .unwrap(); let null_id = $server_backend::ObjectId::null(); handle .send_event(message!( object_id.clone(), 2, [Argument::NewId(obj_1.clone()), Argument::Object(null_id)], )) .unwrap(); // send the second let obj_2 = handle .create_object::<()>( client, &interfaces::QUAD_INTERFACE, 3, Arc::new(DoNothingData), ) .unwrap(); handle .send_event(message!( object_id.clone(), 2, [Argument::NewId(obj_2), Argument::Object(obj_1)] )) .unwrap(); Arc::new(DoNothingData) } } }; } impl_globalhandler!(server_rs); impl_globalhandler!(server_sys); struct ClientData(AtomicU32); macro_rules! impl_client_objectdata { ($client_backend:tt) => { impl $client_backend::ObjectData for ClientData { fn event( self: Arc, handle: &$client_backend::Backend, msg: Message<$client_backend::ObjectId, OwnedFd>, ) -> Option> { assert_eq!(msg.opcode, 2); if self.0.load(Ordering::SeqCst) == 0 { if let [Argument::NewId(obj_1), Argument::Object(null_id)] = &msg.args[..] { let info = handle.info(obj_1.clone()).unwrap(); assert_eq!(info.id, 0xFF00_0000); assert_eq!(info.interface.name, "quad"); assert!(null_id.is_null()); } else { panic!("Bad argument list!"); } self.0.store(1, Ordering::SeqCst); } else { if let [Argument::NewId(obj_2), Argument::Object(obj_1)] = &msg.args[..] { // check obj1 let info = handle.info(obj_1.clone()).unwrap(); assert_eq!(info.id, 0xFF00_0000); assert_eq!(info.interface.name, "quad"); // check obj2 let info = handle.info(obj_2.clone()).unwrap(); assert_eq!(info.id, 0xFF00_0001); assert_eq!(info.interface.name, "quad"); } else { panic!("Bad argument list!"); } self.0.store(2, Ordering::SeqCst); } Some(self) } fn destroyed(&self, _object_id: $client_backend::ObjectId) {} } }; } impl_client_objectdata!(client_rs); impl_client_objectdata!(client_sys); expand_test!(server_created_object, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); let client_data = Arc::new(ClientData(AtomicU32::new(0))); // Prepare a global server.handle().create_global(&interfaces::TEST_GLOBAL_INTERFACE, 3, Arc::new(ServerData)); // get the registry client-side let client_display = client.display_id(); let registry_id = client .send_request( message!(client_display, 1, [Argument::NewId(client_backend::ObjectId::null())],), Some(Arc::new(DoNothingData)), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)), ) .unwrap(); // create the test global let _test_global_id = client .send_request( message!( registry_id, 0, [ Argument::Uint(1), Argument::Str(Some(Box::new( CString::new(interfaces::TEST_GLOBAL_INTERFACE.name.as_bytes()).unwrap(), ))), Argument::Uint(1), Argument::NewId(client_backend::ObjectId::null()), ], ), Some(client_data.clone()), Some((&interfaces::TEST_GLOBAL_INTERFACE, 3)), ) .unwrap(); client.flush().unwrap(); server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); client.prepare_read().unwrap().read().unwrap(); assert_eq!(client_data.0.load(Ordering::SeqCst), 2); }); wayland-backend-0.3.8/src/test/sync.rs000064400000000000000000000107421046102023000157540ustar 00000000000000use std::sync::atomic::{AtomicBool, Ordering}; use super::*; struct SyncData(AtomicBool); impl client_rs::ObjectData for SyncData { fn event( self: Arc, _: &client_rs::Backend, msg: Message, ) -> Option> { assert_eq!(msg.opcode, 0); assert!(matches!(&msg.args[..], [Argument::Uint(_)])); self.0.store(true, Ordering::SeqCst); None } fn destroyed(&self, _: client_rs::ObjectId) {} } impl client_sys::ObjectData for SyncData { fn event( self: Arc, _: &client_sys::Backend, msg: Message, ) -> Option> { assert_eq!(msg.opcode, 0); assert!(matches!(&msg.args[..], [Argument::Uint(_)])); self.0.store(true, Ordering::SeqCst); None } fn destroyed(&self, _: client_sys::ObjectId) {} } // send a wl_display.sync request and receive the response expand_test!(sync, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); // send the request let client_display = client.display_id(); let sync_data = Arc::new(SyncData(AtomicBool::new(false))); let sync_id = client .send_request( message!(client_display, 0, [Argument::NewId(client_backend::ObjectId::null())]), Some(sync_data.clone()), Some((&interfaces::WL_CALLBACK_INTERFACE, 1)), ) .unwrap(); client.flush().unwrap(); std::thread::sleep(std::time::Duration::from_millis(10)); // process it server-side server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); std::thread::sleep(std::time::Duration::from_millis(10)); // ensure the answer is received client-side client.prepare_read().unwrap().read().unwrap(); assert!(sync_data.0.load(Ordering::SeqCst)); // and the sync object should be dead assert!(client.get_data(sync_id).is_err()); }); expand_test!(panic test_bad_placeholder, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); // send the request let client_display = client.display_id(); let sync_data = Arc::new(SyncData(AtomicBool::new(false))); let sync_id = client .send_request( message!(client_display, 0, [Argument::NewId(client_backend::ObjectId::null())]), Some(sync_data.clone()), Some((&interfaces::WL_REGISTRY_INTERFACE, 1)) ) .unwrap(); client.flush().unwrap(); std::thread::sleep(std::time::Duration::from_millis(10)); // process it server-side server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); std::thread::sleep(std::time::Duration::from_millis(10)); // ensure the answer is received client-side client.prepare_read().unwrap().read().unwrap(); assert!(sync_data.0.load(Ordering::SeqCst)); // and the sync object should be dead assert!(client.get_data(sync_id).is_err()); }); expand_test!(panic test_bad_signature, { let (tx, rx) = std::os::unix::net::UnixStream::pair().unwrap(); let mut server = server_backend::Backend::new().unwrap(); let _client_id = server.handle().insert_client(rx, Arc::new(())).unwrap(); let client = client_backend::Backend::connect(tx).unwrap(); // send the request let client_display = client.display_id(); let sync_data = Arc::new(SyncData(AtomicBool::new(false))); let sync_id = client .send_request(message!(client_display, 0, [Argument::Uint(1)]), Some(sync_data.clone()), None) .unwrap(); client.flush().unwrap(); std::thread::sleep(std::time::Duration::from_millis(10)); // process it server-side server.dispatch_all_clients(&mut ()).unwrap(); server.flush(None).unwrap(); std::thread::sleep(std::time::Duration::from_millis(10)); // ensure the answer is received client-side client.prepare_read().unwrap().read().unwrap(); assert!(sync_data.0.load(Ordering::SeqCst)); // and the sync object should be dead assert!(client.get_data(sync_id).is_err()); }); wayland-backend-0.3.8/src/types/client.rs000064400000000000000000000046111046102023000164410ustar 00000000000000/// An error type representing the failure to load libwayland #[derive(Debug)] pub struct NoWaylandLib; impl std::error::Error for NoWaylandLib {} impl std::fmt::Display for NoWaylandLib { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { f.write_str("could not load libwayland-client.so") } } /// An error that can occur when using a Wayland connection #[derive(Debug)] pub enum WaylandError { /// The connection encountered an IO error Io(std::io::Error), /// The connection encountered a protocol error Protocol(crate::protocol::ProtocolError), } impl std::error::Error for WaylandError { #[cfg_attr(coverage, coverage(off))] fn cause(&self) -> Option<&dyn std::error::Error> { match self { Self::Io(e) => Some(e), Self::Protocol(e) => Some(e), } } } impl std::fmt::Display for WaylandError { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { match self { Self::Io(e) => write!(f, "Io error: {}", e), Self::Protocol(e) => std::fmt::Display::fmt(e, f), } } } impl Clone for WaylandError { #[cfg_attr(coverage, coverage(off))] fn clone(&self) -> Self { match self { Self::Protocol(e) => Self::Protocol(e.clone()), Self::Io(e) => { if let Some(code) = e.raw_os_error() { Self::Io(std::io::Error::from_raw_os_error(code)) } else { Self::Io(std::io::Error::new(e.kind(), "")) } } } } } impl From for WaylandError { #[cfg_attr(coverage, coverage(off))] fn from(err: crate::protocol::ProtocolError) -> Self { Self::Protocol(err) } } impl From for WaylandError { #[cfg_attr(coverage, coverage(off))] fn from(err: std::io::Error) -> Self { Self::Io(err) } } /// An error generated when trying to act on an invalid `ObjectId`. #[derive(Clone, Debug)] pub struct InvalidId; impl std::error::Error for InvalidId {} impl std::fmt::Display for InvalidId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { write!(f, "Invalid ObjectId") } } wayland-backend-0.3.8/src/types/mod.rs000064400000000000000000000000401046102023000157320ustar 00000000000000pub mod client; pub mod server; wayland-backend-0.3.8/src/types/server.rs000064400000000000000000000042751046102023000164770ustar 00000000000000use crate::protocol::Interface; /// Description of a global advertised to some clients. #[derive(Debug)] pub struct GlobalInfo { /// The interface of the global. pub interface: &'static Interface, /// The version of the global that is advertised to clients. pub version: u32, /// Whether the global is disabled. pub disabled: bool, } /// An error type representing the failure to initialize a backend #[derive(Debug)] pub enum InitError { /// The wayland system library could not be loaded NoWaylandLib, /// Initialized failed due to an underlying I/O error Io(std::io::Error), } impl std::error::Error for InitError { #[cfg_attr(coverage, coverage(off))] fn cause(&self) -> Option<&dyn std::error::Error> { match self { InitError::Io(ref err) => Some(err), InitError::NoWaylandLib => None, } } } impl std::fmt::Display for InitError { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { match self { InitError::Io(ref err) => std::fmt::Display::fmt(err, f), InitError::NoWaylandLib => f.write_str("could not load libwayland-server.so"), } } } /// An error generated when trying to act on an invalid `ObjectId`. #[derive(Clone, Debug)] pub struct InvalidId; impl std::error::Error for InvalidId {} impl std::fmt::Display for InvalidId { #[cfg_attr(coverage, coverage(off))] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { write!(f, "Invalid Id") } } /// Describes why a client has been disconnected from the server. #[derive(Debug)] pub enum DisconnectReason { /// The connection has been closed by the server or client. ConnectionClosed, /// The server has sent the client a protocol error, terminating the connection. ProtocolError(crate::protocol::ProtocolError), } /// Holds the client credentials #[derive(Debug, Clone, Copy)] pub struct Credentials { /// pid of the client pub pid: rustix::process::RawPid, /// uid of the client pub uid: rustix::process::RawUid, /// gid of the client pub gid: rustix::process::RawGid, } wayland-backend-0.3.8/tests/rs_sys_impls.rs000064400000000000000000000112111046102023000171120ustar 00000000000000#![allow(clippy::test_attr_in_doctest)] //! Tests to ensure the rust and sys types implement the same traits. /// A macro used to assert a type defined in both the rust and sys implementations of wayland-backend /// implement the same traits. /// /// There are four patterns which may be matched using this macro. /// /// For example, assume you want to make sure both the rust and sys versions of `ObjectId` implement [`Debug`]. /// The following pattern would achieve that check. /// /// ```no_run /// #[test] /// fn test() { /// assert_impl!(server::ObjectId: std::fmt::Debug); /// } /// ``` /// /// Multiple traits may be tested by separating each trait with a comma. /// /// ```no_run /// #[test] /// fn test() { /// assert_impl!(server::ObjectId: std::fmt::Debug, Send, Sync); /// } /// ``` /// /// For the client side, simply change the path before the name of the type. /// /// ```no_run /// #[test] /// fn test() { /// assert_impl!(client::ObjectId: std::fmt::Debug) /// } /// ``` /// /// Traits may be tested through prefixing the contents of the macro with `dyn`. /// /// ```ignore /// #[test] /// fn test() { /// assert_impl!(dyn server::SomeTraitWithNoGeneric: std::fmt::Debug) /// } /// ``` /// /// Finally, generics may also be tested by simply adding the generics as expected in a normal type. Do note /// you will need to monomorphize the type with something, such as, `()`, the unit type. /// /// ```no_run /// #[test] /// fn test() { /// assert_impl!(server::Backend<()>: Send, Sync); // No trait /// assert_impl!(dyn server::ObjectData<()>: std::fmt::Debug); // Trait /// } /// ``` macro_rules! assert_impl { // No type parameters with dyn ( dyn $side: ident::$ty: ident: $($trait_: path),+ ) => {{ #[allow(dead_code)] fn assert_impl() {} assert_impl::(); #[cfg(feature = "server_system")] assert_impl::(); }}; // Type parameters with dyn ( dyn $side: ident::$ty: ident<$($types: ty),*>: $($trait_: path),+ ) => {{ #[allow(dead_code)] fn assert_impl() {} assert_impl::>(); #[cfg(feature = "server_system")] assert_impl::>(); }}; // No type parameters ( $side: ident::$ty: ident: $($trait_: path),+ ) => {{ #[allow(dead_code)] fn assert_impl() {} assert_impl::(); #[cfg(feature = "server_system")] assert_impl::(); }}; // Type parameters ( $side: ident::$ty: ident<$($types: ty),*>: $($trait_: path),+ ) => {{ #[allow(dead_code)] fn assert_impl() {} assert_impl::>(); #[cfg(feature = "server_system")] assert_impl::>(); }}; } mod server { #[test] fn test_impls() { // ObjectData assert_impl!( dyn server::ObjectData<()>: std::fmt::Debug, downcast_rs::DowncastSync ); // GlobalHandler assert_impl!( dyn server::GlobalHandler<()>: std::fmt::Debug, downcast_rs::DowncastSync ); // ClientData assert_impl!( dyn server::ClientData: std::fmt::Debug, downcast_rs::DowncastSync ); // ObjectId assert_impl!( server::ObjectId: std::fmt::Debug, std::fmt::Display, Send, Sync, PartialEq, Eq, Clone ); // ClientId assert_impl!(server::ClientId: std::fmt::Debug, Clone, Send, Sync, PartialEq, Eq); // GlobalId assert_impl!(server::GlobalId: std::fmt::Debug, Clone, Send, Sync, PartialEq, Eq); // Handle assert_impl!(server::Handle: std::fmt::Debug); // Backend assert_impl!(server::Backend<()>: Send, Sync); } } mod client { #[test] fn test_impls() { // ObjectData assert_impl!( dyn client::ObjectData: std::fmt::Debug, downcast_rs::DowncastSync ); // ObjectId assert_impl!( client::ObjectId: std::fmt::Debug, std::fmt::Display, Clone, Send, Sync, PartialEq, Eq ); // Backend assert_impl!(client::Backend: Send, Sync, std::fmt::Debug); } }