Compare commits

..

23 Commits

Author SHA1 Message Date
Kovid Goyal
c127517c96 version 0.36.4 2024-09-27 10:20:49 +05:30
Kovid Goyal
f8bdc3d86b Fix #7904 2024-09-26 21:53:37 +05:30
Kovid Goyal
6834e366be 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
Fixes #7892
2024-09-26 01:44:14 +05:30
Kovid Goyal
866c14249b fix minor memory leak in buffers 2024-09-26 01:32:36 +05:30
Kovid Goyal
271665dbaf Temporary workaround for CVDisplayLink deprecation
Apple simply cannot get its APIs right on the first try, ever.
2024-09-26 01:13:10 +05:30
Kovid Goyal
0a7812ab20 Add nil check in send 2024-09-26 00:55:45 +05:30
Kovid Goyal
6f93fb22a4 Update changelog 2024-09-26 00:52:36 +05:30
Kovid Goyal
f48bcb1a17 Wayland GNOME: Fix a crash when using multiple monitors with different scales and starting on or moving to the monitor with lower scale
Fucking GNOME and its fucking lack of support for SSD. How much of my
life I have wasted on these nincompoops.

Fixes #7894
2024-09-26 00:49:04 +05:30
Kovid Goyal
216b6e2e8c Bell color should also not be rendered translucent 2024-09-26 00:29:20 +05:30
Kovid Goyal
cb13233606 Proper fix for always drawing borders opaque 2024-09-26 00:08:36 +05:30
Kovid Goyal
b854589761 Revert "Fix a regression when tinting of background images was introduced that caused window borders to have background_opacity applied to them"
This reverts commit 33e4a0f9cc.
Fixes #7895
2024-09-25 23:28:38 +05:30
Kovid Goyal
44039baa39 version 0.36.3 2024-09-25 09:34:28 +05:30
Kovid Goyal
100f472a08 Clarify docs 2024-09-24 20:15:39 +05:30
Kovid Goyal
126fca0224 ... 2024-09-24 19:34:56 +05:30
Kovid Goyal
dc9eefe050 ... 2024-09-24 19:17:27 +05:30
Kovid Goyal
c1fb18a6ef Implement changing transparent background colors via remote control 2024-09-24 19:02:13 +05:30
Kovid Goyal
c3130419a7 Implement dynamic control of transparent background colors via escape code
Still have to implement it via remote control
2024-09-24 19:02:13 +05:30
Kovid Goyal
dbfeb8d6a4 Store transparent colors on ColorProfile
This will eventually allow them to be changed using remote control and
escape codes.
2024-09-24 19:02:13 +05:30
Kovid Goyal
6ca187c42c Replace the second_transparent_bg option
This is backwards incompatible, but only for a feature released 3 weeks
ago.
2024-09-24 19:02:13 +05:30
Kovid Goyal
e78e86572e Prepare for allowing upto seven additional semi-transparent background colors 2024-09-24 19:02:13 +05:30
Kovid Goyal
d02b1c0b31 ... 2024-09-24 14:38:11 +05:30
Kovid Goyal
83c989d4e9 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
Fixes #7887
2024-09-24 14:21:23 +05:30
Kovid Goyal
958ad0d8b4 Remote control: Fix --match=state:self not working 2024-09-24 14:09:27 +05:30
23 changed files with 160 additions and 66 deletions

View File

@@ -74,7 +74,17 @@ 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`)
@@ -93,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]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -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

View File

@@ -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>

View File

@@ -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"

View File

@@ -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;

View File

@@ -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)

View File

@@ -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'''

View File

@@ -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);
}

View File

@@ -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()

View File

@@ -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) { \
@@ -82,15 +93,9 @@ set_configured_colors(ColorProfile *self, PyObject *opts) {
n(highlight_fg, selection_foreground); n(highlight_bg, selection_background);
n(visual_bell_color, visual_bell_color);
#undef n
memset(self->configured_transparent_colors, 0, sizeof(self->configured_transparent_colors));
RAII_PyObject(src, PyObject_GetAttrString(opts, "transparent_background_colors"));
if (!src) { PyErr_SetString(PyExc_TypeError, "No transparent_background_colors on opts object"); return false; }
for (Py_ssize_t i = 0; i < MIN(PyTuple_GET_SIZE(src), (Py_ssize_t)arraysz(self->configured_transparent_colors)); i++) {
PyObject *e = PyTuple_GET_ITEM(src, i);
self->configured_transparent_colors[i].color = ((Color*)(PyTuple_GET_ITEM(e, 0)))->color.val & 0xffffff;
self->configured_transparent_colors[i].opacity = (float)PyFloat_AsDouble(PyTuple_GET_ITEM(e, 1));
self->configured_transparent_colors[i].is_set = true;
}
set_transparent_background_colors(self->configured_transparent_colors, src);
return PyErr_Occurred() ? false : true;
}
@@ -204,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);
@@ -239,7 +244,12 @@ patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
S(cursor_text_color, cursor_text_color); S(visual_bell_color, visual_bell_color);
#undef SI
#undef S
// TODO: Patch transparent_colors
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;
}
@@ -293,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) { \
@@ -317,18 +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);
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
// TODO: Add transparent_colors
return ans;
return Py_NewRef(ans);
}
static PyObject*
@@ -478,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); \

View File

@@ -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

View File

@@ -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];

View File

@@ -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;

View File

@@ -811,7 +811,7 @@ class ColorProfile:
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,7 +833,8 @@ class ColorProfile:
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

View File

@@ -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]]:

View File

@@ -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

View File

@@ -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):

View File

@@ -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()

View File

@@ -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 = ''

View File

@@ -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:

View File

@@ -1087,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;
}
@@ -1114,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);

View File

@@ -805,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

View File

@@ -35,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 {