Changeset View
Changeset View
Standalone View
Standalone View
src/PlatformBackends/KWinWaylandImageGrabber.cpp
Show All 23 Lines | |||||
24 | #include <QDBusUnixFileDescriptor> | 24 | #include <QDBusUnixFileDescriptor> | ||
25 | 25 | | |||
26 | #include <QtConcurrentRun> | 26 | #include <QtConcurrentRun> | ||
27 | #include <QFutureWatcher> | 27 | #include <QFutureWatcher> | ||
28 | #include <qplatformdefs.h> | 28 | #include <qplatformdefs.h> | ||
29 | 29 | | |||
30 | #include <errno.h> | 30 | #include <errno.h> | ||
31 | 31 | | |||
32 | static int readData(int fd, QByteArray &data) | ||||
33 | { | ||||
34 | // implementation based on QtWayland file qwaylanddataoffer.cpp | ||||
35 | char buf[4096]; | ||||
36 | int retryCount = 0; | ||||
37 | int n; | ||||
38 | while (true) { | ||||
39 | n = QT_READ(fd, buf, sizeof buf); | ||||
40 | // give user 30 sec to click a window, afterwards considered as error | ||||
41 | if (n == -1 && (errno == EAGAIN) && ++retryCount < 30000) { | ||||
42 | usleep(1000); | ||||
43 | } else { | ||||
44 | break; | ||||
45 | } | ||||
46 | } | ||||
47 | if (n > 0) { | ||||
48 | data.append(buf, n); | ||||
49 | n = readData(fd, data); | ||||
50 | } | ||||
51 | return n; | ||||
52 | } | ||||
53 | | ||||
54 | static QImage readImage(int pipeFd) | ||||
55 | { | ||||
56 | QByteArray content; | ||||
57 | if (readData(pipeFd, content) != 0) { | ||||
58 | close(pipeFd); | ||||
59 | return QImage(); | ||||
60 | } | ||||
61 | close(pipeFd); | ||||
62 | QDataStream ds(content); | ||||
63 | QImage image; | ||||
64 | ds >> image; | ||||
65 | return image; | ||||
66 | }; | ||||
67 | | ||||
32 | KWinWaylandImageGrabber::KWinWaylandImageGrabber(QObject *parent) : | 68 | KWinWaylandImageGrabber::KWinWaylandImageGrabber(QObject *parent) : | ||
33 | ImageGrabber(parent) | 69 | ImageGrabber(parent) | ||
34 | { | 70 | { | ||
35 | } | 71 | } | ||
36 | 72 | | |||
37 | KWinWaylandImageGrabber::~KWinWaylandImageGrabber() = default; | 73 | KWinWaylandImageGrabber::~KWinWaylandImageGrabber() = default; | ||
38 | 74 | | |||
39 | bool KWinWaylandImageGrabber::onClickGrabSupported() const | 75 | bool KWinWaylandImageGrabber::onClickGrabSupported() const | ||
40 | { | 76 | { | ||
41 | return true; | 77 | return true; | ||
42 | } | 78 | } | ||
43 | 79 | | |||
44 | void KWinWaylandImageGrabber::grabFullScreen() | 80 | void KWinWaylandImageGrabber::grabFullScreen() | ||
45 | { | 81 | { | ||
46 | // unsupported | 82 | grab(Mode::FullScreen, mCapturePointer); | ||
47 | emit pixmapChanged(QPixmap()); | | |||
48 | } | 83 | } | ||
49 | 84 | | |||
50 | void KWinWaylandImageGrabber::grabCurrentScreen() | 85 | void KWinWaylandImageGrabber::grabCurrentScreen() | ||
51 | { | 86 | { | ||
52 | // unsupported | 87 | grab(Mode::CurrentScreen, mCapturePointer); | ||
53 | emit pixmapChanged(QPixmap()); | | |||
54 | } | 88 | } | ||
55 | 89 | | |||
56 | void KWinWaylandImageGrabber::grabActiveWindow() | 90 | void KWinWaylandImageGrabber::grabActiveWindow() | ||
57 | { | 91 | { | ||
58 | // unsupported | 92 | // unsupported | ||
59 | emit pixmapChanged(QPixmap()); | 93 | emit pixmapChanged(QPixmap()); | ||
60 | } | 94 | } | ||
61 | 95 | | |||
62 | void KWinWaylandImageGrabber::grabRectangularRegion() | 96 | void KWinWaylandImageGrabber::grabRectangularRegion() | ||
63 | { | 97 | { | ||
64 | // unsupported | 98 | // unsupported | ||
65 | emit pixmapChanged(QPixmap()); | 99 | emit pixmapChanged(QPixmap()); | ||
66 | } | 100 | } | ||
67 | 101 | | |||
68 | static int readData(int fd, QByteArray &data) | | |||
69 | { | | |||
70 | // implementation based on QtWayland file qwaylanddataoffer.cpp | | |||
71 | char buf[4096]; | | |||
72 | int retryCount = 0; | | |||
73 | int n; | | |||
74 | while (true) { | | |||
75 | n = QT_READ(fd, buf, sizeof buf); | | |||
76 | // give user 30 sec to click a window, afterwards considered as error | | |||
77 | if (n == -1 && (errno == EAGAIN) && ++retryCount < 30000) { | | |||
78 | usleep(1000); | | |||
79 | } else { | | |||
80 | break; | | |||
81 | } | | |||
82 | } | | |||
83 | if (n > 0) { | | |||
84 | data.append(buf, n); | | |||
85 | n = readData(fd, data); | | |||
86 | } | | |||
87 | return n; | | |||
88 | } | | |||
89 | | ||||
90 | void KWinWaylandImageGrabber::grabWindowUnderCursor() | 102 | void KWinWaylandImageGrabber::grabWindowUnderCursor() | ||
91 | { | 103 | { | ||
92 | QDBusInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Screenshot"), QStringLiteral("org.kde.kwin.Screenshot")); | | |||
93 | | ||||
94 | int mask = 0; | 104 | int mask = 0; | ||
95 | if (mCaptureDecorations) { | 105 | if (mCaptureDecorations) { | ||
96 | mask = 1; | 106 | mask = 1; | ||
97 | } | 107 | } | ||
98 | if (mCapturePointer) { | 108 | if (mCapturePointer) { | ||
99 | mask |= 1 << 1; | 109 | mask |= 1 << 1; | ||
100 | } | 110 | } | ||
101 | 111 | grab(Mode::Window, mask); | |||
102 | int pipeFds[2]; | | |||
103 | if (pipe2(pipeFds, O_CLOEXEC|O_NONBLOCK) != 0) { | | |||
104 | emit imageGrabFailed(); | | |||
105 | return; | | |||
106 | } | 112 | } | ||
107 | const int pipeFd = pipeFds[0]; | | |||
108 | 113 | | |||
109 | auto call = interface.asyncCall(QStringLiteral("interactive"), QVariant::fromValue(QDBusUnixFileDescriptor(pipeFds[1])), mask); | 114 | void KWinWaylandImageGrabber::grabTransientWithParent() | ||
115 | { | ||||
116 | // unsupported, perform grab window under cursor | ||||
117 | grabWindowUnderCursor(); | ||||
118 | } | ||||
110 | 119 | | |||
111 | auto readImage = [pipeFd] () -> QImage { | 120 | QPixmap KWinWaylandImageGrabber::blendCursorImage(const QPixmap &pixmap, int x, int y, int width, int height) | ||
112 | QByteArray content; | 121 | { | ||
113 | if (readData(pipeFd, content) != 0) { | 122 | Q_UNUSED(x) | ||
114 | close(pipeFd); | 123 | Q_UNUSED(y) | ||
115 | return QImage(); | 124 | Q_UNUSED(width) | ||
125 | Q_UNUSED(height) | ||||
126 | return pixmap; | ||||
116 | } | 127 | } | ||
117 | close(pipeFd); | 128 | | ||
118 | QDataStream ds(content); | 129 | void KWinWaylandImageGrabber::startReadImage(int readPipe) | ||
119 | QImage image; | 130 | { | ||
120 | ds >> image; | | |||
121 | return image; | | |||
122 | }; | | |||
123 | QFutureWatcher<QImage> *watcher = new QFutureWatcher<QImage>(this); | 131 | QFutureWatcher<QImage> *watcher = new QFutureWatcher<QImage>(this); | ||
124 | QObject::connect(watcher, &QFutureWatcher<QImage>::finished, this, | 132 | QObject::connect(watcher, &QFutureWatcher<QImage>::finished, this, | ||
125 | [watcher, this] { | 133 | [watcher, this] { | ||
126 | watcher->deleteLater(); | 134 | watcher->deleteLater(); | ||
127 | const QImage img = watcher->result(); | 135 | const QImage img = watcher->result(); | ||
128 | emit pixmapChanged(QPixmap::fromImage(img)); | 136 | emit pixmapChanged(QPixmap::fromImage(img)); | ||
129 | } | 137 | } | ||
130 | ); | 138 | ); | ||
131 | watcher->setFuture(QtConcurrent::run(readImage)); | 139 | watcher->setFuture(QtConcurrent::run(readImage, readPipe)); | ||
132 | | ||||
133 | close(pipeFds[1]); | | |||
134 | } | 140 | } | ||
135 | 141 | | |||
136 | void KWinWaylandImageGrabber::grabTransientWithParent() | 142 | template <typename T> | ||
143 | void KWinWaylandImageGrabber::callDBus(Mode mode, int writeFd, T argument) | ||||
137 | { | 144 | { | ||
138 | // unsupported, perform grab window under cursor | 145 | QDBusInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Screenshot"), QStringLiteral("org.kde.kwin.Screenshot")); | ||
139 | grabWindowUnderCursor(); | 146 | static const QMap<Mode, QString> s_hash = { | ||
147 | {Mode::Window, QStringLiteral("interactive")}, | ||||
148 | {Mode::CurrentScreen, QStringLiteral("screenshotScreen")}, | ||||
149 | {Mode::FullScreen, QStringLiteral("screenshotFullscreen")} | ||||
150 | }; | ||||
151 | auto it = s_hash.find(mode); | ||||
152 | Q_ASSERT(it != s_hash.end()); | ||||
153 | interface.asyncCall(it.value(), QVariant::fromValue(QDBusUnixFileDescriptor(writeFd)), argument); | ||||
140 | } | 154 | } | ||
141 | 155 | | |||
142 | QPixmap KWinWaylandImageGrabber::blendCursorImage(const QPixmap &pixmap, int x, int y, int width, int height) | 156 | template <typename T> | ||
157 | void KWinWaylandImageGrabber::grab(Mode mode, T argument) | ||||
143 | { | 158 | { | ||
144 | Q_UNUSED(x) | 159 | int pipeFds[2]; | ||
145 | Q_UNUSED(y) | 160 | if (pipe2(pipeFds, O_CLOEXEC|O_NONBLOCK) != 0) { | ||
146 | Q_UNUSED(width) | 161 | emit imageGrabFailed(); | ||
147 | Q_UNUSED(height) | 162 | return; | ||
148 | return pixmap; | 163 | } | ||
164 | | ||||
165 | callDBus(mode, pipeFds[1], argument); | ||||
166 | startReadImage(pipeFds[0]); | ||||
167 | | ||||
168 | close(pipeFds[1]); | ||||
149 | } | 169 | } |