xref: /openbsd/lib/libcurses/base/resizeterm.c (revision 09467b48)
1 /* $OpenBSD: resizeterm.c,v 1.3 2010/01/12 23:22:06 nicm Exp $ */
2 
3 /****************************************************************************
4  * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
5  *                                                                          *
6  * Permission is hereby granted, free of charge, to any person obtaining a  *
7  * copy of this software and associated documentation files (the            *
8  * "Software"), to deal in the Software without restriction, including      *
9  * without limitation the rights to use, copy, modify, merge, publish,      *
10  * distribute, distribute with modifications, sublicense, and/or sell       *
11  * copies of the Software, and to permit persons to whom the Software is    *
12  * furnished to do so, subject to the following conditions:                 *
13  *                                                                          *
14  * The above copyright notice and this permission notice shall be included  *
15  * in all copies or substantial portions of the Software.                   *
16  *                                                                          *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24  *                                                                          *
25  * Except as contained in this notice, the name(s) of the above copyright   *
26  * holders shall not be used in advertising or otherwise to promote the     *
27  * sale, use or other dealings in this Software without prior written       *
28  * authorization.                                                           *
29  ****************************************************************************/
30 
31 /****************************************************************************
32  *  Author: Thomas E. Dickey                                                *
33  ****************************************************************************/
34 
35 /*
36  * This is an extension to the curses library.  It provides callers with a hook
37  * into the NCURSES data to resize windows, primarily for use by programs
38  * running in an X Window terminal (e.g., xterm).  I abstracted this module
39  * from my application library for NCURSES because it must be compiled with
40  * the private data structures -- T.Dickey 1995/7/4.
41  */
42 
43 #include <curses.priv.h>
44 #include <term.h>
45 
46 MODULE_ID("$Id: resizeterm.c,v 1.3 2010/01/12 23:22:06 nicm Exp $")
47 
48 #define stolen_lines (screen_lines - SP->_lines_avail)
49 
50 /*
51  * If we're trying to be reentrant, do not want any local statics.
52  */
53 #if USE_REENTRANT
54 #define EXTRA_ARGS ,     CurLines,     CurCols
55 #define EXTRA_DCLS , int CurLines, int CurCols
56 #else
57 static int current_lines;
58 static int current_cols;
59 #define CurLines current_lines
60 #define CurCols  current_cols
61 #define EXTRA_ARGS		/* nothing */
62 #define EXTRA_DCLS		/* nothing */
63 #endif
64 
65 #ifdef TRACE
66 static void
67 show_window_sizes(const char *name)
68 {
69     WINDOWLIST *wp;
70 
71     _nc_lock_global(curses);
72     _tracef("%s resizing: %2d x %2d (%2d x %2d)", name, LINES, COLS,
73 	    screen_lines, screen_columns);
74     for (each_window(wp)) {
75 	_tracef("  window %p is %2ld x %2ld at %2ld,%2ld",
76 		&(wp->win),
77 		(long) wp->win._maxy + 1,
78 		(long) wp->win._maxx + 1,
79 		(long) wp->win._begy,
80 		(long) wp->win._begx);
81     }
82     _nc_unlock_global(curses);
83 }
84 #endif
85 
86 /*
87  * Return true if the given dimensions do not match the internal terminal
88  * structure's size.
89  */
90 NCURSES_EXPORT(bool)
91 is_term_resized(int ToLines, int ToCols)
92 {
93     T((T_CALLED("is_term_resized(%d, %d)"), ToLines, ToCols));
94     returnCode(ToLines > 0
95 	       && ToCols > 0
96 	       && (ToLines != screen_lines
97 		   || ToCols != screen_columns));
98 }
99 
100 /*
101  */
102 static ripoff_t *
103 ripped_window(WINDOW *win)
104 {
105     ripoff_t *result = 0;
106     ripoff_t *rop;
107 
108     if (win != 0) {
109 	for (each_ripoff(rop)) {
110 	    if (rop->win == win && rop->line != 0) {
111 		result = rop;
112 		break;
113 	    }
114 	}
115     }
116     return result;
117 }
118 
119 /*
120  * Returns the number of lines from the bottom for the beginning of a ripped
121  * off window.
122  */
123 static int
124 ripped_bottom(WINDOW *win)
125 {
126     int result = 0;
127     ripoff_t *rop;
128 
129     if (win != 0) {
130 	for (each_ripoff(rop)) {
131 	    if (rop->line < 0) {
132 		result -= rop->line;
133 		if (rop->win == win) {
134 		    break;
135 		}
136 	    }
137 	}
138     }
139     return result;
140 }
141 
142 /*
143  * Return the number of levels of child-windows under the current window.
144  */
145 static int
146 child_depth(WINDOW *cmp)
147 {
148     int depth = 0;
149 
150     if (cmp != 0) {
151 	WINDOWLIST *wp;
152 
153 	for (each_window(wp)) {
154 	    WINDOW *tst = &(wp->win);
155 	    if (tst->_parent == cmp) {
156 		depth = 1 + child_depth(tst);
157 		break;
158 	    }
159 	}
160     }
161     return depth;
162 }
163 
164 /*
165  * Return the number of levels of parent-windows above the current window.
166  */
167 static int
168 parent_depth(WINDOW *cmp)
169 {
170     int depth = 0;
171 
172     if (cmp != 0) {
173 	WINDOW *tst;
174 	while ((tst = cmp->_parent) != 0) {
175 	    ++depth;
176 	    cmp = tst;
177 	}
178     }
179     return depth;
180 }
181 
182 /*
183  * FIXME: must adjust position so it's within the parent!
184  */
185 static int
186 adjust_window(WINDOW *win, int ToLines, int ToCols, int stolen EXTRA_DCLS)
187 {
188     int result;
189     int bottom = CurLines + SP->_topstolen - stolen;
190     int myLines = win->_maxy + 1;
191     int myCols = win->_maxx + 1;
192     ripoff_t *rop = ripped_window(win);
193 
194     T((T_CALLED("adjust_window(%p,%d,%d)%s depth %d/%d currently %ldx%ld at %ld,%ld"),
195        win, ToLines, ToCols,
196        (rop != 0) ? " (rip)" : "",
197        parent_depth(win),
198        child_depth(win),
199        (long) getmaxy(win), (long) getmaxx(win),
200        (long) getbegy(win) + win->_yoffset, (long) getbegx(win)));
201 
202     if (rop != 0 && rop->line < 0) {
203 	/*
204 	 * If it is a ripped-off window at the bottom of the screen, simply
205 	 * move it to the same relative position.
206 	 */
207 	win->_begy = ToLines - ripped_bottom(win) - 0 - win->_yoffset;
208     } else if (win->_begy >= bottom) {
209 	/*
210 	 * If it is below the bottom of the new screen, move up by the same
211 	 * amount that the screen shrank.
212 	 */
213 	win->_begy += (ToLines - CurLines);
214     } else {
215 	if (myLines == (CurLines - stolen)
216 	    && ToLines != CurLines) {
217 	    myLines = ToLines - stolen;
218 	} else if (myLines == CurLines
219 		   && ToLines != CurLines) {
220 	    myLines = ToLines;
221 	}
222     }
223 
224     if (myLines > ToLines) {
225 	myLines = ToLines;
226     }
227 
228     if (myCols > ToCols)
229 	myCols = ToCols;
230 
231     if (myCols == CurCols
232 	&& ToCols != CurCols)
233 	myCols = ToCols;
234 
235     result = wresize(win, myLines, myCols);
236     returnCode(result);
237 }
238 
239 /*
240  * If we're decreasing size, recursively search for windows that have no
241  * children, decrease those to fit, then decrease the containing window, etc.
242  */
243 static int
244 decrease_size(int ToLines, int ToCols, int stolen EXTRA_DCLS)
245 {
246     bool found;
247     int depth = 0;
248     WINDOWLIST *wp;
249 
250     T((T_CALLED("decrease_size(%d, %d)"), ToLines, ToCols));
251 
252     do {
253 	found = FALSE;
254 	TR(TRACE_UPDATE, ("decreasing size of windows to %dx%d, depth=%d",
255 			  ToLines, ToCols, depth));
256 	for (each_window(wp)) {
257 	    WINDOW *win = &(wp->win);
258 
259 	    if (!(win->_flags & _ISPAD)) {
260 		if (child_depth(win) == depth) {
261 		    found = TRUE;
262 		    if (adjust_window(win, ToLines, ToCols,
263 				      stolen EXTRA_ARGS) != OK)
264 			returnCode(ERR);
265 		}
266 	    }
267 	}
268 	++depth;
269     } while (found);
270     returnCode(OK);
271 }
272 
273 /*
274  * If we're increasing size, recursively search for windows that have no
275  * parent, increase those to fit, then increase the contained window, etc.
276  */
277 static int
278 increase_size(int ToLines, int ToCols, int stolen EXTRA_DCLS)
279 {
280     bool found;
281     int depth = 0;
282     WINDOWLIST *wp;
283 
284     T((T_CALLED("increase_size(%d, %d)"), ToLines, ToCols));
285 
286     do {
287 	found = FALSE;
288 	TR(TRACE_UPDATE, ("increasing size of windows to %dx%d, depth=%d",
289 			  ToLines, ToCols, depth));
290 	for (each_window(wp)) {
291 	    WINDOW *win = &(wp->win);
292 
293 	    if (!(win->_flags & _ISPAD)) {
294 		if (parent_depth(win) == depth) {
295 		    found = TRUE;
296 		    if (adjust_window(win, ToLines, ToCols,
297 				      stolen EXTRA_ARGS) != OK)
298 			returnCode(ERR);
299 		}
300 	    }
301 	}
302 	++depth;
303     } while (found);
304     returnCode(OK);
305 }
306 
307 /*
308  * This function reallocates NCURSES window structures, with no side-effects
309  * such as ungetch().
310  */
311 NCURSES_EXPORT(int)
312 resize_term(int ToLines, int ToCols)
313 {
314     int result = OK EXTRA_ARGS;
315     int was_stolen;
316 
317     T((T_CALLED("resize_term(%d,%d) old(%d,%d)"),
318        ToLines, ToCols,
319        screen_lines, screen_columns));
320 
321     if (SP == 0) {
322 	returnCode(ERR);
323     }
324 
325     _nc_lock_global(curses);
326 
327     was_stolen = (screen_lines - SP->_lines_avail);
328     if (is_term_resized(ToLines, ToCols)) {
329 	int myLines = CurLines = screen_lines;
330 	int myCols = CurCols = screen_columns;
331 
332 #ifdef TRACE
333 	if (USE_TRACEF(TRACE_UPDATE)) {
334 	    show_window_sizes("before");
335 	    _nc_unlock_global(tracef);
336 	}
337 #endif
338 	if (ToLines > screen_lines) {
339 	    increase_size(myLines = ToLines, myCols, was_stolen EXTRA_ARGS);
340 	    CurLines = myLines;
341 	    CurCols = myCols;
342 	}
343 
344 	if (ToCols > screen_columns) {
345 	    increase_size(myLines, myCols = ToCols, was_stolen EXTRA_ARGS);
346 	    CurLines = myLines;
347 	    CurCols = myCols;
348 	}
349 
350 	if (ToLines < myLines ||
351 	    ToCols < myCols) {
352 	    decrease_size(ToLines, ToCols, was_stolen EXTRA_ARGS);
353 	}
354 
355 	screen_lines = lines = ToLines;
356 	screen_columns = columns = ToCols;
357 
358 	SP->_lines_avail = lines - was_stolen;
359 
360 	if (SP->oldhash) {
361 	    FreeAndNull(SP->oldhash);
362 	}
363 	if (SP->newhash) {
364 	    FreeAndNull(SP->newhash);
365 	}
366 #ifdef TRACE
367 	if (USE_TRACEF(TRACE_UPDATE)) {
368 	    SET_LINES(ToLines - was_stolen);
369 	    SET_COLS(ToCols);
370 	    show_window_sizes("after");
371 	    _nc_unlock_global(tracef);
372 	}
373 #endif
374     }
375 
376     /*
377      * Always update LINES, to allow for call from lib_doupdate.c which
378      * needs to have the count adjusted by the stolen (ripped off) lines.
379      */
380     SET_LINES(ToLines - was_stolen);
381     SET_COLS(ToCols);
382 
383     _nc_unlock_global(curses);
384 
385     returnCode(result);
386 }
387 
388 /*
389  * This function reallocates NCURSES window structures.  It is invoked in
390  * response to a SIGWINCH interrupt.  Other user-defined windows may also need
391  * to be reallocated.
392  *
393  * Because this performs memory allocation, it should not (in general) be
394  * invoked directly from the signal handler.
395  */
396 NCURSES_EXPORT(int)
397 resizeterm(int ToLines, int ToCols)
398 {
399     int result = ERR;
400 
401     T((T_CALLED("resizeterm(%d,%d) old(%d,%d)"),
402        ToLines, ToCols,
403        screen_lines, screen_columns));
404 
405     if (SP != 0) {
406 	result = OK;
407 	SP->_sig_winch = FALSE;
408 
409 	if (is_term_resized(ToLines, ToCols)) {
410 #if USE_SIGWINCH
411 	    ripoff_t *rop;
412 	    bool slk_visible = (SP != 0
413 				&& SP->_slk != 0
414 				&& !(SP->_slk->hidden));
415 
416 	    if (slk_visible) {
417 		slk_clear();
418 	    }
419 #endif
420 	    result = resize_term(ToLines, ToCols);
421 
422 #if USE_SIGWINCH
423 	    _nc_ungetch(SP, KEY_RESIZE);	/* so application can know this */
424 	    clearok(curscr, TRUE);	/* screen contents are unknown */
425 
426 	    /* ripped-off lines are a special case: if we did not lengthen
427 	     * them, we haven't moved them either.  repaint them, too.
428 	     *
429 	     * for the rest - stdscr and other windows - the client has to
430 	     * decide which to repaint, since without panels, ncurses does
431 	     * not know which are really on top.
432 	     */
433 	    for (each_ripoff(rop)) {
434 		if (rop->win != stdscr
435 		    && rop->win != 0
436 		    && rop->line < 0) {
437 
438 		    if (rop->hook != _nc_slk_initialize) {
439 			touchwin(rop->win);
440 			wnoutrefresh(rop->win);
441 		    }
442 		}
443 	    }
444 
445 	    /* soft-keys are a special case: we _know_ how to repaint them */
446 	    if (slk_visible) {
447 		slk_restore();
448 		slk_touch();
449 
450 		slk_refresh();
451 	    }
452 #endif
453 	}
454     }
455 
456     returnCode(result);
457 }
458