terminal: Add scroll-back history
authorKristian Høgsberg <krh@bitplanet.net>
Wed, 23 Oct 2013 23:29:14 +0000 (16:29 -0700)
committerKristian Høgsberg <krh@bitplanet.net>
Wed, 23 Oct 2013 23:29:14 +0000 (16:29 -0700)
Ctrl-Shift-UP/DOWN to scroll through the history.

clients/terminal.c

index 38abe50..e49fed9 100644 (file)
@@ -441,8 +441,11 @@ struct terminal {
        character_set saved_cs, saved_g0, saved_g1;
        keyboard_mode key_mode;
        int data_pitch, attr_pitch;  /* The width in bytes of a line */
-       int width, height, start, row, column;
+       int width, height, row, column, max_width;
+       uint32_t buffer_height;
+       uint32_t start, end, saved_start, log_size;
        int saved_row, saved_column;
+       int scrolling;
        int send_cursor_position;
        int fd, master;
        uint32_t modifiers;
@@ -545,9 +548,9 @@ terminal_get_row(struct terminal *terminal, int row)
 {
        int index;
 
-       index = (row + terminal->start) % terminal->height;
+       index = (row + terminal->start) & (terminal->buffer_height - 1);
 
-       return &terminal->data[index * terminal->width];
+       return (void *) terminal->data + index * terminal->data_pitch;
 }
 
 static struct attr*
@@ -555,9 +558,9 @@ terminal_get_attr_row(struct terminal *terminal, int row)
 {
        int index;
 
-       index = (row + terminal->start) % terminal->height;
+       index = (row + terminal->start) & (terminal->buffer_height - 1);
 
-       return &terminal->data_attr[index * terminal->width];
+       return (void *) terminal->data_attr + index * terminal->attr_pitch;
 }
 
 union decoded_attr {
@@ -620,18 +623,16 @@ 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) {
+       terminal->start += d;
+       if (d < 0) {
                d = 0 - d;
-               for(i = 0; i < d; i++) {
+               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++) {
+               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);
@@ -733,69 +734,91 @@ terminal_shift_line(struct terminal *terminal, int d)
 }
 
 static void
-terminal_resize_cells(struct terminal *terminal, int width, int height)
+terminal_resize_cells(struct terminal *terminal,
+                     int width, int height)
 {
-       size_t size;
        union utf8_char *data;
        struct attr *data_attr;
        char *tab_ruler;
        int data_pitch, attr_pitch;
        int i, l, total_rows;
+       uint32_t d, uheight = height;
        struct rectangle allocation;
        struct winsize ws;
 
+       if (uheight > terminal->buffer_height)
+               height = terminal->buffer_height;
+
        if (terminal->width == width && terminal->height == height)
                return;
 
-       data_pitch = width * sizeof(union utf8_char);
-       size = data_pitch * height;
-       data = zalloc(size);
-       attr_pitch = width * sizeof(struct attr);
-       data_attr = malloc(attr_pitch * height);
-       tab_ruler = zalloc(width);
-       attr_init(data_attr, terminal->curr_attr, width * height);
-       if (terminal->data && terminal->data_attr) {
-               if (width > terminal->width)
-                       l = terminal->width;
-               else
-                       l = width;
-
-               if (terminal->height > height) {
-                       total_rows = height;
-                       i = 1 + terminal->row - height;
-                       if (i > 0) {
-                           terminal->start = (terminal->start + i) % terminal->height;
-                           terminal->row = terminal->row - i;
-                       }
-               } else {
-                       total_rows = terminal->height;
+       if (terminal->data && width <= terminal->max_width) {
+               d = 0;
+               if (height < terminal->height && height <= terminal->row)
+                       d = terminal->height - height;
+               else if (height > terminal->height &&
+                        terminal->height - 1 == terminal->row) {
+                       d = terminal->height - height;
+                       if (terminal->log_size < uheight)
+                               d = -terminal->start;
                }
 
-               for (i = 0; i < total_rows; i++) {
-                       memcpy(&data[width * i],
-                              terminal_get_row(terminal, i),
-                              l * sizeof(union utf8_char));
-                       memcpy(&data_attr[width * i],
-                              terminal_get_attr_row(terminal, i),
-                              l * sizeof(struct attr));
+               terminal->start += d;
+               terminal->row -= d;
+       } else {
+               terminal->max_width = width;
+               data_pitch = width * sizeof(union utf8_char);
+               data = zalloc(data_pitch * terminal->buffer_height);
+               attr_pitch = width * sizeof(struct attr);
+               data_attr = malloc(attr_pitch * terminal->buffer_height);
+               tab_ruler = zalloc(width);
+               attr_init(data_attr, terminal->curr_attr,
+                         width * terminal->buffer_height);
+
+               if (terminal->data && terminal->data_attr) {
+                       if (width > terminal->width)
+                               l = terminal->width;
+                       else
+                               l = width;
+
+                       if (terminal->height > height) {
+                               total_rows = height;
+                               i = 1 + terminal->row - height;
+                               if (i > 0) {
+                                       terminal->start += i;
+                                       terminal->row = terminal->row - i;
+                               }
+                       } else {
+                               total_rows = terminal->height;
+                       }
+
+                       for (i = 0; i < total_rows; i++) {
+                               memcpy(&data[width * i],
+                                      terminal_get_row(terminal, i),
+                                      l * sizeof(union utf8_char));
+                               memcpy(&data_attr[width * i],
+                                      terminal_get_attr_row(terminal, i),
+                                      l * sizeof(struct attr));
+                       }
+
+                       free(terminal->data);
+                       free(terminal->data_attr);
+                       free(terminal->tab_ruler);
                }
 
-               free(terminal->data);
-               free(terminal->data_attr);
-               free(terminal->tab_ruler);
+               terminal->data_pitch = data_pitch;
+               terminal->attr_pitch = attr_pitch;
+               terminal->data = data;
+               terminal->data_attr = data_attr;
+               terminal->tab_ruler = tab_ruler;
+               terminal_init_tabs(terminal);
+               terminal->start = 0;
        }
 
-       terminal->data_pitch = data_pitch;
-       terminal->attr_pitch = attr_pitch;
        terminal->margin_bottom =
                height - (terminal->height - terminal->margin_bottom);
        terminal->width = width;
        terminal->height = height;
-       terminal->data = data;
-       terminal->data_attr = data_attr;
-       terminal->tab_ruler = tab_ruler;
-       terminal->start = 0;
-       terminal_init_tabs(terminal);
 
        /* Update the window size */
        ws.ws_row = terminal->height;
@@ -1383,12 +1406,9 @@ handle_escape(struct terminal *terminal)
                                    terminal->curr_attr, terminal->width);
                        }
                } else if (args[0] == 2) {
-                       for (i = 0; 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);
-                       }
+                       /* Clear screen by scrolling contents out */
+                       terminal_scroll_buffer(terminal,
+                                              terminal->end - terminal->start);
                }
                break;
        case 'K':    /* EL */
@@ -1827,7 +1847,14 @@ handle_special_char(struct terminal *terminal, char c)
        case '\v':
        case '\f':
                terminal->row++;
-               if(terminal->row > terminal->margin_bottom) {
+               if (terminal->row + terminal->start > terminal->end)
+                       terminal->end = terminal->row + terminal->start;
+               if (terminal->end == terminal->buffer_height)
+                       terminal->log_size = terminal->buffer_height;
+               else if (terminal->log_size < terminal->buffer_height)
+                       terminal->log_size = terminal->end;
+
+               if (terminal->row > terminal->margin_bottom) {
                        terminal->row = terminal->margin_bottom;
                        terminal_scroll(terminal, +1);
                }
@@ -2218,6 +2245,35 @@ handle_bound_key(struct terminal *terminal,
 
                return 1;
 
+       case XKB_KEY_Up:
+               if (!terminal->scrolling)
+                       terminal->saved_start = terminal->start;
+               if (terminal->start == terminal->end - terminal->log_size)
+                       return 1;
+
+               terminal->scrolling = 1;
+               terminal->start--;
+               terminal->row++;
+               terminal->selection_start_row++;
+               terminal->selection_end_row++;
+               widget_schedule_redraw(terminal->widget);
+               return 1;
+
+       case XKB_KEY_Down:
+               if (!terminal->scrolling)
+                       terminal->saved_start = terminal->start;
+
+               if (terminal->start == terminal->saved_start)
+                       return 1;
+
+               terminal->scrolling = 1;
+               terminal->start++;
+               terminal->row--;
+               terminal->selection_start_row--;
+               terminal->selection_end_row--;
+               widget_schedule_redraw(terminal->widget);
+               return 1;
+
        default:
                return 0;
        }
@@ -2231,7 +2287,7 @@ key_handler(struct window *window, struct input *input, uint32_t time,
        struct terminal *terminal = data;
        char ch[MAX_RESPONSE];
        uint32_t modifiers, serial;
-       int ret, len = 0;
+       int ret, len = 0, d;
        bool convert_utf8 = true;
 
        modifiers = input_get_modifiers(input);
@@ -2435,6 +2491,16 @@ key_handler(struct window *window, struct input *input, uint32_t time,
        }
 
        if (state == WL_KEYBOARD_KEY_STATE_PRESSED && len > 0) {
+               if (terminal->scrolling) {
+                       d = terminal->saved_start - terminal->start;
+                       terminal->row -= d;
+                       terminal->selection_start_row -= d;
+                       terminal->selection_end_row -= d;
+                       terminal->start = terminal->saved_start;
+                       terminal->scrolling = 0;
+                       widget_schedule_redraw(terminal->widget);
+               }
+
                terminal_write(terminal, ch, len);
 
                /* Hide cursor, except if this was coming from a
@@ -2661,6 +2727,8 @@ terminal_create(struct display *display)
 
        terminal->display = display;
        terminal->margin = 5;
+       terminal->buffer_height = 1024;
+       terminal->end = 1;
 
        window_set_user_data(terminal->window, terminal);
        window_set_key_handler(terminal->window, key_handler);