diff --git a/docs/protocol-extensions.rst b/docs/protocol-extensions.rst index 3d2785175f2..0e177eb7228 100644 --- a/docs/protocol-extensions.rst +++ b/docs/protocol-extensions.rst @@ -180,23 +180,24 @@ rectangular region of the screen from (3, 4) to (10, 11), you use:: [2*x[4;3;11;10;44$r[*x -Saving and restoring the default foreground/background/selection/cursor colors +Saving and restoring colors --------------------------------------------------------------------------------- -It is often useful for a full screen application with its own color themes -to set the default foreground, background, selection and cursor colors. This -allows for various performance optimizations when drawing the screen. The -problem is that if the user previously used the escape codes to change these -colors herself, then running the full screen application will lose her -changes even after it exits. To avoid this, kitty introduces a new pair of -*OSC* escape codes to push and pop the current color values from a stack:: +It is often useful for a full screen application with its own color themes to +set the default foreground, background, selection and cursor colors and the +ANSI color table. This allows for various performance optimizations when +drawing the screen. The problem is that if the user previously used the escape +codes to change these colors herself, then running the full screen application +will lose her changes even after it exits. To avoid this, kitty introduces a +new pair of *OSC* escape codes to push and pop the current color values from a +stack:: ]30001\ # push onto stack ]30101\ # pop from stack -These escape codes save/restore the so called *dynamic colors*, default +These escape codes save/restore the colors, default background, default foreground, selection background, selection foreground and -cursor color. +cursor color and the 256 colors of the ANSI color table. Pasting to clipboard diff --git a/kitty/colors.c b/kitty/colors.c index a5334057784..394b0b1f05b 100644 --- a/kitty/colors.c +++ b/kitty/colors.c @@ -318,19 +318,56 @@ copy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size self->dirty = false; } -void -colorprofile_push_dynamic_colors(ColorProfile *self) { - if (self->dynamic_color_stack_idx >= arraysz(self->dynamic_color_stack)) { - memmove(self->dynamic_color_stack, self->dynamic_color_stack + 1, sizeof(self->dynamic_color_stack) - sizeof(self->dynamic_color_stack[0])); - self->dynamic_color_stack_idx = arraysz(self->dynamic_color_stack) - 1; +static void +push_onto_color_stack_at(ColorProfile *self, unsigned int i) { + self->color_stack[i].dynamic_colors = self->overridden; + self->color_stack[i].valid = true; + memcpy(self->color_stack[i].color_table, self->color_table, sizeof(self->color_stack->color_table)); +} + +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)); +} + +bool +colorprofile_push_colors(ColorProfile *self, unsigned int idx) { + if (idx == 0) { + for (unsigned i = 0; i < arraysz(self->color_stack); i++) { + if (!self->color_stack[i].valid) { + push_onto_color_stack_at(self, i); + return true; + } + } + memmove(self->color_stack, self->color_stack + 1, sizeof(self->color_stack) - sizeof(self->color_stack[0])); + push_onto_color_stack_at(self, arraysz(self->color_stack) - 1); + return true; + } + if (idx < arraysz(self->color_stack)) { + push_onto_color_stack_at(self, idx); + return true; } - self->dynamic_color_stack[self->dynamic_color_stack_idx++] = self->overridden; + return false; } -void -colorprofile_pop_dynamic_colors(ColorProfile *self) { - if (!self->dynamic_color_stack_idx) return; - self->overridden = self->dynamic_color_stack[--(self->dynamic_color_stack_idx)]; +bool +colorprofile_pop_colors(ColorProfile *self, unsigned int idx) { + if (idx == 0) { + for (unsigned i = arraysz(self->color_stack) - 1; i-- > 0; ) { + if (self->color_stack[i].valid) { + copy_from_color_stack_at(self, i); + self->color_stack[i].valid = false; + return true; + } + } + return false; + } + if (idx < arraysz(self->color_stack)) { + copy_from_color_stack_at(self, idx); + return true; + } + return false; } static PyObject* diff --git a/kitty/data-types.h b/kitty/data-types.h index f042513b6fd..b324334bc21 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -237,14 +237,20 @@ typedef struct { color_type default_fg, default_bg, cursor_color, cursor_text_color, cursor_text_uses_bg, highlight_fg, highlight_bg; } DynamicColor; + +typedef struct { + DynamicColor dynamic_colors; + uint32_t color_table[256]; + bool valid; +} ColorStackEntry; + typedef struct { PyObject_HEAD bool dirty; uint32_t color_table[256]; uint32_t orig_color_table[256]; - DynamicColor dynamic_color_stack[10]; - size_t dynamic_color_stack_idx; + ColorStackEntry color_stack[16]; DynamicColor configured, overridden; color_type mark_foregrounds[MARK_MASK+1], mark_backgrounds[MARK_MASK+1]; } ColorProfile; @@ -304,8 +310,8 @@ bool set_iutf8(int, bool); color_type colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval); float cursor_text_as_bg(ColorProfile *self); void copy_color_table_to_buffer(ColorProfile *self, color_type *address, int offset, size_t stride); -void colorprofile_push_dynamic_colors(ColorProfile*); -void colorprofile_pop_dynamic_colors(ColorProfile*); +bool colorprofile_push_colors(ColorProfile*, unsigned int); +bool colorprofile_pop_colors(ColorProfile*, unsigned int); void set_mouse_cursor(MouseShape); void enter_event(void); diff --git a/kitty/parser.c b/kitty/parser.c index 5072843e084..d05b59b9806 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -414,11 +414,11 @@ dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) { END_DISPATCH case 30001: REPORT_COMMAND(screen_push_dynamic_colors); - screen_push_dynamic_colors(screen); + screen_push_colors(screen, 0); break; case 30101: REPORT_COMMAND(screen_pop_dynamic_colors); - screen_pop_dynamic_colors(screen); + screen_pop_colors(screen, 0); break; default: REPORT_ERROR("Unknown OSC code: %u", code); diff --git a/kitty/screen.c b/kitty/screen.c index 8d31ca03b04..3810116b029 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1594,13 +1594,13 @@ screen_handle_cmd(Screen *self, PyObject *cmd) { } void -screen_push_dynamic_colors(Screen *self) { - colorprofile_push_dynamic_colors(self->color_profile); +screen_push_colors(Screen *self, unsigned int idx) { + colorprofile_push_colors(self->color_profile, idx); } void -screen_pop_dynamic_colors(Screen *self) { - colorprofile_pop_dynamic_colors(self->color_profile); +screen_pop_colors(Screen *self, unsigned int idx) { + colorprofile_pop_colors(self->color_profile, idx); } void diff --git a/kitty/screen.h b/kitty/screen.h index 6d97aeb9614..b99ac8ddba6 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -179,8 +179,8 @@ void screen_erase_characters(Screen *self, unsigned int count); void screen_set_margins(Screen *self, unsigned int top, unsigned int bottom); void screen_change_charset(Screen *, uint32_t to); void screen_handle_cmd(Screen *, PyObject *cmd); -void screen_push_dynamic_colors(Screen *); -void screen_pop_dynamic_colors(Screen *); +void screen_push_colors(Screen *, unsigned int); +void screen_pop_colors(Screen *, unsigned int); void screen_handle_print(Screen *, PyObject *cmd); void screen_designate_charset(Screen *, uint32_t which, uint32_t as); void screen_use_latin1(Screen *, bool);