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