Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/config/ConfigDescriptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)",
Expand Down
2 changes: 2 additions & 0 deletions src/config/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"});
Expand Down
5 changes: 5 additions & 0 deletions src/protocols/Screencopy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; });
}
Expand Down
9 changes: 5 additions & 4 deletions src/protocols/Screencopy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;

Expand Down Expand Up @@ -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;

Expand Down
6 changes: 5 additions & 1 deletion src/protocols/ToplevelExport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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; });
}
Expand Down
9 changes: 5 additions & 4 deletions src/protocols/ToplevelExport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
44 changes: 44 additions & 0 deletions src/render/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1457,6 +1461,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();
Expand Down
25 changes: 25 additions & 0 deletions src/render/decorations/CHyprBorderDecoration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
;
Expand Down Expand Up @@ -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);
Expand Down
Loading