1/*	$NetBSD: win.c,v 1.13 2003/08/07 11:17:34 agc Exp $	*/
2
3/*
4 * Copyright (c) 1983, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Edward Wang at The University of California, Berkeley.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)win.c	8.1 (Berkeley) 6/6/93";
39#else
40__RCSID("$NetBSD: win.c,v 1.13 2003/08/07 11:17:34 agc Exp $");
41#endif
42#endif /* not lint */
43
44#include <string.h>
45#include "defs.h"
46#include "char.h"
47#include "window_string.h"
48
49/*
50 * Higher level routines for dealing with windows.
51 *
52 * There are two types of windows: user window, and information window.
53 * User windows are the ones with a pty and shell.  Information windows
54 * are for displaying error messages, and other information.
55 *
56 * The windows are doubly linked in overlapping order and divided into
57 * two groups: foreground and normal.  Information
58 * windows are always foreground.  User windows can be either.
59 * Addwin() adds a window to the list at the top of one of the two groups.
60 * Deletewin() deletes a window.  Front() moves a window to the front
61 * of its group.  Wwopen(), wwadd(), and wwdelete() should never be called
62 * directly.
63 */
64
65/*
66 * Open a user window.
67 */
68struct ww *
69openwin(int id, int row, int col, int nrow, int ncol, int nline, char *label, int type, int uflags, char *shf, char **sh)
70{
71	struct ww *w;
72
73	if (id < 0 && (id = findid()) < 0)
74		return 0;
75	if (row + nrow <= 0 || row > wwnrow - 1
76	    || col + ncol <= 0 || col > wwncol - 1) {
77		error("Illegal window position.");
78		return 0;
79	}
80	w = wwopen(type, 0, nrow, ncol, row, col, nline);
81	if (w == 0) {
82		error("Can't open window: %s.", wwerror());
83		return 0;
84	}
85	w->ww_id = id;
86	window[id] = w;
87	CLR(w->ww_uflags, WWU_ALLFLAGS);
88	SET(w->ww_uflags, uflags);
89	w->ww_alt = w->ww_w;
90	if (label != 0 && setlabel(w, label) < 0)
91		error("No memory for label.");
92	wwcursor(w, 1);
93	/*
94	 * We have to do this little maneuver to make sure
95	 * addwin() puts w at the top, so we don't waste an
96	 * insert and delete operation.
97	 */
98	setselwin((struct ww *)0);
99	addwin(w, 0);
100	setselwin(w);
101	if (wwspawn(w, shf, sh) < 0) {
102		error("Can't execute %s: %s.", shf, wwerror());
103		closewin(w);
104		return 0;
105	}
106	return w;
107}
108
109int
110findid(void)
111{
112	int i;
113
114	for (i = 0; i < NWINDOW && window[i] != 0; i++)
115		;
116	if (i >= NWINDOW) {
117		error("Too many windows.");
118		return -1;
119	}
120	return i;
121}
122
123struct ww *
124findselwin(void)
125{
126	struct ww *w, *s = 0;
127	int i;
128
129	for (i = 0; i < NWINDOW; i++)
130		if ((w = window[i]) != 0 && w != selwin &&
131		    (s == 0 ||
132		     (!isfg(w) && (w->ww_order < s->ww_order || isfg(s)))))
133			s = w;
134	return s;
135}
136
137/*
138 * Close a user window.  Close all if w == 0.
139 */
140void
141closewin(struct ww *w)
142{
143	char didit = 0;
144	int i;
145
146	if (w != 0) {
147		closewin1(w);
148		didit++;
149	} else
150		for (i = 0; i < NWINDOW; i++) {
151			if ((w = window[i]) == 0)
152				continue;
153			closewin1(w);
154			didit++;
155		}
156	if (didit) {
157		if (selwin == 0) {
158			if (lastselwin != 0) {
159				setselwin(lastselwin);
160				lastselwin = 0;
161			} else if ((w = findselwin()))
162				setselwin(w);
163		}
164		if (lastselwin == 0 && selwin)
165			if ((w = findselwin()))
166				lastselwin = w;
167		reframe();
168	}
169}
170
171/*
172 * Open an information (display) window.
173 */
174struct ww *
175openiwin(int nrow, const char *label)
176{
177	struct ww *w;
178
179	if ((w = wwopen(WWT_INTERNAL, 0, nrow, wwncol, 2, 0, 0)) == 0)
180		return 0;
181	SET(w->ww_wflags, WWW_MAPNL | WWW_NOINTR | WWW_NOUPDATE | WWW_UNCTRL);
182	SET(w->ww_uflags, WWU_HASFRAME | WWU_CENTER);
183	w->ww_id = -1;
184	(void) setlabel(w, label);
185	addwin(w, 1);
186	reframe();
187	return w;
188}
189
190/*
191 * Close an information window.
192 */
193void
194closeiwin(struct ww *w)
195{
196	closewin1(w);
197	reframe();
198}
199
200void
201closewin1(struct ww *w)
202{
203	if (w == selwin)
204		selwin = 0;
205	if (w == lastselwin)
206		lastselwin = 0;
207	if (w->ww_id >= 0 && w->ww_id < NWINDOW)
208		window[w->ww_id] = 0;
209	if (w->ww_label)
210		str_free(w->ww_label);
211	deletewin(w);
212	wwclose(w);
213}
214
215/*
216 * Move the window to the top of its group.
217 * Don't do it if already fully visible.
218 * Wwvisible() doesn't work for tinted windows.
219 * But anything to make it faster.
220 * Always reframe() if doreframe is true.
221 */
222void
223front(struct ww *w, char doreframe)
224{
225	if (w->ww_back != (isfg(w) ? framewin : fgwin) && !wwvisible(w)) {
226		deletewin(w);
227		addwin(w, isfg(w));
228		doreframe = 1;
229	}
230	if (doreframe)
231		reframe();
232}
233
234/*
235 * Add a window at the top of normal windows or foreground windows.
236 * For normal windows, we put it behind the current window.
237 */
238void
239addwin(struct ww *w, char fg)
240{
241	if (fg) {
242		wwadd(w, framewin);
243		if (fgwin == framewin)
244			fgwin = w;
245	} else
246		wwadd(w, selwin != 0 && selwin != w && !isfg(selwin)
247				? selwin : fgwin);
248}
249
250/*
251 * Delete a window.
252 */
253void
254deletewin(struct ww *w)
255{
256	if (fgwin == w)
257		fgwin = w->ww_back;
258	wwdelete(w);
259}
260
261void
262reframe(void)
263{
264	struct ww *w;
265
266	wwunframe(framewin);
267	for (w = wwhead.ww_back; w != &wwhead; w = w->ww_back)
268		if (ISSET(w->ww_uflags, WWU_HASFRAME)) {
269			wwframe(w, framewin);
270			labelwin(w);
271		}
272}
273
274void
275labelwin(struct ww *w)
276{
277	int mode = w == selwin ? WWM_REV : 0;
278
279	if (!ISSET(w->ww_uflags, WWU_HASFRAME))
280		return;
281	if (w->ww_id >= 0) {
282		char buf[2];
283
284		buf[0] = w->ww_id + '1';
285		buf[1] = 0;
286		wwlabel(w, framewin, 1, buf, mode);
287	}
288	if (w->ww_label) {
289		int col;
290
291		if (ISSET(w->ww_uflags, WWU_CENTER)) {
292			col = (w->ww_w.nc - strlen(w->ww_label)) / 2;
293			col = MAX(3, col);
294		} else
295			col = 3;
296		wwlabel(w, framewin, col, w->ww_label, mode);
297	}
298}
299
300void
301stopwin(struct ww *w)
302{
303	if (w->ww_pty >= 0 && w->ww_type == WWT_PTY && wwstoptty(w->ww_pty) < 0)
304		error("Can't stop output: %s.", wwerror());
305	else
306		SET(w->ww_pflags, WWP_STOPPED);
307}
308
309void
310startwin(struct ww *w)
311{
312	if (w->ww_pty >= 0 && w->ww_type == WWT_PTY &&
313	    wwstarttty(w->ww_pty) < 0)
314		error("Can't start output: %s.", wwerror());
315	else
316		CLR(w->ww_pflags, WWP_STOPPED);
317}
318
319void
320sizewin(struct ww *w, int nrow, int ncol)
321{
322	struct ww *back = w->ww_back;
323
324	w->ww_alt.nr = w->ww_w.nr;
325	w->ww_alt.nc = w->ww_w.nc;
326	wwdelete(w);
327	if (wwsize(w, nrow, ncol) < 0)
328		error("Can't resize window: %s.", wwerror());
329	wwadd(w, back);
330	reframe();
331}
332
333void
334waitnl(struct ww *w)
335{
336	(void) waitnl1(w, "[Type any key to continue]");
337}
338
339int
340more(struct ww *w, char always)
341{
342	int c;
343	int uc = ISSET(w->ww_wflags, WWW_UNCTRL);
344
345	if (!always && w->ww_cur.r < w->ww_w.b - 2)
346		return 0;
347	c = waitnl1(w, "[Type escape to abort, any other key to continue]");
348	CLR(w->ww_wflags, WWW_UNCTRL);
349	wwputs("\033E", w);
350	SET(w->ww_wflags, uc);
351	return c == ctrl('[') ? 2 : 1;
352}
353
354int
355waitnl1(struct ww *w, const char *prompt)
356{
357	int uc = ISSET(w->ww_wflags, WWW_UNCTRL);
358
359	CLR(w->ww_wflags, WWW_UNCTRL);
360	front(w, 0);
361	wwprintf(w, "\033Y%c%c\033sA%s\033rA ",
362		w->ww_w.nr - 1 + ' ', ' ', prompt);	/* print on last line */
363	wwcurtowin(w);
364	while (wwpeekc() < 0)
365		wwiomux();
366	SET(w->ww_wflags, uc);
367	return wwgetc();
368}
369