Merge branch 'scrollback'

This commit is contained in:
Daniel Eklöf 2019-07-10 14:57:26 +02:00
commit 0bc3b29b0f
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
9 changed files with 263 additions and 23 deletions

121
commands.c Normal file
View 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
View 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
View file

@ -10,6 +10,13 @@ grid_row(struct grid *grid, int row)
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
grid_swap_row(struct grid *grid, int row_a, int row_b)
{

51
input.c
View file

@ -7,6 +7,8 @@
#include <locale.h>
#include <sys/mman.h>
#include <linux/input-event-codes.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-keysyms.h>
#include <xkbcommon/xkbcommon-compose.h>
@ -17,6 +19,7 @@
#include "terminal.h"
#include "render.h"
#include "keymap.h"
#include "commands.h"
static void
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.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++) {
const struct key_map *k = &key_map[i];
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));
found_map = true;
if (term->grid->view != term->grid->offset) {
term->grid->view = term->grid->offset;
/* TODO: damage view */
term_damage_all(term);
}
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, 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,
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

2
main.c
View file

@ -495,7 +495,7 @@ main(int argc, char *const *argv)
break;
}
if (ret == 0 || !(timeout_ms != -1 && fds[1].revents & POLLIN)) {
if (ret == 0 || (timeout_ms != -1 && !(fds[1].revents & POLLIN))) {
/* Delayed rendering */
if (term.frame_callback == NULL)
grid_render(&term);

View file

@ -58,6 +58,7 @@ endforeach
executable(
'f00ter',
'commands.c', 'commands.h',
'csi.c', 'csi.h',
'font.c', 'font.h',
'grid.c', 'grid.h',

View file

@ -35,13 +35,8 @@ static struct glyph_sequence gseq;
static void
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 height = term->cell_height;
double x = col * width;
@ -243,7 +238,7 @@ grid_render(struct terminal *term)
gseq.count = 0;
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)
continue;
@ -251,7 +246,7 @@ grid_render(struct terminal *term)
//LOG_WARN("rendering line: %d", r);
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;
all_clean = false;
@ -269,7 +264,7 @@ grid_render(struct terminal *term)
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);
render_cell(term, buf, &grid_row_in_view(term->grid, row)->cells[col], col, row, false);
all_clean = false;
wl_surface_damage_buffer(
@ -284,16 +279,31 @@ grid_render(struct terminal *term)
return;
}
render_cell(
term, buf,
&grid_row(term->grid, term->cursor.row)->cells[term->cursor.col],
term->cursor.col, term->cursor.row);
bool cursor_is_visible = false;
int view_end = (term->grid->view + term->rows - 1) % term->grid->num_rows;
int cursor_row = (term->grid->offset + term->cursor.row) % term->grid->num_rows;
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(
term->wl.surface,
term->cursor.col * term->cell_width,
term->cursor.row * term->cell_height,
term->cell_width, term->cell_height);
if (cursor_is_visible) {
render_cell(
term, buf,
&grid_row_in_view(term->grid, term->cursor.row)->cells[term->cursor.col],
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) {
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->view >= 0 && term->grid->view < term->grid->num_rows);
cairo_surface_flush(buf->cairo_surface);
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;
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]));
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]));
}
}
/* Move to terminal.c? */
@ -356,6 +373,8 @@ render_resize(struct terminal *term, int width, int height)
term->width = width;
term->height = height;
const int scrollback_lines = 10000;
const int old_cols = term->cols;
const int old_rows = term->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_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;
/* 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,
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 */
for (int r = 0; r < term->normal.num_rows; r++) {
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)
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,
min(term->cursor.row, term->rows - 1),
min(term->cursor.col, term->cols - 1));
term_damage_all(term);
term_damage_view(term);
if (term->frame_callback == NULL)
grid_render(term);

View file

@ -21,6 +21,13 @@ term_damage_all(struct terminal *term)
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
term_damage_scroll(struct terminal *term, enum damage_type damage_type,
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);
/* Offset grid origin */
bool view_follows = term->grid->view == term->grid->offset;
term->grid->offset += rows;
term->grid->offset %= term->grid->num_rows;
for (int r = max(region.end - rows, 0); r < region.end; r++)
erase_line(term, grid_row(term->grid, r));
if (view_follows)
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->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");
bool view_follows = term->grid->view == term->grid->offset;
term->grid->offset += term->grid->num_rows - rows;
term->grid->offset %= term->grid->num_rows;
if (view_follows)
term->grid->view = term->grid->offset;
/* Bottom non-scrolling region */
for (int i = region.end + rows; i < term->rows + rows; i++)
grid_swap_row(term->grid, i, i - rows);

View file

@ -94,11 +94,13 @@ struct damage {
struct row {
struct cell *cells;
bool dirty;
bool initialized;
};
struct grid {
int num_rows;
int offset;
int view;
struct row **rows;
struct row *cur_row;
@ -248,6 +250,7 @@ struct terminal {
};
void term_damage_all(struct terminal *term);
void term_damage_view(struct terminal *term);
void term_damage_scroll(
struct terminal *term, enum damage_type damage_type,
struct scroll_region region, int lines);