diff --git a/kitty/keys.c b/kitty/keys.c index 8a018e686..b1a26534e 100644 --- a/kitty/keys.c +++ b/kitty/keys.c @@ -131,6 +131,7 @@ on_key_input(int key, int scancode, int action, int mods, const char* text, int switch(state) { case 1: // update pre-edit text update_ime_position(global_state.callback_os_window, w, screen); + screen_draw_overlay_text(screen, text); return; case 2: // commit text if (text && *text) { diff --git a/kitty/lineops.h b/kitty/lineops.h index 6592f81c7..3a9d7e762 100644 --- a/kitty/lineops.h +++ b/kitty/lineops.h @@ -41,6 +41,17 @@ xlimit_for_line(Line *line) { return xlimit; } +static inline void +line_save_cells(Line *line, index_type start, index_type num, GPUCell *gpu_cells, CPUCell *cpu_cells) { + memcpy(gpu_cells + sizeof(GPUCell) * start, line->gpu_cells + sizeof(GPUCell) * start, sizeof(GPUCell) * num); + memcpy(cpu_cells + sizeof(CPUCell) * start, line->cpu_cells + sizeof(CPUCell) * start, sizeof(CPUCell) * num); +} + +static inline void +line_reset_cells(Line *line, index_type start, index_type num, GPUCell *gpu_cells, CPUCell *cpu_cells) { + memcpy(line->gpu_cells + sizeof(GPUCell) * start, gpu_cells + sizeof(GPUCell) * start, sizeof(GPUCell) * num); + memcpy(line->cpu_cells + sizeof(CPUCell) * start, cpu_cells + sizeof(CPUCell) * start, sizeof(CPUCell) * num); +} void line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch); void line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, bool clear_char); diff --git a/kitty/screen.c b/kitty/screen.c index 12babc22f..8d0407a53 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -30,7 +30,6 @@ typedef struct { unsigned int x; int y; } FullSelectionBoundary; - // Constructor/destructor {{{ static inline void @@ -41,6 +40,22 @@ init_tabstops(bool *tabstops, index_type count) { } } +static inline bool +init_overlay_line(Screen *self, index_type columns) { + free(self->overlay_line.cpu_cells); + free(self->overlay_line.gpu_cells); + self->overlay_line.cpu_cells = PyMem_Calloc(columns, sizeof(CPUCell)); + self->overlay_line.gpu_cells = PyMem_Calloc(columns, sizeof(GPUCell)); + if (!self->overlay_line.cpu_cells || !self->overlay_line.gpu_cells) { + PyErr_NoMemory(); return false; + } + self->overlay_line.is_active = false; + self->overlay_line.xnum = 0; + self->overlay_line.ynum = 0; + self->overlay_line.xstart = 0; + return true; +} + #define RESET_CHARSETS \ self->g0_charset = translation_table(0); \ self->g1_charset = self->g0_charset; \ @@ -104,13 +119,18 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { self->tabstops = self->main_tabstops; init_tabstops(self->main_tabstops, self->columns); init_tabstops(self->alt_tabstops, self->columns); + if (!init_overlay_line(self, self->columns)) { Py_CLEAR(self); return NULL; } } return (PyObject*) self; } +static void deactivate_overlay_line(Screen *self); +static inline Line* range_line_(Screen *self, int y); + void screen_reset(Screen *self) { if (self->linebuf == self->alt_linebuf) screen_toggle_screen_buffer(self); + if (self->overlay_line.is_active) deactivate_overlay_line(self); linebuf_clear(self->linebuf, BLANK_CHAR); historybuf_clear(self->historybuf); grman_clear(self->grman, false, self->cell_size); @@ -159,6 +179,7 @@ realloc_lb(LineBuf *old, unsigned int lines, unsigned int columns, index_type *n static bool screen_resize(Screen *self, unsigned int lines, unsigned int columns) { + if (self->overlay_line.is_active) deactivate_overlay_line(self); lines = MAX(1, lines); columns = MAX(1, columns); bool is_main = self->linebuf == self->main_linebuf; @@ -170,6 +191,8 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) { cursor_is_beyond_content = num_content_lines_before > 0 && self->cursor->y >= num_content_lines_before; \ num_content_lines = num_content_lines_after; \ } + // Resize overlay line + if (!init_overlay_line(self, columns)) return false; // Resize main linebuf HistoryBuf *nh = realloc_hb(self->historybuf, self->historybuf->ynum, columns); @@ -246,6 +269,8 @@ dealloc(Screen* self) { Py_CLEAR(self->alt_linebuf); Py_CLEAR(self->historybuf); Py_CLEAR(self->color_profile); + PyMem_Free(self->overlay_line.cpu_cells); + PyMem_Free(self->overlay_line.gpu_cells); PyMem_Free(self->main_tabstops); Py_TYPE(self)->tp_free((PyObject*)self); } // }}} @@ -374,6 +399,37 @@ screen_draw(Screen *self, uint32_t och) { linebuf_mark_line_dirty(self->linebuf, self->cursor->y); } +void +screen_draw_overlay_text(Screen *self, const char *utf8_text) { + if (self->overlay_line.is_active) deactivate_overlay_line(self); + if (!utf8_text || !utf8_text[0]) return; + Line *line = range_line_(self, self->cursor->y); + if (!line) return; + line_save_cells(line, 0, self->columns, self->overlay_line.gpu_cells, self->overlay_line.cpu_cells); + self->overlay_line.is_active = true; + self->overlay_line.ynum = self->cursor->y; + self->overlay_line.xstart = self->cursor->x; + self->overlay_line.xnum = 0; + uint32_t codepoint = 0, state = UTF8_ACCEPT; + bool orig_line_wrap_mode = self->modes.mDECAWM; + self->modes.mDECAWM = false; + self->cursor->reverse ^= true; + index_type before; + while (*utf8_text) { + switch(decode_utf8(&state, &codepoint, *(utf8_text++))) { + case UTF8_ACCEPT: + before = self->cursor->x; + screen_draw(self, codepoint); + self->overlay_line.xnum += self->cursor->x - before; + break; + case UTF8_REJECT: + break; + } + } + self->cursor->reverse ^= true; + self->modes.mDECAWM = orig_line_wrap_mode; +} + void screen_align(Screen *self) { self->margin_top = 0; self->margin_bottom = self->lines - 1; @@ -733,6 +789,7 @@ screen_cursor_to_column(Screen *self, unsigned int column) { } #define INDEX_UP \ + if (self->overlay_line.is_active) deactivate_overlay_line(self); \ linebuf_index(self->linebuf, top, bottom); \ INDEX_GRAPHICS(-1) \ if (self->linebuf == self->main_linebuf && bottom == self->lines - 1) { \ @@ -764,6 +821,7 @@ screen_scroll(Screen *self, unsigned int count) { } #define INDEX_DOWN \ + if (self->overlay_line.is_active) deactivate_overlay_line(self); \ linebuf_reverse_index(self->linebuf, top, bottom); \ linebuf_clear_line(self->linebuf, top); \ INDEX_GRAPHICS(1) \ @@ -1518,6 +1576,20 @@ screen_open_url(Screen *self) { return true; } +static void +deactivate_overlay_line(Screen *self) { + if (self->overlay_line.is_active && self->overlay_line.xnum && self->overlay_line.ynum < self->lines) { + Line *line = range_line_(self, self->overlay_line.ynum); + line_reset_cells(line, self->overlay_line.xstart, self->overlay_line.xnum, self->overlay_line.gpu_cells, self->overlay_line.cpu_cells); + if (self->cursor->y == self->overlay_line.ynum) self->cursor->x = self->overlay_line.xstart; + } + self->overlay_line.is_active = false; + self->overlay_line.ynum = 0; + self->overlay_line.xnum = 0; + self->overlay_line.xstart = 0; +} + + // }}} // Python interface {{{ diff --git a/kitty/screen.h b/kitty/screen.h index 80d4e5bac..29fa3420b 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -53,12 +53,20 @@ typedef struct { index_type start_of_data, count; } SavemodesBuffer; +typedef struct { + CPUCell *cpu_cells; + GPUCell *gpu_cells; + bool is_active; + index_type xstart, ynum, xnum; +} OverlayLine; + typedef struct { PyObject_HEAD unsigned int columns, lines, margin_top, margin_bottom, charset, scrolled_by, last_selection_scrolled_by; CellPixelSize cell_size; + OverlayLine overlay_line; id_type window_id; uint32_t utf8_state, utf8_codepoint, *g0_charset, *g1_charset, *g_charset; unsigned int current_charset; @@ -175,6 +183,7 @@ bool screen_open_url(Screen*); void screen_dirty_sprite_positions(Screen *self); void screen_rescale_images(Screen *self); void screen_report_size(Screen *, unsigned int which); +void screen_draw_overlay_text(Screen *self, const char *utf8_text); #define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen); DECLARE_CH_SCREEN_HANDLER(bell) DECLARE_CH_SCREEN_HANDLER(backspace)