mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-24 09:05:48 -04:00
Merge branch 'scrollback-home-and-end'
This commit is contained in:
commit
bd5576825f
6 changed files with 88 additions and 92 deletions
|
|
@ -43,6 +43,8 @@
|
||||||
|
|
||||||
* OSC-22 - set xcursor pointer.
|
* OSC-22 - set xcursor pointer.
|
||||||
* Add "xterm" as fallback cursor where "text" is not available.
|
* Add "xterm" as fallback cursor where "text" is not available.
|
||||||
|
* `[key-bindings].scrollback-home|end` options.
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
||||||
154
commands.c
154
commands.c
|
|
@ -18,68 +18,58 @@ cmd_scrollback_up(struct terminal *term, int rows)
|
||||||
if (urls_mode_is_active(term))
|
if (urls_mode_is_active(term))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rows = min(rows, term->rows);
|
const struct grid *grid = term->grid;
|
||||||
xassert(term->grid->offset >= 0);
|
const int offset = grid->offset;
|
||||||
|
const int view = grid->view;
|
||||||
|
const int grid_rows = grid->num_rows;
|
||||||
|
const int screen_rows = term->rows;
|
||||||
|
|
||||||
int new_view = term->grid->view - rows;
|
int scrollback_start = (offset + screen_rows) & (grid_rows - 1);
|
||||||
while (new_view < 0)
|
|
||||||
new_view += term->grid->num_rows;
|
|
||||||
new_view %= term->grid->num_rows;
|
|
||||||
|
|
||||||
xassert(new_view >= 0);
|
/* Part of the scrollback may be uninitialized */
|
||||||
xassert(new_view < term->grid->num_rows);
|
while (grid->rows[scrollback_start] == NULL) {
|
||||||
|
scrollback_start++;
|
||||||
/* Avoid scrolling in uninitialized rows */
|
scrollback_start &= grid_rows - 1;
|
||||||
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
|
|
||||||
* uninitialized, and the loop above will bring us back to
|
|
||||||
* where we started.
|
|
||||||
*/
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't scroll past scrollback history */
|
/* Number of rows to scroll, without going past the scrollback start */
|
||||||
int end = (term->grid->offset + term->rows - 1) % term->grid->num_rows;
|
int max_rows = 0;
|
||||||
if (end >= term->grid->offset) {
|
if (view + screen_rows >= grid_rows) {
|
||||||
/* Not wrapped */
|
/* View crosses scrollback wrap-around */
|
||||||
if (new_view >= term->grid->offset && new_view <= end)
|
xassert(scrollback_start <= view);
|
||||||
new_view = (end + 1) % term->grid->num_rows;
|
max_rows = view - scrollback_start;
|
||||||
} else {
|
} else {
|
||||||
if (new_view >= term->grid->offset || new_view <= end)
|
if (scrollback_start <= view)
|
||||||
new_view = (end + 1) % term->grid->num_rows;
|
max_rows = view - scrollback_start;
|
||||||
|
else
|
||||||
|
max_rows = view + (grid_rows - scrollback_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (term->grid->rows[new_view] == NULL)
|
rows = min(rows, max_rows);
|
||||||
new_view = (new_view + 1) % term->grid->num_rows;
|
if (rows == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int new_view = (view + grid_rows) - rows;
|
||||||
|
new_view &= grid_rows - 1;
|
||||||
|
|
||||||
|
xassert(new_view != view);
|
||||||
|
xassert(grid->rows[new_view] != NULL);
|
||||||
#if defined(_DEBUG)
|
#if defined(_DEBUG)
|
||||||
for (int r = 0; r < term->rows; r++)
|
for (int r = 0; r < term->rows; r++)
|
||||||
xassert(term->grid->rows[(new_view + r) % term->grid->num_rows] != NULL);
|
xassert(grid->rows[(new_view + r) & (grid->num_rows - 1)] != NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LOG_DBG("scrollback UP: %d -> %d (offset = %d, end = %d, rows = %d)",
|
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);
|
view, new_view, offset, end, grid_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;
|
|
||||||
|
|
||||||
selection_view_up(term, new_view);
|
selection_view_up(term, new_view);
|
||||||
term->grid->view = new_view;
|
term->grid->view = new_view;
|
||||||
|
|
||||||
if (diff >= 0 && diff < term->rows) {
|
if (rows < term->rows) {
|
||||||
term_damage_scroll(term, DAMAGE_SCROLL_REVERSE_IN_VIEW, (struct scroll_region){0, term->rows}, diff);
|
term_damage_scroll(
|
||||||
term_damage_rows_in_view(term, 0, diff - 1);
|
term, DAMAGE_SCROLL_REVERSE_IN_VIEW,
|
||||||
|
(struct scroll_region){0, term->rows}, rows);
|
||||||
|
term_damage_rows_in_view(term, 0, rows - 1);
|
||||||
} else
|
} else
|
||||||
term_damage_view(term);
|
term_damage_view(term);
|
||||||
|
|
||||||
|
|
@ -95,65 +85,45 @@ cmd_scrollback_down(struct terminal *term, int rows)
|
||||||
if (urls_mode_is_active(term))
|
if (urls_mode_is_active(term))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (term->grid->view == term->grid->offset)
|
const struct grid *grid = term->grid;
|
||||||
|
const int offset = grid->offset;
|
||||||
|
const int view = grid->view;
|
||||||
|
const int grid_rows = grid->num_rows;
|
||||||
|
const int screen_rows = term->rows;
|
||||||
|
|
||||||
|
const int scrollback_end = offset;
|
||||||
|
|
||||||
|
/* Number of rows to scroll, without going past the scrollback end */
|
||||||
|
int max_rows = 0;
|
||||||
|
if (view <= scrollback_end)
|
||||||
|
max_rows = scrollback_end - view;
|
||||||
|
else
|
||||||
|
max_rows = offset + (grid_rows - view);
|
||||||
|
|
||||||
|
rows = min(rows, max_rows);
|
||||||
|
if (rows == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rows = min(rows, term->rows);
|
int new_view = (view + rows) & (grid_rows - 1);
|
||||||
xassert(term->grid->offset >= 0);
|
|
||||||
|
|
||||||
int new_view = (term->grid->view + rows) % term->grid->num_rows;
|
|
||||||
xassert(new_view >= 0);
|
|
||||||
xassert(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
xassert(new_view != view);
|
||||||
|
xassert(grid->rows[new_view] != NULL);
|
||||||
#if defined(_DEBUG)
|
#if defined(_DEBUG)
|
||||||
for (int r = 0; r < term->rows; r++)
|
for (int r = 0; r < term->rows; r++)
|
||||||
xassert(term->grid->rows[(new_view + r) % term->grid->num_rows] != NULL);
|
xassert(grid->rows[(new_view + r) & (grid_rows - 1)] != NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LOG_DBG("scrollback DOWN: %d -> %d (offset = %d, end = %d, rows = %d)",
|
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);
|
view, new_view, offset, end, grid_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;
|
|
||||||
|
|
||||||
selection_view_down(term, new_view);
|
selection_view_down(term, new_view);
|
||||||
term->grid->view = new_view;
|
term->grid->view = new_view;
|
||||||
|
|
||||||
if (diff >= 0 && diff < term->rows) {
|
if (rows < term->rows) {
|
||||||
term_damage_scroll(term, DAMAGE_SCROLL_IN_VIEW, (struct scroll_region){0, term->rows}, diff);
|
term_damage_scroll(
|
||||||
term_damage_rows_in_view(term, term->rows - diff, term->rows - 1);
|
term, DAMAGE_SCROLL_IN_VIEW,
|
||||||
|
(struct scroll_region){0, term->rows}, rows);
|
||||||
|
term_damage_rows_in_view(term, term->rows - rows, screen_rows - 1);
|
||||||
} else
|
} else
|
||||||
term_damage_view(term);
|
term_damage_view(term);
|
||||||
|
|
||||||
|
|
|
||||||
2
config.c
2
config.c
|
|
@ -95,6 +95,8 @@ static const char *const binding_action_map[] = {
|
||||||
[BIND_ACTION_SCROLLBACK_DOWN_PAGE] = "scrollback-down-page",
|
[BIND_ACTION_SCROLLBACK_DOWN_PAGE] = "scrollback-down-page",
|
||||||
[BIND_ACTION_SCROLLBACK_DOWN_HALF_PAGE] = "scrollback-down-half-page",
|
[BIND_ACTION_SCROLLBACK_DOWN_HALF_PAGE] = "scrollback-down-half-page",
|
||||||
[BIND_ACTION_SCROLLBACK_DOWN_LINE] = "scrollback-down-line",
|
[BIND_ACTION_SCROLLBACK_DOWN_LINE] = "scrollback-down-line",
|
||||||
|
[BIND_ACTION_SCROLLBACK_HOME] = "scrollback-home",
|
||||||
|
[BIND_ACTION_SCROLLBACK_END] = "scrollback-end",
|
||||||
[BIND_ACTION_CLIPBOARD_COPY] = "clipboard-copy",
|
[BIND_ACTION_CLIPBOARD_COPY] = "clipboard-copy",
|
||||||
[BIND_ACTION_CLIPBOARD_PASTE] = "clipboard-paste",
|
[BIND_ACTION_CLIPBOARD_PASTE] = "clipboard-paste",
|
||||||
[BIND_ACTION_PRIMARY_PASTE] = "primary-paste",
|
[BIND_ACTION_PRIMARY_PASTE] = "primary-paste",
|
||||||
|
|
|
||||||
|
|
@ -681,6 +681,12 @@ e.g. *search-start=none*.
|
||||||
*scrollback-down-line*
|
*scrollback-down-line*
|
||||||
Scroll down/forward a single line in history. Default: _not bound_.
|
Scroll down/forward a single line in history. Default: _not bound_.
|
||||||
|
|
||||||
|
*scrollback-home*
|
||||||
|
Scroll to the beginning of the scrollback. Default: _not bound_.
|
||||||
|
|
||||||
|
*scrollback-end*
|
||||||
|
Scroll to the end (bottom) of the scrollback. Default: _not bound_.
|
||||||
|
|
||||||
*clipboard-copy*
|
*clipboard-copy*
|
||||||
Copies the current selection into the _clipboard_. Default: _Control+Shift+c_
|
Copies the current selection into the _clipboard_. Default: _Control+Shift+c_
|
||||||
_XF86Copy_.
|
_XF86Copy_.
|
||||||
|
|
|
||||||
14
input.c
14
input.c
|
|
@ -134,6 +134,20 @@ execute_binding(struct seat *seat, struct terminal *term,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BIND_ACTION_SCROLLBACK_HOME:
|
||||||
|
if (term->grid == &term->normal) {
|
||||||
|
cmd_scrollback_up(term, term->grid->num_rows);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BIND_ACTION_SCROLLBACK_END:
|
||||||
|
if (term->grid == &term->normal) {
|
||||||
|
cmd_scrollback_down(term, term->grid->num_rows);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case BIND_ACTION_CLIPBOARD_COPY:
|
case BIND_ACTION_CLIPBOARD_COPY:
|
||||||
selection_to_clipboard(seat, term, serial);
|
selection_to_clipboard(seat, term, serial);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ enum bind_action_normal {
|
||||||
BIND_ACTION_SCROLLBACK_DOWN_PAGE,
|
BIND_ACTION_SCROLLBACK_DOWN_PAGE,
|
||||||
BIND_ACTION_SCROLLBACK_DOWN_HALF_PAGE,
|
BIND_ACTION_SCROLLBACK_DOWN_HALF_PAGE,
|
||||||
BIND_ACTION_SCROLLBACK_DOWN_LINE,
|
BIND_ACTION_SCROLLBACK_DOWN_LINE,
|
||||||
|
BIND_ACTION_SCROLLBACK_HOME,
|
||||||
|
BIND_ACTION_SCROLLBACK_END,
|
||||||
BIND_ACTION_CLIPBOARD_COPY,
|
BIND_ACTION_CLIPBOARD_COPY,
|
||||||
BIND_ACTION_CLIPBOARD_PASTE,
|
BIND_ACTION_CLIPBOARD_PASTE,
|
||||||
BIND_ACTION_PRIMARY_PASTE,
|
BIND_ACTION_PRIMARY_PASTE,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue