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 *
new_window(struct buffer * bp)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
reposition(int f,int n)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
redraw(int f,int n)69 redraw(int f, int n)
70 {
71 return (do_redraw(f, n, FALSE));
72 }
73
74 int
do_redraw(int f,int n,int force)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
nextwind(int f,int n)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
prevwind(int f,int n)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
onlywind(int f,int n)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
splitwind(int f,int n)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
enlargewind(int f,int n)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
shrinkwind(int f,int n)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
delwind(int f,int n)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