xcb-1.2.2/.cargo_vcs_info.json0000644000000001360000000000100116120ustar { "git": { "sha1": "0d4e285e25eae13557ad0395bb9351bb31be44bb" }, "path_in_vcs": "" }xcb-1.2.2/.gitignore000064400000000000000000000001051046102023000123660ustar 00000000000000*.o *.so .*.sw* *.pyc target/ Cargo.lock .vscode/ gen/* !gen/diff.sh xcb-1.2.2/CHANGELOG.md000064400000000000000000000122321046102023000122130ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] - - various authors ## [1.2.2] - 2023-07-22 - various authors ### Fixed - fix serialization panic (impl WiredOut #226, #227) - fix `Send` and `Sync` for events (#233) ## [1.2.1] - 2023-04-23 - various authors ### Fixed - updated `quick-xml` as the old one will no longer be compatible with future rust versions - fixed alignment issue ### Changed - Implement `AsRawXcbConnection` for `Connection` ## [1.2.0] - 2022-11-03 - various authors ### Fixed - Segfault when DISPLAY is unset when using xlib - Return `Unknown` instead of panic for unknown events (disabled feature) (#195) ### Changed - `resolve_event` returns `Event::Unknown` instead of panicking when the event could not be resolved. - Types such as `x::Cw` implement `Ord` and can be sorted dynamically (i.e. for `x::CreateWindow`) - `atoms_struct!` macro can specify `only_if_exists` - add `Event::as_raw()`, `Connection::resolve_event()` ## [1.1.1] - 2022-03-19 - rtbo ### Fixed - Padding offset in events constructors ([x11-clipboard/#26](https://github.com/quininer/x11-clipboard/pull/26)) ## [1.1.0] - 2022-03-16 - rtbo ### Added - Xid types (`x::Atom`, `x::Window`, ...) implement `Hash` - `atoms_struct!` macro - `Connection::send_and_check_request` ### Changed - Some documentation improvements. ## [1.0.0] - 2022-03-06 - rtbo - new 1.0 API - implement the extensions in RUST directly (no need to link to C extension libraries) - XCB resolves protocol events and errors to safe enumerations - requests are structures that can be passed to `Connection::send_request` - typesafe cookies for checked/unchecked and void/non-void requests - typesafe XIDs - `bitflags` masks - `debug_atom_names` feature - improvements of the `Debug` implementations - many, many other improvement and fixes, (~90% of github issues are resolved) - support for xcb-1.14 - support for xinput extension - get rid of the `log` dependency ## [0.10.1] - 2021-09-23 - rtbo - fix some code generation affecting the `present` extension - fix compilation warnings about uninhabited type values ## [0.10.0] - 2021-09-19 - rtbo ### Changed - build script is written in Rust. Python no longer needed. (#56, #62, #89, #99) - moved CI to github actions ### Fixed - fixed some member names (e.g. `size_i_d` => `size_id` / `num__f_b_configs` => `num_fb_configs`) ## [0.9.0] - 2019-11-12 - Lompik ### Fixed - get_reply consume cookies and impl Drop on Cookies (#57) ## [0.8.3] - 2019-11-12 - Lompik ### Fixed - fix use after free when connecting with display names (#65) ## [0.8.2] - 2018-02-13 - chrisduerr/myfreeweb/yamnikov-oleg/eigebong/rtbo - move to log 0.4 (#55) - improve missing python error in build.rs (#49) - add Connection.into_raw_conn - make source generation deterministic (#43) ## [0.8.1] - 2017-08-15 - /rtbo/main--/chrisduerr - fix lifetime inconsistency (#40) - impl AsRawFd for Connection (#39) ## [0.8.0] - 2017-07-11 - mjkillough/eduardosm/rtbo - error trait and unsafe cast_error (#32) - mjkillough - unsafe cast_event - rtbo - allow xcb::connect without xlib_xcb feature (fixes also doc generation) (#35) - eduardosm ## [0.7.8] - 2019/11/12 - Lompik - fix use after free when connecting with display names (#65) (backporting from 0.8) ## [0.7.7] - 2017-08-15 - rtbo/mjkillough - branch 0.7.x to support servo - implement Error/Display for GenericError and ConnError - fix lifetime inconsistencies (#40) - Send and Sync implemented regardless of thread feature ## [0.7.6] - 2016-11-14 - rtbo/ibabushkin - much better handling of union accessors (#27) Credits to Inokentiy Babushkin - other minor fixes ## [0.7.5] - 2016-08 - rtbo - multi-threading support (#23) - other bug fixes ## [0.7.4] - 2016-06 - rtbo - templating send_event* to take event obj instead of str - correct iterator attribute lifetime (#16) ## [0.7.3] - 2016-04-10 - rtbo - templating some accessors ## [0.7.2] - 2016-04-02 - rtbo - fix #14 ## [0.7.1] - 2016-03-29 - rtbo - module names closer to ffi - fix #13 ## [0.7.0] - 2016-03-28 - rtbo - fix connection with strings (#9) - assign response_type in *Event::new (#10) - Connection::connect returns Result (#11) - Some documentation (#12) ## [0.6.2] - 2016-03-04 - rtbo - fix: correct names for DRI2 and 3 FFI constants ## [0.6.1] - 2016-03-02 - rtbo - fix: correct names for 'xtest' extension ## [0.6.0] - 2016-02-22 - rtbo - xlib_xcb: Connection owns the xlib::Display and calls XCloseDisplay - requests accept template slices - POD types distinction ## [0.5.0] - 2016-02-07 - rtbo - adding xlib_xcb - show how to create an opengl enabled window ## [0.4.1] - 2016-02-07 - rtbo - generating union accessors - handling of bool parameters in the wrapper API - rewrite of wrappers structures (pub type instead of struct with base field) - module clean-up and export - Travis CI ## [0.4.0] - 2016-03-02 - rtbo/laumann - first fully functional wrappers - rewritten rs_client.py - new examples - made ffi very close to C - fixed wrappers segfaults ## [0.3.0] - 2013 - Aatch xcb-1.2.2/CONTRIBUTING.md000064400000000000000000000037151046102023000126410ustar 00000000000000# Contribution guide All PRs are welcome. When issueing a pull-request, make sure that the repo is formatted with `cargo fmt` and that `cargo clippy --all-features` and `cargo test --all-features` return no error. The source code is organize as follow: ## `build/` The code generation program (aka. the build script). This program scans the XML definition to produce the Rust source code. It is often tedious to understand changes of the Rust generated code only by looking at the code generation source code. If your PR include a modification of the code generation, please consider including a diff of the generated code. It is easily done as follow: - run `./gen.sh previous` before starting your PR - run `./gen.sh current` when you think your work is ready. - include the output of `diff -ur gen/previous gen/current` in the text of the PR. (see [#110](https://github.com/rust-x-bindings/rust-xcb/pull/110) for an example) In VsCode you can observe a diff directly in the editor by running `gen/diff.sh [module name]`. Sometimes, you have to hardcode exceptions in the code generation (e.g. generated different code for a specific request or event). If you do this (it is better if you don't), add an entry to the `EXCEPTION_LIST.txt` file. If you need to write unit test for the generated code, you can add them under `src/test.rs` ## `examples/` Every example is welcome, especially if it covers requests, events or extensions not covered by the current set of examples. If adding an example, add the corresponding entry in `Cargo.toml`. The Github workflows ensure that every example compiles. ## `gen/` Working directory to observe the generated code. The content is not under version control. A sub-directory can be populated by running `./gen.sh [dirname]`. ## `src/` Base library source code. ## `xml/` The XML definitions from the XCB project. These defintions were slightly modified compared to the upstream definitions to ease a part of the code generation. xcb-1.2.2/Cargo.lock0000644000000102400000000000100075620ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "adler32" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "as-raw-xcb-connection" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5f312b0a56c5cdf967c0aeb67f6289603354951683bc97ddc595ab974ba9aa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crc32fast" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] [[package]] name = "deflate" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" dependencies = [ "adler32", ] [[package]] name = "gl" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" dependencies = [ "gl_generator", ] [[package]] name = "gl_generator" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" dependencies = [ "khronos_api", "log", "xml-rs", ] [[package]] name = "khronos_api" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "libc" version = "0.2.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103" [[package]] name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "miniz_oxide" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" dependencies = [ "adler", ] [[package]] name = "pkg-config" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" [[package]] name = "png" version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" dependencies = [ "bitflags", "crc32fast", "deflate", "miniz_oxide", ] [[package]] name = "quick-xml" version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" dependencies = [ "memchr", ] [[package]] name = "x11" version = "2.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dd0565fa8bfba8c5efe02725b14dff114c866724eff2cfd44d76cea74bcd87a" dependencies = [ "libc", "pkg-config", ] [[package]] name = "xcb" version = "1.2.2" dependencies = [ "as-raw-xcb-connection", "bitflags", "gl", "libc", "png", "quick-xml", "x11", ] [[package]] name = "xml-rs" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" xcb-1.2.2/Cargo.toml0000644000000056250000000000100076200ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "xcb" version = "1.2.2" authors = ["Remi Thebault "] build = "build/main.rs" exclude = [ ".github", "examples/todo/*", "gen", "xml/upstream", "xml/upstream_normalized", ] autoexamples = false description = "Rust safe bindings for XCB" documentation = "https://rust-x-bindings.github.io/rust-xcb/xcb/" readme = "README.md" keywords = [ "xcb", "window", "xlib", "x11", "opengl", ] license = "MIT" repository = "https://github.com/rust-x-bindings/rust-xcb" [package.metadata.docs.rs] all-features = true [[example]] name = "basic_window" [[example]] name = "connect_str" [[example]] name = "connect" [[example]] name = "draw_root_window" [[example]] name = "drawing" [[example]] name = "get_keyboard_names" required-features = [ "xkb", "xinput", "xlib_xcb", ] [[example]] name = "get_all_windows" [[example]] name = "opengl_window" required-features = [ "glx", "xlib_xcb", "dri2", ] [[example]] name = "randr_crtc_info" required-features = ["randr"] [[example]] name = "print_setup" [[example]] name = "randr_crtc_listen" required-features = ["randr"] [[example]] name = "randr_screen_info" required-features = ["randr"] [[example]] name = "randr_screen_modes" required-features = ["randr"] [[example]] name = "screen_info" [[example]] name = "threaded_window" [[example]] name = "xinput_stylus_events" required-features = ["xinput"] [[example]] name = "screenshot" [[example]] name = "xkb_init" required-features = ["xkb"] [dependencies.as-raw-xcb-connection] version = "1.0" optional = true [dependencies.bitflags] version = "1.3.2" [dependencies.libc] version = "0.2.102" [dependencies.x11] version = "2.19.0" features = ["xlib"] optional = true [dev-dependencies.gl] version = "0.14.0" [dev-dependencies.png] version = "0.17.5" [dev-dependencies.x11] version = "2.19.1" features = [ "xlib", "glx", ] [build-dependencies.quick-xml] version = "0.28.1" [features] composite = ["xfixes"] damage = ["xfixes"] debug_atom_names = [] dpms = [] dri2 = [] dri3 = [] ge = [] glx = [] present = [ "render", "xfixes", "sync", ] randr = ["render"] record = [] render = [] res = [] screensaver = [] shape = [] shm = [] sync = [] xevie = [] xf86dri = [] xf86vidmode = [] xfixes = [ "render", "shape", ] xinerama = [] xinput = ["xfixes"] xkb = [] xlib_xcb = ["x11/xlib"] xprint = [] xselinux = [] xtest = [] xv = ["shm"] xvmc = ["xv"] xcb-1.2.2/Cargo.toml.orig000064400000000000000000000044171046102023000132770ustar 00000000000000[package] name = "xcb" version = "1.2.2" authors = [ "Remi Thebault " ] description = "Rust safe bindings for XCB" repository = "https://github.com/rust-x-bindings/rust-xcb" documentation = "https://rust-x-bindings.github.io/rust-xcb/xcb/" readme = "README.md" keywords = ["xcb", "window", "xlib", "x11", "opengl"] license = "MIT" build = "build/main.rs" exclude = [".github", "examples/todo/*", "gen", "xml/upstream", "xml/upstream_normalized"] autoexamples = false edition = "2018" [package.metadata.docs.rs] all-features = true [build-dependencies] quick-xml = "0.28.1" [dependencies] libc = "0.2.102" bitflags = "1.3.2" as-raw-xcb-connection = { version = "1.0", optional = true } [dependencies.x11] version = "2.19.0" optional = true features = ["xlib"] [features] debug_atom_names = [] xlib_xcb = ["x11/xlib"] composite = [ "xfixes" ] damage = [ "xfixes" ] dpms = [] dri2 = [] dri3 = [] ge = [] glx = [] present = [ "render", "xfixes", "sync" ] randr = [ "render" ] record = [] render = [] res = [] screensaver = [] shape = [] shm = [] sync = [] xevie = [] xf86dri = [] xf86vidmode = [] xfixes = [ "render", "shape" ] xinerama = [] xinput = [ "xfixes" ] xkb = [] xprint = [] xselinux = [] xtest = [] xv = [ "shm" ] xvmc = [ "xv" ] [dev-dependencies] gl = "0.14.0" png = "0.17.5" [dev-dependencies.x11] version = "2.19.1" features = ["xlib", "glx"] [[example]] name = "basic_window" [[example]] name = "connect_str" [[example]] name = "connect" [[example]] name = "draw_root_window" [[example]] name = "drawing" [[example]] name = "get_keyboard_names" required-features = ["xkb", "xinput", "xlib_xcb"] [[example]] name = "get_all_windows" [[example]] name = "opengl_window" required-features = ["glx", "xlib_xcb", "dri2"] [[example]] name = "randr_crtc_info" required-features = ["randr"] [[example]] name = "print_setup" [[example]] name = "randr_crtc_listen" required-features = ["randr"] [[example]] name = "randr_screen_info" required-features = ["randr"] [[example]] name = "randr_screen_modes" required-features = ["randr"] [[example]] name = "screen_info" [[example]] name = "threaded_window" [[example]] name = "xinput_stylus_events" required-features = ["xinput"] [[example]] name = "screenshot" [[example]] name = "xkb_init" required-features = ["xkb"] xcb-1.2.2/EXCEPTION_LIST.txt000064400000000000000000000026651046102023000133450ustar 00000000000000This file is an attempt of keeping track of all exceptions introduced in the code generation. An exception is a difference of code generation based on hard-coded name. - switch_exceptions (cg/mod.rs) - mask_exceptions (cg/mod.rs) - enum exceptions (x::SendEventDest in cg/enum.rs) - requests that receive event in parameters (cg/request.rs) - ClientMessageEvent::new: format is inferred from data (cg/event.rs) - ClientMessageEvent::data: variant inferred from format (handle_client_message_data in cg/struct.rs) - randr::NotifyEvent::new: sub_code is inferred from u variant (cg/event.rs) - randr::NotifyEvent::u: variant inferred from sub_code (handle_randr_notify_data in cg/struct.rs) - xkb unions have a special treatment, the variant is inferred from a type field, hidden to the user (cg/union.rs) - xinput::Device is faked from DeviceId and is defined in lib.rs (search for "xinput" in cg folder) - specific documentation for xinput::InputInfoInfo::Button (cg/switch.rs) - A small part of the XML definitions is hand edited to make the code generation a lot easier. and to improve the documentation. To check what has been edited, a copy of the upstream definitions is kept under - xml/upstream - xml/upstream_normalized The normalized copy is simply a normalization of indentation to 4 spaces. The hand edition is the diff from xml/upstream_normalized to xml. Run `diff -ur xml/upstream_normalized xml` to print the diff. xcb-1.2.2/LICENSE000064400000000000000000000022701046102023000114100ustar 00000000000000Copyright (c) 2013 James Miller Copyright (c) 2016 Remi Thebault Thomas Bracht Laumann Jespersen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. xcb-1.2.2/README.md000064400000000000000000000146341046102023000116710ustar 00000000000000# Rust XCB Rust-XCB is a safe Rust interface to [XCB](http://xcb.freedesktop.org). Rust-XCB uses under the hood the core XCB functions to connect and communicate with the X server. __Documentation__: https://rust-x-bindings.github.io/rust-xcb/xcb/ Rust-XCB is constituted of the following components: - the core library - the X protocol and its extensions See [CONTRIBUTING.md](https://github.com/rust-x-bindings/rust-xcb/blob/main/CONTRIBUTING.md) for contributions. ## The core library The main component is the `Connection` class which is used to connect and communicate to the X server. The `Connection` class wraps calls to the C XCB functions in a safe to use interface. In the new API (`v1.0+`), Rust-XCB takes care of all the heavy lifting of event and error resolution, including the handling of the different kinds of event (regular events, "GeGeneric" events, the specifics about Xkb ...) and present them under a unified and safe to use `enum` instead of requesting the user to perform unsafe cast as in the C library. The core library also provides many traits that are used in the protocol implementation. e.g. the `BaseEvent` and `BaseError` traits, the `Reply` trait... The `Raw` trait is also provided to convert into and from C event or error types. ## The protocol implementation The core X protocol and all the extensions present in XCB are generated by the build scripts, entirely written in Rust. The build script do not generate bindings to the C protocol extensions, it generates a safe Rust implementation of the protocol: - Simple structures that have the same memory layout than C are translated in Rust directly (e.g. points etc.) - More complex structures wrap a slice of raw data and provide accessor methods (`Debug` will still print all the members) - The masks use the `bitflags` crate macro - The enums are enums! - The unions are also enums, but they carry data - The Xids (handles to windows, pixmaps etc.) are type-safe implementations of the `Xid` trait, not just integers. - The requests are structures that serialize themselves when passed to the `Connection`. - Each request has two type of cookie, for checked and unchecked requests. This allows type safe reply fetching and error checking - The protocol and each extension provide an `Event` and an `Error` enum, which are unified by the core library. ## Debugging All types in Rust XCB implement `Debug` in a way that allows recursive debug print. E.g. iterators will not print a useless pointer value, but will recurse down to each element. There is in addition the optional `"debug_atom_names"` cargo feature under which each atom will print its name for easier debugging in some situations. For example, Xinput provide some information about input devices with atom identifiers. This allows you to quickly look-up which atoms you need to intern and seek for. E.g. the feature would turn: ``` Atom { res_id: 303, } ``` into ``` Atom("Abs Pressure" ; 303) ``` The feature sets global variable to have access to the connection in the `Debug::fmt` call, so it should be activated only when needed. ## The v1.0 API Here are some highlights of the `v1.0` API compared to the `v0.x` API. ### Modules Previously, the core X protocol was directly accessible under the `xcb` crate namespace. This is no longer the case, the protocol is under the `x` module, and each extension gets its own module too like before. Only the core library is directly accessible under `xcb`. This helps for a clean separation of concerns and save a lot of confusion in regards to the new event and error types. ### Window creation ```rust // v0.x let window = conn.generate_id(); // this is a u32 let values = [ (xcb::CW_BACK_PIXEL, screen.white_pixel()), ( xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_KEY_PRESS, ), ]; xcb::create_window( &conn, xcb::COPY_FROM_PARENT as u8, window, screen.root(), 0, 0, 150, 150, 10, xcb::WINDOW_CLASS_INPUT_OUTPUT as u16, screen.root_visual(), &values, ); // v1.0 let window: x::Window = conn.generate_id(); conn.send_request(&x::CreateWindow { depth: x::COPY_FROM_PARENT as u8, wid: window, parent: screen.root(), x: 0, y: 0, width: 150, height: 150, border_width: 10, class: x::WindowClass::InputOutput, visual: screen.root_visual(), value_list: &[ x::Cw::BackPixel(screen.white_pixel()), x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), ], }); ``` ### Checked void request ```rust // v0.x // same cookie than for xcb::map_window let cookie = xcb::map_window_checked(&conn, window); // this error is a simple wrapper over xcb_generic_error_t cookie.request_check()?; // v1.0 // specific cookie type for the checked request // code would not compile with `conn.send_request(..)` let cookie = conn.send_request_checked(&x::MapWindow {window}); // reports a resolved error enum (e.g. x::Error::Drawable(..)) conn.check_request(cookie)?; ``` ### Event and error handling ```rust // 0.x loop { let event = conn.wait_for_event(); // Errors are only wrappers around generic C errors and must be casted // from the event if `response_type` is 0. That seem very safe Rust... // I am the core author and I don't exactly know how to do proper error // handling with the 0.x API... match event { None => { break; } Some(event) => { let r = event.response_type(); if r == 0 { // This is an error. panic!("received error from the X server"); } else if r == xcb::KEY_PRESS as u8 { let key_press: &xcb::KeyPressEvent = unsafe { // so much for the safe interface xcb::cast_event(&event) }; // do stuff } else if r == xkb_first_event { // resolving extension events like we would in C // very poor support is provided (not better than C) } } } } // 1.0 fn main() -> xcb::Result<()> { // ... loop { match conn.wait_for_event()? { xcb::Event::X(x::Event::KeyPress(key_press)) => { // do stuff } xcb::Event::Xkb(xkb::Event::MapNotify(ev)) => { // do other stuff (pass data to xkbcommon for example) } _ => {} } } } ``` xcb-1.2.2/TODO_v1.md000064400000000000000000000025641046102023000121060ustar 00000000000000# TODO list for v1.0 - [x] Implementation of the new API - [x] Xid - [x] Xid unions - [x] Structs - [x] Accessors - [x] Constructor - [x] Switches - [x] Constructors - [x] Expressions - [x] Enums / Masks - [x] Unions - [x] Errors - [x] Requests - [x] Events - [x] Porting examples - [x] Porting XML examples - [x] Documentation - [x] link to reply in request - [x] Finalize switches naming (e.g. x::CreateWindowValueList -> x::Cw) - [x] Report request name in protocol errors - [x] Support of xinput::SendExtensionEvent - [ ] Examples - [x] Port old examples - [x] Create example with XInput (pen tablet?) - [ ] Update with valuator classes - [ ] Create example with Vulkan - [ ] Create example with wgpu - [ ] Create example using an Fd API - Missing API tweaks / fixes: - [x] Cw::EventMask should take an EventMask - [x] fix xkb::GetMapReplyMap::VirtualMods takes ModMask - [x] use altenum in xinput for Device (All, Master or Id (or slave?)) - [x] xinput::ListInputDevice has incomplete InputInfo - [x] remove fieldrefs in switches - [x] Debug props should match format - [x] Fix Fd API - [x] Reply impl Send and Sync - [x] Fix the wire_len calculation (32 + 4 * length) - [x] Porting toy-xcb - [x] Porting xkbcommon-rs - [x] Porting x11-clipboard xcb-1.2.2/build/cg/doc.rs000064400000000000000000000063701046102023000132130ustar 00000000000000use super::CodeGen; use crate::cg; use crate::ir; use std::io::{self, Write}; #[derive(Debug, Clone)] pub struct DocField { pub name: String, pub text: String, } impl DocField { pub(super) fn emit(&self, out: &mut O, ind: u32) -> io::Result<()> { emit_doc_text(out, ind, &self.text)?; Ok(()) } } #[derive(Debug, Clone)] pub struct DocError { pub typ: String, pub text: String, } #[derive(Debug, Clone)] pub struct DocSee { pub typ: String, pub name: String, } #[derive(Debug, Clone)] pub struct Doc { pub brief: Option, pub description: Option, pub example: Option, pub fields: Vec, pub errors: Vec, pub sees: Vec, } impl Doc { pub(super) fn lookup_field(&self, name: &str) -> Option<&DocField> { self.fields.iter().find(|df| df.name == name) } pub(super) fn emit(&self, out: &mut O, ind: u32) -> io::Result<()> { let mut empty = true; if let Some(brief) = &self.brief { emit_doc_text(out, ind, brief)?; empty = false; } if let Some(desc) = &self.description { if !empty { emit_doc_text(out, ind, "")?; } emit_doc_text(out, ind, desc)?; empty = false; } if let Some(example) = &self.example { if example.contains("fn main") { if !empty { emit_doc_text(out, ind, "")?; } emit_doc_text(out, ind, "# Example")?; emit_doc_text(out, ind, "```no_run")?; emit_doc_text(out, ind, example)?; emit_doc_text(out, ind, "```")?; } } Ok(()) } } pub(super) fn emit_doc_text(out: &mut O, ind: u32, text: &str) -> io::Result<()> { for s in text.split('\n') { let s = s.trim_end(); if !s.is_empty() { writeln!(out, "{}/// {}", cg::ind(ind), s.trim_end())?; } else { writeln!(out, "{}///", cg::ind(ind))?; } } Ok(()) } impl CodeGen { pub(super) fn resolve_doc(&self, doc: Option) -> Option { doc.map(|doc| Doc { brief: doc.brief, description: doc.description, example: doc.example, fields: doc .fields .into_iter() .map(|df| DocField { name: cg::rust_field_name(&df.name), text: df.text, }) .collect(), errors: doc .errors .into_iter() .map(|de| DocError { typ: de.typ, text: de.text, }) .collect(), sees: doc .sees .into_iter() .map(|de| DocSee { typ: de.typ, name: de.name, }) .collect(), }) } pub(super) fn doc_lookup_field(&self, doc: Option<&Doc>, name: &str) -> Option { if let Some(doc) = doc { doc.lookup_field(name).cloned() } else { None } } } xcb-1.2.2/build/cg/enum.rs000064400000000000000000000174511046102023000134140ustar 00000000000000use super::{util, CodeGen}; use crate::cg::doc::emit_doc_text; use crate::cg::{self, Doc, TypeInfo}; use crate::ir::{self, EnumItem}; use std::io::{self, Write}; impl CodeGen { pub(super) fn resolve_enum(&mut self, typ: &str, items: &[EnumItem], doc: &Option) { let doc = self.resolve_doc(doc.clone()); let is_mask = items .iter() .any(|item| matches!(item, ir::EnumItem::Bit { .. })); let mut rs_typ = cg::rust_type_name(typ); if self.xcb_mod == "xinput" && typ == "Device" { self.handle_xinput_device_enum(); return; } // solving name conflicts that may happen with XID of the same name if rs_typ == "Event" || self.rs_typs[&rs_typ] > 1 { *self.rs_typs.get_mut(&rs_typ).unwrap() -= 1; rs_typ.push_str(if is_mask { "Flags" } else { "Enum" }); self.rs_typs.insert(rs_typ.clone(), 1); } for ex in &self.mask_exceptions { if ex.module == self.xcb_mod && ex.rs_typ == rs_typ { rs_typ = ex.new_rs_typ.to_string(); break; } } let items = self.map_enum_items(items, is_mask, doc.as_ref()); let info = if is_mask { TypeInfo::Mask { module: None, rs_typ, items, doc, } } else { TypeInfo::Enum { module: None, rs_typ, items, altenum_typ: None, doc, } }; self.register_typ(typ.to_string(), info); } fn map_enum_items( &self, items: &[EnumItem], is_mask: bool, doc: Option<&Doc>, ) -> Vec<(String, u32, Option)> { let mut items = items.iter(); let mut vec = vec![self.map_enum_item(items.next().unwrap(), is_mask, doc)]; let mut last_val = vec[0].1; for ei in items { let ei = self.map_enum_item(ei, is_mask, doc); if ei.1 != last_val { last_val = ei.1; vec.push(ei); } } vec } fn map_enum_item( &self, ei: &ir::EnumItem, is_mask: bool, doc: Option<&Doc>, ) -> (String, u32, Option) { match ei { ir::EnumItem::Bit(name, value) => { let doc = self.doc_lookup_field(doc, &cg::rust_field_name(name)); (map_mask_item_name(name), 1 << value, doc.map(|d| d.text)) } ir::EnumItem::Value(name, value) => { let doc = self.doc_lookup_field(doc, &cg::rust_field_name(name)); ( if is_mask { map_mask_item_name(name) } else { map_enum_item_name(name) }, *value, doc.map(|d| d.text), ) } } } pub(super) fn emit_enum( &self, out: &mut O, rs_typ: &str, items: &[(String, u32, Option)], altenum_typ: &Option<(Option, String)>, doc: Option<&Doc>, ) -> io::Result<()> { if self.xcb_mod == "xproto" && rs_typ == "SendEventDest" { return self.emit_send_event_dest(out, items); } let mut emit_enum = true; if let Some(altenum_typ) = altenum_typ { let typinfo = self.find_typinfo(altenum_typ.0.as_deref(), &altenum_typ.1); if let TypeInfo::Xid { rs_typ: xid_rs_typ, .. } = typinfo { writeln!(out)?; for item in items { if let Some(text) = &item.2 { emit_doc_text(out, 0, text)?; } let name_rs_typ = if rs_typ.ends_with("Enum") { &rs_typ[0..rs_typ.len() - 4] } else { rs_typ }; let name = format!("{}_{}", name_rs_typ, util::tit_split(&item.0)).to_uppercase(); writeln!( out, "pub const {}: {} = {} {{ res_id: {} }};", name, xid_rs_typ, xid_rs_typ, item.1 )?; } emit_enum = matches!( (self.xcb_mod.as_str(), rs_typ), ("xkb", "Id") | ("xproto", "InputFocus") ); } } if emit_enum { writeln!(out)?; if let Some(doc) = doc { doc.emit(out, 0)?; } writeln!( out, "#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]" )?; writeln!(out, "#[repr(u32)]")?; writeln!(out, "pub enum {} {{", rs_typ)?; for item in items { if let Some(text) = &item.2 { emit_doc_text(out, 1, text)?; } writeln!(out, " {} = {},", item.0, item.1)?; } writeln!(out, "}}")?; } Ok(()) } fn emit_send_event_dest( &self, out: &mut O, items: &[(String, u32, Option)], ) -> io::Result<()> { writeln!(out, "#[derive(Copy, Clone, Debug)]")?; writeln!(out, "pub enum SendEventDest {{")?; for item in items { if let Some(text) = &item.2 { emit_doc_text(out, 1, text)?; } writeln!(out, " {},", item.0)?; } writeln!(out, " Window(Window),")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl SendEventDest {{")?; writeln!(out, " pub fn resource_id(&self) -> u32 {{")?; writeln!(out, " match self {{")?; for item in items { writeln!( out, "{}SendEventDest::{} => {},", cg::ind(3), item.0, item.1 )?; } writeln!( out, "{}SendEventDest::Window(w) => w.resource_id(),", cg::ind(3) )?; writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out)?; writeln!( out, " pub fn serialize(&self, wire_buf: &mut [u8]) -> usize {{" )?; writeln!(out, " let id = self.resource_id();")?; writeln!(out, " WiredOut::serialize(&id, wire_buf)")?; writeln!(out, " }}")?; writeln!(out, "}}")?; Ok(()) } pub(super) fn emit_mask( &self, out: &mut O, rs_typ: &str, items: &[(String, u32, Option)], doc: Option<&Doc>, ) -> io::Result<()> { writeln!(out)?; writeln!(out, "bitflags! {{")?; if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub struct {}: u32 {{", rs_typ)?; for item in items { if let Some(text) = &item.2 { emit_doc_text(out, 2, text)?; } writeln!(out, "{}const {} = {:#010x};", cg::ind(2), item.0, item.1)?; } writeln!(out, " }}")?; writeln!(out, "}}")?; Ok(()) } } pub(super) fn map_enum_item_name(name: &str) -> String { let mut name = util::tit_cap(name); if name.chars().next().unwrap().is_ascii_digit() { name.insert(0, 'N'); } name } pub(super) fn map_mask_item_name(name: &str) -> String { let mut name = util::tit_split(name).to_ascii_uppercase(); if name.chars().next().unwrap().is_ascii_digit() { name.insert(0, 'N'); } name } xcb-1.2.2/build/cg/error.rs000064400000000000000000000210361046102023000135730ustar 00000000000000use super::r#struct::{make_field, ResolvedFields}; use super::{CodeGen, Error}; use crate::cg::{self}; use crate::ir; use std::io::{self, Write}; impl CodeGen { pub(super) fn resolve_error( &mut self, name: String, number: i32, fields: Vec, _doc: Option, ) { let fields = { let mut ff = vec![ make_field("response_type".into(), "CARD8".into()), make_field("error_code".into(), "CARD8".into()), make_field("sequence".into(), "CARD16".into()), ]; for f in fields.into_iter() { ff.push(f); } ff }; let variant = cg::rust_type_name(&name); let rs_typ = variant.clone() + "Error"; let ResolvedFields { fields, wire_sz, .. } = self.resolv_struct_fields(&rs_typ, "", &fields, None); self.rs_typs_need_count.insert(rs_typ.clone(), 1); self.errors.push(Error { rs_typ, copy_from_rs_typ: None, variant, number, fields, wire_sz, }); } pub(super) fn resolve_errorcopy(&mut self, name: String, number: i32, r#ref: String) { let variant = cg::rust_type_name(&name); let rs_typ = variant.clone() + "Error"; let (ref_module, ref_variant) = self.extract_module(&r#ref); let ref_variant = cg::rust_variant_name(ref_variant); let mut implicit_module = None; let error = match &ref_module { Some(module) => { let di = self .depinfo .iter() .find(|di| di.xcb_mod == *module) .unwrap_or_else(|| panic!("could not find {} dependency", module)); di.errors .iter() .find(|e| e.variant == ref_variant) .unwrap_or_else(|| panic!("could not find error {}::{}", module, ref_variant)) } None => self .errors .iter() .find(|e| e.variant == ref_variant) .or_else(|| { for di in &self.depinfo { for err in &di.errors { if err.variant == ref_variant { implicit_module = Some(di.xcb_mod.clone()); return Some(err); } } } None }) .unwrap_or_else(|| { panic!( "{}: cannot find error {} referenced by {}", self.xcb_mod, r#ref, name ) }), } .clone(); self.errors.push(Error { rs_typ, variant, number, copy_from_rs_typ: Some(error.rs_typ), fields: error.fields, wire_sz: error.wire_sz, }); } pub(crate) fn emit_errors(&self, out: &mut O) -> io::Result<()> { if self.errors.is_empty() { return Ok(()); } for error in &self.errors { if let Some(copy_from_rs_typ) = &error.copy_from_rs_typ { let prefix = if copy_from_rs_typ == "ValueError" && self.xcb_mod != "xproto" { "xproto::" } else { "" }; writeln!(out)?; writeln!(out, "/// The `{}` error.", error.rs_typ)?; writeln!( out, "pub type {} = {}{};", error.rs_typ, prefix, copy_from_rs_typ )?; continue; } // Event are struct holding a pointer. // They own the data pointed to that must be freed during drop. writeln!(out)?; writeln!(out, "/// The `{}` error.", error.rs_typ)?; writeln!(out, "pub struct {} {{", error.rs_typ)?; writeln!(out, " raw: *mut xcb_generic_error_t,")?; writeln!(out, "}}")?; writeln!(out)?; writeln!( out, "impl base::Raw for {} {{", error.rs_typ )?; writeln!( out, " unsafe fn from_raw(raw: *mut xcb_generic_error_t) -> Self {{ {} {{ raw }} }}", error.rs_typ )?; writeln!(out)?; writeln!(out, " fn as_raw(&self) -> *mut xcb_generic_error_t {{")?; writeln!(out, " self.raw")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::BaseError for {} {{", error.rs_typ)?; if let Some(ext_info) = self.ext_info.as_ref() { writeln!( out, " const EXTENSION: std::option::Option = Some(ext::Extension::{});", ext_info.rs_name )?; } else { writeln!( out, " const EXTENSION: std::option::Option = None;" )?; } writeln!(out)?; if error.number >= 0 { writeln!(out, " const NUMBER: u32 = {};", error.number)?; } else { writeln!(out, " const NUMBER: u32 = u32::MAX;")?; } writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl {} {{", error.rs_typ)?; writeln!( out, " fn wire_ptr(&self) -> *const u8 {{ self.raw as *const u8 }}" )?; writeln!(out)?; writeln!(out, " fn wire_len(&self) -> usize {{ 32 }}")?; self.emit_struct_accessors(out, &error.rs_typ, &error.fields)?; writeln!(out, "}}")?; self.emit_debug_impl(out, &error.rs_typ, &error.fields)?; writeln!(out)?; writeln!(out, "impl Drop for {} {{", error.rs_typ)?; writeln!(out, " fn drop(&mut self) {{")?; writeln!(out, " unsafe {{ libc::free(self.raw as *mut _); }}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "unsafe impl Send for {} {{}}", error.rs_typ)?; writeln!(out, "unsafe impl Sync for {} {{}}", error.rs_typ)?; } writeln!(out)?; if let Some(ext_info) = &self.ext_info { writeln!( out, "/// Unified error type for the {} extension", ext_info.rs_name )?; } else { writeln!(out, "/// Unified error type for the X core protocol")?; } writeln!(out, "#[derive(Debug)]")?; writeln!(out, "pub enum Error {{")?; for error in &self.errors { writeln!(out, " {}({}),", error.variant, error.rs_typ)?; } writeln!(out, "}}")?; self.emit_resolve_wire_error(out)?; Ok(()) } fn emit_resolve_wire_error(&self, out: &mut O) -> io::Result<()> { writeln!(out)?; writeln!(out, "impl base::ResolveWireError for Error {{")?; writeln!(out, "{}unsafe fn resolve_wire_error(first_error: u8, raw: *mut xcb_generic_error_t) -> Self {{", cg::ind(1))?; writeln!(out, "{}debug_assert!(!raw.is_null());", cg::ind(2))?; writeln!(out, "{}let error_code = (*raw).error_code;", cg::ind(2))?; writeln!(out, "{}match error_code - first_error {{", cg::ind(2))?; for error in &self.errors { if error.number < 0 { continue; } writeln!( out, "{}{} => Error::{}({}::from_raw(raw)),", cg::ind(3), error.number, error.variant, error.rs_typ )?; } writeln!(out, "{}_ => unreachable!(", cg::ind(3))?; writeln!( out, "{}\"Could not resolve {} Error with error_code {{}} and first_error {{}}\",", cg::ind(4), self.xcb_mod )?; writeln!(out, "{}error_code, first_error", cg::ind(4))?; writeln!(out, "{}),", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; writeln!(out, "}}")?; Ok(()) } } xcb-1.2.2/build/cg/event.rs000064400000000000000000000671421046102023000135730ustar 00000000000000use super::r#struct::{make_field, ResolvedFields}; use super::{CodeGen, Event, Field, UnionVariant, UnionVariantContent, WireSz}; use crate::cg::r#struct::{enum_mask_qualified_rs_typ, RANDR_SUBCODES}; use crate::cg::{self, Expr, StructStyle, TypeInfo}; use crate::cg::{util, QualifiedRsTyp}; use crate::ir; use std::io::{self, Write}; impl CodeGen { pub(super) fn resolve_event( &mut self, name: String, number: i32, mut fields: Vec, is_xge: bool, no_seq_number: bool, doc: Option, ) { let doc = self.resolve_doc(doc); let (fields, _must_pack) = { let mut ff = vec![make_field("response_type".into(), "CARD8".into())]; let mut must_pack = false; let mut sz = 1; // response_type size if is_xge { ff.push(make_field("extension".into(), "CARD8".into())); ff.push(make_field("sequence".into(), "CARD16".into())); ff.push(make_field("length".into(), "CARD32".into())); ff.push(make_field("event_type".into(), "CARD16".into())); sz += 9; } else if !no_seq_number { ff.push(fields.remove(0)); ff.push(make_field("sequence".into(), "CARD16".into())); } for f in fields.into_iter() { if is_xge { let fsz = self.ir_field_sizeof(&f); ff.push(f); if sz < 32 { sz += fsz .fixed_length() .expect("can't compute ffi full_sequence position"); if sz == 32 { ff.push(make_field("full_sequence".into(), "CARD32".into())); } } else if let Expr::Value(fsz) = fsz { if fsz == 8 { must_pack = true; } } } else { ff.push(f); } } (ff, must_pack) }; let variant = cg::rust_type_name(&name); let rs_typ = variant.clone() + "Event"; let ResolvedFields { mut fields, wire_sz, .. } = self.resolv_struct_fields(&rs_typ, "", &fields, doc.as_ref()); self.rs_typs_need_count.insert(rs_typ.clone(), 1); for f in &mut fields { if let Field::Field { ref mut name, .. } = f { if name == "new" { *name = "new_".to_string(); } } } self.events.push(Event { rs_typ, variant, number, fields, copy_from_rs_typ: None, wire_sz, doc, is_xge, }); } pub(super) fn resolve_eventcopy(&mut self, name: String, number: i32, r#ref: String) { let variant = cg::rust_type_name(&name); let rs_typ = variant.clone() + "Event"; let (ref_module, ref_variant) = self.extract_module(&r#ref); let ref_variant = cg::rust_variant_name(ref_variant); let mut implicit_module = None; let event = match &ref_module { Some(module) => { let di = self .depinfo .iter() .find(|di| di.xcb_mod == *module) .unwrap_or_else(|| panic!("could not find {} dependency", module)); di.events .iter() .find(|e| e.variant == ref_variant) .unwrap_or_else(|| panic!("could not find event {}::{}", module, ref_variant)) } None => self .events .iter() .find(|e| e.variant == ref_variant) .or_else(|| { for di in &self.depinfo { for ev in &di.events { if ev.variant == ref_variant { implicit_module = Some(di.xcb_mod.clone()); return Some(ev); } } } None }) .unwrap_or_else(|| { panic!( "{}: cannot find error {} referenced by {}", self.xcb_mod, r#ref, name ) }), } .clone(); self.events.push(Event { rs_typ, variant, number, copy_from_rs_typ: Some(event.rs_typ), fields: event.fields, wire_sz: event.wire_sz, is_xge: event.is_xge, doc: event.doc, }); } pub(super) fn resolve_event_struct(&mut self, typ: String, selectors: Vec) { let rs_typ = cg::rust_type_name(&typ); let mut variants = Vec::new(); let mut wire_sz = Expr::Value(32); for es in &selectors { for ev in &self.events { if es.xge == ev.is_xge && ev.number >= es.opcode_range.0 as i32 && ev.number <= es.opcode_range.1 as i32 { variants.push(UnionVariant { variant: ev.variant.clone(), module: None, content: UnionVariantContent::RsTyp(ev.rs_typ.clone()), }); if es.xge { wire_sz = Expr::Unknown("XGE event".into()); } } } } let typ_info = TypeInfo::Union { rs_typ, wire_sz, variants, module: None, type_field: None, impl_clone: false, emit: true, }; self.register_typ(typ, typ_info); } fn ir_field_sizeof(&self, f: &ir::Field) -> Expr { match f { ir::Field::Field { typ, .. } => self.typ_wire_sz(typ), ir::Field::List { typ, len_expr: ir::Expr::Value(len), .. } => { if let Expr::Value(wire_sz) = self.typ_wire_sz(typ) { Expr::Value(*len * wire_sz) } else { Expr::Unknown("variable list".into()) } } ir::Field::List { .. } | ir::Field::ListNoLen { .. } => { Expr::Unknown("variable list".into()) } ir::Field::Pad(sz) => Expr::Value(*sz), _ => unreachable!("{:#?}", f), } } fn typ_wire_sz(&self, typ: &str) -> Expr { let (module, typ) = util::extract_module(typ); let typinfo = self.find_typinfo(module, typ); typinfo.wire_sz() } pub(crate) fn emit_events(&self, out: &mut O) -> io::Result<()> { if self.events.is_empty() { return Ok(()); } for event in &self.events { if event.is_xge && self.xcb_mod == "xproto" { // event GeGeneric is not to be emitted because it is meant // to refer to special extension events (xge events) // rust-xcb handles event in much better way than the C bindings continue; } if let Some(copy_from_rs_typ) = &event.copy_from_rs_typ { writeln!(out)?; if let Some(doc) = &event.doc { doc.emit(out, 0)?; } else { writeln!(out, "/// The `{}` event.", event.rs_typ)?; } writeln!(out, "pub type {} = {};", event.rs_typ, copy_from_rs_typ)?; continue; } // Event are struct holding a pointer. // They own the data pointed to that must be freed during drop. let (trait_impl, raw_typ) = if event.is_xge { ("base::GeEvent", "xcb_ge_generic_event_t") } else { ("base::BaseEvent", "xcb_generic_event_t") }; writeln!(out)?; if let Some(doc) = &event.doc { doc.emit(out, 0)?; } else { writeln!(out, "/// The `{}` event.", event.rs_typ)?; } writeln!(out, "pub struct {} {{", event.rs_typ)?; writeln!(out, " raw: *mut {},", raw_typ)?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::Raw<{}> for {} {{", raw_typ, event.rs_typ)?; writeln!( out, " unsafe fn from_raw(raw: *mut {}) -> Self {{ {} {{ raw }} }}", raw_typ, event.rs_typ )?; writeln!(out)?; writeln!(out, " fn as_raw(&self) -> *mut {} {{", raw_typ)?; writeln!(out, " self.raw")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl {} for {} {{", trait_impl, event.rs_typ)?; if event.is_xge { writeln!( out, " const EXTENSION: ext::Extension = ext::Extension::{};", self.ext_info.as_ref().unwrap().rs_name )?; writeln!(out)?; } else if let Some(ext_info) = self.ext_info.as_ref() { writeln!( out, " const EXTENSION: std::option::Option = Some(ext::Extension::{});", ext_info.rs_name )?; } else { writeln!( out, " const EXTENSION: std::option::Option = None;" )?; } writeln!(out, " const NUMBER: u32 = {};", event.number)?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl {} {{", event.rs_typ)?; if !event.is_xge { // we enable contruction of classic events to pass to SendEvent request self.emit_event_new(out, event)?; writeln!(out)?; } writeln!( out, " fn wire_ptr(&self) -> *const u8 {{ self.raw as *const u8 }}" )?; self.emit_struct_accessors(out, &event.rs_typ, &event.fields)?; writeln!(out, "}}")?; self.emit_debug_impl(out, &event.rs_typ, &event.fields)?; self.emit_wired_impl(out, &event.rs_typ, event.is_xge, raw_typ)?; writeln!(out)?; writeln!(out, "impl Drop for {} {{", event.rs_typ)?; writeln!(out, " fn drop(&mut self) {{")?; writeln!(out, " unsafe {{ libc::free(self.raw as *mut _); }}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "unsafe impl Send for {} {{}}", event.rs_typ)?; writeln!(out, "unsafe impl Sync for {} {{}}", event.rs_typ)?; } writeln!(out)?; if let Some(ext_info) = &self.ext_info { writeln!( out, "/// Unified event type for the {} extension", ext_info.rs_name )?; } else { writeln!(out, "/// Unified event type for the X core protocol")?; } writeln!(out, "#[derive(Debug)]")?; writeln!(out, "pub enum Event {{")?; for event in &self.events { if event.is_xge && self.xcb_mod == "xproto" { // same comment as above continue; } writeln!(out, " {}({}),", event.variant, event.rs_typ)?; } writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl Event {{")?; writeln!(out, " pub fn as_raw(&self) -> *mut xcb_generic_event_t {{")?; writeln!(out, " match self {{")?; for event in &self.events { if event.is_xge && self.xcb_mod == "xproto" { // same comment as above continue; } if event.is_xge { // We can't emit the different type, but it is "OK" to cast // it as xcb_generic_event_t as the response_type can be // used to distinguish the correct type writeln!( out, " Self::{}(e) => e.as_raw() as *mut xcb_generic_event_t,", event.variant )?; } else { writeln!(out, " Self::{}(e) => e.as_raw(),", event.variant)?; } } writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; let has_xge = self.events.iter().any(|ev| ev.is_xge); let has_non_xge = !self.events.iter().all(|ev| ev.is_xge); let _last_event = self.events.iter().map(|ev| ev.number).max().unwrap(); if has_non_xge { self.emit_resolve_wire_event(out)?; } if has_xge && self.xcb_mod != "xproto" { // xproto GeGeneric is not to be considered as it refers to // an extension generic event self.emit_resolve_wire_ge_event(out)?; } Ok(()) } fn emit_event_new(&self, out: &mut O, event: &Event) -> io::Result<()> { // only fixed size events, with size <= 32 assert!( matches!(event.wire_sz, Expr::Value(sz) if sz <= 32), "{:#?}", event ); let need_event_base = self.xcb_mod != "xproto"; let fn_decl = if need_event_base { "new(event_base: u8," } else { "new(" }; writeln!(out, " pub fn {}", fn_decl)?; // emit parameters for f in &event.fields { match f { Field::Field { name, .. } if name == "response_type" => {} Field::Field { name, .. } if name == "sequence" => {} Field::Field { name, .. } if name == "format" => {} Field::Field { name, .. } if name == "sub_code" => {} Field::Field { name, module, rs_typ, r#enum, mask, struct_style: None | Some(StructStyle::FixBuf | StructStyle::WireLayout), .. } => { let q_rs_typ = enum_mask_qualified_rs_typ(module, rs_typ, r#enum, mask); writeln!(out, " {}: {},", name, q_rs_typ)?; } Field::List { name, module, rs_typ, len_expr: Expr::Value(len), struct_style: None | Some(StructStyle::FixBuf | StructStyle::WireLayout), .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!(out, " {}: [{}; {}],", name, q_rs_typ, len)?; } Field::List { name, module, rs_typ, struct_style: None | Some(StructStyle::FixBuf | StructStyle::WireLayout), .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!(out, " {}: &[{}],", name, q_rs_typ)?; } Field::Pad { .. } => {} f => unreachable!("{:#?}", f), } } writeln!(out, " ) -> {} {{", event.rs_typ)?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!(out, "{}let ptr = libc::malloc(32) as *mut u8;", cg::ind(3),)?; writeln!( out, "{}let wire_buf = std::slice::from_raw_parts_mut(ptr, 32);", cg::ind(3) )?; writeln!(out, "{}let mut wire_off = 0usize;", cg::ind(3))?; if need_event_base { let expr = if event.number == 0 { "event_base".to_string() } else { format!("{}u8 + event_base", event.number) }; writeln!(out, "{}let response_type = {};", cg::ind(3), expr)?; } else { writeln!(out, "{}let response_type = {}u8;", cg::ind(3), event.number)?; } writeln!(out, "{}let sequence = 0u16;", cg::ind(3))?; if event.rs_typ == "ClientMessageEvent" { writeln!(out, "{}let format: u8 = match data {{", cg::ind(3))?; for format in [8, 16, 32] { writeln!( out, "{}ClientMessageData::Data{}{{..}} => {},", cg::ind(4), format, format )?; } writeln!(out, "{}}};", cg::ind(3))?; } if self.xcb_mod == "randr" && event.rs_typ == "NotifyEvent" { writeln!(out, "{}let sub_code = match u {{", cg::ind(3))?; for code in RANDR_SUBCODES { writeln!( out, "{}NotifyData::{}{{..}} => Notify::{},", cg::ind(4), code.2, code.0 )?; } writeln!(out, "{}}};", cg::ind(3))?; } writeln!(out)?; // emit serialization // for response_type we write the event number regardless of first_event // is it important? let last_is_pad = matches!(event.fields.last().unwrap(), Field::Pad { .. }); let assignment_limit = if last_is_pad { event.fields.len() - 2 } else { event.fields.len() - 1 }; for (i, f) in event.fields.iter().enumerate() { let assignment = if i < assignment_limit { "wire_off += " } else { "" }; match f { Field::Field { name, rs_typ, r#enum: Some(_), .. } => { writeln!(out, "{}{}(std::mem::transmute::<_, u32>({}) as {}).serialize(&mut wire_buf[wire_off ..]);", cg::ind(3), assignment, name, rs_typ)?; } Field::Field { name, rs_typ, mask: Some(_), .. } => { writeln!( out, "{}{}({}.bits() as {}).serialize(&mut wire_buf[wire_off ..]);", cg::ind(3), assignment, name, rs_typ )?; } Field::Field { name, rs_typ, wire_sz, struct_style: None | Some(StructStyle::FixBuf | StructStyle::WireLayout), .. } => { if rs_typ == "bool" { if let Expr::Value(sz) = wire_sz { writeln!( out, "{}let {}: u{} = if {} {{ 1 }} else {{ 0 }};", cg::ind(3), name, sz * 8, name, )?; } } writeln!( out, "{}{}{}.serialize(&mut wire_buf[wire_off ..]);", cg::ind(3), assignment, name )?; } Field::List { name, module, rs_typ, len_expr, wire_sz, struct_style: None | Some(StructStyle::FixBuf | StructStyle::WireLayout), .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!( out, "{}std::slice::from_raw_parts_mut(ptr.add(wire_off) as *mut {}, {})", cg::ind(3), q_rs_typ, self.build_rs_expr(len_expr, "", "", &[]) )?; writeln!(out, "{} .copy_from_slice(&{});", cg::ind(3), name)?; if i < assignment_limit { writeln!( out, "{}wire_off += {};", cg::ind(3), self.build_rs_expr(wire_sz, "", "", &[]) )?; } } Field::Pad { wire_sz, .. } if i < event.fields.len() - 1 => { writeln!( out, "{}wire_off += {};", cg::ind(3), self.build_rs_expr(wire_sz, "", "", &[]) )?; } Field::Pad { .. } => {} f => unreachable!("{:#?}", f), } } writeln!(out)?; writeln!( out, "{}{}::from_raw(ptr as *mut xcb_generic_event_t)", cg::ind(3), event.rs_typ )?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; Ok(()) } fn emit_resolve_wire_event(&self, out: &mut O) -> io::Result<()> { writeln!(out)?; writeln!(out, "impl base::ResolveWireEvent for Event {{")?; writeln!(out, "{}unsafe fn resolve_wire_event(first_event: u8, raw: *mut xcb_generic_event_t) -> std::option::Option {{", cg::ind(1))?; writeln!(out, "{}debug_assert!(!raw.is_null());", cg::ind(2))?; writeln!( out, "{}let response_type = (*raw).response_type & 0x7F;", cg::ind(2) )?; writeln!( out, "{}debug_assert!(response_type != 0, \"This is not an event but an error!\");", cg::ind(2), )?; writeln!( out, "{}debug_assert!(response_type != XCB_GE_GENERIC, \"This is a GE_GENERIC event!\");", cg::ind(2), )?; if self.xcb_mod == "xkb" { writeln!( out, "{}assert_eq!(response_type, first_event, \"This is not an Xkb event\");", cg::ind(2) )?; writeln!(out, "{}let ptr = raw as *const u8;", cg::ind(2))?; writeln!(out, "{}let xkb_type = *(ptr.add(1));", cg::ind(2))?; writeln!(out, "{}match xkb_type {{", cg::ind(2))?; } else { writeln!(out, "{}match response_type - first_event {{", cg::ind(2))?; } for event in &self.events { if event.is_xge { continue; } writeln!( out, "{}{} => Some(Event::{}({}::from_raw(raw))),", cg::ind(3), event.number, event.variant, event.rs_typ )?; } writeln!(out, "{}_ => None,", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; writeln!(out, "}}")?; Ok(()) } fn emit_resolve_wire_ge_event(&self, out: &mut O) -> io::Result<()> { writeln!(out, "impl base::ResolveWireGeEvent for Event {{")?; writeln!( out, "{}unsafe fn resolve_wire_ge_event(raw: *mut xcb_ge_generic_event_t) -> Self{{", cg::ind(1) )?; writeln!(out, "{}debug_assert!(!raw.is_null());", cg::ind(2))?; writeln!( out, "{}debug_assert!(((*raw).response_type & 0x7F) == XCB_GE_GENERIC);", cg::ind(2) )?; writeln!(out, "{}let event_type = (*raw).event_type;", cg::ind(2))?; writeln!(out, "{}match event_type {{", cg::ind(2))?; for event in &self.events { if !event.is_xge { continue; } writeln!( out, "{}{} => Event::{}({}::from_raw(raw)),", cg::ind(3), event.number, event.variant, event.rs_typ )?; } writeln!( out, "{}_ => panic!(\"Could not resolve GE event for {}: {{}}\", event_type),", cg::ind(3), self.xcb_mod )?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; writeln!(out, "}}")?; Ok(()) } fn emit_wired_impl( &self, out: &mut O, rs_typ: &str, is_xge: bool, raw_typ: &str, ) -> io::Result<()> { writeln!(out)?; writeln!(out, "impl base::WiredOut for {} {{", rs_typ)?; writeln!(out, "{}fn wire_len(&self) -> usize {{", cg::ind(1))?; if is_xge { writeln!(out, "{}32 + 4 * self.length() as usize", cg::ind(2))?; } else { writeln!(out, "{}32usize", cg::ind(2))?; } writeln!(out, "{}}}", cg::ind(1))?; writeln!(out)?; writeln!( out, "{}fn serialize(&self, wire_buf: &mut[u8]) -> usize {{", cg::ind(1) )?; writeln!( out, "{}debug_assert!(wire_buf.len() >= self.wire_len());", cg::ind(2) )?; writeln!( out, "{}let raw_slice = unsafe {{ std::slice::from_raw_parts(self.raw as *const u8, self.wire_len()) }};", cg::ind(2) )?; writeln!( out, "{}wire_buf[0 .. self.wire_len()].copy_from_slice(raw_slice);", cg::ind(2) )?; writeln!(out, "{}self.wire_len()", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::WiredIn for {} {{", rs_typ)?; writeln!(out, " type Params = ();")?; writeln!(out)?; writeln!( out, "{}unsafe fn compute_wire_len({}ptr: *const u8, _params: ()) -> usize {{", cg::ind(1), if is_xge { "" } else { "_" } )?; if is_xge { writeln!( out, "{}32 + 4 * (*(ptr.add(4) as *const u32) as usize)", cg::ind(2) )?; } else { writeln!(out, "{}32", cg::ind(2))?; } writeln!(out, "{}}}", cg::ind(1))?; writeln!(out)?; writeln!( out, "{}unsafe fn unserialize(ptr: *const u8, _params: (), offset: &mut usize) -> Self {{", cg::ind(1) )?; writeln!( out, "{}let sz = Self::compute_wire_len(ptr, ());", cg::ind(2) )?; writeln!(out, "{}*offset += sz;", cg::ind(2))?; writeln!( out, "{}let raw = libc::malloc(sz) as *mut {};", cg::ind(2), raw_typ )?; writeln!( out, "{}std::ptr::copy(ptr as *const {}, raw, sz);", cg::ind(2), raw_typ )?; writeln!(out, "{}{} {{ raw }}", cg::ind(2), rs_typ)?; writeln!(out, "{}}}", cg::ind(1))?; writeln!(out, "}}")?; Ok(()) } } xcb-1.2.2/build/cg/expr.rs000064400000000000000000000341631046102023000134250ustar 00000000000000use super::r#enum::{map_enum_item_name, map_mask_item_name}; use super::TypeInfo; use crate::cg::{self, CodeGen, Field, QualifiedRsTyp}; use crate::ir; // use std::io::{self, Write}; #[derive(Debug, Clone)] pub enum Expr { FieldRef(String), ParamRef(String), EnumRef { module: Option, rs_typ: String, item: String, }, MaskRef { module: Option, rs_typ: String, item: String, }, Value(usize), Op(String, Box, Box), Unop(String, Box), Popcount(Box), SumOf(String, Option>), ListElementRef, AlignPad(usize, Box), UntilEnd, Unknown(String), } impl CodeGen { pub(super) fn resolve_expr(&self, expr: &ir::Expr) -> Expr { match expr { ir::Expr::Value(size) => Expr::Value(*size), ir::Expr::FieldRef(name) => Expr::FieldRef(cg::rust_field_name(name)), ir::Expr::ParamRef(name) => Expr::ParamRef(cg::rust_field_name(name)), ir::Expr::EnumRef { name, item } => { let typinfo = self.find_typinfo(None, name); match typinfo { TypeInfo::Enum { module, rs_typ, .. } => Expr::EnumRef { module: module.clone(), rs_typ: rs_typ.clone(), item: map_enum_item_name(item), }, TypeInfo::Mask { module, rs_typ, .. } => Expr::MaskRef { module: module.clone(), rs_typ: rs_typ.clone(), item: map_mask_item_name(item), }, _ => unreachable!(), } } ir::Expr::SumOf(name, expr) => Expr::SumOf( cg::rust_field_name(name), expr.as_ref().map(|e| Box::new(self.resolve_expr(e))), ), ir::Expr::Popcount(expr) => Expr::Popcount(Box::new(self.resolve_expr(expr))), ir::Expr::Op(op, lhs, rhs) => Expr::Op( op.clone(), Box::new(self.resolve_expr(lhs)), Box::new(self.resolve_expr(rhs)), ), ir::Expr::Unop(op, rhs) if op == "~" => { Expr::Unop("!".to_string(), Box::new(self.resolve_expr(rhs))) } ir::Expr::Unop(op, rhs) => Expr::Unop(op.clone(), Box::new(self.resolve_expr(rhs))), ir::Expr::ListElementRef => Expr::ListElementRef, } } /// Build a Rust expression. The type of the expr is always usize. pub(super) fn build_rs_expr( &self, expr: &Expr, acc_pref: &str, acc_post: &str, fields: &[Field], ) -> String { match expr { Expr::Value(val) => format!("{}usize", val), Expr::FieldRef(name) => { let is_mask = fields .iter() .find_map(|f| match f { Field::Field { name: n, is_mask, .. } if n == name => Some(*is_mask), _ => None, }) .unwrap_or(false); format!( "({}{}{}{} as usize)", acc_pref, name, acc_post, if is_mask { ".bits()" } else { "" } ) } Expr::ParamRef(name) => name.clone(), Expr::Op(op, lhs, rhs) => { format!( "({} {} {})", self.build_rs_expr(lhs, acc_pref, acc_post, fields), op, self.build_rs_expr(rhs, acc_pref, acc_post, fields), ) } Expr::Unop(op, rhs) => { format!( "({}{})", op, self.build_rs_expr(rhs, acc_pref, acc_post, fields) ) } Expr::Popcount(expr) => { format!( "({}.count_ones() as usize)", self.build_rs_expr(expr, acc_pref, acc_post, fields) ) } Expr::EnumRef { module, rs_typ, item, } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); format!("({}::{} as usize)", q_rs_typ, item) } Expr::MaskRef { module, rs_typ, item, } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); format!("({}::{}.bits() as usize)", q_rs_typ, item) } Expr::AlignPad(sz, expr) => { format!( "base::align_pad({}, {})", self.build_rs_expr(expr, acc_pref, acc_post, fields), sz ) } Expr::SumOf(name, None) => { format!( "({}{}{}.iter().sum::() as usize)", acc_pref, name, acc_post ) } Expr::SumOf(name, Some(fr)) => match &**fr { Expr::FieldRef(fname) => { format!( "({}{}{}.iter().map(|el| el.{}()).sum::() as usize)", acc_pref, name, acc_post, fname ) } Expr::Popcount(expr) => { if matches!(&**expr, Expr::ListElementRef) { // count_ones returns u32 for all implementations format!( "({}{}{}.iter().map(|el| el.count_ones()).sum::() as usize)", acc_pref, name, acc_post ) } else { unreachable!("{:#?}", expr); } } _ => unreachable!("{:#?}", fr), }, Expr::UntilEnd => unreachable!("UntilEnd must be handled up-front"), Expr::Unknown(tag) => { format!("(unimplemented!(\"{} expressions\") as usize)", tag) } ex => { format!("(unimplemented!(\"{:?} expressions\") as usize)", ex) } } } } impl Expr { pub(super) fn fixed_length(&self) -> Option { match self { Expr::EnumRef { .. } => None, // FIXME: get the value of the enum item Expr::Value(val) => Some(*val), Expr::Popcount(ex) => ex.fixed_length().map(|sz| sz.count_ones() as _), Expr::Op(op, lhs, rhs) => match (lhs.fixed_length(), rhs.fixed_length()) { (Some(lhs), Some(rhs)) => match op.as_str() { "+" => Some(lhs + rhs), "-" => Some(lhs - rhs), "*" => Some(lhs * rhs), "/" => Some(lhs / rhs), _ => panic!("Unexpected binary operator in Expr: {}", op), }, _ => None, }, Expr::Unop(op, val) => val.fixed_length().map(|val| match op.as_str() { "!" => !val, _ => panic!("Unexpected unary operator in Expr: {}", op), }), Expr::AlignPad(align, expr) => expr.fixed_length().map(|val| align_pad(val, *align)), _ => None, } } // pub(super) fn len_field(&self) -> Option<&str> { // match self { // Expr::FieldRef(name) => Some(name), // Expr::Op(_, lhs, rhs) => lhs.len_field().or_else(|| rhs.len_field()), // Expr::Unop(_, rhs) => rhs.len_field(), // Expr::Popcount(e) => e.len_field(), // _ => None, // } // } // pub(super) fn depth(&self) -> u32 { // match self { // Expr::Value(_) => 0, // Expr::FieldRef(_) => 1, // Expr::ParamRef(_) => 1, // Expr::Op(_, lhs, rhs) => // lhs.depth().max(rhs.depth()) + 1, // Expr::Unop(_, rhs) => // rhs.depth() + 1, // Expr::Popcount(expr) => expr.depth() + 1, // Expr::SumOf(_, expr) => expr.as_ref().map(|e| e.depth()).unwrap_or(0) + 1, // Expr::EnumRef{..} => 1, // Expr::ListElementRef => 1, // Expr::AlignUp(_, expr) => expr.depth() + 1, // Expr::Unknown(_) => 1, // } // } pub(super) fn reduce(self) -> Expr { match self { Expr::Value(..) => self, expr => { if let Some(len) = expr.fixed_length() { Expr::Value(len) } else if let Expr::Op(op, lhs, rhs) = expr { match (op.as_str(), lhs.fixed_length(), rhs.fixed_length()) { ("*", Some(lhs), _) if lhs == 1 => *rhs, ("*", _, Some(rhs)) if rhs == 1 => *lhs, ("+", Some(lhs), _) if lhs == 0 => *rhs, ("+", _, Some(rhs)) if rhs == 0 => *lhs, ("/", _, Some(rhs)) if rhs == 1 => *lhs, ("-", _, Some(rhs)) if rhs == 0 => *lhs, _ => Expr::Op(op, lhs, rhs), } } else { expr } } } } // pub(super) fn has_fieldref(&self, fieldref: &str) -> bool { // match self { // Expr::FieldRef(name) => name == fieldref, // Expr::Op(_, lhs, rhs) => lhs.has_fieldref(fieldref) || rhs.has_fieldref(fieldref), // Expr::Unop(_, rhs) => rhs.has_fieldref(fieldref), // Expr::SumOf(name, _) => name == fieldref, // Expr::Popcount(expr) => expr.has_fieldref(fieldref), // Expr::AlignUp(_, expr) => expr.has_fieldref(fieldref), // _ => false, // } // } pub(super) fn fetch_fieldrefs<'a>(&'a self, fieldrefs: &mut Vec<&'a str>) { match self { Expr::FieldRef(name) => { fieldrefs.push(name.as_str()); } Expr::Op(_, lhs, rhs) => { lhs.fetch_fieldrefs(fieldrefs); rhs.fetch_fieldrefs(fieldrefs); } Expr::Unop(_, rhs) => { rhs.fetch_fieldrefs(fieldrefs); } Expr::SumOf(name, _) => { fieldrefs.push(name.as_str()); } Expr::Popcount(expr) => { expr.fetch_fieldrefs(fieldrefs); } Expr::AlignPad(_, expr) => { expr.fetch_fieldrefs(fieldrefs); } _ => {} } } pub(super) fn fetch_fieldrefs_owned(&self, fieldrefs: &mut Vec) { match self { Expr::FieldRef(name) => { fieldrefs.push(name.clone()); } Expr::Op(_, lhs, rhs) => { lhs.fetch_fieldrefs_owned(fieldrefs); rhs.fetch_fieldrefs_owned(fieldrefs); } Expr::Unop(_, rhs) => { rhs.fetch_fieldrefs_owned(fieldrefs); } Expr::SumOf(name, _) => { fieldrefs.push(name.clone()); } Expr::Popcount(expr) => { expr.fetch_fieldrefs_owned(fieldrefs); } Expr::AlignPad(_, expr) => { expr.fetch_fieldrefs_owned(fieldrefs); } _ => {} } } pub(crate) fn fieldrefs(&self) -> Vec<&str> { let mut fieldrefs = Vec::new(); self.fetch_fieldrefs(&mut fieldrefs); fieldrefs } // pub(crate) fn fieldrefs_str(&self) -> String { // let fields = self.fieldrefs(); // let mut field_str = String::new(); // for f in fields { // field_str.push_str(", "); // field_str.push_str(f); // field_str.push_str(": usize"); // } // field_str // } fn fetch_params<'a>(&'a self, params: &mut Vec<&'a str>) { match self { Expr::ParamRef(name) => { params.push(name.as_str()); } Expr::Op(_, lhs, rhs) => { lhs.fetch_params(params); rhs.fetch_params(params); } Expr::Unop(_, rhs) => { rhs.fetch_params(params); } Expr::SumOf(_, Some(expr)) => { expr.fetch_params(params); } Expr::Popcount(expr) => { expr.fetch_params(params); } Expr::AlignPad(_, expr) => { expr.fetch_params(params); } _ => {} } } pub(super) fn fetch_paramrefs_owned(&self, params: &mut Vec) { match self { Expr::ParamRef(name) => { params.push(name.clone()); } Expr::Op(_, lhs, rhs) => { lhs.fetch_paramrefs_owned(params); rhs.fetch_paramrefs_owned(params); } Expr::Unop(_, rhs) => { rhs.fetch_paramrefs_owned(params); } Expr::SumOf(_, Some(expr)) => { expr.fetch_paramrefs_owned(params); } Expr::Popcount(expr) => { expr.fetch_paramrefs_owned(params); } Expr::AlignPad(_, expr) => { expr.fetch_paramrefs_owned(params); } _ => {} } } pub(crate) fn params(&self) -> Vec<&str> { let mut params = Vec::new(); self.fetch_params(&mut params); params } pub(crate) fn params_str(&self) -> String { let params = self.params(); let mut param_str = String::new(); for p in params { param_str.push_str(", "); param_str.push_str(p); param_str.push_str(": usize"); } param_str } } fn align_pad(base: usize, align: usize) -> usize { debug_assert!(align.is_power_of_two(), "`align` must be a power of two"); let base = base as isize; let align = align as isize; (-base & (align - 1)) as usize } xcb-1.2.2/build/cg/mod.rs000064400000000000000000001063151046102023000132250ustar 00000000000000use crate::ir; use std::collections::HashMap; use std::io::{self, Write}; use self::util::extract_module; mod doc; mod r#enum; mod error; mod event; mod expr; mod request; mod r#struct; mod switch; mod union; mod util; mod xid; use doc::{Doc, DocField}; use expr::Expr; use r#struct::ParamsStruct; /// Information about a type /// for each variant /// - module is the module where the type is defined (None for current module) /// - rs_typ is the rust traduction of the type #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug)] enum TypeInfo { /// simple copyable types such as u32 Simple { rs_typ: String, wire_sz: usize, has_wire_layout: bool, }, Typedef { module: Option, rs_typ: String, old_module: Option, old_typ: String, wire_sz: Expr, has_wire_layout: bool, }, Xid { module: Option, rs_typ: String, }, XidUnion { module: Option, rs_typ: String, variants: Vec, }, Enum { module: Option, rs_typ: String, items: Vec<(String, u32, Option)>, altenum_typ: Option<(Option, String)>, doc: Option, }, Mask { module: Option, rs_typ: String, items: Vec<(String, u32, Option)>, doc: Option, }, Struct { module: Option, rs_typ: String, fields: Vec, wire_sz: Expr, has_wire_layout: bool, params_struct: Option, doc: Option, }, Union { module: Option, rs_typ: String, variants: Vec, wire_sz: Expr, type_field: Option, impl_clone: bool, emit: bool, }, Switch { module: Option, rs_typ: String, expr: Expr, cases: Vec, maskenum: (Option, String), params_struct: ParamsStruct, wire_sz: Expr, is_mask: bool, emit: bool, }, } #[derive(Clone, Debug)] struct SwitchCase { name: String, exprs: Vec, fields: Vec, } #[allow(clippy::enum_variant_names)] #[derive(Clone, Debug)] enum Field { Field { name: String, module: Option, rs_typ: String, wire_off: Expr, wire_sz: Expr, struct_style: Option, params_struct: Option, r#enum: Option<(Option, String)>, mask: Option<(Option, String)>, doc: Option, is_fieldref: bool, is_paramref: bool, is_copy: bool, is_union: bool, is_xid: bool, is_mask: bool, need_compute_offset: bool, is_prop_format: bool, // format field for a property }, List { name: String, module: Option, rs_typ: String, wire_off: Expr, wire_sz: Expr, len_expr: Expr, struct_style: Option, params_struct: Option, r#enum: Option<(Option, String)>, mask: Option<(Option, String)>, doc: Option, is_fieldref: bool, need_compute_offset: bool, is_prop: bool, // property field (resolved with type `void` and a format field is present) is_union: bool, union_typefield: Option, }, Switch { name: String, module: Option, params_struct: ParamsStruct, rs_typ: String, expr: Expr, wire_off: Expr, wire_sz: Expr, doc: Option, is_mask: bool, need_compute_offset: bool, }, Expr { name: String, typ: String, wire_off: Expr, wire_sz: Expr, expr: Expr, }, Pad { wire_off: Expr, wire_sz: Expr, }, AlignPad { wire_off: Expr, wire_sz: Expr, }, } #[derive(Copy, Clone, Debug)] enum StructStyle { WireLayout, FixBuf, DynBuf, } #[derive(Clone, Debug)] struct UnionVariant { variant: String, module: Option, content: UnionVariantContent, } #[derive(Clone, Debug)] struct UnionTypeField { offset: usize, enu_module: Option, enu_typ: String, } #[derive(Clone, Debug)] enum UnionVariantContent { RsTyp(String), Array(String, usize), Struct(Vec), } #[derive(Clone, Debug)] struct Error { rs_typ: String, copy_from_rs_typ: Option, variant: String, number: i32, fields: Vec, wire_sz: Expr, } #[derive(Clone, Debug)] struct Reply { fields: Vec, doc: Option, } #[derive(Clone, Debug)] struct Request { rs_typ: String, opcode: u32, params: Vec, reply: Option, sends_event: bool, doc: Option, } #[derive(Clone, Debug)] struct Event { rs_typ: String, variant: String, number: i32, fields: Vec, copy_from_rs_typ: Option, wire_sz: Expr, is_xge: bool, doc: Option, } #[derive(Debug, Clone)] struct ExtInfo { pub rs_name: String, pub name: String, pub xname: String, pub major_version: u32, pub minor_version: u32, } #[derive(Clone, Debug)] pub struct DepInfo { pub xcb_mod: String, pub deps: Vec, typinfos: HashMap, errors: Vec, events: Vec, } #[derive(Debug)] pub struct CodeGen { xcb_mod: String, ext_info: Option, depinfo: Vec, typinfos: HashMap, typs: Vec, rs_typs: HashMap, // struct and typedefs that are only used in unions are defined as variants and removed everywhere else // we keep track of whether e.g. struct or typedefs must be emitted through the need_count rs_typs_need_count: HashMap, errors: Vec, errors_preregistered: bool, requests: Vec, events: Vec, mask_exceptions: Vec, switch_exceptions: Vec, dbg_atom_names: bool, } impl CodeGen { pub fn new( xcb_mod: String, ext_info: &Option, depinfo: Vec, dbg_atom_names: bool, ) -> CodeGen { let simples = &[ ("CARD8", "u8", 1, true), ("CARD16", "u16", 2, true), ("CARD32", "u32", 4, true), ("CARD64", "u64", 8, true), ("INT8", "i8", 1, true), ("INT16", "i16", 2, true), ("INT32", "i32", 4, true), ("BYTE", "u8", 1, true), ("BOOL", "bool", 1, false), ("BOOL32", "bool", 4, false), ("char", "char", 1, false), ("float", "f32", 4, true), ("double", "f64", 8, true), ("FLOAT32", "f32", 4, true), ("FLOAT64", "f64", 8, true), ("void", "u8", 1, true), // void lists are blob of bytes ("fd", "RawFd", 4, true), ]; let typinfos = simples .iter() .map(|(typ, rs_typ, wire_sz, has_wire_layout)| { ( typ.to_string(), TypeInfo::Simple { rs_typ: rs_typ.to_string(), wire_sz: *wire_sz, has_wire_layout: *has_wire_layout, }, ) }) .collect(); let ext_info = ext_info.as_ref().map(|ei| ExtInfo { rs_name: rust_variant_name(&ei.name), name: ei.name.clone(), xname: ei.xname.clone(), major_version: ei.major_version, minor_version: ei.minor_version, }); CodeGen { xcb_mod, ext_info, depinfo, typinfos, typs: Vec::new(), rs_typs: HashMap::new(), rs_typs_need_count: HashMap::new(), errors: Vec::new(), errors_preregistered: false, requests: Vec::new(), events: Vec::new(), mask_exceptions: mask_exceptions(), switch_exceptions: switch_exceptions(), dbg_atom_names, } } pub fn into_depinfo(self) -> DepInfo { let CodeGen { xcb_mod, depinfo, mut typinfos, errors, events, .. } = self; let deps = depinfo.into_iter().map(|di| di.xcb_mod).collect(); for (_, typinfo) in typinfos.iter_mut() { match typinfo { TypeInfo::Typedef { module, .. } | TypeInfo::Xid { module, .. } | TypeInfo::XidUnion { module, .. } | TypeInfo::Enum { module, .. } | TypeInfo::Mask { module, .. } | TypeInfo::Struct { module, .. } | TypeInfo::Union { module, .. } => *module = Some(xcb_mod.clone()), _ => {} } } DepInfo { xcb_mod, deps, typinfos, errors, events, } } pub fn preregister_item(&mut self, item: &ir::Item) { match item { ir::Item::XidType { typ } | ir::Item::XidUnion { typ, .. } | ir::Item::Enum { typ, .. } => { let rs_typ = rust_type_name(typ); self.preregister_typ(rs_typ); } ir::Item::Error { .. } | ir::Item::ErrorCopy { .. } => { if !self.errors_preregistered { self.preregister_typ("Error".into()); self.errors_preregistered = true; } } _ => {} } } pub fn resolve_type(&mut self, item: &ir::Item) { match item { ir::Item::Typedef { old_typ, new_typ } => { self.resolve_typedef(old_typ, new_typ); } ir::Item::XidType { typ } => { self.resolve_xid(typ); } ir::Item::XidUnion { typ, xidtypes } => { self.resolve_xidunion(typ, xidtypes); } ir::Item::Enum { typ, items, doc } => { self.resolve_enum(typ, items, doc); } ir::Item::Struct { typ, fields, doc } => { self.resolve_struct(typ, fields, doc); } ir::Item::Union { typ, fields, doc } => { self.resolve_union(typ, fields, doc); } _ => {} } } pub fn resolve_error_event_request(&mut self, item: ir::Item) { match item { ir::Item::Error { name, number, fields, doc, } => self.resolve_error(name, number, fields, doc), ir::Item::ErrorCopy { name, number, r#ref, } => self.resolve_errorcopy(name, number, r#ref), ir::Item::Request { name, opcode, params, reply, doc, } => self.resolve_request(name, opcode, params, reply, doc), ir::Item::Event { number, name, fields, xge, no_seq_number, doc, } => self.resolve_event(name, number, fields, xge, no_seq_number, doc), ir::Item::EventCopy { name, number, r#ref, } => self.resolve_eventcopy(name, number, r#ref), ir::Item::EventStruct { typ, selectors } => { assert!(!selectors.is_empty()); self.resolve_event_struct(typ, selectors); } _ => {} } } pub fn emit_prologue(&self, out: &mut O) -> io::Result<()> { writeln!( out, "// This source file is generated automatically from {}.xml", self.xcb_mod )?; writeln!(out)?; writeln!( out, "use crate::base::{{self, BaseError, BaseEvent, GeEvent, Raw, Reply, WiredIn, WiredOut, Xid}};" )?; writeln!(out, "use crate::ext;")?; writeln!(out, "use crate::ffi::base::*;")?; writeln!(out, "use crate::ffi::ext::*;")?; writeln!( out, "use crate::lat1_str::{{Lat1Str, Lat1String, Lat1StrF}};" )?; for di in &self.depinfo { writeln!(out, "use crate::{};", di.xcb_mod)?; } if self.ext_info.is_some() { writeln!(out, "use crate::xproto::PropEl;")?; } writeln!(out)?; writeln!(out, "use bitflags::bitflags;")?; writeln!(out, "use libc::{{self, iovec}};")?; writeln!(out, "use std::convert::TryInto;")?; writeln!(out, "use std::hash::{{Hash, Hasher}};")?; writeln!(out, "use std::os::unix::io::RawFd;")?; if self.xcb_mod == "xproto" { writeln!(out, "use std::cmp::Ordering;")?; } if let Some(ext_info) = self.ext_info.as_ref() { writeln!(out)?; writeln!( out, "/// The official identifier for the `{}` extension.", ext_info.name )?; writeln!(out, "pub const XNAME: &str = \"{}\";", ext_info.xname)?; writeln!( out, "/// The major version of the `{}` extension.", ext_info.name )?; writeln!( out, "pub const MAJOR_VERSION: u32 = {};", ext_info.major_version )?; writeln!( out, "/// The minor version of the `{}` extension.", ext_info.name )?; writeln!( out, "pub const MINOR_VERSION: u32 = {};", ext_info.minor_version )?; writeln!( out, "/// The version string of the `{}` extension.", ext_info.name )?; writeln!( out, "pub const VERSION_STRING: &str = \"{}.{}\";", ext_info.major_version, ext_info.minor_version )?; writeln!(out)?; writeln!( out, "pub(crate) static mut FFI_EXT: xcb_extension_t = xcb_extension_t {{" )?; writeln!( out, " name: \"{}\\0\".as_ptr() as *const _,", ext_info.xname )?; writeln!(out, " global_id: 0,")?; writeln!(out, "}};")?; writeln!(out)?; writeln!( out, "/// Prefetch server runtime info data of the `{}` extension.", ext_info.name )?; writeln!( out, "pub fn prefetch_extension_data(conn: &base::Connection) {{" )?; writeln!(out, " unsafe {{")?; writeln!( out, " xcb_prefetch_extension_data(conn.get_raw_conn(), &mut FFI_EXT as *mut _);" )?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!( out, "/// Fetch server runtime info data of the `{}` extension.", ext_info.name )?; writeln!(out, "///")?; writeln!( out, "/// Might be non-blocking if [prefetch_extension_data] was called before." )?; writeln!( out, "/// This function is of seldom use as the extensions are initialized by the" )?; writeln!(out, "/// [Connection](crate::Connection) constructor.")?; writeln!( out, "pub fn get_extension_data(conn: &base::Connection) -> std::option::Option {{" )?; writeln!(out, " unsafe {{")?; writeln!( out, " let reply = xcb_get_extension_data(conn.get_raw_conn(), &mut FFI_EXT as *mut _);" )?; writeln!( out, " assert!(!reply.is_null(), \"Could not fetch {} extension data\");", ext_info.name )?; writeln!( out, " let reply = xproto::QueryExtensionReply::from_raw(reply);" )?; writeln!(out, " if !reply.present() {{")?; writeln!(out, " std::mem::forget(reply);")?; writeln!(out, " return None;")?; writeln!(out, " }}")?; writeln!(out, " let res = ext::ExtensionData{{")?; writeln!( out, " ext: ext::Extension::{},", ext_info.rs_name )?; writeln!(out, " major_opcode: reply.major_opcode(),")?; writeln!(out, " first_event: reply.first_event(),")?; writeln!(out, " first_error: reply.first_error(),")?; writeln!(out, " }};")?; writeln!(out, " std::mem::forget(reply);")?; writeln!(out, " Some(res)")?; writeln!(out, " }}")?; writeln!(out, "}}")?; } Ok(()) } pub fn emit_types(&self, out: &mut O) -> io::Result<()> { for typ in &self.typs { let typinfo = &self.typinfos[typ]; match typinfo { TypeInfo::Typedef { rs_typ, old_typ, .. } => { self.emit_typedef(out, rs_typ, old_typ)?; } TypeInfo::Xid { rs_typ, .. } => { self.emit_xid(out, rs_typ)?; } TypeInfo::XidUnion { rs_typ, variants, .. } => { self.emit_xidunion(out, rs_typ, variants)?; } TypeInfo::Enum { rs_typ, items, altenum_typ, doc, .. } => { self.emit_enum(out, rs_typ, items, altenum_typ, doc.as_ref())?; } TypeInfo::Mask { rs_typ, items, doc, .. } => { self.emit_mask(out, rs_typ, items, doc.as_ref())?; } TypeInfo::Struct { rs_typ, fields, wire_sz, has_wire_layout, params_struct, doc, .. } => { self.emit_struct( out, rs_typ, fields, wire_sz, *has_wire_layout, params_struct.as_ref(), doc.as_ref(), )?; } TypeInfo::Union { rs_typ, variants, wire_sz, type_field, impl_clone, emit: true, .. } => { self.emit_union(out, rs_typ, variants, wire_sz, type_field, *impl_clone)?; } TypeInfo::Switch { rs_typ, expr, cases, maskenum, emit: true, is_mask, params_struct, .. } => { self.emit_switch(out, rs_typ, expr, cases, maskenum, params_struct, *is_mask)?; } _ => {} } } Ok(()) } fn resolve_typedef(&mut self, old_typ: &str, new_typ: &str) { match new_typ { // this one handled has built-in simple type to get a bool interface "BOOL32" // these two ones are unnecessary in Rust | "FLOAT32" | "FLOAT64" => { return; } _ => {} } let old_typ = match new_typ { "STRING8" => "CARD8", _ => old_typ, }; if self.xcb_mod == "xinput" && new_typ == "DeviceId" { self.handle_xinput_deviceid_typedef(); return; } let rs_typ = rust_type_name(new_typ); let (old_module, old_typ) = extract_module(old_typ); let old_typinfo = self.find_typinfo(old_module, old_typ); let wire_sz = old_typinfo.wire_sz(); let has_wire_layout = old_typinfo.has_wire_layout(); let typinfo = TypeInfo::Typedef { module: None, // this the where typedef is defined, not where old_typ is defined rs_typ, old_module: old_module.map(str::to_owned), old_typ: old_typ.to_string(), wire_sz, has_wire_layout, }; self.register_typ(new_typ.to_string(), typinfo); } fn emit_typedef(&self, out: &mut O, rs_typ: &str, old_typ: &str) -> io::Result<()> { if !self.rs_typ_is_needed(rs_typ) { return Ok(()); } let old_rs_typ = self.typinfos[old_typ].rs_typ(); writeln!(out)?; writeln!(out, "pub type {} = {};", rs_typ, old_rs_typ)?; Ok(()) } fn emit_sizeof_test(&self, out: &mut O, rs_typ: &str, sz: usize) -> io::Result<()> { writeln!(out)?; writeln!(out, "#[test]")?; writeln!( out, "fn test_sizeof_{}() {{", util::tit_split(rs_typ).to_lowercase() )?; writeln!( out, " assert_eq!(std::mem::size_of::<{}>(), {});", rs_typ, sz )?; writeln!(out, "}}")?; Ok(()) } fn extract_module<'a>(&self, typ: &'a str) -> (Option<&'a str>, &'a str) { let (mut module, typ) = util::extract_module(typ); if let Some(m) = module { if m == self.xcb_mod { module = None; } } (module, typ) } fn preregister_typ(&mut self, rs_typ: String) { if let Some(cnt) = self.rs_typs.get_mut(&rs_typ) { *cnt += 1; } else { self.rs_typs.insert(rs_typ, 1); } } fn register_typ(&mut self, typ: String, info: TypeInfo) { self.rs_typs_need_count.insert(info.rs_typ().to_string(), 0); self.typinfos.insert(typ.clone(), info); self.typs.push(typ.clone()); // we insert 0 and use the function to handle typedefs self.typ_need_add(&typ, 1); } fn typ_need_add(&mut self, typ: &str, val: i32) { let typinfo = &self.typinfos[typ]; let rs_typ = typinfo.rs_typ(); if let Some(count) = self.rs_typs_need_count.get_mut(rs_typ) { *count += val; if let Some((None, old_typ)) = typinfo.typedef_old_mod_typ() { let old_typ = old_typ.to_string(); self.typ_need_add(&old_typ, val); } } } fn rs_typ_is_needed(&self, rs_typ: &str) -> bool { if let Some(count) = self.rs_typs_need_count.get(rs_typ) { *count > 0 } else { false } } fn register_altenum_typ( &mut self, enum_typ: &str, module: Option<&str>, new_altenum_typ: &str, ) { let typinfo = self.find_typinfo_mut(None, enum_typ); if let Some(TypeInfo::Enum { altenum_typ, .. }) = typinfo { *altenum_typ = Some((module.map(str::to_owned), new_altenum_typ.into())) } } fn get_depinfo(&self, module: &str) -> &DepInfo { self.depinfo .iter() .find(|&di| di.xcb_mod == module) .unwrap_or_else(|| panic!("cannot find dependency module {}", module)) } fn find_typinfo(&self, module: Option<&str>, typ: &str) -> &TypeInfo { if let Some(module) = module { if module == self.xcb_mod { &self.typinfos[typ] } else { &self.get_depinfo(module).typinfos[typ] } } else if let Some(typinfo) = self.typinfos.get(typ) { typinfo } else { self.depinfo .iter() .find_map(|di| di.typinfos.get(typ)) .unwrap_or_else(|| panic!("could not resolve typeinfo {:?}::{}", module, typ)) } } fn find_typinfo_mut(&mut self, module: Option<&str>, typ: &str) -> Option<&mut TypeInfo> { if let Some(module) = module { if module == self.xcb_mod { self.typinfos.get_mut(typ) } else { None } } else { self.typinfos.get_mut(typ) } } /// Same as find_typinfo, but recurse down in case the result is a typedef fn find_typinfo_recurse(&self, module: Option<&str>, typ: &str) -> &TypeInfo { let typinfo = self.find_typinfo(module, typ); match typinfo { TypeInfo::Typedef { old_module, old_typ, .. } => self.find_typinfo_recurse(old_module.as_ref().map(|m| m.as_str()), old_typ), typinfo => typinfo, } } fn handle_xinput_deviceid_typedef(&mut self) { let typinfo = TypeInfo::Union { module: None, rs_typ: "Device".into(), variants: Vec::new(), wire_sz: Expr::Value(2), type_field: None, impl_clone: true, emit: false, }; self.register_typ("DeviceId".into(), typinfo); } fn handle_xinput_device_enum(&self) { // Do not emit anything } } /// Describe the exceptions in switch and associated enum naming #[derive(Copy, Clone, Debug)] struct RsTypException { module: &'static str, /// Computed rs_typ rs_typ: &'static str, /// What is the new name? new_module: Option<&'static str>, /// What is the new name? new_rs_typ: &'static str, /// Do we emit it? emit: bool, } fn switch_exceptions() -> Vec { vec![ RsTypException { module: "xproto", rs_typ: "CreateWindowValueList", new_module: None, new_rs_typ: "Cw", emit: true, }, RsTypException { module: "xproto", rs_typ: "ChangeWindowAttributesValueList", new_module: None, new_rs_typ: "Cw", emit: false, }, RsTypException { module: "xproto", rs_typ: "ConfigureWindowValueList", new_module: None, new_rs_typ: "ConfigWindow", emit: true, }, RsTypException { module: "xproto", rs_typ: "CreateGcValueList", new_module: None, new_rs_typ: "Gc", emit: true, }, RsTypException { module: "xproto", rs_typ: "ChangeGcValueList", new_module: None, new_rs_typ: "Gc", emit: false, }, RsTypException { module: "xproto", rs_typ: "ChangeKeyboardControlValueList", new_module: None, new_rs_typ: "Kb", emit: true, }, RsTypException { module: "screensaver", rs_typ: "SetAttributesValueList", new_module: Some("xproto"), new_rs_typ: "Cw", emit: false, }, RsTypException { module: "render", rs_typ: "CreatePictureValueList", new_module: None, new_rs_typ: "Cp", emit: true, }, RsTypException { module: "render", rs_typ: "ChangePictureValueList", new_module: None, new_rs_typ: "Cp", emit: false, }, RsTypException { module: "sync", rs_typ: "CreateAlarmValueList", new_module: None, new_rs_typ: "Ca", emit: true, }, RsTypException { module: "sync", rs_typ: "ChangeAlarmValueList", new_module: None, new_rs_typ: "Ca", emit: false, }, ] } fn mask_exceptions() -> Vec { vec![ RsTypException { module: "xproto", rs_typ: "Cw", new_module: None, new_rs_typ: "CwMask", emit: true, }, RsTypException { module: "xproto", rs_typ: "ConfigWindow", new_module: None, new_rs_typ: "ConfigWindowMask", emit: true, }, RsTypException { module: "xproto", rs_typ: "Gc", new_module: None, new_rs_typ: "GcMask", emit: true, }, RsTypException { module: "xproto", rs_typ: "Kb", new_module: None, new_rs_typ: "KbMask", emit: true, }, RsTypException { module: "render", rs_typ: "Cp", new_module: None, new_rs_typ: "CpMask", emit: true, }, RsTypException { module: "sync", rs_typ: "Ca", new_module: None, new_rs_typ: "CaMask", emit: true, }, ] } fn ind(level: u32) -> &'static str { match level { 0 => "", 1 => " ", 2 => " ", 3 => " ", 4 => " ", 5 => " ", 6 => " ", _ => unreachable!(), } } fn rust_type_name(typ: &str) -> String { util::tit_cap(typ) } fn rust_variant_name(name: &str) -> String { util::tit_cap(name) } static KEYWORDS: &[&str] = &["type", "match"]; fn rust_field_name(name: &str) -> String { let mut name = util::tit_split(name).to_lowercase(); if KEYWORDS.contains(&name.as_str()) { name.insert_str(0, "r#"); } name } fn get_struct_style(has_wire_layout: bool, wire_sz: &Expr) -> StructStyle { match (has_wire_layout, wire_sz) { (true, Expr::Value(_)) => StructStyle::WireLayout, (false, Expr::Value(_)) => StructStyle::FixBuf, (true, _) => unreachable!(), _ => StructStyle::DynBuf, } } impl TypeInfo { fn module(&self) -> Option<&str> { match self { TypeInfo::Simple { .. } => None, TypeInfo::Typedef { module, .. } | TypeInfo::Xid { module, .. } | TypeInfo::XidUnion { module, .. } | TypeInfo::Enum { module, .. } | TypeInfo::Mask { module, .. } | TypeInfo::Struct { module, .. } | TypeInfo::Union { module, .. } | TypeInfo::Switch { module, .. } => module.as_ref().map(|m| m.as_str()), } } fn typedef_old_mod_typ(&self) -> Option<(Option<&str>, &str)> { match self { TypeInfo::Typedef { old_module, old_typ, .. } => Some((old_module.as_ref().map(|m| m.as_str()), old_typ)), _ => None, } } fn struct_style(&self) -> Option { match self { TypeInfo::Struct { has_wire_layout, wire_sz, .. } => Some(get_struct_style(*has_wire_layout, wire_sz)), _ => None, } } } type ModRsTyp<'a> = (Option<&'a str>, &'a str); trait RsTyp { fn rs_typ(&self) -> &str; } trait QualifiedRsTyp { fn qualified_rs_typ(&self) -> String; } trait AsModRsTyp { fn as_mod_rs_typ(&self) -> ModRsTyp<'_>; } trait WireSz { fn wire_sz(&self) -> Expr; } trait WireOff { fn wire_off(&self) -> Expr; } trait HasWireLayout { fn has_wire_layout(&self) -> bool; } impl RsTyp for TypeInfo { fn rs_typ(&self) -> &str { match self { TypeInfo::Simple { rs_typ, .. } | TypeInfo::Typedef { rs_typ, .. } | TypeInfo::Xid { rs_typ, .. } | TypeInfo::XidUnion { rs_typ, .. } | TypeInfo::Enum { rs_typ, .. } | TypeInfo::Mask { rs_typ, .. } | TypeInfo::Struct { rs_typ, .. } | TypeInfo::Union { rs_typ, .. } | TypeInfo::Switch { rs_typ, .. } => rs_typ, } } } impl RsTyp for ModRsTyp<'_> { fn rs_typ(&self) -> &str { self.1 } } impl QualifiedRsTyp for ModRsTyp<'_> { fn qualified_rs_typ(&self) -> String { match &self.0 { Some(module) => module.to_string() + "::" + self.1, None => self.1.to_string(), } } } impl QualifiedRsTyp for T { fn qualified_rs_typ(&self) -> String { self.as_mod_rs_typ().qualified_rs_typ() } } impl<'a> AsModRsTyp for (&'a Option, &String) { fn as_mod_rs_typ(&self) -> ModRsTyp<'_> { (self.0.as_ref().map(|m| m.as_str()), self.1) } } impl AsModRsTyp for TypeInfo { fn as_mod_rs_typ(&self) -> ModRsTyp<'_> { (self.module(), self.rs_typ()) } } // impl AsModRsTyp for UnionVariant { // fn as_mod_rs_typ(&self) -> ModRsTyp<'_> { // (self.module.as_ref().map(|m| m.as_str()), &self.rs_typ) // } // } impl WireSz for Field { fn wire_sz(&self) -> Expr { match self { Field::Field { wire_sz, .. } | Field::List { wire_sz, .. } | Field::Switch { wire_sz, .. } | Field::Expr { wire_sz, .. } | Field::Pad { wire_sz, .. } | Field::AlignPad { wire_sz, .. } => wire_sz.clone(), } } } impl WireSz for TypeInfo { fn wire_sz(&self) -> Expr { match self { TypeInfo::Simple { wire_sz, .. } => Expr::Value(*wire_sz), TypeInfo::Typedef { wire_sz, .. } | TypeInfo::Struct { wire_sz, .. } | TypeInfo::Union { wire_sz, .. } | TypeInfo::Switch { wire_sz, .. } => wire_sz.clone(), TypeInfo::Xid { .. } | TypeInfo::XidUnion { .. } | TypeInfo::Enum { .. } | TypeInfo::Mask { .. } => Expr::Value(4), } } } impl WireOff for Field { fn wire_off(&self) -> Expr { match self { Field::Field { wire_off, .. } | Field::List { wire_off, .. } | Field::Switch { wire_off, .. } | Field::Expr { wire_off, .. } | Field::Pad { wire_off, .. } | Field::AlignPad { wire_off, .. } => wire_off.clone(), } } } impl HasWireLayout for TypeInfo { fn has_wire_layout(&self) -> bool { match self { TypeInfo::Simple { has_wire_layout, .. } | TypeInfo::Typedef { has_wire_layout, .. } | TypeInfo::Struct { has_wire_layout, .. } => *has_wire_layout, TypeInfo::Xid { .. } => true, _ => false, } } } xcb-1.2.2/build/cg/request.rs000064400000000000000000001377451046102023000141510ustar 00000000000000use super::r#struct::{self, make_field, ResolvedFields}; use super::{CodeGen, Expr, Field, Request}; use crate::cg::{self, Doc, QualifiedRsTyp, Reply, StructStyle}; use crate::ir::{self}; use std::borrow::Cow; use std::io::{self, Write}; impl CodeGen { pub(super) fn resolve_request( &mut self, name: String, opcode: u32, mut params: Vec, reply: Option, doc: Option, ) { // special case for the request that send events let sends_event = matches!( (self.xcb_mod.as_str(), name.as_str()), ("xproto", "SendEvent") | ("xevie", "Send") ); // let event_is_list = sends_event // && params // .iter() // .any(|p| matches!(p, ir::Field::List{name, ..} if name == "Event")); let rs_typ = cg::rust_type_name(&name); let doc = self.resolve_doc(doc); let params = { let mut fields = vec![ make_field("major_opcode".into(), "CARD8".into()), if self.xcb_mod == "xproto" { if params.is_empty() { ir::Field::Pad(1) } else { params.remove(0) } } else { make_field("minor_opcode".into(), "CARD8".into()) }, make_field("length".into(), "CARD16".into()), ]; fields.append(&mut params); fields }; let ResolvedFields { fields: mut params, .. } = self.resolv_struct_fields(&rs_typ, "", ¶ms, doc.as_ref()); let req_rs_typ = &rs_typ; for param in &mut params { if let Field::Field { name, ref mut rs_typ, .. } = param { if self.xcb_mod == "xproto" && req_rs_typ == "SendEvent" && name == "destination" && rs_typ == "Window" { *rs_typ = "SendEventDest".to_string(); } } } let reply = reply.map(|mut r| { let rs_typ = rs_typ.clone() + "Reply"; let doc = self.resolve_doc(r.doc); let mut fields = vec![ make_field("response_type".into(), "CARD8".into()), if r.fields.is_empty() { ir::Field::Pad(1usize) } else { r.fields.remove(0) }, make_field("sequence".into(), "CARD16".into()), make_field("length".into(), "CARD32".into()), ]; fields.append(&mut r.fields); let ResolvedFields { fields, .. } = self.resolv_struct_fields(&rs_typ, "", &fields, doc.as_ref()); Reply { fields, doc } }); self.requests.push(Request { rs_typ, opcode, params, reply, sends_event, doc, // event_is_list, }); } pub fn emit_requests(&self, out: &mut O) -> io::Result<()> { writeln!(out)?; writeln!( out, "pub(crate) fn request_name(opcode: u16) -> std::option::Option<&'static str> {{" )?; writeln!(out, "{}match opcode {{", cg::ind(1))?; let module = if self.ext_info.is_some() { self.xcb_mod.as_str() } else { "x" }; for r in &self.requests { writeln!( out, "{}{} => Some(\"{}::{}\"),", cg::ind(2), r.opcode, module, r.rs_typ )?; } writeln!(out, "{}_ => None,", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; writeln!(out, "}}")?; for r in &self.requests { let Request { rs_typ, opcode, params, reply, sends_event, doc, } = r; let info = self.query_request_info(rs_typ, params, *opcode, reply.as_ref()); if let Some(reply) = reply { self.emit_cookie_reply(out, reply, &info)?; } self.emit_request_struct(out, rs_typ, params, &info, doc.as_ref(), *sends_event)?; self.emit_request_traits(out, rs_typ, params, &info, *sends_event)?; } Ok(()) } fn query_request_info<'a>( &self, rs_typ: &str, params: &'a [Field], opcode: u32, reply: Option<&Reply>, ) -> RequestInfo<'a> { let mut last_fixed = true; let mut has_lt = false; let mut sections = Vec::new(); let mut start = 0; let mut end = 0; let mut field = |is_fixed: bool, is_align_pad: bool| { if !is_fixed && last_fixed { // we passed the end of a fixed section sections.push(SerializeSection::Fixed(¶ms[start..end])); start = end; } end += 1; if !is_fixed && !is_align_pad { // we reach a variable section sections.push(SerializeSection::Var(¶ms[start])); } if is_align_pad { // each section is followed by an align section, so align pads don't need any treatment assert!(!last_fixed); } if !is_fixed { // fixed fields are grouped together, so we advance the start pointer only when we reach a var field start = end; } last_fixed = is_fixed; }; for p in params { match p { Field::Field { struct_style: Some(StructStyle::DynBuf), .. } => { field(false, false); has_lt = true; } Field::Field { .. } => { field(true, false); } Field::List { struct_style: Some(StructStyle::DynBuf), len_expr: Expr::Value(_), .. } => { field(false, false); has_lt = true; } Field::List { is_union: true, .. } => { field(false, false); has_lt = true; } Field::List { len_expr: Expr::Value(_), .. } => { field(true, false); } Field::List { .. } => { field(false, false); has_lt = true; } Field::Switch { is_mask, .. } => { field(false, false); if *is_mask { has_lt = true; } } Field::Expr { .. } => { field(true, false); } Field::Pad { .. } => { field(true, false); } Field::AlignPad { .. } => { //assert!(last_fixed); field(false, true); } } } if last_fixed && start < end { sections.push(SerializeSection::Fixed(¶ms[start..end])); start = end; } assert!(start == end); let (cookie_rs_typ, reply_rs_typ) = if reply.is_none() { ("base::VoidCookie".to_string(), "()".to_string()) } else { (rs_typ.to_string() + "Cookie", rs_typ.to_string() + "Reply") }; let has_prop_field = params.iter().any(|p| { matches!( p, Field::Field { is_prop_format: true, .. } ) }); let has_fd = params.iter().any(field_is_fd); let reply_has_fd = if let Some(reply) = reply { reply.fields.iter().any(field_is_fd) } else { false }; RequestInfo { rs_typ: rs_typ.to_string(), cookie_rs_typ, reply_rs_typ, sections, has_lifetime: has_lt, opcode, is_void: reply.is_none(), has_prop_field, has_fd, reply_has_fd, } } fn emit_cookie_reply( &self, out: &mut O, reply: &Reply, info: &RequestInfo, ) -> io::Result<()> { let RequestInfo { rs_typ: req_name, cookie_rs_typ, reply_rs_typ, .. } = info; writeln!(out)?; if let Some(doc) = &reply.doc { doc.emit(out, 0)?; } else { writeln!(out, "/// Reply type for [{}].", req_name)?; writeln!(out, "///")?; writeln!(out, "/// Can be obtained from a [{}] with [Connection::wait_for_reply](crate::Connection::wait_for_reply)", cookie_rs_typ)?; writeln!(out, "/// or from a [{}Unchecked] with [Connection::wait_for_reply_unchecked](crate::Connection::wait_for_reply_unchecked)", cookie_rs_typ)?; } writeln!(out, "pub struct {} {{", reply_rs_typ)?; writeln!(out, " raw: *const u8,")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl {} {{", reply_rs_typ)?; writeln!(out)?; writeln!(out, " fn wire_ptr(&self) -> *const u8 {{")?; writeln!(out, " self.raw")?; writeln!(out, " }}")?; writeln!(out)?; // reply length field is expressed in 4 bytes units and start after the 32 bytes reply body writeln!(out, " fn wire_len(&self) -> usize {{")?; writeln!(out, " (32 + self.length() * 4) as _")?; writeln!(out, " }}")?; for f in &reply.fields { if let Field::Field { struct_style: Some(StructStyle::DynBuf), .. } | Field::List { .. } = f { let len_stmts = self.emit_compute_offset_and_get_stmts(out, reply_rs_typ, &reply.fields, None)?; self.emit_compute_func(out, "compute_len", None, &len_stmts)?; break; } } self.emit_struct_accessors(out, reply_rs_typ, &reply.fields)?; self.emit_reply_fds(out, reply_rs_typ, &reply.fields)?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::Reply for {} {{", reply_rs_typ)?; writeln!(out, " unsafe fn from_raw(raw: *const u8) -> Self {{")?; writeln!(out, " Self {{ raw }}")?; writeln!(out, " }}")?; writeln!(out)?; writeln!(out, " unsafe fn into_raw(self) -> *const u8 {{")?; writeln!(out, " let raw = self.raw;")?; writeln!(out, " std::mem::forget(self);")?; writeln!(out, " raw")?; writeln!(out, " }}")?; writeln!(out, "}}")?; self.emit_debug_impl(out, reply_rs_typ, &reply.fields)?; writeln!(out)?; writeln!(out, "impl Drop for {} {{", reply_rs_typ)?; writeln!(out, " fn drop(&mut self) {{")?; writeln!(out, " unsafe {{ libc::free(self.raw as *mut _); }}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!( out, "unsafe impl std::marker::Send for {} {{}}", reply_rs_typ )?; writeln!( out, "unsafe impl std::marker::Sync for {} {{}}", reply_rs_typ )?; writeln!(out)?; writeln!(out, "/// Cookie type for [{}].", req_name)?; writeln!(out, "///")?; writeln!( out, "/// This cookie can be used to get a [{}]", reply_rs_typ )?; writeln!( out, "/// with [Connection::wait_for_reply](crate::Connection::wait_for_reply)" )?; writeln!(out, "#[derive(Debug)]")?; writeln!(out, "pub struct {} {{", cookie_rs_typ)?; writeln!(out, " seq: u64,")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "#[derive(Debug)]")?; writeln!(out, "/// Unchecked cookie type for [{}].", req_name)?; writeln!(out, "///")?; writeln!( out, "/// This cookie can be used to get a [{}]", reply_rs_typ )?; writeln!(out, "/// with [Connection::wait_for_reply_unchecked](crate::Connection::wait_for_reply_unchecked)")?; writeln!(out, "pub struct {}Unchecked {{", cookie_rs_typ)?; writeln!(out, " seq: u64,")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::Cookie for {} {{", cookie_rs_typ)?; writeln!(out, " unsafe fn from_sequence(seq: u64) -> Self {{")?; writeln!(out, " {} {{ seq }}", cookie_rs_typ)?; writeln!(out, " }}")?; writeln!(out)?; writeln!(out, " fn sequence(&self) -> u64 {{")?; writeln!(out, " self.seq")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!( out, "unsafe impl base::CookieChecked for {} {{", cookie_rs_typ )?; writeln!(out, "}}")?; writeln!(out)?; writeln!( out, "unsafe impl base::CookieWithReplyChecked for {} {{", cookie_rs_typ )?; writeln!(out, " type Reply = {};", reply_rs_typ)?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::Cookie for {}Unchecked {{", cookie_rs_typ)?; writeln!(out, " unsafe fn from_sequence(seq: u64) -> Self {{")?; writeln!(out, " {}Unchecked {{ seq }}", cookie_rs_typ)?; writeln!(out, " }}")?; writeln!(out)?; writeln!(out, " fn sequence(&self) -> u64 {{")?; writeln!(out, " self.seq")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!( out, "unsafe impl base::CookieWithReplyUnchecked for {}Unchecked {{", cookie_rs_typ )?; writeln!(out, " type Reply = {};", reply_rs_typ)?; writeln!(out, "}}")?; Ok(()) } fn emit_reply_fds( &self, out: &mut O, reply_rs_typ: &str, fields: &[Field], ) -> io::Result<()> { // We emit the reply fds. // libxcb store them after the wire body. for f in fields { match f { Field::Field { name, rs_typ, .. } if rs_typ == "RawFd" => { writeln!(out)?; writeln!(out, "{}pub fn {}(&self) -> RawFd {{", cg::ind(1), name)?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!( out, "{}assert!(self.nfd() == 1, \"Expected a single Fd for {}::{}\");", cg::ind(3), reply_rs_typ, name )?; writeln!( out, "{}*(self.wire_ptr().add(self.wire_len()) as *const RawFd)", cg::ind(3) )?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; } Field::List { name, rs_typ, .. } if rs_typ == "RawFd" => { writeln!(out)?; writeln!(out, "{}pub fn {}(&self) -> &[RawFd] {{", cg::ind(1), name)?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!(out, "{}let len = self.nfd() as usize;", cg::ind(3))?; writeln!( out, "{}let ptr = self.wire_ptr().add(self.wire_len()) as *const RawFd;", cg::ind(3) )?; writeln!(out, "{}std::slice::from_raw_parts(ptr, len)", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; } _ => {} } } Ok(()) } fn emit_request_struct( &self, out: &mut O, rs_typ: &str, params: &[Field], info: &RequestInfo, doc: Option<&Doc>, sends_event: bool, ) -> io::Result<()> { let (generic_decl, _) = self.generic_decl_and_use(sends_event, info); writeln!(out)?; if let Some(doc) = doc { doc.emit(out, 0)?; } else { writeln!(out, "/// The `{}` request.", rs_typ)?; } writeln!(out, "///")?; if info.is_void { writeln!(out, "/// This request has no reply.")?; writeln!(out, "///")?; writeln!(out, "/// Associated cookie types are [VoidCookie](crate::VoidCookie) and [VoidCookieChecked](crate::VoidCookieChecked).")?; } else { writeln!(out, "/// This request replies [{}].", info.reply_rs_typ)?; writeln!(out, "///")?; writeln!( out, "/// Associated cookie types are [{}] and [{}Unchecked].", info.cookie_rs_typ, info.cookie_rs_typ )?; } if rs_typ == "InternAtom" { writeln!(out, "///")?; writeln!( out, "/// See also [`xcb::atoms_struct`](crate::atoms_struct)." )?; } writeln!(out, "#[derive(Clone, Debug)]")?; writeln!(out, "pub struct {}{} {{", rs_typ, generic_decl)?; for p in params { match p { Field::Field { name, doc, .. } | Field::List { name, doc, .. } if sends_event && name == "event" => { if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, "{}pub event: &'a E,", cg::ind(1))?; } Field::Field { name, module, rs_typ, is_fieldref, struct_style: Some(StructStyle::DynBuf), doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if !is_fieldref { if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub {}: &'a {},", name, q_rs_typ)?; } } Field::Field { name, module, rs_typ, is_fieldref, r#enum, mask, is_prop_format, doc, .. } => { if name == "major_opcode" || name == "minor_opcode" || name == "length" { // These fields are written on the wire, but not needed as parameters. // They are filled in by XCB directly continue; } if *is_prop_format { // format field is inferred from the property data field continue; } let q_rs_typ = r#struct::enum_mask_qualified_rs_typ(module, rs_typ, r#enum, mask); if !is_fieldref || request_fieldref_emitted(name, params, info.has_prop_field) { if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub {}: {},", name, q_rs_typ)?; } } Field::List { name, module, rs_typ, struct_style: Some(StructStyle::DynBuf), len_expr: Expr::Value(len), doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub {}: [{}Buf; {}],", name, q_rs_typ, len)?; } Field::List { name, module, rs_typ, len_expr: Expr::Value(len), doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub {}: [{}; {}],", name, q_rs_typ, len)?; } Field::List { name, module, rs_typ, struct_style: Some(StructStyle::DynBuf), doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub {}: &'a [{}Buf],", name, q_rs_typ)?; } Field::List { name, rs_typ, doc, .. } if rs_typ == "char" => { if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub {}: &'a [u8],", name)?; } Field::List { name, module, rs_typ, is_prop, doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let typ = if *is_prop { "P" } else { &q_rs_typ }; if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub {}: &'a [{}],", name, typ)?; } Field::Switch { name, module, rs_typ, is_mask, doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if let Some(doc) = doc { doc.emit(out, 1)?; } if *is_mask { writeln!(out, " pub {}: &'a [{}],", name, q_rs_typ)?; } else { writeln!(out, " pub {}: {},", name, q_rs_typ)?; } } _ => {} } } writeln!(out, "}}")?; Ok(()) } fn emit_request_traits( &self, out: &mut O, rs_typ: &str, params: &[Field], info: &RequestInfo, sends_event: bool, ) -> io::Result<()> { self.emit_raw_request(out, rs_typ, params, info, sends_event)?; let (generic_decl, generic_use) = self.generic_decl_and_use(sends_event, info); let RequestInfo { is_void, cookie_rs_typ, reply_rs_typ, .. } = info; writeln!(out)?; writeln!( out, "impl{} base::Request for {}{} {{", generic_decl, rs_typ, generic_use )?; writeln!(out, " type Cookie = {};", cookie_rs_typ)?; writeln!(out, " const IS_VOID: bool = {};", is_void)?; writeln!(out, "}}")?; writeln!(out)?; writeln!( out, "impl{} base::Request{}Reply for {}{} {{", generic_decl, if *is_void { "Without" } else { "With" }, rs_typ, generic_use )?; if !*is_void { writeln!(out, " type Reply = {};", reply_rs_typ)?; writeln!(out, " type Cookie = {};", cookie_rs_typ)?; writeln!( out, " type CookieUnchecked = {}Unchecked;", cookie_rs_typ )?; } writeln!(out, "}}")?; Ok(()) } fn generic_decl_and_use(&self, sends_event: bool, info: &RequestInfo) -> (&str, &str) { if sends_event { ("<'a, E: base::BaseEvent>", "<'a, E>") } else if info.has_prop_field { ("<'a, P: PropEl>", "<'a, P>") } else if info.has_lifetime { ("<'a>", "<'a>") } else { ("", "") } } fn emit_raw_request( &self, out: &mut O, rs_typ: &str, params: &[Field], info: &RequestInfo, sends_event: bool, ) -> io::Result<()> { let (generic_decl, generic_use) = self.generic_decl_and_use(sends_event, info); let RequestInfo { sections, opcode, is_void, .. } = info; let parts_count = 2 * sections.len(); // the C implementation always start with two iovec untouched (not sure why) let iovec_count = parts_count + 2; writeln!(out)?; writeln!( out, "unsafe impl{} base::RawRequest for {}{} {{", generic_decl, rs_typ, generic_use )?; writeln!( out, " fn raw_request(&self, c: &base::Connection, checked: bool) -> u64 {{ unsafe {{", )?; self.emit_req_assertions(out, rs_typ, params)?; writeln!( out, "{}let mut protocol_request = xcb_protocol_request_t {{", cg::ind(2), )?; writeln!(out, "{}count: {},", cg::ind(3), parts_count)?; if self.xcb_mod == "xproto" { writeln!(out, "{}ext: std::ptr::null_mut(),", cg::ind(3))?; } else { writeln!(out, "{}ext: (&mut FFI_EXT) as *mut _,", cg::ind(3))?; } writeln!(out, "{}opcode: {},", cg::ind(3), opcode)?; writeln!( out, "{}isvoid: {},", cg::ind(3), if *is_void { 1 } else { 0 } )?; writeln!(out, "{}}};", cg::ind(2))?; writeln!(out)?; writeln!( out, "{}let mut sections: [iovec; {}] = [iovec {{", cg::ind(2), iovec_count )?; writeln!(out, "{} iov_base: std::ptr::null_mut(),", cg::ind(2))?; writeln!(out, "{} iov_len: 0,", cg::ind(2))?; writeln!(out, "{}}}; {}];", cg::ind(2), iovec_count)?; for p in params { // there are no cases where more than one field is fd, so we don't need to accumulate a length here match p { Field::Field { name, rs_typ, .. } if rs_typ == "RawFd" => { writeln!(out, "{}let fds: [RawFd; 1] = [self.{}];", cg::ind(2), name)?; } Field::List { name, rs_typ, .. } if rs_typ == "RawFd" => { writeln!(out, "{}let fds: &[RawFd] = self.{};", cg::ind(2), name)?; } _ => {} } } for (num, sect) in info.sections.iter().enumerate() { writeln!(out)?; match sect { SerializeSection::Fixed(fields) => { self.emit_req_fixed_buf(out, num, fields, params, info, sends_event)?; } SerializeSection::Var(field) => { self.emit_req_var_buf(out, num, field)?; } } writeln!( out, "{}sections[{}].iov_len = base::align_pad(sections[{}].iov_len, 4);", cg::ind(2), num * 2 + 3, num * 2 + 2 )?; } let (unchecked_f, checked_f) = ("base::RequestFlags::NONE", "base::RequestFlags::CHECKED"); let reply_fd_f = if info.reply_has_fd { " | base::RequestFlags::REPLY_FDS" } else { "" }; writeln!(out)?; writeln!( out, "{}let flags = if checked {{ {}{} }} else {{ {}{} }};", cg::ind(2), checked_f, reply_fd_f, unchecked_f, reply_fd_f, )?; let func = if info.has_fd { "xcb_send_request_with_fds64" } else { "xcb_send_request64" }; writeln!(out)?; writeln!(out, "{}{}(", cg::ind(2), func)?; writeln!(out, "{} c.get_raw_conn(),", cg::ind(2))?; writeln!(out, "{} flags.bits() as _,", cg::ind(2))?; writeln!(out, "{} sections.as_mut_ptr().add(2),", cg::ind(2))?; writeln!(out, "{} &mut protocol_request as *mut _,", cg::ind(2))?; if info.has_fd { writeln!(out, "{}fds.len() as _,", cg::ind(3))?; // xcb will not modify the content of fds, the cast is therefore safe writeln!(out, "{}fds.as_ptr() as *mut _,", cg::ind(3))?; } writeln!(out, "{})", cg::ind(2))?; writeln!(out, " }}")?; writeln!(out, "}}}}")?; Ok(()) } fn emit_req_assertions( &self, out: &mut O, rs_typ: &str, params: &[Field], ) -> io::Result<()> { for p in params { if let Field::Switch { name, module, rs_typ: f_rs_typ, is_mask: true, .. } = p { let q_rs_typ = (module, f_rs_typ).qualified_rs_typ(); writeln!( out, "{}assert!({}::is_sorted_distinct(self.{}), \"{}::{} must be sorted!\");", cg::ind(2), q_rs_typ, name, rs_typ, name )?; } } Ok(()) } fn emit_req_fixed_buf( &self, out: &mut O, num: usize, fields: &[Field], params: &[Field], info: &RequestInfo, sends_event: bool, ) -> io::Result<()> { let sz = self.request_fixed_fields_size(fields); writeln!( out, "{}let buf{}: &mut [u8] = &mut [0; {}];", cg::ind(2), num, sz, )?; let mut offset = 0; for f in fields { if field_is_fd(f) { continue; } match f { Field::Field { name, .. } | Field::List { name, .. } if sends_event && name == "event" => { writeln!( out, "{}let raw_{}_slice = std::slice::from_raw_parts(self.{}.as_raw() as *const u8, 32);", cg::ind(2), name, name )?; writeln!(out, "{}std::slice::from_raw_parts_mut(", cg::ind(2))?; writeln!( out, "{} buf{}.as_mut_ptr().add({}), 32", cg::ind(2), num, offset, )?; writeln!(out, "{}).copy_from_slice(raw_{}_slice);", cg::ind(2), name)?; offset += 32; } Field::Field { name, wire_sz: Expr::Value(sz), .. } if name == "major_opcode" || name == "minor_opcode" || name == "length" => { offset += sz; } Field::Field { name, module, rs_typ, wire_sz: Expr::Value(sz), r#enum, mask, is_fieldref, is_prop_format, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let fieldref_value = if *is_fieldref { fieldref_get_value(name, params, info.has_prop_field, "self.") } else { None }; let value_expr; if let Some(value) = fieldref_value { value_expr = format!("({} as {})", value, q_rs_typ); } else if *is_prop_format { value_expr = "P::FORMAT".to_string(); } else if mask.is_some() && rs_typ == "u32" { value_expr = format!("self.{}.bits()", name); } else if mask.is_some() { value_expr = format!("(self.{}.bits() as {})", name, q_rs_typ); } else if r#enum.is_some() && rs_typ == "u32" { value_expr = format!("std::mem::transmute::<_, u32>(self.{})", name); } else if r#enum.is_some() && rs_typ == "bool" { value_expr = format!( "(std::mem::transmute::<_, u32>(self.{}) as u{})", name, 8 * sz ); } else if r#enum.is_some() { value_expr = format!( "(std::mem::transmute::<_, u32>(self.{}) as {})", name, q_rs_typ ); } else if rs_typ == "bool" { value_expr = format!( "(if self.{} {{ 1u{} }} else {{ 0u{} }})", name, 8 * sz, 8 * sz ); } else { value_expr = format!("self.{}", name); } writeln!( out, "{}{}.serialize(&mut buf{}[{} .. ]);", cg::ind(2), value_expr, num, offset )?; offset += sz; } Field::List { name, module, rs_typ, wire_sz: Expr::Value(sz), len_expr: Expr::Value(len), .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!(out, "{}std::slice::from_raw_parts_mut(", cg::ind(2))?; writeln!( out, "{} buf{}.as_mut_ptr().add({}) as *mut {}, {}", cg::ind(2), num, offset, q_rs_typ, len )?; writeln!(out, "{}).copy_from_slice(&self.{});", cg::ind(2), name)?; offset += sz; } Field::Expr { name, typ, expr, wire_sz: Expr::Value(sz), .. } if typ == "BOOL" => { writeln!(out, "{}// {} expression", cg::ind(2), name)?; writeln!( out, "{}*(buf{}.as_mut_ptr().add({}) as *mut u8) = {} as u8;", cg::ind(2), num, offset, // bad hack, but only one occurence of exprfield in the complete library // no need to push further self.build_rs_expr(expr, "self.", "", &[]) .replace("_len", ".len()"), )?; offset += sz; } Field::Pad { wire_sz: Expr::Value(sz), .. } => { offset += sz; } _ => unreachable!("{:#?}", f), } } writeln!( out, "{}sections[{}].iov_base = buf{}.as_mut_ptr() as *mut _;", cg::ind(2), num * 2 + 2, num )?; writeln!( out, "{}sections[{}].iov_len = {};", cg::ind(2), num * 2 + 2, sz )?; Ok(()) } fn emit_req_var_buf(&self, out: &mut O, num: usize, field: &Field) -> io::Result<()> { match field { Field::Field { name, struct_style: Some(StructStyle::DynBuf), .. } => { writeln!( out, "{}sections[{}].iov_base = self.{}.wire_ptr() as *mut _;", cg::ind(2), num * 2 + 2, name, )?; writeln!( out, "{}sections[{}].iov_len = self.{}.wire_len();", cg::ind(2), num * 2 + 2, name, )?; } Field::List { name, struct_style: Some(StructStyle::DynBuf), .. } => { writeln!( out, "{}let len{}: usize = self.{}.iter().map(|el| el.wire_len()).sum();", cg::ind(2), num, name )?; writeln!( out, "{}let mut buf{} = vec![0u8; len{}];", cg::ind(2), num, num )?; writeln!(out, "{}let mut offset = 0usize;", cg::ind(2))?; writeln!(out, "{}for el in self.{} {{", cg::ind(2), name)?; writeln!( out, "{} offset += el.serialize(&mut buf{}[offset..]);", cg::ind(2), num )?; writeln!(out, "{}}}", cg::ind(2))?; writeln!( out, "{}sections[{}].iov_base = buf{}.as_ptr() as *mut _;", cg::ind(2), num * 2 + 2, num )?; writeln!( out, "{}sections[{}].iov_len = buf{}.len();", cg::ind(2), num * 2 + 2, num )?; } Field::List { name, rs_typ, .. } if rs_typ == "char" => { writeln!( out, "{}sections[{}].iov_base = self.{}.as_ptr() as *mut _;", cg::ind(2), num * 2 + 2, name )?; writeln!( out, "{}sections[{}].iov_len = self.{}.len();", cg::ind(2), num * 2 + 2, name )?; } Field::List { name, is_union: true, .. } => { writeln!( out, "{}let len{} = self.{}.iter().map(|el| el.wire_len()).sum::();", cg::ind(2), num, name )?; writeln!( out, "{}let mut buf{} = vec![0u8; len{}];", cg::ind(2), num, num )?; writeln!(out, "{}let mut offset{} = 0usize;", cg::ind(2), num)?; writeln!(out, "{}for el in self.{} {{", cg::ind(2), name)?; writeln!( out, "{}offset{} += el.serialize(&mut buf{}[offset{} ..]);", cg::ind(3), num, num, num )?; writeln!(out, "{}}}", cg::ind(2))?; writeln!( out, "{}sections[{}].iov_base = buf{}.as_ptr() as *mut _;", cg::ind(2), num * 2 + 2, num )?; writeln!( out, "{}sections[{}].iov_len = buf{}.len();", cg::ind(2), num * 2 + 2, num )?; } Field::List { name, module, rs_typ, is_prop, .. } => { writeln!( out, "{}sections[{}].iov_base = self.{}.as_ptr() as *mut _;", cg::ind(2), num * 2 + 2, name, )?; let typ_sz: Cow = if *is_prop { Cow::Borrowed("P") } else { Cow::Owned((module, rs_typ).qualified_rs_typ()) }; writeln!( out, "{}sections[{}].iov_len = self.{}.len() * std::mem::size_of::<{}>();", cg::ind(2), num * 2 + 2, name, typ_sz )?; } Field::Switch { name, .. } => { writeln!( out, "{}let len{} = self.{}.wire_len();", cg::ind(2), num, name, )?; writeln!( out, "{}let mut buf{} = vec![0u8; len{}];", cg::ind(2), num, num )?; writeln!( out, "{}self.{}.serialize(&mut buf{});", cg::ind(2), name, num )?; writeln!( out, "{}sections[{}].iov_base = buf{}.as_ptr() as *mut _;", cg::ind(2), num * 2 + 2, num )?; writeln!( out, "{}sections[{}].iov_len = buf{}.len();", cg::ind(2), num * 2 + 2, num )?; } _ => unreachable!("{:#?}", field), } Ok(()) } fn request_fixed_fields_size(&self, fields: &[Field]) -> usize { let mut sz = 0; for f in fields { if field_is_fd(f) { continue; } match f { Field::Field { wire_sz: Expr::Value(s), .. } => { sz += s; } Field::List { wire_sz: Expr::Value(s), .. } => { sz += s; } Field::Expr { wire_sz: Expr::Value(s), .. } => { sz += s; } Field::Pad { wire_sz: Expr::Value(s), .. } => { sz += s; } _ => unreachable!("{:#?}", f), } } sz } } #[derive(Debug)] struct RequestInfo<'a> { rs_typ: String, cookie_rs_typ: String, reply_rs_typ: String, sections: Vec>, has_lifetime: bool, opcode: u32, is_void: bool, has_prop_field: bool, has_fd: bool, reply_has_fd: bool, } #[derive(Debug)] enum SerializeSection<'a> { Fixed(&'a [Field]), Var(&'a Field), } // When we encounter a field that is referred in eg a list len or switch mask // we try to infer its value from the list or the switch. // But we can manage this only for the simplest cases: // - a single fieldref // - a fieldref multiplied by format divided by 8 (for properties only) // For other cases, we put the field as public paramater and require user to supply a value. pub(super) fn request_fieldref_emitted(name: &str, fields: &[Field], has_prop_field: bool) -> bool { for f in fields { match f { Field::List { len_expr: Expr::FieldRef(fr), .. } if fr == name => { return false; } Field::List { len_expr: Expr::Op(op, lhs, rhs), .. } if has_prop_field && op == "*" && is_prop_field_mult(name, lhs, rhs) => { return false; } Field::Switch { expr: Expr::FieldRef(fr), .. } if fr == name => { return false; } _ => {} } } true } fn is_prop_field_mult<'a>(name: &'a str, lhs: &Expr, rhs: &Expr) -> bool { // we are in the first level of the multiplication `length * (format / 8)` // must reach 4 conditions: // - lhs is fieldref to name // - rhs is a division // - rhs::lhs is fieldref format // - rhs::rhs is value 8 // box pattern matching would really be helpful, but not stabilized at time of coding if !matches!(lhs, Expr::FieldRef(f) if f == name) { return false; } match rhs { Expr::Op(op, lhs, rhs) if op == "/" => { if !matches!(&**lhs, Expr::FieldRef(f) if f == "format") { return false; } if !matches!(&**rhs, Expr::Value(8)) { return false; } } _ => return false, } true } pub(super) fn fieldref_get_value( name: &str, fields: &[Field], has_prop_field: bool, acc: &str, ) -> Option { for f in fields { match f { Field::List { name: fname, len_expr: Expr::FieldRef(fr), .. } if fr == name => { return Some(format!("{}{}.len()", acc, fname)); } Field::List { name: fname, len_expr: Expr::Op(op, lhs, rhs), .. } if has_prop_field && op == "*" && is_prop_field_mult(name, lhs, rhs) => { return Some(format!("{}{}.len()", acc, fname)); } Field::Switch { name: fname, module, rs_typ, expr: Expr::FieldRef(fr), is_mask, .. } if fr == name => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if *is_mask { return Some(format!("{}::get_mask({}{}).bits()", q_rs_typ, acc, fname)); } else { return Some(format!( "std::mem::transmute::<_, u32>({}{}.get_enum())", acc, fname )); } } _ => {} } } None } pub(super) fn field_is_fd(field: &Field) -> bool { matches!( field, Field::Field{rs_typ, ..} | Field::List{rs_typ, ..} if rs_typ == "RawFd" ) } xcb-1.2.2/build/cg/struct.rs000064400000000000000000002730041046102023000137720ustar 00000000000000use crate::cg; use crate::cg::request::{field_is_fd, fieldref_get_value, request_fieldref_emitted}; use crate::cg::util; use crate::ir; use super::UnionTypeField; use super::{ doc::DocField, CodeGen, Doc, Expr, Field, HasWireLayout, QualifiedRsTyp, RsTyp, StructStyle, TypeInfo, WireSz, }; use std::borrow::Cow; use std::io::{self, Write}; /// struct of external parameters passed to functions such as compute_len /// This is very often `()` for which `None` is used, but sometimes an field external to struct is needed /// to compute the len of a list (i.e. element types) #[derive(Clone, Debug)] pub(super) struct ParamsStruct { pub rs_typ: String, pub params: Vec, } impl ParamsStruct { pub fn emit(&self, out: &mut O) -> io::Result<()> { writeln!(out)?; writeln!(out, "#[derive(Copy, Clone, Debug)]")?; writeln!(out, "pub struct {} {{", self.rs_typ)?; for p in &self.params { writeln!(out, " pub {}: usize,", p)?; } writeln!(out, "}}")?; Ok(()) } } impl RsTyp for Option<&ParamsStruct> { fn rs_typ(&self) -> &str { match self { Some(params_struct) => ¶ms_struct.rs_typ, _ => "()", } } } #[derive(Debug)] pub(super) struct ResolvedFields { pub fields: Vec, pub wire_sz: Expr, pub has_wire_layout: bool, pub unresolved_fieldrefs: Vec, pub params_struct: Option, } impl CodeGen { pub(super) fn resolve_struct( &mut self, typ: &str, fields: &[ir::Field], doc: &Option, ) { let rs_typ = cg::rust_type_name(typ); let doc = self.resolve_doc(doc.clone()); let ResolvedFields { fields, wire_sz, has_wire_layout, params_struct, // unresolved_fieldrefs, .. } = self.resolv_struct_fields(&rs_typ, "", fields, doc.as_ref()); // assert!(unresolved_fieldrefs.is_empty()); let typ_info = TypeInfo::Struct { module: None, rs_typ, fields, wire_sz, has_wire_layout, params_struct, doc, }; self.register_typ(typ.to_string(), typ_info); } pub(super) fn resolv_struct_fields( &mut self, struct_rs_typ: &str, parent_switch: &str, fields: &[ir::Field], doc: Option<&Doc>, ) -> ResolvedFields { let mut vec = Vec::new(); let mut wire_off = Expr::Value(0usize); let mut has_wire_layout = true; let mut need_compute_offset = false; // fields that are referenced in list length of this struct let mut fieldrefs = Vec::new(); // external fields that are refereced in list length of this struct let mut paramrefs = Vec::new(); // fields of this struct that are used as list length of other struct let mut paramfields = Vec::new(); let mut unresolved_fieldrefs = Vec::new(); let has_prop_field = fields.iter().any( |f| matches!(f, ir::Field::Field{name, typ, ..} if name == "format" && typ == "CARD8"), ) && fields .iter() .any(|f| matches!(f, ir::Field::List{typ, ..} if typ == "void")); for f in fields { let f_sz = match f { ir::Field::Field { name, typ, r#enum, mask, altenum, .. } => { let FieldInfo { mut name, module, rs_typ, wire_sz, has_wire_layout: hfl, struct_style, is_union, is_xid, is_mask, params_struct, doc, .. } = self.get_field_info(name, typ, r#enum.as_deref(), mask.as_deref(), doc); if !hfl || (rs_typ != "u32" && (r#enum.is_some() || (mask.is_some()))) { has_wire_layout = false; } let r#enum = r#enum.as_ref().map(|typ| self.get_mod_rs_typ(typ)); let mask = mask.as_ref().map(|typ| self.get_mod_rs_typ(typ)); if let Some(altenum) = altenum { self.register_altenum_typ(altenum, module.as_deref(), typ); } if self.xcb_mod == "xinput" && typ == "DeviceId" { name = match name.as_str() { "deviceid" => "device".into(), "device_id" => "device".into(), "sourceid" => "source".into(), _ => name, }; } let wire_sz = wire_sz.reduce(); let is_prop_format = has_prop_field && name == "format"; vec.push(Field::Field { name, module, rs_typ, wire_off: wire_off.clone(), wire_sz: wire_sz.clone(), struct_style, params_struct, doc, is_mask: is_mask || mask.is_some(), r#enum, mask, is_fieldref: false, is_paramref: false, is_copy: hfl, is_union, is_xid, need_compute_offset, is_prop_format, }); wire_sz } ir::Field::Pad(wire_sz) => { has_wire_layout = false; let wire_off = wire_off.clone().reduce(); vec.push(Field::Pad { wire_off, wire_sz: Expr::Value(*wire_sz), }); Expr::Value(*wire_sz) } ir::Field::AlignPad(sz) => { has_wire_layout = false; let wire_off = wire_off.clone().reduce(); let wire_sz = Expr::AlignPad(*sz, Box::new(wire_off.clone())).reduce(); vec.push(Field::AlignPad { wire_off, wire_sz: wire_sz.clone(), }); wire_sz } ir::Field::List { name, typ, len_expr, r#enum, mask, } => { let FieldInfo { name, module, rs_typ, typ, wire_sz, has_wire_layout: hfl, struct_style, params_struct, union_typefield, r#enum, mask, doc, is_union, .. } = self.get_field_info(name, typ, r#enum.as_deref(), mask.as_deref(), doc); let len_expr = self.resolve_expr(len_expr); wire_sz.fetch_paramrefs_owned(&mut paramfields); let wire_sz = Expr::Op( "*".to_string(), Box::new(len_expr.clone()), Box::new(wire_sz), ); let wire_sz = wire_sz.reduce(); let fixed_str = matches!((rs_typ.as_str(), &wire_sz), ("char", Expr::Value(_))); if fixed_str { } else if !hfl || !matches!(wire_sz, Expr::Value(_)) { has_wire_layout = false; } len_expr.fetch_fieldrefs_owned(&mut fieldrefs); len_expr.fetch_paramrefs_owned(&mut paramrefs); // assert!(!need_compute_offset, "{}::{}", self.xcb_mod, name); let is_prop = has_prop_field && typ == "void"; vec.push(Field::List { name, module, rs_typ, wire_off: wire_off.clone(), wire_sz: wire_sz.clone(), struct_style, r#enum, mask, is_fieldref: false, len_expr, need_compute_offset, params_struct, union_typefield, is_prop, is_union, doc, }); if let Some(StructStyle::DynBuf) = struct_style { need_compute_offset = true; } wire_sz } ir::Field::ListNoLen { typ, name, r#enum, mask, } => { let FieldInfo { name, module, rs_typ, typ, struct_style, params_struct, union_typefield, r#enum, mask, is_union, doc, .. } = self.get_field_info(name, typ, r#enum.as_deref(), mask.as_deref(), doc); has_wire_layout = false; let is_prop = has_prop_field && typ == "void"; vec.push(Field::List { name, module, rs_typ, wire_off: wire_off.clone(), wire_sz: Expr::UntilEnd, struct_style, params_struct, union_typefield, r#enum, mask, is_fieldref: false, len_expr: Expr::UntilEnd, need_compute_offset, is_prop, is_union, doc, }); Expr::Unknown("list no len".to_string()) } ir::Field::ValueParam { .. } => { unreachable!(" not anymore used with xcb-1.14") } ir::Field::Switch(name, expr, cases) => { let expr = self.resolve_expr(expr); let field = self.resolve_switch( struct_rs_typ, parent_switch, name, &expr, &wire_off, cases, &mut vec, need_compute_offset, doc, ); expr.fetch_fieldrefs_owned(&mut fieldrefs); if let Field::Switch { params_struct, .. } = &field { let mut params = params_struct.params.clone(); fieldrefs.append(&mut params); } else { unreachable!(); }; vec.push(field); need_compute_offset = true; has_wire_layout = false; Expr::Unknown("switch len".to_string()) } ir::Field::Expr { name, typ, expr } => { let FieldInfo { name, typ, wire_sz, .. } = self.get_field_info(name, typ, None, None, doc); has_wire_layout = false; let wire_sz = wire_sz.reduce(); let expr = self.resolve_expr(expr); vec.push(Field::Expr { name, typ, wire_off: wire_off.clone(), wire_sz: wire_sz.clone(), expr, }); wire_sz } ir::Field::Fd(name) => { eprintln!("partial treatment of Fd field: {}::{}", struct_rs_typ, name); let doc = self.doc_lookup_field(doc, name); vec.push(Field::Field { name: name.clone(), module: None, rs_typ: "RawFd".to_string(), wire_off: wire_off.clone(), wire_sz: Expr::Value(4), struct_style: None, params_struct: None, doc, is_fieldref: false, is_paramref: false, is_copy: true, is_union: false, is_xid: false, is_mask: false, r#enum: None, mask: None, need_compute_offset: false, is_prop_format: false, }); has_wire_layout = false; Expr::Value(4) } // f => unreachable!("{:#?}", f), }; let new_off = Expr::Op("+".to_string(), Box::new(wire_off), Box::new(f_sz)); wire_off = new_off.reduce(); } for fr in fieldrefs { let mut resolved = false; for f in &mut vec { match f { Field::Field { name, is_fieldref, .. } if name == &fr => { *is_fieldref = true; resolved = true; break; } Field::List { name, is_fieldref, .. } if name == &fr => { *is_fieldref = true; resolved = true; break; } _ => {} } } if !resolved { unresolved_fieldrefs.push(fr); } } for pf in ¶mfields { for f in &mut vec { match f { Field::Field { name, is_paramref, .. } if name == pf => { *is_paramref = true; break; } _ => {} } } } let params_struct = if paramrefs.is_empty() { None } else { Some(ParamsStruct { rs_typ: struct_rs_typ.to_string() + "Params", params: paramrefs, }) }; ResolvedFields { fields: vec, wire_sz: wire_off, has_wire_layout, unresolved_fieldrefs, params_struct, } } fn get_mod_rs_typ(&self, typ: &str) -> (Option, String) { let (module, typ) = util::extract_module(typ); let typinfo = self.find_typinfo(module, typ); let module = typinfo.module(); let rs_typ = typinfo.rs_typ(); (module.map(str::to_owned), rs_typ.to_string()) } fn get_field_info( &self, name: &str, typ: &str, r#enum: Option<&str>, mask: Option<&str>, doc: Option<&Doc>, ) -> FieldInfo { let name = cg::rust_field_name(name); let (module, typ) = util::extract_module(typ); let typinfo = self.find_typinfo(module, typ); let module = typinfo.module(); let rs_typ = typinfo.rs_typ(); let wire_sz = typinfo.wire_sz(); let is_union = matches!(typinfo, TypeInfo::Union { .. } | TypeInfo::XidUnion { .. }); let is_xid = matches!(typinfo, TypeInfo::Xid { .. } | TypeInfo::XidUnion { .. }); let is_mask = matches!(typinfo, TypeInfo::Mask { .. }); let params_struct = match typinfo { TypeInfo::Struct { params_struct, .. } => params_struct.clone(), _ => None, }; let union_typefield = match typinfo { TypeInfo::Union { type_field, .. } => type_field.clone(), _ => None, }; let doc = self.doc_lookup_field(doc, &name); let has_wire_layout = r#enum.is_none() && typinfo.has_wire_layout(); let r#enum = r#enum.as_ref().map(|typ| self.get_mod_rs_typ(typ)); let mask = mask.as_ref().map(|typ| self.get_mod_rs_typ(typ)); FieldInfo { name, module: module.map(str::to_owned), typ: typ.to_string(), rs_typ: rs_typ.to_string(), wire_sz, has_wire_layout, struct_style: typinfo.struct_style(), r#enum, mask, params_struct, union_typefield, doc, is_union, is_xid, is_mask, } } pub(super) fn build_params_expr( &self, params_struct: Option<&ParamsStruct>, module: Option<&str>, acc_pref: &str, acc_post: &str, ) -> String { if params_struct.is_none() { return "()".to_string(); } let params_struct = params_struct.unwrap(); let q_rs_typ = (module, params_struct.rs_typ.as_str()).qualified_rs_typ(); let mut expr = q_rs_typ + " {"; for (i, p) in params_struct.params.iter().enumerate() { expr.push_str(&format!("{}: {}{}{} as usize", p, acc_pref, p, acc_post)); if i < params_struct.params.len() - 1 { expr.push_str(", "); } } expr.push('}'); expr } #[allow(clippy::too_many_arguments)] pub(super) fn emit_struct( &self, out: &mut O, rs_typ: &str, fields: &[Field], wire_sz: &Expr, has_wire_layout: bool, params_struct: Option<&ParamsStruct>, doc: Option<&Doc>, ) -> io::Result<()> { if !self.rs_typ_is_needed(rs_typ) { return Ok(()); } match (has_wire_layout, wire_sz) { (true, Expr::Value(wire_sz)) => { self.emit_wire_layout_struct(out, rs_typ, fields, *wire_sz, doc)? } (false, Expr::Value(wire_sz)) => { self.emit_fix_buf_struct(out, rs_typ, fields, *wire_sz, doc)? } (false, wire_sz) => { self.emit_dyn_buf_struct(out, rs_typ, fields, params_struct, wire_sz, doc)? } _ => unreachable!("{}::{}", self.xcb_mod, rs_typ), } Ok(()) } fn emit_wire_layout_struct( &self, out: &mut O, rs_typ: &str, fields: &[Field], wire_sz: usize, doc: Option<&Doc>, ) -> io::Result<()> { writeln!(out)?; if let Some(doc) = doc { doc.emit(out, 0)?; } writeln!(out, "#[derive(Copy, Clone, Debug)]")?; writeln!(out, "#[repr(C)]")?; writeln!(out, "pub struct {} {{", rs_typ)?; for f in fields { match f { Field::Field { name, module, rs_typ, r#enum, mask, doc, .. } => { let q_rs_typ = enum_mask_qualified_rs_typ(module, rs_typ, r#enum, mask); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub {}: {},", name, q_rs_typ)?; } Field::List { name, rs_typ, len_expr: Expr::Value(len), doc, .. } if rs_typ == "char" => { if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub {}: Lat1StrF<{}>,", name, len)?; } Field::List { name, module, rs_typ, len_expr: Expr::Value(len), doc, .. } => { let mod_rs_typ = (module, rs_typ); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, " pub {}: [{}; {}],", name, mod_rs_typ.qualified_rs_typ(), len )?; } _ => unreachable!("emitting struct field {:?}", f), } } writeln!(out, "}}")?; self.emit_sizeof_test(out, rs_typ, wire_sz)?; writeln!(out)?; writeln!(out, "impl base::WiredOut for {} {{", rs_typ)?; writeln!(out, " fn wire_len(&self) -> usize {{ {} }}", wire_sz)?; writeln!(out)?; writeln!( out, " fn serialize(&self, wire_buf: &mut [u8]) -> usize {{" )?; writeln!(out, " let me = unsafe {{")?; writeln!( out, " std::slice::from_raw_parts(self as *const {} as _, {})", rs_typ, wire_sz )?; writeln!(out, " }};")?; writeln!( out, " (&mut wire_buf[..me.len()]).copy_from_slice(me);" )?; writeln!(out, " {}", wire_sz)?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::WiredIn for {} {{", rs_typ)?; writeln!(out, " type Params = ();")?; writeln!( out, " unsafe fn compute_wire_len(_ptr: *const u8, _params: ()) -> usize {{ {} }}", wire_sz )?; writeln!( out, " unsafe fn unserialize(ptr: *const u8, _params: (), offset: &mut usize) -> Self {{" )?; writeln!(out, " *offset += {};", wire_sz)?; writeln!(out, " *(ptr as *const {})", rs_typ)?; writeln!(out, " }}")?; writeln!(out, "}}")?; Ok(()) } fn emit_fix_buf_struct( &self, out: &mut O, rs_typ: &str, fields: &[Field], wire_sz: usize, doc: Option<&Doc>, ) -> io::Result<()> { writeln!(out)?; if let Some(doc) = doc { doc.emit(out, 0)?; } writeln!(out, "#[derive(Copy, Clone)]")?; writeln!(out, "pub struct {} {{", rs_typ)?; writeln!(out, " data: [u8; {}],", wire_sz)?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "#[allow(unused_parens)]")?; writeln!(out, "impl {} {{", rs_typ)?; writeln!( out, " pub(crate) unsafe fn from_data + ?Sized>(data: &D) -> &{} {{", rs_typ )?; writeln!( out, " debug_assert_eq!(data.as_ref().len(), {});", wire_sz )?; writeln!( out, " &*(data.as_ref() as *const [u8] as *const {})", rs_typ )?; writeln!(out, " }}")?; self.emit_struct_ctor(out, rs_typ, fields, &Expr::Value(wire_sz))?; writeln!(out)?; writeln!( out, " fn wire_ptr(&self) -> *const u8 {{ self.data.as_ptr() }}" )?; writeln!(out)?; writeln!(out, " fn wire_len(&self) -> usize {{ self.data.len() }}")?; self.emit_struct_accessors(out, rs_typ, fields)?; writeln!(out, "}}")?; self.emit_sizeof_test(out, rs_typ, wire_sz)?; writeln!(out)?; writeln!(out, "impl base::WiredOut for {} {{", rs_typ)?; writeln!(out, " fn wire_len(&self) -> usize {{ {} }}", wire_sz)?; writeln!(out)?; writeln!( out, " fn serialize(&self, wire_buf: &mut [u8]) -> usize {{" )?; writeln!( out, " (&mut wire_buf[..self.data.len()]).copy_from_slice(&self.data);" )?; writeln!(out, " self.data.len()")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::WiredIn for {} {{", rs_typ)?; writeln!(out, " type Params = ();")?; writeln!( out, " unsafe fn compute_wire_len(_ptr: *const u8, _params: ()) -> usize {{ {} }}", wire_sz )?; writeln!( out, " unsafe fn unserialize(ptr: *const u8, _params: (), offset: &mut usize) -> Self {{" )?; writeln!(out, " *offset += {};", wire_sz)?; writeln!(out, " *(ptr as *const {})", rs_typ)?; writeln!(out, " }}")?; writeln!(out, "}}")?; self.emit_debug_impl(out, rs_typ, fields)?; Ok(()) } fn emit_dyn_buf_struct( &self, out: &mut O, rs_typ: &str, fields: &[Field], params_struct: Option<&ParamsStruct>, wire_sz: &Expr, doc: Option<&Doc>, ) -> io::Result<()> { if let Some(params_struct) = params_struct { params_struct.emit(out)?; } writeln!(out)?; if let Some(doc) = doc { doc.emit(out, 0)?; } writeln!(out, "pub struct {} {{", rs_typ)?; writeln!(out, " data: [u8],")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "#[allow(unused_parens)]")?; writeln!(out, "impl {} {{", rs_typ)?; writeln!( out, " pub(crate) unsafe fn from_data + ?Sized>(data: &D) -> &{} {{", rs_typ )?; if params_struct.is_none() { writeln!( out, " debug_assert_eq!(data.as_ref().len(), <&{} as base::WiredIn>::compute_wire_len(data.as_ref().as_ptr(), ()));", rs_typ )?; } writeln!( out, " &*(data.as_ref() as *const [u8] as *const {})", rs_typ )?; writeln!(out, " }}")?; writeln!(out)?; writeln!( out, " fn wire_ptr(&self) -> *const u8 {{ self.data.as_ptr() }}" )?; let compute_wire_len_stmts = self.emit_compute_offset_and_get_stmts(out, rs_typ, fields, params_struct)?; self.emit_struct_accessors(out, rs_typ, fields)?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::WiredOut for {} {{", rs_typ)?; writeln!(out, " fn wire_len(&self) -> usize {{ self.data.len() }}")?; writeln!(out)?; writeln!( out, " fn serialize(&self, wire_buf: &mut [u8]) -> usize {{" )?; writeln!( out, " (&mut wire_buf[..self.data.len()]).copy_from_slice(&self.data);" )?; writeln!(out, " self.data.len()")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::WiredIn for &{} {{", rs_typ)?; writeln!(out, " type Params = {};", params_struct.rs_typ())?; self.emit_compute_func( out, "compute_wire_len", params_struct, &compute_wire_len_stmts, )?; writeln!( out, " unsafe fn unserialize(ptr: *const u8, params: {}, offset: &mut usize) -> Self {{", params_struct.rs_typ(), )?; writeln!(out, " let sz = Self::compute_wire_len(ptr, params);")?; writeln!(out, " *offset += sz;")?; writeln!( out, " let data = std::slice::from_raw_parts(ptr, sz);" )?; writeln!(out, " {}::from_data(data)", rs_typ)?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "#[derive(Clone)]")?; writeln!(out, "pub struct {}Buf {{", rs_typ)?; writeln!(out, " data: Vec,")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl {}Buf {{", rs_typ)?; writeln!( out, " pub(crate) unsafe fn from_data(data: Vec) -> {}Buf {{", rs_typ )?; if params_struct.is_none() { writeln!( out, " debug_assert_eq!(<&{}>::compute_wire_len(data.as_ptr(), ()), data.len());", rs_typ )?; } writeln!(out, " {}Buf {{ data }}", rs_typ)?; writeln!(out, " }}")?; self.emit_struct_ctor(out, rs_typ, fields, wire_sz)?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::WiredIn for {}Buf {{", rs_typ)?; writeln!(out, " type Params = {};", params_struct.rs_typ())?; writeln!( out, " unsafe fn compute_wire_len(ptr: *const u8, params: {}) -> usize {{", params_struct.rs_typ(), )?; writeln!(out, " <&{}>::compute_wire_len(ptr, params)", rs_typ)?; writeln!(out, "}}")?; writeln!( out, " unsafe fn unserialize(ptr: *const u8, params: {}, offset: &mut usize) -> Self {{", params_struct.rs_typ(), )?; writeln!(out, " let sz = Self::compute_wire_len(ptr, params);")?; writeln!(out, " *offset += sz;")?; writeln!( out, " let data = std::slice::from_raw_parts(ptr, sz);" )?; writeln!(out, " {}Buf::from_data(data.to_vec())", rs_typ)?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl std::ops::Deref for {}Buf {{", rs_typ)?; writeln!(out, " type Target = {};", rs_typ)?; writeln!(out, " fn deref(&self) -> &Self::Target {{")?; writeln!( out, " unsafe {{ {}::from_data(&self.data) }}", rs_typ )?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!( out, "impl std::borrow::Borrow<{}> for {}Buf {{", rs_typ, rs_typ )?; writeln!(out, " fn borrow(&self) -> &{} {{", rs_typ)?; writeln!( out, " unsafe {{ {}::from_data(&self.data) }}", rs_typ )?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl std::borrow::ToOwned for {} {{", rs_typ)?; writeln!(out, " type Owned = {}Buf;", rs_typ)?; writeln!(out, " fn to_owned(&self) -> Self::Owned {{")?; writeln!(out, " {}Buf {{", rs_typ)?; writeln!(out, " data: self.data.to_vec()")?; writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; self.emit_debug_impl(out, rs_typ, fields)?; self.emit_debug_impl(out, &(rs_typ.to_string() + "Buf"), fields)?; self.emit_dyn_buf_struct_iterator(out, rs_typ, params_struct)?; Ok(()) } pub(super) fn field_compute_len_stmts( &self, field: &Field, stmts: &mut Vec, struct_offset: &Expr, ) { let struct_offset = if let Expr::Value(0) = struct_offset { String::new() } else { self.build_rs_expr(struct_offset, "", "", &[]) + " + " }; match field { Field::Field { name, struct_style: Some(StructStyle::DynBuf), params_struct, module, rs_typ, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let params_expr = self.build_params_expr( params_struct.as_ref(), module.as_ref().map(|s| s.as_str()), "", "", ); stmts.push(format!("// {}", name)); stmts.push(format!( "sz += <&{}>::compute_wire_len(ptr.add({}sz), {});", q_rs_typ, struct_offset, params_expr )); } Field::Field { wire_sz, name, is_fieldref, is_paramref, rs_typ, .. } => { stmts.push(format!("// {}", name)); if *is_fieldref || *is_paramref { stmts.push(format!( "let {} = *(ptr.add({}sz) as *const {});", name, struct_offset, rs_typ )); } stmts.push(format!( "sz += {};", self.build_rs_expr(wire_sz, "", "", &[]) )); } Field::List { name, len_expr, module, rs_typ, struct_style: Some(StructStyle::DynBuf), params_struct, is_fieldref, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); stmts.push(format!("// {}", name)); if *is_fieldref { stmts.push(format!("let mut {} = Vec::new();", name)); } stmts.push(format!( "for _ in 0 .. {} {{", self.build_rs_expr(len_expr, "", "", &[]) )); let params_expr = self.build_params_expr( params_struct.as_ref(), module.as_ref().map(|s| s.as_str()), "", "", ); if *is_fieldref { stmts.push(format!( " let len = <&{}>::compute_wire_len(ptr.add({}sz), {});", q_rs_typ, struct_offset, params_expr )); stmts.push(format!( " let data = std::slice::from_raw_parts(ptr.add({}sz), len);", struct_offset )); stmts.push(format!(" {}.push({}::from_data(data));", name, q_rs_typ)); stmts.push(" sz += len;".to_string()); } else { stmts.push(format!( " sz += <&{}>::compute_wire_len(ptr.add({}sz), {});", q_rs_typ, struct_offset, params_expr )); } stmts.push("}".to_string()); } Field::List { name, wire_sz, is_fieldref, len_expr, module, rs_typ, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); stmts.push(format!("// {}", name)); if *is_fieldref { stmts.push(format!("let {} = {{", name)); stmts.push(format!( " let len = {};", self.build_rs_expr(len_expr, "", "", &[]) )); stmts.push(format!( " let data = ptr.add({}sz) as *const {};", struct_offset, q_rs_typ )); stmts.push(format!( " sz += len * std::mem::size_of::<{}>();", q_rs_typ )); stmts.push(" std::slice::from_raw_parts(data, len)".to_string()); stmts.push("};".to_string()); } else { stmts.push(format!( "sz += {};", self.build_rs_expr(wire_sz, "", "", &[]) )); } } Field::Switch { name, module, rs_typ, params_struct, is_mask, .. } => { let params_expr = self.build_params_expr( Some(params_struct), module.as_ref().map(|m| m.as_str()), "", "", ); let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let impl_type = if *is_mask { format!(">", q_rs_typ) } else { q_rs_typ }; stmts.push(format!("// {}", name)); stmts.push(format!( "sz += {}::compute_wire_len(ptr.add({}sz), {});", impl_type, struct_offset, params_expr )); } Field::Pad { wire_sz, .. } => { stmts.push("// pad".to_string()); stmts.push(format!( "sz += {};", self.build_rs_expr(wire_sz, "", "", &[]) )); } Field::AlignPad { wire_sz: Expr::AlignPad(align, _), .. } => { stmts.push("// align pad".to_string()); stmts.push(format!("sz += base::align_pad(sz, {});", align)); } f => unreachable!("missed handling of field in compute_wire_len: {:?}", f), } } pub(super) fn emit_compute_func( &self, out: &mut O, fname: &str, params_struct: Option<&ParamsStruct>, stmts: &[String], ) -> io::Result<()> { writeln!(out)?; writeln!( out, " unsafe fn {}(ptr: *const u8, {}params: {}) -> usize {{", fname, if params_struct.is_none() { "_" } else { "" }, params_struct.rs_typ(), )?; if let Some(params_struct) = params_struct { writeln!(out, " let {} {{", params_struct.rs_typ)?; for p in ¶ms_struct.params { writeln!(out, "{}{},", cg::ind(3), p)?; } writeln!(out, " }} = params;")?; } for s in stmts { writeln!(out, " {}", s)?; } writeln!(out, " sz")?; writeln!(out, " }}")?; Ok(()) } /// Get the statements for each field size calculation, and emit offset function /// each time we hit a field that requires an offset function. /// The resulting statements can be used to emit the compute_wire_len function pub(super) fn emit_compute_offset_and_get_stmts( &self, out: &mut O, _struct_rs_typ: &str, fields: &[Field], params_struct: Option<&ParamsStruct>, ) -> io::Result> { let mut stmts = vec!["let mut sz = 0;".to_string()]; for f in fields { if let Field::Field { name, need_compute_offset: true, .. } | Field::List { name, need_compute_offset: true, .. } | Field::Switch { name, need_compute_offset: true, .. } = f { let fname = format!("compute_{}_offset", name); self.emit_compute_func(out, &fname, params_struct, &stmts)?; } self.field_compute_len_stmts(f, &mut stmts, &Expr::Value(0)); } Ok(stmts) } fn emit_struct_ctor( &self, out: &mut O, struct_rs_typ: &str, fields: &[Field], wire_sz: &Expr, ) -> io::Result<()> { if fields.len() == 1 && matches!(fields[0], Field::Pad { .. }) { return Ok(()); } let is_fixed = matches!(wire_sz, Expr::Value(_)); writeln!(out)?; writeln!( out, "{}/// Construct a new [{}{}].", cg::ind(1), struct_rs_typ, if is_fixed { "" } else { "Buf" } )?; writeln!( out, "{}#[allow(unused_assignments, unused_unsafe)]", cg::ind(1) )?; writeln!(out, "{}pub fn new(", cg::ind(1))?; for f in fields { match f { Field::Field { name, module, rs_typ, is_fieldref, struct_style: Some(StructStyle::DynBuf), .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if !is_fieldref { writeln!(out, "{}{}: &{},", cg::ind(2), name, q_rs_typ)?; } } Field::Field { name, module, rs_typ, is_fieldref, r#enum, mask, .. } => { let q_rs_typ = enum_mask_qualified_rs_typ(module, rs_typ, r#enum, mask); if !is_fieldref || request_fieldref_emitted(name, fields, false) { writeln!(out, "{}{}: {},", cg::ind(2), name, q_rs_typ)?; } } Field::List { name, module, rs_typ, struct_style: Some(StructStyle::DynBuf), len_expr: Expr::Value(len), .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!(out, "{}{}: [{}Buf; {}],", cg::ind(2), name, q_rs_typ, len)?; } Field::List { name, rs_typ, len_expr: Expr::Value(len), .. } if rs_typ == "char" => { writeln!(out, "{}{}: &[u8; {}],", cg::ind(2), name, len)?; } Field::List { name, module, rs_typ, len_expr: Expr::Value(len), .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!(out, "{}{}: &[{}; {}],", cg::ind(2), name, q_rs_typ, len)?; } Field::List { name, module, rs_typ, struct_style: Some(StructStyle::DynBuf), .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!(out, "{}{}: &[{}Buf],", cg::ind(2), name, q_rs_typ)?; } Field::List { name, rs_typ, .. } if rs_typ == "char" => { writeln!(out, "{}{}: &[u8],", cg::ind(2), name)?; } Field::List { name, module, rs_typ, r#enum, mask, .. } => { let q_rs_typ = enum_mask_qualified_rs_typ(module, rs_typ, r#enum, mask); writeln!(out, "{}{}: &[{}],", cg::ind(2), name, q_rs_typ)?; } Field::Switch { name, module, rs_typ, is_mask, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if *is_mask { writeln!(out, "{}{}: &[{}],", cg::ind(2), name, q_rs_typ)?; } else { writeln!(out, "{}{}: {},", cg::ind(2), name, q_rs_typ)?; } } Field::Pad { .. } => {} Field::AlignPad { .. } => {} f => unreachable!("struct ctor arguments: {:#?}", f), } } writeln!( out, "{}) -> {}{} {{", cg::ind(1), struct_rs_typ, if is_fixed { "" } else { "Buf" } )?; writeln!(out, "{}unsafe {{", cg::ind(3))?; if let Expr::Value(sz) = wire_sz { writeln!(out, "{}let mut wire_buf = [0u8; {}];", cg::ind(3), sz)?; } else { writeln!(out, "{}let mut wire_sz = 0usize;", cg::ind(3))?; for f in fields { match f { Field::Field { name, wire_sz: Expr::Value(sz), .. } => { writeln!(out, "{}wire_sz += {}; // {}", cg::ind(3), sz, name)?; } Field::Field { name, .. } => { writeln!(out, "{}wire_sz += {}.wire_len();", cg::ind(3), name)?; } Field::List { name, rs_typ, .. } if rs_typ == "char" => { writeln!(out, "{}wire_sz += {}.len();", cg::ind(3), name)?; } Field::List { name, rs_typ, mask: Some(..), .. } => { writeln!( out, "{}wire_sz += {}.len() * std::mem::size_of::<{}>();", cg::ind(3), name, rs_typ, )?; } Field::List { name, .. } => { writeln!( out, "{}wire_sz += {}.iter().map(|el| el.wire_len()).sum::();", cg::ind(3), name )?; } Field::Switch { name, is_mask: false, .. } => { writeln!(out, "{}wire_sz += {}.wire_len();", cg::ind(3), name)?; } Field::Pad { wire_sz: Expr::Value(sz), .. } => { writeln!(out, "{}wire_sz += {}; // pad", cg::ind(3), sz)?; } Field::AlignPad { wire_sz: Expr::AlignPad(align, ..), .. } => { writeln!( out, "{}wire_sz += base::align_pad(wire_sz, {});", cg::ind(3), align )?; } f => unreachable!("struct ctor wire_sz: {:#?}", f), } } writeln!(out, "{}let mut wire_buf = vec![0u8; wire_sz];", cg::ind(3))?; } writeln!(out, "{}let mut wire_off = 0usize;", cg::ind(3))?; writeln!(out)?; for f in fields { match f { Field::Field { name, is_fieldref, struct_style: Some(StructStyle::DynBuf), .. } => { if !is_fieldref { writeln!( out, "{}wire_off += {}.serialize(&mut wire_buf[wire_off ..]);", cg::ind(3), name )?; } else { writeln!(out, "// TODO fieldref")?; } } Field::Field { name, module, rs_typ, wire_sz: Expr::Value(sz), r#enum, mask, is_fieldref, is_prop_format, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let fieldref_value = if *is_fieldref { fieldref_get_value(name, fields, false, "") } else { None }; let value_expr; if let Some(value) = fieldref_value { value_expr = format!("({} as {})", value, q_rs_typ); } else if *is_prop_format { value_expr = "P::FORMAT".to_string(); } else if mask.is_some() && rs_typ == "u32" { value_expr = format!("{}.bits()", name); } else if mask.is_some() { value_expr = format!("({}.bits() as {})", name, q_rs_typ); } else if r#enum.is_some() && rs_typ == "u32" { value_expr = format!("std::mem::transmute::<_, u32>({})", name); } else if r#enum.is_some() && rs_typ == "bool" { value_expr = format!("(std::mem::transmute::<_, u32>({}) as u{})", name, 8 * sz); } else if r#enum.is_some() { value_expr = format!("(std::mem::transmute::<_, u32>({}) as {})", name, q_rs_typ); } else if rs_typ == "bool" { value_expr = format!("(if {} {{ 1u{} }} else {{ 0u{} }})", name, 8 * sz, 8 * sz); } else { value_expr = name.to_string(); } writeln!( out, "{}wire_off += {}.serialize(&mut wire_buf[wire_off .. ]);", cg::ind(3), value_expr, )?; } Field::List { name, rs_typ, .. } if rs_typ == "char" => { writeln!( out, "{}wire_buf[wire_off .. wire_off + {}.len()].copy_from_slice({});", cg::ind(3), name, name )?; writeln!(out, "{}wire_off += {}.len();", cg::ind(3), name)?; } Field::List { name, rs_typ, mask, .. } => { let transform = if mask.is_some() { format!(".iter().map(|el| el.bits() as {})", rs_typ) } else { String::new() }; writeln!(out, "{}for el in {}{} {{", cg::ind(3), name, transform)?; writeln!( out, "{}wire_off += el.serialize(&mut wire_buf[wire_off ..]);", cg::ind(4) )?; writeln!(out, "{}}}", cg::ind(3))?; } Field::Switch { name, .. } => { writeln!( out, "{}wire_off += {}.serialize(&mut wire_buf[wire_off ..]);", cg::ind(3), name )?; } Field::Pad { wire_sz: Expr::Value(sz), .. } => { writeln!(out, "{}wire_off += {}; // pad", cg::ind(3), sz)?; } Field::AlignPad { wire_sz: Expr::AlignPad(align, ..), .. } => { writeln!( out, "{}wire_off += base::align_pad(wire_off, {});", cg::ind(3), align )?; } f => unreachable!("struct ctor arguments: {:#?}", f), } } writeln!(out)?; writeln!( out, "{}{}{} {{ data: wire_buf }}", cg::ind(3), struct_rs_typ, if is_fixed { "" } else { "Buf" } )?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; Ok(()) } pub(super) fn emit_struct_accessors( &self, out: &mut O, struct_rs_typ: &str, fields: &[Field], ) -> io::Result<()> { for f in fields { if field_is_fd(f) { // fd fields are handled directly in request.rs continue; } if self.handle_client_message_data(out, struct_rs_typ, f)? { continue; } if self.handle_randr_notify_data(out, struct_rs_typ, f)? { continue; } writeln!(out)?; match f { Field::Field { name, rs_typ, wire_off, doc, .. } if rs_typ == "bool" => { if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub fn {}(&self) -> bool {{", name)?; writeln!( out, " let val = unsafe {{ *(self.wire_ptr().add({})) }};", self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!(out, " val != 0")?; writeln!(out, " }}")?; } Field::Field { name, module, rs_typ, wire_off, r#enum, mask, doc, .. } if r#enum.is_some() || mask.is_some() => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let ret_q_rs_typ = r#enum.as_ref().or(mask.as_ref()).unwrap(); let ret_q_rs_typ = { let (module, rs_typ) = ret_q_rs_typ; (module, rs_typ).qualified_rs_typ() }; if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub fn {}(&self) -> {} {{", name, ret_q_rs_typ)?; writeln!(out, " unsafe {{")?; writeln!( out, " let offset = {};", self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!( out, " let ptr = self.wire_ptr().add(offset) as *const {};", q_rs_typ )?; writeln!(out, " let val = *ptr as u32;")?; writeln!( out, " std::mem::transmute::(val)", ret_q_rs_typ, )?; writeln!(out, " }}")?; writeln!(out, " }}")?; } Field::Field { name, module, rs_typ, wire_off, is_xid: true, is_union: true, doc, .. } => { // XID unions constructed out of context require a `Unknown` variant let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " fn {}(&self) -> {} {{", name, q_rs_typ)?; writeln!(out, " unsafe {{")?; writeln!( out, "{}let offset = {};", cg::ind(3), self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!( out, "{}let res_id = *(self.wire_ptr().add(offset) as *const u32);", cg::ind(3) )?; writeln!(out, "{}{}::Unknown(res_id)", cg::ind(3), q_rs_typ)?; writeln!(out, " }}")?; writeln!(out, " }}")?; } Field::Field { name, module, rs_typ, wire_off, is_fieldref, is_copy: true, doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); // NOTE: fieldref fields usually refer to lengths of lists, so in those cases // it's not necessary to make them public as we expose lists as Rust // slices. However, when those fields are named width or height, they're // not simply the length of a slice, and they must be exposed so that the // user can treat the slice as an image. The format field is important for // GetPropertyReply where the value getter would panic if formats mismatch. let visibility = if *is_fieldref && !(name == "width" || name == "height" || name == "format") { "" } else { "pub " }; if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, "{}{}fn {}(&self) -> {} {{", cg::ind(1), visibility, name, q_rs_typ )?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!( out, "{}let offset = {};", cg::ind(3), self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!( out, "{}let ptr = self.wire_ptr().add(offset) as *const {};", cg::ind(3), q_rs_typ )?; writeln!(out, " *ptr")?; writeln!(out, " }}")?; writeln!(out, " }}")?; } Field::Field { name, module, rs_typ, wire_off, wire_sz, is_copy: false, is_union: false, struct_style, params_struct, doc, need_compute_offset, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let params_expr = self.build_params_expr( params_struct.as_ref(), module.as_ref().map(|s| s.as_str()), "self.", "()", ); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub fn {}(&self) -> &{} {{", name, q_rs_typ)?; writeln!(out, "{}unsafe {{", cg::ind(2))?; if *need_compute_offset { writeln!( out, "{}let offset = Self::compute_{}_offset(self.wire_ptr(), {});", cg::ind(3), name, params_expr )?; } else { writeln!( out, "{}let offset = {};", cg::ind(3), self.build_rs_expr(wire_off, "self.", "()", fields) )?; } if let Some(StructStyle::DynBuf) = struct_style { writeln!( out, "{}let len = <&{}>::compute_wire_len(self.wire_ptr().add(offset), {});", cg::ind(3), q_rs_typ, params_expr )?; } else { writeln!( out, " let len = {};", self.build_rs_expr(wire_sz, "self.", "()", fields) )?; } writeln!(out, " let data = std::slice::from_raw_parts(self.wire_ptr().add(offset), len);")?; writeln!(out, " {}::from_data(data)", q_rs_typ,)?; writeln!(out, " }}")?; writeln!(out, " }}")?; } Field::Field { name, module, rs_typ, wire_off, is_copy: false, is_union: true, need_compute_offset, doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!(out, " pub fn {}(&self) -> {} {{", name, q_rs_typ)?; writeln!(out, " unsafe {{")?; if *need_compute_offset { writeln!( out, " let mut offset = Self::compute_{}_offset(self.wire_ptr());", name )?; } else { writeln!( out, " let mut offset = {};", self.build_rs_expr(wire_off, "self.", "()", fields) )?; } writeln!( out, "{}{}::unserialize(self.wire_ptr().add(offset), (), &mut offset)", cg::ind(3), q_rs_typ )?; writeln!(out, " }}")?; writeln!(out, " }}")?; } Field::List { name, wire_off, len_expr, is_prop: true, doc, .. } => { if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, "{}pub fn {}(&self) -> &[P] {{", cg::ind(1), name )?; writeln!(out, "{}unsafe {{", cg::ind(2))?; // #154: format can be zero when len is zero writeln!( out, "{}let len = {};", cg::ind(3), self.build_rs_expr(len_expr, "self.", "()", fields) )?; writeln!(out, "{}if len == 0 {{ return &[]; }}", cg::ind(3))?; writeln!( out, "{}assert_eq!(self.format(), P::FORMAT, \"mismatched format of {}::{}::{}\");", cg::ind(3), self.xcb_mod, struct_rs_typ, name )?; writeln!( out, "{}let offset = {};", cg::ind(3), self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!( out, "{}let len = len / std::mem::size_of::

();", cg::ind(3) )?; writeln!( out, "{}let ptr = self.wire_ptr().add(offset) as *const P;", cg::ind(3), )?; writeln!(out, "{}std::slice::from_raw_parts(ptr, len)", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; } Field::List { name, rs_typ, wire_off, len_expr: Expr::Value(len), doc, .. } if rs_typ == "char" => { writeln!(out)?; // According X protocol spec, strings are latin-1 encoded // Therefore we return them only as bytes, and user may convert them as string // see rust-xcb#34 and rust-xcb#96 if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, "{}pub fn {}(&self) -> &Lat1StrF<{}> {{", cg::ind(1), name, len )?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!( out, "{}let offset = {};", cg::ind(3), self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!( out, "{}&*(self.wire_ptr().add(offset) as *const Lat1StrF<{}>)", cg::ind(3), len )?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; } Field::List { name, rs_typ, wire_off, len_expr, doc, .. } if rs_typ == "char" => { let params = len_expr.params_str(); writeln!(out)?; // According X protocol spec, strings are latin-1 encoded // Therefore we return them only as bytes, and user may convert them as string // see rust-xcb#34 and rust-xcb#96 if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, "{}pub fn {}(&self{}) -> &Lat1Str {{", cg::ind(1), name, params )?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!( out, "{}let offset = {};", cg::ind(3), self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!( out, "{}let len = {} as _;", cg::ind(3), self.build_rs_expr(len_expr, "self.", "()", fields) )?; writeln!(out, "{}let ptr = self.wire_ptr().add(offset);", cg::ind(3))?; writeln!( out, "{}let bytes = std::slice::from_raw_parts(ptr, len);", cg::ind(3) )?; writeln!(out, "{}Lat1Str::from_bytes(bytes)", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; } Field::List { name, module, rs_typ, wire_off, len_expr: Expr::Value(len), struct_style: None, doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, " pub fn {}(&self) -> &[{}; {}] {{", name, q_rs_typ, len )?; writeln!(out, " unsafe {{")?; writeln!( out, " let offset = {};", self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!( out, " let ptr = self.wire_ptr().add(offset) as *const [{}; {}];", q_rs_typ, len )?; writeln!(out, " &*ptr")?; writeln!(out, " }}")?; writeln!(out, " }}")?; } Field::List { name, rs_typ, wire_off, len_expr, doc, r#enum, mask, .. } if rs_typ == "u32" && (r#enum.is_some() || mask.is_some()) => { let q_rs_typ = r#enum.as_ref().or(mask.as_ref()).unwrap(); let q_rs_typ = { let (module, rs_typ) = q_rs_typ; (module, rs_typ).qualified_rs_typ() }; if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, "{}pub fn {}(&self) -> &[{}] {{", cg::ind(1), name, q_rs_typ )?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!( out, "{}let offset = {};", cg::ind(3), self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!( out, "{}let len = {};", cg::ind(3), self.build_rs_expr(len_expr, "self.", "()", fields) )?; writeln!( out, "{}let ptr = self.wire_ptr().add(offset) as *const {};", cg::ind(3), q_rs_typ )?; writeln!(out, "{}std::slice::from_raw_parts(ptr, len)", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; } Field::List { r#enum, mask, .. } if r#enum.is_some() || mask.is_some() => { unimplemented!("return iterator of mask or enum"); } Field::List { name, module, rs_typ, wire_off, len_expr, struct_style: None, doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let params = len_expr.params_str(); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, " pub fn {}(&self{}) -> &[{}] {{", name, params, q_rs_typ )?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!( out, "{}let offset = {};", cg::ind(3), self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!( out, "{}let len = {};", cg::ind(3), self.build_rs_expr(len_expr, "self.", "()", fields) )?; writeln!( out, "{}let ptr = self.wire_ptr().add(offset) as *const {};", cg::ind(3), q_rs_typ )?; writeln!(out, "{}std::slice::from_raw_parts(ptr, len)", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; } Field::List { name, module, rs_typ, wire_off, len_expr: Expr::UntilEnd, struct_style: Some(StructStyle::WireLayout | StructStyle::FixBuf), doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, "{}pub fn {}(&self) -> &[{}] {{", cg::ind(1), name, q_rs_typ )?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!( out, "{}let offset = {};", cg::ind(3), self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!(out, "{}assert_eq!((self.wire_len() as usize - offset) % std::mem::size_of::<{}>(), 0);", cg::ind(3), q_rs_typ)?; writeln!(out, "{}let len = (self.wire_len() as usize - offset) / std::mem::size_of::<{}>();", cg::ind(3), q_rs_typ)?; writeln!(out, "{}std::slice::from_raw_parts(self.wire_ptr().add(offset) as *const {}, len)", cg::ind(3), q_rs_typ)?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; } Field::List { name, module, rs_typ, wire_off, len_expr, struct_style: Some(StructStyle::WireLayout | StructStyle::FixBuf), doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let params = len_expr.params_str(); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, " pub fn {}(&self{}) -> &[{}] {{", name, params, q_rs_typ )?; writeln!(out, " unsafe {{")?; writeln!( out, " let offset = {};", self.build_rs_expr(wire_off, "self.", "()", fields) )?; writeln!( out, " let len = {} as _;", self.build_rs_expr(len_expr, "self.", "()", fields) )?; writeln!( out, " let ptr = self.wire_ptr().add(offset) as *const {};", q_rs_typ )?; writeln!(out, " std::slice::from_raw_parts(ptr, len)",)?; writeln!(out, " }}")?; writeln!(out, " }}")?; } Field::List { name, module, rs_typ, wire_off, len_expr, struct_style: Some(StructStyle::DynBuf), params_struct, need_compute_offset, doc, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let params = len_expr.params_str(); let params_expr = self.build_params_expr( params_struct.as_ref(), module.as_ref().map(|s| s.as_str()), "self.", "()", ); if let Some(doc) = doc { doc.emit(out, 1)?; } writeln!( out, " pub fn {}(&self{}) -> {}Iterator {{", name, params, q_rs_typ )?; writeln!(out, " unsafe {{")?; if *need_compute_offset { writeln!( out, "{}let offset = Self::compute_{}_offset(self.wire_ptr(), {});", cg::ind(3), name, params_expr )?; } else { writeln!( out, "{}let offset = {};", cg::ind(3), self.build_rs_expr(wire_off, "self.", "()", fields) )?; } writeln!(out, "{}{}Iterator {{", cg::ind(3), q_rs_typ)?; writeln!(out, "{} params: {},", cg::ind(3), params_expr)?; writeln!( out, "{} rem: {},", cg::ind(3), self.build_rs_expr(len_expr, "self.", "()", fields) )?; writeln!(out, "{} ptr: self.wire_ptr().add(offset),", cg::ind(3))?; writeln!(out, "{} phantom: std::marker::PhantomData,", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(3))?; writeln!(out, " }}")?; writeln!(out, " }}")?; } Field::Switch { name, module, rs_typ, params_struct, wire_off, is_mask, doc, .. } => { if let Some(doc) = doc { doc.emit(out, 1)?; } let (impl_typ, return_typ) = if *is_mask { ( Cow::from(format!(">", rs_typ)), Cow::from(format!("Vec<{}>", rs_typ)), ) } else { (Cow::from(rs_typ), Cow::from(rs_typ)) }; writeln!( out, "{}pub fn {}(&self) -> {} {{", cg::ind(1), name, return_typ )?; // must define all params locally as usize because build_params_expr // cannot know if a field is a mask or not for p in ¶ms_struct.params { let mut caught = false; for f in fields { match f { Field::Field { name, mask: Some(_), .. } if name == p => { writeln!( out, "{}let {} = self.{}().bits();", cg::ind(2), name, name )?; caught = true; break; } Field::Field { name, .. } if name == p => { writeln!(out, "{}let {} = self.{}();", cg::ind(2), name, name)?; caught = true; break; } _ => {} } } assert!(caught); } let params_expr = self.build_params_expr( Some(params_struct), module.as_ref().map(|s| s.as_str()), "", "", ); let offset_expr = self.build_rs_expr(wire_off, "self.", "()", fields); writeln!(out, "{}let params = {};", cg::ind(2), params_expr)?; writeln!(out, "{}let mut offset = {};", cg::ind(2), offset_expr)?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!(out, "{}{}::unserialize(", cg::ind(3), impl_typ)?; writeln!( out, "{}self.wire_ptr().add(offset), params, &mut offset", cg::ind(4), )?; writeln!(out, "{})", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; } Field::Pad { .. } => {} Field::AlignPad { .. } => {} f => unreachable!("{:#?}", f), } } Ok(()) } // specific handling of ClientMessageEvent::data() // This is nearly static function definition. We could possibly directly define that in the // xproto module in lib.rs fn handle_client_message_data( &self, out: &mut O, struct_rs_typ: &str, field: &Field, ) -> io::Result { if self.xcb_mod == "xproto" && struct_rs_typ == "ClientMessageEvent" && matches!(field, Field::Field{name, ..} if name == "data") { writeln!(out)?; writeln!( out, "{}pub fn data(&self) -> ClientMessageData {{", cg::ind(1) )?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!(out, "{}match self.format() {{", cg::ind(3))?; for sz in [8, 16, 32] { writeln!( out, "{}{} => ClientMessageData::Data{} (", cg::ind(4), sz, sz )?; writeln!(out, "{}std::slice::from_raw_parts(", cg::ind(5))?; writeln!( out, "{}self.wire_ptr().add(12) as *const u{}, {}", cg::ind(6), sz, (20 * 8) / sz )?; writeln!(out, "{}).try_into().unwrap()", cg::ind(5))?; writeln!(out, "{}),", cg::ind(4))?; } writeln!( out, "{}format => unreachable!(\"invalid ClientMessageEvent format: {{}}\", format),", cg::ind(4) )?; writeln!(out, "{}}}", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; Ok(true) } else { Ok(false) } } fn handle_randr_notify_data( &self, out: &mut O, struct_rs_typ: &str, field: &Field, ) -> io::Result { if self.xcb_mod == "randr" && struct_rs_typ == "NotifyEvent" && matches!(field, Field::Field{name, ..} if name == "u") { writeln!(out)?; writeln!(out, "{}pub fn u(&self) -> NotifyData {{", cg::ind(1))?; writeln!(out, "{}unsafe {{", cg::ind(2))?; writeln!(out, "{}match self.sub_code() {{", cg::ind(3))?; for code in RANDR_SUBCODES { writeln!( out, "{}Notify::{} => NotifyData::{}(", cg::ind(4), code.0, code.2 )?; writeln!(out, "{}*{}::from_data(", cg::ind(5), code.1)?; writeln!( out, "{}std::slice::from_raw_parts(self.wire_ptr().add(4), 28)", cg::ind(6) )?; writeln!(out, "{})", cg::ind(5))?; writeln!(out, "{}),", cg::ind(4))?; } writeln!(out, "{}}}", cg::ind(3))?; writeln!(out, "{}}}", cg::ind(2))?; writeln!(out, "{}}}", cg::ind(1))?; Ok(true) } else { Ok(false) } } pub(super) fn emit_debug_impl( &self, out: &mut O, rs_typ: &str, fields: &[Field], ) -> io::Result<()> { writeln!(out)?; writeln!(out, "impl std::fmt::Debug for {} {{", rs_typ)?; writeln!( out, " fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{" )?; for f in fields { if let Field::List { name, is_prop: true, .. } = f { writeln!( out, "{}let {}: Box = match self.format() {{", cg::ind(2), name )?; for format in [8, 16, 32] { writeln!( out, "{}{} => Box::new(self.{}::()),", cg::ind(3), format, name, format )?; } // Format of zero indicates a null length, in which case the type doesn't matter. // If length happens to be non-zero, the method will panic. writeln!(out, "{}0 => Box::new(self.{}::()),", cg::ind(3), name)?; writeln!( out, "{}format => unreachable!(\"impossible prop format: {{}}\", format),", cg::ind(3) )?; writeln!(out, "{}}};", cg::ind(2))?; } } writeln!(out, " f.debug_struct(\"{}\")", rs_typ)?; for f in fields { match f { Field::Field { name, .. } => { writeln!(out, "{}.field(\"{}\", &self.{}())", cg::ind(3), name, name)?; } Field::List { name, is_prop: true, .. } => { writeln!(out, "{}.field(\"{}\", &*{})", cg::ind(3), name, name)?; } Field::List { name, wire_sz, .. } if wire_sz.params().is_empty() => { writeln!(out, "{}.field(\"{}\", &self.{}())", cg::ind(3), name, name)?; } Field::List { .. } => { // TODO } Field::Switch { name, .. } => { writeln!(out, "{}.field(\"{}\", &self.{}())", cg::ind(3), name, name)?; } Field::Pad { wire_sz: Expr::Value(sz), .. } => { writeln!(out, "{}.field(\"pad\", &{})", cg::ind(3), sz)?; } Field::AlignPad { wire_sz: Expr::AlignPad(sz, _), .. } => { writeln!(out, "{}.field(\"align_pad\", &{})", cg::ind(3), sz)?; } _ => {} } } writeln!(out, " .finish()")?; writeln!(out, " }}")?; writeln!(out, "}}")?; Ok(()) } fn emit_dyn_buf_struct_iterator( &self, out: &mut O, rs_typ: &str, params_struct: Option<&ParamsStruct>, ) -> io::Result<()> { writeln!(out)?; writeln!(out, "#[derive(Clone)]")?; writeln!(out, "pub struct {}Iterator<'a> {{", rs_typ)?; writeln!(out, " pub(crate) params: {},", params_struct.rs_typ())?; writeln!(out, " pub(crate) rem: usize,")?; writeln!(out, " pub(crate) ptr: *const u8,")?; writeln!( out, " pub(crate) phantom: std::marker::PhantomData<&'a {}>,", rs_typ )?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl<'a> Iterator for {}Iterator<'a> {{", rs_typ)?; writeln!(out, " type Item = &'a {};", rs_typ)?; writeln!(out)?; writeln!( out, " fn next(&mut self) -> std::option::Option {{" )?; writeln!(out, " if self.rem == 0 {{")?; writeln!(out, " None")?; writeln!(out, " }} else {{ unsafe {{")?; writeln!(out, " self.rem -= 1;")?; writeln!(out, " let mut offset = 0;")?; writeln!( out, " let res = <&{}>::unserialize(self.ptr, self.params, &mut offset);", rs_typ )?; writeln!(out, " self.ptr = self.ptr.add(offset);")?; writeln!(out, " Some(res)")?; writeln!(out, " }}}}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!( out, "impl<'a> std::fmt::Debug for {}Iterator<'a> {{", rs_typ )?; writeln!( out, " fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{" )?; writeln!(out, " f.debug_list().entries(self.clone()).finish()")?; writeln!(out, " }}")?; writeln!(out, "}}")?; Ok(()) } } struct FieldInfo { name: String, module: Option, typ: String, rs_typ: String, wire_sz: Expr, has_wire_layout: bool, struct_style: Option, params_struct: Option, r#enum: Option<(Option, String)>, mask: Option<(Option, String)>, doc: Option, is_union: bool, union_typefield: Option, is_xid: bool, is_mask: bool, } pub(super) const RANDR_SUBCODES: &[(&str, &str, &str)] = &[ ("CrtcChange", "CrtcChange", "Cc"), ("OutputChange", "OutputChange", "Oc"), ("OutputProperty", "OutputProperty", "Op"), ("ProviderChange", "ProviderChange", "Pc"), ("ProviderProperty", "ProviderProperty", "Pp"), ("ResourceChange", "ResourceChange", "Rc"), ("Lease", "LeaseNotify", "Lc"), ]; pub(super) fn enum_mask_qualified_rs_typ( module: &Option, rs_typ: &str, r#enum: &Option<(Option, String)>, mask: &Option<(Option, String)>, ) -> String { let mod_rs_typ = r#enum .as_ref() .or(mask.as_ref()) .map(|(m, t)| (m.as_ref().map(|m| m.as_str()), t.as_str())) .or_else(|| Some((module.as_ref().map(|m| m.as_str()), rs_typ))) .unwrap(); mod_rs_typ.qualified_rs_typ() } pub(super) fn make_field(name: String, typ: String) -> ir::Field { ir::Field::Field { name, typ, r#enum: None, mask: None, altenum: None, altmask: None, } } xcb-1.2.2/build/cg/switch.rs000064400000000000000000001453771046102023000137620ustar 00000000000000use super::r#enum::{self}; use super::r#struct::{ParamsStruct, ResolvedFields}; use super::{CodeGen, Expr, Field, SwitchCase, TypeInfo}; use crate::cg::doc::Doc; use crate::cg::r#struct::enum_mask_qualified_rs_typ; use crate::cg::request::{fieldref_get_value, request_fieldref_emitted}; use crate::cg::{self, util, QualifiedRsTyp, StructStyle}; use crate::ir; use std::io::{self, Write}; impl CodeGen { #[allow(clippy::too_many_arguments)] pub(super) fn resolve_switch( &mut self, parent_rs_typ: &str, parent_switch: &str, name: &str, expr: &Expr, wire_off: &Expr, cases: &[ir::SwitchCase], prev_fields: &mut [Field], need_compute_offset: bool, doc: Option<&Doc>, ) -> Field { // switch types are made-up, so we create the typ (same as intended rs_typ) let typ = parent_rs_typ.to_string() + &util::tit_cap(name); let mut switch_rs_typ = typ.clone(); let mut module = None; let mut emit = true; for ex in &self.switch_exceptions { if ex.module == self.xcb_mod && ex.rs_typ == switch_rs_typ { switch_rs_typ = ex.new_rs_typ.to_string(); module = ex.new_module.map(str::to_owned); emit = ex.emit; break; } } let doc = self.doc_lookup_field(doc, name); let is_mask = { let check1 = cases.iter().all(|sc| sc.bit); let check2 = cases.iter().any(|sc| sc.bit); assert_eq!(check1, check2, "switch cannot mix case and bitcase"); check1 }; let name = util::to_snake_case(name); let switch_name = if parent_switch.is_empty() { name.clone() } else { format!("{}_{}", parent_switch, name) }; let mut param_fields = Vec::new(); let cases: Vec = cases .iter() .map(|sc| { let ir::SwitchCase { name, fields, exprs, .. } = sc; let name = name .as_ref() .map(|n| cg::rust_type_name(n)) .unwrap_or_else(|| match &exprs[0] { ir::Expr::EnumRef { item, .. } => r#enum::map_enum_item_name(item), _ => unreachable!("switch expressions not enum ref"), }); let exprs = exprs .iter() .map(|e| self.resolve_expr(e)) .collect::>(); let ResolvedFields { fields, unresolved_fieldrefs: mut uf, .. } = self.resolv_struct_fields(&switch_rs_typ, &switch_name, fields, None); param_fields.append(&mut uf); SwitchCase { name, fields, exprs, } }) .collect(); let expr_fields = expr.fieldrefs(); assert!( !expr_fields.is_empty(), "switch {}::{} has no fieldref expr: {:?}", parent_rs_typ, name, expr, ); let maskenum = { let (r#enum, mask) = prev_fields .iter() .find_map(|f| match f { Field::Field { name, r#enum, mask, .. } if name == expr_fields[0] => Some((r#enum, mask)), _ => None, }) .unwrap_or_else(|| { panic!( "cannot find field {} for switch {}::{}", expr_fields[0], parent_rs_typ, name ) }); assert!(r#enum.is_some() && !is_mask || mask.is_some() && is_mask); let maskenum = r#enum.as_ref().unwrap_or_else(|| mask.as_ref().unwrap()); maskenum.clone() }; // { // let enumtyp = self.typinfos.get_mut(&maskenum.1); // if let Some(TypeInfo::Bitflags { ref mut rs_typ, .. }) = enumtyp { // switch_rs_typ = rs_typ.clone() + "Mask"; // mem::swap(&mut switch_rs_typ, rs_typ); // } // } param_fields.sort(); param_fields.dedup(); let params = expr_fields .into_iter() .map(str::to_owned) .chain(param_fields.into_iter()) .collect(); let params_struct = ParamsStruct { rs_typ: switch_rs_typ.clone() + "Params", params, }; let typinfo = TypeInfo::Switch { module: module.clone(), rs_typ: switch_rs_typ.clone(), wire_sz: Expr::Unknown("switch len".into()), // FIXME switch size expr (may be a method?) maskenum, params_struct: params_struct.clone(), expr: expr.clone(), cases, is_mask, emit, }; let field = Field::Switch { name, module, rs_typ: switch_rs_typ, wire_sz: Expr::Unknown("switch len".to_string()), wire_off: wire_off.clone(), params_struct, expr: expr.clone(), is_mask, need_compute_offset, doc, }; self.register_typ(typ, typinfo); field } #[allow(clippy::too_many_arguments)] pub(super) fn emit_switch( &self, out: &mut O, rs_typ: &str, expr: &Expr, cases: &[SwitchCase], maskenum: &(Option, String), params_struct: &ParamsStruct, is_mask: bool, ) -> io::Result<()> { params_struct.emit(out)?; writeln!(out)?; // implementing Eq and Ord only for xproto switches which are simple enough if is_mask && self.xcb_mod == "xproto" { writeln!(out, "#[derive(Clone, Debug, PartialEq, Eq)]")?; } else { writeln!(out, "#[derive(Clone, Debug)]")?; } writeln!(out, "pub enum {} {{", rs_typ)?; for c in cases { if visible_fields_len(&c.fields) == 1 { let is_input_info_info = rs_typ == "InputInfoInfo"; assert!(!c.fields.is_empty()); match &c.fields[0] { Field::Field { r#enum: Some(r#enum), .. } => { let q_rs_typ = (&r#enum.0, &r#enum.1).qualified_rs_typ(); writeln!(out, " {}({}),", c.name, q_rs_typ)?; } Field::Field { mask: Some(mask), .. } => { let q_rs_typ = (&mask.0, &mask.1).qualified_rs_typ(); writeln!(out, " {}({}),", c.name, q_rs_typ)?; } Field::Field { module, rs_typ, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); if is_input_info_info && c.name == "Button" { writeln!(out, " /// The value is the number of buttons")?; } writeln!(out, " {}({}),", c.name, q_rs_typ)?; } Field::List { rs_typ, .. } if rs_typ == "char" => { writeln!(out, " {}(Lat1String),", c.name)?; } Field::List { r#enum: Some(r#enum), .. } => { let q_rs_typ = (&r#enum.0, &r#enum.1).qualified_rs_typ(); writeln!(out, " {}(Vec<{}>),", c.name, q_rs_typ)?; } Field::List { mask: Some(mask), .. } => { let q_rs_typ = (&mask.0, &mask.1).qualified_rs_typ(); writeln!(out, " {}(Vec<{}>),", c.name, q_rs_typ)?; } Field::List { module, rs_typ, struct_style, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let buf = if matches!(struct_style, Some(StructStyle::DynBuf)) { "Buf" } else { "" }; writeln!(out, " {}(Vec<{}{}>),", c.name, q_rs_typ, buf)?; } _ => unreachable!(), }; } else { writeln!(out, " {}{{", c.name)?; for f in &c.fields { match f { Field::Field { name, r#enum: Some(r#enum), .. } => { let q_rs_typ = (&r#enum.0, &r#enum.1).qualified_rs_typ(); writeln!(out, " {}: {},", name, q_rs_typ)?; } Field::Field { name, mask: Some(mask), is_fieldref, .. } => { if !*is_fieldref || request_fieldref_emitted(name, &c.fields, false) { let q_rs_typ = (&mask.0, &mask.1).qualified_rs_typ(); writeln!(out, " {}: {},", name, q_rs_typ)?; } } Field::Field { module, name, rs_typ, struct_style, is_fieldref, .. } => { if !*is_fieldref || request_fieldref_emitted(name, &c.fields, false) { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let buf = if matches!(struct_style, Some(StructStyle::DynBuf)) { "Buf" } else { "" }; writeln!(out, " {}: {}{},", name, q_rs_typ, buf)?; } } Field::List { name, rs_typ, .. } if rs_typ == "char" => { writeln!(out, " {}: Lat1String,", name)?; } Field::List { name, r#enum: Some(r#enum), .. } => { let q_rs_typ = (&r#enum.0, &r#enum.1).qualified_rs_typ(); writeln!(out, "{}{}: Vec<{}>,", cg::ind(2), name, q_rs_typ)?; } Field::List { name, mask: Some(mask), .. } => { let q_rs_typ = (&mask.0, &mask.1).qualified_rs_typ(); writeln!(out, "{}{}: Vec<{}>,", cg::ind(2), name, q_rs_typ)?; } Field::List { module, name, rs_typ, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!(out, " {}: Vec<{}>,", name, q_rs_typ)?; } Field::Switch { name, rs_typ, is_mask: true, .. } => { writeln!(out, " {}: Vec<{}>,", name, rs_typ)?; } Field::Pad { .. } => {} Field::AlignPad { .. } => {} _ => unreachable!("{:#?}", f), } } writeln!(out, " }},")?; } } writeln!(out, "}}")?; if is_mask { self.emit_mask_switch_impl(out, rs_typ, maskenum, cases)?; } else { self.emit_enum_switch_impl(out, rs_typ, maskenum, cases)?; } writeln!(out)?; if is_mask { writeln!(out, "impl base::WiredOut for &[{}] {{", rs_typ)?; } else { writeln!(out, "impl base::WiredOut for {} {{", rs_typ)?; } self.emit_switch_wire_len(out, rs_typ, cases, is_mask)?; writeln!(out)?; self.emit_switch_serialize(out, rs_typ, cases, is_mask)?; writeln!(out, "}}")?; writeln!(out)?; if is_mask { writeln!(out, "impl base::WiredIn for Vec<{}> {{", rs_typ)?; } else { writeln!(out, "impl base::WiredIn for {} {{", rs_typ)?; } writeln!(out, " type Params = {};", params_struct.rs_typ)?; self.emit_switch_compute_wire_len(out, expr, rs_typ, cases, params_struct, is_mask)?; self.emit_switch_unserialize(out, expr, rs_typ, is_mask, cases, params_struct)?; writeln!(out, "}}")?; Ok(()) } fn emit_switch_unserialize( &self, out: &mut O, expr: &Expr, rs_typ: &str, is_mask: bool, cases: &[SwitchCase], params_struct: &ParamsStruct, ) -> io::Result<()> { let return_typ = if is_mask { format!("Vec<{}>", rs_typ) } else { rs_typ.to_string() }; writeln!(out)?; writeln!(out, " #[allow(unused_assignments)]")?; writeln!(out, " unsafe fn unserialize(wire_data: *const u8, params: {}, out_offset: &mut usize) -> {} {{", params_struct.rs_typ, return_typ)?; writeln!(out, "{}let {}{{", cg::ind(2), params_struct.rs_typ)?; for p in ¶ms_struct.params { writeln!(out, "{}{},", cg::ind(3), p)?; } writeln!(out, "{}}} = params;", cg::ind(2))?; writeln!( out, "{}let expr = {};", cg::ind(2), self.build_rs_expr(expr, "", "", &[]) )?; if is_mask { writeln!(out, "{}let mut result = Vec::new();", cg::ind(2))?; } for sc in cases { let exprs = sc.exprs.iter().map(|e| self.build_rs_expr(e, "", "", &[])); let expr: String = if is_mask { format!("expr & {} != 0", exprs.collect::>().join(" & ")) } else { exprs .map(|e| format!("expr == {}", e)) .collect::>() .join(" || ") }; writeln!(out, "{}if {} {{", cg::ind(2), expr)?; writeln!(out, "{}let mut offset = 0usize;", cg::ind(3))?; for f in &sc.fields { // TODO: remove fieldrefs match f { Field::Field { name, rs_typ, wire_sz: Expr::Value(sz), r#enum: Some(r#enum), is_fieldref: false, .. } => { let q_rs_typ = (&r#enum.0, &r#enum.1).qualified_rs_typ(); writeln!(out, "{}let {} = std::mem::transmute::<_, {}>(*(wire_data.add(offset) as *const {}) as u32);", cg::ind(3), name, q_rs_typ, rs_typ)?; writeln!(out, "{}offset += {};", cg::ind(3), sz)?; } Field::Field { name, module, rs_typ, struct_style: Some(StructStyle::DynBuf), params_struct, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let params_expr = self.build_params_expr( params_struct.as_ref(), module.as_deref(), "", "", ); writeln!( out, "{}let {} = {}Buf::unserialize(wire_data.add(offset), {}, &mut offset);", cg::ind(3), name, q_rs_typ, params_expr )?; } Field::Field { name, rs_typ, wire_sz: Expr::Value(sz), .. } if rs_typ == "bool" => { let wire_typ = format!("u{}", *sz * 8); writeln!( out, "{}let {} = *(wire_data.add(offset) as *const {}) != 0;", cg::ind(3), name, wire_typ )?; writeln!(out, "{}offset += {};", cg::ind(3), sz)?; } Field::Field { name, module, rs_typ, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!( out, "{}let {} = *(wire_data.add(offset) as *const {});", cg::ind(3), name, q_rs_typ )?; writeln!( out, "{}offset += std::mem::size_of::<{}>();", cg::ind(3), q_rs_typ )?; } Field::List { name, rs_typ, len_expr, wire_sz, .. } if rs_typ == "char" => { let len_expr = self.build_rs_expr(len_expr, "", "", &sc.fields); assert_eq!(len_expr, self.build_rs_expr(wire_sz, "", "", &sc.fields)); writeln!( out, "{}let {}_ptr = wire_data.add(offset);", cg::ind(3), name )?; writeln!( out, "{}let {}_bytes = std::slice::from_raw_parts({}_ptr, {});", cg::ind(3), name, name, len_expr, )?; writeln!( out, "{}let {} = Lat1String::from_bytes({}_bytes);", cg::ind(3), name, name )?; writeln!(out, "{}offset += {}.len();", cg::ind(3), name)?; } Field::List { name, module, rs_typ, struct_style: None | Some(StructStyle::WireLayout | StructStyle::FixBuf), is_union: false, len_expr, mask, r#enum, .. } if rs_typ == "u32" || (r#enum.is_none() || mask.is_none()) => { let q_rs_typ = enum_mask_qualified_rs_typ(module, rs_typ, r#enum, mask); writeln!(out, "{}let {} = {{", cg::ind(3), name)?; writeln!( out, "{} let ptr = wire_data.add(offset) as *const {};", cg::ind(3), q_rs_typ )?; writeln!( out, "{} let len = {};", cg::ind(3), self.build_rs_expr(len_expr, "", "", &[]) )?; writeln!( out, "{} let data = std::slice::from_raw_parts(ptr, len);", cg::ind(3) )?; writeln!( out, "{}offset += len * std::mem::size_of::<{}>();", cg::ind(4), q_rs_typ )?; writeln!(out, "{} data.to_vec()", cg::ind(3))?; writeln!(out, "{}}};", cg::ind(3))?; } Field::List { name, rs_typ, len_expr, mask: Some(mask), .. } => { let q_rs_typ = (&mask.0, &mask.1).qualified_rs_typ(); writeln!(out, "{}let mut {} = Vec::new();", cg::ind(3), name)?; writeln!( out, "{}for i in 0..{} {{", cg::ind(3), self.build_rs_expr(len_expr, "", "", &[]) )?; writeln!(out, "{}{}.push({}::from_bits(*(wire_data.add(offset) as *const {}) as u32).unwrap());", cg::ind(4), name, q_rs_typ, rs_typ)?; writeln!( out, "{}offset += std::mem::size_of::<{}>();", cg::ind(4), rs_typ )?; writeln!(out, "{}}}", cg::ind(3))?; } Field::List { name, module, rs_typ, len_expr, struct_style, params_struct, is_union, union_typefield, .. } if matches!(struct_style, Some(StructStyle::DynBuf)) || *is_union => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!(out, "{}let mut {} = Vec::new();", cg::ind(3), name)?; writeln!( out, "{}let {}_params = {};", cg::ind(3), name, self.build_params_expr( params_struct.as_ref(), module.as_deref(), "", "" ) )?; writeln!( out, "{}for i in 0..{} {{", cg::ind(3), self.build_rs_expr(len_expr, "", "", &[]) )?; let rs_typ_suff = if *is_union { assert!( union_typefield.is_some(), "cannot build a union here without type field" ); "" } else { "Buf" }; writeln!( out, "{}let el = {}{}::unserialize(wire_data.add(offset), {}_params, &mut offset);", cg::ind(4), q_rs_typ, rs_typ_suff,name, )?; writeln!(out, "{}{}.push(el);", cg::ind(4), name,)?; writeln!(out, "{}}}", cg::ind(3))?; } Field::Switch { name, module, rs_typ, params_struct, is_mask, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); let params_expr = self.build_params_expr(Some(params_struct), module.as_deref(), "", ""); // do not supply fields because the masks are defined as integer here let impl_type = if *is_mask { format!(">", q_rs_typ) } else { q_rs_typ.clone() }; writeln!(out, "{}let {}_params = {};", cg::ind(3), name, params_expr)?; writeln!(out, "{}let {} = {}::unserialize(wire_data.add(offset), {}_params, &mut offset);", cg::ind(3), name, impl_type, name)?; } Field::Pad { wire_sz: Expr::Value(sz), .. } => { writeln!(out, "{}offset += {}; // pad", cg::ind(3), sz)?; } Field::AlignPad { wire_sz: Expr::AlignPad(sz, _), .. } => { writeln!( out, "{}offset += base::align_pad(offset, {});", cg::ind(3), sz )?; } f => unreachable!("{:#?}", f), } } writeln!(out, "{}*out_offset += offset;", cg::ind(3))?; let (open, close) = if visible_fields_len(&sc.fields) == 1 { ("(", ")") } else { ("{", "}") }; let stmt = if is_mask { "result.push(" } else { "return " }; writeln!(out, "{}{}{}::{}{}", cg::ind(3), stmt, rs_typ, sc.name, open)?; for f in &sc.fields { match f { Field::Field { name, mask, is_fieldref, .. } => { if !*is_fieldref || request_fieldref_emitted(name, &sc.fields, false) { if let Some((module, mask)) = mask { let pref_name = if visible_fields_len(&sc.fields) != 1 { format!("{}: ", name) } else { "".to_string() }; let q_rs_typ = (module, mask).qualified_rs_typ(); writeln!( out, "{}{}{}::from_bits({} as u32).unwrap(),", cg::ind(4), pref_name, q_rs_typ, name )?; } else { writeln!(out, "{}{},", cg::ind(4), name)?; } } } Field::List { name, .. } => { writeln!(out, "{}{},", cg::ind(4), name)?; } Field::Switch { name, .. } => { writeln!(out, "{}{},", cg::ind(4), name)?; } Field::Pad { .. } => {} Field::AlignPad { .. } => {} f => unreachable!("{:#?}", f), } } writeln!( out, "{}{}{};", cg::ind(3), close, if is_mask { ")" } else { "" } )?; writeln!(out, "{}}}", cg::ind(2))?; } if is_mask { writeln!(out, "{}result", cg::ind(2))?; } else { writeln!( out, "{}unreachable!(\"Could not match any expression for {}\");", cg::ind(2), rs_typ )?; } writeln!(out, " }}")?; Ok(()) } pub(super) fn emit_switch_compute_wire_len( &self, out: &mut O, expr: &Expr, rs_typ: &str, cases: &[SwitchCase], params_struct: &ParamsStruct, is_mask: bool, ) -> io::Result<()> { writeln!(out)?; writeln!( out, " unsafe fn compute_wire_len(ptr: *const u8, params: Self::Params) -> usize {{" )?; writeln!(out, "{}let {} {{", cg::ind(2), params_struct.rs_typ)?; for p in ¶ms_struct.params { writeln!(out, "{}{},", cg::ind(3), p)?; } writeln!(out, "{}}} = params;", cg::ind(2))?; writeln!( out, "{}let expr = {};", cg::ind(2), self.build_rs_expr(expr, "", "", &[]) )?; if is_mask { writeln!(out, "{}let mut sz = 0usize;", cg::ind(2))?; } for c in cases { let exprs: Vec = c .exprs .iter() .map(|e| self.build_rs_expr(e, "", "", &[])) .collect(); let exprs = exprs.join(if is_mask { " | " } else { " || " }); if is_mask { writeln!(out, " if expr & {} != 0 {{", exprs)?; } else { writeln!(out, " if expr == {} {{", exprs)?; writeln!(out, " let mut sz = 0usize;")?; } for cf in &c.fields { let mut stmts = Vec::new(); self.field_compute_len_stmts(cf, &mut stmts, &Expr::Value(0)); for s in stmts { writeln!(out, " {}", s)?; } } if !is_mask { writeln!(out, " return sz;")?; } writeln!(out, " }}")?; } if is_mask { writeln!(out, "{}sz", cg::ind(2))?; } else { writeln!( out, " unreachable!(\"could not match switch expression in {}::{}::compute_wire_len\")", self.xcb_mod, rs_typ )?; } writeln!(out, " }}")?; Ok(()) } fn emit_switch_wire_len( &self, out: &mut O, rs_typ: &str, cases: &[SwitchCase], is_mask: bool, ) -> io::Result<()> { writeln!(out)?; writeln!(out, " fn wire_len(&self) -> usize {{")?; writeln!(out, "{}let mut sz = 0usize;", cg::ind(2))?; let ind = if is_mask { writeln!(out, "{}for el in self.iter() {{", cg::ind(2))?; writeln!(out, "{}match el {{", cg::ind(3))?; 3 } else { writeln!(out, "{}match self {{", cg::ind(2))?; 2 }; for sc in cases { let (open, close) = if visible_fields_len(&sc.fields) == 1 { ("(", ")") } else { ("{", "}") }; writeln!(out, "{}{}::{}{}", cg::ind(ind + 1), rs_typ, sc.name, open)?; for f in &sc.fields { match f { Field::Field { name, is_fieldref: true, .. } => { if request_fieldref_emitted(name, &sc.fields, false) { writeln!(out, "{}{},", cg::ind(ind + 2), name)?; } } Field::Field { wire_sz: Expr::Value(_), .. } | Field::List { wire_sz: Expr::Value(_), .. } | Field::Pad { wire_sz: Expr::Value(_), .. } => {} Field::Field { name, .. } | Field::List { name, .. } | Field::Switch { name, .. } => writeln!(out, "{}{},", cg::ind(ind + 2), name)?, Field::Expr { .. } => unreachable!(), Field::Pad { .. } => {} Field::AlignPad { .. } => {} } } writeln!(out, "{}..", cg::ind(ind + 2))?; writeln!(out, "{}{} => {{", cg::ind(ind + 1), close)?; for f in &sc.fields { match f { Field::Field { wire_sz: Expr::Value(sz), .. } | Field::List { wire_sz: Expr::Value(sz), .. } | Field::Pad { wire_sz: Expr::Value(sz), .. } => { writeln!(out, "{}sz += {};", cg::ind(ind + 2), sz)?; } Field::Field { mask, r#enum, rs_typ, .. } if mask.is_some() || r#enum.is_some() => { writeln!( out, "{}sz += std::mem::size_of::<{}>();", cg::ind(ind + 2), rs_typ )?; } Field::Field { name, .. } => { writeln!(out, "{}sz += {}.wire_len();", cg::ind(ind + 2), name)?; } Field::List { name, rs_typ, .. } if rs_typ == "char" => { writeln!(out, "{}sz += {}.len();", cg::ind(ind + 2), name)?; } Field::List { name, mask, r#enum, rs_typ, .. } if mask.is_some() || r#enum.is_some() => { writeln!( out, "{}sz += {}.len() * std::mem::size_of::<{}>();", cg::ind(ind + 2), name, rs_typ )?; } Field::List { name, .. } => { writeln!(out, "{}for el in {} {{", cg::ind(ind + 2), name)?; writeln!(out, "{}sz += el.wire_len();", cg::ind(ind + 3))?; writeln!(out, "{}}}", cg::ind(ind + 2))?; } Field::Switch { name, is_mask, .. } => { let acc = if *is_mask { ".as_slice()" } else { "" }; writeln!(out, "{}sz += {}{}.wire_len();", cg::ind(ind + 2), name, acc)?; } Field::AlignPad { wire_sz: Expr::AlignPad(sz, _), .. } => { writeln!( out, "{}sz += base::align_pad(sz, {});", cg::ind(ind + 2), sz )?; } _ => unreachable!("{:#?}", f), } } writeln!(out, "{}}}", cg::ind(ind + 1))?; } writeln!(out, "{}}}", cg::ind(ind))?; if is_mask { writeln!(out, "{}}}", cg::ind(2))?; } writeln!(out, "{}sz", cg::ind(2))?; writeln!(out, " }}")?; Ok(()) } fn emit_switch_serialize( &self, out: &mut O, rs_typ: &str, cases: &[SwitchCase], is_mask: bool, ) -> io::Result<()> { writeln!( out, " fn serialize(&self, wire_buf: &mut [u8]) -> usize {{" )?; writeln!(out, "{}let mut offset = 0usize;", cg::ind(2))?; let ind = if is_mask { writeln!(out, "{}for el in self.iter() {{", cg::ind(2))?; writeln!(out, "{}match el {{", cg::ind(3))?; 3 } else { writeln!(out, "{}match self {{", cg::ind(2))?; 2 }; for sc in cases { let (open, close) = if visible_fields_len(&sc.fields) == 1 { ("(", ")") } else { ("{", "}") }; writeln!(out, "{}{}::{}{}", cg::ind(ind + 1), rs_typ, sc.name, open)?; for f in &sc.fields { match f { Field::Field { name, is_fieldref: true, .. } => { if request_fieldref_emitted(name, &sc.fields, false) { writeln!(out, "{}{},", cg::ind(ind + 2), name)?; } } Field::Field { name, .. } | Field::List { name, .. } | Field::Switch { name, .. } => writeln!(out, "{}{},", cg::ind(ind + 2), name)?, Field::Expr { .. } => unreachable!(), Field::Pad { .. } => {} Field::AlignPad { .. } => {} } } writeln!(out, "{}..", cg::ind(ind + 2))?; writeln!(out, "{}{} => {{", cg::ind(ind + 1), close)?; for f in &sc.fields { match f { Field::Field { name, rs_typ, wire_sz: Expr::Value(sz), .. } if rs_typ == "bool" => { // have to take into account "BOOL32" let typ = if *sz == 4 { "u32" } else if *sz == 1 { "u8" } else { unreachable!("unknown sized bool") }; writeln!( out, "{}let {}: {} = if *{} {{ 1 }} else {{ 0 }};", cg::ind(ind + 2), name, typ, name )?; writeln!( out, "{}offset += {}.serialize(&mut wire_buf[offset..]);", cg::ind(ind + 2), name )?; } Field::Field { name, r#enum: Some(_), rs_typ, .. } => { writeln!(out, "{}offset += (unsafe {{ std::mem::transmute::<_, u32>(*{}) }} as {}).serialize(&mut wire_buf[offset..]);", cg::ind(ind+2), name, rs_typ)?; } Field::Field { name, mask: Some(_), is_fieldref, rs_typ, .. } => { let fieldref_value = if *is_fieldref { fieldref_get_value(name, &sc.fields, false, "") } else { None }; let expr = if let Some(fieldref_value) = fieldref_value { fieldref_value } else { name.clone() + ".bits()" }; writeln!( out, "{}offset += ({} as {}).serialize(&mut wire_buf[offset..]);", cg::ind(ind + 2), expr, rs_typ )?; } Field::Field { name, rs_typ, is_fieldref, .. } => { let fieldref_value = if *is_fieldref { fieldref_get_value(name, &sc.fields, false, "") } else { None }; if let Some(fieldref_value) = fieldref_value { writeln!( out, "{}offset += ({} as {}).serialize(&mut wire_buf[offset..]);", cg::ind(ind + 2), fieldref_value, rs_typ )?; } else { writeln!( out, "{}offset += {}.serialize(&mut wire_buf[offset..]);", cg::ind(ind + 2), name )?; } } Field::List { name, rs_typ, .. } if rs_typ == "char" => { writeln!( out, "{}wire_buf[offset..offset+{}.len()].copy_from_slice({}.as_bytes());", cg::ind(ind + 2), name, name )?; writeln!(out, "{}offset += {}.len();", cg::ind(ind + 2), name)?; } Field::List { name, rs_typ, mask: Some(_), .. } => { writeln!(out, "{}for el in {} {{", cg::ind(ind + 2), name)?; writeln!( out, "{}offset += (el.bits() as {}).serialize(&mut wire_buf[offset..]);", cg::ind(ind + 3), rs_typ, )?; writeln!(out, "{}}}", cg::ind(ind + 2))?; } Field::List { name, .. } => { writeln!(out, "{}for el in {} {{", cg::ind(ind + 2), name)?; writeln!( out, "{}offset += el.serialize(&mut wire_buf[offset..]);", cg::ind(ind + 3) )?; writeln!(out, "{}}}", cg::ind(ind + 2))?; } Field::Switch { name, is_mask, .. } => { let acc = if *is_mask { ".as_slice()" } else { "" }; writeln!( out, "{}offset += {}{}.serialize(&mut wire_buf[offset..]);", cg::ind(ind + 2), name, acc )?; } Field::Pad { wire_sz: Expr::Value(sz), .. } => { writeln!(out, "{}offset += {};", cg::ind(ind + 2), sz)?; } Field::AlignPad { wire_sz: Expr::AlignPad(sz, _), .. } => { writeln!( out, "{}offset += base::align_pad(offset, {});", cg::ind(ind + 2), sz )?; } _ => unreachable!("{:#?}", f), } } writeln!(out, "{}}}", cg::ind(ind + 1))?; } writeln!(out, "{}}}", cg::ind(ind))?; if is_mask { writeln!(out, "{}}}", cg::ind(2))?; } writeln!(out, "{}offset", cg::ind(2))?; writeln!(out, " }}")?; Ok(()) } fn emit_mask_switch_impl( &self, out: &mut O, rs_typ: &str, mask: &(Option, String), cases: &[SwitchCase], ) -> io::Result<()> { let mask = (&mask.0, &mask.1).qualified_rs_typ(); writeln!(out)?; writeln!(out, "impl {} {{", rs_typ)?; writeln!( out, " pub(crate) fn get_mask(slice: &[{}]) -> {} {{", rs_typ, mask )?; writeln!(out, " let mut res = {}::empty();", mask)?; writeln!(out, " for el in slice {{")?; writeln!(out, " match el {{")?; for c in cases { let indent = " "; writeln!(out, "{}{}::{}{{..}} => {{", indent, rs_typ, c.name)?; for expr in &c.exprs { if let Expr::MaskRef { item, .. } = expr { writeln!(out, "{} res |= {}::{};", indent, mask, item)?; } else { unreachable!(); } } writeln!(out, "{}}}", indent)?; } writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out, " res")?; writeln!(out, " }}")?; writeln!(out)?; writeln!( out, " pub(crate) fn is_sorted_distinct(slice: &[{}]) -> bool {{", rs_typ )?; writeln!(out, " if slice.len() <= 1 {{")?; writeln!(out, " true")?; writeln!(out, " }} else {{")?; writeln!(out, " let mut last = slice[0].get_ord();")?; writeln!( out, " slice[1..].iter().map(|el| el.get_ord()).all(|o| {{" )?; writeln!(out, " let lasto = last;")?; writeln!(out, " last = o;")?; writeln!(out, " lasto < o")?; writeln!(out, " }})")?; writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out)?; writeln!(out, " fn get_ord(&self) -> u32 {{")?; writeln!(out, " match self {{")?; for (i, c) in cases.iter().enumerate() { writeln!(out, " {}::{}{{..}} => {},", rs_typ, c.name, i)?; } writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; // implementing Ord only for xproto switches which are simple enough if self.xcb_mod == "xproto" { writeln!(out)?; writeln!(out, "impl Ord for {} {{", rs_typ)?; writeln!(out, " fn cmp(&self, other: &Self) -> Ordering {{")?; writeln!(out, " let o = self.get_ord().cmp(&other.get_ord());")?; writeln!(out, " match o {{")?; writeln!(out, " Ordering::Less | Ordering::Greater => o,")?; writeln!(out, " Ordering::Equal => {{")?; writeln!(out, " match (self, other) {{")?; for c in cases.iter() { writeln!( out, "{}({}::{}(val), {}::{}(oval)) => val.cmp(oval),", cg::ind(5), rs_typ, c.name, rs_typ, c.name )?; } writeln!( out, " _ => unreachable!(\"Bug: o should not be Ordering::Equal\")," )?; writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl PartialOrd for {} {{", rs_typ)?; writeln!( out, " fn partial_cmp(&self, other: &Self) -> Option {{" )?; writeln!(out, " Some(self.cmp(other))")?; writeln!(out, " }}")?; writeln!(out, "}}")?; } Ok(()) } fn emit_enum_switch_impl( &self, out: &mut O, rs_typ: &str, r#enum: &(Option, String), cases: &[SwitchCase], ) -> io::Result<()> { let r#enum = (&r#enum.0, &r#enum.1).qualified_rs_typ(); writeln!(out)?; writeln!(out, "impl {} {{", rs_typ)?; writeln!(out, " pub(crate) fn get_enum(&self) -> {} {{", r#enum)?; writeln!(out, " match self {{")?; for c in cases { let indent = " "; writeln!(out, "{}{}::{}{{..}} => {{", indent, rs_typ, c.name)?; for expr in &c.exprs { if let Expr::EnumRef { item, .. } = expr { writeln!(out, "{} {}::{}", indent, r#enum, item)?; } else { unreachable!(); } } writeln!(out, "{}}}", indent)?; } writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; Ok(()) } } fn visible_fields_len(fields: &[Field]) -> usize { let mut len = 0; for f in fields { match f { Field::Pad { .. } => {} Field::AlignPad { .. } => {} _ => { len += 1; } } } len } xcb-1.2.2/build/cg/union.rs000064400000000000000000000474311046102023000136010ustar 00000000000000use crate::{cg, cg::util, ir}; use super::{ AsModRsTyp, CodeGen, Expr, Field, QualifiedRsTyp, RsTyp, TypeInfo, UnionTypeField, UnionVariant, UnionVariantContent, WireSz, }; use std::io::{self, Write}; impl CodeGen { pub(super) fn resolve_union( &mut self, typ: &str, fields: &[ir::Field], _doc: &Option, ) { let rs_typ = cg::rust_type_name(typ); let has_type_field = fields .iter() .any(|f| matches!(f, ir::Field::Field{name, ..} if name == "type")); let (variants, wire_sz, type_field) = if has_type_field { self.resolve_type_field_union_variants(&rs_typ, fields) } else { self.resolve_union_variants(&rs_typ, fields) }; let typ_info = TypeInfo::Union { module: None, rs_typ, variants, wire_sz, type_field, impl_clone: true, emit: true, }; self.register_typ(typ.to_string(), typ_info); } fn resolve_union_variants( &mut self, _union_rs_typ: &str, fields: &[ir::Field], ) -> (Vec, Expr, Option) { let mut vec = Vec::new(); let mut wire_sz = Expr::Value(0usize); for f in fields { match f { ir::Field::Field { name, typ, .. } => { let (module, typ) = util::extract_module(typ); let typinfo = self.find_typinfo(module, typ); let rs_typ = typinfo.rs_typ(); let f_wire_sz = typinfo.wire_sz(); let content = UnionVariantContent::RsTyp(rs_typ.to_string()); if let (Expr::Value(wire_sz), Expr::Value(f_wire_sz)) = (&mut wire_sz, &f_wire_sz) { *wire_sz = (*wire_sz).max(*f_wire_sz); } let variant = util::tit_cap(name); vec.push(UnionVariant { variant, module: module.map(str::to_owned), content, }); } ir::Field::List { name, typ, len_expr: ir::Expr::Value(sz), .. } => { let variant = util::tit_cap(name); let (module, typ) = util::extract_module(typ); let typinfo = self.find_typinfo(module, typ); let rs_typ = typinfo.rs_typ(); let f_wire_sz = typinfo.wire_sz(); let f_wire_sz = Expr::Op( "*".to_string(), Box::new(Expr::Value(*sz)), Box::new(f_wire_sz), ); let f_wire_sz = f_wire_sz.reduce(); if let (Expr::Value(wire_sz), Expr::Value(f_wire_sz)) = (&mut wire_sz, &f_wire_sz) { *wire_sz = (*wire_sz).max(*f_wire_sz); } vec.push(UnionVariant { variant, module: module.map(str::to_owned), content: UnionVariantContent::Array(rs_typ.to_string(), *sz), }); } ir::Field::Pad(..) => unreachable!("pad in union??"), f => unreachable!("{:#?}", f), } } (vec, wire_sz, None) } fn resolve_type_field_union_variants( &mut self, union_rs_typ: &str, fields: &[ir::Field], ) -> (Vec, Expr, Option) { let mut vec = Vec::new(); let mut wire_sz = Expr::Value(0usize); let mut type_field = None; for f in fields { match f { ir::Field::Field { name, typ, r#enum, .. } => { if name == "type" { assert_eq!(typ.as_str(), "CARD8"); let (enu_module, enu_typ) = r#enum .as_ref() .map(|typ| { let (module, typ) = util::extract_module(typ); (module.map(str::to_owned), typ.to_string()) }) .unwrap_or_else(|| (None, union_rs_typ.to_string() + "Type")); type_field = Some(UnionTypeField { offset: 0, enu_module, enu_typ, }); continue; } let (module, typ) = util::extract_module(typ); if name == "common" { if module.is_none() { self.typ_need_add(typ, -1); } continue; } let typinfo = self.find_typinfo(module, typ); let rs_typ = typinfo.rs_typ(); let f_wire_sz = typinfo.wire_sz(); let final_typinfo = self.find_typinfo_recurse(module, typ); let content = match final_typinfo { TypeInfo::Struct { fields, .. } => { UnionVariantContent::Struct(fields.clone()) } _ => UnionVariantContent::RsTyp(rs_typ.to_string()), }; if let (Expr::Value(wire_sz), Expr::Value(f_wire_sz)) = (&mut wire_sz, &f_wire_sz) { *wire_sz = (*wire_sz).max(*f_wire_sz); } let variant = util::tit_cap(name); vec.push(UnionVariant { variant, module: module.map(str::to_owned), content, }); if module.is_none() { self.typ_need_add(typ, -1); } } ir::Field::Pad(..) => unreachable!("pad in union??"), _ => unreachable!(), } } (vec, wire_sz, type_field) } pub(super) fn emit_union( &self, out: &mut O, rs_typ: &str, variants: &[UnionVariant], wire_sz: &Expr, type_field: &Option, impl_clone: bool, ) -> io::Result<()> { writeln!(out)?; writeln!( out, "#[derive({}Debug)]", if impl_clone { "Clone, " } else { "" } )?; writeln!(out, "pub enum {} {{", rs_typ)?; for v in variants { match &v.content { UnionVariantContent::RsTyp(rs_typ) => { writeln!(out, " {}({}),", v.variant, rs_typ)?; } UnionVariantContent::Array(rs_typ, sz) => { writeln!(out, " {}([{}; {}]),", v.variant, rs_typ, sz)?; } UnionVariantContent::Struct(fields) => { if fields.is_empty() { writeln!(out, " {},", v.variant)?; } else { writeln!(out, " {}{{", v.variant)?; for f in fields { if is_type_field(f) { continue; } match f { Field::Field { name, module, rs_typ, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!(out, " {}: {},", name, q_rs_typ)?; } Field::List { name, module, rs_typ, len_expr: Expr::Value(sz), .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!(out, " {}: [{}; {}],", name, q_rs_typ, sz)?; } _ => {} } } writeln!(out, " }},")?; } } } } writeln!(out, "}}")?; if let Some(type_field) = type_field { self.emit_union_ctor_type_field(out, rs_typ, variants, type_field)?; } let wire_sz = match wire_sz { Expr::Value(wire_sz) => wire_sz, _ => unreachable!("{:#?}", wire_sz), }; writeln!(out)?; writeln!(out, "impl base::WiredOut for {} {{", rs_typ)?; writeln!(out, " fn wire_len(&self) -> usize {{ {} }}", wire_sz)?; writeln!(out)?; writeln!( out, " fn serialize(&self, wire_buf: &mut [u8]) -> usize {{" )?; writeln!(out, " match self {{")?; for uv in variants { match &uv.content { UnionVariantContent::RsTyp(_rs_typ) => { assert!(type_field.is_none()); let name = cg::rust_field_name(&uv.variant); writeln!( out, "{}{}::{}({}) => {{", cg::ind(3), rs_typ, uv.variant, name )?; writeln!(out, "{}{}.serialize(wire_buf);", cg::ind(4), name)?; writeln!(out, "{}}}", cg::ind(3))?; } UnionVariantContent::Array(_rs_typ, _sz) => { assert!(type_field.is_none()); let name = cg::rust_field_name(&uv.variant); writeln!( out, "{}{}::{}({}) => {{", cg::ind(3), rs_typ, uv.variant, name )?; writeln!( out, "{}let me = unsafe {{ std::slice::from_raw_parts(", cg::ind(4) )?; writeln!( out, "{}{}.as_ptr() as *const u8, {}", cg::ind(5), name, wire_sz )?; writeln!(out, "{})}};", cg::ind(4))?; writeln!( out, "{}let wire_buf = &mut wire_buf[..{}];", cg::ind(4), wire_sz )?; writeln!(out, "{}wire_buf.copy_from_slice(me);", cg::ind(4))?; writeln!(out, "{}}}", cg::ind(3))?; } UnionVariantContent::Struct(fields) => { writeln!(out, "{}{}::{}{{", cg::ind(3), rs_typ, uv.variant)?; for f in fields { if is_type_field(f) { continue; } match f { Field::Field { name, .. } | Field::List { name, .. } => { writeln!(out, "{}{},", cg::ind(4), name)?; } _ => {} } } writeln!(out, "{}}} => {{", cg::ind(3))?; for f in fields.iter() { if is_type_field(f) { let type_field = type_field.as_ref().unwrap(); let enum_typ = self.find_typinfo( type_field.enu_module.as_deref(), &type_field.enu_typ, ); let enum_expr = enum_typ.qualified_rs_typ() + "::" + &uv.variant; writeln!( out, "{}wire_buf[0] += unsafe {{ std::mem::transmute::<_, u32>({}) }} as u8;", cg::ind(4), enum_expr, )?; continue; } match f { Field::Field { name, wire_off: Expr::Value(off), .. } => { writeln!( out, "{}{}.serialize(&mut wire_buf[{}..]);", cg::ind(4), name, off )?; } Field::List { name, wire_off: Expr::Value(off), wire_sz: Expr::Value(sz), .. } => { writeln!( out, "{}wire_buf[{}..{}].copy_from_slice({});", cg::ind(4), off, *off + *sz, name, )?; } Field::Pad { .. } => {} _ => unreachable!(), } } writeln!(out, "{}}}", cg::ind(3))?; } } } writeln!(out, " }}")?; writeln!(out, " {}", wire_sz)?; writeln!(out, " }}")?; writeln!(out, "}}")?; if type_field.is_some() { writeln!(out)?; writeln!(out, "impl base::WiredIn for {} {{", rs_typ)?; writeln!(out, " type Params = ();")?; writeln!(out)?; writeln!( out, " unsafe fn compute_wire_len(_ptr: *const u8, _params: Self::Params) -> usize {{ {} }}", wire_sz )?; writeln!(out, " unsafe fn unserialize(ptr: *const u8, _params: (), offset: &mut usize) -> Self {{")?; writeln!(out, " let sz = Self::compute_wire_len(ptr, ());")?; writeln!(out, " *offset += sz;")?; writeln!( out, " Self::from_data(std::slice::from_raw_parts(ptr, sz))" )?; writeln!(out, " }}")?; writeln!(out, "}}")?; } Ok(()) } fn emit_union_ctor_type_field( &self, out: &mut O, rs_typ: &str, variants: &[UnionVariant], type_field: &UnionTypeField, ) -> io::Result<()> { let UnionTypeField { enu_module, enu_typ, offset, } = type_field; let (items, _) = { let mod_rs_typ = (enu_module, enu_typ); let mod_rs_typ = mod_rs_typ.as_mod_rs_typ(); let typ_info = self.find_typinfo_recurse(mod_rs_typ.0, mod_rs_typ.1); if let TypeInfo::Enum { items, .. } = typ_info { ( //map_type_field_enum_items(&self.xcb_mod, rs_typ, items), items, typ_info.qualified_rs_typ(), ) } else { unreachable!() } }; writeln!(out)?; writeln!(out, "impl {} {{", rs_typ)?; writeln!( out, " unsafe fn from_data(wire_data: &[u8]) -> {} {{", rs_typ )?; writeln!(out, " let r#type = wire_data[{}] as u32;", offset)?; writeln!(out, " match r#type {{")?; for item in items { let variant = variants.iter().find(|v| v.variant == item.0).unwrap(); match &variant.content { UnionVariantContent::RsTyp(..) => unreachable!(), UnionVariantContent::Array(..) => unreachable!(), UnionVariantContent::Struct(fields) => { if fields.is_empty() { writeln!( out, " {} => {}::{},", item.1, rs_typ, variant.variant )?; } else { writeln!( out, " {} => {}::{}{{", item.1, rs_typ, variant.variant )?; for f in fields { if is_type_field(f) { continue; } match f { Field::Field { name, wire_off: Expr::Value(off), module, rs_typ, .. } => { let q_rs_typ = (module, rs_typ).qualified_rs_typ(); writeln!( out, " {}: *(wire_data.as_ptr().add({}) as *const {}),", name, off, q_rs_typ, )?; } Field::List { name, wire_off: Expr::Value(off), wire_sz: Expr::Value(sz), rs_typ, .. } if rs_typ == "u8" => { writeln!( out, "{}{}: wire_data[{}..{}].try_into().unwrap(),", cg::ind(4), name, *off, *off + *sz )?; } Field::Pad { .. } => {} _ => unreachable!(), } } writeln!(out, " }},")?; } } } } writeln!( out, " _ => unreachable!(\"unexpected type value for {}::{}\"),", self.xcb_mod, rs_typ )?; writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; Ok(()) } } fn is_type_field(f: &Field) -> bool { match f { Field::Field { name, .. } => name == "r#type", _ => false, } } xcb-1.2.2/build/cg/util.rs000064400000000000000000000051571046102023000134250ustar 00000000000000// capitalize each substring beginning by uppercase // said otherwise: every upper preceded by another upper and followed by a upper is turned to lower pub(super) fn tit_cap(name: &str) -> String { if name.len() <= 1 { return name.into(); } let is_high = |c: char| c.is_ascii_uppercase() | c.is_ascii_digit(); let mut res = String::new(); let mut ch = name.chars(); let mut prev = ch.next().unwrap(); res.push(prev.to_ascii_uppercase()); let mut c = ch.next().unwrap(); for next in ch { if c != '_' { if is_high(c) && is_high(prev) && (is_high(next) || next == '_') { res.push(c.to_ascii_lowercase()) } else if prev == '_' && c != '_' { res.push(c.to_ascii_uppercase()) } else { res.push(c) } } prev = c; c = next; } if is_high(c) && is_high(prev) { res.push(c.to_ascii_lowercase()); } else { res.push(c); } res } #[test] fn test_tit_cap() { assert!(tit_cap("SomeString") == "SomeString"); assert!(tit_cap("WINDOW") == "Window"); assert!(tit_cap("CONTEXT_TAG") == "ContextTag"); assert!(tit_cap("value_list") == "ValueList"); assert!(tit_cap("GContext") == "GContext"); assert!(tit_cap("IDChoice") == "IdChoice"); } // insert a underscore before each uppercase/digit preceded or follwed by lowercase // do not apply to the first char pub(super) fn tit_split(name: &str) -> String { if name.len() <= 1 { return name.into(); } let is_high = |c: char| c.is_ascii_uppercase(); let is_low = |c: char| c.is_ascii_lowercase(); let mut res = String::new(); let mut ch = name.chars(); let mut prev = ch.next().unwrap(); res.push(prev); let mut c = ch.next().unwrap(); for next in ch { if (is_low(prev) && is_high(c) || is_high(c) && is_low(next)) && prev != '_' { res.push('_'); } res.push(c); prev = c; c = next; } if is_low(prev) && is_high(c) && prev != '_' { res.push('_'); } res.push(c); res } #[test] fn test_tit_split() { assert_eq!(tit_dig("SomeString"), "Some_String"); assert_eq!(tit_dig("WINDOW"), "WINDOW"); } pub(super) fn to_snake_case(name: &str) -> String { tit_split(name).to_ascii_lowercase() } pub(super) fn extract_module(typ: &str) -> (Option<&str>, &str) { let len = typ.len(); let colon = typ.as_bytes().iter().position(|b| *b == b':'); if let Some(colon) = colon { (Some(&typ[0..colon]), &typ[colon + 1..len]) } else { (None, typ) } } xcb-1.2.2/build/cg/xid.rs000064400000000000000000000136641046102023000132360ustar 00000000000000use super::{CodeGen, QualifiedRsTyp, RsTyp, TypeInfo, UnionVariantContent}; use crate::cg; use std::io::{self, Write}; use super::{util, UnionVariant}; impl CodeGen { pub(super) fn resolve_xid(&mut self, typ: &str) { let rs_typ = match (self.xcb_mod.as_str(), typ) { ("present", "EVENT") => "EventXid".to_string(), (_, typ) => cg::rust_type_name(typ), }; let info = TypeInfo::Xid { module: None, rs_typ, }; self.register_typ(typ.to_string(), info); } pub(super) fn resolve_xidunion(&mut self, typ: &str, xidtypes: &[String]) { let rs_typ = cg::rust_type_name(typ); let variants: Vec<_> = xidtypes .iter() .map(|typ| { let (module, typ) = self.extract_module(typ); let rs_typ = self.find_typinfo(module, typ).rs_typ().to_string(); let variant = match module { Some(module) => util::tit_cap(module) + &rs_typ, None => rs_typ.clone(), }; UnionVariant { variant, module: module.map(str::to_owned), content: UnionVariantContent::RsTyp(rs_typ), } }) .collect(); let info = TypeInfo::XidUnion { module: None, rs_typ, variants, }; self.register_typ(typ.to_string(), info); } pub(super) fn emit_xid(&self, out: &mut O, rs_typ: &str) -> io::Result<()> { let dbg = if self.dbg_atom_names && self.xcb_mod == "xproto" && rs_typ == "Atom" { "" } else { "Debug, " }; writeln!(out)?; writeln!( out, "#[derive(Copy, Clone, {}PartialEq, Eq, Hash, PartialOrd, Ord)]", dbg )?; writeln!(out, "#[repr(C)]")?; writeln!(out, "pub struct {} {{", rs_typ)?; writeln!(out, " res_id: u32,")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::Xid for {} {{", rs_typ)?; writeln!( out, " fn none() -> Self {{ {} {{ res_id: 0 }} }}", rs_typ )?; writeln!(out, " fn resource_id(&self) -> u32 {{ self.res_id }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::XidNew for {} {{", rs_typ)?; writeln!( out, " unsafe fn new(res_id: u32) -> Self {{ {} {{ res_id }} }}", rs_typ )?; writeln!(out, "}}")?; self.emit_sizeof_test(out, rs_typ, 4)?; Ok(()) } pub(super) fn emit_xidunion( &self, out: &mut O, rs_typ: &str, variants: &[UnionVariant], ) -> io::Result<()> { let has_unknown = matches!( (self.xcb_mod.as_str(), rs_typ), ("xproto", "Drawable") | ("glx", "Drawable") ); writeln!(out)?; writeln!(out, "#[derive(Copy, Clone, Debug)]")?; writeln!(out, "pub enum {} {{", rs_typ)?; writeln!(out, " None,")?; if has_unknown { writeln!(out, " /// Whether the drawable is a `Window` or a `Pixmap` is only known to the user context")?; writeln!(out, " Unknown(u32),")?; } for v in variants { if let UnionVariantContent::RsTyp(rs_typ) = &v.content { let mod_rs_typ = (&v.module, rs_typ); writeln!(out, " {}({}),", v.variant, mod_rs_typ.qualified_rs_typ())?; } else { unreachable!(); } } writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl base::Xid for {} {{", rs_typ)?; writeln!(out, " fn none() -> Self {{ {}::None }}", rs_typ)?; writeln!(out)?; writeln!(out, " fn resource_id(&self) -> u32 {{")?; writeln!(out, " match self {{")?; writeln!(out, " {}::None => 0,", rs_typ)?; if has_unknown { writeln!(out, "{}Drawable::Unknown(id) => *id,", cg::ind(3))?; } for v in variants { writeln!( out, " {}::{}(xid) => xid.resource_id(),", rs_typ, v.variant )?; } writeln!(out, " }}")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl PartialEq for {} {{", rs_typ)?; writeln!(out, " fn eq(&self, rhs: &{}) -> bool {{", rs_typ)?; writeln!(out, " self.resource_id() == rhs.resource_id()")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl Eq for {} {{}}", rs_typ)?; writeln!(out)?; writeln!(out, "impl Hash for {} {{", rs_typ)?; writeln!(out, " fn hash(&self, state: &mut H) {{")?; writeln!(out, " self.resource_id().hash(state);")?; writeln!(out, " }}")?; writeln!(out, "}}")?; for v in variants { let variant = if v.variant == "XprotoWindow" { "xproto::Window" } else { &v.variant }; writeln!(out)?; writeln!(out, "impl PartialEq<{}> for {} {{", variant, rs_typ)?; writeln!(out, " fn eq(&self, rhs: &{}) -> bool {{", variant)?; writeln!(out, " self.resource_id() == rhs.resource_id()")?; writeln!(out, " }}")?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "impl PartialEq<{}> for {} {{", rs_typ, variant)?; writeln!(out, " fn eq(&self, rhs: &{}) -> bool {{", rs_typ)?; writeln!(out, " self.resource_id() == rhs.resource_id()")?; writeln!(out, " }}")?; writeln!(out, "}}")?; } Ok(()) } } xcb-1.2.2/build/ir.rs000064400000000000000000000066651046102023000124760ustar 00000000000000//! Intermediate representation module #[derive(Debug, Clone)] pub struct ExtInfo { pub name: String, pub xname: String, pub major_version: u32, pub minor_version: u32, } #[derive(Debug, Clone)] pub struct DocField { pub name: String, pub text: String, } #[derive(Debug, Clone)] pub struct DocError { pub typ: String, pub text: String, } #[derive(Debug, Clone)] pub struct DocSee { pub typ: String, pub name: String, } #[derive(Debug, Clone)] pub struct Doc { pub brief: Option, pub description: Option, pub example: Option, pub fields: Vec, pub errors: Vec, pub sees: Vec, } #[derive(Debug, Clone)] pub enum Expr { FieldRef(String), ParamRef(String), EnumRef { name: String, item: String }, Value(usize), Op(String, Box, Box), Unop(String, Box), Popcount(Box), SumOf(String, Option>), ListElementRef, } #[derive(Debug, Clone)] pub enum EnumItem { Bit(String, u32), Value(String, u32), } #[derive(Debug, Clone)] pub struct SwitchCase { pub bit: bool, pub name: Option, pub exprs: Vec, pub fields: Vec, } #[allow(clippy::enum_variant_names)] #[derive(Debug, Clone)] pub enum Field { Pad(usize), AlignPad(usize), Field { name: String, typ: String, r#enum: Option, mask: Option, altenum: Option, altmask: Option, }, List { name: String, typ: String, len_expr: Expr, r#enum: Option, mask: Option, }, ListNoLen { name: String, typ: String, r#enum: Option, mask: Option, }, Expr { name: String, typ: String, expr: Expr, }, ValueParam { mask_typ: String, mask_name: String, list_name: String, }, Fd(String), Switch(String, Expr, Vec), } #[derive(Debug, Clone)] pub struct Reply { pub fields: Vec, pub doc: Option, } #[derive(Debug, Clone)] pub struct EventSelector { pub extension: String, pub xge: bool, pub opcode_range: (u32, u32), } #[derive(Debug, Clone)] pub enum Item { Error { name: String, number: i32, fields: Vec, doc: Option, }, ErrorCopy { name: String, number: i32, r#ref: String, }, Typedef { old_typ: String, new_typ: String, }, XidType { typ: String, }, XidUnion { typ: String, xidtypes: Vec, }, Enum { typ: String, items: Vec, doc: Option, }, Struct { typ: String, fields: Vec, doc: Option, }, Union { typ: String, fields: Vec, doc: Option, }, Event { number: i32, name: String, fields: Vec, no_seq_number: bool, xge: bool, doc: Option, }, EventCopy { name: String, number: i32, r#ref: String, }, EventStruct { typ: String, selectors: Vec, }, Request { name: String, opcode: u32, params: Vec, reply: Option, doc: Option, }, } xcb-1.2.2/build/main.rs000064400000000000000000000136731046102023000130050ustar 00000000000000extern crate quick_xml; mod cg; mod ir; mod output; mod parse; use std::env; use std::fs; use std::io; use std::path::{Path, PathBuf}; use cg::{CodeGen, DepInfo}; use ir::ExtInfo; use output::Output; use parse::{Event, Parser, Result}; fn is_always(name: &str) -> bool { matches!(name, "xproto" | "bigreq" | "xc_misc") } fn has_feature(name: &str) -> bool { env::var(format!("CARGO_FEATURE_{}", name.to_ascii_uppercase())).is_ok() } fn main() -> io::Result<()> { let root = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string()); let xml_dir = Path::new(&root).join("xml"); let out_dir = env::var("OUT_DIR").unwrap_or_else(|_| "./gen/current".to_string()); let out_dir = Path::new(&out_dir); let rustfmt = env::var("RXCB_RUSTFMT").ok().and_then(|var| { if var == "1" || var == "y" || var == "Y" { find_exe("rustfmt") } else { None } }); let gen_all = env::var("RXCB_GENALL").is_ok(); let export = env::var("RXCB_EXPORT").ok(); if rustfmt.is_some() && export.is_some() { panic!("RXCB_EXPORT and RXCB_RUSTFMT do not work well together. Please choose one or the other."); } let dbg_atom_names = has_feature("debug_atom_names"); let mut dep_info = Vec::new(); for xml_file in iter_xml(&xml_dir) { process_xcb_gen( &xml_file, out_dir, &rustfmt, gen_all, &mut dep_info, dbg_atom_names, ) .unwrap_or_else(|err| { panic!( "Error during processing of {}: {:?}", xml_file.display(), err ) }); } if let Some(export) = export { let export = Path::new(&export); fs::create_dir_all(&export)?; for f in iter_out_rs(out_dir) { let copy = export.join(f.file_name().unwrap()); fs::copy(f, copy)?; } } #[cfg(target_os = "freebsd")] println!("cargo:rustc-link-search=/usr/local/lib"); Ok(()) } fn iter_xml(xml_dir: &Path) -> impl Iterator { fs::read_dir(xml_dir) .unwrap() .map(|e| e.unwrap().path()) .filter(|p| match p.extension() { Some(e) => e == "xml", _ => false, }) } fn iter_out_rs(out_dir: &Path) -> impl Iterator { fs::read_dir(out_dir) .unwrap() .map(|e| e.unwrap().path()) .filter(|p| match p.extension() { Some(e) => e == "rs", _ => false, }) } fn find_exe

(exe_name: P) -> Option where P: AsRef, { env::var_os("PATH").and_then(|paths| { env::split_paths(&paths) .filter_map(|dir| { let full_path = dir.join(&exe_name); if full_path.is_file() { Some(full_path) } else { None } }) .next() }) } fn recurse_push_depinfo(dep_info: &mut Vec, xcb_mod: &str, global: &[DepInfo]) { if dep_info.iter().any(|di| di.xcb_mod == xcb_mod) { return; } let di = global .iter() .find(|di| di.xcb_mod == xcb_mod) .unwrap_or_else(|| panic!("can't find dependency {}", xcb_mod)); dep_info.push(di.clone()); for d in &di.deps { recurse_push_depinfo(dep_info, d, global); } } fn process_xcb_gen( xml_file: &Path, out_dir: &Path, rustfmt: &Option, gen_all: bool, dep_info: &mut Vec, dbg_atom_names: bool, ) -> Result<()> { let xcb_mod = xml_file.file_stem().unwrap(); let xcb_mod = xcb_mod.to_str().unwrap().to_string(); if dep_info.iter().any(|di| di.xcb_mod == xcb_mod) { return Ok(()); } if !gen_all && !is_always(&xcb_mod) && !has_feature(&xcb_mod) { return Ok(()); } let mut parser = Parser::from_file(xml_file); let mut imports = Vec::new(); let mut mod_info: Option<(String, Option)> = None; let mut items = Vec::new(); for e in &mut parser { match e? { Event::ModuleInfo { name, extinfo } => { mod_info = Some((name, extinfo)); } Event::Import(imp) => imports.push(imp), Event::Item(item) => items.push(item), _ => {} } } let mod_info = mod_info.expect("no xcb protocol opening"); if xcb_mod != "xproto" && imports.iter().all(|i| i != "xproto") { imports.insert(0, "xproto".to_string()); } let deps = { let mut deps: Vec = Vec::new(); for i in imports.iter() { let xml_file = xml_file.with_file_name(&format!("{}.xml", i)); // panic also from here to have the correct xml_file reported process_xcb_gen( &xml_file, out_dir, rustfmt, gen_all, dep_info, dbg_atom_names, ) .unwrap_or_else(|err| { panic!( "Error during processing of {}: {:?}", xml_file.display(), err ) }); recurse_push_depinfo(&mut deps, i, dep_info); } deps }; let out_file = out_dir.join(&xcb_mod).with_extension("rs"); let mut out = Output::new(rustfmt, &out_file) .unwrap_or_else(|_| panic!("cannot create Rust output file: {}", out_file.display())); let mut cg = CodeGen::new(xcb_mod, &mod_info.1, deps, dbg_atom_names); for item in &items { cg.preregister_item(item); } for item in &items { cg.resolve_type(item); } for item in items { cg.resolve_error_event_request(item); } cg.emit_prologue(&mut out)?; cg.emit_errors(&mut out)?; cg.emit_events(&mut out)?; cg.emit_types(&mut out)?; cg.emit_requests(&mut out)?; dep_info.push(cg.into_depinfo()); Ok(()) } xcb-1.2.2/build/output.rs000064400000000000000000000023141046102023000134070ustar 00000000000000use std::fs::{self, File}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::process::{Child, Command, Stdio}; #[derive(Debug)] pub enum Output { Fmt(Child), Direct(File), } impl Output { pub fn new(rustfmt: &Option, out: &Path) -> io::Result { fs::create_dir_all(out.parent().unwrap())?; let output = File::create(&out)?; Ok(match rustfmt { Some(rustfmt) => Output::Fmt( Command::new(rustfmt) .arg("--emit=stdout") .arg("--edition=2018") .stdin(Stdio::piped()) .stdout(output) .spawn()?, ), None => Output::Direct(output), }) } } impl Write for Output { fn write(&mut self, buf: &[u8]) -> io::Result { match self { Output::Fmt(child) => child.stdin.as_ref().unwrap().write(buf), Output::Direct(file) => file.write(buf), } } fn flush(&mut self) -> io::Result<()> { match self { Output::Fmt(child) => child.stdin.as_ref().unwrap().flush(), Output::Direct(file) => file.flush(), } } } xcb-1.2.2/build/parse.rs000064400000000000000000001351421046102023000131670ustar 00000000000000use quick_xml::events::attributes::{Attribute, Attributes}; use quick_xml::events::{BytesStart, Event as XmlEv}; use quick_xml::Reader as XmlReader; use std::fs::File; use std::io::{self, BufRead, BufReader}; use std::path::Path; use std::result; use std::str::{self, Utf8Error}; use std::sync::Arc; use crate::ir::{ Doc, DocError, DocField, DocSee, EnumItem, EventSelector, Expr, ExtInfo, Field, Item, Reply, SwitchCase, }; #[derive(Debug)] pub enum Error { IO(Arc), Xml(quick_xml::Error), Utf8(Utf8Error), Parse(String), } impl From> for Error { fn from(err: Arc) -> Self { Error::IO(err) } } impl From for Error { fn from(err: io::Error) -> Self { Error::IO(err.into()) } } impl From for Error { fn from(err: Utf8Error) -> Self { Error::Utf8(err) } } impl From for Error { fn from(err: quick_xml::Error) -> Self { match err { quick_xml::Error::Io(e) => Error::IO(e), quick_xml::Error::NonDecodable(Some(e)) => Error::Utf8(e), e => Error::Xml(e), } } } pub type Result = result::Result; #[allow(clippy::large_enum_variant)] pub enum Event { ModuleInfo { name: String, extinfo: Option, }, Import(String), Item(Item), Ignore, // doctype etc. } pub struct Parser { xml: XmlReader, buf: Vec, } impl Iterator for &mut Parser { type Item = Result; fn next(&mut self) -> Option { self.buf.clear(); match self.xml.read_event_into(&mut self.buf) { Ok(XmlEv::Empty(ref e)) => match e.name().into_inner() { b"typedef" => { let names: [&[u8]; 2] = [b"oldname", b"newname"]; let mut vals: [Option; 2] = [None, None]; if let Err(err) = get_attributes(e.attributes(), &names, &mut vals) { return Some(Err(err)); } let [old_typ, new_typ] = vals; match (old_typ, new_typ) { (Some(old_typ), Some(new_typ)) => { Some(Ok(Event::Item(Item::Typedef { old_typ, new_typ }))) } _ => Some(Err(Error::Parse( "typedef without newname and/or oldname".into(), ))), } } b"xidtype" => Some( expect_attribute(e.attributes(), b"name") .map(|typ| Event::Item(Item::XidType { typ })), ), b"error" => Some({ let start = e.to_owned(); self.parse_op_struct(start, b"").map(|ops| { Event::Item(Item::Error { number: ops.number, name: ops.name, fields: ops.content.fields, doc: ops.content.doc, }) }) }), b"errorcopy" => Some({ let start = e.to_owned(); let nsres = self.parse_op_struct(start, b""); if let Ok(ns) = nsres { if ns.r#ref.is_none() { return Some(Err(Error::Parse("".into()))); } let number = ns.number; let r#ref = ns.r#ref.unwrap(); Ok(Event::Item(Item::ErrorCopy { name: ns.name, number, r#ref, })) } else { Err(Error::Parse(" without ref attribute".into())) } }), b"eventcopy" => Some({ let start = e.to_owned(); let nsres = self.parse_op_struct(start, b""); if let Ok(ns) = nsres { if ns.r#ref.is_none() { return Some(Err(Error::Parse("".into()))); } let number = ns.number; let r#ref = ns.r#ref.unwrap(); Ok(Event::Item(Item::EventCopy { name: ns.name, number, r#ref, })) } else { Err(Error::Parse(" without ref attribute".into())) } }), b"request" => { let start = e.to_owned(); Some(self.parse_request(start, true).map(|req| { Event::Item(Item::Request { name: req.name, opcode: req.opcode, params: req.params, reply: req.reply, doc: req.doc, }) })) } name => Some(Err(Error::Parse(format!( "unexpected XML: <{} />", str::from_utf8(name).unwrap() )))), }, Ok(XmlEv::Start(ref e)) => match e.name().into_inner() { b"xcb" => { let names: [&[u8]; 5] = [ b"header", b"extension-xname", b"extension-name", b"major-version", b"minor-version", ]; let mut vals: [Option; 5] = [None, None, None, None, None]; if let Err(err) = get_attributes(e.attributes(), &names, &mut vals) { return Some(Err(err)); } let [mod_name, xname, name, major_version, minor_version] = vals; if mod_name.is_none() { return Some(Err(Error::Parse(" without header attr".into()))); } let mod_name = mod_name.unwrap(); let extinfo = match (xname, name, major_version, minor_version) { (Some(xname), Some(name), Some(major_version), Some(minor_version)) => { let major_version = major_version .parse::() .expect("could not parse major_version"); let minor_version = minor_version .parse::() .expect("could not parse major_version"); Some(ExtInfo { xname, name, major_version, minor_version, }) } (None, None, None, None) => None, _ => panic!("incomplete extension info for {}", &mod_name), }; Some(Ok(Event::ModuleInfo { name: mod_name, extinfo, })) } b"import" => Some(self.parse_import().map(Event::Import)), b"xidunion" => Some(expect_attribute(e.attributes(), b"name").and_then(|typ| { let xidtypesres = self.parse_xidunion_types(); xidtypesres.map(|xidtypes| Event::Item(Item::XidUnion { typ, xidtypes })) })), b"enum" => Some(expect_attribute(e.attributes(), b"name").and_then(|typ| { let enumres = self.parse_enum_content(); enumres.map(|en| { Event::Item(Item::Enum { typ, items: en.0, doc: en.1, }) }) })), b"struct" => Some(expect_attribute(e.attributes(), b"name").and_then(|typ| { let contentres = self.parse_struct_content(b"struct"); contentres.map(|content| { Event::Item(Item::Struct { typ, fields: content.fields, doc: content.doc, }) }) })), b"union" => Some(expect_attribute(e.attributes(), b"name").and_then(|typ| { let contentres = self.parse_struct_content(b"union"); contentres.map(|content| { Event::Item(Item::Union { typ, fields: content.fields, doc: content.doc, }) }) })), b"error" => Some({ let start = e.to_owned(); self.parse_op_struct(start, b"error").map(|ops| { Event::Item(Item::Error { number: ops.number, name: ops.name, fields: ops.content.fields, doc: ops.content.doc, }) }) }), b"event" => Some({ let start = e.to_owned(); self.parse_op_struct(start, b"event").map( |OpStruct { number, name, content, no_seq_number, xge, .. }| { Event::Item(Item::Event { number, name, fields: content.fields, no_seq_number, xge, doc: content.doc, }) }, ) }), b"eventstruct" => Some({ let start = e.to_owned(); self.parse_eventstruct(start) .map(|(typ, selectors)| Event::Item(Item::EventStruct { typ, selectors })) }), b"request" => { let start = e.to_owned(); Some(self.parse_request(start, false).map(|req| { Event::Item(Item::Request { opcode: req.opcode, name: req.name, params: req.params, reply: req.reply, doc: req.doc, }) })) } name => Some(Err(Error::Parse(format!( "unexpected XML: <{}>", str::from_utf8(name).unwrap() )))), }, Ok(XmlEv::Eof) => None, _ => Some(Ok(Event::Ignore)), } } } impl Parser> { pub fn from_file(xml_file: &Path) -> Self { let mut xml = XmlReader::from_file(xml_file).unwrap(); xml.trim_text(true); Parser { xml, buf: Vec::with_capacity(8 * 1024), } } } impl Parser { fn expect_text(&mut self) -> Result { match self.xml.read_event_into(&mut self.buf)? { XmlEv::Text(e) => Ok(str::from_utf8(e.unescape()?.as_bytes())?.into()), XmlEv::CData(e) => Ok(str::from_utf8(&e)?.into()), ev => Err(Error::Parse(format!("expected text, found {:?}", ev))), } } fn expect_text_trim(&mut self, close_tag: &[u8]) -> Result { let txt = match self.xml.read_event_into(&mut self.buf)? { XmlEv::Text(e) => Vec::from(e.unescape()?.as_bytes()), XmlEv::CData(e) => Vec::from(e.into_inner()), XmlEv::End(e) => { if e.name().0 == close_tag { return Ok(String::new()); } else { return Err(Error::Parse(format!( "expected text, found ", str::from_utf8(e.name().0).unwrap() ))); } } ev => return Err(Error::Parse(format!("expected text, found {:?}", ev))), }; let txt = str::from_utf8(&txt)?.trim().into(); if !close_tag.is_empty() { match self.xml.read_event_into(&mut self.buf)? { XmlEv::End(e) => { if e.name().0 == close_tag { return Ok(txt); } Err(Error::Parse(format!( "expected after text", str::from_utf8(close_tag).unwrap() ))) } ev => Err(Error::Parse(format!( "expected , found {:?}", str::from_utf8(close_tag).unwrap(), ev ))), } } else { Ok(txt) } } fn expect_start(&mut self) -> Result> { match self.xml.read_event_into(&mut self.buf)? { XmlEv::Start(e) | XmlEv::Empty(e) => Ok(Vec::from(e.name().0)), ev => Err(Error::Parse(format!("expected start tag, found {:?}", ev))), } } fn expect_close_tag(&mut self, tag: &[u8]) -> Result<()> { loop { match self.xml.read_event_into(&mut self.buf)? { XmlEv::End(e) => { if e.name().0 == tag { return Ok(()); } else { return Err(Error::Parse(format!( "expected , got ", str::from_utf8(tag).unwrap(), str::from_utf8(e.name().0)? ))); } } XmlEv::Comment(_) => {} ev => { return Err(Error::Parse(format!( "expected , found {:?}", str::from_utf8(tag).unwrap(), ev ))) } } } } fn expect_text_element(&mut self) -> Result<(Vec, String)> { let tag = self.expect_start()?; let txt = self.expect_text()?; self.expect_close_tag(&tag)?; Ok((tag, txt)) } fn parse_doc(&mut self) -> Result { let mut brief = None; let mut description = None; let mut example = None; let mut fields = Vec::new(); let mut errors = Vec::new(); let mut sees = Vec::new(); loop { match self.xml.read_event_into(&mut self.buf)? { XmlEv::Start(ref e) => match e.name().0 { b"brief" => { brief = Some(self.expect_text_trim(b"brief")?); } b"description" => { description = Some(self.expect_text_trim(b"description")?); } b"example" => { example = Some(self.expect_text_trim(b"example")?); } b"field" => { let name = expect_attribute(e.attributes(), b"name")?; let text = self.expect_text_trim(b"field")?; fields.push(DocField { name, text }); } b"error" => { let typ = expect_attribute(e.attributes(), b"type")?; let text = self.expect_text_trim(b"error")?; errors.push(DocError { typ, text }); } b"see" => { let typ = expect_attribute(e.attributes(), b"type")?; let name = self.expect_text_trim(b"error")?; sees.push(DocSee { typ, name }); } _ => {} }, XmlEv::Empty(ref e) => { if e.name().0 == b"field" { let name = expect_attribute(e.attributes(), b"name")?; fields.push(DocField { name, text: String::new(), }); } } XmlEv::Text(_) | XmlEv::CData(_) => { return Err(Error::Parse("Unexpected doc text out of element".into())); } XmlEv::End(ref e) => { if e.name().0 == b"doc" { break; } } _ => {} } } Ok(Doc { brief, description, fields, errors, example, sees, }) } fn parse_expr_content( &mut self, attrs: Attributes, tag: &[u8], is_empty: bool, ) -> Result { match tag { b"fieldref" => { let fr = self.expect_text_trim(b"fieldref")?; Ok(Expr::FieldRef(fr)) } b"paramref" => { let pr = self.expect_text_trim(b"paramref")?; Ok(Expr::ParamRef(pr)) } b"enumref" => { let name = expect_attribute(attrs, b"ref")?; let item = self.expect_text_trim(b"enumref")?; Ok(Expr::EnumRef { name, item }) } b"value" => { let val = self.expect_text_trim(b"value")?; let val: usize = val.parse().map_err(|e| { Error::Parse(format!("could not parse expr tag: {}", e)) })?; Ok(Expr::Value(val)) } b"op" => { let op = expect_attribute(attrs, b"op")?; let lhs = self.parse_expr(b"")?.unwrap(); let rhs = self.parse_expr(b"")?.unwrap(); self.expect_close_tag(b"op")?; Ok(Expr::Op(op, Box::new(lhs), Box::new(rhs))) } b"unop" => { let unop = expect_attribute(attrs, b"op")?; let expr = self.parse_expr(b"")?.unwrap(); self.expect_close_tag(b"unop")?; Ok(Expr::Unop(unop, Box::new(expr))) } b"popcount" => { let expr = self.parse_expr(b"")?.unwrap(); self.expect_close_tag(b"popcount")?; Ok(Expr::Popcount(Box::new(expr))) } b"sumof" => { let list_ref = expect_attribute(attrs, b"ref")?; let expr = if is_empty { None } else { self.parse_expr(b"sumof")? }; Ok(Expr::SumOf(list_ref, expr.map(Box::new))) } b"listelement-ref" => Ok(Expr::ListElementRef), tag => Err(Error::Parse(format!( "Unexpected <{}> in expression", str::from_utf8(tag)? ))), } } fn parse_expr(&mut self, empty_end_tag: &[u8]) -> Result> { match self.xml.read_event_into(&mut self.buf)? { XmlEv::Start(ref e) => { let e = e.to_owned(); Ok(Some(self.parse_expr_content( e.attributes(), e.name().0, false, )?)) } XmlEv::Empty(ref e) => { let e = e.to_owned(); Ok(Some(self.parse_expr_content( e.attributes(), e.name().0, true, )?)) } XmlEv::Comment(_) => self.parse_expr(empty_end_tag), // in case of comment, we just parse the next one XmlEv::End(e) => { if e.name().0 == empty_end_tag { Ok(None) } else { Err(Error::Parse(format!( "Unexpected while parsing expression", str::from_utf8(e.name().0).unwrap() ))) } } ev => Err(Error::Parse(format!( "Unexpected XML while parsing expression: {:?}", ev ))), } } fn parse_import(&mut self) -> Result { let imp = self.expect_text()?; self.expect_close_tag(b"import")?; Ok(imp) } fn parse_xidunion_types(&mut self) -> Result> { let mut xidtypes = Vec::new(); loop { match self.xml.read_event_into(&mut self.buf)? { XmlEv::Start(ref e) => match e.name().0 { b"type" => { let typ = self.expect_text_trim(b"type")?; xidtypes.push(typ); } tag => { return Err(Error::Parse(format!( "Unexpected <{}> in ", str::from_utf8(tag)? ))); } }, XmlEv::End(ref e) => match e.name().0 { b"xidunion" => break, _ => unreachable!(), }, ev => { return Err(Error::Parse(format!( "Unexpected XML in : {:?}", ev ))); } } } Ok(xidtypes) } fn parse_enum_content(&mut self) -> Result<(Vec, Option)> { let mut items = Vec::new(); let mut doc = None; loop { match self.xml.read_event_into(&mut self.buf)? { XmlEv::Empty(ref e) | XmlEv::Start(ref e) => match e.name().0 { b"item" => { let name = expect_attribute(e.attributes(), b"name")?; let (tag, value) = self.expect_text_element()?; if tag != b"bit" && tag != b"value" { return Err(Error::Parse(format!( "expected or for enum {}, got <{}>", name, str::from_utf8(&tag)? ))); } let value: u32 = value.parse().map_err(|e| { Error::Parse(format!( "failed to parse {} of enum {}: {}", str::from_utf8(&tag).unwrap(), name, e )) })?; let item = if tag == b"bit" { EnumItem::Bit(name, value) } else { EnumItem::Value(name, value) }; items.push(item); } b"doc" => { doc = Some(self.parse_doc()?); } tag => { return Err(Error::Parse(format!( "Unexpected tag in enum: {}", str::from_utf8(tag)? ))); } }, XmlEv::End(ref e) => match e.name().0 { b"enum" => break, b"item" => continue, tag => { return Err(Error::Parse(format!( "Unexpected in enum", str::from_utf8(tag)?, ))) } }, XmlEv::Comment(_) => {} ev => { return Err(Error::Parse(format!("unexpected XML in enum: {:?}", ev))); } } } Ok((items, doc)) } fn parse_field_content(&mut self, e: &BytesStart, empty_tag: bool) -> Result> { if empty_tag { match e.name().0 { b"required_start_align" => { // this is meant for checking if padding is correct in the XML // we simply ignore this in the rust bindings Ok(None) } b"field" => { let mut r#type = None; let mut name = None; let mut r#enum = None; let mut mask = None; let mut altenum = None; let mut altmask = None; for attr in e.attributes() { match attr { Ok(attr) if attr.key.0 == b"type" => { r#type = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"name" => { name = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"enum" => { r#enum = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"mask" => { mask = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"altenum" => { altenum = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"altmask" => { altmask = Some(attr_value(&attr).unwrap()); } Ok(attr) => unreachable!( "field attribute {}", str::from_utf8(attr.key.0).unwrap() ), Err(err) => return Err(quick_xml::Error::InvalidAttr(err).into()), } } if let (Some(typ), Some(name)) = (r#type, name) { Ok(Some(Field::Field { name, typ, r#enum, mask, altenum, altmask, })) } else { Err(Error::Parse("struct field without type and/or name".into())) } } b"pad" => { let names: [&[u8]; 2] = [b"bytes", b"align"]; let mut vals: [Option; 2] = [None, None]; get_attributes(e.attributes(), &names, &mut vals)?; let [bytes, align] = vals; if bytes.is_some() && align.is_some() { return Err(Error::Parse(" with both align and bytes attr".into())); } if let Some(bytes) = bytes { let val: usize = bytes.parse().map_err(|e| { Error::Parse(format!("failed to parse pad bytes of struct: {}", e)) })?; Ok(Some(Field::Pad(val))) } else if let Some(align) = align { let val: usize = align.parse().map_err(|e| { Error::Parse(format!("failed to parse pad bytes of struct: {}", e)) })?; Ok(Some(Field::AlignPad(val))) } else { Err(Error::Parse( " with neither align and bytes attr".into(), )) } } b"list" => { let mut r#type = None; let mut name = None; let mut r#enum = None; let mut mask = None; for attr in e.attributes() { match attr { Ok(attr) if attr.key.0 == b"type" => { r#type = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"name" => { name = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"enum" => { r#enum = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"mask" => { mask = Some(attr_value(&attr).unwrap()); } Ok(attr) => unreachable!( "field attribute {}", str::from_utf8(attr.key.0).unwrap() ), Err(err) => return Err(quick_xml::Error::InvalidAttr(err).into()), } } if let (Some(typ), Some(name)) = (r#type, name) { Ok(Some(Field::ListNoLen { name, typ, r#enum, mask, })) } else { Err(Error::Parse( " tag without type and/or name attribute".into(), )) } } b"valueparam" => { let names: [&[u8]; 3] = [b"value-mask-type", b"value-mask-name", b"value-list-name"]; let mut vals: [Option; 3] = [None, None, None]; get_attributes(e.attributes(), &names, &mut vals)?; let [mask_typ, mask_name, list_name] = vals; if let (Some(mask_typ), Some(mask_name), Some(list_name)) = (mask_typ, mask_name, list_name) { Ok(Some(Field::ValueParam { mask_typ, mask_name, list_name, })) } else { Err(Error::Parse( " tag without value-mask-type, value-mask-name or value-list-name attribute".into(), )) } } b"fd" => { let name = expect_attribute(e.attributes(), b"name")?; Ok(Some(Field::Fd(name))) } tag => Err(Error::Parse(format!( "Unexpected <{} /> in field", str::from_utf8(tag)? ))), } } else { match e.name().0 { b"list" => { let mut r#type = None; let mut name = None; let mut r#enum = None; let mut mask = None; for attr in e.attributes() { match attr { Ok(attr) if attr.key.0 == b"type" => { r#type = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"name" => { name = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"enum" => { r#enum = Some(attr_value(&attr).unwrap()); } Ok(attr) if attr.key.0 == b"mask" => { mask = Some(attr_value(&attr).unwrap()); } Ok(attr) => unreachable!( "field attribute {}", str::from_utf8(attr.key.0).unwrap() ), Err(err) => return Err(quick_xml::Error::InvalidAttr(err).into()), } } if let (Some(typ), Some(name)) = (r#type, name) { let len_expr = self.parse_expr(b"list")?; Ok(Some(match len_expr { Some(len_expr) => Field::List { name, typ, len_expr, r#enum, mask, }, None => Field::ListNoLen { name, typ, r#enum, mask, }, })) } else { Err(Error::Parse( " tag without type and/or name attribute".into(), )) } } b"exprfield" => { let names: [&[u8]; 2] = [b"type", b"name"]; let mut vals: [Option; 2] = [None, None]; get_attributes(e.attributes(), &names, &mut vals)?; let [typ, nam] = vals; if let (Some(typ), Some(name)) = (typ, nam) { let expr = self.parse_expr(b"")?.unwrap(); self.xml.read_to_end_into( quick_xml::name::QName(b"exprfield"), &mut self.buf, )?; Ok(Some(Field::Expr { name, typ, expr })) } else { Err(Error::Parse( " tag without type and/or name attribute".into(), )) } } b"switch" => { let name = expect_attribute(e.attributes(), b"name")?; let (expr, cases) = self.parse_switch()?; Ok(Some(Field::Switch(name, expr, cases))) } tag => Err(Error::Parse(format!( "Unexpected <{}> in field", str::from_utf8(tag)? ))), } } } fn parse_struct_content(&mut self, end_tag: &[u8]) -> Result { let mut fields = Vec::new(); let mut reply = None; let mut doc = None; loop { match self.xml.read_event_into(&mut self.buf)? { XmlEv::Empty(ref e) => { let e = e.to_owned(); let f = self.parse_field_content(&e, true)?; if let Some(f) = f { fields.push(f); } } XmlEv::Start(ref e) => match e.name().0 { b"list" | b"exprfield" | b"switch" => { let e = e.to_owned(); let f = self.parse_field_content(&e, false)?; if let Some(f) = f { fields.push(f); } } b"doc" => { doc = Some(self.parse_doc()?); } b"reply" => { let StructContent { fields, doc, .. } = self.parse_struct_content(b"reply")?; reply = Some(Reply { fields, doc }) } tag => { return Err(Error::Parse(format!( "Unexpected <{} /> in struct: {:?}", str::from_utf8(tag)?, e ))) } }, XmlEv::End(ref e) => { if e.name().0 == end_tag { break; } } XmlEv::Comment(_) => {} ev => { return Err(Error::Parse(format!("unexpected XML in struct: {:?}", ev))); } } } Ok(StructContent { fields, reply, doc }) } fn parse_op_struct(&mut self, start: BytesStart, end_tag: &[u8]) -> Result { let names: [&[u8]; 5] = [b"name", b"number", b"ref", b"no-sequence-number", b"xge"]; let mut vals: [Option; 5] = [None, None, None, None, None]; get_attributes(start.attributes(), &names, &mut vals)?; let [name, number, r#ref, no_seq_number, xge] = vals; // FIXME: check if true or false let no_seq_number = no_seq_number.is_some(); let xge = xge.is_some(); match (name, number) { (Some(name), Some(number)) => { let number = str::parse::(&number) .map_err(|_| Error::Parse(format!("can't parse {} as number", number)))?; let content = if end_tag.is_empty() { StructContent { fields: Vec::new(), doc: None, reply: None, } } else { self.parse_struct_content(end_tag)? }; Ok(OpStruct { number, name, content, r#ref, no_seq_number, xge, }) } _ => Err(Error::Parse(format!( "<{}> without name or number", str::from_utf8(start.name().0)? ))), } } fn parse_eventstruct(&mut self, start: BytesStart) -> Result<(String, Vec)> { let name = expect_attribute(start.attributes(), b"name")?; let mut selectors = Vec::new(); loop { match self.xml.read_event_into(&mut self.buf)? { XmlEv::Empty(ref e) if e.name().0 == b"allowed" => { let names: [&[u8]; 4] = [b"extension", b"xge", b"opcode-min", b"opcode-max"]; let mut vals: [Option; 4] = [None, None, None, None]; get_attributes(e.attributes(), &names, &mut vals)?; let [extension, xge, opcode_min, opcode_max] = vals; match (extension, xge, opcode_min, opcode_max) { (Some(extension), Some(xge), Some(opcode_min), Some(opcode_max)) => { let xge = xge == "true"; let opcode_min = opcode_min .parse::() .expect("opcode-min must be a number"); let opcode_max = opcode_max .parse::() .expect("opcode-max must be a number"); selectors.push(EventSelector { extension, xge, opcode_range: (opcode_min, opcode_max), }); } _ => { return Err(Error::Parse( "Incomplete element for event selector".into(), )); } } } XmlEv::End(ref e) if e.name().0 == b"eventstruct" => { break; } XmlEv::Comment(..) => {} ev => { return Err(Error::Parse(format!("Unexpected XML: {:#?}", ev))); } } } Ok((name, selectors)) } fn parse_request(&mut self, start: BytesStart, is_empty: bool) -> Result { let names: [&[u8]; 2] = [b"name", b"opcode"]; let mut vals: [Option; 2] = [None, None]; get_attributes(start.attributes(), &names, &mut vals)?; let [name, opcode] = vals; if name.is_none() && opcode.is_none() { return Err(Error::Parse( " without name or opcode attributes".into(), )); } let name = name.unwrap(); let opcode = opcode.unwrap(); let opcode: u32 = str::parse(&opcode) .map_err(|_| Error::Parse(format!("cannot parse {} as int", &opcode)))?; if is_empty { Ok(Request { name, opcode, params: Vec::new(), doc: None, reply: None, }) } else { let StructContent { fields, doc, reply } = self.parse_struct_content(b"request")?; Ok(Request { name, opcode, params: fields, doc, reply, }) } } fn parse_switch_case(&mut self, name: Option, end_tag: &[u8]) -> Result { let bit = end_tag == b"bitcase"; let mut exprs = Vec::new(); let mut fields = Vec::new(); loop { match self.xml.read_event_into(&mut self.buf)? { XmlEv::Start(ref e) => { let e = e.to_owned(); if is_expr_tag(e.name().0) { let expr = self.parse_expr_content(e.attributes(), e.name().0, false)?; exprs.push(expr); } else { let field = self.parse_field_content(&e, false)?; if let Some(field) = field { fields.push(field); } } } XmlEv::Empty(ref e) => { let e = e.to_owned(); if is_expr_tag(e.name().0) { let expr = self.parse_expr_content(e.attributes(), e.name().0, true)?; exprs.push(expr); } else { let field = self.parse_field_content(&e, true)?; if let Some(field) = field { fields.push(field); } } } XmlEv::Comment(_) => {} XmlEv::End(ref e) => { if e.name().0 == end_tag { break; } } ev => { return Err(Error::Parse(format!( "unexpected XML in switch case: {:?}", ev ))) } } } Ok(SwitchCase { bit, name, exprs, fields, }) } fn parse_switch(&mut self) -> Result<(Expr, Vec)> { let expr = self.parse_expr(b"")?.unwrap(); let mut cases = Vec::new(); loop { match self.xml.read_event_into(&mut self.buf)? { XmlEv::Start(ref e) => { let names: [&[u8]; 1] = [b"name"]; let mut vals: [Option; 1] = [None]; get_attributes(e.attributes(), &names, &mut vals)?; let [name] = vals; match e.name().0 { b"bitcase" => { cases.push(self.parse_switch_case(name, b"bitcase")?); } b"case" => { cases.push(self.parse_switch_case(name, b"case")?); } name => { return Err(Error::Parse(format!( "unexpected <{}> in ", str::from_utf8(name).unwrap() ))); } } } XmlEv::End(ref e) => { if e.name().0 == b"switch" { break; } } _ => {} } } Ok((expr, cases)) } } struct OpStruct { number: i32, name: String, content: StructContent, r#ref: Option, no_seq_number: bool, xge: bool, } struct StructContent { fields: Vec, reply: Option, doc: Option, } pub struct Request { pub name: String, pub opcode: u32, pub params: Vec, pub reply: Option, pub doc: Option, } fn is_expr_tag(tag: &[u8]) -> bool { matches!( tag, b"fieldref" | b"paramref" | b"op" | b"unop" | b"popcount" | b"value" | b"enumref" | b"sumof" ) } fn attr_value(attr: &Attribute) -> Result { let val = attr.unescape_value()?; Ok(str::from_utf8(val.as_bytes())?.into()) } fn expect_attribute(attrs: Attributes, name: &[u8]) -> Result { for attr in attrs { match attr { Ok(attr) => { if attr.key.0 == name { return attr_value(&attr); } } Err(err) => return Err(quick_xml::Error::InvalidAttr(err).into()), } } Err(Error::Parse(format!( "could not find expected attribute: {}", str::from_utf8(name).unwrap() ))) } fn get_attributes(attrs: Attributes, names: &[&[u8]], output: &mut [Option]) -> Result<()> { assert_eq!(names.len(), output.len()); for attr in attrs { match attr { Ok(attr) => { for (i, nam) in names.iter().enumerate() { if attr.key.0 == *nam { output[i] = Some(attr_value(&attr)?); } } } Err(err) => return Err(quick_xml::Error::InvalidAttr(err).into()), } } Ok(()) } xcb-1.2.2/examples/basic_window.rs000064400000000000000000000105601046102023000152400ustar 00000000000000use xcb::{x, Xid}; xcb::atoms_struct! { #[derive(Debug)] struct Atoms { wm_protocols => b"WM_PROTOCOLS", wm_del_window => b"WM_DELETE_WINDOW", wm_state => b"_NET_WM_STATE", wm_state_maxv => b"_NET_WM_STATE_MAXIMIZED_VERT", wm_state_maxh => b"_NET_WM_STATE_MAXIMIZED_HORZ", } } fn main() -> xcb::Result<()> { let (conn, screen_num) = xcb::Connection::connect(None).unwrap(); let setup = conn.get_setup(); let screen = setup.roots().nth(screen_num as usize).unwrap(); let window: x::Window = conn.generate_id(); println!("window: {}", window.resource_id()); println!("root: {}", screen.root().resource_id()); println!("root_visual: {}", screen.root_visual()); conn.send_request(&x::CreateWindow { depth: x::COPY_FROM_PARENT as u8, wid: window, parent: screen.root(), x: 0, y: 0, width: 150, height: 150, border_width: 10, class: x::WindowClass::InputOutput, visual: screen.root_visual(), value_list: &[ x::Cw::BackPixel(screen.white_pixel()), x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), ], }); conn.send_request(&x::MapWindow { window }); let title = "Basic Window"; conn.send_request(&x::ChangeProperty { mode: x::PropMode::Replace, window, property: x::ATOM_WM_NAME, r#type: x::ATOM_STRING, data: title.as_bytes(), }); conn.flush()?; // retrieving title let cookie = conn.send_request(&x::GetProperty { delete: false, window, property: x::ATOM_WM_NAME, r#type: x::ATOM_STRING, long_offset: 0, long_length: 1024, }); let reply = conn.wait_for_reply(cookie)?; assert_eq!(reply.value::(), title.as_bytes()); // retrieving a few atoms let atoms = Atoms::intern_all(&conn)?; println!("atoms = {:#?}", atoms); // activate the sending of close event through `x::Event::ClientMessage` // either the request must be checked as follow, or conn.flush() must be called before entering the loop conn.send_and_check_request(&x::ChangeProperty { mode: x::PropMode::Replace, window, property: atoms.wm_protocols, r#type: x::ATOM_ATOM, data: &[atoms.wm_del_window], })?; let mut maximized = false; loop { let event = match conn.wait_for_event() { Err(xcb::Error::Connection(err)) => { panic!("unexpected I/O error: {}", err); } Err(xcb::Error::Protocol(err)) => { panic!("unexpected protocol error: {:#?}", err); } Ok(event) => event, }; println!("Received event {:#?}", event); match event { xcb::Event::X(x::Event::KeyPress(ev)) => { println!("Key '{}' pressed", ev.detail()); if ev.detail() == 0x3a { // M (on qwerty) // toggle maximized let data = x::ClientMessageData::Data32([ if maximized { 0 } else { 1 }, atoms.wm_state_maxv.resource_id(), atoms.wm_state_maxh.resource_id(), 0, 0, ]); let event = x::ClientMessageEvent::new(window, atoms.wm_state, data); conn.send_request(&x::SendEvent { propagate: false, destination: x::SendEventDest::Window(screen.root()), event_mask: x::EventMask::STRUCTURE_NOTIFY, event: &event, }); conn.flush()?; maximized = !maximized; } else if ev.detail() == 0x18 { // Q (on qwerty) break Ok(()); } } xcb::Event::X(x::Event::ClientMessage(ev)) => { if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { if atom == atoms.wm_del_window.resource_id() { // window "x" button clicked by user, we gracefully exit break Ok(()); } } } _ => {} } } } xcb-1.2.2/examples/connect.rs000064400000000000000000000003221046102023000142140ustar 00000000000000fn main() { if let Ok((_, screen_num)) = xcb::Connection::connect(None) { println!("Connected to X on screen \"{}\"!", screen_num); } else { println!("Could not connect to X!"); } } xcb-1.2.2/examples/connect_str.rs000064400000000000000000000003351046102023000151100ustar 00000000000000fn main() { let dpy = ":0"; if let Ok((_, _)) = xcb::Connection::connect(Some(&dpy)) { println!("Connected to X on display \"{}\"!", dpy); } else { println!("Could not connect to X!"); } } xcb-1.2.2/examples/draw_root_window.rs000064400000000000000000000043741046102023000161650ustar 00000000000000use xcb::x; fn main() -> xcb::Result<()> { let rectangles: &[x::Rectangle] = &[x::Rectangle { x: 200, y: 200, width: 400, height: 400, }]; let (conn, screen_num) = xcb::Connection::connect(None).unwrap(); let setup = conn.get_setup(); let screen = setup.roots().nth(screen_num as usize).unwrap(); /* get the root window of the screen */ let window = screen.root(); /* Request modification of the root window attributes: background color and * event mask */ conn.send_request(&x::ChangeWindowAttributes { window, value_list: &[ x::Cw::BackPixel(screen.white_pixel()), // events that will be waited for x::Cw::EventMask(x::EventMask::EXPOSURE), ], }); let gc: x::Gcontext = conn.generate_id(); /* Request the creation of the graphical context */ conn.send_request(&x::CreateGc { cid: gc, drawable: x::Drawable::Window(window), value_list: &[ x::Gc::Foreground(screen.black_pixel()), x::Gc::Background(screen.white_pixel()), x::Gc::LineWidth(1), x::Gc::LineStyle(x::LineStyle::OnOffDash), x::Gc::SubwindowMode(x::SubwindowMode::IncludeInferiors), ], }); conn.send_request(&x::MapWindow { window }); /* Flush the requests */ conn.flush()?; loop { let event = match conn.wait_for_event() { Err(xcb::Error::Connection(xcb::ConnError::Connection)) => { // graceful shutdown, likely "x" close button clicked in title bar break Ok(()); } Err(err) => { panic!("unexpected error: {:#?}", err); } Ok(event) => event, }; match event { xcb::Event::X(x::Event::Expose(_ev)) => { let drawable = x::Drawable::Window(window); /* Draw a rectangle on screen using the graphical context */ conn.send_request(&x::PolyRectangle { drawable, gc, rectangles, }); /* We flush the request */ conn.flush()?; } _ => {} } } } xcb-1.2.2/examples/drawing.rs000064400000000000000000000105041046102023000142210ustar 00000000000000use xcb::x; fn main() -> xcb::Result<()> { let points: &[x::Point] = &[ x::Point { x: 10, y: 10 }, x::Point { x: 10, y: 20 }, x::Point { x: 20, y: 10 }, x::Point { x: 20, y: 20 }, ]; let polyline: &[x::Point] = &[ x::Point { x: 50, y: 10 }, x::Point { x: 5, y: 20 }, x::Point { x: 25, y: -20 }, /* rest of points are relative */ x::Point { x: 10, y: 10 }, ]; let segments: &[x::Segment] = &[ x::Segment { x1: 100, y1: 10, x2: 140, y2: 30, }, x::Segment { x1: 110, y1: 25, x2: 130, y2: 60, }, ]; let rectangles: &[x::Rectangle] = &[ x::Rectangle { x: 10, y: 50, width: 40, height: 20, }, x::Rectangle { x: 80, y: 50, width: 10, height: 40, }, ]; let arcs: &[x::Arc] = &[ x::Arc { x: 10, y: 100, width: 60, height: 40, angle1: 0, angle2: 90 << 6, }, x::Arc { x: 90, y: 100, width: 55, height: 40, angle1: 0, angle2: 270 << 6, }, ]; let (conn, screen_num) = xcb::Connection::connect(None).unwrap(); let setup = conn.get_setup(); let screen = setup.roots().nth(screen_num as usize).unwrap(); let gc: x::Gcontext = conn.generate_id(); let window: x::Window = conn.generate_id(); conn.send_request(&x::CreateWindow { depth: x::COPY_FROM_PARENT as u8, wid: window, parent: screen.root(), x: 0, y: 0, width: 150, height: 150, border_width: 10, class: x::WindowClass::InputOutput, visual: screen.root_visual(), value_list: &[ x::Cw::BackPixel(screen.white_pixel()), x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), ], }); conn.send_request(&x::MapWindow { window }); conn.send_request(&x::CreateGc { cid: gc, drawable: x::Drawable::Window(window), value_list: &[ x::Gc::Foreground(screen.black_pixel()), x::Gc::GraphicsExposures(false), ], }); conn.flush()?; loop { let event = match conn.wait_for_event() { Err(xcb::Error::Connection(xcb::ConnError::Connection)) => { // graceful shutdown, likely "x" close button clicked in title bar break Ok(()); } Err(err) => { panic!("unexpected error: {:#?}", err); } Ok(event) => event, }; match event { xcb::Event::X(x::Event::Expose(_ev)) => { let drawable = x::Drawable::Window(window); /* We draw the points */ conn.send_request(&x::PolyPoint { coordinate_mode: x::CoordMode::Origin, drawable, gc, points, }); /* We draw the polygonal line */ conn.send_request(&x::PolyLine { coordinate_mode: x::CoordMode::Previous, drawable, gc, points: &polyline, }); /* We draw the segements */ conn.send_request(&x::PolySegment { drawable, gc, segments, }); /* We draw the rectangles */ conn.send_request(&x::PolyRectangle { drawable, gc, rectangles, }); /* We draw the arcs */ conn.send_request(&x::PolyArc { drawable, gc, arcs }); /* We flush the request */ conn.flush()?; } xcb::Event::X(x::Event::KeyPress(key_press)) => { println!("Key '{}' pressed", key_press.detail()); if key_press.detail() == 0x18 { // Q (on qwerty) break Ok(()); } } _ => {} } } } xcb-1.2.2/examples/get_all_windows.rs000064400000000000000000000031351046102023000157510ustar 00000000000000use std::str; use xcb::x; fn main() -> xcb::Result<()> { let (conn, _) = xcb::Connection::connect(None).unwrap(); let setup = conn.get_setup(); let wm_client_list = conn.send_request(&x::InternAtom { only_if_exists: true, name: "_NET_CLIENT_LIST".as_bytes(), }); let wm_client_list = conn.wait_for_reply(wm_client_list)?.atom(); assert!(wm_client_list != x::ATOM_NONE, "EWMH not supported"); for screen in setup.roots() { let window = screen.root(); let pointer = conn.send_request(&x::QueryPointer { window }); let pointer = conn.wait_for_reply(pointer)?; if pointer.same_screen() { let list = conn.send_request(&x::GetProperty { delete: false, window, property: wm_client_list, r#type: x::ATOM_NONE, long_offset: 0, long_length: 100, }); let list = conn.wait_for_reply(list)?; for client in list.value::() { let cookie = conn.send_request(&x::GetProperty { delete: false, window: *client, property: x::ATOM_WM_NAME, r#type: x::ATOM_STRING, long_offset: 0, long_length: 1024, }); let reply = conn.wait_for_reply(cookie)?; let title = reply.value(); let title = str::from_utf8(title).expect("invalid UTF-8"); println!("{}", title); } } } Ok(()) } xcb-1.2.2/examples/get_keyboard_names.rs000064400000000000000000000021771046102023000164170ustar 00000000000000use xcb::{self, xinput, xkb}; use xcb::{Connection, Extension}; fn main() -> xcb::Result<()> { let (conn, _screen_num) = Connection::connect_with_xlib_display_and_extensions( &[Extension::Input, Extension::Xkb], &[], )?; let cookie = conn.send_request(&xinput::ListInputDevices {}); let devices = conn.wait_for_reply(cookie)?; devices .devices() .iter() .filter(|device| { device.device_use() == xinput::DeviceUse::IsXExtensionKeyboard || device.device_use() == xinput::DeviceUse::IsXKeyboard }) .try_for_each::<_, xcb::Result<()>>(|device| { let device_spec = device.device_id() as xkb::DeviceSpec; let cookie = conn.send_request(&xcb::xkb::GetNames { device_spec, which: xkb::NameDetail::SYMBOLS, }); let components = conn.wait_for_reply(cookie)?; println!( "Get components for device {} got result {:?}", device_spec, components.value_list() ); Ok(()) })?; Ok(()) } xcb-1.2.2/examples/opengl_window.rs000064400000000000000000000232761046102023000154530ustar 00000000000000use xcb::ffi::xcb_generic_event_t; use xcb::{self, dri2, glx, x}; use xcb::{Raw, Xid}; use x11::glx::*; use x11::xlib; use std::ffi::{CStr, CString}; use std::os::raw::{c_int, c_void}; use std::ptr; const GLX_CONTEXT_MAJOR_VERSION_ARB: u32 = 0x2091; const GLX_CONTEXT_MINOR_VERSION_ARB: u32 = 0x2092; type GlXCreateContextAttribsARBProc = unsafe extern "C" fn( dpy: *mut xlib::Display, fbc: GLXFBConfig, share_context: GLXContext, direct: xlib::Bool, attribs: *const c_int, ) -> GLXContext; unsafe fn load_gl_func(name: &str) -> *mut c_void { let cname = CString::new(name).unwrap(); let ptr: *mut c_void = std::mem::transmute(glXGetProcAddress(cname.as_ptr() as *const u8)); if ptr.is_null() { panic!("could not load {}", name); } ptr } fn check_glx_extension(glx_exts: &str, ext_name: &str) -> bool { for glx_ext in glx_exts.split(" ") { if glx_ext == ext_name { return true; } } false } static mut CTX_ERROR_OCCURED: bool = false; unsafe extern "C" fn ctx_error_handler( _dpy: *mut xlib::Display, _ev: *mut xlib::XErrorEvent, ) -> i32 { CTX_ERROR_OCCURED = true; 0 } unsafe fn check_gl_error() { let err = gl::GetError(); if err != gl::NO_ERROR { println!("got gl error {}", err); } } fn get_glxfbconfig( dpy: *mut xlib::Display, screen_num: i32, visual_attribs: &[i32], ) -> GLXFBConfig { unsafe { let mut fbcount: c_int = 0; let fbcs = glXChooseFBConfig( dpy, screen_num, visual_attribs.as_ptr(), &mut fbcount as *mut c_int, ); if fbcount == 0 { panic!("could not find compatible fb config"); } // we pick the first from the list let fbc = *fbcs; xlib::XFree(fbcs as *mut c_void); fbc } } fn main() -> xcb::Result<()> { let (conn, screen_num) = xcb::Connection::connect_with_xlib_display_and_extensions(&[], &[xcb::Extension::Dri2])?; conn.set_event_queue_owner(xcb::EventQueueOwner::Xcb); let glx_ver = conn.wait_for_reply(conn.send_request(&glx::QueryVersion { major_version: 1, minor_version: 3, }))?; assert!(glx_ver.major_version() >= 1 && glx_ver.minor_version() >= 3); let fbc = get_glxfbconfig( conn.get_raw_dpy(), screen_num, &[ GLX_X_RENDERABLE, 1, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, 8, GLX_DOUBLEBUFFER, 1, 0, ], ); let vi_ptr: *mut xlib::XVisualInfo = unsafe { glXGetVisualFromFBConfig(conn.get_raw_dpy(), fbc) }; let vi = unsafe { *vi_ptr }; // retrieving a few atoms let (wm_protocols, wm_del_window) = { let cookies = ( conn.send_request(&x::InternAtom { only_if_exists: false, name: b"WM_PROTOCOLS", }), conn.send_request(&x::InternAtom { only_if_exists: false, name: b"WM_DELETE_WINDOW", }), ); ( conn.wait_for_reply(cookies.0)?.atom(), conn.wait_for_reply(cookies.1)?.atom(), ) }; let setup = conn.get_setup(); let screen = setup.roots().nth(vi.screen as usize).unwrap(); let cmap: x::Colormap = conn.generate_id(); let win: x::Window = conn.generate_id(); conn.send_request(&x::CreateColormap { alloc: x::ColormapAlloc::None, mid: cmap, window: screen.root(), visual: vi.visualid as u32, }); conn.send_request(&x::CreateWindow { depth: x::COPY_FROM_PARENT as u8, wid: win, parent: screen.root(), x: 0, y: 0, width: 640, height: 480, border_width: 0, class: x::WindowClass::InputOutput, visual: vi.visualid as u32, value_list: &[ x::Cw::BackPixel(screen.white_pixel()), x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), x::Cw::Colormap(cmap), ], }); unsafe { xlib::XFree(vi_ptr as *mut c_void); } let title = "XCB OpenGL"; conn.check_request(conn.send_request_checked(&x::ChangeProperty { mode: x::PropMode::Replace, window: win, property: x::ATOM_WM_NAME, r#type: x::ATOM_STRING, data: title.as_bytes(), }))?; conn.check_request(conn.send_request_checked(&x::ChangeProperty { mode: x::PropMode::Replace, window: win, property: wm_protocols, r#type: x::ATOM_ATOM, data: &[wm_del_window], }))?; conn.check_request(conn.send_request_checked(&x::MapWindow { window: win }))?; unsafe { xlib::XSync(conn.get_raw_dpy(), xlib::False); } let glx_exts = unsafe { CStr::from_ptr(glXQueryExtensionsString(conn.get_raw_dpy(), screen_num)) } .to_str() .unwrap(); if !check_glx_extension(&glx_exts, "GLX_ARB_create_context") { panic!("could not find GLX extension GLX_ARB_create_context"); } // with glx, no need of a current context is needed to load symbols // otherwise we would need to create a temporary legacy GL context // for loading symbols (at least glXCreateContextAttribsARB) let glx_create_context_attribs: GlXCreateContextAttribsARBProc = unsafe { std::mem::transmute(load_gl_func("glXCreateContextAttribsARB")) }; // loading all other symbols unsafe { gl::load_with(|n| load_gl_func(&n)); } if !gl::GenVertexArrays::is_loaded() { panic!("no GL3 support available!"); } // installing an event handler to check if error is generated unsafe { CTX_ERROR_OCCURED = false; } let old_handler = unsafe { xlib::XSetErrorHandler(Some(ctx_error_handler)) }; let context_attribs: [c_int; 5] = [ GLX_CONTEXT_MAJOR_VERSION_ARB as c_int, 3, GLX_CONTEXT_MINOR_VERSION_ARB as c_int, 0, 0, ]; let ctx = unsafe { glx_create_context_attribs( conn.get_raw_dpy(), fbc, ptr::null_mut(), xlib::True, &context_attribs[0] as *const c_int, ) }; conn.flush()?; unsafe { xlib::XSync(conn.get_raw_dpy(), xlib::False); xlib::XSetErrorHandler(std::mem::transmute(old_handler)); } if ctx.is_null() || unsafe { CTX_ERROR_OCCURED } { panic!("error when creating gl-3.0 context"); } if unsafe { glXIsDirect(conn.get_raw_dpy(), ctx) } == 0 { panic!("obtained indirect rendering context") } loop { conn.flush()?; match conn.wait_for_event()? { xcb::Event::X(x::Event::Expose(_)) => unsafe { glXMakeCurrent(conn.get_raw_dpy(), win.resource_id() as xlib::XID, ctx); gl::ClearColor(0.5f32, 0.5f32, 1.0f32, 1.0f32); gl::Clear(gl::COLOR_BUFFER_BIT); gl::Flush(); check_gl_error(); glXSwapBuffers(conn.get_raw_dpy(), win.resource_id() as xlib::XID); glXMakeCurrent(conn.get_raw_dpy(), 0, ptr::null_mut()); }, xcb::Event::X(x::Event::KeyPress(_ev)) => {} xcb::Event::X(x::Event::ClientMessage(ev)) => { if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { if atom == wm_del_window.resource_id() { // window "x" button clicked by user, we gracefully exit break; } } } // Following stuff is not obvious at all. // I've seen this as necessary in the past to handle GL when XCB owns the event queue. // It doesn't seem necessary anymore (in fact DRI2 is not present // on my system, so I won't even hit this code) but I leave it here // in case it is useful to someone. // These are libgl dri2 event that need special handling // see https://bugs.freedesktop.org/show_bug.cgi?id=35945#c4 // and mailing thread starting here: // http://lists.freedesktop.org/archives/xcb/2015-November/010556.html xcb::Event::Dri2(dri2::Event::BufferSwapComplete(ev)) => unsafe { rewire_event(&conn, ev.as_raw()) }, xcb::Event::Dri2(dri2::Event::InvalidateBuffers(ev)) => unsafe { rewire_event(&conn, ev.as_raw()) }, _ => {} } } unsafe { glXDestroyContext(conn.get_raw_dpy(), ctx); } conn.send_request(&x::UnmapWindow { window: win }); conn.send_request(&x::DestroyWindow { window: win }); conn.send_request(&x::FreeColormap { cmap }); conn.flush()?; Ok(()) } unsafe fn rewire_event(conn: &xcb::Connection, raw_ev: *mut xcb_generic_event_t) { let ev_type = ((*raw_ev).response_type & 0x7f) as i32; if let Some(proc) = xlib::XESetWireToEvent(conn.get_raw_dpy(), ev_type, None) { xlib::XESetWireToEvent(conn.get_raw_dpy(), ev_type, Some(proc)); (*raw_ev).sequence = xlib::XLastKnownRequestProcessed(conn.get_raw_dpy()) as u16; let mut dummy: xlib::XEvent = std::mem::zeroed(); proc( conn.get_raw_dpy(), &mut dummy as *mut xlib::XEvent, raw_ev as *mut xlib::xEvent, ); } } xcb-1.2.2/examples/print_setup.rs000064400000000000000000000002131046102023000151360ustar 00000000000000fn main() { let (conn, _) = xcb::Connection::connect(None).unwrap(); let setup = conn.get_setup(); println!("{:#?}", setup); } xcb-1.2.2/examples/randr_crtc_info.rs000064400000000000000000000026171046102023000157300ustar 00000000000000use xcb::{randr, x}; fn main() -> xcb::Result<()> { let (conn, screen_num) = xcb::Connection::connect(None)?; let setup = conn.get_setup(); let screen = setup.roots().nth(screen_num as usize).unwrap(); let window_dummy: x::Window = conn.generate_id(); conn.send_request(&x::CreateWindow { depth: 0, wid: window_dummy, parent: screen.root(), x: 0, y: 0, width: 1, height: 1, border_width: 0, class: x::WindowClass::InputOutput, visual: screen.root_visual(), value_list: &[], }); conn.flush()?; let screen_res = conn.wait_for_reply(conn.send_request(&randr::GetScreenResources { window: window_dummy, }))?; let crtc_cookies = screen_res .crtcs() .iter() .map(|crtc| { conn.send_request(&randr::GetCrtcInfo { crtc: *crtc, config_timestamp: 0, }) }) .collect::>(); for (i, cookie) in crtc_cookies.into_iter().enumerate() { let reply = conn.wait_for_reply(cookie)?; if i != 0 { println!(""); } println!("CRTC[{}] INFO:", i); println!(" x-off\t: {}", reply.x()); println!(" y-off\t: {}", reply.y()); println!(" width\t: {}", reply.width()); println!(" height\t: {}", reply.height()); } Ok(()) } xcb-1.2.2/examples/randr_crtc_listen.rs000064400000000000000000000031151046102023000162650ustar 00000000000000use xcb::randr; fn main() -> xcb::Result<()> { let (conn, screen_num) = xcb::Connection::connect_with_extensions(None, &[xcb::Extension::RandR], &[]).unwrap(); let screen = conn.get_setup().roots().nth(screen_num as usize).unwrap(); conn.check_request(conn.send_request_checked(&randr::SelectInput { window: screen.root(), enable: randr::NotifyMask::CRTC_CHANGE, }))?; loop { conn.flush()?; let event = match conn.wait_for_event() { Err(xcb::Error::Connection(err)) => { panic!("unexpected I/O error: {}", err); } Err(xcb::Error::Protocol(err)) => { panic!("unexpected protocol error: {:#?}", err); } Ok(event) => event, }; match event { xcb::Event::RandR(randr::Event::Notify(ev)) => match ev.u() { randr::NotifyData::Cc(cc) => { println!( "received CRTC_CHANGE event:\n\ \ttimestamp: {}, window: {:?}, crtc: {:?}, mode: {:?}, rotation: {:?}\n\ \tx: {}, y: {}, width: {}, height: {}", cc.timestamp(), cc.window(), cc.crtc(), cc.mode(), cc.rotation(), cc.x(), cc.y(), cc.width(), cc.height() ); } _ => {} }, _ => {} } } } xcb-1.2.2/examples/randr_screen_info.rs000064400000000000000000000020241046102023000162440ustar 00000000000000use xcb::{randr, x}; fn main() -> xcb::Result<()> { let (conn, screen_num) = xcb::Connection::connect(None)?; let setup = conn.get_setup(); let screen = setup.roots().nth(screen_num as usize).unwrap(); let window_dummy: x::Window = conn.generate_id(); conn.send_request(&x::CreateWindow { depth: 0, wid: window_dummy, parent: screen.root(), x: 0, y: 0, width: 1, height: 1, border_width: 0, class: x::WindowClass::InputOutput, visual: screen.root_visual(), value_list: &[], }); conn.flush()?; let reply = conn.wait_for_reply(conn.send_request(&randr::GetScreenInfo { window: window_dummy, }))?; for (i, size) in reply.sizes().iter().enumerate() { if i != 0 { println!(""); } println!("size of screen {}:", i + 1); println!( " {} x {} ({}mm x {}mm)", size.width, size.height, size.mwidth, size.mheight, ); } Ok(()) } xcb-1.2.2/examples/randr_screen_modes.rs000064400000000000000000000030361046102023000164240ustar 00000000000000use xcb::{randr, x}; // per https://gitlab.freedesktop.org/xorg/app/xrandr/-/blob/master/xrandr.c#L576 fn mode_refresh(mode_info: &randr::ModeInfo) -> f64 { let vtotal = { let mut val = mode_info.vtotal; if mode_info.mode_flags.contains(randr::ModeFlag::DOUBLE_SCAN) { val *= 2; } if mode_info.mode_flags.contains(randr::ModeFlag::INTERLACE) { val /= 2; } val }; if vtotal != 0 && mode_info.htotal != 0 { (mode_info.dot_clock as f64) / (vtotal as f64 * mode_info.htotal as f64) } else { 0.0 } } fn main() -> xcb::Result<()> { let (conn, screen_num) = xcb::Connection::connect(None)?; let setup = conn.get_setup(); let screen = setup.roots().nth(screen_num as usize).unwrap(); let window_dummy: x::Window = conn.generate_id(); conn.send_request(&x::CreateWindow { depth: 0, wid: window_dummy, parent: screen.root(), x: 0, y: 0, width: 1, height: 1, border_width: 0, class: x::WindowClass::InputOutput, visual: screen.root_visual(), value_list: &[], }); conn.flush()?; let reply = conn.wait_for_reply(conn.send_request(&randr::GetScreenResources { window: window_dummy, }))?; for (i, mode) in reply.modes().iter().enumerate() { println!("mode {}", i + 1); println!("\tresolution = {}x{}", mode.width, mode.height); println!("\trefresh rate = {:.1}Hz", mode_refresh(mode)); } Ok(()) } xcb-1.2.2/examples/screen_info.rs000064400000000000000000000010351046102023000150570ustar 00000000000000fn main() -> xcb::Result<()> { let (conn, screen_num) = xcb::Connection::connect(None)?; let setup = conn.get_setup(); let screen = setup.roots().nth(screen_num as usize).unwrap(); println!(""); println!("Informations of screen {}:", screen_num); println!(" width..........: {}", screen.width_in_pixels()); println!(" height.........: {}", screen.height_in_pixels()); println!(" white pixel....: {:x}", screen.white_pixel()); println!(" black pixel....: {:x}", screen.black_pixel()); Ok(()) } xcb-1.2.2/examples/screenshot.rs000064400000000000000000000026211046102023000147440ustar 00000000000000extern crate xcb; use std::{fs::File, io::BufWriter}; use xcb::x; fn main() { let (conn, screen_num) = xcb::Connection::connect(None).unwrap(); let setup = conn.get_setup(); let screen = setup.roots().nth(screen_num as usize).unwrap(); let width = screen.width_in_pixels(); let height = screen.height_in_pixels(); let cookie = conn.send_request(&x::GetImage { format: x::ImageFormat::ZPixmap, drawable: x::Drawable::Window(screen.root()), x: 0, y: 0, width, height, plane_mask: u32::MAX, }); let file = File::create("screenshot.png").unwrap(); let writer = BufWriter::new(file); let mut encoder = png::Encoder::new(writer, width as _, height as _); encoder.set_color(png::ColorType::Rgb); encoder.set_depth(png::BitDepth::Eight); let mut writer = encoder .write_header() .expect("Failed to write image header"); let reply = conn.wait_for_reply(cookie).unwrap(); let src = reply.data(); let mut data = vec![0; width as usize * height as usize * 3]; for (src, dest) in src.chunks(4).zip(data.chunks_mut(3)) { // Captured image stores orders pixels as BGR, so we need to // reorder them. dest[0] = src[2]; dest[1] = src[1]; dest[2] = src[0]; } writer .write_image_data(&data) .expect("Failed to write image data"); } xcb-1.2.2/examples/threaded_window.rs000064400000000000000000000064741046102023000157500ustar 00000000000000extern crate xcb; use std::iter::Iterator; use std::sync::Arc; use std::{thread, time}; use xcb::x; fn main() -> xcb::Result<()> { let (conn, screen_num) = { let (conn, screen_num) = xcb::Connection::connect(None)?; (Arc::new(conn), screen_num) }; let setup = conn.get_setup(); let screen = setup.roots().nth(screen_num as usize).unwrap(); let window = conn.generate_id(); let values = [ x::Cw::BackPixel(screen.white_pixel()), x::Cw::EventMask( x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS | x::EventMask::STRUCTURE_NOTIFY | x::EventMask::PROPERTY_CHANGE, ), ]; let create_window_cookie = conn.send_request_checked(&x::CreateWindow { depth: x::COPY_FROM_PARENT as u8, wid: window, parent: screen.root(), x: 0, y: 0, width: 320, height: 240, border_width: 10, class: x::WindowClass::InputOutput, visual: screen.root_visual(), value_list: &values, }); conn.check_request(create_window_cookie)?; let map_window_cookie = conn.send_request_checked(&x::MapWindow { window }); conn.check_request(map_window_cookie)?; { let conn = conn.clone(); thread::spawn(move || { let mut blink = false; loop { let title = if blink { "Basic Threaded Window ;-)" } else { "Basic Threaded Window :-)" }; let cookie = conn.send_request_checked(&x::ChangeProperty { mode: x::PropMode::Replace, window, property: x::ATOM_WM_NAME, r#type: x::ATOM_STRING, data: title.as_bytes(), }); if conn.has_error().is_err() || conn.check_request(cookie).is_err() { break; } blink = !blink; thread::sleep(time::Duration::from_millis(500)); } }); } loop { let event = conn.wait_for_event(); match event { Ok(xcb::Event::X(x::Event::PropertyNotify(ev))) => { if ev.atom() == x::ATOM_WM_NAME { // retrieving title let cookie = conn.send_request(&x::GetProperty { delete: false, window, property: x::ATOM_WM_NAME, r#type: x::ATOM_STRING, long_offset: 0, long_length: 1024, }); let title = conn.wait_for_reply(cookie)?; println!( "title changed to \"{}\"", std::str::from_utf8(title.value()).unwrap() ); } } Ok(xcb::Event::X(x::Event::KeyPress(ev))) => { println!("Key '{}' pressed", ev.detail()); if ev.detail() == 0x18 { // Q (on qwerty) break; } } Ok(_) => { continue; } Err(_) => { break; } } } Ok(()) } xcb-1.2.2/examples/xinput_stylus_events.rs000064400000000000000000000105441046102023000171300ustar 00000000000000use std::env; use xcb::{x, xinput}; #[derive(Debug)] struct StylusDevice { dev: xinput::Device, name: String, } fn main() -> xcb::Result<()> { // if none, will list devices and exit let find_device = { let mut args = env::args(); if args.len() <= 1 { eprintln!(r#"warning: no argument, will default to find a "stylus" device"#); Some("stylus".to_string()) } else { let arg = args.nth(1).unwrap(); if arg == "--list" || arg == "-l" { None } else { Some(arg) } } }; let (conn, screen_num) = xcb::Connection::connect_with_extensions(None, &[xcb::Extension::Input], &[]).unwrap(); // Check if XI2 is supported conn.wait_for_reply(conn.send_request(&xinput::XiQueryVersion { major_version: 2, minor_version: 0, })) .expect("XI2 not supported"); let cookie = conn.send_request(&xinput::ListInputDevices {}); let device_list = conn.wait_for_reply(cookie)?; println!("{:#?}", device_list); if find_device.is_none() { println!("{:#?}", device_list); return Ok(()); } let find_device = find_device.unwrap(); let device = { let mut device: Option = None; for (i, dev) in device_list.devices().iter().enumerate() { let name = device_list.names().nth(i).unwrap().name().to_utf8(); if name.contains(&find_device) { device = Some(StylusDevice { dev: xinput::Device::from_id(dev.device_id() as _), name: name.to_string(), }); break; } } device.expect("could not find a stylus device") }; println!("found device \"{}\" ({:?})", device.name, device.dev); let cookie = conn.send_request(&xinput::OpenDevice { device_id: device.dev.id() as u8, }); conn.wait_for_reply(cookie)?; let setup = conn.get_setup(); let screen = setup.roots().nth(screen_num as usize).unwrap(); let window: x::Window = conn.generate_id(); conn.send_request(&x::CreateWindow { depth: x::COPY_FROM_PARENT as u8, wid: window, parent: screen.root(), x: 0, y: 0, width: 500, height: 500, border_width: 10, class: x::WindowClass::InputOutput, visual: screen.root_visual(), value_list: &[ x::Cw::BackPixel(screen.white_pixel()), x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), ], }); conn.send_request(&x::MapWindow { window }); let title = "Stylus Window"; conn.send_request(&x::ChangeProperty { mode: x::PropMode::Replace, window, property: x::ATOM_WM_NAME, r#type: x::ATOM_STRING, data: title.as_bytes(), }); let info = conn.wait_for_reply(conn.send_request(&xinput::XiQueryDevice { device: device.dev })); println!("{:#?}", info); conn.send_request(&xinput::XiSelectEvents { window, masks: &[xinput::EventMaskBuf::new( device.dev, &[xinput::XiEventMask::MOTION | xinput::XiEventMask::BUTTON_PRESS | xinput::XiEventMask::BUTTON_RELEASE], )], }); conn.flush()?; loop { let ev = conn.wait_for_event()?; match ev { xcb::Event::Input(xinput::Event::Motion(ev)) => { // TODO, query valuator infos to ensure on which axis is the pressure // This works for me with a Wacom One, but could be different with another device/config println!("received stylus motion event"); println!(" event_x = {}", fp1616_to_f64(ev.event_x())); println!(" event_y = {}", fp1616_to_f64(ev.event_y())); println!(" pressure = {}", ev.axisvalues()[2].integral); println!(); } xcb::Event::Input(xinput::Event::ButtonPress(_ev)) => { println!("received stylus press"); } xcb::Event::Input(xinput::Event::ButtonRelease(_ev)) => { println!("received stylus release"); } _ => {} } } //Ok(()) } fn fp1616_to_f64(val: xinput::Fp1616) -> f64 { (val as f64) / (u16::MAX as f64) } xcb-1.2.2/examples/xkb_init.rs000064400000000000000000000030721046102023000143770ustar 00000000000000use xcb::xkb; fn main() -> xcb::Result<()> { let (conn, _) = xcb::Connection::connect_with_extensions(None, &[xcb::Extension::Xkb], &[])?; // we need at least xkb-1.0 to be available on client // machine { let xkb_ver = conn.wait_for_reply(conn.send_request(&xkb::UseExtension { wanted_major: 1, wanted_minor: 0, }))?; assert!(xkb_ver.supported(), "xkb-1.0 support is required"); } // we now select what events we want to receive // such as map change, keyboard hotplug ... // note that key strokes are given directly by // the x::Event::KeyPress, not by xkb { let events = xkb::EventType::NEW_KEYBOARD_NOTIFY | xkb::EventType::MAP_NOTIFY | xkb::EventType::STATE_NOTIFY; let map_parts = xkb::MapPart::KEY_TYPES | xkb::MapPart::KEY_SYMS | xkb::MapPart::MODIFIER_MAP | xkb::MapPart::EXPLICIT_COMPONENTS | xkb::MapPart::KEY_ACTIONS | xkb::MapPart::KEY_BEHAVIORS | xkb::MapPart::VIRTUAL_MODS | xkb::MapPart::VIRTUAL_MOD_MAP; let cookie = conn.send_request_checked(&xkb::SelectEvents { device_spec: xkb::Id::UseCoreKbd as xkb::DeviceSpec, affect_which: events, clear: xkb::EventType::empty(), select_all: events, affect_map: map_parts, map: map_parts, details: &[], }); conn.check_request(cookie)?; } // proceed with create_window and event loop... Ok(()) } xcb-1.2.2/gen.sh000075500000000000000000000011501046102023000115070ustar 00000000000000#!/bin/bash # This is a script that runs the code generation and export a copy of it under gen/$1. # This is NOT the main code generation script, but a tool to help looking at the generated code # (which is otherwise lost somewhere under target/) and performing diffs with previous versions. if [ -z "$1" ] then echo -e "Error: a subfolder in gen/ must be supplied as argument" exit 1 fi # trigger rebuild touch build/cg/mod.rs # rebuild and export code RXCB_EXPORT=gen/$1 RUST_BACKTRACE=1 cargo build --all-features # format exported code for file in gen/$1/*.rs do rustfmt $file --edition=2018 done xcb-1.2.2/src/base.rs000064400000000000000000001710251046102023000124570ustar 00000000000000use crate::error::{self, ProtocolError}; use crate::event::{self, Event}; use crate::ext::{Extension, ExtensionData}; #[cfg(feature = "present")] use crate::present; use crate::x::{Atom, Keysym, Setup, Timestamp}; #[cfg(feature = "xinput")] use crate::xinput; use crate::{cache_extensions_data, ffi::*}; #[cfg(feature = "xlib_xcb")] use x11::xlib; use bitflags::bitflags; use libc::{c_char, c_int}; use std::cell::RefCell; use std::ffi::{CStr, CString}; use std::fmt::{self, Display, Formatter}; use std::marker::{self, PhantomData}; use std::mem; use std::os::unix::prelude::{AsRawFd, RawFd}; use std::ptr; use std::result; use std::slice; /// A X resource trait pub trait Xid { /// Build a null X resource fn none() -> Self; /// Get the underlying id of the resource fn resource_id(&self) -> u32; /// Check whether this resource is null or not fn is_none(&self) -> bool { self.resource_id() == 0 } } /// Trait for X resources that can be created directly from `Connection::generate_id` /// /// The resources that cannot be created that way are the Xid unions, which are created /// from their underlying resource. pub trait XidNew: Xid { /// Build a new X resource /// /// # Safety /// res_id must be obtained from `xcb_generate_id`. `0` is also a valid value to create a null resource. /// Users should not use this function directly but rather use /// `Connection::generate_id` unsafe fn new(res_id: u32) -> Self; } /// Trait for types that own a C allocated pointer and are represented by the data pointed to. pub trait Raw: Sized { /// Build `Self` from a raw pointer /// /// # Safety /// `raw` must be a valid pointer to the representation of Self, and be allocated with `libc::malloc` unsafe fn from_raw(raw: *mut T) -> Self; /// Convert self into a raw pointer /// /// Returned value should be freed with `libc::free` or sent back to `from_raw` to avoid memory leak. fn into_raw(self) -> *mut T { let raw = self.as_raw(); mem::forget(self); raw } /// Obtain the raw pointer representation fn as_raw(&self) -> *mut T; } /// Trait for base events (aka. non GE_GENERIC events) pub trait BaseEvent: Raw { /// The extension associated to this event, or `None` for the main protocol const EXTENSION: Option; /// The number associated to this event const NUMBER: u32; } /// A trait for GE_GENERIC events /// /// A GE_GENERIC event is an extension event that does not follow /// the regular `response_type` offset. /// This system was introduced because the protocol eventually run /// out of event numbers. /// /// This should be completely transparent to the user, as [Event] is /// resolving all types of events together. pub trait GeEvent: Raw { /// The extension associated to this event const EXTENSION: Extension; /// The number associated to this event const NUMBER: u32; } /// A trait to designate base protocol errors. /// /// The base errors follow the usual resolution idiom of `error_code` offset. /// /// This should be completely transparent to the user, as [ProtocolError] is /// resolving all types of errors together. pub trait BaseError: Raw { /// The extension associated to this error, or `None` for the main protocol const EXTENSION: Option; /// The number associated to this error const NUMBER: u32; } /// Trait for the resolution of raw wire event to a unified event enum. /// /// `Self` is normally an enum of several event subtypes. /// See [crate::x::Event] and [crate::Event] pub(crate) trait ResolveWireEvent: Sized { /// Resolve a pointer to `xcb_generic_event_t` to `Self`, inferring the correct subtype /// using `response_type` field and `first_event` /// /// # Panics /// Panics if the event subtype cannot be resolved to `Self`. That is, /// `response_type` field must be checked beforehand to be in range with /// `first_event`. /// /// # Safety /// `event` must be a valid, non-null event returned by `xcb_wait_for_event` /// or similar function unsafe fn resolve_wire_event(first_event: u8, event: *mut xcb_generic_event_t) -> Option; } /// Trait for the resolution of raw wire GE_GENERIC event to a unified event enum. /// /// `Self` is normally an enum of several event subtypes. /// See [crate::xinput::Event] and [crate::Event] pub(crate) trait ResolveWireGeEvent: Sized { /// Resolve a pointer to `xcb_ge_generic_event_t` to `Self`, inferring the correct subtype /// using `event_type` field. /// /// # Panics /// Panics if the event subtype cannot be resolved for `Self`. That is, `event_type` /// must be checked beforehand to be in range. /// /// # Safety /// `event` must be a valid, non-null event returned by `xcb_wait_for_event` /// or similar function unsafe fn resolve_wire_ge_event(event: *mut xcb_ge_generic_event_t) -> Self; } /// Trait for the resolution of raw wire error to a unified error enum. /// /// `Self` is normally an enum of several event subtypes. /// See [crate::x::Error] and [crate::ProtocolError] pub(crate) trait ResolveWireError { /// Convert a pointer to `xcb_generic_error_t` to `Self`, inferring the correct subtype /// using `response_type` field and `first_error`. /// /// # Panics /// Panics if the error subtype cannot be resolved for `self`. That is, /// `response_type` field must be checked beforehand to be in range with /// `first_error`. /// /// # Safety /// `err` must be a valid, non-null error obtained by `xcb_wait_for_reply` /// or similar function unsafe fn resolve_wire_error(first_error: u8, error: *mut xcb_generic_error_t) -> Self; } /// Trait for types that can serialize themselves to the X wire. /// /// This trait is used internally for requests serialization. pub(crate) trait WiredOut { /// Compute the length of wired serialized data of self fn wire_len(&self) -> usize; /// Serialize `self` over the X wire and returns how many bytes were written. /// /// `wire_buf` must be larger or as long as the value returned by `wire_len`. /// `serialize` MUST write the data in its entirety at once, at the begining of `wire_buf`. /// That is, it returns the same value as `wire_len`. /// The interest in returning the value is that it is easy to compute in `serialize` and allow /// to easily chain serialization of fields in a struct. /// /// # Panics /// Panics if `wire_buf` is too small to contain the serialized representation of `self`. fn serialize(&self, wire_buf: &mut [u8]) -> usize; } /// Trait for types that can unserialize themselves from the X wire. pub(crate) trait WiredIn { /// type of external context necessary to figure out the representation of the data type Params: Copy; /// Compute the length of serialized data of an instance starting by `ptr`. /// /// # Safety /// This function is highly unsafe as the pointer must point to data that is a valid /// wired representation of `Self`. Failure to respect this will lead to dereferencing invalid memory. unsafe fn compute_wire_len(ptr: *const u8, params: Self::Params) -> usize; /// Unserialize an instance of `Self` from the X wire /// /// `offset` value is increased by the number of bytes corresponding to the representation of `Self`. /// This allows for efficient chaining of unserialization as the data offset is either known at /// compile time, or has to be computed anyway. /// /// # Safety /// This function is highly unsafe as the pointer must point to data that is a valid /// wired representation of `Self`. Failure to respect this will lead to dereferencing invalid memory. unsafe fn unserialize(ptr: *const u8, params: Self::Params, offset: &mut usize) -> Self; } macro_rules! impl_wired_simple { ($typ:ty) => { impl WiredOut for $typ { fn wire_len(&self) -> usize { mem::size_of::() } fn serialize(&self, wire_buf: &mut [u8]) -> usize { debug_assert!(wire_buf.len() >= mem::size_of::()); let buf_size = self.wire_len(); let src = unsafe { slice::from_raw_parts(self as *const Self as *const u8, buf_size) }; wire_buf[..buf_size].copy_from_slice(src); buf_size } } impl WiredIn for $typ { type Params = (); unsafe fn compute_wire_len(_ptr: *const u8, _params: Self::Params) -> usize { mem::size_of::() } unsafe fn unserialize( ptr: *const u8, _params: Self::Params, offset: &mut usize, ) -> Self { *offset += mem::size_of::(); *(ptr as *const Self) } } }; } impl_wired_simple!(u8); impl_wired_simple!(u16); impl_wired_simple!(u32); impl_wired_simple!(u64); impl_wired_simple!(i8); impl_wired_simple!(i16); impl_wired_simple!(i32); impl_wired_simple!(f32); impl WiredOut for T { fn wire_len(&self) -> usize { 4 } fn serialize(&self, wire_buf: &mut [u8]) -> usize { debug_assert!(wire_buf.len() >= 4); let buf_size = self.wire_len(); let resource_id = self.resource_id(); let src = unsafe { slice::from_raw_parts(&resource_id as *const u32 as *const u8, buf_size) }; wire_buf[..buf_size].copy_from_slice(src); buf_size } } impl WiredIn for T { type Params = (); unsafe fn compute_wire_len(_ptr: *const u8, _params: Self::Params) -> usize { 4 } unsafe fn unserialize(ptr: *const u8, _params: Self::Params, offset: &mut usize) -> Self { *offset += 4; let xid = *(ptr as *const u32); T::new(xid) } } /// Trait for request replies pub trait Reply { /// Build the reply struct from a raw pointer. /// /// # Safety /// `raw` must be a pointer to a valid wire representation of `Self`, allocated with [`libc::malloc`]. unsafe fn from_raw(raw: *const u8) -> Self; /// Consume the reply struct into a raw pointer. /// /// # Safety /// The returned pointer must be freed with [`libc::free`] to avoid any memory leak, or be used /// to build another reply. unsafe fn into_raw(self) -> *const u8; } /// General trait for cookies returned by requests. pub trait Cookie { /// # Safety /// `seq` must be a valid cookie for a given `Request` or `Reply`. unsafe fn from_sequence(seq: u64) -> Self; /// The raw sequence number associated with the cookie. fn sequence(&self) -> u64; } /// A marker trait for a cookie that allows synchronized error checking. /// /// # Safety /// Cookies not implementing this trait acknowledge that the error is sent to the event loop /// /// See also [Connection::send_request], [Connection::send_request_checked] and [Connection::check_request] pub unsafe trait CookieChecked: Cookie {} /// A trait for checked cookies of requests that send a reply. /// /// # Safety /// Cookies implementing this trait acknowledge that their error is checked when the reply is fetched from the server. /// This is the default cookie type for requests with reply. /// /// See also [Connection::send_request], [Connection::wait_for_reply] pub unsafe trait CookieWithReplyChecked: CookieChecked { /// The reply type associated with the cookie type Reply: Reply; } /// A trait for unchecked cookies of requests that send a reply. /// /// # Safety /// Cookies implementing this trait acknowledge that their error is not checked when the reply is fetched from the server /// but in the event loop. /// /// See also [Connection::send_request_unchecked], [Connection::wait_for_event] pub unsafe trait CookieWithReplyUnchecked: Cookie { /// The reply type associated with the cookie type Reply: Reply; } /// The default cookie type returned by void-requests. /// /// See [Connection::send_request] #[derive(Debug)] pub struct VoidCookie { seq: u64, } impl Cookie for VoidCookie { unsafe fn from_sequence(seq: u64) -> Self { VoidCookie { seq } } fn sequence(&self) -> u64 { self.seq } } /// The checked cookie type returned by void-requests. /// /// See [Connection::send_request_checked] #[derive(Debug)] pub struct VoidCookieChecked { seq: u64, } impl Cookie for VoidCookieChecked { unsafe fn from_sequence(seq: u64) -> Self { VoidCookieChecked { seq } } fn sequence(&self) -> u64 { self.seq } } unsafe impl CookieChecked for VoidCookieChecked {} /// Trait implemented by all requests to send the serialized data over the wire. /// /// # Safety /// Types implementing this trait acknowledge that the returned value of `raw_request` correspond /// to a cookie for `Self` request and is checked or unchecked depending on the `checked` flag value. pub unsafe trait RawRequest { /// Actual implementation of the request sending /// /// Send the request over the `conn` wire and return a cookie sequence fitting with the `checked` flag /// of `Self` fn raw_request(&self, conn: &Connection, checked: bool) -> u64; } /// Trait implemented by requests types. /// /// See [crate::x::CreateWindow] as an example. pub trait Request: RawRequest { /// The default cookie associated to this request. type Cookie: Cookie; /// `false` if the request returns a reply, `true` otherwise. const IS_VOID: bool; } /// Marker trait for requests that do not return a reply. /// /// These trait is implicitely associated with [`VoidCookie`] and [`VoidCookieChecked`]. pub trait RequestWithoutReply: Request {} /// Trait for requests that return a reply. pub trait RequestWithReply: Request { /// Reply associated with the request type Reply: Reply; /// Default cookie type for the request, as returned by [Connection::send_request]. type Cookie: CookieWithReplyChecked; /// Unchecked cookie type for the request, as returned by [Connection::send_request_unchecked]. type CookieUnchecked: CookieWithReplyUnchecked; } /// Determines whether Xlib or XCB owns the event queue of [`Connection`]. /// /// This item is behind the `xlib_xcb` cargo feature. /// /// See [`Connection::set_event_queue_owner`]. #[cfg(feature = "xlib_xcb")] #[derive(Debug)] pub enum EventQueueOwner { /// XCB owns the event queue Xcb, /// Xlib owns the event queue Xlib, } /// Container for authentication information to connect to the X server /// /// See [Connection::connect_to_display_with_auth_info] and [Connection::connect_to_fd]. #[derive(Copy, Clone, Debug)] pub struct AuthInfo<'a> { /// String containing the authentication protocol name, /// such as "MIT-MAGIC-COOKIE-1" or "XDM-AUTHORIZATION-1". pub name: &'a str, /// data interpreted in a protocol specific manner pub data: &'a str, } /// Display info returned by [`parse_display`] #[derive(Debug)] pub struct DisplayInfo { /// The hostname host: String, /// The display number display: i32, /// The screen number screen: i32, } /// Parses a display string in the form documented by [X (7x)](https://linux.die.net/man/7/x). /// /// Returns `Some(DisplayInfo)` on success and `None` otherwise. /// Has no side effects on failure. /// /// If `name` empty, it uses the environment variable `DISPLAY`. /// /// If `name` does not contain a screen number, `DisplayInfo::screen` is set to `0`. pub fn parse_display(name: &str) -> Option { unsafe { let name = CString::new(name).unwrap(); let mut hostp: *mut c_char = ptr::null_mut(); let mut display = 0i32; let mut screen = 0i32; let success = xcb_parse_display( name.as_ptr(), &mut hostp as *mut _, &mut display as *mut _, &mut screen as *mut _, ); if success != 0 { let host = CStr::from_ptr(hostp as *const _) .to_str() .unwrap() .to_string(); libc::free(hostp as *mut _); Some(DisplayInfo { host, display, screen, }) } else { None } } } /// A struct that serve as an identifier for internal special queue in XCB /// /// See [Connection::register_for_special_xge]. #[cfg(any(feature = "xinput", feature = "present"))] #[derive(Debug)] pub struct SpecialEventId { raw: *mut xcb_special_event_t, stamp: Timestamp, } #[cfg(any(feature = "xinput", feature = "present"))] impl SpecialEventId { /// The X timestamp associated with this special event Id pub fn stamp(&self) -> Timestamp { self.stamp } } /// Error type that is returned by `Connection::has_error`. #[derive(Debug)] pub enum ConnError { /// xcb connection errors because of socket, pipe and other stream errors. Connection, /// xcb connection shutdown because of extension not supported ClosedExtNotSupported, /// malloc(), calloc() and realloc() error upon failure, for eg ENOMEM ClosedMemInsufficient, /// Connection closed, exceeding request length that server accepts. ClosedReqLenExceed, /// Connection closed, error during parsing display string. ClosedParseErr, /// Connection closed because the server does not have a screen /// matching the display. ClosedInvalidScreen, /// Connection closed because some file descriptor passing operation failed. ClosedFdPassingFailed, /// XOpenDisplay returned NULL #[cfg(feature = "xlib_xcb")] XOpenDisplay, } impl ConnError { fn to_str(&self) -> &str { match *self { ConnError::Connection => "Connection error, possible I/O error", ConnError::ClosedExtNotSupported => "Connection closed, X extension not supported", ConnError::ClosedMemInsufficient => "Connection closed, insufficient memory", ConnError::ClosedReqLenExceed => { "Connection closed, exceeded request length that server accepts." } ConnError::ClosedParseErr => "Connection closed, error during parsing display string", ConnError::ClosedInvalidScreen => { "Connection closed, the server does not have a screen matching the display" } ConnError::ClosedFdPassingFailed => { "Connection closed, some file descriptor passing operation failed" } #[cfg(feature = "xlib_xcb")] ConnError::XOpenDisplay => { "XOpenDisplay failed to open a display. Check the $DISPLAY env var" } } } } impl Display for ConnError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.to_str().fmt(f) } } impl std::error::Error for ConnError { fn description(&self) -> &str { self.to_str() } } /// The result type associated with [ConnError]. pub type ConnResult = result::Result; /// The result type associated with [ProtocolError]. pub type ProtocolResult = result::Result; /// The general error type for Rust-XCB. #[derive(Debug)] pub enum Error { /// I/O error issued from the connection. Connection(ConnError), /// A protocol related error issued by the X server. Protocol(ProtocolError), } impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Error::Connection(_) => f.write_str("xcb connection error"), Error::Protocol(_) => f.write_str("xcb protocol error"), } } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Error::Connection(err) => Some(err), Error::Protocol(err) => Some(err), } } } impl From for Error { fn from(err: ConnError) -> Error { Error::Connection(err) } } impl From for Error { fn from(err: ProtocolError) -> Error { Error::Protocol(err) } } /// The general result type for Rust-XCB. pub type Result = result::Result; /// `Connection` is the central object of XCB. /// /// It handles all communications with the X server. /// It dispatches the requests, receives the replies, poll/wait the events. /// It also resolves the errors and events from X server. /// /// `Connection` is thread safe. /// /// It internally wraps an `xcb_connection_t` object and /// will call `xcb_disconnect` when the `Connection` goes out of scope. pub struct Connection { c: *mut xcb_connection_t, #[cfg(feature = "xlib_xcb")] dpy: *mut xlib::Display, ext_data: Vec, // Following field is used to handle the // rare (if existing) cases of multiple connections // per application. // Only the first established connections is used // to print the name of atoms during Debug #[cfg(feature = "debug_atom_names")] dbg_atom_names: bool, } unsafe impl Send for Connection {} unsafe impl Sync for Connection {} impl Connection { /// Connects to the X server. /// /// Connects to the X server specified by `display_name.` If /// `display_name` is `None,` uses the value of the `DISPLAY` environment /// variable. /// /// If no screen is preferred, the second member of the tuple is set to 0. /// /// # Example /// ```no_run /// fn main() -> xcb::Result<()> { /// let (conn, screen) = xcb::Connection::connect(None)?; /// Ok(()) /// } /// ``` pub fn connect(display_name: Option<&str>) -> ConnResult<(Connection, i32)> { Self::connect_with_extensions(display_name, &[], &[]) } /// Connects to the X server and cache extension data. /// /// Connects to the X server specified by `display_name.` If /// `display_name` is `None,` uses the value of the `DISPLAY` environment /// variable. /// /// Extension data specified by `mandatory` and `optional` is cached to allow /// the resolution of events and errors in these extensions. /// /// If no screen is preferred, the second member of the tuple is set to 0. /// /// # Panics /// Panics if one of the mandatory extension is not present. /// /// # Example /// ```no_run /// fn main() -> xcb::Result<()> { /// let (conn, screen) = xcb::Connection::connect_with_extensions( /// None, &[xcb::Extension::Input, xcb::Extension::Xkb], &[] /// )?; /// Ok(()) /// } /// ``` pub fn connect_with_extensions( display_name: Option<&str>, mandatory: &[Extension], optional: &[Extension], ) -> ConnResult<(Connection, i32)> { let mut screen_num: c_int = 0; let displayname = display_name.map(|s| CString::new(s).unwrap()); unsafe { let conn = if let Some(display) = displayname { xcb_connect(display.as_ptr(), &mut screen_num) } else { xcb_connect(ptr::null(), &mut screen_num) }; check_connection_error(conn)?; let conn = Self::from_raw_conn_and_extensions(conn, mandatory, optional); conn.has_error().map(|_| (conn, screen_num as i32)) } } /// Open a new connection with Xlib. /// /// The event queue owner defaults to Xlib. /// One would need to open an XCB connection with Xlib in order to use /// OpenGL. /// /// This function is behind the `xlib_xcb` cargo feature. #[cfg(feature = "xlib_xcb")] pub fn connect_with_xlib_display() -> ConnResult<(Connection, i32)> { unsafe { let dpy = xlib::XOpenDisplay(ptr::null()); if dpy.is_null() { return Err(ConnError::XOpenDisplay); } check_connection_error(XGetXCBConnection(dpy))?; let conn = Self::from_xlib_display(dpy); conn.has_error() .map(|_| (conn, xlib::XDefaultScreen(dpy) as i32)) } } /// Open a new connection with Xlib and cache the provided extensions data. /// /// Data of extensions specified by `mandatory` and `optional` is cached to allow /// the resolution of events and errors in these extensions. /// /// The event queue owner defaults to Xlib. /// One would need to open an XCB connection with Xlib in order to use /// OpenGL. /// /// This function is behind the `xlib_xcb` cargo feature. /// /// # Panics /// Panics if one of the mandatory extension is not present. #[cfg(feature = "xlib_xcb")] pub fn connect_with_xlib_display_and_extensions( mandatory: &[Extension], optional: &[Extension], ) -> ConnResult<(Connection, i32)> { unsafe { let dpy = xlib::XOpenDisplay(ptr::null()); if dpy.is_null() { return Err(ConnError::XOpenDisplay); } check_connection_error(XGetXCBConnection(dpy))?; let conn = Self::from_xlib_display_and_extensions(dpy, mandatory, optional); conn.has_error() .map(|_| (conn, xlib::XDefaultScreen(dpy) as i32)) } } /// Connects to the X server with an open socket file descriptor and optional authentification info. /// /// Connects to an X server, given the open socket fd and the /// `auth_info`. The file descriptor `fd` is bidirectionally connected to an X server. /// If the connection should be unauthenticated, `auth_info` must be `None`. pub fn connect_to_fd(fd: RawFd, auth_info: Option>) -> ConnResult { Self::connect_to_fd_with_extensions(fd, auth_info, &[], &[]) } /// Connects to the X server with an open socket file descriptor and optional authentification info. /// /// Extension data specified by `mandatory` and `optional` is cached to allow /// the resolution of events and errors in these extensions. /// /// Connects to an X server, given the open socket fd and the /// `auth_info`. The file descriptor `fd` is bidirectionally connected to an X server. /// If the connection should be unauthenticated, `auth_info` must be `None`. /// /// # Panics /// Panics if one of the mandatory extension is not present. pub fn connect_to_fd_with_extensions( fd: RawFd, auth_info: Option>, mandatory: &[Extension], optional: &[Extension], ) -> ConnResult { let mut auth_info = auth_info.map(|auth_info| { let auth_name = CString::new(auth_info.name).unwrap(); let auth_data = CString::new(auth_info.data).unwrap(); let auth_info = xcb_auth_info_t { namelen: auth_name.as_bytes().len() as _, name: auth_name.as_ptr() as *mut _, datalen: auth_data.as_bytes().len() as _, data: auth_data.as_ptr() as *mut _, }; // return the strings too otherwise they would drop (auth_info, auth_name, auth_data) }); let ai_ptr = if let Some(auth_info) = auth_info.as_mut() { &mut auth_info.0 as *mut _ } else { ptr::null_mut() }; let conn = unsafe { let conn = xcb_connect_to_fd(fd, ai_ptr); check_connection_error(conn)?; Self::from_raw_conn_and_extensions(conn, mandatory, optional) }; conn.has_error().map(|_| conn) } /// Connects to the X server, using an authorization information. /// /// Connects to the X server specified by `display_name`, using the /// authorization `auth_info`. If a particular screen on that server, it is /// returned in the second tuple member, which is otherwise set to `0`. pub fn connect_to_display_with_auth_info( display_name: Option<&str>, auth_info: AuthInfo<'_>, ) -> ConnResult<(Connection, i32)> { Self::connect_to_display_with_auth_info_and_extensions(display_name, auth_info, &[], &[]) } /// Connects to the X server, using an authorization information. /// /// Extension data specified by `mandatory` and `optional` is cached to allow /// the resolution of events and errors in these extensions. /// /// Connects to the X server specified by `display_name`, using the /// authorization `auth_info`. If a particular screen on that server, it is /// returned in the second tuple member, which is otherwise set to `0`. /// /// # Panics /// Panics if one of the mandatory extension is not present. pub fn connect_to_display_with_auth_info_and_extensions( display_name: Option<&str>, auth_info: AuthInfo<'_>, mandatory: &[Extension], optional: &[Extension], ) -> ConnResult<(Connection, i32)> { let mut screen_num: c_int = 0; let display_name = display_name.map(|s| CString::new(s).unwrap()); unsafe { let display_name = if let Some(display_name) = &display_name { display_name.as_ptr() } else { ptr::null() }; let auth_name = CString::new(auth_info.name).unwrap(); let auth_data = CString::new(auth_info.data).unwrap(); let mut auth_info = xcb_auth_info_t { namelen: auth_name.as_bytes().len() as _, name: auth_name.as_ptr() as *mut _, datalen: auth_data.as_bytes().len() as _, data: auth_data.as_ptr() as *mut _, }; let conn = xcb_connect_to_display_with_auth_info( display_name, &mut auth_info as *mut _, &mut screen_num as *mut _, ); check_connection_error(conn)?; let conn = Self::from_raw_conn_and_extensions(conn, mandatory, optional); conn.has_error().map(|_| (conn, screen_num as i32)) } } /// builds a new Connection object from an available connection /// /// # Safety /// The `conn` pointer must point to a valid `xcb_connection_t` pub unsafe fn from_raw_conn(conn: *mut xcb_connection_t) -> Connection { Self::from_raw_conn_and_extensions(conn, &[], &[]) } /// Builds a new `Connection` object from an available connection and cache the extension data /// /// Extension data specified by `mandatory` and `optional` is cached to allow /// the resolution of events and errors in these extensions. /// /// # Panics /// Panics if the connection is null or in error state. /// Panics if one of the mandatory extension is not present. /// /// # Safety /// The `conn` pointer must point to a valid `xcb_connection_t` pub unsafe fn from_raw_conn_and_extensions( conn: *mut xcb_connection_t, mandatory: &[Extension], optional: &[Extension], ) -> Connection { assert!(!conn.is_null()); assert!(check_connection_error(conn).is_ok()); #[cfg(feature = "debug_atom_names")] let dbg_atom_names = { if dan::DAN_CONN.is_null() { dan::DAN_CONN = conn; true } else { false } }; let ext_data = cache_extensions_data(conn, mandatory, optional); #[cfg(not(feature = "xlib_xcb"))] #[cfg(not(feature = "debug_atom_names"))] return Connection { c: conn, ext_data }; #[cfg(not(feature = "xlib_xcb"))] #[cfg(feature = "debug_atom_names")] return Connection { c: conn, ext_data, dbg_atom_names, }; #[cfg(feature = "xlib_xcb")] #[cfg(not(feature = "debug_atom_names"))] return Connection { c: conn, dpy: ptr::null_mut(), ext_data, }; #[cfg(feature = "xlib_xcb")] #[cfg(feature = "debug_atom_names")] return Connection { c: conn, dpy: ptr::null_mut(), ext_data, dbg_atom_names, }; } /// Initialize a new `Connection` from an existing Xlib display. /// /// Wraps a `xlib::Display` and get an XCB connection from an exisiting object /// `xlib::XCloseDisplay` will be called when the returned object is dropped. /// /// This function is behind the `xlib_xcb` cargo feature. /// /// # Safety /// The `dpy` pointer must be a pointer to a valid `xlib::Display` #[cfg(feature = "xlib_xcb")] pub unsafe fn from_xlib_display(dpy: *mut xlib::Display) -> Connection { Self::from_xlib_display_and_extensions(dpy, &[], &[]) } /// Initialize a new `Connection` from an existing Xlib display. /// /// Wraps a `xlib::Display` and get an XCB connection from an exisiting object /// `xlib::XCloseDisplay` will be called when the returned object is dropped. /// /// Extension data specified by `mandatory` and `optional` is cached to allow /// the resolution of events and errors in these extensions. /// /// This function is behind the `xlib_xcb` cargo feature. /// /// # Panics /// Panics if the connection is null or in error state. /// /// # Safety /// The `dpy` pointer must be a pointer to a valid `xlib::Display`. #[cfg(feature = "xlib_xcb")] pub unsafe fn from_xlib_display_and_extensions( dpy: *mut xlib::Display, mandatory: &[Extension], optional: &[Extension], ) -> Connection { assert!(!dpy.is_null(), "attempt connect with null display"); let c = XGetXCBConnection(dpy); assert!(check_connection_error(c).is_ok()); #[cfg(feature = "debug_atom_names")] let dbg_atom_names = { if dan::DAN_CONN.is_null() { dan::DAN_CONN = c; true } else { false } }; let ext_data = cache_extensions_data(c, mandatory, optional); #[cfg(feature = "debug_atom_names")] return Connection { c, dpy, ext_data, dbg_atom_names, }; #[cfg(not(feature = "debug_atom_names"))] return Connection { c, dpy, ext_data }; } /// Get the extensions activated for this connection. /// /// You may use this to check if an optional extension is present or not. /// /// # Example /// ```no_run /// # fn main() -> xcb::Result<()> { /// // Xkb is mandatory, Input is optional /// let (conn, screen) = xcb::Connection::connect_with_extensions( /// None, &[xcb::Extension::Xkb], &[xcb::Extension::Input] /// )?; /// // now we check if Input is present or not /// let has_input_ext = conn.active_extensions().any(|e| e == xcb::Extension::Input); /// # Ok(()) /// # } /// ``` pub fn active_extensions(&self) -> impl Iterator + '_ { self.ext_data.iter().map(|eed| eed.ext) } /// Returns the inner ffi `xcb_connection_t` pointer pub fn get_raw_conn(&self) -> *mut xcb_connection_t { self.c } /// Consumes this object, returning the inner ffi `xcb_connection_t` pointer pub fn into_raw_conn(self) -> *mut xcb_connection_t { let c = self.c; mem::forget(self); c } /// Returns the inner ffi `xlib::Display` pointer. /// /// This function is behind the `xlib_xcb` cargo feature. #[cfg(feature = "xlib_xcb")] pub fn get_raw_dpy(&self) -> *mut xlib::Display { self.dpy } /// Sets the owner of the event queue in the case if the connection is opened /// with the Xlib interface. In that case, the default owner is Xlib. /// /// This function is behind the `xlib_xcb` cargo feature. #[cfg(feature = "xlib_xcb")] pub fn set_event_queue_owner(&self, owner: EventQueueOwner) { debug_assert!(!self.dpy.is_null()); unsafe { XSetEventQueueOwner( self.dpy, match owner { EventQueueOwner::Xcb => XCBOwnsEventQueue, EventQueueOwner::Xlib => XlibOwnsEventQueue, }, ); } } /// Returns the maximum request length that this server accepts. /// /// In the absence of the BIG-REQUESTS extension, returns the /// maximum request length field from the connection setup data, which /// may be as much as 65535. If the server supports BIG-REQUESTS, then /// the maximum request length field from the reply to the /// BigRequestsEnable request will be returned instead. /// /// Note that this length is measured in four-byte units, making the /// theoretical maximum lengths roughly 256kB without BIG-REQUESTS and /// 16GB with. pub fn get_maximum_request_length(&self) -> u32 { unsafe { xcb_get_maximum_request_length(self.c) } } /// Prefetch the maximum request length without blocking. /// /// Without blocking, does as much work as possible toward computing /// the maximum request length accepted by the X server. /// /// Invoking this function may send the [crate::bigreq::Enable] request, /// but will not block waiting for the reply. /// [Connection::get_maximum_request_length] will return the prefetched data /// after possibly blocking while the reply is retrieved. /// /// Note that in order for this function to be fully non-blocking, the /// application must previously have called [crate::bigreq::prefetch_extension_data]. pub fn prefetch_maximum_request_length(&self) { unsafe { xcb_prefetch_maximum_request_length(self.c); } } /// Allocates an XID for a new object. /// /// Returned value is typically used in requests such as `CreateWindow`. /// /// # Example /// ```no_run /// # use xcb::x; /// # fn main() -> xcb::Result<()> { /// # let conn = xcb::Connection::connect(None)?.0; /// let window: x::Window = conn.generate_id(); /// # Ok(()) /// # } /// ``` pub fn generate_id(&self) -> T { unsafe { XidNew::new(xcb_generate_id(self.c)) } } /// Forces any buffered output to be written to the server. /// /// Forces any buffered output to be written to the server. Blocks /// until the write is complete. /// /// There are several occasions ones want to flush the connection. /// One of them is before entering or re-entering the event loop after performing unchecked requests. /// /// The main difference between `flush` and `check_request` is that `flush` will not report protocol errors. /// If a protocol error is emitted by an unchecked void request, it will be reported through the event loop. /// /// See also: [wait_for_event](Connection::wait_for_event), [check_request](Connection::check_request), /// [send_and_check_request](Connection::send_and_check_request). pub fn flush(&self) -> ConnResult<()> { unsafe { let ret = xcb_flush(self.c); if ret > 0 { Ok(()) } else { self.has_error()?; unreachable!() } } } unsafe fn handle_wait_for_event(&self, ev: *mut xcb_generic_event_t) -> Result { if ev.is_null() { self.has_error()?; panic!("xcb_wait_for_event returned null with I/O error"); } else if is_error(ev) { Err(error::resolve_error(ev as *mut _, &self.ext_data).into()) } else { Ok(event::resolve_event(ev, &self.ext_data)) } } /// Resolve an xcb_generic_event_t pointer into an Event. /// # Safety /// The caller is repsonsible to ensure that the `ev` pointer is not NULL. /// The ownership of the pointer is effectively transferred to the /// returned Event and it will be destroyed when the Event is /// dropped. pub unsafe fn resolve_event(&self, ev: &mut xcb_generic_event_t) -> Event { event::resolve_event(ev, &self.ext_data) } unsafe fn handle_poll_for_event(&self, ev: *mut xcb_generic_event_t) -> Result> { if ev.is_null() { self.has_error()?; Ok(None) } else if is_error(ev) { Err(error::resolve_error(ev as *mut _, &self.ext_data).into()) } else { Ok(Some(event::resolve_event(ev, &self.ext_data))) } } /// Blocks and returns the next event or error from the server. /// /// # Example /// ```no_run /// use xcb::x; /// fn main() -> xcb::Result<()> { /// # let conn = xcb::Connection::connect(None)?.0; /// // ... /// loop { /// let event = match conn.wait_for_event() { /// Err(xcb::Error::Connection(err)) => { /// panic!("unexpected I/O error: {}", err); /// } /// Err(xcb::Error::Protocol(xcb::ProtocolError::X(x::Error::Font(err), _req_name))) => { /// // may be this particular error is fine? /// continue; /// } /// Err(xcb::Error::Protocol(err)) => { /// panic!("unexpected protocol error: {:#?}", err); /// } /// Ok(event) => event, /// }; /// match event { /// xcb::Event::X(x::Event::KeyPress(ev)) => { /// // do stuff with the key press /// } /// // handle other events /// _ => { /// break Ok(()); /// } /// } /// } /// } /// ``` pub fn wait_for_event(&self) -> Result { unsafe { let ev = xcb_wait_for_event(self.c); self.handle_wait_for_event(ev) } } /// Returns the next event or error from the server without blocking. /// /// Returns the next event or error from the server, if one is /// available. If no event is available, that /// might be because an I/O error like connection close occurred while /// attempting to read the next event, in which case the connection is /// shut down when this function returns. pub fn poll_for_event(&self) -> Result> { unsafe { let ev = xcb_poll_for_event(self.c); self.handle_poll_for_event(ev) } } /// Returns the next event without reading from the connection. /// /// This is a version of [Connection::poll_for_event] that only examines the /// event queue for new events. The function doesn't try to read new /// events from the connection if no queued events are found. /// /// This function is useful for callers that know in advance that all /// interesting events have already been read from the connection. For /// example, callers might use [Connection::wait_for_reply] and be interested /// only of events that preceded a specific reply. pub fn poll_for_queued_event(&self) -> ProtocolResult> { unsafe { let ev = xcb_poll_for_queued_event(self.c); if ev.is_null() { Ok(None) } else if is_error(ev) { Err(error::resolve_error(ev as *mut _, &self.ext_data)) } else { Ok(Some(event::resolve_event(ev, &self.ext_data))) } } } /// Start listening for a special event. /// /// Effectively creates an internal special queue for this event /// XGE events are only defined in the `xinput` and `present` extensions /// /// This function is present only if either of the `xinput` or `present` cargo features are active. #[cfg(any(feature = "xinput", feature = "present"))] pub fn register_for_special_xge(&self) -> SpecialEventId { unsafe { let ext: *mut xcb_extension_t = match XGE::EXTENSION { #[cfg(feature = "xinput")] Extension::Input => &mut xinput::FFI_EXT as *mut _, #[cfg(feature = "present")] Extension::Present => &mut present::FFI_EXT as *mut _, _ => unreachable!("only Input and Present have XGE events"), }; let mut stamp: Timestamp = 0; let raw = xcb_register_for_special_xge(self.c, ext, XGE::NUMBER, &mut stamp as *mut _); SpecialEventId { raw, stamp } } } /// Stop listening to a special event #[cfg(any(feature = "xinput", feature = "present"))] pub fn unregister_for_special_xge(&self, se: SpecialEventId) { unsafe { xcb_unregister_for_special_xge(self.c, se.raw); } } /// Returns the next event from a special queue, blocking until one arrives #[cfg(any(feature = "xinput", feature = "present"))] pub fn wait_for_special_event(&self, se: SpecialEventId) -> Result { unsafe { let ev = xcb_wait_for_special_event(self.c, se.raw); self.handle_wait_for_event(ev) } } /// Returns the next event from a special queue #[cfg(any(feature = "xinput", feature = "present"))] pub fn poll_for_special_event(&self, se: SpecialEventId) -> Result> { unsafe { let ev = xcb_poll_for_special_event(self.c, se.raw); self.handle_poll_for_event(ev) } } /// Discards the reply for a request. /// /// Discards the reply for a request. Additionally, any error generated /// by the request is also discarded (unless it was an _unchecked request /// and the error has already arrived). /// /// This function will not block even if the reply is not yet available. fn discard_reply(&self, cookie: C) { unsafe { xcb_discard_reply64(self.c, cookie.sequence()); } } /// Access the data returned by the server. /// /// Accessor for the data returned by the server when the connection /// was initialized. This data includes /// - the server's required format for images, /// - a list of available visuals, /// - a list of available screens, /// - the server's maximum request length (in the absence of the /// BIG-REQUESTS extension), /// - and other assorted information. /// /// See the X protocol specification for more details. pub fn get_setup(&self) -> &Setup { unsafe { let ptr = xcb_get_setup(self.c); // let len = <&Setup as WiredIn>::compute_wire_len(ptr, ()); let mut _offset = 0; <&Setup as WiredIn>::unserialize(ptr, (), &mut _offset) } } /// Test whether the connection has shut down due to a fatal error. /// /// Some errors that occur in the context of a connection /// are unrecoverable. When such an error occurs, the /// connection is shut down and further operations on the /// connection have no effect. pub fn has_error(&self) -> ConnResult<()> { unsafe { check_connection_error(self.c) } } /// Send a request to the X server. /// /// This function never blocks. A cookie is returned to keep track of the request. /// If the request expect a reply, the cookie can be used to retrieve the reply with /// [Connection::wait_for_reply]. /// /// # Example /// ```no_run /// # use xcb::x; /// # fn main() -> xcb::Result<()> { /// # let (conn, screen_num) = xcb::Connection::connect(None)?; /// # let setup = conn.get_setup(); /// # let screen = setup.roots().nth(screen_num as usize).unwrap(); /// # let window: x::Window = conn.generate_id(); /// // Example of void request. /// // Error (if any) will be sent to the event loop (see `wait_for_event`). /// // In this case, the cookie can be discarded. /// conn.send_request(&x::CreateWindow { /// depth: x::COPY_FROM_PARENT as u8, /// wid: window, /// parent: screen.root(), /// x: 0, /// y: 0, /// width: 150, /// height: 150, /// border_width: 10, /// class: x::WindowClass::InputOutput, /// visual: screen.root_visual(), /// value_list: &[ /// x::Cw::BackPixel(screen.white_pixel()), /// x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), /// ], /// }); /// /// // Example of request with reply. The error (if any) is obtained with the reply. /// let cookie = conn.send_request(&x::InternAtom { /// only_if_exists: true, /// name: b"WM_PROTOCOLS", /// }); /// let wm_protocols_atom: x::Atom = conn /// .wait_for_reply(cookie)? /// .atom(); /// # Ok(()) /// # } /// ``` pub fn send_request(&self, req: &R) -> R::Cookie where R: Request, { unsafe { R::Cookie::from_sequence(req.raw_request(self, !R::IS_VOID)) } } /// Send a checked request to the X server. /// /// Checked requests do not expect a reply, but the returned cookie can be used to check for /// errors using `Connection::check_request`. /// /// # Example /// ```no_run /// # use xcb::x; /// # fn main() -> xcb::Result<()> { /// # let (conn, screen_num) = xcb::Connection::connect(None)?; /// # let window: x::Window = conn.generate_id(); /// let cookie = conn.send_request_checked(&x::MapWindow { window }); /// conn.check_request(cookie)?; /// # Ok(()) /// # } /// ``` pub fn send_request_checked(&self, req: &R) -> VoidCookieChecked where R: RequestWithoutReply, { unsafe { VoidCookieChecked::from_sequence(req.raw_request(self, true)) } } /// Send an unchecked request to the X server. /// /// Unchecked requests expect a reply that is to be retrieved by [Connection::wait_for_reply_unchecked]. /// Unchecked means that the error is not checked when the reply is fetched. Instead, the error will /// be sent to the event loop /// /// # Example /// ```no_run /// # use xcb::x; /// # fn main() -> xcb::Result<()> { /// # let (conn, screen_num) = xcb::Connection::connect(None)?; /// let cookie = conn.send_request_unchecked(&x::InternAtom { /// only_if_exists: true, /// name: b"WM_PROTOCOLS", /// }); /// let wm_protocols_atom: Option = conn /// .wait_for_reply_unchecked(cookie)? /// .map(|rep| rep.atom()); /// # Ok(()) /// # } /// ``` pub fn send_request_unchecked(&self, req: &R) -> R::CookieUnchecked where R: RequestWithReply, { unsafe { R::CookieUnchecked::from_sequence(req.raw_request(self, false)) } } /// Check a checked request for errors. /// /// The cookie supplied to this function must have resulted /// from a call to [Connection::send_request_checked]. This function will block /// until one of two conditions happens. If an error is received, it will be /// returned. If a reply to a subsequent request has already arrived, no error /// can arrive for this request, so this function will return `Ok(())`. /// /// Note that this function will perform a sync if needed to ensure that the /// sequence number will advance beyond that provided in cookie; this is a /// convenience to avoid races in determining whether the sync is needed. /// /// # Example /// ```no_run /// # use xcb::x; /// # fn main() -> xcb::Result<()> { /// # let (conn, screen_num) = xcb::Connection::connect(None)?; /// # let window: x::Window = conn.generate_id(); /// conn.check_request(conn.send_request_checked(&x::MapWindow { window }))?; /// # Ok(()) /// # } /// ``` pub fn check_request(&self, cookie: VoidCookieChecked) -> ProtocolResult<()> { let cookie = xcb_void_cookie_t { seq: cookie.sequence() as u32, }; let error = unsafe { xcb_request_check(self.c, cookie) }; if error.is_null() { Ok(()) } else { unsafe { let res = error::resolve_error(error, &self.ext_data); Err(res) } } } /// Send the request to the server and check it. /// /// This is a sugar for `conn.check_request(conn.send_request_checked(req))` /// /// This method is useful as well in place of code sending a void request /// and flushing the connection right after. Checking the request effectively /// flushes the connection, but in addition reports possible protocol errors /// at the calling site instead of reporting them through the event loop. /// /// # Example /// ```no_run /// # use xcb::x; /// # fn main() -> xcb::Result<()> { /// # let (conn, screen_num) = xcb::Connection::connect(None)?; /// # let window: x::Window = conn.generate_id(); /// conn.send_and_check_request(&x::MapWindow { window })?; /// # Ok(()) /// # } /// ``` pub fn send_and_check_request(&self, req: &R) -> ProtocolResult<()> where R: RequestWithoutReply, { self.check_request(self.send_request_checked(req)) } /// Get the reply of a previous request, or an error if one occured. /// /// # Example /// ```no_run /// # use xcb::x; /// # fn main() -> xcb::Result<()> { /// # let (conn, screen_num) = xcb::Connection::connect(None)?; /// let cookie = conn.send_request(&x::InternAtom { /// only_if_exists: true, /// name: b"WM_PROTOCOLS", /// }); /// let wm_protocols_atom: x::Atom = conn /// .wait_for_reply(cookie)? /// .atom(); /// # Ok(()) /// # } /// ``` pub fn wait_for_reply(&self, cookie: C) -> Result where C: CookieWithReplyChecked, { unsafe { let mut error: *mut xcb_generic_error_t = std::ptr::null_mut(); let reply = xcb_wait_for_reply64(self.c, cookie.sequence(), &mut error as *mut _); match (reply.is_null(), error.is_null()) { (true, true) => { self.has_error()?; unreachable!("xcb_wait_for_reply64 returned null without I/O error"); } (true, false) => { let error = error::resolve_error(error, &self.ext_data); Err(error.into()) } (false, true) => Ok(C::Reply::from_raw(reply as *const u8)), (false, false) => unreachable!("xcb_wait_for_reply64 returned two pointers"), } } } /// Get the reply of a previous unchecked request. /// /// If an error occured, `None` is returned and the error will be delivered to the event loop. /// /// # Example /// ```no_run /// # use xcb::x; /// # fn main() -> xcb::Result<()> { /// # let (conn, screen_num) = xcb::Connection::connect(None)?; /// let cookie = conn.send_request_unchecked(&x::InternAtom { /// only_if_exists: true, /// name: b"WM_PROTOCOLS", /// }); /// let wm_protocols_atom: Option = conn /// .wait_for_reply_unchecked(cookie)? // connection error may happen /// .map(|rep| rep.atom()); /// # Ok(()) /// # } /// ``` pub fn wait_for_reply_unchecked(&self, cookie: C) -> ConnResult> where C: CookieWithReplyUnchecked, { unsafe { let reply = xcb_wait_for_reply64(self.c, cookie.sequence(), ptr::null_mut()); if reply.is_null() { self.has_error()?; Ok(None) } else { Ok(Some(C::Reply::from_raw(reply as *const u8))) } } } /// Obtain number of bytes read from the connection. /// /// Returns cumulative number of bytes received from the connection. /// /// This retrieves the total number of bytes read from this connection, /// to be used for diagnostic/monitoring/informative purposes. pub fn total_read(&self) -> usize { unsafe { xcb_total_read(self.c) as usize } } /// Obtain number of bytes written to the connection. /// /// Returns cumulative number of bytes sent to the connection. /// /// This retrieves the total number of bytes written to this connection, /// to be used for diagnostic/monitoring/informative purposes. pub fn total_written(&self) -> usize { unsafe { xcb_total_written(self.c) as usize } } } impl AsRef for Connection { fn as_ref(&self) -> &Connection { self } } impl AsRawFd for Connection { fn as_raw_fd(&self) -> RawFd { unsafe { xcb_get_file_descriptor(self.c) } } } // SAFETY: We provide a valid xcb_connection_t that is valid for as long as required by the trait. #[cfg(feature = "as-raw-xcb-connection")] unsafe impl as_raw_xcb_connection::AsRawXcbConnection for Connection { fn as_raw_xcb_connection(&self) -> *mut as_raw_xcb_connection::xcb_connection_t { self.get_raw_conn().cast() } } impl Drop for Connection { fn drop(&mut self) { #[cfg(feature = "debug_atom_names")] if self.dbg_atom_names { unsafe { dan::DAN_CONN = ptr::null_mut(); } } #[cfg(not(feature = "xlib_xcb"))] unsafe { xcb_disconnect(self.c); } #[cfg(feature = "xlib_xcb")] unsafe { if self.dpy.is_null() { xcb_disconnect(self.c); } else { xlib::XCloseDisplay(self.dpy); } } } } #[cfg(feature = "debug_atom_names")] mod dan { use super::{Connection, Xid}; use crate::ffi::base::xcb_connection_t; use crate::x; use std::fmt; use std::mem; use std::ptr; use std::str; pub(crate) static mut DAN_CONN: *mut xcb_connection_t = ptr::null_mut(); impl fmt::Debug for x::Atom { #[allow(clippy::print_in_format_impl)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.resource_id() == 0 { return f.write_str("ATOM_NONE"); } let conn = unsafe { Connection::from_raw_conn(DAN_CONN) }; let cookie = conn.send_request(&x::GetAtomName { atom: *self }); let reply = conn.wait_for_reply(cookie).map_err(|err| { eprintln!( "Error during fmt::Debug of x::Atom (fetching atom name): {:#?}", err ); fmt::Error })?; let name = reply.name().to_utf8(); f.write_fmt(format_args!("Atom(\"{}\" ; {})", name, self.resource_id()))?; mem::forget(conn); Ok(()) } } } unsafe fn check_connection_error(conn: *mut xcb_connection_t) -> ConnResult<()> { match xcb_connection_has_error(conn) { 0 => Ok(()), XCB_CONN_ERROR => Err(ConnError::Connection), XCB_CONN_CLOSED_EXT_NOTSUPPORTED => Err(ConnError::ClosedExtNotSupported), XCB_CONN_CLOSED_MEM_INSUFFICIENT => Err(ConnError::ClosedMemInsufficient), XCB_CONN_CLOSED_REQ_LEN_EXCEED => Err(ConnError::ClosedReqLenExceed), XCB_CONN_CLOSED_PARSE_ERR => Err(ConnError::ClosedParseErr), XCB_CONN_CLOSED_INVALID_SCREEN => Err(ConnError::ClosedInvalidScreen), XCB_CONN_CLOSED_FDPASSING_FAILED => Err(ConnError::ClosedFdPassingFailed), code => unreachable!("unexpected error code from XCB: {}", code), } } unsafe fn is_error(ev: *mut xcb_generic_event_t) -> bool { debug_assert!(!ev.is_null()); (*ev).response_type == 0 } bitflags! { pub(crate) struct RequestFlags: u32 { const NONE = 0; const CHECKED = 1; const RAW = 2; const DISCARD_REPLY = 4; const REPLY_FDS = 8; } } /// Compute the necessary padding after `base` to have `align` alignment pub(crate) fn align_pad(base: usize, align: usize) -> usize { debug_assert!(align.is_power_of_two(), "`align` must be a power of two"); let base = base as isize; let align = align as isize; (-base & (align - 1)) as usize } #[test] fn test_align_pad() { // align 1 assert_eq!(align_pad(0, 1), 0); assert_eq!(align_pad(1234, 1), 0); assert_eq!(align_pad(1235, 1), 0); // align 2 assert_eq!(align_pad(0, 2), 0); assert_eq!(align_pad(1233, 2), 1); assert_eq!(align_pad(1234, 2), 0); // align 4 assert_eq!(align_pad(0, 4), 0); assert_eq!(align_pad(12, 4), 0); assert_eq!(align_pad(13, 4), 3); assert_eq!(align_pad(14, 4), 2); assert_eq!(align_pad(15, 4), 1); assert_eq!(align_pad(16, 4), 0); } xcb-1.2.2/src/error.rs000064400000000000000000000332031046102023000126710ustar 00000000000000use crate::base::ResolveWireError; use crate::ext::{Extension, ExtensionData}; use crate::ffi::*; use crate::x; use std::mem; #[cfg(feature = "damage")] use crate::damage; #[cfg(feature = "glx")] use crate::glx; #[cfg(feature = "randr")] use crate::randr; #[cfg(feature = "render")] use crate::render; #[cfg(feature = "shm")] use crate::shm; #[cfg(feature = "sync")] use crate::sync; #[cfg(feature = "xf86vidmode")] use crate::xf86vidmode; #[cfg(feature = "xfixes")] use crate::xfixes; #[cfg(feature = "xinput")] use crate::xinput; #[cfg(feature = "xkb")] use crate::xkb; #[cfg(feature = "xprint")] use crate::xprint; #[cfg(feature = "xv")] use crate::xv; /// A protocol error issued from the X server /// /// The second member is the name of the request that emitted the error (if any). #[derive(Debug)] pub enum ProtocolError { /// The error is from the core X protocol. X(x::Error, Option<&'static str>), #[cfg(feature = "damage")] /// The error is issued from the `DAMAGE` extension. Damage(damage::Error, Option<&'static str>), #[cfg(feature = "glx")] /// The error is issued from the `GLX` extension. Glx(glx::Error, Option<&'static str>), #[cfg(feature = "randr")] /// The error is issued from the `RANDR` extension. RandR(randr::Error, Option<&'static str>), #[cfg(feature = "render")] /// The error is issued from the `RENDER` extension. Render(render::Error, Option<&'static str>), #[cfg(feature = "shm")] /// The error is issued from the `MIT-SHM` extension. Shm(shm::Error, Option<&'static str>), #[cfg(feature = "sync")] /// The error is issued from the `SYNC` extension. Sync(sync::Error, Option<&'static str>), #[cfg(feature = "xf86vidmode")] /// The error is issued from the `XFree86-VidModeExtension` extension. Xf86VidMode(xf86vidmode::Error, Option<&'static str>), #[cfg(feature = "xfixes")] /// The error is issued from the `XFIXES` extension. XFixes(xfixes::Error, Option<&'static str>), #[cfg(feature = "xinput")] /// The error is issued from the `XInputExtension` extension. Input(xinput::Error, Option<&'static str>), #[cfg(feature = "xkb")] /// The error is issued from the `XKEYBOARD` extension. Xkb(xkb::Error, Option<&'static str>), #[cfg(feature = "xprint")] /// The error is issued from the `XpExtension` extension. XPrint(xprint::Error, Option<&'static str>), #[cfg(feature = "xv")] /// The error is issued from the `XVideo` extension. Xv(xv::Error, Option<&'static str>), } impl std::fmt::Display for ProtocolError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Debug::fmt(self, f) } } impl std::error::Error for ProtocolError {} pub(crate) unsafe fn resolve_error( error: *mut xcb_generic_error_t, extension_data: &[ExtensionData], ) -> ProtocolError { debug_assert!(!error.is_null()); let error_code = (*error).error_code; let major_code = (*error).major_code; let minor_code = (*error).minor_code; let (best, emitting_ext) = { let mut best: Option<&ExtensionData> = None; let mut emitting_ext = None; for data in extension_data { if data.major_opcode == major_code { emitting_ext = Some(data.ext); } if data.first_error > 0 && error_code >= data.first_error { if let Some(ed) = best { if data.first_error > ed.first_error { best = Some(data); } } else { best = Some(data); } } } (best, emitting_ext) }; let emitted_by = if let Some(ext) = emitting_ext { match ext { Extension::BigRequests => crate::bigreq::request_name(minor_code), Extension::XcMisc => crate::xc_misc::request_name(minor_code), #[cfg(feature = "composite")] Extension::Composite => crate::composite::request_name(minor_code), #[cfg(feature = "damage")] Extension::Damage => crate::damage::request_name(minor_code), #[cfg(feature = "dpms")] Extension::Dpms => crate::dpms::request_name(minor_code), #[cfg(feature = "dri2")] Extension::Dri2 => crate::dri2::request_name(minor_code), #[cfg(feature = "dri3")] Extension::Dri3 => crate::dri3::request_name(minor_code), #[cfg(feature = "ge")] Extension::GenericEvent => crate::ge::request_name(minor_code), #[cfg(feature = "glx")] Extension::Glx => crate::glx::request_name(minor_code), #[cfg(feature = "present")] Extension::Present => crate::present::request_name(minor_code), #[cfg(feature = "randr")] Extension::RandR => crate::randr::request_name(minor_code), #[cfg(feature = "record")] Extension::Record => crate::record::request_name(minor_code), #[cfg(feature = "render")] Extension::Render => crate::render::request_name(minor_code), #[cfg(feature = "res")] Extension::Res => crate::res::request_name(minor_code), #[cfg(feature = "screensaver")] Extension::ScreenSaver => crate::screensaver::request_name(minor_code), #[cfg(feature = "shape")] Extension::Shape => crate::shape::request_name(minor_code), #[cfg(feature = "shm")] Extension::Shm => crate::shm::request_name(minor_code), #[cfg(feature = "sync")] Extension::Sync => crate::sync::request_name(minor_code), #[cfg(feature = "xevie")] Extension::Xevie => crate::xevie::request_name(minor_code), #[cfg(feature = "xf86dri")] Extension::Xf86Dri => crate::xf86dri::request_name(minor_code), #[cfg(feature = "xf86vidmode")] Extension::Xf86VidMode => crate::xf86vidmode::request_name(minor_code), #[cfg(feature = "xfixes")] Extension::XFixes => crate::xfixes::request_name(minor_code), #[cfg(feature = "xinerama")] Extension::Xinerama => crate::xinerama::request_name(minor_code), #[cfg(feature = "xinput")] Extension::Input => crate::xinput::request_name(minor_code), #[cfg(feature = "xkb")] Extension::Xkb => crate::xkb::request_name(minor_code), #[cfg(feature = "xprint")] Extension::XPrint => crate::xprint::request_name(minor_code), #[cfg(feature = "xselinux")] Extension::SeLinux => crate::xselinux::request_name(minor_code), #[cfg(feature = "xtest")] Extension::Test => crate::xtest::request_name(minor_code), #[cfg(feature = "xv")] Extension::Xv => crate::xv::request_name(minor_code), #[cfg(feature = "xvmc")] Extension::XvMc => crate::xvmc::request_name(minor_code), } } else { crate::x::request_name(major_code as u16) }; if let Some(ext_data) = best { match ext_data.ext { #[cfg(feature = "damage")] Extension::Damage => ProtocolError::Damage( damage::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), #[cfg(feature = "glx")] Extension::Glx => ProtocolError::Glx( glx::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), #[cfg(feature = "randr")] Extension::RandR => ProtocolError::RandR( randr::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), #[cfg(feature = "shm")] Extension::Shm => ProtocolError::Shm( shm::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), #[cfg(feature = "sync")] Extension::Sync => ProtocolError::Sync( sync::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), #[cfg(feature = "xf86vidmode")] Extension::Xf86VidMode => ProtocolError::Xf86VidMode( xf86vidmode::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), #[cfg(feature = "xfixes")] Extension::XFixes => ProtocolError::XFixes( xfixes::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), #[cfg(feature = "xinput")] Extension::Input => ProtocolError::Input( xinput::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), #[cfg(feature = "xkb")] Extension::Xkb => ProtocolError::Xkb( xkb::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), #[cfg(feature = "xprint")] Extension::XPrint => ProtocolError::XPrint( xprint::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), #[cfg(feature = "xv")] Extension::Xv => ProtocolError::Xv( xv::Error::resolve_wire_error(ext_data.first_error, error), emitted_by, ), _ => unreachable!("Could not match extension event"), } } else { ProtocolError::X(x::Error::resolve_wire_error(0, error), emitted_by) } } #[cfg(all(feature = "xinput", feature = "xkb", feature = "screensaver"))] #[test] fn test_resolve_error() { // resolve a core error with core request let mut error = xcb_generic_error_t { response_type: 0, error_code: 2, sequence: 12000, resource_id: 12, minor_code: 0, major_code: 4, pad0: 0, pad: [0; 5], full_sequence: 12000, }; let extension_data = []; let err = unsafe { resolve_error(&mut error as *mut _, &extension_data) }; assert!( matches!(err, ProtocolError::X(x::Error::Value(_), Some(req_name)) if req_name == "x::DestroyWindow") ); mem::forget(err); // resolve a core error with xinput request let mut error = xcb_generic_error_t { response_type: 0, error_code: 2, sequence: 12000, resource_id: 12, minor_code: 4, major_code: 100, pad0: 0, pad: [0; 5], full_sequence: 12000, }; let extension_data = [ ExtensionData { ext: Extension::Input, major_opcode: 100, first_event: 50, first_error: 20, }, ExtensionData { ext: Extension::Xkb, major_opcode: 200, first_event: 80, first_error: 40, }, ]; let err = unsafe { resolve_error(&mut error as *mut _, &extension_data) }; assert!( matches!(err, ProtocolError::X(x::Error::Value(_), Some(req_name)) if req_name == "xinput::CloseDevice") ); mem::forget(err); // resolve a xinput error with xinput request let mut error = xcb_generic_error_t { response_type: 0, error_code: 22, sequence: 12000, resource_id: 12, minor_code: 4, major_code: 100, pad0: 0, pad: [0; 5], full_sequence: 12000, }; let extension_data = [ ExtensionData { ext: Extension::Input, major_opcode: 100, first_event: 50, first_error: 20, }, ExtensionData { ext: Extension::Xkb, major_opcode: 200, first_event: 80, first_error: 40, }, ]; let err = unsafe { resolve_error(&mut error as *mut _, &extension_data) }; assert!( matches!(err, ProtocolError::Input(xinput::Error::Mode(_), Some(req_name)) if req_name == "xinput::CloseDevice") ); mem::forget(err); // same as previous, but reverse order of extension data let mut error = xcb_generic_error_t { response_type: 0, error_code: 22, sequence: 12000, resource_id: 12, minor_code: 4, major_code: 100, pad0: 0, pad: [0; 5], full_sequence: 12000, }; let extension_data = [ ExtensionData { ext: Extension::Input, major_opcode: 100, first_event: 50, first_error: 20, }, ExtensionData { ext: Extension::Xkb, major_opcode: 200, first_event: 80, first_error: 40, }, ]; let err = unsafe { resolve_error(&mut error as *mut _, &extension_data) }; assert!( matches!(err, ProtocolError::Input(xinput::Error::Mode(_), Some(req_name)) if req_name == "xinput::CloseDevice") ); mem::forget(err); // mimic a regular X error (value) with extension that do not have errors (screensaver) let mut error = xcb_generic_error_t { response_type: 0, error_code: 2, sequence: 12000, resource_id: 12, minor_code: 0, major_code: 1, pad0: 0, pad: [0; 5], full_sequence: 12000, }; let extension_data = [ExtensionData { ext: Extension::ScreenSaver, major_opcode: 144, first_event: 92, first_error: 0, }]; let err = unsafe { resolve_error(&mut error as *mut _, &extension_data) }; assert!( matches!(err, ProtocolError::X(x::Error::Value(_), Some(req_name)) if req_name == "x::CreateWindow") ); mem::forget(err); } xcb-1.2.2/src/event.rs000064400000000000000000000241421046102023000126630ustar 00000000000000use crate::base::{BaseEvent, Raw, ResolveWireEvent, ResolveWireGeEvent}; use crate::ext::{Extension, ExtensionData}; use crate::ffi::*; use crate::x; #[cfg(feature = "damage")] use crate::damage; #[cfg(feature = "dri2")] use crate::dri2; #[cfg(feature = "glx")] use crate::glx; #[cfg(feature = "present")] use crate::present; #[cfg(feature = "randr")] use crate::randr; #[cfg(feature = "screensaver")] use crate::screensaver; #[cfg(feature = "shape")] use crate::shape; #[cfg(feature = "shm")] use crate::shm; #[cfg(feature = "sync")] use crate::sync; #[cfg(feature = "xfixes")] use crate::xfixes; #[cfg(feature = "xinput")] use crate::xinput; #[cfg(feature = "xkb")] use crate::xkb; #[cfg(feature = "xprint")] use crate::xprint; #[cfg(feature = "xv")] use crate::xv; /// Unified Event type from the X server. #[derive(Debug)] pub enum Event { /// The event is issued from the X core protocol. X(x::Event), #[cfg(feature = "damage")] /// The event is issued from the `DAMAGE` extension. Damage(damage::Event), #[cfg(feature = "dri2")] /// The event is issued from the `DRI2` extension. Dri2(dri2::Event), #[cfg(feature = "glx")] /// The event is issued from the `GLX` extension. Glx(glx::Event), #[cfg(feature = "present")] /// The event is issued from the `Present` extension. Present(present::Event), #[cfg(feature = "randr")] /// The event is issued from the `RANDR` extension. RandR(randr::Event), #[cfg(feature = "screensaver")] /// The event is issued from the `MIT-SCREEN-SAVER` extension. ScreenSaver(screensaver::Event), #[cfg(feature = "shape")] /// The event is issued from the `SHAPE` extension. Shape(shape::Event), #[cfg(feature = "shm")] /// The event is issued from the `MIT-SHM` extension. Shm(shm::Event), #[cfg(feature = "sync")] /// The event is issued from the `SYNC` extension. Sync(sync::Event), #[cfg(feature = "xfixes")] /// The event is issued from the `XFIXES` extension. XFixes(xfixes::Event), #[cfg(feature = "xinput")] /// The event is issued from the `XInputExtension` extension. Input(xinput::Event), #[cfg(feature = "xkb")] /// The event is issued from the `XKEYBOARD` extension. Xkb(xkb::Event), #[cfg(feature = "xprint")] /// The event is issued from the `XpExtension` extension. XPrint(xprint::Event), #[cfg(feature = "xv")] /// The event is issued from the `XVideo` extension. Xv(xv::Event), /// The event was not recognized, it was likely issued from a disabled extension. Unknown(UnknownEvent), } impl Event { pub fn as_raw(&self) -> *mut xcb_generic_event_t { match self { Self::X(e) => e.as_raw(), #[cfg(feature = "damage")] Self::Damage(e) => e.as_raw(), #[cfg(feature = "dri2")] Self::Dri2(e) => e.as_raw(), #[cfg(feature = "glx")] Self::Glx(e) => e.as_raw(), #[cfg(feature = "present")] Self::Present(e) => e.as_raw(), #[cfg(feature = "randr")] Self::RandR(e) => e.as_raw(), #[cfg(feature = "screensaver")] Self::ScreenSaver(e) => e.as_raw(), #[cfg(feature = "shape")] Self::Shape(e) => e.as_raw(), #[cfg(feature = "shm")] Self::Shm(e) => e.as_raw(), #[cfg(feature = "sync")] Self::Sync(e) => e.as_raw(), #[cfg(feature = "xfixes")] Self::XFixes(e) => e.as_raw(), #[cfg(feature = "xinput")] Self::Input(e) => e.as_raw(), #[cfg(feature = "xkb")] Self::Xkb(e) => e.as_raw(), #[cfg(feature = "xprint")] Self::XPrint(e) => e.as_raw(), #[cfg(feature = "xv")] Self::Xv(e) => e.as_raw(), Self::Unknown(e) => e.as_raw(), } } } /// an event was not recognized as part of the core protocol or any enabled extension pub struct UnknownEvent { raw: *mut xcb_generic_event_t, } impl Raw for UnknownEvent { unsafe fn from_raw(raw: *mut xcb_generic_event_t) -> Self { UnknownEvent { raw } } fn as_raw(&self) -> *mut xcb_generic_event_t { self.raw } } impl BaseEvent for UnknownEvent { const EXTENSION: Option = None; const NUMBER: u32 = u32::MAX; } impl UnknownEvent { pub fn response_type(&self) -> u8 { unsafe { (*self.raw).response_type } } pub fn sequence(&self) -> u16 { unsafe { (*self.raw).sequence } } pub fn full_sequence(&self) -> u32 { unsafe { (*self.raw).full_sequence } } } unsafe impl Send for UnknownEvent {} unsafe impl Sync for UnknownEvent {} impl std::fmt::Debug for UnknownEvent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("UnknownEvent").finish() } } impl Drop for UnknownEvent { fn drop(&mut self) { unsafe { libc::free(self.raw as *mut _) } } } pub(crate) unsafe fn resolve_event( event: *mut xcb_generic_event_t, extension_data: &[ExtensionData], ) -> Event { let response_type = (*event).response_type & 0x7F; if response_type == XCB_GE_GENERIC { let event = event as *mut xcb_ge_generic_event_t; let extension = (*event).extension; for ext in extension_data { if ext.major_opcode == extension { match ext.ext { #[cfg(feature = "present")] Extension::Present => { return Event::Present(present::Event::resolve_wire_ge_event(event)); } #[cfg(feature = "xinput")] Extension::Input => { return Event::Input(xinput::Event::resolve_wire_ge_event(event)); } _ => panic!("could not resolve Generic Event extension"), } } } } for data in extension_data { if response_type >= data.first_event && data.first_event != 0 { match data.ext { #[cfg(feature = "damage")] Extension::Damage => { return Event::Damage( damage::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "dri2")] Extension::Dri2 => { return Event::Dri2( dri2::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "glx")] Extension::Glx => { return Event::Glx( glx::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "present")] Extension::Present => { return Event::Present( present::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "randr")] Extension::RandR => { return Event::RandR( randr::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "screensaver")] Extension::ScreenSaver => { return Event::ScreenSaver( screensaver::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "shape")] Extension::Shape => { return Event::Shape( shape::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "shm")] Extension::Shm => { return Event::Shm( shm::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "sync")] Extension::Sync => { return Event::Sync( sync::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "xfixes")] Extension::XFixes => { return Event::XFixes( xfixes::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "xinput")] Extension::Input => { return Event::Input( xinput::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "xkb")] Extension::Xkb => { return Event::Xkb( xkb::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "xprint")] Extension::XPrint => { return Event::XPrint( xprint::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } #[cfg(feature = "xv")] Extension::Xv => { return Event::Xv( xv::Event::resolve_wire_event(data.first_event, event).unwrap(), ); } _ => {} } } } x::Event::resolve_wire_event(0, event) .map(Event::X) .unwrap_or_else(|| { // SAFETY the event type is checked above and the function panicked if it was // not a basic event (XCB_GE_GENERIC) let unknown = UnknownEvent::from_raw(event); Event::Unknown(unknown) }) } #[test] fn test_event_send_sync() { fn assert_send() {} fn assert_sync() {} assert_send::(); assert_sync::(); } xcb-1.2.2/src/ext.rs000064400000000000000000000274741046102023000123550ustar 00000000000000use crate::base::{Connection, Reply}; use crate::ffi::{ xcb_connection_t, xcb_extension_t, xcb_get_extension_data, xcb_prefetch_extension_data, }; use crate::x; use std::fmt; use std::mem; /// Refers to a X protocol extension. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Extension { /// The `BIG-REQUESTS` extension. BigRequests, /// The `XCMISC` extension. XcMisc, #[cfg(feature = "composite")] /// The `Composite` extension. /// Available with the `composite` cargo feature. Composite, #[cfg(feature = "damage")] /// The `DAMAGE` extension. /// Available with the `damage` cargo feature. Damage, #[cfg(feature = "dpms")] /// The `DPMS` extension. /// Available with the `dpms` cargo feature. Dpms, #[cfg(feature = "dri2")] /// The `DRI2` extension. /// Available with the `dri2` cargo feature. Dri2, #[cfg(feature = "dri3")] /// The `DRI3` extension. /// Available with the `dri3` cargo feature. Dri3, #[cfg(feature = "ge")] /// The `Generic Event Extension` extension. /// Available with the `ge` cargo feature. GenericEvent, #[cfg(feature = "glx")] /// The `GLX` extension. /// Available with the `glx` cargo feature. Glx, #[cfg(feature = "present")] /// The `Present` extension. /// Available with the `present` cargo feature. Present, #[cfg(feature = "randr")] /// The `RANDR` extension. /// Available with the `randr` cargo feature. RandR, #[cfg(feature = "record")] /// The `RECORD` extension. /// Available with the `record` cargo feature. Record, #[cfg(feature = "render")] /// The `RENDER` extension. /// Available with the `render` cargo feature. Render, #[cfg(feature = "res")] /// The `X-Resource` extension. /// Available with the `res` cargo feature. Res, #[cfg(feature = "screensaver")] /// The `MIT-SCREEN-SAVER` extension. /// Available with the `screensaver` cargo feature. ScreenSaver, #[cfg(feature = "shape")] /// The `SHAPE` extension. /// Available with the `shape` cargo feature. Shape, #[cfg(feature = "shm")] /// The `MIT-SHM` extension. /// Available with the `shm` cargo feature. Shm, #[cfg(feature = "sync")] /// The `SYNC` extension. /// Available with the `sync` cargo feature. Sync, #[cfg(feature = "xevie")] /// The `XEVIE` extension. /// Available with the `xevie` cargo feature. Xevie, #[cfg(feature = "xf86dri")] /// The `XFree86-DRI` extension. /// Available with the `xf86dri` cargo feature. Xf86Dri, #[cfg(feature = "xf86vidmode")] /// The `XFree86-VidModeExtension` extension. /// Available with the `xf86vidmode` cargo feature. Xf86VidMode, #[cfg(feature = "xfixes")] /// The `XFIXES` extension. /// Available with the `xfixes` cargo feature. XFixes, #[cfg(feature = "xinerama")] /// The `XINERAMA` extension. /// Available with the `xinerama` cargo feature. Xinerama, #[cfg(feature = "xinput")] /// The `XInputExtension` extension. /// Available with the `xinput` cargo feature. Input, #[cfg(feature = "xkb")] /// The `XKEYBOARD` extension. /// Available with the `xkb` cargo feature. Xkb, #[cfg(feature = "xprint")] /// The `XpExtension` extension. /// Available with the `xprint` cargo feature. XPrint, #[cfg(feature = "xselinux")] /// The `SELinux` extension. /// Available with the `xselinux` cargo feature. SeLinux, #[cfg(feature = "xtest")] /// The `XTEST` extension. /// Available with the `xtest` cargo feature. Test, #[cfg(feature = "xv")] /// The `XVideo` extension. /// Available with the `xv` cargo feature. Xv, #[cfg(feature = "xvmc")] /// The `XVideo-MotionCompensation` extension. /// Available with the `xvmc` cargo feature. XvMc, } impl Extension { /// Returns the official X-Name of the extension, /// such as `"BIG-REQUESTS"`. fn xname(&self) -> &'static str { match self { Extension::BigRequests => crate::bigreq::XNAME, Extension::XcMisc => crate::xc_misc::XNAME, #[cfg(feature = "composite")] Extension::Composite => crate::composite::XNAME, #[cfg(feature = "damage")] Extension::Damage => crate::damage::XNAME, #[cfg(feature = "dpms")] Extension::Dpms => crate::dpms::XNAME, #[cfg(feature = "dri2")] Extension::Dri2 => crate::dri2::XNAME, #[cfg(feature = "dri3")] Extension::Dri3 => crate::dri3::XNAME, #[cfg(feature = "ge")] Extension::GenericEvent => crate::ge::XNAME, #[cfg(feature = "glx")] Extension::Glx => crate::glx::XNAME, #[cfg(feature = "present")] Extension::Present => crate::present::XNAME, #[cfg(feature = "randr")] Extension::RandR => crate::randr::XNAME, #[cfg(feature = "record")] Extension::Record => crate::record::XNAME, #[cfg(feature = "render")] Extension::Render => crate::render::XNAME, #[cfg(feature = "res")] Extension::Res => crate::res::XNAME, #[cfg(feature = "screensaver")] Extension::ScreenSaver => crate::screensaver::XNAME, #[cfg(feature = "shape")] Extension::Shape => crate::shape::XNAME, #[cfg(feature = "shm")] Extension::Shm => crate::shm::XNAME, #[cfg(feature = "sync")] Extension::Sync => crate::sync::XNAME, #[cfg(feature = "xevie")] Extension::Xevie => crate::xevie::XNAME, #[cfg(feature = "xf86dri")] Extension::Xf86Dri => crate::xf86dri::XNAME, #[cfg(feature = "xf86vidmode")] Extension::Xf86VidMode => crate::xf86vidmode::XNAME, #[cfg(feature = "xfixes")] Extension::XFixes => crate::xfixes::XNAME, #[cfg(feature = "xinerama")] Extension::Xinerama => crate::xinerama::XNAME, #[cfg(feature = "xinput")] Extension::Input => crate::xinput::XNAME, #[cfg(feature = "xkb")] Extension::Xkb => crate::xkb::XNAME, #[cfg(feature = "xprint")] Extension::XPrint => crate::xprint::XNAME, #[cfg(feature = "xselinux")] Extension::SeLinux => crate::xselinux::XNAME, #[cfg(feature = "xtest")] Extension::Test => crate::xtest::XNAME, #[cfg(feature = "xv")] Extension::Xv => crate::xv::XNAME, #[cfg(feature = "xvmc")] Extension::XvMc => crate::xvmc::XNAME, } } } impl fmt::Display for Extension { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.xname().fmt(f) } } /// Extension data as returned by each extensions `get_extension_data`. /// /// See [crate::bigreq::get_extension_data] as example. #[derive(Debug)] pub struct ExtensionData { pub ext: Extension, pub major_opcode: u8, pub first_event: u8, pub first_error: u8, } pub(crate) fn cache_extensions_data( conn: *mut xcb_connection_t, mandatory: &[Extension], optional: &[Extension], ) -> Vec { unsafe { for ext in mandatory { let ext_id = get_extension_id(*ext); xcb_prefetch_extension_data(conn, ext_id as *mut _); } for ext in optional { let ext_id = get_extension_id(*ext); xcb_prefetch_extension_data(conn, ext_id as *mut _); } let mut ext_data = Vec::new(); for ext in mandatory { let ext_id = get_extension_id(*ext); let raw = xcb_get_extension_data(conn, ext_id as *mut _); let reply = x::QueryExtensionReply::from_raw(raw); assert!( reply.present(), "mandatory extension {} is not present on this system", ext ); ext_data.push(ExtensionData { ext: *ext, major_opcode: reply.major_opcode(), first_event: reply.first_event(), first_error: reply.first_error(), }); mem::forget(reply); } for ext in optional { let ext_id = get_extension_id(*ext); let raw = xcb_get_extension_data(conn, ext_id as *mut _); let reply = x::QueryExtensionReply::from_raw(raw); if !reply.present() { mem::forget(reply); continue; } ext_data.push(ExtensionData { ext: *ext, major_opcode: reply.major_opcode(), first_event: reply.first_event(), first_error: reply.first_error(), }); mem::forget(reply); } // we sort by event in reverse order to optimize the event algo ext_data.sort_by(|a, b| b.first_event.cmp(&a.first_event)); ext_data } } unsafe fn get_extension_id(ext: Extension) -> &'static mut xcb_extension_t { match ext { Extension::BigRequests => &mut crate::bigreq::FFI_EXT, Extension::XcMisc => &mut crate::xc_misc::FFI_EXT, #[cfg(feature = "composite")] Extension::Composite => &mut crate::composite::FFI_EXT, #[cfg(feature = "damage")] Extension::Damage => &mut crate::damage::FFI_EXT, #[cfg(feature = "dpms")] Extension::Dpms => &mut crate::dpms::FFI_EXT, #[cfg(feature = "dri2")] Extension::Dri2 => &mut crate::dri2::FFI_EXT, #[cfg(feature = "dri3")] Extension::Dri3 => &mut crate::dri3::FFI_EXT, #[cfg(feature = "ge")] Extension::GenericEvent => &mut crate::ge::FFI_EXT, #[cfg(feature = "glx")] Extension::Glx => &mut crate::glx::FFI_EXT, #[cfg(feature = "present")] Extension::Present => &mut crate::present::FFI_EXT, #[cfg(feature = "randr")] Extension::RandR => &mut crate::randr::FFI_EXT, #[cfg(feature = "record")] Extension::Record => &mut crate::record::FFI_EXT, #[cfg(feature = "render")] Extension::Render => &mut crate::render::FFI_EXT, #[cfg(feature = "res")] Extension::Res => &mut crate::res::FFI_EXT, #[cfg(feature = "screensaver")] Extension::ScreenSaver => &mut crate::screensaver::FFI_EXT, #[cfg(feature = "shape")] Extension::Shape => &mut crate::shape::FFI_EXT, #[cfg(feature = "shm")] Extension::Shm => &mut crate::shm::FFI_EXT, #[cfg(feature = "sync")] Extension::Sync => &mut crate::sync::FFI_EXT, #[cfg(feature = "xevie")] Extension::Xevie => &mut crate::xevie::FFI_EXT, #[cfg(feature = "xf86dri")] Extension::Xf86Dri => &mut crate::xf86dri::FFI_EXT, #[cfg(feature = "xf86vidmode")] Extension::Xf86VidMode => &mut crate::xf86vidmode::FFI_EXT, #[cfg(feature = "xfixes")] Extension::XFixes => &mut crate::xfixes::FFI_EXT, #[cfg(feature = "xinerama")] Extension::Xinerama => &mut crate::xinerama::FFI_EXT, #[cfg(feature = "xinput")] Extension::Input => &mut crate::xinput::FFI_EXT, #[cfg(feature = "xkb")] Extension::Xkb => &mut crate::xkb::FFI_EXT, #[cfg(feature = "xprint")] Extension::XPrint => &mut crate::xprint::FFI_EXT, #[cfg(feature = "xselinux")] Extension::SeLinux => &mut crate::xselinux::FFI_EXT, #[cfg(feature = "xtest")] Extension::Test => &mut crate::xtest::FFI_EXT, #[cfg(feature = "xv")] Extension::Xv => &mut crate::xv::FFI_EXT, #[cfg(feature = "xvmc")] Extension::XvMc => &mut crate::xvmc::FFI_EXT, } } xcb-1.2.2/src/ffi/base.rs000064400000000000000000000143211046102023000132160ustar 00000000000000use crate::AuthInfo; use super::ext::*; use libc::{c_char, c_int, c_uint, c_void}; /// Current protocol version pub const X_PROTOCOL: u32 = 11; /// Current minor version pub const X_PROTOCOL_REVISION: u32 = 0; /// X_TCP_PORT + display number = server port for TCP transport pub const X_TCP_PORT: u32 = 6000; /// xcb connection errors because of socket, pipe and other stream errors. pub const XCB_CONN_ERROR: i32 = 1; /// xcb connection shutdown because of extension not supported pub const XCB_CONN_CLOSED_EXT_NOTSUPPORTED: i32 = 2; /// malloc(), calloc() and realloc() error upon failure, for eg ENOMEM pub const XCB_CONN_CLOSED_MEM_INSUFFICIENT: i32 = 3; /// Connection closed, exceeding request length that server accepts. pub const XCB_CONN_CLOSED_REQ_LEN_EXCEED: i32 = 4; /// Connection closed, error during parsing display string. pub const XCB_CONN_CLOSED_PARSE_ERR: i32 = 5; /// Connection closed because the server does not have a screen matching the display. pub const XCB_CONN_CLOSED_INVALID_SCREEN: i32 = 6; /// Connection closed because some FD passing operation failed pub const XCB_CONN_CLOSED_FDPASSING_FAILED: i32 = 7; /// XCB Connection structure. /// /// A structure that contain all data that XCB needs to communicate with an X server. pub enum xcb_connection_t {} pub(crate) enum xcb_special_event_t {} /// Generic event. /// /// A generic event structure. #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct xcb_generic_event_t { /// Type of the response pub response_type: u8, /// Padding pub pad0: u8, /// Sequence number pub sequence: u16, /// Padding pub pad: [u32; 7], /// full sequence pub full_sequence: u32, } // GE_GENERIC stuff is actually generated in X-proto, but we need it here in FFI form, // so it is just copy pasted from xproto.h /// `response_type` number corresponding to a [xcb_ge_generic_event_t]. pub const XCB_GE_GENERIC: u8 = 35; /// FFI type for the Generic Event Extension. #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct xcb_ge_generic_event_t { pub response_type: u8, pub extension: u8, pub sequence: u16, pub length: u32, pub event_type: u16, pub pad0: [u8; 22], pub full_sequence: u32, } /// Generic error #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct xcb_generic_error_t { pub response_type: u8, pub error_code: u8, pub sequence: u16, pub resource_id: u32, pub minor_code: u16, pub major_code: u8, pub pad0: u8, pub pad: [u32; 5], pub full_sequence: u32, } /// FFI type for a void request cookie. #[derive(Copy, Clone, Debug)] #[repr(C)] pub(crate) struct xcb_void_cookie_t { pub(crate) seq: u32, } /// Container for authorization information. /// A container for authorization information to be sent to the X server #[repr(C)] pub(crate) struct xcb_auth_info_t { /// length of the string name (as returned by strlen) pub namelen: c_int, /// String containing the authentication protocol name, /// such as "MIT-MAGIC-COOKIE-1" or "XDM-AUTHORIZATION-1". pub name: *mut c_char, /// length of the data member pub datalen: c_int, /// data interpreted in a protocol specific manner pub data: *mut c_char, } #[link(name = "xcb")] extern "C" { pub(crate) fn xcb_flush(c: *mut xcb_connection_t) -> c_int; pub(crate) fn xcb_get_maximum_request_length(c: *mut xcb_connection_t) -> u32; pub(crate) fn xcb_prefetch_maximum_request_length(c: *mut xcb_connection_t) -> c_void; pub(crate) fn xcb_wait_for_event(c: *mut xcb_connection_t) -> *mut xcb_generic_event_t; pub(crate) fn xcb_poll_for_event(c: *mut xcb_connection_t) -> *mut xcb_generic_event_t; pub(crate) fn xcb_poll_for_queued_event(c: *mut xcb_connection_t) -> *mut xcb_generic_event_t; pub(crate) fn xcb_poll_for_special_event( c: *mut xcb_connection_t, se: *mut xcb_special_event_t, ) -> *mut xcb_generic_event_t; pub(crate) fn xcb_wait_for_special_event( c: *mut xcb_connection_t, se: *mut xcb_special_event_t, ) -> *mut xcb_generic_event_t; pub(crate) fn xcb_register_for_special_xge( c: *mut xcb_connection_t, ext: *mut xcb_extension_t, eid: u32, stamp: *mut u32, ) -> *mut xcb_special_event_t; pub(crate) fn xcb_unregister_for_special_xge( c: *mut xcb_connection_t, se: *mut xcb_special_event_t, ); pub(crate) fn xcb_request_check( c: *mut xcb_connection_t, cookie: xcb_void_cookie_t, ) -> *mut xcb_generic_error_t; pub(crate) fn xcb_discard_reply(c: *mut xcb_connection_t, sequence: c_uint); pub(crate) fn xcb_discard_reply64(c: *mut xcb_connection_t, sequence: u64); // We trick the result from `*const xcb_query_extension_reply_t` to `*const u8` to be compatible with // `x::QueryExtensionReply::from_raw` pub(crate) fn xcb_get_extension_data( c: *mut xcb_connection_t, ext: *mut xcb_extension_t, ) -> *const u8; pub(crate) fn xcb_prefetch_extension_data(c: *mut xcb_connection_t, ext: *mut xcb_extension_t); // We trick the result from `*const xcb_setup_t` to `*const u8` to be compatible with // `x::Setup::from_data` pub(crate) fn xcb_get_setup(c: *mut xcb_connection_t) -> *const u8; pub(crate) fn xcb_get_file_descriptor(c: *mut xcb_connection_t) -> c_int; pub(crate) fn xcb_connection_has_error(c: *mut xcb_connection_t) -> c_int; pub(crate) fn xcb_connect_to_fd( fd: c_int, auth_info: *mut xcb_auth_info_t, ) -> *mut xcb_connection_t; pub(crate) fn xcb_disconnect(c: *mut xcb_connection_t); pub(crate) fn xcb_parse_display( name: *const c_char, host: *mut *mut c_char, display: *mut c_int, screen: *mut c_int, ) -> c_int; pub(crate) fn xcb_connect( displayname: *const c_char, screenp: *mut c_int, ) -> *mut xcb_connection_t; pub(crate) fn xcb_connect_to_display_with_auth_info( display: *const c_char, auth: *mut xcb_auth_info_t, screen: *mut c_int, ) -> *mut xcb_connection_t; pub(crate) fn xcb_generate_id(c: *mut xcb_connection_t) -> u32; pub(crate) fn xcb_total_read(c: *mut xcb_connection_t) -> u64; pub(crate) fn xcb_total_written(c: *mut xcb_connection_t) -> u64; } xcb-1.2.2/src/ffi/ext.rs000064400000000000000000000246651046102023000131200ustar 00000000000000/* * Copyright (C) 2013 James Miller * Copyright (c) 2016 * Remi Thebault * Thomas Bracht Laumann Jespersen * * Permission is hereby granted, free of charge, to any * person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the * Software without restriction, including without * limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software * is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice * shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ use crate::ffi::base::*; use libc::{c_char, c_int, c_uint, c_void, iovec}; #[derive(Debug, Copy, Clone)] #[repr(C)] pub(crate) struct xcb_extension_t { pub(crate) name: *const c_char, pub(crate) global_id: c_int, } #[repr(C)] pub(crate) struct xcb_protocol_request_t { pub(crate) count: usize, pub(crate) ext: *mut xcb_extension_t, pub(crate) opcode: u8, pub(crate) isvoid: u8, } #[repr(C)] pub(crate) enum xcb_send_request_flags_t { XCB_REQUEST_CHECKED = 1 << 0, XCB_REQUEST_RAW = 1 << 1, XCB_REQUEST_DISCARD_REPLY = 1 << 2, XCB_REQUEST_REPLY_FDS = 1 << 3, } #[link(name = "xcb")] extern "C" { pub(crate) fn xcb_send_request( c: *mut xcb_connection_t, flags: c_int, vector: *mut iovec, request: *const xcb_protocol_request_t, ) -> c_uint; /** * @brief Send a request to the server. * @param c The connection to the X server. * @param flags A combination of flags from the xcb_send_request_flags_t enumeration. * @param vector Data to send; must have two iovecs before start for internal use. * @param request Information about the request to be sent. * @param num_fds Number of additional file descriptors to send to the server * @param fds Additional file descriptors that should be send to the server. * @return The request's sequence number on success, 0 otherwise. * * This function sends a new request to the X server. The data of the request is * given as an array of @c iovecs in the @p vector argument. The length of that * array and the necessary management information are given in the @p request * argument. * * If @p num_fds is non-zero, @p fds points to an array of file descriptors that * will be sent to the X server along with this request. After this function * returns, all file descriptors sent are owned by xcb and will be closed * eventually. * * When this function returns, the request might or might not be sent already. * Use xcb_flush() to make sure that it really was sent. * * Please note that this function is not the preferred way for sending requests. * * Please note that xcb might use index -1 and -2 of the @p vector array internally, * so they must be valid! */ pub(crate) fn xcb_send_request_with_fds( c: *mut xcb_connection_t, flags: c_int, vector: *mut iovec, request: *const xcb_protocol_request_t, num_fds: c_uint, fds: *mut c_int, ) -> c_uint; pub(crate) fn xcb_send_request64( c: *mut xcb_connection_t, flags: c_int, vector: *mut iovec, request: *const xcb_protocol_request_t, ) -> u64; /** * @brief Send a request to the server, with 64-bit sequence number returned. * @param c The connection to the X server. * @param flags A combination of flags from the xcb_send_request_flags_t enumeration. * @param vector Data to send; must have two iovecs before start for internal use. * @param request Information about the request to be sent. * @param num_fds Number of additional file descriptors to send to the server * @param fds Additional file descriptors that should be send to the server. * @return The request's sequence number on success, 0 otherwise. * * This function sends a new request to the X server. The data of the request is * given as an array of @c iovecs in the @p vector argument. The length of that * array and the necessary management information are given in the @p request * argument. * * If @p num_fds is non-zero, @p fds points to an array of file descriptors that * will be sent to the X server along with this request. After this function * returns, all file descriptors sent are owned by xcb and will be closed * eventually. * * When this function returns, the request might or might not be sent already. * Use xcb_flush() to make sure that it really was sent. * * Please note that this function is not the preferred way for sending requests. * It's better to use the generated wrapper functions. * * Please note that xcb might use index -1 and -2 of the @p vector array internally, * so they must be valid! */ pub(crate) fn xcb_send_request_with_fds64( c: *mut xcb_connection_t, flags: c_int, vector: *mut iovec, request: *const xcb_protocol_request_t, num_fds: c_uint, fds: *mut c_int, ) -> u64; /** * @brief Send a file descriptor to the server in the next call to xcb_send_request. * @param c The connection to the X server. * @param fd The file descriptor to send. * * After this function returns, the file descriptor given is owned by xcb and * will be closed eventually. * * @deprecated This function cannot be used in a thread-safe way. Two threads * that run xcb_send_fd(); xcb_send_request(); could mix up their file * descriptors. Instead, xcb_send_request_with_fds() should be used. */ pub(crate) fn xcb_send_fd(c: *mut xcb_connection_t, fd: c_int); /** * @brief Take over the write side of the socket * @param c The connection to the X server. * @param return_socket Callback function that will be called when xcb wants * to use the socket again. * @param closure Argument to the callback function. * @param flags A combination of flags from the xcb_send_request_flags_t enumeration. * @param sent Location to the sequence number of the last sequence request. * Must not be NULL. * @return 1 on success, else 0. * * xcb_take_socket allows external code to ask XCB for permission to * take over the write side of the socket and send raw data with * xcb_writev. xcb_take_socket provides the sequence number of the last * request XCB sent. The caller of xcb_take_socket must supply a * callback which XCB can call when it wants the write side of the * socket back to make a request. This callback synchronizes with the * external socket owner and flushes any output queues if appropriate. * If you are sending requests which won't cause a reply, please note the * comment for xcb_writev which explains some sequence number wrap issues. * * All replies that are generated while the socket is owned externally have * @p flags applied to them. For example, use XCB_REQUEST_CHECK if you don't * want errors to go to xcb's normal error handling, but instead having to be * picked up via xcb_wait_for_reply(), xcb_poll_for_reply() or * xcb_request_check(). */ pub(crate) fn xcb_take_socket( c: *mut xcb_connection_t, return_socket: fn(closure: *mut c_void), closure: *mut c_void, flags: c_int, sent: *mut u64, ) -> c_int; /** * @brief Send raw data to the X server. * @param c The connection to the X server. * @param vector Array of data to be sent. * @param count Number of entries in @p vector. * @param requests Number of requests that are being sent. * @return 1 on success, else 0. * * You must own the write-side of the socket (you've called * xcb_take_socket, and haven't returned from return_socket yet) to call * xcb_writev. Also, the iovec must have at least 1 byte of data in it. * You have to make sure that xcb can detect sequence number wraps correctly. * This means that the first request you send after xcb_take_socket must cause a * reply (e.g. just insert a GetInputFocus request). After every (1 << 16) - 1 * requests without a reply, you have to insert a request which will cause a * reply. You can again use GetInputFocus for this. You do not have to wait for * any of the GetInputFocus replies, but can instead handle them via * xcb_discard_reply(). */ pub(crate) fn xcb_writev( c: *mut xcb_connection_t, vector: *mut iovec, count: c_int, requests: u64, ) -> c_int; pub(crate) fn xcb_wait_for_reply( c: *mut xcb_connection_t, request: c_uint, e: *mut *mut xcb_generic_error_t, ) -> *mut c_void; pub(crate) fn xcb_wait_for_reply64( c: *mut xcb_connection_t, request: u64, e: *mut *mut xcb_generic_error_t, ) -> *mut c_void; pub(crate) fn xcb_poll_for_reply( c: *mut xcb_connection_t, request: c_uint, reply: *mut *mut c_void, e: *mut *mut xcb_generic_error_t, ) -> *mut c_void; pub(crate) fn xcb_poll_for_reply64( c: *mut xcb_connection_t, request: u64, reply: *mut *mut c_void, e: *mut *mut xcb_generic_error_t, ) -> *mut c_void; /** * @brief Don't use this, only needed by the generated code. * @param c The connection to the X server. * @param reply A reply that was received from the server * @param replylen The size of the reply. * @return Pointer to the location where received file descriptors are stored. */ pub(crate) fn xcb_get_reply_fds( c: *mut xcb_connection_t, reply: *mut c_void, replylen: usize, ) -> *mut c_int; } xcb-1.2.2/src/ffi/xlib_xcb.rs000064400000000000000000000043771046102023000141100ustar 00000000000000/* * Copyright (C) 2013 James Miller * Copyright (c) 2016 * Remi Thebault * Thomas Bracht Laumann Jespersen * * Permission is hereby granted, free of charge, to any * person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the * Software without restriction, including without * limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software * is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice * shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #![allow(non_upper_case_globals)] #![allow(non_snake_case)] use crate::ffi::xcb_connection_t; use libc::c_uint; use x11::xlib; /// Type for [XSetEventQueueOwner] owner parameter /// /// This item is behind the `xlib_xcb` cargo feature. pub type XEventQueueOwner = c_uint; /// Xlib owns the event queue /// /// This item is behind the `xlib_xcb` cargo feature. pub const XlibOwnsEventQueue: XEventQueueOwner = 0; /// XCB owns the event queue /// /// This item is behind the `xlib_xcb` cargo feature. pub const XCBOwnsEventQueue: XEventQueueOwner = 1; #[link(name = "X11-xcb")] extern "C" { /// Get an XCB connection from the `xlib::Display`. /// /// This function is behind the `xlib_xcb` cargo feature. pub fn XGetXCBConnection(dpy: *mut xlib::Display) -> *mut xcb_connection_t; /// Set the owner of the X client event queue. /// /// This function is behind the `xlib_xcb` cargo feature. pub fn XSetEventQueueOwner(dpy: *mut xlib::Display, owner: XEventQueueOwner); } xcb-1.2.2/src/lat1_str.rs000064400000000000000000000166671046102023000133100ustar 00000000000000use std::borrow::Cow; use std::fmt; use std::str; /// Error that can produce Latin-1 string operations #[derive(Debug, Copy, Clone)] pub enum Lat1Error { /// Some non-ASCII characters were encountered. /// This error is generated when attempting to borrow /// a latin-1 string out of a UTF-8 string. /// For such borrow, only the ASCII character set is allowed. /// See [Lat1Str::try_from_ascii]. NonAscii, } /// A slice to a Latin-1 (aka. ISO 8859-1) string. /// /// It is usually seen in its borrowed form, `&Lat1Str`. /// Lat1Str contains a slice of bytes and is by definition always /// valid Latin-1. /// /// This type is useful for XCB because strings in the X protocol are /// expected to be Latin-1 encoded. /// Although the X strings are Latin-1, in reality ASCII can be /// expected without too much risk, hence all the ASCII related functions. /// /// This does not account for strings passed as raw bytes /// to [x::ChangeProperty](crate::x::ChangeProperty) (e.g. to set a window title). /// These strings are passed as-is by the X server to the window compositor and /// encoding is implied by the property itself /// (e.g. UTF-8 for `_NET_WM_NAME` aka. window title). pub struct Lat1Str { data: [u8], } impl Lat1Str { /// Returns a reference to a Lat1Str that borrows the passed bytes pub fn from_bytes(bytes: &[u8]) -> &Self { unsafe { &*(bytes as *const [u8] as *const Self) } } /// Returns a reference to a `Lat1Str` that borrows the passed string bytes /// only if `str` is pure ASCII. /// Otherwise, a `Lat1Error::NonAscii` is returned. pub fn try_from_ascii(str: &str) -> Result<&Self, Lat1Error> { if str.is_ascii() { Ok(Self::from_bytes(str.as_bytes())) } else { Err(Lat1Error::NonAscii) } } /// Returns a reference to a `Lat1Str` that borrows the passed string bytes /// only if `str` is pure ASCII. /// /// # Panics /// This function panics if `str` contains non-ASCII chars. pub fn from_ascii(str: &str) -> &Self { Self::try_from_ascii(str).unwrap() } /// Returns a reference to a `Lat1Str` that borrows the passed string bytes. /// /// # Safety /// If `str` contains non-ASCII characters, the returned string will not correspond /// to the passed string (the latin-1 will contain utf-8 encoding). pub unsafe fn from_ascii_unchecked(str: &str) -> &Self { Self::from_bytes(str.as_bytes()) } /// Returns a Latin-1 string built from a UTF-8 string /// /// `Cow::Borrowed` is returned if `str` contains only ASCII, /// otherwise, a conversion from UTF-8 is performed and `Cow::Owned` is returned. pub fn from_utf8(str: &str) -> Cow { if str.is_ascii() { Cow::Borrowed(Lat1Str::from_bytes(str.as_bytes())) } else { Cow::Owned(Lat1String::from_utf8(str)) } } /// Checks whether the slice only contains ASCII characters. pub fn is_ascii(&self) -> bool { self.data.is_ascii() } /// Returns the number of characters in the string. pub fn len(&self) -> usize { self.data.len() } /// Returns the string as slice of bytes. pub fn as_bytes(&self) -> &[u8] { &self.data } /// Returns the string in UTF-8 encoding, only if the string is pure ASCII. /// Otherwise, a `Lat1Error::NonAscii` is returned. pub fn try_as_ascii(&self) -> Result<&str, Lat1Error> { if self.is_ascii() { Ok(unsafe { str::from_utf8_unchecked(&self.data) }) } else { Err(Lat1Error::NonAscii) } } /// Returns the string in UTF-8 encoding, only if the string is pure ASCII. /// /// # Panics /// This function panics if the string contains non-ASCII chars. pub fn as_ascii(&self) -> &str { self.try_as_ascii().unwrap() } /// Returns the string in UTF-8 encoding. /// /// # Safety /// If the string contains non-ASCII characters, the returned string will be /// invalid UTF-8. pub unsafe fn as_ascii_unchecked(&self) -> &str { str::from_utf8_unchecked(&self.data) } /// Returns the string converted to UTF-8. /// /// `Cow::Borrowed` is returned if the string is pure ASCII, /// otherwise a conversion to UTF-8 is performed and `Cow::Owned` is returned. pub fn to_utf8(&self) -> Cow { if self.is_ascii() { Cow::Borrowed(unsafe { self.as_ascii_unchecked() }) } else { Cow::Owned(self.data.iter().map(|c| *c as char).collect()) } } } impl std::borrow::ToOwned for Lat1Str { type Owned = Lat1String; fn to_owned(&self) -> Self::Owned { Lat1String { data: self.as_bytes().to_vec(), } } } impl fmt::Display for Lat1Str { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = self.to_utf8(); f.write_str(&s) } } impl fmt::Debug for Lat1Str { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = self.to_utf8(); f.write_fmt(format_args!("Lat1(\"{}\")", s)) } } /// A struct owning a Latin-1 (aka. ISO 8859-1) string. /// /// See [Lat1Str] for details. #[derive(Clone)] pub struct Lat1String { data: Vec, } impl Lat1String { /// Construct a [Lat1String] from a slice of bytes. pub fn from_bytes(bytes: &[u8]) -> Self { Lat1String { data: bytes.to_vec(), } } /// Construct a [Lat1String] from UTF-8 (a conversion to Latin-1 is performed). pub fn from_utf8(str: &str) -> Self { Lat1String { data: str.chars().map(|c| c as u8).collect(), } } } impl std::ops::Deref for Lat1String { type Target = Lat1Str; fn deref(&self) -> &Self::Target { Lat1Str::from_bytes(self.data.as_slice()) } } impl std::borrow::Borrow for Lat1String { fn borrow(&self) -> &Lat1Str { Lat1Str::from_bytes(self.data.as_slice()) } } impl fmt::Display for Lat1String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = self.to_utf8(); f.write_str(&s) } } impl fmt::Debug for Lat1String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = self.to_utf8(); f.write_fmt(format_args!("Lat1(\"{}\")", s)) } } #[derive(Copy, Clone)] /// Latin-1 (aka. ISO 8859-1) of fixed size pub struct Lat1StrF { data: [u8; N], } impl Lat1StrF { pub fn from_bytes(bytes: [u8; N]) -> Self { Self { data: bytes } } } impl std::ops::Deref for Lat1StrF { type Target = Lat1Str; fn deref(&self) -> &Self::Target { Lat1Str::from_bytes(self.data.as_slice()) } } impl fmt::Display for Lat1StrF { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = self.to_utf8(); f.write_str(&s) } } impl fmt::Debug for Lat1StrF { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = self.to_utf8(); f.write_fmt(format_args!("Lat1(\"{}\")", s)) } } #[test] fn test_latin_str() { let utf8 = "Mon frère est là."; let latin1: &[u8] = &[ 0x4D, 0x6F, 0x6E, 0x20, 0x66, 0x72, 0xE8, 0x72, 0x65, 0x20, 0x65, 0x73, 0x74, 0x20, 0x6C, 0xE0, 0x2E, ]; let ls = Lat1String::from_utf8(utf8); assert_eq!(ls.as_bytes(), latin1); let ls = Lat1Str::from_bytes(latin1); assert_eq!(ls.to_utf8(), utf8); } xcb-1.2.2/src/lib.rs000064400000000000000000000622301046102023000123100ustar 00000000000000/* * Copyright (C) 2013 James Miller * Copyright (c) 2016 * Remi Thebault * Thomas Bracht Laumann Jespersen * Copyright (c) 2017-2021 Remi Thebault * * Permission is hereby granted, free of charge, to any * person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the * Software without restriction, including without * limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software * is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice * shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #![allow(dead_code)] #![allow(unused_imports)] #![allow(unused_parens)] #![allow(clippy::len_without_is_empty)] #![allow(clippy::diverging_sub_expression)] //! Rust bindings to the XCB library. //! //! The X protocol C-language Binding (XCB - ) is //! a replacement for Xlib featuring a small footprint, latency hiding, direct //! access to the protocol, improved threading support, and extensibility. //! //! The communication is established with the X server by the creation of a //! [Connection] object. //! //! A client communicates with the server by sending requests. There are 2 types //! of requests: //! //! - void requests: requests that do not expect an answer //! (e.g. [x::ChangeProperty]) //! - non-void requests: requests that need a `Reply` (e.g. [x::GetProperty]) //! //! Requests are passed to the server by filling a request structure e.g. //! [x::CreateWindow] and passing it to [Connection::send_request]. //! //! The server can also communicate with clients by sending `Event`s. //! The client listens to events with calls such as [Connection::wait_for_event] //! (blocking) or [Connection::poll_for_event] (non-blocking). //! //! The [x] module contains definitions of the core X protocol. //! Each extension is defined in its own module such as [xkb] or [render], and //! is activated by a cargo feature of the same name. //! //! # Example //! //! Here is a walk-through of a simple `xcb` client. //! ```no_run //! // we import the necessary modules (only the core X module in this application). //! use xcb::{x}; //! // we need to import the `Xid` trait for the `resource_id` call down there. //! use xcb::{Xid}; //! //! // Many xcb functions return a `xcb::Result` or compatible result. //! fn main() -> xcb::Result<()> { //! // Connect to the X server. //! let (conn, screen_num) = xcb::Connection::connect(None)?; //! //! // Fetch the `x::Setup` and get the main `x::Screen` object. //! let setup = conn.get_setup(); //! let screen = setup.roots().nth(screen_num as usize).unwrap(); //! //! // Generate an `Xid` for the client window. //! // The type inference is needed here. //! let window: x::Window = conn.generate_id(); //! //! // We can now create a window. For this we pass a `Request` //! // object to the `send_request_checked` method. The method //! // returns a cookie that will be used to check for success. //! let cookie = conn.send_request_checked(&x::CreateWindow { //! depth: x::COPY_FROM_PARENT as u8, //! wid: window, //! parent: screen.root(), //! x: 0, //! y: 0, //! width: 150, //! height: 150, //! border_width: 0, //! class: x::WindowClass::InputOutput, //! visual: screen.root_visual(), //! // this list must be in same order than `Cw` enum order //! value_list: &[ //! x::Cw::BackPixel(screen.white_pixel()), //! x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS) //! ], //! }); //! // We now check if the window creation worked. //! // A cookie can't be cloned; it is moved to the function. //! conn.check_request(cookie)?; //! //! // Let's change the window title //! let cookie = conn.send_request_checked(&x::ChangeProperty { //! mode: x::PropMode::Replace, //! window, //! property: x::ATOM_WM_NAME, //! r#type: x::ATOM_STRING, //! data: b"My XCB Window", //! }); //! // And check for success again //! conn.check_request(cookie)?; //! //! // We now show ("map" in X terminology) the window. //! // This time we do not check for success, so we discard the cookie. //! conn.send_request(&x::MapWindow { //! window, //! }); //! //! // We need a few atoms for our application. //! // We send a few requests in a row and wait for the replies after. //! let (wm_protocols, wm_del_window, wm_state, wm_state_maxv, wm_state_maxh) = { //! let cookies = ( //! conn.send_request(&x::InternAtom { //! only_if_exists: true, //! name: b"WM_PROTOCOLS", //! }), //! conn.send_request(&x::InternAtom { //! only_if_exists: true, //! name: b"WM_DELETE_WINDOW", //! }), //! conn.send_request(&x::InternAtom { //! only_if_exists: true, //! name: b"_NET_WM_STATE", //! }), //! conn.send_request(&x::InternAtom { //! only_if_exists: true, //! name: b"_NET_WM_STATE_MAXIMIZED_VERT", //! }), //! conn.send_request(&x::InternAtom { //! only_if_exists: true, //! name: b"_NET_WM_STATE_MAXIMIZED_HORZ", //! }), //! ); //! ( //! conn.wait_for_reply(cookies.0)?.atom(), //! conn.wait_for_reply(cookies.1)?.atom(), //! conn.wait_for_reply(cookies.2)?.atom(), //! conn.wait_for_reply(cookies.3)?.atom(), //! conn.wait_for_reply(cookies.4)?.atom(), //! ) //! }; //! //! // We now activate the window close event by sending the following request. //! // If we don't do this we can still close the window by clicking on the "x" button, //! // but the event loop is notified through a connection shutdown error. //! conn.check_request(conn.send_request_checked(&x::ChangeProperty { //! mode: x::PropMode::Replace, //! window, //! property: wm_protocols, //! r#type: x::ATOM_ATOM, //! data: &[wm_del_window], //! }))?; //! //! // Previous request was checked, so a flush is not necessary in this case. //! // Otherwise, here is how to perform a connection flush. //! conn.flush()?; //! //! let mut maximized = false; //! //! // We enter the main event loop //! loop { //! match conn.wait_for_event()? { //! xcb::Event::X(x::Event::KeyPress(ev)) => { //! if ev.detail() == 0x3a { //! // The M key was pressed //! // (M only on qwerty keyboards. Keymap support is done //! // with the `xkb` extension and the `xkbcommon-rs` crate) //! //! // We toggle maximized state, for this we send a message //! // by building a `x::ClientMessageEvent` with the proper //! // atoms and send it to the server. //! //! let data = x::ClientMessageData::Data32([ //! if maximized { 0 } else { 1 }, //! wm_state_maxv.resource_id(), //! wm_state_maxh.resource_id(), //! 0, //! 0, //! ]); //! let event = x::ClientMessageEvent::new(window, wm_state, data); //! let cookie = conn.send_request_checked(&x::SendEvent { //! propagate: false, //! destination: x::SendEventDest::Window(screen.root()), //! event_mask: x::EventMask::STRUCTURE_NOTIFY, //! event: &event, //! }); //! conn.check_request(cookie)?; //! //! // Same as before, if we don't check for error, we have to flush //! // the connection. //! // conn.flush()?; //! //! maximized = !maximized; //! } else if ev.detail() == 0x18 { //! // Q (on qwerty) //! //! // We exit the event loop (and the program) //! break Ok(()); //! } //! } //! xcb::Event::X(x::Event::ClientMessage(ev)) => { //! // We have received a message from the server //! if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { //! if atom == wm_del_window.resource_id() { //! // The received atom is "WM_DELETE_WINDOW". //! // We can check here if the user needs to save before //! // exit, or in our case, exit right away. //! break Ok(()); //! } //! } //! } //! _ => {} //! } //! } //! } //! ``` //! //! # Cargo features //! //! The following Cargo features are available //! //! ## `xlib_xcb` //! //! This feature activates the use of `xlib::Display` to connect to XCB, therefore making //! available both Xlib and XCB functions to the same connection. //! While XCB is sufficient to handle all communication with the X server, some things can //! still only be done by Xlib. E.g. hardware initialization for OpenGL is done by Xlib only. //! //! ## `debug_atom_names` //! //! When this feature is activated, the `fmt::Debug` implementation for `x::Atom` will print //! out the name of the atom in addition to its value. //! //! E.g. the feature would turn `Atom { res_id: 303 }` into `Atom("Abs Pressure" ; 303)`. //! //! This can be useful in situations where you are not sure which atom you have to intern //! in order to look up some data in a reply. //! //! It should be noted that the feature sets global variable to have access to //! the connection in the `fmt::Debug` implementation, and that the `Debug` print //! have side effects (`x::GetAtomName` requests) which can sometimes not be desirable. //! The feature should therefore only be activated when needed. //! //! ## Extension features //! //! The following X extensions are activated by a cargo feature: //! //! | Extension name | Cargo feature | //! |-------------------------------|---------------| //! | `Composite` | `composite` | //! | `DAMAGE` | `damage` | //! | `DPMS` | `dpms` | //! | `DRI2` | `dri2` | //! | `DRI3` | `dri3` | //! | `Generic Event Extension` | `ge` | //! | `GLX` | `glx` | //! | `Present` | `present` | //! | `RANDR` | `randr` | //! | `RECORD` | `record` | //! | `RENDER` | `render` | //! | `X-Resource` | `res` | //! | `MIT-SCREEN-SAVER` | `screensaver` | //! | `SHAPE` | `shape` | //! | `MIT-SHM` | `shm` | //! | `SYNC` | `sync` | //! | `XEVIE` | `xevie` | //! | `XFree86-DRI` | `xf86dri` | //! | `XFree86-VidModeExtension` | `xf86vidmode` | //! | `XFIXES` | `xfixes` | //! | `XINERAMA` | `xinerama` | //! | `XInputExtension` | `xinput` | //! | `XKEYBOARD` | `xkb` | //! | `XpExtension` | `xprint` | //! | `SELinux` | `xselinux` | //! | `TEST` | `xtest` | //! | `XVideo` | `xv` | //! | `XVideo-MotionCompensation` | `xvmc` | mod base; mod error; mod event; mod ext; mod lat1_str; pub use base::*; pub use error::*; pub use event::*; pub use ext::*; pub use lat1_str::*; pub mod x { //! The core X protocol definitions pub use super::xproto::*; } pub mod ffi { //! Module for Foreign Function Interface bindings. #![allow(non_camel_case_types)] #![allow(improper_ctypes)] pub(crate) mod base; pub(crate) mod ext; #[cfg(feature = "xlib_xcb")] pub(crate) mod xlib_xcb; pub use base::*; pub use ext::*; #[cfg(feature = "xlib_xcb")] pub use xlib_xcb::*; } #[cfg(test)] mod test; mod xproto { #![allow(unused_variables)] #![allow(clippy::unit_arg)] #![allow(clippy::new_ret_no_self)] #![allow(clippy::too_many_arguments)] /// `COPY_FROM_PARENT` can be used for many `CreateWindow` fields pub const COPY_FROM_PARENT: u32 = 0; /// `CURRENT_TIME` can be used in most requests that take a `Timestamp` pub const CURRENT_TIME: Timestamp = 0; /// `NO_SYMBOL` fills in unused entries in `Keysym` tables pub const NO_SYMBOL: Keysym = 0; /// `GRAB_ANY` can be used in various requests such as `GrabKey`, `UngrabKey`, `xinput::GrabDeviceKey`... pub const GRAB_ANY: Keycode = 0; pub const ATOM_ANY: Atom = Atom { res_id: 0 }; /// Trait for element in a property list /// /// In events (e.g. `GetProperty::value`), it allows to assert that the format /// correspond to the type cast and therefore to do the cast safely at runtime. /// /// In request (e.g. `ChangeProperty::data`), it allows to infer the format value /// from the type of passed data. pub trait PropEl { const FORMAT: u8; } impl PropEl for u8 { const FORMAT: u8 = 8; } impl PropEl for u16 { const FORMAT: u8 = 16; } impl PropEl for u32 { const FORMAT: u8 = 32; } impl PropEl for Atom { const FORMAT: u8 = 32; } impl PropEl for Window { // _NET_CLIENT_LIST returns a list of windows const FORMAT: u8 = 32; } include!(concat!(env!("OUT_DIR"), "/xproto.rs")); } /// An helper macro that generate a struct of atoms. /// /// The struct provide a constructor `intern_all` that takes a `Connection` as parameter, /// interns all the atoms and return `xcb::Result<[struct name]>`. /// `intern_all` takes advantage of XCB asynchronous design by sending all the /// [`x::InternAtom`] requests before starting to wait for the first reply. /// Fields that refer to atoms not existing in the server are set to `x::ATOM_NONE` /// (i.e. `only_if_exists` is always set to `true`). /// /// Both the struct and each field can receive visibility attributes. /// /// # Example /// ```no_run /// # use xcb::x; /// xcb::atoms_struct! { /// #[derive(Copy, Clone, Debug)] /// pub(crate) struct Atoms { /// pub wm_protocols => b"WM_PROTOCOLS", /// pub wm_del_window => b"WM_DELETE_WINDOW", /// /// Supported EWMH hints /// pub net_supported => b"_NET_SUPPORTED", /// /// // You can also explicitly set the `only_if_exists` argument when interning /// // each atom with the following syntax (the default is `true`): /// pub custom_atom => b"MY_CUSTOM_ATOM" only_if_exists = false, /// } /// } /// /// fn main() -> xcb::Result<()> { /// # let (conn, screen_num) = xcb::Connection::connect(None)?; /// # let window = conn.generate_id(); /// // ... /// let atoms = Atoms::intern_all(&conn)?; /// /// conn.check_request(conn.send_request_checked(&x::ChangeProperty { /// mode: x::PropMode::Replace, /// window, /// property: atoms.wm_protocols, /// r#type: x::ATOM_ATOM, /// data: &[atoms.wm_del_window], /// }))?; /// // ... /// # Ok(()) /// } /// ``` #[macro_export] macro_rules! atoms_struct { ( $(#[$outer:meta])* $vis:vis struct $Atoms:ident { $( $(#[$fmeta:meta])* $fvis:vis $field:ident => $name:tt $( only_if_exists = $only_if_exists:expr)?, )* } ) => { $(#[$outer])* $vis struct $Atoms { $($(#[$fmeta])* $fvis $field: xcb::x::Atom,)* } impl $Atoms { #[allow(dead_code)] pub fn intern_all(conn: &xcb::Connection) -> xcb::Result<$Atoms> { $( #[allow(unused_assignments)] let mut only_if_exists = true; $( only_if_exists = $only_if_exists; )? let $field = conn.send_request(&xcb::x::InternAtom { only_if_exists, name: $name, }); )* $( let $field = conn.wait_for_reply($field)?.atom(); )* Ok($Atoms { $($field,)* }) } } }; } pub mod bigreq { //! The `BIG-REQUESTS` extension. include!(concat!(env!("OUT_DIR"), "/bigreq.rs")); } pub mod xc_misc { //! The `XC-MISC` extension. include!(concat!(env!("OUT_DIR"), "/xc_misc.rs")); } #[cfg(feature = "composite")] pub mod composite { //! The `Composite` X extension. //! //! Accessible with the `composite` cargo feature. include!(concat!(env!("OUT_DIR"), "/composite.rs")); } #[cfg(feature = "damage")] pub mod damage { //! The `DAMAGE` X extension. //! //! Accessible with the `damage` cargo feature. include!(concat!(env!("OUT_DIR"), "/damage.rs")); } #[cfg(feature = "dpms")] pub mod dpms { //! The `DPMS` X extension. //! //! Accessible with the `dpms` cargo feature. include!(concat!(env!("OUT_DIR"), "/dpms.rs")); } #[cfg(feature = "dri2")] pub mod dri2 { //! The `DRI2` X extension. //! //! Accessible with the `dri2` cargo feature. #![allow(clippy::too_many_arguments)] include!(concat!(env!("OUT_DIR"), "/dri2.rs")); } #[cfg(feature = "dri3")] pub mod dri3 { //! The `DRI3` X extension. //! //! Accessible with the `dri3` cargo feature. include!(concat!(env!("OUT_DIR"), "/dri3.rs")); } #[cfg(feature = "ge")] pub mod ge { //! The `Generic Event Extension` X extension. //! //! Accessible with the `ge` cargo feature. include!(concat!(env!("OUT_DIR"), "/ge.rs")); } #[cfg(feature = "glx")] pub mod glx { //! The `GLX` X extension. //! //! Accessible with the `glx` cargo feature. #![allow(clippy::too_many_arguments)] include!(concat!(env!("OUT_DIR"), "/glx.rs")); } #[cfg(feature = "xinput")] pub mod xinput { //! The `XInputExtension` X extension. //! //! Accessible with the `xinput` cargo feature. #![allow(unused_variables)] #![allow(unused_mut)] #![allow(clippy::unit_arg)] #![allow(clippy::too_many_arguments)] #[derive(Copy, Clone, Debug)] pub enum Device { All, AllMaster, Id(u16), } impl Device { pub fn from_id(id: u16) -> Self { match id { 0 => Device::All, 1 => Device::AllMaster, id => Device::Id(id), } } pub fn id(&self) -> u16 { match self { Device::All => 0, Device::AllMaster => 1, Device::Id(id) => *id, } } } impl WiredOut for Device { fn wire_len(&self) -> usize { 2 } fn serialize(&self, wire_buf: &mut [u8]) -> usize { assert!(wire_buf.len() >= 2); unsafe { *(wire_buf.as_mut_ptr() as *mut u16) = self.id(); } 2 } } impl WiredIn for Device { type Params = (); unsafe fn compute_wire_len(_ptr: *const u8, _params: ()) -> usize { 2 } unsafe fn unserialize(ptr: *const u8, params: Self::Params, offset: &mut usize) -> Self { *offset = 2; let id = *(ptr as *const u16); Device::from_id(id) } } include!(concat!(env!("OUT_DIR"), "/xinput.rs")); } #[cfg(feature = "present")] pub mod present { //! The `Present` X extension. //! //! Accessible with the `present` cargo feature. #![allow(clippy::unit_arg)] include!(concat!(env!("OUT_DIR"), "/present.rs")); } #[cfg(feature = "randr")] pub mod randr { //! The `RANDR` X extension. //! //! Accessible with the `randr` cargo feature. #![allow(clippy::unit_arg)] #![allow(clippy::too_many_arguments)] include!(concat!(env!("OUT_DIR"), "/randr.rs")); } #[cfg(feature = "record")] pub mod record { //! The `RECORD` X extension. //! //! Accessible with the `record` cargo feature. #![allow(clippy::unit_arg)] #![allow(clippy::too_many_arguments)] include!(concat!(env!("OUT_DIR"), "/record.rs")); } #[cfg(feature = "render")] pub mod render { //! The `RENDER` X extension. //! //! Accessible with the `render` cargo feature. #![allow(unused_variables)] #![allow(clippy::unit_arg)] include!(concat!(env!("OUT_DIR"), "/render.rs")); } #[cfg(feature = "res")] pub mod res { //! The `X-Resource` X extension. //! //! Accessible with the `res` cargo feature. #![allow(unused_variables)] #![allow(clippy::unit_arg)] include!(concat!(env!("OUT_DIR"), "/res.rs")); } #[cfg(feature = "screensaver")] pub mod screensaver { //! The `MIT-SCREEN-SAVER` X extension. //! //! Accessible with the `screensaver` cargo feature. #![allow(unused_variables)] include!(concat!(env!("OUT_DIR"), "/screensaver.rs")); } #[cfg(feature = "xselinux")] pub mod xselinux { //! The `SELinux` X extension. //! //! Accessible with the `xselinux` cargo feature. #![allow(clippy::unit_arg)] include!(concat!(env!("OUT_DIR"), "/xselinux.rs")); } #[cfg(feature = "shape")] pub mod shape { //! The `SHAPE` X extension. //! //! Accessible with the `shape` cargo feature. #![allow(clippy::too_many_arguments)] include!(concat!(env!("OUT_DIR"), "/shape.rs")); } #[cfg(feature = "shm")] pub mod shm { //! The `MIT-SHM` X extension. //! //! Accessible with the `shm` cargo feature. include!(concat!(env!("OUT_DIR"), "/shm.rs")); } #[cfg(feature = "sync")] pub mod sync { //! The `SYNC` X extension. //! //! Accessible with the `sync` cargo feature. #![allow(unused_variables)] #![allow(clippy::unit_arg)] #![allow(clippy::too_many_arguments)] include!(concat!(env!("OUT_DIR"), "/sync.rs")); } #[cfg(feature = "xtest")] pub mod xtest { //! The `XTEST` X extension. //! //! Accessible with the `xtest` cargo feature. include!(concat!(env!("OUT_DIR"), "/xtest.rs")); } #[cfg(feature = "xprint")] pub mod xprint { //! The `XpExtension` X extension. //! //! Accessible with the `xprint` cargo feature. #![allow(clippy::unit_arg)] include!(concat!(env!("OUT_DIR"), "/xprint.rs")); } #[cfg(feature = "xevie")] pub mod xevie { //! The `XEVIE` X extension. //! //! Accessible with the `xevie` cargo feature. include!(concat!(env!("OUT_DIR"), "/xevie.rs")); } #[cfg(feature = "xf86dri")] pub mod xf86dri { //! The `XFree86-DRI` X extension. //! //! Accessible with the `xf86dri` cargo feature. include!(concat!(env!("OUT_DIR"), "/xf86dri.rs")); } #[cfg(feature = "xf86vidmode")] pub mod xf86vidmode { //! The `XFree86-VidModeExtension` X extension. //! //! Accessible with the `xf86vidmode` cargo feature. #![allow(clippy::too_many_arguments)] include!(concat!(env!("OUT_DIR"), "/xf86vidmode.rs")); } #[cfg(feature = "xfixes")] pub mod xfixes { //! The `XFIXES` X extension. //! //! Accessible with the `xfixes` cargo feature. include!(concat!(env!("OUT_DIR"), "/xfixes.rs")); } #[cfg(feature = "xinerama")] pub mod xinerama { //! The `XINERAMA` X extension. //! //! Accessible with the `xinerama` cargo feature. include!(concat!(env!("OUT_DIR"), "/xinerama.rs")); } #[cfg(feature = "xkb")] pub mod xkb { //! The `XKEYBOARD` X extension. //! //! Accessible with the `xkb` cargo feature. #![allow(unused_variables)] #![allow(clippy::let_unit_value)] #![allow(clippy::unit_arg)] #![allow(clippy::too_many_arguments)] include!(concat!(env!("OUT_DIR"), "/xkb.rs")); } #[cfg(feature = "xv")] pub mod xv { //! The `XVideo` X extension. //! //! Accessible with the `xv` cargo feature. #![allow(clippy::unit_arg)] #![allow(clippy::too_many_arguments)] include!(concat!(env!("OUT_DIR"), "/xv.rs")); } #[cfg(feature = "xvmc")] pub mod xvmc { //! The `XVideo-MotionCompensation` X extension. //! //! Accessible with the `xvmc` cargo feature. include!(concat!(env!("OUT_DIR"), "/xvmc.rs")); } xcb-1.2.2/src/test.rs000064400000000000000000000142411046102023000125200ustar 00000000000000use crate::base::{self, BaseEvent, WiredIn, WiredOut, XidNew}; use crate::x::{self}; fn visual_data(bits_per_rgb_value: u8) -> Vec { vec![ 0x50, 0x40, 0x30, 0x20, // visual_id 3, // visual class bits_per_rgb_value, // bits_per_rgb_value 0x50, 0x40, // colormap_entries 0xff, 0x00, 0x00, 0x00, // red_mask 0x00, 0xff, 0x00, 0x00, // green_mask 0x00, 0x00, 0xff, 0x00, // blue_mask 0x00, 0x00, 0x00, 0x00, // pad ] } fn depth_data(visual_bprv: &[u8], extra_garbage: usize) -> Vec { let data: &[u8] = &[ 16, // depth 0x00, // pad 0x02, 0x00, // visuals_len 0x00, 0x00, 0x00, 0x00, // pad ]; let mut vec = Vec::from(data); let visuals_data = visual_bprv.iter().map(|bprv| visual_data(*bprv)); for mut vd in visuals_data { vec.append(&mut vd); } // append garbage for g in 0..extra_garbage { vec.push(((g ^ extra_garbage) & 0xff) as u8); } vec } #[test] #[cfg(target_endian = "little")] fn test_fixbuf_from_data() { let data = visual_data(24); let vt = unsafe { x::Visualtype::from_data(&data) }; assert_eq!(vt.visual_id(), 0x20304050); assert_eq!(vt.class(), x::VisualClass::PseudoColor); assert_eq!(vt.bits_per_rgb_value(), 24); assert_eq!(vt.colormap_entries(), 0x4050); assert_eq!(vt.red_mask(), 0x000000ff); assert_eq!(vt.green_mask(), 0x0000ff00); assert_eq!(vt.blue_mask(), 0x00ff0000); } #[test] #[cfg(target_endian = "little")] fn test_dynbuf_from_data() { let mut data = depth_data(&[32, 24], 4); assert_eq!(data.len(), 60); assert_eq!( unsafe { <&x::Depth as WiredIn>::compute_wire_len(data.as_ptr(), ()) }, 56 ); { let depth = unsafe { x::Depth::from_data(&data[0..56]) }; assert_eq!(depth.depth(), 16); let visuals = depth.visuals(); assert_eq!(visuals.len(), 2); let vt = visuals[0]; assert_eq!(vt.visual_id(), 0x20304050); assert_eq!(vt.class(), x::VisualClass::PseudoColor); assert_eq!(vt.bits_per_rgb_value(), 32); assert_eq!(vt.colormap_entries(), 0x4050); assert_eq!(vt.red_mask(), 0x000000ff); assert_eq!(vt.green_mask(), 0x0000ff00); assert_eq!(vt.blue_mask(), 0x00ff0000); let vt = visuals[1]; assert_eq!(vt.visual_id(), 0x20304050); assert_eq!(vt.class(), x::VisualClass::PseudoColor); assert_eq!(vt.bits_per_rgb_value(), 24); assert_eq!(vt.colormap_entries(), 0x4050); assert_eq!(vt.red_mask(), 0x000000ff); assert_eq!(vt.green_mask(), 0x0000ff00); assert_eq!(vt.blue_mask(), 0x00ff0000); } // pop garbage data.pop(); data.pop(); data.pop(); data.pop(); let depth = unsafe { x::DepthBuf::from_data(data) }; assert_eq!(depth.depth(), 16); let visuals = depth.visuals(); assert_eq!(visuals.len(), 2); let vt = visuals[0]; assert_eq!(vt.visual_id(), 0x20304050); assert_eq!(vt.class(), x::VisualClass::PseudoColor); assert_eq!(vt.bits_per_rgb_value(), 32); assert_eq!(vt.colormap_entries(), 0x4050); assert_eq!(vt.red_mask(), 0x000000ff); assert_eq!(vt.green_mask(), 0x0000ff00); assert_eq!(vt.blue_mask(), 0x00ff0000); let vt = visuals[1]; assert_eq!(vt.visual_id(), 0x20304050); assert_eq!(vt.class(), x::VisualClass::PseudoColor); assert_eq!(vt.bits_per_rgb_value(), 24); assert_eq!(vt.colormap_entries(), 0x4050); assert_eq!(vt.red_mask(), 0x000000ff); assert_eq!(vt.green_mask(), 0x0000ff00); assert_eq!(vt.blue_mask(), 0x00ff0000); } #[test] #[should_panic] #[cfg(target_endian = "little")] fn test_dynbuf_from_data_panic() { let data = depth_data(&[32, 24], 4); unsafe { x::DepthBuf::from_data(data) }; } #[test] fn test_cw_is_sorted_distinct() { assert!(x::Cw::is_sorted_distinct(&[ x::Cw::BackPixel(4), x::Cw::BitGravity(x::Gravity::West), x::Cw::WinGravity(x::Gravity::West), ])); assert!(!x::Cw::is_sorted_distinct(&[ x::Cw::BackPixel(4), x::Cw::WinGravity(x::Gravity::West), x::Cw::BitGravity(x::Gravity::West), ])); assert!(!x::Cw::is_sorted_distinct(&[ x::Cw::BackPixel(4), x::Cw::BitGravity(x::Gravity::West), x::Cw::BitGravity(x::Gravity::West), ])); assert!(!x::Cw::is_sorted_distinct(&[ x::Cw::BackPixel(4), x::Cw::BitGravity(x::Gravity::West), x::Cw::BitGravity(x::Gravity::Center), ])); } #[test] fn test_cw_cmp() { assert!(x::Cw::BorderPixel(12) < x::Cw::BorderPixel(13)); assert!(x::Cw::BorderPixel(13) > x::Cw::BorderPixel(12)); assert!(x::Cw::BorderPixel(12) == x::Cw::BorderPixel(12)); assert!(x::Cw::BorderPixel(12) > x::Cw::BackPixel(13)); } #[test] fn atom_hashmap() { use std::collections::HashMap; let map = { let mut map = HashMap::new(); map.insert(x::ATOM_ATOM, "Atom"); map.insert(x::ATOM_CURSOR, "Cursor"); map.insert(x::ATOM_PIXMAP, "Pixmap"); map.insert(x::ATOM_STRING, "String"); map.insert(x::ATOM_WINDOW, "Window"); map }; assert_eq!(map.get(&x::ATOM_ATOM), Some(&"Atom")); assert_eq!(map.get(&x::ATOM_CURSOR), Some(&"Cursor")); assert_eq!(map.get(&x::ATOM_PIXMAP), Some(&"Pixmap")); assert_eq!(map.get(&x::ATOM_STRING), Some(&"String")); assert_eq!(map.get(&x::ATOM_WINDOW), Some(&"Window")); } #[test] fn test_event_new() { let time = x::CURRENT_TIME; let requestor = unsafe { x::Window::new(12) }; let selection = x::ATOM_STRING; let target = x::ATOM_FAMILY_NAME; let property = x::ATOM_RGB_COLOR_MAP; let ev = x::SelectionNotifyEvent::new(time, requestor, selection, target, property); assert_eq!(ev.response_type(), x::SelectionNotifyEvent::NUMBER as u8); assert_eq!(ev.sequence(), 0u16); assert_eq!(ev.time(), time); assert_eq!(ev.requestor(), requestor); assert_eq!(ev.selection(), selection); assert_eq!(ev.target(), target); assert_eq!(ev.property(), property); } xcb-1.2.2/xml/bigreq.xml000064400000000000000000000032701046102023000131770ustar 00000000000000 xcb-1.2.2/xml/composite.xml000064400000000000000000000071551046102023000137360ustar 00000000000000 xproto xfixes 0 1 xcb-1.2.2/xml/damage.xml000064400000000000000000000066601046102023000131520ustar 00000000000000 xproto xfixes 0 1 2 3 xcb-1.2.2/xml/dpms.xml000064400000000000000000000065271046102023000127010ustar 00000000000000 0 1 2 3 xcb-1.2.2/xml/dri2.xml000064400000000000000000000251331046102023000125700ustar 00000000000000 xproto 0 1 2 3 4 5 6 7 8 9 10 0 1 1 2 3 driver_name_length driver_name_length 3 3 driver_name_length device_name_length count count xcb-1.2.2/xml/dri3.xml000064400000000000000000000141251046102023000125700ustar 00000000000000 xproto num_window_modifiers num_screen_modifiers num_buffers nfd nfd nfd xcb-1.2.2/xml/ge.xml000064400000000000000000000035601046102023000123230ustar 00000000000000 xcb-1.2.2/xml/glx.xml000064400000000000000000001531041046102023000125220ustar 00000000000000 xproto xproto:WINDOW PBUFFER glx:PIXMAP glx:WINDOW 32791 32792 32793 32794 data_len 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 16777215 length 24 length 4 str_len str_len length num_attribs 2 num_attribs 2 num_attribs 2 num_attribs 2 num_attribs 2 num_attribs 2 num_versions 2 gl_str_len glx_str_len num_attribs 2 num_versions 3 gl_str_len glx_str_len n 7168 7169 7170 length 4 n length 2 n n n n n n n n n n n n n length 4 n n n n n n length 4 n n n n n length 4 n length length 4 n n length 4 n n length 4 length 4 n n length 4 n n length 4 n length n n n xcb-1.2.2/xml/present.xml000064400000000000000000000203611046102023000134060ustar 00000000000000 xproto randr xfixes sync 0 1 2 3 0 0 1 2 3 0 0 1 2 3 0 0 1 2 0 1 0 1 2 3 xcb-1.2.2/xml/randr.xml000064400000000000000000001102731046102023000130360ustar 00000000000000 xproto render 0 1 2 3 4 5 nRates 0 1 2 3 0 1 2 3 4 5 6 7 nSizes nInfo nSizes 0 1 2 3 4 5 6 7 8 9 10 11 12 13 num_crtcs num_outputs num_modes names_len 0 1 2 num_crtcs num_modes num_clones name_len num_atoms length num_units format 8 num_items format 8 num_outputs num_possible_outputs size size size size size size num_crtcs num_outputs num_modes names_len 0 1 2 3 filter_len pending_len pending_nparams current_len current_nparams num_providers 0 1 2 3 num_crtcs num_outputs num_associated_providers num_associated_providers name_len num_atoms length num_items format 8 num_items format 8 0 1 2 3 4 5 6 nOutput nMonitors num_crtcs num_outputs xcb-1.2.2/xml/record.xml000064400000000000000000000150661046102023000132120ustar 00000000000000 0 1 2 1 2 3 num_ranges num_client_specs num_ranges num_client_specs num_ranges num_client_specs num_intercepted_clients length 4 xcb-1.2.2/xml/render.xml000064400000000000000000000653521046102023000132160ustar 00000000000000 xproto 0 1 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 16 17 18 19 20 21 22 23 24 25 26 27 32 33 34 35 36 37 38 39 40 41 42 43 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 0 1 0 1 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 0 1 2 3 num_visuals num_depths num_formats num_screens num_subpixel num_values value_mask Repeat AlphaMap AlphaXOrigin AlphaYOrigin ClipXOrigin ClipYOrigin ClipMask GraphicsExposure SubwindowMode PolyEdge PolyMode Dither ComponentAlpha value_mask Repeat AlphaMap AlphaXOrigin AlphaYOrigin ClipXOrigin ClipYOrigin ClipMask GraphicsExposure SubwindowMode PolyEdge PolyMode Dither ComponentAlpha glyphs_len glyphs_len num_aliases num_filters filter_len num_stops num_stops num_stops num_stops num_stops num_stops xcb-1.2.2/xml/res.xml000064400000000000000000000134661046102023000125270ustar 00000000000000 xproto 0 1 length 4 num_cross_references num_clients num_types num_specs num_ids num_specs num_sizes xcb-1.2.2/xml/screensaver.xml000064400000000000000000000163021046102023000142460ustar 00000000000000 xproto 0 1 2 0 1 0 1 2 3 value_mask BackPixmap BackPixel BorderPixmap BorderPixel BitGravity WinGravity BackingStore BackingPlanes BackingPixel OverrideRedirect SaveUnder EventMask DontPropagate Colormap Cursor xcb-1.2.2/xml/shape.xml000064400000000000000000000147201046102023000130300ustar 00000000000000 xproto 0 1 2 3 4 0 1 2 rectangles_len xcb-1.2.2/xml/shm.xml000064400000000000000000000120151046102023000125120ustar 00000000000000 xproto xcb-1.2.2/xml/sync.xml000064400000000000000000000227151046102023000127070ustar 00000000000000 xproto 0 1 2 0 1 2 3 0 1 0 1 2 3 4 5 name_len counters_len value_mask Counter ValueType Value TestType Delta Events value_mask Counter ValueType Value TestType Delta Events xcb-1.2.2/xml/xc_misc.xml000064400000000000000000000023771046102023000133620ustar 00000000000000 ids_len xcb-1.2.2/xml/xcb.xsd000064400000000000000000000374041046102023000125060ustar 00000000000000 xcb-1.2.2/xml/xevie.xml000064400000000000000000000061511046102023000130470ustar 00000000000000 0 1 xcb-1.2.2/xml/xf86dri.xml000064400000000000000000000147041046102023000132240ustar 00000000000000 bus_id_len client_driver_name_len num_clip_rects num_back_clip_rects device_private_size xcb-1.2.2/xml/xf86vidmode.xml000064400000000000000000000432311046102023000140720ustar 00000000000000 0 1 2 3 4 5 6 7 8 9 10 11 12 0 0 1 privsize privsize num_hsync num_vsync vendor_length vendor_length 3 3 vendor_length model_length modecount privsize privsize privsize privsize 1 flags 1 clocks size 1 1 size 1 1 size 1 1 size 1 1 size 1 1 size 1 1 xcb-1.2.2/xml/xfixes.xml000064400000000000000000000323551046102023000132420ustar 00000000000000 xproto render shape 0 1 0 1 0 1 0 1 2 0 1 2 0 0 width height 0 length 2 nbytes nbytes width height nbytes nbytes 0 1 2 3 num_devices xcb-1.2.2/xml/xinerama.xml000064400000000000000000000065701046102023000135400ustar 00000000000000 xproto number xcb-1.2.2/xml/xinput.xml000064400000000000000000003174241046102023000132660ustar 00000000000000 xfixes xproto name_len 0 1 2 3 4 0 1 2 3 4 5 6 0 1 axes_len class_id Key Button Valuator axes_len len devices_len num_class_info devices_len num_classes num_classes num_this_classes num_all_classes 0 1 num_classes num_classes num_axes num_events num_classes 255 num_classes num_classes 0 1 2 3 4 5 0 1 2 3 4 5 32 num_keysyms class_id Keyboard 32 Pointer String num_keysyms Integer Led Bell num_feedbacks num_keysyms class_id Keyboard Pointer String num_keysyms Integer Led Bell 0 1 2 3 4 5 6 7 0 0 0 1 2 length keycode_count keysyms_per_keycode keycodes_per_modifier 8 keycodes_per_modifier 8 map_size map_size 32 32 0 1 num_valuators class_id Key 32 Button 32 Valuator num_valuators num_classes num_valuators 1 2 3 4 5 num_valuators num_valuators num_valuators control_id resolution num_valuators num_valuators num_valuators abs_calib core enable abs_area num_valuators control_id resolution num_valuators abs_calib core enable abs_area num_atoms 8 16 32 format 8Bits num_items 16Bits num_items 32Bits num_items format 8Bits num_items 16Bits num_items 32Bits num_items 0 1 buttons_len 1 2 3 4 1 2 name_len type AddMaster name_len RemoveMaster AttachSlave DetachSlave num_changes 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 mask_len num_mask 0 1 2 3 8 1 2 3 4 5 0 1 1 2 1 2 num_buttons 31 32 num_buttons num_keys type Key num_keys Button num_buttons 31 32 num_buttons Valuator Scroll Touch name_len num_classes num_infos 0 1 mask_len 0 1 2 3 4 5 6 7 0 1 2 0 1 2 3 4 31 mask_len num_modifiers num_modifiers num_modifiers num_properties format 8Bits num_items 16Bits num_items 32Bits num_items format 8Bits num_items 16Bits num_items 32Bits num_items num_masks num_barriers 6 7 7 6 2 1 0 4 4 3 0 1 28 28 0 1 2 3 4 5 1 2 num_classes 16 buttons_len valuators_len 16 buttons_len valuators_len 0 1 2 3 4 5 0 1 2 3 4 5 6 7 buttons_len 0 1 2 3 4 5 6 7 num_infos 0 1 2 valuators_len valuators_len 16 17 buttons_len valuators_len 0 valuators_len 0 1 num_events num_classes xcb-1.2.2/xml/xkb.xml000064400000000000000000003445771046102023000125340ustar 00000000000000 xproto 255 32 4 0 1 2 3 4 5 6 7 8 9 10 11 0 1 2 0 1 2 3 4 5 6 0 1 2 3 4 5 6 7 0 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5 6 7 8 9 10 11 12 27 28 29 30 31 0 1 2 3 4 5 6 7 8 9 10 11 0 4 0 4 768 1280 0 5 0 5 768 256 512 768 1024 1280 1536 65280 0 1 2 3 254 255 0 1 2 3 7 0 6 7 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 0 1 2 3 4 7 127 7 6 5 4 3 2 1 0 4 3 2 1 0 0 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5 6 7 0 1 2 3 4 0 1 2 3 4 4 4 4 length length 5 3 length 2 nMapEntries hasPreserve nMapEntries 4 nSyms 0 1 2 3 4 129 130 131 132 nMapEntries preserve nMapEntries nPoints nOutlines 4 4 4 nKeys nRows nKeys 1 2 3 4 5 length namesPresent mapsPresent 255 254 253 0 1 2 2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 0 1 2 2 0 0 1 2 2 3 3 4 5 6 0 2 0 1 2 3 4 0 1 2 3 4 5 6 7 0 1 2 6 0 1 0 1 2 3 4 5 7 affectWhich clear selectAll NewKeyboardNotify StateNotify ControlsNotify IndicatorStateNotify IndicatorMapNotify NamesNotify CompatMapNotify BellNotify ActionMessage AccessXNotify ExtensionDeviceNotify 32 32 present KeyTypes nTypes KeySyms nKeySyms KeyActions nKeyActions totalActions KeyBehaviors totalKeyBehaviors VirtualMods virtualMods ExplicitComponents totalKeyExplicit ModifierMap totalModMapKeys VirtualModMap totalVModMapKeys present KeyTypes nTypes KeySyms nKeySyms KeyActions nKeyActions totalActions KeyBehaviors totalKeyBehaviors VirtualMods virtualMods ExplicitComponents totalKeyExplicit ModifierMap totalModMapKeys VirtualModMap totalVModMapKeys nSIRtrn groupsRtrn nSI groups which which which Keycodes Geometry Symbols PhysSymbols Types Compat KeyTypeNames nTypes KTLevelNames nTypes IndicatorNames indicators VirtualModNames virtualMods GroupNames groupNames KeyNames nKeys KeyAliases nKeyAliases RGNames nRadioGroups which Keycodes Geometry Symbols PhysSymbols Types Compat KeyTypeNames nTypes KTLevelNames nTypes IndicatorNames indicators VirtualModNames virtualMods GroupNames groupNames KeyNames nKeys KeyAliases nKeyAliases RGNames nRadioGroups nKeymaps nKeycodes nTypes nCompatMaps nSymbols nGeometries reported Types ClientSymbols ServerSymbols present KeyTypes nTypes KeySyms nKeySyms KeyActions nKeyActions totalActions KeyBehaviors totalKeyBehaviors VirtualMods virtualMods ExplicitComponents totalKeyExplicit ModifierMap totalModMapKeys VirtualModMap totalVModMapKeys CompatMap nSIRtrn groupsRtrn IndicatorMaps nIndicators KeyNames OtherNames which Keycodes Geometry Symbols PhysSymbols Types Compat KeyTypeNames nTypes KTLevelNames nTypes IndicatorNames indicators VirtualModNames virtualMods GroupNames groupNames KeyNames nKeys KeyAliases nKeyAliases RGNames nRadioGroups Geometry nameLen nBtnsRtrn nDeviceLedFBs nBtns nDeviceLedFBs msgLength 8 xcb-1.2.2/xml/xml-xcb.txt000064400000000000000000000407621046102023000133260ustar 00000000000000 xcb/proto Description =========== xcb/proto is a set of XML files describing the X Window System protocol It is designed for use with libxcb, the X C binding . xcb/proto consists of: xcb.xsd An XML Schema defining the data format for describing the X protocol. *.py Code generator helpers that read the protocol descriptions into python structures. See libxcb for example usage. *.xml XML descriptions of the core X protocol and many extensions. Generating C bindings ===================== See libxcb . Protocol Description Format =========================== Root element ------------ top-level elements This is the root element of a protocol description. The attributes are all various forms of the extension name. header is the basename of the XML protocol description file, which will be used as the basename for generated bindings as well. extension-name is the name of the extension in InterCaps, which will be used in the names of functions. extension-xname is the name of the extension as passed to QueryExtension. As an example, the XML-XCB description for the GO-FASTER extension would use the root element ; as a result, C bindings will be put in gofaster.h and gofaster.c, extension functions will be named XCBGoFasterFunctionName, and the extension initialization will call QueryExtension with the name "GO-FASTER". This element can contain any number of the elements listed in the section "Top-Level Elements" below. Top-Level Elements ------------------ header_name The import element allows the protocol description to reference types declared in another extension. The content is be the basename of the extension XML file, which is also the header attribute of the extension's root node. Note that types from xproto are automatically available, without explicitly importing them. structure contents This element represents a data structure. The name attribute gives the name of the structure. The content represents the fields of the structure, and consists of one or more of the field, pad, and list elements described in the section "Structure Contents" below. structure contents This element represents a union of data types, which can hold one value of any of those types. The name attribute gives the name of the union. The content represents the fields of the union, and consists of one or more of the field and pad elements described in the section "Structure Contents below". event-type-selector list This element represents a data structure that is the wire-representation of an event. The event can be any type that's selected by the event-type-selector list. This element represents an identifier for a particular type of resource. The name attribute gives the name of the new type. [optional expression] ... The enum element represents an enumeration type, which can take on any of the values given by the contained item elements. The name attribute on the enum gives the name of the enumerated type. The item element represents one possible value of an enumerated type. The name attribute on the item gives the name of that value, and the optional content is an expression giving the numeric value. If the expression is omitted, the value will be one more than that of the previous item, or 0 for the first item. The typedef element declares the type given by the newname attribute to be an alias for the type given by the oldname attribute. structure contents [structure contents] The request element represents an X protocol request. The name attribute gives the name of the request, and the opcode attribute gives the numeric request code. The content of the request element represents the fields in the request, and consists of one or more of any of the elements listed in the "Structure Contents" section below. Note that for requests in the core protocol, the first field in the request goes into the one-byte gap between the major opcode and the length; if the request does not have any data in that gap, put a one byte pad as the first element. Extension requests always have this gap filled with the minor opcode. The optional reply element is present if the request has a reply. The content of the reply element represents the fields in the reply, and consists of zero or more of the field, pad, and list elements listed in the "Structure Contents" section below. Note that the first field in the reply always goes into the one-byte gap between the response type and the sequence number; if the reply does not have any data in that gap, put a one byte pad as the first element. If the optional combine-adjacent attribute is true, multiple adjacent requests of the same type may be combined into a single request without affecting the semantics of the requests. structure contents This element represents an X protocol event. The name attribute gives the name of the event, and the number attribute gives the event number. The content of the event element represents the fields in the event, and consists of zero or more of the field, pad, and list elements listed in the "Structure Contents" section below. If the optional no-sequence-number attribute is true, the event does not include a sequence number. This is a special-case for the KeymapNotify event in the core protocol, and should not be used in any other event. If the optional xge attribute is true, the event is an X Generic Event and will be treated as such. The no-sequence-number and xge attribute can not be combined. structure contents This element represents an X protocol error. The name attribute gives the name of the error, and the number attribute gives the error number. The content of the error element represents the fields in the error, and consists of zero or more of the field, pad, and list elements listed in the "Structure Contents" section below. This element creates an alias for the event named in the ref attribute, with the new name given in the name attribute, and the new event number given in the number attribute. This element creates an alias for the error named in the ref attribute, with the new name given in the name attribute, and the new error number given in the number attribute. Structure Contents ------------------ Note: "type" attributes below refer to types defined by previous elements, either in the current extension, xproto, or one of the imported extensions. The type name must refer to only one possible type; if more than one type matches, an error occurs. To avoid this, the type may be explicitly prefixed with a namespace, which should be the value of the header attribute on the protocol description containing the desired type. The namespace and type are separated by a single colon. For example, to refer to the PIXMAP type defined in glx rather than the one defined in xproto, use type="glx:PIXMAP" rather than type="PIXMAP". Note: Most of the below may optionally contain an enum, altenum, mask or altmask attribute, which follows the above rules for "type". "enum" is an exhaustive enum; the value is restricted to one of the constants named in the enum. "altenum" may be one of the values contained in the enum, but it need not be. "mask" refers to an exhaustive enum to be used as a bitmask. "altmask" may be a mask from the referred enum, but it need not be. This element declares some padding in a data structure. The bytes attribute declares the number of bytes of padding. If serialize="true", then the pad will be serialized/deserialized. This is only needed for ABI compatibility with legacy. Newly added pads should not be defined with serialize="true". The serialize attribute may be omitted. Default is serialize="false". This element represents a field in a data structure. The type attribute declares the data type of the field, and the name attribute gives the name of the field. This element represents a file descriptor field passed with the request. The name attribute gives the name of the field. While ordinary fields are encoded as part of the request, file descriptor fields are generally passed via an out-of-band mechanism. expression This element represents an array or list of fields in a data structure. The type attribute declares the data type of the field, and the name attribute gives the name of the field. The content is an expression giving the length of the list in terms of other fields in the structure. See the section "Expressions" for details on the expression representation. expression This element represents a field in a request that is calculated rather than supplied by the caller. The type attribute declares the data type of the field, and the name attribute gives the name of the field. The content is the expression giving the value of the field. See the section "Expressions" for details on the expression representation. This element represents a BITMASK/LISTofVALUE parameter pair: a bitmask defining the set of values included, and a list containing these values. value-mask-type gives the type of the bitmask; this must be CARD16 or CARD32. value-mask-name gives the field name of the bitmask, and value-list-name gives the field name of the list of values. Please use instead for new protocol definitions. switch expression bitcase expression(s), fields case expression(s), fields This element represents conditional inclusion of fields. It can be viewed as sequence of multiple ifs: : if ( switch expression & bitcase expression ) is non-zero, bitcase fields are included in structure. : if ( switch expression == case expression ) is true, then case fields are included in structure. It can be used only as the last field of a structure. When a bitcase or case includes multiple clauses, the contents of the bitcase or case are only present once regardless of the number of bitcase or case expressions that match. inside can only refer to an enum's members. inside can only refer to an enum's members. A switch may contain multiple or elements. Usually it will only contain elements or only contain elements. That is, mixing of and usually doesn't make any sense. The same value may appear in multiple or elements. New protocol definitions should prefer to use this instead of and instead of . Expressions ----------- Expressions consist of a tree of elements with leaves consisting of or elements. expression expression The op element represents an operator, with the op attribute specifying which operator. The supported operations are +, -, *, /, &, and <<, and their semantics are identical to the corresponding operators in C. The two operand expressions may be other expression elements. identifier The fieldref element represents a reference to the value of another field in the structure containing this expression. The identifier is the value of the "name" attribute on the referenced field. identifier A paramref is similar to a fieldref, but it refers to the value of a field in the context which refers to the struct which contains the paramref. So, it refers to a field outside of the structure where it is defined. This has the following consequences: * The generator cannot deduce its type. So, it is mandatory to specify its type. * The identifier-name must not be used as a field in the structure which contaons the paramref. For an example, see struct "DeviceTimeCoord" and request/reply "GetDeviceMotionEvents" in xinput.xml, where paramref "num_axes" in struct DeviceTimeCoord refers to field "num_axes" in the DeviceTimeCoord reply. integer The value element represents a literal integer value in an expression. The integer may be expressed in decimal or hexadecimal. integer The bit element represents a literal bitmask value in an expression. The integer must be in the range 0..31, expanding to (1<enum item identifier This element represents a reference to item of enum. expression This element represents a unary operator, with the op attribute specifying which operator. The only supported operation so far is ~, and its semantic is identical to the corresponding operator in C. This element represents a sumation of the elements of the referenced list. expression The expression is evaluated for each element of the referenced list, in the context of this element. This sumof element then represents a sumation of the results of these evaluations. expression will usually be a fieldref which references a field of a list-element or an expression containing a fieldref, such as popcount of a fieldref. expression This element represents the number of bits set in the expression. This element represents the current list-element when used inside a list-iteration expression such as . Event-Type-Selector List ------------------------ The event-type-selector list selects a set of eventtypes. It consists of any number of the following elements: The extension attribute selects events from the given extension. If the xge attribute is true, the event is an X Generic Event and will be treated as such. opcode-min and opcode-max describe the minimum and maximum opcode respectively. The opcode is the same number as the number-attribute of an event definition. I.e. this is the offset from the event-base to the actual number used on the wire. In the current implementation, only xge="false" is supported. Documentation ------------- Documentation for each request, reply or event is stored in the appropriate element using a element. The element can contain the following elements: brief description A short description of the request, reply or event. For example "makes a window visible" for MapWindow. This will end up in the manpage NAME section and in the doxygen @brief description. The full description. Use `` to highlight words, such as "Draws `points_len`-1 lines between each pair of points…" Example C code illustrating the usage of the particular request, reply or event. field description The full description for the specified field. Depending on the context, this is either a request parameter or a reply/event datastructure field. error description The full description for an error which can occur due to this request. A reference to another relevant program, function, request or event. xcb-1.2.2/xml/xprint.xml000064400000000000000000000263671046102023000132660ustar 00000000000000 xproto nameLen descLen 0 1 0 0 1 1 2 3 4 5 6 1 2 3 4 5 6 7 printerNameLen localeLen listCount printerNameLen localeLen len_data len_fmt len_options dataLen stringLen nameLen valueLen listCount xcb-1.2.2/xml/xproto.xml000064400000000000000000007370401046102023000132720ustar 00000000000000 WINDOW PIXMAP FONT GCONTEXT 0 1 2 3 4 5 visuals_len 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 0 1 2 allowed_depths_len authorization_protocol_name_len authorization_protocol_data_len reason_len length 4 0 1 vendor_len pixmap_formats_len roots_len 0 1 2 3 4 5 6 7 15 0 1 2 3 4 5 6 7 8 9 10 11 12 0 a key was pressed/released 8 9 10 11 12 15 a mouse button was pressed/released 0 1 a key was pressed 0 1 2 3 4 5 6 7 0 1 2 3 the pointer is in a different window NOT YET DOCUMENTED 31 NOT YET DOCUMENTED 0 1 2 a window is destroyed a window is unmapped a window was mapped window wants to be mapped NOT YET DOCUMENTED 0 1 NOT YET DOCUMENTED 0 1 a window property changed 0 0 0 1 2 3 4 5 6 7 8 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 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 0 1 0 the colormap for some window changed 20 10 5 NOT YET DOCUMENTED 0 1 2 keyboard mapping changed generic event (with length) 0 1 2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 0 1 0 0 1 2 3 4 5 6 7 8 9 10 value_mask BackPixmap BackPixel BorderPixmap BorderPixel BitGravity WinGravity BackingStore BackingPlanes BackingPixel OverrideRedirect SaveUnder EventMask DontPropagate Colormap Cursor Creates a window value_mask BackPixmap BackPixel BorderPixmap BorderPixel BitGravity WinGravity BackingStore BackingPlanes BackingPixel OverrideRedirect SaveUnder EventMask DontPropagate Colormap Cursor change window attributes 0 1 2 Gets window attributes Destroys a window 0 1 Changes a client's save set Reparents a window Makes a window visible Makes a window invisible 0 1 2 3 4 5 6 0 1 2 3 4 value_mask X Y Width Height BorderWidth Sibling StackMode Configures window attributes xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let window: x::Window = conn.generate_id(); // Configures the given window to the left upper corner // with a size of 1024x768 pixels. conn.send_request(&x::ConfigureWindow { window, value_list: &[ x::ConfigWindow::X(0), x::ConfigWindow::Y(0), x::ConfigWindow::Width(0), x::ConfigWindow::Height(0), ], }); conn.flush()?; # Ok(()) # } ]]> 0 1 Change window stacking order Get current window geometry xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let window: x::Window = conn.generate_id(); // Get the X and Y coordinates of a window. let cookie = conn.send_request(&x::GetGeometry { drawable: x::Drawable::Window(window), }); let reply = conn.wait_for_reply(cookie)?; let (x, y) = (reply.x(), reply.y()); # Ok(()) # } ]]> children_len query the window tree xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let window: x::Window = conn.generate_id(); // Get the root, parent and children of the specified window. let cookie = conn.send_request(&x::QueryTree { window, }); let reply = conn.wait_for_reply(cookie)?; // type inference is not needed here, only to show the type of returned value. let root: x::Window = reply.root(); let parent: x::Window = reply.parent(); let children: &[x::Window] = reply.children(); # Ok(()) # } ]]> name_len Get atom identifier by name xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let window: x::Window = conn.generate_id(); // Resolve the _NET_WM_NAME atom. let cookie = conn.send_request(&x::InternAtom { only_if_exists: false, name: b"_NET_WM_NAME", }); let reply = conn.wait_for_reply(cookie)?; let wm_name = reply.atom(); # Ok(()) # } ]]> name_len 0 1 2 data_len format 8 Changes a window property xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let window: x::Window = conn.generate_id(); // Set the WM_NAME property of the window "XCB Example". conn.send_request(&x::ChangeProperty { mode: x::PropMode::Replace, window, property: x::ATOM_WM_NAME, r#type: x::ATOM_STRING, data: b"XCB Example", }); # Ok(()) # } ]]> 0 value_len format 8 Gets a window property xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let window: x::Window = conn.generate_id(); // Get the WM_NAME property of the window let cookie = conn.send_request(&x::GetProperty { delete: false, window, property: x::ATOM_WM_NAME, r#type: x::ATOM_STRING, long_offset: 0, long_length: 0, }); let reply = conn.wait_for_reply(cookie)?; // value() returns &[u8] let title = str::from_utf8(reply.value()).expect("The WM_NAME property is not valid UTF-8"); # Ok(()) # } ]]> atoms_len Sets the owner of a selection Gets the owner of a selection 0 1 32 send an event xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let window: x::Window = conn.generate_id(); // Tell the given window that it was configured to a size of 800x600 pixels. let event = x::ConfigureNotifyEvent::new( window, window, x::Window::none(), 0, 0, 800, 600, 0, false, ); conn.send_request(&x::SendEvent { propagate: false, destination: x::SendEventDest::Window(window), event_mask: x::EventMask::STRUCTURE_NOTIFY, event: &event, }); conn.flush()?; # Ok(()) # } ]]> 0 1 0 1 2 3 4 0 Grab the pointer xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let setup = conn.get_setup(); # let screen = setup.roots().nth(screen_num as usize).unwrap(); # let window: x::Window = conn.generate_id(); # let cursor: x::Cursor = conn.generate_id(); // Grabs the pointer actively let cookie = conn.send_request(&x::GrabPointer { owner_events: false, // get all pointer events specified by the following mask grab_window: screen.root(), // grab the root window event_mask: x::EventMask::NO_EVENT, // which event to let through pointer_mode: x::GrabMode::Async, // pointer events should continue as normal keyboard_mode: x::GrabMode::Async, // pointer events should continue as normal confine_to: x::Window::none(), // in which window should the cursor stay cursor, // we change the cursor time: x::CURRENT_TIME, }); let reply = conn.wait_for_reply(cookie)?; assert!(reply.status() == x::GrabStatus::Success, "GrabPointer did not succeed"); # Ok(()) # } ]]> release the pointer 0 1 2 3 4 5 Grab pointer button(s) Grab the keyboard xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let setup = conn.get_setup(); # let screen = setup.roots().nth(screen_num as usize).unwrap(); # let window: x::Window = conn.generate_id(); # let cursor: x::Cursor = conn.generate_id(); // Grabs the keyboard actively let cookie = conn.send_request(&x::GrabKeyboard { owner_events: true, // get all pointer events specified by the following mask grab_window: screen.root(), // grab the root window time: x::CURRENT_TIME, pointer_mode: x::GrabMode::Async, // process events as normal, do not require sync keyboard_mode: x::GrabMode::Async, }); let reply = conn.wait_for_reply(cookie)?; assert!(reply.status() == x::GrabStatus::Success, "GrabKeyboard did not succeed"); # Ok(()) # } ]]> 0 Grab keyboard key(s) release a key combination 0 1 2 3 4 5 6 7 release queued events get pointer coordinates events_len move mouse pointer 0 1 2 3 Sets input focus 32 name_len opens a font 0 1 properties_len char_infos_len query font metrics string_len 1 get text extents name_len pattern_len names_len get matching font names pattern_len properties_len name_len get matching font names and information font_qty path_len Creates a pixmap Destroys a pixmap 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 0 1 2 3 0 1 2 0 1 2 3 0 1 0 1 0 1 value_mask Function PlaneMask Foreground Background LineWidth LineStyle CapStyle JoinStyle FillStyle FillRule Tile Stipple TileStippleOriginX TileStippleOriginY Font SubwindowMode GraphicsExposures ClipOriginX ClipOriginY ClipMask DashOffset DashList ArcMode Creates a graphics context value_mask Function PlaneMask Foreground Background LineWidth LineStyle CapStyle JoinStyle FillStyle FillRule Tile Stipple TileStippleOriginX TileStippleOriginY Font SubwindowMode GraphicsExposures ClipOriginX ClipOriginY ClipMask DashOffset DashList ArcMode change graphics context components xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let setup = conn.get_setup(); # let screen = setup.roots().nth(screen_num as usize).unwrap(); # let gc: x::Gcontext = conn.generate_id(); // Change the foreground and background component // of a graphics context to white on black. // `value_list` components order must be in the same order // than in the `Gc` enum. conn.send_request(&x::ChangeGc { gc, value_list: &[ x::Gc::Foreground(screen.white_pixel()), x::Gc::Background(screen.black_pixel()), ], }); conn.flush()?; # Ok(()) # } ]]> dashes_len 0 1 2 3 Destroys a graphics context copy areas 0 1 Draw lines xcb::Result<()> { # let (conn, screen_num) = xcb::Connection::connect(None)?; # let setup = conn.get_setup(); # let screen = setup.roots().nth(screen_num as usize).unwrap(); # let window: x::Window = conn.generate_id(); # let gc: x::Gcontext = conn.generate_id(); // Draw a straight line on a window let points = &[ x::Point { x: 10, y: 10 }, x::Point { x: 100, y: 10 }, ]; conn.send_request(&x::PolyLine { coordinate_mode: x::CoordMode::Origin, drawable: x::Drawable::Window(window), gc, points, }); conn.flush()?; # Ok(()) # } ]]> Draw lines 0 1 2 Fills rectangles 0 1 2 length 4 string_len Draws text string_len Draws text 0 1 cmaps_len Allocate a color name_len pixels_len masks_len pixels_len 0 1 2 name_len colors_len name_len 0 0 create cursor Deletes a cursor 0 1 2 name_len check if extension is present names_len keycode_count keysyms_per_keycode length 0 1 2 3 4 5 6 7 0 1 0 1 2 value_mask KeyClickPercent BellPercent BellPitch BellDuration Led LedMode Key AutoRepeatMode 32 0 1 2 0 1 2 0 1 0 1 2 5 6 address_len address_len hosts_len 0 1 0 1 2 0 kills a client atoms_len 0 1 0 1 2 map_len map_len 0 1 2 3 4 5 6 7 keycodes_per_modifier 8 keycodes_per_modifier 8 xcb-1.2.2/xml/xselinux.xml000064400000000000000000000222671046102023000136140ustar 00000000000000 xproto context_len context_len context_len context_len context_len context_len context_len object_context_len data_context_len context_len context_len context_len context_len context_len context_len properties_len context_len context_len context_len context_len context_len context_len selections_len context_len xcb-1.2.2/xml/xtest.xml000064400000000000000000000076211046102023000131010ustar 00000000000000 xproto 0 1 xcb-1.2.2/xml/xv.xml000064400000000000000000000402461046102023000123670ustar 00000000000000 xproto shm 0 1 2 3 4 0 1 0 1 0 1 0 1 2 3 4 0 1 0 1 2 3 4 5 name_size num_formats name_size num_planes num_planes data_size size 16 32 num_adaptors num_encodings num_attributes num_formats num_planes num_planes xcb-1.2.2/xml/xvmc.xml000064400000000000000000000124421046102023000127040ustar 00000000000000 xv num length length 4 length num