1 /* 2 KWin - the KDE window manager 3 This file is part of the KDE project. 4 5 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org> 6 7 SPDX-License-Identifier: GPL-2.0-or-later 8 */ 9 #ifndef KWIN_WAYLAND_TEST_H 10 #define KWIN_WAYLAND_TEST_H 11 12 #include "main.h" 13 14 // Qt 15 #include <QtTest> 16 17 #include "qwayland-idle-inhibit-unstable-v1.h" 18 #include "qwayland-wlr-layer-shell-unstable-v1.h" 19 #include "qwayland-text-input-unstable-v3.h" 20 #include "qwayland-xdg-decoration-unstable-v1.h" 21 #include "qwayland-xdg-shell.h" 22 #include "qwayland-kde-output-device-v2.h" 23 #include "qwayland-kde-output-management-v2.h" 24 25 namespace KWayland 26 { 27 namespace Client 28 { 29 class AppMenuManager; 30 class ConnectionThread; 31 class Compositor; 32 class Output; 33 class PlasmaShell; 34 class PlasmaWindowManagement; 35 class PointerConstraints; 36 class Seat; 37 class ServerSideDecorationManager; 38 class ShadowManager; 39 class ShmPool; 40 class SubCompositor; 41 class SubSurface; 42 class Surface; 43 class TextInputManager; 44 } 45 } 46 47 namespace QtWayland 48 { 49 class zwp_input_panel_surface_v1; 50 class zwp_text_input_v3; 51 class zwp_text_input_manager_v3; 52 } 53 54 namespace KWin 55 { 56 namespace Xwl 57 { 58 class Xwayland; 59 } 60 61 class AbstractClient; 62 63 class WaylandTestApplication : public ApplicationWaylandAbstract 64 { 65 Q_OBJECT 66 public: 67 WaylandTestApplication(OperationMode mode, int &argc, char **argv); 68 ~WaylandTestApplication() override; 69 setInputMethodServerToStart(const QString & inputMethodServer)70 void setInputMethodServerToStart(const QString &inputMethodServer) { 71 m_inputMethodServerToStart = inputMethodServer; 72 } 73 protected: 74 void performStartup() override; 75 76 private: 77 void continueStartupWithScreens(); 78 void continueStartupWithScene(); 79 void finalizeStartup(); 80 81 Xwl::Xwayland *m_xwayland = nullptr; 82 QString m_inputMethodServerToStart; 83 }; 84 85 namespace Test 86 { 87 88 class MockInputMethod; 89 90 class TextInputManagerV3 : public QtWayland::zwp_text_input_manager_v3 91 { 92 public: ~TextInputManagerV3()93 ~TextInputManagerV3() override { destroy(); } 94 }; 95 96 class TextInputV3 : public QtWayland::zwp_text_input_v3 97 { ~TextInputV3()98 ~TextInputV3() override { destroy(); } 99 }; 100 101 class LayerShellV1 : public QtWayland::zwlr_layer_shell_v1 102 { 103 public: 104 ~LayerShellV1() override; 105 }; 106 107 class LayerSurfaceV1 : public QObject, public QtWayland::zwlr_layer_surface_v1 108 { 109 Q_OBJECT 110 111 public: 112 ~LayerSurfaceV1() override; 113 114 protected: 115 void zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height) override; 116 void zwlr_layer_surface_v1_closed() override; 117 118 Q_SIGNALS: 119 void closeRequested(); 120 void configureRequested(quint32 serial, const QSize &size); 121 }; 122 123 /** 124 * The XdgShell class represents the @c xdg_wm_base global. 125 */ 126 class XdgShell : public QtWayland::xdg_wm_base 127 { 128 public: 129 ~XdgShell() override; xdg_wm_base_ping(uint32_t serial)130 void xdg_wm_base_ping(uint32_t serial) override 131 { 132 pong(serial); 133 } 134 }; 135 136 /** 137 * The XdgSurface class represents an xdg_surface object. 138 */ 139 class XdgSurface : public QObject, public QtWayland::xdg_surface 140 { 141 Q_OBJECT 142 143 public: 144 explicit XdgSurface(XdgShell *shell, KWayland::Client::Surface *surface, QObject *parent = nullptr); 145 ~XdgSurface() override; 146 147 KWayland::Client::Surface *surface() const; 148 149 Q_SIGNALS: 150 void configureRequested(quint32 serial); 151 152 protected: 153 void xdg_surface_configure(uint32_t serial) override; 154 155 private: 156 KWayland::Client::Surface *m_surface; 157 }; 158 159 /** 160 * The XdgToplevel class represents an xdg_toplevel surface. Note that the XdgToplevel surface 161 * takes the ownership of the underlying XdgSurface object. 162 */ 163 class XdgToplevel : public QObject, public QtWayland::xdg_toplevel 164 { 165 Q_OBJECT 166 167 public: 168 enum class State { 169 Maximized = 1 << 0, 170 Fullscreen = 1 << 1, 171 Resizing = 1 << 2, 172 Activated = 1 << 3 173 }; 174 Q_DECLARE_FLAGS(States, State) 175 176 explicit XdgToplevel(XdgSurface *surface, QObject *parent = nullptr); 177 ~XdgToplevel() override; 178 179 XdgSurface *xdgSurface() const; 180 181 Q_SIGNALS: 182 void configureRequested(const QSize &size, KWin::Test::XdgToplevel::States states); 183 void closeRequested(); 184 185 protected: 186 void xdg_toplevel_configure(int32_t width, int32_t height, wl_array *states) override; 187 void xdg_toplevel_close() override; 188 189 private: 190 QScopedPointer<XdgSurface> m_xdgSurface; 191 }; 192 193 /** 194 * The XdgPositioner class represents an xdg_positioner object. 195 */ 196 class XdgPositioner : public QtWayland::xdg_positioner 197 { 198 public: 199 explicit XdgPositioner(XdgShell *shell); 200 ~XdgPositioner() override; 201 }; 202 203 /** 204 * The XdgPopup class represents an xdg_popup surface. Note that the XdgPopup surface takes 205 * the ownership of the underlying XdgSurface object. 206 */ 207 class XdgPopup : public QObject, public QtWayland::xdg_popup 208 { 209 Q_OBJECT 210 211 public: 212 XdgPopup(XdgSurface *surface, XdgSurface *parentSurface, XdgPositioner *positioner, QObject *parent = nullptr); 213 ~XdgPopup() override; 214 215 XdgSurface *xdgSurface() const; 216 217 Q_SIGNALS: 218 void configureRequested(const QRect &rect); 219 void doneReceived(); 220 221 protected: 222 void xdg_popup_configure(int32_t x, int32_t y, int32_t width, int32_t height) override; 223 void xdg_popup_popup_done() override; 224 225 private: 226 QScopedPointer<XdgSurface> m_xdgSurface; 227 }; 228 229 class XdgDecorationManagerV1 : public QtWayland::zxdg_decoration_manager_v1 230 { 231 public: 232 ~XdgDecorationManagerV1() override; 233 }; 234 235 class XdgToplevelDecorationV1 : public QObject, public QtWayland::zxdg_toplevel_decoration_v1 236 { 237 Q_OBJECT 238 239 public: 240 XdgToplevelDecorationV1(XdgDecorationManagerV1 *manager, XdgToplevel *toplevel, QObject *parent = nullptr); 241 ~XdgToplevelDecorationV1() override; 242 243 Q_SIGNALS: 244 void configureRequested(QtWayland::zxdg_toplevel_decoration_v1::mode mode); 245 246 protected: 247 void zxdg_toplevel_decoration_v1_configure(uint32_t mode) override; 248 }; 249 250 class IdleInhibitManagerV1 : public QtWayland::zwp_idle_inhibit_manager_v1 251 { 252 public: 253 ~IdleInhibitManagerV1() override; 254 }; 255 256 class IdleInhibitorV1 : public QtWayland::zwp_idle_inhibitor_v1 257 { 258 public: 259 IdleInhibitorV1(IdleInhibitManagerV1 *manager, KWayland::Client::Surface *surface); 260 ~IdleInhibitorV1() override; 261 }; 262 263 class WaylandOutputConfigurationV2 : public QObject, public QtWayland::kde_output_configuration_v2 264 { 265 Q_OBJECT 266 public: 267 WaylandOutputConfigurationV2(struct ::kde_output_configuration_v2 *object); 268 269 Q_SIGNALS: 270 void applied(); 271 void failed(); 272 273 protected: 274 void kde_output_configuration_v2_applied() override; 275 void kde_output_configuration_v2_failed() override; 276 }; 277 278 class WaylandOutputManagementV2 : public QObject, public QtWayland::kde_output_management_v2 279 { 280 Q_OBJECT 281 public: 282 WaylandOutputManagementV2(struct ::wl_registry *registry, int id, int version); 283 284 WaylandOutputConfigurationV2 *createConfiguration(); 285 }; 286 287 class WaylandOutputDeviceV2Mode : public QObject, public QtWayland::kde_output_device_mode_v2 288 { 289 Q_OBJECT 290 291 public: 292 WaylandOutputDeviceV2Mode(struct ::kde_output_device_mode_v2 *object); 293 ~WaylandOutputDeviceV2Mode() override; 294 295 int refreshRate() const; 296 QSize size() const; 297 bool preferred() const; 298 299 bool operator==(const WaylandOutputDeviceV2Mode &other); 300 301 static WaylandOutputDeviceV2Mode *get(struct ::kde_output_device_mode_v2 *object); 302 303 Q_SIGNALS: 304 void removed(); 305 306 protected: 307 void kde_output_device_mode_v2_size(int32_t width, int32_t height) override; 308 void kde_output_device_mode_v2_refresh(int32_t refresh) override; 309 void kde_output_device_mode_v2_preferred() override; 310 void kde_output_device_mode_v2_removed() override; 311 312 private: 313 int m_refreshRate = 60000; 314 QSize m_size; 315 bool m_preferred = false; 316 }; 317 318 class WaylandOutputDeviceV2 : public QObject, public QtWayland::kde_output_device_v2 319 { 320 Q_OBJECT 321 322 public: 323 WaylandOutputDeviceV2(int id); 324 ~WaylandOutputDeviceV2() override; 325 326 QByteArray edid() const; 327 bool enabled() const; 328 int id() const; 329 QString name() const; 330 QString model() const; 331 QString manufacturer() const; 332 qreal scale() const; 333 QPoint globalPosition() const; 334 QSize pixelSize() const; 335 int refreshRate() const; 336 uint32_t vrrPolicy() const; 337 uint32_t overscan() const; 338 uint32_t capabilities() const; 339 uint32_t rgbRange() const; 340 341 QString modeId() const; 342 343 Q_SIGNALS: 344 void enabledChanged(); 345 void done(); 346 347 protected: 348 void kde_output_device_v2_geometry(int32_t x, 349 int32_t y, 350 int32_t physical_width, 351 int32_t physical_height, 352 int32_t subpixel, 353 const QString &make, 354 const QString &model, 355 int32_t transform) override; 356 void kde_output_device_v2_current_mode(struct ::kde_output_device_mode_v2 *mode) override; 357 void kde_output_device_v2_mode(struct ::kde_output_device_mode_v2 *mode) override; 358 void kde_output_device_v2_done() override; 359 void kde_output_device_v2_scale(wl_fixed_t factor) override; 360 void kde_output_device_v2_edid(const QString &raw) override; 361 void kde_output_device_v2_enabled(int32_t enabled) override; 362 void kde_output_device_v2_uuid(const QString &uuid) override; 363 void kde_output_device_v2_serial_number(const QString &serialNumber) override; 364 void kde_output_device_v2_eisa_id(const QString &eisaId) override; 365 void kde_output_device_v2_capabilities(uint32_t flags) override; 366 void kde_output_device_v2_overscan(uint32_t overscan) override; 367 void kde_output_device_v2_vrr_policy(uint32_t vrr_policy) override; 368 void kde_output_device_v2_rgb_range(uint32_t rgb_range) override; 369 370 private: 371 QString modeName(const WaylandOutputDeviceV2Mode *m) const; 372 WaylandOutputDeviceV2Mode *deviceModeFromId(const int modeId) const; 373 374 WaylandOutputDeviceV2Mode *m_mode; 375 QList<WaylandOutputDeviceV2Mode *> m_modes; 376 377 int m_id; 378 QPoint m_pos; 379 QSize m_physicalSize; 380 int32_t m_subpixel; 381 QString m_manufacturer; 382 QString m_model; 383 int32_t m_transform; 384 qreal m_factor; 385 QByteArray m_edid; 386 int32_t m_enabled; 387 QString m_uuid; 388 QString m_serialNumber; 389 QString m_eisaId; 390 uint32_t m_flags; 391 uint32_t m_overscan; 392 uint32_t m_vrr_policy; 393 uint32_t m_rgbRange; 394 }; 395 396 enum class AdditionalWaylandInterface { 397 Seat = 1 << 0, 398 Decoration = 1 << 1, 399 PlasmaShell = 1 << 2, 400 WindowManagement = 1 << 3, 401 PointerConstraints = 1 << 4, 402 IdleInhibitV1 = 1 << 5, 403 AppMenu = 1 << 6, 404 ShadowManager = 1 << 7, 405 XdgDecorationV1 = 1 << 8, 406 OutputManagementV2 = 1 << 9, 407 TextInputManagerV2 = 1 << 10, 408 InputMethodV1 = 1 << 11, 409 LayerShellV1 = 1 << 12, 410 TextInputManagerV3 = 1 << 13, 411 OutputDeviceV2 = 1 << 14, 412 }; 413 Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface) 414 /** 415 * Creates a Wayland Connection in a dedicated thread and creates various 416 * client side objects which can be used to create windows. 417 * @returns @c true if created successfully, @c false if there was an error 418 * @see destroyWaylandConnection 419 */ 420 bool setupWaylandConnection(AdditionalWaylandInterfaces flags = AdditionalWaylandInterfaces()); 421 422 /** 423 * Destroys the Wayland Connection created with @link{setupWaylandConnection}. 424 * This can be called from cleanup in order to ensure that no Wayland Connection 425 * leaks into the next test method. 426 * @see setupWaylandConnection 427 */ 428 void destroyWaylandConnection(); 429 430 KWayland::Client::ConnectionThread *waylandConnection(); 431 KWayland::Client::Compositor *waylandCompositor(); 432 KWayland::Client::SubCompositor *waylandSubCompositor(); 433 KWayland::Client::ShadowManager *waylandShadowManager(); 434 KWayland::Client::ShmPool *waylandShmPool(); 435 KWayland::Client::Seat *waylandSeat(); 436 KWayland::Client::ServerSideDecorationManager *waylandServerSideDecoration(); 437 KWayland::Client::PlasmaShell *waylandPlasmaShell(); 438 KWayland::Client::PlasmaWindowManagement *waylandWindowManagement(); 439 KWayland::Client::PointerConstraints *waylandPointerConstraints(); 440 KWayland::Client::AppMenuManager *waylandAppMenuManager(); 441 WaylandOutputManagementV2 *waylandOutputManagementV2(); 442 KWayland::Client::TextInputManager *waylandTextInputManager(); 443 QVector<KWayland::Client::Output *> waylandOutputs(); 444 QVector<WaylandOutputDeviceV2 *> waylandOutputDevicesV2(); 445 446 bool waitForWaylandSurface(AbstractClient *client); 447 448 bool waitForWaylandPointer(); 449 bool waitForWaylandTouch(); 450 bool waitForWaylandKeyboard(); 451 452 void flushWaylandConnection(); 453 454 KWayland::Client::Surface *createSurface(QObject *parent = nullptr); 455 KWayland::Client::SubSurface *createSubSurface(KWayland::Client::Surface *surface, 456 KWayland::Client::Surface *parentSurface, QObject *parent = nullptr); 457 458 LayerSurfaceV1 *createLayerSurfaceV1(KWayland::Client::Surface *surface, 459 const QString &scope, 460 KWayland::Client::Output *output = nullptr, 461 LayerShellV1::layer layer = LayerShellV1::layer_top); 462 463 TextInputManagerV3 *waylandTextInputManagerV3(); 464 465 enum class CreationSetup { 466 CreateOnly, 467 CreateAndConfigure, /// commit and wait for the configure event, making this surface ready to commit buffers 468 }; 469 470 QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(KWayland::Client::Surface *surface, 471 KWayland::Client::Output *output); 472 473 XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent = nullptr, 474 CreationSetup configureMode = CreationSetup::CreateAndConfigure); 475 476 XdgPositioner *createXdgPositioner(); 477 478 XdgPopup *createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface, 479 XdgPositioner *positioner, QObject *parent = nullptr, 480 CreationSetup configureMode = CreationSetup::CreateAndConfigure); 481 482 XdgToplevelDecorationV1 *createXdgToplevelDecorationV1(XdgToplevel *toplevel, QObject *parent = nullptr); 483 IdleInhibitorV1 *createIdleInhibitorV1(KWayland::Client::Surface *surface); 484 485 486 /** 487 * Creates a shared memory buffer of @p size in @p color and attaches it to the @p surface. 488 * The @p surface gets damaged and committed, thus it's rendered. 489 */ 490 void render(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format = QImage::Format_ARGB32_Premultiplied); 491 492 /** 493 * Creates a shared memory buffer using the supplied image @p img and attaches it to the @p surface 494 */ 495 void render(KWayland::Client::Surface *surface, const QImage &img); 496 497 /** 498 * Waits till a new AbstractClient is shown and returns the created AbstractClient. 499 * If no AbstractClient gets shown during @p timeout @c null is returned. 500 */ 501 AbstractClient *waitForWaylandWindowShown(int timeout = 5000); 502 503 /** 504 * Combination of @link{render} and @link{waitForWaylandWindowShown}. 505 */ 506 AbstractClient *renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format = QImage::Format_ARGB32, int timeout = 5000); 507 508 /** 509 * Waits for the @p client to be destroyed. 510 */ 511 bool waitForWindowDestroyed(AbstractClient *client); 512 513 /** 514 * Locks the screen and waits till the screen is locked. 515 * @returns @c true if the screen could be locked, @c false otherwise 516 */ 517 bool lockScreen(); 518 519 /** 520 * Unlocks the screen and waits till the screen is unlocked. 521 * @returns @c true if the screen could be unlocked, @c false otherwise 522 */ 523 bool unlockScreen(); 524 525 void initWaylandWorkspace(); 526 527 AbstractClient *inputPanelClient(); 528 KWayland::Client::Surface *inputPanelSurface(); 529 530 } 531 532 } 533 534 Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::Test::AdditionalWaylandInterfaces) 535 Q_DECLARE_METATYPE(KWin::Test::XdgToplevel::States) 536 Q_DECLARE_METATYPE(QtWayland::zxdg_toplevel_decoration_v1::mode) 537 538 #define WAYLANDTEST_MAIN_HELPER(TestObject, DPI, OperationMode) \ 539 int main(int argc, char *argv[]) \ 540 { \ 541 setenv("QT_QPA_PLATFORM", "wayland-org.kde.kwin.qpa", true); \ 542 setenv("QT_QPA_PLATFORM_PLUGIN_PATH", QFileInfo(QString::fromLocal8Bit(argv[0])).absolutePath().toLocal8Bit().constData(), true); \ 543 setenv("KWIN_FORCE_OWN_QPA", "1", true); \ 544 qunsetenv("KDE_FULL_SESSION"); \ 545 qunsetenv("KDE_SESSION_VERSION"); \ 546 qunsetenv("XDG_SESSION_DESKTOP"); \ 547 qunsetenv("XDG_CURRENT_DESKTOP"); \ 548 DPI; \ 549 KWin::WaylandTestApplication app(OperationMode, argc, argv); \ 550 app.setAttribute(Qt::AA_Use96Dpi, true); \ 551 TestObject tc; \ 552 return QTest::qExec(&tc, argc, argv); \ 553 } 554 555 #ifdef NO_XWAYLAND 556 #define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps), KWin::Application::OperationModeWaylandOnly) 557 #else 558 #define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps), KWin::Application::OperationModeXwayland) 559 #endif 560 561 #endif 562