mirror of
https://github.com/hyprwm/xdg-desktop-portal-hyprland.git
synced 2024-11-23 22:55:58 +01:00
window sharing
This commit is contained in:
parent
f0afc1ab21
commit
c85ae51531
8 changed files with 347 additions and 26 deletions
|
@ -226,6 +226,10 @@ void CPortalManager::onGlobal(void* data, struct wl_registry* registry, uint32_t
|
||||||
|
|
||||||
else if (INTERFACE == wl_shm_interface.name)
|
else if (INTERFACE == wl_shm_interface.name)
|
||||||
m_sWaylandConnection.shm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, version);
|
m_sWaylandConnection.shm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, version);
|
||||||
|
|
||||||
|
else if (INTERFACE == zwlr_foreign_toplevel_manager_v1_interface.name)
|
||||||
|
m_sHelpers.toplevel =
|
||||||
|
std::make_unique<CToplevelManager>((zwlr_foreign_toplevel_manager_v1*)wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, version));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPortalManager::onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name) {
|
void CPortalManager::onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name) {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "../portals/Screencopy.hpp"
|
#include "../portals/Screencopy.hpp"
|
||||||
#include "../helpers/Timer.hpp"
|
#include "../helpers/Timer.hpp"
|
||||||
|
#include "../shared/ToplevelManager.hpp"
|
||||||
#include <gbm.h>
|
#include <gbm.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
|
|
||||||
|
@ -43,6 +44,10 @@ class CPortalManager {
|
||||||
std::unique_ptr<CScreencopyPortal> screencopy;
|
std::unique_ptr<CScreencopyPortal> screencopy;
|
||||||
} m_sPortals;
|
} m_sPortals;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::unique_ptr<CToplevelManager> toplevel;
|
||||||
|
} m_sHelpers;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
wl_display* display = nullptr;
|
wl_display* display = nullptr;
|
||||||
void* hyprlandToplevelMgr = nullptr;
|
void* hyprlandToplevelMgr = nullptr;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
// --------------- Wayland Protocol Handlers --------------- //
|
// --------------- Wayland Protocol Handlers --------------- //
|
||||||
|
|
||||||
static void wlrOnBuffer(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
|
static void wlrOnBuffer(void* data, zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
|
||||||
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
Debug::log(TRACE, "[sc] wlrOnBuffer for {}", (void*)PSESSION);
|
Debug::log(TRACE, "[sc] wlrOnBuffer for {}", (void*)PSESSION);
|
||||||
|
@ -23,7 +23,7 @@ static void wlrOnBuffer(void* data, struct zwlr_screencopy_frame_v1* frame, uint
|
||||||
// todo: done if ver < 3
|
// todo: done if ver < 3
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wlrOnFlags(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t flags) {
|
static void wlrOnFlags(void* data, zwlr_screencopy_frame_v1* frame, uint32_t flags) {
|
||||||
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
Debug::log(TRACE, "[sc] wlrOnFlags for {}", (void*)PSESSION);
|
Debug::log(TRACE, "[sc] wlrOnFlags for {}", (void*)PSESSION);
|
||||||
|
@ -31,7 +31,7 @@ static void wlrOnFlags(void* data, struct zwlr_screencopy_frame_v1* frame, uint3
|
||||||
// todo: maybe check for y invert?
|
// todo: maybe check for y invert?
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wlrOnReady(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
static void wlrOnReady(void* data, zwlr_screencopy_frame_v1* frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
||||||
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
Debug::log(TRACE, "[sc] wlrOnReady for {}", (void*)PSESSION);
|
Debug::log(TRACE, "[sc] wlrOnReady for {}", (void*)PSESSION);
|
||||||
|
@ -52,7 +52,7 @@ static void wlrOnReady(void* data, struct zwlr_screencopy_frame_v1* frame, uint3
|
||||||
PSESSION->sharingData.frameCallback = nullptr;
|
PSESSION->sharingData.frameCallback = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wlrOnFailed(void* data, struct zwlr_screencopy_frame_v1* frame) {
|
static void wlrOnFailed(void* data, zwlr_screencopy_frame_v1* frame) {
|
||||||
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
Debug::log(TRACE, "[sc] wlrOnFailed for {}", (void*)PSESSION);
|
Debug::log(TRACE, "[sc] wlrOnFailed for {}", (void*)PSESSION);
|
||||||
|
@ -60,7 +60,7 @@ static void wlrOnFailed(void* data, struct zwlr_screencopy_frame_v1* frame) {
|
||||||
PSESSION->sharingData.status = FRAME_FAILED;
|
PSESSION->sharingData.status = FRAME_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wlrOnDamage(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
|
static void wlrOnDamage(void* data, zwlr_screencopy_frame_v1* frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
|
||||||
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
Debug::log(TRACE, "[sc] wlrOnDamage for {}", (void*)PSESSION);
|
Debug::log(TRACE, "[sc] wlrOnDamage for {}", (void*)PSESSION);
|
||||||
|
@ -75,7 +75,7 @@ static void wlrOnDamage(void* data, struct zwlr_screencopy_frame_v1* frame, uint
|
||||||
Debug::log(TRACE, "[sc] wlr damage: {} {} {} {}", x, y, width, height);
|
Debug::log(TRACE, "[sc] wlr damage: {} {} {} {}", x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wlrOnDmabuf(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height) {
|
static void wlrOnDmabuf(void* data, zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height) {
|
||||||
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
Debug::log(TRACE, "[sc] wlrOnDmabuf for {}", (void*)PSESSION);
|
Debug::log(TRACE, "[sc] wlrOnDmabuf for {}", (void*)PSESSION);
|
||||||
|
@ -85,7 +85,7 @@ static void wlrOnDmabuf(void* data, struct zwlr_screencopy_frame_v1* frame, uint
|
||||||
PSESSION->sharingData.frameInfoDMA.fmt = format;
|
PSESSION->sharingData.frameInfoDMA.fmt = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wlrOnBufferDone(void* data, struct zwlr_screencopy_frame_v1* frame) {
|
static void wlrOnBufferDone(void* data, zwlr_screencopy_frame_v1* frame) {
|
||||||
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)PSESSION);
|
Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)PSESSION);
|
||||||
|
@ -143,6 +143,141 @@ static const zwlr_screencopy_frame_v1_listener wlrFrameListener = {
|
||||||
.buffer_done = wlrOnBufferDone,
|
.buffer_done = wlrOnBufferDone,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void hlOnBuffer(void* data, hyprland_toplevel_export_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
|
||||||
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] hlOnBuffer for {}", (void*)PSESSION);
|
||||||
|
|
||||||
|
PSESSION->sharingData.frameInfoSHM.w = width;
|
||||||
|
PSESSION->sharingData.frameInfoSHM.h = height;
|
||||||
|
PSESSION->sharingData.frameInfoSHM.fmt = drmFourccFromSHM((wl_shm_format)format);
|
||||||
|
PSESSION->sharingData.frameInfoSHM.size = stride * height;
|
||||||
|
PSESSION->sharingData.frameInfoSHM.stride = stride;
|
||||||
|
|
||||||
|
// todo: done if ver < 3
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hlOnFlags(void* data, hyprland_toplevel_export_frame_v1* frame, uint32_t flags) {
|
||||||
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] hlOnFlags for {}", (void*)PSESSION);
|
||||||
|
|
||||||
|
// todo: maybe check for y invert?
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hlOnReady(void* data, hyprland_toplevel_export_frame_v1* frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
||||||
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] hlOnReady for {}", (void*)PSESSION);
|
||||||
|
|
||||||
|
PSESSION->sharingData.status = FRAME_READY;
|
||||||
|
|
||||||
|
PSESSION->sharingData.tvSec = ((((uint64_t)tv_sec_hi) << 32) + (uint64_t)tv_sec_lo);
|
||||||
|
PSESSION->sharingData.tvNsec = tv_nsec;
|
||||||
|
PSESSION->sharingData.tvTimestampNs = PSESSION->sharingData.tvSec * SPA_NSEC_PER_SEC + PSESSION->sharingData.tvNsec;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] frame timestamp sec: {} nsec: {} combined: {}ns", PSESSION->sharingData.tvSec, PSESSION->sharingData.tvNsec, PSESSION->sharingData.tvTimestampNs);
|
||||||
|
|
||||||
|
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->enqueue(PSESSION);
|
||||||
|
|
||||||
|
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(PSESSION);
|
||||||
|
|
||||||
|
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||||
|
PSESSION->sharingData.windowFrameCallback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hlOnFailed(void* data, hyprland_toplevel_export_frame_v1* frame) {
|
||||||
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] hlOnFailed for {}", (void*)PSESSION);
|
||||||
|
|
||||||
|
PSESSION->sharingData.status = FRAME_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hlOnDamage(void* data, hyprland_toplevel_export_frame_v1* frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
|
||||||
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] hlOnDamage for {}", (void*)PSESSION);
|
||||||
|
|
||||||
|
if (PSESSION->sharingData.damageCount > 3) {
|
||||||
|
PSESSION->sharingData.damage[0] = {0, 0, PSESSION->sharingData.frameInfoDMA.w, PSESSION->sharingData.frameInfoDMA.h};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PSESSION->sharingData.damage[PSESSION->sharingData.damageCount++] = {x, y, width, height};
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] hl damage: {} {} {} {}", x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hlOnDmabuf(void* data, hyprland_toplevel_export_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height) {
|
||||||
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] hlOnDmabuf for {}", (void*)PSESSION);
|
||||||
|
|
||||||
|
PSESSION->sharingData.frameInfoDMA.w = width;
|
||||||
|
PSESSION->sharingData.frameInfoDMA.h = height;
|
||||||
|
PSESSION->sharingData.frameInfoDMA.fmt = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hlOnBufferDone(void* data, hyprland_toplevel_export_frame_v1* frame) {
|
||||||
|
const auto PSESSION = (CScreencopyPortal::SSession*)data;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] hlOnBufferDone for {}", (void*)PSESSION);
|
||||||
|
|
||||||
|
const auto PSTREAM = g_pPortalManager->m_sPortals.screencopy->m_pPipewire->streamFromSession(PSESSION);
|
||||||
|
|
||||||
|
if (!PSTREAM) {
|
||||||
|
Debug::log(TRACE, "[sc] hlOnBufferDone: no stream");
|
||||||
|
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||||
|
PSESSION->sharingData.windowFrameCallback = nullptr;
|
||||||
|
PSESSION->sharingData.status = FRAME_NONE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] pw format {} size {}x{}", (int)PSTREAM->pwVideoInfo.format, PSTREAM->pwVideoInfo.size.width, PSTREAM->pwVideoInfo.size.height);
|
||||||
|
Debug::log(TRACE, "[sc] hl format {} size {}x{}", (int)PSESSION->sharingData.frameInfoSHM.fmt, PSESSION->sharingData.frameInfoSHM.w, PSESSION->sharingData.frameInfoSHM.h);
|
||||||
|
|
||||||
|
const auto FMT = PSTREAM->isDMA ? PSESSION->sharingData.frameInfoDMA.fmt : PSESSION->sharingData.frameInfoSHM.fmt;
|
||||||
|
if ((PSTREAM->pwVideoInfo.format != pwFromDrmFourcc(FMT) && PSTREAM->pwVideoInfo.format != pwStripAlpha(pwFromDrmFourcc(FMT))) ||
|
||||||
|
(PSTREAM->pwVideoInfo.size.width != PSESSION->sharingData.frameInfoDMA.w || PSTREAM->pwVideoInfo.size.height != PSESSION->sharingData.frameInfoDMA.h)) {
|
||||||
|
Debug::log(LOG, "[sc] Incompatible formats, renegotiate stream");
|
||||||
|
PSESSION->sharingData.status = FRAME_RENEG;
|
||||||
|
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||||
|
PSESSION->sharingData.windowFrameCallback = nullptr;
|
||||||
|
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->updateStreamParam(PSTREAM);
|
||||||
|
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(PSESSION);
|
||||||
|
PSESSION->sharingData.status = FRAME_NONE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PSTREAM->currentPWBuffer) {
|
||||||
|
Debug::log(TRACE, "[sc] wlrOnBufferDone: dequeue, no current buffer");
|
||||||
|
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->dequeue(PSESSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PSTREAM->currentPWBuffer) {
|
||||||
|
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||||
|
PSESSION->sharingData.windowFrameCallback = nullptr;
|
||||||
|
Debug::log(LOG, "[screencopy/pipewire] Out of buffers");
|
||||||
|
PSESSION->sharingData.status = FRAME_NONE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hyprland_toplevel_export_frame_v1_copy(frame, PSTREAM->currentPWBuffer->wlBuffer, false);
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[sc] wlr frame copied");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const hyprland_toplevel_export_frame_v1_listener hyprlandFrameListener = {
|
||||||
|
.buffer = hlOnBuffer,
|
||||||
|
.damage = hlOnDamage,
|
||||||
|
.flags = hlOnFlags,
|
||||||
|
.ready = hlOnReady,
|
||||||
|
.failed = hlOnFailed,
|
||||||
|
.linux_dmabuf = hlOnDmabuf,
|
||||||
|
.buffer_done = hlOnBufferDone,
|
||||||
|
};
|
||||||
|
|
||||||
// --------------------------------------------------------- //
|
// --------------------------------------------------------- //
|
||||||
|
|
||||||
void onCloseRequest(sdbus::MethodCall& call, CScreencopyPortal::SSession* sess) {
|
void onCloseRequest(sdbus::MethodCall& call, CScreencopyPortal::SSession* sess) {
|
||||||
|
@ -403,13 +538,14 @@ void CScreencopyPortal::startFrameCopy(CScreencopyPortal::SSession* pSession) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!POUTPUT) {
|
if (!POUTPUT && (pSession->selection.type == TYPE_GEOMETRY || pSession->selection.type == TYPE_OUTPUT)) {
|
||||||
Debug::log(ERR, "[screencopy] Output {} not found??", pSession->selection.output);
|
Debug::log(ERR, "[screencopy] Output {} not found??", pSession->selection.output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pSession->sharingData.frameCallback) {
|
if ((pSession->sharingData.frameCallback && (pSession->selection.type == TYPE_GEOMETRY || pSession->selection.type == TYPE_OUTPUT)) ||
|
||||||
Debug::log(ERR, "[screencopy] tried scheduling on already scheduled cb");
|
(pSession->sharingData.windowFrameCallback && pSession->selection.type == TYPE_WINDOW)) {
|
||||||
|
Debug::log(ERR, "[screencopy] tried scheduling on already scheduled cb (type {})", (int)pSession->selection.type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,6 +556,14 @@ void CScreencopyPortal::startFrameCopy(CScreencopyPortal::SSession* pSession) {
|
||||||
} else if (pSession->selection.type == TYPE_OUTPUT) {
|
} else if (pSession->selection.type == TYPE_OUTPUT) {
|
||||||
pSession->sharingData.frameCallback = zwlr_screencopy_manager_v1_capture_output(m_sState.screencopy, pSession->cursorMode, POUTPUT->output);
|
pSession->sharingData.frameCallback = zwlr_screencopy_manager_v1_capture_output(m_sState.screencopy, pSession->cursorMode, POUTPUT->output);
|
||||||
pSession->sharingData.transform = POUTPUT->transform;
|
pSession->sharingData.transform = POUTPUT->transform;
|
||||||
|
} else if (pSession->selection.type == TYPE_WINDOW) {
|
||||||
|
if (!pSession->selection.windowHandle) {
|
||||||
|
Debug::log(ERR, "[screencopy] selected invalid window?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pSession->sharingData.windowFrameCallback =
|
||||||
|
hyprland_toplevel_export_manager_v1_capture_toplevel_with_wlr_toplevel_handle(m_sState.toplevel, pSession->cursorMode, pSession->selection.windowHandle);
|
||||||
|
pSession->sharingData.transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||||
} else {
|
} else {
|
||||||
Debug::log(ERR, "[screencopy] Unsupported selection {}", (int)pSession->selection.type);
|
Debug::log(ERR, "[screencopy] Unsupported selection {}", (int)pSession->selection.type);
|
||||||
return;
|
return;
|
||||||
|
@ -427,7 +571,10 @@ void CScreencopyPortal::startFrameCopy(CScreencopyPortal::SSession* pSession) {
|
||||||
|
|
||||||
pSession->sharingData.status = FRAME_QUEUED;
|
pSession->sharingData.status = FRAME_QUEUED;
|
||||||
|
|
||||||
|
if (pSession->sharingData.frameCallback)
|
||||||
zwlr_screencopy_frame_v1_add_listener(pSession->sharingData.frameCallback, &wlrFrameListener, pSession);
|
zwlr_screencopy_frame_v1_add_listener(pSession->sharingData.frameCallback, &wlrFrameListener, pSession);
|
||||||
|
else if (pSession->sharingData.windowFrameCallback)
|
||||||
|
hyprland_toplevel_export_frame_v1_add_listener(pSession->sharingData.windowFrameCallback, &hyprlandFrameListener, pSession);
|
||||||
|
|
||||||
Debug::log(LOG, "[screencopy] frame callbacks initialized");
|
Debug::log(LOG, "[screencopy] frame callbacks initialized");
|
||||||
}
|
}
|
||||||
|
@ -441,6 +588,9 @@ void CScreencopyPortal::queueNextShareFrame(CScreencopyPortal::SSession* pSessio
|
||||||
g_pPortalManager->m_vTimers.emplace_back(
|
g_pPortalManager->m_vTimers.emplace_back(
|
||||||
std::make_unique<CTimer>(1000.0 / pSession->sharingData.framerate, [pSession]() { g_pPortalManager->m_sPortals.screencopy->startFrameCopy(pSession); }));
|
std::make_unique<CTimer>(1000.0 / pSession->sharingData.framerate, [pSession]() { g_pPortalManager->m_sPortals.screencopy->startFrameCopy(pSession); }));
|
||||||
}
|
}
|
||||||
|
bool CScreencopyPortal::hasToplevelCapabilities() {
|
||||||
|
return m_sState.toplevel;
|
||||||
|
}
|
||||||
|
|
||||||
CScreencopyPortal::SSession* CScreencopyPortal::getSession(sdbus::ObjectPath& path) {
|
CScreencopyPortal::SSession* CScreencopyPortal::getSession(sdbus::ObjectPath& path) {
|
||||||
for (auto& s : m_vSessions) {
|
for (auto& s : m_vSessions) {
|
||||||
|
|
|
@ -72,6 +72,7 @@ class CScreencopyPortal {
|
||||||
struct {
|
struct {
|
||||||
bool active = false;
|
bool active = false;
|
||||||
zwlr_screencopy_frame_v1* frameCallback = nullptr;
|
zwlr_screencopy_frame_v1* frameCallback = nullptr;
|
||||||
|
hyprland_toplevel_export_frame_v1* windowFrameCallback = nullptr;
|
||||||
frameStatus status = FRAME_NONE;
|
frameStatus status = FRAME_NONE;
|
||||||
uint64_t tvSec = 0;
|
uint64_t tvSec = 0;
|
||||||
uint32_t tvNsec = 0;
|
uint32_t tvNsec = 0;
|
||||||
|
@ -100,6 +101,7 @@ class CScreencopyPortal {
|
||||||
|
|
||||||
void startFrameCopy(SSession* pSession);
|
void startFrameCopy(SSession* pSession);
|
||||||
void queueNextShareFrame(SSession* pSession);
|
void queueNextShareFrame(SSession* pSession);
|
||||||
|
bool hasToplevelCapabilities();
|
||||||
|
|
||||||
std::unique_ptr<CPipewireConnection> m_pPipewire;
|
std::unique_ptr<CPipewireConnection> m_pPipewire;
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,43 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
std::string sanitizeNameForWindowList(const std::string& name) {
|
||||||
|
std::string result = name;
|
||||||
|
for (size_t i = 1; i < result.size(); ++i) {
|
||||||
|
if (result[i - 1] == '>' && result[i] == ']')
|
||||||
|
result[i] = ' ';
|
||||||
|
if (result[i] == '\"')
|
||||||
|
result[i] = ' ';
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string buildWindowList() {
|
||||||
|
std::string result = "";
|
||||||
|
if (!g_pPortalManager->m_sPortals.screencopy->hasToplevelCapabilities())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
for (auto& e : g_pPortalManager->m_sHelpers.toplevel->m_vToplevels) {
|
||||||
|
|
||||||
|
result += std::format("{}[HC>]{}[HT>]{}[HE>]", (uint32_t)(((uint64_t)e->handle) & 0xFFFFFFFF), sanitizeNameForWindowList(e->windowClass),
|
||||||
|
sanitizeNameForWindowList(e->windowTitle));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
SSelectionData promptForScreencopySelection() {
|
SSelectionData promptForScreencopySelection() {
|
||||||
SSelectionData data;
|
SSelectionData data;
|
||||||
|
|
||||||
const auto RETVAL = execAndGet("hyprland-share-picker");
|
const char* WAYLAND_DISPLAY = getenv("WAYLAND_DISPLAY");
|
||||||
|
const char* XCURSOR_SIZE = getenv("XCURSOR_SIZE");
|
||||||
|
const char* HYPRLAND_INSTANCE_SIGNATURE = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||||
|
|
||||||
|
std::string cmd =
|
||||||
|
std::format("WAYLAND_DISPLAY={} QT_QPA_PLATFORM=\"wayland\" XCURSOR_SIZE={} HYPRLAND_INSTANCE_SIGNATURE={} XDPH_WINDOW_SHARING_LIST=\"{}\" hyprland-share-picker",
|
||||||
|
WAYLAND_DISPLAY ? WAYLAND_DISPLAY : "", XCURSOR_SIZE ? XCURSOR_SIZE : "24", HYPRLAND_INSTANCE_SIGNATURE ? HYPRLAND_INSTANCE_SIGNATURE : "0", buildWindowList());
|
||||||
|
|
||||||
|
const auto RETVAL = execAndGet(cmd.c_str());
|
||||||
|
|
||||||
Debug::log(LOG, "[sc] Selection: {}", RETVAL);
|
Debug::log(LOG, "[sc] Selection: {}", RETVAL);
|
||||||
|
|
||||||
|
@ -22,7 +55,19 @@ SSelectionData promptForScreencopySelection() {
|
||||||
|
|
||||||
data.output.pop_back();
|
data.output.pop_back();
|
||||||
} else if (RETVAL.find("window:") == 0) {
|
} else if (RETVAL.find("window:") == 0) {
|
||||||
// todo
|
data.type = TYPE_WINDOW;
|
||||||
|
uint32_t handleLo = std::stoull(RETVAL.substr(7));
|
||||||
|
data.windowHandle = nullptr;
|
||||||
|
|
||||||
|
for (auto& e : g_pPortalManager->m_sHelpers.toplevel->m_vToplevels) {
|
||||||
|
uint32_t handleLoE = (uint32_t)(((uint64_t)e->handle) & 0xFFFFFFFF);
|
||||||
|
|
||||||
|
if (handleLoE == handleLo) {
|
||||||
|
data.windowHandle = e->handle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if (RETVAL.find("region:") == 0) {
|
} else if (RETVAL.find("region:") == 0) {
|
||||||
std::string running = RETVAL;
|
std::string running = RETVAL;
|
||||||
running = running.substr(7);
|
running = running.substr(7);
|
||||||
|
|
|
@ -28,10 +28,12 @@ enum eSelectionType
|
||||||
TYPE_WORKSPACE,
|
TYPE_WORKSPACE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct zwlr_foreign_toplevel_handle_v1;
|
||||||
|
|
||||||
struct SSelectionData {
|
struct SSelectionData {
|
||||||
eSelectionType type = TYPE_INVALID;
|
eSelectionType type = TYPE_INVALID;
|
||||||
std::string output;
|
std::string output;
|
||||||
uint64_t windowHandle = 0;
|
zwlr_foreign_toplevel_handle_v1* windowHandle = nullptr;
|
||||||
uint32_t x = 0, y = 0, w = 0, h = 0; // for TYPE_GEOMETRY
|
uint32_t x = 0, y = 0, w = 0, h = 0; // for TYPE_GEOMETRY
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
87
src/shared/ToplevelManager.cpp
Normal file
87
src/shared/ToplevelManager.cpp
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#include "ToplevelManager.hpp"
|
||||||
|
#include "../helpers/Log.hpp"
|
||||||
|
|
||||||
|
static void toplevelTitle(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, const char* title) {
|
||||||
|
const auto PTL = (SToplevelHandle*)data;
|
||||||
|
|
||||||
|
if (title)
|
||||||
|
PTL->windowTitle = title;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[toplevel] toplevel at {} set title to {}", data, PTL->windowTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toplevelAppid(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, const char* app_id) {
|
||||||
|
const auto PTL = (SToplevelHandle*)data;
|
||||||
|
|
||||||
|
if (app_id)
|
||||||
|
PTL->windowClass = app_id;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[toplevel] toplevel at {} set class to {}", data, PTL->windowClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toplevelEnterOutput(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, wl_output* output) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toplevelLeaveOutput(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, wl_output* output) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toplevelState(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, wl_array* state) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toplevelDone(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toplevelClosed(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1) {
|
||||||
|
const auto PTL = (SToplevelHandle*)data;
|
||||||
|
|
||||||
|
std::erase_if(PTL->mgr->m_vToplevels, [&](const auto& e) { return e.get() == PTL; });
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[toplevel] toplevel at {} closed", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toplevelParent(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, struct zwlr_foreign_toplevel_handle_v1* parent) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const zwlr_foreign_toplevel_handle_v1_listener toplevelListener = {
|
||||||
|
.title = toplevelTitle,
|
||||||
|
.app_id = toplevelAppid,
|
||||||
|
.output_enter = toplevelEnterOutput,
|
||||||
|
.output_leave = toplevelLeaveOutput,
|
||||||
|
.state = toplevelState,
|
||||||
|
.done = toplevelDone,
|
||||||
|
.closed = toplevelClosed,
|
||||||
|
.parent = toplevelParent,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void managerToplevel(void* data, zwlr_foreign_toplevel_manager_v1* mgr, zwlr_foreign_toplevel_handle_v1* toplevel) {
|
||||||
|
const auto PMGR = (CToplevelManager*)data;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "[toplevel] New toplevel at {}", (void*)toplevel);
|
||||||
|
|
||||||
|
const auto PTL = PMGR->m_vToplevels.emplace_back(std::make_unique<SToplevelHandle>("?", "?", toplevel, PMGR)).get();
|
||||||
|
|
||||||
|
zwlr_foreign_toplevel_handle_v1_add_listener(toplevel, &toplevelListener, PTL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void managerFinished(void* data, zwlr_foreign_toplevel_manager_v1* mgr) {
|
||||||
|
const auto PMGR = (CToplevelManager*)data;
|
||||||
|
|
||||||
|
Debug::log(ERR, "[toplevel] Compositor sent .finished???");
|
||||||
|
|
||||||
|
PMGR->m_vToplevels.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const zwlr_foreign_toplevel_manager_v1_listener managerListener = {
|
||||||
|
.toplevel = managerToplevel,
|
||||||
|
.finished = managerFinished,
|
||||||
|
};
|
||||||
|
|
||||||
|
CToplevelManager::CToplevelManager(zwlr_foreign_toplevel_manager_v1* mgr) {
|
||||||
|
m_pManager = mgr;
|
||||||
|
zwlr_foreign_toplevel_manager_v1_add_listener(mgr, &managerListener, this);
|
||||||
|
}
|
26
src/shared/ToplevelManager.hpp
Normal file
26
src/shared/ToplevelManager.hpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class CToplevelManager;
|
||||||
|
|
||||||
|
struct SToplevelHandle {
|
||||||
|
std::string windowClass;
|
||||||
|
std::string windowTitle;
|
||||||
|
zwlr_foreign_toplevel_handle_v1* handle = nullptr;
|
||||||
|
CToplevelManager* mgr = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CToplevelManager {
|
||||||
|
public:
|
||||||
|
CToplevelManager(zwlr_foreign_toplevel_manager_v1* mgr);
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<SToplevelHandle>> m_vToplevels;
|
||||||
|
|
||||||
|
private:
|
||||||
|
zwlr_foreign_toplevel_manager_v1* m_pManager;
|
||||||
|
};
|
Loading…
Reference in a new issue