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