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