1 use std::io::{self, Write};
2 
3 use super::{
4     capitalize, emit_doc_field, emit_doc_text, enum_suffix_exception, expr_fixed_length,
5     extract_module, field_name, has_fd, make_field, tit_dig_split, CodeGen,
6 };
7 use crate::ast::{Doc, Expr, Reply, Struct, StructField, SwitchCase};
8 
9 impl CodeGen {
has_ffi_type(&self, typ: &str) -> bool10     fn has_ffi_type(&self, typ: &str) -> bool {
11         self.ffi_typ_reg.contains(typ)
12     }
13 
14     /// FFI type name
ffi_decl_type_name(&self, typ: &str) -> String15     pub fn ffi_decl_type_name(&self, typ: &str) -> String {
16         let typ = tit_dig_split(typ).to_ascii_lowercase();
17         format!("xcb_{}{}_t", &self.xcb_mod_prefix, typ)
18     }
19 
20     /// same as ffi_decl_type_name but can also have a namespace before (with a single colon)
ffi_use_type_name(&self, typ: &str) -> String21     pub fn ffi_use_type_name(&self, typ: &str) -> String {
22         match typ {
23             "CARD8" => "u8".into(),
24             "CARD16" => "u16".into(),
25             "CARD32" => "u32".into(),
26             "CARD64" => "u64".into(),
27             "INT8" => "i8".into(),
28             "INT16" => "i16".into(),
29             "INT32" => "i32".into(),
30             "BYTE" => "u8".into(),
31             "BOOL" => "u8".into(),
32             "char" => "c_char".into(),
33             "float" => "f32".into(),
34             "double" => "f64".into(),
35             "void" => "c_void".into(),
36             typ => {
37                 let (module, typ) = extract_module(typ);
38 
39                 if let Some(module) = module {
40                     let typ = tit_dig_split(typ).to_ascii_lowercase();
41                     let mod_prefix = if module == "xproto" {
42                         String::new()
43                     } else {
44                         format!("{}_", module)
45                     };
46                     format!("xcb_{}{}_t", &mod_prefix, typ)
47                 } else {
48                     let mod_prefix = if self.has_type(typ) {
49                         &self.xcb_mod_prefix
50                     } else {
51                         let mut pref = "";
52 
53                         for di in self.dep_info.iter() {
54                             if di.has_type(typ) {
55                                 pref = &di.xcb_mod_prefix;
56                                 break;
57                             }
58                         }
59 
60                         pref
61                     };
62                     let typ = tit_dig_split(typ).to_ascii_lowercase();
63                     format!("xcb_{}{}_t", mod_prefix, typ)
64                 }
65             }
66         }
67     }
68 
ffi_iterator_name(&self, typ: &str) -> String69     pub fn ffi_iterator_name(&self, typ: &str) -> String {
70         let mod_prefix = if self.has_type(typ) {
71             &self.xcb_mod_prefix
72         } else {
73             let mut pref = "";
74 
75             for di in self.dep_info.iter() {
76                 if di.has_type(typ) {
77                     pref = &di.xcb_mod_prefix;
78                     break;
79                 }
80             }
81 
82             pref
83         };
84         format!(
85             "xcb_{}{}_iterator_t",
86             &mod_prefix,
87             tit_dig_split(typ).to_ascii_lowercase()
88         )
89     }
90 
ffi_type_sizeof(&self, typ: &str) -> Option<usize>91     pub fn ffi_type_sizeof(&self, typ: &str) -> Option<usize> {
92         match typ {
93             "CARD8" => Some(1),
94             "CARD16" => Some(2),
95             "CARD32" => Some(4),
96             "CARD64" => Some(8),
97             "INT8" => Some(1),
98             "INT16" => Some(2),
99             "INT32" => Some(4),
100             "BYTE" => Some(1),
101             "BOOL" => Some(1),
102             "char" => Some(1),
103             "float" => Some(4),
104             "double" => Some(8),
105             "void" => Some(0),
106             typ => {
107                 // FIXME: handle module
108                 let (_, typ) = extract_module(typ);
109 
110                 if let Some(sz) = self.ffi_type_sizes.get(typ) {
111                     *sz
112                 } else {
113                     // checking in the imported dependencies
114                     for di in self.dep_info.iter() {
115                         if let Some(sz) = di.ffi_type_sizes.get(typ) {
116                             return *sz;
117                         }
118                     }
119                     None
120                 }
121             }
122         }
123     }
124 
ffi_enum_type_name(&mut self, typ: &str) -> String125     pub fn ffi_enum_type_name(&mut self, typ: &str) -> String {
126         let base = tit_dig_split(typ).to_ascii_lowercase();
127         let try1 = format!("xcb_{}{}_t", self.xcb_mod_prefix, base);
128         if self.has_ffi_type(&try1) || enum_suffix_exception(&self.xcb_mod, typ) {
129             format!("xcb_{}{}_enum_t", self.xcb_mod_prefix, base)
130         } else {
131             try1
132         }
133     }
134 
compute_ffi_struct_field_sizeof(&self, field: &StructField) -> Option<usize>135     pub fn compute_ffi_struct_field_sizeof(&self, field: &StructField) -> Option<usize> {
136         match field {
137             StructField::Field { typ, .. } => self.ffi_type_sizeof(typ),
138             StructField::Pad(_, pad_sz) => Some(*pad_sz),
139             StructField::List { typ, len_expr, .. } => {
140                 match (self.ffi_type_sizeof(typ), expr_fixed_length(len_expr)) {
141                     (Some(sz), Some(len)) => Some(sz * len),
142                     _ => None,
143                 }
144             }
145             StructField::ListNoLen { .. } => None,
146             f => unimplemented!("{:?}", f),
147         }
148     }
149 
compute_ffi_struct_size(&self, stru: &Struct) -> Option<usize>150     pub fn compute_ffi_struct_size(&self, stru: &Struct) -> Option<usize> {
151         let mut stru_sz = Some(0usize);
152 
153         for f in stru.fields.iter() {
154             match f {
155                 StructField::AlignPad(_, alignment) => match stru_sz.as_mut() {
156                     Some(sz) => {
157                         let curr = *sz % alignment;
158                         if curr != 0 {
159                             *sz += alignment - curr;
160                         }
161                     }
162                     _ => {
163                         return None;
164                     }
165                 },
166                 field => {
167                     match (
168                         stru_sz.as_mut(),
169                         self.compute_ffi_struct_field_sizeof(field),
170                     ) {
171                         (Some(stru_sz), Some(f_sz)) => {
172                             *stru_sz += f_sz;
173                         }
174                         _ => {
175                             return None;
176                         }
177                     }
178                 }
179             }
180         }
181 
182         stru_sz
183     }
184 
compute_ffi_union_size(&self, stru: &Struct) -> usize185     pub fn compute_ffi_union_size(&self, stru: &Struct) -> usize {
186         let mut biggest = 1;
187         let mut alignment = 1;
188 
189         for f in stru.fields.iter() {
190             let mut fs = self.ptr_width;
191             let mut fa = self.ptr_width;
192             match f {
193                 StructField::AlignPad(_, _) => panic!("Unexpected align pad in union"),
194                 StructField::Pad(_, _) => panic!("Unexpected pad in union"),
195                 StructField::Field { typ, .. } => {
196                     if let Some(sz) = self.ffi_type_sizeof(typ) {
197                         fs = sz;
198                         fa = sz;
199                     }
200                 }
201                 StructField::List { typ, len_expr, .. } => {
202                     if let Some(sz) = self.ffi_type_sizeof(typ) {
203                         fs = sz;
204                         fa = sz;
205                     }
206                     if let Some(len) = expr_fixed_length(len_expr) {
207                         fs = len * fa;
208                     }
209                 }
210                 StructField::ListNoLen { .. } => panic!("Unexpected list without length in union"),
211 
212                 f => unimplemented!("{:?}", f),
213             }
214 
215             biggest = biggest.max(fs);
216             alignment = alignment.max(fa);
217         }
218 
219         let mut num_aligned = biggest / alignment;
220         if biggest % alignment > 0 {
221             num_aligned += 1;
222         }
223         num_aligned * alignment
224     }
225 
emit_ffi_iterator( &mut self, name: &str, typ: &str, has_lifetime: bool, ) -> io::Result<String>226     pub fn emit_ffi_iterator(
227         &mut self,
228         name: &str,
229         typ: &str,
230         has_lifetime: bool,
231     ) -> io::Result<String> {
232         let it_typ = self.ffi_iterator_name(name);
233         let it_next = iterator_next_fn_name(&self.xcb_mod_prefix, name);
234         let it_end = iterator_end_fn_name(&self.xcb_mod_prefix, name);
235 
236         let out = &mut self.ffi;
237 
238         let lifetime = if has_lifetime { "<'a>" } else { "" };
239 
240         writeln!(out)?;
241         writeln!(out, "#[repr(C)]")?;
242         writeln!(out, "#[derive(Debug)]")?;
243         writeln!(out, "pub struct {}{} {{", &it_typ, lifetime)?;
244         writeln!(out, "    pub data:  *mut {},", &typ)?;
245         writeln!(out, "    pub rem:   c_int,")?;
246         writeln!(out, "    pub index: c_int,")?;
247         if has_lifetime {
248             writeln!(out, "    _phantom: std::marker::PhantomData<&'a {}>,", &typ)?;
249         }
250         writeln!(out, "}}")?;
251 
252         let out = &mut self.ffi_buf;
253         writeln!(out).unwrap();
254         writeln!(out, "pub fn {}(i: *mut {});", &it_next, &it_typ).unwrap();
255         writeln!(out).unwrap();
256         writeln!(
257             out,
258             "pub fn {}(i: *mut {}) -> xcb_generic_iterator_t;",
259             &it_end, &it_typ
260         )
261         .unwrap();
262         Ok(it_typ)
263     }
264 
emit_ffi_field_list_accessor( &mut self, ffi_typ: &str, xcb_name: &str, fname: &str, ftyp: &str, toplevel_typ: Option<&str>, fixed_size: bool, ) -> io::Result<()>265     fn emit_ffi_field_list_accessor(
266         &mut self,
267         ffi_typ: &str,
268         xcb_name: &str,
269         fname: &str,
270         ftyp: &str,
271         toplevel_typ: Option<&str>,
272         fixed_size: bool,
273     ) -> io::Result<()> {
274         let is_simple = self.typ_is_simple(ftyp);
275 
276         let accessor_needed = fixed_size;
277         let length_needed = true;
278         let end_needed = is_simple;
279         let iterator_needed = !is_simple;
280 
281         let has_lifetime = self.type_has_lifetime(ftyp);
282 
283         let args = if let Some(toplevel_typ) = toplevel_typ {
284             format!(
285                 "R: *const {}, S: *const {}",
286                 self.ffi_use_type_name(toplevel_typ),
287                 ffi_typ
288             )
289         } else {
290             format!("R: *const {}", ffi_typ)
291         };
292 
293         if accessor_needed {
294             let ftyp = self.ffi_use_type_name(ftyp);
295             let acc_fn = field_list_iterator_acc_fn_name(&self.xcb_mod_prefix, xcb_name, fname);
296             // the following only for diff equality with Python code
297             let param = if toplevel_typ.is_some() { "S" } else { "R" };
298             let out = &mut self.ffi_buf;
299             writeln!(out)?;
300             writeln!(
301                 out,
302                 "pub fn {}({}: *const {}) -> *mut {};",
303                 &acc_fn, param, &ffi_typ, &ftyp
304             )?;
305         }
306 
307         if length_needed {
308             let len_fn = field_list_iterator_len_fn_name(&self.xcb_mod_prefix, xcb_name, fname);
309             let out = &mut self.ffi_buf;
310             writeln!(out)?;
311             writeln!(out, "pub fn {}({}) -> c_int;", &len_fn, &args)?;
312         }
313 
314         if end_needed {
315             let end_fn = field_list_iterator_end_fn_name(&self.xcb_mod_prefix, xcb_name, fname);
316             let out = &mut self.ffi_buf;
317             writeln!(out)?;
318             writeln!(
319                 out,
320                 "pub fn {}({}) -> xcb_generic_iterator_t;",
321                 &end_fn, &args
322             )?;
323         }
324 
325         if iterator_needed {
326             let lifetime = if has_lifetime { "<'a>" } else { "" };
327             let it_fn = field_list_iterator_it_fn_name(&self.xcb_mod_prefix, xcb_name, fname);
328             let it_typ = self.ffi_iterator_name(ftyp);
329             let out = &mut self.ffi_buf;
330             writeln!(out)?;
331             writeln!(
332                 out,
333                 "pub fn {}{}({}) -> {}{};",
334                 &it_fn, &lifetime, &args, &it_typ, &lifetime
335             )?;
336         }
337         Ok(())
338     }
339 
emit_ffi_field_list_accessors( &mut self, ffi_typ: &str, xcb_name: &str, fields: &[StructField], toplevel_typ: Option<&str>, is_switch: bool, ) -> io::Result<()>340     pub fn emit_ffi_field_list_accessors(
341         &mut self,
342         ffi_typ: &str,
343         xcb_name: &str,
344         fields: &[StructField],
345         toplevel_typ: Option<&str>,
346         is_switch: bool,
347     ) -> io::Result<()> {
348         for f in fields {
349             match f {
350                 StructField::List {
351                     name,
352                     typ,
353                     len_expr,
354                 } => {
355                     let fixed_size = self.ffi_type_sizeof(typ).is_some();
356                     let fixed_len = expr_fixed_length(len_expr).is_some();
357 
358                     if fixed_size && fixed_len {
359                         continue;
360                     }
361                     self.emit_ffi_field_list_accessor(
362                         ffi_typ,
363                         xcb_name,
364                         name,
365                         typ,
366                         toplevel_typ,
367                         fixed_size,
368                     )?;
369                 }
370                 StructField::ListNoLen {
371                     name, typ
372                 } => {
373                     let fixed_size = self.ffi_type_sizeof(typ).is_some();
374                     self.emit_ffi_field_list_accessor(
375                         ffi_typ,
376                         xcb_name,
377                         name,
378                         typ,
379                         toplevel_typ,
380                         fixed_size,
381                     )?;
382                 }
383                 StructField::ValueParam { list_name, .. } => {
384                     self.emit_ffi_field_list_accessor(
385                         ffi_typ,
386                         xcb_name,
387                         list_name,
388                         "CARD32",
389                         toplevel_typ,
390                         true,
391                     )?;
392                 }
393                 StructField::Field { name, typ, .. } => {
394                     if is_switch && !self.typ_is_pod(typ) {
395                         let fn_name = switch_accessor_fn(&self.xcb_mod_prefix, xcb_name, name);
396                         let ret = self.ffi_use_type_name(typ);
397                         let out = &mut self.ffi_buf;
398                         writeln!(
399                             out,
400                             "pub fn {}(R: *const {}) -> *mut {};",
401                             &fn_name, ffi_typ, ret,
402                         )?;
403                     }
404                 }
405                 StructField::Switch(name, ..) => {
406                     let fn_name = switch_accessor_fn(&self.xcb_mod_prefix, xcb_name, name);
407                     let ret = if is_switch {
408                         let typ = xcb_name.to_string() + &capitalize(name);
409                         self.notify_typ(typ.to_string());
410                         self.ffi_use_type_name(&typ)
411                     } else {
412                         "c_void".to_string()
413                     };
414 
415                     let out = &mut self.ffi_buf;
416 
417                     writeln!(
418                         out,
419                         "pub fn {}(R: *const {}) -> *mut {};",
420                         &fn_name, ffi_typ, ret,
421                     )?;
422                 }
423                 _ => {}
424             }
425         }
426         Ok(())
427     }
428 
emit_ffi_struct( &mut self, stru: &Struct, case_req_name: Option<&str>, must_pack: bool, no_copy: bool, is_switch: bool, ) -> io::Result<String>429     pub fn emit_ffi_struct(
430         &mut self,
431         stru: &Struct,
432         case_req_name: Option<&str>,
433         must_pack: bool,
434         no_copy: bool,
435         is_switch: bool,
436     ) -> io::Result<String> {
437         let Struct { name, fields, doc } = &stru;
438 
439         let ffi_typ = if name.starts_with('_') {
440             name.clone()
441         } else {
442             self.ffi_decl_type_name(name)
443         };
444 
445         let impl_copy_clone = must_pack || self.eligible_to_copy(stru) && !no_copy;
446 
447         let copyclone = if impl_copy_clone { "Copy, Clone, " } else { "" };
448 
449         {
450             let must_pack = if must_pack { ", packed" } else { "" };
451 
452             let out = &mut self.ffi;
453             writeln!(out)?;
454             emit_doc_text(out, doc)?;
455             writeln!(out, "#[derive({}Debug)]", copyclone)?;
456             writeln!(out, "#[repr(C{})]", must_pack)?;
457             writeln!(out, "pub struct {} {{", &ffi_typ)?;
458         }
459 
460         // cases of ValueParam were the mask is declared as a field in the struct
461         // before the actual ValueParam field. We keep track of all fields written
462         // to avoid doubles
463         let mut written_fields = Vec::new();
464 
465         for f in fields.iter() {
466             match f {
467                 StructField::Field { name, typ, .. } | StructField::Expr { name, typ, .. } => {
468                     let ptr = if self.ffi_type_sizeof(typ).is_some() {
469                         ""
470                     } else {
471                         "*mut "
472                     };
473                     //let ptr = if self.typ_is_pod(&typ) { "" } else {"*mut "};
474                     let typ = self.ffi_use_type_name(typ);
475                     let out = &mut self.ffi;
476                     emit_doc_field(out, doc, name)?;
477                     writeln!(out, "    pub {}: {}{},", field_name(name), ptr, typ,)?;
478                     written_fields.push(name.as_str());
479                 }
480                 StructField::Pad(name, sz) => {
481                     let out = &mut self.ffi;
482                     let padtyp = match sz {
483                         1 => "u8".into(),
484                         x => format!("[u8; {}]", x),
485                     };
486                     writeln!(out, "    pub {}: {},", name, padtyp)?;
487                 }
488                 StructField::AlignPad(name, _) => {
489                     let out = &mut self.ffi;
490                     if is_switch {
491                         writeln!(out, "    pub {}: *mut u8,", name)?;
492                     }
493                 }
494                 StructField::List {
495                     name,
496                     typ,
497                     len_expr,
498                 } => {
499                     if let Some(sz) = expr_fixed_length(len_expr) {
500                         let typ = self.ffi_use_type_name(typ);
501                         let out = &mut self.ffi;
502 
503                         emit_doc_field(out, doc, name)?;
504                         writeln!(out, "    pub {}: [{}; {}],", field_name(name), &typ, sz)?;
505                     } else if is_switch {
506                         let typ = self.ffi_use_type_name(typ);
507                         let out = &mut self.ffi;
508 
509                         emit_doc_field(out, doc, name)?;
510                         writeln!(out, "    pub {}: *mut {},", field_name(name), &typ)?;
511                     }
512                 }
513                 StructField::ValueParam {
514                     mask_name,
515                     mask_typ,
516                     ..
517                 } => {
518                     if written_fields.contains(&mask_name.as_str()) {
519                         continue;
520                     }
521                     let mask_typ = self.ffi_use_type_name(mask_typ);
522                     let out = &mut self.ffi;
523                     emit_doc_field(out, doc, mask_name)?;
524                     writeln!(out, "    pub {}: {},", field_name(mask_name), mask_typ,)?;
525                 }
526                 StructField::Switch(name, _, _) => {
527                     if let Some(case_req_name) = case_req_name {
528                         let typ = switch_struct_name(&self.xcb_mod_prefix, case_req_name, name);
529                         let out = &mut self.ffi;
530                         writeln!(out, "    pub {}: {},", field_name(name), typ)?;
531                     }
532                 }
533                 StructField::NamedCase(name, typ) => {
534                     let out = &mut self.ffi;
535                     writeln!(out, "    pub {}: {},", field_name(name), typ)?;
536                 }
537                 _ => {}
538             }
539         }
540 
541         let out = &mut self.ffi;
542         writeln!(out, "}}")?;
543 
544         Ok(ffi_typ)
545     }
546 
emit_ffi_switch_struct( &mut self, typ_name: &str, switch_name: &str, _expr: &Expr<usize>, cases: &[SwitchCase], toplevel_typ: &str, parent_switch: Option<&str>, ) -> io::Result<String>547     pub fn emit_ffi_switch_struct(
548         &mut self,
549         typ_name: &str,
550         switch_name: &str,
551         _expr: &Expr<usize>,
552         cases: &[SwitchCase],
553         toplevel_typ: &str,
554         parent_switch: Option<&str>,
555     ) -> io::Result<String> {
556         let fields = {
557             let mut fields = Vec::new();
558             for c in cases.iter() {
559                 if let Some(name) = &c.name {
560                     let typ = switch_named_case(&self.xcb_mod_prefix, typ_name, switch_name, name);
561                     fields.push(StructField::NamedCase(name.clone(), typ));
562                 } else {
563                     fields.append(&mut c.fields.clone());
564                 }
565             }
566             fields
567         };
568 
569         let stru_name = typ_name.to_string() + &capitalize(switch_name);
570 
571         let stru = Struct {
572             name: stru_name.clone(),
573             fields,
574             doc: None,
575         };
576         let ffi_typ = self.emit_ffi_struct(&stru, None, false, true, true)?;
577 
578         let ffi_typ = parent_switch.unwrap_or(&ffi_typ);
579 
580         self.emit_ffi_field_list_accessors(
581             ffi_typ,
582             &stru_name,
583             &stru.fields,
584             Some(toplevel_typ),
585             true,
586         )?;
587 
588         for c in cases.iter() {
589             if let Some(name) = &c.name {
590                 let typ = switch_named_case(&self.xcb_mod_prefix, typ_name, switch_name, name);
591                 let stru = Struct {
592                     name: typ.clone(),
593                     fields: c.fields.clone(),
594                     doc: None,
595                 };
596                 let case_req_name = stru_name.clone() + &capitalize(name);
597                 self.emit_ffi_struct(&stru, Some(&case_req_name), false, true, true)?;
598 
599                 self.emit_ffi_field_list_accessors(
600                     ffi_typ,
601                     &case_req_name,
602                     &stru.fields,
603                     Some(toplevel_typ),
604                     true,
605                 )?;
606             }
607         }
608         for c in cases.iter() {
609             for f in c.fields.iter() {
610                 if let StructField::Switch(cname, cexpr, ccases) = f {
611                     let stru_name = if let Some(name) = &c.name {
612                         stru_name.clone() + &capitalize(name)
613                     } else {
614                         stru_name.clone()
615                     };
616                     self.emit_ffi_switch_struct(
617                         &stru_name,
618                         cname,
619                         cexpr,
620                         ccases,
621                         toplevel_typ,
622                         Some(ffi_typ),
623                     )?;
624                 }
625             }
626         }
627         Ok(ffi_typ.to_string())
628     }
629 
emit_ffi_req_fn( &mut self, req_name: &str, fn_name: &str, cookie_name: &str, fields: &[StructField], doc: &Option<Doc>, ) -> io::Result<()>630     pub fn emit_ffi_req_fn(
631         &mut self,
632         req_name: &str,
633         fn_name: &str,
634         cookie_name: &str,
635         fields: &[StructField],
636         doc: &Option<Doc>,
637     ) -> io::Result<()> {
638         let cookie_typ = self.ffi_use_type_name(cookie_name);
639         {
640             let out = &mut self.ffi_buf;
641             writeln!(out)?;
642             emit_doc_text(out, doc)?;
643             writeln!(out, "    pub fn {} (", &fn_name)?;
644             writeln!(out, "        c: *mut xcb_connection_t,")?;
645         }
646         let mut written_fields = Vec::new();
647         for f in fields.iter() {
648             match f {
649                 StructField::Field { name, typ, .. } => {
650                     written_fields.push(name);
651                     let name = field_name(name);
652                     let is_pod = self.typ_is_pod(typ);
653                     let is_simple = self.typ_is_simple(typ);
654                     let typ = self.ffi_use_type_name(typ);
655                     if is_simple || is_pod {
656                         writeln!(&mut self.ffi_buf, "        {}: {},", &name, &typ)?;
657                     } else {
658                         writeln!(&mut self.ffi_buf, "        {}: *mut {},", &name, &typ)?;
659                     }
660                 }
661                 StructField::Fd(name) => {
662                     let name = field_name(name);
663                     writeln!(&mut self.ffi_buf, "        {}: i32,", &name)?;
664                 }
665                 StructField::ValueParam {
666                     mask_typ,
667                     mask_name,
668                     list_name,
669                 } => {
670                     let mask_typ = self.ffi_use_type_name(mask_typ);
671                     let list_name = field_name(list_name);
672 
673                     let out = &mut self.ffi_buf;
674                     if !written_fields.contains(&mask_name) {
675                         let mask_name = field_name(mask_name);
676                         writeln!(out, "        {}: {},", &mask_name, &mask_typ)?;
677                     }
678                     writeln!(out, "        {}: *const u32,", &list_name)?;
679                 }
680                 StructField::List { name, typ, .. } => {
681                     let name = field_name(name);
682                     let typ = self.ffi_use_type_name(typ);
683                     let out = &mut self.ffi_buf;
684                     writeln!(out, "        {}: *const {},", &name, &typ)?;
685                 }
686                 StructField::ListNoLen { name, typ } => {
687                     let len_name = name.clone() + "_len";
688                     let name = field_name(name);
689                     let typ = self.ffi_use_type_name(typ);
690                     let out = &mut self.ffi_buf;
691                     writeln!(out, "        {}: u32,", &len_name)?;
692                     writeln!(out, "        {}: *const {},", &name, &typ)?;
693                 }
694                 StructField::Switch(name, ..) => {
695                     let name = field_name(name);
696                     let typ = switch_struct_name(&self.xcb_mod_prefix, req_name, &name);
697                     writeln!(&mut self.ffi_buf, "        {}: *const {},", &name, &typ)?;
698                 }
699                 _ => {}
700             }
701         }
702 
703         let out = &mut self.ffi_buf;
704         writeln!(out, "    ) -> {};", &cookie_typ)?;
705 
706         Ok(())
707     }
708 
emit_ffi_reply( &mut self, req_name: &str, mut reply: Reply, ) -> io::Result<(String, String, String)>709     pub fn emit_ffi_reply(
710         &mut self,
711         req_name: &str,
712         mut reply: Reply,
713     ) -> io::Result<(String, String, String)> {
714         // writting cookie struct
715         let cookie_name = req_name.to_string() + "Cookie";
716         let cookie_ffi_typ = self.ffi_decl_type_name(&cookie_name);
717         {
718             let out = &mut self.ffi;
719             writeln!(out)?;
720             writeln!(out, "#[derive(Copy, Clone, Debug)]")?;
721             writeln!(out, "#[repr(C)]")?;
722             writeln!(out, "pub struct {} {{", &cookie_ffi_typ)?;
723             writeln!(out, "    pub(crate) sequence: c_uint,")?;
724             writeln!(out, "}}")?;
725         }
726 
727         for f in &reply.fields {
728             if let StructField::Switch(name, expr, cases) = f {
729                 let toplevel = req_name.to_string() + "Reply";
730                 self.notify_typ(toplevel.clone());
731                 self.emit_ffi_switch_struct(req_name, name, expr, cases, &toplevel, None)?;
732             }
733         }
734 
735         let reply_fields = {
736             let mut fields = vec![
737                 make_field("response_type".into(), "CARD8".into()),
738                 if reply.fields.is_empty() {
739                     StructField::Pad("pad0".into(), 1usize)
740                 } else {
741                     reply.fields.remove(0)
742                 },
743                 make_field("sequence".into(), "CARD16".into()),
744                 make_field("length".into(), "CARD32".into()),
745             ];
746             fields.append(&mut reply.fields);
747             fields
748         };
749         let reply = Struct {
750             name: req_name.to_string() + "Reply",
751             fields: reply_fields,
752             doc: reply.doc,
753         };
754 
755         let ffi_reply_typ = self.emit_ffi_struct(&reply, None, false, false, false)?;
756 
757         self.emit_ffi_field_list_accessors(&ffi_reply_typ, req_name, &reply.fields, None, false)?;
758 
759         let ffi_reply_fn = reply_fn_name(&self.xcb_mod_prefix, req_name);
760         {
761             let out = &mut self.ffi_buf;
762             writeln!(out)?;
763             writeln!(
764                 out,
765                 "    /// the returned value must be freed by the caller using libc::free()."
766             )?;
767             writeln!(out, "    pub fn {} (", &ffi_reply_fn)?;
768             writeln!(out, "        c: *mut xcb_connection_t,")?;
769             writeln!(out, "        cookie: {},", &cookie_ffi_typ)?;
770             writeln!(out, "        error: *mut *mut xcb_generic_error_t,")?;
771             writeln!(out, "    ) -> *mut {};", &ffi_reply_typ)?;
772         }
773 
774         if has_fd(&reply.fields) {
775             let fds_fn = reply_fds_fn_name(&self.xcb_mod_prefix, req_name);
776             let out = &mut self.ffi_buf;
777             writeln!(out)?;
778             writeln!(out, "    pub fn {}(", &fds_fn)?;
779             writeln!(out, "        c: *mut xcb_connection_t,")?;
780             writeln!(out, "        reply: *mut {},", &ffi_reply_typ)?;
781             writeln!(out, "    ) -> *mut c_int;")?;
782         }
783 
784         Ok((cookie_ffi_typ, ffi_reply_fn, ffi_reply_typ))
785     }
786 }
787 
enum_item_name(xcb_mod_prefix: &str, name: &str, item: &str) -> String788 pub fn enum_item_name(xcb_mod_prefix: &str, name: &str, item: &str) -> String {
789     format!(
790         "XCB_{}{}_{}",
791         xcb_mod_prefix,
792         tit_dig_split(name),
793         tit_dig_split(item)
794     )
795     .to_ascii_uppercase()
796 }
797 
iterator_next_fn_name(xcb_mod_prefix: &str, typ: &str) -> String798 pub fn iterator_next_fn_name(xcb_mod_prefix: &str, typ: &str) -> String {
799     format!(
800         "xcb_{}{}_next",
801         xcb_mod_prefix,
802         tit_dig_split(typ).to_ascii_lowercase()
803     )
804 }
805 
iterator_end_fn_name(xcb_mod_prefix: &str, typ: &str) -> String806 pub fn iterator_end_fn_name(xcb_mod_prefix: &str, typ: &str) -> String {
807     format!(
808         "xcb_{}{}_end",
809         xcb_mod_prefix,
810         tit_dig_split(typ).to_ascii_lowercase()
811     )
812 }
813 
field_list_iterator_acc_fn_name( xcb_mod_prefix: &str, typ_name: &str, field: &str, ) -> String814 pub fn field_list_iterator_acc_fn_name(
815     xcb_mod_prefix: &str,
816     typ_name: &str,
817     field: &str,
818 ) -> String {
819     format!(
820         "xcb_{}{}_{}",
821         &xcb_mod_prefix,
822         tit_dig_split(typ_name).to_ascii_lowercase(),
823         tit_dig_split(field).to_ascii_lowercase()
824     )
825 }
826 
field_list_iterator_len_fn_name( xcb_mod_prefix: &str, typ_name: &str, field: &str, ) -> String827 pub fn field_list_iterator_len_fn_name(
828     xcb_mod_prefix: &str,
829     typ_name: &str,
830     field: &str,
831 ) -> String {
832     format!(
833         "xcb_{}{}_{}_length",
834         &xcb_mod_prefix,
835         tit_dig_split(typ_name).to_ascii_lowercase(),
836         tit_dig_split(field).to_ascii_lowercase()
837     )
838 }
839 
field_list_iterator_end_fn_name( xcb_mod_prefix: &str, typ_name: &str, field: &str, ) -> String840 pub fn field_list_iterator_end_fn_name(
841     xcb_mod_prefix: &str,
842     typ_name: &str,
843     field: &str,
844 ) -> String {
845     format!(
846         "xcb_{}{}_{}_end",
847         &xcb_mod_prefix,
848         tit_dig_split(typ_name).to_ascii_lowercase(),
849         tit_dig_split(field).to_ascii_lowercase()
850     )
851 }
852 
field_list_iterator_it_fn_name(xcb_mod_prefix: &str, typ_name: &str, field: &str) -> String853 pub fn field_list_iterator_it_fn_name(xcb_mod_prefix: &str, typ_name: &str, field: &str) -> String {
854     format!(
855         "xcb_{}{}_{}_iterator",
856         &xcb_mod_prefix,
857         tit_dig_split(typ_name).to_ascii_lowercase(),
858         tit_dig_split(field).to_ascii_lowercase()
859     )
860 }
861 
switch_struct_name(xcb_mod_prefix: &str, req_name: &str, switch_name: &str) -> String862 pub fn switch_struct_name(xcb_mod_prefix: &str, req_name: &str, switch_name: &str) -> String {
863     format!(
864         "xcb_{}{}_{}_t",
865         &xcb_mod_prefix,
866         tit_dig_split(req_name).to_ascii_lowercase(),
867         tit_dig_split(switch_name).to_ascii_lowercase()
868     )
869 }
870 
switch_accessor_fn(xcb_mod_prefix: &str, req_name: &str, switch_name: &str) -> String871 pub fn switch_accessor_fn(xcb_mod_prefix: &str, req_name: &str, switch_name: &str) -> String {
872     format!(
873         "xcb_{}{}_{}",
874         &xcb_mod_prefix,
875         tit_dig_split(req_name).to_ascii_lowercase(),
876         tit_dig_split(switch_name).to_ascii_lowercase()
877     )
878 }
879 
switch_named_case( xcb_mod_prefix: &str, req_name: &str, switch_name: &str, case_name: &str, ) -> String880 pub fn switch_named_case(
881     xcb_mod_prefix: &str,
882     req_name: &str,
883     switch_name: &str,
884     case_name: &str,
885 ) -> String {
886     format!(
887         "_xcb_{}{}_{}__{}",
888         &xcb_mod_prefix,
889         tit_dig_split(req_name).to_ascii_lowercase(),
890         tit_dig_split(switch_name),
891         tit_dig_split(case_name)
892     )
893 }
894 
request_fn_name(xcb_mod_prefix: &str, req_name: &str) -> String895 pub fn request_fn_name(xcb_mod_prefix: &str, req_name: &str) -> String {
896     format!(
897         "xcb_{}{}",
898         &xcb_mod_prefix,
899         tit_dig_split(req_name).to_ascii_lowercase(),
900     )
901 }
902 
reply_fn_name(xcb_mod_prefix: &str, req_name: &str) -> String903 pub fn reply_fn_name(xcb_mod_prefix: &str, req_name: &str) -> String {
904     format!(
905         "xcb_{}{}_reply",
906         &xcb_mod_prefix,
907         tit_dig_split(req_name).to_ascii_lowercase(),
908     )
909 }
910 
reply_fds_fn_name(xcb_mod_prefix: &str, req_name: &str) -> String911 pub fn reply_fds_fn_name(xcb_mod_prefix: &str, req_name: &str) -> String {
912     format!(
913         "xcb_{}{}_reply_fds",
914         &xcb_mod_prefix,
915         tit_dig_split(req_name).to_ascii_lowercase(),
916     )
917 }
918 
opcode_name(xcb_mod_prefix: &str, name: &str) -> String919 pub fn opcode_name(xcb_mod_prefix: &str, name: &str) -> String {
920     format!(
921         "XCB_{}{}",
922         xcb_mod_prefix.to_ascii_uppercase(),
923         tit_dig_split(name).to_ascii_uppercase()
924     )
925 }
926 
emit_opcode<Out: Write>( out: &mut Out, xcb_mod_prefix: &str, name: &str, num: i32, ) -> io::Result<()>927 pub fn emit_opcode<Out: Write>(
928     out: &mut Out,
929     xcb_mod_prefix: &str,
930     name: &str,
931     num: i32,
932 ) -> io::Result<()> {
933     let op_name = opcode_name(xcb_mod_prefix, name);
934     let num_typ = if num < 0 { "i8" } else { "u8" };
935     writeln!(out)?;
936     writeln!(out, "pub const {}: {} = {};", &op_name, &num_typ, num)?;
937     Ok(())
938 }
939