mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
Merge branch 'no-more-linear'
This commit is contained in:
commit
8cf7d7f7ab
8 changed files with 446 additions and 653 deletions
115
csi.c
115
csi.c
|
|
@ -16,29 +16,29 @@
|
|||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
static const struct rgba colors_regular[] = {
|
||||
{0.000000, 0.000000, 0.000000, 1.000000}, /* 0x000000ff */
|
||||
{0.800000, 0.576471, 0.576471, 1.000000}, /* 0xcc9393ff */
|
||||
{0.498039, 0.623529, 0.498039, 1.000000}, /* 0x7f9f7fff */
|
||||
{0.815686, 0.749020, 0.560784, 1.000000}, /* 0xd0bf8fff */
|
||||
{0.423529, 0.627451, 0.639216, 1.000000}, /* 0x6ca0a3ff */
|
||||
{0.862745, 0.549020, 0.764706, 1.000000}, /* 0xdc8cc3ff */
|
||||
{0.576471, 0.878431, 0.890196, 1.000000}, /* 0x93e0e3ff */
|
||||
{0.862745, 0.862745, 0.800000, 1.000000}, /* 0xdcdcccff */
|
||||
static const struct rgb colors_regular[] = {
|
||||
{0.000000, 0.000000, 0.000000}, /* 0x000000 */
|
||||
{0.800000, 0.576471, 0.576471}, /* 0xcc9393 */
|
||||
{0.498039, 0.623529, 0.498039}, /* 0x7f9f7f */
|
||||
{0.815686, 0.749020, 0.560784}, /* 0xd0bf8f */
|
||||
{0.423529, 0.627451, 0.639216}, /* 0x6ca0a3 */
|
||||
{0.862745, 0.549020, 0.764706}, /* 0xdc8cc3 */
|
||||
{0.576471, 0.878431, 0.890196}, /* 0x93e0e3 */
|
||||
{0.862745, 0.862745, 0.800000}, /* 0xdcdccc */
|
||||
};
|
||||
|
||||
static const struct rgba colors_bright[] = {
|
||||
{0.000000, 0.000000, 0.000000, 1.000000}, /* 0x000000ff */
|
||||
{0.862745, 0.639216, 0.639216, 1.000000}, /* 0xdca3a3ff */
|
||||
{0.749020, 0.921569, 0.749020, 1.000000}, /* 0xbfebbfff */
|
||||
{0.941176, 0.874510, 0.686275, 1.000000}, /* 0xf0dfafff */
|
||||
{0.549020, 0.815686, 0.827451, 1.000000}, /* 0x8cd0d3ff */
|
||||
{0.862745, 0.549020, 0.764706, 1.000000}, /* 0xdc8cc3ff */
|
||||
{0.576471, 0.878431, 0.890196, 1.000000}, /* 0x93e0e3ff */
|
||||
{1.000000, 1.000000, 1.000000, 1.000000}, /* 0xffffffff */
|
||||
static const struct rgb colors_bright[] = {
|
||||
{0.000000, 0.000000, 0.000000}, /* 0x000000 */
|
||||
{0.862745, 0.639216, 0.639216}, /* 0xdca3a3 */
|
||||
{0.749020, 0.921569, 0.749020}, /* 0xbfebbf */
|
||||
{0.941176, 0.874510, 0.686275}, /* 0xf0dfaf */
|
||||
{0.549020, 0.815686, 0.827451}, /* 0x8cd0d3 */
|
||||
{0.862745, 0.549020, 0.764706}, /* 0xdc8cc3 */
|
||||
{0.576471, 0.878431, 0.890196}, /* 0x93e0e3 */
|
||||
{1.000000, 1.000000, 1.000000}, /* 0xffffff */
|
||||
};
|
||||
|
||||
static struct rgba colors256[256];
|
||||
static struct rgb colors256[256];
|
||||
|
||||
static void __attribute__((constructor))
|
||||
initialize_colors256(void)
|
||||
|
|
@ -51,22 +51,20 @@ initialize_colors256(void)
|
|||
for (size_t r = 0; r < 6; r++) {
|
||||
for (size_t g = 0; g < 6; g++) {
|
||||
for (size_t b = 0; b < 6; b++) {
|
||||
colors256[16 + r * 6 * 6 + g * 6 + b] = (struct rgba) {
|
||||
colors256[16 + r * 6 * 6 + g * 6 + b] = (struct rgb) {
|
||||
r * 51 / 255.0,
|
||||
g * 51 / 255.0,
|
||||
b * 51 / 255.0,
|
||||
1.0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 24; i++){
|
||||
colors256[232 + i] = (struct rgba) {
|
||||
colors256[232 + i] = (struct rgb) {
|
||||
i * 11 / 255.0,
|
||||
i * 11 / 255.0,
|
||||
i * 11 / 255.0,
|
||||
1.0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -152,11 +150,10 @@ csi_sgr(struct terminal *term)
|
|||
uint8_t r = term->vt.params.v[i + 2].value;
|
||||
uint8_t g = term->vt.params.v[i + 3].value;
|
||||
uint8_t b = term->vt.params.v[i + 4].value;
|
||||
term->vt.attrs.foreground = (struct rgba) {
|
||||
term->vt.attrs.foreground = (struct rgb) {
|
||||
r / 255.0,
|
||||
g / 255.0,
|
||||
b / 255.0,
|
||||
1.0,
|
||||
};
|
||||
term->vt.attrs.have_foreground = true;
|
||||
i += 4;
|
||||
|
|
@ -199,11 +196,10 @@ csi_sgr(struct terminal *term)
|
|||
uint8_t r = term->vt.params.v[i + 2].value;
|
||||
uint8_t g = term->vt.params.v[i + 3].value;
|
||||
uint8_t b = term->vt.params.v[i + 4].value;
|
||||
term->vt.attrs.background = (struct rgba) {
|
||||
term->vt.attrs.background = (struct rgb) {
|
||||
r / 255.0,
|
||||
g / 255.0,
|
||||
b / 255.0,
|
||||
1.0
|
||||
};
|
||||
term->vt.attrs.have_background = true;
|
||||
i += 4;
|
||||
|
|
@ -338,25 +334,26 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
/* Erase screen */
|
||||
|
||||
int param = param_get(term, 0, 0);
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
switch (param) {
|
||||
case 0:
|
||||
/* From cursor to end of screen */
|
||||
start = term->cursor.linear;
|
||||
end = term->cols * term->rows;
|
||||
term_erase(
|
||||
term,
|
||||
&term->cursor,
|
||||
&(struct coord){term->cols - 1, term->rows - 1});
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* From start of screen to cursor */
|
||||
start = 0;
|
||||
end = term->cursor.linear;
|
||||
term_erase(term, &(struct coord){0, 0}, &term->cursor);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* Erase entire screen */
|
||||
start = 0;
|
||||
end = term->cols * term->rows;
|
||||
term_erase(
|
||||
term,
|
||||
&(struct coord){0, 0},
|
||||
&(struct coord){term->cols - 1, term->rows - 1});
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -365,8 +362,6 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
abort();
|
||||
break;
|
||||
}
|
||||
|
||||
term_erase(term, start, end);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -374,25 +369,27 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
/* Erase line */
|
||||
|
||||
int param = param_get(term, 0, 0);
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
switch (param) {
|
||||
case 0:
|
||||
/* From cursor to end of line */
|
||||
start = term->cursor.linear;
|
||||
end = term_cursor_linear(term, term->cursor.row, term->cols);
|
||||
term_erase(
|
||||
term,
|
||||
&term->cursor,
|
||||
&(struct coord){term->cols - 1, term->cursor.row});
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* From start of line to cursor */
|
||||
start = term_cursor_linear(term, term->cursor.row, 0);
|
||||
end = term->cursor.linear;
|
||||
term_erase(
|
||||
term, &(struct coord){0, term->cursor.row}, &term->cursor);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* Entire line */
|
||||
start = term_cursor_linear(term, term->cursor.row, 0);
|
||||
end = term_cursor_linear(term, term->cursor.row, term->cols);
|
||||
term_erase(
|
||||
term,
|
||||
&(struct coord){0, term->cursor.row},
|
||||
&(struct coord){term->cols - 1, term->cursor.row});
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -402,7 +399,6 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
break;
|
||||
}
|
||||
|
||||
term_erase(term, start, end);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -453,15 +449,25 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
int remaining = term->cols - (term->cursor.col + count);
|
||||
|
||||
/* 'Delete' characters by moving the remaining ones */
|
||||
memmove(&term->grid->cur_line[term->cursor.col],
|
||||
&term->grid->cur_line[term->cursor.col + count],
|
||||
remaining * sizeof(term->grid->cur_line[0]));
|
||||
memmove(&term->grid->cur_row->cells[term->cursor.col],
|
||||
&term->grid->cur_row->cells[term->cursor.col + count],
|
||||
remaining * sizeof(term->grid->cur_row->cells[0]));
|
||||
#if 0
|
||||
term_damage_update(term, term->cursor.linear, remaining);
|
||||
#else
|
||||
term->grid->cur_row->dirty = true;
|
||||
#endif
|
||||
|
||||
/* Erase the remainder of the line */
|
||||
term_erase(
|
||||
term,
|
||||
&(struct coord){term->cursor.col + remaining, term->cursor.row},
|
||||
&(struct coord){term->cols - 1, term->cursor.row});
|
||||
#if 0
|
||||
term_erase(
|
||||
term, term->cursor.linear + remaining,
|
||||
term->cursor.linear + remaining + count);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -478,9 +484,16 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
int count = min(
|
||||
param_get(term, 0, 1), term->cols - term->cursor.col);
|
||||
|
||||
term_erase(
|
||||
term,
|
||||
&term->cursor,
|
||||
&(struct coord){term->cursor.col + count, term->cursor.row});
|
||||
|
||||
#if 0
|
||||
memset(&term->grid->cur_line[term->cursor.col],
|
||||
0, count * sizeof(term->grid->cur_line[0]));
|
||||
term_damage_erase(term, term->cursor.linear, count);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -630,8 +643,8 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
tll_free(term->alt.damage);
|
||||
tll_free(term->alt.scroll_damage);
|
||||
|
||||
grid_memclear(term->grid, 0, term->rows * term->cols);
|
||||
term_damage_erase(term, 0, term->rows * term->cols);
|
||||
//grid_memclear(term->grid, 0, term->rows * term->cols);
|
||||
//term_damage_erase(term, 0, term->rows * term->cols);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
3
grid.c
3
grid.c
|
|
@ -6,7 +6,7 @@
|
|||
#define LOG_MODULE "grid"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
|
||||
#if 0
|
||||
struct cell *
|
||||
grid_get_range(struct grid *grid, int start, int *length)
|
||||
{
|
||||
|
|
@ -89,3 +89,4 @@ grid_memmove(struct grid *grid, int dst, int src, int length)
|
|||
copy_idx += count;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
28
grid.h
28
grid.h
|
|
@ -3,6 +3,34 @@
|
|||
#include <stddef.h>
|
||||
#include "terminal.h"
|
||||
|
||||
static inline struct row *
|
||||
grid_row(struct grid *grid, int row)
|
||||
{
|
||||
assert(grid->offset >= 0);
|
||||
return grid->rows[(grid->offset + row + grid->num_rows) % grid->num_rows];
|
||||
}
|
||||
|
||||
static inline void
|
||||
grid_swap_row(struct grid *grid, int row_a, int row_b)
|
||||
{
|
||||
assert(grid->offset >= 0);
|
||||
assert(row_a != row_b);
|
||||
assert(row_a >= 0);
|
||||
assert(row_b >= 0);
|
||||
|
||||
int real_a = (grid->offset + row_a + grid->num_rows) % grid->num_rows;
|
||||
int real_b = (grid->offset + row_b + grid->num_rows) % grid->num_rows;
|
||||
|
||||
struct row *tmp = grid->rows[real_a];
|
||||
grid->rows[real_a] = grid->rows[real_b];
|
||||
grid->rows[real_b] = tmp;
|
||||
|
||||
grid->rows[real_a]->dirty = true;
|
||||
grid->rows[real_b]->dirty = true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
struct cell *grid_get_range(struct grid *grid, int start, int *length);
|
||||
void grid_memclear(struct grid *grid, int start, int length);
|
||||
void grid_memmove(struct grid *grid, int dst, int src, int length);
|
||||
#endif
|
||||
|
|
|
|||
41
main.c
41
main.c
|
|
@ -10,8 +10,6 @@
|
|||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
|
||||
//#include <termios.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-cursor.h>
|
||||
#include <xdg-shell.h>
|
||||
|
|
@ -32,12 +30,15 @@
|
|||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
static const struct rgba default_foreground = {0.86, 0.86, 0.86, 1.0};
|
||||
static const struct rgba default_background = {0.067, 0.067, 0.067, 1.0};
|
||||
static const struct rgb default_foreground = {0.86, 0.86, 0.86};
|
||||
static const struct rgb default_background = {0.067, 0.067, 0.067};
|
||||
|
||||
static void
|
||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
if (format == WL_SHM_FORMAT_ARGB8888)
|
||||
term->wl.have_argb8888 = true;
|
||||
}
|
||||
|
||||
static const struct wl_shm_listener shm_listener = {
|
||||
|
|
@ -107,7 +108,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||
term->wl.shm = wl_registry_bind(
|
||||
term->wl.registry, name, &wl_shm_interface, 1);
|
||||
wl_shm_add_listener(term->wl.shm, &shm_listener, NULL);
|
||||
wl_shm_add_listener(term->wl.shm, &shm_listener, term);
|
||||
wl_display_roundtrip(term->wl.display);
|
||||
}
|
||||
|
||||
|
|
@ -378,6 +379,10 @@ main(int argc, char *const *argv)
|
|||
LOG_ERR("no XDG shell interface");
|
||||
goto out;
|
||||
}
|
||||
if (!term.wl.have_argb8888) {
|
||||
LOG_ERR("compositor does not support ARGB surfaces");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Cursor */
|
||||
term.wl.pointer.surface = wl_compositor_create_surface(term.wl.compositor);
|
||||
|
|
@ -491,7 +496,7 @@ main(int argc, char *const *argv)
|
|||
|
||||
if (ret == 0 || !(timeout_ms != -1 && fds[1].revents & POLLIN)) {
|
||||
/* Delayed rendering */
|
||||
if (!term.frame_is_scheduled)
|
||||
if (term.frame_callback == NULL)
|
||||
grid_render(&term);
|
||||
}
|
||||
|
||||
|
|
@ -579,6 +584,8 @@ out:
|
|||
mtx_unlock(&term.kbd.repeat.mutex);
|
||||
|
||||
shm_fini();
|
||||
if (term.frame_callback != NULL)
|
||||
wl_callback_destroy(term.frame_callback);
|
||||
if (term.wl.xdg_toplevel != NULL)
|
||||
xdg_toplevel_destroy(term.wl.xdg_toplevel);
|
||||
if (term.wl.xdg_surface != NULL)
|
||||
|
|
@ -589,6 +596,10 @@ out:
|
|||
wl_pointer_destroy(term.wl.pointer.pointer);
|
||||
if (term.wl.pointer.surface != NULL)
|
||||
wl_surface_destroy(term.wl.pointer.surface);
|
||||
if (term.wl.keyboard != NULL)
|
||||
wl_keyboard_destroy(term.wl.keyboard);
|
||||
if (term.wl.seat != NULL)
|
||||
wl_seat_destroy(term.wl.seat);
|
||||
if (term.wl.surface != NULL)
|
||||
wl_surface_destroy(term.wl.surface);
|
||||
if (term.wl.shell != NULL)
|
||||
|
|
@ -601,9 +612,23 @@ out:
|
|||
wl_registry_destroy(term.wl.registry);
|
||||
if (term.wl.display != NULL)
|
||||
wl_display_disconnect(term.wl.display);
|
||||
if (term.kbd.xkb_keymap != NULL)
|
||||
xkb_keymap_unref(term.kbd.xkb_keymap);
|
||||
if (term.kbd.xkb_state != NULL)
|
||||
xkb_state_unref(term.kbd.xkb_state);
|
||||
if (term.kbd.xkb != NULL)
|
||||
xkb_context_unref(term.kbd.xkb);
|
||||
|
||||
free(term.normal.cells);
|
||||
free(term.alt.cells);
|
||||
for (int row = 0; row < term.normal.num_rows; row++) {
|
||||
free(term.normal.rows[row]->cells);
|
||||
free(term.normal.rows[row]);
|
||||
}
|
||||
free(term.normal.rows);
|
||||
for (int row = 0; row < term.alt.num_rows; row++) {
|
||||
free(term.alt.rows[row]->cells);
|
||||
free(term.alt.rows[row]);
|
||||
}
|
||||
free(term.alt.rows);
|
||||
|
||||
for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++) {
|
||||
if (term.fonts[i] != NULL)
|
||||
|
|
|
|||
505
render.c
505
render.c
|
|
@ -10,6 +10,7 @@
|
|||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "shm.h"
|
||||
#include "grid.h"
|
||||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
|
@ -27,227 +28,93 @@ struct glyph_sequence {
|
|||
int count;
|
||||
|
||||
struct attributes attrs;
|
||||
struct rgba foreground;
|
||||
struct rgb foreground;
|
||||
};
|
||||
|
||||
static struct glyph_sequence gseq;
|
||||
|
||||
static void
|
||||
grid_render_update(struct terminal *term, struct buffer *buf, const struct damage *dmg)
|
||||
render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
|
||||
int col, int row)
|
||||
{
|
||||
LOG_DBG("damage: UPDATE: %d -> %d (offset = %d)",
|
||||
(dmg->range.start - term->grid->offset) % term->grid->size,
|
||||
(dmg->range.start - term->grid->offset) % term->grid->size + dmg->range.length,
|
||||
term->grid->offset);
|
||||
/* Cursor here? */
|
||||
bool has_cursor
|
||||
= (!term->hide_cursor &&
|
||||
(term->cursor.col == col && term->cursor.row == row));
|
||||
|
||||
int start = dmg->range.start;
|
||||
int length = dmg->range.length;
|
||||
double width = term->cell_width;
|
||||
double height = term->cell_height;
|
||||
double x = col * width;
|
||||
double y = row * height;
|
||||
|
||||
if (start < term->grid->offset) {
|
||||
int end = start + length;
|
||||
if (end >= term->grid->offset) {
|
||||
start = term->grid->offset;
|
||||
length = end - start;
|
||||
} else
|
||||
return;
|
||||
const struct rgb *foreground = cell->attrs.have_foreground
|
||||
? &cell->attrs.foreground
|
||||
: !term->reverse ? &term->foreground : &term->background;
|
||||
const struct rgb *background = cell->attrs.have_background
|
||||
? &cell->attrs.background
|
||||
: !term->reverse ? &term->background : &term->foreground;
|
||||
|
||||
/* If *one* is set, we reverse */
|
||||
if (has_cursor ^ cell->attrs.reverse) {
|
||||
const struct rgb *swap = foreground;
|
||||
foreground = background;
|
||||
background = swap;
|
||||
}
|
||||
|
||||
const int cols = term->cols;
|
||||
/* Background */
|
||||
cairo_set_source_rgb(buf->cairo, background->r, background->g, background->b);
|
||||
cairo_rectangle(buf->cairo, x, y, width, height);
|
||||
cairo_fill(buf->cairo);
|
||||
|
||||
for (int linear_cursor = start,
|
||||
row = ((start - term->grid->offset) % term->grid->size) / cols,
|
||||
col = ((start - term->grid->offset) % term->grid->size) % cols;
|
||||
linear_cursor < start + length;
|
||||
linear_cursor++,
|
||||
col = col + 1 >= term->cols ? 0 : col + 1,
|
||||
row += col == 0 ? 1 : 0)
|
||||
if (cell->c[0] == '\0' || cell->c[0] == ' ')
|
||||
return;
|
||||
|
||||
if (cell->attrs.conceal)
|
||||
return;
|
||||
|
||||
/*
|
||||
* cairo_show_glyphs() apparently works *much* faster when
|
||||
* called once with a large array of glyphs, compared to
|
||||
* multiple calls with a single glyph.
|
||||
*
|
||||
* So, collect glyphs until cell attributes change, then we
|
||||
* 'flush' (render) the glyphs.
|
||||
*/
|
||||
|
||||
if (memcmp(&cell->attrs, &gseq.attrs, sizeof(cell->attrs)) != 0 ||
|
||||
gseq.count >= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - 10 ||
|
||||
memcmp(&gseq.foreground, foreground, sizeof(*foreground)) != 0)
|
||||
{
|
||||
if (gseq.count >= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - 10)
|
||||
LOG_WARN("hit glyph limit");
|
||||
|
||||
assert(row >= 0);
|
||||
assert(row < term->rows);
|
||||
assert(col >= 0);
|
||||
assert(col < term->cols);
|
||||
cairo_set_scaled_font(buf->cairo, attrs_to_font(term, &gseq.attrs));
|
||||
cairo_set_source_rgb(
|
||||
buf->cairo, gseq.foreground.r, gseq.foreground.g,
|
||||
gseq.foreground.b);
|
||||
|
||||
int cell_idx = linear_cursor % term->grid->size;
|
||||
if (cell_idx < 0)
|
||||
cell_idx += term->grid->size;
|
||||
cairo_show_glyphs(buf->cairo, gseq.glyphs, gseq.count);
|
||||
|
||||
assert(cell_idx >= 0);
|
||||
assert(cell_idx < term->rows * term->cols);
|
||||
|
||||
const struct cell *cell = &term->grid->cells[cell_idx];
|
||||
|
||||
/* Cursor here? */
|
||||
bool has_cursor
|
||||
= (!term->hide_cursor &&
|
||||
(term->cursor.linear == linear_cursor - term->grid->offset));
|
||||
|
||||
int x = col * term->cell_width;
|
||||
int y = row * term->cell_height;
|
||||
int width = term->cell_width;
|
||||
int height = term->cell_height;
|
||||
|
||||
struct rgba foreground = cell->attrs.have_foreground
|
||||
? cell->attrs.foreground
|
||||
: !term->reverse ? term->foreground : term->background;
|
||||
struct rgba background = cell->attrs.have_background
|
||||
? cell->attrs.background
|
||||
: !term->reverse ? term->background : term->foreground;
|
||||
|
||||
if (has_cursor) {
|
||||
struct rgba swap = foreground;
|
||||
foreground = background;
|
||||
background = swap;
|
||||
}
|
||||
|
||||
if (cell->attrs.reverse) {
|
||||
struct rgba swap = foreground;
|
||||
foreground = background;
|
||||
background = swap;
|
||||
}
|
||||
|
||||
/* Background */
|
||||
cairo_set_source_rgba(
|
||||
buf->cairo, background.r, background.g, background.b, background.a);
|
||||
cairo_rectangle(buf->cairo, x, y, width, height);
|
||||
cairo_fill(buf->cairo);
|
||||
|
||||
if (cell->c[0] == '\0' || cell->c[0] == ' ')
|
||||
continue;
|
||||
|
||||
if (cell->attrs.conceal)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* cairo_show_glyphs() apparently works *much* faster when
|
||||
* called once with a large array of glyphs, compared to
|
||||
* multiple calls with a single glyph.
|
||||
*
|
||||
* So, collect glyphs until cell attributes change, then we
|
||||
* 'flush' (render) the glyphs.
|
||||
*/
|
||||
|
||||
if (memcmp(&cell->attrs, &gseq.attrs, sizeof(cell->attrs)) != 0 ||
|
||||
gseq.count >= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - 10 ||
|
||||
memcmp(&gseq.foreground, &foreground, sizeof(foreground)) != 0)
|
||||
{
|
||||
if (gseq.count >= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - 10)
|
||||
LOG_WARN("hit glyph limit");
|
||||
cairo_set_scaled_font(buf->cairo, attrs_to_font(term, &gseq.attrs));
|
||||
cairo_set_source_rgba(
|
||||
buf->cairo, gseq.foreground.r, gseq.foreground.g,
|
||||
gseq.foreground.b, gseq.foreground.a);
|
||||
|
||||
cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER);
|
||||
cairo_show_glyphs(buf->cairo, gseq.glyphs, gseq.count);
|
||||
|
||||
gseq.g = gseq.glyphs;
|
||||
gseq.count = 0;
|
||||
gseq.attrs = cell->attrs;
|
||||
gseq.foreground = foreground;
|
||||
}
|
||||
|
||||
int new_glyphs
|
||||
= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - gseq.count;
|
||||
|
||||
cairo_status_t status = cairo_scaled_font_text_to_glyphs(
|
||||
attrs_to_font(term, &cell->attrs), x, y + term->fextents.ascent,
|
||||
cell->c, strlen(cell->c), &gseq.g, &new_glyphs,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
if (status != CAIRO_STATUS_SUCCESS)
|
||||
continue;
|
||||
|
||||
gseq.g += new_glyphs;
|
||||
gseq.count += new_glyphs;
|
||||
assert(gseq.count <= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]));
|
||||
gseq.g = gseq.glyphs;
|
||||
gseq.count = 0;
|
||||
gseq.attrs = cell->attrs;
|
||||
gseq.foreground = *foreground;
|
||||
}
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface,
|
||||
0, ((dmg->range.start - term->grid->offset) / cols) * term->cell_height,
|
||||
buf->width, (dmg->range.length + cols - 1) / cols * term->cell_height);
|
||||
}
|
||||
int new_glyphs
|
||||
= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - gseq.count;
|
||||
|
||||
static void
|
||||
grid_render_erase(struct terminal *term, struct buffer *buf, const struct damage *dmg)
|
||||
{
|
||||
LOG_DBG("damage: ERASE: %d -> %d (offset = %d)",
|
||||
(dmg->range.start - term->grid->offset) % term->grid->size,
|
||||
(dmg->range.start - term->grid->offset) % term->grid->size + dmg->range.length,
|
||||
term->grid->offset);
|
||||
cairo_status_t status = cairo_scaled_font_text_to_glyphs(
|
||||
attrs_to_font(term, &cell->attrs), x, y + term->fextents.ascent,
|
||||
cell->c, strlen(cell->c), &gseq.g, &new_glyphs,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
assert(dmg->range.start >= term->grid->offset);
|
||||
if (status != CAIRO_STATUS_SUCCESS)
|
||||
return;
|
||||
|
||||
const struct rgba *bg = !term->reverse ?
|
||||
&term->background : &term->foreground;
|
||||
|
||||
cairo_set_source_rgba(buf->cairo, bg->r, bg->g, bg->b, bg->a);
|
||||
|
||||
const int cols = term->cols;
|
||||
|
||||
int start = (dmg->range.start - term->grid->offset) % term->grid->size;
|
||||
int left = dmg->range.length;
|
||||
|
||||
int row = start / cols;
|
||||
int col = start % cols;
|
||||
|
||||
/* Partial initial line */
|
||||
if (col != 0) {
|
||||
int cell_count = min(left, cols - col);
|
||||
|
||||
int x = col * term->cell_width;
|
||||
int y = row * term->cell_height;
|
||||
int width = cell_count * term->cell_width;
|
||||
int height = term->cell_height;
|
||||
|
||||
cairo_rectangle(buf->cairo, x, y, width, height);
|
||||
cairo_fill(buf->cairo);
|
||||
wl_surface_damage_buffer(term->wl.surface, x, y, width, height);
|
||||
|
||||
start += cell_count;
|
||||
left -= cell_count;
|
||||
|
||||
row = start / cols;
|
||||
col = start % cols;
|
||||
}
|
||||
|
||||
assert(left == 0 || col == 0);
|
||||
|
||||
/* One or more full lines left */
|
||||
if (left >= cols) {
|
||||
int line_count = left / cols;
|
||||
|
||||
int x = 0;
|
||||
int y = row * term->cell_height;
|
||||
int width = buf->width;
|
||||
int height = line_count * term->cell_height;
|
||||
|
||||
cairo_rectangle(buf->cairo, x, y, width, height);
|
||||
cairo_fill(buf->cairo);
|
||||
wl_surface_damage_buffer(term->wl.surface, x, y, width, height);
|
||||
|
||||
start += line_count * cols;
|
||||
left -= line_count * cols;
|
||||
|
||||
row += line_count;
|
||||
col = 0;
|
||||
}
|
||||
|
||||
assert(left == 0 || col == 0);
|
||||
assert(left < cols);
|
||||
|
||||
/* Partial last line */
|
||||
if (left > 0) {
|
||||
int x = 0;
|
||||
int y = row * term->cell_height;
|
||||
int width = left * term->cell_width;
|
||||
int height = term->cell_height;
|
||||
|
||||
cairo_rectangle(buf->cairo, x, y, width, height);
|
||||
cairo_fill(buf->cairo);
|
||||
wl_surface_damage_buffer(term->wl.surface, x, y, width, height);
|
||||
}
|
||||
gseq.g += new_glyphs;
|
||||
gseq.count += new_glyphs;
|
||||
assert(gseq.count <= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]));
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -277,19 +144,6 @@ grid_render_scroll(struct terminal *term, struct buffer *buf,
|
|||
|
||||
wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height);
|
||||
}
|
||||
|
||||
const int cols = term->cols;
|
||||
|
||||
struct damage erase = {
|
||||
.type = DAMAGE_ERASE,
|
||||
.range = {
|
||||
.start = term->grid->offset + max(dmg->scroll.region.end - dmg->scroll.lines,
|
||||
dmg->scroll.region.start) * cols,
|
||||
.length = min(dmg->scroll.region.end - dmg->scroll.region.start,
|
||||
dmg->scroll.lines) * cols,
|
||||
},
|
||||
};
|
||||
grid_render_erase(term, buf, &erase);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -319,18 +173,6 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
|
|||
|
||||
wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height);
|
||||
}
|
||||
|
||||
const int cols = term->cols;
|
||||
|
||||
struct damage erase = {
|
||||
.type = DAMAGE_ERASE,
|
||||
.range = {
|
||||
.start = term->grid->offset + dmg->scroll.region.start * cols,
|
||||
.length = min(dmg->scroll.region.end - dmg->scroll.region.start,
|
||||
dmg->scroll.lines) * cols,
|
||||
},
|
||||
};
|
||||
grid_render_erase(term, buf, &erase);
|
||||
}
|
||||
|
||||
static void frame_callback(
|
||||
|
|
@ -345,13 +187,6 @@ grid_render(struct terminal *term)
|
|||
{
|
||||
static int last_cursor;
|
||||
|
||||
if (tll_length(term->grid->damage) == 0 &&
|
||||
tll_length(term->grid->scroll_damage) == 0 &&
|
||||
last_cursor == term->grid->offset + term->cursor.linear)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
assert(term->width > 0);
|
||||
assert(term->height > 0);
|
||||
|
||||
|
|
@ -369,9 +204,9 @@ grid_render(struct terminal *term)
|
|||
int rmargin_width = term->width - rmargin;
|
||||
int bmargin_height = term->height - bmargin;
|
||||
|
||||
const struct rgba *bg = !term->reverse ?
|
||||
const struct rgb *bg = !term->reverse ?
|
||||
&term->background : &term->foreground;
|
||||
cairo_set_source_rgba(buf->cairo, bg->r, bg->g, bg->b, bg->a);
|
||||
cairo_set_source_rgb(buf->cairo, bg->r, bg->g, bg->b);
|
||||
|
||||
cairo_rectangle(buf->cairo, rmargin, 0, rmargin_width, term->height);
|
||||
cairo_rectangle(buf->cairo, 0, bmargin, term->width, bmargin_height);
|
||||
|
|
@ -388,6 +223,8 @@ grid_render(struct terminal *term)
|
|||
last_buf = buf;
|
||||
}
|
||||
|
||||
bool all_clean = tll_length(term->grid->scroll_damage) == 0;
|
||||
|
||||
tll_foreach(term->grid->scroll_damage, it) {
|
||||
switch (it->item.type) {
|
||||
case DAMAGE_SCROLL:
|
||||
|
|
@ -397,11 +234,6 @@ grid_render(struct terminal *term)
|
|||
case DAMAGE_SCROLL_REVERSE:
|
||||
grid_render_scroll_reverse(term, buf, &it->item);
|
||||
break;
|
||||
|
||||
case DAMAGE_UPDATE:
|
||||
case DAMAGE_ERASE:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
tll_remove(term->grid->scroll_damage, it);
|
||||
|
|
@ -410,57 +242,75 @@ grid_render(struct terminal *term)
|
|||
gseq.g = gseq.glyphs;
|
||||
gseq.count = 0;
|
||||
|
||||
tll_foreach(term->grid->damage, it) {
|
||||
switch (it->item.type) {
|
||||
case DAMAGE_ERASE: grid_render_erase(term, buf, &it->item); break;
|
||||
case DAMAGE_UPDATE: grid_render_update(term, buf, &it->item); break;
|
||||
for (int r = 0; r < term->rows; r++) {
|
||||
struct row *row = grid_row(term->grid, r);
|
||||
|
||||
case DAMAGE_SCROLL:
|
||||
case DAMAGE_SCROLL_REVERSE:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
if (!row->dirty)
|
||||
continue;
|
||||
|
||||
tll_remove(term->grid->damage, it);
|
||||
//LOG_WARN("rendering line: %d", r);
|
||||
|
||||
for (int col = 0; col < term->cols; col++)
|
||||
render_cell(term, buf, &row->cells[col], col, r);
|
||||
|
||||
row->dirty = false;
|
||||
all_clean = false;
|
||||
|
||||
wl_surface_damage_buffer(term->wl.surface, 0, r * term->cell_height, term->width, term->cell_height);
|
||||
}
|
||||
|
||||
/* TODO: break out to function */
|
||||
/* Re-render last cursor cell and current cursor cell */
|
||||
/* Make sure previous cursor is refreshed (to avoid "ghost" cursors) */
|
||||
if (last_cursor != term->cursor.linear) {
|
||||
struct damage prev_cursor = {
|
||||
.type = DAMAGE_UPDATE,
|
||||
.range = {.start = last_cursor, .length = 1},
|
||||
};
|
||||
grid_render_update(term, buf, &prev_cursor);
|
||||
int cursor_as_linear
|
||||
= (term->grid->offset + term->cursor.row) * term->cols + term->cursor.col;
|
||||
|
||||
if (last_cursor != cursor_as_linear) {
|
||||
int row = last_cursor / term->cols - term->grid->offset;
|
||||
int col = last_cursor % term->cols;
|
||||
if (row >= 0 && row < term->rows) {
|
||||
render_cell(term, buf, &grid_row(term->grid, row)->cells[col], col, row);
|
||||
all_clean = false;
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface, col * term->cell_width, row * term->cell_height,
|
||||
term->cell_width, term->cell_height);
|
||||
}
|
||||
last_cursor = cursor_as_linear;
|
||||
}
|
||||
|
||||
struct damage cursor = {
|
||||
.type = DAMAGE_UPDATE,
|
||||
.range = {.start = term->grid->offset + term->cursor.linear, .length = 1},
|
||||
};
|
||||
grid_render_update(term, buf, &cursor);
|
||||
last_cursor = term->grid->offset + term->cursor.linear;
|
||||
if (all_clean) {
|
||||
buf->busy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
render_cell(
|
||||
term, buf,
|
||||
&grid_row(term->grid, term->cursor.row)->cells[term->cursor.col],
|
||||
term->cursor.col, term->cursor.row);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface,
|
||||
term->cursor.col * term->cell_width,
|
||||
term->cursor.row * term->cell_height,
|
||||
term->cell_width, term->cell_height);
|
||||
|
||||
if (gseq.count > 0) {
|
||||
cairo_set_scaled_font(buf->cairo, attrs_to_font(term, &gseq.attrs));
|
||||
cairo_set_source_rgba(
|
||||
cairo_set_source_rgb(
|
||||
buf->cairo, gseq.foreground.r, gseq.foreground.g,
|
||||
gseq.foreground.b, gseq.foreground.a);
|
||||
cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER);
|
||||
gseq.foreground.b);
|
||||
cairo_show_glyphs(buf->cairo, gseq.glyphs, gseq.count);
|
||||
}
|
||||
|
||||
term->grid->offset %= term->grid->size;
|
||||
if (term->grid->offset < 0)
|
||||
term->grid->offset += term->grid->size;
|
||||
assert(term->grid->offset >= 0 && term->grid->offset < term->grid->num_rows);
|
||||
|
||||
//cairo_surface_flush(buf->cairo_surface);
|
||||
cairo_surface_flush(buf->cairo_surface);
|
||||
wl_surface_attach(term->wl.surface, buf->wl_buf, 0, 0);
|
||||
|
||||
struct wl_callback *cb = wl_surface_frame(term->wl.surface);
|
||||
wl_callback_add_listener(cb, &frame_listener, term);
|
||||
term->frame_is_scheduled = true;
|
||||
assert(term->frame_callback == NULL);
|
||||
term->frame_callback = wl_surface_frame(term->wl.surface);
|
||||
wl_callback_add_listener(term->frame_callback, &frame_listener, term);
|
||||
|
||||
wl_surface_commit(term->wl.surface);
|
||||
}
|
||||
|
|
@ -470,11 +320,32 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
|
|||
{
|
||||
struct terminal *term = data;
|
||||
|
||||
term->frame_is_scheduled = false;
|
||||
assert(term->frame_callback == wl_callback);
|
||||
wl_callback_destroy(wl_callback);
|
||||
term->frame_callback = NULL;
|
||||
grid_render(term);
|
||||
}
|
||||
|
||||
static void
|
||||
reflow(struct row **new_grid, int new_cols, int new_rows,
|
||||
struct row *const *old_grid, int old_cols, int old_rows)
|
||||
{
|
||||
/* TODO: actually reflow */
|
||||
for (int r = 0; r < min(new_rows, old_rows); r++) {
|
||||
size_t copy_cols = min(new_cols, old_cols);
|
||||
size_t clear_cols = new_cols - copy_cols;
|
||||
|
||||
struct cell *new_cells = new_grid[r]->cells;
|
||||
const struct cell *old_cells = old_grid[r]->cells;
|
||||
|
||||
memcpy(new_cells, old_cells, copy_cols * sizeof(new_cells[0]));
|
||||
memset(&new_cells[copy_cols], 0, clear_cols * sizeof(new_cells[0]));
|
||||
}
|
||||
|
||||
for (int r = min(new_rows, old_rows); r < new_rows; r++)
|
||||
memset(new_grid[r]->cells, 0, new_cols * sizeof(new_grid[r]->cells[0]));
|
||||
}
|
||||
|
||||
/* Move to terminal.c? */
|
||||
void
|
||||
render_resize(struct terminal *term, int width, int height)
|
||||
|
|
@ -485,44 +356,60 @@ render_resize(struct terminal *term, int width, int height)
|
|||
term->width = width;
|
||||
term->height = height;
|
||||
|
||||
const size_t old_rows = term->rows;
|
||||
const size_t normal_old_size = term->normal.size;
|
||||
const size_t alt_old_size = term->alt.size;
|
||||
const int old_cols = term->cols;
|
||||
const int old_rows = term->rows;
|
||||
const int old_normal_grid_rows = term->normal.num_rows;
|
||||
const int old_alt_grid_rows = term->alt.num_rows;
|
||||
|
||||
term->cols = term->width / term->cell_width;
|
||||
term->rows = term->height / term->cell_height;
|
||||
const int new_cols = term->width / term->cell_width;
|
||||
const int new_rows = term->height / term->cell_height;
|
||||
const int new_normal_grid_rows = new_rows;
|
||||
const int new_alt_grid_rows = new_rows;
|
||||
|
||||
term->normal.size = term->cols * term->rows;
|
||||
term->alt.size = term->cols * term->rows;
|
||||
|
||||
term->normal.cells = realloc(
|
||||
term->normal.cells,
|
||||
term->normal.size * sizeof(term->normal.cells[0]));
|
||||
term->alt.cells = realloc(
|
||||
term->alt.cells,
|
||||
term->alt.size * sizeof(term->alt.cells[0]));
|
||||
|
||||
term->normal.offset
|
||||
= (term->normal.offset + term->cols - 1) / term->cols * term->cols;
|
||||
term->alt.offset
|
||||
= (term->alt.offset + term->cols - 1) / term->cols * term->cols;
|
||||
|
||||
/* TODO: memset */
|
||||
for (size_t i = normal_old_size; i < term->normal.size; i++) {
|
||||
term->normal.cells[i] = (struct cell){
|
||||
.attrs = {.foreground = term->foreground,
|
||||
.background = term->background},
|
||||
};
|
||||
/* Allocate new 'normal' grid */
|
||||
struct row **normal = malloc(new_normal_grid_rows * sizeof(normal[0]));
|
||||
for (int r = 0; r < new_normal_grid_rows; r++) {
|
||||
struct row *row = malloc(sizeof(*row));
|
||||
row->cells = malloc(new_cols * sizeof(row->cells[0]));
|
||||
normal[r] = row;
|
||||
}
|
||||
|
||||
/* TODO: memset */
|
||||
for (size_t i = alt_old_size; i < term->alt.size; i++) {
|
||||
term->alt.cells[i] = (struct cell){
|
||||
.attrs = {.foreground = term->foreground,
|
||||
.background = term->background},
|
||||
};
|
||||
/* Allocate new 'alt' grid */
|
||||
struct row **alt = malloc(new_alt_grid_rows * sizeof(alt[0]));
|
||||
for (int r = 0; r < new_alt_grid_rows; r++) {
|
||||
struct row *row = malloc(sizeof(*row));
|
||||
row->cells = malloc(new_cols * sizeof(row->cells[0]));
|
||||
alt[r] = row;
|
||||
}
|
||||
|
||||
/* Reflow content */
|
||||
reflow(normal, new_cols, new_normal_grid_rows,
|
||||
term->normal.rows, old_cols, old_normal_grid_rows);
|
||||
reflow(alt, new_cols, new_alt_grid_rows,
|
||||
term->alt.rows, old_cols, old_alt_grid_rows);
|
||||
|
||||
/* Free old 'normal' grid */
|
||||
for (int r = 0; r < term->normal.num_rows; r++) {
|
||||
free(term->normal.rows[r]->cells);
|
||||
free(term->normal.rows[r]);
|
||||
}
|
||||
free(term->normal.rows);
|
||||
|
||||
/* Free old 'alt' grid */
|
||||
for (int r = 0; r < term->alt.num_rows; r++) {
|
||||
free(term->alt.rows[r]->cells);
|
||||
free(term->alt.rows[r]);
|
||||
}
|
||||
free(term->alt.rows);
|
||||
|
||||
term->cols = new_cols;
|
||||
term->rows = new_rows;
|
||||
|
||||
term->normal.rows = normal;
|
||||
term->normal.num_rows = new_normal_grid_rows;
|
||||
term->alt.rows = alt;
|
||||
term->alt.num_rows = new_alt_grid_rows;
|
||||
|
||||
LOG_INFO("resize: %dx%d, grid: cols=%d, rows=%d",
|
||||
term->width, term->height, term->cols, term->rows);
|
||||
|
||||
|
|
@ -537,17 +424,19 @@ render_resize(struct terminal *term, int width, int height)
|
|||
LOG_ERRNO("TIOCSWINSZ");
|
||||
}
|
||||
|
||||
if (term->scroll_region.end == old_rows)
|
||||
if (term->scroll_region.start >= term->rows)
|
||||
term->scroll_region.start = 0;
|
||||
|
||||
if (term->scroll_region.end >= old_rows)
|
||||
term->scroll_region.end = term->rows;
|
||||
|
||||
term_cursor_to(
|
||||
term,
|
||||
min(term->cursor.row, term->rows - 1),
|
||||
min(term->cursor.col, term->cols - 1));
|
||||
|
||||
term_damage_all(term);
|
||||
|
||||
if (!term->frame_is_scheduled)
|
||||
if (term->frame_callback == NULL)
|
||||
grid_render(term);
|
||||
}
|
||||
|
||||
|
|
|
|||
317
terminal.c
317
terminal.c
|
|
@ -4,6 +4,8 @@
|
|||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
#define LOG_MODULE "terminal"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
|
|
@ -12,131 +14,17 @@
|
|||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
static bool
|
||||
damage_merge_range(struct terminal *term, const struct damage *dmg)
|
||||
{
|
||||
if (tll_length(term->grid->damage) == 0)
|
||||
return false;
|
||||
|
||||
struct damage *old = &tll_back(term->grid->damage);
|
||||
if (old->type != dmg->type)
|
||||
return false;
|
||||
|
||||
const int start = dmg->range.start;
|
||||
const int end = start + dmg->range.length;
|
||||
|
||||
const int prev_start = old->range.start;
|
||||
const int prev_end = prev_start + old->range.length;
|
||||
|
||||
if ((start >= prev_start && start <= prev_end) ||
|
||||
(end >= prev_start && end <= prev_end) ||
|
||||
(start <= prev_start && end >= prev_end))
|
||||
{
|
||||
/* The two damage ranges intersect */
|
||||
int new_start = min(start, prev_start);
|
||||
int new_end = max(end, prev_end);
|
||||
|
||||
old->range.start = new_start;
|
||||
old->range.length = new_end - new_start;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
term_damage_update_or_erase(struct terminal *term, enum damage_type damage_type,
|
||||
int start, int length)
|
||||
{
|
||||
#if 1
|
||||
if (tll_length(term->grid->damage) > 1024) {
|
||||
term_damage_all(term);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct damage dmg = {
|
||||
.type = damage_type,
|
||||
.range = {.start = term->grid->offset + start, .length = length},
|
||||
};
|
||||
|
||||
if (damage_merge_range(term, &dmg))
|
||||
return;
|
||||
|
||||
tll_push_back(term->grid->damage, dmg);
|
||||
}
|
||||
|
||||
void
|
||||
term_damage_update(struct terminal *term, int start, int length)
|
||||
{
|
||||
assert(start + length <= term->rows * term->cols);
|
||||
term_damage_update_or_erase(term, DAMAGE_UPDATE, start, length);
|
||||
}
|
||||
|
||||
void
|
||||
term_damage_erase(struct terminal *term, int start, int length)
|
||||
{
|
||||
assert(start + length <= term->rows * term->cols);
|
||||
term_damage_update_or_erase(term, DAMAGE_ERASE, start, length);
|
||||
}
|
||||
|
||||
void
|
||||
term_damage_all(struct terminal *term)
|
||||
{
|
||||
tll_free(term->grid->damage);
|
||||
tll_free(term->grid->scroll_damage);
|
||||
term_damage_update(term, 0, term->rows * term->cols);
|
||||
for (int i = 0; i < term->rows; i++)
|
||||
grid_row(term->grid, i)->dirty = true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
damage_adjust_after_scroll(struct terminal *term, enum damage_type damage_type,
|
||||
struct scroll_region region, int lines)
|
||||
{
|
||||
tll_foreach(term->grid->damage, it) {
|
||||
#if 0
|
||||
if (it->item.range.start < term->grid->offset) {
|
||||
int end = it->item.range.start + it->item.range.length;
|
||||
if (end >= term->grid->offset) {
|
||||
it->item.range.start = term->grid->offset;
|
||||
it->item.range.length = end - it->item.range.start;
|
||||
} else {
|
||||
tll_remove(term->grid->damage, it);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int start = it->item.range.start;
|
||||
int end = start + it->item.range.length;
|
||||
|
||||
if (start -
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
term_damage_scroll(struct terminal *term, enum damage_type damage_type,
|
||||
struct scroll_region region, int lines)
|
||||
{
|
||||
//damage_adjust_after_scroll(term, damage_type, region, lines);
|
||||
if (damage_type == DAMAGE_SCROLL) {
|
||||
tll_foreach(term->grid->damage, it) {
|
||||
int start = it->item.range.start;
|
||||
int length = it->item.range.length;
|
||||
|
||||
if (start < term->grid->offset) {
|
||||
int end = start + length;
|
||||
if (end >= term->grid->offset) {
|
||||
it->item.range.start = term->grid->offset;
|
||||
it->item.range.length = end - it->item.range.start;
|
||||
} else
|
||||
tll_remove(term->grid->damage, it);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tll_length(term->grid->scroll_damage) > 0) {
|
||||
struct damage *dmg = &tll_back(term->grid->scroll_damage);
|
||||
|
||||
|
|
@ -155,43 +43,51 @@ term_damage_scroll(struct terminal *term, enum damage_type damage_type,
|
|||
tll_push_back(term->grid->scroll_damage, dmg);
|
||||
}
|
||||
|
||||
void
|
||||
term_erase(struct terminal *term, int start, int end)
|
||||
static inline void
|
||||
erase_cell_range(struct terminal *term, struct row *row, int start, int end)
|
||||
{
|
||||
LOG_DBG("erase: %d-%d", start, end);
|
||||
assert(end >= start);
|
||||
assert(end <= term->rows * term->cols);
|
||||
assert(start < term->cols);
|
||||
assert(end < term->cols);
|
||||
|
||||
if (term->vt.attrs.have_background) {
|
||||
int erase_start = start;
|
||||
int left = end - erase_start;
|
||||
|
||||
while (left > 0) {
|
||||
int len = left;
|
||||
struct cell *cells = grid_get_range(term->grid, erase_start, &len);
|
||||
|
||||
memset(cells, 0, len * sizeof(cells[0]));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
cells[i].attrs.background = term->vt.attrs.background;
|
||||
cells[i].attrs.have_background = true;
|
||||
}
|
||||
|
||||
erase_start += len;
|
||||
left -= len;
|
||||
if (unlikely(term->vt.attrs.have_background)) {
|
||||
for (int col = start; col <= end; col++) {
|
||||
row->cells[col].c[0] = '\0';
|
||||
row->cells[col].attrs.have_background = true;
|
||||
row->cells[col].attrs.background = term->vt.attrs.background;
|
||||
}
|
||||
|
||||
term_damage_update(term, start, end - start);
|
||||
} else {
|
||||
grid_memclear(term->grid, start, end - start);
|
||||
term_damage_erase(term, start, end - start);
|
||||
memset(&row->cells[start], 0, (end - start + 1) * sizeof(row->cells[0]));
|
||||
}
|
||||
row->dirty = true;
|
||||
}
|
||||
|
||||
int
|
||||
term_cursor_linear(const struct terminal *term, int row, int col)
|
||||
static inline void
|
||||
erase_line(struct terminal *term, struct row *row)
|
||||
{
|
||||
return row * term->cols + col;
|
||||
erase_cell_range(term, row, 0, term->cols - 1);
|
||||
}
|
||||
|
||||
void
|
||||
term_erase(struct terminal *term, const struct coord *start, const struct coord *end)
|
||||
{
|
||||
assert(start->row <= end->row);
|
||||
assert(start->col <= end->col || start->row < end->row);
|
||||
|
||||
if (start->row == end->row) {
|
||||
struct row *row = grid_row(term->grid, start->row);
|
||||
erase_cell_range(term, row, start->col, end->col);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(end->row > start->row);
|
||||
|
||||
erase_cell_range(
|
||||
term, grid_row(term->grid, start->row), start->col, term->cols - 1);
|
||||
|
||||
for (int r = start->row + 1; r < end->row; r++)
|
||||
erase_line(term, grid_row(term->grid, r));
|
||||
|
||||
erase_cell_range(term, grid_row(term->grid, end->row), 0, end->col);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -200,28 +96,20 @@ term_cursor_to(struct terminal *term, int row, int col)
|
|||
assert(row < term->rows);
|
||||
assert(col < term->cols);
|
||||
|
||||
int new_linear = row * term->cols + col;
|
||||
assert(new_linear < term->rows * term->cols);
|
||||
|
||||
term->print_needs_wrap = false;
|
||||
|
||||
term->cursor.linear = new_linear;
|
||||
term->cursor.col = col;
|
||||
term->cursor.row = row;
|
||||
|
||||
int len = term->cols;
|
||||
term->grid->cur_line = grid_get_range(
|
||||
term->grid, term->cursor.linear - col, &len);
|
||||
|
||||
assert(len == term->cols);
|
||||
term->grid->cur_row = grid_row(term->grid, row);
|
||||
}
|
||||
|
||||
void
|
||||
term_cursor_left(struct terminal *term, int count)
|
||||
{
|
||||
int move_amount = min(term->cursor.col, count);
|
||||
term->cursor.linear -= move_amount;
|
||||
term->cursor.col -= move_amount;
|
||||
assert(term->cursor.col >= 0);
|
||||
term->print_needs_wrap = false;
|
||||
}
|
||||
|
||||
|
|
@ -229,8 +117,8 @@ void
|
|||
term_cursor_right(struct terminal *term, int count)
|
||||
{
|
||||
int move_amount = min(term->cols - term->cursor.col - 1, count);
|
||||
term->cursor.linear += move_amount;
|
||||
term->cursor.col += move_amount;
|
||||
assert(term->cursor.col < term->cols);
|
||||
term->print_needs_wrap = false;
|
||||
}
|
||||
|
||||
|
|
@ -253,58 +141,25 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
|
|||
{
|
||||
LOG_DBG("scroll: %d rows", rows);
|
||||
|
||||
if (region.start > 0) {
|
||||
/* TODO: check if it's worth memoving the scroll area instead,
|
||||
* under certain circumstances */
|
||||
assert(rows < term->rows && "unimplemented");
|
||||
|
||||
grid_memmove(term->grid, rows * term->cols, 0, region.start * term->cols);
|
||||
/* Top non-scrolling region */
|
||||
for (int i = region.start - 1; i >= 0; i--)
|
||||
grid_swap_row(term->grid, i, i + rows);
|
||||
|
||||
tll_foreach(term->grid->damage, it) {
|
||||
int start = it->item.range.start - term->grid->offset;
|
||||
int end __attribute__((unused)) = start + it->item.range.length;
|
||||
|
||||
if (start < region.start * term->cols) {
|
||||
assert(end <= region.start * term->cols);
|
||||
it->item.range.start += rows * term->cols;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (region.end < term->rows) {
|
||||
/* Copy scrolled-up bottom region to new bottom region */
|
||||
grid_memmove(
|
||||
term->grid,
|
||||
(region.end + rows) * term->cols,
|
||||
region.end * term->cols,
|
||||
(term->rows - region.end) * term->cols);
|
||||
|
||||
tll_foreach(term->grid->damage, it) {
|
||||
int start = it->item.range.start - term->grid->offset;
|
||||
int end = start + it->item.range.length;
|
||||
|
||||
if (end > region.end * term->cols) {
|
||||
assert(start >= region.end * term->cols);
|
||||
it->item.range.start += rows * term->cols;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Bottom non-scrolling region */
|
||||
for (int i = term->rows - 1; i >= region.end; i--)
|
||||
grid_swap_row(term->grid, i, i + rows);
|
||||
|
||||
/* Offset grid origin */
|
||||
term->grid->offset += rows * term->cols;
|
||||
term->grid->offset += rows;
|
||||
term->grid->offset %= term->grid->num_rows;
|
||||
|
||||
/* Clear scrolled-in lines */
|
||||
grid_memclear(
|
||||
term->grid,
|
||||
max(0, region.end - rows) * term->cols,
|
||||
min(rows, term->rows) * term->cols);
|
||||
for (int r = max(region.end - rows, 0); r < region.end; r++)
|
||||
erase_line(term, grid_row(term->grid, r));
|
||||
|
||||
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
|
||||
|
||||
int len = term->cols;
|
||||
term->grid->cur_line = grid_get_range(
|
||||
term->grid, term->cursor.linear - term->cursor.col, &len);
|
||||
|
||||
assert(len == term->cols);
|
||||
term->grid->cur_row = grid_row(term->grid, term->cursor.row);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -317,56 +172,26 @@ void
|
|||
term_scroll_reverse_partial(struct terminal *term,
|
||||
struct scroll_region region, int rows)
|
||||
{
|
||||
if (region.end < term->rows) {
|
||||
grid_memmove(
|
||||
term->grid,
|
||||
(region.end - rows) * term->cols,
|
||||
region.end * term->cols,
|
||||
(term->rows - region.end) * term->cols);
|
||||
assert(rows < term->rows && "unimplemented");
|
||||
|
||||
tll_foreach(term->grid->damage, it) {
|
||||
int start = it->item.range.start - term->grid->offset;
|
||||
int end = start + it->item.range.length;
|
||||
term->grid->offset += term->grid->num_rows - rows;
|
||||
term->grid->offset %= term->grid->num_rows;
|
||||
|
||||
if (end > region.end * term->cols) {
|
||||
assert(start >= region.end * term->cols);
|
||||
it->item.range.start -= rows * term->cols;
|
||||
}
|
||||
}
|
||||
/* Bottom non-scrolling region */
|
||||
for (int i = region.end + rows; i < term->rows + rows; i++)
|
||||
grid_swap_row(term->grid, i, i - rows);
|
||||
|
||||
}
|
||||
/* Top non-scrolling region */
|
||||
for (int i = 0 + rows; i < region.start + rows; i++)
|
||||
grid_swap_row(term->grid, i, i - rows);
|
||||
|
||||
if (region.start > 0) {
|
||||
grid_memmove(
|
||||
term->grid, -rows * term->cols, 0, region.start * term->cols);
|
||||
|
||||
tll_foreach(term->grid->damage, it) {
|
||||
int start = it->item.range.start - term->grid->offset;
|
||||
int end __attribute__((unused)) = start + it->item.range.length;
|
||||
|
||||
if (start < region.start * term->cols) {
|
||||
if (end > region.start * term->cols) {
|
||||
LOG_ERR("region.start = %d, rows = %d, damage.start = %d, damage.end = %d (%s)",
|
||||
region.start, rows, start, end, it->item.type == DAMAGE_UPDATE ? "UPDATE" : "ERASE");
|
||||
abort();
|
||||
}
|
||||
assert(end <= region.start * term->cols);
|
||||
it->item.range.start -= rows * term->cols;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
term->grid->offset -= rows * term->cols;
|
||||
|
||||
grid_memclear(term->grid, region.start * term->cols, rows * term->cols);
|
||||
term_erase(
|
||||
term,
|
||||
&(struct coord){0, region.start},
|
||||
&(struct coord){term->cols - 1, min(region.start + rows, region.end) - 1});
|
||||
|
||||
term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows);
|
||||
|
||||
int len = term->cols;
|
||||
term->grid->cur_line = grid_get_range(
|
||||
term->grid, term->cursor.linear - term->cursor.col, &len);
|
||||
|
||||
assert(len == term->cols);
|
||||
term->grid->cur_row = grid_row(term->grid, term->cursor.row);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -375,8 +200,6 @@ term_scroll_reverse(struct terminal *term, int rows)
|
|||
term_scroll_reverse_partial(term, term->scroll_region, rows);
|
||||
}
|
||||
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
static int
|
||||
linux_mouse_button_to_x(int button)
|
||||
{
|
||||
|
|
|
|||
63
terminal.h
63
terminal.h
|
|
@ -7,6 +7,7 @@
|
|||
#include <threads.h>
|
||||
|
||||
#include <cairo.h>
|
||||
#include <wayland-client.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
|
||||
|
|
@ -34,9 +35,10 @@ struct wayland {
|
|||
struct xdg_wm_base *shell;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
bool have_argb8888;
|
||||
};
|
||||
|
||||
struct rgba { double r, g, b, a; } __attribute__((packed));
|
||||
struct rgb { double r, g, b; } __attribute__((packed));
|
||||
|
||||
struct attributes {
|
||||
#if 0
|
||||
|
|
@ -60,8 +62,8 @@ struct attributes {
|
|||
uint8_t have_foreground:1;
|
||||
uint8_t have_background:1;
|
||||
#endif
|
||||
struct rgba foreground; /* Only valid when have_foreground == true */
|
||||
struct rgba background; /* Only valid when have_background == true */
|
||||
struct rgb foreground; /* Only valid when have_foreground == true */
|
||||
struct rgb background; /* Only valid when have_background == true */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct cell {
|
||||
|
|
@ -74,36 +76,32 @@ struct scroll_region {
|
|||
int end;
|
||||
};
|
||||
|
||||
struct cursor {
|
||||
int row;
|
||||
struct coord {
|
||||
int col;
|
||||
int linear;
|
||||
int row;
|
||||
};
|
||||
|
||||
enum damage_type {DAMAGE_UPDATE, DAMAGE_ERASE, DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE};
|
||||
enum damage_type {DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE};
|
||||
struct damage {
|
||||
enum damage_type type;
|
||||
union {
|
||||
/* DAMAGE_UPDATE, DAMAGE_ERASE */
|
||||
struct {
|
||||
int start;
|
||||
int length;
|
||||
} range;
|
||||
/* DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE */
|
||||
struct {
|
||||
struct scroll_region region;
|
||||
int lines;
|
||||
} scroll;
|
||||
};
|
||||
|
||||
/* DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE */
|
||||
struct {
|
||||
struct scroll_region region;
|
||||
int lines;
|
||||
} scroll;
|
||||
};
|
||||
struct row {
|
||||
struct cell *cells;
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
struct grid {
|
||||
int size;
|
||||
int num_rows;
|
||||
int offset;
|
||||
|
||||
struct cell *cells;
|
||||
struct cell *cur_line;
|
||||
struct row **rows;
|
||||
struct row *cur_row;
|
||||
|
||||
tll(struct damage) damage;
|
||||
tll(struct damage) scroll_damage;
|
||||
|
|
@ -223,18 +221,18 @@ struct terminal {
|
|||
bool print_needs_wrap;
|
||||
struct scroll_region scroll_region;
|
||||
|
||||
struct rgba foreground;
|
||||
struct rgba background;
|
||||
struct rgb foreground;
|
||||
struct rgb background;
|
||||
|
||||
struct {
|
||||
int row;
|
||||
int col;
|
||||
int row;
|
||||
int button;
|
||||
} mouse;
|
||||
|
||||
struct cursor cursor;
|
||||
struct cursor saved_cursor;
|
||||
struct cursor alt_saved_cursor;
|
||||
struct coord cursor;
|
||||
struct coord saved_cursor;
|
||||
struct coord alt_saved_cursor;
|
||||
|
||||
struct grid normal;
|
||||
struct grid alt;
|
||||
|
|
@ -244,17 +242,16 @@ struct terminal {
|
|||
cairo_font_extents_t fextents;
|
||||
|
||||
struct wayland wl;
|
||||
bool frame_is_scheduled;
|
||||
struct wl_callback *frame_callback;
|
||||
};
|
||||
|
||||
void term_damage_all(struct terminal *term);
|
||||
void term_damage_update(struct terminal *term, int start, int length);
|
||||
void term_damage_erase(struct terminal *term, int start, int length);
|
||||
void term_damage_scroll(
|
||||
struct terminal *term, enum damage_type damage_type,
|
||||
struct scroll_region region, int lines);
|
||||
|
||||
void term_erase(struct terminal *term, int start, int end);
|
||||
void term_erase(
|
||||
struct terminal *term, const struct coord *start, const struct coord *end);
|
||||
|
||||
void term_cursor_to(struct terminal *term, int row, int col);
|
||||
void term_cursor_left(struct terminal *term, int count);
|
||||
|
|
@ -270,8 +267,6 @@ void term_scroll_partial(
|
|||
void term_scroll_reverse_partial(
|
||||
struct terminal *term, struct scroll_region region, int rows);
|
||||
|
||||
int term_cursor_linear(const struct terminal *term, int row, int col);
|
||||
|
||||
void term_mouse_down(struct terminal *term, int button, int row, int col,
|
||||
bool shift, bool alt, bool ctrl);
|
||||
void term_mouse_up(struct terminal *term, int button, int row, int col,
|
||||
|
|
|
|||
27
vt.c
27
vt.c
|
|
@ -658,6 +658,7 @@ pre_print(struct terminal *term)
|
|||
static inline void
|
||||
post_print(struct terminal *term)
|
||||
{
|
||||
term->grid->cur_row->dirty = true;
|
||||
if (term->cursor.col < term->cols - 1)
|
||||
term_cursor_right(term, 1);
|
||||
else
|
||||
|
|
@ -669,11 +670,19 @@ print_insert(struct terminal *term)
|
|||
{
|
||||
if (unlikely(term->insert_mode)) {
|
||||
assert(false && "untested");
|
||||
grid_memmove(
|
||||
term->grid, term->cursor.linear + 1, term->cursor.linear,
|
||||
|
||||
struct row *row = term->grid->cur_row;
|
||||
memmove(
|
||||
&row[term->cursor.col + 1],
|
||||
&row[term->cursor.col],
|
||||
term->cols - term->cursor.col - 1);
|
||||
|
||||
#if 0
|
||||
term_damage_update(
|
||||
term, term->cursor.linear + 1, term->cols - term->cursor.col - 1);
|
||||
#else
|
||||
row->dirty = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -682,8 +691,13 @@ action_print_utf8(struct terminal *term)
|
|||
{
|
||||
pre_print(term);
|
||||
|
||||
struct cell *cell = &term->grid->cur_line[term->cursor.col];
|
||||
struct row *row = term->grid->cur_row;
|
||||
struct cell *cell = &row->cells[term->cursor.col];
|
||||
#if 0
|
||||
term_damage_update(term, term->cursor.linear, 1);
|
||||
#else
|
||||
row->dirty = true;
|
||||
#endif
|
||||
|
||||
print_insert(term);
|
||||
|
||||
|
|
@ -701,8 +715,13 @@ action_print(struct terminal *term, uint8_t c)
|
|||
{
|
||||
pre_print(term);
|
||||
|
||||
struct cell *cell = &term->grid->cur_line[term->cursor.col];
|
||||
struct row *row = term->grid->cur_row;
|
||||
struct cell *cell = &row->cells[term->cursor.col];
|
||||
#if 0
|
||||
term_damage_update(term, term->cursor.linear, 1);
|
||||
#else
|
||||
row->dirty = true;
|
||||
#endif
|
||||
|
||||
print_insert(term);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue