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