File indexing completed on 2024-12-01 13:42: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 "session.h" 0015 #include "utils.h" 0016 #include "waylandintegration.h" 0017 #include <KLocalizedString> 0018 #include <KNotification> 0019 #include <QGuiApplication> 0020 #include <QRegion> 0021 #include <QScreen> 0022 0023 RemoteDesktopPortal::RemoteDesktopPortal(QObject *parent) 0024 : QDBusAbstractAdaptor(parent) 0025 { 0026 } 0027 0028 RemoteDesktopPortal::~RemoteDesktopPortal() 0029 { 0030 } 0031 0032 uint RemoteDesktopPortal::CreateSession(const QDBusObjectPath &handle, 0033 const QDBusObjectPath &session_handle, 0034 const QString &app_id, 0035 const QVariantMap &options, 0036 QVariantMap &results) 0037 { 0038 Q_UNUSED(results); 0039 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "CreateSession called with parameters:"; 0040 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " handle: " << handle.path(); 0041 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0042 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " app_id: " << app_id; 0043 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0044 0045 Session *session = Session::createSession(this, Session::RemoteDesktop, app_id, session_handle.path()); 0046 0047 if (!session) { 0048 return 2; 0049 } 0050 0051 if (!WaylandIntegration::isStreamingAvailable()) { 0052 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "zkde_screencast_unstable_v1 does not seem to be available"; 0053 return 2; 0054 } 0055 0056 connect(session, &Session::closed, [session] { 0057 auto remoteDesktopSession = qobject_cast<RemoteDesktopSession *>(session); 0058 const auto streams = remoteDesktopSession->streams(); 0059 for (const WaylandIntegration::Stream &stream : streams) { 0060 WaylandIntegration::stopStreaming(stream.nodeId); 0061 } 0062 }); 0063 0064 return 0; 0065 } 0066 0067 uint RemoteDesktopPortal::SelectDevices(const QDBusObjectPath &handle, 0068 const QDBusObjectPath &session_handle, 0069 const QString &app_id, 0070 const QVariantMap &options, 0071 QVariantMap &results) 0072 { 0073 Q_UNUSED(results); 0074 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "SelectDevices called with parameters:"; 0075 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " handle: " << handle.path(); 0076 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0077 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " app_id: " << app_id; 0078 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0079 0080 const auto types = static_cast<RemoteDesktopPortal::DeviceTypes>(options.value(QStringLiteral("types")).toUInt()); 0081 if (types == None) { 0082 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "There are no devices to remotely control"; 0083 return 2; 0084 } 0085 0086 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0087 0088 if (!session) { 0089 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to select sources on non-existing session " << session_handle.path(); 0090 return 2; 0091 } 0092 0093 session->setDeviceTypes(types); 0094 0095 return 0; 0096 } 0097 0098 uint RemoteDesktopPortal::Start(const QDBusObjectPath &handle, 0099 const QDBusObjectPath &session_handle, 0100 const QString &app_id, 0101 const QString &parent_window, 0102 const QVariantMap &options, 0103 QVariantMap &results) 0104 { 0105 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "Start called with parameters:"; 0106 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " handle: " << handle.path(); 0107 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0108 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " app_id: " << app_id; 0109 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " parent_window: " << parent_window; 0110 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0111 0112 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0113 0114 if (!session) { 0115 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call start on non-existing session " << session_handle.path(); 0116 return 2; 0117 } 0118 0119 if (WaylandIntegration::screens().isEmpty()) { 0120 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Failed to show dialog as there is no screen to select"; 0121 return 2; 0122 } 0123 0124 if (app_id.isEmpty()) { 0125 // non-sandboxed local applications are generally able to take over the system already without much hassle. 0126 // Instead of making users interact with the dialog (which they probably can't because this is for remote access), just 0127 // show a notification so they are aware of it. 0128 auto notification = new KNotification(QStringLiteral("remotedesktopstarted"), KNotification::CloseOnTimeout); 0129 notification->setTitle(i18nc("title of notification about input systems taken over", "Remote control session started")); 0130 notification->setText(RemoteDesktopDialog::buildDescription(app_id, session->deviceTypes(), session->screenSharingEnabled())); 0131 notification->setIconName(QStringLiteral("krfb")); 0132 notification->sendEvent(); 0133 } else { 0134 QScopedPointer<RemoteDesktopDialog, QScopedPointerDeleteLater> remoteDesktopDialog( 0135 new RemoteDesktopDialog(app_id, session->deviceTypes(), session->screenSharingEnabled())); 0136 Utils::setParentWindow(remoteDesktopDialog->windowHandle(), parent_window); 0137 Request::makeClosableDialogRequest(handle, remoteDesktopDialog.get()); 0138 connect(session, &Session::closed, remoteDesktopDialog.data(), &RemoteDesktopDialog::reject); 0139 0140 if (!remoteDesktopDialog->exec()) { 0141 return 1; 0142 } 0143 } 0144 0145 if (session->screenSharingEnabled()) { 0146 WaylandIntegration::Streams streams; 0147 const auto screens = qGuiApp->screens(); 0148 if (session->multipleSources() || screens.count() == 1) { 0149 for (const auto &screen : screens) { 0150 auto stream = WaylandIntegration::startStreamingOutput(screen, Screencasting::Metadata); 0151 if (!stream.isValid()) { 0152 return 2; 0153 } 0154 streams << stream; 0155 } 0156 } else { 0157 streams << WaylandIntegration::startStreamingWorkspace(Screencasting::Metadata); 0158 } 0159 0160 session->setStreams(streams); 0161 results.insert(QStringLiteral("streams"), QVariant::fromValue<WaylandIntegration::Streams>(streams)); 0162 } else { 0163 qCWarning(XdgDesktopPortalKdeRemoteDesktop()) << "Only stream input"; 0164 session->refreshDescription(); 0165 } 0166 session->acquireStreamingInput(); 0167 0168 results.insert(QStringLiteral("devices"), QVariant::fromValue<uint>(session->deviceTypes())); 0169 0170 return 0; 0171 } 0172 0173 void RemoteDesktopPortal::NotifyPointerMotion(const QDBusObjectPath &session_handle, const QVariantMap &options, double dx, double dy) 0174 { 0175 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyPointerMotion called with parameters:"; 0176 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0177 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0178 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " dx: " << dx; 0179 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " dy: " << dy; 0180 0181 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0182 0183 if (!session) { 0184 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerMotion on non-existing session " << session_handle.path(); 0185 return; 0186 } 0187 0188 WaylandIntegration::requestPointerMotion(QSizeF(dx, dy)); 0189 } 0190 0191 void RemoteDesktopPortal::NotifyPointerMotionAbsolute(const QDBusObjectPath &session_handle, const QVariantMap &options, uint stream, double x, double y) 0192 { 0193 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyPointerMotionAbsolute called with parameters:"; 0194 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0195 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0196 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " stream: " << stream; 0197 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " x: " << x; 0198 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " y: " << y; 0199 0200 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0201 0202 if (!session) { 0203 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerMotionAbsolute on non-existing session " << session_handle.path(); 0204 return; 0205 } 0206 0207 WaylandIntegration::requestPointerMotionAbsolute(stream, QPointF(x, y)); 0208 } 0209 0210 void RemoteDesktopPortal::NotifyPointerButton(const QDBusObjectPath &session_handle, const QVariantMap &options, int button, uint state) 0211 { 0212 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyPointerButton called with parameters:"; 0213 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0214 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0215 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " button: " << button; 0216 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " state: " << state; 0217 0218 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0219 0220 if (!session) { 0221 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerButton on non-existing session " << session_handle.path(); 0222 return; 0223 } 0224 0225 if (state) { 0226 WaylandIntegration::requestPointerButtonPress(button); 0227 } else { 0228 WaylandIntegration::requestPointerButtonRelease(button); 0229 } 0230 } 0231 0232 void RemoteDesktopPortal::NotifyPointerAxis(const QDBusObjectPath &session_handle, const QVariantMap &options, double dx, double dy) 0233 { 0234 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyPointerAxis called with parameters:"; 0235 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0236 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0237 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " dx: " << dx; 0238 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " dy: " << dy; 0239 0240 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0241 0242 if (!session) { 0243 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyKeyboardKeysym on non-existing session " << session_handle.path(); 0244 return; 0245 } 0246 0247 WaylandIntegration::requestPointerAxis(dx, dy); 0248 } 0249 0250 void RemoteDesktopPortal::NotifyPointerAxisDiscrete(const QDBusObjectPath &session_handle, const QVariantMap &options, uint axis, int steps) 0251 { 0252 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyPointerAxisDiscrete called with parameters:"; 0253 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0254 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0255 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " axis: " << axis; 0256 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " steps: " << steps; 0257 0258 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0259 0260 if (!session) { 0261 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerAxisDiscrete on non-existing session " << session_handle.path(); 0262 return; 0263 } 0264 0265 WaylandIntegration::requestPointerAxisDiscrete(!axis ? Qt::Vertical : Qt::Horizontal, steps); 0266 } 0267 0268 void RemoteDesktopPortal::NotifyKeyboardKeysym(const QDBusObjectPath &session_handle, const QVariantMap &options, int keysym, uint state) 0269 { 0270 Q_UNUSED(options) 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::requestKeyboardKeysym(keysym, state != 0); 0280 } 0281 0282 void RemoteDesktopPortal::NotifyKeyboardKeycode(const QDBusObjectPath &session_handle, const QVariantMap &options, int keycode, uint state) 0283 { 0284 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "NotifyKeyboardKeycode called with parameters:"; 0285 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path(); 0286 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options; 0287 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " keycode: " << keycode; 0288 qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " state: " << state; 0289 0290 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0291 0292 if (!session) { 0293 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyKeyboardKeycode on non-existing session " << session_handle.path(); 0294 return; 0295 } 0296 0297 WaylandIntegration::requestKeyboardKeycode(keycode, state != 0); 0298 } 0299 0300 void RemoteDesktopPortal::NotifyTouchDown(const QDBusObjectPath &session_handle, const QVariantMap &options, uint stream, uint slot, int x, int y) 0301 { 0302 Q_UNUSED(options) 0303 Q_UNUSED(stream) 0304 0305 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0306 if (!session) { 0307 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerAxisDiscrete on non-existing session " << session_handle.path(); 0308 return; 0309 } 0310 WaylandIntegration::requestTouchDown(slot, QPoint(x, y)); 0311 } 0312 0313 void RemoteDesktopPortal::NotifyTouchMotion(const QDBusObjectPath &session_handle, const QVariantMap &options, uint stream, uint slot, int x, int y) 0314 { 0315 Q_UNUSED(options) 0316 Q_UNUSED(stream) 0317 0318 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0319 if (!session) { 0320 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerAxisDiscrete on non-existing session " << session_handle.path(); 0321 return; 0322 } 0323 WaylandIntegration::requestTouchMotion(slot, QPoint(x, y)); 0324 } 0325 0326 void RemoteDesktopPortal::NotifyTouchUp(const QDBusObjectPath &session_handle, const QVariantMap &options, uint slot) 0327 { 0328 Q_UNUSED(options) 0329 0330 RemoteDesktopSession *session = qobject_cast<RemoteDesktopSession *>(Session::getSession(session_handle.path())); 0331 if (!session) { 0332 qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to call NotifyPointerAxisDiscrete on non-existing session " << session_handle.path(); 0333 return; 0334 } 0335 0336 WaylandIntegration::requestTouchUp(slot); 0337 }