diff options
Diffstat (limited to 'src/nvim/screen.c')
-rw-r--r-- | src/nvim/screen.c | 327 |
1 files changed, 202 insertions, 125 deletions
diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 3446a944cd..38ed2816ba 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -101,6 +101,7 @@ #include "nvim/option.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" @@ -165,7 +166,7 @@ static bool resizing = false; #endif #define SEARCH_HL_PRIORITY 0 -static char * provider_first_error = NULL; +static char * provider_err = NULL; static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args, bool default_true) @@ -187,10 +188,10 @@ static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, const char *ns_name = describe_ns(ns_id); ELOG("error in provider %s:%s: %s", ns_name, name, err.msg); bool verbose_errs = true; // TODO(bfredl): - if (verbose_errs && provider_first_error == NULL) { + if (verbose_errs && provider_err == NULL) { static char errbuf[IOSIZE]; snprintf(errbuf, sizeof errbuf, "%s: %s", ns_name, err.msg); - provider_first_error = xstrdup(errbuf); + provider_err = xstrdup(errbuf); } } @@ -437,8 +438,8 @@ int update_screen(int type) } } } - redraw_cmdline = TRUE; - redraw_tabline = TRUE; + redraw_cmdline = true; + redraw_tabline = true; } msg_scrolled = 0; msg_scrolled_at_flush = 0; @@ -1186,9 +1187,12 @@ static void win_update(win_T *wp, Providers *providers) getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc); ve_flags = save_ve_flags; - ++toc; - if (curwin->w_curswant == MAXCOL) + toc++; + // Highlight to the end of the line, unless 'virtualedit' has + // "block". + if (curwin->w_curswant == MAXCOL && !(ve_flags & VE_BLOCK)) { toc = MAXCOL; + } if (fromc != wp->w_old_cursor_fcol || toc != wp->w_old_cursor_lcol) { @@ -1368,7 +1372,9 @@ static void win_update(win_T *wp, Providers *providers) // match in fixed position might need redraw // if lines were inserted or deleted || (wp->w_match_head != NULL - && buf->b_mod_xlines != 0)))))) { + && buf->b_mod_xlines != 0))))) + || (wp->w_p_cul && (lnum == wp->w_cursor.lnum + || lnum == wp->w_last_cursorline))) { if (lnum == mod_top) { top_to_mod = false; } @@ -1378,10 +1384,12 @@ static void win_update(win_T *wp, Providers *providers) * up or down to minimize redrawing. * Don't do this when the change continues until the end. * Don't scroll when dollar_vcol >= 0, keep the "$". + * Don't scroll when redrawing the top, scrolled already above. */ if (lnum == mod_top && mod_bot != MAXLNUM - && !(dollar_vcol >= 0 && mod_bot == mod_top + 1)) { + && !(dollar_vcol >= 0 && mod_bot == mod_top + 1) + && row >= top_end) { int old_rows = 0; int new_rows = 0; int xtra_rows; @@ -1687,7 +1695,7 @@ static void win_update(win_T *wp, Providers *providers) /* * There is a trick with w_botline. If we invalidate it on each * change that might modify it, this will cause a lot of expensive - * calls to plines() in update_topline() each time. Therefore the + * calls to plines_win() in update_topline() each time. Therefore the * value of w_botline is often approximated, and this value is used to * compute the value of w_topline. If the value of w_botline was * wrong, check that the value of w_topline is correct (cursor is on @@ -2003,7 +2011,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, char_u *line; // current line char_u *ptr; // current position in "line" int row; // row in the window, excl w_winrow - ScreenGrid *grid = &wp->w_grid; // grid specfic to the window + ScreenGrid *grid = &wp->w_grid; // grid specific to the window char_u extra[57]; // sign, line number and 'fdc' must // fit in here @@ -2087,7 +2095,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, sign_attrs_T sattrs[SIGN_SHOW_MAX]; // attributes for signs int num_signs; // number of signs for line int line_attr = 0; // attribute for the whole line + int line_attr_save; int line_attr_lowprio = 0; // low-priority attribute for the line + int line_attr_lowprio_save; matchitem_T *cur; // points to the match list match_T *shl; // points to search_hl or a match bool shl_flag; // flag to indicate whether search_hl @@ -2100,13 +2110,20 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool search_attr_from_match = false; // if search_attr is from :match bool has_decor = false; // this buffer has decoration - bool do_virttext = false; // draw virtual text for this line int win_col_offset = 0; // offset for window columns char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext bool area_active = false; + int cul_attr = 0; // set when 'cursorline' active + // 'cursorlineopt' has "screenline" and cursor is in this line + bool cul_screenline = false; + // margin columns for the screen line, needed for when 'cursorlineopt' + // contains "screenline" + int left_curline_col = 0; + int right_curline_col = 0; + /* draw_state: items that are drawn in sequence: */ #define WL_START 0 /* nothing done yet */ # define WL_CMDLINE WL_START + 1 /* cmdline window column */ @@ -2145,8 +2162,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, row = startrow; - char *err_text = NULL; - buf_T *buf = wp->w_buffer; if (!number_only) { @@ -2191,14 +2206,20 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } - if (has_decor) { - extra_check = true; + if (provider_err) { + Decoration err_decor = DECORATION_INIT; + int hl_err = syn_check_group((char_u *)S_LEN("ErrorMsg")); + kv_push(err_decor.virt_text, + ((VirtTextChunk){ .text = provider_err, + .hl_id = hl_err })); + err_decor.virt_text_width = mb_string2cells((char_u *)provider_err); + decor_add_ephemeral(lnum-1, 0, lnum-1, 0, &err_decor); + provider_err = NULL; + has_decor = true; } - if (provider_first_error) { - err_text = provider_first_error; - provider_first_error = NULL; - do_virttext = true; + if (has_decor) { + extra_check = true; } // Check for columns to display for 'colorcolumn'. @@ -2354,25 +2375,34 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // Cursor line highlighting for 'cursorline' in the current window. if (lnum == wp->w_cursor.lnum) { - // Do not show the cursor line when Visual mode is active, because it's - // not clear what is selected then. - if (wp->w_p_cul && !(wp == curwin && VIsual_active)) { - int cul_attr = win_hl_attr(wp, HLF_CUL); + // Do not show the cursor line in the text when Visual mode is active, + // because it's not clear what is selected then. + if (wp->w_p_cul && !(wp == curwin && VIsual_active) + && wp->w_p_culopt_flags != CULOPT_NBR) { + cul_screenline = (wp->w_p_wrap + && (wp->w_p_culopt_flags & CULOPT_SCRLINE)); + cul_attr = win_hl_attr(wp, HLF_CUL); HlAttrs ae = syn_attr2entry(cul_attr); // We make a compromise here (#7383): // * low-priority CursorLine if fg is not set // * high-priority ("same as Vim" priority) CursorLine if fg is set - if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) { - line_attr_lowprio = cul_attr; - } else { - if (!(State & INSERT) && bt_quickfix(wp->w_buffer) - && qf_current_entry(wp) == lnum) { - line_attr = hl_combine_attr(cul_attr, line_attr); + if (!cul_screenline) { + if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) { + line_attr_lowprio = cul_attr; } else { - line_attr = cul_attr; + if (!(State & INSERT) && bt_quickfix(wp->w_buffer) + && qf_current_entry(wp) == lnum) { + line_attr = hl_combine_attr(cul_attr, line_attr); + } else { + line_attr = cul_attr; + } } + } else { + cul_attr = 0; + margin_columns_win(wp, &left_curline_col, &right_curline_col); } + area_highlighting = true; } // Update w_last_cursorline even if Visual mode is active. wp->w_last_cursorline = wp->w_cursor.lnum; @@ -2397,6 +2427,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, area_highlighting = true; } + if (cul_screenline) { + line_attr_save = line_attr; + line_attr_lowprio_save = line_attr_lowprio; + } + line = ml_get_buf(wp->w_buffer, lnum, FALSE); ptr = line; @@ -2650,7 +2685,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, off += col; } - // wont highlight after TERM_ATTRS_MAX columns + // won't highlight after TERM_ATTRS_MAX columns int term_attrs[TERM_ATTRS_MAX] = { 0 }; if (wp->w_buffer->terminal) { terminal_get_line_attributes(wp->w_buffer->terminal, wp, lnum, term_attrs); @@ -2779,11 +2814,16 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (num_sattr != NULL) { // :sign defined with "numhl" highlight. char_attr = num_sattr->sat_numhl; - } else if ((wp->w_p_cul || wp->w_p_rnu) + } else if (wp->w_p_cul && lnum == wp->w_cursor.lnum + && (wp->w_p_culopt_flags & CULOPT_NBR) + && (row == startrow + || wp->w_p_culopt_flags & CULOPT_LINE) && filler_todo == 0) { // When 'cursorline' is set highlight the line number of // the current line differently. + // When 'cursorlineopt' has "screenline" only highlight + // the line number itself. // TODO(vim): Can we use CursorLine instead of CursorLineNr // when CursorLineNr isn't set? char_attr = win_hl_attr(wp, HLF_CLN); @@ -2815,8 +2855,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (diff_hlf != (hlf_T)0) { char_attr = win_hl_attr(wp, diff_hlf); - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { - char_attr = hl_combine_attr(char_attr, win_hl_attr(wp, HLF_CUL)); + if (cul_attr) { + char_attr = hl_combine_attr(char_attr, cul_attr); } } p_extra = NULL; @@ -2875,8 +2915,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (tocol == vcol) tocol += n_extra; // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'. - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { - char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUL), char_attr); + if (cul_attr) { + char_attr = hl_combine_attr(cul_attr, char_attr); } } } @@ -2903,6 +2943,24 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } + if (cul_screenline) { + if (draw_state == WL_LINE + && vcol >= left_curline_col + && vcol < right_curline_col) { + cul_attr = win_hl_attr(wp, HLF_CUL); + HlAttrs ae = syn_attr2entry(cul_attr); + if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) { + line_attr_lowprio = cul_attr; + } else { + line_attr = cul_attr; + } + } else { + cul_attr = 0; + line_attr = line_attr_save; + line_attr_lowprio = line_attr_lowprio_save; + } + } + // When still displaying '$' of change command, stop at cursor if (((dollar_vcol >= 0 && wp == curwin @@ -3110,12 +3168,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } line_attr = win_hl_attr(wp, diff_hlf); // Overlay CursorLine onto diff-mode highlight. - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { + if (cul_attr) { line_attr = 0 != line_attr_lowprio // Low-priority CursorLine - ? hl_combine_attr(hl_combine_attr(win_hl_attr(wp, HLF_CUL), - line_attr), + ? hl_combine_attr(hl_combine_attr(cul_attr, line_attr), hl_get_underline()) - : hl_combine_attr(line_attr, win_hl_attr(wp, HLF_CUL)); + : hl_combine_attr(line_attr, cul_attr); } } @@ -3191,6 +3248,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, (void)mb_l; multi_attr = win_hl_attr(wp, HLF_AT); + if (cul_attr) { + multi_attr = 0 != line_attr_lowprio + ? hl_combine_attr(cul_attr, multi_attr) + : hl_combine_attr(multi_attr, cul_attr); + } + // put the pointer back to output the double-width // character at the start of the next line. n_extra++; @@ -3351,7 +3414,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, ptr = line + v; if (!attr_pri) { - char_attr = syntax_attr; + if (cul_attr) { + char_attr = 0 != line_attr_lowprio + ? hl_combine_attr(cul_attr, syntax_attr) + : hl_combine_attr(syntax_attr, cul_attr); + } else { + char_attr = syntax_attr; + } } else { char_attr = hl_combine_attr(syntax_attr, char_attr); } @@ -3918,8 +3987,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } int eol_attr = char_attr; - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { - eol_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUL), eol_attr); + if (cul_attr) { + eol_attr = hl_combine_attr(cul_attr, eol_attr); } linebuf_attr[off] = eol_attr; if (wp->w_p_rl) { @@ -3950,19 +4019,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (draw_color_col) draw_color_col = advance_color_col(VCOL_HLC, &color_cols); - VirtText virt_text = KV_INITIAL_VALUE; - bool has_aligned = false; - if (err_text) { - int hl_err = syn_check_group((char_u *)S_LEN("ErrorMsg")); - kv_push(virt_text, ((VirtTextChunk){ .text = err_text, - .hl_id = hl_err })); - do_virttext = true; - } else if (has_decor) { - virt_text = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr, - &has_aligned); - if (kv_size(virt_text)) { - do_virttext = true; - } + bool has_virttext = false; + // Make sure alignment is the same regardless + // if listchars=eol:X is used or not. + int eol_skip = (wp->w_p_lcs_chars.eol == lcs_eol_one && eol_hl_off == 0 + ? 1 : 0); + + if (has_decor) { + has_virttext = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr, + col + eol_skip); } if (((wp->w_p_cuc @@ -3971,20 +4036,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, grid->Columns * (row - startrow + 1) + v && lnum != wp->w_cursor.lnum) || draw_color_col || line_attr_lowprio || line_attr - || diff_hlf != (hlf_T)0 || do_virttext - || has_aligned)) { + || diff_hlf != (hlf_T)0 || has_virttext)) { int rightmost_vcol = 0; int i; - size_t virt_pos = 0; - LineState s = LINE_STATE(""); - int virt_attr = 0; - - // Make sure alignment is the same regardless - // if listchars=eol:X is used or not. - bool delay_virttext = wp->w_p_lcs_chars.eol == lcs_eol_one - && eol_hl_off == 0; - if (wp->w_p_cuc) { rightmost_vcol = wp->w_virtcol; } @@ -4010,37 +4065,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr); - if (base_attr || line_attr || has_aligned) { + if (base_attr || line_attr || has_virttext) { rightmost_vcol = INT_MAX; } int col_stride = wp->w_p_rl ? -1 : 1; while (wp->w_p_rl ? col >= 0 : col < grid->Columns) { - int cells = -1; - if (do_virttext && !delay_virttext) { - if (*s.p == NUL) { - if (virt_pos < virt_text.size) { - s.p = kv_A(virt_text, virt_pos).text; - int hl_id = kv_A(virt_text, virt_pos).hl_id; - virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; - virt_pos++; - } else { - do_virttext = false; - } - } - if (*s.p != NUL) { - cells = line_putchar(&s, &linebuf_char[off], grid->Columns - col, - false); - } - } - delay_virttext = false; - - if (cells == -1) { - schar_from_ascii(linebuf_char[off], ' '); - cells = 1; - } - col += cells * col_stride; + schar_from_ascii(linebuf_char[off], ' '); + col += col_stride; if (draw_color_col) { draw_color_col = advance_color_col(VCOL_HLC, &color_cols); } @@ -4053,24 +4086,16 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, col_attr = mc_attr; } - if (do_virttext) { - col_attr = hl_combine_attr(col_attr, virt_attr); - } - col_attr = hl_combine_attr(col_attr, line_attr); linebuf_attr[off] = col_attr; - if (cells == 2) { - linebuf_attr[off+1] = col_attr; - } - off += cells * col_stride; + off += col_stride; - if (VCOL_HLC >= rightmost_vcol && *s.p == NUL - && virt_pos >= virt_text.size) { + if (VCOL_HLC >= rightmost_vcol) { break; } - vcol += cells; + vcol += 1; } } @@ -4095,7 +4120,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, /* * Update w_cline_height and w_cline_folded if the cursor line was - * updated (saves a call to plines() later). + * updated (saves a call to plines_win() later). */ if (wp == curwin && lnum == curwin->w_cursor.lnum) { curwin->w_cline_row = startrow; @@ -4389,7 +4414,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } xfree(p_extra_free); - xfree(err_text); return row; } @@ -4397,13 +4421,17 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col) { DecorState *state = &decor_state; int right_pos = max_col; + bool do_eol = state->eol_col > -1; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange *item = &kv_A(state->active, i); if (item->start_row == state->row && kv_size(item->decor.virt_text)) { if (item->win_col == -1) { if (item->decor.virt_text_pos == kVTRightAlign) { - right_pos -= item->decor.col; + right_pos -= item->decor.virt_text_width; item->win_col = right_pos; + } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { + item->win_col = state->eol_col; + state->eol_col += item->decor.virt_text_width; } else if (item->decor.virt_text_pos == kVTWinCol) { item->win_col = MAX(item->decor.col+col_off, 0); } @@ -4421,14 +4449,20 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col) while (col < max_col) { if (!*s.p) { - if (virt_pos == kv_size(vt)) { + if (virt_pos >= kv_size(vt)) { + break; + } + virt_attr = 0; + do { + s.p = kv_A(vt, virt_pos).text; + int hl_id = kv_A(vt, virt_pos).hl_id; + virt_attr = hl_combine_attr(virt_attr, + hl_id > 0 ? syn_id2attr(hl_id) : 0); + virt_pos++; + } while (!s.p && virt_pos < kv_size(vt)); + if (!s.p) { break; } - s.p = kv_A(vt, virt_pos).text; - int hl_id = kv_A(vt, virt_pos).hl_id; - virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; - virt_pos++; - continue; } int attr; bool through = false; @@ -5527,7 +5561,7 @@ static void win_redr_border(win_T *wp) } } -// Low-level functions to manipulate invidual character cells on the +// Low-level functions to manipulate individual character cells on the // screen grid. /// Put a ASCII character in a screen cell. @@ -6783,8 +6817,8 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, // Show the current mode and ruler. // -// If clear_cmdline is TRUE, clear the rest of the cmdline. -// If clear_cmdline is FALSE there may be a message there that needs to be +// If clear_cmdline is true, clear the rest of the cmdline. +// If clear_cmdline is false there may be a message there that needs to be // cleared only if a mode is shown. // Return the length of the message (0 if no message). int showmode(void) @@ -6793,7 +6827,6 @@ int showmode(void) int length = 0; int do_mode; int attr; - int nwr_save; int sub_attr; if (ui_has(kUIMessages) && clear_cmdline) { @@ -6815,11 +6848,11 @@ int showmode(void) // Call char_avail() only when we are going to show something, because // it takes a bit of time. if (!redrawing() || (char_avail() && !KeyTyped) || msg_silent != 0) { - redraw_cmdline = TRUE; /* show mode later */ + redraw_cmdline = true; // show mode later return 0; } - nwr_save = need_wait_return; + bool nwr_save = need_wait_return; /* wait a bit before overwriting an important message */ check_for_delay(FALSE); @@ -6834,7 +6867,7 @@ int showmode(void) msg_pos_mode(); attr = HL_ATTR(HLF_CM); // Highlight mode - // When the screen is too narrow to show the entire mode messsage, + // When the screen is too narrow to show the entire mode message, // avoid scrolling and truncate instead. msg_no_more = true; int save_lines_left = lines_left; @@ -6936,10 +6969,11 @@ int showmode(void) need_clear = true; } - mode_displayed = TRUE; - if (need_clear || clear_cmdline) + mode_displayed = true; + if (need_clear || clear_cmdline) { msg_clr_eos(); - msg_didout = FALSE; /* overwrite this message */ + } + msg_didout = false; // overwrite this message length = msg_col; msg_col = 0; msg_no_more = false; @@ -6950,7 +6984,7 @@ int showmode(void) msg_clr_cmdline(); } - // NB: also handles clearing the showmode if it was emtpy or disabled + // NB: also handles clearing the showmode if it was empty or disabled msg_ext_flush_showmode(); /* In Visual mode the size of the selected area must be redrawn. */ @@ -7195,7 +7229,7 @@ void draw_tabline(void) /* Reset the flag here again, in case evaluating 'tabline' causes it to be * set. */ - redraw_tabline = FALSE; + redraw_tabline = false; } void ui_ext_tabline_update(void) @@ -7539,6 +7573,49 @@ int number_width(win_T *wp) return n; } +/// Used when 'cursorlineopt' contains "screenline": compute the margins between +/// which the highlighting is used. +static void margin_columns_win(win_T *wp, int *left_col, int *right_col) +{ + // cache previous calculations depending on w_virtcol + static int saved_w_virtcol; + static win_T *prev_wp; + static int prev_left_col; + static int prev_right_col; + static int prev_col_off; + + int cur_col_off = win_col_off(wp); + int width1; + int width2; + + if (saved_w_virtcol == wp->w_virtcol && prev_wp == wp + && prev_col_off == cur_col_off) { + *right_col = prev_right_col; + *left_col = prev_left_col; + return; + } + + width1 = wp->w_width - cur_col_off; + width2 = width1 + win_col_off2(wp); + + *left_col = 0; + *right_col = width1; + + if (wp->w_virtcol >= (colnr_T)width1) { + *right_col = width1 + ((wp->w_virtcol - width1) / width2 + 1) * width2; + } + if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) { + *left_col = (wp->w_virtcol - width1) / width2 * width2 + width1; + } + + // cache values + prev_left_col = *left_col; + prev_right_col = *right_col; + prev_wp = wp; + saved_w_virtcol = wp->w_virtcol; + prev_col_off = cur_col_off; +} + /// Set dimensions of the Nvim application "shell". void screen_resize(int width, int height) { |