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