// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include #include #include #include class Scanner { public: explicit Scanner() {} ~Scanner() { delete m_xml; } bool parseArguments(int argc, char **argv); void printUsage(); bool process(); void printErrors(); private: struct WaylandEnumEntry { QByteArray name; QByteArray value; QByteArray summary; }; struct WaylandEnum { QByteArray name; std::vector entries; }; struct WaylandArgument { QByteArray name; QByteArray type; QByteArray interface; QByteArray summary; bool allowNull; }; struct WaylandEvent { bool request; QByteArray name; QByteArray type; std::vector arguments; }; struct WaylandInterface { QByteArray name; int version; std::vector enums; std::vector events; std::vector requests; }; bool isServerSide(); bool parseOption(const QByteArray &str); QByteArray byteArrayValue(const QXmlStreamReader &xml, const char *name); int intValue(const QXmlStreamReader &xml, const char *name, int defaultValue = 0); bool boolValue(const QXmlStreamReader &xml, const char *name); WaylandEvent readEvent(QXmlStreamReader &xml, bool request); Scanner::WaylandEnum readEnum(QXmlStreamReader &xml); Scanner::WaylandInterface readInterface(QXmlStreamReader &xml); QByteArray waylandToCType(const QByteArray &waylandType, const QByteArray &interface); QByteArray waylandToQtType(const QByteArray &waylandType, const QByteArray &interface, bool cStyleArray); const Scanner::WaylandArgument *newIdArgument(const std::vector &arguments); void printEvent(const WaylandEvent &e, bool omitNames = false, bool withResource = false); void printEventHandlerSignature(const WaylandEvent &e, const char *interfaceName, bool deepIndent = true); void printEnums(const std::vector &enums); QByteArray stripInterfaceName(const QByteArray &name); bool ignoreInterface(const QByteArray &name); enum Option { ClientHeader, ServerHeader, ClientCode, ServerCode } m_option; QByteArray m_protocolName; QByteArray m_protocolFilePath; QByteArray m_scannerName; QByteArray m_headerPath; QByteArray m_prefix; QByteArray m_buildMacro; QList m_includes; QXmlStreamReader *m_xml = nullptr; }; bool Scanner::parseArguments(int argc, char **argv) { QList args; args.reserve(argc); for (int i = 0; i < argc; ++i) args << QByteArray(argv[i]); m_scannerName = args[0]; if (argc <= 2 || !parseOption(args[1])) return false; m_protocolFilePath = args[2]; if (argc > 3 && !args[3].startsWith('-')) { // legacy positional arguments m_headerPath = args[3]; if (argc == 5) m_prefix = args[4]; } else { // --header-path= (14 characters) // --prefix= (9 characters) // --add-include= (14 characters) for (int pos = 3; pos < argc; pos++) { const QByteArray &option = args[pos]; if (option.startsWith("--header-path=")) { m_headerPath = option.mid(14); } else if (option.startsWith("--prefix=")) { m_prefix = option.mid(10); } else if (option.startsWith("--build-macro=")) { m_buildMacro = option.mid(14); } else if (option.startsWith("--add-include=")) { auto include = option.mid(14); if (!include.isEmpty()) m_includes << include; } else { return false; } } } return true; } void Scanner::printUsage() { fprintf(stderr, "Usage: %s [client-header|server-header|client-code|server-code] specfile [--header-path=] [--prefix=] [--add-include=]\n", m_scannerName.constData()); } bool Scanner::isServerSide() { return m_option == ServerHeader || m_option == ServerCode; } bool Scanner::parseOption(const QByteArray &str) { if (str == "client-header") m_option = ClientHeader; else if (str == "server-header") m_option = ServerHeader; else if (str == "client-code") m_option = ClientCode; else if (str == "server-code") m_option = ServerCode; else return false; return true; } QByteArray Scanner::byteArrayValue(const QXmlStreamReader &xml, const char *name) { if (xml.attributes().hasAttribute(name)) return xml.attributes().value(name).toUtf8(); return QByteArray(); } int Scanner::intValue(const QXmlStreamReader &xml, const char *name, int defaultValue) { bool ok; int result = byteArrayValue(xml, name).toInt(&ok); return ok ? result : defaultValue; } bool Scanner::boolValue(const QXmlStreamReader &xml, const char *name) { return byteArrayValue(xml, name) == "true"; } Scanner::WaylandEvent Scanner::readEvent(QXmlStreamReader &xml, bool request) { WaylandEvent event = { .request = request, .name = byteArrayValue(xml, "name"), .type = byteArrayValue(xml, "type"), .arguments = {}, }; while (xml.readNextStartElement()) { if (xml.name() == u"arg") { WaylandArgument argument = { .name = byteArrayValue(xml, "name"), .type = byteArrayValue(xml, "type"), .interface = byteArrayValue(xml, "interface"), .summary = byteArrayValue(xml, "summary"), .allowNull = boolValue(xml, "allowNull"), }; event.arguments.push_back(std::move(argument)); } xml.skipCurrentElement(); } return event; } Scanner::WaylandEnum Scanner::readEnum(QXmlStreamReader &xml) { WaylandEnum result = { .name = byteArrayValue(xml, "name"), .entries = {}, }; while (xml.readNextStartElement()) { if (xml.name() == u"entry") { WaylandEnumEntry entry = { .name = byteArrayValue(xml, "name"), .value = byteArrayValue(xml, "value"), .summary = byteArrayValue(xml, "summary"), }; result.entries.push_back(std::move(entry)); } xml.skipCurrentElement(); } return result; } Scanner::WaylandInterface Scanner::readInterface(QXmlStreamReader &xml) { WaylandInterface interface = { .name = byteArrayValue(xml, "name"), .version = intValue(xml, "version", 1), .enums = {}, .events = {}, .requests = {}, }; while (xml.readNextStartElement()) { if (xml.name() == u"event") interface.events.push_back(readEvent(xml, false)); else if (xml.name() == u"request") interface.requests.push_back(readEvent(xml, true)); else if (xml.name() == u"enum") interface.enums.push_back(readEnum(xml)); else xml.skipCurrentElement(); } return interface; } QByteArray Scanner::waylandToCType(const QByteArray &waylandType, const QByteArray &interface) { if (waylandType == "string") return "const char *"; else if (waylandType == "int") return "int32_t"; else if (waylandType == "uint") return "uint32_t"; else if (waylandType == "fixed") return "wl_fixed_t"; else if (waylandType == "fd") return "int32_t"; else if (waylandType == "array") return "wl_array *"; else if (waylandType == "object" || waylandType == "new_id") { if (isServerSide()) return "struct ::wl_resource *"; if (interface.isEmpty()) return "struct ::wl_object *"; return "struct ::" + interface + " *"; } return waylandType; } QByteArray Scanner::waylandToQtType(const QByteArray &waylandType, const QByteArray &interface, bool cStyleArray) { if (waylandType == "string") return "const QString &"; else if (waylandType == "array") return cStyleArray ? "wl_array *" : "const QByteArray &"; else return waylandToCType(waylandType, interface); } const Scanner::WaylandArgument *Scanner::newIdArgument(const std::vector &arguments) { for (const WaylandArgument &a : arguments) { if (a.type == "new_id") return &a; } return nullptr; } void Scanner::printEvent(const WaylandEvent &e, bool omitNames, bool withResource) { printf("%s(", e.name.constData()); bool needsComma = false; if (isServerSide()) { if (e.request) { printf("Resource *%s", omitNames ? "" : "resource"); needsComma = true; } else if (withResource) { printf("struct ::wl_resource *%s", omitNames ? "" : "resource"); needsComma = true; } } for (const WaylandArgument &a : e.arguments) { bool isNewId = a.type == "new_id"; if (isNewId && !isServerSide() && (a.interface.isEmpty() != e.request)) continue; if (needsComma) printf(", "); needsComma = true; if (isNewId) { if (isServerSide()) { if (e.request) { printf("uint32_t"); if (!omitNames) printf(" %s", a.name.constData()); continue; } } else { if (e.request) { printf("const struct ::wl_interface *%s, uint32_t%s", omitNames ? "" : "interface", omitNames ? "" : " version"); continue; } } } QByteArray qtType = waylandToQtType(a.type, a.interface, e.request == isServerSide()); printf("%s%s%s", qtType.constData(), qtType.endsWith("&") || qtType.endsWith("*") ? "" : " ", omitNames ? "" : a.name.constData()); } printf(")"); } void Scanner::printEventHandlerSignature(const WaylandEvent &e, const char *interfaceName, bool deepIndent) { const char *indent = deepIndent ? " " : ""; printf("handle_%s(\n", e.name.constData()); if (isServerSide()) { printf(" %s::wl_client *client,\n", indent); printf(" %sstruct wl_resource *resource", indent); } else { printf(" %svoid *data,\n", indent); printf(" %sstruct ::%s *object", indent, interfaceName); } for (const WaylandArgument &a : e.arguments) { printf(",\n"); bool isNewId = a.type == "new_id"; if (isServerSide() && isNewId) { printf(" %suint32_t %s", indent, a.name.constData()); } else { QByteArray cType = waylandToCType(a.type, a.interface); printf(" %s%s%s%s", indent, cType.constData(), cType.endsWith("*") ? "" : " ", a.name.constData()); } } printf(")"); } void Scanner::printEnums(const std::vector &enums) { for (const WaylandEnum &e : enums) { printf("\n"); printf(" enum %s {\n", e.name.constData()); for (const WaylandEnumEntry &entry : e.entries) { printf(" %s_%s = %s,", e.name.constData(), entry.name.constData(), entry.value.constData()); if (!entry.summary.isNull()) printf(" // %s", entry.summary.constData()); printf("\n"); } printf(" };\n"); } } QByteArray Scanner::stripInterfaceName(const QByteArray &name) { if (!m_prefix.isEmpty() && name.startsWith(m_prefix)) return name.mid(m_prefix.size()); if (name.startsWith("qt_") || name.startsWith("wl_")) return name.mid(3); return name; } bool Scanner::ignoreInterface(const QByteArray &name) { return name == "wl_display" || (isServerSide() && name == "wl_registry"); } bool Scanner::process() { QFile file(m_protocolFilePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { fprintf(stderr, "Unable to open file %s\n", m_protocolFilePath.constData()); return false; } m_xml = new QXmlStreamReader(&file); if (!m_xml->readNextStartElement()) return false; if (m_xml->name() != u"protocol") { m_xml->raiseError(QStringLiteral("The file is not a wayland protocol file.")); return false; } m_protocolName = byteArrayValue(*m_xml, "name"); if (m_protocolName.isEmpty()) { m_xml->raiseError(QStringLiteral("Missing protocol name.")); return false; } //We should convert - to _ so that the preprocessor wont generate code which will lead to unexpected behavior //However, the wayland-scanner doesn't do so we will do the same for now //QByteArray preProcessorProtocolName = QByteArray(m_protocolName).replace('-', '_').toUpper(); QByteArray preProcessorProtocolName = QByteArray(m_protocolName).toUpper(); std::vector interfaces; while (m_xml->readNextStartElement()) { if (m_xml->name() == u"interface") interfaces.push_back(readInterface(*m_xml)); else m_xml->skipCurrentElement(); } if (m_xml->hasError()) return false; printf("// This file was generated by qtwaylandscanner\n"); printf("// source file is %s\n\n", qPrintable(m_protocolFilePath)); for (auto b : std::as_const(m_includes)) printf("#include %s\n", b.constData()); auto printExportMacro = [this](const char *prefix, const QByteArray &preProcessorProtocolName) { QByteArray exportMacro = prefix + preProcessorProtocolName + "_EXPORT"; printf("#if !defined(%s)\n", exportMacro.constData()); printf("# if defined(QT_SHARED) && !defined(QT_STATIC)\n"); if (m_buildMacro.isEmpty()) { printf("# define %s Q_DECL_EXPORT\n", exportMacro.constData()); } else { printf("# if defined(%s)\n", m_buildMacro.constData()); printf("# define %s Q_DECL_EXPORT\n", exportMacro.constData()); printf("# else\n"); printf("# define %s Q_DECL_IMPORT\n", exportMacro.constData()); printf("# endif\n"); } printf("# else\n"); printf("# define %s\n", exportMacro.constData()); printf("# endif\n"); printf("#endif\n"); return exportMacro; }; if (m_option == ServerHeader) { QByteArray inclusionGuard = QByteArray("QT_WAYLAND_SERVER_") + preProcessorProtocolName.constData(); printf("#ifndef %s\n", inclusionGuard.constData()); printf("#define %s\n", inclusionGuard.constData()); printf("\n"); printf("#include \"wayland-server-core.h\"\n"); if (m_headerPath.isEmpty()) printf("#include \"wayland-%s-server-protocol.h\"\n", QByteArray(m_protocolName).replace('_', '-').constData()); else printf("#include <%s/wayland-%s-server-protocol.h>\n", m_headerPath.constData(), QByteArray(m_protocolName).replace('_', '-').constData()); printf("#include \n"); printf("#include \n"); printf("#include \n"); printf("\n"); printf("#ifndef WAYLAND_VERSION_CHECK\n"); printf("#define WAYLAND_VERSION_CHECK(major, minor, micro) \\\n"); printf(" ((WAYLAND_VERSION_MAJOR > (major)) || \\\n"); printf(" (WAYLAND_VERSION_MAJOR == (major) && WAYLAND_VERSION_MINOR > (minor)) || \\\n"); printf(" (WAYLAND_VERSION_MAJOR == (major) && WAYLAND_VERSION_MINOR == (minor) && WAYLAND_VERSION_MICRO >= (micro)))\n"); printf("#endif\n"); printf("\n"); printf("QT_BEGIN_NAMESPACE\n"); printf("QT_WARNING_PUSH\n"); printf("QT_WARNING_DISABLE_GCC(\"-Wmissing-field-initializers\")\n"); printf("QT_WARNING_DISABLE_CLANG(\"-Wmissing-field-initializers\")\n"); QByteArray serverExport; if (m_headerPath.size()) serverExport = printExportMacro("Q_WAYLAND_SERVER_", preProcessorProtocolName); printf("\n"); printf("namespace QtWaylandServer {\n"); bool needsNewLine = false; for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; if (needsNewLine) printf("\n"); needsNewLine = true; const char *interfaceName = interface.name.constData(); QByteArray stripped = stripInterfaceName(interface.name); const char *interfaceNameStripped = stripped.constData(); printf(" class %s %s\n {\n", serverExport.constData(), interfaceName); printf(" public:\n"); printf(" %s(struct ::wl_client *client, uint32_t id, int version);\n", interfaceName); printf(" %s(struct ::wl_display *display, int version);\n", interfaceName); printf(" %s(struct ::wl_resource *resource);\n", interfaceName); printf(" %s();\n", interfaceName); printf("\n"); printf(" virtual ~%s();\n", interfaceName); printf("\n"); printf(" class Resource\n"); printf(" {\n"); printf(" public:\n"); printf(" Resource() : %s_object(nullptr), handle(nullptr) {}\n", interfaceNameStripped); printf(" virtual ~Resource() {}\n"); printf("\n"); printf(" %s *%s_object;\n", interfaceName, interfaceNameStripped); printf(" %s *object() { return %s_object; } \n", interfaceName, interfaceNameStripped); printf(" struct ::wl_resource *handle;\n"); printf("\n"); printf(" struct ::wl_client *client() const { return wl_resource_get_client(handle); }\n"); printf(" int version() const { return wl_resource_get_version(handle); }\n"); printf("\n"); printf(" static Resource *fromResource(struct ::wl_resource *resource);\n"); printf(" };\n"); printf("\n"); printf(" void init(struct ::wl_client *client, uint32_t id, int version);\n"); printf(" void init(struct ::wl_display *display, int version);\n"); printf(" void init(struct ::wl_resource *resource);\n"); printf("\n"); printf(" Resource *add(struct ::wl_client *client, int version);\n"); printf(" Resource *add(struct ::wl_client *client, uint32_t id, int version);\n"); printf(" Resource *add(struct wl_list *resource_list, struct ::wl_client *client, uint32_t id, int version);\n"); printf("\n"); printf(" Resource *resource() { return m_resource; }\n"); printf(" const Resource *resource() const { return m_resource; }\n"); printf("\n"); printf(" QMultiMap resourceMap() { return m_resource_map; }\n"); printf(" const QMultiMap resourceMap() const { return m_resource_map; }\n"); printf("\n"); printf(" bool isGlobal() const { return m_global != nullptr; }\n"); printf(" bool isResource() const { return m_resource != nullptr; }\n"); printf("\n"); printf(" static const struct ::wl_interface *interface();\n"); printf(" static QByteArray interfaceName() { return interface()->name; }\n"); printf(" static int interfaceVersion() { return interface()->version; }\n"); printf("\n"); printEnums(interface.enums); bool hasEvents = !interface.events.empty(); if (hasEvents) { printf("\n"); for (const WaylandEvent &e : interface.events) { printf(" void send_"); printEvent(e); printf(";\n"); printf(" void send_"); printEvent(e, false, true); printf(";\n"); } } printf("\n"); printf(" protected:\n"); printf(" virtual Resource *%s_allocate();\n", interfaceNameStripped); printf("\n"); printf(" virtual void %s_bind_resource(Resource *resource);\n", interfaceNameStripped); printf(" virtual void %s_destroy_resource(Resource *resource);\n", interfaceNameStripped); bool hasRequests = !interface.requests.empty(); if (hasRequests) { printf("\n"); for (const WaylandEvent &e : interface.requests) { printf(" virtual void %s_", interfaceNameStripped); printEvent(e); printf(";\n"); } } printf("\n"); printf(" private:\n"); printf(" static void bind_func(struct ::wl_client *client, void *data, uint32_t version, uint32_t id);\n"); printf(" static void destroy_func(struct ::wl_resource *client_resource);\n"); printf(" static void display_destroy_func(struct ::wl_listener *listener, void *data);\n"); printf("\n"); printf(" Resource *bind(struct ::wl_client *client, uint32_t id, int version);\n"); printf(" Resource *bind(struct ::wl_resource *handle);\n"); if (hasRequests) { printf("\n"); printf(" static const struct ::%s_interface m_%s_interface;\n", interfaceName, interfaceName); printf("\n"); for (const WaylandEvent &e : interface.requests) { printf(" static void "); printEventHandlerSignature(e, interfaceName); printf(";\n"); } } printf("\n"); printf(" QMultiMap m_resource_map;\n"); printf(" Resource *m_resource;\n"); printf(" struct ::wl_global *m_global;\n"); printf(" struct DisplayDestroyedListener : ::wl_listener {\n"); printf(" %s *parent;\n", interfaceName); printf(" };\n"); printf(" DisplayDestroyedListener m_displayDestroyedListener;\n"); printf(" };\n"); } printf("}\n"); printf("\n"); printf("QT_WARNING_POP\n"); printf("QT_END_NAMESPACE\n"); printf("\n"); printf("#endif\n"); } if (m_option == ServerCode) { if (m_headerPath.isEmpty()) printf("#include \"qwayland-server-%s.h\"\n", QByteArray(m_protocolName).replace('_', '-').constData()); else printf("#include <%s/qwayland-server-%s.h>\n", m_headerPath.constData(), QByteArray(m_protocolName).replace('_', '-').constData()); printf("\n"); printf("QT_BEGIN_NAMESPACE\n"); printf("QT_WARNING_PUSH\n"); printf("QT_WARNING_DISABLE_GCC(\"-Wmissing-field-initializers\")\n"); printf("QT_WARNING_DISABLE_CLANG(\"-Wmissing-field-initializers\")\n"); printf("\n"); printf("namespace QtWaylandServer {\n"); bool needsNewLine = false; for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; if (needsNewLine) printf("\n"); needsNewLine = true; const char *interfaceName = interface.name.constData(); QByteArray stripped = stripInterfaceName(interface.name); const char *interfaceNameStripped = stripped.constData(); printf(" %s::%s(struct ::wl_client *client, uint32_t id, int version)\n", interfaceName, interfaceName); printf(" : m_resource_map()\n"); printf(" , m_resource(nullptr)\n"); printf(" , m_global(nullptr)\n"); printf(" {\n"); printf(" init(client, id, version);\n"); printf(" }\n"); printf("\n"); printf(" %s::%s(struct ::wl_display *display, int version)\n", interfaceName, interfaceName); printf(" : m_resource_map()\n"); printf(" , m_resource(nullptr)\n"); printf(" , m_global(nullptr)\n"); printf(" {\n"); printf(" init(display, version);\n"); printf(" }\n"); printf("\n"); printf(" %s::%s(struct ::wl_resource *resource)\n", interfaceName, interfaceName); printf(" : m_resource_map()\n"); printf(" , m_resource(nullptr)\n"); printf(" , m_global(nullptr)\n"); printf(" {\n"); printf(" init(resource);\n"); printf(" }\n"); printf("\n"); printf(" %s::%s()\n", interfaceName, interfaceName); printf(" : m_resource_map()\n"); printf(" , m_resource(nullptr)\n"); printf(" , m_global(nullptr)\n"); printf(" {\n"); printf(" }\n"); printf("\n"); printf(" %s::~%s()\n", interfaceName, interfaceName); printf(" {\n"); printf(" for (auto resource : std::as_const(m_resource_map))\n"); printf(" resource->%s_object = nullptr;\n", interfaceNameStripped); printf("\n"); printf(" if (m_resource)\n"); printf(" m_resource->%s_object = nullptr;\n", interfaceNameStripped); printf("\n"); printf(" if (m_global) {\n"); printf(" wl_global_destroy(m_global);\n"); printf(" wl_list_remove(&m_displayDestroyedListener.link);\n"); printf(" }\n"); printf(" }\n"); printf("\n"); printf(" void %s::init(struct ::wl_client *client, uint32_t id, int version)\n", interfaceName); printf(" {\n"); printf(" m_resource = bind(client, id, version);\n"); printf(" }\n"); printf("\n"); printf(" void %s::init(struct ::wl_resource *resource)\n", interfaceName); printf(" {\n"); printf(" m_resource = bind(resource);\n"); printf(" }\n"); printf("\n"); printf(" %s::Resource *%s::add(struct ::wl_client *client, int version)\n", interfaceName, interfaceName); printf(" {\n"); printf(" Resource *resource = bind(client, 0, version);\n"); printf(" m_resource_map.insert(client, resource);\n"); printf(" return resource;\n"); printf(" }\n"); printf("\n"); printf(" %s::Resource *%s::add(struct ::wl_client *client, uint32_t id, int version)\n", interfaceName, interfaceName); printf(" {\n"); printf(" Resource *resource = bind(client, id, version);\n"); printf(" m_resource_map.insert(client, resource);\n"); printf(" return resource;\n"); printf(" }\n"); printf("\n"); printf(" void %s::init(struct ::wl_display *display, int version)\n", interfaceName); printf(" {\n"); printf(" m_global = wl_global_create(display, &::%s_interface, version, this, bind_func);\n", interfaceName); printf(" m_displayDestroyedListener.notify = %s::display_destroy_func;\n", interfaceName); printf(" m_displayDestroyedListener.parent = this;\n"); printf(" wl_display_add_destroy_listener(display, &m_displayDestroyedListener);\n"); printf(" }\n"); printf("\n"); printf(" const struct wl_interface *%s::interface()\n", interfaceName); printf(" {\n"); printf(" return &::%s_interface;\n", interfaceName); printf(" }\n"); printf("\n"); printf(" %s::Resource *%s::%s_allocate()\n", interfaceName, interfaceName, interfaceNameStripped); printf(" {\n"); printf(" return new Resource;\n"); printf(" }\n"); printf("\n"); printf(" void %s::%s_bind_resource(Resource *)\n", interfaceName, interfaceNameStripped); printf(" {\n"); printf(" }\n"); printf("\n"); printf(" void %s::%s_destroy_resource(Resource *)\n", interfaceName, interfaceNameStripped); printf(" {\n"); printf(" }\n"); printf("\n"); printf(" void %s::bind_func(struct ::wl_client *client, void *data, uint32_t version, uint32_t id)\n", interfaceName); printf(" {\n"); printf(" %s *that = static_cast<%s *>(data);\n", interfaceName, interfaceName); printf(" that->add(client, id, version);\n"); printf(" }\n"); printf("\n"); printf(" void %s::display_destroy_func(struct ::wl_listener *listener, void *data)\n", interfaceName); printf(" {\n"); printf(" Q_UNUSED(data);\n"); printf(" %s *that = static_cast<%s::DisplayDestroyedListener *>(listener)->parent;\n", interfaceName, interfaceName); printf(" that->m_global = nullptr;\n"); printf(" }\n"); printf("\n"); printf(" void %s::destroy_func(struct ::wl_resource *client_resource)\n", interfaceName); printf(" {\n"); printf(" Resource *resource = Resource::fromResource(client_resource);\n"); printf(" Q_ASSERT(resource);\n"); printf(" %s *that = resource->%s_object;\n", interfaceName, interfaceNameStripped); printf(" if (Q_LIKELY(that)) {\n"); printf(" that->m_resource_map.remove(resource->client(), resource);\n"); printf(" that->%s_destroy_resource(resource);\n", interfaceNameStripped); printf("\n"); printf(" that = resource->%s_object;\n", interfaceNameStripped); printf(" if (that && that->m_resource == resource)\n"); printf(" that->m_resource = nullptr;\n"); printf(" }\n"); printf(" delete resource;\n"); printf(" }\n"); printf("\n"); bool hasRequests = !interface.requests.empty(); QByteArray interfaceMember = hasRequests ? "&m_" + interface.name + "_interface" : QByteArray("nullptr"); //We should consider changing bind so that it doesn't special case id == 0 //and use function overloading instead. Jan do you have a lot of code dependent on this behavior? printf(" %s::Resource *%s::bind(struct ::wl_client *client, uint32_t id, int version)\n", interfaceName, interfaceName); printf(" {\n"); printf(" Q_ASSERT_X(!wl_client_get_object(client, id), \"QWaylandObject bind\", QStringLiteral(\"binding to object %%1 more than once\").arg(id).toLocal8Bit().constData());\n"); printf(" struct ::wl_resource *handle = wl_resource_create(client, &::%s_interface, version, id);\n", interfaceName); printf(" return bind(handle);\n"); printf(" }\n"); printf("\n"); printf(" %s::Resource *%s::bind(struct ::wl_resource *handle)\n", interfaceName, interfaceName); printf(" {\n"); printf(" Resource *resource = %s_allocate();\n", interfaceNameStripped); printf(" resource->%s_object = this;\n", interfaceNameStripped); printf("\n"); printf(" wl_resource_set_implementation(handle, %s, resource, destroy_func);", interfaceMember.constData()); printf("\n"); printf(" resource->handle = handle;\n"); printf(" %s_bind_resource(resource);\n", interfaceNameStripped); printf(" return resource;\n"); printf(" }\n"); printf(" %s::Resource *%s::Resource::fromResource(struct ::wl_resource *resource)\n", interfaceName, interfaceName); printf(" {\n"); printf(" if (Q_UNLIKELY(!resource))\n"); printf(" return nullptr;\n"); printf(" if (wl_resource_instance_of(resource, &::%s_interface, %s))\n", interfaceName, interfaceMember.constData()); printf(" return static_cast(wl_resource_get_user_data(resource));\n"); printf(" return nullptr;\n"); printf(" }\n"); if (hasRequests) { printf("\n"); printf(" const struct ::%s_interface %s::m_%s_interface = {", interfaceName, interfaceName, interfaceName); bool needsComma = false; for (const WaylandEvent &e : interface.requests) { if (needsComma) printf(","); needsComma = true; printf("\n"); printf(" %s::handle_%s", interfaceName, e.name.constData()); } printf("\n"); printf(" };\n"); for (const WaylandEvent &e : interface.requests) { printf("\n"); printf(" void %s::%s_", interfaceName, interfaceNameStripped); printEvent(e, true); printf("\n"); printf(" {\n"); printf(" }\n"); } printf("\n"); for (const WaylandEvent &e : interface.requests) { printf("\n"); printf(" void %s::", interfaceName); printEventHandlerSignature(e, interfaceName, false); printf("\n"); printf(" {\n"); printf(" Q_UNUSED(client);\n"); printf(" Resource *r = Resource::fromResource(resource);\n"); printf(" if (Q_UNLIKELY(!r->%s_object)) {\n", interfaceNameStripped); if (e.type == "destructor") printf(" wl_resource_destroy(resource);\n"); printf(" return;\n"); printf(" }\n"); printf(" static_cast<%s *>(r->%s_object)->%s_%s(\n", interfaceName, interfaceNameStripped, interfaceNameStripped, e.name.constData()); printf(" r"); for (const WaylandArgument &a : e.arguments) { printf(",\n"); QByteArray cType = waylandToCType(a.type, a.interface); QByteArray qtType = waylandToQtType(a.type, a.interface, e.request); const char *argumentName = a.name.constData(); if (cType == qtType) printf(" %s", argumentName); else if (a.type == "string") printf(" QString::fromUtf8(%s)", argumentName); } printf(");\n"); printf(" }\n"); } } for (const WaylandEvent &e : interface.events) { printf("\n"); printf(" void %s::send_", interfaceName); printEvent(e); printf("\n"); printf(" {\n"); printf(" Q_ASSERT_X(m_resource, \"%s::%s\", \"Uninitialised resource\");\n", interfaceName, e.name.constData()); printf(" if (Q_UNLIKELY(!m_resource)) {\n"); printf(" qWarning(\"could not call %s::%s as it's not initialised\");\n", interfaceName, e.name.constData()); printf(" return;\n"); printf(" }\n"); printf(" send_%s(\n", e.name.constData()); printf(" m_resource->handle"); for (const WaylandArgument &a : e.arguments) { printf(",\n"); printf(" %s", a.name.constData()); } printf(");\n"); printf(" }\n"); printf("\n"); printf(" void %s::send_", interfaceName); printEvent(e, false, true); printf("\n"); printf(" {\n"); for (const WaylandArgument &a : e.arguments) { if (a.type != "array") continue; QByteArray array = a.name + "_data"; const char *arrayName = array.constData(); const char *variableName = a.name.constData(); printf(" struct wl_array %s;\n", arrayName); printf(" %s.size = %s.size();\n", arrayName, variableName); printf(" %s.data = static_cast(const_cast(%s.constData()));\n", arrayName, variableName); printf(" %s.alloc = 0;\n", arrayName); printf("\n"); } printf(" %s_send_%s(\n", interfaceName, e.name.constData()); printf(" resource"); for (const WaylandArgument &a : e.arguments) { printf(",\n"); QByteArray cType = waylandToCType(a.type, a.interface); QByteArray qtType = waylandToQtType(a.type, a.interface, e.request); if (a.type == "string") printf(" %s.toUtf8().constData()", a.name.constData()); else if (a.type == "array") printf(" &%s_data", a.name.constData()); else if (cType == qtType) printf(" %s", a.name.constData()); } printf(");\n"); printf(" }\n"); printf("\n"); } } printf("}\n"); printf("\n"); printf("QT_WARNING_POP\n"); printf("QT_END_NAMESPACE\n"); } if (m_option == ClientHeader) { QByteArray inclusionGuard = QByteArray("QT_WAYLAND_") + preProcessorProtocolName.constData(); printf("#ifndef %s\n", inclusionGuard.constData()); printf("#define %s\n", inclusionGuard.constData()); printf("\n"); if (m_headerPath.isEmpty()) printf("#include \"wayland-%s-client-protocol.h\"\n", QByteArray(m_protocolName).replace('_', '-').constData()); else printf("#include <%s/wayland-%s-client-protocol.h>\n", m_headerPath.constData(), QByteArray(m_protocolName).replace('_', '-').constData()); printf("#include \n"); printf("#include \n"); printf("\n"); printf("struct wl_registry;\n"); printf("\n"); printf("QT_BEGIN_NAMESPACE\n"); printf("QT_WARNING_PUSH\n"); printf("QT_WARNING_DISABLE_GCC(\"-Wmissing-field-initializers\")\n"); printf("QT_WARNING_DISABLE_CLANG(\"-Wmissing-field-initializers\")\n"); QByteArray clientExport; if (m_headerPath.size()) clientExport = printExportMacro("Q_WAYLAND_CLIENT_", preProcessorProtocolName); printf("\n"); printf("namespace QtWayland {\n"); bool needsNewLine = false; for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; if (needsNewLine) printf("\n"); needsNewLine = true; const char *interfaceName = interface.name.constData(); QByteArray stripped = stripInterfaceName(interface.name); const char *interfaceNameStripped = stripped.constData(); printf(" class %s %s\n {\n", clientExport.constData(), interfaceName); printf(" public:\n"); printf(" %s(struct ::wl_registry *registry, uint32_t id, int version);\n", interfaceName); printf(" %s(struct ::%s *object);\n", interfaceName, interfaceName); printf(" %s();\n", interfaceName); printf("\n"); printf(" virtual ~%s();\n", interfaceName); printf("\n"); printf(" void init(struct ::wl_registry *registry, uint32_t id, int version);\n"); printf(" void init(struct ::%s *object);\n", interfaceName); printf("\n"); printf(" struct ::%s *object() { return m_%s; }\n", interfaceName, interfaceName); printf(" const struct ::%s *object() const { return m_%s; }\n", interfaceName, interfaceName); printf(" static %s *fromObject(struct ::%s *object);\n", interfaceName, interfaceName); printf("\n"); printf(" bool isInitialized() const;\n"); printf("\n"); printf(" uint32_t version() const;"); printf("\n"); printf(" static const struct ::wl_interface *interface();\n"); printEnums(interface.enums); if (!interface.requests.empty()) { printf("\n"); for (const WaylandEvent &e : interface.requests) { const WaylandArgument *new_id = newIdArgument(e.arguments); QByteArray new_id_str = "void "; if (new_id) { if (new_id->interface.isEmpty()) new_id_str = "void *"; else new_id_str = "struct ::" + new_id->interface + " *"; } printf(" %s", new_id_str.constData()); printEvent(e); printf(";\n"); } } bool hasEvents = !interface.events.empty(); if (hasEvents) { printf("\n"); printf(" protected:\n"); for (const WaylandEvent &e : interface.events) { printf(" virtual void %s_", interfaceNameStripped); printEvent(e); printf(";\n"); } } printf("\n"); printf(" private:\n"); if (hasEvents) { printf(" void init_listener();\n"); printf(" static const struct %s_listener m_%s_listener;\n", interfaceName, interfaceName); for (const WaylandEvent &e : interface.events) { printf(" static void "); printEventHandlerSignature(e, interfaceName); printf(";\n"); } } printf(" struct ::%s *m_%s;\n", interfaceName, interfaceName); printf(" };\n"); } printf("}\n"); printf("\n"); printf("QT_WARNING_POP\n"); printf("QT_END_NAMESPACE\n"); printf("\n"); printf("#endif\n"); } if (m_option == ClientCode) { if (m_headerPath.isEmpty()) printf("#include \"qwayland-%s.h\"\n", QByteArray(m_protocolName).replace('_', '-').constData()); else printf("#include <%s/qwayland-%s.h>\n", m_headerPath.constData(), QByteArray(m_protocolName).replace('_', '-').constData()); printf("\n"); printf("QT_BEGIN_NAMESPACE\n"); printf("QT_WARNING_PUSH\n"); printf("QT_WARNING_DISABLE_GCC(\"-Wmissing-field-initializers\")\n"); printf("QT_WARNING_DISABLE_CLANG(\"-Wmissing-field-initializers\")\n"); printf("\n"); printf("namespace QtWayland {\n"); printf("\n"); // wl_registry_bind is part of the protocol, so we can't use that... instead we use core // libwayland API to do the same thing a wayland-scanner generated wl_registry_bind would. printf("static inline void *wlRegistryBind(struct ::wl_registry *registry, uint32_t name, const struct ::wl_interface *interface, uint32_t version)\n"); printf("{\n"); printf(" const uint32_t bindOpCode = 0;\n"); printf(" return (void *) wl_proxy_marshal_constructor_versioned((struct wl_proxy *) registry,\n"); printf(" bindOpCode, interface, version, name, interface->name, version, nullptr);\n"); printf("}\n"); printf("\n"); bool needsNewLine = false; for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; if (needsNewLine) printf("\n"); needsNewLine = true; const char *interfaceName = interface.name.constData(); QByteArray stripped = stripInterfaceName(interface.name); const char *interfaceNameStripped = stripped.constData(); bool hasEvents = !interface.events.empty(); printf(" %s::%s(struct ::wl_registry *registry, uint32_t id, int version)\n", interfaceName, interfaceName); printf(" {\n"); printf(" init(registry, id, version);\n"); printf(" }\n"); printf("\n"); printf(" %s::%s(struct ::%s *obj)\n", interfaceName, interfaceName, interfaceName); printf(" : m_%s(obj)\n", interfaceName); printf(" {\n"); if (hasEvents) printf(" init_listener();\n"); printf(" }\n"); printf("\n"); printf(" %s::%s()\n", interfaceName, interfaceName); printf(" : m_%s(nullptr)\n", interfaceName); printf(" {\n"); printf(" }\n"); printf("\n"); printf(" %s::~%s()\n", interfaceName, interfaceName); printf(" {\n"); printf(" }\n"); printf("\n"); printf(" void %s::init(struct ::wl_registry *registry, uint32_t id, int version)\n", interfaceName); printf(" {\n"); printf(" m_%s = static_cast(wlRegistryBind(registry, id, &%s_interface, version));\n", interfaceName, interfaceName, interfaceName); if (hasEvents) printf(" init_listener();\n"); printf(" }\n"); printf("\n"); printf(" void %s::init(struct ::%s *obj)\n", interfaceName, interfaceName); printf(" {\n"); printf(" m_%s = obj;\n", interfaceName); if (hasEvents) printf(" init_listener();\n"); printf(" }\n"); printf("\n"); printf(" %s *%s::fromObject(struct ::%s *object)\n", interfaceName, interfaceName, interfaceName); printf(" {\n"); if (hasEvents) { printf(" if (wl_proxy_get_listener((struct ::wl_proxy *)object) != (void *)&m_%s_listener)\n", interfaceName); printf(" return nullptr;\n"); } printf(" return static_cast<%s *>(%s_get_user_data(object));\n", interfaceName, interfaceName); printf(" }\n"); printf("\n"); printf(" bool %s::isInitialized() const\n", interfaceName); printf(" {\n"); printf(" return m_%s != nullptr;\n", interfaceName); printf(" }\n"); printf("\n"); printf(" uint32_t %s::version() const\n", interfaceName); printf(" {\n"); printf(" return wl_proxy_get_version(reinterpret_cast(m_%s));\n", interfaceName); printf(" }\n"); printf("\n"); printf(" const struct wl_interface *%s::interface()\n", interfaceName); printf(" {\n"); printf(" return &::%s_interface;\n", interfaceName); printf(" }\n"); for (const WaylandEvent &e : interface.requests) { printf("\n"); const WaylandArgument *new_id = newIdArgument(e.arguments); QByteArray new_id_str = "void "; if (new_id) { if (new_id->interface.isEmpty()) new_id_str = "void *"; else new_id_str = "struct ::" + new_id->interface + " *"; } printf(" %s%s::", new_id_str.constData(), interfaceName); printEvent(e); printf("\n"); printf(" {\n"); for (const WaylandArgument &a : e.arguments) { if (a.type != "array") continue; QByteArray array = a.name + "_data"; const char *arrayName = array.constData(); const char *variableName = a.name.constData(); printf(" struct wl_array %s;\n", arrayName); printf(" %s.size = %s.size();\n", arrayName, variableName); printf(" %s.data = static_cast(const_cast(%s.constData()));\n", arrayName, variableName); printf(" %s.alloc = 0;\n", arrayName); printf("\n"); } int actualArgumentCount = new_id ? int(e.arguments.size()) - 1 : int(e.arguments.size()); printf(" %s::%s_%s(\n", new_id ? "return " : "", interfaceName, e.name.constData()); printf(" m_%s%s", interfaceName, actualArgumentCount > 0 ? "," : ""); bool needsComma = false; for (const WaylandArgument &a : e.arguments) { bool isNewId = a.type == "new_id"; if (isNewId && !a.interface.isEmpty()) continue; if (needsComma) printf(","); needsComma = true; printf("\n"); if (isNewId) { printf(" interface,\n"); printf(" version"); } else { QByteArray cType = waylandToCType(a.type, a.interface); QByteArray qtType = waylandToQtType(a.type, a.interface, e.request); if (a.type == "string") printf(" %s.toUtf8().constData()", a.name.constData()); else if (a.type == "array") printf(" &%s_data", a.name.constData()); else if (cType == qtType) printf(" %s", a.name.constData()); } } printf(");\n"); if (e.type == "destructor") printf(" m_%s = nullptr;\n", interfaceName); printf(" }\n"); } if (hasEvents) { printf("\n"); for (const WaylandEvent &e : interface.events) { printf(" void %s::%s_", interfaceName, interfaceNameStripped); printEvent(e, true); printf("\n"); printf(" {\n"); printf(" }\n"); printf("\n"); printf(" void %s::", interfaceName); printEventHandlerSignature(e, interfaceName, false); printf("\n"); printf(" {\n"); printf(" Q_UNUSED(object);\n"); printf(" static_cast<%s *>(data)->%s_%s(", interfaceName, interfaceNameStripped, e.name.constData()); bool needsComma = false; for (const WaylandArgument &a : e.arguments) { if (needsComma) printf(","); needsComma = true; printf("\n"); const char *argumentName = a.name.constData(); if (a.type == "string") printf(" QString::fromUtf8(%s)", argumentName); else printf(" %s", argumentName); } printf(");\n"); printf(" }\n"); printf("\n"); } printf(" const struct %s_listener %s::m_%s_listener = {\n", interfaceName, interfaceName, interfaceName); for (const WaylandEvent &e : interface.events) { printf(" %s::handle_%s,\n", interfaceName, e.name.constData()); } printf(" };\n"); printf("\n"); printf(" void %s::init_listener()\n", interfaceName); printf(" {\n"); printf(" %s_add_listener(m_%s, &m_%s_listener, this);\n", interfaceName, interfaceName, interfaceName); printf(" }\n"); } } printf("}\n"); printf("\n"); printf("QT_WARNING_POP\n"); printf("QT_END_NAMESPACE\n"); } return true; } void Scanner::printErrors() { if (m_xml->hasError()) fprintf(stderr, "XML error: %s\nLine %lld, column %lld\n", m_xml->errorString().toLocal8Bit().constData(), m_xml->lineNumber(), m_xml->columnNumber()); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); Scanner scanner; if (!scanner.parseArguments(argc, argv)) { scanner.printUsage(); return EXIT_FAILURE; } if (!scanner.process()) { scanner.printErrors(); return EXIT_FAILURE; } return EXIT_SUCCESS; }