This commit is contained in:
Xavier 2025-12-19 19:44:03 +03:00 committed by GitHub
commit 42fed34743
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 103 additions and 9 deletions

View file

@ -15,6 +15,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{1, 0, 20},
},
SConfigOptionDescription{
.value = "general:screencast_border.enabled",
.description = "enable red border indicator when screen sharing",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "general:screencast_border.color",
.description = "border color when screen sharing",
.type = CONFIG_OPTION_GRADIENT,
.data = SConfigOptionDescription::SGradientData{"0xffff0000"},
},
SConfigOptionDescription{
.value = "general:gaps_in",
.description = "gaps between windows\n\nsupports css style gaps (top, right, bottom, left -> 5 10 15 20)",

View file

@ -443,6 +443,8 @@ CConfigManager::CConfigManager() {
m_config = makeUnique<Hyprlang::CConfig>(m_configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true});
registerConfigVar("general:border_size", Hyprlang::INT{1});
registerConfigVar("general:screencast_border.enabled", Hyprlang::INT{1});
registerConfigVar("general:screencast_border.color", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xffff0000"});
registerConfigVar("general:gaps_in", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "5"});
registerConfigVar("general:gaps_out", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "20"});
registerConfigVar("general:float_gaps", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "0"});

View file

@ -514,6 +514,11 @@ void CScreencopyProtocol::destroyResource(CScreencopyClient* client) {
}
void CScreencopyProtocol::destroyResource(CScreencopyFrame* frame) {
// Damage the monitor before destroying the frame so the border updates
if (frame && frame->m_monitor) {
if (auto monitor = frame->m_monitor.lock())
g_pHyprRenderer->damageMonitor(monitor);
}
std::erase_if(m_frames, [&](const auto& other) { return other.get() == frame; });
std::erase_if(m_framesAwaitingWrite, [&](const auto& other) { return !other || other.get() == frame; });
}

View file

@ -36,12 +36,13 @@ class CScreencopyClient {
CTimer m_lastFrame;
int m_frameCounter = 0;
bool m_sentScreencast = false;
private:
SP<CZwlrScreencopyManagerV1> m_resource;
int m_framesInLastHalfSecond = 0;
CTimer m_lastMeasure;
bool m_sentScreencast = false;
SP<HOOK_CALLBACK_FN> m_tickCallback;
void onTick();
@ -59,11 +60,10 @@ class CScreencopyFrame {
WP<CScreencopyFrame> m_self;
WP<CScreencopyClient> m_client;
PHLMONITORREF m_monitor;
private:
SP<CZwlrScreencopyFrameV1> m_resource;
PHLMONITORREF m_monitor;
bool m_overlayCursor = false;
bool m_withDamage = false;
@ -97,8 +97,9 @@ class CScreencopyProtocol : public IWaylandProtocol {
void onOutputCommit(PHLMONITOR pMonitor);
private:
std::vector<SP<CScreencopyFrame>> m_frames;
private:
std::vector<WP<CScreencopyFrame>> m_framesAwaitingWrite;
std::vector<SP<CScreencopyClient>> m_clients;

View file

@ -74,7 +74,7 @@ bool CToplevelExportClient::good() {
return m_resource->resource();
}
CToplevelExportFrame::CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> resource_, int32_t overlayCursor_, PHLWINDOW pWindow_) : m_resource(resource_), m_window(pWindow_) {
CToplevelExportFrame::CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> resource_, int32_t overlayCursor_, PHLWINDOW pWindow_) : m_window(pWindow_), m_resource(resource_) {
if UNLIKELY (!good())
return;
@ -411,6 +411,10 @@ void CToplevelExportProtocol::destroyResource(CToplevelExportClient* client) {
}
void CToplevelExportProtocol::destroyResource(CToplevelExportFrame* frame) {
// Damage the window before destroying the frame so the border updates
if (frame && frame->m_window) {
g_pHyprRenderer->damageWindow(frame->m_window);
}
std::erase_if(m_frames, [&](const auto& other) { return other.get() == frame; });
std::erase_if(m_framesAwaitingWrite, [&](const auto& other) { return !other || other.get() == frame; });
}

View file

@ -22,12 +22,13 @@ class CToplevelExportClient {
CTimer m_lastFrame;
int m_frameCounter = 0;
bool m_sentScreencast = false;
private:
SP<CHyprlandToplevelExportManagerV1> m_resource;
int m_framesInLastHalfSecond = 0;
CTimer m_lastMeasure;
bool m_sentScreencast = false;
SP<HOOK_CALLBACK_FN> m_tickCallback;
void onTick();
@ -45,11 +46,10 @@ class CToplevelExportFrame {
WP<CToplevelExportFrame> m_self;
WP<CToplevelExportClient> m_client;
PHLWINDOW m_window;
private:
SP<CHyprlandToplevelExportFrameV1> m_resource;
PHLWINDOW m_window;
bool m_cursorOverlayRequested = false;
bool m_ignoreDamage = false;
@ -79,9 +79,10 @@ class CToplevelExportProtocol : IWaylandProtocol {
void onOutputCommit(PHLMONITOR pMonitor);
std::vector<SP<CToplevelExportFrame>> m_frames;
private:
std::vector<SP<CToplevelExportClient>> m_clients;
std::vector<SP<CToplevelExportFrame>> m_frames;
std::vector<WP<CToplevelExportFrame>> m_framesAwaitingWrite;
void onWindowUnmap(PHLWINDOW pWindow);

View file

@ -41,6 +41,10 @@
#include "../protocols/types/ContentType.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "render/OpenGL.hpp"
#include "../protocols/Screencopy.hpp"
#include "../config/ConfigDataValues.hpp"
#include "../helpers/Color.hpp"
#include "pass/BorderPassElement.hpp"
#include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::Utils;
@ -1459,6 +1463,46 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
m_renderPass.add(makeUnique<CRectPassElement>(data));
}
// Render border around monitor if it's being shared
static auto PENABLED = CConfigValue<Hyprlang::INT>("general:screencast_border.enabled");
if (*PENABLED) {
// Check if monitor is being shared through screencopy
bool isMonitorShared = false;
if (PROTO::screencopy) {
for (const auto& frame : PROTO::screencopy->m_frames) {
if (frame && frame->m_monitor && frame->m_monitor.lock() == pMonitor) {
isMonitorShared = frame->m_client->m_sentScreencast;
break;
}
}
}
if (isMonitorShared) {
static auto PBORDERSIZE = CConfigValue<Hyprlang::INT>("general:border_size");
static auto PCOLOR = CConfigValue<Hyprlang::CUSTOMTYPE>("general:screencast_border.color");
const int borderSize = *PBORDERSIZE > 0 ? *PBORDERSIZE : 1;
const int scaledBorderSize = std::round(borderSize * pMonitor->m_scale);
CBox monitorBox = {scaledBorderSize, scaledBorderSize, std::max(1, sc<int>(pMonitor->m_transformedSize.x) - (2 * scaledBorderSize)),
std::max(1, sc<int>(pMonitor->m_transformedSize.y) - (2 * scaledBorderSize))};
const auto* const PGRAD = sc<CGradientValueData*>((PCOLOR.ptr())->getData());
CGradientValueData borderGrad = PGRAD ? *PGRAD : CGradientValueData(Colors::RED); // fallback to red if config not loaded
CBorderPassElement::SBorderData borderData;
borderData.box = monitorBox;
borderData.grad1 = borderGrad;
borderData.round = 0;
borderData.outerRound = -1;
borderData.roundingPower = 2.0f;
borderData.a = 1.0f;
borderData.borderSize = borderSize;
borderData.hasGrad2 = false;
m_renderPass.add(makeUnique<CBorderPassElement>(borderData));
}
}
EMIT_HOOK_EVENT("render", RENDER_LAST_MOMENT);
endRender();

View file

@ -5,6 +5,10 @@
#include "../pass/BorderPassElement.hpp"
#include "../Renderer.hpp"
#include "../../managers/HookSystemManager.hpp"
#include "../../protocols/Screencopy.hpp"
#include "../../protocols/ToplevelExport.hpp"
#include "../../helpers/Color.hpp"
#include "../../config/ConfigDataValues.hpp"
CHyprBorderDecoration::CHyprBorderDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_window(pWindow) {
;
@ -60,6 +64,27 @@ void CHyprBorderDecoration::draw(PHLMONITOR pMonitor, float const& a) {
auto grad = m_window->m_realBorderColor;
const bool ANIMATED = m_window->m_borderFadeAnimationProgress->isBeingAnimated();
static auto PENABLED = CConfigValue<Hyprlang::INT>("general:screencast_border.enabled");
static auto PCOLOR = CConfigValue<Hyprlang::CUSTOMTYPE>("general:screencast_border.color");
if (*PENABLED) {
// Check if window is being shared through toplevel export
bool isWindowShared = false;
if (PROTO::toplevelExport) {
for (const auto& frame : PROTO::toplevelExport->m_frames) {
if (frame && frame->m_window && frame->m_window == m_window) {
isWindowShared = frame->m_client->m_sentScreencast;
break;
}
}
}
if (isWindowShared) {
const auto* const PGRAD = sc<CGradientValueData*>((PCOLOR.ptr())->getData());
grad = PGRAD ? *PGRAD : CGradientValueData(Colors::RED); // fallback to red if config not loaded
}
}
if (m_window->m_borderAngleAnimationProgress->enabled()) {
grad.m_angle += m_window->m_borderAngleAnimationProgress->value() * M_PI * 2;
grad.m_angle = normalizeAngleRad(grad.m_angle);