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