File indexing completed on 2024-04-28 05:36:52
0001 /* 0002 * SPDX-FileCopyrightText: 2018 Red Hat Inc 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 * 0006 * SPDX-FileCopyrightText: 2018 Jan Grulich <jgrulich@redhat.com> 0007 * SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0008 */ 0009 0010 #include "remotedesktop.h" 0011 #include "remotedesktop_debug.h" 0012 #include "remotedesktopdialog.h" 0013 #include "request.h" 0014 #include "restoredata.h" 0015 #include "session.h" 0016 #include "utils.h" 0017 #include "waylandintegration.h" 0018 #include <KLocalizedString> 0019 #include <KNotification> 0020 #include <QGuiApplication> 0021 #include <QRegion> 0022 #include <QScreen> 0023 0024 RemoteDesktopPortal::RemoteDesktopPortal(QObject *parent) 0025 : QDBusAbstractAdaptor(parent) 0026 { 0027 } 0028 0029 RemoteDesktopPortal::~RemoteDesktopPortal() 0030 { 0031 } 0032 0033 uint RemoteDesktopPortal::CreateSession(const QDBusObjectPath &handle, 0034 const QDBusObjectPath &session_handle, 0035 const QString &app_id, 0036 const QVariantMap &options, 0037 QVariantMap &results) 0038 { 0039 Q_UNUSED(results); 0040 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "CreateSession called with parameters:"; 0041 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " handle: " << handle.path(); 0042 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0043 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " app_id: " << app_id; 0044 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0045 0046 Session *session = Session::createSession(this, Session::RemoteDesktop, app_id, session_handle.path()); 0047 0048 if (!session) { 0049 return 2; 0050 } 0051 0052 if (!WaylandIntegration::isStreamingAvailable()) { 0053 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "zkde_screencast_unstable_v1 does not seem to be available"; 0054 return 2; 0055 } 0056 0057 connect(session, &Session::closed, [session] { 0058 auto remoteDesktopSession = qobject_cast<RemoteDesktopSession *>(session); 0059 const auto streams = remoteDesktopSession->streams(); 0060 for (const WaylandIntegration::Stream &stream : streams) { 0061 WaylandIntegration::stopStreaming(stream.nodeId); 0062 } 0063 }); 0064 0065 return 0; 0066 } 0067 0068 uint RemoteDesktopPortal::SelectDevices(const QDBusObjectPath &handle, 0069 const QDBusObjectPath &session_handle, 0070 const QString &app_id, 0071 const QVariantMap &options, 0072 QVariantMap &results) 0073 { 0074 Q_UNUSED(results); 0075 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "SelectDevices called with parameters:"; 0076 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " handle: " << handle.path(); 0077 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0078 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " app_id: " << app_id; 0079 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0080 0081 const auto types = static_cast<RemoteDesktopPortal::DeviceTypes>(options.value(QStringLiteral("types")).toUInt()); 0082 if (types == None) { 0083 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "There are no devices to remotely control"; 0084 return 2; 0085 } 0086 0087 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0088 0089 if (!session) { 0090 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to select sources on non-existing session " << session_handle.path(); 0091 return 2; 0092 } 0093 0094 session->setDeviceTypes(types); 0095 session->setPersistMode(ScreenCastPortal::PersistMode(options.value(QStringLiteral("persist_mode")).toUInt())); 0096 session->setRestoreData(options.value(QStringLiteral("restore_data"))); 0097 0098 return 0; 0099 } 0100 0101 uint RemoteDesktopPortal::Start(const QDBusObjectPath &handle, 0102 const QDBusObjectPath &session_handle, 0103 const QString &app_id, 0104 const QString &parent_window, 0105 const QVariantMap &options, 0106 QVariantMap &results) 0107 { 0108 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "Start called with parameters:"; 0109 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " handle: " << handle.path(); 0110 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0111 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " app_id: " << app_id; 0112 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " parent_window: " << parent_window; 0113 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0114 0115 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0116 0117 if (!session) { 0118 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call start on non-existing session " << session_handle.path(); 0119 return 2; 0120 } 0121 0122 if (QGuiApplication::screens().isEmpty()) { 0123 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Failed to show dialog as there is no screen to select"; 0124 return 2; 0125 } 0126 0127 const ScreenCastPortal::PersistMode persist = session->persistMode(); 0128 QList<Output> selectedOutputs; 0129 0130 bool restored = false; 0131 0132 if (persist != ScreenCastPortal::NoPersist && session->restoreData().isValid()) { 0133 const RestoreData restoreData = qdbus_cast<RestoreData>(session->restoreData().value<QDBusArgument>()); 0134 if (restoreData.session == QLatin1String("KDE") && restoreData.version == RestoreData::currentRestoreDataVersion()) { 0135 // check we asked for the same key content both times; if not, don't restore 0136 // some settings (like ScreenCast multipleSources or cursorMode) don't involve user prompts so use whatever was explicitly 0137 // requested this time 0138 if (session->deviceTypes() != restoreData.payload["devices"].value<RemoteDesktopPortal::DeviceTypes>()) { 0139 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "Not restoring session as requested devices don't match"; 0140 } else if (session->screenSharingEnabled() != restoreData.payload["screenShareEnabled"].toBool()) { 0141 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "Not restoring session as requested screen sharing doesn't match"; 0142 } else { 0143 restored = true; 0144 } 0145 } 0146 } 0147 0148 if (restored) { 0149 auto notification = new KNotification(QStringLiteral("remotedesktopstarted"), KNotification::CloseOnTimeout); 0150 notification->setTitle(i18nc("title of notification about input systems taken over", "Remote control session started")); 0151 notification->setText(RemoteDesktopDialog::buildDescription(app_id, session->deviceTypes(), session->screenSharingEnabled())); 0152 notification->setIconName(QStringLiteral("krfb")); 0153 notification->sendEvent(); 0154 } else { 0155 QScopedPointer<RemoteDesktopDialog, QScopedPointerDeleteLater> remoteDesktopDialog( 0156 new RemoteDesktopDialog(app_id, session->deviceTypes(), session->screenSharingEnabled())); 0157 Utils::setParentWindow(remoteDesktopDialog->windowHandle(), parent_window); 0158 Request::makeClosableDialogRequest(handle, remoteDesktopDialog.get()); 0159 connect(session, &Session::closed, remoteDesktopDialog.data(), &RemoteDesktopDialog::reject); 0160 0161 if (!remoteDesktopDialog->exec()) { 0162 return 1; 0163 } 0164 } 0165 0166 if (session->screenSharingEnabled()) { 0167 WaylandIntegration::Streams streams; 0168 const auto screens = qGuiApp->screens(); 0169 if (session->multipleSources() || screens.count() == 1) { 0170 for (const auto &screen : screens) { 0171 auto stream = WaylandIntegration::startStreamingOutput(screen, Screencasting::Metadata); 0172 if (!stream.isValid()) { 0173 return 2; 0174 } 0175 streams << stream; 0176 } 0177 } else { 0178 streams << WaylandIntegration::startStreamingWorkspace(Screencasting::Metadata); 0179 } 0180 0181 session->setStreams(streams); 0182 results.insert(QStringLiteral("streams"), QVariant::fromValue<WaylandIntegration::Streams>(streams)); 0183 } else { 0184 qCWarning(XdgDesktopPortalKdeRemoteDesktop()) << "Only stream input"; 0185 session->refreshDescription(); 0186 } 0187 session->acquireStreamingInput(); 0188 0189 results.insert(QStringLiteral("devices"), QVariant::fromValue<uint>(session->deviceTypes())); 0190 results.insert(QStringLiteral("clipboard_enabled"), false); 0191 if (session->persistMode() != ScreenCastPortal::NoPersist) { 0192 results.insert("persist_mode", quint32(persist)); 0193 if (persist != ScreenCastPortal::NoPersist) { 0194 const RestoreData restoreData = { 0195 "KDE", 0196 RestoreData::currentRestoreDataVersion(), 0197 QVariantMap{{"screenShareEnabled", session->screenSharingEnabled()}, {"devices", QVariant::fromValue(session->deviceTypes())}}}; 0198 results.insert("restore_data", QVariant::fromValue<RestoreData>(restoreData)); 0199 } 0200 } 0201 0202 return 0; 0203 } 0204 0205 void RemoteDesktopPortal::NotifyPointerMotion(const QDBusObjectPath &session_handle, const QVariantMap &options, double dx, double dy) 0206 { 0207 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyPointerMotion called with parameters:"; 0208 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0209 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0210 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " dx: " << dx; 0211 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " dy: " << dy; 0212 0213 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0214 0215 if (!session) { 0216 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerMotion on non-existing session " << session_handle.path(); 0217 return; 0218 } 0219 0220 WaylandIntegration::requestPointerMotion(QSizeF(dx, dy)); 0221 } 0222 0223 void RemoteDesktopPortal::NotifyPointerMotionAbsolute(const QDBusObjectPath &session_handle, const QVariantMap &options, uint stream, double x, double y) 0224 { 0225 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyPointerMotionAbsolute called with parameters:"; 0226 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0227 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0228 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " stream: " << stream; 0229 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " x: " << x; 0230 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " y: " << y; 0231 0232 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0233 0234 if (!session) { 0235 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerMotionAbsolute on non-existing session " << session_handle.path(); 0236 return; 0237 } 0238 0239 WaylandIntegration::requestPointerMotionAbsolute(stream, QPointF(x, y)); 0240 } 0241 0242 void RemoteDesktopPortal::NotifyPointerButton(const QDBusObjectPath &session_handle, const QVariantMap &options, int button, uint state) 0243 { 0244 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyPointerButton called with parameters:"; 0245 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0246 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0247 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " button: " << button; 0248 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " state: " << state; 0249 0250 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0251 0252 if (!session) { 0253 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerButton on non-existing session " << session_handle.path(); 0254 return; 0255 } 0256 0257 if (state) { 0258 WaylandIntegration::requestPointerButtonPress(button); 0259 } else { 0260 WaylandIntegration::requestPointerButtonRelease(button); 0261 } 0262 } 0263 0264 void RemoteDesktopPortal::NotifyPointerAxis(const QDBusObjectPath &session_handle, const QVariantMap &options, double dx, double dy) 0265 { 0266 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyPointerAxis called with parameters:"; 0267 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0268 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0269 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " dx: " << dx; 0270 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " dy: " << dy; 0271 0272 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0273 0274 if (!session) { 0275 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyKeyboardKeysym on non-existing session " << session_handle.path(); 0276 return; 0277 } 0278 0279 WaylandIntegration::requestPointerAxis(dx, dy); 0280 } 0281 0282 void RemoteDesktopPortal::NotifyPointerAxisDiscrete(const QDBusObjectPath &session_handle, const QVariantMap &options, uint axis, int steps) 0283 { 0284 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyPointerAxisDiscrete called with parameters:"; 0285 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0286 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0287 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " axis: " << axis; 0288 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " steps: " << steps; 0289 0290 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0291 0292 if (!session) { 0293 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerAxisDiscrete on non-existing session " << session_handle.path(); 0294 return; 0295 } 0296 0297 WaylandIntegration::requestPointerAxisDiscrete(!axis ? Qt::Vertical : Qt::Horizontal, steps); 0298 } 0299 0300 void RemoteDesktopPortal::NotifyKeyboardKeysym(const QDBusObjectPath &session_handle, const QVariantMap &options, int keysym, uint state) 0301 { 0302 Q_UNUSED(options) 0303 0304 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0305 0306 if (!session) { 0307 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyKeyboardKeysym on non-existing session " << session_handle.path(); 0308 return; 0309 } 0310 0311 WaylandIntegration::requestKeyboardKeysym(keysym, state != 0); 0312 } 0313 0314 void RemoteDesktopPortal::NotifyKeyboardKeycode(const QDBusObjectPath &session_handle, const QVariantMap &options, int keycode, uint state) 0315 { 0316 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyKeyboardKeycode called with parameters:"; 0317 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0318 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0319 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " keycode: " << keycode; 0320 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " state: " << state; 0321 0322 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0323 0324 if (!session) { 0325 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyKeyboardKeycode on non-existing session " << session_handle.path(); 0326 return; 0327 } 0328 0329 WaylandIntegration::requestKeyboardKeycode(keycode, state != 0); 0330 } 0331 0332 void RemoteDesktopPortal::NotifyTouchDown(const QDBusObjectPath &session_handle, const QVariantMap &options, uint stream, uint slot, int x, int y) 0333 { 0334 Q_UNUSED(options) 0335 Q_UNUSED(stream) 0336 0337 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0338 if (!session) { 0339 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerAxisDiscrete on non-existing session " << session_handle.path(); 0340 return; 0341 } 0342 WaylandIntegration::requestTouchDown(slot, QPoint(x, y)); 0343 } 0344 0345 void RemoteDesktopPortal::NotifyTouchMotion(const QDBusObjectPath &session_handle, const QVariantMap &options, uint stream, uint slot, int x, int y) 0346 { 0347 Q_UNUSED(options) 0348 Q_UNUSED(stream) 0349 0350 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0351 if (!session) { 0352 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerAxisDiscrete on non-existing session " << session_handle.path(); 0353 return; 0354 } 0355 WaylandIntegration::requestTouchMotion(slot, QPoint(x, y)); 0356 } 0357 0358 void RemoteDesktopPortal::NotifyTouchUp(const QDBusObjectPath &session_handle, const QVariantMap &options, uint slot) 0359 { 0360 Q_UNUSED(options) 0361 0362 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0363 if (!session) { 0364 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerAxisDiscrete on non-existing session " << session_handle.path(); 0365 return; 0366 } 0367 0368 WaylandIntegration::requestTouchUp(slot); 0369 }