diff --git a/commands.c b/commands.c new file mode 100644 index 00000000..dea9b36e --- /dev/null +++ b/commands.c @@ -0,0 +1,61 @@ +#include "commands.h" + +#define LOG_MODULE "commands" +#define LOG_ENABLE_DBG 1 +#include "log.h" +#include "terminal.h" +#include "render.h" +#include "grid.h" + +#define max(x, y) ((x) > (y) ? (x) : (y)) + +void +cmd_scrollback_up(struct terminal *term) +{ + assert(term->grid->offset >= 0); + int new_view = (term->grid->view + term->grid->num_rows - term->rows) % term->grid->num_rows; + + LOG_WARN("%d" ,new_view); + assert(new_view >= 0); + assert(new_view < term->grid->num_rows); + + while (!term->grid->rows[new_view]->initialized) + new_view = (new_view + 1) % term->grid->num_rows; + + LOG_DBG("scrollback UP: %d -> %d (offset = %d, rows = %d)", + term->grid->view, new_view, term->grid->offset, term->grid->num_rows); + term->grid->view = new_view; + + for (int i = 0; i < term->rows; i++) + grid_row_in_view(term->grid, i)->dirty = true; + + if (term->frame_callback == NULL) + grid_render(term); +} + +void +cmd_scrollback_down(struct terminal *term) +{ + assert(term->grid->offset >= 0); + int new_view = (term->grid->view + term->rows) % term->grid->num_rows; + + LOG_WARN("%d" ,new_view); + assert(new_view >= 0); + assert(new_view < term->grid->num_rows); + + if (!term->grid->rows[new_view]->initialized) { + while (!term->grid->rows[new_view]->initialized) + new_view = (new_view + term->grid->num_rows - 1) % term->grid->num_rows; + new_view = (new_view + term->grid->num_rows - term->rows + 1) % term->grid->num_rows; + } + + LOG_DBG("scrollback DOWN: %d -> %d (offset = %d, rows = %d)", + term->grid->view, new_view, term->grid->offset, term->grid->num_rows); + term->grid->view = new_view; + + for (int i = 0; i < term->rows; i++) + grid_row_in_view(term->grid, i)->dirty = true; + + if (term->frame_callback == NULL) + grid_render(term); +} diff --git a/commands.h b/commands.h new file mode 100644 index 00000000..865cd83f --- /dev/null +++ b/commands.h @@ -0,0 +1,6 @@ +#pragma once + +#include "terminal.h" + +void cmd_scrollback_up(struct terminal *term); +void cmd_scrollback_down(struct terminal *term); diff --git a/grid.h b/grid.h index d6ddabee..e170f6dd 100644 --- a/grid.h +++ b/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]; } +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) { diff --git a/input.c b/input.c index 4ac45bbe..a7aba971 100644 --- a/input.c +++ b/input.c @@ -17,6 +17,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 +141,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); + found_map = true; + } + + else if (sym == XKB_KEY_Page_Down) { + cmd_scrollback_down(term); + 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 +173,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 +202,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); + } + } } diff --git a/meson.build b/meson.build index c55938b1..7cb057bf 100644 --- a/meson.build +++ b/meson.build @@ -58,6 +58,7 @@ endforeach executable( 'f00ter', + 'commands.c', 'commands.h', 'csi.c', 'csi.h', 'font.c', 'font.h', 'grid.c', 'grid.h', diff --git a/render.c b/render.c index d42a04ec..685a9031 100644 --- a/render.c +++ b/render.c @@ -243,7 +243,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; @@ -266,10 +266,10 @@ grid_render(struct terminal *term) = (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 row = last_cursor / term->cols - term->grid->view; 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); all_clean = false; wl_surface_damage_buffer( @@ -286,7 +286,7 @@ grid_render(struct terminal *term) render_cell( term, buf, - &grid_row(term->grid, term->cursor.row)->cells[term->cursor.col], + &grid_row_in_view(term->grid, term->cursor.row)->cells[term->cursor.col], term->cursor.col, term->cursor.row); wl_surface_damage_buffer( @@ -304,6 +304,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 +339,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 +363,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 +372,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 +397,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,10 +444,18 @@ 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->alt.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)); + + /* TODO: damage view */ term_damage_all(term); if (term->frame_callback == NULL) diff --git a/terminal.c b/terminal.c index 1cf15cfb..56ac6cee 100644 --- a/terminal.c +++ b/terminal.c @@ -152,11 +152,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 +181,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); diff --git a/terminal.h b/terminal.h index 6f42398a..35a1fae7 100644 --- a/terminal.h +++ b/terminal.h @@ -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;