KWin
Loading...
Searching...
No Matches
debug_console.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "debug_console.h"
10#include "compositor.h"
12#include "core/inputdevice.h"
14#include "input_event.h"
15#include "internalwindow.h"
16#include "keyboard_input.h"
17#include "main.h"
18#include "opengl/glplatform.h"
19#include "opengl/glutils.h"
26#include "wayland/datasource.h"
27#include "wayland/display.h"
29#include "wayland/seat.h"
31#include "wayland/surface.h"
32#include "wayland_server.h"
33#include "waylandwindow.h"
34#include "workspace.h"
35#include "x11window.h"
36#include "xkb.h"
37#include <cerrno>
38
39#include "ui_debug_console.h"
40
41// frameworks
42#include <KLocalizedString>
43#include <NETWM>
44// Qt
45#include <QFutureWatcher>
46#include <QMetaProperty>
47#include <QMetaType>
48#include <QMouseEvent>
49#include <QScopeGuard>
50#include <QSortFilterProxyModel>
51#include <QtConcurrentRun>
52
53#include <wayland-server-core.h>
54
55// xkb
56#include <xkbcommon/xkbcommon.h>
57
58#include <fcntl.h>
59#include <functional>
60#include <sys/poll.h>
61#include <unistd.h>
62
63namespace KWin
64{
65
66static QString tableHeaderRow(const QString &title)
67{
68 return QStringLiteral("<tr><th colspan=\"2\">%1</th></tr>").arg(title);
69}
70
71template<typename T>
72static QString tableRow(const QString &title, const T &argument)
73{
74 return QStringLiteral("<tr><td>%1</td><td>%2</td></tr>").arg(title).arg(argument);
75}
76
77static QString timestampRow(std::chrono::microseconds timestamp)
78{
79 return tableRow(i18n("Timestamp"), std::chrono::duration_cast<std::chrono::milliseconds>(timestamp).count());
80}
81
82static QString timestampRowUsec(std::chrono::microseconds timestamp)
83{
84 return tableRow(i18n("Timestamp (µsec)"), timestamp.count());
85}
86
87static QString buttonToString(Qt::MouseButton button)
88{
89 switch (button) {
90 case Qt::LeftButton:
91 return i18nc("A mouse button", "Left");
92 case Qt::RightButton:
93 return i18nc("A mouse button", "Right");
94 case Qt::MiddleButton:
95 return i18nc("A mouse button", "Middle");
96 case Qt::BackButton:
97 return i18nc("A mouse button", "Back");
98 case Qt::ForwardButton:
99 return i18nc("A mouse button", "Forward");
100 case Qt::TaskButton:
101 return i18nc("A mouse button", "Task");
102 case Qt::ExtraButton4:
103 return i18nc("A mouse button", "Extra Button 4");
104 case Qt::ExtraButton5:
105 return i18nc("A mouse button", "Extra Button 5");
106 case Qt::ExtraButton6:
107 return i18nc("A mouse button", "Extra Button 6");
108 case Qt::ExtraButton7:
109 return i18nc("A mouse button", "Extra Button 7");
110 case Qt::ExtraButton8:
111 return i18nc("A mouse button", "Extra Button 8");
112 case Qt::ExtraButton9:
113 return i18nc("A mouse button", "Extra Button 9");
114 case Qt::ExtraButton10:
115 return i18nc("A mouse button", "Extra Button 10");
116 case Qt::ExtraButton11:
117 return i18nc("A mouse button", "Extra Button 11");
118 case Qt::ExtraButton12:
119 return i18nc("A mouse button", "Extra Button 12");
120 case Qt::ExtraButton13:
121 return i18nc("A mouse button", "Extra Button 13");
122 case Qt::ExtraButton14:
123 return i18nc("A mouse button", "Extra Button 14");
124 case Qt::ExtraButton15:
125 return i18nc("A mouse button", "Extra Button 15");
126 case Qt::ExtraButton16:
127 return i18nc("A mouse button", "Extra Button 16");
128 case Qt::ExtraButton17:
129 return i18nc("A mouse button", "Extra Button 17");
130 case Qt::ExtraButton18:
131 return i18nc("A mouse button", "Extra Button 18");
132 case Qt::ExtraButton19:
133 return i18nc("A mouse button", "Extra Button 19");
134 case Qt::ExtraButton20:
135 return i18nc("A mouse button", "Extra Button 20");
136 case Qt::ExtraButton21:
137 return i18nc("A mouse button", "Extra Button 21");
138 case Qt::ExtraButton22:
139 return i18nc("A mouse button", "Extra Button 22");
140 case Qt::ExtraButton23:
141 return i18nc("A mouse button", "Extra Button 23");
142 case Qt::ExtraButton24:
143 return i18nc("A mouse button", "Extra Button 24");
144 default:
145 return QString();
146 }
147}
148
149static QString deviceRow(InputDevice *device)
150{
151 if (!device) {
152 return tableRow(i18n("Input Device"), i18nc("The input device of the event is not known", "Unknown"));
153 }
154 return tableRow(i18n("Input Device"), QStringLiteral("%1 (%2)").arg(device->name(), device->sysName()));
155}
156
157static QString buttonsToString(Qt::MouseButtons buttons)
158{
159 QString ret;
160 for (uint i = 1; i < Qt::ExtraButton24; i = i << 1) {
161 if (buttons & i) {
162 ret.append(buttonToString(Qt::MouseButton(uint(buttons) & i)));
163 ret.append(QStringLiteral(" "));
164 }
165 };
166 return ret.trimmed();
167}
168
169static const QString s_hr = QStringLiteral("<hr/>");
170static const QString s_tableStart = QStringLiteral("<table>");
171static const QString s_tableEnd = QStringLiteral("</table>");
172
174 : InputEventSpy()
175 , m_textEdit(textEdit)
176{
177}
178
180
182{
183 QString text = s_hr;
184 const QString timestamp = timestampRow(event->timestamp());
185
186 text.append(s_tableStart);
187 switch (event->type()) {
188 case QEvent::MouseMove: {
189 text.append(tableHeaderRow(i18nc("A mouse pointer motion event", "Pointer Motion")));
190 text.append(deviceRow(event->device()));
191 text.append(timestamp);
192 text.append(timestampRowUsec(event->timestamp()));
193 if (!event->delta().isNull()) {
194 text.append(tableRow(i18nc("The relative mouse movement", "Delta"),
195 QStringLiteral("%1/%2").arg(event->delta().x()).arg(event->delta().y())));
196 }
197 if (!event->deltaUnaccelerated().isNull()) {
198 text.append(tableRow(i18nc("The relative mouse movement", "Delta (not accelerated)"),
199 QStringLiteral("%1/%2").arg(event->deltaUnaccelerated().x()).arg(event->deltaUnaccelerated().y())));
200 }
201 text.append(tableRow(i18nc("The global mouse pointer position", "Global Position"), QStringLiteral("%1/%2").arg(event->pos().x()).arg(event->pos().y())));
202 break;
203 }
204 case QEvent::MouseButtonPress:
205 text.append(tableHeaderRow(i18nc("A mouse pointer button press event", "Pointer Button Press")));
206 text.append(deviceRow(event->device()));
207 text.append(timestamp);
208 text.append(tableRow(i18nc("A button in a mouse press/release event", "Button"), buttonToString(event->button())));
209 text.append(tableRow(i18nc("A button in a mouse press/release event", "Native Button code"), event->nativeButton()));
210 text.append(tableRow(i18nc("All currently pressed buttons in a mouse press/release event", "Pressed Buttons"), buttonsToString(event->buttons())));
211 break;
212 case QEvent::MouseButtonRelease:
213 text.append(tableHeaderRow(i18nc("A mouse pointer button release event", "Pointer Button Release")));
214 text.append(deviceRow(event->device()));
215 text.append(timestamp);
216 text.append(tableRow(i18nc("A button in a mouse press/release event", "Button"), buttonToString(event->button())));
217 text.append(tableRow(i18nc("A button in a mouse press/release event", "Native Button code"), event->nativeButton()));
218 text.append(tableRow(i18nc("All currently pressed buttons in a mouse press/release event", "Pressed Buttons"), buttonsToString(event->buttons())));
219 break;
220 default:
221 break;
222 }
223 text.append(s_tableEnd);
224
225 m_textEdit->insertHtml(text);
226 m_textEdit->ensureCursorVisible();
227}
228
230{
231 QString text = s_hr;
232 text.append(s_tableStart);
233 text.append(tableHeaderRow(i18nc("A mouse pointer axis (wheel) event", "Pointer Axis")));
234 text.append(deviceRow(event->device()));
235 text.append(timestampRow(event->timestamp()));
236 const Qt::Orientation orientation = event->angleDelta().x() == 0 ? Qt::Vertical : Qt::Horizontal;
237 text.append(tableRow(i18nc("The orientation of a pointer axis event", "Orientation"),
238 orientation == Qt::Horizontal ? i18nc("An orientation of a pointer axis event", "Horizontal")
239 : i18nc("An orientation of a pointer axis event", "Vertical")));
240 text.append(tableRow(i18nc("The angle delta of a pointer axis event", "Delta"),
241 orientation == Qt::Horizontal ? event->angleDelta().x() : event->angleDelta().y()));
242 text.append(s_tableEnd);
243
244 m_textEdit->insertHtml(text);
245 m_textEdit->ensureCursorVisible();
246}
247
249{
250 QString text = s_hr;
251 text.append(s_tableStart);
252
253 switch (event->type()) {
254 case QEvent::KeyPress:
255 text.append(tableHeaderRow(i18nc("A key press event", "Key Press")));
256 break;
257 case QEvent::KeyRelease:
258 text.append(tableHeaderRow(i18nc("A key release event", "Key Release")));
259 break;
260 default:
261 break;
262 }
263 text.append(deviceRow(event->device()));
264 auto modifiersToString = [event] {
265 QString ret;
266 if (event->modifiers().testFlag(Qt::ShiftModifier)) {
267 ret.append(i18nc("A keyboard modifier", "Shift"));
268 ret.append(QStringLiteral(" "));
269 }
270 if (event->modifiers().testFlag(Qt::ControlModifier)) {
271 ret.append(i18nc("A keyboard modifier", "Control"));
272 ret.append(QStringLiteral(" "));
273 }
274 if (event->modifiers().testFlag(Qt::AltModifier)) {
275 ret.append(i18nc("A keyboard modifier", "Alt"));
276 ret.append(QStringLiteral(" "));
277 }
278 if (event->modifiers().testFlag(Qt::MetaModifier)) {
279 ret.append(i18nc("A keyboard modifier", "Meta"));
280 ret.append(QStringLiteral(" "));
281 }
282 if (event->modifiers().testFlag(Qt::KeypadModifier)) {
283 ret.append(i18nc("A keyboard modifier", "Keypad"));
284 ret.append(QStringLiteral(" "));
285 }
286 if (event->modifiers().testFlag(Qt::GroupSwitchModifier)) {
287 ret.append(i18nc("A keyboard modifier", "Group-switch"));
288 ret.append(QStringLiteral(" "));
289 }
290 return ret;
291 };
292 text.append(timestampRow(event->timestamp()));
293 text.append(tableRow(i18nc("Whether the event is an automatic key repeat", "Repeat"), event->isAutoRepeat()));
294
295 const auto keyMetaObject = Qt::qt_getEnumMetaObject(Qt::Key());
296 const auto enumerator = keyMetaObject->enumerator(keyMetaObject->indexOfEnumerator("Key"));
297 text.append(tableRow(i18nc("The code as read from the input device", "Scan code"), event->nativeScanCode()));
298 text.append(tableRow(i18nc("Key according to Qt", "Qt::Key code"),
299 enumerator.valueToKey(event->key())));
300 text.append(tableRow(i18nc("The translated code to an Xkb symbol", "Xkb symbol"), event->nativeVirtualKey()));
301 text.append(tableRow(i18nc("The translated code interpreted as text", "Utf8"), event->text()));
302 text.append(tableRow(i18nc("The currently active modifiers", "Modifiers"), modifiersToString()));
303
304 text.append(s_tableEnd);
305
306 m_textEdit->insertHtml(text);
307 m_textEdit->ensureCursorVisible();
308}
309
310void DebugConsoleFilter::touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time)
311{
312 QString text = s_hr;
313 text.append(s_tableStart);
314 text.append(tableHeaderRow(i18nc("A touch down event", "Touch down")));
315 text.append(timestampRow(time));
316 text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id));
317 text.append(tableRow(i18nc("The global position of the touch point", "Global position"),
318 QStringLiteral("%1/%2").arg(pos.x()).arg(pos.y())));
319 text.append(s_tableEnd);
320
321 m_textEdit->insertHtml(text);
322 m_textEdit->ensureCursorVisible();
323}
324
325void DebugConsoleFilter::touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time)
326{
327 QString text = s_hr;
328 text.append(s_tableStart);
329 text.append(tableHeaderRow(i18nc("A touch motion event", "Touch Motion")));
330 text.append(timestampRow(time));
331 text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id));
332 text.append(tableRow(i18nc("The global position of the touch point", "Global position"),
333 QStringLiteral("%1/%2").arg(pos.x()).arg(pos.y())));
334 text.append(s_tableEnd);
335
336 m_textEdit->insertHtml(text);
337 m_textEdit->ensureCursorVisible();
338}
339
340void DebugConsoleFilter::touchUp(qint32 id, std::chrono::microseconds time)
341{
342 QString text = s_hr;
343 text.append(s_tableStart);
344 text.append(tableHeaderRow(i18nc("A touch up event", "Touch Up")));
345 text.append(timestampRow(time));
346 text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id));
347 text.append(s_tableEnd);
348
349 m_textEdit->insertHtml(text);
350 m_textEdit->ensureCursorVisible();
351}
352
353void DebugConsoleFilter::pinchGestureBegin(int fingerCount, std::chrono::microseconds time)
354{
355 QString text = s_hr;
356 text.append(s_tableStart);
357 text.append(tableHeaderRow(i18nc("A pinch gesture is started", "Pinch start")));
358 text.append(timestampRow(time));
359 text.append(tableRow(i18nc("Number of fingers in this pinch gesture", "Finger count"), fingerCount));
360 text.append(s_tableEnd);
361
362 m_textEdit->insertHtml(text);
363 m_textEdit->ensureCursorVisible();
364}
365
366void DebugConsoleFilter::pinchGestureUpdate(qreal scale, qreal angleDelta, const QPointF &delta, std::chrono::microseconds time)
367{
368 QString text = s_hr;
369 text.append(s_tableStart);
370 text.append(tableHeaderRow(i18nc("A pinch gesture is updated", "Pinch update")));
371 text.append(timestampRow(time));
372 text.append(tableRow(i18nc("Current scale in pinch gesture", "Scale"), scale));
373 text.append(tableRow(i18nc("Current angle in pinch gesture", "Angle delta"), angleDelta));
374 text.append(tableRow(i18nc("Current delta in pinch gesture", "Delta x"), delta.x()));
375 text.append(tableRow(i18nc("Current delta in pinch gesture", "Delta y"), delta.y()));
376 text.append(s_tableEnd);
377
378 m_textEdit->insertHtml(text);
379 m_textEdit->ensureCursorVisible();
380}
381
382void DebugConsoleFilter::pinchGestureEnd(std::chrono::microseconds time)
383{
384 QString text = s_hr;
385 text.append(s_tableStart);
386 text.append(tableHeaderRow(i18nc("A pinch gesture ended", "Pinch end")));
387 text.append(timestampRow(time));
388 text.append(s_tableEnd);
389
390 m_textEdit->insertHtml(text);
391 m_textEdit->ensureCursorVisible();
392}
393
394void DebugConsoleFilter::pinchGestureCancelled(std::chrono::microseconds time)
395{
396 QString text = s_hr;
397 text.append(s_tableStart);
398 text.append(tableHeaderRow(i18nc("A pinch gesture got cancelled", "Pinch cancelled")));
399 text.append(timestampRow(time));
400 text.append(s_tableEnd);
401
402 m_textEdit->insertHtml(text);
403 m_textEdit->ensureCursorVisible();
404}
405
406void DebugConsoleFilter::swipeGestureBegin(int fingerCount, std::chrono::microseconds time)
407{
408 QString text = s_hr;
409 text.append(s_tableStart);
410 text.append(tableHeaderRow(i18nc("A swipe gesture is started", "Swipe start")));
411 text.append(timestampRow(time));
412 text.append(tableRow(i18nc("Number of fingers in this swipe gesture", "Finger count"), fingerCount));
413 text.append(s_tableEnd);
414
415 m_textEdit->insertHtml(text);
416 m_textEdit->ensureCursorVisible();
417}
418
419void DebugConsoleFilter::swipeGestureUpdate(const QPointF &delta, std::chrono::microseconds time)
420{
421 QString text = s_hr;
422 text.append(s_tableStart);
423 text.append(tableHeaderRow(i18nc("A swipe gesture is updated", "Swipe update")));
424 text.append(timestampRow(time));
425 text.append(tableRow(i18nc("Current delta in swipe gesture", "Delta x"), delta.x()));
426 text.append(tableRow(i18nc("Current delta in swipe gesture", "Delta y"), delta.y()));
427 text.append(s_tableEnd);
428
429 m_textEdit->insertHtml(text);
430 m_textEdit->ensureCursorVisible();
431}
432
433void DebugConsoleFilter::swipeGestureEnd(std::chrono::microseconds time)
434{
435 QString text = s_hr;
436 text.append(s_tableStart);
437 text.append(tableHeaderRow(i18nc("A swipe gesture ended", "Swipe end")));
438 text.append(timestampRow(time));
439 text.append(s_tableEnd);
440
441 m_textEdit->insertHtml(text);
442 m_textEdit->ensureCursorVisible();
443}
444
445void DebugConsoleFilter::swipeGestureCancelled(std::chrono::microseconds time)
446{
447 QString text = s_hr;
448 text.append(s_tableStart);
449 text.append(tableHeaderRow(i18nc("A swipe gesture got cancelled", "Swipe cancelled")));
450 text.append(timestampRow(time));
451 text.append(s_tableEnd);
452
453 m_textEdit->insertHtml(text);
454 m_textEdit->ensureCursorVisible();
455}
456
458{
459 QString text = s_hr;
460 text.append(s_tableStart);
461 text.append(tableHeaderRow(i18nc("A hardware switch (e.g. notebook lid) got toggled", "Switch toggled")));
462 text.append(timestampRow(event->timestamp()));
463 text.append(timestampRowUsec(event->timestamp()));
464 text.append(deviceRow(event->device()));
465 QString switchName;
466 if (event->device()->isLidSwitch()) {
467 switchName = i18nc("Name of a hardware switch", "Notebook lid");
468 } else if (event->device()->isTabletModeSwitch()) {
469 switchName = i18nc("Name of a hardware switch", "Tablet mode");
470 }
471 text.append(tableRow(i18nc("A hardware switch", "Switch"), switchName));
472 QString switchState;
473 switch (event->state()) {
475 switchState = i18nc("The hardware switch got turned off", "Off");
476 break;
478 switchState = i18nc("The hardware switch got turned on", "On");
479 break;
480 default:
481 Q_UNREACHABLE();
482 }
483 text.append(tableRow(i18nc("State of a hardware switch (on/off)", "State"), switchState));
484 text.append(s_tableEnd);
485
486 m_textEdit->insertHtml(text);
487 m_textEdit->ensureCursorVisible();
488}
489
491{
492 QString typeString;
493 {
494 QDebug d(&typeString);
495 d << event->type();
496 }
497
498 QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Tool"))
499 + tableRow(i18n("EventType"), typeString)
500 + tableRow(i18n("Position"),
501 QStringLiteral("%1,%2").arg(event->pos().x()).arg(event->pos().y()))
502 + tableRow(i18n("Tilt"),
503 QStringLiteral("%1,%2").arg(event->xTilt()).arg(event->yTilt()))
504 + tableRow(i18n("Rotation"), QString::number(event->rotation()))
505 + tableRow(i18n("Pressure"), QString::number(event->pressure()))
506 + tableRow(i18n("Buttons"), QString::number(event->buttons()))
507 + tableRow(i18n("Modifiers"), QString::number(event->modifiers()))
508 + s_tableEnd;
509
510 m_textEdit->insertHtml(text);
511 m_textEdit->ensureCursorVisible();
512}
513
514void DebugConsoleFilter::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId, std::chrono::microseconds time)
515{
516 QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Tool Button"))
517 + tableRow(i18n("Button"), button)
518 + tableRow(i18n("Pressed"), pressed)
519 + tableRow(i18n("Tablet"), qHash(tabletToolId.m_deviceGroupData))
520 + timestampRow(time)
521 + s_tableEnd;
522
523 m_textEdit->insertHtml(text);
524 m_textEdit->ensureCursorVisible();
525}
526
527void DebugConsoleFilter::tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId, std::chrono::microseconds time)
528{
529 QString text = s_hr + s_tableStart
530 + tableHeaderRow(i18n("Tablet Pad Button"))
531 + tableRow(i18n("Button"), button)
532 + tableRow(i18n("Pressed"), pressed)
533 + tableRow(i18n("Tablet"), qHash(tabletPadId.data))
534 + timestampRow(time)
535 + s_tableEnd;
536
537 m_textEdit->insertHtml(text);
538 m_textEdit->ensureCursorVisible();
539}
540
541void DebugConsoleFilter::tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time)
542{
543 QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Pad Strip"))
544 + tableRow(i18n("Number"), number)
545 + tableRow(i18n("Position"), position)
546 + tableRow(i18n("isFinger"), isFinger)
547 + tableRow(i18n("Tablet"), qHash(tabletPadId.data))
548 + timestampRow(time)
549 + s_tableEnd;
550
551 m_textEdit->insertHtml(text);
552 m_textEdit->ensureCursorVisible();
553}
554
555void DebugConsoleFilter::tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time)
556{
557 QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Pad Ring"))
558 + tableRow(i18n("Number"), number)
559 + tableRow(i18n("Position"), position)
560 + tableRow(i18n("isFinger"), isFinger)
561 + tableRow(i18n("Tablet"), qHash(tabletPadId.data))
562 + timestampRow(time)
563 + s_tableEnd;
564
565 m_textEdit->insertHtml(text);
566 m_textEdit->ensureCursorVisible();
567}
568
569static QString sourceString(const AbstractDataSource *const source)
570{
571 if (!source) {
572 return QString();
573 }
574
575 if (!source->client()) {
576 return QStringLiteral("XWayland source");
577 }
578
579 const QString executable = waylandServer()->display()->getConnection(source->client())->executablePath();
580
581 if (auto dataSource = qobject_cast<const DataSourceInterface *const>(source)) {
582 return QStringLiteral("wl_data_source@%1 of %2").arg(wl_resource_get_id(dataSource->resource())).arg(executable);
583 } else if (qobject_cast<const PrimarySelectionSourceV1Interface *const>(source)) {
584 return QStringLiteral("zwp_primary_selection_source_v1 of %2").arg(executable);
585 } else if (qobject_cast<const DataControlSourceV1Interface *const>(source)) {
586 return QStringLiteral("data control by %1").arg(executable);
587 }
588 return QStringLiteral("unknown source of").arg(executable);
589}
590
592 : QWidget()
593 , m_ui(new Ui::DebugConsole)
594{
595 setAttribute(Qt::WA_ShowWithoutActivating);
596 m_ui->setupUi(this);
597
598 auto windowsModel = new DebugConsoleModel(this);
599 QSortFilterProxyModel *proxyWindowsModel = new QSortFilterProxyModel(this);
600 proxyWindowsModel->setSourceModel(windowsModel);
601 m_ui->windowsView->setModel(proxyWindowsModel);
602 m_ui->windowsView->sortByColumn(0, Qt::AscendingOrder);
603 m_ui->windowsView->header()->setSortIndicatorShown(true);
604 m_ui->windowsView->setItemDelegate(new DebugConsoleDelegate(this));
605
606 m_ui->surfacesView->setModel(new SurfaceTreeModel(this));
607 m_ui->clipboardContent->setModel(new DataSourceModel(this));
608 m_ui->primaryContent->setModel(new DataSourceModel(this));
609 m_ui->inputDevicesView->setModel(new InputDeviceModel(this));
610 m_ui->inputDevicesView->setItemDelegate(new DebugConsoleDelegate(this));
611 m_ui->quitButton->setIcon(QIcon::fromTheme(QStringLiteral("application-exit")));
612 m_ui->tabWidget->setTabIcon(0, QIcon::fromTheme(QStringLiteral("view-list-tree")));
613 m_ui->tabWidget->setTabIcon(1, QIcon::fromTheme(QStringLiteral("view-list-tree")));
614
615 if (kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) {
616 m_ui->tabWidget->setTabEnabled(1, false);
617 m_ui->tabWidget->setTabEnabled(2, false);
618 m_ui->tabWidget->setTabEnabled(6, false);
619 setWindowFlags(Qt::X11BypassWindowManagerHint);
620 }
621
622 connect(m_ui->quitButton, &QAbstractButton::clicked, this, &DebugConsole::deleteLater);
623 connect(m_ui->tabWidget, &QTabWidget::currentChanged, this, [this](int index) {
624 // delay creation of input event filter until the tab is selected
625 if (index == 2 && !m_inputFilter) {
626 m_inputFilter = std::make_unique<DebugConsoleFilter>(m_ui->inputTextEdit);
627 input()->installInputEventSpy(m_inputFilter.get());
628 }
629 if (index == 5) {
630 updateKeyboardTab();
631 connect(input(), &InputRedirection::keyStateChanged, this, &DebugConsole::updateKeyboardTab);
632 }
633 if (index == 6) {
634 static_cast<DataSourceModel *>(m_ui->clipboardContent->model())->setSource(waylandServer()->seat()->selection());
635 m_ui->clipboardSource->setText(sourceString(waylandServer()->seat()->selection()));
636 connect(waylandServer()->seat(), &SeatInterface::selectionChanged, this, [this](AbstractDataSource *source) {
637 static_cast<DataSourceModel *>(m_ui->clipboardContent->model())->setSource(source);
638 m_ui->clipboardSource->setText(sourceString(source));
639 });
640 static_cast<DataSourceModel *>(m_ui->primaryContent->model())->setSource(waylandServer()->seat()->primarySelection());
641 m_ui->primarySource->setText(sourceString(waylandServer()->seat()->primarySelection()));
642 connect(waylandServer()->seat(), &SeatInterface::primarySelectionChanged, this, [this](AbstractDataSource *source) {
643 static_cast<DataSourceModel *>(m_ui->primaryContent->model())->setSource(source);
644 m_ui->primarySource->setText(sourceString(source));
645 });
646 }
647 });
648
649 initGLTab();
650}
651
652DebugConsole::~DebugConsole() = default;
653
654void DebugConsole::initGLTab()
655{
656 if (!effects || !effects->isOpenGLCompositing()) {
657 m_ui->noOpenGLLabel->setVisible(true);
658 m_ui->glInfoScrollArea->setVisible(false);
659 return;
660 }
661 GLPlatform *gl = GLPlatform::instance();
662 m_ui->noOpenGLLabel->setVisible(false);
663 m_ui->glInfoScrollArea->setVisible(true);
664 m_ui->glVendorStringLabel->setText(QString::fromLocal8Bit(gl->glVendorString()));
665 m_ui->glRendererStringLabel->setText(QString::fromLocal8Bit(gl->glRendererString()));
666 m_ui->glVersionStringLabel->setText(QString::fromLocal8Bit(gl->glVersionString()));
667 m_ui->glslVersionStringLabel->setText(QString::fromLocal8Bit(gl->glShadingLanguageVersionString()));
668 m_ui->glDriverLabel->setText(GLPlatform::driverToString(gl->driver()));
669 m_ui->glGPULabel->setText(GLPlatform::chipClassToString(gl->chipClass()));
670 m_ui->glVersionLabel->setText(gl->glVersion().toString());
671 m_ui->glslLabel->setText(gl->glslVersion().toString());
672
673 auto extensionsString = [](const auto &extensions) {
674 QString text = QStringLiteral("<ul>");
675 for (auto extension : extensions) {
676 text.append(QStringLiteral("<li>%1</li>").arg(QString::fromLocal8Bit(extension)));
677 }
678 text.append(QStringLiteral("</ul>"));
679 return text;
680 };
681
682 const OpenGLBackend *backend = static_cast<OpenGLBackend *>(Compositor::self()->backend());
683 m_ui->platformExtensionsLabel->setText(extensionsString(backend->extensions()));
684 m_ui->openGLExtensionsLabel->setText(extensionsString(openGLExtensions()));
685}
686
687template<typename T>
688QString keymapComponentToString(xkb_keymap *map, const T &count, std::function<const char *(xkb_keymap *, T)> f)
689{
690 QString text = QStringLiteral("<ul>");
691 for (T i = 0; i < count; i++) {
692 text.append(QStringLiteral("<li>%1</li>").arg(QString::fromLocal8Bit(f(map, i))));
693 }
694 text.append(QStringLiteral("</ul>"));
695 return text;
696}
697
698template<typename T>
699QString stateActiveComponents(xkb_state *state, const T &count, std::function<int(xkb_state *, T)> f, std::function<const char *(xkb_keymap *, T)> name)
700{
701 QString text = QStringLiteral("<ul>");
702 xkb_keymap *map = xkb_state_get_keymap(state);
703 for (T i = 0; i < count; i++) {
704 if (f(state, i) == 1) {
705 text.append(QStringLiteral("<li>%1</li>").arg(QString::fromLocal8Bit(name(map, i))));
706 }
707 }
708 text.append(QStringLiteral("</ul>"));
709 return text;
710}
711
712void DebugConsole::updateKeyboardTab()
713{
714 auto xkb = input()->keyboard()->xkb();
715 xkb_keymap *map = xkb->keymap();
716 xkb_state *state = xkb->state();
717 m_ui->layoutsLabel->setText(keymapComponentToString<xkb_layout_index_t>(map, xkb_keymap_num_layouts(map), &xkb_keymap_layout_get_name));
718 m_ui->currentLayoutLabel->setText(xkb_keymap_layout_get_name(map, xkb->currentLayout()));
719 m_ui->modifiersLabel->setText(keymapComponentToString<xkb_mod_index_t>(map, xkb_keymap_num_mods(map), &xkb_keymap_mod_get_name));
720 m_ui->ledsLabel->setText(keymapComponentToString<xkb_led_index_t>(map, xkb_keymap_num_leds(map), &xkb_keymap_led_get_name));
721 m_ui->activeLedsLabel->setText(stateActiveComponents<xkb_led_index_t>(state, xkb_keymap_num_leds(map), &xkb_state_led_index_is_active, &xkb_keymap_led_get_name));
722
723 using namespace std::placeholders;
724 auto modActive = std::bind(xkb_state_mod_index_is_active, _1, _2, XKB_STATE_MODS_EFFECTIVE);
725 m_ui->activeModifiersLabel->setText(stateActiveComponents<xkb_mod_index_t>(state, xkb_keymap_num_mods(map), modActive, &xkb_keymap_mod_get_name));
726}
727
728void DebugConsole::showEvent(QShowEvent *event)
729{
730 QWidget::showEvent(event);
731
732 // delay the connection to the show event as in ctor the windowHandle returns null
733 connect(windowHandle(), &QWindow::visibleChanged, this, [this](bool visible) {
734 if (visible) {
735 // ignore
736 return;
737 }
738 deleteLater();
739 });
740}
741
742DebugConsoleDelegate::DebugConsoleDelegate(QObject *parent)
743 : QStyledItemDelegate(parent)
744{
745}
746
748
749QString DebugConsoleDelegate::displayText(const QVariant &value, const QLocale &locale) const
750{
751 switch (value.userType()) {
752 case QMetaType::QPoint: {
753 const QPoint p = value.toPoint();
754 return QStringLiteral("%1,%2").arg(p.x()).arg(p.y());
755 }
756 case QMetaType::QPointF: {
757 const QPointF p = value.toPointF();
758 return QStringLiteral("%1,%2").arg(p.x()).arg(p.y());
759 }
760 case QMetaType::QSize: {
761 const QSize s = value.toSize();
762 return QStringLiteral("%1x%2").arg(s.width()).arg(s.height());
763 }
764 case QMetaType::QSizeF: {
765 const QSizeF s = value.toSizeF();
766 return QStringLiteral("%1x%2").arg(s.width()).arg(s.height());
767 }
768 case QMetaType::QRect: {
769 const QRect r = value.toRect();
770 return QStringLiteral("%1,%2 %3x%4").arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());
771 }
772 case QMetaType::QRectF: {
773 const QRectF r = value.toRectF();
774 return QStringLiteral("%1,%2 %3x%4").arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());
775 }
776 default:
777 if (value.userType() == qMetaTypeId<KWin::SurfaceInterface *>()) {
778 if (auto s = value.value<KWin::SurfaceInterface *>()) {
779 return QStringLiteral("KWin::SurfaceInterface(0x%1)").arg(qulonglong(s), 0, 16);
780 } else {
781 return QStringLiteral("nullptr");
782 }
783 }
784 if (value.userType() == qMetaTypeId<KWin::Window *>()) {
785 if (auto w = value.value<KWin::Window *>()) {
786 return w->caption() + QLatin1Char(' ') + QString::fromUtf8(w->metaObject()->className());
787 } else {
788 return QStringLiteral("nullptr");
789 }
790 }
791 if (value.userType() == qMetaTypeId<Qt::MouseButtons>()) {
792 const auto buttons = value.value<Qt::MouseButtons>();
793 if (buttons == Qt::NoButton) {
794 return i18n("No Mouse Buttons");
795 }
796 QStringList list;
797 if (buttons.testFlag(Qt::LeftButton)) {
798 list << i18nc("Mouse Button", "left");
799 }
800 if (buttons.testFlag(Qt::RightButton)) {
801 list << i18nc("Mouse Button", "right");
802 }
803 if (buttons.testFlag(Qt::MiddleButton)) {
804 list << i18nc("Mouse Button", "middle");
805 }
806 if (buttons.testFlag(Qt::BackButton)) {
807 list << i18nc("Mouse Button", "back");
808 }
809 if (buttons.testFlag(Qt::ForwardButton)) {
810 list << i18nc("Mouse Button", "forward");
811 }
812 if (buttons.testFlag(Qt::ExtraButton1)) {
813 list << i18nc("Mouse Button", "extra 1");
814 }
815 if (buttons.testFlag(Qt::ExtraButton2)) {
816 list << i18nc("Mouse Button", "extra 2");
817 }
818 if (buttons.testFlag(Qt::ExtraButton3)) {
819 list << i18nc("Mouse Button", "extra 3");
820 }
821 if (buttons.testFlag(Qt::ExtraButton4)) {
822 list << i18nc("Mouse Button", "extra 4");
823 }
824 if (buttons.testFlag(Qt::ExtraButton5)) {
825 list << i18nc("Mouse Button", "extra 5");
826 }
827 if (buttons.testFlag(Qt::ExtraButton6)) {
828 list << i18nc("Mouse Button", "extra 6");
829 }
830 if (buttons.testFlag(Qt::ExtraButton7)) {
831 list << i18nc("Mouse Button", "extra 7");
832 }
833 if (buttons.testFlag(Qt::ExtraButton8)) {
834 list << i18nc("Mouse Button", "extra 8");
835 }
836 if (buttons.testFlag(Qt::ExtraButton9)) {
837 list << i18nc("Mouse Button", "extra 9");
838 }
839 if (buttons.testFlag(Qt::ExtraButton10)) {
840 list << i18nc("Mouse Button", "extra 10");
841 }
842 if (buttons.testFlag(Qt::ExtraButton11)) {
843 list << i18nc("Mouse Button", "extra 11");
844 }
845 if (buttons.testFlag(Qt::ExtraButton12)) {
846 list << i18nc("Mouse Button", "extra 12");
847 }
848 if (buttons.testFlag(Qt::ExtraButton13)) {
849 list << i18nc("Mouse Button", "extra 13");
850 }
851 if (buttons.testFlag(Qt::ExtraButton14)) {
852 list << i18nc("Mouse Button", "extra 14");
853 }
854 if (buttons.testFlag(Qt::ExtraButton15)) {
855 list << i18nc("Mouse Button", "extra 15");
856 }
857 if (buttons.testFlag(Qt::ExtraButton16)) {
858 list << i18nc("Mouse Button", "extra 16");
859 }
860 if (buttons.testFlag(Qt::ExtraButton17)) {
861 list << i18nc("Mouse Button", "extra 17");
862 }
863 if (buttons.testFlag(Qt::ExtraButton18)) {
864 list << i18nc("Mouse Button", "extra 18");
865 }
866 if (buttons.testFlag(Qt::ExtraButton19)) {
867 list << i18nc("Mouse Button", "extra 19");
868 }
869 if (buttons.testFlag(Qt::ExtraButton20)) {
870 list << i18nc("Mouse Button", "extra 20");
871 }
872 if (buttons.testFlag(Qt::ExtraButton21)) {
873 list << i18nc("Mouse Button", "extra 21");
874 }
875 if (buttons.testFlag(Qt::ExtraButton22)) {
876 list << i18nc("Mouse Button", "extra 22");
877 }
878 if (buttons.testFlag(Qt::ExtraButton23)) {
879 list << i18nc("Mouse Button", "extra 23");
880 }
881 if (buttons.testFlag(Qt::ExtraButton24)) {
882 list << i18nc("Mouse Button", "extra 24");
883 }
884 if (buttons.testFlag(Qt::TaskButton)) {
885 list << i18nc("Mouse Button", "task");
886 }
887 return list.join(QStringLiteral(", "));
888 }
889 break;
890 }
891 return QStyledItemDelegate::displayText(value, locale);
892}
893
894static const int s_x11WindowId = 1;
895static const int s_x11UnmanagedId = 2;
896static const int s_waylandWindowId = 3;
897static const int s_workspaceInternalId = 4;
898static const quint32 s_propertyBitMask = 0xFFFF0000;
899static const quint32 s_windowBitMask = 0x0000FFFF;
900static const quint32 s_idDistance = 10000;
901
902template<class T>
903void DebugConsoleModel::add(int parentRow, QList<T *> &windows, T *window)
904{
905 beginInsertRows(index(parentRow, 0, QModelIndex()), windows.count(), windows.count());
906 windows.append(window);
907 endInsertRows();
908}
909
910template<class T>
911void DebugConsoleModel::remove(int parentRow, QList<T *> &windows, T *window)
912{
913 const int remove = windows.indexOf(window);
914 if (remove == -1) {
915 return;
916 }
917 beginRemoveRows(index(parentRow, 0, QModelIndex()), remove, remove);
918 windows.removeAt(remove);
919 endRemoveRows();
920}
921
923 : QAbstractItemModel(parent)
924{
925 const auto windows = workspace()->windows();
926 for (auto window : windows) {
927 handleWindowAdded(window);
928 }
929 connect(workspace(), &Workspace::windowAdded, this, &DebugConsoleModel::handleWindowAdded);
930 connect(workspace(), &Workspace::windowRemoved, this, &DebugConsoleModel::handleWindowRemoved);
931}
932
933void DebugConsoleModel::handleWindowAdded(Window *window)
934{
935 if (auto x11 = qobject_cast<X11Window *>(window)) {
936 if (x11->isUnmanaged()) {
937 add(s_x11UnmanagedId - 1, m_unmanageds, x11);
938 } else {
939 add(s_x11WindowId - 1, m_x11Windows, x11);
940 }
941 return;
942 }
943
944 if (auto wayland = qobject_cast<WaylandWindow *>(window)) {
945 add(s_waylandWindowId - 1, m_waylandWindows, wayland);
946 return;
947 }
948
949 if (auto internal = qobject_cast<InternalWindow *>(window)) {
950 add(s_workspaceInternalId - 1, m_internalWindows, internal);
951 return;
952 }
953}
954
955void DebugConsoleModel::handleWindowRemoved(Window *window)
956{
957 if (auto x11 = qobject_cast<X11Window *>(window)) {
958 if (x11->isUnmanaged()) {
959 remove(s_x11UnmanagedId - 1, m_unmanageds, x11);
960 } else {
961 remove(s_x11WindowId - 1, m_x11Windows, x11);
962 }
963 return;
964 }
965
966 if (auto wayland = qobject_cast<WaylandWindow *>(window)) {
967 remove(s_waylandWindowId - 1, m_waylandWindows, wayland);
968 return;
969 }
970
971 if (auto internal = qobject_cast<InternalWindow *>(window)) {
972 remove(s_workspaceInternalId - 1, m_internalWindows, internal);
973 return;
974 }
975}
976
978
979int DebugConsoleModel::columnCount(const QModelIndex &parent) const
980{
981 return 2;
982}
983
984int DebugConsoleModel::topLevelRowCount() const
985{
986 return kwinApp()->shouldUseWaylandForCompositing() ? 4 : 2;
987}
988
989template<class T>
990int DebugConsoleModel::propertyCount(const QModelIndex &parent, T *(DebugConsoleModel::*filter)(const QModelIndex &) const) const
991{
992 if (T *t = (this->*filter)(parent)) {
993 return t->metaObject()->propertyCount();
994 }
995 return 0;
996}
997
998int DebugConsoleModel::rowCount(const QModelIndex &parent) const
999{
1000 if (!parent.isValid()) {
1001 return topLevelRowCount();
1002 }
1003
1004 switch (parent.internalId()) {
1005 case s_x11WindowId:
1006 return m_x11Windows.count();
1007 case s_x11UnmanagedId:
1008 return m_unmanageds.count();
1009 case s_waylandWindowId:
1010 return m_waylandWindows.count();
1011 case s_workspaceInternalId:
1012 return m_internalWindows.count();
1013 default:
1014 break;
1015 }
1016
1017 if (parent.internalId() & s_propertyBitMask) {
1018 // properties do not have children
1019 return 0;
1020 }
1021
1022 if (parent.internalId() < s_idDistance * (s_x11WindowId + 1)) {
1023 return propertyCount(parent, &DebugConsoleModel::x11Window);
1024 } else if (parent.internalId() < s_idDistance * (s_x11UnmanagedId + 1)) {
1025 return propertyCount(parent, &DebugConsoleModel::unmanaged);
1026 } else if (parent.internalId() < s_idDistance * (s_waylandWindowId + 1)) {
1027 return propertyCount(parent, &DebugConsoleModel::waylandWindow);
1028 } else if (parent.internalId() < s_idDistance * (s_workspaceInternalId + 1)) {
1029 return propertyCount(parent, &DebugConsoleModel::internalWindow);
1030 }
1031
1032 return 0;
1033}
1034
1035template<class T>
1036QModelIndex DebugConsoleModel::indexForWindow(int row, int column, const QList<T *> &windows, int id) const
1037{
1038 if (column != 0) {
1039 return QModelIndex();
1040 }
1041 if (row >= windows.count()) {
1042 return QModelIndex();
1043 }
1044 return createIndex(row, column, s_idDistance * id + row);
1045}
1046
1047template<class T>
1048QModelIndex DebugConsoleModel::indexForProperty(int row, int column, const QModelIndex &parent, T *(DebugConsoleModel::*filter)(const QModelIndex &) const) const
1049{
1050 if (T *t = (this->*filter)(parent)) {
1051 if (row >= t->metaObject()->propertyCount()) {
1052 return QModelIndex();
1053 }
1054 return createIndex(row, column, quint32(row + 1) << 16 | parent.internalId());
1055 }
1056 return QModelIndex();
1057}
1058
1059QModelIndex DebugConsoleModel::index(int row, int column, const QModelIndex &parent) const
1060{
1061 if (!parent.isValid()) {
1062 // index for a top level item
1063 if (column != 0 || row >= topLevelRowCount()) {
1064 return QModelIndex();
1065 }
1066 return createIndex(row, column, row + 1);
1067 }
1068 if (column >= 2) {
1069 // max of 2 columns
1070 return QModelIndex();
1071 }
1072 // index for a window (second level)
1073 switch (parent.internalId()) {
1074 case s_x11WindowId:
1075 return indexForWindow(row, column, m_x11Windows, s_x11WindowId);
1076 case s_x11UnmanagedId:
1077 return indexForWindow(row, column, m_unmanageds, s_x11UnmanagedId);
1078 case s_waylandWindowId:
1079 return indexForWindow(row, column, m_waylandWindows, s_waylandWindowId);
1080 case s_workspaceInternalId:
1081 return indexForWindow(row, column, m_internalWindows, s_workspaceInternalId);
1082 default:
1083 break;
1084 }
1085
1086 // index for a property (third level)
1087 if (parent.internalId() < s_idDistance * (s_x11WindowId + 1)) {
1088 return indexForProperty(row, column, parent, &DebugConsoleModel::x11Window);
1089 } else if (parent.internalId() < s_idDistance * (s_x11UnmanagedId + 1)) {
1090 return indexForProperty(row, column, parent, &DebugConsoleModel::unmanaged);
1091 } else if (parent.internalId() < s_idDistance * (s_waylandWindowId + 1)) {
1092 return indexForProperty(row, column, parent, &DebugConsoleModel::waylandWindow);
1093 } else if (parent.internalId() < s_idDistance * (s_workspaceInternalId + 1)) {
1094 return indexForProperty(row, column, parent, &DebugConsoleModel::internalWindow);
1095 }
1096
1097 return QModelIndex();
1098}
1099
1100QModelIndex DebugConsoleModel::parent(const QModelIndex &child) const
1101{
1102 if (child.internalId() <= s_workspaceInternalId) {
1103 return QModelIndex();
1104 }
1105 if (child.internalId() & s_propertyBitMask) {
1106 // a property
1107 const quint32 parentId = child.internalId() & s_windowBitMask;
1108 if (parentId < s_idDistance * (s_x11WindowId + 1)) {
1109 return createIndex(parentId - (s_idDistance * s_x11WindowId), 0, parentId);
1110 } else if (parentId < s_idDistance * (s_x11UnmanagedId + 1)) {
1111 return createIndex(parentId - (s_idDistance * s_x11UnmanagedId), 0, parentId);
1112 } else if (parentId < s_idDistance * (s_waylandWindowId + 1)) {
1113 return createIndex(parentId - (s_idDistance * s_waylandWindowId), 0, parentId);
1114 } else if (parentId < s_idDistance * (s_workspaceInternalId + 1)) {
1115 return createIndex(parentId - (s_idDistance * s_workspaceInternalId), 0, parentId);
1116 }
1117 return QModelIndex();
1118 }
1119 if (child.internalId() < s_idDistance * (s_x11WindowId + 1)) {
1120 return createIndex(s_x11WindowId - 1, 0, s_x11WindowId);
1121 } else if (child.internalId() < s_idDistance * (s_x11UnmanagedId + 1)) {
1122 return createIndex(s_x11UnmanagedId - 1, 0, s_x11UnmanagedId);
1123 } else if (child.internalId() < s_idDistance * (s_waylandWindowId + 1)) {
1124 return createIndex(s_waylandWindowId - 1, 0, s_waylandWindowId);
1125 } else if (child.internalId() < s_idDistance * (s_workspaceInternalId + 1)) {
1126 return createIndex(s_workspaceInternalId - 1, 0, s_workspaceInternalId);
1127 }
1128 return QModelIndex();
1129}
1130
1131QVariant DebugConsoleModel::propertyData(QObject *object, const QModelIndex &index, int role) const
1132{
1133 const auto property = object->metaObject()->property(index.row());
1134 if (index.column() == 0) {
1135 return property.name();
1136 } else {
1137 const QVariant value = property.read(object);
1138 if (qstrcmp(property.name(), "windowType") == 0) {
1139 switch (value.toInt()) {
1140 case NET::Normal:
1141 return QStringLiteral("NET::Normal");
1142 case NET::Desktop:
1143 return QStringLiteral("NET::Desktop");
1144 case NET::Dock:
1145 return QStringLiteral("NET::Dock");
1146 case NET::Toolbar:
1147 return QStringLiteral("NET::Toolbar");
1148 case NET::Menu:
1149 return QStringLiteral("NET::Menu");
1150 case NET::Dialog:
1151 return QStringLiteral("NET::Dialog");
1152 case NET::Override:
1153 return QStringLiteral("NET::Override");
1154 case NET::TopMenu:
1155 return QStringLiteral("NET::TopMenu");
1156 case NET::Utility:
1157 return QStringLiteral("NET::Utility");
1158 case NET::Splash:
1159 return QStringLiteral("NET::Splash");
1160 case NET::DropdownMenu:
1161 return QStringLiteral("NET::DropdownMenu");
1162 case NET::PopupMenu:
1163 return QStringLiteral("NET::PopupMenu");
1164 case NET::Tooltip:
1165 return QStringLiteral("NET::Tooltip");
1166 case NET::Notification:
1167 return QStringLiteral("NET::Notification");
1168 case NET::ComboBox:
1169 return QStringLiteral("NET::ComboBox");
1170 case NET::DNDIcon:
1171 return QStringLiteral("NET::DNDIcon");
1172 case NET::OnScreenDisplay:
1173 return QStringLiteral("NET::OnScreenDisplay");
1174 case NET::CriticalNotification:
1175 return QStringLiteral("NET::CriticalNotification");
1176 case NET::AppletPopup:
1177 return QStringLiteral("NET::AppletPopup");
1178 case NET::Unknown:
1179 default:
1180 return QStringLiteral("NET::Unknown");
1181 }
1182 } else if (qstrcmp(property.name(), "layer") == 0) {
1183 return QMetaEnum::fromType<Layer>().valueToKey(value.value<Layer>());
1184 }
1185 return value;
1186 }
1187 return QVariant();
1188}
1189
1190template<class T>
1191QVariant DebugConsoleModel::windowData(const QModelIndex &index, int role, const QList<T *> windows, const std::function<QString(T *)> &toString) const
1192{
1193 if (index.row() >= windows.count()) {
1194 return QVariant();
1195 }
1196 auto c = windows.at(index.row());
1197 if (role == Qt::DisplayRole) {
1198 return toString(c);
1199 } else if (role == Qt::DecorationRole) {
1200 return c->icon();
1201 }
1202 return QVariant();
1203}
1204
1205QVariant DebugConsoleModel::data(const QModelIndex &index, int role) const
1206{
1207 if (!index.isValid()) {
1208 return QVariant();
1209 }
1210 if (!index.parent().isValid()) {
1211 // one of the top levels
1212 if (index.column() != 0 || role != Qt::DisplayRole) {
1213 return QVariant();
1214 }
1215 switch (index.internalId()) {
1216 case s_x11WindowId:
1217 return i18n("X11 Windows");
1218 case s_x11UnmanagedId:
1219 return i18n("X11 Unmanaged Windows");
1220 case s_waylandWindowId:
1221 return i18n("Wayland Windows");
1222 case s_workspaceInternalId:
1223 return i18n("Internal Windows");
1224 default:
1225 return QVariant();
1226 }
1227 }
1228 if (index.internalId() & s_propertyBitMask) {
1229 if (index.column() >= 2 || role != Qt::DisplayRole) {
1230 return QVariant();
1231 }
1232 if (Window *w = waylandWindow(index)) {
1233 return propertyData(w, index, role);
1234 } else if (InternalWindow *w = internalWindow(index)) {
1235 return propertyData(w, index, role);
1236 } else if (X11Window *w = x11Window(index)) {
1237 return propertyData(w, index, role);
1238 } else if (X11Window *u = unmanaged(index)) {
1239 return propertyData(u, index, role);
1240 }
1241 } else {
1242 if (index.column() != 0) {
1243 return QVariant();
1244 }
1245
1246 auto generic = [](Window *c) -> QString {
1247 return c->caption() + QLatin1Char(' ') + QString::fromUtf8(c->metaObject()->className());
1248 };
1249 switch (index.parent().internalId()) {
1250 case s_x11WindowId:
1251 return windowData<X11Window>(index, role, m_x11Windows, [](X11Window *c) -> QString {
1252 return QStringLiteral("0x%1: %2").arg(c->window(), 0, 16).arg(c->caption());
1253 });
1254 case s_x11UnmanagedId: {
1255 if (index.row() >= m_unmanageds.count()) {
1256 return QVariant();
1257 }
1258 auto u = m_unmanageds.at(index.row());
1259 if (role == Qt::DisplayRole) {
1260 return QStringLiteral("0x%1").arg(u->window(), 0, 16);
1261 }
1262 break;
1263 }
1264 case s_waylandWindowId:
1265 return windowData<WaylandWindow>(index, role, m_waylandWindows, generic);
1266 case s_workspaceInternalId:
1267 return windowData<InternalWindow>(index, role, m_internalWindows, generic);
1268 default:
1269 break;
1270 }
1271 }
1272
1273 return QVariant();
1274}
1275
1276template<class T>
1277static T *windowForIndex(const QModelIndex &index, const QList<T *> &windows, int id)
1278{
1279 const qint32 row = (index.internalId() & s_windowBitMask) - (s_idDistance * id);
1280 if (row < 0 || row >= windows.count()) {
1281 return nullptr;
1282 }
1283 return windows.at(row);
1284}
1285
1286WaylandWindow *DebugConsoleModel::waylandWindow(const QModelIndex &index) const
1287{
1288 return windowForIndex(index, m_waylandWindows, s_waylandWindowId);
1289}
1290
1291InternalWindow *DebugConsoleModel::internalWindow(const QModelIndex &index) const
1292{
1293 return windowForIndex(index, m_internalWindows, s_workspaceInternalId);
1294}
1295
1296X11Window *DebugConsoleModel::x11Window(const QModelIndex &index) const
1297{
1298 return windowForIndex(index, m_x11Windows, s_x11WindowId);
1299}
1300
1301X11Window *DebugConsoleModel::unmanaged(const QModelIndex &index) const
1302{
1303 return windowForIndex(index, m_unmanageds, s_x11UnmanagedId);
1304}
1305
1308 : QAbstractItemModel(parent)
1309{
1310 // TODO: it would be nice to not have to reset the model on each change
1311 auto reset = [this] {
1312 beginResetModel();
1313 endResetModel();
1314 };
1315
1316 auto watchSubsurfaces = [this, reset](Window *c) {
1317 if (!c->surface()) {
1318 return;
1319 }
1320 auto monitor = new SubSurfaceMonitor(c->surface(), this);
1321 connect(monitor, &SubSurfaceMonitor::subSurfaceAdded, this, reset);
1322 connect(monitor, &SubSurfaceMonitor::subSurfaceRemoved, this, reset);
1323 connect(c, &QObject::destroyed, monitor, &QObject::deleteLater);
1324 };
1325
1326 for (auto c : workspace()->windows()) {
1327 watchSubsurfaces(c);
1328 }
1329 connect(workspace(), &Workspace::windowAdded, this, [reset, watchSubsurfaces](Window *c) {
1330 watchSubsurfaces(c);
1331 reset();
1332 });
1333 connect(workspace(), &Workspace::windowRemoved, this, reset);
1334}
1335
1337
1338int SurfaceTreeModel::columnCount(const QModelIndex &parent) const
1339{
1340 return 1;
1341}
1342
1343int SurfaceTreeModel::rowCount(const QModelIndex &parent) const
1344{
1345 if (parent.isValid()) {
1346 if (SurfaceInterface *surface = static_cast<SurfaceInterface *>(parent.internalPointer())) {
1347 return surface->below().count() + surface->above().count();
1348 }
1349 return 0;
1350 }
1351 // toplevel are all windows
1352 return workspace()->windows().count();
1353}
1354
1355QModelIndex SurfaceTreeModel::index(int row, int column, const QModelIndex &parent) const
1356{
1357 if (column != 0) {
1358 // invalid column
1359 return QModelIndex();
1360 }
1361
1362 if (parent.isValid()) {
1363 if (SurfaceInterface *surface = static_cast<SurfaceInterface *>(parent.internalPointer())) {
1364 int reference = 0;
1365 const auto &below = surface->below();
1366 if (row < reference + below.count()) {
1367 return createIndex(row, column, below.at(row - reference)->surface());
1368 }
1369 reference += below.count();
1370
1371 const auto &above = surface->above();
1372 if (row < reference + above.count()) {
1373 return createIndex(row, column, above.at(row - reference)->surface());
1374 }
1375 }
1376 return QModelIndex();
1377 }
1378 // a window
1379 const auto &allClients = workspace()->windows();
1380 if (row < allClients.count()) {
1381 // references a client
1382 return createIndex(row, column, allClients.at(row)->surface());
1383 }
1384 // not found
1385 return QModelIndex();
1386}
1387
1388QModelIndex SurfaceTreeModel::parent(const QModelIndex &child) const
1389{
1390 if (SurfaceInterface *surface = static_cast<SurfaceInterface *>(child.internalPointer())) {
1391 const auto &subsurface = surface->subSurface();
1392 if (!subsurface) {
1393 // doesn't reference a subsurface, this is a top-level window
1394 return QModelIndex();
1395 }
1396 SurfaceInterface *parent = subsurface->parentSurface();
1397 if (!parent) {
1398 // something is wrong
1399 return QModelIndex();
1400 }
1401 // is the parent a subsurface itself?
1402 if (parent->subSurface()) {
1403 auto grandParent = parent->subSurface()->parentSurface();
1404 if (!grandParent) {
1405 // something is wrong
1406 return QModelIndex();
1407 }
1408 int row = 0;
1409 const auto &below = grandParent->below();
1410 for (int i = 0; i < below.count(); i++) {
1411 if (below.at(i) == parent->subSurface()) {
1412 return createIndex(row + i, 0, parent);
1413 }
1414 }
1415 row += below.count();
1416 const auto &above = grandParent->above();
1417 for (int i = 0; i < above.count(); i++) {
1418 if (above.at(i) == parent->subSurface()) {
1419 return createIndex(row + i, 0, parent);
1420 }
1421 }
1422 return QModelIndex();
1423 }
1424 // not a subsurface, thus it's a true window
1425 const auto &allClients = workspace()->windows();
1426 for (int row = 0; row < allClients.count(); row++) {
1427 if (allClients.at(row)->surface() == parent) {
1428 return createIndex(row, 0, parent);
1429 }
1430 }
1431 }
1432 return QModelIndex();
1433}
1434
1435QVariant SurfaceTreeModel::data(const QModelIndex &index, int role) const
1436{
1437 if (!index.isValid()) {
1438 return QVariant();
1439 }
1440 if (SurfaceInterface *surface = static_cast<SurfaceInterface *>(index.internalPointer())) {
1441 if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
1442 return QStringLiteral("%1 (%2) - %3").arg(surface->client()->executablePath()).arg(surface->client()->processId()).arg(surface->id());
1443 } else if (role == Qt::DecorationRole) {
1444 if (surface->buffer()) {
1445 const GraphicsBufferView view(surface->buffer());
1446 if (const QImage *image = view.image()) {
1447 return image->scaled(QSize(64, 64), Qt::KeepAspectRatio);
1448 }
1449 }
1450 return QImage();
1451 }
1452 }
1453 return QVariant();
1454}
1455
1457 : QAbstractItemModel(parent)
1458 , m_devices(input()->devices())
1459{
1460 for (auto it = m_devices.constBegin(); it != m_devices.constEnd(); ++it) {
1461 setupDeviceConnections(*it);
1462 }
1463
1464 connect(input(), &InputRedirection::deviceAdded, this, [this](InputDevice *d) {
1465 beginInsertRows(QModelIndex(), m_devices.count(), m_devices.count());
1466 m_devices << d;
1467 setupDeviceConnections(d);
1468 endInsertRows();
1469 });
1470 connect(input(), &InputRedirection::deviceRemoved, this, [this](InputDevice *d) {
1471 const int index = m_devices.indexOf(d);
1472 if (index == -1) {
1473 return;
1474 }
1475 beginRemoveRows(QModelIndex(), index, index);
1476 m_devices.removeAt(index);
1477 endRemoveRows();
1478 });
1479}
1480
1482
1483int InputDeviceModel::columnCount(const QModelIndex &parent) const
1484{
1485 return 2;
1486}
1487
1488QVariant InputDeviceModel::data(const QModelIndex &index, int role) const
1489{
1490 if (!index.isValid()) {
1491 return QVariant();
1492 }
1493 if (!index.parent().isValid() && index.column() == 0) {
1494 if (index.row() >= m_devices.count()) {
1495 return QVariant();
1496 }
1497 if (role == Qt::DisplayRole) {
1498 return m_devices.at(index.row())->name();
1499 }
1500 }
1501 if (index.parent().isValid()) {
1502 if (role == Qt::DisplayRole) {
1503 const auto device = m_devices.at(index.parent().row());
1504 const auto property = device->metaObject()->property(index.row());
1505 if (index.column() == 0) {
1506 return property.name();
1507 } else if (index.column() == 1) {
1508 return device->property(property.name());
1509 }
1510 }
1511 }
1512 return QVariant();
1513}
1514
1515QModelIndex InputDeviceModel::index(int row, int column, const QModelIndex &parent) const
1516{
1517 if (column >= 2) {
1518 return QModelIndex();
1519 }
1520 if (parent.isValid()) {
1521 if (parent.internalId() & s_propertyBitMask) {
1522 return QModelIndex();
1523 }
1524 if (row >= m_devices.at(parent.row())->metaObject()->propertyCount()) {
1525 return QModelIndex();
1526 }
1527 return createIndex(row, column, quint32(row + 1) << 16 | parent.internalId());
1528 }
1529 if (row >= m_devices.count()) {
1530 return QModelIndex();
1531 }
1532 return createIndex(row, column, row + 1);
1533}
1534
1535int InputDeviceModel::rowCount(const QModelIndex &parent) const
1536{
1537 if (!parent.isValid()) {
1538 return m_devices.count();
1539 }
1540 if (parent.internalId() & s_propertyBitMask) {
1541 return 0;
1542 }
1543
1544 return m_devices.at(parent.row())->metaObject()->propertyCount();
1545}
1546
1547QModelIndex InputDeviceModel::parent(const QModelIndex &child) const
1548{
1549 if (child.internalId() & s_propertyBitMask) {
1550 const quintptr parentId = child.internalId() & s_windowBitMask;
1551 return createIndex(parentId - 1, 0, parentId);
1552 }
1553 return QModelIndex();
1554}
1555
1556void InputDeviceModel::slotPropertyChanged()
1557{
1558 const auto device = static_cast<InputDevice *>(sender());
1559
1560 for (int i = 0; i < device->metaObject()->propertyCount(); ++i) {
1561 const QMetaProperty metaProperty = device->metaObject()->property(i);
1562 if (metaProperty.notifySignalIndex() == senderSignalIndex()) {
1563 const QModelIndex parent = index(m_devices.indexOf(device), 0, QModelIndex());
1564 const QModelIndex child = index(i, 1, parent);
1565 Q_EMIT dataChanged(child, child, QList<int>{Qt::DisplayRole});
1566 }
1567 }
1568}
1569
1570void InputDeviceModel::setupDeviceConnections(InputDevice *device)
1571{
1572 QMetaMethod handler = metaObject()->method(metaObject()->indexOfMethod("slotPropertyChanged()"));
1573 for (int i = 0; i < device->metaObject()->propertyCount(); ++i) {
1574 const QMetaProperty metaProperty = device->metaObject()->property(i);
1575 if (metaProperty.hasNotifySignal()) {
1576 connect(device, metaProperty.notifySignal(), this, handler);
1577 }
1578 }
1579}
1580
1581QModelIndex DataSourceModel::index(int row, int column, const QModelIndex &parent) const
1582{
1583 if (!m_source || parent.isValid() || column >= 2 || row >= m_source->mimeTypes().size()) {
1584 return QModelIndex();
1585 }
1586 return createIndex(row, column, nullptr);
1587}
1588
1589QModelIndex DataSourceModel::parent(const QModelIndex &child) const
1590{
1591 return QModelIndex();
1592}
1593
1594int DataSourceModel::rowCount(const QModelIndex &parent) const
1595{
1596 if (!parent.isValid()) {
1597 return m_source ? m_source->mimeTypes().count() : 0;
1598 }
1599 return 0;
1600}
1601
1602QVariant DataSourceModel::headerData(int section, Qt::Orientation orientation, int role) const
1603{
1604 if (role != Qt::DisplayRole || orientation != Qt::Horizontal || section >= 2) {
1605 return QVariant();
1606 }
1607 return section == 0 ? QStringLiteral("Mime type") : QStringLiteral("Content");
1608}
1609
1610QVariant DataSourceModel::data(const QModelIndex &index, int role) const
1611{
1612 if (!checkIndex(index, CheckIndexOption::ParentIsInvalid | CheckIndexOption::IndexIsValid)) {
1613 return QVariant();
1614 }
1615 const QString mimeType = m_source->mimeTypes().at(index.row());
1616 ;
1617 if (index.column() == 0 && role == Qt::DisplayRole) {
1618 return mimeType;
1619 } else if (index.column() == 1 && index.row() < m_data.count()) {
1620 const QByteArray &data = m_data.at(index.row());
1621 if (mimeType.contains(QLatin1String("image"))) {
1622 if (role == Qt::DecorationRole) {
1623 return QImage::fromData(data);
1624 }
1625 } else if (role == Qt::DisplayRole) {
1626 return data;
1627 }
1628 }
1629 return QVariant();
1630}
1631
1632static QByteArray readData(int fd)
1633{
1634 pollfd pfd;
1635 pfd.fd = fd;
1636 pfd.events = POLLIN;
1637 FileDescriptor closeFd{fd};
1638 QByteArray data;
1639 while (true) {
1640 const int ready = poll(&pfd, 1, 1000);
1641 if (ready < 0) {
1642 if (errno != EINTR) {
1643 return QByteArrayLiteral("poll() failed: ") + strerror(errno);
1644 }
1645 } else if (ready == 0) {
1646 return QByteArrayLiteral("timeout reading from pipe");
1647 } else {
1648 char buf[4096];
1649 int n = read(fd, buf, sizeof buf);
1650
1651 if (n < 0) {
1652 return QByteArrayLiteral("read failed: ") + strerror(errno);
1653 } else if (n == 0) {
1654 return data;
1655 } else if (n > 0) {
1656 data.append(buf, n);
1657 }
1658 }
1659 }
1660}
1661
1663{
1664 beginResetModel();
1665 m_source = source;
1666 m_data.clear();
1667 if (source) {
1668 const QStringList mimeTypes = m_source->mimeTypes();
1669 m_data.resize(mimeTypes.size());
1670 for (auto type = mimeTypes.begin(); type != mimeTypes.end(); ++type) {
1671 int pipeFds[2];
1672 if (pipe2(pipeFds, O_CLOEXEC) != 0) {
1673 continue;
1674 }
1675 source->requestData(*type, pipeFds[1]);
1676 QFuture<QByteArray> data = QtConcurrent::run(readData, pipeFds[0]);
1677 auto watcher = new QFutureWatcher<QByteArray>(this);
1678 watcher->setFuture(data);
1679 const int index = type - mimeTypes.begin();
1680 connect(watcher, &QFutureWatcher<QByteArray>::finished, this, [this, watcher, index, source = QPointer(source)] {
1681 watcher->deleteLater();
1682 if (source && source == m_source) {
1683 m_data[index] = watcher->result();
1684 Q_EMIT dataChanged(this->index(index, 1), this->index(index, 1), {Qt::DecorationRole | Qt::DisplayRole});
1685 }
1686 });
1687 }
1688 }
1689 endResetModel();
1690}
1691}
1692
1693#include "moc_debug_console.cpp"
quint32 qHash(const Trigger &t)
The AbstractDataSource class abstracts the data that can be transferred to another client.
virtual QStringList mimeTypes() const =0
virtual wl_client * client() const
virtual void requestData(const QString &mimeType, qint32 fd)=0
@ OperationModeX11
KWin uses only X11 for managing windows and compositing.
Definition main.h:87
QModelIndex parent(const QModelIndex &child) const override
int rowCount(const QModelIndex &parent) const override
QVariant data(const QModelIndex &index, int role) const override
void setSource(AbstractDataSource *source)
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
QString displayText(const QVariant &value, const QLocale &locale) const override
void tabletToolEvent(TabletEvent *event) override
void touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
void swipeGestureEnd(std::chrono::microseconds time) override
void switchEvent(SwitchEvent *event) override
void touchUp(qint32 id, std::chrono::microseconds time) override
void pinchGestureCancelled(std::chrono::microseconds time) override
void keyEvent(KeyEvent *event) override
DebugConsoleFilter(QTextEdit *textEdit)
void swipeGestureBegin(int fingerCount, std::chrono::microseconds time) override
void touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
void tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
void tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
void tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId, std::chrono::microseconds time) override
void tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
void pinchGestureUpdate(qreal scale, qreal angleDelta, const QPointF &delta, std::chrono::microseconds time) override
void swipeGestureCancelled(std::chrono::microseconds time) override
void wheelEvent(WheelEvent *event) override
void pinchGestureBegin(int fingerCount, std::chrono::microseconds time) override
~DebugConsoleFilter() override
void swipeGestureUpdate(const QPointF &delta, std::chrono::microseconds time) override
void pinchGestureEnd(std::chrono::microseconds time) override
void pointerEvent(MouseEvent *event) override
DebugConsoleModel(QObject *parent=nullptr)
QModelIndex index(int row, int column, const QModelIndex &parent) const override
~DebugConsoleModel() override
QModelIndex parent(const QModelIndex &child) const override
int rowCount(const QModelIndex &parent) const override
QVariant data(const QModelIndex &index, int role) const override
int columnCount(const QModelIndex &parent) const override
ClientConnection * getConnection(wl_client *client)
Definition display.cpp:200
bool isOpenGLCompositing() const
Whether the Compositor is OpenGL based (either GL 1 or 2).
virtual bool isTabletModeSwitch() const =0
virtual bool isLidSwitch() const =0
InputDeviceModel(QObject *parent=nullptr)
int columnCount(const QModelIndex &parent) const override
QModelIndex parent(const QModelIndex &child) const override
QModelIndex index(int row, int column, const QModelIndex &parent) const override
int rowCount(const QModelIndex &parent) const override
QVariant data(const QModelIndex &index, int role) const override
~InputDeviceModel() override
void deviceAdded(InputDevice *device)
KeyboardInputRedirection * keyboard() const
Definition input.h:216
void deviceRemoved(InputDevice *device)
void keyStateChanged(quint32 keyCode, InputRedirection::KeyboardKeyState state)
Emitted when the state of a key changed.
InputDevice * device() const
std::chrono::microseconds timestamp() const
QPointF deltaUnaccelerated() const
Definition input_event.h:33
QPointF delta() const
Definition input_event.h:28
quint32 nativeButton() const
Definition input_event.h:58
InputDevice * device() const
Definition input_event.h:43
std::chrono::microseconds timestamp() const
Definition input_event.h:38
void selectionChanged(KWin::AbstractDataSource *)
void primarySelectionChanged(KWin::AbstractDataSource *)
AbstractDataSource * selection() const
Definition seat.cpp:1278
AbstractDataSource * primarySelection() const
Definition seat.cpp:1314
Resource representing a wl_surface.
Definition surface.h:80
SurfaceTreeModel(QObject *parent=nullptr)
~SurfaceTreeModel() override
int rowCount(const QModelIndex &parent) const override
int columnCount(const QModelIndex &parent) const override
QModelIndex parent(const QModelIndex &child) const override
QModelIndex index(int row, int column, const QModelIndex &parent) const override
QVariant data(const QModelIndex &index, int role) const override
std::chrono::microseconds timestamp() const
State state() const
InputDevice * device() const
SeatInterface * seat() const
Display * display() const
InputDevice * device() const
std::chrono::microseconds timestamp() const
QString caption
Definition window.h:392
void windowRemoved(KWin::Window *)
void windowAdded(KWin::Window *)
const QList< Window * > windows() const
Definition workspace.h:248
xcb_window_t window() const
xkb_keymap * keymap() const
Definition xkb.h:75
QString keymapComponentToString(xkb_keymap *map, const T &count, std::function< const char *(xkb_keymap *, T)> f)
Session::Type type
Definition session.cpp:17
QList< QByteArray > openGLExtensions()
Definition glutils.cpp:143
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
InputRedirection * input()
Definition input.h:549
Layer
Definition globals.h:162
QString stateActiveComponents(xkb_state *state, const T &count, std::function< int(xkb_state *, T)> f, std::function< const char *(xkb_keymap *, T)> name)