terminal: Scroll margins

Implement scroll margins and related escape codes.

Signed-off-by: Callum Lowcay <callum@callumscode.com>
dev
Callum Lowcay 14 years ago committed by Kristian Høgsberg
parent b8609ada50
commit bbeac60b8a
  1. 206
      clients/terminal.c

@ -187,7 +187,9 @@ struct terminal {
union utf8_char *data;
struct attr *data_attr;
struct attr curr_attr;
char origin_mode;
union utf8_char last_char;
int margin_top, margin_bottom;
int data_pitch, attr_pitch; /* The width in bytes of a line */
int width, height, start, row, column;
int fd, master;
@ -211,6 +213,7 @@ static void
terminal_init(struct terminal *terminal)
{
terminal->curr_attr = terminal->color_scheme->default_attr;
terminal->origin_mode = 0;
terminal->row = 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];
}
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
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->width = width;
terminal->height = height;
if(terminal->margin_bottom >= terminal->height)
terminal->margin_bottom = terminal->height - 1;
terminal->data = data;
terminal->data_attr = data_attr;
@ -530,13 +623,39 @@ handle_char(struct terminal *terminal, union utf8_char utf8);
static void
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
handle_escape(struct terminal *terminal)
{
union utf8_char *row;
struct attr *attr_row;
char *p;
int i, count;
int i, count, top, bottom;
int args[10], set[10] = { 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));
attr_init(&attr_row[terminal->column], terminal->curr_attr, (terminal->width - terminal->column));
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 */
if (set[0] && set[1] && set[2] && args[1] == 5) {
if (args[0] == 38) {
@ -631,11 +766,31 @@ handle_escape(struct terminal *terminal)
}
}
break;
case '?':
if (strcmp(p, "?25l") == 0) {
/* hide cursor */
} else if (strcmp(p, "?25h") == 0) {
/* show cursor */
case 'r':
if(!set[0]) {
terminal->margin_top = 0;
terminal->margin_bottom = terminal->height-1;
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;
default:
@ -647,6 +802,28 @@ handle_escape(struct terminal *terminal)
static void
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
@ -735,16 +912,10 @@ handle_special_char(struct terminal *terminal, char c)
/* fallthrough */
case '\v':
case '\f':
if (terminal->row + 1 < terminal->height) {
terminal->row++;
} else {
terminal->start++;
if (terminal->start == terminal->height)
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);
terminal->row++;
if(terminal->row > terminal->margin_bottom) {
terminal->row = terminal->margin_bottom;
terminal_scroll(terminal, +1);
}
break;
@ -969,6 +1140,9 @@ terminal_create(struct display *display, int fullscreen)
terminal->fullscreen = fullscreen;
terminal->color_scheme = &DEFAULT_COLORS;
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",
500, 400);

Loading…
Cancel
Save