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 48MODULE_ID("$Id: resizeterm.c,v 1.45 2012/07/07 17:07:23 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 57static int current_lines; 58static 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 66static void 67show_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 */ 95NCURSES_EXPORT(bool) 96NCURSES_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 106NCURSES_EXPORT(bool) 107is_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 */ 115static ripoff_t * 116ripped_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 */ 139static int 140ripped_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 */ 164static int 165child_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 */ 189static int 190parent_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 */ 207static int 208adjust_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 */ 271static int 272decrease_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 */ 305static int 306increase_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 */ 339NCURSES_EXPORT(int) 340NCURSES_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 result = increase_size(NCURSES_SP_ARGx 369 myLines = ToLines, 370 myCols, 371 was_stolen EXTRA_ARGS); 372 CurLines = myLines; 373 CurCols = myCols; 374 } 375 376 if ((result == OK) 377 && (ToCols > screen_columns(SP_PARM))) { 378 result = increase_size(NCURSES_SP_ARGx 379 myLines, 380 myCols = ToCols, 381 was_stolen EXTRA_ARGS); 382 CurLines = myLines; 383 CurCols = myCols; 384 } 385 386 if ((result == OK) 387 && (ToLines < myLines || 388 ToCols < myCols)) { 389 result = decrease_size(NCURSES_SP_ARGx 390 ToLines, 391 ToCols, 392 was_stolen EXTRA_ARGS); 393 } 394 395 if (result == OK) { 396 screen_lines(SP_PARM) = (NCURSES_SIZE_T) ToLines; 397 screen_columns(SP_PARM) = (NCURSES_SIZE_T) ToCols; 398 399#ifdef USE_TERM_DRIVER 400 CallDriver_2(SP_PARM, setsize, ToLines, ToCols); 401#else 402 lines = (NCURSES_SIZE_T) ToLines; 403 columns = (NCURSES_SIZE_T) ToCols; 404#endif 405 406 SP_PARM->_lines_avail = (NCURSES_SIZE_T) (ToLines - was_stolen); 407 408 if (SP_PARM->oldhash) { 409 FreeAndNull(SP_PARM->oldhash); 410 } 411 if (SP_PARM->newhash) { 412 FreeAndNull(SP_PARM->newhash); 413 } 414#ifdef TRACE 415 if (USE_TRACEF(TRACE_UPDATE)) { 416 SET_LINES(ToLines - was_stolen); 417 SET_COLS(ToCols); 418 show_window_sizes("after"); 419 _nc_unlock_global(tracef); 420 } 421#endif 422 } 423 } 424 425 if (result == OK) { 426 /* 427 * Always update LINES, to allow for call from lib_doupdate.c which 428 * needs to have the count adjusted by the stolen (ripped off) lines. 429 */ 430 SET_LINES(ToLines - was_stolen); 431 SET_COLS(ToCols); 432 } 433 434 _nc_nonsp_unlock_global(curses); 435 436 returnCode(result); 437} 438 439#if NCURSES_SP_FUNCS 440NCURSES_EXPORT(int) 441resize_term(int ToLines, int ToCols) 442{ 443 int res = ERR; 444 _nc_sp_lock_global(curses); 445 res = NCURSES_SP_NAME(resize_term) (CURRENT_SCREEN, ToLines, ToCols); 446 _nc_sp_unlock_global(curses); 447 return (res); 448} 449#endif 450 451/* 452 * This function reallocates NCURSES window structures. It is invoked in 453 * response to a SIGWINCH interrupt. Other user-defined windows may also need 454 * to be reallocated. 455 * 456 * Because this performs memory allocation, it should not (in general) be 457 * invoked directly from the signal handler. 458 */ 459NCURSES_EXPORT(int) 460NCURSES_SP_NAME(resizeterm) (NCURSES_SP_DCLx int ToLines, int ToCols) 461{ 462 int result = ERR; 463 464 T((T_CALLED("resizeterm(%p, %d,%d) old(%d,%d)"), 465 (void *) SP_PARM, ToLines, ToCols, 466 (SP_PARM == 0) ? -1 : screen_lines(SP_PARM), 467 (SP_PARM == 0) ? -1 : screen_columns(SP_PARM))); 468 469 if (SP_PARM != 0) { 470 result = OK; 471 SP_PARM->_sig_winch = FALSE; 472 473 if (NCURSES_SP_NAME(is_term_resized) (NCURSES_SP_ARGx ToLines, ToCols)) { 474#if USE_SIGWINCH 475 ripoff_t *rop; 476 bool slk_visible = (SP_PARM != 0 477 && SP_PARM->_slk != 0 478 && !(SP_PARM->_slk->hidden)); 479 480 if (slk_visible) { 481 slk_clear(); 482 } 483#endif 484 result = NCURSES_SP_NAME(resize_term) (NCURSES_SP_ARGx ToLines, ToCols); 485 486#if USE_SIGWINCH 487 clearok(CurScreen(SP_PARM), TRUE); /* screen contents are unknown */ 488 489 /* ripped-off lines are a special case: if we did not lengthen 490 * them, we haven't moved them either. repaint them, too. 491 * 492 * for the rest - stdscr and other windows - the client has to 493 * decide which to repaint, since without panels, ncurses does 494 * not know which are really on top. 495 */ 496 for (each_ripoff(rop)) { 497 if (rop->win != StdScreen(SP_PARM) 498 && rop->win != 0 499 && rop->line < 0) { 500 501 if (rop->hook != _nc_slk_initialize) { 502 touchwin(rop->win); 503 wnoutrefresh(rop->win); 504 } 505 } 506 } 507 508 /* soft-keys are a special case: we _know_ how to repaint them */ 509 if (slk_visible) { 510 NCURSES_SP_NAME(slk_restore) (NCURSES_SP_ARG); 511 NCURSES_SP_NAME(slk_touch) (NCURSES_SP_ARG); 512 NCURSES_SP_NAME(slk_refresh) (NCURSES_SP_ARG); 513 } 514#endif 515 } 516#if USE_SIGWINCH 517 safe_ungetch(SP_PARM, KEY_RESIZE); /* so application can know this */ 518#endif 519 } 520 521 returnCode(result); 522} 523 524#if NCURSES_SP_FUNCS 525NCURSES_EXPORT(int) 526resizeterm(int ToLines, int ToCols) 527{ 528 return NCURSES_SP_NAME(resizeterm) (CURRENT_SCREEN, ToLines, ToCols); 529} 530#endif 531