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