1/* $NetBSD: vs_split.c,v 1.6 2013/12/01 02:34:54 christos Exp $ */ 2/*- 3 * Copyright (c) 1993, 1994 4 * The Regents of the University of California. All rights reserved. 5 * Copyright (c) 1993, 1994, 1995, 1996 6 * Keith Bostic. All rights reserved. 7 * 8 * See the LICENSE file for redistribution information. 9 */ 10 11#include "config.h" 12 13#include <sys/cdefs.h> 14#if 0 15#ifndef lint 16static const char sccsid[] = "Id: vs_split.c,v 10.42 2001/06/25 15:19:38 skimo Exp (Berkeley) Date: 2001/06/25 15:19:38 "; 17#endif /* not lint */ 18#else 19__RCSID("$NetBSD$"); 20#endif 21 22#include <sys/types.h> 23#include <sys/queue.h> 24#include <sys/time.h> 25 26#include <bitstring.h> 27#include <errno.h> 28#include <limits.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32 33#include "../common/common.h" 34#include "vi.h" 35 36typedef enum { HORIZ_FOLLOW, HORIZ_PRECEDE, VERT_FOLLOW, VERT_PRECEDE } jdir_t; 37 38static SCR *vs_getbg __P((SCR *, const char *)); 39static void vs_insert __P((SCR *sp, WIN *wp)); 40static int vs_join __P((SCR *, SCR **, jdir_t *)); 41 42/* 43 * vs_split -- 44 * Create a new screen, horizontally. 45 * 46 * PUBLIC: int vs_split __P((SCR *, SCR *, int)); 47 */ 48int 49vs_split(SCR *sp, SCR *new, int ccl) 50 51 /* Colon-command line split. */ 52{ 53 GS *gp; 54 SMAP *smp; 55 size_t half; 56 int issmallscreen, splitup; 57 58 gp = sp->gp; 59 60 /* Check to see if it's possible. */ 61 /* XXX: The IS_ONELINE fix will change this, too. */ 62 if (sp->rows < 4) { 63 msgq(sp, M_ERR, 64 "222|Screen must be larger than %d lines to split", 4 - 1); 65 return (1); 66 } 67 68 /* Wait for any messages in the screen. */ 69 vs_resolve(sp, NULL, 1); 70 71 /* Get a new screen map. */ 72 CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP)); 73 if (_HMAP(new) == NULL) 74 return (1); 75 _HMAP(new)->lno = sp->lno; 76 _HMAP(new)->coff = 0; 77 _HMAP(new)->soff = 1; 78 79 /* Split the screen in half. */ 80 half = sp->rows / 2; 81 if (ccl && half > 6) 82 half = 6; 83 84 /* 85 * Small screens: see vs_refresh.c section 6a. Set a flag so 86 * we know to fix the screen up later. 87 */ 88 issmallscreen = IS_SMALL(sp); 89 90 /* The columns in the screen don't change. */ 91 new->coff = sp->coff; 92 new->cols = sp->cols; 93 94 /* 95 * Split the screen, and link the screens together. If creating a 96 * screen to edit the colon command line or the cursor is in the top 97 * half of the current screen, the new screen goes under the current 98 * screen. Else, it goes above the current screen. 99 * 100 * Recalculate current cursor position based on sp->lno, we're called 101 * with the cursor on the colon command line. Then split the screen 102 * in half and update the shared information. 103 */ 104 splitup = 105 !ccl && (vs_sm_cursor(sp, &smp) ? 0 : (size_t)(smp - HMAP) + 1) >= half; 106 if (splitup) { /* Old is bottom half. */ 107 new->rows = sp->rows - half; /* New. */ 108 new->roff = sp->roff; 109 sp->rows = half; /* Old. */ 110 sp->roff += new->rows; 111 112 /* 113 * If the parent is the bottom half of the screen, shift 114 * the map down to match on-screen text. 115 */ 116 memcpy(_HMAP(sp), _HMAP(sp) + new->rows, 117 (sp->t_maxrows - new->rows) * sizeof(SMAP)); 118 } else { /* Old is top half. */ 119 new->rows = half; /* New. */ 120 sp->rows -= half; /* Old. */ 121 new->roff = sp->roff + sp->rows; 122 } 123 124 /* Adjust maximum text count. */ 125 sp->t_maxrows = IS_ONELINE(sp) ? 1 : sp->rows - 1; 126 new->t_maxrows = IS_ONELINE(new) ? 1 : new->rows - 1; 127 128 /* 129 * Small screens: see vs_refresh.c, section 6a. 130 * 131 * The child may have different screen options sizes than the parent, 132 * so use them. Guarantee that text counts aren't larger than the 133 * new screen sizes. 134 */ 135 if (issmallscreen) { 136 /* Fix the text line count for the parent. */ 137 if (splitup) 138 sp->t_rows -= new->rows; 139 140 /* Fix the parent screen. */ 141 if (sp->t_rows > sp->t_maxrows) 142 sp->t_rows = sp->t_maxrows; 143 if (sp->t_minrows > sp->t_maxrows) 144 sp->t_minrows = sp->t_maxrows; 145 146 /* Fix the child screen. */ 147 new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW); 148 if (new->t_rows > new->t_maxrows) 149 new->t_rows = new->t_maxrows; 150 if (new->t_minrows > new->t_maxrows) 151 new->t_minrows = new->t_maxrows; 152 } else { 153 sp->t_minrows = sp->t_rows = IS_ONELINE(sp) ? 1 : sp->rows - 1; 154 155 /* 156 * The new screen may be a small screen, even if the parent 157 * was not. Don't complain if O_WINDOW is too large, we're 158 * splitting the screen so the screen is much smaller than 159 * normal. 160 */ 161 new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW); 162 if (new->t_rows > new->rows - 1) 163 new->t_minrows = new->t_rows = 164 IS_ONELINE(new) ? 1 : new->rows - 1; 165 } 166 167 /* Adjust the ends of the new and old maps. */ 168 _TMAP(sp) = IS_ONELINE(sp) ? 169 _HMAP(sp) : _HMAP(sp) + (sp->t_rows - 1); 170 _TMAP(new) = IS_ONELINE(new) ? 171 _HMAP(new) : _HMAP(new) + (new->t_rows - 1); 172 173 /* Reset the length of the default scroll. */ 174 if ((sp->defscroll = sp->t_maxrows / 2) == 0) 175 sp->defscroll = 1; 176 if ((new->defscroll = new->t_maxrows / 2) == 0) 177 new->defscroll = 1; 178 179 /* Fit the screen into the logical chain. */ 180 vs_insert(new, sp->wp); 181 182 /* Tell the display that we're splitting. */ 183 (void)gp->scr_split(sp, new); 184 185 /* 186 * Initialize the screen flags: 187 * 188 * If we're in vi mode in one screen, we don't have to reinitialize. 189 * This isn't just a cosmetic fix. The path goes like this: 190 * 191 * return into vi(), SC_SSWITCH set 192 * call vs_refresh() with SC_STATUS set 193 * call vs_resolve to display the status message 194 * call vs_refresh() because the SC_SCR_VI bit isn't set 195 * 196 * Things go downhill at this point. 197 * 198 * Draw the new screen from scratch, and add a status line. 199 */ 200 F_SET(new, 201 SC_SCR_REFORMAT | SC_STATUS | 202 F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX)); 203 return (0); 204} 205 206/* 207 * vs_vsplit -- 208 * Create a new screen, vertically. 209 * 210 * PUBLIC: int vs_vsplit __P((SCR *, SCR *)); 211 */ 212int 213vs_vsplit(SCR *sp, SCR *new) 214{ 215 GS *gp; 216 size_t cols; 217 218 gp = sp->gp; 219 220 /* Check to see if it's possible. */ 221 if (sp->cols / 2 <= MINIMUM_SCREEN_COLS) { 222 msgq(sp, M_ERR, 223 "288|Screen must be larger than %d columns to split", 224 MINIMUM_SCREEN_COLS * 2); 225 return (1); 226 } 227 228 /* Wait for any messages in the screen. */ 229 vs_resolve(sp, NULL, 1); 230 231 /* Get a new screen map. */ 232 CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP)); 233 if (_HMAP(new) == NULL) 234 return (1); 235 _HMAP(new)->lno = sp->lno; 236 _HMAP(new)->coff = 0; 237 _HMAP(new)->soff = 1; 238 239 /* 240 * Split the screen in half; we have to sacrifice a column to delimit 241 * the screens. 242 * 243 * XXX 244 * We always split to the right... that makes more sense to me, and 245 * I don't want to play the stupid games that I play when splitting 246 * horizontally. 247 * 248 * XXX 249 * We reserve a column for the screen, "knowing" that curses needs 250 * one. This should be worked out with the display interface. 251 */ 252 cols = sp->cols / 2; 253 new->cols = sp->cols - cols - 1; 254 sp->cols = cols; 255 new->coff = sp->coff + cols + 1; 256 sp->cno = 0; 257 258 /* Nothing else changes. */ 259 new->rows = sp->rows; 260 new->t_rows = sp->t_rows; 261 new->t_maxrows = sp->t_maxrows; 262 new->t_minrows = sp->t_minrows; 263 new->roff = sp->roff; 264 new->defscroll = sp->defscroll; 265 _TMAP(new) = _HMAP(new) + (new->t_rows - 1); 266 267 /* Fit the screen into the logical chain. */ 268 vs_insert(new, sp->wp); 269 270 /* Tell the display that we're splitting. */ 271 (void)gp->scr_split(sp, new); 272 273 /* Redraw the old screen from scratch. */ 274 F_SET(sp, SC_SCR_REFORMAT | SC_STATUS); 275 276 /* 277 * Initialize the screen flags: 278 * 279 * If we're in vi mode in one screen, we don't have to reinitialize. 280 * This isn't just a cosmetic fix. The path goes like this: 281 * 282 * return into vi(), SC_SSWITCH set 283 * call vs_refresh() with SC_STATUS set 284 * call vs_resolve to display the status message 285 * call vs_refresh() because the SC_SCR_VI bit isn't set 286 * 287 * Things go downhill at this point. 288 * 289 * Draw the new screen from scratch, and add a status line. 290 */ 291 F_SET(new, 292 SC_SCR_REFORMAT | SC_STATUS | 293 F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX)); 294 return (0); 295} 296 297/* 298 * vs_insert -- 299 * Insert the new screen into the correct place in the logical 300 * chain. 301 */ 302static void 303vs_insert(SCR *sp, WIN *wp) 304{ 305 SCR *tsp; 306 307 sp->wp = wp; 308 309 /* Move past all screens with lower row numbers. */ 310 TAILQ_FOREACH(tsp, &wp->scrq, q) 311 if (tsp->roff >= sp->roff) 312 break; 313 /* 314 * Move past all screens with the same row number and lower 315 * column numbers. 316 */ 317 for (; tsp != NULL; tsp = TAILQ_NEXT(tsp, q)) 318 if (tsp->roff != sp->roff || tsp->coff > sp->coff) 319 break; 320 321 /* 322 * If we reached the end, this screen goes there. Otherwise, 323 * put it before or after the screen where we stopped. 324 */ 325 if (tsp == NULL) { 326 TAILQ_INSERT_TAIL(&wp->scrq, sp, q); 327 } else if (tsp->roff < sp->roff || 328 (tsp->roff == sp->roff && tsp->coff < sp->coff)) { 329 TAILQ_INSERT_AFTER(&wp->scrq, tsp, sp, q); 330 } else 331 TAILQ_INSERT_BEFORE(tsp, sp, q); 332} 333 334/* 335 * vs_discard -- 336 * Discard the screen, folding the real-estate into a related screen, 337 * if one exists, and return that screen. 338 * 339 * PUBLIC: int vs_discard __P((SCR *, SCR **)); 340 */ 341int 342vs_discard(SCR *sp, SCR **spp) 343{ 344 GS *gp; 345 SCR *tsp, **lp, *list[100]; 346 jdir_t jdir; 347 348 gp = sp->gp; 349 350 /* 351 * Save the old screen's cursor information. 352 * 353 * XXX 354 * If called after file_end(), and the underlying file was a tmp 355 * file, it may have gone away. 356 */ 357 if (sp->frp != NULL) { 358 sp->frp->lno = sp->lno; 359 sp->frp->cno = sp->cno; 360 F_SET(sp->frp, FR_CURSORSET); 361 } 362 363 /* If no other screens to join, we're done. */ 364 if (!IS_SPLIT(sp)) { 365 (void)gp->scr_discard(sp, NULL); 366 367 if (spp != NULL) 368 *spp = NULL; 369 return (0); 370 } 371 372 /* 373 * Find a set of screens that cover one of the screen's borders. 374 * Check the vertical axis first, for no particular reason. 375 * 376 * XXX 377 * It's possible (I think?), to create a screen that shares no full 378 * border with any other set of screens, so we can't discard it. We 379 * just complain at the user until they clean it up. 380 */ 381 if (vs_join(sp, list, &jdir)) 382 return (1); 383 384 /* 385 * Modify the affected screens. Redraw the modified screen(s) from 386 * scratch, setting a status line. If this is ever a performance 387 * problem we could play games with the map, but I wrote that code 388 * before and it was never clean or easy. 389 * 390 * Don't clean up the discarded screen's information. If the screen 391 * isn't exiting, we'll do the work when the user redisplays it. 392 */ 393 switch (jdir) { 394 case HORIZ_FOLLOW: 395 case HORIZ_PRECEDE: 396 for (lp = &list[0]; (tsp = *lp) != NULL; ++lp) { 397 /* 398 * Small screens: see vs_refresh.c section 6a. Adjust 399 * text line info, unless it's a small screen. 400 * 401 * Reset the length of the default scroll. 402 * 403 * Reset the map references. 404 */ 405 tsp->rows += sp->rows; 406 if (!IS_SMALL(tsp)) 407 tsp->t_rows = tsp->t_minrows = tsp->rows - 1; 408 tsp->t_maxrows = tsp->rows - 1; 409 410 tsp->defscroll = tsp->t_maxrows / 2; 411 412 *(_HMAP(tsp) + (tsp->t_rows - 1)) = *_TMAP(tsp); 413 _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1); 414 415 switch (jdir) { 416 case HORIZ_FOLLOW: 417 tsp->roff = sp->roff; 418 vs_sm_fill(tsp, OOBLNO, P_TOP); 419 break; 420 case HORIZ_PRECEDE: 421 vs_sm_fill(tsp, OOBLNO, P_BOTTOM); 422 break; 423 default: 424 abort(); 425 } 426 F_SET(tsp, SC_STATUS); 427 } 428 break; 429 case VERT_FOLLOW: 430 case VERT_PRECEDE: 431 for (lp = &list[0]; (tsp = *lp) != NULL; ++lp) { 432 if (jdir == VERT_FOLLOW) 433 tsp->coff = sp->coff; 434 tsp->cols += sp->cols + 1; /* XXX: DIVIDER */ 435 vs_sm_fill(tsp, OOBLNO, P_TOP); 436 F_SET(tsp, SC_STATUS); 437 } 438 break; 439 default: 440 abort(); 441 } 442 443 /* Find the closest screen that changed and move to it. */ 444 tsp = list[0]; 445 if (spp != NULL) 446 *spp = tsp; 447 448 /* Tell the display that we're discarding a screen. */ 449 (void)gp->scr_discard(sp, list); 450 451 return (0); 452} 453 454/* 455 * vs_join -- 456 * Find a set of screens that covers a screen's border. 457 */ 458static int 459vs_join(SCR *sp, SCR **listp, jdir_t *jdirp) 460{ 461 WIN *wp; 462 SCR **lp, *tsp; 463 int first; 464 size_t tlen; 465 466 wp = sp->wp; 467 468 /* Check preceding vertical. */ 469 lp = listp; 470 tlen = sp->rows; 471 TAILQ_FOREACH(tsp, &wp->scrq, q) { 472 if (sp == tsp) 473 continue; 474 /* Test if precedes the screen vertically. */ 475 if (tsp->coff + tsp->cols + 1 != sp->coff) 476 continue; 477 /* 478 * Test if a subset on the vertical axis. If overlaps the 479 * beginning or end, we can't join on this axis at all. 480 */ 481 if (tsp->roff > sp->roff + sp->rows) 482 continue; 483 if (tsp->roff < sp->roff) { 484 if (tsp->roff + tsp->rows >= sp->roff) 485 break; 486 continue; 487 } 488 if (tsp->roff + tsp->rows > sp->roff + sp->rows) 489 break; 490#ifdef DEBUG 491 if (tlen < tsp->rows) 492 abort(); 493#endif 494 tlen -= tsp->rows; 495 *lp++ = tsp; 496 } 497 if (tlen == 0) { 498 *lp = NULL; 499 *jdirp = VERT_PRECEDE; 500 return (0); 501 } 502 503 /* Check following vertical. */ 504 lp = listp; 505 tlen = sp->rows; 506 TAILQ_FOREACH(tsp, &wp->scrq, q) { 507 if (sp == tsp) 508 continue; 509 /* Test if follows the screen vertically. */ 510 if (tsp->coff != sp->coff + sp->cols + 1) 511 continue; 512 /* 513 * Test if a subset on the vertical axis. If overlaps the 514 * beginning or end, we can't join on this axis at all. 515 */ 516 if (tsp->roff > sp->roff + sp->rows) 517 continue; 518 if (tsp->roff < sp->roff) { 519 if (tsp->roff + tsp->rows >= sp->roff) 520 break; 521 continue; 522 } 523 if (tsp->roff + tsp->rows > sp->roff + sp->rows) 524 break; 525#ifdef DEBUG 526 if (tlen < tsp->rows) 527 abort(); 528#endif 529 tlen -= tsp->rows; 530 *lp++ = tsp; 531 } 532 if (tlen == 0) { 533 *lp = NULL; 534 *jdirp = VERT_FOLLOW; 535 return (0); 536 } 537 538 /* Check preceding horizontal. */ 539 first = 0; 540 lp = listp; 541 tlen = sp->cols; 542 TAILQ_FOREACH(tsp, &wp->scrq, q) { 543 if (sp == tsp) 544 continue; 545 /* Test if precedes the screen horizontally. */ 546 if (tsp->roff + tsp->rows != sp->roff) 547 continue; 548 /* 549 * Test if a subset on the horizontal axis. If overlaps the 550 * beginning or end, we can't join on this axis at all. 551 */ 552 if (tsp->coff > sp->coff + sp->cols) 553 continue; 554 if (tsp->coff < sp->coff) { 555 if (tsp->coff + tsp->cols >= sp->coff) 556 break; 557 continue; 558 } 559 if (tsp->coff + tsp->cols > sp->coff + sp->cols) 560 break; 561#ifdef DEBUG 562 if (tlen < tsp->cols) 563 abort(); 564#endif 565 tlen -= tsp->cols + first; 566 first = 1; 567 *lp++ = tsp; 568 } 569 if (tlen == 0) { 570 *lp = NULL; 571 *jdirp = HORIZ_PRECEDE; 572 return (0); 573 } 574 575 /* Check following horizontal. */ 576 first = 0; 577 lp = listp; 578 tlen = sp->cols; 579 TAILQ_FOREACH(tsp, &wp->scrq, q) { 580 if (sp == tsp) 581 continue; 582 /* Test if precedes the screen horizontally. */ 583 if (tsp->roff != sp->roff + sp->rows) 584 continue; 585 /* 586 * Test if a subset on the horizontal axis. If overlaps the 587 * beginning or end, we can't join on this axis at all. 588 */ 589 if (tsp->coff > sp->coff + sp->cols) 590 continue; 591 if (tsp->coff < sp->coff) { 592 if (tsp->coff + tsp->cols >= sp->coff) 593 break; 594 continue; 595 } 596 if (tsp->coff + tsp->cols > sp->coff + sp->cols) 597 break; 598#ifdef DEBUG 599 if (tlen < tsp->cols) 600 abort(); 601#endif 602 tlen -= tsp->cols + first; 603 first = 1; 604 *lp++ = tsp; 605 } 606 if (tlen == 0) { 607 *lp = NULL; 608 *jdirp = HORIZ_FOLLOW; 609 return (0); 610 } 611 return (1); 612} 613 614/* 615 * vs_fg -- 616 * Background the current screen, and foreground a new one. 617 * 618 * PUBLIC: int vs_fg __P((SCR *, SCR **, CHAR_T *, int)); 619 */ 620int 621vs_fg(SCR *sp, SCR **nspp, CHAR_T *name, int newscreen) 622{ 623 GS *gp; 624 WIN *wp; 625 SCR *nsp; 626 const char *np; 627 size_t nlen; 628 629 gp = sp->gp; 630 wp = sp->wp; 631 632 if (name) 633 INT2CHAR(sp, name, STRLEN(name) + 1, np, nlen); 634 else 635 np = NULL; 636 if (newscreen) 637 /* Get the specified background screen. */ 638 nsp = vs_getbg(sp, np); 639 else 640 /* Swap screens. */ 641 if (vs_swap(sp, &nsp, np)) 642 return (1); 643 644 if ((*nspp = nsp) == NULL) { 645 msgq_wstr(sp, M_ERR, name, 646 name == NULL ? 647 "223|There are no background screens" : 648 "224|There's no background screen editing a file named %s"); 649 return (1); 650 } 651 652 if (newscreen) { 653 /* Remove the new screen from the background queue. */ 654 TAILQ_REMOVE(&gp->hq, nsp, q); 655 656 /* Split the screen; if we fail, hook the screen back in. */ 657 if (vs_split(sp, nsp, 0)) { 658 TAILQ_INSERT_TAIL(&gp->hq, nsp, q); 659 return (1); 660 } 661 } else { 662 /* Move the old screen to the background queue. */ 663 TAILQ_REMOVE(&wp->scrq, sp, q); 664 TAILQ_INSERT_TAIL(&gp->hq, sp, q); 665 } 666 return (0); 667} 668 669/* 670 * vs_bg -- 671 * Background the screen, and switch to the next one. 672 * 673 * PUBLIC: int vs_bg __P((SCR *)); 674 */ 675int 676vs_bg(SCR *sp) 677{ 678 GS *gp; 679 WIN *wp; 680 SCR *nsp; 681 682 gp = sp->gp; 683 wp = sp->wp; 684 685 /* Try and join with another screen. */ 686 if (vs_discard(sp, &nsp)) 687 return (1); 688 if (nsp == NULL) { 689 msgq(sp, M_ERR, 690 "225|You may not background your only displayed screen"); 691 return (1); 692 } 693 694 /* Move the old screen to the background queue. */ 695 TAILQ_REMOVE(&wp->scrq, sp, q); 696 TAILQ_INSERT_TAIL(&gp->hq, sp, q); 697 698 /* Toss the screen map. */ 699 free(_HMAP(sp)); 700 _HMAP(sp) = NULL; 701 702 /* Switch screens. */ 703 sp->nextdisp = nsp; 704 F_SET(sp, SC_SSWITCH); 705 706 return (0); 707} 708 709/* 710 * vs_swap -- 711 * Swap the current screen with a backgrounded one. 712 * 713 * PUBLIC: int vs_swap __P((SCR *, SCR **, const char *)); 714 */ 715int 716vs_swap(SCR *sp, SCR **nspp, const char *name) 717{ 718 GS *gp; 719 WIN *wp; 720 SCR *nsp, *list[2]; 721 722 gp = sp->gp; 723 wp = sp->wp; 724 725 /* Get the specified background screen. */ 726 if ((*nspp = nsp = vs_getbg(sp, name)) == NULL) 727 return (0); 728 729 /* 730 * Save the old screen's cursor information. 731 * 732 * XXX 733 * If called after file_end(), and the underlying file was a tmp 734 * file, it may have gone away. 735 */ 736 if (sp->frp != NULL) { 737 sp->frp->lno = sp->lno; 738 sp->frp->cno = sp->cno; 739 F_SET(sp->frp, FR_CURSORSET); 740 } 741 742 /* Switch screens. */ 743 sp->nextdisp = nsp; 744 F_SET(sp, SC_SSWITCH); 745 746 /* Initialize terminal information. */ 747 VIP(nsp)->srows = VIP(sp)->srows; 748 749 /* Initialize screen information. */ 750 nsp->cols = sp->cols; 751 nsp->rows = sp->rows; /* XXX: Only place in vi that sets rows. */ 752 nsp->roff = sp->roff; 753 754 /* 755 * Small screens: see vs_refresh.c, section 6a. 756 * 757 * The new screens may have different screen options sizes than the 758 * old one, so use them. Make sure that text counts aren't larger 759 * than the new screen sizes. 760 */ 761 if (IS_SMALL(nsp)) { 762 nsp->t_minrows = nsp->t_rows = O_VAL(nsp, O_WINDOW); 763 if (nsp->t_rows > sp->t_maxrows) 764 nsp->t_rows = nsp->t_maxrows; 765 if (nsp->t_minrows > sp->t_maxrows) 766 nsp->t_minrows = nsp->t_maxrows; 767 } else 768 nsp->t_rows = nsp->t_maxrows = nsp->t_minrows = nsp->rows - 1; 769 770 /* Reset the length of the default scroll. */ 771 nsp->defscroll = nsp->t_maxrows / 2; 772 773 /* Allocate a new screen map. */ 774 CALLOC_RET(nsp, _HMAP(nsp), SMAP *, SIZE_HMAP(nsp), sizeof(SMAP)); 775 _TMAP(nsp) = _HMAP(nsp) + (nsp->t_rows - 1); 776 777 /* Fill the map. */ 778 nsp->wp = sp->wp; 779 if (vs_sm_fill(nsp, nsp->lno, P_FILL)) 780 return (1); 781 782 /* 783 * The new screen replaces the old screen in the parent/child list. 784 * We insert the new screen after the old one. If we're exiting, 785 * the exit will delete the old one, if we're foregrounding, the fg 786 * code will move the old one to the background queue. 787 */ 788 TAILQ_REMOVE(&gp->hq, nsp, q); 789 TAILQ_INSERT_AFTER(&wp->scrq, sp, nsp, q); 790 791 /* 792 * Don't change the screen's cursor information other than to 793 * note that the cursor is wrong. 794 */ 795 F_SET(VIP(nsp), VIP_CUR_INVALID); 796 797 /* Draw the new screen from scratch, and add a status line. */ 798 F_SET(nsp, SC_SCR_REDRAW | SC_STATUS); 799 800 list[0] = nsp; list[1] = NULL; 801 (void)gp->scr_discard(sp, list); 802 803 return (0); 804} 805 806/* 807 * vs_resize -- 808 * Change the absolute size of the current screen. 809 * 810 * PUBLIC: int vs_resize __P((SCR *, long, adj_t)); 811 */ 812int 813vs_resize(SCR *sp, long int count, adj_t adj) 814{ 815 GS *gp; 816 SCR *g, *s, *prev, *next, *list[3] = {NULL, NULL, NULL}; 817 size_t g_off, s_off; 818 819 gp = sp->gp; 820 821 /* 822 * Figure out which screens will grow, which will shrink, and 823 * make sure it's possible. 824 */ 825 if (count == 0) 826 return (0); 827 if (adj == A_SET) { 828 if (sp->t_maxrows == (size_t)count) 829 return (0); 830 if (sp->t_maxrows > (size_t)count) { 831 adj = A_DECREASE; 832 count = sp->t_maxrows - count; 833 } else { 834 adj = A_INCREASE; 835 count = count - sp->t_maxrows; 836 } 837 } 838 839 /* Find first overlapping screen */ 840 for (next = TAILQ_NEXT(sp, q); 841 next != NULL && 842 (next->coff >= sp->coff + sp->cols || 843 next->coff + next->cols <= sp->coff); 844 next = TAILQ_NEXT(next, q)) 845 continue; 846 /* See if we can use it */ 847 if (next != NULL && 848 (sp->coff != next->coff || sp->cols != next->cols)) 849 next = NULL; 850 for (prev = TAILQ_PREV(sp, _scrh, q); 851 prev != NULL && 852 (prev->coff >= sp->coff + sp->cols || 853 prev->coff + prev->cols <= sp->coff); 854 prev = TAILQ_PREV(prev, _scrh, q)) 855 continue; 856 if (prev != NULL && 857 (sp->coff != prev->coff || sp->cols != prev->cols)) 858 prev = NULL; 859 860 g_off = s_off = 0; 861 if (adj == A_DECREASE) { 862 if (count < 0) 863 count = -count; 864 s = sp; 865 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + (size_t)count) 866 goto toosmall; 867 if ((g = prev) == NULL) { 868 if ((g = next) == NULL) 869 goto toobig; 870 g_off = -count; 871 } else 872 s_off = count; 873 } else { 874 g = sp; 875 if ((s = next) != NULL && 876 s->t_maxrows >= MINIMUM_SCREEN_ROWS + (size_t)count) 877 s_off = count; 878 else 879 s = NULL; 880 if (s == NULL) { 881 if ((s = prev) == NULL) { 882toobig: msgq(sp, M_BERR, adj == A_DECREASE ? 883 "227|The screen cannot shrink" : 884 "228|The screen cannot grow"); 885 return (1); 886 } 887 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + (size_t)count) { 888toosmall: msgq(sp, M_BERR, 889 "226|The screen can only shrink to %d rows", 890 MINIMUM_SCREEN_ROWS); 891 return (1); 892 } 893 g_off = -count; 894 } 895 } 896 897 /* 898 * Fix up the screens; we could optimize the reformatting of the 899 * screen, but this isn't likely to be a common enough operation 900 * to make it worthwhile. 901 */ 902 s->rows += -count; 903 s->roff += s_off; 904 g->rows += count; 905 g->roff += g_off; 906 907 g->t_rows += count; 908 if (g->t_minrows == g->t_maxrows) 909 g->t_minrows += count; 910 g->t_maxrows += count; 911 _TMAP(g) += count; 912 F_SET(g, SC_SCR_REFORMAT | SC_STATUS); 913 914 s->t_rows -= count; 915 s->t_maxrows -= count; 916 if (s->t_minrows > s->t_maxrows) 917 s->t_minrows = s->t_maxrows; 918 _TMAP(s) -= count; 919 F_SET(s, SC_SCR_REFORMAT | SC_STATUS); 920 921 /* XXXX */ 922 list[0] = g; list[1] = s; 923 gp->scr_discard(0, list); 924 925 return (0); 926} 927 928/* 929 * vs_getbg -- 930 * Get the specified background screen, or, if name is NULL, the first 931 * background screen. 932 */ 933static SCR * 934vs_getbg(SCR *sp, const char *name) 935{ 936 GS *gp; 937 SCR *nsp; 938 char *p; 939 940 gp = sp->gp; 941 942 /* If name is NULL, return the first background screen on the list. */ 943 if (name == NULL) { 944 return TAILQ_FIRST(&gp->hq); 945 } 946 947 /* Search for a full match. */ 948 TAILQ_FOREACH(nsp, &gp->hq, q) 949 if (!strcmp(nsp->frp->name, name)) 950 break; 951 if (nsp != NULL) 952 return (nsp); 953 954 /* Search for a last-component match. */ 955 TAILQ_FOREACH(nsp, &gp->hq, q) { 956 if ((p = strrchr(nsp->frp->name, '/')) == NULL) 957 p = nsp->frp->name; 958 else 959 ++p; 960 if (!strcmp(p, name)) 961 break; 962 } 963 return nsp; 964} 965