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