1#!/usr/bin/env python3 2# Copyright (c) 2016 Remi Thebault <remi.thebault@gmail.com> 3# 4# Permission is hereby granted, free of charge, to any 5# person obtaining a copy of this software and associated 6# documentation files (the "Software"), to deal in the 7# Software without restriction, including without 8# limitation the rights to use, copy, modify, merge, 9# publish, distribute, sublicense, and/or sell copies of 10# the Software, and to permit persons to whom the Software 11# is furnished to do so, subject to the following 12# conditions: 13# 14# The above copyright notice and this permission notice 15# shall be included in all copies or substantial portions 16# of the Software. 17# 18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 19# ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 20# TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 21# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 22# SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 25# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26# DEALINGS IN THE SOFTWARE. 27''' 28script that generates rust code from xcb xml definitions 29Each invokation of this script generates one ffi file and one 30rust file for an extension or the main X Protocol. 31 32Usage: ./rs_client.py -o src xml/xproto.xml 33''' 34 35import sys 36import os 37import re 38 39 40 41class SourceFile(object): 42 ''' 43 buffer to append code in various sections of a file 44 in any order 45 ''' 46 47 _one_indent_level = ' ' 48 49 def __init__(self): 50 self._section = 0 51 self._lines = [] 52 self._indents = [] 53 54 def getsection(self): 55 return self._section 56 57 def section(self, section): 58 ''' 59 Set the section of the file where to append code. 60 Allows to make different sections in the file to append 61 to in any order 62 ''' 63 while len(self._lines) <= section: 64 self._lines.append([]) 65 while len(self._indents) <= section: 66 self._indents.append(0) 67 self._section = section 68 69 def getindent(self): 70 ''' 71 returns indentation of the current section 72 ''' 73 return self._indents[self._section] 74 75 def setindent(self, indent): 76 ''' 77 sets indentation of the current section 78 ''' 79 self._indents[self._section] = indent; 80 81 def indent_block(self): 82 class Indenter(object): 83 def __init__(self, sf): 84 self.sf = sf 85 def __enter__(self): 86 self.sf.indent() 87 def __exit__(self, type, value, traceback): 88 self.sf.unindent() 89 return Indenter(self) 90 91 def indent(self): 92 ''' 93 adds one level of indentation to the current section 94 ''' 95 self._indents[self._section] += 1 96 97 def unindent(self): 98 ''' 99 removes one level of indentation to the current section 100 ''' 101 assert self.getindent() > 0, "negative indent" 102 self._indents[self._section] -= 1 103 104 def __call__(self, fmt, *args): 105 ''' 106 Append a line to the file at in its current section and 107 indentation of the current section 108 ''' 109 indent = SourceFile._one_indent_level * self._indents[self._section] 110 self._lines[self._section].append(indent + (fmt % args)) 111 112 113 def writeout(self, path): 114 os.makedirs(os.path.dirname(path), exist_ok=True) 115 with open(path, 'w') as f: 116 for section in self._lines: 117 for line in section: 118 print(line.rstrip(), file=f) 119 120 121# FFI source file 122_f = SourceFile() 123 124# Rust interface file 125_r = SourceFile() 126 127# utility to add same code in both files 128def _rf(fmt, *args): 129 _f(fmt, *args) 130 _r(fmt, *args) 131 132 133_ns = None 134_ext_names = {} 135 136# global variable to keep track of serializers and 137# switch data types due to weird dependencies 138finished_serializers = [] 139finished_sizeof = [] 140finished_switch = [] 141 142_types_uneligible_to_copy = [] 143 144# current handler is used for error reporting 145current_handler = None 146 147# Keep tracks of types that have lifetime parameter 148# Initialized with types that are defined in one module and used in other modules 149types_with_lifetime = [ 150 "xcb_str_iterator_t", # defined in xproto, used in render 151 "xcb_xv_image_format_info_iterator_t" # defined in xv, used in xvmc 152] 153 154# link exceptions 155link_exceptions = { 156 "bigreq": "xcb", 157 "xc_misc": "xcb" 158} 159 160 161#type translation 162_ffi_type_translation = { 163 'BOOL': 'u8' 164} 165_rs_type_translation = { 166 'BOOL': 'bool' 167} 168 169# struct with only simple fields are defined as typedef to the ffi struct (issue #7) 170# this list adds exception to this behavior 171_rs_typedef_exceptions = [ 172 # not strictly necessary has Setup has complex fields 173 # however intent is clear: 'xproto::Setup' MUST use StructPtr 174 'xproto::Setup' 175] 176 177# exported functions to xcbgen start by 'rs_' 178 179# starting with opening and closing 180 181def rs_open(module): 182 ''' 183 Handles module open. 184 module is a xcbgen.state.Module object 185 ''' 186 global _ns 187 _ns = module.namespace 188 189 linklib = "xcb" 190 if _ns.is_ext: 191 linklib = 'xcb-' + _ns.header 192 _ext_names[_ns.ext_name.lower()] = _ns.header 193 for (n, h) in module.direct_imports: 194 if h != 'xproto': 195 _ext_names[n.lower()] = h 196 197 if _ns.header in link_exceptions: 198 linklib = link_exceptions[_ns.header] 199 200 ext_id_name = _ffi_name(_ns.prefix + ('id',)) 201 202 _r.section(0) 203 _f.section(0) 204 _rf('// Generated automatically from %s by rs_client.py version %s.', 205 _ns.file, os.getenv('CARGO_PKG_VERSION', 'undefined')) 206 _rf('// Do not edit!') 207 _rf('') 208 209 _f('') 210 _f('use ffi::base::*;') 211 212 if _ns.is_ext: 213 for (n, h) in module.imports: 214 _f('use ffi::%s::*;', _module_name(n)) 215 _f('') 216 _f('use libc::{c_char, c_int, c_uint, c_void};') 217 _f('use std;') 218 _f('') 219 220 _f.section(1) 221 _f('') 222 _f('') 223 _f('#[link(name="%s")]', linklib) 224 _f('extern {') 225 _f.indent() 226 if _ns.is_ext: 227 _f('') 228 _f('pub static mut %s: xcb_extension_t;', ext_id_name) 229 230 231 _r('use base;') 232 if _ns.is_ext: 233 for (n, h) in module.imports: 234 _r('use %s;', _module_name(n)) 235 _r('use ffi::base::*;') 236 _r('use ffi::%s::*;', _module_name(_ns.ext_name)) 237 if _ns.is_ext: 238 for (n, h) in module.imports: 239 _r('use ffi::%s::*;', _module_name(n)) 240 _r('use libc::{self, c_char, c_int, c_uint, c_void};') 241 _r('use std;') 242 _r('use std::iter::Iterator;') 243 _r('') 244 if _ns.is_ext: 245 _r('') 246 _r("pub fn id() -> &'static mut base::Extension {") 247 _r(' unsafe {') 248 _r(' &mut %s', ext_id_name) 249 _r(' }') 250 _r('}') 251 252 _r.section(1) 253 _r('') 254 _r('') 255 256 257 258 if _ns.is_ext: 259 _f.section(0) 260 _f('') 261 _f('pub const %s: u32 = %s;', 262 _ffi_const_name(('xcb', _ns.ext_name, 'major', 'version')), 263 _ns.major_version) 264 _f('pub const %s: u32 = %s;', 265 _ffi_const_name(('xcb', _ns.ext_name, 'minor', 'version')), 266 _ns.minor_version) 267 268 _r.section(0) 269 _r('') 270 _r('pub const MAJOR_VERSION: u32 = %s;', _ns.major_version) 271 _r('pub const MINOR_VERSION: u32 = %s;', _ns.minor_version) 272 273 274 275 EnumCodegen.build_collision_table(module) 276 277 278 279 280def rs_close(module): 281 ''' 282 Handles module close. 283 module is a xcbgen.state.Module object. 284 main task is to write the files out 285 ''' 286 287 _f.section(1) 288 289 _f('') 290 _f.unindent() 291 _f('} // extern') 292 293 _f.writeout(os.path.join(module.rs_srcdir, "ffi", "%s.rs" % _module_name(_ns.ext_name))) 294 _r.writeout(os.path.join(module.rs_srcdir, "%s.rs" % _module_name(_ns.ext_name))) 295 296 297 298# transformation of name tuples 299 300_cname_re = re.compile('([A-Z0-9][a-z]+|[A-Z0-9]+(?![a-z])|[a-z]+)') 301_rs_keywords = ['type', 'str', 'match', 'new'] 302 303 304def _tit_split(string): 305 ''' 306 splits string with '_' on each titlecase letter 307 >>> _tit_split('SomeString') 308 Some_String 309 >>> _tit_split('WINDOW') 310 WINDOW 311 ''' 312 split = _cname_re.finditer(string) 313 name_parts = [match.group(0) for match in split] 314 return '_'.join(name_parts) 315 316def _tit_cap(string): 317 ''' 318 capitalize each substring beggining by a titlecase letter 319 >>> _tit_cap('SomeString') 320 SomeString 321 >>> _tit_cap('WINDOW') 322 Window 323 ''' 324 split = _cname_re.finditer(string) 325 name_parts = [match.group(0) for match in split] 326 name_parts = [i[0].upper() + i[1:].lower() for i in name_parts] 327 return ''.join(name_parts) 328 329 330_extension_special_cases = ['XPrint', 'XCMisc', 'BigRequests'] 331 332def _module_name(name): 333 if len(name): 334 if name in _extension_special_cases: 335 return _tit_split(name).lower() 336 else: 337 return name.lower() 338 else: 339 return 'xproto' 340 341 342def _symbol(string): 343 if string in _rs_keywords: 344 string += '_' 345 return string 346 347def _upper_1st(string): 348 ''' 349 return copy of string with first letter turned into upper. 350 Other letters are untouched. 351 ''' 352 if len(string) == 0: 353 return '' 354 if len(string) == 1: 355 return string.upper() 356 return string[0].upper() + string[1:] 357 358def _upper_name(nametup): 359 ''' 360 return a string made from a nametuple with all upper case 361 joined with underscore 362 >>> _upper_name(('xcb', 'constant', 'AwesomeValue')) 363 XCB_CONSTANT_AWESOME_VALUE 364 ''' 365 return '_'.join(tuple(_tit_split(name) for name in nametup)).upper() 366 367def _cap_name(nametup): 368 ''' 369 return a string made from a nametuple with joined title case 370 >>> _cap_name(('xcb', 'Type', 'Name')) 371 XcbTypeName 372 >>> _cap_name(('xcb', 'TypeName')) 373 XcbTypeName 374 >>> _cap_name(('xcb', 'TYPENAME')) 375 XcbTypename 376 ''' 377 return ''.join(tuple(_upper_1st(name) for name in nametup)) 378 379def _lower_name(nametup): 380 ''' 381 return a string made from a nametuple with all lower case 382 joined with underscore 383 >>> _upper_name(('xcb', 'Ext', 'RequestName')) 384 xcb_ext_request_name 385 ''' 386 return '_'.join(tuple(_tit_split(name) for name in nametup)).lower() 387 388 389def _ext_nametup(nametup): 390 ''' 391 return the nametup with 2nd name lowered if module is an extension 392 >>> _ext_nametup(('u32',)) 393 ('u32',) 394 >>> _ext_nametup(('xcb', 'XprotoType')) 395 ('xcb', 'XprotoType') 396 >>> _ext_nametup(('xcb', 'RandR', 'SuperType')) 397 ('xcb', 'randr', 'SuperType') 398 ''' 399 if len(nametup) > 2 and nametup[1].lower() in _ext_names: 400 #nametup = tuple(_ext_names[name.lower()] if i == 1 else name 401 # for (i, name) in enumerate(nametup)) 402 # lowers extension to avoid '_' split with title letters 403 nametup = tuple(_module_name(name) if i == 1 else name 404 for (i, name) in enumerate(nametup)) 405 return nametup 406 407def _ffi_type_name(nametup): 408 ''' 409 turns the nametup into a FFI type 410 >>> _ffi_type_name(('u32',)) 411 u32 412 >>> _ffi_type_name(('xcb', 'XprotoType')) 413 xcb_xproto_type_t 414 >>> _ffi_type_name(('xcb', 'RandR', 'SuperType')) 415 xcb_randr_super_type_t 416 ''' 417 if len(nametup) == 1: 418 # handles SimpleType 419 if nametup[0] in _ffi_type_translation: 420 return _ffi_type_translation[nametup[0]] 421 return nametup[0] 422 return _ffi_name(nametup + ('t',)) 423 424 425def _ffi_name(nametup): 426 ''' 427 turns the nametup into a FFI name 428 >>> _ffi_type_name(('u32',)) 429 u32 430 >>> _ffi_type_name(('xcb', 'XprotoType', 't')) 431 xcb_xproto_type_t 432 >>> _ffi_type_name(('xcb', 'RandR', 'SuperType', 't')) 433 xcb_randr_super_type_t 434 ''' 435 secondIsExt = (len(nametup) > 2 and nametup[1].lower() in _ext_names) 436 nametup = _ext_nametup(nametup) 437 438 if secondIsExt: 439 return '_'.join(tuple(name if i==1 else _tit_split(name) 440 for (i, name) in enumerate(nametup))).lower() 441 else: 442 return '_'.join(tuple(_tit_split(name) for name in nametup)).lower() 443 444 445def _ffi_const_name(nametup): 446 return _ffi_name(_ext_nametup(nametup)).upper() 447 448 449def _rs_extract_module(nametup): 450 ''' 451 returns the module extracted from nametup 452 along with the nametup without the module parts 453 if module is local module, an empty module is returned 454 >>> _rs_extract_module(('u32',)) 455 ("", "u32") 456 >>> _rs_extract_module(('xcb', 'Type')) 457 ("", ("Type")) 458 >>> _rs_extract_module(('xcb', 'RandR', 'SuperType')) 459 ("randr::", ("SuperType")) 460 ''' 461 # handles SimpleType 462 if len(nametup) == 1: 463 return ("", nametup[0]) 464 465 # remove 'xcb' 466 if nametup[0].lower() == 'xcb': 467 nametup = nametup[1:] 468 469 module = '' 470 # handle extension type 471 if nametup[0].lower() in _ext_names: 472 ext = _ext_names[nametup[0].lower()] 473 if (not _ns.is_ext or 474 ext != _ns.header): 475 module = ext + '::' 476 nametup = nametup[1:] 477 478 # handle xproto type for extensions 479 else: 480 if _ns.is_ext: 481 module = 'xproto::' 482 483 return (module, nametup) 484 485 486 487def _rs_type_name(nametup): 488 ''' 489 turns the nametup into a Rust type name 490 foreign rust type names include module prefix 491 >>> _rs_type_name(('u32',)) 492 u32 493 >>> _rs_type_name(('xcb', 'Type')) 494 xproto::Type 495 >>> _rs_type_name(('xcb', 'RandR', 'SuperType')) 496 randr::SuperType 497 ''' 498 if len(nametup) == 1: 499 if nametup[0] in _rs_type_translation: 500 return _rs_type_translation[nametup[0]] 501 return nametup[0] 502 503 (module, nametup) = _rs_extract_module(nametup) 504 505 return module + ''.join([_tit_cap(n) for n in nametup]) 506 507 508def _rs_name(nametup): 509 510 (module, nametup) = _rs_extract_module(nametup) 511 512 return module + '_'.join([_tit_split(n) for n in nametup]).lower() 513 514 515def _rs_const_name(nametup): 516 return _upper_name(_rs_extract_module(nametup)[1]) 517 518def _rs_field_name(string): 519 res = '' 520 for c in string: 521 if c.isupper(): 522 res = res + '_' + c.lower() 523 else: 524 res = res + c 525 return res 526 527 528def _set_type_lifetime(typeobj, has_lifetime): 529 typeobj.has_lifetime = has_lifetime 530 531 # handle successive calls to _set_type_lifetime on the same object 532 def ensure_in(val): 533 if not val in types_with_lifetime: 534 types_with_lifetime.append(val) 535 536 def ensure_out(val): 537 while val in types_with_lifetime: 538 types_with_lifetime.remove(val) 539 540 if has_lifetime: 541 ensure_in(typeobj.ffi_iterator_type) 542 ensure_in(typeobj.rs_type) 543 ensure_in(typeobj.rs_iterator_type) 544 else: 545 ensure_out(typeobj.ffi_iterator_type) 546 ensure_out(typeobj.rs_type) 547 ensure_out(typeobj.rs_iterator_type) 548 549 550 551# FFI codegen functions 552 553def _ffi_type_setup(typeobj, nametup, suffix=()): 554 ''' 555 Sets up all the C-related state by adding additional data fields to 556 all Field and Type objects. Here is where we figure out most of our 557 variable and function names. 558 559 Recurses into child fields and list member types. 560 ''' 561 # Do all the various names in advance 562 typeobj.ffi_type = _ffi_type_name(nametup + suffix) 563 564 typeobj.ffi_iterator_type = _ffi_type_name(nametup + ('iterator',)) 565 typeobj.ffi_next_fn = _ffi_name(nametup + ('next',)) 566 typeobj.ffi_end_fn = _ffi_name(nametup + ('end',)) 567 568 typeobj.ffi_request_fn = _ffi_name(nametup) 569 typeobj.ffi_checked_fn = _ffi_name(nametup + ('checked',)) 570 typeobj.ffi_unchecked_fn = _ffi_name(nametup + ('unchecked',)) 571 typeobj.ffi_reply_fn = _ffi_name(nametup + ('reply',)) 572 typeobj.ffi_reply_type = _ffi_type_name(nametup + ('reply',)) 573 typeobj.ffi_cookie_type = _ffi_type_name(nametup + ('cookie',)) 574 typeobj.ffi_reply_fds_fn = _ffi_name(nametup + ('reply_fds',)) 575 576 typeobj.ffi_need_aux = False 577 typeobj.ffi_need_serialize = False 578 typeobj.ffi_need_sizeof = False 579 580 typeobj.ffi_aux_fn = _ffi_name(nametup + ('aux',)) 581 typeobj.ffi_aux_checked_fn = _ffi_name(nametup + ('aux', 'checked')) 582 typeobj.ffi_aux_unchecked_fn = _ffi_name(nametup + ('aux', 'unchecked')) 583 typeobj.ffi_serialize_fn = _ffi_name(nametup + ('serialize',)) 584 typeobj.ffi_unserialize_fn = _ffi_name(nametup + ('unserialize',)) 585 typeobj.ffi_unpack_fn = _ffi_name(nametup + ('unpack',)) 586 typeobj.ffi_sizeof_fn = _ffi_name(nametup + ('sizeof',)) 587 588 # special case: structs where variable size fields are followed 589 # by fixed size fields 590 typeobj.ffi_var_followed_by_fixed_fields = False 591 592 if not typeobj.fixed_size(): 593 if not typeobj in _types_uneligible_to_copy: 594 _types_uneligible_to_copy.append(typeobj) 595 if hasattr(typeobj, 'parents'): 596 for p in typeobj.parents: 597 _types_uneligible_to_copy.append(p) 598 599 600 if typeobj.is_container: 601 602 prev_varsized_field = None 603 prev_varsized_offset = 0 604 first_field_after_varsized = None 605 606 for field in typeobj.fields: 607 _ffi_type_setup(field.type, field.field_type, ()) 608 if field.type.is_list: 609 _ffi_type_setup(field.type.member, field.field_type, ()) 610 if (field.type.nmemb is None): 611 typeobj.ffi_need_sizeof = True 612 613 field.ffi_field_type = _ffi_type_name(field.field_type) 614 field.ffi_field_name = _symbol(field.field_name) 615 field.has_subscript = (field.type.nmemb and 616 field.type.nmemb > 1) 617 field.ffi_need_const = (field.type.nmemb != 1) 618 field.ffi_need_pointer = (field.type.nmemb != 1) 619 620 # correct the need_pointer field for variable size non-list types 621 if not field.type.fixed_size(): 622 field.ffi_need_pointer = True 623 if field.type.is_list and not field.type.member.fixed_size(): 624 field.ffi_need_pointer = True 625 626 if field.type.is_switch: 627 field.ffi_need_const = True 628 field.ffi_need_pointer = True 629 field.ffi_need_aux = True 630 elif not field.type.fixed_size() and not field.type.is_bitcase: 631 typeobj.ffi_need_sizeof = True 632 633 field.ffi_iterator_type = _ffi_type_name( 634 field.field_type + ('iterator',)) 635 field.ffi_iterator_fn = _ffi_name( 636 nametup + (field.field_name, 'iterator')) 637 field.ffi_accessor_fn = _ffi_name( 638 nametup + (field.field_name,)) 639 field.ffi_length_fn = _ffi_name( 640 nametup + (field.field_name, 'length')) 641 field.ffi_end_fn = _ffi_name( 642 nametup + (field.field_name, 'end')) 643 644 field.prev_varsized_field = prev_varsized_field 645 field.prev_varsized_offset = prev_varsized_offset 646 647 if prev_varsized_offset == 0: 648 first_field_after_varsized = field 649 field.first_field_after_varsized = first_field_after_varsized 650 651 if field.type.fixed_size(): 652 prev_varsized_offset += field.type.size 653 # special case: intermixed fixed and variable size fields 654 if (prev_varsized_field is not None and 655 not field.type.is_pad and field.wire): 656 if not typeobj.is_union: 657 typeobj.ffi_need_serialize = True 658 typeobj.ffi_var_followed_by_fixed_fields = True 659 else: 660 typeobj.last_varsized_field = field 661 prev_varsized_field = field 662 prev_varsized_offset = 0 663 664 if typeobj.ffi_var_followed_by_fixed_fields: 665 if field.type.fixed_size(): 666 field.prev_varsized_field = None 667 668 if typeobj.ffi_need_serialize: 669 # when _unserialize() is wanted, create _sizeof() as well 670 # for consistency reasons 671 typeobj.ffi_need_sizeof = True 672 673 if not typeobj.is_bitcase: 674 if typeobj.ffi_need_serialize: 675 if typeobj.ffi_serialize_fn not in finished_serializers: 676 finished_serializers.append(typeobj.ffi_serialize_fn) 677 #_ffi_serialize('serialize', typeobj) 678 679 # _unpack() and _unserialize() are only needed 680 # for special cases: 681 # switch -> unpack 682 # special cases -> unserialize 683 if (typeobj.is_switch or 684 typeobj.ffi_var_followed_by_fixed_fields): 685 pass 686 #_ffi_serialize('unserialize', typeobj) 687 688 if typeobj.ffi_need_sizeof: 689 if typeobj.ffi_sizeof_fn not in finished_sizeof: 690 if not _ns.is_ext or typeobj.name[:2] == _ns.prefix: 691 finished_sizeof.append(typeobj.ffi_sizeof_fn) 692 #_ffi_serialize('sizeof', typeobj) 693 694 695 696 697def _ffi_bitcase_name(switch, bitcase): 698 assert switch.is_switch and bitcase.type.has_name 699 switch_name = _lower_name(_ext_nametup(switch.name)) 700 return '_%s__%s' % (switch_name, bitcase.ffi_field_name) 701 702 703def _ffi_struct(typeobj, must_pack=False): 704 ''' 705 Helper function for handling all structure types. 706 Called for structs, requests, replies, events, errors... 707 ''' 708 709 struct_fields = [] 710 711 for field in typeobj.fields: 712 if (not field.type.fixed_size() 713 and not typeobj.is_switch 714 and not typeobj.is_union): 715 continue 716 if field.wire: 717 struct_fields.append(field) 718 719 _f.section(0) 720 _f('') 721 _write_doc_brief_desc(_f, typeobj.doc) 722 _f('#[repr(C%s)]', ', packed' if must_pack else '') 723 _f('pub struct %s {', typeobj.ffi_type) 724 _f.indent() 725 726 maxfieldlen = 0 727 if not typeobj.is_switch: 728 for field in typeobj.fields: 729 maxfieldlen = max(maxfieldlen, len(field.ffi_field_name)) 730 else: 731 for b in typeobj.bitcases: 732 if b.type.has_name: 733 maxfieldlen = max(maxfieldlen, len(b.ffi_field_name)) 734 else: 735 for field in b.type.fields: 736 maxfieldlen = max(maxfieldlen, len(field.ffi_field_name)) 737 738 739 740 def _ffi_struct_field(field): 741 ftype = field.ffi_field_type 742 space = ' '* (maxfieldlen - len(field.ffi_field_name)) 743 if (field.type.fixed_size() or typeobj.is_union or 744 # in case of switch with switch children, 745 # don't make the field a pointer 746 # necessary for unserialize to work 747 (typeobj.is_switch and field.type.is_switch)): 748 if field.has_subscript: 749 ftype = '[%s; %d]' % (ftype, field.type.nmemb) 750 _f('pub %s: %s%s,', field.ffi_field_name, space, ftype) 751 else: 752 assert not field.has_subscript 753 _f('pub %s: %s*mut %s,', field.ffi_field_name, space, ftype) 754 755 named_bitcases = [] 756 757 if not typeobj.is_switch: 758 for field in struct_fields: 759 for d in typeobj.doc.fields[field.field_name]: 760 _f('/// %s', d) 761 _ffi_struct_field(field) 762 else: 763 for b in typeobj.bitcases: 764 if b.type.has_name: 765 named_bitcases.append(b) 766 space = ' ' * (maxfieldlen - len(b.ffi_field_name)) 767 _f('pub %s: %s%s,', b.ffi_field_name, space, 768 _ffi_bitcase_name(typeobj, b)) 769 else: 770 for field in b.type.fields: 771 _ffi_struct_field(field) 772 773 _f.unindent() 774 _f('}') 775 if not typeobj in _types_uneligible_to_copy: 776 _f('') 777 _f('impl Copy for %s {}', typeobj.ffi_type) 778 _f('impl Clone for %s {', typeobj.ffi_type) 779 _f(' fn clone(&self) -> %s { *self }', typeobj.ffi_type) 780 _f('}') 781 782 for b in named_bitcases: 783 _f('') 784 _f('#[repr(C)]') 785 _f('pub struct %s {', _ffi_bitcase_name(typeobj, b)) 786 _f.indent() 787 maxfieldlen = 0 788 for field in b.type.fields: 789 maxfieldlen = max(maxfieldlen, len(field.ffi_field_name)) 790 for field in b.type.fields: 791 _ffi_struct_field(field) 792 _f.unindent() 793 _f('}') 794 795 796 797def _ffi_accessors_list(typeobj, field): 798 ''' 799 Declares the accessor functions for a list field. 800 Declares a direct-accessor function only if the list members 801 are fixed size. 802 Declares length and get-iterator functions always. 803 ''' 804 805 list = field.type 806 ffi_type = typeobj.ffi_type 807 808 # special case: switch 809 # in case of switch, 2 params have to be supplied to certain 810 # accessor functions: 811 # 1. the anchestor object (request or reply) 812 # 2. the (anchestor) switch object 813 # the reason is that switch is either a child of a request/reply 814 # or nested in another switch, 815 # so whenever we need to access a length field, we might need to 816 # refer to some anchestor type 817 switch_obj = typeobj if typeobj.is_switch else None 818 if typeobj.is_bitcase: 819 switch_obj = typeobj.parents[-1] 820 if switch_obj is not None: 821 ffi_type = switch_obj.ffi_type 822 823 params = [] 824 parents = typeobj.parents if hasattr(typeobj, 'parents') else [typeobj] 825 # 'R': parents[0] is always the 'toplevel' container type 826 params.append(('R: *const %s' % parents[0].ffi_type, parents[0])) 827 # auxiliary object for 'R' parameters 828 R_obj = parents[0] 829 830 if switch_obj is not None: 831 # now look where the fields are defined that are needed to evaluate 832 # the switch expr, and store the parent objects in accessor_params and 833 # the fields in switch_fields 834 835 # 'S': name for the 'toplevel' switch 836 toplevel_switch = parents[1] 837 params.append(('S: *const %s' % toplevel_switch.ffi_type, 838 toplevel_switch)) 839 840 # auxiliary object for 'S' parameter 841 S_obj = parents[1] 842 843 _f.section(1) 844 if list.member.fixed_size(): 845 idx = 1 if switch_obj is not None else 0 846 _f('') 847 _f('pub fn %s (%s)', field.ffi_accessor_fn, params[idx][0]) 848 _f(' -> *mut %s;', field.ffi_field_type) 849 850 def _may_switch_fn(fn_name, return_type): 851 _f('') 852 has_lifetime = return_type in types_with_lifetime 853 lifetime = "<'a>" if has_lifetime else "" 854 if switch_obj is not None: 855 fn_start = 'pub fn %s%s (' % (fn_name, lifetime) 856 spacing = ' '*len(fn_start) 857 _f('%sR: *const %s,', fn_start, R_obj.ffi_type) 858 _f('%sS: *const %s)', spacing, S_obj.ffi_type) 859 _f(' -> %s%s;', return_type, lifetime) 860 else: 861 _f('pub fn %s%s (R: *const %s)', fn_name, lifetime, ffi_type) 862 _f(' -> %s%s;', return_type, lifetime) 863 864 _may_switch_fn(field.ffi_length_fn, 'c_int') 865 866 if field.type.member.is_simple: 867 _may_switch_fn(field.ffi_end_fn, 'xcb_generic_iterator_t') 868 else: 869 _may_switch_fn(field.ffi_iterator_fn, field.ffi_iterator_type) 870 871 872 873def _ffi_accessors_field(typeobj, field): 874 ''' 875 Declares the accessor functions for a non-list field that follows 876 a variable-length field. 877 ''' 878 ffi_type = typeobj.ffi_type 879 880 # special case: switch 881 switch_obj = typeobj if typeobj.is_switch else None 882 if typeobj.is_bitcase: 883 switch_obj = typeobj.parents[-1] 884 if switch_obj is not None: 885 ffi_type = switch_obj.ffi_type 886 887 _f.section(1) 888 if field.type.is_simple: 889 _f('') 890 _f('pub fn %s (R: *const %s)', field.ffi_accessor_fn, ffi_type) 891 _f(' -> %s;', field.ffi_field_type) 892 else: 893 if field.type.is_switch and switch_obj is None: 894 return_type = '*mut c_void' 895 else: 896 return_type = '*mut %s' % field.ffi_field_type 897 898 _f('') 899 _f('pub fn %s (R: *const %s)', field.ffi_accessor_fn, ffi_type) 900 _f(' -> %s;', return_type) 901 902 903def _ffi_accessors(typeobj, nametup): 904 for field in typeobj.fields: 905 if not field.type.is_pad: 906 if field.type.is_list and not field.type.fixed_size(): 907 _ffi_accessors_list(typeobj, field) 908 elif (field.prev_varsized_field is not None 909 or not field.type.fixed_size()): 910 _ffi_accessors_field(typeobj, field) 911 912 913def _ffi_iterator(typeobj, nametup): 914 915 has_lifetime = typeobj.ffi_iterator_type in types_with_lifetime 916 lifetime = "<'a>" if has_lifetime else "" 917 918 _f.section(0) 919 _f('') 920 _f('#[repr(C)]') 921 _f("pub struct %s%s {", typeobj.ffi_iterator_type, lifetime) 922 _f(' pub data: *mut %s,', typeobj.ffi_type) 923 _f(' pub rem: c_int,') 924 _f(' pub index: c_int,') 925 if has_lifetime: 926 _f(" _phantom: std::marker::PhantomData<&'a %s>,", typeobj.ffi_type) 927 _f('}') 928 929 _f.section(1) 930 _f('') 931 _f('pub fn %s (i: *mut %s);', typeobj.ffi_next_fn, 932 typeobj.ffi_iterator_type) 933 934 _f('') 935 _f('pub fn %s (i: *mut %s)', typeobj.ffi_end_fn, 936 typeobj.ffi_iterator_type) 937 _f(' -> xcb_generic_iterator_t;') 938 939 940 941 942def _ffi_reply(request): 943 ''' 944 Declares the function that returns the reply structure. 945 ''' 946 _f.section(1) 947 _f('') 948 _f('/// the returned value must be freed by the caller using ' + 949 'libc::free().') 950 fn_start = 'pub fn %s (' % request.ffi_reply_fn 951 spacing = ' ' * len(fn_start) 952 _f('%sc: *mut xcb_connection_t,', fn_start) 953 _f('%scookie: %s,', spacing, request.ffi_cookie_type) 954 _f('%serror: *mut *mut xcb_generic_error_t)', spacing) 955 _f(' -> *mut %s;', request.ffi_reply_type) 956 957 958def _ffi_reply_has_fds(self): 959 for field in self.fields: 960 if field.isfd: 961 return True 962 return False 963 964 965def _ffi_reply_fds(request, name): 966 ''' 967 Declares the function that returns fds related to the reply. 968 ''' 969 _f.section(1) 970 _f('') 971 _f('/// the returned value must be freed by the caller using ' + 972 'libc::free().') 973 fn_start = 'pub fn %s (' % request.ffi_reply_fds_fn 974 spacing = ' ' * len(fn_start) 975 _f('%sc: *mut xcb_connection_t,', fn_start) 976 _f('%sreply: *mut %s)', spacing, request.ffi_reply_type) 977 _f(' -> *mut c_int;') 978 979 980 981# Rust codegen function 982 983def _rs_type_setup(typeobj, nametup, suffix=()): 984 #assert typeobj.hasattr('ffi_type') 985 986 typeobj.rs_type = _rs_type_name(nametup + suffix) 987 988 if len(nametup) == 1: 989 typeobj.rs_qualified_type = typeobj.rs_type 990 else: 991 module = _ns.ext_name.lower() if _ns.is_ext else 'xproto' 992 typeobj.rs_qualified_type = '%s::%s' % (module, typeobj.rs_type) 993 994 typeobj.rs_iterator_type = _rs_type_name(nametup+('iterator',)) 995 typeobj.rs_request_fn = _rs_name(nametup) 996 typeobj.rs_checked_fn = _rs_name(nametup+('checked',)) 997 typeobj.rs_unchecked_fn = _rs_name(nametup+('unchecked',)) 998 999 typeobj.rs_aux_fn = _rs_name(nametup+('aux',)) 1000 typeobj.rs_aux_checked_fn = _rs_name(nametup+('aux', 'checked')) 1001 typeobj.rs_aux_unchecked_fn = _rs_name(nametup+('aux', 'unchecked')) 1002 typeobj.rs_reply_type = _rs_type_name(nametup + ('reply',)) 1003 typeobj.rs_cookie_type = _rs_type_name(nametup + ('cookie',)) 1004 1005 typeobj.rs_is_pod = False 1006 1007 if typeobj.is_container: 1008 has_complex = False 1009 for field in typeobj.fields: 1010 _rs_type_setup(field.type, field.field_type) 1011 if field.type.is_list: 1012 _rs_type_setup(field.type.member, field.field_type) 1013 field.rs_field_name = _symbol(_rs_field_name(field.field_name)) 1014 field.rs_field_type = _rs_type_name(field.field_type) 1015 1016 field.rs_iterator_type = _rs_type_name( 1017 field.field_type + ('iterator',)) 1018 1019 if not field.type.is_simple and not field.type.rs_is_pod \ 1020 and not field.type.is_pad: 1021 has_complex = True 1022 1023 typeobj.rs_only_has_simple = not has_complex 1024 # we restrict POD a little 1025 typeobj.rs_is_pod = ( 1026 (not has_complex) and 1027 (not typeobj.rs_qualified_type in _rs_typedef_exceptions) and 1028 (not typeobj.is_reply and not typeobj.is_union) and 1029 (not typeobj.is_switch)) 1030 1031 if typeobj.rs_is_pod: 1032 _set_type_lifetime(typeobj, False) 1033 1034 1035 1036def _rs_struct(typeobj): 1037 _r.section(1) 1038 _r('') 1039 _write_doc_brief_desc(_r, typeobj.doc) 1040 if typeobj.rs_is_pod: 1041 _r('#[derive(Copy, Clone)]') 1042 _r('pub struct %s {', typeobj.rs_type) 1043 _r(' pub base: %s,', typeobj.ffi_type) 1044 _r('}') 1045 else: 1046 has_lifetime = typeobj.rs_type in types_with_lifetime 1047 lifetime1 = "<'a>" if has_lifetime else "" 1048 lifetime2 = "'a, " if has_lifetime else "" 1049 1050 _r("pub type %s%s = base::StructPtr<%s%s>;", typeobj.rs_type, lifetime1, 1051 lifetime2, typeobj.ffi_type) 1052 1053 1054def _rs_accessors(typeobj): 1055 1056 has_lifetime = typeobj.rs_type in types_with_lifetime 1057 lifetime = "<'a>" if has_lifetime else "" 1058 1059 _r.section(1) 1060 _r('') 1061 _r('impl%s %s%s {', lifetime, typeobj.rs_type, lifetime) 1062 with _r.indent_block(): 1063 if typeobj.rs_is_pod: 1064 # POD structs have a new method 1065 fnstart = 'pub fn new(' 1066 fnspace = ' '*len(fnstart) 1067 argfields = [] 1068 for f in typeobj.fields: 1069 if not f.type.is_pad: 1070 argfields.append(f) 1071 maxfieldlen = 0 1072 for f in typeobj.fields: 1073 maxfieldlen = max(maxfieldlen, len(f.rs_field_name)) 1074 if len(argfields): 1075 eol = ',' if len(argfields) > 1 else ')' 1076 f1 = argfields[0] 1077 space1 = ' '*(maxfieldlen - len(f1.rs_field_name)) 1078 _r('#[allow(unused_unsafe)]') 1079 _r('%s%s: %s%s%s', fnstart, f1.rs_field_name, space1, f1.rs_field_type, eol) 1080 for (i, f) in enumerate(argfields[1:]): 1081 argspace = ' '*(maxfieldlen-len(f.rs_field_name)) 1082 eol = ',' if i < len(argfields)-2 else ')' 1083 _r('%s%s: %s%s%s', fnspace, f.rs_field_name, argspace, f.rs_field_type, eol) 1084 _r(' -> %s {', typeobj.rs_type) 1085 else: 1086 _r('#[allow(unused_unsafe)]') 1087 _r('%s) -> %s {', fnstart, typeobj.rs_type) 1088 1089 with _r.indent_block(): 1090 _r('unsafe {') 1091 with _r.indent_block(): 1092 _r('%s {', typeobj.rs_type) 1093 with _r.indent_block(): 1094 _r('base: %s {', typeobj.ffi_type) 1095 with _r.indent_block(): 1096 for f in typeobj.fields: 1097 space = ' '*(maxfieldlen-len(f.rs_field_name)) 1098 if f.type.rs_is_pod: 1099 _r('%s: %sstd::mem::transmute(%s),', f.rs_field_name, space, f.rs_field_name) 1100 elif f.type.is_pad: 1101 fval = '0' 1102 if f.has_subscript: 1103 fval = '[0; %d]' % f.type.nmemb 1104 _r('%s: %s%s,', f.rs_field_name, space, fval) 1105 else: 1106 assignment = f.rs_field_name 1107 if f.rs_field_type == 'bool': 1108 assignment = 'if %s { 1 } else { 0 }' % f.rs_field_name 1109 _r('%s: %s%s,', f.ffi_field_name, space, assignment) 1110 _r('}') 1111 _r('}') 1112 _r('}') 1113 _r('}') 1114 1115 for (i, field) in enumerate(typeobj.fields): 1116 if field.visible and not field.type.is_switch: 1117 for d in typeobj.doc.fields[field.field_name]: 1118 _r('/// %s', d) 1119 if typeobj.is_union: 1120 _rs_union_accessor(typeobj, field) 1121 else: 1122 _rs_accessor(typeobj, field) 1123 _r('}') 1124 1125 1126def _rs_reply_accessors(reply): 1127 ''' 1128 same as _rs_accessors but handles fds special case 1129 ''' 1130 has_lifetime = reply.rs_type in types_with_lifetime 1131 lifetime = "<'a>" if has_lifetime else "" 1132 1133 fd_field = None 1134 nfd_field = None 1135 for f in reply.fields: 1136 if f.rs_field_name == 'nfd': 1137 nfd_field = f 1138 if f.isfd: 1139 fd_field = f 1140 1141 reply_fields = [] 1142 for f in reply.fields: 1143 if f.rs_field_name == 'nfd': 1144 # writing nfd field only if fds is not written 1145 if not fd_field or not nfd_field: 1146 reply_fields.append(f) 1147 elif not f.isfd: 1148 reply_fields.append(f) 1149 1150 1151 _r.section(1) 1152 _r('') 1153 _r('impl%s %s%s {', lifetime, reply.rs_type, lifetime) 1154 with _r.indent_block(): 1155 # regular fields 1156 for field in reply_fields: 1157 if field.visible and not field.type.is_switch: 1158 _rs_accessor(reply, field) 1159 1160 # fds field if any 1161 if nfd_field and fd_field: 1162 getter = reply.request.ffi_reply_fds_fn 1163 # adding 's' 1164 fname = fd_field.rs_field_name 1165 if not fname.endswith('s'): 1166 fname += 's' 1167 _r('pub fn %s(&self, c: &base::Connection) -> &[i32] {', fname) 1168 with _r.indent_block(): 1169 _r('unsafe {') 1170 with _r.indent_block(): 1171 _r('let nfd = (*self.ptr).nfd as usize;') 1172 _r('let ptr = %s(c.get_raw_conn(), self.ptr);', getter) 1173 _r('') 1174 _r('std::slice::from_raw_parts(ptr, nfd)') 1175 _r('}') 1176 _r('}') 1177 _r('}') 1178 1179 1180def _rs_union_accessor(typeobj, field): 1181 if field.type.is_simple or field.type.rs_is_pod: 1182 _r('pub fn %s(&self) -> %s {', field.rs_field_name, field.rs_field_type) 1183 with _r.indent_block(): 1184 _r('unsafe {') 1185 with _r.indent_block(): 1186 convert = '' 1187 if field.rs_field_type == 'bool': 1188 convert = ' != 0' 1189 _r('let _ptr = self.data.as_ptr() as *const %s;', field.rs_field_type) 1190 _r('*_ptr%s', convert) 1191 _r('}') 1192 _r('}') 1193 _r('pub fn from_%s(%s: %s) -> %s {', field.rs_field_name, 1194 field.rs_field_name, field.rs_field_type, typeobj.rs_type) 1195 with _r.indent_block(): 1196 _r('unsafe {') 1197 with _r.indent_block(): 1198 if field.rs_field_type == 'bool': 1199 _r('let %s: %s = %s != 0;', field.rs_field_name, 1200 field.ffi_field_type, field.rs_field_name) 1201 _r('let mut res = %s { data: [0; %d] };', typeobj.rs_type, 1202 typeobj.union_num_bytes) 1203 _r('let res_ptr = res.data.as_mut_ptr() as *mut %s;', field.rs_field_type) 1204 _r('*res_ptr = %s;', field.rs_field_name) 1205 _r('res') 1206 _r('}') 1207 _r('}') 1208 1209 1210 elif field.type.is_list and field.type.fixed_size(): 1211 assert (typeobj.union_num_bytes % field.type.size) == 0 1212 _r('pub fn %s(&self) -> &[%s] {', 1213 field.rs_field_name, field.rs_field_type) 1214 with _r.indent_block(): 1215 _r('unsafe {') 1216 with _r.indent_block(): 1217 _r('let ptr = self.data.as_ptr() as *const %s;', field.rs_field_type) 1218 _r('std::slice::from_raw_parts(ptr, %d)', 1219 typeobj.union_num_bytes / field.type.size) 1220 _r('}') 1221 _r('}') 1222 _r('pub fn from_%s(%s: [%s; %d]) -> %s {', field.rs_field_name, 1223 field.rs_field_name, field.rs_field_type, 1224 typeobj.union_num_bytes / field.type.size, 1225 typeobj.rs_type) 1226 with _r.indent_block(): 1227 _r('unsafe {') 1228 with _r.indent_block(): 1229 _r('%s { data: std::mem::transmute(%s) }', typeobj.rs_type, 1230 field.rs_field_name) 1231 _r('}') 1232 _r('}') 1233 1234 1235 elif field.type.is_container: 1236 if not field.type.rs_is_pod: 1237 _r('pub fn %s<\'a>(&\'a self) -> %s<\'a> {', 1238 field.rs_field_name, field.rs_field_type) 1239 else: 1240 _r('pub fn %s(&self) -> %s {', field.rs_field_name, field.rs_field_type) 1241 1242 with _r.indent_block(): 1243 _r('unsafe {') 1244 with _r.indent_block(): 1245 if not field.type.rs_is_pod: 1246 _r('std::mem::transmute(self)') 1247 else: 1248 _r('let _ptr = self.data.as_ptr() as *const %s;', field.rs_field_type) 1249 _r('*_ptr') 1250 _r('}') 1251 _r('}') 1252 1253 1254 1255def _rs_accessor(typeobj, field, disable_pod_acc=False): 1256 if field.type.is_simple or field.type.rs_is_pod: 1257 _r('pub fn %s(&self) -> %s {', field.rs_field_name, 1258 field.rs_field_type) 1259 1260 acc = '(*self.ptr)' 1261 if typeobj.rs_is_pod and not disable_pod_acc: 1262 acc = 'self.base' 1263 1264 with _r.indent_block(): 1265 convert = '' 1266 if field.rs_field_type == 'bool': 1267 convert = ' != 0' 1268 _r('unsafe {') 1269 with _r.indent_block(): 1270 if field.type.rs_is_pod: 1271 _r('std::mem::transmute(%s.%s)', acc, field.ffi_field_name) 1272 else: 1273 _r('%s.%s%s', acc, field.ffi_field_name, convert) 1274 _r('}') 1275 _r('}') 1276 1277 elif field.type.is_union: 1278 # do we already have a lifetime declared? 1279 has_lifetime = typeobj.rs_type in types_with_lifetime 1280 lifetime = "<'a>" if not has_lifetime else "" 1281 _r("pub fn %s%s(&'a self) -> &'a %s {", field.rs_field_name, lifetime, 1282 field.rs_field_type) 1283 with _r.indent_block(): 1284 _r('unsafe {') 1285 with _r.indent_block(): 1286 _r('&(*self.ptr).%s', field.ffi_field_name) 1287 _r('}') 1288 _r('}') 1289 1290 elif field.type.is_list and not field.type.fixed_size(): 1291 if field.type.member.rs_type == 'bool': 1292 # special case for bool: we need to convert all elements into an owned vec 1293 _r('pub fn %s(&self) -> Vec<bool> {', field.rs_field_name) 1294 with _r.indent_block(): 1295 _r('unsafe {') 1296 with _r.indent_block(): 1297 _r('let field = self.ptr;') 1298 _r('let len = %s(field);', field.ffi_length_fn) 1299 _r('let data = %s(field);', field.ffi_accessor_fn) 1300 _r('let slice = std::slice::from_raw_parts(data, len as usize);') 1301 _r('slice.iter().map(|el| if *el == 0 {false} else{true}).collect()') 1302 _r('}') 1303 _r('}') 1304 elif field.type.member.is_simple: 1305 field_type = field.type.member.rs_type 1306 is_template = False 1307 if field_type == 'c_char': 1308 return_type = '&str' 1309 elif field_type == 'c_void': 1310 is_template = True 1311 return_type = '&[T]' 1312 else: 1313 return_type = '&[%s]' % field_type 1314 _r('pub fn %s%s(&self) -> %s {', field.rs_field_name, 1315 '<T>' if is_template else '', return_type) 1316 with _r.indent_block(): 1317 _r('unsafe {') 1318 with _r.indent_block(): 1319 _r('let field = self.ptr;') 1320 _r('let len = %s(field) as usize;', field.ffi_length_fn) 1321 _r('let data = %s(field);', field.ffi_accessor_fn) 1322 if field_type == 'c_char': 1323 _r('let slice = ' + 1324 'std::slice::from_raw_parts(' + 1325 'data as *const u8, len);') 1326 _r('// should we check what comes from X?') 1327 _r('std::str::from_utf8_unchecked(&slice)') 1328 elif is_template: 1329 _r('debug_assert_eq!(len %% std::mem::size_of::<T>(), 0);') 1330 _r('std::slice::from_raw_parts(data as *const T, ' + 1331 'len / std::mem::size_of::<T>())') 1332 else: 1333 _r('std::slice::from_raw_parts(data, len)') 1334 _r('}') 1335 _r('}') 1336 else: 1337 lifetime = "" 1338 if field.rs_iterator_type in types_with_lifetime and \ 1339 typeobj.rs_type in types_with_lifetime: 1340 lifetime = "<'a>" 1341 _r('pub fn %s(&self) -> %s%s {', 1342 field.rs_field_name, field.rs_iterator_type, lifetime) 1343 with _r.indent_block(): 1344 _r('unsafe {') 1345 with _r.indent_block(): 1346 _r('%s(self.ptr)', field.ffi_iterator_fn) 1347 _r('}') 1348 _r('}') 1349 pass 1350 1351 elif field.type.is_list: 1352 _r('pub fn %s(&self) -> &[%s] {', 1353 field.rs_field_name, field.rs_field_type) 1354 with _r.indent_block(): 1355 _r('unsafe {') 1356 with _r.indent_block(): 1357 _r('&(*self.ptr).%s', field.ffi_field_name) 1358 _r('}') 1359 _r('}') 1360 1361 elif field.type.is_container: 1362 _r('pub fn %s(&self) -> %s {', 1363 field.rs_field_name, field.rs_field_type) 1364 with _r.indent_block(): 1365 _r('unsafe {') 1366 with _r.indent_block(): 1367 _r('std::mem::transmute(&(*self.ptr).%s)', 1368 field.ffi_field_name) 1369 _r('}') 1370 _r('}') 1371 1372 elif not field.type.is_pad: 1373 raise Exception('did not treat accessor %s.%s' 1374 % (typeobj.ffi_type, field.ffi_field_name)) 1375 1376 1377 1378def _rs_iterator(typeobj): 1379 1380 has_lifetime = typeobj.rs_iterator_type in types_with_lifetime 1381 lifetime1 = "<'a>" if has_lifetime else "" 1382 lifetime2 = "'a, " if has_lifetime else "" 1383 return_expr = '*data' 1384 if typeobj.rs_is_pod: 1385 return_expr = 'std::mem::transmute(*data)' 1386 elif typeobj.is_container and not typeobj.is_union: 1387 return_expr = 'std::mem::transmute(data)' 1388 1389 _r.section(1) 1390 _r('') 1391 _r("pub type %s%s = %s%s;", 1392 typeobj.rs_iterator_type, lifetime1, typeobj.ffi_iterator_type, lifetime1) 1393 1394 _r('') 1395 _r("impl%s Iterator for %s%s {", lifetime1, typeobj.rs_iterator_type, lifetime1) 1396 _r(" type Item = %s%s;", typeobj.rs_type, lifetime1) 1397 _r(" fn next(&mut self) -> std::option::Option<%s%s> {", 1398 typeobj.rs_type, lifetime1) 1399 _r(' if self.rem == 0 { None }') 1400 _r(' else {') 1401 _r(' unsafe {') 1402 _r(' let iter = self as *mut %s;', 1403 typeobj.ffi_iterator_type) 1404 _r(' let data = (*iter).data;') 1405 _r(' %s(iter);', typeobj.ffi_next_fn) 1406 _r(' Some(%s)', return_expr) 1407 _r(' }') 1408 _r(' }') 1409 _r(' }') 1410 _r('}') 1411 1412 1413 1414def _rs_reply(request): 1415 1416 _r.section(1) 1417 _r('') 1418 _r('pub type %s = base::Reply<%s>;', request.rs_reply_type, request.ffi_reply_type); 1419 1420 1421 1422 1423# Common codegen utilities 1424 1425def _prepare_doc(typeobj): 1426 # preparing doc for easier handling 1427 # each typeobj must have a doc attribute with brief, description and fields 1428 1429 def rework_phrase(phrase): 1430 # having 'unknown start of token' error by rustdoc sometimes. 1431 # This silents it 1432 # return phrase.replace('`', '') 1433 # Edit: not necessary anymore 1434 return phrase 1435 1436 if hasattr(typeobj, "doc_prepared"): 1437 assert typeobj.doc_prepared == True 1438 return 1439 if hasattr(typeobj, "doc") and typeobj.doc: 1440 if typeobj.doc.brief: 1441 typeobj.doc.brief = [rework_phrase(p) for p in typeobj.doc.brief.split('\n')] 1442 else: 1443 typeobj.doc.brief = [] 1444 if typeobj.doc.description: 1445 typeobj.doc.description = [rework_phrase(p) for p in typeobj.doc.description.split('\n')] 1446 else: 1447 typeobj.doc.description = [] 1448 if hasattr(typeobj, "fields"): 1449 if not hasattr(typeobj.doc, "fields"): 1450 typeobj.doc.fields = {} 1451 for f in typeobj.fields: 1452 if f.field_name in typeobj.doc.fields: 1453 typeobj.doc.fields[f.field_name] = \ 1454 [rework_phrase(p) for p in typeobj.doc.fields[f.field_name].split('\n')] 1455 else: 1456 typeobj.doc.fields[f.field_name] = [] 1457 else: 1458 class Doc(object): pass 1459 typeobj.doc = Doc() 1460 typeobj.doc.brief = [] 1461 typeobj.doc.description = [] 1462 typeobj.doc.fields = {} 1463 if hasattr(typeobj, "fields"): 1464 for f in typeobj.fields: 1465 typeobj.doc.fields[f.field_name] = [] 1466 1467 typeobj.doc_prepared = True 1468 1469 1470def _write_docs(sf, doclist): 1471 for s in doclist: 1472 sf('/// %s', s) 1473 1474 1475def _write_doc_brief_desc(sf, doc): 1476 _write_docs(sf, doc.brief) 1477 if len(doc.brief) and len(doc.description): 1478 sf('///') 1479 _write_docs(sf, doc.description) 1480 1481 1482class EnumCodegen(object): 1483 1484 namecount = {} 1485 1486 def build_collision_table(module): 1487 for v in module.types.values(): 1488 key = _ffi_type_name(v[0]) 1489 EnumCodegen.namecount[key] = ( 1490 (EnumCodegen.namecount.get(key) or 0) + 1 1491 ) 1492 1493 1494 def __init__(self, nametup, doc): 1495 self._nametup = nametup 1496 self._doc = doc 1497 1498 self.done_vals = {} 1499 self.unique_discriminants = [] 1500 self.conflicts = [] 1501 self.all_discriminants = [] 1502 key = _ffi_type_name(nametup) 1503 if EnumCodegen.namecount[key] > 1: 1504 nametup = nametup + ('enum',) 1505 self.ffi_name = _ffi_type_name(nametup) 1506 self.rs_name = _rs_type_name(nametup) 1507 1508 1509 def add_discriminant(self, name, val): 1510 class Discriminant: pass 1511 d = Discriminant() 1512 #d.rs_name = name 1513 d.rs_name = _rs_const_name(self._nametup+(name,)) 1514 d.ffi_name = _ffi_const_name(self._nametup+(name,)) 1515 d.valstr = '0x%02x' % val 1516 d.val = val 1517 d.doc = None 1518 if self._doc and name in self._doc.fields: 1519 d.doc = self._doc.fields[name] 1520 self.all_discriminants.append(d) 1521 if val in self.done_vals: 1522 self.conflicts.append(d) 1523 else: 1524 self.done_vals[val] = d 1525 self.unique_discriminants.append(d) 1526 1527 1528 def maxlen(self, name_field): 1529 maxnamelen = 0 1530 maxvallen = 0 1531 for d in self.unique_discriminants: 1532 maxvallen = max(maxvallen, len(d.valstr)) 1533 maxnamelen = max(maxnamelen, len(getattr(d, name_field))) 1534 return (maxnamelen, maxvallen) 1535 1536 1537 def write_ffi(self): 1538 (maxnamelen, maxvallen) = self.maxlen('ffi_name') 1539 type_name = self.ffi_name 1540 _f.section(0) 1541 _f('') 1542 _write_doc_brief_desc(_f, self._doc) 1543 _f('pub type %s = u32;', type_name) 1544 for d in self.all_discriminants: 1545 d_name = d.ffi_name 1546 namespace = ' ' * (maxnamelen-len(d_name)) 1547 valspace = ' ' * (maxvallen-len(d.valstr)) 1548 if d.doc: 1549 ddocs = d.doc.split('\n') 1550 for dd in ddocs: 1551 _f('/// %s', dd) 1552 _f('pub const %s%s: %s =%s %s;', d_name, namespace, type_name, 1553 valspace, d.valstr) 1554 1555 def write_rs(self): 1556 (maxnamelen, maxvallen) = self.maxlen("rs_name") 1557 _r.section(0) 1558 _r('') 1559 _write_doc_brief_desc(_r, self._doc) 1560 _r('pub type %s = u32;', self.rs_name) 1561 for d in self.all_discriminants: 1562 namespace = ' ' * (maxnamelen-len(d.rs_name)) 1563 valspace = ' ' * (maxvallen-len(d.valstr)) 1564 if d.doc: 1565 ddocs = d.doc.split('\n') 1566 for dd in ddocs: 1567 _r('/// %s', dd) 1568 _r('pub const %s%s: %s =%s %s;', d.rs_name, namespace, self.rs_name, 1569 valspace, d.valstr) 1570 1571 1572 1573 1574 1575class RequestCodegen(object): 1576 1577 def __init__(self, request): 1578 self.request = request 1579 1580 self.void = False if self.request.reply else True 1581 1582 self.ffi_cookie_type = ('xcb_void_cookie_t' if self.void 1583 else self.request.ffi_cookie_type) 1584 self.rs_cookie_type = ('base::VoidCookie' if self.void 1585 else self.request.rs_cookie_type) 1586 1587 self.visible_fields = [] 1588 for field in self.request.fields: 1589 if field.visible: 1590 self.visible_fields.append(field) 1591 1592 # for, we do not filter out any visible field, 1593 # but we must find out if it is pointer, const ... 1594 self.ffi_params = [] 1595 for field in self.visible_fields: 1596 self.ffi_params.append(field) 1597 1598 1599 # Rust is more complicated because of lists 1600 # here we pack lists in slices 1601 1602 # there's basically 3 cases: 1603 # 1. regular fields, passed as-is to the ffi func 1604 # 2. masked lists (such as create_window event mask) 1605 # given to rs slice of tuple (mask, value) and unpacked 1606 # into int and pointer to ffi func 1607 # 3. regular lists, for which a length and a pointer 1608 # must be passed to the ffi_func. these are given to 1609 # rs by a slice 1610 1611 # it happens to have 2 or more lists for same length field. 1612 # in this case, we will make 2 slices and runtime assert same length 1613 # eg: take a look at render::create_conical_gradient 1614 1615 rs_num_template = 0 1616 template_letters = ['T', 'U', 'V', 'W'] 1617 1618 # xproto::send_event is special. 1619 # the FFI takes an event argument casted to a char* 1620 # here we are going to require an &Event<T> for the rs func 1621 self.rs_send_event = False 1622 if _ns.header == "xproto" and \ 1623 self.request.rs_request_fn.startswith("send_event"): 1624 self.rs_send_event = True 1625 1626 for f in self.visible_fields: 1627 f.rs_is_slice = False 1628 f.rs_template_let = '' 1629 f.rs_lenfield = None 1630 f.rs_is_mask_slice = False 1631 f.rs_maskfield = None 1632 f.rs_skip = False 1633 1634 for (ffi_index, field) in enumerate(self.visible_fields): 1635 field.ffi_index = ffi_index 1636 1637 if self.rs_send_event and field.rs_field_name == "event": 1638 field.rs_template_let = template_letters[rs_num_template] 1639 rs_num_template += 1 1640 1641 elif field.type.is_list: 1642 1643 if field.type.expr.bitfield: 1644 # field associated with a mask 1645 # eg. create_window last field 1646 field.rs_is_mask_slice = True 1647 else: 1648 # regular list with length and ptr 1649 field.rs_is_slice = True 1650 if field.type.member.rs_type == 'c_void': 1651 field.rs_template_let = template_letters[rs_num_template] 1652 rs_num_template += 1 1653 field.rs_lenfield = field.type.expr.lenfield 1654 if not field.rs_lenfield: 1655 len_name = field.type.expr.lenfield_name 1656 for f in self.visible_fields: 1657 if f.field_name == len_name: 1658 field.rs_lenfield = f 1659 # the mask is mandatory, but not the length (eg c strings) 1660 if field.rs_is_mask_slice: 1661 assert field.rs_lenfield 1662 if field.rs_lenfield: 1663 field.rs_lenfield.rs_skip = True 1664 1665 self.rs_params = [] 1666 1667 for field in self.visible_fields: 1668 if not field.rs_skip: 1669 self.rs_params.append(field) 1670 1671 self.rs_template = "<'a>" 1672 if rs_num_template: 1673 self.rs_template = "<'a" 1674 for i in range(rs_num_template): 1675 self.rs_template += ', ' + template_letters[i] 1676 self.rs_template += '>' 1677 1678 def ffi_func_name(self, regular, aux): 1679 checked = self.void and not regular 1680 unchecked = not self.void and not regular 1681 1682 if checked: 1683 func_name = (self.request.ffi_checked_fn if not aux else 1684 self.request.ffi_aux_checked_fn) 1685 elif unchecked: 1686 func_name = (self.request.ffi_unchecked_fn if not aux else 1687 self.request.ffi_aux_unchecked_fn) 1688 else: 1689 func_name = (self.request.ffi_request_fn if not aux else 1690 self.request.ffi_aux_fn) 1691 1692 return func_name 1693 1694 1695 1696 def rs_func_name(self, regular, aux): 1697 checked = self.void and not regular 1698 unchecked = not self.void and not regular 1699 1700 if checked: 1701 func_name = (self.request.rs_checked_fn if not aux else 1702 self.request.rs_aux_checked_fn) 1703 elif unchecked: 1704 func_name = (self.request.rs_unchecked_fn if not aux else 1705 self.request.rs_aux_unchecked_fn) 1706 else: 1707 func_name = (self.request.rs_request_fn if not aux else 1708 self.request.rs_aux_fn) 1709 1710 return func_name 1711 1712 def ffi_rq_type(self, field, aux): 1713 ffi_rq_type = field.ffi_field_type 1714 if field.ffi_need_pointer: 1715 pointer = '*const ' if field.ffi_need_const else '*mut ' 1716 ffi_rq_type = pointer + ffi_rq_type 1717 if field.type.ffi_need_serialize and not aux: 1718 ffi_rq_type = '*const c_void' 1719 return ffi_rq_type 1720 1721 1722 1723 def write_ffi_rs(self, regular, aux=False): 1724 self.write_ffi(regular, aux) 1725 self.write_rs(regular, aux) 1726 1727 1728 def write_ffi(self, regular, aux=False): 1729 1730 ffi_func_name = self.ffi_func_name(regular, aux) 1731 1732 maxnamelen = 1 1733 for p in self.ffi_params: 1734 maxnamelen = max(maxnamelen, len(p.ffi_field_name)) 1735 1736 _f.section(1) 1737 _f("") 1738 _write_doc_brief_desc(_f, self.request.doc) 1739 fn_start = "pub fn %s (" % ffi_func_name 1740 func_spacing = ' ' * len(fn_start) 1741 spacing = " " * (maxnamelen-len('c')) 1742 eol = ',' if len(self.ffi_params) else ')' 1743 _f("%sc: %s*mut xcb_connection_t%s", fn_start, spacing, eol) 1744 1745 for (i, p) in enumerate(self.ffi_params): 1746 ffi_rq_type = self.ffi_rq_type(p, aux) 1747 1748 spacing = ' '*(maxnamelen-len(p.ffi_field_name)) 1749 eol = ')' if i == (len(self.ffi_params)-1) else ',' 1750 _f('%s%s: %s%s%s', func_spacing, p.ffi_field_name, spacing, 1751 ffi_rq_type, eol) 1752 1753 _f(" -> %s;", self.ffi_cookie_type) 1754 1755 1756 def write_rs(self, regular, aux=False): 1757 checked = (self.void and not regular) \ 1758 or ((not self.void) and regular) 1759 rs_func_name = self.rs_func_name(regular, aux) 1760 ffi_func_name = self.ffi_func_name(regular, aux) 1761 1762 maxnamelen = len('c') 1763 for p in self.rs_params: 1764 maxnamelen = max(maxnamelen, len(p.rs_field_name)) 1765 1766 let_lines = [] 1767 call_params = [] 1768 1769 _r.section(1) 1770 _r('') 1771 _write_doc_brief_desc(_r, self.request.doc) 1772 doc_params = False 1773 for f in self.rs_params: 1774 if len(self.request.doc.fields[f.field_name]): 1775 doc_params = True 1776 break 1777 if doc_params: 1778 _r('///') 1779 _r('/// parameters:') 1780 _r('///') 1781 _r('/// - __c__:') 1782 _r('/// The connection object to the server') 1783 for f in self.rs_params: 1784 _r('///') 1785 _r('/// - __%s__:', f.field_name) 1786 for fd in self.request.doc.fields[f.field_name]: 1787 _r('/// %s', fd) 1788 fn_start = "pub fn %s%s(" % (rs_func_name, self.rs_template) 1789 func_spacing = ' ' * len(fn_start) 1790 eol = ',' if len(self.rs_params) else ')' 1791 spacing = ' ' * (maxnamelen-len('c')) 1792 _r("%sc%s: &'a base::Connection%s", fn_start, spacing, eol) 1793 1794 for (i, p) in enumerate(self.rs_params): 1795 1796 ffi_rq_type = self.ffi_rq_type(p, aux) 1797 rs_typestr = p.rs_field_type 1798 1799 if self.rs_send_event and p.rs_field_name == "event": 1800 rs_typestr = "&base::Event<%s>" % p.rs_template_let 1801 let_lines.append("let event_ptr = " + 1802 "std::mem::transmute(event.ptr);") 1803 call_params.append((p.ffi_index, "event_ptr")) 1804 pass 1805 elif p.rs_is_mask_slice: 1806 1807 maskfield = p.rs_lenfield 1808 rs_typestr = '&[(%s, %s)]' % (maskfield.rs_field_type, 1809 p.rs_field_type) 1810 1811 let_lines.append('let mut %s_copy = %s.to_vec();' % 1812 (p.rs_field_name, p.rs_field_name)) 1813 let_lines.append(('let (%s_mask, %s_vec) = ' + 1814 'base::pack_bitfield(&mut %s_copy);') % 1815 (p.rs_field_name, p.rs_field_name, p.rs_field_name)) 1816 let_lines.append("let %s_ptr = %s_vec.as_ptr();" % 1817 (p.rs_field_name, p.rs_field_name)) 1818 1819 # adding mask field if not already done 1820 # (already done should not happen with masks) 1821 if not next((cp for cp in call_params 1822 if cp[0] == maskfield.ffi_index), None): 1823 call_params.append((maskfield.ffi_index, "%s_mask as %s" % 1824 (p.rs_field_name, maskfield.ffi_field_type))) 1825 1826 # adding actual field 1827 call_params.append((p.ffi_index, '%s_ptr as %s' % 1828 (p.rs_field_name, ffi_rq_type))) 1829 1830 elif p.rs_is_slice: 1831 1832 if p.type.member.rs_type == 'c_char': 1833 rs_typestr = '&str' 1834 let_lines.append('let %s = %s.as_bytes();' % 1835 (p.rs_field_name, p.rs_field_name)) 1836 elif p.type.member.rs_type == 'c_void': 1837 rs_typestr = '&[%s]' % p.rs_template_let 1838 else: 1839 rs_typestr = '&[%s]' % rs_typestr 1840 1841 if p.rs_lenfield: 1842 lenfield = p.rs_lenfield 1843 # adding len field if not already done 1844 # (already done can happen with lists) 1845 if not next((cp for cp in call_params 1846 if cp[0] == lenfield.ffi_index), None): 1847 let_lines.append('let %s_len = %s.len();' % 1848 (p.rs_field_name, p.rs_field_name)) 1849 call_params.append((lenfield.ffi_index, 1850 "%s_len as %s" % 1851 (p.rs_field_name, lenfield.ffi_field_type))) 1852 1853 let_lines.append('let %s_ptr = %s.as_ptr();' % 1854 (p.rs_field_name, p.rs_field_name)) 1855 # adding actual field 1856 call_params.append((p.ffi_index, '%s_ptr as %s' % 1857 (p.rs_field_name, ffi_rq_type))) 1858 1859 elif p.type.is_container and p.ffi_need_pointer: 1860 rs_typestr = 'std::option::Option<%s>' % rs_typestr 1861 let_lines.append('let %s_ptr = match %s {' % (p.rs_field_name, 1862 p.rs_field_name)) 1863 let_lines.append(' Some(p) => p.ptr as %s,' % 1864 ffi_rq_type) 1865 let_lines.append(' None => std::ptr::null()') 1866 let_lines.append('};') 1867 call_params.append((p.ffi_index, '%s_ptr' % p.rs_field_name)) 1868 1869 elif p.type.is_container and not p.type.rs_is_pod: 1870 call_params.append((p.ffi_index, '*(%s.ptr)' % 1871 p.rs_field_name)) 1872 1873 elif p.type.rs_is_pod: 1874 call_params.append((p.ffi_index, '%s.base' % 1875 p.rs_field_name)) 1876 else: 1877 call_params.append((p.ffi_index, 1878 '%s as %s' % (p.rs_field_name, ffi_rq_type))) 1879 1880 spacing = ' ' * (maxnamelen-len(p.rs_field_name)) 1881 eol = ',' if i < (len(self.rs_params)-1) else ')' 1882 _r('%s%s%s: %s%s', func_spacing, p.rs_field_name, 1883 spacing, rs_typestr, eol) 1884 1885 _r(" -> %s<'a> {", self.rs_cookie_type) 1886 1887 with _r.indent_block(): 1888 _r('unsafe {') 1889 with _r.indent_block(): 1890 for l in let_lines: 1891 _r(l) 1892 1893 call_start = 'let cookie = %s(' % ffi_func_name 1894 eol = ',' if len(call_params) else ');' 1895 spacing = ' ' * len(call_start) 1896 _r('%sc.get_raw_conn()%s', call_start, eol) 1897 1898 call_params.sort(key=lambda x: x[0]) 1899 1900 for (i, (ffi_ind, p)) in enumerate(call_params): 1901 eol = ',' if i < (len(call_params)-1) else ');' 1902 _r('%s%s%s // %d', spacing, p, eol, ffi_ind) 1903 1904 _r("%s {", self.rs_cookie_type) 1905 _r(" cookie: cookie,") 1906 _r(" conn: c,") 1907 _r(" checked: %s", 'true' if checked else 'false') 1908 _r("}") 1909 _r('}') 1910 _r('}') 1911 1912 1913 1914 1915def _opcode(nametup, opcode): 1916 # handle GLX with -1 opcode 1917 optype = 'u8' if int(opcode) >= 0 else 'i8' 1918 1919 ffi_name = _ffi_const_name(nametup) 1920 _f.section(0) 1921 _f('') 1922 _f('pub const %s: %s = %s;', ffi_name, optype, opcode) 1923 1924 rs_name = _rs_const_name(nametup) 1925 _r.section(1) 1926 _r('') 1927 _r('pub const %s: %s = %s;', rs_name, optype, opcode) 1928 1929 1930 1931def _cookie(request): 1932 _f.section(0) 1933 _f('') 1934 _f('#[derive(Copy, Clone)]') 1935 _f('#[repr(C)]') 1936 _f('pub struct %s {', request.ffi_cookie_type) 1937 _f(' pub(crate) sequence: c_uint') 1938 _f('}') 1939 1940 _r("impl base::CookieSeq for %s {", request.ffi_cookie_type) 1941 with _r.indent_block(): 1942 _r("fn sequence(&self) -> c_uint { self.sequence }") 1943 _r("}") 1944 1945 1946 1947 _r.section(1) 1948 _r("") 1949 _r("pub type %s<'a> = base::Cookie<'a, %s>;", 1950 request.rs_cookie_type, request.ffi_cookie_type) 1951 1952 cookie = request.rs_cookie_type 1953 reply = request.rs_reply_type 1954 func = request.ffi_reply_fn 1955 1956 _r.section(1) 1957 _r('') 1958 _r("impl<'a> %s<'a> {", cookie) 1959 with _r.indent_block(): 1960 _r("pub fn get_reply(self) -> Result<%s, base::GenericError> {", reply) 1961 with _r.indent_block(): 1962 _r('unsafe {') 1963 with _r.indent_block(): 1964 _r("if self.checked {") 1965 _r(" let mut err: *mut xcb_generic_error_t = " 1966 + "std::ptr::null_mut();") 1967 _r(" let reply = %s {", reply) 1968 _r(" ptr: %s (self.conn.get_raw_conn(), self.cookie, &mut err)", func) 1969 _r(" };") 1970 _r(" std::mem::forget(self);") 1971 _r(" if err.is_null() { Ok (reply) }") 1972 _r(" else { Err(base::GenericError { ptr: err }) }") 1973 _r("} else {") 1974 _r(" let res = %s {", reply) 1975 _r(" ptr: %s (self.conn.get_raw_conn(), self.cookie, ", func) 1976 _r(" std::ptr::null_mut())") 1977 _r(" };") 1978 _r(" std::mem::forget(self);") 1979 _r(" Ok(res)") 1980 _r("}") 1981 _r('}') 1982 _r('}') 1983 _r('}') 1984 1985 1986 1987 1988def _must_pack_event(event, nametup): 1989 # The generic event structure xcb_ge_event_t has the full_sequence field 1990 # at the 32byte boundary. That's why we've to inject this field into GE 1991 # events while generating the structure for them. Otherwise we would read 1992 # garbage (the internal full_sequence) when accessing normal event fields 1993 # there. 1994 must_pack = False 1995 if (hasattr(event, 'is_ge_event') 1996 and event.is_ge_event 1997 and event.name == nametup): 1998 event_size = 0 1999 for field in event.fields: 2000 if field.type.size != None and field.type.nmemb != None: 2001 event_size += field.type.size * field.type.nmemb 2002 if event_size == 32: 2003 full_sequence = Field(tcard32, 2004 tcard32.name, 'full_sequence', 2005 False, True, True) 2006 idx = event.fields.index(field) 2007 event.fields.insert(idx + 1, full_sequence) 2008 2009 # If the event contains any 64-bit extended fields, they need 2010 # to remain aligned on a 64-bit boundary. Adding full_sequence 2011 # would normally break that; force the struct to be packed. 2012 must_pack = any(f.type.size == 8 and f.type.is_simple 2013 for f in event.fields[(idx+1):]) 2014 break 2015 2016 return must_pack 2017 2018 2019def _handle_switch(typeobj, nametup): 2020 if typeobj.is_switch and typeobj.ffi_type not in finished_switch: 2021 finished_switch.append(typeobj.ffi_type) 2022 2023 for bitcase in typeobj.bitcases: 2024 fname = _symbol(bitcase.field_name) 2025 bitcase.ffi_field_name = fname 2026 bitcase.rs_field_name = fname 2027 bitcase.nametup = (bitcase.field_type if bitcase.type.has_name 2028 else nametup) 2029 _ffi_type_setup(bitcase.type, bitcase.nametup, ()) 2030 _rs_type_setup(bitcase.type, bitcase.nametup, ()) 2031 2032 _set_type_lifetime(typeobj, True) 2033 2034 _ffi_struct(typeobj) 2035 _rs_struct(typeobj) 2036 2037 for bitcase in typeobj.bitcases: 2038 _ffi_accessors(bitcase.type, bitcase.nametup) 2039 # TODO: rs accessors 2040 2041 if typeobj.is_container: 2042 for f in typeobj.fields: 2043 _prepare_doc(f.type) 2044 _handle_switch(f.type, f.field_type) 2045 2046 2047# codegen drivers 2048 2049def rs_simple(simple, nametup): 2050 ''' 2051 simple is SimpleType object 2052 nametup is a name tuple 2053 ''' 2054 global current_handler 2055 current_handler = ('simple: ', nametup) 2056 2057 _prepare_doc(simple) 2058 2059 simple.has_lifetime = False 2060 2061 _ffi_type_setup(simple, nametup) 2062 _f.section(0) 2063 assert len(simple.name) == 1 2064 _f('') 2065 _write_doc_brief_desc(_f, simple.doc) 2066 _f('pub type %s = %s;', simple.ffi_type, simple.name[0]) 2067 _ffi_iterator(simple, nametup) 2068 2069 _rs_type_setup(simple, nametup) 2070 _r.section(0) 2071 _r('') 2072 _write_doc_brief_desc(_r, simple.doc) 2073 _r('pub type %s = %s;', simple.rs_type, simple.ffi_type) 2074 2075 2076 2077def rs_enum(typeobj, nametup): 2078 ''' 2079 typeobj is xcbgen.xtypes.Enum object 2080 nametup is a name tuple 2081 ''' 2082 global current_handler 2083 current_handler = ('enum: ', nametup) 2084 2085 _prepare_doc(typeobj) 2086 2087 ecg = EnumCodegen(nametup, typeobj.doc) 2088 2089 val = -1 2090 for (enam, eval) in typeobj.values: 2091 val = int(eval) if eval != '' else val+1 2092 ecg.add_discriminant(enam, val) 2093 2094 ecg.write_ffi() 2095 ecg.write_rs() 2096 2097 2098 2099 2100def rs_struct(struct, nametup): 2101 ''' 2102 struct is Struct object 2103 nametup is a name tuple 2104 ''' 2105 global current_handler 2106 current_handler = ('struct: ', nametup) 2107 2108 _prepare_doc(struct) 2109 2110 struct.has_lifetime = True 2111 2112 _ffi_type_setup(struct, nametup) 2113 _rs_type_setup(struct, nametup) 2114 _handle_switch(struct, nametup) 2115 2116 _set_type_lifetime(struct, struct.has_lifetime) 2117 2118 _ffi_struct(struct) 2119 _ffi_accessors(struct, nametup) 2120 _ffi_iterator(struct, nametup) 2121 2122 _rs_struct(struct) 2123 _rs_accessors(struct) 2124 _rs_iterator(struct) 2125 2126 2127 2128def rs_union(union, nametup): 2129 ''' 2130 union is Union object 2131 nametup is a name tuple 2132 ''' 2133 global current_handler 2134 current_handler = ('union: ', nametup) 2135 2136 _prepare_doc(union) 2137 2138 union.has_lifetime = False 2139 2140 _ffi_type_setup(union, nametup) 2141 _rs_type_setup(union, nametup) 2142 2143 biggest = 1 2144 most_aligned = 1 2145 ptr_size = 8 if sys.maxsize > 2**32 else 4 2146 for field in union.fields: 2147 fs = ptr_size 2148 fa = ptr_size 2149 if field.type.size: 2150 fs = field.type.size 2151 fa = field.type.size 2152 if field.type.nmemb: 2153 fs = fa * field.type.nmemb 2154 biggest = max(biggest, fs) 2155 most_aligned = max(most_aligned, fa) 2156 2157 assert biggest >= most_aligned 2158 2159 num_aligned = int(biggest / most_aligned) 2160 if biggest % most_aligned: 2161 num_aligned += 1 2162 2163 num_bytes = num_aligned * most_aligned 2164 union.union_num_bytes = num_bytes 2165 2166 _f.section(0) 2167 _f('') 2168 _write_doc_brief_desc(_f, union.doc) 2169 _f('// union') 2170 _f('#[repr(C)]') 2171 _f('pub struct %s {', union.ffi_type) 2172 _f(' pub data: [u8; %d]', num_bytes) 2173 _f('}') 2174 2175 _f('') 2176 _f('impl Copy for %s {}', union.ffi_type) 2177 _f('impl Clone for %s {', union.ffi_type) 2178 _f(' fn clone(&self) -> %s { *self }', union.ffi_type) 2179 _f('}') 2180 2181 _ffi_iterator(union, nametup) 2182 2183 _r.section(1) 2184 _r('') 2185 _r('pub type %s = %s;', union.rs_type, union.ffi_type) 2186 _rs_accessors(union) 2187 _rs_iterator(union) 2188 2189 2190 2191def rs_request(request, nametup): 2192 ''' 2193 request is Request object 2194 nametup is a name tuple 2195 ''' 2196 global current_handler 2197 current_handler = ('request: ', nametup) 2198 2199 _prepare_doc(request) 2200 2201 request.has_lifetime = False 2202 2203 _ffi_type_setup(request, nametup, ('request',)) 2204 _rs_type_setup(request, nametup, ('request',)) 2205 _handle_switch(request, nametup) 2206 2207 _set_type_lifetime(request, request.has_lifetime) 2208 2209 rcg = RequestCodegen(request) 2210 2211 _opcode(nametup, request.opcode) 2212 _ffi_struct(request) 2213 2214 if request.reply: 2215 _prepare_doc(request.reply) 2216 # enable getting the request from the reply 2217 request.reply.request = request 2218 request.reply.has_lifetime = False 2219 2220 _cookie(request) 2221 2222 _ffi_type_setup(request.reply, nametup, ('reply',)) 2223 _rs_type_setup(request.reply, nametup, ('reply',)) 2224 _handle_switch(request.reply, nametup) 2225 2226 _set_type_lifetime(request.reply, request.reply.has_lifetime) 2227 2228 _ffi_struct(request.reply) 2229 _ffi_accessors(request.reply, nametup + ('reply',)) 2230 _ffi_reply(request) 2231 if _ffi_reply_has_fds(request.reply): 2232 _ffi_reply_fds(request, nametup) 2233 2234 _rs_reply(request) 2235 _rs_reply_accessors(request.reply) 2236 2237 # regular call 'request_name' 2238 rcg.write_ffi_rs(True, False) 2239 # unregular call 'request_name_checked' or 'request_name_unchecked' 2240 # depending on cookie type 2241 rcg.write_ffi_rs(False, False) 2242 2243 if request.ffi_need_aux: 2244 rcg.write_ffi_rs(True, True) 2245 rcg.write_ffi_rs(False, True) 2246 2247 2248def rs_event(event, nametup): 2249 ''' 2250 event is Event object 2251 nametup is a name tuple 2252 ''' 2253 global current_handler 2254 current_handler = ('event: ', nametup) 2255 2256 must_pack = _must_pack_event(event, nametup) 2257 # _must_pack_event may insert fields, 2258 # therefore must be called before _prepare_doc 2259 _prepare_doc(event) 2260 2261 if must_pack: 2262 print('event ', nametup, ' is packed') 2263 2264 event.has_lifetime = False 2265 2266 _ffi_type_setup(event, nametup, ('event',)) 2267 _rs_type_setup(event, nametup, ('event',)) 2268 2269 _set_type_lifetime(event, event.has_lifetime) 2270 2271 _opcode(nametup, event.opcodes[nametup]) 2272 2273 _r.section(1) 2274 _r('') 2275 _write_doc_brief_desc(_r, event.doc) 2276 _r('pub type %s = base::Event<%s>;', event.rs_type, event.ffi_type) 2277 2278 if event.name == nametup: 2279 _ffi_struct(event, must_pack) 2280 2281 accessor_fields = [] 2282 for f in event.fields: 2283 if not f.visible: continue 2284 accessor_fields.append(f) 2285 if f.type.is_list or f.type.is_switch or f.type.is_bitcase: 2286 try: 2287 accessor_fields.remove(f.type.expr.lenfield) 2288 except: 2289 pass 2290 2291 new_params = [] 2292 if len(event.opcodes) > 1: 2293 new_params.append('response_type: u8') 2294 2295 _r.section(1) 2296 _r('') 2297 _r('impl %s {', event.rs_type) 2298 with _r.indent_block(): 2299 for f in accessor_fields: 2300 for fd in event.doc.fields[f.field_name]: 2301 _r('/// %s', fd) 2302 _rs_accessor(event, f, True) 2303 2304 rs_ftype = f.rs_field_type 2305 if f.has_subscript: 2306 rs_ftype = "[%s; %d]" % (rs_ftype, f.type.nmemb) 2307 2308 new_params.append("%s: %s" % (f.rs_field_name, rs_ftype)) 2309 2310 _r('/// Constructs a new %s', event.rs_type) 2311 if len(event.opcodes) > 1: 2312 _r('/// `response_type` must be set to one of:') 2313 for opname in event.opcodes: 2314 _r('/// - `%s`', _rs_const_name(opname)) 2315 else: 2316 _r('/// `response_type` will be set automatically to %s', 2317 _rs_const_name(nametup)) 2318 fn_start = "pub fn new(" 2319 fn_space = ' ' * len(fn_start) 2320 p = new_params[0] if len(new_params) else '' 2321 eol = ',' if len(new_params)>1 else ')' 2322 _r('%s%s%s', fn_start, p, eol) 2323 for (i, p) in enumerate(new_params[1:]): 2324 eol = ',' if i != len(new_params)-2 else ')' 2325 _r("%s%s%s", fn_space, p, eol) 2326 2327 _r(' -> %s {', event.rs_type) 2328 with _r.indent_block(): 2329 _r('unsafe {') 2330 with _r.indent_block(): 2331 _r('let raw = libc::malloc(32 as usize) as *mut %s;', 2332 event.ffi_type) 2333 if len(event.opcodes) > 1: 2334 # build list of possible opcodes 2335 orlist = ' ||\n '.join( 2336 [('response_type == %s' % _rs_const_name(opname)) 2337 for opname in event.opcodes]) 2338 _r('assert!(%s,', orlist) 2339 _r(' "wrong response_type supplied to %s::new");', 2340 event.rs_type) 2341 _r('(*raw).response_type = response_type;') 2342 else: 2343 _r('(*raw).response_type = %s;', _rs_const_name(nametup)) 2344 for f in event.fields: 2345 if not f.visible: continue 2346 if f.type.is_container and not f.type.is_union \ 2347 and not f.type.rs_is_pod: 2348 _r('(*raw).%s = *%s.ptr;', 2349 f.ffi_field_name, f.rs_field_name) 2350 2351 elif f.type.rs_is_pod: 2352 _r('(*raw).%s = %s.base;', f.ffi_field_name, 2353 f.rs_field_name) 2354 2355 else: 2356 assignment = f.rs_field_name 2357 if f.rs_field_type == 'bool': 2358 assignment = ('if %s { 1 } else { 0 }' % 2359 f.rs_field_name) 2360 _r('(*raw).%s = %s;', f.ffi_field_name, assignment) 2361 _r('%s {', event.rs_type) 2362 _r(' ptr: raw') 2363 _r('}') 2364 _r('}') 2365 _r('}') 2366 _r('}') 2367 2368 2369 else: 2370 _f.section(0) 2371 _f('') 2372 _f('pub type %s = %s;', _ffi_type_name(nametup+('event',)), 2373 _ffi_type_name(event.name+('event',))) 2374 2375 2376 2377def rs_error(error, nametup): 2378 ''' 2379 error is Error object 2380 nametup is a name tuple 2381 ''' 2382 global current_handler 2383 current_handler = ('error: ', nametup) 2384 2385 _prepare_doc(error) 2386 2387 _ffi_type_setup(error, nametup, ('error',)) 2388 _opcode(nametup, error.opcodes[nametup]) 2389 2390 if error.name == nametup: 2391 _ffi_struct(error) 2392 else: 2393 _f.section(0) 2394 _f('') 2395 _f('pub type %s = %s;', _ffi_type_name(nametup+('error',)), 2396 _ffi_type_name(error.name+('error',))) 2397 2398 _rs_type_setup(error, nametup, ('error',)) 2399 _r.section(0) 2400 _r('') 2401 _r('pub struct %s {', error.rs_type) 2402 _r(' pub base: base::Error<%s>', error.ffi_type) 2403 _r('}') 2404 2405 2406def usage(program): 2407 print('Usage: {} -o SRCDIR file.xml', program, file=sys.stderr) 2408 2409 2410if __name__ == '__main__': 2411 2412 from optparse import OptionParser 2413 2414 parser = OptionParser(usage="Usage: %prog -o SRCDIR file.xml") 2415 parser.add_option('-o', '--output', dest='srcdir', metavar='SRCDIR', 2416 help='specifies rust src dir where to generate files') 2417 2418 (options, args) = parser.parse_args(sys.argv) 2419 2420 if options.srcdir == None: 2421 parser.error('-o SRCDIR is mandatory') 2422 2423 if not os.path.isdir(options.srcdir): 2424 parser.error('-o SRCDIR must be a directory') 2425 2426 if len(args) < 2: 2427 parser.error('input XML file must be supplied') 2428 2429 output = { 'open' : rs_open, 2430 'close' : rs_close, 2431 'simple' : rs_simple, 2432 'enum' : rs_enum, 2433 'struct' : rs_struct, 2434 'union' : rs_union, 2435 'request' : rs_request, 2436 'event' : rs_event, 2437 'error' : rs_error } 2438 try: 2439 from xcbgen.state import Module 2440 from xcbgen.xtypes import * 2441 except ImportError: 2442 print('failed to load xcbgen', file=sys.stderr) 2443 raise 2444 2445 # Parse the xml header 2446 module = Module(args[1], output) 2447 module.rs_srcdir = options.srcdir 2448 2449 # Build type-registry and resolve type dependencies 2450 module.register() 2451 module.resolve() 2452 2453 # Output the code 2454 try: 2455 module.generate() 2456 except: 2457 print('error occured in handler: ', current_handler, file=sys.stderr) 2458 raise 2459