1use std::cmp;
2use std::iter::repeat;
3
4use proc_macro2::{Literal, TokenStream};
5use quote::{format_ident, quote};
6
7use crate::protocol::{Interface, Message, Protocol, Type};
8
9pub(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
41pub(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
78fn 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
135fn 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
168pub 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