1 | use std::cmp; |
2 | use std::iter::repeat; |
3 | |
4 | use proc_macro2::{Literal, TokenStream}; |
5 | use quote::{format_ident, quote}; |
6 | |
7 | use crate::protocol::{Interface, Message, Protocol, Type}; |
8 | |
9 | pub(crate) fn generate_interfaces_prefix(protocol: &Protocol) -> TokenStream { |
10 | let longest_nulls = protocol.interfaces.iter().fold(0, |max, interface| { |
11 | let request_longest_null = interface.requests.iter().fold(0, |max, request| { |
12 | if request.all_null() { |
13 | cmp::max(request.args.len(), max) |
14 | } else { |
15 | max |
16 | } |
17 | }); |
18 | let events_longest_null = interface.events.iter().fold(0, |max, event| { |
19 | if event.all_null() { |
20 | cmp::max(event.args.len(), max) |
21 | } else { |
22 | max |
23 | } |
24 | }); |
25 | cmp::max(max, cmp::max(request_longest_null, events_longest_null)) |
26 | }); |
27 | |
28 | let types_null_len = Literal::usize_unsuffixed(longest_nulls); |
29 | |
30 | let nulls = repeat(quote! { NULLPTR as *const wayland_backend::protocol::wl_interface }) |
31 | .take(longest_nulls); |
32 | |
33 | quote! { |
34 | const NULLPTR: *const std::os::raw::c_void = 0 as *const std::os::raw::c_void; |
35 | static mut types_null: [*const wayland_backend::protocol::wl_interface; #types_null_len] = [ |
36 | #(#nulls,)* |
37 | ]; |
38 | } |
39 | } |
40 | |
41 | pub(crate) fn generate_interface(interface: &Interface) -> TokenStream { |
42 | let requests = gen_messages(interface, &interface.requests, "requests" ); |
43 | let events = gen_messages(interface, &interface.events, "events" ); |
44 | |
45 | let interface_ident = format_ident!(" {}_interface" , interface.name); |
46 | let name_value = null_terminated_byte_string_literal(&interface.name); |
47 | let version_value = Literal::i32_unsuffixed(interface.version as i32); |
48 | let request_count_value = Literal::i32_unsuffixed(interface.requests.len() as i32); |
49 | let requests_value = if interface.requests.is_empty() { |
50 | quote! { NULLPTR as *const wayland_backend::protocol::wl_message } |
51 | } else { |
52 | let requests_ident = format_ident!(" {}_requests" , interface.name); |
53 | quote! { unsafe { &#requests_ident as *const _ } } |
54 | }; |
55 | let event_count_value = Literal::i32_unsuffixed(interface.events.len() as i32); |
56 | let events_value = if interface.events.is_empty() { |
57 | quote! { NULLPTR as *const wayland_backend::protocol::wl_message } |
58 | } else { |
59 | let events_ident = format_ident!(" {}_events" , interface.name); |
60 | quote! { unsafe { &#events_ident as *const _ } } |
61 | }; |
62 | |
63 | quote! { |
64 | #requests |
65 | #events |
66 | |
67 | pub static mut #interface_ident: wayland_backend::protocol::wl_interface = wayland_backend::protocol::wl_interface { |
68 | name: #name_value as *const u8 as *const std::os::raw::c_char, |
69 | version: #version_value, |
70 | request_count: #request_count_value, |
71 | requests: #requests_value, |
72 | event_count: #event_count_value, |
73 | events: #events_value, |
74 | }; |
75 | } |
76 | } |
77 | |
78 | fn gen_messages(interface: &Interface, messages: &[Message], which: &str) -> TokenStream { |
79 | if messages.is_empty() { |
80 | return TokenStream::new(); |
81 | } |
82 | |
83 | let types_arrays = messages.iter().filter_map(|msg| { |
84 | if msg.all_null() { |
85 | None |
86 | } else { |
87 | let array_ident = format_ident!(" {}_ {}_ {}_types" , interface.name, which, msg.name); |
88 | let array_len = Literal::usize_unsuffixed(msg.args.len()); |
89 | let array_values = msg.args.iter().map(|arg| match (arg.typ, &arg.interface) { |
90 | (Type::Object, &Some(ref inter)) | (Type::NewId, &Some(ref inter)) => { |
91 | let interface_ident =format_ident!(" {}_interface" , inter); |
92 | quote! { unsafe { &#interface_ident as *const wayland_backend::protocol::wl_interface } } |
93 | } |
94 | _ => quote! { NULLPTR as *const wayland_backend::protocol::wl_interface }, |
95 | }); |
96 | |
97 | Some(quote! { |
98 | static mut #array_ident: [*const wayland_backend::protocol::wl_interface; #array_len] = [ |
99 | #(#array_values,)* |
100 | ]; |
101 | }) |
102 | } |
103 | }); |
104 | |
105 | let message_array_ident = format_ident!(" {}_ {}" , interface.name, which); |
106 | let message_array_len = Literal::usize_unsuffixed(messages.len()); |
107 | let message_array_values = messages.iter().map(|msg| { |
108 | let name_value = null_terminated_byte_string_literal(&msg.name); |
109 | let signature_value = Literal::byte_string(&message_signature(msg)); |
110 | |
111 | let types_ident = if msg.all_null() { |
112 | format_ident!("types_null" ) |
113 | } else { |
114 | format_ident!(" {}_ {}_ {}_types" , interface.name, which, msg.name) |
115 | }; |
116 | |
117 | quote! { |
118 | wayland_backend::protocol::wl_message { |
119 | name: #name_value as *const u8 as *const std::os::raw::c_char, |
120 | signature: #signature_value as *const u8 as *const std::os::raw::c_char, |
121 | types: unsafe { &#types_ident as *const _ }, |
122 | } |
123 | } |
124 | }); |
125 | |
126 | quote! { |
127 | #(#types_arrays)* |
128 | |
129 | pub static mut #message_array_ident: [wayland_backend::protocol::wl_message; #message_array_len] = [ |
130 | #(#message_array_values,)* |
131 | ]; |
132 | } |
133 | } |
134 | |
135 | fn message_signature(msg: &Message) -> Vec<u8> { |
136 | let mut res = Vec::new(); |
137 | |
138 | if msg.since > 1 { |
139 | res.extend_from_slice(msg.since.to_string().as_bytes()); |
140 | } |
141 | |
142 | for arg in &msg.args { |
143 | if arg.typ.nullable() && arg.allow_null { |
144 | res.push(b'?' ); |
145 | } |
146 | match arg.typ { |
147 | Type::NewId => { |
148 | if arg.interface.is_none() { |
149 | res.extend_from_slice(b"su" ); |
150 | } |
151 | res.push(b'n' ); |
152 | } |
153 | Type::Uint => res.push(b'u' ), |
154 | Type::Fixed => res.push(b'f' ), |
155 | Type::String => res.push(b's' ), |
156 | Type::Object => res.push(b'o' ), |
157 | Type::Array => res.push(b'a' ), |
158 | Type::Fd => res.push(b'h' ), |
159 | Type::Int => res.push(b'i' ), |
160 | _ => {} |
161 | } |
162 | } |
163 | |
164 | res.push(0); |
165 | res |
166 | } |
167 | |
168 | pub fn null_terminated_byte_string_literal(string: &str) -> Literal { |
169 | let mut val: Vec = Vec::with_capacity(string.len() + 1); |
170 | val.extend_from_slice(string.as_bytes()); |
171 | val.push(0); |
172 | |
173 | Literal::byte_string(&val) |
174 | } |
175 | |