KWin
Loading...
Searching...
No Matches
renderingservertest.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
10#include "wayland/display.h"
11#include "wayland/keyboard.h"
12#include "wayland/output.h"
13#include "wayland/pointer.h"
14#include "wayland/seat.h"
15#include "wayland/xdgshell.h"
16
17#include "fakeoutput.h"
18
19#include <QApplication>
20#include <QCommandLineParser>
21#include <QDateTime>
22#include <QFile>
23#include <QKeyEvent>
24#include <QMouseEvent>
25#include <QPainter>
26#include <QPointer>
27#include <QThreadPool>
28#include <QWidget>
29
30#include <iostream>
31#include <unistd.h>
32
33static int startXServer()
34{
35 const QByteArray process = QByteArrayLiteral("Xwayland");
36 int pipeFds[2];
37 if (pipe(pipeFds) != 0) {
38 std::cerr << "FATAL ERROR failed to create pipe to start X Server " << process.constData() << std::endl;
39 exit(1);
40 }
41
42 pid_t pid = fork();
43 if (pid == 0) {
44 // child process - should be turned into Xwayland
45 // writes to pipe, closes read side
46 close(pipeFds[0]);
47 char fdbuf[16];
48 sprintf(fdbuf, "%d", pipeFds[1]);
49 execlp(process.constData(), process.constData(), "-displayfd", fdbuf, "-rootless", (char *)nullptr);
50 close(pipeFds[1]);
51 exit(20);
52 }
53 // parent process - this is the wayland server
54 // reads from pipe, closes write side
55 close(pipeFds[1]);
56 return pipeFds[0];
57}
58
59static void readDisplayFromPipe(int pipe)
60{
61 QFile readPipe;
62 if (!readPipe.open(pipe, QIODevice::ReadOnly)) {
63 std::cerr << "FATAL ERROR failed to open pipe to start X Server XWayland" << std::endl;
64 exit(1);
65 }
66 QByteArray displayNumber = readPipe.readLine();
67
68 displayNumber.prepend(QByteArray(":"));
69 displayNumber.remove(displayNumber.size() - 1, 1);
70 std::cout << "X-Server started on display " << displayNumber.constData() << std::endl;
71
72 setenv("DISPLAY", displayNumber.constData(), true);
73
74 // close our pipe
75 close(pipe);
76}
77
78class CompositorWindow : public QWidget
79{
80 Q_OBJECT
81public:
82 explicit CompositorWindow(QWidget *parent = nullptr);
84
86
87 void setSeat(const QPointer<KWin::SeatInterface> &seat);
88
89protected:
90 void paintEvent(QPaintEvent *event) override;
91 void keyPressEvent(QKeyEvent *event) override;
92 void keyReleaseEvent(QKeyEvent *event) override;
93 void mouseMoveEvent(QMouseEvent *event) override;
94 void mousePressEvent(QMouseEvent *event) override;
95 void mouseReleaseEvent(QMouseEvent *event) override;
96 void wheelEvent(QWheelEvent *event) override;
97
98private:
99 void updateFocus();
100 QList<KWin::XdgToplevelInterface *> m_stackingOrder;
101 QPointer<KWin::SeatInterface> m_seat;
102};
103
105 : QWidget(parent)
106{
107 setMouseTracking(true);
108}
109
111
113{
114 using namespace KWin;
115 surface->sendConfigure(QSize(), XdgToplevelInterface::States());
116 m_stackingOrder << surface;
117 connect(surface->surface(), &SurfaceInterface::damaged, this, static_cast<void (CompositorWindow::*)()>(&CompositorWindow::update));
118 connect(surface, &XdgToplevelInterface::destroyed, this, [surface, this] {
119 m_stackingOrder.removeAll(surface);
120 updateFocus();
121 update();
122 });
123 updateFocus();
124}
125
126void CompositorWindow::updateFocus()
127{
128 using namespace KWin;
129 if (!m_seat || m_stackingOrder.isEmpty()) {
130 return;
131 }
132 auto it = std::find_if(m_stackingOrder.constBegin(), m_stackingOrder.constEnd(), [](XdgToplevelInterface *toplevel) {
133 return toplevel->surface()->isMapped();
134 });
135 if (it == m_stackingOrder.constEnd()) {
136 return;
137 }
138 m_seat->notifyPointerEnter((*it)->surface(), m_seat->pointerPos());
139 m_seat->setFocusedKeyboardSurface((*it)->surface());
140}
141
142void CompositorWindow::setSeat(const QPointer<KWin::SeatInterface> &seat)
143{
144 m_seat = seat;
145}
146
147void CompositorWindow::paintEvent(QPaintEvent *event)
148{
149 QWidget::paintEvent(event);
150 QPainter p(this);
151 for (auto window : m_stackingOrder) {
152 KWin::SurfaceInterface *surface = window->surface();
153 if (!surface || !surface->isMapped()) {
154 continue;
155 }
156 KWin::GraphicsBufferView view(surface->buffer());
157 if (view.image()) {
158 p.drawImage(QPoint(0, 0), *view.image());
159 }
160 surface->frameRendered(QDateTime::currentMSecsSinceEpoch());
161 }
162}
163
165{
166 QWidget::keyPressEvent(event);
167 if (!m_seat) {
168 return;
169 }
170 if (!m_seat->focusedKeyboardSurface()) {
171 updateFocus();
172 }
173 m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
174 m_seat->notifyKeyboardKey(event->nativeScanCode() - 8, KWin::KeyboardKeyState::Pressed);
175}
176
178{
179 QWidget::keyReleaseEvent(event);
180 if (!m_seat) {
181 return;
182 }
183 m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
184 m_seat->notifyKeyboardKey(event->nativeScanCode() - 8, KWin::KeyboardKeyState::Released);
185}
186
187void CompositorWindow::mouseMoveEvent(QMouseEvent *event)
188{
189 QWidget::mouseMoveEvent(event);
190 if (!m_seat->focusedPointerSurface()) {
191 updateFocus();
192 }
193 m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
194 m_seat->notifyPointerMotion(event->localPos().toPoint());
195 m_seat->notifyPointerFrame();
196}
197
198void CompositorWindow::mousePressEvent(QMouseEvent *event)
199{
200 QWidget::mousePressEvent(event);
201 if (!m_seat->focusedPointerSurface()) {
202 if (!m_stackingOrder.isEmpty()) {
203 m_seat->notifyPointerEnter(m_stackingOrder.last()->surface(), event->globalPos());
204 }
205 }
206 m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
207 m_seat->notifyPointerButton(event->button(), KWin::PointerButtonState::Pressed);
208 m_seat->notifyPointerFrame();
209}
210
212{
213 QWidget::mouseReleaseEvent(event);
214 m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
215 m_seat->notifyPointerButton(event->button(), KWin::PointerButtonState::Released);
216 m_seat->notifyPointerFrame();
217}
218
219void CompositorWindow::wheelEvent(QWheelEvent *event)
220{
221 QWidget::wheelEvent(event);
222 m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
223 const QPoint &angle = event->angleDelta() / (8 * 15);
224 if (angle.x() != 0) {
225 m_seat->notifyPointerAxis(Qt::Horizontal, angle.x(), 1, KWin::PointerAxisSource::Wheel);
226 }
227 if (angle.y() != 0) {
228 m_seat->notifyPointerAxis(Qt::Vertical, angle.y(), 1, KWin::PointerAxisSource::Wheel);
229 }
230 m_seat->notifyPointerFrame();
231}
232
233int main(int argc, char **argv)
234{
235 using namespace KWin;
236 QApplication app(argc, argv);
237
238 QCommandLineParser parser;
239 parser.addHelpOption();
240 QCommandLineOption xwaylandOption(QStringList{QStringLiteral("x"), QStringLiteral("xwayland")}, QStringLiteral("Start a rootless Xwayland server"));
241 parser.addOption(xwaylandOption);
242 parser.process(app);
243
244 KWin::Display display;
245 display.start();
246 new DataDeviceManagerInterface(&display);
247 new CompositorInterface(&display, &display);
248 XdgShellInterface *shell = new XdgShellInterface(&display);
249 display.createShm();
250
251 const QSize windowSize(1024, 768);
252
253 auto outputHandle = std::make_unique<FakeOutput>();
254 outputHandle->setPhysicalSize(QSize(269, 202));
255 outputHandle->setMode(windowSize, 60000);
256
257 auto outputInterface = std::make_unique<OutputInterface>(&display, outputHandle.get());
258
259 SeatInterface *seat = new SeatInterface(&display);
260 seat->setHasKeyboard(true);
261 seat->setHasPointer(true);
262 seat->setName(QStringLiteral("testSeat0"));
263
264 CompositorWindow compositorWindow;
265 compositorWindow.setSeat(seat);
266 compositorWindow.setMinimumSize(windowSize);
267 compositorWindow.setMaximumSize(windowSize);
268 compositorWindow.setGeometry(QRect(QPoint(0, 0), windowSize));
269 compositorWindow.show();
270 QObject::connect(shell, &XdgShellInterface::toplevelCreated, &compositorWindow, &CompositorWindow::surfaceCreated);
271
272 // start XWayland
273 if (parser.isSet(xwaylandOption)) {
274 // starts XWayland by forking and opening a pipe
275 const int pipe = startXServer();
276 if (pipe == -1) {
277 exit(1);
278 }
279
280 QThreadPool::globalInstance()->start([pipe] {
281 readDisplayFromPipe(pipe);
282 });
283 }
284
285 return app.exec();
286}
287
288#include "renderingservertest.moc"
CompositorWindow(QWidget *parent=nullptr)
void mousePressEvent(QMouseEvent *event) override
void mouseReleaseEvent(QMouseEvent *event) override
void setSeat(const QPointer< KWin::SeatInterface > &seat)
void keyReleaseEvent(QKeyEvent *event) override
virtual ~CompositorWindow()
void surfaceCreated(KWin::XdgToplevelInterface *surface)
void mouseMoveEvent(QMouseEvent *event) override
void wheelEvent(QWheelEvent *event) override
void keyPressEvent(QKeyEvent *event) override
void paintEvent(QPaintEvent *event) override
Represents the Global for wl_data_device_manager interface.
Class holding the Wayland server display loop.
Definition display.h:34
void createShm()
Definition display.cpp:128
bool start()
Definition display.cpp:92
Represents a Seat on the Wayland Display.
Definition seat.h:134
Resource representing a wl_surface.
Definition surface.h:80
bool isMapped() const
Definition surface.cpp:891
GraphicsBuffer * buffer() const
Definition surface.cpp:809
void frameRendered(quint32 msec)
Definition surface.cpp:459
quint32 sendConfigure(const QSize &size, const States &states)
Definition xdgshell.cpp:587
SurfaceInterface * surface() const
Definition xdgshell.cpp:552