1 /* $OpenBSD: window.c,v 1.37 2023/03/08 04:43:11 guenther Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Window handling. 7 */ 8 9 #include <sys/queue.h> 10 #include <signal.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 14 #include "def.h" 15 16 struct mgwin * 17 new_window(struct buffer *bp) 18 { 19 struct mgwin *wp; 20 21 wp = calloc(1, sizeof(struct mgwin)); 22 if (wp == NULL) 23 return (NULL); 24 25 wp->w_bufp = bp; 26 wp->w_dotp = NULL; 27 wp->w_doto = 0; 28 wp->w_markp = NULL; 29 wp->w_marko = 0; 30 wp->w_rflag = 0; 31 wp->w_frame = 0; 32 wp->w_wrapline = NULL; 33 wp->w_dotline = wp->w_markline = 1; 34 if (bp) 35 bp->b_nwnd++; 36 return (wp); 37 } 38 39 /* 40 * Reposition dot in the current window to line "n". If the argument is 41 * positive, it is that line. If it is negative it is that line from the 42 * bottom. If it is 0 the window is centered (this is what the standard 43 * redisplay code does). 44 */ 45 int 46 reposition(int f, int n) 47 { 48 curwp->w_frame = (f & FFARG) ? (n >= 0 ? n + 1 : n) : 0; 49 curwp->w_rflag |= WFFRAME; 50 sgarbf = TRUE; 51 return (TRUE); 52 } 53 54 /* 55 * Refresh the display. A call is made to the "ttresize" entry in the 56 * terminal handler, which tries to reset "nrow" and "ncol". They will, 57 * however, never be set outside of the NROW or NCOL range. If the display 58 * changed size, arrange that everything is redone, then call "update" to 59 * fix the display. We do this so the new size can be displayed. In the 60 * normal case the call to "update" in "main.c" refreshes the screen, and 61 * all of the windows need not be recomputed. This call includes a 62 * 'force' parameter to ensure that the redraw is done, even after a 63 * a suspend/continue (where the window size parameters will already 64 * be updated). Note that when you get to the "display unusable" 65 * message, the screen will be messed up. If you make the window bigger 66 * again, and send another command, everything will get fixed! 67 */ 68 int 69 redraw(int f, int n) 70 { 71 return (do_redraw(f, n, FALSE)); 72 } 73 74 int 75 do_redraw(int f, int n, int force) 76 { 77 struct mgwin *wp; 78 int oldnrow, oldncol; 79 80 oldnrow = nrow; 81 oldncol = ncol; 82 ttresize(); 83 if (nrow != oldnrow || ncol != oldncol || force) { 84 85 /* find last */ 86 wp = wheadp; 87 while (wp->w_wndp != NULL) 88 wp = wp->w_wndp; 89 90 /* check if too small */ 91 if (nrow < wp->w_toprow + 3) { 92 dobeep(); 93 ewprintf("Display unusable"); 94 return (FALSE); 95 } 96 wp->w_ntrows = nrow - wp->w_toprow - 2; 97 sgarbf = TRUE; 98 update(CMODE); 99 } else 100 sgarbf = TRUE; 101 return (TRUE); 102 } 103 104 /* 105 * The command to make the next window (next => down the screen) the current 106 * window. There are no real errors, although the command does nothing if 107 * there is only 1 window on the screen. 108 */ 109 int 110 nextwind(int f, int n) 111 { 112 struct mgwin *wp; 113 114 if ((wp = curwp->w_wndp) == NULL) 115 wp = wheadp; 116 curwp = wp; 117 curbp = wp->w_bufp; 118 return (TRUE); 119 } 120 121 /* not in GNU Emacs */ 122 /* 123 * This command makes the previous window (previous => up the screen) the 124 * current window. There are no errors, although the command does not do 125 * a lot if there is only 1 window. 126 */ 127 int 128 prevwind(int f, int n) 129 { 130 struct mgwin *wp1, *wp2; 131 132 wp1 = wheadp; 133 wp2 = curwp; 134 if (wp1 == wp2) 135 wp2 = NULL; 136 while (wp1->w_wndp != wp2) 137 wp1 = wp1->w_wndp; 138 curwp = wp1; 139 curbp = wp1->w_bufp; 140 return (TRUE); 141 } 142 143 /* 144 * This command makes the current window the only window on the screen. Try 145 * to set the framing so that "." does not have to move on the display. Some 146 * care has to be taken to keep the values of dot and mark in the buffer 147 * structures right if the destruction of a window makes a buffer become 148 * undisplayed. 149 */ 150 int 151 onlywind(int f, int n) 152 { 153 struct mgwin *wp; 154 struct line *lp; 155 int i; 156 157 while (wheadp != curwp) { 158 wp = wheadp; 159 wheadp = wp->w_wndp; 160 if (--wp->w_bufp->b_nwnd == 0) { 161 wp->w_bufp->b_dotp = wp->w_dotp; 162 wp->w_bufp->b_doto = wp->w_doto; 163 wp->w_bufp->b_markp = wp->w_markp; 164 wp->w_bufp->b_marko = wp->w_marko; 165 wp->w_bufp->b_dotline = wp->w_dotline; 166 wp->w_bufp->b_markline = wp->w_markline; 167 } 168 free(wp); 169 } 170 while (curwp->w_wndp != NULL) { 171 wp = curwp->w_wndp; 172 curwp->w_wndp = wp->w_wndp; 173 if (--wp->w_bufp->b_nwnd == 0) { 174 wp->w_bufp->b_dotp = wp->w_dotp; 175 wp->w_bufp->b_doto = wp->w_doto; 176 wp->w_bufp->b_markp = wp->w_markp; 177 wp->w_bufp->b_marko = wp->w_marko; 178 wp->w_bufp->b_dotline = wp->w_dotline; 179 wp->w_bufp->b_markline = wp->w_markline; 180 } 181 free(wp); 182 } 183 lp = curwp->w_linep; 184 i = curwp->w_toprow; 185 while (i != 0 && lback(lp) != curbp->b_headp) { 186 --i; 187 lp = lback(lp); 188 } 189 curwp->w_toprow = 0; 190 191 /* 2 = mode, echo */ 192 curwp->w_ntrows = nrow - 2; 193 curwp->w_linep = lp; 194 curwp->w_rflag |= WFMODE | WFFULL; 195 return (TRUE); 196 } 197 198 /* 199 * Split the current window. A window smaller than 3 lines cannot be split. 200 * The only other error that is possible is a "malloc" failure allocating the 201 * structure for the new window. 202 * If called with a FFOTHARG, flags on the new window are set to 'n'. 203 */ 204 int 205 splitwind(int f, int n) 206 { 207 struct mgwin *wp, *wp1, *wp2; 208 struct line *lp; 209 int ntru, ntrd, ntrl; 210 211 if (curwp->w_ntrows < 3) { 212 dobeep(); 213 ewprintf("Cannot split a %d line window", curwp->w_ntrows); 214 return (FALSE); 215 } 216 wp = new_window(curbp); 217 if (wp == NULL) { 218 dobeep(); 219 ewprintf("Unable to create a window"); 220 return (FALSE); 221 } 222 223 /* use the current dot and mark */ 224 wp->w_dotp = curwp->w_dotp; 225 wp->w_doto = curwp->w_doto; 226 wp->w_markp = curwp->w_markp; 227 wp->w_marko = curwp->w_marko; 228 wp->w_dotline = curwp->w_dotline; 229 wp->w_markline = curwp->w_markline; 230 231 /* figure out which half of the screen we're in */ 232 ntru = (curwp->w_ntrows - 1) / 2; /* Upper size */ 233 ntrl = (curwp->w_ntrows - 1) - ntru; /* Lower size */ 234 235 for (lp = curwp->w_linep, ntrd = 0; lp != curwp->w_dotp; 236 lp = lforw(lp)) 237 ntrd++; 238 239 lp = curwp->w_linep; 240 241 /* old is upper window */ 242 if (ntrd <= ntru) { 243 /* hit mode line */ 244 if (ntrd == ntru) 245 lp = lforw(lp); 246 curwp->w_ntrows = ntru; 247 wp->w_wndp = curwp->w_wndp; 248 curwp->w_wndp = wp; 249 wp->w_toprow = curwp->w_toprow + ntru + 1; 250 wp->w_ntrows = ntrl; 251 /* old is lower window */ 252 } else { 253 wp1 = NULL; 254 wp2 = wheadp; 255 while (wp2 != curwp) { 256 wp1 = wp2; 257 wp2 = wp2->w_wndp; 258 } 259 if (wp1 == NULL) 260 wheadp = wp; 261 else 262 wp1->w_wndp = wp; 263 wp->w_wndp = curwp; 264 wp->w_toprow = curwp->w_toprow; 265 wp->w_ntrows = ntru; 266 267 /* mode line */ 268 ++ntru; 269 curwp->w_toprow += ntru; 270 curwp->w_ntrows = ntrl; 271 while (ntru--) 272 lp = lforw(lp); 273 } 274 275 /* adjust the top lines if necessary */ 276 curwp->w_linep = lp; 277 wp->w_linep = lp; 278 279 curwp->w_rflag |= WFMODE | WFFULL; 280 wp->w_rflag |= WFMODE | WFFULL; 281 /* if FFOTHARG, set flags) */ 282 if (f & FFOTHARG) 283 wp->w_flag = n; 284 285 return (TRUE); 286 } 287 288 /* 289 * Enlarge the current window. Find the window that loses space. Make sure 290 * it is big enough. If so, hack the window descriptions, and ask redisplay 291 * to do all the hard work. You don't just set "force reframe" because dot 292 * would move. 293 */ 294 int 295 enlargewind(int f, int n) 296 { 297 struct mgwin *adjwp; 298 struct line *lp; 299 int i; 300 301 if (n < 0) 302 return (shrinkwind(f, -n)); 303 if (wheadp->w_wndp == NULL) { 304 dobeep(); 305 ewprintf("Only one window"); 306 return (FALSE); 307 } 308 if ((adjwp = curwp->w_wndp) == NULL) { 309 adjwp = wheadp; 310 while (adjwp->w_wndp != curwp) 311 adjwp = adjwp->w_wndp; 312 } 313 if (adjwp->w_ntrows <= n) { 314 dobeep(); 315 ewprintf("Impossible change"); 316 return (FALSE); 317 } 318 319 /* shrink below */ 320 if (curwp->w_wndp == adjwp) { 321 lp = adjwp->w_linep; 322 for (i = 0; i < n && lp != adjwp->w_bufp->b_headp; ++i) 323 lp = lforw(lp); 324 adjwp->w_linep = lp; 325 adjwp->w_toprow += n; 326 /* shrink above */ 327 } else { 328 lp = curwp->w_linep; 329 for (i = 0; i < n && lback(lp) != curbp->b_headp; ++i) 330 lp = lback(lp); 331 curwp->w_linep = lp; 332 curwp->w_toprow -= n; 333 } 334 curwp->w_ntrows += n; 335 adjwp->w_ntrows -= n; 336 curwp->w_rflag |= WFMODE | WFFULL; 337 adjwp->w_rflag |= WFMODE | WFFULL; 338 return (TRUE); 339 } 340 341 /* 342 * Shrink the current window. Find the window that gains space. Hack at the 343 * window descriptions. Ask the redisplay to do all the hard work. 344 */ 345 int 346 shrinkwind(int f, int n) 347 { 348 struct mgwin *adjwp; 349 struct line *lp; 350 int i; 351 352 if (n < 0) 353 return (enlargewind(f, -n)); 354 if (wheadp->w_wndp == NULL) { 355 dobeep(); 356 ewprintf("Only one window"); 357 return (FALSE); 358 } 359 /* 360 * Bit of flakiness - FFRAND means it was an internal call, and 361 * to be trusted implicitly about sizes. 362 */ 363 if (!(f & FFRAND) && curwp->w_ntrows <= n) { 364 dobeep(); 365 ewprintf("Impossible change"); 366 return (FALSE); 367 } 368 if ((adjwp = curwp->w_wndp) == NULL) { 369 adjwp = wheadp; 370 while (adjwp->w_wndp != curwp) 371 adjwp = adjwp->w_wndp; 372 } 373 374 /* grow below */ 375 if (curwp->w_wndp == adjwp) { 376 lp = adjwp->w_linep; 377 for (i = 0; i < n && lback(lp) != adjwp->w_bufp->b_headp; ++i) 378 lp = lback(lp); 379 adjwp->w_linep = lp; 380 adjwp->w_toprow -= n; 381 /* grow above */ 382 } else { 383 lp = curwp->w_linep; 384 for (i = 0; i < n && lp != curbp->b_headp; ++i) 385 lp = lforw(lp); 386 curwp->w_linep = lp; 387 curwp->w_toprow += n; 388 } 389 curwp->w_ntrows -= n; 390 adjwp->w_ntrows += n; 391 curwp->w_rflag |= WFMODE | WFFULL; 392 adjwp->w_rflag |= WFMODE | WFFULL; 393 return (TRUE); 394 } 395 396 /* 397 * Delete current window. Call shrink-window to do the screen updating, then 398 * throw away the window. 399 */ 400 int 401 delwind(int f, int n) 402 { 403 struct mgwin *wp, *nwp; 404 405 wp = curwp; /* Cheap... */ 406 407 /* shrinkwind returning false means only one window... */ 408 if (shrinkwind(FFRAND, wp->w_ntrows + 1) == FALSE) 409 return (FALSE); 410 if (--wp->w_bufp->b_nwnd == 0) { 411 wp->w_bufp->b_dotp = wp->w_dotp; 412 wp->w_bufp->b_doto = wp->w_doto; 413 wp->w_bufp->b_markp = wp->w_markp; 414 wp->w_bufp->b_marko = wp->w_marko; 415 wp->w_bufp->b_dotline = wp->w_dotline; 416 wp->w_bufp->b_markline = wp->w_markline; 417 } 418 419 /* since shrinkwind did't crap out, we know we have a second window */ 420 if (wp == wheadp) 421 wheadp = curwp = wp->w_wndp; 422 else if ((curwp = wp->w_wndp) == NULL) 423 curwp = wheadp; 424 curbp = curwp->w_bufp; 425 for (nwp = wheadp; nwp != NULL; nwp = nwp->w_wndp) 426 if (nwp->w_wndp == wp) { 427 nwp->w_wndp = wp->w_wndp; 428 break; 429 } 430 free(wp); 431 return (TRUE); 432 } 433