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