ncurses 5.6 - patch 20071013
[ncurses.git] / ncurses / base / resizeterm.c
1 /****************************************************************************
2  * Copyright (c) 1998-2006,2007 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
44 MODULE_ID("$Id: resizeterm.c,v 1.23 2007/10/13 20:12:13 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
55 static int current_lines;
56 static 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
64 static void
65 show_window_sizes(const char *name)
66 {
67     WINDOWLIST *wp;
68
69     _tracef("%s resizing: %2d x %2d (%2d x %2d)", name, LINES, COLS,
70             screen_lines, screen_columns);
71     for (wp = _nc_windows; wp != 0; wp = wp->next) {
72         _tracef("  window %p is %2ld x %2ld at %2ld,%2ld",
73                 &(wp->win),
74                 (long) wp->win._maxy + 1,
75                 (long) wp->win._maxx + 1,
76                 (long) wp->win._begy,
77                 (long) wp->win._begx);
78     }
79 }
80 #endif
81
82 NCURSES_EXPORT(bool)
83 is_term_resized(int ToLines, int ToCols)
84 {
85     T((T_CALLED("is_term_resized(%d, %d)"), ToLines, ToCols));
86     returnCode(ToLines > 0
87                && ToCols > 0
88                && (ToLines != screen_lines
89                    || ToCols != screen_columns));
90 }
91
92 /*
93  * Return the number of levels of child-windows under the current window.
94  */
95 static int
96 child_depth(WINDOW *cmp)
97 {
98     int depth = 0;
99
100     if (cmp != 0) {
101         WINDOWLIST *wp;
102
103         for (wp = _nc_windows; wp != 0; wp = wp->next) {
104             WINDOW *tst = &(wp->win);
105             if (tst->_parent == cmp) {
106                 depth = 1 + child_depth(tst);
107                 break;
108             }
109         }
110     }
111     return depth;
112 }
113
114 /*
115  * Return the number of levels of parent-windows above the current window.
116  */
117 static int
118 parent_depth(WINDOW *cmp)
119 {
120     int depth = 0;
121
122     if (cmp != 0) {
123         WINDOW *tst;
124         while ((tst = cmp->_parent) != 0) {
125             ++depth;
126             cmp = tst;
127         }
128     }
129     return depth;
130 }
131
132 /*
133  * FIXME: must adjust position so it's within the parent!
134  */
135 static int
136 adjust_window(WINDOW *win, int ToLines, int ToCols, int stolen EXTRA_DCLS)
137 {
138     int result;
139     int bottom = CurLines + SP->_topstolen - stolen;
140     int myLines = win->_maxy + 1;
141     int myCols = win->_maxx + 1;
142
143     T((T_CALLED("adjust_window(%p,%d,%d) currently %ldx%ld at %ld,%ld"),
144        win, ToLines, ToCols,
145        (long) getmaxy(win), (long) getmaxx(win),
146        (long) getbegy(win), (long) getbegx(win)));
147
148     if (win->_begy >= bottom) {
149         win->_begy += (ToLines - CurLines);
150     } else {
151         if (myLines == CurLines - stolen
152             && ToLines != CurLines)
153             myLines = ToLines - stolen;
154         else if (myLines == CurLines
155                  && ToLines != CurLines)
156             myLines = ToLines;
157     }
158
159     if (myLines > ToLines)
160         myLines = ToLines;
161
162     if (myCols > ToCols)
163         myCols = ToCols;
164
165     if (myLines == CurLines
166         && ToLines != CurLines)
167         myLines = ToLines;
168
169     if (myCols == CurCols
170         && ToCols != CurCols)
171         myCols = ToCols;
172
173     result = wresize(win, myLines, myCols);
174     returnCode(result);
175 }
176
177 /*
178  * If we're decreasing size, recursively search for windows that have no
179  * children, decrease those to fit, then decrease the containing window, etc.
180  */
181 static int
182 decrease_size(int ToLines, int ToCols, int stolen EXTRA_DCLS)
183 {
184     bool found;
185     int depth = 0;
186     WINDOWLIST *wp;
187
188     T((T_CALLED("decrease_size(%d, %d)"), ToLines, ToCols));
189
190     do {
191         found = FALSE;
192         TR(TRACE_UPDATE, ("decreasing size of windows to %dx%d, depth=%d",
193                           ToLines, ToCols, depth));
194         for (wp = _nc_windows; wp != 0; wp = wp->next) {
195             WINDOW *win = &(wp->win);
196
197             if (!(win->_flags & _ISPAD)) {
198                 if (child_depth(win) == depth) {
199                     found = TRUE;
200                     if (adjust_window(win, ToLines, ToCols,
201                                       stolen EXTRA_ARGS) != OK)
202                         returnCode(ERR);
203                 }
204             }
205         }
206         ++depth;
207     } while (found);
208     returnCode(OK);
209 }
210
211 /*
212  * If we're increasing size, recursively search for windows that have no
213  * parent, increase those to fit, then increase the contained window, etc.
214  */
215 static int
216 increase_size(int ToLines, int ToCols, int stolen EXTRA_DCLS)
217 {
218     bool found;
219     int depth = 0;
220     WINDOWLIST *wp;
221
222     T((T_CALLED("increase_size(%d, %d)"), ToLines, ToCols));
223
224     do {
225         found = FALSE;
226         TR(TRACE_UPDATE, ("increasing size of windows to %dx%d, depth=%d",
227                           ToLines, ToCols, depth));
228         for (wp = _nc_windows; wp != 0; wp = wp->next) {
229             WINDOW *win = &(wp->win);
230
231             if (!(win->_flags & _ISPAD)) {
232                 if (parent_depth(win) == depth) {
233                     found = TRUE;
234                     if (adjust_window(win, ToLines, ToCols,
235                                       stolen EXTRA_ARGS) != OK)
236                         returnCode(ERR);
237                 }
238             }
239         }
240         ++depth;
241     } while (found);
242     returnCode(OK);
243 }
244
245 /*
246  * This function reallocates NCURSES window structures, with no side-effects
247  * such as ungetch().
248  */
249 NCURSES_EXPORT(int)
250 resize_term(int ToLines, int ToCols)
251 {
252     int result = OK EXTRA_ARGS;
253     int was_stolen;
254
255     T((T_CALLED("resize_term(%d,%d) old(%d,%d)"),
256        ToLines, ToCols,
257        screen_lines, screen_columns));
258
259     if (SP == 0) {
260         returnCode(ERR);
261     }
262     was_stolen = (screen_lines - SP->_lines_avail);
263     if (is_term_resized(ToLines, ToCols)) {
264         int myLines = CurLines = screen_lines;
265         int myCols = CurCols = screen_columns;
266
267 #ifdef TRACE
268         if (USE_TRACEF(TRACE_UPDATE)) {
269             show_window_sizes("before");
270             _nc_unlock_global(tracef);
271         }
272 #endif
273         if (ToLines > screen_lines) {
274             increase_size(myLines = ToLines, myCols, was_stolen EXTRA_ARGS);
275             CurLines = myLines;
276             CurCols = myCols;
277         }
278
279         if (ToCols > screen_columns) {
280             increase_size(myLines, myCols = ToCols, was_stolen EXTRA_ARGS);
281             CurLines = myLines;
282             CurCols = myCols;
283         }
284
285         if (ToLines < myLines ||
286             ToCols < myCols) {
287             decrease_size(ToLines, ToCols, was_stolen EXTRA_ARGS);
288         }
289
290         screen_lines = lines = ToLines;
291         screen_columns = columns = ToCols;
292
293         SP->_lines_avail = lines - was_stolen;
294
295         if (SP->oldhash) {
296             FreeAndNull(SP->oldhash);
297         }
298         if (SP->newhash) {
299             FreeAndNull(SP->newhash);
300         }
301 #ifdef TRACE
302         if (USE_TRACEF(TRACE_UPDATE)) {
303             SET_LINES(ToLines - was_stolen);
304             SET_COLS(ToCols);
305             show_window_sizes("after");
306             _nc_unlock_global(tracef);
307         }
308 #endif
309     }
310
311     /*
312      * Always update LINES, to allow for call from lib_doupdate.c which
313      * needs to have the count adjusted by the stolen (ripped off) lines.
314      */
315     SET_LINES(ToLines - was_stolen);
316     SET_COLS(ToCols);
317
318     returnCode(result);
319 }
320
321 /*
322  * This function reallocates NCURSES window structures.  It is invoked in
323  * response to a SIGWINCH interrupt.  Other user-defined windows may also need
324  * to be reallocated.
325  *
326  * Because this performs memory allocation, it should not (in general) be
327  * invoked directly from the signal handler.
328  */
329 NCURSES_EXPORT(int)
330 resizeterm(int ToLines, int ToCols)
331 {
332     int result = ERR;
333
334     T((T_CALLED("resizeterm(%d,%d) old(%d,%d)"),
335        ToLines, ToCols,
336        screen_lines, screen_columns));
337
338     if (SP != 0) {
339         result = OK;
340         SP->_sig_winch = FALSE;
341
342         if (is_term_resized(ToLines, ToCols)) {
343
344 #if USE_SIGWINCH
345             ungetch(KEY_RESIZE);        /* so application can know this */
346             clearok(curscr, TRUE);      /* screen contents are unknown */
347 #endif
348
349             result = resize_term(ToLines, ToCols);
350         }
351     }
352
353     returnCode(result);
354 }