Patch 8.1.1493
Problem: Redrawing with popups is slow and causes flicker.
Solution: Avoid clearing and redrawing using a zindex mask.
Files: src/globals.h, src/screen.c, src/proto/
screen.pro, src/popupwin.c,
src/popupmnu.c
*** ../vim-8.1.1492/src/globals.h 2019-06-02 18:40:02.637508840 +0200
--- src/globals.h 2019-06-08 14:56:46.164845189 +0200
***************
*** 70,75 ****
--- 70,90 ----
*/
EXTERN short *TabPageIdxs INIT(= NULL);
+ #ifdef FEAT_TEXT_PROP
+ // Array with size Rows x Columns containing zindex of popups.
+ EXTERN short *popup_mask INIT(= NULL);
+
+ // Flag set to TRUE when popup_mask needs to be updated.
+ EXTERN int popup_mask_refresh INIT(= TRUE);
+
+ // Tab that was used to fill popup_mask.
+ EXTERN tabpage_T *popup_mask_tab INIT(= NULL);
+
+ // Zindex in for screen_char(): if lower than the value in "popup_mask"
+ // drawing the character is skipped.
+ EXTERN int screen_zindex INIT(= 0);
+ #endif
+
EXTERN int screen_Rows INIT(= 0); /* actual size of ScreenLines[] */
EXTERN int screen_Columns INIT(= 0); /* actual size of ScreenLines[] */
*** ../vim-8.1.1492/src/screen.c 2019-06-03 22:53:27.453687723 +0200
--- src/screen.c 2019-06-08 15:51:14.982063054 +0200
***************
*** 610,624 ****
curwin->w_lines_valid = 0; /* don't use w_lines[].wl_size now */
return FAIL;
}
#ifdef FEAT_TEXT_PROP
! // TODO: avoid redrawing everything when there is a popup window.
! if (popup_any_visible())
! {
! if (type < NOT_VALID)
! type = NOT_VALID;
! FOR_ALL_WINDOWS(wp)
! wp->w_redr_type = NOT_VALID;
! }
#endif
updating_screen = TRUE;
--- 610,619 ----
curwin->w_lines_valid = 0; /* don't use w_lines[].wl_size now */
return FAIL;
}
+
#ifdef FEAT_TEXT_PROP
! // Update popup_mask if needed.
! type = may_update_popup_mask(type);
#endif
updating_screen = TRUE;
***************
*** 880,885 ****
--- 875,884 ----
#ifdef FEAT_SEARCH_EXTRA
start_search_hl();
#endif
+ #ifdef FEAT_TEXT_PROP
+ // Update popup_mask if needed.
+ may_update_popup_mask(0);
+ #endif
}
/*
***************
*** 992,1002 ****
win_redr_status(wp, FALSE);
}
- #ifdef FEAT_TEXT_PROP
- // Display popup windows on top of the others.
- update_popups();
- #endif
-
update_finish();
}
#endif
--- 991,996 ----
***************
*** 1021,1026 ****
--- 1015,1096 ----
#ifdef FEAT_TEXT_PROP
/*
+ * Update "popup_mask" if needed.
+ * Also recomputes the popup size and positions.
+ * Also updates "popup_visible".
+ * If more redrawing is needed than "type_arg" a higher value is returned.
+ */
+ int
+ may_update_popup_mask(int type_arg)
+ {
+ int type = type_arg;
+ win_T *wp;
+
+ if (popup_mask_tab != curtab)
+ popup_mask_refresh = TRUE;
+ if (!popup_mask_refresh)
+ {
+ // Check if any buffer has changed.
+ for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+ if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
+ popup_mask_refresh = TRUE;
+ for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
+ if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
+ popup_mask_refresh = TRUE;
+ if (!popup_mask_refresh)
+ return type;
+ }
+
+ popup_mask_refresh = FALSE;
+ popup_mask_tab = curtab;
+
+ popup_visible = FALSE;
+ vim_memset(popup_mask, 0, screen_Rows * screen_Columns * sizeof(short));
+
+ // Find the window with the lowest zindex that hasn't been handled yet,
+ // so that the window with a higher zindex overwrites the value in
+ // popup_mask.
+ popup_reset_handled();
+ while ((wp = find_next_popup(TRUE)) != NULL)
+ {
+ int top_off, bot_off;
+ int left_off, right_off;
+ short *p;
+ int line, col;
+
+ popup_visible = TRUE;
+
+ // Recompute the position if the text changed.
+ if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
+ popup_adjust_position(wp);
+
+ // the position and size are for the inside, add the padding and
+ // border
+ top_off = wp->w_popup_padding[0] + wp->w_popup_border[0];
+ bot_off = wp->w_popup_padding[2] + wp->w_popup_border[2];
+ left_off = wp->w_popup_padding[3] + wp->w_popup_border[3];
+ right_off = wp->w_popup_padding[1] + wp->w_popup_border[1];
+
+ for (line = wp->w_winrow + top_off;
+ line < wp->w_winrow + wp->w_height + bot_off
+ && line < screen_Rows; ++line)
+ for (col = wp->w_wincol + left_off;
+ col < wp->w_wincol + wp->w_width + right_off
+ && col < screen_Columns; ++col)
+ {
+ p = popup_mask + line * screen_Columns + col;
+ if (*p != wp->w_zindex)
+ {
+ *p = wp->w_zindex;
+ type = NOT_VALID;
+ }
+ }
+ }
+
+ return type;
+ }
+
+ /*
* Return a string of "len" spaces in IObuff.
*/
static char_u *
***************
*** 1049,1062 ****
// Find the window with the lowest zindex that hasn't been updated yet,
// so that the window with a higher zindex is drawn later, thus goes on
// top.
- // TODO: don't redraw every popup every time.
- popup_visible = FALSE;
popup_reset_handled();
while ((wp = find_next_popup(TRUE)) != NULL)
{
! // Recompute the position if the text changed.
! if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
! popup_adjust_position(wp);
// adjust w_winrow and w_wincol for border and padding, since
// win_update() doesn't handle them.
--- 1119,1131 ----
// Find the window with the lowest zindex that hasn't been updated yet,
// so that the window with a higher zindex is drawn later, thus goes on
// top.
popup_reset_handled();
while ((wp = find_next_popup(TRUE)) != NULL)
{
! // This drawing uses the zindex of the popup window, so that it's on
! // top of the text but doesn't draw when another popup with higher
! // zindex is on top of the character.
! screen_zindex = wp->w_zindex;
// adjust w_winrow and w_wincol for border and padding, since
// win_update() doesn't handle them.
***************
*** 1067,1073 ****
// Draw the popup text.
win_update(wp);
- popup_visible = TRUE;
wp->w_winrow -= top_off;
wp->w_wincol -= left_off;
--- 1136,1141 ----
***************
*** 1190,1195 ****
--- 1258,1266 ----
wp->w_wincol + total_width - 1, border_attr[2]);
}
}
+
+ // Back to the normal zindex.
+ screen_zindex = 0;
}
}
#endif
***************
*** 6477,6482 ****
--- 6548,6558 ----
redraw_this = TRUE;
}
#endif
+ #ifdef FEAT_TEXT_PROP
+ // Skip if under a(nother) popup.
+ if (popup_mask[row * screen_Columns + col + coloff] > screen_zindex)
+ redraw_this = FALSE;
+ #endif
if (redraw_this)
{
***************
*** 6721,6726 ****
--- 6797,6803 ----
if (clear_width > 0
#ifdef FEAT_TEXT_PROP
&& !(flags & SLF_POPUP) // no separator for popup window
+ && popup_mask[row * screen_Columns + col + coloff] <= screen_zindex
#endif
)
{
***************
*** 6821,6831 ****
win_redr_status(wp, FALSE);
if (redraw_tabline)
draw_tabline();
-
- #ifdef FEAT_TEXT_PROP
- // Display popup windows on top of the status lines.
- update_popups();
- #endif
}
#if defined(FEAT_WILDMENU) || defined(PROTO)
--- 6898,6903 ----
***************
*** 8577,8585 ****
return;
#ifdef FEAT_INS_EXPAND
! if (pum_under_menu(row, col))
return;
#endif
/* Outputting a character in the last cell on the screen may scroll the
* screen up. Only do it when the "xn" termcap property is set, otherwise
* mark the character invalid (update it when scrolled up). */
--- 8649,8669 ----
return;
#ifdef FEAT_INS_EXPAND
! // Skip if under the popup menu.
! // Popup windows with zindex higher than POPUPMENU_ZINDEX go on top.
! if (pum_under_menu(row, col)
! # ifdef FEAT_TEXT_PROP
! && screen_zindex <= POPUPMENU_ZINDEX
! # endif
! )
return;
#endif
+ #ifdef FEAT_TEXT_PROP
+ // Skip if under a(nother) popup.
+ if (popup_mask[row * screen_Columns + col] > screen_zindex)
+ return;
+ #endif
+
/* Outputting a character in the last cell on the screen may scroll the
* screen up. Only do it when the "xn" termcap property is set, otherwise
* mark the character invalid (update it when scrolled up). */
***************
*** 8869,8875 ****
c = c1;
for (col = start_col; col < end_col; ++col)
{
! if (ScreenLines[off] != c
|| (enc_utf8 && (int)ScreenLinesUC[off]
!= (c >= 0x80 ? c : 0))
|| ScreenAttrs[off] != attr
--- 8953,8959 ----
c = c1;
for (col = start_col; col < end_col; ++col)
{
! if ((ScreenLines[off] != c
|| (enc_utf8 && (int)ScreenLinesUC[off]
!= (c >= 0x80 ? c : 0))
|| ScreenAttrs[off] != attr
***************
*** 8877,8882 ****
--- 8961,8971 ----
|| force_next
#endif
)
+ #ifdef FEAT_TEXT_PROP
+ // Skip if under a(nother) popup.
+ && popup_mask[row * screen_Columns + col] <= screen_zindex
+ #endif
+ )
{
#if defined(FEAT_GUI) || defined(UNIX)
/* The bold trick may make a single row of pixels appear in
***************
*** 9013,9018 ****
--- 9102,9110 ----
unsigned *new_LineOffset;
char_u *new_LineWraps;
short *new_TabPageIdxs;
+ #ifdef FEAT_TEXT_PROP
+ short *new_popup_mask;
+ #endif
tabpage_T *tp;
static int entered = FALSE; /* avoid recursiveness */
static int done_outofmem_msg = FALSE; /* did outofmem message */
***************
*** 9094,9099 ****
--- 9186,9194 ----
new_LineOffset = LALLOC_MULT(unsigned, Rows);
new_LineWraps = LALLOC_MULT(char_u, Rows);
new_TabPageIdxs = LALLOC_MULT(short, Columns);
+ #ifdef FEAT_TEXT_PROP
+ new_popup_mask = LALLOC_MULT(short, Rows * Columns);
+ #endif
FOR_ALL_TAB_WINDOWS(tp, wp)
{
***************
*** 9136,9141 ****
--- 9231,9239 ----
|| new_LineOffset == NULL
|| new_LineWraps == NULL
|| new_TabPageIdxs == NULL
+ #ifdef FEAT_TEXT_PROP
+ || new_popup_mask == NULL
+ #endif
|| outofmem)
{
if (ScreenLines != NULL || !done_outofmem_msg)
***************
*** 9156,9161 ****
--- 9254,9262 ----
VIM_CLEAR(new_LineOffset);
VIM_CLEAR(new_LineWraps);
VIM_CLEAR(new_TabPageIdxs);
+ #ifdef FEAT_TEXT_PROP
+ VIM_CLEAR(new_popup_mask);
+ #endif
}
else
{
***************
*** 9242,9247 ****
--- 9343,9353 ----
LineOffset = new_LineOffset;
LineWraps = new_LineWraps;
TabPageIdxs = new_TabPageIdxs;
+ #ifdef FEAT_TEXT_PROP
+ popup_mask = new_popup_mask;
+ vim_memset(popup_mask, 0, screen_Rows * screen_Columns * sizeof(short));
+ popup_mask_refresh = TRUE;
+ #endif
/* It's important that screen_Rows and screen_Columns reflect the actual
* size of ScreenLines[]. Set them before calling anything. */
***************
*** 9296,9310 ****
{
int i;
! vim_free(ScreenLinesUC);
for (i = 0; i < Screen_mco; ++i)
! vim_free(ScreenLinesC[i]);
! vim_free(ScreenLines2);
! vim_free(ScreenLines);
! vim_free(ScreenAttrs);
! vim_free(LineOffset);
! vim_free(LineWraps);
! vim_free(TabPageIdxs);
}
void
--- 9402,9419 ----
{
int i;
! VIM_CLEAR(ScreenLinesUC);
for (i = 0; i < Screen_mco; ++i)
! VIM_CLEAR(ScreenLinesC[i]);
! VIM_CLEAR(ScreenLines2);
! VIM_CLEAR(ScreenLines);
! VIM_CLEAR(ScreenAttrs);
! VIM_CLEAR(LineOffset);
! VIM_CLEAR(LineWraps);
! VIM_CLEAR(TabPageIdxs);
! #ifdef FEAT_TEXT_PROP
! VIM_CLEAR(popup_mask);
! #endif
}
void
***************
*** 9429,9434 ****
--- 9538,9544 ----
/*
* Return TRUE if clearing with term string "p" would work.
* It can't work when the string is empty or it won't set the right background.
+ * Don't clear to end-of-line when there are popups, it may cause flicker.
*/
int
can_clear(char_u *p)
***************
*** 9443,9449 ****
#else
|| cterm_normal_bg_color == 0
#endif
! || *T_UT != NUL));
}
/*
--- 9553,9563 ----
#else
|| cterm_normal_bg_color == 0
#endif
! || *T_UT != NUL)
! #ifdef FEAT_TEXT_PROP
! && !(p == T_CE && popup_visible)
! #endif
! );
}
/*
***************
*** 9891,9912 ****
if (!redrawing() || line_count <= 0)
return FAIL;
! /* When inserting lines would result in loss of command output, just redraw
! * the lines. */
if (no_win_do_lines_ins && !del)
return FAIL;
! /* only a few lines left: redraw is faster */
if (mayclear && Rows - line_count < 5 && wp->w_width == Columns)
{
if (!no_win_do_lines_ins)
! screenclear(); /* will set wp->w_lines_valid to 0 */
return FAIL;
}
! /*
! * Delete all remaining lines
! */
if (row + line_count >= wp->w_height)
{
screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height,
--- 10005,10030 ----
if (!redrawing() || line_count <= 0)
return FAIL;
! // When inserting lines would result in loss of command output, just redraw
! // the lines.
if (no_win_do_lines_ins && !del)
return FAIL;
! // only a few lines left: redraw is faster
if (mayclear && Rows - line_count < 5 && wp->w_width == Columns)
{
if (!no_win_do_lines_ins)
! screenclear(); // will set wp->w_lines_valid to 0
return FAIL;
}
! #ifdef FEAT_TEXT_PROP
! // this doesn't work when tere are popups visible
! if (popup_visible)
! return FAIL;
! #endif
!
! // Delete all remaining lines
if (row + line_count >= wp->w_height)
{
screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height,
***************
*** 10024,10035 ****
* - the line count is less than one
* - the line count is more than 'ttyscroll'
* - redrawing for a callback and there is a modeless selection
*/
! if (!screen_valid(TRUE) || line_count <= 0 || line_count > p_ttyscroll
#ifdef FEAT_CLIPBOARD
|| (clip_star.state != SELECT_CLEARED
&& redrawing_for_callback > 0)
#endif
)
return FAIL;
--- 10142,10158 ----
* - the line count is less than one
* - the line count is more than 'ttyscroll'
* - redrawing for a callback and there is a modeless selection
+ * - there is a popup window
*/
! if (!screen_valid(TRUE)
! || line_count <= 0 || line_count > p_ttyscroll
#ifdef FEAT_CLIPBOARD
|| (clip_star.state != SELECT_CLEARED
&& redrawing_for_callback > 0)
#endif
+ #ifdef FEAT_TEXT_PROP
+ || popup_visible
+ #endif
)
return FAIL;
***************
*** 11136,11146 ****
/* Redraw the tab pages line if needed. */
if (redraw_tabline)
draw_tabline();
-
- #ifdef FEAT_TEXT_PROP
- // Display popup windows on top of everything.
- update_popups();
- #endif
}
#ifdef FEAT_CMDL_INFO
--- 11259,11264 ----
*** ../vim-8.1.1492/src/proto/
screen.pro 2019-05-29 22:28:25.763184805 +0200
--- src/proto/
screen.pro 2019-06-08 12:58:02.582485294 +0200
***************
*** 16,21 ****
--- 16,22 ----
int conceal_cursor_line(win_T *wp);
void conceal_check_cursor_line(void);
void update_debug_sign(buf_T *buf, linenr_T lnum);
+ int may_update_popup_mask(int type_arg);
void updateWindow(win_T *wp);
int screen_get_current_line_off(void);
void screen_line(int row, int coloff, int endcol, int clear_width, int flags);
*** ../vim-8.1.1492/src/popupwin.c 2019-06-02 19:53:40.998714309 +0200
--- src/popupwin.c 2019-06-08 15:50:25.594279356 +0200
***************
*** 185,192 ****
get_pos_options(wp, dict);
wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
! #if defined(FEAT_TIMERS)
// Add timer to close the popup after some time.
nr = dict_get_number(dict, (char_u *)"time");
if (nr > 0)
--- 185,196 ----
get_pos_options(wp, dict);
wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
+ if (wp->w_zindex < 1)
+ wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
+ if (wp->w_zindex > 32000)
+ wp->w_zindex = 32000;
! # if defined(FEAT_TIMERS)
// Add timer to close the popup after some time.
nr = dict_get_number(dict, (char_u *)"time");
if (nr > 0)
***************
*** 204,210 ****
clear_tv(&tv);
}
}
! #endif
// Option values resulting in setting an option.
str = dict_get_string(dict, (char_u *)"highlight", FALSE);
--- 208,214 ----
clear_tv(&tv);
}
}
! # endif
// Option values resulting in setting an option.
str = dict_get_string(dict, (char_u *)"highlight", FALSE);
***************
*** 330,335 ****
--- 334,341 ----
else
semsg(_(e_invarg2), tv_get_string(&di->di_tv));
}
+
+ popup_mask_refresh = TRUE;
}
/*
***************
*** 435,440 ****
--- 441,450 ----
int left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3];
int extra_height = top_extra + bot_extra;
int extra_width = left_extra + right_extra;
+ int org_winrow = wp->w_winrow;
+ int org_wincol = wp->w_wincol;
+ int org_width = wp->w_width;
+ int org_height = wp->w_height;
wp->w_winrow = 0;
wp->w_wincol = 0;
***************
*** 554,559 ****
--- 564,579 ----
}
wp->w_popup_last_changedtick = CHANGEDTICK(wp->w_buffer);
+
+ // Need to update popup_mask if the position or size changed.
+ if (org_winrow != wp->w_winrow
+ || org_wincol != wp->w_wincol
+ || org_width != wp->w_width
+ || org_height != wp->w_height)
+ {
+ redraw_all_later(NOT_VALID);
+ popup_mask_refresh = TRUE;
+ }
}
typedef enum
***************
*** 565,571 ****
/*
* popup_create({text}, {options})
* popup_atcursor({text}, {options})
! * When called from f_popup_atcursor() "atcursor" is TRUE.
*/
static void
popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
--- 585,591 ----
/*
* popup_create({text}, {options})
* popup_atcursor({text}, {options})
! * When called from f_popup_atcursor() "type" is TYPE_ATCURSOR.
*/
static void
popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
***************
*** 675,692 ****
set_moved_columns(wp, FIND_STRING);
}
// Deal with options.
apply_options(wp, buf, argvars[1].vval.v_dict);
- // set default values
- if (wp->w_zindex == 0)
- wp->w_zindex = 50;
-
popup_adjust_position(wp);
wp->w_vsep_width = 0;
redraw_all_later(NOT_VALID);
}
/*
--- 695,712 ----
set_moved_columns(wp, FIND_STRING);
}
+ // set default values
+ wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
+
// Deal with options.
apply_options(wp, buf, argvars[1].vval.v_dict);
popup_adjust_position(wp);
wp->w_vsep_width = 0;
redraw_all_later(NOT_VALID);
+ popup_mask_refresh = TRUE;
}
/*
***************
*** 815,820 ****
--- 835,841 ----
wp->w_popup_flags |= POPF_HIDDEN;
--wp->w_buffer->b_nwindows;
redraw_all_later(NOT_VALID);
+ popup_mask_refresh = TRUE;
}
}
***************
*** 832,837 ****
--- 853,859 ----
wp->w_popup_flags &= ~POPF_HIDDEN;
++wp->w_buffer->b_nwindows;
redraw_all_later(NOT_VALID);
+ popup_mask_refresh = TRUE;
}
}
***************
*** 843,848 ****
--- 865,871 ----
clear_cmdline = TRUE;
win_free_popup(wp);
redraw_all_later(NOT_VALID);
+ popup_mask_refresh = TRUE;
}
/*
***************
*** 944,950 ****
if (wp->w_winrow + wp->w_height >= cmdline_row)
clear_cmdline = TRUE;
popup_adjust_position(wp);
- redraw_all_later(NOT_VALID);
}
/*
--- 967,972 ----
***************
*** 984,990 ****
}
/*
! * f_popup_getoptions({id})
*/
void
f_popup_getoptions(typval_T *argvars, typval_T *rettv)
--- 1006,1012 ----
}
/*
! * popup_getoptions({id})
*/
void
f_popup_getoptions(typval_T *argvars, typval_T *rettv)
*** ../vim-8.1.1492/src/popupmnu.c 2019-05-30 15:53:26.210807270 +0200
--- src/popupmnu.c 2019-06-08 15:51:45.165930792 +0200
***************
*** 431,436 ****
--- 431,442 ----
/ (pum_size - pum_height);
}
+ #ifdef FEAT_TEXT_PROP
+ // The popup menu is drawn over popup menus with zindex under
+ // POPUPMENU_ZINDEX.
+ screen_zindex = POPUPMENU_ZINDEX;
+ #endif
+
for (i = 0; i < pum_height; ++i)
{
idx = i + pum_first;
***************
*** 611,616 ****
--- 617,626 ----
++row;
}
+
+ #ifdef FEAT_TEXT_PROP
+ screen_zindex = 0;
+ #endif
}
/*
*** ../vim-8.1.1492/src/version.c 2019-06-08 12:05:18.696163864 +0200
--- src/version.c 2019-06-08 15:52:04.829844633 +0200
***************
*** 769,770 ****
--- 769,772 ----
{ /* Add new patch number below this line */
+ /**/
+ 1493,
/**/
--
hundred-and-one symptoms of being an internet addict:
119. You are reading a book and look for the scroll bar to get to
the next page.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///