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