12#include "screenshot2adaptor.h"
13#include "screenshotlogging.h"
17#include <KLocalizedString>
19#include <QDBusConnection>
20#include <QDBusConnectionInterface>
45 qCWarning(KWIN_SCREENSHOT) <<
"failed to get screenshot fd flags:" << strerror(errno);
48 if (!(flags & O_NONBLOCK)) {
50 qCWarning(KWIN_SCREENSHOT) <<
"failed to make screenshot fd non blocking:" << strerror(errno);
57 qCWarning(KWIN_SCREENSHOT) << Q_FUNC_INFO <<
"failed to open pipe:" << file.errorString();
61 const QByteArrayView buffer(
m_image.constBits(),
m_image.sizeInBytes());
62 qint64 remainingSize = buffer.size();
66 pfds[0].events = POLLOUT;
69 const int ready = poll(pfds, 1, 60000);
72 qCWarning(KWIN_SCREENSHOT) << Q_FUNC_INFO <<
"poll() failed:" << strerror(errno);
75 }
else if (ready == 0) {
76 qCWarning(KWIN_SCREENSHOT) << Q_FUNC_INFO <<
"timed out writing to pipe";
78 }
else if (!(pfds[0].revents & POLLOUT)) {
79 qCWarning(KWIN_SCREENSHOT) << Q_FUNC_INFO <<
"pipe is broken";
82 const char *chunk = buffer.constData() + (buffer.size() - remainingSize);
83 const qint64 writtenCount = file.write(chunk, remainingSize);
85 if (writtenCount < 0) {
86 qCWarning(KWIN_SCREENSHOT) << Q_FUNC_INFO <<
"write() failed:" << file.errorString();
90 remainingSize -= writtenCount;
91 if (writtenCount == 0 || remainingSize == 0) {
103static ScreenShotFlags screenShotFlagsFromOptions(
const QVariantMap &
options)
105 ScreenShotFlags flags = ScreenShotFlags();
107 const QVariant includeDecoration =
options.value(QStringLiteral(
"include-decoration"));
108 if (includeDecoration.toBool()) {
112 const QVariant includeShadow =
options.value(QStringLiteral(
"include-shadow"),
true);
113 if (includeShadow.toBool()) {
117 const QVariant includeCursor =
options.value(QStringLiteral(
"include-cursor"));
118 if (includeCursor.toBool()) {
122 const QVariant nativeResolution =
options.value(QStringLiteral(
"native-resolution"));
123 if (nativeResolution.toBool()) {
130static const QString s_dbusServiceName = QStringLiteral(
"org.kde.KWin.ScreenShot2");
131static const QString s_dbusInterface = QStringLiteral(
"org.kde.KWin.ScreenShot2");
132static const QString s_dbusObjectPath = QStringLiteral(
"/org/kde/KWin/ScreenShot2");
134static const QString s_errorNotAuthorized = QStringLiteral(
"org.kde.KWin.ScreenShot2.Error.NoAuthorized");
135static const QString s_errorNotAuthorizedMessage = QStringLiteral(
"The process is not authorized to take a screenshot");
136static const QString s_errorCancelled = QStringLiteral(
"org.kde.KWin.ScreenShot2.Error.Cancelled");
137static const QString s_errorCancelledMessage = QStringLiteral(
"Screenshot got cancelled");
138static const QString s_errorInvalidWindow = QStringLiteral(
"org.kde.KWin.ScreenShot2.Error.InvalidWindow");
139static const QString s_errorInvalidWindowMessage = QStringLiteral(
"Invalid window requested");
140static const QString s_errorInvalidArea = QStringLiteral(
"org.kde.KWin.ScreenShot2.Error.InvalidArea");
141static const QString s_errorInvalidAreaMessage = QStringLiteral(
"Invalid area requested");
142static const QString s_errorInvalidScreen = QStringLiteral(
"org.kde.KWin.ScreenShot2.Error.InvalidScreen");
143static const QString s_errorInvalidScreenMessage = QStringLiteral(
"Invalid screen requested");
144static const QString s_errorFileDescriptor = QStringLiteral(
"org.kde.KWin.ScreenShot2.Error.FileDescriptor");
145static const QString s_errorFileDescriptorMessage = QStringLiteral(
"No valid file descriptor");
165 QFuture<QImage> m_future;
211 void flush(
const QImage &image,
const QVariantMap &attributes);
214 QDBusMessage m_replyMessage;
224 m_watcher->setFuture(m_future);
229 return m_future.isCanceled();
234 return m_future.isFinished();
239 return QVariantMap();
249 ScreenShotFlags flags)
251 , m_name(screen->name())
258 {QStringLiteral(
"screen"), m_name},
264 ScreenShotFlags flags)
271 ScreenShotFlags flags)
273 , m_internalId(window->internalId())
280 {QStringLiteral(
"windowId"), m_internalId.toString()},
285 : m_replyMessage(replyMessage)
286 , m_fileDescriptor(fileDescriptor)
292 QDBusConnection::sessionBus().send(m_replyMessage.createErrorReply(s_errorCancelled,
293 s_errorCancelledMessage));
298 if (!m_fileDescriptor.
isValid()) {
303 QVariantMap results = attributes;
304 results.insert(QStringLiteral(
"type"), QStringLiteral(
"raw"));
305 results.insert(QStringLiteral(
"format"), quint32(image.format()));
306 results.insert(QStringLiteral(
"width"), quint32(image.width()));
307 results.insert(QStringLiteral(
"height"), quint32(image.height()));
308 results.insert(QStringLiteral(
"stride"), quint32(image.bytesPerLine()));
309 results.insert(QStringLiteral(
"scale"),
double(image.devicePixelRatio()));
310 QDBusConnection::sessionBus().send(m_replyMessage.createReply(results));
313 writer->setAutoDelete(
true);
314 QThreadPool::globalInstance()->start(writer);
321 new ScreenShot2Adaptor(
this);
323 QDBusConnection::sessionBus().registerObject(s_dbusObjectPath,
this);
324 QDBusConnection::sessionBus().registerService(s_dbusServiceName);
329 QDBusConnection::sessionBus().unregisterService(s_dbusServiceName);
330 QDBusConnection::sessionBus().unregisterObject(s_dbusObjectPath);
338bool ScreenShotDBusInterface2::checkPermissions()
const
340 if (!calledFromDBus()) {
344 static bool permissionCheckDisabled = qEnvironmentVariableIntValue(
"KWIN_SCREENSHOT_NO_PERMISSION_CHECKS") == 1;
345 if (permissionCheckDisabled) {
349 const QDBusReply<uint> reply =
connection().interface()->servicePid(message().service());
350 if (reply.isValid()) {
351 const uint pid = reply.value();
352 const auto interfaces = KWin::fetchRestrictedDBusInterfacesFromPid(pid);
353 if (!interfaces.contains(s_dbusInterface)) {
354 sendErrorReply(s_errorNotAuthorized, s_errorNotAuthorizedMessage);
365 QDBusUnixFileDescriptor pipe)
367 if (!checkPermissions()) {
368 return QVariantMap();
373 sendErrorReply(s_errorInvalidWindow, s_errorInvalidWindowMessage);
374 return QVariantMap();
377 const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
378 if (fileDescriptor == -1) {
379 sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
380 return QVariantMap();
383 takeScreenShot(window, screenShotFlagsFromOptions(
options),
386 setDelayedReply(
true);
387 return QVariantMap();
392 QDBusUnixFileDescriptor pipe)
394 if (!checkPermissions()) {
395 return QVariantMap();
401 const int winId = handle.toInt(&ok);
405 qCWarning(KWIN_SCREENSHOT) <<
"Invalid handle:" << handle;
409 sendErrorReply(s_errorInvalidWindow, s_errorInvalidWindowMessage);
410 return QVariantMap();
413 const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
414 if (fileDescriptor == -1) {
415 sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
416 return QVariantMap();
419 takeScreenShot(window, screenShotFlagsFromOptions(
options),
422 setDelayedReply(
true);
423 return QVariantMap();
428 QDBusUnixFileDescriptor pipe)
430 if (!checkPermissions()) {
431 return QVariantMap();
434 const QRect area(x, y, width, height);
435 if (area.isEmpty()) {
436 sendErrorReply(s_errorInvalidArea, s_errorInvalidAreaMessage);
437 return QVariantMap();
440 const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
441 if (fileDescriptor == -1) {
442 sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
443 return QVariantMap();
446 takeScreenShot(area, screenShotFlagsFromOptions(
options),
449 setDelayedReply(
true);
450 return QVariantMap();
455 QDBusUnixFileDescriptor pipe)
457 if (!checkPermissions()) {
458 return QVariantMap();
463 sendErrorReply(s_errorInvalidScreen, s_errorInvalidScreenMessage);
464 return QVariantMap();
467 const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
468 if (fileDescriptor == -1) {
469 sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
470 return QVariantMap();
473 takeScreenShot(screen, screenShotFlagsFromOptions(
options),
476 setDelayedReply(
true);
477 return QVariantMap();
481 QDBusUnixFileDescriptor pipe)
483 if (!checkPermissions()) {
484 return QVariantMap();
489 sendErrorReply(s_errorInvalidScreen, s_errorInvalidScreenMessage);
490 return QVariantMap();
493 const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
494 if (fileDescriptor == -1) {
495 sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
496 return QVariantMap();
499 takeScreenShot(screen, screenShotFlagsFromOptions(
options),
502 setDelayedReply(
true);
503 return QVariantMap();
508 QDBusUnixFileDescriptor pipe)
510 const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
511 if (fileDescriptor == -1) {
512 sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
513 return QVariantMap();
516 const QDBusMessage replyMessage = message();
523 close(fileDescriptor);
525 QDBusConnection bus = QDBusConnection::sessionBus();
526 bus.send(replyMessage.createErrorReply(s_errorCancelled, s_errorCancelledMessage));
528 takeScreenShot(window, screenShotFlagsFromOptions(
options),
533 "Escape or right click to cancel."),
534 QStringLiteral(
"spectacle"));
539 if (point == QPoint(-1, -1)) {
540 close(fileDescriptor);
542 QDBusConnection bus = QDBusConnection::sessionBus();
543 bus.send(replyMessage.createErrorReply(s_errorCancelled, s_errorCancelledMessage));
546 takeScreenShot(screen, screenShotFlagsFromOptions(
options),
551 "Escape or right click to cancel."),
552 QStringLiteral(
"spectacle"));
555 setDelayedReply(
true);
556 return QVariantMap();
561 if (!checkPermissions()) {
562 return QVariantMap();
565 const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
566 if (fileDescriptor == -1) {
567 sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
568 return QVariantMap();
574 setDelayedReply(
true);
575 return QVariantMap();
584 source->deleteLater();
591 source->deleteLater();
595void ScreenShotDBusInterface2::takeScreenShot(Output *screen, ScreenShotFlags flags,
596 ScreenShotSinkPipe2 *sink)
598 bind(sink,
new ScreenShotSourceScreen2(m_effect, screen, flags));
601void ScreenShotDBusInterface2::takeScreenShot(
const QRect &area, ScreenShotFlags flags,
602 ScreenShotSinkPipe2 *sink)
604 bind(sink,
new ScreenShotSourceArea2(m_effect, area, flags));
607void ScreenShotDBusInterface2::takeScreenShot(EffectWindow *window, ScreenShotFlags flags,
608 ScreenShotSinkPipe2 *sink)
610 bind(sink,
new ScreenShotSourceWindow2(m_effect, window, flags));
615#include "screenshotdbusinterface2.moc"
617#include "moc_screenshotdbusinterface2.cpp"
Representation of a window used by/for Effect classes.
Q_SCRIPTABLE KWin::EffectWindow * findWindow(WId id) const
void startInteractivePositionSelection(std::function< void(const QPointF &)> callback)
void showOnScreenMessage(const QString &message, const QString &iconName=QString())
Output * screenAt(const QPoint &point) const
Output * findScreen(const QString &name) const
KWin::EffectWindow * activeWindow
QRect virtualScreenGeometry
void startInteractiveWindowSelection(std::function< void(KWin::EffectWindow *)> callback)
void hideOnScreenMessage(OnScreenMessageHideFlags flags=OnScreenMessageHideFlags())
KWin::Output * activeScreen
QVariantMap CaptureActiveScreen(const QVariantMap &options, QDBusUnixFileDescriptor pipe)
QVariantMap CaptureWorkspace(const QVariantMap &options, QDBusUnixFileDescriptor pipe)
QVariantMap CaptureInteractive(uint kind, const QVariantMap &options, QDBusUnixFileDescriptor pipe)
QVariantMap CaptureScreen(const QString &name, const QVariantMap &options, QDBusUnixFileDescriptor pipe)
QVariantMap CaptureActiveWindow(const QVariantMap &options, QDBusUnixFileDescriptor pipe)
QVariantMap CaptureWindow(const QString &handle, const QVariantMap &options, QDBusUnixFileDescriptor pipe)
~ScreenShotDBusInterface2() override
QVariantMap CaptureArea(int x, int y, int width, int height, const QVariantMap &options, QDBusUnixFileDescriptor pipe)
ScreenShotDBusInterface2(ScreenShotEffect *effect)
ScreenShotSinkPipe2(int fileDescriptor, QDBusMessage replyMessage)
void flush(const QImage &image, const QVariantMap &attributes)
void marshal(ScreenShotSinkPipe2 *sink)
ScreenShotSource2(const QFuture< QImage > &future)
virtual QVariantMap attributes() const
ScreenShotSourceArea2(ScreenShotEffect *effect, const QRect &area, ScreenShotFlags flags)
QVariantMap attributes() const override
ScreenShotSourceScreen2(ScreenShotEffect *effect, Output *screen, ScreenShotFlags flags)
QVariantMap attributes() const override
ScreenShotSourceWindow2(ScreenShotEffect *effect, EffectWindow *window, ScreenShotFlags flags)
FileDescriptor m_fileDescriptor
ScreenShotWriter2(FileDescriptor &&fileDescriptor, const QImage &image)
KWIN_EXPORT xcb_connection_t * connection()
@ ScreenShotIncludeShadow
Include the window shadow.
@ ScreenShotNativeResolution
Take the screenshot at the native resolution.
@ ScreenShotIncludeCursor
Include the cursor.
@ ScreenShotIncludeDecoration
Include window titlebar and borders.