fix(deps): backport secure Wayland XML parser
This commit is contained in:
Generated
+10
-5
@@ -7125,9 +7125,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.39.3"
|
version = "0.41.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "721da970c312655cde9b4ffe0547f20a8494866a4af5ff51f18b7c633d0c870b"
|
checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -9819,6 +9819,12 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "similar"
|
||||||
|
version = "2.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simplecss"
|
name = "simplecss"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -11818,12 +11824,11 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-scanner"
|
name = "wayland-scanner"
|
||||||
version = "0.31.10"
|
version = "0.31.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quick-xml 0.39.3",
|
"quick-xml 0.41.0",
|
||||||
"quote",
|
"quote",
|
||||||
|
"similar",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ members = [
|
|||||||
"crates/ely_domain",
|
"crates/ely_domain",
|
||||||
"crates/ely_servo_host",
|
"crates/ely_servo_host",
|
||||||
"crates/ely_sync_client",
|
"crates/ely_sync_client",
|
||||||
|
"third_party/wayland-scanner",
|
||||||
]
|
]
|
||||||
default-members = ["crates/ely_app"]
|
default-members = ["crates/ely_app"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
@@ -43,6 +44,7 @@ gpui = { path = "third_party/gpui" }
|
|||||||
gpui_http_client = { path = "third_party/gpui_support/gpui_http_client" }
|
gpui_http_client = { path = "third_party/gpui_support/gpui_http_client" }
|
||||||
gpui_sum_tree = { path = "third_party/gpui_support/gpui_sum_tree" }
|
gpui_sum_tree = { path = "third_party/gpui_support/gpui_sum_tree" }
|
||||||
gpui_util = { path = "third_party/gpui_support/gpui_util" }
|
gpui_util = { path = "third_party/gpui_support/gpui_util" }
|
||||||
|
wayland-scanner = { path = "third_party/wayland-scanner" }
|
||||||
zed-sum-tree = { path = "third_party/gpui_support/zed-sum-tree" }
|
zed-sum-tree = { path = "third_party/gpui_support/zed-sum-tree" }
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
|
|||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
# CHANGELOG: wayland-scanner
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
## 0.31.8 -- 2025-12-30
|
||||||
|
|
||||||
|
- Update `quick-xml` to 0.38
|
||||||
|
- Generate bindings for `WlRegistry`
|
||||||
|
* Needed for client/server to use `wl_fixes`
|
||||||
|
|
||||||
|
## 0.31.7 -- 2025-07-28
|
||||||
|
|
||||||
|
- Update `quick-xml` to 0.37
|
||||||
|
|
||||||
|
## 0.31.5 -- 2024-09-04
|
||||||
|
|
||||||
|
- Update `quick-xml` to 0.36
|
||||||
|
- Allow `<!DOCTYPE>` in XML input
|
||||||
|
|
||||||
|
## 0.31.3 -- 2024-07-03
|
||||||
|
|
||||||
|
- Update `quick-xml` to 0.34
|
||||||
|
|
||||||
|
## 0.31.2 -- 2024-05-30
|
||||||
|
|
||||||
|
- Use wrapper type implementing `Sync` instead of `static mut`s.
|
||||||
|
- Add headerless xml file parsing possibility for `parse` function.
|
||||||
|
|
||||||
|
## 0.31.1 -- 2024-01-29
|
||||||
|
|
||||||
|
- Include an `std::convert::Infallible` in hidden `__phantom_lifetime` enum variants,
|
||||||
|
so they're explicitly unconstructable.
|
||||||
|
|
||||||
|
## 0.31.0 -- 2023-09-02
|
||||||
|
|
||||||
|
#### Breaking changes
|
||||||
|
|
||||||
|
- Bump bitflags to 2.0
|
||||||
|
- Remove `io-lifetimes` from the generated code following wayland-backend 0.3 dropping it.
|
||||||
|
- Generate `BorrowedFd<'_>` arguments instead of `RawFd`
|
||||||
|
|
||||||
|
# 0.30.1 -- 2023-06-17
|
||||||
|
|
||||||
|
- Generated protocol object types now implement `Borrow<ObjectId>` and `Hash`
|
||||||
|
|
||||||
|
## 0.30.0 -- 2022-12-27
|
||||||
|
|
||||||
|
## 0.30.0-beta.10
|
||||||
|
|
||||||
|
#### Additions
|
||||||
|
|
||||||
|
- An `opcode()` method is added to message enums to retrive the opcode associated with a message variant.
|
||||||
|
|
||||||
|
## 0.30.0-beta.9
|
||||||
|
|
||||||
|
- Migrate from xml-rs to quick-xml
|
||||||
|
|
||||||
|
## 0.30.0-beta.6
|
||||||
|
|
||||||
|
- Generated enums now derive `Ord` and `Hash`.
|
||||||
|
- The scanner now generates constants for the opcode values of the protocol messages.
|
||||||
|
|
||||||
|
## 0.30.0-alpha1
|
||||||
|
|
||||||
|
Full rework of the crate together of the reworks of `wayland-client` and `wayland-server`.
|
||||||
|
|
||||||
|
The code generation is now achieved using procedural macros rather than build scripts.
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||||
|
#
|
||||||
|
# When uploading crates to the registry Cargo will automatically
|
||||||
|
# "normalize" Cargo.toml files for maximal compatibility
|
||||||
|
# with all versions of Cargo and also rewrite `path` dependencies
|
||||||
|
# to registry (e.g., crates.io) dependencies.
|
||||||
|
#
|
||||||
|
# If you are reading this file be aware that the original Cargo.toml
|
||||||
|
# will likely look very different (and much more reasonable).
|
||||||
|
# See Cargo.toml.orig for the original contents.
|
||||||
|
|
||||||
|
[package]
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.71"
|
||||||
|
name = "wayland-scanner"
|
||||||
|
version = "0.31.10"
|
||||||
|
authors = ["Elinor Berger <elinor@safaradeg.net>"]
|
||||||
|
build = false
|
||||||
|
autolib = false
|
||||||
|
autobins = false
|
||||||
|
autoexamples = false
|
||||||
|
autotests = false
|
||||||
|
autobenches = false
|
||||||
|
description = "Wayland Scanner for generating rust APIs from XML wayland protocol files."
|
||||||
|
documentation = "https://docs.rs/wayland-scanner/"
|
||||||
|
readme = "README.md"
|
||||||
|
keywords = [
|
||||||
|
"wayland",
|
||||||
|
"codegen",
|
||||||
|
]
|
||||||
|
categories = [
|
||||||
|
"gui",
|
||||||
|
"api-bindings",
|
||||||
|
]
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/smithay/wayland-rs"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = [
|
||||||
|
"--cfg",
|
||||||
|
"docsrs",
|
||||||
|
]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "wayland_scanner"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies.proc-macro2]
|
||||||
|
version = "1.0.11"
|
||||||
|
|
||||||
|
[dependencies.quick-xml]
|
||||||
|
version = "0.41"
|
||||||
|
|
||||||
|
[dependencies.quote]
|
||||||
|
version = "1.0"
|
||||||
|
|
||||||
|
[dev-dependencies.similar]
|
||||||
|
version = "2"
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
name = "wayland-scanner"
|
||||||
|
version = "0.31.10"
|
||||||
|
authors = ["Elinor Berger <elinor@safaradeg.net>"]
|
||||||
|
repository = "https://github.com/smithay/wayland-rs"
|
||||||
|
documentation = "https://docs.rs/wayland-scanner/"
|
||||||
|
description = "Wayland Scanner for generating rust APIs from XML wayland protocol files."
|
||||||
|
license = "MIT"
|
||||||
|
categories = ["gui", "api-bindings"]
|
||||||
|
keywords = ["wayland", "codegen"]
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.71"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = "1.0.11"
|
||||||
|
quote = "1.0"
|
||||||
|
quick-xml = "0.41"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
similar = "2"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
# ELY security backport
|
||||||
|
|
||||||
|
Source: `wayland-scanner` 0.31.10 from crates.io, archive SHA-256
|
||||||
|
`9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a`, published under the MIT license.
|
||||||
|
|
||||||
|
ELY carries the following changes while the upstream security release remains unpublished:
|
||||||
|
|
||||||
|
- Upgrade `quick-xml` from 0.39 to 0.41 for RUSTSEC-2026-0194 and RUSTSEC-2026-0195.
|
||||||
|
- Use `GeneralRef::xml10_content()` for the quick-xml 0.41 API.
|
||||||
|
- Split message parsing and writing generation into a dedicated module so production source files remain below 500 lines.
|
||||||
|
|
||||||
|
Upstream references:
|
||||||
|
|
||||||
|
- Security dependency update: `d07c4f91f28b42e5a485823ffd9d8d5a210b1053`.
|
||||||
|
- quick-xml API migration: `ec2d932855593d48aa83c76820f3efbcfea86d39`.
|
||||||
|
- Equivalent boolean attribute parsing cleanup: `006b508ee49669fc42031a150ea9b316297e9cf8`.
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2015 Elinor Berger
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
[](https://crates.io/crates/wayland-scanner)
|
||||||
|
[](https://docs.rs/wayland-scanner)
|
||||||
|
[](https://github.com/Smithay/wayland-rs/actions?query=workflow%3A%22Continuous+Integration%22)
|
||||||
|
[](https://codecov.io/gh/Smithay/wayland-rs)
|
||||||
|
|
||||||
|
# wayland-scanner
|
||||||
|
|
||||||
|
This crate provides procedural macros for generating the rust code associated with a
|
||||||
|
Wayland XML protocol specification, for use with the `wayland-client`, `wayland-server`
|
||||||
|
and `wayland-backend` crates.
|
||||||
|
|
||||||
|
Before trying to use this crate, you may check if the protocol extension you want to use
|
||||||
|
is not already exposed in the `wayland-protocols` crate.
|
||||||
+172
@@ -0,0 +1,172 @@
|
|||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
use proc_macro2::{Literal, TokenStream};
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
use crate::protocol::{Interface, Message, Protocol, Type};
|
||||||
|
|
||||||
|
pub(crate) fn generate_interfaces_prefix(protocol: &Protocol) -> TokenStream {
|
||||||
|
let longest_nulls = protocol.interfaces.iter().fold(0, |max, interface| {
|
||||||
|
let request_longest_null = interface.requests.iter().fold(0, |max, request| {
|
||||||
|
if request.all_null() {
|
||||||
|
cmp::max(request.args.len(), max)
|
||||||
|
} else {
|
||||||
|
max
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let events_longest_null = interface.events.iter().fold(0, |max, event| {
|
||||||
|
if event.all_null() {
|
||||||
|
cmp::max(event.args.len(), max)
|
||||||
|
} else {
|
||||||
|
max
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cmp::max(max, cmp::max(request_longest_null, events_longest_null))
|
||||||
|
});
|
||||||
|
|
||||||
|
let types_null_len = Literal::usize_unsuffixed(longest_nulls);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
use std::ptr::null;
|
||||||
|
struct SyncWrapper<T>(T);
|
||||||
|
unsafe impl<T> Sync for SyncWrapper<T> {}
|
||||||
|
static types_null: SyncWrapper<[*const wayland_backend::protocol::wl_interface; #types_null_len]> = SyncWrapper([
|
||||||
|
null::<wayland_backend::protocol::wl_interface>(); #types_null_len
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate_interface(interface: &Interface) -> TokenStream {
|
||||||
|
let requests = gen_messages(interface, &interface.requests, "requests");
|
||||||
|
let events = gen_messages(interface, &interface.events, "events");
|
||||||
|
|
||||||
|
let interface_ident = format_ident!("{}_interface", interface.name);
|
||||||
|
let name_value = null_terminated_byte_string_literal(&interface.name);
|
||||||
|
let version_value = Literal::i32_unsuffixed(interface.version as i32);
|
||||||
|
let request_count_value = Literal::i32_unsuffixed(interface.requests.len() as i32);
|
||||||
|
let requests_value = if interface.requests.is_empty() {
|
||||||
|
quote! { null::<wayland_backend::protocol::wl_message>() }
|
||||||
|
} else {
|
||||||
|
let requests_ident = format_ident!("{}_requests", interface.name);
|
||||||
|
quote! { #requests_ident.0.as_ptr() }
|
||||||
|
};
|
||||||
|
let event_count_value = Literal::i32_unsuffixed(interface.events.len() as i32);
|
||||||
|
let events_value = if interface.events.is_empty() {
|
||||||
|
quote! { null::<wayland_backend::protocol::wl_message>() }
|
||||||
|
} else {
|
||||||
|
let events_ident = format_ident!("{}_events", interface.name);
|
||||||
|
quote! { #events_ident.0.as_ptr() }
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#requests
|
||||||
|
#events
|
||||||
|
|
||||||
|
pub static #interface_ident: wayland_backend::protocol::wl_interface = wayland_backend::protocol::wl_interface {
|
||||||
|
name: #name_value as *const u8 as *const std::os::raw::c_char,
|
||||||
|
version: #version_value,
|
||||||
|
request_count: #request_count_value,
|
||||||
|
requests: #requests_value,
|
||||||
|
event_count: #event_count_value,
|
||||||
|
events: #events_value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_messages(interface: &Interface, messages: &[Message], which: &str) -> TokenStream {
|
||||||
|
if messages.is_empty() {
|
||||||
|
return TokenStream::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
let types_arrays = messages.iter().filter_map(|msg| {
|
||||||
|
if msg.all_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let array_ident = format_ident!("{}_{}_{}_types", interface.name, which, msg.name);
|
||||||
|
let array_len = Literal::usize_unsuffixed(msg.args.len());
|
||||||
|
let array_values = msg.args.iter().map(|arg| match (arg.typ, &arg.interface) {
|
||||||
|
(Type::Object, &Some(ref inter)) | (Type::NewId, &Some(ref inter)) => {
|
||||||
|
let interface_ident =format_ident!("{}_interface", inter);
|
||||||
|
quote! { &#interface_ident as *const wayland_backend::protocol::wl_interface }
|
||||||
|
}
|
||||||
|
_ => quote! { null::<wayland_backend::protocol::wl_interface>() },
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(quote! {
|
||||||
|
static #array_ident: SyncWrapper<[*const wayland_backend::protocol::wl_interface; #array_len]> = SyncWrapper([
|
||||||
|
#(#array_values,)*
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let message_array_ident = format_ident!("{}_{}", interface.name, which);
|
||||||
|
let message_array_len = Literal::usize_unsuffixed(messages.len());
|
||||||
|
let message_array_values = messages.iter().map(|msg| {
|
||||||
|
let name_value = null_terminated_byte_string_literal(&msg.name);
|
||||||
|
let signature_value = Literal::byte_string(&message_signature(msg));
|
||||||
|
|
||||||
|
let types_ident = if msg.all_null() {
|
||||||
|
format_ident!("types_null")
|
||||||
|
} else {
|
||||||
|
format_ident!("{}_{}_{}_types", interface.name, which, msg.name)
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: #name_value as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: #signature_value as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: #types_ident.0.as_ptr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#(#types_arrays)*
|
||||||
|
|
||||||
|
static #message_array_ident: SyncWrapper<[wayland_backend::protocol::wl_message; #message_array_len]> = SyncWrapper([
|
||||||
|
#(#message_array_values,)*
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message_signature(msg: &Message) -> Vec<u8> {
|
||||||
|
let mut res = Vec::new();
|
||||||
|
|
||||||
|
if msg.since > 1 {
|
||||||
|
res.extend_from_slice(msg.since.to_string().as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
for arg in &msg.args {
|
||||||
|
if arg.typ.nullable() && arg.allow_null {
|
||||||
|
res.push(b'?');
|
||||||
|
}
|
||||||
|
match arg.typ {
|
||||||
|
Type::NewId => {
|
||||||
|
if arg.interface.is_none() {
|
||||||
|
res.extend_from_slice(b"su");
|
||||||
|
}
|
||||||
|
res.push(b'n');
|
||||||
|
}
|
||||||
|
Type::Uint => res.push(b'u'),
|
||||||
|
Type::Fixed => res.push(b'f'),
|
||||||
|
Type::String => res.push(b's'),
|
||||||
|
Type::Object => res.push(b'o'),
|
||||||
|
Type::Array => res.push(b'a'),
|
||||||
|
Type::Fd => res.push(b'h'),
|
||||||
|
Type::Int => res.push(b'i'),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push(0);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn null_terminated_byte_string_literal(string: &str) -> Literal {
|
||||||
|
let mut val = Vec::with_capacity(string.len() + 1);
|
||||||
|
val.extend_from_slice(string.as_bytes());
|
||||||
|
val.push(0);
|
||||||
|
|
||||||
|
Literal::byte_string(&val)
|
||||||
|
}
|
||||||
+337
@@ -0,0 +1,337 @@
|
|||||||
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
|
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
protocol::{Interface, Protocol, Type},
|
||||||
|
util::{description_to_doc_attr, dotted_to_relname, is_keyword, snake_to_camel, to_doc_attr},
|
||||||
|
Side,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn generate_client_objects(protocol: &Protocol) -> TokenStream {
|
||||||
|
protocol.interfaces.iter().map(generate_objects_for).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_objects_for(interface: &Interface) -> TokenStream {
|
||||||
|
let mod_name = Ident::new(&interface.name, Span::call_site());
|
||||||
|
let mod_doc = interface.description.as_ref().map(description_to_doc_attr);
|
||||||
|
let iface_name = Ident::new(&snake_to_camel(&interface.name), Span::call_site());
|
||||||
|
let iface_const_name = format_ident!("{}_INTERFACE", interface.name.to_ascii_uppercase());
|
||||||
|
|
||||||
|
let enums = crate::common::generate_enums_for(interface);
|
||||||
|
let sinces = crate::common::gen_msg_constants(&interface.requests, &interface.events);
|
||||||
|
|
||||||
|
let requests = crate::common::gen_message_enum(
|
||||||
|
&format_ident!("Request"),
|
||||||
|
Side::Client,
|
||||||
|
false,
|
||||||
|
&interface.requests,
|
||||||
|
);
|
||||||
|
let events = crate::common::gen_message_enum(
|
||||||
|
&format_ident!("Event"),
|
||||||
|
Side::Client,
|
||||||
|
true,
|
||||||
|
&interface.events,
|
||||||
|
);
|
||||||
|
|
||||||
|
let parse_body = crate::message_io::gen_parse_body(interface, Side::Client);
|
||||||
|
let write_body = crate::message_io::gen_write_body(interface, Side::Client);
|
||||||
|
let methods = gen_methods(interface);
|
||||||
|
|
||||||
|
let event_ref = if interface.events.is_empty() {
|
||||||
|
"This interface has no events."
|
||||||
|
} else {
|
||||||
|
"See also the [Event] enum for this interface."
|
||||||
|
};
|
||||||
|
let docs = match &interface.description {
|
||||||
|
Some((short, long)) => format!("{short}\n\n{long}\n\n{event_ref}"),
|
||||||
|
None => format!("{}\n\n{}", interface.name, event_ref),
|
||||||
|
};
|
||||||
|
let doc_attr = to_doc_attr(&docs);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#mod_doc
|
||||||
|
pub mod #mod_name {
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::os::unix::io::OwnedFd;
|
||||||
|
|
||||||
|
use super::wayland_client::{
|
||||||
|
backend::{
|
||||||
|
Backend, WeakBackend, smallvec, ObjectData, ObjectId, InvalidId,
|
||||||
|
protocol::{WEnum, Argument, Message, Interface, same_interface}
|
||||||
|
},
|
||||||
|
QueueProxyData, Proxy, Connection, Dispatch, QueueHandle, DispatchError, Weak,
|
||||||
|
};
|
||||||
|
|
||||||
|
#enums
|
||||||
|
#sinces
|
||||||
|
#requests
|
||||||
|
#events
|
||||||
|
|
||||||
|
#doc_attr
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct #iface_name {
|
||||||
|
id: ObjectId,
|
||||||
|
version: u32,
|
||||||
|
data: Option<Arc<dyn ObjectData>>,
|
||||||
|
backend: WeakBackend,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::PartialEq for #iface_name {
|
||||||
|
fn eq(&self, other: &#iface_name) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::Eq for #iface_name {}
|
||||||
|
|
||||||
|
impl PartialEq<Weak<#iface_name>> for #iface_name {
|
||||||
|
fn eq(&self, other: &Weak<#iface_name>) -> bool {
|
||||||
|
self.id == other.id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::borrow::Borrow<ObjectId> for #iface_name {
|
||||||
|
fn borrow(&self) -> &ObjectId {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::hash::Hash for #iface_name {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.id.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::wayland_client::Proxy for #iface_name {
|
||||||
|
type Request<'request> = Request<'request>;
|
||||||
|
type Event = Event;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn interface() -> &'static Interface{
|
||||||
|
&super::#iface_const_name
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn id(&self) -> ObjectId {
|
||||||
|
self.id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn version(&self) -> u32 {
|
||||||
|
self.version
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn data<U: Send + Sync + 'static>(&self) -> Option<&U> {
|
||||||
|
self.data.as_ref().and_then(|arc| arc.data_as_any().downcast_ref::<U>())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn object_data(&self) -> Option<&Arc<dyn ObjectData>> {
|
||||||
|
self.data.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backend(&self) -> &WeakBackend {
|
||||||
|
&self.backend
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_request(&self, req: Self::Request<'_>) -> Result<(), InvalidId> {
|
||||||
|
let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
|
||||||
|
let id = conn.send_request(self, req, None)?;
|
||||||
|
debug_assert!(id.is_null());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_constructor<I: Proxy>(&self, req: Self::Request<'_>, data: Arc<dyn ObjectData>) -> Result<I, InvalidId> {
|
||||||
|
let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
|
||||||
|
let id = conn.send_request(self, req, Some(data))?;
|
||||||
|
Proxy::from_id(&conn, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_id(conn: &Connection, id: ObjectId) -> Result<Self, InvalidId> {
|
||||||
|
if !same_interface(id.interface(), Self::interface()) && !id.is_null() {
|
||||||
|
return Err(InvalidId);
|
||||||
|
}
|
||||||
|
let version = conn.object_info(id.clone()).map(|info| info.version).unwrap_or(0);
|
||||||
|
let data = conn.get_object_data(id.clone()).ok();
|
||||||
|
let backend = conn.backend().downgrade();
|
||||||
|
Ok(#iface_name { id, data, version, backend })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn inert(backend: WeakBackend) -> Self {
|
||||||
|
#iface_name { id: ObjectId::null(), data: None, version: 0, backend }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_event(conn: &Connection, msg: Message<ObjectId, OwnedFd>) -> Result<(Self, Self::Event), DispatchError> {
|
||||||
|
#parse_body
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_request<'a>(&self, conn: &Connection, msg: Self::Request<'a>) -> Result<(Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, Option<(&'static Interface, u32)>), InvalidId> {
|
||||||
|
#write_body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #iface_name {
|
||||||
|
#methods
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_methods(interface: &Interface) -> TokenStream {
|
||||||
|
interface.requests.iter().map(|request| {
|
||||||
|
let created_interface = request.args.iter().find(|arg| arg.typ == Type::NewId).map(|arg| &arg.interface);
|
||||||
|
|
||||||
|
let method_name = format_ident!("{}{}", if is_keyword(&request.name) { "_" } else { "" }, request.name);
|
||||||
|
let enum_variant = Ident::new(&snake_to_camel(&request.name), Span::call_site());
|
||||||
|
|
||||||
|
let fn_args = request.args.iter().flat_map(|arg| {
|
||||||
|
if arg.typ == Type::NewId {
|
||||||
|
if arg.interface.is_none() {
|
||||||
|
// the new_id argument of a bind-like method
|
||||||
|
// it shoudl expand as a (interface, type) tuple, but the type is already handled by
|
||||||
|
// the prototype type parameter, so just put a version here
|
||||||
|
return Some(quote! { version: u32 });
|
||||||
|
} else {
|
||||||
|
// this is a regular new_id, skip it
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg_name = format_ident!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name);
|
||||||
|
|
||||||
|
let arg_type = if let Some(ref enu) = arg.enum_ {
|
||||||
|
let enum_type = dotted_to_relname(enu);
|
||||||
|
quote! { #enum_type }
|
||||||
|
} else {
|
||||||
|
match arg.typ {
|
||||||
|
Type::Uint => quote! { u32 },
|
||||||
|
Type::Int => quote! { i32 },
|
||||||
|
Type::Fixed => quote! { f64 },
|
||||||
|
Type::String => if arg.allow_null { quote!{ Option<String> } } else { quote!{ String } },
|
||||||
|
Type::Array => if arg.allow_null { quote!{ Option<Vec<u8>> } } else { quote!{ Vec<u8> } },
|
||||||
|
Type::Fd => quote! { ::std::os::unix::io::BorrowedFd<'_> },
|
||||||
|
Type::Object => {
|
||||||
|
let iface = arg.interface.as_ref().unwrap();
|
||||||
|
let iface_mod = Ident::new(iface, Span::call_site());
|
||||||
|
let iface_type =
|
||||||
|
Ident::new(&snake_to_camel(iface), Span::call_site());
|
||||||
|
if arg.allow_null { quote! { Option<&super::#iface_mod::#iface_type> } } else { quote! { &super::#iface_mod::#iface_type } }
|
||||||
|
},
|
||||||
|
Type::NewId => unreachable!(),
|
||||||
|
Type::Destructor => panic!("An argument cannot have type \"destructor\"."),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(quote! {
|
||||||
|
#arg_name: #arg_type
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let enum_args = request.args.iter().flat_map(|arg| {
|
||||||
|
let arg_name = format_ident!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name);
|
||||||
|
if arg.enum_.is_some() {
|
||||||
|
Some(quote! { #arg_name: WEnum::Value(#arg_name) })
|
||||||
|
} else if arg.typ == Type::NewId {
|
||||||
|
if arg.interface.is_none() {
|
||||||
|
Some(quote! { #arg_name: (I::interface(), version) })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else if arg.typ == Type::Object {
|
||||||
|
if arg.allow_null {
|
||||||
|
Some(quote! { #arg_name: #arg_name.cloned() })
|
||||||
|
} else {
|
||||||
|
Some(quote! { #arg_name: #arg_name.clone() })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(quote! { #arg_name })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let doc_attr = request
|
||||||
|
.description
|
||||||
|
.as_ref()
|
||||||
|
.map(description_to_doc_attr);
|
||||||
|
|
||||||
|
match created_interface {
|
||||||
|
Some(Some(ref created_interface)) => {
|
||||||
|
// a regular creating request
|
||||||
|
let created_iface_mod = Ident::new(created_interface, Span::call_site());
|
||||||
|
let created_iface_type = Ident::new(&snake_to_camel(created_interface), Span::call_site());
|
||||||
|
quote! {
|
||||||
|
#doc_attr
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn #method_name<U: Send + Sync + 'static, D: Dispatch<super::#created_iface_mod::#created_iface_type, U> + 'static>(&self, #(#fn_args,)* qh: &QueueHandle<D>, udata: U) -> super::#created_iface_mod::#created_iface_type {
|
||||||
|
self.send_constructor(
|
||||||
|
Request::#enum_variant {
|
||||||
|
#(#enum_args),*
|
||||||
|
},
|
||||||
|
qh.make_data::<super::#created_iface_mod::#created_iface_type, U>(udata),
|
||||||
|
).unwrap_or_else(|_| Proxy::inert(self.backend.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(None) => {
|
||||||
|
// a bind-like request
|
||||||
|
quote! {
|
||||||
|
#doc_attr
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn #method_name<I: Proxy + 'static, U: Send + Sync + 'static, D: Dispatch<I, U> + 'static>(&self, #(#fn_args,)* qh: &QueueHandle<D>, udata: U) -> I {
|
||||||
|
self.send_constructor(
|
||||||
|
Request::#enum_variant {
|
||||||
|
#(#enum_args),*
|
||||||
|
},
|
||||||
|
qh.make_data::<I, U>(udata),
|
||||||
|
).unwrap_or_else(|_| Proxy::inert(self.backend.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
// a non-creating request
|
||||||
|
quote! {
|
||||||
|
#doc_attr
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn #method_name(&self, #(#fn_args),*) {
|
||||||
|
let backend = match self.backend.upgrade() {
|
||||||
|
Some(b) => b,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let conn = Connection::from_backend(backend);
|
||||||
|
let _ = conn.send_request(
|
||||||
|
self,
|
||||||
|
Request::#enum_variant {
|
||||||
|
#(#enum_args),*
|
||||||
|
},
|
||||||
|
None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn client_gen() {
|
||||||
|
let protocol_file =
|
||||||
|
std::fs::File::open("./tests/scanner_assets/test-protocol.xml").unwrap();
|
||||||
|
let protocol_parsed = crate::parse::parse(protocol_file);
|
||||||
|
let generated: String = super::generate_client_objects(&protocol_parsed).to_string();
|
||||||
|
let generated = crate::format_rust_code(&generated);
|
||||||
|
|
||||||
|
let reference =
|
||||||
|
std::fs::read_to_string("./tests/scanner_assets/test-client-code.rs").unwrap();
|
||||||
|
let reference = crate::format_rust_code(&reference);
|
||||||
|
|
||||||
|
if reference != generated {
|
||||||
|
let diff = similar::TextDiff::from_lines(&reference, &generated);
|
||||||
|
print!("{}", diff.unified_diff().context_radius(10).header("reference", "generated"));
|
||||||
|
panic!("Generated does not match reference!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+330
@@ -0,0 +1,330 @@
|
|||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
||||||
|
|
||||||
|
use quote::{format_ident, quote, ToTokens};
|
||||||
|
|
||||||
|
use crate::{protocol::*, util::*, Side};
|
||||||
|
|
||||||
|
pub(crate) fn generate_enums_for(interface: &Interface) -> TokenStream {
|
||||||
|
interface.enums.iter().map(ToTokens::into_token_stream).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for Enum {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let enum_decl;
|
||||||
|
let enum_impl;
|
||||||
|
|
||||||
|
let doc_attr = self.description.as_ref().map(description_to_doc_attr);
|
||||||
|
let ident = Ident::new(&snake_to_camel(&self.name), Span::call_site());
|
||||||
|
|
||||||
|
if self.bitfield {
|
||||||
|
let entries = self.entries.iter().map(|entry| {
|
||||||
|
let doc_attr = entry
|
||||||
|
.description
|
||||||
|
.as_ref()
|
||||||
|
.map(description_to_doc_attr)
|
||||||
|
.or_else(|| entry.summary.as_ref().map(|s| to_doc_attr(s)));
|
||||||
|
|
||||||
|
let prefix = if entry.name.chars().next().unwrap().is_numeric() { "_" } else { "" };
|
||||||
|
let ident = format_ident!("{}{}", prefix, snake_to_camel(&entry.name));
|
||||||
|
|
||||||
|
let value = Literal::u32_unsuffixed(entry.value);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#doc_attr
|
||||||
|
const #ident = #value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
enum_decl = quote! {
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#doc_attr
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub struct #ident: u32 {
|
||||||
|
#(#entries)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
enum_impl = quote! {
|
||||||
|
impl std::convert::TryFrom<u32> for #ident {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(val: u32) -> Result<#ident, ()> {
|
||||||
|
#ident::from_bits(val).ok_or(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::convert::From<#ident> for u32 {
|
||||||
|
fn from(val: #ident) -> u32 {
|
||||||
|
val.bits()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let variants = self.entries.iter().map(|entry| {
|
||||||
|
let doc_attr = entry
|
||||||
|
.description
|
||||||
|
.as_ref()
|
||||||
|
.map(description_to_doc_attr)
|
||||||
|
.or_else(|| entry.summary.as_ref().map(|s| to_doc_attr(s)));
|
||||||
|
|
||||||
|
let prefix = if entry.name.chars().next().unwrap().is_numeric() { "_" } else { "" };
|
||||||
|
let variant = format_ident!("{}{}", prefix, snake_to_camel(&entry.name));
|
||||||
|
|
||||||
|
let value = Literal::u32_unsuffixed(entry.value);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#doc_attr
|
||||||
|
#variant = #value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
enum_decl = quote! {
|
||||||
|
#doc_attr
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum #ident {
|
||||||
|
#(#variants,)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let match_arms = self.entries.iter().map(|entry| {
|
||||||
|
let value = Literal::u32_unsuffixed(entry.value);
|
||||||
|
|
||||||
|
let prefix = if entry.name.chars().next().unwrap().is_numeric() { "_" } else { "" };
|
||||||
|
let variant = format_ident!("{}{}", prefix, snake_to_camel(&entry.name));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#value => Ok(#ident::#variant)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
enum_impl = quote! {
|
||||||
|
impl std::convert::TryFrom<u32> for #ident {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(val: u32) -> Result<#ident, ()> {
|
||||||
|
match val {
|
||||||
|
#(#match_arms,)*
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::convert::From<#ident> for u32 {
|
||||||
|
fn from(val: #ident) -> u32 {
|
||||||
|
val as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum_decl.to_tokens(tokens);
|
||||||
|
enum_impl.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn gen_msg_constants(requests: &[Message], events: &[Message]) -> TokenStream {
|
||||||
|
let req_constants = requests.iter().enumerate().map(|(opcode, msg)| {
|
||||||
|
let since_cstname = format_ident!("REQ_{}_SINCE", msg.name.to_ascii_uppercase());
|
||||||
|
let opcode_cstname = format_ident!("REQ_{}_OPCODE", msg.name.to_ascii_uppercase());
|
||||||
|
let since = msg.since;
|
||||||
|
let opcode = opcode as u16;
|
||||||
|
quote! {
|
||||||
|
/// The minimal object version supporting this request
|
||||||
|
pub const #since_cstname: u32 = #since;
|
||||||
|
/// The wire opcode for this request
|
||||||
|
pub const #opcode_cstname: u16 = #opcode;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let evt_constants = events.iter().enumerate().map(|(opcode, msg)| {
|
||||||
|
let since_cstname = format_ident!("EVT_{}_SINCE", msg.name.to_ascii_uppercase());
|
||||||
|
let opcode_cstname = format_ident!("EVT_{}_OPCODE", msg.name.to_ascii_uppercase());
|
||||||
|
let since = msg.since;
|
||||||
|
let opcode = opcode as u16;
|
||||||
|
quote! {
|
||||||
|
/// The minimal object version supporting this event
|
||||||
|
pub const #since_cstname: u32 = #since;
|
||||||
|
/// The wire opcode for this event
|
||||||
|
pub const #opcode_cstname: u16 = #opcode;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#(#req_constants)*
|
||||||
|
#(#evt_constants)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn gen_message_enum(
|
||||||
|
name: &Ident,
|
||||||
|
side: Side,
|
||||||
|
receiver: bool,
|
||||||
|
messages: &[Message],
|
||||||
|
) -> TokenStream {
|
||||||
|
let variants = messages
|
||||||
|
.iter()
|
||||||
|
.map(|msg| {
|
||||||
|
let mut docs = String::new();
|
||||||
|
if let Some((ref short, ref long)) = msg.description {
|
||||||
|
write!(docs, "{}\n\n{}\n", short, long.trim()).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(Type::Destructor) = msg.typ {
|
||||||
|
write!(
|
||||||
|
docs,
|
||||||
|
"\nThis is a destructor, once {} this object cannot be used any longer.",
|
||||||
|
if receiver { "received" } else { "sent" },
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
if msg.since > 1 {
|
||||||
|
write!(docs, "\nOnly available since version {} of the interface", msg.since)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let doc_attr = to_doc_attr(&docs);
|
||||||
|
let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
|
||||||
|
let msg_variant_decl =
|
||||||
|
if msg.args.is_empty() {
|
||||||
|
msg_name.into_token_stream()
|
||||||
|
} else {
|
||||||
|
let fields = msg.args.iter().flat_map(|arg| {
|
||||||
|
let field_name =
|
||||||
|
format_ident!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name);
|
||||||
|
let field_type_inner = if let Some(ref enu) = arg.enum_ {
|
||||||
|
let enum_type = dotted_to_relname(enu);
|
||||||
|
quote! { WEnum<#enum_type> }
|
||||||
|
} else {
|
||||||
|
match arg.typ {
|
||||||
|
Type::Uint => quote! { u32 },
|
||||||
|
Type::Int => quote! { i32 },
|
||||||
|
Type::Fixed => quote! { f64 },
|
||||||
|
Type::String => quote! { String },
|
||||||
|
Type::Array => quote! { Vec<u8> },
|
||||||
|
Type::Fd => {
|
||||||
|
if receiver {
|
||||||
|
quote! { OwnedFd }
|
||||||
|
} else {
|
||||||
|
quote! { std::os::unix::io::BorrowedFd<'a> }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Object => {
|
||||||
|
if let Some(ref iface) = arg.interface {
|
||||||
|
let iface_mod = Ident::new(iface, Span::call_site());
|
||||||
|
let iface_type =
|
||||||
|
Ident::new(&snake_to_camel(iface), Span::call_site());
|
||||||
|
quote! { super::#iface_mod::#iface_type }
|
||||||
|
} else if side == Side::Client {
|
||||||
|
quote! { super::wayland_client::ObjectId }
|
||||||
|
} else {
|
||||||
|
quote! { super::wayland_server::ObjectId }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::NewId if !receiver && side == Side::Client => {
|
||||||
|
// Client-side sending does not have a pre-existing object
|
||||||
|
// so skip serializing it
|
||||||
|
if arg.interface.is_some() {
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
quote! { (&'static Interface, u32) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::NewId => {
|
||||||
|
if let Some(ref iface) = arg.interface {
|
||||||
|
let iface_mod = Ident::new(iface, Span::call_site());
|
||||||
|
let iface_type =
|
||||||
|
Ident::new(&snake_to_camel(iface), Span::call_site());
|
||||||
|
if receiver && side == Side::Server {
|
||||||
|
quote! { New<super::#iface_mod::#iface_type> }
|
||||||
|
} else {
|
||||||
|
quote! { super::#iface_mod::#iface_type }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// bind-like function
|
||||||
|
if side == Side::Client {
|
||||||
|
quote! { (String, u32, super::wayland_client::ObjectId) }
|
||||||
|
} else {
|
||||||
|
quote! { (String, u32, super::wayland_server::ObjectId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Destructor => panic!("An argument cannot have type \"destructor\"."),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let field_type = if arg.allow_null {
|
||||||
|
quote! { Option<#field_type_inner> }
|
||||||
|
} else {
|
||||||
|
field_type_inner.into_token_stream()
|
||||||
|
};
|
||||||
|
|
||||||
|
let doc_attr = arg
|
||||||
|
.description
|
||||||
|
.as_ref()
|
||||||
|
.map(description_to_doc_attr)
|
||||||
|
.or_else(|| arg.summary.as_ref().map(|s| to_doc_attr(s)));
|
||||||
|
|
||||||
|
Some(quote! {
|
||||||
|
#doc_attr
|
||||||
|
#field_name: #field_type
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#msg_name {
|
||||||
|
#(#fields,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#doc_attr
|
||||||
|
#msg_variant_decl
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let opcodes = messages.iter().enumerate().map(|(opcode, msg)| {
|
||||||
|
let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
|
||||||
|
let opcode = opcode as u16;
|
||||||
|
if msg.args.is_empty() {
|
||||||
|
quote! {
|
||||||
|
#name::#msg_name => #opcode
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#name::#msg_name { .. } => #opcode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Placeholder to allow generic argument to be added later, without ABI
|
||||||
|
// break.
|
||||||
|
// TODO Use never type.
|
||||||
|
let (generic, phantom_variant, phantom_case) = if !receiver {
|
||||||
|
(
|
||||||
|
quote! { 'a },
|
||||||
|
quote! { #[doc(hidden)] __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()>, never: std::convert::Infallible } },
|
||||||
|
quote! { #name::__phantom_lifetime { never, .. } => match never {} },
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(quote! {}, quote! {}, quote! {})
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum #name<#generic> {
|
||||||
|
#(#variants,)*
|
||||||
|
#phantom_variant
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<#generic> #name<#generic> {
|
||||||
|
#[doc="Get the opcode number of this message"]
|
||||||
|
pub fn opcode(&self) -> u16 {
|
||||||
|
match *self {
|
||||||
|
#(#opcodes,)*
|
||||||
|
#phantom_case
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
use proc_macro2::TokenStream;
|
||||||
|
|
||||||
|
use crate::protocol::{Interface, Message, Protocol, Type};
|
||||||
|
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
pub fn generate(protocol: &Protocol, with_c_interfaces: bool) -> TokenStream {
|
||||||
|
let interfaces =
|
||||||
|
protocol.interfaces.iter().map(|iface| generate_interface(iface, with_c_interfaces));
|
||||||
|
if with_c_interfaces {
|
||||||
|
let prefix = super::c_interfaces::generate_interfaces_prefix(protocol);
|
||||||
|
quote! {
|
||||||
|
#prefix
|
||||||
|
#(#interfaces)*
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
interfaces.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate_interface(interface: &Interface, with_c: bool) -> TokenStream {
|
||||||
|
let const_name = format_ident!("{}_INTERFACE", interface.name.to_ascii_uppercase());
|
||||||
|
let iface_name = &interface.name;
|
||||||
|
let iface_version = interface.version;
|
||||||
|
let requests = build_messagedesc_list(&interface.requests);
|
||||||
|
let events = build_messagedesc_list(&interface.events);
|
||||||
|
|
||||||
|
let c_name = format_ident!("{}_interface", interface.name);
|
||||||
|
|
||||||
|
if with_c {
|
||||||
|
let c_iface = super::c_interfaces::generate_interface(interface);
|
||||||
|
quote! {
|
||||||
|
pub static #const_name: wayland_backend::protocol::Interface = wayland_backend::protocol::Interface {
|
||||||
|
name: #iface_name,
|
||||||
|
version: #iface_version,
|
||||||
|
requests: #requests,
|
||||||
|
events: #events,
|
||||||
|
c_ptr: Some(unsafe { & #c_name }),
|
||||||
|
};
|
||||||
|
|
||||||
|
#c_iface
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
pub static #const_name: wayland_backend::protocol::Interface = wayland_backend::protocol::Interface {
|
||||||
|
name: #iface_name,
|
||||||
|
version: #iface_version,
|
||||||
|
requests: #requests,
|
||||||
|
events: #events,
|
||||||
|
c_ptr: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_messagedesc_list(list: &[Message]) -> TokenStream {
|
||||||
|
let desc_list = list.iter().map(|message| {
|
||||||
|
let name = &message.name;
|
||||||
|
let since = message.since;
|
||||||
|
let is_destructor = message.typ == Some(Type::Destructor);
|
||||||
|
let signature = message.args.iter().map(|arg| {
|
||||||
|
if arg.typ == Type::NewId && arg.interface.is_none() {
|
||||||
|
// this is a special generic message, it expands to multiple arguments
|
||||||
|
quote! {
|
||||||
|
wayland_backend::protocol::ArgumentType::Str(wayland_backend::protocol::AllowNull::No),
|
||||||
|
wayland_backend::protocol::ArgumentType::Uint,
|
||||||
|
wayland_backend::protocol::ArgumentType::NewId
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let typ = arg.typ.common_type();
|
||||||
|
if arg.typ.nullable() {
|
||||||
|
if arg.allow_null {
|
||||||
|
quote! { wayland_backend::protocol::ArgumentType::#typ(wayland_backend::protocol::AllowNull::Yes) }
|
||||||
|
} else {
|
||||||
|
quote! { wayland_backend::protocol::ArgumentType::#typ(wayland_backend::protocol::AllowNull::No) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! { wayland_backend::protocol::ArgumentType::#typ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let child_interface = match message
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.find(|arg| arg.typ == Type::NewId)
|
||||||
|
.and_then(|arg| arg.interface.as_ref())
|
||||||
|
{
|
||||||
|
Some(name) => {
|
||||||
|
let target_iface = format_ident!("{}_INTERFACE", name.to_ascii_uppercase());
|
||||||
|
quote! { Some(&#target_iface) }
|
||||||
|
}
|
||||||
|
None => quote! { None },
|
||||||
|
};
|
||||||
|
let arg_interfaces = message.args.iter().filter(|arg| arg.typ == Type::Object).map(|arg| {
|
||||||
|
match arg.interface {
|
||||||
|
Some(ref name) => {
|
||||||
|
let target_iface = format_ident!("{}_INTERFACE", name.to_ascii_uppercase());
|
||||||
|
quote! { &#target_iface }
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
quote! { &wayland_backend::protocol::ANONYMOUS_INTERFACE }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: #name,
|
||||||
|
signature: &[ #(#signature),* ],
|
||||||
|
since: #since,
|
||||||
|
is_destructor: #is_destructor,
|
||||||
|
child_interface: #child_interface,
|
||||||
|
arg_interfaces: &[ #(#arg_interfaces),* ],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote!(
|
||||||
|
&[ #(#desc_list),* ]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn interface_gen() {
|
||||||
|
let protocol_file =
|
||||||
|
std::fs::File::open("./tests/scanner_assets/test-protocol.xml").unwrap();
|
||||||
|
let protocol_parsed = crate::parse::parse(protocol_file);
|
||||||
|
let generated: String = super::generate(&protocol_parsed, true).to_string();
|
||||||
|
let generated = crate::format_rust_code(&generated);
|
||||||
|
|
||||||
|
let reference =
|
||||||
|
std::fs::read_to_string("./tests/scanner_assets/test-interfaces.rs").unwrap();
|
||||||
|
let reference = crate::format_rust_code(&reference);
|
||||||
|
|
||||||
|
if reference != generated {
|
||||||
|
let diff = similar::TextDiff::from_lines(&reference, &generated);
|
||||||
|
print!("{}", diff.unified_diff().context_radius(10).header("reference", "generated"));
|
||||||
|
panic!("Generated does not match reference!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
//! Wayland protocol code-generation machinnery
|
||||||
|
//!
|
||||||
|
//! This crate provides procedural macros for generating the rust code associated with a
|
||||||
|
//! Wayland XML protocol specification, for use with the `wayland-client`, `wayland-server`
|
||||||
|
//! and `wayland-backend` crates.
|
||||||
|
//!
|
||||||
|
//! Before trying to use this crate, you may check if the protocol extension you want to use
|
||||||
|
//! is not already exposed in the `wayland-protocols` crate.
|
||||||
|
//!
|
||||||
|
//! ## Example usage
|
||||||
|
//!
|
||||||
|
//! Below is a template for generating the code for a custom protocol client-side. Server-side
|
||||||
|
//! is identical, just replacing `client` by `server`. The path to the XML file is relative to the
|
||||||
|
//! crate root.
|
||||||
|
//!
|
||||||
|
//! ```rust,ignore
|
||||||
|
//! // Generate the bindings in their own module
|
||||||
|
//! pub mod my_protocol {
|
||||||
|
//! use wayland_client;
|
||||||
|
//! // import objects from the core protocol if needed
|
||||||
|
//! use wayland_client::protocol::*;
|
||||||
|
//!
|
||||||
|
//! // This module hosts a low-level representation of the protocol objects
|
||||||
|
//! // you will not need to interact with it yourself, but the code generated
|
||||||
|
//! // by the generate_client_code! macro will use it
|
||||||
|
//! pub mod __interfaces {
|
||||||
|
//! // import the interfaces from the core protocol if needed
|
||||||
|
//! use wayland_client::protocol::__interfaces::*;
|
||||||
|
//! wayland_scanner::generate_interfaces!("./path/to/the/protocol.xml");
|
||||||
|
//! }
|
||||||
|
//! use self::__interfaces::*;
|
||||||
|
//!
|
||||||
|
//! // This macro generates the actual types that represent the wayland objects of
|
||||||
|
//! // your custom protocol
|
||||||
|
//! wayland_scanner::generate_client_code!("./path/to/the/protocol.xml");
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use std::{ffi::OsString, path::PathBuf};
|
||||||
|
|
||||||
|
mod c_interfaces;
|
||||||
|
mod client_gen;
|
||||||
|
mod common;
|
||||||
|
mod interfaces;
|
||||||
|
mod message_io;
|
||||||
|
mod parse;
|
||||||
|
mod protocol;
|
||||||
|
mod server_gen;
|
||||||
|
mod token;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
/// Proc-macro for generating low-level interfaces associated with an XML specification
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn generate_interfaces(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let path: OsString = token::parse_lit_str_token(stream).into();
|
||||||
|
let path = if let Some(manifest_dir) = std::env::var_os("CARGO_MANIFEST_DIR") {
|
||||||
|
let mut buf = PathBuf::from(manifest_dir);
|
||||||
|
buf.push(path);
|
||||||
|
buf
|
||||||
|
} else {
|
||||||
|
path.into()
|
||||||
|
};
|
||||||
|
let file = match std::fs::File::open(&path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(e) => panic!("Failed to open protocol file {}: {}", path.display(), e),
|
||||||
|
};
|
||||||
|
let protocol = parse::parse(file);
|
||||||
|
interfaces::generate(&protocol, true).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Proc-macro for generating client-side API associated with an XML specification
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn generate_client_code(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let path: OsString = token::parse_lit_str_token(stream).into();
|
||||||
|
let path = if let Some(manifest_dir) = std::env::var_os("CARGO_MANIFEST_DIR") {
|
||||||
|
let mut buf = PathBuf::from(manifest_dir);
|
||||||
|
buf.push(path);
|
||||||
|
buf
|
||||||
|
} else {
|
||||||
|
path.into()
|
||||||
|
};
|
||||||
|
let file = match std::fs::File::open(&path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(e) => panic!("Failed to open protocol file {}: {}", path.display(), e),
|
||||||
|
};
|
||||||
|
let protocol = parse::parse(file);
|
||||||
|
client_gen::generate_client_objects(&protocol).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Proc-macro for generating server-side API associated with an XML specification
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn generate_server_code(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let path: OsString = token::parse_lit_str_token(stream).into();
|
||||||
|
let path = if let Some(manifest_dir) = std::env::var_os("CARGO_MANIFEST_DIR") {
|
||||||
|
let mut buf = PathBuf::from(manifest_dir);
|
||||||
|
buf.push(path);
|
||||||
|
buf
|
||||||
|
} else {
|
||||||
|
path.into()
|
||||||
|
};
|
||||||
|
let file = match std::fs::File::open(&path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(e) => panic!("Failed to open protocol file {}: {}", path.display(), e),
|
||||||
|
};
|
||||||
|
let protocol = parse::parse(file);
|
||||||
|
server_gen::generate_server_objects(&protocol).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn format_rust_code(code: &str) -> String {
|
||||||
|
use std::{
|
||||||
|
io::Write,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
if let Ok(mut proc) = Command::new("rustfmt")
|
||||||
|
.arg("--emit=stdout")
|
||||||
|
.arg("--edition=2018")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
//.stderr(Stdio::null())
|
||||||
|
.spawn()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
let stdin = proc.stdin.as_mut().unwrap();
|
||||||
|
stdin.write_all(code.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
if let Ok(output) = proc.wait_with_output() {
|
||||||
|
if output.status.success() {
|
||||||
|
return std::str::from_utf8(&output.stdout).unwrap().to_owned();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("Rustfmt failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
enum Side {
|
||||||
|
/// wayland client applications
|
||||||
|
Client,
|
||||||
|
/// wayland compositors
|
||||||
|
Server,
|
||||||
|
}
|
||||||
+305
@@ -0,0 +1,305 @@
|
|||||||
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
use crate::{protocol::*, util::*, Side};
|
||||||
|
|
||||||
|
pub(crate) fn gen_parse_body(interface: &Interface, side: Side) -> TokenStream {
|
||||||
|
let msgs = match side {
|
||||||
|
Side::Client => &interface.events,
|
||||||
|
Side::Server => &interface.requests,
|
||||||
|
};
|
||||||
|
let object_type = Ident::new(
|
||||||
|
match side {
|
||||||
|
Side::Client => "Proxy",
|
||||||
|
Side::Server => "Resource",
|
||||||
|
},
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
let msg_type = Ident::new(
|
||||||
|
match side {
|
||||||
|
Side::Client => "Event",
|
||||||
|
Side::Server => "Request",
|
||||||
|
},
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let match_arms = msgs.iter().enumerate().map(|(opcode, msg)| {
|
||||||
|
let opcode = opcode as u16;
|
||||||
|
let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
|
||||||
|
let args_pat = msg.args.iter().map(|arg| {
|
||||||
|
let arg_name = Ident::new(
|
||||||
|
&format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name),
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
match arg.typ {
|
||||||
|
Type::Uint => quote!{ Some(Argument::Uint(#arg_name)) },
|
||||||
|
Type::Int => quote!{ Some(Argument::Int(#arg_name)) },
|
||||||
|
Type::String => quote!{ Some(Argument::Str(#arg_name)) },
|
||||||
|
Type::Fixed => quote!{ Some(Argument::Fixed(#arg_name)) },
|
||||||
|
Type::Array => quote!{ Some(Argument::Array(#arg_name)) },
|
||||||
|
Type::Object => quote!{ Some(Argument::Object(#arg_name)) },
|
||||||
|
Type::NewId => quote!{ Some(Argument::NewId(#arg_name)) },
|
||||||
|
Type::Fd => quote!{ Some(Argument::Fd(#arg_name)) },
|
||||||
|
Type::Destructor => panic!("Argument {}.{}.{} has type destructor ?!", interface.name, msg.name, arg.name),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let args_iter = msg.args.iter().map(|_| quote!{ arg_iter.next() });
|
||||||
|
|
||||||
|
let arg_names = msg.args.iter().map(|arg| {
|
||||||
|
let arg_name = format_ident!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name);
|
||||||
|
if arg.enum_.is_some() {
|
||||||
|
quote! { #arg_name: From::from(#arg_name as u32) }
|
||||||
|
} else {
|
||||||
|
match arg.typ {
|
||||||
|
Type::Uint | Type::Int | Type::Fd => quote!{ #arg_name },
|
||||||
|
Type::Fixed => quote!{ #arg_name: (#arg_name as f64) / 256.},
|
||||||
|
Type::String => {
|
||||||
|
if arg.allow_null {
|
||||||
|
quote! {
|
||||||
|
#arg_name: #arg_name.as_ref().map(|s| String::from_utf8_lossy(s.as_bytes()).into_owned())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#arg_name: String::from_utf8_lossy(#arg_name.as_ref().unwrap().as_bytes()).into_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Type::Object => {
|
||||||
|
let create_proxy = if let Some(ref created_interface) = arg.interface {
|
||||||
|
let created_iface_mod = Ident::new(created_interface, Span::call_site());
|
||||||
|
let created_iface_type = Ident::new(&snake_to_camel(created_interface), Span::call_site());
|
||||||
|
quote! {
|
||||||
|
match <super::#created_iface_mod::#created_iface_type as #object_type>::from_id(conn, #arg_name.clone()) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return Err(DispatchError::BadMessage {
|
||||||
|
sender_id: msg.sender_id,
|
||||||
|
interface: Self::interface().name,
|
||||||
|
opcode: msg.opcode
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! { #arg_name.clone() }
|
||||||
|
};
|
||||||
|
if arg.allow_null {
|
||||||
|
quote! {
|
||||||
|
#arg_name: if #arg_name.is_null() { None } else { Some(#create_proxy) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#arg_name: #create_proxy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Type::NewId => {
|
||||||
|
let create_proxy = if let Some(ref created_interface) = arg.interface {
|
||||||
|
let created_iface_mod = Ident::new(created_interface, Span::call_site());
|
||||||
|
let created_iface_type = Ident::new(&snake_to_camel(created_interface), Span::call_site());
|
||||||
|
quote! {
|
||||||
|
match <super::#created_iface_mod::#created_iface_type as #object_type>::from_id(conn, #arg_name.clone()) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return Err(DispatchError::BadMessage {
|
||||||
|
sender_id: msg.sender_id,
|
||||||
|
interface: Self::interface().name,
|
||||||
|
opcode: msg.opcode,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if side == Side::Server {
|
||||||
|
quote! { New::wrap(#arg_name.clone()) }
|
||||||
|
} else {
|
||||||
|
quote! { #arg_name.clone() }
|
||||||
|
};
|
||||||
|
if arg.allow_null {
|
||||||
|
if side == Side::Server {
|
||||||
|
quote! {
|
||||||
|
#arg_name: if #arg_name.is_null() { None } else { Some(New::wrap(#create_proxy)) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#arg_name: if #arg_name.is_null() { None } else { Some(#create_proxy) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if side == Side::Server {
|
||||||
|
quote! {
|
||||||
|
#arg_name: New::wrap(#create_proxy)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#arg_name: #create_proxy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Type::Array => {
|
||||||
|
if arg.allow_null {
|
||||||
|
quote! { if #arg_name.len() == 0 { None } else { Some(*#arg_name) } }
|
||||||
|
} else {
|
||||||
|
quote! { #arg_name: *#arg_name }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Type::Destructor => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#opcode => {
|
||||||
|
if let (#(#args_pat),*) = (#(#args_iter),*) {
|
||||||
|
Ok((me, #msg_type::#msg_name { #(#arg_names),* }))
|
||||||
|
} else {
|
||||||
|
Err(DispatchError::BadMessage { sender_id: msg.sender_id, interface: Self::interface().name, opcode: msg.opcode })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
let me = Self::from_id(conn, msg.sender_id.clone()).unwrap();
|
||||||
|
let mut arg_iter = msg.args.into_iter();
|
||||||
|
match msg.opcode {
|
||||||
|
#(#match_arms),*
|
||||||
|
_ => Err(DispatchError::BadMessage { sender_id: msg.sender_id, interface: Self::interface().name, opcode: msg.opcode }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn gen_write_body(interface: &Interface, side: Side) -> TokenStream {
|
||||||
|
let msgs = match side {
|
||||||
|
Side::Client => &interface.requests,
|
||||||
|
Side::Server => &interface.events,
|
||||||
|
};
|
||||||
|
let msg_type = Ident::new(
|
||||||
|
match side {
|
||||||
|
Side::Client => "Request",
|
||||||
|
Side::Server => "Event",
|
||||||
|
},
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
let arms = msgs.iter().enumerate().map(|(opcode, msg)| {
|
||||||
|
let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
|
||||||
|
let opcode = opcode as u16;
|
||||||
|
let arg_names = msg.args.iter().flat_map(|arg| {
|
||||||
|
if arg.typ == Type::NewId && arg.interface.is_some() && side == Side::Client {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(format_ident!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut child_spec = None;
|
||||||
|
let args = msg.args.iter().flat_map(|arg| {
|
||||||
|
let arg_name = format_ident!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name);
|
||||||
|
|
||||||
|
match arg.typ {
|
||||||
|
Type::Int => vec![if arg.enum_.is_some() { quote!{ Argument::Int(Into::<u32>::into(#arg_name) as i32) } } else { quote!{ Argument::Int(#arg_name) } }],
|
||||||
|
Type::Uint => vec![if arg.enum_.is_some() { quote!{ Argument::Uint(#arg_name.into()) } } else { quote!{ Argument::Uint(#arg_name) } }],
|
||||||
|
Type::Fd => vec![quote!{ Argument::Fd(#arg_name) }],
|
||||||
|
Type::Fixed => vec![quote! { Argument::Fixed((#arg_name * 256.) as i32) }],
|
||||||
|
Type::Object => if arg.allow_null {
|
||||||
|
if side == Side::Server {
|
||||||
|
vec![quote! { if let Some(obj) = #arg_name { Argument::Object(Resource::id(&obj)) } else { Argument::Object(ObjectId::null()) } }]
|
||||||
|
} else {
|
||||||
|
vec![quote! { if let Some(obj) = #arg_name { Argument::Object(Proxy::id(&obj)) } else { Argument::Object(ObjectId::null()) } }]
|
||||||
|
}
|
||||||
|
} else if side == Side::Server {
|
||||||
|
vec![quote!{ Argument::Object(Resource::id(&#arg_name)) }]
|
||||||
|
} else {
|
||||||
|
vec![quote!{ Argument::Object(Proxy::id(&#arg_name)) }]
|
||||||
|
},
|
||||||
|
Type::Array => if arg.allow_null {
|
||||||
|
vec![quote! { if let Some(array) = #arg_name { Argument::Array(Box::new(array)) } else { Argument::Array(Box::new(Vec::new()))}}]
|
||||||
|
} else {
|
||||||
|
vec![quote! { Argument::Array(Box::new(#arg_name)) }]
|
||||||
|
},
|
||||||
|
Type::String => if arg.allow_null {
|
||||||
|
vec![quote! { Argument::Str(#arg_name.map(|s| Box::new(std::ffi::CString::new(s).unwrap()))) }]
|
||||||
|
} else {
|
||||||
|
vec![quote! { Argument::Str(Some(Box::new(std::ffi::CString::new(#arg_name).unwrap()))) }]
|
||||||
|
},
|
||||||
|
Type::NewId => if side == Side::Client {
|
||||||
|
if let Some(ref created_interface) = arg.interface {
|
||||||
|
let created_iface_mod = Ident::new(created_interface, Span::call_site());
|
||||||
|
let created_iface_type = Ident::new(&snake_to_camel(created_interface), Span::call_site());
|
||||||
|
assert!(child_spec.is_none());
|
||||||
|
child_spec = Some(quote! { {
|
||||||
|
let my_info = conn.object_info(self.id())?;
|
||||||
|
Some((super::#created_iface_mod::#created_iface_type::interface(), my_info.version))
|
||||||
|
} });
|
||||||
|
vec![quote! { Argument::NewId(ObjectId::null()) }]
|
||||||
|
} else {
|
||||||
|
assert!(child_spec.is_none());
|
||||||
|
child_spec = Some(quote! {
|
||||||
|
Some((#arg_name.0, #arg_name.1))
|
||||||
|
});
|
||||||
|
vec![
|
||||||
|
quote! {
|
||||||
|
Argument::Str(Some(Box::new(std::ffi::CString::new(#arg_name.0.name).unwrap())))
|
||||||
|
},
|
||||||
|
quote! {
|
||||||
|
Argument::Uint(#arg_name.1)
|
||||||
|
},
|
||||||
|
quote! {
|
||||||
|
Argument::NewId(ObjectId::null())
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// server-side NewId is the same as Object
|
||||||
|
if arg.allow_null {
|
||||||
|
vec![quote! { if let Some(obj) = #arg_name { Argument::NewId(Resource::id(&obj)) } else { Argument::NewId(ObjectId::null()) } }]
|
||||||
|
} else {
|
||||||
|
vec![quote!{ Argument::NewId(Resource::id(&#arg_name)) }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Type::Destructor => panic!("Argument {}.{}.{} has type destructor ?!", interface.name, msg.name, arg.name),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let args = if msg.args.is_empty() {
|
||||||
|
quote! {
|
||||||
|
smallvec::SmallVec::new()
|
||||||
|
}
|
||||||
|
} else if msg.args.len() <= 4 {
|
||||||
|
// Note: Keep in sync with `wayland_backend::protocol::INLINE_ARGS`.
|
||||||
|
// Fits in SmallVec inline capacity
|
||||||
|
quote! { {
|
||||||
|
let mut vec = smallvec::SmallVec::new();
|
||||||
|
#(
|
||||||
|
vec.push(#args);
|
||||||
|
)*
|
||||||
|
vec
|
||||||
|
} }
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
smallvec::SmallVec::from_vec(vec![#(#args),*])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if side == Side::Client {
|
||||||
|
let child_spec = child_spec.unwrap_or_else(|| quote! { None });
|
||||||
|
quote! {
|
||||||
|
#msg_type::#msg_name { #(#arg_names),* } => {
|
||||||
|
let child_spec = #child_spec;
|
||||||
|
let args = #args;
|
||||||
|
Ok((Message {
|
||||||
|
sender_id: self.id.clone(),
|
||||||
|
opcode: #opcode,
|
||||||
|
args
|
||||||
|
}, child_spec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#msg_type::#msg_name { #(#arg_names),* } => Ok(Message {
|
||||||
|
sender_id: self.id.clone(),
|
||||||
|
opcode: #opcode,
|
||||||
|
args: #args,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
match msg {
|
||||||
|
#(#arms,)*
|
||||||
|
#msg_type::__phantom_lifetime { never, .. } => match never {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+405
@@ -0,0 +1,405 @@
|
|||||||
|
use super::protocol::*;
|
||||||
|
use std::{
|
||||||
|
io::{BufRead, BufReader, Read},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use quick_xml::{
|
||||||
|
events::{attributes::Attributes, Event},
|
||||||
|
Reader,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn parse<S: Read>(stream: S) -> Protocol {
|
||||||
|
let mut reader = Reader::from_reader(BufReader::new(stream));
|
||||||
|
let reader_config = reader.config_mut();
|
||||||
|
reader_config.trim_text(true);
|
||||||
|
reader_config.expand_empty_elements = true;
|
||||||
|
parse_protocol(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_utf8_or_panic(txt: Vec<u8>) -> String {
|
||||||
|
match String::from_utf8(txt) {
|
||||||
|
Ok(txt) => txt,
|
||||||
|
Err(e) => panic!("Invalid UTF8: '{}'", String::from_utf8_lossy(&e.into_bytes())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_or_panic<T: FromStr>(txt: &[u8]) -> T {
|
||||||
|
match std::str::from_utf8(txt).ok().and_then(|val| val.parse().ok()) {
|
||||||
|
Some(version) => version,
|
||||||
|
None => panic!(
|
||||||
|
"Invalid value '{}' for parsing type '{}'",
|
||||||
|
String::from_utf8_lossy(txt),
|
||||||
|
std::any::type_name::<T>()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_protocol<R: BufRead>(reader: &mut Reader<R>) -> Protocol {
|
||||||
|
// Check two firsts lines for protocol tag
|
||||||
|
for _ in 0..3 {
|
||||||
|
match reader.read_event_into(&mut Vec::new()) {
|
||||||
|
Ok(Event::Decl(_) | Event::DocType(_) | Event::Comment(_)) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Ok(Event::Start(bytes)) => {
|
||||||
|
assert!(bytes.name().into_inner() == b"protocol", "Missing protocol toplevel tag");
|
||||||
|
if let Some(attr) = bytes
|
||||||
|
.attributes()
|
||||||
|
.filter_map(|res| res.ok())
|
||||||
|
.find(|attr| attr.key.into_inner() == b"name")
|
||||||
|
{
|
||||||
|
return Protocol::new(decode_utf8_or_panic(attr.value.into_owned()));
|
||||||
|
} else {
|
||||||
|
panic!("Protocol must have a name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("Ill-formed protocol file"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("Ill-formed protocol file");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_protocol<R: BufRead>(mut reader: Reader<R>) -> Protocol {
|
||||||
|
let mut protocol = init_protocol(&mut reader);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match reader.read_event_into(&mut Vec::new()) {
|
||||||
|
Ok(Event::Start(bytes)) => {
|
||||||
|
match bytes.name().into_inner() {
|
||||||
|
b"copyright" => {
|
||||||
|
// parse the copyright
|
||||||
|
let mut copyright = String::new();
|
||||||
|
loop {
|
||||||
|
match reader.read_event_into(&mut Vec::new()) {
|
||||||
|
Ok(Event::Text(text)) => {
|
||||||
|
if let Ok(text) = text.decode() {
|
||||||
|
copyright.push_str(&text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Event::CData(cdata)) => {
|
||||||
|
if let Ok(cdata) = String::from_utf8(cdata.into_inner().into())
|
||||||
|
{
|
||||||
|
copyright.push_str(&cdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Event::GeneralRef(byte_ref)) => {
|
||||||
|
if let Ok(Some(c)) = byte_ref.resolve_char_ref() {
|
||||||
|
copyright.push(c);
|
||||||
|
} else if let Ok(content) = byte_ref.xml10_content() {
|
||||||
|
if let Some(s) =
|
||||||
|
quick_xml::escape::resolve_xml_entity(&content)
|
||||||
|
{
|
||||||
|
copyright.push_str(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Event::End(bytes)) => {
|
||||||
|
assert!(
|
||||||
|
bytes.name().into_inner() == "copyright".as_bytes(),
|
||||||
|
"Ill-formed protocol file"
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
e => {
|
||||||
|
panic!("Ill-formed protocol file: {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol.copyright = Some(copyright)
|
||||||
|
}
|
||||||
|
b"interface" => {
|
||||||
|
protocol.interfaces.push(parse_interface(&mut reader, bytes.attributes()));
|
||||||
|
}
|
||||||
|
b"description" => {
|
||||||
|
protocol.description =
|
||||||
|
Some(parse_description(&mut reader, bytes.attributes()));
|
||||||
|
}
|
||||||
|
name => panic!(
|
||||||
|
"Ill-formed protocol file: unexpected token `{}` in protocol {}",
|
||||||
|
String::from_utf8_lossy(name),
|
||||||
|
protocol.name
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Event::End(bytes)) => {
|
||||||
|
let name = bytes.name().into_inner();
|
||||||
|
assert!(
|
||||||
|
name == b"protocol",
|
||||||
|
"Unexpected closing token `{}`",
|
||||||
|
String::from_utf8_lossy(name)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// ignore comments
|
||||||
|
Ok(Event::Comment(_)) => {}
|
||||||
|
e => panic!("Ill-formed protocol file: unexpected token {e:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_interface<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Interface {
|
||||||
|
let mut interface = Interface::new();
|
||||||
|
for attr in attrs.filter_map(|res| res.ok()) {
|
||||||
|
match attr.key.into_inner() {
|
||||||
|
b"name" => interface.name = decode_utf8_or_panic(attr.value.into_owned()),
|
||||||
|
b"version" => interface.version = parse_or_panic(&attr.value),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match reader.read_event_into(&mut Vec::new()) {
|
||||||
|
Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
|
||||||
|
b"description" => {
|
||||||
|
interface.description = Some(parse_description(reader, bytes.attributes()))
|
||||||
|
}
|
||||||
|
b"request" => interface.requests.push(parse_request(reader, bytes.attributes())),
|
||||||
|
b"event" => interface.events.push(parse_event(reader, bytes.attributes())),
|
||||||
|
b"enum" => interface.enums.push(parse_enum(reader, bytes.attributes())),
|
||||||
|
name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
|
||||||
|
},
|
||||||
|
Ok(Event::End(bytes)) if bytes.name().into_inner() == b"interface" => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_description<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> (String, String) {
|
||||||
|
let mut summary = String::new();
|
||||||
|
for attr in attrs.filter_map(|res| res.ok()) {
|
||||||
|
if attr.key.into_inner() == b"summary" {
|
||||||
|
summary = String::from_utf8_lossy(&attr.value)
|
||||||
|
.split_whitespace()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut description = String::new();
|
||||||
|
// Some protocols have comments inside their descriptions, so we need to parse them in a loop and
|
||||||
|
// concatenate the parts into a single block of text
|
||||||
|
loop {
|
||||||
|
match reader.read_event_into(&mut Vec::new()) {
|
||||||
|
Ok(Event::Text(bytes)) => {
|
||||||
|
if !description.is_empty() {
|
||||||
|
description.push_str("\n\n");
|
||||||
|
}
|
||||||
|
description.push_str(&bytes.decode().unwrap_or_default())
|
||||||
|
}
|
||||||
|
Ok(Event::End(bytes)) if bytes.name().into_inner() == b"description" => break,
|
||||||
|
Ok(Event::Comment(_)) => {}
|
||||||
|
e => panic!("Ill-formed protocol file: {e:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(summary, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_request<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Message {
|
||||||
|
let mut request = Message::new();
|
||||||
|
for attr in attrs.filter_map(|res| res.ok()) {
|
||||||
|
match attr.key.into_inner() {
|
||||||
|
b"name" => request.name = decode_utf8_or_panic(attr.value.into_owned()),
|
||||||
|
b"type" => request.typ = Some(parse_type(&attr.value)),
|
||||||
|
b"since" => request.since = parse_or_panic(&attr.value),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match reader.read_event_into(&mut Vec::new()) {
|
||||||
|
Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
|
||||||
|
b"description" => {
|
||||||
|
request.description = Some(parse_description(reader, bytes.attributes()))
|
||||||
|
}
|
||||||
|
b"arg" => request.args.push(parse_arg(reader, bytes.attributes())),
|
||||||
|
name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
|
||||||
|
},
|
||||||
|
Ok(Event::End(bytes)) if bytes.name().into_inner() == b"request" => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_enum<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Enum {
|
||||||
|
let mut enu = Enum::new();
|
||||||
|
for attr in attrs.filter_map(|res| res.ok()) {
|
||||||
|
match attr.key.into_inner() {
|
||||||
|
b"name" => enu.name = decode_utf8_or_panic(attr.value.into_owned()),
|
||||||
|
b"since" => enu.since = parse_or_panic(&attr.value),
|
||||||
|
b"bitfield" if attr.value.as_ref() == b"true" => enu.bitfield = true,
|
||||||
|
b"bitfield" => {}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match reader.read_event_into(&mut Vec::new()) {
|
||||||
|
Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
|
||||||
|
b"description" => {
|
||||||
|
enu.description = Some(parse_description(reader, bytes.attributes()))
|
||||||
|
}
|
||||||
|
b"entry" => enu.entries.push(parse_entry(reader, bytes.attributes())),
|
||||||
|
name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
|
||||||
|
},
|
||||||
|
Ok(Event::End(bytes)) if bytes.name().into_inner() == b"enum" => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enu
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_event<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Message {
|
||||||
|
let mut event = Message::new();
|
||||||
|
for attr in attrs.filter_map(|res| res.ok()) {
|
||||||
|
match attr.key.into_inner() {
|
||||||
|
b"name" => event.name = decode_utf8_or_panic(attr.value.into_owned()),
|
||||||
|
b"type" => event.typ = Some(parse_type(&attr.value)),
|
||||||
|
b"since" => event.since = parse_or_panic(&attr.value),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match reader.read_event_into(&mut Vec::new()) {
|
||||||
|
Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
|
||||||
|
b"description" => {
|
||||||
|
event.description = Some(parse_description(reader, bytes.attributes()))
|
||||||
|
}
|
||||||
|
b"arg" => event.args.push(parse_arg(reader, bytes.attributes())),
|
||||||
|
name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
|
||||||
|
},
|
||||||
|
Ok(Event::End(bytes)) if bytes.name().into_inner() == b"event" => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_arg<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Arg {
|
||||||
|
let mut arg = Arg::new();
|
||||||
|
for attr in attrs.filter_map(|res| res.ok()) {
|
||||||
|
match attr.key.into_inner() {
|
||||||
|
b"name" => arg.name = decode_utf8_or_panic(attr.value.into_owned()),
|
||||||
|
b"type" => arg.typ = parse_type(&attr.value),
|
||||||
|
b"summary" => {
|
||||||
|
arg.summary = Some(
|
||||||
|
String::from_utf8_lossy(&attr.value)
|
||||||
|
.split_whitespace()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" "),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
b"interface" => arg.interface = Some(parse_or_panic(&attr.value)),
|
||||||
|
b"allow-null" if attr.value.as_ref() == b"true" => arg.allow_null = true,
|
||||||
|
b"allow-null" => {}
|
||||||
|
b"enum" => arg.enum_ = Some(decode_utf8_or_panic(attr.value.into_owned())),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match reader.read_event_into(&mut Vec::new()) {
|
||||||
|
Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
|
||||||
|
b"description" => {
|
||||||
|
arg.description = Some(parse_description(reader, bytes.attributes()))
|
||||||
|
}
|
||||||
|
name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
|
||||||
|
},
|
||||||
|
Ok(Event::End(bytes)) if bytes.name().into_inner() == b"arg" => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_type(txt: &[u8]) -> Type {
|
||||||
|
match txt {
|
||||||
|
b"int" => Type::Int,
|
||||||
|
b"uint" => Type::Uint,
|
||||||
|
b"fixed" => Type::Fixed,
|
||||||
|
b"string" => Type::String,
|
||||||
|
b"object" => Type::Object,
|
||||||
|
b"new_id" => Type::NewId,
|
||||||
|
b"array" => Type::Array,
|
||||||
|
b"fd" => Type::Fd,
|
||||||
|
b"destructor" => Type::Destructor,
|
||||||
|
e => panic!("Unexpected type: {}", String::from_utf8_lossy(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_entry<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Entry {
|
||||||
|
let mut entry = Entry::new();
|
||||||
|
for attr in attrs.filter_map(|res| res.ok()) {
|
||||||
|
match attr.key.into_inner() {
|
||||||
|
b"name" => entry.name = decode_utf8_or_panic(attr.value.into_owned()),
|
||||||
|
b"value" => {
|
||||||
|
entry.value = if attr.value.starts_with(b"0x") {
|
||||||
|
if let Some(val) = std::str::from_utf8(&attr.value[2..])
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| u32::from_str_radix(s, 16).ok())
|
||||||
|
{
|
||||||
|
val
|
||||||
|
} else {
|
||||||
|
panic!("Invalid number: {}", String::from_utf8_lossy(&attr.value))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parse_or_panic(&attr.value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
b"since" => entry.since = parse_or_panic(&attr.value),
|
||||||
|
b"summary" => {
|
||||||
|
entry.summary = Some(
|
||||||
|
String::from_utf8_lossy(&attr.value)
|
||||||
|
.split_whitespace()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" "),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match reader.read_event_into(&mut Vec::new()) {
|
||||||
|
Ok(Event::Start(bytes)) => match bytes.name().into_inner() {
|
||||||
|
b"description" => {
|
||||||
|
entry.description = Some(parse_description(reader, bytes.attributes()))
|
||||||
|
}
|
||||||
|
name => panic!("Unexpected token: `{}`", String::from_utf8_lossy(name)),
|
||||||
|
},
|
||||||
|
Ok(Event::End(bytes)) if bytes.name().into_inner() == b"entry" => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn xml_parse() {
|
||||||
|
let protocol_file =
|
||||||
|
std::fs::File::open("./tests/scanner_assets/test-protocol.xml").unwrap();
|
||||||
|
let _ = crate::parse::parse(protocol_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn headerless_xml_parse() {
|
||||||
|
let protocol_file =
|
||||||
|
std::fs::File::open("./tests/scanner_assets/test-headerless-protocol.xml").unwrap();
|
||||||
|
let _ = crate::parse::parse(protocol_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
+154
@@ -0,0 +1,154 @@
|
|||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Protocol {
|
||||||
|
pub name: String,
|
||||||
|
pub copyright: Option<String>,
|
||||||
|
pub description: Option<(String, String)>,
|
||||||
|
pub interfaces: Vec<Interface>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Protocol {
|
||||||
|
pub fn new(name: String) -> Protocol {
|
||||||
|
Protocol { name, copyright: None, description: None, interfaces: Vec::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Interface {
|
||||||
|
pub name: String,
|
||||||
|
pub version: u32,
|
||||||
|
pub description: Option<(String, String)>,
|
||||||
|
pub requests: Vec<Message>,
|
||||||
|
pub events: Vec<Message>,
|
||||||
|
pub enums: Vec<Enum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interface {
|
||||||
|
pub fn new() -> Interface {
|
||||||
|
Interface {
|
||||||
|
name: String::new(),
|
||||||
|
version: 1,
|
||||||
|
description: None,
|
||||||
|
requests: Vec::new(),
|
||||||
|
events: Vec::new(),
|
||||||
|
enums: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Message {
|
||||||
|
pub name: String,
|
||||||
|
pub typ: Option<Type>,
|
||||||
|
pub since: u32,
|
||||||
|
pub description: Option<(String, String)>,
|
||||||
|
pub args: Vec<Arg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message {
|
||||||
|
pub fn new() -> Message {
|
||||||
|
Message { name: String::new(), typ: None, since: 1, description: None, args: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_null(&self) -> bool {
|
||||||
|
self.args
|
||||||
|
.iter()
|
||||||
|
.all(|a| !((a.typ == Type::Object || a.typ == Type::NewId) && a.interface.is_some()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Arg {
|
||||||
|
pub name: String,
|
||||||
|
pub typ: Type,
|
||||||
|
pub interface: Option<String>,
|
||||||
|
pub summary: Option<String>,
|
||||||
|
pub description: Option<(String, String)>,
|
||||||
|
pub allow_null: bool,
|
||||||
|
pub enum_: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Arg {
|
||||||
|
pub fn new() -> Arg {
|
||||||
|
Arg {
|
||||||
|
name: String::new(),
|
||||||
|
typ: Type::Object,
|
||||||
|
interface: None,
|
||||||
|
summary: None,
|
||||||
|
description: None,
|
||||||
|
allow_null: false,
|
||||||
|
enum_: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Enum {
|
||||||
|
pub name: String,
|
||||||
|
pub since: u16,
|
||||||
|
pub description: Option<(String, String)>,
|
||||||
|
pub entries: Vec<Entry>,
|
||||||
|
pub bitfield: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Enum {
|
||||||
|
pub fn new() -> Enum {
|
||||||
|
Enum {
|
||||||
|
name: String::new(),
|
||||||
|
since: 1,
|
||||||
|
description: None,
|
||||||
|
entries: Vec::new(),
|
||||||
|
bitfield: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Entry {
|
||||||
|
pub name: String,
|
||||||
|
pub value: u32,
|
||||||
|
pub since: u16,
|
||||||
|
pub description: Option<(String, String)>,
|
||||||
|
pub summary: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entry {
|
||||||
|
pub fn new() -> Entry {
|
||||||
|
Entry { name: String::new(), value: 0, since: 1, description: None, summary: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub enum Type {
|
||||||
|
Int,
|
||||||
|
Uint,
|
||||||
|
Fixed,
|
||||||
|
String,
|
||||||
|
Object,
|
||||||
|
NewId,
|
||||||
|
Array,
|
||||||
|
Fd,
|
||||||
|
Destructor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
pub fn nullable(self) -> bool {
|
||||||
|
matches!(self, Type::String | Type::Object)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn common_type(self) -> TokenStream {
|
||||||
|
match self {
|
||||||
|
Type::Int => quote!(Int),
|
||||||
|
Type::Uint => quote!(Uint),
|
||||||
|
Type::Fixed => quote!(Fixed),
|
||||||
|
Type::Array => quote!(Array),
|
||||||
|
Type::Fd => quote!(Fd),
|
||||||
|
Type::String => quote!(Str),
|
||||||
|
Type::Object => quote!(Object),
|
||||||
|
Type::NewId => quote!(NewId),
|
||||||
|
Type::Destructor => panic!("Destructor is not a valid argument type."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+298
@@ -0,0 +1,298 @@
|
|||||||
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
|
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
protocol::{Interface, Protocol, Type},
|
||||||
|
util::{description_to_doc_attr, dotted_to_relname, is_keyword, snake_to_camel, to_doc_attr},
|
||||||
|
Side,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn generate_server_objects(protocol: &Protocol) -> TokenStream {
|
||||||
|
protocol
|
||||||
|
.interfaces
|
||||||
|
.iter()
|
||||||
|
.filter(|iface| iface.name != "wl_display")
|
||||||
|
.map(generate_objects_for)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_objects_for(interface: &Interface) -> TokenStream {
|
||||||
|
let mod_name = Ident::new(&interface.name, Span::call_site());
|
||||||
|
let mod_doc = interface.description.as_ref().map(description_to_doc_attr);
|
||||||
|
let iface_name = Ident::new(&snake_to_camel(&interface.name), Span::call_site());
|
||||||
|
let iface_const_name = format_ident!("{}_INTERFACE", interface.name.to_ascii_uppercase());
|
||||||
|
|
||||||
|
let enums = crate::common::generate_enums_for(interface);
|
||||||
|
let msg_constants = crate::common::gen_msg_constants(&interface.requests, &interface.events);
|
||||||
|
|
||||||
|
let requests = crate::common::gen_message_enum(
|
||||||
|
&format_ident!("Request"),
|
||||||
|
Side::Server,
|
||||||
|
true,
|
||||||
|
&interface.requests,
|
||||||
|
);
|
||||||
|
let events = crate::common::gen_message_enum(
|
||||||
|
&format_ident!("Event"),
|
||||||
|
Side::Server,
|
||||||
|
false,
|
||||||
|
&interface.events,
|
||||||
|
);
|
||||||
|
|
||||||
|
let parse_body = if interface.name == "wl_registry" {
|
||||||
|
quote! { unimplemented!("`wl_registry` is implemented internally in `wayland-server`") }
|
||||||
|
} else {
|
||||||
|
crate::message_io::gen_parse_body(interface, Side::Server)
|
||||||
|
};
|
||||||
|
let write_body = crate::message_io::gen_write_body(interface, Side::Server);
|
||||||
|
let methods = gen_methods(interface);
|
||||||
|
|
||||||
|
let event_ref = if interface.requests.is_empty() {
|
||||||
|
"This interface has no requests."
|
||||||
|
} else {
|
||||||
|
"See also the [Request] enum for this interface."
|
||||||
|
};
|
||||||
|
let docs = match &interface.description {
|
||||||
|
Some((short, long)) => format!("{short}\n\n{long}\n\n{event_ref}"),
|
||||||
|
None => format!("{}\n\n{}", interface.name, event_ref),
|
||||||
|
};
|
||||||
|
let doc_attr = to_doc_attr(&docs);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#mod_doc
|
||||||
|
pub mod #mod_name {
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::os::unix::io::OwnedFd;
|
||||||
|
|
||||||
|
use super::wayland_server::{
|
||||||
|
backend::{
|
||||||
|
smallvec, ObjectData, ObjectId, InvalidId, WeakHandle,
|
||||||
|
protocol::{WEnum, Argument, Message, Interface, same_interface}
|
||||||
|
},
|
||||||
|
Resource, Dispatch, DisplayHandle, DispatchError, ResourceData, New, Weak,
|
||||||
|
};
|
||||||
|
|
||||||
|
#enums
|
||||||
|
#msg_constants
|
||||||
|
#requests
|
||||||
|
#events
|
||||||
|
|
||||||
|
#doc_attr
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct #iface_name {
|
||||||
|
id: ObjectId,
|
||||||
|
version: u32,
|
||||||
|
data: Option<Arc<dyn std::any::Any + Send + Sync + 'static>>,
|
||||||
|
handle: WeakHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::PartialEq for #iface_name {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &#iface_name) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::Eq for #iface_name {}
|
||||||
|
|
||||||
|
impl PartialEq<Weak<#iface_name>> for #iface_name {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Weak<#iface_name>) -> bool {
|
||||||
|
self.id == other.id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::borrow::Borrow<ObjectId> for #iface_name {
|
||||||
|
#[inline]
|
||||||
|
fn borrow(&self) -> &ObjectId {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::hash::Hash for #iface_name {
|
||||||
|
#[inline]
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.id.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::wayland_server::Resource for #iface_name {
|
||||||
|
type Request = Request;
|
||||||
|
type Event<'event> = Event<'event>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn interface() -> &'static Interface{
|
||||||
|
&super::#iface_const_name
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn id(&self) -> ObjectId {
|
||||||
|
self.id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn version(&self) -> u32 {
|
||||||
|
self.version
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn data<U: 'static>(&self) -> Option<&U> {
|
||||||
|
self.data.as_ref().and_then(|arc| (&**arc).downcast_ref::<ResourceData<Self, U>>()).map(|data| &data.udata)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn object_data(&self) -> Option<&Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
|
self.data.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(&self) -> &WeakHandle {
|
||||||
|
&self.handle
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_id(conn: &DisplayHandle, id: ObjectId) -> Result<Self, InvalidId> {
|
||||||
|
if !same_interface(id.interface(), Self::interface()) && !id.is_null(){
|
||||||
|
return Err(InvalidId)
|
||||||
|
}
|
||||||
|
let version = conn.object_info(id.clone()).map(|info| info.version).unwrap_or(0);
|
||||||
|
let data = conn.get_object_data(id.clone()).ok();
|
||||||
|
Ok(#iface_name { id, data, version, handle: conn.backend_handle().downgrade() })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_event(&self, evt: Self::Event<'_>) -> Result<(), InvalidId> {
|
||||||
|
let handle = DisplayHandle::from(self.handle.upgrade().ok_or(InvalidId)?);
|
||||||
|
handle.send_event(self, evt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_request(conn: &DisplayHandle, msg: Message<ObjectId, OwnedFd>) -> Result<(Self, Self::Request), DispatchError> {
|
||||||
|
#parse_body
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_event<'a>(&self, conn: &DisplayHandle, msg: Self::Event<'a>) -> Result<Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, InvalidId> {
|
||||||
|
#write_body
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __set_object_data(&mut self, odata: std::sync::Arc<dyn std::any::Any + Send + Sync + 'static>) {
|
||||||
|
self.data = Some(odata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #iface_name {
|
||||||
|
#methods
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_methods(interface: &Interface) -> TokenStream {
|
||||||
|
interface
|
||||||
|
.events
|
||||||
|
.iter()
|
||||||
|
.map(|request| {
|
||||||
|
let method_name = format_ident!(
|
||||||
|
"{}{}",
|
||||||
|
if is_keyword(&request.name) { "_" } else { "" },
|
||||||
|
request.name
|
||||||
|
);
|
||||||
|
let enum_variant = Ident::new(&snake_to_camel(&request.name), Span::call_site());
|
||||||
|
|
||||||
|
let fn_args = request.args.iter().flat_map(|arg| {
|
||||||
|
let arg_name =
|
||||||
|
format_ident!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name);
|
||||||
|
|
||||||
|
let arg_type = if let Some(ref enu) = arg.enum_ {
|
||||||
|
let enum_type = dotted_to_relname(enu);
|
||||||
|
quote! { #enum_type }
|
||||||
|
} else {
|
||||||
|
match arg.typ {
|
||||||
|
Type::Uint => quote! { u32 },
|
||||||
|
Type::Int => quote! { i32 },
|
||||||
|
Type::Fixed => quote! { f64 },
|
||||||
|
Type::String => {
|
||||||
|
if arg.allow_null {
|
||||||
|
quote! { Option<String> }
|
||||||
|
} else {
|
||||||
|
quote! { String }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Array => {
|
||||||
|
if arg.allow_null {
|
||||||
|
quote! { Option<Vec<u8>> }
|
||||||
|
} else {
|
||||||
|
quote! { Vec<u8> }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Fd => quote! { ::std::os::unix::io::BorrowedFd<'_> },
|
||||||
|
Type::Object | Type::NewId => {
|
||||||
|
let iface = arg.interface.as_ref().unwrap();
|
||||||
|
let iface_mod = Ident::new(iface, Span::call_site());
|
||||||
|
let iface_type = Ident::new(&snake_to_camel(iface), Span::call_site());
|
||||||
|
if arg.allow_null {
|
||||||
|
quote! { Option<&super::#iface_mod::#iface_type> }
|
||||||
|
} else {
|
||||||
|
quote! { &super::#iface_mod::#iface_type }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Destructor => panic!("An argument cannot have type \"destructor\"."),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(quote! {
|
||||||
|
#arg_name: #arg_type
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let enum_args = request.args.iter().flat_map(|arg| {
|
||||||
|
let arg_name =
|
||||||
|
format_ident!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name);
|
||||||
|
if arg.enum_.is_some() {
|
||||||
|
Some(quote! { #arg_name: WEnum::Value(#arg_name) })
|
||||||
|
} else if arg.typ == Type::Object || arg.typ == Type::NewId {
|
||||||
|
if arg.allow_null {
|
||||||
|
Some(quote! { #arg_name: #arg_name.cloned() })
|
||||||
|
} else {
|
||||||
|
Some(quote! { #arg_name: #arg_name.clone() })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(quote! { #arg_name })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let doc_attr = request.description.as_ref().map(description_to_doc_attr);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#doc_attr
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn #method_name(&self, #(#fn_args),*) {
|
||||||
|
let _ = self.send_event(
|
||||||
|
Event::#enum_variant {
|
||||||
|
#(#enum_args),*
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn server_gen() {
|
||||||
|
let protocol_file =
|
||||||
|
std::fs::File::open("./tests/scanner_assets/test-protocol.xml").unwrap();
|
||||||
|
let protocol_parsed = crate::parse::parse(protocol_file);
|
||||||
|
let generated: String = super::generate_server_objects(&protocol_parsed).to_string();
|
||||||
|
let generated = crate::format_rust_code(&generated);
|
||||||
|
|
||||||
|
let reference =
|
||||||
|
std::fs::read_to_string("./tests/scanner_assets/test-server-code.rs").unwrap();
|
||||||
|
let reference = crate::format_rust_code(&reference);
|
||||||
|
|
||||||
|
if reference != generated {
|
||||||
|
let diff = similar::TextDiff::from_lines(&reference, &generated);
|
||||||
|
print!("{}", diff.unified_diff().context_radius(10).header("reference", "generated"));
|
||||||
|
panic!("Generated does not match reference!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+186
@@ -0,0 +1,186 @@
|
|||||||
|
// `bytes`, `next_chr`, `parse_lit_str`, `parse_lit_str_cooked` and `parse_lit_str_raw` are adapted
|
||||||
|
// from syn:
|
||||||
|
// https://github.com/dtolnay/syn/blob/362ee2d02df3f1b2e74c7b7a4cf2ed3c106404c9/src/lit.rs#L1062-L1167
|
||||||
|
// and
|
||||||
|
// https://github.com/dtolnay/syn/blob/362ee2d02df3f1b2e74c7b7a4cf2ed3c106404c9/src/lit.rs#L1327-L1388
|
||||||
|
|
||||||
|
/// Get the byte at offset idx, or a default of `b'\0'` if we're looking
|
||||||
|
/// past the end of the input buffer.
|
||||||
|
fn byte(s: &str, idx: usize) -> u8 {
|
||||||
|
if idx < s.len() {
|
||||||
|
s.as_bytes()[idx]
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_chr(s: &str) -> char {
|
||||||
|
s.chars().next().unwrap_or('\0')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns (content, suffix).
|
||||||
|
fn parse_lit_str(s: &str) -> String {
|
||||||
|
match byte(s, 0) {
|
||||||
|
b'"' => parse_lit_str_cooked(s),
|
||||||
|
b'r' => parse_lit_str_raw(s),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clippy false positive
|
||||||
|
// https://github.com/rust-lang-nursery/rust-clippy/issues/2329
|
||||||
|
#[allow(clippy::needless_continue)]
|
||||||
|
fn parse_lit_str_cooked(mut s: &str) -> String {
|
||||||
|
assert_eq!(byte(s, 0), b'"');
|
||||||
|
s = &s[1..];
|
||||||
|
|
||||||
|
let mut content = String::new();
|
||||||
|
'outer: loop {
|
||||||
|
let ch = match byte(s, 0) {
|
||||||
|
b'"' => break,
|
||||||
|
b'\\' => {
|
||||||
|
let b = byte(s, 1);
|
||||||
|
s = &s[2..];
|
||||||
|
match b {
|
||||||
|
b'x' => {
|
||||||
|
let (byte, rest) = backslash_x(s);
|
||||||
|
s = rest;
|
||||||
|
assert!(byte <= 0x80, "Invalid \\x byte in string literal");
|
||||||
|
char::from_u32(u32::from(byte)).unwrap()
|
||||||
|
}
|
||||||
|
b'u' => {
|
||||||
|
let (chr, rest) = backslash_u(s);
|
||||||
|
s = rest;
|
||||||
|
chr
|
||||||
|
}
|
||||||
|
b'n' => '\n',
|
||||||
|
b'r' => '\r',
|
||||||
|
b't' => '\t',
|
||||||
|
b'\\' => '\\',
|
||||||
|
b'0' => '\0',
|
||||||
|
b'\'' => '\'',
|
||||||
|
b'"' => '"',
|
||||||
|
b'\r' | b'\n' => loop {
|
||||||
|
let ch = next_chr(s);
|
||||||
|
if ch.is_whitespace() {
|
||||||
|
s = &s[ch.len_utf8()..];
|
||||||
|
} else {
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
b => panic!("unexpected byte {b:?} after \\ character in byte literal"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b'\r' => {
|
||||||
|
assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
|
||||||
|
s = &s[2..];
|
||||||
|
'\n'
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let ch = next_chr(s);
|
||||||
|
s = &s[ch.len_utf8()..];
|
||||||
|
ch
|
||||||
|
}
|
||||||
|
};
|
||||||
|
content.push(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(s.starts_with('"'));
|
||||||
|
content
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_lit_str_raw(mut s: &str) -> String {
|
||||||
|
assert_eq!(byte(s, 0), b'r');
|
||||||
|
s = &s[1..];
|
||||||
|
|
||||||
|
let mut pounds = 0;
|
||||||
|
while byte(s, pounds) == b'#' {
|
||||||
|
pounds += 1;
|
||||||
|
}
|
||||||
|
assert_eq!(byte(s, pounds), b'"');
|
||||||
|
let close = s.rfind('"').unwrap();
|
||||||
|
for end in s[close + 1..close + 1 + pounds].bytes() {
|
||||||
|
assert_eq!(end, b'#');
|
||||||
|
}
|
||||||
|
|
||||||
|
s[pounds + 1..close].to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backslash_x(s: &str) -> (u8, &str) {
|
||||||
|
let mut ch = 0;
|
||||||
|
let b0 = byte(s, 0);
|
||||||
|
let b1 = byte(s, 1);
|
||||||
|
ch += 0x10
|
||||||
|
* match b0 {
|
||||||
|
b'0'..=b'9' => b0 - b'0',
|
||||||
|
b'a'..=b'f' => 10 + (b0 - b'a'),
|
||||||
|
b'A'..=b'F' => 10 + (b0 - b'A'),
|
||||||
|
_ => panic!("unexpected non-hex character after \\x"),
|
||||||
|
};
|
||||||
|
ch += match b1 {
|
||||||
|
b'0'..=b'9' => b1 - b'0',
|
||||||
|
b'a'..=b'f' => 10 + (b1 - b'a'),
|
||||||
|
b'A'..=b'F' => 10 + (b1 - b'A'),
|
||||||
|
_ => panic!("unexpected non-hex character after \\x"),
|
||||||
|
};
|
||||||
|
(ch, &s[2..])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backslash_u(mut s: &str) -> (char, &str) {
|
||||||
|
if byte(s, 0) != b'{' {
|
||||||
|
panic!("{}", "expected { after \\u");
|
||||||
|
}
|
||||||
|
s = &s[1..];
|
||||||
|
|
||||||
|
let mut ch = 0;
|
||||||
|
let mut digits = 0;
|
||||||
|
loop {
|
||||||
|
let b = byte(s, 0);
|
||||||
|
let digit = match b {
|
||||||
|
b'0'..=b'9' => b - b'0',
|
||||||
|
b'a'..=b'f' => 10 + b - b'a',
|
||||||
|
b'A'..=b'F' => 10 + b - b'A',
|
||||||
|
b'_' if digits > 0 => {
|
||||||
|
s = &s[1..];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
b'}' if digits == 0 => panic!("invalid empty unicode escape"),
|
||||||
|
b'}' => break,
|
||||||
|
_ => panic!("unexpected non-hex character after \\u"),
|
||||||
|
};
|
||||||
|
if digits == 6 {
|
||||||
|
panic!("overlong unicode escape (must have at most 6 hex digits)");
|
||||||
|
}
|
||||||
|
ch *= 0x10;
|
||||||
|
ch += u32::from(digit);
|
||||||
|
digits += 1;
|
||||||
|
s = &s[1..];
|
||||||
|
}
|
||||||
|
assert!(byte(s, 0) == b'}');
|
||||||
|
s = &s[1..];
|
||||||
|
|
||||||
|
if let Some(ch) = char::from_u32(ch) {
|
||||||
|
(ch, s)
|
||||||
|
} else {
|
||||||
|
panic!("character code {ch:x} is not a valid unicode character");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// End of code adapted from syn
|
||||||
|
|
||||||
|
pub fn parse_lit_str_token(mut stream: proc_macro::TokenStream) -> String {
|
||||||
|
loop {
|
||||||
|
let mut iter = stream.into_iter();
|
||||||
|
let token = iter.next().expect("expected string argument");
|
||||||
|
assert!(iter.next().is_none(), "unexpected trailing token");
|
||||||
|
let literal = match token {
|
||||||
|
proc_macro::TokenTree::Literal(literal) => literal,
|
||||||
|
proc_macro::TokenTree::Group(group) => {
|
||||||
|
stream = group.stream();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => panic!("expected string argument found `{token:?}`"),
|
||||||
|
};
|
||||||
|
return parse_lit_str(&literal.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
|
use quote::{quote, ToTokens};
|
||||||
|
|
||||||
|
pub(crate) fn to_doc_attr(text: &str) -> TokenStream {
|
||||||
|
let text = text.lines().map(str::trim).collect::<Vec<_>>().join("\n");
|
||||||
|
let text = text.trim();
|
||||||
|
|
||||||
|
quote!(#[doc = #text])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn description_to_doc_attr((short, long): &(String, String)) -> TokenStream {
|
||||||
|
to_doc_attr(&format!("{short}\n\n{long}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_keyword(txt: &str) -> bool {
|
||||||
|
matches!(
|
||||||
|
txt,
|
||||||
|
"abstract"
|
||||||
|
| "alignof"
|
||||||
|
| "as"
|
||||||
|
| "become"
|
||||||
|
| "box"
|
||||||
|
| "break"
|
||||||
|
| "const"
|
||||||
|
| "continue"
|
||||||
|
| "crate"
|
||||||
|
| "do"
|
||||||
|
| "else"
|
||||||
|
| "enum"
|
||||||
|
| "extern"
|
||||||
|
| "false"
|
||||||
|
| "final"
|
||||||
|
| "fn"
|
||||||
|
| "for"
|
||||||
|
| "if"
|
||||||
|
| "impl"
|
||||||
|
| "in"
|
||||||
|
| "let"
|
||||||
|
| "loop"
|
||||||
|
| "macro"
|
||||||
|
| "match"
|
||||||
|
| "mod"
|
||||||
|
| "move"
|
||||||
|
| "mut"
|
||||||
|
| "offsetof"
|
||||||
|
| "override"
|
||||||
|
| "priv"
|
||||||
|
| "proc"
|
||||||
|
| "pub"
|
||||||
|
| "pure"
|
||||||
|
| "ref"
|
||||||
|
| "return"
|
||||||
|
| "Self"
|
||||||
|
| "self"
|
||||||
|
| "sizeof"
|
||||||
|
| "static"
|
||||||
|
| "struct"
|
||||||
|
| "super"
|
||||||
|
| "trait"
|
||||||
|
| "true"
|
||||||
|
| "type"
|
||||||
|
| "typeof"
|
||||||
|
| "unsafe"
|
||||||
|
| "unsized"
|
||||||
|
| "use"
|
||||||
|
| "virtual"
|
||||||
|
| "where"
|
||||||
|
| "while"
|
||||||
|
| "yield"
|
||||||
|
| "__handler"
|
||||||
|
| "__object"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_camel_keyword(txt: &str) -> bool {
|
||||||
|
matches!(txt, "Self")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn snake_to_camel(input: &str) -> String {
|
||||||
|
let result = input
|
||||||
|
.split('_')
|
||||||
|
.flat_map(|s| {
|
||||||
|
let mut first = true;
|
||||||
|
s.chars().map(move |c| {
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
c.to_ascii_uppercase()
|
||||||
|
} else {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
if is_camel_keyword(&result) {
|
||||||
|
format!("_{}", &result)
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dotted_to_relname(input: &str) -> TokenStream {
|
||||||
|
let mut it = input.split('.');
|
||||||
|
match (it.next(), it.next()) {
|
||||||
|
(Some(module), Some(name)) => {
|
||||||
|
let module = Ident::new(module, Span::call_site());
|
||||||
|
let ident = Ident::new(&snake_to_camel(name), Span::call_site());
|
||||||
|
quote::quote!(super::#module::#ident)
|
||||||
|
}
|
||||||
|
(Some(name), None) => {
|
||||||
|
Ident::new(&snake_to_camel(name), Span::call_site()).into_token_stream()
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
|||||||
|
<protocol name="test-protocol">
|
||||||
|
</protocol>
|
||||||
@@ -0,0 +1,524 @@
|
|||||||
|
use std::ptr::null;
|
||||||
|
struct SyncWrapper<T>(T);
|
||||||
|
unsafe impl<T> Sync for SyncWrapper<T> {}
|
||||||
|
static types_null: SyncWrapper<[*const wayland_backend::protocol::wl_interface; 6]> =
|
||||||
|
SyncWrapper([null::<wayland_backend::protocol::wl_interface>(); 6]);
|
||||||
|
pub static WL_DISPLAY_INTERFACE: wayland_backend::protocol::Interface =
|
||||||
|
wayland_backend::protocol::Interface {
|
||||||
|
name: "wl_display",
|
||||||
|
version: 1u32,
|
||||||
|
requests: &[
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "sync",
|
||||||
|
signature: &[wayland_backend::protocol::ArgumentType::NewId],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: Some(&WL_CALLBACK_INTERFACE),
|
||||||
|
arg_interfaces: &[],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "get_registry",
|
||||||
|
signature: &[wayland_backend::protocol::ArgumentType::NewId],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: Some(&WL_REGISTRY_INTERFACE),
|
||||||
|
arg_interfaces: &[],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
events: &[
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "error",
|
||||||
|
signature: &[
|
||||||
|
wayland_backend::protocol::ArgumentType::Object(
|
||||||
|
wayland_backend::protocol::AllowNull::No,
|
||||||
|
),
|
||||||
|
wayland_backend::protocol::ArgumentType::Uint,
|
||||||
|
wayland_backend::protocol::ArgumentType::Str(
|
||||||
|
wayland_backend::protocol::AllowNull::No,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[&wayland_backend::protocol::ANONYMOUS_INTERFACE],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "delete_id",
|
||||||
|
signature: &[wayland_backend::protocol::ArgumentType::Uint],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
c_ptr: Some(unsafe { &wl_display_interface }),
|
||||||
|
};
|
||||||
|
static wl_display_requests_sync_types: SyncWrapper<
|
||||||
|
[*const wayland_backend::protocol::wl_interface; 1],
|
||||||
|
> = SyncWrapper([&wl_callback_interface as *const wayland_backend::protocol::wl_interface]);
|
||||||
|
static wl_display_requests_get_registry_types: SyncWrapper<
|
||||||
|
[*const wayland_backend::protocol::wl_interface; 1],
|
||||||
|
> = SyncWrapper([&wl_registry_interface as *const wayland_backend::protocol::wl_interface]);
|
||||||
|
static wl_display_requests: SyncWrapper<[wayland_backend::protocol::wl_message; 2]> =
|
||||||
|
SyncWrapper([
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"sync\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"n\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: wl_display_requests_sync_types.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"get_registry\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"n\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: wl_display_requests_get_registry_types.0.as_ptr(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
static wl_display_events: SyncWrapper<[wayland_backend::protocol::wl_message; 2]> = SyncWrapper([
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"error\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"ous\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"delete_id\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"u\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
pub static wl_display_interface: wayland_backend::protocol::wl_interface =
|
||||||
|
wayland_backend::protocol::wl_interface {
|
||||||
|
name: b"wl_display\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
version: 1,
|
||||||
|
request_count: 2,
|
||||||
|
requests: wl_display_requests.0.as_ptr(),
|
||||||
|
event_count: 2,
|
||||||
|
events: wl_display_events.0.as_ptr(),
|
||||||
|
};
|
||||||
|
pub static WL_REGISTRY_INTERFACE: wayland_backend::protocol::Interface =
|
||||||
|
wayland_backend::protocol::Interface {
|
||||||
|
name: "wl_registry",
|
||||||
|
version: 1u32,
|
||||||
|
requests: &[wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "bind",
|
||||||
|
signature: &[
|
||||||
|
wayland_backend::protocol::ArgumentType::Uint,
|
||||||
|
wayland_backend::protocol::ArgumentType::Str(
|
||||||
|
wayland_backend::protocol::AllowNull::No,
|
||||||
|
),
|
||||||
|
wayland_backend::protocol::ArgumentType::Uint,
|
||||||
|
wayland_backend::protocol::ArgumentType::NewId,
|
||||||
|
],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
}],
|
||||||
|
events: &[
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "global",
|
||||||
|
signature: &[
|
||||||
|
wayland_backend::protocol::ArgumentType::Uint,
|
||||||
|
wayland_backend::protocol::ArgumentType::Str(
|
||||||
|
wayland_backend::protocol::AllowNull::No,
|
||||||
|
),
|
||||||
|
wayland_backend::protocol::ArgumentType::Uint,
|
||||||
|
],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "global_remove",
|
||||||
|
signature: &[wayland_backend::protocol::ArgumentType::Uint],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
c_ptr: Some(unsafe { &wl_registry_interface }),
|
||||||
|
};
|
||||||
|
static wl_registry_requests: SyncWrapper<[wayland_backend::protocol::wl_message; 1]> =
|
||||||
|
SyncWrapper([wayland_backend::protocol::wl_message {
|
||||||
|
name: b"bind\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"usun\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
}]);
|
||||||
|
static wl_registry_events: SyncWrapper<[wayland_backend::protocol::wl_message; 2]> = SyncWrapper([
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"global\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"usu\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"global_remove\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"u\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
pub static wl_registry_interface: wayland_backend::protocol::wl_interface =
|
||||||
|
wayland_backend::protocol::wl_interface {
|
||||||
|
name: b"wl_registry\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
version: 1,
|
||||||
|
request_count: 1,
|
||||||
|
requests: wl_registry_requests.0.as_ptr(),
|
||||||
|
event_count: 2,
|
||||||
|
events: wl_registry_events.0.as_ptr(),
|
||||||
|
};
|
||||||
|
pub static WL_CALLBACK_INTERFACE: wayland_backend::protocol::Interface =
|
||||||
|
wayland_backend::protocol::Interface {
|
||||||
|
name: "wl_callback",
|
||||||
|
version: 1u32,
|
||||||
|
requests: &[],
|
||||||
|
events: &[wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "done",
|
||||||
|
signature: &[wayland_backend::protocol::ArgumentType::Uint],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: true,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
}],
|
||||||
|
c_ptr: Some(unsafe { &wl_callback_interface }),
|
||||||
|
};
|
||||||
|
static wl_callback_events: SyncWrapper<[wayland_backend::protocol::wl_message; 1]> =
|
||||||
|
SyncWrapper([wayland_backend::protocol::wl_message {
|
||||||
|
name: b"done\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"u\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
}]);
|
||||||
|
pub static wl_callback_interface: wayland_backend::protocol::wl_interface =
|
||||||
|
wayland_backend::protocol::wl_interface {
|
||||||
|
name: b"wl_callback\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
version: 1,
|
||||||
|
request_count: 0,
|
||||||
|
requests: null::<wayland_backend::protocol::wl_message>(),
|
||||||
|
event_count: 1,
|
||||||
|
events: wl_callback_events.0.as_ptr(),
|
||||||
|
};
|
||||||
|
pub static TEST_GLOBAL_INTERFACE: wayland_backend::protocol::Interface =
|
||||||
|
wayland_backend::protocol::Interface {
|
||||||
|
name: "test_global",
|
||||||
|
version: 5u32,
|
||||||
|
requests: &[
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "many_args",
|
||||||
|
signature: &[
|
||||||
|
wayland_backend::protocol::ArgumentType::Uint,
|
||||||
|
wayland_backend::protocol::ArgumentType::Int,
|
||||||
|
wayland_backend::protocol::ArgumentType::Fixed,
|
||||||
|
wayland_backend::protocol::ArgumentType::Array,
|
||||||
|
wayland_backend::protocol::ArgumentType::Str(
|
||||||
|
wayland_backend::protocol::AllowNull::No,
|
||||||
|
),
|
||||||
|
wayland_backend::protocol::ArgumentType::Fd,
|
||||||
|
],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "get_secondary",
|
||||||
|
signature: &[wayland_backend::protocol::ArgumentType::NewId],
|
||||||
|
since: 2u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: Some(&SECONDARY_INTERFACE),
|
||||||
|
arg_interfaces: &[],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "get_tertiary",
|
||||||
|
signature: &[wayland_backend::protocol::ArgumentType::NewId],
|
||||||
|
since: 3u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: Some(&TERTIARY_INTERFACE),
|
||||||
|
arg_interfaces: &[],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "link",
|
||||||
|
signature: &[
|
||||||
|
wayland_backend::protocol::ArgumentType::Object(
|
||||||
|
wayland_backend::protocol::AllowNull::No,
|
||||||
|
),
|
||||||
|
wayland_backend::protocol::ArgumentType::Object(
|
||||||
|
wayland_backend::protocol::AllowNull::Yes,
|
||||||
|
),
|
||||||
|
wayland_backend::protocol::ArgumentType::Uint,
|
||||||
|
],
|
||||||
|
since: 3u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[&SECONDARY_INTERFACE, &TERTIARY_INTERFACE],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "destroy",
|
||||||
|
signature: &[],
|
||||||
|
since: 4u32,
|
||||||
|
is_destructor: true,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "reverse_link",
|
||||||
|
signature: &[
|
||||||
|
wayland_backend::protocol::ArgumentType::Object(
|
||||||
|
wayland_backend::protocol::AllowNull::Yes,
|
||||||
|
),
|
||||||
|
wayland_backend::protocol::ArgumentType::Object(
|
||||||
|
wayland_backend::protocol::AllowNull::No,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
since: 5u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[&SECONDARY_INTERFACE, &TERTIARY_INTERFACE],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "newid_and_allow_null",
|
||||||
|
signature: &[
|
||||||
|
wayland_backend::protocol::ArgumentType::NewId,
|
||||||
|
wayland_backend::protocol::ArgumentType::Object(
|
||||||
|
wayland_backend::protocol::AllowNull::Yes,
|
||||||
|
),
|
||||||
|
wayland_backend::protocol::ArgumentType::Object(
|
||||||
|
wayland_backend::protocol::AllowNull::No,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
since: 5u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: Some(&QUAD_INTERFACE),
|
||||||
|
arg_interfaces: &[&SECONDARY_INTERFACE, &TERTIARY_INTERFACE],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
events: &[
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "many_args_evt",
|
||||||
|
signature: &[
|
||||||
|
wayland_backend::protocol::ArgumentType::Uint,
|
||||||
|
wayland_backend::protocol::ArgumentType::Int,
|
||||||
|
wayland_backend::protocol::ArgumentType::Fixed,
|
||||||
|
wayland_backend::protocol::ArgumentType::Array,
|
||||||
|
wayland_backend::protocol::ArgumentType::Str(
|
||||||
|
wayland_backend::protocol::AllowNull::No,
|
||||||
|
),
|
||||||
|
wayland_backend::protocol::ArgumentType::Fd,
|
||||||
|
],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "ack_secondary",
|
||||||
|
signature: &[wayland_backend::protocol::ArgumentType::Object(
|
||||||
|
wayland_backend::protocol::AllowNull::No,
|
||||||
|
)],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[&SECONDARY_INTERFACE],
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "cycle_quad",
|
||||||
|
signature: &[
|
||||||
|
wayland_backend::protocol::ArgumentType::NewId,
|
||||||
|
wayland_backend::protocol::ArgumentType::Object(
|
||||||
|
wayland_backend::protocol::AllowNull::Yes,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
since: 1u32,
|
||||||
|
is_destructor: false,
|
||||||
|
child_interface: Some(&QUAD_INTERFACE),
|
||||||
|
arg_interfaces: &[&QUAD_INTERFACE],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
c_ptr: Some(unsafe { &test_global_interface }),
|
||||||
|
};
|
||||||
|
static test_global_requests_get_secondary_types: SyncWrapper<
|
||||||
|
[*const wayland_backend::protocol::wl_interface; 1],
|
||||||
|
> = SyncWrapper([&secondary_interface as *const wayland_backend::protocol::wl_interface]);
|
||||||
|
static test_global_requests_get_tertiary_types: SyncWrapper<
|
||||||
|
[*const wayland_backend::protocol::wl_interface; 1],
|
||||||
|
> = SyncWrapper([&tertiary_interface as *const wayland_backend::protocol::wl_interface]);
|
||||||
|
static test_global_requests_link_types: SyncWrapper<
|
||||||
|
[*const wayland_backend::protocol::wl_interface; 3],
|
||||||
|
> = SyncWrapper([
|
||||||
|
&secondary_interface as *const wayland_backend::protocol::wl_interface,
|
||||||
|
&tertiary_interface as *const wayland_backend::protocol::wl_interface,
|
||||||
|
null::<wayland_backend::protocol::wl_interface>(),
|
||||||
|
]);
|
||||||
|
static test_global_requests_reverse_link_types: SyncWrapper<
|
||||||
|
[*const wayland_backend::protocol::wl_interface; 2],
|
||||||
|
> = SyncWrapper([
|
||||||
|
&secondary_interface as *const wayland_backend::protocol::wl_interface,
|
||||||
|
&tertiary_interface as *const wayland_backend::protocol::wl_interface,
|
||||||
|
]);
|
||||||
|
static test_global_requests_newid_and_allow_null_types: SyncWrapper<
|
||||||
|
[*const wayland_backend::protocol::wl_interface; 3],
|
||||||
|
> = SyncWrapper([
|
||||||
|
&quad_interface as *const wayland_backend::protocol::wl_interface,
|
||||||
|
&secondary_interface as *const wayland_backend::protocol::wl_interface,
|
||||||
|
&tertiary_interface as *const wayland_backend::protocol::wl_interface,
|
||||||
|
]);
|
||||||
|
static test_global_requests: SyncWrapper<[wayland_backend::protocol::wl_message; 7]> =
|
||||||
|
SyncWrapper([
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"many_args\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"uifash\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"get_secondary\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"2n\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: test_global_requests_get_secondary_types.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"get_tertiary\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"3n\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: test_global_requests_get_tertiary_types.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"link\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"3o?ou\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: test_global_requests_link_types.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"destroy\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"4\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"reverse_link\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"5?oo\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: test_global_requests_reverse_link_types.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"newid_and_allow_null\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"5n?oo\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: test_global_requests_newid_and_allow_null_types.0.as_ptr(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
static test_global_events_ack_secondary_types: SyncWrapper<
|
||||||
|
[*const wayland_backend::protocol::wl_interface; 1],
|
||||||
|
> = SyncWrapper([&secondary_interface as *const wayland_backend::protocol::wl_interface]);
|
||||||
|
static test_global_events_cycle_quad_types: SyncWrapper<
|
||||||
|
[*const wayland_backend::protocol::wl_interface; 2],
|
||||||
|
> = SyncWrapper([
|
||||||
|
&quad_interface as *const wayland_backend::protocol::wl_interface,
|
||||||
|
&quad_interface as *const wayland_backend::protocol::wl_interface,
|
||||||
|
]);
|
||||||
|
static test_global_events: SyncWrapper<[wayland_backend::protocol::wl_message; 3]> = SyncWrapper([
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"many_args_evt\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"uifash\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"ack_secondary\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"o\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: test_global_events_ack_secondary_types.0.as_ptr(),
|
||||||
|
},
|
||||||
|
wayland_backend::protocol::wl_message {
|
||||||
|
name: b"cycle_quad\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"n?o\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: test_global_events_cycle_quad_types.0.as_ptr(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
pub static test_global_interface: wayland_backend::protocol::wl_interface =
|
||||||
|
wayland_backend::protocol::wl_interface {
|
||||||
|
name: b"test_global\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
version: 5,
|
||||||
|
request_count: 7,
|
||||||
|
requests: test_global_requests.0.as_ptr(),
|
||||||
|
event_count: 3,
|
||||||
|
events: test_global_events.0.as_ptr(),
|
||||||
|
};
|
||||||
|
pub static SECONDARY_INTERFACE: wayland_backend::protocol::Interface =
|
||||||
|
wayland_backend::protocol::Interface {
|
||||||
|
name: "secondary",
|
||||||
|
version: 5u32,
|
||||||
|
requests: &[wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "destroy",
|
||||||
|
signature: &[],
|
||||||
|
since: 2u32,
|
||||||
|
is_destructor: true,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
}],
|
||||||
|
events: &[],
|
||||||
|
c_ptr: Some(unsafe { &secondary_interface }),
|
||||||
|
};
|
||||||
|
static secondary_requests: SyncWrapper<[wayland_backend::protocol::wl_message; 1]> =
|
||||||
|
SyncWrapper([wayland_backend::protocol::wl_message {
|
||||||
|
name: b"destroy\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"2\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
}]);
|
||||||
|
pub static secondary_interface: wayland_backend::protocol::wl_interface =
|
||||||
|
wayland_backend::protocol::wl_interface {
|
||||||
|
name: b"secondary\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
version: 5,
|
||||||
|
request_count: 1,
|
||||||
|
requests: secondary_requests.0.as_ptr(),
|
||||||
|
event_count: 0,
|
||||||
|
events: null::<wayland_backend::protocol::wl_message>(),
|
||||||
|
};
|
||||||
|
pub static TERTIARY_INTERFACE: wayland_backend::protocol::Interface =
|
||||||
|
wayland_backend::protocol::Interface {
|
||||||
|
name: "tertiary",
|
||||||
|
version: 5u32,
|
||||||
|
requests: &[wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "destroy",
|
||||||
|
signature: &[],
|
||||||
|
since: 3u32,
|
||||||
|
is_destructor: true,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
}],
|
||||||
|
events: &[],
|
||||||
|
c_ptr: Some(unsafe { &tertiary_interface }),
|
||||||
|
};
|
||||||
|
static tertiary_requests: SyncWrapper<[wayland_backend::protocol::wl_message; 1]> =
|
||||||
|
SyncWrapper([wayland_backend::protocol::wl_message {
|
||||||
|
name: b"destroy\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"3\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
}]);
|
||||||
|
pub static tertiary_interface: wayland_backend::protocol::wl_interface =
|
||||||
|
wayland_backend::protocol::wl_interface {
|
||||||
|
name: b"tertiary\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
version: 5,
|
||||||
|
request_count: 1,
|
||||||
|
requests: tertiary_requests.0.as_ptr(),
|
||||||
|
event_count: 0,
|
||||||
|
events: null::<wayland_backend::protocol::wl_message>(),
|
||||||
|
};
|
||||||
|
pub static QUAD_INTERFACE: wayland_backend::protocol::Interface =
|
||||||
|
wayland_backend::protocol::Interface {
|
||||||
|
name: "quad",
|
||||||
|
version: 5u32,
|
||||||
|
requests: &[wayland_backend::protocol::MessageDesc {
|
||||||
|
name: "destroy",
|
||||||
|
signature: &[],
|
||||||
|
since: 3u32,
|
||||||
|
is_destructor: true,
|
||||||
|
child_interface: None,
|
||||||
|
arg_interfaces: &[],
|
||||||
|
}],
|
||||||
|
events: &[],
|
||||||
|
c_ptr: Some(unsafe { &quad_interface }),
|
||||||
|
};
|
||||||
|
static quad_requests: SyncWrapper<[wayland_backend::protocol::wl_message; 1]> =
|
||||||
|
SyncWrapper([wayland_backend::protocol::wl_message {
|
||||||
|
name: b"destroy\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
signature: b"3\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
types: types_null.0.as_ptr(),
|
||||||
|
}]);
|
||||||
|
pub static quad_interface: wayland_backend::protocol::wl_interface =
|
||||||
|
wayland_backend::protocol::wl_interface {
|
||||||
|
name: b"quad\0" as *const u8 as *const std::os::raw::c_char,
|
||||||
|
version: 5,
|
||||||
|
request_count: 1,
|
||||||
|
requests: quad_requests.0.as_ptr(),
|
||||||
|
event_count: 0,
|
||||||
|
events: null::<wayland_backend::protocol::wl_message>(),
|
||||||
|
};
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="test-protocol">
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
A dummy copyright to make the parser work
|
||||||
|
|
||||||
|
Including a few more lines.
|
||||||
|
Of text.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<!-- Include the core interfaces display/registry/callback -->
|
||||||
|
|
||||||
|
<interface name="wl_display" version="1">
|
||||||
|
<description summary="core global object">
|
||||||
|
The core global object. This is a special singleton object. It
|
||||||
|
is used for internal Wayland protocol features.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="sync">
|
||||||
|
<description summary="asynchronous roundtrip">
|
||||||
|
The sync request asks the server to emit the 'done' event
|
||||||
|
on the returned wl_callback object. Since requests are
|
||||||
|
handled in-order and events are delivered in-order, this can
|
||||||
|
be used as a barrier to ensure all previous requests and the
|
||||||
|
resulting events have been handled.
|
||||||
|
|
||||||
|
The object returned by this request will be destroyed by the
|
||||||
|
compositor after the callback is fired and as such the client must not
|
||||||
|
attempt to use it after that point.
|
||||||
|
|
||||||
|
The callback_data passed in the callback is the event serial.
|
||||||
|
</description>
|
||||||
|
<arg name="callback" type="new_id" interface="wl_callback"
|
||||||
|
summary="callback object for the sync request"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_registry">
|
||||||
|
<description summary="get global registry object">
|
||||||
|
This request creates a registry object that allows the client
|
||||||
|
to list and bind the global objects available from the
|
||||||
|
compositor.
|
||||||
|
|
||||||
|
It should be noted that the server side resources consumed in
|
||||||
|
response to a get_registry request can only be released when the
|
||||||
|
client disconnects, not when the client side proxy is destroyed.
|
||||||
|
Therefore, clients should invoke get_registry as infrequently as
|
||||||
|
possible to avoid wasting memory.
|
||||||
|
</description>
|
||||||
|
<arg name="registry" type="new_id" interface="wl_registry"
|
||||||
|
summary="global registry object"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="error">
|
||||||
|
<description summary="fatal error event">
|
||||||
|
The error event is sent out when a fatal (non-recoverable)
|
||||||
|
error has occurred. The object_id argument is the object
|
||||||
|
where the error occurred, most often in response to a request
|
||||||
|
to that object. The code identifies the error and is defined
|
||||||
|
by the object interface. As such, each interface defines its
|
||||||
|
own set of error codes. The message is a brief description
|
||||||
|
of the error, for (debugging) convenience.
|
||||||
|
</description>
|
||||||
|
<arg name="object_id" type="object" summary="object where the error occurred"/>
|
||||||
|
<arg name="code" type="uint" summary="error code"/>
|
||||||
|
<arg name="message" type="string" summary="error description"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<description summary="global error values">
|
||||||
|
These errors are global and can be emitted in response to any
|
||||||
|
server request.
|
||||||
|
</description>
|
||||||
|
<entry name="invalid_object" value="0"
|
||||||
|
summary="server couldn't find object"/>
|
||||||
|
<entry name="invalid_method" value="1"
|
||||||
|
summary="method doesn't exist on the specified interface or malformed request"/>
|
||||||
|
<entry name="no_memory" value="2"
|
||||||
|
summary="server is out of memory"/>
|
||||||
|
<entry name="implementation" value="3"
|
||||||
|
summary="implementation error in compositor"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<event name="delete_id">
|
||||||
|
<description summary="acknowledge object ID deletion">
|
||||||
|
This event is used internally by the object ID management
|
||||||
|
logic. When a client deletes an object that it had created,
|
||||||
|
the server will send this event to acknowledge that it has
|
||||||
|
seen the delete request. When the client receives this event,
|
||||||
|
it will know that it can safely reuse the object ID.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="uint" summary="deleted object ID"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="wl_registry" version="1">
|
||||||
|
<description summary="global registry object">
|
||||||
|
The singleton global registry object. The server has a number of
|
||||||
|
global objects that are available to all clients. These objects
|
||||||
|
typically represent an actual object in the server (for example,
|
||||||
|
an input device) or they are singleton objects that provide
|
||||||
|
extension functionality.
|
||||||
|
|
||||||
|
When a client creates a registry object, the registry object
|
||||||
|
will emit a global event for each global currently in the
|
||||||
|
registry. Globals come and go as a result of device or
|
||||||
|
monitor hotplugs, reconfiguration or other events, and the
|
||||||
|
registry will send out global and global_remove events to
|
||||||
|
keep the client up to date with the changes. To mark the end
|
||||||
|
of the initial burst of events, the client can use the
|
||||||
|
wl_display.sync request immediately after calling
|
||||||
|
wl_display.get_registry.
|
||||||
|
|
||||||
|
A client can bind to a global object by using the bind
|
||||||
|
request. This creates a client-side handle that lets the object
|
||||||
|
emit events to the client and lets the client invoke requests on
|
||||||
|
the object.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="bind">
|
||||||
|
<description summary="bind an object to the display">
|
||||||
|
Binds a new, client-created object to the server using the
|
||||||
|
specified name as the identifier.
|
||||||
|
</description>
|
||||||
|
<arg name="name" type="uint" summary="unique numeric name of the object"/>
|
||||||
|
<arg name="id" type="new_id" summary="bounded object"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="global">
|
||||||
|
<description summary="announce global object">
|
||||||
|
Notify the client of global objects.
|
||||||
|
|
||||||
|
The event notifies the client that a global object with
|
||||||
|
the given name is now available, and it implements the
|
||||||
|
given version of the given interface.
|
||||||
|
</description>
|
||||||
|
<arg name="name" type="uint" summary="numeric name of the global object"/>
|
||||||
|
<arg name="interface" type="string" summary="interface implemented by the object"/>
|
||||||
|
<arg name="version" type="uint" summary="interface version"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="global_remove">
|
||||||
|
<description summary="announce removal of global object">
|
||||||
|
Notify the client of removed global objects.
|
||||||
|
|
||||||
|
This event notifies the client that the global identified
|
||||||
|
by name is no longer available. If the client bound to
|
||||||
|
the global using the bind request, the client should now
|
||||||
|
destroy that object.
|
||||||
|
|
||||||
|
The object remains valid and requests to the object will be
|
||||||
|
ignored until the client destroys it, to avoid races between
|
||||||
|
the global going away and a client sending a request to it.
|
||||||
|
</description>
|
||||||
|
<arg name="name" type="uint" summary="numeric name of the global object"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="wl_callback" version="1">
|
||||||
|
<description summary="callback object">
|
||||||
|
Clients can handle the 'done' event to get notified when
|
||||||
|
the related request is done.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<event name="done" type="destructor">
|
||||||
|
<description summary="done event">
|
||||||
|
Notify the client when the related request is done.
|
||||||
|
</description>
|
||||||
|
<arg name="callback_data" type="uint" summary="request-specific data for the callback"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<!-- And now the test interfaces -->
|
||||||
|
|
||||||
|
<interface name="test_global" version="5">
|
||||||
|
<request name="many_args">
|
||||||
|
<description summary="a request with every possible non-object arg"></description>
|
||||||
|
<arg name="unsigned_int" type="uint" summary="an unsigned int" />
|
||||||
|
<arg name="signed_int" type="int" summary="a singed int" />
|
||||||
|
<arg name="fixed_point" type="fixed" summary="a fixed point number" />
|
||||||
|
<arg name="number_array" type="array" summary="an array" />
|
||||||
|
<arg name="some_text" type="string" summary="some text" />
|
||||||
|
<arg name="file_descriptor" type="fd" summary="a file descriptor" />
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_secondary" since="2">
|
||||||
|
<arg name="sec" type="new_id" interface="secondary" summary="create a secondary" />
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_tertiary" since="3">
|
||||||
|
<arg name="ter" type="new_id" interface="tertiary" summary="create a tertiary" />
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="link" since="3">
|
||||||
|
<description summary="link a secondary and a tertiary"></description>
|
||||||
|
<arg name="sec" type="object" interface="secondary" />
|
||||||
|
<arg name="ter" type="object" interface="tertiary" allow-null="true" />
|
||||||
|
<arg name="time" type="uint" />
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor" since="4">
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="reverse_link" since="5">
|
||||||
|
<description summary="reverse link a secondary and a tertiary"></description>
|
||||||
|
<arg name="sec" type="object" interface="secondary" allow-null="true" />
|
||||||
|
<arg name="ter" type="object" interface="tertiary" />
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="newid_and_allow_null" since="5">
|
||||||
|
<description summary="a newid request that also takes allow null arg"></description>
|
||||||
|
<arg name="quad" type="new_id" interface="quad" />
|
||||||
|
<arg name="sec" type="object" interface="secondary" allow-null="true" />
|
||||||
|
<arg name="ter" type="object" interface="tertiary" />
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="many_args_evt">
|
||||||
|
<description summary="an event with every possible non-object arg"></description>
|
||||||
|
<arg name="unsigned_int" type="uint" summary="an unsigned int" />
|
||||||
|
<arg name="signed_int" type="int" summary="a singed int" />
|
||||||
|
<arg name="fixed_point" type="fixed" summary="a fixed point number" />
|
||||||
|
<arg name="number_array" type="array" summary="an array" />
|
||||||
|
<arg name="some_text" type="string" summary="some text" />
|
||||||
|
<arg name="file_descriptor" type="fd" summary="a file descriptor" />
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="ack_secondary">
|
||||||
|
<description summary="acking the creation of a secondary"></description>
|
||||||
|
<arg name="sec" type="object" interface="secondary" />
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="cycle_quad">
|
||||||
|
<description summary="create a new quad optionally replacing a previous one"></description>
|
||||||
|
<arg name="new_quad" type="new_id" interface="quad" />
|
||||||
|
<arg name="old_quad" type="object" interface="quad" allow-null="true" />
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="secondary" version="5">
|
||||||
|
<request name="destroy" type="destructor" since="2">
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="tertiary" version="5">
|
||||||
|
<request name="destroy" type="destructor" since="3">
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="quad" version="5">
|
||||||
|
<request name="destroy" type="destructor" since="3">
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user