1 | /* |
2 | SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org> |
3 | |
4 | SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
5 | */ |
6 | #include "generator.h" |
7 | |
8 | #include <QBuffer> |
9 | #include <QByteArray> |
10 | #include <QCommandLineParser> |
11 | #include <QCoreApplication> |
12 | #include <QDate> |
13 | #include <QFile> |
14 | #include <QFutureWatcher> |
15 | #include <QMutexLocker> |
16 | #include <QProcess> |
17 | #include <QStandardPaths> |
18 | #include <QTextStream> |
19 | #include <QtConcurrentRun> |
20 | |
21 | #include <QDebug> |
22 | |
23 | #include <functional> |
24 | |
25 | namespace KWayland |
26 | { |
27 | namespace Tools |
28 | { |
29 | static QMap<QString, QString> s_clientClassNameMapping; |
30 | |
31 | static QString toQtInterfaceName(const QString &wlInterface) |
32 | { |
33 | auto it = s_clientClassNameMapping.constFind(key: wlInterface); |
34 | if (it != s_clientClassNameMapping.constEnd()) { |
35 | return it.value(); |
36 | } else { |
37 | qWarning() << "Cannot find mapping for " << wlInterface; |
38 | } |
39 | return wlInterface; |
40 | } |
41 | |
42 | static QString toCamelCase(const QString &underscoreName) |
43 | { |
44 | const QStringList parts = underscoreName.split(QStringLiteral("_" )); |
45 | if (parts.count() < 2) { |
46 | return underscoreName; |
47 | } |
48 | auto it = parts.constBegin(); |
49 | QString camelCase = (*it); |
50 | it++; |
51 | for (; it != parts.constEnd(); ++it) { |
52 | camelCase.append(s: (*it).left(n: 1).toUpper()); |
53 | camelCase.append(s: (*it).mid(position: 1)); |
54 | } |
55 | return camelCase; |
56 | } |
57 | |
58 | Argument::Argument() |
59 | { |
60 | } |
61 | |
62 | Argument::Argument(const QXmlStreamAttributes &attributes) |
63 | : m_name(attributes.value(QStringLiteral("name" )).toString()) |
64 | , m_type(parseType(type: attributes.value(QStringLiteral("type" )))) |
65 | , m_allowNull(attributes.hasAttribute(QStringLiteral("allow-null" ))) |
66 | , m_inteface(attributes.value(QStringLiteral("interface" )).toString()) |
67 | { |
68 | } |
69 | |
70 | Argument::~Argument() = default; |
71 | |
72 | Argument::Type Argument::parseType(const QStringView type) |
73 | { |
74 | if (type.compare(s: QLatin1String("new_id" )) == 0) { |
75 | return Type::NewId; |
76 | } |
77 | if (type.compare(s: QLatin1String("destructor" )) == 0) { |
78 | return Type::Destructor; |
79 | } |
80 | if (type.compare(s: QLatin1String("object" )) == 0) { |
81 | return Type::Object; |
82 | } |
83 | if (type.compare(s: QLatin1String("fd" )) == 0) { |
84 | return Type::FileDescriptor; |
85 | } |
86 | if (type.compare(s: QLatin1String("fixed" )) == 0) { |
87 | return Type::Fixed; |
88 | } |
89 | if (type.compare(s: QLatin1String("uint" )) == 0) { |
90 | return Type::Uint; |
91 | } |
92 | if (type.compare(s: QLatin1String("int" )) == 0) { |
93 | return Type::Int; |
94 | } |
95 | if (type.compare(s: QLatin1String("string" )) == 0) { |
96 | return Type::String; |
97 | } |
98 | |
99 | return Type::Unknown; |
100 | } |
101 | |
102 | QString Argument::typeAsQt() const |
103 | { |
104 | switch (m_type) { |
105 | case Type::Destructor: |
106 | return QString(); |
107 | case Type::FileDescriptor: |
108 | return QStringLiteral("int" ); |
109 | case Type::Fixed: |
110 | return QStringLiteral("qreal" ); |
111 | case Type::Int: |
112 | return QStringLiteral("qint32" ); |
113 | case Type::NewId: |
114 | case Type::Object: |
115 | return toQtInterfaceName(wlInterface: m_inteface); |
116 | case Type::String: |
117 | return QStringLiteral("const QString &" ); |
118 | case Type::Uint: |
119 | return QStringLiteral("quint32" ); |
120 | case Type::Unknown: |
121 | return QString(); |
122 | default: |
123 | Q_UNREACHABLE(); |
124 | } |
125 | } |
126 | |
127 | QString Argument::typeAsServerWl() const |
128 | { |
129 | switch (m_type) { |
130 | case Type::Destructor: |
131 | return QString(); |
132 | case Type::FileDescriptor: |
133 | return QStringLiteral("int32_t" ); |
134 | case Type::Fixed: |
135 | return QStringLiteral("wl_fixed" ); |
136 | case Type::Int: |
137 | return QStringLiteral("int32_t" ); |
138 | case Type::Object: |
139 | return QStringLiteral("wl_resource *" ); |
140 | case Type::String: |
141 | return QStringLiteral("const char *" ); |
142 | case Type::Uint: |
143 | case Type::NewId: |
144 | return QStringLiteral("uint32_t" ); |
145 | case Type::Unknown: |
146 | return QString(); |
147 | default: |
148 | Q_UNREACHABLE(); |
149 | } |
150 | } |
151 | |
152 | Request::Request() |
153 | { |
154 | } |
155 | |
156 | Request::Request(const QString &name) |
157 | : m_name(name) |
158 | { |
159 | } |
160 | |
161 | Request::~Request() = default; |
162 | |
163 | bool Request::isFactory() const |
164 | { |
165 | for (const auto &a : m_arguments) { |
166 | if (a.type() == Argument::Type::NewId) { |
167 | return true; |
168 | } |
169 | } |
170 | return false; |
171 | } |
172 | |
173 | Event::Event() |
174 | { |
175 | } |
176 | |
177 | Event::Event(const QString &name) |
178 | : m_name(name) |
179 | { |
180 | } |
181 | |
182 | Event::~Event() = default; |
183 | |
184 | Interface::Interface() = default; |
185 | |
186 | Interface::Interface(const QXmlStreamAttributes &attributes) |
187 | : m_name(attributes.value(QStringLiteral("name" )).toString()) |
188 | , m_version(attributes.value(QStringLiteral("version" )).toUInt()) |
189 | , m_factory(nullptr) |
190 | { |
191 | auto it = s_clientClassNameMapping.constFind(key: m_name); |
192 | if (it != s_clientClassNameMapping.constEnd()) { |
193 | m_clientName = it.value(); |
194 | } else { |
195 | qWarning() << "Failed to map " << m_name << " to a KWayland name" ; |
196 | } |
197 | } |
198 | |
199 | Interface::~Interface() = default; |
200 | |
201 | Generator::Generator(QObject *parent) |
202 | : QObject(parent) |
203 | { |
204 | } |
205 | |
206 | Generator::~Generator() = default; |
207 | |
208 | void Generator::start() |
209 | { |
210 | startAuthorNameProcess(); |
211 | startAuthorEmailProcess(); |
212 | |
213 | startParseXml(); |
214 | |
215 | startGenerateHeaderFile(); |
216 | startGenerateCppFile(); |
217 | startGenerateServerHeaderFile(); |
218 | startGenerateServerCppFile(); |
219 | } |
220 | |
221 | void Generator::startParseXml() |
222 | { |
223 | if (m_xmlFileName.isEmpty()) { |
224 | return; |
225 | } |
226 | QFile xmlFile(m_xmlFileName); |
227 | xmlFile.open(flags: QIODevice::ReadOnly); |
228 | m_xmlReader.setDevice(&xmlFile); |
229 | while (!m_xmlReader.atEnd()) { |
230 | if (!m_xmlReader.readNextStartElement()) { |
231 | continue; |
232 | } |
233 | if (m_xmlReader.qualifiedName().compare(s: QLatin1String("protocol" )) == 0) { |
234 | parseProtocol(); |
235 | } |
236 | } |
237 | |
238 | auto findFactory = [this](const QString interfaceName) -> Interface * { |
239 | for (auto it = m_interfaces.begin(); it != m_interfaces.end(); ++it) { |
240 | if ((*it).name().compare(s: interfaceName) == 0) { |
241 | continue; |
242 | } |
243 | for (auto r : (*it).requests()) { |
244 | for (auto a : r.arguments()) { |
245 | if (a.type() == Argument::Type::NewId && a.interface().compare(s: interfaceName) == 0) { |
246 | return &(*it); |
247 | } |
248 | } |
249 | } |
250 | } |
251 | return nullptr; |
252 | }; |
253 | for (auto it = m_interfaces.begin(); it != m_interfaces.end(); ++it) { |
254 | Interface *factory = findFactory((*it).name()); |
255 | if (factory) { |
256 | qDebug() << (*it).name() << "gets factored by" << factory->kwaylandClientName(); |
257 | (*it).setFactory(factory); |
258 | } else { |
259 | qDebug() << (*it).name() << "considered as a global" ; |
260 | (*it).markAsGlobal(); |
261 | } |
262 | } |
263 | } |
264 | |
265 | void Generator::parseProtocol() |
266 | { |
267 | const auto attributes = m_xmlReader.attributes(); |
268 | const QString protocolName = attributes.value(QStringLiteral("name" )).toString(); |
269 | |
270 | if (m_baseFileName.isEmpty()) { |
271 | m_baseFileName = protocolName.toLower(); |
272 | } |
273 | |
274 | while (!m_xmlReader.atEnd()) { |
275 | if (!m_xmlReader.readNextStartElement()) { |
276 | if (m_xmlReader.qualifiedName().compare(s: QLatin1String("protocol" )) == 0) { |
277 | return; |
278 | } |
279 | continue; |
280 | } |
281 | if (m_xmlReader.qualifiedName().compare(s: QLatin1String("interface" )) == 0) { |
282 | m_interfaces << parseInterface(); |
283 | } |
284 | } |
285 | } |
286 | |
287 | Interface Generator::parseInterface() |
288 | { |
289 | Interface interface(m_xmlReader.attributes()); |
290 | while (!m_xmlReader.atEnd()) { |
291 | if (!m_xmlReader.readNextStartElement()) { |
292 | if (m_xmlReader.qualifiedName().compare(s: QLatin1String("interface" )) == 0) { |
293 | break; |
294 | } |
295 | continue; |
296 | } |
297 | if (m_xmlReader.qualifiedName().compare(s: QLatin1String("request" )) == 0) { |
298 | interface.addRequest(request: parseRequest()); |
299 | } |
300 | if (m_xmlReader.qualifiedName().compare(s: QLatin1String("event" )) == 0) { |
301 | interface.addEvent(event: parseEvent()); |
302 | } |
303 | } |
304 | return interface; |
305 | } |
306 | |
307 | Request Generator::parseRequest() |
308 | { |
309 | const auto attributes = m_xmlReader.attributes(); |
310 | Request request(attributes.value(QStringLiteral("name" )).toString()); |
311 | if (attributes.value(QStringLiteral("type" )).toString().compare(other: QLatin1String("destructor" )) == 0) { |
312 | request.markAsDestructor(); |
313 | } |
314 | while (!m_xmlReader.atEnd()) { |
315 | if (!m_xmlReader.readNextStartElement()) { |
316 | if (m_xmlReader.qualifiedName().compare(s: QLatin1String("request" )) == 0) { |
317 | break; |
318 | } |
319 | continue; |
320 | } |
321 | if (m_xmlReader.qualifiedName().compare(s: QLatin1String("arg" )) == 0) { |
322 | request.addArgument(arg: Argument(m_xmlReader.attributes())); |
323 | } |
324 | } |
325 | return request; |
326 | } |
327 | |
328 | Event Generator::parseEvent() |
329 | { |
330 | const auto attributes = m_xmlReader.attributes(); |
331 | Event event(attributes.value(QStringLiteral("name" )).toString()); |
332 | while (!m_xmlReader.atEnd()) { |
333 | if (!m_xmlReader.readNextStartElement()) { |
334 | if (m_xmlReader.qualifiedName().compare(s: QLatin1String("event" )) == 0) { |
335 | break; |
336 | } |
337 | continue; |
338 | } |
339 | if (m_xmlReader.qualifiedName().compare(s: QLatin1String("arg" )) == 0) { |
340 | event.addArgument(arg: Argument(m_xmlReader.attributes())); |
341 | } |
342 | } |
343 | return event; |
344 | } |
345 | |
346 | void Generator::startGenerateHeaderFile() |
347 | { |
348 | QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this); |
349 | connect(sender: watcher, signal: &QFutureWatcher<void>::finished, context: this, slot: &Generator::checkEnd); |
350 | m_finishedCounter++; |
351 | watcher->setFuture(QtConcurrent::run(f: [this] { |
352 | QFile file(QStringLiteral("%1.h" ).arg(a: m_baseFileName)); |
353 | file.open(flags: QIODevice::WriteOnly); |
354 | m_stream.setLocalData(new QTextStream(&file)); |
355 | m_project.setLocalData(Project::Client); |
356 | generateCopyrightHeader(); |
357 | generateStartIncludeGuard(); |
358 | generateHeaderIncludes(); |
359 | generateWaylandForwardDeclarations(); |
360 | generateStartNamespace(); |
361 | generateNamespaceForwardDeclarations(); |
362 | for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { |
363 | generateClass(interface: *it); |
364 | } |
365 | generateEndNamespace(); |
366 | generateEndIncludeGuard(); |
367 | |
368 | m_stream.setLocalData(nullptr); |
369 | file.close(); |
370 | })); |
371 | } |
372 | |
373 | void Generator::startGenerateCppFile() |
374 | { |
375 | QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this); |
376 | connect(sender: watcher, signal: &QFutureWatcher<void>::finished, context: this, slot: &Generator::checkEnd); |
377 | m_finishedCounter++; |
378 | watcher->setFuture(QtConcurrent::run(f: [this] { |
379 | QFile file(QStringLiteral("%1.cpp" ).arg(a: m_baseFileName)); |
380 | file.open(flags: QIODevice::WriteOnly); |
381 | m_stream.setLocalData(new QTextStream(&file)); |
382 | m_project.setLocalData(Project::Client); |
383 | generateCopyrightHeader(); |
384 | generateCppIncludes(); |
385 | generateStartNamespace(); |
386 | for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { |
387 | generatePrivateClass(interface: *it); |
388 | generateClientCpp(interface: *it); |
389 | generateClientCppRequests(interface: *it); |
390 | } |
391 | |
392 | generateEndNamespace(); |
393 | |
394 | m_stream.setLocalData(nullptr); |
395 | file.close(); |
396 | })); |
397 | } |
398 | |
399 | void Generator::startGenerateServerHeaderFile() |
400 | { |
401 | QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this); |
402 | connect(sender: watcher, signal: &QFutureWatcher<void>::finished, context: this, slot: &Generator::checkEnd); |
403 | m_finishedCounter++; |
404 | watcher->setFuture(QtConcurrent::run(f: [this] { |
405 | QFile file(QStringLiteral("%1_interface.h" ).arg(a: m_baseFileName)); |
406 | file.open(flags: QIODevice::WriteOnly); |
407 | m_stream.setLocalData(new QTextStream(&file)); |
408 | m_project.setLocalData(Project::Server); |
409 | generateCopyrightHeader(); |
410 | generateStartIncludeGuard(); |
411 | generateHeaderIncludes(); |
412 | generateStartNamespace(); |
413 | generateNamespaceForwardDeclarations(); |
414 | if (std::any_of(first: m_interfaces.constBegin(), last: m_interfaces.constEnd(), pred: [](const Interface &i) { |
415 | return i.isUnstableInterface(); |
416 | })) { |
417 | // generate the unstable semantic version |
418 | auto it = std::find_if(first: m_interfaces.constBegin(), last: m_interfaces.constEnd(), pred: [](const Interface &i) { |
419 | return i.isGlobal(); |
420 | }); |
421 | if (it != m_interfaces.constEnd()) { |
422 | const QString templateString = QStringLiteral( |
423 | "/**\n" |
424 | " * Enum describing the interface versions the %1 can support.\n" |
425 | " *\n" |
426 | " * @since 5.XX\n" |
427 | " **/\n" |
428 | "enum class %1Version {\n" |
429 | " /**\n" |
430 | " * %2\n" |
431 | " **/\n" |
432 | " UnstableV%3\n" |
433 | "};\n\n" ); |
434 | *m_stream.localData() << templateString.arg(a: (*it).kwaylandServerName()) |
435 | .arg(a: (*it).name()) |
436 | .arg(a: (*it).name().mid(position: (*it).name().lastIndexOf(QStringLiteral("_v" )) + 2)); |
437 | } |
438 | } |
439 | for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { |
440 | generateClass(interface: *it); |
441 | } |
442 | generateEndNamespace(); |
443 | generateEndIncludeGuard(); |
444 | |
445 | m_stream.setLocalData(nullptr); |
446 | file.close(); |
447 | })); |
448 | } |
449 | |
450 | void Generator::startGenerateServerCppFile() |
451 | { |
452 | QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this); |
453 | connect(sender: watcher, signal: &QFutureWatcher<void>::finished, context: this, slot: &Generator::checkEnd); |
454 | m_finishedCounter++; |
455 | watcher->setFuture(QtConcurrent::run(f: [this] { |
456 | QFile file(QStringLiteral("%1_interface.cpp" ).arg(a: m_baseFileName)); |
457 | file.open(flags: QIODevice::WriteOnly); |
458 | m_stream.setLocalData(new QTextStream(&file)); |
459 | m_project.setLocalData(Project::Server); |
460 | generateCopyrightHeader(); |
461 | generateCppIncludes(); |
462 | generateStartNamespace(); |
463 | for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { |
464 | generatePrivateClass(interface: *it); |
465 | // generateClientCpp(*it); |
466 | // generateClientCppRequests(*it); |
467 | } |
468 | |
469 | generateEndNamespace(); |
470 | |
471 | m_stream.setLocalData(nullptr); |
472 | file.close(); |
473 | })); |
474 | } |
475 | |
476 | void Generator::checkEnd() |
477 | { |
478 | m_finishedCounter--; |
479 | if (m_finishedCounter == 0) { |
480 | QCoreApplication::quit(); |
481 | } |
482 | } |
483 | |
484 | static QString findGitExec() |
485 | { |
486 | const QString exec = QStandardPaths::findExecutable(QStringLiteral("git" )); |
487 | if (exec.isEmpty()) { |
488 | qWarning() << "Could not find git executable in PATH." ; |
489 | } |
490 | return exec; |
491 | } |
492 | |
493 | void Generator::startAuthorNameProcess() |
494 | { |
495 | const QString exec = findGitExec(); |
496 | if (exec.isEmpty()) { |
497 | return; |
498 | } |
499 | QProcess *proc = new QProcess(this); |
500 | proc->setArguments(QStringList{QStringLiteral("config" ), QStringLiteral("--get" ), QStringLiteral("user.name" )}); |
501 | proc->setProgram(exec); |
502 | connect(sender: proc, signal: static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), context: this, slot: [this, proc] { |
503 | QMutexLocker locker(&m_mutex); |
504 | m_authorName = QString::fromLocal8Bit(ba: proc->readAllStandardOutput()).trimmed(); |
505 | proc->deleteLater(); |
506 | m_waitCondition.wakeAll(); |
507 | }); |
508 | proc->start(); |
509 | } |
510 | |
511 | void Generator::startAuthorEmailProcess() |
512 | { |
513 | const QString exec = findGitExec(); |
514 | if (exec.isEmpty()) { |
515 | return; |
516 | } |
517 | QProcess *proc = new QProcess(this); |
518 | proc->setArguments(QStringList{QStringLiteral("config" ), QStringLiteral("--get" ), QStringLiteral("user.email" )}); |
519 | proc->setProgram(exec); |
520 | connect(sender: proc, signal: static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), context: this, slot: [this, proc] { |
521 | QMutexLocker locker(&m_mutex); |
522 | m_authorEmail = QString::fromLocal8Bit(ba: proc->readAllStandardOutput()).trimmed(); |
523 | proc->deleteLater(); |
524 | m_waitCondition.wakeAll(); |
525 | }); |
526 | proc->start(); |
527 | } |
528 | |
529 | void Generator::generateCopyrightHeader() |
530 | { |
531 | m_mutex.lock(); |
532 | while (m_authorEmail.isEmpty() || m_authorName.isEmpty()) { |
533 | m_waitCondition.wait(lockedMutex: &m_mutex); |
534 | } |
535 | m_mutex.unlock(); |
536 | const QString templateString = QStringLiteral( |
537 | "/****************************************************************************\n" |
538 | "Copyright %1 %2 <%3>\n" |
539 | "\n" |
540 | "This library is free software; you can redistribute it and/or\n" |
541 | "modify it under the terms of the GNU Lesser General Public\n" |
542 | "License as published by the Free Software Foundation; either\n" |
543 | "version 2.1 of the License, or (at your option) version 3, or any\n" |
544 | "later version accepted by the membership of KDE e.V. (or its\n" |
545 | "successor approved by the membership of KDE e.V.), which shall\n" |
546 | "act as a proxy defined in Section 6 of version 3 of the license.\n" |
547 | "\n" |
548 | "This library is distributed in the hope that it will be useful,\n" |
549 | "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" |
550 | "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" |
551 | "Lesser General Public License for more details.\n" |
552 | "\n" |
553 | "You should have received a copy of the GNU Lesser General Public\n" |
554 | "License along with this library. If not, see <http://www.gnu.org/licenses/>.\n" |
555 | "****************************************************************************/\n" ); |
556 | QDate date = QDate::currentDate(); |
557 | *m_stream.localData() << templateString.arg(a: date.year()).arg(a: m_authorName).arg(a: m_authorEmail); |
558 | } |
559 | |
560 | void Generator::generateEndIncludeGuard() |
561 | { |
562 | *m_stream.localData() << QStringLiteral("#endif\n" ); |
563 | } |
564 | |
565 | void Generator::generateStartIncludeGuard() |
566 | { |
567 | const QString templateString = QStringLiteral( |
568 | "#ifndef KWAYLAND_%1_%2_H\n" |
569 | "#define KWAYLAND_%1_%2_H\n\n" ); |
570 | |
571 | *m_stream.localData() << templateString.arg(a: projectToName().toUpper()).arg(a: m_baseFileName.toUpper()); |
572 | } |
573 | |
574 | void Generator::generateStartNamespace() |
575 | { |
576 | const QString templateString = QStringLiteral( |
577 | "namespace KWayland\n" |
578 | "{\n" |
579 | "namespace %1\n" |
580 | "{\n\n" ); |
581 | *m_stream.localData() << templateString.arg(a: projectToName()); |
582 | } |
583 | |
584 | void Generator::generateEndNamespace() |
585 | { |
586 | *m_stream.localData() << QStringLiteral("\n}\n}\n\n" ); |
587 | } |
588 | |
589 | void Generator::generateHeaderIncludes() |
590 | { |
591 | switch (m_project.localData()) { |
592 | case Project::Client: |
593 | *m_stream.localData() << QStringLiteral("#include <QObject>\n\n" ); |
594 | break; |
595 | case Project::Server: |
596 | *m_stream.localData() << QStringLiteral( |
597 | "#include \"global.h\"\n" |
598 | "#include \"resource.h\"\n\n" ); |
599 | break; |
600 | default: |
601 | Q_UNREACHABLE(); |
602 | } |
603 | *m_stream.localData() << QStringLiteral("#include <KWayland/%1/kwayland%2_export.h>\n\n" ).arg(a: projectToName()).arg(a: projectToName().toLower()); |
604 | } |
605 | |
606 | void Generator::generateCppIncludes() |
607 | { |
608 | switch (m_project.localData()) { |
609 | case Project::Client: |
610 | *m_stream.localData() << QStringLiteral("#include \"%1.h\"\n" ).arg(a: m_baseFileName.toLower()); |
611 | *m_stream.localData() << QStringLiteral("#include \"event_queue.h\"\n" ); |
612 | *m_stream.localData() << QStringLiteral("#include \"wayland_pointer_p.h\"\n\n" ); |
613 | break; |
614 | case Project::Server: |
615 | *m_stream.localData() << QStringLiteral("#include \"%1_interface.h\"\n" ).arg(a: m_baseFileName.toLower()); |
616 | *m_stream.localData() << QStringLiteral( |
617 | "#include \"display.h\"\n" |
618 | "#include \"global_p.h\"\n" |
619 | "#include \"resource_p.h\"\n\n" ); |
620 | break; |
621 | default: |
622 | Q_UNREACHABLE(); |
623 | } |
624 | } |
625 | |
626 | void Generator::generateClass(const Interface &interface) |
627 | { |
628 | switch (m_project.localData()) { |
629 | case Project::Client: |
630 | if (interface.isGlobal()) { |
631 | generateClientGlobalClass(interface); |
632 | } else { |
633 | generateClientResourceClass(interface); |
634 | } |
635 | break; |
636 | case Project::Server: |
637 | if (interface.isGlobal()) { |
638 | generateServerGlobalClass(interface); |
639 | } else { |
640 | generateServerResourceClass(interface); |
641 | } |
642 | break; |
643 | default: |
644 | Q_UNREACHABLE(); |
645 | } |
646 | } |
647 | |
648 | void Generator::generateClientGlobalClass(const Interface &interface) |
649 | { |
650 | generateClientGlobalClassDoxy(interface); |
651 | generateClientClassQObjectDerived(interface); |
652 | generateClientGlobalClassCtor(interface); |
653 | generateClientClassDtor(interface); |
654 | generateClientGlobalClassSetup(interface); |
655 | generateClientClassReleaseDestroy(interface); |
656 | generateClientClassStart(interface); |
657 | generateClientClassRequests(interface); |
658 | generateClientClassCasts(interface); |
659 | generateClientClassSignals(interface); |
660 | generateClientGlobalClassEnd(interface); |
661 | } |
662 | |
663 | void Generator::generateClientResourceClass(const Interface &interface) |
664 | { |
665 | generateClientClassQObjectDerived(interface); |
666 | generateClientClassDtor(interface); |
667 | generateClientResourceClassSetup(interface); |
668 | generateClientClassReleaseDestroy(interface); |
669 | generateClientClassRequests(interface); |
670 | generateClientClassCasts(interface); |
671 | generateClientResourceClassEnd(interface); |
672 | } |
673 | |
674 | void Generator::generateServerGlobalClass(const Interface &interface) |
675 | { |
676 | if (interface.isUnstableInterface()) { |
677 | generateServerGlobalClassUnstable(interface); |
678 | return; |
679 | } |
680 | const QString templateString = QStringLiteral( |
681 | "class KWAYLANDSERVER_EXPORT %1 : public Global\n" |
682 | "{\n" |
683 | " Q_OBJECT\n" |
684 | "public:\n" |
685 | " virtual ~%1();\n" |
686 | "\n" |
687 | "private:\n" |
688 | " explicit %1(Display *display, QObject *parent = nullptr);\n" |
689 | " friend class Display;\n" |
690 | " class Private;\n" |
691 | "};\n" |
692 | "\n" ); |
693 | *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()); |
694 | } |
695 | |
696 | void Generator::generateServerGlobalClassUnstable(const Interface &interface) |
697 | { |
698 | const QString templateString = QStringLiteral( |
699 | "class KWAYLANDSERVER_EXPORT %1 : public Global\n" |
700 | "{\n" |
701 | " Q_OBJECT\n" |
702 | "public:\n" |
703 | " virtual ~%1();\n" |
704 | "\n" |
705 | " /**\n" |
706 | " * @returns The interface version used by this %1\n" |
707 | " **/\n" |
708 | " %1Version interfaceVersion() const;\n" |
709 | "\n" |
710 | "protected:\n" |
711 | " class Private;\n" |
712 | " explicit %1(Private *d, QObject *parent = nullptr);\n" |
713 | "\n" |
714 | "private:\n" |
715 | " Private *d_func() const;\n" |
716 | "};\n" |
717 | "\n" ); |
718 | *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()); |
719 | } |
720 | |
721 | void Generator::generateServerResourceClass(const Interface &interface) |
722 | { |
723 | if (interface.factory()->isUnstableInterface()) { |
724 | generateServerResourceClassUnstable(interface); |
725 | return; |
726 | } |
727 | const QString templateString = QStringLiteral( |
728 | "class KWAYLANDSERVER_EXPORT %1 : public Resource\n" |
729 | "{\n" |
730 | " Q_OBJECT\n" |
731 | "public:\n" |
732 | " virtual ~%1();\n" |
733 | "\n" |
734 | "private:\n" |
735 | " explicit %1(%2 *parent, wl_resource *parentResource);\n" |
736 | " friend class %2;\n" |
737 | "\n" |
738 | " class Private;\n" |
739 | " Private *d_func() const;\n" |
740 | "};\n" |
741 | "\n" ); |
742 | *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.factory()->kwaylandServerName()); |
743 | } |
744 | |
745 | void Generator::generateServerResourceClassUnstable(const Interface &interface) |
746 | { |
747 | const QString templateString = QStringLiteral( |
748 | "class KWAYLANDSERVER_EXPORT %1 : public Resource\n" |
749 | "{\n" |
750 | " Q_OBJECT\n" |
751 | "public:\n" |
752 | "\n" |
753 | " virtual ~%1();\n" |
754 | "\n" |
755 | " /**\n" |
756 | " * @returns The interface version used by this %1\n" |
757 | " **/\n" |
758 | " %2Version interfaceVersion() const;\n" |
759 | "\n" |
760 | "protected:\n" |
761 | " class Private;\n" |
762 | " explicit %1(Private *p, QObject *parent = nullptr);\n" |
763 | "\n" |
764 | "private:\n" |
765 | " Private *d_func() const;\n" |
766 | "};\n" |
767 | "\n" ); |
768 | *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.factory()->kwaylandServerName()); |
769 | } |
770 | |
771 | void Generator::generatePrivateClass(const Interface &interface) |
772 | { |
773 | switch (m_project.localData()) { |
774 | case Project::Client: |
775 | generateClientPrivateClass(interface); |
776 | break; |
777 | case Project::Server: |
778 | if (interface.isGlobal()) { |
779 | generateServerPrivateGlobalClass(interface); |
780 | } else { |
781 | generateServerPrivateResourceClass(interface); |
782 | } |
783 | break; |
784 | default: |
785 | Q_UNREACHABLE(); |
786 | } |
787 | } |
788 | |
789 | void Generator::generateServerPrivateGlobalClass(const Interface &interface) |
790 | { |
791 | QString templateString = QStringLiteral( |
792 | "class %1::Private : public Global::Private\n" |
793 | "{\n" |
794 | "public:\n" |
795 | " Private(%1 *q, Display *d);\n" |
796 | "\n" |
797 | "private:\n" |
798 | " void bind(wl_client *client, uint32_t version, uint32_t id) override;\n" |
799 | "\n" |
800 | " static void unbind(wl_resource *resource);\n" |
801 | " static Private *cast(wl_resource *r) {\n" |
802 | " return reinterpret_cast<Private*>(wl_resource_get_user_data(r));\n" |
803 | " }\n" |
804 | "\n" ); |
805 | *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()); |
806 | |
807 | generateServerPrivateCallbackDefinitions(interface); |
808 | |
809 | templateString = QStringLiteral( |
810 | " %1 *q;\n" |
811 | " static const struct %2_interface s_interface;\n" |
812 | " static const quint32 s_version;\n" |
813 | "};\n" |
814 | "\n" |
815 | "const quint32 %1::Private::s_version = %3;\n" |
816 | "\n" ); |
817 | *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.name()).arg(a: interface.version()); |
818 | generateServerPrivateInterfaceClass(interface); |
819 | generateServerPrivateCallbackImpl(interface); |
820 | generateServerPrivateGlobalCtorBindClass(interface); |
821 | } |
822 | |
823 | void Generator::generateServerPrivateCallbackDefinitions(const Interface &interface) |
824 | { |
825 | for (const auto &r : interface.requests()) { |
826 | if (r.isDestructor() && !interface.isGlobal()) { |
827 | continue; |
828 | } |
829 | *m_stream.localData() << QStringLiteral(" static void %1Callback(wl_client *client, wl_resource *resource" ).arg(a: toCamelCase(underscoreName: r.name())); |
830 | for (const auto &a : r.arguments()) { |
831 | *m_stream.localData() << QStringLiteral(", %1 %2" ).arg(a: a.typeAsServerWl()).arg(a: a.name()); |
832 | } |
833 | *m_stream.localData() << QStringLiteral(");\n" ); |
834 | } |
835 | *m_stream.localData() << QStringLiteral("\n" ); |
836 | } |
837 | |
838 | void Generator::generateServerPrivateCallbackImpl(const Interface &interface) |
839 | { |
840 | for (const auto &r : interface.requests()) { |
841 | if (r.isDestructor() && !interface.isGlobal()) { |
842 | continue; |
843 | } |
844 | *m_stream.localData() << QStringLiteral("void %2::Private::%1Callback(wl_client *client, wl_resource *resource" ) |
845 | .arg(a: toCamelCase(underscoreName: r.name())) |
846 | .arg(a: interface.kwaylandServerName()); |
847 | for (const auto &a : r.arguments()) { |
848 | *m_stream.localData() << QStringLiteral(", %1 %2" ).arg(a: a.typeAsServerWl()).arg(a: a.name()); |
849 | } |
850 | *m_stream.localData() << QStringLiteral( |
851 | ")\n" |
852 | "{\n" ); |
853 | if (r.isDestructor()) { |
854 | *m_stream.localData() << QStringLiteral( |
855 | " Q_UNUSED(client)\n" |
856 | " wl_resource_destroy(resource);\n" ); |
857 | } else { |
858 | *m_stream.localData() << QStringLiteral(" // TODO: implement\n" ); |
859 | } |
860 | *m_stream.localData() << QStringLiteral( |
861 | "}\n" |
862 | "\n" ); |
863 | } |
864 | } |
865 | |
866 | void Generator::generateServerPrivateGlobalCtorBindClass(const Interface &interface) |
867 | { |
868 | QString templateString = QStringLiteral( |
869 | "%1::Private::Private(%1 *q, Display *d)\n" |
870 | " : Global::Private(d, &%2_interface, s_version)\n" |
871 | " , q(q)\n" |
872 | "{\n" |
873 | "}\n" |
874 | "\n" |
875 | "void %1::Private::bind(wl_client *client, uint32_t version, uint32_t id)\n" |
876 | "{\n" |
877 | " auto c = display->getConnection(client);\n" |
878 | " wl_resource *resource = c->createResource(&%2_interface, qMin(version, s_version), id);\n" |
879 | " if (!resource) {\n" |
880 | " wl_client_post_no_memory(client);\n" |
881 | " return;\n" |
882 | " }\n" |
883 | " wl_resource_set_implementation(resource, &s_interface, this, unbind);\n" |
884 | " // TODO: should we track?\n" |
885 | "}\n" |
886 | "\n" |
887 | "void %1::Private::unbind(wl_resource *resource)\n" |
888 | "{\n" |
889 | " Q_UNUSED(resource)\n" |
890 | " // TODO: implement?\n" |
891 | "}\n" |
892 | "\n" ); |
893 | *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.name()); |
894 | } |
895 | |
896 | void Generator::generateServerPrivateResourceClass(const Interface &interface) |
897 | { |
898 | QString templateString = QStringLiteral( |
899 | "class %1::Private : public Resource::Private\n" |
900 | "{\n" |
901 | "public:\n" |
902 | " Private(%1 *q, %2 *c, wl_resource *parentResource);\n" |
903 | " ~Private();\n" |
904 | "\n" |
905 | "private:\n" ); |
906 | *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.factory()->kwaylandServerName()); |
907 | |
908 | generateServerPrivateCallbackDefinitions(interface); |
909 | |
910 | templateString = QStringLiteral( |
911 | " %1 *q_func() {\n" |
912 | " return reinterpret_cast<%1 *>(q);\n" |
913 | " }\n" |
914 | "\n" |
915 | " static const struct %2_interface s_interface;\n" |
916 | "};\n" |
917 | "\n" ); |
918 | *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.name()); |
919 | |
920 | generateServerPrivateInterfaceClass(interface); |
921 | generateServerPrivateCallbackImpl(interface); |
922 | generateServerPrivateResourceCtorDtorClass(interface); |
923 | } |
924 | |
925 | void Generator::generateServerPrivateInterfaceClass(const Interface &interface) |
926 | { |
927 | *m_stream.localData() << QStringLiteral("#ifndef K_DOXYGEN\n" ); |
928 | *m_stream.localData() |
929 | << QStringLiteral("const struct %2_interface %1::Private::s_interface = {\n" ).arg(a: interface.kwaylandServerName()).arg(a: interface.name()); |
930 | bool first = true; |
931 | for (auto r : interface.requests()) { |
932 | if (!first) { |
933 | *m_stream.localData() << QStringLiteral(",\n" ); |
934 | } else { |
935 | first = false; |
936 | } |
937 | if (r.isDestructor() && !interface.isGlobal()) { |
938 | *m_stream.localData() << QStringLiteral(" resourceDestroyedCallback" ); |
939 | } else { |
940 | *m_stream.localData() << QStringLiteral(" %1Callback" ).arg(a: toCamelCase(underscoreName: r.name())); |
941 | } |
942 | } |
943 | *m_stream.localData() << QStringLiteral("\n};\n#endif\n\n" ); |
944 | } |
945 | |
946 | void Generator::generateServerPrivateResourceCtorDtorClass(const Interface &interface) |
947 | { |
948 | QString templateString = QStringLiteral( |
949 | "%1::Private::Private(%1 *q, %2 *c, wl_resource *parentResource)\n" |
950 | " : Resource::Private(q, c, parentResource, &%3_interface, &s_interface)\n" |
951 | "{\n" |
952 | "}\n" |
953 | "\n" |
954 | "%1::Private::~Private()\n" |
955 | "{\n" |
956 | " if (resource) {\n" |
957 | " wl_resource_destroy(resource);\n" |
958 | " resource = nullptr;\n" |
959 | " }\n" |
960 | "}\n" ); |
961 | *m_stream.localData() << templateString.arg(a: interface.kwaylandServerName()).arg(a: interface.factory()->kwaylandServerName()).arg(a: interface.name()); |
962 | } |
963 | |
964 | void Generator::generateClientPrivateClass(const Interface &interface) |
965 | { |
966 | if (interface.isGlobal()) { |
967 | generateClientPrivateGlobalClass(interface); |
968 | } else { |
969 | generateClientPrivateResourceClass(interface); |
970 | } |
971 | |
972 | const auto events = interface.events(); |
973 | if (!events.isEmpty()) { |
974 | *m_stream.localData() << QStringLiteral("\nprivate:\n" ); |
975 | // generate the callbacks |
976 | for (auto event : events) { |
977 | const QString templateString = QStringLiteral(" static void %1Callback(void *data, %2 *%2" ); |
978 | *m_stream.localData() << templateString.arg(a: event.name()).arg(a: interface.name()); |
979 | const auto arguments = event.arguments(); |
980 | for (auto argument : arguments) { |
981 | if (argument.interface().isNull()) { |
982 | *m_stream.localData() << QStringLiteral(", %1 %2" ).arg(a: argument.typeAsServerWl()).arg(a: argument.name()); |
983 | } else { |
984 | *m_stream.localData() << QStringLiteral(", %1 *%2" ).arg(a: argument.interface()).arg(a: argument.name()); |
985 | } |
986 | } |
987 | *m_stream.localData() << ");\n" ; |
988 | } |
989 | *m_stream.localData() << QStringLiteral("\n static const %1_listener s_listener;\n" ).arg(a: interface.name()); |
990 | } |
991 | |
992 | *m_stream.localData() << QStringLiteral("};\n\n" ); |
993 | } |
994 | |
995 | void Generator::generateClientPrivateResourceClass(const Interface &interface) |
996 | { |
997 | const QString templateString = QStringLiteral( |
998 | "class %1::Private\n" |
999 | "{\n" |
1000 | "public:\n" |
1001 | " Private(%1 *q);\n" |
1002 | "\n" |
1003 | " void setup(%2 *arg);\n" |
1004 | "\n" |
1005 | " WaylandPointer<%2, %2_destroy> %3;\n" |
1006 | "\n" |
1007 | "private:\n" |
1008 | " %1 *q;\n" ); |
1009 | |
1010 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()).arg(a: interface.name()).arg(a: interface.kwaylandClientName().toLower()); |
1011 | } |
1012 | |
1013 | void Generator::generateClientPrivateGlobalClass(const Interface &interface) |
1014 | { |
1015 | const QString templateString = QStringLiteral( |
1016 | "class %1::Private\n" |
1017 | "{\n" |
1018 | "public:\n" |
1019 | " Private() = default;\n" |
1020 | "\n" |
1021 | " void setup(%2 *arg);\n" |
1022 | "\n" |
1023 | " WaylandPointer<%2, %2_destroy> %3;\n" |
1024 | " EventQueue *queue = nullptr;\n" ); |
1025 | |
1026 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()).arg(a: interface.name()).arg(a: interface.kwaylandClientName().toLower()); |
1027 | } |
1028 | |
1029 | void Generator::generateClientCpp(const Interface &interface) |
1030 | { |
1031 | // TODO: generate listener and callbacks |
1032 | const auto events = interface.events(); |
1033 | if (!events.isEmpty()) { |
1034 | // listener |
1035 | *m_stream.localData() << QStringLiteral("const %1_listener %2::Private::s_listener = {\n" ).arg(a: interface.name()).arg(a: interface.kwaylandClientName()); |
1036 | bool first = true; |
1037 | for (auto event : events) { |
1038 | if (!first) { |
1039 | *m_stream.localData() << QStringLiteral(",\n" ); |
1040 | } |
1041 | *m_stream.localData() << QStringLiteral(" %1Callback" ).arg(a: event.name()); |
1042 | first = false; |
1043 | } |
1044 | *m_stream.localData() << QStringLiteral("\n};\n\n" ); |
1045 | |
1046 | // callbacks |
1047 | for (auto event : events) { |
1048 | *m_stream.localData() << QStringLiteral("void %1::Private::%2Callback(void *data, %3 *%3" ) |
1049 | .arg(a: interface.kwaylandClientName()) |
1050 | .arg(a: event.name()) |
1051 | .arg(a: interface.name()); |
1052 | |
1053 | const auto arguments = event.arguments(); |
1054 | for (auto argument : arguments) { |
1055 | if (argument.interface().isNull()) { |
1056 | *m_stream.localData() << QStringLiteral(", %1 %2" ).arg(a: argument.typeAsServerWl()).arg(a: argument.name()); |
1057 | } else { |
1058 | *m_stream.localData() << QStringLiteral(", %1 *%2" ).arg(a: argument.interface()).arg(a: argument.name()); |
1059 | } |
1060 | } |
1061 | |
1062 | *m_stream.localData() << QStringLiteral( |
1063 | ")\n" |
1064 | "{\n" |
1065 | " auto p = reinterpret_cast<%1::Private*>(data);\n" |
1066 | " Q_ASSERT(p->%2 == %3);\n" ) |
1067 | .arg(a: interface.kwaylandClientName()) |
1068 | .arg(a: interface.kwaylandClientName().toLower()) |
1069 | .arg(a: interface.name()); |
1070 | for (auto argument : arguments) { |
1071 | *m_stream.localData() << QStringLiteral(" Q_UNUSED(%1)\n" ).arg(a: argument.name()); |
1072 | } |
1073 | *m_stream.localData() << QStringLiteral(" // TODO: implement\n}\n\n" ); |
1074 | } |
1075 | } |
1076 | |
1077 | if (interface.isGlobal()) { |
1078 | // generate ctor without this pointer to Private |
1079 | const QString templateString = QStringLiteral( |
1080 | "%1::%1(QObject *parent)\n" |
1081 | " : QObject(parent)\n" |
1082 | " , d(new Private)\n" |
1083 | "{\n" |
1084 | "}\n" ); |
1085 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()); |
1086 | } else { |
1087 | // Private ctor |
1088 | const QString templateString = QStringLiteral( |
1089 | "%1::Private::Private(%1 *q)\n" |
1090 | " : q(q)\n" |
1091 | "{\n" |
1092 | "}\n" |
1093 | "\n" |
1094 | "%1::%1(QObject *parent)\n" |
1095 | " : QObject(parent)\n" |
1096 | " , d(new Private(this))\n" |
1097 | "{\n" |
1098 | "}\n" ); |
1099 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()); |
1100 | } |
1101 | |
1102 | // setup call with optional add_listener |
1103 | const QString setupTemplate = QStringLiteral( |
1104 | "\n" |
1105 | "void %1::Private::setup(%3 *arg)\n" |
1106 | "{\n" |
1107 | " Q_ASSERT(arg);\n" |
1108 | " Q_ASSERT(!%2);\n" |
1109 | " %2.setup(arg);\n" ); |
1110 | *m_stream.localData() << setupTemplate.arg(a: interface.kwaylandClientName()).arg(a: interface.kwaylandClientName().toLower()).arg(a: interface.name()); |
1111 | if (!interface.events().isEmpty()) { |
1112 | *m_stream.localData() |
1113 | << QStringLiteral(" %1_add_listener(%2, &s_listener, this);\n" ).arg(a: interface.name()).arg(a: interface.kwaylandClientName().toLower()); |
1114 | } |
1115 | *m_stream.localData() << QStringLiteral("}\n" ); |
1116 | |
1117 | const QString templateString = QStringLiteral( |
1118 | "\n" |
1119 | "%1::~%1()\n" |
1120 | "{\n" |
1121 | " release();\n" |
1122 | "}\n" |
1123 | "\n" |
1124 | "void %1::setup(%3 *%2)\n" |
1125 | "{\n" |
1126 | " d->setup(%2);\n" |
1127 | "}\n" |
1128 | "\n" |
1129 | "void %1::release()\n" |
1130 | "{\n" |
1131 | " d->%2.release();\n" |
1132 | "}\n" |
1133 | "\n" |
1134 | "void %1::destroy()\n" |
1135 | "{\n" |
1136 | " d->%2.destroy();\n" |
1137 | "}\n" |
1138 | "\n" |
1139 | "%1::operator %3*() {\n" |
1140 | " return d->%2;\n" |
1141 | "}\n" |
1142 | "\n" |
1143 | "%1::operator %3*() const {\n" |
1144 | " return d->%2;\n" |
1145 | "}\n" |
1146 | "\n" |
1147 | "bool %1::isValid() const\n" |
1148 | "{\n" |
1149 | " return d->%2.isValid();\n" |
1150 | "}\n\n" ); |
1151 | |
1152 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()).arg(a: interface.kwaylandClientName().toLower()).arg(a: interface.name()); |
1153 | if (interface.isGlobal()) { |
1154 | const QString templateStringGlobal = QStringLiteral( |
1155 | "void %1::setEventQueue(EventQueue *queue)\n" |
1156 | "{\n" |
1157 | " d->queue = queue;\n" |
1158 | "}\n" |
1159 | "\n" |
1160 | "EventQueue *%1::eventQueue()\n" |
1161 | "{\n" |
1162 | " return d->queue;\n" |
1163 | "}\n\n" ); |
1164 | *m_stream.localData() << templateStringGlobal.arg(a: interface.kwaylandClientName()); |
1165 | } |
1166 | } |
1167 | |
1168 | void Generator::generateClientGlobalClassDoxy(const Interface &interface) |
1169 | { |
1170 | const QString templateString = QStringLiteral( |
1171 | "/**\n" |
1172 | " * @short Wrapper for the %2 interface.\n" |
1173 | " *\n" |
1174 | " * This class provides a convenient wrapper for the %2 interface.\n" |
1175 | " *\n" |
1176 | " * To use this class one needs to interact with the Registry. There are two\n" |
1177 | " * possible ways to create the %1 interface:\n" |
1178 | " * @code\n" |
1179 | " * %1 *c = registry->create%1(name, version);\n" |
1180 | " * @endcode\n" |
1181 | " *\n" |
1182 | " * This creates the %1 and sets it up directly. As an alternative this\n" |
1183 | " * can also be done in a more low level way:\n" |
1184 | " * @code\n" |
1185 | " * %1 *c = new %1;\n" |
1186 | " * c->setup(registry->bind%1(name, version));\n" |
1187 | " * @endcode\n" |
1188 | " *\n" |
1189 | " * The %1 can be used as a drop-in replacement for any %2\n" |
1190 | " * pointer as it provides matching cast operators.\n" |
1191 | " *\n" |
1192 | " * @see Registry\n" |
1193 | " **/\n" ); |
1194 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()).arg(a: interface.name()); |
1195 | } |
1196 | |
1197 | void Generator::generateClientClassQObjectDerived(const Interface &interface) |
1198 | { |
1199 | const QString templateString = QStringLiteral( |
1200 | "class KWAYLANDCLIENT_EXPORT %1 : public QObject\n" |
1201 | "{\n" |
1202 | " Q_OBJECT\n" |
1203 | "public:\n" ); |
1204 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()); |
1205 | } |
1206 | |
1207 | void Generator::generateClientGlobalClassCtor(const Interface &interface) |
1208 | { |
1209 | const QString templateString = QStringLiteral( |
1210 | " /**\n" |
1211 | " * Creates a new %1.\n" |
1212 | " * Note: after constructing the %1 it is not yet valid and one needs\n" |
1213 | " * to call setup. In order to get a ready to use %1 prefer using\n" |
1214 | " * Registry::create%1.\n" |
1215 | " **/\n" |
1216 | " explicit %1(QObject *parent = nullptr);\n" ); |
1217 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()); |
1218 | } |
1219 | |
1220 | void Generator::generateClientClassDtor(const Interface &interface) |
1221 | { |
1222 | *m_stream.localData() << QStringLiteral(" virtual ~%1();\n\n" ).arg(a: interface.kwaylandClientName()); |
1223 | } |
1224 | |
1225 | void Generator::generateClientClassReleaseDestroy(const Interface &interface) |
1226 | { |
1227 | const QString templateString = QStringLiteral( |
1228 | " /**\n" |
1229 | " * @returns @c true if managing a %2.\n" |
1230 | " **/\n" |
1231 | " bool isValid() const;\n" |
1232 | " /**\n" |
1233 | " * Releases the %2 interface.\n" |
1234 | " * After the interface has been released the %1 instance is no\n" |
1235 | " * longer valid and can be setup with another %2 interface.\n" |
1236 | " **/\n" |
1237 | " void release();\n" |
1238 | " /**\n" |
1239 | " * Destroys the data held by this %1.\n" |
1240 | " * This method is supposed to be used when the connection to the Wayland\n" |
1241 | " * server goes away. If the connection is not valid anymore, it's not\n" |
1242 | " * possible to call release anymore as that calls into the Wayland\n" |
1243 | " * connection and the call would fail. This method cleans up the data, so\n" |
1244 | " * that the instance can be deleted or set up to a new %2 interface\n" |
1245 | " * once there is a new connection available.\n" |
1246 | " *\n" |
1247 | " * It is suggested to connect this method to ConnectionThread::connectionDied:\n" |
1248 | " * @code\n" |
1249 | " * connect(connection, &ConnectionThread::connectionDied, %3, &%1::destroy);\n" |
1250 | " * @endcode\n" |
1251 | " *\n" |
1252 | " * @see release\n" |
1253 | " **/\n" |
1254 | " void destroy();\n" |
1255 | "\n" ); |
1256 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()).arg(a: interface.name()).arg(a: interface.kwaylandClientName().toLower()); |
1257 | } |
1258 | |
1259 | void Generator::generateClientGlobalClassSetup(const Interface &interface) |
1260 | { |
1261 | const QString templateString = QStringLiteral( |
1262 | " /**\n" |
1263 | " * Setup this %1 to manage the @p %3.\n" |
1264 | " * When using Registry::create%1 there is no need to call this\n" |
1265 | " * method.\n" |
1266 | " **/\n" |
1267 | " void setup(%2 *%3);\n" ); |
1268 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()).arg(a: interface.name()).arg(a: interface.kwaylandClientName().toLower()); |
1269 | } |
1270 | |
1271 | void Generator::generateClientResourceClassSetup(const Interface &interface) |
1272 | { |
1273 | const QString templateString = QStringLiteral( |
1274 | " /**\n" |
1275 | " * Setup this %1 to manage the @p %3.\n" |
1276 | " * When using %4::create%1 there is no need to call this\n" |
1277 | " * method.\n" |
1278 | " **/\n" |
1279 | " void setup(%2 *%3);\n" ); |
1280 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()) |
1281 | .arg(a: interface.name()) |
1282 | .arg(a: interface.kwaylandClientName().toLower()) |
1283 | .arg(a: interface.factory()->kwaylandClientName()); |
1284 | } |
1285 | |
1286 | void Generator::generateClientClassStart(const Interface &interface) |
1287 | { |
1288 | const QString templateString = QStringLiteral( |
1289 | " /**\n" |
1290 | " * Sets the @p queue to use for creating objects with this %1.\n" |
1291 | " **/\n" |
1292 | " void setEventQueue(EventQueue *queue);\n" |
1293 | " /**\n" |
1294 | " * @returns The event queue to use for creating objects with this %1.\n" |
1295 | " **/\n" |
1296 | " EventQueue *eventQueue();\n\n" ); |
1297 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()); |
1298 | } |
1299 | |
1300 | void Generator::generateClientClassRequests(const Interface &interface) |
1301 | { |
1302 | const auto requests = interface.requests(); |
1303 | const QString templateString = QStringLiteral(" void %1(%2);\n\n" ); |
1304 | const QString factoryTemplateString = QStringLiteral(" %1 *%2(%3);\n\n" ); |
1305 | for (const auto &r : requests) { |
1306 | if (r.isDestructor()) { |
1307 | continue; |
1308 | } |
1309 | QString arguments; |
1310 | bool first = true; |
1311 | QString factored; |
1312 | for (const auto &a : r.arguments()) { |
1313 | if (a.type() == Argument::Type::NewId) { |
1314 | factored = a.interface(); |
1315 | continue; |
1316 | } |
1317 | if (!first) { |
1318 | arguments.append(QStringLiteral(", " )); |
1319 | } else { |
1320 | first = false; |
1321 | } |
1322 | if (a.type() == Argument::Type::Object) { |
1323 | arguments.append(QStringLiteral("%1 *%2" ).arg(a: a.typeAsQt()).arg(a: toCamelCase(underscoreName: a.name()))); |
1324 | } else { |
1325 | arguments.append(QStringLiteral("%1 %2" ).arg(a: a.typeAsQt()).arg(a: toCamelCase(underscoreName: a.name()))); |
1326 | } |
1327 | } |
1328 | if (factored.isEmpty()) { |
1329 | *m_stream.localData() << templateString.arg(a: toCamelCase(underscoreName: (r.name()))).arg(a: arguments); |
1330 | } else { |
1331 | if (!first) { |
1332 | arguments.append(QStringLiteral(", " )); |
1333 | } |
1334 | arguments.append(QStringLiteral("QObject *parent = nullptr" )); |
1335 | *m_stream.localData() << factoryTemplateString.arg(a: toQtInterfaceName(wlInterface: factored)).arg(a: toCamelCase(underscoreName: r.name())).arg(a: arguments); |
1336 | } |
1337 | } |
1338 | } |
1339 | |
1340 | void Generator::generateClientCppRequests(const Interface &interface) |
1341 | { |
1342 | const auto requests = interface.requests(); |
1343 | const QString templateString = QStringLiteral( |
1344 | "void %1::%2(%3)\n" |
1345 | "{\n" |
1346 | " Q_ASSERT(isValid());\n" |
1347 | " %4_%5(d->%6%7);\n" |
1348 | "}\n\n" ); |
1349 | const QString factoryTemplateString = QStringLiteral( |
1350 | "%2 *%1::%3(%4)\n" |
1351 | "{\n" |
1352 | " Q_ASSERT(isValid());\n" |
1353 | " auto p = new %2(parent);\n" |
1354 | " auto w = %5_%6(d->%7%8);\n" |
1355 | " if (d->queue) {\n" |
1356 | " d->queue->addProxy(w);\n" |
1357 | " }\n" |
1358 | " p->setup(w);\n" |
1359 | " return p;\n" |
1360 | "}\n\n" ); |
1361 | for (const auto &r : requests) { |
1362 | if (r.isDestructor()) { |
1363 | continue; |
1364 | } |
1365 | QString arguments; |
1366 | QString requestArguments; |
1367 | bool first = true; |
1368 | QString factored; |
1369 | for (const auto &a : r.arguments()) { |
1370 | if (a.type() == Argument::Type::NewId) { |
1371 | factored = a.interface(); |
1372 | continue; |
1373 | } |
1374 | if (!first) { |
1375 | arguments.append(QStringLiteral(", " )); |
1376 | } else { |
1377 | first = false; |
1378 | } |
1379 | if (a.type() == Argument::Type::Object) { |
1380 | arguments.append(QStringLiteral("%1 *%2" ).arg(a: a.typeAsQt()).arg(a: toCamelCase(underscoreName: a.name()))); |
1381 | requestArguments.append(QStringLiteral(", *%1" ).arg(a: toCamelCase(underscoreName: a.name()))); |
1382 | } else { |
1383 | arguments.append(QStringLiteral("%1 %2" ).arg(a: a.typeAsQt()).arg(a: toCamelCase(underscoreName: a.name()))); |
1384 | QString arg = toCamelCase(underscoreName: a.name()); |
1385 | if (a.type() == Argument::Type::Fixed) { |
1386 | arg = QStringLiteral("wl_fixed_from_double(%1)" ).arg(a: arg); |
1387 | } |
1388 | requestArguments.append(QStringLiteral(", %1" ).arg(a: arg)); |
1389 | } |
1390 | } |
1391 | if (factored.isEmpty()) { |
1392 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()) |
1393 | .arg(a: toCamelCase(underscoreName: r.name())) |
1394 | .arg(a: arguments) |
1395 | .arg(a: interface.name()) |
1396 | .arg(a: r.name()) |
1397 | .arg(a: interface.kwaylandClientName().toLower()) |
1398 | .arg(a: requestArguments); |
1399 | } else { |
1400 | if (!first) { |
1401 | arguments.append(QStringLiteral(", " )); |
1402 | } |
1403 | arguments.append(QStringLiteral("QObject *parent" )); |
1404 | *m_stream.localData() << factoryTemplateString.arg(a: interface.kwaylandClientName()) |
1405 | .arg(a: toQtInterfaceName(wlInterface: factored)) |
1406 | .arg(a: toCamelCase(underscoreName: r.name())) |
1407 | .arg(a: arguments) |
1408 | .arg(a: interface.name()) |
1409 | .arg(a: r.name()) |
1410 | .arg(a: interface.kwaylandClientName().toLower()) |
1411 | .arg(a: requestArguments); |
1412 | } |
1413 | } |
1414 | } |
1415 | |
1416 | void Generator::generateClientClassCasts(const Interface &interface) |
1417 | { |
1418 | const QString templateString = QStringLiteral( |
1419 | " operator %1*();\n" |
1420 | " operator %1*() const;\n\n" ); |
1421 | *m_stream.localData() << templateString.arg(a: interface.name()); |
1422 | } |
1423 | |
1424 | void Generator::generateClientGlobalClassEnd(const Interface &interface) |
1425 | { |
1426 | Q_UNUSED(interface) |
1427 | *m_stream.localData() << QStringLiteral("private:\n" ); |
1428 | generateClientClassDptr(interface); |
1429 | *m_stream.localData() << QStringLiteral("};\n\n" ); |
1430 | } |
1431 | |
1432 | void Generator::generateClientClassDptr(const Interface &interface) |
1433 | { |
1434 | Q_UNUSED(interface) |
1435 | *m_stream.localData() << QStringLiteral( |
1436 | " class Private;\n" |
1437 | " QScopedPointer<Private> d;\n" ); |
1438 | } |
1439 | |
1440 | void Generator::generateClientResourceClassEnd(const Interface &interface) |
1441 | { |
1442 | *m_stream.localData() << QStringLiteral( |
1443 | "private:\n" |
1444 | " friend class %2;\n" |
1445 | " explicit %1(QObject *parent = nullptr);\n" ) |
1446 | .arg(a: interface.kwaylandClientName()) |
1447 | .arg(a: interface.factory()->kwaylandClientName()); |
1448 | generateClientClassDptr(interface); |
1449 | *m_stream.localData() << QStringLiteral("};\n\n" ); |
1450 | } |
1451 | |
1452 | void Generator::generateWaylandForwardDeclarations() |
1453 | { |
1454 | for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { |
1455 | *m_stream.localData() << QStringLiteral("struct %1;\n" ).arg(a: (*it).name()); |
1456 | } |
1457 | *m_stream.localData() << "\n" ; |
1458 | } |
1459 | |
1460 | void Generator::generateNamespaceForwardDeclarations() |
1461 | { |
1462 | QSet<QString> referencedObjects; |
1463 | for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { |
1464 | const auto events = (*it).events(); |
1465 | const auto requests = (*it).requests(); |
1466 | for (const auto &e : events) { |
1467 | const auto args = e.arguments(); |
1468 | for (const auto &a : args) { |
1469 | if (a.type() != Argument::Type::Object && a.type() != Argument::Type::NewId) { |
1470 | continue; |
1471 | } |
1472 | referencedObjects << a.interface(); |
1473 | } |
1474 | } |
1475 | for (const auto &r : requests) { |
1476 | const auto args = r.arguments(); |
1477 | for (const auto &a : args) { |
1478 | if (a.type() != Argument::Type::Object && a.type() != Argument::Type::NewId) { |
1479 | continue; |
1480 | } |
1481 | referencedObjects << a.interface(); |
1482 | } |
1483 | } |
1484 | } |
1485 | |
1486 | switch (m_project.localData()) { |
1487 | case Project::Client: |
1488 | *m_stream.localData() << QStringLiteral("class EventQueue;\n" ); |
1489 | for (const auto &o : referencedObjects) { |
1490 | auto it = s_clientClassNameMapping.constFind(key: o); |
1491 | if (it != s_clientClassNameMapping.constEnd()) { |
1492 | *m_stream.localData() << QStringLiteral("class %1;\n" ).arg(a: it.value()); |
1493 | } else { |
1494 | qWarning() << "Cannot forward declare KWayland class for interface " << o; |
1495 | } |
1496 | } |
1497 | *m_stream.localData() << QStringLiteral("\n" ); |
1498 | break; |
1499 | case Project::Server: |
1500 | *m_stream.localData() << QStringLiteral("class Display;\n\n" ); |
1501 | break; |
1502 | default: |
1503 | Q_UNREACHABLE(); |
1504 | } |
1505 | } |
1506 | |
1507 | void Generator::generateClientClassSignals(const Interface &interface) |
1508 | { |
1509 | const QString templateString = QStringLiteral( |
1510 | "Q_SIGNALS:\n" |
1511 | " /**\n" |
1512 | " * The corresponding global for this interface on the Registry got removed.\n" |
1513 | " *\n" |
1514 | " * This signal gets only emitted if the %1 got created by\n" |
1515 | " * Registry::create%1\n" |
1516 | " **/\n" |
1517 | " void removed();\n\n" ); |
1518 | *m_stream.localData() << templateString.arg(a: interface.kwaylandClientName()); |
1519 | } |
1520 | |
1521 | QString Generator::projectToName() const |
1522 | { |
1523 | switch (m_project.localData()) { |
1524 | case Project::Client: |
1525 | return QStringLiteral("Client" ); |
1526 | case Project::Server: |
1527 | return QStringLiteral("Server" ); |
1528 | default: |
1529 | Q_UNREACHABLE(); |
1530 | } |
1531 | } |
1532 | |
1533 | static void parseMapping() |
1534 | { |
1535 | QFile mappingFile(QStringLiteral(MAPPING_FILE)); |
1536 | mappingFile.open(flags: QIODevice::ReadOnly); |
1537 | QTextStream stream(&mappingFile); |
1538 | while (!stream.atEnd()) { |
1539 | QString line = stream.readLine(); |
1540 | if (line.startsWith(s: QLatin1String("#" )) || line.isEmpty()) { |
1541 | continue; |
1542 | } |
1543 | const QStringList parts = line.split(QStringLiteral(";" )); |
1544 | if (parts.count() < 2) { |
1545 | continue; |
1546 | } |
1547 | s_clientClassNameMapping.insert(key: parts.first(), value: parts.at(i: 1)); |
1548 | } |
1549 | } |
1550 | |
1551 | } |
1552 | } |
1553 | |
1554 | int main(int argc, char **argv) |
1555 | { |
1556 | using namespace KWayland::Tools; |
1557 | |
1558 | parseMapping(); |
1559 | |
1560 | QCoreApplication app(argc, argv); |
1561 | |
1562 | QCommandLineParser parser; |
1563 | QCommandLineOption xmlFile(QStringList{QStringLiteral("x" ), QStringLiteral("xml" )}, |
1564 | QStringLiteral("The wayland protocol to parse." ), |
1565 | QStringLiteral("FileName" )); |
1566 | QCommandLineOption fileName(QStringList{QStringLiteral("f" ), QStringLiteral("file" )}, |
1567 | QStringLiteral("The base name of files to be generated. E.g. for \"foo\" the files \"foo.h\" and \"foo.cpp\" are generated." |
1568 | "If not provided the base name gets derived from the xml protocol name" ), |
1569 | QStringLiteral("FileName" )); |
1570 | |
1571 | parser.addHelpOption(); |
1572 | parser.addOption(commandLineOption: xmlFile); |
1573 | parser.addOption(commandLineOption: fileName); |
1574 | |
1575 | parser.process(app); |
1576 | |
1577 | Generator generator(&app); |
1578 | generator.setXmlFileName(parser.value(option: xmlFile)); |
1579 | generator.setBaseFileName(parser.value(option: fileName)); |
1580 | generator.start(); |
1581 | |
1582 | return app.exec(); |
1583 | } |
1584 | |
1585 | #include "moc_generator.cpp" |
1586 | |