├── .github └── workflows │ └── CI.yml ├── .gitignore ├── .mergify.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── README.md ├── cairo-example ├── Cargo.toml └── src │ └── main.rs ├── clippy.toml ├── codecov.yml ├── doc ├── changelog.md ├── comparison.md ├── generated_code.md └── making_a_release.md ├── extract-generated-code-doc ├── Cargo.toml └── src │ ├── doc.rs │ └── main.rs ├── generator ├── Cargo.toml └── src │ ├── generator │ ├── error_events.rs │ ├── mod.rs │ ├── namespace │ │ ├── async_switch.rs │ │ ├── expr_to_str.rs │ │ ├── header.rs │ │ ├── helpers.rs │ │ ├── mod.rs │ │ ├── parse.rs │ │ ├── request.rs │ │ ├── resource_wrapper.rs │ │ ├── serialize.rs │ │ ├── struct_type.rs │ │ └── switch.rs │ ├── output.rs │ ├── requests_replies.rs │ ├── resources.rs │ └── special_cases.rs │ └── main.rs ├── x11rb-async ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── examples │ ├── shared_memory_async.rs │ └── xclock_utc_async.rs ├── src │ ├── blocking.rs │ ├── connection.rs │ ├── cookie.rs │ ├── lib.rs │ ├── protocol │ │ ├── bigreq.rs │ │ ├── composite.rs │ │ ├── damage.rs │ │ ├── dbe.rs │ │ ├── dpms.rs │ │ ├── dri2.rs │ │ ├── dri3.rs │ │ ├── ge.rs │ │ ├── glx.rs │ │ ├── mod.rs │ │ ├── present.rs │ │ ├── randr.rs │ │ ├── record.rs │ │ ├── render.rs │ │ ├── res.rs │ │ ├── screensaver.rs │ │ ├── shape.rs │ │ ├── shm.rs │ │ ├── sync.rs │ │ ├── xc_misc.rs │ │ ├── xevie.rs │ │ ├── xf86dri.rs │ │ ├── xf86vidmode.rs │ │ ├── xfixes.rs │ │ ├── xinerama.rs │ │ ├── xinput.rs │ │ ├── xkb.rs │ │ ├── xprint.rs │ │ ├── xproto.rs │ │ ├── xselinux.rs │ │ ├── xtest.rs │ │ ├── xv.rs │ │ └── xvmc.rs │ └── rust_connection │ │ ├── extensions.rs │ │ ├── mod.rs │ │ ├── nb_connect.rs │ │ ├── shared_state.rs │ │ ├── stream.rs │ │ └── write_buffer.rs └── tests │ └── connection_regression_tests.rs ├── x11rb-protocol ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── benches │ └── proto_connection.rs ├── src │ ├── connect.rs │ ├── connection │ │ └── mod.rs │ ├── errors.rs │ ├── id_allocator.rs │ ├── lib.rs │ ├── packet_reader.rs │ ├── parse_display │ │ ├── connect_instruction.rs │ │ └── mod.rs │ ├── protocol │ │ ├── bigreq.rs │ │ ├── composite.rs │ │ ├── damage.rs │ │ ├── dbe.rs │ │ ├── dpms.rs │ │ ├── dri2.rs │ │ ├── dri3.rs │ │ ├── ge.rs │ │ ├── glx.rs │ │ ├── mod.rs │ │ ├── present.rs │ │ ├── randr.rs │ │ ├── record.rs │ │ ├── render.rs │ │ ├── res.rs │ │ ├── screensaver.rs │ │ ├── shape.rs │ │ ├── shm.rs │ │ ├── sync.rs │ │ ├── xc_misc.rs │ │ ├── xevie.rs │ │ ├── xf86dri.rs │ │ ├── xf86vidmode.rs │ │ ├── xfixes.rs │ │ ├── xinerama.rs │ │ ├── xinput.rs │ │ ├── xkb.rs │ │ ├── xprint.rs │ │ ├── xproto.rs │ │ ├── xselinux.rs │ │ ├── xtest.rs │ │ ├── xv.rs │ │ └── xvmc.rs │ ├── resource_manager │ │ ├── matcher.rs │ │ ├── mod.rs │ │ └── parser.rs │ ├── test.rs │ ├── utils.rs │ ├── wrapper.rs │ ├── x11_utils.rs │ └── xauth.rs └── tests │ ├── enum_tests.rs │ ├── parsing_tests.rs │ └── request_parsing_tests.rs ├── x11rb ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── examples │ ├── check_unchecked_requests.rs │ ├── display_ppm.rs │ ├── generic_events.rs │ ├── hypnomoire.rs │ ├── integration_test_util │ │ ├── connect.rs │ │ └── util.rs │ ├── list_fonts.rs │ ├── record.rs │ ├── shared_memory.rs │ ├── simple_window.rs │ ├── simple_window_manager.rs │ ├── tutorial.rs │ ├── xclock_utc.rs │ └── xeyes.rs ├── src │ ├── connection │ │ ├── impls.rs │ │ └── mod.rs │ ├── cookie.rs │ ├── cursor │ │ ├── find_cursor.rs │ │ ├── mod.rs │ │ └── parse_cursor.rs │ ├── errors.rs │ ├── event_loop_integration.rs │ ├── extension_manager.rs │ ├── image.rs │ ├── lib.rs │ ├── properties.rs │ ├── protocol │ │ ├── bigreq.rs │ │ ├── composite.rs │ │ ├── damage.rs │ │ ├── dbe.rs │ │ ├── dpms.rs │ │ ├── dri2.rs │ │ ├── dri3.rs │ │ ├── ge.rs │ │ ├── glx.rs │ │ ├── mod.rs │ │ ├── present.rs │ │ ├── randr.rs │ │ ├── record.rs │ │ ├── render.rs │ │ ├── res.rs │ │ ├── screensaver.rs │ │ ├── shape.rs │ │ ├── shm.rs │ │ ├── sync.rs │ │ ├── xc_misc.rs │ │ ├── xevie.rs │ │ ├── xf86dri.rs │ │ ├── xf86vidmode.rs │ │ ├── xfixes.rs │ │ ├── xinerama.rs │ │ ├── xinput.rs │ │ ├── xkb.rs │ │ ├── xprint.rs │ │ ├── xproto.rs │ │ ├── xselinux.rs │ │ ├── xtest.rs │ │ ├── xv.rs │ │ └── xvmc.rs │ ├── resource_manager │ │ └── mod.rs │ ├── rust_connection │ │ ├── mod.rs │ │ ├── packet_reader.rs │ │ ├── stream.rs │ │ └── write_buffer.rs │ ├── test.rs │ ├── tracing.rs │ ├── utils.rs │ ├── wrapper.rs │ ├── x11_utils.rs │ └── xcb_ffi │ │ ├── atomic_u64.rs │ │ ├── mod.rs │ │ ├── pending_errors.rs │ │ └── raw_ffi │ │ ├── ffi.rs │ │ ├── mod.rs │ │ └── test.rs └── tests │ ├── multithread_test.rs │ ├── regression_tests.rs │ ├── resource_manager.rs │ └── x11_utils.rs ├── xcb-proto-1.17.0 ├── .gitignore ├── .gitlab-ci.yml ├── COPYING ├── HACKING ├── INSTALL ├── Makefile.am ├── NEWS ├── README.md ├── TODO ├── autogen.sh ├── configure.ac ├── doc │ └── xml-xcb.txt ├── src │ ├── .gitattributes │ ├── Makefile.am │ ├── bigreq.xml │ ├── composite.xml │ ├── damage.xml │ ├── dbe.xml │ ├── dpms.xml │ ├── dri2.xml │ ├── dri3.xml │ ├── ge.xml │ ├── glx.xml │ ├── present.xml │ ├── randr.xml │ ├── record.xml │ ├── render.xml │ ├── res.xml │ ├── screensaver.xml │ ├── shape.xml │ ├── shm.xml │ ├── sync.xml │ ├── xc_misc.xml │ ├── xcb.xsd │ ├── xevie.xml │ ├── xf86dri.xml │ ├── xf86vidmode.xml │ ├── xfixes.xml │ ├── xinerama.xml │ ├── xinput.xml │ ├── xkb.xml │ ├── xprint.xml │ ├── xproto.xml │ ├── xselinux.xml │ ├── xtest.xml │ ├── xv.xml │ └── xvmc.xml ├── xcb-proto.pc.in └── xcbgen │ ├── Makefile.am │ ├── __init__.py │ ├── align.py │ ├── error.py │ ├── expr.py │ ├── matcher.py │ ├── state.py │ └── xtypes.py ├── xcbgen-rs ├── Cargo.toml └── src │ ├── defs │ ├── alignment.rs │ ├── doc.rs │ ├── expression.rs │ ├── fields.rs │ ├── mod.rs │ └── top_level.rs │ ├── lib.rs │ ├── parser.rs │ └── resolver │ ├── align_checker.rs │ ├── extra_fields_inserter.rs │ ├── field_ref_resolver.rs │ ├── mod.rs │ ├── nesting_checker.rs │ ├── param_ref_gatherer.rs │ └── type_resolver.rs ├── xkbcommon-example ├── Cargo.toml └── src │ └── main.rs └── xtrace-example ├── Cargo.toml └── src ├── connection.rs ├── connection_inner.rs ├── forwarder.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: Automatic merge 3 | conditions: 4 | - label!=no-mergify 5 | - "#approved-reviews-by>=1" 6 | actions: 7 | merge: 8 | method: merge 9 | delete_head_branch: {} 10 | dismiss_reviews: {} 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "generator", 4 | "extract-generated-code-doc", 5 | "xcbgen-rs", 6 | "x11rb-protocol", 7 | "x11rb", 8 | "x11rb-async", 9 | "cairo-example", 10 | "xtrace-example", 11 | "xkbcommon-example", 12 | ] 13 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2019 x11rb Contributers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROTO=xcb-proto-1.17.0 2 | PROTO_OUT=x11rb-protocol/src/protocol 3 | X11RB_OUT=x11rb/src/protocol 4 | ASYNC_OUT=x11rb-async/src/protocol 5 | 6 | generate: 7 | mkdir -p "$(PROTO_OUT)" "$(X11RB_OUT)" "$(ASYNC_OUT)" 8 | cargo run -p x11rb-generator -- "$(PROTO)/src" "$(PROTO_OUT)" "$(X11RB_OUT)" "$(ASYNC_OUT)" 9 | cargo run -p extract-generated-code-doc -- "doc/generated_code.md" "$(PROTO_OUT)/xproto.rs" "$(X11RB_OUT)/xproto.rs" 10 | 11 | .PHONY: generate 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # X11 rust bindings 2 | 3 | [![GitHub Actions Status](https://github.com/psychon/x11rb/workflows/CI/badge.svg)](https://github.com/psychon/x11rb/actions) 4 | [![Crate](https://img.shields.io/crates/v/x11rb.svg)](https://crates.io/crates/x11rb) 5 | [![API](https://docs.rs/x11rb/badge.svg)](https://docs.rs/x11rb) 6 | ![Minimum rustc version](https://img.shields.io/badge/rustc-1.64+-lightgray.svg) 7 | [![License](https://img.shields.io/crates/l/x11rb.svg)](https://github.com/psychon/x11rb#license) 8 | 9 | Feel free to open issues for any problems or questions you might have. 10 | A comparison with some other Rust X11 libraries is available in an [extra 11 | document](doc/comparison.md). 12 | 13 | 14 | ## Building 15 | 16 | This crate uses a code generator that is implemented in Rust. A copy of the 17 | generated code is included, so you do not need to run the generator unless 18 | you have modified the definitions or the generator itself. 19 | 20 | The code generator uses the X11 XML description from `xcb-proto`. A copy of 21 | xcb-proto that comes with the source code is used. 22 | 23 | The interaction with libxcb via `XCBConnection` requires at least libxcb 1.12. 24 | 25 | 26 | ## Crate features 27 | 28 | Most X11 extensions are feature-gated. For example, to use the shared memory 29 | extension, the `shm` feature has to be enabled. 30 | 31 | The `all-extensions` feature just enables all X11 extensions. 32 | 33 | Additionally, the `allow-unsafe-code` feature enables `XCBConnection`. This uses 34 | `libxcb` internally and allows sharing the underlying `xcb_connection_t` pointer 35 | with other code. 36 | 37 | The `cursor` feature enables X11 cursor support via the `cursor` module. This 38 | module helps with loading cursors from the current cursor theme. 39 | 40 | 41 | ## Current state 42 | 43 | The full X11 protocol is supported by this library. All extensions that are 44 | available in `xcb-proto` can be used and even [FD 45 | passing](x11rb/examples/shared_memory.rs) with the server is supported. 46 | 47 | The changelog is available in a [separate file](doc/changelog.md). 48 | 49 | 50 | ## License 51 | 52 | Licensed under either of 53 | 54 | * Apache License, Version 2.0 55 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 56 | * MIT license 57 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 58 | 59 | at your option. 60 | 61 | The subdirectory xcb-proto-1.17.0 contains a vendored copy of the 62 | package of the same name. It is covered by the MIT license. See 63 | [xcb-proto-1.17.0/COPYING](xcb-proto-1.17.0/COPYING) for details. 64 | 65 | ## Contribution 66 | 67 | Unless you explicitly state otherwise, any contribution intentionally submitted 68 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 69 | dual licensed as above, without any additional terms or conditions. 70 | -------------------------------------------------------------------------------- /cairo-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cairo-example" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies.x11rb] 10 | path = "../x11rb" 11 | features = ["allow-unsafe-code", "render"] 12 | 13 | [dependencies.cairo-rs] 14 | version = "0.16" 15 | features = ["xcb"] 16 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | msrv = "1.64.0" 2 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | # Coverage for examples is not interesting, so exclude examples 3 | - "x11rb/examples" 4 | - "cairo-example" 5 | - "xtrace-example" 6 | 7 | coverage: 8 | status: 9 | project: 10 | default: 11 | informational: true 12 | -------------------------------------------------------------------------------- /doc/comparison.md: -------------------------------------------------------------------------------- 1 | # Comparison with other Rust X11 libraries 2 | 3 | My main motivation for writing this library is fun and getting some experience 4 | with Rust. As such, "there is already a library" does not count. 5 | 6 | [![Motivation](https://imgs.xkcd.com/comics/standards.png)](https://xkcd.com/927/) 7 | 8 | (The image is licensed under a Creative Commons Attribution-NonCommercial 2.5 License) 9 | 10 | However, since you brought this topic up, let us look at some other libraries 11 | that allow accessing an X11 server from Rust. If you know about more libraries 12 | or want me to know that I got something wrong, feel free to tell me about them, 13 | for example by opening an issue. 14 | 15 | 16 | ## xproto-rs 17 | 18 | I only found this [on crates.io](https://crates.io/crates/xproto). The 19 | Repository link is broken and documentation looks like someone dumped the result 20 | of `bindgen` on the Xlib headers into a crate. 21 | 22 | 23 | ## xrb 24 | 25 | The [Pure Rust bindings for X11](https://github.com/DaMrNelson/xrb) seem to 26 | contain hand-written code for parsing and sending X11 messages. Also, its README 27 | currently claims that this project is in an early state. 28 | 29 | 30 | ## x11-rs 31 | 32 | This seems to provide FFI wrappers around Xlib and various related libraries. I 33 | recently heard about this library because [its 34 | unsafe](https://github.com/erlepereira/x11-rs/issues/99) code is 35 | [unsound](https://github.com/rust-lang/rust/issues/52898) and causes undefined 36 | behaviour. This is basically all I know and heard about this library. 37 | 38 | 39 | ## rust-xcb 40 | 41 | This project uses xcb-proto, the XML description of the X11 protocol that comes 42 | from the libxcb project. Based on this XML, code is generated that provides a 43 | foreign function interface to the various libxcb libraries. Due to its FFI 44 | nature, this project contains many instances of `unsafe`. Worse, its 45 | `basic_window` example indicates that users of this library must also use 46 | `unsafe` for [handling 47 | events](https://github.com/rtbo/rust-xcb/blob/d7cb614a6fe9f4424ed26939a5720770f84acd05/examples/basic_window.rs#L66). 48 | How can one ever be sure that there is nothing wrong with the `unsafe` one 49 | writes? 50 | 51 | I briefly looked at this project and found a [NULL pointer 52 | dereference](https://github.com/rtbo/rust-xcb/issues/64) and re-discovered an 53 | already known [leak](https://github.com/rtbo/rust-xcb/issues/57). 54 | 55 | 56 | ## xcb-dl 57 | 58 | The [xcb-dl](https://github.com/mahkoh/xcb-dl) project seems to be similar to 59 | rust-xcb: It provides FFI bindings to the libxcb C library. It uses the XML 60 | description to generate this binding and the necessary types. It thus also has 61 | many instances of `unsafe` and requires `unsafe` for using it. 62 | 63 | The big difference to rust-xcb is that this library uses the libloading crate to 64 | dynamically load function pointers at runtime instead of linking at compile 65 | time. 66 | 67 | 68 | ## breadx 69 | 70 | "An implementation of the X Window System Protocol in Rust. 100% safe and mutex-free." 71 | 72 | [breadx](https://github.com/bread-graphics/breadx) uses the same generated bindings as `x11rb`. The project 73 | claims that it is "generally faster (awaiting verification)". 74 | 75 | One highlight is support for async. However, interactions with the X11 server 76 | require `&mut`-access to the 77 | [Display](https://docs.rs/breadx/0.1.3/breadx/display/struct.Display.html). 78 | Thus, the Display needs to be wrapped in a `Mutex` or `RefCell` to share with 79 | multiple async tasks. 80 | 81 | Another highlight is `no_std` support. 82 | 83 | 84 | ## xcb-sys 85 | 86 | The [xcb-sys crate](https://crates.io/crates/xcb-sys) uses bindgen to 87 | automatically generate a Rust API from libxcb's C headers. Everything is unsafe 88 | and as a user you are basically writing C code in Rust. 89 | 90 | Put differently: There is lots of existing documentation (for C code) and 91 | reference code (written in C) that you can use. It is pretty much directly 92 | transferable to this library. 93 | 94 | 95 | ## x11rb (this project) 96 | 97 | x11rb, the x11 rust bindings, is based on the XML description of the X11 98 | protocol that comes from the libxcb project, similar to rust-xcb. However, 99 | instead of providing a foreign function interface to libxcb, the generated code 100 | reimplements the serialising and unserialising code that is provided by libxcb. 101 | libxcb is only used for receiving and sending opaque packets. 102 | 103 | This reimplementation tries to avoid uses of `unsafe` and thus should enjoy 104 | Rust's usual safety guarantees. After all, the best way to trust the unsafe code 105 | coming out of your code generator is if your code generator does not generate 106 | any unsafe code. Unsafe code is currently necessary for FFI binding to a handful 107 | of functions from libxcb (see `src/xcb_ffi.rs`). 108 | 109 | This means that this project is even safer than libxcb, because libxcb forces 110 | its users to blindly trust length fields that come from the X11 server. 111 | 112 | The downside of this is possibly slower code. However, if your bottleneck is in 113 | talking to the X11 server, you are seriously doing something wrong. 114 | 115 | Examples of the generated code [can be found here](generated_code.md). Feel 116 | free to suggest improvements to it. 117 | -------------------------------------------------------------------------------- /doc/making_a_release.md: -------------------------------------------------------------------------------- 1 | ``` 2 | vim doc/changelog.md # Update the changelog 3 | vim {x11rb,x11rb-async,x11rb-protocol}/Cargo.toml # Update version number in all published crates 4 | vim x11rb/Cargo.toml # Update version number in x11rb-protocol dependency 5 | vim x11rb-async/Cargo.toml # Update version number in x11rb and x11rb-protocol dependency 6 | # Now get these changes merged 7 | git tag -a -m "Version 0.2.0" v0.2.0 8 | cargo publish --dry-run -p x11rb-protocol 9 | cargo publish -p x11rb-protocol 10 | cargo publish --dry-run -p x11rb 11 | cargo publish -p x11rb 12 | cargo publish --dry-run -p x11rb-async 13 | cargo publish -p x11rb-async 14 | git push origin v0.2.0 15 | ``` 16 | -------------------------------------------------------------------------------- /extract-generated-code-doc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "extract-generated-code-doc" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | regex = "1.10" 11 | -------------------------------------------------------------------------------- /extract-generated-code-doc/src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny( 2 | rust_2018_idioms, 3 | trivial_numeric_casts, 4 | unsafe_code, 5 | unreachable_pub, 6 | unused, 7 | unused_qualifications 8 | )] 9 | #![forbid(unsafe_code)] 10 | // This crate is not shipped to users and does not follow our MSRV 11 | #![allow(clippy::incompatible_msrv)] 12 | 13 | use std::io::Error as IoError; 14 | use std::path::Path; 15 | use std::process::ExitCode; 16 | 17 | pub mod doc; 18 | 19 | /// Representation of a file split on empty lines 20 | #[derive(Debug)] 21 | pub struct Sections(Vec); 22 | 23 | impl Sections { 24 | fn new_from_empty_lines(input: &str) -> Self { 25 | let mut vec = Vec::new(); 26 | let mut previous = None; 27 | for entry in input.split("\n\n").map(String::from) { 28 | if let Some(prev) = previous { 29 | // If the new entry starts with indentation, it belongs to the previous section 30 | if Some(&b' ') == entry.as_bytes().first() { 31 | previous = Some(format!("{}\n\n{}", prev, entry)); 32 | } else { 33 | vec.push(prev); 34 | previous = Some(entry); 35 | } 36 | } else { 37 | previous = Some(entry); 38 | } 39 | } 40 | vec.extend(previous); 41 | Self(vec) 42 | } 43 | 44 | fn new_from_trait(input: &str) -> Self { 45 | let mut result = input 46 | .split(" }") 47 | .map(|s| format!(" {} }}", s.trim_start())) 48 | .collect::>(); 49 | if let Some(first) = result.first_mut() { 50 | if let Some((_, rest)) = 51 | first.split_once("pub trait ConnectionExt: RequestConnection {\n") 52 | { 53 | *first = rest.to_string(); 54 | } 55 | } 56 | Self(result) 57 | } 58 | 59 | fn get_range_by_index(&self, range: std::ops::RangeTo) -> String { 60 | self.0[range].join("\n\n") 61 | } 62 | 63 | fn get_by_needle(&self, needle: &str) -> &str { 64 | self.0 65 | .iter() 66 | .find(|text| text.contains(needle)) 67 | .unwrap_or_else(|| panic!("Could not find text by needle '{needle}'")) 68 | } 69 | } 70 | 71 | fn load_sections(path: &Path) -> Result { 72 | Ok(Sections::new_from_empty_lines(&std::fs::read_to_string( 73 | path, 74 | )?)) 75 | } 76 | 77 | fn main() -> Result { 78 | let args: Vec<_> = std::env::args_os().collect(); 79 | if args.len() != 4 { 80 | eprintln!("USAGE:"); 81 | eprintln!( 82 | " {} ", 83 | args[0].to_string_lossy() 84 | ); 85 | return Ok(ExitCode::FAILURE); 86 | } 87 | let output_file = Path::new(&args[1]); 88 | let proto_xproto = load_sections(Path::new(&args[2]))?; 89 | let x11rb_xproto = load_sections(Path::new(&args[3]))?; 90 | 91 | let output = doc::generate(&proto_xproto, &x11rb_xproto); 92 | std::fs::write(output_file, output)?; 93 | 94 | Ok(ExitCode::SUCCESS) 95 | } 96 | -------------------------------------------------------------------------------- /generator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "x11rb-generator" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | roxmltree = "0.19.0" 11 | xcbgen = { path = "../xcbgen-rs" } 12 | regex = "1.10" 13 | -------------------------------------------------------------------------------- /generator/src/generator/namespace/async_switch.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// Emit sync or async code. 4 | #[derive(Debug, PartialEq)] 5 | pub(super) enum ImplMode { 6 | Sync, 7 | Async, 8 | } 9 | 10 | impl ImplMode { 11 | pub(super) fn fn_async(&self) -> impl fmt::Display { 12 | match self { 13 | ImplMode::Sync => "", 14 | ImplMode::Async => "async ", 15 | } 16 | } 17 | 18 | pub(super) fn dot_await(&self) -> impl fmt::Display { 19 | match self { 20 | ImplMode::Sync => "", 21 | ImplMode::Async => ".await", 22 | } 23 | } 24 | 25 | pub(super) fn ret_ty(&self, inner: impl fmt::Display, named: bool) -> impl fmt::Display { 26 | let (begin, end) = match self { 27 | ImplMode::Sync => ("", "".to_string()), 28 | ImplMode::Async => ( 29 | "Pin + Send + '{}>>", if named { "future" } else { "_" },), 31 | ), 32 | }; 33 | 34 | format!("{}{}{}", begin, inner, end) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /generator/src/generator/output.rs: -------------------------------------------------------------------------------- 1 | pub(super) struct Output { 2 | data: String, 3 | indent: usize, 4 | } 5 | 6 | impl Output { 7 | #[inline] 8 | pub(super) fn new() -> Self { 9 | Self { 10 | data: String::new(), 11 | indent: 0, 12 | } 13 | } 14 | 15 | #[inline] 16 | pub(super) fn into_data(self) -> String { 17 | assert_eq!(self.indent, 0); 18 | self.data 19 | } 20 | 21 | #[inline] 22 | fn incr_indent(&mut self) { 23 | self.indent += 1; 24 | } 25 | 26 | #[inline] 27 | fn decr_indent(&mut self) { 28 | self.indent -= 1; 29 | } 30 | 31 | #[inline] 32 | pub(super) fn indented(&mut self, f: impl FnOnce(&mut Self)) { 33 | self.incr_indent(); 34 | f(self); 35 | self.decr_indent(); 36 | } 37 | 38 | #[inline] 39 | pub(super) fn indent(&mut self) -> Indented<'_> { 40 | Indented { out: self } 41 | } 42 | } 43 | 44 | impl std::fmt::Write for Output { 45 | fn write_str(&mut self, s: &str) -> std::fmt::Result { 46 | for (i, line) in s.split('\n').enumerate() { 47 | if i != 0 { 48 | self.data.push('\n'); 49 | } 50 | if !line.is_empty() { 51 | if self.data.ends_with('\n') { 52 | for _ in 0..self.indent { 53 | self.data.push_str(" "); 54 | } 55 | } 56 | self.data.push_str(line); 57 | } 58 | } 59 | Ok(()) 60 | } 61 | } 62 | 63 | pub(super) struct Indented<'a> { 64 | out: &'a mut Output, 65 | } 66 | 67 | impl std::fmt::Write for Indented<'_> { 68 | fn write_str(&mut self, s: &str) -> std::fmt::Result { 69 | self.out.incr_indent(); 70 | let r = std::fmt::Write::write_str(&mut self.out, s); 71 | self.out.decr_indent(); 72 | r 73 | } 74 | } 75 | 76 | macro_rules! out { 77 | ($out:expr, $($args:tt)+) => { 78 | { 79 | use std::fmt::Write as _; 80 | write!($out, $($args)+).unwrap(); 81 | } 82 | }; 83 | } 84 | 85 | macro_rules! outln { 86 | ($out:expr, $($args:tt)+) => { 87 | { 88 | use std::fmt::Write as _; 89 | writeln!($out, $($args)+).unwrap(); 90 | } 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /x11rb-async/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "x11rb-async" 3 | version = "0.13.1" 4 | description = "Async Rust bindings to X11" 5 | authors = [ 6 | "Uli Schlachter ", 7 | "Eduardo Sánchez Muñoz ", 8 | "notgull ", 9 | ] 10 | repository = "https://github.com/psychon/x11rb" 11 | readme = "../README.md" 12 | edition = "2021" 13 | rust-version = "1.64" 14 | license = "MIT OR Apache-2.0" 15 | keywords = ["xcb", "X11", "async"] 16 | 17 | [dependencies] 18 | async-io = "2.3" 19 | async-lock = "3.3" 20 | blocking = "1.5" 21 | event-listener = "5.0" 22 | futures-lite = "2.2" 23 | tracing = { version = "0.1.33", default-features = false } 24 | x11rb = { version = "0.13.1", path = "../x11rb", default-features = false } 25 | x11rb-protocol = { version = "0.13.1", default-features = false, features = ["std"], path = "../x11rb-protocol" } 26 | 27 | [features] 28 | # Enable this feature to enable all the X11 extensions 29 | all-extensions = [ 30 | "x11rb-protocol/all-extensions", 31 | "composite", 32 | "damage", 33 | "dbe", 34 | "dpms", 35 | "dri2", 36 | "dri3", 37 | "glx", 38 | "present", 39 | "randr", 40 | "record", 41 | "render", 42 | "res", 43 | "screensaver", 44 | "shape", 45 | "shm", 46 | "sync", 47 | "xevie", 48 | "xf86dri", 49 | "xf86vidmode", 50 | "xfixes", 51 | "xinerama", 52 | "xinput", 53 | "xkb", 54 | "xprint", 55 | "xselinux", 56 | "xtest", 57 | "xv", 58 | "xvmc" 59 | ] 60 | 61 | # Enable extra traits on protocol types. 62 | extra-traits = ["x11rb-protocol/extra-traits"] 63 | 64 | composite = ["x11rb-protocol/composite", "xfixes"] 65 | damage = ["x11rb-protocol/damage", "xfixes"] 66 | dbe = ["x11rb-protocol/dbe"] 67 | dpms = ["x11rb-protocol/dpms"] 68 | dri2 = ["x11rb-protocol/dri2"] 69 | dri3 = ["x11rb-protocol/dri3"] 70 | glx = ["x11rb-protocol/glx"] 71 | present = ["x11rb-protocol/present", "randr", "xfixes", "sync"] 72 | randr = ["x11rb-protocol/randr", "render"] 73 | record = ["x11rb-protocol/record"] 74 | render = ["x11rb-protocol/render"] 75 | res = ["x11rb-protocol/res"] 76 | screensaver = ["x11rb-protocol/screensaver"] 77 | shape = ["x11rb-protocol/shape"] 78 | shm = ["x11rb-protocol/shm"] 79 | sync = ["x11rb-protocol/sync"] 80 | xevie = ["x11rb-protocol/xevie"] 81 | xf86dri = ["x11rb-protocol/xf86dri"] 82 | xf86vidmode = ["x11rb-protocol/xf86vidmode"] 83 | xfixes = ["x11rb-protocol/xfixes", "render", "shape"] 84 | xinerama = ["x11rb-protocol/xinerama"] 85 | xinput = ["x11rb-protocol/xinput", "xfixes"] 86 | xkb = ["x11rb-protocol/xkb"] 87 | xprint = ["x11rb-protocol/xprint"] 88 | xselinux = ["x11rb-protocol/xselinux"] 89 | xtest = ["x11rb-protocol/xtest"] 90 | xv = ["x11rb-protocol/xv", "shm"] 91 | xvmc = ["x11rb-protocol/xvmc", "xv"] 92 | 93 | allow-unsafe-code = ["x11rb/allow-unsafe-code"] 94 | 95 | [package.metadata.docs.rs] 96 | all-features = true 97 | 98 | [dev-dependencies] 99 | async-executor = "1.8" 100 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 101 | 102 | [target.'cfg(unix)'.dev-dependencies.rustix] 103 | version = "1.0" 104 | default-features = false 105 | features = ["mm", "pipe"] 106 | 107 | [[example]] 108 | name = "shared_memory_async" 109 | required-features = ["shm"] 110 | -------------------------------------------------------------------------------- /x11rb-async/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /x11rb-async/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /x11rb-async/src/lib.rs: -------------------------------------------------------------------------------- 1 | // This code is dual licensed under MIT OR Apache 2.0. 2 | 3 | //! Asynchronous X11 rust bindings. 4 | //! 5 | //! This library allows to interact with an X11 server from rust code. A connection to an X11 6 | //! server is represented by an implementation of the `Connection` trait. 7 | //! 8 | //! The client can interact with the server by sending requests. The server can answer requests and 9 | //! can also generate events. 10 | //! 11 | //! The examples that come with this library might be a good starting point for new users. 12 | //! 13 | //! 14 | //! # Feature flags 15 | //! 16 | //! This crate uses [feature 17 | //! flags](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section) to reduce 18 | //! the amount of compiled code. There are two kinds of feature flags available: 19 | //! 20 | //! * Feature flags for specific X11 extensions 21 | //! * Feature flags for additional functionality 22 | //! 23 | //! ## Feature flags for specific X11 extensions 24 | //! 25 | //! By default, only the core X11 protocol and X11 extensions that are needed internally are 26 | //! enabled. These are the `bigreq`, `ge` and `xc_misc` extensions. Further extensions need to be 27 | //! explicitly enabled via their feature flag: 28 | //! 29 | //! `composite`, `damage`, `dpms`, `dri2`, `dri3`, `glx`, `present`, `randr`, `record`, `render`, 30 | //! `res`, `screensaver`, `shape`, `shm`, `sync`, `xevie`, `xf86dri`, `xf86vidmode`, `xfixes`, 31 | //! `xinerama`, `xinput`, `xkb`, `xprint`, `xselinux`, `xtest`, `xv`, `xvmc`. 32 | //! 33 | //! If you want to take the "I do not want to think about this"-approach, you can enable the 34 | //! `all-extensions` feature to just enable, well, all extensions. 35 | //! 36 | //! ## Feature flags for additional functionality 37 | //! 38 | //! Additionally, the following flags exist: 39 | //! * `allow-unsafe-code`: Enable the same feature in x11rb and implement 40 | //! [`blocking::BlockingConnection`] for [`x11rb::xcb_ffi::XCBConnection`] 41 | //! * `extra-traits`: Implement extra traits for X11 types. This improves the output of the `Debug` 42 | //! impl and adds `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash` where possible. 43 | 44 | // A list of lints that are only #![deny] and not the stronger #![forbid]. Each one has a comment 45 | // explaining why it gets the weaker treatment. 46 | #![deny( 47 | // Contains unreachable_code and "?" generates an #[allow] for this 48 | unused, 49 | // #[derive] generates an #[allow] for this; not part of "unused" 50 | unused_qualifications, 51 | // Not everything in x11rb::protocol has doc comments 52 | missing_docs, 53 | )] 54 | #![forbid( 55 | missing_copy_implementations, 56 | missing_debug_implementations, 57 | rustdoc::private_doc_tests, 58 | rust_2018_idioms, 59 | //single_use_lifetimes, 60 | trivial_casts, 61 | trivial_numeric_casts, 62 | unreachable_pub, 63 | unused_must_use, 64 | unused_results, 65 | clippy::cast_lossless, 66 | clippy::needless_pass_by_value, 67 | )] 68 | #![cfg_attr(not(feature = "allow-unsafe-code"), forbid(unsafe_code))] 69 | 70 | // -- Public Modules -- 71 | 72 | pub mod blocking; 73 | pub mod connection; 74 | #[allow(clippy::type_complexity, missing_docs)] 75 | #[rustfmt::skip] 76 | pub mod protocol; 77 | pub mod rust_connection; 78 | 79 | #[doc(inline)] 80 | pub use x11rb::{errors, x11_utils}; 81 | 82 | #[doc(inline)] 83 | pub use x11rb_protocol::SequenceNumber; 84 | 85 | // -- Private Modules -- 86 | 87 | mod cookie; 88 | 89 | pub use cookie::{Cookie, CookieWithFds, VoidCookie}; 90 | 91 | pub mod utils { 92 | //! Utility functions that are not specific to X11. 93 | pub use x11rb::utils::RawFdContainer; 94 | } 95 | -------------------------------------------------------------------------------- /x11rb-async/src/protocol/bigreq.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the `BigRequests` X11 extension. 5 | 6 | #![allow(clippy::too_many_arguments)] 7 | 8 | #[allow(unused_imports)] 9 | use std::borrow::Cow; 10 | #[allow(unused_imports)] 11 | use std::convert::TryInto; 12 | #[allow(unused_imports)] 13 | use crate::utils::RawFdContainer; 14 | #[allow(unused_imports)] 15 | use crate::x11_utils::{Request, RequestHeader, Serialize, TryParse, TryParseFd}; 16 | use std::io::IoSlice; 17 | use crate::connection::RequestConnection; 18 | #[allow(unused_imports)] 19 | use crate::connection::Connection as X11Connection; 20 | #[allow(unused_imports)] 21 | use crate::cookie::{Cookie, CookieWithFds, VoidCookie}; 22 | use crate::errors::ConnectionError; 23 | #[allow(unused_imports)] 24 | use crate::errors::ReplyOrIdError; 25 | use std::future::Future; 26 | use std::pin::Pin; 27 | 28 | pub use x11rb_protocol::protocol::bigreq::*; 29 | 30 | /// Get the major opcode of this extension 31 | async fn major_opcode(conn: &Conn) -> Result { 32 | let info = conn.extension_information(X11_EXTENSION_NAME).await?; 33 | let info = info.ok_or(ConnectionError::UnsupportedExtension)?; 34 | Ok(info.major_opcode) 35 | } 36 | 37 | /// Enable the BIG-REQUESTS extension. 38 | /// 39 | /// This enables the BIG-REQUESTS extension, which allows for requests larger than 40 | /// 262140 bytes in length. When enabled, if the 16-bit length field is zero, it 41 | /// is immediately followed by a 32-bit length field specifying the length of the 42 | /// request in 4-byte units. 43 | pub async fn enable(conn: &Conn) -> Result, ConnectionError> 44 | where 45 | Conn: RequestConnection + ?Sized, 46 | { 47 | let request0 = EnableRequest; 48 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 49 | let slices = [IoSlice::new(&bytes[0])]; 50 | assert_eq!(slices.len(), bytes.len()); 51 | conn.send_request_with_reply(&slices, fds).await 52 | } 53 | /// Extension trait defining the requests of this extension. 54 | pub trait ConnectionExt: RequestConnection { 55 | /// Enable the BIG-REQUESTS extension. 56 | /// 57 | /// This enables the BIG-REQUESTS extension, which allows for requests larger than 58 | /// 262140 bytes in length. When enabled, if the 16-bit length field is zero, it 59 | /// is immediately followed by a 32-bit length field specifying the length of the 60 | /// request in 4-byte units. 61 | fn bigreq_enable(&self) -> Pin, ConnectionError>> + Send + '_>> 62 | { 63 | Box::pin(enable(self)) 64 | } 65 | } 66 | 67 | impl ConnectionExt for C {} 68 | -------------------------------------------------------------------------------- /x11rb-async/src/protocol/ge.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the `GenericEvent` X11 extension. 5 | 6 | #![allow(clippy::too_many_arguments)] 7 | 8 | #[allow(unused_imports)] 9 | use std::borrow::Cow; 10 | #[allow(unused_imports)] 11 | use std::convert::TryInto; 12 | #[allow(unused_imports)] 13 | use crate::utils::RawFdContainer; 14 | #[allow(unused_imports)] 15 | use crate::x11_utils::{Request, RequestHeader, Serialize, TryParse, TryParseFd}; 16 | use std::io::IoSlice; 17 | use crate::connection::RequestConnection; 18 | #[allow(unused_imports)] 19 | use crate::connection::Connection as X11Connection; 20 | #[allow(unused_imports)] 21 | use crate::cookie::{Cookie, CookieWithFds, VoidCookie}; 22 | use crate::errors::ConnectionError; 23 | #[allow(unused_imports)] 24 | use crate::errors::ReplyOrIdError; 25 | use std::future::Future; 26 | use std::pin::Pin; 27 | 28 | pub use x11rb_protocol::protocol::ge::*; 29 | 30 | /// Get the major opcode of this extension 31 | async fn major_opcode(conn: &Conn) -> Result { 32 | let info = conn.extension_information(X11_EXTENSION_NAME).await?; 33 | let info = info.ok_or(ConnectionError::UnsupportedExtension)?; 34 | Ok(info.major_opcode) 35 | } 36 | 37 | pub async fn query_version(conn: &Conn, client_major_version: u16, client_minor_version: u16) -> Result, ConnectionError> 38 | where 39 | Conn: RequestConnection + ?Sized, 40 | { 41 | let request0 = QueryVersionRequest { 42 | client_major_version, 43 | client_minor_version, 44 | }; 45 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 46 | let slices = [IoSlice::new(&bytes[0])]; 47 | assert_eq!(slices.len(), bytes.len()); 48 | conn.send_request_with_reply(&slices, fds).await 49 | } 50 | /// Extension trait defining the requests of this extension. 51 | pub trait ConnectionExt: RequestConnection { 52 | fn ge_query_version(&self, client_major_version: u16, client_minor_version: u16) -> Pin, ConnectionError>> + Send + '_>> 53 | { 54 | Box::pin(query_version(self, client_major_version, client_minor_version)) 55 | } 56 | } 57 | 58 | impl ConnectionExt for C {} 59 | -------------------------------------------------------------------------------- /x11rb-async/src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the X11 protocol. 5 | //! 6 | //! Each sub-module of this module corresponds to one X11 extension. It contains all the 7 | //! definitions from that extension. The core X11 protocol is in [`xproto`](xproto/index.html). 8 | 9 | // Clippy does not like some names from the XML. 10 | #![allow(clippy::upper_case_acronyms)] 11 | // This is not easy to fix, so ignore it. 12 | #![allow(clippy::needless_borrow, clippy::needless_lifetimes)] 13 | pub mod xproto; 14 | pub mod bigreq; 15 | #[cfg(feature = "composite")] 16 | pub mod composite; 17 | #[cfg(feature = "damage")] 18 | pub mod damage; 19 | #[cfg(feature = "dbe")] 20 | pub mod dbe; 21 | #[cfg(feature = "dpms")] 22 | pub mod dpms; 23 | #[cfg(feature = "dri2")] 24 | pub mod dri2; 25 | #[cfg(feature = "dri3")] 26 | pub mod dri3; 27 | pub mod ge; 28 | #[cfg(feature = "glx")] 29 | pub mod glx; 30 | #[cfg(feature = "present")] 31 | pub mod present; 32 | #[cfg(feature = "randr")] 33 | pub mod randr; 34 | #[cfg(feature = "record")] 35 | pub mod record; 36 | #[cfg(feature = "render")] 37 | pub mod render; 38 | #[cfg(feature = "res")] 39 | pub mod res; 40 | #[cfg(feature = "screensaver")] 41 | pub mod screensaver; 42 | #[cfg(feature = "shape")] 43 | pub mod shape; 44 | #[cfg(feature = "shm")] 45 | pub mod shm; 46 | #[cfg(feature = "sync")] 47 | pub mod sync; 48 | pub mod xc_misc; 49 | #[cfg(feature = "xevie")] 50 | pub mod xevie; 51 | #[cfg(feature = "xf86dri")] 52 | pub mod xf86dri; 53 | #[cfg(feature = "xf86vidmode")] 54 | pub mod xf86vidmode; 55 | #[cfg(feature = "xfixes")] 56 | pub mod xfixes; 57 | #[cfg(feature = "xinerama")] 58 | pub mod xinerama; 59 | #[cfg(feature = "xinput")] 60 | pub mod xinput; 61 | #[cfg(feature = "xkb")] 62 | pub mod xkb; 63 | #[cfg(feature = "xprint")] 64 | pub mod xprint; 65 | #[cfg(feature = "xselinux")] 66 | pub mod xselinux; 67 | #[cfg(feature = "xtest")] 68 | pub mod xtest; 69 | #[cfg(feature = "xv")] 70 | pub mod xv; 71 | #[cfg(feature = "xvmc")] 72 | pub mod xvmc; 73 | 74 | pub use x11rb_protocol::protocol::Request; 75 | pub use x11rb_protocol::protocol::Reply; 76 | pub use x11rb_protocol::protocol::ErrorKind; 77 | pub use x11rb_protocol::protocol::Event; 78 | -------------------------------------------------------------------------------- /x11rb-async/src/protocol/xc_misc.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the `XCMisc` X11 extension. 5 | 6 | #![allow(clippy::too_many_arguments)] 7 | 8 | #[allow(unused_imports)] 9 | use std::borrow::Cow; 10 | #[allow(unused_imports)] 11 | use std::convert::TryInto; 12 | #[allow(unused_imports)] 13 | use crate::utils::RawFdContainer; 14 | #[allow(unused_imports)] 15 | use crate::x11_utils::{Request, RequestHeader, Serialize, TryParse, TryParseFd}; 16 | use std::io::IoSlice; 17 | use crate::connection::RequestConnection; 18 | #[allow(unused_imports)] 19 | use crate::connection::Connection as X11Connection; 20 | #[allow(unused_imports)] 21 | use crate::cookie::{Cookie, CookieWithFds, VoidCookie}; 22 | use crate::errors::ConnectionError; 23 | #[allow(unused_imports)] 24 | use crate::errors::ReplyOrIdError; 25 | use std::future::Future; 26 | use std::pin::Pin; 27 | 28 | pub use x11rb_protocol::protocol::xc_misc::*; 29 | 30 | /// Get the major opcode of this extension 31 | async fn major_opcode(conn: &Conn) -> Result { 32 | let info = conn.extension_information(X11_EXTENSION_NAME).await?; 33 | let info = info.ok_or(ConnectionError::UnsupportedExtension)?; 34 | Ok(info.major_opcode) 35 | } 36 | 37 | pub async fn get_version(conn: &Conn, client_major_version: u16, client_minor_version: u16) -> Result, ConnectionError> 38 | where 39 | Conn: RequestConnection + ?Sized, 40 | { 41 | let request0 = GetVersionRequest { 42 | client_major_version, 43 | client_minor_version, 44 | }; 45 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 46 | let slices = [IoSlice::new(&bytes[0])]; 47 | assert_eq!(slices.len(), bytes.len()); 48 | conn.send_request_with_reply(&slices, fds).await 49 | } 50 | pub async fn get_xid_range(conn: &Conn) -> Result, ConnectionError> 51 | where 52 | Conn: RequestConnection + ?Sized, 53 | { 54 | let request0 = GetXIDRangeRequest; 55 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 56 | let slices = [IoSlice::new(&bytes[0])]; 57 | assert_eq!(slices.len(), bytes.len()); 58 | conn.send_request_with_reply(&slices, fds).await 59 | } 60 | pub async fn get_xid_list(conn: &Conn, count: u32) -> Result, ConnectionError> 61 | where 62 | Conn: RequestConnection + ?Sized, 63 | { 64 | let request0 = GetXIDListRequest { 65 | count, 66 | }; 67 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 68 | let slices = [IoSlice::new(&bytes[0])]; 69 | assert_eq!(slices.len(), bytes.len()); 70 | conn.send_request_with_reply(&slices, fds).await 71 | } 72 | /// Extension trait defining the requests of this extension. 73 | pub trait ConnectionExt: RequestConnection { 74 | fn xc_misc_get_version(&self, client_major_version: u16, client_minor_version: u16) -> Pin, ConnectionError>> + Send + '_>> 75 | { 76 | Box::pin(get_version(self, client_major_version, client_minor_version)) 77 | } 78 | fn xc_misc_get_xid_range(&self) -> Pin, ConnectionError>> + Send + '_>> 79 | { 80 | Box::pin(get_xid_range(self)) 81 | } 82 | fn xc_misc_get_xid_list(&self, count: u32) -> Pin, ConnectionError>> + Send + '_>> 83 | { 84 | Box::pin(get_xid_list(self, count)) 85 | } 86 | } 87 | 88 | impl ConnectionExt for C {} 89 | -------------------------------------------------------------------------------- /x11rb-async/src/protocol/xevie.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the `Xevie` X11 extension. 5 | 6 | #![allow(clippy::too_many_arguments)] 7 | 8 | #[allow(unused_imports)] 9 | use std::borrow::Cow; 10 | #[allow(unused_imports)] 11 | use std::convert::TryInto; 12 | #[allow(unused_imports)] 13 | use crate::utils::RawFdContainer; 14 | #[allow(unused_imports)] 15 | use crate::x11_utils::{Request, RequestHeader, Serialize, TryParse, TryParseFd}; 16 | use std::io::IoSlice; 17 | use crate::connection::RequestConnection; 18 | #[allow(unused_imports)] 19 | use crate::connection::Connection as X11Connection; 20 | #[allow(unused_imports)] 21 | use crate::cookie::{Cookie, CookieWithFds, VoidCookie}; 22 | use crate::errors::ConnectionError; 23 | #[allow(unused_imports)] 24 | use crate::errors::ReplyOrIdError; 25 | use std::future::Future; 26 | use std::pin::Pin; 27 | 28 | pub use x11rb_protocol::protocol::xevie::*; 29 | 30 | /// Get the major opcode of this extension 31 | async fn major_opcode(conn: &Conn) -> Result { 32 | let info = conn.extension_information(X11_EXTENSION_NAME).await?; 33 | let info = info.ok_or(ConnectionError::UnsupportedExtension)?; 34 | Ok(info.major_opcode) 35 | } 36 | 37 | pub async fn query_version(conn: &Conn, client_major_version: u16, client_minor_version: u16) -> Result, ConnectionError> 38 | where 39 | Conn: RequestConnection + ?Sized, 40 | { 41 | let request0 = QueryVersionRequest { 42 | client_major_version, 43 | client_minor_version, 44 | }; 45 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 46 | let slices = [IoSlice::new(&bytes[0])]; 47 | assert_eq!(slices.len(), bytes.len()); 48 | conn.send_request_with_reply(&slices, fds).await 49 | } 50 | pub async fn start(conn: &Conn, screen: u32) -> Result, ConnectionError> 51 | where 52 | Conn: RequestConnection + ?Sized, 53 | { 54 | let request0 = StartRequest { 55 | screen, 56 | }; 57 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 58 | let slices = [IoSlice::new(&bytes[0])]; 59 | assert_eq!(slices.len(), bytes.len()); 60 | conn.send_request_with_reply(&slices, fds).await 61 | } 62 | pub async fn end(conn: &Conn, cmap: u32) -> Result, ConnectionError> 63 | where 64 | Conn: RequestConnection + ?Sized, 65 | { 66 | let request0 = EndRequest { 67 | cmap, 68 | }; 69 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 70 | let slices = [IoSlice::new(&bytes[0])]; 71 | assert_eq!(slices.len(), bytes.len()); 72 | conn.send_request_with_reply(&slices, fds).await 73 | } 74 | pub async fn send(conn: &Conn, event: Event, data_type: u32) -> Result, ConnectionError> 75 | where 76 | Conn: RequestConnection + ?Sized, 77 | { 78 | let request0 = SendRequest { 79 | event, 80 | data_type, 81 | }; 82 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 83 | let slices = [IoSlice::new(&bytes[0])]; 84 | assert_eq!(slices.len(), bytes.len()); 85 | conn.send_request_with_reply(&slices, fds).await 86 | } 87 | pub async fn select_input(conn: &Conn, event_mask: u32) -> Result, ConnectionError> 88 | where 89 | Conn: RequestConnection + ?Sized, 90 | { 91 | let request0 = SelectInputRequest { 92 | event_mask, 93 | }; 94 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 95 | let slices = [IoSlice::new(&bytes[0])]; 96 | assert_eq!(slices.len(), bytes.len()); 97 | conn.send_request_with_reply(&slices, fds).await 98 | } 99 | /// Extension trait defining the requests of this extension. 100 | pub trait ConnectionExt: RequestConnection { 101 | fn xevie_query_version(&self, client_major_version: u16, client_minor_version: u16) -> Pin, ConnectionError>> + Send + '_>> 102 | { 103 | Box::pin(query_version(self, client_major_version, client_minor_version)) 104 | } 105 | fn xevie_start(&self, screen: u32) -> Pin, ConnectionError>> + Send + '_>> 106 | { 107 | Box::pin(start(self, screen)) 108 | } 109 | fn xevie_end(&self, cmap: u32) -> Pin, ConnectionError>> + Send + '_>> 110 | { 111 | Box::pin(end(self, cmap)) 112 | } 113 | fn xevie_send(&self, event: Event, data_type: u32) -> Pin, ConnectionError>> + Send + '_>> 114 | { 115 | Box::pin(send(self, event, data_type)) 116 | } 117 | fn xevie_select_input(&self, event_mask: u32) -> Pin, ConnectionError>> + Send + '_>> 118 | { 119 | Box::pin(select_input(self, event_mask)) 120 | } 121 | } 122 | 123 | impl ConnectionExt for C {} 124 | -------------------------------------------------------------------------------- /x11rb-async/src/protocol/xtest.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the `Test` X11 extension. 5 | 6 | #![allow(clippy::too_many_arguments)] 7 | 8 | #[allow(unused_imports)] 9 | use std::borrow::Cow; 10 | #[allow(unused_imports)] 11 | use std::convert::TryInto; 12 | #[allow(unused_imports)] 13 | use crate::utils::RawFdContainer; 14 | #[allow(unused_imports)] 15 | use crate::x11_utils::{Request, RequestHeader, Serialize, TryParse, TryParseFd}; 16 | use std::io::IoSlice; 17 | use crate::connection::RequestConnection; 18 | #[allow(unused_imports)] 19 | use crate::connection::Connection as X11Connection; 20 | #[allow(unused_imports)] 21 | use crate::cookie::{Cookie, CookieWithFds, VoidCookie}; 22 | use crate::errors::ConnectionError; 23 | #[allow(unused_imports)] 24 | use crate::errors::ReplyOrIdError; 25 | use std::future::Future; 26 | use std::pin::Pin; 27 | #[allow(unused_imports)] 28 | use super::xproto; 29 | 30 | pub use x11rb_protocol::protocol::xtest::*; 31 | 32 | /// Get the major opcode of this extension 33 | async fn major_opcode(conn: &Conn) -> Result { 34 | let info = conn.extension_information(X11_EXTENSION_NAME).await?; 35 | let info = info.ok_or(ConnectionError::UnsupportedExtension)?; 36 | Ok(info.major_opcode) 37 | } 38 | 39 | pub async fn get_version(conn: &Conn, major_version: u8, minor_version: u16) -> Result, ConnectionError> 40 | where 41 | Conn: RequestConnection + ?Sized, 42 | { 43 | let request0 = GetVersionRequest { 44 | major_version, 45 | minor_version, 46 | }; 47 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 48 | let slices = [IoSlice::new(&bytes[0])]; 49 | assert_eq!(slices.len(), bytes.len()); 50 | conn.send_request_with_reply(&slices, fds).await 51 | } 52 | pub async fn compare_cursor(conn: &Conn, window: xproto::Window, cursor: xproto::Cursor) -> Result, ConnectionError> 53 | where 54 | Conn: RequestConnection + ?Sized, 55 | { 56 | let request0 = CompareCursorRequest { 57 | window, 58 | cursor, 59 | }; 60 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 61 | let slices = [IoSlice::new(&bytes[0])]; 62 | assert_eq!(slices.len(), bytes.len()); 63 | conn.send_request_with_reply(&slices, fds).await 64 | } 65 | pub async fn fake_input(conn: &Conn, type_: u8, detail: u8, time: u32, root: xproto::Window, root_x: i16, root_y: i16, deviceid: u8) -> Result, ConnectionError> 66 | where 67 | Conn: RequestConnection + ?Sized, 68 | { 69 | let request0 = FakeInputRequest { 70 | type_, 71 | detail, 72 | time, 73 | root, 74 | root_x, 75 | root_y, 76 | deviceid, 77 | }; 78 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 79 | let slices = [IoSlice::new(&bytes[0])]; 80 | assert_eq!(slices.len(), bytes.len()); 81 | conn.send_request_without_reply(&slices, fds).await 82 | } 83 | pub async fn grab_control(conn: &Conn, impervious: bool) -> Result, ConnectionError> 84 | where 85 | Conn: RequestConnection + ?Sized, 86 | { 87 | let request0 = GrabControlRequest { 88 | impervious, 89 | }; 90 | let (bytes, fds) = request0.serialize(major_opcode(conn).await?); 91 | let slices = [IoSlice::new(&bytes[0])]; 92 | assert_eq!(slices.len(), bytes.len()); 93 | conn.send_request_without_reply(&slices, fds).await 94 | } 95 | /// Extension trait defining the requests of this extension. 96 | pub trait ConnectionExt: RequestConnection { 97 | fn xtest_get_version(&self, major_version: u8, minor_version: u16) -> Pin, ConnectionError>> + Send + '_>> 98 | { 99 | Box::pin(get_version(self, major_version, minor_version)) 100 | } 101 | fn xtest_compare_cursor(&self, window: xproto::Window, cursor: xproto::Cursor) -> Pin, ConnectionError>> + Send + '_>> 102 | { 103 | Box::pin(compare_cursor(self, window, cursor)) 104 | } 105 | fn xtest_fake_input(&self, type_: u8, detail: u8, time: u32, root: xproto::Window, root_x: i16, root_y: i16, deviceid: u8) -> Pin, ConnectionError>> + Send + '_>> 106 | { 107 | Box::pin(fake_input(self, type_, detail, time, root, root_x, root_y, deviceid)) 108 | } 109 | fn xtest_grab_control(&self, impervious: bool) -> Pin, ConnectionError>> + Send + '_>> 110 | { 111 | Box::pin(grab_control(self, impervious)) 112 | } 113 | } 114 | 115 | impl ConnectionExt for C {} 116 | -------------------------------------------------------------------------------- /x11rb-async/src/rust_connection/extensions.rs: -------------------------------------------------------------------------------- 1 | //! Extension state for the connection 2 | 3 | use crate::connection::Connection; 4 | use crate::Cookie; 5 | use std::collections::hash_map::{Entry, HashMap}; 6 | use x11rb::errors::{ConnectionError, ReplyError}; 7 | use x11rb_protocol::protocol::xproto::QueryExtensionReply; 8 | use x11rb_protocol::x11_utils::{ExtInfoProvider, ExtensionInformation}; 9 | use x11rb_protocol::SequenceNumber; 10 | 11 | /// Cache for X11 extensions supported by the server 12 | #[derive(Debug, Default)] 13 | pub(super) struct Extensions(HashMap<&'static str, ExtensionState>); 14 | 15 | #[derive(Debug)] 16 | enum ExtensionState { 17 | /// Currently loading the extension. 18 | Loading(SequenceNumber), 19 | 20 | /// The extension is loaded. 21 | Loaded(Option), 22 | } 23 | 24 | impl Extensions { 25 | fn iter(&self) -> impl Iterator { 26 | self.0.iter().filter_map(|(name, state)| match state { 27 | ExtensionState::Loaded(ref ext) => ext.map(|ext| (*name, ext)), 28 | _ => None, 29 | }) 30 | } 31 | 32 | /// Prefetch information for a given extension, if this was not yet done. 33 | pub(super) async fn prefetch( 34 | &mut self, 35 | conn: &C, 36 | name: &'static str, 37 | ) -> Result<(), ConnectionError> { 38 | // Check if there is already a cache entry. 39 | if let Entry::Vacant(entry) = self.0.entry(name) { 40 | tracing::debug!("Prefetching information about '{}' extension", name); 41 | 42 | // Send a QueryExtension request. 43 | let cookie = crate::protocol::xproto::query_extension(conn, name.as_bytes()).await?; 44 | 45 | // Add the extension to the cache. 46 | let _entry = entry.insert(ExtensionState::Loading(cookie.sequence_number())); 47 | 48 | std::mem::forget(cookie); 49 | } 50 | 51 | Ok(()) 52 | } 53 | 54 | /// Get information about an X11 extension. 55 | pub(super) async fn information( 56 | &mut self, 57 | conn: &C, 58 | name: &'static str, 59 | ) -> Result, ConnectionError> { 60 | // Prefetch the implementation in case this was not yet done 61 | self.prefetch(conn, name).await?; 62 | 63 | // Get the entry from the cache 64 | let mut entry = match self.0.entry(name) { 65 | Entry::Occupied(o) => o, 66 | _ => unreachable!("We just prefetched this."), 67 | }; 68 | 69 | // Complete the request if we need to. 70 | match entry.get() { 71 | ExtensionState::Loaded(info) => Ok(*info), 72 | 73 | ExtensionState::Loading(cookie) => { 74 | tracing::debug!("Waiting for QueryInfo reply for '{}' extension", name); 75 | 76 | // Load the extension information. 77 | let cookie = Cookie::<'_, _, QueryExtensionReply>::new(conn, *cookie); 78 | 79 | // Get the reply. 80 | let reply = cookie.reply().await.map_err(|e| { 81 | tracing::warn!( 82 | "Got error {:?} for QueryInfo reply for '{}' extension", 83 | e, 84 | name 85 | ); 86 | match e { 87 | ReplyError::ConnectionError(e) => e, 88 | ReplyError::X11Error(_) => ConnectionError::UnknownError, 89 | } 90 | })?; 91 | 92 | let ext_info = if reply.present { 93 | let info = ExtensionInformation { 94 | major_opcode: reply.major_opcode, 95 | first_event: reply.first_event, 96 | first_error: reply.first_error, 97 | }; 98 | tracing::debug!("Extension '{}' is present: {:?}", name, info); 99 | Some(info) 100 | } else { 101 | tracing::debug!("Extension '{}' is not present", name); 102 | None 103 | }; 104 | 105 | // Update the cache. 106 | *entry.get_mut() = ExtensionState::Loaded(ext_info); 107 | 108 | Ok(ext_info) 109 | } 110 | } 111 | } 112 | } 113 | 114 | impl ExtInfoProvider for Extensions { 115 | fn get_from_major_opcode(&self, major_opcode: u8) -> Option<(&str, ExtensionInformation)> { 116 | self.iter() 117 | .find(|(_, info)| info.major_opcode == major_opcode) 118 | } 119 | 120 | fn get_from_event_code(&self, event_code: u8) -> Option<(&str, ExtensionInformation)> { 121 | self.iter() 122 | .filter(|(_, info)| info.first_event <= event_code) 123 | .max_by_key(|(_, info)| info.first_event) 124 | } 125 | 126 | fn get_from_error_code(&self, error_code: u8) -> Option<(&str, ExtensionInformation)> { 127 | self.iter() 128 | .filter(|(_, info)| info.first_error <= error_code) 129 | .max_by_key(|(_, info)| info.first_error) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /x11rb-async/src/rust_connection/stream.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `Stream` trait for `RustConnection`. 2 | 3 | use std::future::Future; 4 | use std::io; 5 | use std::pin::Pin; 6 | use std::task::{Context, Poll}; 7 | 8 | #[cfg(unix)] 9 | use std::os::unix::io::AsFd; 10 | 11 | #[cfg(windows)] 12 | use std::os::windows::io::AsSocket as AsFd; 13 | 14 | use async_io::Async; 15 | use futures_lite::future; 16 | 17 | use x11rb::rust_connection::{ 18 | DefaultStream as X11rbDefaultStream, PollMode, Stream as X11rbStream, 19 | }; 20 | use x11rb::utils::RawFdContainer; 21 | 22 | /// A stream that bytes can be read from or written to. 23 | pub trait StreamBase<'a>: X11rbStream { 24 | /// The future returned by `readable`. 25 | type Readable: Future> + Send + 'a; 26 | 27 | /// The future returned by `writable`. 28 | type Writable: Future> + Send + 'a; 29 | 30 | /// Wait until the stream is readable. 31 | fn readable(&'a self) -> Self::Readable; 32 | 33 | /// Wait until the stream is writable. 34 | fn writable(&'a self) -> Self::Writable; 35 | } 36 | 37 | /// A stream that bytes can be read from or written to. 38 | pub trait Stream: for<'a> StreamBase<'a> {} 39 | impl StreamBase<'a>> Stream for S {} 40 | 41 | /// The default stream type. 42 | pub type DefaultStream = StreamAdaptor; 43 | 44 | /// An adaptor that implements a `Stream` for a type that implements `X11rbStream`. 45 | #[derive(Debug)] 46 | pub struct StreamAdaptor { 47 | inner: Async, 48 | } 49 | 50 | impl StreamAdaptor { 51 | /// Create a new `StreamAdaptor` from a stream. 52 | pub fn new(stream: S) -> io::Result { 53 | Async::new(stream).map(|inner| Self { inner }) 54 | } 55 | } 56 | 57 | /// A future for reading from a [`StreamAdaptor`]. 58 | #[derive(Debug)] 59 | pub struct Readable<'a, S>(async_io::Readable<'a, S>); 60 | 61 | impl Unpin for Readable<'_, S> {} 62 | 63 | impl Future for Readable<'_, S> { 64 | type Output = io::Result<()>; 65 | 66 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 67 | Pin::new(&mut self.0).poll(cx) 68 | } 69 | } 70 | 71 | /// A future for writing to a [`StreamAdaptor`]. 72 | #[derive(Debug)] 73 | pub struct Writable<'a, S>(async_io::Writable<'a, S>); 74 | 75 | impl Unpin for Writable<'_, S> {} 76 | 77 | impl Future for Writable<'_, S> { 78 | type Output = io::Result<()>; 79 | 80 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 81 | Pin::new(&mut self.0).poll(cx) 82 | } 83 | } 84 | 85 | impl<'a, S: 'a + X11rbStream + Sync> StreamBase<'a> for StreamAdaptor { 86 | type Readable = Readable<'a, S>; 87 | type Writable = Writable<'a, S>; 88 | 89 | fn readable(&'a self) -> Self::Readable { 90 | Readable(self.inner.readable()) 91 | } 92 | 93 | fn writable(&'a self) -> Self::Writable { 94 | Writable(self.inner.writable()) 95 | } 96 | } 97 | 98 | impl X11rbStream for StreamAdaptor { 99 | fn poll(&self, mode: PollMode) -> io::Result<()> { 100 | use async_io::block_on; 101 | 102 | // Block on the necessary futures. 103 | match mode { 104 | PollMode::Readable => block_on(self.inner.readable()), 105 | PollMode::Writable => block_on(self.inner.writable()), 106 | PollMode::ReadAndWritable => { 107 | block_on(future::or(self.inner.readable(), self.inner.writable())) 108 | } 109 | } 110 | } 111 | 112 | fn read(&self, buf: &mut [u8], fd_storage: &mut Vec) -> io::Result { 113 | self.inner.get_ref().read(buf, fd_storage) 114 | } 115 | 116 | fn write(&self, buf: &[u8], fds: &mut Vec) -> io::Result { 117 | self.inner.get_ref().write(buf, fds) 118 | } 119 | 120 | fn write_vectored( 121 | &self, 122 | bufs: &[io::IoSlice<'_>], 123 | fds: &mut Vec, 124 | ) -> io::Result { 125 | self.inner.get_ref().write_vectored(bufs, fds) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /x11rb-async/tests/connection_regression_tests.rs: -------------------------------------------------------------------------------- 1 | use futures_lite::future::Ready; 2 | use std::io::IoSlice; 3 | use std::sync::{Arc, Mutex}; 4 | 5 | use x11rb::errors::ConnectionError; 6 | use x11rb::protocol::xproto::Setup; 7 | use x11rb::rust_connection::{PollMode, Stream as SyncStream}; 8 | use x11rb::utils::RawFdContainer; 9 | use x11rb_async::connection::{Connection, RequestConnection}; 10 | use x11rb_async::rust_connection::{RustConnection, Stream as AsyncStream, StreamBase}; 11 | 12 | #[derive(Debug, Default)] 13 | struct FakeStream(Arc>>); 14 | 15 | impl SyncStream for FakeStream { 16 | fn poll(&self, _: PollMode) -> Result<(), std::io::Error> { 17 | unimplemented!() 18 | } 19 | fn read(&self, _: &mut [u8], _: &mut Vec) -> Result { 20 | unimplemented!() 21 | } 22 | fn write(&self, buf: &[u8], _: &mut Vec) -> Result { 23 | self.0.lock().unwrap().extend(buf); 24 | Ok(buf.len()) 25 | } 26 | } 27 | 28 | impl StreamBase<'_> for FakeStream { 29 | type Readable = Ready>; 30 | type Writable = Ready>; 31 | 32 | fn readable(&self) -> Self::Readable { 33 | unimplemented!() 34 | } 35 | 36 | fn writable(&self) -> Self::Writable { 37 | unimplemented!() 38 | } 39 | } 40 | 41 | fn make_setup() -> Setup { 42 | Setup { 43 | resource_id_mask: (1 << 8) - 1, 44 | ..Default::default() 45 | } 46 | } 47 | 48 | #[test] 49 | fn connection_breaks_on_immediate_drop() { 50 | let (conn, driver) = 51 | RustConnection::for_connected_stream(FakeStream::default(), make_setup()).unwrap(); 52 | 53 | // Drop the driver future. This should break the connection. 54 | drop(driver); 55 | 56 | // Check that waiting for input now errors. 57 | match async_io::block_on(conn.wait_for_event()) { 58 | Err(ConnectionError::IoError(err)) => { 59 | assert_eq!(err.kind(), std::io::ErrorKind::Other); 60 | assert_eq!( 61 | err.into_inner().unwrap().to_string(), 62 | "Driving future was dropped" 63 | ); 64 | } 65 | _ => unreachable!(), 66 | } 67 | } 68 | 69 | fn send_request_via_connection( 70 | stream: impl AsyncStream + Send + Sync, 71 | length: usize, 72 | fds: Vec, 73 | ) -> Option { 74 | let (conn, _driver) = RustConnection::for_connected_stream(stream, make_setup()).unwrap(); 75 | 76 | // Send a large request. This should be larger than the write buffer size, which is 16384 bytes. 77 | let mut request = vec![0; length]; 78 | 79 | // Set the length field correctly (x11rb-async expects this) 80 | request[2..4].copy_from_slice(&u16::try_from(length / 4).unwrap().to_ne_bytes()); 81 | 82 | async_io::block_on(conn.send_request_without_reply(&[IoSlice::new(&request)], fds)).err() 83 | } 84 | 85 | #[test] 86 | fn connection_sends_large_request() { 87 | let data = Default::default(); 88 | 89 | // Send a large request. This should be larger than the write buffer size, which is 16384 bytes. 90 | let length = 16384 * 2; 91 | let res = send_request_via_connection(FakeStream(Arc::clone(&data)), length, Vec::new()); 92 | assert!(res.is_none(), "{:?}", res); 93 | 94 | // Check that all the data was sent 95 | assert_eq!(data.lock().unwrap().len(), length); 96 | } 97 | 98 | #[test] 99 | #[cfg(unix)] 100 | fn retry_for_left_over_fds() { 101 | // FakeStream ignores FDs. This should result in some error and not be silently ignored. 102 | // Right now that error is WriteZero. That is still better than no error at all. 103 | 104 | let fds = { 105 | let (fd0, fd1) = rustix::pipe::pipe().unwrap(); 106 | vec![fd0, fd1] 107 | }; 108 | 109 | // Send a large request. This should be larger than the write buffer size, which is 16384 bytes. 110 | let length = 16384 * 2; 111 | match send_request_via_connection(FakeStream(Default::default()), length, fds) { 112 | Some(ConnectionError::IoError(e)) => { 113 | assert_eq!(e.kind(), std::io::ErrorKind::Other); 114 | assert_eq!(e.to_string(), "Left over FDs after sending the request"); 115 | } 116 | e => panic!("Unexpected error: {:?}", e), 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /x11rb-protocol/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "x11rb-protocol" 3 | version = "0.13.1" 4 | description = "Rust bindings to X11" 5 | authors = [ 6 | "Uli Schlachter ", 7 | "Eduardo Sánchez Muñoz ", 8 | "notgull ", 9 | ] 10 | repository = "https://github.com/psychon/x11rb" 11 | readme = "../README.md" 12 | edition = "2021" 13 | rust-version = "1.64" 14 | license = "MIT OR Apache-2.0" 15 | keywords = ["xcb", "X11"] 16 | 17 | [dependencies] 18 | serde = { version = "1", features = ["derive"], optional = true } 19 | 20 | [dev-dependencies] 21 | criterion = "0.5" 22 | 23 | [features] 24 | default = ["std"] 25 | std = [] 26 | 27 | # Enable extra traits for the X11 types 28 | extra-traits = [] 29 | 30 | # Enable parsing for requests. 31 | # 32 | # This adds a lot of extra code that isn't used in the common case. 33 | request-parsing = [] 34 | 35 | # Enable utility functions in `x11rb::resource_manager` for querying the 36 | # resource databases. 37 | resource_manager = ["std"] 38 | 39 | # Enable this feature to enable all the X11 extensions 40 | all-extensions = [ 41 | "composite", 42 | "damage", 43 | "dbe", 44 | "dpms", 45 | "dri2", 46 | "dri3", 47 | "glx", 48 | "present", 49 | "randr", 50 | "record", 51 | "render", 52 | "res", 53 | "screensaver", 54 | "shape", 55 | "shm", 56 | "sync", 57 | "xevie", 58 | "xf86dri", 59 | "xf86vidmode", 60 | "xfixes", 61 | "xinerama", 62 | "xinput", 63 | "xkb", 64 | "xprint", 65 | "xselinux", 66 | "xtest", 67 | "xv", 68 | "xvmc", 69 | ] 70 | 71 | # Features to enable individual X11 extensions 72 | composite = ["xfixes"] 73 | damage = ["xfixes"] 74 | dbe = [] 75 | dpms = [] 76 | dri2 = [] 77 | dri3 = [] 78 | glx = [] 79 | present = ["randr", "xfixes", "sync", "dri3"] 80 | randr = ["render"] 81 | record = [] 82 | render = [] 83 | res = [] 84 | screensaver = [] 85 | shape = [] 86 | shm = [] 87 | sync = [] 88 | xevie = [] 89 | xf86dri = [] 90 | xf86vidmode = [] 91 | xfixes = ["render", "shape"] 92 | xinerama = [] 93 | xinput = ["xfixes"] 94 | xkb = [] 95 | xprint = [] 96 | xselinux = [] 97 | xtest = [] 98 | xv = ["shm"] 99 | xvmc = ["xv"] 100 | 101 | [package.metadata.docs.rs] 102 | all-features = true 103 | 104 | [[bench]] 105 | name = "proto_connection" 106 | harness = false 107 | -------------------------------------------------------------------------------- /x11rb-protocol/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /x11rb-protocol/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /x11rb-protocol/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! X11 rust bindings. 2 | //! 3 | //! This crate provides a representation of the X11 protocol in Rust. With this protocol, raw X11 4 | //! bytes can be parsed into a structured representation or raw bytes can be produces. 5 | //! 6 | //! This protocol does not do any I/O. If you need an X11 client library, look at 7 | //! . 8 | //! 9 | //! # Feature flags 10 | //! 11 | //! This crate uses [feature 12 | //! flags](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section) to reduce 13 | //! the amount of compiled code. There are two kinds of feature flags available: 14 | //! 15 | //! * Feature flags for specific X11 extensions 16 | //! * Feature flags for additional functionality 17 | //! 18 | //! ## Feature flags for specific X11 extensions 19 | //! 20 | //! By default, only the core X11 protocol and some small, commonly needed X11 extensions are 21 | //! enabled. These are the `bigreq`, `ge` and `xc_misc` extensions. Further extensions need to be 22 | //! explicitly enabled via their feature flag: 23 | //! 24 | //! `composite`, `damage`, `dpms`, `dri2`, `dri3`, `glx`, `present`, `randr`, `record`, `render`, 25 | //! `res`, `screensaver`, `shape`, `shm`, `sync`, `xevie`, `xf86dri`, `xf86vidmode`, `xfixes`, 26 | //! `xinerama`, `xinput`, `xkb`, `xprint`, `xselinux`, `xtest`, `xv`, `xvmc`. 27 | //! 28 | //! If you want to take the "I do not want to think about this"-approach, you can enable the 29 | //! `all-extensions` feature to just enable, well, all extensions. 30 | //! 31 | //! ## Feature flags for additional functionality 32 | //! 33 | //! Additionally, the following flags exist: 34 | //! * `std` (enabled by default): Enable functionality needing the std library, e.g. environment 35 | //! variables or [`std::os::unix::io::OwnedFd`]. 36 | //! * `resource_manager`: Enable the code in [resource_manager] for loading and querying the 37 | //! X11 resource database. 38 | //! * `serde`: Implement [`serde::Serialize`] and [`serde::Deserialize`] for all objects. 39 | //! * `request-parsing`: Add the ability to parse X11 requests. Not normally needed. 40 | //! * `extra-traits`: Implement extra traits for types. This improves the output of the `Debug` 41 | //! impl and adds `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash` where possible. 42 | 43 | // A list of lints that are only #![deny] and not the stronger #![forbid]. Each one has a comment 44 | // explaining why it gets the weaker treatment. 45 | #![deny( 46 | // Contains unreachable_code and "?" generates an #[allow] for this 47 | unused, 48 | // #[derive] generates an #[allow] for this; not part of "unused" 49 | unused_qualifications, 50 | // serde's Deserialize/Serialize impls add allows for this 51 | rust_2018_idioms, 52 | // Not everything in x11rb_protocol::protocol has doc comments 53 | missing_docs, 54 | )] 55 | #![forbid( 56 | missing_copy_implementations, 57 | missing_debug_implementations, 58 | rustdoc::private_doc_tests, 59 | //single_use_lifetimes, 60 | trivial_casts, 61 | trivial_numeric_casts, 62 | unreachable_pub, 63 | unsafe_code, 64 | unused_must_use, 65 | unused_results, 66 | clippy::cast_lossless, 67 | clippy::needless_pass_by_value, 68 | )] 69 | #![no_std] 70 | 71 | // std crate imports 72 | extern crate alloc; 73 | #[cfg(feature = "std")] 74 | extern crate std; 75 | 76 | use alloc::vec::Vec; 77 | 78 | pub mod connect; 79 | pub mod connection; 80 | #[macro_use] 81 | pub mod x11_utils; 82 | pub mod errors; 83 | pub mod id_allocator; 84 | pub mod packet_reader; 85 | pub mod parse_display; 86 | #[rustfmt::skip] 87 | #[allow(missing_docs)] 88 | pub mod protocol; 89 | #[cfg(feature = "resource_manager")] 90 | pub mod resource_manager; 91 | #[cfg(test)] 92 | mod test; 93 | mod utils; 94 | pub mod wrapper; 95 | pub mod xauth; 96 | 97 | pub use utils::RawFdContainer; 98 | 99 | // Used to avoid too-complex types. 100 | /// A combination of a buffer and a list of file descriptors. 101 | pub type BufWithFds = (B, Vec); 102 | 103 | /// Number type used for referring to things that were sent to the server in responses from the 104 | /// server. 105 | /// 106 | /// Each request sent to the X11 server is implicitly assigned a monotonically increasing sequence 107 | /// number. Replies, events, and errors contain the sequence number of the last request that the 108 | /// server received. This allows to map replies to their requests and to figure out which request 109 | /// caused an error. 110 | pub type SequenceNumber = u64; 111 | 112 | /// The raw bytes of an event and its sequence number. 113 | pub type RawEventAndSeqNumber = (B, SequenceNumber); 114 | 115 | /// Variants describing which responses to a request should be discarded. 116 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 117 | pub enum DiscardMode { 118 | /// Only discard the actual reply. Errors go to the main loop. 119 | DiscardReply, 120 | /// Ignore any kind of response that this request generates. 121 | DiscardReplyAndError, 122 | } 123 | -------------------------------------------------------------------------------- /x11rb-protocol/src/parse_display/connect_instruction.rs: -------------------------------------------------------------------------------- 1 | //! Provides the `ConnectInstruction` structure, which allows for a `ParsedDisplay` 2 | //! to be transformed into a server connection. 3 | 4 | use super::ParsedDisplay; 5 | use alloc::format; 6 | use alloc::string::String; 7 | use alloc::vec::Vec; 8 | 9 | /// A possible address for an X11 server. 10 | #[derive(Debug, Clone, PartialEq, Eq)] 11 | #[non_exhaustive] 12 | pub enum ConnectAddress<'a> { 13 | /// Connect to this hostname and port over TCP. 14 | Hostname(&'a str, u16), 15 | /// Connect to this Unix socket. 16 | /// 17 | /// First, the given path should be attempted in the abstract namespace. Only if that fails, 18 | /// then the named socket with the given name should be tried. 19 | Socket(String), 20 | } 21 | 22 | /// Get an iterator over all of the addresses we should target with a 23 | /// `ParsedDisplay`. 24 | pub(super) fn connect_addresses(p: &ParsedDisplay) -> impl Iterator> { 25 | const TCP_PORT_BASE: u16 = 6000; 26 | let ParsedDisplay { 27 | host, 28 | protocol, 29 | display, 30 | .. 31 | } = p; 32 | 33 | let mut targets = Vec::new(); 34 | 35 | if (protocol.is_none() || protocol.as_deref() != Some("unix")) 36 | && !host.is_empty() 37 | && host != "unix" 38 | { 39 | targets.push(ConnectAddress::Hostname(host, TCP_PORT_BASE + display)); 40 | } else { 41 | if protocol.is_none() || protocol.as_deref() == Some("unix") { 42 | let file_name = format!("/tmp/.X11-unix/X{}", display); 43 | targets.push(ConnectAddress::Socket(file_name)); 44 | } 45 | 46 | if protocol.is_none() && host.is_empty() { 47 | targets.push(ConnectAddress::Hostname( 48 | "localhost", 49 | TCP_PORT_BASE + display, 50 | )); 51 | } 52 | } 53 | 54 | targets.into_iter() 55 | } 56 | 57 | #[cfg(all(test, feature = "std"))] 58 | mod tests { 59 | // make sure iterator properties are clean 60 | use super::{super::parse_display, ConnectAddress}; 61 | use alloc::{vec, vec::Vec}; 62 | 63 | #[test] 64 | fn basic_test() { 65 | let pd = parse_display(Some(":0")).unwrap(); 66 | let ci = pd.connect_instruction(); 67 | let ci = ci.collect::>(); 68 | 69 | assert_eq!( 70 | ci, 71 | vec![ 72 | ConnectAddress::Socket("/tmp/.X11-unix/X0".into()), 73 | ConnectAddress::Hostname("localhost", 6000), 74 | ] 75 | ); 76 | } 77 | 78 | #[test] 79 | fn try_over_hostname() { 80 | let pd = parse_display(Some("192.168.1.111:0")).unwrap(); 81 | let ci = pd.connect_instruction(); 82 | 83 | let ci = ci.collect::>(); 84 | 85 | assert_eq!(ci, vec![ConnectAddress::Hostname("192.168.1.111", 6000),]); 86 | } 87 | 88 | #[test] 89 | fn try_over_unix_hostname() { 90 | let pd = parse_display(Some("unix/host:0")).unwrap(); 91 | let ci = pd.connect_instruction(); 92 | 93 | let ci = ci.collect::>(); 94 | 95 | assert_eq!(ci, vec![ConnectAddress::Socket("/tmp/.X11-unix/X0".into())]); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /x11rb-protocol/src/test.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "std")] 2 | use std::borrow::Cow; 3 | 4 | use crate::protocol::{get_request_name, request_name}; 5 | use crate::x11_utils::{ExtInfoProvider, ExtensionInformation}; 6 | 7 | struct ExtInfo<'a> { 8 | extension: Option<&'a str>, 9 | major_opcode: u8, 10 | } 11 | 12 | impl ExtInfoProvider for ExtInfo<'_> { 13 | fn get_from_major_opcode(&self, major_opcode: u8) -> Option<(&str, ExtensionInformation)> { 14 | assert_eq!(self.major_opcode, major_opcode); 15 | self.extension.map(|ext_name| { 16 | ( 17 | ext_name, 18 | ExtensionInformation { 19 | major_opcode, 20 | first_event: 0, 21 | first_error: 0, 22 | }, 23 | ) 24 | }) 25 | } 26 | 27 | fn get_from_event_code(&self, _event_code: u8) -> Option<(&str, ExtensionInformation)> { 28 | unimplemented!() 29 | } 30 | 31 | fn get_from_error_code(&self, _error_code: u8) -> Option<(&str, ExtensionInformation)> { 32 | unimplemented!() 33 | } 34 | } 35 | 36 | /// Test whether request_name() and get_request_name() provide the correct information for the 37 | /// given request. 38 | fn get_request_names( 39 | extension: Option<&str>, 40 | major_opcode: u8, 41 | minor_opcode: u8, 42 | ) -> (Option<&'static str>, Cow<'static, str>) { 43 | let ext_info = ExtInfo { 44 | extension, 45 | major_opcode, 46 | }; 47 | 48 | let (ext_name, name) = request_name(&ext_info, major_opcode, minor_opcode.into()); 49 | let get_name = get_request_name(&ext_info, major_opcode, minor_opcode); 50 | assert_eq!(ext_name.as_deref(), extension); 51 | (name, get_name) 52 | } 53 | 54 | #[test] 55 | fn test_core_request_names() { 56 | assert_eq!( 57 | get_request_names(None, 1, 0), 58 | (Some("CreateWindow"), Cow::from("CreateWindow")) 59 | ); 60 | // Test that the minor opcode (which is actually the depth in CreateWindow) does not influence 61 | // the result 62 | assert_eq!( 63 | get_request_names(None, 1, 1), 64 | (Some("CreateWindow"), Cow::from("CreateWindow")) 65 | ); 66 | assert_eq!( 67 | get_request_names(None, 1, 255), 68 | (Some("CreateWindow"), Cow::from("CreateWindow")) 69 | ); 70 | // Special case: minor_opcode does not fit into u8. 71 | assert_eq!( 72 | request_name( 73 | &ExtInfo { 74 | extension: None, 75 | major_opcode: 42 76 | }, 77 | 1, 78 | 54321 79 | ), 80 | (None, Some("CreateWindow")) 81 | ); 82 | 83 | assert_eq!( 84 | get_request_names(None, 100, 0), 85 | ( 86 | Some("ChangeKeyboardMapping"), 87 | Cow::from("ChangeKeyboardMapping") 88 | ) 89 | ); 90 | assert_eq!( 91 | get_request_names(None, 120, 0), 92 | (None, Cow::from("xproto::opcode 120")) 93 | ); 94 | assert_eq!( 95 | get_request_names(None, 127, 0), 96 | (Some("NoOperation"), Cow::from("NoOperation")) 97 | ); 98 | } 99 | 100 | #[test] 101 | fn test_bigreq_request_names() { 102 | assert_eq!( 103 | get_request_names(Some("BIG-REQUESTS"), 130, 0), 104 | (Some("Enable"), Cow::from("BigRequests::Enable")) 105 | ); 106 | assert_eq!( 107 | get_request_names(Some("BIG-REQUESTS"), 200, 0), 108 | (Some("Enable"), Cow::from("BigRequests::Enable")) 109 | ); 110 | 111 | assert_eq!( 112 | get_request_names(Some("BIG-REQUESTS"), 130, 1), 113 | (None, Cow::from("BigRequests::opcode 1")) 114 | ); 115 | assert_eq!( 116 | get_request_names(Some("BIG-REQUESTS"), 130, 255), 117 | (None, Cow::from("BigRequests::opcode 255")) 118 | ); 119 | } 120 | 121 | #[test] 122 | fn test_xc_misc_request_names() { 123 | assert_eq!( 124 | get_request_names(Some("XC-MISC"), 130, 0), 125 | (Some("GetVersion"), Cow::from("XCMisc::GetVersion")) 126 | ); 127 | 128 | assert_eq!( 129 | get_request_names(Some("XC-MISC"), 200, 0), 130 | (Some("GetVersion"), Cow::from("XCMisc::GetVersion")) 131 | ); 132 | assert_eq!( 133 | get_request_names(Some("XC-MISC"), 200, 1), 134 | (Some("GetXIDRange"), Cow::from("XCMisc::GetXIDRange")) 135 | ); 136 | assert_eq!( 137 | get_request_names(Some("XC-MISC"), 200, 2), 138 | (Some("GetXIDList"), Cow::from("XCMisc::GetXIDList")) 139 | ); 140 | assert_eq!( 141 | get_request_names(Some("XC-MISC"), 200, 3), 142 | (None, Cow::from("XCMisc::opcode 3")) 143 | ); 144 | assert_eq!( 145 | get_request_names(Some("XC-MISC"), 200, 100), 146 | (None, Cow::from("XCMisc::opcode 100")) 147 | ); 148 | } 149 | 150 | #[test] 151 | fn test_unknown_extension_request_names() { 152 | assert_eq!( 153 | get_request_names(None, 210, 123), 154 | (None, Cow::from("ext 210::opcode 123")) 155 | ); 156 | assert_eq!( 157 | get_request_names(Some("FOO-BAR"), 210, 123), 158 | (None, Cow::from("ext FOO-BAR::opcode 123")) 159 | ); 160 | } 161 | -------------------------------------------------------------------------------- /x11rb-protocol/src/wrapper.rs: -------------------------------------------------------------------------------- 1 | //! Helpers for the generated code 2 | 3 | use super::x11_utils::TryParse; 4 | use core::marker::PhantomData; 5 | 6 | /// Iterator implementation used by [GetPropertyReply]. 7 | /// 8 | /// This is the actual type returned by [GetPropertyReply::value8], [GetPropertyReply::value16], 9 | /// and [GetPropertyReply::value32]. This type needs to be public due to Rust's visibility rules. 10 | /// 11 | /// [GetPropertyReply]: crate::protocol::xproto::GetPropertyReply 12 | /// [GetPropertyReply::value8]: crate::protocol::xproto::GetPropertyReply::value8 13 | /// [GetPropertyReply::value16]: crate::protocol::xproto::GetPropertyReply::value16 14 | /// [GetPropertyReply::value32]: crate::protocol::xproto::GetPropertyReply::value32 15 | #[derive(Debug, Clone)] 16 | pub struct PropertyIterator<'a, T>(&'a [u8], PhantomData); 17 | 18 | impl<'a, T> PropertyIterator<'a, T> { 19 | pub(crate) fn new(value: &'a [u8]) -> Self { 20 | PropertyIterator(value, PhantomData) 21 | } 22 | } 23 | 24 | impl Iterator for PropertyIterator<'_, T> 25 | where 26 | T: TryParse, 27 | { 28 | type Item = T; 29 | 30 | fn next(&mut self) -> Option { 31 | match T::try_parse(self.0) { 32 | Ok((value, remaining)) => { 33 | self.0 = remaining; 34 | Some(value) 35 | } 36 | Err(_) => { 37 | self.0 = &[]; 38 | None 39 | } 40 | } 41 | } 42 | 43 | fn size_hint(&self) -> (usize, Option) { 44 | use core::mem::size_of; 45 | let size = self.0.len() / size_of::(); 46 | (size, Some(size)) 47 | } 48 | } 49 | 50 | impl core::iter::FusedIterator for PropertyIterator<'_, T> {} 51 | 52 | #[cfg(test)] 53 | mod tests { 54 | use super::PropertyIterator; 55 | use alloc::vec::Vec; 56 | 57 | #[test] 58 | fn test_parse_u8() { 59 | let input = [0u8, 1, 2, 3, 4, 5]; 60 | let output = PropertyIterator::new(&input).collect::>(); 61 | assert_eq!(&input[..], output); 62 | } 63 | 64 | #[test] 65 | fn test_parse_u32() { 66 | let expected = [0u32, 1, 2, 3, 4, 5]; 67 | let input = { 68 | let mut input = Vec::new(); 69 | for value in &expected { 70 | input.extend_from_slice(&value.to_ne_bytes()); 71 | } 72 | input 73 | }; 74 | 75 | let output = PropertyIterator::new(&input).collect::>(); 76 | assert_eq!(&expected[..], output); 77 | } 78 | 79 | #[test] 80 | fn test_size_hint() { 81 | let hint = PropertyIterator::::new(&[0; 0]).size_hint(); 82 | assert_eq!(hint, (0, Some(0))); 83 | 84 | let hint = PropertyIterator::::new(&[0; 8]).size_hint(); 85 | assert_eq!(hint, (2, Some(2))); 86 | 87 | // In this case, the data is not an exact multiple of the element size 88 | let hint = PropertyIterator::::new(&[0; 30]).size_hint(); 89 | assert_eq!(hint, (7, Some(7))); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /x11rb-protocol/tests/enum_tests.rs: -------------------------------------------------------------------------------- 1 | use x11rb_protocol::protocol::xproto::EventMask; 2 | 3 | #[test] 4 | fn test_conversion() { 5 | assert_eq!(0, u32::from(EventMask::NO_EVENT)); 6 | assert_eq!(1, u32::from(EventMask::KEY_PRESS)); 7 | assert_eq!(4, u32::from(EventMask::BUTTON_PRESS)); 8 | assert_eq!(Some(16u32), EventMask::ENTER_WINDOW.into()); 9 | assert_eq!(EventMask::NO_EVENT, 0u8.into()); 10 | assert_eq!(EventMask::KEY_PRESS, 1u8.into()); 11 | } 12 | 13 | #[test] 14 | fn test_bit_or() { 15 | assert_eq!( 16 | EventMask::KEY_PRESS, 17 | EventMask::KEY_PRESS | EventMask::NO_EVENT 18 | ); 19 | assert_eq!(EventMask::KEY_PRESS, 1 | EventMask::NO_EVENT); 20 | assert_eq!(EventMask::KEY_PRESS, EventMask::NO_EVENT | 1); 21 | 22 | let mut mask = EventMask::KEY_PRESS; 23 | mask |= EventMask::BUTTON_PRESS; 24 | assert_eq!(5, u32::from(mask)); 25 | 26 | let mut mask = EventMask::KEY_PRESS; 27 | mask |= 4u32; 28 | assert_eq!(5, u32::from(mask)); 29 | 30 | let mut mask = 1u32; 31 | mask |= EventMask::BUTTON_PRESS; 32 | assert_eq!(5, mask); 33 | } 34 | 35 | #[test] 36 | fn test_bit_and() { 37 | assert_eq!( 38 | EventMask::NO_EVENT, 39 | EventMask::KEY_PRESS & EventMask::NO_EVENT 40 | ); 41 | assert_eq!( 42 | EventMask::KEY_PRESS, 43 | EventMask::KEY_PRESS & EventMask::KEY_PRESS 44 | ); 45 | assert_eq!( 46 | EventMask::KEY_PRESS, 47 | EventMask::from(5u32) & EventMask::KEY_PRESS 48 | ); 49 | assert_eq!(EventMask::KEY_PRESS, 5 & EventMask::KEY_PRESS); 50 | assert_eq!(EventMask::KEY_PRESS, EventMask::KEY_PRESS & 5); 51 | 52 | let mut mask = EventMask::from(5u32); 53 | mask &= EventMask::BUTTON_PRESS; 54 | assert_eq!(EventMask::BUTTON_PRESS, mask); 55 | 56 | let mut mask = EventMask::from(5u32); 57 | mask &= 4u32; 58 | assert_eq!(EventMask::BUTTON_PRESS, mask); 59 | 60 | let mut mask = 7u32; 61 | mask &= EventMask::from(21u32); 62 | assert_eq!(5, mask); 63 | } 64 | 65 | #[test] 66 | fn test_contains() { 67 | let mask = EventMask::KEY_PRESS; 68 | assert!(mask.contains(EventMask::KEY_PRESS)); 69 | assert!(mask.contains(EventMask::NO_EVENT)); 70 | assert!(!mask.contains(EventMask::KEY_PRESS | EventMask::BUTTON_PRESS)); 71 | assert!(!mask.contains(EventMask::BUTTON_PRESS)); 72 | 73 | let mask = EventMask::KEY_PRESS | EventMask::BUTTON_PRESS; 74 | assert!(mask.contains(EventMask::KEY_PRESS)); 75 | assert!(mask.contains(EventMask::BUTTON_PRESS)); 76 | assert!(mask.contains(EventMask::KEY_PRESS | EventMask::BUTTON_PRESS)); 77 | assert!(!mask.contains(EventMask::ENTER_WINDOW)); 78 | assert!(!mask.contains(EventMask::ENTER_WINDOW | EventMask::BUTTON_PRESS)); 79 | 80 | assert!(mask.contains(1u32)); 81 | assert!(mask.contains(4u32)); 82 | assert!(mask.contains(5u32)); 83 | assert!(!mask.contains(16u32)); 84 | assert!(!mask.contains(20u32)); 85 | } 86 | 87 | #[test] 88 | fn test_intersects() { 89 | let mask = EventMask::KEY_PRESS; 90 | assert!(mask.intersects(EventMask::KEY_PRESS)); 91 | assert!(!mask.intersects(EventMask::NO_EVENT)); 92 | assert!(mask.intersects(EventMask::KEY_PRESS | EventMask::BUTTON_PRESS)); 93 | assert!(!mask.intersects(EventMask::BUTTON_PRESS)); 94 | 95 | let mask = EventMask::KEY_PRESS | EventMask::BUTTON_PRESS; 96 | assert!(mask.intersects(EventMask::KEY_PRESS)); 97 | assert!(mask.intersects(EventMask::BUTTON_PRESS)); 98 | assert!(mask.intersects(EventMask::KEY_PRESS | EventMask::BUTTON_PRESS)); 99 | assert!(!mask.intersects(EventMask::ENTER_WINDOW)); 100 | assert!(mask.intersects(EventMask::ENTER_WINDOW | EventMask::BUTTON_PRESS)); 101 | 102 | assert!(mask.intersects(1u32)); 103 | assert!(mask.intersects(4u32)); 104 | assert!(mask.intersects(5u32)); 105 | assert!(!mask.intersects(16u32)); 106 | assert!(mask.intersects(20u32)); 107 | } 108 | 109 | #[test] 110 | fn test_remove() { 111 | let no_event = EventMask::NO_EVENT; 112 | let key_press = EventMask::KEY_PRESS; 113 | let button_press = EventMask::BUTTON_PRESS; 114 | let exposure = EventMask::EXPOSURE; 115 | let key_and_button_press = key_press | button_press; 116 | 117 | assert_eq!(no_event, key_press.remove(key_press)); 118 | assert_eq!(key_press, key_press.remove(no_event)); 119 | assert_eq!(key_press, key_press.remove(button_press)); 120 | assert_eq!(key_press, key_and_button_press.remove(button_press)); 121 | assert_eq!(key_press, key_and_button_press.remove(button_press)); 122 | assert_eq!(key_and_button_press, key_and_button_press.remove(exposure)); 123 | 124 | assert_eq!(no_event, key_press.remove(1u32)); 125 | assert_eq!(key_press, key_press.remove(0u32)); 126 | assert_eq!(key_press, key_press.remove(4u32)); 127 | assert_eq!(key_press, key_and_button_press.remove(4u32)); 128 | assert_eq!( 129 | key_and_button_press, 130 | key_and_button_press.remove(1u32 << 15) 131 | ); 132 | } 133 | -------------------------------------------------------------------------------- /x11rb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "x11rb" 3 | version = "0.13.1" 4 | description = "Rust bindings to X11" 5 | authors = [ 6 | "Uli Schlachter ", 7 | "Eduardo Sánchez Muñoz ", 8 | "notgull ", 9 | ] 10 | repository = "https://github.com/psychon/x11rb" 11 | readme = "../README.md" 12 | edition = "2021" 13 | rust-version = "1.64" 14 | license = "MIT OR Apache-2.0" 15 | keywords = ["xcb", "X11"] 16 | 17 | [dependencies] 18 | x11rb-protocol = { version = "0.13.1", default-features = false, features = ["std"], path = "../x11rb-protocol" } 19 | libc = { version = "0.2", optional = true } 20 | libloading = { version = "0.8.0", optional = true } 21 | once_cell = { version = "1.19", optional = true } 22 | raw-window-handle = { version = "0.5.0", optional = true } 23 | as-raw-xcb-connection = { version = "1.0", optional = true } 24 | tracing = { version = "0.1", optional = true, default-features = false } 25 | rustix = { version = "1.0", default-features = false, features = ["std", "event", "fs", "net", "system"] } 26 | xcursor = { version = "0.3.7", optional = true } 27 | gethostname = "1.0" 28 | 29 | [dev-dependencies] 30 | polling = "3.4" 31 | tracing-subscriber = "0.3" 32 | 33 | [features] 34 | # Without this feature, all uses of `unsafe` in the crate are forbidden via 35 | # #![deny(unsafe_code)]. This has the effect of disabling the XCB FFI bindings. 36 | allow-unsafe-code = ["libc", "as-raw-xcb-connection"] 37 | 38 | # Enable utility functions in `x11rb::cursor` for loading mouse cursors. 39 | cursor = ["render", "resource_manager", "xcursor"] 40 | 41 | # Enable utility functions in `x11rb::image` for working with image data. 42 | image = [] 43 | 44 | # Enable utility functions in `x11rb::resource_manager` for querying the 45 | # resource databases. 46 | resource_manager = ["x11rb-protocol/resource_manager"] 47 | 48 | dl-libxcb = ["allow-unsafe-code", "libloading", "once_cell"] 49 | 50 | # Enable extra traits on protocol types. 51 | extra-traits = ["x11rb-protocol/extra-traits"] 52 | 53 | # Add the ability to parse X11 requests (not normally needed). 54 | request-parsing = ["x11rb-protocol/request-parsing"] 55 | 56 | # Enable this feature to enable all the X11 extensions 57 | all-extensions = [ 58 | "x11rb-protocol/all-extensions", 59 | "composite", 60 | "damage", 61 | "dbe", 62 | "dpms", 63 | "dri2", 64 | "dri3", 65 | "glx", 66 | "present", 67 | "randr", 68 | "record", 69 | "render", 70 | "res", 71 | "screensaver", 72 | "shape", 73 | "shm", 74 | "sync", 75 | "xevie", 76 | "xf86dri", 77 | "xf86vidmode", 78 | "xfixes", 79 | "xinerama", 80 | "xinput", 81 | "xkb", 82 | "xprint", 83 | "xselinux", 84 | "xtest", 85 | "xv", 86 | "xvmc" 87 | ] 88 | 89 | # Features to enable individual X11 extensions 90 | composite = ["x11rb-protocol/composite", "xfixes"] 91 | damage = ["x11rb-protocol/damage", "xfixes"] 92 | dbe = ["x11rb-protocol/dbe"] 93 | dpms = ["x11rb-protocol/dpms"] 94 | dri2 = ["x11rb-protocol/dri2"] 95 | dri3 = ["x11rb-protocol/dri3"] 96 | glx = ["x11rb-protocol/glx"] 97 | present = ["x11rb-protocol/present", "randr", "xfixes", "sync"] 98 | randr = ["x11rb-protocol/randr", "render"] 99 | record = ["x11rb-protocol/record"] 100 | render = ["x11rb-protocol/render"] 101 | res = ["x11rb-protocol/res"] 102 | screensaver = ["x11rb-protocol/screensaver"] 103 | shape = ["x11rb-protocol/shape"] 104 | shm = ["x11rb-protocol/shm"] 105 | sync = ["x11rb-protocol/sync"] 106 | xevie = ["x11rb-protocol/xevie"] 107 | xf86dri = ["x11rb-protocol/xf86dri"] 108 | xf86vidmode = ["x11rb-protocol/xf86vidmode"] 109 | xfixes = ["x11rb-protocol/xfixes", "render", "shape"] 110 | xinerama = ["x11rb-protocol/xinerama"] 111 | xinput = ["x11rb-protocol/xinput", "xfixes"] 112 | xkb = ["x11rb-protocol/xkb"] 113 | xprint = ["x11rb-protocol/xprint"] 114 | xselinux = ["x11rb-protocol/xselinux"] 115 | xtest = ["x11rb-protocol/xtest"] 116 | xv = ["x11rb-protocol/xv", "shm"] 117 | xvmc = ["x11rb-protocol/xvmc", "xv"] 118 | 119 | [package.metadata.docs.rs] 120 | all-features = true 121 | 122 | [[example]] 123 | name = "generic_events" 124 | required-features = ["present"] 125 | 126 | [[example]] 127 | name = "shared_memory" 128 | required-features = ["libc", "shm"] 129 | 130 | [[example]] 131 | name = "xeyes" 132 | required-features = ["shape"] 133 | 134 | [[example]] 135 | name = "simple_window" 136 | required-features = ["cursor", "resource_manager", "tracing", "tracing-subscriber/env-filter"] 137 | 138 | [[example]] 139 | name = "display_ppm" 140 | required-features = ["image"] 141 | 142 | [[example]] 143 | name = "record" 144 | required-features = ["record"] 145 | -------------------------------------------------------------------------------- /x11rb/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /x11rb/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /x11rb/examples/check_unchecked_requests.rs: -------------------------------------------------------------------------------- 1 | // This program shows error handling. It causes some X11 errors and shows where they end up. 2 | // 3 | // This program also serves as a (bad) integration test. To verify that the expected behaviour 4 | // occurs, it needs to do some extra work. In particular, all calls to .sequence_number() are not 5 | // needed for the example, but only for the test. Where needed, extra comments indicate how the 6 | // short version of each case would look. 7 | 8 | extern crate x11rb; 9 | 10 | use x11rb::connection::Connection; 11 | use x11rb::errors::ReplyError; 12 | use x11rb::protocol::xproto::ConnectionExt as _; 13 | use x11rb::protocol::Event; 14 | use x11rb::wrapper::ConnectionExt as _; 15 | 16 | const INVALID_WINDOW: u32 = 0; 17 | 18 | fn main() -> Result<(), Box> { 19 | let (conn, _) = connect(None).unwrap(); 20 | 21 | // For requests with responses, there are four possibilities: 22 | 23 | // We can just normally get the response or error to the request via reply() 24 | let res = conn.get_geometry(INVALID_WINDOW)?.reply(); 25 | assert!(res.is_err()); 26 | 27 | // We can decide that we do not care about the response and also do not care about errors via 28 | // discard_reply_and_errors() 29 | conn.get_geometry(INVALID_WINDOW)? 30 | .discard_reply_and_errors(); 31 | 32 | // Errors can show up as 'events' in wait_for_event() via reply_unchecked() 33 | let cookie = conn.get_geometry(INVALID_WINDOW)?; 34 | let seq1 = cookie.sequence_number(); 35 | let res = cookie.reply_unchecked()?; 36 | assert!(res.is_none()); 37 | // The short version of the above would be: 38 | // let res = conn.get_geomtry(INVALID_WINDOW)?.reply_unchecked()?; 39 | 40 | // Errors can show up as 'events' in wait_for_event() by just dropping the cookie 41 | let cookie = conn.get_geometry(INVALID_WINDOW)?; 42 | let seq2 = cookie.sequence_number(); 43 | drop(cookie); 44 | // The short version of the above would be: 45 | // drop(conn.get_geometry(INVALID_WINDOW)?); 46 | 47 | // For requests without responses, there are three possibilities 48 | 49 | // We can check for errors explicitly 50 | match conn.destroy_window(INVALID_WINDOW)?.check() { 51 | Err(ReplyError::X11Error(_)) => {} 52 | e => panic!("{:?} unexpected", e), 53 | } 54 | 55 | // We can silently ignore the error 56 | conn.destroy_window(INVALID_WINDOW)?.ignore_error(); 57 | 58 | // An error can be handled as an event. 59 | let cookie = conn.destroy_window(INVALID_WINDOW)?; 60 | let seq3 = cookie.sequence_number(); 61 | drop(cookie); 62 | // The short version of the above would be: 63 | // drop(conn.destroy_window(INVALID_WINDOW)?); 64 | 65 | // Synchronise with the server so that all errors are already received. 66 | conn.sync()?; 67 | 68 | // Now check if the things above really caused errors. This is the part that is supposed to 69 | // turn this example into a (bad) integration test. 70 | for &seq in &[seq1, seq2, seq3] { 71 | let (event, seq2) = conn.wait_for_event_with_sequence()?; 72 | match event { 73 | Event::Error(_) => {} 74 | event => panic!("Unexpectedly got {:?} instead of an X11 error", event), 75 | } 76 | assert_eq!(seq, seq2); 77 | } 78 | assert!(conn.poll_for_event()?.is_none()); 79 | 80 | println!("Done"); 81 | 82 | Ok(()) 83 | } 84 | 85 | include!("integration_test_util/connect.rs"); 86 | -------------------------------------------------------------------------------- /x11rb/examples/generic_events.rs: -------------------------------------------------------------------------------- 1 | // This example tests support for generic events (XGE). It generates a window and uses the PRESENT 2 | // extension to cause an XGE event to be sent. 3 | 4 | use std::process::exit; 5 | 6 | use x11rb::connection::{Connection as _, RequestConnection as _}; 7 | use x11rb::protocol::xproto::{ 8 | ConfigureWindowAux, ConnectionExt as _, CreateWindowAux, WindowClass, 9 | }; 10 | use x11rb::protocol::{present, Event}; 11 | use x11rb::COPY_DEPTH_FROM_PARENT; 12 | 13 | fn main() -> Result<(), Box> { 14 | let (conn, screen_num) = connect(None)?; 15 | let screen = &conn.setup().roots[screen_num]; 16 | 17 | if conn 18 | .extension_information(present::X11_EXTENSION_NAME)? 19 | .is_none() 20 | { 21 | eprintln!("Present extension is not supported"); 22 | exit(1); 23 | } 24 | 25 | // Create a window 26 | let win_id = conn.generate_id()?; 27 | let win_aux = CreateWindowAux::new().background_pixel(screen.white_pixel); 28 | conn.create_window( 29 | COPY_DEPTH_FROM_PARENT, 30 | win_id, 31 | screen.root, 32 | 0, 33 | 0, 34 | 10, 35 | 10, 36 | 0, 37 | WindowClass::INPUT_OUTPUT, 38 | 0, 39 | &win_aux, 40 | )?; 41 | 42 | // Ask for present ConfigureNotify events 43 | let event_id = conn.generate_id()?; 44 | present::select_input( 45 | &conn, 46 | event_id, 47 | win_id, 48 | present::EventMask::CONFIGURE_NOTIFY, 49 | )?; 50 | 51 | // Cause an event 52 | conn.configure_window(win_id, &ConfigureWindowAux::new().width(20))?; 53 | 54 | // Wait for the event 55 | conn.flush()?; 56 | let event = conn.wait_for_event()?; 57 | 58 | // Now check that the event really is what we wanted to get 59 | let event = match event { 60 | Event::PresentConfigureNotify(event) => event, 61 | other => panic!("Unexpected event {:?}", other), 62 | }; 63 | println!( 64 | "Got a Present ConfigureNotify event for event ID 0x{:x} and window 0x{:x}.", 65 | event.event, event.window 66 | ); 67 | println!( 68 | "x={}, y={}, width={}, height={}, off_x={}, off_y={}, pixmap_width={}, pixmap_height={}, \ 69 | pixmap_flags={:x}", 70 | event.x, 71 | event.y, 72 | event.width, 73 | event.height, 74 | event.off_x, 75 | event.off_y, 76 | event.pixmap_width, 77 | event.pixmap_height, 78 | event.pixmap_flags, 79 | ); 80 | assert_eq!( 81 | (20, 10, 0), 82 | (event.pixmap_width, event.pixmap_height, event.pixmap_flags) 83 | ); 84 | 85 | Ok(()) 86 | } 87 | 88 | include!("integration_test_util/connect.rs"); 89 | -------------------------------------------------------------------------------- /x11rb/examples/integration_test_util/connect.rs: -------------------------------------------------------------------------------- 1 | /// Establish a new connection to an X11 server. 2 | /// 3 | /// Returns a `XCBConnection` if `allow-unsafe-code`, otherwise returns a `RustConnection`. 4 | /// This function is meant to test code with both connection types. Production code 5 | /// usually wants to use `x11rb::connect`, `x11rb::rust_connection::RustConnection::connect` 6 | /// or `x11rb::xcb_ffi::XCBConnection::connect`. 7 | pub fn connect( 8 | dpy_name: Option<&str>, 9 | ) -> Result<(impl x11rb::connection::Connection + Send + Sync, usize), x11rb::errors::ConnectError> 10 | { 11 | #[cfg(feature = "allow-unsafe-code")] 12 | { 13 | let dpy_name = dpy_name 14 | .map(std::ffi::CString::new) 15 | .transpose() 16 | .map_err(|_| x11rb::errors::DisplayParsingError::Unknown)?; 17 | let dpy_name = dpy_name.as_deref(); 18 | x11rb::xcb_ffi::XCBConnection::connect(dpy_name) 19 | } 20 | #[cfg(not(feature = "allow-unsafe-code"))] 21 | { 22 | x11rb::rust_connection::RustConnection::connect(dpy_name) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /x11rb/examples/integration_test_util/util.rs: -------------------------------------------------------------------------------- 1 | // As far as I can see, I cannot easily share code between different examples. The following code 2 | // is used by several examples to react to the $X11RB_EXAMPLE_TIMEOUT variable. This code is 3 | // include!()d in the examples 4 | 5 | mod util { 6 | use std::env; 7 | use std::sync::Arc; 8 | use std::thread; 9 | use std::time::Duration; 10 | 11 | use x11rb::connection::Connection; 12 | use x11rb::protocol::xproto::{ 13 | ClientMessageEvent, ConnectionExt as _, EventMask, Window, 14 | }; 15 | 16 | pub fn start_timeout_thread(conn: Arc, window: Window) 17 | where 18 | C: Connection + Send + Sync + 'static, 19 | { 20 | let timeout = match env::var("X11RB_EXAMPLE_TIMEOUT") 21 | .ok() 22 | .and_then(|str| str.parse().ok()) 23 | { 24 | None => return, 25 | Some(timeout) => timeout, 26 | }; 27 | 28 | thread::spawn(move || { 29 | let wm_protocols = conn.intern_atom(false, b"WM_PROTOCOLS").unwrap(); 30 | let wm_delete_window = conn.intern_atom(false, b"WM_DELETE_WINDOW").unwrap(); 31 | 32 | thread::sleep(Duration::from_secs(timeout)); 33 | 34 | let event = ClientMessageEvent::new( 35 | 32, 36 | window, 37 | wm_protocols.reply().unwrap().atom, 38 | [wm_delete_window.reply().unwrap().atom, 0, 0, 0, 0], 39 | ); 40 | 41 | if let Err(err) = conn.send_event(false, window, EventMask::NO_EVENT, event) { 42 | eprintln!("Error while sending event: {:?}", err); 43 | } 44 | if let Err(err) = conn.send_event( 45 | false, 46 | window, 47 | EventMask::SUBSTRUCTURE_REDIRECT, 48 | event, 49 | ) { 50 | eprintln!("Error while sending event: {:?}", err); 51 | } 52 | conn.flush().unwrap(); 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /x11rb/examples/list_fonts.rs: -------------------------------------------------------------------------------- 1 | // This program should produce output identical to `xlsfonts -lu`. 2 | 3 | extern crate x11rb; 4 | 5 | use x11rb::protocol::xproto::{ConnectionExt, FontDraw}; 6 | 7 | fn main() -> Result<(), Box> { 8 | let (conn, _) = connect(None)?; 9 | 10 | println!("DIR MIN MAX EXIST DFLT PROP ASC DESC NAME"); 11 | for reply in conn.list_fonts_with_info(u16::MAX, b"*")? { 12 | let reply = reply?; 13 | 14 | let dir = if reply.draw_direction == FontDraw::LEFT_TO_RIGHT { 15 | "-->" 16 | } else if reply.draw_direction == FontDraw::RIGHT_TO_LEFT { 17 | "<--" 18 | } else { 19 | "???" 20 | }; 21 | 22 | let (min, max, indicator) = if reply.min_byte1 == 0 && reply.max_byte1 == 0 { 23 | (reply.min_char_or_byte2, reply.max_char_or_byte2, ' ') 24 | } else { 25 | (u16::from(reply.min_byte1), u16::from(reply.max_byte1), '*') 26 | }; 27 | 28 | let all = if reply.all_chars_exist { "all" } else { "some" }; 29 | 30 | let name = String::from_utf8_lossy(&reply.name); 31 | 32 | println!( 33 | "{} {}{:3} {}{:3} {:>5} {:4} {:4} {:3} {:4} {}", 34 | dir, 35 | indicator, 36 | min, 37 | indicator, 38 | max, 39 | all, 40 | reply.default_char, 41 | reply.properties.len(), 42 | reply.font_ascent, 43 | reply.font_descent, 44 | name 45 | ); 46 | } 47 | Ok(()) 48 | } 49 | 50 | include!("integration_test_util/connect.rs"); 51 | -------------------------------------------------------------------------------- /x11rb/src/cursor/find_cursor.rs: -------------------------------------------------------------------------------- 1 | //! Find the right cursor file from a cursor name 2 | 3 | // Based on libxcb-cursor's load_cursor.c which has: 4 | // 5 | // Copyright © 2013 Michael Stapelberg 6 | // Copyright © 2002 Keith Packard 7 | // 8 | // and is licensed under MIT/X Consortium License 9 | 10 | use std::fs::File; 11 | use xcursor::CursorTheme; 12 | 13 | static CORE_CURSORS: &[(&str, u16)] = &[ 14 | ("X_cursor", 0), 15 | ("arrow", 1), 16 | ("based_arrow_down", 2), 17 | ("based_arrow_up", 3), 18 | ("boat", 4), 19 | ("bogosity", 5), 20 | ("bottom_left_corner", 6), 21 | ("bottom_right_corner", 7), 22 | ("bottom_side", 8), 23 | ("bottom_tee", 9), 24 | ("box_spiral", 10), 25 | ("center_ptr", 11), 26 | ("circle", 12), 27 | ("clock", 13), 28 | ("coffee_mug", 14), 29 | ("cross", 15), 30 | ("cross_reverse", 16), 31 | ("crosshair", 17), 32 | ("diamond_cross", 18), 33 | ("dot", 19), 34 | ("dotbox", 20), 35 | ("double_arrow", 21), 36 | ("draft_large", 22), 37 | ("draft_small", 23), 38 | ("draped_box", 24), 39 | ("exchange", 25), 40 | ("fleur", 26), 41 | ("gobbler", 27), 42 | ("gumby", 28), 43 | ("hand1", 29), 44 | ("hand2", 30), 45 | ("heart", 31), 46 | ("icon", 32), 47 | ("iron_cross", 33), 48 | ("left_ptr", 34), 49 | ("left_side", 35), 50 | ("left_tee", 36), 51 | ("leftbutton", 37), 52 | ("ll_angle", 38), 53 | ("lr_angle", 39), 54 | ("man", 40), 55 | ("middlebutton", 41), 56 | ("mouse", 42), 57 | ("pencil", 43), 58 | ("pirate", 44), 59 | ("plus", 45), 60 | ("question_arrow", 46), 61 | ("right_ptr", 47), 62 | ("right_side", 48), 63 | ("right_tee", 49), 64 | ("rightbutton", 50), 65 | ("rtl_logo", 51), 66 | ("sailboat", 52), 67 | ("sb_down_arrow", 53), 68 | ("sb_h_double_arrow", 54), 69 | ("sb_left_arrow", 55), 70 | ("sb_right_arrow", 56), 71 | ("sb_up_arrow", 57), 72 | ("sb_v_double_arrow", 58), 73 | ("shuttle", 59), 74 | ("sizing", 60), 75 | ("spider", 61), 76 | ("spraycan", 62), 77 | ("star", 63), 78 | ("target", 64), 79 | ("tcross", 65), 80 | ("top_left_arrow", 66), 81 | ("top_left_corner", 67), 82 | ("top_right_corner", 68), 83 | ("top_side", 69), 84 | ("top_tee", 70), 85 | ("trek", 71), 86 | ("ul_angle", 72), 87 | ("umbrella", 73), 88 | ("ur_angle", 74), 89 | ("watch", 75), 90 | ("xterm", 76), 91 | ]; 92 | 93 | /// Find a core cursor based on its name 94 | /// 95 | /// This function checks a built-in list of known names. 96 | fn cursor_shape_to_id(name: &str) -> Option { 97 | CORE_CURSORS 98 | .iter() 99 | .find(|&(name2, _)| name == *name2) 100 | .map(|&(_, id)| id) 101 | } 102 | 103 | #[cfg(test)] 104 | mod test_cursor_shape_to_id { 105 | use super::cursor_shape_to_id; 106 | 107 | #[test] 108 | fn test_cursor_shape_to_id() { 109 | assert_eq!(cursor_shape_to_id("heart"), Some(31)); 110 | } 111 | } 112 | 113 | /// The result of finding a cursor 114 | #[derive(Debug)] 115 | pub(crate) enum Cursor { 116 | /// The cursor is a core cursor that can be created with xproto's `CreateGlyphCursor` 117 | CoreChar(u16), 118 | 119 | /// A cursor file was opened 120 | File(File), 121 | } 122 | 123 | /// Find a cursor file based on the name of a cursor theme and the name of the cursor. 124 | pub(crate) fn find_cursor(theme: &str, name: &str) -> Option { 125 | if theme == "core" { 126 | if let Some(id) = cursor_shape_to_id(name) { 127 | return Some(Cursor::CoreChar(id)); 128 | } 129 | } 130 | if let Some(path) = CursorTheme::load(theme).load_icon(name) { 131 | if let Ok(file) = File::open(path) { 132 | return Some(Cursor::File(file)); 133 | } 134 | } 135 | None 136 | } 137 | -------------------------------------------------------------------------------- /x11rb/src/cursor/parse_cursor.rs: -------------------------------------------------------------------------------- 1 | //! Parse the contents of a cursor file 2 | 3 | // This code is loosely based on parse_cursor_file.c from libxcb-cursor, which is: 4 | // Copyright © 2013 Michael Stapelberg 5 | // and is covered by MIT/X Consortium License 6 | 7 | use std::io::{Read, Seek}; 8 | use xcursor::parser::{parse_xcursor_stream, Image}; 9 | 10 | /// An error that occurred while parsing 11 | #[derive(Debug)] 12 | pub(crate) enum Error { 13 | /// An I/O error occurred 14 | Io, 15 | 16 | /// The file contains no images 17 | NoImages, 18 | } 19 | 20 | impl From for Error { 21 | fn from(_: std::io::Error) -> Self { 22 | Error::Io 23 | } 24 | } 25 | 26 | /// Find the size of the image in the toc with a size as close as possible to the desired size. 27 | fn find_best_size(images: &[Image], desired_size: u32) -> Result { 28 | fn distance(a: u32, b: u32) -> u32 { 29 | a.max(b) - a.min(b) 30 | } 31 | 32 | fn is_better(desired_size: u32, entry: &Image, result: &Result) -> bool { 33 | match result { 34 | Err(_) => true, 35 | Ok(size) => distance(entry.size, desired_size) < distance(*size, desired_size), 36 | } 37 | } 38 | 39 | let mut result = Err(Error::NoImages); 40 | for entry in images { 41 | // If this is better than the best so far, replace best 42 | if is_better(desired_size, entry, &result) { 43 | result = Ok(entry.size) 44 | } 45 | } 46 | result 47 | } 48 | 49 | /// Parse a complete cursor file 50 | pub(crate) fn parse_cursor( 51 | input: &mut R, 52 | desired_size: u32, 53 | ) -> Result, Error> { 54 | let mut cursors = parse_xcursor_stream(input)?; 55 | let size = find_best_size(&cursors, desired_size)?; 56 | cursors.retain(|image| image.size == size); 57 | Ok(cursors) 58 | } 59 | 60 | #[cfg(test)] 61 | mod test { 62 | use super::{find_best_size, Error, Image}; 63 | 64 | #[test] 65 | fn find_best_size_empty_input() { 66 | let res = find_best_size(&[], 42); 67 | match res { 68 | Err(Error::NoImages) => {} 69 | r => panic!("Unexpected result {:?}", r), 70 | } 71 | } 72 | 73 | fn fake_image_with_size(size: u32) -> Image { 74 | Image { 75 | size, 76 | width: 42, 77 | height: 42, 78 | xhot: 0, 79 | yhot: 0, 80 | delay: 0, 81 | pixels_rgba: Vec::new(), 82 | pixels_argb: Vec::new(), 83 | } 84 | } 85 | 86 | #[test] 87 | fn find_best_size_one_input() { 88 | let input = [fake_image_with_size(42)]; 89 | assert_eq!(42, find_best_size(&input, 10).unwrap()); 90 | } 91 | 92 | #[test] 93 | fn find_best_size_selects_better() { 94 | let input = [ 95 | fake_image_with_size(42), 96 | fake_image_with_size(32), 97 | fake_image_with_size(3), 98 | fake_image_with_size(22), 99 | ]; 100 | assert_eq!(3, find_best_size(&input, 10).unwrap()); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /x11rb/src/event_loop_integration.rs: -------------------------------------------------------------------------------- 1 | //! # Integrating x11rb with an Event Loop 2 | //! 3 | //! To integrate x11rb with an event loop, 4 | //! [`std::os::unix::io::AsRawFd`](https://doc.rust-lang.org/std/os/unix/io/trait.AsRawFd.html) is 5 | //! implemented by [`RustConnection`](../rust_connection/struct.RustConnection.html)'s 6 | //! [`DefaultStream`](../rust_connection/struct.DefaultStream.html#impl-AsRawFd) and 7 | //! [`XCBConnection`](../xcb_ffi/struct.XCBConnection.html#impl-AsRawFd). This allows to integrate 8 | //! with an event loop that also handles timeouts or network I/O. See 9 | //! [`xclock_utc`](https://github.com/psychon/x11rb/blob/master/x11rb/examples/xclock_utc.rs) for an 10 | //! example. 11 | //! 12 | //! The general form of such an integration could be as follows: 13 | //! ```no_run 14 | //! #[cfg(unix)] 15 | //! use std::os::unix::io::{AsRawFd, RawFd}; 16 | //! #[cfg(windows)] 17 | //! use std::os::windows::io::{AsRawSocket, RawSocket}; 18 | //! use x11rb::connection::Connection; 19 | //! use x11rb::rust_connection::RustConnection; 20 | //! use x11rb::errors::ConnectionError; 21 | //! 22 | //! fn main_loop(conn: &RustConnection) -> Result<(), ConnectionError> { 23 | //! #[cfg(unix)] 24 | //! let raw_handle = conn.stream().as_raw_fd(); 25 | //! #[cfg(windows)] 26 | //! let raw_handle = conn.stream().as_raw_socket(); 27 | //! loop { 28 | //! while let Some(event) = conn.poll_for_event()? { 29 | //! handle_event(event); 30 | //! } 31 | //! 32 | //! poll_for_readable(raw_handle); 33 | //! 34 | //! // Do other work here. 35 | //! } 36 | //! } 37 | //! # fn handle_event(event: T) {} 38 | //! # fn poll_for_readable(event: T) {} 39 | //! ``` 40 | //! The function `poll_for_readable` could wait for any number of I/O streams (besides the one from 41 | //! x11rb) to become readable. It can also implement timeouts, as seen in the 42 | //! [`xclock_utc` example](https://github.com/psychon/x11rb/blob/master/x11rb/examples/xclock_utc.rs). 43 | //! 44 | //! 45 | //! ## Threads and Races 46 | //! 47 | //! Both [`RustConnection`](../rust_connection/struct.RustConnection.html) and 48 | //! [`XCBConnection`](../xcb_ffi/struct.XCBConnection.html) are `Sync+Send`. However, it is still 49 | //! possible to see races in the presence of threads and an event loop. 50 | //! 51 | //! The underlying problem is that the following two points are not equivalent: 52 | //! 53 | //! 1. A new event is available and can be returned from `conn.poll_for_event()`. 54 | //! 2. The underlying I/O stream is readable. 55 | //! 56 | //! The reason for this is an internal buffer that is required: When an event is received from the 57 | //! X11 server, but we are currently not in `conn.poll_for_event()`, then this event is added to an 58 | //! internal buffer. Thus, it can happen that there is an event available, but the stream is not 59 | //! readable. 60 | //! 61 | //! An example for such an other function is `conn.get_input_focus()?.reply()?`: The 62 | //! `GetInputFocus` request is sent to the server and then `reply()` waits for the reply. It does 63 | //! so by reading X11 packets from the X11 server until the right reply arrives. Any events that 64 | //! are read during this are buffered internally in the `Connection`. 65 | //! 66 | //! If this race occurs, the main loop would sit in `poll_for_readable` and wait, while the already 67 | //! buffered event is available. When something else wakes up the main loop and 68 | //! `conn.poll_for_event()` is called the next time, the event is finally processed. 69 | //! 70 | //! There are two ways around this: 71 | //! 72 | //! 1. Only interact with x11rb from one thread. 73 | //! 2. Use a dedicated thread for waiting for event. 74 | //! 75 | //! In case (1), one can call `conn.poll_for_event()` before waiting for the underlying I/O stream 76 | //! to be readable. Since there are no other threads, nothing can read a new event from the stream 77 | //! after `conn.poll_for_event()` returned `None`. 78 | //! 79 | //! Option (2) is to start a thread that calls `conn.wait_for_event()` in a loop. This is basically 80 | //! a dedicated event loop for fetching events from the X11 server. All other threads can now 81 | //! freely use the X11 connection without events possibly getting stuck and only being processed 82 | //! later. 83 | -------------------------------------------------------------------------------- /x11rb/src/protocol/bigreq.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the `BigRequests` X11 extension. 5 | 6 | #![allow(clippy::too_many_arguments)] 7 | 8 | #[allow(unused_imports)] 9 | use std::borrow::Cow; 10 | #[allow(unused_imports)] 11 | use std::convert::TryInto; 12 | #[allow(unused_imports)] 13 | use crate::utils::RawFdContainer; 14 | #[allow(unused_imports)] 15 | use crate::x11_utils::{Request, RequestHeader, Serialize, TryParse, TryParseFd}; 16 | use std::io::IoSlice; 17 | use crate::connection::RequestConnection; 18 | #[allow(unused_imports)] 19 | use crate::connection::Connection as X11Connection; 20 | #[allow(unused_imports)] 21 | use crate::cookie::{Cookie, CookieWithFds, VoidCookie}; 22 | use crate::errors::ConnectionError; 23 | #[allow(unused_imports)] 24 | use crate::errors::ReplyOrIdError; 25 | 26 | pub use x11rb_protocol::protocol::bigreq::*; 27 | 28 | /// Get the major opcode of this extension 29 | fn major_opcode(conn: &Conn) -> Result { 30 | let info = conn.extension_information(X11_EXTENSION_NAME)?; 31 | let info = info.ok_or(ConnectionError::UnsupportedExtension)?; 32 | Ok(info.major_opcode) 33 | } 34 | 35 | /// Enable the BIG-REQUESTS extension. 36 | /// 37 | /// This enables the BIG-REQUESTS extension, which allows for requests larger than 38 | /// 262140 bytes in length. When enabled, if the 16-bit length field is zero, it 39 | /// is immediately followed by a 32-bit length field specifying the length of the 40 | /// request in 4-byte units. 41 | pub fn enable(conn: &Conn) -> Result, ConnectionError> 42 | where 43 | Conn: RequestConnection + ?Sized, 44 | { 45 | let request0 = EnableRequest; 46 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 47 | let slices = [IoSlice::new(&bytes[0])]; 48 | assert_eq!(slices.len(), bytes.len()); 49 | conn.send_request_with_reply(&slices, fds) 50 | } 51 | 52 | /// Extension trait defining the requests of this extension. 53 | pub trait ConnectionExt: RequestConnection { 54 | /// Enable the BIG-REQUESTS extension. 55 | /// 56 | /// This enables the BIG-REQUESTS extension, which allows for requests larger than 57 | /// 262140 bytes in length. When enabled, if the 16-bit length field is zero, it 58 | /// is immediately followed by a 32-bit length field specifying the length of the 59 | /// request in 4-byte units. 60 | fn bigreq_enable(&self) -> Result, ConnectionError> 61 | { 62 | enable(self) 63 | } 64 | } 65 | 66 | impl ConnectionExt for C {} 67 | -------------------------------------------------------------------------------- /x11rb/src/protocol/ge.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the `GenericEvent` X11 extension. 5 | 6 | #![allow(clippy::too_many_arguments)] 7 | 8 | #[allow(unused_imports)] 9 | use std::borrow::Cow; 10 | #[allow(unused_imports)] 11 | use std::convert::TryInto; 12 | #[allow(unused_imports)] 13 | use crate::utils::RawFdContainer; 14 | #[allow(unused_imports)] 15 | use crate::x11_utils::{Request, RequestHeader, Serialize, TryParse, TryParseFd}; 16 | use std::io::IoSlice; 17 | use crate::connection::RequestConnection; 18 | #[allow(unused_imports)] 19 | use crate::connection::Connection as X11Connection; 20 | #[allow(unused_imports)] 21 | use crate::cookie::{Cookie, CookieWithFds, VoidCookie}; 22 | use crate::errors::ConnectionError; 23 | #[allow(unused_imports)] 24 | use crate::errors::ReplyOrIdError; 25 | 26 | pub use x11rb_protocol::protocol::ge::*; 27 | 28 | /// Get the major opcode of this extension 29 | fn major_opcode(conn: &Conn) -> Result { 30 | let info = conn.extension_information(X11_EXTENSION_NAME)?; 31 | let info = info.ok_or(ConnectionError::UnsupportedExtension)?; 32 | Ok(info.major_opcode) 33 | } 34 | 35 | pub fn query_version(conn: &Conn, client_major_version: u16, client_minor_version: u16) -> Result, ConnectionError> 36 | where 37 | Conn: RequestConnection + ?Sized, 38 | { 39 | let request0 = QueryVersionRequest { 40 | client_major_version, 41 | client_minor_version, 42 | }; 43 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 44 | let slices = [IoSlice::new(&bytes[0])]; 45 | assert_eq!(slices.len(), bytes.len()); 46 | conn.send_request_with_reply(&slices, fds) 47 | } 48 | 49 | /// Extension trait defining the requests of this extension. 50 | pub trait ConnectionExt: RequestConnection { 51 | fn ge_query_version(&self, client_major_version: u16, client_minor_version: u16) -> Result, ConnectionError> 52 | { 53 | query_version(self, client_major_version, client_minor_version) 54 | } 55 | } 56 | 57 | impl ConnectionExt for C {} 58 | -------------------------------------------------------------------------------- /x11rb/src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the X11 protocol. 5 | //! 6 | //! Each sub-module of this module corresponds to one X11 extension. It contains all the 7 | //! definitions from that extension. The core X11 protocol is in [`xproto`](xproto/index.html). 8 | 9 | // Clippy does not like some names from the XML. 10 | #![allow(clippy::upper_case_acronyms)] 11 | // This is not easy to fix, so ignore it. 12 | #![allow(clippy::needless_borrow, clippy::needless_lifetimes)] 13 | pub mod xproto; 14 | pub mod bigreq; 15 | #[cfg(feature = "composite")] 16 | pub mod composite; 17 | #[cfg(feature = "damage")] 18 | pub mod damage; 19 | #[cfg(feature = "dbe")] 20 | pub mod dbe; 21 | #[cfg(feature = "dpms")] 22 | pub mod dpms; 23 | #[cfg(feature = "dri2")] 24 | pub mod dri2; 25 | #[cfg(feature = "dri3")] 26 | pub mod dri3; 27 | pub mod ge; 28 | #[cfg(feature = "glx")] 29 | pub mod glx; 30 | #[cfg(feature = "present")] 31 | pub mod present; 32 | #[cfg(feature = "randr")] 33 | pub mod randr; 34 | #[cfg(feature = "record")] 35 | pub mod record; 36 | #[cfg(feature = "render")] 37 | pub mod render; 38 | #[cfg(feature = "res")] 39 | pub mod res; 40 | #[cfg(feature = "screensaver")] 41 | pub mod screensaver; 42 | #[cfg(feature = "shape")] 43 | pub mod shape; 44 | #[cfg(feature = "shm")] 45 | pub mod shm; 46 | #[cfg(feature = "sync")] 47 | pub mod sync; 48 | pub mod xc_misc; 49 | #[cfg(feature = "xevie")] 50 | pub mod xevie; 51 | #[cfg(feature = "xf86dri")] 52 | pub mod xf86dri; 53 | #[cfg(feature = "xf86vidmode")] 54 | pub mod xf86vidmode; 55 | #[cfg(feature = "xfixes")] 56 | pub mod xfixes; 57 | #[cfg(feature = "xinerama")] 58 | pub mod xinerama; 59 | #[cfg(feature = "xinput")] 60 | pub mod xinput; 61 | #[cfg(feature = "xkb")] 62 | pub mod xkb; 63 | #[cfg(feature = "xprint")] 64 | pub mod xprint; 65 | #[cfg(feature = "xselinux")] 66 | pub mod xselinux; 67 | #[cfg(feature = "xtest")] 68 | pub mod xtest; 69 | #[cfg(feature = "xv")] 70 | pub mod xv; 71 | #[cfg(feature = "xvmc")] 72 | pub mod xvmc; 73 | 74 | pub use x11rb_protocol::protocol::Request; 75 | pub use x11rb_protocol::protocol::Reply; 76 | pub use x11rb_protocol::protocol::ErrorKind; 77 | pub use x11rb_protocol::protocol::Event; 78 | -------------------------------------------------------------------------------- /x11rb/src/protocol/xc_misc.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the `XCMisc` X11 extension. 5 | 6 | #![allow(clippy::too_many_arguments)] 7 | 8 | #[allow(unused_imports)] 9 | use std::borrow::Cow; 10 | #[allow(unused_imports)] 11 | use std::convert::TryInto; 12 | #[allow(unused_imports)] 13 | use crate::utils::RawFdContainer; 14 | #[allow(unused_imports)] 15 | use crate::x11_utils::{Request, RequestHeader, Serialize, TryParse, TryParseFd}; 16 | use std::io::IoSlice; 17 | use crate::connection::RequestConnection; 18 | #[allow(unused_imports)] 19 | use crate::connection::Connection as X11Connection; 20 | #[allow(unused_imports)] 21 | use crate::cookie::{Cookie, CookieWithFds, VoidCookie}; 22 | use crate::errors::ConnectionError; 23 | #[allow(unused_imports)] 24 | use crate::errors::ReplyOrIdError; 25 | 26 | pub use x11rb_protocol::protocol::xc_misc::*; 27 | 28 | /// Get the major opcode of this extension 29 | fn major_opcode(conn: &Conn) -> Result { 30 | let info = conn.extension_information(X11_EXTENSION_NAME)?; 31 | let info = info.ok_or(ConnectionError::UnsupportedExtension)?; 32 | Ok(info.major_opcode) 33 | } 34 | 35 | pub fn get_version(conn: &Conn, client_major_version: u16, client_minor_version: u16) -> Result, ConnectionError> 36 | where 37 | Conn: RequestConnection + ?Sized, 38 | { 39 | let request0 = GetVersionRequest { 40 | client_major_version, 41 | client_minor_version, 42 | }; 43 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 44 | let slices = [IoSlice::new(&bytes[0])]; 45 | assert_eq!(slices.len(), bytes.len()); 46 | conn.send_request_with_reply(&slices, fds) 47 | } 48 | 49 | pub fn get_xid_range(conn: &Conn) -> Result, ConnectionError> 50 | where 51 | Conn: RequestConnection + ?Sized, 52 | { 53 | let request0 = GetXIDRangeRequest; 54 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 55 | let slices = [IoSlice::new(&bytes[0])]; 56 | assert_eq!(slices.len(), bytes.len()); 57 | conn.send_request_with_reply(&slices, fds) 58 | } 59 | 60 | pub fn get_xid_list(conn: &Conn, count: u32) -> Result, ConnectionError> 61 | where 62 | Conn: RequestConnection + ?Sized, 63 | { 64 | let request0 = GetXIDListRequest { 65 | count, 66 | }; 67 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 68 | let slices = [IoSlice::new(&bytes[0])]; 69 | assert_eq!(slices.len(), bytes.len()); 70 | conn.send_request_with_reply(&slices, fds) 71 | } 72 | 73 | /// Extension trait defining the requests of this extension. 74 | pub trait ConnectionExt: RequestConnection { 75 | fn xc_misc_get_version(&self, client_major_version: u16, client_minor_version: u16) -> Result, ConnectionError> 76 | { 77 | get_version(self, client_major_version, client_minor_version) 78 | } 79 | fn xc_misc_get_xid_range(&self) -> Result, ConnectionError> 80 | { 81 | get_xid_range(self) 82 | } 83 | fn xc_misc_get_xid_list(&self, count: u32) -> Result, ConnectionError> 84 | { 85 | get_xid_list(self, count) 86 | } 87 | } 88 | 89 | impl ConnectionExt for C {} 90 | -------------------------------------------------------------------------------- /x11rb/src/protocol/xevie.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the `Xevie` X11 extension. 5 | 6 | #![allow(clippy::too_many_arguments)] 7 | 8 | #[allow(unused_imports)] 9 | use std::borrow::Cow; 10 | #[allow(unused_imports)] 11 | use std::convert::TryInto; 12 | #[allow(unused_imports)] 13 | use crate::utils::RawFdContainer; 14 | #[allow(unused_imports)] 15 | use crate::x11_utils::{Request, RequestHeader, Serialize, TryParse, TryParseFd}; 16 | use std::io::IoSlice; 17 | use crate::connection::RequestConnection; 18 | #[allow(unused_imports)] 19 | use crate::connection::Connection as X11Connection; 20 | #[allow(unused_imports)] 21 | use crate::cookie::{Cookie, CookieWithFds, VoidCookie}; 22 | use crate::errors::ConnectionError; 23 | #[allow(unused_imports)] 24 | use crate::errors::ReplyOrIdError; 25 | 26 | pub use x11rb_protocol::protocol::xevie::*; 27 | 28 | /// Get the major opcode of this extension 29 | fn major_opcode(conn: &Conn) -> Result { 30 | let info = conn.extension_information(X11_EXTENSION_NAME)?; 31 | let info = info.ok_or(ConnectionError::UnsupportedExtension)?; 32 | Ok(info.major_opcode) 33 | } 34 | 35 | pub fn query_version(conn: &Conn, client_major_version: u16, client_minor_version: u16) -> Result, ConnectionError> 36 | where 37 | Conn: RequestConnection + ?Sized, 38 | { 39 | let request0 = QueryVersionRequest { 40 | client_major_version, 41 | client_minor_version, 42 | }; 43 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 44 | let slices = [IoSlice::new(&bytes[0])]; 45 | assert_eq!(slices.len(), bytes.len()); 46 | conn.send_request_with_reply(&slices, fds) 47 | } 48 | 49 | pub fn start(conn: &Conn, screen: u32) -> Result, ConnectionError> 50 | where 51 | Conn: RequestConnection + ?Sized, 52 | { 53 | let request0 = StartRequest { 54 | screen, 55 | }; 56 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 57 | let slices = [IoSlice::new(&bytes[0])]; 58 | assert_eq!(slices.len(), bytes.len()); 59 | conn.send_request_with_reply(&slices, fds) 60 | } 61 | 62 | pub fn end(conn: &Conn, cmap: u32) -> Result, ConnectionError> 63 | where 64 | Conn: RequestConnection + ?Sized, 65 | { 66 | let request0 = EndRequest { 67 | cmap, 68 | }; 69 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 70 | let slices = [IoSlice::new(&bytes[0])]; 71 | assert_eq!(slices.len(), bytes.len()); 72 | conn.send_request_with_reply(&slices, fds) 73 | } 74 | 75 | pub fn send(conn: &Conn, event: Event, data_type: u32) -> Result, ConnectionError> 76 | where 77 | Conn: RequestConnection + ?Sized, 78 | { 79 | let request0 = SendRequest { 80 | event, 81 | data_type, 82 | }; 83 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 84 | let slices = [IoSlice::new(&bytes[0])]; 85 | assert_eq!(slices.len(), bytes.len()); 86 | conn.send_request_with_reply(&slices, fds) 87 | } 88 | 89 | pub fn select_input(conn: &Conn, event_mask: u32) -> Result, ConnectionError> 90 | where 91 | Conn: RequestConnection + ?Sized, 92 | { 93 | let request0 = SelectInputRequest { 94 | event_mask, 95 | }; 96 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 97 | let slices = [IoSlice::new(&bytes[0])]; 98 | assert_eq!(slices.len(), bytes.len()); 99 | conn.send_request_with_reply(&slices, fds) 100 | } 101 | 102 | /// Extension trait defining the requests of this extension. 103 | pub trait ConnectionExt: RequestConnection { 104 | fn xevie_query_version(&self, client_major_version: u16, client_minor_version: u16) -> Result, ConnectionError> 105 | { 106 | query_version(self, client_major_version, client_minor_version) 107 | } 108 | fn xevie_start(&self, screen: u32) -> Result, ConnectionError> 109 | { 110 | start(self, screen) 111 | } 112 | fn xevie_end(&self, cmap: u32) -> Result, ConnectionError> 113 | { 114 | end(self, cmap) 115 | } 116 | fn xevie_send(&self, event: Event, data_type: u32) -> Result, ConnectionError> 117 | { 118 | send(self, event, data_type) 119 | } 120 | fn xevie_select_input(&self, event_mask: u32) -> Result, ConnectionError> 121 | { 122 | select_input(self, event_mask) 123 | } 124 | } 125 | 126 | impl ConnectionExt for C {} 127 | -------------------------------------------------------------------------------- /x11rb/src/protocol/xtest.rs: -------------------------------------------------------------------------------- 1 | // This file contains generated code. Do not edit directly. 2 | // To regenerate this, run 'make'. 3 | 4 | //! Bindings to the `Test` X11 extension. 5 | 6 | #![allow(clippy::too_many_arguments)] 7 | 8 | #[allow(unused_imports)] 9 | use std::borrow::Cow; 10 | #[allow(unused_imports)] 11 | use std::convert::TryInto; 12 | #[allow(unused_imports)] 13 | use crate::utils::RawFdContainer; 14 | #[allow(unused_imports)] 15 | use crate::x11_utils::{Request, RequestHeader, Serialize, TryParse, TryParseFd}; 16 | use std::io::IoSlice; 17 | use crate::connection::RequestConnection; 18 | #[allow(unused_imports)] 19 | use crate::connection::Connection as X11Connection; 20 | #[allow(unused_imports)] 21 | use crate::cookie::{Cookie, CookieWithFds, VoidCookie}; 22 | use crate::errors::ConnectionError; 23 | #[allow(unused_imports)] 24 | use crate::errors::ReplyOrIdError; 25 | #[allow(unused_imports)] 26 | use super::xproto; 27 | 28 | pub use x11rb_protocol::protocol::xtest::*; 29 | 30 | /// Get the major opcode of this extension 31 | fn major_opcode(conn: &Conn) -> Result { 32 | let info = conn.extension_information(X11_EXTENSION_NAME)?; 33 | let info = info.ok_or(ConnectionError::UnsupportedExtension)?; 34 | Ok(info.major_opcode) 35 | } 36 | 37 | pub fn get_version(conn: &Conn, major_version: u8, minor_version: u16) -> Result, ConnectionError> 38 | where 39 | Conn: RequestConnection + ?Sized, 40 | { 41 | let request0 = GetVersionRequest { 42 | major_version, 43 | minor_version, 44 | }; 45 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 46 | let slices = [IoSlice::new(&bytes[0])]; 47 | assert_eq!(slices.len(), bytes.len()); 48 | conn.send_request_with_reply(&slices, fds) 49 | } 50 | 51 | pub fn compare_cursor(conn: &Conn, window: xproto::Window, cursor: xproto::Cursor) -> Result, ConnectionError> 52 | where 53 | Conn: RequestConnection + ?Sized, 54 | { 55 | let request0 = CompareCursorRequest { 56 | window, 57 | cursor, 58 | }; 59 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 60 | let slices = [IoSlice::new(&bytes[0])]; 61 | assert_eq!(slices.len(), bytes.len()); 62 | conn.send_request_with_reply(&slices, fds) 63 | } 64 | 65 | pub fn fake_input(conn: &Conn, type_: u8, detail: u8, time: u32, root: xproto::Window, root_x: i16, root_y: i16, deviceid: u8) -> Result, ConnectionError> 66 | where 67 | Conn: RequestConnection + ?Sized, 68 | { 69 | let request0 = FakeInputRequest { 70 | type_, 71 | detail, 72 | time, 73 | root, 74 | root_x, 75 | root_y, 76 | deviceid, 77 | }; 78 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 79 | let slices = [IoSlice::new(&bytes[0])]; 80 | assert_eq!(slices.len(), bytes.len()); 81 | conn.send_request_without_reply(&slices, fds) 82 | } 83 | 84 | pub fn grab_control(conn: &Conn, impervious: bool) -> Result, ConnectionError> 85 | where 86 | Conn: RequestConnection + ?Sized, 87 | { 88 | let request0 = GrabControlRequest { 89 | impervious, 90 | }; 91 | let (bytes, fds) = request0.serialize(major_opcode(conn)?); 92 | let slices = [IoSlice::new(&bytes[0])]; 93 | assert_eq!(slices.len(), bytes.len()); 94 | conn.send_request_without_reply(&slices, fds) 95 | } 96 | 97 | /// Extension trait defining the requests of this extension. 98 | pub trait ConnectionExt: RequestConnection { 99 | fn xtest_get_version(&self, major_version: u8, minor_version: u16) -> Result, ConnectionError> 100 | { 101 | get_version(self, major_version, minor_version) 102 | } 103 | fn xtest_compare_cursor(&self, window: xproto::Window, cursor: xproto::Cursor) -> Result, ConnectionError> 104 | { 105 | compare_cursor(self, window, cursor) 106 | } 107 | fn xtest_fake_input(&self, type_: u8, detail: u8, time: u32, root: xproto::Window, root_x: i16, root_y: i16, deviceid: u8) -> Result, ConnectionError> 108 | { 109 | fake_input(self, type_, detail, time, root, root_x, root_y, deviceid) 110 | } 111 | fn xtest_grab_control(&self, impervious: bool) -> Result, ConnectionError> 112 | { 113 | grab_control(self, impervious) 114 | } 115 | } 116 | 117 | impl ConnectionExt for C {} 118 | -------------------------------------------------------------------------------- /x11rb/src/resource_manager/mod.rs: -------------------------------------------------------------------------------- 1 | //! X11 resource manager library. 2 | //! 3 | //! Usage example (please cache the database returned by [`new_from_default`] in real applications 4 | //! instead of re-opening it whenever a value is needed): 5 | //! ``` 6 | //! use x11rb::{connection::Connection, errors::ReplyError, resource_manager::new_from_default}; 7 | //! fn get_xft_dpi(conn: &impl Connection) -> Result, ReplyError> { 8 | //! let db = new_from_default(conn)?; 9 | //! let value = db.get_value("Xft.dpi", ""); 10 | //! Ok(value.ok().flatten()) 11 | //! } 12 | //! ``` 13 | //! 14 | //! This functionality is similar to what is available to C code through xcb-util-xrm and Xlib's 15 | //! `Xrm*` function family. Not all their functionality is available in this library. Please open a 16 | //! feature request if you need something that is not available. 17 | //! 18 | //! The code in this module is only available when the `resource_manager` feature of the library is 19 | //! enabled. 20 | 21 | use crate::connection::Connection; 22 | use crate::errors::ReplyError; 23 | use crate::protocol::xproto::GetPropertyReply; 24 | 25 | pub use x11rb_protocol::resource_manager::Database; 26 | 27 | fn send_request(conn: &impl Connection) -> Result { 28 | let mut request = Database::GET_RESOURCE_DATABASE; 29 | request.window = conn.setup().roots[0].root; 30 | conn.send_trait_request_with_reply(request)?.reply() 31 | } 32 | 33 | /// Create a new X11 resource database from the `RESOURCE_MANAGER` property of the first 34 | /// screen's root window. 35 | /// 36 | /// This function returns an error if the `GetProperty` request to get the `RESOURCE_MANAGER` 37 | /// property fails. It returns `Ok(None)` if the property does not exist, has the wrong format, 38 | /// or is empty. 39 | pub fn new_from_resource_manager(conn: &impl Connection) -> Result, ReplyError> { 40 | Ok(Database::new_from_get_property_reply(&send_request(conn)?)) 41 | } 42 | 43 | /// Create a new X11 resource database from the default locations. 44 | /// 45 | /// The default location is a combination of two places. First, the following places are 46 | /// searched for data: 47 | /// - The `RESOURCE_MANAGER` property of the first screen's root window (See 48 | /// [`new_from_resource_manager`]). 49 | /// - If not found, the file `$HOME/.Xresources` is loaded. 50 | /// - If not found, the file `$HOME/.Xdefaults` is loaded. 51 | /// 52 | /// The result of the above search of the above search is combined with: 53 | /// - The contents of the file `$XENVIRONMENT`, if this environment variable is set. 54 | /// - Otherwise, the contents of `$HOME/.Xdefaults-[hostname]`. 55 | /// 56 | /// This function only returns an error if communication with the X11 server fails. All other 57 | /// errors are ignored. It might be that an empty database is returned. 58 | /// 59 | /// The behaviour of this function is mostly equivalent to Xlib's `XGetDefault()`. The 60 | /// exception is that `XGetDefault()` does not load `$HOME/.Xresources`. 61 | /// 62 | /// The behaviour of this function is equivalent to xcb-util-xrm's 63 | /// `xcb_xrm_database_from_default()`. 64 | pub fn new_from_default(conn: &impl Connection) -> Result { 65 | Ok(Database::new_from_default( 66 | &send_request(conn)?, 67 | gethostname::gethostname(), 68 | )) 69 | } 70 | -------------------------------------------------------------------------------- /x11rb/src/test.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::sync::Arc; 3 | 4 | use crate::connection::Connection; 5 | use crate::errors::ReplyOrIdError; 6 | use crate::protocol::xproto::{ 7 | ColormapAlloc, ColormapWrapper, FontWrapper, ModMask, SendEventDest, VisualClass, Visualid, 8 | Window, 9 | }; 10 | 11 | #[test] 12 | fn test_enum_debug() { 13 | assert_eq!("TRUE_COLOR", format!("{:?}", VisualClass::TRUE_COLOR)); 14 | assert_eq!("TrueColor", format!("{:#?}", VisualClass::TRUE_COLOR)); 15 | assert_eq!( 16 | "POINTER_WINDOW", 17 | format!("{:?}", SendEventDest::POINTER_WINDOW) 18 | ); 19 | assert_eq!( 20 | "PointerWindow", 21 | format!("{:#?}", SendEventDest::POINTER_WINDOW) 22 | ); 23 | assert_eq!("ITEM_FOCUS", format!("{:?}", SendEventDest::ITEM_FOCUS)); 24 | assert_eq!("ItemFocus", format!("{:#?}", SendEventDest::ITEM_FOCUS)); 25 | } 26 | 27 | #[test] 28 | fn test_bitmask_debug() { 29 | assert_eq!("SHIFT", format!("{:?}", ModMask::SHIFT)); 30 | assert_eq!("Shift", format!("{:#?}", ModMask::SHIFT)); 31 | assert_eq!( 32 | "SHIFT | LOCK", 33 | format!("{:?}", ModMask::SHIFT | ModMask::LOCK) 34 | ); 35 | assert_eq!( 36 | "Shift | Lock", 37 | format!("{:#?}", ModMask::SHIFT | ModMask::LOCK) 38 | ); 39 | assert_eq!("0", format!("{:?}", ModMask::from(0u8))); 40 | } 41 | 42 | fn _compile_test_arc_in_wrapper( 43 | conn: &Arc, 44 | ) -> Result>, ReplyOrIdError> { 45 | FontWrapper::open_font(Arc::clone(conn), b"font") 46 | } 47 | 48 | fn _compile_test_rc_in_wrapper( 49 | conn: &Rc, 50 | window: Window, 51 | visual: Visualid, 52 | ) -> Result>, ReplyOrIdError> { 53 | ColormapWrapper::create_colormap(Rc::clone(conn), ColormapAlloc::NONE, window, visual) 54 | } 55 | -------------------------------------------------------------------------------- /x11rb/src/tracing.rs: -------------------------------------------------------------------------------- 1 | //! Wrapper around tracing so that tracing can be an optional dependency. 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | #[allow(dead_code)] 5 | pub(crate) enum Level { 6 | Error, 7 | Warn, 8 | Info, 9 | Debug, 10 | Trace, 11 | } 12 | 13 | #[cfg(feature = "tracing")] 14 | pub(crate) mod implementation { 15 | impl super::Level { 16 | pub(crate) const fn to_tracing(self) -> tracing::Level { 17 | match self { 18 | Self::Error => tracing::Level::ERROR, 19 | Self::Warn => tracing::Level::WARN, 20 | Self::Info => tracing::Level::INFO, 21 | Self::Debug => tracing::Level::DEBUG, 22 | Self::Trace => tracing::Level::TRACE, 23 | } 24 | } 25 | } 26 | 27 | macro_rules! event { 28 | ( $lvl:expr, $($arg:tt)+ ) => { 29 | tracing::event!($crate::tracing::Level::to_tracing($lvl), $($arg)+) 30 | } 31 | } 32 | 33 | macro_rules! span { 34 | ( $lvl:expr, $name:expr, $($fields:tt)* ) => { 35 | tracing::span!($crate::tracing::Level::to_tracing($lvl), $name, $($fields)*) 36 | } 37 | } 38 | 39 | pub(crate) use event; 40 | pub(crate) use span; 41 | } 42 | 43 | #[cfg(not(feature = "tracing"))] 44 | pub(crate) mod implementation { 45 | macro_rules! event { 46 | ( $lvl:expr, { $($fields:tt)+ }, $($arg:tt)+ ) => { 47 | let _ = format_args!($($arg)+); 48 | }; 49 | ( $lvl:expr, $($arg:tt)+ ) => { 50 | let _ = format_args!($($arg)+); 51 | }; 52 | } 53 | 54 | pub(crate) struct Span; 55 | pub(crate) struct EnteredSpan; 56 | 57 | impl Span { 58 | pub(crate) fn entered(&self) -> EnteredSpan { 59 | EnteredSpan 60 | } 61 | } 62 | 63 | macro_rules! span { 64 | ( $lvl:expr, $name:expr, $($fields:tt)* ) => { 65 | $crate::tracing::implementation::Span 66 | }; 67 | } 68 | 69 | pub(crate) use event; 70 | pub(crate) use span; 71 | } 72 | 73 | macro_rules! error { 74 | ( $($arg:tt)+ ) => { $crate::tracing::implementation::event!($crate::tracing::Level::Error, $($arg)+) }; 75 | } 76 | macro_rules! warning { 77 | ( $($arg:tt)+ ) => { $crate::tracing::implementation::event!($crate::tracing::Level::Warn, $($arg)+) }; 78 | } 79 | macro_rules! info { 80 | ( $($arg:tt)+ ) => { $crate::tracing::implementation::event!($crate::tracing::Level::Info, $($arg)+) }; 81 | } 82 | macro_rules! debug { 83 | ( $($arg:tt)+ ) => { $crate::tracing::implementation::event!($crate::tracing::Level::Debug, $($arg)+) }; 84 | } 85 | macro_rules! trace { 86 | ( $($arg:tt)+ ) => { $crate::tracing::implementation::event!($crate::tracing::Level::Trace, $($arg)+) }; 87 | } 88 | 89 | pub(crate) use debug; 90 | pub(crate) use error; 91 | pub(crate) use info; 92 | pub(crate) use trace; 93 | pub(crate) use warning; 94 | 95 | #[allow(unused_macros)] 96 | macro_rules! error_span { 97 | ( $name:expr ) => { $crate::tracing::implementation::span!($crate::tracing::Level::Error, $name, ) }; 98 | ( $name:expr, $($fields:tt)* ) => { $crate::tracing::implementation::span!($crate::tracing::Level::Error, $name, $($fields)*) }; 99 | } 100 | #[allow(unused_macros)] 101 | macro_rules! warning_span { 102 | ( $name:expr ) => { $crate::tracing::implementation::span!($crate::tracing::Level::Warn, $name, ) }; 103 | ( $name:expr, $($fields:tt)* ) => { $crate::tracing::implementation::span!($crate::tracing::Level::Warn, $name, $($fields)*) }; 104 | } 105 | #[allow(unused_macro_rules)] 106 | macro_rules! info_span { 107 | ( $name:expr ) => { $crate::tracing::implementation::span!($crate::tracing::Level::Info, $name, ) }; 108 | ( $name:expr, $($fields:tt)* ) => { $crate::tracing::implementation::span!($crate::tracing::Level::Info, $name, $($fields)*) }; 109 | } 110 | macro_rules! debug_span { 111 | ( $name:expr ) => { $crate::tracing::implementation::span!($crate::tracing::Level::Debug, $name, ) }; 112 | ( $name:expr, $($fields:tt)* ) => { $crate::tracing::implementation::span!($crate::tracing::Level::Debug, $name, $($fields)*) }; 113 | } 114 | #[allow(unused_macro_rules)] 115 | macro_rules! trace_span { 116 | ( $name:expr ) => { $crate::tracing::implementation::span!($crate::tracing::Level::Trace, $name, ) }; 117 | ( $name:expr, $($fields:tt)* ) => { $crate::tracing::implementation::span!($crate::tracing::Level::Trace, $name, $($fields)*) }; 118 | } 119 | 120 | pub(crate) use debug_span; 121 | #[allow(unused_imports)] 122 | pub(crate) use error_span; 123 | pub(crate) use info_span; 124 | pub(crate) use trace_span; 125 | #[allow(unused_imports)] 126 | pub(crate) use warning_span; 127 | -------------------------------------------------------------------------------- /x11rb/src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions that are not specific to X11. 2 | //! 3 | //! # CSlice 4 | //! 5 | //! [`CSlice`] is a wrapper around some bytes in memory. It is unsafe to construct, but takes 6 | //! ownership of the bytes and allows accessing them as a `[u8]`. When dropped, the underlying 7 | //! memory is freed via [`libc::free`]. 8 | //! 9 | //! `CSlice` is only available when the `allow-unsafe-code` feature is enabled. 10 | 11 | pub use x11rb_protocol::RawFdContainer; 12 | 13 | #[cfg(feature = "allow-unsafe-code")] 14 | mod unsafe_code { 15 | use std::mem::forget; 16 | use std::ops::{Deref, Index}; 17 | use std::ptr::NonNull; 18 | use std::slice::{from_raw_parts, SliceIndex}; 19 | 20 | use libc::free; 21 | 22 | /// Wrapper around a slice that was allocated in C code. 23 | /// 24 | /// `CSlice` is only available when the `allow-unsafe-code` feature is enabled. 25 | pub struct CSlice { 26 | ptr: NonNull<[u8]>, 27 | } 28 | 29 | // `CSlice` is `Send` and `Sync` because, once created, it is 30 | // completely immutable (it does not have interior mutability), 31 | // so it can be safely accessed from different threads simultaneously. 32 | unsafe impl Send for CSlice {} 33 | unsafe impl Sync for CSlice {} 34 | 35 | impl CSlice { 36 | /// Constructs a new `CSlice` from the given parts. `libc::free` will be called on the given 37 | /// pointer when the slice is dropped. 38 | /// 39 | /// # Safety 40 | /// 41 | /// The same rules as for `std::slice::from_raw_parts` apply. Additionally, the given pointer 42 | /// must be safe to free with `libc::free`. 43 | pub unsafe fn new(ptr: *const u8, len: usize) -> CSlice { 44 | CSlice { 45 | ptr: NonNull::from(from_raw_parts(ptr, len)), 46 | } 47 | } 48 | 49 | /// Convert `self` into a raw part. 50 | /// 51 | /// Ownership of the returned pointer is given to the caller. Specifically, `libc::free` will 52 | /// not be called on it by `CSlice`. 53 | pub fn into_ptr(self) -> *const u8 { 54 | let ptr = self.ptr.as_ptr() as *const u8; 55 | forget(self); 56 | ptr 57 | } 58 | } 59 | 60 | impl Drop for CSlice { 61 | fn drop(&mut self) { 62 | unsafe { free(self.ptr.as_ptr() as _) } 63 | } 64 | } 65 | 66 | impl Deref for CSlice { 67 | type Target = [u8]; 68 | 69 | fn deref(&self) -> &[u8] { 70 | unsafe { self.ptr.as_ref() } 71 | } 72 | } 73 | 74 | impl AsRef<[u8]> for CSlice { 75 | fn as_ref(&self) -> &[u8] { 76 | self 77 | } 78 | } 79 | 80 | impl Index for CSlice 81 | where 82 | I: SliceIndex<[u8]>, 83 | { 84 | type Output = I::Output; 85 | 86 | fn index(&self, index: I) -> &I::Output { 87 | (**self).index(index) 88 | } 89 | } 90 | 91 | impl std::fmt::Debug for CSlice { 92 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 93 | std::fmt::Debug::fmt(&**self, f) 94 | } 95 | } 96 | } 97 | 98 | #[cfg(feature = "allow-unsafe-code")] 99 | pub use unsafe_code::CSlice; 100 | -------------------------------------------------------------------------------- /x11rb/src/wrapper.rs: -------------------------------------------------------------------------------- 1 | //! Some wrappers around the generated code to simplify use. 2 | 3 | use super::cookie::VoidCookie; 4 | use super::errors::{ConnectionError, ReplyError}; 5 | use super::protocol::xproto::{Atom, ConnectionExt as XProtoConnectionExt, PropMode, Window}; 6 | 7 | /// Extension trait that simplifies API use 8 | pub trait ConnectionExt: XProtoConnectionExt { 9 | /// Change a property on a window with format 8. 10 | fn change_property8( 11 | &self, 12 | mode: PropMode, 13 | window: Window, 14 | property: A, 15 | type_: B, 16 | data: &[u8], 17 | ) -> Result, ConnectionError> 18 | where 19 | A: Into, 20 | B: Into, 21 | { 22 | self.change_property( 23 | mode, 24 | window, 25 | property, 26 | type_, 27 | 8, 28 | data.len().try_into().expect("`data` has too many elements"), 29 | data, 30 | ) 31 | } 32 | 33 | /// Change a property on a window with format 16. 34 | fn change_property16( 35 | &self, 36 | mode: PropMode, 37 | window: Window, 38 | property: A, 39 | type_: B, 40 | data: &[u16], 41 | ) -> Result, ConnectionError> 42 | where 43 | A: Into, 44 | B: Into, 45 | { 46 | let mut data_u8 = Vec::with_capacity(data.len() * 2); 47 | for item in data { 48 | data_u8.extend(item.to_ne_bytes()); 49 | } 50 | self.change_property( 51 | mode, 52 | window, 53 | property, 54 | type_, 55 | 16, 56 | data.len().try_into().expect("`data` has too many elements"), 57 | &data_u8, 58 | ) 59 | } 60 | 61 | /// Change a property on a window with format 32. 62 | fn change_property32( 63 | &self, 64 | mode: PropMode, 65 | window: Window, 66 | property: A, 67 | type_: B, 68 | data: &[u32], 69 | ) -> Result, ConnectionError> 70 | where 71 | A: Into, 72 | B: Into, 73 | { 74 | let mut data_u8 = Vec::with_capacity(data.len() * 4); 75 | for item in data { 76 | data_u8.extend(item.to_ne_bytes()); 77 | } 78 | self.change_property( 79 | mode, 80 | window, 81 | property, 82 | type_, 83 | 32, 84 | data.len().try_into().expect("`data` has too many elements"), 85 | &data_u8, 86 | ) 87 | } 88 | 89 | /// Synchronise with the X11 server. 90 | /// 91 | /// This function synchronises with the X11 server. This means that all requests that are still 92 | /// in the output buffer are sent to the server. Then, we wait until the X11 server processed 93 | /// all requests. 94 | fn sync(&self) -> Result<(), ReplyError> { 95 | // When a new request is generated, it is appended to the output buffer. Thus, this causes 96 | // all previous requests to be sent. 97 | // The X11 server is single-threaded and processes requests in-order. Thus, it will only 98 | // reply to our GetInputFocus after everything before was processed. 99 | self.get_input_focus()?.reply().and(Ok(())) 100 | } 101 | } 102 | impl ConnectionExt for C {} 103 | 104 | /// A RAII-like wrapper around [super::protocol::xproto::grab_server] and 105 | /// [super::protocol::xproto::ungrab_server]. 106 | /// 107 | /// Instances of this struct represent that we sent a [super::protocol::xproto::grab_server] 108 | /// request. When this struct is dropped, an [super::protocol::xproto::ungrab_server] request is 109 | /// sent. 110 | /// 111 | /// Any errors during `Drop` are silently ignored. Most likely an error here means that your 112 | /// X11 connection is broken and later requests will also fail. 113 | #[derive(Debug)] 114 | pub struct GrabServer<'c, C: XProtoConnectionExt>(&'c C); 115 | 116 | impl<'c, C: XProtoConnectionExt> GrabServer<'c, C> { 117 | /// Grab the server by sending a [super::protocol::xproto::grab_server] request. 118 | /// 119 | /// The returned type will call [super::protocol::xproto::ungrab_server] when it is dropped. 120 | pub fn grab(conn: &'c C) -> Result { 121 | // Grab the server, return any errors, ignore the resulting VoidCookie 122 | drop(conn.grab_server()?); 123 | Ok(Self(conn)) 124 | } 125 | } 126 | 127 | impl Drop for GrabServer<'_, C> { 128 | fn drop(&mut self) { 129 | let _ = (self.0).ungrab_server(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /x11rb/src/xcb_ffi/atomic_u64.rs: -------------------------------------------------------------------------------- 1 | //! Either `AtomicU64` or emulating `AtomicU64` through a `Mutex`. 2 | 3 | // Use the `AtomicU64` from the standard library if we're on a platform that supports atomic 4 | // 64-bit operations. 5 | #[cfg(target_has_atomic = "64")] 6 | pub(crate) use std::sync::atomic::AtomicU64; 7 | 8 | #[cfg(not(target_has_atomic = "64"))] 9 | mod impl_ { 10 | use std::sync::atomic::Ordering; 11 | use std::sync::Mutex; 12 | 13 | #[derive(Debug)] 14 | pub(crate) struct AtomicU64(Mutex); 15 | 16 | impl AtomicU64 { 17 | pub(crate) fn new(val: u64) -> Self { 18 | Self(Mutex::new(val)) 19 | } 20 | 21 | pub(crate) fn load(&self, _: Ordering) -> u64 { 22 | *self.0.lock().unwrap() 23 | } 24 | 25 | pub(crate) fn fetch_max(&self, val: u64, _: Ordering) -> u64 { 26 | let mut lock = self.0.lock().unwrap(); 27 | let old = *lock; 28 | *lock = old.max(val); 29 | old 30 | } 31 | } 32 | } 33 | 34 | #[cfg(not(target_has_atomic = "64"))] 35 | pub(crate) use self::impl_::AtomicU64; 36 | -------------------------------------------------------------------------------- /x11rb/src/xcb_ffi/pending_errors.rs: -------------------------------------------------------------------------------- 1 | //! Management of pending errors for the XCB connection. 2 | //! 3 | //! We add our own error tracking ontop of what XCB already provides. For this reason, this module 4 | //! contains a struct for tracking requests that were send and also tracking errors that were 5 | //! received, but not yet given to the user of this library. 6 | 7 | use std::cmp::Reverse; 8 | use std::collections::{BinaryHeap, VecDeque}; 9 | use std::sync::Mutex; 10 | 11 | use super::{Buffer, XCBConnection}; 12 | use x11rb_protocol::SequenceNumber; 13 | 14 | #[derive(Debug, Default)] 15 | struct PendingErrorsInner { 16 | in_flight: BinaryHeap>, 17 | pending: VecDeque<(SequenceNumber, Buffer)>, 18 | } 19 | 20 | /// A management struct for pending X11 errors 21 | #[derive(Debug, Default)] 22 | pub(crate) struct PendingErrors { 23 | inner: Mutex, 24 | } 25 | 26 | impl PendingErrors { 27 | pub(crate) fn append_error(&self, error: (SequenceNumber, Buffer)) { 28 | self.inner.lock().unwrap().pending.push_back(error) 29 | } 30 | 31 | pub(crate) fn discard_reply(&self, sequence: SequenceNumber) { 32 | self.inner.lock().unwrap().in_flight.push(Reverse(sequence)); 33 | } 34 | 35 | pub(crate) fn get(&self, conn: &XCBConnection) -> Option<(SequenceNumber, Buffer)> { 36 | let mut inner = self.inner.lock().unwrap(); 37 | 38 | // Check if we already have an element at hand 39 | let err = inner.pending.pop_front(); 40 | if err.is_some() { 41 | return err; 42 | } 43 | 44 | // Check if any of the still in-flight responses got a reply/error 45 | while let Some(&Reverse(seqno)) = inner.in_flight.peek() { 46 | let result = match conn.poll_for_reply(seqno) { 47 | Err(()) => { 48 | // This request was not answered/errored yet, so later request will not 49 | // have answers as well. 50 | return None; 51 | } 52 | Ok(reply) => reply, 53 | }; 54 | 55 | let seqno2 = inner.in_flight.pop(); 56 | assert_eq!(Some(Reverse(seqno)), seqno2); 57 | 58 | if let Some(result) = result { 59 | // Is this an error? 60 | if result[0] == 0 { 61 | return Some((seqno, result)); 62 | } else { 63 | // It's a reply, just ignore it 64 | } 65 | } 66 | } 67 | 68 | None 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /x11rb/src/xcb_ffi/raw_ffi/mod.rs: -------------------------------------------------------------------------------- 1 | //! The low-level foreign function interface to interact with libxcb. 2 | //! 3 | //! This module contains some `#[repr(C)]` type definitions that match libxcb's definitions. The 4 | //! actual functions are defined in the `ffi` submodule. There is also a `test` submodule that 5 | //! contains a mock of the interface that is used for unit tests. 6 | 7 | use std::ptr::NonNull; 8 | 9 | #[cfg(not(all(test, unix)))] 10 | use libc::c_void; 11 | #[cfg(unix)] 12 | pub(crate) use libc::iovec; 13 | use libc::{c_char, c_int, c_uint}; 14 | 15 | // As defined in xcb_windefs.h 16 | #[cfg(not(unix))] 17 | #[derive(Copy, Clone, Debug)] 18 | #[repr(C)] 19 | pub(crate) struct iovec { 20 | pub(crate) iov_base: *mut c_void, 21 | pub(crate) iov_len: c_int, 22 | } 23 | 24 | #[allow(non_camel_case_types)] 25 | #[repr(C)] 26 | pub(crate) struct xcb_connection_t { 27 | _unused: [u8; 0], 28 | } 29 | 30 | #[derive(Debug)] 31 | #[doc(hidden)] 32 | pub struct XcbConnectionWrapper { 33 | ptr: NonNull, 34 | should_drop: bool, 35 | } 36 | 37 | unsafe impl as_raw_xcb_connection::AsRawXcbConnection for XcbConnectionWrapper { 38 | fn as_raw_xcb_connection(&self) -> *mut as_raw_xcb_connection::xcb_connection_t { 39 | self.ptr.as_ptr().cast() 40 | } 41 | } 42 | 43 | // libxcb is fully thread-safe (well, except for xcb_disconnect()), so the following is 44 | // actually fine and safe: 45 | unsafe impl Send for XcbConnectionWrapper {} 46 | unsafe impl Sync for XcbConnectionWrapper {} 47 | 48 | impl Drop for XcbConnectionWrapper { 49 | fn drop(&mut self) { 50 | if self.should_drop { 51 | unsafe { 52 | xcb_disconnect(self.ptr.as_ptr()); 53 | } 54 | } 55 | } 56 | } 57 | 58 | impl XcbConnectionWrapper { 59 | pub(crate) unsafe fn new(ptr: *mut xcb_connection_t, should_drop: bool) -> Self { 60 | Self { 61 | ptr: NonNull::new_unchecked(ptr), 62 | should_drop, 63 | } 64 | } 65 | 66 | pub(crate) fn as_ptr(&self) -> *mut xcb_connection_t { 67 | self.ptr.as_ptr() 68 | } 69 | } 70 | 71 | #[allow(non_camel_case_types)] 72 | #[repr(C)] 73 | pub(crate) struct xcb_generic_event_t { 74 | pub(crate) response_type: u8, 75 | pub(crate) pad0: u8, 76 | pub(crate) sequence: u16, 77 | pub(crate) pad: [u32; 7], 78 | pub(crate) full_sequence: u32, 79 | } 80 | 81 | #[allow(non_camel_case_types)] 82 | #[repr(C)] 83 | pub(crate) struct xcb_generic_error_t { 84 | pub(crate) response_type: u8, 85 | pub(crate) error_code: u8, 86 | pub(crate) sequence: u16, 87 | pub(crate) resource_id: u32, 88 | pub(crate) minor_code: u16, 89 | pub(crate) major_code: u8, 90 | pub(crate) pad0: u8, 91 | pub(crate) pad: [u32; 5], 92 | pub(crate) full_sequence: u32, 93 | } 94 | 95 | #[derive(Clone, Copy)] 96 | #[allow(non_camel_case_types)] 97 | #[repr(C)] 98 | pub(crate) struct xcb_void_cookie_t { 99 | pub(crate) sequence: c_uint, 100 | } 101 | 102 | #[allow(non_camel_case_types)] 103 | #[repr(C)] 104 | pub(crate) struct xcb_extension_t { 105 | pub(crate) name: *const c_char, 106 | pub(crate) global_id: c_int, 107 | } 108 | 109 | #[allow(non_camel_case_types)] 110 | #[repr(C)] 111 | pub(crate) struct xcb_protocol_request_t { 112 | pub(crate) count: usize, 113 | pub(crate) ext: *mut xcb_extension_t, 114 | pub(crate) opcode: u8, 115 | pub(crate) isvoid: u8, 116 | } 117 | 118 | #[allow(non_camel_case_types)] 119 | #[repr(C)] 120 | pub(crate) struct xcb_setup_t { 121 | _unused: [u8; 0], 122 | } 123 | 124 | pub(crate) mod connection_errors { 125 | use std::os::raw::c_int; 126 | 127 | pub(crate) const ERROR: c_int = 1; 128 | pub(crate) const EXT_NOTSUPPORTED: c_int = 2; 129 | pub(crate) const MEM_INSUFFICIENT: c_int = 3; 130 | pub(crate) const REQ_LEN_EXCEED: c_int = 4; 131 | pub(crate) const PARSE_ERR: c_int = 5; 132 | pub(crate) const INVALID_SCREEN: c_int = 6; 133 | pub(crate) const FDPASSING_FAILED: c_int = 7; 134 | } 135 | 136 | pub(crate) mod send_request_flags { 137 | use libc::c_int; 138 | 139 | pub(crate) const CHECKED: c_int = 1; 140 | pub(crate) const RAW: c_int = 2; 141 | //pub(crate) const DISCARD_REPLY: c_int = 4; 142 | pub(crate) const REPLY_FDS: c_int = 8; 143 | } 144 | 145 | #[cfg(not(test))] 146 | mod ffi; 147 | 148 | #[cfg(not(test))] 149 | pub(crate) use ffi::*; 150 | 151 | #[cfg(test)] 152 | mod test; 153 | 154 | #[cfg(test)] 155 | pub(crate) use test::*; 156 | -------------------------------------------------------------------------------- /x11rb/src/xcb_ffi/raw_ffi/test.rs: -------------------------------------------------------------------------------- 1 | //! A mock of libxcb that is used by the unit tests in [`x11rb::xcb_ffi`]. 2 | 3 | use std::ffi::CStr; 4 | 5 | use libc::{c_char, c_int, c_uint, c_void}; 6 | 7 | use super::{ 8 | iovec, xcb_connection_t, xcb_generic_error_t, xcb_generic_event_t, xcb_protocol_request_t, 9 | xcb_setup_t, xcb_void_cookie_t, 10 | }; 11 | use crate::protocol::xproto::{ImageOrder, Setup}; 12 | use crate::x11_utils::Serialize; 13 | 14 | #[repr(C)] 15 | struct ConnectionMock { 16 | error: c_int, 17 | setup: Vec, 18 | } 19 | 20 | // From xcb.h 21 | pub(crate) unsafe fn xcb_flush(_c: *mut xcb_connection_t) -> c_int { 22 | unimplemented!(); 23 | } 24 | 25 | pub(crate) unsafe fn xcb_get_maximum_request_length(_c: *mut xcb_connection_t) -> u32 { 26 | unimplemented!(); 27 | } 28 | 29 | pub(crate) unsafe fn xcb_prefetch_maximum_request_length(_c: *mut xcb_connection_t) { 30 | unimplemented!(); 31 | } 32 | 33 | pub(crate) unsafe fn xcb_wait_for_event(_c: *mut xcb_connection_t) -> *mut xcb_generic_event_t { 34 | unimplemented!(); 35 | } 36 | 37 | pub(crate) unsafe fn xcb_poll_for_event(_c: *mut xcb_connection_t) -> *mut xcb_generic_event_t { 38 | unimplemented!(); 39 | } 40 | 41 | pub(crate) unsafe fn xcb_request_check( 42 | _c: *mut xcb_connection_t, 43 | _void_cookie: xcb_void_cookie_t, 44 | ) -> *mut xcb_generic_error_t { 45 | unimplemented!(); 46 | } 47 | 48 | pub(crate) unsafe fn xcb_discard_reply64(_c: *mut xcb_connection_t, _sequence: u64) { 49 | unimplemented!(); 50 | } 51 | 52 | pub(crate) unsafe fn xcb_get_setup(c: *mut xcb_connection_t) -> *const xcb_setup_t { 53 | // The pointer is suitable aligned since our xcb_connect() mock above created it 54 | #[allow(clippy::cast_ptr_alignment)] 55 | ((*(c as *const ConnectionMock)).setup.as_ptr() as _) 56 | } 57 | 58 | #[cfg(unix)] 59 | pub(crate) unsafe fn xcb_get_file_descriptor(_c: *mut xcb_connection_t) -> c_int { 60 | unimplemented!(); 61 | } 62 | 63 | pub(crate) unsafe fn xcb_connection_has_error(c: *mut xcb_connection_t) -> c_int { 64 | // The pointer is suitable aligned since our xcb_connect() mock above created it 65 | #[allow(clippy::cast_ptr_alignment)] 66 | (*(c as *const ConnectionMock)).error 67 | } 68 | 69 | pub(crate) unsafe fn xcb_disconnect(c: *mut xcb_connection_t) { 70 | // The pointer is suitable aligned since our xcb_connect() mock above created it 71 | #[allow(clippy::cast_ptr_alignment)] 72 | let _ = Box::from_raw(c as *mut ConnectionMock); 73 | } 74 | 75 | pub(crate) unsafe fn xcb_connect( 76 | displayname: *const c_char, 77 | screenp: *mut c_int, 78 | ) -> *mut xcb_connection_t { 79 | // Test that the provided displayname is correct 80 | assert_eq!( 81 | CStr::from_ptr(displayname).to_str().unwrap(), 82 | "display name", 83 | "Did not get the expected displayname", 84 | ); 85 | std::ptr::write(screenp, 0); 86 | 87 | let length_field = 10; 88 | let setup = Setup { 89 | status: 0, 90 | protocol_major_version: 0, 91 | protocol_minor_version: 0, 92 | length: length_field, 93 | release_number: 0, 94 | resource_id_base: 0, 95 | resource_id_mask: 0, 96 | motion_buffer_size: 0, 97 | maximum_request_length: 0, 98 | image_byte_order: ImageOrder::LSB_FIRST, 99 | bitmap_format_bit_order: ImageOrder::LSB_FIRST, 100 | bitmap_format_scanline_unit: 0, 101 | bitmap_format_scanline_pad: 0, 102 | min_keycode: 0, 103 | max_keycode: 0, 104 | vendor: Default::default(), 105 | pixmap_formats: Default::default(), 106 | roots: Default::default(), 107 | }; 108 | let setup = setup.serialize(); 109 | assert_eq!(setup.len(), 4 * length_field as usize); 110 | 111 | let mock = ConnectionMock { error: 0, setup }; 112 | Box::into_raw(Box::new(mock)) as _ 113 | } 114 | 115 | pub(crate) unsafe fn xcb_generate_id(_c: *mut xcb_connection_t) -> u32 { 116 | unimplemented!(); 117 | } 118 | 119 | // From xcbext.h 120 | pub(crate) unsafe fn xcb_send_request64( 121 | _c: *mut xcb_connection_t, 122 | _flags: c_int, 123 | _vector: *mut iovec, 124 | _request: *const xcb_protocol_request_t, 125 | ) -> u64 { 126 | unimplemented!(); 127 | } 128 | 129 | #[cfg(unix)] 130 | pub(crate) unsafe fn xcb_send_request_with_fds64( 131 | _c: *mut xcb_connection_t, 132 | _flags: c_int, 133 | _vector: *mut iovec, 134 | _request: *const xcb_protocol_request_t, 135 | _num_fds: c_uint, 136 | _fds: *mut c_int, 137 | ) -> u64 { 138 | unimplemented!(); 139 | } 140 | 141 | pub(crate) unsafe fn xcb_wait_for_reply64( 142 | _c: *mut xcb_connection_t, 143 | _request: u64, 144 | _e: *mut *mut xcb_generic_error_t, 145 | ) -> *mut c_void { 146 | unimplemented!(); 147 | } 148 | 149 | pub(crate) unsafe fn xcb_poll_for_reply64( 150 | _c: *mut xcb_connection_t, 151 | _request: u64, 152 | _reply: *mut *mut c_void, 153 | _error: *mut *mut xcb_generic_error_t, 154 | ) -> c_int { 155 | unimplemented!(); 156 | } 157 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/.gitignore: -------------------------------------------------------------------------------- 1 | aclocal.m4 2 | autom4te.cache 3 | compile 4 | depcomp 5 | install-sh 6 | libtool 7 | ltmain.sh 8 | missing 9 | mkinstalldirs 10 | py-compile 11 | config.guess 12 | config.h 13 | config.h.in 14 | config.log 15 | config.status 16 | config.sub 17 | configure 18 | configure.lineno 19 | .deps 20 | .dirstamp 21 | .libs 22 | *.lo 23 | *.loT 24 | *.la 25 | Makefile 26 | Makefile.in 27 | stamp-h1 28 | *.o 29 | *.pc 30 | *.tar.bz2 31 | *.tar.gz 32 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # vim: set expandtab shiftwidth=2 tabstop=8 textwidth=0 filetype=yaml: 2 | # 3 | # This CI uses the freedesktop.org ci-templates. 4 | # Please see the ci-templates documentation for details: 5 | # https://freedesktop.pages.freedesktop.org/ci-templates/ 6 | 7 | .templates_sha: &template_sha c5626190ec14b475271288dda7a7dae8dbe0cd76 # see https://docs.gitlab.com/ee/ci/yaml/#includefile 8 | 9 | 10 | include: 11 | # Arch container builder template 12 | - project: 'freedesktop/ci-templates' 13 | ref: *template_sha 14 | file: '/templates/arch.yml' 15 | - project: 'freedesktop/ci-templates' 16 | ref: *template_sha 17 | file: '/templates/ci-fairy.yml' 18 | - template: Security/SAST.gitlab-ci.yml 19 | 20 | 21 | stages: 22 | - prep # prep work like rebuilding the container images if there is a change 23 | - build # for actually building and testing things in a container 24 | - test 25 | - deploy 26 | 27 | 28 | variables: 29 | FDO_UPSTREAM_REPO: 'xorg/proto/xcbproto' 30 | # The tag should be updated each time the list of packages is updated. 31 | # Changing a tag forces the associated image to be rebuilt. 32 | # Note: the tag has no meaning, we use a date format purely for readability 33 | FDO_DISTRIBUTION_TAG: '2022-07-23.0' 34 | # Packages needed to build xcbproto 35 | XCBPROTO_PACKAGES: 'git gcc pkgconf autoconf automake make libxml2 python' 36 | # Additional packages needed to build libxcb 37 | LIBXCB_PACKAGES: 'libtool xorg-util-macros doxygen graphviz check libxslt libxau libxdmcp' 38 | FDO_DISTRIBUTION_PACKAGES: $XCBPROTO_PACKAGES $LIBXCB_PACKAGES 39 | 40 | # 41 | # Verify that commit messages are as expected 42 | # 43 | check-commits: 44 | extends: 45 | - .fdo.ci-fairy 46 | stage: prep 47 | script: 48 | - ci-fairy check-commits --junit-xml=results.xml 49 | except: 50 | - master@xorg/proto/xcbproto 51 | variables: 52 | GIT_DEPTH: 100 53 | artifacts: 54 | reports: 55 | junit: results.xml 56 | allow_failure: true 57 | 58 | # 59 | # Verify that the merge request has the allow-collaboration checkbox ticked 60 | # 61 | check-merge-request: 62 | extends: 63 | - .fdo.ci-fairy 64 | stage: deploy 65 | script: 66 | - ci-fairy check-merge-request --require-allow-collaboration --junit-xml=results.xml 67 | artifacts: 68 | when: on_failure 69 | reports: 70 | junit: results.xml 71 | allow_failure: true 72 | 73 | 74 | # 75 | # Build a container with the given tag and the packages pre-installed. 76 | # This only happens if/when the tag changes, otherwise the existing image is 77 | # re-used. 78 | # 79 | container-prep: 80 | extends: 81 | - .fdo.container-build@arch 82 | stage: prep 83 | variables: 84 | GIT_STRATEGY: none 85 | 86 | 87 | # 88 | # The default build, runs on the image built above. 89 | # 90 | build: 91 | stage: build 92 | extends: 93 | - .fdo.distribution-image@arch 94 | script: 95 | - export INSTDIR="$PWD/_inst" 96 | - mkdir _builddir 97 | - pushd _builddir > /dev/null 98 | - ../autogen.sh --disable-silent-rules --prefix="$INSTDIR" 99 | - make 100 | - make check 101 | - make install 102 | - make distcheck 103 | - popd > /dev/null 104 | variables: 105 | artifacts: 106 | paths: 107 | - _inst 108 | 109 | # 110 | # Build libxcb with the new xcbproto, runs on the image built above. 111 | # 112 | build-libxcb: 113 | stage: test 114 | extends: 115 | - .fdo.distribution-image@arch 116 | script: 117 | - export INSTDIR="$PWD/_inst" 118 | - export PKG_CONFIG_PATH=$(find $INSTDIR/ -name '*.pc' -printf "%h:") 119 | - git clone --depth=1 https://gitlab.freedesktop.org/xorg/lib/libxcb 120 | - pushd libxcb > /dev/null 121 | - mkdir _builddir 122 | - pushd _builddir > /dev/null 123 | - ../autogen.sh --disable-silent-rules --enable-devel-docs --with-doxygen 124 | - make 125 | - make check 126 | - make install 127 | - make distcheck 128 | - popd > /dev/null 129 | - popd > /dev/null 130 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2001-2006 Bart Massey, Jamey Sharp, and Josh Triplett. 2 | All Rights Reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the 7 | Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, 9 | sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall 14 | be included in all copies or substantial portions of the 15 | Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 18 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 19 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 20 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 21 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | Except as contained in this notice, the names of the authors 27 | or their institutions shall not be used in advertising or 28 | otherwise to promote the sale, use or other dealings in this 29 | Software without prior written authorization from the 30 | authors. 31 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/HACKING: -------------------------------------------------------------------------------- 1 | Make patches more descriptive 2 | ============================= 3 | 4 | By default hunk headers of a patch will look like this: 5 | 6 | @@ -1227,6 +1227,8 @@ authorization from the authors. 7 | 8 | Which is not very usefull as it doesn't give a hint where the change 9 | happened (except for the line number). To make those hunk headers more 10 | descriptive, i.e.: 11 | 12 | @@ -1227,6 +1227,8 @@ 13 | 14 | Add these 2 lines to your .git/config file: 15 | 16 | [diff "xcb"] 17 | xfuncname = "(<\\b(xcb|enum|event|request|struct|union)\\b.*>)" 18 | 19 | For more background on this magic have a look at src/.gitattributes and 20 | the man page gitattributes(5) chapter "Defining a custom hunk-header". 21 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src xcbgen 2 | 3 | pkgconfigdir = $(datarootdir)/pkgconfig 4 | pkgconfig_DATA = xcb-proto.pc 5 | 6 | EXTRA_DIST=doc xcb-proto.pc.in autogen.sh README.md 7 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/README.md: -------------------------------------------------------------------------------- 1 | About xcb-proto 2 | =============== 3 | 4 | xcb-proto provides the XML-XCB protocol descriptions that libxcb uses to 5 | generate the majority of its code and API. We provide them separately 6 | from libxcb to allow reuse by other projects, such as additional 7 | language bindings, protocol dissectors, or documentation generators. 8 | 9 | This separation between the XCB transport layer and the 10 | automatically-generated protocol layer also makes it far easier to write 11 | new extensions. With the Xlib infrastructure, client-side support for 12 | new extensions requires significant duplication of effort. With XCB and 13 | the XML-XCB protocol descriptions, client-side support for a new 14 | extension requires only an XML description of the extension, and not a 15 | single line of code. 16 | 17 | Python libraries: xcb-proto also contains language-independent Python 18 | libraries that are used to parse an XML description and create objects 19 | used by Python code generators in individual language bindings. These 20 | libraries are installed into $(prefix)/lib/pythonX.X/site-packages. If 21 | this location is not on your system's Python path, scripts that import 22 | them will fail with import errors. In this case you must add the 23 | install location to your Python path by creating a file with a `.pth' 24 | extension in a directory that _is_ on the Python path, and put the 25 | path to the install location in that file. For example, on my system 26 | there is a file named 'local.pth' in /usr/lib/python2.5/site-packages, 27 | which contains '/usr/local/lib/python2.5/site-packages'. Note that 28 | this is only necessary on machines where XCB is being built. 29 | 30 | Please report any issues you find to the freedesktop.org bug tracker at: 31 | 32 | https://gitlab.freedesktop.org/xorg/proto/xcbproto/issues 33 | 34 | Discussion about XCB occurs on the XCB mailing list: 35 | 36 | https://lists.freedesktop.org/mailman/listinfo/xcb 37 | 38 | You can obtain the latest development versions of xcb-proto using GIT from 39 | the xcbproto code repository at: 40 | 41 | https://gitlab.freedesktop.org/xorg/proto/xcbproto 42 | 43 | For anonymous checkouts, use: 44 | 45 | git clone https://gitlab.freedesktop.org/xorg/proto/xcbproto.git 46 | 47 | For developers, use: 48 | 49 | git clone git@gitlab.freedesktop.org:xorg/proto/xcbproto.git 50 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/TODO: -------------------------------------------------------------------------------- 1 | Last Updated: 2006-04-27 2 | 3 | Extension Status: 4 | 5 | X - Present, tested, works. 6 | U - Present, untested. 7 | I - Incomplete. 8 | 9 | X BIG-REQUESTS 10 | X COMPOSITE 11 | X DAMAGE 12 | DOUBLE-BUFFER 13 | X DPMS 14 | Extended-Visual-Information 15 | X GLX 16 | LBX 17 | X MIT-SCREEN-SAVER 18 | X MIT-SHM 19 | MIT-SUNDRY-NONSTANDARD 20 | Multi-Buffering 21 | X RANDR 22 | X RECORD 23 | X RENDER 24 | SECURITY 25 | SGI-GLX 26 | X SHAPE 27 | X SYNC 28 | TOG-CUP 29 | X X-Resource 30 | XC-APPGROUP 31 | X XC-MISC 32 | X XEVIE 33 | X XFIXES 34 | XFree86-Bigfont 35 | XFree86-DGA 36 | X XFree86-DRI 37 | XFree86-Misc 38 | XFree86-VidModeExtension 39 | XINERAMA 40 | XInputExtension 41 | XKEYBOARD 42 | I XpExtension (XPRINT) 43 | X XTEST 44 | X XVideo 45 | U XVideo-MotionCompensation 46 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | srcdir=`dirname "$0"` 4 | test -z "$srcdir" && srcdir=. 5 | 6 | ORIGDIR=`pwd` 7 | cd "$srcdir" 8 | 9 | autoreconf -v --install || exit 1 10 | cd "$ORIGDIR" || exit $? 11 | 12 | git config --local --get format.subjectPrefix >/dev/null 2>&1 || 13 | git config --local format.subjectPrefix "PATCH xcbproto" 14 | 15 | if test -z "$NOCONFIGURE"; then 16 | exec "$srcdir"/configure "$@" 17 | fi 18 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.57) 5 | AC_INIT([XCB Proto], 6 | 1.17.0, 7 | [xcb@lists.freedesktop.org]) 8 | AC_CONFIG_SRCDIR([xcb-proto.pc.in]) 9 | AM_INIT_AUTOMAKE([foreign dist-xz]) 10 | 11 | AC_PATH_PROG(XMLLINT, xmllint, no) 12 | AM_CONDITIONAL(HAVE_XMLLINT, test "x$XMLLINT" != "xno") 13 | if test "$XMLLINT" = "no"; then 14 | AC_MSG_WARN([xmllint not found; unable to validate against schema.]) 15 | fi 16 | 17 | AM_PATH_PYTHON([2.5]) 18 | 19 | xcbincludedir='${datadir}/xcb' 20 | AC_SUBST(xcbincludedir) 21 | 22 | AC_CONFIG_FILES([Makefile src/Makefile xcbgen/Makefile xcb-proto.pc]) 23 | AC_OUTPUT 24 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/src/.gitattributes: -------------------------------------------------------------------------------- 1 | *.xml diff=xcb 2 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/src/Makefile.am: -------------------------------------------------------------------------------- 1 | xcbinclude_HEADERS = \ 2 | xcb.xsd \ 3 | xproto.xml \ 4 | bigreq.xml \ 5 | composite.xml \ 6 | damage.xml \ 7 | dbe.xml \ 8 | dpms.xml \ 9 | dri2.xml \ 10 | dri3.xml \ 11 | ge.xml \ 12 | glx.xml \ 13 | present.xml \ 14 | randr.xml \ 15 | record.xml \ 16 | render.xml \ 17 | res.xml \ 18 | screensaver.xml \ 19 | shape.xml \ 20 | shm.xml \ 21 | sync.xml \ 22 | xc_misc.xml \ 23 | xevie.xml \ 24 | xf86dri.xml \ 25 | xf86vidmode.xml \ 26 | xfixes.xml \ 27 | xinerama.xml \ 28 | xinput.xml \ 29 | xkb.xml \ 30 | xprint.xml \ 31 | xselinux.xml \ 32 | xtest.xml \ 33 | xv.xml \ 34 | xvmc.xml 35 | 36 | if HAVE_XMLLINT 37 | check-local: 38 | $(XMLLINT) --noout --schema $(srcdir)/xcb.xsd $(srcdir)/*.xml 39 | endif 40 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/src/bigreq.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | The maximum length of requests supported by the server, in 4-byte units. 39 | 40 | 41 | 42 | 43 | Enable the BIG-REQUESTS extension 44 | 45 | This enables the BIG-REQUESTS extension, which allows for requests larger than 46 | 262140 bytes in length. When enabled, if the 16-bit length field is zero, it 47 | is immediately followed by a 32-bit length field specifying the length of the 48 | request in 4-byte units. 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/src/dpms.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 30 | xproto 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 0 71 | 1 72 | 2 73 | 3 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 0 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/src/ge.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/src/xc_misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ids_len 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/src/xevie.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 0 59 | 1 60 | 61 | 62 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/src/xinerama.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 31 | 32 | 33 | 34 | xproto 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | number 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/src/xtest.xml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 31 | 32 | xproto 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 0 47 | 1 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/xcb-proto.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | datarootdir=@datarootdir@ 4 | datadir=@datadir@ 5 | xcbincludedir=${pc_sysrootdir}@xcbincludedir@ 6 | PYTHON_PREFIX=@PYTHON_PREFIX@ 7 | pythondir=${pc_sysrootdir}@pythondir@ 8 | 9 | Name: XCB Proto 10 | Description: X protocol descriptions for XCB 11 | Version: @PACKAGE_VERSION@ 12 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/xcbgen/Makefile.am: -------------------------------------------------------------------------------- 1 | pkgpythondir = $(pythondir)/xcbgen 2 | 3 | pkgpython_PYTHON = __init__.py error.py expr.py align.py matcher.py state.py xtypes.py 4 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/xcbgen/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/xcbgen/error.py: -------------------------------------------------------------------------------- 1 | class ResolveException(Exception): 2 | ''' 3 | Gets thrown when a type doesn't resolve in the XML. 4 | ''' 5 | pass 6 | -------------------------------------------------------------------------------- /xcb-proto-1.17.0/xcbgen/matcher.py: -------------------------------------------------------------------------------- 1 | ''' 2 | XML parser. One function for each top-level element in the schema. 3 | 4 | Most functions just declare a new object and add it to the module. 5 | For typedefs, eventcopies, xidtypes, and other aliases though, 6 | we do not create a new type object, we just record the existing one under a new name. 7 | ''' 8 | 9 | from os.path import join 10 | from sys import version_info 11 | 12 | if version_info[:2] >= (3, 3): 13 | from xml.etree.ElementTree import parse 14 | else: 15 | from xml.etree.cElementTree import parse 16 | 17 | from xcbgen.xtypes import * 18 | 19 | def import_(node, module, namespace): 20 | ''' 21 | For imports, we load the file, create a new namespace object, 22 | execute recursively, then record the import (for header files, etc.) 23 | ''' 24 | # To avoid circular import error 25 | from xcbgen import state 26 | module.import_level = module.import_level + 1 27 | new_file = join(namespace.dir, '%s.xml' % node.text) 28 | new_root = parse(new_file).getroot() 29 | new_namespace = state.Namespace(new_file) 30 | execute(module, new_namespace) 31 | module.import_level = module.import_level - 1 32 | if not module.has_import(node.text): 33 | module.add_import(node.text, new_namespace) 34 | 35 | def typedef(node, module, namespace): 36 | id = node.get('newname') 37 | name = namespace.prefix + (id,) 38 | type = module.get_type(node.get('oldname')) 39 | module.add_type(id, namespace.ns, name, type) 40 | 41 | def xidtype(node, module, namespace): 42 | id = node.get('name') 43 | name = namespace.prefix + (id,) 44 | type = module.get_type('CARD32') 45 | module.add_type(id, namespace.ns, name, type) 46 | 47 | def xidunion(node, module, namespace): 48 | id = node.get('name') 49 | name = namespace.prefix + (id,) 50 | type = module.get_type('CARD32') 51 | module.add_type(id, namespace.ns, name, type) 52 | 53 | def enum(node, module, namespace): 54 | id = node.get('name') 55 | name = namespace.prefix + (id,) 56 | type = Enum(name, node) 57 | module.add_type(id, namespace.ns, name, type) 58 | 59 | def struct(node, module, namespace): 60 | id = node.get('name') 61 | name = namespace.prefix + (id,) 62 | type = Struct(name, node) 63 | module.add_type(id, namespace.ns, name, type) 64 | 65 | def eventstruct(node, module, namespace): 66 | id = node.get('name') 67 | name = namespace.prefix + (id,) 68 | type = EventStruct(name, node) 69 | module.add_type(id, namespace.ns, name, type) 70 | 71 | def union(node, module, namespace): 72 | id = node.get('name') 73 | name = namespace.prefix + (id,) 74 | type = Union(name, node) 75 | module.add_type(id, namespace.ns, name, type) 76 | 77 | def request(node, module, namespace): 78 | id = node.get('name') 79 | name = namespace.prefix + (id,) 80 | type = Request(name, node) 81 | module.add_request(id, name, type) 82 | 83 | def event(node, module, namespace): 84 | id = node.get('name') 85 | name = namespace.prefix + (id,) 86 | event = Event(name, node) 87 | event.add_opcode(node.get('number'), name, True) 88 | module.add_event(id, name, event) 89 | 90 | def eventcopy(node, module, namespace): 91 | id = node.get('name') 92 | name = namespace.prefix + (id,) 93 | event = module.get_event(node.get('ref')) 94 | event.add_opcode(node.get('number'), name, False) 95 | module.add_event(id, name, event) 96 | 97 | def error(node, module, namespace): 98 | id = node.get('name') 99 | name = namespace.prefix + (id,) 100 | error = Error(name, node) 101 | error.add_opcode(node.get('number'), name, True) 102 | module.add_error(id, name, error) 103 | 104 | def errorcopy(node, module, namespace): 105 | id = node.get('name') 106 | name = namespace.prefix + (id,) 107 | error = module.get_error(node.get('ref')) 108 | error.add_opcode(node.get('number'), name, False) 109 | module.add_error(id, name, error) 110 | 111 | funcs = {'import' : import_, 112 | 'typedef' : typedef, 113 | 'xidtype' : xidtype, 114 | 'xidunion' : xidunion, 115 | 'enum' : enum, 116 | 'struct' : struct, 117 | 'eventstruct' : eventstruct, 118 | 'union' : union, 119 | 'request' : request, 120 | 'event' : event, 121 | 'eventcopy' : eventcopy, 122 | 'error' : error, 123 | 'errorcopy' : errorcopy} 124 | 125 | def execute(module, namespace): 126 | for elt in list(namespace.root): 127 | funcs[elt.tag](elt, module, namespace) 128 | -------------------------------------------------------------------------------- /xcbgen-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xcbgen" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | once_cell = "1.19.0" 11 | roxmltree = "0.19.0" 12 | -------------------------------------------------------------------------------- /xcbgen-rs/src/defs/doc.rs: -------------------------------------------------------------------------------- 1 | /// Documentation of some type. 2 | #[derive(Clone, Debug)] 3 | pub struct Doc { 4 | /// A brief, one-line description. 5 | pub brief: Option, 6 | 7 | /// A verbose description. 8 | pub description: Option, 9 | 10 | /// A usage example. This will most likely assume libxcb's C API. 11 | pub example: Option, 12 | 13 | /// Documentation of the individual fields. 14 | pub fields: Vec, 15 | 16 | /// The possible X11 errors that can occur. 17 | pub errors: Vec, 18 | 19 | /// A reference to a related type. 20 | pub sees: Vec, 21 | } 22 | 23 | /// Documentation for a single field. 24 | #[derive(Clone, Debug)] 25 | pub struct FieldDoc { 26 | /// The name of the field. 27 | pub name: String, 28 | 29 | /// A description of this field. 30 | pub doc: Option, 31 | } 32 | 33 | /// Documentation of a X11 error that can occur. 34 | #[derive(Clone, Debug)] 35 | pub struct ErrorDoc { 36 | /// The name of the error. 37 | pub type_: String, 38 | 39 | /// A description of when this error occurs. 40 | pub doc: Option, 41 | } 42 | 43 | /// A reference to another type for further information. 44 | #[derive(Clone, Debug)] 45 | pub struct SeeDoc { 46 | /// The kind of type that is referenced. 47 | // TODO: Turn this into an enum? 48 | pub type_: String, 49 | 50 | /// The name of the thing that is referenced, for example the name of a request. 51 | pub name: String, 52 | } 53 | -------------------------------------------------------------------------------- /xcbgen-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Structures for working with the xcb-proto XML descriptions. 2 | //! 3 | //! xcb-proto contains a machine readable description of the X11 protocol. This library contains 4 | //! structures to read this XML description and to work with it. Basically, this is a Rust version 5 | //! of xcb-proto's `xcbgen`. 6 | 7 | #![deny( 8 | rust_2018_idioms, 9 | missing_docs, 10 | trivial_casts, 11 | trivial_numeric_casts, 12 | unsafe_code, 13 | unreachable_pub, 14 | unused, 15 | unused_qualifications, 16 | missing_copy_implementations, 17 | missing_debug_implementations, 18 | rustdoc::private_doc_tests, 19 | single_use_lifetimes, 20 | clippy::cast_lossless, 21 | clippy::needless_pass_by_value 22 | )] 23 | #![forbid(unsafe_code)] 24 | 25 | pub mod defs; 26 | mod parser; 27 | mod resolver; 28 | 29 | pub use parser::{ParseError, Parser}; 30 | pub use resolver::{resolve, ResolveError}; 31 | -------------------------------------------------------------------------------- /xcbgen-rs/src/resolver/nesting_checker.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use std::rc::Rc; 3 | 4 | use crate::{defs, ResolveError}; 5 | 6 | /// Check that there is no infinite struct nesting, i.e. no structs containing themselves (even 7 | /// indirectly). 8 | #[inline] 9 | pub(super) fn check(module: &defs::Module) -> Result<(), ResolveError> { 10 | NestingChecker::new().check_module(module) 11 | } 12 | 13 | struct NestingChecker { 14 | stack: Vec, 15 | /// Pointers converted to `usize`. 16 | checked: HashSet, 17 | } 18 | 19 | impl NestingChecker { 20 | fn new() -> Self { 21 | Self { 22 | stack: Vec::new(), 23 | checked: HashSet::default(), 24 | } 25 | } 26 | 27 | fn push(&mut self, item: NestingStackItem) -> Result<(), ResolveError> { 28 | if self 29 | .stack 30 | .iter() 31 | .any(|stack_item| stack_item.same_as(&item)) 32 | { 33 | return Err(ResolveError::InfiniteStructNesting); 34 | } 35 | self.stack.push(item); 36 | Ok(()) 37 | } 38 | 39 | fn pop(&mut self) { 40 | self.stack.pop(); 41 | } 42 | 43 | fn check_module(&mut self, module: &defs::Module) -> Result<(), ResolveError> { 44 | for ns in module.namespaces.borrow().values() { 45 | for type_def in ns.type_defs.borrow().values() { 46 | self.check_type_def(type_def)?; 47 | } 48 | } 49 | Ok(()) 50 | } 51 | 52 | fn check_type_def(&mut self, type_def: &defs::TypeDef) -> Result<(), ResolveError> { 53 | match type_def { 54 | defs::TypeDef::Struct(struct_def) => self.check_struct(struct_def), 55 | defs::TypeDef::Union(union_def) => self.check_union(union_def), 56 | _ => Ok(()), 57 | } 58 | } 59 | 60 | fn check_struct(&mut self, struct_def: &Rc) -> Result<(), ResolveError> { 61 | self.push(NestingStackItem::Struct(struct_def.clone()))?; 62 | let struct_def_ptr: *const defs::StructDef = &**struct_def; 63 | if self.checked.insert(struct_def_ptr as usize) { 64 | // Not checked yet 65 | for field in struct_def.fields.borrow().iter() { 66 | self.check_field(field)?; 67 | } 68 | } 69 | self.pop(); 70 | Ok(()) 71 | } 72 | 73 | fn check_union(&mut self, union_def: &Rc) -> Result<(), ResolveError> { 74 | self.push(NestingStackItem::Union(union_def.clone()))?; 75 | let union_def_ptr: *const defs::UnionDef = &**union_def; 76 | if self.checked.insert(union_def_ptr as usize) { 77 | // Not checked yet 78 | for field in union_def.fields.iter() { 79 | self.check_field(field)?; 80 | } 81 | } 82 | self.pop(); 83 | Ok(()) 84 | } 85 | 86 | fn check_field(&mut self, field: &defs::FieldDef) -> Result<(), ResolveError> { 87 | match field { 88 | defs::FieldDef::Pad(_) => Ok(()), 89 | defs::FieldDef::Normal(normal_field) => { 90 | self.check_field_value_type(&normal_field.type_)?; 91 | Ok(()) 92 | } 93 | defs::FieldDef::List(list_field) => { 94 | self.check_field_value_type(&list_field.element_type)?; 95 | Ok(()) 96 | } 97 | defs::FieldDef::Switch(switch_field) => { 98 | for case in switch_field.cases.iter() { 99 | for case_field in case.fields.borrow().iter() { 100 | self.check_field(case_field)?; 101 | } 102 | } 103 | Ok(()) 104 | } 105 | defs::FieldDef::Fd(_) => Ok(()), 106 | defs::FieldDef::FdList(_) => Ok(()), 107 | defs::FieldDef::Expr(_) => Ok(()), 108 | defs::FieldDef::VirtualLen(_) => Ok(()), 109 | } 110 | } 111 | 112 | fn check_field_value_type(&mut self, type_: &defs::FieldValueType) -> Result<(), ResolveError> { 113 | self.check_type_ref(type_.type_.get_resolved()) 114 | } 115 | 116 | fn check_type_ref(&mut self, type_: &defs::TypeRef) -> Result<(), ResolveError> { 117 | match type_ { 118 | defs::TypeRef::Struct(struct_def) => self.check_struct(&struct_def.upgrade().unwrap()), 119 | defs::TypeRef::Union(union_def) => self.check_union(&union_def.upgrade().unwrap()), 120 | defs::TypeRef::Alias(type_alias_def) => { 121 | let type_alias_def = type_alias_def.upgrade().unwrap(); 122 | self.check_type_ref(type_alias_def.old_name.get_resolved()) 123 | } 124 | _ => Ok(()), 125 | } 126 | } 127 | } 128 | 129 | enum NestingStackItem { 130 | Struct(Rc), 131 | Union(Rc), 132 | } 133 | 134 | impl NestingStackItem { 135 | fn same_as(&self, other: &Self) -> bool { 136 | match (self, other) { 137 | (Self::Struct(this), Self::Struct(other)) => Rc::ptr_eq(this, other), 138 | (Self::Union(this), Self::Union(other)) => Rc::ptr_eq(this, other), 139 | _ => false, 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /xcbgen-rs/src/resolver/param_ref_gatherer.rs: -------------------------------------------------------------------------------- 1 | use crate::{defs, ResolveError}; 2 | 3 | /// Gather elements to param references in the module. 4 | pub(super) fn gather(module: &defs::Module) -> Result<(), ResolveError> { 5 | for ns in module.namespaces.borrow().values() { 6 | for type_def in ns.type_defs.borrow().values() { 7 | if let defs::TypeDef::Struct(struct_def) = type_def { 8 | StructParamRefGatherer::gather_param_refs_in_struct(struct_def)?; 9 | } 10 | } 11 | } 12 | Ok(()) 13 | } 14 | 15 | struct StructParamRefGatherer { 16 | external_params: Vec, 17 | } 18 | 19 | impl StructParamRefGatherer { 20 | fn add_external_param(&mut self, name: &str, type_: defs::TypeRef) -> Result<(), ResolveError> { 21 | for ext_param in self.external_params.iter() { 22 | if ext_param.name == name { 23 | if !ext_param.type_.same_as(&type_) { 24 | return Err(ResolveError::DiscrepantParamRefTypes(name.into())); 25 | } else { 26 | return Ok(()); 27 | } 28 | } 29 | } 30 | self.external_params.push(defs::ExternalParam { 31 | name: name.into(), 32 | type_, 33 | }); 34 | Ok(()) 35 | } 36 | 37 | fn gather_param_refs_in_struct(struct_def: &defs::StructDef) -> Result<(), ResolveError> { 38 | let mut gatherer = Self { 39 | external_params: Vec::new(), 40 | }; 41 | 42 | if let Some(ref length_expr) = struct_def.length_expr { 43 | gatherer.gather_param_refs_in_expr(length_expr)?; 44 | } 45 | 46 | for field in struct_def.fields.borrow().iter() { 47 | gatherer.gather_param_refs_in_field(field)?; 48 | } 49 | 50 | struct_def.external_params.replace(gatherer.external_params); 51 | 52 | Ok(()) 53 | } 54 | 55 | fn gather_param_refs_in_field(&mut self, field: &defs::FieldDef) -> Result<(), ResolveError> { 56 | match field { 57 | defs::FieldDef::Pad(_) => Ok(()), 58 | defs::FieldDef::Normal(_) => Ok(()), 59 | defs::FieldDef::List(list_field) => { 60 | if let Some(ref length_expr) = list_field.length_expr { 61 | self.gather_param_refs_in_expr(length_expr)?; 62 | } 63 | Ok(()) 64 | } 65 | defs::FieldDef::Switch(switch_field) => { 66 | self.gather_param_refs_in_expr(&switch_field.expr)?; 67 | for case in switch_field.cases.iter() { 68 | for case_expr in case.exprs.iter() { 69 | self.gather_param_refs_in_expr(case_expr)?; 70 | } 71 | for case_field in case.fields.borrow().iter() { 72 | self.gather_param_refs_in_field(case_field)?; 73 | } 74 | } 75 | Ok(()) 76 | } 77 | defs::FieldDef::Fd(_) => Ok(()), 78 | defs::FieldDef::FdList(fd_list_field) => { 79 | self.gather_param_refs_in_expr(&fd_list_field.length_expr)?; 80 | Ok(()) 81 | } 82 | defs::FieldDef::Expr(expr_field) => { 83 | self.gather_param_refs_in_expr(&expr_field.expr)?; 84 | Ok(()) 85 | } 86 | defs::FieldDef::VirtualLen(_) => Ok(()), 87 | } 88 | } 89 | 90 | fn gather_param_refs_in_expr(&mut self, expr: &defs::Expression) -> Result<(), ResolveError> { 91 | match expr { 92 | defs::Expression::BinaryOp(bin_op_expr) => { 93 | self.gather_param_refs_in_expr(&bin_op_expr.lhs)?; 94 | self.gather_param_refs_in_expr(&bin_op_expr.rhs)?; 95 | Ok(()) 96 | } 97 | defs::Expression::UnaryOp(unary_op_expr) => { 98 | self.gather_param_refs_in_expr(&unary_op_expr.rhs)?; 99 | Ok(()) 100 | } 101 | defs::Expression::FieldRef(_) => Ok(()), 102 | defs::Expression::ParamRef(param_ref_expr) => { 103 | self.add_external_param( 104 | ¶m_ref_expr.field_name, 105 | param_ref_expr.type_.get_resolved().clone(), 106 | )?; 107 | Ok(()) 108 | } 109 | defs::Expression::EnumRef(_) => Ok(()), 110 | defs::Expression::PopCount(expr) => { 111 | self.gather_param_refs_in_expr(expr)?; 112 | Ok(()) 113 | } 114 | defs::Expression::SumOf(sum_of_expr) => { 115 | self.gather_param_refs_in_expr(&sum_of_expr.operand)?; 116 | Ok(()) 117 | } 118 | defs::Expression::ListElementRef => Ok(()), 119 | defs::Expression::Value(_) => Ok(()), 120 | defs::Expression::Bit(_) => Ok(()), 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /xkbcommon-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xkbcommon-example" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies.x11rb] 10 | path = "../x11rb" 11 | features = ["allow-unsafe-code", "xkb"] 12 | 13 | [dependencies.xkbcommon] 14 | version = "0.7" 15 | features = ["x11"] 16 | -------------------------------------------------------------------------------- /xtrace-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtrace-example" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | smol = "2.0" 11 | 12 | [dependencies.x11rb-protocol] 13 | path = "../x11rb-protocol" 14 | features = ["all-extensions", "request-parsing"] 15 | 16 | [dependencies.futures-util] 17 | version = "0.3" 18 | default-features = false 19 | features = ["async-await", "async-await-macro", "io", "std"] 20 | 21 | [dependencies.futures-io] 22 | version = "0.3" 23 | default-features = false 24 | -------------------------------------------------------------------------------- /xtrace-example/src/forwarder.rs: -------------------------------------------------------------------------------- 1 | use futures_io::{AsyncRead, AsyncWrite}; 2 | use futures_util::{AsyncReadExt, AsyncWriteExt}; 3 | use std::io::Result as IOResult; 4 | 5 | /// Forward data between a reader and a writer, calling a callback on the data. 6 | /// 7 | /// This function copies all data from `read` to `write`. At each step, a callback is invoked with 8 | /// the data of the current packet. If `None` is returned, then the packet is complete and can be 9 | /// forwarded to the other end. If `Some(length)` is returned, then the packet is incomplete and at 10 | /// least `length` more bytes are needed. 11 | pub async fn forward_with_callback( 12 | mut read: impl AsyncRead + Unpin, 13 | mut write: impl AsyncWrite + Unpin, 14 | callback: F, 15 | ) -> IOResult<()> 16 | where 17 | F: Fn(&[u8]) -> Option, 18 | { 19 | let mut buffer = Vec::new(); 20 | loop { 21 | // Ask the callback for an estimation of the size of the packet 22 | match callback(&buffer) { 23 | None => { 24 | // We have a full packet, forward it 25 | write.write_all(&buffer).await?; 26 | buffer.clear(); 27 | } 28 | Some(extra_length) => { 29 | // Need to read more 30 | assert!(extra_length > 0); 31 | let old_len = buffer.len(); 32 | buffer.resize_with(old_len + extra_length, Default::default); 33 | read.read_exact(&mut buffer[old_len..]).await?; 34 | } 35 | } 36 | } 37 | } 38 | --------------------------------------------------------------------------------