Changeset View
Changeset View
Standalone View
Standalone View
src/waylandintegration.cpp
Show All 15 Lines | |||||
16 | * | 16 | * | ||
17 | * Authors: | 17 | * Authors: | ||
18 | * Jan Grulich <jgrulich@redhat.com> | 18 | * Jan Grulich <jgrulich@redhat.com> | ||
19 | */ | 19 | */ | ||
20 | 20 | | |||
21 | #include "waylandintegration.h" | 21 | #include "waylandintegration.h" | ||
22 | #include "waylandintegration_p.h" | 22 | #include "waylandintegration_p.h" | ||
23 | 23 | | |||
24 | | ||||
25 | #include <QDBusArgument> | 24 | #include <QDBusArgument> | ||
26 | #include <QDBusMetaType> | 25 | #include <QDBusMetaType> | ||
27 | 26 | | |||
28 | #include <QEventLoop> | 27 | #include <QEventLoop> | ||
29 | #include <QLoggingCategory> | 28 | #include <QLoggingCategory> | ||
30 | #include <QThread> | 29 | #include <QThread> | ||
31 | #include <QTimer> | 30 | #include <QTimer> | ||
31 | #include <KNotification> | ||||
32 | 32 | | |||
33 | #include <QImage> | 33 | #include <QImage> | ||
34 | 34 | | |||
35 | #include <KLocalizedString> | 35 | #include <KLocalizedString> | ||
36 | 36 | | |||
37 | // KWayland | 37 | // KWayland | ||
38 | #include <KWayland/Client/connection_thread.h> | 38 | #include <KWayland/Client/connection_thread.h> | ||
39 | #include <KWayland/Client/event_queue.h> | 39 | #include <KWayland/Client/event_queue.h> | ||
40 | #include <KWayland/Client/registry.h> | 40 | #include <KWayland/Client/registry.h> | ||
41 | #include <KWayland/Client/plasmawindowmanagement.h> | 41 | #include <KWayland/Client/plasmawindowmanagement.h> | ||
42 | 42 | | |||
43 | // system | 43 | // system | ||
44 | #include <fcntl.h> | 44 | #include <fcntl.h> | ||
45 | #include <unistd.h> | 45 | #include <unistd.h> | ||
46 | 46 | | |||
47 | #if HAVE_PIPEWIRE_SUPPORT | | |||
48 | #include "screencaststream.h" | | |||
49 | | ||||
50 | #include <KWayland/Client/fakeinput.h> | 47 | #include <KWayland/Client/fakeinput.h> | ||
51 | #include <KWayland/Client/output.h> | 48 | #include <KWayland/Client/output.h> | ||
52 | #include <KWayland/Client/remote_access.h> | 49 | #include <KWayland/Client/screencasting.h> | ||
53 | #endif | | |||
54 | 50 | | |||
55 | Q_LOGGING_CATEGORY(XdgDesktopPortalKdeWaylandIntegration, "xdp-kde-wayland-integration") | 51 | Q_LOGGING_CATEGORY(XdgDesktopPortalKdeWaylandIntegration, "xdp-kde-wayland-integration") | ||
56 | 52 | | |||
57 | Q_GLOBAL_STATIC(WaylandIntegration::WaylandIntegrationPrivate, globalWaylandIntegration) | 53 | Q_GLOBAL_STATIC(WaylandIntegration::WaylandIntegrationPrivate, globalWaylandIntegration) | ||
58 | 54 | | |||
59 | void WaylandIntegration::init() | 55 | void WaylandIntegration::init() | ||
60 | { | 56 | { | ||
61 | #if HAVE_PIPEWIRE_SUPPORT | | |||
62 | globalWaylandIntegration->initDrm(); | | |||
63 | #endif | | |||
64 | globalWaylandIntegration->initWayland(); | 57 | globalWaylandIntegration->initWayland(); | ||
65 | } | 58 | } | ||
66 | 59 | | |||
67 | #if HAVE_PIPEWIRE_SUPPORT | | |||
68 | void WaylandIntegration::authenticate() | 60 | void WaylandIntegration::authenticate() | ||
69 | { | 61 | { | ||
70 | globalWaylandIntegration->authenticate(); | 62 | globalWaylandIntegration->authenticate(); | ||
71 | } | 63 | } | ||
72 | 64 | | |||
73 | bool WaylandIntegration::isEGLInitialized() | | |||
74 | { | | |||
75 | return globalWaylandIntegration->isEGLInitialized(); | | |||
76 | } | | |||
77 | | ||||
78 | bool WaylandIntegration::isStreamingEnabled() | 65 | bool WaylandIntegration::isStreamingEnabled() | ||
79 | { | 66 | { | ||
80 | return globalWaylandIntegration->isStreamingEnabled(); | 67 | return globalWaylandIntegration->isStreamingEnabled(); | ||
81 | } | 68 | } | ||
82 | 69 | | |||
83 | void WaylandIntegration::startStreamingInput() | 70 | void WaylandIntegration::startStreamingInput() | ||
84 | { | 71 | { | ||
85 | globalWaylandIntegration->startStreamingInput(); | 72 | globalWaylandIntegration->startStreamingInput(); | ||
86 | } | 73 | } | ||
87 | 74 | | |||
88 | bool WaylandIntegration::startStreaming(quint32 outputName) | 75 | bool WaylandIntegration::startStreaming(const KWayland::Client::ScreencastingSource &source) | ||
89 | { | 76 | { | ||
90 | return globalWaylandIntegration->startStreaming(outputName); | 77 | return globalWaylandIntegration->startStreaming(source); | ||
91 | } | 78 | } | ||
92 | 79 | | |||
93 | void WaylandIntegration::stopStreaming() | 80 | void WaylandIntegration::stopAllStreaming() | ||
94 | { | 81 | { | ||
95 | globalWaylandIntegration->stopStreaming(); | 82 | globalWaylandIntegration->stopAllStreaming(); | ||
96 | } | 83 | } | ||
97 | 84 | | |||
98 | void WaylandIntegration::requestPointerButtonPress(quint32 linuxButton) | 85 | void WaylandIntegration::requestPointerButtonPress(quint32 linuxButton) | ||
99 | { | 86 | { | ||
100 | globalWaylandIntegration->requestPointerButtonPress(linuxButton); | 87 | globalWaylandIntegration->requestPointerButtonPress(linuxButton); | ||
101 | } | 88 | } | ||
102 | 89 | | |||
103 | void WaylandIntegration::requestPointerButtonRelease(quint32 linuxButton) | 90 | void WaylandIntegration::requestPointerButtonRelease(quint32 linuxButton) | ||
Show All 16 Lines | 106 | { | |||
120 | globalWaylandIntegration->requestPointerAxisDiscrete(axis, delta); | 107 | globalWaylandIntegration->requestPointerAxisDiscrete(axis, delta); | ||
121 | } | 108 | } | ||
122 | 109 | | |||
123 | void WaylandIntegration::requestKeyboardKeycode(int keycode, bool state) | 110 | void WaylandIntegration::requestKeyboardKeycode(int keycode, bool state) | ||
124 | { | 111 | { | ||
125 | globalWaylandIntegration->requestKeyboardKeycode(keycode, state); | 112 | globalWaylandIntegration->requestKeyboardKeycode(keycode, state); | ||
126 | } | 113 | } | ||
127 | 114 | | |||
128 | WaylandIntegration::EGLStruct WaylandIntegration::egl() | | |||
129 | { | | |||
130 | return globalWaylandIntegration->egl(); | | |||
131 | } | | |||
132 | | ||||
133 | QMap<quint32, WaylandIntegration::WaylandOutput> WaylandIntegration::screens() | 115 | QMap<quint32, WaylandIntegration::WaylandOutput> WaylandIntegration::screens() | ||
134 | { | 116 | { | ||
135 | return globalWaylandIntegration->screens(); | 117 | return globalWaylandIntegration->screens(); | ||
136 | } | 118 | } | ||
137 | 119 | | |||
138 | QVariant WaylandIntegration::streams() | 120 | QVariant WaylandIntegration::streams() | ||
139 | { | 121 | { | ||
140 | return globalWaylandIntegration->streams(); | 122 | return globalWaylandIntegration->streams(); | ||
141 | } | 123 | } | ||
142 | 124 | | |||
143 | const char * WaylandIntegration::formatGLError(GLenum err) | | |||
144 | { | | |||
145 | switch(err) { | | |||
146 | case GL_NO_ERROR: | | |||
147 | return "GL_NO_ERROR"; | | |||
148 | case GL_INVALID_ENUM: | | |||
149 | return "GL_INVALID_ENUM"; | | |||
150 | case GL_INVALID_VALUE: | | |||
151 | return "GL_INVALID_VALUE"; | | |||
152 | case GL_INVALID_OPERATION: | | |||
153 | return "GL_INVALID_OPERATION"; | | |||
154 | case GL_STACK_OVERFLOW: | | |||
155 | return "GL_STACK_OVERFLOW"; | | |||
156 | case GL_STACK_UNDERFLOW: | | |||
157 | return "GL_STACK_UNDERFLOW"; | | |||
158 | case GL_OUT_OF_MEMORY: | | |||
159 | return "GL_OUT_OF_MEMORY"; | | |||
160 | default: | | |||
161 | return (QLatin1String("0x") + QString::number(err, 16)).toLocal8Bit().constData(); | | |||
162 | } | | |||
163 | } | | |||
164 | | ||||
165 | // Thank you kscreen | 125 | // Thank you kscreen | ||
166 | void WaylandIntegration::WaylandOutput::setOutputType(const QString &type) | 126 | void WaylandIntegration::WaylandOutput::setOutputType(const QString &type) | ||
167 | { | 127 | { | ||
168 | const auto embedded = { QLatin1String("LVDS"), | 128 | const auto embedded = { QLatin1String("LVDS"), | ||
169 | QLatin1String("IDP"), | 129 | QLatin1String("IDP"), | ||
170 | QLatin1String("EDP"), | 130 | QLatin1String("EDP"), | ||
171 | QLatin1String("LCD") }; | 131 | QLatin1String("LCD") }; | ||
172 | 132 | | |||
173 | for (const QLatin1String &pre : embedded) { | 133 | for (const QLatin1String &pre : embedded) { | ||
174 | if (type.toUpper().startsWith(pre)) { | 134 | if (type.startsWith(pre, Qt::CaseInsensitive)) { | ||
175 | m_outputType = OutputType::Laptop; | 135 | m_outputType = OutputType::Laptop; | ||
176 | return; | 136 | return; | ||
177 | } | 137 | } | ||
178 | } | 138 | } | ||
179 | 139 | | |||
180 | if (type.contains(QLatin1String("VGA")) || type.contains(QLatin1String("DVI")) || type.contains(QLatin1String("HDMI")) || type.contains(QLatin1String("Panel")) || | 140 | if (type.contains(QLatin1String("VGA")) || type.contains(QLatin1String("DVI")) || type.contains(QLatin1String("HDMI")) || type.contains(QLatin1String("Panel")) || | ||
181 | type.contains(QLatin1String("DisplayPort")) || type.startsWith(QLatin1String("DP")) || type.contains(QLatin1String("unknown"))) { | 141 | type.contains(QLatin1String("DisplayPort")) || type.startsWith(QLatin1String("DP")) || type.contains(QLatin1String("unknown"))) { | ||
182 | m_outputType = OutputType::Monitor; | 142 | m_outputType = OutputType::Monitor; | ||
Show All 31 Lines | 171 | { | |||
214 | arg << stream.map; | 174 | arg << stream.map; | ||
215 | arg.endStructure(); | 175 | arg.endStructure(); | ||
216 | 176 | | |||
217 | return arg; | 177 | return arg; | ||
218 | } | 178 | } | ||
219 | 179 | | |||
220 | Q_DECLARE_METATYPE(WaylandIntegration::WaylandIntegrationPrivate::Stream) | 180 | Q_DECLARE_METATYPE(WaylandIntegration::WaylandIntegrationPrivate::Stream) | ||
221 | Q_DECLARE_METATYPE(WaylandIntegration::WaylandIntegrationPrivate::Streams) | 181 | Q_DECLARE_METATYPE(WaylandIntegration::WaylandIntegrationPrivate::Streams) | ||
222 | #endif | | |||
223 | 182 | | |||
224 | KWayland::Client::PlasmaWindowManagement * WaylandIntegration::plasmaWindowManagement() | 183 | KWayland::Client::PlasmaWindowManagement * WaylandIntegration::plasmaWindowManagement() | ||
225 | { | 184 | { | ||
226 | return globalWaylandIntegration->plasmaWindowManagement(); | 185 | return globalWaylandIntegration->plasmaWindowManagement(); | ||
227 | } | 186 | } | ||
228 | 187 | | |||
229 | WaylandIntegration::WaylandIntegration * WaylandIntegration::waylandIntegration() | 188 | WaylandIntegration::WaylandIntegration * WaylandIntegration::waylandIntegration() | ||
230 | { | 189 | { | ||
231 | return globalWaylandIntegration; | 190 | return globalWaylandIntegration; | ||
232 | } | 191 | } | ||
233 | 192 | | |||
234 | WaylandIntegration::WaylandIntegrationPrivate::WaylandIntegrationPrivate() | 193 | WaylandIntegration::WaylandIntegrationPrivate::WaylandIntegrationPrivate() | ||
235 | : WaylandIntegration() | 194 | : WaylandIntegration() | ||
236 | , m_registryInitialized(false) | 195 | , m_registryInitialized(false) | ||
237 | , m_connection(nullptr) | 196 | , m_connection(nullptr) | ||
238 | , m_queue(nullptr) | 197 | , m_queue(nullptr) | ||
239 | , m_registry(nullptr) | 198 | , m_registry(nullptr) | ||
240 | #if HAVE_PIPEWIRE_SUPPORT | | |||
241 | , m_fakeInput(nullptr) | 199 | , m_fakeInput(nullptr) | ||
242 | , m_remoteAccessManager(nullptr) | 200 | , m_screencasting(nullptr) | ||
243 | #endif | | |||
244 | { | 201 | { | ||
245 | #if HAVE_PIPEWIRE_SUPPORT | | |||
246 | qDBusRegisterMetaType<WaylandIntegrationPrivate::Stream>(); | 202 | qDBusRegisterMetaType<WaylandIntegrationPrivate::Stream>(); | ||
247 | qDBusRegisterMetaType<WaylandIntegrationPrivate::Streams>(); | 203 | qDBusRegisterMetaType<WaylandIntegrationPrivate::Streams>(); | ||
248 | #endif | | |||
249 | } | | |||
250 | | ||||
251 | WaylandIntegration::WaylandIntegrationPrivate::~WaylandIntegrationPrivate() | | |||
252 | { | | |||
253 | #if HAVE_PIPEWIRE_SUPPORT | | |||
254 | if (m_remoteAccessManager) { | | |||
255 | m_remoteAccessManager->destroy(); | | |||
256 | } | 204 | } | ||
257 | 205 | | |||
258 | if (m_gbmDevice) { | 206 | WaylandIntegration::WaylandIntegrationPrivate::~WaylandIntegrationPrivate() = default; | ||
259 | gbm_device_destroy(m_gbmDevice); | | |||
260 | } | | |||
261 | #endif | | |||
262 | } | | |||
263 | | ||||
264 | #if HAVE_PIPEWIRE_SUPPORT | | |||
265 | bool WaylandIntegration::WaylandIntegrationPrivate::isEGLInitialized() const | | |||
266 | { | | |||
267 | return m_eglInitialized; | | |||
268 | } | | |||
269 | 207 | | |||
270 | bool WaylandIntegration::WaylandIntegrationPrivate::isStreamingEnabled() const | 208 | bool WaylandIntegration::WaylandIntegrationPrivate::isStreamingEnabled() const | ||
271 | { | 209 | { | ||
272 | return m_streamingEnabled; | 210 | return m_streams.isEmpty(); | ||
273 | } | | |||
274 | | ||||
275 | void WaylandIntegration::WaylandIntegrationPrivate::bindOutput(int outputName, int outputVersion) | | |||
276 | { | | |||
277 | KWayland::Client::Output *output = new KWayland::Client::Output(this); | | |||
278 | output->setup(m_registry->bindOutput(outputName, outputVersion)); | | |||
279 | m_bindOutputs << output; | | |||
280 | } | 211 | } | ||
281 | 212 | | |||
282 | void WaylandIntegration::WaylandIntegrationPrivate::startStreamingInput() | 213 | void WaylandIntegration::WaylandIntegrationPrivate::startStreamingInput() | ||
283 | { | 214 | { | ||
284 | m_streamInput = true; | 215 | m_streamInput = true; | ||
285 | } | 216 | } | ||
286 | 217 | | |||
287 | bool WaylandIntegration::WaylandIntegrationPrivate::startStreaming(quint32 outputName) | 218 | QVector<KWayland::Client::ScreencastingSource> WaylandIntegration::screencastingSources() | ||
288 | { | 219 | { | ||
289 | WaylandOutput output = m_outputMap.value(outputName); | 220 | return globalWaylandIntegration->videoStreamingSources(); | ||
290 | m_streamedScreenPosition = output.globalPosition(); | 221 | } | ||
291 | 222 | | |||
292 | m_stream = new ScreenCastStream(output.resolution()); | 223 | bool WaylandIntegration::WaylandIntegrationPrivate::startStreaming(const KWayland::Client::ScreencastingSource &source) | ||
293 | m_stream->init(); | 224 | { | ||
225 | m_streamedScreenPosition = source.geometry().topLeft(); | ||||
294 | 226 | | |||
295 | connect(m_stream, &ScreenCastStream::startStreaming, this, [this, output] { | 227 | m_screencasting->create(source); | ||
296 | m_streamingEnabled = true; | 228 | QEventLoop loop; | ||
297 | startStreamingInput(); | 229 | bool streamReady = false; | ||
298 | bindOutput(output.waylandOutputName(), output.waylandOutputVersion()); | 230 | connect(m_screencasting, &KWayland::Client::Screencasting::failed, this, [&] (const KWayland::Client::ScreencastingSource &newSource, const QString &error) { | ||
299 | }); | 231 | if (source != newSource) { | ||
232 | return; | ||||
233 | } | ||||
234 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "failed to start streaming" << source.description() << error; | ||||
300 | 235 | | |||
301 | connect(m_stream, &ScreenCastStream::stopStreaming, this, &WaylandIntegrationPrivate::stopStreaming); | 236 | KNotification *notification = new KNotification(QStringLiteral("screencastfailure"), KNotification::CloseOnTimeout); | ||
237 | notification->setTitle(i18n("Failed to start screencasting")); | ||||
238 | notification->setText(error); | ||||
239 | notification->setIconName(QStringLiteral("dialog-error")); | ||||
240 | notification->sendEvent(); | ||||
302 | 241 | | |||
303 | bool streamReady = false; | 242 | streamReady = false; | ||
304 | QEventLoop loop; | | |||
305 | connect(m_stream, &ScreenCastStream::streamReady, this, [&loop, &streamReady] { | | |||
306 | loop.quit(); | 243 | loop.quit(); | ||
244 | }); | ||||
245 | connect(m_screencasting, &KWayland::Client::Screencasting::created, this, [&] (const KWayland::Client::ScreencastingSource& newSource, uint32_t nodeid) { | ||||
246 | if (source != newSource) { | ||||
247 | return; | ||||
248 | } | ||||
249 | m_streams.append({nodeid, {{QLatin1String("size"), source.geometry().size()}}}); | ||||
250 | startStreamingInput(); | ||||
251 | | ||||
307 | streamReady = true; | 252 | streamReady = true; | ||
253 | loop.quit(); | ||||
308 | }); | 254 | }); | ||
309 | 255 | | |||
310 | // HACK wait for stream to be ready | 256 | connect(m_screencasting, &KWayland::Client::Screencasting::closed, this, &WaylandIntegrationPrivate::stopStreaming); | ||
jgrulich: This has been removed, but I think it will still be needed. | |||||
311 | QTimer::singleShot(3000, &loop, &QEventLoop::quit); | | |||
312 | loop.exec(); | | |||
313 | 257 | | |||
314 | disconnect(m_stream, &ScreenCastStream::streamReady, this, nullptr); | 258 | disconnect(m_screencasting, &KWayland::Client::Screencasting::created, this, nullptr); | ||
315 | 259 | | |||
316 | if (!streamReady) { | 260 | return streamReady; | ||
317 | if (m_stream) { | | |||
318 | delete m_stream; | | |||
319 | m_stream = nullptr; | | |||
320 | } | | |||
321 | return false; | | |||
322 | } | 261 | } | ||
323 | 262 | | |||
324 | // TODO support multiple outputs | 263 | void WaylandIntegration::WaylandIntegrationPrivate::Stream::close() | ||
325 | 264 | { | |||
326 | if (m_registry->hasInterface(KWayland::Client::Registry::Interface::RemoteAccessManager)) { | 265 | globalWaylandIntegration->m_screencasting->close(nodeId); | ||
327 | KWayland::Client::Registry::AnnouncedInterface interface = m_registry->interface(KWayland::Client::Registry::Interface::RemoteAccessManager); | | |||
328 | if (!interface.name && !interface.version) { | | |||
329 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Failed to start streaming: remote access manager interface is not initialized yet"; | | |||
330 | return false; | | |||
331 | } | | |||
332 | m_remoteAccessManager = m_registry->createRemoteAccessManager(interface.name, interface.version); | | |||
333 | connect(m_remoteAccessManager, &KWayland::Client::RemoteAccessManager::bufferReady, this, [this] (const void *output, const KWayland::Client::RemoteBuffer * rbuf) { | | |||
334 | Q_UNUSED(output); | | |||
335 | connect(rbuf, &KWayland::Client::RemoteBuffer::parametersObtained, this, [this, rbuf] { | | |||
336 | processBuffer(rbuf); | | |||
337 | }); | | |||
338 | }); | | |||
339 | m_output = output.waylandOutputName(); | | |||
340 | return true; | | |||
341 | } | 266 | } | ||
342 | 267 | | |||
343 | if (m_stream) { | 268 | void WaylandIntegration::WaylandIntegrationPrivate::stopAllStreaming() | ||
344 | delete m_stream; | 269 | { | ||
345 | m_stream = nullptr; | 270 | for (auto & stream : m_streams) { | ||
271 | stream.close(); | ||||
346 | } | 272 | } | ||
273 | m_streams.clear(); | ||||
347 | 274 | | |||
348 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Failed to start streaming: no remote access manager interface"; | 275 | m_streamInput = false; | ||
349 | return false; | 276 | // First unbound outputs and destroy remote access manager so we no longer receive buffers | ||
350 | } | 277 | } | ||
351 | 278 | | |||
352 | void WaylandIntegration::WaylandIntegrationPrivate::stopStreaming() | 279 | void WaylandIntegration::WaylandIntegrationPrivate::stopStreaming(uint32_t nodeid) | ||
353 | { | 280 | { | ||
354 | m_streamInput = false; | 281 | for(auto it = m_streams.begin(), itEnd = m_streams.end(); it != itEnd; ++it) { | ||
355 | if (m_streamingEnabled) { | 282 | if (it->nodeId == nodeid) { | ||
356 | m_streamingEnabled = false; | 283 | m_streams.erase(it); | ||
357 | 284 | break; | |||
358 | // First unbound outputs and destroy remote access manager so we no longer receive buffers | | |||
359 | if (m_remoteAccessManager) { | | |||
360 | m_remoteAccessManager->release(); | | |||
361 | m_remoteAccessManager->destroy(); | | |||
362 | } | | |||
363 | qDeleteAll(m_bindOutputs); | | |||
364 | m_bindOutputs.clear(); | | |||
365 | | ||||
366 | if (m_stream) { | | |||
367 | delete m_stream; | | |||
368 | m_stream = nullptr; | | |||
369 | } | 285 | } | ||
370 | } | 286 | } | ||
287 | | ||||
288 | if (m_streams.isEmpty()) { | ||||
289 | stopAllStreaming(); | ||||
290 | } | ||||
371 | } | 291 | } | ||
372 | 292 | | |||
373 | void WaylandIntegration::WaylandIntegrationPrivate::requestPointerButtonPress(quint32 linuxButton) | 293 | void WaylandIntegration::WaylandIntegrationPrivate::requestPointerButtonPress(quint32 linuxButton) | ||
374 | { | 294 | { | ||
375 | if (m_streamInput && m_fakeInput) { | 295 | if (m_streamInput && m_fakeInput) { | ||
376 | m_fakeInput->requestPointerButtonPress(linuxButton); | 296 | m_fakeInput->requestPointerButtonPress(linuxButton); | ||
377 | } | 297 | } | ||
378 | } | 298 | } | ||
Show All 32 Lines | 330 | if (m_streamInput && m_fakeInput) { | |||
411 | if (state) { | 331 | if (state) { | ||
412 | m_fakeInput->requestKeyboardKeyPress(keycode); | 332 | m_fakeInput->requestKeyboardKeyPress(keycode); | ||
413 | } else { | 333 | } else { | ||
414 | m_fakeInput->requestKeyboardKeyRelease(keycode); | 334 | m_fakeInput->requestKeyboardKeyRelease(keycode); | ||
415 | } | 335 | } | ||
416 | } | 336 | } | ||
417 | } | 337 | } | ||
418 | 338 | | |||
419 | WaylandIntegration::EGLStruct WaylandIntegration::WaylandIntegrationPrivate::egl() | | |||
420 | { | | |||
421 | return m_egl; | | |||
422 | } | | |||
423 | | ||||
424 | QMap<quint32, WaylandIntegration::WaylandOutput> WaylandIntegration::WaylandIntegrationPrivate::screens() | 339 | QMap<quint32, WaylandIntegration::WaylandOutput> WaylandIntegration::WaylandIntegrationPrivate::screens() | ||
425 | { | 340 | { | ||
426 | return m_outputMap; | 341 | return m_outputMap; | ||
427 | } | 342 | } | ||
428 | 343 | | |||
429 | QVariant WaylandIntegration::WaylandIntegrationPrivate::streams() | 344 | QVariant WaylandIntegration::WaylandIntegrationPrivate::streams() | ||
430 | { | 345 | { | ||
431 | Stream stream; | 346 | return QVariant::fromValue<WaylandIntegrationPrivate::Streams>(m_streams); | ||
432 | stream.nodeId = m_stream->nodeId(); | | |||
433 | stream.map = QVariantMap({{QLatin1String("size"), m_outputMap.value(m_output).resolution()}}); | | |||
434 | return QVariant::fromValue<WaylandIntegrationPrivate::Streams>({stream}); | | |||
435 | } | | |||
436 | | ||||
437 | void WaylandIntegration::WaylandIntegrationPrivate::initDrm() | | |||
438 | { | | |||
439 | m_drmFd = open("/dev/dri/renderD128", O_RDWR); | | |||
440 | | ||||
441 | if (m_drmFd == -1) { | | |||
442 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Cannot open render node: " << strerror(errno); | | |||
443 | return; | | |||
444 | } | | |||
445 | | ||||
446 | m_gbmDevice = gbm_create_device(m_drmFd); | | |||
447 | | ||||
448 | if (!m_gbmDevice) { | | |||
449 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Cannot create GBM device: " << strerror(errno); | | |||
450 | } | | |||
451 | | ||||
452 | initEGL(); | | |||
453 | } | 347 | } | ||
454 | 348 | | |||
455 | void WaylandIntegration::WaylandIntegrationPrivate::initEGL() | 349 | QVector<KWayland::Client::ScreencastingSource> WaylandIntegration::WaylandIntegrationPrivate::videoStreamingSources() | ||
456 | { | 350 | { | ||
457 | // Get the list of client extensions | 351 | return m_screencasting->sources(); | ||
458 | const char* clientExtensionsCString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); | | |||
459 | const QByteArray clientExtensionsString = QByteArray::fromRawData(clientExtensionsCString, qstrlen(clientExtensionsCString)); | | |||
460 | if (clientExtensionsString.isEmpty()) { | | |||
461 | // If eglQueryString() returned NULL, the implementation doesn't support | | |||
462 | // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error. | | |||
463 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "No client extensions defined! " << formatGLError(eglGetError()); | | |||
464 | return; | | |||
465 | } | | |||
466 | | ||||
467 | m_egl.extensions = clientExtensionsString.split(' '); | | |||
468 | | ||||
469 | // Use eglGetPlatformDisplayEXT() to get the display pointer | | |||
470 | // if the implementation supports it. | | |||
471 | if (!m_egl.extensions.contains(QByteArrayLiteral("EGL_EXT_platform_base")) || | | |||
472 | !m_egl.extensions.contains(QByteArrayLiteral("EGL_MESA_platform_gbm"))) { | | |||
473 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "One of required EGL extensions is missing"; | | |||
474 | return; | | |||
475 | } | | |||
476 | | ||||
477 | m_egl.display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, m_gbmDevice, nullptr); | | |||
478 | | ||||
479 | if (m_egl.display == EGL_NO_DISPLAY) { | | |||
480 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Error during obtaining EGL display: " << formatGLError(eglGetError()); | | |||
481 | return; | | |||
482 | } | | |||
483 | | ||||
484 | EGLint major, minor; | | |||
485 | if (eglInitialize(m_egl.display, &major, &minor) == EGL_FALSE) { | | |||
486 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Error during eglInitialize: " << formatGLError(eglGetError()); | | |||
487 | return; | | |||
488 | } | | |||
489 | | ||||
490 | if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { | | |||
491 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "bind OpenGL API failed"; | | |||
492 | return; | | |||
493 | } | | |||
494 | | ||||
495 | m_egl.context = eglCreateContext(m_egl.display, nullptr, EGL_NO_CONTEXT, nullptr); | | |||
496 | | ||||
497 | if (m_egl.context == EGL_NO_CONTEXT) { | | |||
498 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Couldn't create EGL context: " << formatGLError(eglGetError()); | | |||
499 | return; | | |||
500 | } | | |||
501 | | ||||
502 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Egl initialization succeeded"; | | |||
503 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << QStringLiteral("EGL version: %1.%2").arg(major).arg(minor); | | |||
504 | | ||||
505 | m_eglInitialized = true; | | |||
506 | } | 352 | } | ||
507 | 353 | | |||
508 | void WaylandIntegration::WaylandIntegrationPrivate::authenticate() | 354 | void WaylandIntegration::WaylandIntegrationPrivate::authenticate() | ||
509 | { | 355 | { | ||
510 | if (!m_waylandAuthenticationRequested) { | 356 | if (!m_waylandAuthenticationRequested) { | ||
511 | m_fakeInput->authenticate(i18n("xdg-desktop-portals-kde"), i18n("Remote desktop")); | 357 | m_fakeInput->authenticate(i18n("xdg-desktop-portals-kde"), i18n("Remote desktop")); | ||
512 | m_waylandAuthenticationRequested = true; | 358 | m_waylandAuthenticationRequested = true; | ||
513 | } | 359 | } | ||
514 | } | 360 | } | ||
515 | 361 | | |||
516 | #endif | | |||
517 | | ||||
518 | KWayland::Client::PlasmaWindowManagement * WaylandIntegration::WaylandIntegrationPrivate::plasmaWindowManagement() | 362 | KWayland::Client::PlasmaWindowManagement * WaylandIntegration::WaylandIntegrationPrivate::plasmaWindowManagement() | ||
519 | { | 363 | { | ||
520 | return m_windowManagement; | 364 | return m_windowManagement; | ||
521 | } | 365 | } | ||
522 | 366 | | |||
523 | void WaylandIntegration::WaylandIntegrationPrivate::initWayland() | 367 | void WaylandIntegration::WaylandIntegrationPrivate::initWayland() | ||
524 | { | 368 | { | ||
525 | m_thread = new QThread(this); | 369 | m_thread = new QThread(this); | ||
Show All 24 Lines | 392 | connect(m_connection, &KWayland::Client::ConnectionThread::failed, this, [this] { | |||
550 | m_thread->wait(); | 394 | m_thread->wait(); | ||
551 | }); | 395 | }); | ||
552 | 396 | | |||
553 | m_thread->start(); | 397 | m_thread->start(); | ||
554 | m_connection->moveToThread(m_thread); | 398 | m_connection->moveToThread(m_thread); | ||
555 | m_connection->initConnection(); | 399 | m_connection->initConnection(); | ||
556 | } | 400 | } | ||
557 | 401 | | |||
558 | #if HAVE_PIPEWIRE_SUPPORT | | |||
559 | void WaylandIntegration::WaylandIntegrationPrivate::addOutput(quint32 name, quint32 version) | 402 | void WaylandIntegration::WaylandIntegrationPrivate::addOutput(quint32 name, quint32 version) | ||
560 | { | 403 | { | ||
561 | KWayland::Client::Output *output = new KWayland::Client::Output(this); | 404 | KWayland::Client::Output *output = new KWayland::Client::Output(this); | ||
562 | output->setup(m_registry->bindOutput(name, version)); | 405 | output->setup(m_registry->bindOutput(name, version)); | ||
563 | 406 | | |||
564 | connect(output, &KWayland::Client::Output::changed, this, [this, name, version, output] () { | 407 | connect(output, &KWayland::Client::Output::changed, this, [this, name, version, output] () { | ||
565 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Adding output:"; | 408 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Adding output:"; | ||
566 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " manufacturer: " << output->manufacturer(); | 409 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " manufacturer: " << output->manufacturer(); | ||
Show All 18 Lines | |||||
585 | void WaylandIntegration::WaylandIntegrationPrivate::removeOutput(quint32 name) | 428 | void WaylandIntegration::WaylandIntegrationPrivate::removeOutput(quint32 name) | ||
586 | { | 429 | { | ||
587 | WaylandOutput output = m_outputMap.take(name); | 430 | WaylandOutput output = m_outputMap.take(name); | ||
588 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Removing output:"; | 431 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Removing output:"; | ||
589 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " manufacturer: " << output.manufacturer(); | 432 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " manufacturer: " << output.manufacturer(); | ||
590 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " model: " << output.model(); | 433 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " model: " << output.model(); | ||
591 | } | 434 | } | ||
592 | 435 | | |||
593 | void WaylandIntegration::WaylandIntegrationPrivate::processBuffer(const KWayland::Client::RemoteBuffer* rbuf) | | |||
594 | { | | |||
595 | QScopedPointer<const KWayland::Client::RemoteBuffer> guard(rbuf); | | |||
596 | | ||||
597 | auto gbmHandle = rbuf->fd(); | | |||
598 | auto width = rbuf->width(); | | |||
599 | auto height = rbuf->height(); | | |||
600 | auto stride = rbuf->stride(); | | |||
601 | auto format = rbuf->format(); | | |||
602 | | ||||
603 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << QStringLiteral("Incoming GBM fd %1, %2x%3, stride %4, fourcc 0x%5").arg(gbmHandle).arg(width).arg(height).arg(stride).arg(QString::number(format, 16)); | | |||
604 | | ||||
605 | if (!m_streamingEnabled) { | | |||
606 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Streaming is disabled"; | | |||
607 | close(gbmHandle); | | |||
608 | return; | | |||
609 | } | | |||
610 | | ||||
611 | if (m_lastFrameTime.isValid() && | | |||
612 | m_lastFrameTime.msecsTo(QDateTime::currentDateTime()) < (1000 / m_stream->framerate())) { | | |||
613 | close(gbmHandle); | | |||
614 | return; | | |||
615 | } | | |||
616 | | ||||
617 | if (!gbm_device_is_format_supported(m_gbmDevice, format, GBM_BO_USE_SCANOUT)) { | | |||
618 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Failed to process buffer: GBM format is not supported by device!"; | | |||
619 | close(gbmHandle); | | |||
620 | return; | | |||
621 | } | | |||
622 | | ||||
623 | // import GBM buffer that was passed from KWin | | |||
624 | gbm_import_fd_data importInfo = {gbmHandle, width, height, stride, format}; | | |||
625 | gbm_bo *imported = gbm_bo_import(m_gbmDevice, GBM_BO_IMPORT_FD, &importInfo, GBM_BO_USE_SCANOUT); | | |||
626 | if (!imported) { | | |||
627 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Failed to process buffer: Cannot import passed GBM fd - " << strerror(errno); | | |||
628 | close(gbmHandle); | | |||
629 | return; | | |||
630 | } | | |||
631 | | ||||
632 | if (m_stream->recordFrame(imported, width, height, stride)) { | | |||
633 | m_lastFrameTime = QDateTime::currentDateTime(); | | |||
634 | } | | |||
635 | | ||||
636 | gbm_bo_destroy(imported); | | |||
637 | close(gbmHandle); | | |||
638 | } | | |||
639 | #endif | | |||
640 | | ||||
641 | void WaylandIntegration::WaylandIntegrationPrivate::setupRegistry() | 436 | void WaylandIntegration::WaylandIntegrationPrivate::setupRegistry() | ||
642 | { | 437 | { | ||
643 | m_queue = new KWayland::Client::EventQueue(this); | 438 | m_queue = new KWayland::Client::EventQueue(this); | ||
644 | m_queue->setup(m_connection); | 439 | m_queue->setup(m_connection); | ||
645 | 440 | | |||
646 | m_registry = new KWayland::Client::Registry(this); | 441 | m_registry = new KWayland::Client::Registry(this); | ||
647 | 442 | | |||
648 | #if HAVE_PIPEWIRE_SUPPORT | | |||
649 | connect(m_registry, &KWayland::Client::Registry::fakeInputAnnounced, this, [this] (quint32 name, quint32 version) { | 443 | connect(m_registry, &KWayland::Client::Registry::fakeInputAnnounced, this, [this] (quint32 name, quint32 version) { | ||
650 | m_fakeInput = m_registry->createFakeInput(name, version, this); | 444 | m_fakeInput = m_registry->createFakeInput(name, version, this); | ||
651 | }); | 445 | }); | ||
652 | connect(m_registry, &KWayland::Client::Registry::outputAnnounced, this, &WaylandIntegrationPrivate::addOutput); | 446 | connect(m_registry, &KWayland::Client::Registry::outputAnnounced, this, &WaylandIntegrationPrivate::addOutput); | ||
653 | connect(m_registry, &KWayland::Client::Registry::outputRemoved, this, &WaylandIntegrationPrivate::removeOutput); | 447 | connect(m_registry, &KWayland::Client::Registry::outputRemoved, this, &WaylandIntegrationPrivate::removeOutput); | ||
654 | #endif | | |||
655 | 448 | | |||
449 | connect(m_registry, &KWayland::Client::Registry::screencastingAnnounced, this, [this] (quint32 name, quint32 version) { | ||||
450 | qDebug() << "screen!"; | ||||
davidedmundson: leftover | |||||
451 | m_screencasting = m_registry->createScreencasting(name, version, this); | ||||
452 | }); | ||||
656 | connect(m_registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, this, [this] (quint32 name, quint32 version) { | 453 | connect(m_registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, this, [this] (quint32 name, quint32 version) { | ||
657 | m_windowManagement = m_registry->createPlasmaWindowManagement(name, version, this); | 454 | m_windowManagement = m_registry->createPlasmaWindowManagement(name, version, this); | ||
658 | Q_EMIT waylandIntegration()->plasmaWindowManagementInitialized(); | 455 | Q_EMIT waylandIntegration()->plasmaWindowManagementInitialized(); | ||
659 | }); | 456 | }); | ||
660 | 457 | | |||
661 | connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, this, [this] { | 458 | connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, this, [this] { | ||
662 | m_registryInitialized = true; | 459 | m_registryInitialized = true; | ||
663 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Registry initialized"; | 460 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Registry initialized"; | ||
664 | }); | 461 | }); | ||
665 | 462 | | |||
666 | m_registry->create(m_connection); | 463 | m_registry->create(m_connection); | ||
667 | m_registry->setEventQueue(m_queue); | 464 | m_registry->setEventQueue(m_queue); | ||
668 | m_registry->setup(); | 465 | m_registry->setup(); | ||
669 | } | 466 | } |
This has been removed, but I think it will still be needed.