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