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