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