mirror of
https://github.com/hyprwm/hyprlock.git
synced 2025-01-24 19:39:49 +01:00
core: fix background screenshot on nvidia (#656)
Fixes DMA buffer screencopy on nvidia cards. Additionally adds shm screencopy as an option
This commit is contained in:
parent
742eb98c6a
commit
07b5e1b4cd
13 changed files with 615 additions and 273 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,3 +11,4 @@ compile_commands.json
|
|||
protocols/*.cpp
|
||||
protocols/*.hpp
|
||||
*.kdev4
|
||||
.gdb_history
|
||||
|
|
|
@ -217,6 +217,7 @@ void CConfigManager::init() {
|
|||
m_config.addConfigValue("general:ignore_empty_input", Hyprlang::INT{0});
|
||||
m_config.addConfigValue("general:immediate_render", Hyprlang::INT{0});
|
||||
m_config.addConfigValue("general:fractional_scaling", Hyprlang::INT{2});
|
||||
m_config.addConfigValue("general:screencopy_mode", Hyprlang::INT{0});
|
||||
|
||||
m_config.addConfigValue("auth:pam:enabled", Hyprlang::INT{1});
|
||||
m_config.addConfigValue("auth:pam:module", Hyprlang::STRING{"hyprlock"});
|
||||
|
|
|
@ -286,6 +286,8 @@ void CHyprlock::run() {
|
|||
else if (IFACE == zwlr_screencopy_manager_v1_interface.name)
|
||||
m_sWaylandState.screencopy =
|
||||
makeShared<CCZwlrScreencopyManagerV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &zwlr_screencopy_manager_v1_interface, 3));
|
||||
else if (IFACE == wl_shm_interface.name)
|
||||
m_sWaylandState.shm = makeShared<CCWlShm>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wl_shm_interface, 1));
|
||||
else
|
||||
return;
|
||||
|
||||
|
@ -834,6 +836,10 @@ SP<CCZwlrScreencopyManagerV1> CHyprlock::getScreencopy() {
|
|||
return m_sWaylandState.screencopy;
|
||||
}
|
||||
|
||||
SP<CCWlShm> CHyprlock::getShm() {
|
||||
return m_sWaylandState.shm;
|
||||
}
|
||||
|
||||
void CHyprlock::attemptRestoreOnDeath() {
|
||||
if (m_bTerminate || m_sCurrentDesktop != "Hyprland")
|
||||
return;
|
||||
|
|
|
@ -79,6 +79,7 @@ class CHyprlock {
|
|||
SP<CCWpFractionalScaleManagerV1> getFractionalMgr();
|
||||
SP<CCWpViewporter> getViewporter();
|
||||
SP<CCZwlrScreencopyManagerV1> getScreencopy();
|
||||
SP<CCWlShm> getShm();
|
||||
|
||||
int32_t m_iKeebRepeatRate = 25;
|
||||
int32_t m_iKeebRepeatDelay = 600;
|
||||
|
@ -130,6 +131,7 @@ class CHyprlock {
|
|||
SP<CCWpFractionalScaleManagerV1> fractional = nullptr;
|
||||
SP<CCWpViewporter> viewporter = nullptr;
|
||||
SP<CCZwlrScreencopyManagerV1> screencopy = nullptr;
|
||||
SP<CCWlShm> shm = nullptr;
|
||||
} m_sWaylandState;
|
||||
|
||||
void addDmabufListener();
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <fcntl.h>
|
||||
#include "MiscFunctions.hpp"
|
||||
#include "Log.hpp"
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
|
@ -12,7 +15,7 @@ std::string absolutePath(const std::string& rawpath, const std::string& currentD
|
|||
// Handling where rawpath starts with '~'
|
||||
if (!rawpath.empty() && rawpath[0] == '~') {
|
||||
static const char* const ENVHOME = getenv("HOME");
|
||||
path = std::filesystem::path(ENVHOME) / path.relative_path().string().substr(2);
|
||||
path = std::filesystem::path(ENVHOME) / path.relative_path().string().substr(2);
|
||||
}
|
||||
|
||||
// Handling e.g. ./, ../
|
||||
|
@ -97,4 +100,40 @@ int64_t configStringToInt(const std::string& VALUE) {
|
|||
} catch (std::exception& e) { throw std::invalid_argument(std::string{"stoll threw: "} + e.what()); }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int createPoolFile(size_t size, std::string& name) {
|
||||
const auto XDGRUNTIMEDIR = getenv("XDG_RUNTIME_DIR");
|
||||
if (!XDGRUNTIMEDIR) {
|
||||
Debug::log(CRIT, "XDG_RUNTIME_DIR not set!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
name = std::string(XDGRUNTIMEDIR) + "/.hyprlock_sc_XXXXXX";
|
||||
|
||||
const auto FD = mkstemp((char*)name.c_str());
|
||||
if (FD < 0) {
|
||||
Debug::log(CRIT, "createPoolFile: fd < 0");
|
||||
return -1;
|
||||
}
|
||||
// set cloexec
|
||||
long flags = fcntl(FD, F_GETFD);
|
||||
if (flags == -1) {
|
||||
close(FD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fcntl(FD, F_SETFD, flags | FD_CLOEXEC) == -1) {
|
||||
close(FD);
|
||||
Debug::log(CRIT, "createPoolFile: fcntl < 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ftruncate(FD, size) < 0) {
|
||||
close(FD);
|
||||
Debug::log(CRIT, "createPoolFile: ftruncate < 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return FD;
|
||||
}
|
|
@ -6,3 +6,4 @@
|
|||
|
||||
std::string absolutePath(const std::string&, const std::string&);
|
||||
int64_t configStringToInt(const std::string& VALUE);
|
||||
int createPoolFile(size_t size, std::string& name);
|
||||
|
|
|
@ -15,13 +15,13 @@ using namespace Hyprgraphics;
|
|||
|
||||
CAsyncResourceGatherer::CAsyncResourceGatherer() {
|
||||
if (g_pHyprlock->getScreencopy())
|
||||
enqueueDMAFrames();
|
||||
enqueueScreencopyFrames();
|
||||
|
||||
initialGatherThread = std::thread([this]() { this->gather(); });
|
||||
asyncLoopThread = std::thread([this]() { this->asyncAssetSpinLock(); });
|
||||
}
|
||||
|
||||
void CAsyncResourceGatherer::enqueueDMAFrames() {
|
||||
void CAsyncResourceGatherer::enqueueScreencopyFrames() {
|
||||
// some things can't be done async :(
|
||||
// gather background textures when needed
|
||||
|
||||
|
@ -56,7 +56,7 @@ void CAsyncResourceGatherer::enqueueDMAFrames() {
|
|||
|
||||
const auto PMONITOR = MON->get();
|
||||
|
||||
dmas.emplace_back(std::make_unique<CDMAFrame>(PMONITOR));
|
||||
scframes.emplace_back(std::make_unique<CScreencopyFrame>(PMONITOR));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,9 +73,9 @@ SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
|
|||
}
|
||||
};
|
||||
|
||||
for (auto& dma : dmas) {
|
||||
if (id == dma->resourceID)
|
||||
return dma->asset.ready ? &dma->asset : nullptr;
|
||||
for (auto& frame : scframes) {
|
||||
if (id == frame->m_resourceID)
|
||||
return frame->m_asset.ready ? &frame->m_asset : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -130,7 +130,7 @@ void CAsyncResourceGatherer::gather() {
|
|||
}
|
||||
}
|
||||
|
||||
while (!g_pHyprlock->m_bTerminate && std::any_of(dmas.begin(), dmas.end(), [](const auto& d) { return !d->asset.ready; })) {
|
||||
while (!g_pHyprlock->m_bTerminate && std::any_of(scframes.begin(), scframes.end(), [](const auto& d) { return !d->m_asset.ready; })) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
|
@ -216,7 +216,7 @@ void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
|
|||
target.id = rq.id;
|
||||
|
||||
const int FONTSIZE = rq.props.contains("font_size") ? std::any_cast<int>(rq.props.at("font_size")) : 16;
|
||||
const CHyprColor FONTCOLOR = rq.props.contains("color") ? std::any_cast<CHyprColor>(rq.props.at("color")) : CHyprColor(1.0, 1.0, 1.0, 1.0);
|
||||
const CHyprColor FONTCOLOR = rq.props.contains("color") ? std::any_cast<CHyprColor>(rq.props.at("color")) : CHyprColor(1.0, 1.0, 1.0, 1.0);
|
||||
const std::string FONTFAMILY = rq.props.contains("font_family") ? std::any_cast<std::string>(rq.props.at("font_family")) : "Sans";
|
||||
const bool ISCMD = rq.props.contains("cmd") ? std::any_cast<bool>(rq.props.at("cmd")) : false;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "DMAFrame.hpp"
|
||||
#include "Screencopy.hpp"
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
@ -75,7 +75,7 @@ class CAsyncResourceGatherer {
|
|||
Vector2D size;
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<CDMAFrame>> dmas;
|
||||
std::vector<std::unique_ptr<CScreencopyFrame>> scframes;
|
||||
|
||||
std::vector<SPreloadTarget> preloadTargets;
|
||||
std::mutex preloadTargetsMutex;
|
||||
|
@ -83,5 +83,5 @@ class CAsyncResourceGatherer {
|
|||
std::unordered_map<std::string, SPreloadedAsset> assets;
|
||||
|
||||
void gather();
|
||||
void enqueueDMAFrames();
|
||||
void enqueueScreencopyFrames();
|
||||
};
|
||||
|
|
|
@ -1,216 +0,0 @@
|
|||
#include "DMAFrame.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "../core/Egl.hpp"
|
||||
#include <EGL/eglext.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <GLES3/gl32.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullptr;
|
||||
static PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr;
|
||||
|
||||
//
|
||||
std::string CDMAFrame::getResourceId(COutput* output) {
|
||||
return std::format("dma:{}-{}x{}", output->stringPort, output->size.x, output->size.y);
|
||||
}
|
||||
|
||||
CDMAFrame::CDMAFrame(COutput* output_) {
|
||||
resourceID = getResourceId(output_);
|
||||
|
||||
if (!glEGLImageTargetTexture2DOES) {
|
||||
glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
|
||||
if (!glEGLImageTargetTexture2DOES) {
|
||||
Debug::log(ERR, "No glEGLImageTargetTexture2DOES??");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!eglQueryDmaBufModifiersEXT)
|
||||
eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT");
|
||||
|
||||
// firstly, plant a listener for the frame
|
||||
frameCb = makeShared<CCZwlrScreencopyFrameV1>(g_pHyprlock->getScreencopy()->sendCaptureOutput(false, output_->output->resource()));
|
||||
|
||||
frameCb->setBufferDone([this](CCZwlrScreencopyFrameV1* r) {
|
||||
Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)this);
|
||||
|
||||
if (!onBufferDone()) {
|
||||
Debug::log(ERR, "onBufferDone failed");
|
||||
return;
|
||||
}
|
||||
|
||||
frameCb->sendCopy(wlBuffer->resource());
|
||||
|
||||
Debug::log(TRACE, "[sc] wlr frame copied");
|
||||
});
|
||||
|
||||
frameCb->setLinuxDmabuf([this](CCZwlrScreencopyFrameV1* r, uint32_t format, uint32_t width, uint32_t height) {
|
||||
Debug::log(TRACE, "[sc] wlrOnDmabuf for {}", (void*)this);
|
||||
|
||||
w = width;
|
||||
h = height;
|
||||
fmt = format;
|
||||
|
||||
Debug::log(TRACE, "[sc] DMABUF format reported: {:x}", format);
|
||||
});
|
||||
|
||||
frameCb->setReady([this](CCZwlrScreencopyFrameV1* r, uint32_t, uint32_t, uint32_t) {
|
||||
Debug::log(TRACE, "[sc] wlrOnReady for {}", (void*)this);
|
||||
|
||||
if (!onBufferReady()) {
|
||||
Debug::log(ERR, "onBufferReady failed");
|
||||
return;
|
||||
}
|
||||
|
||||
frameCb.reset();
|
||||
});
|
||||
|
||||
frameCb->setBuffer([this](CCZwlrScreencopyFrameV1* r, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
|
||||
Debug::log(TRACE, "[sc] wlrOnBuffer for {}", (void*)this);
|
||||
|
||||
frameSize = stride * height;
|
||||
frameStride = stride;
|
||||
});
|
||||
}
|
||||
|
||||
CDMAFrame::~CDMAFrame() {
|
||||
if (g_pEGL)
|
||||
eglDestroyImage(g_pEGL->eglDisplay, image);
|
||||
|
||||
// leaks bo and stuff but lives throughout so for now who cares
|
||||
}
|
||||
|
||||
bool CDMAFrame::onBufferDone() {
|
||||
uint32_t flags = GBM_BO_USE_RENDERING;
|
||||
|
||||
if (!eglQueryDmaBufModifiersEXT) {
|
||||
Debug::log(WARN, "Querying modifiers without eglQueryDmaBufModifiersEXT support");
|
||||
bo = gbm_bo_create(g_pHyprlock->dma.gbmDevice, w, h, fmt, flags);
|
||||
} else {
|
||||
std::vector<uint64_t> mods;
|
||||
mods.resize(64);
|
||||
std::vector<EGLBoolean> externalOnly;
|
||||
externalOnly.resize(64);
|
||||
int num = 0;
|
||||
if (!eglQueryDmaBufModifiersEXT(g_pEGL->eglDisplay, fmt, 64, mods.data(), externalOnly.data(), &num) || num == 0) {
|
||||
Debug::log(WARN, "eglQueryDmaBufModifiersEXT failed, falling back to regular bo");
|
||||
bo = gbm_bo_create(g_pHyprlock->dma.gbmDevice, w, h, fmt, flags);
|
||||
} else {
|
||||
Debug::log(LOG, "eglQueryDmaBufModifiersEXT found {} mods", num);
|
||||
std::vector<uint64_t> goodMods;
|
||||
for (int i = 0; i < num; ++i) {
|
||||
if (externalOnly[i]) {
|
||||
Debug::log(TRACE, "Modifier {:x} failed test", mods[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug::log(TRACE, "Modifier {:x} passed test", mods[i]);
|
||||
goodMods.push_back(mods[i]);
|
||||
}
|
||||
|
||||
uint64_t zero = 0;
|
||||
bool hasLinear = std::find(goodMods.begin(), goodMods.end(), 0) != goodMods.end();
|
||||
|
||||
bo = gbm_bo_create_with_modifiers2(g_pHyprlock->dma.gbmDevice, w, h, fmt, hasLinear ? &zero : goodMods.data(), hasLinear ? 1 : goodMods.size(), flags);
|
||||
}
|
||||
}
|
||||
|
||||
if (!bo) {
|
||||
Debug::log(ERR, "Couldn't create a drm buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
planes = gbm_bo_get_plane_count(bo);
|
||||
|
||||
uint64_t mod = gbm_bo_get_modifier(bo);
|
||||
Debug::log(LOG, "bo chose modifier {:x}", mod);
|
||||
|
||||
auto params = makeShared<CCZwpLinuxBufferParamsV1>(g_pHyprlock->dma.linuxDmabuf->sendCreateParams());
|
||||
if (!params) {
|
||||
Debug::log(ERR, "zwp_linux_dmabuf_v1_create_params failed");
|
||||
gbm_bo_destroy(bo);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t plane = 0; plane < (size_t)planes; plane++) {
|
||||
size[plane] = 0;
|
||||
stride[plane] = gbm_bo_get_stride_for_plane(bo, plane);
|
||||
offset[plane] = gbm_bo_get_offset(bo, plane);
|
||||
fd[plane] = gbm_bo_get_fd_for_plane(bo, plane);
|
||||
|
||||
if (fd[plane] < 0) {
|
||||
Debug::log(ERR, "gbm_bo_get_fd_for_plane failed");
|
||||
params.reset();
|
||||
gbm_bo_destroy(bo);
|
||||
for (size_t plane_tmp = 0; plane_tmp < plane; plane_tmp++) {
|
||||
close(fd[plane_tmp]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
params->sendAdd(fd[plane], plane, offset[plane], stride[plane], mod >> 32, mod & 0xffffffff);
|
||||
}
|
||||
|
||||
wlBuffer = makeShared<CCWlBuffer>(params->sendCreateImmed(w, h, fmt, (zwpLinuxBufferParamsV1Flags)0));
|
||||
params.reset();
|
||||
|
||||
if (!wlBuffer) {
|
||||
Debug::log(ERR, "[pw] zwp_linux_buffer_params_v1_create_immed failed");
|
||||
gbm_bo_destroy(bo);
|
||||
for (size_t plane = 0; plane < (size_t)planes; plane++)
|
||||
close(fd[plane]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDMAFrame::onBufferReady() {
|
||||
static const int general_attribs = 3;
|
||||
static const int plane_attribs = 5;
|
||||
static const int entries_per_attrib = 2;
|
||||
EGLAttrib attribs[(general_attribs + plane_attribs * 4) * entries_per_attrib + 1];
|
||||
int attr = 0;
|
||||
Vector2D size{w, h};
|
||||
|
||||
attribs[attr++] = EGL_WIDTH;
|
||||
attribs[attr++] = size.x;
|
||||
attribs[attr++] = EGL_HEIGHT;
|
||||
attribs[attr++] = size.y;
|
||||
attribs[attr++] = EGL_LINUX_DRM_FOURCC_EXT;
|
||||
attribs[attr++] = fmt;
|
||||
attribs[attr++] = EGL_DMA_BUF_PLANE0_FD_EXT;
|
||||
attribs[attr++] = fd[0];
|
||||
attribs[attr++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
|
||||
attribs[attr++] = offset[0];
|
||||
attribs[attr++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
|
||||
attribs[attr++] = stride[0];
|
||||
attribs[attr] = EGL_NONE;
|
||||
|
||||
image = eglCreateImage(g_pEGL->eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
|
||||
|
||||
if (image == EGL_NO_IMAGE) {
|
||||
Debug::log(ERR, "failed creating an egl image");
|
||||
return false;
|
||||
}
|
||||
|
||||
asset.texture.allocate();
|
||||
asset.texture.m_vSize = size;
|
||||
glBindTexture(GL_TEXTURE_2D, asset.texture.m_iTexID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
Debug::log(LOG, "Got dma frame with size {}", size);
|
||||
|
||||
asset.ready = true;
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "../core/Output.hpp"
|
||||
#include <gbm.h>
|
||||
#include "Shared.hpp"
|
||||
#include "linux-dmabuf-v1.hpp"
|
||||
#include "wlr-screencopy-unstable-v1.hpp"
|
||||
|
||||
class CDMAFrame;
|
||||
|
||||
class CDMAFrame {
|
||||
public:
|
||||
static std::string getResourceId(COutput* output);
|
||||
|
||||
CDMAFrame(COutput* mon);
|
||||
~CDMAFrame();
|
||||
|
||||
bool onBufferDone();
|
||||
bool onBufferReady();
|
||||
|
||||
SP<CCWlBuffer> wlBuffer = nullptr;
|
||||
|
||||
std::string resourceID;
|
||||
|
||||
SPreloadedAsset asset;
|
||||
|
||||
private:
|
||||
gbm_bo* bo = nullptr;
|
||||
|
||||
int planes = 0;
|
||||
|
||||
int fd[4];
|
||||
uint32_t size[4], stride[4], offset[4];
|
||||
|
||||
SP<CCZwlrScreencopyFrameV1> frameCb = nullptr;
|
||||
int w = 0, h = 0;
|
||||
uint32_t fmt = 0;
|
||||
size_t frameSize = 0;
|
||||
size_t frameStride = 0;
|
||||
|
||||
EGLImage image = nullptr;
|
||||
};
|
|
@ -1,5 +1,6 @@
|
|||
#include "Renderer.hpp"
|
||||
#include "Shaders.hpp"
|
||||
#include "Screencopy.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../core/AnimationManager.hpp"
|
||||
#include "../core/Egl.hpp"
|
||||
|
@ -7,9 +8,9 @@
|
|||
#include "../core/hyprlock.hpp"
|
||||
#include "../helpers/Color.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "../renderer/DMAFrame.hpp"
|
||||
#include <GLES3/gl32.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <algorithm>
|
||||
#include "widgets/PasswordInputField.hpp"
|
||||
#include "widgets/Background.hpp"
|
||||
|
@ -416,7 +417,7 @@ std::vector<std::unique_ptr<IWidget>>* CRenderer::getOrCreateWidgetsFor(const CS
|
|||
|
||||
std::string resourceID = "";
|
||||
if (PATH == "screenshot") {
|
||||
resourceID = CDMAFrame::getResourceId(surf->output);
|
||||
resourceID = CScreencopyFrame::getResourceId(surf->output);
|
||||
// When the initial gather of the asyncResourceGatherer is completed (ready), all DMAFrames are available.
|
||||
// Dynamic ones are tricky, because a screencopy would copy hyprlock itself.
|
||||
if (asyncResourceGatherer->gathered) {
|
||||
|
|
456
src/renderer/Screencopy.cpp
Normal file
456
src/renderer/Screencopy.cpp
Normal file
|
@ -0,0 +1,456 @@
|
|||
#include "Screencopy.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "../core/Egl.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "wlr-screencopy-unstable-v1.hpp"
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <gbm.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <GLES3/gl32.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullptr;
|
||||
static PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr;
|
||||
|
||||
//
|
||||
std::string CScreencopyFrame::getResourceId(COutput* output) {
|
||||
return std::format("screencopy:{}-{}x{}", output->stringPort, output->size.x, output->size.y);
|
||||
}
|
||||
|
||||
CScreencopyFrame::CScreencopyFrame(COutput* output) : m_output(output) {
|
||||
m_resourceID = getResourceId(m_output);
|
||||
|
||||
captureOutput();
|
||||
|
||||
static auto* const PSCMODE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:screencopy_mode");
|
||||
if (**PSCMODE == 1)
|
||||
m_frame = std::make_unique<CSCSHMFrame>(m_sc);
|
||||
else
|
||||
m_frame = std::make_unique<CSCDMAFrame>(m_sc);
|
||||
}
|
||||
|
||||
void CScreencopyFrame::captureOutput() {
|
||||
m_sc = makeShared<CCZwlrScreencopyFrameV1>(g_pHyprlock->getScreencopy()->sendCaptureOutput(false, m_output->output->resource()));
|
||||
|
||||
m_sc->setBufferDone([this](CCZwlrScreencopyFrameV1* r) {
|
||||
Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)this);
|
||||
|
||||
if (!m_frame || !m_frame->onBufferDone() || !m_frame->m_wlBuffer) {
|
||||
Debug::log(ERR, "[sc] Failed to create a wayland buffer for the screencopy frame");
|
||||
return;
|
||||
}
|
||||
|
||||
m_sc->sendCopy(m_frame->m_wlBuffer->resource());
|
||||
|
||||
Debug::log(TRACE, "[sc] wlr frame copied");
|
||||
});
|
||||
|
||||
m_sc->setFailed([this](CCZwlrScreencopyFrameV1* r) {
|
||||
Debug::log(ERR, "[sc] wlrOnFailed for {}", (void*)r);
|
||||
|
||||
m_frame.reset();
|
||||
});
|
||||
|
||||
m_sc->setReady([this](CCZwlrScreencopyFrameV1* r, uint32_t, uint32_t, uint32_t) {
|
||||
Debug::log(TRACE, "[sc] wlrOnReady for {}", (void*)this);
|
||||
|
||||
if (!m_frame || !m_frame->onBufferReady(m_asset)) {
|
||||
Debug::log(ERR, "[sc] Failed to bind the screencopy buffer to a texture");
|
||||
return;
|
||||
}
|
||||
|
||||
m_sc.reset();
|
||||
});
|
||||
}
|
||||
|
||||
CSCDMAFrame::CSCDMAFrame(SP<CCZwlrScreencopyFrameV1> sc) : m_sc(sc) {
|
||||
if (!glEGLImageTargetTexture2DOES) {
|
||||
glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
|
||||
if (!glEGLImageTargetTexture2DOES) {
|
||||
Debug::log(ERR, "No glEGLImageTargetTexture2DOES??");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!eglQueryDmaBufModifiersEXT)
|
||||
eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT");
|
||||
|
||||
m_sc->setLinuxDmabuf([this](CCZwlrScreencopyFrameV1* r, uint32_t format, uint32_t width, uint32_t height) {
|
||||
Debug::log(TRACE, "[sc] wlrOnDmabuf for {}", (void*)this);
|
||||
|
||||
m_w = width;
|
||||
m_h = height;
|
||||
m_fmt = format;
|
||||
|
||||
Debug::log(TRACE, "[sc] DMABUF format reported: {:x}", format);
|
||||
});
|
||||
|
||||
m_sc->setBuffer([](CCZwlrScreencopyFrameV1* r, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
|
||||
; // unused by dma
|
||||
});
|
||||
}
|
||||
|
||||
CSCDMAFrame::~CSCDMAFrame() {
|
||||
if (g_pEGL)
|
||||
eglDestroyImage(g_pEGL->eglDisplay, m_image);
|
||||
|
||||
// leaks bo and stuff but lives throughout so for now who cares
|
||||
}
|
||||
|
||||
bool CSCDMAFrame::onBufferDone() {
|
||||
uint32_t flags = GBM_BO_USE_RENDERING;
|
||||
|
||||
if (!eglQueryDmaBufModifiersEXT) {
|
||||
Debug::log(WARN, "Querying modifiers without eglQueryDmaBufModifiersEXT support");
|
||||
m_bo = gbm_bo_create(g_pHyprlock->dma.gbmDevice, m_w, m_h, m_fmt, flags);
|
||||
} else {
|
||||
std::array<uint64_t, 64> mods;
|
||||
std::array<EGLBoolean, 64> externalOnly;
|
||||
int num = 0;
|
||||
if (!eglQueryDmaBufModifiersEXT(g_pEGL->eglDisplay, m_fmt, 64, mods.data(), externalOnly.data(), &num) || num == 0) {
|
||||
Debug::log(WARN, "eglQueryDmaBufModifiersEXT failed, falling back to regular bo");
|
||||
m_bo = gbm_bo_create(g_pHyprlock->dma.gbmDevice, m_w, m_h, m_fmt, flags);
|
||||
} else {
|
||||
Debug::log(LOG, "eglQueryDmaBufModifiersEXT found {} mods", num);
|
||||
std::vector<uint64_t> goodMods;
|
||||
for (int i = 0; i < num; ++i) {
|
||||
if (externalOnly[i]) {
|
||||
Debug::log(TRACE, "Modifier {:x} failed test", mods[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug::log(TRACE, "Modifier {:x} passed test", mods[i]);
|
||||
goodMods.emplace_back(mods[i]);
|
||||
}
|
||||
|
||||
m_bo = gbm_bo_create_with_modifiers2(g_pHyprlock->dma.gbmDevice, m_w, m_h, m_fmt, goodMods.data(), goodMods.size(), flags);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_bo) {
|
||||
Debug::log(ERR, "[bo] Couldn't create a drm buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_planes = gbm_bo_get_plane_count(m_bo);
|
||||
Debug::log(LOG, "[bo] has {} plane(s)", m_planes);
|
||||
|
||||
m_mod = gbm_bo_get_modifier(m_bo);
|
||||
Debug::log(LOG, "[bo] chose modifier {:x}", m_mod);
|
||||
|
||||
auto params = makeShared<CCZwpLinuxBufferParamsV1>(g_pHyprlock->dma.linuxDmabuf->sendCreateParams());
|
||||
if (!params) {
|
||||
Debug::log(ERR, "zwp_linux_dmabuf_v1_create_params failed");
|
||||
gbm_bo_destroy(m_bo);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t plane = 0; plane < (size_t)m_planes; plane++) {
|
||||
m_stride[plane] = gbm_bo_get_stride_for_plane(m_bo, plane);
|
||||
m_offset[plane] = gbm_bo_get_offset(m_bo, plane);
|
||||
m_fd[plane] = gbm_bo_get_fd_for_plane(m_bo, plane);
|
||||
|
||||
if (m_fd[plane] < 0) {
|
||||
Debug::log(ERR, "gbm_m_bo_get_fd_for_plane failed");
|
||||
params.reset();
|
||||
gbm_bo_destroy(m_bo);
|
||||
for (size_t plane_tmp = 0; plane_tmp < plane; plane_tmp++) {
|
||||
close(m_fd[plane_tmp]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
params->sendAdd(m_fd[plane], plane, m_offset[plane], m_stride[plane], m_mod >> 32, m_mod & 0xffffffff);
|
||||
}
|
||||
|
||||
m_wlBuffer = makeShared<CCWlBuffer>(params->sendCreateImmed(m_w, m_h, m_fmt, (zwpLinuxBufferParamsV1Flags)0));
|
||||
params.reset();
|
||||
|
||||
if (!m_wlBuffer) {
|
||||
Debug::log(ERR, "[pw] zwp_linux_buffer_params_v1_create_immed failed");
|
||||
gbm_bo_destroy(m_bo);
|
||||
for (size_t plane = 0; plane < (size_t)m_planes; plane++)
|
||||
close(m_fd[plane]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSCDMAFrame::onBufferReady(SPreloadedAsset& asset) {
|
||||
static constexpr struct {
|
||||
EGLAttrib fd;
|
||||
EGLAttrib offset;
|
||||
EGLAttrib pitch;
|
||||
EGLAttrib modlo;
|
||||
EGLAttrib modhi;
|
||||
} attrNames[4] = {
|
||||
{EGL_DMA_BUF_PLANE0_FD_EXT, EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGL_DMA_BUF_PLANE0_PITCH_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT},
|
||||
{EGL_DMA_BUF_PLANE1_FD_EXT, EGL_DMA_BUF_PLANE1_OFFSET_EXT, EGL_DMA_BUF_PLANE1_PITCH_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT},
|
||||
{EGL_DMA_BUF_PLANE2_FD_EXT, EGL_DMA_BUF_PLANE2_OFFSET_EXT, EGL_DMA_BUF_PLANE2_PITCH_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT},
|
||||
{EGL_DMA_BUF_PLANE3_FD_EXT, EGL_DMA_BUF_PLANE3_OFFSET_EXT, EGL_DMA_BUF_PLANE3_PITCH_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT}};
|
||||
|
||||
std::vector<EGLAttrib> attribs = {
|
||||
EGL_WIDTH, m_w, EGL_HEIGHT, m_h, EGL_LINUX_DRM_FOURCC_EXT, m_fmt,
|
||||
};
|
||||
for (int i = 0; i < m_planes; i++) {
|
||||
attribs.emplace_back(attrNames[i].fd);
|
||||
attribs.emplace_back(m_fd[i]);
|
||||
attribs.emplace_back(attrNames[i].offset);
|
||||
attribs.emplace_back(m_offset[i]);
|
||||
attribs.emplace_back(attrNames[i].pitch);
|
||||
attribs.emplace_back(m_stride[i]);
|
||||
if (m_mod != DRM_FORMAT_MOD_INVALID) {
|
||||
attribs.emplace_back(attrNames[i].modlo);
|
||||
attribs.emplace_back(m_mod & 0xFFFFFFFF);
|
||||
attribs.emplace_back(attrNames[i].modhi);
|
||||
attribs.emplace_back(m_mod >> 32);
|
||||
}
|
||||
}
|
||||
attribs.emplace_back(EGL_NONE);
|
||||
|
||||
m_image = eglCreateImage(g_pEGL->eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data());
|
||||
|
||||
if (m_image == EGL_NO_IMAGE) {
|
||||
Debug::log(ERR, "Failed creating an egl image");
|
||||
return false;
|
||||
}
|
||||
|
||||
asset.texture.allocate();
|
||||
asset.texture.m_vSize = {m_w, m_h};
|
||||
glBindTexture(GL_TEXTURE_2D, asset.texture.m_iTexID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
Debug::log(LOG, "Got dma frame with size {}", asset.texture.m_vSize);
|
||||
|
||||
asset.ready = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CSCSHMFrame::CSCSHMFrame(SP<CCZwlrScreencopyFrameV1> sc) : m_sc(sc) {
|
||||
Debug::log(TRACE, "[sc] [shm] Creating a SHM frame");
|
||||
|
||||
m_sc->setBuffer([this](CCZwlrScreencopyFrameV1* r, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
|
||||
Debug::log(TRACE, "[sc] [shm] wlrOnBuffer for {}", (void*)this);
|
||||
|
||||
const auto SIZE = stride * height;
|
||||
m_shmFmt = format;
|
||||
m_w = width;
|
||||
m_h = height;
|
||||
m_stride = stride;
|
||||
|
||||
// Create a shm pool with format and size
|
||||
std::string shmPoolFile;
|
||||
const auto FD = createPoolFile(SIZE, shmPoolFile);
|
||||
|
||||
if (FD < 0) {
|
||||
Debug::log(ERR, "[sc] [shm] failed to create a pool file");
|
||||
return;
|
||||
}
|
||||
|
||||
m_shmData = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FD, 0);
|
||||
if (m_shmData == MAP_FAILED) {
|
||||
Debug::log(ERR, "[sc] [shm] failed to (errno {})", strerror(errno));
|
||||
close(FD);
|
||||
m_ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_pHyprlock->getShm()) {
|
||||
Debug::log(ERR, "[sc] [shm] Failed to get WLShm global");
|
||||
close(FD);
|
||||
m_ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto pShmPool = makeShared<CCWlShmPool>(g_pHyprlock->getShm()->sendCreatePool(FD, SIZE));
|
||||
m_wlBuffer = makeShared<CCWlBuffer>(pShmPool->sendCreateBuffer(0, width, height, stride, m_shmFmt));
|
||||
|
||||
pShmPool.reset();
|
||||
|
||||
close(FD);
|
||||
});
|
||||
|
||||
m_sc->setLinuxDmabuf([](CCZwlrScreencopyFrameV1* r, uint32_t, uint32_t, uint32_t) {
|
||||
; // unused by scshm
|
||||
});
|
||||
}
|
||||
|
||||
CSCSHMFrame::~CSCSHMFrame() {
|
||||
if (m_convBuffer)
|
||||
free(m_convBuffer);
|
||||
if (m_shmData)
|
||||
munmap(m_shmData, m_stride * m_h);
|
||||
}
|
||||
|
||||
void CSCSHMFrame::convertBuffer() {
|
||||
const auto BYTESPERPX = m_stride / m_w;
|
||||
if (BYTESPERPX == 4) {
|
||||
switch (m_shmFmt) {
|
||||
case WL_SHM_FORMAT_ARGB8888:
|
||||
case WL_SHM_FORMAT_XRGB8888: {
|
||||
Debug::log(LOG, "[sc] [shm] Converting ARGB to RGBA");
|
||||
uint8_t* data = (uint8_t*)m_shmData;
|
||||
|
||||
for (uint32_t y = 0; y < m_h; ++y) {
|
||||
for (uint32_t x = 0; x < m_w; ++x) {
|
||||
struct pixel {
|
||||
// little-endian ARGB
|
||||
unsigned char blue;
|
||||
unsigned char green;
|
||||
unsigned char red;
|
||||
unsigned char alpha;
|
||||
}* px = (struct pixel*)(data + y * m_w * 4 + x * 4);
|
||||
|
||||
// RGBA
|
||||
*px = {px->red, px->green, px->blue, px->alpha};
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case WL_SHM_FORMAT_ABGR8888:
|
||||
case WL_SHM_FORMAT_XBGR8888: {
|
||||
Debug::log(LOG, "[sc] [shm] Converting ABGR to RGBA");
|
||||
uint8_t* data = (uint8_t*)m_shmData;
|
||||
|
||||
for (uint32_t y = 0; y < m_h; ++y) {
|
||||
for (uint32_t x = 0; x < m_w; ++x) {
|
||||
struct pixel {
|
||||
// little-endian ARGB
|
||||
unsigned char blue;
|
||||
unsigned char green;
|
||||
unsigned char red;
|
||||
unsigned char alpha;
|
||||
}* px = (struct pixel*)(data + y * m_w * 4 + x * 4);
|
||||
|
||||
// RGBA
|
||||
*px = {px->blue, px->green, px->red, px->alpha};
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case WL_SHM_FORMAT_ABGR2101010:
|
||||
case WL_SHM_FORMAT_ARGB2101010:
|
||||
case WL_SHM_FORMAT_XRGB2101010:
|
||||
case WL_SHM_FORMAT_XBGR2101010: {
|
||||
Debug::log(LOG, "[sc] [shm] Converting 10-bit channels to 8-bit");
|
||||
uint8_t* data = (uint8_t*)m_shmData;
|
||||
|
||||
const bool FLIP = m_shmFmt != WL_SHM_FORMAT_XBGR2101010;
|
||||
|
||||
for (uint32_t y = 0; y < m_h; ++y) {
|
||||
for (uint32_t x = 0; x < m_w; ++x) {
|
||||
uint32_t* px = (uint32_t*)(data + y * m_w * 4 + x * 4);
|
||||
|
||||
// conv to 8 bit
|
||||
uint8_t R = (uint8_t)std::round((255.0 * (((*px) & 0b00000000000000000000001111111111) >> 0) / 1023.0));
|
||||
uint8_t G = (uint8_t)std::round((255.0 * (((*px) & 0b00000000000011111111110000000000) >> 10) / 1023.0));
|
||||
uint8_t B = (uint8_t)std::round((255.0 * (((*px) & 0b00111111111100000000000000000000) >> 20) / 1023.0));
|
||||
uint8_t A = (uint8_t)std::round((255.0 * (((*px) & 0b11000000000000000000000000000000) >> 30) / 3.0));
|
||||
|
||||
// write 8-bit values
|
||||
*px = ((FLIP ? B : R) << 0) + (G << 8) + ((FLIP ? R : B) << 16) + (A << 24);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
Debug::log(WARN, "[sc] [shm] Unsupported format {}", m_shmFmt);
|
||||
}
|
||||
}
|
||||
} else if (BYTESPERPX == 3) {
|
||||
Debug::log(LOG, "[sc] [shm] Converting 24 bit to 32 bit");
|
||||
m_convBuffer = malloc(m_w * m_h * 4);
|
||||
const int NEWSTRIDE = m_w * 4;
|
||||
RASSERT(m_convBuffer, "malloc failed");
|
||||
|
||||
switch (m_shmFmt) {
|
||||
case WL_SHM_FORMAT_BGR888: {
|
||||
Debug::log(LOG, "[sc] [shm] Converting BGR to RGBA");
|
||||
for (uint32_t y = 0; y < m_h; ++y) {
|
||||
for (uint32_t x = 0; x < m_w; ++x) {
|
||||
struct pixel3 {
|
||||
// little-endian RGB
|
||||
unsigned char blue;
|
||||
unsigned char green;
|
||||
unsigned char red;
|
||||
}* srcPx = (struct pixel3*)((char*)m_shmData + y * m_stride + x * 3);
|
||||
struct pixel4 {
|
||||
// little-endian ARGB
|
||||
unsigned char blue;
|
||||
unsigned char green;
|
||||
unsigned char red;
|
||||
unsigned char alpha;
|
||||
}* dstPx = (struct pixel4*)((char*)m_convBuffer + y * NEWSTRIDE + x * 4);
|
||||
*dstPx = {srcPx->blue, srcPx->green, srcPx->red, 0xFF};
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case WL_SHM_FORMAT_RGB888: {
|
||||
Debug::log(LOG, "[sc] [shm] Converting RGB to RGBA");
|
||||
for (uint32_t y = 0; y < m_h; ++y) {
|
||||
for (uint32_t x = 0; x < m_w; ++x) {
|
||||
struct pixel3 {
|
||||
// big-endian RGB
|
||||
unsigned char red;
|
||||
unsigned char green;
|
||||
unsigned char blue;
|
||||
}* srcPx = (struct pixel3*)((char*)m_shmData + y * m_stride + x * 3);
|
||||
struct pixel4 {
|
||||
// big-endian ARGB
|
||||
unsigned char alpha;
|
||||
unsigned char red;
|
||||
unsigned char green;
|
||||
unsigned char blue;
|
||||
}* dstPx = (struct pixel4*)((char*)m_convBuffer + y * NEWSTRIDE + x * 4);
|
||||
*dstPx = {srcPx->red, srcPx->green, srcPx->blue, 0xFF};
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
Debug::log(ERR, "[sc] [shm] Unsupported format for 24bit buffer {}", m_shmFmt);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
Debug::log(ERR, "[sc] [shm] Unsupported bytes per pixel {}", BYTESPERPX);
|
||||
}
|
||||
}
|
||||
|
||||
bool CSCSHMFrame::onBufferReady(SPreloadedAsset& asset) {
|
||||
convertBuffer();
|
||||
|
||||
asset.texture.allocate();
|
||||
asset.texture.m_vSize.x = m_w;
|
||||
asset.texture.m_vSize.y = m_h;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, asset.texture.m_iTexID);
|
||||
|
||||
void* buffer = m_convBuffer ? m_convBuffer : m_shmData;
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_w, m_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
Debug::log(LOG, "[sc] [shm] Got screenshot with size {}", asset.texture.m_vSize);
|
||||
|
||||
asset.ready = true;
|
||||
|
||||
return true;
|
||||
}
|
94
src/renderer/Screencopy.hpp
Normal file
94
src/renderer/Screencopy.hpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "../core/Output.hpp"
|
||||
#include <cstdint>
|
||||
#include <gbm.h>
|
||||
#include <memory>
|
||||
#include "Shared.hpp"
|
||||
#include "linux-dmabuf-v1.hpp"
|
||||
#include "wlr-screencopy-unstable-v1.hpp"
|
||||
|
||||
class ISCFrame {
|
||||
public:
|
||||
ISCFrame() = default;
|
||||
virtual ~ISCFrame() = default;
|
||||
|
||||
virtual bool onBufferDone() = 0;
|
||||
virtual bool onBufferReady(SPreloadedAsset& asset) = 0;
|
||||
|
||||
SP<CCWlBuffer> m_wlBuffer = nullptr;
|
||||
};
|
||||
|
||||
class CScreencopyFrame {
|
||||
public:
|
||||
static std::string getResourceId(COutput* output);
|
||||
|
||||
CScreencopyFrame(COutput* mon);
|
||||
~CScreencopyFrame() = default;
|
||||
|
||||
void captureOutput();
|
||||
|
||||
SP<CCZwlrScreencopyFrameV1> m_sc = nullptr;
|
||||
|
||||
std::string m_resourceID;
|
||||
SPreloadedAsset m_asset;
|
||||
|
||||
private:
|
||||
COutput* m_output = nullptr;
|
||||
std::unique_ptr<ISCFrame> m_frame = nullptr;
|
||||
|
||||
bool m_dmaFailed = false;
|
||||
};
|
||||
|
||||
// Uses a gpu buffer created via gbm_bo
|
||||
class CSCDMAFrame : public ISCFrame {
|
||||
public:
|
||||
CSCDMAFrame(SP<CCZwlrScreencopyFrameV1> sc);
|
||||
virtual ~CSCDMAFrame();
|
||||
|
||||
virtual bool onBufferReady(SPreloadedAsset& asset);
|
||||
virtual bool onBufferDone();
|
||||
|
||||
private:
|
||||
gbm_bo* m_bo = nullptr;
|
||||
|
||||
int m_planes = 0;
|
||||
uint64_t m_mod = 0;
|
||||
|
||||
int m_fd[4];
|
||||
uint32_t m_stride[4], m_offset[4];
|
||||
|
||||
int m_w = 0, m_h = 0;
|
||||
uint32_t m_fmt = 0;
|
||||
|
||||
SP<CCZwlrScreencopyFrameV1> m_sc = nullptr;
|
||||
|
||||
EGLImage m_image = nullptr;
|
||||
};
|
||||
|
||||
// Uses a shm buffer - is slow and needs ugly format conversion
|
||||
// Used as a fallback just in case.
|
||||
class CSCSHMFrame : public ISCFrame {
|
||||
public:
|
||||
CSCSHMFrame(SP<CCZwlrScreencopyFrameV1> sc);
|
||||
virtual ~CSCSHMFrame();
|
||||
|
||||
virtual bool onBufferDone() {
|
||||
return m_ok;
|
||||
}
|
||||
virtual bool onBufferReady(SPreloadedAsset& asset);
|
||||
void convertBuffer();
|
||||
|
||||
private:
|
||||
bool m_ok = true;
|
||||
|
||||
uint32_t m_w = 0, m_h = 0;
|
||||
uint32_t m_stride = 0;
|
||||
|
||||
SP<CCZwlrScreencopyFrameV1> m_sc = nullptr;
|
||||
|
||||
uint32_t m_shmFmt = 0;
|
||||
void* m_shmData = nullptr;
|
||||
void* m_convBuffer = nullptr;
|
||||
};
|
Loading…
Reference in a new issue