mirror of
				https://gitlab.freedesktop.org/wayland/wayland.git
				synced 2025-11-03 09:01:42 -05:00 
			
		
		
		
	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
					
				
					 1 changed files with 190 additions and 16 deletions
				
			
		| 
						 | 
				
			
			@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue