1 // Copyright © 2017-2021 Rémi Thebault 2 /++ 3 + Wayland scanner for D. 4 + Generation of client protocol code. 5 +/ 6 module wayland.scanner.client; 7 8 import wayland.scanner; 9 import wayland.scanner.common; 10 11 import arsd.dom; 12 13 import std.algorithm; 14 import std.exception; 15 import std.format; 16 import std.range; 17 18 19 alias Interface = wayland.scanner.common.Interface; 20 21 class ClientFactory : Factory 22 { 23 override Protocol makeProtocol(Element el) 24 { 25 return new ClientProtocol(el); 26 } 27 override Interface makeInterface(Element el, Protocol protocol) 28 { 29 return new ClientInterface(el, protocol); 30 } 31 override Message makeMessage(Element el, Interface iface) 32 { 33 return new ClientMessage(el, iface); 34 } 35 override Arg makeArg(Element el) 36 { 37 return new ClientArg(el); 38 } 39 } 40 41 class ClientArg : Arg 42 { 43 this(Element el) 44 { 45 super(el); 46 } 47 48 override @property string dType() const 49 { 50 final switch(type) { 51 case ArgType.Int: 52 case ArgType.UInt: 53 case ArgType.Fixed: 54 case ArgType.String: 55 case ArgType.Array: 56 case ArgType.Fd: 57 return Arg.dType; 58 case ArgType.NewId: 59 case ArgType.Object: 60 if (iface.length) 61 return ifaceDName(iface); 62 else 63 return "WlProxy"; 64 } 65 } 66 67 override @property string cType() const 68 { 69 final switch(type) { 70 case ArgType.Int: 71 case ArgType.UInt: 72 case ArgType.Fixed: 73 case ArgType.String: 74 case ArgType.Array: 75 case ArgType.Fd: 76 case ArgType.Object: 77 return Arg.cType; 78 case ArgType.NewId: 79 return "wl_proxy*"; 80 } 81 } 82 83 override @property string cCastExpr() const 84 { 85 final switch(type) { 86 case ArgType.Int: 87 case ArgType.UInt: 88 case ArgType.Fixed: 89 case ArgType.String: 90 case ArgType.Array: 91 case ArgType.Fd: 92 return Arg.cCastExpr; 93 case ArgType.NewId: 94 case ArgType.Object: 95 return format("%s.proxy", paramName); 96 } 97 } 98 99 @property string reqCType() const 100 { 101 final switch(type) { 102 case ArgType.Int: 103 case ArgType.UInt: 104 case ArgType.Fixed: 105 case ArgType.String: 106 case ArgType.Array: 107 case ArgType.Fd: 108 return Arg.cType; 109 case ArgType.NewId: 110 case ArgType.Object: 111 return "wl_proxy*"; 112 } 113 } 114 115 @property string evCType() const 116 { 117 final switch(type) { 118 case ArgType.Int: 119 case ArgType.UInt: 120 case ArgType.Fixed: 121 case ArgType.String: 122 case ArgType.Array: 123 case ArgType.Fd: 124 return Arg.cType; 125 case ArgType.NewId: 126 case ArgType.Object: 127 if (iface.empty) return "void*"; 128 else return "wl_proxy*"; 129 } 130 } 131 132 override string dCastExpr(string parentIface) const 133 { 134 final switch(type) { 135 case ArgType.Int: 136 case ArgType.UInt: 137 case ArgType.Fixed: 138 case ArgType.String: 139 case ArgType.Array: 140 case ArgType.Fd: 141 return Arg.dCastExpr(parentIface); 142 case ArgType.NewId: 143 if (iface) 144 return format("new %s(%s)", ifaceDName(iface), paramName); 145 else 146 return format("WlProxy.get(%s)", paramName); 147 case ArgType.Object: 148 auto expr = format("WlProxy.get(%s)", paramName); 149 if (iface) 150 return format("cast(%s)%s", ifaceDName(iface), expr); 151 else 152 return expr; 153 } 154 } 155 } 156 157 158 class ClientMessage : Message 159 { 160 this (Element el, Interface iface) 161 { 162 super(el, iface); 163 } 164 165 @property auto clArgs() 166 { 167 return args.map!(a => cast(ClientArg)a); 168 } 169 170 @property string reqRetStr() 171 { 172 final switch (reqType) 173 { 174 case ReqType.newObj: 175 return ifaceDName(reqRet.iface); 176 case ReqType.dynObj: 177 return "WlProxy"; 178 case ReqType.void_: 179 return "void"; 180 } 181 } 182 183 void writeRequestCode(SourceFile sf) 184 { 185 final switch(reqType) 186 { 187 case ReqType.void_: 188 writeVoidReqDefinitionCode(sf); 189 break; 190 case ReqType.newObj: 191 writeNewObjReqDefinitionCode(sf); 192 break; 193 case ReqType.dynObj: 194 writeDynObjReqDefinitionCode(sf); 195 break; 196 } 197 } 198 199 void writeEventDgAlias(SourceFile sf) 200 { 201 sf.writeln("/// Event delegate signature of %s.%s.", ifaceDName(ifaceName), dHandlerName); 202 string[] rtArgs = [ 203 format("%s %s", ifaceDName(ifaceName), camelName(ifaceName)) 204 ]; 205 foreach(a; args) 206 { 207 rtArgs ~= format("%s %s", a.dType, a.paramName); 208 } 209 writeDelegateAlias(sf, dEvDgType, "void", rtArgs); 210 } 211 212 void writeEventDgAccessor(SourceFile sf) 213 { 214 description.writeCode(sf); 215 sf.writeln("@property void %s(%s dg)", dHandlerName, dEvDgType); 216 sf.bracedBlock!({ 217 sf.writeln("_%s = dg;", dHandlerName); 218 }); 219 } 220 221 void writeVoidReqDefinitionCode(SourceFile sf) 222 { 223 string[] rtArgs; 224 string[] exprs = [ 225 "proxy", opCodeName 226 ]; 227 foreach(arg; args) 228 { 229 rtArgs ~= (arg.dType ~ " " ~ arg.paramName); 230 exprs ~= arg.cCastExpr; 231 } 232 string[] postStmt = isDtor ? ["super.destroyNotify();"] : []; 233 234 description.writeCode(sf); 235 sf.writeFnSig("void", dMethodName, rtArgs); 236 sf.writeFnBody([], "wl_proxy_marshal", exprs, postStmt); 237 } 238 239 void writeNewObjReqDefinitionCode(SourceFile sf) 240 { 241 string[] rtArgs; 242 string[] exprs = [ 243 "proxy", opCodeName, format("%s.iface.native", ifaceDName(reqRet.iface)) 244 ]; 245 foreach(arg; args) 246 { 247 if (arg is reqRet) 248 { 249 exprs ~= "null"; 250 } 251 else 252 { 253 rtArgs ~= format("%s %s", arg.dType, arg.paramName); 254 exprs ~= arg.cCastExpr; 255 } 256 } 257 description.writeCode(sf); 258 sf.writeFnSig(reqRetStr, dMethodName, rtArgs); 259 sf.writeFnBody([], 260 "auto _pp = wl_proxy_marshal_constructor", exprs, 261 [ "if (!_pp) return null;", 262 "auto _p = WlProxy.get(_pp);", 263 format("if (_p) return cast(%s)_p;", reqRetStr), 264 format("return new %s(_pp);", reqRetStr) ] 265 ); 266 } 267 268 void writeDynObjReqDefinitionCode(SourceFile sf) 269 { 270 string[] rtArgs; 271 string[] exprs = [ 272 "proxy", opCodeName, "iface.native", "ver" 273 ]; 274 foreach(arg; args) 275 { 276 if (arg is reqRet) 277 { 278 rtArgs ~= [ "immutable(WlProxyInterface) iface", "uint ver" ]; 279 exprs ~= [ "iface.native.name", "ver" ]; 280 } 281 else 282 { 283 rtArgs ~= format("%s %s", arg.dType, arg.paramName); 284 exprs ~= arg.cCastExpr; 285 } 286 } 287 description.writeCode(sf); 288 sf.writeFnSig(reqRetStr, dMethodName, rtArgs); 289 sf.writeFnBody([], 290 "auto _pp = wl_proxy_marshal_constructor_versioned", exprs, 291 [ "if (!_pp) return null;", 292 "auto _p = WlProxy.get(_pp);", 293 "if (_p) return _p;", 294 "return iface.makeProxy(_pp);" ] 295 ); 296 } 297 298 void writePrivListenerSig(SourceFile sf) 299 { 300 enum fstLine = "void function("; 301 immutable lstEol = format(") %s;", cEvName); 302 303 immutable indent = ' '.repeat.take(fstLine.length).array(); 304 sf.writeln("%svoid* data,", fstLine); 305 auto eol = args.empty ? lstEol : ","; 306 sf.writeln("%swl_proxy* proxy%s", indent, eol); 307 foreach(i, arg; enumerate(clArgs)) 308 { 309 eol = i == args.length-1 ? lstEol : ","; 310 sf.writeln("%s%s %s%s", indent, arg.evCType, arg.paramName, eol); 311 } 312 } 313 314 void writePrivListenerStub(SourceFile sf) 315 { 316 immutable fstLine = format("void wl_d_on_%s_%s(", ifaceName, name); 317 immutable indent = ' '.repeat.take(fstLine.length).array(); 318 sf.writeln("%svoid* data,", fstLine); 319 auto eol = args.empty ? ")" : ","; 320 sf.writeln("%swl_proxy* proxy%s", indent, eol); 321 foreach(i, arg; enumerate(clArgs)) 322 { 323 eol = i == args.length-1 ? ")" : ","; 324 sf.writeln("%s%s %s%s", indent, arg.evCType, arg.paramName, eol); 325 } 326 sf.bracedBlock!({ 327 sf.writeln("nothrowFnWrapper!({"); 328 sf.indentedBlock!({ 329 sf.writeln("auto _p = WlProxy.get(proxy);"); 330 sf.writeln("assert(_p, \"listener stub without proxy\");"); 331 sf.writeln("auto _i = cast(%s)_p;", ifaceDName(ifaceName)); 332 sf.writeln("assert(_i, \"listener stub proxy is not %s\");", ifaceDName(ifaceName)); 333 sf.writeln("if (_i._%s)", dHandlerName); 334 sf.bracedBlock!({ 335 string sep = args.length ? ", " : ""; 336 sf.write("_i._%s(_i%s", dHandlerName, sep); 337 foreach (i, arg; args) 338 { 339 sep = (i == args.length-1) ? "" : ", "; 340 sf.write("%s%s", arg.dCastExpr(ifaceName), sep); 341 } 342 sf.writeln(");"); 343 }); 344 345 }); 346 sf.writeln("});"); 347 }); 348 } 349 } 350 351 352 class ClientInterface : Interface 353 { 354 this (Element el, Protocol protocol) 355 { 356 super(el, protocol); 357 } 358 359 @property auto clRequests() 360 { 361 return requests.map!(r => cast(ClientMessage)r); 362 } 363 364 @property auto clEvents() 365 { 366 return events.map!(r => cast(ClientMessage)r); 367 } 368 369 @property string globalNativeListenerName() 370 { 371 return format("wl_d_%s_listener", name); 372 } 373 374 override void writeCode(SourceFile sf) 375 { 376 description.writeCode(sf); 377 378 sf.writeln("final class %s : %s", dName, 379 name == "wl_display" ? 380 "WlDisplayBase" : 381 "WlProxy"); 382 sf.bracedBlock!( 383 { 384 writeVersion(sf); 385 sf.writeln(); 386 sf.writeln("/// Build a %s from a native object.", dName); 387 sf.writeln(name == "wl_display" ? 388 "package(wayland) this(wl_display* native)" : 389 "private this(wl_proxy* native)" 390 ); 391 sf.bracedBlock!({ 392 sf.writeln("super(native);"); 393 if (writeEvents) 394 { 395 sf.writeln( 396 "wl_proxy_add_listener(proxy, cast(void_func_t*)&%s, null);", 397 globalNativeListenerName 398 ); 399 } 400 }); 401 sf.writeln(); 402 sf.writeln("/// Interface object that creates %s objects.", dName); 403 sf.writeln("static @property immutable(WlProxyInterface) iface()"); 404 sf.bracedBlock!({ 405 sf.writeln("return %sIface;", camelName(name)); 406 }); 407 writeConstants(sf); 408 if (writeEvents) 409 { 410 sf.writeln(); 411 foreach(msg; clEvents) 412 { 413 msg.writeEventDgAlias(sf); 414 } 415 } 416 foreach (en; enums) 417 { 418 sf.writeln(); 419 en.writeCode(sf); 420 } 421 writeDtorCode(sf); 422 foreach (msg; clRequests) 423 { 424 sf.writeln(); 425 msg.writeRequestCode(sf); 426 } 427 if (writeEvents) 428 { 429 foreach(msg; clEvents) 430 { 431 sf.writeln(); 432 msg.writeEventDgAccessor(sf); 433 } 434 435 sf.writeln(); 436 foreach(msg; events) 437 { 438 sf.writeln("private %s _%s;", msg.dEvDgType, msg.dHandlerName); 439 } 440 } 441 }); 442 } 443 444 void writeConstants(SourceFile sf) 445 { 446 if (requests.length) 447 { 448 sf.writeln(); 449 foreach(i, msg; requests) 450 { 451 sf.writeln("/// Op-code of %s.%s.", dName, msg.dMethodName); 452 sf.writeln("enum %s = %d;", msg.opCodeName, i); 453 } 454 sf.writeln(); 455 foreach(msg; requests) 456 { 457 sf.writeln( 458 "/// Version of %s protocol introducing %s.%s.", 459 protocol.name, dName, msg.dMethodName 460 ); 461 sf.writeln("enum %sSinceVersion = %d;", camelName(msg.name), msg.since); 462 } 463 } 464 if (events.length) 465 { 466 sf.writeln(); 467 foreach(msg; events) 468 { 469 sf.writeln( 470 "/// %s protocol version introducing %s.%s.", 471 protocol.name, dName, msg.dHandlerName 472 ); 473 sf.writeln("enum %sSinceVersion = %d;", msg.dHandlerName, msg.since); 474 } 475 } 476 } 477 478 void writeDtorCode(SourceFile sf) 479 { 480 immutable hasDtor = requests.canFind!(rq => rq.isDtor); 481 immutable hasDestroy = requests.canFind!(rq => rq.name == "destroy"); 482 483 enforce(!hasDestroy || hasDtor); 484 485 if (!hasDestroy && name != "wl_display") 486 { 487 sf.writeln(); 488 sf.writeln("/// Destroy this %s object.", dName); 489 sf.writeln("void destroy()"); 490 sf.bracedBlock!({ 491 sf.writeln("wl_proxy_destroy(proxy);"); 492 sf.writeln("super.destroyNotify();"); 493 }); 494 } 495 } 496 497 void writePrivListener(SourceFile sf) 498 { 499 if (!writeEvents) return; 500 501 sf.writeln("struct %s_listener", name); 502 sf.bracedBlock!({ 503 foreach(ev; clEvents) 504 { 505 ev.writePrivListenerSig(sf); 506 } 507 }); 508 509 sf.writeln(); 510 immutable fstLine = format("__gshared %s = %s_listener (", globalNativeListenerName, name); 511 immutable indent = ' '.repeat(fstLine.length).array(); 512 foreach (i, ev; events) 513 { 514 sf.writeln("%s&wl_d_on_%s_%s%s", (i == 0 ? fstLine : indent), 515 name, ev.name, 516 (i == events.length-1) ? ");" : ","); 517 } 518 } 519 520 void writePrivListenerStubs(SourceFile sf) 521 { 522 if (!writeEvents) return; 523 524 foreach(ev; clEvents) 525 { 526 sf.writeln(); 527 ev.writePrivListenerStub(sf); 528 } 529 } 530 } 531 532 533 534 class ClientProtocol : Protocol 535 { 536 this(Element el) 537 { 538 super(el); 539 } 540 541 @property auto clIfaces() 542 { 543 return ifaces.map!(iface => cast(ClientInterface)iface); 544 } 545 546 override void writeCode(SourceFile sf, in Options opt) 547 { 548 writeHeader(sf, opt); 549 if (name == "wayland") sf.writeln("import wayland.client.core;"); 550 else sf.writeln("import wayland.client;"); 551 sf.writeln("import wayland.native.client;"); 552 sf.writeln("import wayland.native.util;"); 553 sf.writeln("import wayland.util;"); 554 sf.writeln(); 555 sf.writeln("import std.exception : enforce;"); 556 sf.writeln("import std.string : fromStringz, toStringz;"); 557 sf.writeln(); 558 559 foreach(iface; ifaces) 560 { 561 iface.writeCode(sf); 562 sf.writeln(); 563 } 564 565 // writing private code 566 sf.writeln("private:"); 567 sf.writeln(); 568 569 writePrivIfaces(sf); 570 sf.writeln(); 571 572 sf.writeln("extern(C) nothrow"); 573 sf.bracedBlock!({ 574 foreach(i, iface; enumerate(clIfaces)) 575 { 576 if (i != 0) sf.writeln(); 577 iface.writePrivListener(sf); 578 iface.writePrivListenerStubs(sf); 579 } 580 }); 581 } 582 583 override void writePrivIfaces(SourceFile sf) 584 { 585 foreach(iface; ifaces) 586 { 587 sf.writeln("immutable WlProxyInterface %sIface;", camelName(iface.name)); 588 } 589 foreach (iface; ifaces) 590 { 591 sf.writeln(); 592 sf.writeln("immutable final class %sIface : WlProxyInterface", 593 titleCamelName(iface.name)); 594 sf.bracedBlock!({ 595 sf.writeln("this(immutable wl_interface* native)"); 596 sf.bracedBlock!({ 597 sf.writeln("super(native);"); 598 }); 599 sf.writeln("override WlProxy makeProxy(wl_proxy* proxy) immutable"); 600 sf.bracedBlock!({ 601 if (iface.name == "wl_display") 602 { 603 sf.writeln("return new WlDisplay(cast(wl_display*)proxy);"); 604 } 605 else 606 sf.writeln("return new %s(proxy);", iface.dName); 607 }); 608 }); 609 } 610 writeNativeIfaces(sf); 611 } 612 613 override void writeNativeIfacesAssignment(SourceFile sf) 614 { 615 foreach (iface; ifaces) 616 { 617 sf.writeln("%sIface = new immutable %sIface( &wl_ifaces[%s] );", 618 camelName(iface.name), 619 titleCamelName(iface.name), 620 indexSymbol(iface.name)); 621 } 622 } 623 624 }