Skip to content
Merged
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
3 changes: 3 additions & 0 deletions bin/st/shitty.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ border = 2
# CLI: -cr COLOR — Set the cursor RGB color; omit the key to use the foreground color.
cr = "#88c0d0"

# CLI: -cursorKeepSelectionFg / +cursorKeepSelectionFg — Keep selected or reverse-video text's foreground color under the block cursor.
cursorKeepSelectionFg = false

# CLI: -dump PATH — Write raw PTY input to PATH; normally omitted.
# dump = "/tmp/shitty.pty"

Expand Down
2 changes: 2 additions & 0 deletions lib/shitty/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ namespace {
{"config", OptionKind::SepArg, nullptr, nullptr, "Path to the TOML config file", true},
{"colorScheme", OptionKind::SepArg, nullptr, "default", "Named terminal color scheme"},
{"cr", OptionKind::SepArg, nullptr, nullptr, "Cursor color"},
{"cursorKeepSelectionFg", OptionKind::NoArg, "true", "false", "Keep selected or reverse-video text color under the block cursor"},
{"debug", OptionKind::SepArg, nullptr, nullptr, "Append window, font and grid diagnostics to this file", true},
{"dump", OptionKind::SepArg, nullptr, nullptr, "Dump raw PTY input to file"},
{"fg", OptionKind::SepArg, nullptr, "#fff", "Foreground color"},
Expand Down Expand Up @@ -1276,6 +1277,7 @@ void OptionsParser::parse() {
maximized = getBool("maximized");
fullscreen = getBool("fullscreen");
showWraps = getBool("showWraps");
cursorKeepSelectionFg = getBool("cursorKeepSelectionFg");
vt.verbose = getBool("verbose");
vt.modifyOtherKeys = getInteger("modifyOtherKeys", 0, 2);
} catch (Exception& error) {
Expand Down
1 change: 1 addition & 0 deletions lib/shitty/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ struct Options {
bool titleFallbackProcess = false;
bool optical = false;
bool showWraps = false;
bool cursorKeepSelectionFg = false;
bool rv = false;

static Options* create(stl::ObjPool& pool, Brand& brand, char** argv, int argc, OptionsLoad load = OptionsLoad::Startup);
Expand Down
13 changes: 13 additions & 0 deletions lib/shitty/options_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ namespace {
}

STD_TEST_SUITE(Options) {
STD_TEST(CursorKeepSelectionFgFlag) {
auto pool = ObjPool::fromMemory();
char program[] = "st";
char enabled[] = "-cursorKeepSelectionFg";
char disabled[] = "+cursorKeepSelectionFg";
char* defaults[] = {program, nullptr};
char* on[] = {program, enabled, nullptr};
char* off[] = {program, enabled, disabled, nullptr};
STD_INSIST(!Options::create(*pool, *Brand::generic(), defaults, 1)->cursorKeepSelectionFg);
STD_INSIST(Options::create(*pool, *Brand::generic(), on, 2)->cursorKeepSelectionFg);
STD_INSIST(!Options::create(*pool, *Brand::generic(), off, 3)->cursorKeepSelectionFg);
}

STD_TEST(SymbolFontTablesParseFromConfig) {
auto pool = ObjPool::fromMemory();
Buffer config = writeTempConfig(StringView(
Expand Down
7 changes: 5 additions & 2 deletions lib/shitty/render.comp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ layout (push_constant) uniform PushConstants
uint hoveredLinkBegin;
uint hoveredLinkEnd;
uint updateCount;
uint cursorKeepSelectionFg;
} parameters;

vec3 unpackColor (uint color)
Expand Down Expand Up @@ -592,7 +593,8 @@ void main ()
foreground = background;
background = temporary;
}
if (selected (cellPosition, doubleWidth))
bool selectedCell = selected (cellPosition, doubleWidth);
if (selectedCell)
{
if (parameters.selectionColorMask == 0)
{
Expand Down Expand Up @@ -633,7 +635,8 @@ void main ()
(parameters.cursorBlink == 0 || parameters.blinkVisible != 0);
if (cursorHere && parameters.cursorStyle == 1)
{
foreground = background;
if (!(selectedCell || inverse != 0) || parameters.cursorKeepSelectionFg == 0)
foreground = background;
background = cursor;
}

Expand Down
4 changes: 3 additions & 1 deletion lib/shitty/render_metal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,10 @@
u32 hoveredLinkBegin;
u32 hoveredLinkEnd;
u32 updateCount;
u32 cursorKeepSelectionFg;
};

static_assert(sizeof(PushConstants) == 116, "Metal push constant layout mismatch");
static_assert(sizeof(PushConstants) == 120, "Metal push constant layout mismatch");

struct PresentationState {
TerminalCursor cursor;
Expand Down Expand Up @@ -694,6 +695,7 @@
state.hoveredLinkBegin,
state.hoveredLinkEnd,
updateCount,
composer.opts->cursorKeepSelectionFg ? 1u : 0u,
};

// The spirv-cross assignment for this shader: push constants at
Expand Down
7 changes: 5 additions & 2 deletions lib/shitty/render_reference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,8 @@ void ReferenceRendererImpl::renderCell(const TerminalUpdate& update, const Refer
if ((source.inverse != 0) != update.screenReverse) {
xchg(foreground, background);
}
if (selected(update, source, column, row)) {
const bool selectedCell = selected(update, source, column, row);
if (selectedCell) {
if (update.selectionColorMask == 0) {
xchg(foreground, background);
} else {
Expand All @@ -448,7 +449,9 @@ void ReferenceRendererImpl::renderCell(const TerminalUpdate& update, const Refer
}
const bool cursorHere = column == update.cursor.posX && row == update.cursor.posY && (!update.cursorBlink || update.blinkVisible);
if (cursorHere && update.cursor.style == TerminalCursor::Style::filled_block) {
foreground = background;
if (!(selectedCell || source.inverse) || !composer_.opts->cursorKeepSelectionFg) {
foreground = background;
}
background = cursor;
}

Expand Down
57 changes: 57 additions & 0 deletions lib/shitty/render_reference_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,63 @@ STD_TEST_SUITE(ReferenceRenderer) {
STD_INSIST((cellPixel(image, 1, 0) == Color{4, 5, 6}));
}

STD_TEST(CursorKeepsSelectedForegroundWhenEnabled) {
ScreenFixture fx(1, 1);
TerminalCell attrs = coloredCell({255, 0, 0}, {0, 0, 255});
// An overline gives exact foreground pixels without glyph antialiasing.
attrs.overline = true;
fx.writeText(0, 0, " ", attrs);
Options options;
fx.composer->setOptions(&options);
ReferenceFixture renderer(*fx.composer);
TerminalUpdate update = fx.capture();
update.snappedSelection = Rect(0, 0);
update.cursor.color = {0, 255, 0};
update.cursor.style = TerminalCursor::Style::filled_block;
const auto checkColors = [&](Color foreground) {
const ReferenceImage image = renderer->render(update);
STD_INSIST(image.pixels != nullptr);
STD_INSIST(cellPixel(image, 0, 0) == foreground);
STD_INSIST(cellPixel(image, 0, 1) == update.cursor.color);
};

checkColors({255, 0, 0});
options.cursorKeepSelectionFg = true;
checkColors({0, 0, 255});
update.snappedSelection = {};
checkColors({0, 0, 255});
}

STD_TEST(CursorKeepsReverseVideoForegroundWhenEnabled) {
ScreenFixture fx(1, 1);
TerminalCell attrs = coloredCell({255, 0, 0}, {0, 0, 255});
attrs.inverse = true;
attrs.overline = true;
fx.writeText(0, 0, " ", attrs);
Options options;
fx.composer->setOptions(&options);
ReferenceFixture renderer(*fx.composer);
TerminalUpdate update = fx.capture();
update.cursor.color = {0, 255, 0};
update.cursor.style = TerminalCursor::Style::filled_block;
const auto checkColors = [&](Color foreground) {
const ReferenceImage image = renderer->render(update);
STD_INSIST(image.pixels != nullptr);
STD_INSIST(cellPixel(image, 0, 0) == foreground);
STD_INSIST(cellPixel(image, 0, 1) == update.cursor.color);
};

checkColors({255, 0, 0});
options.cursorKeepSelectionFg = true;
checkColors({0, 0, 255});
attrs.inverse = false;
fx.writeText(0, 0, " ", attrs);
update = fx.capture();
update.cursor.color = {0, 255, 0};
update.cursor.style = TerminalCursor::Style::filled_block;
checkColors({0, 0, 255});
}

STD_TEST(SelectionOfWideContinuationHighlightsWholeGlyph) {
auto pool = ObjPool::fromMemory();
Composer& composer = *pool->make<Composer>(pool.mutPtr());
Expand Down
4 changes: 3 additions & 1 deletion lib/shitty/render_vk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,10 @@ namespace {
u32 hoveredLinkBegin;
u32 hoveredLinkEnd;
u32 updateCount;
u32 cursorKeepSelectionFg;
};

static_assert(sizeof(PushConstants) == 116, "Vulkan push constant layout mismatch");
static_assert(sizeof(PushConstants) == 120, "Vulkan push constant layout mismatch");

// The strip arenas mirrored on the device; append-only between
// collections, so only the tail uploads each frame.
Expand Down Expand Up @@ -1793,6 +1794,7 @@ void RendererImpl::recordCommands(FrameResources& frame, u32 imageIndex, const P
state.hoveredLinkBegin,
state.hoveredLinkEnd,
updateCount,
composer.opts->cursorKeepSelectionFg ? 1u : 0u,
};
vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
vkCmdBindDescriptorSets(frame.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &frame.descriptorSet, 0, nullptr);
Expand Down