]> granicus.if.org Git - vim/commitdiff
patch 8.1.1493: redrawing with popups is slow and causes flicker v8.1.1493
authorBram Moolenaar <Bram@vim.org>
Sat, 8 Jun 2019 14:01:13 +0000 (16:01 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 8 Jun 2019 14:01:13 +0000 (16:01 +0200)
Problem:    Redrawing with popups is slow and causes flicker.
Solution:   Avoid clearing and redrawing using a zindex mask.

src/globals.h
src/popupmnu.c
src/popupwin.c
src/proto/screen.pro
src/screen.c
src/version.c

index 4973d3191a511b14742a77738e942fe7b460b5a1..06091f97b4db6c8f5ffffe67a92aa6f0cd4075ed 100644 (file)
@@ -70,6 +70,21 @@ EXTERN schar_T       *ScreenLines2 INIT(= NULL);
  */
 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[] */
 
index 276d49fd013898ecbcac9e966e7f1854659f62c9..09faa69e88083d0508af89f8e71f3f2edae6f3ae 100644 (file)
@@ -431,6 +431,12 @@ pum_redraw(void)
                                                    / (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,6 +617,10 @@ pum_redraw(void)
 
        ++row;
     }
+
+#ifdef FEAT_TEXT_PROP
+    screen_zindex = 0;
+#endif
 }
 
 /*
index 973358c55d07362ef7ba52d5f607015723e2fbff..13ba6391db42fe1f597eb78e46aa03a412be15eb 100644 (file)
@@ -185,8 +185,12 @@ apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict)
     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)
+# 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,7 +208,7 @@ apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict)
            clear_tv(&tv);
        }
     }
-#endif
+# endif
 
     // Option values resulting in setting an option.
     str = dict_get_string(dict, (char_u *)"highlight", FALSE);
@@ -330,6 +334,8 @@ apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict)
        else
            semsg(_(e_invarg2), tv_get_string(&di->di_tv));
     }
+
+    popup_mask_refresh = TRUE;
 }
 
 /*
@@ -435,6 +441,10 @@ popup_adjust_position(win_T *wp)
     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,6 +564,16 @@ popup_adjust_position(win_T *wp)
     }
 
     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,7 +585,7 @@ typedef enum
 /*
  * popup_create({text}, {options})
  * popup_atcursor({text}, {options})
- * When called from f_popup_atcursor() "atcursor" is TRUE.
+ * 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,18 +695,18 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
        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);
 
-    // 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);
+    popup_mask_refresh = TRUE;
 }
 
 /*
@@ -815,6 +835,7 @@ f_popup_hide(typval_T *argvars, typval_T *rettv UNUSED)
        wp->w_popup_flags |= POPF_HIDDEN;
        --wp->w_buffer->b_nwindows;
        redraw_all_later(NOT_VALID);
+       popup_mask_refresh = TRUE;
     }
 }
 
@@ -832,6 +853,7 @@ f_popup_show(typval_T *argvars, typval_T *rettv UNUSED)
        wp->w_popup_flags &= ~POPF_HIDDEN;
        ++wp->w_buffer->b_nwindows;
        redraw_all_later(NOT_VALID);
+       popup_mask_refresh = TRUE;
     }
 }
 
@@ -843,6 +865,7 @@ popup_free(win_T *wp)
        clear_cmdline = TRUE;
     win_free_popup(wp);
     redraw_all_later(NOT_VALID);
+    popup_mask_refresh = TRUE;
 }
 
 /*
@@ -944,7 +967,6 @@ f_popup_move(typval_T *argvars, typval_T *rettv UNUSED)
     if (wp->w_winrow + wp->w_height >= cmdline_row)
        clear_cmdline = TRUE;
     popup_adjust_position(wp);
-    redraw_all_later(NOT_VALID);
 }
 
 /*
@@ -984,7 +1006,7 @@ f_popup_getpos(typval_T *argvars, typval_T *rettv)
 }
 
 /*
- * f_popup_getoptions({id})
+ * popup_getoptions({id})
  */
     void
 f_popup_getoptions(typval_T *argvars, typval_T *rettv)
index ab73e0ba9b88c244e7c7daa21a22dbbed9514dec..8c2e0083b787c84d7ee3d083229d1c780b5832a2 100644 (file)
@@ -16,6 +16,7 @@ int update_screen(int type_arg);
 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);
index 8092fd2448851c750df64edcc8ff148b2b11c483..fb108532779c08a029e885af18c69c61653c48b4 100644 (file)
@@ -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
@@ -1020,6 +1014,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.
  */
@@ -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,11 +10142,16 @@ 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
index d1ef98b55c208175d4f6cfc55104c827be7f38e3..fb159d95e717ad0dafcb7486821da13978369f07 100644 (file)
@@ -767,6 +767,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1493,
 /**/
     1492,
 /**/