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