mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-13 05:33:51 -04:00
Merge branch 'scrollback'
This commit is contained in:
commit
0bc3b29b0f
9 changed files with 263 additions and 23 deletions
121
commands.c
Normal file
121
commands.c
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
#include "commands.h"
|
||||||
|
|
||||||
|
#define LOG_MODULE "commands"
|
||||||
|
#define LOG_ENABLE_DBG 0
|
||||||
|
#include "log.h"
|
||||||
|
#include "terminal.h"
|
||||||
|
#include "render.h"
|
||||||
|
#include "grid.h"
|
||||||
|
|
||||||
|
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||||
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||||
|
|
||||||
|
void
|
||||||
|
cmd_scrollback_up(struct terminal *term, int rows)
|
||||||
|
{
|
||||||
|
if (term->grid == &term->alt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rows = min(rows, term->grid->num_rows - term->rows);
|
||||||
|
|
||||||
|
assert(term->grid->offset >= 0);
|
||||||
|
assert(rows <= term->rows);
|
||||||
|
|
||||||
|
int new_view = (term->grid->view + term->grid->num_rows - rows) % term->grid->num_rows;
|
||||||
|
assert(new_view >= 0);
|
||||||
|
assert(new_view < term->grid->num_rows);
|
||||||
|
|
||||||
|
/* Avoid scrolling in uninitialized rows */
|
||||||
|
while (!term->grid->rows[new_view]->initialized)
|
||||||
|
new_view = (new_view + 1) % term->grid->num_rows;
|
||||||
|
|
||||||
|
if (new_view == term->grid->view) {
|
||||||
|
/*
|
||||||
|
* This happens when scrolling up in a newly opened terminal;
|
||||||
|
* every single line (except those already visible) are
|
||||||
|
* uninitalized, and the loop above will bring us back to
|
||||||
|
* where we started.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't scroll past scrollback history */
|
||||||
|
int end = (term->grid->offset + term->rows - 1) % term->grid->num_rows;
|
||||||
|
if (end >= term->grid->offset) {
|
||||||
|
/* Not wrapped */
|
||||||
|
if (new_view >= term->grid->offset && new_view <= end)
|
||||||
|
new_view = end + 1;
|
||||||
|
} else {
|
||||||
|
if (new_view >= term->grid->offset || new_view <= end)
|
||||||
|
new_view = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("scrollback UP: %d -> %d (offset = %d, end = %d, rows = %d)",
|
||||||
|
term->grid->view, new_view, term->grid->offset, end, term->grid->num_rows);
|
||||||
|
|
||||||
|
if (new_view == term->grid->view)
|
||||||
|
return;
|
||||||
|
|
||||||
|
term->grid->view = new_view;
|
||||||
|
|
||||||
|
term_damage_view(term);
|
||||||
|
if (term->frame_callback == NULL)
|
||||||
|
grid_render(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cmd_scrollback_down(struct terminal *term, int rows)
|
||||||
|
{
|
||||||
|
if (term->grid == &term->alt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (term->grid->view == term->grid->offset)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rows = min(rows, term->grid->num_rows - term->rows);
|
||||||
|
|
||||||
|
assert(term->grid->offset >= 0);
|
||||||
|
|
||||||
|
int new_view = (term->grid->view + rows) % term->grid->num_rows;
|
||||||
|
assert(new_view >= 0);
|
||||||
|
assert(new_view < term->grid->num_rows);
|
||||||
|
|
||||||
|
/* Prevent scrolling in uninitialized rows */
|
||||||
|
bool all_initialized = false;
|
||||||
|
do {
|
||||||
|
all_initialized = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < term->rows; i++) {
|
||||||
|
int row_no = (new_view + i) % term->grid->num_rows;
|
||||||
|
if (!term->grid->rows[row_no]->initialized) {
|
||||||
|
all_initialized = false;
|
||||||
|
new_view--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!all_initialized);
|
||||||
|
|
||||||
|
/* Don't scroll past scrollback history */
|
||||||
|
int end = (term->grid->offset + term->rows - 1) % term->grid->num_rows;
|
||||||
|
if (end >= term->grid->offset) {
|
||||||
|
/* Not wrapped */
|
||||||
|
if (new_view >= term->grid->offset && new_view <= end)
|
||||||
|
new_view = term->grid->offset;
|
||||||
|
} else {
|
||||||
|
if (new_view >= term->grid->offset || new_view <= end)
|
||||||
|
new_view = term->grid->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LOG_DBG("scrollback DOWN: %d -> %d (offset = %d, end = %d, rows = %d)",
|
||||||
|
term->grid->view, new_view, term->grid->offset, end, term->grid->num_rows);
|
||||||
|
|
||||||
|
if (new_view == term->grid->view)
|
||||||
|
return;
|
||||||
|
|
||||||
|
term->grid->view = new_view;
|
||||||
|
|
||||||
|
term_damage_view(term);
|
||||||
|
if (term->frame_callback == NULL)
|
||||||
|
grid_render(term);
|
||||||
|
}
|
||||||
6
commands.h
Normal file
6
commands.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "terminal.h"
|
||||||
|
|
||||||
|
void cmd_scrollback_up(struct terminal *term, int rows);
|
||||||
|
void cmd_scrollback_down(struct terminal *term, int rows);
|
||||||
7
grid.h
7
grid.h
|
|
@ -10,6 +10,13 @@ grid_row(struct grid *grid, int row)
|
||||||
return grid->rows[(grid->offset + row + grid->num_rows) % grid->num_rows];
|
return grid->rows[(grid->offset + row + grid->num_rows) % grid->num_rows];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct row *
|
||||||
|
grid_row_in_view(struct grid *grid, int row)
|
||||||
|
{
|
||||||
|
assert(grid->view >= 0);
|
||||||
|
return grid->rows[(grid->view + row + grid->num_rows) % grid->num_rows];
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
grid_swap_row(struct grid *grid, int row_a, int row_b)
|
grid_swap_row(struct grid *grid, int row_a, int row_b)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
51
input.c
51
input.c
|
|
@ -7,6 +7,8 @@
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||||
#include <xkbcommon/xkbcommon-compose.h>
|
#include <xkbcommon/xkbcommon-compose.h>
|
||||||
|
|
@ -17,6 +19,7 @@
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "keymap.h"
|
#include "keymap.h"
|
||||||
|
#include "commands.h"
|
||||||
|
|
||||||
static void
|
static void
|
||||||
keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
|
@ -140,6 +143,18 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||||
keymap_mods |= term->kbd.alt ? MOD_ALT : MOD_NONE;
|
keymap_mods |= term->kbd.alt ? MOD_ALT : MOD_NONE;
|
||||||
keymap_mods |= term->kbd.ctrl ? MOD_CTRL : MOD_NONE;
|
keymap_mods |= term->kbd.ctrl ? MOD_CTRL : MOD_NONE;
|
||||||
|
|
||||||
|
if (effective_mods == shift) {
|
||||||
|
if (sym == XKB_KEY_Page_Up) {
|
||||||
|
cmd_scrollback_up(term, term->rows);
|
||||||
|
found_map = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (sym == XKB_KEY_Page_Down) {
|
||||||
|
cmd_scrollback_down(term, term->rows);
|
||||||
|
found_map = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(key_map) / sizeof(key_map[0]) && !found_map; i++) {
|
for (size_t i = 0; i < sizeof(key_map) / sizeof(key_map[0]) && !found_map; i++) {
|
||||||
const struct key_map *k = &key_map[i];
|
const struct key_map *k = &key_map[i];
|
||||||
if (k->sym != sym)
|
if (k->sym != sym)
|
||||||
|
|
@ -160,6 +175,13 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||||
|
|
||||||
write(term->ptmx, info->seq, strlen(info->seq));
|
write(term->ptmx, info->seq, strlen(info->seq));
|
||||||
found_map = true;
|
found_map = true;
|
||||||
|
|
||||||
|
if (term->grid->view != term->grid->offset) {
|
||||||
|
term->grid->view = term->grid->offset;
|
||||||
|
/* TODO: damage view */
|
||||||
|
term_damage_all(term);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -182,6 +204,13 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||||
write(term->ptmx, "\x1b", 1);
|
write(term->ptmx, "\x1b", 1);
|
||||||
|
|
||||||
write(term->ptmx, buf, count);
|
write(term->ptmx, buf, count);
|
||||||
|
|
||||||
|
if (term->grid->view != term->grid->offset) {
|
||||||
|
term->grid->view = term->grid->offset;
|
||||||
|
/* TODO: damage view */
|
||||||
|
term_damage_all(term);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,6 +344,28 @@ static void
|
||||||
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
||||||
uint32_t time, uint32_t axis, wl_fixed_t value)
|
uint32_t time, uint32_t axis, wl_fixed_t value)
|
||||||
{
|
{
|
||||||
|
struct terminal *term = data;
|
||||||
|
|
||||||
|
/* TODO: generate button event for BTN_FORWARD/BTN_BACK? */
|
||||||
|
|
||||||
|
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int amount = wl_fixed_to_int(value);
|
||||||
|
|
||||||
|
if (amount < 0) {
|
||||||
|
for (int i = 0; i < -amount; i++) {
|
||||||
|
term_mouse_down(term, BTN_BACK, term->mouse.row, term->mouse.col,
|
||||||
|
term->kbd.shift, term->kbd.alt, term->kbd.ctrl);
|
||||||
|
}
|
||||||
|
cmd_scrollback_up(term, -amount);
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
term_mouse_down(term, BTN_FORWARD, term->mouse.row, term->mouse.col,
|
||||||
|
term->kbd.shift, term->kbd.alt, term->kbd.ctrl);
|
||||||
|
}
|
||||||
|
cmd_scrollback_down(term, amount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
||||||
2
main.c
2
main.c
|
|
@ -495,7 +495,7 @@ main(int argc, char *const *argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0 || !(timeout_ms != -1 && fds[1].revents & POLLIN)) {
|
if (ret == 0 || (timeout_ms != -1 && !(fds[1].revents & POLLIN))) {
|
||||||
/* Delayed rendering */
|
/* Delayed rendering */
|
||||||
if (term.frame_callback == NULL)
|
if (term.frame_callback == NULL)
|
||||||
grid_render(&term);
|
grid_render(&term);
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ endforeach
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
'f00ter',
|
'f00ter',
|
||||||
|
'commands.c', 'commands.h',
|
||||||
'csi.c', 'csi.h',
|
'csi.c', 'csi.h',
|
||||||
'font.c', 'font.h',
|
'font.c', 'font.h',
|
||||||
'grid.c', 'grid.h',
|
'grid.c', 'grid.h',
|
||||||
|
|
|
||||||
72
render.c
72
render.c
|
|
@ -35,13 +35,8 @@ static struct glyph_sequence gseq;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
|
render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
|
||||||
int col, int row)
|
int col, int row, bool has_cursor)
|
||||||
{
|
{
|
||||||
/* Cursor here? */
|
|
||||||
bool has_cursor
|
|
||||||
= (!term->hide_cursor &&
|
|
||||||
(term->cursor.col == col && term->cursor.row == row));
|
|
||||||
|
|
||||||
double width = term->cell_width;
|
double width = term->cell_width;
|
||||||
double height = term->cell_height;
|
double height = term->cell_height;
|
||||||
double x = col * width;
|
double x = col * width;
|
||||||
|
|
@ -243,7 +238,7 @@ grid_render(struct terminal *term)
|
||||||
gseq.count = 0;
|
gseq.count = 0;
|
||||||
|
|
||||||
for (int r = 0; r < term->rows; r++) {
|
for (int r = 0; r < term->rows; r++) {
|
||||||
struct row *row = grid_row(term->grid, r);
|
struct row *row = grid_row_in_view(term->grid, r);
|
||||||
|
|
||||||
if (!row->dirty)
|
if (!row->dirty)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -251,7 +246,7 @@ grid_render(struct terminal *term)
|
||||||
//LOG_WARN("rendering line: %d", r);
|
//LOG_WARN("rendering line: %d", r);
|
||||||
|
|
||||||
for (int col = 0; col < term->cols; col++)
|
for (int col = 0; col < term->cols; col++)
|
||||||
render_cell(term, buf, &row->cells[col], col, r);
|
render_cell(term, buf, &row->cells[col], col, r, false);
|
||||||
|
|
||||||
row->dirty = false;
|
row->dirty = false;
|
||||||
all_clean = false;
|
all_clean = false;
|
||||||
|
|
@ -269,7 +264,7 @@ grid_render(struct terminal *term)
|
||||||
int row = last_cursor / term->cols - term->grid->offset;
|
int row = last_cursor / term->cols - term->grid->offset;
|
||||||
int col = last_cursor % term->cols;
|
int col = last_cursor % term->cols;
|
||||||
if (row >= 0 && row < term->rows) {
|
if (row >= 0 && row < term->rows) {
|
||||||
render_cell(term, buf, &grid_row(term->grid, row)->cells[col], col, row);
|
render_cell(term, buf, &grid_row_in_view(term->grid, row)->cells[col], col, row, false);
|
||||||
all_clean = false;
|
all_clean = false;
|
||||||
|
|
||||||
wl_surface_damage_buffer(
|
wl_surface_damage_buffer(
|
||||||
|
|
@ -284,16 +279,31 @@ grid_render(struct terminal *term)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
render_cell(
|
bool cursor_is_visible = false;
|
||||||
term, buf,
|
int view_end = (term->grid->view + term->rows - 1) % term->grid->num_rows;
|
||||||
&grid_row(term->grid, term->cursor.row)->cells[term->cursor.col],
|
int cursor_row = (term->grid->offset + term->cursor.row) % term->grid->num_rows;
|
||||||
term->cursor.col, term->cursor.row);
|
if (view_end >= term->grid->view) {
|
||||||
|
/* Not wrapped */
|
||||||
|
if (cursor_row >= term->grid->view && cursor_row <= view_end)
|
||||||
|
cursor_is_visible = true;
|
||||||
|
} else {
|
||||||
|
/* Wrapped */
|
||||||
|
if (cursor_row >= term->grid->view || cursor_row <= view_end)
|
||||||
|
cursor_is_visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
wl_surface_damage_buffer(
|
if (cursor_is_visible) {
|
||||||
term->wl.surface,
|
render_cell(
|
||||||
term->cursor.col * term->cell_width,
|
term, buf,
|
||||||
term->cursor.row * term->cell_height,
|
&grid_row_in_view(term->grid, term->cursor.row)->cells[term->cursor.col],
|
||||||
term->cell_width, term->cell_height);
|
term->cursor.col, term->cursor.row, true);
|
||||||
|
|
||||||
|
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) {
|
if (gseq.count > 0) {
|
||||||
cairo_set_scaled_font(buf->cairo, attrs_to_font(term, &gseq.attrs));
|
cairo_set_scaled_font(buf->cairo, attrs_to_font(term, &gseq.attrs));
|
||||||
|
|
@ -304,6 +314,7 @@ grid_render(struct terminal *term)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(term->grid->offset >= 0 && term->grid->offset < term->grid->num_rows);
|
assert(term->grid->offset >= 0 && term->grid->offset < term->grid->num_rows);
|
||||||
|
assert(term->grid->view >= 0 && term->grid->view < 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);
|
wl_surface_attach(term->wl.surface, buf->wl_buf, 0, 0);
|
||||||
|
|
@ -338,12 +349,18 @@ reflow(struct row **new_grid, int new_cols, int new_rows,
|
||||||
struct cell *new_cells = new_grid[r]->cells;
|
struct cell *new_cells = new_grid[r]->cells;
|
||||||
const struct cell *old_cells = old_grid[r]->cells;
|
const struct cell *old_cells = old_grid[r]->cells;
|
||||||
|
|
||||||
|
new_grid[r]->initialized = old_grid[r]->initialized;
|
||||||
|
new_grid[r]->dirty = old_grid[r]->dirty;
|
||||||
|
|
||||||
memcpy(new_cells, old_cells, copy_cols * sizeof(new_cells[0]));
|
memcpy(new_cells, old_cells, copy_cols * sizeof(new_cells[0]));
|
||||||
memset(&new_cells[copy_cols], 0, clear_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++)
|
for (int r = min(new_rows, old_rows); r < new_rows; r++) {
|
||||||
|
new_grid[r]->initialized = false;
|
||||||
|
new_grid[r]->dirty = false;
|
||||||
memset(new_grid[r]->cells, 0, new_cols * sizeof(new_grid[r]->cells[0]));
|
memset(new_grid[r]->cells, 0, new_cols * sizeof(new_grid[r]->cells[0]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move to terminal.c? */
|
/* Move to terminal.c? */
|
||||||
|
|
@ -356,6 +373,8 @@ render_resize(struct terminal *term, int width, int height)
|
||||||
term->width = width;
|
term->width = width;
|
||||||
term->height = height;
|
term->height = height;
|
||||||
|
|
||||||
|
const int scrollback_lines = 10000;
|
||||||
|
|
||||||
const int old_cols = term->cols;
|
const int old_cols = term->cols;
|
||||||
const int old_rows = term->rows;
|
const int old_rows = term->rows;
|
||||||
const int old_normal_grid_rows = term->normal.num_rows;
|
const int old_normal_grid_rows = term->normal.num_rows;
|
||||||
|
|
@ -363,7 +382,7 @@ render_resize(struct terminal *term, int width, int height)
|
||||||
|
|
||||||
const int new_cols = term->width / term->cell_width;
|
const int new_cols = term->width / term->cell_width;
|
||||||
const int new_rows = term->height / term->cell_height;
|
const int new_rows = term->height / term->cell_height;
|
||||||
const int new_normal_grid_rows = new_rows;
|
const int new_normal_grid_rows = new_rows + scrollback_lines;
|
||||||
const int new_alt_grid_rows = new_rows;
|
const int new_alt_grid_rows = new_rows;
|
||||||
|
|
||||||
/* Allocate new 'normal' grid */
|
/* Allocate new 'normal' grid */
|
||||||
|
|
@ -388,6 +407,11 @@ render_resize(struct terminal *term, int width, int height)
|
||||||
reflow(alt, new_cols, new_alt_grid_rows,
|
reflow(alt, new_cols, new_alt_grid_rows,
|
||||||
term->alt.rows, old_cols, old_alt_grid_rows);
|
term->alt.rows, old_cols, old_alt_grid_rows);
|
||||||
|
|
||||||
|
for (int r = 0; r < new_rows; r++) {
|
||||||
|
normal[r]->initialized = true;
|
||||||
|
alt[r]->initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Free old 'normal' grid */
|
/* Free old 'normal' grid */
|
||||||
for (int r = 0; r < term->normal.num_rows; r++) {
|
for (int r = 0; r < term->normal.num_rows; r++) {
|
||||||
free(term->normal.rows[r]->cells);
|
free(term->normal.rows[r]->cells);
|
||||||
|
|
@ -430,11 +454,19 @@ render_resize(struct terminal *term, int width, int height)
|
||||||
if (term->scroll_region.end >= old_rows)
|
if (term->scroll_region.end >= old_rows)
|
||||||
term->scroll_region.end = term->rows;
|
term->scroll_region.end = term->rows;
|
||||||
|
|
||||||
|
term->normal.offset %= term->normal.num_rows;
|
||||||
|
term->normal.view %= term->normal.num_rows;
|
||||||
|
|
||||||
|
term->alt.offset %= term->alt.num_rows;
|
||||||
|
term->alt.view %= term->alt.num_rows;
|
||||||
|
|
||||||
term_cursor_to(
|
term_cursor_to(
|
||||||
term,
|
term,
|
||||||
min(term->cursor.row, term->rows - 1),
|
min(term->cursor.row, term->rows - 1),
|
||||||
min(term->cursor.col, term->cols - 1));
|
min(term->cursor.col, term->cols - 1));
|
||||||
|
|
||||||
term_damage_all(term);
|
term_damage_all(term);
|
||||||
|
term_damage_view(term);
|
||||||
|
|
||||||
if (term->frame_callback == NULL)
|
if (term->frame_callback == NULL)
|
||||||
grid_render(term);
|
grid_render(term);
|
||||||
|
|
|
||||||
23
terminal.c
23
terminal.c
|
|
@ -21,6 +21,13 @@ term_damage_all(struct terminal *term)
|
||||||
grid_row(term->grid, i)->dirty = true;
|
grid_row(term->grid, i)->dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
term_damage_view(struct terminal *term)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < term->rows; i++)
|
||||||
|
grid_row_in_view(term->grid, i)->dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
term_damage_scroll(struct terminal *term, enum damage_type damage_type,
|
term_damage_scroll(struct terminal *term, enum damage_type damage_type,
|
||||||
struct scroll_region region, int lines)
|
struct scroll_region region, int lines)
|
||||||
|
|
@ -152,11 +159,18 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
|
||||||
grid_swap_row(term->grid, i, i + rows);
|
grid_swap_row(term->grid, i, i + rows);
|
||||||
|
|
||||||
/* Offset grid origin */
|
/* Offset grid origin */
|
||||||
|
bool view_follows = term->grid->view == term->grid->offset;
|
||||||
term->grid->offset += rows;
|
term->grid->offset += rows;
|
||||||
term->grid->offset %= term->grid->num_rows;
|
term->grid->offset %= term->grid->num_rows;
|
||||||
|
|
||||||
for (int r = max(region.end - rows, 0); r < region.end; r++)
|
if (view_follows)
|
||||||
erase_line(term, grid_row(term->grid, r));
|
term->grid->view = term->grid->offset;
|
||||||
|
|
||||||
|
for (int r = max(region.end - rows, 0); r < region.end; r++) {
|
||||||
|
struct row *row = grid_row(term->grid, r);
|
||||||
|
erase_line(term, row);
|
||||||
|
row->initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
|
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
|
||||||
term->grid->cur_row = grid_row(term->grid, term->cursor.row);
|
term->grid->cur_row = grid_row(term->grid, term->cursor.row);
|
||||||
|
|
@ -174,9 +188,14 @@ term_scroll_reverse_partial(struct terminal *term,
|
||||||
{
|
{
|
||||||
assert(rows < term->rows && "unimplemented");
|
assert(rows < term->rows && "unimplemented");
|
||||||
|
|
||||||
|
bool view_follows = term->grid->view == term->grid->offset;
|
||||||
|
|
||||||
term->grid->offset += term->grid->num_rows - rows;
|
term->grid->offset += term->grid->num_rows - rows;
|
||||||
term->grid->offset %= term->grid->num_rows;
|
term->grid->offset %= term->grid->num_rows;
|
||||||
|
|
||||||
|
if (view_follows)
|
||||||
|
term->grid->view = term->grid->offset;
|
||||||
|
|
||||||
/* Bottom non-scrolling region */
|
/* Bottom non-scrolling region */
|
||||||
for (int i = region.end + rows; i < term->rows + rows; i++)
|
for (int i = region.end + rows; i < term->rows + rows; i++)
|
||||||
grid_swap_row(term->grid, i, i - rows);
|
grid_swap_row(term->grid, i, i - rows);
|
||||||
|
|
|
||||||
|
|
@ -94,11 +94,13 @@ struct damage {
|
||||||
struct row {
|
struct row {
|
||||||
struct cell *cells;
|
struct cell *cells;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
|
bool initialized;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct grid {
|
struct grid {
|
||||||
int num_rows;
|
int num_rows;
|
||||||
int offset;
|
int offset;
|
||||||
|
int view;
|
||||||
|
|
||||||
struct row **rows;
|
struct row **rows;
|
||||||
struct row *cur_row;
|
struct row *cur_row;
|
||||||
|
|
@ -248,6 +250,7 @@ struct terminal {
|
||||||
};
|
};
|
||||||
|
|
||||||
void term_damage_all(struct terminal *term);
|
void term_damage_all(struct terminal *term);
|
||||||
|
void term_damage_view(struct terminal *term);
|
||||||
void term_damage_scroll(
|
void term_damage_scroll(
|
||||||
struct terminal *term, enum damage_type damage_type,
|
struct terminal *term, enum damage_type damage_type,
|
||||||
struct scroll_region region, int lines);
|
struct scroll_region region, int lines);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue