Changeset View
Changeset View
Standalone View
Standalone View
src/waylandintegration.cpp
Show All 14 Lines | |||||
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. | 15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. | ||
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 | #include "screencasting.h" | |||
24 | 24 | | |||
25 | #include <QDBusArgument> | 25 | #include <QDBusArgument> | ||
26 | #include <QDBusMetaType> | 26 | #include <QDBusMetaType> | ||
27 | 27 | | |||
28 | #include <QEventLoop> | 28 | #include <QEventLoop> | ||
29 | #include <QLoggingCategory> | 29 | #include <QLoggingCategory> | ||
30 | #include <QThread> | 30 | #include <QThread> | ||
31 | #include <QTimer> | 31 | #include <QTimer> | ||
32 | #include <KNotification> | ||||
32 | 33 | | |||
33 | #include <QImage> | 34 | #include <QImage> | ||
34 | 35 | | |||
35 | #include <KLocalizedString> | 36 | #include <KLocalizedString> | ||
36 | 37 | | |||
37 | // KWayland | 38 | // KWayland | ||
38 | #include <KWayland/Client/connection_thread.h> | 39 | #include <KWayland/Client/connection_thread.h> | ||
39 | #include <KWayland/Client/event_queue.h> | 40 | #include <KWayland/Client/event_queue.h> | ||
40 | #include <KWayland/Client/registry.h> | 41 | #include <KWayland/Client/registry.h> | ||
41 | #include <KWayland/Client/plasmawindowmanagement.h> | 42 | #include <KWayland/Client/plasmawindowmanagement.h> | ||
42 | 43 | | |||
43 | // system | 44 | // system | ||
44 | #include <fcntl.h> | 45 | #include <fcntl.h> | ||
45 | #include <unistd.h> | 46 | #include <unistd.h> | ||
46 | 47 | | |||
47 | #if HAVE_PIPEWIRE_SUPPORT | | |||
48 | #include "screencaststream.h" | | |||
49 | | ||||
50 | #include <KWayland/Client/fakeinput.h> | 48 | #include <KWayland/Client/fakeinput.h> | ||
51 | #include <KWayland/Client/output.h> | 49 | #include <KWayland/Client/output.h> | ||
52 | #include <KWayland/Client/remote_access.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::startStreamingOutput(quint32 outputName) | ||
76 | { | ||||
77 | return globalWaylandIntegration->startStreamingOutput(outputName); | ||||
78 | } | ||||
79 | | ||||
80 | bool WaylandIntegration::startStreamingWindow(quint32 winid) | ||||
89 | { | 81 | { | ||
90 | return globalWaylandIntegration->startStreaming(outputName); | 82 | return globalWaylandIntegration->startStreamingWindow(winid); | ||
91 | } | 83 | } | ||
92 | 84 | | |||
93 | void WaylandIntegration::stopStreaming() | 85 | void WaylandIntegration::stopAllStreaming() | ||
94 | { | 86 | { | ||
95 | globalWaylandIntegration->stopStreaming(); | 87 | globalWaylandIntegration->stopAllStreaming(); | ||
96 | } | 88 | } | ||
97 | 89 | | |||
98 | void WaylandIntegration::requestPointerButtonPress(quint32 linuxButton) | 90 | void WaylandIntegration::requestPointerButtonPress(quint32 linuxButton) | ||
99 | { | 91 | { | ||
100 | globalWaylandIntegration->requestPointerButtonPress(linuxButton); | 92 | globalWaylandIntegration->requestPointerButtonPress(linuxButton); | ||
101 | } | 93 | } | ||
102 | 94 | | |||
103 | void WaylandIntegration::requestPointerButtonRelease(quint32 linuxButton) | 95 | void WaylandIntegration::requestPointerButtonRelease(quint32 linuxButton) | ||
Show All 16 Lines | 111 | { | |||
120 | globalWaylandIntegration->requestPointerAxisDiscrete(axis, delta); | 112 | globalWaylandIntegration->requestPointerAxisDiscrete(axis, delta); | ||
121 | } | 113 | } | ||
122 | 114 | | |||
123 | void WaylandIntegration::requestKeyboardKeycode(int keycode, bool state) | 115 | void WaylandIntegration::requestKeyboardKeycode(int keycode, bool state) | ||
124 | { | 116 | { | ||
125 | globalWaylandIntegration->requestKeyboardKeycode(keycode, state); | 117 | globalWaylandIntegration->requestKeyboardKeycode(keycode, state); | ||
126 | } | 118 | } | ||
127 | 119 | | |||
128 | WaylandIntegration::EGLStruct WaylandIntegration::egl() | | |||
129 | { | | |||
130 | return globalWaylandIntegration->egl(); | | |||
131 | } | | |||
132 | | ||||
133 | QMap<quint32, WaylandIntegration::WaylandOutput> WaylandIntegration::screens() | 120 | QMap<quint32, WaylandIntegration::WaylandOutput> WaylandIntegration::screens() | ||
134 | { | 121 | { | ||
135 | return globalWaylandIntegration->screens(); | 122 | return globalWaylandIntegration->screens(); | ||
136 | } | 123 | } | ||
137 | 124 | | |||
138 | QVariant WaylandIntegration::streams() | 125 | QVariant WaylandIntegration::streams() | ||
139 | { | 126 | { | ||
140 | return globalWaylandIntegration->streams(); | 127 | return globalWaylandIntegration->streams(); | ||
141 | } | 128 | } | ||
142 | 129 | | |||
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 | 130 | // Thank you kscreen | ||
166 | void WaylandIntegration::WaylandOutput::setOutputType(const QString &type) | 131 | void WaylandIntegration::WaylandOutput::setOutputType(const QString &type) | ||
167 | { | 132 | { | ||
168 | const auto embedded = { QLatin1String("LVDS"), | 133 | const auto embedded = { QLatin1String("LVDS"), | ||
169 | QLatin1String("IDP"), | 134 | QLatin1String("IDP"), | ||
170 | QLatin1String("EDP"), | 135 | QLatin1String("EDP"), | ||
171 | QLatin1String("LCD") }; | 136 | QLatin1String("LCD") }; | ||
172 | 137 | | |||
173 | for (const QLatin1String &pre : embedded) { | 138 | for (const QLatin1String &pre : embedded) { | ||
174 | if (type.toUpper().startsWith(pre)) { | 139 | if (type.startsWith(pre, Qt::CaseInsensitive)) { | ||
175 | m_outputType = OutputType::Laptop; | 140 | m_outputType = OutputType::Laptop; | ||
176 | return; | 141 | return; | ||
177 | } | 142 | } | ||
178 | } | 143 | } | ||
179 | 144 | | |||
180 | if (type.contains(QLatin1String("VGA")) || type.contains(QLatin1String("DVI")) || type.contains(QLatin1String("HDMI")) || type.contains(QLatin1String("Panel")) || | 145 | 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"))) { | 146 | type.contains(QLatin1String("DisplayPort")) || type.startsWith(QLatin1String("DP")) || type.contains(QLatin1String("unknown"))) { | ||
182 | m_outputType = OutputType::Monitor; | 147 | m_outputType = OutputType::Monitor; | ||
Show All 31 Lines | 176 | { | |||
214 | arg << stream.map; | 179 | arg << stream.map; | ||
215 | arg.endStructure(); | 180 | arg.endStructure(); | ||
216 | 181 | | |||
217 | return arg; | 182 | return arg; | ||
218 | } | 183 | } | ||
219 | 184 | | |||
220 | Q_DECLARE_METATYPE(WaylandIntegration::WaylandIntegrationPrivate::Stream) | 185 | Q_DECLARE_METATYPE(WaylandIntegration::WaylandIntegrationPrivate::Stream) | ||
221 | Q_DECLARE_METATYPE(WaylandIntegration::WaylandIntegrationPrivate::Streams) | 186 | Q_DECLARE_METATYPE(WaylandIntegration::WaylandIntegrationPrivate::Streams) | ||
222 | #endif | | |||
223 | 187 | | |||
224 | KWayland::Client::PlasmaWindowManagement * WaylandIntegration::plasmaWindowManagement() | 188 | KWayland::Client::PlasmaWindowManagement * WaylandIntegration::plasmaWindowManagement() | ||
225 | { | 189 | { | ||
226 | return globalWaylandIntegration->plasmaWindowManagement(); | 190 | return globalWaylandIntegration->plasmaWindowManagement(); | ||
227 | } | 191 | } | ||
228 | 192 | | |||
229 | WaylandIntegration::WaylandIntegration * WaylandIntegration::waylandIntegration() | 193 | WaylandIntegration::WaylandIntegration * WaylandIntegration::waylandIntegration() | ||
230 | { | 194 | { | ||
231 | return globalWaylandIntegration; | 195 | return globalWaylandIntegration; | ||
232 | } | 196 | } | ||
233 | 197 | | |||
234 | WaylandIntegration::WaylandIntegrationPrivate::WaylandIntegrationPrivate() | 198 | WaylandIntegration::WaylandIntegrationPrivate::WaylandIntegrationPrivate() | ||
235 | : WaylandIntegration() | 199 | : WaylandIntegration() | ||
236 | , m_registryInitialized(false) | 200 | , m_registryInitialized(false) | ||
237 | , m_connection(nullptr) | 201 | , m_connection(nullptr) | ||
238 | , m_queue(nullptr) | 202 | , m_queue(nullptr) | ||
239 | , m_registry(nullptr) | 203 | , m_registry(nullptr) | ||
240 | #if HAVE_PIPEWIRE_SUPPORT | | |||
241 | , m_fakeInput(nullptr) | 204 | , m_fakeInput(nullptr) | ||
242 | , m_remoteAccessManager(nullptr) | 205 | , m_screencasting(nullptr) | ||
243 | #endif | | |||
244 | { | 206 | { | ||
245 | #if HAVE_PIPEWIRE_SUPPORT | | |||
246 | qDBusRegisterMetaType<WaylandIntegrationPrivate::Stream>(); | 207 | qDBusRegisterMetaType<WaylandIntegrationPrivate::Stream>(); | ||
247 | qDBusRegisterMetaType<WaylandIntegrationPrivate::Streams>(); | 208 | qDBusRegisterMetaType<WaylandIntegrationPrivate::Streams>(); | ||
248 | #endif | | |||
249 | } | 209 | } | ||
250 | 210 | | |||
251 | WaylandIntegration::WaylandIntegrationPrivate::~WaylandIntegrationPrivate() | 211 | WaylandIntegration::WaylandIntegrationPrivate::~WaylandIntegrationPrivate() = default; | ||
252 | { | | |||
253 | #if HAVE_PIPEWIRE_SUPPORT | | |||
254 | if (m_remoteAccessManager) { | | |||
255 | m_remoteAccessManager->destroy(); | | |||
256 | } | | |||
257 | | ||||
258 | if (m_gbmDevice) { | | |||
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 | 212 | | |||
270 | bool WaylandIntegration::WaylandIntegrationPrivate::isStreamingEnabled() const | 213 | bool WaylandIntegration::WaylandIntegrationPrivate::isStreamingEnabled() const | ||
271 | { | 214 | { | ||
272 | return m_streamingEnabled; | 215 | return !m_streams.isEmpty(); | ||
273 | } | 216 | } | ||
274 | 217 | | |||
275 | void WaylandIntegration::WaylandIntegrationPrivate::bindOutput(int outputName, int outputVersion) | 218 | void WaylandIntegration::WaylandIntegrationPrivate::bindOutput(int outputName, int outputVersion) | ||
276 | { | 219 | { | ||
277 | KWayland::Client::Output *output = new KWayland::Client::Output(this); | 220 | KWayland::Client::Output *output = new KWayland::Client::Output(this); | ||
278 | output->setup(m_registry->bindOutput(outputName, outputVersion)); | 221 | output->setup(m_registry->bindOutput(outputName, outputVersion)); | ||
279 | m_bindOutputs << output; | 222 | m_bindOutputs << output; | ||
280 | } | 223 | } | ||
281 | 224 | | |||
282 | void WaylandIntegration::WaylandIntegrationPrivate::startStreamingInput() | 225 | void WaylandIntegration::WaylandIntegrationPrivate::startStreamingInput() | ||
283 | { | 226 | { | ||
284 | m_streamInput = true; | 227 | m_streamInput = true; | ||
285 | } | 228 | } | ||
286 | 229 | | |||
287 | bool WaylandIntegration::WaylandIntegrationPrivate::startStreaming(quint32 outputName) | 230 | bool WaylandIntegration::WaylandIntegrationPrivate::startStreamingWindow(quint32 winid) | ||
288 | { | 231 | { | ||
289 | WaylandOutput output = m_outputMap.value(outputName); | 232 | return startStreaming(m_screencasting->createWindowStream(winid), {}); | ||
290 | m_streamedScreenPosition = output.globalPosition(); | 233 | } | ||
291 | | ||||
292 | m_stream = new ScreenCastStream(output.resolution()); | | |||
293 | m_stream->init(); | | |||
294 | 234 | | |||
295 | connect(m_stream, &ScreenCastStream::startStreaming, this, [this, output] { | 235 | bool WaylandIntegration::WaylandIntegrationPrivate::startStreamingOutput(quint32 outputName) | ||
296 | m_streamingEnabled = true; | 236 | { | ||
297 | startStreamingInput(); | 237 | auto output = m_outputMap.value(outputName).output(); | ||
298 | bindOutput(output.waylandOutputName(), output.waylandOutputVersion()); | | |||
299 | }); | | |||
300 | 238 | | |||
301 | connect(m_stream, &ScreenCastStream::stopStreaming, this, &WaylandIntegrationPrivate::stopStreaming); | 239 | return startStreaming(m_screencasting->createOutputStream(output.data()), output->globalPosition()); | ||
240 | } | ||||
302 | 241 | | |||
303 | bool streamReady = false; | 242 | bool WaylandIntegration::WaylandIntegrationPrivate::startStreaming(KWayland::Client::ScreencastingStream *stream, const QPoint &globalPosition) | ||
243 | { | ||||
304 | QEventLoop loop; | 244 | QEventLoop loop; | ||
305 | connect(m_stream, &ScreenCastStream::streamReady, this, [&loop, &streamReady] { | 245 | bool streamReady = false; | ||
246 | connect(stream, &KWayland::Client::ScreencastingStream::failed, this, [&] (const QString &error) { | ||||
247 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "failed to start streaming" << stream << error; | ||||
248 | | ||||
249 | KNotification *notification = new KNotification(QStringLiteral("screencastfailure"), KNotification::CloseOnTimeout); | ||||
250 | notification->setTitle(i18n("Failed to start screencasting")); | ||||
251 | notification->setText(error); | ||||
252 | notification->setIconName(QStringLiteral("dialog-error")); | ||||
253 | notification->sendEvent(); | ||||
254 | | ||||
255 | streamReady = false; | ||||
306 | loop.quit(); | 256 | loop.quit(); | ||
307 | streamReady = true; | | |||
308 | }); | 257 | }); | ||
258 | connect(stream, &KWayland::Client::ScreencastingStream::created, this, [&] (uint32_t nodeid, const QSize &pixelSize) { | ||||
259 | m_streamedScreenPosition = globalPosition; | ||||
260 | Stream s; | ||||
261 | s.stream = stream; | ||||
262 | s.nodeId = nodeid; | ||||
263 | s.map = {{QLatin1String("size"), pixelSize}}; | ||||
264 | m_streams.append(s); | ||||
265 | startStreamingInput(); | ||||
309 | 266 | | |||
310 | // HACK wait for stream to be ready | 267 | qDebug() << "wop!"; | ||
jgrulich: This has been removed, but I think it will still be needed. | |||||
268 | connect(stream, &KWayland::Client::ScreencastingStream::closed, this, [this, nodeid] { stopStreaming(nodeid); }); | ||||
269 | streamReady = true; | ||||
270 | loop.quit(); | ||||
271 | }); | ||||
311 | QTimer::singleShot(3000, &loop, &QEventLoop::quit); | 272 | QTimer::singleShot(3000, &loop, &QEventLoop::quit); | ||
312 | loop.exec(); | 273 | loop.exec(); | ||
313 | 274 | | |||
314 | disconnect(m_stream, &ScreenCastStream::streamReady, this, nullptr); | 275 | return streamReady; | ||
315 | | ||||
316 | if (!streamReady) { | | |||
317 | if (m_stream) { | | |||
318 | delete m_stream; | | |||
319 | m_stream = nullptr; | | |||
320 | } | 276 | } | ||
321 | return false; | | |||
322 | } | | |||
323 | | ||||
324 | // TODO support multiple outputs | | |||
325 | 277 | | |||
326 | if (m_registry->hasInterface(KWayland::Client::Registry::Interface::RemoteAccessManager)) { | 278 | void WaylandIntegration::WaylandIntegrationPrivate::Stream::close() | ||
327 | KWayland::Client::Registry::AnnouncedInterface interface = m_registry->interface(KWayland::Client::Registry::Interface::RemoteAccessManager); | 279 | { | ||
328 | if (!interface.name && !interface.version) { | 280 | stream->close(); | ||
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 | } | 281 | } | ||
342 | 282 | | |||
343 | if (m_stream) { | 283 | void WaylandIntegration::WaylandIntegrationPrivate::stopAllStreaming() | ||
344 | delete m_stream; | 284 | { | ||
345 | m_stream = nullptr; | 285 | for (auto & stream : m_streams) { | ||
286 | stream.close(); | ||||
346 | } | 287 | } | ||
288 | m_streams.clear(); | ||||
347 | 289 | | |||
348 | qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Failed to start streaming: no remote access manager interface"; | 290 | m_streamInput = false; | ||
349 | return false; | 291 | // First unbound outputs and destroy remote access manager so we no longer receive buffers | ||
350 | } | 292 | } | ||
351 | 293 | | |||
352 | void WaylandIntegration::WaylandIntegrationPrivate::stopStreaming() | 294 | void WaylandIntegration::WaylandIntegrationPrivate::stopStreaming(uint32_t nodeid) | ||
353 | { | 295 | { | ||
354 | m_streamInput = false; | 296 | for(auto it = m_streams.begin(), itEnd = m_streams.end(); it != itEnd; ++it) { | ||
355 | if (m_streamingEnabled) { | 297 | if (it->nodeId == nodeid) { | ||
356 | m_streamingEnabled = false; | 298 | m_streams.erase(it); | ||
357 | 299 | 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 | } | 300 | } | ||
370 | } | 301 | } | ||
302 | | ||||
303 | if (m_streams.isEmpty()) { | ||||
304 | stopAllStreaming(); | ||||
305 | } | ||||
371 | } | 306 | } | ||
372 | 307 | | |||
373 | void WaylandIntegration::WaylandIntegrationPrivate::requestPointerButtonPress(quint32 linuxButton) | 308 | void WaylandIntegration::WaylandIntegrationPrivate::requestPointerButtonPress(quint32 linuxButton) | ||
374 | { | 309 | { | ||
375 | if (m_streamInput && m_fakeInput) { | 310 | if (m_streamInput && m_fakeInput) { | ||
376 | m_fakeInput->requestPointerButtonPress(linuxButton); | 311 | m_fakeInput->requestPointerButtonPress(linuxButton); | ||
377 | } | 312 | } | ||
378 | } | 313 | } | ||
Show All 32 Lines | 345 | if (m_streamInput && m_fakeInput) { | |||
411 | if (state) { | 346 | if (state) { | ||
412 | m_fakeInput->requestKeyboardKeyPress(keycode); | 347 | m_fakeInput->requestKeyboardKeyPress(keycode); | ||
413 | } else { | 348 | } else { | ||
414 | m_fakeInput->requestKeyboardKeyRelease(keycode); | 349 | m_fakeInput->requestKeyboardKeyRelease(keycode); | ||
415 | } | 350 | } | ||
416 | } | 351 | } | ||
417 | } | 352 | } | ||
418 | 353 | | |||
419 | WaylandIntegration::EGLStruct WaylandIntegration::WaylandIntegrationPrivate::egl() | | |||
420 | { | | |||
421 | return m_egl; | | |||
422 | } | | |||
423 | | ||||
424 | QMap<quint32, WaylandIntegration::WaylandOutput> WaylandIntegration::WaylandIntegrationPrivate::screens() | 354 | QMap<quint32, WaylandIntegration::WaylandOutput> WaylandIntegration::WaylandIntegrationPrivate::screens() | ||
425 | { | 355 | { | ||
426 | return m_outputMap; | 356 | return m_outputMap; | ||
427 | } | 357 | } | ||
428 | 358 | | |||
429 | QVariant WaylandIntegration::WaylandIntegrationPrivate::streams() | 359 | QVariant WaylandIntegration::WaylandIntegrationPrivate::streams() | ||
430 | { | 360 | { | ||
431 | Stream stream; | 361 | 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 | } | | |||
454 | | ||||
455 | void WaylandIntegration::WaylandIntegrationPrivate::initEGL() | | |||
456 | { | | |||
457 | // Get the list of client extensions | | |||
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 | } | 362 | } | ||
507 | 363 | | |||
508 | void WaylandIntegration::WaylandIntegrationPrivate::authenticate() | 364 | void WaylandIntegration::WaylandIntegrationPrivate::authenticate() | ||
509 | { | 365 | { | ||
510 | if (!m_waylandAuthenticationRequested) { | 366 | if (!m_waylandAuthenticationRequested) { | ||
511 | m_fakeInput->authenticate(i18n("xdg-desktop-portals-kde"), i18n("Remote desktop")); | 367 | m_fakeInput->authenticate(i18n("xdg-desktop-portals-kde"), i18n("Remote desktop")); | ||
512 | m_waylandAuthenticationRequested = true; | 368 | m_waylandAuthenticationRequested = true; | ||
513 | } | 369 | } | ||
514 | } | 370 | } | ||
515 | 371 | | |||
516 | #endif | | |||
517 | | ||||
518 | KWayland::Client::PlasmaWindowManagement * WaylandIntegration::WaylandIntegrationPrivate::plasmaWindowManagement() | 372 | KWayland::Client::PlasmaWindowManagement * WaylandIntegration::WaylandIntegrationPrivate::plasmaWindowManagement() | ||
519 | { | 373 | { | ||
520 | return m_windowManagement; | 374 | return m_windowManagement; | ||
521 | } | 375 | } | ||
522 | 376 | | |||
523 | void WaylandIntegration::WaylandIntegrationPrivate::initWayland() | 377 | void WaylandIntegration::WaylandIntegrationPrivate::initWayland() | ||
524 | { | 378 | { | ||
525 | m_thread = new QThread(this); | 379 | m_thread = new QThread(this); | ||
Show All 24 Lines | 402 | connect(m_connection, &KWayland::Client::ConnectionThread::failed, this, [this] { | |||
550 | m_thread->wait(); | 404 | m_thread->wait(); | ||
551 | }); | 405 | }); | ||
552 | 406 | | |||
553 | m_thread->start(); | 407 | m_thread->start(); | ||
554 | m_connection->moveToThread(m_thread); | 408 | m_connection->moveToThread(m_thread); | ||
555 | m_connection->initConnection(); | 409 | m_connection->initConnection(); | ||
556 | } | 410 | } | ||
557 | 411 | | |||
558 | #if HAVE_PIPEWIRE_SUPPORT | | |||
559 | void WaylandIntegration::WaylandIntegrationPrivate::addOutput(quint32 name, quint32 version) | 412 | void WaylandIntegration::WaylandIntegrationPrivate::addOutput(quint32 name, quint32 version) | ||
560 | { | 413 | { | ||
561 | KWayland::Client::Output *output = new KWayland::Client::Output(this); | 414 | QSharedPointer<KWayland::Client::Output> output(new KWayland::Client::Output(this)); | ||
562 | output->setup(m_registry->bindOutput(name, version)); | 415 | output->setup(m_registry->bindOutput(name, version)); | ||
563 | 416 | | |||
564 | connect(output, &KWayland::Client::Output::changed, this, [this, name, version, output] () { | 417 | connect(output.data(), &KWayland::Client::Output::changed, this, [this, name, version, output] () { | ||
565 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Adding output:"; | 418 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Adding output:"; | ||
566 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " manufacturer: " << output->manufacturer(); | 419 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " manufacturer: " << output->manufacturer(); | ||
567 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " model: " << output->model(); | 420 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " model: " << output->model(); | ||
568 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " resolution: " << output->pixelSize(); | 421 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " resolution: " << output->pixelSize(); | ||
569 | 422 | | |||
570 | WaylandOutput portalOutput; | 423 | WaylandOutput portalOutput; | ||
571 | portalOutput.setManufacturer(output->manufacturer()); | 424 | portalOutput.setOutput(output); | ||
572 | portalOutput.setModel(output->model()); | | |||
573 | portalOutput.setOutputType(output->model()); | | |||
574 | portalOutput.setGlobalPosition(output->globalPosition()); | | |||
575 | portalOutput.setResolution(output->pixelSize()); | | |||
576 | portalOutput.setWaylandOutputName(name); | 425 | portalOutput.setWaylandOutputName(name); | ||
577 | portalOutput.setWaylandOutputVersion(version); | 426 | portalOutput.setWaylandOutputVersion(version); | ||
578 | 427 | | |||
579 | m_outputMap.insert(name, portalOutput); | 428 | m_outputMap.insert(name, portalOutput); | ||
580 | | ||||
581 | delete output; | | |||
582 | }); | 429 | }); | ||
583 | } | 430 | } | ||
584 | 431 | | |||
585 | void WaylandIntegration::WaylandIntegrationPrivate::removeOutput(quint32 name) | 432 | void WaylandIntegration::WaylandIntegrationPrivate::removeOutput(quint32 name) | ||
586 | { | 433 | { | ||
587 | WaylandOutput output = m_outputMap.take(name); | 434 | WaylandOutput output = m_outputMap.take(name); | ||
588 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Removing output:"; | 435 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Removing output:"; | ||
589 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " manufacturer: " << output.manufacturer(); | 436 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " manufacturer: " << output.manufacturer(); | ||
590 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " model: " << output.model(); | 437 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " model: " << output.model(); | ||
591 | } | 438 | } | ||
592 | 439 | | |||
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() | 440 | void WaylandIntegration::WaylandIntegrationPrivate::setupRegistry() | ||
642 | { | 441 | { | ||
643 | m_queue = new KWayland::Client::EventQueue(this); | 442 | m_queue = new KWayland::Client::EventQueue(this); | ||
644 | m_queue->setup(m_connection); | 443 | m_queue->setup(m_connection); | ||
645 | 444 | | |||
646 | m_registry = new KWayland::Client::Registry(this); | 445 | m_registry = new KWayland::Client::Registry(this); | ||
647 | 446 | | |||
648 | #if HAVE_PIPEWIRE_SUPPORT | | |||
649 | connect(m_registry, &KWayland::Client::Registry::fakeInputAnnounced, this, [this] (quint32 name, quint32 version) { | 447 | connect(m_registry, &KWayland::Client::Registry::fakeInputAnnounced, this, [this] (quint32 name, quint32 version) { | ||
650 | m_fakeInput = m_registry->createFakeInput(name, version, this); | 448 | m_fakeInput = m_registry->createFakeInput(name, version, this); | ||
651 | }); | 449 | }); | ||
652 | connect(m_registry, &KWayland::Client::Registry::outputAnnounced, this, &WaylandIntegrationPrivate::addOutput); | 450 | connect(m_registry, &KWayland::Client::Registry::outputAnnounced, this, &WaylandIntegrationPrivate::addOutput); | ||
653 | connect(m_registry, &KWayland::Client::Registry::outputRemoved, this, &WaylandIntegrationPrivate::removeOutput); | 451 | connect(m_registry, &KWayland::Client::Registry::outputRemoved, this, &WaylandIntegrationPrivate::removeOutput); | ||
654 | #endif | | |||
655 | 452 | | |||
453 | connect(m_registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this] (const QByteArray &interfaceName, quint32 name, quint32 version) { | ||||
454 | if (interfaceName != "zkde_screencast_unstable_v1") | ||||
davidedmundson: leftover | |||||
455 | return; | ||||
456 | m_screencasting = new KWayland::Client::Screencasting(m_registry, name, version, this); | ||||
457 | }); | ||||
656 | connect(m_registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, this, [this] (quint32 name, quint32 version) { | 458 | connect(m_registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, this, [this] (quint32 name, quint32 version) { | ||
657 | m_windowManagement = m_registry->createPlasmaWindowManagement(name, version, this); | 459 | m_windowManagement = m_registry->createPlasmaWindowManagement(name, version, this); | ||
658 | Q_EMIT waylandIntegration()->plasmaWindowManagementInitialized(); | 460 | Q_EMIT waylandIntegration()->plasmaWindowManagementInitialized(); | ||
659 | }); | 461 | }); | ||
660 | 462 | | |||
661 | connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, this, [this] { | 463 | connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, this, [this] { | ||
662 | m_registryInitialized = true; | 464 | m_registryInitialized = true; | ||
663 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Registry initialized"; | 465 | qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Registry initialized"; | ||
664 | }); | 466 | }); | ||
665 | 467 | | |||
666 | m_registry->create(m_connection); | 468 | m_registry->create(m_connection); | ||
667 | m_registry->setEventQueue(m_queue); | 469 | m_registry->setEventQueue(m_queue); | ||
668 | m_registry->setup(); | 470 | m_registry->setup(); | ||
669 | } | 471 | } |
This has been removed, but I think it will still be needed.