xref: /openbsd/lib/libcurses/base/lib_refresh.c (revision c7ef0cfc)
1 /* $OpenBSD: lib_refresh.c,v 1.6 2023/10/17 09:52:08 nicm Exp $ */
2 
3 /****************************************************************************
4  * Copyright 2020-2021,2023 Thomas E. Dickey                                *
5  * Copyright 1998-2010,2011 Free Software Foundation, Inc.                  *
6  *                                                                          *
7  * Permission is hereby granted, free of charge, to any person obtaining a  *
8  * copy of this software and associated documentation files (the            *
9  * "Software"), to deal in the Software without restriction, including      *
10  * without limitation the rights to use, copy, modify, merge, publish,      *
11  * distribute, distribute with modifications, sublicense, and/or sell       *
12  * copies of the Software, and to permit persons to whom the Software is    *
13  * furnished to do so, subject to the following conditions:                 *
14  *                                                                          *
15  * The above copyright notice and this permission notice shall be included  *
16  * in all copies or substantial portions of the Software.                   *
17  *                                                                          *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
21  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
22  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
23  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
24  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
25  *                                                                          *
26  * Except as contained in this notice, the name(s) of the above copyright   *
27  * holders shall not be used in advertising or otherwise to promote the     *
28  * sale, use or other dealings in this Software without prior written       *
29  * authorization.                                                           *
30  ****************************************************************************/
31 
32 /****************************************************************************
33  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
34  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
35  *     and: Thomas E. Dickey                        1996-on                 *
36  *     and: Juergen Pfeifer                                                 *
37  ****************************************************************************/
38 
39 /*
40  *	lib_refresh.c
41  *
42  *	The routines wrefresh() and wnoutrefresh().
43  *
44  */
45 
46 #include <curses.priv.h>
47 
48 MODULE_ID("$Id: lib_refresh.c,v 1.6 2023/10/17 09:52:08 nicm Exp $")
49 
NCURSES_EXPORT(int)50 NCURSES_EXPORT(int)
51 wrefresh(WINDOW *win)
52 {
53     int code;
54 #if NCURSES_SP_FUNCS
55     SCREEN *SP_PARM = _nc_screen_of(win);
56 #endif
57 
58     T((T_CALLED("wrefresh(%p)"), (void *) win));
59 
60     if (win == 0) {
61 	code = ERR;
62     } else if (win == CurScreen(SP_PARM)) {
63 	CurScreen(SP_PARM)->_clear = TRUE;
64 	code = NCURSES_SP_NAME(doupdate) (NCURSES_SP_ARG);
65     } else if ((code = wnoutrefresh(win)) == OK) {
66 	if (win->_clear)
67 	    NewScreen(SP_PARM)->_clear = TRUE;
68 	code = NCURSES_SP_NAME(doupdate) (NCURSES_SP_ARG);
69 	/*
70 	 * Reset the clearok() flag in case it was set for the special
71 	 * case in hardscroll.c (if we don't reset it here, we'll get 2
72 	 * refreshes because the flag is copied from stdscr to newscr).
73 	 * Resetting the flag shouldn't do any harm, anyway.
74 	 */
75 	win->_clear = FALSE;
76     }
77     returnCode(code);
78 }
79 
80 NCURSES_EXPORT(int)
wnoutrefresh(WINDOW * win)81 wnoutrefresh(WINDOW *win)
82 {
83     int limit_x;
84     int src_row, src_col;
85     int begx;
86     int begy;
87     int dst_row, dst_col;
88 #if USE_SCROLL_HINTS
89     bool wide;
90 #endif
91 #if NCURSES_SP_FUNCS
92     SCREEN *SP_PARM = _nc_screen_of(win);
93 #endif
94 
95     T((T_CALLED("wnoutrefresh(%p)"), (void *) win));
96 
97     if (win == NULL)
98 	returnCode(ERR);
99 
100     /*
101      * Handle pads as a special case.
102      */
103     if (IS_PAD(win)) {
104 	returnCode(pnoutrefresh(win,
105 				win->_pad._pad_y,
106 				win->_pad._pad_x,
107 				win->_pad._pad_top,
108 				win->_pad._pad_left,
109 				win->_pad._pad_bottom,
110 				win->_pad._pad_right));
111     }
112 #ifdef TRACE
113     if (USE_TRACEF(TRACE_UPDATE)) {
114 	_tracedump("...win", win);
115 	_nc_unlock_global(tracef);
116     }
117 #endif /* TRACE */
118 
119     /* put them here so "win == 0" won't break our code */
120     begx = win->_begx;
121     begy = win->_begy;
122 
123     NewScreen(SP_PARM)->_nc_bkgd = win->_nc_bkgd;
124     WINDOW_ATTRS(NewScreen(SP_PARM)) = WINDOW_ATTRS(win);
125 
126     /* merge in change information from all subwindows of this window */
127     wsyncdown(win);
128 
129 #if USE_SCROLL_HINTS
130     /*
131      * For pure efficiency, we'd want to transfer scrolling information
132      * from the window to newscr whenever the window is wide enough that
133      * its update will dominate the cost of the update for the horizontal
134      * band of newscr that it occupies.  Unfortunately, this threshold
135      * tends to be complex to estimate, and in any case scrolling the
136      * whole band and rewriting the parts outside win's image would look
137      * really ugly.  So.  What we do is consider the window "wide" if it
138      * either (a) occupies the whole width of newscr, or (b) occupies
139      * all but at most one column on either vertical edge of the screen
140      * (this caters to fussy people who put boxes around full-screen
141      * windows).  Note that changing this formula will not break any code,
142      * merely change the costs of various update cases.
143      */
144     wide = (begx <= 1 && win->_maxx >= (NewScreen(SP_PARM)->_maxx - 1));
145 #endif
146 
147     win->_flags &= ~_HASMOVED;
148 
149     /*
150      * Microtweaking alert!  This double loop is one of the genuine
151      * hot spots in the code.  Even gcc doesn't seem to do enough
152      * common-subexpression chunking to make it really tense,
153      * so we'll force the issue.
154      */
155 
156     /* limit(dst_col) */
157     limit_x = win->_maxx;
158     /* limit(src_col) */
159     if (limit_x > NewScreen(SP_PARM)->_maxx - begx)
160 	limit_x = NewScreen(SP_PARM)->_maxx - begx;
161 
162     for (src_row = 0, dst_row = begy + win->_yoffset;
163 	 src_row <= win->_maxy && dst_row <= NewScreen(SP_PARM)->_maxy;
164 	 src_row++, dst_row++) {
165 	struct ldat *nline = &(NewScreen(SP_PARM)->_line[dst_row]);
166 	struct ldat *oline = &win->_line[src_row];
167 
168 	if (oline->firstchar != _NOCHANGE) {
169 	    int last_src = oline->lastchar;
170 
171 	    if (last_src > limit_x)
172 		last_src = limit_x;
173 
174 	    src_col = oline->firstchar;
175 	    dst_col = src_col + begx;
176 
177 	    if_WIDEC({
178 		int j;
179 
180 		/*
181 		 * Ensure that we will copy complete multi-column characters
182 		 * on the left-boundary.
183 		 */
184 		if (isWidecExt(oline->text[src_col])) {
185 		    j = 1 + dst_col - WidecExt(oline->text[src_col]);
186 		    if (j < 0)
187 			j = 0;
188 		    if (dst_col > j) {
189 			src_col -= (dst_col - j);
190 			dst_col = j;
191 		    }
192 		}
193 
194 		/*
195 		 * Ensure that we will copy complete multi-column characters
196 		 * on the right-boundary.
197 		 */
198 		j = last_src;
199 		if (WidecExt(oline->text[j])) {
200 		    ++j;
201 		    while (j <= limit_x) {
202 			if (isWidecBase(oline->text[j])) {
203 			    break;
204 			} else {
205 			    last_src = j;
206 			}
207 			++j;
208 		    }
209 		}
210 	    });
211 
212 	    if_WIDEC({
213 		int last_dst = begx + ((last_src < win->_maxx)
214 				       ? last_src
215 				       : win->_maxx);
216 		int fix_left = dst_col;
217 		int fix_right = last_dst;
218 		int j;
219 
220 		/*
221 		 * Check for boundary cases where we may overwrite part of a
222 		 * multi-column character.  For those, wipe the remainder of
223 		 * the character to blanks.
224 		 */
225 		j = dst_col;
226 		if (isWidecExt(nline->text[j])) {
227 		    /*
228 		     * On the left, we only care about multi-column characters
229 		     * that extend into the changed region.
230 		     */
231 		    fix_left = 1 + j - WidecExt(nline->text[j]);
232 		    if (fix_left < 0)
233 			fix_left = 0;	/* only if cell is corrupt */
234 		}
235 
236 		j = last_dst;
237 		if (WidecExt(nline->text[j]) != 0) {
238 		    /*
239 		     * On the right, any multi-column character is a problem,
240 		     * unless it happens to be contained in the change, and
241 		     * ending at the right boundary of the change.  The
242 		     * computation for 'fix_left' accounts for the left-side of
243 		     * this character.  Find the end of the character.
244 		     */
245 		    ++j;
246 		    while (j <= NewScreen(SP_PARM)->_maxx &&
247 			   isWidecExt(nline->text[j])) {
248 			fix_right = j++;
249 		    }
250 		}
251 
252 		/*
253 		 * The analysis is simpler if we do the clearing afterwards.
254 		 * Do that now.
255 		 */
256 		if (fix_left < dst_col || fix_right > last_dst) {
257 		    for (j = fix_left; j <= fix_right; ++j) {
258 			static cchar_t blank = BLANK;
259 			nline->text[j] = blank;
260 			CHANGED_CELL(nline, j);
261 		    }
262 		}
263 	    });
264 
265 	    /*
266 	     * Copy the changed text.
267 	     */
268 	    for (; src_col <= last_src; src_col++, dst_col++) {
269 		if (!CharEq(oline->text[src_col], nline->text[dst_col])) {
270 		    nline->text[dst_col] = oline->text[src_col];
271 		    CHANGED_CELL(nline, dst_col);
272 		}
273 	    }
274 
275 	}
276 #if USE_SCROLL_HINTS
277 	if (wide) {
278 	    int oind = oline->oldindex;
279 
280 	    nline->oldindex = ((oind == _NEWINDEX)
281 			       ? _NEWINDEX
282 			       : (begy + oind + win->_yoffset));
283 	}
284 #endif /* USE_SCROLL_HINTS */
285 
286 	oline->firstchar = oline->lastchar = _NOCHANGE;
287 	if_USE_SCROLL_HINTS(oline->oldindex = src_row);
288     }
289 
290     if (win->_clear) {
291 	win->_clear = FALSE;
292 	NewScreen(SP_PARM)->_clear = TRUE;
293     }
294 
295     if (!win->_leaveok) {
296 	NewScreen(SP_PARM)->_cury = (NCURSES_SIZE_T) (win->_cury +
297 						      win->_begy + win->_yoffset);
298 	NewScreen(SP_PARM)->_curx = (NCURSES_SIZE_T) (win->_curx + win->_begx);
299     }
300     NewScreen(SP_PARM)->_leaveok = win->_leaveok;
301 
302 #ifdef TRACE
303     if (USE_TRACEF(TRACE_UPDATE)) {
304 	_tracedump("newscr", NewScreen(SP_PARM));
305 	_nc_unlock_global(tracef);
306     }
307 #endif /* TRACE */
308     returnCode(OK);
309 }
310