KWin
Loading...
Searching...
No Matches
wayland_output.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: 2019 Roman Gilg <subdiff@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "wayland_output.h"
10#include "compositor.h"
11#include "core/outputlayer.h"
12#include "core/renderbackend.h"
13#include "core/renderloop_p.h"
14#include "wayland_backend.h"
15#include "wayland_display.h"
16
17#include <KWayland/Client/compositor.h>
18#include <KWayland/Client/pointer.h>
19#include <KWayland/Client/pointerconstraints.h>
20#include <KWayland/Client/surface.h>
21#include <KWayland/Client/xdgdecoration.h>
22
23#include <KLocalizedString>
24
25#include <QPainter>
26
27#include <cmath>
28
29namespace KWin
30{
31namespace Wayland
32{
33
34using namespace KWayland::Client;
35static const int s_refreshRate = 60000; // TODO: can we get refresh rate data from Wayland host?
36
38 : m_surface(backend->display()->compositor()->createSurface())
39{
40}
41
43
44KWayland::Client::Pointer *WaylandCursor::pointer() const
45{
46 return m_pointer;
47}
48
49void WaylandCursor::setPointer(KWayland::Client::Pointer *pointer)
50{
51 if (m_pointer == pointer) {
52 return;
53 }
54 m_pointer = pointer;
55 if (m_pointer) {
56 m_pointer->setCursor(m_surface.get(), m_hotspot);
57 }
58}
59
61{
62 if (m_enabled != enable) {
63 m_enabled = enable;
64 sync();
65 }
66}
67
68void WaylandCursor::update(wl_buffer *buffer, qreal scale, const QPoint &hotspot)
69{
70 if (m_buffer != buffer || m_scale != scale || m_hotspot != hotspot) {
71 m_buffer = buffer;
72 m_scale = scale;
73 m_hotspot = hotspot;
74
75 sync();
76 }
77}
78
79void WaylandCursor::sync()
80{
81 if (!m_enabled) {
82 m_surface->attachBuffer(KWayland::Client::Buffer::Ptr());
83 m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
84 } else {
85 m_surface->attachBuffer(m_buffer);
86 m_surface->setScale(std::ceil(m_scale));
87 m_surface->damageBuffer(QRect(0, 0, INT32_MAX, INT32_MAX));
88 m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
89 }
90
91 if (m_pointer) {
92 m_pointer->setCursor(m_surface.get(), m_hotspot);
93 }
94}
95
96WaylandOutput::WaylandOutput(const QString &name, WaylandBackend *backend)
97 : Output(backend)
98 , m_renderLoop(std::make_unique<RenderLoop>(this))
99 , m_surface(backend->display()->compositor()->createSurface())
100 , m_xdgShellSurface(backend->display()->xdgShell()->createSurface(m_surface.get()))
101 , m_backend(backend)
102 , m_cursor(std::make_unique<WaylandCursor>(backend))
103{
104 if (KWayland::Client::XdgDecorationManager *manager = m_backend->display()->xdgDecorationManager()) {
105 m_xdgDecoration.reset(manager->getToplevelDecoration(m_xdgShellSurface.get()));
106 m_xdgDecoration->setMode(KWayland::Client::XdgDecoration::Mode::ServerSide);
107 }
108
110 .name = name,
111 .model = name,
112 .capabilities = Capability::Dpms,
113 });
114
115 m_turnOffTimer.setSingleShot(true);
116 m_turnOffTimer.setInterval(dimAnimationTime());
117 connect(&m_turnOffTimer, &QTimer::timeout, this, [this] {
119 });
120
121 m_configureThrottleTimer.setSingleShot(true);
122 connect(&m_configureThrottleTimer, &QTimer::timeout, this, [this]() {
123 applyConfigure(m_pendingConfigureSize, m_pendingConfigureSerial);
124 });
125
126 connect(m_surface.get(), &KWayland::Client::Surface::frameRendered, this, [this]() {
127 Q_ASSERT(m_frame);
128 const auto primary = Compositor::self()->backend()->primaryLayer(this);
129 m_frame->presented(std::chrono::nanoseconds(1'000'000'000'000 / refreshRate()), std::chrono::steady_clock::now().time_since_epoch(), primary ? primary->queryRenderTime() : std::chrono::nanoseconds::zero(), PresentationMode::VSync);
130 m_frame.reset();
131 });
132
133 updateWindowTitle();
134
135 connect(m_xdgShellSurface.get(), &XdgShellSurface::configureRequested, this, &WaylandOutput::handleConfigure);
136 connect(m_xdgShellSurface.get(), &XdgShellSurface::closeRequested, qApp, &QCoreApplication::quit);
137 connect(this, &WaylandOutput::enabledChanged, this, &WaylandOutput::updateWindowTitle);
138 connect(this, &WaylandOutput::dpmsModeChanged, this, &WaylandOutput::updateWindowTitle);
139}
140
142{
143 m_xdgDecoration.reset();
144 m_xdgShellSurface.reset();
145 m_surface.reset();
146}
147
148void WaylandOutput::framePending(const std::shared_ptr<OutputFrame> &frame)
149{
150 m_frame = frame;
151}
152
154{
155 return m_ready;
156}
157
158KWayland::Client::Surface *WaylandOutput::surface() const
159{
160 return m_surface.get();
161}
162
164{
165 return m_cursor.get();
166}
167
169{
170 return m_backend;
171}
172
174{
175 return m_renderLoop.get();
176}
177
179{
180 if (m_hasPointerLock) {
181 m_cursor->setEnabled(false);
182 return false;
183 } else {
184 m_cursor->setEnabled(Compositor::self()->backend()->cursorLayer(this)->isEnabled());
185 // the layer already takes care of updating the image
186 return true;
187 }
188}
189
190void WaylandOutput::init(const QSize &pixelSize, qreal scale)
191{
192 m_renderLoop->setRefreshRate(s_refreshRate);
193
194 auto mode = std::make_shared<OutputMode>(pixelSize, s_refreshRate);
195
196 State initialState;
197 initialState.modes = {mode};
198 initialState.currentMode = mode;
199 initialState.scale = scale;
200 setState(initialState);
201
202 m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
203}
204
205void WaylandOutput::resize(const QSize &pixelSize)
206{
207 auto mode = std::make_shared<OutputMode>(pixelSize, s_refreshRate);
208
209 State next = m_state;
210 next.modes = {mode};
211 next.currentMode = mode;
212 setState(next);
213
214 Q_EMIT m_backend->outputsQueried();
215}
216
218{
219 if (mode == DpmsMode::Off) {
220 if (!m_turnOffTimer.isActive()) {
221 Q_EMIT aboutToTurnOff(std::chrono::milliseconds(m_turnOffTimer.interval()));
222 m_turnOffTimer.start();
223 }
224 } else {
225 m_turnOffTimer.stop();
226 if (mode != dpmsMode()) {
227 updateDpmsMode(mode);
228 Q_EMIT wakeUp();
229 }
230 }
231}
232
234{
235 State next = m_state;
236 next.dpmsMode = dpmsMode;
237 setState(next);
238}
239
241{
242 State next = m_state;
243 next.enabled = enabled;
244 setState(next);
245}
246
247void WaylandOutput::handleConfigure(const QSize &size, XdgShellSurface::States states, quint32 serial)
248{
249 if (!m_ready) {
250 m_ready = true;
251
252 applyConfigure(size, serial);
253 } else {
254 // Output resizing is a resource intensive task, so the configure events are throttled.
255 m_pendingConfigureSerial = serial;
256 m_pendingConfigureSize = size;
257
258 if (!m_configureThrottleTimer.isActive()) {
259 m_configureThrottleTimer.start(1000000 / m_state.currentMode->refreshRate());
260 }
261 }
262}
263
264void WaylandOutput::applyConfigure(const QSize &size, quint32 serial)
265{
266 m_xdgShellSurface->ackConfigure(serial);
267 if (!size.isEmpty()) {
268 resize(size * scale());
269 }
270}
271
272void WaylandOutput::updateWindowTitle()
273{
274 QString grab;
275 if (m_hasPointerLock) {
276 grab = i18n("Press right control to ungrab pointer");
277 } else if (m_backend->display()->pointerConstraints()) {
278 grab = i18n("Press right control key to grab pointer");
279 }
280
281 QString title = i18nc("Title of nested KWin Wayland with Wayland socket identifier as argument",
282 "KDE Wayland Compositor %1", name());
283
284 if (!isEnabled()) {
285 title += i18n("- Output disabled");
286 } else if (dpmsMode() != DpmsMode::On) {
287 title += i18n("- Output dimmed");
288 } else if (!grab.isEmpty()) {
289 title += QStringLiteral(" — ") + grab;
290 }
291 m_xdgShellSurface->setTitle(title);
292}
293
294void WaylandOutput::lockPointer(Pointer *pointer, bool lock)
295{
296 if (!lock) {
297 const bool surfaceWasLocked = m_pointerLock && m_hasPointerLock;
298 m_pointerLock.reset();
299 m_hasPointerLock = false;
300 if (surfaceWasLocked) {
301 updateWindowTitle();
303 Q_EMIT m_backend->pointerLockChanged(false);
304 }
305 return;
306 }
307
308 Q_ASSERT(!m_pointerLock);
309 m_pointerLock.reset(m_backend->display()->pointerConstraints()->lockPointer(surface(), pointer, nullptr, PointerConstraints::LifeTime::OneShot));
310 if (!m_pointerLock->isValid()) {
311 m_pointerLock.reset();
312 return;
313 }
314 connect(m_pointerLock.get(), &LockedPointer::locked, this, [this]() {
315 m_hasPointerLock = true;
316 updateWindowTitle();
317 updateCursorLayer();
318 Q_EMIT m_backend->pointerLockChanged(true);
319 });
320 connect(m_pointerLock.get(), &LockedPointer::unlocked, this, [this]() {
321 m_pointerLock.reset();
322 m_hasPointerLock = false;
323 updateWindowTitle();
324 updateCursorLayer();
325 Q_EMIT m_backend->pointerLockChanged(false);
326 });
327}
328
329}
330}
331
332#include "moc_wayland_output.cpp"
static Compositor * self()
void enabledChanged()
qreal scale() const
Definition output.cpp:455
void setInformation(const Information &information)
Definition output.cpp:556
void setState(const State &state)
Definition output.cpp:562
static std::chrono::milliseconds dimAnimationTime()
Definition output.cpp:419
State m_state
Definition output.h:477
void aboutToTurnOff(std::chrono::milliseconds time)
QString name
Definition output.h:136
bool isEnabled() const
Definition output.cpp:536
void dpmsModeChanged()
QSize pixelSize() const
Definition output.cpp:485
DpmsMode dpmsMode() const
Definition output.cpp:647
Class encapsulating all Wayland data structures needed by the Egl backend.
WaylandDisplay * display() const
void pointerLockChanged(bool locked)
KWayland::Client::Pointer * pointer() const
WaylandCursor(WaylandBackend *backend)
void update(wl_buffer *buffer, qreal scale, const QPoint &hotspot)
void setPointer(KWayland::Client::Pointer *pointer)
KWayland::Client::PointerConstraints * pointerConstraints() const
KWayland::Client::XdgDecorationManager * xdgDecorationManager() const
void lockPointer(KWayland::Client::Pointer *pointer, bool lock)
void init(const QSize &pixelSize, qreal scale)
WaylandCursor * cursor() const
void resize(const QSize &pixelSize)
void framePending(const std::shared_ptr< OutputFrame > &frame)
WaylandBackend * backend() const
void updateDpmsMode(DpmsMode dpmsMode)
void setDpmsMode(DpmsMode mode) override
WaylandOutput(const QString &name, WaylandBackend *backend)
RenderLoop * renderLoop() const override
KWayland::Client::Surface * surface() const
std::shared_ptr< OutputMode > currentMode
Definition output.h:454
QList< std::shared_ptr< OutputMode > > modes
Definition output.h:453
DpmsMode dpmsMode
Definition output.h:455