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