mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-16 05:27:50 +02:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c127517c96 | ||
|
|
f8bdc3d86b | ||
|
|
6834e366be | ||
|
|
866c14249b | ||
|
|
271665dbaf | ||
|
|
0a7812ab20 | ||
|
|
6f93fb22a4 | ||
|
|
f48bcb1a17 | ||
|
|
216b6e2e8c | ||
|
|
cb13233606 | ||
|
|
b854589761 | ||
|
|
44039baa39 | ||
|
|
100f472a08 | ||
|
|
126fca0224 | ||
|
|
dc9eefe050 | ||
|
|
c1fb18a6ef | ||
|
|
c3130419a7 | ||
|
|
dbfeb8d6a4 | ||
|
|
6ca187c42c | ||
|
|
e78e86572e | ||
|
|
d02b1c0b31 | ||
|
|
83c989d4e9 | ||
|
|
958ad0d8b4 |
@@ -74,9 +74,21 @@ consumption to do the same tasks.
|
||||
Detailed list of changes
|
||||
-------------------------------------
|
||||
|
||||
0.36.3 [future]
|
||||
0.36.4 [2024-09-27]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Fix a regression in the previous release that caused window padding to be rendered opaque even when :opt:`background_opacity` is less than 1 (:iss:`7895`)
|
||||
|
||||
- Wayland GNOME: Fix a crash when using multiple monitors with different scales and starting on or moving to the monitor with lower scale (:iss:`7894`)
|
||||
|
||||
- macOS: Fix a regression in the previous release that caused junk to be rendered in font previews in the choose fonts kitten and crash on Intel macs (:iss:`7892`)
|
||||
|
||||
|
||||
0.36.3 [2024-09-25]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- The option ``second_transparent_bg`` has been removed and replaced by :opt:`transparent_background_colors` which allows setting up to seven additional colors that will be transparent, with individual opacities per color (:iss:`7646`)
|
||||
|
||||
- Fix a regression in the previous release that broke use of the ``cd`` command in session files (:iss:`7829`)
|
||||
|
||||
- macOS: Fix shortcuts that become entries in the global menubar being reported as removed shortcuts in the debug output
|
||||
@@ -91,6 +103,10 @@ Detailed list of changes
|
||||
|
||||
- kitten @ ls: Fix the ``--self`` flag not working (:iss:`7864`)
|
||||
|
||||
- Remote control: Fix ``--match state:self`` not working (:disc:`7886`)
|
||||
|
||||
- Splits layout: Allow setting the ``split_axis`` option to ``auto`` so that all new windows have their split axis chosen automatically unless explicitly specified in the launch command (:iss:`7887`)
|
||||
|
||||
0.36.2 [2024-09-06]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -132,7 +148,7 @@ Detailed list of changes
|
||||
|
||||
- launch command: A new :option:`launch --bias` option to adjust the size of newly created windows declaratively (:iss:`7634`)
|
||||
|
||||
- A new option :opt:`second_transparent_bg` to make a second background color semi-transparent via :opt:`background_opacity`. Useful for things like cursor line highlight in editors (:iss:`7646`)
|
||||
- A new option :opt:`transparent_background_colors` to make a second background color semi-transparent via :opt:`background_opacity`. Useful for things like cursor line highlight in editors (:iss:`7646`)
|
||||
|
||||
- A new :doc:`notify </kittens/notify>` kitten to show desktop notifications
|
||||
from the command line with support for icons, buttons and more.
|
||||
|
||||
@@ -69,8 +69,11 @@ selection_foreground The foreground color of selections
|
||||
cursor The color of the text cursor Foreground color
|
||||
cursor_text The color of text under the cursor Background color
|
||||
visual_bell The color of a visual bell Automatic color selection based on current screen colors
|
||||
second_transparent_background A color that might be rendered semi-transparent No second color is made transparent
|
||||
in addition to the default background color
|
||||
transparent_background_color1..8 A background color that is rendered Unset
|
||||
with the specified opacity in cells that have
|
||||
the specified background color. An opacity
|
||||
value less than zero means, use the
|
||||
:opt:`background_opacity` value.
|
||||
================================= =============================================== ===============================
|
||||
|
||||
In this table the third column shows what effect setting the color to *dynamic*
|
||||
@@ -160,8 +163,19 @@ RGB colors are encoded in one of three forms:
|
||||
|
||||
``rgbi:<red>/<green>/<blue>``
|
||||
red, green, and blue are floating-point values between 0.0 and 1.0, inclusive. The input format for these values is an optional
|
||||
sign, a string of numbers possibly containing a decimal point, and an optional exponent field containing an E or e followed by a possibly
|
||||
signed integer string.
|
||||
sign, a string of numbers possibly containing a decimal point, and an optional exponent field containing an E or e followed by a possibly
|
||||
signed integer string. Values outside the ``0 - 1`` range must be clipped to be within the range.
|
||||
|
||||
If a color should have an alpha component, it must be suffixed to the color
|
||||
specification in the form :code:`@number between zero and one`. For example::
|
||||
|
||||
red@0.5 rgb:ff0000@0.1 #ff0000@0.3
|
||||
|
||||
The syntax for the floating point alpha component is the same as used for the
|
||||
components of ``rgbi`` defined above. When not specified, the default alpha
|
||||
value is ``1.0``. Values outside the range ``0 - 1`` must be clipped
|
||||
to be within the range, negative values may have special context dependent
|
||||
meaning.
|
||||
|
||||
In addition, the following color names are accepted (case-insensitively) corresponding to the
|
||||
specified RGB values.
|
||||
|
||||
@@ -184,11 +184,13 @@ in a split using the ``rotate`` action with an argument of ``180`` and rotate
|
||||
and swap with an argument of ``270``.
|
||||
|
||||
This layout takes one option, ``split_axis`` that controls whether new windows
|
||||
are placed into vertical or horizontal splits when a :option:`--location <launch
|
||||
--location>` is not specified. A value of ``horizontal`` (same as
|
||||
``--location=vsplit``) means when a new split is created the two windows will be
|
||||
placed side by side and a value of ``vertical`` (same as ``--location=hsplit``)
|
||||
means the two windows will be placed one on top of the other. By default::
|
||||
are placed into vertical or horizontal splits when a :option:`--location
|
||||
<launch --location>` is not specified. A value of ``horizontal`` (same as
|
||||
``--location=vsplit``) means when a new split is created the two windows will
|
||||
be placed side by side and a value of ``vertical`` (same as
|
||||
``--location=hsplit``) means the two windows will be placed one on top of the
|
||||
other. A value of ``auto`` means the axis of the split is chosen automatically
|
||||
(same as ``--location=split``). By default::
|
||||
|
||||
enabled_layouts splits:split_axis=horizontal
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
// It is fine to use C99 in this file because it will not be built with VS
|
||||
//========================================================================
|
||||
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
// It is fine to use C99 in this file because it will not be built with VS
|
||||
//========================================================================
|
||||
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#include "../kitty/monotonic.h"
|
||||
#include "glfw3.h"
|
||||
#include "internal.h"
|
||||
|
||||
11
glfw/wl_client_side_decorations.c
vendored
11
glfw/wl_client_side_decorations.c
vendored
@@ -468,10 +468,8 @@ render_shadows(_GLFWwindow *window) {
|
||||
|
||||
static bool
|
||||
create_shm_buffers(_GLFWwindow* window) {
|
||||
const double scale = _glfwWaylandWindowScale(window);
|
||||
|
||||
decs.mapping.size = 0;
|
||||
#define bp(which, width, height) decs.mapping.size += init_buffer_pair(&decs.which.buffer, width, height, scale);
|
||||
#define bp(which, width, height) decs.mapping.size += init_buffer_pair(&decs.which.buffer, width, height, decs.for_window_state.fscale);
|
||||
bp(titlebar, window->wl.width, decs.metrics.visible_titlebar_height);
|
||||
bp(shadow_top, window->wl.width, decs.metrics.width);
|
||||
bp(shadow_bottom, window->wl.width, decs.metrics.width);
|
||||
@@ -504,7 +502,7 @@ create_shm_buffers(_GLFWwindow* window) {
|
||||
wl_shm_pool_destroy(pool);
|
||||
render_title_bar(window, true);
|
||||
render_shadows(window);
|
||||
debug("Created decoration buffers at scale: %f\n", scale);
|
||||
debug("Created decoration buffers at scale: %f\n", decs.for_window_state.fscale);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -577,10 +575,11 @@ ensure_csd_resources(_GLFWwindow *window) {
|
||||
if (!window_is_csd_capable(window)) return false;
|
||||
const bool is_focused = window->id == _glfw.focusedWindowId;
|
||||
const bool focus_changed = is_focused != decs.for_window_state.focused;
|
||||
const double current_scale = _glfwWaylandWindowScale(window);
|
||||
const bool size_changed = (
|
||||
decs.for_window_state.width != window->wl.width ||
|
||||
decs.for_window_state.height != window->wl.height ||
|
||||
decs.for_window_state.fscale != _glfwWaylandWindowScale(window) ||
|
||||
decs.for_window_state.fscale != current_scale ||
|
||||
!decs.mapping.data
|
||||
);
|
||||
const bool state_changed = decs.for_window_state.toplevel_states != window->wl.current.toplevel_states;
|
||||
@@ -589,6 +588,7 @@ ensure_csd_resources(_GLFWwindow *window) {
|
||||
decs.for_window_state.width, decs.for_window_state.height, window->wl.width, window->wl.height, needs_update,
|
||||
size_changed, state_changed, decs.buffer_destroyed);
|
||||
if (!needs_update) return false;
|
||||
decs.for_window_state.fscale = current_scale; // used in create_shm_buffers
|
||||
if (size_changed || decs.buffer_destroyed) {
|
||||
free_csd_buffers(window);
|
||||
if (!create_shm_buffers(window)) return false;
|
||||
@@ -618,7 +618,6 @@ ensure_csd_resources(_GLFWwindow *window) {
|
||||
|
||||
decs.for_window_state.width = window->wl.width;
|
||||
decs.for_window_state.height = window->wl.height;
|
||||
decs.for_window_state.fscale = _glfwWaylandWindowScale(window);
|
||||
decs.for_window_state.focused = is_focused;
|
||||
decs.for_window_state.toplevel_states = window->wl.current.toplevel_states;
|
||||
return true;
|
||||
|
||||
@@ -65,6 +65,9 @@ func (k *kitty_font_backend_type) start() (err error) {
|
||||
var kitty_font_backend kitty_font_backend_type
|
||||
|
||||
func (k *kitty_font_backend_type) send(v any) error {
|
||||
if k.to == nil {
|
||||
return fmt.Errorf("Trying to send data when to pipe is nil")
|
||||
}
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not encode message to kitty with error: %w", err)
|
||||
|
||||
@@ -92,7 +92,8 @@ available or broken, using an alternate interpreter can be useful.
|
||||
|
||||
opt('remote_dir', '.local/share/kitty-ssh-kitten', long_text='''
|
||||
The location on the remote host where the files needed for this kitten are
|
||||
installed. Relative paths are resolved with respect to :code:`$HOME`.
|
||||
installed. Relative paths are resolved with respect to :code:`$HOME`. Absolute
|
||||
paths have their leading / removed and so are also resolved with respect to $HOME.
|
||||
''')
|
||||
|
||||
opt('+copy', '', add_to_default=False, ctype='CopyInstruction', long_text=f'''
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
uniform uvec2 viewport;
|
||||
uniform uint colors[9];
|
||||
uniform float background_opacity;
|
||||
uniform float tint_opacity, tint_premult;
|
||||
uniform float gamma_lut[256];
|
||||
in vec4 rect; // left, top, right, bottom
|
||||
@@ -41,7 +42,11 @@ void main() {
|
||||
float is_window_bg = is_integer_value(rc, 3.);
|
||||
float is_default_bg = is_integer_value(rc, 0.);
|
||||
color3 = is_window_bg * window_bg + (1. - is_window_bg) * color3;
|
||||
float final_opacity = is_default_bg * tint_opacity + (1. - is_default_bg);
|
||||
float final_premult_opacity = is_default_bg * tint_premult + (1. - is_default_bg);
|
||||
// Border must be always drawn opaque
|
||||
float is_border_bg = 1. - step(0.5, abs((float(rc) - 2.) * (float(rc) - 1.) * (float(rc) - 4.))); // 1 if rc in (1, 2, 4) else 0
|
||||
float final_opacity = is_default_bg * tint_opacity + (1. - is_default_bg) * background_opacity;
|
||||
final_opacity = is_border_bg + (1. - is_border_bg) * final_opacity;
|
||||
float final_premult_opacity = is_default_bg * tint_premult + (1. - is_default_bg) * background_opacity;
|
||||
final_premult_opacity = is_border_bg + (1. - is_border_bg) * final_premult_opacity;
|
||||
color = vec4(color3 * final_premult_opacity, final_opacity);
|
||||
}
|
||||
|
||||
@@ -2617,7 +2617,7 @@ class Boss:
|
||||
window.screen.disable_ligatures = strategy
|
||||
window.refresh()
|
||||
|
||||
def patch_colors(self, spec: dict[str, Optional[int]], configured: bool = False) -> None:
|
||||
def patch_colors(self, spec: dict[str, Optional[int]], transparent_background_colors: tuple[tuple[Color, float], ...], configured: bool = False) -> None:
|
||||
opts = get_options()
|
||||
if configured:
|
||||
for k, v in spec.items():
|
||||
@@ -2627,6 +2627,7 @@ class Boss:
|
||||
setattr(opts, k, None)
|
||||
else:
|
||||
setattr(opts, k, color_from_int(v))
|
||||
opts.transparent_background_colors = transparent_background_colors
|
||||
for tm in self.all_tab_managers:
|
||||
tm.tab_bar.patch_colors(spec)
|
||||
tm.tab_bar.layout()
|
||||
|
||||
@@ -187,7 +187,7 @@ void main() {
|
||||
#ifdef TRANSPARENT
|
||||
final_color = vec4_premul(background, bg_alpha);
|
||||
#else
|
||||
final_color = vec4(background, draw_bg);
|
||||
final_color = vec4(background, draw_bg * bg_alpha);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -4,13 +4,16 @@
|
||||
|
||||
// Inputs {{{
|
||||
layout(std140) uniform CellRenderData {
|
||||
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg;
|
||||
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg;
|
||||
|
||||
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted, second_transparent_bg;
|
||||
uint default_fg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
|
||||
|
||||
uint xnum, ynum, cursor_fg_sprite_idx;
|
||||
float cursor_x, cursor_y, cursor_w, cursor_opacity;
|
||||
|
||||
// must have unique entries with 0 being default_bg and unset being UINT32_MAX
|
||||
uint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7;
|
||||
float bg_opacities0, bg_opacities1, bg_opacities2, bg_opacities3, bg_opacities4, bg_opacities5, bg_opacities6, bg_opacities7;
|
||||
uint color_table[NUM_COLORS + MARK_MASK + MARK_MASK + 2];
|
||||
};
|
||||
#if (PHASE == PHASE_BACKGROUND)
|
||||
@@ -145,12 +148,30 @@ CellData set_vertex_position() {
|
||||
return CellData(has_cursor, has_cursor * is_block_cursor, pos);
|
||||
}
|
||||
|
||||
float background_opacity_for(uint bg, uint colorval, float opacity_if_matched) { // opacity_if_matched if bg == colorval else 1
|
||||
float not_matched = step(1.f, abs(float(colorval - bg))); // not_matched = 0 if bg == colorval else 1
|
||||
return not_matched + opacity_if_matched * (1.f - not_matched);
|
||||
}
|
||||
|
||||
float calc_background_opacity(uint bg) {
|
||||
return (
|
||||
background_opacity_for(bg, bg_colors0, bg_opacities0) *
|
||||
background_opacity_for(bg, bg_colors1, bg_opacities1) *
|
||||
background_opacity_for(bg, bg_colors2, bg_opacities2) *
|
||||
background_opacity_for(bg, bg_colors3, bg_opacities3) *
|
||||
background_opacity_for(bg, bg_colors4, bg_opacities4) *
|
||||
background_opacity_for(bg, bg_colors5, bg_opacities5) *
|
||||
background_opacity_for(bg, bg_colors6, bg_opacities6) *
|
||||
background_opacity_for(bg, bg_colors7, bg_opacities7)
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
CellData cell_data = set_vertex_position();
|
||||
|
||||
// set cell color indices {{{
|
||||
uvec2 default_colors = uvec2(default_fg, default_bg);
|
||||
uvec2 default_colors = uvec2(default_fg, bg_colors0);
|
||||
uint text_attrs = sprite_coords[3];
|
||||
uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & ONE);
|
||||
uint is_inverted = is_reversed + inverted;
|
||||
@@ -194,42 +215,34 @@ void main() {
|
||||
// }}}
|
||||
|
||||
// Background {{{
|
||||
float bg_is_not_transparent = step(1, float(abs(
|
||||
(bg_as_uint - default_colors[1]) * (bg_as_uint - second_transparent_bg)
|
||||
))); // bg_is_not_transparent = 0 if bg_as_uint in (default_colors[1], second_transparent_bg) else 1
|
||||
#if PHASE == PHASE_BOTH && !defined(TRANSPARENT) // fast case single pass opaque background
|
||||
bg_alpha = 1;
|
||||
draw_bg = 1;
|
||||
|
||||
#else
|
||||
bg_alpha = calc_background_opacity(bg_as_uint);
|
||||
#if (PHASE == PHASE_BACKGROUND)
|
||||
// draw_bg_bitfield has bit 0 set to draw default bg cells and bit 1 set to draw non-default bg cells
|
||||
uint draw_bg_mask = uint(2 * bg_is_not_transparent + (1 - bg_is_not_transparent));
|
||||
draw_bg = step(1, float(draw_bg_bitfield & draw_bg_mask));
|
||||
float cell_has_non_default_bg = step(1.f, abs(float(bg_as_uint - bg_colors0))); // 0 if has default bg else 1
|
||||
uint draw_bg_mask = uint(2.f * cell_has_non_default_bg + (1.f - cell_has_non_default_bg)); // 1 if has default bg else 2
|
||||
draw_bg = step(0.5, float(draw_bg_bitfield & draw_bg_mask));
|
||||
#else
|
||||
draw_bg = 1;
|
||||
#endif
|
||||
|
||||
bg_alpha = 1.f;
|
||||
#ifdef TRANSPARENT
|
||||
// Set bg_alpha to background_opacity on cells that have the default background color
|
||||
// Which means they must not have a block cursor or a selection or reverse video
|
||||
// On other cells it should be 1. For the SPECIAL program it should be 1 on cells with
|
||||
// selections/block cursor and 0 everywhere else.
|
||||
float is_special_cell = cell_data.has_block_cursor + float(is_selected & ONE);
|
||||
#if (PHASE != PHASE_SPECIAL)
|
||||
is_special_cell += bg_is_not_transparent + float(is_reversed);
|
||||
#endif
|
||||
#if PHASE == PHASE_SPECIAL
|
||||
// Only special cells must be drawn and they must have bg_alpha 1
|
||||
bg_alpha = step(0.5, is_special_cell); // bg_alpha = 1 if is_special_cell else 0
|
||||
#if (PHASE != PHASE_SPECIAL)
|
||||
bg_alpha = bg_alpha + (1.0f - bg_alpha) * background_opacity; // bg_alpha = 1 if bg_alpha else background_opacity
|
||||
bg_alpha *= draw_bg; // if not draw_bg: bg_alpha = 0
|
||||
#endif
|
||||
#else
|
||||
is_special_cell += float(is_reversed); // bg_alpha should be 1 for reverse video cells as well
|
||||
is_special_cell = step(0.5, is_special_cell); // is_special_cell = 1 if is_special_cell else 0
|
||||
bg_alpha = bg_alpha * (1. - float(is_special_cell)) + is_special_cell; // bg_alpha = 1 if is_special_cell else bg_alpha
|
||||
#endif
|
||||
bg_alpha *= draw_bg;
|
||||
#endif // ends fast case #if else
|
||||
|
||||
// Selection and cursor
|
||||
bg = choose_color(float(is_selected & ONE), choose_color(use_cell_for_selection_bg, color_to_vec(fg_as_uint), color_to_vec(highlight_bg)), bg);
|
||||
background = choose_color(cell_data.has_block_cursor, mix(bg, color_to_vec(cursor_bg), cursor_opacity), bg);
|
||||
#if !defined(TRANSPARENT) && (PHASE == PHASE_SPECIAL)
|
||||
float is_special_cell = cell_data.has_block_cursor + float(is_selected & ONE);
|
||||
bg_alpha = step(0.5, is_special_cell);
|
||||
#endif
|
||||
|
||||
// }}}
|
||||
|
||||
}
|
||||
|
||||
124
kitty/colors.c
124
kitty/colors.c
@@ -60,6 +60,17 @@ create_256_color_table(void) {
|
||||
return ans;
|
||||
}
|
||||
|
||||
static void
|
||||
set_transparent_background_colors(TransparentDynamicColor *dest, PyObject *src) {
|
||||
memset(dest, 0, sizeof(((ColorProfile*)0)->configured_transparent_colors));
|
||||
for (Py_ssize_t i = 0; i < MIN(PyTuple_GET_SIZE(src), (Py_ssize_t)arraysz(((ColorProfile*)0)->configured_transparent_colors)); i++) {
|
||||
PyObject *e = PyTuple_GET_ITEM(src, i);
|
||||
dest[i].color = ((Color*)(PyTuple_GET_ITEM(e, 0)))->color.val & 0xffffff;
|
||||
dest[i].opacity = (float)PyFloat_AsDouble(PyTuple_GET_ITEM(e, 1));
|
||||
dest[i].is_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
set_configured_colors(ColorProfile *self, PyObject *opts) {
|
||||
#define n(which, attr) { \
|
||||
@@ -80,9 +91,12 @@ set_configured_colors(ColorProfile *self, PyObject *opts) {
|
||||
n(default_fg, foreground); n(default_bg, background);
|
||||
n(cursor_color, cursor); n(cursor_text_color, cursor_text_color);
|
||||
n(highlight_fg, selection_foreground); n(highlight_bg, selection_background);
|
||||
n(visual_bell_color, visual_bell_color); n(second_transparent_bg, second_transparent_bg);
|
||||
n(visual_bell_color, visual_bell_color);
|
||||
#undef n
|
||||
return true;
|
||||
RAII_PyObject(src, PyObject_GetAttrString(opts, "transparent_background_colors"));
|
||||
if (!src) { PyErr_SetString(PyExc_TypeError, "No transparent_background_colors on opts object"); return false; }
|
||||
set_transparent_background_colors(self->configured_transparent_colors, src);
|
||||
return PyErr_Occurred() ? false : true;
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -162,6 +176,8 @@ copy_color_profile(ColorProfile *dest, ColorProfile *src) {
|
||||
memcpy(dest->orig_color_table, src->orig_color_table, sizeof(dest->color_table));
|
||||
memcpy(&dest->configured, &src->configured, sizeof(dest->configured));
|
||||
memcpy(&dest->overridden, &src->overridden, sizeof(dest->overridden));
|
||||
memcpy(dest->overriden_transparent_colors, src->overriden_transparent_colors, sizeof(dest->overriden_transparent_colors));
|
||||
memcpy(dest->configured_transparent_colors, src->configured_transparent_colors, sizeof(dest->configured_transparent_colors));
|
||||
dest->dirty = true;
|
||||
}
|
||||
|
||||
@@ -193,8 +209,8 @@ patch_color_table(const char *key, PyObject *profiles, PyObject *spec, size_t wh
|
||||
|
||||
static PyObject*
|
||||
patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
|
||||
PyObject *spec, *profiles, *v; ColorProfile *self; int change_configured;
|
||||
if (!PyArg_ParseTuple(args, "O!O!p", &PyDict_Type, &spec, &PyTuple_Type, &profiles, &change_configured)) return NULL;
|
||||
PyObject *spec, *transparent_background_colors, *profiles, *v; ColorProfile *self; int change_configured;
|
||||
if (!PyArg_ParseTuple(args, "O!O!O!p", &PyDict_Type, &spec, &PyTuple_Type, &transparent_background_colors, &PyTuple_Type, &profiles, &change_configured)) return NULL;
|
||||
char key[32] = {0};
|
||||
for (size_t i = 0; i < arraysz(FG_BG_256); i++) {
|
||||
snprintf(key, sizeof(key) - 1, "color%zu", i);
|
||||
@@ -226,12 +242,35 @@ patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
|
||||
S(foreground, default_fg); S(background, default_bg); S(cursor, cursor_color);
|
||||
S(selection_foreground, highlight_fg); S(selection_background, highlight_bg);
|
||||
S(cursor_text_color, cursor_text_color); S(visual_bell_color, visual_bell_color);
|
||||
S(second_transparent_bg, second_transparent_bg);
|
||||
#undef SI
|
||||
#undef S
|
||||
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) {
|
||||
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i);
|
||||
set_transparent_background_colors(self->overriden_transparent_colors, transparent_background_colors);
|
||||
if (change_configured) set_transparent_background_colors(self->configured_transparent_colors, transparent_background_colors);
|
||||
}
|
||||
if (PyErr_Occurred()) return NULL;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
bool
|
||||
colorprofile_to_transparent_color(const ColorProfile *self, unsigned index, color_type *color, float *opacity) {
|
||||
*color = UINT32_MAX; *opacity = 1.0;
|
||||
if (index < arraysz(self->configured_transparent_colors)) {
|
||||
if (self->overriden_transparent_colors[index].is_set) {
|
||||
*color = self->overriden_transparent_colors[index].color; *opacity = self->overriden_transparent_colors[index].opacity;
|
||||
if (*opacity < 0) *opacity = OPT(background_opacity);
|
||||
return true;
|
||||
}
|
||||
if (self->configured_transparent_colors[index].is_set) {
|
||||
*color = self->configured_transparent_colors[index].color; *opacity = self->configured_transparent_colors[index].opacity;
|
||||
if (*opacity < 0) *opacity = OPT(background_opacity);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DynamicColor
|
||||
colorprofile_to_color(const ColorProfile *self, DynamicColor entry, DynamicColor defval) {
|
||||
switch(entry.type) {
|
||||
@@ -264,20 +303,21 @@ colorprofile_to_color_with_fallback(ColorProfile *self, DynamicColor entry, Dyna
|
||||
}
|
||||
return entry.rgb;
|
||||
}
|
||||
static Color* alloc_color(unsigned char r, unsigned char g, unsigned char b, unsigned a);
|
||||
|
||||
static PyObject*
|
||||
as_dict(ColorProfile *self, PyObject *args UNUSED) {
|
||||
#define as_dict_doc "Return all colors as a dictionary of color_name to integer or None (names are the same as used in kitty.conf)"
|
||||
PyObject *ans = PyDict_New();
|
||||
RAII_PyObject(ans, PyDict_New());
|
||||
if (ans == NULL) return PyErr_NoMemory();
|
||||
for (unsigned i = 0; i < arraysz(self->color_table); i++) {
|
||||
static char buf[32] = {0};
|
||||
snprintf(buf, sizeof(buf) - 1, "color%u", i);
|
||||
PyObject *val = PyLong_FromUnsignedLong(self->color_table[i]);
|
||||
if (!val) { Py_CLEAR(ans); return PyErr_NoMemory(); }
|
||||
if (!val) { return PyErr_NoMemory(); }
|
||||
int ret = PyDict_SetItemString(ans, buf, val);
|
||||
Py_CLEAR(val);
|
||||
if (ret != 0) { Py_CLEAR(ans); return NULL; }
|
||||
if (ret != 0) { return NULL; }
|
||||
}
|
||||
#define D(attr, name) { \
|
||||
if (self->overridden.attr.type != COLOR_NOT_SET) { \
|
||||
@@ -288,17 +328,33 @@ as_dict(ColorProfile *self, PyObject *args UNUSED) {
|
||||
color_type c = colorprofile_to_color(self, self->overridden.attr, self->configured.attr).rgb; \
|
||||
val = PyLong_FromUnsignedLong(c); \
|
||||
} \
|
||||
if (!val) { Py_CLEAR(ans); return NULL; } \
|
||||
if (!val) { return NULL; } \
|
||||
ret = PyDict_SetItemString(ans, #name, val); \
|
||||
Py_CLEAR(val); \
|
||||
if (ret != 0) { Py_CLEAR(ans); return NULL; } \
|
||||
if (ret != 0) { return NULL; } \
|
||||
}}
|
||||
D(default_fg, foreground); D(default_bg, background);
|
||||
D(cursor_color, cursor); D(cursor_text_color, cursor_text); D(highlight_fg, selection_foreground);
|
||||
D(highlight_bg, selection_background); D(visual_bell_color, visual_bell_color); D(second_transparent_bg, second_transparent_bg);
|
||||
|
||||
D(highlight_bg, selection_background); D(visual_bell_color, visual_bell_color);
|
||||
RAII_PyObject(transparent_background_colors, PyList_New(0));
|
||||
if (!transparent_background_colors) return NULL;
|
||||
for (size_t i = 0; i < arraysz(self->overriden_transparent_colors); i++) {
|
||||
TransparentDynamicColor *c = NULL;
|
||||
if (self->overriden_transparent_colors[i].is_set) c = self->overriden_transparent_colors + i;
|
||||
else if (self->configured_transparent_colors[i].is_set) c = self->configured_transparent_colors + i;
|
||||
if (c) {
|
||||
RAII_PyObject(t, Py_BuildValue("Nf", alloc_color((c->color >> 16) & 0xff, (c->color >> 8) & 0xff, c->color & 0xff, 0), c->opacity));
|
||||
if (!t) return NULL;
|
||||
if (PyList_Append(transparent_background_colors, t) != 0) return NULL;
|
||||
}
|
||||
}
|
||||
if (PyList_GET_SIZE(transparent_background_colors)) {
|
||||
RAII_PyObject(t, PyList_AsTuple(transparent_background_colors));
|
||||
if (!t) return NULL;
|
||||
if (PyDict_SetItemString(ans, "transparent_background_colors", t) != 0) return NULL;
|
||||
}
|
||||
#undef D
|
||||
return ans;
|
||||
return Py_NewRef(ans);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
@@ -373,6 +429,8 @@ copy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size
|
||||
|
||||
static void
|
||||
push_onto_color_stack_at(ColorProfile *self, unsigned int i) {
|
||||
self->color_stack[i].dynamic_colors = self->overridden;
|
||||
memcpy(self->color_stack[i].transparent_colors, self->overriden_transparent_colors, sizeof(self->overriden_transparent_colors));
|
||||
self->color_stack[i].dynamic_colors = self->overridden;
|
||||
memcpy(self->color_stack[i].color_table, self->color_table, sizeof(self->color_stack->color_table));
|
||||
}
|
||||
@@ -381,6 +439,7 @@ static void
|
||||
copy_from_color_stack_at(ColorProfile *self, unsigned int i) {
|
||||
self->overridden = self->color_stack[i].dynamic_colors;
|
||||
memcpy(self->color_table, self->color_stack[i].color_table, sizeof(self->color_table));
|
||||
memcpy(self->overriden_transparent_colors, self->color_stack[i].transparent_colors, sizeof(self->overriden_transparent_colors));
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -445,7 +504,6 @@ default_color_table(PyObject *self UNUSED, PyObject *args UNUSED) {
|
||||
|
||||
// Boilerplate {{{
|
||||
|
||||
static Color* alloc_color(unsigned char r, unsigned char g, unsigned char b, unsigned a);
|
||||
#define CGETSET(name, nullable) \
|
||||
static PyObject* name##_get(ColorProfile *self, void UNUSED *closure) { \
|
||||
DynamicColor ans = colorprofile_to_color(self, self->overridden.name, self->configured.name); \
|
||||
@@ -477,7 +535,6 @@ CGETSET(cursor_text_color, true)
|
||||
CGETSET(highlight_fg, true)
|
||||
CGETSET(highlight_bg, true)
|
||||
CGETSET(visual_bell_color, true)
|
||||
CGETSET(second_transparent_bg, true)
|
||||
#undef CGETSET
|
||||
|
||||
static PyGetSetDef cp_getsetters[] = {
|
||||
@@ -488,7 +545,6 @@ static PyGetSetDef cp_getsetters[] = {
|
||||
GETSET(highlight_fg)
|
||||
GETSET(highlight_bg)
|
||||
GETSET(visual_bell_color)
|
||||
GETSET(second_transparent_bg)
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
@@ -508,6 +564,36 @@ reload_from_opts(ColorProfile *self, PyObject *args UNUSED) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
get_transparent_background_color(ColorProfile *self, PyObject *index) {
|
||||
if (!PyLong_Check(index)) { PyErr_SetString(PyExc_TypeError, "index must be an int"); return NULL; }
|
||||
unsigned long idx = PyLong_AsUnsignedLong(index);
|
||||
if (PyErr_Occurred()) return NULL;
|
||||
if (idx >= arraysz(self->configured_transparent_colors)) Py_RETURN_NONE;
|
||||
TransparentDynamicColor *c = self->overriden_transparent_colors[idx].is_set ? self->overriden_transparent_colors + idx : self->configured_transparent_colors + idx;
|
||||
if (!c->is_set) Py_RETURN_NONE;
|
||||
float opacity = c->opacity >= 0 ? c->opacity : OPT(background_opacity);
|
||||
return (PyObject*)alloc_color((c->color >> 16) & 0xff, (c->color >> 8) & 0xff, c->color & 0xff, (unsigned)(255.f * opacity));
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
set_transparent_background_color(ColorProfile *self, PyObject *const *args, Py_ssize_t nargs) {
|
||||
if (nargs < 1) { PyErr_SetString(PyExc_TypeError, "must specify index"); return NULL; }
|
||||
if (!PyLong_Check(args[0])) { PyErr_SetString(PyExc_TypeError, "index must be an int"); return NULL; }
|
||||
unsigned long idx = PyLong_AsUnsignedLong(args[0]);
|
||||
if (PyErr_Occurred()) return NULL;
|
||||
if (idx >= arraysz(self->configured_transparent_colors)) Py_RETURN_NONE;
|
||||
if (nargs < 2) { self->overriden_transparent_colors[idx].is_set = false; Py_RETURN_NONE; }
|
||||
if (!PyObject_TypeCheck(args[1], &Color_Type)) { PyErr_SetString(PyExc_TypeError, "color must be Color object"); return NULL; }
|
||||
Color *c = (Color*)args[1];
|
||||
float opacity = (float)(c->color.alpha) / 255.f;
|
||||
if (nargs > 2 && PyFloat_Check(args[2])) opacity = (float)PyFloat_AsDouble(args[2]);
|
||||
self->overriden_transparent_colors[idx].is_set = true;
|
||||
self->overriden_transparent_colors[idx].color = c->color.rgb;
|
||||
self->overriden_transparent_colors[idx].opacity = MAX(-1.f, MIN(opacity, 1.f));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyMethodDef cp_methods[] = {
|
||||
METHOD(reset_color_table, METH_NOARGS)
|
||||
METHOD(as_dict, METH_NOARGS)
|
||||
@@ -515,7 +601,9 @@ static PyMethodDef cp_methods[] = {
|
||||
METHOD(as_color, METH_O)
|
||||
METHOD(reset_color, METH_O)
|
||||
METHOD(set_color, METH_VARARGS)
|
||||
METHODB(get_transparent_background_color, METH_O),
|
||||
METHODB(reload_from_opts, METH_VARARGS),
|
||||
{"set_transparent_background_color", (PyCFunction)(void(*)(void))set_transparent_background_color, METH_FASTCALL, ""},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
@@ -552,7 +640,7 @@ new_color(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds) {
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
color_as_int(Color *self) {
|
||||
Color_as_int(Color *self) {
|
||||
return PyLong_FromUnsignedLong(self->color.val);
|
||||
}
|
||||
|
||||
@@ -566,7 +654,7 @@ color_truediv(Color *self, PyObject *divisor) {
|
||||
}
|
||||
|
||||
static PyNumberMethods color_number_methods = {
|
||||
.nb_int = (unaryfunc)color_as_int,
|
||||
.nb_int = (unaryfunc)Color_as_int,
|
||||
.nb_true_divide = (binaryfunc)color_truediv,
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class Version(NamedTuple):
|
||||
|
||||
appname: str = 'kitty'
|
||||
kitty_face = '🐱'
|
||||
version: Version = Version(0, 36, 2)
|
||||
version: Version = Version(0, 36, 4)
|
||||
str_version: str = '.'.join(map(str, version))
|
||||
_plat = sys.platform.lower()
|
||||
is_macos: bool = 'darwin' in _plat
|
||||
|
||||
@@ -744,6 +744,7 @@ ensure_render_space(size_t width, size_t height, size_t num_glyphs) {
|
||||
}
|
||||
if (buffers.sz < num_glyphs) {
|
||||
buffers.sz = MAX(128, num_glyphs * 2);
|
||||
free(buffers.boxes); free(buffers.glyphs); free(buffers.positions);
|
||||
buffers.boxes = calloc(sizeof(buffers.boxes[0]), buffers.sz);
|
||||
buffers.glyphs = calloc(sizeof(buffers.glyphs[0]), buffers.sz);
|
||||
buffers.positions = calloc(sizeof(buffers.positions[0]), buffers.sz);
|
||||
@@ -812,13 +813,13 @@ render_sample_text(CTFace *self, PyObject *args) {
|
||||
if (!PyArg_ParseTuple(args, "Ukk|k", &ptext, &canvas_width, &canvas_height, &fg)) return NULL;
|
||||
unsigned int cell_width, cell_height, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
|
||||
cell_metrics((PyObject*)self, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness);
|
||||
RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height));
|
||||
if (!pbuf) return NULL;
|
||||
memset(PyBytes_AS_STRING(pbuf), 0, PyBytes_GET_SIZE(pbuf));
|
||||
if (!cell_width || !cell_height) return Py_BuildValue("OII", pbuf, cell_width, cell_height);
|
||||
if (!cell_width || !cell_height) return Py_BuildValue("yII", "", cell_width, cell_height);
|
||||
size_t num_chars = PyUnicode_GET_LENGTH(ptext);
|
||||
int num_chars_per_line = canvas_width / cell_width, num_of_lines = (int)ceil((float)num_chars / (float)num_chars_per_line);
|
||||
canvas_height = MIN(canvas_height, num_of_lines * cell_height);
|
||||
RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height));
|
||||
if (!pbuf) return NULL;
|
||||
memset(PyBytes_AS_STRING(pbuf), 0, PyBytes_GET_SIZE(pbuf));
|
||||
|
||||
__attribute__((cleanup(destroy_hb_buffer))) hb_buffer_t *hb_buffer = hb_buffer_create();
|
||||
if (!hb_buffer_pre_allocate(hb_buffer, 4*num_chars)) { PyErr_NoMemory(); return NULL; }
|
||||
@@ -857,9 +858,10 @@ render_sample_text(CTFace *self, PyObject *args) {
|
||||
render_glyphs(font, canvas_width, canvas_height, baseline, num_glyphs);
|
||||
uint8_t r = (fg >> 16) & 0xff, g = (fg >> 8) & 0xff, b = fg & 0xff;
|
||||
const uint8_t *last_pixel = (uint8_t*)PyBytes_AS_STRING(pbuf) + PyBytes_GET_SIZE(pbuf) - sizeof(pixel);
|
||||
const uint8_t *s_limit = buffers.render_buf + canvas_width * canvas_height;
|
||||
for (
|
||||
uint8_t *p = (uint8_t*)PyBytes_AS_STRING(pbuf), *s = buffers.render_buf;
|
||||
p <= last_pixel;
|
||||
p <= last_pixel && s < s_limit;
|
||||
p += sizeof(pixel), s++
|
||||
) {
|
||||
p[0] = r; p[1] = g; p[2] = b; p[3] = s[0];
|
||||
|
||||
@@ -17,6 +17,20 @@
|
||||
#include <sys/mman.h>
|
||||
#include <structmember.h>
|
||||
|
||||
#ifdef LIBRESSL_VERSION_NUMBER
|
||||
/* from: https://github.com/libressl/portable/blob/master/include/compat/string.h#L63 */
|
||||
#define explicit_bzero libressl_explicit_bzero
|
||||
void explicit_bzero(void *, size_t);
|
||||
/* from: https://github.com/libressl/portable/blob/master/crypto/compat/freezero.c */
|
||||
void
|
||||
freezero(void *ptr, size_t sz) {
|
||||
if (ptr == NULL) return;
|
||||
explicit_bzero(ptr, sz);
|
||||
free(ptr);
|
||||
}
|
||||
#define OPENSSL_clear_free freezero
|
||||
#endif
|
||||
|
||||
#define SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH
|
||||
|
||||
typedef enum HASH_ALGORITHM { SHA1_HASH, SHA224_HASH, SHA256_HASH, SHA384_HASH, SHA512_HASH } HASH_ALGORITHM;
|
||||
|
||||
@@ -323,15 +323,21 @@ typedef union DynamicColor {
|
||||
} DynamicColor;
|
||||
|
||||
typedef struct {
|
||||
DynamicColor default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg, visual_bell_color, second_transparent_bg;
|
||||
DynamicColor default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg, visual_bell_color;
|
||||
} DynamicColors;
|
||||
|
||||
typedef struct TransparentDynamicColor {
|
||||
color_type color; float opacity; bool is_set;
|
||||
} TransparentDynamicColor;
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
bool dirty;
|
||||
uint32_t color_table[256], orig_color_table[256];
|
||||
struct { DynamicColors dynamic_colors; uint32_t color_table[256]; } *color_stack;
|
||||
TransparentDynamicColor configured_transparent_colors[8], overriden_transparent_colors[8];
|
||||
struct { DynamicColors dynamic_colors; uint32_t color_table[256]; TransparentDynamicColor transparent_colors[8]; } *color_stack;
|
||||
unsigned int color_stack_idx, color_stack_sz;
|
||||
DynamicColors configured, overridden;
|
||||
color_type mark_foregrounds[MARK_MASK+1], mark_backgrounds[MARK_MASK+1];
|
||||
@@ -403,6 +409,7 @@ bool schedule_write_to_child_python(unsigned long id, const char *prefix, PyObje
|
||||
bool set_iutf8(int, bool);
|
||||
|
||||
DynamicColor colorprofile_to_color(const ColorProfile *self, DynamicColor entry, DynamicColor defval);
|
||||
bool colorprofile_to_transparent_color(const ColorProfile *self, unsigned index, color_type *color, float *opacity);
|
||||
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);
|
||||
|
||||
@@ -809,14 +809,9 @@ class ColorProfile:
|
||||
@visual_bell_color.setter
|
||||
def visual_bell_color(self, val: Union[None|int|Color]) -> None: ...
|
||||
|
||||
@property
|
||||
def second_transparent_bg(self) -> Optional[Color]: ...
|
||||
@second_transparent_bg.setter
|
||||
def second_transparent_bg(self, val: Union[None|int|Color]) -> None: ...
|
||||
|
||||
def __init__(self, opts: Optional[Options] = None): ...
|
||||
|
||||
def as_dict(self) -> Dict[str, Optional[int]]:
|
||||
def as_dict(self) -> Dict[str, int | None | tuple[tuple[Color, float], ...]]:
|
||||
pass
|
||||
|
||||
def as_color(self, val: int) -> Optional[Color]:
|
||||
@@ -833,9 +828,13 @@ class ColorProfile:
|
||||
|
||||
def reload_from_opts(self, opts: Optional[Options] = None) -> None: ...
|
||||
|
||||
def get_transparent_background_color(self, index: int) -> Color | None: ...
|
||||
def set_transparent_background_color(self, index: int, color: Color | None = None, opacity: float | None = None) -> None: ...
|
||||
|
||||
|
||||
def patch_color_profiles(
|
||||
spec: Dict[str, Optional[int]], profiles: Tuple[ColorProfile, ...], change_configured: bool
|
||||
spec: Dict[str, Optional[int]], transparent_background_colors: tuple[tuple[Color, float], ...],
|
||||
profiles: Tuple[ColorProfile, ...], change_configured: bool
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@@ -478,9 +478,9 @@ class LaunchKwds(TypedDict):
|
||||
|
||||
def apply_colors(window: Window, spec: Sequence[str]) -> None:
|
||||
from kitty.rc.set_colors import parse_colors
|
||||
colors = parse_colors(spec)
|
||||
colors, transparent_background_colors = parse_colors(spec)
|
||||
profiles = window.screen.color_profile,
|
||||
patch_color_profiles(colors, profiles, True)
|
||||
patch_color_profiles(colors, transparent_background_colors, profiles, True)
|
||||
|
||||
|
||||
def parse_var(defn: Iterable[str]) -> Iterator[tuple[str, str]]:
|
||||
|
||||
@@ -423,10 +423,14 @@ class Pair:
|
||||
|
||||
class SplitsLayoutOpts(LayoutOpts):
|
||||
|
||||
default_axis_is_horizontal: bool = True
|
||||
default_axis_is_horizontal: Optional[bool] = True
|
||||
|
||||
def __init__(self, data: Dict[str, str]):
|
||||
self.default_axis_is_horizontal = data.get('split_axis', 'horizontal') == 'horizontal'
|
||||
q = data.get('split_axis', 'horizontal')
|
||||
if q == 'auto':
|
||||
self.default_axis_is_horizontal = None
|
||||
else:
|
||||
self.default_axis_is_horizontal = q == 'horizontal'
|
||||
|
||||
def serialized(self) -> Dict[str, Any]:
|
||||
return {'default_axis_is_horizontal': self.default_axis_is_horizontal}
|
||||
@@ -439,14 +443,17 @@ class Splits(Layout):
|
||||
no_minimal_window_borders = True
|
||||
|
||||
@property
|
||||
def default_axis_is_horizontal(self) -> bool:
|
||||
def default_axis_is_horizontal(self) -> Optional[bool]:
|
||||
return self.layout_opts.default_axis_is_horizontal
|
||||
|
||||
@property
|
||||
def pairs_root(self) -> Pair:
|
||||
root: Optional[Pair] = getattr(self, '_pairs_root', None)
|
||||
if root is None:
|
||||
self._pairs_root = root = Pair(horizontal=self.default_axis_is_horizontal)
|
||||
horizontal = self.default_axis_is_horizontal
|
||||
if horizontal is None:
|
||||
horizontal = True
|
||||
self._pairs_root = root = Pair(horizontal=horizontal)
|
||||
return root
|
||||
|
||||
@pairs_root.setter
|
||||
@@ -508,7 +515,7 @@ class Splits(Layout):
|
||||
group_id = ag.id
|
||||
pair = self.pairs_root.pair_for_window(group_id)
|
||||
if pair is not None:
|
||||
if location == 'split':
|
||||
if location == 'split' or horizontal is None:
|
||||
wwidth = aw.geometry.right - aw.geometry.left
|
||||
wheight = aw.geometry.bottom - aw.geometry.top
|
||||
horizontal = wwidth >= wheight
|
||||
|
||||
@@ -1481,7 +1481,7 @@ theme with a background color in your editor, it will not be rendered as
|
||||
transparent. Instead you should change the default background color in your
|
||||
kitty config and not use a background color in the editor color scheme. Or use
|
||||
the escape codes to set the terminals default colors in a shell script to
|
||||
launch your editor. See also :opt:`second_transparent_bg`.
|
||||
launch your editor. See also :opt:`transparent_background_colors`.
|
||||
Be aware that using a value less than 1.0 is a (possibly
|
||||
significant) performance hit. When using a low value for this setting, it is
|
||||
desirable that you set the :opt:`background` color to a color the matches the
|
||||
@@ -1527,14 +1527,22 @@ opt('background_image_linear', 'no',
|
||||
long_text='When background image is scaled, whether linear interpolation should be used.'
|
||||
)
|
||||
|
||||
opt('second_transparent_bg', 'none', option_type='to_color_or_none', long_text='''
|
||||
When the background color matches this color, :opt:`background_opacity` is applied to it
|
||||
to render it as semi-transparent, just as for colors matching the main :opt:`background` color.
|
||||
opt('transparent_background_colors', '', option_type='transparent_background_colors', long_text='''
|
||||
A space separated list of upto 7 colors, with opacity. When the background color of a cell matches one of these colors,
|
||||
it is rendered semi-transparent using the specified opacity.
|
||||
|
||||
Useful in more complex UIs like editors where you could want more than a single background color
|
||||
to be rendered as transparent, for instance, for a cursor highlight line background.
|
||||
to be rendered as transparent, for instance, for a cursor highlight line background or a highlighted block.
|
||||
Terminal applications can set this color using :ref:`The kitty color control <color_control>`
|
||||
escape code.
|
||||
''')
|
||||
|
||||
The syntax for specifiying colors is: :code:`color@opacity`, where the :code:`@opacity`
|
||||
part is optional. When unspecified, the value of :opt:`background_opacity` is used. For example::
|
||||
|
||||
transparent_background_colors red@0.5 #00ff00@0.3
|
||||
|
||||
'''
|
||||
)
|
||||
|
||||
opt('dynamic_background_opacity', 'no',
|
||||
option_type='to_bool', ctype='bool',
|
||||
|
||||
11
kitty/options/parse.py
generated
11
kitty/options/parse.py
generated
@@ -19,8 +19,9 @@ from kitty.options.utils import (
|
||||
shell_integration, store_multiple, symbol_map, tab_activity_symbol, tab_bar_edge,
|
||||
tab_bar_margin_height, tab_bar_min_tabs, tab_fade, tab_font_style, tab_separator,
|
||||
tab_title_template, titlebar_color, to_cursor_shape, to_cursor_unfocused_shape, to_font_size,
|
||||
to_layout_names, to_modifiers, url_prefixes, url_style, visual_bell_duration,
|
||||
visual_window_select_characters, window_border_width, window_logo_scale, window_size
|
||||
to_layout_names, to_modifiers, transparent_background_colors, url_prefixes, url_style,
|
||||
visual_bell_duration, visual_window_select_characters, window_border_width, window_logo_scale,
|
||||
window_size
|
||||
)
|
||||
|
||||
|
||||
@@ -1193,9 +1194,6 @@ class Parser:
|
||||
def scrollback_pager_history_size(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['scrollback_pager_history_size'] = scrollback_pager_history_size(val)
|
||||
|
||||
def second_transparent_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['second_transparent_bg'] = to_color_or_none(val)
|
||||
|
||||
def select_by_word_characters(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['select_by_word_characters'] = str(val)
|
||||
|
||||
@@ -1326,6 +1324,9 @@ class Parser:
|
||||
def touch_scroll_multiplier(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['touch_scroll_multiplier'] = float(val)
|
||||
|
||||
def transparent_background_colors(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['transparent_background_colors'] = transparent_background_colors(val)
|
||||
|
||||
def undercurl_style(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
val = val.lower()
|
||||
if val not in self.choices_for_undercurl_style:
|
||||
|
||||
5
kitty/options/types.py
generated
5
kitty/options/types.py
generated
@@ -409,7 +409,6 @@ option_names = ( # {{{
|
||||
'scrollback_lines',
|
||||
'scrollback_pager',
|
||||
'scrollback_pager_history_size',
|
||||
'second_transparent_bg',
|
||||
'select_by_word_characters',
|
||||
'select_by_word_characters_forward',
|
||||
'selection_background',
|
||||
@@ -443,6 +442,7 @@ option_names = ( # {{{
|
||||
'text_composition_strategy',
|
||||
'text_fg_override_threshold',
|
||||
'touch_scroll_multiplier',
|
||||
'transparent_background_colors',
|
||||
'undercurl_style',
|
||||
'underline_hyperlinks',
|
||||
'update_check_interval',
|
||||
@@ -574,7 +574,6 @@ class Options:
|
||||
scrollback_lines: int = 2000
|
||||
scrollback_pager: typing.List[str] = ['less', '--chop-long-lines', '--RAW-CONTROL-CHARS', '+INPUT_LINE_NUMBER']
|
||||
scrollback_pager_history_size: int = 0
|
||||
second_transparent_bg: typing.Optional[kitty.fast_data_types.Color] = None
|
||||
select_by_word_characters: str = '@-./_~?&=%+#'
|
||||
select_by_word_characters_forward: str = ''
|
||||
selection_background: typing.Optional[kitty.fast_data_types.Color] = Color(255, 250, 205)
|
||||
@@ -607,6 +606,7 @@ class Options:
|
||||
text_composition_strategy: str = 'platform'
|
||||
text_fg_override_threshold: float = 0.0
|
||||
touch_scroll_multiplier: float = 1.0
|
||||
transparent_background_colors: typing.Tuple[typing.Tuple[kitty.fast_data_types.Color, float], ...] = ()
|
||||
undercurl_style: choices_for_undercurl_style = 'thin-sparse'
|
||||
underline_hyperlinks: choices_for_underline_hyperlinks = 'hover'
|
||||
update_check_interval: float = 24.0
|
||||
@@ -1035,7 +1035,6 @@ nullable_colors = frozenset({
|
||||
'active_border_color'
|
||||
'tab_bar_background'
|
||||
'tab_bar_margin_color'
|
||||
'second_transparent_bg'
|
||||
'selection_foreground'
|
||||
'selection_background'
|
||||
})
|
||||
@@ -1554,6 +1554,23 @@ def visual_bell_duration(spec: str) -> Tuple[float, EasingFunction, EasingFuncti
|
||||
return parse_animation(spec, interval=0.)
|
||||
|
||||
|
||||
def transparent_background_colors(spec: str) -> Tuple[Tuple[Color, float], ...]:
|
||||
if not spec:
|
||||
return ()
|
||||
ans: list[tuple[Color, float]] = []
|
||||
seen: dict[Color, int] = {}
|
||||
for part in spec.split():
|
||||
col, sep, alpha = part.partition('@')
|
||||
c = to_color(col)
|
||||
o = unit_float(alpha) if alpha else -1
|
||||
if (idx := seen.get(c)) is not None:
|
||||
ans[idx] = c, o
|
||||
continue
|
||||
seen[c] = len(ans)
|
||||
ans.append((c, o))
|
||||
return tuple(ans[:7])
|
||||
|
||||
|
||||
def deprecated_hide_window_decorations_aliases(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||
if not hasattr(deprecated_hide_window_decorations_aliases, key):
|
||||
setattr(deprecated_hide_window_decorations_aliases, key, True)
|
||||
|
||||
@@ -398,7 +398,7 @@ class RemoteCommand:
|
||||
window = window or boss.active_window
|
||||
windows = [window] if window else []
|
||||
if payload_get(window_match_name):
|
||||
windows = list(boss.match_windows(payload_get(window_match_name)))
|
||||
windows = list(boss.match_windows(payload_get(window_match_name), window))
|
||||
if not windows:
|
||||
raise MatchError(payload_get(window_match_name))
|
||||
if payload_get(tab_match_name):
|
||||
|
||||
@@ -49,7 +49,7 @@ configured colors.
|
||||
for k, v in windows[0].current_colors.items():
|
||||
if v is None:
|
||||
ans.pop(k, None)
|
||||
else:
|
||||
elif isinstance(v, int):
|
||||
ans[k] = color_from_int(v)
|
||||
tab = windows[0].tabref()
|
||||
tm = None if tab is None else tab.tab_manager_ref()
|
||||
|
||||
@@ -194,7 +194,7 @@ on bracketed paste mode.
|
||||
|
||||
def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType:
|
||||
sid = payload_get('session_id', '')
|
||||
windows = self.windows_for_payload(boss, None, payload_get, window_match_name='match')
|
||||
windows = self.windows_for_payload(boss, window, payload_get, window_match_name='match')
|
||||
pdata: str = payload_get('data')
|
||||
encoding, _, q = pdata.partition(':')
|
||||
session = ''
|
||||
|
||||
@@ -27,16 +27,19 @@ if TYPE_CHECKING:
|
||||
from kitty.cli_stub import SetColorsRCOptions as CLIOptions
|
||||
|
||||
|
||||
def parse_colors(args: Iterable[str]) -> Dict[str, Optional[int]]:
|
||||
def parse_colors(args: Iterable[str]) -> tuple[Dict[str, Optional[int]], tuple[tuple[Color, float], ...]]:
|
||||
from kitty.options.types import nullable_colors
|
||||
colors: Dict[str, Optional[Color]] = {}
|
||||
nullable_color_map: Dict[str, Optional[int]] = {}
|
||||
transparent_background_colors = ()
|
||||
for spec in args:
|
||||
if '=' in spec:
|
||||
colors.update(parse_config((spec.replace('=', ' '),)))
|
||||
conf = parse_config((spec.replace('=', ' '),))
|
||||
else:
|
||||
with open(os.path.expanduser(spec), encoding='utf-8', errors='replace') as f:
|
||||
colors.update(parse_config(f))
|
||||
conf = parse_config(f)
|
||||
transparent_background_colors = conf.pop('transparent_background_colors', ())
|
||||
colors.update(conf)
|
||||
for k in nullable_colors:
|
||||
q = colors.pop(k, False)
|
||||
if q is not False:
|
||||
@@ -44,13 +47,13 @@ def parse_colors(args: Iterable[str]) -> Dict[str, Optional[int]]:
|
||||
nullable_color_map[k] = val
|
||||
ans: Dict[str, Optional[int]] = {k: int(v) for k, v in colors.items() if isinstance(v, Color)}
|
||||
ans.update(nullable_color_map)
|
||||
return ans
|
||||
return ans, transparent_background_colors
|
||||
|
||||
|
||||
class SetColors(RemoteCommand):
|
||||
|
||||
protocol_spec = __doc__ = '''
|
||||
colors+/dict.colors: An object mapping names to colors as 24-bit RGB integers or null for nullable colors
|
||||
colors+/dict.colors: An object mapping names to colors as 24-bit RGB integers or null for nullable colors. Or a string for transparent_background_colors.
|
||||
match_window/str: Window to change colors in
|
||||
match_tab/str: Tab to change colors in
|
||||
all/bool: Boolean indicating change colors everywhere or not
|
||||
@@ -88,14 +91,18 @@ this option, any color arguments are ignored and :option:`kitten @ set-colors --
|
||||
completion=RemoteCommand.CompletionSpec.from_string('type:file group:"CONF files", ext:conf'))
|
||||
|
||||
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||
final_colors: Dict[str, Optional[int]] = {}
|
||||
final_colors: Dict[str, int | None | str] = {}
|
||||
transparent_background_colors: tuple[tuple[Color, float], ...] = ()
|
||||
if not opts.reset:
|
||||
try:
|
||||
final_colors = parse_colors(args)
|
||||
fc, transparent_background_colors = parse_colors(args)
|
||||
except FileNotFoundError as err:
|
||||
raise ParsingOfArgsFailed(f'The colors configuration file {emph(err.filename)} was not found.') from err
|
||||
except Exception as err:
|
||||
raise ParsingOfArgsFailed(str(err)) from err
|
||||
final_colors.update(fc)
|
||||
if transparent_background_colors:
|
||||
final_colors['transparent_background_colors'] = ' '.join(f'{c.as_sharp}@{f}' for c, f in transparent_background_colors)
|
||||
ans = {
|
||||
'match_window': opts.match, 'match_tab': opts.match_tab,
|
||||
'all': opts.all or opts.reset, 'configured': opts.configured or opts.reset,
|
||||
@@ -105,12 +112,18 @@ this option, any color arguments are ignored and :option:`kitten @ set-colors --
|
||||
|
||||
def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType:
|
||||
windows = self.windows_for_payload(boss, window, payload_get)
|
||||
colors: Dict[str, Optional[int]] = payload_get('colors')
|
||||
colors: Dict[str, int | None] = payload_get('colors')
|
||||
tbc = colors.get('transparent_background_colors')
|
||||
if payload_get('reset'):
|
||||
colors = {k: None if v is None else int(v) for k, v in boss.color_settings_at_startup.items()}
|
||||
profiles = tuple(w.screen.color_profile for w in windows if w)
|
||||
patch_color_profiles(colors, profiles, payload_get('configured'))
|
||||
boss.patch_colors(colors, payload_get('configured'))
|
||||
if tbc:
|
||||
from kitty.options.utils import transparent_background_colors
|
||||
parsed_tbc = transparent_background_colors(str(tbc))
|
||||
else:
|
||||
parsed_tbc = ()
|
||||
patch_color_profiles(colors, parsed_tbc, profiles, payload_get('configured'))
|
||||
boss.patch_colors(colors, parsed_tbc, payload_get('configured'))
|
||||
default_bg_changed = 'background' in colors
|
||||
for w in windows:
|
||||
if w:
|
||||
|
||||
@@ -293,12 +293,14 @@ pick_cursor_color(Line *line, const ColorProfile *color_profile, color_type cell
|
||||
static void
|
||||
cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, const CellRenderData *crd, CursorRenderInfo *cursor, OSWindow *os_window) {
|
||||
struct GPUCellRenderData {
|
||||
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_color, use_cell_for_selection_bg;
|
||||
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_color, use_cell_for_selection_bg;
|
||||
|
||||
GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted, second_transparent_bg;
|
||||
GLuint default_fg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
|
||||
|
||||
GLuint xnum, ynum, cursor_fg_sprite_idx;
|
||||
GLfloat cursor_x, cursor_y, cursor_w, cursor_opacity;
|
||||
GLuint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7;
|
||||
GLfloat bg_opacities0, bg_opacities1, bg_opacities2, bg_opacities3, bg_opacities4, bg_opacities5, bg_opacities6, bg_opacities7;
|
||||
};
|
||||
// Send the uniform data
|
||||
struct GPUCellRenderData *rd = (struct GPUCellRenderData*)map_vao_buffer(vao_idx, uniform_buffer, GL_WRITE_ONLY);
|
||||
@@ -307,8 +309,13 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
||||
copy_color_table_to_buffer(cp, (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(cp, cp->overridden.name, cp->configured.name).rgb
|
||||
rd->default_fg = COLOR(default_fg); rd->default_bg = COLOR(default_bg);
|
||||
rd->default_fg = COLOR(default_fg);
|
||||
rd->highlight_fg = COLOR(highlight_fg); rd->highlight_bg = COLOR(highlight_bg);
|
||||
rd->bg_colors0 = COLOR(default_bg);
|
||||
rd->bg_opacities0 = os_window->is_semi_transparent ? os_window->background_opacity : 1.0f;
|
||||
#define SETBG(which) colorprofile_to_transparent_color(cp, which - 1, &rd->bg_colors##which, &rd->bg_opacities##which)
|
||||
SETBG(1); SETBG(2); SETBG(3); SETBG(4); SETBG(5); SETBG(6); SETBG(7);
|
||||
#undef SETBG
|
||||
// selection
|
||||
if (IS_SPECIAL_COLOR(highlight_fg)) {
|
||||
if (IS_SPECIAL_COLOR(highlight_bg)) {
|
||||
@@ -320,7 +327,6 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
||||
rd->use_cell_bg_for_selection_fg = 0.f; rd->use_cell_fg_for_selection_color = 0.f;
|
||||
}
|
||||
rd->use_cell_for_selection_bg = IS_SPECIAL_COLOR(highlight_bg) ? 1. : 0.;
|
||||
rd->second_transparent_bg = IS_SPECIAL_COLOR(second_transparent_bg) ? rd->default_bg : COLOR(second_transparent_bg);
|
||||
// Cursor position
|
||||
enum { BLOCK_IDX = 0, BEAM_IDX = NUM_UNDERLINE_STYLES + 3, UNDERLINE_IDX = NUM_UNDERLINE_STYLES + 4, UNFOCUSED_IDX = NUM_UNDERLINE_STYLES + 5 };
|
||||
Line *line_for_cursor = NULL;
|
||||
@@ -338,7 +344,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
||||
case CURSOR_HOLLOW:
|
||||
rd->cursor_fg_sprite_idx = UNFOCUSED_IDX; break;
|
||||
};
|
||||
color_type cell_fg = rd->default_fg, cell_bg = rd->default_bg;
|
||||
color_type cell_fg = rd->default_fg, cell_bg = rd->bg_colors0;
|
||||
index_type cell_color_x = cursor->x;
|
||||
bool reversed = false;
|
||||
if (cursor->x < screen->columns && cursor->y < screen->lines) {
|
||||
@@ -352,10 +358,10 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
||||
colors_for_cell(line_for_cursor, cp, &cell_color_x, &cell_fg, &cell_bg, &reversed);
|
||||
}
|
||||
if (IS_SPECIAL_COLOR(cursor_color)) {
|
||||
if (line_for_cursor) pick_cursor_color(line_for_cursor, cp, cell_fg, cell_bg, cell_color_x, &rd->cursor_fg, &rd->cursor_bg, rd->default_fg, rd->default_bg);
|
||||
else { rd->cursor_fg = rd->default_bg; rd->cursor_bg = rd->default_fg; }
|
||||
if (line_for_cursor) pick_cursor_color(line_for_cursor, cp, cell_fg, cell_bg, cell_color_x, &rd->cursor_fg, &rd->cursor_bg, rd->default_fg, rd->bg_colors0);
|
||||
else { rd->cursor_fg = rd->bg_colors0; rd->cursor_bg = rd->default_fg; }
|
||||
if (cell_bg == cell_fg) {
|
||||
rd->cursor_fg = rd->default_bg; rd->cursor_bg = rd->default_fg;
|
||||
rd->cursor_fg = rd->bg_colors0; rd->cursor_bg = rd->default_fg;
|
||||
} else { rd->cursor_fg = cell_bg; rd->cursor_bg = cell_fg; }
|
||||
} else {
|
||||
rd->cursor_bg = COLOR(cursor_color);
|
||||
@@ -375,7 +381,6 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
||||
sprite_tracker_current_layout(os_window->fonts_data, &x, &y, &z);
|
||||
rd->sprite_dx = 1.0f / (float)x; rd->sprite_dy = 1.0f / (float)y;
|
||||
rd->inverted = screen_invert_colors(screen) ? 1 : 0;
|
||||
rd->background_opacity = os_window->is_semi_transparent ? os_window->background_opacity : 1.0f;
|
||||
|
||||
#undef COLOR
|
||||
rd->url_color = OPT(url_color); rd->url_style = OPT(url_style);
|
||||
@@ -1082,13 +1087,15 @@ create_border_vao(void) {
|
||||
|
||||
void
|
||||
draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg, OSWindow *w) {
|
||||
float tint_opacity = w->is_semi_transparent ? w->background_opacity: 1.0f;
|
||||
float tint_premult = tint_opacity;
|
||||
float background_opacity = w->is_semi_transparent ? w->background_opacity: 1.0f;
|
||||
float tint_opacity = background_opacity;
|
||||
float tint_premult = background_opacity;
|
||||
if (has_bgimage(w)) {
|
||||
glEnable(GL_BLEND);
|
||||
BLEND_ONTO_OPAQUE;
|
||||
draw_background_image(w);
|
||||
BLEND_ONTO_OPAQUE;
|
||||
background_opacity = 1.0f;
|
||||
tint_opacity = OPT(background_tint) * OPT(background_tint_gaps);
|
||||
tint_premult = w->is_semi_transparent ? OPT(background_tint) : 1.0f;
|
||||
}
|
||||
@@ -1109,6 +1116,7 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
|
||||
w->tab_bar_edge_color.left, w->tab_bar_edge_color.right
|
||||
};
|
||||
glUniform1uiv(border_program_layout.uniforms.colors, arraysz(colors), colors);
|
||||
glUniform1f(border_program_layout.uniforms.background_opacity, background_opacity);
|
||||
glUniform1f(border_program_layout.uniforms.tint_opacity, tint_opacity);
|
||||
glUniform1f(border_program_layout.uniforms.tint_premult, tint_premult);
|
||||
glUniform2ui(border_program_layout.uniforms.viewport, viewport_width, viewport_height);
|
||||
|
||||
@@ -447,17 +447,46 @@ def process_remote_print(msg: memoryview) -> str:
|
||||
return replace_c0_codes_except_nl_space_tab(base64_decode(msg)).decode('utf-8', 'replace')
|
||||
|
||||
|
||||
def transparent_background_color_control(cp: ColorProfile, responses: dict[str, str], index: int, key: str, sep: str, val: str) -> None:
|
||||
if sep == '=':
|
||||
if val == '?':
|
||||
if index > 8:
|
||||
responses[key] = '?'
|
||||
else:
|
||||
c = cp.get_transparent_background_color(index - 1)
|
||||
if c is None:
|
||||
responses[key] = ''
|
||||
else:
|
||||
opacity = max(0, min(c.alpha / 255.0, 1))
|
||||
responses[key] = f'rgb:{c.red:02x}/{c.green:02x}/{c.blue:02x}@{opacity:.4f}'
|
||||
elif index <= 8:
|
||||
col, _, o = val.partition('@')
|
||||
try:
|
||||
opacity = float(o)
|
||||
except Exception:
|
||||
opacity = -1.0
|
||||
c = to_color(col)
|
||||
if c is not None:
|
||||
cp.set_transparent_background_color(index - 1, c, opacity)
|
||||
elif index <= 8:
|
||||
cp.set_transparent_background_color(index - 1)
|
||||
|
||||
|
||||
def color_control(cp: ColorProfile, code: int, value: Union[str, bytes, memoryview] = '') -> str:
|
||||
if isinstance(value, (bytes, memoryview)):
|
||||
value = str(value, 'utf-8', 'replace')
|
||||
responses = {}
|
||||
responses: dict[str, str] = {}
|
||||
for rec in value.split(';'):
|
||||
key, sep, val = rec.partition('=')
|
||||
if key.startswith('transparent_background_color'):
|
||||
index = int(key[len('transparent_background_color'):])
|
||||
transparent_background_color_control(cp, responses, index, key, sep, val)
|
||||
continue
|
||||
attr = {
|
||||
'foreground': 'default_fg', 'background': 'default_bg',
|
||||
'selection_background': 'highlight_bg', 'selection_foreground': 'highlight_fg',
|
||||
'cursor': 'cursor_color', 'cursor_text': 'cursor_text_color',
|
||||
'visual_bell': 'visual_bell_color', 'second_transparent_background': 'second_transparent_bg',
|
||||
'visual_bell': 'visual_bell_color',
|
||||
}.get(key, '')
|
||||
colnum = -1
|
||||
with suppress(Exception):
|
||||
@@ -480,6 +509,7 @@ def color_control(cp: ColorProfile, code: int, value: Union[str, bytes, memoryvi
|
||||
else:
|
||||
if attr:
|
||||
if val:
|
||||
val = val.partition('@')[0]
|
||||
col = to_color(val)
|
||||
if col is not None:
|
||||
setattr(cp, attr, col)
|
||||
@@ -488,6 +518,7 @@ def color_control(cp: ColorProfile, code: int, value: Union[str, bytes, memoryvi
|
||||
setattr(cp, attr, None)
|
||||
else:
|
||||
if 0 <= colnum <= 255:
|
||||
val = val.partition('@')[0]
|
||||
col = to_color(val)
|
||||
if col is not None:
|
||||
cp.set_color(colnum, color_as_int(col))
|
||||
@@ -774,7 +805,7 @@ class Window:
|
||||
return tab.overlay_parent(self)
|
||||
|
||||
@property
|
||||
def current_colors(self) -> dict[str, Optional[int]]:
|
||||
def current_colors(self) -> dict[str, Union[int, None, tuple[tuple[Color, float], ...]]]:
|
||||
return self.screen.color_profile.as_dict()
|
||||
|
||||
@property
|
||||
|
||||
@@ -59,6 +59,8 @@ class Callbacks:
|
||||
response = color_control(self.color_profile, code, data)
|
||||
if response:
|
||||
def p(x):
|
||||
if '@' in x:
|
||||
return (to_color(x.partition('@')[0]), int(255 * float(x.partition('@')[2])))
|
||||
ans = to_color(x)
|
||||
if ans is None:
|
||||
ans = x
|
||||
|
||||
@@ -1280,3 +1280,10 @@ class TestScreen(BaseTest):
|
||||
q({'selection_background': ''})
|
||||
self.assertIsNone(s.color_profile.highlight_bg)
|
||||
q({'selection_background': '?'}, {'selection_background': ''})
|
||||
s.color_profile.reload_from_opts(defaults)
|
||||
q({'transparent_background_color9': '?'}, {'transparent_background_color9': '?'})
|
||||
q({'transparent_background_color2': '?'}, {'transparent_background_color2': ''})
|
||||
q({'transparent_background_color2': 'red@0.5'})
|
||||
q({'transparent_background_color2': '?'}, {'transparent_background_color2': (Color(255, 0, 0), 126)})
|
||||
q({'transparent_background_color2': '#ffffff@-1'})
|
||||
q({'transparent_background_color2': '?'}, {'transparent_background_color2': (Color(255, 255, 255), 255)})
|
||||
|
||||
@@ -15,15 +15,14 @@ import (
|
||||
var nullable_colors = map[string]bool{
|
||||
// generated by gen-config.py do not edit
|
||||
// NULLABLE_COLORS_START
|
||||
"active_border_color": true,
|
||||
"cursor": true,
|
||||
"cursor_text_color": true,
|
||||
"second_transparent_bg": true,
|
||||
"selection_background": true,
|
||||
"selection_foreground": true,
|
||||
"tab_bar_background": true,
|
||||
"tab_bar_margin_color": true,
|
||||
"visual_bell_color": true,
|
||||
"active_border_color": true,
|
||||
"cursor": true,
|
||||
"cursor_text_color": true,
|
||||
"selection_background": true,
|
||||
"selection_foreground": true,
|
||||
"tab_bar_background": true,
|
||||
"tab_bar_margin_color": true,
|
||||
"visual_bell_color": true,
|
||||
// NULLABLE_COLORS_END
|
||||
}
|
||||
|
||||
@@ -36,6 +35,8 @@ func set_color_in_color_map(key, val string, ans map[string]any, check_nullable,
|
||||
return fmt.Errorf("The color %s cannot be set to none", key)
|
||||
}
|
||||
ans[key] = nil
|
||||
} else if key == "transparent_background_colors" {
|
||||
ans[key] = val
|
||||
} else {
|
||||
col, err := style.ParseColor(val)
|
||||
if err != nil {
|
||||
|
||||
@@ -309,7 +309,6 @@ var AllColorSettingNames = map[string]bool{ // {{{
|
||||
"mark2_foreground": true,
|
||||
"mark3_background": true,
|
||||
"mark3_foreground": true,
|
||||
"second_transparent_bg": true,
|
||||
"selection_background": true,
|
||||
"selection_foreground": true,
|
||||
"tab_bar_background": true,
|
||||
|
||||
Reference in New Issue
Block a user