From 0a099ca2ab8e4b5da01189202740bbc4bbe81f5b Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:01:05 +0100 Subject: [PATCH] Hyprland Screencopy impl (#1800) --------- Co-authored-by: Mihai Fufezan --- CMakeLists.txt | 1 + protocols/meson.build | 1 + src/Compositor.cpp | 1 - src/managers/ProtocolManager.cpp | 1 + src/managers/ProtocolManager.hpp | 2 + src/protocols/Screencopy.cpp | 413 +++++++++++++++++++++++ src/protocols/Screencopy.hpp | 71 ++++ src/protocols/ToplevelExport.cpp | 14 +- src/protocols/ToplevelExportWlrFuncs.hpp | 17 +- src/render/Renderer.cpp | 21 +- src/render/Renderer.hpp | 2 + 11 files changed, 527 insertions(+), 17 deletions(-) create mode 100644 src/protocols/Screencopy.cpp create mode 100644 src/protocols/Screencopy.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4695c248..7f08cfcd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,5 +144,6 @@ target_link_libraries(Hyprland ${CMAKE_SOURCE_DIR}/hyprland-toplevel-export-v1-protocol.o ${CMAKE_SOURCE_DIR}/fractional-scale-v1-protocol.o ${CMAKE_SOURCE_DIR}/text-input-unstable-v1-protocol.o + ${CMAKE_SOURCE_DIR}/wlr-screencopy-unstable-v1-protocol.o ${CMAKE_SOURCE_DIR}/subprojects/udis86/build/libudis86/liblibudis86.a ) diff --git a/protocols/meson.build b/protocols/meson.build index 47b2dcae..c7a1c038 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -26,6 +26,7 @@ protocols = [ ['wlr-foreign-toplevel-management-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['wlr-output-power-management-unstable-v1.xml'], + ['wlr-screencopy-unstable-v1.xml'], ['ext-workspace-unstable-v1.xml'], ['pointer-constraints-unstable-v1.xml'], ['tablet-unstable-v2.xml'], diff --git a/src/Compositor.cpp b/src/Compositor.cpp index bf7de828..5bffc785 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -152,7 +152,6 @@ void CCompositor::initServer() { m_sWLRDataDevMgr = wlr_data_device_manager_create(m_sWLDisplay); wlr_export_dmabuf_manager_v1_create(m_sWLDisplay); - wlr_screencopy_manager_v1_create(m_sWLDisplay); wlr_data_control_manager_v1_create(m_sWLDisplay); wlr_gamma_control_manager_v1_create(m_sWLDisplay); wlr_primary_selection_v1_device_manager_create(m_sWLDisplay); diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 6271b0ca..d9aa299f 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -4,4 +4,5 @@ CProtocolManager::CProtocolManager() { m_pToplevelExportProtocolManager = std::make_unique(); m_pFractionalScaleProtocolManager = std::make_unique(); m_pTextInputV1ProtocolManager = std::make_unique(); + m_pScreencopyProtocolManager = std::make_unique(); } \ No newline at end of file diff --git a/src/managers/ProtocolManager.hpp b/src/managers/ProtocolManager.hpp index 6160176d..340785ec 100644 --- a/src/managers/ProtocolManager.hpp +++ b/src/managers/ProtocolManager.hpp @@ -4,6 +4,7 @@ #include "../protocols/ToplevelExport.hpp" #include "../protocols/FractionalScale.hpp" #include "../protocols/TextInputV1.hpp" +#include "../protocols/Screencopy.hpp" class CProtocolManager { public: @@ -12,6 +13,7 @@ class CProtocolManager { std::unique_ptr m_pToplevelExportProtocolManager; std::unique_ptr m_pFractionalScaleProtocolManager; std::unique_ptr m_pTextInputV1ProtocolManager; + std::unique_ptr m_pScreencopyProtocolManager; }; inline std::unique_ptr g_pProtocolManager; diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp new file mode 100644 index 00000000..f42be1c5 --- /dev/null +++ b/src/protocols/Screencopy.cpp @@ -0,0 +1,413 @@ +#include "Screencopy.hpp" +#include "../Compositor.hpp" +#include + +#include + +#include "ToplevelExportWlrFuncs.hpp" + +#define SCREENCOPY_VERSION 3 + +static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) { + g_pProtocolManager->m_pScreencopyProtocolManager->bindManager(client, data, version, id); +} + +static void handleDisplayDestroy(struct wl_listener* listener, void* data) { + g_pProtocolManager->m_pScreencopyProtocolManager->displayDestroy(); +} + +void CScreencopyProtocolManager::displayDestroy() { + wl_global_destroy(m_pGlobal); +} + +static SScreencopyFrame* frameFromResource(wl_resource*); + +CScreencopyProtocolManager::CScreencopyProtocolManager() { + +#ifndef GLES32 + Debug::log(WARN, "Screensharing is not supported on LEGACY_RENDERER!"); + return; +#endif + + m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &zwlr_screencopy_manager_v1_interface, SCREENCOPY_VERSION, this, bindManagerInt); + + if (!m_pGlobal) { + Debug::log(ERR, "ScreencopyProtocolManager could not start! Screensharing will not work!"); + return; + } + + m_liDisplayDestroy.notify = handleDisplayDestroy; + wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy); + + Debug::log(LOG, "ScreencopyProtocolManager started successfully!"); +} + +static void handleCaptureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output) { + g_pProtocolManager->m_pScreencopyProtocolManager->captureOutput(client, resource, frame, overlay_cursor, output); +} + +static void handleCaptureRegion(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, int32_t x, int32_t y, int32_t width, + int32_t height) { + g_pProtocolManager->m_pScreencopyProtocolManager->captureOutput(client, resource, frame, overlay_cursor, output, {x, y, width, height}); +} + +static void handleDestroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +static void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer) { + g_pProtocolManager->m_pScreencopyProtocolManager->copyFrame(client, resource, buffer); +} + +static void handleCopyWithDamage(wl_client* client, wl_resource* resource, wl_resource* buffer) { + const auto PFRAME = frameFromResource(resource); + PFRAME->withDamage = true; + handleCopyFrame(client, resource, buffer); +} + +static void handleDestroyFrame(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +static const struct zwlr_screencopy_manager_v1_interface screencopyMgrImpl = { + .capture_output = handleCaptureOutput, + .capture_output_region = handleCaptureRegion, + .destroy = handleDestroy, +}; + +static const struct zwlr_screencopy_frame_v1_interface screencopyFrameImpl = { + .copy = handleCopyFrame, + .destroy = handleDestroyFrame, + .copy_with_damage = handleCopyWithDamage, +}; + +static SScreencopyClient* clientFromResource(wl_resource* resource) { + ASSERT(wl_resource_instance_of(resource, &zwlr_screencopy_manager_v1_interface, &screencopyMgrImpl)); + return (SScreencopyClient*)wl_resource_get_user_data(resource); +} + +static SScreencopyFrame* frameFromResource(wl_resource* resource) { + ASSERT(wl_resource_instance_of(resource, &zwlr_screencopy_frame_v1_interface, &screencopyFrameImpl)); + return (SScreencopyFrame*)wl_resource_get_user_data(resource); +} + +void CScreencopyProtocolManager::removeClient(SScreencopyClient* client, bool force) { + if (!force) { + if (!client || client->ref <= 0) + return; + + if (--client->ref != 0) + return; + } + + m_lClients.remove(*client); // TODO: this doesn't get cleaned up after sharing app exits??? +} + +static void handleManagerResourceDestroy(wl_resource* resource) { + const auto PCLIENT = clientFromResource(resource); + + g_pProtocolManager->m_pScreencopyProtocolManager->removeClient(PCLIENT, true); +} + +void CScreencopyProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) { + const auto PCLIENT = &m_lClients.emplace_back(); + + PCLIENT->resource = wl_resource_create(client, &zwlr_screencopy_manager_v1_interface, version, id); + + if (!PCLIENT->resource) { + Debug::log(ERR, "ScreencopyProtocolManager could not bind! (out of memory?)"); + m_lClients.remove(*PCLIENT); + wl_client_post_no_memory(client); + return; + } + + PCLIENT->ref = 1; + + wl_resource_set_implementation(PCLIENT->resource, &screencopyMgrImpl, PCLIENT, handleManagerResourceDestroy); + + Debug::log(LOG, "ScreencopyProtocolManager bound successfully!"); +} + +static void handleFrameResourceDestroy(wl_resource* resource) { + const auto PFRAME = frameFromResource(resource); + + g_pProtocolManager->m_pScreencopyProtocolManager->removeFrame(PFRAME); +} + +void CScreencopyProtocolManager::removeFrame(SScreencopyFrame* frame, bool force) { + if (!frame) + return; + + std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other == frame; }); + + wl_resource_set_user_data(frame->resource, nullptr); + wlr_buffer_unlock(frame->buffer); + removeClient(frame->client, force); + m_lFrames.remove(*frame); +} + +void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, wlr_box box) { + const auto PCLIENT = clientFromResource(resource); + + const auto PFRAME = &m_lFrames.emplace_back(); + PFRAME->overlayCursor = !!overlay_cursor; + PFRAME->resource = wl_resource_create(client, &zwlr_screencopy_frame_v1_interface, wl_resource_get_version(resource), frame); + PFRAME->pMonitor = g_pCompositor->getMonitorFromOutput(wlr_output_from_resource(output)); + + if (!PFRAME->pMonitor) { + Debug::log(ERR, "client requested sharing of a monitor that doesnt exist"); + zwlr_screencopy_frame_v1_send_failed(PFRAME->resource); + removeFrame(PFRAME); + return; + } + + if (!PFRAME->resource) { + Debug::log(ERR, "Couldn't alloc frame for sharing! (no memory)"); + removeFrame(PFRAME); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(PFRAME->resource, &screencopyFrameImpl, PFRAME, handleFrameResourceDestroy); + + PFRAME->client = PCLIENT; + PCLIENT->ref++; + + PFRAME->shmFormat = wlr_output_preferred_read_format(PFRAME->pMonitor->output); + if (PFRAME->shmFormat == DRM_FORMAT_INVALID) { + Debug::log(ERR, "No format supported by renderer in capture output"); + zwlr_screencopy_frame_v1_send_failed(resource); + removeFrame(PFRAME); + return; + } + + const auto PSHMINFO = drm_get_pixel_format_info(PFRAME->shmFormat); + if (!PSHMINFO) { + Debug::log(ERR, "No pixel format supported by renderer in capture output"); + zwlr_screencopy_frame_v1_send_failed(resource); + removeFrame(PFRAME); + return; + } + + if (PFRAME->pMonitor->output->allocator && (PFRAME->pMonitor->output->allocator->buffer_caps & WLR_BUFFER_CAP_DMABUF)) { + PFRAME->dmabufFormat = PFRAME->pMonitor->output->render_format; + } else { + PFRAME->dmabufFormat = DRM_FORMAT_INVALID; + } + + if (box.width == 0 && box.height == 0) + PFRAME->box = {0, 0, (int)(PFRAME->pMonitor->vecSize.x * PFRAME->pMonitor->scale), (int)(PFRAME->pMonitor->vecSize.y * PFRAME->pMonitor->scale)}; + else { + PFRAME->box = box; + scaleBox(&PFRAME->box, PFRAME->pMonitor->scale); + } + int ow, oh; + wlr_output_effective_resolution(PFRAME->pMonitor->output, &ow, &oh); + wlr_box_transform(&PFRAME->box, &PFRAME->box, PFRAME->pMonitor->transform, ow, oh); + + PFRAME->shmStride = (PSHMINFO->bpp / 8) * PFRAME->box.width; + + zwlr_screencopy_frame_v1_send_buffer(PFRAME->resource, convert_drm_format_to_wl_shm(PFRAME->shmFormat), PFRAME->box.width, PFRAME->box.height, PFRAME->shmStride); + + if (wl_resource_get_version(resource) >= 3) { + // todo + // if (PFRAME->dmabufFormat != DRM_FORMAT_INVALID) { + // zwlr_screencopy_frame_v1_send_linux_dmabuf(PFRAME->resource, PFRAME->dmabufFormat, PFRAME->box.width, PFRAME->box.height); + // } + + zwlr_screencopy_frame_v1_send_buffer_done(PFRAME->resource); + } +} + +void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer) { + const auto PFRAME = frameFromResource(resource); + + if (!PFRAME) { + Debug::log(ERR, "No frame in copyFrame??"); + return; + } + + const auto PBUFFER = wlr_buffer_from_resource(buffer); + if (!PBUFFER) { + wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer"); + removeFrame(PFRAME); + return; + } + + if (PBUFFER->width != PFRAME->box.width || PBUFFER->height != PFRAME->box.height) { + wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions"); + removeFrame(PFRAME); + return; + } + + if (PFRAME->buffer) { + wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used"); + removeFrame(PFRAME); + return; + } + + wlr_dmabuf_attributes dmabufAttrs; + void* wlrBufferAccessData; + uint32_t wlrBufferAccessFormat; + size_t wlrBufferAccessStride; + if (wlr_buffer_get_dmabuf(PBUFFER, &dmabufAttrs)) { + PFRAME->bufferCap = WLR_BUFFER_CAP_DMABUF; + + if (dmabufAttrs.format != PFRAME->dmabufFormat) { + wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); + removeFrame(PFRAME); + return; + } + } else if (wlr_buffer_begin_data_ptr_access(PBUFFER, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &wlrBufferAccessData, &wlrBufferAccessFormat, &wlrBufferAccessStride)) { + wlr_buffer_end_data_ptr_access(PBUFFER); + + if (wlrBufferAccessFormat != PFRAME->shmFormat) { + wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); + removeFrame(PFRAME); + return; + } else if ((int)wlrBufferAccessStride != PFRAME->shmStride) { + wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride"); + removeFrame(PFRAME); + return; + } + } else { + wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type"); + removeFrame(PFRAME); + return; + } + + PFRAME->buffer = PBUFFER; + + m_vFramesAwaitingWrite.emplace_back(PFRAME); + + g_pHyprRenderer->m_bDirectScanoutBlocked = true; + if (PFRAME->overlayCursor) + g_pHyprRenderer->m_bSoftwareCursorsLocked = true; +} + +void CScreencopyProtocolManager::onRenderEnd(CMonitor* pMonitor) { + if (m_vFramesAwaitingWrite.empty()) + return; // nothing to share + + std::vector framesToRemove; + + // share frame if correct output + for (auto& f : m_vFramesAwaitingWrite) { + if (!f->pMonitor) { + framesToRemove.push_back(f); + continue; + } + + shareFrame(f); + + framesToRemove.push_back(f); + } + + for (auto& f : framesToRemove) { + removeFrame(f); + } + + g_pHyprRenderer->m_bSoftwareCursorsLocked = false; + + if (m_vFramesAwaitingWrite.empty()) { + g_pHyprRenderer->m_bDirectScanoutBlocked = false; + } else { + for (auto& f : m_vFramesAwaitingWrite) { + if (f->overlayCursor) { + g_pHyprRenderer->m_bSoftwareCursorsLocked = true; + break; + } + } + } +} + +void CScreencopyProtocolManager::shareFrame(SScreencopyFrame* frame) { + if (!frame->buffer || (!pixman_region32_not_empty(g_pHyprOpenGL->m_RenderData.pDamage) && frame->withDamage)) + return; + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + uint32_t flags = 0; + if (frame->bufferCap == WLR_BUFFER_CAP_DMABUF) { + if (!copyFrameDmabuf(frame)) { + zwlr_screencopy_frame_v1_send_failed(frame->resource); + return; + } + } else { + if (!copyFrameShm(frame, &now)) { + zwlr_screencopy_frame_v1_send_failed(frame->resource); + return; + } + } + + zwlr_screencopy_frame_v1_send_flags(frame->resource, flags); + sendFrameDamage(frame); + uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0; + uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF; + zwlr_screencopy_frame_v1_send_ready(frame->resource, tvSecHi, tvSecLo, now.tv_nsec); +} +void CScreencopyProtocolManager::sendFrameDamage(SScreencopyFrame* frame) { + if (!frame->withDamage) + return; + + PIXMAN_DAMAGE_FOREACH(g_pHyprOpenGL->m_RenderData.pDamage) { + const auto RECT = RECTSARR[i]; + zwlr_screencopy_frame_v1_send_damage(frame->resource, RECT.x1, RECT.y1, RECT.x2 - RECT.x1, RECT.y2 - RECT.y1); + } +} + +bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) { + void* data; + uint32_t format; + size_t stride; + if (!wlr_buffer_begin_data_ptr_access(frame->buffer, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) + return false; + + // render the client + const auto PMONITOR = frame->pMonitor; + pixman_region32_t fakeDamage; + pixman_region32_init_rect(&fakeDamage, 0, 0, PMONITOR->vecPixelSize.x * 10, PMONITOR->vecPixelSize.y * 10); + + if (!wlr_output_attach_render(PMONITOR->output, nullptr)) { + Debug::log(ERR, "[screencopy] Couldn't attach render"); + pixman_region32_fini(&fakeDamage); + wlr_buffer_end_data_ptr_access(frame->buffer); + return false; + } + + const auto PFORMAT = get_gles2_format_from_drm(format); + if (!PFORMAT) { + Debug::log(ERR, "[screencopy] Cannot read pixels, unsupported format %x", PFORMAT); + pixman_region32_fini(&fakeDamage); + wlr_buffer_end_data_ptr_access(frame->buffer); + return false; + } + + g_pHyprOpenGL->begin(PMONITOR, &fakeDamage, true); + + // we should still have the last frame by this point in the original fb + glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_RenderData.pCurrentMonData->primaryFB.m_iFb); + + glFinish(); // flush + + glReadPixels(frame->box.x, frame->box.y, frame->box.width, frame->box.height, PFORMAT->gl_format, PFORMAT->gl_type, data); + + glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_iWLROutputFb); + + g_pHyprOpenGL->end(); + + wlr_output_rollback(PMONITOR->output); + + pixman_region32_fini(&fakeDamage); + + wlr_buffer_end_data_ptr_access(frame->buffer); + + return true; +} + +bool CScreencopyProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) { + // todo + Debug::log(ERR, "DMABUF copying not impl'd!"); + return false; +} \ No newline at end of file diff --git a/src/protocols/Screencopy.hpp b/src/protocols/Screencopy.hpp new file mode 100644 index 00000000..d854c3bd --- /dev/null +++ b/src/protocols/Screencopy.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include "../defines.hpp" +#include "wlr-screencopy-unstable-v1-protocol.h" + +#include +#include + +class CMonitor; + +struct SScreencopyClient { + int ref = 0; + wl_resource* resource = nullptr; + + bool operator==(const SScreencopyClient& other) const { + return resource == other.resource; + } +}; + +struct SScreencopyFrame { + wl_resource* resource = nullptr; + SScreencopyClient* client = nullptr; + + uint32_t shmFormat = 0; + uint32_t dmabufFormat = 0; + wlr_box box = {0}; + int shmStride = 0; + + bool overlayCursor = false; + bool withDamage = false; + + wlr_buffer_cap bufferCap = WLR_BUFFER_CAP_SHM; + + wlr_buffer* buffer = nullptr; + + CMonitor* pMonitor = nullptr; + + bool operator==(const SScreencopyFrame& other) const { + return resource == other.resource && client == other.client; + } +}; + +class CScreencopyProtocolManager { + public: + CScreencopyProtocolManager(); + + void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id); + void removeClient(SScreencopyClient* client, bool force = false); + void removeFrame(SScreencopyFrame* frame, bool force = false); + void displayDestroy(); + + void captureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, wlr_box box = {0, 0, 0, 0}); + + void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer); + + void onRenderEnd(CMonitor* pMonitor); + + private: + wl_global* m_pGlobal = nullptr; + std::list m_lFrames; + std::list m_lClients; + + wl_listener m_liDisplayDestroy; + + std::vector m_vFramesAwaitingWrite; + + void shareFrame(SScreencopyFrame* frame); + void sendFrameDamage(SScreencopyFrame* frame); + bool copyFrameDmabuf(SScreencopyFrame* frame); + bool copyFrameShm(SScreencopyFrame* frame, timespec* now); +}; \ No newline at end of file diff --git a/src/protocols/ToplevelExport.cpp b/src/protocols/ToplevelExport.cpp index bee1deb6..50027046 100644 --- a/src/protocols/ToplevelExport.cpp +++ b/src/protocols/ToplevelExport.cpp @@ -47,11 +47,11 @@ wlr_foreign_toplevel_handle_v1* zwlrHandleFromResource(wl_resource* resource) { return (wlr_foreign_toplevel_handle_v1*)wl_resource_get_user_data(resource); } -void handleCaptureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, uint32_t handle) { +static void handleCaptureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, uint32_t handle) { g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, g_pCompositor->getWindowFromHandle(handle)); } -void handleCaptureToplevelWithWlr(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* handle) { +static void handleCaptureToplevelWithWlr(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* handle) { g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, g_pCompositor->getWindowFromZWLRHandle(handle)); } @@ -59,11 +59,11 @@ static void handleDestroy(wl_client* client, wl_resource* resource) { wl_resource_destroy(resource); } -void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage) { +static void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage) { g_pProtocolManager->m_pToplevelExportProtocolManager->copyFrame(client, resource, buffer, ignore_damage); } -void handleDestroyFrame(wl_client* client, wl_resource* resource) { +static void handleDestroyFrame(wl_client* client, wl_resource* resource) { wl_resource_destroy(resource); } @@ -75,12 +75,12 @@ static const struct hyprland_toplevel_export_manager_v1_interface toplevelExport static const struct hyprland_toplevel_export_frame_v1_interface toplevelFrameImpl = {.copy = handleCopyFrame, .destroy = handleDestroyFrame}; -SToplevelClient* clientFromResource(wl_resource* resource) { +static SToplevelClient* clientFromResource(wl_resource* resource) { ASSERT(wl_resource_instance_of(resource, &hyprland_toplevel_export_manager_v1_interface, &toplevelExportManagerImpl)); return (SToplevelClient*)wl_resource_get_user_data(resource); } -SToplevelFrame* frameFromResource(wl_resource* resource) { +static SToplevelFrame* frameFromResource(wl_resource* resource) { ASSERT(wl_resource_instance_of(resource, &hyprland_toplevel_export_frame_v1_interface, &toplevelFrameImpl)); return (SToplevelFrame*)wl_resource_get_user_data(resource); } @@ -122,7 +122,7 @@ void CToplevelExportProtocolManager::bindManager(wl_client* client, void* data, Debug::log(LOG, "ToplevelExportManager bound successfully!"); } -void handleFrameResourceDestroy(wl_resource* resource) { +static void handleFrameResourceDestroy(wl_resource* resource) { const auto PFRAME = frameFromResource(resource); g_pProtocolManager->m_pToplevelExportProtocolManager->removeFrame(PFRAME); diff --git a/src/protocols/ToplevelExportWlrFuncs.hpp b/src/protocols/ToplevelExportWlrFuncs.hpp index 39e2961b..97bf96f8 100644 --- a/src/protocols/ToplevelExportWlrFuncs.hpp +++ b/src/protocols/ToplevelExportWlrFuncs.hpp @@ -1,5 +1,8 @@ #include +#ifndef DRM_WLR_FUNCS +#define DRM_WLR_FUNCS + struct wlr_pixel_format_info { uint32_t drm_format; @@ -156,9 +159,9 @@ static const struct wlr_pixel_format_info pixel_format_info[] = { }, }; -static const size_t pixel_format_info_size = sizeof(pixel_format_info) / sizeof(pixel_format_info[0]); +static const size_t pixel_format_info_size = sizeof(pixel_format_info) / sizeof(pixel_format_info[0]); -const struct wlr_pixel_format_info* drm_get_pixel_format_info(uint32_t fmt) { +static const struct wlr_pixel_format_info* drm_get_pixel_format_info(uint32_t fmt) { for (size_t i = 0; i < pixel_format_info_size; ++i) { if (pixel_format_info[i].drm_format == fmt) { return &pixel_format_info[i]; @@ -168,15 +171,15 @@ const struct wlr_pixel_format_info* drm_get_pixel_format_info(uint32_t fmt) { return NULL; } -uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt) { +/*static uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt) { switch (fmt) { case WL_SHM_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888; case WL_SHM_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888; default: return (uint32_t)fmt; } -} +}*/ -enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt) { +static enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt) { switch (fmt) { case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888; case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888; @@ -295,7 +298,7 @@ static const struct wlr_gles2_pixel_format formats[] = { #endif }; -const struct wlr_gles2_pixel_format* get_gles2_format_from_drm(uint32_t fmt) { +static const struct wlr_gles2_pixel_format* get_gles2_format_from_drm(uint32_t fmt) { for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) { if (formats[i].drm_format == fmt) { return &formats[i]; @@ -303,3 +306,5 @@ const struct wlr_gles2_pixel_format* get_gles2_format_from_drm(uint32_t fmt) { } return NULL; } + +#endif \ No newline at end of file diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index b7945dee..a8ba8b22 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -662,7 +662,7 @@ void countSubsurfacesIter(wlr_surface* pSurface, int x, int y, void* data) { } bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) { - if (!pMonitor->mirrors.empty() || pMonitor->isMirror()) + if (!pMonitor->mirrors.empty() || pMonitor->isMirror() || m_bDirectScanoutBlocked) return false; // do not DS if this monitor is being mirrored. Will break the functionality. const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pMonitor->activeWorkspace); @@ -820,6 +820,10 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { return; } + const bool UNLOCK_SC = g_pHyprRenderer->m_bSoftwareCursorsLocked; + if (UNLOCK_SC) + wlr_output_lock_software_cursors(pMonitor->output, true); + if (!wlr_output_damage_attach_render(pMonitor->damage, &hasChanged, &damage)) { Debug::log(ERR, "Couldn't attach render to display %s ???", pMonitor->szName.c_str()); return; @@ -938,12 +942,23 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { g_pHyprRenderer->damageMirrorsWith(pMonitor, &frameDamage); pixman_region32_fini(&frameDamage); - pixman_region32_fini(&damage); pMonitor->renderingActive = false; - if (!wlr_output_commit(pMonitor->output)) + if (!wlr_output_commit(pMonitor->output)) { + pixman_region32_fini(&damage); + + if (UNLOCK_SC) + wlr_output_lock_software_cursors(pMonitor->output, false); + return; + } + + g_pProtocolManager->m_pScreencopyProtocolManager->onRenderEnd(pMonitor); + pixman_region32_fini(&damage); + + if (UNLOCK_SC) + wlr_output_lock_software_cursors(pMonitor->output, false); if (*PDAMAGEBLINK || *PVFR == 0 || pMonitor->pendingFrame) g_pCompositor->scheduleFrameForMonitor(pMonitor); diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index 4ce0f6dc..82da4b69 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -55,6 +55,8 @@ class CHyprRenderer { bool m_bRenderingSnapshot = false; CWindow* m_pLastScanout = nullptr; CMonitor* m_pMostHzMonitor = nullptr; + bool m_bDirectScanoutBlocked = false; + bool m_bSoftwareCursorsLocked = false; DAMAGETRACKINGMODES damageTrackingModeFromStr(const std::string&);