From a9a3375b78390dcd2c82bfcf97bcc4bb2ef12bbd Mon Sep 17 00:00:00 2001 From: jixiufeng Date: Sat, 10 Nov 2018 19:27:57 +0800 Subject: replace refresh_row with refresh_lines --- vterm-module.c | 169 ++++++++++++++++++++++++++++----------------------------- vterm-module.h | 1 - vterm.el | 29 +++------- 3 files changed, 90 insertions(+), 109 deletions(-) diff --git a/vterm-module.c b/vterm-module.c index 1b6d630..f44f3c7 100644 --- a/vterm-module.c +++ b/vterm-module.c @@ -138,58 +138,57 @@ static size_t get_col_offset(Term *term, int row, int end_col) { return offset; } -static size_t refresh_row(Term *term, emacs_env *env, int row, int end_col, - bool append_newline) { - int j; - char *ptr = term->textbuf; +static void refresh_lines(Term *term, emacs_env *env, int start_row, + int end_row, int end_col) { + if (end_row < start_row) { + return; + } + int i, j; + + char buffer[((end_row - start_row + 1) * end_col) * 4]; int length = 0; VTermScreenCell cell; VTermScreenCell lastCell; - fetch_cell(term, row, 0, &lastCell); - - for (j = 0; j < end_col; j++) { - VTermPos pos = {.row = row, .col = j}; - fetch_cell(term, row, j, &cell); - - if (!compare_cells(&cell, &lastCell)) { - ptr[length] = '\0'; - emacs_value text = render_text(env, ptr, length, &lastCell); - insert(env, text); - ptr += length; - length = 0; - } + fetch_cell(term, start_row, 0, &lastCell); - lastCell = cell; - if (cell.chars[0] == 0) { - ptr[length] = ' '; - length++; - } else { - unsigned char bytes[4]; - size_t count = codepoint_to_utf8(cell.chars[0], bytes); - int k; - for (k = 0; k < count; k++) { - ptr[length] = bytes[k]; + int offset = 0; + for (i = start_row; i < end_row; i++) { + for (j = 0; j < end_col; j++) { + fetch_cell(term, i, j, &cell); + + if (!compare_cells(&cell, &lastCell)) { + emacs_value text = render_text(env, buffer, length, &lastCell); + insert(env, text); + length = 0; + } + + lastCell = cell; + if (cell.chars[0] == 0) { + buffer[length] = ' '; length++; + } else { + unsigned char bytes[4]; + size_t count = codepoint_to_utf8(cell.chars[0], bytes); + for (int k = 0; k < count; k++) { + buffer[length] = bytes[k]; + length++; + } } - } - if (cell.width > 1) { - int w = cell.width - 1; - j = j + w; + if (cell.width > 1) { + int w = cell.width - 1; + offset += w; + j = j + w; + } } + + buffer[length] = '\n'; + length++; } - if (length > 0) { - emacs_value text = render_text(env, ptr, length, &lastCell); - insert(env, text); - ptr += length; - } - if (append_newline) { - *ptr = '\n'; - ptr += 1; - insert(env, env->make_string(env, "\n", 1)); - } - *ptr = 0; - return ptr - term->textbuf; + emacs_value text = render_text(env, buffer, length, &lastCell); + insert(env, text); + + return; } // Refresh the screen (visible part of the buffer when the terminal is @@ -197,33 +196,26 @@ static size_t refresh_row(Term *term, emacs_env *env, int row, int end_col, static void refresh_screen(Term *term, emacs_env *env) { int height; int width; + + /* if (term->invalid_end < term->invalid_start) { */ + /* goto end; */ + /* } */ + vterm_get_size(term->vt, &height, &width); - // Term height may have decreased before `invalid_end` reflects it. /* refresh full screen now */ /* TODO: only refresh invalid lines */ term->invalid_start = 0; term->invalid_end = height; + // Term height may have decreased before `invalid_end` reflects it. int line_start = row_to_linenr(term, term->invalid_start); - goto_line(env, line_start - 1); - int liner; - int r; - for (r = term->invalid_start; r < term->invalid_end; r++) { - liner = row_to_linenr(term, r); - int buffer_lnum = env->extract_integer(env, buffer_line_number(env)); - if (liner > buffer_lnum) { - goto_line(env, buffer_lnum); /* maybe should goto end of buffer */ - int i; - for (i = 0; i < liner - buffer_lnum; i++) { - insert(env, env->make_string(env, "\n", 1)); - } - } - delete_lines(env, liner, 1, false); - goto_line(env, liner); - refresh_row(term, env, r, width, false); - } - term->invalid_start = INT_MAX; - term->invalid_end = -1; + goto_line(env, line_start); + delete_lines(env, line_start, term->invalid_end - term->invalid_start, true); + refresh_lines(term, env, term->invalid_start, term->invalid_end, width); + + /* end: */ + /* term->invalid_start = INT_MAX; */ + /* term->invalid_end = -1; */ } // Refresh the scrollback of an invalidated terminal. @@ -232,31 +224,30 @@ static void refresh_scrollback(Term *term, emacs_env *env) { int buffer_lnum; vterm_get_size(term->vt, &height, &width); - while (term->sb_pending > 0) { + if (term->sb_pending > 0) { // This means that either the window height has decreased or the screen // became full and libvterm had to push all rows up. Convert the first // pending scrollback row into a string and append it just above the visible // section of the buffer buffer_lnum = env->extract_integer(env, buffer_line_number(env)); - if ((buffer_lnum - height) >= (int)term->sb_size) { - // scrollback full, delete lines at the top - delete_lines(env, 1, 1, true); - /* insert(env,env->make_string(env, "\n", 1)); */ + int del_cnt = buffer_lnum - height - (int)term->sb_size + term->sb_pending; + if (del_cnt > 0) { + delete_lines(env, 1, del_cnt, true); + buffer_lnum = env->extract_integer(env, buffer_line_number(env)); } - buffer_lnum = env->extract_integer(env, buffer_line_number(env)); int buf_index = buffer_lnum - height + 1; goto_line(env, buf_index); - size_t length = refresh_row(term, env, -term->sb_pending, width, true); - term->sb_pending--; + refresh_lines(term, env, -term->sb_pending, 0, width); + term->sb_pending = 0; } - // Remove extra lines at the bottom int max_line_count = (int)term->sb_current + height; buffer_lnum = env->extract_integer(env, buffer_line_number(env)); // Remove extra lines at the bottom if (buffer_lnum > max_line_count) { - delete_lines(env, max_line_count, buffer_lnum - max_line_count, true); + delete_lines(env, max_line_count + 1, buffer_lnum - max_line_count + 1, + true); } } @@ -274,7 +265,6 @@ static void adjust_topline(Term *term, emacs_env *env, long added) { size_t offset = get_col_offset(term, pos.row, pos.col); forward_char(env, env->make_integer(env, pos.col - offset)); - bool following = buffer_lnum == cursor_lnum + added; // cursor at end? emacs_value window = get_buffer_window(env); @@ -283,7 +273,8 @@ static void adjust_topline(Term *term, emacs_env *env, long added) { if (swindow == window) { if (following) { // "Follow" the terminal output - recenter(env, env->make_integer(env, -1)); /* make current line at the screen bottom */ + recenter(env, env->make_integer( + env, -1)); /* make current line at the screen bottom */ } else { recenter(env, env->make_integer(env, pos.row)); } @@ -291,12 +282,18 @@ static void adjust_topline(Term *term, emacs_env *env, long added) { } static void term_redraw(Term *term, emacs_env *env) { - long ml_before = env->extract_integer(env, buffer_line_number(env)); + /* if (term->is_invalidated) { */ + toggle_cursor_blinking(env, term->cursor_blinking); + toggle_cursor(env, term->cursor_visible); + long bufline_before = env->extract_integer(env, buffer_line_number(env)); + /* refresh_size(term, env); */ refresh_scrollback(term, env); refresh_screen(term, env); - long ml_added = - env->extract_integer(env, buffer_line_number(env)) - ml_before; - adjust_topline(term, env, ml_added); + long line_added = + env->extract_integer(env, buffer_line_number(env)) - bufline_before; + adjust_topline(term, env, line_added); + /* } */ + /* term->is_invalidated = false; */ } static VTermScreenCallbacks vterm_screen_callbacks = { @@ -538,7 +535,7 @@ static emacs_value Fvterm_new(emacs_env *env, ptrdiff_t nargs, term->sb_pending = 0; term->sb_buffer = malloc(sizeof(ScrollbackLine *) * term->sb_size); term->invalid_start = 0; - term->invalid_end = cols; + term->invalid_end = rows; return env->make_user_ptr(env, term_finalize, term); } @@ -547,9 +544,6 @@ static emacs_value Fvterm_update(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) { Term *term = env->get_user_ptr(env, args[0]); - toggle_cursor_blinking(env, term->cursor_blinking); - toggle_cursor(env, term->cursor_visible); - // Process keys if (nargs > 1) { ptrdiff_t len = string_bytes(env, args[1]); @@ -643,12 +637,13 @@ int emacs_module_init(struct emacs_runtime *ert) { Fdelete_lines = env->make_global_ref(env, env->intern(env, "vterm--delete-lines")); Frecenter = env->make_global_ref(env, env->intern(env, "recenter")); - Fforward_char = - env->make_global_ref(env, env->intern(env, "forward-char")); + Fforward_char = env->make_global_ref(env, env->intern(env, "forward-char")); Fblink_cursor_mode = env->make_global_ref(env, env->intern(env, "blink-cursor-mode")); - Fget_buffer_window = env->make_global_ref(env, env->intern(env, "get-buffer-window")); - Fselected_window = env->make_global_ref(env, env->intern(env, "selected-window")); + Fget_buffer_window = + env->make_global_ref(env, env->intern(env, "get-buffer-window")); + Fselected_window = + env->make_global_ref(env, env->intern(env, "selected-window")); // Faces Qterm = env->make_global_ref(env, env->intern(env, "vterm")); diff --git a/vterm-module.h b/vterm-module.h index 3e96da6..2754e4a 100644 --- a/vterm-module.h +++ b/vterm-module.h @@ -28,7 +28,6 @@ typedef struct Term { // buffer used to: // - convert VTermScreen cell arrays into utf8 strings // - receive data from libvterm as a result of key presses. - char textbuf[0x1fff]; ScrollbackLine **sb_buffer; // Scrollback buffer storage for libvterm size_t sb_current; // number of rows pushed to sb_buffer size_t sb_size; // sb_buffer size diff --git a/vterm.el b/vterm.el index 0e2cb96..7d5256e 100644 --- a/vterm.el +++ b/vterm.el @@ -184,10 +184,10 @@ be send to the terminal." "I/O Event. Feeds PROCESS's INPUT to the virtual terminal. Then triggers a redraw from the module." - (with-current-buffer (process-buffer process) - (vterm--write-input vterm--term input) - (let ((inhibit-read-only t) - (inhibit-redisplay t)) + (let ((inhibit-redisplay t) + (inhibit-read-only t)) + (with-current-buffer (process-buffer process) + (vterm--write-input vterm--term input) (vterm--update vterm--term)))) (defun vterm--window-size-change (frame) @@ -215,23 +215,10 @@ Feeds the size change to the virtual terminal." then this command kills the whole line including its terminating newline" (save-excursion (when (vterm--goto-line line-num) - (delete-region (point) (point-at-eol)) - (when delete-whole-line - (when (looking-at "\n") - (delete-char 1)) - (when (and (eobp) (looking-back "\n")) - (delete-char -1)) - - ) - (cl-loop repeat (1- count) do - (when (or delete-whole-line (forward-line 1)) - (delete-region (point) (point-at-eol)) - (when delete-whole-line - (when (looking-at "\n") - (delete-char 1)) - (when (and (eobp) (looking-back "\n")) - (delete-char -1))) - ))))) + (delete-region (point) (point-at-eol count)) + (when (and delete-whole-line + (looking-at "\n")) + (delete-char 1))))) (defun vterm--goto-line(n) "If move succ return t" -- cgit v1.0 From aabbe279baa722c6c115f1ebccb7383d3561979d Mon Sep 17 00:00:00 2001 From: jixiufeng Date: Sat, 10 Nov 2018 19:28:10 +0800 Subject: only refresh invalid lines --- vterm-module.c | 95 ++++++++++++++++++++++++++++++++++++++++++---------------- vterm-module.h | 6 ++++ 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/vterm-module.c b/vterm-module.c index f44f3c7..a16ea2c 100644 --- a/vterm-module.c +++ b/vterm-module.c @@ -197,15 +197,11 @@ static void refresh_screen(Term *term, emacs_env *env) { int height; int width; - /* if (term->invalid_end < term->invalid_start) { */ - /* goto end; */ - /* } */ + if (term->invalid_end < term->invalid_start) { + goto end; + } vterm_get_size(term->vt, &height, &width); - /* refresh full screen now */ - /* TODO: only refresh invalid lines */ - term->invalid_start = 0; - term->invalid_end = height; // Term height may have decreased before `invalid_end` reflects it. int line_start = row_to_linenr(term, term->invalid_start); @@ -213,9 +209,21 @@ static void refresh_screen(Term *term, emacs_env *env) { delete_lines(env, line_start, term->invalid_end - term->invalid_start, true); refresh_lines(term, env, term->invalid_start, term->invalid_end, width); - /* end: */ - /* term->invalid_start = INT_MAX; */ - /* term->invalid_end = -1; */ +end: + term->invalid_start = INT_MAX; + term->invalid_end = -1; +} + +static void refresh_size(Term *term, emacs_env *env) { + if (!term->pending_resize) { + return; + } + + term->pending_resize = false; + int width, height; + vterm_get_size(term->vt, &height, &width); + term->invalid_start = 0; + term->invalid_end = height; } // Refresh the scrollback of an invalidated terminal. @@ -281,25 +289,55 @@ static void adjust_topline(Term *term, emacs_env *env, long added) { } } +static void invalidate_terminal(Term *term, int start_row, int end_row) { + if (start_row != -1 && end_row != -1) { + term->invalid_start = MIN(term->invalid_start, start_row); + term->invalid_end = MAX(term->invalid_end, end_row); + } + term->is_invalidated = true; +} + +static int term_damage(VTermRect rect, void *data) { + invalidate_terminal(data, rect.start_row, rect.end_row); + return 1; +} + +static int term_moverect(VTermRect dest, VTermRect src, void *data) { + invalidate_terminal(data, MIN(dest.start_row, src.start_row), + MAX(dest.end_row, src.end_row)); + return 1; +} + +static int term_movecursor(VTermPos new, VTermPos old, int visible, + void *data) { + Term *term = data; + term->cursor.row = new.row; + term->cursor.col = new.col; + invalidate_terminal(term, old.row, old.row + 1); + invalidate_terminal(term, new.row, new.row + 1); + + return 1; +} + static void term_redraw(Term *term, emacs_env *env) { - /* if (term->is_invalidated) { */ - toggle_cursor_blinking(env, term->cursor_blinking); - toggle_cursor(env, term->cursor_visible); - long bufline_before = env->extract_integer(env, buffer_line_number(env)); - /* refresh_size(term, env); */ - refresh_scrollback(term, env); - refresh_screen(term, env); - long line_added = - env->extract_integer(env, buffer_line_number(env)) - bufline_before; - adjust_topline(term, env, line_added); - /* } */ - /* term->is_invalidated = false; */ + if (term->is_invalidated) { + toggle_cursor_blinking(env, term->cursor_blinking); + toggle_cursor(env, term->cursor_visible); + long bufline_before = env->extract_integer(env, buffer_line_number(env)); + /* refresh_size(term, env); */ + refresh_scrollback(term, env); + refresh_screen(term, env); + long line_added = + env->extract_integer(env, buffer_line_number(env)) - bufline_before; + adjust_topline(term, env, line_added); + } + term->is_invalidated = false; } static VTermScreenCallbacks vterm_screen_callbacks = { - /* .damage = term_damage, */ - /* .moverect = term_moverect, */ - /* .movecursor = term_movecursor, */ + .damage = term_damage, + .moverect = term_moverect, + .movecursor = term_movecursor, .settermprop = term_settermprop, /* .bell = term_bell, */ .sb_pushline = term_sb_push, @@ -331,6 +369,8 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *user_data) { Term *term = (Term *)user_data; switch (prop) { case VTERM_PROP_CURSORVISIBLE: + invalidate_terminal(term, term->cursor.row, term->cursor.row + 1); + term->cursor_visible = val->boolean; break; case VTERM_PROP_CURSORBLINK: @@ -578,6 +618,7 @@ static emacs_value Fvterm_write_input(emacs_env *env, ptrdiff_t nargs, env->copy_string_contents(env, args[1], bytes, &len); vterm_input_write(term->vt, bytes, len); + vterm_screen_flush_damage(term->vts); return env->make_integer(env, 0); } @@ -593,6 +634,10 @@ static emacs_value Fvterm_set_size(emacs_env *env, ptrdiff_t nargs, if (cols != old_cols || rows != old_rows) { vterm_set_size(term->vt, rows, cols); + vterm_screen_flush_damage(term->vts); + term->pending_resize = true; + invalidate_terminal(term, -1, -1); + term_redraw(term, env); } diff --git a/vterm-module.h b/vterm-module.h index 2754e4a..3e636c7 100644 --- a/vterm-module.h +++ b/vterm-module.h @@ -38,6 +38,12 @@ typedef struct Term { int sb_pending; int invalid_start, invalid_end; // invalid rows in libvterm screen + bool pending_resize; // pending width/height + bool is_invalidated; + + struct { + int row, col; + } cursor; // Flag to indicate cursor is visible bool cursor_visible; -- cgit v1.0 From 3e4c65ad13f6f84219c5d0a5afaa7c186cd9e0b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrmetz?= Date: Sun, 11 Nov 2018 22:18:00 +0100 Subject: Replace goto with a if --- vterm-module.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/vterm-module.c b/vterm-module.c index a16ea2c..9274a06 100644 --- a/vterm-module.c +++ b/vterm-module.c @@ -197,19 +197,16 @@ static void refresh_screen(Term *term, emacs_env *env) { int height; int width; - if (term->invalid_end < term->invalid_start) { - goto end; + if (term->invalid_end >= term->invalid_start) { + vterm_get_size(term->vt, &height, &width); + + // Term height may have decreased before `invalid_end` reflects it. + int line_start = row_to_linenr(term, term->invalid_start); + goto_line(env, line_start); + delete_lines(env, line_start, term->invalid_end - term->invalid_start, true); + refresh_lines(term, env, term->invalid_start, term->invalid_end, width); } - vterm_get_size(term->vt, &height, &width); - - // Term height may have decreased before `invalid_end` reflects it. - int line_start = row_to_linenr(term, term->invalid_start); - goto_line(env, line_start); - delete_lines(env, line_start, term->invalid_end - term->invalid_start, true); - refresh_lines(term, env, term->invalid_start, term->invalid_end, width); - -end: term->invalid_start = INT_MAX; term->invalid_end = -1; } -- cgit v1.0 From ff9290905d5ea4dfe1381e0662bbe8bc88203f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrmetz?= Date: Sun, 11 Nov 2018 22:19:43 +0100 Subject: Refactor Cursor --- vterm-module.c | 10 +++++----- vterm-module.h | 15 +++++++-------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/vterm-module.c b/vterm-module.c index 9274a06..1eed2f1 100644 --- a/vterm-module.c +++ b/vterm-module.c @@ -318,8 +318,8 @@ static int term_movecursor(VTermPos new, VTermPos old, int visible, static void term_redraw(Term *term, emacs_env *env) { if (term->is_invalidated) { - toggle_cursor_blinking(env, term->cursor_blinking); - toggle_cursor(env, term->cursor_visible); + toggle_cursor_blinking(env, term->cursor.blinking); + toggle_cursor(env, term->cursor.visible); long bufline_before = env->extract_integer(env, buffer_line_number(env)); /* refresh_size(term, env); */ refresh_scrollback(term, env); @@ -368,10 +368,10 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *user_data) { case VTERM_PROP_CURSORVISIBLE: invalidate_terminal(term, term->cursor.row, term->cursor.row + 1); - term->cursor_visible = val->boolean; + term->cursor.visible = val->boolean; break; case VTERM_PROP_CURSORBLINK: - term->cursor_blinking = val->boolean; + term->cursor.blinking = val->boolean; default: return 0; } @@ -630,9 +630,9 @@ static emacs_value Fvterm_set_size(emacs_env *env, ptrdiff_t nargs, vterm_get_size(term->vt, &old_rows, &old_cols); if (cols != old_cols || rows != old_rows) { + term->pending_resize = true; vterm_set_size(term->vt, rows, cols); vterm_screen_flush_damage(term->vts); - term->pending_resize = true; invalidate_terminal(term, -1, -1); term_redraw(term, env); diff --git a/vterm-module.h b/vterm-module.h index 3e636c7..dc8f778 100644 --- a/vterm-module.h +++ b/vterm-module.h @@ -22,6 +22,12 @@ static bool refresh_pending = false; #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif +typedef struct Cursor { + int row, col; + bool blinking; + bool visible; +} Cursor; + typedef struct Term { VTerm *vt; VTermScreen *vts; @@ -41,14 +47,7 @@ typedef struct Term { bool pending_resize; // pending width/height bool is_invalidated; - struct { - int row, col; - } cursor; - - // Flag to indicate cursor is visible - bool cursor_visible; - // Flag to indicate cursor is blinking - bool cursor_blinking; + Cursor cursor; } Term; // Faces -- cgit v1.0