[
  {
    "path": ".gitignore",
    "content": ".qmake.*\n*.o\nMakefile\nmoc_*.cpp\nmoc_*.h\n*.moc\n"
  },
  {
    "path": "LICENSE",
    "content": "The source code in this repository is available under the same terms as that of\nthe open source licenses Qt itself is available under: LGPL v3 or GPL 2+.\n\nIf these license terms are not suitable, please contact us: info@crimson.no.\n"
  },
  {
    "path": "README.md",
    "content": "# introduction\n\ngtkplatform is a Qt Platform Abstraction plugin providing Qt applications with\nthe capability to use gtk+ as a host toolkit, primarily intended for use on\nLinux desktops.\n\nThat is: it lets Qt applications render with native gtk+ menus, and use gtk+ for\ninput (mouse, keyboard, touch), and getting window content on screen, the same\nas it uses e.g. cocoa on macOS for instance.\n\nThanks to:\n\n* Robin Burchell (@rburchell, initial idea & heavy lifting)\n* John Brooks (@special, rendering work and OpenGL implementation)\n* Gunnar Sletta (@sletta, all sorts of assistance and brainstorming)\n* Donald Carr (@sirspudd, Arch Linux packaging)\n\nIf you'd like to have a chat with us, feel free to\n[drop in on Telegram](https://t.me/joinchat/FlwGHw366p2Z9tBZ_f1yTA).\n\n## what this is\n\nIt's a way to get better integrated, consistent application behaviour on the\nLinux desktop.\n\n## what this is **not**\n\nIt's not the most performant way to run applications, and as a result, not well\nsuited for the embedded environment. This is particularly noticable with QtQuick\napplications, as they make use of OpenGL. This goes through a copy step: the\nscene is drawn offscreen, copied, and uploaded to the gtk+ window, which is\nrather inefficient. Hopefully, gtk+ will grow API to allow this to be done\nbetter in the future.\n\n## current state\n\nWhat works:\n\n* Showing, resizing, and hiding windows windows (all hopefully flicker-free)\n* Rendering in those windows\n    * Using QPainter\n    * Using QOpenGLContext\n    * A mix of OpenGL and software rendering in those windows (QOpenGLWidget, etc)\n    * QtWebEngine (if patched, tracked at [#9](https://github.com/CrimsonAS/gtkplatform/issues/9))\n* Simple clipboard interaction (text/image copying)\n* Native gtk+ dialogs (taken from Qt)\n* Native gtk+ menubar\n* Notifications using libnotify\n* Input events\n    * Touch\n    * Keyboard\n    * Mouse, including smooth scroll events\n\nNot everything does work, though. See the known issues section.\n\n# screenshots\n\nHere's Qt Creator running with the gtk+ platform plugin:\n\n![Creator with the gtk+ platform plugin](https://qtl.me/E3D02BB87DD13C40C2DEF08E2440B735.png)\n\nIf you'd like to see more, [go take a look at the wiki](https://github.com/CrimsonAS/gtkplatform/wiki).\n\n# building\n\nThese are the versions I test with.\n\n* Qt 5.10.1\n* gtk+ 3.22.30\n* libnotify 0.7.7\n\nThese are all available in Fedora 28, which is where I do testing/development.\nGood support is also available on Arch Linux, using [the package generously\nmaintained by @sirspudd](https://aur.archlinux.org/packages/qt-gtk-platform-plugin/).\n\nNote that on Fedora, you need qt5-qtbase-static and redhat-rpm-config installed\nto build from source.\n\nOther distributions may, or may not work, but I don't have any involvement with them.\n\nWith dependencies installed:\n\n* `qmake`\n* `make`\n* `make install` (as root)\n\nThen try launch something after setting `QT_QPA_PLATFORM=gtk` (or `-platform gtk`\nas a command line option)\n\n# history\n\nQt is pretty portable. I don't think there's any doubt to this statement; just\ntake a look at the vast myriad of platform ports out there. It runs on macOS,\nWindows, even Haiku. It's everywhere.\n\nThere's a bit of a fly in the ointment, though: on the Linux desktop, things\naren't quite so well defined. There isn't a \"sanctioned\" platform toolkit.\nAs a result, Qt has to do quite a lot of heavy lifting itself, and this doesn't\nalways result in something that is too well integrated with the host desktop\nsystem.\n\nAs an additional problem, the Linux desktop world is changing. The stability\n(which some may consider stagnation) of xcb has been giving way to the rise of\nWayland. In many ways, this change has been beneficial: it's a lot harder to\nintroduce some bad graphical glitches like flicker on resize. On the other hand,\nit introduces a host of its own brand new problems.\n\nEven discounting all of these as solvable problems on top of the usual\nthings like reasonably performant flicker-free graphics that we ought to have\nand ought to be able to take for granted, there's the root issue\nthat there is a significant amount of duplicate work going on here: any new\ndevelopment has to be solved in (at least) two major toolkits.\n\nSo with this background, we get to the situation that lead to this project. A\nwhile ago, I moved from macOS back to Linux as my day to day desktop system, and\nquickly experienced frustration with a myriad of bad, very user-visible bugs\non Linux like variances in how high DPI is dealt with, font sizing and selection\nthat wasn't identical, black flicker on resizing, bad trackpad scroll behaviour,\nthe reliance of Qt applications on xwayland rather than being first class\nWayland citizens, etc.\n\nThis project aims to help mitigate those issues.\n\n# Known Issues\n\n* Popup positioning (like combo box dropdowns) will often be wrong.\n\n  When running on Wayland, this platform plugin will not allow absolute\n  positioning of a window in global coordinate space. Instead, popups are\n  positioned relative to their parent window. This usually manifests as windows\n  appearing very far away from where they ought to have triggered because they\n  failed to set a parent.\n\n* Notifications don't work right.\n\n  Right now, we're using libnotify, because using `GtkApplication` without using\n  `g_application_run` doesn't seem trivial. This means that we're not using the\n  latest and greatest stuff, unfortunately. I'd like to fix this somehow.\n\n* Accessibility doesn't work\n* Drag and drop doesn't work\n\n  It isn't written yet. There's rather a lot of features like that, actually.\n  I'm sure it will improve with time.\n\n* My menu shortcuts have funny things in them\n\n  The mapping of GTK+ keys to Qt keys is incomplete. See [#8](https://github.com/CrimsonAS/gtkplatform/issues/8).\n\n* QtWebEngine doesn't work out of the box\n\n  Correct. Currently there's a hardcoded \"whitelist\" of platform plugins that\n  will work. The patch to make it work is quite trivial, see `src/core/content_browser_client_qt.cpp`,\n  and make it request \"eglcontext\" unconditionally (or when using the \"gtk\"\n  plugin). See [#9](https://github.com/CrimsonAS/gtkplatform/issues/9).\n\n# FAQ\n\n* **Q:** But my desktop works just fine. I don't want to use this.\n\n  **A:** That's fine. Keep using what you are using today, and pretend this\n  doesn't exist.\n\n* **Q:** Why is this any better than a theme for Qt which looks like a gtk+ theme?\n\n  **A:** These are separate, but related concerns. If I was just interested in\n  getting the contents of a window to look like the contents of a Qt window,\n  then sure, a theme/style plugin alone would be more than sufficient.\n\n  More importantly than the contents of the window, though, I want consistency\n  on the level of things underneath theming too. For instance, using this, you\n  get transparent Wayland support that looks and works good *right now* without\n  having to fix the numerous desktop-related pieces that are missing from QtWayland.\n  You get consistent high DPI support. You get window resizing that doesn't\n  flicker, unlike that of the xcb platform plugin.\n\n  Longer term, I have even bigger goals than this. I want to be able to use\n  platform-native features like GNotification, GtkHeaderBar, app menus, and more.\n  I'd also like to look into mapping GtkGesture into something that Qt applications\n  can make use of, so pinch/rotate/etc all work in the same way across all desktop\n  applications.\n\n  Most of this isn't realised yet, as for the time being I'm focusing on the\n  \"basics\", but in the longer term I expect it will come. In the short term,\n  this is about a more consistent, more usable out-of-the-box desktop\n  experience.\n\n* **Q:** Are you changing Qt's API?\n\n  **A:** No. Your existing Qt applications of today will work with this with the\n  same amount of tweaking they might need to make when running on a new platform\n  like macOS or Wayland. We may provide some helpers to allow using some gtk+\n  specific features in future, like GtkHeaderBar, though.\n\n* **Q:** Why are you doing this? Qt supports _my pet feature_ better.\n\n  **A:** That might be true. The main problem here is inconsistencies between\n  different applications on my desktop. I want everything to look, and act the\n  same. And maybe once I have that, we can focus on improving everything at once\n  rather than fighting over who has the better toolkit.\n\n* **Q:** Why isn't this part of Qt?\n\n  **A:** Firstly, it's easier to develop something that is rapidly changing\n  out-of-tree. Secondly, I want this to be usable on my desktop _now_, not\n  in 6-12 months time.\n\n* **Q:** There's no system tray icon support\n\n  **A:** [Correct](https://bugzilla.gnome.org/show_bug.cgi?id=785956).\n\n  Given that GtkStatusIcon is deprecated, and the system tray is a dead concept inside GNOME, I don't think this warrants supporting.\n\n* **Q:** How does high DPI support work?\n\n  **A:** We're using gtk+, so, it works the same way it does there. Window size\n  etc is reported in units that aren't real pixels. The window content is drawn\n  by scaling those sizes by the appropriate amount into a larger buffer, which\n  is then drawn by gtk+.\n\n* **Q:** How do multiple monitors work?\n\n  **A:** They probably don't.\n\n  More seriously, I haven't tested them much yet. There's probably going to\n  be a lot of bugs there. When all that stabilizes, though, it should work the\n  same as it does with gtk+ applications.\n\n* **Q:** My application doesn't work!\n\n  **A:** Not exactly a question, but: many applications rely on features of the\n  underlying platform at build time. This of course won't work out of the box in\n  many cases, sometimes due to missing mapping of a feature, and sometimes\n  because it's outright not possible.\n\n  It might be a bug in gtkplatform, or it might be that the application requires\n  adaptation.\n\n* **Q:** Does this render using gtk+?\n\n  **A:** Sort of. With the exception of menu bars on windows, the content of the\n  window is entirely rendered by Qt right now. Once Qt is done with that, it\n  passes over to our code, and we use gtk+ to get that content on screen as\n  well as to provide some of the desktop's settings, like font settings.\n\n  So the reason that widgets look \"native\" is for the most part not down to the\n  work that we've done, but rather the work of the people who wrote the Adwaita\n  theme plugin. This having been said, it might be an area of interest to look\n  at the possibility of using Pango to render text in the future, rather than\n  fontconfig/freetype.\n\n* **Q:** What technologies does this build on?\n\n  **A:** The \"glib\" event dispatcher, originally written by [Bradley Hughes](https://blog.qt.io/blog/2006/02/24/qt-and-glib/)\n  during his time working on Qt has proven very useful. Without that, this\n  would have been a more difficult task.\n  The [\"adwaita\" QStyle plugin](https://github.com/MartinBriza/adwaita-qt) is\n  also great in that it helps the contents of windows blend in very well.\n\n  Of course, none of this would be possible without the work of everyone in the\n  greater Linux desktop community, too, particularly the gtk+ and Qt contributors.\n"
  },
  {
    "path": "examples/examples.pro",
    "content": "TEMPLATE = subdirs\nSUBDIRS += gtkextras\n"
  },
  {
    "path": "examples/gtkextras/gtkextras.pro",
    "content": "TEMPLATE = subdirs\nSUBDIRS += \\\n    headerbar\n"
  },
  {
    "path": "examples/gtkextras/headerbar/headerbar.pro",
    "content": "QT += widgets gui-private gtkextras\n\nTEMPLATE = app\nTARGET = testheaderbar\nINCLUDEPATH += ../src\n\n# Input\nSOURCES += main.cpp\n\nCONFIG += link_pkgconfig\nPKGCONFIG += gtk+-3.0\n\nCONFIG += no_keywords\n\ntarget.path = $$[QT_INSTALL_EXAMPLES]/gtkextras/headerbar\nINSTALLS += target\n"
  },
  {
    "path": "examples/gtkextras/headerbar/main.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include <QApplication>\n#include <QWidget>\n#include <QWindow>\n#include <QLabel>\n#include <QDebug>\n\n#include <QtGtkExtras/QGtkHeaderBar>\n#include <QtGtkExtras/QGtkRefPtr>\n\n#include <gtk/gtk.h>\n\nclass TestWindow : public QWidget\n{\npublic:\n    TestWindow()\n    {\n        QGtkHeaderBar *qhb = new QGtkHeaderBar(this);\n        GtkWidget *hb = qhb->headerBarWidget();\n\n        gtk_header_bar_set_title(GTK_HEADER_BAR(hb), \"A magical Qt test\");\n        gtk_header_bar_set_subtitle(GTK_HEADER_BAR(hb), \"Featuring a real GtkHeaderBar\");\n        gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(hb), TRUE);\n\n        openMenuButton = gtk_button_new_from_icon_name(\"open-menu-symbolic\", GTK_ICON_SIZE_BUTTON);\n        gtk_header_bar_pack_end(GTK_HEADER_BAR(hb), openMenuButton.get());\n    }\n\nprivate:\n    QGtkRefPtr<GtkWidget> openMenuButton;\n};\n\nint main(int argc, char **argv)\n{\n    QApplication app(argc, argv);\n\n    TestWindow w;\n\n    QLabel l(&w);\n    l.setText(\"I'm a QLabel\");\n\n    w.resize(200, 200);\n    w.show();\n\n    return app.exec();\n}\n"
  },
  {
    "path": "gtkplatform.pro",
    "content": "load(qt_parts)\n"
  },
  {
    "path": "src/gtkextras/gtkextras.pro",
    "content": "TARGET = QtGtkExtras\nQT -= gui\nQT += widgets gui-private\n\nHEADERS += \\\n    qgtkrefptr.h \\\n    qgtkheaderbar.h\n\nSOURCES += \\\n    qgtkheaderbar.cpp\n\nCONFIG += link_pkgconfig\nPKGCONFIG_PRIVATE += gdk-3.0 gtk+-3.0\n\nCONFIG += no_keywords\n\nload(qt_module)\nCONFIG -= create_cmake # should be fixed, but ...\n"
  },
  {
    "path": "src/gtkextras/qgtkextrasglobal.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2018 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QTGTKEXTRASGLOBAL_H\n#define QTGTKEXTRASGLOBAL_H\n\n#include <QtCore/qglobal.h>\n\nQT_BEGIN_NAMESPACE\n\n#ifndef QT_STATIC\n#  if defined(QT_BUILD_GTKEXTRAS_LIB)\n#    define Q_GTKEXTRAS_EXPORT Q_DECL_EXPORT\n#  else\n#    define Q_GTKEXTRAS_EXPORT Q_DECL_IMPORT\n#  endif\n#else\n#  define Q_GTKEXTRAS_EXPORT\n#endif\n\nQT_END_NAMESPACE\n\n#endif // QTGTKEXTRASGLOBAL_H\n\n"
  },
  {
    "path": "src/gtkextras/qgtkheaderbar.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2018 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include <QWindow>\n#include <QWidget>\n#include <QGuiApplication>\n#include <QDebug>\n#include <qpa/qplatformnativeinterface.h>\n\n#include \"qgtkheaderbar.h\"\n#include \"qgtkrefptr.h\"\n\nclass QGtkHeaderBar::QGtkHeaderBarPrivate\n{\npublic:\n    QGtkRefPtr<GtkWidget> headerBar;\n};\n\nQGtkHeaderBar::QGtkHeaderBar(QObject *parent)\n    : d(new QGtkHeaderBarPrivate)\n{\n    QWindow *win = 0;\n\n    win = qobject_cast<QWindow*>(parent);\n    if (!win) {\n        QWidget *w = qobject_cast<QWidget*>(parent);\n\n        if (!w->isVisible()) {\n            w->show(); // ensure pwin creation\n            w->hide();\n        }\n\n        qWarning() << \"widget w\" << w;\n        if (w) {\n            // ### this stuff all feels super sketchy\n            //w = w->window();\n            //if (!w) {\n            //    qFatal(\"Created a QGtkHeaderBar on a widget with no parent window...\");\n            //}\n            win = w->windowHandle();\n            qWarning() << \"widget win\" << win;\n            if (!win) {\n                qWarning() << \"widget npw\" << w->nativeParentWidget();\n                qWarning() << \"widget npw win\" << w->nativeParentWidget()->windowHandle();\n                win = w->nativeParentWidget()->windowHandle();\n                if (!win) {\n                    qFatal(\"QGtkHeaderBar couldn't get window handle of widget...\");\n                }\n            }\n        }\n    }\n\n    // ensure that QPA resources are created\n    win->create();\n\n    d->headerBar = gtk_header_bar_new();\n\n    QPlatformNativeInterface *platformNativeInterface = QGuiApplication::platformNativeInterface();\n    GtkWidget *w = static_cast<GtkWidget*>(platformNativeInterface->nativeResourceForWindow(\"gtkwindow\", win));\n    if (!w) {\n        qFatal(\"no GtkWidget! (not using the right QPA?)\");\n    }\n    gtk_window_set_titlebar(GTK_WINDOW(w), d->headerBar.get());\n}\n\nQGtkHeaderBar::~QGtkHeaderBar()\n{\n    delete d;\n}\n\nGtkWidget *QGtkHeaderBar::headerBarWidget() const\n{\n    return d->headerBar.get();\n}\n\n"
  },
  {
    "path": "src/gtkextras/qgtkheaderbar.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2018 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKHEADERBAR_H\n#define QGTKHEADERBAR_H\n\n#include <QtCore/qglobal.h>\n#include <QtCore/qobject.h>\n#include <gtk/gtk.h>\n\n#include \"qgtkextrasglobal.h\"\n\nQT_BEGIN_NAMESPACE\n\nclass Q_GTKEXTRAS_EXPORT QGtkHeaderBar : public QObject\n{\npublic:\n    QGtkHeaderBar(QObject *parent);\n    ~QGtkHeaderBar();\n\n    GtkWidget *headerBarWidget() const;\n\nprivate:\n    class QGtkHeaderBarPrivate;\n    QGtkHeaderBarPrivate *d;\n};\n\nQT_END_NAMESPACE\n\n#endif // QGTKHEADERBAR_H\n"
  },
  {
    "path": "src/gtkextras/qgtkrefptr.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKREFPTR_H\n#define QGTKREFPTR_H\n\n#include <QtCore/qglobal.h>\n#include <gtk/gtk.h>\n\nQT_BEGIN_NAMESPACE\n\ntemplate<typename T>\nclass QGtkRefPtr\n{\npublic:\n    QGtkRefPtr()\n        : m_obj(0)\n    {\n\n    }\n\n    QGtkRefPtr(gpointer obj)\n        : m_obj(obj)\n    {\n        if (m_obj)\n            g_object_ref_sink(m_obj);\n    }\n\n    QGtkRefPtr(const QGtkRefPtr& other)\n        : m_obj(other.m_obj)\n    {\n        if (m_obj)\n            g_object_ref(m_obj);\n    }\n\n    ~QGtkRefPtr()\n    {\n        if (m_obj)\n            g_object_unref(m_obj);\n    }\n\n    QGtkRefPtr<T>& operator=(const QGtkRefPtr &other)\n    {\n        reset(other.get());\n        return *this;\n    }\n\n    bool operator==(const QGtkRefPtr &other)\n    {\n        return this->m_obj == other.m_obj;\n    }\n\n    bool operator!=(const QGtkRefPtr &other)\n    {\n        return !(*this == other);\n    }\n\n    bool operator==(gpointer other)\n    {\n        return this->m_obj == other;\n    }\n\n    bool operator!=(gpointer other)\n    {\n        return !(*this == other);\n    }\n\n    // ### consider moves\n\n    T* get() const { return static_cast<T*>(m_obj); }\n\n    operator bool() const { return m_obj != nullptr; }\n\n    void reset(T* newObj)\n    {\n        if (m_obj)\n            g_object_unref(m_obj);\n        m_obj = newObj;\n        if (m_obj)\n            g_object_ref_sink(m_obj);\n    }\n\nprivate:\n    gpointer m_obj;\n};\n\nQT_END_NAMESPACE\n\n#endif // QGTKREFPTR_H\n"
  },
  {
    "path": "src/platform-plugin/CSystrace.cpp",
    "content": "/*\n * Copyright (c) 2017 Crimson AS <info@crimson.no>\n * Author: Robin Burchell <robin.burchell@crimson.no>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include <errno.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <time.h>\n#include <sys/time.h>\n#include <assert.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/mman.h>\n#include <fcntl.h>\n// MAC\n#include <unistd.h> // syscall()\n#include <sys/syscall.h> // SYS_thread_selfid\n// ENDMAC\n\n#include <unordered_map>\n\n#include \"CSystrace.h\"\n#include \"CTraceMessages.h\"\n\n#include <atomic>\n\n// Information about SHM chunks\nconst int ShmChunkSize = 1024 * 10;\n\n// Data about the process of tracing itself.\n// This is held thread-local.\nstruct CTracerThreadData\n{\n    // The FD for the open SHM chunk\n    int m_shm_fd = -1;\n\n    // The pointer to the start of the SHM chunk\n    char *m_shmInitialPtr = 0;\n\n    // The pointer to the current location in the SHM chunk (so written len\n    // would be m_shmPtr - m_shmInitialPtr).\n    char *m_shmPtr = 0;\n\n    // The name of the current SHM chunk\n    char *m_currentChunkName = 0;\n\n    // How much of the SHM chunk for this thread is left, in bytes?\n    int m_remainingChunkSize = 0;\n\n    // A map of string -> ID for this thread. Each thread registers strings\n    // independently (as it has its own chunks, its own code, and we don't want\n    // to lock as much as possible).\n    std::unordered_map<const char *, uint64_t> m_registeredStrings;\n};\n\nstatic thread_local CTracerThreadData tracerThreadData;\n\n// Global data. There are no locks in place, so don't be dumb when using this.\nstruct CTracerGlobalData\n{\n    // FD to communicate with traced\n    int m_traced_fd = -1;\n\n    // Each thread registers unique strings as it comes across them here and sends a\n    // registration message to traced.\n    std::atomic<uint64_t> m_currentStringId;\n\n    // When the trace started (when systrace_init was called).\n    // Do not modify this outside of systrace_init! It is read from multiple\n    // threads.\n    struct timespec m_originalTp;\n};\n\nstatic CTracerGlobalData tracerGlobalData;\n\n//gettid(); except that mac sucks\nstatic int gettid()\n{\n#ifdef __APPLE__\n    return syscall(SYS_thread_selfid);\n#else\n    return syscall(SYS_gettid);\n#endif\n}\n\n/*! Update the book keeping for the current position in the chunk.\n */\nstatic void advance_chunk(int len)\n{\n    tracerThreadData.m_shmPtr += len;\n    tracerThreadData.m_remainingChunkSize -= len;\n    assert(tracerThreadData.m_remainingChunkSize >= 0);\n}\n\n/*!\n * Send the current chunk to traced for processing.\n *\n * ### right now, this will not be called if a thread terminates abruptly.\n * we should somehow monitor old, stale chunks and force-submit them.\n */\nstatic void submit_chunk()\n{\n    if (tracerThreadData.m_shm_fd == -1)\n        return;\n\n    munmap(tracerThreadData.m_shmInitialPtr, ShmChunkSize);\n    close(tracerThreadData.m_shm_fd);\n    tracerThreadData.m_shm_fd = -1;\n    tracerThreadData.m_shmPtr = 0;\n\n    char buf[1024];\n    int blen = sprintf(buf, \"%s\\n\", tracerThreadData.m_currentChunkName);\n    if (0) // left for debug purposes\n        printf(\"TID %d sending %s\", gettid(), buf);\n    int ret = write(tracerGlobalData.m_traced_fd, buf, blen);\n    if (ret == -1) {\n        // ### we also need to ignore SIGPIPE or clients will die if traced does.\n        perror(\"Can't write to traced! Giving up!\");\n        shm_unlink(tracerThreadData.m_currentChunkName);\n        close(tracerGlobalData.m_traced_fd);\n        tracerGlobalData.m_traced_fd = -1;\n    }\n}\n\nstatic uint64_t getMicroseconds()\n{\n    struct timespec tp;\n    if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1) {\n        perror(\"Can't get time\");\n        abort();\n    }\n\n    return (tp.tv_sec - tracerGlobalData.m_originalTp.tv_sec) * 1000000 +\n           (tp.tv_nsec / 1000) - (tracerGlobalData.m_originalTp.tv_nsec / 1000);\n}\n\n\nstatic void systrace_debug()\n{\n#if 0\n    static thread_local bool debugging = false;\n    if (debugging)\n        return;\n\n    debugging = true;\n    // These vars are to try avoid spurious reporting.\n    static thread_local int lastm_remainingChunkSize = 0;\n    if (m_remainingChunkSize != lastm_remainingChunkSize) {\n        lastm_remainingChunkSize = m_remainingChunkSize;\n        systrace_record_counter(\"systrace\",  \"m_remainingChunkSize\",  m_remainingChunkSize, gettid());\n    }\n    static uint64_t lastStringCount = 0;\n    if (lastStringCount != tracerGlobalData.m_currentStringId.load()) {\n        lastStringCount = tracerGlobalData.m_currentStringId.load();\n        systrace_record_counter(\"systrace\", \"registeredStringCount\", lastStringCount); // not thread-specific\n    }\n    debugging = false;\n#endif\n}\n\n/*!\n * Make sure we have a valid SHM chunk to write events to, or abort if not.\n */\nstatic void ensure_chunk(int mlen)\n{\n    if (tracerThreadData.m_shm_fd != -1 && tracerThreadData.m_remainingChunkSize >= mlen)\n        return;\n\n    if (tracerThreadData.m_shm_fd != -1) {\n        submit_chunk();\n    }\n\n    // ### linux via /dev/shm or memfd_create?\n    int nextShmId = 0;\n    while (tracerThreadData.m_shm_fd == -1 && nextShmId < TRACED_MAX_SHM_CHUNKS) {\n        if (!tracerThreadData.m_currentChunkName)\n            asprintf(&tracerThreadData.m_currentChunkName, \"tracechunk-%d\", nextShmId++);\n\n        tracerThreadData.m_shm_fd = shm_open(tracerThreadData.m_currentChunkName, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);\n        if (tracerThreadData.m_shm_fd != -1) {\n            break; // we won!\n        } else {\n            // try again. either something is using that chunk name, or traced\n            // hasn't released it for us to reuse yet.\n            free(tracerThreadData.m_currentChunkName);\n            tracerThreadData.m_currentChunkName = 0;\n        }\n    }\n\n    if (tracerThreadData.m_shm_fd == -1) {\n        fprintf(stderr, \"Something is seriously screwed. Can't find any free SHM chunk, tried all %d\\n\", TRACED_MAX_SHM_CHUNKS);\n        abort();\n    }\n\n    if (ftruncate(tracerThreadData.m_shm_fd, ShmChunkSize) == -1) {\n        perror(\"Can't ftruncate SHM!\");\n        abort();\n    }\n    tracerThreadData.m_shmPtr = (char*)mmap(0, ShmChunkSize, PROT_READ | PROT_WRITE, MAP_SHARED, tracerThreadData.m_shm_fd, 0);\n    if (tracerThreadData.m_shmPtr == MAP_FAILED) {\n        perror(\"Can't map SHM!\");\n        abort();\n    }\n    tracerThreadData.m_remainingChunkSize = ShmChunkSize;\n\n    ChunkHeader *h = (ChunkHeader*)tracerThreadData.m_shmPtr;\n    h->magic = TRACED_PROTOCOL_MAGIC;\n    h->version = TRACED_PROTOCOL_VERSION;\n    h->pid = getpid();\n    h->tid = gettid();\n    h->epoch = (tracerGlobalData.m_originalTp.tv_sec * 1000000) +\n               (tracerGlobalData.m_originalTp.tv_nsec / 1000);\n    advance_chunk(sizeof(ChunkHeader));\n}\n\n__attribute__((constructor)) void systrace_init()\n{\n    if (clock_gettime(CLOCK_MONOTONIC, &tracerGlobalData.m_originalTp) == -1) {\n        perror(\"Can't get time\");\n        abort();\n    }\n\n    if (getenv(\"TRACED\") == NULL) {\n        tracerGlobalData.m_traced_fd = open(\"/tmp/traced\", O_WRONLY);\n\n        if ((tracerGlobalData.m_traced_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {\n            perror(\"Can't create socket for traced!\");\n        }\n\n        struct sockaddr_un remote;\n        remote.sun_family = AF_UNIX;\n        strcpy(remote.sun_path, \"/tmp/traced\");\n        int len = strlen(remote.sun_path) + sizeof(remote.sun_family) + 1;\n        if (connect(tracerGlobalData.m_traced_fd, (struct sockaddr *)&remote, len) == -1) {\n            perror(\"Can't connect to traced!\");\n        }\n    } else {\n        fprintf(stderr, \"Running trace daemon. Not tracing.\\n\");\n    }\n}\n\n__attribute__((destructor)) void systrace_deinit()\n{\n    if (tracerGlobalData.m_traced_fd == -1)\n        return;\n    submit_chunk();\n    close(tracerGlobalData.m_traced_fd);\n    tracerGlobalData.m_traced_fd = -1;\n}\n\nint systrace_should_trace(const char *module)\n{\n    if (tracerGlobalData.m_traced_fd == -1)\n        return 0;\n    // hack this if you want to temporarily omit some traces.\n    return 1;\n}\n\nstatic uint64_t getStringId(const char *string)\n{\n    auto it = tracerThreadData.m_registeredStrings.find(string);\n    if (it == tracerThreadData.m_registeredStrings.end()) {\n        uint64_t nid = tracerGlobalData.m_currentStringId.fetch_add(1);\n        tracerThreadData.m_registeredStrings[string] = nid;\n\n        int slen = strlen(string);\n        assert(slen < ShmChunkSize / 100); // 102 characters, assuming 10kb\n        ensure_chunk(sizeof(RegisterStringMessage) + slen);\n        RegisterStringMessage *m = (RegisterStringMessage*)tracerThreadData.m_shmPtr;\n        m->messageType = MessageType::RegisterStringMessage;\n        m->id = nid;\n        m->length = slen;\n        strncpy(&m->stringData, string, slen);\n        advance_chunk(sizeof(RegisterStringMessage) + slen);\n        systrace_debug();\n        return nid;\n    }\n\n    return it->second;\n}\n\nvoid systrace_duration_begin(const char *module, const char *tracepoint)\n{\n     if (!systrace_should_trace(module))\n         return;\n\n    uint64_t modid = getStringId(module);\n    uint64_t tpid = getStringId(tracepoint);\n\n    ensure_chunk(sizeof(BeginMessage));\n    BeginMessage *m = (BeginMessage*)tracerThreadData.m_shmPtr;\n    m->messageType = MessageType::BeginMessage;\n    m->microseconds = getMicroseconds();\n    m->categoryId = modid;\n    m->tracepointId = tpid;\n    advance_chunk(sizeof(BeginMessage));\n\n    systrace_debug();\n}\n\nvoid systrace_duration_end(const char *module, const char *tracepoint)\n{\n    if (!systrace_should_trace(module))\n        return;\n\n    uint64_t modid = getStringId(module);\n    uint64_t tpid = getStringId(tracepoint);\n\n    ensure_chunk(sizeof(EndMessage));\n    EndMessage *m = (EndMessage*)tracerThreadData.m_shmPtr;\n    m->messageType = MessageType::EndMessage;\n    m->microseconds = getMicroseconds();\n    m->categoryId = modid;\n    m->tracepointId = tpid;\n    advance_chunk(sizeof(EndMessage));\n\n    systrace_debug();\n}\n\nvoid systrace_duration_begin(CSystraceEvent &event)\n{\n    if (!systrace_should_trace(event.m_module))\n        return;\n\n    event.m_begin = getMicroseconds();\n    // Do nothing We will write the event on end.\n}\n\nvoid systrace_duration_end(CSystraceEvent &event)\n{\n    if (!systrace_should_trace(event.m_module))\n        return;\n\n    uint64_t modid = getStringId(event.m_module);\n    uint64_t tpid = getStringId(event.m_tracepoint);\n\n    ensure_chunk(sizeof(DurationMessage));\n    DurationMessage *m = (DurationMessage*)tracerThreadData.m_shmPtr;\n    m->messageType = MessageType::DurationMessage;\n    m->microseconds = event.m_begin;\n    m->duration = getMicroseconds() - event.m_begin;\n    m->categoryId = modid;\n    m->tracepointId = tpid;\n    advance_chunk(sizeof(DurationMessage));\n\n    systrace_debug();\n}\n\nvoid systrace_record_counter(const char *module, const char *tracepoint, int value, int id)\n{\n    if (!systrace_should_trace(module))\n        return;\n\n    uint64_t modid = getStringId(module);\n    uint64_t tpid = getStringId(tracepoint);\n\n    if (id == -1) {\n        ensure_chunk(sizeof(CounterMessage));\n        CounterMessage *m = (CounterMessage*)tracerThreadData.m_shmPtr;\n        m->messageType = MessageType::CounterMessage;\n        m->microseconds = getMicroseconds();\n        m->categoryId = modid;\n        m->tracepointId = tpid;\n        m->value = value;\n        advance_chunk(sizeof(CounterMessage));\n    } else {\n        ensure_chunk(sizeof(CounterMessageWithId));\n        CounterMessageWithId *m = (CounterMessageWithId*)tracerThreadData.m_shmPtr;\n        m->messageType = MessageType::CounterMessageWithId;\n        m->microseconds = getMicroseconds();\n        m->categoryId = modid;\n        m->tracepointId = tpid;\n        m->value = value;\n        m->id = id;\n        advance_chunk(sizeof(CounterMessageWithId));\n    }\n\n    systrace_debug();\n}\n\nvoid systrace_async_begin(const char *module, const char *tracepoint, const void *cookie)\n{\n    if (!systrace_should_trace(module))\n        return;\n\n    uint64_t modid = getStringId(module);\n    uint64_t tpid = getStringId(tracepoint);\n\n    ensure_chunk(sizeof(AsyncBeginMessage));\n    AsyncBeginMessage *m = (AsyncBeginMessage*)tracerThreadData.m_shmPtr;\n    m->messageType = MessageType::AsyncBeginMessage;\n    m->microseconds = getMicroseconds();\n    m->categoryId = modid;\n    m->tracepointId = tpid;\n    m->cookie = (intptr_t)cookie;\n    advance_chunk(sizeof(AsyncBeginMessage));\n\n    systrace_debug();\n}\n\nvoid systrace_async_end(const char *module, const char *tracepoint, const void *cookie)\n{\n    if (!systrace_should_trace(module))\n        return;\n\n    uint64_t modid = getStringId(module);\n    uint64_t tpid = getStringId(tracepoint);\n\n    ensure_chunk(sizeof(AsyncEndMessage));\n    AsyncEndMessage *m = (AsyncEndMessage*)tracerThreadData.m_shmPtr;\n    m->messageType = MessageType::AsyncEndMessage;\n    m->microseconds = getMicroseconds();\n    m->categoryId = modid;\n    m->tracepointId = tpid;\n    m->cookie = (intptr_t)cookie;\n    advance_chunk(sizeof(AsyncEndMessage));\n\n    systrace_debug();\n}\n\n\n"
  },
  {
    "path": "src/platform-plugin/CSystrace.h",
    "content": "/*\n * Copyright (c) 2017 Crimson AS <info@crimson.no>\n * Author: Robin Burchell <robin.burchell@crimson.no>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#ifndef SYSTRACE_H\n#define SYSTRACE_H\n\n#if defined(DISABLE_TRACE_CODE)\nstruct CSystraceEvent;\n\ninline void systrace_init() {}\ninline void systrace_deinit() {}\ninline int systrace_should_trace(const char *) { return 0; }\ninline void systrace_duration_begin(const char *, const char *) {}\ninline void systrace_duration_end(const char *, const char *) {}\ninline void systrace_duration_begin(CSystraceEvent &) {}\ninline void systrace_duration_end(CSystraceEvent &) {}\ninline void systrace_record_counter(const char *, const char *, int , int = -1) {}\ninline void systrace_async_begin(const char *, const char *, const void *) {}\ninline void systrace_async_end(const char *, const char *, const void *) {}\n\nstruct CSystraceEvent\n{\npublic:\n    CSystraceEvent(const char *, const char *)\n    {\n    }\n\n    ~CSystraceEvent()\n    {\n    }\n};\n\nstruct CSystraceAsyncEvent\n{\npublic:\n    CSystraceAsyncEvent(const char *, const char *, const void *)\n    {\n    }\n\n    ~CSystraceAsyncEvent()\n    {\n    }\n};\n\n#else\n\n#include <pthread.h>\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n\n#if defined(_WIN32) || defined(__CYGWIN__)\n# if defined(BUILDING_DLL)\n#  if defined(__GNUC__)\n#   define SYSTRACE_EXPORT __attribute__ ((dllexport))\n#  else\n#   define SYSTRACE_EXPORT __declspec(dllexport)\n#  endif\n# else\n#  if defined(__GNUC__)\n#   define SYSTRACE_EXPORT __attribute__ ((dllimport))\n#  else\n#   define SYSTRACE_EXPORT __declspec(dllimport)\n#  endif\n# endif\n#else\n# define SYSTRACE_EXPORT __attribute__ ((visibility (\"default\")))\n#endif\n\n/*!\n * Perform necessary set up. Should be called before any other functions.\n *\n * \\note If your compiler supports the `constructor` attribute (gcc does),\n * then this method will be called for you. Calling it multiple times will not\n * cause trouble, however, so feel free to call it yourself.\n *\n * \\sa systrace_deinit()\n */\nSYSTRACE_EXPORT void systrace_init();\n\n/*!\n * Perform necessary tear down. Should be called before termination, and no systrace\n * methods should be called after it.\n *\n * \\note If your compiler supports the `destructor` attribute (gcc does),\n * then this method will be called for you. Calling it multiple times will not\n * cause trouble, however, so feel free to call it yourself.\n *\n * \\sa systrace_init()\n */\nSYSTRACE_EXPORT void systrace_deinit();\n\n/*!\n * Determine whether or not a given \\a module should be traced.\n * This can be used to avoid expensive setup (such as allocation of data for the\n * trace event).\n *\n * Returns 1 if the event should be traced, 0 otherwise.\n */\nSYSTRACE_EXPORT int systrace_should_trace(const char *module);\n\nstruct CSystraceEvent;\n\nSYSTRACE_EXPORT void systrace_duration_begin(const char *module, const char *tracepoint);\nSYSTRACE_EXPORT void systrace_duration_end(const char *module, const char *tracepoint);\nSYSTRACE_EXPORT void systrace_duration_begin(CSystraceEvent &event);\nSYSTRACE_EXPORT void systrace_duration_end(CSystraceEvent &event);\nSYSTRACE_EXPORT void systrace_record_counter(const char *module, const char *tracepoint, int value, int id = -1);\nSYSTRACE_EXPORT void systrace_async_begin(const char *module, const char *tracepoint, const void *cookie);\nSYSTRACE_EXPORT void systrace_async_end(const char *module, const char *tracepoint, const void *cookie);\nstruct SYSTRACE_EXPORT CSystraceEvent\n{\npublic:\n    CSystraceEvent(const char *module, const char *tracepoint)\n        : m_module(module)\n        , m_tracepoint(tracepoint)\n    {\n        systrace_duration_begin(*this);\n    }\n\n    ~CSystraceEvent()\n    {\n        systrace_duration_end(*this);\n    }\n\n    const char *m_module;\n    const char *m_tracepoint;\n    uint64_t m_begin;\n};\n\nstruct SYSTRACE_EXPORT CSystraceAsyncEvent\n{\npublic:\n    CSystraceAsyncEvent(const char *module, const char *tracepoint, const void *cookie)\n        : m_module(module)\n        , m_tracepoint(tracepoint)\n        , m_cookie(cookie)\n    {\n        systrace_async_begin(m_module, m_tracepoint, m_cookie);\n    }\n\n    ~CSystraceAsyncEvent()\n    {\n        systrace_async_end(m_module, m_tracepoint, m_cookie);\n    }\n\nprivate:\n    const char *m_module;\n    const char *m_tracepoint;\n    const void *m_cookie;\n};\n\n#endif // DISABLE_TRACE_CODE\n\n#define COMBINE1(X,Y) X##Y  // helper macros\n#define COMBINE(X,Y) COMBINE1(X,Y)\n\n// ### TRACE_STR_COPY\n// ### TRACE_EVENT_COPY_XXX\n\n// Records a pair of begin and end events called \"name\" for the current\n// scope, with 0, 1 or 2 associated arguments. If the category is not\n// enabled, then this does nothing.\n// - category and name strings must have application lifetime (statics or\n//   literals). They may not include \" chars.\n#define TRACE_EVENT0(module, tracepoint) \\\n    CSystraceEvent COMBINE(ev, __LINE__) (module, tracepoint);\n// ### EVENT1, EVENT2\n\n// ### TRACE_EVENT_INSTANT0?\n\n#define TRACE_EVENT_BEGIN0(module, tracepoint) \\\n    systrace_duration_begin(module, tracepoint);\n#define TRACE_EVENT_END0(module, tracepoint) \\\n    systrace_duration_end(module, tracepoint);\n// ### BEGIN & END 1, 2\n\n\n// ###:\n// Pointers can be used for the ID parameter, and they will be mangled\n// internally so that the same pointer on two different processes will not\n// match.\n#define TRACE_EVENT_ASYNC_BEGIN0(module, tracepoint, cookie) \\\n    systrace_async_begin(module, tracepoint, cookie);\n#define TRACE_EVENT_ASYNC_END0(module, tracepoint, cookie) \\\n    systrace_async_end(module, tracepoint, cookie);\n// ### 1 & 2\n\n#define TRACE_COUNTER1(module, tracepoint, value) \\\n    systrace_record_counter(module, tracepoint, value);\n// ### TRACE_COUNTER2\n\n#define TRACE_COUNTER_ID1(module, tracepoint, value, id) \\\n    systrace_record_counter(module, tracepoint, value, id);\n\n#endif // SYSTRACE_H\n"
  },
  {
    "path": "src/platform-plugin/CTraceMessages.h",
    "content": "/*\n * Copyright (c) 2017 Crimson AS <info@crimson.no>\n * Author: Robin Burchell <robin.burchell@crimson.no>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#ifndef CTRACEMESSAGES_H\n#define CTRACEMESSAGES_H\n\n// Used by traced to clean up at startup time. Used client side to figure out\n// what to allocate.\n#define TRACED_MAX_SHM_CHUNKS 99999\n\n// Used to mark a SHM chunk for some measure of safety.\n#define TRACED_PROTOCOL_MAGIC 0xDEADBEEFBAAD\n\n// Used to mark a SHM chunk as being written/read by a given version, for\n// safety's sake. Bump this if the protocol changes.\n#define TRACED_PROTOCOL_VERSION 256\n\nenum class MessageType : uint8_t\n{\n    NoMessage = 0,\n    RegisterStringMessage = 1,\n    BeginMessage = 2,\n    EndMessage = 3,\n    DurationMessage = 4,\n    AsyncBeginMessage = 5,\n    AsyncEndMessage = 6,\n    CounterMessage = 7,\n    CounterMessageWithId = 8\n};\n\n// ### consider splitting ChunkHeader to a ProcessHeaderMessage and\n// ChunkHeaderMessage. pid & epoch should never change, so that's 16 bytes of\n// each chunk wasted at present.\nstruct ChunkHeader\n{\n    uint64_t magic;\n    uint16_t version;\n    uint64_t pid;\n    uint64_t tid;\n\n    // when the process under trace started. traced uses this against its own start\n    // time to calculate relative times.\n    uint64_t epoch;\n};\n\nstruct BaseMessage\n{\n    MessageType messageType;\n};\n\nstruct RegisterStringMessage : public BaseMessage\n{\n    uint64_t id;\n    uint8_t length;\n    char stringData; // and it follows on for length bytes\n};\n\nstruct RegularMessage : public BaseMessage\n{\n    uint64_t microseconds;\n    uint16_t categoryId;\n    uint64_t tracepointId;\n};\n\nstruct BeginMessage : public RegularMessage\n{\n};\n\nstruct EndMessage : public RegularMessage\n{\n};\n\nstruct DurationMessage : public RegularMessage\n{\n    uint64_t duration;\n};\n\nstruct AsyncBeginMessage : public RegularMessage\n{\n    uint64_t cookie;\n};\n\nstruct AsyncEndMessage : public RegularMessage\n{\n    uint64_t cookie;\n};\n\nstruct CounterMessage : public RegularMessage\n{\n    uint64_t value;\n};\n\nstruct CounterMessageWithId : public CounterMessage\n{\n    uint64_t id;\n};\n\n\n#endif // CTRACEMESSAGES_H\n"
  },
  {
    "path": "src/platform-plugin/gtk.json",
    "content": "{ \"Keys\": [ \"gtk\" ] }\n"
  },
  {
    "path": "src/platform-plugin/main.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include <qpa/qplatformintegrationplugin.h>\n#include \"qgtkintegration.h\"\n\nQT_BEGIN_NAMESPACE\n\nclass QGtkIntegrationPlugin : public QPlatformIntegrationPlugin\n{\n    Q_OBJECT\n    Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE \"gtk.json\")\npublic:\n    QPlatformIntegration *create(const QString&, const QStringList&) Q_DECL_OVERRIDE;\n};\n\nQPlatformIntegration *QGtkIntegrationPlugin::create(const QString& system, const QStringList& paramList)\n{\n    if (!system.compare(QLatin1String(\"gtk\"), Qt::CaseInsensitive))\n        return new QGtkIntegration(paramList);\n\n    return 0;\n}\n\nQT_END_NAMESPACE\n\n#include \"main.moc\"\n"
  },
  {
    "path": "src/platform-plugin/platform-plugin.pro",
    "content": "TARGET = qgtk\n\n!gtkplatform-release {\n    CONFIG -= release\n    CONFIG += debug\n}\nQT += core-private gui-private widgets\n\nequals(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 8): {\n    QT += platformsupport-private\n} else {\n    QT += fontdatabase_support_private glx_support_private egl_support_private service_support_private theme_support_private\n}\n\nequals(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 7): {\n    # qhighdpi has some bugs with this in at least 5.7.\n    DEFINES += QT_NO_FOREACH\n}\n\nSOURCES =   main.cpp \\\n            qgtkintegration.cpp \\\n            qgtkbackingstore.cpp \\\n            qgtkscreen.cpp \\\n            qgtkwindow.cpp \\\n            qgtkwindow_keyboard.cpp \\\n            qgtkwindow_mouse.cpp \\\n            qgtkwindow_touch.cpp \\\n            qgtkwindow_render.cpp \\\n            qgtkwindow_gesture.cpp \\\n            qgtktheme.cpp \\\n            qgtksystemtrayicon.cpp \\\n            qgtkmenubar.cpp \\\n            qgtkmenu.cpp \\\n            qgtkmenuitem.cpp \\\n            qgtkhelpers.cpp \\\n            qgtk3dialoghelpers.cpp \\\n            qgtkopenglcontext.cpp \\\n            qgtkopenglcontext_wayland.cpp \\\n            qgtkopenglcontext_x11.cpp \\\n            qgtkcursor.cpp \\\n            qgtkeventdispatcher.cpp \\\n            qgtkclipboard.cpp \\\n            qgtkservices.cpp\n\nHEADERS =   qgtkintegration.h \\\n            qgtkbackingstore.h \\\n            qgtkscreen.h \\\n            qgtkwindow.h \\\n            qgtktheme.h \\\n            qgtksystemtrayicon.h \\\n            qgtkmenubar.h \\\n            qgtkmenu.h \\\n            qgtkmenuitem.h \\\n            qgtkhelpers.h \\\n            qgtk3dialoghelpers.h \\\n            qgtkopenglcontext.h \\\n            qgtkcursor.h \\\n            qgtkeventdispatcher.h \\\n            qgtkclipboard.h \\\n            qgtkservices.h\n\n# CSystrace\nDEFINES += DISABLE_TRACE_CODE\n#LIBS += -lrt\n#SOURCES +=  CSystrace.cpp\nHEADERS +=  CSystrace.h \\\n            CTraceMessages.h\n\nQT += gtkextras\n#INCLUDEPATH += ../gtkextras\n\nCONFIG += qpa/genericunixfontdatabase\n\nLIBS += -lX11-xcb -lxcb\n\nCONFIG += link_pkgconfig\nPKGCONFIG_PRIVATE += gdk-3.0 gtk+-3.0 libnotify\n\n# for GL\nPKGCONFIG_PRIVATE += egl\n\nCONFIG += no_keywords\n\nPLUGIN_TYPE = platforms\nPLUGIN_CLASS_NAME = QGtkIntegrationPlugin\n!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = -\nload(qt_plugin)\n"
  },
  {
    "path": "src/platform-plugin/qgtk3dialoghelpers.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2016 The Qt Company Ltd.\n** Contact: https://www.qt.io/licensing/\n**\n** This file is part of the plugins of the Qt Toolkit.\n**\n** $QT_BEGIN_LICENSE:LGPL$\n** Commercial License Usage\n** Licensees holding valid commercial Qt licenses may use this file in\n** accordance with the commercial license agreement provided with the\n** Software or, alternatively, in accordance with the terms contained in\n** a written agreement between you and The Qt Company. For licensing terms\n** and conditions see https://www.qt.io/terms-conditions. For further\n** information use the contact form at https://www.qt.io/contact-us.\n**\n** GNU Lesser General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n** $QT_END_LICENSE$\n**\n****************************************************************************/\n\n#include \"qgtk3dialoghelpers.h\"\n#include \"qgtktheme.h\"\n#include \"qgtkwindow.h\"\n\n#include <qeventloop.h>\n#include <qwindow.h>\n#include <qcolor.h>\n#include <qdebug.h>\n#include <qfont.h>\n\n#include <private/qguiapplication_p.h>\n#include <qpa/qplatformfontdatabase.h>\n\n#undef signals\n#include <gtk/gtk.h>\n#include <gdk/gdk.h>\n#include <gdk/gdkx.h>\n#include <pango/pango.h>\n\nQT_BEGIN_NAMESPACE\n\nstatic const char *standardButtonText(int button)\n{\n    return QGtkTheme::defaultStandardButtonText(button).toUtf8();\n}\n\nQGtk3Dialog::QGtk3Dialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget)\n{\n    g_signal_connect_swapped(G_OBJECT(gtkWidget), \"response\", G_CALLBACK(onResponse), this);\n    g_signal_connect(G_OBJECT(gtkWidget), \"delete-event\", G_CALLBACK(gtk_widget_hide_on_delete), NULL);\n}\n\nQGtk3Dialog::~QGtk3Dialog()\n{\n    gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));\n    gtk_widget_destroy(gtkWidget);\n}\n\nGtkDialog *QGtk3Dialog::gtkDialog() const\n{\n    return GTK_DIALOG(gtkWidget);\n}\n\nvoid QGtk3Dialog::exec()\n{\n    if (modality() == Qt::ApplicationModal) {\n        // transient to the active window (best we can do really).\n        QWindow *focusWin = QGuiApplication::focusWindow();\n        if (focusWin) {\n            QGtkWindow *parentWin = static_cast<QGtkWindow*>(focusWin->handle());\n            gtk_window_set_transient_for(GTK_WINDOW(gtkWidget), GTK_WINDOW(parentWin->gtkWindow().get()));\n        }\n        // block input to the whole app, including other GTK dialogs\n        // \n        gtk_dialog_run(gtkDialog());\n    } else {\n        // block input to the window, allow input to other GTK dialogs\n        QEventLoop loop;\n        connect(this, SIGNAL(accept()), &loop, SLOT(quit()));\n        connect(this, SIGNAL(reject()), &loop, SLOT(quit()));\n        loop.exec();\n    }\n}\n\nbool QGtk3Dialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)\n{\n    if (parent) {\n        connect(parent, &QWindow::destroyed, this, &QGtk3Dialog::onParentWindowDestroyed,\n                Qt::UniqueConnection);\n    }\n    setParent(parent);\n    setFlags(flags);\n    setModality(modality);\n\n    gtk_widget_realize(gtkWidget); // creates X window\n\n    GdkWindow *gdkWindow = gtk_widget_get_window(gtkWidget);\n    if (parent)  {\n        QGtkWindow *parentWin = static_cast<QGtkWindow*>(parent->handle());\n        gtk_window_set_transient_for(GTK_WINDOW(gtkWidget), GTK_WINDOW(parentWin->gtkWindow().get()));\n    }\n\n    if (modality != Qt::NonModal) {\n        gdk_window_set_modal_hint(gdkWindow, true);\n        QGuiApplicationPrivate::showModalWindow(this);\n    }\n\n    gtk_widget_show(gtkWidget);\n    gdk_window_focus(gdkWindow, GDK_CURRENT_TIME);\n    return true;\n}\n\nvoid QGtk3Dialog::hide()\n{\n    QGuiApplicationPrivate::hideModalWindow(this);\n    gtk_widget_hide(gtkWidget);\n}\n\nvoid QGtk3Dialog::onResponse(QGtk3Dialog *dialog, int response)\n{\n    if (response == GTK_RESPONSE_OK)\n        Q_EMIT dialog->accept();\n    else\n        Q_EMIT dialog->reject();\n}\n\nvoid QGtk3Dialog::onParentWindowDestroyed()\n{\n    // The QGtk3*DialogHelper classes own this object. Make sure the parent doesn't delete it.\n    setParent(0);\n}\n\nQGtk3ColorDialogHelper::QGtk3ColorDialogHelper()\n{\n    d.reset(new QGtk3Dialog(gtk_color_chooser_dialog_new(\"\", 0)));\n    connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted()));\n    connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject()));\n\n    g_signal_connect_swapped(d->gtkDialog(), \"notify::rgba\", G_CALLBACK(onColorChanged), this);\n}\n\nQGtk3ColorDialogHelper::~QGtk3ColorDialogHelper()\n{\n}\n\nbool QGtk3ColorDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)\n{\n    applyOptions();\n    return d->show(flags, modality, parent);\n}\n\nvoid QGtk3ColorDialogHelper::exec()\n{\n    d->exec();\n}\n\nvoid QGtk3ColorDialogHelper::hide()\n{\n    d->hide();\n}\n\nvoid QGtk3ColorDialogHelper::setCurrentColor(const QColor &color)\n{\n    GtkDialog *gtkDialog = d->gtkDialog();\n    if (color.alpha() < 255)\n        gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(gtkDialog), true);\n    GdkRGBA gdkColor;\n    gdkColor.red = color.redF();\n    gdkColor.green = color.greenF();\n    gdkColor.blue = color.blueF();\n    gdkColor.alpha = color.alphaF();\n    gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(gtkDialog), &gdkColor);\n}\n\nQColor QGtk3ColorDialogHelper::currentColor() const\n{\n    GtkDialog *gtkDialog = d->gtkDialog();\n    GdkRGBA gdkColor;\n    gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(gtkDialog), &gdkColor);\n    return QColor::fromRgbF(gdkColor.red, gdkColor.green, gdkColor.blue, gdkColor.alpha);\n}\n\nvoid QGtk3ColorDialogHelper::onAccepted()\n{\n    Q_EMIT accept();\n}\n\nvoid QGtk3ColorDialogHelper::onColorChanged(QGtk3ColorDialogHelper *dialog)\n{\n    Q_EMIT dialog->currentColorChanged(dialog->currentColor());\n}\n\nvoid QGtk3ColorDialogHelper::applyOptions()\n{\n    GtkDialog *gtkDialog = d->gtkDialog();\n    gtk_window_set_title(GTK_WINDOW(gtkDialog), options()->windowTitle().toUtf8());\n\n    gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(gtkDialog), options()->testOption(QColorDialogOptions::ShowAlphaChannel));\n}\n\nQGtk3FileDialogHelper::QGtk3FileDialogHelper()\n{\n    d.reset(new QGtk3Dialog(gtk_file_chooser_dialog_new(\"\", 0,\n                                                        GTK_FILE_CHOOSER_ACTION_OPEN,\n                                                        standardButtonText(QPlatformDialogHelper::Cancel), GTK_RESPONSE_CANCEL,\n                                                        standardButtonText(QPlatformDialogHelper::Ok), GTK_RESPONSE_OK,\n                                                        NULL)));\n\n    connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted()));\n    connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject()));\n\n    g_signal_connect(GTK_FILE_CHOOSER(d->gtkDialog()), \"selection-changed\", G_CALLBACK(onSelectionChanged), this);\n    g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), \"current-folder-changed\", G_CALLBACK(onCurrentFolderChanged), this);\n    g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), \"notify::filter\", G_CALLBACK(onFilterChanged), this);\n}\n\nQGtk3FileDialogHelper::~QGtk3FileDialogHelper()\n{\n}\n\nbool QGtk3FileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)\n{\n    _dir.clear();\n    _selection.clear();\n\n    applyOptions();\n    return d->show(flags, modality, parent);\n}\n\nvoid QGtk3FileDialogHelper::exec()\n{\n    d->exec();\n}\n\nvoid QGtk3FileDialogHelper::hide()\n{\n    // After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder()\n    // & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual\n    // values before hiding the dialog\n    _dir = directory();\n    _selection = selectedFiles();\n\n    d->hide();\n}\n\nbool QGtk3FileDialogHelper::defaultNameFilterDisables() const\n{\n    return false;\n}\n\nvoid QGtk3FileDialogHelper::setDirectory(const QUrl &directory)\n{\n    GtkDialog *gtkDialog = d->gtkDialog();\n    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), directory.toLocalFile().toUtf8());\n}\n\nQUrl QGtk3FileDialogHelper::directory() const\n{\n    // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder()\n    // returns a bogus value -> return the cached value before hiding\n    if (!_dir.isEmpty())\n        return _dir;\n\n    QString ret;\n    GtkDialog *gtkDialog = d->gtkDialog();\n    gchar *folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(gtkDialog));\n    if (folder) {\n        ret = QString::fromUtf8(folder);\n        g_free(folder);\n    }\n    return QUrl::fromLocalFile(ret);\n}\n\nvoid QGtk3FileDialogHelper::selectFile(const QUrl &filename)\n{\n    GtkDialog *gtkDialog = d->gtkDialog();\n    if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {\n        QFileInfo fi(filename.toLocalFile());\n        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), fi.path().toUtf8());\n        gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(gtkDialog), fi.fileName().toUtf8());\n    } else {\n        gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), filename.toLocalFile().toUtf8());\n    }\n}\n\nQList<QUrl> QGtk3FileDialogHelper::selectedFiles() const\n{\n    // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames()\n    // returns a bogus value -> return the cached value before hiding\n    if (!_selection.isEmpty())\n        return _selection;\n\n    QList<QUrl> selection;\n    GtkDialog *gtkDialog = d->gtkDialog();\n    GSList *filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(gtkDialog));\n    for (GSList *it  = filenames; it; it = it->next)\n        selection += QUrl::fromLocalFile(QString::fromUtf8((const char*)it->data));\n    g_slist_free(filenames);\n    return selection;\n}\n\nvoid QGtk3FileDialogHelper::setFilter()\n{\n    applyOptions();\n}\n\nvoid QGtk3FileDialogHelper::selectNameFilter(const QString &filter)\n{\n    GtkFileFilter *gtkFilter = _filters.value(filter);\n    if (gtkFilter) {\n        GtkDialog *gtkDialog = d->gtkDialog();\n        gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter);\n    }\n}\n\nQString QGtk3FileDialogHelper::selectedNameFilter() const\n{\n    GtkDialog *gtkDialog = d->gtkDialog();\n    GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(gtkDialog));\n    return _filterNames.value(gtkFilter);\n}\n\nvoid QGtk3FileDialogHelper::onAccepted()\n{\n    Q_EMIT accept();\n}\n\nvoid QGtk3FileDialogHelper::onSelectionChanged(GtkDialog *gtkDialog, QGtk3FileDialogHelper *helper)\n{\n    QString selection;\n    gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog));\n    if (filename) {\n        selection = QString::fromUtf8(filename);\n        g_free(filename);\n    }\n    Q_EMIT helper->currentChanged(QUrl::fromLocalFile(selection));\n}\n\nvoid QGtk3FileDialogHelper::onCurrentFolderChanged(QGtk3FileDialogHelper *dialog)\n{\n    Q_EMIT dialog->directoryEntered(dialog->directory());\n}\n\nvoid QGtk3FileDialogHelper::onFilterChanged(QGtk3FileDialogHelper *dialog)\n{\n    Q_EMIT dialog->filterSelected(dialog->selectedNameFilter());\n}\n\nstatic GtkFileChooserAction gtkFileChooserAction(const QSharedPointer<QFileDialogOptions> &options)\n{\n    switch (options->fileMode()) {\n    case QFileDialogOptions::AnyFile:\n    case QFileDialogOptions::ExistingFile:\n    case QFileDialogOptions::ExistingFiles:\n        if (options->acceptMode() == QFileDialogOptions::AcceptOpen)\n            return GTK_FILE_CHOOSER_ACTION_OPEN;\n        else\n            return GTK_FILE_CHOOSER_ACTION_SAVE;\n    case QFileDialogOptions::Directory:\n    case QFileDialogOptions::DirectoryOnly:\n    default:\n        if (options->acceptMode() == QFileDialogOptions::AcceptOpen)\n            return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;\n        else\n            return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;\n    }\n}\n\nvoid QGtk3FileDialogHelper::applyOptions()\n{\n    GtkDialog *gtkDialog = d->gtkDialog();\n    const QSharedPointer<QFileDialogOptions> &opts = options();\n\n    gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8());\n    gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(gtkDialog), true);\n\n    const GtkFileChooserAction action = gtkFileChooserAction(opts);\n    gtk_file_chooser_set_action(GTK_FILE_CHOOSER(gtkDialog), action);\n\n    const bool selectMultiple = opts->fileMode() == QFileDialogOptions::ExistingFiles;\n    gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(gtkDialog), selectMultiple);\n\n    const bool confirmOverwrite = !opts->testOption(QFileDialogOptions::DontConfirmOverwrite);\n    gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(gtkDialog), confirmOverwrite);\n\n    const QStringList nameFilters = opts->nameFilters();\n    if (!nameFilters.isEmpty())\n        setNameFilters(nameFilters);\n\n    if (opts->initialDirectory().isLocalFile())\n        setDirectory(opts->initialDirectory());\n\n    for (const QUrl &filename : opts->initiallySelectedFiles())\n        selectFile(filename);\n\n    const QString initialNameFilter = opts->initiallySelectedNameFilter();\n    if (!initialNameFilter.isEmpty())\n        selectNameFilter(initialNameFilter);\n\n    GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK);\n    if (acceptButton) {\n        if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept))\n            gtk_button_set_label(GTK_BUTTON(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8());\n        else if (opts->acceptMode() == QFileDialogOptions::AcceptOpen)\n            gtk_button_set_label(GTK_BUTTON(acceptButton), standardButtonText(QPlatformDialogHelper::Open));\n        else\n            gtk_button_set_label(GTK_BUTTON(acceptButton), standardButtonText(QPlatformDialogHelper::Save));\n    }\n\n    GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL);\n    if (rejectButton) {\n        if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject))\n            gtk_button_set_label(GTK_BUTTON(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8());\n        else\n            gtk_button_set_label(GTK_BUTTON(rejectButton), standardButtonText(QPlatformDialogHelper::Cancel));\n    }\n}\n\nvoid QGtk3FileDialogHelper::setNameFilters(const QStringList &filters)\n{\n    GtkDialog *gtkDialog = d->gtkDialog();\n    for (GtkFileFilter *filter : _filters)\n        gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(gtkDialog), filter);\n\n    _filters.clear();\n    _filterNames.clear();\n\n    for (const QString &filter : filters) {\n        GtkFileFilter *gtkFilter = gtk_file_filter_new();\n        const QString name = filter.left(filter.indexOf(QLatin1Char('(')));\n        const QStringList extensions = cleanFilterList(filter);\n\n        gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(\", \")).toUtf8() : name.toUtf8());\n        for (const QString &ext : extensions)\n            gtk_file_filter_add_pattern(gtkFilter, ext.toUtf8());\n\n        gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter);\n\n        _filters.insert(filter, gtkFilter);\n        _filterNames.insert(gtkFilter, filter);\n    }\n}\n\nQGtk3FontDialogHelper::QGtk3FontDialogHelper()\n{\n    d.reset(new QGtk3Dialog(gtk_font_chooser_dialog_new(\"\", 0)));\n    connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted()));\n    connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject()));\n\n    g_signal_connect_swapped(d->gtkDialog(), \"notify::font\", G_CALLBACK(onFontChanged), this);\n}\n\nQGtk3FontDialogHelper::~QGtk3FontDialogHelper()\n{\n}\n\nbool QGtk3FontDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)\n{\n    applyOptions();\n    return d->show(flags, modality, parent);\n}\n\nvoid QGtk3FontDialogHelper::exec()\n{\n    d->exec();\n}\n\nvoid QGtk3FontDialogHelper::hide()\n{\n    d->hide();\n}\n\nstatic QString qt_fontToString(const QFont &font)\n{\n    PangoFontDescription *desc = pango_font_description_new();\n    pango_font_description_set_size(desc, (font.pointSizeF() > 0.0 ? font.pointSizeF() : QFontInfo(font).pointSizeF()) * PANGO_SCALE);\n    pango_font_description_set_family(desc, QFontInfo(font).family().toUtf8());\n\n    int weight = font.weight();\n    if (weight >= QFont::Black)\n        pango_font_description_set_weight(desc, PANGO_WEIGHT_HEAVY);\n    else if (weight >= QFont::ExtraBold)\n        pango_font_description_set_weight(desc, PANGO_WEIGHT_ULTRABOLD);\n    else if (weight >= QFont::Bold)\n        pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);\n    else if (weight >= QFont::DemiBold)\n        pango_font_description_set_weight(desc, PANGO_WEIGHT_SEMIBOLD);\n    else if (weight >= QFont::Medium)\n        pango_font_description_set_weight(desc, PANGO_WEIGHT_MEDIUM);\n    else if (weight >= QFont::Normal)\n        pango_font_description_set_weight(desc, PANGO_WEIGHT_NORMAL);\n    else if (weight >= QFont::Light)\n        pango_font_description_set_weight(desc, PANGO_WEIGHT_LIGHT);\n    else if (weight >= QFont::ExtraLight)\n        pango_font_description_set_weight(desc, PANGO_WEIGHT_ULTRALIGHT);\n    else\n        pango_font_description_set_weight(desc, PANGO_WEIGHT_THIN);\n\n    int style = font.style();\n    if (style == QFont::StyleItalic)\n        pango_font_description_set_style(desc, PANGO_STYLE_ITALIC);\n    else if (style == QFont::StyleOblique)\n        pango_font_description_set_style(desc, PANGO_STYLE_OBLIQUE);\n    else\n        pango_font_description_set_style(desc, PANGO_STYLE_NORMAL);\n\n    char *str = pango_font_description_to_string(desc);\n    QString name = QString::fromUtf8(str);\n    pango_font_description_free(desc);\n    g_free(str);\n    return name;\n}\n\nstatic QFont qt_fontFromString(const QString &name)\n{\n    QFont font;\n    PangoFontDescription *desc = pango_font_description_from_string(name.toUtf8());\n    font.setPointSizeF(static_cast<float>(pango_font_description_get_size(desc)) / PANGO_SCALE);\n\n    QString family = QString::fromUtf8(pango_font_description_get_family(desc));\n    if (!family.isEmpty())\n        font.setFamily(family);\n\n    const int weight = pango_font_description_get_weight(desc);\n    font.setWeight(QPlatformFontDatabase::weightFromInteger(weight));\n\n    PangoStyle style = pango_font_description_get_style(desc);\n    if (style == PANGO_STYLE_ITALIC)\n        font.setStyle(QFont::StyleItalic);\n    else if (style == PANGO_STYLE_OBLIQUE)\n        font.setStyle(QFont::StyleOblique);\n    else\n        font.setStyle(QFont::StyleNormal);\n\n    pango_font_description_free(desc);\n    return font;\n}\n\nvoid QGtk3FontDialogHelper::setCurrentFont(const QFont &font)\n{\n    GtkFontChooser *gtkDialog = GTK_FONT_CHOOSER(d->gtkDialog());\n    gtk_font_chooser_set_font(gtkDialog, qt_fontToString(font).toUtf8());\n}\n\nQFont QGtk3FontDialogHelper::currentFont() const\n{\n    GtkFontChooser *gtkDialog = GTK_FONT_CHOOSER(d->gtkDialog());\n    gchar *name = gtk_font_chooser_get_font(gtkDialog);\n    QFont font = qt_fontFromString(QString::fromUtf8(name));\n    g_free(name);\n    return font;\n}\n\nvoid QGtk3FontDialogHelper::onAccepted()\n{\n    Q_EMIT accept();\n}\n\nvoid QGtk3FontDialogHelper::onFontChanged(QGtk3FontDialogHelper *dialog)\n{\n    Q_EMIT dialog->currentFontChanged(dialog->currentFont());\n}\n\nvoid QGtk3FontDialogHelper::applyOptions()\n{\n    GtkDialog *gtkDialog = d->gtkDialog();\n    const QSharedPointer<QFontDialogOptions> &opts = options();\n\n    gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8());\n}\n\nQT_END_NAMESPACE\n\n"
  },
  {
    "path": "src/platform-plugin/qgtk3dialoghelpers.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2016 The Qt Company Ltd.\n** Contact: https://www.qt.io/licensing/\n**\n** This file is part of the plugins of the Qt Toolkit.\n**\n** $QT_BEGIN_LICENSE:LGPL$\n** Commercial License Usage\n** Licensees holding valid commercial Qt licenses may use this file in\n** accordance with the commercial license agreement provided with the\n** Software or, alternatively, in accordance with the terms contained in\n** a written agreement between you and The Qt Company. For licensing terms\n** and conditions see https://www.qt.io/terms-conditions. For further\n** information use the contact form at https://www.qt.io/contact-us.\n**\n** GNU Lesser General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n** $QT_END_LICENSE$\n**\n****************************************************************************/\n\n#ifndef QGTK3DIALOGHELPERS_H\n#define QGTK3DIALOGHELPERS_H\n\n#include <QtCore/qhash.h>\n#include <QtCore/qlist.h>\n#include <QtCore/qurl.h>\n#include <QtCore/qscopedpointer.h>\n#include <QtCore/qstring.h>\n#include <QtGui/qwindow.h>\n#include <qpa/qplatformdialoghelper.h>\n\n#include <gtk/gtk.h>\n\nQT_BEGIN_NAMESPACE\n\nclass QColor;\n\nclass QGtk3Dialog : public QWindow\n{\n    Q_OBJECT\n\npublic:\n    QGtk3Dialog(GtkWidget *gtkWidget);\n    ~QGtk3Dialog();\n\n    GtkDialog *gtkDialog() const;\n\n    void exec();\n    bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);\n    void hide();\n\nQ_SIGNALS:\n    void accept();\n    void reject();\n\nprotected:\n    static void onResponse(QGtk3Dialog *dialog, int response);\n\nprivate Q_SLOTS:\n    void onParentWindowDestroyed();\n\nprivate:\n    GtkWidget *gtkWidget;\n};\n\n\nclass QGtk3ColorDialogHelper : public QPlatformColorDialogHelper\n{\n    Q_OBJECT\n\npublic:\n    QGtk3ColorDialogHelper();\n    ~QGtk3ColorDialogHelper();\n\n    bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE;\n    void exec() Q_DECL_OVERRIDE;\n    void hide() Q_DECL_OVERRIDE;\n\n    void setCurrentColor(const QColor &color) Q_DECL_OVERRIDE;\n    QColor currentColor() const Q_DECL_OVERRIDE;\n\nprivate Q_SLOTS:\n    void onAccepted();\n\nprivate:\n    static void onColorChanged(QGtk3ColorDialogHelper *helper);\n    void applyOptions();\n\n    QScopedPointer<QGtk3Dialog> d;\n};\n\nclass QGtk3FileDialogHelper : public QPlatformFileDialogHelper\n{\n    Q_OBJECT\n\npublic:\n    QGtk3FileDialogHelper();\n    ~QGtk3FileDialogHelper();\n\n    bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE;\n    void exec() Q_DECL_OVERRIDE;\n    void hide() Q_DECL_OVERRIDE;\n\n    bool defaultNameFilterDisables() const Q_DECL_OVERRIDE;\n    void setDirectory(const QUrl &directory) Q_DECL_OVERRIDE;\n    QUrl directory() const Q_DECL_OVERRIDE;\n    void selectFile(const QUrl &filename) Q_DECL_OVERRIDE;\n    QList<QUrl> selectedFiles() const Q_DECL_OVERRIDE;\n    void setFilter() Q_DECL_OVERRIDE;\n    void selectNameFilter(const QString &filter) Q_DECL_OVERRIDE;\n    QString selectedNameFilter() const Q_DECL_OVERRIDE;\n\nprivate Q_SLOTS:\n    void onAccepted();\n\nprivate:\n    static void onSelectionChanged(GtkDialog *dialog, QGtk3FileDialogHelper *helper);\n    static void onCurrentFolderChanged(QGtk3FileDialogHelper *helper);\n    static void onFilterChanged(QGtk3FileDialogHelper *helper);\n    void applyOptions();\n    void setNameFilters(const QStringList &filters);\n\n    QUrl _dir;\n    QList<QUrl> _selection;\n    QHash<QString, GtkFileFilter*> _filters;\n    QHash<GtkFileFilter*, QString> _filterNames;\n    QScopedPointer<QGtk3Dialog> d;\n};\n\nclass QGtk3FontDialogHelper : public QPlatformFontDialogHelper\n{\n    Q_OBJECT\n\npublic:\n    QGtk3FontDialogHelper();\n    ~QGtk3FontDialogHelper();\n\n    bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE;\n    void exec() Q_DECL_OVERRIDE;\n    void hide() Q_DECL_OVERRIDE;\n\n    void setCurrentFont(const QFont &font) Q_DECL_OVERRIDE;\n    QFont currentFont() const Q_DECL_OVERRIDE;\n\nprivate Q_SLOTS:\n    void onAccepted();\n\nprivate:\n    static void onFontChanged(QGtk3FontDialogHelper *helper);\n    void applyOptions();\n\n    QScopedPointer<QGtk3Dialog> d;\n};\n\nQT_END_NAMESPACE\n\n#endif // QGTK3DIALOGHELPERS_H\n"
  },
  {
    "path": "src/platform-plugin/qgtkbackingstore.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkbackingstore.h\"\n#include \"qgtkintegration.h\"\n#include \"qgtkwindow.h\"\n#include \"qscreen.h\"\n#include <QtCore/qdebug.h>\n#include <qpa/qplatformscreen.h>\n#include <private/qguiapplication_p.h>\n\n#include \"CSystrace.h\"\n\n// we have no_keywords, but this header uses one.\n#define foreach Q_FOREACH\n#include <private/qhighdpiscaling_p.h>\n#undef foreach\n\nQGtkBackingStore::QGtkBackingStore(QWindow *window)\n    : QPlatformBackingStore(window)\n    , m_paintImage(nullptr)\n{\n}\n\nQGtkBackingStore::~QGtkBackingStore()\n{\n}\n\nQPaintDevice *QGtkBackingStore::paintDevice()\n{\n    return m_paintImage;\n}\n\nQImage QGtkBackingStore::toImage() const\n{\n    return static_cast<QGtkWindow*>(window()->handle())->currentFrameImage();\n}\n\nvoid QGtkBackingStore::beginPaint(const QRegion &region)\n{\n    TRACE_EVENT_ASYNC_BEGIN0(\"gfx\", \"QGtkBackingStore::paint\", this);\n    Q_UNUSED(region);\n    if (!m_paintImage)\n        m_paintImage = static_cast<QGtkWindow*>(window()->handle())->beginUpdateFrame(\"beginPaint\");\n}\n\nvoid QGtkBackingStore::endPaint()\n{\n    Q_ASSERT(m_paintImage);\n    static_cast<QGtkWindow*>(window()->handle())->endUpdateFrame(\"endPaint\");\n    m_paintImage = nullptr;\n    TRACE_EVENT_ASYNC_END0(\"gfx\", \"QGtkBackingStore::paint\", this);\n}\n\nvoid QGtkBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n                                       QPlatformTextureList *textures, bool translucentBackground)\n#else\n                                       QPlatformTextureList *textures, QOpenGLContext *context, bool translucentBackground)\n#endif\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkBackingStore::composeAndFlush\");\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n    QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground);\n#else\n    QPlatformBackingStore::composeAndFlush(window, region, offset, textures, context, translucentBackground);\n#endif\n}\n\nvoid QGtkBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkBackingStore::flush\");\n    static_cast<QGtkWindow*>(window->handle())->invalidateRegion(region.translated(offset));\n}\n\nvoid QGtkBackingStore::resize(const QSize &size, const QRegion &)\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkBackingStore::resize\");\n    QGtkWindow *qgwin = static_cast<QGtkWindow*>(window()->handle());\n\n    QImage *image = qgwin->beginUpdateFrame(\"resize\");\n    qreal dpr = window()->devicePixelRatio() / QHighDpiScaling::factor(window());\n    QSize realSize = size * dpr;\n    QImage::Format format = QGuiApplication::primaryScreen()->handle()->format();\n    if (image->size() != realSize) {\n        *image = QImage(realSize, format);\n        image->setDevicePixelRatio(dpr);\n    }\n    qgwin->endUpdateFrame(\"resize\");\n}\n\nbool QGtkBackingStore::scroll(const QRegion &region, int dx, int dy)\n{\n    // ### temporarily disabled\n    //\n    // this helps to accelerate widget scrolling by copying a region of the\n    // backing store rather than re-rendering them from scratch. however, it's\n    // currently not working quite right because of bad event ordering.\n    //\n    // the way this is supposed to work:\n    // QWidget::scroll()\n    //      -> backingStore->scroll()\n    //          -> QPABackingStore->scroll()\n    //          -> QWidget::update()\n    // QPABackingStore::beginPaint\n    // QWidget::paintEvent\n    // ...\n    // gtk_render_stuff\n    //\n    // But events are arriving out of order at the moment (perhaps due to event\n    // dispatcher? perhaps due to something else?) and as a result we get:\n    // QWidget::scroll\n    //      -> backingStore->scroll\n    //          -> QPABackingStore->scroll\n    //          -> QWidget::update\n    // gtk_render_stuff <--- OOPS! didn't render the area that we couldn't // scroll yet!\n    // QPABackingStore::beginPaint\n    // QWidget::paintEvent\n    // gtk_render_stuff <- eventually consistent, but looks bad.\n    return false;\n\n    TRACE_EVENT0(\"gfx\", \"QGtkBackingStore::scroll\");\n    extern void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &);\n    QGtkWindow *qgwin = static_cast<QGtkWindow*>(window()->handle());\n    const qreal dpr = qgwin->devicePixelRatio();\n    const QPoint delta = QPoint(dx * dpr, dy * dpr);\n    QImage *image = qgwin->beginUpdateFrame(\"scroll\");\n\n    for (const QRect &rect : region.rects()) {\n        qt_scrollRectInImage(*image, QRect(rect.topLeft() * dpr, rect.size() * dpr), delta);\n    }\n\n    qgwin->endUpdateFrame(\"scroll\");\n    QRegion uregion = region.united(region.translated(delta));\n    qgwin->invalidateRegion(uregion);\n\n    return true;\n}\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkbackingstore.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QBACKINGSTORE_GTK_H\n#define QBACKINGSTORE_GTK_H\n\n#include <qpa/qplatformbackingstore.h>\n#include <qpa/qplatformwindow.h>\n#include <QtGui/QImage>\n\nQT_BEGIN_NAMESPACE\n\nclass QGtkBackingStore : public QPlatformBackingStore\n{\npublic:\n    QGtkBackingStore(QWindow *window);\n    ~QGtkBackingStore();\n\n    QPaintDevice *paintDevice() override;\n    void beginPaint(const QRegion &region) override;\n    void endPaint() override;\n    void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n                         QPlatformTextureList *textures, bool translucentBackground) override;\n#else\n                         QPlatformTextureList *textures, QOpenGLContext *context, bool translucentBackground) override;\n#endif\n    void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;\n    void resize(const QSize &size, const QRegion &staticContents) override;\n    QImage toImage() const override;\n    bool scroll(const QRegion &region, int dx, int dy) override;\n\nprivate:\n    QImage *m_paintImage;\n};\n\nQT_END_NAMESPACE\n\n#endif\n"
  },
  {
    "path": "src/platform-plugin/qgtkclipboard.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkclipboard.h\"\n#include \"qgtkhelpers.h\"\n\n#include <gtk/gtk.h>\n\n#include <QtCore/qloggingcategory.h>\n\nQ_LOGGING_CATEGORY(lcClipboard, \"qt.qpa.gtk.clipboard\");\n\nenum TargetTypes {\n    TargetTypeText = 1,\n    TargetTypeImage = 2\n};\n\nQGtkClipboard::QGtkClipboard(QObject *parent)\n    : QObject(parent)\n    , m_clipData(QClipboard::Clipboard)\n    , m_selData(QClipboard::Selection)\n{\n    QObject::connect(&m_clipData, &QGtkClipboardData::changed, this, [=](){ emitChanged(QClipboard::Clipboard); qCDebug(lcClipboard) << \"Clipboard changed\"; });\n    QObject::connect(&m_selData, &QGtkClipboardData::changed, this, [=](){ emitChanged(QClipboard::Selection); qCDebug(lcClipboard) << \"Selection changed\"; });\n}\n\nQGtkClipboard::~QGtkClipboard()\n{\n}\n\nQGtkClipboardData *QGtkClipboard::mimeForMode(QClipboard::Mode mode) const\n{\n    switch (mode) {\n        case QClipboard::Clipboard:\n            return const_cast<QGtkClipboardData*>(&m_clipData);\n        case QClipboard::Selection:\n            return const_cast<QGtkClipboardData*>(&m_selData);\n        default:\n            Q_UNREACHABLE();\n    }\n}\n\nQMimeData *QGtkClipboard::mimeData(QClipboard::Mode mode)\n{\n    if (!supportsMode(mode))\n        return 0;\n\n    QGtkClipboardData *m = mimeForMode(mode);\n    return m->mimeData();\n}\n\nvoid QGtkClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)\n{\n    if (!supportsMode(mode))\n        return;\n    QGtkClipboardData *m = mimeForMode(mode);\n    m->setData(data);\n    qCDebug(lcClipboard) << \"setMimeData changed to \" << data;\n    emitChanged(mode);\n}\n\nbool QGtkClipboard::supportsMode(QClipboard::Mode mode) const\n{\n    switch (mode) {\n    case QClipboard::Clipboard:\n    case QClipboard::Selection:\n        return true;\n    default:\n        return false;\n    }\n}\n\nbool QGtkClipboard::ownsMode(QClipboard::Mode mode) const\n{\n    if (!supportsMode(mode))\n        return false;\n    QGtkClipboardData *m = mimeForMode(mode);\n    return m->ownsMode();\n}\n\nQGtkClipboardData::QGtkClipboardData(QClipboard::Mode clipboardMode)\n    : m_mode(clipboardMode)\n{\n    switch (clipboardMode) {\n    case QClipboard::Clipboard:\n        m_clipboard = gtk_clipboard_get(gdk_atom_intern(\"CLIPBOARD\", TRUE));\n        break;\n    case QClipboard::Selection:\n        m_clipboard = gtk_clipboard_get(gdk_atom_intern(\"PRIMARY\", TRUE));\n        break;\n    default:\n        Q_UNREACHABLE();\n    }\n}\n\nQGtkClipboardData::~QGtkClipboardData()\n{\n    gtk_clipboard_set_can_store(m_clipboard, NULL, 0);\n    delete m_localData;\n    delete m_systemData;\n}\n\nbool QGtkClipboardData::ownsMode() const\n{\n    return gtk_clipboard_get_owner(m_clipboard) != NULL;\n}\n\nstatic void getFun(GtkClipboard *, GtkSelectionData *selection_data, guint info, gpointer gtkClipboardData)\n{\n    QGtkClipboardData *gdata = static_cast<QGtkClipboardData*>(gtkClipboardData);\n    gdata->onLocalGet(selection_data, info);\n}\n\nstatic void clearFun(GtkClipboard*, gpointer gtkClipboardData)\n{\n    QGtkClipboardData *gdata = static_cast<QGtkClipboardData*>(gtkClipboardData);\n    gdata->onLocalClear();\n}\n\nvoid QGtkClipboardData::onLocalClear()\n{\n    qCDebug(lcClipboard) << \"Clear func\" << m_mode;\n    delete m_localData;\n    m_localData = nullptr;\n}\n\n// Request for the data from our set clipboard to give to a remote client\nvoid QGtkClipboardData::onLocalGet(GtkSelectionData *selection_data, guint info)\n{\n    qCDebug(lcClipboard) << \"Local get for \" << m_localData;\n    if (!m_localData) {\n        return;\n    }\n\n    if (info == TargetTypeText) {\n        gtk_selection_data_set_text(selection_data, m_localData->text().toUtf8().constData(), -1);\n    } else if (info == TargetTypeImage) {\n        QImage imageData = qvariant_cast<QImage>(m_localData->imageData());\n        gtk_selection_data_set_pixbuf(selection_data, qt_pixmapToPixbuf(QPixmap::fromImage(imageData)).get());\n    }\n}\n\n// Set our local clipboard, and inform the system about it\nvoid QGtkClipboardData::setData(QMimeData *data)\n{\n    qCDebug(lcClipboard) << \"Setting mime data \" << data << m_mode << (data ? data->formats() : QStringList());\n    if (!data || data->formats().isEmpty()) {\n        qCDebug(lcClipboard) << \"Clearing mime data\" << data;\n        gtk_clipboard_clear(m_clipboard);\n        return;\n    }\n\n    GtkTargetList *targetList = gtk_target_list_new(nullptr, 0);\n\n    if (data->hasText()) {\n        gtk_target_list_add_text_targets(targetList, TargetTypeText);\n    }\n\n    if (data->hasImage()) {\n        QImage imageData = qvariant_cast<QImage>(data->imageData());\n        gtk_target_list_add_image_targets(targetList, TargetTypeImage, TRUE);\n    }\n\n    // ### rich text? html? what else should we handle...\n\n    int targetCount = 0;\n    GtkTargetEntry *table = gtk_target_table_new_from_list(targetList, &targetCount);\n\n    if (targetCount > 0 && table) {\n        if (gtk_clipboard_set_with_data(\n            m_clipboard,\n            table,\n            targetCount,\n            getFun,\n            clearFun,\n            this\n        ) == TRUE) {\n            gtk_clipboard_set_can_store(m_clipboard, nullptr, 0);\n        } else {\n            qCWarning(lcClipboard) << \"Store FAILED\";\n        }\n    } else {\n        qCWarning(lcClipboard) << \"No targets\";\n    }\n\n    if (table) {\n        gtk_target_table_free(table, targetCount);\n    }\n\n    gtk_target_list_unref(targetList);\n    m_localData = data;\n}\n\nQMimeData *QGtkClipboardData::mimeData() const\n{\n    qCDebug(lcClipboard) << \"Getting data\" << m_mode << m_localData << m_systemData;\n    if (ownsMode()) {\n        qCDebug(lcClipboard) << \"Getting local data\";\n        return m_localData;\n    }\n\n    if (m_systemData) {\n        qCDebug(lcClipboard) << \"Clearing system data\";\n        m_systemData->clear();\n    } else {\n        qCDebug(lcClipboard) << \"Creating system data\";\n        const_cast<QGtkClipboardData*>(this)->m_systemData = new QMimeData();\n    }\n\n    GtkSelectionData *gsel = gtk_clipboard_wait_for_contents(m_clipboard, gdk_atom_intern(\"TARGETS\", TRUE));\n    if (gsel) {\n        if (gtk_selection_data_targets_include_image(gsel, FALSE)) {\n            qCDebug(lcClipboard) << \"Reading image data\";\n            QGtkRefPtr<GdkPixbuf> img = gtk_clipboard_wait_for_image(m_clipboard);\n            if (img.get()) {\n                QImage data = qt_pixbufToImage(img);\n                if (!data.isNull()) {\n                    m_systemData->setImageData(QVariant::fromValue(data));\n                    qCDebug(lcClipboard) << \"Read image \" << data;\n                }\n            }\n        }\n\n        if (gtk_selection_data_targets_include_text(gsel)) {\n\n            qCDebug(lcClipboard) << \"Reading text data\";\n            const gchar *rdata = gtk_clipboard_wait_for_text(m_clipboard);\n            if (rdata) {\n                QString data = QString::fromUtf8((rdata), strlen(rdata));\n                g_free((void*)rdata);\n                if (!data.isNull()) {\n                    m_systemData->setText(data);\n                    qCDebug(lcClipboard) << \"Read text \" << data;\n                }\n            }\n        }\n\n        gtk_selection_data_free(gsel);\n    }\n\n\n\n    return m_systemData;\n}\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkclipboard.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKCLIPBOARD_H\n#define QGTKCLIPBOARD_H\n\n#include <qpa/qplatformclipboard.h>\n#include <private/qdnd_p.h>\n\n#include <gtk/gtk.h>\n\nQT_BEGIN_NAMESPACE\n\nclass QGtkClipboardData : public QObject\n{\n    Q_OBJECT\npublic:\n    QGtkClipboardData(QClipboard::Mode clipboardMode);\n    ~QGtkClipboardData();\n\n    void setData(QMimeData *data);\n    QMimeData *mimeData() const;\n    bool ownsMode() const;\n\n    void onLocalClear();\n    void onLocalGet(GtkSelectionData *selection_data, guint info);\n\nQ_SIGNALS:\n    void changed();\n\nprivate:\n    QStringList formats() const;\n\n    GtkClipboard *m_clipboard = nullptr;\n    QMimeData *m_localData = nullptr; // app-local\n    QMimeData *m_systemData = nullptr; // system-global, remote\n    QClipboard::Mode m_mode;\n};\n\nclass QGtkClipboard : public QPlatformClipboard, QObject\n{\npublic:\n    QGtkClipboard(QObject *parent = 0);\n    ~QGtkClipboard();\n\n    QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override;\n    void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard) override;\n    bool supportsMode(QClipboard::Mode mode) const override;\n    bool ownsMode(QClipboard::Mode mode) const override;\n\nprivate:\n    QGtkClipboardData m_clipData;\n    QGtkClipboardData m_selData;\n    QGtkClipboardData *mimeForMode(QClipboard::Mode mode) const;\n};\n\nQT_END_NAMESPACE\n\n#endif // QGTKCLIPBOARD_H\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkcursor.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkcursor.h\"\n#include \"qgtkhelpers.h\"\n#include \"qgtkwindow.h\"\n\nQGtkCursor::QGtkCursor()\n    : QPlatformCursor()\n{\n}\n\nvoid QGtkCursor::changeCursor(QCursor *windowCursor, QWindow *window)\n{\n    Qt::CursorShape shape = Qt::BlankCursor;\n    if (windowCursor) {\n        shape = windowCursor->shape();\n    }\n\n    QByteArray gtkCursorName;\n    bool bitmapCursor = false;\n\n    switch (shape) {\n    case Qt::BlankCursor:\n    case Qt::CustomCursor:\n    case Qt::ArrowCursor:\n        gtkCursorName = \"default\";\n        break;\n    case Qt::UpArrowCursor:\n        gtkCursorName = \"default\";\n        break;\n    case Qt::CrossCursor:\n        gtkCursorName = \"crosshair\";\n        break;\n    case Qt::WaitCursor:\n        gtkCursorName = \"wait\";\n        break;\n    case Qt::IBeamCursor:\n        gtkCursorName = \"text\";\n        break;\n    case Qt::SizeVerCursor:\n        gtkCursorName = \"row-resize\";\n        break;\n    case Qt::SizeHorCursor:\n        gtkCursorName = \"col-resize\";\n        break;\n    case Qt::SizeBDiagCursor:\n        gtkCursorName = \"nesw-resize\";\n        break;\n    case Qt::SizeFDiagCursor:\n        gtkCursorName = \"nwse-resize\";\n        break;\n    case Qt::SizeAllCursor:\n        gtkCursorName = \"all-scroll\";\n        break;\n    case Qt::SplitVCursor:\n        gtkCursorName = \"ns-resize\";\n        break;\n    case Qt::SplitHCursor:\n        gtkCursorName = \"ew-resize\";\n        break;\n    case Qt::PointingHandCursor:\n        gtkCursorName = \"pointer\";\n        break;\n    case Qt::ForbiddenCursor:\n        gtkCursorName = \"not-allowed\";\n        break;\n    case Qt::OpenHandCursor:\n        gtkCursorName = \"grab\";\n        break;\n    case Qt::ClosedHandCursor:\n        gtkCursorName = \"grabbing\";\n        break;\n    case Qt::WhatsThisCursor:\n        gtkCursorName = \"help\";\n        break;\n    case Qt::BusyCursor:\n        gtkCursorName = \"progress\";\n        break;\n    case Qt::DragMoveCursor:\n        gtkCursorName = \"grabbing\";\n        break;\n    case Qt::DragCopyCursor:\n        gtkCursorName = \"copy\";\n        break;\n    case Qt::DragLinkCursor:\n        gtkCursorName = \"alias\";\n        break;\n    case Qt::BitmapCursor:\n        bitmapCursor = true;\n        break;\n    }\n\n    QGtkRefPtr<GdkCursor> c;\n    if (bitmapCursor == false) {\n        c = gdk_cursor_new_from_name(gdk_display_get_default(), gtkCursorName.constData());\n    } else {\n        c = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), qt_pixmapToPixbuf(windowCursor->pixmap()).get(), 0, 0);\n    }\n\n    QGtkWindow *pw = static_cast<QGtkWindow*>(window->handle());\n\n    // ### are we called before being realized, sometimes? why does this happen?\n    if (gtk_widget_get_window(pw->gtkWindow().get()) != nullptr) {\n        gdk_window_set_cursor(gtk_widget_get_window(pw->gtkWindow().get()), c.get());\n    }\n}\n\nQPoint QGtkCursor::pos() const\n{\n    // not supported.\n    return m_pos;\n}\n\nvoid QGtkCursor::setPos(const QPoint &pos)\n{\n    // not supported.\n    m_pos = pos;\n}\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkcursor.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKCURSOR_H\n#define QGTKCURSOR_H\n\n#include <qpa/qplatformcursor.h>\n\n#include <gdk/gdk.h>\n\nQT_BEGIN_NAMESPACE\n\nclass QGtkCursor;\n\nclass QGtkCursor : public QPlatformCursor\n{\npublic:\n    QGtkCursor();\n\n    void changeCursor(QCursor *windowCursor, QWindow *window) override;\n    QPoint pos() const override;\n    void setPos(const QPoint &pos) override;\n\nprivate:\n    QPoint m_pos;\n};\n\n\nQT_END_NAMESPACE\n\n#endif // QGTKCURSOR_H\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkeventdispatcher.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2016 The Qt Company Ltd.\n** Contact: https://www.qt.io/licensing/\n**\n** This file is part of the QtCore module of the Qt Toolkit.\n**\n** $QT_BEGIN_LICENSE:LGPL$\n** Commercial License Usage\n** Licensees holding valid commercial Qt licenses may use this file in\n** accordance with the commercial license agreement provided with the\n** Software or, alternatively, in accordance with the terms contained in\n** a written agreement between you and The Qt Company. For licensing terms\n** and conditions see https://www.qt.io/terms-conditions. For further\n** information use the contact form at https://www.qt.io/contact-us.\n**\n** GNU Lesser General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n** $QT_END_LICENSE$\n**\n****************************************************************************/\n\n#include \"qgtkeventdispatcher.h\"\n#include <private/qeventdispatcher_unix_p.h>\n#include <qpa/qwindowsysteminterface.h>\n\n#include <private/qthread_p.h>\n\n#include \"qcoreapplication.h\"\n#include \"qsocketnotifier.h\"\n\n#include <QtCore/qlist.h>\n#include <QtCore/qpair.h>\n\n#include <glib.h>\n\n#include \"CSystrace.h\"\n\nQT_BEGIN_NAMESPACE\n\nstruct GPollFDWithQSocketNotifier\n{\n    GPollFD pollfd;\n    QSocketNotifier *socketNotifier;\n};\n\nstruct GSocketNotifierSource\n{\n    GSource source;\n    QList<GPollFDWithQSocketNotifier *> pollfds;\n};\n\nstatic gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)\n{\n    if (timeout)\n        *timeout = -1;\n    return false;\n}\n\nstatic gboolean socketNotifierSourceCheck(GSource *source)\n{\n    GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);\n\n    bool pending = false;\n    for (int i = 0; !pending && i < src->pollfds.count(); ++i) {\n        GPollFDWithQSocketNotifier *p = src->pollfds.at(i);\n\n        if (p->pollfd.revents & G_IO_NVAL) {\n            // disable the invalid socket notifier\n            static const char *t[] = { \"Read\", \"Write\", \"Exception\" };\n            qWarning(\"QSocketNotifier: Invalid socket %d and type '%s', disabling...\",\n                     p->pollfd.fd, t[int(p->socketNotifier->type())]);\n            // ### note, modifies src->pollfds!\n            p->socketNotifier->setEnabled(false);\n        }\n\n        pending = ((p->pollfd.revents & p->pollfd.events) != 0);\n    }\n\n    return pending;\n}\n\nstatic gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpointer)\n{\n    QEvent event(QEvent::SockAct);\n\n    GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);\n    for (int i = 0; i < src->pollfds.count(); ++i) {\n        GPollFDWithQSocketNotifier *p = src->pollfds.at(i);\n\n        if ((p->pollfd.revents & p->pollfd.events) != 0) {\n            TRACE_EVENT0(\"gfx\", \"QGtkEventDispatcher::socketNotifierSourceDispatch\");\n            QCoreApplication::sendEvent(p->socketNotifier, &event);\n        }\n    }\n\n    return true; // ??? don't remove, right?\n}\n\nstatic GSourceFuncs socketNotifierSourceFuncs = {\n    socketNotifierSourcePrepare,\n    socketNotifierSourceCheck,\n    socketNotifierSourceDispatch,\n    NULL,\n    NULL,\n    NULL\n};\n\nstruct GTimerSource\n{\n    GSource source;\n    QTimerInfoList timerList;\n    QEventLoop::ProcessEventsFlags processEventsFlags;\n    bool runWithIdlePriority;\n};\n\nstatic gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)\n{\n    timespec tv = { 0l, 0l };\n    if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv))\n        *timeout = (tv.tv_sec * 1000) + ((tv.tv_nsec + 999999) / 1000 / 1000);\n    else\n        *timeout = -1;\n\n    return (*timeout == 0);\n}\n\nstatic gboolean timerSourceCheckHelper(GTimerSource *src)\n{\n    if (src->timerList.isEmpty()\n        || (src->processEventsFlags & QEventLoop::X11ExcludeTimers))\n        return false;\n\n    if (src->timerList.updateCurrentTime() < src->timerList.constFirst()->timeout)\n        return false;\n\n    return true;\n}\n\nstatic gboolean timerSourcePrepare(GSource *source, gint *timeout)\n{\n    gint dummy;\n    if (!timeout)\n        timeout = &dummy;\n\n    GTimerSource *src = reinterpret_cast<GTimerSource *>(source);\n    if (src->runWithIdlePriority) {\n        if (timeout)\n            *timeout = -1;\n        return false;\n    }\n\n    return timerSourcePrepareHelper(src, timeout);\n}\n\nstatic gboolean timerSourceCheck(GSource *source)\n{\n    GTimerSource *src = reinterpret_cast<GTimerSource *>(source);\n    if (src->runWithIdlePriority)\n        return false;\n    return timerSourceCheckHelper(src);\n}\n\nstatic gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer)\n{\n    GTimerSource *timerSource = reinterpret_cast<GTimerSource *>(source);\n    if (timerSource->processEventsFlags & QEventLoop::X11ExcludeTimers)\n        return true;\n    timerSource->runWithIdlePriority = true;\n    (void) timerSource->timerList.activateTimers();\n    return true; // ??? don't remove, right again?\n}\n\nstatic GSourceFuncs timerSourceFuncs = {\n    timerSourcePrepare,\n    timerSourceCheck,\n    timerSourceDispatch,\n    NULL,\n    NULL,\n    NULL\n};\n\nstruct GIdleTimerSource\n{\n    GSource source;\n    GTimerSource *timerSource;\n};\n\nstatic gboolean idleTimerSourcePrepare(GSource *source, gint *timeout)\n{\n    GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);\n    GTimerSource *timerSource = idleTimerSource->timerSource;\n    if (!timerSource->runWithIdlePriority) {\n        // Yield to the normal priority timer source\n        if (timeout)\n            *timeout = -1;\n        return false;\n    }\n\n    return timerSourcePrepareHelper(timerSource, timeout);\n}\n\nstatic gboolean idleTimerSourceCheck(GSource *source)\n{\n    GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);\n    GTimerSource *timerSource = idleTimerSource->timerSource;\n    if (!timerSource->runWithIdlePriority) {\n        // Yield to the normal priority timer source\n        return false;\n    }\n    return timerSourceCheckHelper(timerSource);\n}\n\nstatic gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)\n{\n    GTimerSource *timerSource = reinterpret_cast<GIdleTimerSource *>(source)->timerSource;\n    (void) timerSourceDispatch(&timerSource->source, 0, 0);\n    return true;\n}\n\nstatic GSourceFuncs idleTimerSourceFuncs = {\n    idleTimerSourcePrepare,\n    idleTimerSourceCheck,\n    idleTimerSourceDispatch,\n    NULL,\n    NULL,\n    NULL\n};\n\nstruct GPostEventSource\n{\n    GSource source;\n    QAtomicInt serialNumber;\n    int lastSerialNumber;\n    QGtkEventDispatcherPrivate *d;\n};\n\nstatic gboolean postEventSourcePrepare(GSource *s, gint *timeout)\n{\n    QThreadData *data = reinterpret_cast<QThreadPrivate*>(QObjectPrivate::get(QThread::currentThread()))->data;\n    if (!data)\n        return false;\n\n    gint dummy;\n    if (!timeout)\n        timeout = &dummy;\n    const bool canWait = data->canWaitLocked();\n    *timeout = canWait ? -1 : 0;\n\n    GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);\n    return (!canWait\n#if QT_VERSION >= QT_VERSION_CHECK(5,14,0)\n            || (source->serialNumber.loadRelaxed() != source->lastSerialNumber));\n#else\n            || (source->serialNumber.load() != source->lastSerialNumber));\n#endif\n}\n\nstatic gboolean postEventSourceCheck(GSource *source)\n{\n    return postEventSourcePrepare(source, 0);\n}\n\nstatic gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkEventDispatcher::postEventSourceDispatch\");\n    GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);\n#if QT_VERSION >= QT_VERSION_CHECK(5,14,0)\n    source->lastSerialNumber = source->serialNumber.loadRelaxed();\n#else\n    source->lastSerialNumber = source->serialNumber.load();\n#endif\n    QCoreApplication::sendPostedEvents();\n    source->d->runTimersOnceWithNormalPriority();\n    return true; // i dunno, george...\n}\n\nstatic GSourceFuncs postEventSourceFuncs = {\n    postEventSourcePrepare,\n    postEventSourceCheck,\n    postEventSourceDispatch,\n    NULL,\n    NULL,\n    NULL\n};\n\nstruct GUserEventSource\n{\n    GSource source;\n    QGtkEventDispatcherPrivate *d;\n};\n\nstatic gboolean userEventSourcePrepare(GSource *s, gint *timeout)\n{\n    Q_UNUSED(s);\n    Q_UNUSED(timeout);\n\n    return QWindowSystemInterface::windowSystemEventsQueued() > 0;\n}\n\nstatic gboolean userEventSourceCheck(GSource *source)\n{\n    return userEventSourcePrepare(source, 0);\n}\n\nstatic gboolean userEventSourceDispatch(GSource *source, GSourceFunc, gpointer)\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkEventDispatcher::userEventSourceDispatch\");\n    GUserEventSource *userEventSource = reinterpret_cast<GUserEventSource*>(source);\n    QGtkEventDispatcherPrivate *dispatcher = userEventSource->d;\n    QWindowSystemInterface::sendWindowSystemEvents(dispatcher->m_flags);\n    return true;\n}\n\nstatic GSourceFuncs userEventSourceFuncs = {\n    userEventSourcePrepare,\n    userEventSourceCheck,\n    userEventSourceDispatch,\n    NULL,\n    NULL,\n    NULL\n};\n\nQGtkEventDispatcherPrivate::QGtkEventDispatcherPrivate(GMainContext *context)\n    : mainContext(context)\n{\n#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32\n    if (qEnvironmentVariableIsEmpty(\"QT_NO_THREADED_GLIB\")) {\n        static QBasicMutex mutex;\n        QMutexLocker locker(&mutex);\n        if (!g_thread_supported())\n            g_thread_init(NULL);\n    }\n#endif\n\n    if (mainContext) {\n        g_main_context_ref(mainContext);\n    } else {\n        QCoreApplication *app = QCoreApplication::instance();\n        if (app && QThread::currentThread() == app->thread()) {\n            mainContext = g_main_context_default();\n            g_main_context_ref(mainContext);\n        } else {\n            mainContext = g_main_context_new();\n        }\n    }\n\n#if GLIB_CHECK_VERSION (2, 22, 0)\n    g_main_context_push_thread_default (mainContext);\n#endif\n\n    // ### RB: modified from upstream\n    // We must ensure that gtk's priority settings are above us, otherwise we\n    // might exhaust gtk, meaning that windows won't show etc.\n    int QT_BASE_PRIORITY = G_PRIORITY_LOW - 1;\n\n    // user event source\n    userEventSource = reinterpret_cast<GUserEventSource *>(g_source_new(&userEventSourceFuncs,\n                                                                        sizeof(GUserEventSource)));\n    userEventSource->d = this;\n    // must be above QT_BASE_PRIORITY, and similar to gtk's base priorities, otherwise we will have window flicker on show/resize.\n    g_source_set_priority(&userEventSource->source, G_PRIORITY_DEFAULT);\n    g_source_set_can_recurse(&userEventSource->source, true);\n    g_source_attach(&userEventSource->source, mainContext);\n\n    // setup post event source\n    postEventSource = reinterpret_cast<GPostEventSource *>(g_source_new(&postEventSourceFuncs,\n                                                                        sizeof(GPostEventSource)));\n    g_source_set_priority(&postEventSource->source, QT_BASE_PRIORITY - 1);\n#if QT_VERSION >= QT_VERSION_CHECK(5,14,0)\n    postEventSource->serialNumber.storeRelaxed(1);\n#else\n    postEventSource->serialNumber.store(1);\n#endif\n    postEventSource->d = this;\n    g_source_set_can_recurse(&postEventSource->source, true);\n    g_source_attach(&postEventSource->source, mainContext);\n\n    // setup socketNotifierSource\n    socketNotifierSource =\n        reinterpret_cast<GSocketNotifierSource *>(g_source_new(&socketNotifierSourceFuncs,\n                                                               sizeof(GSocketNotifierSource)));\n    g_source_set_priority(&socketNotifierSource->source, QT_BASE_PRIORITY - 3);\n    (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();\n    g_source_set_can_recurse(&socketNotifierSource->source, true);\n    g_source_attach(&socketNotifierSource->source, mainContext);\n\n    // setup normal and idle timer sources\n    timerSource = reinterpret_cast<GTimerSource *>(g_source_new(&timerSourceFuncs,\n                                                                sizeof(GTimerSource)));\n    g_source_set_priority(&timerSource->source, QT_BASE_PRIORITY - 3);\n    (void) new (&timerSource->timerList) QTimerInfoList();\n    timerSource->processEventsFlags = QEventLoop::AllEvents;\n    timerSource->runWithIdlePriority = false;\n    g_source_set_can_recurse(&timerSource->source, true);\n    g_source_attach(&timerSource->source, mainContext);\n\n    idleTimerSource = reinterpret_cast<GIdleTimerSource *>(g_source_new(&idleTimerSourceFuncs,\n                                                                        sizeof(GIdleTimerSource)));\n    g_source_set_priority(&idleTimerSource->source, QT_BASE_PRIORITY - 4);\n    idleTimerSource->timerSource = timerSource;\n    g_source_set_can_recurse(&idleTimerSource->source, true);\n    g_source_attach(&idleTimerSource->source, mainContext);\n}\n\nvoid QGtkEventDispatcherPrivate::runTimersOnceWithNormalPriority()\n{\n    timerSource->runWithIdlePriority = false;\n}\n\nQGtkEventDispatcher::QGtkEventDispatcher(QObject *parent)\n    : QAbstractEventDispatcher(*(new QGtkEventDispatcherPrivate), parent)\n{\n}\n\nQGtkEventDispatcher::QGtkEventDispatcher(GMainContext *mainContext, QObject *parent)\n    : QAbstractEventDispatcher(*(new QGtkEventDispatcherPrivate(mainContext)), parent)\n{ }\n\nQGtkEventDispatcher::~QGtkEventDispatcher()\n{\n    Q_D(QGtkEventDispatcher);\n\n    // destroy user event source\n    g_source_destroy(&d->userEventSource->source);\n    g_source_unref(&d->userEventSource->source);\n    d->userEventSource = 0;\n\n    // destroy all timer sources\n    qDeleteAll(d->timerSource->timerList);\n    d->timerSource->timerList.~QTimerInfoList();\n    g_source_destroy(&d->timerSource->source);\n    g_source_unref(&d->timerSource->source);\n    d->timerSource = 0;\n    g_source_destroy(&d->idleTimerSource->source);\n    g_source_unref(&d->idleTimerSource->source);\n    d->idleTimerSource = 0;\n\n    // destroy socket notifier source\n    for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {\n        GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds[i];\n        g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);\n        delete p;\n    }\n    d->socketNotifierSource->pollfds.~QList<GPollFDWithQSocketNotifier *>();\n    g_source_destroy(&d->socketNotifierSource->source);\n    g_source_unref(&d->socketNotifierSource->source);\n    d->socketNotifierSource = 0;\n\n    // destroy post event source\n    g_source_destroy(&d->postEventSource->source);\n    g_source_unref(&d->postEventSource->source);\n    d->postEventSource = 0;\n\n    Q_ASSERT(d->mainContext != 0);\n#if GLIB_CHECK_VERSION (2, 22, 0)\n    g_main_context_pop_thread_default (d->mainContext);\n#endif\n    g_main_context_unref(d->mainContext);\n    d->mainContext = 0;\n}\n\nbool QGtkEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkEventDispatcher::processEvents\");\n    Q_D(QGtkEventDispatcher);\n\n    d->m_flags = flags;\n\n    const bool canWait = (flags & QEventLoop::WaitForMoreEvents);\n    if (canWait)\n        Q_EMIT aboutToBlock();\n    else\n        Q_EMIT awake();\n\n    // tell postEventSourcePrepare() and timerSource about any new flags\n    QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags;\n    d->timerSource->processEventsFlags = flags;\n\n    if (!(flags & QEventLoop::EventLoopExec)) {\n        // force timers to be sent at normal priority\n        d->timerSource->runWithIdlePriority = false;\n    }\n\n    bool result = g_main_context_iteration(d->mainContext, canWait);\n    while (!result && canWait)\n        result = g_main_context_iteration(d->mainContext, canWait);\n\n    d->timerSource->processEventsFlags = savedFlags;\n\n    if (canWait)\n        Q_EMIT awake();\n\n    return result;\n}\n\nbool QGtkEventDispatcher::hasPendingEvents()\n{\n    Q_D(QGtkEventDispatcher);\n    return g_main_context_pending(d->mainContext);\n}\n\nvoid QGtkEventDispatcher::registerSocketNotifier(QSocketNotifier *notifier)\n{\n    Q_ASSERT(notifier);\n    int sockfd = notifier->socket();\n    int type = notifier->type();\n#ifndef QT_NO_DEBUG\n    if (sockfd < 0) {\n        qWarning(\"QSocketNotifier: Internal error\");\n        return;\n    } else if (notifier->thread() != thread()\n               || thread() != QThread::currentThread()) {\n        qWarning(\"QSocketNotifier: socket notifiers cannot be enabled from another thread\");\n        return;\n    }\n#endif\n\n    Q_D(QGtkEventDispatcher);\n    TRACE_COUNTER1(\"core\", \"registeredSockets\", ++d->m_sockets);\n\n\n    GPollFDWithQSocketNotifier *p = new GPollFDWithQSocketNotifier;\n    p->pollfd.fd = sockfd;\n    switch (type) {\n    case QSocketNotifier::Read:\n        p->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;\n        break;\n    case QSocketNotifier::Write:\n        p->pollfd.events = G_IO_OUT | G_IO_ERR;\n        break;\n    case QSocketNotifier::Exception:\n        p->pollfd.events = G_IO_PRI | G_IO_ERR;\n        break;\n    }\n    p->socketNotifier = notifier;\n\n    d->socketNotifierSource->pollfds.append(p);\n\n    g_source_add_poll(&d->socketNotifierSource->source, &p->pollfd);\n}\n\nvoid QGtkEventDispatcher::unregisterSocketNotifier(QSocketNotifier *notifier)\n{\n    Q_ASSERT(notifier);\n#ifndef QT_NO_DEBUG\n    int sockfd = notifier->socket();\n    if (sockfd < 0) {\n        qWarning(\"QSocketNotifier: Internal error\");\n        return;\n    } else if (notifier->thread() != thread()\n               || thread() != QThread::currentThread()) {\n        qWarning(\"QSocketNotifier: socket notifiers cannot be disabled from another thread\");\n        return;\n    }\n#endif\n\n    Q_D(QGtkEventDispatcher);\n    TRACE_COUNTER1(\"core\", \"registeredSockets\", --d->m_sockets);\n\n    for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {\n        GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);\n        if (p->socketNotifier == notifier) {\n            // found it\n            g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);\n\n            d->socketNotifierSource->pollfds.removeAt(i);\n            delete p;\n\n            return;\n        }\n    }\n}\n\nvoid QGtkEventDispatcher::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object)\n{\n#ifndef QT_NO_DEBUG\n    if (timerId < 1 || interval < 0 || !object) {\n        qWarning(\"QGtkEventDispatcher::registerTimer: invalid arguments\");\n        return;\n    } else if (object->thread() != thread() || thread() != QThread::currentThread()) {\n        qWarning(\"QGtkEventDispatcher::registerTimer: timers cannot be started from another thread\");\n        return;\n    }\n#endif\n\n    Q_D(QGtkEventDispatcher);\n    TRACE_COUNTER1(\"core\", \"registeredTimers\", ++d->m_timers);\n    d->timerSource->timerList.registerTimer(timerId, interval, timerType, object);\n}\n\nbool QGtkEventDispatcher::unregisterTimer(int timerId)\n{\n#ifndef QT_NO_DEBUG\n    if (timerId < 1) {\n        qWarning(\"QGtkEventDispatcher::unregisterTimer: invalid argument\");\n        return false;\n    } else if (thread() != QThread::currentThread()) {\n        qWarning(\"QGtkEventDispatcher::unregisterTimer: timers cannot be stopped from another thread\");\n        return false;\n    }\n#endif\n\n    Q_D(QGtkEventDispatcher);\n    TRACE_COUNTER1(\"core\", \"registeredTimers\", --d->m_timers);\n    return d->timerSource->timerList.unregisterTimer(timerId);\n}\n\nbool QGtkEventDispatcher::unregisterTimers(QObject *object)\n{\n#ifndef QT_NO_DEBUG\n    if (!object) {\n        qWarning(\"QGtkEventDispatcher::unregisterTimers: invalid argument\");\n        return false;\n    } else if (object->thread() != thread() || thread() != QThread::currentThread()) {\n        qWarning(\"QGtkEventDispatcher::unregisterTimers: timers cannot be stopped from another thread\");\n        return false;\n    }\n#endif\n\n    Q_D(QGtkEventDispatcher);\n    return d->timerSource->timerList.unregisterTimers(object);\n}\n\nQList<QGtkEventDispatcher::TimerInfo> QGtkEventDispatcher::registeredTimers(QObject *object) const\n{\n    if (!object) {\n        qWarning(\"QGtkEventDispatcher:registeredTimers: invalid argument\");\n        return QList<TimerInfo>();\n    }\n\n    Q_D(const QGtkEventDispatcher);\n    return d->timerSource->timerList.registeredTimers(object);\n}\n\nint QGtkEventDispatcher::remainingTime(int timerId)\n{\n#ifndef QT_NO_DEBUG\n    if (timerId < 1) {\n        qWarning(\"QGtkEventDispatcher::remainingTimeTime: invalid argument\");\n        return -1;\n    }\n#endif\n\n    Q_D(QGtkEventDispatcher);\n    return d->timerSource->timerList.timerRemainingTime(timerId);\n}\n\nvoid QGtkEventDispatcher::interrupt()\n{\n    wakeUp();\n}\n\nvoid QGtkEventDispatcher::wakeUp()\n{\n    Q_D(QGtkEventDispatcher);\n    d->postEventSource->serialNumber.ref();\n    g_main_context_wakeup(d->mainContext);\n}\n\nvoid QGtkEventDispatcher::flush()\n{\n}\n\nbool QGtkEventDispatcher::versionSupported()\n{\n#if !defined(GLIB_MAJOR_VERSION) || !defined(GLIB_MINOR_VERSION) || !defined(GLIB_MICRO_VERSION)\n    return false;\n#else\n    return ((GLIB_MAJOR_VERSION << 16) + (GLIB_MINOR_VERSION << 8) + GLIB_MICRO_VERSION) >= 0x020301;\n#endif\n}\n\nQGtkEventDispatcher::QGtkEventDispatcher(QGtkEventDispatcherPrivate &dd, QObject *parent)\n    : QAbstractEventDispatcher(dd, parent)\n{\n}\n\nQT_END_NAMESPACE\n"
  },
  {
    "path": "src/platform-plugin/qgtkeventdispatcher.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QEVENTDISPATCHER_GLIB_P_H\n#define QEVENTDISPATCHER_GLIB_P_H\n\n//\n//  W A R N I N G\n//  -------------\n//\n// This file is not part of the Qt API.  It exists for the convenience\n// of the QLibrary class.  This header file may change from\n// version to version without notice, or even be removed.\n//\n// We mean it.\n//\n\n#include <QtCore/qabstracteventdispatcher.h>\n#include <private/qabstracteventdispatcher_p.h>\n\ntypedef struct _GMainContext GMainContext;\n\nQT_BEGIN_NAMESPACE\n\nclass QGtkEventDispatcherPrivate;\n\nclass Q_CORE_EXPORT QGtkEventDispatcher : public QAbstractEventDispatcher\n{\n    Q_OBJECT\n    Q_DECLARE_PRIVATE(QGtkEventDispatcher)\n\npublic:\n    explicit QGtkEventDispatcher(QObject *parent = 0);\n    explicit QGtkEventDispatcher(GMainContext *context, QObject *parent = 0);\n    ~QGtkEventDispatcher();\n\n    bool processEvents(QEventLoop::ProcessEventsFlags flags) Q_DECL_OVERRIDE;\n    bool hasPendingEvents() Q_DECL_OVERRIDE;\n\n    void registerSocketNotifier(QSocketNotifier *socketNotifier) Q_DECL_FINAL;\n    void unregisterSocketNotifier(QSocketNotifier *socketNotifier) Q_DECL_FINAL;\n\n    void registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object) Q_DECL_FINAL;\n    bool unregisterTimer(int timerId) Q_DECL_FINAL;\n    bool unregisterTimers(QObject *object) Q_DECL_FINAL;\n    QList<TimerInfo> registeredTimers(QObject *object) const Q_DECL_FINAL;\n\n    int remainingTime(int timerId) Q_DECL_FINAL;\n\n    void wakeUp() Q_DECL_FINAL;\n    void interrupt() Q_DECL_FINAL;\n    void flush() Q_DECL_FINAL;\n\n    static bool versionSupported();\n\nprotected:\n    QGtkEventDispatcher(QGtkEventDispatcherPrivate &dd, QObject *parent);\n};\n\nstruct GPostEventSource;\nstruct GSocketNotifierSource;\nstruct GTimerSource;\nstruct GIdleTimerSource;\nstruct GUserEventSource;\n\nclass Q_CORE_EXPORT QGtkEventDispatcherPrivate : public QAbstractEventDispatcherPrivate\n{\n\npublic:\n    QGtkEventDispatcherPrivate(GMainContext *context = 0);\n    GMainContext *mainContext;\n    GPostEventSource *postEventSource;\n    GSocketNotifierSource *socketNotifierSource;\n    GTimerSource *timerSource;\n    GIdleTimerSource *idleTimerSource;\n    GUserEventSource *userEventSource;\n\n    void runTimersOnceWithNormalPriority();\n\n    QEventLoop::ProcessEventsFlags m_flags = QEventLoop::AllEvents;\n\n    uint m_timers = 0;\n    uint m_sockets = 0;\n};\n\nQT_END_NAMESPACE\n\n#endif // QEVENTDISPATCHER_GLIB_P_H\n"
  },
  {
    "path": "src/platform-plugin/qgtkhelpers.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkhelpers.h\"\n\nQGtkRefPtr<GdkPixbuf> qt_imageToPixbuf(const QImage &cimage)\n{\n    if (cimage.isNull())\n        return 0;\n    QImage image = cimage;\n    if (image.hasAlphaChannel()) {\n        if (image.format() != QImage::Format_RGBA8888) {\n            image = image.convertToFormat(QImage::Format_RGBA8888);\n        }\n    } else {\n        if (image.format() != QImage::Format_RGB888) {\n            image = image.convertToFormat(QImage::Format_RGB888);\n        }\n    }\n    guchar *buf = (guchar*)malloc(image.byteCount());\n    memcpy(buf, image.constBits(), image.byteCount());\n    QGtkRefPtr<GdkPixbuf> gpb = gdk_pixbuf_new_from_data(\n        buf,\n        GDK_COLORSPACE_RGB,\n        image.hasAlphaChannel(),\n        8,\n        image.width(),\n        image.height(),\n        image.bytesPerLine(),\n        (GdkPixbufDestroyNotify)free,\n        NULL\n    );\n    return gpb;\n}\n\nQGtkRefPtr<GdkPixbuf> qt_pixmapToPixbuf(const QPixmap &pixmap)\n{\n    QImage i = pixmap.toImage();\n    return qt_imageToPixbuf(i);\n}\n\nQImage qt_pixbufToImage(const QGtkRefPtr<GdkPixbuf> &pixbuf)\n{\n    QImage data;\n    if (gdk_pixbuf_get_has_alpha(pixbuf.get())) {\n        data = QImage(gdk_pixbuf_get_pixels(pixbuf.get()),\n                      gdk_pixbuf_get_width(pixbuf.get()), gdk_pixbuf_get_height(pixbuf.get()),\n                      gdk_pixbuf_get_rowstride(pixbuf.get()),\n                      QImage::Format_RGBA8888).copy();\n    } else {\n        data = QImage(gdk_pixbuf_get_pixels(pixbuf.get()),\n                      gdk_pixbuf_get_width(pixbuf.get()), gdk_pixbuf_get_height(pixbuf.get()),\n                      gdk_pixbuf_get_rowstride(pixbuf.get()),\n                      QImage::Format_RGB888).copy();\n    }\n    return data;\n}\n\n// Returns the largest image for this icon, in RGBA format.\nQImage qt_getBiggestImageForIcon(const QIcon &icon)\n{\n    QIcon::State st = QIcon::On;\n    QList<QSize> sizes = icon.availableSizes(QIcon::Normal, st);\n    if (!sizes.length()) {\n        st = QIcon::Off;\n        sizes = icon.availableSizes(QIcon::Normal, st);\n        if (!sizes.length()) {\n            qWarning() << \"No available icons for icon?\" << icon;\n            return QImage();\n        }\n    }\n\n    // Find the largest size, hopefully it's the best looking.\n    QSize sz;\n    for (int i = 0; i < sizes.length(); ++i) {\n        const QSize &nsz = sizes.at(i);\n\n        if (nsz.width() * nsz.height() >= sz.width() * sz.height()) {\n            sz = nsz;\n        }\n    }\n\n    QPixmap p = icon.pixmap(sz, QIcon::Normal, st);\n    QImage i = p.toImage().convertToFormat(QImage::Format_RGBA8888);\n    return i;\n}\n\n// Convert a QIcon to a GdkPixbuf for use elsewhere.\nQGtkRefPtr<GdkPixbuf> qt_iconToPixbuf(const QIcon &icon)\n{\n    QImage i = qt_getBiggestImageForIcon(icon);\n    return qt_imageToPixbuf(i);\n}\n\n// Convert a QIcon to a GIcon for use elsewhere.\nQGtkRefPtr<GIcon> qt_iconToIcon(const QIcon &icon)\n{\n    QImage i = qt_getBiggestImageForIcon(icon);\n    QGtkRefPtr<GBytes> bytes = g_bytes_new_take(const_cast<uchar*>(i.constBits()), i.byteCount());\n    QGtkRefPtr<GIcon> ico = g_bytes_icon_new(bytes.get());\n    return ico;\n}\n\n// Qt uses &, gtk uses _.\nQString qt_convertToGtkMnemonics(const QString &text)\n{\n    QString cpy = text;\n    return cpy.replace(\"&\", \"_\"); // ### too simple! need to leave &&.\n}\n\nQt::KeyboardModifiers qt_convertToQtKeyboardMods(guint mask)\n{\n    Qt::KeyboardModifiers mods = Qt::NoModifier;\n\n    if (mask & GDK_SHIFT_MASK)\n        mods |= Qt::ShiftModifier;\n    if (mask & GDK_CONTROL_MASK)\n        mods |= Qt::ControlModifier;\n    if (mask & GDK_MOD1_MASK)\n        mods |= Qt::AltModifier;\n    if (mask & GDK_META_MASK)\n        mods |= Qt::MetaModifier;\n#if 0\n    if (mask & GDK_SUPER_MASK)\n        qDebug() << \"Super\";\n    if (mask & GDK_HYPER_MASK)\n        qDebug() << \"Hyper\";\n    if (mask & GDK_MOD2_MASK)\n        qDebug() << \"Mod2\";\n    if (mask & GDK_MOD3_MASK)\n        qDebug() << \"Mod3\";\n    if (mask & GDK_MOD4_MASK)\n        qDebug() << \"Mod4\";\n    if (mask & GDK_MOD5_MASK)\n        qDebug() << \"Mod5\";\n#endif\n    return mods;\n}\n\nQt::Key qt_convertToQtKey(int keyval)\n{\n    switch (keyval) {\n    case GDK_KEY_BackSpace:\n        return Qt::Key_Backspace;\n    case GDK_KEY_KP_Tab:\n    case GDK_KEY_Tab:\n        return Qt::Key_Tab;\n    case GDK_KEY_Clear:\n        return Qt::Key_Clear;\n    case GDK_KEY_Return:\n        return Qt::Key_Return;\n    case GDK_KEY_KP_Enter:\n        return Qt::Key_Enter;\n    case GDK_KEY_Pause:\n        return Qt::Key_Pause;\n    case GDK_KEY_Scroll_Lock:\n        return Qt::Key_ScrollLock;\n    case GDK_KEY_Sys_Req:\n        return Qt::Key_SysReq;\n    case GDK_KEY_Escape:\n        return Qt::Key_Escape;\n    case GDK_KEY_KP_Delete:\n    case GDK_KEY_Delete:\n        return Qt::Key_Delete;\n    case GDK_KEY_Multi_key:\n        return Qt::Key_Multi_key;\n    case GDK_KEY_Codeinput:\n        return Qt::Key_Codeinput;\n    case GDK_KEY_SingleCandidate:\n        return Qt::Key_SingleCandidate;\n    case GDK_KEY_MultipleCandidate:\n        return Qt::Key_MultipleCandidate;\n    case GDK_KEY_PreviousCandidate:\n        return Qt::Key_PreviousCandidate;\n    case GDK_KEY_Kanji:\n        return Qt::Key_Kanji;\n    case GDK_KEY_Muhenkan:\n        return Qt::Key_Muhenkan;\n    case GDK_KEY_Henkan:\n        return Qt::Key_Henkan;\n    case GDK_KEY_Romaji:\n        return Qt::Key_Romaji;\n    case GDK_KEY_Hiragana:\n        return Qt::Key_Hiragana;\n    case GDK_KEY_Katakana:\n        return Qt::Key_Katakana;\n    case GDK_KEY_Hiragana_Katakana:\n        return Qt::Key_Hiragana_Katakana;\n    case GDK_KEY_Zenkaku:\n        return Qt::Key_Zenkaku;\n    case GDK_KEY_Hankaku:\n        return Qt::Key_Hankaku;\n    case GDK_KEY_Zenkaku_Hankaku:\n        return Qt::Key_Zenkaku_Hankaku;\n    case GDK_KEY_Touroku:\n        return Qt::Key_Touroku;\n    case GDK_KEY_Massyo:\n        return Qt::Key_Massyo;\n    case GDK_KEY_Kana_Lock:\n        return Qt::Key_Kana_Lock;\n    case GDK_KEY_Kana_Shift:\n        return Qt::Key_Kana_Shift;\n    case GDK_KEY_Eisu_Shift:\n        return Qt::Key_Eisu_Shift;\n    case GDK_KEY_Eisu_toggle:\n        return Qt::Key_Eisu_toggle;\n    case GDK_KEY_KP_Home:\n    case GDK_KEY_Home:\n        return Qt::Key_Home;\n    case GDK_KEY_KP_Left:\n    case GDK_KEY_Left:\n        return Qt::Key_Left;\n    case GDK_KEY_KP_Up:\n    case GDK_KEY_Up:\n        return Qt::Key_Up;\n    case GDK_KEY_KP_Right:\n    case GDK_KEY_Right:\n        return Qt::Key_Right;\n    case GDK_KEY_KP_Down:\n    case GDK_KEY_Down:\n        return Qt::Key_Down;\n    case GDK_KEY_KP_Page_Up:\n    case GDK_KEY_Page_Up:\n        return Qt::Key_PageUp;\n    case GDK_KEY_KP_Page_Down:\n    case GDK_KEY_Page_Down:\n        return Qt::Key_PageDown;\n    case GDK_KEY_KP_End:\n    case GDK_KEY_End:\n        return Qt::Key_End;\n    case GDK_KEY_KP_Begin:\n    case GDK_KEY_Begin:\n        return Qt::Key_Home;\n    case GDK_KEY_Select:\n        return Qt::Key_Select;\n    case GDK_KEY_Print:\n        return Qt::Key_Print;\n    case GDK_KEY_Execute:\n        return Qt::Key_Execute;\n    case GDK_KEY_KP_Insert:\n    case GDK_KEY_Insert:\n        return Qt::Key_Insert;\n    case GDK_KEY_Menu:\n        return Qt::Key_Menu;\n    case GDK_KEY_Cancel:\n        return Qt::Key_Cancel;\n    case GDK_KEY_Help:\n        return Qt::Key_Help;\n    case GDK_KEY_Mode_switch:\n        return Qt::Key_Mode_switch;\n    case GDK_KEY_Num_Lock:\n        return Qt::Key_NumLock;\n    case GDK_KEY_F1:\n    case GDK_KEY_KP_F1:\n        return Qt::Key_F1;\n    case GDK_KEY_F2:\n    case GDK_KEY_KP_F2:\n        return Qt::Key_F2;\n    case GDK_KEY_F3:\n    case GDK_KEY_KP_F3:\n        return Qt::Key_F3;\n    case GDK_KEY_F4:\n    case GDK_KEY_KP_F4:\n        return Qt::Key_F4;\n    case GDK_KEY_F5:\n        return Qt::Key_F5;\n    case GDK_KEY_F6:\n        return Qt::Key_F6;\n    case GDK_KEY_F7:\n        return Qt::Key_F7;\n    case GDK_KEY_F8:\n        return Qt::Key_F8;\n    case GDK_KEY_F9:\n        return Qt::Key_F9;\n    case GDK_KEY_F10:\n        return Qt::Key_F10;\n    case GDK_KEY_F11:\n        return Qt::Key_F11;\n    case GDK_KEY_F12:\n        return Qt::Key_F12;\n    case GDK_KEY_F13:\n        return Qt::Key_F13;\n    case GDK_KEY_F14:\n        return Qt::Key_F14;\n    case GDK_KEY_F15:\n        return Qt::Key_F15;\n    case GDK_KEY_F16:\n        return Qt::Key_F16;\n    case GDK_KEY_F17:\n        return Qt::Key_F17;\n    case GDK_KEY_F18:\n        return Qt::Key_F18;\n    case GDK_KEY_F19:\n        return Qt::Key_F19;\n    case GDK_KEY_F20:\n        return Qt::Key_F20;\n    case GDK_KEY_F21:\n        return Qt::Key_F21;\n    case GDK_KEY_F22:\n        return Qt::Key_F22;\n    case GDK_KEY_F23:\n        return Qt::Key_F23;\n    case GDK_KEY_F24:\n        return Qt::Key_F24;\n    case GDK_KEY_F25:\n        return Qt::Key_F25;\n    case GDK_KEY_F26:\n        return Qt::Key_F26;\n    case GDK_KEY_F27:\n        return Qt::Key_F27;\n    case GDK_KEY_F28:\n        return Qt::Key_F28;\n    case GDK_KEY_F29:\n        return Qt::Key_F29;\n    case GDK_KEY_F30:\n        return Qt::Key_F30;\n    case GDK_KEY_F31:\n        return Qt::Key_F31;\n    case GDK_KEY_F32:\n        return Qt::Key_F32;\n    case GDK_KEY_F33:\n        return Qt::Key_F33;\n    case GDK_KEY_F34:\n        return Qt::Key_F34;\n    case GDK_KEY_F35:\n        return Qt::Key_F35;\n    case GDK_KEY_Shift_L:\n    case GDK_KEY_Shift_R:\n        return Qt::Key_Shift;\n    case GDK_KEY_Control_L:\n    case GDK_KEY_Control_R:\n        return Qt::Key_Control;\n    case GDK_KEY_Caps_Lock:\n        return Qt::Key_CapsLock;\n    case GDK_KEY_Meta_L:\n    case GDK_KEY_Meta_R:\n        return Qt::Key_Meta;\n    case GDK_KEY_Alt_L:\n    case GDK_KEY_Alt_R:\n        return Qt::Key_Alt;\n    case GDK_KEY_Super_L:\n        return Qt::Key_Super_L;\n    case GDK_KEY_Super_R:\n        return Qt::Key_Super_R;\n    case GDK_KEY_Hyper_L:\n        return Qt::Key_Hyper_R;\n    case GDK_KEY_Hyper_R:\n        return Qt::Key_Hyper_R;\n    case GDK_KEY_MonBrightnessUp:\n        return Qt::Key_MonBrightnessUp;\n    case GDK_KEY_MonBrightnessDown:\n        return Qt::Key_MonBrightnessDown;\n    case GDK_KEY_KbdLightOnOff:\n        return Qt::Key_KeyboardLightOnOff;\n    case GDK_KEY_KbdBrightnessUp:\n        return Qt::Key_KeyboardBrightnessUp;\n    case GDK_KEY_KbdBrightnessDown:\n        return Qt::Key_KeyboardBrightnessDown;\n    case GDK_KEY_Standby:\n        return Qt::Key_Standby;\n    case GDK_KEY_AudioLowerVolume:\n        return Qt::Key_VolumeDown;\n    case GDK_KEY_AudioMute:\n        return Qt::Key_VolumeMute;\n    case GDK_KEY_AudioRaiseVolume:\n        return Qt::Key_VolumeUp;\n    case GDK_KEY_AudioPlay:\n        return Qt::Key_MediaPlay;\n    case GDK_KEY_AudioStop:\n        return Qt::Key_MediaStop;\n    case GDK_KEY_AudioPrev:\n        return Qt::Key_MediaPrevious;\n    case GDK_KEY_AudioNext:\n        return Qt::Key_MediaNext;\n    case GDK_KEY_HomePage:\n        return Qt::Key_HomePage;\n    case GDK_KEY_Mail:\n        return Qt::Key_LaunchMail;\n    case GDK_KEY_Start:\n        return Qt::Key_Standby;\n    case GDK_KEY_Find:\n    case GDK_KEY_Search:\n        return Qt::Key_Search;\n    case GDK_KEY_AudioRecord:\n    case GDK_KEY_Calculator:\n        return Qt::Key_Calculator;\n    case GDK_KEY_Memo:\n        return Qt::Key_Memo;\n    case GDK_KEY_ToDoList:\n        return Qt::Key_ToDoList;\n    case GDK_KEY_Calendar:\n        return Qt::Key_Calendar;\n    case GDK_KEY_PowerDown:\n        return Qt::Key_PowerDown;\n    case GDK_KEY_ContrastAdjust:\n        return Qt::Key_ContrastAdjust;\n    case GDK_KEY_Back:\n        return Qt::Key_Back;\n    case GDK_KEY_Forward:\n        return Qt::Key_Forward;\n    case GDK_KEY_Stop:\n        return Qt::Key_Stop;\n    case GDK_KEY_Refresh:\n        return Qt::Key_Refresh;\n    case GDK_KEY_PowerOff:\n        return Qt::Key_PowerOff;\n    case GDK_KEY_WakeUp:\n        return Qt::Key_WakeUp;\n    case GDK_KEY_Eject:\n        return Qt::Key_Eject;\n    case GDK_KEY_ScreenSaver:\n        return Qt::Key_ScreenSaver;\n    case GDK_KEY_WWW:\n        return Qt::Key_WWW;\n    case GDK_KEY_Sleep:\n        return Qt::Key_Sleep;\n    case GDK_KEY_Favorites:\n        return Qt::Key_Favorites;\n    case GDK_KEY_AudioPause:\n        return Qt::Key_MediaPause;\n    case GDK_KEY_AudioMedia:\n        return Qt::Key_LaunchMedia;\n    case GDK_KEY_MyComputer:\n        return Qt::Key_Launch0;\n    case GDK_KEY_VendorHome:\n        return Qt::Key_OfficeHome;\n    case GDK_KEY_LightBulb:\n        return Qt::Key_LightBulb;\n    case GDK_KEY_Shop:\n        return Qt::Key_Shop;\n    case GDK_KEY_History:\n        return Qt::Key_History;\n    case GDK_KEY_OpenURL:\n        return Qt::Key_OpenUrl;\n    case GDK_KEY_AddFavorite:\n        return Qt::Key_AddFavorite;\n    case GDK_KEY_HotLinks:\n        return Qt::Key_HotLinks;\n    case GDK_KEY_BrightnessAdjust:\n        return Qt::Key_BrightnessAdjust;\n    case GDK_KEY_Finance:\n        return Qt::Key_Finance;\n    case GDK_KEY_Community:\n        return Qt::Key_Community;\n    case GDK_KEY_AudioRewind:\n        return Qt::Key_AudioRewind;\n    case GDK_KEY_BackForward:\n        return Qt::Key_BackForward;\n    case GDK_KEY_Launch0:\n        return Qt::Key_Launch0;\n    case GDK_KEY_Launch1:\n        return Qt::Key_Launch1;\n    case GDK_KEY_Launch2:\n        return Qt::Key_Launch2;\n    case GDK_KEY_Launch3:\n        return Qt::Key_Launch3;\n    case GDK_KEY_Launch4:\n        return Qt::Key_Launch4;\n    case GDK_KEY_Launch5:\n        return Qt::Key_Launch5;\n    case GDK_KEY_Launch6:\n        return Qt::Key_Launch6;\n    case GDK_KEY_Launch7:\n        return Qt::Key_Launch7;\n    case GDK_KEY_Launch8:\n        return Qt::Key_Launch8;\n    case GDK_KEY_Launch9:\n        return Qt::Key_Launch9;\n    case GDK_KEY_LaunchA:\n        return Qt::Key_LaunchA;\n    case GDK_KEY_LaunchB:\n        return Qt::Key_LaunchB;\n    case GDK_KEY_LaunchC:\n        return Qt::Key_LaunchC;\n    case GDK_KEY_LaunchD:\n        return Qt::Key_LaunchD;\n    case GDK_KEY_LaunchE:\n        return Qt::Key_LaunchE;\n    case GDK_KEY_LaunchF:\n        return Qt::Key_LaunchF;\n    case GDK_KEY_ApplicationLeft:\n        return Qt::Key_ApplicationLeft;\n    case GDK_KEY_ApplicationRight:\n        return Qt::Key_ApplicationRight;\n    case GDK_KEY_Book:\n        return Qt::Key_Book;\n    case GDK_KEY_CD:\n        return Qt::Key_CD;\n    case GDK_KEY_Close:\n        return Qt::Key_Close;\n    case GDK_KEY_Copy:\n        return Qt::Key_Copy;\n    case GDK_KEY_Cut:\n        return Qt::Key_Cut;\n    case GDK_KEY_Display:\n        return Qt::Key_Display;\n    case GDK_KEY_DOS:\n        return Qt::Key_DOS;\n    case GDK_KEY_Documents:\n        return Qt::Key_Documents;\n    case GDK_KEY_Excel:\n        return Qt::Key_Excel;\n    case GDK_KEY_Explorer:\n        return Qt::Key_Explorer;\n    case GDK_KEY_Game:\n        return Qt::Key_Game;\n    case GDK_KEY_Go:\n        return Qt::Key_Go;\n    case GDK_KEY_iTouch:\n        return Qt::Key_iTouch;\n    case GDK_KEY_LogOff:\n        return Qt::Key_LogOff;\n    case GDK_KEY_Market:\n        return Qt::Key_Market;\n    case GDK_KEY_Meeting:\n        return Qt::Key_Meeting;\n    case GDK_KEY_MenuKB:\n        return Qt::Key_MenuKB;\n    case GDK_KEY_MenuPB:\n        return Qt::Key_MenuPB;\n    case GDK_KEY_MySites:\n        return Qt::Key_MySites;\n    case GDK_KEY_News:\n        return Qt::Key_News;\n    case GDK_KEY_OfficeHome:\n        return Qt::Key_OfficeHome;\n    case GDK_KEY_Option:\n        return Qt::Key_Option;\n    case GDK_KEY_Paste:\n        return Qt::Key_Paste;\n    case GDK_KEY_Phone:\n        return Qt::Key_Phone;\n    case GDK_KEY_Reply:\n        return Qt::Key_Reply;\n    case GDK_KEY_Reload:\n        return Qt::Key_Reload;\n    case GDK_KEY_RotateWindows:\n        return Qt::Key_RotateWindows;\n    case GDK_KEY_RotationPB:\n        return Qt::Key_RotationPB;\n    case GDK_KEY_RotationKB:\n        return Qt::Key_RotationKB;\n    case GDK_KEY_Save:\n        return Qt::Key_Save;\n    case GDK_KEY_Send:\n        return Qt::Key_Send;\n    case GDK_KEY_Spell:\n        return Qt::Key_Spell;\n    case GDK_KEY_SplitScreen:\n        return Qt::Key_SplitScreen;\n    case GDK_KEY_Support:\n        return Qt::Key_Support;\n    case GDK_KEY_TaskPane:\n        return Qt::Key_TaskPane;\n    case GDK_KEY_Terminal:\n        return Qt::Key_Terminal;\n    case GDK_KEY_Tools:\n        return Qt::Key_Tools;\n    case GDK_KEY_Travel:\n        return Qt::Key_Travel;\n    case GDK_KEY_Video:\n        return Qt::Key_Video;\n    case GDK_KEY_Word:\n        return Qt::Key_Word;\n    case GDK_KEY_Xfer:\n        return Qt::Key_Xfer;\n    case GDK_KEY_ZoomIn:\n        return Qt::Key_ZoomIn;\n    case GDK_KEY_ZoomOut:\n        return Qt::Key_ZoomOut;\n    case GDK_KEY_Away:\n        return Qt::Key_Away;\n    case GDK_KEY_Messenger:\n        return Qt::Key_Messenger;\n    case GDK_KEY_WebCam:\n        return Qt::Key_WebCam;\n    case GDK_KEY_MailForward:\n        return Qt::Key_MailForward;\n    case GDK_KEY_Pictures:\n        return Qt::Key_Pictures;\n    case GDK_KEY_Music:\n        return Qt::Key_Music;\n    case GDK_KEY_Battery:\n        return Qt::Key_Battery;\n    case GDK_KEY_Bluetooth:\n        return Qt::Key_Bluetooth;\n    case GDK_KEY_WLAN:\n        return Qt::Key_WLAN;\n    case GDK_KEY_UWB:\n        return Qt::Key_UWB;\n    case GDK_KEY_AudioForward:\n        return Qt::Key_AudioForward;\n    case GDK_KEY_AudioRepeat:\n        return Qt::Key_AudioRepeat;\n    case GDK_KEY_AudioRandomPlay:\n        return Qt::Key_AudioRandomPlay;\n    case GDK_KEY_Subtitle:\n        return Qt::Key_Subtitle;\n    case GDK_KEY_AudioCycleTrack:\n        return Qt::Key_AudioCycleTrack;\n    case GDK_KEY_Time:\n        return Qt::Key_Time;\n    case GDK_KEY_View:\n        return Qt::Key_View;\n    case GDK_KEY_TopMenu:\n        return Qt::Key_TopMenu;\n    case GDK_KEY_Suspend:\n        return Qt::Key_Suspend;\n    case GDK_KEY_Hibernate:\n        return Qt::Key_Hibernate;\n    case GDK_KEY_ClearGrab:\n        return Qt::Key_ClearGrab;\n    }\n\n    if (keyval < 256) {\n        if (isprint(keyval))\n            keyval = toupper(keyval);\n    }\n    return Qt::Key(keyval);\n}\n\n// ### keyvalToQtKey in reverse, ugh\n// ### finish\nguint qt_convertToGdkKeyval(Qt::Key qKey)\n{\n    switch (qKey) {\n    case Qt::Key_Insert:\n        return GDK_KEY_Insert;\n    case Qt::Key_Delete:\n        return GDK_KEY_Delete;\n    case Qt::Key_Left:\n        return GDK_KEY_Left;\n    case Qt::Key_Right:\n        return GDK_KEY_Right;\n    case Qt::Key_Up:\n        return GDK_KEY_Up;\n    case Qt::Key_Down:\n        return GDK_KEY_Down;\n    case Qt::Key_Tab:\n        return GDK_KEY_Tab;\n    case Qt::Key_F1:\n        return GDK_KEY_F1;\n    case Qt::Key_F2:\n        return GDK_KEY_F2;\n    case Qt::Key_F3:\n        return GDK_KEY_F3;\n    case Qt::Key_F4:\n        return GDK_KEY_F4;\n    case Qt::Key_F5:\n        return GDK_KEY_F5;\n    case Qt::Key_F6:\n        return GDK_KEY_F6;\n    case Qt::Key_F7:\n        return GDK_KEY_F7;\n    case Qt::Key_F8:\n        return GDK_KEY_F8;\n    case Qt::Key_F9:\n        return GDK_KEY_F9;\n    case Qt::Key_F10:\n        return GDK_KEY_F10;\n    case Qt::Key_F11:\n        return GDK_KEY_F11;\n    case Qt::Key_F12:\n        return GDK_KEY_F12;\n    case Qt::Key_F13:\n        return GDK_KEY_F13;\n    case Qt::Key_F14:\n        return GDK_KEY_F14;\n    case Qt::Key_F15:\n        return GDK_KEY_F15;\n    case Qt::Key_F16:\n        return GDK_KEY_F16;\n    case Qt::Key_F17:\n        return GDK_KEY_F17;\n    case Qt::Key_F18:\n        return GDK_KEY_F18;\n    case Qt::Key_F19:\n        return GDK_KEY_F19;\n    case Qt::Key_F20:\n        return GDK_KEY_F20;\n    case Qt::Key_F21:\n        return GDK_KEY_F21;\n    }\n\n    return (guint)qKey;\n}\n\nQt::MouseButton qt_convertGButtonToQButton(guint button)\n{\n    Qt::MouseButton b = Qt::NoButton;\n    switch (button) {\n    case 1:\n        b = Qt::LeftButton;\n        break;\n    case 2:\n        b = Qt::MiddleButton;\n        break;\n    case 3:\n        b = Qt::RightButton;\n        break;\n    case 4:\n        b = Qt::ExtraButton1;\n        break;\n    case 5:\n        b = Qt::ExtraButton2;\n        break;\n    case 6:\n        b = Qt::ExtraButton3;\n        break;\n    case 7:\n        b = Qt::ExtraButton4;\n        break;\n    case 8:\n        b = Qt::ExtraButton5;\n        break;\n    case 9:\n        b = Qt::ExtraButton6;\n        break;\n    case 10:\n        b = Qt::ExtraButton7;\n        break;\n    case 11:\n        b = Qt::ExtraButton8;\n        break;\n    case 12:\n        b = Qt::ExtraButton9;\n        break;\n    case 13:\n        b = Qt::ExtraButton10;\n        break;\n    case 14:\n        b = Qt::ExtraButton11;\n        break;\n    case 15:\n        b = Qt::ExtraButton12;\n        break;\n    case 16:\n        b = Qt::ExtraButton13;\n        break;\n    case 17:\n        b = Qt::ExtraButton14;\n        break;\n    case 18:\n        b = Qt::ExtraButton15;\n        break;\n    case 19:\n        b = Qt::ExtraButton16;\n        break;\n    case 20:\n        b = Qt::ExtraButton17;\n        break;\n    case 21:\n        b = Qt::ExtraButton18;\n        break;\n    case 22:\n        b = Qt::ExtraButton19;\n        break;\n    case 23:\n        b = Qt::ExtraButton20;\n        break;\n    case 24:\n        b = Qt::ExtraButton21;\n        break;\n    case 25:\n        b = Qt::ExtraButton22;\n        break;\n    case 26:\n        b = Qt::ExtraButton23;\n        break;\n    case 27:\n        b = Qt::ExtraButton24;\n        break;\n    default:\n        qWarning() << \"Unrecognized button\" << button;\n    }\n\n    return b;\n}\n\nQt::TouchPointState qt_convertToQtTouchPointState(GdkEventType type)\n{\n    switch (type) {\n    case GDK_TOUCH_BEGIN:\n        return Qt::TouchPointPressed;\n    case GDK_TOUCH_UPDATE:\n        return Qt::TouchPointMoved;\n    case GDK_TOUCH_END:\n    case GDK_TOUCH_CANCEL:\n        return Qt::TouchPointReleased;\n    default:\n        Q_UNREACHABLE();\n    }\n}\n\ncairo_region_t *qt_convertToCairoRegion(const QRegion &region)\n{\n    cairo_region_t *r = cairo_region_create();\n    for (const QRect &qrect : region.rects()) {\n        cairo_rectangle_int_t rect = { qrect.x(), qrect.y(), qrect.width(), qrect.height() };\n        cairo_region_union_rectangle(r, &rect);\n    }\n    return r;\n}\n\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkhelpers.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkmenuitem.h\"\n#include \"qgtkrefptr.h\"\n\n#include <QtCore/qdebug.h>\n\n#ifndef QGTKHELPERS_H\n#define QGTKHELPERS_H\n\nQT_BEGIN_NAMESPACE\n\nQGtkRefPtr<GdkPixbuf> qt_imageToPixbuf(const QImage &image);\nQGtkRefPtr<GdkPixbuf> qt_pixmapToPixbuf(const QPixmap &pixmap);\nQImage qt_pixbufToImage(const QGtkRefPtr<GdkPixbuf> &pixbuf);\nQGtkRefPtr<GdkPixbuf> qt_iconToPixbuf(const QIcon &icon);\nQGtkRefPtr<GIcon> qt_iconToIcon(const QIcon &icon);\nQString qt_convertToGtkMnemonics(const QString &text);\nQt::KeyboardModifiers qt_convertToQtKeyboardMods(guint mask);\nQt::Key qt_convertToQtKey(int keyval);\nguint qt_convertToGdkKeyval(Qt::Key qKey);\nQt::MouseButton qt_convertGButtonToQButton(guint button);\nQt::TouchPointState qt_convertToQtTouchPointState(GdkEventType type);\ncairo_region_t *qt_convertToCairoRegion(const QRegion &region);\n\nQT_END_NAMESPACE\n\n#endif\n"
  },
  {
    "path": "src/platform-plugin/qgtkintegration.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkintegration.h\"\n#include \"qgtkbackingstore.h\"\n#include \"qgtkwindow.h\"\n#include \"qgtkscreen.h\"\n#include \"qgtktheme.h\"\n#include \"qgtkopenglcontext.h\"\n#include \"qgtkeventdispatcher.h\"\n#include \"qgtkclipboard.h\"\n\n#include <QtWidgets/qapplication.h>\n#include <QtGui/private/qpixmap_raster_p.h>\n#include <QtGui/private/qguiapplication_p.h>\n\n#if QT_VERSION >= QT_VERSION_CHECK(5,8,0)\n#include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h>\n#include <QtServiceSupport/private/qgenericunixservices_p.h>\n#else\n#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h>\n#include <QtPlatformSupport/private/qgenericunixservices_p.h>\n#endif\n\n#include <gtk/gtk.h>\n\n#include <libnotify/notify.h>\n\n#ifdef GDK_WINDOWING_WAYLAND\n#include <EGL/egl.h>\n#include <gdk/gdkwayland.h>\nstatic EGLDisplay createWaylandEGLDisplay(wl_display *display);\n#endif\n#ifdef GDK_WINDOWING_X11\n# include <gdk/gdkx.h>\n# include <X11/Xlib-xcb.h>\n#endif\n\n#include \"CSystrace.h\"\n\nQT_BEGIN_NAMESPACE\n\nclass QCoreTextFontEngine;\n\nvoid monitor_added(GdkDisplay *, GdkMonitor *monitor, gpointer integration)\n{\n    QGtkIntegration *ig = static_cast<QGtkIntegration*>(integration);\n    ig->onMonitorAdded(monitor);\n}\n\nvoid monitor_removed(GdkDisplay *, GdkMonitor *monitor, gpointer integration)\n{\n    QGtkIntegration *ig = static_cast<QGtkIntegration*>(integration);\n    ig->onMonitorRemoved(monitor);\n}\n\nQGtkIntegration::QGtkIntegration(const QStringList &)\n    : m_services(new QGtkServices)\n    , m_fontDatabase(new QGenericUnixFontDatabase)\n    , m_eglDisplay(nullptr)\n{\n    systrace_init();\n    gtk_init(NULL, NULL);\n    notify_init(qApp->applicationName().toUtf8().constData());\n\n    // Set up screens\n    m_display = gdk_display_get_default();\n    g_signal_connect(m_display, \"monitor-added\", G_CALLBACK(monitor_added), this);\n    g_signal_connect(m_display, \"monitor-removed\", G_CALLBACK(monitor_removed), this);\n\n    int num_monitors = gdk_display_get_n_monitors(m_display);\n\n    for (int i = 0; i < num_monitors; i++) {\n        GdkMonitor *monitor = gdk_display_get_monitor(m_display, i);\n        monitor_added(m_display, monitor, this);\n    }\n\n#ifdef GDK_WINDOWING_WAYLAND\n    if (GDK_IS_WAYLAND_DISPLAY(m_display)) {\n        wl_display *wldisplay = gdk_wayland_display_get_wl_display(GDK_WAYLAND_DISPLAY(m_display));\n        m_eglDisplay = createWaylandEGLDisplay(wldisplay);\n        Q_ASSERT(m_eglDisplay);\n    }\n    else\n#endif\n#ifdef GDK_WINDOWING_X11\n    if (GDK_IS_X11_DISPLAY(m_display)) {\n        qWarning() << \"Application is running under X11. While this may work, it is experimental.\";\n        qWarning() << \"Run under Wayland for best results.\";\n    }\n    else\n#endif\n        qWarning(\"GTK platform does not support this display backend; GL contexts will fail\");\n}\n\nQGtkIntegration::~QGtkIntegration()\n{\n    notify_uninit();\n#ifdef GDK_WINDOWING_WAYLAND\n    if (m_eglDisplay) {\n        eglTerminate(m_eglDisplay);\n    }\n#endif\n    systrace_deinit();\n}\n\nvoid QGtkIntegration::onMonitorAdded(GdkMonitor *monitor)\n{\n    bool isPrimary = gdk_monitor_is_primary(monitor) || m_screens.count() == 0;\n    qDebug() << \"Added \" << monitor << \" isPrimary \" << isPrimary;\n    QGtkScreen *screen = new QGtkScreen(monitor);\n    screen->setPrimary(isPrimary);\n    m_screens.append(screen);\n#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))\n    QWindowSystemInterface::handleScreenAdded(screen, isPrimary);\n#else\n    screenAdded(screen, isPrimary);\n#endif\n    if (isPrimary) {\n        qDebug() << \"Changed primary screen on add\";\n        // clear old screens\n        for (int i = 0; i < m_screens.count(); i++) {\n            QGtkScreen *os = m_screens.at(i);\n            screen->setPrimary(os == screen);\n        }\n\n#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))\n        QWindowSystemInterface::handlePrimaryScreenChanged(screen);\n#else\n        setPrimaryScreen(screen);\n#endif\n    }\n}\n\nvoid QGtkIntegration::onMonitorRemoved(GdkMonitor *monitor)\n{\n    qDebug() << \"Removed \" << monitor;\n    for (int i = 0; i < m_screens.count(); ++i) {\n        QGtkScreen *screen = m_screens.at(i);\n        if (screen->monitor() == monitor) {\n            qDebug() << \"Removing QGtkScreen \" << screen << screen->isPrimary();\n            bool wasPrimary = screen->isPrimary();\n#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))\n            QWindowSystemInterface::handleScreenRemoved(screen);\n#else\n            removeScreen(screen->screen());\n#endif\n            m_screens.removeAt(i);\n\n            if (wasPrimary) {\n                qDebug() << \"Changed primary screen on remove\";\n                GdkMonitor *primaryScreen = gdk_display_get_primary_monitor(m_display);\n                QGtkScreen *newPrimary = nullptr;\n                for (int i = 0; i < m_screens.count(); ++i) {\n                    QGtkScreen *screen = m_screens.at(i);\n                    screen->setPrimary(false);\n                    if (screen->monitor() == primaryScreen || primaryScreen == nullptr) {\n                        qDebug() << \"Changed primary screen to index \" << i << \" ptr \" << screen;\n                        newPrimary = screen;\n                    }\n                }\n\n#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))\n                QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary);\n#else\n                setPrimaryScreen(newPrimary);\n#endif\n                newPrimary->setPrimary(true);\n            }\n\n            return;\n        }\n    }\n\n}\n\nQPlatformOpenGLContext *QGtkIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const\n{\n#ifdef GDK_WINDOWING_WAYLAND\n    if (GDK_IS_WAYLAND_DISPLAY(m_display)) {\n        return new QGtkWaylandContext(context->format(), static_cast<QGtkOpenGLContext*>(context->shareHandle()));\n    }\n#endif\n#ifdef GDK_WINDOWING_X11\n    if (GDK_IS_X11_DISPLAY(m_display)) {\n        return new QGtkX11Context(context->format(), static_cast<QGtkOpenGLContext*>(context->shareHandle()));\n    }\n#endif\n    return nullptr;\n}\n\nbool QGtkIntegration::hasCapability(QPlatformIntegration::Capability cap) const\n{\n    switch (cap) {\n    case ThreadedPixmaps:\n    case MultipleWindows:\n    case OpenGL:\n    case ThreadedOpenGL:\n    case RasterGLSurface:\n    case WindowManagement:\n        return true;\n    default:\n        return QPlatformIntegration::hasCapability(cap);\n    }\n}\n\nQPlatformClipboard *QGtkIntegration::clipboard() const\n{\n    if (m_clipboard == nullptr) {\n        QGtkIntegration *that = const_cast<QGtkIntegration*>(this);\n        that->m_clipboard = new QGtkClipboard(that);\n    }\n    return this->m_clipboard;\n}\n\nQPlatformFontDatabase *QGtkIntegration::fontDatabase() const\n{\n    return m_fontDatabase.data();\n}\n\nQStringList QGtkIntegration::themeNames() const\n{\n    return QStringList(QLatin1String(QGtkTheme::name));\n}\n\nQPlatformTheme *QGtkIntegration::createPlatformTheme(const QString &name) const\n{\n    if (name == QLatin1String(QGtkTheme::name))\n        return new QGtkTheme;\n    return QPlatformIntegration::createPlatformTheme(name);\n}\n\nQPlatformServices *QGtkIntegration::services() const\n{\n    return m_services.data();\n}\n\nQPlatformNativeInterface *QGtkIntegration::nativeInterface() const\n{\n    return const_cast<QGtkIntegration*>(this);\n}\n\nvoid *QGtkIntegration::nativeResourceForIntegration(const QByteArray &resource)\n{\n    void *result = 0;\n\n    if (resource == \"egldisplay\") {\n        result = reinterpret_cast<void*>(m_eglDisplay);\n    } else if (resource == \"connection\") {\n#ifdef GDK_WINDOWING_X11\n        static bool xcb_warned = false;\n        if (!xcb_warned) {\n            qWarning() << \"XCB connection requested; this is experimental, and may not work well.\";\n            xcb_warned = true;\n        }\n        Display *dpy = nullptr;\n        if (GDK_IS_X11_DISPLAY(m_display)) {\n            dpy = gdk_x11_display_get_xdisplay(m_display);\n        } else {\n            qWarning() << \"Can't get XCB connection, GDK_BACKEND is not X11.\";\n        }\n        xcb_connection_t *conn = XGetXCBConnection(dpy);\n        result = reinterpret_cast<void*>(conn);\n#endif\n    } else if (resource == \"display\") {\n#ifdef GDK_WINDOWING_X11\n        static bool x11_warned = false;\n        if (!x11_warned) {\n            qWarning() << \"X11 display handle; this is experimental, and may not work well.\";\n            x11_warned = true;\n        }\n        Display *dpy = nullptr;\n        if (GDK_IS_X11_DISPLAY(m_display)) {\n            dpy = gdk_x11_display_get_xdisplay(m_display);\n        } else {\n            qWarning() << \"Can't get XCB connection, GDK_BACKEND is not X11.\";\n        }\n        result = reinterpret_cast<void*>(dpy);\n#endif\n    } else {\n        qWarning() << \"Unimplemented request for \" << resource;\n    }\n\n    return result;\n}\n\nvoid *QGtkIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)\n{\n    void *result = 0;\n    QByteArray res = resource.toLower();\n    // ### notify on change\n    if (res == \"antialiasingenabled\") {\n        int aa = -1;\n        g_object_get(gtk_settings_get_default(), \"gtk-xft-antialias\", &aa, NULL);\n        result = reinterpret_cast<void*>(aa + 1);\n    } else if (res == \"subpixeltype\") {\n        GtkSettings *s = gtk_settings_get_default();\n        gchararray value;\n        g_object_get(s, \"gtk-xft-rgba\", &value, NULL);\n        QString qtVal = QString::fromUtf8(value);\n        g_free(value);\n\n        QFontEngine::SubpixelAntialiasingType type = QFontEngine::SubpixelAntialiasingType(-1);\n        if (qtVal == \"none\") {\n            type = QFontEngine::Subpixel_None;\n        } else if (qtVal == \"rgb\") {\n            type = QFontEngine::Subpixel_RGB;\n        } else if (qtVal == \"bgr\") {\n            type = QFontEngine::Subpixel_BGR;\n        } else if (qtVal == \"vrgb\") {\n            type = QFontEngine::Subpixel_VRGB;\n        } else if (qtVal == \"vbgr\") {\n            type = QFontEngine::Subpixel_VBGR;\n        }\n\n        result = reinterpret_cast<void*>(type + 1);\n    } else if (resource == \"rootwindow\") {\n#ifdef GDK_WINDOWING_X11\n        static bool rootwin_warned = false;\n        if (!rootwin_warned) {\n            qWarning() << \"X root window requested; this is experimental, and may not work well.\";\n            rootwin_warned = true;\n        }\n        Display *dpy = nullptr;\n        xcb_screen_t *screen = nullptr;\n        if (GDK_IS_X11_DISPLAY(m_display)) {\n            dpy = gdk_x11_display_get_xdisplay(m_display);\n            xcb_connection_t *conn = XGetXCBConnection(dpy);\n\n            // use the first screen... hopefully this is okay? sigh...\n            screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;\n        } else {\n            qWarning() << \"Can't get root X window, GDK_BACKEND is not X11.\";\n        }\n\n        result = reinterpret_cast<void*>(screen ? screen->root : 0);\n#endif\n    } else {\n        qWarning() << \"Unimplemented request for \" << resource << \" on \" << screen;\n    }\n    return result;\n}\n\nvoid *QGtkIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window)\n{\n    void *result = 0;\n    if (resource == \"gtkwindow\") {\n        return static_cast<QGtkWindow*>(window->handle())->gtkWindow().get();\n    }\n    qWarning() << \"Unimplemented request for \" << resource << \" on \" << window;\n    return result;\n}\n\nvoid *QGtkIntegration::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context)\n{\n    void *result = 0;\n\n    if (!context->handle()) {\n        return result;\n    }\n\n    result = static_cast<QGtkOpenGLContext*>(context->handle())->nativeResource(resource);\n    if (!result) {\n        qWarning() << \"Unimplemented request for \" << resource << \" on \" << context;\n    }\n    return result;\n}\n\nQPlatformNativeInterface::NativeResourceForContextFunction QGtkIntegration::nativeResourceFunctionForContext(const QByteArray &resource)\n{\n    qWarning() << \"Unimplemented request for \" << resource;\n    return 0;\n}\n\nQPlatformWindow *QGtkIntegration::createPlatformWindow(QWindow *window) const\n{\n    Q_UNUSED(window);\n    QGtkWindow *w = new QGtkWindow(window);\n    return w;\n}\n\nQPlatformBackingStore *QGtkIntegration::createPlatformBackingStore(QWindow *window) const\n{\n    return new QGtkBackingStore(window);\n}\n\nQAbstractEventDispatcher *QGtkIntegration::createEventDispatcher() const\n{\n    return new QGtkEventDispatcher;\n}\n\nQGtkIntegration *QGtkIntegration::instance()\n{\n    return static_cast<QGtkIntegration *>(QGuiApplicationPrivate::platformIntegration());\n}\n\n#ifdef GDK_WINDOWING_WAYLAND\nstatic EGLDisplay createWaylandEGLDisplay(wl_display *display)\n{\n    eglBindAPI(EGL_OPENGL_API);\n\n    EGLDisplay dpy = eglGetDisplay((EGLNativeDisplayType)display);\n    if (dpy == EGL_NO_DISPLAY) {\n        qWarning() << \"eglGetDisplay failed\";\n        return dpy;\n    }\n\n    if (!eglInitialize(dpy, NULL, NULL)) {\n        qWarning() << \"eglInitialize failed\";\n        return EGL_NO_DISPLAY;\n    }\n\n    return dpy;\n}\n#endif\n\nEGLDisplay QGtkIntegration::eglDisplay() const\n{\n    return m_eglDisplay;\n}\n\nQT_END_NAMESPACE\n"
  },
  {
    "path": "src/platform-plugin/qgtkintegration.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QPLATFORMINTEGRATION_GTK_H\n#define QPLATFORMINTEGRATION_GTK_H\n\n#include \"qgtkservices.h\"\n\n#include <qpa/qplatformintegration.h>\n#include <qpa/qplatformnativeinterface.h>\n#include <qpa/qplatformscreen.h>\n#include <QtCore/qscopedpointer.h>\n\n#include <gtk/gtk.h>\n#include <gdk/gdk.h>\n\ntypedef void *EGLDisplay;\n\nQT_BEGIN_NAMESPACE\n\nclass QTouchDevice;\nclass QGtkScreen;\nclass QGtkClipboard;\n\nclass QGtkIntegration : public QPlatformIntegration, public QPlatformNativeInterface\n{\npublic:\n    explicit QGtkIntegration(const QStringList &parameters);\n    ~QGtkIntegration();\n\n    QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;\n    bool hasCapability(QPlatformIntegration::Capability cap) const override;\n    QPlatformFontDatabase *fontDatabase() const override;\n    QPlatformClipboard *clipboard() const override;\n    QStringList themeNames() const override;\n    QPlatformTheme *createPlatformTheme(const QString &name) const override;\n    QPlatformServices *services() const override;\n    QPlatformNativeInterface *nativeInterface() const override;\n\n    // QPlatformNativeInterface\n    void *nativeResourceForIntegration(const QByteArray &resource) override;\n    void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) override;\n    void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;\n    void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) override;\n    NativeResourceForContextFunction nativeResourceFunctionForContext(const QByteArray &resource) override;\n\n    QPlatformWindow *createPlatformWindow(QWindow *window) const override;\n    QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;\n    QAbstractEventDispatcher *createEventDispatcher() const override;\n\n    static QGtkIntegration *instance();\n\n    void onMonitorAdded(GdkMonitor *monitor);\n    void onMonitorRemoved(GdkMonitor *monitor);\n\n    GtkApplication *application() const;\n\n    EGLDisplay eglDisplay() const;\n\nprivate:\n    QScopedPointer<QGtkServices> m_services;\n    QScopedPointer<QPlatformFontDatabase> m_fontDatabase;\n    GdkDisplay *m_display;\n    QVector<const char*> m_arguments; /* must remain allocated for gdk's sake */\n    QVector<QGtkScreen*> m_screens;\n    QGtkClipboard *m_clipboard = nullptr;\n\n    EGLDisplay m_eglDisplay; // non-null for wayland platforms\n};\n\nQT_END_NAMESPACE\n\n#endif\n"
  },
  {
    "path": "src/platform-plugin/qgtkmenu.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkwindow.h\"\n#include \"qgtkmenu.h\"\n#include \"qgtkmenuitem.h\"\n#include \"qgtkhelpers.h\"\n\n#include <QtGui/qguiapplication.h>\n#include <QtGui/qwindow.h>\n#include <QtCore/qdebug.h>\n#include <QtCore/qloggingcategory.h>\n\nQ_LOGGING_CATEGORY(lcMenu, \"qt.qpa.gtk.menu\");\n\nQGtkMenu::QGtkMenu()\n    : m_tag((qintptr)this)\n{\n}\n\nQGtkMenu::~QGtkMenu()\n{\n}\n\nvoid QGtkMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)\n{\n    QGtkMenuItem *mi = static_cast<QGtkMenuItem*>(menuItem);\n    QGtkMenuItem *bi = static_cast<QGtkMenuItem*>(before);\n\n    int idx = m_items.indexOf(bi);\n    if (idx < 0) {\n        m_items.append(mi);\n    } else {\n        m_items.insert(idx, mi);\n    }\n    if (mi->menu()) {\n        connect(mi->menu(), &QGtkMenu::updated, this, &QGtkMenu::updated, Qt::UniqueConnection);\n    }\n    qCDebug(lcMenu) << \"Added menu item \" << mi << \" before \" << bi << \" at \" << idx;\n    Q_EMIT updated();\n}\n\nvoid QGtkMenu::removeMenuItem(QPlatformMenuItem *menuItem)\n{\n    QGtkMenuItem *mi = static_cast<QGtkMenuItem*>(menuItem);\n    int idx = m_items.indexOf(mi);\n    m_items.removeAt(idx);\n    m_items.removeAll(0); // if it was deleted, remove those too.\n    if (mi->menu()) {\n        disconnect(mi->menu(), &QGtkMenu::updated, this, &QGtkMenu::updated);\n    }\n    Q_EMIT updated();\n}\n\nvoid QGtkMenu::syncMenuItem(QPlatformMenuItem *menuItem)\n{\n    QGtkMenu *m = static_cast<QGtkMenuItem*>(menuItem)->menu();\n    if (m) {\n        connect(m, &QGtkMenu::updated, this, &QGtkMenu::updated, Qt::UniqueConnection);\n    }\n\n    Q_EMIT updated();\n}\n\nvoid QGtkMenu::syncSeparatorsCollapsible(bool enable)\n{\n    Q_UNUSED(enable);\n}\n\nvoid QGtkMenu::setTag(quintptr tag)\n{\n    m_tag = tag;\n    Q_EMIT updated();\n}\n\nquintptr QGtkMenu::tag()const\n{\n    return m_tag;\n}\n\nvoid QGtkMenu::setText(const QString &text)\n{\n    m_text = text;\n    Q_EMIT updated();\n}\n\nvoid QGtkMenu::setIcon(const QIcon &icon)\n{\n    Q_UNUSED(icon);\n}\n\nvoid QGtkMenu::setEnabled(bool enabled)\n{\n    m_enabled = enabled;\n    Q_EMIT updated();\n}\n\nbool QGtkMenu::isEnabled() const\n{\n    return m_enabled;\n}\n\nvoid QGtkMenu::setVisible(bool visible)\n{\n    //aboutToShow, aboutToHide signals\n    Q_UNUSED(visible);\n    m_visible = visible;\n    Q_EMIT updated();\n}\n\nvoid QGtkMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item)\n{\n    Q_UNUSED(item);\n\n    if (m_popup) {\n        dismiss();\n    }\n\n    Q_EMIT aboutToShow();\n\n    QPoint pos = QPoint(targetRect.left(), targetRect.top() + targetRect.height());\n    if (parentWindow)\n        pos = parentWindow->mapToGlobal(pos);\n\n    m_popup = gtkMenu();\n    GdkRectangle gRect { pos.x(), pos.y(), targetRect.width(), targetRect.height() };\n    gtk_menu_popup_at_rect(\n        m_popup.get(),\n        gtk_widget_get_window(static_cast<QGtkWindow*>(parentWindow->handle())->gtkWindow().get()),\n        &gRect,\n        GdkGravity(GDK_GRAVITY_NORTH_WEST),\n        GdkGravity(GDK_GRAVITY_NORTH_WEST),\n        NULL\n    );\n    connect(qGuiApp, &QGuiApplication::focusObjectChanged, this, &QGtkMenu::dismiss);\n}\n\nvoid QGtkMenu::dismiss()\n{\n    Q_EMIT aboutToHide();\n\n    if (m_popup) {\n        gtk_menu_popdown(m_popup.get());\n        gtk_widget_destroy(GTK_WIDGET(m_popup.get()));\n        m_popup = nullptr;\n    }\n    disconnect(qGuiApp, &QGuiApplication::focusObjectChanged, this, &QGtkMenu::dismiss);\n}\n\nQPlatformMenuItem *QGtkMenu::menuItemAt(int position) const\n{\n    int idx = 0;\n    while (position >= 0 && idx < m_items.size()) {\n        if (m_items.at(idx)) {\n            position--;\n        }\n        idx++;\n    }\n    if (idx >= 0 && idx < m_items.size())\n        return m_items.at(idx);\n    return nullptr;\n}\n\nQPlatformMenuItem *QGtkMenu::menuItemForTag(quintptr tag) const\n{\n    for (QGtkMenuItem *item : qAsConst(m_items)) {\n        if (item && item->tag() == tag) {\n            return item;\n        }\n    }\n\n    return nullptr;\n}\n\nQGtkRefPtr<GtkMenuItem> QGtkMenu::gtkMenuItem() const\n{\n    QGtkRefPtr<GtkMenuItem> mi = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(qt_convertToGtkMnemonics(m_text).toUtf8().constData()));\n    gtk_menu_item_set_submenu(mi.get(), GTK_WIDGET(gtkMenu().get()));\n    gtk_widget_set_sensitive(GTK_WIDGET(mi.get()), m_enabled);\n    gtk_widget_set_visible(GTK_WIDGET(mi.get()), m_visible);\n    return mi;\n}\n\nQGtkRefPtr<GtkMenu> QGtkMenu::gtkMenu() const\n{\n    QGtkRefPtr<GtkMenu> menu = GTK_MENU(gtk_menu_new());\n    for (QGtkMenuItem *i : m_items) {\n        if (i)\n            gtk_menu_shell_append(GTK_MENU_SHELL(menu.get()), i->gtkMenuItem().get());\n    }\n    return menu;\n}\n\nQVector<QPointer<QGtkMenuItem>> QGtkMenu::items() const\n{\n    return m_items;\n}\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkmenu.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKMENU_H\n#define QGTKMENU_H\n\n#include \"qgtkrefptr.h\"\n\n#include <qpa/qplatformmenu.h>\n\n#include <gtk/gtk.h>\n\nQT_BEGIN_NAMESPACE\n\nclass QGtkMenuItem;\n\nclass QGtkMenu : public QPlatformMenu\n{\n    Q_OBJECT\npublic:\n    QGtkMenu();\n    ~QGtkMenu();\n\n    void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override;\n    void removeMenuItem(QPlatformMenuItem *menuItem) override;\n    void syncMenuItem(QPlatformMenuItem *menuItem) override;\n    void syncSeparatorsCollapsible(bool enable) override;\n\n    void setTag(quintptr tag) override;\n    quintptr tag()const override;\n\n    void setText(const QString &text) override;\n    void setIcon(const QIcon &icon) override;\n    void setEnabled(bool enabled) override;\n    bool isEnabled() const override;\n    void setVisible(bool visible) override;\n\n    void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem  *item) override;\n    void dismiss() override;\n\n    QPlatformMenuItem *menuItemAt(int position) const override;\n    QPlatformMenuItem *menuItemForTag(quintptr tag) const override;\n\n    QGtkRefPtr<GtkMenu> gtkMenu() const;\n    QGtkRefPtr<GtkMenuItem> gtkMenuItem() const;\n\n    QVector<QPointer<QGtkMenuItem>> items() const;\n\nQ_SIGNALS:\n    void updated();\n\nprivate:\n    QVector<QPointer<QGtkMenuItem>> m_items;\n    QGtkRefPtr<GtkMenu> m_popup = nullptr;\n    bool m_enabled = true;\n    bool m_visible = true;\n    QString m_text;\n    qintptr m_tag;\n};\n\nQT_END_NAMESPACE\n\n#endif // QGTKMENU_H\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkmenubar.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkmenubar.h\"\n#include \"qgtkmenu.h\"\n#include \"qgtkmenuitem.h\"\n#include \"qgtkwindow.h\"\n\n#include <QtCore/qdebug.h>\n#include <QtCore/qloggingcategory.h>\n\nQ_LOGGING_CATEGORY(lcMenuBar, \"qt.qpa.gtk.menubar\");\n\nQGtkMenuBar::QGtkMenuBar()\n{\n    connect(this, &QGtkMenuBar::updated, this, &QGtkMenuBar::queueRegenerate);\n}\n\nQGtkMenuBar::~QGtkMenuBar()\n{\n}\n\nvoid QGtkMenuBar::insertMenu(QPlatformMenu *menu, QPlatformMenu *before)\n{\n    QGtkMenu *m = static_cast<QGtkMenu*>(menu);\n    QGtkMenu *b = static_cast<QGtkMenu*>(before);\n\n    Q_ASSERT(m && !m_items.contains(m));\n    Q_ASSERT(!b || m_items.contains(b));\n\n    int idx = m_items.indexOf(b);\n    qCDebug(lcMenuBar) << \"Inserting menu \" << m << idx;\n    if (idx < 0) {\n        m_items.append(m);\n    } else {\n        m_items.insert(idx, m);\n    }\n\n    connect(m, &QGtkMenu::updated, this, &QGtkMenuBar::queueRegenerate);\n    syncMenu(menu);\n    Q_EMIT updated();\n}\n\nvoid QGtkMenuBar::removeMenu(QPlatformMenu *menu)\n{\n    QGtkMenu *m = static_cast<QGtkMenu*>(menu);\n\n    int idx = m_items.indexOf(m);\n    Q_ASSERT(idx >= 0);\n    qCDebug(lcMenuBar) << \"Removing menu \" << m_items.at(idx) << idx;\n    m_items.removeAt(idx);\n    m_items.removeAll(0); // if it was deleted, remove nulls too.\n\n    disconnect(m, &QGtkMenu::updated, this, &QGtkMenuBar::queueRegenerate);\n    Q_EMIT updated();\n}\n\nvoid QGtkMenuBar::syncMenu(QPlatformMenu *menuItem)\n{\n    QGtkMenu *menu = static_cast<QGtkMenu*>(menuItem);\n    for (QGtkMenuItem *item : menu->items()) {\n        if (item)\n            menu->syncMenuItem(item);\n    }\n}\n\nvoid QGtkMenuBar::queueRegenerate()\n{\n    if (m_regenerateQueued) {\n        return;\n    }\n\n    QMetaObject::invokeMethod(this, \"regenerate\", Qt::QueuedConnection);\n    m_regenerateQueued = true;\n}\n\nvoid QGtkMenuBar::regenerate()\n{\n    m_regenerateQueued = false;\n    GtkContainer *omb = GTK_CONTAINER(m_menubar.get());\n    GList *children = gtk_container_get_children(omb);\n    for (GList *iter = children; iter != NULL; iter = g_list_next(iter)) {\n        GtkWidget *menuChild = (GtkWidget*)iter->data;\n        gtk_container_remove(omb, menuChild);\n    }\n    g_list_free(children);\n\n    for (QGtkMenu *menu : m_items) {\n        if (menu)\n            gtk_menu_shell_append(GTK_MENU_SHELL(m_menubar.get()), GTK_WIDGET(menu->gtkMenuItem().get()));\n    }\n}\n\nvoid QGtkMenuBar::handleReparent(QWindow *newParentWindow)\n{\n    QGtkRefPtr<GtkMenuBar> oldMenuBar = m_menubar;\n\n    if (!newParentWindow) {\n        m_menubar.reset(nullptr);\n    } else {\n        QGtkWindow *w = static_cast<QGtkWindow*>(newParentWindow->handle());\n        if (!w) {\n            // force creation of pwin\n            newParentWindow->create();\n        }\n        w = static_cast<QGtkWindow*>(newParentWindow->handle());\n        m_menubar = w->gtkMenuBar();\n    }\n\n    if (oldMenuBar) {\n        GtkContainer *omb = GTK_CONTAINER(oldMenuBar.get());\n        GtkContainer *nmb = GTK_CONTAINER(m_menubar.get());\n        GList *children = gtk_container_get_children(omb);\n        for (GList *iter = children; iter != NULL; iter = g_list_next(iter)) {\n            GtkWidget *menuChild = (GtkWidget*)iter->data;\n            g_object_ref(menuChild); // temporaray ref, to save it past remove()\n            gtk_container_remove(omb, menuChild);\n            if (m_menubar.get()) {\n                gtk_container_add(nmb, menuChild);\n            }\n            g_object_unref(menuChild);\n        }\n        g_list_free(children);\n    }\n}\n\nQPlatformMenu *QGtkMenuBar::menuForTag(quintptr tag) const\n{\n    for (QGtkMenu *menu : qAsConst(m_items)) {\n        if (menu && menu->tag() == tag) {\n            return menu;\n        }\n    }\n\n    return nullptr;\n}\n\nQPlatformMenu *QGtkMenuBar::createMenu() const\n{\n    return new QGtkMenu;\n}\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkmenubar.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKMENUBAR_H\n#define QGTKMENUBAR_H\n\n#include \"qgtkrefptr.h\"\n\n#include <qpa/qplatformmenu.h>\n\n#include <gtk/gtk.h>\n\nQT_BEGIN_NAMESPACE\n\nclass QGtkMenu;\n\nclass QGtkMenuBar : public QPlatformMenuBar\n{\n    Q_OBJECT\npublic:\n    QGtkMenuBar();\n    ~QGtkMenuBar();\n\n    void insertMenu(QPlatformMenu *menu, QPlatformMenu *before) override;\n    void removeMenu(QPlatformMenu *menu) override;\n    void syncMenu(QPlatformMenu *menuItem) override;\n    void handleReparent(QWindow *newParentWindow) override;\n\n    QPlatformMenu *menuForTag(quintptr tag) const override;\n    QPlatformMenu *createMenu() const override;\n\nQ_SIGNALS:\n    void updated();\n\nprivate Q_SLOTS:\n    void queueRegenerate();\n    void regenerate();\n\nprivate:\n    QGtkRefPtr<GtkMenuBar> m_menubar;\n    QVector<QPointer<QGtkMenu>> m_items;\n    bool m_regenerateQueued = false;\n};\n\nQT_END_NAMESPACE\n\n#endif // QGTKMENUBAR\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkmenuitem.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkmenuitem.h\"\n#include \"qgtkhelpers.h\"\n#include \"qgtkmenu.h\"\n\n#include <QtCore/qdebug.h>\n\nstatic void select_cb(GtkMenuItem *, gpointer qgtkMenuItem)\n{\n    QGtkMenuItem *gm = static_cast<QGtkMenuItem*>(qgtkMenuItem);\n    gm->emitSelect();\n}\n\nstatic void activate_cb(GtkMenuItem *, gpointer qgtkMenuItem)\n{\n    QGtkMenuItem *gm = static_cast<QGtkMenuItem*>(qgtkMenuItem);\n    gm->emitActivate();\n}\n\nQGtkMenuItem::QGtkMenuItem()\n    : m_tag((qintptr)this)\n{\n}\n\nQGtkMenuItem::~QGtkMenuItem()\n{\n}\n\n\nvoid QGtkMenuItem::setTag(quintptr tag)\n{\n    m_tag = tag;\n    Q_EMIT updated();\n}\n\nquintptr QGtkMenuItem::tag()const\n{\n    return m_tag;\n}\n\nvoid QGtkMenuItem::setText(const QString &text)\n{\n    m_text = qt_convertToGtkMnemonics(text);\n    Q_EMIT updated();\n}\n\nvoid QGtkMenuItem::setIcon(const QIcon &icon)\n{\n    Q_UNUSED(icon);\n}\n\nvoid QGtkMenuItem::setMenu(QPlatformMenu *pmenu)\n{\n    QGtkMenu *childMenu = static_cast<QGtkMenu*>(pmenu);\n    m_childMenu = childMenu;\n    Q_EMIT updated();\n}\n\nvoid QGtkMenuItem::setVisible(bool isVisible)\n{\n    m_visible = isVisible;\n    Q_EMIT updated();\n}\n\nvoid QGtkMenuItem::setIsSeparator(bool isSeparator)\n{\n    m_isSeparator = isSeparator;\n    Q_EMIT updated();\n}\n\nvoid QGtkMenuItem::setFont(const QFont &font)\n{\n    Q_UNUSED(font);\n}\n\nvoid QGtkMenuItem::setRole(MenuRole role)\n{\n    Q_UNUSED(role);\n}\n\nvoid QGtkMenuItem::setCheckable(bool checkable)\n{\n    m_checkable = checkable;\n    Q_EMIT updated();\n}\n\nvoid QGtkMenuItem::setChecked(bool isChecked)\n{\n    m_checked = isChecked;\n    Q_EMIT updated();\n}\n\nvoid QGtkMenuItem::setShortcut(const QKeySequence& shortcut)\n{\n    m_shortcut = shortcut;\n    Q_EMIT updated();\n}\n\nvoid QGtkMenuItem::setEnabled(bool enabled)\n{\n    m_enabled = enabled;\n    Q_EMIT updated();\n}\n\nvoid QGtkMenuItem::setIconSize(int size)\n{\n    Q_UNUSED(size);\n}\n\nvoid QGtkMenuItem::setNativeContents(WId item)\n{\n    Q_UNUSED(item);\n}\n\nvoid QGtkMenuItem::setHasExclusiveGroup(bool hasExclusiveGroup)\n{\n    m_hasExclusiveGroup = hasExclusiveGroup;\n    Q_EMIT updated();\n}\n\nQGtkRefPtr<GtkWidget> QGtkMenuItem::gtkMenuItem() const\n{\n    QGtkRefPtr<GtkWidget> ret;\n    if (m_isSeparator) {\n        ret = gtk_separator_menu_item_new();\n    } else if (m_childMenu) {\n        QGtkRefPtr<GtkMenuItem> mi = m_childMenu->gtkMenuItem();\n        //g_signal_connect(mi, \"select\", G_CALLBACK(select_cb), const_cast<QGtkMenuItem*>(this));\n        //g_signal_connect(mi, \"activate\", G_CALLBACK(activate_cb), const_cast<QGtkMenuItem*>(this));\n\n        // stick our title on it\n        GtkWidget *child = gtk_bin_get_child(GTK_BIN(mi.get()));\n        gtk_label_set_markup_with_mnemonic(GTK_LABEL(child), m_text.toUtf8().constData());\n        gtk_widget_set_sensitive(GTK_WIDGET(mi.get()), m_enabled);\n        ret = GTK_WIDGET(mi.get());\n    } else {\n        GtkWidget *mi = nullptr;\n        if (m_checkable) {\n            mi = gtk_check_menu_item_new_with_mnemonic(m_text.toUtf8().constData());\n            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), m_checked);\n        } else {\n            mi = gtk_menu_item_new_with_mnemonic(m_text.toUtf8().constData());\n        }\n\n        if (GTK_IS_CHECK_MENU_ITEM(mi)) {\n            g_object_set(mi, \"draw-as-radio\", m_hasExclusiveGroup, NULL);\n        }\n        gtk_widget_set_sensitive(mi, m_enabled);\n        g_signal_connect(mi, \"select\", G_CALLBACK(select_cb), const_cast<QGtkMenuItem*>(this));\n        g_signal_connect(mi, \"activate\", G_CALLBACK(activate_cb), const_cast<QGtkMenuItem*>(this));\n        GtkWidget *label = gtk_bin_get_child(GTK_BIN(mi));\n\n        Qt::KeyboardModifiers qtMods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier;\n\n        // Only attempt to map top level shortcuts. GTK+ accels only take a\n        // single key, but QKeySequence can take multiple-- to overcome this\n        // mismatch, we map the simple key sequences (and their modifiers), but\n        // don't even attempt to try map more complex sequences.\n        if (m_shortcut[1] == 0 && m_shortcut[2] == 0 && m_shortcut[3] == 0) {\n            guint gKey = qt_convertToGdkKeyval(Qt::Key(m_shortcut[0] & ~qtMods));\n            guint gModifiers = 0;\n\n            if (m_shortcut[0] & Qt::ShiftModifier) {\n                gModifiers |= GDK_SHIFT_MASK;\n            }\n            if (m_shortcut[0] & Qt::ControlModifier) {\n                gModifiers |= GDK_CONTROL_MASK;\n            }\n            if (m_shortcut[0] & Qt::AltModifier) {\n                gModifiers |= GDK_MOD1_MASK;\n            }\n            if (m_shortcut[0] & Qt::MetaModifier) {\n                gModifiers |= GDK_META_MASK;\n            }\n\n            gtk_accel_label_set_accel(GTK_ACCEL_LABEL(label), gKey, GdkModifierType(gModifiers));\n        }\n\n        ret = mi;\n    }\n\n    gtk_widget_set_visible(ret.get(), m_visible);\n\n    return ret;\n}\n\nvoid QGtkMenuItem::emitSelect()\n{\n    // ### right?\n    Q_EMIT hovered();\n}\n\nvoid QGtkMenuItem::emitActivate()\n{\n    Q_EMIT activated();\n}\n\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkmenuitem.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKMENUITEM_H\n#define QGTKMENUITEM_H\n\n#include \"qgtkrefptr.h\"\n#include \"qgtkmenu.h\"\n\n#include <qpa/qplatformmenu.h>\n\n#include <gtk/gtk.h>\n\nclass QGtkMenuItem : public QPlatformMenuItem\n{\n    Q_OBJECT\npublic:\n    QGtkMenuItem();\n    ~QGtkMenuItem();\n\n    void setTag(quintptr tag) override;\n    quintptr tag()const override;\n    void setText(const QString &text) override;\n    void setIcon(const QIcon &icon) override;\n    void setMenu(QPlatformMenu *menu) override;\n    void setVisible(bool isVisible) override;\n    void setIsSeparator(bool isSeparator) override;\n    void setFont(const QFont &font) override;\n    void setRole(MenuRole role) override;\n    void setCheckable(bool checkable) override;\n    void setChecked(bool isChecked) override;\n    void setShortcut(const QKeySequence& shortcut) override;\n    void setEnabled(bool enabled) override;\n    void setIconSize(int size) override;\n    void setNativeContents(WId item) override;\n    void setHasExclusiveGroup(bool hasExclusiveGroup) override;\n\n    QGtkRefPtr<GtkWidget> gtkMenuItem() const;\n\n    void emitSelect();\n    void emitActivate();\n\n    QGtkMenu *menu() { return m_childMenu.data(); }\n\nQ_SIGNALS:\n    void updated();\n\nprivate:\n    QString m_text;\n    bool m_checkable = false;\n    bool m_isSeparator = false;\n    bool m_enabled = true;\n    bool m_visible = true;\n    bool m_checked = false;\n    bool m_hasExclusiveGroup = false;\n    QPointer<QGtkMenu> m_childMenu;\n    QKeySequence m_shortcut;\n    qintptr m_tag;\n};\n\n#endif // QGTKMENUITEM_H\n\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkopenglcontext.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkopenglcontext.h\"\n#include \"qgtkwindow.h\"\n#include \"qgtkintegration.h\"\n\n#include <QtCore/qcoreapplication.h>\n#include <QtCore/qthread.h>\n#include <QtCore/qdebug.h>\n#include <QtGui/qopenglcontext.h>\n#include <QtGui/qopenglfunctions.h>\n\n#include <gtk/gtk.h>\n#include <gdk/gdk.h>\n#include <EGL/egl.h>\n#include <dlfcn.h>\n\n#ifdef GDK_WINDOWING_WAYLAND\n\n#if QT_VERSION >= QT_VERSION_CHECK(5,8,0)\n#include <QtEglSupport/private/qeglconvenience_p.h>\n#else\n#include <QtPlatformSupport/private/qeglconvenience_p.h>\n#endif\n\n#include <gdk/gdkwayland.h>\n#endif\n#ifdef GDK_WINDOWING_X11\n#include <gdk/gdkx.h>\n#endif\n\n#include <QLoggingCategory>\n\n#include \"CSystrace.h\"\n\nQ_LOGGING_CATEGORY(lcContext, \"qt.qpa.gtk.context\");\n\n// GDK creates an internal 'paint context' for each GDKWindow, and exposes\n// only an API to create additional contexts which share with the paint\n// context. Unfortunately, that means it's not possible to create a GDK\n// context that can share with multiple GDKWindows, which is a requirement\n// of how the QOpenGLContext API is structured. QOpenGLContext is created\n// independently of surfaces and can be attached to different services at\n// makeCurrent() time.\n//\n// Since the GDK APIs aren't really useful here, QGtkOpenGLContext creates\n// contexts directly with EGL/GLX.\n//\n// Even more unfortunately, GDK as of now doesn't actually have opengl\n// windows; all GL rendering happens into a framebuffer that is downloaded\n// and composited by cairo. Since this is what GDK would be doing anyway,\n// QGtkOpenGLContext downloads the framebuffer on swapBuffers and passes\n// the raster image back to QGtkWindow to composite.\n\nQGtkOpenGLContext::QGtkOpenGLContext(const QSurfaceFormat &format, QGtkOpenGLContext *shareContext)\n    : m_shareContext(nullptr)\n    , m_fbo(nullptr)\n    , m_fbo_mirrored(nullptr)\n{\n    m_format = format;\n    m_shareContext = shareContext;\n}\n\nQGtkOpenGLContext::~QGtkOpenGLContext()\n{\n    delete m_fbo;\n    delete m_fbo_mirrored;\n}\n\nQSurfaceFormat QGtkOpenGLContext::format() const\n{\n    return m_format;\n}\n\nGLuint QGtkOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) const\n{\n    // XXX This will result in recreating FBOs if a context renders to differently\n    // sized surfaces. It would be smarter to store the FBO with the surface, and\n    // recreate it only if the context changes.\n    Q_UNUSED(surface);\n    Q_ASSERT(m_fbo);\n    return m_fbo->handle();\n}\n\nvoid QGtkOpenGLContext::swapBuffers(QPlatformSurface *surface)\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkOpenGLContext::swapBuffers\");\n    QGtkWindow *win = static_cast<QGtkWindow*>(surface);\n\n    QImage *image = win->beginUpdateFrame(\"swapBuffers\");\n\n    // ### perhaps this should be done in one place (inside QGtkBackingStore)?\n    if (image->size() != QSize(m_fbo_mirrored->width(), m_fbo_mirrored->height()) ||\n        image->format() != QImage::Format_ARGB32)\n    {\n        *image = QImage(m_fbo_mirrored->width(), m_fbo_mirrored->height(), QImage::Format_ARGB32);\n        image->setDevicePixelRatio(win->devicePixelRatio());\n    }\n\n    // Download rendered frame, slowly, so slowly.\n    // First we need to invert y, otherwise we'll draw upside down. To do that,\n    // blit to a framebuffer with inverted coordinate.\n    QRect srcRect(0, image->size().height(), image->size().width(), -image->size().height());\n    QRect destRect(0, 0, image->size().width(), image->size().height());\n    QOpenGLFramebufferObject::blitFramebuffer(m_fbo_mirrored, destRect,\n                                              m_fbo, srcRect,\n                                              GL_COLOR_BUFFER_BIT, GL_LINEAR, 0, 0, QOpenGLFramebufferObject::DontRestoreFramebufferBinding);\n\n    // Now read back the flipped data into the backing store image.\n    QOpenGLFunctions funcs(QOpenGLContext::currentContext());\n    m_fbo_mirrored->bind();\n    funcs.glReadPixels(0, 0, image->width(), image->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, image->bits());\n\n    win->endUpdateFrame(\"swapBuffers\");\n    win->invalidateRegion(QRegion());\n\n    // If swap is called on the main thread, then assume that the application\n    // knows what it is doing, and can self-throttle (either via requestUpdate,\n    // or QWidget). We can't really do more than this because if we do block we\n    // may lose the chance to process some events.\n    //\n    // If the application requested a swap interval, though, that means they\n    // want us to block, so let's try so they don't render as fast as our little\n    // bits can take us.\n    if (m_format.swapInterval() > 0) {\n        // So yeah, this isn't exactly an ideal throttling mechanism, but it\n        // should be quite deadlock-proof, and works well enough for the time\n        // being. We take the allowed frame time, and if we're finishing ahead\n        // of the allowed time, we sleep. This isn't perfect in that it doesn't\n        // include the time taken by gtk+ to get the frame onto the display but\n        // it's better than nothing.\n        qint64 timeBudget = 1000 / win->window()->screen()->refreshRate();\n        qint64 delta = timeBudget - m_swapTimer.elapsed();\n        if (m_swapTimer.isValid() && m_swapTimer.elapsed() < timeBudget) {\n            TRACE_EVENT0(\"gfx\", \"QGtkOpenGLContext::swapBuffers::usleep\");\n            usleep(delta * 1000);\n        }\n        m_swapTimer.restart();\n    }\n}\n\nbool QGtkOpenGLContext::makeCurrent(QPlatformSurface *surface)\n{\n    QGtkWindow *win = static_cast<QGtkWindow*>(surface);\n    QSize sz = win->geometry().size() * win->devicePixelRatio();\n    if (sz.isEmpty())\n        sz = QSize(1, 1);\n\n    if (m_fbo && m_fbo->size() != sz) {\n        qCDebug(lcContext) << \"clearing old context FBO of size\" << m_fbo->size();\n        delete m_fbo_mirrored;\n        m_fbo_mirrored = nullptr;\n        // XXX ###: I've seen defaultFramebufferObject getting called when a\n        // QOpenGLFramebufferObject is deleted. That seems scary given this. To\n        // reproduce, delete m_fbo_mirrored after m_fbo is already set to\n        // nullptr, and watch it assert.\n        delete m_fbo;\n        m_fbo = nullptr;\n    }\n    if (!m_fbo) {\n        m_fbo = new QOpenGLFramebufferObject(sz, QOpenGLFramebufferObject::CombinedDepthStencil);\n        m_fbo_mirrored = new QOpenGLFramebufferObject(sz, QOpenGLFramebufferObject::CombinedDepthStencil);\n        qCDebug(lcContext) << \"created new context FBO of size\" << m_fbo->size();\n    }\n\n    if (!m_fbo->isValid())\n        return false;\n    m_fbo->bind();\n    return true;\n}\n\nvoid QGtkOpenGLContext::doneCurrent()\n{\n}\n\nbool QGtkOpenGLContext::isSharing() const\n{\n    return m_shareContext;\n}\n\nbool QGtkOpenGLContext::isValid() const\n{\n    return false;\n}\n\nvoid *QGtkOpenGLContext::nativeResource(const QByteArray &resource) const\n{\n    Q_UNUSED(resource);\n    return nullptr;\n}\n"
  },
  {
    "path": "src/platform-plugin/qgtkopenglcontext.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKOPENGLCONTEXT_H\n#define QGTKOPENGLCONTEXT_H\n\n#include <QtCore/qelapsedtimer.h>\n#include <QtGui/qopenglframebufferobject.h>\n#include <qpa/qplatformopenglcontext.h>\n#include <gdk/gdk.h>\n\n#ifdef GDK_WINDOWING_WAYLAND\ntypedef void *EGLContext;\ntypedef void *EGLDisplay;\ntypedef void *EGLConfig;\n#endif\n\nQT_BEGIN_NAMESPACE\n\nclass QGtkOpenGLContext : public QPlatformOpenGLContext\n{\npublic:\n    QGtkOpenGLContext(const QSurfaceFormat &format, QGtkOpenGLContext *shareContext);\n    ~QGtkOpenGLContext();\n\n    GLuint defaultFramebufferObject(QPlatformSurface *surface) const override;\n    QSurfaceFormat format() const override;\n\n    void swapBuffers(QPlatformSurface *surface) override;\n    bool makeCurrent(QPlatformSurface *surface) override;\n    void doneCurrent() override;\n\n    bool isSharing() const override;\n    bool isValid() const override;\n\n    virtual void *nativeResource(const QByteArray &resource) const;\n\nprotected:\n    QSurfaceFormat m_format;\n    QGtkOpenGLContext *m_shareContext;\n    QOpenGLFramebufferObject *m_fbo;\n    QOpenGLFramebufferObject *m_fbo_mirrored;\n    QElapsedTimer m_swapTimer;\n};\n\n#ifdef GDK_WINDOWING_WAYLAND\nclass QGtkWaylandContext : public QGtkOpenGLContext\n{\npublic:\n    using QGtkOpenGLContext::QGtkOpenGLContext;\n    virtual ~QGtkWaylandContext();\n\n    void initialize() override;\n    bool makeCurrent(QPlatformSurface *surface) override;\n    void doneCurrent() override;\n\n    QFunctionPointer getProcAddress(const char *procName) override;\n\n    bool isValid() const override;\n\n    virtual void *nativeResource(const QByteArray &resource) const override;\n    EGLContext eglContext() const;\n    EGLDisplay eglDisplay() const;\n    EGLConfig eglConfig() const;\n\nprotected:\n    EGLContext m_eglContext;\n    EGLDisplay m_eglDisplay;\n    EGLConfig m_eglConfig;\n};\n#endif\n\n#ifdef GDK_WINDOWING_X11\nclass QGtkX11Context : public QGtkOpenGLContext\n{\n    using QGtkOpenGLContext::QGtkOpenGLContext;\n    virtual ~QGtkX11Context();\n\n    void initialize() override;\n    bool makeCurrent(QPlatformSurface *surface) override;\n    void doneCurrent() override;\n\n    QFunctionPointer getProcAddress(const char *procName) override;\n\n    bool isValid() const override;\n\nprotected:\n    void *m_display;\n    void *m_glxContext;\n};\n#endif\n\nQT_END_NAMESPACE\n\n#endif // QGTKOPENGLCONTEXT_H\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkopenglcontext_wayland.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n// This file is not compiled if GDK does not support Wayland.\n#include \"qgtkopenglcontext.h\"\n#ifdef GDK_WINDOWING_WAYLAND\n\n#include \"qgtkintegration.h\"\n\n#include <QtCore/qdebug.h>\n#include <QtGui/qopenglcontext.h>\n\n#include <EGL/egl.h>\n#include <gdk/gdkwayland.h>\n#include <dlfcn.h>\n\n#if QT_VERSION >= QT_VERSION_CHECK(5,8,0)\n#include <QtEglSupport/private/qeglconvenience_p.h>\n#else\n#include <QtPlatformSupport/private/qeglconvenience_p.h>\n#endif\n\n#include <QLoggingCategory>\n#include \"CSystrace.h\"\n\nstatic QSurfaceFormat qgtk_wayland_update_format(const QSurfaceFormat &refFormat, EGLDisplay display, EGLContext context);\n\nvoid QGtkWaylandContext::initialize()\n{\n    QGtkIntegration *integration = QGtkIntegration::instance();\n    m_eglDisplay = integration->eglDisplay();\n    // QGtkWaylandContext should not be created on non-wayland displays\n    Q_ASSERT(m_eglDisplay);\n\n    EGLContext shareEgl = nullptr;\n    if (m_shareContext)\n        shareEgl = static_cast<QGtkWaylandContext*>(m_shareContext)->eglContext();\n\n    m_eglConfig = q_configFromGLFormat(m_eglDisplay, m_format);\n\n    QVector<EGLint> eglContextAttrs;\n    eglContextAttrs.append(EGL_CONTEXT_CLIENT_VERSION);\n    eglContextAttrs.append(m_format.majorVersion());\n    const bool hasKHRCreateContext = q_hasEglExtension(m_eglDisplay, \"EGL_KHR_create_context\");\n    if (hasKHRCreateContext) {\n        eglContextAttrs.append(EGL_CONTEXT_MINOR_VERSION_KHR);\n        eglContextAttrs.append(m_format.minorVersion());\n        int flags = 0;\n        // The debug bit is supported both for OpenGL and OpenGL ES.\n        if (m_format.testOption(QSurfaceFormat::DebugContext))\n            flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;\n        // The fwdcompat bit is only for OpenGL 3.0+.\n        if (m_format.renderableType() == QSurfaceFormat::OpenGL\n            && m_format.majorVersion() >= 3\n            && !m_format.testOption(QSurfaceFormat::DeprecatedFunctions))\n            flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;\n        if (flags) {\n            eglContextAttrs.append(EGL_CONTEXT_FLAGS_KHR);\n            eglContextAttrs.append(flags);\n        }\n        // Profiles are OpenGL only and mandatory in 3.2+. The value is silently ignored for < 3.2.\n        if (m_format.renderableType() == QSurfaceFormat::OpenGL) {\n            eglContextAttrs.append(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR);\n            eglContextAttrs.append(m_format.profile() == QSurfaceFormat::CoreProfile\n                                ? EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR\n                                : EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR);\n        }\n    }\n    eglContextAttrs.append(EGL_NONE);\n\n    m_format = q_glFormatFromConfig(m_eglDisplay, m_eglConfig, m_format);\n    m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig,\n                                    shareEgl, eglContextAttrs.data());\n\n\tif (m_eglContext)\n        m_format = qgtk_wayland_update_format(m_format, m_eglDisplay, m_eglContext);\n}\n\n// Gently borrowed from the wayland QPA plugin; this code appears in several places,\n// it should really be put into eglconvenience instead.\nstatic QSurfaceFormat qgtk_wayland_update_format(const QSurfaceFormat &refFormat, EGLDisplay display, EGLContext context)\n{\n\tQSurfaceFormat format = refFormat;\n\n    // Have to save & restore to prevent QOpenGLContext::currentContext() from becoming\n    // inconsistent after QOpenGLContext::create().\n    EGLDisplay prevDisplay = eglGetCurrentDisplay();\n    if (prevDisplay == EGL_NO_DISPLAY) // when no context is current\n        prevDisplay = display;\n    EGLContext prevContext = eglGetCurrentContext();\n    EGLSurface prevSurfaceDraw = eglGetCurrentSurface(EGL_DRAW);\n    EGLSurface prevSurfaceRead = eglGetCurrentSurface(EGL_READ);\n\n    if (eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context)) {\n        if (format.renderableType() == QSurfaceFormat::OpenGL\n            || format.renderableType() == QSurfaceFormat::OpenGLES) {\n            const GLubyte *s = glGetString(GL_VERSION);\n            if (s) {\n                QByteArray version = QByteArray(reinterpret_cast<const char *>(s));\n                int major, minor;\n                if (QPlatformOpenGLContext::parseOpenGLVersion(version, major, minor)) {\n                    format.setMajorVersion(major);\n                    format.setMinorVersion(minor);\n                }\n            }\n            format.setProfile(QSurfaceFormat::NoProfile);\n            format.setOptions(QSurfaceFormat::FormatOptions());\n            if (format.renderableType() == QSurfaceFormat::OpenGL) {\n                // Check profile and options.\n                if (format.majorVersion() < 3) {\n                    format.setOption(QSurfaceFormat::DeprecatedFunctions);\n                } else {\n                    GLint value = 0;\n                    glGetIntegerv(GL_CONTEXT_FLAGS, &value);\n                    if (!(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT))\n                        format.setOption(QSurfaceFormat::DeprecatedFunctions);\n                    if (value & GL_CONTEXT_FLAG_DEBUG_BIT)\n                        format.setOption(QSurfaceFormat::DebugContext);\n                    if (format.version() >= qMakePair(3, 2)) {\n                        value = 0;\n                        glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);\n                        if (value & GL_CONTEXT_CORE_PROFILE_BIT)\n                            format.setProfile(QSurfaceFormat::CoreProfile);\n                        else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)\n                            format.setProfile(QSurfaceFormat::CompatibilityProfile);\n                    }\n                }\n            }\n        }\n        eglMakeCurrent(prevDisplay, prevSurfaceDraw, prevSurfaceRead, prevContext);\n    }\n\n    return format;\n}\n\nQGtkWaylandContext::~QGtkWaylandContext()\n{\n    if (m_eglContext) {\n        eglDestroyContext(m_eglDisplay, m_eglContext);\n    }\n}\n\nbool QGtkWaylandContext::makeCurrent(QPlatformSurface *surface)\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkWaylandContext::makeCurrent\");\n    if (!m_eglContext) {\n        qWarning(\"No context in QGtkOpenGLContext::makeCurrent\");\n        return false;\n    }\n\n    bool ok = eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, m_eglContext);\n    if (!ok) {\n        qWarning() << \"eglMakeCurrent failed\";\n        return ok;\n    }\n\n    return QGtkOpenGLContext::makeCurrent(surface);\n}\n\nvoid QGtkWaylandContext::doneCurrent()\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkWaylandContext::doneCurrent\");\n    if (m_eglContext) {\n        eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);\n    }\n    QGtkOpenGLContext::doneCurrent();\n}\n\nbool QGtkWaylandContext::isValid() const\n{\n    return m_eglContext;\n}\n\nQFunctionPointer QGtkWaylandContext::getProcAddress(const char *procName)\n{\n    eglBindAPI(EGL_OPENGL_API); // ### EGL_OPENGL_ES_API?\n    QFunctionPointer proc = (QFunctionPointer)eglGetProcAddress(procName);\n    if (!proc) {\n        proc = (QFunctionPointer)dlsym(RTLD_DEFAULT, procName);\n    }\n    return proc;\n}\n\nvoid *QGtkWaylandContext::nativeResource(const QByteArray &resource) const\n{\n    if (resource == \"eglcontext\") {\n        return m_eglContext;\n    } else if (resource == \"eglconfig\") {\n        return m_eglConfig;\n    } else if (resource == \"egldisplay\") {\n        return m_eglDisplay;\n    } else {\n        return QGtkOpenGLContext::nativeResource(resource);\n    }\n}\n\nEGLContext QGtkWaylandContext::eglContext() const\n{\n    return m_eglContext;\n}\n\nEGLDisplay QGtkWaylandContext::eglDisplay() const\n{\n    return m_eglDisplay;\n}\n\nEGLConfig QGtkWaylandContext::eglConfig() const\n{\n    return m_eglConfig;\n}\n\n#endif // GDK_WINDOWING_WAYLAND\n"
  },
  {
    "path": "src/platform-plugin/qgtkopenglcontext_x11.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n// This file is not compiled if GDK does not support X11.\n#include \"qgtkopenglcontext.h\"\n#ifdef GDK_WINDOWING_X11\n\n#include <QtCore/qdebug.h>\n#include <QtGui/qopenglcontext.h>\n\n#if QT_VERSION >= QT_VERSION_CHECK(5,8,0)\n#include <QtGlxSupport/private/qglxconvenience_p.h>\n#else\n#include <QtPlatformSupport/private/qglxconvenience_p.h>\n#endif\n\n#include <gdk/gdkx.h>\n#include <GL/glx.h>\n\n#include <QLoggingCategory>\n#include \"CSystrace.h\"\n\nstatic void updateFormatFromContext(QSurfaceFormat &format)\n{\n    // Update the version, profile, and context bit of the format\n    int major = 0, minor = 0;\n    QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));\n    if (QPlatformOpenGLContext::parseOpenGLVersion(versionString, major, minor)) {\n        format.setMajorVersion(major);\n        format.setMinorVersion(minor);\n    }\n\n    format.setProfile(QSurfaceFormat::NoProfile);\n    format.setOptions(QSurfaceFormat::FormatOptions());\n\n    GLint value = 0;\n    glGetIntegerv(GL_CONTEXT_FLAGS, &value);\n    if (!(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT))\n        format.setOption(QSurfaceFormat::DeprecatedFunctions);\n    if (value & GL_CONTEXT_FLAG_DEBUG_BIT)\n        format.setOption(QSurfaceFormat::DebugContext);\n    if (format.version() < qMakePair(3, 2))\n        return;\n\n    // Version 3.2 and newer have a profile\n    value = 0;\n    glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);\n\n    if (value & GL_CONTEXT_CORE_PROFILE_BIT)\n        format.setProfile(QSurfaceFormat::CoreProfile);\n    else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)\n        format.setProfile(QSurfaceFormat::CompatibilityProfile);\n}\n\ntypedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);\n\nvoid QGtkX11Context::initialize()\n{\n    m_glxContext = nullptr;\n    m_display = gdk_x11_get_default_xdisplay();\n\n    if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {\n        qFatal(\"QGtkX11Context should not be used for non-X11 displays\");\n    }\n\n    Display *display = reinterpret_cast<Display*>(m_display);\n    int xScreen = gdk_x11_get_default_screen();\n\n    GLXContext shareCtx = nullptr;\n    if (m_shareContext)\n        shareCtx = reinterpret_cast<GLXContext>(static_cast<QGtkX11Context*>(m_shareContext)->m_glxContext);\n\n    // Requiring OpenGL 3.0+ for surfaceless contexts. It would be possible to add support for other configs.\n    m_format.setRenderableType(QSurfaceFormat::OpenGL);\n    if (m_format.version() < qMakePair(3, 0))\n        m_format.setVersion(3, 0);\n\n    GLXFBConfig glConfig = qglx_findConfig(display, xScreen, m_format);\n\n    glXCreateContextAttribsARBProc glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress((const GLubyte*)\"glXCreateContextAttribsARB\");\n\n    // Try to create an OpenGL context for each known OpenGL version in descending\n    // order from the requested version.\n    const int requestedVersion = m_format.majorVersion() * 10 + qMin(m_format.minorVersion(), 9);\n\n    QVector<int> glVersions;\n    if (requestedVersion > 45)\n        glVersions << requestedVersion;\n\n    // Don't bother with versions below 3.0\n    glVersions << 45 << 44 << 43 << 42 << 41 << 40 << 33 << 32 << 31 << 30;\n\n    for (int i = 0; !m_glxContext && i < glVersions.count(); i++) {\n        const int version = glVersions[i];\n        if (version > requestedVersion)\n            continue;\n\n        const int majorVersion = version / 10;\n        const int minorVersion = version % 10;\n\n        QVector<int> contextAttributes;\n        contextAttributes << GLX_CONTEXT_MAJOR_VERSION_ARB << majorVersion\n                          << GLX_CONTEXT_MINOR_VERSION_ARB << minorVersion;\n\n        // If asking for OpenGL 3.2 or newer we should also specify a profile\n        if (version >= 32) {\n            if (m_format.profile() == QSurfaceFormat::CoreProfile)\n                contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_CORE_PROFILE_BIT_ARB;\n            else\n                contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;\n        }\n\n        int flags = 0;\n\n        if (m_format.testOption(QSurfaceFormat::DebugContext))\n            flags |= GLX_CONTEXT_DEBUG_BIT_ARB;\n\n        // A forward-compatible context may be requested for 3.0 and later\n        if (version >= 30 && !m_format.testOption(QSurfaceFormat::DeprecatedFunctions))\n            flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;\n\n        if (flags != 0)\n            contextAttributes << GLX_CONTEXT_FLAGS_ARB << flags;\n\n        contextAttributes << None;\n\n        m_glxContext = glXCreateContextAttribsARB(display, glConfig, shareCtx, true, contextAttributes.data());\n        if (!m_glxContext && shareCtx) {\n            // re-try without a shared glx context\n            m_glxContext = glXCreateContextAttribsARB(display, glConfig, 0, true, contextAttributes.data());\n            if (m_glxContext)\n                m_shareContext = 0;\n        }\n    }\n\n    if (m_glxContext) {\n        qglx_surfaceFormatFromGLXFBConfig(&m_format, display, glConfig);\n        // Query the OpenGL version and profile\n        GLXContext prevContext = glXGetCurrentContext();\n        GLXDrawable prevDrawable = glXGetCurrentDrawable();\n        glXMakeContextCurrent(display, None, None, reinterpret_cast<GLXContext>(m_glxContext));\n        updateFormatFromContext(m_format);\n        // Make our context non-current\n        glXMakeContextCurrent(display, prevDrawable, prevDrawable, prevContext);\n    }\n}\n\nQGtkX11Context::~QGtkX11Context()\n{\n    if (m_glxContext) {\n        glXDestroyContext(reinterpret_cast<Display*>(m_display), reinterpret_cast<GLXContext>(m_glxContext));\n    }\n}\n\nbool QGtkX11Context::makeCurrent(QPlatformSurface *surface)\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkX11Context::makeCurrent\");\n    if (!m_glxContext) {\n        qWarning(\"No context in QGtkOpenGLContext::makeCurrent\");\n        return false;\n    }\n\n    // Assuming support for surfaceless contexts\n    bool ok = glXMakeContextCurrent(reinterpret_cast<Display*>(m_display), None, None, reinterpret_cast<GLXContext>(m_glxContext));\n    if (!ok)\n        return ok;\n\n    return QGtkOpenGLContext::makeCurrent(surface);\n}\n\nvoid QGtkX11Context::doneCurrent()\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkX11Context::doneCurrent\");\n    glXMakeContextCurrent(reinterpret_cast<Display*>(m_display), None, None, NULL);\n    QGtkOpenGLContext::doneCurrent();\n}\n\nbool QGtkX11Context::isValid() const\n{\n    return m_glxContext;\n}\n\nQFunctionPointer QGtkX11Context::getProcAddress(const char *procName)\n{\n    return (QFunctionPointer)glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName));\n}\n\n#endif // GDK_WINDOWING_X11\n"
  },
  {
    "path": "src/platform-plugin/qgtkscreen.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkscreen.h\"\n#include \"qgtkcursor.h\"\n\n#include <QtCore/qdebug.h>\n#include <QtCore/qloggingcategory.h>\n\n#include <gtk/gtk.h>\n\n#ifdef GDK_WINDOWING_WAYLAND\n#include <gdk/gdkwayland.h>\n#endif\n\nQ_LOGGING_CATEGORY(lcScreen, \"qt.qpa.gtk.screen\");\n\nQGtkScreen::QGtkScreen(GdkMonitor *monitor)\n    : m_monitor(monitor)\n    , m_cursor(new QGtkCursor())\n{\n}\n\nQRect QGtkScreen::availableGeometry() const\n{\n    qreal dpr = 1.0;\n#if defined(GDK_WINDOWING_WAYLAND) && !GTK_CHECK_VERSION(3, 22, 25)\n    GdkDisplay *dpy = gdk_display_get_default();\n    // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=788497\n    if (GDK_IS_WAYLAND_DISPLAY(dpy)) {\n        dpr = devicePixelRatio();\n    }\n#endif\n    GdkRectangle geometry;\n    gdk_monitor_get_workarea(m_monitor, &geometry);\n    return QRect(geometry.x / dpr, geometry.y / dpr, geometry.width / dpr, geometry.height / dpr);\n}\n\nQRect QGtkScreen::geometry() const\n{\n    qreal dpr = 1.0;\n#if defined(GDK_WINDOWING_WAYLAND) && !GTK_CHECK_VERSION(3, 22, 25)\n    GdkDisplay *dpy = gdk_display_get_default();\n    // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=788497\n    if (GDK_IS_WAYLAND_DISPLAY(dpy)) {\n        dpr = devicePixelRatio();\n    }\n#endif\n    GdkRectangle geometry;\n    gdk_monitor_get_geometry(m_monitor, &geometry);\n    return QRect(geometry.x / dpr, geometry.y / dpr, geometry.width / dpr, geometry.height / dpr);\n}\n\nint QGtkScreen::depth() const\n{\n    return 32;\n}\n\nQImage::Format QGtkScreen::format() const\n{\n    return QImage::Format_ARGB32_Premultiplied;\n}\n\nQSizeF QGtkScreen::physicalSize() const\n{\n    return QSizeF(gdk_monitor_get_width_mm(m_monitor), gdk_monitor_get_height_mm(m_monitor));\n}\n\nQDpi QGtkScreen::logicalDpi() const\n{\n    // ### notify on change\n    int dpi = -1;\n    g_object_get(gtk_settings_get_default(), \"gtk-xft-dpi\", &dpi, NULL);\n    if (dpi == -1) {\n        dpi = 96;\n    } else {\n        dpi /= 1024;\n    }\n    return QDpi(dpi, dpi);\n}\n\nqreal QGtkScreen::devicePixelRatio() const\n{\n    return gdk_monitor_get_scale_factor(m_monitor);\n}\n\nqreal QGtkScreen::refreshRate() const\n{\n    // gdk gives us millihz..\n    return gdk_monitor_get_refresh_rate(m_monitor) / 1000;\n}\n\nQPlatformCursor *QGtkScreen::cursor() const\n{\n    return m_cursor.get();\n}\n"
  },
  {
    "path": "src/platform-plugin/qgtkscreen.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKSCREEN_H\n#define QGTKSCREEN_H\n\n#include <qpa/qplatformscreen.h>\n\n#include <gdk/gdk.h>\n\n#include <memory>\n\nQT_BEGIN_NAMESPACE\n\nclass QGtkCursor;\n\nclass QGtkScreen : public QPlatformScreen\n{\npublic:\n    QGtkScreen(GdkMonitor *monitor);\n\n    QRect availableGeometry() const override;\n    QRect geometry() const override;\n    int depth() const override;\n    QImage::Format format() const override;\n    QSizeF physicalSize() const override;\n    QDpi logicalDpi() const override;\n    qreal devicePixelRatio() const override;\n    qreal refreshRate() const override;\n    QPlatformCursor *cursor() const override;\n\n    GdkMonitor *monitor() const { return m_monitor; }\n    bool isPrimary() const { return m_isPrimary; }\n    void setPrimary(bool p) { m_isPrimary = p; }\n\npublic:\n    GdkMonitor *m_monitor;\n    std::unique_ptr<QGtkCursor> m_cursor;\n    bool m_isPrimary = false;\n};\n\n\nQT_END_NAMESPACE\n\n#endif // QGTKSCREEN_H\n"
  },
  {
    "path": "src/platform-plugin/qgtkservices.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkservices.h\"\n\n#include <QtCore/qdebug.h>\n#include <QtCore/qbytearray.h>\n#include <QtCore/qurl.h>\n\n#include <gio/gio.h>\n\nQGtkServices::QGtkServices()\n{\n\n}\n\nQGtkServices::~QGtkServices()\n{\n\n}\n\nbool QGtkServices::openUrl(const QUrl &url)\n{\n    GError *err = nullptr;\n    g_app_info_launch_default_for_uri(url.toString(QUrl::FullyEncoded).toUtf8().constData(), NULL, &err);\n    if (err) {\n        qWarning() << \"Open failed: \" << err->message;\n        g_error_free(err);\n        return false;\n    }\n    return true;\n}\n\nbool QGtkServices::openDocument(const QUrl &url)\n{\n    GError *err = nullptr;\n    g_app_info_launch_default_for_uri(url.toString(QUrl::FullyEncoded).toUtf8().constData(), NULL, &err);\n    if (err) {\n        qWarning() << \"Open failed: \" << err->message;\n        g_error_free(err);\n        return false;\n    }\n    return true;\n}\n\nQByteArray QGtkServices::desktopEnvironment() const\n{\n    return qgetenv(\"XDG_CURRENT_DESKTOP\");\n}\n"
  },
  {
    "path": "src/platform-plugin/qgtkservices.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKSERVICES_H\n#define QGTKSERVICES_H\n\n#include <QtCore/qglobal.h>\n#include <qpa/qplatformservices.h>\n\nQT_BEGIN_NAMESPACE\n\nclass QUrl;\n\nclass QGtkServices : public QPlatformServices\n{\npublic:\n    QGtkServices();\n    virtual ~QGtkServices();\n\n    bool openUrl(const QUrl &url) override;\n    bool openDocument(const QUrl &url) override;\n\n    QByteArray desktopEnvironment() const override;\n};\n\nQT_END_NAMESPACE\n\n#endif // QGTKSERVICES_H\n\n"
  },
  {
    "path": "src/platform-plugin/qgtksystemtrayicon.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkhelpers.h\"\n#include \"qgtksystemtrayicon.h\"\n#include \"qgtkintegration.h\"\n\n#include <QtCore/qdebug.h>\n#include <QtGui/qicon.h>\n#include <QtGui/private/qguiapplication_p.h>\n\nQGtkSystemTrayIcon::QGtkSystemTrayIcon()\n{\n}\n\nQGtkSystemTrayIcon::~QGtkSystemTrayIcon()\n{\n}\n\nvoid QGtkSystemTrayIcon::init()\n{\n}\n\nvoid QGtkSystemTrayIcon::cleanup()\n{\n}\n\nvoid QGtkSystemTrayIcon::updateIcon(const QIcon &)\n{\n}\n\nvoid QGtkSystemTrayIcon::updateToolTip(const QString &)\n{\n}\n\nvoid QGtkSystemTrayIcon::updateMenu(QPlatformMenu *)\n{\n}\n\nQRect QGtkSystemTrayIcon::geometry() const\n{\n    return QRect();\n}\n\nvoid action_cb(NotifyNotification*, gchar *, gpointer gtkSystemTrayIcon)\n{\n    QGtkSystemTrayIcon *ico = (QGtkSystemTrayIcon*)gtkSystemTrayIcon;\n    Q_EMIT ico->messageClicked();\n}\n\nvoid QGtkSystemTrayIcon::showMessage(const QString &title, const QString &msg,\n                 const QIcon& icon, MessageIcon iconType, int msecs)\n{\n    NotifyNotification *n = notify_notification_new(title.toUtf8().constData(), msg.toUtf8().constData(), nullptr);\n\n    // ### technically, we could delete this after 'msecs'. we need to keep it\n    // around to fire action_cb.\n    m_notification.reset(n);\n\n    if (!icon.isNull()) {\n        QGtkRefPtr<GdkPixbuf> ico = qt_iconToPixbuf(icon);\n        notify_notification_set_icon_from_pixbuf(n, ico.get());\n    }\n\n    switch (iconType) {\n    case QPlatformSystemTrayIcon::NoIcon:\n    case QPlatformSystemTrayIcon::Information:\n        notify_notification_set_urgency(n, NOTIFY_URGENCY_LOW);\n        break;\n    case QPlatformSystemTrayIcon::Warning:\n        notify_notification_set_urgency(n, NOTIFY_URGENCY_NORMAL);\n        break;\n    case QPlatformSystemTrayIcon::Critical:\n        notify_notification_set_urgency(n, NOTIFY_URGENCY_CRITICAL);\n        break;\n    }\n\n    notify_notification_set_timeout(n, msecs);\n    notify_notification_add_action(n, \"default\", \"default\", action_cb, this, NULL);\n\n    notify_notification_show(n, NULL);\n}\n\nbool QGtkSystemTrayIcon::isSystemTrayAvailable() const\n{\n    return true;\n}\n\nbool QGtkSystemTrayIcon::supportsMessages() const\n{\n    return true;\n}\n\n"
  },
  {
    "path": "src/platform-plugin/qgtksystemtrayicon.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n #ifndef QGTKSYSTEMTRAYICON_P_H\n #define QGTKSYSTEMTRAYICON_P_H\n\n #include <QtCore/qglobal.h>\n\n #include \"QtCore/qstring.h\"\n #include \"QtGui/qpa/qplatformsystemtrayicon.h\"\n\n#include <gtk/gtk.h>\n\n#include <libnotify/notification.h>\n\n QT_BEGIN_NAMESPACE\n\n class Q_GUI_EXPORT QGtkSystemTrayIcon : public QPlatformSystemTrayIcon\n {\n public:\n     QGtkSystemTrayIcon();\n     ~QGtkSystemTrayIcon();\n\n     void init() override;\n     void cleanup() override;\n     void updateIcon(const QIcon &icon) override;\n     void updateToolTip(const QString &toolTip) override;\n     void updateMenu(QPlatformMenu *menu) override;\n     QRect geometry() const override;\n     void showMessage(const QString &title, const QString &msg,\n                      const QIcon& icon, MessageIcon iconType, int msecs) override;\n\n     bool isSystemTrayAvailable() const override;\n     bool supportsMessages() const override;\n\n private:\n     QGtkRefPtr<GtkStatusIcon> m_icon;\n     QGtkRefPtr<NotifyNotification> m_notification;\n };\n\n QT_END_NAMESPACE\n\n #endif // QGTKSYSTEMTRAYICON_P_H\n"
  },
  {
    "path": "src/platform-plugin/qgtktheme.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtk3dialoghelpers.h\"\n#include \"qgtkmenubar.h\"\n#include \"qgtkmenu.h\"\n#include \"qgtkmenuitem.h\"\n#include \"qgtktheme.h\"\n#include \"qgtksystemtrayicon.h\"\n\n#if QT_VERSION >= QT_VERSION_CHECK(5,8,0)\n# include <QtThemeSupport/private/qgenericunixthemes_p.h>\n#else\n #include <QtPlatformSupport/private/qgenericunixthemes_p.h>\n#endif\n\n#include <QtCore/qmimetype.h>\n#include <QtCore/qmimedatabase.h>\n#include <QtCore/qdebug.h>\n#include <QtCore/qvariant.h>\n#include <QtGui/qpixmap.h>\n\nconst char *QGtkTheme::name = \"gtkqpainternal\";\n\nQGtkTheme::QGtkTheme()\n{\n\n}\n\nQGtkTheme::~QGtkTheme()\n{\n\n}\n\nQPlatformMenuItem* QGtkTheme::createPlatformMenuItem() const\n{\n    return new QGtkMenuItem;\n}\n\nQPlatformMenu* QGtkTheme::createPlatformMenu() const\n{\n    return new QGtkMenu;\n}\n\nQPlatformMenuBar* QGtkTheme::createPlatformMenuBar() const\n{\n    return new QGtkMenuBar;\n}\n\n#ifndef QT_NO_SYSTEMTRAYICON\nQPlatformSystemTrayIcon *QGtkTheme::createPlatformSystemTrayIcon() const\n{\n    return new QGtkSystemTrayIcon;\n}\n#endif\n\nbool QGtkTheme::usePlatformNativeDialog(DialogType dialogType) const\n{\n    switch (dialogType) {\n    case QPlatformTheme::FileDialog:\n    case QPlatformTheme::FontDialog:\n    case QPlatformTheme::ColorDialog:\n        return true;\n    case QPlatformTheme::MessageDialog:\n        break;\n    }\n    return false;\n}\n\nQPlatformDialogHelper *QGtkTheme::createPlatformDialogHelper(DialogType dialogType) const\n{\n    switch (dialogType) {\n    case QPlatformTheme::FileDialog:\n        return new QGtk3FileDialogHelper;\n    case QPlatformTheme::FontDialog:\n        return new QGtk3FontDialogHelper;\n    case QPlatformTheme::ColorDialog:\n        return new QGtk3ColorDialogHelper;\n    case QPlatformTheme::MessageDialog:\n        break;\n    }\n    return 0;\n}\n\nconst QPalette *QGtkTheme::palette(Palette type) const\n{\n    return QPlatformTheme::palette(type);\n}\n\nconst QFont *QGtkTheme::font(Font type) const\n{\n    if (!m_fontConfigured) {\n        m_fontConfigured = true;\n\n        GtkSettings *s = gtk_settings_get_default();\n        gchararray value;\n        g_object_get(s, \"gtk-font-name\", &value, NULL);\n        QString qtVal = QString::fromUtf8(value);\n        g_free(value);\n\n        if (qtVal.isNull()) {\n            m_systemFont = QFont(\"Sans Serif\", 11);\n            m_monoFont = QFont(\"Monospace\", m_systemFont.pointSize());\n        } else {\n            int lastSpace = qtVal.lastIndexOf(' ');\n            int pointSize = qtVal.midRef(lastSpace+1).toInt();\n            m_systemFont = QFont(qtVal.left(lastSpace), pointSize);\n            // ### dconf also has monospace fonts, document fonts... how can we get these?\n            // this is a bit of a hack...\n            m_monoFont = QFont(\"Monospace\", m_systemFont.pointSize());\n        }\n    }\n\n    if (type == QPlatformTheme::FixedFont) {\n        return &m_monoFont;\n    }\n    return &m_systemFont;\n}\n\nQPixmap QGtkTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const\n{\n    return QPlatformTheme::standardPixmap(sp, size);\n}\n\nstatic QIcon fileIconForFile(const QFileInfo &fi)\n{\n    QMimeDatabase md;\n    QMimeType mt = md.mimeTypeForFile(fi);\n    if (!mt.isValid()) {\n        return QIcon();\n    }\n\n    const QString &iconName = mt.iconName();\n    if (!iconName.isEmpty()) {\n        QIcon ico = QIcon::fromTheme(iconName);\n        if (!ico.isNull()) {\n            return ico;\n        }\n    }\n\n    const QString &genericIconName = mt.genericIconName();\n    return QIcon::fromTheme(genericIconName);\n}\n\n#if QT_VERSION >= QT_VERSION_CHECK(5,8,0)\nQIcon QGtkTheme::fileIcon(const QFileInfo &fileInfo,\n        QPlatformTheme::IconOptions options) const\n{\n    return fileIconForFile(fileInfo);\n}\n#else\nQPixmap QGtkTheme::fileIconPixmap(const QFileInfo &fileInfo,\n        const QSizeF &size,\n        QPlatformTheme::IconOptions options) const\n{\n    Q_UNUSED(options);\n\n    QIcon ico = fileIconForFile(fileInfo);\n    return ico.pixmap(size.toSize());\n}\n#endif\n\nQVariant QGtkTheme::themeHint(ThemeHint hint) const\n{\n    switch (hint) {\n    case QPlatformTheme::SystemIconThemeName:\n        return QVariant(\"Adwaita\");\n    case QPlatformTheme::SystemIconFallbackThemeName:\n        return QVariant(\"gnome\");\n    case QPlatformTheme::StyleNames:\n        return QStringList() << \"Adwaita\" << \"Fusion\";\n    case QPlatformTheme::PasswordMaskCharacter:\n        return QVariant(QChar(0x2022));\n    case QPlatformTheme::IconThemeSearchPaths:\n        return QVariant(QGenericUnixTheme::xdgIconThemePaths());\n    case QPlatformTheme::IconPixmapSizes:\n        return QVariant::fromValue(QIcon::fromTheme(QStringLiteral(\"inode-directory\")).availableSizes());\n    default:\n        break;\n    }\n\n    return QPlatformTheme::themeHint(hint);\n}\n\nQString QGtkTheme::standardButtonText(int button) const\n{\n    return QPlatformTheme::standardButtonText(button);\n}\n\n"
  },
  {
    "path": "src/platform-plugin/qgtktheme.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKTHEME_H\n#define QGTKTHEME_H\n\n#include <QtGui/qfont.h>\n#include <qpa/qplatformtheme.h>\n\nclass QGtkTheme : public QPlatformTheme\n{\npublic:\n    QGtkTheme();\n    ~QGtkTheme();\n\n    QPlatformMenuItem* createPlatformMenuItem() const override;\n    QPlatformMenu* createPlatformMenu() const override;\n    QPlatformMenuBar* createPlatformMenuBar() const override;\n\n#ifndef QT_NO_SYSTEMTRAYICON\n    QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;\n#endif\n\n    bool usePlatformNativeDialog(DialogType dialogType) const override;\n    QPlatformDialogHelper *createPlatformDialogHelper(DialogType dialogType) const override;\n\n    const QPalette *palette(Palette type = SystemPalette) const override;\n    const QFont *font(Font type = SystemFont) const override;\n    QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override;\n#if QT_VERSION >= QT_VERSION_CHECK(5,8,0)\n    QIcon fileIcon(const QFileInfo &fileInfo,\n            QPlatformTheme::IconOptions options = 0) const override;\n#else\n    QPixmap fileIconPixmap(const QFileInfo &fileInfo,\n            const QSizeF &size,\n            QPlatformTheme::IconOptions options = 0) const override;\n#endif\n\n    QVariant themeHint(ThemeHint hint) const override;\n    QString standardButtonText(int button) const override;\n\n    static const char *name;\n    mutable QFont m_systemFont;\n    mutable QFont m_monoFont;\n    mutable bool m_fontConfigured = false;\n};\n\n#endif // QGTKTHEME_H\n"
  },
  {
    "path": "src/platform-plugin/qgtkwindow.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkwindow.h\"\n#include \"qgtkhelpers.h\"\n\n#include <QtGui/qguiapplication.h>\n#include <qpa/qwindowsysteminterface.h>\n#include <QtGui/private/qwindow_p.h>\n#include <QtCore/qcoreapplication.h>\n#include <QtCore/qtimer.h>\n#include <QtCore/qdebug.h>\n#include <QtCore/qloggingcategory.h>\n\nQ_LOGGING_CATEGORY(lcWindow, \"qt.qpa.gtk.window\");\nQ_LOGGING_CATEGORY(lcWindowEvents, \"qt.qpa.gtk.window\");\n\nstatic gboolean map_cb(GtkWidget *, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"map_cb\" << pw;\n    pw->onMap();\n    return FALSE;\n}\n\nstatic gboolean unmap_cb(GtkWidget *, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"unmap_cb\" << pw;\n    pw->onUnmap();\n    return FALSE;\n}\n\nstatic gboolean configure_cb(GtkWidget *, GdkEvent *, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"configure_cb\" << pw;\n    pw->onConfigure();\n    return FALSE;\n}\n\nstatic gboolean size_allocate_cb(GtkWidget *, GdkRectangle *, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"size_allocate_cb\" << pw;\n    pw->onConfigure();\n    return FALSE;\n}\n\nstatic gboolean delete_cb(GtkWidget *, GdkEvent *, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"delete_cb\" << pw;\n    return pw->onDelete() ? TRUE : FALSE;\n}\n\nstatic gboolean key_press_cb(GtkWidget *, GdkEvent *event, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"key_press_cb\" << pw;\n    return pw->onKeyPress(event) ? TRUE : FALSE;\n}\n\nstatic gboolean key_release_cb(GtkWidget *, GdkEvent *event, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"key_release_cb\" << pw;\n    return pw->onKeyRelease(event) ? TRUE : FALSE;\n}\n\nstatic gboolean button_press_cb(GtkWidget *, GdkEvent *event, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"button_press_cb\" << pw;\n    return pw->onButtonPress(event) ? TRUE : FALSE;\n}\n\nstatic gboolean button_release_cb(GtkWidget *, GdkEvent *event, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"button_release_cb\" << pw;\n    return pw->onButtonRelease(event) ? TRUE : FALSE;\n}\n\nstatic gboolean touch_event_cb(GtkWidget *, GdkEvent *event, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"touch_event_cb\" << pw;\n    return pw->onTouchEvent(event) ? TRUE : FALSE;\n}\n\nstatic gboolean motion_notify_cb(GtkWidget *, GdkEvent *event, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"motion_notify_cb\" << pw;\n    return pw->onMotionNotify(event) ? TRUE : FALSE;\n}\n\nstatic gboolean scroll_cb(GtkWidget *, GdkEvent *event, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"scroll_cb\" << pw;\n    return pw->onScrollEvent(event) ? TRUE : FALSE;\n}\n\nstatic gboolean window_state_event_cb(GtkWidget *, GdkEvent *event, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"window_state_event_cb\" << pw;\n    pw->onWindowStateEvent(event);\n    return FALSE;\n}\n\nstatic gboolean enter_leave_window_notify_cb(GtkWidget *, GdkEvent *event, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    bool entering = event->type == GDK_ENTER_NOTIFY;\n    qCDebug(lcWindowEvents) << \"enter_leave_window_notify_cb\" << pw << entering;\n    pw->onEnterLeaveWindow(event, entering);\n    return false;\n}\n\nstatic gboolean leave_content_notify_cb(GtkWidget *, GdkEvent *, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowEvents) << \"leave_content_notify_cb\" << pw;\n    pw->onLeaveContent();\n    return false;\n}\n\nQGtkWindow::QGtkWindow(QWindow *window)\n    : QPlatformWindow(window)\n    , m_buttons(Qt::NoButton)\n    , m_windowGeometry(0, 0, 1, 1)\n{\n    create(window->type());\n\n    if (!QGtkCourierObject::instance)\n        QGtkCourierObject::instance = new QGtkCourierObject(QCoreApplication::instance());\n}\n\nvoid QGtkWindow::create(Qt::WindowType windowType)\n{\n    if (m_window) {\n        gtk_widget_destroy(m_window.get());\n    }\n\n    // Determine the window type. GTK_WINDOW_TOPLEVEL is usually right.\n    GtkWindowType gtkWindowType = GTK_WINDOW_TOPLEVEL;\n    if (windowType == Qt::ToolTip ||\n        windowType == Qt::Popup) {\n        gtkWindowType = GTK_WINDOW_POPUP;\n    }\n\n    // Create the window.\n    m_window = gtk_window_new(gtkWindowType);\n\n    // First things first, set a proper type hint on the window.\n    switch (windowType) {\n    case Qt::Window:\n        gtk_window_set_type_hint(GTK_WINDOW(m_window.get()), GDK_WINDOW_TYPE_HINT_NORMAL);\n        break;\n    case Qt::Dialog:\n    case Qt::Sheet:\n        gtk_window_set_type_hint(GTK_WINDOW(m_window.get()), GDK_WINDOW_TYPE_HINT_DIALOG);\n        break;\n    case Qt::Popup:\n        gtk_window_set_type_hint(GTK_WINDOW(m_window.get()), GDK_WINDOW_TYPE_HINT_MENU);\n        break;\n    case Qt::Tool:\n        gtk_window_set_type_hint(GTK_WINDOW(m_window.get()), GDK_WINDOW_TYPE_HINT_TOOLBAR);\n        break;\n    case Qt::SplashScreen:\n        gtk_window_set_type_hint(GTK_WINDOW(m_window.get()), GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);\n        break;\n    case Qt::ToolTip:\n        gtk_window_set_type_hint(GTK_WINDOW(m_window.get()), GDK_WINDOW_TYPE_HINT_TOOLTIP);\n        break;\n    default:\n        break;\n    }\n\n    // Now set a transient parent (for things that ought to have one). This is\n    // required otherwise things like positioning windows will not work, as\n    // Wayland doesn't have any concept of a global window position.\n    maybeForceTransientParent(windowType);\n\n    g_signal_connect(m_window.get(), \"map\", G_CALLBACK(map_cb), this);\n    g_signal_connect(m_window.get(), \"unmap\", G_CALLBACK(unmap_cb), this);\n    g_signal_connect(m_window.get(), \"configure-event\", G_CALLBACK(configure_cb), this);\n    g_signal_connect(m_window.get(), \"enter-notify-event\", G_CALLBACK(enter_leave_window_notify_cb), this);\n    g_signal_connect(m_window.get(), \"leave-notify-event\", G_CALLBACK(enter_leave_window_notify_cb), this);\n\n    // for whatever reason, configure-event is not enough. it doesn't seem to\n    // get emitted for popup type windows. so also connect to size-allocate just\n    // to be sure...\n    g_signal_connect(m_window.get(), \"size-allocate\", G_CALLBACK(size_allocate_cb), this);\n    g_signal_connect(m_window.get(), \"delete-event\", G_CALLBACK(delete_cb), this);\n    g_signal_connect(m_window.get(), \"window-state-event\", G_CALLBACK(window_state_event_cb), this);\n\n    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);\n    gtk_container_add(GTK_CONTAINER(m_window.get()), vbox);\n\n    m_menubar = GTK_MENU_BAR(gtk_menu_bar_new());\n    gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(m_menubar.get()), FALSE, FALSE, 0);\n\n    m_content = gtk_drawing_area_new();\n    g_signal_connect(m_content.get(), \"draw\", G_CALLBACK(QGtkWindow::drawCallback), this);\n\n    gtk_box_pack_end(GTK_BOX(vbox), m_content.get(), TRUE, TRUE, 0);\n\n    // ### Proximity? Touchpad gesture? Tablet?\n    gtk_widget_set_events(m_content.get(),\n        GDK_POINTER_MOTION_MASK |\n        GDK_BUTTON_PRESS_MASK |\n        GDK_BUTTON_RELEASE_MASK |\n        GDK_SCROLL_MASK |\n        GDK_SMOOTH_SCROLL_MASK |\n        GDK_TOUCH_MASK |\n        GDK_LEAVE_NOTIFY_MASK\n    );\n\n    // Register event handlers that need coordinates on the content widget, not\n    // the window.\n    g_signal_connect(m_content.get(), \"button-press-event\", G_CALLBACK(button_press_cb), this);\n    g_signal_connect(m_content.get(), \"button-release-event\", G_CALLBACK(button_release_cb), this);\n    g_signal_connect(m_content.get(), \"touch-event\", G_CALLBACK(touch_event_cb), this);\n    g_signal_connect(m_content.get(), \"motion-notify-event\", G_CALLBACK(motion_notify_cb), this);\n    g_signal_connect(m_content.get(), \"key-press-event\", G_CALLBACK(key_press_cb), this);\n    g_signal_connect(m_content.get(), \"key-release-event\", G_CALLBACK(key_release_cb), this);\n    g_signal_connect(m_content.get(), \"scroll-event\", G_CALLBACK(scroll_cb), this);\n    g_signal_connect(m_content.get(), \"leave-notify-event\", G_CALLBACK(leave_content_notify_cb), this);\n    gtk_widget_set_can_focus(m_content.get(), true);\n\n    m_zoomGesture = gtk_gesture_zoom_new(m_content.get());\n    gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(m_zoomGesture.get()), GTK_PHASE_CAPTURE);\n    g_signal_connect(m_zoomGesture.get(), \"scale-changed\", G_CALLBACK(QGtkWindow::zoom_cb), this);\n    g_signal_connect(m_zoomGesture.get(), \"begin\", G_CALLBACK(QGtkWindow::begin_zoom_cb), this);\n    g_signal_connect(m_zoomGesture.get(), \"cancel\", G_CALLBACK(QGtkWindow::cancel_zoom_cb), this);\n    g_signal_connect(m_zoomGesture.get(), \"end\", G_CALLBACK(QGtkWindow::end_zoom_cb), this);\n\n    m_rotateGesture = gtk_gesture_rotate_new(m_content.get());\n    gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(m_rotateGesture.get()), GTK_PHASE_CAPTURE);\n    g_signal_connect(m_rotateGesture.get(), \"angle-changed\", G_CALLBACK(QGtkWindow::rotate_cb), this);\n    g_signal_connect(m_rotateGesture.get(), \"begin\", G_CALLBACK(QGtkWindow::begin_rotate_cb), this);\n    g_signal_connect(m_rotateGesture.get(), \"cancel\", G_CALLBACK(QGtkWindow::cancel_rotate_cb), this);\n    g_signal_connect(m_rotateGesture.get(), \"end\", G_CALLBACK(QGtkWindow::end_rotate_cb), this);\n\n    gtk_gesture_group(m_zoomGesture.get(), m_rotateGesture.get());\n\n    m_touchDevice = new QTouchDevice;\n    m_touchDevice->setType(QTouchDevice::TouchScreen); // ### use GdkDevice or not?\n    m_touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::MouseEmulation);\n    QWindowSystemInterface::registerTouchDevice(m_touchDevice);\n\n    setWindowState(window()->windowState());\n    propagateSizeHints();\n    setWindowFlags(window()->flags());\n    setGeometry(window()->geometry());\n    gtk_window_set_modal(GTK_WINDOW(m_window.get()), window()->modality() != Qt::NonModal);\n    if (!window()->title().isEmpty())\n        setWindowTitle(window()->title());\n\n    if (!qFuzzyCompare(QWindowPrivate::get(window())->opacity, qreal(1.0))) {\n        setOpacity(QWindowPrivate::get(window())->opacity);\n    }\n    if (window()->isTopLevel()) {\n        setWindowIcon(window()->icon());\n    }\n}\n\nQGtkWindow::~QGtkWindow()\n{\n    gtk_widget_remove_tick_callback(m_window.get(), m_tick_callback);\n    QWindowSystemInterface::unregisterTouchDevice(m_touchDevice);\n    gtk_widget_destroy(m_window.get());\n}\n\nvoid QGtkWindow::maybeForceTransientParent(Qt::WindowType windowType)\n{\n    bool shouldTransient = window()->modality() != Qt::NonModal;\n\n    switch (windowType) {\n    case Qt::Dialog:\n    case Qt::Sheet:\n    case Qt::Tool:\n    case Qt::SplashScreen:\n    case Qt::ToolTip:\n    case Qt::Drawer:\n    case Qt::Popup:\n        shouldTransient = true;\n        break;\n    default:\n        break;\n    }\n\n    if (!shouldTransient) {\n        return;\n    }\n\n    // Hope they specified one first.\n    QWindow *transientParent = window()->transientParent();\n    if (transientParent) {\n        reallyForceTransientFor(transientParent);\n        return;\n    }\n\n    // Try fall back to focus. We must have a top level window, though.\n    if (qApp->focusWindow() && qApp->focusWindow()->type() == Qt::Window) {\n        qWarning() << \"Forcing transient parent to focus window \" << qApp->focusWindow() << \" for window \" << window() << \" -- this is bad, it ought to have a transientParent set, the window may end up incorrectly positioned\";\n        reallyForceTransientFor(qApp->focusWindow());\n        return;\n    }\n\n    // Last ditch effort: try find a top level window.\n    QWindowList wl = qApp->topLevelWindows();\n    for (QWindow *win : wl) {\n        if (win->type() == Qt::Window) {\n            qWarning() << \"Forcing transient parent to first available toplevel \" << win << \" for window \" << window() << \" -- this is bad, it ought to have a transientParent set, the window may end up incorrectly positioned.\";\n            reallyForceTransientFor(win);\n            return;\n        }\n    }\n\n    qWarning() << \"Showing \" << window() << \" as a transient window without a transient parent, positioning will almost certainly be incorrect (if it works at all!)\";\n}\n\nvoid QGtkWindow::reallyForceTransientFor(QWindow *transientParent)\n{\n    transientParent->create(); // force pwin creation\n    QGtkWindow *transientParentPlatform = static_cast<QGtkWindow*>(transientParent->handle());\n    gtk_window_set_transient_for(GTK_WINDOW(m_window.get()), GTK_WINDOW(transientParentPlatform->gtkWindow().get()));\n}\n\nvoid QGtkWindow::onMap()\n{\n    QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));\n}\n\nvoid QGtkWindow::onUnmap()\n{\n    QWindowSystemInterface::handleExposeEvent(window(), QRegion());\n}\n\nvoid QGtkWindow::onConfigure()\n{\n    // windowX and windowY are the window system coordinates of the top-left of the client\n    // portion of the window. They don't include server-side decorations but do include client-side.\n    int windowX = 0, windowY = 0;\n    GdkWindow *gwindow = gtk_widget_get_window(m_window.get());\n    if (gwindow)\n        gdk_window_get_position(gwindow, &windowX, &windowY);\n\n    // contentRect is the drawn content area of the window in window coordinates. This excludes\n    // client-side decorations and other frame elements (e.g. menubar).\n    GdkRectangle contentRect;\n    gtk_widget_get_allocated_size(m_content.get(), &contentRect, nullptr);\n\n    // QWindow geometry is contentRect translated to window system coordinates with windowX/windowY\n    m_newGeometry = QRect(windowX + contentRect.x, windowY + contentRect.y,\n                          contentRect.width, contentRect.height);\n}\n\nbool QGtkWindow::onDelete()\n{\n#if QT_VERSION >= QT_VERSION_CHECK(5,14,0)\n    return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());\n#else\n    bool accepted = false;\n    QWindowSystemInterface::handleCloseEvent(window(), &accepted);\n    QWindowSystemInterface::flushWindowSystemEvents();\n    return accepted;\n#endif\n}\n\nQSurfaceFormat QGtkWindow::format() const\n{\n    return window()->requestedFormat();\n}\n\nvoid QGtkWindow::setGeometry(const QRect &crect)\n{\n    QRect rect(crect);\n    Qt::WindowType type = static_cast<Qt::WindowType>(int(m_flags & Qt::WindowType_Mask));\n    if (type != Qt::Window) {\n        // Ensure that child windows are positioned somewhere that makes sense.\n        // If we don't do this, then popup-type windows will end up off-screen,\n        // which isn't very useful.\n        //\n        // It'd be quite nice if in Qt 6, we could consider doing away with\n        // absolute positioning of menus and such, and rather, tie them to a\n        // pointer device or a parent widget + offset inside that widget, or\n        // something.\n        const QSize screenSize = window()->screen()->availableGeometry().size();\n        int deltaX = rect.x() - rect.width();\n        int deltaY = rect.y() - rect.height();\n        if (rect.y() + rect.height() > screenSize.height()) {\n            rect.moveTop(deltaY);\n        }\n        if (rect.x() + rect.width() > screenSize.width()) {\n            rect.moveLeft(deltaX);\n        }\n    }\n\n    if (!window()->isVisible()) {\n        // if we aren't visible, we won't get a configure event, so cache the\n        // geometry for the time being.\n        m_windowGeometry = QRect(QPoint(0, 0), rect.size());\n    }\n    gtk_window_move(GTK_WINDOW(m_window.get()), rect.x(), rect.y());\n    gtk_window_resize(GTK_WINDOW(m_window.get()), qMax(rect.width(), 1), qMax(rect.height(), 1));\n}\n\nQRect QGtkWindow::geometry() const\n{\n    return m_windowGeometry;\n}\n\nQRect QGtkWindow::normalGeometry() const\n{\n    return geometry();\n}\n\nqreal QGtkWindow::devicePixelRatio() const\n{\n    // ### may change on configure event\n    return gtk_widget_get_scale_factor(m_window.get());\n}\n\nQMargins QGtkWindow::frameMargins() const\n{\n    GdkWindow *gwindow = gtk_widget_get_window(m_window.get());\n    if (!gwindow) {\n        return QMargins();\n    }\n\n    // Bounding rectangle of the entire window, including server-side frame\n    // x and y are in root window coordinates\n    GdkRectangle frameRect;\n    gdk_window_get_frame_extents(gwindow, &frameRect);\n\n    // Position of the top-left of the window area, excluding server-side frames,\n    // also in root window coordinates\n    int originX, originY;\n    gdk_window_get_origin(gwindow, &originX, &originY);\n\n    // Rectangle in window coordinates of the content area, excluding client-side frames\n    GdkRectangle contentRect;\n    gtk_widget_get_allocated_size(m_content.get(), &contentRect, nullptr);\n\n    // Size of the margin for top and left. This is the server-side margin (difference\n    // between the frame and origin's X) plus the client-side margin (contentRect.x)\n    int leftMargin = originX - frameRect.x + contentRect.x;\n    int topMargin = originY - frameRect.y + contentRect.y;\n\n    // Bottom and right margins are the remainder of frameRect's size after removing the\n    // top/left margins and contentRect size.\n    return QMargins(leftMargin, topMargin,\n                    frameRect.width - leftMargin - contentRect.width,\n                    frameRect.height - topMargin - contentRect.height);\n}\n\nvoid QGtkWindow::setVisible(bool visible)\n{\n    if (visible) {\n        gtk_widget_show_all(m_window.get());\n        gtk_widget_grab_focus(m_content.get());\n    } else {\n        gtk_widget_hide(m_window.get());\n    }\n}\n\nvoid QGtkWindow::setWindowFlags(Qt::WindowFlags flags)\n{\n    if (flags == m_flags) {\n        // probably means we're being called from create(), do our best to make\n        // it harmless.\n        return;\n    }\n\n    Qt::WindowType oldType = static_cast<Qt::WindowType>(int(m_flags & Qt::WindowType_Mask));\n    Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));\n    m_flags = flags;\n\n    if (type == Qt::Popup) {\n        flags |= Qt::FramelessWindowHint;\n    }\n\n    if (type != oldType) {\n        if (type == Qt::Popup) {\n            // ### do we really support changing to other types of windows at\n            // runtime? this will probably break all sorts of stuff.\n            //create(Qt::Popup);\n        }\n    }\n\n    // ### recreate the window if the type changes, but be careful, we may\n    // recurse.\n    gtk_window_set_decorated(GTK_WINDOW(m_window.get()), !(flags & Qt::FramelessWindowHint));\n\n    if ((flags & Qt::CustomizeWindowHint)) {\n        gtk_window_set_deletable(GTK_WINDOW(m_window.get()), (flags & Qt::WindowCloseButtonHint));\n    }\n}\n\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\nvoid QGtkWindow::setWindowState(Qt::WindowStates requestedState)\n{\n    const Qt::WindowState state = QWindowPrivate::effectiveState(requestedState);\n#else\nvoid QGtkWindow::setWindowState(Qt::WindowState requestedState)\n{\n    const Qt::WindowState state = requestedState;\n#endif\n\n    if (state == m_state) {\n        return;\n    }\n\n    switch (m_state) {\n    case Qt::WindowMinimized:\n        gtk_window_deiconify(GTK_WINDOW(m_window.get()));\n        break;\n    case Qt::WindowMaximized:\n        gtk_window_unmaximize(GTK_WINDOW(m_window.get()));\n        break;\n    case Qt::WindowFullScreen:\n        gtk_window_unfullscreen(GTK_WINDOW(m_window.get()));\n        break;\n    case Qt::WindowNoState:\n    case Qt::WindowActive:\n        break;\n    }\n\n    switch (state) {\n    case Qt::WindowMinimized:\n        gtk_window_iconify(GTK_WINDOW(m_window.get()));\n        break;\n    case Qt::WindowMaximized:\n        gtk_window_maximize(GTK_WINDOW(m_window.get()));\n        break;\n    case Qt::WindowFullScreen:\n        gtk_window_fullscreen(GTK_WINDOW(m_window.get()));\n        break;\n    case Qt::WindowNoState:\n    case Qt::WindowActive:\n        break;\n    }\n\n    m_state = state;\n}\n\nvoid QGtkWindow::onWindowStateEvent(GdkEvent *event)\n{\n    GdkEventWindowState *ev = (GdkEventWindowState*)event;\n    Qt::WindowState newState = Qt::WindowNoState;\n\n    if (ev->new_window_state & GDK_WINDOW_STATE_ICONIFIED) {\n        newState = Qt::WindowMinimized;\n    }\n    if (ev->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {\n        newState = Qt::WindowMaximized;\n    }\n    if (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) {\n        newState = Qt::WindowFullScreen;\n    }\n\n    if (newState != m_state) {\n        m_state = newState;\n        QWindowSystemInterface::handleWindowStateChanged(window(), newState);\n    }\n\n    Qt::WindowType type = static_cast<Qt::WindowType>(int(m_flags & Qt::WindowType_Mask));\n\n    // We must not send window activation changes for tooltip windows, as they\n    // will auto-hide on activation change.\n    if (type != Qt::ToolTip && (ev->changed_mask & GDK_WINDOW_STATE_FOCUSED)) {\n        static QPointer<QWindow> newActiveWindow = nullptr;\n        if (ev->new_window_state & GDK_WINDOW_STATE_FOCUSED) {\n            qCDebug(lcWindow) << window() << \" focused\";\n            newActiveWindow = window();\n        } else if (newActiveWindow == window()) {\n            qCDebug(lcWindow) << window() << \" unfocused\";\n            newActiveWindow = nullptr;\n        }\n\n        // We need a timer here to debounce the focus changes. Reason being that\n        // one window appearing results in two GDK_WINDOW_STATE_FOCUSED changes:\n        // one for the old window to remove it, one for the new window to add\n        // it.\n        //\n        // Without a debounce, we set the active window like this:\n        // old\n        // nullptr\n        // new\n        //\n        // ... which, in the case of say, popups, may result in their being\n        // dismissed (since a combo box shouldn't be kept open if its parent\n        // window loses focus to something other than the combo).\n        QTimer::singleShot(0, [=]() {\n            qCDebug(lcWindow) << \"Active changed to \" << newActiveWindow.data();\n            QWindowSystemInterface::handleWindowActivated(newActiveWindow.data(), Qt::ActiveWindowFocusReason);\n        });\n    }\n\n    // GDK_WINDOW_STATE_TILED not handled.\n    // GDK_WINDOW_STATE_STICKY not handled.\n    // GDK_WINDOW_STATE_ABOVE not handled.\n    // GDK_WINDOW_STATE_BELOW not handled.\n    // GDK_WINDOW_STATE_WITHDRAWN not handled.\n}\n\nvoid QGtkWindow::onEnterLeaveWindow(GdkEvent *event, bool entered)\n{\n    GdkEventCrossing *ev = (GdkEventCrossing*)event;\n    static QPointer<QWindow> enterWindow;\n    static QPoint enterPos;\n    static QPoint globalEnterPos;\n    static QPointer<QWindow> leaveWindow;\n    static QPoint leavePos;\n    static QPoint globalLeavePos;\n\n    if (entered) {\n        enterWindow = window();\n        enterPos = QPoint(ev->x, ev->y);\n        globalEnterPos = QPoint(ev->x_root, ev->y_root);\n    } else {\n        leaveWindow = window();\n        leavePos = QPoint(ev->x, ev->y);\n        globalLeavePos = QPoint(ev->x_root, ev->y_root);\n    }\n\n    QTimer::singleShot(0, [=]() {\n        if (enterWindow && leaveWindow) {\n            QWindowSystemInterface::handleEnterLeaveEvent(enterWindow.data(), leaveWindow.data(), leavePos, globalLeavePos);\n        } else if (enterWindow) {\n            QWindowSystemInterface::handleEnterEvent(enterWindow.data(), enterPos, globalEnterPos);\n        } else if (leaveWindow) {\n            QWindowSystemInterface::handleLeaveEvent(leaveWindow.data());\n        }\n    });\n}\n\nvoid QGtkWindow::onLeaveContent()\n{\n    // reset the mouse cursor.\n    QGtkRefPtr<GdkCursor> c = gdk_cursor_new_from_name(gdk_display_get_default(), \"default\");\n    gdk_window_set_cursor(gtk_widget_get_window(m_window.get()), c.get());\n}\n\nWId QGtkWindow::winId() const\n{\n    return (WId)m_window.get();\n}\n\nvoid QGtkWindow::setParent(const QPlatformWindow *)\n{\n}\n\nvoid QGtkWindow::setWindowTitle(const QString &title)\n{\n    gtk_window_set_title(GTK_WINDOW(m_window.get()), title.toUtf8().constData());\n}\n\nvoid QGtkWindow::setWindowFilePath(const QString &title)\n{\n    // we can't do anything useful with this\n    Q_UNUSED(title);\n}\n\nvoid QGtkWindow::setWindowIcon(const QIcon &icon)\n{\n    if (icon.isNull()) {\n        gtk_window_set_icon(GTK_WINDOW(m_window.get()), nullptr);\n        return;\n    }\n\n    QGtkRefPtr<GdkPixbuf> pb = qt_iconToPixbuf(icon);\n\n    // ### consider gtk_window_set_icon_list\n    gtk_window_set_icon(GTK_WINDOW(m_window.get()), pb.get());\n}\n\nvoid QGtkWindow::raise()\n{\n    // we cannot control the stacking order\n}\n\nvoid QGtkWindow::lower()\n{\n    // we cannot control the stacking order\n}\n\nbool QGtkWindow::isExposed() const\n{\n    return gtk_widget_get_visible(m_window.get());\n}\n\nbool QGtkWindow::isActive() const\n{\n    return gtk_widget_has_focus(m_window.get());\n}\n\nvoid QGtkWindow::propagateSizeHints()\n{\n    QSize minSize = windowMinimumSize();\n    QSize maxSize = windowMaximumSize();\n    QSize baseSize = windowBaseSize();\n    QSize sizeIncrement = windowSizeIncrement();\n\n    int activeHints = GdkWindowHints(0);\n    GdkGeometry hints;\n    if (!minSize.isNull()) {\n        hints.min_width = minSize.width();\n        hints.min_height = minSize.height();\n        activeHints |= GDK_HINT_MIN_SIZE;\n        gtk_widget_set_size_request(GTK_WIDGET(m_content.get()), hints.min_width, hints.min_height);\n    }\n\n    if (!maxSize.isNull()) {\n        hints.max_width = maxSize.width();\n        hints.max_height = maxSize.height();\n        activeHints |= GDK_HINT_MAX_SIZE;\n    }\n\n    if (!baseSize.isNull()) {\n        hints.base_width = baseSize.width();\n        hints.base_height = baseSize.height();\n        activeHints |= GDK_HINT_BASE_SIZE;\n    }\n\n    if (sizeIncrement.isNull()) {\n        hints.width_inc = sizeIncrement.width();\n        hints.height_inc = sizeIncrement.height();\n        activeHints |= GDK_HINT_RESIZE_INC;\n    }\n\n    if ((activeHints & GDK_HINT_MIN_SIZE) && (activeHints & GDK_HINT_MAX_SIZE)) {\n        if (minSize == maxSize) {\n            gtk_window_set_resizable(GTK_WINDOW(m_window.get()), false);\n        } else {\n            gtk_window_set_resizable(GTK_WINDOW(m_window.get()), true);\n        }\n    } else {\n        gtk_window_set_resizable(GTK_WINDOW(m_window.get()), true);\n    }\n\n    gtk_window_set_geometry_hints(\n        GTK_WINDOW(m_window.get()),\n        m_window.get(),\n        &hints,\n        GdkWindowHints(activeHints)\n    );\n}\n\nvoid QGtkWindow::setOpacity(qreal level)\n{\n    gtk_widget_set_opacity(m_window.get(), level);\n}\n\nvoid QGtkWindow::requestActivateWindow()\n{\n    qCDebug(lcWindow) << \"Request activate\" << window();\n    gtk_window_present(GTK_WINDOW(m_window.get()));\n}\n\nvoid QGtkWindow::setMask(const QRegion &region)\n{\n    // can't do anything useful with this\n    Q_UNUSED(region);\n}\n\nbool QGtkWindow::setKeyboardGrabEnabled(bool grab)\n{\n    if (grab) {\n        gtk_window_present(GTK_WINDOW(m_window.get()));\n    }\n    return true;\n}\nbool QGtkWindow::setMouseGrabEnabled(bool grab)\n{\n    if (grab) {\n        gtk_window_present(GTK_WINDOW(m_window.get()));\n    }\n    return true;\n}\n\n/*\nvoid QGtkWindow::setFrameStrutEventsEnabled(bool enabled){}\nbool QGtkWindow::frameStrutEventsEnabled() const{}\n*/\n\nvoid QGtkWindow::setAlertState(bool enabled)\n{\n    gtk_window_set_urgency_hint(GTK_WINDOW(m_window.get()), enabled);\n}\n\nbool QGtkWindow::isAlertState() const\n{\n    return gtk_window_get_urgency_hint(GTK_WINDOW(m_window.get()));\n}\n\n/*\nvoid QGtkWindow::invalidateSurface(){}\n*/\n\nQGtkRefPtr<GtkWidget> QGtkWindow::gtkWindow() const\n{\n    return m_window;\n}\n\nQGtkRefPtr<GtkMenuBar> QGtkWindow::gtkMenuBar() const\n{\n    return m_menubar;\n}\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkwindow.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#ifndef QGTKWINDOW_H\n#define QGTKWINDOW_H\n\n#include \"qgtkrefptr.h\"\n\n#include <QDebug>\n#include <qpa/qplatformwindow.h>\n#include <qpa/qwindowsysteminterface.h>\n\n#include <gtk/gtk.h>\n\nQT_BEGIN_NAMESPACE\n\nclass QTouchDevice;\n\nclass QGtkWindow : public QObject, public QPlatformWindow\n{\npublic:\n    QGtkWindow(QWindow *window);\n    ~QGtkWindow();\n\n    void create(Qt::WindowType windowType);\n\n    QSurfaceFormat format() const override;\n\n    void setGeometry(const QRect &rect) override;\n    QRect geometry() const override;\n    QRect normalGeometry() const override;\n    qreal devicePixelRatio() const;\n\n    QMargins frameMargins() const override;\n\n    void setVisible(bool visible) override;\n    void setWindowFlags(Qt::WindowFlags flags) override;\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n    void setWindowState(Qt::WindowStates state) override;\n#else\n    void setWindowState(Qt::WindowState state) override;\n#endif\n\n    WId winId() const override;\n    void setParent(const QPlatformWindow *window) override;\n\n    void setWindowTitle(const QString &title) override;\n    void setWindowFilePath(const QString &title) override;\n    void setWindowIcon(const QIcon &icon) override;\n    void raise() override;\n    void lower() override;\n\n    bool isExposed() const override;\n    bool isActive() const override;\n    void propagateSizeHints() override;\n    void setOpacity(qreal level) override;\n    void requestActivateWindow() override;\n    void setMask(const QRegion &region) override;\n    bool setKeyboardGrabEnabled(bool grab) override;\n    bool setMouseGrabEnabled(bool grab) override;\n/*\n    bool setWindowModified(bool modified) override;\n\n    void windowEvent(QEvent *event) override;\n\n    bool startSystemResize(const QPoint &pos, Qt::Corner corner) override;\n\n    void setFrameStrutEventsEnabled(bool enabled) override;\n    bool frameStrutEventsEnabled() const override;\n*/\n    void setAlertState(bool enabled) override;\n    bool isAlertState() const override;\n/*\n    void invalidateSurface() override;\n*/\n    void requestUpdate() override;\n\n    // End API, start implementation.\n    void onDraw(cairo_t *cr);\n    void onRender();\n    void onMap();\n    void onUnmap();\n    void onConfigure();\n    bool onDelete();\n    bool onKeyPress(GdkEvent *event);\n    bool onKeyRelease(GdkEvent *event);\n    bool onButtonPress(GdkEvent *event);\n    bool onButtonRelease(GdkEvent *event);\n    bool onMotionNotify(GdkEvent *event);\n    bool onTouchEvent(GdkEvent *event);\n    bool onScrollEvent(GdkEvent *event);\n    void onWindowStateEvent(GdkEvent *event);\n    void onWindowTickCallback();\n    void onEnterLeaveWindow(GdkEvent *event, bool entered);\n    void onLeaveContent();\n    QImage *beginUpdateFrame(const QString &reason);\n    void endUpdateFrame(const QString &reason);\n    void invalidateRegion(const QRegion &region);\n    QImage currentFrameImage() const;\n\n    QGtkRefPtr<GtkMenuBar> gtkMenuBar() const;\n    QGtkRefPtr<GtkWidget> gtkWindow() const;\n\n    void beginZoom(QPointF &contentPoint, guint32 ts);\n    void zoom(QPointF &contentPoint, double scale, guint32 ts);\n    void endZoom(QPointF &contentPoint, guint32 ts);\n\n    void beginRotate(QPointF &contentPoint, guint32 ts);\n    void rotate(QPointF &contentPoint, double angle, double angle_delta, guint32 ts);\n    void endRotate(QPointF &contentPoint, guint32 ts);\n\nprivate:\n    void maybeForceTransientParent(Qt::WindowType windowType);\n    void reallyForceTransientFor(QWindow *transientParent);\n\n    QGtkRefPtr<GtkWidget> m_window;\n    QGtkRefPtr<GtkMenuBar> m_menubar;\n    QGtkRefPtr<GtkWidget> m_content;\n    QMutex m_frameMutex;\n    QImage m_frame;\n    QTouchDevice *m_touchDevice = nullptr;\n    QList<QWindowSystemInterface::TouchPoint> m_activeTouchPoints;\n    Qt::MouseButtons m_buttons;\n    Qt::WindowState m_state = Qt::WindowNoState;\n    bool m_wantsUpdate = false;\n    guint m_tick_callback = 0;\n    Qt::WindowFlags m_flags = Qt::Widget;\n    QRect m_windowGeometry; // must be cached as it's accessed from multiple threads\n    QRect m_newGeometry;\n    Qt::KeyboardModifiers m_scrollModifiers = Qt::NoModifier;\n    bool m_scrollStarted = false;\n\n    static void drawCallback(GtkWidget *, cairo_t *cr, gpointer platformWindow);\n    static gboolean windowTickCallback(GtkWidget*, GdkFrameClock *, gpointer platformWindow);\n\n    static void zoom_cb(GtkGestureZoom *pt, gdouble scale, gpointer platformWindow);\n    static void begin_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);\n    static void end_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);\n    static void cancel_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);\n\n    static void rotate_cb(GtkGestureRotate *pt, gdouble angle, gdouble angle_delta, gpointer platformWindow);\n    static void begin_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);\n    static void end_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);\n    static void cancel_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);\n\n    QGtkRefPtr<GtkGesture> m_zoomGesture;\n    QGtkRefPtr<GtkGesture> m_rotateGesture;\n    int m_activeNativeGestures = 0;\n    bool m_initialZoomSet = false;\n    double m_initialZoom = 0;\n    bool m_initialRotateSet = false;\n    double m_initialRotate = 0;\n    bool m_hasTickCallback = false;\n    QTimer *m_cancelTickTimer = nullptr;\n};\n\nclass QGtkCourierObject : public QObject\n{\n    Q_OBJECT\n\npublic:\n    static QGtkCourierObject *instance;\n\n    QGtkCourierObject(QObject *parent = nullptr);\n    Q_INVOKABLE void queueDraw(QGtkWindow *win);\n};\n\n// QGtkWindow is ambiguous with the QObject* and QPlatformSurface* overloads; choose one\ninline QDebug operator<<(QDebug debug, const QGtkWindow *surface)\n{\n    return debug << static_cast<const QPlatformWindow*>(surface);\n}\n\nQT_END_NAMESPACE\n\n#endif // QGTKWINDOW_H\n"
  },
  {
    "path": "src/platform-plugin/qgtkwindow_gesture.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkwindow.h\"\n\n#include <math.h> // M_PI\n\n#include <QtCore/qloggingcategory.h>\n\nQ_LOGGING_CATEGORY(lcGesture, \"qt.qpa.gtk.gesture\");\n\n\nstatic void populateTsAndPoint(GtkGesture *gesture, guint32 &ts, QPointF &contentPoint)\n{\n    gdouble x;\n    gdouble y;\n\n    GdkEventSequence *seq = gtk_gesture_get_last_updated_sequence(gesture);\n    gtk_gesture_get_point(gesture, seq, &x, &y);\n    contentPoint = QPointF(x, y);\n    const GdkEvent *ev = gtk_gesture_get_last_event(gesture, seq);\n    ts = gdk_event_get_time(ev);\n}\n\nvoid QGtkWindow::zoom_cb(GtkGestureZoom *pt, gdouble scale, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    guint32 ts;\n    QPointF contentPoint;\n    populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);\n    GdkEventSequence *seq = gtk_gesture_get_last_updated_sequence(GTK_GESTURE(pt));\n    gtk_gesture_set_sequence_state(GTK_GESTURE(pt), seq, GTK_EVENT_SEQUENCE_CLAIMED); // ### not really sure this should be done here, but crashes in begin\n\n    pw->zoom(contentPoint, scale, ts);\n}\n\nvoid QGtkWindow::begin_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    guint32 ts;\n    QPointF contentPoint;\n    populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);\n\n    qCDebug(lcGesture) << \"Begin zoom \" << pw->window() << ts << contentPoint;\n    pw->beginZoom(contentPoint, ts);\n}\n\nvoid QGtkWindow::end_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    guint32 ts;\n    QPointF contentPoint;\n    populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);\n\n    qCDebug(lcGesture) << \"End zoom \" << pw->window() << ts << contentPoint;\n    pw->endZoom(contentPoint, ts);\n}\n\nvoid QGtkWindow::cancel_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    guint32 ts;\n    QPointF contentPoint;\n    populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);\n\n    qCDebug(lcGesture) << \"Cancel zoom \" << pw->window() << ts << contentPoint;\n    pw->endZoom(contentPoint, ts);\n}\n\nvoid QGtkWindow::beginZoom(QPointF &contentPoint, guint32 ts)\n{\n    m_initialZoomSet = false;\n    if (m_activeNativeGestures++ == 0) {\n        qCDebug(lcGesture) << \"Started native gesture sequence (due to zoom)\";\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n        QWindowSystemInterface::handleGestureEvent(window(), nullptr, ts, Qt::BeginNativeGesture, contentPoint, contentPoint);\n#else\n        QWindowSystemInterface::handleGestureEvent(window(), ts, Qt::BeginNativeGesture, contentPoint, contentPoint);\n#endif\n    }\n}\n\nvoid QGtkWindow::zoom(QPointF &contentPoint, double scale, guint32 ts)\n{\n    if  (scale == 0.0) {\n        return; // insane\n    }\n\n    if (!m_initialZoomSet) {\n        m_initialZoomSet = true;\n        m_initialZoom = scale;\n    }\n    double modScale = (scale - m_initialZoom) / m_initialZoom;\n    m_initialZoom = scale;\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n    QWindowSystemInterface::handleGestureEventWithRealValue(window(), nullptr, ts, Qt::ZoomNativeGesture, modScale, contentPoint, contentPoint);\n#else\n    QWindowSystemInterface::handleGestureEventWithRealValue(window(), ts, Qt::ZoomNativeGesture, modScale, contentPoint, contentPoint);\n#endif\n}\n\nvoid QGtkWindow::endZoom(QPointF &contentPoint, guint32 ts)\n{\n    if (--m_activeNativeGestures == 0) {\n        qCDebug(lcGesture) << \"Ended native gesture sequence (due to zoom)\";\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n        QWindowSystemInterface::handleGestureEvent(window(), nullptr, ts, Qt::EndNativeGesture, contentPoint, contentPoint);\n#else\n        QWindowSystemInterface::handleGestureEvent(window(), ts, Qt::EndNativeGesture, contentPoint, contentPoint);\n#endif\n    }\n}\n\nvoid QGtkWindow::rotate_cb(GtkGestureRotate *pt, gdouble angle, gdouble angle_delta, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    guint32 ts;\n    QPointF contentPoint;\n    populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);\n    GdkEventSequence *seq = gtk_gesture_get_last_updated_sequence(GTK_GESTURE(pt));\n    gtk_gesture_set_sequence_state(GTK_GESTURE(pt), seq, GTK_EVENT_SEQUENCE_CLAIMED); // ### not really sure this should be done here, but crashes in begin\n\n    pw->rotate(contentPoint, angle, angle_delta, ts);\n}\n\nvoid QGtkWindow::begin_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    guint32 ts;\n    QPointF contentPoint;\n    populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);\n\n    qCDebug(lcGesture) << \"Begin rotate \" << pw->window() << ts << contentPoint;\n    pw->beginRotate(contentPoint, ts);\n}\n\nvoid QGtkWindow::end_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    guint32 ts;\n    QPointF contentPoint;\n    populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);\n\n    qCDebug(lcGesture) << \"End rotate \" << pw->window() << ts << contentPoint;\n    pw->endRotate(contentPoint, ts);\n}\n\nvoid QGtkWindow::cancel_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    guint32 ts;\n    QPointF contentPoint;\n    populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);\n\n    qCDebug(lcGesture) << \"Cancel rotate \" << pw->window() << ts << contentPoint;\n    pw->endRotate(contentPoint, ts);\n}\n\nvoid QGtkWindow::beginRotate(QPointF &contentPoint, guint32 ts)\n{\n    m_initialRotateSet = false;\n    if (m_activeNativeGestures++ == 0) {\n        qCDebug(lcGesture) << \"Started native gesture sequence (due to rotate)\";\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n        QWindowSystemInterface::handleGestureEvent(window(), nullptr, ts, Qt::BeginNativeGesture, contentPoint, contentPoint);\n#else\n        QWindowSystemInterface::handleGestureEvent(window(), ts, Qt::BeginNativeGesture, contentPoint, contentPoint);\n#endif\n    }\n}\n\nvoid QGtkWindow::rotate(QPointF &contentPoint, double angle, double angle_delta, guint32 ts)\n{\n    Q_UNUSED(angle_delta)\n    angle = -angle;\n    if (!m_initialRotateSet) {\n        m_initialRotateSet = true;\n        m_initialRotate = angle * 180 / M_PI;\n    }\n    double degrees = m_initialRotate - (angle * 180 / M_PI);\n    m_initialRotate = angle * 180 / M_PI;\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n    QWindowSystemInterface::handleGestureEventWithRealValue(window(), nullptr, ts, Qt::RotateNativeGesture, degrees, contentPoint, contentPoint);\n#else\n    QWindowSystemInterface::handleGestureEventWithRealValue(window(), ts, Qt::RotateNativeGesture, degrees, contentPoint, contentPoint);\n#endif\n}\n\nvoid QGtkWindow::endRotate(QPointF &contentPoint, guint32 ts)\n{\n    if (--m_activeNativeGestures == 0) {\n        qCDebug(lcGesture) << \"Ended native gesture sequence (due to rotate)\";\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n        QWindowSystemInterface::handleGestureEvent(window(), nullptr, ts, Qt::EndNativeGesture, contentPoint, contentPoint);\n#else\n        QWindowSystemInterface::handleGestureEvent(window(), ts, Qt::EndNativeGesture, contentPoint, contentPoint);\n#endif\n    }\n}\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkwindow_keyboard.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkwindow.h\"\n#include \"qgtkhelpers.h\"\n\n#include <qpa/qwindowsysteminterface.h>\n\n#include <QtCore/qdebug.h>\n\n#include \"CSystrace.h\"\n\nbool QGtkWindow::onKeyPress(GdkEvent *event)\n{\n    GdkEventKey *ev = (GdkEventKey*)event;\n    TRACE_EVENT_ASYNC_BEGIN0(\"input\", \"QGtkWindow::keyDown\", (void*)ev->hardware_keycode);\n    TRACE_EVENT0(\"input\", \"QGtkWindow::onKeyPress\");\n\n    const QString text = QString::fromUtf8(ev->string, ev->length);\n    Qt::KeyboardModifiers qtMods = qt_convertToQtKeyboardMods(ev->state);\n    Qt::Key qtKey = qt_convertToQtKey(ev->keyval);\n\n    return QWindowSystemInterface::handleExtendedKeyEvent(\n        window(),\n        ev->time,\n        QEvent::KeyPress,\n        qtKey,\n        qtMods,\n        ev->hardware_keycode,\n        ev->hardware_keycode,\n        0,\n        text\n    );\n}\n\nbool QGtkWindow::onKeyRelease(GdkEvent *event)\n{\n    GdkEventKey *ev = (GdkEventKey*)event;\n    TRACE_EVENT_ASYNC_END0(\"input\", \"QGtkWindow::keyDown\", (void*)ev->hardware_keycode);\n    TRACE_EVENT0(\"input\", \"QGtkWindow::onKeyRelease\");\n\n    const QString text = QString::fromUtf8(ev->string, ev->length);\n    Qt::KeyboardModifiers qtMods = qt_convertToQtKeyboardMods(ev->state);\n    Qt::Key qtKey = qt_convertToQtKey(ev->keyval);\n\n    return QWindowSystemInterface::handleExtendedKeyEvent(\n        window(),\n        ev->time,\n        QEvent::KeyRelease,\n        qtKey,\n        qtMods,\n        ev->hardware_keycode,\n        ev->hardware_keycode,\n        0,\n        text\n    );\n}\n\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkwindow_mouse.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkwindow.h\"\n#include \"qgtkhelpers.h\"\n\n#include <qpa/qwindowsysteminterface.h>\n#include <QtCore/qloggingcategory.h>\n#include <QtCore/qdebug.h>\n\n#include \"CSystrace.h\"\n\nQ_LOGGING_CATEGORY(lcMouse, \"qt.qpa.gtk.mouse\");\nQ_LOGGING_CATEGORY(lcMouseMotion, \"qt.qpa.gtk.mouse.motion\");\n\nbool QGtkWindow::onButtonPress(GdkEvent *event)\n{\n    TRACE_EVENT0(\"input\", \"QGtkWindow::onButtonPress\");\n    TRACE_EVENT_ASYNC_BEGIN0(\"input\", \"QGtkWindow::mouseDown\", this);\n    GdkEventButton *ev = (GdkEventButton*)event;\n\n    // ### would be nice if we could support GDK_2BUTTON_PRESS/GDK_3BUTTON_PRESS\n    // directly (and not via emulation internally).\n\n    Qt::MouseButton b = qt_convertGButtonToQButton(ev->button);\n    m_buttons |= b;\n    qCDebug(lcMouse) << \"Pressed \" << b << \" at \" << ev->x << ev->y << ev->x_root << ev->y_root << \" total pressed \" << m_buttons;\n\n    bool isTabletEvent = false;\n    QWindowSystemInterface::handleMouseEvent(\n        window(),\n        ev->time,\n        QPointF(ev->x, ev->y),\n        QPointF(ev->x_root, ev->y_root), // ### _root is probably wrong.\n        m_buttons,\n        qt_convertToQtKeyboardMods(ev->state),\n        isTabletEvent ? Qt::MouseEventSynthesizedByQt : Qt::MouseEventNotSynthesized\n    );\n    return true;\n}\n\nbool QGtkWindow::onButtonRelease(GdkEvent *event)\n{\n    TRACE_EVENT0(\"input\", \"QGtkWindow::onButtonRelease\");\n    GdkEventButton *ev = (GdkEventButton*)event;\n\n    Qt::MouseButton b = qt_convertGButtonToQButton(ev->button);\n    m_buttons &= ~b;\n    qCDebug(lcMouse) << \"Released \" << b << \" at \" << ev->x << ev->y << ev->x_root << ev->y_root << \" total pressed \" << m_buttons;\n\n    bool isTabletEvent = false;\n    QWindowSystemInterface::handleMouseEvent(\n        window(),\n        ev->time,\n        QPointF(ev->x, ev->y),\n        QPointF(ev->x_root, ev->y_root),\n        m_buttons,\n        qt_convertToQtKeyboardMods(ev->state),\n        isTabletEvent ? Qt::MouseEventSynthesizedByQt : Qt::MouseEventNotSynthesized\n    );\n    TRACE_EVENT_ASYNC_END0(\"input\", \"QGtkWindow::mouseDown\", this);\n    return true;\n}\n\nbool QGtkWindow::onMotionNotify(GdkEvent *event)\n{\n    TRACE_EVENT0(\"input\", \"QGtkWindow::onMotionNotify\");\n    GdkEventButton *ev = (GdkEventButton*)event;\n    qCDebug(lcMouseMotion) << \"Moved mouse at \" << ev->x << ev->y << ev->x_root << ev->y_root;\n\n    QPoint mousePos(ev->x, ev->y);\n    mousePos = window()->mapToGlobal(mousePos);\n    QCursor::setPos(mousePos);\n\n    bool isTabletEvent = false;\n    QWindowSystemInterface::handleMouseEvent(\n        window(),\n        ev->time,\n        QPointF(ev->x, ev->y),\n        QPointF(ev->x_root, ev->y_root),\n        m_buttons,\n        qt_convertToQtKeyboardMods(ev->state),\n        isTabletEvent ? Qt::MouseEventSynthesizedByQt : Qt::MouseEventNotSynthesized\n    );\n    return true;\n}\n\nbool QGtkWindow::onScrollEvent(GdkEvent *event)\n{\n    TRACE_EVENT0(\"input\", \"QGtkWindow::onScrollEvent\");\n    GdkEventScroll *ev = (GdkEventScroll*)event;\n\n    QPoint angleDelta;\n    QPoint pixelDelta;\n    Qt::MouseEventSource source = Qt::MouseEventNotSynthesized;\n    Qt::ScrollPhase phase = Qt::ScrollUpdate;\n\n    // We cache the modifiers as they should not change after the scroll has\n    // started. Doing that means that you'll zoom text in Creator or something,\n    // which is pretty annoying.\n    if (!m_scrollStarted) {\n        m_scrollStarted = true;\n        m_scrollModifiers = qt_convertToQtKeyboardMods(ev->state);\n        phase = Qt::ScrollBegin;\n    }\n    if (gdk_event_is_scroll_stop_event(event)) {\n        m_scrollStarted = false;\n        m_scrollModifiers = Qt::NoModifier;\n        phase = Qt::ScrollEnd;\n    }\n\n    if (ev->direction == GDK_SCROLL_SMOOTH) {\n        // ### I have literally no idea what I'm doing here\n        const int pixelsToDegrees = 50;\n        angleDelta.setX(-ev->delta_x * pixelsToDegrees);\n        angleDelta.setY(-ev->delta_y * pixelsToDegrees);\n        source = Qt::MouseEventSynthesizedBySystem;\n\n        pixelDelta.setX(ev->delta_x * pixelsToDegrees);\n        pixelDelta.setY(-ev->delta_y * pixelsToDegrees);\n    } else if (ev->direction == GDK_SCROLL_UP ||\n               ev->direction == GDK_SCROLL_DOWN) {\n        angleDelta.setY(qBound(-120, int(ev->delta_y * 10000), 120));\n    } else if (ev->direction == GDK_SCROLL_LEFT ||\n               ev->direction == GDK_SCROLL_RIGHT) {\n        angleDelta.setX(qBound(-120, int(ev->delta_x * 10000), 120));\n    } else {\n        Q_UNREACHABLE();\n    }\n\n    qCDebug(lcMouseMotion) << \"Scrolled mouse at \" << ev->x << ev->y << ev->x_root << ev->y_root << \" angle delta \" << angleDelta << \" pixelDelta \" << pixelDelta << \" original deltas \" << ev->delta_x << ev->delta_y;\n\n    QWindowSystemInterface::handleWheelEvent(\n        window(),\n        ev->time,\n        QPointF(ev->x, ev->y),\n        QPointF(ev->x_root, ev->y_root),\n        pixelDelta,\n        angleDelta,\n        m_scrollModifiers,\n        phase,\n        source,\n        false /* isInverted */\n    );\n    return true;\n}\n\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkwindow_render.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkwindow.h\"\n#include \"qgtkhelpers.h\"\n\n#include <QtCore/qloggingcategory.h>\n#include <QtCore/qthread.h>\n#include <QtCore/qtimer.h>\n#include <QtGui/private/qwindow_p.h>\n\n#include \"CSystrace.h\"\n\nQ_LOGGING_CATEGORY(lcWindowRender, \"qt.qpa.gtk.window.render\");\n\n// debug QGtkWindow lock on surface content\n#undef LOCK_DEBUG\n\nvoid QGtkWindow::drawCallback(GtkWidget *, cairo_t *cr, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowRender) << \"drawCallback\" << pw;\n    pw->onDraw(cr);\n}\n\nvoid QGtkWindow::onDraw(cairo_t *cr)\n{\n    if (m_newGeometry != m_windowGeometry) {\n        bool needsExpose = m_newGeometry.size() != m_windowGeometry.size();\n#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)\n        QWindowSystemInterface::handleGeometryChange(window(), m_newGeometry);\n#else\n        QWindowSystemInterface::handleGeometryChange(window(), m_newGeometry, m_windowGeometry);\n#endif\n        m_windowGeometry = m_newGeometry;\n\n        if (needsExpose) {\n            QWindowSystemInterface::handleExposeEvent(window(), m_windowGeometry);\n\n            // we must flush, otherwise the content we'll render might be out of date.\n            // ### would be nice if we could compress these, somehow: at the least we'll\n            // get a configure and a size-allocate independent of each other.\n            QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);\n        }\n    }\n    TRACE_EVENT0(\"gfx\", \"QGtkWindow::onDraw\");\n    // Hold frameMutex during blit to cairo to prevent changes\n    QMutexLocker lock(&m_frameMutex);\n#if defined(LOCK_DEBUG)\n    qWarning() << \"rendering (LOCKED)\" << this;\n#endif\n    if (m_frame.isNull())\n        return;\n\n#if 0\n    QString clipString;\n    cairo_rectangle_list_t *clip = cairo_copy_clip_rectangle_list(cr);\n    for (int i = 0; i < clip->num_rectangles; i++) {\n        auto r = clip->rectangles[i];\n        clipString += QString(\"%1,%2@%3x%4  \").arg(r.x).arg(r.y).arg(r.width).arg(r.height);\n    }\n    qDebug(lcWindow) << \"onDraw with clip:\" << clipString;\n#endif\n\n    cairo_surface_t *surf = cairo_image_surface_create_for_data(\n            const_cast<uchar*>(m_frame.constBits()),\n            CAIRO_FORMAT_ARGB32,\n            m_frame.width(),\n            m_frame.height(),\n            m_frame.bytesPerLine()\n    );\n    int sf = gtk_widget_get_scale_factor(m_window.get());\n    cairo_surface_set_device_scale(surf, sf, sf);\n    cairo_set_source_surface(cr, surf, 0, 0);\n    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);\n    // cairo_paint respects the current clip, which GTK sets based on\n    // updated regions of the window, so we don't need to do anything\n    // other than include the updated regions in queue_draw calls.\n    cairo_paint(cr);\n    cairo_surface_destroy(surf);\n#if defined(LOCK_DEBUG)\n    qWarning() << \"rendering (UNLOCKING)\" << this;\n#endif\n}\n\n\ngboolean QGtkWindow::windowTickCallback(GtkWidget*, GdkFrameClock *, gpointer platformWindow)\n{\n    QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);\n    qCDebug(lcWindowRender) << \"windowTickCallback\" << pw;\n    pw->onWindowTickCallback();\n    return G_SOURCE_CONTINUE;\n}\n\n\nvoid QGtkWindow::onWindowTickCallback()\n{\n    TRACE_EVENT0(\"gfx\", \"QGtkWindow::onWindowTickCallback\");\n    if (m_wantsUpdate) {\n        m_wantsUpdate = false;\n        TRACE_EVENT_ASYNC_END0(\"gfx\", \"QGtkWindow::requestUpdate\", this);\n#if QT_VERSION >= QT_VERSION_CHECK(5,12,0)\n        this->deliverUpdateRequest();\n#else\n        QWindowPrivate::get(window())->deliverUpdateRequest();\n#endif\n    } else {\n        if (m_cancelTickTimer) {\n            return;\n        }\n        qCDebug(lcWindowRender) << \"Preparing to remove tick callback\" << this;\n        // Stop delivering ticks if update requests end for a long time.\n        m_cancelTickTimer = new QTimer(this);\n        m_cancelTickTimer->setInterval(1000);\n        m_cancelTickTimer->setSingleShot(true);\n        m_cancelTickTimer->start();\n        connect(m_cancelTickTimer, &QTimer::timeout, this, [=]() {\n            if (!m_wantsUpdate) {\n                qCDebug(lcWindowRender) << \"Removing tick callback\" << this;\n                gtk_widget_remove_tick_callback(m_window.get(), m_tick_callback);\n                m_tick_callback = 0;\n                m_hasTickCallback = false;\n                m_cancelTickTimer->deleteLater();\n                m_cancelTickTimer = nullptr;\n            }\n        });\n    }\n}\n\nvoid QGtkWindow::requestUpdate()\n{\n    TRACE_EVENT_ASYNC_BEGIN0(\"gfx\", \"QGtkWindow::requestUpdate\", this);\n    m_wantsUpdate = true;\n    if (!m_hasTickCallback) {\n        qCDebug(lcWindowRender) << \"Installing tick callback\" << this;\n        m_hasTickCallback = true;\n        m_tick_callback = gtk_widget_add_tick_callback(m_window.get(), QGtkWindow::windowTickCallback, this, NULL);\n    }\n    if (m_cancelTickTimer) {\n        m_cancelTickTimer->deleteLater();\n        m_cancelTickTimer = nullptr;\n        qCDebug(lcWindowRender) << \"Cancelling remove of tick callback\" << this;\n    }\n}\n\nQGtkCourierObject::QGtkCourierObject(QObject *parent)\n    : QObject(parent)\n{\n    qRegisterMetaType<QGtkWindow*>(\"QGtkWindow*\");\n}\n\nvoid QGtkCourierObject::queueDraw(QGtkWindow *win)\n{\n    gtk_widget_queue_draw(win->gtkWindow().get());\n}\n\nQGtkCourierObject *QGtkCourierObject::instance;\n\nvoid QGtkWindow::invalidateRegion(const QRegion &region)\n{\n    auto courier = QGtkCourierObject::instance;\n    Q_ASSERT(courier);\n    if (courier->thread() != QThread::currentThread()) {\n        // In the multithreaded case, always signal a full screen update for now\n        courier->metaObject()->invokeMethod(courier, \"queueDraw\", Qt::QueuedConnection, Q_ARG(QGtkWindow*, this));\n        return;\n    }\n\n    QRegion realRegion = region.isNull() ? QRegion(m_frame.rect()) : region;\n    cairo_region_t *cairoRegion = qt_convertToCairoRegion(realRegion);\n    gtk_widget_queue_draw_region(m_content.get(), cairoRegion);\n    cairo_region_destroy(cairoRegion);\n}\n\nQImage *QGtkWindow::beginUpdateFrame(const QString &reason)\n{\n    m_frameMutex.lock();\n    Q_UNUSED(reason);\n#if defined(LOCK_DEBUG)\n    qWarning() << \"beginUpdateFrame \" << reason << \"(LOCKED)\" << this;\n#endif\n    return &m_frame;\n}\n\nvoid QGtkWindow::endUpdateFrame(const QString &reason)\n{\n    m_frameMutex.unlock();\n    Q_UNUSED(reason);\n#if defined(LOCK_DEBUG)\n    qWarning() << \"endUpdateFrame \" << reason << \"(UNLOCKED)\" << this;\n#endif\n}\n\nQImage QGtkWindow::currentFrameImage() const\n{\n    return m_frame;\n}\n\n"
  },
  {
    "path": "src/platform-plugin/qgtkwindow_touch.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2017 Crimson AS <info@crimson.no>\n** Contact: https://www.crimson.no\n**\n** GNU Lesser General Public License Usage\n** This file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n****************************************************************************/\n\n#include \"qgtkwindow.h\"\n#include \"qgtkhelpers.h\"\n\n#include <qpa/qwindowsysteminterface.h>\n\n#include <QtCore/qdebug.h>\n#include <QtCore/qloggingcategory.h>\n\n#include \"CSystrace.h\"\n\nQ_LOGGING_CATEGORY(lcTouch, \"qt.qpa.gtk.touch\");\nQ_LOGGING_CATEGORY(lcTouchUpdate, \"qt.qpa.gtk.touch.update\");\n\nbool QGtkWindow::onTouchEvent(GdkEvent *event)\n{\n    TRACE_EVENT0(\"input\", \"QGtkWindow::onTouchEvent\");\n    GdkEventTouch *ev = (GdkEventTouch*)event;\n\n    QWindowSystemInterface::TouchPoint *tp = 0;\n    int touchpointId = int(reinterpret_cast<intptr_t>(ev->sequence));\n\n    switch (ev->type) {\n    case GDK_TOUCH_BEGIN:\n        TRACE_EVENT_ASYNC_BEGIN0(\"input\", \"QGtkWindow::touchDown\", (void*)touchpointId);\n        qCDebug(lcTouch) << \"Begin \" << touchpointId;\n        m_activeTouchPoints.append(QWindowSystemInterface::TouchPoint());\n        tp = &m_activeTouchPoints.last();\n        tp->id = touchpointId;\n        break;\n    case GDK_TOUCH_UPDATE:\n        qCDebug(lcTouchUpdate) << \"Update \" << touchpointId;\n        for (int i = 0; i < m_activeTouchPoints.length(); ++i) {\n            if (m_activeTouchPoints.at(i).id == touchpointId) {\n                tp = &m_activeTouchPoints[i];\n                break;\n            }\n        }\n        break;\n    case GDK_TOUCH_END:\n        qCDebug(lcTouch) << \"End \" << touchpointId;\n        for (int i = 0; i < m_activeTouchPoints.length(); ++i) {\n            if (m_activeTouchPoints.at(i).id == touchpointId) {\n                tp = &m_activeTouchPoints[i];\n                break;\n            }\n        }\n        break;\n    case GDK_TOUCH_CANCEL:\n        qCDebug(lcTouch) << \"Cancel \" << touchpointId;\n        for (int i = 0; i < m_activeTouchPoints.length(); ++i) {\n            if (m_activeTouchPoints.at(i).id == touchpointId) {\n                tp = &m_activeTouchPoints[i];\n                break;\n            }\n        }\n        break;\n    default:\n        qWarning() << \"Unknown touch type\" << ev->type;\n        return false;\n    }\n\n    // ### it's unfortunate that we have to report touch events so often.\n    // perhaps we should tie the report of these into the frameclock so they are\n    // only sent once per frame? or ask gtk for a 'touch frame' event?\n\n    if (tp) {\n        // Update it\n        tp->pressure = 1.0; // ### should be able to read this somehow\n        tp->state = qt_convertToQtTouchPointState(ev->type);\n\n        // ### touchpoint size?\n        // the area is supposed to be centered on the point, hence the - 0.5\n        // subtractions (as we're reporting a size of 1).\n        QRectF tpArea = QRectF(ev->x - 0.5, ev->y - 0.5, 1, 1);\n\n        // make sure it really moved...\n        if (tp->state == Qt::TouchPointMoved) {\n            if (tp->area == tpArea) {\n                tp->state = Qt::TouchPointStationary;\n            }\n        }\n\n        tp->area = tpArea;\n\n        QSize s = window()->screen()->size();\n        qreal nx = ev->x / (s.width() / 2);\n        qreal ny = ev->y / (s.height() / 2);\n        tp->normalPosition = QPointF(nx, ny);\n    }\n\n    // report unconditionally even if tp was not found\n    // (in that case there was a release event)\n    QWindowSystemInterface::handleTouchEvent(\n        window(),\n        ev->time,\n        m_touchDevice,\n        m_activeTouchPoints,\n        qt_convertToQtKeyboardMods(ev->state)\n    );\n\n    switch (ev->type) {\n    case GDK_TOUCH_END:\n    case GDK_TOUCH_CANCEL:\n        for (int i = 0; i < m_activeTouchPoints.length(); ++i) {\n            if (m_activeTouchPoints.at(i).id == touchpointId) {\n                m_activeTouchPoints.removeAt(i);\n                break;\n            }\n        }\n        TRACE_EVENT_ASYNC_END0(\"input\", \"QGtkWindow::touchDown\", (void*)touchpointId);\n        break;\n    default:\n        break;\n    }\n\n    // Eat the event so that GTK doesn't synthesize pointer events from these.\n    return true;\n}\n\n\n"
  },
  {
    "path": "src/src.pro",
    "content": "TEMPLATE = subdirs\nSUBDIRS += \\\n    gtkextras \\\n    platform_plugin\n\nplatform_plugin.subdir = platform-plugin\nplatform_plugin.depends = gtkextras\n"
  },
  {
    "path": "sync.profile",
    "content": "%modules = (\n    \"QtGtkExtras\" => \"$basedir/src/gtkextras\",\n);\n\n%moduleheaders = (\n);\n"
  }
]