To: vim_dev@googlegroups.com Subject: Patch 8.1.1493 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@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 ///