KWayland

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

KDE's Doxygen guidelines are available online.