Last active
July 4, 2023 17:58
-
-
Save twaik/563ea91526dccd15e2bf5974d5d3ef10 to your computer and use it in GitHub Desktop.
Gradle waylandpp's wayland-scanner++ task
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This code is based on | |
* https://github.com/NilsBrause/waylandpp/blob/4321ed5c7b4bffa41b8a2a13dc7f3ece1191f4f3/scanner/scanner.cpp | |
* and should generate code identical to wayland-scanner++ | |
* | |
* It is created to be used as gradle task in Android Studio builds where we can not use generators | |
* (crosscompilation does not allow us build code for host). | |
*/ | |
import org.xml.sax.SAXException | |
import javax.xml.transform.stream.StreamSource | |
import javax.xml.validation.SchemaFactory | |
import javax.xml.XMLConstants | |
import groovy.xml.XmlSlurper | |
abstract class WaylandScannerTask extends DefaultTask { | |
class element_t { | |
String name | |
String summary | |
String description | |
static final List<String> keywords = [ | |
"alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", | |
"case", "catch", "char", "char8_t", "char16_t", "char32_t", "class", "compl", "concept", | |
"const", "consteval", "constexpr", "constinit", "const_cast", "continue", "co_await", | |
"co_return", "co_yield", "decltype", "default", "delete", "do", "double", "dynamic_cast", | |
"else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto", | |
"if", "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "not", "not_eq", | |
"nullptr", "operator", "or", "or_eq", "private", "protected", "public", "register", | |
"reinterpret_cast", "requires", "return", "short", "signed", "sizeof", "static", | |
"static_assert", "static_cast", "struct", "switch", "template", "this", "thread_local", | |
"throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", | |
"virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" | |
] | |
static String sanitise(String str) { | |
return ((keywords.contains(str)) ? "_" : "") + str | |
} | |
} | |
class argument_t extends element_t { | |
String type | |
String iface | |
String enum_iface | |
String enum_name | |
Boolean allow_null = false | |
String print_enum_wire_type() { | |
switch(type) { | |
case "int": | |
return "int32_t" | |
case "uint": | |
return "uint32_t" | |
default: | |
throw new RuntimeException("Enum type must be int or uint (have " + type + ")") | |
} | |
} | |
String print_type() { | |
if (!iface.isEmpty()) | |
return iface + "_t" | |
if (enum_iface?.length() > 0) | |
return enum_iface + "_" + enum_name | |
switch (type) { | |
case "int": | |
return "int32_t" | |
case "uint": | |
return "uint32_t" | |
case "fixed": | |
return "double" | |
case "string": | |
return "std::string" | |
case "object": | |
case "new_id": | |
return "resource_t" | |
case "fd": | |
return "int" | |
case "array": | |
return "array_t" | |
default: | |
return type | |
} | |
} | |
String print_short() { | |
switch(type) { | |
case "int": | |
return "i" | |
case "uint": | |
return "u" | |
case "fixed": | |
return "f" | |
case "string": | |
return "s" | |
case "object": | |
return "o" | |
case "new_id": | |
return "n" | |
case "array": | |
return "a" | |
case "fd": | |
return "h" | |
default: | |
return "x" | |
} | |
} | |
String print_argument() { | |
return print_type() + | |
((iface?.length() > 0 || enum_iface?.length() > 0 || | |
type == "string" || type == "array") ? " const& " : " ") + | |
sanitise(name) | |
} | |
} | |
class event_t extends element_t { | |
List<argument_t> args | |
int since = 0 | |
argument_t ret | |
int opcode = 0 | |
String print_functional() { | |
def _args = [] | |
args.each {arg -> _args.add(arg.print_type()) } | |
return " std::function<void(" + _args.join(", ") + ")> " + sanitise(name) + ";" | |
} | |
String print_dispatcher(int opcode) { | |
def ss = "" << "" | |
int c = 0 | |
def _args = [] | |
args.each { arg -> | |
if(arg.enum_name != null && !arg.enum_name.empty && arg.type != "array") | |
_args.add(arg.print_type() << "(args[" << c++ << "].get<" << arg.print_enum_wire_type() << ">())") | |
else if(!arg.iface.empty) | |
_args.add(arg.print_type() << "(args[" << c++ << "].get<resource_t>())") | |
else | |
_args.add("args[" << c++ << "].get<" << arg.print_type() << ">()") | |
} | |
ss << " case " + opcode + ":\n" | |
ss << " if(events->" + sanitise(name) + ") events->" + sanitise(name) + "(" | |
ss << _args.join(", ") << ");\n" | |
ss << " break;" | |
return ss.toString() | |
} | |
String print_signal_header() { | |
def ss = "" << "" | |
ss << " /** \\brief " << summary << "\n" | |
args.each {arg -> | |
ss << " \\param " << arg.name << " " << arg.summary << "\n" | |
} | |
ss << description << "\n" | |
ss << " */" << "\n" | |
def _args = [] | |
args.each { a -> _args.add(a.print_type()) } | |
ss << " std::function<void(" + _args.join(", ") << ")> &on_" << name << "();\n" | |
return ss.toString() | |
} | |
String print_signal_body(String iface_name) { | |
def ss = "" << "" | |
def _args = [] | |
args.each { arg-> _args.add(arg.print_type()) } | |
ss << "std::function<void(" << _args.join(", ") << ")> &" + iface_name + | |
"_t::on_" + name + "()\n{\n" | |
ss << " return std::static_pointer_cast<events_t>(get_events())->" + sanitise(name) + ";\n" | |
ss << "}\n\n" | |
return ss.toString() | |
} | |
String availability_function_name() { | |
return ((since > 1) ? "can_" + name : "") | |
} | |
String since_version_constant_name() { | |
return name + "_since_version" | |
} | |
String print_header() { | |
def ss = "" << "" | |
ss << " /** \\brief " << summary << "\n" | |
if (ret?.summary?.length() > 0) | |
ss << " \\return " << ret.summary << "\n" | |
args.each { arg -> | |
if (arg.type == "new_id") { | |
if (arg.iface.isEmpty()) { | |
ss << " \\param interface Interface to bind\n" | |
ss << " \\param version Interface version\n" | |
} | |
} else | |
ss << " \\param " << sanitise(arg.name) << " " << arg.summary << "\n" | |
} | |
ss << description << "\n" | |
ss << " */\n" | |
def _args = [] | |
args.each { arg -> | |
if(arg.type == "new_id") { | |
if(arg.iface.empty) | |
_args.add("proxy_t &interface, uint32_t version") | |
} | |
else | |
_args.add(arg.print_argument()) | |
} | |
_args.add("bool post = true") | |
ss << " void " << sanitise(name) << "(" << _args.join(", ") << ");\n" | |
ss << "\n" | |
ss << " /** \\brief Minimum protocol version required for the \\ref " << sanitise(name) << " function\n" | |
ss << " */\n" | |
ss << " static constexpr std::uint32_t " << since_version_constant_name() << " = " << since << ";\n" | |
if (!availability_function_name().isEmpty()) { | |
ss << "\n" | |
ss << " /** \\brief Check whether the \\ref " << name << " function is available with\n" | |
ss << " the currently bound version of the protocol\n" | |
ss << " */\n" | |
ss << " bool " << availability_function_name() << "() const;\n" | |
} | |
return ss.toString() | |
} | |
String print_body(String iface_name) { | |
def new_id_arg = false | |
def _args = [] | |
args.each { arg -> | |
if(arg.type == "new_id") { | |
if(arg.iface.empty) { | |
_args.add("proxy_t &interface, uint32_t version") | |
new_id_arg = true | |
} | |
} else | |
_args.add(arg.print_argument()) | |
} | |
_args.add("bool post") | |
def ss = "" << "" | |
ss << "void" << " " << iface_name << "_t::" << sanitise(name) << "(" | |
ss << _args.join(", ") << ")\n{\n" | |
_args = [] | |
args.each { arg -> | |
if(arg.type == "new_id") { | |
if(arg.iface.empty) | |
_args.add("std::string(interface.interface->name), version") | |
_args.add("nullptr") | |
} | |
else if(arg.type == "fd") | |
_args.add("argument_t::fd(" << sanitise(arg.name) << ")") | |
else if(arg.type == "object") | |
_args.add(sanitise(arg.name) << ".proxy_has_object() ? reinterpret_cast<wl_object*>(" << sanitise(arg.name) << ".c_ptr()) : nullptr") | |
else if(arg.enum_name != null && !arg.enum_name.empty) | |
_args.add("static_cast<" << arg.print_enum_wire_type() << ">(" << sanitise(arg.name) + ")") | |
else | |
_args.add(sanitise(arg.name)) | |
} | |
ss << " send_event(post, " << opcode << ((args.size > 0) ? ", " << _args.join(", ") : "") << ");\n" | |
ss << "}\n" | |
if (!availability_function_name().isEmpty()) { | |
ss << "\n" | |
ss << "bool " << iface_name << "_t::" << availability_function_name() << "() const\n" | |
ss << "{\n" | |
ss << " return (get_version() >= " << since_version_constant_name() << ");\n" | |
ss << "}\n" | |
} | |
return ss.toString() | |
} | |
} | |
class request_t extends event_t { | |
} | |
class enum_entry_t extends element_t { | |
String value | |
} | |
class enumeration_t extends element_t { | |
List<enum_entry_t> entries | |
Boolean bitfield = false | |
int id = 0 | |
int width = 0 | |
String print_forward(String iface_name) { | |
if (!bitfield) | |
return "enum class " << iface_name << "_" << name << " : uint32_t;\n" | |
return "struct " << iface_name << "_" << name << ";\n" | |
} | |
String print_header(String iface_name) { | |
def ss = "" << "" | |
ss << "/** \\brief " << summary << "\n" | |
ss << description << "\n" | |
ss << " */\n" | |
if(!bitfield) { | |
ss << "enum class " << iface_name << "_" << name << " : uint32_t\n" | |
ss << " {\n" | |
} else { | |
ss << "struct " << iface_name << "_" << name << " : public wayland::detail::bitfield<" << width << ", " << id << ">\n" | |
ss << "{\n" | |
ss << " " << iface_name << "_" << name << "(const wayland::detail::bitfield<" << width << ", " << id << "> &b)\n" | |
ss << " : wayland::detail::bitfield<" << width << ", " << id << ">(b) {}\n" | |
ss << " " << iface_name << "_" << name << "(const uint32_t value)\n" | |
ss << " : wayland::detail::bitfield<" << width << ", " << id << ">(value) {}\n" | |
} | |
entries.each { entry -> | |
if (!entry.summary.isEmpty()) | |
ss << " /** \\brief " << entry.summary << " */\n" | |
if (!bitfield) | |
ss << " " << sanitise(entry.name) << " = " << entry.value << ",\n" | |
else | |
ss << " static const wayland::detail::bitfield<" << width << ", " << id << "> " << sanitise(entry.name) << ";\n" | |
} | |
if (bitfield) | |
ss << "}\n" | |
ss.setLength(ss.length() - 2) | |
if (!bitfield) | |
ss << "\n" | |
ss << "};\n" | |
return ss.toString() | |
} | |
String print_body(String iface_name) { | |
def ss = "" << "" | |
if (bitfield) | |
entries.each { entry -> | |
ss << "const bitfield<" << width << ", " << id << "> " << iface_name << "_" | |
ss << name << "::" << sanitise(entry.name) << "{" << entry.value << "};\n" | |
} | |
return ss.toString() | |
} | |
} | |
class post_error_t extends element_t | |
{ | |
String print_server_header() { | |
def ss = "" << "" | |
ss << " /** \\brief Post error: " << summary << "\n" | |
if(description?.length() > 0) | |
ss << " " << description << "\n" | |
ss << " */\n" | |
ss << " void post_" << name << "(std::string const& msg);\n" | |
return ss.toString() | |
} | |
String print_server_body(String iface_name) { | |
def ss = "" << "" | |
ss << "void " << iface_name << "_t::post_" << name << "(std::string const& msg)\n" | |
ss << "{\n" | |
ss << " post_error(static_cast<uint32_t>(" << iface_name << "_error::" << sanitise(name) << "), msg);\n" | |
ss << "}\n" | |
return ss.toString() | |
} | |
} | |
class interface_t extends element_t { | |
int version = 0 | |
String orig_name | |
int destroy_opcode = 0 | |
List<request_t> requests | |
List<event_t> events | |
List<enumeration_t> enums | |
List<post_error_t> errors | |
String print_forward() { | |
def ss = "" << "" | |
ss << "class " << name << "_t;\n" | |
enums.each { e -> ss << e.print_forward(name) } | |
return ss.toString() | |
} | |
String print_c_forward() { | |
def ss = "" << "" | |
ss << "struct " << orig_name << ";\n" | |
return ss.toString() | |
} | |
String print_server_header() { | |
def ss = "" << "" | |
ss << "/** \\brief " << summary << "\n" | |
ss << description << "\n" | |
ss << "*/\n" | |
ss << "class " << name << "_t : public resource_t\n" | |
ss << "{\n" | |
ss << "private:\n" | |
ss << " struct events_t : public resource_t::events_base_t\n" | |
ss << " {\n" | |
requests.each { request -> ss << request.print_functional() << "\n" } | |
ss << " };\n" | |
ss << "\n" | |
ss << " static int dispatcher(int opcode, const std::vector<wayland::detail::any>& args, const std::shared_ptr<resource_t::events_base_t>& e);\n" | |
ss << "\n" | |
ss << "protected:\n" | |
ss << " static constexpr const wl_interface *interface = &wayland::server::detail::" << name << "_interface;\n" | |
ss << " static constexpr const unsigned int max_version = " << version << ";\n" | |
ss << "\n" | |
ss << " friend class global_t<" << name << "_t>;\n" | |
ss << " friend class global_base_t;\n" | |
ss << "\n" | |
ss << "public:\n" | |
ss << " " << name << "_t() = default;\n" | |
ss << " " << name << "_t(const client_t& client, uint32_t id, int version = " << version << ");\n" | |
ss << " " << name << "_t(const resource_t &resource);\n" | |
ss << "\n" | |
ss << " static const std::string interface_name;\n" | |
ss << "\n" | |
ss << " operator " << orig_name << "*() const;\n" | |
ss << "\n" | |
requests.each { request -> ss << request.print_signal_header() << "\n" } | |
events.each { event -> ss << event.print_header() << "\n" } | |
errors.each { error -> ss << error.print_server_header() << "\n" } | |
ss << "};\n" | |
ss << "\n" | |
ss << "using global_" << name << "_t = global_t<" << name << "_t>;\n" | |
ss << "\n" | |
enums.each { e -> ss << e.print_header(name) << "\n" } | |
return ss.toString() | |
} | |
String print_interface_header() { | |
return " extern const wl_interface " << name << "_interface;\n" | |
} | |
String print_server_body() { | |
def ss = "" << "" | |
ss << name << "_t::" << name << "_t(const client_t& client, uint32_t id, int version)\n" | |
ss << " : resource_t(client, &server::detail::" << name << "_interface, id, version)\n" | |
ss << "{\n" | |
ss << " set_events(std::shared_ptr<resource_t::events_base_t>(new events_t), dispatcher);\n" | |
ss << "}\n" | |
ss << "\n" | |
ss << name << "_t::" << name << "_t(const resource_t &resource)\n" | |
ss << " : resource_t(resource)\n" | |
ss << "{\n" | |
ss << " set_events(std::shared_ptr<resource_t::events_base_t>(new events_t), dispatcher);\n" | |
ss << "}\n" | |
ss << "\n" | |
ss << "const std::string " << name << "_t::interface_name = \"" << orig_name << "\";\n" | |
ss << "\n" | |
ss << name << "_t::operator " << orig_name << "*() const\n" | |
ss << "{\n" | |
ss << " return reinterpret_cast<" << orig_name << "*> (c_ptr());\n" | |
ss << "}\n" | |
ss << "\n" | |
requests.each { request -> ss << request.print_signal_body(name) << "\n" } | |
events.each { event -> ss << event.print_body(name) << "\n" } | |
errors.each { error -> ss << error.print_server_body(name) << "\n" } | |
ss << "int " << name << "_t::dispatcher(int opcode, const std::vector<any>& args, const std::shared_ptr<resource_t::events_base_t>& e)\n" | |
ss << "{\n" | |
if(!requests.empty) { | |
ss << " std::shared_ptr<events_t> events = std::static_pointer_cast<events_t>(e);\n" | |
ss << " switch(opcode)\n" | |
ss << " {\n" | |
int opcode = 0 | |
requests.each { request -> ss << request.print_dispatcher(opcode++) << "\n" } | |
ss << " }\n" | |
} | |
ss << " return 0;\n" | |
ss << "}\n" | |
enums.each { e -> ss << e.print_body(name) << "\n" } | |
return ss.toString() | |
} | |
String print_interface_body() { | |
def ss = "" << "" | |
requests.each { request -> | |
ss << "const wl_interface* " << name << "_interface_" << request.name << "_request_server" << "[" << request.args.size() << "] = {\n" | |
request.args.each { arg -> | |
if (!arg.iface.isEmpty()) | |
ss << " &" << arg.iface << "_interface,\n" | |
else | |
ss << " nullptr,\n" | |
} | |
ss << "};\n" | |
ss << "\n" | |
} | |
events.each { event -> | |
ss << "const wl_interface* " << name << "_interface_" << event.name << "_event_server" << "[" << event.args.size() << "] = {\n" | |
event.args.each { arg -> | |
if (!arg.iface.isEmpty()) | |
ss << " &" << arg.iface << "_interface,\n" | |
else | |
ss << " nullptr,\n" | |
} | |
ss << "};\n" | |
ss << "\n" | |
} | |
ss << "const wl_message " << name << "_interface_requests_server" << "[" << requests.size() << "] = {\n" | |
requests.each { request -> | |
ss << " {\n" | |
ss << " \"" << request.name << "\",\n" | |
ss << " \"" | |
if (request.since > 1) | |
ss << request.since | |
request.args.each { arg -> | |
if (arg.allow_null) | |
ss << "?" | |
if (arg.type == "new_id" && arg.iface.empty) | |
ss << "su" | |
ss << arg.print_short() | |
} | |
ss << "\",\n" | |
ss << " " << name << "_interface_" << request.name << "_request_server" << ",\n" | |
ss << " },\n" | |
} | |
ss << "};\n" | |
ss << "\n" | |
ss << "const wl_message " << name << "_interface_events_server" << "[" << events.size() << "] = {\n" | |
events.each { event -> | |
ss << " {\n" | |
ss << " \"" << event.name << "\",\n" | |
ss << " \"" | |
if(event.since > 1) | |
ss << event.since | |
event.args.each { arg -> | |
if(arg.allow_null) | |
ss << "?" | |
if(arg.type == "new_id" && arg.iface.empty) | |
ss << "su" | |
ss << arg.print_short() | |
} | |
ss << "\",\n" | |
ss << " " << name << "_interface_" << event.name << "_event_server" << ",\n" | |
ss << " },\n" | |
} | |
ss << "};\n" | |
ss << "\n" | |
ss << "const wl_interface wayland::server::detail::" << name << "_interface =\n" | |
ss << " {\n" | |
ss << " \"" << orig_name << "\",\n" | |
ss << " " << version << ",\n" | |
ss << " " << requests.size() << ",\n" | |
ss << " " << name << "_interface_requests_server" << ",\n" | |
ss << " " << events.size() << ",\n" | |
ss << " " << name << "_interface_events_server" << ",\n" | |
ss << " };\n" | |
ss << "\n" | |
return ss.toString() | |
} | |
} | |
static String unprefix(String name) | |
{ | |
def prefix_len = name.indexOf('_') | |
if(prefix_len != -1) { | |
def prefix = name.substring(0, prefix_len) | |
if(prefix == "wl" || prefix == "wp") | |
return name.substring(prefix_len+1, name.size()) | |
} | |
return name | |
} | |
boolean xmlValidate(File xmlFile, File schemaFile) { | |
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI) | |
try { | |
schemaFactory.newSchema(schemaFile) | |
.newValidator() | |
.validate(new StreamSource(xmlFile)) | |
return true | |
} catch (SAXException e) { | |
println(xmlFile.toString() + " is NOT valid reason:" + e) | |
return false | |
} catch (IOException ignored) { | |
return false | |
} | |
} | |
@Input | |
abstract Property<String> getFrom() | |
WaylandScannerTask() { | |
from.convention('') | |
groovy.xml.slurpersupport.NodeChild.metaClass.children << { name -> | |
return delegate.depthFirst().findAll { t -> t.name() == name } | |
} | |
} | |
def parseXml(File xmlFile) { | |
File schemaFile = project.file("wayland.xsd") | |
xmlValidate(xmlFile, schemaFile) | |
List<interface_t> ifaces = new ArrayList<interface_t>() | |
def protocol = new XmlSlurper().parseText(xmlFile.getText()) | |
int enum_id = 0 | |
protocol.children("interface").each { iface -> | |
interface_t i = new interface_t() | |
i.requests = new ArrayList<request_t>() | |
i.events = new ArrayList<event_t>() | |
i.enums = new ArrayList<enumeration_t>() | |
i.errors = new ArrayList<post_error_t>() | |
i.destroy_opcode = -1 | |
i.orig_name = iface.@name | |
i.name = unprefix([email protected]()) | |
if (iface.@version != null) | |
i.version = [email protected]() as int | |
else | |
i.version = 1 | |
if (iface.description) { | |
i.summary = iface.description.@summary | |
i.description = iface.description.text() | |
} | |
int opcode = 0 // Opcodes are in order of the XML. (Sadly undocumented) | |
iface.children("request").each { req -> | |
request_t r = new request_t() | |
r.name = req.@name | |
r.opcode = opcode++ | |
r.args = new ArrayList<argument_t>() | |
if (req?.@since?.text()?.length() > 0) | |
r.since = [email protected]() as int | |
else | |
r.since = 1 | |
if (req.description) { | |
r.summary = req.description.@summary | |
r.description = req.description.text() | |
} | |
// destruction takes place through the class destructor | |
if (req.@name == "destroy") | |
i.destroy_opcode = r.opcode | |
req.children("arg").each { arg -> | |
argument_t a = new argument_t() | |
a.name = arg.@name | |
a.type = arg.@type | |
a.summary = arg.@summary | |
a.iface = unprefix([email protected]()) | |
String enum_val = arg.@enum | |
if (!enum_val.isEmpty()) { | |
if (enum_val.indexOf('.') == -1) { | |
a.enum_iface = i.name | |
a.enum_name = enum_val | |
} else { | |
a.enum_iface = unprefix(enum_val.substring(0, enum_val.indexOf('.'))) | |
a.enum_name = enum_val.substring(enum_val.indexOf('.') + 1) | |
} | |
} | |
a.allow_null = arg.@"allow-null" == "true" | |
if (arg.@type == "new_id") | |
r.ret = a | |
r.args.add(a) | |
} | |
i.requests.add(r) | |
} | |
opcode = 0 | |
iface.children("event").each { ev -> | |
event_t e = new event_t() | |
e.name = ev.@name | |
e.opcode = opcode++ | |
e.args = new ArrayList<argument_t>() | |
if (ev?.@since?.text()?.length() > 0) | |
e.since = [email protected]() as int | |
else | |
e.since = 1 | |
if (ev.description) { | |
e.summary = ev.description.@summary | |
e.description = ev.description.text() | |
} | |
ev.children("arg").each { arg -> | |
argument_t a = new argument_t() | |
a.name = arg.@name | |
a.type = arg.@type | |
a.summary = arg.@summary | |
a.iface = unprefix([email protected]()) | |
String enum_val = [email protected]() | |
if (enum_val != null && !enum_val.isEmpty()) { | |
if (enum_val.indexOf('.') == -1) { | |
a.enum_iface = i.name | |
a.enum_name = enum_val | |
} else { | |
a.enum_iface = unprefix(enum_val.substring(0, enum_val.indexOf('.'))) | |
a.enum_name = enum_val.substring(enum_val.indexOf('.') + 1) | |
} | |
} | |
a.allow_null = arg.@"allow-null" == "true" | |
if (arg.@type == "new_id") | |
e.ret = a | |
e.args.add(a) | |
} | |
i.events.add(e) | |
} | |
iface.children("enum").each { en -> | |
enumeration_t e = new enumeration_t() | |
e.name = en.@name | |
e.bitfield = (en.@bitfield == "true") | |
e.id = enum_id++ | |
e.width = 0 | |
e.entries = new ArrayList<enum_entry_t>() | |
if (en.description) { | |
e.summary = en.description.@summary | |
e.description = en.description.text() | |
} | |
en.children("entry").each { entry -> | |
enum_entry_t ent = new enum_entry_t() | |
ent.name = entry.@name | |
if (ent.name != null && | |
(ent.name == "default" || Character.isDigit(ent.name.charAt(0)))) | |
ent.name = "_" + ent.name | |
ent.value = entry.@value | |
ent.summary = entry.@summary | |
Long val | |
if (ent.value.startsWith("0x")) | |
val = Long.parseLong(ent.value.replace("0x", ""), 16) | |
else | |
val = Long.parseLong(ent.value) | |
int tmp = Math.log(val)/Math.log(2) + 1 | |
if (e.width < tmp) | |
e.width = tmp | |
e.entries.add(ent) | |
if (e.name == "error") { | |
post_error_t error = new post_error_t() | |
error.name = ent.name | |
error.summary = ent.summary | |
error.description = ent.description | |
i.errors.add(error) | |
} | |
} | |
i.enums.add(e) | |
} | |
ifaces.add(i) | |
} | |
return ifaces | |
} | |
@TaskAction | |
def execute() { | |
if (from.get().isEmpty()) { | |
throw new Exception("from property is empty") | |
} | |
File xmlFile = project.file(from.get()) | |
File outFolder = new File(project.buildDir, "/intermediates/generated/wayland") | |
File wayland_hpp = new File(outFolder, "/wayland.hpp") | |
File wayland_cpp = new File(outFolder, "/wayland.cpp") | |
outFolder.mkdirs() | |
wayland_hpp.text = '' | |
wayland_cpp.text = '' | |
def ifaces = parseXml(xmlFile) | |
// header intro | |
wayland_hpp << "#pragma once\n" | |
wayland_hpp << "\n" | |
wayland_hpp << "#include <array>\n" | |
wayland_hpp << "#include <functional>\n" | |
wayland_hpp << "#include <memory>\n" | |
wayland_hpp << "#include <string>\n" | |
wayland_hpp << "#include <vector>\n" | |
wayland_hpp << "\n" | |
wayland_hpp << "#include <wayland-server.hpp>\n" | |
wayland_hpp << "\n" | |
// C forward declarations | |
ifaces.each {iface -> | |
if(iface.name != "display") | |
wayland_hpp << iface.print_c_forward() | |
} | |
wayland_hpp << "\n" | |
wayland_hpp << "namespace wayland\n" | |
wayland_hpp << "{\n" | |
wayland_hpp << "namespace server\n" | |
wayland_hpp << "{\n" | |
// C++ forward declarations | |
ifaces.each { iface -> | |
if(iface.name != "display") | |
wayland_hpp << iface.print_forward() | |
} | |
wayland_hpp << "\n" | |
// interface headers | |
wayland_hpp << "namespace detail\n" | |
wayland_hpp << "{\n" | |
ifaces.each { iface -> | |
wayland_hpp << iface.print_interface_header() | |
} | |
wayland_hpp << "}\n" | |
wayland_hpp << "\n" | |
// class declarations | |
ifaces.each { iface -> | |
if(iface.name != "display") { | |
wayland_hpp << iface.print_server_header() << "\n" | |
} | |
} | |
wayland_hpp << "\n" | |
wayland_hpp << "}\n" | |
wayland_hpp << "}\n" | |
// body intro | |
wayland_cpp << "#include <" << wayland_hpp.getName() << ">\n" | |
wayland_cpp << "\n" | |
wayland_cpp << "using namespace wayland;\n" | |
wayland_cpp << "using namespace wayland::detail;\n" | |
wayland_cpp << "using namespace wayland::server;\n" | |
wayland_cpp << "using namespace wayland::server::detail;\n" | |
wayland_cpp << "\n" | |
// interface bodies | |
ifaces.each { iface -> wayland_cpp << iface.print_interface_body() } | |
// class member definitions | |
ifaces.each { iface -> | |
if(iface.name != "display") { | |
wayland_cpp << iface.print_server_body() << "\n" | |
} | |
} | |
wayland_cpp << "\n" | |
} | |
} | |
tasks.register('scanner',WaylandScannerTask) { | |
from = 'wayland.xml' | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<!-- Do not edit. This document is automatically generated | |
from wayland.dtd using IntelliJ Idea's XML Actions... --> | |
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> | |
<xs:element name="protocol"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element minOccurs="0" ref="copyright"/> | |
<xs:element minOccurs="0" ref="description"/> | |
<xs:element maxOccurs="unbounded" ref="interface"/> | |
</xs:sequence> | |
<xs:attribute name="name" use="required"/> | |
</xs:complexType> | |
</xs:element> | |
<xs:element name="copyright" type="xs:string"/> | |
<xs:element name="interface"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element minOccurs="0" ref="description"/> | |
<xs:choice maxOccurs="unbounded"> | |
<xs:element ref="request"/> | |
<xs:element ref="event"/> | |
<xs:element ref="enum"/> | |
</xs:choice> | |
</xs:sequence> | |
<xs:attribute name="name" use="required"/> | |
<xs:attribute name="version" use="required"/> | |
</xs:complexType> | |
</xs:element> | |
<xs:element name="request"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element minOccurs="0" ref="description"/> | |
<xs:element minOccurs="0" maxOccurs="unbounded" ref="arg"/> | |
</xs:sequence> | |
<xs:attribute name="name" use="required"/> | |
<xs:attribute name="type"/> | |
<xs:attribute name="since"/> | |
</xs:complexType> | |
</xs:element> | |
<xs:element name="event"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element minOccurs="0" ref="description"/> | |
<xs:element minOccurs="0" maxOccurs="unbounded" ref="arg"/> | |
</xs:sequence> | |
<xs:attribute name="name" use="required"/> | |
<xs:attribute name="type"/> | |
<xs:attribute name="since"/> | |
</xs:complexType> | |
</xs:element> | |
<xs:element name="enum"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element minOccurs="0" ref="description"/> | |
<xs:element minOccurs="0" maxOccurs="unbounded" ref="entry"/> | |
</xs:sequence> | |
<xs:attribute name="name" use="required"/> | |
<xs:attribute name="since"/> | |
<xs:attribute name="bitfield"/> | |
</xs:complexType> | |
</xs:element> | |
<xs:element name="entry"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element minOccurs="0" ref="description"/> | |
</xs:sequence> | |
<xs:attribute name="name" use="required"/> | |
<xs:attribute name="value" use="required"/> | |
<xs:attribute name="summary"/> | |
<xs:attribute name="since"/> | |
</xs:complexType> | |
</xs:element> | |
<xs:element name="arg"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element minOccurs="0" ref="description"/> | |
</xs:sequence> | |
<xs:attribute name="name" use="required"/> | |
<xs:attribute name="type" use="required"/> | |
<xs:attribute name="summary"/> | |
<xs:attribute name="interface"/> | |
<xs:attribute name="allow-null"/> | |
<xs:attribute name="enum"/> | |
</xs:complexType> | |
</xs:element> | |
<xs:element name="description"> | |
<xs:complexType mixed="true"> | |
<xs:attribute name="summary" use="required"/> | |
</xs:complexType> | |
</xs:element> | |
</xs:schema> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment