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