Skip to content

Commit 19f32ae

Browse files
committed
implement image copy capture cursor session
1 parent 832946a commit 19f32ae

File tree

6 files changed

+528
-9
lines changed

6 files changed

+528
-9
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#include "ScreenshareManager.hpp"
2+
#include "../PointerManager.hpp"
3+
#include "../../protocols/core/Seat.hpp"
4+
#include "../permissions/DynamicPermissionManager.hpp"
5+
#include "../../render/Renderer.hpp"
6+
7+
#include <cstring>
8+
9+
CCursorshareSession::CCursorshareSession(wl_client* client, WP<CWLPointerResource> pointer) : m_client(client), m_pointer(pointer) {
10+
m_listeners.pointerDestroyed = m_pointer->m_events.destroyed.listen([this] { stop(); });
11+
m_listeners.cursorChanged = g_pPointerManager->m_events.cursorChanged.listen([this] {
12+
calculateConstraints();
13+
m_events.constraintsChanged.emit();
14+
});
15+
16+
calculateConstraints();
17+
}
18+
19+
CCursorshareSession::~CCursorshareSession() {
20+
stop();
21+
}
22+
23+
void CCursorshareSession::stop() {
24+
if (m_stopped)
25+
return;
26+
m_stopped = true;
27+
m_events.stopped.emit();
28+
}
29+
30+
void CCursorshareSession::calculateConstraints() {
31+
const auto& cursorImage = g_pPointerManager->currentCursorImage();
32+
33+
// TODO: should cursor share have a format bit flip for RGBA?
34+
if (auto attrs = cursorImage.pBuffer->shm(); attrs.success) {
35+
m_format = attrs.format;
36+
} else {
37+
// we only have shm cursors
38+
return;
39+
}
40+
41+
m_hotspot = cursorImage.hotspot;
42+
m_bufferSize = cursorImage.size;
43+
}
44+
45+
// TODO: allow render to buffer without monitor and remove monitor param
46+
eScreenshareError CCursorshareSession::share(PHLMONITOR monitor, SP<IHLBuffer> buffer, FScreenshareCallback callback) {
47+
if (m_stopped || m_pointer.expired())
48+
return ERROR_STOPPED;
49+
50+
if UNLIKELY (!buffer || !buffer->m_resource || !buffer->m_resource->good()) {
51+
LOGM(ERR, "Client requested sharing to an invalid buffer");
52+
return ERROR_NO_BUFFER;
53+
}
54+
55+
if UNLIKELY (buffer->size != m_bufferSize) {
56+
LOGM(ERR, "Client requested sharing to an invalid buffer size");
57+
return ERROR_BUFFER_SIZE;
58+
}
59+
60+
if (auto attrs = buffer->dmabuf(); attrs.success && attrs.format != m_format) {
61+
LOGM(ERR, "Invalid dmabuf format {} in {:x}", attrs.format, (uintptr_t)this);
62+
return ERROR_BUFFER_FORMAT;
63+
} else if (auto attrs = buffer->shm(); attrs.success && attrs.format != m_format) {
64+
LOGM(ERR, "Invalid shm format {} in {:x}", attrs.format, (uintptr_t)this);
65+
return ERROR_BUFFER_FORMAT;
66+
} else {
67+
LOGM(ERR, "Client requested sharing to an invalid buffer");
68+
return ERROR_NO_BUFFER;
69+
}
70+
71+
if (!copy(monitor, buffer, callback)) {
72+
callback(RESULT_NOT_COPIED);
73+
return ERROR_UNKNOWN;
74+
}
75+
76+
return ERROR_NONE;
77+
}
78+
79+
bool CCursorshareSession::copy(PHLMONITOR monitor, SP<IHLBuffer> buffer, FScreenshareCallback callback) {
80+
const auto& cursorImage = g_pPointerManager->currentCursorImage();
81+
82+
const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(m_client, PERMISSION_TYPE_CURSOR_POS);
83+
if (PERM != PERMISSION_RULE_ALLOW_MODE_PENDING && PERM != PERMISSION_RULE_ALLOW_MODE_ALLOW)
84+
return false;
85+
86+
// FIXME: this doesn't really make sense but just to be safe
87+
callback(RESULT_TIMESTAMP);
88+
89+
if (auto attrs = buffer->dmabuf(); attrs.success) {
90+
if (attrs.format != m_format)
91+
return false;
92+
93+
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
94+
95+
if (!g_pHyprRenderer->beginRender(monitor, fakeDamage, RENDER_MODE_TO_BUFFER, buffer, nullptr, true)) {
96+
LOGM(ERR, "Can't copy: failed to begin rendering to dma frame");
97+
return false;
98+
}
99+
100+
if (cursorImage.surface && cursorImage.bufferTex && PERM != PERMISSION_RULE_ALLOW_MODE_PENDING) {
101+
CBox texbox = {{}, cursorImage.bufferTex->m_size};
102+
g_pHyprOpenGL->renderTexture(cursorImage.bufferTex, texbox, {});
103+
} else {
104+
g_pHyprOpenGL->clear(Colors::BLACK);
105+
}
106+
107+
g_pHyprOpenGL->m_renderData.blockScreenShader = true;
108+
109+
g_pHyprRenderer->endRender([callback]() {
110+
if (callback)
111+
callback(RESULT_COPIED);
112+
});
113+
} else if (auto attrs = buffer->shm(); attrs.success) {
114+
auto [bufData, fmt, bufLen] = buffer->beginDataPtr(0);
115+
116+
if (cursorImage.surface && cursorImage.pBuffer && PERM != PERMISSION_RULE_ALLOW_MODE_PENDING) {
117+
// we have to copy from gpu if we render this anyway, so dont render and just copy
118+
auto [cursorPixelData, cursorFmt, cursorBufLen] = cursorImage.pBuffer->beginDataPtr(0);
119+
120+
if (fmt != m_format || fmt != cursorFmt || bufLen != cursorBufLen)
121+
return false;
122+
123+
memcpy(bufData, cursorPixelData, bufLen);
124+
125+
buffer->endDataPtr();
126+
cursorImage.pBuffer->endDataPtr();
127+
128+
} else {
129+
memset(bufData, 0, bufLen);
130+
}
131+
132+
callback(RESULT_COPIED);
133+
} else
134+
return false;
135+
136+
return true;
137+
}
138+
139+
DRMFormat CCursorshareSession::format() const {
140+
return m_format;
141+
}
142+
143+
Vector2D CCursorshareSession::bufferSize() const {
144+
return m_bufferSize;
145+
}
146+
147+
Vector2D CCursorshareSession::hotspot() const {
148+
return m_hotspot;
149+
}

src/managers/screenshare/ScreenshareManager.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "../HookSystemManager.hpp"
66
#include "../EventManager.hpp"
77
#include "../eventLoop/EventLoopManager.hpp"
8+
#include "../../protocols/core/Seat.hpp"
89

910
CScreenshareManager::CScreenshareManager() {
1011
m_tickTimer = makeShared<CEventLoopTimer>(std::chrono::microseconds(500), [this](SP<CEventLoopTimer> self, void* data) { onTick(); }, nullptr);
@@ -129,6 +130,15 @@ UP<CScreenshareSession> CScreenshareManager::newSession(wl_client* client, PHLWI
129130
return session;
130131
}
131132

133+
UP<CCursorshareSession> CScreenshareManager::newCursorSession(wl_client* client, WP<CWLPointerResource> pointer) {
134+
UP<CCursorshareSession> session = UP<CCursorshareSession>(new CCursorshareSession(client, pointer));
135+
136+
session->m_self = session;
137+
m_cursorSessions.emplace_back(session);
138+
139+
return session;
140+
}
141+
132142
WP<CScreenshareSession> CScreenshareManager::getManagedSession(wl_client* client, PHLMONITOR monitor) {
133143
return getManagedSession(SHARE_MONITOR, client, monitor, nullptr, {});
134144
}

src/managers/screenshare/ScreenshareManager.hpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ struct std::formatter<eScreenshareType> : std::formatter<std::string> {
4545
using FScreenshareCallback = std::function<void(eScreenshareResult result)>;
4646

4747
class CScreenshareFrame;
48+
class CWLPointerResource;
4849

4950
class CScreenshareSession {
5051
public:
@@ -105,6 +106,51 @@ class CScreenshareSession {
105106
friend class CScreenshareManager;
106107
};
107108

109+
class CCursorshareSession {
110+
public:
111+
CCursorshareSession(const CCursorshareSession&) = delete;
112+
CCursorshareSession(CCursorshareSession&&) = delete;
113+
~CCursorshareSession();
114+
115+
eScreenshareError share(PHLMONITOR monitor, SP<IHLBuffer> buffer, FScreenshareCallback callback);
116+
void stop();
117+
118+
// constraints
119+
DRMFormat format() const;
120+
Vector2D bufferSize() const;
121+
Vector2D hotspot() const;
122+
123+
struct {
124+
CSignalT<> stopped;
125+
CSignalT<> constraintsChanged;
126+
} m_events;
127+
128+
private:
129+
CCursorshareSession(wl_client* client, WP<CWLPointerResource> pointer);
130+
131+
WP<CCursorshareSession> m_self;
132+
bool m_stopped = false;
133+
134+
wl_client* m_client;
135+
WP<CWLPointerResource> m_pointer;
136+
137+
// constraints
138+
DRMFormat m_format;
139+
Vector2D m_hotspot;
140+
Vector2D m_bufferSize;
141+
142+
struct {
143+
CHyprSignalListener pointerDestroyed;
144+
CHyprSignalListener cursorChanged;
145+
} m_listeners;
146+
147+
bool copy(PHLMONITOR monitor, SP<IHLBuffer> buffer, FScreenshareCallback callback);
148+
void calculateConstraints();
149+
150+
friend class CScreenshareFrame;
151+
friend class CScreenshareManager;
152+
};
153+
108154
class CScreenshareFrame {
109155
public:
110156
CScreenshareFrame(const CScreenshareFrame&) = delete;
@@ -158,12 +204,15 @@ class CScreenshareManager {
158204
WP<CScreenshareSession> getManagedSession(wl_client* client, PHLMONITOR monitor, CBox captureBox);
159205
WP<CScreenshareSession> getManagedSession(wl_client* client, PHLWINDOW window);
160206

207+
UP<CCursorshareSession> newCursorSession(wl_client* client, WP<CWLPointerResource> pointer);
208+
161209
void destroyClientSessions(wl_client* client);
162210

163211
void onOutputCommit(PHLMONITOR monitor);
164212

165213
private:
166214
std::vector<WP<CScreenshareSession>> m_sessions;
215+
std::vector<WP<CCursorshareSession>> m_cursorSessions;
167216
std::vector<WP<CScreenshareFrame>> m_frames;
168217

169218
struct SManagedSession {

src/protocols/ImageCaptureSource.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class CImageCaptureSource {
2626
PHLWINDOWREF m_window;
2727

2828
friend class CImageCopyCaptureSession;
29+
friend class CImageCopyCaptureCursorSession;
2930
};
3031

3132
class COutputImageCaptureSourceProtocol : public IWaylandProtocol {

0 commit comments

Comments
 (0)