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 44 MODULE_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 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 _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 */ 88 NCURSES_EXPORT(bool) 89 is_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 */ 100 static ripoff_t * 101 ripped_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 */ 121 static int 122 ripped_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 */ 143 static int 144 child_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 */ 165 static int 166 parent_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 */ 183 static int 184 adjust_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 */ 241 static int 242 decrease_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 */ 275 static int 276 increase_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 */ 309 NCURSES_EXPORT(int) 310 resize_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 */ 394 NCURSES_EXPORT(int) 395 resizeterm(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