xref: /openbsd/lib/libcurses/base/lib_pad.c (revision c7ef0cfc)
1 /* $OpenBSD: lib_pad.c,v 1.6 2023/10/17 09:52:08 nicm Exp $ */
2 
3 /****************************************************************************
4  * Copyright 2020,2021 Thomas E. Dickey                                     *
5  * Copyright 1998-2010,2017 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                         2009                    *
37  ****************************************************************************/
38 
39 /*
40  * lib_pad.c
41  * newpad	-- create a new pad
42  * pnoutrefresh -- refresh a pad, no update
43  * pechochar	-- add a char to a pad and refresh
44  */
45 
46 #include <curses.priv.h>
47 
48 MODULE_ID("$Id: lib_pad.c,v 1.6 2023/10/17 09:52:08 nicm Exp $")
49 
NCURSES_EXPORT(WINDOW *)50 NCURSES_EXPORT(WINDOW *)
51 NCURSES_SP_NAME(newpad) (NCURSES_SP_DCLx int l, int c)
52 {
53     WINDOW *win;
54     NCURSES_CH_T *ptr;
55     int i;
56 
57     T((T_CALLED("newpad(%p,%d, %d)"), (void *) SP_PARM, l, c));
58 
59     if (l <= 0 || c <= 0)
60 	returnWin(0);
61 
62     win = NCURSES_SP_NAME(_nc_makenew) (NCURSES_SP_ARGx l, c, 0, 0, _ISPAD);
63     if (win == NULL)
64 	returnWin(0);
65 
66     for (i = 0; i < l; i++) {
67 	if_USE_SCROLL_HINTS(win->_line[i].oldindex = _NEWINDEX);
68 	if ((win->_line[i].text = typeCalloc(NCURSES_CH_T, ((size_t) c))) == 0) {
69 	    (void) _nc_freewin(win);
70 	    returnWin(0);
71 	}
72 	for (ptr = win->_line[i].text; ptr < win->_line[i].text + c; ptr++)
73 	    SetChar(*ptr, BLANK_TEXT, BLANK_ATTR);
74     }
75 
76     returnWin(win);
77 }
78 
79 #if NCURSES_SP_FUNCS
80 NCURSES_EXPORT(WINDOW *)
newpad(int l,int c)81 newpad(int l, int c)
82 {
83     return NCURSES_SP_NAME(newpad) (CURRENT_SCREEN, l, c);
84 }
85 #endif
86 
87 NCURSES_EXPORT(WINDOW *)
subpad(WINDOW * orig,int l,int c,int begy,int begx)88 subpad(WINDOW *orig, int l, int c, int begy, int begx)
89 {
90     WINDOW *win = (WINDOW *) 0;
91 
92     T((T_CALLED("subpad(%d, %d)"), l, c));
93 
94     if (orig) {
95 	if (!IS_PAD(orig)
96 	    || ((win = derwin(orig, l, c, begy, begx)) == NULL))
97 	    returnWin(0);
98     }
99     returnWin(win);
100 }
101 
102 NCURSES_EXPORT(int)
prefresh(WINDOW * win,int pminrow,int pmincol,int sminrow,int smincol,int smaxrow,int smaxcol)103 prefresh(WINDOW *win,
104 	 int pminrow,
105 	 int pmincol,
106 	 int sminrow,
107 	 int smincol,
108 	 int smaxrow,
109 	 int smaxcol)
110 {
111 #if NCURSES_SP_FUNCS
112     SCREEN *sp = _nc_screen_of(win);
113 #endif
114 
115     T((T_CALLED("prefresh()")));
116     if (pnoutrefresh(win, pminrow, pmincol, sminrow, smincol, smaxrow,
117 		     smaxcol) != ERR
118 	&& NCURSES_SP_NAME(doupdate) (NCURSES_SP_ARG) != ERR) {
119 	returnCode(OK);
120     }
121     returnCode(ERR);
122 }
123 
124 NCURSES_EXPORT(int)
pnoutrefresh(WINDOW * win,int pminrow,int pmincol,int sminrow,int smincol,int smaxrow,int smaxcol)125 pnoutrefresh(WINDOW *win,
126 	     int pminrow,
127 	     int pmincol,
128 	     int sminrow,
129 	     int smincol,
130 	     int smaxrow,
131 	     int smaxcol)
132 {
133     int i, j;
134     int m, n;
135     int pmaxrow;
136     int pmaxcol;
137     SCREEN *sp;
138 
139 #if USE_SCROLL_HINTS
140     const int my_len = 2;	/* parameterize the threshold for hardscroll */
141     NCURSES_SIZE_T displaced;
142     bool wide;
143 #endif
144 
145     T((T_CALLED("pnoutrefresh(%p, %d, %d, %d, %d, %d, %d)"),
146        (void *) win, pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol));
147 
148     if (win == 0)
149 	returnCode(ERR);
150 
151     if (!IS_PAD(win))
152 	returnCode(ERR);
153 
154     sp = _nc_screen_of(win);
155 
156     /* negative values are interpreted as zero */
157     if (pminrow < 0)
158 	pminrow = 0;
159     if (pmincol < 0)
160 	pmincol = 0;
161     if (sminrow < 0)
162 	sminrow = 0;
163     if (smincol < 0)
164 	smincol = 0;
165 
166     pmaxrow = pminrow + smaxrow - sminrow;
167     pmaxcol = pmincol + smaxcol - smincol;
168 
169     T((" pminrow + smaxrow - sminrow %ld, win->_maxy %ld",
170        (long) pmaxrow, (long) win->_maxy));
171     T((" pmincol + smaxcol - smincol %ld, win->_maxx %ld",
172        (long) pmaxcol, (long) win->_maxx));
173 
174     /*
175      * Trim the caller's screen size back to the actual limits.
176      */
177     if (pmaxrow > win->_maxy) {
178 	smaxrow -= (pmaxrow - win->_maxy);
179 	pmaxrow = pminrow + smaxrow - sminrow;
180     }
181     if (pmaxcol > win->_maxx) {
182 	smaxcol -= (pmaxcol - win->_maxx);
183 	pmaxcol = pmincol + smaxcol - smincol;
184     }
185 
186     if (smaxrow >= screen_lines(sp)
187 	|| smaxcol >= screen_columns(sp)
188 	|| sminrow > smaxrow
189 	|| smincol > smaxcol)
190 	returnCode(ERR);
191 
192     T(("pad being refreshed"));
193 
194 #ifdef TRACE
195     if (USE_TRACEF(TRACE_UPDATE)) {
196 	_tracedump("...pad", win);
197 	_nc_unlock_global(tracef);
198     }
199 #endif /* TRACE */
200 #if USE_SCROLL_HINTS
201     if (win->_pad._pad_y >= 0) {
202 	displaced = pminrow - win->_pad._pad_y
203 	    - (sminrow - win->_pad._pad_top);
204 	T(("pad being shifted by %d line(s)", displaced));
205     } else
206 	displaced = 0;
207 #endif
208 
209     /*
210      * For pure efficiency, we'd want to transfer scrolling information
211      * from the pad to newscr whenever the window is wide enough that
212      * its update will dominate the cost of the update for the horizontal
213      * band of newscr that it occupies.  Unfortunately, this threshold
214      * tends to be complex to estimate, and in any case scrolling the
215      * whole band and rewriting the parts outside win's image would look
216      * really ugly.  So.  What we do is consider the pad "wide" if it
217      * either (a) occupies the whole width of newscr, or (b) occupies
218      * all but at most one column on either vertical edge of the screen
219      * (this caters to fussy people who put boxes around full-screen
220      * windows).  Note that changing this formula will not break any code,
221      * merely change the costs of various update cases.
222      */
223 #if USE_SCROLL_HINTS
224     wide = (smincol < my_len && smaxcol > (NewScreen(sp)->_maxx - my_len));
225 #endif
226 
227     for (i = pminrow, m = sminrow + win->_yoffset;
228 	 i <= pmaxrow && m <= NewScreen(sp)->_maxy;
229 	 i++, m++) {
230 	register struct ldat *nline = &NewScreen(sp)->_line[m];
231 	register struct ldat *oline = &win->_line[i];
232 	for (j = pmincol, n = smincol; j <= pmaxcol; j++, n++) {
233 	    NCURSES_CH_T ch = oline->text[j];
234 #if USE_WIDEC_SUPPORT
235 	    /*
236 	     * Special case for leftmost character of the displayed area.
237 	     * Only half of a double-width character may be visible.
238 	     */
239 	    if (j == pmincol
240 		&& j > 0
241 		&& isWidecExt(ch)) {
242 		SetChar(ch, L(' '), AttrOf(oline->text[j - 1]));
243 	    }
244 #endif
245 	    if (!CharEq(ch, nline->text[n])) {
246 		nline->text[n] = ch;
247 		CHANGED_CELL(nline, n);
248 	    }
249 	}
250 
251 #if USE_SCROLL_HINTS
252 	if (wide) {
253 	    int nind = m + displaced;
254 	    if (oline->oldindex < 0
255 		|| nind < sminrow
256 		|| nind > smaxrow) {
257 		nind = _NEWINDEX;
258 	    } else if (displaced) {
259 		register struct ldat *pline = &CurScreen(sp)->_line[nind];
260 		for (j = 0; j <= my_len; j++) {
261 		    int k = NewScreen(sp)->_maxx - j;
262 		    if (pline->text[j] != nline->text[j]
263 			|| pline->text[k] != nline->text[k]) {
264 			nind = _NEWINDEX;
265 			break;
266 		    }
267 		}
268 	    }
269 
270 	    nline->oldindex = nind;
271 	}
272 #endif /* USE_SCROLL_HINTS */
273 	oline->firstchar = oline->lastchar = _NOCHANGE;
274 	if_USE_SCROLL_HINTS(oline->oldindex = i);
275     }
276 
277     /*
278      * Clean up debris from scrolling or resizing the pad, so we do not
279      * accidentally pick up the index value during the next call to this
280      * procedure.  The only rows that should have an index value are those
281      * that are displayed during this cycle.
282      */
283 #if USE_SCROLL_HINTS
284     for (i = pminrow - 1; (i >= 0) && (win->_line[i].oldindex >= 0); i--)
285 	win->_line[i].oldindex = _NEWINDEX;
286     for (i = pmaxrow + 1; (i <= win->_maxy)
287 	 && (win->_line[i].oldindex >= 0); i++)
288 	win->_line[i].oldindex = _NEWINDEX;
289 #endif
290 
291     win->_begx = (NCURSES_SIZE_T) smincol;
292     win->_begy = (NCURSES_SIZE_T) sminrow;
293 
294     if (win->_clear) {
295 	win->_clear = FALSE;
296 	NewScreen(sp)->_clear = TRUE;
297     }
298 
299     /*
300      * Use the pad's current position, if it will be visible.
301      * If not, don't do anything; it is not an error.
302      */
303     if (win->_leaveok == FALSE
304 	&& win->_cury >= pminrow
305 	&& win->_curx >= pmincol
306 	&& win->_cury <= pmaxrow
307 	&& win->_curx <= pmaxcol) {
308 	NewScreen(sp)->_cury = (NCURSES_SIZE_T) (win->_cury - pminrow
309 						 + win->_begy + win->_yoffset);
310 	NewScreen(sp)->_curx = (NCURSES_SIZE_T) (win->_curx - pmincol
311 						 + win->_begx);
312     }
313     NewScreen(sp)->_leaveok = win->_leaveok;
314     win->_flags &= ~_HASMOVED;
315 
316     /*
317      * Update our cache of the line-numbers that we displayed from the pad.
318      * We will use this on subsequent calls to this function to derive
319      * values to stuff into 'oldindex[]' -- for scrolling optimization.
320      */
321     win->_pad._pad_y = (NCURSES_SIZE_T) pminrow;
322     win->_pad._pad_x = (NCURSES_SIZE_T) pmincol;
323     win->_pad._pad_top = (NCURSES_SIZE_T) sminrow;
324     win->_pad._pad_left = (NCURSES_SIZE_T) smincol;
325     win->_pad._pad_bottom = (NCURSES_SIZE_T) smaxrow;
326     win->_pad._pad_right = (NCURSES_SIZE_T) smaxcol;
327 
328     returnCode(OK);
329 }
330 
331 NCURSES_EXPORT(int)
pechochar(WINDOW * pad,const chtype ch)332 pechochar(WINDOW *pad, const chtype ch)
333 {
334     T((T_CALLED("pechochar(%p, %s)"), (void *) pad, _tracechtype(ch)));
335 
336     if (pad == 0)
337 	returnCode(ERR);
338 
339     if (!IS_PAD(pad))
340 	returnCode(wechochar(pad, ch));
341 
342     waddch(pad, ch);
343     prefresh(pad, pad->_pad._pad_y,
344 	     pad->_pad._pad_x,
345 	     pad->_pad._pad_top,
346 	     pad->_pad._pad_left,
347 	     pad->_pad._pad_bottom,
348 	     pad->_pad._pad_right);
349 
350     returnCode(OK);
351 }
352