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