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