mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge pull request #9193 from bfredl/scrollstuff
UI/TUI: improvements and cleanups for scrolling and clearing
This commit is contained in:
		| @@ -325,14 +325,14 @@ numerical highlight `id`:s to the actual attributes. | ||||
| 		+-------------------------+ src_top    | | ||||
| 		| src (moved up) and dst  |            | | ||||
| 		|-------------------------| dst_bot    | | ||||
| 		| src (cleared)           |            | | ||||
| 		| src (invalid)           |            | | ||||
| 		+=========================+ src_bot | ||||
| < | ||||
| 	If `rows` is less than zero, move a rectangle in the SR down, this can | ||||
| 	happen while scrolling up. | ||||
| > | ||||
| 		+=========================+ src_top | ||||
| 		| src (cleared)           |            | | ||||
| 		| src (invalid)           |            | | ||||
| 		|------------------------ | dst_top    | | ||||
| 		| src (moved down) and dst|            | | ||||
| 		+-------------------------+ src_bot    | | ||||
| @@ -348,6 +348,10 @@ numerical highlight `id`:s to the actual attributes. | ||||
| 	end-exclusive, which is consistent with API conventions, but different | ||||
| 	from `set_scroll_region` which was end-inclusive. | ||||
|  | ||||
| 	The scrolled-in area will be filled using |ui-event-grid_line| directly | ||||
| 	after the scroll event. The UI thus doesn't need to clear this area as | ||||
| 	part of handling the scroll event. | ||||
|  | ||||
| ============================================================================== | ||||
| Legacy Grid Events (cell based)					   *ui-grid-old* | ||||
|  | ||||
|   | ||||
| @@ -37,17 +37,6 @@ typedef struct attr_entry { | ||||
|   .cterm_bg_color = 0, \ | ||||
| } | ||||
|  | ||||
| // sentinel value that compares unequal to any valid highlight | ||||
| #define HLATTRS_INVALID (HlAttrs) { \ | ||||
|   .rgb_ae_attr = -1, \ | ||||
|   .cterm_ae_attr = -1, \ | ||||
|   .rgb_fg_color = -1, \ | ||||
|   .rgb_bg_color = -1, \ | ||||
|   .rgb_sp_color = -1, \ | ||||
|   .cterm_fg_color = 0, \ | ||||
|   .cterm_bg_color = 0, \ | ||||
| } | ||||
|  | ||||
| /// Values for index in highlight_attr[]. | ||||
| /// When making changes, also update hlf_names below! | ||||
| typedef enum { | ||||
|   | ||||
| @@ -1896,6 +1896,9 @@ static void msg_scroll_up(void) | ||||
|   } else { | ||||
|     screen_del_lines(0, 1, (int)Rows, 0, Columns); | ||||
|   } | ||||
|   // TODO(bfredl): when msgsep display is properly batched, this fill should be | ||||
|   // eliminated. | ||||
|   screen_fill(Rows-1, Rows, 0, (int)Columns, ' ', ' ', 0); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -2311,6 +2314,7 @@ static int do_more_prompt(int typed_char) | ||||
|  | ||||
|           if (toscroll == -1 | ||||
|               && screen_ins_lines(0, 1, (int)Rows, 0, (int)Columns) == OK) { | ||||
|             screen_fill(0, 1, 0, (int)Columns, ' ', ' ', 0); | ||||
|             // display line at top | ||||
|             (void)disp_sb_line(0, mp); | ||||
|           } else { | ||||
|   | ||||
| @@ -918,9 +918,9 @@ void curs_columns( | ||||
|  | ||||
|     extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width; | ||||
|     if (extra > 0) { | ||||
|       win_ins_lines(curwin, 0, extra, false); | ||||
|       win_ins_lines(curwin, 0, extra); | ||||
|     } else if (extra < 0) { | ||||
|       win_del_lines(curwin, 0, -extra, false); | ||||
|       win_del_lines(curwin, 0, -extra); | ||||
|     } | ||||
|   } else { | ||||
|     curwin->w_skipcol = 0; | ||||
|   | ||||
| @@ -888,10 +888,7 @@ static void win_update(win_T *wp) | ||||
|           // Try to insert the correct number of lines. | ||||
|           // If not the last window, delete the lines at the bottom. | ||||
|           // win_ins_lines may fail when the terminal can't do it. | ||||
|           if (i > 0) { | ||||
|             check_for_delay(false); | ||||
|           } | ||||
|           if (win_ins_lines(wp, 0, i, false) == OK) { | ||||
|           if (win_ins_lines(wp, 0, i) == OK) { | ||||
|             if (wp->w_lines_valid != 0) { | ||||
|               /* Need to update rows that are new, stop at the | ||||
|                * first one that scrolled down. */ | ||||
| @@ -949,8 +946,7 @@ static void win_update(win_T *wp) | ||||
|         /* ... but don't delete new filler lines. */ | ||||
|         row -= wp->w_topfill; | ||||
|         if (row > 0) { | ||||
|           check_for_delay(false); | ||||
|           if (win_del_lines(wp, 0, row, false) == OK) { | ||||
|           if (win_del_lines(wp, 0, row) == OK) { | ||||
|             bot_start = wp->w_height - row; | ||||
|           } else { | ||||
|             mid_start = 0;                      // redraw all lines | ||||
| @@ -1305,8 +1301,7 @@ static void win_update(win_T *wp) | ||||
|             if (row - xtra_rows >= wp->w_height - 2) { | ||||
|               mod_bot = MAXLNUM; | ||||
|             } else { | ||||
|               check_for_delay(false); | ||||
|               if (win_del_lines(wp, row, -xtra_rows, false) == FAIL) { | ||||
|               if (win_del_lines(wp, row, -xtra_rows) == FAIL) { | ||||
|                 mod_bot = MAXLNUM; | ||||
|               } else { | ||||
|                   bot_start = wp->w_height + xtra_rows; | ||||
| @@ -1319,8 +1314,7 @@ static void win_update(win_T *wp) | ||||
|             if (row + xtra_rows >= wp->w_height - 2) { | ||||
|               mod_bot = MAXLNUM; | ||||
|             } else { | ||||
|               check_for_delay(false); | ||||
|               if (win_ins_lines(wp, row + old_rows, xtra_rows, false) == FAIL) { | ||||
|               if (win_ins_lines(wp, row + old_rows, xtra_rows) == FAIL) { | ||||
|                 mod_bot = MAXLNUM; | ||||
|               } else if (top_end > row + old_rows) { | ||||
|                 // Scrolled the part at the top that requires | ||||
| @@ -1513,8 +1507,7 @@ static void win_update(win_T *wp) | ||||
|       wp->w_botline = lnum; | ||||
|     } | ||||
|   } else { | ||||
|     draw_vsep_win(wp, row); | ||||
|     if (eof) {                  /* we hit the end of the file */ | ||||
|     if (eof) {  // we hit the end of the file | ||||
|       wp->w_botline = buf->b_ml.ml_line_count + 1; | ||||
|       j = diff_check_fill(wp, wp->w_botline); | ||||
|       if (j > 0 && !wp->w_botfill) { | ||||
| @@ -1538,6 +1531,10 @@ static void win_update(win_T *wp) | ||||
|     win_draw_end(wp, fill_eob, ' ', row, wp->w_height, HLF_EOB); | ||||
|   } | ||||
|  | ||||
|   if (wp->w_redr_type >= REDRAW_TOP) { | ||||
|     draw_vsep_win(wp, 0); | ||||
|   } | ||||
|  | ||||
|   /* Reset the type of redrawing required, the window has been updated. */ | ||||
|   wp->w_redr_type = 0; | ||||
|   wp->w_old_topfill = wp->w_topfill; | ||||
| @@ -4262,7 +4259,6 @@ win_line ( | ||||
|           && filler_todo <= 0 | ||||
|           ) { | ||||
|         win_draw_end(wp, '@', ' ', row, wp->w_height, HLF_AT); | ||||
|         draw_vsep_win(wp, row); | ||||
|         row = endrow; | ||||
|       } | ||||
|  | ||||
| @@ -4348,7 +4344,6 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, | ||||
|   unsigned max_off_from; | ||||
|   unsigned max_off_to; | ||||
|   int col = 0; | ||||
|   int hl; | ||||
|   bool redraw_this;                         // Does character need redraw? | ||||
|   bool redraw_next;                         // redraw_this for next character | ||||
|   bool clear_next = false; | ||||
| @@ -4474,24 +4469,10 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (clear_width > 0) { | ||||
|     // For a window that's left of another, draw the separator char. | ||||
|     if (col + coloff < Columns && wp->w_vsep_width > 0) { | ||||
|       int c = fillchar_vsep(wp, &hl); | ||||
|       schar_T sc; | ||||
|       schar_from_char(sc, c); | ||||
|  | ||||
|       if (schar_cmp(ScreenLines[off_to], sc) | ||||
|           || ScreenAttrs[off_to] != hl) { | ||||
|         schar_copy(ScreenLines[off_to], sc); | ||||
|         ScreenAttrs[off_to] = hl; | ||||
|         if (start_dirty == -1) { | ||||
|           start_dirty = col; | ||||
|         } | ||||
|         end_dirty = col+1; | ||||
|       } | ||||
|     } else | ||||
|       LineWraps[row] = FALSE; | ||||
|   if (clear_width > 0 || wp->w_width != Columns) { | ||||
|     // If we cleared after the end of the line, it did not wrap. | ||||
|     // For vsplit, line wrapping is not possible. | ||||
|     LineWraps[row] = false; | ||||
|   } | ||||
|  | ||||
|   if (clear_end < end_dirty) { | ||||
| @@ -6071,10 +6052,10 @@ static void screenclear2(void) | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   /* blank out ScreenLines */ | ||||
|   for (i = 0; i < Rows; ++i) { | ||||
|     lineclear(LineOffset[i], (int)Columns); | ||||
|     LineWraps[i] = FALSE; | ||||
|   // blank out ScreenLines | ||||
|   for (i = 0; i < Rows; i++) { | ||||
|     lineclear(LineOffset[i], (int)Columns, true); | ||||
|     LineWraps[i] = false; | ||||
|   } | ||||
|  | ||||
|   ui_call_grid_clear(1);  // clear the display | ||||
| @@ -6098,12 +6079,13 @@ static void screenclear2(void) | ||||
| /* | ||||
|  * Clear one line in ScreenLines. | ||||
|  */ | ||||
| static void lineclear(unsigned off, int width) | ||||
| static void lineclear(unsigned off, int width, bool valid) | ||||
| { | ||||
|   for (int col = 0; col < width; col++) { | ||||
|     schar_from_ascii(ScreenLines[off + col], ' '); | ||||
|   } | ||||
|   (void)memset(ScreenAttrs + off, 0, (size_t)width * sizeof(sattr_T)); | ||||
|   int fill = valid ? 0 : -1; | ||||
|   (void)memset(ScreenAttrs + off, fill, (size_t)width * sizeof(sattr_T)); | ||||
| } | ||||
|  | ||||
| /// Copy part of a Screenline for vertically split window. | ||||
| @@ -6139,53 +6121,36 @@ void setcursor(void) | ||||
| } | ||||
|  | ||||
| /// Insert 'line_count' lines at 'row' in window 'wp'. | ||||
| /// If 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated. | ||||
| /// If 'mayclear' is TRUE the screen will be cleared if it is faster than | ||||
| /// scrolling. | ||||
| /// Returns FAIL if the lines are not inserted, OK for success. | ||||
| int win_ins_lines(win_T *wp, int row, int line_count, int invalid) | ||||
| int win_ins_lines(win_T *wp, int row, int line_count) | ||||
| { | ||||
|   if (wp->w_height < 5) { | ||||
|     return FAIL; | ||||
|   } | ||||
|  | ||||
|   return win_do_lines(wp, row, line_count, invalid, false); | ||||
|   return win_do_lines(wp, row, line_count, false); | ||||
| } | ||||
|  | ||||
| /// Delete "line_count" window lines at "row" in window "wp". | ||||
| /// If "invalid" is TRUE curwin->w_lines[] is invalidated. | ||||
| /// If "mayclear" is TRUE the screen will be cleared if it is faster than | ||||
| /// scrolling | ||||
| /// Return OK for success, FAIL if the lines are not deleted. | ||||
| int win_del_lines(win_T *wp, int row, int line_count, int invalid) | ||||
| int win_del_lines(win_T *wp, int row, int line_count) | ||||
| { | ||||
|   return win_do_lines(wp, row, line_count, invalid, true); | ||||
|   return win_do_lines(wp, row, line_count, true); | ||||
| } | ||||
|  | ||||
| // Common code for win_ins_lines() and win_del_lines(). | ||||
| // Returns OK or FAIL when the work has been done. | ||||
| static int win_do_lines(win_T *wp, int row, int line_count, | ||||
|                         int invalid, int del) | ||||
| static int win_do_lines(win_T *wp, int row, int line_count, int del) | ||||
| { | ||||
|   if (invalid) { | ||||
|     wp->w_lines_valid = 0; | ||||
|   } | ||||
|  | ||||
|   if (!redrawing() || line_count <= 0) { | ||||
|     return FAIL; | ||||
|   } | ||||
|  | ||||
|   // Delete all remaining lines | ||||
|   // No lines are being moved, just draw over the entire area | ||||
|   if (row + line_count >= wp->w_height) { | ||||
|     screen_fill(wp->w_winrow + row, wp->w_winrow + wp->w_height, | ||||
|                 wp->w_wincol, W_ENDCOL(wp), | ||||
|                 ' ', ' ', 0); | ||||
|     return OK; | ||||
|   } | ||||
|  | ||||
|   // when scrolling, the message on the command line should be cleared, | ||||
|   // otherwise it will stay there forever. | ||||
|   clear_cmdline = TRUE; | ||||
|   check_for_delay(false); | ||||
|   clear_cmdline = true; | ||||
|   int retval; | ||||
|  | ||||
|   if (del) { | ||||
| @@ -6237,7 +6202,7 @@ int screen_ins_lines(int row, int line_count, int end, int col, int width) | ||||
|         linecopy(j + line_count, j, col, width); | ||||
|       } | ||||
|       j += line_count; | ||||
|       lineclear(LineOffset[j] + col, width); | ||||
|       lineclear(LineOffset[j] + col, width, false); | ||||
|       LineWraps[j] = false; | ||||
|     } else { | ||||
|       j = end - 1 - i; | ||||
| @@ -6248,7 +6213,7 @@ int screen_ins_lines(int row, int line_count, int end, int col, int width) | ||||
|       } | ||||
|       LineOffset[j + line_count] = temp; | ||||
|       LineWraps[j + line_count] = false; | ||||
|       lineclear(temp, (int)Columns); | ||||
|       lineclear(temp, (int)Columns, false); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -6283,7 +6248,7 @@ int screen_del_lines(int row, int line_count, int end, int col, int width) | ||||
|         linecopy(j - line_count, j, col, width); | ||||
|       } | ||||
|       j -= line_count; | ||||
|       lineclear(LineOffset[j] + col, width); | ||||
|       lineclear(LineOffset[j] + col, width, false); | ||||
|       LineWraps[j] = false; | ||||
|     } else { | ||||
|       // whole width, moving the line pointers is faster | ||||
| @@ -6295,7 +6260,7 @@ int screen_del_lines(int row, int line_count, int end, int col, int width) | ||||
|       } | ||||
|       LineOffset[j - line_count] = temp; | ||||
|       LineWraps[j - line_count] = false; | ||||
|       lineclear(temp, (int)Columns); | ||||
|       lineclear(temp, (int)Columns, false); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -94,6 +94,7 @@ typedef struct { | ||||
|   bool can_change_scroll_region; | ||||
|   bool can_set_lr_margin; | ||||
|   bool can_set_left_right_margin; | ||||
|   bool can_erase_chars; | ||||
|   bool immediate_wrap_after_last_column; | ||||
|   bool bce; | ||||
|   bool mouse_enabled; | ||||
| @@ -103,7 +104,8 @@ typedef struct { | ||||
|   cursorentry_T cursor_shapes[SHAPE_IDX_COUNT]; | ||||
|   HlAttrs clear_attrs; | ||||
|   kvec_t(HlAttrs) attrs; | ||||
|   HlAttrs print_attrs; | ||||
|   int print_attr_id; | ||||
|   bool has_bg; | ||||
|   bool default_attr; | ||||
|   ModeShape showing_mode; | ||||
|   struct { | ||||
| @@ -119,6 +121,7 @@ typedef struct { | ||||
|     int set_cursor_style, reset_cursor_style; | ||||
|     int enter_undercurl_mode, exit_undercurl_mode, set_underline_color; | ||||
|   } unibi_ext; | ||||
|   char *space_buf; | ||||
| } TUIData; | ||||
|  | ||||
| static bool volatile got_winch = false; | ||||
| @@ -184,6 +187,7 @@ static void terminfo_start(UI *ui) | ||||
|   data->scroll_region_is_full_screen = true; | ||||
|   data->bufpos = 0; | ||||
|   data->default_attr = false; | ||||
|   data->has_bg = false; | ||||
|   data->is_invisible = true; | ||||
|   data->busy = false; | ||||
|   data->cork = false; | ||||
| @@ -239,6 +243,7 @@ static void terminfo_start(UI *ui) | ||||
|   data->can_set_left_right_margin = | ||||
|     !!unibi_get_str(data->ut, unibi_set_left_margin_parm) | ||||
|     && !!unibi_get_str(data->ut, unibi_set_right_margin_parm); | ||||
|   data->can_erase_chars = !!unibi_get_str(data->ut, unibi_erase_chars); | ||||
|   data->immediate_wrap_after_last_column = | ||||
|     terminfo_is_term_family(term, "cygwin") | ||||
|     || terminfo_is_term_family(term, "interix"); | ||||
| @@ -302,7 +307,7 @@ static void terminfo_stop(UI *ui) | ||||
| static void tui_terminal_start(UI *ui) | ||||
| { | ||||
|   TUIData *data = ui->data; | ||||
|   data->print_attrs = HLATTRS_INVALID; | ||||
|   data->print_attr_id = -1; | ||||
|   ugrid_init(&data->grid); | ||||
|   terminfo_start(ui); | ||||
|   update_size(ui); | ||||
| @@ -401,6 +406,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui) | ||||
|   loop_close(&tui_loop, false); | ||||
|   kv_destroy(data->invalid_regions); | ||||
|   kv_destroy(data->attrs); | ||||
|   xfree(data->space_buf); | ||||
|   xfree(data); | ||||
| } | ||||
|  | ||||
| @@ -435,8 +441,17 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) | ||||
|   ui_schedule_refresh(); | ||||
| } | ||||
|  | ||||
| static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb) | ||||
| static bool attrs_differ(UI *ui, int id1, int id2, bool rgb) | ||||
| { | ||||
|   TUIData *data = ui->data; | ||||
|   if (id1 == id2) { | ||||
|     return false; | ||||
|   } else if (id1 < 0 || id2 < 0) { | ||||
|     return true; | ||||
|   } | ||||
|   HlAttrs a1 = kv_A(data->attrs, (size_t)id1); | ||||
|   HlAttrs a2 = kv_A(data->attrs, (size_t)id2); | ||||
|  | ||||
|   if (rgb) { | ||||
|     return a1.rgb_fg_color != a2.rgb_fg_color | ||||
|       || a1.rgb_bg_color != a2.rgb_bg_color | ||||
| @@ -451,21 +466,16 @@ static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb) | ||||
|   } | ||||
| } | ||||
|  | ||||
| static bool no_bg(UI *ui, HlAttrs attrs) | ||||
| { | ||||
|   return  ui->rgb ? attrs.rgb_bg_color == -1 | ||||
|                   : attrs.cterm_bg_color == 0; | ||||
| } | ||||
|  | ||||
| static void update_attrs(UI *ui, HlAttrs attrs) | ||||
| static void update_attrs(UI *ui, int attr_id) | ||||
| { | ||||
|   TUIData *data = ui->data; | ||||
|  | ||||
|   if (!attrs_differ(attrs, data->print_attrs, ui->rgb)) { | ||||
|   if (!attrs_differ(ui, attr_id, data->print_attr_id, ui->rgb)) { | ||||
|     data->print_attr_id = attr_id; | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   data->print_attrs = attrs; | ||||
|   data->print_attr_id = attr_id; | ||||
|   HlAttrs attrs = kv_A(data->attrs, (size_t)attr_id); | ||||
|  | ||||
|   int fg = ui->rgb ? attrs.rgb_fg_color : (attrs.cterm_fg_color - 1); | ||||
|   if (fg == -1) { | ||||
| @@ -479,6 +489,8 @@ static void update_attrs(UI *ui, HlAttrs attrs) | ||||
|                  : (data->clear_attrs.cterm_bg_color - 1); | ||||
|   } | ||||
|  | ||||
|   data->has_bg = bg != -1; | ||||
|  | ||||
|   int attr = ui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr; | ||||
|   bool bold = attr & HL_BOLD; | ||||
|   bool italic = attr & HL_ITALIC; | ||||
| @@ -595,7 +607,7 @@ static void print_cell(UI *ui, UCell *ptr) | ||||
|     // Printing the next character finally advances the cursor. | ||||
|     final_column_wrap(ui); | ||||
|   } | ||||
|   update_attrs(ui, kv_A(data->attrs, ptr->attr)); | ||||
|   update_attrs(ui, ptr->attr); | ||||
|   out(ui, ptr->data, strlen(ptr->data)); | ||||
|   grid->col++; | ||||
|   if (data->immediate_wrap_after_last_column) { | ||||
| @@ -611,8 +623,8 @@ static bool cheap_to_print(UI *ui, int row, int col, int next) | ||||
|   UCell *cell = grid->cells[row] + col; | ||||
|   while (next) { | ||||
|     next--; | ||||
|     if (attrs_differ(kv_A(data->attrs, cell->attr), | ||||
|                      data->print_attrs, ui->rgb)) { | ||||
|     if (attrs_differ(ui, cell->attr, | ||||
|                      data->print_attr_id, ui->rgb)) { | ||||
|       if (data->default_attr) { | ||||
|         return false; | ||||
|       } | ||||
| @@ -662,7 +674,7 @@ static void cursor_goto(UI *ui, int row, int col) | ||||
|       int n = col - grid->col; | ||||
|       if (n <= (row == grid->row ? 4 : 2) | ||||
|           && cheap_to_print(ui, grid->row, grid->col, n)) { | ||||
|         UGRID_FOREACH_CELL(grid, grid->row, grid->row, grid->col, col - 1, { | ||||
|         UGRID_FOREACH_CELL(grid, grid->row, grid->col, col, { | ||||
|           print_cell(ui, cell); | ||||
|         }); | ||||
|       } | ||||
| @@ -734,50 +746,47 @@ safe_move: | ||||
| } | ||||
|  | ||||
| static void clear_region(UI *ui, int top, int bot, int left, int right, | ||||
|                          HlAttrs attrs) | ||||
|                          int attr_id) | ||||
| { | ||||
|   TUIData *data = ui->data; | ||||
|   UGrid *grid = &data->grid; | ||||
|  | ||||
|   bool cleared = false; | ||||
|   update_attrs(ui, attr_id); | ||||
|  | ||||
|   // non-BCE terminals can't clear with non-default background color | ||||
|   bool can_clear = data->bce || no_bg(ui, attrs); | ||||
|   bool can_clear = data->bce || !data->has_bg; | ||||
|  | ||||
|   if (can_clear && right == ui->width -1) { | ||||
|     // Background is set to the default color and the right edge matches the | ||||
|     // screen end, try to use terminal codes for clearing the requested area. | ||||
|     update_attrs(ui, attrs); | ||||
|     if (left == 0) { | ||||
|       if (bot == ui->height - 1) { | ||||
|         if (top == 0) { | ||||
|           unibi_out(ui, unibi_clear_screen); | ||||
|           ugrid_goto(&data->grid, top, left); | ||||
|         } else { | ||||
|           cursor_goto(ui, top, 0); | ||||
|           unibi_out(ui, unibi_clr_eos); | ||||
|         } | ||||
|         cleared = true; | ||||
|       } | ||||
|   // Background is set to the default color and the right edge matches the | ||||
|   // screen end, try to use terminal codes for clearing the requested area. | ||||
|   if (can_clear && left == 0 && right == ui->width && bot == ui->height) { | ||||
|     if (top == 0) { | ||||
|       unibi_out(ui, unibi_clear_screen); | ||||
|       ugrid_goto(&data->grid, top, left); | ||||
|     } else { | ||||
|       cursor_goto(ui, top, 0); | ||||
|       unibi_out(ui, unibi_clr_eos); | ||||
|     } | ||||
|   } else { | ||||
|     int width = right-left; | ||||
|  | ||||
|     if (!cleared) { | ||||
|       // iterate through each line and clear with clr_eol | ||||
|       for (int row = top; row <= bot; row++) { | ||||
|         cursor_goto(ui, row, left); | ||||
|     // iterate through each line and clear | ||||
|     for (int row = top; row < bot; row++) { | ||||
|       cursor_goto(ui, row, left); | ||||
|       if (can_clear && right == ui->width) { | ||||
|         unibi_out(ui, unibi_clr_eol); | ||||
|       } else if (data->can_erase_chars && can_clear && width >= 5) { | ||||
|         UNIBI_SET_NUM_VAR(data->params[0], width); | ||||
|         unibi_out(ui, unibi_erase_chars); | ||||
|       } else { | ||||
|         out(ui, data->space_buf, (size_t)width); | ||||
|         grid->col += width; | ||||
|         if (data->immediate_wrap_after_last_column) { | ||||
|           // Printing at the right margin immediately advances the cursor. | ||||
|           final_column_wrap(ui); | ||||
|         } | ||||
|       } | ||||
|       cleared = true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (!cleared) { | ||||
|     // could not clear using faster terminal codes, refresh the whole region | ||||
|     UGRID_FOREACH_CELL(grid, top, bot, left, right, { | ||||
|       cursor_goto(ui, row, col); | ||||
|       print_cell(ui, cell); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void set_scroll_region(UI *ui, int top, int bot, int left, int right) | ||||
| @@ -838,12 +847,16 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height) | ||||
|   UGrid *grid = &data->grid; | ||||
|   ugrid_resize(grid, (int)width, (int)height); | ||||
|  | ||||
|   xfree(data->space_buf); | ||||
|   data->space_buf = xmalloc((size_t)width * sizeof(*data->space_buf)); | ||||
|   memset(data->space_buf, ' ', (size_t)width); | ||||
|  | ||||
|   // resize might not always be followed by a clear before flush | ||||
|   // so clip the invalid region | ||||
|   for (size_t i = 0; i < kv_size(data->invalid_regions); i++) { | ||||
|     Rect *r = &kv_A(data->invalid_regions, i); | ||||
|     r->bot = MIN(r->bot, grid->height-1); | ||||
|     r->right = MIN(r->right, grid->width-1); | ||||
|     r->bot = MIN(r->bot, grid->height); | ||||
|     r->right = MIN(r->right, grid->width); | ||||
|   } | ||||
|  | ||||
|   if (!got_winch) {  // Try to resize the terminal window. | ||||
| @@ -866,8 +879,7 @@ static void tui_grid_clear(UI *ui, Integer g) | ||||
|   UGrid *grid = &data->grid; | ||||
|   ugrid_clear(grid); | ||||
|   kv_size(data->invalid_regions) = 0; | ||||
|   clear_region(ui, 0, grid->height-1, 0, grid->width-1, | ||||
|                data->clear_attrs); | ||||
|   clear_region(ui, 0, grid->height, 0, grid->width, 0); | ||||
| } | ||||
|  | ||||
| static void tui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) | ||||
| @@ -1025,9 +1037,7 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow, | ||||
|   data->scroll_region_is_full_screen = fullwidth | ||||
|         && top == 0 && bot == ui->height-1; | ||||
|  | ||||
|   int clear_top, clear_bot; | ||||
|   ugrid_scroll(grid, top, bot, left, right, (int)rows, | ||||
|                &clear_top, &clear_bot); | ||||
|   ugrid_scroll(grid, top, bot, left, right, (int)rows); | ||||
|  | ||||
|   bool can_scroll = data->scroll_region_is_full_screen | ||||
|                     || (data->can_change_scroll_region | ||||
| @@ -1041,8 +1051,6 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow, | ||||
|       set_scroll_region(ui, top, bot, left, right); | ||||
|     } | ||||
|     cursor_goto(ui, top, left); | ||||
|     // also set default color attributes or some terminals can become funny | ||||
|     update_attrs(ui, data->clear_attrs); | ||||
|  | ||||
|     if (rows > 0) { | ||||
|       if (rows == 1) { | ||||
| @@ -1064,16 +1072,14 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow, | ||||
|     if (!data->scroll_region_is_full_screen) { | ||||
|       reset_scroll_region(ui, fullwidth); | ||||
|     } | ||||
|  | ||||
|     if (!(data->bce || no_bg(ui, data->clear_attrs))) { | ||||
|       // Scrolling will leave wrong background in the cleared area on non-BCE | ||||
|       // terminals. Update the cleared area. | ||||
|       clear_region(ui, clear_top, clear_bot, left, right, | ||||
|                    data->clear_attrs); | ||||
|     } | ||||
|   } else { | ||||
|     // Mark the entire scroll region as invalid for redrawing later | ||||
|     invalidate(ui, top, bot, left, right); | ||||
|     // Mark the moved region as invalid for redrawing later | ||||
|     if (rows > 0) { | ||||
|       endrow = endrow - rows; | ||||
|     } else { | ||||
|       startrow = startrow - rows; | ||||
|     } | ||||
|     invalidate(ui, (int)startrow, (int)endrow, (int)startcol, (int)endcol); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -1106,8 +1112,8 @@ static void tui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, | ||||
|   data->clear_attrs.cterm_fg_color = (int)cterm_fg; | ||||
|   data->clear_attrs.cterm_bg_color = (int)cterm_bg; | ||||
|  | ||||
|   data->print_attrs = HLATTRS_INVALID; | ||||
|   invalidate(ui, 0, data->grid.height-1, 0, data->grid.width-1); | ||||
|   data->print_attr_id = -1; | ||||
|   invalidate(ui, 0, data->grid.height, 0, data->grid.width); | ||||
| } | ||||
|  | ||||
| static void tui_flush(UI *ui) | ||||
| @@ -1129,11 +1135,27 @@ static void tui_flush(UI *ui) | ||||
|  | ||||
|   while (kv_size(data->invalid_regions)) { | ||||
|     Rect r = kv_pop(data->invalid_regions); | ||||
|     assert(r.bot < grid->height && r.right < grid->width); | ||||
|     UGRID_FOREACH_CELL(grid, r.top, r.bot, r.left, r.right, { | ||||
|       cursor_goto(ui, row, col); | ||||
|       print_cell(ui, cell); | ||||
|     }); | ||||
|     assert(r.bot <= grid->height && r.right <= grid->width); | ||||
|  | ||||
|     for (int row = r.top; row < r.bot; row++) { | ||||
|       int clear_attr = grid->cells[row][r.right-1].attr; | ||||
|       int clear_col; | ||||
|       for (clear_col = r.right; clear_col > 0; clear_col--) { | ||||
|         UCell *cell = &grid->cells[row][clear_col-1]; | ||||
|         if (!(cell->data[0] == ' ' && cell->data[1] == NUL | ||||
|               && cell->attr == clear_attr)) { | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       UGRID_FOREACH_CELL(grid, row, r.left, clear_col, { | ||||
|         cursor_goto(ui, row, col); | ||||
|         print_cell(ui, cell); | ||||
|       }); | ||||
|       if (clear_col < r.right) { | ||||
|         clear_region(ui, row, row+1, clear_col, r.right, clear_attr); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   cursor_goto(ui, data->row, data->col); | ||||
| @@ -1218,8 +1240,8 @@ static void tui_option_set(UI *ui, String name, Object value) | ||||
|   if (strequal(name.data, "termguicolors")) { | ||||
|     ui->rgb = value.data.boolean; | ||||
|  | ||||
|     data->print_attrs = HLATTRS_INVALID; | ||||
|     invalidate(ui, 0, data->grid.height-1, 0, data->grid.width-1); | ||||
|     data->print_attr_id = -1; | ||||
|     invalidate(ui, 0, data->grid.height, 0, data->grid.width); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -1235,18 +1257,16 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, | ||||
|     assert((size_t)attrs[c-startcol] < kv_size(data->attrs)); | ||||
|     grid->cells[linerow][c].attr = attrs[c-startcol]; | ||||
|   } | ||||
|   UGRID_FOREACH_CELL(grid, (int)linerow, (int)linerow, (int)startcol, | ||||
|                      (int)endcol-1, { | ||||
|     cursor_goto(ui, row, col); | ||||
|   UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, { | ||||
|     cursor_goto(ui, (int)linerow, col); | ||||
|     print_cell(ui, cell); | ||||
|   }); | ||||
|  | ||||
|   if (clearcol > endcol) { | ||||
|     HlAttrs cl_attrs = kv_A(data->attrs, (size_t)clearattr); | ||||
|     ugrid_clear_chunk(grid, (int)linerow, (int)endcol, (int)clearcol, | ||||
|                       (sattr_T)clearattr); | ||||
|     clear_region(ui, (int)linerow, (int)linerow, (int)endcol, (int)clearcol-1, | ||||
|                  cl_attrs); | ||||
|     clear_region(ui, (int)linerow, (int)linerow+1, (int)endcol, (int)clearcol, | ||||
|                  (int)clearattr); | ||||
|   } | ||||
|  | ||||
|   if (wrap && ui->width == grid->width && linerow + 1 < grid->height) { | ||||
| @@ -1269,27 +1289,17 @@ static void invalidate(UI *ui, int top, int bot, int left, int right) | ||||
| { | ||||
|   TUIData *data = ui->data; | ||||
|   Rect *intersects = NULL; | ||||
|   // Increase dimensions before comparing to ensure adjacent regions are | ||||
|   // treated as intersecting | ||||
|   --top; | ||||
|   ++bot; | ||||
|   --left; | ||||
|   ++right; | ||||
|  | ||||
|   for (size_t i = 0; i < kv_size(data->invalid_regions); i++) { | ||||
|     Rect *r = &kv_A(data->invalid_regions, i); | ||||
|     if (!(top > r->bot || bot < r->top | ||||
|           || left > r->right || right < r->left)) { | ||||
|     // adjacent regions are treated as overlapping | ||||
|     if (!(top > r->bot || bot < r->top) | ||||
|         && !(left > r->right || right < r->left)) { | ||||
|       intersects = r; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ++top; | ||||
|   --bot; | ||||
|   ++left; | ||||
|   --right; | ||||
|  | ||||
|   if (intersects) { | ||||
|     // If top/bot/left/right intersects with a invalid rect, we replace it | ||||
|     // by the union | ||||
|   | ||||
| @@ -52,8 +52,7 @@ void ugrid_goto(UGrid *grid, int row, int col) | ||||
|   grid->col = col; | ||||
| } | ||||
|  | ||||
| void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, | ||||
|                   int count, int *clear_top, int *clear_bot) | ||||
| void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, int count) | ||||
| { | ||||
|   // Compute start/stop/step for the loop below | ||||
|   int start, stop, step; | ||||
| @@ -76,26 +75,18 @@ void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, | ||||
|     memcpy(target_row, source_row, | ||||
|            sizeof(UCell) * (size_t)(right - left + 1)); | ||||
|   } | ||||
|  | ||||
|   // clear cells in the emptied region, | ||||
|   if (count > 0) { | ||||
|     *clear_top = stop; | ||||
|     *clear_bot = stop + count - 1; | ||||
|   } else { | ||||
|     *clear_bot = stop; | ||||
|     *clear_top = stop + count + 1; | ||||
|   } | ||||
|   clear_region(grid, *clear_top, *clear_bot, left, right, 0); | ||||
| } | ||||
|  | ||||
| static void clear_region(UGrid *grid, int top, int bot, int left, int right, | ||||
|                          sattr_T attr) | ||||
| { | ||||
|   UGRID_FOREACH_CELL(grid, top, bot, left, right, { | ||||
|     cell->data[0] = ' '; | ||||
|     cell->data[1] = 0; | ||||
|     cell->attr = attr; | ||||
|   }); | ||||
|   for (int row = top; row <= bot; row++) { | ||||
|     UGRID_FOREACH_CELL(grid, row, left, right+1, { | ||||
|       cell->data[0] = ' '; | ||||
|       cell->data[1] = 0; | ||||
|       cell->attr = attr; | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void destroy_cells(UGrid *grid) | ||||
|   | ||||
| @@ -22,15 +22,13 @@ struct ugrid { | ||||
|  | ||||
| // -V:UGRID_FOREACH_CELL:625 | ||||
|  | ||||
| #define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \ | ||||
| #define UGRID_FOREACH_CELL(grid, row, startcol, endcol, code) \ | ||||
|   do { \ | ||||
|     for (int row = top; row <= bot; row++) { \ | ||||
|       UCell *row_cells = (grid)->cells[row]; \ | ||||
|       for (int col = left; col <= right; col++) { \ | ||||
|         UCell *cell = row_cells + col; \ | ||||
|         (void)(cell); \ | ||||
|         code; \ | ||||
|       } \ | ||||
|     UCell *row_cells = (grid)->cells[row]; \ | ||||
|     for (int col = startcol; col < endcol; col++) { \ | ||||
|       UCell *cell = row_cells + col; \ | ||||
|       (void)(cell); \ | ||||
|       code; \ | ||||
|     } \ | ||||
|   } while (0) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Björn Linse
					Björn Linse