Allow rendering the cursor with reverse video

Also clean up handling of dynamic global colors.
TODO: Implement none for selection_fg
TODO: Add some tests
TODO: Check that changing colors via remote control works

Fixes #126
This commit is contained in:
Kovid Goyal
2021-10-28 00:19:36 +05:30
parent 3ca3c67828
commit 0fdafd8398
16 changed files with 167 additions and 101 deletions

View File

@@ -16,6 +16,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
the keyboard. Pressing it causes numbers to appear over each visible window the keyboard. Pressing it causes numbers to appear over each visible window
and you can press the number to focus the corresponding window (:iss:`4110`) and you can press the number to focus the corresponding window (:iss:`4110`)
- Allow rendering the cursor with a *reverse video* effect. See :opt:`cursor`
for details (:iss:`126`)
- A new option :opt:`tab_bar_align` to draw the tab bar centered or right - A new option :opt:`tab_bar_align` to draw the tab bar centered or right
aligned (:iss:`3946`) aligned (:iss:`3946`)

View File

@@ -12,9 +12,9 @@
// Inputs {{{ // Inputs {{{
layout(std140) uniform CellRenderData { layout(std140) uniform CellRenderData {
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, cursor_text_uses_bg; float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, cursor_text_color, url_color, url_style, inverted; uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
uint xnum, ynum, cursor_fg_sprite_idx; uint xnum, ynum, cursor_fg_sprite_idx;
float cursor_x, cursor_y, cursor_w; float cursor_x, cursor_y, cursor_w;
@@ -198,8 +198,8 @@ void main() {
strike_pos = to_sprite_pos(pos, ((text_attrs >> STRIKE_SHIFT) & ONE) * FOUR, ZERO, ZERO); strike_pos = to_sprite_pos(pos, ((text_attrs >> STRIKE_SHIFT) & ONE) * FOUR, ZERO, ZERO);
// Cursor // Cursor
cursor_color_vec = vec4(color_to_vec(cursor_color), 1.0); cursor_color_vec = vec4(color_to_vec(cursor_bg), 1.0);
vec3 final_cursor_text_color = mix(color_to_vec(cursor_text_color), bg, cursor_text_uses_bg); vec3 final_cursor_text_color = color_to_vec(cursor_fg);
foreground = choose_color(cell_has_block_cursor, final_cursor_text_color, foreground); foreground = choose_color(cell_has_block_cursor, final_cursor_text_color, foreground);
decoration_fg = choose_color(cell_has_block_cursor, final_cursor_text_color, decoration_fg); decoration_fg = choose_color(cell_has_block_cursor, final_cursor_text_color, decoration_fg);
cursor_pos = to_sprite_pos(pos, cursor_fg_sprite_idx * uint(cell_has_cursor), ZERO, ZERO); cursor_pos = to_sprite_pos(pos, cursor_fg_sprite_idx * uint(cell_has_cursor), ZERO, ZERO);
@@ -237,7 +237,7 @@ void main() {
#if defined(SPECIAL) || defined(SIMPLE) #if defined(SPECIAL) || defined(SIMPLE)
// Selection and cursor // Selection and cursor
bg = choose_color(float(is_selected & ONE), color_to_vec(highlight_bg), bg); bg = choose_color(float(is_selected & ONE), color_to_vec(highlight_bg), bg);
background = choose_color(cell_has_block_cursor, color_to_vec(cursor_color), bg); background = choose_color(cell_has_block_cursor, color_to_vec(cursor_bg), bg);
#if !defined(TRANSPARENT) && defined(SPECIAL) #if !defined(TRANSPARENT) && defined(SPECIAL)
float is_special_cell = cell_has_block_cursor + float(is_selected & ONE); float is_special_cell = cell_has_block_cursor + float(is_selected & ONE);
bg_alpha = step(0.5, is_special_cell); bg_alpha = step(0.5, is_special_cell);

View File

@@ -574,9 +574,7 @@ collect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow
} }
if (!do_draw_cursor) { ans->is_visible = false; return; } if (!do_draw_cursor) { ans->is_visible = false; return; }
ans->is_visible = true; ans->is_visible = true;
ColorProfile *cp = rd->screen->color_profile;
ans->shape = cursor->shape ? cursor->shape : OPT(cursor_shape); ans->shape = cursor->shape ? cursor->shape : OPT(cursor_shape);
ans->color = colorprofile_to_color(cp, cp->overridden.cursor_color, cp->configured.cursor_color);
ans->is_focused = os_window->is_focused; ans->is_focused = os_window->is_focused;
} }
@@ -617,7 +615,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
#define WD w->render_data #define WD w->render_data
if (w->visible && WD.screen) { if (w->visible && WD.screen) {
*num_visible_windows += 1; *num_visible_windows += 1;
color_type window_bg = colorprofile_to_color(WD.screen->color_profile, WD.screen->color_profile->overridden.default_bg, WD.screen->color_profile->configured.default_bg); color_type window_bg = colorprofile_to_color(WD.screen->color_profile, WD.screen->color_profile->overridden.default_bg, WD.screen->color_profile->configured.default_bg).rgb;
if (*num_visible_windows == 1) first_window_bg = window_bg; if (*num_visible_windows == 1) first_window_bg = window_bg;
if (first_window_bg != window_bg) *all_windows_have_same_bg = false; if (first_window_bg != window_bg) *all_windows_have_same_bg = false;
if (w->last_drag_scroll_at > 0) { if (w->last_drag_scroll_at > 0) {

View File

@@ -151,65 +151,65 @@ patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
S(background, i); S(foreground, i); S(background, i); S(foreground, i);
#undef S #undef S
} }
#define SI(profile_name) \
DynamicColor color; \
if (PyLong_Check(v)) { \
color.rgb = PyLong_AsUnsignedLong(v); color.type = COLOR_IS_RGB; \
} else { color.rgb = 0; color.type = COLOR_IS_SPECIAL; }\
self->overridden.profile_name = color; \
if (change_configured) self->configured.profile_name = color; \
self->dirty = true;
#define S(config_name, profile_name) { \ #define S(config_name, profile_name) { \
v = PyDict_GetItemString(spec, #config_name); \ v = PyDict_GetItemString(spec, #config_name); \
if (v && PyLong_Check(v)) { \ if (v) { \
color_type color = PyLong_AsUnsignedLong(v); \
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) { \ for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) { \
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i); \ self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i); \
self->overridden.profile_name = (color << 8) | 2; \ SI(profile_name); \
if (change_configured) self->configured.profile_name = color; \
self->dirty = true; \
} \ } \
} \ } \
} }
S(foreground, default_fg); S(background, default_bg); S(cursor, cursor_color); S(foreground, default_fg); S(background, default_bg); S(cursor, cursor_color);
S(selection_foreground, highlight_fg); S(selection_background, highlight_bg); S(selection_foreground, highlight_fg); S(selection_background, highlight_bg);
S(cursor_text_color, cursor_text_color);
#undef SI
#undef S #undef S
PyObject *cursor_text_color = PyDict_GetItemString(spec, "cursor_text_color");
if (cursor_text_color) {
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) {
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i);
self->overridden.cursor_text_color = 0x111111;
self->overridden.cursor_text_uses_bg = 3;
if (PyLong_Check(cursor_text_color)) {
self->overridden.cursor_text_color = (PyLong_AsUnsignedLong(cursor_text_color) << 8) | 2;
self->overridden.cursor_text_uses_bg = 1;
}
if (change_configured) {
self->configured.cursor_text_color = self->overridden.cursor_text_color;
self->configured.cursor_text_uses_bg = self->overridden.cursor_text_uses_bg;
}
self->dirty = true;
}
}
Py_RETURN_NONE; Py_RETURN_NONE;
} }
color_type DynamicColor
colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval) { colorprofile_to_color(ColorProfile *self, DynamicColor entry, DynamicColor defval) {
color_type t = entry & 0xFF, r; switch(entry.type) {
switch(t) { case COLOR_NOT_SET:
case 1:
r = (entry >> 8) & 0xff;
return self->color_table[r];
case 2:
return entry >> 8;
default:
return defval; return defval;
case COLOR_IS_INDEX: {
DynamicColor ans;
ans.rgb = self->color_table[entry.rgb & 0xff] & 0xffffff;
ans.type = COLOR_IS_RGB;
return ans;
}
case COLOR_IS_RGB:
case COLOR_IS_SPECIAL:
return entry;
} }
return entry;
} }
float color_type
cursor_text_as_bg(ColorProfile *self) { colorprofile_to_color_with_fallback(ColorProfile *self, DynamicColor entry, DynamicColor defval, DynamicColor fallback, DynamicColor falback_defval) {
if (self->overridden.cursor_text_uses_bg & 1) { switch(entry.type) {
return self->overridden.cursor_text_uses_bg & 2 ? 1.f : 0.f; case COLOR_NOT_SET:
case COLOR_IS_SPECIAL:
if (defval.type == COLOR_IS_SPECIAL) return colorprofile_to_color(self, fallback, falback_defval).rgb;
return defval.rgb;
case COLOR_IS_RGB:
return entry.rgb;
case COLOR_IS_INDEX:
return self->color_table[entry.rgb & 0xff] & 0xffffff;
} }
return self->configured.cursor_text_uses_bg & 2 ? 1.f : 0.f; return entry.rgb;
} }
static PyObject* static PyObject*
as_dict(ColorProfile *self, PyObject *args UNUSED) { as_dict(ColorProfile *self, PyObject *args UNUSED) {
#define as_dict_doc "Return all colors as a dictionary of color_name to integer (names are the same as used in kitty.conf)" #define as_dict_doc "Return all colors as a dictionary of color_name to integer (names are the same as used in kitty.conf)"
@@ -225,8 +225,8 @@ as_dict(ColorProfile *self, PyObject *args UNUSED) {
if (ret != 0) { Py_CLEAR(ans); return NULL; } if (ret != 0) { Py_CLEAR(ans); return NULL; }
} }
#define D(attr, name) { \ #define D(attr, name) { \
color_type c = colorprofile_to_color(self, self->overridden.attr, 0xffffffff); \ if (self->overridden.attr.type != COLOR_NOT_SET) { \
if (c != 0xffffffff) { \ color_type c = colorprofile_to_color(self, self->overridden.attr, self->configured.attr).rgb; \
PyObject *val = PyLong_FromUnsignedLong(c); \ PyObject *val = PyLong_FromUnsignedLong(c); \
if (!val) { Py_CLEAR(ans); return PyErr_NoMemory(); } \ if (!val) { Py_CLEAR(ans); return PyErr_NoMemory(); } \
int ret = PyDict_SetItemString(ans, #name, val); \ int ret = PyDict_SetItemString(ans, #name, val); \
@@ -296,11 +296,14 @@ set_color(ColorProfile *self, PyObject *args) {
static PyObject* static PyObject*
set_configured_colors(ColorProfile *self, PyObject *args) { set_configured_colors(ColorProfile *self, PyObject *args) {
#define set_configured_colors_doc "Set the configured colors" #define set_configured_colors_doc "Set the configured colors"
if (!PyArg_ParseTuple( unsigned int default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg;
args, "II|IIIII", if (!PyArg_ParseTuple(args, "II|IIII", &default_fg, &default_bg,
&(self->configured.default_fg), &(self->configured.default_bg), &cursor_color, &cursor_text_color, &highlight_fg, &highlight_bg)) return NULL;
&(self->configured.cursor_color), &(self->configured.cursor_text_color), &(self->configured.cursor_text_uses_bg), #define S(which) \
&(self->configured.highlight_fg), &(self->configured.highlight_bg))) return NULL; self->configured.which.rgb = which & 0xffffff; \
self->configured.which.type = (which & 0xff000000) ? COLOR_IS_RGB : COLOR_IS_SPECIAL;
S(default_fg); S(default_bg); S(cursor_color); S(cursor_text_color); S(highlight_fg); S(highlight_bg);
#undef S
self->dirty = true; self->dirty = true;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@@ -395,8 +398,14 @@ default_color_table(PyObject *self UNUSED, PyObject *args UNUSED) {
// Boilerplate {{{ // Boilerplate {{{
#define CGETSET(name) \ #define CGETSET(name) \
static PyObject* name##_get(ColorProfile *self, void UNUSED *closure) { return PyLong_FromUnsignedLong(colorprofile_to_color(self, self->overridden.name, self->configured.name)); } \ static PyObject* name##_get(ColorProfile *self, void UNUSED *closure) { return PyLong_FromUnsignedLong(colorprofile_to_color(self, self->overridden.name, self->configured.name).rgb); } \
static int name##_set(ColorProfile *self, PyObject *val, void UNUSED *closure) { if (val == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete attribute"); return -1; } self->overridden.name = (color_type) PyLong_AsUnsignedLong(val); self->dirty = true; return 0; } static int name##_set(ColorProfile *self, PyObject *v, void UNUSED *closure) { \
if (v == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete attribute: " #name); return -1; } \
unsigned long val = PyLong_AsUnsignedLong(v); \
self->overridden.name.rgb = val & 0xffffff; \
self->overridden.name.type = (val & 0xff000000) ? COLOR_IS_RGB : COLOR_NOT_SET; \
self->dirty = true; return 0; \
}
CGETSET(default_fg) CGETSET(default_fg)
CGETSET(default_bg) CGETSET(default_bg)

View File

@@ -239,16 +239,27 @@ typedef struct {
bool is_visible, is_focused; bool is_visible, is_focused;
CursorShape shape; CursorShape shape;
unsigned int x, y; unsigned int x, y;
color_type color;
} CursorRenderInfo; } CursorRenderInfo;
typedef struct { typedef enum DynamicColorType {
color_type default_fg, default_bg, cursor_color, cursor_text_color, cursor_text_uses_bg, highlight_fg, highlight_bg; COLOR_NOT_SET, COLOR_IS_SPECIAL, COLOR_IS_INDEX, COLOR_IS_RGB
} DynamicColorType;
typedef union DynamicColor {
struct {
color_type rgb: 24;
DynamicColorType type: 8;
};
color_type val;
} DynamicColor; } DynamicColor;
typedef struct {
DynamicColor default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg;
} DynamicColors;
typedef struct { typedef struct {
DynamicColor dynamic_colors; DynamicColors dynamic_colors;
uint32_t color_table[256]; uint32_t color_table[256];
} ColorStackEntry; } ColorStackEntry;
@@ -260,7 +271,7 @@ typedef struct {
uint32_t orig_color_table[256]; uint32_t orig_color_table[256];
ColorStackEntry *color_stack; ColorStackEntry *color_stack;
unsigned int color_stack_idx, color_stack_sz; unsigned int color_stack_idx, color_stack_sz;
DynamicColor configured, overridden; DynamicColors configured, overridden;
color_type mark_foregrounds[MARK_MASK+1], mark_backgrounds[MARK_MASK+1]; color_type mark_foregrounds[MARK_MASK+1], mark_backgrounds[MARK_MASK+1];
} ColorProfile; } ColorProfile;
@@ -332,8 +343,9 @@ bool schedule_write_to_child(unsigned long id, unsigned int num, ...);
bool schedule_write_to_child_python(unsigned long id, const char *prefix, PyObject* tuple_of_str_or_bytes, const char *suffix); bool schedule_write_to_child_python(unsigned long id, const char *prefix, PyObject* tuple_of_str_or_bytes, const char *suffix);
bool set_iutf8(int, bool); bool set_iutf8(int, bool);
color_type colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval); DynamicColor colorprofile_to_color(ColorProfile *self, DynamicColor entry, DynamicColor defval);
float cursor_text_as_bg(ColorProfile *self); color_type
colorprofile_to_color_with_fallback(ColorProfile *self, DynamicColor entry, DynamicColor defval, DynamicColor fallback, DynamicColor falback_defval);
void copy_color_table_to_buffer(ColorProfile *self, color_type *address, int offset, size_t stride); void copy_color_table_to_buffer(ColorProfile *self, color_type *address, int offset, size_t stride);
bool colorprofile_push_colors(ColorProfile*, unsigned int); bool colorprofile_push_colors(ColorProfile*, unsigned int);
bool colorprofile_pop_colors(ColorProfile*, unsigned int); bool colorprofile_pop_colors(ColorProfile*, unsigned int);

View File

@@ -642,7 +642,7 @@ class ColorProfile:
pass pass
def set_configured_colors( def set_configured_colors(
self, fg: int, bg: int, cursor: int = 0, cursor_text: int = 0, cursor_text_uses_bg: int = 0, highlight_fg: int = 0, highlight_bg: int = 0 self, fg: int, bg: int, cursor: int = 0, cursor_text: int = 0, highlight_fg: int = 0, highlight_bg: int = 0
) -> None: ) -> None:
pass pass

View File

@@ -162,7 +162,7 @@ blank_os_window(OSWindow *w) {
Window *w = t->windows + t->active_window; Window *w = t->windows + t->active_window;
Screen *s = w->render_data.screen; Screen *s = w->render_data.screen;
if (s) { if (s) {
color = colorprofile_to_color(s->color_profile, s->color_profile->overridden.default_bg, s->color_profile->configured.default_bg); color = colorprofile_to_color(s->color_profile, s->color_profile->overridden.default_bg, s->color_profile->configured.default_bg).rgb;
} }
} }
} }

View File

@@ -213,8 +213,14 @@ egr() # }}}
agr('cursor', 'Cursor customization') agr('cursor', 'Cursor customization')
opt('cursor', '#cccccc', opt('cursor', '#cccccc',
option_type='to_color', option_type='to_color_or_none', long_text='''
long_text='Default cursor color' Default cursor color. If set to the special value :code:`none` the cursor will be
rendered with a "reverse video" effect. It's color will be the color of the text in
the cell it is over and the text will be rendered with the background color of the cell.
Note that if the program running in the terminal sets a cursor color, this takes precedence.
Also, the reverse video effect is not applied if the cell's foreground and background colors
are the same.
'''
) )
opt('cursor_text_color', '#111111', opt('cursor_text_color', '#111111',
@@ -222,7 +228,7 @@ opt('cursor_text_color', '#111111',
long_text=''' long_text='''
Choose the color of text under the cursor. If you want it rendered with the Choose the color of text under the cursor. If you want it rendered with the
background color of the cell underneath instead, use the special keyword: background color of the cell underneath instead, use the special keyword:
background background.
''' '''
) )

View File

@@ -888,7 +888,7 @@ class Parser:
ans['copy_on_select'] = copy_on_select(val) ans['copy_on_select'] = copy_on_select(val)
def cursor(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: def cursor(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['cursor'] = to_color(val) ans['cursor'] = to_color_or_none(val)
def cursor_beam_thickness(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: def cursor_beam_thickness(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['cursor_beam_thickness'] = positive_float(val) ans['cursor_beam_thickness'] = positive_float(val)

View File

@@ -27,7 +27,6 @@ color_or_none_as_int(PyObject *color) {
return color_as_int(color); return color_as_int(color);
} }
static inline color_type static inline color_type
active_border_color(PyObject *color) { active_border_color(PyObject *color) {
if (color == Py_None) return 0x00ff00; if (color == Py_None) return 0x00ff00;

View File

@@ -472,7 +472,7 @@ class Options:
command_on_bell: typing.List[str] = ['none'] command_on_bell: typing.List[str] = ['none']
confirm_os_window_close: int = 0 confirm_os_window_close: int = 0
copy_on_select: str = '' copy_on_select: str = ''
cursor: Color = Color(red=204, green=204, blue=204) cursor: typing.Optional[kitty.rgb.Color] = Color(red=204, green=204, blue=204)
cursor_beam_thickness: float = 1.5 cursor_beam_thickness: float = 1.5
cursor_blink_interval: float = -1.0 cursor_blink_interval: float = -1.0
cursor_shape: int = 1 cursor_shape: int = 1

View File

@@ -18,7 +18,7 @@ if TYPE_CHECKING:
from kitty.cli_stub import SetColorsRCOptions as CLIOptions from kitty.cli_stub import SetColorsRCOptions as CLIOptions
nullable_colors = ('cursor_text_color', 'tab_bar_background', 'tab_bar_margin_color') nullable_colors = ('cursor', 'cursor_text_color', 'tab_bar_background', 'tab_bar_margin_color', 'selection_foreground')
def parse_colors(args: Iterable[str]) -> Dict[str, Optional[int]]: def parse_colors(args: Iterable[str]) -> Dict[str, Optional[int]]:

View File

@@ -168,7 +168,7 @@ screen_reset(Screen *self) {
self->modes = empty_modes; self->modes = empty_modes;
self->saved_modes = empty_modes; self->saved_modes = empty_modes;
self->active_hyperlink_id = 0; self->active_hyperlink_id = 0;
#define R(name) self->color_profile->overridden.name = 0 #define R(name) self->color_profile->overridden.name.val = 0
R(default_fg); R(default_bg); R(cursor_color); R(highlight_fg); R(highlight_bg); R(default_fg); R(default_bg); R(cursor_color); R(highlight_fg); R(highlight_bg);
#undef R #undef R
RESET_CHARSETS; RESET_CHARSETS;
@@ -1509,6 +1509,28 @@ screen_fake_move_cursor_to_position(Screen *self, index_type x, index_type y) {
return count > 0; return count > 0;
} }
static color_type
resolve_color(ColorProfile *cp, color_type val, color_type defval) {
switch(val & 0xff) {
case 1:
return cp->color_table[(val >> 8) & 0xff];
case 2:
return val >> 8;
default:
return defval;
}
}
bool
resolve_cell_colors(Screen *self, index_type x, index_type y, color_type *fg, color_type *bg, color_type default_fg, color_type default_bg) {
if (x < self->columns && y < self->lines) {
linebuf_init_line(self->linebuf, y);
GPUCell *g = self->linebuf->line->gpu_cells + x;
*fg = resolve_color(self->color_profile, g->fg, default_fg);
*bg = resolve_color(self->color_profile, g->bg, default_bg);
return true;
} else { *fg = 0; *bg = 0; return false; }
}
// }}} // }}}
// Editing {{{ // Editing {{{

View File

@@ -249,6 +249,7 @@ void screen_report_key_encoding_flags(Screen *self);
bool screen_detect_url(Screen *screen, unsigned int x, unsigned int y); bool screen_detect_url(Screen *screen, unsigned int x, unsigned int y);
int screen_cursor_at_a_shell_prompt(const Screen *); int screen_cursor_at_a_shell_prompt(const Screen *);
bool screen_fake_move_cursor_to_position(Screen *, index_type x, index_type y); bool screen_fake_move_cursor_to_position(Screen *, index_type x, index_type y);
bool resolve_cell_colors(Screen *self, index_type x, index_type y, color_type *fg, color_type *bg, color_type default_fg, color_type default_bg);
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen); #define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);
DECLARE_CH_SCREEN_HANDLER(bell) DECLARE_CH_SCREEN_HANDLER(bell)
DECLARE_CH_SCREEN_HANDLER(backspace) DECLARE_CH_SCREEN_HANDLER(backspace)

View File

@@ -260,9 +260,9 @@ send_graphics_data_to_gpu(size_t image_count, ssize_t gvao_idx, const ImageRende
static void static void
cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor, bool inverted, OSWindow *os_window) { cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor, bool inverted, OSWindow *os_window) {
struct CellRenderData { struct CellRenderData {
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, cursor_text_uses_bg; GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, cursor_text_color, url_color, url_style, inverted; GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
GLuint xnum, ynum, cursor_fg_sprite_idx; GLuint xnum, ynum, cursor_fg_sprite_idx;
GLfloat cursor_x, cursor_y, cursor_w; GLfloat cursor_x, cursor_y, cursor_w;
@@ -274,6 +274,9 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
if (UNLIKELY(screen->color_profile->dirty || screen->reload_all_gpu_data)) { if (UNLIKELY(screen->color_profile->dirty || screen->reload_all_gpu_data)) {
copy_color_table_to_buffer(screen->color_profile, (GLuint*)rd, cell_program_layouts[CELL_PROGRAM].color_table.offset / sizeof(GLuint), cell_program_layouts[CELL_PROGRAM].color_table.stride / sizeof(GLuint)); copy_color_table_to_buffer(screen->color_profile, (GLuint*)rd, cell_program_layouts[CELL_PROGRAM].color_table.offset / sizeof(GLuint), cell_program_layouts[CELL_PROGRAM].color_table.stride / sizeof(GLuint));
} }
#define COLOR(name) colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name).rgb
#define IS_SPECIAL_COLOR(name) (screen->color_profile->overridden.name.type == COLOR_IS_SPECIAL || (screen->color_profile->overridden.name.type == COLOR_NOT_SET && screen->color_profile->configured.name.type == COLOR_IS_SPECIAL))
rd->default_fg = COLOR(default_fg); rd->default_bg = COLOR(default_bg); rd->highlight_fg = COLOR(highlight_fg); rd->highlight_bg = COLOR(highlight_bg);
// Cursor position // Cursor position
enum { BLOCK_IDX = 0, BEAM_IDX = 6, UNDERLINE_IDX = 7, UNFOCUSED_IDX = 8 }; enum { BLOCK_IDX = 0, BEAM_IDX = 6, UNDERLINE_IDX = 7, UNFOCUSED_IDX = 8 };
if (cursor->is_visible) { if (cursor->is_visible) {
@@ -288,6 +291,21 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
rd->cursor_fg_sprite_idx = UNDERLINE_IDX; break; rd->cursor_fg_sprite_idx = UNDERLINE_IDX; break;
} }
} else rd->cursor_fg_sprite_idx = UNFOCUSED_IDX; } else rd->cursor_fg_sprite_idx = UNFOCUSED_IDX;
color_type cell_fg, cell_bg;
resolve_cell_colors(screen, screen->cursor->x, screen->cursor->y, &cell_fg, &cell_bg, rd->default_fg, rd->default_bg);
if (screen->color_profile->overridden.cursor_color.type == COLOR_IS_INDEX || screen->color_profile->overridden.cursor_color.type == COLOR_IS_RGB) {
// since the program is controlling the cursor color we hope it has chosen one
// that has good contrast with the text color of the cell
rd->cursor_fg = cell_fg; rd->cursor_bg = COLOR(cursor_color);
} else if (IS_SPECIAL_COLOR(cursor_color)) {
if (cell_bg == cell_fg) {
rd->cursor_fg = rd->default_bg; rd->cursor_bg = rd->default_fg;
} else { rd->cursor_fg = cell_bg; rd->cursor_bg = cell_fg; }
} else {
rd->cursor_bg = COLOR(cursor_color);
if (IS_SPECIAL_COLOR(cursor_text_color)) rd->cursor_fg = cell_bg;
else rd->cursor_fg = COLOR(cursor_text_color);
}
} else rd->cursor_x = screen->columns, rd->cursor_y = screen->lines; } else rd->cursor_x = screen->columns, rd->cursor_y = screen->lines;
rd->cursor_w = rd->cursor_x; rd->cursor_w = rd->cursor_x;
if ( if (
@@ -304,12 +322,9 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
rd->inverted = inverted ? 1 : 0; rd->inverted = inverted ? 1 : 0;
rd->background_opacity = os_window->is_semi_transparent ? os_window->background_opacity : 1.0f; rd->background_opacity = os_window->is_semi_transparent ? os_window->background_opacity : 1.0f;
#define COLOR(name) colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name)
rd->default_fg = COLOR(default_fg); rd->default_bg = COLOR(default_bg); rd->highlight_fg = COLOR(highlight_fg); rd->highlight_bg = COLOR(highlight_bg);
rd->cursor_text_color = COLOR(cursor_text_color);
#undef COLOR #undef COLOR
rd->cursor_color = cursor->color; rd->url_color = OPT(url_color); rd->url_style = OPT(url_style); #undef IS_SPECIAL_COLOR
rd->cursor_text_uses_bg = cursor_text_as_bg(screen->color_profile); rd->url_color = OPT(url_color); rd->url_style = OPT(url_style);
unmap_vao_buffer(vao_idx, uniform_buffer); rd = NULL; unmap_vao_buffer(vao_idx, uniform_buffer); rd = NULL;
} }
@@ -456,7 +471,7 @@ has_bgimage(OSWindow *w) {
static void static void
draw_tint(bool premult, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat width, GLfloat height) { draw_tint(bool premult, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat width, GLfloat height) {
bind_program(TINT_PROGRAM); bind_program(TINT_PROGRAM);
color_type window_bg = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.default_bg, screen->color_profile->configured.default_bg); color_type window_bg = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.default_bg, screen->color_profile->configured.default_bg).rgb;
#define C(shift) ((((GLfloat)((window_bg >> shift) & 0xFF)) / 255.0f)) #define C(shift) ((((GLfloat)((window_bg >> shift) & 0xFF)) / 255.0f))
float alpha = OPT(background_tint); float alpha = OPT(background_tint);
if (premult) glUniform4f(tint_program_layout.tint_color_location, C(16) * alpha, C(8) * alpha, C(0) * alpha, alpha); if (premult) glUniform4f(tint_program_layout.tint_color_location, C(16) * alpha, C(8) * alpha, C(0) * alpha, alpha);
@@ -506,8 +521,8 @@ render_window_title(OSWindow *os_window, Screen *screen UNUSED, GLfloat xstart,
static char title[2048] = {0}; static char title[2048] = {0};
if (window->title_bar_data.last_drawn_title_object_id != window->title) { if (window->title_bar_data.last_drawn_title_object_id != window->title) {
snprintf(title, arraysz(title), " %s", PyUnicode_AsUTF8(window->title)); snprintf(title, arraysz(title), " %s", PyUnicode_AsUTF8(window->title));
#define RGBCOL(which) ( 0xff000000 | colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.which, screen->color_profile->configured.which) ) #define RGBCOL(which, fallback) ( 0xff000000 | colorprofile_to_color_with_fallback(screen->color_profile, screen->color_profile->overridden.which, screen->color_profile->configured.which, screen->color_profile->overridden.fallback, screen->color_profile->configured.fallback))
if (!draw_window_title(os_window, title, RGBCOL(highlight_fg), RGBCOL(highlight_bg), window->title_bar_data.buf, bar_width, bar_height)) return 0; if (!draw_window_title(os_window, title, RGBCOL(highlight_fg, default_fg), RGBCOL(highlight_bg, default_bg), window->title_bar_data.buf, bar_width, bar_height)) return 0;
#undef RGBCOL #undef RGBCOL
window->title_bar_data.last_drawn_title_object_id = window->title; window->title_bar_data.last_drawn_title_object_id = window->title;
} }
@@ -550,7 +565,7 @@ draw_window_number(OSWindow *os_window, Screen *screen, GLfloat xstart, GLfloat
bind_program(SEVEN_SEGMENT_PROGRAM); bind_program(SEVEN_SEGMENT_PROGRAM);
glUniform4f(seven_segment_program_layout.edges_location, xstart, ystart - height, xstart + width, ystart); glUniform4f(seven_segment_program_layout.edges_location, xstart, ystart - height, xstart + width, ystart);
glUniform4f(seven_segment_program_layout.area_bounds_location, left, top, right - left, bottom - top); glUniform4f(seven_segment_program_layout.area_bounds_location, left, top, right - left, bottom - top);
color_type digit_color = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg); color_type digit_color = colorprofile_to_color_with_fallback(screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg, screen->color_profile->overridden.default_fg, screen->color_profile->configured.default_fg);
#define C(shift) ((((GLfloat)((digit_color >> shift) & 0xFF)) / 255.0f)) #define C(shift) ((((GLfloat)((digit_color >> shift) & 0xFF)) / 255.0f))
glUniform4f(seven_segment_program_layout.digit_color_location, C(16), C(8), C(0), 1.); glUniform4f(seven_segment_program_layout.digit_color_location, C(16), C(8), C(0), 1.);
#undef C #undef C
@@ -566,7 +581,8 @@ draw_visual_bell_flash(GLfloat intensity, GLfloat xstart, GLfloat ystart, GLfloa
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
bind_program(TINT_PROGRAM); bind_program(TINT_PROGRAM);
GLfloat attenuation = 0.4f; GLfloat attenuation = 0.4f;
const color_type flash = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg); const color_type flash = colorprofile_to_color_with_fallback(
screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg, screen->color_profile->overridden.default_fg, screen->color_profile->configured.default_fg);
#define C(shift) ((((GLfloat)((flash >> shift) & 0xFF)) / 255.0f) ) #define C(shift) ((((GLfloat)((flash >> shift) & 0xFF)) / 255.0f) )
const GLfloat r = C(16), g = C(8), b = C(0); const GLfloat r = C(16), g = C(8), b = C(0);
const GLfloat max_channel = r > g ? (r > b ? r : b) : (g > b ? g : b); const GLfloat max_channel = r > g ? (r > b ? r : b) : (g > b ? g : b);

View File

@@ -36,13 +36,13 @@ from .fast_data_types import (
from .keys import keyboard_mode_name, mod_mask from .keys import keyboard_mode_name, mod_mask
from .notify import NotificationCommand, handle_notification_cmd from .notify import NotificationCommand, handle_notification_cmd
from .options.types import Options from .options.types import Options
from .rgb import to_color from .rgb import Color, to_color
from .terminfo import get_capabilities from .terminfo import get_capabilities
from .types import MouseEvent, ScreenGeometry, WindowGeometry, ac from .types import MouseEvent, ScreenGeometry, WindowGeometry, ac
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
from .utils import ( from .utils import (
color_as_int, get_primary_selection, load_shaders, log_error, open_cmd, get_primary_selection, load_shaders, log_error, open_cmd, open_url,
open_url, parse_color_set, sanitize_title, set_primary_selection parse_color_set, sanitize_title, set_primary_selection
) )
MatchPatternType = Union[Pattern[str], Tuple[Pattern[str], Optional[Pattern[str]]]] MatchPatternType = Union[Pattern[str], Tuple[Pattern[str], Optional[Pattern[str]]]]
@@ -264,14 +264,14 @@ load_shader_programs = LoadShaderPrograms()
def setup_colors(screen: Screen, opts: Options) -> None: def setup_colors(screen: Screen, opts: Options) -> None:
screen.color_profile.update_ansi_color_table(build_ansi_color_table(opts)) screen.color_profile.update_ansi_color_table(build_ansi_color_table(opts))
cursor_text_color = opts.cursor_text_color or (12, 12, 12)
cursor_text_color_as_bg = 3 if opts.cursor_text_color is None else 1 def s(c: Optional[Color]) -> int:
sfg = (0, 0, 0) if opts.selection_foreground is None else opts.selection_foreground return 0 if c is None else (0xff000000 | int(c))
screen.color_profile.set_configured_colors(*map(color_as_int, ( screen.color_profile.set_configured_colors(
opts.foreground, opts.background, opts.cursor, s(opts.foreground), s(opts.background),
cursor_text_color, (0, 0, cursor_text_color_as_bg), s(opts.cursor), s(opts.cursor_text_color),
sfg, opts.selection_background) s(opts.selection_foreground), s(opts.selection_background)
)) )
def text_sanitizer(as_ansi: bool, add_wrap_markers: bool) -> Callable[[str], str]: def text_sanitizer(as_ansi: bool, add_wrap_markers: bool) -> Callable[[str], str]:
@@ -762,16 +762,16 @@ class Window:
def change_colors(self, changes: Dict[DynamicColor, Optional[str]]) -> None: def change_colors(self, changes: Dict[DynamicColor, Optional[str]]) -> None:
dirtied = default_bg_changed = False dirtied = default_bg_changed = False
def item(raw: Optional[str]) -> Optional[int]: def item(raw: Optional[str]) -> int:
if raw is None: if raw is None:
return 0 return 0
val = to_color(raw) v = to_color(raw)
return None if val is None else (color_as_int(val) << 8) | 2 if v is None:
return 0
return 0xff000000 | int(v)
for which, val_ in changes.items(): for which, val_ in changes.items():
val = item(val_) val = item(val_)
if val is None:
continue
dirtied = True dirtied = True
setattr(self.screen.color_profile, which.name, val) setattr(self.screen.color_profile, which.name, val)
if which.name == 'default_bg': if which.name == 'default_bg':