terminal: Scroll margins
Implement scroll margins and related escape codes. Signed-off-by: Callum Lowcay <callum@callumscode.com>
This commit is contained in:
parent
b8609ada50
commit
bbeac60b8a
@ -187,7 +187,9 @@ struct terminal {
|
|||||||
union utf8_char *data;
|
union utf8_char *data;
|
||||||
struct attr *data_attr;
|
struct attr *data_attr;
|
||||||
struct attr curr_attr;
|
struct attr curr_attr;
|
||||||
|
char origin_mode;
|
||||||
union utf8_char last_char;
|
union utf8_char last_char;
|
||||||
|
int margin_top, margin_bottom;
|
||||||
int data_pitch, attr_pitch; /* The width in bytes of a line */
|
int data_pitch, attr_pitch; /* The width in bytes of a line */
|
||||||
int width, height, start, row, column;
|
int width, height, start, row, column;
|
||||||
int fd, master;
|
int fd, master;
|
||||||
@ -211,6 +213,7 @@ static void
|
|||||||
terminal_init(struct terminal *terminal)
|
terminal_init(struct terminal *terminal)
|
||||||
{
|
{
|
||||||
terminal->curr_attr = terminal->color_scheme->default_attr;
|
terminal->curr_attr = terminal->color_scheme->default_attr;
|
||||||
|
terminal->origin_mode = 0;
|
||||||
|
|
||||||
terminal->row = 0;
|
terminal->row = 0;
|
||||||
terminal->column = 0;
|
terminal->column = 0;
|
||||||
@ -265,6 +268,94 @@ terminal_get_attr(struct terminal *terminal, int row, int col) {
|
|||||||
return terminal_get_attr_row(terminal, row)[col];
|
return terminal_get_attr_row(terminal, row)[col];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
terminal_scroll_buffer(struct terminal *terminal, int d)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
d = d % (terminal->height + 1);
|
||||||
|
terminal->start = (terminal->start + d) % terminal->height;
|
||||||
|
if (terminal->start < 0) terminal->start = terminal->height + terminal->start;
|
||||||
|
if(d < 0) {
|
||||||
|
d = 0 - d;
|
||||||
|
for(i = 0; i < d; i++) {
|
||||||
|
memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
|
||||||
|
attr_init(terminal_get_attr_row(terminal, i),
|
||||||
|
terminal->curr_attr, terminal->width);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(i = terminal->height - d; i < terminal->height; i++) {
|
||||||
|
memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
|
||||||
|
attr_init(terminal_get_attr_row(terminal, i),
|
||||||
|
terminal->curr_attr, terminal->width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
terminal_scroll_window(struct terminal *terminal, int d)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int window_height;
|
||||||
|
int from_row, to_row;
|
||||||
|
struct attr *dup_attr;
|
||||||
|
|
||||||
|
// scrolling range is inclusive
|
||||||
|
window_height = terminal->margin_bottom - terminal->margin_top + 1;
|
||||||
|
d = d % (window_height + 1);
|
||||||
|
if(d < 0) {
|
||||||
|
d = 0 - d;
|
||||||
|
to_row = terminal->margin_bottom;
|
||||||
|
from_row = terminal->margin_bottom - d;
|
||||||
|
|
||||||
|
for (i = 0; i < (window_height - d); i++) {
|
||||||
|
memcpy(terminal_get_row(terminal, to_row - i),
|
||||||
|
terminal_get_row(terminal, from_row - i),
|
||||||
|
terminal->data_pitch);
|
||||||
|
memcpy(terminal_get_attr_row(terminal, to_row - i),
|
||||||
|
terminal_get_attr_row(terminal, from_row - i),
|
||||||
|
terminal->attr_pitch);
|
||||||
|
}
|
||||||
|
dup_attr = terminal_get_attr_row(terminal, terminal->margin_top);
|
||||||
|
for (i = terminal->margin_top; i < (terminal->margin_top + d); i++) {
|
||||||
|
memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
|
||||||
|
if (i > terminal->margin_top) {
|
||||||
|
memcpy(terminal_get_attr_row(terminal, i),
|
||||||
|
dup_attr, terminal->attr_pitch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
to_row = terminal->margin_top;
|
||||||
|
from_row = terminal->margin_top + d;
|
||||||
|
|
||||||
|
for (i = 0; i < (window_height - d); i++) {
|
||||||
|
memcpy(terminal_get_row(terminal, to_row + i),
|
||||||
|
terminal_get_row(terminal, from_row + i),
|
||||||
|
terminal->data_pitch);
|
||||||
|
memcpy(terminal_get_attr_row(terminal, to_row + i),
|
||||||
|
terminal_get_attr_row(terminal, from_row + i),
|
||||||
|
terminal->attr_pitch);
|
||||||
|
}
|
||||||
|
dup_attr = terminal_get_attr_row(terminal, terminal->margin_bottom);
|
||||||
|
for (i = terminal->margin_bottom - d + 1; i <= terminal->margin_bottom; i++) {
|
||||||
|
memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
|
||||||
|
if (i < terminal->margin_bottom) {
|
||||||
|
memcpy(terminal_get_attr_row(terminal, i),
|
||||||
|
dup_attr, terminal->attr_pitch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
terminal_scroll(struct terminal *terminal, int d)
|
||||||
|
{
|
||||||
|
if(terminal->margin_top == 0 && terminal->margin_bottom == terminal->height - 1)
|
||||||
|
terminal_scroll_buffer(terminal, d);
|
||||||
|
else
|
||||||
|
terminal_scroll_window(terminal, d);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
terminal_resize(struct terminal *terminal, int width, int height)
|
terminal_resize(struct terminal *terminal, int width, int height)
|
||||||
{
|
{
|
||||||
@ -317,6 +408,8 @@ terminal_resize(struct terminal *terminal, int width, int height)
|
|||||||
terminal->attr_pitch = attr_pitch;
|
terminal->attr_pitch = attr_pitch;
|
||||||
terminal->width = width;
|
terminal->width = width;
|
||||||
terminal->height = height;
|
terminal->height = height;
|
||||||
|
if(terminal->margin_bottom >= terminal->height)
|
||||||
|
terminal->margin_bottom = terminal->height - 1;
|
||||||
terminal->data = data;
|
terminal->data = data;
|
||||||
terminal->data_attr = data_attr;
|
terminal->data_attr = data_attr;
|
||||||
|
|
||||||
@ -530,13 +623,39 @@ handle_char(struct terminal *terminal, union utf8_char utf8);
|
|||||||
static void
|
static void
|
||||||
handle_sgr(struct terminal *terminal, int code);
|
handle_sgr(struct terminal *terminal, int code);
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_term_parameter(struct terminal *terminal, int code, int sr)
|
||||||
|
{
|
||||||
|
if (terminal->qmark_flag) {
|
||||||
|
switch(code) {
|
||||||
|
case 6: /* DECOM */
|
||||||
|
terminal->origin_mode = sr;
|
||||||
|
if (terminal->origin_mode)
|
||||||
|
terminal->row = terminal->margin_top;
|
||||||
|
else
|
||||||
|
terminal->row = 0;
|
||||||
|
terminal->column = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown parameter: ?%d\n", code);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch(code) {
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown parameter: %d\n", code);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_escape(struct terminal *terminal)
|
handle_escape(struct terminal *terminal)
|
||||||
{
|
{
|
||||||
union utf8_char *row;
|
union utf8_char *row;
|
||||||
struct attr *attr_row;
|
struct attr *attr_row;
|
||||||
char *p;
|
char *p;
|
||||||
int i, count;
|
int i, count, top, bottom;
|
||||||
int args[10], set[10] = { 0, };
|
int args[10], set[10] = { 0, };
|
||||||
|
|
||||||
terminal->escape[terminal->escape_length++] = '\0';
|
terminal->escape[terminal->escape_length++] = '\0';
|
||||||
@ -610,6 +729,22 @@ handle_escape(struct terminal *terminal)
|
|||||||
memset(&row[terminal->column], 0, (terminal->width - terminal->column) * sizeof(union utf8_char));
|
memset(&row[terminal->column], 0, (terminal->width - terminal->column) * sizeof(union utf8_char));
|
||||||
attr_init(&attr_row[terminal->column], terminal->curr_attr, (terminal->width - terminal->column));
|
attr_init(&attr_row[terminal->column], terminal->curr_attr, (terminal->width - terminal->column));
|
||||||
break;
|
break;
|
||||||
|
case 'S': /* SU */
|
||||||
|
terminal_scroll(terminal, set[0] ? args[0] : 1);
|
||||||
|
break;
|
||||||
|
case 'T': /* SD */
|
||||||
|
terminal_scroll(terminal, 0 - (set[0] ? args[0] : 1));
|
||||||
|
break;
|
||||||
|
case 'h': /* SM */
|
||||||
|
for(i = 0; i < 10 && set[i]; i++) {
|
||||||
|
handle_term_parameter(terminal, args[i], 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'l': /* RM */
|
||||||
|
for(i = 0; i < 10 && set[i]; i++) {
|
||||||
|
handle_term_parameter(terminal, args[i], 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'm': /* SGR */
|
case 'm': /* SGR */
|
||||||
if (set[0] && set[1] && set[2] && args[1] == 5) {
|
if (set[0] && set[1] && set[2] && args[1] == 5) {
|
||||||
if (args[0] == 38) {
|
if (args[0] == 38) {
|
||||||
@ -631,11 +766,31 @@ handle_escape(struct terminal *terminal)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '?':
|
case 'r':
|
||||||
if (strcmp(p, "?25l") == 0) {
|
if(!set[0]) {
|
||||||
/* hide cursor */
|
terminal->margin_top = 0;
|
||||||
} else if (strcmp(p, "?25h") == 0) {
|
terminal->margin_bottom = terminal->height-1;
|
||||||
/* show cursor */
|
terminal->row = 0;
|
||||||
|
terminal->column = 0;
|
||||||
|
} else {
|
||||||
|
top = (set[0] ? args[0] : 1) - 1;
|
||||||
|
top = top < 0 ? 0 :
|
||||||
|
(top >= terminal->height ? terminal->height - 1 : top);
|
||||||
|
bottom = (set[1] ? args[1] : 1) - 1;
|
||||||
|
bottom = bottom < 0 ? 0 :
|
||||||
|
(bottom >= terminal->height ? terminal->height - 1 : bottom);
|
||||||
|
if(bottom > top) {
|
||||||
|
terminal->margin_top = top;
|
||||||
|
terminal->margin_bottom = bottom;
|
||||||
|
} else {
|
||||||
|
terminal->margin_top = 0;
|
||||||
|
terminal->margin_bottom = terminal->height-1;
|
||||||
|
}
|
||||||
|
if(terminal->origin_mode)
|
||||||
|
terminal->row = terminal->margin_top;
|
||||||
|
else
|
||||||
|
terminal->row = 0;
|
||||||
|
terminal->column = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -647,6 +802,28 @@ handle_escape(struct terminal *terminal)
|
|||||||
static void
|
static void
|
||||||
handle_non_csi_escape(struct terminal *terminal, char code)
|
handle_non_csi_escape(struct terminal *terminal, char code)
|
||||||
{
|
{
|
||||||
|
switch(code) {
|
||||||
|
case 'M': /* RI */
|
||||||
|
terminal->row -= 1;
|
||||||
|
if(terminal->row < terminal->margin_top) {
|
||||||
|
terminal->row = terminal->margin_top;
|
||||||
|
terminal_scroll(terminal, -1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'E': /* NEL */
|
||||||
|
terminal->column = 0;
|
||||||
|
// fallthrough
|
||||||
|
case 'D': /* IND */
|
||||||
|
terminal->row += 1;
|
||||||
|
if(terminal->row > terminal->margin_bottom) {
|
||||||
|
terminal->row = terminal->margin_bottom;
|
||||||
|
terminal_scroll(terminal, +1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown escape code: %c\n", code);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -735,16 +912,10 @@ handle_special_char(struct terminal *terminal, char c)
|
|||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case '\v':
|
case '\v':
|
||||||
case '\f':
|
case '\f':
|
||||||
if (terminal->row + 1 < terminal->height) {
|
|
||||||
terminal->row++;
|
terminal->row++;
|
||||||
} else {
|
if(terminal->row > terminal->margin_bottom) {
|
||||||
terminal->start++;
|
terminal->row = terminal->margin_bottom;
|
||||||
if (terminal->start == terminal->height)
|
terminal_scroll(terminal, +1);
|
||||||
terminal->start = 0;
|
|
||||||
memset(terminal_get_row(terminal, terminal->row),
|
|
||||||
0, terminal->width * sizeof(union utf8_char));
|
|
||||||
attr_init(terminal_get_attr_row(terminal, terminal->row),
|
|
||||||
terminal->curr_attr, terminal->width);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -969,6 +1140,9 @@ terminal_create(struct display *display, int fullscreen)
|
|||||||
terminal->fullscreen = fullscreen;
|
terminal->fullscreen = fullscreen;
|
||||||
terminal->color_scheme = &DEFAULT_COLORS;
|
terminal->color_scheme = &DEFAULT_COLORS;
|
||||||
terminal_init(terminal);
|
terminal_init(terminal);
|
||||||
|
terminal->margin_top = 0;
|
||||||
|
terminal->margin_bottom = 10000; /* much too large, will be trimmed down
|
||||||
|
* by terminal_resize */
|
||||||
terminal->window = window_create(display, "Wayland Terminal",
|
terminal->window = window_create(display, "Wayland Terminal",
|
||||||
500, 400);
|
500, 400);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user