Branch :
/* $OpenBSD: wsdisplay.c,v 1.156 2026/04/17 06:18:19 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 <sys/param.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/task.h>
#include <sys/tty.h>
#include <sys/signalvar.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/vnode.h>
#include <sys/timeout.h>
#include <dev/wscons/wscons_features.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wsksymvar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsemulvar.h>
#include <dev/wscons/wscons_callbacks.h>
#include <dev/cons.h>
#include "wsdisplay.h"
#include "wskbd.h"
#include "wsmux.h"
#if NWSKBD > 0
#include <dev/wscons/wseventvar.h>
#include <dev/wscons/wsmuxvar.h>
#endif
#ifdef DDB
#include <ddb/db_output.h>
#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 task scr_emulbell_task;
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, uint32_t);
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);
int wsdisplay_driver_ioctl(struct wsdisplay_softc *, u_long, caddr_t,
int, struct proc *);
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;
struct taskq *sc_taskq;
#ifdef HAVE_BURNER_SUPPORT
struct timeout sc_burner;
int sc_burnoutintvl; /* delay before blanking (ms) */
int sc_burninintvl; /* delay before unblanking (ms) */
int sc_burnout; /* current sc_burner delay (ms) */
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_match(struct device *, void *, void *);
void wsdisplay_attach(struct device *, struct device *, void *);
int wsdisplay_detach(struct device *, int);
int wsdisplay_activate(struct device *, int);
void wsdisplay_emulbell_task(void *);
struct cfdriver wsdisplay_cd = {
NULL, "wsdisplay", DV_TTY
};
const struct cfattach wsdisplay_ca = {
sizeof(struct wsdisplay_softc), wsdisplay_match,
wsdisplay_attach, wsdisplay_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_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
};
/*
* Function pointers for wsconsctl parameter handling.
* These are used for firmware-provided display brightness control.
*/
int (*ws_get_param)(struct wsdisplay_param *);
int (*ws_set_param)(struct wsdisplay_param *);
#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,
uint32_t 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;
}
task_set(&scr->scr_emulbell_task, wsdisplay_emulbell_task, scr);
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);
taskq_del_barrier(scr->sc->sc_taskq, &scr->scr_emulbell_task);
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;
uint32_t 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);
strlcpy(sd->screentype, scr->scr_dconf->scrdata->name,
WSSCREEN_NAME_SIZE);
strlcpy(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_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 (cf->wsemuldisplaydevcf_primary != WSEMULDISPLAYDEVCF_PRIMARY_UNK) {
/*
* If primary-ness of device specified, either match
* exactly (at high priority), or fail.
*/
if (cf->wsemuldisplaydevcf_primary != 0 && ap->primary != 0)
return (10);
else
return (0);
}
/* If console-ness and primary-ness unspecified, it wins. */
return (1);
}
int
wsdisplay_activate(struct device *self, int act)
{
int ret = 0;
switch (act) {
case DVACT_POWERDOWN:
wsdisplay_switchtoconsole();
break;
}
return (ret);
}
/*
* Detach a display.
*/
int
wsdisplay_detach(struct device *self, int flags)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)self;
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
taskq_destroy(sc->sc_taskq);
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_attach(struct device *parent, struct device *self, void *aux)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)self;
struct wsemuldisplaydev_attach_args *ap = aux;
u_int defaultscreens = ap->defaultscreens;
int i, start = 0;
#if NWSKBD > 0
struct wsevsrc *kme;
#if NWSMUX > 0
int kbdmux = sc->sc_dv.dv_cfdata->wsemuldisplaydevcf_mux;
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 = ap->console;
sc->sc_resumescreen = WSDISPLAY_NULLSCREEN;
sc->sc_taskq = taskq_create(sc->sc_dv.dv_xname, 1, IPL_TTY, 0);
if (ap->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 = ap->accessops;
sc->sc_accesscookie = ap->accesscookie;
sc->sc_scrdata = ap->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 = WSDISPLAY_DEFBURNOUT_MSEC;
sc->sc_burninintvl = WSDISPLAY_DEFBURNIN_MSEC;
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 (ap->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
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));
}
}
void
wsdisplay_cnattach(const struct wsscreen_descr *type, void *cookie, int ccol,
int crow, uint32_t 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;
#ifdef DDB
db_resize(type->ncols, type->nrows);
#endif
}
/*
* 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)
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)
explicit_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)) {
switch (cmd) {
case WSDISPLAYIO_GTYPE:
case WSDISPLAYIO_GETSCREENTYPE:
/* pass to the first screen */
dev = makedev(major(dev), WSDISPLAYMINOR(unit, 0));
break;
default:
return (wsdisplay_cfg_ioctl(sc, cmd, data, flag, p));
}
}
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 wsdisplay_driver_ioctl(sc, 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;
d->off = sc->sc_burnoutintvl;
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 (d->off==0 || (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 = d->on;
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);
}
}
sc->sc_burnoutintvl = d->off;
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 < 0 || d->idx >= sc->sc_scrdata->nscreens)
return(EINVAL);
d->nidx = sc->sc_scrdata->nscreens;
strlcpy(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);
strlcpy(d->name, wsemul_getname(d->idx), WSEMUL_NAME_SIZE);
return (0);
#undef d
}
/* check ioctls for display */
return wsdisplay_driver_ioctl(sc, cmd, data, flag, p);
}
int
wsdisplay_driver_ioctl(struct wsdisplay_softc *sc, u_long cmd, caddr_t data,
int flag, struct proc *p)
{
int error;
error = ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data,
flag, p));
/* Do not report parameters with empty ranges to userland. */
if (error == 0 && cmd == WSDISPLAYIO_GETPARAM) {
struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
switch (dp->param) {
case WSDISPLAYIO_PARAM_BACKLIGHT:
case WSDISPLAYIO_PARAM_BRIGHTNESS:
case WSDISPLAYIO_PARAM_CONTRAST:
if (dp->min == dp->max)
error = ENOTTY;
break;
}
}
return error;
}
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);
if (d->fontheight > 64 || d->stride > 8) /* 64x64 pixels */
return (EINVAL);
if (d->numchars > 65536) /* unicode plane */
return (EINVAL);
/*
* Some mapchar emulops, such as rasops_mapchar, require the
* availability of the question mark character to use for
* missing glyphs, so make sure it exists.
*/
if (d->firstchar > '?' || d->firstchar + d->numchars <= '?')
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
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)
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;
task_add(scr->sc->sc_taskq, &scr->scr_emulbell_task);
}
void
wsdisplay_emulbell_task(void *v)
{
struct wsscreen *scr = v;
(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();
if (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN && !waitok) {
splx(s);
return (EBUSY);
}
while (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN && res == 0)
res = tsleep_nsec(&sc->sc_resumescreen, PCATCH, "wsrestore",
INFSLP);
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_nsec(sc, PCATCH, "wswait", INFSLP);
}
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_nsec(scr, PCATCH, "wswait2", INFSLP);
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);
}
}
/*
* Switch the console display to its ddb screen, avoiding locking
* where we can.
*/
void
wsdisplay_enter_ddb(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;
if (sc->sc_accessops->enter_ddb) {
(*sc->sc_accessops->enter_ddb)(sc->sc_accesscookie,
scr->scr_dconf->emulcookie);
} else {
(*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
*/
retry:
idx = WSDISPLAY_MAXSCREEN;
if (scr->scr_flags & SCR_GRAPHICS) {
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_msec(&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_msec(&sc->sc_burner, sc->sc_burnout);
} else
sc->sc_burnout = sc->sc_burninintvl;
sc->sc_burnman = !sc->sc_burnman;
splx(s);
}
}
#endif
int
wsdisplay_get_param(struct wsdisplay_softc *sc, struct wsdisplay_param *dp)
{
int error = ENXIO;
int i;
if (sc != NULL)
return wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_GETPARAM, dp);
for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) {
sc = wsdisplay_cd.cd_devs[i];
if (sc == NULL)
continue;
error = wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_GETPARAM, dp);
if (error == 0)
break;
}
if (error && ws_get_param)
error = ws_get_param(dp);
return error;
}
int
wsdisplay_set_param(struct wsdisplay_softc *sc, struct wsdisplay_param *dp)
{
int error = ENXIO;
int i;
if (sc != NULL)
return wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_SETPARAM, dp);
for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) {
sc = wsdisplay_cd.cd_devs[i];
if (sc == NULL)
continue;
error = wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_SETPARAM, dp);
if (error == 0)
break;
}
if (error && ws_set_param)
error = ws_set_param(dp);
return error;
}
void
wsdisplay_brightness_step(struct device *dev, int dir)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsdisplay_param dp;
int delta, new;
dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
if (wsdisplay_get_param(sc, &dp))
return;
/* Use a step size of approximately 5%. */
delta = max(1, ((dp.max - dp.min) * 5) / 100);
new = dp.curval;
if (dir > 0) {
if (delta > dp.max - dp.curval)
new = dp.max;
else
new += delta;
} else if (dir < 0) {
if (delta > dp.curval - dp.min)
new = dp.min;
else
new -= delta;
}
if (dp.curval == new)
return;
dp.curval = new;
wsdisplay_set_param(sc, &dp);
}
void
wsdisplay_brightness_zero(struct device *dev)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsdisplay_param dp;
dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
if (wsdisplay_get_param(sc, &dp))
return;
dp.curval = dp.min;
wsdisplay_set_param(sc, &dp);
}
void
wsdisplay_brightness_cycle(struct device *dev)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsdisplay_param dp;
dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
if (wsdisplay_get_param(sc, &dp))
return;
if (dp.curval == dp.max)
wsdisplay_brightness_zero(dev);
else
wsdisplay_brightness_step(dev, 1);
}
#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;
uint32_t 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->pack_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, sc->sc_copybuffer_size);
}
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 */