blob: 9691b857f27b3b9b7795c0b6532ea319666c4575 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QCoreApplication>
#include <QFile>
#include <QXmlStreamReader>
#include <vector>
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<WaylandEnumEntry> entries;
};
struct WaylandArgument {
QByteArray name;
QByteArray type;
QByteArray interface;
QByteArray summary;
bool allowNull;
};
struct WaylandEvent {
bool request;
QByteArray name;
QByteArray type;
std::vector<WaylandArgument> arguments;
};
struct WaylandInterface {
QByteArray name;
int version;
std::vector<WaylandEnum> enums;
std::vector<WaylandEvent> events;
std::vector<WaylandEvent> requests;
};
bool isServerSide();
bool parseOption(const char *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<WaylandArgument> &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<WaylandEnum> &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;
QXmlStreamReader *m_xml = nullptr;
};
bool Scanner::parseArguments(int argc, char **argv)
{
m_scannerName = argv[0];
if (argc <= 2 || !parseOption(argv[1]))
return false;
m_protocolFilePath = QByteArray(argv[2]);
if (argc >= 4)
m_headerPath = QByteArray(argv[3]);
if (argc == 5)
m_prefix = QByteArray(argv[4]);
return true;
}
void Scanner::printUsage()
{
fprintf(stderr, "Usage: %s [client-header|server-header|client-code|server-code] specfile [header-path] [prefix]\n", m_scannerName.constData());
}
bool Scanner::isServerSide()
{
return m_option == ServerHeader || m_option == ServerCode;
}
bool Scanner::parseOption(const char *str)
{
if (str == QLatin1String("client-header"))
m_option = ClientHeader;
else if (str == QLatin1String("server-header"))
m_option = ServerHeader;
else if (str == QLatin1String("client-code"))
m_option = ClientCode;
else if (str == QLatin1String("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() == "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() == "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() == "event")
interface.events.push_back(readEvent(xml, false));
else if (xml.name() == "request")
interface.requests.push_back(readEvent(xml, true));
else if (xml.name() == "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<WaylandArgument> &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<WaylandEnum> &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() != "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<WaylandInterface> interfaces;
while (m_xml->readNextStartElement()) {
if (m_xml->name() == "interface")
interfaces.push_back(readInterface(*m_xml));
else
m_xml->skipCurrentElement();
}
if (m_xml->hasError())
return false;
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 <QByteArray>\n");
printf("#include <QMultiMap>\n");
printf("#include <QString>\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 = QByteArray("Q_WAYLAND_SERVER_") + preProcessorProtocolName + "_EXPORT";
printf("\n");
printf("#if !defined(%s)\n", serverExport.constData());
printf("# if defined(QT_SHARED)\n");
printf("# define %s Q_DECL_EXPORT\n", serverExport.constData());
printf("# else\n");
printf("# define %s\n", serverExport.constData());
printf("# endif\n");
printf("#endif\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(" class %s %s\n {\n", serverExport.constData(), interfaceName);
printf(" public:\n");
printf(" %s(struct ::wl_client *client, int 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, int 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, int id, int version);\n");
printf(" Resource *add(struct wl_list *resource_list, struct ::wl_client *client, int 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<struct ::wl_client*, Resource*> resourceMap() { return m_resource_map; }\n");
printf(" const QMultiMap<struct ::wl_client*, Resource*> 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<struct ::wl_client*, Resource*> m_resource_map;\n");
printf(" Resource *m_resource;\n");
printf(" struct ::wl_global *m_global;\n");
printf(" uint32_t m_globalVersion;\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("\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, int 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 : qAsConst(m_resource_map))\n");
printf(" wl_resource_set_implementation(resource->handle, nullptr, nullptr, nullptr);\n");
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, int 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, int 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_globalVersion = version;\n");
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, qMin(that->m_globalVersion, 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(" that->m_resource_map.remove(resource->client(), resource);\n");
printf(" that->%s_destroy_resource(resource);\n", interfaceNameStripped);
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<Resource *>(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(" 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(" 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<void *>(const_cast<char *>(%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 <QByteArray>\n");
printf("#include <QString>\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");
QByteArray clientExport;
if (m_headerPath.size()) {
clientExport = QByteArray("Q_WAYLAND_CLIENT_") + preProcessorProtocolName + "_EXPORT";
printf("\n");
printf("#if !defined(%s)\n", clientExport.constData());
printf("# if defined(QT_SHARED)\n");
printf("# define %s Q_DECL_EXPORT\n", clientExport.constData());
printf("# else\n");
printf("# define %s\n", clientExport.constData());
printf("# endif\n");
printf("#endif\n");
}
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, int 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, int 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(" 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("\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("#if (WAYLAND_VERSION_MAJOR == 1 && WAYLAND_VERSION_MINOR > 10) || WAYLAND_VERSION_MAJOR > 1\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("#else\n");
printf(" return (void *) wl_proxy_marshal_constructor((struct wl_proxy *) registry,\n");
printf(" bindOpCode, interface, name, interface->name, version, nullptr);\n");
printf("#endif\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, int 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, int id, int version)\n", interfaceName);
printf(" {\n");
printf(" m_%s = static_cast<struct ::%s *>(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(" 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<void *>(const_cast<char *>(%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;
}