ncurses 5.7 - patch 20090510
[ncurses.git] / ncurses / base / resizeterm.c
1 /****************************************************************************
2  * Copyright (c) 1998-2008,2009 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 #ifndef CUR
45 #define CUR SP_TERMTYPE 
46 #endif
47
48 MODULE_ID("$Id: resizeterm.c,v 1.39 2009/05/10 00:48:29 tom Exp $")
49
50 #define stolen_lines (screen_lines - SP->_lines_avail)
51
52 /*
53  * If we're trying to be reentrant, do not want any local statics.
54  */
55 #if USE_REENTRANT
56 #define EXTRA_ARGS ,     CurLines,     CurCols
57 #define EXTRA_DCLS , int CurLines, int CurCols
58 #else
59 static int current_lines;
60 static int current_cols;
61 #define CurLines current_lines
62 #define CurCols  current_cols
63 #define EXTRA_ARGS              /* nothing */
64 #define EXTRA_DCLS              /* nothing */
65 #endif
66
67 #ifdef TRACE
68 static void
69 show_window_sizes(const char *name)
70 {
71     SCREEN *sp;
72     WINDOWLIST *wp;
73
74     _nc_lock_global(curses);
75     for (each_screen(sp)) {
76         _tracef("%s resizing: %p: %2d x %2d (%2d x %2d)", name, sp,
77                 *(ptrLines(sp)),
78                 *(ptrCols(sp)),
79                 screen_lines(sp), screen_columns(sp));
80         for (each_window(sp, wp)) {
81             _tracef("  window %p is %2ld x %2ld at %2ld,%2ld",
82                     &(wp->win),
83                     (long) wp->win._maxy + 1,
84                     (long) wp->win._maxx + 1,
85                     (long) wp->win._begy,
86                     (long) wp->win._begx);
87         }
88     }
89     _nc_unlock_global(curses);
90 }
91 #endif
92
93 /*
94  * Return true if the given dimensions do not match the internal terminal
95  * structure's size.
96  */
97 NCURSES_EXPORT(bool)
98 NCURSES_SP_NAME(is_term_resized) (NCURSES_SP_DCLx int ToLines, int ToCols)
99 {
100     T((T_CALLED("is_term_resized(%p, %d, %d)"), SP_PARM, ToLines, ToCols));
101     returnCode(ToLines > 0
102                && ToCols > 0
103                && (ToLines != screen_lines(SP_PARM)
104                    || ToCols != screen_columns(SP_PARM)));
105 }
106
107 #if NCURSES_SP_FUNCS
108 NCURSES_EXPORT(bool)
109 is_term_resized(int ToLines, int ToCols)
110 {
111     return NCURSES_SP_NAME(is_term_resized) (CURRENT_SCREEN, ToLines, ToCols);
112 }
113 #endif
114
115 /*
116  */
117 static ripoff_t *
118 ripped_window(WINDOW *win)
119 {
120     ripoff_t *result = 0;
121     ripoff_t *rop;
122
123     if (win != 0) {
124 #if NCURSES_SP_FUNCS
125         SCREEN *sp = _nc_screen_of(win);
126 #endif
127         for (each_ripoff(rop)) {
128             if (rop->win == win && rop->line != 0) {
129                 result = rop;
130                 break;
131             }
132         }
133     }
134     return result;
135 }
136
137 /*
138  * Returns the number of lines from the bottom for the beginning of a ripped
139  * off window.
140  */
141 static int
142 ripped_bottom(WINDOW *win)
143 {
144     int result = 0;
145     ripoff_t *rop;
146
147     if (win != 0) {
148 #if NCURSES_SP_FUNCS
149         SCREEN *sp = _nc_screen_of(win);
150 #endif
151         for (each_ripoff(rop)) {
152             if (rop->line < 0) {
153                 result -= rop->line;
154                 if (rop->win == win) {
155                     break;
156                 }
157             }
158         }
159     }
160     return result;
161 }
162
163 /*
164  * Return the number of levels of child-windows under the current window.
165  */
166 static int
167 child_depth(WINDOW *cmp)
168 {
169     int depth = 0;
170
171     if (cmp != 0) {
172 #if NCURSES_SP_FUNCS
173         SCREEN *sp = _nc_screen_of(cmp);
174 #endif
175         WINDOWLIST *wp;
176
177         for (each_window(sp, wp)) {
178             WINDOW *tst = &(wp->win);
179             if (tst->_parent == cmp) {
180                 depth = 1 + child_depth(tst);
181                 break;
182             }
183         }
184     }
185     return depth;
186 }
187
188 /*
189  * Return the number of levels of parent-windows above the current window.
190  */
191 static int
192 parent_depth(WINDOW *cmp)
193 {
194     int depth = 0;
195
196     if (cmp != 0) {
197         WINDOW *tst;
198         while ((tst = cmp->_parent) != 0) {
199             ++depth;
200             cmp = tst;
201         }
202     }
203     return depth;
204 }
205
206 /*
207  * FIXME: must adjust position so it's within the parent!
208  */
209 static int
210 adjust_window(WINDOW *win, int ToLines, int ToCols, int stolen EXTRA_DCLS)
211 {
212     int result;
213     int bottom = CurLines + _nc_screen_of(win)->_topstolen - stolen;
214     int myLines = win->_maxy + 1;
215     int myCols = win->_maxx + 1;
216     ripoff_t *rop = ripped_window(win);
217
218     T((T_CALLED("adjust_window(%p,%d,%d)%s depth %d/%d currently %ldx%ld at %ld,%ld"),
219        win, ToLines, ToCols,
220        (rop != 0) ? " (rip)" : "",
221        parent_depth(win),
222        child_depth(win),
223        (long) getmaxy(win), (long) getmaxx(win),
224        (long) getbegy(win) + win->_yoffset, (long) getbegx(win)));
225
226     if (rop != 0 && rop->line < 0) {
227         /*
228          * If it is a ripped-off window at the bottom of the screen, simply
229          * move it to the same relative position.
230          */
231         win->_begy = ToLines - ripped_bottom(win) - 0 - win->_yoffset;
232     } else if (win->_begy >= bottom) {
233         /*
234          * If it is below the bottom of the new screen, move up by the same
235          * amount that the screen shrank.
236          */
237         win->_begy += (ToLines - CurLines);
238     } else {
239         if (myLines == (CurLines - stolen)
240             && ToLines != CurLines) {
241             myLines = ToLines - stolen;
242         } else if (myLines == CurLines
243                    && ToLines != CurLines) {
244             myLines = ToLines;
245         }
246     }
247
248     if (myLines > ToLines) {
249         myLines = ToLines;
250     }
251
252     if (myCols > ToCols)
253         myCols = ToCols;
254
255     if (myCols == CurCols
256         && ToCols != CurCols)
257         myCols = ToCols;
258
259     result = wresize(win, myLines, myCols);
260     returnCode(result);
261 }
262
263 /*
264  * If we're decreasing size, recursively search for windows that have no
265  * children, decrease those to fit, then decrease the containing window, etc.
266  */
267 static int
268 decrease_size(NCURSES_SP_DCLx int ToLines, int ToCols, int stolen EXTRA_DCLS)
269 {
270     bool found;
271     int depth = 0;
272     WINDOWLIST *wp;
273
274     T((T_CALLED("decrease_size(%p, %d, %d)"), SP_PARM, ToLines, ToCols));
275
276     do {
277         found = FALSE;
278         TR(TRACE_UPDATE, ("decreasing size of windows to %dx%d, depth=%d",
279                           ToLines, ToCols, depth));
280         for (each_window(SP_PARM, wp)) {
281             WINDOW *win = &(wp->win);
282
283             if (!(win->_flags & _ISPAD)) {
284                 if (child_depth(win) == depth) {
285                     found = TRUE;
286                     if (adjust_window(win, ToLines, ToCols,
287                                       stolen EXTRA_ARGS) != OK)
288                         returnCode(ERR);
289                 }
290             }
291         }
292         ++depth;
293     } while (found);
294     returnCode(OK);
295 }
296
297 /*
298  * If we're increasing size, recursively search for windows that have no
299  * parent, increase those to fit, then increase the contained window, etc.
300  */
301 static int
302 increase_size(NCURSES_SP_DCLx int ToLines, int ToCols, int stolen EXTRA_DCLS)
303 {
304     bool found;
305     int depth = 0;
306     WINDOWLIST *wp;
307
308     T((T_CALLED("increase_size(%p, %d, %d)"), SP_PARM, ToLines, ToCols));
309
310     do {
311         found = FALSE;
312         TR(TRACE_UPDATE, ("increasing size of windows to %dx%d, depth=%d",
313                           ToLines, ToCols, depth));
314         for (each_window(SP_PARM, wp)) {
315             WINDOW *win = &(wp->win);
316
317             if (!(win->_flags & _ISPAD)) {
318                 if (parent_depth(win) == depth) {
319                     found = TRUE;
320                     if (adjust_window(win, ToLines, ToCols,
321                                       stolen EXTRA_ARGS) != OK)
322                         returnCode(ERR);
323                 }
324             }
325         }
326         ++depth;
327     } while (found);
328     returnCode(OK);
329 }
330
331 /*
332  * This function reallocates NCURSES window structures, with no side-effects
333  * such as ungetch().
334  */
335 NCURSES_EXPORT(int)
336 NCURSES_SP_NAME(resize_term) (NCURSES_SP_DCLx int ToLines, int ToCols)
337 {
338     int result = OK EXTRA_ARGS;
339     int was_stolen;
340
341     T((T_CALLED("resize_term(%p,%d,%d) old(%d,%d)"),
342        SP_PARM, ToLines, ToCols,
343        SP_PARM == 0 ? -1 : screen_lines(SP_PARM),
344        SP_PARM == 0 ? -1 : screen_columns(SP_PARM)));
345
346     if (SP_PARM == 0) {
347         returnCode(ERR);
348     }
349
350     _nc_lock_global(curses);
351
352     was_stolen = (screen_lines(SP_PARM) - SP_PARM->_lines_avail);
353     if (NCURSES_SP_NAME(is_term_resized) (NCURSES_SP_ARGx ToLines, ToCols)) {
354         int myLines = CurLines = screen_lines(SP_PARM);
355         int myCols = CurCols = screen_columns(SP_PARM);
356
357 #ifdef TRACE
358         if (USE_TRACEF(TRACE_UPDATE)) {
359             show_window_sizes("before");
360             _nc_unlock_global(tracef);
361         }
362 #endif
363         if (ToLines > screen_lines(SP_PARM)) {
364             increase_size(NCURSES_SP_ARGx
365                           myLines = ToLines, myCols, was_stolen EXTRA_ARGS);
366             CurLines = myLines;
367             CurCols = myCols;
368         }
369
370         if (ToCols > screen_columns(SP_PARM)) {
371             increase_size(NCURSES_SP_ARGx
372                           myLines, myCols = ToCols, was_stolen EXTRA_ARGS);
373             CurLines = myLines;
374             CurCols = myCols;
375         }
376
377         if (ToLines < myLines ||
378             ToCols < myCols) {
379             decrease_size(NCURSES_SP_ARGx ToLines, ToCols, was_stolen EXTRA_ARGS);
380         }
381
382         screen_lines(SP_PARM) = lines = ToLines;
383         screen_columns(SP_PARM) = columns = ToCols;
384
385         SP_PARM->_lines_avail = lines - was_stolen;
386
387         if (SP_PARM->oldhash) {
388             FreeAndNull(SP_PARM->oldhash);
389         }
390         if (SP_PARM->newhash) {
391             FreeAndNull(SP_PARM->newhash);
392         }
393 #ifdef TRACE
394         if (USE_TRACEF(TRACE_UPDATE)) {
395             SET_LINES(ToLines - was_stolen);
396             SET_COLS(ToCols);
397             show_window_sizes("after");
398             _nc_unlock_global(tracef);
399         }
400 #endif
401     }
402
403     /*
404      * Always update LINES, to allow for call from lib_doupdate.c which
405      * needs to have the count adjusted by the stolen (ripped off) lines.
406      */
407     SET_LINES(ToLines - was_stolen);
408     SET_COLS(ToCols);
409
410     _nc_unlock_global(curses);
411
412     returnCode(result);
413 }
414
415 #if NCURSES_SP_FUNCS
416 NCURSES_EXPORT(int)
417 resize_term(int ToLines, int ToCols)
418 {
419     int res = ERR;
420     _nc_lock_global(curses);
421     res = NCURSES_SP_NAME(resize_term) (CURRENT_SCREEN, ToLines, ToCols);
422     _nc_unlock_global(curses);
423     return (res);
424 }
425 #endif
426
427 /*
428  * This function reallocates NCURSES window structures.  It is invoked in
429  * response to a SIGWINCH interrupt.  Other user-defined windows may also need
430  * to be reallocated.
431  *
432  * Because this performs memory allocation, it should not (in general) be
433  * invoked directly from the signal handler.
434  */
435 NCURSES_EXPORT(int)
436 NCURSES_SP_NAME(resizeterm) (NCURSES_SP_DCLx int ToLines, int ToCols)
437 {
438     int result = ERR;
439
440     T((T_CALLED("resizeterm(%p, %d,%d) old(%d,%d)"),
441        SP_PARM, ToLines, ToCols,
442        SP_PARM == 0 ? -1 : screen_lines(SP_PARM), screen_columns(SP_PARM)));
443
444     if (SP_PARM != 0) {
445         result = OK;
446         SP_PARM->_sig_winch = FALSE;
447
448         if (NCURSES_SP_NAME(is_term_resized) (NCURSES_SP_ARGx ToLines, ToCols)) {
449 #if USE_SIGWINCH
450             ripoff_t *rop;
451             bool slk_visible = (SP_PARM != 0
452                                 && SP_PARM->_slk != 0
453                                 && !(SP_PARM->_slk->hidden));
454
455             if (slk_visible) {
456                 slk_clear();
457             }
458 #endif
459             result = NCURSES_SP_NAME(resize_term) (NCURSES_SP_ARGx ToLines, ToCols);
460
461 #if USE_SIGWINCH
462             safe_ungetch(SP_PARM, KEY_RESIZE);  /* so application can know this */
463             clearok(CurScreen(SP_PARM), TRUE);  /* screen contents are unknown */
464
465             /* ripped-off lines are a special case: if we did not lengthen
466              * them, we haven't moved them either.  repaint them, too.
467              *
468              * for the rest - stdscr and other windows - the client has to
469              * decide which to repaint, since without panels, ncurses does
470              * not know which are really on top.
471              */
472             for (each_ripoff(rop)) {
473                 if (rop->win != StdScreen(SP_PARM)
474                     && rop->win != 0
475                     && rop->line < 0) {
476
477                     if (rop->hook != _nc_slk_initialize) {
478                         touchwin(rop->win);
479                         wnoutrefresh(rop->win);
480                     }
481                 }
482             }
483
484             /* soft-keys are a special case: we _know_ how to repaint them */
485             if (slk_visible) {
486                 NCURSES_SP_NAME(slk_restore) (NCURSES_SP_ARG);
487                 NCURSES_SP_NAME(slk_touch) (NCURSES_SP_ARG);
488                 NCURSES_SP_NAME(slk_refresh) (NCURSES_SP_ARG);
489             }
490 #endif
491         }
492     }
493
494     returnCode(result);
495 }
496
497 #if NCURSES_SP_FUNCS
498 NCURSES_EXPORT(int)
499 resizeterm(int ToLines, int ToCols)
500 {
501     return NCURSES_SP_NAME(resizeterm) (CURRENT_SCREEN, ToLines, ToCols);
502 }
503 #endif