mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-03 21:23:43 +02:00
Compare commits
8 Commits
copilot/fi
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb452f2066 | ||
|
|
0bb8502f31 | ||
|
|
4feb167e99 | ||
|
|
28cbe0188d | ||
|
|
0b27865625 | ||
|
|
6fb8ff1829 | ||
|
|
9e92c57af3 | ||
|
|
3313e5a6be |
1
.github/workflows/ci.py
vendored
1
.github/workflows/ci.py
vendored
@@ -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
|
||||
|
||||
@@ -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
2
glfw/linux_notify.c
vendored
@@ -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
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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";;
|
||||
|
||||
Reference in New Issue
Block a user