/* $OpenBSD: wsdisplay.c,v 1.124 2015/09/08 11:13:20 deraadt Exp $ */ /* $NetBSD: wsdisplay.c,v 1.82 2005/02/27 00:27:52 perry Exp $ */ /* * Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Christopher G. Demetriou * for the NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wsdisplay.h" #include "wskbd.h" #include "wsmux.h" #if NWSKBD > 0 #include #include #endif #include "wsmoused.h" struct wsscreen_internal { const struct wsdisplay_emulops *emulops; void *emulcookie; const struct wsscreen_descr *scrdata; const struct wsemul_ops *wsemul; void *wsemulcookie; }; struct wsscreen { struct wsscreen_internal *scr_dconf; struct tty *scr_tty; int scr_hold_screen; /* hold tty output */ int scr_flags; #define SCR_OPEN 1 /* is it open? */ #define SCR_WAITACTIVE 2 /* someone waiting on activation */ #define SCR_GRAPHICS 4 /* graphics mode, no text (emulation) output */ #define SCR_DUMBFB 8 /* in use as dumb fb (iff SCR_GRAPHICS) */ #ifdef WSDISPLAY_COMPAT_USL const struct wscons_syncops *scr_syncops; void *scr_synccookie; #endif #ifdef WSDISPLAY_COMPAT_RAWKBD int scr_rawkbd; #endif struct wsdisplay_softc *sc; #ifdef HAVE_WSMOUSED_SUPPORT /* mouse console support via wsmoused(8) */ u_int mouse; /* mouse cursor position */ u_int cursor; /* selection cursor position (if different from mouse cursor pos) */ u_int cpy_start; /* position of the copy start mark*/ u_int cpy_end; /* position of the copy end mark */ u_int orig_start; /* position of the original sel. start*/ u_int orig_end; /* position of the original sel. end */ u_int mouse_flags; /* flags, status of the mouse */ #define MOUSE_VISIBLE 0x01 /* flag, the mouse cursor is visible */ #define SEL_EXISTS 0x02 /* flag, a selection exists */ #define SEL_IN_PROGRESS 0x04 /* flag, a selection is in progress */ #define SEL_EXT_AFTER 0x08 /* flag, selection is extended after */ #define BLANK_TO_EOL 0x10 /* flag, there are only blanks characters to eol */ #define SEL_BY_CHAR 0x20 /* flag, select character by character*/ #define SEL_BY_WORD 0x40 /* flag, select word by word */ #define SEL_BY_LINE 0x80 /* flag, select line by line */ #define IS_MOUSE_VISIBLE(scr) ((scr)->mouse_flags & MOUSE_VISIBLE) #define IS_SEL_EXISTS(scr) ((scr)->mouse_flags & SEL_EXISTS) #define IS_SEL_IN_PROGRESS(scr) ((scr)->mouse_flags & SEL_IN_PROGRESS) #define IS_SEL_EXT_AFTER(scr) ((scr)->mouse_flags & SEL_EXT_AFTER) #define IS_BLANK_TO_EOL(scr) ((scr)->mouse_flags & BLANK_TO_EOL) #define IS_SEL_BY_CHAR(scr) ((scr)->mouse_flags & SEL_BY_CHAR) #define IS_SEL_BY_WORD(scr) ((scr)->mouse_flags & SEL_BY_WORD) #define IS_SEL_BY_LINE(scr) ((scr)->mouse_flags & SEL_BY_LINE) #endif /* HAVE_WSMOUSED_SUPPORT */ }; struct wsscreen *wsscreen_attach(struct wsdisplay_softc *, int, const char *, const struct wsscreen_descr *, void *, int, int, long); void wsscreen_detach(struct wsscreen *); int wsdisplay_addscreen(struct wsdisplay_softc *, int, const char *, const char *); int wsdisplay_getscreen(struct wsdisplay_softc *, struct wsdisplay_addscreendata *); void wsdisplay_resume_device(struct device *); void wsdisplay_suspend_device(struct device *); void wsdisplay_addscreen_print(struct wsdisplay_softc *, int, int); void wsdisplay_closescreen(struct wsdisplay_softc *, struct wsscreen *); int wsdisplay_delscreen(struct wsdisplay_softc *, int, int); void wsdisplay_burner_setup(struct wsdisplay_softc *, struct wsscreen *); void wsdisplay_burner(void *v); struct wsdisplay_softc { struct device sc_dv; const struct wsdisplay_accessops *sc_accessops; void *sc_accesscookie; const struct wsscreen_list *sc_scrdata; struct wsscreen *sc_scr[WSDISPLAY_MAXSCREEN]; int sc_focusidx; /* available only if sc_focus isn't null */ struct wsscreen *sc_focus; #ifdef HAVE_BURNER_SUPPORT struct timeout sc_burner; int sc_burnoutintvl; /* delay before blanking */ int sc_burninintvl; /* delay before unblanking */ int sc_burnout; /* current sc_burner delay */ int sc_burnman; /* nonzero if screen blanked */ int sc_burnflags; #endif int sc_isconsole; int sc_flags; #define SC_SWITCHPENDING 0x01 #define SC_PASTE_AVAIL 0x02 int sc_screenwanted, sc_oldscreen; /* valid with SC_SWITCHPENDING */ int sc_resumescreen; /* if set, can't switch until resume. */ #if NWSKBD > 0 struct wsevsrc *sc_input; #ifdef WSDISPLAY_COMPAT_RAWKBD int sc_rawkbd; #endif #endif /* NWSKBD > 0 */ #ifdef HAVE_WSMOUSED_SUPPORT char *sc_copybuffer; u_int sc_copybuffer_size; #endif }; extern struct cfdriver wsdisplay_cd; /* Autoconfiguration definitions. */ int wsdisplay_emul_match(struct device *, void *, void *); void wsdisplay_emul_attach(struct device *, struct device *, void *); int wsdisplay_emul_detach(struct device *, int); int wsdisplay_activate(struct device *, int); struct cfdriver wsdisplay_cd = { NULL, "wsdisplay", DV_TTY }; struct cfattach wsdisplay_emul_ca = { sizeof(struct wsdisplay_softc), wsdisplay_emul_match, wsdisplay_emul_attach, wsdisplay_emul_detach, wsdisplay_activate }; void wsdisplaystart(struct tty *); int wsdisplayparam(struct tty *, struct termios *); /* Internal macros, functions, and variables. */ #define WSDISPLAYUNIT(dev) (minor(dev) >> 8) #define WSDISPLAYSCREEN(dev) (minor(dev) & 0xff) #define ISWSDISPLAYCTL(dev) (WSDISPLAYSCREEN(dev) == 255) #define WSDISPLAYMINOR(unit, screen) (((unit) << 8) | (screen)) #define WSSCREEN_HAS_TTY(scr) ((scr)->scr_tty != NULL) void wsdisplay_common_attach(struct wsdisplay_softc *sc, int console, int mux, const struct wsscreen_list *, const struct wsdisplay_accessops *accessops, void *accesscookie, u_int defaultscreens); int wsdisplay_common_detach(struct wsdisplay_softc *, int); void wsdisplay_kbdholdscr(struct wsscreen *, int); #ifdef WSDISPLAY_COMPAT_RAWKBD int wsdisplay_update_rawkbd(struct wsdisplay_softc *, struct wsscreen *); #endif int wsdisplay_console_initted; struct wsdisplay_softc *wsdisplay_console_device; struct wsscreen_internal wsdisplay_console_conf; int wsdisplay_getc_dummy(dev_t); void wsdisplay_pollc(dev_t, int); int wsdisplay_cons_pollmode; void (*wsdisplay_cons_kbd_pollc)(dev_t, int); struct consdev wsdisplay_cons = { NULL, NULL, wsdisplay_getc_dummy, wsdisplay_cnputc, wsdisplay_pollc, NULL, NODEV, CN_LOWPRI }; #ifndef WSDISPLAY_DEFAULTSCREENS #define WSDISPLAY_DEFAULTSCREENS 1 #endif int wsdisplay_defaultscreens = WSDISPLAY_DEFAULTSCREENS; int wsdisplay_switch1(void *, int, int); int wsdisplay_switch2(void *, int, int); int wsdisplay_switch3(void *, int, int); int wsdisplay_clearonclose; struct wsscreen * wsscreen_attach(struct wsdisplay_softc *sc, int console, const char *emul, const struct wsscreen_descr *type, void *cookie, int ccol, int crow, long defattr) { struct wsscreen_internal *dconf; struct wsscreen *scr; scr = malloc(sizeof(*scr), M_DEVBUF, M_ZERO | M_NOWAIT); if (!scr) return (NULL); if (console) { dconf = &wsdisplay_console_conf; /* * Tell the emulation about the callback argument. * The other stuff is already there. */ (void)(*dconf->wsemul->attach)(1, 0, 0, 0, 0, scr, 0); } else { /* not console */ dconf = malloc(sizeof(*dconf), M_DEVBUF, M_NOWAIT); if (dconf == NULL) goto fail; dconf->emulops = type->textops; dconf->emulcookie = cookie; if (dconf->emulops == NULL || (dconf->wsemul = wsemul_pick(emul)) == NULL) goto fail; dconf->wsemulcookie = (*dconf->wsemul->attach)(0, type, cookie, ccol, crow, scr, defattr); if (dconf->wsemulcookie == NULL) goto fail; dconf->scrdata = type; } scr->scr_dconf = dconf; scr->scr_tty = ttymalloc(0); scr->sc = sc; return (scr); fail: if (dconf != NULL) free(dconf, M_DEVBUF, sizeof(*dconf)); free(scr, M_DEVBUF, sizeof(*scr)); return (NULL); } void wsscreen_detach(struct wsscreen *scr) { int ccol, crow; /* XXX */ if (WSSCREEN_HAS_TTY(scr)) { timeout_del(&scr->scr_tty->t_rstrt_to); ttyfree(scr->scr_tty); } (*scr->scr_dconf->wsemul->detach)(scr->scr_dconf->wsemulcookie, &ccol, &crow); free(scr->scr_dconf, M_DEVBUF, sizeof(*scr->scr_dconf)); free(scr, M_DEVBUF, sizeof(*scr)); } const struct wsscreen_descr * wsdisplay_screentype_pick(const struct wsscreen_list *scrdata, const char *name) { int i; const struct wsscreen_descr *scr; KASSERT(scrdata->nscreens > 0); if (name == NULL || *name == '\0') return (scrdata->screens[0]); for (i = 0; i < scrdata->nscreens; i++) { scr = scrdata->screens[i]; if (!strncmp(name, scr->name, WSSCREEN_NAME_SIZE)) return (scr); } return (0); } /* * print info about attached screen */ void wsdisplay_addscreen_print(struct wsdisplay_softc *sc, int idx, int count) { printf("%s: screen %d", sc->sc_dv.dv_xname, idx); if (count > 1) printf("-%d", idx + (count-1)); printf(" added (%s, %s emulation)\n", sc->sc_scr[idx]->scr_dconf->scrdata->name, sc->sc_scr[idx]->scr_dconf->wsemul->name); } int wsdisplay_addscreen(struct wsdisplay_softc *sc, int idx, const char *screentype, const char *emul) { const struct wsscreen_descr *scrdesc; int error; void *cookie; int ccol, crow; long defattr; struct wsscreen *scr; int s; if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN) return (EINVAL); if (sc->sc_scr[idx] != NULL) return (EBUSY); scrdesc = wsdisplay_screentype_pick(sc->sc_scrdata, screentype); if (!scrdesc) return (ENXIO); error = (*sc->sc_accessops->alloc_screen)(sc->sc_accesscookie, scrdesc, &cookie, &ccol, &crow, &defattr); if (error) return (error); scr = wsscreen_attach(sc, 0, emul, scrdesc, cookie, ccol, crow, defattr); if (scr == NULL) { (*sc->sc_accessops->free_screen)(sc->sc_accesscookie, cookie); return (ENXIO); } sc->sc_scr[idx] = scr; /* if no screen has focus yet, activate the first we get */ s = spltty(); if (!sc->sc_focus) { (*sc->sc_accessops->show_screen)(sc->sc_accesscookie, scr->scr_dconf->emulcookie, 0, 0, 0); sc->sc_focusidx = idx; sc->sc_focus = scr; } splx(s); #ifdef HAVE_WSMOUSED_SUPPORT allocate_copybuffer(sc); /* enlarge the copy buffer if necessary */ #endif return (0); } int wsdisplay_getscreen(struct wsdisplay_softc *sc, struct wsdisplay_addscreendata *sd) { struct wsscreen *scr; if (sd->idx < 0 && sc->sc_focus) sd->idx = sc->sc_focusidx; if (sd->idx < 0 || sd->idx >= WSDISPLAY_MAXSCREEN) return (EINVAL); scr = sc->sc_scr[sd->idx]; if (scr == NULL) return (ENXIO); strncpy(sd->screentype, scr->scr_dconf->scrdata->name, WSSCREEN_NAME_SIZE); strncpy(sd->emul, scr->scr_dconf->wsemul->name, WSEMUL_NAME_SIZE); return (0); } void wsdisplay_closescreen(struct wsdisplay_softc *sc, struct wsscreen *scr) { int maj, mn, idx; /* hangup */ if (WSSCREEN_HAS_TTY(scr)) { struct tty *tp = scr->scr_tty; (*linesw[tp->t_line].l_modem)(tp, 0); } /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if (cdevsw[maj].d_open == wsdisplayopen) break; /* locate the screen index */ for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++) if (scr == sc->sc_scr[idx]) break; #ifdef DIAGNOSTIC if (idx == WSDISPLAY_MAXSCREEN) panic("wsdisplay_forceclose: bad screen"); #endif /* nuke the vnodes */ mn = WSDISPLAYMINOR(sc->sc_dv.dv_unit, idx); vdevgone(maj, mn, mn, VCHR); } int wsdisplay_delscreen(struct wsdisplay_softc *sc, int idx, int flags) { struct wsscreen *scr; int s; void *cookie; if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN) return (EINVAL); if ((scr = sc->sc_scr[idx]) == NULL) return (ENXIO); if (scr->scr_dconf == &wsdisplay_console_conf || #ifdef WSDISPLAY_COMPAT_USL scr->scr_syncops || #endif ((scr->scr_flags & SCR_OPEN) && !(flags & WSDISPLAY_DELSCR_FORCE))) return (EBUSY); wsdisplay_closescreen(sc, scr); /* * delete pointers, so neither device entries * nor keyboard input can reference it anymore */ s = spltty(); if (sc->sc_focus == scr) { sc->sc_focus = NULL; #ifdef WSDISPLAY_COMPAT_RAWKBD wsdisplay_update_rawkbd(sc, 0); #endif } sc->sc_scr[idx] = NULL; splx(s); /* * Wake up processes waiting for the screen to * be activated. Sleepers must check whether * the screen still exists. */ if (scr->scr_flags & SCR_WAITACTIVE) wakeup(scr); /* save a reference to the graphics screen */ cookie = scr->scr_dconf->emulcookie; wsscreen_detach(scr); (*sc->sc_accessops->free_screen)(sc->sc_accesscookie, cookie); if ((flags & WSDISPLAY_DELSCR_QUIET) == 0) printf("%s: screen %d deleted\n", sc->sc_dv.dv_xname, idx); return (0); } /* * Autoconfiguration functions. */ int wsdisplay_emul_match(struct device *parent, void *match, void *aux) { struct cfdata *cf = match; struct wsemuldisplaydev_attach_args *ap = aux; if (cf->wsemuldisplaydevcf_console != WSEMULDISPLAYDEVCF_CONSOLE_UNK) { /* * If console-ness of device specified, either match * exactly (at high priority), or fail. */ if (cf->wsemuldisplaydevcf_console != 0 && ap->console != 0) return (10); else return (0); } /* If console-ness unspecified, it wins. */ return (1); } void wsdisplay_emul_attach(struct device *parent, struct device *self, void *aux) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)self; struct wsemuldisplaydev_attach_args *ap = aux; wsdisplay_common_attach(sc, ap->console, sc->sc_dv.dv_cfdata->wsemuldisplaydevcf_mux, ap->scrdata, ap->accessops, ap->accesscookie, ap->defaultscreens); if (ap->console && cn_tab == &wsdisplay_cons) { int maj; /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if (cdevsw[maj].d_open == wsdisplayopen) break; cn_tab->cn_dev = makedev(maj, WSDISPLAYMINOR(self->dv_unit, 0)); } } /* * Detach a display. */ int wsdisplay_emul_detach(struct device *self, int flags) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)self; return (wsdisplay_common_detach(sc, flags)); } int wsdisplay_activate(struct device *self, int act) { int ret = 0; switch (act) { case DVACT_POWERDOWN: wsdisplay_switchtoconsole(); break; } return (ret); } int wsdisplay_common_detach(struct wsdisplay_softc *sc, int flags) { int i; int rc; /* We don't support detaching the console display yet. */ if (sc->sc_isconsole) return (EBUSY); /* Delete all screens managed by this display */ for (i = 0; i < WSDISPLAY_MAXSCREEN; i++) if (sc->sc_scr[i] != NULL) { if ((rc = wsdisplay_delscreen(sc, i, WSDISPLAY_DELSCR_QUIET | (flags & DETACH_FORCE ? WSDISPLAY_DELSCR_FORCE : 0))) != 0) return (rc); } #ifdef HAVE_BURNER_SUPPORT timeout_del(&sc->sc_burner); #endif #if NWSKBD > 0 if (sc->sc_input != NULL) { #if NWSMUX > 0 /* * If we are the display of the mux we are attached to, * disconnect all input devices from us. */ if (sc->sc_input->me_dispdv == &sc->sc_dv) { if ((rc = wsmux_set_display((struct wsmux_softc *) sc->sc_input, NULL)) != 0) return (rc); } /* * XXX * If we created a standalone mux (dmux), we should destroy it * there, but there is currently no support for this in wsmux. */ #else if ((rc = wskbd_set_display((struct device *)sc->sc_input, NULL)) != 0) return (rc); #endif } #endif return (0); } /* Print function (for parent devices). */ int wsemuldisplaydevprint(void *aux, const char *pnp) { #if 0 /* -Wunused */ struct wsemuldisplaydev_attach_args *ap = aux; #endif if (pnp) printf("wsdisplay at %s", pnp); #if 0 /* don't bother; it's ugly */ printf(" console %d", ap->console); #endif return (UNCONF); } /* Submatch function (for parent devices). */ int wsemuldisplaydevsubmatch(struct device *parent, void *match, void *aux) { extern struct cfdriver wsdisplay_cd; struct cfdata *cf = match; /* only allow wsdisplay to attach */ if (cf->cf_driver == &wsdisplay_cd) return ((*cf->cf_attach->ca_match)(parent, match, aux)); return (0); } void wsdisplay_common_attach(struct wsdisplay_softc *sc, int console, int kbdmux, const struct wsscreen_list *scrdata, const struct wsdisplay_accessops *accessops, void *accesscookie, u_int defaultscreens) { int i, start = 0; #if NWSKBD > 0 struct wsevsrc *kme; #if NWSMUX > 0 struct wsmux_softc *mux; if (kbdmux >= 0) mux = wsmux_getmux(kbdmux); else mux = wsmux_create("dmux", sc->sc_dv.dv_unit); /* XXX panic()ing isn't nice, but attach cannot fail */ if (mux == NULL) panic("wsdisplay_common_attach: no memory"); sc->sc_input = &mux->sc_base; if (kbdmux >= 0) printf(" mux %d", kbdmux); #else #if 0 /* not worth keeping, especially since the default value is not -1... */ if (kbdmux >= 0) printf(" (mux ignored)"); #endif #endif /* NWSMUX > 0 */ #endif /* NWSKBD > 0 */ sc->sc_isconsole = console; sc->sc_resumescreen = WSDISPLAY_NULLSCREEN; if (console) { KASSERT(wsdisplay_console_initted); KASSERT(wsdisplay_console_device == NULL); sc->sc_scr[0] = wsscreen_attach(sc, 1, 0, 0, 0, 0, 0, 0); if (sc->sc_scr[0] == NULL) return; wsdisplay_console_device = sc; printf(": console (%s, %s emulation)", wsdisplay_console_conf.scrdata->name, wsdisplay_console_conf.wsemul->name); #if NWSKBD > 0 kme = wskbd_set_console_display(&sc->sc_dv, sc->sc_input); if (kme != NULL) printf(", using %s", kme->me_dv.dv_xname); #if NWSMUX == 0 sc->sc_input = kme; #endif #endif sc->sc_focusidx = 0; sc->sc_focus = sc->sc_scr[0]; start = 1; } printf("\n"); #if NWSKBD > 0 && NWSMUX > 0 /* * If this mux did not have a display device yet, volunteer for * the job. */ if (mux->sc_displaydv == NULL) wsmux_set_display(mux, &sc->sc_dv); #endif sc->sc_accessops = accessops; sc->sc_accesscookie = accesscookie; sc->sc_scrdata = scrdata; /* * Set up a number of virtual screens if wanted. The * WSDISPLAYIO_ADDSCREEN ioctl is more flexible, so this code * is for special cases like installation kernels, as well as * sane multihead defaults. */ if (defaultscreens == 0) defaultscreens = wsdisplay_defaultscreens; for (i = start; i < defaultscreens; i++) { if (wsdisplay_addscreen(sc, i, 0, 0)) break; } if (i > start) wsdisplay_addscreen_print(sc, start, i-start); #ifdef HAVE_BURNER_SUPPORT sc->sc_burnoutintvl = (hz * WSDISPLAY_DEFBURNOUT) / 1000; sc->sc_burninintvl = (hz * WSDISPLAY_DEFBURNIN) / 1000; sc->sc_burnflags = WSDISPLAY_BURN_OUTPUT | WSDISPLAY_BURN_KBD | WSDISPLAY_BURN_MOUSE; timeout_set(&sc->sc_burner, wsdisplay_burner, sc); sc->sc_burnout = sc->sc_burnoutintvl; wsdisplay_burn(sc, sc->sc_burnflags); #endif #if NWSKBD > 0 && NWSMUX == 0 if (console == 0) { /* * In the non-wsmux world, always connect wskbd0 and wsdisplay0 * together. */ extern struct cfdriver wskbd_cd; if (wskbd_cd.cd_ndevs != 0 && sc->sc_dv.dv_unit == 0) { if (wsdisplay_set_kbd(&sc->sc_dv, (struct wsevsrc *)wskbd_cd.cd_devs[0]) == 0) wskbd_set_display(wskbd_cd.cd_devs[0], &sc->sc_dv); } } #endif } void wsdisplay_cnattach(const struct wsscreen_descr *type, void *cookie, int ccol, int crow, long defattr) { const struct wsemul_ops *wsemul; const struct wsdisplay_emulops *emulops; KASSERT(type->nrows > 0); KASSERT(type->ncols > 0); KASSERT(crow < type->nrows); KASSERT(ccol < type->ncols); wsdisplay_console_conf.emulops = emulops = type->textops; wsdisplay_console_conf.emulcookie = cookie; wsdisplay_console_conf.scrdata = type; #ifdef WSEMUL_DUMB /* * If the emulops structure is crippled, force a dumb emulation. */ if (emulops->cursor == NULL || emulops->copycols == NULL || emulops->copyrows == NULL || emulops->erasecols == NULL || emulops->eraserows == NULL) wsemul = wsemul_pick("dumb"); else #endif wsemul = wsemul_pick(""); wsdisplay_console_conf.wsemul = wsemul; wsdisplay_console_conf.wsemulcookie = (*wsemul->cnattach)(type, cookie, ccol, crow, defattr); if (!wsdisplay_console_initted) cn_tab = &wsdisplay_cons; wsdisplay_console_initted = 1; } /* * Tty and cdevsw functions. */ int wsdisplayopen(dev_t dev, int flag, int mode, struct proc *p) { struct wsdisplay_softc *sc; struct tty *tp; int unit, newopen, error; struct wsscreen *scr; unit = WSDISPLAYUNIT(dev); if (unit >= wsdisplay_cd.cd_ndevs || /* make sure it was attached */ (sc = wsdisplay_cd.cd_devs[unit]) == NULL) return (ENXIO); if (ISWSDISPLAYCTL(dev)) return (0); if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN) return (ENXIO); if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) return (ENXIO); if (WSSCREEN_HAS_TTY(scr)) { tp = scr->scr_tty; tp->t_oproc = wsdisplaystart; tp->t_param = wsdisplayparam; tp->t_dev = dev; newopen = (tp->t_state & TS_ISOPEN) == 0; if (newopen) { ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; wsdisplayparam(tp, &tp->t_termios); ttsetwater(tp); } else if ((tp->t_state & TS_XCLUDE) != 0 && suser(p, 0) != 0) return (EBUSY); tp->t_state |= TS_CARR_ON; error = ((*linesw[tp->t_line].l_open)(dev, tp, p)); if (error) return (error); if (newopen) { /* set window sizes as appropriate, and reset the emulation */ tp->t_winsize.ws_row = scr->scr_dconf->scrdata->nrows; tp->t_winsize.ws_col = scr->scr_dconf->scrdata->ncols; } } scr->scr_flags |= SCR_OPEN; return (0); } int wsdisplayclose(dev_t dev, int flag, int mode, struct proc *p) { struct wsdisplay_softc *sc; struct tty *tp; int unit; struct wsscreen *scr; unit = WSDISPLAYUNIT(dev); sc = wsdisplay_cd.cd_devs[unit]; if (ISWSDISPLAYCTL(dev)) return (0); if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) return (ENXIO); if (WSSCREEN_HAS_TTY(scr)) { if (scr->scr_hold_screen) { int s; /* XXX RESET KEYBOARD LEDS, etc. */ s = spltty(); /* avoid conflict with keyboard */ wsdisplay_kbdholdscr(scr, 0); splx(s); } tp = scr->scr_tty; (*linesw[tp->t_line].l_close)(tp, flag, p); ttyclose(tp); } #ifdef WSDISPLAY_COMPAT_USL if (scr->scr_syncops) (*scr->scr_syncops->destroy)(scr->scr_synccookie); #endif scr->scr_flags &= ~SCR_GRAPHICS; (*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie, WSEMUL_RESET); if (wsdisplay_clearonclose) (*scr->scr_dconf->wsemul->reset) (scr->scr_dconf->wsemulcookie, WSEMUL_CLEARSCREEN); #ifdef WSDISPLAY_COMPAT_RAWKBD if (scr->scr_rawkbd) { int kbmode = WSKBD_TRANSLATED; (void) wsdisplay_internal_ioctl(sc, scr, WSKBDIO_SETMODE, (caddr_t)&kbmode, FWRITE, p); } #endif scr->scr_flags &= ~SCR_OPEN; #ifdef HAVE_WSMOUSED_SUPPORT /* remove the selection at logout */ if (sc->sc_copybuffer != NULL) bzero(sc->sc_copybuffer, sc->sc_copybuffer_size); CLR(sc->sc_flags, SC_PASTE_AVAIL); #endif return (0); } int wsdisplayread(dev_t dev, struct uio *uio, int flag) { struct wsdisplay_softc *sc; struct tty *tp; int unit; struct wsscreen *scr; unit = WSDISPLAYUNIT(dev); sc = wsdisplay_cd.cd_devs[unit]; if (ISWSDISPLAYCTL(dev)) return (0); if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) return (ENXIO); if (!WSSCREEN_HAS_TTY(scr)) return (ENODEV); tp = scr->scr_tty; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int wsdisplaywrite(dev_t dev, struct uio *uio, int flag) { struct wsdisplay_softc *sc; struct tty *tp; int unit; struct wsscreen *scr; unit = WSDISPLAYUNIT(dev); sc = wsdisplay_cd.cd_devs[unit]; if (ISWSDISPLAYCTL(dev)) return (0); if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) return (ENXIO); if (!WSSCREEN_HAS_TTY(scr)) return (ENODEV); tp = scr->scr_tty; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } struct tty * wsdisplaytty(dev_t dev) { struct wsdisplay_softc *sc; int unit; struct wsscreen *scr; unit = WSDISPLAYUNIT(dev); sc = wsdisplay_cd.cd_devs[unit]; if (ISWSDISPLAYCTL(dev)) panic("wsdisplaytty() on ctl device"); if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) return (NULL); return (scr->scr_tty); } int wsdisplayioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct wsdisplay_softc *sc; struct tty *tp; int unit, error; struct wsscreen *scr; unit = WSDISPLAYUNIT(dev); sc = wsdisplay_cd.cd_devs[unit]; #ifdef WSDISPLAY_COMPAT_USL error = wsdisplay_usl_ioctl1(sc, cmd, data, flag, p); if (error >= 0) return (error); #endif if (ISWSDISPLAYCTL(dev)) { if (cmd != WSDISPLAYIO_GTYPE) return (wsdisplay_cfg_ioctl(sc, cmd, data, flag, p)); /* pass WSDISPLAYIO_GTYPE to the first screen */ dev = makedev(major(dev), WSDISPLAYMINOR(unit, 0)); } if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN) return (ENODEV); if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) return (ENXIO); if (WSSCREEN_HAS_TTY(scr)) { tp = scr->scr_tty; /* printf("disc\n"); */ /* do the line discipline ioctls first */ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); /* printf("tty\n"); */ /* then the tty ioctls */ error = ttioctl(tp, cmd, data, flag, p); if (error >= 0) return (error); } #ifdef WSDISPLAY_COMPAT_USL error = wsdisplay_usl_ioctl2(sc, scr, cmd, data, flag, p); if (error >= 0) return (error); #endif error = wsdisplay_internal_ioctl(sc, scr, cmd, data, flag, p); return (error != -1 ? error : ENOTTY); } int wsdisplay_param(struct device *dev, u_long cmd, struct wsdisplay_param *dp) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, (caddr_t)dp, 0, NULL)); } int wsdisplay_internal_ioctl(struct wsdisplay_softc *sc, struct wsscreen *scr, u_long cmd, caddr_t data, int flag, struct proc *p) { int error; #if NWSKBD > 0 struct wsevsrc *inp; #ifdef WSDISPLAY_COMPAT_RAWKBD switch (cmd) { case WSKBDIO_SETMODE: if ((flag & FWRITE) == 0) return (EACCES); scr->scr_rawkbd = (*(int *)data == WSKBD_RAW); return (wsdisplay_update_rawkbd(sc, scr)); case WSKBDIO_GETMODE: *(int *)data = (scr->scr_rawkbd ? WSKBD_RAW : WSKBD_TRANSLATED); return (0); } #endif inp = sc->sc_input; if (inp != NULL) { error = wsevsrc_display_ioctl(inp, cmd, data, flag, p); if (error >= 0) return (error); } #endif /* NWSKBD > 0 */ switch (cmd) { case WSDISPLAYIO_SMODE: case WSDISPLAYIO_USEFONT: #ifdef HAVE_BURNER_SUPPORT case WSDISPLAYIO_SVIDEO: case WSDISPLAYIO_SBURNER: #endif case WSDISPLAYIO_SETSCREEN: if ((flag & FWRITE) == 0) return (EACCES); } switch (cmd) { case WSDISPLAYIO_GMODE: if (scr->scr_flags & SCR_GRAPHICS) { if (scr->scr_flags & SCR_DUMBFB) *(u_int *)data = WSDISPLAYIO_MODE_DUMBFB; else *(u_int *)data = WSDISPLAYIO_MODE_MAPPED; } else *(u_int *)data = WSDISPLAYIO_MODE_EMUL; return (0); case WSDISPLAYIO_SMODE: #define d (*(int *)data) if (d != WSDISPLAYIO_MODE_EMUL && d != WSDISPLAYIO_MODE_MAPPED && d != WSDISPLAYIO_MODE_DUMBFB) return (EINVAL); scr->scr_flags &= ~SCR_GRAPHICS; if (d == WSDISPLAYIO_MODE_MAPPED || d == WSDISPLAYIO_MODE_DUMBFB) { scr->scr_flags |= SCR_GRAPHICS | ((d == WSDISPLAYIO_MODE_DUMBFB) ? SCR_DUMBFB : 0); /* clear cursor */ (*scr->scr_dconf->wsemul->reset) (scr->scr_dconf->wsemulcookie, WSEMUL_CLEARCURSOR); } #ifdef HAVE_BURNER_SUPPORT wsdisplay_burner_setup(sc, scr); #endif (void)(*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data, flag, p); return (0); #undef d case WSDISPLAYIO_USEFONT: #define d ((struct wsdisplay_font *)data) if (!sc->sc_accessops->load_font) return (EINVAL); d->data = NULL; error = (*sc->sc_accessops->load_font)(sc->sc_accesscookie, scr->scr_dconf->emulcookie, d); if (!error) (*scr->scr_dconf->wsemul->reset) (scr->scr_dconf->wsemulcookie, WSEMUL_SYNCFONT); return (error); #undef d #ifdef HAVE_BURNER_SUPPORT case WSDISPLAYIO_GVIDEO: *(u_int *)data = !sc->sc_burnman; break; case WSDISPLAYIO_SVIDEO: if (*(u_int *)data != WSDISPLAYIO_VIDEO_OFF && *(u_int *)data != WSDISPLAYIO_VIDEO_ON) return (EINVAL); if (sc->sc_accessops->burn_screen == NULL) return (EOPNOTSUPP); (*sc->sc_accessops->burn_screen)(sc->sc_accesscookie, *(u_int *)data, sc->sc_burnflags); sc->sc_burnman = *(u_int *)data == WSDISPLAYIO_VIDEO_OFF; break; case WSDISPLAYIO_GBURNER: #define d ((struct wsdisplay_burner *)data) d->on = sc->sc_burninintvl * 1000 / hz; d->off = sc->sc_burnoutintvl * 1000 / hz; d->flags = sc->sc_burnflags; return (0); case WSDISPLAYIO_SBURNER: { struct wsscreen *active; if (d->flags & ~(WSDISPLAY_BURN_VBLANK | WSDISPLAY_BURN_KBD | WSDISPLAY_BURN_MOUSE | WSDISPLAY_BURN_OUTPUT)) return EINVAL; error = 0; sc->sc_burnflags = d->flags; /* disable timeout if necessary */ if ((sc->sc_burnflags & (WSDISPLAY_BURN_OUTPUT | WSDISPLAY_BURN_KBD | WSDISPLAY_BURN_MOUSE)) == 0) { if (sc->sc_burnout) timeout_del(&sc->sc_burner); } active = sc->sc_focus; if (active == NULL) active = scr; if (d->on) { sc->sc_burninintvl = hz * d->on / 1000; if (sc->sc_burnman) { sc->sc_burnout = sc->sc_burninintvl; /* reinit timeout if changed */ if ((active->scr_flags & SCR_GRAPHICS) == 0) wsdisplay_burn(sc, sc->sc_burnflags); } } if (d->off) { sc->sc_burnoutintvl = hz * d->off / 1000; if (!sc->sc_burnman) { sc->sc_burnout = sc->sc_burnoutintvl; /* reinit timeout if changed */ if ((active->scr_flags & SCR_GRAPHICS) == 0) wsdisplay_burn(sc, sc->sc_burnflags); } } return (error); } #undef d #endif /* HAVE_BURNER_SUPPORT */ case WSDISPLAYIO_GETSCREEN: return (wsdisplay_getscreen(sc, (struct wsdisplay_addscreendata *)data)); case WSDISPLAYIO_SETSCREEN: return (wsdisplay_switch((void *)sc, *(int *)data, 1)); case WSDISPLAYIO_GETSCREENTYPE: #define d ((struct wsdisplay_screentype *)data) if (d->idx >= sc->sc_scrdata->nscreens) return(EINVAL); d->nidx = sc->sc_scrdata->nscreens; strncpy(d->name, sc->sc_scrdata->screens[d->idx]->name, WSSCREEN_NAME_SIZE); d->ncols = sc->sc_scrdata->screens[d->idx]->ncols; d->nrows = sc->sc_scrdata->screens[d->idx]->nrows; d->fontwidth = sc->sc_scrdata->screens[d->idx]->fontwidth; d->fontheight = sc->sc_scrdata->screens[d->idx]->fontheight; return (0); #undef d case WSDISPLAYIO_GETEMULTYPE: #define d ((struct wsdisplay_emultype *)data) if (wsemul_getname(d->idx) == NULL) return(EINVAL); strncpy(d->name, wsemul_getname(d->idx), WSEMUL_NAME_SIZE); return (0); #undef d } /* check ioctls for display */ return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data, flag, p)); } int wsdisplay_cfg_ioctl(struct wsdisplay_softc *sc, u_long cmd, caddr_t data, int flag, struct proc *p) { int error; void *buf; size_t fontsz; #if NWSKBD > 0 struct wsevsrc *inp; #endif switch (cmd) { #ifdef HAVE_WSMOUSED_SUPPORT case WSDISPLAYIO_WSMOUSED: error = wsmoused(sc, data, flag, p); return (error); #endif case WSDISPLAYIO_ADDSCREEN: #define d ((struct wsdisplay_addscreendata *)data) if ((error = wsdisplay_addscreen(sc, d->idx, d->screentype, d->emul)) == 0) wsdisplay_addscreen_print(sc, d->idx, 0); return (error); #undef d case WSDISPLAYIO_DELSCREEN: #define d ((struct wsdisplay_delscreendata *)data) return (wsdisplay_delscreen(sc, d->idx, d->flags)); #undef d case WSDISPLAYIO_GETSCREEN: return (wsdisplay_getscreen(sc, (struct wsdisplay_addscreendata *)data)); case WSDISPLAYIO_SETSCREEN: return (wsdisplay_switch((void *)sc, *(int *)data, 1)); case WSDISPLAYIO_LDFONT: #define d ((struct wsdisplay_font *)data) if (!sc->sc_accessops->load_font) return (EINVAL); fontsz = d->fontheight * d->stride * d->numchars; if (fontsz > WSDISPLAY_MAXFONTSZ) return (EINVAL); buf = malloc(fontsz, M_DEVBUF, M_WAITOK); error = copyin(d->data, buf, fontsz); if (error) { free(buf, M_DEVBUF, fontsz); return (error); } d->data = buf; error = (*sc->sc_accessops->load_font)(sc->sc_accesscookie, 0, d); if (error) free(buf, M_DEVBUF, fontsz); return (error); case WSDISPLAYIO_LSFONT: if (!sc->sc_accessops->list_font) return (EINVAL); error = (*sc->sc_accessops->list_font)(sc->sc_accesscookie, d); return (error); case WSDISPLAYIO_DELFONT: return (EINVAL); #undef d #if NWSKBD > 0 case WSMUXIO_ADD_DEVICE: #define d ((struct wsmux_device *)data) if (d->idx == -1 && d->type == WSMUX_KBD) d->idx = wskbd_pickfree(); #undef d /* FALLTHROUGH */ case WSMUXIO_INJECTEVENT: case WSMUXIO_REMOVE_DEVICE: case WSMUXIO_LIST_DEVICES: inp = sc->sc_input; if (inp == NULL) return (ENXIO); return (wsevsrc_ioctl(inp, cmd, data, flag,p)); #endif /* NWSKBD > 0 */ } return (EINVAL); } paddr_t wsdisplaymmap(dev_t dev, off_t offset, int prot) { struct wsdisplay_softc *sc = wsdisplay_cd.cd_devs[WSDISPLAYUNIT(dev)]; struct wsscreen *scr; if (ISWSDISPLAYCTL(dev)) return (-1); if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) return (-1); if (!(scr->scr_flags & SCR_GRAPHICS)) return (-1); /* pass mmap to display */ return ((*sc->sc_accessops->mmap)(sc->sc_accesscookie, offset, prot)); } int wsdisplaypoll(dev_t dev, int events, struct proc *p) { struct wsdisplay_softc *sc = wsdisplay_cd.cd_devs[WSDISPLAYUNIT(dev)]; struct wsscreen *scr; if (ISWSDISPLAYCTL(dev)) return (0); if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) return (POLLERR); if (!WSSCREEN_HAS_TTY(scr)) return (POLLERR); return (ttpoll(dev, events, p)); } int wsdisplaykqfilter(dev_t dev, struct knote *kn) { struct wsdisplay_softc *sc = wsdisplay_cd.cd_devs[WSDISPLAYUNIT(dev)]; struct wsscreen *scr; if (ISWSDISPLAYCTL(dev)) return (ENXIO); if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) return (ENXIO); if (!WSSCREEN_HAS_TTY(scr)) return (ENXIO); return (ttkqfilter(dev, kn)); } void wsdisplaystart(struct tty *tp) { struct wsdisplay_softc *sc; struct wsscreen *scr; int s, n, done, unit; u_char *buf; unit = WSDISPLAYUNIT(tp->t_dev); if (unit >= wsdisplay_cd.cd_ndevs || (sc = wsdisplay_cd.cd_devs[unit]) == NULL) return; s = spltty(); if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { splx(s); return; } if (tp->t_outq.c_cc == 0 && tp->t_wsel.si_selpid == 0) goto low; if ((scr = sc->sc_scr[WSDISPLAYSCREEN(tp->t_dev)]) == NULL) { splx(s); return; } if (scr->scr_hold_screen) { tp->t_state |= TS_TIMEOUT; splx(s); return; } tp->t_state |= TS_BUSY; splx(s); /* * Drain output from ring buffer. * The output will normally be in one contiguous chunk, but when the * ring wraps, it will be in two pieces.. one at the end of the ring, * the other at the start. For performance, rather than loop here, * we output one chunk, see if there's another one, and if so, output * it too. */ n = ndqb(&tp->t_outq, 0); buf = tp->t_outq.c_cf; if (!(scr->scr_flags & SCR_GRAPHICS)) { #ifdef HAVE_BURNER_SUPPORT wsdisplay_burn(sc, WSDISPLAY_BURN_OUTPUT); #endif #ifdef HAVE_WSMOUSED_SUPPORT if (scr == sc->sc_focus) mouse_remove(scr); #endif done = (*scr->scr_dconf->wsemul->output) (scr->scr_dconf->wsemulcookie, buf, n, 0); } else done = n; ndflush(&tp->t_outq, done); if (done == n) { if ((n = ndqb(&tp->t_outq, 0)) > 0) { buf = tp->t_outq.c_cf; if (!(scr->scr_flags & SCR_GRAPHICS)) { done = (*scr->scr_dconf->wsemul->output) (scr->scr_dconf->wsemulcookie, buf, n, 0); } else done = n; ndflush(&tp->t_outq, done); } } s = spltty(); tp->t_state &= ~TS_BUSY; /* Come back if there's more to do */ if (tp->t_outq.c_cc) { tp->t_state |= TS_TIMEOUT; timeout_add(&tp->t_rstrt_to, (hz > 128) ? (hz / 128) : 1); } low: ttwakeupwr(tp); splx(s); } int wsdisplaystop(struct tty *tp, int flag) { int s; s = spltty(); if (ISSET(tp->t_state, TS_BUSY)) if (!ISSET(tp->t_state, TS_TTSTOP)) SET(tp->t_state, TS_FLUSH); splx(s); return (0); } /* Set line parameters. */ int wsdisplayparam(struct tty *tp, struct termios *t) { tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; return (0); } /* * Callbacks for the emulation code. */ void wsdisplay_emulbell(void *v) { struct wsscreen *scr = v; if (scr == NULL) /* console, before real attach */ return; if (scr->scr_flags & SCR_GRAPHICS) /* can this happen? */ return; (void) wsdisplay_internal_ioctl(scr->sc, scr, WSKBDIO_BELL, NULL, FWRITE, NULL); } #if !defined(WSEMUL_NO_VT100) void wsdisplay_emulinput(void *v, const u_char *data, u_int count) { struct wsscreen *scr = v; struct tty *tp; if (v == NULL) /* console, before real attach */ return; if (scr->scr_flags & SCR_GRAPHICS) /* XXX can't happen */ return; if (!WSSCREEN_HAS_TTY(scr)) return; tp = scr->scr_tty; while (count-- > 0) (*linesw[tp->t_line].l_rint)(*data++, tp); } #endif /* * Calls from the keyboard interface. */ void wsdisplay_kbdinput(struct device *dev, kbd_t layout, keysym_t *ks, int num) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; struct wsscreen *scr; const u_char *dp; int count; struct tty *tp; scr = sc->sc_focus; if (!scr || !WSSCREEN_HAS_TTY(scr)) return; tp = scr->scr_tty; for (; num > 0; num--) { count = (*scr->scr_dconf->wsemul->translate) (scr->scr_dconf->wsemulcookie, layout, *ks++, &dp); while (count-- > 0) (*linesw[tp->t_line].l_rint)(*dp++, tp); } } #ifdef WSDISPLAY_COMPAT_RAWKBD void wsdisplay_rawkbdinput(struct device *dev, u_char *buf, int num) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; struct wsscreen *scr; struct tty *tp; scr = sc->sc_focus; if (!scr || !WSSCREEN_HAS_TTY(scr)) return; tp = scr->scr_tty; while (num-- > 0) (*linesw[tp->t_line].l_rint)(*buf++, tp); } int wsdisplay_update_rawkbd(struct wsdisplay_softc *sc, struct wsscreen *scr) { #if NWSKBD > 0 int s, raw, data, error; struct wsevsrc *inp; s = spltty(); raw = (scr ? scr->scr_rawkbd : 0); if (scr != sc->sc_focus || sc->sc_rawkbd == raw) { splx(s); return (0); } data = raw ? WSKBD_RAW : WSKBD_TRANSLATED; inp = sc->sc_input; if (inp == NULL) { splx(s); return (ENXIO); } error = wsevsrc_display_ioctl(inp, WSKBDIO_SETMODE, &data, FWRITE, 0); if (!error) sc->sc_rawkbd = raw; splx(s); return (error); #else return (0); #endif } #endif int wsdisplay_switch3(void *arg, int error, int waitok) { struct wsdisplay_softc *sc = arg; int no; struct wsscreen *scr; #ifdef WSDISPLAY_COMPAT_USL if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) { printf("wsdisplay_switch3: not switching\n"); return (EINVAL); } no = sc->sc_screenwanted; if (no < 0 || no >= WSDISPLAY_MAXSCREEN) panic("wsdisplay_switch3: invalid screen %d", no); scr = sc->sc_scr[no]; if (!scr) { printf("wsdisplay_switch3: screen %d disappeared\n", no); error = ENXIO; } if (error) { /* try to recover, avoid recursion */ if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) { printf("wsdisplay_switch3: giving up\n"); sc->sc_focus = NULL; #ifdef WSDISPLAY_COMPAT_RAWKBD wsdisplay_update_rawkbd(sc, 0); #endif CLR(sc->sc_flags, SC_SWITCHPENDING); return (error); } sc->sc_screenwanted = sc->sc_oldscreen; sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; return (wsdisplay_switch1(arg, 0, waitok)); } #else /* * If we do not have syncops support, we come straight from * wsdisplay_switch2 which has already validated our arguments * and did not sleep. */ no = sc->sc_screenwanted; scr = sc->sc_scr[no]; #endif CLR(sc->sc_flags, SC_SWITCHPENDING); #ifdef HAVE_BURNER_SUPPORT if (!error) wsdisplay_burner_setup(sc, scr); #endif if (!error && (scr->scr_flags & SCR_WAITACTIVE)) wakeup(scr); return (error); } int wsdisplay_switch2(void *arg, int error, int waitok) { struct wsdisplay_softc *sc = arg; int no; struct wsscreen *scr; if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) { printf("wsdisplay_switch2: not switching\n"); return (EINVAL); } no = sc->sc_screenwanted; if (no < 0 || no >= WSDISPLAY_MAXSCREEN) panic("wsdisplay_switch2: invalid screen %d", no); scr = sc->sc_scr[no]; if (!scr) { printf("wsdisplay_switch2: screen %d disappeared\n", no); error = ENXIO; } if (error) { /* try to recover, avoid recursion */ if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) { printf("wsdisplay_switch2: giving up\n"); sc->sc_focus = NULL; CLR(sc->sc_flags, SC_SWITCHPENDING); return (error); } sc->sc_screenwanted = sc->sc_oldscreen; sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; return (wsdisplay_switch1(arg, 0, waitok)); } sc->sc_focusidx = no; sc->sc_focus = scr; #ifdef WSDISPLAY_COMPAT_RAWKBD (void) wsdisplay_update_rawkbd(sc, scr); #endif /* keyboard map??? */ #ifdef WSDISPLAY_COMPAT_USL #define wsswitch_cb3 ((void (*)(void *, int, int))wsdisplay_switch3) if (scr->scr_syncops) { error = (*scr->scr_syncops->attach)(scr->scr_synccookie, waitok, sc->sc_isconsole && wsdisplay_cons_pollmode ? 0 : wsswitch_cb3, sc); if (error == EAGAIN) { /* switch will be done asynchronously */ return (0); } } #endif return (wsdisplay_switch3(sc, error, waitok)); } int wsdisplay_switch1(void *arg, int error, int waitok) { struct wsdisplay_softc *sc = arg; int no; struct wsscreen *scr; if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) { printf("wsdisplay_switch1: not switching\n"); return (EINVAL); } no = sc->sc_screenwanted; if (no == WSDISPLAY_NULLSCREEN) { CLR(sc->sc_flags, SC_SWITCHPENDING); if (!error) { sc->sc_focus = NULL; } wakeup(sc); return (error); } if (no < 0 || no >= WSDISPLAY_MAXSCREEN) panic("wsdisplay_switch1: invalid screen %d", no); scr = sc->sc_scr[no]; if (!scr) { printf("wsdisplay_switch1: screen %d disappeared\n", no); error = ENXIO; } if (error) { CLR(sc->sc_flags, SC_SWITCHPENDING); return (error); } #define wsswitch_cb2 ((void (*)(void *, int, int))wsdisplay_switch2) error = (*sc->sc_accessops->show_screen)(sc->sc_accesscookie, scr->scr_dconf->emulcookie, waitok, sc->sc_isconsole && wsdisplay_cons_pollmode ? 0 : wsswitch_cb2, sc); if (error == EAGAIN) { /* switch will be done asynchronously */ return (0); } return (wsdisplay_switch2(sc, error, waitok)); } int wsdisplay_switch(struct device *dev, int no, int waitok) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; int s, res = 0; struct wsscreen *scr; if (no != WSDISPLAY_NULLSCREEN) { if (no < 0 || no >= WSDISPLAY_MAXSCREEN) return (EINVAL); if (sc->sc_scr[no] == NULL) return (ENXIO); } s = spltty(); while (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN && res == 0) res = tsleep(&sc->sc_resumescreen, PCATCH, "wsrestore", 0); if (res) { splx(s); return (res); } if ((sc->sc_focus && no == sc->sc_focusidx) || (sc->sc_focus == NULL && no == WSDISPLAY_NULLSCREEN)) { splx(s); return (0); } if (ISSET(sc->sc_flags, SC_SWITCHPENDING)) { splx(s); return (EBUSY); } SET(sc->sc_flags, SC_SWITCHPENDING); sc->sc_screenwanted = no; splx(s); scr = sc->sc_focus; if (!scr) { sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; return (wsdisplay_switch1(sc, 0, waitok)); } else sc->sc_oldscreen = sc->sc_focusidx; #ifdef WSDISPLAY_COMPAT_USL #define wsswitch_cb1 ((void (*)(void *, int, int))wsdisplay_switch1) if (scr->scr_syncops) { res = (*scr->scr_syncops->detach)(scr->scr_synccookie, waitok, sc->sc_isconsole && wsdisplay_cons_pollmode ? 0 : wsswitch_cb1, sc); if (res == EAGAIN) { /* switch will be done asynchronously */ return (0); } } else if (scr->scr_flags & SCR_GRAPHICS) { /* no way to save state */ res = EBUSY; } #endif #ifdef HAVE_WSMOUSED_SUPPORT mouse_remove(scr); #endif return (wsdisplay_switch1(sc, res, waitok)); } void wsdisplay_reset(struct device *dev, enum wsdisplay_resetops op) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; struct wsscreen *scr; scr = sc->sc_focus; if (!scr) return; switch (op) { case WSDISPLAY_RESETEMUL: (*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie, WSEMUL_RESET); break; case WSDISPLAY_RESETCLOSE: wsdisplay_closescreen(sc, scr); break; } } #ifdef WSDISPLAY_COMPAT_USL /* * Interface for (external) VT switch / process synchronization code */ int wsscreen_attach_sync(struct wsscreen *scr, const struct wscons_syncops *ops, void *cookie) { if (scr->scr_syncops) { /* * The screen is already claimed. * Check if the owner is still alive. */ if ((*scr->scr_syncops->check)(scr->scr_synccookie)) return (EBUSY); } scr->scr_syncops = ops; scr->scr_synccookie = cookie; return (0); } int wsscreen_detach_sync(struct wsscreen *scr) { if (!scr->scr_syncops) return (EINVAL); scr->scr_syncops = NULL; return (0); } int wsscreen_lookup_sync(struct wsscreen *scr, const struct wscons_syncops *ops, /* used as ID */ void **cookiep) { if (!scr->scr_syncops || ops != scr->scr_syncops) return (EINVAL); *cookiep = scr->scr_synccookie; return (0); } #endif /* * Interface to virtual screen stuff */ int wsdisplay_maxscreenidx(struct wsdisplay_softc *sc) { return (WSDISPLAY_MAXSCREEN - 1); } int wsdisplay_screenstate(struct wsdisplay_softc *sc, int idx) { if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN) return (EINVAL); if (!sc->sc_scr[idx]) return (ENXIO); return ((sc->sc_scr[idx]->scr_flags & SCR_OPEN) ? EBUSY : 0); } int wsdisplay_getactivescreen(struct wsdisplay_softc *sc) { return (sc->sc_focus ? sc->sc_focusidx : WSDISPLAY_NULLSCREEN); } int wsscreen_switchwait(struct wsdisplay_softc *sc, int no) { struct wsscreen *scr; int s, res = 0; if (no == WSDISPLAY_NULLSCREEN) { s = spltty(); while (sc->sc_focus && res == 0) { res = tsleep(sc, PCATCH, "wswait", 0); } splx(s); return (res); } if (no < 0 || no >= WSDISPLAY_MAXSCREEN) return (ENXIO); scr = sc->sc_scr[no]; if (!scr) return (ENXIO); s = spltty(); if (scr != sc->sc_focus) { scr->scr_flags |= SCR_WAITACTIVE; res = tsleep(scr, PCATCH, "wswait2", 0); if (scr != sc->sc_scr[no]) res = ENXIO; /* disappeared in the meantime */ else scr->scr_flags &= ~SCR_WAITACTIVE; } splx(s); return (res); } void wsdisplay_kbdholdscr(struct wsscreen *scr, int hold) { if (hold) scr->scr_hold_screen = 1; else { scr->scr_hold_screen = 0; timeout_add(&scr->scr_tty->t_rstrt_to, 0); /* "immediate" */ } } void wsdisplay_kbdholdscreen(struct device *dev, int hold) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; struct wsscreen *scr; scr = sc->sc_focus; if (scr != NULL && WSSCREEN_HAS_TTY(scr)) wsdisplay_kbdholdscr(scr, hold); } #if NWSKBD > 0 void wsdisplay_set_console_kbd(struct wsevsrc *src) { if (wsdisplay_console_device == NULL) { src->me_dispdv = NULL; return; } #if NWSMUX > 0 if (wsmux_attach_sc((struct wsmux_softc *) wsdisplay_console_device->sc_input, src)) { src->me_dispdv = NULL; return; } #else wsdisplay_console_device->sc_input = src; #endif src->me_dispdv = &wsdisplay_console_device->sc_dv; } #if NWSMUX == 0 int wsdisplay_set_kbd(struct device *disp, struct wsevsrc *kbd) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)disp; if (sc->sc_input != NULL) return (EBUSY); sc->sc_input = kbd; return (0); } #endif #endif /* NWSKBD > 0 */ /* * Console interface. */ void wsdisplay_cnputc(dev_t dev, int i) { struct wsscreen_internal *dc; char c = i; if (!wsdisplay_console_initted) return; if (wsdisplay_console_device != NULL && (wsdisplay_console_device->sc_scr[0] != NULL) && (wsdisplay_console_device->sc_scr[0]->scr_flags & SCR_GRAPHICS)) return; dc = &wsdisplay_console_conf; #ifdef HAVE_BURNER_SUPPORT /*wsdisplay_burn(wsdisplay_console_device, WSDISPLAY_BURN_OUTPUT);*/ #endif (void)(*dc->wsemul->output)(dc->wsemulcookie, &c, 1, 1); } int wsdisplay_getc_dummy(dev_t dev) { /* panic? */ return (0); } void wsdisplay_pollc(dev_t dev, int on) { wsdisplay_cons_pollmode = on; /* notify to fb drivers */ if (wsdisplay_console_device != NULL && wsdisplay_console_device->sc_accessops->pollc != NULL) (*wsdisplay_console_device->sc_accessops->pollc) (wsdisplay_console_device->sc_accesscookie, on); /* notify to kbd drivers */ if (wsdisplay_cons_kbd_pollc) (*wsdisplay_cons_kbd_pollc)(dev, on); } void wsdisplay_set_cons_kbd(int (*get)(dev_t), void (*poll)(dev_t, int), void (*bell)(dev_t, u_int, u_int, u_int)) { wsdisplay_cons.cn_getc = get; wsdisplay_cons.cn_bell = bell; wsdisplay_cons_kbd_pollc = poll; } void wsdisplay_unset_cons_kbd(void) { wsdisplay_cons.cn_getc = wsdisplay_getc_dummy; wsdisplay_cons.cn_bell = NULL; wsdisplay_cons_kbd_pollc = NULL; } /* * Switch the console display to its first screen. */ void wsdisplay_switchtoconsole(void) { struct wsdisplay_softc *sc; struct wsscreen *scr; if (wsdisplay_console_device != NULL && cn_tab == &wsdisplay_cons) { sc = wsdisplay_console_device; if ((scr = sc->sc_scr[0]) == NULL) return; (*sc->sc_accessops->show_screen)(sc->sc_accesscookie, scr->scr_dconf->emulcookie, 0, NULL, NULL); } } /* * Deal with the xserver doing driver in userland and thus screwing up suspend * and resume by switching away from it at suspend/resume time. * * these functions must be called from the MD suspend callback, since we may * need to sleep if we have a user (probably an X server) on a vt. therefore * this can't be a config_suspend() hook. */ void wsdisplay_suspend(void) { int i; for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) if (wsdisplay_cd.cd_devs[i] != NULL) wsdisplay_suspend_device(wsdisplay_cd.cd_devs[i]); } void wsdisplay_suspend_device(struct device *dev) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; struct wsscreen *scr; int active, idx, ret = 0, s; if ((active = wsdisplay_getactivescreen(sc)) == WSDISPLAY_NULLSCREEN) return; scr = sc->sc_scr[active]; /* * We want to switch out of graphics mode for the suspend, but * only if we're in WSDISPLAY_MODE_MAPPED. */ retry: idx = WSDISPLAY_MAXSCREEN; if (scr->scr_flags & SCR_GRAPHICS && (scr->scr_flags & SCR_DUMBFB) == 0) { for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++) { if (sc->sc_scr[idx] == NULL || sc->sc_scr[idx] == scr) continue; if ((sc->sc_scr[idx]->scr_flags & SCR_GRAPHICS) == 0) break; } } /* if we don't have anything to switch to, we can't do anything */ if (idx == WSDISPLAY_MAXSCREEN) return; /* * we do a lot of magic here because we need to know that the * switch has completed before we return */ ret = wsdisplay_switch((struct device *)sc, idx, 1); if (ret == EBUSY) { /* XXX sleep on what's going on */ goto retry; } else if (ret) return; s = spltty(); sc->sc_resumescreen = active; /* block other vt switches until resume */ splx(s); /* * This will either return ENXIO (invalid (shouldn't happen) or * wsdisplay disappeared (problem solved)), or EINTR/ERESTART. * Not much we can do about the latter since we can't return to * userland. */ (void)wsscreen_switchwait(sc, idx); } void wsdisplay_resume(void) { int i; for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) if (wsdisplay_cd.cd_devs[i] != NULL) wsdisplay_resume_device(wsdisplay_cd.cd_devs[i]); } void wsdisplay_resume_device(struct device *dev) { struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; int idx, s; if (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN) { s = spltty(); idx = sc->sc_resumescreen; sc->sc_resumescreen = WSDISPLAY_NULLSCREEN; wakeup(&sc->sc_resumescreen); splx(s); (void)wsdisplay_switch((struct device *)sc, idx, 1); } } #ifdef HAVE_SCROLLBACK_SUPPORT void wsscrollback(void *arg, int op) { struct wsdisplay_softc *sc = arg; int lines; if (sc->sc_focus == NULL) return; if (op == WSDISPLAY_SCROLL_RESET) lines = 0; else { lines = sc->sc_focus->scr_dconf->scrdata->nrows - 1; if (op == WSDISPLAY_SCROLL_BACKWARD) lines = -lines; } if (sc->sc_accessops->scrollback) { (*sc->sc_accessops->scrollback)(sc->sc_accesscookie, sc->sc_focus->scr_dconf->emulcookie, lines); } } #endif #ifdef HAVE_BURNER_SUPPORT /* * Update screen burner behaviour after either a screen focus change or * a screen mode change. * This is needed to allow X11 to manage screen blanking without any * interference from the kernel. */ void wsdisplay_burner_setup(struct wsdisplay_softc *sc, struct wsscreen *scr) { if (scr->scr_flags & SCR_GRAPHICS) { /* enable video _immediately_ if it needs to be... */ if (sc->sc_burnman) wsdisplay_burner(sc); /* ...and disable the burner while X is running */ if (sc->sc_burnout) { timeout_del(&sc->sc_burner); sc->sc_burnout = 0; } } else { /* reenable the burner after exiting from X */ if (!sc->sc_burnman) { sc->sc_burnout = sc->sc_burnoutintvl; wsdisplay_burn(sc, sc->sc_burnflags); } } } void wsdisplay_burn(void *v, u_int flags) { struct wsdisplay_softc *sc = v; if ((flags & sc->sc_burnflags & (WSDISPLAY_BURN_OUTPUT | WSDISPLAY_BURN_KBD | WSDISPLAY_BURN_MOUSE)) && sc->sc_accessops->burn_screen) { if (sc->sc_burnout) timeout_add(&sc->sc_burner, sc->sc_burnout); if (sc->sc_burnman) sc->sc_burnout = 0; } } void wsdisplay_burner(void *v) { struct wsdisplay_softc *sc = v; int s; if (sc->sc_accessops->burn_screen) { (*sc->sc_accessops->burn_screen)(sc->sc_accesscookie, sc->sc_burnman, sc->sc_burnflags); s = spltty(); if (sc->sc_burnman) { sc->sc_burnout = sc->sc_burnoutintvl; timeout_add(&sc->sc_burner, sc->sc_burnout); } else sc->sc_burnout = sc->sc_burninintvl; sc->sc_burnman = !sc->sc_burnman; splx(s); } } #endif #ifdef HAVE_WSMOUSED_SUPPORT /* * wsmoused(8) support functions */ /* * Main function, called from wsdisplay_cfg_ioctl. */ int wsmoused(struct wsdisplay_softc *sc, caddr_t data, int flag, struct proc *p) { struct wscons_event mouse_event = *(struct wscons_event *)data; if (IS_MOTION_EVENT(mouse_event.type)) { if (sc->sc_focus != NULL) motion_event(sc->sc_focus, mouse_event.type, mouse_event.value); return 0; } if (IS_BUTTON_EVENT(mouse_event.type)) { if (sc->sc_focus != NULL) { /* XXX tv_sec contains the number of clicks */ if (mouse_event.type == WSCONS_EVENT_MOUSE_DOWN) { button_event(sc->sc_focus, mouse_event.value, mouse_event.time.tv_sec); } else button_event(sc->sc_focus, mouse_event.value, 0); } return (0); } if (IS_CTRL_EVENT(mouse_event.type)) { return ctrl_event(sc, mouse_event.type, mouse_event.value, p); } return -1; } /* * Mouse motion events */ void motion_event(struct wsscreen *scr, u_int type, int value) { switch (type) { case WSCONS_EVENT_MOUSE_DELTA_X: mouse_moverel(scr, value, 0); break; case WSCONS_EVENT_MOUSE_DELTA_Y: mouse_moverel(scr, 0, -value); break; #ifdef HAVE_SCROLLBACK_SUPPORT case WSCONS_EVENT_MOUSE_DELTA_Z: mouse_zaxis(scr, value); break; #endif default: break; } } /* * Button clicks events */ void button_event(struct wsscreen *scr, int button, int clicks) { switch (button) { case MOUSE_COPY_BUTTON: switch (clicks % 4) { case 0: /* button is up */ mouse_copy_end(scr); mouse_copy_selection(scr); break; case 1: /* single click */ mouse_copy_start(scr); mouse_copy_selection(scr); break; case 2: /* double click */ mouse_copy_word(scr); mouse_copy_selection(scr); break; case 3: /* triple click */ mouse_copy_line(scr); mouse_copy_selection(scr); break; } break; case MOUSE_PASTE_BUTTON: if (clicks != 0) mouse_paste(scr); break; case MOUSE_EXTEND_BUTTON: if (clicks != 0) mouse_copy_extend_after(scr); break; default: break; } } /* * Control events */ int ctrl_event(struct wsdisplay_softc *sc, u_int type, int value, struct proc *p) { struct wsscreen *scr; int i; switch (type) { case WSCONS_EVENT_WSMOUSED_OFF: CLR(sc->sc_flags, SC_PASTE_AVAIL); return (0); case WSCONS_EVENT_WSMOUSED_ON: if (!sc->sc_accessops->getchar) /* no wsmoused(8) support in the display driver */ return (1); allocate_copybuffer(sc); CLR(sc->sc_flags, SC_PASTE_AVAIL); for (i = 0 ; i < WSDISPLAY_DEFAULTSCREENS ; i++) if ((scr = sc->sc_scr[i]) != NULL) { scr->mouse = (WS_NCOLS(scr) * WS_NROWS(scr)) / 2; scr->cursor = scr->mouse; scr->cpy_start = 0; scr->cpy_end = 0; scr->orig_start = 0; scr->orig_end = 0; scr->mouse_flags = 0; } return (0); default: /* can't happen, really */ return 0; } } void mouse_moverel(struct wsscreen *scr, int dx, int dy) { struct wsscreen_internal *dconf = scr->scr_dconf; u_int old_mouse = scr->mouse; int mouse_col = scr->mouse % N_COLS(dconf); int mouse_row = scr->mouse / N_COLS(dconf); /* update position */ if (mouse_col + dx >= MAXCOL(dconf)) mouse_col = MAXCOL(dconf); else { if (mouse_col + dx <= 0) mouse_col = 0; else mouse_col += dx; } if (mouse_row + dy >= MAXROW(dconf)) mouse_row = MAXROW(dconf); else { if (mouse_row + dy <= 0) mouse_row = 0; else mouse_row += dy; } scr->mouse = mouse_row * N_COLS(dconf) + mouse_col; /* if we have moved */ if (old_mouse != scr->mouse) { /* XXX unblank screen if display.ms_act */ if (ISSET(scr->mouse_flags, SEL_IN_PROGRESS)) { /* selection in progress */ mouse_copy_extend(scr); } else { inverse_char(scr, scr->mouse); if (ISSET(scr->mouse_flags, MOUSE_VISIBLE)) inverse_char(scr, old_mouse); else SET(scr->mouse_flags, MOUSE_VISIBLE); } } } void inverse_char(struct wsscreen *scr, u_int pos) { struct wsscreen_internal *dconf = scr->scr_dconf; struct wsdisplay_charcell cell; int fg, bg, ul; int flags; int tmp; long attr; GETCHAR(scr, pos, &cell); (*dconf->emulops->unpack_attr)(dconf->emulcookie, cell.attr, &fg, &bg, &ul); /* * Display the mouse cursor as a color inverted cell whenever * possible. If this is not possible, ask for the video reverse * attribute. */ flags = 0; if (dconf->scrdata->capabilities & WSSCREEN_WSCOLORS) { flags |= WSATTR_WSCOLORS; tmp = fg; fg = bg; bg = tmp; } else if (dconf->scrdata->capabilities & WSSCREEN_REVERSE) { flags |= WSATTR_REVERSE; } if ((*dconf->emulops->alloc_attr)(dconf->emulcookie, fg, bg, flags | (ul ? WSATTR_UNDERLINE : 0), &attr) == 0) { cell.attr = attr; PUTCHAR(dconf, pos, cell.uc, cell.attr); } } void inverse_region(struct wsscreen *scr, u_int start, u_int end) { struct wsscreen_internal *dconf = scr->scr_dconf; u_int current_pos; u_int abs_end; /* sanity check, useful because 'end' can be (u_int)-1 */ abs_end = N_COLS(dconf) * N_ROWS(dconf); if (end > abs_end) return; current_pos = start; while (current_pos <= end) inverse_char(scr, current_pos++); } /* * Return the number of contiguous blank characters between the right margin * if border == 1 or between the next non-blank character and the current mouse * cursor if border == 0 */ u_int skip_spc_right(struct wsscreen *scr, int border) { struct wsscreen_internal *dconf = scr->scr_dconf; struct wsdisplay_charcell cell; u_int current = scr->cpy_end; u_int mouse_col = scr->cpy_end % N_COLS(dconf); u_int limit = current + (N_COLS(dconf) - mouse_col - 1); u_int res = 0; while (GETCHAR(scr, current, &cell) == 0 && cell.uc == ' ' && current <= limit) { current++; res++; } if (border == BORDER) { if (current > limit) return (res - 1); else return (0); } else { if (res != 0) return (res - 1); else return (res); } } /* * Return the number of contiguous blank characters between the first of the * contiguous blank characters and the current mouse cursor */ u_int skip_spc_left(struct wsscreen *scr) { struct wsscreen_internal *dconf = scr->scr_dconf; struct wsdisplay_charcell cell; u_int current = scr->cpy_start; u_int mouse_col = scr->mouse % N_COLS(dconf); u_int limit = current - mouse_col; u_int res = 0; while (GETCHAR(scr, current, &cell) == 0 && cell.uc == ' ' && current >= limit) { current--; res++; } if (res != 0) res--; return (res); } /* * Class of characters * Stolen from xterm sources of the Xfree project (see cvs tag below) * $TOG: button.c /main/76 1997/07/30 16:56:19 kaleb $ */ static const int charClass[256] = { /* NUL SOH STX ETX EOT ENQ ACK BEL */ 32, 1, 1, 1, 1, 1, 1, 1, /* BS HT NL VT NP CR SO SI */ 1, 32, 1, 1, 1, 1, 1, 1, /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ 1, 1, 1, 1, 1, 1, 1, 1, /* CAN EM SUB ESC FS GS RS US */ 1, 1, 1, 1, 1, 1, 1, 1, /* SP ! " # $ % & ' */ 32, 33, 34, 35, 36, 37, 38, 39, /* ( ) * + , - . / */ 40, 41, 42, 43, 44, 45, 46, 47, /* 0 1 2 3 4 5 6 7 */ 48, 48, 48, 48, 48, 48, 48, 48, /* 8 9 : ; < = > ? */ 48, 48, 58, 59, 60, 61, 62, 63, /* @ A B C D E F G */ 64, 48, 48, 48, 48, 48, 48, 48, /* H I J K L M N O */ 48, 48, 48, 48, 48, 48, 48, 48, /* P Q R S T U V W */ 48, 48, 48, 48, 48, 48, 48, 48, /* X Y Z [ \ ] ^ _ */ 48, 48, 48, 91, 92, 93, 94, 48, /* ` a b c d e f g */ 96, 48, 48, 48, 48, 48, 48, 48, /* h i j k l m n o */ 48, 48, 48, 48, 48, 48, 48, 48, /* p q r s t u v w */ 48, 48, 48, 48, 48, 48, 48, 48, /* x y z { | } ~ DEL */ 48, 48, 48, 123, 124, 125, 126, 1, /* x80 x81 x82 x83 IND NEL SSA ESA */ 1, 1, 1, 1, 1, 1, 1, 1, /* HTS HTJ VTS PLD PLU RI SS2 SS3 */ 1, 1, 1, 1, 1, 1, 1, 1, /* DCS PU1 PU2 STS CCH MW SPA EPA */ 1, 1, 1, 1, 1, 1, 1, 1, /* x98 x99 x9A CSI ST OSC PM APC */ 1, 1, 1, 1, 1, 1, 1, 1, /* - i c/ L ox Y- | So */ 160, 161, 162, 163, 164, 165, 166, 167, /* .. c0 ip << _ R0 - */ 168, 169, 170, 171, 172, 173, 174, 175, /* o +- 2 3 ' u q| . */ 176, 177, 178, 179, 180, 181, 182, 183, /* , 1 2 >> 1/4 1/2 3/4 ? */ 184, 185, 186, 187, 188, 189, 190, 191, /* A` A' A^ A~ A: Ao AE C, */ 48, 48, 48, 48, 48, 48, 48, 48, /* E` E' E^ E: I` I' I^ I: */ 48, 48, 48, 48, 48, 48, 48, 48, /* D- N~ O` O' O^ O~ O: X */ 48, 48, 48, 48, 48, 48, 48, 216, /* O/ U` U' U^ U: Y' P B */ 48, 48, 48, 48, 48, 48, 48, 48, /* a` a' a^ a~ a: ao ae c, */ 48, 48, 48, 48, 48, 48, 48, 48, /* e` e' e^ e: i` i' i^ i: */ 48, 48, 48, 48, 48, 48, 48, 48, /* d n~ o` o' o^ o~ o: -: */ 48, 48, 48, 48, 48, 48, 48, 248, /* o/ u` u' u^ u: y' P y: */ 48, 48, 48, 48, 48, 48, 48, 48 }; /* * Find the first blank beginning after the current cursor position */ u_int skip_char_right(struct wsscreen *scr, u_int offset) { struct wsscreen_internal *dconf = scr->scr_dconf; struct wsdisplay_charcell cell; u_int current = offset; u_int limit = current + (N_COLS(dconf) - (scr->mouse % N_COLS(dconf)) - 1); u_int class; u_int res = 0; GETCHAR(scr, current, &cell); class = charClass[cell.uc & 0xff]; while (GETCHAR(scr, current, &cell) == 0 && charClass[cell.uc & 0xff] == class && current <= limit) { current++; res++; } if (res != 0) res--; return (res); } /* * Find the first non-blank character before the cursor position */ u_int skip_char_left(struct wsscreen *scr, u_int offset) { struct wsscreen_internal *dconf = scr->scr_dconf; struct wsdisplay_charcell cell; u_int current = offset; u_int limit = current - (scr->mouse % N_COLS(dconf)); u_int class; u_int res = 0; GETCHAR(scr, current, &cell); class = charClass[cell.uc & 0xff]; while (GETCHAR(scr, current, &cell) == 0 && charClass[cell.uc & 0xff] == class && current >= limit) { current--; res++; } if (res != 0) res--; return (res); } /* * Compare character classes */ u_int class_cmp(struct wsscreen *scr, u_int first, u_int second) { struct wsdisplay_charcell cell; u_int first_class; u_int second_class; if (GETCHAR(scr, first, &cell) != 0) return (1); first_class = charClass[cell.uc & 0xff]; if (GETCHAR(scr, second, &cell) != 0) return (1); second_class = charClass[cell.uc & 0xff]; if (first_class != second_class) return (1); else return (0); } /* * Beginning of a copy operation */ void mouse_copy_start(struct wsscreen *scr) { u_int right; /* if no selection, then that's the first one */ SET(scr->sc->sc_flags, SC_PASTE_AVAIL); /* remove the previous selection */ if (ISSET(scr->mouse_flags, SEL_EXISTS)) remove_selection(scr); /* initial show of the cursor */ if (!ISSET(scr->mouse_flags, MOUSE_VISIBLE)) inverse_char(scr, scr->mouse); scr->cpy_start = scr->cpy_end = scr->mouse; scr->orig_start = scr->cpy_start; scr->orig_end = scr->cpy_end; scr->cursor = scr->cpy_end + 1; /* init value */ /* useful later, in mouse_copy_extend */ right = skip_spc_right(scr, BORDER); if (right) SET(scr->mouse_flags, BLANK_TO_EOL); SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_CHAR); CLR(scr->mouse_flags, SEL_BY_WORD | SEL_BY_LINE); CLR(scr->mouse_flags, MOUSE_VISIBLE); /* cursor hidden in selection */ } /* * Copy of the word under the cursor */ void mouse_copy_word(struct wsscreen *scr) { struct wsdisplay_charcell cell; u_int right; u_int left; if (ISSET(scr->mouse_flags, SEL_EXISTS)) remove_selection(scr); if (ISSET(scr->mouse_flags, MOUSE_VISIBLE)) inverse_char(scr, scr->mouse); scr->cpy_start = scr->cpy_end = scr->mouse; if (GETCHAR(scr, scr->mouse, &cell) == 0 && IS_ALPHANUM(cell.uc)) { right = skip_char_right(scr, scr->cpy_end); left = skip_char_left(scr, scr->cpy_start); } else { right = skip_spc_right(scr, NO_BORDER); left = skip_spc_left(scr); } scr->cpy_start -= left; scr->cpy_end += right; scr->orig_start = scr->cpy_start; scr->orig_end = scr->cpy_end; scr->cursor = scr->cpy_end + 1; /* init value, never happen */ inverse_region(scr, scr->cpy_start, scr->cpy_end); SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_WORD); CLR(scr->mouse_flags, SEL_BY_CHAR | SEL_BY_LINE); /* mouse cursor hidden in the selection */ CLR(scr->mouse_flags, BLANK_TO_EOL | MOUSE_VISIBLE); } /* * Copy of the current line */ void mouse_copy_line(struct wsscreen *scr) { struct wsscreen_internal *dconf = scr->scr_dconf; u_int row = scr->mouse / N_COLS(dconf); if (ISSET(scr->mouse_flags, SEL_EXISTS)) remove_selection(scr); if (ISSET(scr->mouse_flags, MOUSE_VISIBLE)) inverse_char(scr, scr->mouse); scr->cpy_start = row * N_COLS(dconf); scr->cpy_end = scr->cpy_start + (N_COLS(dconf) - 1); scr->orig_start = scr->cpy_start; scr->orig_end = scr->cpy_end; scr->cursor = scr->cpy_end + 1; inverse_region(scr, scr->cpy_start, scr->cpy_end); SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_LINE); CLR(scr->mouse_flags, SEL_BY_CHAR | SEL_BY_WORD); /* mouse cursor hidden in the selection */ CLR(scr->mouse_flags, BLANK_TO_EOL | MOUSE_VISIBLE); } /* * End of a copy operation */ void mouse_copy_end(struct wsscreen *scr) { CLR(scr->mouse_flags, SEL_IN_PROGRESS); if (ISSET(scr->mouse_flags, SEL_BY_WORD) || ISSET(scr->mouse_flags, SEL_BY_LINE)) { if (scr->cursor != scr->cpy_end + 1) inverse_char(scr, scr->cursor); scr->cursor = scr->cpy_end + 1; } } /* * Generic selection extend function */ void mouse_copy_extend(struct wsscreen *scr) { if (ISSET(scr->mouse_flags, SEL_BY_CHAR)) mouse_copy_extend_char(scr); if (ISSET(scr->mouse_flags, SEL_BY_WORD)) mouse_copy_extend_word(scr); if (ISSET(scr->mouse_flags, SEL_BY_LINE)) mouse_copy_extend_line(scr); } /* * Extend a selected region, character by character */ void mouse_copy_extend_char(struct wsscreen *scr) { u_int right; if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { if (ISSET(scr->mouse_flags, BLANK_TO_EOL)) { /* * First extension of selection. We handle special * cases of blank characters to eol */ right = skip_spc_right(scr, BORDER); if (scr->mouse > scr->orig_start) { /* the selection goes to the lower part of the screen */ /* remove the previous cursor, start of selection is now next line */ inverse_char(scr, scr->cpy_start); scr->cpy_start += (right + 1); scr->cpy_end = scr->cpy_start; scr->orig_start = scr->cpy_start; /* simulate the initial mark */ inverse_char(scr, scr->cpy_start); } else { /* the selection goes to the upper part of the screen */ /* remove the previous cursor, start of selection is now at the eol */ inverse_char(scr, scr->cpy_start); scr->orig_start += (right + 1); scr->cpy_start = scr->orig_start - 1; scr->cpy_end = scr->orig_start - 1; /* simulate the initial mark */ inverse_char(scr, scr->cpy_start); } CLR(scr->mouse_flags, BLANK_TO_EOL); } if (scr->mouse < scr->orig_start && scr->cpy_end >= scr->orig_start) { /* we go to the upper part of the screen */ /* reverse the old selection region */ remove_selection(scr); scr->cpy_end = scr->orig_start - 1; scr->cpy_start = scr->orig_start; } if (scr->cpy_start < scr->orig_start && scr->mouse >= scr->orig_start) { /* we go to the lower part of the screen */ /* reverse the old selection region */ remove_selection(scr); scr->cpy_start = scr->orig_start; scr->cpy_end = scr->orig_start - 1; } /* restore flags cleared in remove_selection() */ SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS); } if (scr->mouse >= scr->orig_start) { /* lower part of the screen */ if (scr->mouse > scr->cpy_end) { /* extending selection */ inverse_region(scr, scr->cpy_end + 1, scr->mouse); } else { /* reducing selection */ inverse_region(scr, scr->mouse + 1, scr->cpy_end); } scr->cpy_end = scr->mouse; } else { /* upper part of the screen */ if (scr->mouse < scr->cpy_start) { /* extending selection */ inverse_region(scr, scr->mouse, scr->cpy_start - 1); } else { /* reducing selection */ inverse_region(scr, scr->cpy_start, scr->mouse - 1); } scr->cpy_start = scr->mouse; } } /* * Extend a selected region, word by word */ void mouse_copy_extend_word(struct wsscreen *scr) { u_int old_cpy_end; u_int old_cpy_start; if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { /* remove cursor in selection (black one) */ if (scr->cursor != scr->cpy_end + 1) inverse_char(scr, scr->cursor); /* now, switch between lower and upper part of the screen */ if (scr->mouse < scr->orig_start && scr->cpy_end >= scr->orig_start) { /* going to the upper part of the screen */ inverse_region(scr, scr->orig_end + 1, scr->cpy_end); scr->cpy_end = scr->orig_end; } if (scr->mouse > scr->orig_end && scr->cpy_start <= scr->orig_start) { /* going to the lower part of the screen */ inverse_region(scr, scr->cpy_start, scr->orig_start - 1); scr->cpy_start = scr->orig_start; } } if (scr->mouse >= scr->orig_start) { /* lower part of the screen */ if (scr->mouse > scr->cpy_end) { /* extending selection */ old_cpy_end = scr->cpy_end; scr->cpy_end = scr->mouse + skip_char_right(scr, scr->mouse); inverse_region(scr, old_cpy_end + 1, scr->cpy_end); } else { if (class_cmp(scr, scr->mouse, scr->mouse + 1)) { /* reducing selection (remove last word) */ old_cpy_end = scr->cpy_end; scr->cpy_end = scr->mouse; inverse_region(scr, scr->cpy_end + 1, old_cpy_end); } else { old_cpy_end = scr->cpy_end; scr->cpy_end = scr->mouse + skip_char_right(scr, scr->mouse); if (scr->cpy_end != old_cpy_end) { /* reducing selection, from the end of * next word */ inverse_region(scr, scr->cpy_end + 1, old_cpy_end); } } } } else { /* upper part of the screen */ if (scr->mouse < scr->cpy_start) { /* extending selection */ old_cpy_start = scr->cpy_start; scr->cpy_start = scr->mouse - skip_char_left(scr, scr->mouse); inverse_region(scr, scr->cpy_start, old_cpy_start - 1); } else { if (class_cmp(scr, scr->mouse - 1, scr->mouse)) { /* reducing selection (remove last word) */ old_cpy_start = scr->cpy_start; scr->cpy_start = scr->mouse; inverse_region(scr, old_cpy_start, scr->cpy_start - 1); } else { old_cpy_start = scr->cpy_start; scr->cpy_start = scr->mouse - skip_char_left(scr, scr->mouse); if (scr->cpy_start != old_cpy_start) { inverse_region(scr, old_cpy_start, scr->cpy_start - 1); } } } } if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { /* display new cursor */ scr->cursor = scr->mouse; inverse_char(scr, scr->cursor); } } /* * Extend a selected region, line by line */ void mouse_copy_extend_line(struct wsscreen *scr) { struct wsscreen_internal *dconf = scr->scr_dconf; u_int old_row; u_int new_row; u_int old_cpy_start; u_int old_cpy_end; if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { /* remove cursor in selection (black one) */ if (scr->cursor != scr->cpy_end + 1) inverse_char(scr, scr->cursor); /* now, switch between lower and upper part of the screen */ if (scr->mouse < scr->orig_start && scr->cpy_end >= scr->orig_start) { /* going to the upper part of the screen */ inverse_region(scr, scr->orig_end + 1, scr->cpy_end); scr->cpy_end = scr->orig_end; } if (scr->mouse > scr->orig_end && scr->cpy_start <= scr->orig_start) { /* going to the lower part of the screen */ inverse_region(scr, scr->cpy_start, scr->orig_start - 1); scr->cpy_start = scr->orig_start; } } if (scr->mouse >= scr->orig_start) { /* lower part of the screen */ if (scr->cursor == scr->cpy_end + 1) scr->cursor = scr->cpy_end; old_row = scr->cursor / N_COLS(dconf); new_row = scr->mouse / N_COLS(dconf); old_cpy_end = scr->cpy_end; scr->cpy_end = new_row * N_COLS(dconf) + MAXCOL(dconf); if (new_row > old_row) inverse_region(scr, old_cpy_end + 1, scr->cpy_end); else if (new_row < old_row) inverse_region(scr, scr->cpy_end + 1, old_cpy_end); } else { /* upper part of the screen */ old_row = scr->cursor / N_COLS(dconf); new_row = scr->mouse / N_COLS(dconf); old_cpy_start = scr->cpy_start; scr->cpy_start = new_row * N_COLS(dconf); if (new_row < old_row) inverse_region(scr, scr->cpy_start, old_cpy_start - 1); else if (new_row > old_row) inverse_region(scr, old_cpy_start, scr->cpy_start - 1); } if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { /* display new cursor */ scr->cursor = scr->mouse; inverse_char(scr, scr->cursor); } } /* * Add an extension to a selected region, word by word */ void mouse_copy_extend_after(struct wsscreen *scr) { u_int start_dist; u_int end_dist; if (ISSET(scr->mouse_flags, SEL_EXISTS)) { SET(scr->mouse_flags, SEL_EXT_AFTER); mouse_hide(scr); /* hide current cursor */ if (scr->cpy_start > scr->mouse) start_dist = scr->cpy_start - scr->mouse; else start_dist = scr->mouse - scr->cpy_start; if (scr->mouse > scr->cpy_end) end_dist = scr->mouse - scr->cpy_end; else end_dist = scr->cpy_end - scr->mouse; if (start_dist < end_dist) { /* upper part of the screen*/ scr->orig_start = scr->mouse + 1; /* only used in mouse_copy_extend_line() */ scr->cursor = scr->cpy_start; } else { /* lower part of the screen */ scr->orig_start = scr->mouse; /* only used in mouse_copy_extend_line() */ scr->cursor = scr->cpy_end; } if (ISSET(scr->mouse_flags, SEL_BY_CHAR)) mouse_copy_extend_char(scr); if (ISSET(scr->mouse_flags, SEL_BY_WORD)) mouse_copy_extend_word(scr); if (ISSET(scr->mouse_flags, SEL_BY_LINE)) mouse_copy_extend_line(scr); mouse_copy_selection(scr); } } void mouse_hide(struct wsscreen *scr) { if (ISSET(scr->mouse_flags, MOUSE_VISIBLE)) { inverse_char(scr, scr->mouse); CLR(scr->mouse_flags, MOUSE_VISIBLE); } } /* * Remove a previously selected region */ void remove_selection(struct wsscreen *scr) { if (ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { /* reset the flag indicating an extension of selection */ CLR(scr->mouse_flags, SEL_EXT_AFTER); } inverse_region(scr, scr->cpy_start, scr->cpy_end); CLR(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS); } /* * Put the current visual selection in the selection buffer */ void mouse_copy_selection(struct wsscreen *scr) { struct wsscreen_internal *dconf = scr->scr_dconf; struct wsdisplay_charcell cell; u_int current = 0; u_int blank = current; u_int buf_end = (N_COLS(dconf) + 1) * N_ROWS(dconf); u_int sel_cur; u_int sel_end; sel_cur = scr->cpy_start; sel_end = scr->cpy_end; while (sel_cur <= sel_end && current < buf_end - 1) { if (GETCHAR(scr, sel_cur, &cell) != 0) break; scr->sc->sc_copybuffer[current] = cell.uc; if (!IS_SPACE(cell.uc)) blank = current + 1; /* first blank after non-blank */ current++; if (sel_cur % N_COLS(dconf) == MAXCOL(dconf)) { /* * If we are on the last column of the screen, * insert a carriage return. */ scr->sc->sc_copybuffer[blank] = '\r'; current = ++blank; } sel_cur++; } scr->sc->sc_copybuffer[current] = '\0'; } /* * Paste the current selection */ void mouse_paste(struct wsscreen *scr) { char *current = scr->sc->sc_copybuffer; struct tty *tp; u_int len; if (ISSET(scr->sc->sc_flags, SC_PASTE_AVAIL)) { if (!WSSCREEN_HAS_TTY(scr)) return; tp = scr->scr_tty; for (len = strlen(scr->sc->sc_copybuffer); len != 0; len--) (*linesw[tp->t_line].l_rint)(*current++, tp); } } #ifdef HAVE_SCROLLBACK_SUPPORT /* * Handle the z axis. * The z axis (roller or wheel) is mapped by default to scrollback. */ void mouse_zaxis(struct wsscreen *scr, int z) { if (z < 0) wsscrollback(scr->sc, WSDISPLAY_SCROLL_BACKWARD); else wsscrollback(scr->sc, WSDISPLAY_SCROLL_FORWARD); } #endif /* * Allocate the copy buffer. The size is: * (cols + 1) * (rows) * (+1 for '\n' at the end of lines), * where cols and rows are the maximum of column and rows of all screens. */ void allocate_copybuffer(struct wsdisplay_softc *sc) { int nscreens = sc->sc_scrdata->nscreens; int i, s; const struct wsscreen_descr **screens_list = sc->sc_scrdata->screens; const struct wsscreen_descr *current; u_int size = sc->sc_copybuffer_size; s = spltty(); for (i = 0; i < nscreens; i++) { current = *screens_list; if ((current->ncols + 1) * current->nrows > size) size = (current->ncols + 1) * current->nrows; screens_list++; } if (size != sc->sc_copybuffer_size && sc->sc_copybuffer_size != 0) { bzero(sc->sc_copybuffer, sc->sc_copybuffer_size); free(sc->sc_copybuffer, M_DEVBUF, 0); } if ((sc->sc_copybuffer = (char *)malloc(size, M_DEVBUF, M_NOWAIT)) == NULL) { printf("%s: couldn't allocate copy buffer\n", sc->sc_dv.dv_xname); size = 0; } sc->sc_copybuffer_size = size; splx(s); } /* Remove selection and cursor on current screen */ void mouse_remove(struct wsscreen *scr) { if (ISSET(scr->mouse_flags, SEL_EXISTS)) remove_selection(scr); mouse_hide(scr); } #endif /* HAVE_WSMOUSED_SUPPORT */