mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-11 04:27:49 -05:00
commit
f72555f29a
8 changed files with 320 additions and 13 deletions
|
|
@ -62,8 +62,12 @@
|
|||
* Support for OSC-176, _"Set App-ID"_
|
||||
(https://gist.github.com/delthas/d451e2cc1573bb2364839849c7117239).
|
||||
* Support for `DECRQM` queries with ANSI/ECMA-48 modes (`CSI Ps $ p`).
|
||||
* Rectangular edit functions: `DECCARA`, `DECRARA`, `DECCRA`, `DECFRA`
|
||||
and `DECERA` ([#1633][1633]).
|
||||
* `Rect` capability to terminfo.
|
||||
|
||||
[1348]: https://codeberg.org/dnkl/foot/issues/1348
|
||||
[1633]: https://codeberg.org/dnkl/foot/issues/1633
|
||||
|
||||
|
||||
### Changed
|
||||
|
|
|
|||
230
csi.c
230
csi.c
|
|
@ -673,6 +673,24 @@ xtrestore(struct terminal *term, unsigned param)
|
|||
decset_decrst(term, param, enable);
|
||||
}
|
||||
|
||||
static bool
|
||||
params_to_rectangular_area(const struct terminal *term, int first_idx,
|
||||
int *top, int *left, int *bottom, int *right)
|
||||
{
|
||||
int rel_top = vt_param_get(term, first_idx + 0, 1) - 1;
|
||||
*left = min(vt_param_get(term, first_idx + 1, 1) - 1, term->cols - 1);
|
||||
int rel_bottom = vt_param_get(term, first_idx + 2, term->rows) - 1;
|
||||
*right = min(vt_param_get(term, first_idx + 3, term->cols) - 1, term->cols - 1);
|
||||
|
||||
if (rel_top > rel_bottom || *left > *right)
|
||||
return false;
|
||||
|
||||
*top = term_row_rel_to_abs(term, rel_top);
|
||||
*bottom = term_row_rel_to_abs(term, rel_bottom);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
csi_dispatch(struct terminal *term, uint8_t final)
|
||||
{
|
||||
|
|
@ -743,10 +761,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
* Note: tertiary DA responds with "FOOT".
|
||||
*/
|
||||
if (term->conf->tweak.sixel) {
|
||||
static const char reply[] = "\033[?62;4;22c";
|
||||
static const char reply[] = "\033[?62;4;22;28c";
|
||||
term_to_slave(term, reply, sizeof(reply) - 1);
|
||||
} else {
|
||||
static const char reply[] = "\033[?62;22c";
|
||||
static const char reply[] = "\033[?62;22;28c";
|
||||
term_to_slave(term, reply, sizeof(reply) - 1);
|
||||
}
|
||||
break;
|
||||
|
|
@ -1752,6 +1770,214 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
break; /* private[0] == '=' */
|
||||
}
|
||||
|
||||
case '$': {
|
||||
switch (final) {
|
||||
case 'r': { /* DECCARA */
|
||||
int top, left, bottom, right;
|
||||
if (!params_to_rectangular_area(
|
||||
term, 0, &top, &left, &bottom, &right))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int r = top; r <= bottom; r++) {
|
||||
struct row *row = grid_row(term->grid, r);
|
||||
row->dirty = true;
|
||||
|
||||
for (int c = left; c <= right; c++) {
|
||||
struct attributes *a = &row->cells[c].attrs;
|
||||
a->clean = 0;
|
||||
|
||||
for (size_t i = 4; i < term->vt.params.idx; i++) {
|
||||
const int param = term->vt.params.v[i].value;
|
||||
|
||||
/* DECCARA only supports a sub-set of SGR parameters */
|
||||
switch (param) {
|
||||
case 0:
|
||||
a->bold = false;
|
||||
a->underline = false;
|
||||
a->blink = false;
|
||||
a->reverse = false;
|
||||
break;
|
||||
|
||||
case 1: a->bold = true; break;
|
||||
case 4: a->underline = true; break;
|
||||
case 5: a->blink = true; break;
|
||||
case 7: a->reverse = true; break;
|
||||
|
||||
case 22: a->bold = false; break;
|
||||
case 24: a->underline = false; break;
|
||||
case 25: a->blink = false; break;
|
||||
case 27: a->reverse = false; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 't': { /* DECRARA */
|
||||
int top, left, bottom, right;
|
||||
if (!params_to_rectangular_area(
|
||||
term, 0, &top, &left, &bottom, &right))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int r = top; r <= bottom; r++) {
|
||||
struct row *row = grid_row(term->grid, r);
|
||||
row->dirty = true;
|
||||
|
||||
for (int c = left; c <= right; c++) {
|
||||
struct attributes *a = &row->cells[c].attrs;
|
||||
a->clean = 0;
|
||||
|
||||
for (size_t i = 4; i < term->vt.params.idx; i++) {
|
||||
const int param = term->vt.params.v[i].value;
|
||||
|
||||
/* DECRARA only supports a sub-set of SGR parameters */
|
||||
switch (param) {
|
||||
case 0:
|
||||
a->bold = !a->bold;
|
||||
a->underline = !a->underline;
|
||||
a->blink = !a->blink;
|
||||
a->reverse = !a->reverse;
|
||||
break;
|
||||
|
||||
case 1: a->bold = !a->bold; break;
|
||||
case 4: a->underline = !a->underline; break;
|
||||
case 5: a->blink = !a->blink; break;
|
||||
case 7: a->reverse = !a->reverse; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'v': { /* DECCRA */
|
||||
int src_top, src_left, src_bottom, src_right;
|
||||
if (!params_to_rectangular_area(
|
||||
term, 0, &src_top, &src_left, &src_bottom, &src_right))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int src_page = vt_param_get(term, 4, 1);
|
||||
|
||||
int dst_rel_top = vt_param_get(term, 5, 1) - 1;
|
||||
int dst_left = vt_param_get(term, 6, 1) - 1;
|
||||
int dst_page = vt_param_get(term, 7, 1);
|
||||
|
||||
if (unlikely(src_page != 1 || dst_page != 1)) {
|
||||
/* We don’t support “pages” */
|
||||
break;
|
||||
}
|
||||
|
||||
int dst_rel_bottom = dst_rel_top + (src_bottom - src_top);
|
||||
int dst_right = min(dst_left + (src_right - src_left), term->cols - 1);
|
||||
|
||||
int dst_top = term_row_rel_to_abs(term, dst_rel_top);
|
||||
int dst_bottom = term_row_rel_to_abs(term, dst_rel_bottom);
|
||||
|
||||
/* Target area outside the screen is clipped */
|
||||
const size_t row_count = min(src_bottom - src_top,
|
||||
dst_bottom - dst_top) + 1;
|
||||
const size_t cell_count = min(src_right - src_left,
|
||||
dst_right - dst_left) + 1;
|
||||
|
||||
sixel_overwrite_by_rectangle(
|
||||
term, dst_top, dst_left, row_count, cell_count);
|
||||
|
||||
/*
|
||||
* Copy source area
|
||||
*
|
||||
* Note: since source and destination may overlap, we need
|
||||
* to copy out the entire source region first, and _then_
|
||||
* write the destination. I.e. this is similar to how
|
||||
* memmove() behaves, but adapted to our row/cell
|
||||
* structure.
|
||||
*/
|
||||
struct cell **copy = xmalloc(row_count * sizeof(copy[0]));
|
||||
for (int r = 0; r < row_count; r++) {
|
||||
copy[r] = xmalloc(cell_count * sizeof(copy[r][0]));
|
||||
|
||||
const struct row *row = grid_row(term->grid, src_top + r);
|
||||
const struct cell *cell = &row->cells[src_left];
|
||||
memcpy(copy[r], cell, cell_count * sizeof(copy[r][0]));
|
||||
}
|
||||
|
||||
/* Paste into destination area */
|
||||
for (int r = 0; r < row_count; r++) {
|
||||
struct row *row = grid_row(term->grid, dst_top + r);
|
||||
row->dirty = true;
|
||||
|
||||
struct cell *cell = &row->cells[dst_left];
|
||||
memcpy(cell, copy[r], cell_count * sizeof(copy[r][0]));
|
||||
free(copy[r]);
|
||||
|
||||
for (;cell < &row->cells[dst_left + cell_count]; cell++)
|
||||
cell->attrs.clean = 0;
|
||||
|
||||
if (unlikely(row->extra != NULL)) {
|
||||
/* TODO: technically, we should copy the source URIs... */
|
||||
grid_row_uri_range_erase(row, dst_left, dst_right);
|
||||
}
|
||||
}
|
||||
free(copy);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'x': { /* DECFRA */
|
||||
const uint8_t c = vt_param_get(term, 0, 0);
|
||||
|
||||
if (unlikely(!((c >= 32 && c < 126) || c >= 160)))
|
||||
break;
|
||||
|
||||
int top, left, bottom, right;
|
||||
if (!params_to_rectangular_area(
|
||||
term, 1, &top, &left, &bottom, &right))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Erase the entire region at once (MUCH cheaper than
|
||||
* doing it row by row, or even character by
|
||||
* character). */
|
||||
sixel_overwrite_by_rectangle(
|
||||
term, top, left, bottom - top + 1, right - left + 1);
|
||||
|
||||
for (int r = top; r <= bottom; r++)
|
||||
term_fill(term, r, left, c, right - left + 1, true);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'z': { /* DECERA */
|
||||
int top, left, bottom, right;
|
||||
if (!params_to_rectangular_area(
|
||||
term, 0, &top, &left, &bottom, &right))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: term_erase() _also_ erases sixels, but since
|
||||
* we’re forced to erase one row at a time, erasing the
|
||||
* entire sixel here is more efficient.
|
||||
*/
|
||||
sixel_overwrite_by_rectangle(
|
||||
term, top, left, bottom - top + 1, right - left + 1);
|
||||
|
||||
for (int r = top; r <= bottom; r++)
|
||||
term_erase(term, r, left, r, right);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break; /* private[0] == ‘$’ */
|
||||
}
|
||||
|
||||
case 0x243f: /* ?$ */
|
||||
switch (final) {
|
||||
case 'p': {
|
||||
|
|
|
|||
|
|
@ -495,6 +495,31 @@ manipulation sequences. The generic format is:
|
|||
: VT320
|
||||
: Request status of ECMA-48/ANSI mode. See the descriptions for SM/RM
|
||||
above for recognized _Ps_ values.
|
||||
| \\E[ _Pt_ ; _Pl_ ; _Pb_ ; _Pr_ ; _Pm_ $ r
|
||||
: DECCARA
|
||||
: VT400
|
||||
: Change attributes in rectangular area. _Pt_, _Pl_, _Pb_ and _Pr_
|
||||
denotes the rectangle, _Pm_ denotes the SGR attributes.
|
||||
| \\E[ _Pt_ ; _Pl_ ; _Pb_ ; _Pr_ ; _Pm_ $ t
|
||||
: DECRARA
|
||||
: VT400
|
||||
: Invert attributes in rectangular area. _Pt_, _Pl_, _Pb_ and _Pr_
|
||||
denotes the rectangle, _Pm_ denotes the SGR attributes.
|
||||
| \\E[ _Pt_ ; _Pl_ ; _Pb_ ; _Pr_ ; _Pp_ ; _Pt_ ; _Pl_ ; _Pp_ $ v
|
||||
: DECCRA
|
||||
: VT400
|
||||
: Copy rectangular area. _Pt_, _Pl_, _Pb_ and _Pr_ denotes the
|
||||
rectangle, _Pt_ and _Pl_ denotes the target location.
|
||||
| \\E[ _Pc_ ; _Pt_ ; _Pl_ ; _Pb_ ; _Pr_ $ x
|
||||
: DECFRA
|
||||
: VT420
|
||||
: Fill rectangular area. _Pc_ is the character to use, _Pt_, _Pl_,
|
||||
_Pb_ and _Pr_ denotes the rectangle.
|
||||
| \\E[ _Pt_ ; _Pl_ ; _Pb_ ; _Pr_ $ z
|
||||
: DECERA
|
||||
: VT400
|
||||
: Erase rectangular area. _Pt_, _Pl_, _Pb_ and _Pr_ denotes the
|
||||
rectangle.
|
||||
| \\E[ _Ps_ T
|
||||
: SD
|
||||
: VT420
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
PE=\E[201~,
|
||||
PS=\E[200~,
|
||||
RV=\E[>c,
|
||||
Rect=\E[%p1%d;%p2%d;%p3%d;%p4%d;%p5%d$x,
|
||||
Se=\E[ q,
|
||||
Ss=\E[%p1%d q,
|
||||
Sync=\E[?2026%?%p1%{1}%-%tl%eh%;,
|
||||
|
|
|
|||
1
grid.h
1
grid.h
|
|
@ -86,7 +86,6 @@ grid_row_in_view(struct grid *grid, int row_no)
|
|||
|
||||
void grid_row_uri_range_put(
|
||||
struct row *row, int col, const char *uri, uint64_t id);
|
||||
void grid_row_uri_range_add(struct row *row, struct row_uri_range range);
|
||||
void grid_row_uri_range_erase(struct row *row, int start, int end);
|
||||
|
||||
static inline void
|
||||
|
|
|
|||
50
terminal.c
50
terminal.c
|
|
@ -3498,6 +3498,54 @@ print_spacer(struct terminal *term, int col, int remaining)
|
|||
cell->attrs = term->vt.attrs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Puts a character on the grid. Coordinates are in screen coordinates
|
||||
* (i.e. ‘cursor’ coordinates).
|
||||
*
|
||||
* Does NOT:
|
||||
* - update the cursor
|
||||
* - linewrap
|
||||
* - erase sixels
|
||||
*
|
||||
* Limitations:
|
||||
* - double width characters not supported
|
||||
*/
|
||||
void
|
||||
term_fill(struct terminal *term, int r, int c, uint8_t data, size_t count,
|
||||
bool use_sgr_attrs)
|
||||
{
|
||||
struct row *row = grid_row(term->grid, r);
|
||||
row->dirty = true;
|
||||
|
||||
xassert(c + count <= term->cols);
|
||||
|
||||
struct attributes attrs = use_sgr_attrs
|
||||
? term->vt.attrs
|
||||
: (struct attributes){0};
|
||||
|
||||
const struct cell *last = &row->cells[c + count];
|
||||
for (struct cell *cell = &row->cells[c]; cell < last; cell++) {
|
||||
cell->wc = data;
|
||||
cell->attrs = attrs;
|
||||
|
||||
if (unlikely(term->vt.osc8.uri != NULL)) {
|
||||
grid_row_uri_range_put(row, c, term->vt.osc8.uri, term->vt.osc8.id);
|
||||
|
||||
switch (term->conf->url.osc8_underline) {
|
||||
case OSC8_UNDERLINE_ALWAYS:
|
||||
cell->attrs.url = true;
|
||||
break;
|
||||
|
||||
case OSC8_UNDERLINE_URL_MODE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(row->extra != NULL))
|
||||
grid_row_uri_range_erase(row, c, c + count - 1);
|
||||
}
|
||||
|
||||
void
|
||||
term_print(struct terminal *term, char32_t wc, int width)
|
||||
{
|
||||
|
|
@ -3566,7 +3614,7 @@ term_print(struct terminal *term, char32_t wc, int width)
|
|||
grid_row_uri_range_erase(row, col, col + width - 1);
|
||||
|
||||
/* Advance cursor the 'additional' columns while dirty:ing the cells */
|
||||
for (int i = 1; i < width && col < term->cols - 1; i++) {
|
||||
for (int i = 1; i < width && col < term->cols; i++) {
|
||||
col++;
|
||||
print_spacer(term, col, width - i);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -798,6 +798,8 @@ void term_cursor_down(struct terminal *term, int count);
|
|||
void term_cursor_blink_update(struct terminal *term);
|
||||
|
||||
void term_print(struct terminal *term, char32_t wc, int width);
|
||||
void term_fill(struct terminal *term, int row, int col, uint8_t c, size_t count,
|
||||
bool use_sgr_attrs);
|
||||
|
||||
void term_scroll(struct terminal *term, int rows);
|
||||
void term_scroll_reverse(struct terminal *term, int rows);
|
||||
|
|
|
|||
20
vt.c
20
vt.c
|
|
@ -18,6 +18,7 @@
|
|||
#include "debug.h"
|
||||
#include "grid.h"
|
||||
#include "osc.h"
|
||||
#include "sixel.h"
|
||||
#include "util.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
|
|
@ -560,15 +561,16 @@ action_esc_dispatch(struct terminal *term, uint8_t final)
|
|||
|
||||
case '#':
|
||||
switch (final) {
|
||||
case '8':
|
||||
for (int r = 0; r < term->rows; r++) {
|
||||
struct row *row = grid_row(term->grid, r);
|
||||
for (int c = 0; c < term->cols; c++) {
|
||||
row->cells[c].wc = U'E';
|
||||
row->cells[c].attrs = (struct attributes){0};
|
||||
}
|
||||
row->dirty = true;
|
||||
}
|
||||
case '8': /* DECALN */
|
||||
sixel_overwrite_by_rectangle(term, 0, 0, term->rows, term->cols);
|
||||
|
||||
term->scroll_region.start = 0;
|
||||
term->scroll_region.end = term->rows;
|
||||
|
||||
for (int r = 0; r < term->rows; r++)
|
||||
term_fill(term, r, 0, 'E', term->cols, false);
|
||||
|
||||
term_cursor_home(term);
|
||||
break;
|
||||
}
|
||||
break; /* private[0] == '#' */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue