From e62dad48a67f10af43e524fb538c58a632db469c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 23 Mar 2016 11:25:58 +0100 Subject: [PATCH] Add a surface tree to DebugConsole Summary: While developing support for sub-surfaces it became obvious that there is a need for visualizing the tree of sub-surfaces. The surface-tree is a new mode added in the debug console. There are now two buttons to switch between the default window tree and the surface tree. The surface tree is a little bit more basic than the windows tree. The type of window (whether x11, wayland or internal) is ignored. All windows build up the top level with the sub surfaces as children. Each surface is represented by some basic information about it: * Client (binary path and pid) * internal surface id If the surface has a shared memory buffer mapped a scaled down version is used as the window decoration role. The model gets reset whenever the tree changes in some way as it's rather complex to track correctly and well, it's just a debug console. Currently the tree is not really functional yet as KWin doesn't announce support for sub-compositor protocol, which is also the reason for lack of autotests for the model. Will be added once sub-compositor works properly. Reviewers: #plasma Subscribers: plasma-devel Projects: #plasma Differential Revision: https://phabricator.kde.org/D1205 --- debug_console.cpp | 226 ++++++++++++++++++++++++++++++++++++++++++++++ debug_console.h | 14 +++ debug_console.ui | 27 +++++- 3 files changed, 265 insertions(+), 2 deletions(-) diff --git a/debug_console.cpp b/debug_console.cpp index 055c64829a..d6ef0c9796 100644 --- a/debug_console.cpp +++ b/debug_console.cpp @@ -28,6 +28,9 @@ along with this program. If not, see . #include "ui_debug_console.h" // KWayland +#include +#include +#include #include // frameworks #include @@ -47,8 +50,34 @@ DebugConsole::DebugConsole() m_ui->treeView->setItemDelegate(new DebugConsoleDelegate(this)); m_ui->treeView->setModel(new DebugConsoleModel(this)); m_ui->quitButton->setIcon(QIcon::fromTheme(QStringLiteral("application-exit"))); + m_ui->windowsButton->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree"))); + m_ui->surfacesButton->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree"))); + + if (kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) { + m_ui->surfacesButton->setDisabled(true); + } connect(m_ui->quitButton, &QAbstractButton::clicked, this, &DebugConsole::deleteLater); + connect(m_ui->windowsButton, &QAbstractButton::toggled, this, + [this] (bool toggled) { + if (!toggled) { + return; + } + m_ui->surfacesButton->setChecked(false); + m_ui->treeView->model()->deleteLater(); + m_ui->treeView->setModel(new DebugConsoleModel(this)); + } + ); + connect(m_ui->surfacesButton, &QAbstractButton::toggled, this, + [this] (bool toggled) { + if (!toggled) { + return; + } + m_ui->windowsButton->setChecked(false); + m_ui->treeView->model()->deleteLater(); + m_ui->treeView->setModel(new SurfaceTreeModel(this)); + } + ); // for X11 setWindowFlags(Qt::X11BypassWindowManagerHint); @@ -514,4 +543,201 @@ Unmanaged *DebugConsoleModel::unmanaged(const QModelIndex &index) const return clientForIndex(index, m_unmanageds, s_x11UnmanagedId); } +/////////////////////////////////////// SurfaceTreeModel +SurfaceTreeModel::SurfaceTreeModel(QObject *parent) + : QAbstractItemModel(parent) +{ + // TODO: it would be nice to not have to reset the model on each change + auto reset = [this] { + beginResetModel(); + endResetModel(); + }; + using namespace KWayland::Server; + + const auto unmangeds = workspace()->unmanagedList(); + for (auto u : unmangeds) { + connect(u->surface(), &SurfaceInterface::subSurfaceTreeChanged, this, reset); + } + for (auto c : workspace()->allClientList()) { + connect(c->surface(), &SurfaceInterface::subSurfaceTreeChanged, this, reset); + } + for (auto c : workspace()->desktopList()) { + connect(c->surface(), &SurfaceInterface::subSurfaceTreeChanged, this, reset); + } + for (auto c : waylandServer()->internalClients()) { + connect(c->surface(), &SurfaceInterface::subSurfaceTreeChanged, this, reset); + } + connect(waylandServer(), &WaylandServer::shellClientAdded, this, + [this, reset] (ShellClient *c) { + connect(c->surface(), &SurfaceInterface::subSurfaceTreeChanged, this, reset); + reset(); + } + ); + connect(workspace(), &Workspace::clientAdded, this, + [this, reset] (AbstractClient *c) { + connect(c->surface(), &SurfaceInterface::subSurfaceTreeChanged, this, reset); + reset(); + } + ); + connect(workspace(), &Workspace::clientRemoved, this, reset); + connect(workspace(), &Workspace::unmanagedAdded, this, + [this, reset] (Unmanaged *u) { + connect(u->surface(), &SurfaceInterface::subSurfaceTreeChanged, this, reset); + reset(); + } + ); + connect(workspace(), &Workspace::unmanagedRemoved, this, reset); +} + +SurfaceTreeModel::~SurfaceTreeModel() = default; + +int SurfaceTreeModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return 1; +} + +int SurfaceTreeModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + using namespace KWayland::Server; + if (SurfaceInterface *surface = static_cast(parent.internalPointer())) { + const auto &children = surface->childSubSurfaces(); + return children.count(); + } + return 0; + } + // toplevel are all windows + return workspace()->allClientList().count() + + workspace()->desktopList().count() + + workspace()->unmanagedList().count() + + waylandServer()->internalClients().count(); +} + +QModelIndex SurfaceTreeModel::index(int row, int column, const QModelIndex &parent) const +{ + if (column != 0) { + // invalid column + return QModelIndex(); + } + + if (parent.isValid()) { + using namespace KWayland::Server; + if (SurfaceInterface *surface = static_cast(parent.internalPointer())) { + const auto &children = surface->childSubSurfaces(); + if (row < children.count()) { + return createIndex(row, column, children.at(row)->surface().data()); + } + } + return QModelIndex(); + } + // a window + const auto &allClients = workspace()->allClientList(); + if (row < allClients.count()) { + // references a client + return createIndex(row, column, allClients.at(row)->surface()); + } + int reference = allClients.count(); + const auto &desktopClients = workspace()->desktopList(); + if (row < reference + desktopClients.count()) { + return createIndex(row, column, desktopClients.at(row-reference)->surface()); + } + reference += desktopClients.count(); + const auto &unmanaged = workspace()->unmanagedList(); + if (row < reference + unmanaged.count()) { + return createIndex(row, column, unmanaged.at(row-reference)->surface()); + } + reference += unmanaged.count(); + const auto &internal = waylandServer()->internalClients(); + if (row < reference + internal.count()) { + return createIndex(row, column, internal.at(row-reference)->surface()); + } + // not found + return QModelIndex(); +} + +QModelIndex SurfaceTreeModel::parent(const QModelIndex &child) const +{ + using namespace KWayland::Server; + if (SurfaceInterface *surface = static_cast(child.internalPointer())) { + const auto &subsurface = surface->subSurface(); + if (subsurface.isNull()) { + // doesn't reference a subsurface, this is a top-level window + return QModelIndex(); + } + SurfaceInterface *parent = subsurface->parentSurface().data(); + if (!parent) { + // something is wrong + return QModelIndex(); + } + // is the parent a subsurface itself? + if (parent->subSurface()) { + auto grandParent = parent->subSurface()->parentSurface(); + if (grandParent.isNull()) { + // something is wrong + return QModelIndex(); + } + const auto &children = grandParent->childSubSurfaces(); + for (int row = 0; row < children.count(); row++) { + if (children.at(row).data() == parent->subSurface().data()) { + return createIndex(row, 0, parent); + } + } + return QModelIndex(); + } + // not a subsurface, thus it's a true window + int row = 0; + const auto &allClients = workspace()->allClientList(); + for (; row < allClients.count(); row++) { + if (allClients.at(row)->surface() == parent) { + return createIndex(row, 0, parent); + } + } + row = allClients.count(); + const auto &desktopClients = workspace()->desktopList(); + for (int i = 0; i < desktopClients.count(); i++) { + if (desktopClients.at(i)->surface() == parent) { + return createIndex(row + i, 0, parent); + } + } + row += desktopClients.count(); + const auto &unmanaged = workspace()->unmanagedList(); + for (int i = 0; i < unmanaged.count(); i++) { + if (unmanaged.at(i)->surface() == parent) { + return createIndex(row + i, 0, parent); + } + } + row += unmanaged.count(); + const auto &internal = waylandServer()->internalClients(); + for (int i = 0; i < internal.count(); i++) { + if (internal.at(i)->surface() == parent) { + return createIndex(row + i, 0, parent); + } + } + } + return QModelIndex(); +} + +QVariant SurfaceTreeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + using namespace KWayland::Server; + if (SurfaceInterface *surface = static_cast(index.internalPointer())) { + if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { + return QStringLiteral("%1 (%2) - %3").arg(surface->client()->executablePath()) + .arg(surface->client()->processId()) + .arg(surface->id()); + } else if (role == Qt::DecorationRole) { + if (auto buffer = surface->buffer()) { + if (buffer->shmBuffer()) { + return buffer->data().scaled(QSize(64, 64), Qt::KeepAspectRatio); + } + } + } + } + return QVariant(); +} + } diff --git a/debug_console.h b/debug_console.h index b94db41e02..d68683e08c 100644 --- a/debug_console.h +++ b/debug_console.h @@ -100,6 +100,20 @@ private: QScopedPointer m_ui; }; +class SurfaceTreeModel : public QAbstractItemModel +{ + Q_OBJECT +public: + explicit SurfaceTreeModel(QObject *parent = nullptr); + virtual ~SurfaceTreeModel(); + + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QModelIndex index(int row, int column, const QModelIndex & parent) const override; + int rowCount(const QModelIndex &parent) const override; + QModelIndex parent(const QModelIndex &child) const override; +}; + } #endif diff --git a/debug_console.ui b/debug_console.ui index 8937eca8dd..735014558f 100644 --- a/debug_console.ui +++ b/debug_console.ui @@ -17,9 +17,25 @@ - + - Quit Debug Console + Windows + + + true + + + true + + + + + + + Surfaces + + + true @@ -36,6 +52,13 @@ + + + + Quit Debug Console + + +