1 // Copyright © 2017 Rémi Thebault 2 /++ 3 + Wayland scanner for D. 4 + Generation of server protocol code. 5 +/ 6 module wayland.scanner.server; 7 8 import wayland.scanner; 9 import wayland.scanner.common; 10 11 import arsd.dom; 12 13 import std.algorithm; 14 import std.format; 15 import std.range; 16 17 alias Interface = wayland.scanner.common.Interface; 18 19 class ServerFactory : Factory 20 { 21 override Protocol makeProtocol(Element el) 22 { 23 return new ServerProtocol(el); 24 } 25 override Interface makeInterface(Element el, Protocol protocol) 26 { 27 return new ServerInterface(el, protocol); 28 } 29 override Message makeMessage(Element el, Interface iface) 30 { 31 return new ServerMessage(el, iface); 32 } 33 override Arg makeArg(Element el) 34 { 35 return new ServerArg(el); 36 } 37 } 38 39 40 // Server bindings implementation notes: 41 // There are two kind of server objects: globals and resources. 42 // 43 // Globals inherit WlGlobal and are to be created by the compositor at startup. 44 // Each created global is announced by the registry to the client. 45 // `[Global].bind` function create a `[Global].Resource` object whose implementation 46 // is set automatically to the outer Global object, which MUST override the abstract 47 // request handlers. 48 // 49 // Resources inherit WlResource. They are created by global or other parent resource 50 // objects upon client requests. These resources must be subclassed by the application 51 // and their abstract request handlers must be overriden. 52 53 54 class ServerArg : Arg 55 { 56 this(Element el) 57 { 58 super(el); 59 } 60 61 override @property string dType() const 62 { 63 final switch(type) { 64 case ArgType.Int: 65 case ArgType.UInt: 66 case ArgType.Fixed: 67 case ArgType.String: 68 case ArgType.NewId: 69 case ArgType.Array: 70 case ArgType.Fd: 71 return Arg.dType; 72 case ArgType.Object: 73 if (iface.length) 74 { 75 auto i = ServerInterface.get(iface); 76 if (i && i.isGlobal) { 77 return i.dName ~ ".Resource"; 78 } 79 else if (i && !i.isGlobal) { 80 return i.dName; 81 } 82 } 83 return "WlResource"; 84 } 85 } 86 87 override @property string cCastExpr() const 88 { 89 final switch(type) { 90 case ArgType.Int: 91 case ArgType.UInt: 92 case ArgType.Fixed: 93 case ArgType.String: 94 case ArgType.NewId: 95 case ArgType.Array: 96 case ArgType.Fd: 97 return Arg.cCastExpr; 98 case ArgType.Object: 99 return format("%s.native", paramName); 100 } 101 } 102 } 103 104 105 class ServerMessage : Message 106 { 107 this (Element el, Interface iface) 108 { 109 super(el, iface); 110 } 111 112 @property ServerInterface svIface() 113 { 114 return cast(ServerInterface)iface; 115 } 116 117 @property auto svArgs() 118 { 119 return args.map!(a => cast(ServerArg)a); 120 } 121 122 @property string reqMethodName() 123 { 124 return camelName(name); 125 } 126 127 @property string sendName() 128 { 129 return "send" ~ titleCamelName(name); 130 } 131 132 @property string privRqListenerStubName() 133 { 134 return format("wl_d_%s_%s", ifaceName, name); 135 } 136 137 @property string[] reqRtArgs() 138 { 139 string[] rtArgs = [ "WlClient cl" ]; 140 if (iface.isGlobal) rtArgs ~= format( 141 "%s res", svIface.selfResType(Yes.local) 142 ); 143 foreach (a; args) { 144 if (a.type == ArgType.NewId && !a.iface.length) 145 { 146 rtArgs ~= [ 147 "string iface", "uint ver", format("uint %s", a.paramName) 148 ]; 149 } 150 else { 151 rtArgs ~= format("%s %s", a.dType, a.paramName); 152 } 153 } 154 return rtArgs; 155 } 156 157 @property string reqRetStr() 158 { 159 final switch(reqType) 160 { 161 case ReqType.newObj: 162 return ifaceDName(reqRet.iface); 163 case ReqType.dynObj: 164 return "WlResource"; 165 case ReqType.void_: 166 return "void"; 167 } 168 } 169 170 void writeReqMethodDecl(SourceFile sf, string[] attrs) 171 { 172 writeFnSigRaw(sf, attrs.join(" "), reqRetStr, reqMethodName, reqRtArgs); 173 sf.writeln(";"); 174 } 175 176 177 void writeSendResMethod(SourceFile sf) 178 { 179 description.writeCode(sf); 180 string[] rtArgs; 181 string[] exprs = [ 182 "this.native", 183 opCodeName, 184 ]; 185 foreach (arg; args) 186 { 187 rtArgs ~= format("%s %s", arg.dType, arg.paramName); 188 exprs ~= arg.cCastExpr; 189 } 190 sf.writeFnSig("void", sendName, rtArgs); 191 sf.writeFnBody([], "wl_resource_post_event", exprs, []); 192 } 193 194 void writePrivRqListenerStub(SourceFile sf) 195 { 196 string[] rtArgs = [ 197 "wl_client* natCl", "wl_resource* natRes", 198 ]; 199 string[] exprs = [ 200 "cast(WlClient)ObjectCache.get(natCl)", 201 ]; 202 if (iface.isGlobal) exprs ~= "_res"; 203 foreach (a; args) { 204 if (a.type == ArgType.Object) 205 { 206 rtArgs ~= format("wl_resource* %s", a.paramName); 207 // TODO: check if wl_resource_get_user_data could work here 208 exprs ~= format("cast(%s)ObjectCache.get(%s)", a.dType, a.paramName); 209 } 210 else if (a.type == ArgType.NewId && !a.iface.length) 211 { 212 rtArgs ~= [ 213 "const(char)* iface", "uint ver", format("uint %s", a.paramName) 214 ]; 215 exprs ~= [ 216 "fromStringz(iface).idup", "ver", a.paramName 217 ]; 218 } 219 else { 220 rtArgs ~= format("%s %s", a.cType, a.paramName); 221 exprs ~= a.dCastExpr(ifaceName); 222 } 223 } 224 writeFnSig(sf, "void", privRqListenerStubName, rtArgs); 225 sf.bracedBlock!({ 226 sf.writeln("nothrowFnWrapper!({"); 227 sf.indentedBlock!({ 228 immutable resType = svIface.selfResType(No.local); 229 sf.writeln("auto _res = cast(%s)wl_resource_get_user_data(natRes);", resType); 230 immutable outer = iface.isGlobal ? ".outer" : ""; 231 writeFnExpr(sf, format("_res%s.%s", outer, reqMethodName), exprs); 232 }); 233 sf.writeln("});"); 234 }); 235 } 236 237 void writePrivStubSig(SourceFile sf) 238 { 239 string[] rtArgs = [ 240 "wl_client* natCl", "wl_resource* natRes", 241 ]; 242 foreach (a; args) { 243 if (a.type == ArgType.Object) 244 { 245 rtArgs ~= format("wl_resource* %s", a.paramName); 246 } 247 else if (a.type == ArgType.NewId && !a.iface.length) 248 { 249 rtArgs ~= [ 250 "const(char)* iface", "uint ver", format("uint %s", a.paramName) 251 ]; 252 } 253 else { 254 rtArgs ~= format("%s %s", a.cType, a.paramName); 255 } 256 } 257 writeFnPointer(sf, name, "void", rtArgs); 258 } 259 } 260 261 262 class ServerInterface : Interface 263 { 264 265 266 static ServerInterface get(string name) 267 { 268 auto i = Interface.get(name); 269 if (i) return cast(ServerInterface)i; 270 else return null; 271 } 272 273 274 this (Element el, Protocol protocol) 275 { 276 super(el, protocol); 277 } 278 279 @property auto svRequests() 280 { 281 return requests.map!(m => cast(ServerMessage)m); 282 } 283 284 @property auto svEvents() 285 { 286 return events.map!(m => cast(ServerMessage)m); 287 } 288 289 string selfResType(Flag!"local" local) 290 { 291 if (local) { 292 return isGlobal ? "Resource" : dName; 293 } 294 else { 295 return dName ~ (isGlobal ? ".Resource" : ""); 296 } 297 } 298 299 @property string bindFuncName() 300 { 301 return format("wl_d_bind_%s", name); 302 } 303 304 @property string listenerStubsStructName() 305 { 306 return format("%sListenersAggregate", titleCamelName(name)); 307 } 308 309 @property string listenerStubsSymbol() 310 { 311 return format("%sListeners", camelName(name)); 312 } 313 314 override void writeCode(SourceFile sf) 315 { 316 description.writeCode(sf); 317 immutable heritage = name == "wl_display" ? " : WlDisplayBase" : 318 (isGlobal ? " : WlGlobal" : " : WlResource"); 319 immutable attrs = name == "wl_display" ? "" : 320 (requests.length ? "abstract " : ""); 321 sf.writeln("%sclass %s%s", attrs, dName, heritage); 322 sf.bracedBlock!({ 323 writeVersion(sf); 324 sf.writeln(); 325 if (name == "wl_display") 326 { 327 sf.writeln("this(wl_display* native)"); 328 sf.bracedBlock!({ 329 sf.writeln("super(native);"); 330 }); 331 sf.writeln(); 332 } 333 334 writeIfaceAccess(sf); 335 336 writeConstants(sf); 337 338 foreach (en; enums) 339 { 340 sf.writeln(); 341 en.writeCode(sf); 342 } 343 344 if (name != "wl_display") 345 { 346 if (isGlobal) 347 { 348 writeGlobalCode(sf); 349 } 350 else 351 { 352 writeResourceCode(sf); 353 } 354 } 355 }); 356 } 357 358 void writeIfaceAccess(SourceFile sf) 359 { 360 sf.writeln("/// Access to the interface of \"%s.%s\"", protocol.name, name); 361 sf.writeln("static @property immutable(WlInterface) iface()"); 362 sf.bracedBlock!({ 363 sf.writeln("return %sIface;", camelName(name)); 364 }); 365 } 366 367 void writeConstants(SourceFile sf) 368 { 369 if (events.length) 370 { 371 sf.writeln(); 372 foreach(i, msg; events) 373 { 374 sf.writeln("/// Op-code of %s.%s.", name, msg.name); 375 sf.writeln("enum %s = %d;", msg.opCodeName, i); 376 } 377 sf.writeln(); 378 foreach(msg; events) 379 { 380 sf.writeln( 381 "/// Version of %s protocol introducing %s.%s.", 382 protocol.name, name, msg.name 383 ); 384 sf.writeln("enum %sSinceVersion = %d;", camelName(msg.name), msg.since); 385 } 386 } 387 if (requests.length) 388 { 389 sf.writeln(); 390 foreach(msg; requests) 391 { 392 sf.writeln( 393 "/// %s protocol version introducing %s.%s.", 394 protocol.name, name, msg.name 395 ); 396 sf.writeln("enum %sSinceVersion = %d;", camelName(msg.name), msg.since); 397 } 398 } 399 } 400 401 void writeGlobalCode(SourceFile sf) 402 { 403 sf.writeln(); 404 sf.writeln("protected this(WlDisplay dpy, uint ver)"); 405 sf.bracedBlock!({ 406 sf.writeln("super(wl_global_create("); 407 sf.indentedBlock!({ 408 sf.writeln("dpy.native, iface.native, ver, cast(void*)this, &%s", bindFuncName); 409 }); 410 sf.writeln("));"); 411 }); 412 sf.writeln(); 413 sf.writeln("/// Create a Resource object when a client connects."); 414 sf.writeln("Resource bind(WlClient cl, uint ver, uint id)"); 415 sf.bracedBlock!({ 416 sf.writeln("return new Resource(cl, ver, id);"); 417 }); 418 foreach(rq; svRequests) 419 { 420 sf.writeln(); 421 rq.description.writeCode(sf); 422 rq.writeReqMethodDecl(sf, ["abstract", "protected"]); 423 } 424 sf.writeln(); 425 writeResourceCodeForGlobal(sf); 426 } 427 428 void writeResourceCodeForGlobal(SourceFile sf) 429 { 430 sf.writeln("class Resource : WlResource"); 431 sf.bracedBlock!({ 432 writeResourceCtors(sf, []); 433 foreach(ev; svEvents) 434 { 435 sf.writeln(); 436 ev.writeSendResMethod(sf); 437 } 438 }); 439 } 440 441 void writeResourceCode(SourceFile sf) 442 { 443 sf.writeln(); 444 writeResourceCtors(sf, ["protected"]); 445 foreach(rq; svRequests) 446 { 447 sf.writeln(); 448 rq.description.writeCode(sf); 449 rq.writeReqMethodDecl(sf, ["abstract", "protected"]); 450 } 451 foreach(ev; svEvents) 452 { 453 sf.writeln(); 454 ev.writeSendResMethod(sf); 455 } 456 } 457 458 void writeResourceCtors(SourceFile sf, string[] attrs) 459 { 460 immutable attrStr = attrs.join(" ") ~ (attrs.length ? " " : ""); 461 sf.writeln("%sthis(WlClient cl, uint ver, uint id)", attrStr); 462 sf.bracedBlock!({ 463 immutable natExpr = "wl_resource_create(cl.native, iface.native, ver, id)"; 464 if (requests.length) 465 { 466 sf.writeln("auto native = %s;", natExpr); 467 writeFnExpr(sf, "wl_resource_set_implementation", [ 468 "native", format("&%s", listenerStubsSymbol), 469 "cast(void*)this", "null" 470 ]); 471 sf.writeln("super(native);"); 472 } 473 else 474 { 475 sf.writeln("super(%s);", natExpr); 476 } 477 }); 478 sf.writeln(); 479 sf.writeln("%sthis(wl_resource* natRes)", attrStr); 480 sf.bracedBlock!({ 481 sf.writeln("super(natRes);"); 482 }); 483 } 484 485 void writePrivBindStub(SourceFile sf) 486 { 487 sf.writeln("void %s(wl_client* natCl, void* data, uint ver, uint id)", bindFuncName); 488 sf.bracedBlock!({ 489 sf.writeln("nothrowFnWrapper!({"); 490 sf.indentedBlock!({ 491 sf.writeln("auto g = cast(%s)data;", dName); 492 sf.writeln("auto cl = cast(WlClient)ObjectCache.get(natCl);"); 493 sf.writeln(`assert(g && cl, "%s: could not get global or client from cache");`, bindFuncName); 494 sf.writeln("g.bind(cl, ver, id);"); 495 }); 496 sf.writeln("});"); 497 }); 498 } 499 500 void writePrivRqListenerStubs(SourceFile sf) 501 { 502 sf.writeln("// %s listener stubs", name); 503 foreach(rq; svRequests) 504 { 505 sf.writeln(); 506 rq.writePrivRqListenerStub(sf); 507 } 508 509 sf.writeln(); 510 sf.writeln("struct %s", listenerStubsStructName); 511 sf.bracedBlock!({ 512 foreach (rq; svRequests) { 513 rq.writePrivStubSig(sf); 514 } 515 }); 516 } 517 518 void writePrivRqListenerStubsSymbol(SourceFile sf) 519 { 520 sf.writeln("__gshared %s = %s (", listenerStubsSymbol, listenerStubsStructName); 521 sf.indentedBlock!({ 522 foreach(rq; svRequests) { 523 sf.writeln("&%s,", rq.privRqListenerStubName); 524 } 525 }); 526 sf.writeln(");"); 527 } 528 } 529 530 class ServerProtocol : Protocol 531 { 532 this(Element el) 533 { 534 super(el); 535 } 536 537 @property auto svIfaces() 538 { 539 return ifaces.map!(i => cast(ServerInterface)i); 540 } 541 542 @property auto svGlobalIfaces() 543 { 544 return svIfaces.filter!(i => i.isGlobal); 545 } 546 547 @property auto svIfacesWithRq() 548 { 549 return svIfaces.filter!(i => i.requests.length > 0); 550 } 551 552 override void writeCode(SourceFile sf, in Options opt) 553 { 554 writeHeader(sf, opt); 555 if (name == "wayland") sf.writeln("import wayland.server.core;"); 556 else sf.writeln("import wayland.server;"); 557 sf.writeln("import wayland.native.server;"); 558 sf.writeln("import wayland.native.util;"); 559 sf.writeln("import wayland.util;"); 560 sf.writeln("import std.string : toStringz, fromStringz;"); 561 sf.writeln(); 562 563 foreach(iface; ifaces) 564 { 565 iface.writeCode(sf); 566 sf.writeln(); 567 } 568 569 // writing private code 570 sf.writeln("private:"); 571 sf.writeln(); 572 573 writePrivIfaces(sf); 574 sf.writeln(); 575 576 sf.writeln("extern(C) nothrow"); 577 sf.bracedBlock!({ 578 bool needNL; 579 foreach(iface; svGlobalIfaces.filter!(i=>i.name != "wl_display")) 580 { 581 if (needNL) sf.writeln(); 582 else needNL = true; 583 iface.writePrivBindStub(sf); 584 } 585 586 foreach(iface; svIfacesWithRq.filter!(i=>i.name != "wl_display")) 587 { 588 if (needNL) sf.writeln(); 589 else needNL = true; 590 iface.writePrivRqListenerStubs(sf); 591 } 592 }); 593 foreach(iface; svIfacesWithRq.filter!(i=>i.name != "wl_display")) 594 { 595 sf.writeln(); 596 iface.writePrivRqListenerStubsSymbol(sf); 597 } 598 } 599 600 override void writePrivIfaces(SourceFile sf) 601 { 602 foreach(iface; ifaces) 603 { 604 sf.writeln("immutable WlInterface %sIface;", camelName(iface.name)); 605 } 606 607 writeNativeIfaces(sf); 608 } 609 610 override void writeNativeIfacesAssignment(SourceFile sf) 611 { 612 foreach (iface; ifaces) 613 { 614 sf.writeln("%sIface = new immutable WlInterface ( &wl_ifaces[%s] );", 615 camelName(iface.name), indexSymbol(iface.name) 616 ); 617 } 618 } 619 }