forked from quickshell/quickshell
wayland/screencopy: add screencopy
This commit is contained in:
parent
918dd2392d
commit
cd429142a4
37 changed files with 3149 additions and 3 deletions
225
src/wayland/screencopy/image_copy_capture/image_copy_capture.cpp
Normal file
225
src/wayland/screencopy/image_copy_capture/image_copy_capture.cpp
Normal file
|
@ -0,0 +1,225 @@
|
|||
#include "image_copy_capture.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
#include <private/qwaylandscreen_p.h>
|
||||
#include <qlogging.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qrect.h>
|
||||
#include <qscreen.h>
|
||||
#include <qtmetamacros.h>
|
||||
#include <qwayland-ext-image-copy-capture-v1.h>
|
||||
#include <qwaylandclientextension.h>
|
||||
#include <sys/types.h>
|
||||
#include <wayland-ext-image-copy-capture-v1-client-protocol.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
#include "../manager.hpp"
|
||||
#include "image_copy_capture_p.hpp"
|
||||
|
||||
namespace qs::wayland::screencopy::icc {
|
||||
|
||||
namespace {
|
||||
Q_LOGGING_CATEGORY(logIcc, "quickshell.wayland.screencopy.icc", QtWarningMsg);
|
||||
}
|
||||
|
||||
using IccCaptureSession = QtWayland::ext_image_copy_capture_session_v1;
|
||||
using IccCaptureFrame = QtWayland::ext_image_copy_capture_frame_v1;
|
||||
|
||||
IccScreencopyContext::IccScreencopyContext(::ext_image_copy_capture_session_v1* session)
|
||||
: IccCaptureSession(session) {}
|
||||
|
||||
IccScreencopyContext::~IccScreencopyContext() {
|
||||
if (this->IccCaptureSession::object()) {
|
||||
this->IccCaptureSession::destroy();
|
||||
}
|
||||
|
||||
if (this->IccCaptureFrame::object()) {
|
||||
this->IccCaptureFrame::destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void IccScreencopyContext::captureFrame() {
|
||||
if (this->IccCaptureFrame::object() || this->capturePending) return;
|
||||
|
||||
if (this->statePending) this->capturePending = true;
|
||||
else this->doCapture();
|
||||
}
|
||||
|
||||
void IccScreencopyContext::ext_image_copy_capture_session_v1_buffer_size(
|
||||
uint32_t width,
|
||||
uint32_t height
|
||||
) {
|
||||
this->clearOldState();
|
||||
|
||||
this->request.width = width;
|
||||
this->request.height = height;
|
||||
}
|
||||
|
||||
void IccScreencopyContext::ext_image_copy_capture_session_v1_shm_format(uint32_t format) {
|
||||
this->clearOldState();
|
||||
|
||||
this->request.shm.formats.push(format);
|
||||
}
|
||||
|
||||
void IccScreencopyContext::ext_image_copy_capture_session_v1_dmabuf_device(wl_array* device) {
|
||||
this->clearOldState();
|
||||
|
||||
if (device->size != sizeof(dev_t)) {
|
||||
qCFatal(logIcc) << "The size of dev_t used by the compositor and quickshell is mismatched. Try "
|
||||
"recompiling both.";
|
||||
}
|
||||
|
||||
this->request.dmabuf.device = *reinterpret_cast<dev_t*>(device->data);
|
||||
}
|
||||
|
||||
void IccScreencopyContext::ext_image_copy_capture_session_v1_dmabuf_format(
|
||||
uint32_t format,
|
||||
wl_array* modifiers
|
||||
) {
|
||||
this->clearOldState();
|
||||
|
||||
auto* modifierArray = reinterpret_cast<uint64_t*>(modifiers->data);
|
||||
auto modifierCount = modifiers->size / sizeof(uint64_t);
|
||||
|
||||
auto reqFormat = buffer::WlBufferRequest::DmaFormat(format);
|
||||
|
||||
for (uint16_t i = 0; i != modifierCount; i++) {
|
||||
reqFormat.modifiers.push(modifierArray[i]); // NOLINT
|
||||
}
|
||||
|
||||
this->request.dmabuf.formats.push(reqFormat);
|
||||
}
|
||||
|
||||
void IccScreencopyContext::ext_image_copy_capture_session_v1_done() {
|
||||
this->statePending = false;
|
||||
|
||||
if (this->capturePending) {
|
||||
this->doCapture();
|
||||
}
|
||||
}
|
||||
|
||||
void IccScreencopyContext::ext_image_copy_capture_session_v1_stopped() {
|
||||
qCInfo(logIcc) << "Ending recording due to screencopy stop for" << this;
|
||||
emit this->stopped();
|
||||
}
|
||||
|
||||
void IccScreencopyContext::clearOldState() {
|
||||
if (!this->statePending) {
|
||||
this->request = buffer::WlBufferRequest();
|
||||
this->statePending = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IccScreencopyContext::doCapture() {
|
||||
this->capturePending = false;
|
||||
|
||||
auto newBuffer = false;
|
||||
auto* backbuffer = this->mSwapchain.createBackbuffer(this->request, &newBuffer);
|
||||
|
||||
this->IccCaptureFrame::init(this->IccCaptureSession::create_frame());
|
||||
this->IccCaptureFrame::attach_buffer(backbuffer->buffer());
|
||||
|
||||
if (newBuffer) {
|
||||
// If the buffer was replaced, it will be blank and the compositor needs
|
||||
// to repaint the whole thing.
|
||||
this->IccCaptureFrame::damage_buffer(
|
||||
0,
|
||||
0,
|
||||
static_cast<int>(this->request.width),
|
||||
static_cast<int>(this->request.height)
|
||||
);
|
||||
|
||||
// We don't care about partial damage if the whole buffer was replaced.
|
||||
this->lastDamage = QRect();
|
||||
} else if (!this->lastDamage.isEmpty()) {
|
||||
// If buffers were swapped between the last frame and the current one, request a repaint
|
||||
// of the backbuffer in the same places that changes to the frontbuffer were recorded.
|
||||
this->IccCaptureFrame::damage_buffer(
|
||||
this->lastDamage.x(),
|
||||
this->lastDamage.y(),
|
||||
this->lastDamage.width(),
|
||||
this->lastDamage.height()
|
||||
);
|
||||
|
||||
// We don't need to do this more than once per buffer swap.
|
||||
this->lastDamage = QRect();
|
||||
}
|
||||
|
||||
this->IccCaptureFrame::capture();
|
||||
}
|
||||
|
||||
void IccScreencopyContext::ext_image_copy_capture_frame_v1_transform(uint32_t transform) {
|
||||
this->mSwapchain.backbuffer()->transform = transform;
|
||||
}
|
||||
|
||||
void IccScreencopyContext::ext_image_copy_capture_frame_v1_damage(
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
int32_t width,
|
||||
int32_t height
|
||||
) {
|
||||
this->damage = this->damage.united(QRect(x, y, width, height));
|
||||
}
|
||||
|
||||
void IccScreencopyContext::ext_image_copy_capture_frame_v1_ready() {
|
||||
this->IccCaptureFrame::destroy();
|
||||
|
||||
this->mSwapchain.swapBuffers();
|
||||
this->lastDamage = this->damage;
|
||||
this->damage = QRect();
|
||||
|
||||
emit this->frameCaptured();
|
||||
}
|
||||
|
||||
void IccScreencopyContext::ext_image_copy_capture_frame_v1_failed(uint32_t reason) {
|
||||
switch (static_cast<IccCaptureFrame::failure_reason>(reason)) {
|
||||
case IccCaptureFrame::failure_reason_buffer_constraints:
|
||||
qFatal(logIcc) << "Got a buffer_constraints failure, however the buffer matches the last sent "
|
||||
"size. There is a bug in quickshell or your compositor.";
|
||||
break;
|
||||
case IccCaptureFrame::failure_reason_stopped:
|
||||
// Handled in the ExtCaptureSession handler.
|
||||
break;
|
||||
case IccCaptureFrame::failure_reason_unknown:
|
||||
qCWarning(logIcc) << "Ending recording due to screencopy failure for" << this;
|
||||
emit this->stopped();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IccManager::IccManager(): QWaylandClientExtensionTemplate(1) { this->initialize(); }
|
||||
|
||||
IccManager* IccManager::instance() {
|
||||
static auto* instance = new IccManager();
|
||||
return instance;
|
||||
}
|
||||
|
||||
ScreencopyContext*
|
||||
IccManager::createSession(::ext_image_capture_source_v1* source, bool paintCursors) {
|
||||
auto* session = this->create_session(
|
||||
source,
|
||||
paintCursors ? QtWayland::ext_image_copy_capture_manager_v1::options_paint_cursors : 0
|
||||
);
|
||||
return new IccScreencopyContext(session);
|
||||
}
|
||||
|
||||
IccOutputSourceManager::IccOutputSourceManager(): QWaylandClientExtensionTemplate(1) {
|
||||
this->initialize();
|
||||
}
|
||||
|
||||
IccOutputSourceManager* IccOutputSourceManager::instance() {
|
||||
static auto* instance = new IccOutputSourceManager();
|
||||
return instance;
|
||||
}
|
||||
|
||||
ScreencopyContext* IccOutputSourceManager::captureOutput(QScreen* screen, bool paintCursors) {
|
||||
auto* waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen*>(screen->handle());
|
||||
if (!waylandScreen) return nullptr;
|
||||
|
||||
return IccManager::instance()->createSession(
|
||||
this->create_source(waylandScreen->output()),
|
||||
paintCursors
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace qs::wayland::screencopy::icc
|
Loading…
Add table
Add a link
Reference in a new issue