Compare commits

..

8 Commits

Author SHA1 Message Date
Kovid Goyal
bb452f2066 Do not try to auto install kitten when generating completions
See https://github.com/kovidgoyal/kitty/discussions/10218
2026-07-03 23:30:59 +05:30
Kovid Goyal
0bb8502f31 Increase timeout for receiving capabilities from notification daemon
Fixes #10215
2026-07-03 13:43:41 +05:30
Kovid Goyal
4feb167e99 Cleanup previous PR 2026-07-03 11:08:14 +05:30
Kovid Goyal
28cbe0188d Merge branch 'copilot/fix-tab-bar-border-edges' of https://github.com/kovidgoyal/kitty 2026-07-03 11:08:09 +05:30
copilot-swe-agent[bot]
0b27865625 Address review comments: clarify reversed reset and vertical NULL params 2026-07-03 04:05:40 +00:00
copilot-swe-agent[bot]
6fb8ff1829 Fix tab bar edge strips: use bg when edge cell has default color 2026-07-03 04:01:56 +00:00
Kovid Goyal
9e92c57af3 Bump dependency for CVE 2026-07-03 09:07:55 +05:30
Kovid Goyal
3313e5a6be Ignore inapplicable CVE 2026-07-03 08:58:07 +05:30
10 changed files with 97 additions and 26 deletions

View File

@@ -236,6 +236,7 @@ IGNORED_DEPENDENCY_CVES = [
'CVE-2026-7210', # DoS in unused XML parser
'CVE-2026-3276', # DoS in unicodedata.normalize()
'CVE-2026-7774', # tarfile.data_filter path traversal bypass
'CVE-2026-12003', # bug in release builds irrelevant to us
# github.com/nwaples/rardecode/v2
'CVE-2025-11579', # rardecode is version 2.2.1, not vulnerable
'CVE-2026-2673', # openssl fix not released

View File

@@ -202,12 +202,12 @@
},
{
"name": "glib 2.86.3",
"name": "glib 2.88.1",
"os": "linux",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:b3211d8d34b9df5dca05787ef0ad5d7ca75dec998b970e1aab0001d229977c65",
"urls": ["https://download.gnome.org/sources/glib/{version_except_last}/{filename}"]
"hash": "sha256:51ab804c56f6eab3e5045c774d1290ac5e4c923d4f9a3d8e33123bee45c1840e",
"urls": ["https://ftp.gnome.org/pub/GNOME/sources/glib/{version_except_last}/{filename}"]
}
},

2
glfw/linux_notify.c vendored
View File

@@ -123,7 +123,7 @@ got_capabilities(DBusMessage *msg, const DBusError* err, void* data UNUSED) {
static bool
get_capabilities(DBusConnection *session_bus) {
return glfw_dbus_call_method_with_reply(session_bus, NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE, "GetCapabilities", 60, got_capabilities, NULL, DBUS_TYPE_INVALID);
return glfw_dbus_call_method_with_reply(session_bus, NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE, "GetCapabilities", 1000, got_capabilities, NULL, DBUS_TYPE_INVALID);
}
notification_id_type

View File

@@ -1775,7 +1775,7 @@ def layer_shell_config_for_os_window(os_window_id: int) -> dict[str, Any] | None
def set_layer_shell_config(os_window_id: int, cfg: LayerShellConfig) -> bool: ...
def wrapped_kitten_names() -> List[str]: ...
def expand_ansi_c_escapes(test: str) -> str: ...
def update_tab_bar_edge_colors(os_window_id: int) -> bool: ...
def update_tab_bar_edge_colors(os_window_id: int, is_vertical: bool = False) -> tuple[bool, bool] | None: ...
def mask_kitty_signals_process_wide() -> None: ...
def is_modifier_key(key: int) -> bool: ...
def base64_encode(src: Union[str, ReadableBuffer], add_padding: bool = False) -> bytes: ...

View File

@@ -3439,23 +3439,33 @@ effective_cell_edge_color(char_type ch, color_type fg, color_type bg, bool is_le
bool
get_line_edge_colors(Screen *self, color_type *left, color_type *right) {
// Return the color at the left and right edges of the line with the cursor on it
Line *line = range_line_(self, self->cursor->y);
get_line_edge_colors_at_row(Screen *self, index_type y, color_type *left, color_type *right, bool *left_is_default, bool *right_is_default) {
// Return the color at the left and right edges of the specified row.
// Any of the output pointers may be NULL if that value is not needed.
Line *line = range_line_(self, y);
if (!line) return false;
color_type left_cell_fg = OPT(foreground), left_cell_bg = OPT(background), right_cell_bg = OPT(background), right_cell_fg = OPT(foreground);
index_type cell_color_x = 0;
char_type left_char = line_get_char(line, cell_color_x);
bool reversed = false;
colors_for_cell(line, self->color_profile, &cell_color_x, &left_cell_fg, &left_cell_bg, &reversed);
if (left_is_default) *left_is_default = (line->gpu_cells[cell_color_x].bg & 0xff) == 0;
if (left) *left = effective_cell_edge_color(left_char, left_cell_fg, left_cell_bg, true);
if (line->xnum > 0) cell_color_x = line->xnum - 1;
char_type right_char = line_get_char(line, cell_color_x);
reversed = false; // reset: colors_for_cell only sets this flag, never clears it
colors_for_cell(line, self->color_profile, &cell_color_x, &right_cell_fg, &right_cell_bg, &reversed);
*left = effective_cell_edge_color(left_char, left_cell_fg, left_cell_bg, true);
*right = effective_cell_edge_color(right_char, right_cell_fg, right_cell_bg, false);
if (right_is_default) *right_is_default = (line->gpu_cells[cell_color_x].bg & 0xff) == 0;
if (right) *right = effective_cell_edge_color(right_char, right_cell_fg, right_cell_bg, false);
return true;
}
bool
get_line_edge_colors(Screen *self, color_type *left, color_type *right) {
// Return the color at the left and right edges of the line with the cursor on it
return get_line_edge_colors_at_row(self, self->cursor->y, left, right, NULL, NULL);
}
static void
update_line_data(Line *line, unsigned int dest_y, uint8_t *data) {

View File

@@ -332,6 +332,7 @@ bool screen_prompt_supports_click_events(const Screen *, bool *is_relative);
bool screen_fake_move_cursor_to_position(Screen *, index_type x, index_type y);
bool screen_send_signal_for_key(Screen *, char key);
bool get_line_edge_colors(Screen *self, color_type *left, color_type *right);
bool get_line_edge_colors_at_row(Screen *self, index_type y, color_type *left, color_type *right, bool *left_is_default, bool *right_is_default);
bool parse_sgr(Screen *screen, const uint8_t *buf, unsigned int num, const char *report_name, bool is_deccara);
bool screen_pause_rendering(Screen *self, bool pause, int for_in_ms);
void screen_check_pause_rendering(Screen *self, monotonic_t now);

View File

@@ -1404,13 +1404,41 @@ PYWRAP1(patch_global_colors) {
PYWRAP1(update_tab_bar_edge_colors) {
id_type os_window_id;
PA("K", &os_window_id);
int is_vertical = 0;
PA("K|p", &os_window_id, &is_vertical);
WITH_OS_WINDOW(os_window_id)
if (os_window->tab_bar_render_data.screen) {
if (get_line_edge_colors(os_window->tab_bar_render_data.screen, &os_window->tab_bar_edge_color.left, &os_window->tab_bar_edge_color.right)) { Py_RETURN_TRUE; }
Screen *screen = os_window->tab_bar_render_data.screen;
if (screen) {
bool left_is_default = true, right_is_default = true;
bool ok;
if (!is_vertical) {
ok = get_line_edge_colors_at_row(
screen, screen->cursor->y,
&os_window->tab_bar_edge_color.left,
&os_window->tab_bar_edge_color.right,
&left_is_default, &right_is_default);
} else {
color_type top_color = 0, bottom_color = 0;
bool top_is_default = true, bottom_is_default = true;
// For vertical bars we only need the left-edge color of each row (the
// right-edge output is unused since tabs span the full row width).
ok = get_line_edge_colors_at_row(screen, 0, &top_color, NULL, &top_is_default, NULL) &&
get_line_edge_colors_at_row(screen, screen->lines - 1, &bottom_color, NULL, &bottom_is_default, NULL);
if (ok) {
os_window->tab_bar_edge_color.left = top_color;
os_window->tab_bar_edge_color.right = bottom_color;
left_is_default = top_is_default;
right_is_default = bottom_is_default;
}
}
if (ok) {
return Py_BuildValue("OO",
left_is_default ? Py_True : Py_False,
right_is_default ? Py_True : Py_False);
}
}
END_WITH_OS_WINDOW
Py_RETURN_FALSE;
Py_RETURN_NONE;
}
static PyObject*

View File

@@ -619,6 +619,9 @@ class TabBar:
self.blank_rects: tuple[Border, ...] = ()
self.tab_extents: Sequence[TabExtent] = ()
self.laid_out_once = False
self.left_edge_is_default = True
self.right_edge_is_default = True
self._last_viewport: tuple[Region, Region, int, int] | None = None
self.apply_options()
def apply_options(self) -> None:
@@ -753,18 +756,23 @@ class TabBar:
blank_rects.append(Border(0, tab_bar.bottom, vw, central.top, bg))
g = self.window_geometry
if self.is_vertical:
if opts.tab_bar_margin_color is None:
top_bg = bg if self.left_edge_is_default else BorderColor.tab_bar_left_edge_color
bottom_bg = bg if self.right_edge_is_default else BorderColor.tab_bar_right_edge_color
else:
top_bg = bottom_bg = bg
if g.left > tab_bar.left:
blank_rects.append(Border(tab_bar.left, g.top, g.left, g.bottom, bg))
if g.right < tab_bar.right:
blank_rects.append(Border(g.right, g.top, tab_bar.right, g.bottom, bg))
if g.top > tab_bar.top:
blank_rects.append(Border(g.left, tab_bar.top, g.right, g.top, bg))
blank_rects.append(Border(g.left, tab_bar.top, g.right, g.top, top_bg))
if g.bottom < tab_bar.bottom:
blank_rects.append(Border(g.left, g.bottom, g.right, tab_bar.bottom, bg))
blank_rects.append(Border(g.left, g.bottom, g.right, tab_bar.bottom, bottom_bg))
else:
if opts.tab_bar_margin_color is None:
left_bg = BorderColor.tab_bar_left_edge_color
right_bg = BorderColor.tab_bar_right_edge_color
left_bg = bg if self.left_edge_is_default else BorderColor.tab_bar_left_edge_color
right_bg = bg if self.right_edge_is_default else BorderColor.tab_bar_right_edge_color
else:
left_bg = right_bg = bg
if g.left > tab_bar.left:
@@ -811,15 +819,31 @@ class TabBar:
self.window_geometry = g = WindowGeometry(
left_margin, tab_bar.top, left_margin + cell_area_width, tab_bar.bottom, s.columns, s.lines)
self.laid_out_once = True
self._last_viewport = (central, tab_bar, vw, vh)
self.update_blank_rects(central, tab_bar, vw, vh)
set_tab_bar_render_data(self.os_window_id, self.screen, *g[:4])
def update(self, data: Sequence[TabBarData]) -> None:
def _update_edge_defaults(self, is_vertical: bool) -> bool:
'''Call update_tab_bar_edge_colors, update cached is-default flags.
Returns True when the flags changed and blank_rects need rebuilding.'''
result = update_tab_bar_edge_colors(self.os_window_id, is_vertical)
if result is None:
return False
left_is_default, right_is_default = result
if left_is_default != self.left_edge_is_default or right_is_default != self.right_edge_is_default:
self.left_edge_is_default = left_is_default
self.right_edge_is_default = right_is_default
if self._last_viewport is not None:
self.update_blank_rects(*self._last_viewport)
return True
return False
def update(self, data: Sequence[TabBarData]) -> bool:
if not self.laid_out_once:
return
return False
if self.is_vertical:
self.update_vertical(data)
return
return self.update_vertical(data)
s = self.screen
last_tab = data[-1] if data else None
ed = ExtraData()
@@ -884,16 +908,16 @@ class TabBar:
self.tab_extents = cr
s.erase_in_line(0, False) # Ensure no long titles bleed after the last tab
self.align()
update_tab_bar_edge_colors(self.os_window_id)
return self._update_edge_defaults(False)
def update_vertical(self, data: Sequence[TabBarData]) -> None:
def update_vertical(self, data: Sequence[TabBarData]) -> bool:
s = self.screen
self.last_laid_out_tabs = data
self.tab_extents = ()
s.cursor.x = s.cursor.y = 0
s.erase_in_display(2, False)
if not data:
return
return self._update_edge_defaults(True)
max_tab_length = max(1, s.columns - 1)
tab_line_height = max(1, min(MAX_VERTICAL_TAB_LINES, s.lines // max(1, len(data))))
rows_to_draw = min(len(data), max(1, s.lines // tab_line_height))
@@ -926,6 +950,7 @@ class TabBar:
s.cursor.fg = as_rgb(0xff0000)
s.draw('')
self.tab_extents = tuple(cr)
return self._update_edge_defaults(True)
def align_with_factor(self, factor: int = 1) -> None:
if not self.tab_extents:

View File

@@ -1330,7 +1330,9 @@ class TabManager: # {{{
watcher(boss, w, data)
def update_tab_bar_data(self) -> None:
self.tab_bar.update(self.tab_bar_data)
if self.tab_bar.update(self.tab_bar_data):
for tab in self.tabs:
tab.relayout_borders()
def title_changed(self, tab: Tab) -> None:
self.mark_tab_bar_dirty()

View File

@@ -40,6 +40,10 @@ embed_exe="$(command dirname "$script_dir")/install/bin/kitten"
exec_kitty "$@"
}
# If called for shell completion with no binary available, do nothing to avoid
# downloading kitten and showing spurious output during tab completion.
[ "$1" = "__complete__" ] && exit 0
case "$(command uname)" in
'Linux') OS="linux";;
'Darwin') OS="darwin";;