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