diff --git a/src/events/Events.hpp b/src/events/Events.hpp index d0a1df39..e0f44727 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -100,6 +100,7 @@ namespace Events { DYNLISTENFUNC(monitorStateRequest); DYNLISTENFUNC(monitorDamage); DYNLISTENFUNC(monitorNeedsFrame); + DYNLISTENFUNC(monitorCommit); // XWayland LISTENER(readyXWayland); diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp index fab7fc89..111076d5 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -212,3 +212,11 @@ void Events::listener_monitorNeedsFrame(void* owner, void* data) { g_pCompositor->scheduleFrameForMonitor(PMONITOR); } + +void Events::listener_monitorCommit(void* owner, void* data) { + const auto PMONITOR = (CMonitor*)owner; + + const auto E = (wlr_output_event_commit*)data; + + g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR, E); +} diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index e1cf54cd..cae76e8b 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -22,11 +22,13 @@ void CMonitor::onConnect(bool noRule) { hyprListener_monitorStateRequest.removeCallback(); hyprListener_monitorDamage.removeCallback(); hyprListener_monitorNeedsFrame.removeCallback(); + hyprListener_monitorCommit.removeCallback(); hyprListener_monitorFrame.initCallback(&output->events.frame, &Events::listener_monitorFrame, this); hyprListener_monitorDestroy.initCallback(&output->events.destroy, &Events::listener_monitorDestroy, this); hyprListener_monitorStateRequest.initCallback(&output->events.request_state, &Events::listener_monitorStateRequest, this); hyprListener_monitorDamage.initCallback(&output->events.damage, &Events::listener_monitorDamage, this); hyprListener_monitorNeedsFrame.initCallback(&output->events.needs_frame, &Events::listener_monitorNeedsFrame, this); + hyprListener_monitorCommit.initCallback(&output->events.commit, &Events::listener_monitorCommit, this); if (m_bEnabled) { wlr_output_enable(output, 1); @@ -221,6 +223,7 @@ void CMonitor::onDisconnect() { hyprListener_monitorFrame.removeCallback(); hyprListener_monitorDamage.removeCallback(); hyprListener_monitorNeedsFrame.removeCallback(); + hyprListener_monitorCommit.removeCallback(); for (size_t i = 0; i < 4; ++i) { for (auto& ls : m_aLayerSurfaceLayers[i]) { diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index bb475189..25753687 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -67,6 +67,7 @@ class CMonitor { DYNLISTENER(monitorStateRequest); DYNLISTENER(monitorDamage); DYNLISTENER(monitorNeedsFrame); + DYNLISTENER(monitorCommit); // hack: a group = workspaces on a monitor. // I don't really care lol :P diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp index a697abc9..2dca96bb 100644 --- a/src/protocols/Screencopy.cpp +++ b/src/protocols/Screencopy.cpp @@ -51,11 +51,20 @@ static void handleDestroy(wl_client* client, wl_resource* resource) { } static void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer) { + const auto PFRAME = frameFromResource(resource); + + if (!PFRAME) + return; + 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); + const auto PFRAME = frameFromResource(resource); + + if (!PFRAME) + return; + PFRAME->withDamage = true; handleCopyFrame(client, resource, buffer); } @@ -206,10 +215,9 @@ void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* r 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); - // } + 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); } @@ -284,7 +292,17 @@ void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resou g_pCompositor->scheduleFrameForMonitor(PFRAME->pMonitor); } +void CScreencopyProtocolManager::onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e) { + m_pLastMonitorBackBuffer = e->buffer; + shareAllFrames(pMonitor, true); + m_pLastMonitorBackBuffer = nullptr; +} + void CScreencopyProtocolManager::onRenderEnd(CMonitor* pMonitor) { + shareAllFrames(pMonitor, false); +} + +void CScreencopyProtocolManager::shareAllFrames(CMonitor* pMonitor, bool dmabuf) { if (m_vFramesAwaitingWrite.empty()) return; // nothing to share @@ -297,7 +315,7 @@ void CScreencopyProtocolManager::onRenderEnd(CMonitor* pMonitor) { continue; } - if (f->pMonitor != pMonitor) + if (f->pMonitor != pMonitor || dmabuf != (f->bufferCap == WLR_BUFFER_CAP_DMABUF)) continue; shareFrame(f); @@ -349,6 +367,7 @@ void CScreencopyProtocolManager::shareFrame(SScreencopyFrame* frame) { 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; @@ -408,7 +427,27 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* } bool CScreencopyProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) { - // todo - Debug::log(ERR, "DMABUF copying not impl'd!"); - return false; + wlr_texture* sourceTex = wlr_texture_from_buffer(g_pCompositor->m_sWLRRenderer, m_pLastMonitorBackBuffer); + if (!sourceTex) + return false; + + float glMatrix[9]; + wlr_matrix_identity(glMatrix); + wlr_matrix_scale(glMatrix, frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y); + wlr_matrix_translate(glMatrix, -frame->box.x, -frame->box.y); + + if (!wlr_renderer_begin_with_buffer(g_pCompositor->m_sWLRRenderer, frame->buffer)) { + wlr_texture_destroy(sourceTex); + return false; + } + + float color[] = {0, 0, 0, 0}; + wlr_renderer_clear(g_pCompositor->m_sWLRRenderer, color); + wlr_render_texture_with_matrix(g_pCompositor->m_sWLRRenderer, sourceTex, glMatrix, 1.0f); + + wlr_texture_destroy(sourceTex); + + wlr_renderer_end(g_pCompositor->m_sWLRRenderer); + + return true; } diff --git a/src/protocols/Screencopy.hpp b/src/protocols/Screencopy.hpp index d854c3bd..77d2b5c3 100644 --- a/src/protocols/Screencopy.hpp +++ b/src/protocols/Screencopy.hpp @@ -54,6 +54,7 @@ class CScreencopyProtocolManager { void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer); void onRenderEnd(CMonitor* pMonitor); + void onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e); private: wl_global* m_pGlobal = nullptr; @@ -64,6 +65,9 @@ class CScreencopyProtocolManager { std::vector m_vFramesAwaitingWrite; + wlr_buffer* m_pLastMonitorBackBuffer = nullptr; + + void shareAllFrames(CMonitor* pMonitor, bool dmabuf); void shareFrame(SScreencopyFrame* frame); void sendFrameDamage(SScreencopyFrame* frame); bool copyFrameDmabuf(SScreencopyFrame* frame);