version 1.221, 2023/09/14 13:01:35 |
version 1.222, 2023/09/15 15:49:05 |
|
|
const char *); |
const char *); |
static int screen_write_overwrite(struct screen_write_ctx *, |
static int screen_write_overwrite(struct screen_write_ctx *, |
struct grid_cell *, u_int); |
struct grid_cell *, u_int); |
static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, |
static int screen_write_combine(struct screen_write_ctx *, |
const struct utf8_data *, u_int *, u_int *); |
const struct grid_cell *); |
|
|
struct screen_write_citem { |
struct screen_write_citem { |
u_int x; |
u_int x; |
|
|
|
|
if (ci->used == 0) |
if (ci->used == 0) |
return; |
return; |
ctx->flags &= ~SCREEN_WRITE_COMBINE; |
|
|
|
before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used, |
before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used, |
&wrapped); |
&wrapped); |
|
|
{ |
{ |
struct screen *s = ctx->s; |
struct screen *s = ctx->s; |
struct grid *gd = s->grid; |
struct grid *gd = s->grid; |
struct grid_cell copy; |
const struct utf8_data *ud = &gc->data; |
const struct utf8_data *ud = &gc->data, *previous = NULL, *combine; |
|
struct grid_line *gl; |
struct grid_line *gl; |
struct grid_cell_entry *gce; |
struct grid_cell_entry *gce; |
struct grid_cell tmp_gc, now_gc; |
struct grid_cell tmp_gc, now_gc; |
struct tty_ctx ttyctx; |
struct tty_ctx ttyctx; |
u_int sx = screen_size_x(s), sy = screen_size_y(s); |
u_int sx = screen_size_x(s), sy = screen_size_y(s); |
u_int width = ud->width, xx, last, cx, cy; |
u_int width = ud->width, xx, not_wrap; |
int selected, skip = 1; |
int selected, skip = 1; |
|
|
/* Ignore padding cells. */ |
/* Ignore padding cells. */ |
if (gc->flags & GRID_FLAG_PADDING) |
if (gc->flags & GRID_FLAG_PADDING) |
return; |
return; |
|
|
/* Check if this cell needs to be combined with the previous cell. */ |
/* Get the previous cell to check for combining. */ |
if (ctx->flags & SCREEN_WRITE_COMBINE) |
if (screen_write_combine(ctx, gc) != 0) |
previous = &ctx->previous; |
|
switch (utf8_try_combined(ud, previous, &combine, &width)) { |
|
case UTF8_DISCARD_NOW: |
|
log_debug("%s: UTF8_DISCARD_NOW (width %u)", __func__, width); |
|
ctx->flags &= ~SCREEN_WRITE_COMBINE; |
|
return; |
return; |
case UTF8_WRITE_NOW: |
|
log_debug("%s: UTF8_WRITE_NOW (width %u)", __func__, width); |
|
ctx->flags &= ~SCREEN_WRITE_COMBINE; |
|
break; |
|
case UTF8_COMBINE_NOW: |
|
log_debug("%s: UTF8_COMBINE_NOW (width %u)", __func__, width); |
|
screen_write_collect_flush(ctx, 0, __func__); |
|
gc = screen_write_combine(ctx, combine, &xx, &cx); |
|
if (gc != NULL) { |
|
cx = s->cx; cy = s->cy; |
|
screen_write_set_cursor(ctx, xx, s->cy); |
|
screen_write_initctx(ctx, &ttyctx, 0); |
|
ttyctx.cell = gc; |
|
tty_write(tty_cmd_cell, &ttyctx); |
|
s->cx = cx; s->cy = cy; |
|
} |
|
ctx->flags &= ~SCREEN_WRITE_COMBINE; |
|
return; |
|
case UTF8_WRITE_MAYBE_COMBINE: |
|
log_debug("%s: UTF8_WRITE_MAYBE_COMBINE (width %u)", __func__, |
|
width); |
|
utf8_copy(&ctx->previous, ud); |
|
ctx->flags |= SCREEN_WRITE_COMBINE; |
|
break; |
|
case UTF8_DISCARD_MAYBE_COMBINE: |
|
log_debug("%s: UTF8_DISCARD_MAYBE_COMBINE (width %u)", __func__, |
|
width); |
|
utf8_copy(&ctx->previous, ud); |
|
ctx->flags |= SCREEN_WRITE_COMBINE; |
|
return; |
|
} |
|
if (width != ud->width) { |
|
memcpy(©, gc, sizeof copy); |
|
copy.data.width = width; |
|
gc = © |
|
} |
|
ud = NULL; |
|
|
|
/* Flush any existing scrolling. */ |
/* Flush any existing scrolling. */ |
screen_write_collect_flush(ctx, 1, __func__); |
screen_write_collect_flush(ctx, 1, __func__); |
|
|
* Move the cursor. If not wrapping, stick at the last character and |
* Move the cursor. If not wrapping, stick at the last character and |
* replace it. |
* replace it. |
*/ |
*/ |
last = !(s->mode & MODE_WRAP); |
not_wrap = !(s->mode & MODE_WRAP); |
if (s->cx <= sx - last - width) |
if (s->cx <= sx - not_wrap - width) |
screen_write_set_cursor(ctx, s->cx + width, -1); |
screen_write_set_cursor(ctx, s->cx + width, -1); |
else |
else |
screen_write_set_cursor(ctx, sx - last, -1); |
screen_write_set_cursor(ctx, sx - not_wrap, -1); |
|
|
/* Create space for character in insert mode. */ |
/* Create space for character in insert mode. */ |
if (s->mode & MODE_INSERT) { |
if (s->mode & MODE_INSERT) { |
|
|
} |
} |
} |
} |
|
|
/* Combine a UTF-8 zero-width character onto the previous. */ |
/* Combine a UTF-8 zero-width character onto the previous if necessary. */ |
static const struct grid_cell * |
static int |
screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud, |
screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc) |
u_int *xx, u_int *cx) |
|
{ |
{ |
struct screen *s = ctx->s; |
struct screen *s = ctx->s; |
struct grid *gd = s->grid; |
struct grid *gd = s->grid; |
static struct grid_cell gc; |
const struct utf8_data *ud = &gc->data; |
u_int n, width; |
u_int n, cx = s->cx, cy = s->cy; |
|
struct grid_cell last; |
|
struct tty_ctx ttyctx; |
|
int force_wide = 0, zero_width = 0; |
|
|
/* Can't combine if at 0. */ |
/* |
if (s->cx == 0) { |
* Is this character which makes no sense without being combined? If |
*xx = 0; |
* this is true then flag it here and discard the character (return 1) |
return (NULL); |
* if we cannot combine it. |
} |
*/ |
*xx = s->cx; |
if (utf8_is_zwj(ud)) |
|
zero_width = 1; |
|
else if (utf8_is_vs(ud)) |
|
zero_width = force_wide = 1; |
|
else if (ud->width == 0) |
|
zero_width = 1; |
|
|
/* Empty data is out. */ |
/* Cannot combine empty character or at left. */ |
if (ud->size == 0) |
if (ud->size < 2 || cx == 0) |
fatalx("UTF-8 data empty"); |
return (zero_width); |
|
log_debug("%s: character %.*s at %u,%u (width %u)", __func__, |
|
(int)ud->size, ud->data, cx, cy, ud->width); |
|
|
/* Retrieve the previous cell. */ |
/* Find the cell to combine with. */ |
for (n = 1; n <= s->cx; n++) { |
n = 1; |
grid_view_get_cell(gd, s->cx - n, s->cy, &gc); |
grid_view_get_cell(gd, cx - n, cy, &last); |
if (~gc.flags & GRID_FLAG_PADDING) |
if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) { |
break; |
n = 2; |
|
grid_view_get_cell(gd, cx - n, cy, &last); |
} |
} |
if (n > s->cx) |
if (n != last.data.width || (last.flags & GRID_FLAG_PADDING)) |
return (NULL); |
return (zero_width); |
|
|
/* Check there is enough space. */ |
/* |
if (gc.data.size + ud->size > sizeof gc.data.data) |
* Check if we need to combine characters. This could be zero width |
return (NULL); |
* (zet above), a modifier character (with an existing Unicode |
(*xx) -= n; |
* character) or a previous ZWJ. |
|
*/ |
|
if (!zero_width) { |
|
if (utf8_is_modifier(ud)) { |
|
if (last.data.size < 2) |
|
return (0); |
|
force_wide = 1; |
|
} else if (!utf8_has_zwj(&last.data)) |
|
return (0); |
|
} |
|
|
log_debug("%s: %.*s onto %.*s at %u,%u (width %u)", __func__, |
/* Combining; flush any pending output. */ |
(int)ud->size, ud->data, (int)gc.data.size, gc.data.data, *xx, |
screen_write_collect_flush(ctx, 0, __func__); |
s->cy, gc.data.width); |
|
|
|
|
log_debug("%s: %.*s -> %.*s at %u,%u (offset %u, width %u)", __func__, |
|
(int)ud->size, ud->data, (int)last.data.size, last.data.data, |
|
cx - n, cy, n, last.data.width); |
|
|
/* Append the data. */ |
/* Append the data. */ |
memcpy(gc.data.data + gc.data.size, ud->data, ud->size); |
memcpy(last.data.data + last.data.size, ud->data, ud->size); |
gc.data.size += ud->size; |
last.data.size += ud->size; |
width = gc.data.width; |
|
|
|
/* If this is U+FE0F VARIATION SELECTOR-16, force the width to 2. */ |
/* Force the width to 2 for modifiers and variation selector. */ |
if (gc.data.width == 1 && |
if (last.data.width == 1 && force_wide) { |
ud->size == 3 && |
last.data.width = 2; |
memcmp(ud->data, "\357\270\217", 3) == 0) { |
n = 2; |
grid_view_set_padding(gd, (*xx) + 1, s->cy); |
cx++; |
gc.data.width = 2; |
} else |
width += 2; |
force_wide = 0; |
} |
|
|
|
/* Set the new cell. */ |
/* Set the new cell. */ |
grid_view_set_cell(gd, *xx, s->cy, &gc); |
grid_view_set_cell(gd, cx - n, cy, &last); |
|
if (force_wide) |
|
grid_view_set_padding(gd, cx, cy); |
|
|
*cx = (*xx) + width; |
/* |
log_debug("%s: character at %u; cursor at %u", __func__, *xx, *cx); |
* Redraw the combined cell. If forcing the cell to width 2, reset the |
return (&gc); |
* cached cursor position in the tty, since we don't really know |
|
* whether the terminal thought the character was width 1 or width 2 |
|
* and what it is going to do now. |
|
*/ |
|
screen_write_set_cursor(ctx, cx - n, cy); |
|
screen_write_initctx(ctx, &ttyctx, 0); |
|
ttyctx.cell = &last; |
|
ttyctx.num = force_wide; /* reset cached cursor position */ |
|
tty_write(tty_cmd_cell, &ttyctx); |
|
screen_write_set_cursor(ctx, cx, cy); |
|
|
|
return (1); |
} |
} |
|
|
/* |
/* |