Long overdue change to the way we store cells in the grid: now, instead

of storing a full grid_cell with UTF-8 data and everything, store a new
type grid_cell_entry. This can either be the cell itself (for ASCII
cells), or an offset into an extended array (per line) for UTF-8
data.

This avoid a large (8 byte) overhead on non-UTF-8 cells (by far the
majority for most users) without the complexity of the shadow array we
had before. Grid memory without any UTF-8 is about half.

The disadvantage that cells can no longer be modified in place and need
to be copied out of the grid and back but it turned out to be lot less
complicated than I expected.
pull/210/head
nicm 2015-11-13 08:09:28 +00:00
parent e71a915412
commit c5689a5a40
12 changed files with 297 additions and 282 deletions

View File

@ -76,7 +76,6 @@ SRCS= alerts.c \
control-notify.c \
environ.c \
format.c \
grid-cell.c \
grid-view.c \
grid.c \
input-keys.c \

View File

@ -415,6 +415,7 @@ format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe)
for (i = 0; i < gd->hsize; i++) {
gl = &gd->linedata[i];
size += gl->cellsize * sizeof *gl->celldata;
size += gl->extdsize * sizeof *gl->extddata;
}
size += gd->hsize * sizeof *gd->linedata;

View File

@ -1,55 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2012 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
#include "tmux.h"
/* Get cell width. */
u_int
grid_cell_width(const struct grid_cell *gc)
{
return (gc->xstate >> 4);
}
/* Get cell data. */
void
grid_cell_get(const struct grid_cell *gc, struct utf8_data *ud)
{
ud->size = gc->xstate & 0xf;
ud->width = gc->xstate >> 4;
memcpy(ud->data, gc->xdata, ud->size);
}
/* Set cell data. */
void
grid_cell_set(struct grid_cell *gc, const struct utf8_data *ud)
{
memcpy(gc->xdata, ud->data, ud->size);
gc->xstate = (ud->width << 4) | ud->size;
}
/* Set a single character as cell data. */
void
grid_cell_one(struct grid_cell *gc, u_char ch)
{
*gc->xdata = ch;
gc->xstate = (1 << 4) | 1;
}

View File

@ -30,24 +30,17 @@
#define grid_view_x(gd, x) (x)
#define grid_view_y(gd, y) ((gd)->hsize + (y))
/* Get cell for reading. */
const struct grid_cell *
grid_view_peek_cell(struct grid *gd, u_int px, u_int py)
/* Get cel. */
void
grid_view_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
{
return (grid_peek_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py)));
}
/* Get cell for writing. */
struct grid_cell *
grid_view_get_cell(struct grid *gd, u_int px, u_int py)
{
return (grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py)));
grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc);
}
/* Set cell. */
void
grid_view_set_cell(
struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
grid_view_set_cell(struct grid *gd, u_int px, u_int py,
const struct grid_cell *gc)
{
grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc);
}

169
grid.c
View File

@ -36,15 +36,17 @@
*/
/* Default grid cell data. */
const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " };
#define grid_put_cell(gd, px, py, gc) do { \
memcpy(&gd->linedata[py].celldata[px], \
gc, sizeof gd->linedata[py].celldata[px]); \
} while (0)
const struct grid_cell grid_default_cell = {
0, 0, 8, 8, { { ' ' }, 0, 1, 1 }
};
const struct grid_cell_entry grid_default_entry = {
0, { .data = { 0, 8, 8, ' ' } }
};
int grid_check_y(struct grid *, u_int);
void grid_reflow_copy(struct grid_line *, u_int, struct grid_line *l,
u_int, u_int);
void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int);
void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int,
u_int);
@ -54,6 +56,13 @@ size_t grid_string_cells_bg(const struct grid_cell *, int *);
void grid_string_cells_code(const struct grid_cell *,
const struct grid_cell *, char *, size_t, int);
/* Copy default into a cell. */
static void
grid_clear_cell(struct grid *gd, u_int px, u_int py)
{
gd->linedata[py].celldata[px] = grid_default_entry;
}
/* Check grid y position. */
int
grid_check_y(struct grid *gd, u_int py)
@ -95,6 +104,7 @@ grid_destroy(struct grid *gd)
for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
gl = &gd->linedata[yy];
free(gl->celldata);
free(gl->extddata);
}
free(gd->linedata);
@ -107,7 +117,7 @@ int
grid_compare(struct grid *ga, struct grid *gb)
{
struct grid_line *gla, *glb;
struct grid_cell *gca, *gcb;
struct grid_cell gca, gcb;
u_int xx, yy;
if (ga->sx != gb->sx || ga->sy != gb->sy)
@ -118,10 +128,10 @@ grid_compare(struct grid *ga, struct grid *gb)
glb = &gb->linedata[yy];
if (gla->cellsize != glb->cellsize)
return (1);
for (xx = 0; xx < ga->sx; xx++) {
gca = &gla->celldata[xx];
gcb = &glb->celldata[xx];
if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0)
for (xx = 0; xx < gla->cellsize; xx++) {
grid_get_cell(ga, xx, yy, &gca);
grid_get_cell(gb, xx, yy, &gcb);
if (memcmp(&gca, &gcb, sizeof (struct grid_cell)) != 0)
return (1);
}
}
@ -224,7 +234,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx)
gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
for (xx = gl->cellsize; xx < sx; xx++)
grid_put_cell(gd, xx, py, &grid_default_cell);
grid_clear_cell(gd, xx, py);
gl->cellsize = sx;
}
@ -238,37 +248,72 @@ grid_peek_line(struct grid *gd, u_int py)
}
/* Get cell for reading. */
const struct grid_cell *
grid_peek_cell(struct grid *gd, u_int px, u_int py)
void
grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
{
if (grid_check_y(gd, py) != 0)
return (&grid_default_cell);
struct grid_line *gl;
struct grid_cell_entry *gce;
if (px >= gd->linedata[py].cellsize)
return (&grid_default_cell);
return (&gd->linedata[py].celldata[px]);
}
if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) {
memcpy(gc, &grid_default_cell, sizeof *gc);
return;
}
/* Get cell at relative position (for writing). */
struct grid_cell *
grid_get_cell(struct grid *gd, u_int px, u_int py)
{
if (grid_check_y(gd, py) != 0)
return (NULL);
gl = &gd->linedata[py];
gce = &gl->celldata[px];
grid_expand_line(gd, py, px + 1);
return (&gd->linedata[py].celldata[px]);
if (gce->flags & GRID_FLAG_EXTENDED) {
if (gce->offset >= gl->extdsize)
memcpy(gc, &grid_default_cell, sizeof *gc);
else
memcpy(gc, &gl->extddata[gce->offset], sizeof *gc);
return;
}
gc->flags = gce->flags & ~GRID_FLAG_EXTENDED;
gc->attr = gce->data.attr;
gc->fg = gce->data.fg;
gc->bg = gce->data.bg;
utf8_set(&gc->data, gce->data.data);
}
/* Set cell at relative position. */
void
grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
{
struct grid_line *gl;
struct grid_cell_entry *gce;
struct grid_cell *gcp;
if (grid_check_y(gd, py) != 0)
return;
grid_expand_line(gd, py, px + 1);
grid_put_cell(gd, px, py, gc);
gl = &gd->linedata[py];
gce = &gl->celldata[px];
if ((gce->flags & GRID_FLAG_EXTENDED) || gc->data.size != 1 ||
gc->data.width != 1) {
if (~gce->flags & GRID_FLAG_EXTENDED) {
gl->extddata = xreallocarray(gl->extddata,
gl->extdsize + 1, sizeof *gl->extddata);
gce->offset = gl->extdsize++;
gce->flags = gc->flags | GRID_FLAG_EXTENDED;
}
if (gce->offset >= gl->extdsize)
fatalx("offset too big");
gcp = &gl->extddata[gce->offset];
memcpy(gcp, gc, sizeof *gcp);
return;
}
gce->flags = gc->flags & ~GRID_FLAG_EXTENDED;
gce->data.attr = gc->attr;
gce->data.fg = gc->fg;
gce->data.bg = gc->bg;
gce->data.data = gc->data.data[0];
}
/* Clear area. */
@ -300,7 +345,7 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny)
for (xx = px; xx < px + nx; xx++) {
if (xx >= gd->linedata[yy].cellsize)
break;
grid_put_cell(gd, xx, yy, &grid_default_cell);
grid_clear_cell(gd, xx, yy);
}
}
}
@ -324,6 +369,10 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny)
gl = &gd->linedata[yy];
free(gl->celldata);
memset(gl, 0, sizeof *gl);
free(gl->extddata);
gl->extddata = NULL;
gl->extdsize = 0;
}
}
@ -386,7 +435,7 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx)
for (xx = px; xx < px + nx; xx++) {
if (xx >= dx && xx < dx + nx)
continue;
grid_put_cell(gd, xx, py, &grid_default_cell);
grid_clear_cell(gd, xx, py);
}
}
@ -568,9 +617,8 @@ char *
grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
struct grid_cell **lastgc, int with_codes, int escape_c0, int trim)
{
const struct grid_cell *gc;
struct grid_cell gc;
static struct grid_cell lastgc1;
struct utf8_data ud;
const char *data;
char *buf, code[128];
size_t len, off, size, codelen;
@ -590,21 +638,20 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
for (xx = px; xx < px + nx; xx++) {
if (gl == NULL || xx >= gl->cellsize)
break;
gc = &gl->celldata[xx];
if (gc->flags & GRID_FLAG_PADDING)
grid_get_cell(gd, xx, py, &gc);
if (gc.flags & GRID_FLAG_PADDING)
continue;
grid_cell_get(gc, &ud);
if (with_codes) {
grid_string_cells_code(*lastgc, gc, code, sizeof code,
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
escape_c0);
codelen = strlen(code);
memcpy(*lastgc, gc, sizeof *gc);
memcpy(*lastgc, &gc, sizeof **lastgc);
} else
codelen = 0;
data = ud.data;
size = ud.size;
data = gc.data.data;
size = gc.data.size;
if (escape_c0 && size == 1 && *data == '\\') {
data = "\\\\";
size = 2;
@ -663,11 +710,44 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
} else
dstl->celldata = NULL;
if (srcl->extdsize != 0) {
dstl->extdsize = srcl->extdsize;
dstl->extddata = xreallocarray(NULL, dstl->extdsize,
sizeof *dstl->extddata);
memcpy(dstl->extddata, srcl->extddata, dstl->extdsize *
sizeof *dstl->extddata);
}
sy++;
dy++;
}
}
/* Copy a section of a line. */
void
grid_reflow_copy(struct grid_line *dst_gl, u_int to, struct grid_line *src_gl,
u_int from, u_int to_copy)
{
struct grid_cell_entry *gce;
u_int i, was;
memcpy(&dst_gl->celldata[to], &src_gl->celldata[from],
to_copy * sizeof *dst_gl->celldata);
for (i = to; i < to + to_copy; i++) {
gce = &dst_gl->celldata[i];
if (~gce->flags & GRID_FLAG_EXTENDED)
continue;
was = gce->offset;
dst_gl->extddata = xreallocarray(dst_gl->extddata,
dst_gl->extdsize + 1, sizeof *dst_gl->extddata);
gce->offset = dst_gl->extdsize++;
memcpy(&dst_gl->extddata[gce->offset], &src_gl->extddata[was],
sizeof *dst_gl->extddata);
}
}
/* Join line data. */
void
grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl,
@ -692,8 +772,7 @@ grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl,
dst_gl->cellsize = nx;
/* Append as much as possible. */
memcpy(&dst_gl->celldata[ox], &src_gl->celldata[0],
to_copy * sizeof src_gl->celldata[0]);
grid_reflow_copy(dst_gl, ox, src_gl, 0, to_copy);
/* If there is any left in the source, split it. */
if (src_gl->cellsize > to_copy) {
@ -732,8 +811,7 @@ grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl,
dst_gl->flags |= GRID_LINE_WRAPPED;
/* Copy the data. */
memcpy(&dst_gl->celldata[0], &src_gl->celldata[offset],
to_copy * sizeof dst_gl->celldata[0]);
grid_reflow_copy(dst_gl, 0, src_gl, offset, to_copy);
/* Move offset and reduce old line size. */
offset += to_copy;
@ -763,6 +841,7 @@ grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl)
/* Clear old line. */
src_gl->celldata = NULL;
src_gl->extddata = NULL;
}
/*
@ -792,7 +871,7 @@ grid_reflow(struct grid *dst, struct grid *src, u_int new_x)
/* Previous was wrapped. Try to join. */
grid_reflow_join(dst, &py, src_gl, new_x);
}
previous_wrapped = src_gl->flags & GRID_LINE_WRAPPED;
previous_wrapped = (src_gl->flags & GRID_LINE_WRAPPED);
}
grid_destroy(src);

View File

@ -1006,7 +1006,7 @@ input_print(struct input_ctx *ictx)
else
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
grid_cell_one(&ictx->cell.cell, ictx->ch);
utf8_set(&ictx->cell.cell.data, ictx->ch);
screen_write_cell(&ictx->ctx, &ictx->cell.cell);
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
@ -1945,7 +1945,7 @@ input_utf8_close(struct input_ctx *ictx)
utf8_append(&ictx->utf8data, ictx->ch);
grid_cell_set(&ictx->cell.cell, &ictx->utf8data);
utf8_copy(&ictx->cell.cell.data, &ictx->utf8data);
screen_write_cell(&ictx->ctx, &ictx->cell.cell);
return (0);

View File

@ -67,7 +67,7 @@ void
screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc,
u_char ch)
{
grid_cell_one(gc, ch);
utf8_set(&gc->data, ch);
screen_write_cell(ctx, gc);
}
@ -126,7 +126,7 @@ screen_write_strlen(const char *fmt, ...)
ptr++;
left = strlen(ptr);
if (left < ud.size - 1)
if (left < (size_t)ud.size - 1)
break;
while (utf8_append(&ud, *ptr))
ptr++;
@ -185,7 +185,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
ptr++;
left = strlen(ptr);
if (left < ud.size - 1)
if (left < (size_t)ud.size - 1)
break;
while (utf8_append(&ud, *ptr))
ptr++;
@ -201,7 +201,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
}
size += ud.width;
grid_cell_set(gc, &ud);
utf8_copy(&gc->data, &ud);
screen_write_cell(ctx, gc);
} else {
if (maxlen > 0 && size + 1 > (size_t) maxlen)
@ -258,7 +258,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
ptr++;
left = strlen(ptr);
if (left < ud.size - 1)
if (left < (size_t)ud.size - 1)
break;
while (utf8_append(&ud, *ptr))
ptr++;
@ -274,7 +274,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
}
size += ud.width;
grid_cell_set(&lgc, &ud);
utf8_copy(&lgc.data, &ud);
screen_write_cell(ctx, &lgc);
} else {
if (maxlen > 0 && size + 1 > (size_t) maxlen)
@ -299,8 +299,7 @@ screen_write_copy(struct screen_write_ctx *ctx,
struct screen *s = ctx->s;
struct grid *gd = src->grid;
struct grid_line *gl;
const struct grid_cell *gc;
struct utf8_data ud;
struct grid_cell gc;
u_int xx, yy, cx, cy, ax, bx;
cx = s->cx;
@ -324,12 +323,8 @@ screen_write_copy(struct screen_write_ctx *ctx,
bx = px + nx;
for (xx = ax; xx < bx; xx++) {
if (xx >= gl->cellsize)
gc = &grid_default_cell;
else
gc = &gl->celldata[xx];
grid_cell_get(gc, &ud);
screen_write_cell(ctx, gc);
grid_get_cell(gd, xx, yy, &gc);
screen_write_cell(ctx, &gc);
}
if (px + nx == gd->sx && px + nx > gl->cellsize)
screen_write_clearendofline(ctx);
@ -342,12 +337,12 @@ screen_write_copy(struct screen_write_ctx *ctx,
/* Set up context for TTY command. */
void
screen_write_initctx(
struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int save_last)
screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
int save_last)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
const struct grid_cell *gc;
struct grid_cell gc;
u_int xx;
ttyctx->wp = ctx->wp;
@ -362,14 +357,14 @@ screen_write_initctx(
return;
/* Save the last cell on the screen. */
gc = &grid_default_cell;
memcpy(&gc, &grid_default_cell, sizeof gc);
for (xx = 1; xx <= screen_size_x(s); xx++) {
gc = grid_view_peek_cell(gd, screen_size_x(s) - xx, s->cy);
if (!(gc->flags & GRID_FLAG_PADDING))
grid_view_get_cell(gd, screen_size_x(s) - xx, s->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
}
ttyctx->last_width = xx;
memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell);
memcpy(&ttyctx->last_cell, &gc, sizeof ttyctx->last_cell);
}
/* Set a mode. */
@ -507,7 +502,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
screen_write_initctx(ctx, &ttyctx, 0);
memcpy(&gc, &grid_default_cell, sizeof gc);
grid_cell_one(&gc, 'E');
utf8_set(&gc.data, 'E');
for (yy = 0; yy < screen_size_y(s); yy++) {
for (xx = 0; xx < screen_size_x(s); xx++)
@ -904,14 +899,13 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
u_int width, xx, last;
struct grid_cell tmp_gc, *tmp_gcp;
struct utf8_data ud;
struct grid_cell tmp_gc;
int insert;
/* Ignore padding. */
if (gc->flags & GRID_FLAG_PADDING)
return;
width = grid_cell_width(gc);
width = gc->data.width;
/*
* If this is a wide character and there is no room on the screen, for
@ -928,8 +922,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
* there is space.
*/
if (width == 0) {
grid_cell_get(gc, &ud);
if (screen_write_combine(ctx, &ud) == 0) {
if (screen_write_combine(ctx, &gc->data) == 0) {
screen_write_initctx(ctx, &ttyctx, 0);
tty_write(tty_cmd_utf8character, &ttyctx);
}
@ -964,11 +957,11 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
* If the new character is UTF-8 wide, fill in padding cells. Have
* already ensured there is enough room.
*/
for (xx = s->cx + 1; xx < s->cx + width; xx++) {
tmp_gcp = grid_view_get_cell(gd, xx, s->cy);
if (tmp_gcp != NULL)
tmp_gcp->flags |= GRID_FLAG_PADDING;
}
memcpy(&tmp_gc, &grid_default_cell, sizeof tmp_gc);
tmp_gc.flags |= GRID_FLAG_PADDING;
tmp_gc.data.width = 0;
for (xx = s->cx + 1; xx < s->cx + width; xx++)
grid_view_set_cell(gd, xx, s->cy, &tmp_gc);
/* Set the cell. */
grid_view_set_cell(gd, s->cx, s->cy, gc);
@ -990,8 +983,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
}
if (screen_check_selection(s, s->cx - width, s->cy)) {
memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc);
grid_cell_get(gc, &ud);
grid_cell_set(&tmp_gc, &ud);
utf8_copy(&tmp_gc.data, &gc->data);
tmp_gc.attr = tmp_gc.attr & ~GRID_ATTR_CHARSET;
tmp_gc.attr |= gc->attr & GRID_ATTR_CHARSET;
tmp_gc.flags = gc->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
@ -1011,8 +1003,7 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct grid_cell *gc;
struct utf8_data ud1;
struct grid_cell gc;
/* Can't combine if at 0. */
if (s->cx == 0)
@ -1023,17 +1014,18 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud)
fatalx("UTF-8 data empty");
/* Retrieve the previous cell. */
gc = grid_view_get_cell(gd, s->cx - 1, s->cy);
grid_cell_get(gc, &ud1);
grid_view_get_cell(gd, s->cx - 1, s->cy, &gc);
/* Check there is enough space. */
if (ud1.size + ud->size > sizeof ud1.data)
if (gc.data.size + ud->size > sizeof gc.data.data)
return (-1);
/* Append the data and set the cell. */
memcpy(ud1.data + ud1.size, ud->data, ud->size);
ud1.size += ud->size;
grid_cell_set(gc, &ud1);
/* Append the data. */
memcpy(gc.data.data + gc.data.size, ud->data, ud->size);
gc.data.size += ud->size;
/* Set the new cell. */
grid_view_set_cell(gd, s->cx - 1, s->cy, &gc);
return (0);
}
@ -1052,11 +1044,11 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
const struct grid_cell *gc;
struct grid_cell gc;
u_int xx;
gc = grid_view_peek_cell(gd, s->cx, s->cy);
if (gc->flags & GRID_FLAG_PADDING) {
grid_view_get_cell(gd, s->cx, s->cy, &gc);
if (gc.flags & GRID_FLAG_PADDING) {
/*
* A padding cell, so clear any following and leading padding
* cells back to the character. Don't overwrite the current
@ -1064,8 +1056,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width)
*/
xx = s->cx + 1;
while (--xx > 0) {
gc = grid_view_peek_cell(gd, xx, s->cy);
if (!(gc->flags & GRID_FLAG_PADDING))
grid_view_get_cell(gd, xx, s->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
}
@ -1080,8 +1072,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width)
*/
xx = s->cx + width - 1;
while (++xx < screen_size_x(s)) {
gc = grid_view_peek_cell(gd, xx, s->cy);
if (!(gc->flags & GRID_FLAG_PADDING))
grid_view_get_cell(gd, xx, s->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
}

View File

@ -746,7 +746,7 @@ status_prompt_redraw(struct client *c)
struct session *s = c->session;
struct screen old_status;
size_t i, size, left, len, off;
struct grid_cell gc, *gcp;
struct grid_cell gc;
if (c->tty.sx == 0 || c->tty.sy == 0)
return (0);
@ -789,8 +789,9 @@ status_prompt_redraw(struct client *c)
/* Apply fake cursor. */
off = len + c->prompt_index - off;
gcp = grid_view_get_cell(c->status.grid, off, 0);
gcp->attr ^= GRID_ATTR_REVERSE;
grid_view_get_cell(c->status.grid, off, 0, &gc);
gc.attr ^= GRID_ATTR_REVERSE;
grid_view_set_cell(c->status.grid, off, 0, &gc);
if (grid_compare(c->status.grid, old_status.grid) == 0) {
screen_free(&old_status);

72
tmux.h
View File

@ -621,11 +621,11 @@ struct mode_key_table {
struct utf8_data {
u_char data[UTF8_SIZE];
size_t have;
size_t size;
u_char have;
u_char size;
u_int width;
};
u_char width;
} __packed;
/* Grid attributes. */
#define GRID_ATTR_BRIGHT 0x1
@ -641,41 +641,56 @@ struct utf8_data {
#define GRID_FLAG_FG256 0x1
#define GRID_FLAG_BG256 0x2
#define GRID_FLAG_PADDING 0x4
#define GRID_FLAG_EXTENDED 0x8
/* Grid line flags. */
#define GRID_LINE_WRAPPED 0x1
/* Grid cell data. */
struct grid_cell {
u_char attr;
u_char flags;
u_char fg;
u_char bg;
u_char flags;
u_char attr;
u_char fg;
u_char bg;
struct utf8_data data;
u_char xstate; /* top 4 bits width, bottom 4 bits size */
u_char xdata[UTF8_SIZE];
};
struct grid_cell_entry {
u_char flags;
union {
u_int offset;
struct {
u_char attr;
u_char fg;
u_char bg;
u_char data;
} data;
};
} __packed;
/* Grid line. */
struct grid_line {
u_int cellsize;
struct grid_cell *celldata;
u_int cellsize;
struct grid_cell_entry *celldata;
int flags;
u_int extdsize;
struct grid_cell *extddata;
int flags;
} __packed;
/* Entire grid of cells. */
struct grid {
int flags;
#define GRID_HISTORY 0x1 /* scroll lines into history */
int flags;
#define GRID_HISTORY 0x1 /* scroll lines into history */
u_int sx;
u_int sy;
u_int sx;
u_int sy;
u_int hsize;
u_int hlimit;
u_int hsize;
u_int hlimit;
struct grid_line *linedata;
struct grid_line *linedata;
};
/* Option data structures. */
@ -1854,9 +1869,8 @@ void grid_scroll_history(struct grid *);
void grid_scroll_history_region(struct grid *, u_int, u_int);
void grid_clear_history(struct grid *);
void grid_expand_line(struct grid *, u_int, u_int);
const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int);
const struct grid_line *grid_peek_line(struct grid *, u_int);
struct grid_cell *grid_get_cell(struct grid *, u_int, u_int);
void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *);
void grid_clear(struct grid *, u_int, u_int, u_int, u_int);
void grid_clear_lines(struct grid *, u_int, u_int);
@ -1868,17 +1882,10 @@ void grid_duplicate_lines(
struct grid *, u_int, struct grid *, u_int, u_int);
u_int grid_reflow(struct grid *, struct grid *, u_int);
/* grid-cell.c */
u_int grid_cell_width(const struct grid_cell *);
void grid_cell_get(const struct grid_cell *, struct utf8_data *);
void grid_cell_set(struct grid_cell *, const struct utf8_data *);
void grid_cell_one(struct grid_cell *, u_char);
/* grid-view.c */
const struct grid_cell *grid_view_peek_cell(struct grid *, u_int, u_int);
struct grid_cell *grid_view_get_cell(struct grid *, u_int, u_int);
void grid_view_set_cell(
struct grid *, u_int, u_int, const struct grid_cell *);
void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
void grid_view_set_cell(struct grid *, u_int, u_int,
const struct grid_cell *);
void grid_view_clear_history(struct grid *);
void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int);
void grid_view_scroll_region_up(struct grid *, u_int, u_int);
@ -2183,6 +2190,7 @@ void session_renumber_windows(struct session *);
/* utf8.c */
u_int utf8_width(u_int);
void utf8_set(struct utf8_data *, u_char);
void utf8_copy(struct utf8_data *, const struct utf8_data *);
int utf8_open(struct utf8_data *, u_char);
int utf8_append(struct utf8_data *, u_char);
u_int utf8_combine(const struct utf8_data *);

37
tty.c
View File

@ -656,10 +656,8 @@ void
tty_draw_line(struct tty *tty, const struct window_pane *wp,
struct screen *s, u_int py, u_int ox, u_int oy)
{
const struct grid_cell *gc;
struct grid_cell gc;
struct grid_line *gl;
struct grid_cell tmpgc;
struct utf8_data ud;
u_int i, sx;
int flags;
@ -686,18 +684,13 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
tty_cursor(tty, ox, oy + py);
for (i = 0; i < sx; i++) {
gc = grid_view_peek_cell(s->grid, i, py);
grid_view_get_cell(s->grid, i, py, &gc);
if (screen_check_selection(s, i, py)) {
memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc);
grid_cell_get(gc, &ud);
grid_cell_set(&tmpgc, &ud);
tmpgc.flags = gc->flags &
~(GRID_FLAG_FG256|GRID_FLAG_BG256);
tmpgc.flags |= s->sel.cell.flags &
gc.flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
gc.flags |= s->sel.cell.flags &
(GRID_FLAG_FG256|GRID_FLAG_BG256);
tty_cell(tty, &tmpgc, wp);
} else
tty_cell(tty, gc, wp);
}
tty_cell(tty, &gc, wp);
}
if (sx < tty->sx) {
@ -1078,7 +1071,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
/* Is the cursor in the very last position? */
width = grid_cell_width(ctx->cell);
width = ctx->cell->data.width;
if (ctx->ocx > wp->sx - width) {
if (ctx->xoff != 0 || wp->sx != tty->sx) {
/*
@ -1095,7 +1088,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
* move as far left as possible and redraw the last
* cell to move into the last position.
*/
cx = screen_size_x(s) - grid_cell_width(&ctx->last_cell);
cx = screen_size_x(s) - ctx->last_cell.data.width;
tty_cursor_pane(tty, ctx, cx, ctx->ocy);
tty_cell(tty, &ctx->last_cell, wp);
}
@ -1155,8 +1148,7 @@ void
tty_cell(struct tty *tty, const struct grid_cell *gc,
const struct window_pane *wp)
{
struct utf8_data ud;
u_int i;
u_int i;
/* Skip last character if terminal is stupid. */
if (tty->term->flags & TERM_EARLYWRAP &&
@ -1171,23 +1163,22 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
tty_attributes(tty, gc, wp);
/* Get the cell and if ASCII write with putc to do ACS translation. */
grid_cell_get(gc, &ud);
if (ud.size == 1) {
if (*ud.data < 0x20 || *ud.data == 0x7f)
if (gc->data.size == 1) {
if (*gc->data.data < 0x20 || *gc->data.data == 0x7f)
return;
tty_putc(tty, *ud.data);
tty_putc(tty, *gc->data.data);
return;
}
/* If not UTF-8, write _. */
if (!(tty->flags & TTY_UTF8)) {
for (i = 0; i < ud.width; i++)
for (i = 0; i < gc->data.width; i++)
tty_putc(tty, '_');
return;
}
/* Write the data. */
tty_putn(tty, ud.data, ud.size, ud.width);
tty_putn(tty, gc->data.data, gc->data.size, gc->data.width);
}
void

17
utf8.c
View File

@ -352,10 +352,27 @@ static void utf8_build(void);
void
utf8_set(struct utf8_data *ud, u_char ch)
{
u_int i;
*ud->data = ch;
ud->size = 1;
ud->width = 1;
for (i = ud->size; i < sizeof ud->data; i++)
ud->data[i] = '\0';
}
/* Copy UTF-8 character. */
void
utf8_copy(struct utf8_data *to, const struct utf8_data *from)
{
u_int i;
memcpy(to, from, sizeof *to);
for (i = to->size; i < sizeof to->data; i++)
to->data[i] = '\0';
}
/*

View File

@ -942,21 +942,21 @@ int
window_copy_search_compare(struct grid *gd, u_int px, u_int py,
struct grid *sgd, u_int spx, int cis)
{
const struct grid_cell *gc, *sgc;
struct utf8_data ud, sud;
struct grid_cell gc, sgc;
const struct utf8_data *ud, *sud;
gc = grid_peek_cell(gd, px, py);
grid_cell_get(gc, &ud);
sgc = grid_peek_cell(sgd, spx, 0);
grid_cell_get(sgc, &sud);
grid_get_cell(gd, px, py, &gc);
ud = &gc.data;
grid_get_cell(sgd, spx, 0, &sgc);
sud = &sgc.data;
if (ud.size != sud.size || ud.width != sud.width)
if (ud->size != sud->size || ud->width != sud->width)
return (0);
if (cis && ud.size == 1)
return (tolower(ud.data[0]) == sud.data[0]);
if (cis && ud->size == 1)
return (tolower(ud->data[0]) == sud->data[0]);
return (memcmp(ud.data, sud.data, ud.size) == 0);
return (memcmp(ud->data, sud->data, ud->size) == 0);
}
int
@ -1541,12 +1541,12 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname)
}
void
window_copy_copy_line(struct window_pane *wp,
char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy,
u_int sx, u_int ex)
{
struct window_copy_mode_data *data = wp->modedata;
struct grid *gd = data->backing->grid;
const struct grid_cell *gc;
struct grid_cell gc;
struct grid_line *gl;
struct utf8_data ud;
u_int i, xx, wrapped = 0;
@ -1575,11 +1575,11 @@ window_copy_copy_line(struct window_pane *wp,
if (sx < ex) {
for (i = sx; i < ex; i++) {
gc = grid_peek_cell(gd, i, sy);
if (gc->flags & GRID_FLAG_PADDING)
grid_get_cell(gd, i, sy, &gc);
if (gc.flags & GRID_FLAG_PADDING)
continue;
grid_cell_get(gc, &ud);
if (ud.size == 1 && (gc->attr & GRID_ATTR_CHARSET)) {
utf8_copy(&ud, &gc.data);
if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) {
s = tty_acs_get(NULL, ud.data[0]);
if (s != NULL && strlen(s) <= sizeof ud.data) {
ud.size = strlen(s);
@ -1618,16 +1618,17 @@ int
window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
{
struct window_copy_mode_data *data = wp->modedata;
const struct grid_cell *gc;
struct utf8_data ud;
struct grid_cell gc;
const struct utf8_data *ud;
gc = grid_peek_cell(data->backing->grid, px, py);
grid_cell_get(gc, &ud);
if (ud.size != 1 || gc->flags & GRID_FLAG_PADDING)
grid_get_cell(data->backing->grid, px, py, &gc);
ud = &gc.data;
if (ud->size != 1 || (gc.flags & GRID_FLAG_PADDING))
return (0);
if (*ud.data == 0x00 || *ud.data == 0x7f)
if (*ud->data == 0x00 || *ud->data == 0x7f)
return (0);
return (strchr(set, *ud.data) != NULL);
return (strchr(set, *ud->data) != NULL);
}
u_int
@ -1635,8 +1636,7 @@ window_copy_find_length(struct window_pane *wp, u_int py)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *s = data->backing;
const struct grid_cell *gc;
struct utf8_data ud;
struct grid_cell gc;
u_int px;
/*
@ -1649,9 +1649,8 @@ window_copy_find_length(struct window_pane *wp, u_int py)
if (px > screen_size_x(s))
px = screen_size_x(s);
while (px > 0) {
gc = grid_peek_cell(s->grid, px - 1, py);
grid_cell_get(gc, &ud);
if (ud.size != 1 || *ud.data != ' ')
grid_get_cell(s->grid, px - 1, py, &gc);
if (gc.data.size != 1 || *gc.data.data != ' ')
break;
px--;
}
@ -1685,17 +1684,15 @@ window_copy_cursor_back_to_indentation(struct window_pane *wp)
{
struct window_copy_mode_data *data = wp->modedata;
u_int px, py, xx;
const struct grid_cell *gc;
struct utf8_data ud;
struct grid_cell gc;
px = 0;
py = screen_hsize(data->backing) + data->cy - data->oy;
xx = window_copy_find_length(wp, py);
while (px < xx) {
gc = grid_peek_cell(data->backing->grid, px, py);
grid_cell_get(gc, &ud);
if (ud.size != 1 || *ud.data != ' ')
grid_get_cell(data->backing->grid, px, py, &gc);
if (gc.data.size != 1 || *gc.data.data != ' ')
break;
px++;
}
@ -1909,8 +1906,7 @@ window_copy_cursor_jump(struct window_pane *wp)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *back_s = data->backing;
const struct grid_cell *gc;
struct utf8_data ud;
struct grid_cell gc;
u_int px, py, xx;
px = data->cx + 1;
@ -1918,10 +1914,9 @@ window_copy_cursor_jump(struct window_pane *wp)
xx = window_copy_find_length(wp, py);
while (px < xx) {
gc = grid_peek_cell(back_s->grid, px, py);
grid_cell_get(gc, &ud);
if (!(gc->flags & GRID_FLAG_PADDING) &&
ud.size == 1 && *ud.data == data->jumpchar) {
grid_get_cell(back_s->grid, px, py, &gc);
if (!(gc.flags & GRID_FLAG_PADDING) &&
gc.data.size == 1 && *gc.data.data == data->jumpchar) {
window_copy_update_cursor(wp, px, data->cy);
if (window_copy_update_selection(wp, 1))
window_copy_redraw_lines(wp, data->cy, 1);
@ -1936,8 +1931,7 @@ window_copy_cursor_jump_back(struct window_pane *wp)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *back_s = data->backing;
const struct grid_cell *gc;
struct utf8_data ud;
struct grid_cell gc;
u_int px, py;
px = data->cx;
@ -1947,10 +1941,9 @@ window_copy_cursor_jump_back(struct window_pane *wp)
px--;
for (;;) {
gc = grid_peek_cell(back_s->grid, px, py);
grid_cell_get(gc, &ud);
if (!(gc->flags & GRID_FLAG_PADDING) &&
ud.size == 1 && *ud.data == data->jumpchar) {
grid_get_cell(back_s->grid, px, py, &gc);
if (!(gc.flags & GRID_FLAG_PADDING) &&
gc.data.size == 1 && *gc.data.data == data->jumpchar) {
window_copy_update_cursor(wp, px, data->cy);
if (window_copy_update_selection(wp, 1))
window_copy_redraw_lines(wp, data->cy, 1);
@ -1967,8 +1960,7 @@ window_copy_cursor_jump_to(struct window_pane *wp, int jump_again)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *back_s = data->backing;
const struct grid_cell *gc;
struct utf8_data ud;
struct grid_cell gc;
u_int px, py, xx;
px = data->cx + 1 + jump_again;
@ -1976,10 +1968,9 @@ window_copy_cursor_jump_to(struct window_pane *wp, int jump_again)
xx = window_copy_find_length(wp, py);
while (px < xx) {
gc = grid_peek_cell(back_s->grid, px, py);
grid_cell_get(gc, &ud);
if (!(gc->flags & GRID_FLAG_PADDING) &&
ud.size == 1 && *ud.data == data->jumpchar) {
grid_get_cell(back_s->grid, px, py, &gc);
if (!(gc.flags & GRID_FLAG_PADDING) &&
gc.data.size == 1 && *gc.data.data == data->jumpchar) {
window_copy_update_cursor(wp, px - 1, data->cy);
if (window_copy_update_selection(wp, 1))
window_copy_redraw_lines(wp, data->cy, 1);
@ -1994,8 +1985,7 @@ window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *back_s = data->backing;
const struct grid_cell *gc;
struct utf8_data ud;
struct grid_cell gc;
u_int px, py;
px = data->cx;
@ -2008,10 +1998,9 @@ window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again)
px--;
for (;;) {
gc = grid_peek_cell(back_s->grid, px, py);
grid_cell_get(gc, &ud);
if (!(gc->flags & GRID_FLAG_PADDING) &&
ud.size == 1 && *ud.data == data->jumpchar) {
grid_get_cell(back_s->grid, px, py, &gc);
if (!(gc.flags & GRID_FLAG_PADDING) &&
gc.data.size == 1 && *gc.data.data == data->jumpchar) {
window_copy_update_cursor(wp, px + 1, data->cy);
if (window_copy_update_selection(wp, 1))
window_copy_redraw_lines(wp, data->cy, 1);