wayland-scanner-0.29.5/.cargo_vcs_info.json0000644000000001550000000000100142200ustar { "git": { "sha1": "8f4127a21d65cf10189ff0e3b78983e9686dd288" }, "path_in_vcs": "wayland-scanner" }wayland-scanner-0.29.5/Cargo.toml0000644000000023360000000000100122210ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "wayland-scanner" version = "0.29.5" authors = ["Victor Berger "] description = "Wayland Scanner for generating rust APIs from XML wayland protocol files. Intended for use with wayland-sys. You should only need this crate if you are working on custom wayland protocol extensions. Look at the crate wayland-client for usable bindings." documentation = "https://smithay.github.io/wayland-rs/wayland_scanner/" readme = "README.md" keywords = [ "wayland", "codegen", ] categories = [ "gui", "api-bindings", ] license = "MIT" repository = "https://github.com/smithay/wayland-rs" [dependencies.proc-macro2] version = "1.0.11" [dependencies.quote] version = "1.0" [dependencies.xml-rs] version = ">=0.7, <0.9" wayland-scanner-0.29.5/Cargo.toml.orig000064400000000000000000000012751046102023000157030ustar 00000000000000[package] name = "wayland-scanner" version = "0.29.5" authors = ["Victor Berger "] repository = "https://github.com/smithay/wayland-rs" documentation = "https://smithay.github.io/wayland-rs/wayland_scanner/" description = "Wayland Scanner for generating rust APIs from XML wayland protocol files. Intended for use with wayland-sys. You should only need this crate if you are working on custom wayland protocol extensions. Look at the crate wayland-client for usable bindings." license = "MIT" categories = ["gui", "api-bindings"] keywords = ["wayland", "codegen"] edition = "2018" readme = "README.md" [dependencies] proc-macro2 = "1.0.11" quote = "1.0" xml-rs = ">=0.7, <0.9" wayland-scanner-0.29.5/LICENSE.txt000064400000000000000000000020401046102023000146260ustar 00000000000000Copyright (c) 2015 Victor Berger Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.wayland-scanner-0.29.5/README.md000064400000000000000000000015241046102023000142700ustar 00000000000000[![crates.io](https://img.shields.io/crates/v/wayland-scanner.svg)](https://crates.io/crates/wayland-scanner) [![docs.rs](https://docs.rs/wayland-scanner/badge.svg)](https://docs.rs/wayland-scanner) [![Continuous Integration](https://github.com/Smithay/wayland-rs/workflows/Continuous%20Integration/badge.svg)](https://github.com/Smithay/wayland-rs/actions?query=workflow%3A%22Continuous+Integration%22) [![codecov](https://codecov.io/gh/Smithay/wayland-rs/branch/master/graph/badge.svg)](https://codecov.io/gh/Smithay/wayland-rs) # wayland-scanner Code-generation for Wayland protocols, to be used with `wayland-client` or `wayland-server` to integrate them with your own protocol extensions. Most general protocol extensions are already exposed by the `wayland-protocols` crate, so you don't need to use `wayland-scanner` directly to support them.wayland-scanner-0.29.5/src/c_code_gen.rs000064400000000000000000000540011046102023000162110ustar 00000000000000use std::iter; use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::quote; use crate::common_gen::*; use crate::protocol::*; use crate::util::*; use crate::Side; pub(crate) fn generate_protocol_client(protocol: Protocol) -> TokenStream { // Force the fallback to work around https://github.com/alexcrichton/proc-macro2/issues/218 proc_macro2::fallback::force(); let modules = protocol.interfaces.iter().map(|iface| { let doc_attr = iface.description.as_ref().map(description_to_doc_attr); let mod_name = Ident::new(&iface.name, Span::call_site()); let iface_name = Ident::new(&snake_to_camel(&iface.name), Span::call_site()); let enums = &iface.enums; let ident = Ident::new("Request", Span::call_site()); let requests = gen_messagegroup( &ident, Side::Client, false, &iface.requests, Some(messagegroup_c_addon(&ident, &iface_name, Side::Client, false, &iface.requests)), ); let ident = Ident::new("Event", Span::call_site()); let events = gen_messagegroup( &ident, Side::Client, true, &iface.events, Some(messagegroup_c_addon(&ident, &iface_name, Side::Client, true, &iface.events)), ); let interface = gen_interface( &iface_name, &iface.name, iface.version, Some(interface_c_addon(&iface.name)), Side::Client, ); let object_methods = gen_object_methods(&iface_name, &iface.requests, Side::Client); let sinces = gen_since_constants(&iface.requests, &iface.events); let c_interface = super::c_interface_gen::generate_interface(iface); quote! { #doc_attr pub mod #mod_name { use std::os::raw::c_char; use super::{ Proxy, AnonymousObject, Interface, MessageGroup, MessageDesc, ArgumentType, Object, Message, Argument, ObjectMetadata, types_null, NULLPTR, Main, smallvec, }; use super::sys::common::{wl_interface, wl_array, wl_argument, wl_message}; use super::sys::client::*; #(#enums)* #requests #events #interface #object_methods #sinces #c_interface } } }); let c_prefix = super::c_interface_gen::generate_interfaces_prefix(&protocol); quote! { #c_prefix #(#modules)* } } pub(crate) fn generate_protocol_server(protocol: Protocol) -> TokenStream { // Force the fallback to work around https://github.com/alexcrichton/proc-macro2/issues/218 proc_macro2::fallback::force(); let modules = protocol .interfaces .iter() // display and registry are handled specially .filter(|iface| iface.name != "wl_display" && iface.name != "wl_registry") .map(|iface| { let doc_attr = iface.description.as_ref().map(description_to_doc_attr); let mod_name = Ident::new(&iface.name, Span::call_site()); let iface_name = Ident::new(&snake_to_camel(&iface.name), Span::call_site()); let enums = &iface.enums; let ident = Ident::new("Request", Span::call_site()); let requests = gen_messagegroup( &ident, Side::Server, true, &iface.requests, Some(messagegroup_c_addon( &ident, &iface_name, Side::Server, true, &iface.requests, )), ); let ident = Ident::new("Event", Span::call_site()); let events = gen_messagegroup( &ident, Side::Server, false, &iface.events, Some(messagegroup_c_addon( &ident, &iface_name, Side::Server, false, &iface.events, )), ); let interface = gen_interface( &Ident::new(&snake_to_camel(&iface.name), Span::call_site()), &iface.name, iface.version, Some(interface_c_addon(&iface.name)), Side::Server, ); let object_methods = gen_object_methods(&iface_name, &iface.events, Side::Server); let sinces = gen_since_constants(&iface.requests, &iface.events); let c_interface = super::c_interface_gen::generate_interface(iface); quote! { #doc_attr pub mod #mod_name { use std::os::raw::c_char; use super::{ Resource, AnonymousObject, Interface, MessageGroup, MessageDesc, Main, smallvec, ArgumentType, Object, Message, Argument, ObjectMetadata, types_null, NULLPTR }; use super::sys::common::{wl_argument, wl_interface, wl_array, wl_message}; use super::sys::server::*; #(#enums)* #requests #events #interface #object_methods #sinces #c_interface } } }); let c_prefix = super::c_interface_gen::generate_interfaces_prefix(&protocol); quote! { #c_prefix #(#modules)* } } fn messagegroup_c_addon( name: &Ident, parent_iface: &Ident, side: Side, receiver: bool, messages: &[Message], ) -> TokenStream { let from_raw_c_body = if receiver { let match_arms = messages .iter() .enumerate() .map(|(i, msg)| { let pattern = Literal::u16_unsuffixed(i as u16); let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site()); let msg_name_qualified = quote!(#name::#msg_name); let (args_binding, result) = if msg.args.is_empty() { (None, msg_name_qualified) } else { let len = Literal::usize_unsuffixed(msg.args.len()); let fields = msg.args.iter().enumerate().map(|(j, arg)| { let field_name = Ident::new( &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name), Span::call_site(), ); let idx = Literal::usize_unsuffixed(j); let field_value = match arg.typ { Type::Uint => { if let Some(ref enu) = arg.enum_ { let enum_type = dotted_to_relname(enu); quote!(#enum_type::from_raw(_args[#idx].u).ok_or(())?) } else { quote!(_args[#idx].u) } } Type::Int => { if let Some(ref enu) = arg.enum_ { let enum_type = dotted_to_relname(enu); quote!(#enum_type::from_raw(_args[#idx].i as u32).ok_or(())?) } else { quote!(_args[#idx].i) } } Type::Fixed => quote!((_args[#idx].f as f64) / 256.), Type::String => { let string_conversion = quote! { ::std::ffi::CStr::from_ptr(_args[#idx].s).to_string_lossy().into_owned() }; if arg.allow_null { quote! { if _args[#idx].s.is_null() { None } else { Some(#string_conversion) } } } else { string_conversion } } Type::Array => { let array_conversion = quote! { { let array = &*_args[#idx].a; ::std::slice::from_raw_parts(array.data as *const u8, array.size) .to_owned() } }; if arg.allow_null { quote! { if _args[#idx].a.is_null() { None } else { Some(#array_conversion) } } } else { array_conversion } } Type::Fd => quote!(_args[#idx].h), Type::Object => { let object_name = side.object_name(); let object_conversion = 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! { #object_name::::from_c_ptr( _args[#idx].o as *mut _, ).into() } } else { quote! { #object_name::::from_c_ptr(_args[#idx].o as *mut _).into() } }; if arg.allow_null { quote! { if _args[#idx].o.is_null() { None } else { Some(#object_conversion) } } } else { object_conversion } } Type::NewId => { let new_id_conversion = 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()); match side { Side::Client => { quote! { Main::::from_c_ptr( _args[#idx].o as *mut _ ) } } Side::Server => { quote! { { let me = Resource::<#parent_iface>::from_c_ptr(obj as *mut _); me.make_child_for::(_args[#idx].n).unwrap() } } } } } else { // bind-like function quote!(panic!("Cannot unserialize anonymous new id.")) }; if arg.allow_null { quote! { if _args[#idx].o.is_null() { None } else { Some(#new_id_conversion) } } } else { new_id_conversion } } Type::Destructor => panic!("An argument cannot have type \"destructor\"."), }; quote!(#field_name: #field_value) }); let result = quote! { #msg_name_qualified { #(#fields,)* } }; let args_binding = quote! { let _args = ::std::slice::from_raw_parts(args, #len); }; (Some(args_binding), result) }; quote! { #pattern => { #args_binding Ok(#result) } } }) .chain(iter::once(quote!(_ => return Err(())))); quote! { match opcode { #(#match_arms,)* } } } else { let panic_message = format!("{}::from_raw_c can not be used {:?}-side.", name, side); quote!(panic!(#panic_message)) }; let as_raw_c_in_body = if receiver { let panic_message = format!("{}::as_raw_c_in can not be used {:?}-side.", name, side); quote!(panic!(#panic_message)) } else { let match_arms = messages.iter().enumerate().map(|(i, msg)| { let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site()); let pattern = if msg.args.is_empty() { quote!(#name::#msg_name) } else { let fields = msg.args.iter().flat_map(|arg| { // Client-side newid request do not contain a placeholder if side == Side::Client && arg.typ == Type::NewId && arg.interface.is_some() { None } else { Some(Ident::new( &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name), Span::call_site(), )) } }); quote!(#name::#msg_name { #(#fields),* }) }; let buffer_len = Literal::usize_unsuffixed( msg.args.len() + 2 * msg .args .iter() .filter(|arg| arg.typ == Type::NewId && arg.interface.is_none()) .count(), ); let mut j = 0; let args_array_init_stmts = msg.args.iter().map(|arg| { let idx = Literal::usize_unsuffixed(j); let arg_name = Ident::new( &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name), Span::call_site(), ); let res = match arg.typ { Type::Uint => { if arg.enum_.is_some() { quote! { _args_array[#idx].u = #arg_name.to_raw(); } } else { quote! { _args_array[#idx].u = #arg_name; } } } Type::Int => { if arg.enum_.is_some() { quote! { _args_array[#idx].i = #arg_name.to_raw() as i32; } } else { quote! { _args_array[#idx].i = #arg_name; } } } Type::Fixed => quote! { _args_array[#idx].f = (#arg_name * 256.) as i32; }, Type::String => { let arg_variable = Ident::new(&format!("_arg_{}", j), Span::call_site()); if arg.allow_null { quote! { let #arg_variable = #arg_name.map(|s| ::std::ffi::CString::new(s).unwrap()); _args_array[#idx].s = (&#arg_variable).as_ref().map(|s| s.as_ptr()).unwrap_or(::std::ptr::null()); } } else { quote! { let #arg_variable = ::std::ffi::CString::new(#arg_name).unwrap(); _args_array[#idx].s = #arg_variable.as_ptr(); } } } Type::Array => { let arg_variable = Ident::new(&format!("_arg_{}", j), Span::call_site()); if arg.allow_null { quote! { let #arg_variable = #arg_name.as_ref().map(|vec| wl_array { size: vec.len(), alloc: vec.capacity(), data: vec.as_ptr() as *mut _, }); _args_array[#idx].a = #arg_variable .as_ref() .map(|a| a as *const wl_array) .unwrap_or(::std::ptr::null()); } } else { quote! { let #arg_variable = wl_array { size: #arg_name.len(), alloc: #arg_name.capacity(), data: #arg_name.as_ptr() as *mut _, }; _args_array[#idx].a = &#arg_variable; } } } Type::Fd => quote! { _args_array[#idx].h = #arg_name; }, Type::Object => { if arg.allow_null { quote! { _args_array[#idx].o = #arg_name .map(|o| o.as_ref().c_ptr() as *mut _) .unwrap_or(::std::ptr::null_mut()); } } else { quote! { _args_array[#idx].o = #arg_name.as_ref().c_ptr() as *mut _; } } } Type::NewId => { if arg.interface.is_some() { if side == Side::Client { quote! { _args_array[#idx].o = ::std::ptr::null_mut() as *mut _; } } else { quote! { _args_array[#idx].o = #arg_name.c_ptr() as *mut _; } } } else { assert!( side != Side::Server, "Cannot serialize anonymous NewID from server." ); // The arg is actually (string, uint, NULL) let arg_variable = Ident::new(&format!("_arg_{}_s", j), Span::call_site()); let idx1 = Literal::usize_unsuffixed(j + 1); let idx2 = Literal::usize_unsuffixed(j + 2); let res = quote! { let #arg_variable = ::std::ffi::CString::new(#arg_name.0).unwrap(); _args_array[#idx].s = #arg_variable.as_ptr(); _args_array[#idx1].u = #arg_name.1; _args_array[#idx2].o = ::std::ptr::null_mut(); }; j += 2; res } } Type::Destructor => panic!("An argument cannot have type \"destructor\"."), }; j += 1; res }); let idx = Literal::u32_unsuffixed(i as u32); quote! { #pattern => { let mut _args_array: [wl_argument; #buffer_len] = unsafe { ::std::mem::zeroed() }; #(#args_array_init_stmts)* f(#idx, &mut _args_array) } } }); quote! { match self { #(#match_arms,)* } } }; quote! { unsafe fn from_raw_c( obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument, ) -> Result<#name, ()> { #from_raw_c_body } fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { #as_raw_c_in_body } } } fn interface_c_addon(low_name: &str) -> TokenStream { let iface_name = Ident::new(&format!("{}_interface", low_name), Span::call_site()); quote! { fn c_interface() -> *const wl_interface { unsafe { &#iface_name } } } } wayland-scanner-0.29.5/src/c_interface_gen.rs000064400000000000000000000143001046102023000172350ustar 00000000000000use std::cmp; use std::iter::repeat; use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::quote; use crate::protocol::*; use crate::util::null_terminated_byte_string_literal; 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); let nulls = repeat(quote!(NULLPTR as *const sys::common::wl_interface)).take(longest_nulls); quote! { use std::os::raw::{c_char, c_void}; const NULLPTR: *const c_void = 0 as *const c_void; static mut types_null: [*const sys::common::wl_interface; #types_null_len] = [ #(#nulls,)* ]; } } 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 = Ident::new(&format!("{}_interface", interface.name), Span::call_site()); 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!(NULLPTR as *const wl_message) } else { let requests_ident = Ident::new(&format!("{}_requests", interface.name), Span::call_site()); quote!(unsafe { &#requests_ident as *const _ }) }; let event_count_value = Literal::i32_unsuffixed(interface.events.len() as i32); let events_value = if interface.events.is_empty() { quote!(NULLPTR as *const wl_message) } else { let events_ident = Ident::new(&format!("{}_events", interface.name), Span::call_site()); quote!(unsafe { &#events_ident as *const _ }) }; quote!( #requests #events /// C representation of this interface, for interop pub static mut #interface_ident: wl_interface = wl_interface { name: #name_value as *const u8 as *const 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 = Ident::new( &format!("{}_{}_{}_types", interface.name, which, msg.name), Span::call_site(), ); 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 module = Ident::new(inter, Span::call_site()); let interface_ident = Ident::new(&format!("{}_interface", inter), Span::call_site()); quote!(unsafe { &super::#module::#interface_ident as *const wl_interface }) } _ => quote!(NULLPTR as *const wl_interface), }); Some(quote! { static mut #array_ident: [*const wl_interface; #array_len] = [ #(#array_values,)* ]; }) } }); let message_array_ident = Ident::new(&format!("{}_{}", interface.name, which), Span::call_site()); 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() { Ident::new("types_null", Span::call_site()) } else { Ident::new( &format!("{}_{}_{}_types", interface.name, which, msg.name), Span::call_site(), ) }; quote! { wl_message { name: #name_value as *const u8 as *const c_char, signature: #signature_value as *const u8 as *const c_char, types: unsafe { &#types_ident as *const _ }, } } }); quote! { #(#types_arrays)* /// C-representation of the messages of this interface, for interop pub static mut #message_array_ident: [wl_message; #message_array_len] = [ #(#message_array_values,)* ]; } } fn message_signature(msg: &Message) -> Vec { 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 } wayland-scanner-0.29.5/src/common_gen.rs000064400000000000000000000772061046102023000163010ustar 00000000000000#[allow(unused_imports)] #[allow(deprecated)] use std::ascii::AsciiExt; use std::iter; use std::str; use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::{quote, ToTokens}; use crate::protocol::*; use crate::util::*; use crate::Side; pub(crate) fn to_doc_attr(text: &str) -> TokenStream { let text = text.lines().map(str::trim).collect::>().join("\n"); let text = text.trim(); quote!(#[doc = #text]) } pub(crate) fn description_to_doc_attr(&(ref short, ref long): &(String, String)) -> TokenStream { to_doc_attr(&format!("{}\n\n{}", short, long)) } 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 = Ident::new( &format!("{}{}", prefix, snake_to_camel(&entry.name)), Span::call_site(), ); let value = Literal::u32_unsuffixed(entry.value); quote! { #doc_attr const #ident = #value; } }); enum_decl = quote! { bitflags! { #doc_attr pub struct #ident: u32 { #(#entries)* } } }; enum_impl = quote! { impl #ident { pub fn from_raw(n: u32) -> Option<#ident> { Some(#ident::from_bits_truncate(n)) } pub fn to_raw(&self) -> u32 { self.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 = Ident::new( &format!("{}{}", prefix, snake_to_camel(&entry.name)), Span::call_site(), ); let value = Literal::u32_unsuffixed(entry.value); quote! { #doc_attr #variant = #value } }); enum_decl = quote! { #doc_attr #[repr(u32)] #[derive(Copy, Clone, Debug, PartialEq)] #[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 = Ident::new( &format!("{}{}", prefix, snake_to_camel(&entry.name)), Span::call_site(), ); quote! { #value => Some(#ident::#variant) } }); enum_impl = quote! { impl #ident { pub fn from_raw(n: u32) -> Option<#ident> { match n { #(#match_arms,)* _ => Option::None } } pub fn to_raw(&self) -> u32 { *self as u32 } } }; } enum_decl.to_tokens(tokens); enum_impl.to_tokens(tokens); } } pub(crate) fn gen_since_constants(requests: &[Message], events: &[Message]) -> TokenStream { let req_constants = requests.iter().map(|msg| { let cstname = Ident::new(&format!("REQ_{}_SINCE", msg.name.to_ascii_uppercase()), Span::call_site()); let since = msg.since; quote! { /// The minimal object version supporting this request pub const #cstname: u32 = #since; } }); let evt_constants = events.iter().map(|msg| { let cstname = Ident::new(&format!("EVT_{}_SINCE", msg.name.to_ascii_uppercase()), Span::call_site()); let since = msg.since; quote! { /// The minimal object version supporting this event pub const #cstname: u32 = #since; } }); quote! { #(#req_constants)* #(#evt_constants)* } } pub(crate) fn gen_messagegroup( name: &Ident, side: Side, receiver: bool, messages: &[Message], addon: Option, ) -> TokenStream { let variants = messages.iter().map(|msg| { let mut docs = String::new(); if let Some((ref short, ref long)) = msg.description { docs += &format!("{}\n\n{}\n", short, long.trim()); } if let Some(Type::Destructor) = msg.typ { docs += &format!( "\nThis is a destructor, once {} this object cannot be used any longer.", if receiver { "received" } else { "sent" }, ); } if msg.since > 1 { docs += &format!("\nOnly available since version {} of the interface", msg.since); } 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 = Ident::new( &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name), Span::call_site(), ); let field_type_inner = if let Some(ref enu) = arg.enum_ { dotted_to_relname(enu) } else { match arg.typ { Type::Uint => quote!(u32), Type::Int => quote!(i32), Type::Fixed => quote!(f64), Type::String => quote!(String), Type::Array => quote!(Vec), Type::Fd => quote!(::std::os::unix::io::RawFd), 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 { quote!(AnonymousObject) } } 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!((String, u32)) } } Type::NewId => { let object_name = if side == Side::Server && !receiver { Ident::new("Resource", Span::call_site()) } else { Ident::new("Main", Span::call_site()) }; 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!(#object_name) } else { // bind-like function quote!((String, u32, AnonymousObject)) } } 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() }; Some(quote! { #field_name: #field_type }) }); quote! { #msg_name { #(#fields,)* } } }; quote! { #doc_attr #msg_variant_decl } }); let message_array_values = messages.iter().map(|msg| { let name_value = &msg.name; let since_value = Literal::u32_unsuffixed(msg.since); let signature_values = msg.args.iter().map(|arg| { let common_type = arg.typ.common_type(); quote!(super::ArgumentType::#common_type) }); let is_destructor = msg.typ == Some(Type::Destructor); quote! { super::MessageDesc { name: #name_value, since: #since_value, signature: &[ #(#signature_values,)* ], destructor: #is_destructor, } } }); let map_type = if side == Side::Client { quote!(ProxyMap) } else { quote!(ResourceMap) }; // Can't be a closure because closures are never Copy / Clone in rustc < 1.26.0, and we supports 1.21.0 fn map_fn((msg, name): (&Message, &Ident)) -> TokenStream { let msg_type = Ident::new(&snake_to_camel(&msg.name), Span::call_site()); let msg_type_qualified = quote!(#name::#msg_type); if msg.args.is_empty() { msg_type_qualified } else { quote!(#msg_type_qualified { .. }) } } let message_match_patterns = messages.iter().zip(iter::repeat(name)).map(map_fn); let mut is_destructor_match_arms = messages .iter() .zip(message_match_patterns.clone()) .filter(|&(msg, _)| msg.typ == Some(Type::Destructor)) .map(|(_, pattern)| quote!(#pattern => true)) .collect::>(); if messages.len() > is_destructor_match_arms.len() { is_destructor_match_arms.push(quote!(_ => false)); } let opcode_match_arms = message_match_patterns.clone().enumerate().map(|(opcode, pattern)| { let value = Literal::u16_unsuffixed(opcode as u16); quote!(#pattern => #value) }); let since_match_arms = messages.iter().zip(message_match_patterns).map(|(msg, pattern)| { let since = Literal::u32_unsuffixed(msg.since as u32); quote!(#pattern => #since) }); let child_match_arms = messages .iter() .enumerate() .filter_map(|(opcode, msg)| { let mut it = msg.args.iter().filter_map(|a| { if a.typ == Type::NewId { a.interface.as_ref() } else { None } }); it.next().map(|new_iface| { assert!( it.next().is_none(), "Got a message with more than one new_id in {}.{}", name, msg.name ); let pattern = Literal::u16_unsuffixed(opcode as u16); let new_iface_mod = Ident::new(new_iface, Span::call_site()); let new_iface_type = Ident::new(&snake_to_camel(new_iface), Span::call_site()); quote! { #pattern => Some(Object::from_interface::( version, meta.child(), )) } }) }) .chain(iter::once(quote!(_ => None))); let from_raw_body = if receiver { let match_arms = messages .iter() .enumerate() .map(|(opcode, msg)| { let pattern = Literal::u16_unsuffixed(opcode as u16); let msg_type = Ident::new(&snake_to_camel(&msg.name), Span::call_site()); let msg_type_qualified = quote!(#name::#msg_type); let block = if msg.args.is_empty() { quote!(Ok(#msg_type_qualified)) } else { let fields = msg.args.iter().map(|arg| { let field_name = Ident::new( &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name), Span::call_site(), ); let some_code_path = match arg.typ { Type::Int => { if let Some(ref enu) = arg.enum_ { let enum_ident = dotted_to_relname(enu); quote!(#enum_ident::from_raw(val as u32).ok_or(())?) } else { quote!(val) } } Type::Uint => { if let Some(ref enu) = arg.enum_ { let enum_ident = dotted_to_relname(enu); quote!(#enum_ident::from_raw(val).ok_or(())?) } else { quote!(val) } } Type::Fixed => quote!((val as f64) / 256.), Type::Array => { if arg.allow_null { quote!(if val.len() == 0 { None } else { Some(*val) }) } else { quote!(*val) } } Type::String => { let string_conversion = quote! { let s = String::from_utf8(val.into_bytes()) .unwrap_or_else(|e| String::from_utf8_lossy(&e.into_bytes()).into()); }; if arg.allow_null { quote! { #string_conversion if s.len() == 0 { None } else { Some(s) } } } else { quote! { #string_conversion s } } } Type::Fd => quote!(val), Type::Object => { let map_lookup = if side == Side::Client { quote!(map.get_or_dead(val).into()) } else { quote!(map.get(val).ok_or(())?.into()) }; if arg.allow_null { quote!(if val == 0 { None } else { Some(#map_lookup) }) } else { map_lookup } } Type::NewId => { let map_lookup = quote!(map.get_new(val).ok_or(())?); if arg.allow_null { quote!(if val == 0 { None } else { Some(#map_lookup) }) } else { map_lookup } } Type::Destructor => panic!("An argument cannot have type destructor!"), }; let common_type = arg.typ.common_type(); quote! { #field_name: { if let Some(Argument::#common_type(val)) = args.next() { #some_code_path } else { return Err(()); } } } }); quote! { { let mut args = msg.args.into_iter(); Ok(#msg_type_qualified { #(#fields,)* }) } } }; quote!(#pattern => #block) }) .chain(iter::once(quote!(_ => Err(())))); quote! { match msg.opcode { #(#match_arms,)* } } } else { let panic_message = format!("{}::from_raw can not be used {:?}-side.", name, side); quote!(panic!(#panic_message)) }; let into_raw_body = if receiver { let panic_message = format!("{}::into_raw can not be used {:?}-side.", name, side); quote!(panic!(#panic_message)) } else { let match_arms = messages.iter().enumerate().map(|(opcode, msg)| { let msg_type = Ident::new(&snake_to_camel(&msg.name), Span::call_site()); let msg_type_qualified = quote!(#name::#msg_type); let pattern = if msg.args.is_empty() { msg_type_qualified } else { let fields = msg.args.iter().flat_map(|arg| { // Client-side newid request do not contain a placeholder if side == Side::Client && arg.typ == Type::NewId && arg.interface.is_some() { None } else { Some(Ident::new( &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name), Span::call_site(), )) } }); quote!(#msg_type_qualified { #(#fields),* }) }; let opcode_value = Literal::u16_unsuffixed(opcode as u16); let args_values = msg.args.iter().map(|arg| { let arg_ident = Ident::new( &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name), Span::call_site(), ); match arg.typ { Type::Int => { if arg.enum_.is_some() { quote!(Argument::Int(#arg_ident.to_raw() as i32)) } else { quote!(Argument::Int(#arg_ident)) } } Type::Uint => { if arg.enum_.is_some() { quote!(Argument::Uint(#arg_ident.to_raw())) } else { quote!(Argument::Uint(#arg_ident)) } } Type::Fixed => quote!(Argument::Fixed((#arg_ident * 256.) as i32)), Type::String => { if arg.allow_null { quote! { Argument::Str(Box::new(unsafe { ::std::ffi::CString::from_vec_unchecked( #arg_ident.map(Into::into).unwrap_or_else(Vec::new), ) })) } } else { quote! { Argument::Str(Box::new(unsafe { ::std::ffi::CString::from_vec_unchecked(#arg_ident.into()) })) } } } Type::Array => { if arg.allow_null { quote!(Argument::Array(Box::new(#arg_ident.unwrap_or_else(Vec::new)))) } else { quote!(Argument::Array(Box::new(#arg_ident))) } } Type::Fd => quote!(Argument::Fd(#arg_ident)), Type::NewId => { if arg.interface.is_some() { let id = if side == Side::Client { quote!(0) } else { quote!(#arg_ident.id()) }; quote!(Argument::NewId(#id)) } else { let id = if side == Side::Client { quote!(0) } else { quote!(#arg_ident.2.id()) }; quote! { Argument::Str(Box::new(unsafe { ::std::ffi::CString::from_vec_unchecked(#arg_ident.0.into()) })), Argument::Uint(#arg_ident.1), Argument::NewId(#id) } } } Type::Object => { if arg.allow_null { quote!(Argument::Object(#arg_ident.map(|o| o.as_ref().id()).unwrap_or(0))) } else { quote!(Argument::Object(#arg_ident.as_ref().id())) } } Type::Destructor => panic!("An argument cannot have type Destructor"), } }); quote!(#pattern => Message { sender_id: sender_id, opcode: #opcode_value, args: smallvec![ #(#args_values,)* ], }) }); quote! { match self { #(#match_arms,)* } } }; quote! { #[derive(Debug)] #[non_exhaustive] pub enum #name { #(#variants,)* } impl super::MessageGroup for #name { const MESSAGES: &'static [super::MessageDesc] = &[ #(#message_array_values,)* ]; type Map = super::#map_type; fn is_destructor(&self) -> bool { match *self { #(#is_destructor_match_arms,)* } } fn opcode(&self) -> u16 { match *self { #(#opcode_match_arms,)* } } fn since(&self) -> u32 { match *self { #(#since_match_arms,)* } } fn child(opcode: u16, version: u32, meta: &Meta) -> Option> { match opcode { #(#child_match_arms,)* } } fn from_raw(msg: Message, map: &mut Self::Map) -> Result { #from_raw_body } fn into_raw(self, sender_id: u32) -> Message { #into_raw_body } #addon } } } pub(crate) fn gen_interface( name: &Ident, low_name: &str, version: u32, addon: Option, side: Side, ) -> TokenStream { let object_type = side.object_name(); let version_lit = Literal::u32_unsuffixed(version); quote! { #[derive(Clone, Eq, PartialEq)] pub struct #name(#object_type<#name>); impl AsRef<#object_type<#name>> for #name { #[inline] fn as_ref(&self) -> &#object_type { &self.0 } } impl From<#object_type<#name>> for #name { #[inline] fn from(value: #object_type) -> Self { #name(value) } } impl From<#name> for #object_type<#name> { #[inline] fn from(value: #name) -> Self { value.0 } } impl std::fmt::Debug for #name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("{:?}", self.0)) } } impl Interface for #name { type Request = Request; type Event = Event; const NAME: &'static str = #low_name; const VERSION: u32 = #version_lit; #addon } } } pub fn method_prototype<'a>( iname: &Ident, msg: &'a Message, side: Side, ) -> (TokenStream, Option<&'a Arg>) { let mut it = msg.args.iter().filter(|arg| arg.typ == Type::NewId); let mut newid = it.next(); assert!( newid.is_none() || it.next().is_none(), "Request {}.{} returns more than one new_id", iname, msg.name ); // Serverside we don't deal with NewId arguments and treat them as objects. if side == Side::Server { newid = None; } let fn_name = Ident::new( &format!("{}{}", if is_keyword(&msg.name) { "_" } else { "" }, msg.name), Span::call_site(), ); let mut args = Vec::new(); let generics = if let Some(arg) = newid { if arg.interface.is_none() { args.push(quote!(version: u32)); Some(quote!(T: Interface + From> + AsRef>)) } else { None } } else { None } .into_iter(); args.extend(msg.args.iter().filter_map(|arg| { let arg_type_inner = if let Some(ref name) = arg.enum_ { dotted_to_relname(name) } else { let mut typ = arg.typ; if typ == Type::NewId && side == Side::Server { typ = Type::Object; } match typ { Type::Object => arg .interface .as_ref() .map(|iface| { 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) }) .unwrap_or(quote!(&super::AnonymousObject)), Type::NewId => { // client-side, the return-type handles that return None; } _ => arg.typ.rust_type(), } }; let arg_name = Ident::new( &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name), Span::call_site(), ); let arg_type = if arg.allow_null { quote!(Option<#arg_type_inner>) } else { arg_type_inner }; Some(quote!(#arg_name: #arg_type)) })); let return_type = if let Some(arg) = newid { match arg.interface { Some(ref iface) => { let iface_mod = Ident::new(iface, Span::call_site()); let iface_type = Ident::new(&snake_to_camel(iface), Span::call_site()); quote!(Main) } None => quote!(Main), } } else { quote!(()) }; let prototype = quote! { pub fn #fn_name#(<#generics>)*(&self, #(#args),*) -> #return_type }; (prototype, newid) } pub(crate) fn gen_object_methods(name: &Ident, messages: &[Message], side: Side) -> TokenStream { let outgoing_message_type = Ident::new( match side { Side::Client => "Request", Side::Server => "Event", }, Span::call_site(), ); let method_impls = messages.iter().map(|msg| { let mut docs = String::new(); if let Some((ref short, ref long)) = msg.description { docs += &format!("{}\n\n{}\n", short, long); } if let Some(Type::Destructor) = msg.typ { docs += "\nThis is a destructor, you cannot send requests to this object any longer once this method is called."; } if msg.since > 1 { docs += &format!("\nOnly available since version {} of the interface.", msg.since); } let doc_attr = to_doc_attr(&docs); let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site()); let (proto, return_type) = method_prototype(name, msg, side); let msg_init = if msg.args.is_empty() { TokenStream::new() } else { let args = msg.args.iter().flat_map(|arg| { let arg_name = Ident::new( &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name), Span::call_site(), ); let arg_value = match (arg.typ, side) { (Type::NewId, Side::Client) => { if arg.interface.is_some() { return None; } else { quote!((T::NAME.into(), version)) } }, (Type::NewId, Side::Server) => { if arg.allow_null { quote!(#arg_name.map(|o| o.as_ref().clone())) } else { quote!(#arg_name.as_ref().clone()) } } (Type::Object, _) => { if arg.allow_null { quote!(#arg_name.map(|o| o.clone())) } else { quote!(#arg_name.clone()) } } _ => quote!(#arg_name), }; Some(quote!(#arg_name: #arg_value)) }); quote!({ #(#args),* }) }; let send_stmt = match return_type { Some(ret_type) if ret_type.interface.is_none() => { quote!(self.0.send(msg, Some(version)).unwrap()) } Some(_) => quote!(self.0.send(msg, None).unwrap()), None => if side == Side::Client { quote! { self.0.send::(msg, None); } } else { quote! { self.0.send(msg); } } }; quote! { #doc_attr #proto { let msg = #outgoing_message_type::#msg_name #msg_init; #send_stmt } } }); quote! { impl #name { #(#method_impls)* } } } wayland-scanner-0.29.5/src/lib.rs000064400000000000000000000164751046102023000147270ustar 00000000000000//! Wayland scanner crate //! //! //! This crate is a rust equivalent of the wayland-scanner tool from the //! official wayland C library. //! //! You can use in your build script to generate the rust code for any //! wayland protocol file, to use alongside the `wayland_client` and //! `wayland_server` crate to build your applications. //! //! ## How to use this crate //! //! This crate is to be used in a build script. It provides the function `generate_code`. //! It'll allow you to generate the code to use with the `wayland_client` or //! `wayland_server` crates for any XML wayland protocol file (NB: you don't //! need to do it for the core protocol, which is already included in both crates). //! //! First, have the XML files you want to use in your project, somewhere the build script //! will be able to read them. //! //! Then, you'll need to invoke `generate_code` for each of these files. //! //! A sample build script: //! //! ```no_run //! extern crate wayland_scanner; //! //! use std::env::var; //! use std::path::Path; //! //! use wayland_scanner::{Side, generate_code}; //! //! // Location of the xml file, relative to the `Cargo.toml` //! let protocol_file = "./my_protocol.xml"; //! //! // Target directory for the generate files //! let out_dir_str = var("OUT_DIR").unwrap(); //! let out_dir = Path::new(&out_dir_str); //! //! generate_code( //! protocol_file, //! out_dir.join("my_protocol_api.rs"), //! Side::Client, // Replace by `Side::Server` for server-side code //! ); //! ``` //! //! The above example will output a `.rs` file in the `OUT_DIR` defined by //! cargo. Then, you'll need to include this generated file (using the //! macro of the same name) to make this code available in your crate. //! //! ```ignore //! // The generated code will import stuff from wayland_commons //! extern crate wayland_commons; //! extern crate wayland_client; //! //! // Re-export only the actual code, and then only use this re-export //! // The `generated` module below is just some boilerplate to properly isolate stuff //! // and avoid exposing internal details. //! // //! // You can use all the types from my_protocol as if they went from `wayland_client::protocol`. //! pub use generated::client as my_protocol; //! //! mod generated { //! // The generated code tends to trigger a lot of warnings //! // so we isolate it into a very permissive module //! #![allow(dead_code,non_camel_case_types,unused_unsafe,unused_variables)] //! #![allow(non_upper_case_globals,non_snake_case,unused_imports)] //! //! pub mod client { //! // These imports are used by the generated code //! pub(crate) use wayland_client::{Main, Attached, Proxy, ProxyMap, AnonymousObject}; //! pub(crate) use wayland_commons::map::{Object, ObjectMetadata}; //! pub(crate) use wayland_commons::{Interface, MessageGroup}; //! pub(crate) use wayland_commons::wire::{Argument, MessageDesc, ArgumentType, Message}; //! pub(crate) use wayland_commons::smallvec; //! pub(crate) use wayland_client::protocol::{$($import),*}; //! pub(crate) use wayland_client::sys; //! // If you protocol interacts with objects from other protocols, you'll need to import //! // their modules, like so: //! pub(crate) use wayland_client::protocol::{wl_surface, wl_region}; //! include!(concat!(env!("OUT_DIR"), "/my_protocol_code.rs")); //! } //! } //! ``` #![warn(missing_docs)] // disable clippy lints that are not compatible with rust 1.41 #![allow(clippy::match_like_matches_macro)] use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; use std::path::Path; use std::process::Command; mod c_code_gen; mod c_interface_gen; mod common_gen; mod parse; mod protocol; mod side; mod util; pub use side::Side; fn load_xml>(prot: P) -> protocol::Protocol { let pfile = File::open(prot.as_ref()) .unwrap_or_else(|_| panic!("Unable to open protocol file `{}`.", prot.as_ref().display())); parse::parse_stream(pfile) } /// Generate the code for a protocol /// /// See this crate toplevel documentation for details. /// /// Args: /// /// - `protocol`: a path to the XML file describing the protocol, absolute or relative to /// the build script using this function. /// - `target`: the path of the file to store the code in. /// - `side`: the side (client or server) to generate code for. pub fn generate_code, P2: AsRef>(prot: P1, target: P2, side: Side) { generate_code_with_destructor_events(prot, target, side, &[]); } /// Generate the code for a protocol with aditionnal destructor events /// /// Same as `generate_code`, but allows you to additionnaly specify some events /// (in the format `("interface_name", "event_name")`) as being destructor, as this /// information is not encoded in the protocol files but instead written in the /// protocol documentation. pub fn generate_code_with_destructor_events, P2: AsRef>( prot: P1, target: P2, side: Side, events: &[(&str, &str)], ) { let mut protocol = load_xml(prot); for interface in &mut protocol.interfaces { for event in &mut interface.events { if events.contains(&(&interface.name, &event.name)) { event.typ = Some(crate::protocol::Type::Destructor); } } } { let mut out = OpenOptions::new().write(true).truncate(true).create(true).open(&target).unwrap(); let output = match side { Side::Client => c_code_gen::generate_protocol_client(protocol), Side::Server => c_code_gen::generate_protocol_server(protocol), }; write!(&mut out, "{}", output).unwrap(); } let _ = Command::new("rustfmt").arg(target.as_ref()).status(); } /// Generate the code for a protocol from/to IO streams /// /// Like `generate_code`, but takes IO Streams directly rather than filenames /// /// Args: /// /// - `protocol`: an object `Read`-able containing the XML protocol file /// - `target`: a `Write`-able object to which the generated code will be outputted to /// - `side`: the side (client or server) to generate code for. pub fn generate_code_streams(protocol: P1, target: &mut P2, side: Side) { generate_code_streams_with_destructor_events(protocol, target, side, &[]) } /// Generate the code for a protocol from/to IO streams with aditionnal destructor events /// /// Same as `generate_code_streams`, but allows you to additionnaly specify some events /// (in the format `("interface_name", "event_name")`) as being destructor, as this /// information is not encoded in the protocol files but instead written in the documentation /// of the protocol. pub fn generate_code_streams_with_destructor_events( protocol: P1, target: &mut P2, side: Side, events: &[(&str, &str)], ) { let mut protocol = parse::parse_stream(protocol); for interface in &mut protocol.interfaces { for event in &mut interface.events { if events.contains(&(&interface.name, &event.name)) { event.typ = Some(crate::protocol::Type::Destructor); } } } let output = match side { Side::Client => c_code_gen::generate_protocol_client(protocol), Side::Server => c_code_gen::generate_protocol_server(protocol), }; write!(target, "{}", output).unwrap(); } wayland-scanner-0.29.5/src/parse.rs000064400000000000000000000243171046102023000152650ustar 00000000000000use crate::protocol::*; use std::io::Read; use xml::attribute::OwnedAttribute; use xml::reader::ParserConfig; use xml::reader::XmlEvent; use xml::EventReader; macro_rules! extract_from( ($it: expr => $pattern: pat => $result: expr) => ( match $it.next() { Ok($pattern) => { $result }, e => panic!("Ill-formed protocol file: {:?}", e) } ) ); macro_rules! extract_end_tag( ($it: expr => $tag: expr) => ( extract_from!($it => XmlEvent::EndElement { name } => { assert!(name.local_name == $tag, "Ill-formed protocol file"); }); ) ); pub fn parse_stream(stream: S) -> Protocol { let mut reader = EventReader::new_with_config(stream, ParserConfig::new().trim_whitespace(true)); reader.next().expect("Could not read from event reader"); parse_protocol(reader) } fn parse_protocol(mut reader: EventReader) -> Protocol { let mut protocol = extract_from!( reader => XmlEvent::StartElement { name, attributes, .. } => { assert!(name.local_name == "protocol", "Missing protocol toplevel tag"); assert!(attributes[0].name.local_name == "name", "Protocol must have a name"); Protocol::new(attributes[0].value.clone()) } ); loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => { match &name.local_name[..] { "copyright" => { // parse the copyright let copyright = match reader.next() { Ok(XmlEvent::Characters(copyright)) | Ok(XmlEvent::CData(copyright)) => copyright, e => panic!("Ill-formed protocol file: {:?}", e), }; extract_end_tag!(reader => "copyright"); protocol.copyright = Some(copyright); } "interface" => { protocol.interfaces.push(parse_interface(&mut reader, attributes)); } "description" => { protocol.description = Some(parse_description(&mut reader, attributes)); } _ => panic!( "Ill-formed protocol file: unexpected token `{}` in protocol {}", name.local_name, protocol.name ), } } Ok(XmlEvent::EndElement { name }) => { assert!( name.local_name == "protocol", "Unexpected closing token `{}`", name.local_name ); break; } e => panic!("Ill-formed protocol file: {:?}", e), } } protocol } fn parse_interface(reader: &mut EventReader, attrs: Vec) -> Interface { let mut interface = Interface::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => interface.name = attr.value, "version" => interface.version = attr.value.parse().unwrap(), _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => { interface.description = Some(parse_description(reader, attributes)) } "request" => interface.requests.push(parse_request(reader, attributes)), "event" => interface.events.push(parse_event(reader, attributes)), "enum" => interface.enums.push(parse_enum(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "interface" => break, _ => {} } } interface } fn parse_description( reader: &mut EventReader, attrs: Vec, ) -> (String, String) { let mut summary = String::new(); for attr in attrs { if &attr.name.local_name[..] == "summary" { summary = attr.value.split_whitespace().collect::>().join(" "); } } let description = match reader.next() { Ok(XmlEvent::Characters(txt)) => { extract_end_tag!(reader => "description"); txt } Ok(XmlEvent::EndElement { ref name }) if name.local_name == "description" => String::new(), e => panic!("Ill-formed protocol file: {:?}", e), }; (summary, description) } fn parse_request(reader: &mut EventReader, attrs: Vec) -> Message { let mut request = Message::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => request.name = attr.value, "type" => request.typ = Some(parse_type(&attr.value)), "since" => request.since = attr.value.parse().unwrap(), _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => request.description = Some(parse_description(reader, attributes)), "arg" => request.args.push(parse_arg(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "request" => break, _ => {} } } request } fn parse_enum(reader: &mut EventReader, attrs: Vec) -> Enum { let mut enu = Enum::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => enu.name = attr.value, "since" => enu.since = attr.value.parse().unwrap(), "bitfield" => { if &attr.value[..] == "true" { enu.bitfield = true } } _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => enu.description = Some(parse_description(reader, attributes)), "entry" => enu.entries.push(parse_entry(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "enum" => break, _ => {} } } enu } fn parse_event(reader: &mut EventReader, attrs: Vec) -> Message { let mut event = Message::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => event.name = attr.value, "type" => event.typ = Some(parse_type(&attr.value)), "since" => event.since = attr.value.parse().unwrap(), _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => event.description = Some(parse_description(reader, attributes)), "arg" => event.args.push(parse_arg(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "event" => break, _ => {} } } event } fn parse_arg(reader: &mut EventReader, attrs: Vec) -> Arg { let mut arg = Arg::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => arg.name = attr.value, "type" => arg.typ = parse_type(&attr.value), "summary" => { arg.summary = Some(attr.value.split_whitespace().collect::>().join(" ")) } "interface" => arg.interface = Some(attr.value), "allow-null" => { if attr.value == "true" { arg.allow_null = true } } "enum" => arg.enum_ = Some(attr.value), _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => arg.description = Some(parse_description(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "arg" => break, _ => {} } } arg } fn parse_type(txt: &str) -> Type { match txt { "int" => Type::Int, "uint" => Type::Uint, "fixed" => Type::Fixed, "string" => Type::String, "object" => Type::Object, "new_id" => Type::NewId, "array" => Type::Array, "fd" => Type::Fd, "destructor" => Type::Destructor, e => panic!("Unexpected type: {}", e), } } fn parse_entry(reader: &mut EventReader, attrs: Vec) -> Entry { let mut entry = Entry::new(); for attr in attrs { match &attr.name.local_name[..] { "name" => entry.name = attr.value, "value" => { entry.value = if attr.value.starts_with("0x") { u32::from_str_radix(&attr.value[2..], 16).unwrap() } else { attr.value.parse().unwrap() }; } "since" => entry.since = attr.value.parse().unwrap(), "summary" => { entry.summary = Some(attr.value.split_whitespace().collect::>().join(" ")) } _ => {} } } loop { match reader.next() { Ok(XmlEvent::StartElement { name, attributes, .. }) => match &name.local_name[..] { "description" => entry.description = Some(parse_description(reader, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, Ok(XmlEvent::EndElement { ref name }) if name.local_name == "entry" => break, _ => {} } } entry } wayland-scanner-0.29.5/src/protocol.rs000064400000000000000000000077531046102023000160210ustar 00000000000000use proc_macro2::TokenStream; use quote::quote; #[derive(Clone, Debug)] pub struct Protocol { pub name: String, pub copyright: Option, pub description: Option<(String, String)>, pub interfaces: Vec, } 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, pub events: Vec, pub enums: Vec, } 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, pub since: u32, pub description: Option<(String, String)>, pub args: Vec, pub type_index: usize, } impl Message { pub fn new() -> Message { Message { name: String::new(), typ: None, since: 1, description: None, args: Vec::new(), type_index: 0, } } 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, pub summary: Option, pub description: Option<(String, String)>, pub allow_null: bool, pub enum_: Option, } 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, 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, } 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 { match self { Type::String | Type::Object | Type::NewId | Type::Array => true, _ => false, } } pub fn rust_type(self) -> TokenStream { match self { Type::Int => quote!(i32), Type::Uint => quote!(u32), Type::Fixed => quote!(f64), Type::Array => quote!(Vec), Type::Fd => quote!(::std::os::unix::io::RawFd), Type::String => quote!(String), Type::Object => quote!(ProxyId), _ => quote!(()), } } 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."), } } } wayland-scanner-0.29.5/src/side.rs000064400000000000000000000011051046102023000150650ustar 00000000000000use proc_macro2::{Ident, Span}; use self::Side::{Client, Server}; /// Side to generate /// /// This enum represents the two possible sides of /// the protocol API that can be generated. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Side { /// wayland client applications Client, /// wayland compositors Server, } impl Side { pub(crate) fn object_name(self) -> Ident { Ident::new( match self { Client => "Proxy", Server => "Resource", }, Span::call_site(), ) } } wayland-scanner-0.29.5/src/util.rs000064400000000000000000000041321046102023000151210ustar 00000000000000use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::ToTokens; pub fn is_keyword(txt: &str) -> bool { match 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" => true, _ => false, } } pub fn is_camel_keyword(txt: &str) -> bool { match txt { "Self" => true, _ => false, } } 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::(); 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!(), } } 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) }