mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
This patch adds support for the OSC-133;A sequence, introduced by FinalTerm and implemented by iTerm2, Kitty and more. See https://iterm2.com/documentation-one-page.html#documentation-escape-codes.html. The shell emits the OSC just before printing the prompt. This lets the terminal know where, in the scrollback, there are prompts. We implement this using a simple boolean in the row struct ("this row has a prompt"). The prompt marker must be reflowed along with the text on window resizes. In an ideal world, erasing, or overwriting the cell where the OSC was emitted, would remove the prompt mark. Since we don't store this information in the cell struct, we can't do that. The best we can do is reset it in erase_line(). This works well enough in the "normal" screen, when used with a "normal" shell. It doesn't really work in fullscreen apps, on the alt screen. But that doesn't matter since we don't support jumping between prompts on the alt screen anyway. To be able to jump between prompts, two new key bindings have been added: prompt-prev and prompt-next, bound to ctrl+shift+z and ctrl+shift+x respectively. prompt-prev will jump to the previous, not currently visible, prompt, by moving the viewport, ensuring the prompt is at the top of the screen. prompt-next jumps to the next prompt, visible or not. Again, by moving the viewport to ensure the prompt is at the top of the screen. If we're at the bottom of the scrollback, the viewport is instead moved as far down as possible. Closes #30
115 lines
3.1 KiB
C
115 lines
3.1 KiB
C
#include "commands.h"
|
||
|
||
#define LOG_MODULE "commands"
|
||
#define LOG_ENABLE_DBG 0
|
||
#include "log.h"
|
||
#include "grid.h"
|
||
#include "render.h"
|
||
#include "selection.h"
|
||
#include "terminal.h"
|
||
#include "url-mode.h"
|
||
#include "util.h"
|
||
|
||
void
|
||
cmd_scrollback_up(struct terminal *term, int rows)
|
||
{
|
||
if (term->grid == &term->alt)
|
||
return;
|
||
if (urls_mode_is_active(term))
|
||
return;
|
||
|
||
const struct grid *grid = term->grid;
|
||
const int view = grid->view;
|
||
const int grid_rows = grid->num_rows;
|
||
|
||
/* The view row number in scrollback relative coordinates. This is
|
||
* the maximum number of rows we’re allowed to scroll */
|
||
int sb_start = grid_sb_start_ignore_uninitialized(grid, term->rows);
|
||
int view_sb_rel =
|
||
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, view);
|
||
|
||
rows = min(rows, view_sb_rel);
|
||
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)
|
||
for (int r = 0; r < term->rows; r++)
|
||
xassert(grid->rows[(new_view + r) & (grid->num_rows - 1)] != NULL);
|
||
#endif
|
||
|
||
LOG_DBG("scrollback UP: %d -> %d (offset = %d, rows = %d)",
|
||
view, new_view, offset, grid_rows);
|
||
|
||
selection_view_up(term, new_view);
|
||
term->grid->view = new_view;
|
||
|
||
if (rows < term->rows) {
|
||
term_damage_scroll(
|
||
term, DAMAGE_SCROLL_REVERSE_IN_VIEW,
|
||
(struct scroll_region){0, term->rows}, rows);
|
||
term_damage_rows_in_view(term, 0, rows - 1);
|
||
} else
|
||
term_damage_view(term);
|
||
|
||
render_refresh_urls(term);
|
||
render_refresh(term);
|
||
}
|
||
|
||
void
|
||
cmd_scrollback_down(struct terminal *term, int rows)
|
||
{
|
||
if (term->grid == &term->alt)
|
||
return;
|
||
if (urls_mode_is_active(term))
|
||
return;
|
||
|
||
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;
|
||
|
||
int new_view = (view + rows) & (grid_rows - 1);
|
||
|
||
xassert(new_view != view);
|
||
xassert(grid->rows[new_view] != NULL);
|
||
#if defined(_DEBUG)
|
||
for (int r = 0; r < term->rows; r++)
|
||
xassert(grid->rows[(new_view + r) & (grid_rows - 1)] != NULL);
|
||
#endif
|
||
|
||
LOG_DBG("scrollback DOWN: %d -> %d (offset = %d, rows = %d)",
|
||
view, new_view, offset, grid_rows);
|
||
|
||
selection_view_down(term, new_view);
|
||
term->grid->view = new_view;
|
||
|
||
if (rows < term->rows) {
|
||
term_damage_scroll(
|
||
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
|
||
term_damage_view(term);
|
||
|
||
render_refresh_urls(term);
|
||
render_refresh(term);
|
||
}
|