foot/commands.c
Daniel Eklöf 7c7720a3ab
scrolling: optimize row access by assuming number of rows is a power of 2
With this assumption, we can replace 'a % b' with 'a & (b - 1)'. In
terms of instructions, this means a fast 'and' instead of a slow
'div'.

Further optimize scrolling by:

* not double-initializing empty rows. Previously, grid_row_alloc()
  called calloc(), which was then followed by a memset() when
  scrolling. This is of course unnecessary.

* Don't loop the entire set of visible rows (this was done to ensure
  all visible rows had been allocated, and to prefetch the cell
  contents).

  This isn't necessary; only newly pulled in rows can be NULL. For
  now, don't prefetch at all.
2019-08-22 17:33:23 +02:00

151 lines
4.4 KiB
C

#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->rows);
assert(term->grid->offset >= 0);
int new_view = term->grid->view - rows;
while (new_view < 0)
new_view += term->grid->num_rows;
new_view %= 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] == NULL)
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;
}
#if defined(_DEBUG)
for (int r = 0; r < term->rows; r++)
assert(term->grid->rows[(new_view + r) % term->grid->num_rows] != NULL);
#endif
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;
int diff = -1;
if (new_view < term->grid->view)
diff = term->grid->view - new_view;
else
diff = (term->grid->num_rows - new_view) + term->grid->view;
term->grid->view = new_view;
if (diff >= 0 && diff < term->rows) {
term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, (struct scroll_region){0, term->rows}, diff);
term_damage_rows_in_view(term, 0, diff - 1);
} else
term_damage_view(term);
render_refresh(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->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] == NULL) {
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;
}
#if defined(_DEBUG)
for (int r = 0; r < term->rows; r++)
assert(term->grid->rows[(new_view + r) % term->grid->num_rows] != NULL);
#endif
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;
int diff = -1;
if (new_view > term->grid->view)
diff = new_view - term->grid->view;
else
diff = (term->grid->num_rows - term->grid->view) + new_view;
term->grid->view = new_view;
if (diff >= 0 && diff < term->rows) {
term_damage_scroll(term, DAMAGE_SCROLL, (struct scroll_region){0, term->rows}, diff);
term_damage_rows_in_view(term, term->rows - diff, term->rows - 1);
} else
term_damage_view(term);
render_refresh(term);
}