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