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