xref: /openbsd-src/sys/dev/wscons/wsdisplay.c (revision 7350f337b9e3eb4461d99580e625c7ef148d107c)
1 /* $OpenBSD: wsdisplay.c,v 1.132 2019/05/04 11:34:48 kettenis Exp $ */
2 /* $NetBSD: wsdisplay.c,v 1.82 2005/02/27 00:27:52 perry Exp $ */
3 
4 /*
5  * Copyright (c) 1996, 1997 Christopher G. Demetriou.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Christopher G. Demetriou
18  *	for the NetBSD Project.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/param.h>
35 #include <sys/conf.h>
36 #include <sys/device.h>
37 #include <sys/ioctl.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/syslog.h>
41 #include <sys/systm.h>
42 #include <sys/tty.h>
43 #include <sys/signalvar.h>
44 #include <sys/errno.h>
45 #include <sys/fcntl.h>
46 #include <sys/vnode.h>
47 #include <sys/timeout.h>
48 #include <sys/poll.h>
49 
50 #include <dev/wscons/wscons_features.h>
51 #include <dev/wscons/wsconsio.h>
52 #include <dev/wscons/wsdisplayvar.h>
53 #include <dev/wscons/wsksymvar.h>
54 #include <dev/wscons/wsksymdef.h>
55 #include <dev/wscons/wsemulvar.h>
56 #include <dev/wscons/wscons_callbacks.h>
57 #include <dev/cons.h>
58 
59 #include "wsdisplay.h"
60 #include "wskbd.h"
61 #include "wsmux.h"
62 
63 #if NWSKBD > 0
64 #include <dev/wscons/wseventvar.h>
65 #include <dev/wscons/wsmuxvar.h>
66 #endif
67 
68 #include "wsmoused.h"
69 
70 struct wsscreen_internal {
71 	const struct wsdisplay_emulops *emulops;
72 	void	*emulcookie;
73 
74 	const struct wsscreen_descr *scrdata;
75 
76 	const struct wsemul_ops *wsemul;
77 	void	*wsemulcookie;
78 };
79 
80 struct wsscreen {
81 	struct wsscreen_internal *scr_dconf;
82 
83 	struct tty *scr_tty;
84 	int	scr_hold_screen;		/* hold tty output */
85 
86 	int scr_flags;
87 #define SCR_OPEN 1		/* is it open? */
88 #define SCR_WAITACTIVE 2	/* someone waiting on activation */
89 #define SCR_GRAPHICS 4		/* graphics mode, no text (emulation) output */
90 #define	SCR_DUMBFB 8		/* in use as dumb fb (iff SCR_GRAPHICS) */
91 
92 #ifdef WSDISPLAY_COMPAT_USL
93 	const struct wscons_syncops *scr_syncops;
94 	void *scr_synccookie;
95 #endif
96 
97 #ifdef WSDISPLAY_COMPAT_RAWKBD
98 	int scr_rawkbd;
99 #endif
100 
101 	struct wsdisplay_softc *sc;
102 
103 #ifdef HAVE_WSMOUSED_SUPPORT
104 	/* mouse console support via wsmoused(8) */
105 	u_int mouse;		/* mouse cursor position */
106 	u_int cursor;		/* selection cursor position (if
107 				   different from mouse cursor pos) */
108 	u_int cpy_start;	/* position of the copy start mark*/
109 	u_int cpy_end;		/* position of the copy end mark */
110 	u_int orig_start;	/* position of the original sel. start*/
111 	u_int orig_end;		/* position of the original sel. end */
112 
113 	u_int mouse_flags;	/* flags, status of the mouse */
114 #define MOUSE_VISIBLE	0x01	/* flag, the mouse cursor is visible */
115 #define SEL_EXISTS	0x02	/* flag, a selection exists */
116 #define SEL_IN_PROGRESS 0x04	/* flag, a selection is in progress */
117 #define SEL_EXT_AFTER	0x08	/* flag, selection is extended after */
118 #define BLANK_TO_EOL	0x10	/* flag, there are only blanks
119 				   characters to eol */
120 #define SEL_BY_CHAR	0x20	/* flag, select character by character*/
121 #define SEL_BY_WORD	0x40	/* flag, select word by word */
122 #define SEL_BY_LINE	0x80	/* flag, select line by line */
123 
124 #define IS_MOUSE_VISIBLE(scr)	((scr)->mouse_flags & MOUSE_VISIBLE)
125 #define IS_SEL_EXISTS(scr)	((scr)->mouse_flags & SEL_EXISTS)
126 #define IS_SEL_IN_PROGRESS(scr)	((scr)->mouse_flags & SEL_IN_PROGRESS)
127 #define IS_SEL_EXT_AFTER(scr)	((scr)->mouse_flags & SEL_EXT_AFTER)
128 #define IS_BLANK_TO_EOL(scr)	((scr)->mouse_flags & BLANK_TO_EOL)
129 #define IS_SEL_BY_CHAR(scr)	((scr)->mouse_flags & SEL_BY_CHAR)
130 #define IS_SEL_BY_WORD(scr)	((scr)->mouse_flags & SEL_BY_WORD)
131 #define IS_SEL_BY_LINE(scr)	((scr)->mouse_flags & SEL_BY_LINE)
132 #endif	/* HAVE_WSMOUSED_SUPPORT */
133 };
134 
135 struct wsscreen *wsscreen_attach(struct wsdisplay_softc *, int, const char *,
136 	    const struct wsscreen_descr *, void *, int, int, long);
137 void	wsscreen_detach(struct wsscreen *);
138 int	wsdisplay_addscreen(struct wsdisplay_softc *, int, const char *,
139 	    const char *);
140 int	wsdisplay_getscreen(struct wsdisplay_softc *,
141 	    struct wsdisplay_addscreendata *);
142 void	wsdisplay_resume_device(struct device *);
143 void	wsdisplay_suspend_device(struct device *);
144 void	wsdisplay_addscreen_print(struct wsdisplay_softc *, int, int);
145 void	wsdisplay_closescreen(struct wsdisplay_softc *, struct wsscreen *);
146 int	wsdisplay_delscreen(struct wsdisplay_softc *, int, int);
147 
148 void	wsdisplay_burner_setup(struct wsdisplay_softc *, struct wsscreen *);
149 void	wsdisplay_burner(void *v);
150 
151 struct wsdisplay_softc {
152 	struct device sc_dv;
153 
154 	const struct wsdisplay_accessops *sc_accessops;
155 	void	*sc_accesscookie;
156 
157 	const struct wsscreen_list *sc_scrdata;
158 
159 	struct wsscreen *sc_scr[WSDISPLAY_MAXSCREEN];
160 	int sc_focusidx;	/* available only if sc_focus isn't null */
161 	struct wsscreen *sc_focus;
162 
163 #ifdef HAVE_BURNER_SUPPORT
164 	struct timeout sc_burner;
165 	int	sc_burnoutintvl;	/* delay before blanking */
166 	int	sc_burninintvl;		/* delay before unblanking */
167 	int	sc_burnout;		/* current sc_burner delay */
168 	int	sc_burnman;		/* nonzero if screen blanked */
169 	int	sc_burnflags;
170 #endif
171 
172 	int	sc_isconsole;
173 
174 	int sc_flags;
175 #define SC_SWITCHPENDING	0x01
176 #define	SC_PASTE_AVAIL		0x02
177 	int sc_screenwanted, sc_oldscreen; /* valid with SC_SWITCHPENDING */
178 	int sc_resumescreen; /* if set, can't switch until resume. */
179 
180 #if NWSKBD > 0
181 	struct wsevsrc *sc_input;
182 #ifdef WSDISPLAY_COMPAT_RAWKBD
183 	int sc_rawkbd;
184 #endif
185 #endif /* NWSKBD > 0 */
186 
187 #ifdef HAVE_WSMOUSED_SUPPORT
188 	char *sc_copybuffer;
189 	u_int sc_copybuffer_size;
190 #endif
191 };
192 
193 extern struct cfdriver wsdisplay_cd;
194 
195 /* Autoconfiguration definitions. */
196 int	wsdisplay_emul_match(struct device *, void *, void *);
197 void	wsdisplay_emul_attach(struct device *, struct device *, void *);
198 int	wsdisplay_emul_detach(struct device *, int);
199 
200 int	wsdisplay_activate(struct device *, int);
201 
202 struct cfdriver wsdisplay_cd = {
203 	NULL, "wsdisplay", DV_TTY
204 };
205 
206 struct cfattach wsdisplay_emul_ca = {
207 	sizeof(struct wsdisplay_softc), wsdisplay_emul_match,
208 	wsdisplay_emul_attach, wsdisplay_emul_detach, wsdisplay_activate
209 };
210 
211 void	wsdisplaystart(struct tty *);
212 int	wsdisplayparam(struct tty *, struct termios *);
213 
214 /* Internal macros, functions, and variables. */
215 #define	WSDISPLAYUNIT(dev)		(minor(dev) >> 8)
216 #define	WSDISPLAYSCREEN(dev)		(minor(dev) & 0xff)
217 #define ISWSDISPLAYCTL(dev)		(WSDISPLAYSCREEN(dev) == 255)
218 #define WSDISPLAYMINOR(unit, screen)	(((unit) << 8) | (screen))
219 
220 #define	WSSCREEN_HAS_TTY(scr)		((scr)->scr_tty != NULL)
221 
222 void	wsdisplay_common_attach(struct wsdisplay_softc *sc,
223 	    int console, int mux, const struct wsscreen_list *,
224 	    const struct wsdisplay_accessops *accessops,
225 	    void *accesscookie, u_int defaultscreens);
226 int	wsdisplay_common_detach(struct wsdisplay_softc *, int);
227 void	wsdisplay_kbdholdscr(struct wsscreen *, int);
228 
229 #ifdef WSDISPLAY_COMPAT_RAWKBD
230 int	wsdisplay_update_rawkbd(struct wsdisplay_softc *, struct wsscreen *);
231 #endif
232 
233 int	wsdisplay_console_initted;
234 struct wsdisplay_softc *wsdisplay_console_device;
235 struct wsscreen_internal wsdisplay_console_conf;
236 
237 int	wsdisplay_getc_dummy(dev_t);
238 void	wsdisplay_pollc(dev_t, int);
239 
240 int	wsdisplay_cons_pollmode;
241 void	(*wsdisplay_cons_kbd_pollc)(dev_t, int);
242 
243 struct consdev wsdisplay_cons = {
244 	NULL, NULL, wsdisplay_getc_dummy, wsdisplay_cnputc,
245 	    wsdisplay_pollc, NULL, NODEV, CN_LOWPRI
246 };
247 
248 #ifndef WSDISPLAY_DEFAULTSCREENS
249 #define WSDISPLAY_DEFAULTSCREENS	1
250 #endif
251 int	wsdisplay_defaultscreens = WSDISPLAY_DEFAULTSCREENS;
252 
253 int	wsdisplay_switch1(void *, int, int);
254 int	wsdisplay_switch2(void *, int, int);
255 int	wsdisplay_switch3(void *, int, int);
256 
257 int	wsdisplay_clearonclose;
258 
259 struct wsscreen *
260 wsscreen_attach(struct wsdisplay_softc *sc, int console, const char *emul,
261     const struct wsscreen_descr *type, void *cookie, int ccol, int crow,
262     long defattr)
263 {
264 	struct wsscreen_internal *dconf;
265 	struct wsscreen *scr;
266 
267 	scr = malloc(sizeof(*scr), M_DEVBUF, M_ZERO | M_NOWAIT);
268 	if (!scr)
269 		return (NULL);
270 
271 	if (console) {
272 		dconf = &wsdisplay_console_conf;
273 		/*
274 		 * Tell the emulation about the callback argument.
275 		 * The other stuff is already there.
276 		 */
277 		(void)(*dconf->wsemul->attach)(1, 0, 0, 0, 0, scr, 0);
278 	} else { /* not console */
279 		dconf = malloc(sizeof(*dconf), M_DEVBUF, M_NOWAIT);
280 		if (dconf == NULL)
281 			goto fail;
282 		dconf->emulops = type->textops;
283 		dconf->emulcookie = cookie;
284 		if (dconf->emulops == NULL ||
285 		    (dconf->wsemul = wsemul_pick(emul)) == NULL)
286 			goto fail;
287 		dconf->wsemulcookie = (*dconf->wsemul->attach)(0, type, cookie,
288 		    ccol, crow, scr, defattr);
289 		if (dconf->wsemulcookie == NULL)
290 			goto fail;
291 		dconf->scrdata = type;
292 	}
293 
294 	scr->scr_dconf = dconf;
295 	scr->scr_tty = ttymalloc(0);
296 	scr->sc = sc;
297 	return (scr);
298 
299 fail:
300 	if (dconf != NULL)
301 		free(dconf, M_DEVBUF, sizeof(*dconf));
302 	free(scr, M_DEVBUF, sizeof(*scr));
303 	return (NULL);
304 }
305 
306 void
307 wsscreen_detach(struct wsscreen *scr)
308 {
309 	int ccol, crow; /* XXX */
310 
311 	if (WSSCREEN_HAS_TTY(scr)) {
312 		timeout_del(&scr->scr_tty->t_rstrt_to);
313 		ttyfree(scr->scr_tty);
314 	}
315 	(*scr->scr_dconf->wsemul->detach)(scr->scr_dconf->wsemulcookie,
316 	    &ccol, &crow);
317 	free(scr->scr_dconf, M_DEVBUF, sizeof(*scr->scr_dconf));
318 	free(scr, M_DEVBUF, sizeof(*scr));
319 }
320 
321 const struct wsscreen_descr *
322 wsdisplay_screentype_pick(const struct wsscreen_list *scrdata, const char *name)
323 {
324 	int i;
325 	const struct wsscreen_descr *scr;
326 
327 	KASSERT(scrdata->nscreens > 0);
328 
329 	if (name == NULL || *name == '\0')
330 		return (scrdata->screens[0]);
331 
332 	for (i = 0; i < scrdata->nscreens; i++) {
333 		scr = scrdata->screens[i];
334 		if (!strncmp(name, scr->name, WSSCREEN_NAME_SIZE))
335 			return (scr);
336 	}
337 
338 	return (0);
339 }
340 
341 /*
342  * print info about attached screen
343  */
344 void
345 wsdisplay_addscreen_print(struct wsdisplay_softc *sc, int idx, int count)
346 {
347 	printf("%s: screen %d", sc->sc_dv.dv_xname, idx);
348 	if (count > 1)
349 		printf("-%d", idx + (count-1));
350 	printf(" added (%s, %s emulation)\n",
351 	    sc->sc_scr[idx]->scr_dconf->scrdata->name,
352 	    sc->sc_scr[idx]->scr_dconf->wsemul->name);
353 }
354 
355 int
356 wsdisplay_addscreen(struct wsdisplay_softc *sc, int idx,
357     const char *screentype, const char *emul)
358 {
359 	const struct wsscreen_descr *scrdesc;
360 	int error;
361 	void *cookie;
362 	int ccol, crow;
363 	long defattr;
364 	struct wsscreen *scr;
365 	int s;
366 
367 	if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN)
368 		return (EINVAL);
369 	if (sc->sc_scr[idx] != NULL)
370 		return (EBUSY);
371 
372 	scrdesc = wsdisplay_screentype_pick(sc->sc_scrdata, screentype);
373 	if (!scrdesc)
374 		return (ENXIO);
375 	error = (*sc->sc_accessops->alloc_screen)(sc->sc_accesscookie,
376 	    scrdesc, &cookie, &ccol, &crow, &defattr);
377 	if (error)
378 		return (error);
379 
380 	scr = wsscreen_attach(sc, 0, emul, scrdesc,
381 	    cookie, ccol, crow, defattr);
382 	if (scr == NULL) {
383 		(*sc->sc_accessops->free_screen)(sc->sc_accesscookie, cookie);
384 		return (ENXIO);
385 	}
386 
387 	sc->sc_scr[idx] = scr;
388 
389 	/* if no screen has focus yet, activate the first we get */
390 	s = spltty();
391 	if (!sc->sc_focus) {
392 		(*sc->sc_accessops->show_screen)(sc->sc_accesscookie,
393 		    scr->scr_dconf->emulcookie, 0, 0, 0);
394 		sc->sc_focusidx = idx;
395 		sc->sc_focus = scr;
396 	}
397 	splx(s);
398 
399 #ifdef HAVE_WSMOUSED_SUPPORT
400 	allocate_copybuffer(sc); /* enlarge the copy buffer if necessary */
401 #endif
402 	return (0);
403 }
404 
405 int
406 wsdisplay_getscreen(struct wsdisplay_softc *sc,
407     struct wsdisplay_addscreendata *sd)
408 {
409 	struct wsscreen *scr;
410 
411 	if (sd->idx < 0 && sc->sc_focus)
412 		sd->idx = sc->sc_focusidx;
413 
414 	if (sd->idx < 0 || sd->idx >= WSDISPLAY_MAXSCREEN)
415 		return (EINVAL);
416 
417 	scr = sc->sc_scr[sd->idx];
418 	if (scr == NULL)
419 		return (ENXIO);
420 
421 	strncpy(sd->screentype, scr->scr_dconf->scrdata->name,
422 	    WSSCREEN_NAME_SIZE);
423 	strncpy(sd->emul, scr->scr_dconf->wsemul->name, WSEMUL_NAME_SIZE);
424 
425 	return (0);
426 }
427 
428 void
429 wsdisplay_closescreen(struct wsdisplay_softc *sc, struct wsscreen *scr)
430 {
431 	int maj, mn, idx;
432 
433 	/* hangup */
434 	if (WSSCREEN_HAS_TTY(scr)) {
435 		struct tty *tp = scr->scr_tty;
436 		(*linesw[tp->t_line].l_modem)(tp, 0);
437 	}
438 
439 	/* locate the major number */
440 	for (maj = 0; maj < nchrdev; maj++)
441 		if (cdevsw[maj].d_open == wsdisplayopen)
442 			break;
443 	/* locate the screen index */
444 	for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++)
445 		if (scr == sc->sc_scr[idx])
446 			break;
447 #ifdef DIAGNOSTIC
448 	if (idx == WSDISPLAY_MAXSCREEN)
449 		panic("wsdisplay_forceclose: bad screen");
450 #endif
451 
452 	/* nuke the vnodes */
453 	mn = WSDISPLAYMINOR(sc->sc_dv.dv_unit, idx);
454 	vdevgone(maj, mn, mn, VCHR);
455 }
456 
457 int
458 wsdisplay_delscreen(struct wsdisplay_softc *sc, int idx, int flags)
459 {
460 	struct wsscreen *scr;
461 	int s;
462 	void *cookie;
463 
464 	if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN)
465 		return (EINVAL);
466 	if ((scr = sc->sc_scr[idx]) == NULL)
467 		return (ENXIO);
468 
469 	if (scr->scr_dconf == &wsdisplay_console_conf ||
470 #ifdef WSDISPLAY_COMPAT_USL
471 	    scr->scr_syncops ||
472 #endif
473 	    ((scr->scr_flags & SCR_OPEN) && !(flags & WSDISPLAY_DELSCR_FORCE)))
474 		return (EBUSY);
475 
476 	wsdisplay_closescreen(sc, scr);
477 
478 	/*
479 	 * delete pointers, so neither device entries
480 	 * nor keyboard input can reference it anymore
481 	 */
482 	s = spltty();
483 	if (sc->sc_focus == scr) {
484 		sc->sc_focus = NULL;
485 #ifdef WSDISPLAY_COMPAT_RAWKBD
486 		wsdisplay_update_rawkbd(sc, 0);
487 #endif
488 	}
489 	sc->sc_scr[idx] = NULL;
490 	splx(s);
491 
492 	/*
493 	 * Wake up processes waiting for the screen to
494 	 * be activated. Sleepers must check whether
495 	 * the screen still exists.
496 	 */
497 	if (scr->scr_flags & SCR_WAITACTIVE)
498 		wakeup(scr);
499 
500 	/* save a reference to the graphics screen */
501 	cookie = scr->scr_dconf->emulcookie;
502 
503 	wsscreen_detach(scr);
504 
505 	(*sc->sc_accessops->free_screen)(sc->sc_accesscookie, cookie);
506 
507 	if ((flags & WSDISPLAY_DELSCR_QUIET) == 0)
508 		printf("%s: screen %d deleted\n", sc->sc_dv.dv_xname, idx);
509 	return (0);
510 }
511 
512 /*
513  * Autoconfiguration functions.
514  */
515 int
516 wsdisplay_emul_match(struct device *parent, void *match, void *aux)
517 {
518 	struct cfdata *cf = match;
519 	struct wsemuldisplaydev_attach_args *ap = aux;
520 
521 	if (cf->wsemuldisplaydevcf_console != WSEMULDISPLAYDEVCF_CONSOLE_UNK) {
522 		/*
523 		 * If console-ness of device specified, either match
524 		 * exactly (at high priority), or fail.
525 		 */
526 		if (cf->wsemuldisplaydevcf_console != 0 && ap->console != 0)
527 			return (10);
528 		else
529 			return (0);
530 	}
531 
532 	if (cf->wsemuldisplaydevcf_primary != WSEMULDISPLAYDEVCF_PRIMARY_UNK) {
533 		/*
534 		 * If primary-ness of device specified, either match
535 		 * exactly (at high priority), or fail.
536 		 */
537 		if (cf->wsemuldisplaydevcf_primary != 0 && ap->primary != 0)
538 			return (10);
539 		else
540 			return (0);
541 	}
542 
543 	/* If console-ness and primary-ness unspecified, it wins. */
544 	return (1);
545 }
546 
547 void
548 wsdisplay_emul_attach(struct device *parent, struct device *self, void *aux)
549 {
550 	struct wsdisplay_softc *sc = (struct wsdisplay_softc *)self;
551 	struct wsemuldisplaydev_attach_args *ap = aux;
552 
553 	wsdisplay_common_attach(sc, ap->console,
554 	    sc->sc_dv.dv_cfdata->wsemuldisplaydevcf_mux, ap->scrdata,
555 	    ap->accessops, ap->accesscookie, ap->defaultscreens);
556 
557 	if (ap->console && cn_tab == &wsdisplay_cons) {
558 		int maj;
559 
560 		/* locate the major number */
561 		for (maj = 0; maj < nchrdev; maj++)
562 			if (cdevsw[maj].d_open == wsdisplayopen)
563 				break;
564 
565 		cn_tab->cn_dev = makedev(maj, WSDISPLAYMINOR(self->dv_unit, 0));
566 	}
567 }
568 
569 /*
570  * Detach a display.
571  */
572 int
573 wsdisplay_emul_detach(struct device *self, int flags)
574 {
575 	struct wsdisplay_softc *sc = (struct wsdisplay_softc *)self;
576 
577 	return (wsdisplay_common_detach(sc, flags));
578 }
579 
580 int
581 wsdisplay_activate(struct device *self, int act)
582 {
583 	int ret = 0;
584 
585 	switch (act) {
586 	case DVACT_POWERDOWN:
587 		wsdisplay_switchtoconsole();
588 		break;
589 	}
590 
591 	return (ret);
592 }
593 
594 int
595 wsdisplay_common_detach(struct wsdisplay_softc *sc, int flags)
596 {
597 	int i;
598 	int rc;
599 
600 	/* We don't support detaching the console display yet. */
601 	if (sc->sc_isconsole)
602 		return (EBUSY);
603 
604 	/* Delete all screens managed by this display */
605 	for (i = 0; i < WSDISPLAY_MAXSCREEN; i++)
606 		if (sc->sc_scr[i] != NULL) {
607 			if ((rc = wsdisplay_delscreen(sc, i,
608 			    WSDISPLAY_DELSCR_QUIET | (flags & DETACH_FORCE ?
609 			     WSDISPLAY_DELSCR_FORCE : 0))) != 0)
610 				return (rc);
611 		}
612 
613 #ifdef HAVE_BURNER_SUPPORT
614 	timeout_del(&sc->sc_burner);
615 #endif
616 
617 #if NWSKBD > 0
618 	if (sc->sc_input != NULL) {
619 #if NWSMUX > 0
620 		/*
621 		 * If we are the display of the mux we are attached to,
622 		 * disconnect all input devices from us.
623 		 */
624 		if (sc->sc_input->me_dispdv == &sc->sc_dv) {
625 			if ((rc = wsmux_set_display((struct wsmux_softc *)
626 						    sc->sc_input, NULL)) != 0)
627 				return (rc);
628 		}
629 
630 		/*
631 		 * XXX
632 		 * If we created a standalone mux (dmux), we should destroy it
633 		 * there, but there is currently no support for this in wsmux.
634 		 */
635 #else
636 		if ((rc = wskbd_set_display((struct device *)sc->sc_input,
637 		    NULL)) != 0)
638 			return (rc);
639 #endif
640 	}
641 #endif
642 
643 	return (0);
644 }
645 
646 /* Print function (for parent devices). */
647 int
648 wsemuldisplaydevprint(void *aux, const char *pnp)
649 {
650 #if 0 /* -Wunused */
651 	struct wsemuldisplaydev_attach_args *ap = aux;
652 #endif
653 
654 	if (pnp)
655 		printf("wsdisplay at %s", pnp);
656 #if 0 /* don't bother; it's ugly */
657 	printf(" console %d", ap->console);
658 #endif
659 
660 	return (UNCONF);
661 }
662 
663 /* Submatch function (for parent devices). */
664 int
665 wsemuldisplaydevsubmatch(struct device *parent, void *match, void *aux)
666 {
667 	extern struct cfdriver wsdisplay_cd;
668 	struct cfdata *cf = match;
669 
670 	/* only allow wsdisplay to attach */
671 	if (cf->cf_driver == &wsdisplay_cd)
672 		return ((*cf->cf_attach->ca_match)(parent, match, aux));
673 
674 	return (0);
675 }
676 
677 void
678 wsdisplay_common_attach(struct wsdisplay_softc *sc, int console, int kbdmux,
679     const struct wsscreen_list *scrdata,
680     const struct wsdisplay_accessops *accessops, void *accesscookie,
681     u_int defaultscreens)
682 {
683 	int i, start = 0;
684 #if NWSKBD > 0
685 	struct wsevsrc *kme;
686 #if NWSMUX > 0
687 	struct wsmux_softc *mux;
688 
689 	if (kbdmux >= 0)
690 		mux = wsmux_getmux(kbdmux);
691 	else
692 		mux = wsmux_create("dmux", sc->sc_dv.dv_unit);
693 	/* XXX panic()ing isn't nice, but attach cannot fail */
694 	if (mux == NULL)
695 		panic("wsdisplay_common_attach: no memory");
696 	sc->sc_input = &mux->sc_base;
697 
698 	if (kbdmux >= 0)
699 		printf(" mux %d", kbdmux);
700 #else
701 #if 0	/* not worth keeping, especially since the default value is not -1... */
702 	if (kbdmux >= 0)
703 		printf(" (mux ignored)");
704 #endif
705 #endif	/* NWSMUX > 0 */
706 #endif	/* NWSKBD > 0 */
707 
708 	sc->sc_isconsole = console;
709 	sc->sc_resumescreen = WSDISPLAY_NULLSCREEN;
710 
711 	if (console) {
712 		KASSERT(wsdisplay_console_initted);
713 		KASSERT(wsdisplay_console_device == NULL);
714 
715 		sc->sc_scr[0] = wsscreen_attach(sc, 1, 0, 0, 0, 0, 0, 0);
716 		if (sc->sc_scr[0] == NULL)
717 			return;
718 		wsdisplay_console_device = sc;
719 
720 		printf(": console (%s, %s emulation)",
721 		       wsdisplay_console_conf.scrdata->name,
722 		       wsdisplay_console_conf.wsemul->name);
723 
724 #if NWSKBD > 0
725 		kme = wskbd_set_console_display(&sc->sc_dv, sc->sc_input);
726 		if (kme != NULL)
727 			printf(", using %s", kme->me_dv.dv_xname);
728 #if NWSMUX == 0
729 		sc->sc_input = kme;
730 #endif
731 #endif
732 
733 		sc->sc_focusidx = 0;
734 		sc->sc_focus = sc->sc_scr[0];
735 		start = 1;
736 	}
737 	printf("\n");
738 
739 #if NWSKBD > 0 && NWSMUX > 0
740 	/*
741 	 * If this mux did not have a display device yet, volunteer for
742 	 * the job.
743 	 */
744 	if (mux->sc_displaydv == NULL)
745 		wsmux_set_display(mux, &sc->sc_dv);
746 #endif
747 
748 	sc->sc_accessops = accessops;
749 	sc->sc_accesscookie = accesscookie;
750 	sc->sc_scrdata = scrdata;
751 
752 	/*
753 	 * Set up a number of virtual screens if wanted. The
754 	 * WSDISPLAYIO_ADDSCREEN ioctl is more flexible, so this code
755 	 * is for special cases like installation kernels, as well as
756 	 * sane multihead defaults.
757 	 */
758 	if (defaultscreens == 0)
759 		defaultscreens = wsdisplay_defaultscreens;
760 	for (i = start; i < defaultscreens; i++) {
761 		if (wsdisplay_addscreen(sc, i, 0, 0))
762 			break;
763 	}
764 
765 	if (i > start)
766 		wsdisplay_addscreen_print(sc, start, i-start);
767 
768 #ifdef HAVE_BURNER_SUPPORT
769 	sc->sc_burnoutintvl = (hz * WSDISPLAY_DEFBURNOUT) / 1000;
770 	sc->sc_burninintvl = (hz * WSDISPLAY_DEFBURNIN) / 1000;
771 	sc->sc_burnflags = WSDISPLAY_BURN_OUTPUT | WSDISPLAY_BURN_KBD |
772 	    WSDISPLAY_BURN_MOUSE;
773 	timeout_set(&sc->sc_burner, wsdisplay_burner, sc);
774 	sc->sc_burnout = sc->sc_burnoutintvl;
775 	wsdisplay_burn(sc, sc->sc_burnflags);
776 #endif
777 
778 #if NWSKBD > 0 && NWSMUX == 0
779 	if (console == 0) {
780 		/*
781 		 * In the non-wsmux world, always connect wskbd0 and wsdisplay0
782 		 * together.
783 		 */
784 		extern struct cfdriver wskbd_cd;
785 
786 		if (wskbd_cd.cd_ndevs != 0 && sc->sc_dv.dv_unit == 0) {
787 			if (wsdisplay_set_kbd(&sc->sc_dv,
788 			    (struct wsevsrc *)wskbd_cd.cd_devs[0]) == 0)
789 				wskbd_set_display(wskbd_cd.cd_devs[0],
790 				    &sc->sc_dv);
791 		}
792 	}
793 #endif
794 }
795 
796 void
797 wsdisplay_cnattach(const struct wsscreen_descr *type, void *cookie, int ccol,
798     int crow, long defattr)
799 {
800 	const struct wsemul_ops *wsemul;
801 	const struct wsdisplay_emulops *emulops;
802 
803 	KASSERT(type->nrows > 0);
804 	KASSERT(type->ncols > 0);
805 	KASSERT(crow < type->nrows);
806 	KASSERT(ccol < type->ncols);
807 
808 	wsdisplay_console_conf.emulops = emulops = type->textops;
809 	wsdisplay_console_conf.emulcookie = cookie;
810 	wsdisplay_console_conf.scrdata = type;
811 
812 #ifdef WSEMUL_DUMB
813 	/*
814 	 * If the emulops structure is crippled, force a dumb emulation.
815 	 */
816 	if (emulops->cursor == NULL ||
817 	    emulops->copycols == NULL || emulops->copyrows == NULL ||
818 	    emulops->erasecols == NULL || emulops->eraserows == NULL)
819 		wsemul = wsemul_pick("dumb");
820 	else
821 #endif
822 		wsemul = wsemul_pick("");
823 	wsdisplay_console_conf.wsemul = wsemul;
824 	wsdisplay_console_conf.wsemulcookie =
825 	    (*wsemul->cnattach)(type, cookie, ccol, crow, defattr);
826 
827 	if (!wsdisplay_console_initted)
828 		cn_tab = &wsdisplay_cons;
829 
830 	wsdisplay_console_initted = 1;
831 }
832 
833 /*
834  * Tty and cdevsw functions.
835  */
836 int
837 wsdisplayopen(dev_t dev, int flag, int mode, struct proc *p)
838 {
839 	struct wsdisplay_softc *sc;
840 	struct tty *tp;
841 	int unit, newopen, error;
842 	struct wsscreen *scr;
843 
844 	unit = WSDISPLAYUNIT(dev);
845 	if (unit >= wsdisplay_cd.cd_ndevs ||	/* make sure it was attached */
846 	    (sc = wsdisplay_cd.cd_devs[unit]) == NULL)
847 		return (ENXIO);
848 
849 	if (ISWSDISPLAYCTL(dev))
850 		return (0);
851 
852 	if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN)
853 		return (ENXIO);
854 	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
855 		return (ENXIO);
856 
857 	if (WSSCREEN_HAS_TTY(scr)) {
858 		tp = scr->scr_tty;
859 		tp->t_oproc = wsdisplaystart;
860 		tp->t_param = wsdisplayparam;
861 		tp->t_dev = dev;
862 		newopen = (tp->t_state & TS_ISOPEN) == 0;
863 		if (newopen) {
864 			ttychars(tp);
865 			tp->t_iflag = TTYDEF_IFLAG;
866 			tp->t_oflag = TTYDEF_OFLAG;
867 			tp->t_cflag = TTYDEF_CFLAG;
868 			tp->t_lflag = TTYDEF_LFLAG;
869 			tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
870 			wsdisplayparam(tp, &tp->t_termios);
871 			ttsetwater(tp);
872 		} else if ((tp->t_state & TS_XCLUDE) != 0 &&
873 			   suser(p) != 0)
874 			return (EBUSY);
875 		tp->t_state |= TS_CARR_ON;
876 
877 		error = ((*linesw[tp->t_line].l_open)(dev, tp, p));
878 		if (error)
879 			return (error);
880 
881 		if (newopen) {
882 			/* set window sizes as appropriate, and reset
883 			   the emulation */
884 			tp->t_winsize.ws_row = scr->scr_dconf->scrdata->nrows;
885 			tp->t_winsize.ws_col = scr->scr_dconf->scrdata->ncols;
886 		}
887 	}
888 
889 	scr->scr_flags |= SCR_OPEN;
890 	return (0);
891 }
892 
893 int
894 wsdisplayclose(dev_t dev, int flag, int mode, struct proc *p)
895 {
896 	struct wsdisplay_softc *sc;
897 	struct tty *tp;
898 	int unit;
899 	struct wsscreen *scr;
900 
901 	unit = WSDISPLAYUNIT(dev);
902 	sc = wsdisplay_cd.cd_devs[unit];
903 
904 	if (ISWSDISPLAYCTL(dev))
905 		return (0);
906 
907 	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
908 		return (ENXIO);
909 
910 	if (WSSCREEN_HAS_TTY(scr)) {
911 		if (scr->scr_hold_screen) {
912 			int s;
913 
914 			/* XXX RESET KEYBOARD LEDS, etc. */
915 			s = spltty();	/* avoid conflict with keyboard */
916 			wsdisplay_kbdholdscr(scr, 0);
917 			splx(s);
918 		}
919 		tp = scr->scr_tty;
920 		(*linesw[tp->t_line].l_close)(tp, flag, p);
921 		ttyclose(tp);
922 	}
923 
924 #ifdef WSDISPLAY_COMPAT_USL
925 	if (scr->scr_syncops)
926 		(*scr->scr_syncops->destroy)(scr->scr_synccookie);
927 #endif
928 
929 	scr->scr_flags &= ~SCR_GRAPHICS;
930 	(*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie,
931 					 WSEMUL_RESET);
932 	if (wsdisplay_clearonclose)
933 		(*scr->scr_dconf->wsemul->reset)
934 			(scr->scr_dconf->wsemulcookie, WSEMUL_CLEARSCREEN);
935 
936 #ifdef WSDISPLAY_COMPAT_RAWKBD
937 	if (scr->scr_rawkbd) {
938 		int kbmode = WSKBD_TRANSLATED;
939 		(void) wsdisplay_internal_ioctl(sc, scr, WSKBDIO_SETMODE,
940 		    (caddr_t)&kbmode, FWRITE, p);
941 	}
942 #endif
943 
944 	scr->scr_flags &= ~SCR_OPEN;
945 
946 #ifdef HAVE_WSMOUSED_SUPPORT
947 	/* remove the selection at logout */
948 	if (sc->sc_copybuffer != NULL)
949 		explicit_bzero(sc->sc_copybuffer, sc->sc_copybuffer_size);
950 	CLR(sc->sc_flags, SC_PASTE_AVAIL);
951 #endif
952 
953 	return (0);
954 }
955 
956 int
957 wsdisplayread(dev_t dev, struct uio *uio, int flag)
958 {
959 	struct wsdisplay_softc *sc;
960 	struct tty *tp;
961 	int unit;
962 	struct wsscreen *scr;
963 
964 	unit = WSDISPLAYUNIT(dev);
965 	sc = wsdisplay_cd.cd_devs[unit];
966 
967 	if (ISWSDISPLAYCTL(dev))
968 		return (0);
969 
970 	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
971 		return (ENXIO);
972 
973 	if (!WSSCREEN_HAS_TTY(scr))
974 		return (ENODEV);
975 
976 	tp = scr->scr_tty;
977 	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
978 }
979 
980 int
981 wsdisplaywrite(dev_t dev, struct uio *uio, int flag)
982 {
983 	struct wsdisplay_softc *sc;
984 	struct tty *tp;
985 	int unit;
986 	struct wsscreen *scr;
987 
988 	unit = WSDISPLAYUNIT(dev);
989 	sc = wsdisplay_cd.cd_devs[unit];
990 
991 	if (ISWSDISPLAYCTL(dev))
992 		return (0);
993 
994 	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
995 		return (ENXIO);
996 
997 	if (!WSSCREEN_HAS_TTY(scr))
998 		return (ENODEV);
999 
1000 	tp = scr->scr_tty;
1001 	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
1002 }
1003 
1004 struct tty *
1005 wsdisplaytty(dev_t dev)
1006 {
1007 	struct wsdisplay_softc *sc;
1008 	int unit;
1009 	struct wsscreen *scr;
1010 
1011 	unit = WSDISPLAYUNIT(dev);
1012 	sc = wsdisplay_cd.cd_devs[unit];
1013 
1014 	if (ISWSDISPLAYCTL(dev))
1015 		panic("wsdisplaytty() on ctl device");
1016 
1017 	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1018 		return (NULL);
1019 
1020 	return (scr->scr_tty);
1021 }
1022 
1023 int
1024 wsdisplayioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
1025 {
1026 	struct wsdisplay_softc *sc;
1027 	struct tty *tp;
1028 	int unit, error;
1029 	struct wsscreen *scr;
1030 
1031 	unit = WSDISPLAYUNIT(dev);
1032 	sc = wsdisplay_cd.cd_devs[unit];
1033 
1034 #ifdef WSDISPLAY_COMPAT_USL
1035 	error = wsdisplay_usl_ioctl1(sc, cmd, data, flag, p);
1036 	if (error >= 0)
1037 		return (error);
1038 #endif
1039 
1040 	if (ISWSDISPLAYCTL(dev)) {
1041 	       	if (cmd != WSDISPLAYIO_GTYPE)
1042 			return (wsdisplay_cfg_ioctl(sc, cmd, data, flag, p));
1043 		/* pass WSDISPLAYIO_GTYPE to the first screen */
1044 		dev = makedev(major(dev), WSDISPLAYMINOR(unit, 0));
1045 	}
1046 
1047 	if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN)
1048 		return (ENODEV);
1049 
1050 	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1051 		return (ENXIO);
1052 
1053 	if (WSSCREEN_HAS_TTY(scr)) {
1054 		tp = scr->scr_tty;
1055 
1056 /* printf("disc\n"); */
1057 		/* do the line discipline ioctls first */
1058 		error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
1059 		if (error >= 0)
1060 			return (error);
1061 
1062 /* printf("tty\n"); */
1063 		/* then the tty ioctls */
1064 		error = ttioctl(tp, cmd, data, flag, p);
1065 		if (error >= 0)
1066 			return (error);
1067 	}
1068 
1069 #ifdef WSDISPLAY_COMPAT_USL
1070 	error = wsdisplay_usl_ioctl2(sc, scr, cmd, data, flag, p);
1071 	if (error >= 0)
1072 		return (error);
1073 #endif
1074 
1075 	error = wsdisplay_internal_ioctl(sc, scr, cmd, data, flag, p);
1076 	return (error != -1 ? error : ENOTTY);
1077 }
1078 
1079 int
1080 wsdisplay_param(struct device *dev, u_long cmd, struct wsdisplay_param *dp)
1081 {
1082 	struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
1083 
1084 	return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd,
1085 	    (caddr_t)dp, 0, NULL));
1086 }
1087 
1088 int
1089 wsdisplay_internal_ioctl(struct wsdisplay_softc *sc, struct wsscreen *scr,
1090     u_long cmd, caddr_t data, int flag, struct proc *p)
1091 {
1092 	int error;
1093 
1094 #if NWSKBD > 0
1095 	struct wsevsrc *inp;
1096 
1097 #ifdef WSDISPLAY_COMPAT_RAWKBD
1098 	switch (cmd) {
1099 	case WSKBDIO_SETMODE:
1100 		if ((flag & FWRITE) == 0)
1101 			return (EACCES);
1102 		scr->scr_rawkbd = (*(int *)data == WSKBD_RAW);
1103 		return (wsdisplay_update_rawkbd(sc, scr));
1104 	case WSKBDIO_GETMODE:
1105 		*(int *)data = (scr->scr_rawkbd ?
1106 				WSKBD_RAW : WSKBD_TRANSLATED);
1107 		return (0);
1108 	}
1109 #endif
1110 	inp = sc->sc_input;
1111 	if (inp != NULL) {
1112 		error = wsevsrc_display_ioctl(inp, cmd, data, flag, p);
1113 		if (error >= 0)
1114 			return (error);
1115 	}
1116 #endif /* NWSKBD > 0 */
1117 
1118 	switch (cmd) {
1119 	case WSDISPLAYIO_SMODE:
1120 	case WSDISPLAYIO_USEFONT:
1121 #ifdef HAVE_BURNER_SUPPORT
1122 	case WSDISPLAYIO_SVIDEO:
1123 	case WSDISPLAYIO_SBURNER:
1124 #endif
1125 	case WSDISPLAYIO_SETSCREEN:
1126 		if ((flag & FWRITE) == 0)
1127 			return (EACCES);
1128 	}
1129 
1130 	switch (cmd) {
1131 	case WSDISPLAYIO_GMODE:
1132 		if (scr->scr_flags & SCR_GRAPHICS) {
1133 			if (scr->scr_flags & SCR_DUMBFB)
1134 				*(u_int *)data = WSDISPLAYIO_MODE_DUMBFB;
1135 			else
1136 				*(u_int *)data = WSDISPLAYIO_MODE_MAPPED;
1137 		} else
1138 			*(u_int *)data = WSDISPLAYIO_MODE_EMUL;
1139 		return (0);
1140 
1141 	case WSDISPLAYIO_SMODE:
1142 #define d (*(int *)data)
1143 		if (d != WSDISPLAYIO_MODE_EMUL &&
1144 		    d != WSDISPLAYIO_MODE_MAPPED &&
1145 		    d != WSDISPLAYIO_MODE_DUMBFB)
1146 			return (EINVAL);
1147 
1148 		scr->scr_flags &= ~SCR_GRAPHICS;
1149 		if (d == WSDISPLAYIO_MODE_MAPPED ||
1150 		    d == WSDISPLAYIO_MODE_DUMBFB) {
1151 			scr->scr_flags |= SCR_GRAPHICS |
1152 			    ((d == WSDISPLAYIO_MODE_DUMBFB) ?  SCR_DUMBFB : 0);
1153 
1154 			/* clear cursor */
1155 			(*scr->scr_dconf->wsemul->reset)
1156 			    (scr->scr_dconf->wsemulcookie, WSEMUL_CLEARCURSOR);
1157 		}
1158 
1159 #ifdef HAVE_BURNER_SUPPORT
1160 		wsdisplay_burner_setup(sc, scr);
1161 #endif
1162 
1163 		(void)(*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data,
1164 		    flag, p);
1165 
1166 		return (0);
1167 #undef d
1168 
1169 	case WSDISPLAYIO_USEFONT:
1170 #define d ((struct wsdisplay_font *)data)
1171 		if (!sc->sc_accessops->load_font)
1172 			return (EINVAL);
1173 		d->data = NULL;
1174 		error = (*sc->sc_accessops->load_font)(sc->sc_accesscookie,
1175 		    scr->scr_dconf->emulcookie, d);
1176 		if (!error)
1177 			(*scr->scr_dconf->wsemul->reset)
1178 			    (scr->scr_dconf->wsemulcookie, WSEMUL_SYNCFONT);
1179 		return (error);
1180 #undef d
1181 #ifdef HAVE_BURNER_SUPPORT
1182 	case WSDISPLAYIO_GVIDEO:
1183 		*(u_int *)data = !sc->sc_burnman;
1184 		break;
1185 
1186 	case WSDISPLAYIO_SVIDEO:
1187 		if (*(u_int *)data != WSDISPLAYIO_VIDEO_OFF &&
1188 		    *(u_int *)data != WSDISPLAYIO_VIDEO_ON)
1189 			return (EINVAL);
1190 		if (sc->sc_accessops->burn_screen == NULL)
1191 			return (EOPNOTSUPP);
1192 		(*sc->sc_accessops->burn_screen)(sc->sc_accesscookie,
1193 		     *(u_int *)data, sc->sc_burnflags);
1194 		sc->sc_burnman = *(u_int *)data == WSDISPLAYIO_VIDEO_OFF;
1195 		break;
1196 
1197 	case WSDISPLAYIO_GBURNER:
1198 #define d ((struct wsdisplay_burner *)data)
1199 		d->on  = sc->sc_burninintvl  * 1000 / hz;
1200 		d->off = sc->sc_burnoutintvl * 1000 / hz;
1201 		d->flags = sc->sc_burnflags;
1202 		return (0);
1203 
1204 	case WSDISPLAYIO_SBURNER:
1205 	    {
1206 		struct wsscreen *active;
1207 
1208 		if (d->flags & ~(WSDISPLAY_BURN_VBLANK | WSDISPLAY_BURN_KBD |
1209 		    WSDISPLAY_BURN_MOUSE | WSDISPLAY_BURN_OUTPUT))
1210 			return EINVAL;
1211 
1212 		error = 0;
1213 		sc->sc_burnflags = d->flags;
1214 		/* disable timeout if necessary */
1215 		if ((sc->sc_burnflags & (WSDISPLAY_BURN_OUTPUT |
1216 		    WSDISPLAY_BURN_KBD | WSDISPLAY_BURN_MOUSE)) == 0) {
1217 			if (sc->sc_burnout)
1218 				timeout_del(&sc->sc_burner);
1219 		}
1220 
1221 		active = sc->sc_focus;
1222 		if (active == NULL)
1223 			active = scr;
1224 
1225 		if (d->on) {
1226 			sc->sc_burninintvl = hz * d->on / 1000;
1227 			if (sc->sc_burnman) {
1228 				sc->sc_burnout = sc->sc_burninintvl;
1229 				/* reinit timeout if changed */
1230 				if ((active->scr_flags & SCR_GRAPHICS) == 0)
1231 					wsdisplay_burn(sc, sc->sc_burnflags);
1232 			}
1233 		}
1234 		if (d->off) {
1235 			sc->sc_burnoutintvl = hz * d->off / 1000;
1236 			if (!sc->sc_burnman) {
1237 				sc->sc_burnout = sc->sc_burnoutintvl;
1238 				/* reinit timeout if changed */
1239 				if ((active->scr_flags & SCR_GRAPHICS) == 0)
1240 					wsdisplay_burn(sc, sc->sc_burnflags);
1241 			}
1242 		}
1243 		return (error);
1244 	    }
1245 #undef d
1246 #endif	/* HAVE_BURNER_SUPPORT */
1247 	case WSDISPLAYIO_GETSCREEN:
1248 		return (wsdisplay_getscreen(sc,
1249 		    (struct wsdisplay_addscreendata *)data));
1250 
1251 	case WSDISPLAYIO_SETSCREEN:
1252 		return (wsdisplay_switch((void *)sc, *(int *)data, 1));
1253 
1254 	case WSDISPLAYIO_GETSCREENTYPE:
1255 #define d ((struct wsdisplay_screentype *)data)
1256 		if (d->idx >= sc->sc_scrdata->nscreens)
1257 			return(EINVAL);
1258 
1259 		d->nidx = sc->sc_scrdata->nscreens;
1260 		strncpy(d->name, sc->sc_scrdata->screens[d->idx]->name,
1261 			WSSCREEN_NAME_SIZE);
1262 		d->ncols = sc->sc_scrdata->screens[d->idx]->ncols;
1263 		d->nrows = sc->sc_scrdata->screens[d->idx]->nrows;
1264 		d->fontwidth = sc->sc_scrdata->screens[d->idx]->fontwidth;
1265 		d->fontheight = sc->sc_scrdata->screens[d->idx]->fontheight;
1266 		return (0);
1267 #undef d
1268 	case WSDISPLAYIO_GETEMULTYPE:
1269 #define d ((struct wsdisplay_emultype *)data)
1270 		if (wsemul_getname(d->idx) == NULL)
1271 			return(EINVAL);
1272 		strncpy(d->name, wsemul_getname(d->idx), WSEMUL_NAME_SIZE);
1273 		return (0);
1274 #undef d
1275         }
1276 
1277 	/* check ioctls for display */
1278 	return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data,
1279 	    flag, p));
1280 }
1281 
1282 int
1283 wsdisplay_cfg_ioctl(struct wsdisplay_softc *sc, u_long cmd, caddr_t data,
1284     int flag, struct proc *p)
1285 {
1286 	int error;
1287 	void *buf;
1288 	size_t fontsz;
1289 #if NWSKBD > 0
1290 	struct wsevsrc *inp;
1291 #endif
1292 
1293 	switch (cmd) {
1294 #ifdef HAVE_WSMOUSED_SUPPORT
1295 	case WSDISPLAYIO_WSMOUSED:
1296 		error = wsmoused(sc, data, flag, p);
1297 		return (error);
1298 #endif
1299 	case WSDISPLAYIO_ADDSCREEN:
1300 #define d ((struct wsdisplay_addscreendata *)data)
1301 		if ((error = wsdisplay_addscreen(sc, d->idx,
1302 		    d->screentype, d->emul)) == 0)
1303 			wsdisplay_addscreen_print(sc, d->idx, 0);
1304 		return (error);
1305 #undef d
1306 	case WSDISPLAYIO_DELSCREEN:
1307 #define d ((struct wsdisplay_delscreendata *)data)
1308 		return (wsdisplay_delscreen(sc, d->idx, d->flags));
1309 #undef d
1310 	case WSDISPLAYIO_GETSCREEN:
1311 		return (wsdisplay_getscreen(sc,
1312 		    (struct wsdisplay_addscreendata *)data));
1313 	case WSDISPLAYIO_SETSCREEN:
1314 		return (wsdisplay_switch((void *)sc, *(int *)data, 1));
1315 	case WSDISPLAYIO_LDFONT:
1316 #define d ((struct wsdisplay_font *)data)
1317 		if (!sc->sc_accessops->load_font)
1318 			return (EINVAL);
1319 		if (d->fontheight > 64 || d->stride > 8) /* 64x64 pixels */
1320 			return (EINVAL);
1321 		if (d->numchars > 65536) /* unicode plane */
1322 			return (EINVAL);
1323 		fontsz = d->fontheight * d->stride * d->numchars;
1324 		if (fontsz > WSDISPLAY_MAXFONTSZ)
1325 			return (EINVAL);
1326 
1327 		buf = malloc(fontsz, M_DEVBUF, M_WAITOK);
1328 		error = copyin(d->data, buf, fontsz);
1329 		if (error) {
1330 			free(buf, M_DEVBUF, fontsz);
1331 			return (error);
1332 		}
1333 		d->data = buf;
1334 		error =
1335 		  (*sc->sc_accessops->load_font)(sc->sc_accesscookie, 0, d);
1336 		if (error)
1337 			free(buf, M_DEVBUF, fontsz);
1338 		return (error);
1339 
1340 	case WSDISPLAYIO_LSFONT:
1341 		if (!sc->sc_accessops->list_font)
1342 			return (EINVAL);
1343 		error =
1344 		  (*sc->sc_accessops->list_font)(sc->sc_accesscookie, d);
1345 		return (error);
1346 
1347 	case WSDISPLAYIO_DELFONT:
1348 		return (EINVAL);
1349 #undef d
1350 
1351 #if NWSKBD > 0
1352 	case WSMUXIO_ADD_DEVICE:
1353 #define d ((struct wsmux_device *)data)
1354 		if (d->idx == -1 && d->type == WSMUX_KBD)
1355 			d->idx = wskbd_pickfree();
1356 #undef d
1357 		/* FALLTHROUGH */
1358 	case WSMUXIO_INJECTEVENT:
1359 	case WSMUXIO_REMOVE_DEVICE:
1360 	case WSMUXIO_LIST_DEVICES:
1361 		inp = sc->sc_input;
1362 		if (inp == NULL)
1363 			return (ENXIO);
1364 		return (wsevsrc_ioctl(inp, cmd, data, flag,p));
1365 #endif /* NWSKBD > 0 */
1366 
1367 	}
1368 	return (EINVAL);
1369 }
1370 
1371 paddr_t
1372 wsdisplaymmap(dev_t dev, off_t offset, int prot)
1373 {
1374 	struct wsdisplay_softc *sc = wsdisplay_cd.cd_devs[WSDISPLAYUNIT(dev)];
1375 	struct wsscreen *scr;
1376 
1377 	if (ISWSDISPLAYCTL(dev))
1378 		return (-1);
1379 
1380 	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1381 		return (-1);
1382 
1383 	if (!(scr->scr_flags & SCR_GRAPHICS))
1384 		return (-1);
1385 
1386 	/* pass mmap to display */
1387 	return ((*sc->sc_accessops->mmap)(sc->sc_accesscookie, offset, prot));
1388 }
1389 
1390 int
1391 wsdisplaypoll(dev_t dev, int events, struct proc *p)
1392 {
1393 	struct wsdisplay_softc *sc = wsdisplay_cd.cd_devs[WSDISPLAYUNIT(dev)];
1394 	struct wsscreen *scr;
1395 
1396 	if (ISWSDISPLAYCTL(dev))
1397 		return (0);
1398 
1399 	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1400 		return (POLLERR);
1401 
1402 	if (!WSSCREEN_HAS_TTY(scr))
1403 		return (POLLERR);
1404 
1405 	return (ttpoll(dev, events, p));
1406 }
1407 
1408 int
1409 wsdisplaykqfilter(dev_t dev, struct knote *kn)
1410 {
1411 	struct wsdisplay_softc *sc = wsdisplay_cd.cd_devs[WSDISPLAYUNIT(dev)];
1412 	struct wsscreen *scr;
1413 
1414 	if (ISWSDISPLAYCTL(dev))
1415 		return (ENXIO);
1416 
1417 	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1418 		return (ENXIO);
1419 
1420 	if (!WSSCREEN_HAS_TTY(scr))
1421 		return (ENXIO);
1422 
1423 	return (ttkqfilter(dev, kn));
1424 }
1425 
1426 void
1427 wsdisplaystart(struct tty *tp)
1428 {
1429 	struct wsdisplay_softc *sc;
1430 	struct wsscreen *scr;
1431 	int s, n, done, unit;
1432 	u_char *buf;
1433 
1434 	unit = WSDISPLAYUNIT(tp->t_dev);
1435 	if (unit >= wsdisplay_cd.cd_ndevs ||
1436 	    (sc = wsdisplay_cd.cd_devs[unit]) == NULL)
1437 		return;
1438 
1439 	s = spltty();
1440 	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
1441 		splx(s);
1442 		return;
1443 	}
1444 	if (tp->t_outq.c_cc == 0 && tp->t_wsel.si_seltid == 0)
1445 		goto low;
1446 
1447 	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(tp->t_dev)]) == NULL) {
1448 		splx(s);
1449 		return;
1450 	}
1451 	if (scr->scr_hold_screen) {
1452 		tp->t_state |= TS_TIMEOUT;
1453 		splx(s);
1454 		return;
1455 	}
1456 	tp->t_state |= TS_BUSY;
1457 	splx(s);
1458 
1459 	/*
1460 	 * Drain output from ring buffer.
1461 	 * The output will normally be in one contiguous chunk, but when the
1462 	 * ring wraps, it will be in two pieces.. one at the end of the ring,
1463 	 * the other at the start.  For performance, rather than loop here,
1464 	 * we output one chunk, see if there's another one, and if so, output
1465 	 * it too.
1466 	 */
1467 
1468 	n = ndqb(&tp->t_outq, 0);
1469 	buf = tp->t_outq.c_cf;
1470 
1471 	if (!(scr->scr_flags & SCR_GRAPHICS)) {
1472 #ifdef HAVE_BURNER_SUPPORT
1473 		wsdisplay_burn(sc, WSDISPLAY_BURN_OUTPUT);
1474 #endif
1475 #ifdef HAVE_WSMOUSED_SUPPORT
1476 		if (scr == sc->sc_focus)
1477 			mouse_remove(scr);
1478 #endif
1479 		done = (*scr->scr_dconf->wsemul->output)
1480 		    (scr->scr_dconf->wsemulcookie, buf, n, 0);
1481 	} else
1482 		done = n;
1483 	ndflush(&tp->t_outq, done);
1484 
1485 	if (done == n) {
1486 		if ((n = ndqb(&tp->t_outq, 0)) > 0) {
1487 			buf = tp->t_outq.c_cf;
1488 
1489 			if (!(scr->scr_flags & SCR_GRAPHICS)) {
1490 				done = (*scr->scr_dconf->wsemul->output)
1491 				    (scr->scr_dconf->wsemulcookie, buf, n, 0);
1492 			} else
1493 				done = n;
1494 			ndflush(&tp->t_outq, done);
1495 		}
1496 	}
1497 
1498 	s = spltty();
1499 	tp->t_state &= ~TS_BUSY;
1500 	/* Come back if there's more to do */
1501 	if (tp->t_outq.c_cc) {
1502 		tp->t_state |= TS_TIMEOUT;
1503 		timeout_add(&tp->t_rstrt_to, (hz > 128) ? (hz / 128) : 1);
1504 	}
1505 low:
1506 	ttwakeupwr(tp);
1507 	splx(s);
1508 }
1509 
1510 int
1511 wsdisplaystop(struct tty *tp, int flag)
1512 {
1513 	int s;
1514 
1515 	s = spltty();
1516 	if (ISSET(tp->t_state, TS_BUSY))
1517 		if (!ISSET(tp->t_state, TS_TTSTOP))
1518 			SET(tp->t_state, TS_FLUSH);
1519 	splx(s);
1520 
1521 	return (0);
1522 }
1523 
1524 /* Set line parameters. */
1525 int
1526 wsdisplayparam(struct tty *tp, struct termios *t)
1527 {
1528 
1529 	tp->t_ispeed = t->c_ispeed;
1530 	tp->t_ospeed = t->c_ospeed;
1531 	tp->t_cflag = t->c_cflag;
1532 	return (0);
1533 }
1534 
1535 /*
1536  * Callbacks for the emulation code.
1537  */
1538 void
1539 wsdisplay_emulbell(void *v)
1540 {
1541 	struct wsscreen *scr = v;
1542 
1543 	if (scr == NULL)		/* console, before real attach */
1544 		return;
1545 
1546 	if (scr->scr_flags & SCR_GRAPHICS) /* can this happen? */
1547 		return;
1548 
1549 	(void) wsdisplay_internal_ioctl(scr->sc, scr, WSKBDIO_BELL, NULL,
1550 	    FWRITE, NULL);
1551 }
1552 
1553 #if !defined(WSEMUL_NO_VT100)
1554 void
1555 wsdisplay_emulinput(void *v, const u_char *data, u_int count)
1556 {
1557 	struct wsscreen *scr = v;
1558 	struct tty *tp;
1559 
1560 	if (v == NULL)			/* console, before real attach */
1561 		return;
1562 
1563 	if (scr->scr_flags & SCR_GRAPHICS) /* XXX can't happen */
1564 		return;
1565 	if (!WSSCREEN_HAS_TTY(scr))
1566 		return;
1567 
1568 	tp = scr->scr_tty;
1569 	while (count-- > 0)
1570 		(*linesw[tp->t_line].l_rint)(*data++, tp);
1571 }
1572 #endif
1573 
1574 /*
1575  * Calls from the keyboard interface.
1576  */
1577 void
1578 wsdisplay_kbdinput(struct device *dev, kbd_t layout, keysym_t *ks, int num)
1579 {
1580 	struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
1581 	struct wsscreen *scr;
1582 	const u_char *dp;
1583 	int count;
1584 	struct tty *tp;
1585 
1586 	scr = sc->sc_focus;
1587 	if (!scr || !WSSCREEN_HAS_TTY(scr))
1588 		return;
1589 
1590 
1591 	tp = scr->scr_tty;
1592 	for (; num > 0; num--) {
1593 		count = (*scr->scr_dconf->wsemul->translate)
1594 		    (scr->scr_dconf->wsemulcookie, layout, *ks++, &dp);
1595 		while (count-- > 0)
1596 			(*linesw[tp->t_line].l_rint)(*dp++, tp);
1597 	}
1598 }
1599 
1600 #ifdef WSDISPLAY_COMPAT_RAWKBD
1601 void
1602 wsdisplay_rawkbdinput(struct device *dev, u_char *buf, int num)
1603 {
1604 	struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
1605 	struct wsscreen *scr;
1606 	struct tty *tp;
1607 
1608 	scr = sc->sc_focus;
1609 	if (!scr || !WSSCREEN_HAS_TTY(scr))
1610 		return;
1611 
1612 	tp = scr->scr_tty;
1613 	while (num-- > 0)
1614 		(*linesw[tp->t_line].l_rint)(*buf++, tp);
1615 }
1616 int
1617 wsdisplay_update_rawkbd(struct wsdisplay_softc *sc, struct wsscreen *scr)
1618 {
1619 #if NWSKBD > 0
1620 	int s, raw, data, error;
1621 	struct wsevsrc *inp;
1622 
1623 	s = spltty();
1624 
1625 	raw = (scr ? scr->scr_rawkbd : 0);
1626 
1627 	if (scr != sc->sc_focus || sc->sc_rawkbd == raw) {
1628 		splx(s);
1629 		return (0);
1630 	}
1631 
1632 	data = raw ? WSKBD_RAW : WSKBD_TRANSLATED;
1633 	inp = sc->sc_input;
1634 	if (inp == NULL) {
1635 		splx(s);
1636 		return (ENXIO);
1637 	}
1638 	error = wsevsrc_display_ioctl(inp, WSKBDIO_SETMODE, &data, FWRITE, 0);
1639 	if (!error)
1640 		sc->sc_rawkbd = raw;
1641 	splx(s);
1642 	return (error);
1643 #else
1644 	return (0);
1645 #endif
1646 }
1647 #endif
1648 
1649 int
1650 wsdisplay_switch3(void *arg, int error, int waitok)
1651 {
1652 	struct wsdisplay_softc *sc = arg;
1653 	int no;
1654 	struct wsscreen *scr;
1655 
1656 #ifdef WSDISPLAY_COMPAT_USL
1657 	if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) {
1658 		printf("wsdisplay_switch3: not switching\n");
1659 		return (EINVAL);
1660 	}
1661 
1662 	no = sc->sc_screenwanted;
1663 	if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
1664 		panic("wsdisplay_switch3: invalid screen %d", no);
1665 	scr = sc->sc_scr[no];
1666 	if (!scr) {
1667 		printf("wsdisplay_switch3: screen %d disappeared\n", no);
1668 		error = ENXIO;
1669 	}
1670 
1671 	if (error) {
1672 		/* try to recover, avoid recursion */
1673 
1674 		if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) {
1675 			printf("wsdisplay_switch3: giving up\n");
1676 			sc->sc_focus = NULL;
1677 #ifdef WSDISPLAY_COMPAT_RAWKBD
1678 			wsdisplay_update_rawkbd(sc, 0);
1679 #endif
1680 			CLR(sc->sc_flags, SC_SWITCHPENDING);
1681 			return (error);
1682 		}
1683 
1684 		sc->sc_screenwanted = sc->sc_oldscreen;
1685 		sc->sc_oldscreen = WSDISPLAY_NULLSCREEN;
1686 		return (wsdisplay_switch1(arg, 0, waitok));
1687 	}
1688 #else
1689 	/*
1690 	 * If we do not have syncops support, we come straight from
1691 	 * wsdisplay_switch2 which has already validated our arguments
1692 	 * and did not sleep.
1693 	 */
1694 	no = sc->sc_screenwanted;
1695 	scr = sc->sc_scr[no];
1696 #endif
1697 
1698 	CLR(sc->sc_flags, SC_SWITCHPENDING);
1699 
1700 #ifdef HAVE_BURNER_SUPPORT
1701 	if (!error)
1702 		wsdisplay_burner_setup(sc, scr);
1703 #endif
1704 
1705 	if (!error && (scr->scr_flags & SCR_WAITACTIVE))
1706 		wakeup(scr);
1707 	return (error);
1708 }
1709 
1710 int
1711 wsdisplay_switch2(void *arg, int error, int waitok)
1712 {
1713 	struct wsdisplay_softc *sc = arg;
1714 	int no;
1715 	struct wsscreen *scr;
1716 
1717 	if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) {
1718 		printf("wsdisplay_switch2: not switching\n");
1719 		return (EINVAL);
1720 	}
1721 
1722 	no = sc->sc_screenwanted;
1723 	if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
1724 		panic("wsdisplay_switch2: invalid screen %d", no);
1725 	scr = sc->sc_scr[no];
1726 	if (!scr) {
1727 		printf("wsdisplay_switch2: screen %d disappeared\n", no);
1728 		error = ENXIO;
1729 	}
1730 
1731 	if (error) {
1732 		/* try to recover, avoid recursion */
1733 
1734 		if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) {
1735 			printf("wsdisplay_switch2: giving up\n");
1736 			sc->sc_focus = NULL;
1737 			CLR(sc->sc_flags, SC_SWITCHPENDING);
1738 			return (error);
1739 		}
1740 
1741 		sc->sc_screenwanted = sc->sc_oldscreen;
1742 		sc->sc_oldscreen = WSDISPLAY_NULLSCREEN;
1743 		return (wsdisplay_switch1(arg, 0, waitok));
1744 	}
1745 
1746 	sc->sc_focusidx = no;
1747 	sc->sc_focus = scr;
1748 
1749 #ifdef WSDISPLAY_COMPAT_RAWKBD
1750 	(void) wsdisplay_update_rawkbd(sc, scr);
1751 #endif
1752 	/* keyboard map??? */
1753 
1754 #ifdef WSDISPLAY_COMPAT_USL
1755 #define wsswitch_cb3 ((void (*)(void *, int, int))wsdisplay_switch3)
1756 	if (scr->scr_syncops) {
1757 		error = (*scr->scr_syncops->attach)(scr->scr_synccookie, waitok,
1758 		    sc->sc_isconsole && wsdisplay_cons_pollmode ?
1759 		      0 : wsswitch_cb3, sc);
1760 		if (error == EAGAIN) {
1761 			/* switch will be done asynchronously */
1762 			return (0);
1763 		}
1764 	}
1765 #endif
1766 
1767 	return (wsdisplay_switch3(sc, error, waitok));
1768 }
1769 
1770 int
1771 wsdisplay_switch1(void *arg, int error, int waitok)
1772 {
1773 	struct wsdisplay_softc *sc = arg;
1774 	int no;
1775 	struct wsscreen *scr;
1776 
1777 	if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) {
1778 		printf("wsdisplay_switch1: not switching\n");
1779 		return (EINVAL);
1780 	}
1781 
1782 	no = sc->sc_screenwanted;
1783 	if (no == WSDISPLAY_NULLSCREEN) {
1784 		CLR(sc->sc_flags, SC_SWITCHPENDING);
1785 		if (!error) {
1786 			sc->sc_focus = NULL;
1787 		}
1788 		wakeup(sc);
1789 		return (error);
1790 	}
1791 	if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
1792 		panic("wsdisplay_switch1: invalid screen %d", no);
1793 	scr = sc->sc_scr[no];
1794 	if (!scr) {
1795 		printf("wsdisplay_switch1: screen %d disappeared\n", no);
1796 		error = ENXIO;
1797 	}
1798 
1799 	if (error) {
1800 		CLR(sc->sc_flags, SC_SWITCHPENDING);
1801 		return (error);
1802 	}
1803 
1804 #define wsswitch_cb2 ((void (*)(void *, int, int))wsdisplay_switch2)
1805 	error = (*sc->sc_accessops->show_screen)(sc->sc_accesscookie,
1806 	    scr->scr_dconf->emulcookie, waitok,
1807 	    sc->sc_isconsole && wsdisplay_cons_pollmode ? 0 : wsswitch_cb2, sc);
1808 	if (error == EAGAIN) {
1809 		/* switch will be done asynchronously */
1810 		return (0);
1811 	}
1812 
1813 	return (wsdisplay_switch2(sc, error, waitok));
1814 }
1815 
1816 int
1817 wsdisplay_switch(struct device *dev, int no, int waitok)
1818 {
1819 	struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
1820 	int s, res = 0;
1821 	struct wsscreen *scr;
1822 
1823 	if (no != WSDISPLAY_NULLSCREEN) {
1824 		if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
1825 			return (EINVAL);
1826 		if (sc->sc_scr[no] == NULL)
1827 			return (ENXIO);
1828 	}
1829 
1830 	s = spltty();
1831 
1832 	while (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN && res == 0)
1833 		res = tsleep(&sc->sc_resumescreen, PCATCH, "wsrestore", 0);
1834 	if (res) {
1835 		splx(s);
1836 		return (res);
1837 	}
1838 
1839 	if ((sc->sc_focus && no == sc->sc_focusidx) ||
1840 	    (sc->sc_focus == NULL && no == WSDISPLAY_NULLSCREEN)) {
1841 		splx(s);
1842 		return (0);
1843 	}
1844 
1845 	if (ISSET(sc->sc_flags, SC_SWITCHPENDING)) {
1846 		splx(s);
1847 		return (EBUSY);
1848 	}
1849 
1850 	SET(sc->sc_flags, SC_SWITCHPENDING);
1851 	sc->sc_screenwanted = no;
1852 
1853 	splx(s);
1854 
1855 	scr = sc->sc_focus;
1856 	if (!scr) {
1857 		sc->sc_oldscreen = WSDISPLAY_NULLSCREEN;
1858 		return (wsdisplay_switch1(sc, 0, waitok));
1859 	} else
1860 		sc->sc_oldscreen = sc->sc_focusidx;
1861 
1862 #ifdef WSDISPLAY_COMPAT_USL
1863 #define wsswitch_cb1 ((void (*)(void *, int, int))wsdisplay_switch1)
1864 	if (scr->scr_syncops) {
1865 		res = (*scr->scr_syncops->detach)(scr->scr_synccookie, waitok,
1866 		    sc->sc_isconsole && wsdisplay_cons_pollmode ?
1867 		      0 : wsswitch_cb1, sc);
1868 		if (res == EAGAIN) {
1869 			/* switch will be done asynchronously */
1870 			return (0);
1871 		}
1872 	} else if (scr->scr_flags & SCR_GRAPHICS) {
1873 		/* no way to save state */
1874 		res = EBUSY;
1875 	}
1876 #endif
1877 
1878 #ifdef HAVE_WSMOUSED_SUPPORT
1879 	mouse_remove(scr);
1880 #endif
1881 
1882 	return (wsdisplay_switch1(sc, res, waitok));
1883 }
1884 
1885 void
1886 wsdisplay_reset(struct device *dev, enum wsdisplay_resetops op)
1887 {
1888 	struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
1889 	struct wsscreen *scr;
1890 
1891 	scr = sc->sc_focus;
1892 
1893 	if (!scr)
1894 		return;
1895 
1896 	switch (op) {
1897 	case WSDISPLAY_RESETEMUL:
1898 		(*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie,
1899 		    WSEMUL_RESET);
1900 		break;
1901 	case WSDISPLAY_RESETCLOSE:
1902 		wsdisplay_closescreen(sc, scr);
1903 		break;
1904 	}
1905 }
1906 
1907 #ifdef WSDISPLAY_COMPAT_USL
1908 /*
1909  * Interface for (external) VT switch / process synchronization code
1910  */
1911 int
1912 wsscreen_attach_sync(struct wsscreen *scr, const struct wscons_syncops *ops,
1913     void *cookie)
1914 {
1915 	if (scr->scr_syncops) {
1916 		/*
1917 		 * The screen is already claimed.
1918 		 * Check if the owner is still alive.
1919 		 */
1920 		if ((*scr->scr_syncops->check)(scr->scr_synccookie))
1921 			return (EBUSY);
1922 	}
1923 	scr->scr_syncops = ops;
1924 	scr->scr_synccookie = cookie;
1925 	return (0);
1926 }
1927 
1928 int
1929 wsscreen_detach_sync(struct wsscreen *scr)
1930 {
1931 	if (!scr->scr_syncops)
1932 		return (EINVAL);
1933 	scr->scr_syncops = NULL;
1934 	return (0);
1935 }
1936 
1937 int
1938 wsscreen_lookup_sync(struct wsscreen *scr,
1939     const struct wscons_syncops *ops, /* used as ID */
1940     void **cookiep)
1941 {
1942 	if (!scr->scr_syncops || ops != scr->scr_syncops)
1943 		return (EINVAL);
1944 	*cookiep = scr->scr_synccookie;
1945 	return (0);
1946 }
1947 #endif
1948 
1949 /*
1950  * Interface to virtual screen stuff
1951  */
1952 int
1953 wsdisplay_maxscreenidx(struct wsdisplay_softc *sc)
1954 {
1955 	return (WSDISPLAY_MAXSCREEN - 1);
1956 }
1957 
1958 int
1959 wsdisplay_screenstate(struct wsdisplay_softc *sc, int idx)
1960 {
1961 	if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN)
1962 		return (EINVAL);
1963 	if (!sc->sc_scr[idx])
1964 		return (ENXIO);
1965 	return ((sc->sc_scr[idx]->scr_flags & SCR_OPEN) ? EBUSY : 0);
1966 }
1967 
1968 int
1969 wsdisplay_getactivescreen(struct wsdisplay_softc *sc)
1970 {
1971 	return (sc->sc_focus ? sc->sc_focusidx : WSDISPLAY_NULLSCREEN);
1972 }
1973 
1974 int
1975 wsscreen_switchwait(struct wsdisplay_softc *sc, int no)
1976 {
1977 	struct wsscreen *scr;
1978 	int s, res = 0;
1979 
1980 	if (no == WSDISPLAY_NULLSCREEN) {
1981 		s = spltty();
1982 		while (sc->sc_focus && res == 0) {
1983 			res = tsleep(sc, PCATCH, "wswait", 0);
1984 		}
1985 		splx(s);
1986 		return (res);
1987 	}
1988 
1989 	if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
1990 		return (ENXIO);
1991 	scr = sc->sc_scr[no];
1992 	if (!scr)
1993 		return (ENXIO);
1994 
1995 	s = spltty();
1996 	if (scr != sc->sc_focus) {
1997 		scr->scr_flags |= SCR_WAITACTIVE;
1998 		res = tsleep(scr, PCATCH, "wswait2", 0);
1999 		if (scr != sc->sc_scr[no])
2000 			res = ENXIO; /* disappeared in the meantime */
2001 		else
2002 			scr->scr_flags &= ~SCR_WAITACTIVE;
2003 	}
2004 	splx(s);
2005 	return (res);
2006 }
2007 
2008 void
2009 wsdisplay_kbdholdscr(struct wsscreen *scr, int hold)
2010 {
2011 	if (hold)
2012 		scr->scr_hold_screen = 1;
2013 	else {
2014 		scr->scr_hold_screen = 0;
2015 		timeout_add(&scr->scr_tty->t_rstrt_to, 0); /* "immediate" */
2016 	}
2017 }
2018 
2019 void
2020 wsdisplay_kbdholdscreen(struct device *dev, int hold)
2021 {
2022 	struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
2023 	struct wsscreen *scr;
2024 
2025 	scr = sc->sc_focus;
2026 	if (scr != NULL && WSSCREEN_HAS_TTY(scr))
2027 		wsdisplay_kbdholdscr(scr, hold);
2028 }
2029 
2030 #if NWSKBD > 0
2031 void
2032 wsdisplay_set_console_kbd(struct wsevsrc *src)
2033 {
2034 	if (wsdisplay_console_device == NULL) {
2035 		src->me_dispdv = NULL;
2036 		return;
2037 	}
2038 #if NWSMUX > 0
2039 	if (wsmux_attach_sc((struct wsmux_softc *)
2040 			    wsdisplay_console_device->sc_input, src)) {
2041 		src->me_dispdv = NULL;
2042 		return;
2043 	}
2044 #else
2045 	wsdisplay_console_device->sc_input = src;
2046 #endif
2047 	src->me_dispdv = &wsdisplay_console_device->sc_dv;
2048 }
2049 
2050 #if NWSMUX == 0
2051 int
2052 wsdisplay_set_kbd(struct device *disp, struct wsevsrc *kbd)
2053 {
2054 	struct wsdisplay_softc *sc = (struct wsdisplay_softc *)disp;
2055 
2056 	if (sc->sc_input != NULL)
2057 		return (EBUSY);
2058 
2059 	sc->sc_input = kbd;
2060 
2061 	return (0);
2062 }
2063 #endif
2064 
2065 #endif /* NWSKBD > 0 */
2066 
2067 /*
2068  * Console interface.
2069  */
2070 void
2071 wsdisplay_cnputc(dev_t dev, int i)
2072 {
2073 	struct wsscreen_internal *dc;
2074 	char c = i;
2075 
2076 	if (!wsdisplay_console_initted)
2077 		return;
2078 
2079 	if (wsdisplay_console_device != NULL &&
2080 	    (wsdisplay_console_device->sc_scr[0] != NULL) &&
2081 	    (wsdisplay_console_device->sc_scr[0]->scr_flags & SCR_GRAPHICS))
2082 		return;
2083 
2084 	dc = &wsdisplay_console_conf;
2085 #ifdef HAVE_BURNER_SUPPORT
2086 	/*wsdisplay_burn(wsdisplay_console_device, WSDISPLAY_BURN_OUTPUT);*/
2087 #endif
2088 	(void)(*dc->wsemul->output)(dc->wsemulcookie, &c, 1, 1);
2089 }
2090 
2091 int
2092 wsdisplay_getc_dummy(dev_t dev)
2093 {
2094 	/* panic? */
2095 	return (0);
2096 }
2097 
2098 void
2099 wsdisplay_pollc(dev_t dev, int on)
2100 {
2101 
2102 	wsdisplay_cons_pollmode = on;
2103 
2104 	/* notify to fb drivers */
2105 	if (wsdisplay_console_device != NULL &&
2106 	    wsdisplay_console_device->sc_accessops->pollc != NULL)
2107 		(*wsdisplay_console_device->sc_accessops->pollc)
2108 		    (wsdisplay_console_device->sc_accesscookie, on);
2109 
2110 	/* notify to kbd drivers */
2111 	if (wsdisplay_cons_kbd_pollc)
2112 		(*wsdisplay_cons_kbd_pollc)(dev, on);
2113 }
2114 
2115 void
2116 wsdisplay_set_cons_kbd(int (*get)(dev_t), void (*poll)(dev_t, int),
2117     void (*bell)(dev_t, u_int, u_int, u_int))
2118 {
2119 	wsdisplay_cons.cn_getc = get;
2120 	wsdisplay_cons.cn_bell = bell;
2121 	wsdisplay_cons_kbd_pollc = poll;
2122 }
2123 
2124 void
2125 wsdisplay_unset_cons_kbd(void)
2126 {
2127 	wsdisplay_cons.cn_getc = wsdisplay_getc_dummy;
2128 	wsdisplay_cons.cn_bell = NULL;
2129 	wsdisplay_cons_kbd_pollc = NULL;
2130 }
2131 
2132 /*
2133  * Switch the console display to its first screen.
2134  */
2135 void
2136 wsdisplay_switchtoconsole(void)
2137 {
2138 	struct wsdisplay_softc *sc;
2139 	struct wsscreen *scr;
2140 
2141 	if (wsdisplay_console_device != NULL && cn_tab == &wsdisplay_cons) {
2142 		sc = wsdisplay_console_device;
2143 		if ((scr = sc->sc_scr[0]) == NULL)
2144 			return;
2145 		(*sc->sc_accessops->show_screen)(sc->sc_accesscookie,
2146 		    scr->scr_dconf->emulcookie, 0, NULL, NULL);
2147 	}
2148 }
2149 
2150 /*
2151  * Switch rhe console display to its ddb screen, avoiding locking
2152  * where we can.
2153  */
2154 void
2155 wsdisplay_enter_ddb(void)
2156 {
2157 	struct wsdisplay_softc *sc;
2158 	struct wsscreen *scr;
2159 
2160 	if (wsdisplay_console_device != NULL && cn_tab == &wsdisplay_cons) {
2161 		sc = wsdisplay_console_device;
2162 		if ((scr = sc->sc_scr[0]) == NULL)
2163 			return;
2164 		if (sc->sc_accessops->enter_ddb) {
2165 			(*sc->sc_accessops->enter_ddb)(sc->sc_accesscookie,
2166 			    scr->scr_dconf->emulcookie);
2167 		} else {
2168 			(*sc->sc_accessops->show_screen)(sc->sc_accesscookie,
2169 			    scr->scr_dconf->emulcookie, 0, NULL, NULL);
2170 		}
2171 	}
2172 }
2173 
2174 /*
2175  * Deal with the xserver doing driver in userland and thus screwing up suspend
2176  * and resume by switching away from it at suspend/resume time.
2177  *
2178  * these functions must be called from the MD suspend callback, since we may
2179  * need to sleep if we have a user (probably an X server) on a vt. therefore
2180  * this can't be a config_suspend() hook.
2181  */
2182 void
2183 wsdisplay_suspend(void)
2184 {
2185 	int	i;
2186 
2187 	for (i = 0; i < wsdisplay_cd.cd_ndevs; i++)
2188 		if (wsdisplay_cd.cd_devs[i] != NULL)
2189 			wsdisplay_suspend_device(wsdisplay_cd.cd_devs[i]);
2190 }
2191 
2192 void
2193 wsdisplay_suspend_device(struct device *dev)
2194 {
2195 	struct wsdisplay_softc	*sc = (struct wsdisplay_softc *)dev;
2196 	struct wsscreen		*scr;
2197 	int			 active, idx, ret = 0, s;
2198 
2199 	if ((active = wsdisplay_getactivescreen(sc)) == WSDISPLAY_NULLSCREEN)
2200 		return;
2201 
2202 	scr = sc->sc_scr[active];
2203 	/*
2204 	 * We want to switch out of graphics mode for the suspend, but
2205 	 * only if we're in WSDISPLAY_MODE_MAPPED.
2206 	 */
2207 retry:
2208 	idx = WSDISPLAY_MAXSCREEN;
2209 	if (scr->scr_flags & SCR_GRAPHICS &&
2210 	    (scr->scr_flags & SCR_DUMBFB) == 0) {
2211 		for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++) {
2212 			if (sc->sc_scr[idx] == NULL || sc->sc_scr[idx] == scr)
2213 				continue;
2214 
2215 			if ((sc->sc_scr[idx]->scr_flags & SCR_GRAPHICS) == 0)
2216 				break;
2217 		}
2218 	}
2219 
2220 	/* if we don't have anything to switch to, we can't do anything */
2221 	if (idx == WSDISPLAY_MAXSCREEN)
2222 		return;
2223 
2224 	/*
2225 	 * we do a lot of magic here because we need to know that the
2226 	 * switch has completed before we return
2227 	 */
2228 	ret = wsdisplay_switch((struct device *)sc, idx, 1);
2229 	if (ret == EBUSY) {
2230 		/* XXX sleep on what's going on */
2231 		goto retry;
2232 	} else if (ret)
2233 		return;
2234 
2235 	s = spltty();
2236 	sc->sc_resumescreen = active; /* block other vt switches until resume */
2237 	splx(s);
2238 	/*
2239 	 * This will either return ENXIO (invalid (shouldn't happen) or
2240 	 * wsdisplay disappeared (problem solved)), or EINTR/ERESTART.
2241 	 * Not much we can do about the latter since we can't return to
2242 	 * userland.
2243 	 */
2244 	(void)wsscreen_switchwait(sc, idx);
2245 }
2246 
2247 void
2248 wsdisplay_resume(void)
2249 {
2250 	int	i;
2251 
2252 	for (i = 0; i < wsdisplay_cd.cd_ndevs; i++)
2253 		if (wsdisplay_cd.cd_devs[i] != NULL)
2254 			wsdisplay_resume_device(wsdisplay_cd.cd_devs[i]);
2255 }
2256 
2257 void
2258 wsdisplay_resume_device(struct device *dev)
2259 {
2260 	struct wsdisplay_softc	*sc = (struct wsdisplay_softc *)dev;
2261 	int			 idx, s;
2262 
2263 	if (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN) {
2264 		s = spltty();
2265 		idx = sc->sc_resumescreen;
2266 		sc->sc_resumescreen = WSDISPLAY_NULLSCREEN;
2267 		wakeup(&sc->sc_resumescreen);
2268 		splx(s);
2269 		(void)wsdisplay_switch((struct device *)sc, idx, 1);
2270 	}
2271 }
2272 
2273 #ifdef HAVE_SCROLLBACK_SUPPORT
2274 void
2275 wsscrollback(void *arg, int op)
2276 {
2277 	struct wsdisplay_softc *sc = arg;
2278 	int lines;
2279 
2280 	if (sc->sc_focus == NULL)
2281 		return;
2282 
2283 	if (op == WSDISPLAY_SCROLL_RESET)
2284 		lines = 0;
2285 	else {
2286 		lines = sc->sc_focus->scr_dconf->scrdata->nrows - 1;
2287 		if (op == WSDISPLAY_SCROLL_BACKWARD)
2288 			lines = -lines;
2289 	}
2290 
2291 	if (sc->sc_accessops->scrollback) {
2292 		(*sc->sc_accessops->scrollback)(sc->sc_accesscookie,
2293 		    sc->sc_focus->scr_dconf->emulcookie, lines);
2294 	}
2295 }
2296 #endif
2297 
2298 #ifdef HAVE_BURNER_SUPPORT
2299 /*
2300  * Update screen burner behaviour after either a screen focus change or
2301  * a screen mode change.
2302  * This is needed to allow X11 to manage screen blanking without any
2303  * interference from the kernel.
2304  */
2305 void
2306 wsdisplay_burner_setup(struct wsdisplay_softc *sc, struct wsscreen *scr)
2307 {
2308 	if (scr->scr_flags & SCR_GRAPHICS) {
2309 		/* enable video _immediately_ if it needs to be... */
2310 		if (sc->sc_burnman)
2311 			wsdisplay_burner(sc);
2312 		/* ...and disable the burner while X is running */
2313 		if (sc->sc_burnout) {
2314 			timeout_del(&sc->sc_burner);
2315 			sc->sc_burnout = 0;
2316 		}
2317 	} else {
2318 		/* reenable the burner after exiting from X */
2319 		if (!sc->sc_burnman) {
2320 			sc->sc_burnout = sc->sc_burnoutintvl;
2321 			wsdisplay_burn(sc, sc->sc_burnflags);
2322 		}
2323 	}
2324 }
2325 
2326 void
2327 wsdisplay_burn(void *v, u_int flags)
2328 {
2329 	struct wsdisplay_softc *sc = v;
2330 
2331 	if ((flags & sc->sc_burnflags & (WSDISPLAY_BURN_OUTPUT |
2332 	    WSDISPLAY_BURN_KBD | WSDISPLAY_BURN_MOUSE)) &&
2333 	    sc->sc_accessops->burn_screen) {
2334 		if (sc->sc_burnout)
2335 			timeout_add(&sc->sc_burner, sc->sc_burnout);
2336 		if (sc->sc_burnman)
2337 			sc->sc_burnout = 0;
2338 	}
2339 }
2340 
2341 void
2342 wsdisplay_burner(void *v)
2343 {
2344 	struct wsdisplay_softc *sc = v;
2345 	int s;
2346 
2347 	if (sc->sc_accessops->burn_screen) {
2348 		(*sc->sc_accessops->burn_screen)(sc->sc_accesscookie,
2349 		    sc->sc_burnman, sc->sc_burnflags);
2350 		s = spltty();
2351 		if (sc->sc_burnman) {
2352 			sc->sc_burnout = sc->sc_burnoutintvl;
2353 			timeout_add(&sc->sc_burner, sc->sc_burnout);
2354 		} else
2355 			sc->sc_burnout = sc->sc_burninintvl;
2356 		sc->sc_burnman = !sc->sc_burnman;
2357 		splx(s);
2358 	}
2359 }
2360 #endif
2361 
2362 #ifdef HAVE_WSMOUSED_SUPPORT
2363 /*
2364  * wsmoused(8) support functions
2365  */
2366 
2367 /*
2368  * Main function, called from wsdisplay_cfg_ioctl.
2369  */
2370 int
2371 wsmoused(struct wsdisplay_softc *sc, caddr_t data, int flag, struct proc *p)
2372 {
2373 	struct wscons_event mouse_event = *(struct wscons_event *)data;
2374 
2375 	if (IS_MOTION_EVENT(mouse_event.type)) {
2376 		if (sc->sc_focus != NULL)
2377 			motion_event(sc->sc_focus, mouse_event.type,
2378 			    mouse_event.value);
2379 		return 0;
2380 	}
2381 	if (IS_BUTTON_EVENT(mouse_event.type)) {
2382 		if (sc->sc_focus != NULL) {
2383 			/* XXX tv_sec contains the number of clicks */
2384 			if (mouse_event.type ==
2385 			    WSCONS_EVENT_MOUSE_DOWN) {
2386 				button_event(sc->sc_focus,
2387 				    mouse_event.value,
2388 				    mouse_event.time.tv_sec);
2389 			} else
2390 				button_event(sc->sc_focus,
2391 				    mouse_event.value, 0);
2392 		}
2393 		return (0);
2394 	}
2395 	if (IS_CTRL_EVENT(mouse_event.type)) {
2396 		return ctrl_event(sc, mouse_event.type,
2397 		    mouse_event.value, p);
2398 	}
2399 	return -1;
2400 }
2401 
2402 /*
2403  * Mouse motion events
2404  */
2405 void
2406 motion_event(struct wsscreen *scr, u_int type, int value)
2407 {
2408 	switch (type) {
2409 	case WSCONS_EVENT_MOUSE_DELTA_X:
2410 		mouse_moverel(scr, value, 0);
2411 		break;
2412 	case WSCONS_EVENT_MOUSE_DELTA_Y:
2413 		mouse_moverel(scr, 0, -value);
2414 		break;
2415 #ifdef HAVE_SCROLLBACK_SUPPORT
2416 	case WSCONS_EVENT_MOUSE_DELTA_Z:
2417 		mouse_zaxis(scr, value);
2418 		break;
2419 #endif
2420 	default:
2421 		break;
2422 	}
2423 }
2424 
2425 /*
2426  * Button clicks events
2427  */
2428 void
2429 button_event(struct wsscreen *scr, int button, int clicks)
2430 {
2431 	switch (button) {
2432 	case MOUSE_COPY_BUTTON:
2433 		switch (clicks % 4) {
2434 		case 0: /* button is up */
2435 			mouse_copy_end(scr);
2436 			mouse_copy_selection(scr);
2437 			break;
2438 		case 1: /* single click */
2439 			mouse_copy_start(scr);
2440 			mouse_copy_selection(scr);
2441 			break;
2442 		case 2: /* double click */
2443 			mouse_copy_word(scr);
2444 			mouse_copy_selection(scr);
2445 			break;
2446 		case 3: /* triple click */
2447 			mouse_copy_line(scr);
2448 			mouse_copy_selection(scr);
2449 			break;
2450 		}
2451 		break;
2452 	case MOUSE_PASTE_BUTTON:
2453 		if (clicks != 0)
2454 			mouse_paste(scr);
2455 		break;
2456 	case MOUSE_EXTEND_BUTTON:
2457 		if (clicks != 0)
2458 			mouse_copy_extend_after(scr);
2459 		break;
2460 	default:
2461 		break;
2462 	}
2463 }
2464 
2465 /*
2466  * Control events
2467  */
2468 int
2469 ctrl_event(struct wsdisplay_softc *sc, u_int type, int value, struct proc *p)
2470 {
2471 	struct wsscreen *scr;
2472 	int i;
2473 
2474 	switch (type) {
2475 	case WSCONS_EVENT_WSMOUSED_OFF:
2476 		CLR(sc->sc_flags, SC_PASTE_AVAIL);
2477 		return (0);
2478 	case WSCONS_EVENT_WSMOUSED_ON:
2479 		if (!sc->sc_accessops->getchar)
2480 			/* no wsmoused(8) support in the display driver */
2481 			return (1);
2482 		allocate_copybuffer(sc);
2483 		CLR(sc->sc_flags, SC_PASTE_AVAIL);
2484 
2485 		for (i = 0 ; i < WSDISPLAY_DEFAULTSCREENS ; i++)
2486 			if ((scr = sc->sc_scr[i]) != NULL) {
2487 				scr->mouse =
2488 				    (WS_NCOLS(scr) * WS_NROWS(scr)) / 2;
2489 				scr->cursor = scr->mouse;
2490 				scr->cpy_start = 0;
2491 				scr->cpy_end = 0;
2492 				scr->orig_start = 0;
2493 				scr->orig_end = 0;
2494 				scr->mouse_flags = 0;
2495 			}
2496 		return (0);
2497 	default:	/* can't happen, really */
2498 		return 0;
2499 	}
2500 }
2501 
2502 void
2503 mouse_moverel(struct wsscreen *scr, int dx, int dy)
2504 {
2505 	struct wsscreen_internal *dconf = scr->scr_dconf;
2506 	u_int old_mouse = scr->mouse;
2507 	int mouse_col = scr->mouse % N_COLS(dconf);
2508 	int mouse_row = scr->mouse / N_COLS(dconf);
2509 
2510 	/* update position */
2511 	if (mouse_col + dx >= MAXCOL(dconf))
2512 		mouse_col = MAXCOL(dconf);
2513 	else {
2514 		if (mouse_col + dx <= 0)
2515 			mouse_col = 0;
2516 		else
2517 			mouse_col += dx;
2518 	}
2519 	if (mouse_row + dy >= MAXROW(dconf))
2520 		mouse_row = MAXROW(dconf);
2521 	else {
2522 		if (mouse_row + dy <= 0)
2523 			mouse_row = 0;
2524 		else
2525 			mouse_row += dy;
2526 	}
2527 	scr->mouse = mouse_row * N_COLS(dconf) + mouse_col;
2528 
2529 	/* if we have moved */
2530 	if (old_mouse != scr->mouse) {
2531 		/* XXX unblank screen if display.ms_act */
2532 		if (ISSET(scr->mouse_flags, SEL_IN_PROGRESS)) {
2533 			/* selection in progress */
2534 			mouse_copy_extend(scr);
2535 		} else {
2536 			inverse_char(scr, scr->mouse);
2537 			if (ISSET(scr->mouse_flags, MOUSE_VISIBLE))
2538 				inverse_char(scr, old_mouse);
2539 			else
2540 				SET(scr->mouse_flags, MOUSE_VISIBLE);
2541 		}
2542 	}
2543 }
2544 
2545 void
2546 inverse_char(struct wsscreen *scr, u_int pos)
2547 {
2548 	struct wsscreen_internal *dconf = scr->scr_dconf;
2549 	struct wsdisplay_charcell cell;
2550 	int fg, bg, ul;
2551 	int flags;
2552 	int tmp;
2553 	long attr;
2554 
2555 	GETCHAR(scr, pos, &cell);
2556 
2557 	(*dconf->emulops->unpack_attr)(dconf->emulcookie, cell.attr, &fg,
2558 	    &bg, &ul);
2559 
2560 	/*
2561 	 * Display the mouse cursor as a color inverted cell whenever
2562 	 * possible. If this is not possible, ask for the video reverse
2563 	 * attribute.
2564 	 */
2565 	flags = 0;
2566 	if (dconf->scrdata->capabilities & WSSCREEN_WSCOLORS) {
2567 		flags |= WSATTR_WSCOLORS;
2568 		tmp = fg;
2569 		fg = bg;
2570 		bg = tmp;
2571 	} else if (dconf->scrdata->capabilities & WSSCREEN_REVERSE) {
2572 		flags |= WSATTR_REVERSE;
2573 	}
2574 	if ((*dconf->emulops->alloc_attr)(dconf->emulcookie, fg, bg, flags |
2575 	    (ul ? WSATTR_UNDERLINE : 0), &attr) == 0) {
2576 		cell.attr = attr;
2577 		PUTCHAR(dconf, pos, cell.uc, cell.attr);
2578 	}
2579 }
2580 
2581 void
2582 inverse_region(struct wsscreen *scr, u_int start, u_int end)
2583 {
2584 	struct wsscreen_internal *dconf = scr->scr_dconf;
2585 	u_int current_pos;
2586 	u_int abs_end;
2587 
2588 	/* sanity check, useful because 'end' can be (u_int)-1 */
2589 	abs_end = N_COLS(dconf) * N_ROWS(dconf);
2590 	if (end > abs_end)
2591 		return;
2592 	current_pos = start;
2593 	while (current_pos <= end)
2594 		inverse_char(scr, current_pos++);
2595 }
2596 
2597 /*
2598  * Return the number of contiguous blank characters between the right margin
2599  * if border == 1 or between the next non-blank character and the current mouse
2600  * cursor if border == 0
2601  */
2602 u_int
2603 skip_spc_right(struct wsscreen *scr, int border)
2604 {
2605 	struct wsscreen_internal *dconf = scr->scr_dconf;
2606 	struct wsdisplay_charcell cell;
2607 	u_int current = scr->cpy_end;
2608 	u_int mouse_col = scr->cpy_end % N_COLS(dconf);
2609 	u_int limit = current + (N_COLS(dconf) - mouse_col - 1);
2610 	u_int res = 0;
2611 
2612 	while (GETCHAR(scr, current, &cell) == 0 && cell.uc == ' ' &&
2613 	    current <= limit) {
2614 		current++;
2615 		res++;
2616 	}
2617 	if (border == BORDER) {
2618 		if (current > limit)
2619 			return (res - 1);
2620 		else
2621 			return (0);
2622 	} else {
2623 		if (res != 0)
2624 			return (res - 1);
2625 		else
2626 			return (res);
2627 	}
2628 }
2629 
2630 /*
2631  * Return the number of contiguous blank characters between the first of the
2632  * contiguous blank characters and the current mouse cursor
2633  */
2634 u_int
2635 skip_spc_left(struct wsscreen *scr)
2636 {
2637 	struct wsscreen_internal *dconf = scr->scr_dconf;
2638 	struct wsdisplay_charcell cell;
2639 	u_int current = scr->cpy_start;
2640 	u_int mouse_col = scr->mouse % N_COLS(dconf);
2641 	u_int limit = current - mouse_col;
2642 	u_int res = 0;
2643 
2644 	while (GETCHAR(scr, current, &cell) == 0 && cell.uc == ' ' &&
2645 	    current >= limit) {
2646 		current--;
2647 		res++;
2648 	}
2649 	if (res != 0)
2650 		res--;
2651 	return (res);
2652 }
2653 
2654 /*
2655  * Class of characters
2656  * Stolen from xterm sources of the Xfree project (see cvs tag below)
2657  * $TOG: button.c /main/76 1997/07/30 16:56:19 kaleb $
2658  */
2659 static const int charClass[256] = {
2660 /* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
2661     32,   1,   1,   1,   1,   1,   1,   1,
2662 /*  BS   HT   NL   VT   NP   CR   SO   SI */
2663      1,  32,   1,   1,   1,   1,   1,   1,
2664 /* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
2665      1,   1,   1,   1,   1,   1,   1,   1,
2666 /* CAN   EM  SUB  ESC   FS   GS   RS   US */
2667      1,   1,   1,   1,   1,   1,   1,   1,
2668 /*  SP    !    "    #    $    %    &    ' */
2669     32,  33,  34,  35,  36,  37,  38,  39,
2670 /*   (    )    *    +    ,    -    .    / */
2671     40,  41,  42,  43,  44,  45,  46,  47,
2672 /*   0    1    2    3    4    5    6    7 */
2673     48,  48,  48,  48,  48,  48,  48,  48,
2674 /*   8    9    :    ;    <    =    >    ? */
2675     48,  48,  58,  59,  60,  61,  62,  63,
2676 /*   @    A    B    C    D    E    F    G */
2677     64,  48,  48,  48,  48,  48,  48,  48,
2678 /*   H    I    J    K    L    M    N    O */
2679     48,  48,  48,  48,  48,  48,  48,  48,
2680 /*   P    Q    R    S    T    U    V    W */
2681     48,  48,  48,  48,  48,  48,  48,  48,
2682 /*   X    Y    Z    [    \    ]    ^    _ */
2683     48,  48,  48,  91,  92,  93,  94,  48,
2684 /*   `    a    b    c    d    e    f    g */
2685     96,  48,  48,  48,  48,  48,  48,  48,
2686 /*   h    i    j    k    l    m    n    o */
2687     48,  48,  48,  48,  48,  48,  48,  48,
2688 /*   p    q    r    s    t    u    v    w */
2689     48,  48,  48,  48,  48,  48,  48,  48,
2690 /*   x    y    z    {    |    }    ~  DEL */
2691     48,  48,  48, 123, 124, 125, 126,   1,
2692 /* x80  x81  x82  x83  IND  NEL  SSA  ESA */
2693      1,   1,   1,   1,   1,   1,   1,   1,
2694 /* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
2695      1,   1,   1,   1,   1,   1,   1,   1,
2696 /* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
2697      1,   1,   1,   1,   1,   1,   1,   1,
2698 /* x98  x99  x9A  CSI   ST  OSC   PM  APC */
2699      1,   1,   1,   1,   1,   1,   1,   1,
2700 /*   -    i   c/    L   ox   Y-    |   So */
2701    160, 161, 162, 163, 164, 165, 166, 167,
2702 /*  ..   c0   ip   <<    _        R0    - */
2703    168, 169, 170, 171, 172, 173, 174, 175,
2704 /*   o   +-    2    3    '    u   q|    . */
2705    176, 177, 178, 179, 180, 181, 182, 183,
2706 /*   ,    1    2   >>  1/4  1/2  3/4    ? */
2707    184, 185, 186, 187, 188, 189, 190, 191,
2708 /*  A`   A'   A^   A~   A:   Ao   AE   C, */
2709     48,  48,  48,  48,  48,  48,  48,  48,
2710 /*  E`   E'   E^   E:   I`   I'   I^   I: */
2711     48,  48,  48,  48,  48,  48,  48,  48,
2712 /*  D-   N~   O`   O'   O^   O~   O:    X */
2713     48,  48,  48,  48,  48,  48,  48, 216,
2714 /*  O/   U`   U'   U^   U:   Y'    P    B */
2715     48,  48,  48,  48,  48,  48,  48,  48,
2716 /*  a`   a'   a^   a~   a:   ao   ae   c, */
2717     48,  48,  48,  48,  48,  48,  48,  48,
2718 /*  e`   e'   e^   e:    i`  i'   i^   i: */
2719     48,  48,  48,  48,  48,  48,  48,  48,
2720 /*   d   n~   o`   o'   o^   o~   o:   -: */
2721     48,  48,  48,  48,  48,  48,  48,  248,
2722 /*  o/   u`   u'   u^   u:   y'    P   y: */
2723     48,  48,  48,  48,  48,  48,  48,  48
2724 };
2725 
2726 /*
2727  * Find the first blank beginning after the current cursor position
2728  */
2729 u_int
2730 skip_char_right(struct wsscreen *scr, u_int offset)
2731 {
2732 	struct wsscreen_internal *dconf = scr->scr_dconf;
2733 	struct wsdisplay_charcell cell;
2734 	u_int current = offset;
2735 	u_int limit = current +
2736 	    (N_COLS(dconf) - (scr->mouse % N_COLS(dconf)) - 1);
2737 	u_int class;
2738 	u_int res = 0;
2739 
2740 	GETCHAR(scr, current, &cell);
2741 	class = charClass[cell.uc & 0xff];
2742 	while (GETCHAR(scr, current, &cell) == 0 &&
2743 	    charClass[cell.uc & 0xff] == class && current <= limit) {
2744 		current++;
2745 		res++;
2746 	}
2747 	if (res != 0)
2748 		res--;
2749 	return (res);
2750 }
2751 
2752 /*
2753  * Find the first non-blank character before the cursor position
2754  */
2755 u_int
2756 skip_char_left(struct wsscreen *scr, u_int offset)
2757 {
2758 	struct wsscreen_internal *dconf = scr->scr_dconf;
2759 	struct wsdisplay_charcell cell;
2760 	u_int current = offset;
2761 	u_int limit = current - (scr->mouse % N_COLS(dconf));
2762 	u_int class;
2763 	u_int res = 0;
2764 
2765 	GETCHAR(scr, current, &cell);
2766 	class = charClass[cell.uc & 0xff];
2767 	while (GETCHAR(scr, current, &cell) == 0 &&
2768 	    charClass[cell.uc & 0xff] == class && current >= limit) {
2769 		current--;
2770 		res++;
2771 	}
2772 	if (res != 0)
2773 		res--;
2774 	return (res);
2775 }
2776 
2777 /*
2778  * Compare character classes
2779  */
2780 u_int
2781 class_cmp(struct wsscreen *scr, u_int first, u_int second)
2782 {
2783 	struct wsdisplay_charcell cell;
2784 	u_int first_class;
2785 	u_int second_class;
2786 
2787 	if (GETCHAR(scr, first, &cell) != 0)
2788 		return (1);
2789 	first_class = charClass[cell.uc & 0xff];
2790 	if (GETCHAR(scr, second, &cell) != 0)
2791 		return (1);
2792 	second_class = charClass[cell.uc & 0xff];
2793 
2794 	if (first_class != second_class)
2795 		return (1);
2796 	else
2797 		return (0);
2798 }
2799 
2800 /*
2801  * Beginning of a copy operation
2802  */
2803 void
2804 mouse_copy_start(struct wsscreen *scr)
2805 {
2806 	u_int right;
2807 
2808 	/* if no selection, then that's the first one */
2809 	SET(scr->sc->sc_flags, SC_PASTE_AVAIL);
2810 
2811 	/* remove the previous selection */
2812 	if (ISSET(scr->mouse_flags, SEL_EXISTS))
2813 		remove_selection(scr);
2814 
2815 	/* initial show of the cursor */
2816 	if (!ISSET(scr->mouse_flags, MOUSE_VISIBLE))
2817 		inverse_char(scr, scr->mouse);
2818 
2819 	scr->cpy_start = scr->cpy_end = scr->mouse;
2820 	scr->orig_start = scr->cpy_start;
2821 	scr->orig_end = scr->cpy_end;
2822 	scr->cursor = scr->cpy_end + 1; /* init value */
2823 
2824 	/* useful later, in mouse_copy_extend */
2825 	right = skip_spc_right(scr, BORDER);
2826 	if (right)
2827 		SET(scr->mouse_flags, BLANK_TO_EOL);
2828 
2829 	SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_CHAR);
2830 	CLR(scr->mouse_flags, SEL_BY_WORD | SEL_BY_LINE);
2831 	CLR(scr->mouse_flags, MOUSE_VISIBLE); /* cursor hidden in selection */
2832 }
2833 
2834 /*
2835  * Copy of the word under the cursor
2836  */
2837 void
2838 mouse_copy_word(struct wsscreen *scr)
2839 {
2840 	struct wsdisplay_charcell cell;
2841 	u_int right;
2842 	u_int left;
2843 
2844 	if (ISSET(scr->mouse_flags, SEL_EXISTS))
2845 		remove_selection(scr);
2846 
2847 	if (ISSET(scr->mouse_flags, MOUSE_VISIBLE))
2848 		inverse_char(scr, scr->mouse);
2849 
2850 	scr->cpy_start = scr->cpy_end = scr->mouse;
2851 
2852 	if (GETCHAR(scr, scr->mouse, &cell) == 0 &&
2853 	    IS_ALPHANUM(cell.uc)) {
2854 		right = skip_char_right(scr, scr->cpy_end);
2855 		left = skip_char_left(scr, scr->cpy_start);
2856 	} else {
2857 		right = skip_spc_right(scr, NO_BORDER);
2858 		left = skip_spc_left(scr);
2859 	}
2860 
2861 	scr->cpy_start -= left;
2862 	scr->cpy_end += right;
2863 	scr->orig_start = scr->cpy_start;
2864 	scr->orig_end = scr->cpy_end;
2865 	scr->cursor = scr->cpy_end + 1; /* init value, never happen */
2866 	inverse_region(scr, scr->cpy_start, scr->cpy_end);
2867 
2868 	SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_WORD);
2869 	CLR(scr->mouse_flags, SEL_BY_CHAR | SEL_BY_LINE);
2870 	/* mouse cursor hidden in the selection */
2871 	CLR(scr->mouse_flags, BLANK_TO_EOL | MOUSE_VISIBLE);
2872 }
2873 
2874 /*
2875  * Copy of the current line
2876  */
2877 void
2878 mouse_copy_line(struct wsscreen *scr)
2879 {
2880 	struct wsscreen_internal *dconf = scr->scr_dconf;
2881 	u_int row = scr->mouse / N_COLS(dconf);
2882 
2883 	if (ISSET(scr->mouse_flags, SEL_EXISTS))
2884 		remove_selection(scr);
2885 
2886 	if (ISSET(scr->mouse_flags, MOUSE_VISIBLE))
2887 		inverse_char(scr, scr->mouse);
2888 
2889 	scr->cpy_start = row * N_COLS(dconf);
2890 	scr->cpy_end = scr->cpy_start + (N_COLS(dconf) - 1);
2891 	scr->orig_start = scr->cpy_start;
2892 	scr->orig_end = scr->cpy_end;
2893 	scr->cursor = scr->cpy_end + 1;
2894 	inverse_region(scr, scr->cpy_start, scr->cpy_end);
2895 
2896 	SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_LINE);
2897 	CLR(scr->mouse_flags, SEL_BY_CHAR | SEL_BY_WORD);
2898 	/* mouse cursor hidden in the selection */
2899 	CLR(scr->mouse_flags, BLANK_TO_EOL | MOUSE_VISIBLE);
2900 }
2901 
2902 /*
2903  * End of a copy operation
2904  */
2905 void
2906 mouse_copy_end(struct wsscreen *scr)
2907 {
2908 	CLR(scr->mouse_flags, SEL_IN_PROGRESS);
2909 	if (ISSET(scr->mouse_flags, SEL_BY_WORD) ||
2910 	    ISSET(scr->mouse_flags, SEL_BY_LINE)) {
2911 		if (scr->cursor != scr->cpy_end + 1)
2912 			inverse_char(scr, scr->cursor);
2913 		scr->cursor = scr->cpy_end + 1;
2914 	}
2915 }
2916 
2917 
2918 /*
2919  * Generic selection extend function
2920  */
2921 void
2922 mouse_copy_extend(struct wsscreen *scr)
2923 {
2924 	if (ISSET(scr->mouse_flags, SEL_BY_CHAR))
2925 		mouse_copy_extend_char(scr);
2926 	if (ISSET(scr->mouse_flags, SEL_BY_WORD))
2927 		mouse_copy_extend_word(scr);
2928 	if (ISSET(scr->mouse_flags, SEL_BY_LINE))
2929 		mouse_copy_extend_line(scr);
2930 }
2931 
2932 /*
2933  * Extend a selected region, character by character
2934  */
2935 void
2936 mouse_copy_extend_char(struct wsscreen *scr)
2937 {
2938 	u_int right;
2939 
2940 	if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
2941 		if (ISSET(scr->mouse_flags, BLANK_TO_EOL)) {
2942 			/*
2943 			 * First extension of selection. We handle special
2944 			 * cases of blank characters to eol
2945 			 */
2946 
2947 			right = skip_spc_right(scr, BORDER);
2948 			if (scr->mouse > scr->orig_start) {
2949 				/* the selection goes to the lower part of
2950 				   the screen */
2951 
2952 				/* remove the previous cursor, start of
2953 				   selection is now next line */
2954 				inverse_char(scr, scr->cpy_start);
2955 				scr->cpy_start += (right + 1);
2956 				scr->cpy_end = scr->cpy_start;
2957 				scr->orig_start = scr->cpy_start;
2958 				/* simulate the initial mark */
2959 				inverse_char(scr, scr->cpy_start);
2960 			} else {
2961 				/* the selection goes to the upper part
2962 				   of the screen */
2963 				/* remove the previous cursor, start of
2964 				   selection is now at the eol */
2965 				inverse_char(scr, scr->cpy_start);
2966 				scr->orig_start += (right + 1);
2967 				scr->cpy_start = scr->orig_start - 1;
2968 				scr->cpy_end = scr->orig_start - 1;
2969 				/* simulate the initial mark */
2970 				inverse_char(scr, scr->cpy_start);
2971 			}
2972 			CLR(scr->mouse_flags, BLANK_TO_EOL);
2973 		}
2974 
2975 		if (scr->mouse < scr->orig_start &&
2976 		    scr->cpy_end >= scr->orig_start) {
2977 			/* we go to the upper part of the screen */
2978 
2979 			/* reverse the old selection region */
2980 			remove_selection(scr);
2981 			scr->cpy_end = scr->orig_start - 1;
2982 			scr->cpy_start = scr->orig_start;
2983 		}
2984 		if (scr->cpy_start < scr->orig_start &&
2985 		    scr->mouse >= scr->orig_start) {
2986 			/* we go to the lower part of the screen */
2987 
2988 			/* reverse the old selection region */
2989 
2990 			remove_selection(scr);
2991 			scr->cpy_start = scr->orig_start;
2992 			scr->cpy_end = scr->orig_start - 1;
2993 		}
2994 		/* restore flags cleared in remove_selection() */
2995 		SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS);
2996 	}
2997 
2998 	if (scr->mouse >= scr->orig_start) {
2999 		/* lower part of the screen */
3000 		if (scr->mouse > scr->cpy_end) {
3001 			/* extending selection */
3002 			inverse_region(scr, scr->cpy_end + 1, scr->mouse);
3003 		} else {
3004 			/* reducing selection */
3005 			inverse_region(scr, scr->mouse + 1, scr->cpy_end);
3006 		}
3007 		scr->cpy_end = scr->mouse;
3008 	} else {
3009 		/* upper part of the screen */
3010 		if (scr->mouse < scr->cpy_start) {
3011 			/* extending selection */
3012 			inverse_region(scr, scr->mouse, scr->cpy_start - 1);
3013 		} else {
3014 			/* reducing selection */
3015 			inverse_region(scr, scr->cpy_start, scr->mouse - 1);
3016 		}
3017 		scr->cpy_start = scr->mouse;
3018 	}
3019 }
3020 
3021 /*
3022  * Extend a selected region, word by word
3023  */
3024 void
3025 mouse_copy_extend_word(struct wsscreen *scr)
3026 {
3027 	u_int old_cpy_end;
3028 	u_int old_cpy_start;
3029 
3030 	if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
3031 		/* remove cursor in selection (black one) */
3032 		if (scr->cursor != scr->cpy_end + 1)
3033 			inverse_char(scr, scr->cursor);
3034 
3035 		/* now, switch between lower and upper part of the screen */
3036 		if (scr->mouse < scr->orig_start &&
3037 		    scr->cpy_end >= scr->orig_start) {
3038 			/* going to the upper part of the screen */
3039 			inverse_region(scr, scr->orig_end + 1, scr->cpy_end);
3040 			scr->cpy_end = scr->orig_end;
3041 		}
3042 
3043 		if (scr->mouse > scr->orig_end &&
3044 		    scr->cpy_start <= scr->orig_start) {
3045 			/* going to the lower part of the screen */
3046 			inverse_region(scr, scr->cpy_start,
3047 			    scr->orig_start - 1);
3048 			scr->cpy_start = scr->orig_start;
3049 		}
3050 	}
3051 
3052 	if (scr->mouse >= scr->orig_start) {
3053 		/* lower part of the screen */
3054 		if (scr->mouse > scr->cpy_end) {
3055 			/* extending selection */
3056 			old_cpy_end = scr->cpy_end;
3057 			scr->cpy_end = scr->mouse +
3058 			    skip_char_right(scr, scr->mouse);
3059 			inverse_region(scr, old_cpy_end + 1, scr->cpy_end);
3060 		} else {
3061 			if (class_cmp(scr, scr->mouse, scr->mouse + 1)) {
3062 				/* reducing selection (remove last word) */
3063 				old_cpy_end = scr->cpy_end;
3064 				scr->cpy_end = scr->mouse;
3065 				inverse_region(scr, scr->cpy_end + 1,
3066 				    old_cpy_end);
3067 			} else {
3068 				old_cpy_end = scr->cpy_end;
3069 				scr->cpy_end = scr->mouse +
3070 				    skip_char_right(scr, scr->mouse);
3071 				if (scr->cpy_end != old_cpy_end) {
3072 					/* reducing selection, from the end of
3073 					 * next word */
3074 					inverse_region(scr, scr->cpy_end + 1,
3075 					    old_cpy_end);
3076 				}
3077 			}
3078 		}
3079 	} else {
3080 		/* upper part of the screen */
3081 		if (scr->mouse < scr->cpy_start) {
3082 			/* extending selection */
3083 			old_cpy_start = scr->cpy_start;
3084 			scr->cpy_start = scr->mouse -
3085 			    skip_char_left(scr, scr->mouse);
3086 			inverse_region(scr, scr->cpy_start, old_cpy_start - 1);
3087 		} else {
3088 			if (class_cmp(scr, scr->mouse - 1, scr->mouse)) {
3089 				/* reducing selection (remove last word) */
3090 				old_cpy_start = scr->cpy_start;
3091 				scr->cpy_start = scr->mouse;
3092 				inverse_region(scr, old_cpy_start,
3093 				    scr->cpy_start - 1);
3094 			} else {
3095 				old_cpy_start = scr->cpy_start;
3096 				scr->cpy_start = scr->mouse -
3097 				    skip_char_left(scr, scr->mouse);
3098 				if (scr->cpy_start != old_cpy_start) {
3099 					inverse_region(scr, old_cpy_start,
3100 					    scr->cpy_start - 1);
3101 				}
3102 			}
3103 		}
3104 	}
3105 
3106 	if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
3107 		/* display new cursor */
3108 		scr->cursor = scr->mouse;
3109 		inverse_char(scr, scr->cursor);
3110 	}
3111 }
3112 
3113 /*
3114  * Extend a selected region, line by line
3115  */
3116 void
3117 mouse_copy_extend_line(struct wsscreen *scr)
3118 {
3119 	struct wsscreen_internal *dconf = scr->scr_dconf;
3120 	u_int old_row;
3121 	u_int new_row;
3122 	u_int old_cpy_start;
3123 	u_int old_cpy_end;
3124 
3125 	if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
3126 		/* remove cursor in selection (black one) */
3127 		if (scr->cursor != scr->cpy_end + 1)
3128 			inverse_char(scr, scr->cursor);
3129 
3130 		/* now, switch between lower and upper part of the screen */
3131 		if (scr->mouse < scr->orig_start &&
3132 		    scr->cpy_end >= scr->orig_start) {
3133 			/* going to the upper part of the screen */
3134 			inverse_region(scr, scr->orig_end + 1, scr->cpy_end);
3135 			scr->cpy_end = scr->orig_end;
3136 		}
3137 
3138 		if (scr->mouse > scr->orig_end &&
3139 		    scr->cpy_start <= scr->orig_start) {
3140 			/* going to the lower part of the screen */
3141 			inverse_region(scr, scr->cpy_start,
3142 			    scr->orig_start - 1);
3143 			scr->cpy_start = scr->orig_start;
3144 		}
3145 	}
3146 
3147 	if (scr->mouse >= scr->orig_start) {
3148 		/* lower part of the screen */
3149 		if (scr->cursor == scr->cpy_end + 1)
3150 			scr->cursor = scr->cpy_end;
3151 		old_row = scr->cursor / N_COLS(dconf);
3152 		new_row = scr->mouse / N_COLS(dconf);
3153 		old_cpy_end = scr->cpy_end;
3154 		scr->cpy_end = new_row * N_COLS(dconf) + MAXCOL(dconf);
3155 		if (new_row > old_row)
3156 			inverse_region(scr, old_cpy_end + 1, scr->cpy_end);
3157 		else if (new_row < old_row)
3158 			inverse_region(scr, scr->cpy_end + 1, old_cpy_end);
3159 	} else {
3160 		/* upper part of the screen */
3161 		old_row = scr->cursor / N_COLS(dconf);
3162 		new_row = scr->mouse / N_COLS(dconf);
3163 		old_cpy_start = scr->cpy_start;
3164 		scr->cpy_start = new_row * N_COLS(dconf);
3165 		if (new_row < old_row)
3166 			inverse_region(scr, scr->cpy_start, old_cpy_start - 1);
3167 		else if (new_row > old_row)
3168 			inverse_region(scr, old_cpy_start, scr->cpy_start - 1);
3169 	}
3170 
3171 	if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
3172 		/* display new cursor */
3173 		scr->cursor = scr->mouse;
3174 		inverse_char(scr, scr->cursor);
3175 	}
3176 }
3177 
3178 /*
3179  * Add an extension to a selected region, word by word
3180  */
3181 void
3182 mouse_copy_extend_after(struct wsscreen *scr)
3183 {
3184 	u_int start_dist;
3185 	u_int end_dist;
3186 
3187 	if (ISSET(scr->mouse_flags, SEL_EXISTS)) {
3188 		SET(scr->mouse_flags, SEL_EXT_AFTER);
3189 		mouse_hide(scr); /* hide current cursor */
3190 
3191 		if (scr->cpy_start > scr->mouse)
3192 			start_dist = scr->cpy_start - scr->mouse;
3193 		else
3194 			start_dist = scr->mouse - scr->cpy_start;
3195 		if (scr->mouse > scr->cpy_end)
3196 			end_dist = scr->mouse - scr->cpy_end;
3197 		else
3198 			end_dist = scr->cpy_end - scr->mouse;
3199 		if (start_dist < end_dist) {
3200 			/* upper part of the screen*/
3201 			scr->orig_start = scr->mouse + 1;
3202 			/* only used in mouse_copy_extend_line() */
3203 			scr->cursor = scr->cpy_start;
3204 		} else {
3205 			/* lower part of the screen */
3206 			scr->orig_start = scr->mouse;
3207 			/* only used in mouse_copy_extend_line() */
3208 			scr->cursor = scr->cpy_end;
3209 		}
3210 		if (ISSET(scr->mouse_flags, SEL_BY_CHAR))
3211 			mouse_copy_extend_char(scr);
3212 		if (ISSET(scr->mouse_flags, SEL_BY_WORD))
3213 			mouse_copy_extend_word(scr);
3214 		if (ISSET(scr->mouse_flags, SEL_BY_LINE))
3215 			mouse_copy_extend_line(scr);
3216 		mouse_copy_selection(scr);
3217 	}
3218 }
3219 
3220 void
3221 mouse_hide(struct wsscreen *scr)
3222 {
3223 	if (ISSET(scr->mouse_flags, MOUSE_VISIBLE)) {
3224 		inverse_char(scr, scr->mouse);
3225 		CLR(scr->mouse_flags, MOUSE_VISIBLE);
3226 	}
3227 }
3228 
3229 /*
3230  * Remove a previously selected region
3231  */
3232 void
3233 remove_selection(struct wsscreen *scr)
3234 {
3235 	if (ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
3236 		/* reset the flag indicating an extension of selection */
3237 		CLR(scr->mouse_flags, SEL_EXT_AFTER);
3238 	}
3239 	inverse_region(scr, scr->cpy_start, scr->cpy_end);
3240 	CLR(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS);
3241 }
3242 
3243 /*
3244  * Put the current visual selection in the selection buffer
3245  */
3246 void
3247 mouse_copy_selection(struct wsscreen *scr)
3248 {
3249 	struct wsscreen_internal *dconf = scr->scr_dconf;
3250 	struct wsdisplay_charcell cell;
3251 	u_int current = 0;
3252 	u_int blank = current;
3253 	u_int buf_end = (N_COLS(dconf) + 1) * N_ROWS(dconf);
3254 	u_int sel_cur;
3255 	u_int sel_end;
3256 
3257 	sel_cur = scr->cpy_start;
3258 	sel_end = scr->cpy_end;
3259 
3260 	while (sel_cur <= sel_end && current < buf_end - 1) {
3261 		if (GETCHAR(scr, sel_cur, &cell) != 0)
3262 			break;
3263 		scr->sc->sc_copybuffer[current] = cell.uc;
3264 		if (!IS_SPACE(cell.uc))
3265 			blank = current + 1; /* first blank after non-blank */
3266 		current++;
3267 		if (sel_cur % N_COLS(dconf) == MAXCOL(dconf)) {
3268 			/*
3269 			 * If we are on the last column of the screen,
3270 			 * insert a carriage return.
3271 			 */
3272 			scr->sc->sc_copybuffer[blank] = '\r';
3273 			current = ++blank;
3274 		}
3275 		sel_cur++;
3276 	}
3277 
3278 	scr->sc->sc_copybuffer[current] = '\0';
3279 }
3280 
3281 /*
3282  * Paste the current selection
3283  */
3284 void
3285 mouse_paste(struct wsscreen *scr)
3286 {
3287 	char *current = scr->sc->sc_copybuffer;
3288 	struct tty *tp;
3289 	u_int len;
3290 
3291 	if (ISSET(scr->sc->sc_flags, SC_PASTE_AVAIL)) {
3292 		if (!WSSCREEN_HAS_TTY(scr))
3293 			return;
3294 
3295 		tp = scr->scr_tty;
3296 		for (len = strlen(scr->sc->sc_copybuffer); len != 0; len--)
3297 			(*linesw[tp->t_line].l_rint)(*current++, tp);
3298 	}
3299 }
3300 
3301 #ifdef HAVE_SCROLLBACK_SUPPORT
3302 /*
3303  * Handle the z axis.
3304  * The z axis (roller or wheel) is mapped by default to scrollback.
3305  */
3306 void
3307 mouse_zaxis(struct wsscreen *scr, int z)
3308 {
3309 	if (z < 0)
3310 		wsscrollback(scr->sc, WSDISPLAY_SCROLL_BACKWARD);
3311 	else
3312 		wsscrollback(scr->sc, WSDISPLAY_SCROLL_FORWARD);
3313 }
3314 #endif
3315 
3316 /*
3317  * Allocate the copy buffer. The size is:
3318  * (cols + 1) * (rows)
3319  * (+1 for '\n' at the end of lines),
3320  * where cols and rows are the maximum of column and rows of all screens.
3321  */
3322 void
3323 allocate_copybuffer(struct wsdisplay_softc *sc)
3324 {
3325 	int nscreens = sc->sc_scrdata->nscreens;
3326 	int i, s;
3327 	const struct wsscreen_descr **screens_list = sc->sc_scrdata->screens;
3328 	const struct wsscreen_descr *current;
3329 	u_int size = sc->sc_copybuffer_size;
3330 
3331 	s = spltty();
3332 	for (i = 0; i < nscreens; i++) {
3333 		current = *screens_list;
3334 		if ((current->ncols + 1) * current->nrows > size)
3335 			size = (current->ncols + 1) * current->nrows;
3336 		screens_list++;
3337 	}
3338 	if (size != sc->sc_copybuffer_size && sc->sc_copybuffer_size != 0) {
3339 		bzero(sc->sc_copybuffer, sc->sc_copybuffer_size);
3340 		free(sc->sc_copybuffer, M_DEVBUF, sc->sc_copybuffer_size);
3341 	}
3342 	if ((sc->sc_copybuffer = (char *)malloc(size, M_DEVBUF, M_NOWAIT)) ==
3343 	    NULL) {
3344 		printf("%s: couldn't allocate copy buffer\n",
3345 		    sc->sc_dv.dv_xname);
3346 		size = 0;
3347 	}
3348 	sc->sc_copybuffer_size = size;
3349 	splx(s);
3350 }
3351 
3352 /* Remove selection and cursor on current screen */
3353 void
3354 mouse_remove(struct wsscreen *scr)
3355 {
3356 	if (ISSET(scr->mouse_flags, SEL_EXISTS))
3357 		remove_selection(scr);
3358 
3359 	mouse_hide(scr);
3360 }
3361 
3362 #endif /* HAVE_WSMOUSED_SUPPORT */
3363