summaryrefslogtreecommitdiff
path: root/src/screen.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-06-08 16:01:13 +0200
committerBram Moolenaar <Bram@vim.org>2019-06-08 16:01:13 +0200
commit33796b39b9f00b42ca57fa00dbbb52316d9d38ff (patch)
tree61be6b15346ab09eeaf971afbad6900ecb43efb0 /src/screen.c
parent7c348bb5ad106cfa35dd45560c5ac5d3c8496c96 (diff)
downloadvim-git-33796b39b9f00b42ca57fa00dbbb52316d9d38ff.tar.gz
patch 8.1.1493: redrawing with popups is slow and causes flickerv8.1.1493
Problem: Redrawing with popups is slow and causes flicker. Solution: Avoid clearing and redrawing using a zindex mask.
Diffstat (limited to 'src/screen.c')
-rw-r--r--src/screen.c214
1 files changed, 166 insertions, 48 deletions
diff --git a/src/screen.c b/src/screen.c
index 8092fd244..fb1085327 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -610,15 +610,10 @@ update_screen(int type_arg)
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;
- }
+ // Update popup_mask if needed.
+ type = may_update_popup_mask(type);
#endif
updating_screen = TRUE;
@@ -880,6 +875,10 @@ update_prepare(void)
#ifdef FEAT_SEARCH_EXTRA
start_search_hl();
#endif
+#ifdef FEAT_TEXT_PROP
+ // Update popup_mask if needed.
+ may_update_popup_mask(0);
+#endif
}
/*
@@ -992,11 +991,6 @@ update_debug_sign(buf_T *buf, linenr_T lnum)
win_redr_status(wp, FALSE);
}
-#ifdef FEAT_TEXT_PROP
- // Display popup windows on top of the others.
- update_popups();
-#endif
-
update_finish();
}
#endif
@@ -1021,6 +1015,82 @@ get_wcr_attr(win_T *wp)
#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,14 +1119,13 @@ update_popups(void)
// 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);
+ // 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,7 +1136,6 @@ update_popups(void)
// Draw the popup text.
win_update(wp);
- popup_visible = TRUE;
wp->w_winrow -= top_off;
wp->w_wincol -= left_off;
@@ -1190,6 +1258,9 @@ update_popups(void)
wp->w_wincol + total_width - 1, border_attr[2]);
}
}
+
+ // Back to the normal zindex.
+ screen_zindex = 0;
}
}
#endif
@@ -6477,6 +6548,11 @@ screen_line(
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,6 +6797,7 @@ screen_line(
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,11 +6898,6 @@ redraw_statuslines(void)
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)
@@ -8577,9 +8649,21 @@ screen_char(unsigned off, int row, int col)
return;
#ifdef FEAT_INS_EXPAND
- if (pum_under_menu(row, col))
+ // 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,7 +8953,7 @@ screen_fill(
c = c1;
for (col = start_col; col < end_col; ++col)
{
- if (ScreenLines[off] != c
+ if ((ScreenLines[off] != c
|| (enc_utf8 && (int)ScreenLinesUC[off]
!= (c >= 0x80 ? c : 0))
|| ScreenAttrs[off] != attr
@@ -8877,6 +8961,11 @@ screen_fill(
|| 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,6 +9102,9 @@ screenalloc(int doclear)
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,6 +9186,9 @@ retry:
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,6 +9231,9 @@ give_up:
|| 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,6 +9254,9 @@ give_up:
VIM_CLEAR(new_LineOffset);
VIM_CLEAR(new_LineWraps);
VIM_CLEAR(new_TabPageIdxs);
+#ifdef FEAT_TEXT_PROP
+ VIM_CLEAR(new_popup_mask);
+#endif
}
else
{
@@ -9242,6 +9343,11 @@ give_up:
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,15 +9402,18 @@ free_screenlines(void)
{
int i;
- vim_free(ScreenLinesUC);
+ VIM_CLEAR(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);
+ 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,6 +9538,7 @@ linecopy(int to, int from, win_T *wp)
/*
* 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,7 +9553,11 @@ can_clear(char_u *p)
#else
|| cterm_normal_bg_color == 0
#endif
- || *T_UT != NUL));
+ || *T_UT != NUL)
+#ifdef FEAT_TEXT_PROP
+ && !(p == T_CE && popup_visible)
+#endif
+ );
}
/*
@@ -9891,22 +10005,26 @@ win_do_lines(
if (!redrawing() || line_count <= 0)
return FAIL;
- /* When inserting lines would result in loss of command output, just redraw
- * the lines. */
+ // 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 */
+ // 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 */
+ screenclear(); // will set wp->w_lines_valid to 0
return FAIL;
}
- /*
- * Delete all remaining lines
- */
+#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,12 +10142,17 @@ screen_ins_lines(
* - 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
+ 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,11 +11259,6 @@ showruler(int always)
/* 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