diff --git a/csi.c b/csi.c index c5612031..9fbecc10 100644 --- a/csi.c +++ b/csi.c @@ -987,11 +987,15 @@ csi_dispatch(struct terminal *term, uint8_t final) /* Erase entire screen */ term_erase(term, 0, 0, term->rows - 1, term->cols - 1); term->grid->cursor.lcf = false; + if (unlikely(term->multi_cursor.shapes != NULL)) + term_remove_all_multi_cursors(term); break; case 3: { /* Erase scrollback */ term_erase_scrollback(term); + if (unlikely(term->multi_cursor.shapes != NULL)) + term_remove_all_multi_cursors(term); break; } @@ -2229,7 +2233,7 @@ csi_dispatch(struct terminal *term, uint8_t final) switch (final) { case 'q': { if (term->vt.params.idx == 0) { - LOG_WARN("multi-cursor: query support"); + LOG_WARN("multi-cursor: unimplemented: query support"); break; } @@ -2300,18 +2304,6 @@ csi_dispatch(struct terminal *term, uint8_t final) } } - LOG_WARN("modified cursor locations:"); - int rect_count = 0; - const pixman_box32_t *boxes = pixman_region32_rectangles(&modified, &rect_count); - for (int j = 0; j < rect_count; j++) { - const pixman_box32_t *box = &boxes[j]; - for (int r = box->y1; r < box->y2; r++) { - for (int c = box->x1; c < box->x2; c++) { - LOG_WARN(" (%d, %d)", r, c); - } - } - } - if (shape == 0) { /* Cursors disabled, remove from active set */ pixman_region32_subtract(&term->multi_cursor.active, &term->multi_cursor.active, &modified); @@ -2320,34 +2312,45 @@ csi_dispatch(struct terminal *term, uint8_t final) pixman_region32_union(&term->multi_cursor.active, &term->multi_cursor.active, &modified); } - LOG_WARN("active multi-cursor locations:"); - rect_count = 0; - boxes = pixman_region32_rectangles(&term->multi_cursor.active, &rect_count); - for (int j = 0; j < rect_count; j++) { - const pixman_box32_t *box = &boxes[j]; - for (int r = box->y1; r < box->y2; r++) { - for (int c = box->x1; c < box->x2; c++) { - LOG_WARN(" (%d, %d)", r, c); + if (pixman_region32_empty(&term->multi_cursor.active)) { + free(term->multi_cursor.shapes); + term->multi_cursor.shapes = NULL; + } else { + if (term->multi_cursor.shapes == NULL) { + term->multi_cursor.shapes = xcalloc( + term->cols * term->rows, + sizeof(enum multi_cursor_shape)); + } + + int rect_count = 0; + const pixman_box32_t *boxes = pixman_region32_rectangles(&modified, &rect_count); + for (int j = 0; j < rect_count; j++) { + const pixman_box32_t *box = &boxes[j]; + for (int r = box->y1; r < box->y2; r++) { + for (int c = box->x1; c < box->x2; c++) { + term->multi_cursor.shapes[r * term->cols + c] = shape; + } } } } - LOG_WARN("multi-cursors are now %s", pixman_region32_not_empty(&term->multi_cursor.active) ? "active" : "disabled"); pixman_region32_fini(&modified); break; case 30: - case 40: - LOG_WARN("multi-cursor: change color of %s", shape == 30 ? "text under cursor" : "cursor"); - + case 40: { const unsigned color_space = vt_param_get(term, 1, 0); + enum multi_cursor_color_source color_source = MULTI_CURSOR_COLOR_PRIMARY; + uint32_t color = shape == 30 ? term->multi_cursor.text_color + : term->multi_cursor.cursor_color; + switch (color_space) { case 0: - LOG_WARN("use main cursor colors"); + color_source = MULTI_CURSOR_COLOR_PRIMARY; break; case 1: - LOG_WARN("special (reverse)"); + color_source = MULTI_CURSOR_COLOR_SPECIAL; break; case 2: { @@ -2356,7 +2359,8 @@ csi_dispatch(struct terminal *term, uint8_t final) const uint8_t red = param->sub.value[0]; const uint8_t green = param->sub.value[1]; const uint8_t blue = param->sub.value[2]; - LOG_WARN("red=%hhu, green=%hhu, blue=%hhu", red, green, blue); + color_source = MULTI_CURSOR_COLOR_RGB; + color = 0xffu << 24 | red << 16 | green << 8 | blue; } break; } @@ -2365,7 +2369,8 @@ csi_dispatch(struct terminal *term, uint8_t final) const struct vt_param *param = &term->vt.params.v[1]; if (param->sub.idx == 1) { const unsigned color_index = param->sub.value[0]; - LOG_WARN("indexed color=%u", color_index); + color_source = MULTI_CURSOR_COLOR_RGB; + color = color_index; } break; } @@ -2374,14 +2379,23 @@ csi_dispatch(struct terminal *term, uint8_t final) LOG_WARN("multi-cursor: invalid color space: %u", color_space); return; } + + if (shape == 30) { + term->multi_cursor.text_color_source = color_source; + term->multi_cursor.text_color = color; + } else { + term->multi_cursor.cursor_color_source = color_source; + term->multi_cursor.cursor_color = color; + } break; + } case 100: - LOG_WARN("multi-cursor: query extra cursors"); + LOG_WARN("multi-cursor: unimplemented: query extra cursors"); break; case 101: - LOG_WARN("multi-cursor: query extra cursors' color"); + LOG_WARN("multi-cursor: unimplemented: query extra cursors' color"); break; default: diff --git a/render.c b/render.c index 1c24bafa..822df686 100644 --- a/render.c +++ b/render.c @@ -4812,6 +4812,8 @@ render_resize(struct terminal *term, int width, int height, uint8_t opts) term->render.last_cursor.row = NULL; + term_remove_all_multi_cursors(term); + damage_view: /* Signal TIOCSWINSZ */ send_dimensions_to_client(term); diff --git a/terminal.c b/terminal.c index 62bcd7d9..8c008af2 100644 --- a/terminal.c +++ b/terminal.c @@ -1404,6 +1404,11 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, .ime_enabled = true, #endif .active_notifications = tll_init(), + .multi_cursor = { + .shapes = NULL, + .cursor_color = MULTI_CURSOR_COLOR_PRIMARY, + .text_color = MULTI_CURSOR_COLOR_PRIMARY, + }, }; pixman_region32_init(&term->render.last_overlay_clip); @@ -1946,6 +1951,7 @@ term_destroy(struct terminal *term) free(term->color_stack.stack); pixman_region32_fini(&term->multi_cursor.active); + free(term->multi_cursor.shapes); int ret = EXIT_SUCCESS; @@ -2090,6 +2096,15 @@ term_theme_apply(struct terminal *term, const struct color_theme *theme) memcpy(term->colors.table, theme->table, sizeof(term->colors.table)); } +void +term_remove_all_multi_cursors(struct terminal *term) +{ + pixman_region32_fini(&term->multi_cursor.active); + pixman_region32_init(&term->multi_cursor.active); + free(term->multi_cursor.shapes); + term->multi_cursor.shapes = NULL; +} + void term_reset(struct terminal *term, bool hard) { @@ -2170,6 +2185,12 @@ term_reset(struct terminal *term, bool hard) term->bits_affecting_ascii_printer.value = 0; term_update_ascii_printer(term); + term_remove_all_multi_cursors(term); + term->multi_cursor.cursor_color_source = MULTI_CURSOR_COLOR_PRIMARY; + term->multi_cursor.text_color_source = MULTI_CURSOR_COLOR_PRIMARY; + term->multi_cursor.cursor_color = 0; + term->multi_cursor.text_color = 0; + if (!hard) return; diff --git a/terminal.h b/terminal.h index 89963b75..6f61cc16 100644 --- a/terminal.h +++ b/terminal.h @@ -407,6 +407,23 @@ struct colors { enum which_color_theme active_theme; }; +/* Synchronized with the multi-cursor protocol specification */ +enum multi_cursor_shape { + MULTI_CURSOR_SHAPE_NONE = 0, + MULTI_CURSOR_SHAPE_BLOCK = 1, + MULTI_CURSOR_SHAPE_BEAM = 2, + MULTI_CURSOR_SHAPE_UNDERLINE = 3, + MULTI_CURSOR_SHAPE_PRIMARY = 29, +}; + +/* Synchronized with the multi-cursor protocol specification */ +enum multi_cursor_color_source { + MULTI_CURSOR_COLOR_PRIMARY = 0, + MULTI_CURSOR_COLOR_SPECIAL = 1, + MULTI_CURSOR_COLOR_RGB = 2, + MULTI_CURSOR_COLOR_256 = 5, +}; + struct terminal { struct fdm *fdm; struct reaper *reaper; @@ -834,6 +851,11 @@ struct terminal { struct { pixman_region32_t active; + enum multi_cursor_shape *shapes; /* Flattened 2-dimensional array */ + enum multi_cursor_color_source cursor_color_source; + enum multi_cursor_color_source text_color_source; + uint32_t cursor_color; + uint32_t text_color; } multi_cursor; }; @@ -992,6 +1014,8 @@ void term_theme_switch_to_1(struct terminal *term); void term_theme_switch_to_2(struct terminal *term); void term_theme_toggle(struct terminal *term); +void term_remove_all_multi_cursors(struct terminal *term); + static inline void term_reset_grapheme_state(struct terminal *term) { #if defined(FOOT_GRAPHEME_CLUSTERING)