150276Speter/****************************************************************************
2176187Srafan * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
350276Speter *                                                                          *
450276Speter * Permission is hereby granted, free of charge, to any person obtaining a  *
550276Speter * copy of this software and associated documentation files (the            *
650276Speter * "Software"), to deal in the Software without restriction, including      *
750276Speter * without limitation the rights to use, copy, modify, merge, publish,      *
850276Speter * distribute, distribute with modifications, sublicense, and/or sell       *
950276Speter * copies of the Software, and to permit persons to whom the Software is    *
1050276Speter * furnished to do so, subject to the following conditions:                 *
1150276Speter *                                                                          *
1250276Speter * The above copyright notice and this permission notice shall be included  *
1350276Speter * in all copies or substantial portions of the Software.                   *
1450276Speter *                                                                          *
1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2250276Speter *                                                                          *
2350276Speter * Except as contained in this notice, the name(s) of the above copyright   *
2450276Speter * holders shall not be used in advertising or otherwise to promote the     *
2550276Speter * sale, use or other dealings in this Software without prior written       *
2650276Speter * authorization.                                                           *
2750276Speter ****************************************************************************/
2850276Speter
2950276Speter/****************************************************************************
30166124Srafan *  Author: Thomas E. Dickey                                                *
3150276Speter ****************************************************************************/
3250276Speter
3350276Speter/*
3450276Speter * This is an extension to the curses library.  It provides callers with a hook
3550276Speter * into the NCURSES data to resize windows, primarily for use by programs
3650276Speter * running in an X Window terminal (e.g., xterm).  I abstracted this module
3750276Speter * from my application library for NCURSES because it must be compiled with
3850276Speter * the private data structures -- T.Dickey 1995/7/4.
3950276Speter */
4050276Speter
4150276Speter#include <curses.priv.h>
4250276Speter#include <term.h>
4350276Speter
44184989SrafanMODULE_ID("$Id: resizeterm.c,v 1.34 2008/06/07 13:58:40 tom Exp $")
4550276Speter
46166124Srafan#define stolen_lines (screen_lines - SP->_lines_avail)
47166124Srafan
48174993Srafan/*
49174993Srafan * If we're trying to be reentrant, do not want any local statics.
50174993Srafan */
51174993Srafan#if USE_REENTRANT
52174993Srafan#define EXTRA_ARGS ,     CurLines,     CurCols
53174993Srafan#define EXTRA_DCLS , int CurLines, int CurCols
54174993Srafan#else
55166124Srafanstatic int current_lines;
56166124Srafanstatic int current_cols;
57174993Srafan#define CurLines current_lines
58174993Srafan#define CurCols  current_cols
59174993Srafan#define EXTRA_ARGS		/* nothing */
60174993Srafan#define EXTRA_DCLS		/* nothing */
61174993Srafan#endif
62166124Srafan
63166124Srafan#ifdef TRACE
64166124Srafanstatic void
65166124Srafanshow_window_sizes(const char *name)
66166124Srafan{
67166124Srafan    WINDOWLIST *wp;
68166124Srafan
69184989Srafan    _nc_lock_global(curses);
70166124Srafan    _tracef("%s resizing: %2d x %2d (%2d x %2d)", name, LINES, COLS,
71166124Srafan	    screen_lines, screen_columns);
72178866Srafan    for (each_window(wp)) {
73166124Srafan	_tracef("  window %p is %2ld x %2ld at %2ld,%2ld",
74166124Srafan		&(wp->win),
75166124Srafan		(long) wp->win._maxy + 1,
76166124Srafan		(long) wp->win._maxx + 1,
77166124Srafan		(long) wp->win._begy,
78166124Srafan		(long) wp->win._begx);
79166124Srafan    }
80184989Srafan    _nc_unlock_global(curses);
81166124Srafan}
82166124Srafan#endif
83166124Srafan
84176187Srafan/*
85176187Srafan * Return true if the given dimensions do not match the internal terminal
86176187Srafan * structure's size.
87176187Srafan */
8897049SpeterNCURSES_EXPORT(bool)
8997049Speteris_term_resized(int ToLines, int ToCols)
9097049Speter{
91166124Srafan    T((T_CALLED("is_term_resized(%d, %d)"), ToLines, ToCols));
92166124Srafan    returnCode(ToLines > 0
93166124Srafan	       && ToCols > 0
94166124Srafan	       && (ToLines != screen_lines
95166124Srafan		   || ToCols != screen_columns));
9697049Speter}
9797049Speter
9850276Speter/*
99176187Srafan */
100176187Srafanstatic ripoff_t *
101176187Srafanripped_window(WINDOW *win)
102176187Srafan{
103176187Srafan    ripoff_t *result = 0;
104176187Srafan    ripoff_t *rop;
105176187Srafan
106176187Srafan    if (win != 0) {
107178866Srafan	for (each_ripoff(rop)) {
108176187Srafan	    if (rop->win == win && rop->line != 0) {
109176187Srafan		result = rop;
110176187Srafan		break;
111176187Srafan	    }
112176187Srafan	}
113176187Srafan    }
114176187Srafan    return result;
115176187Srafan}
116176187Srafan
117176187Srafan/*
118176187Srafan * Returns the number of lines from the bottom for the beginning of a ripped
119176187Srafan * off window.
120176187Srafan */
121176187Srafanstatic int
122176187Srafanripped_bottom(WINDOW *win)
123176187Srafan{
124176187Srafan    int result = 0;
125176187Srafan    ripoff_t *rop;
126176187Srafan
127176187Srafan    if (win != 0) {
128178866Srafan	for (each_ripoff(rop)) {
129176187Srafan	    if (rop->line < 0) {
130176187Srafan		result -= rop->line;
131176187Srafan		if (rop->win == win) {
132176187Srafan		    break;
133176187Srafan		}
134176187Srafan	    }
135176187Srafan	}
136176187Srafan    }
137176187Srafan    return result;
138176187Srafan}
139176187Srafan
140176187Srafan/*
141166124Srafan * Return the number of levels of child-windows under the current window.
142166124Srafan */
143166124Srafanstatic int
144166124Srafanchild_depth(WINDOW *cmp)
145166124Srafan{
146166124Srafan    int depth = 0;
147166124Srafan
148166124Srafan    if (cmp != 0) {
149166124Srafan	WINDOWLIST *wp;
150166124Srafan
151178866Srafan	for (each_window(wp)) {
152166124Srafan	    WINDOW *tst = &(wp->win);
153166124Srafan	    if (tst->_parent == cmp) {
154166124Srafan		depth = 1 + child_depth(tst);
155166124Srafan		break;
156166124Srafan	    }
157166124Srafan	}
158166124Srafan    }
159166124Srafan    return depth;
160166124Srafan}
161166124Srafan
162166124Srafan/*
163166124Srafan * Return the number of levels of parent-windows above the current window.
164166124Srafan */
165166124Srafanstatic int
166166124Srafanparent_depth(WINDOW *cmp)
167166124Srafan{
168166124Srafan    int depth = 0;
169166124Srafan
170166124Srafan    if (cmp != 0) {
171166124Srafan	WINDOW *tst;
172166124Srafan	while ((tst = cmp->_parent) != 0) {
173166124Srafan	    ++depth;
174166124Srafan	    cmp = tst;
175166124Srafan	}
176166124Srafan    }
177166124Srafan    return depth;
178166124Srafan}
179166124Srafan
180166124Srafan/*
181166124Srafan * FIXME: must adjust position so it's within the parent!
182166124Srafan */
183166124Srafanstatic int
184174993Srafanadjust_window(WINDOW *win, int ToLines, int ToCols, int stolen EXTRA_DCLS)
185166124Srafan{
186166124Srafan    int result;
187174993Srafan    int bottom = CurLines + SP->_topstolen - stolen;
188166124Srafan    int myLines = win->_maxy + 1;
189166124Srafan    int myCols = win->_maxx + 1;
190176187Srafan    ripoff_t *rop = ripped_window(win);
191166124Srafan
192176187Srafan    T((T_CALLED("adjust_window(%p,%d,%d)%s depth %d/%d currently %ldx%ld at %ld,%ld"),
193166124Srafan       win, ToLines, ToCols,
194176187Srafan       (rop != 0) ? " (rip)" : "",
195176187Srafan       parent_depth(win),
196176187Srafan       child_depth(win),
197166124Srafan       (long) getmaxy(win), (long) getmaxx(win),
198176187Srafan       (long) getbegy(win) + win->_yoffset, (long) getbegx(win)));
199166124Srafan
200176187Srafan    if (rop != 0 && rop->line < 0) {
201176187Srafan	/*
202176187Srafan	 * If it is a ripped-off window at the bottom of the screen, simply
203176187Srafan	 * move it to the same relative position.
204176187Srafan	 */
205176187Srafan	win->_begy = ToLines - ripped_bottom(win) - 0 - win->_yoffset;
206176187Srafan    } else if (win->_begy >= bottom) {
207176187Srafan	/*
208176187Srafan	 * If it is below the bottom of the new screen, move up by the same
209176187Srafan	 * amount that the screen shrank.
210176187Srafan	 */
211174993Srafan	win->_begy += (ToLines - CurLines);
212166124Srafan    } else {
213176187Srafan	if (myLines == (CurLines - stolen)
214176187Srafan	    && ToLines != CurLines) {
215166124Srafan	    myLines = ToLines - stolen;
216176187Srafan	} else if (myLines == CurLines
217176187Srafan		   && ToLines != CurLines) {
218166124Srafan	    myLines = ToLines;
219176187Srafan	}
220166124Srafan    }
221166124Srafan
222176187Srafan    if (myLines > ToLines) {
223166124Srafan	myLines = ToLines;
224176187Srafan    }
225166124Srafan
226166124Srafan    if (myCols > ToCols)
227166124Srafan	myCols = ToCols;
228166124Srafan
229174993Srafan    if (myCols == CurCols
230174993Srafan	&& ToCols != CurCols)
231166124Srafan	myCols = ToCols;
232166124Srafan
233166124Srafan    result = wresize(win, myLines, myCols);
234166124Srafan    returnCode(result);
235166124Srafan}
236166124Srafan
237166124Srafan/*
238166124Srafan * If we're decreasing size, recursively search for windows that have no
239166124Srafan * children, decrease those to fit, then decrease the containing window, etc.
240166124Srafan */
241166124Srafanstatic int
242174993Srafandecrease_size(int ToLines, int ToCols, int stolen EXTRA_DCLS)
243166124Srafan{
244166124Srafan    bool found;
245166124Srafan    int depth = 0;
246166124Srafan    WINDOWLIST *wp;
247166124Srafan
248166124Srafan    T((T_CALLED("decrease_size(%d, %d)"), ToLines, ToCols));
249166124Srafan
250166124Srafan    do {
251166124Srafan	found = FALSE;
252166124Srafan	TR(TRACE_UPDATE, ("decreasing size of windows to %dx%d, depth=%d",
253166124Srafan			  ToLines, ToCols, depth));
254178866Srafan	for (each_window(wp)) {
255166124Srafan	    WINDOW *win = &(wp->win);
256166124Srafan
257166124Srafan	    if (!(win->_flags & _ISPAD)) {
258166124Srafan		if (child_depth(win) == depth) {
259166124Srafan		    found = TRUE;
260174993Srafan		    if (adjust_window(win, ToLines, ToCols,
261174993Srafan				      stolen EXTRA_ARGS) != OK)
262166124Srafan			returnCode(ERR);
263166124Srafan		}
264166124Srafan	    }
265166124Srafan	}
266166124Srafan	++depth;
267166124Srafan    } while (found);
268166124Srafan    returnCode(OK);
269166124Srafan}
270166124Srafan
271166124Srafan/*
272166124Srafan * If we're increasing size, recursively search for windows that have no
273166124Srafan * parent, increase those to fit, then increase the contained window, etc.
274166124Srafan */
275166124Srafanstatic int
276174993Srafanincrease_size(int ToLines, int ToCols, int stolen EXTRA_DCLS)
277166124Srafan{
278166124Srafan    bool found;
279166124Srafan    int depth = 0;
280166124Srafan    WINDOWLIST *wp;
281166124Srafan
282166124Srafan    T((T_CALLED("increase_size(%d, %d)"), ToLines, ToCols));
283166124Srafan
284166124Srafan    do {
285166124Srafan	found = FALSE;
286166124Srafan	TR(TRACE_UPDATE, ("increasing size of windows to %dx%d, depth=%d",
287166124Srafan			  ToLines, ToCols, depth));
288178866Srafan	for (each_window(wp)) {
289166124Srafan	    WINDOW *win = &(wp->win);
290166124Srafan
291166124Srafan	    if (!(win->_flags & _ISPAD)) {
292166124Srafan		if (parent_depth(win) == depth) {
293166124Srafan		    found = TRUE;
294174993Srafan		    if (adjust_window(win, ToLines, ToCols,
295174993Srafan				      stolen EXTRA_ARGS) != OK)
296166124Srafan			returnCode(ERR);
297166124Srafan		}
298166124Srafan	    }
299166124Srafan	}
300166124Srafan	++depth;
301166124Srafan    } while (found);
302166124Srafan    returnCode(OK);
303166124Srafan}
304166124Srafan
305166124Srafan/*
30697049Speter * This function reallocates NCURSES window structures, with no side-effects
30797049Speter * such as ungetch().
30850276Speter */
30976726SpeterNCURSES_EXPORT(int)
31097049Speterresize_term(int ToLines, int ToCols)
31150276Speter{
312174993Srafan    int result = OK EXTRA_ARGS;
313174993Srafan    int was_stolen;
31450276Speter
31597049Speter    T((T_CALLED("resize_term(%d,%d) old(%d,%d)"),
31676726Speter       ToLines, ToCols,
31776726Speter       screen_lines, screen_columns));
31850276Speter
319174993Srafan    if (SP == 0) {
320174993Srafan	returnCode(ERR);
321174993Srafan    }
322174993Srafan
323184989Srafan    _nc_lock_global(curses);
324174993Srafan
325174993Srafan    was_stolen = (screen_lines - SP->_lines_avail);
32697049Speter    if (is_term_resized(ToLines, ToCols)) {
327174993Srafan	int myLines = CurLines = screen_lines;
328174993Srafan	int myCols = CurCols = screen_columns;
32950276Speter
330166124Srafan#ifdef TRACE
331174993Srafan	if (USE_TRACEF(TRACE_UPDATE)) {
332166124Srafan	    show_window_sizes("before");
333174993Srafan	    _nc_unlock_global(tracef);
334174993Srafan	}
335166124Srafan#endif
336166124Srafan	if (ToLines > screen_lines) {
337174993Srafan	    increase_size(myLines = ToLines, myCols, was_stolen EXTRA_ARGS);
338174993Srafan	    CurLines = myLines;
339174993Srafan	    CurCols = myCols;
340166124Srafan	}
34150276Speter
342166124Srafan	if (ToCols > screen_columns) {
343174993Srafan	    increase_size(myLines, myCols = ToCols, was_stolen EXTRA_ARGS);
344174993Srafan	    CurLines = myLines;
345174993Srafan	    CurCols = myCols;
346166124Srafan	}
34750276Speter
348166124Srafan	if (ToLines < myLines ||
349166124Srafan	    ToCols < myCols) {
350174993Srafan	    decrease_size(ToLines, ToCols, was_stolen EXTRA_ARGS);
35176726Speter	}
35250276Speter
35376726Speter	screen_lines = lines = ToLines;
35476726Speter	screen_columns = columns = ToCols;
35550276Speter
356166124Srafan	SP->_lines_avail = lines - was_stolen;
35750276Speter
35876726Speter	if (SP->oldhash) {
35976726Speter	    FreeAndNull(SP->oldhash);
36050276Speter	}
36176726Speter	if (SP->newhash) {
36276726Speter	    FreeAndNull(SP->newhash);
36376726Speter	}
364166124Srafan#ifdef TRACE
365174993Srafan	if (USE_TRACEF(TRACE_UPDATE)) {
366174993Srafan	    SET_LINES(ToLines - was_stolen);
367174993Srafan	    SET_COLS(ToCols);
368166124Srafan	    show_window_sizes("after");
369174993Srafan	    _nc_unlock_global(tracef);
370166124Srafan	}
371166124Srafan#endif
37276726Speter    }
37350276Speter
37476726Speter    /*
37576726Speter     * Always update LINES, to allow for call from lib_doupdate.c which
37676726Speter     * needs to have the count adjusted by the stolen (ripped off) lines.
37776726Speter     */
378174993Srafan    SET_LINES(ToLines - was_stolen);
379174993Srafan    SET_COLS(ToCols);
38050276Speter
381184989Srafan    _nc_unlock_global(curses);
382174993Srafan
383166124Srafan    returnCode(result);
38450276Speter}
38597049Speter
38697049Speter/*
38797049Speter * This function reallocates NCURSES window structures.  It is invoked in
38897049Speter * response to a SIGWINCH interrupt.  Other user-defined windows may also need
38997049Speter * to be reallocated.
39097049Speter *
39197049Speter * Because this performs memory allocation, it should not (in general) be
39297049Speter * invoked directly from the signal handler.
39397049Speter */
39497049SpeterNCURSES_EXPORT(int)
39597049Speterresizeterm(int ToLines, int ToCols)
39697049Speter{
397174993Srafan    int result = ERR;
39897049Speter
39997049Speter    T((T_CALLED("resizeterm(%d,%d) old(%d,%d)"),
40097049Speter       ToLines, ToCols,
40197049Speter       screen_lines, screen_columns));
40297049Speter
403174993Srafan    if (SP != 0) {
404174993Srafan	result = OK;
405174993Srafan	SP->_sig_winch = FALSE;
40697049Speter
407174993Srafan	if (is_term_resized(ToLines, ToCols)) {
408176187Srafan#if USE_SIGWINCH
409176187Srafan	    ripoff_t *rop;
410176187Srafan	    bool slk_visible = (SP != 0
411176187Srafan				&& SP->_slk != 0
412176187Srafan				&& !(SP->_slk->hidden));
413174993Srafan
414176187Srafan	    if (slk_visible) {
415176187Srafan		slk_clear();
416176187Srafan	    }
417176187Srafan#endif
418176187Srafan	    result = resize_term(ToLines, ToCols);
419176187Srafan
42097049Speter#if USE_SIGWINCH
421184989Srafan	    _nc_ungetch(SP, KEY_RESIZE);	/* so application can know this */
422174993Srafan	    clearok(curscr, TRUE);	/* screen contents are unknown */
423176187Srafan
424176187Srafan	    /* ripped-off lines are a special case: if we did not lengthen
425176187Srafan	     * them, we haven't moved them either.  repaint them, too.
426176187Srafan	     *
427176187Srafan	     * for the rest - stdscr and other windows - the client has to
428176187Srafan	     * decide which to repaint, since without panels, ncurses does
429176187Srafan	     * not know which are really on top.
430176187Srafan	     */
431178866Srafan	    for (each_ripoff(rop)) {
432176187Srafan		if (rop->win != stdscr
433176187Srafan		    && rop->win != 0
434176187Srafan		    && rop->line < 0) {
435176187Srafan
436176187Srafan		    if (rop->hook != _nc_slk_initialize) {
437176187Srafan			touchwin(rop->win);
438176187Srafan			wnoutrefresh(rop->win);
439176187Srafan		    }
440176187Srafan		}
441176187Srafan	    }
442176187Srafan
443176187Srafan	    /* soft-keys are a special case: we _know_ how to repaint them */
444176187Srafan	    if (slk_visible) {
445176187Srafan		slk_restore();
446176187Srafan		slk_touch();
447176187Srafan
448176187Srafan		slk_refresh();
449176187Srafan	    }
45097049Speter#endif
451174993Srafan	}
45297049Speter    }
45397049Speter
45497049Speter    returnCode(result);
45597049Speter}
456