062af604a1fde34a26efb535e6f44b5900554e25
[ncurses.git] / ncurses / base / resizeterm.c
1 /****************************************************************************
2  * Copyright (c) 1998-2004,2006 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.19 2006/12/30 17:12:29 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 #ifdef _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 = (screen_lines - SP->_lines_avail);
254
255     T((T_CALLED("resize_term(%d,%d) old(%d,%d)"),
256        ToLines, ToCols,
257        screen_lines, screen_columns));
258
259     if (is_term_resized(ToLines, ToCols)) {
260         int myLines = CurLines = screen_lines;
261         int myCols = CurCols = screen_columns;
262
263 #ifdef TRACE
264         if (_nc_tracing & TRACE_UPDATE)
265             show_window_sizes("before");
266 #endif
267         if (ToLines > screen_lines) {
268             increase_size(myLines = ToLines, myCols, was_stolen EXTRA_ARGS);
269             CurLines = myLines;
270             CurCols = myCols;
271         }
272
273         if (ToCols > screen_columns) {
274             increase_size(myLines, myCols = ToCols, was_stolen EXTRA_ARGS);
275             CurLines = myLines;
276             CurCols = myCols;
277         }
278
279         if (ToLines < myLines ||
280             ToCols < myCols) {
281             decrease_size(ToLines, ToCols, was_stolen EXTRA_ARGS);
282         }
283
284         screen_lines = lines = ToLines;
285         screen_columns = columns = ToCols;
286
287         SP->_lines_avail = lines - was_stolen;
288
289         if (SP->oldhash) {
290             FreeAndNull(SP->oldhash);
291         }
292         if (SP->newhash) {
293             FreeAndNull(SP->newhash);
294         }
295 #ifdef TRACE
296         if (_nc_tracing & TRACE_UPDATE) {
297             LINES = ToLines - was_stolen;
298             COLS = ToCols;
299             show_window_sizes("after");
300         }
301 #endif
302     }
303
304     /*
305      * Always update LINES, to allow for call from lib_doupdate.c which
306      * needs to have the count adjusted by the stolen (ripped off) lines.
307      */
308     LINES = ToLines - was_stolen;
309     COLS = ToCols;
310
311     returnCode(result);
312 }
313
314 /*
315  * This function reallocates NCURSES window structures.  It is invoked in
316  * response to a SIGWINCH interrupt.  Other user-defined windows may also need
317  * to be reallocated.
318  *
319  * Because this performs memory allocation, it should not (in general) be
320  * invoked directly from the signal handler.
321  */
322 NCURSES_EXPORT(int)
323 resizeterm(int ToLines, int ToCols)
324 {
325     int result = OK;
326
327     SP->_sig_winch = FALSE;
328
329     T((T_CALLED("resizeterm(%d,%d) old(%d,%d)"),
330        ToLines, ToCols,
331        screen_lines, screen_columns));
332
333     if (is_term_resized(ToLines, ToCols)) {
334
335 #if USE_SIGWINCH
336         ungetch(KEY_RESIZE);    /* so application can know this */
337         clearok(curscr, TRUE);  /* screen contents are unknown */
338 #endif
339
340         result = resize_term(ToLines, ToCols);
341     }
342
343     returnCode(result);
344 }