1710 lines
43 KiB
C
1710 lines
43 KiB
C
/* $OpenBSD: wsmouse.c,v 1.73 2024/06/26 01:40:49 jsg Exp $ */
|
|
/* $NetBSD: wsmouse.c,v 1.35 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.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1992, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This software was developed by the Computer Systems Engineering group
|
|
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
|
|
* contributed to Berkeley.
|
|
*
|
|
* All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Lawrence Berkeley Laboratory.
|
|
*
|
|
* 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. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
|
|
*
|
|
* @(#)ms.c 8.1 (Berkeley) 6/11/93
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2015, 2016 Ulf Brosziewski
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* Mouse driver.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/device.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/malloc.h>
|
|
|
|
#include <dev/wscons/wscons_features.h>
|
|
#include <dev/wscons/wsconsio.h>
|
|
#include <dev/wscons/wsmousevar.h>
|
|
#include <dev/wscons/wseventvar.h>
|
|
#include <dev/wscons/wsmouseinput.h>
|
|
|
|
#include "wsmux.h"
|
|
#include "wsdisplay.h"
|
|
#include "wskbd.h"
|
|
|
|
#include <dev/wscons/wsmuxvar.h>
|
|
|
|
#if defined(WSMUX_DEBUG) && NWSMUX > 0
|
|
#define DPRINTF(x) if (wsmuxdebug) printf x
|
|
extern int wsmuxdebug;
|
|
#else
|
|
#define DPRINTF(x)
|
|
#endif
|
|
|
|
struct wsmouse_softc {
|
|
struct wsevsrc sc_base;
|
|
|
|
const struct wsmouse_accessops *sc_accessops;
|
|
void *sc_accesscookie;
|
|
|
|
struct wsmouseinput sc_input;
|
|
|
|
int sc_refcnt;
|
|
u_char sc_dying; /* device is being detached */
|
|
};
|
|
|
|
int wsmouse_match(struct device *, void *, void *);
|
|
void wsmouse_attach(struct device *, struct device *, void *);
|
|
int wsmouse_detach(struct device *, int);
|
|
int wsmouse_activate(struct device *, int);
|
|
|
|
int wsmouse_do_ioctl(struct wsmouse_softc *, u_long, caddr_t,
|
|
int, struct proc *);
|
|
|
|
#if NWSMUX > 0
|
|
int wsmouse_mux_open(struct wsevsrc *, struct wseventvar *);
|
|
int wsmouse_mux_close(struct wsevsrc *);
|
|
#endif
|
|
|
|
int wsmousedoioctl(struct device *, u_long, caddr_t, int,
|
|
struct proc *);
|
|
int wsmousedoopen(struct wsmouse_softc *, struct wseventvar *);
|
|
|
|
struct cfdriver wsmouse_cd = {
|
|
NULL, "wsmouse", DV_TTY
|
|
};
|
|
|
|
const struct cfattach wsmouse_ca = {
|
|
sizeof (struct wsmouse_softc), wsmouse_match, wsmouse_attach,
|
|
wsmouse_detach, wsmouse_activate
|
|
};
|
|
|
|
#if NWSMUX > 0
|
|
struct wssrcops wsmouse_srcops = {
|
|
.type = WSMUX_MOUSE,
|
|
.dopen = wsmouse_mux_open,
|
|
.dclose = wsmouse_mux_close,
|
|
.dioctl = wsmousedoioctl,
|
|
.ddispioctl = NULL,
|
|
.dsetdisplay = NULL,
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
* Print function (for parent devices).
|
|
*/
|
|
int
|
|
wsmousedevprint(void *aux, const char *pnp)
|
|
{
|
|
|
|
if (pnp)
|
|
printf("wsmouse at %s", pnp);
|
|
return (UNCONF);
|
|
}
|
|
|
|
int
|
|
wsmouse_match(struct device *parent, void *match, void *aux)
|
|
{
|
|
return (1);
|
|
}
|
|
|
|
void
|
|
wsmouse_attach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
|
|
struct wsmousedev_attach_args *ap = aux;
|
|
#if NWSMUX > 0
|
|
int mux, error;
|
|
#endif
|
|
|
|
sc->sc_accessops = ap->accessops;
|
|
sc->sc_accesscookie = ap->accesscookie;
|
|
|
|
sc->sc_input.evar = &sc->sc_base.me_evp;
|
|
|
|
#if NWSMUX > 0
|
|
sc->sc_base.me_ops = &wsmouse_srcops;
|
|
mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux;
|
|
if (mux >= 0) {
|
|
error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
|
|
if (error)
|
|
printf(" attach error=%d", error);
|
|
else
|
|
printf(" mux %d", mux);
|
|
}
|
|
#else
|
|
#if 0 /* not worth keeping, especially since the default value is not -1... */
|
|
if (sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux >= 0)
|
|
printf(" (mux ignored)");
|
|
#endif
|
|
#endif /* NWSMUX > 0 */
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
int
|
|
wsmouse_activate(struct device *self, int act)
|
|
{
|
|
struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
|
|
|
|
if (act == DVACT_DEACTIVATE)
|
|
sc->sc_dying = 1;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Detach a mouse. To keep track of users of the softc we keep
|
|
* a reference count that's incremented while inside, e.g., read.
|
|
* If the mouse is active and the reference count is > 0 (0 is the
|
|
* normal state) we post an event and then wait for the process
|
|
* that had the reference to wake us up again. Then we blow away the
|
|
* vnode and return (which will deallocate the softc).
|
|
*/
|
|
int
|
|
wsmouse_detach(struct device *self, int flags)
|
|
{
|
|
struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
|
|
struct wseventvar *evar;
|
|
int maj, mn;
|
|
int s;
|
|
|
|
#if NWSMUX > 0
|
|
/* Tell parent mux we're leaving. */
|
|
if (sc->sc_base.me_parent != NULL) {
|
|
DPRINTF(("%s\n", __func__));
|
|
wsmux_detach_sc(&sc->sc_base);
|
|
}
|
|
#endif
|
|
|
|
/* If we're open ... */
|
|
evar = sc->sc_base.me_evp;
|
|
if (evar != NULL) {
|
|
s = spltty();
|
|
if (--sc->sc_refcnt >= 0) {
|
|
/* Wake everyone by generating a dummy event. */
|
|
if (++evar->ws_put >= WSEVENT_QSIZE)
|
|
evar->ws_put = 0;
|
|
WSEVENT_WAKEUP(evar);
|
|
/* Wait for processes to go away. */
|
|
if (tsleep_nsec(sc, PZERO, "wsmdet", SEC_TO_NSEC(60)))
|
|
printf("wsmouse_detach: %s didn't detach\n",
|
|
sc->sc_base.me_dv.dv_xname);
|
|
}
|
|
splx(s);
|
|
}
|
|
|
|
/* locate the major number */
|
|
for (maj = 0; maj < nchrdev; maj++)
|
|
if (cdevsw[maj].d_open == wsmouseopen)
|
|
break;
|
|
|
|
/* Nuke the vnodes for any open instances (calls close). */
|
|
mn = self->dv_unit;
|
|
vdevgone(maj, mn, mn, VCHR);
|
|
|
|
wsmouse_input_cleanup(&sc->sc_input);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
wsmouseopen(dev_t dev, int flags, int mode, struct proc *p)
|
|
{
|
|
struct wsmouse_softc *sc;
|
|
struct wseventvar *evar;
|
|
int error, unit;
|
|
|
|
unit = minor(dev);
|
|
if (unit >= wsmouse_cd.cd_ndevs || /* make sure it was attached */
|
|
(sc = wsmouse_cd.cd_devs[unit]) == NULL)
|
|
return (ENXIO);
|
|
|
|
#if NWSMUX > 0
|
|
DPRINTF(("%s: %s mux=%p\n", __func__, sc->sc_base.me_dv.dv_xname,
|
|
sc->sc_base.me_parent));
|
|
#endif
|
|
|
|
if (sc->sc_dying)
|
|
return (EIO);
|
|
|
|
if ((flags & (FREAD | FWRITE)) == FWRITE)
|
|
return (0); /* always allow open for write
|
|
so ioctl() is possible. */
|
|
|
|
#if NWSMUX > 0
|
|
if (sc->sc_base.me_parent != NULL) {
|
|
/* Grab the mouse out of the greedy hands of the mux. */
|
|
DPRINTF(("%s: detach\n", __func__));
|
|
wsmux_detach_sc(&sc->sc_base);
|
|
}
|
|
#endif
|
|
|
|
if (sc->sc_base.me_evp != NULL)
|
|
return (EBUSY);
|
|
|
|
evar = &sc->sc_base.me_evar;
|
|
if (wsevent_init(evar))
|
|
return (EBUSY);
|
|
|
|
error = wsmousedoopen(sc, evar);
|
|
if (error)
|
|
wsevent_fini(evar);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
wsmouseclose(dev_t dev, int flags, int mode, struct proc *p)
|
|
{
|
|
struct wsmouse_softc *sc =
|
|
(struct wsmouse_softc *)wsmouse_cd.cd_devs[minor(dev)];
|
|
struct wseventvar *evar = sc->sc_base.me_evp;
|
|
|
|
if ((flags & (FREAD | FWRITE)) == FWRITE)
|
|
/* Not open for read */
|
|
return (0);
|
|
|
|
sc->sc_base.me_evp = NULL;
|
|
(*sc->sc_accessops->disable)(sc->sc_accesscookie);
|
|
wsevent_fini(evar);
|
|
|
|
#if NWSMUX > 0
|
|
if (sc->sc_base.me_parent == NULL) {
|
|
int mux, error;
|
|
|
|
DPRINTF(("%s: attach\n", __func__));
|
|
mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux;
|
|
if (mux >= 0) {
|
|
error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
|
|
if (error)
|
|
printf("%s: can't attach mux (error=%d)\n",
|
|
sc->sc_base.me_dv.dv_xname, error);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
wsmousedoopen(struct wsmouse_softc *sc, struct wseventvar *evp)
|
|
{
|
|
int error;
|
|
|
|
/* The device could already be attached to a mux. */
|
|
if (sc->sc_base.me_evp != NULL)
|
|
return (EBUSY);
|
|
sc->sc_base.me_evp = evp;
|
|
|
|
wsmouse_input_reset(&sc->sc_input);
|
|
|
|
/* enable the device, and punt if that's not possible */
|
|
error = (*sc->sc_accessops->enable)(sc->sc_accesscookie);
|
|
if (error)
|
|
sc->sc_base.me_evp = NULL;
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
wsmouseread(dev_t dev, struct uio *uio, int flags)
|
|
{
|
|
struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
|
|
int error;
|
|
|
|
if (sc->sc_dying)
|
|
return (EIO);
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (sc->sc_base.me_evp == NULL) {
|
|
printf("wsmouseread: evp == NULL\n");
|
|
return (EINVAL);
|
|
}
|
|
#endif
|
|
|
|
sc->sc_refcnt++;
|
|
error = wsevent_read(sc->sc_base.me_evp, uio, flags);
|
|
if (--sc->sc_refcnt < 0) {
|
|
wakeup(sc);
|
|
error = EIO;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
wsmouseioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
|
|
{
|
|
return (wsmousedoioctl(wsmouse_cd.cd_devs[minor(dev)],
|
|
cmd, data, flag, p));
|
|
}
|
|
|
|
/* A wrapper around the ioctl() workhorse to make reference counting easy. */
|
|
int
|
|
wsmousedoioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
|
|
struct proc *p)
|
|
{
|
|
struct wsmouse_softc *sc = (struct wsmouse_softc *)dv;
|
|
int error;
|
|
|
|
sc->sc_refcnt++;
|
|
error = wsmouse_do_ioctl(sc, cmd, data, flag, p);
|
|
if (--sc->sc_refcnt < 0)
|
|
wakeup(sc);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
wsmouse_param_ioctl(struct wsmouse_softc *sc,
|
|
u_long cmd, struct wsmouse_param *params, u_int nparams)
|
|
{
|
|
struct wsmouse_param *buf;
|
|
int error, s, size;
|
|
|
|
if (params == NULL || nparams > WSMOUSECFG_MAX)
|
|
return (EINVAL);
|
|
|
|
size = nparams * sizeof(struct wsmouse_param);
|
|
buf = malloc(size, M_DEVBUF, M_WAITOK);
|
|
if (buf == NULL)
|
|
return (ENOMEM);
|
|
|
|
if ((error = copyin(params, buf, size))) {
|
|
free(buf, M_DEVBUF, size);
|
|
return (error);
|
|
}
|
|
|
|
s = spltty();
|
|
if (cmd == WSMOUSEIO_SETPARAMS) {
|
|
if (wsmouse_set_params((struct device *) sc, buf, nparams))
|
|
error = EINVAL;
|
|
} else {
|
|
if (wsmouse_get_params((struct device *) sc, buf, nparams))
|
|
error = EINVAL;
|
|
else
|
|
error = copyout(buf, params, size);
|
|
}
|
|
splx(s);
|
|
free(buf, M_DEVBUF, size);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
wsmouse_do_ioctl(struct wsmouse_softc *sc, u_long cmd, caddr_t data, int flag,
|
|
struct proc *p)
|
|
{
|
|
struct wseventvar *evar;
|
|
int error;
|
|
|
|
if (sc->sc_dying)
|
|
return (EIO);
|
|
|
|
/*
|
|
* Try the generic ioctls that the wsmouse interface supports.
|
|
*/
|
|
|
|
switch (cmd) {
|
|
case FIOASYNC:
|
|
case FIOSETOWN:
|
|
case TIOCSPGRP:
|
|
if ((flag & FWRITE) == 0)
|
|
return (EACCES);
|
|
}
|
|
|
|
switch (cmd) {
|
|
case FIONBIO: /* we will remove this someday (soon???) */
|
|
return (0);
|
|
|
|
case FIOASYNC:
|
|
if (sc->sc_base.me_evp == NULL)
|
|
return (EINVAL);
|
|
sc->sc_base.me_evp->ws_async = *(int *)data != 0;
|
|
return (0);
|
|
|
|
case FIOGETOWN:
|
|
case TIOCGPGRP:
|
|
evar = sc->sc_base.me_evp;
|
|
if (evar == NULL)
|
|
return (EINVAL);
|
|
sigio_getown(&evar->ws_sigio, cmd, data);
|
|
return (0);
|
|
|
|
case FIOSETOWN:
|
|
case TIOCSPGRP:
|
|
evar = sc->sc_base.me_evp;
|
|
if (evar == NULL)
|
|
return (EINVAL);
|
|
return (sigio_setown(&evar->ws_sigio, cmd, data));
|
|
|
|
case WSMOUSEIO_GETPARAMS:
|
|
case WSMOUSEIO_SETPARAMS:
|
|
return (wsmouse_param_ioctl(sc, cmd,
|
|
((struct wsmouse_parameters *) data)->params,
|
|
((struct wsmouse_parameters *) data)->nparams));
|
|
}
|
|
|
|
/*
|
|
* Try the mouse driver for WSMOUSEIO ioctls. It returns -1
|
|
* if it didn't recognize the request.
|
|
*/
|
|
error = (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd,
|
|
data, flag, p);
|
|
return (error != -1 ? error : ENOTTY);
|
|
}
|
|
|
|
int
|
|
wsmousekqfilter(dev_t dev, struct knote *kn)
|
|
{
|
|
struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
|
|
|
|
if (sc->sc_base.me_evp == NULL)
|
|
return (ENXIO);
|
|
return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
|
|
}
|
|
|
|
#if NWSMUX > 0
|
|
int
|
|
wsmouse_mux_open(struct wsevsrc *me, struct wseventvar *evp)
|
|
{
|
|
struct wsmouse_softc *sc = (struct wsmouse_softc *)me;
|
|
|
|
return (wsmousedoopen(sc, evp));
|
|
}
|
|
|
|
int
|
|
wsmouse_mux_close(struct wsevsrc *me)
|
|
{
|
|
struct wsmouse_softc *sc = (struct wsmouse_softc *)me;
|
|
|
|
(*sc->sc_accessops->disable)(sc->sc_accesscookie);
|
|
sc->sc_base.me_evp = NULL;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
wsmouse_add_mux(int unit, struct wsmux_softc *muxsc)
|
|
{
|
|
struct wsmouse_softc *sc;
|
|
|
|
if (unit < 0 || unit >= wsmouse_cd.cd_ndevs ||
|
|
(sc = wsmouse_cd.cd_devs[unit]) == NULL)
|
|
return (ENXIO);
|
|
|
|
if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
|
|
return (EBUSY);
|
|
|
|
return (wsmux_attach_sc(muxsc, &sc->sc_base));
|
|
}
|
|
#endif /* NWSMUX > 0 */
|
|
|
|
void
|
|
wsmouse_buttons(struct device *sc, u_int buttons)
|
|
{
|
|
struct btn_state *btn = &((struct wsmouse_softc *) sc)->sc_input.btn;
|
|
|
|
if (btn->sync)
|
|
/* Restore the old state. */
|
|
btn->buttons ^= btn->sync;
|
|
|
|
btn->sync = btn->buttons ^ buttons;
|
|
btn->buttons = buttons;
|
|
}
|
|
|
|
void
|
|
wsmouse_motion(struct device *sc, int dx, int dy, int dz, int dw)
|
|
{
|
|
struct motion_state *motion =
|
|
&((struct wsmouse_softc *) sc)->sc_input.motion;
|
|
|
|
motion->dx = dx;
|
|
motion->dy = dy;
|
|
motion->dz = dz;
|
|
motion->dw = dw;
|
|
if (dx || dy || dz || dw)
|
|
motion->sync |= SYNC_DELTAS;
|
|
}
|
|
|
|
static inline void
|
|
set_x(struct position *pos, int x, u_int *sync, u_int mask)
|
|
{
|
|
if (*sync & mask) {
|
|
if (x == pos->x)
|
|
return;
|
|
pos->x -= pos->dx;
|
|
pos->acc_dx -= pos->dx;
|
|
}
|
|
if ((pos->dx = x - pos->x)) {
|
|
pos->x = x;
|
|
if ((pos->dx > 0) == (pos->acc_dx > 0))
|
|
pos->acc_dx += pos->dx;
|
|
else
|
|
pos->acc_dx = pos->dx;
|
|
*sync |= mask;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
set_y(struct position *pos, int y, u_int *sync, u_int mask)
|
|
{
|
|
if (*sync & mask) {
|
|
if (y == pos->y)
|
|
return;
|
|
pos->y -= pos->dy;
|
|
pos->acc_dy -= pos->dy;
|
|
}
|
|
if ((pos->dy = y - pos->y)) {
|
|
pos->y = y;
|
|
if ((pos->dy > 0) == (pos->acc_dy > 0))
|
|
pos->acc_dy += pos->dy;
|
|
else
|
|
pos->acc_dy = pos->dy;
|
|
*sync |= mask;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
cleardeltas(struct position *pos)
|
|
{
|
|
pos->dx = pos->acc_dx = 0;
|
|
pos->dy = pos->acc_dy = 0;
|
|
}
|
|
|
|
void
|
|
wsmouse_position(struct device *sc, int x, int y)
|
|
{
|
|
struct motion_state *motion =
|
|
&((struct wsmouse_softc *) sc)->sc_input.motion;
|
|
|
|
set_x(&motion->pos, x, &motion->sync, SYNC_X);
|
|
set_y(&motion->pos, y, &motion->sync, SYNC_Y);
|
|
}
|
|
|
|
static inline int
|
|
normalized_pressure(struct wsmouseinput *input, int pressure)
|
|
{
|
|
int limit = imax(input->touch.min_pressure, 1);
|
|
|
|
if (pressure >= limit)
|
|
return pressure;
|
|
else
|
|
return (pressure < 0 ? limit : 0);
|
|
}
|
|
|
|
void
|
|
wsmouse_touch(struct device *sc, int pressure, int contacts)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
struct touch_state *touch = &input->touch;
|
|
|
|
pressure = normalized_pressure(input, pressure);
|
|
contacts = (pressure ? imax(contacts, 1) : 0);
|
|
|
|
if (pressure == 0 || pressure != touch->pressure) {
|
|
/*
|
|
* pressure == 0: Drivers may report possibly arbitrary
|
|
* coordinates in this case; touch_update will correct them.
|
|
*/
|
|
touch->pressure = pressure;
|
|
touch->sync |= SYNC_PRESSURE;
|
|
}
|
|
if (contacts != touch->contacts) {
|
|
touch->contacts = contacts;
|
|
touch->sync |= SYNC_CONTACTS;
|
|
}
|
|
}
|
|
|
|
void
|
|
wsmouse_mtstate(struct device *sc, int slot, int x, int y, int pressure)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
struct mt_state *mt = &input->mt;
|
|
struct mt_slot *mts;
|
|
u_int bit;
|
|
|
|
if (slot < 0 || slot >= mt->num_slots)
|
|
return;
|
|
|
|
bit = (1 << slot);
|
|
mt->frame |= bit;
|
|
|
|
mts = &mt->slots[slot];
|
|
|
|
set_x(&mts->pos, x, mt->sync + MTS_X, bit);
|
|
set_y(&mts->pos, y, mt->sync + MTS_Y, bit);
|
|
|
|
/* Is this a new touch? */
|
|
if ((mt->touches & bit) == (mt->sync[MTS_TOUCH] & bit))
|
|
cleardeltas(&mts->pos);
|
|
|
|
pressure = normalized_pressure(input, pressure);
|
|
if (pressure != mts->pressure) {
|
|
mts->pressure = pressure;
|
|
mt->sync[MTS_PRESSURE] |= bit;
|
|
|
|
if (pressure) {
|
|
if ((mt->touches & bit) == 0) {
|
|
mt->num_touches++;
|
|
mt->touches |= bit;
|
|
mt->sync[MTS_TOUCH] |= bit;
|
|
|
|
mt->sync[MTS_X] |= bit;
|
|
mt->sync[MTS_Y] |= bit;
|
|
}
|
|
} else if (mt->touches & bit) {
|
|
mt->num_touches--;
|
|
mt->touches ^= bit;
|
|
mt->sync[MTS_TOUCH] |= bit;
|
|
mt->ptr_mask &= mt->touches;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
wsmouse_set(struct device *sc, enum wsmouseval type, int value, int aux)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
struct mt_slot *mts;
|
|
|
|
if (WSMOUSE_IS_MT_CODE(type)) {
|
|
if (aux < 0 || aux >= input->mt.num_slots)
|
|
return;
|
|
mts = &input->mt.slots[aux];
|
|
}
|
|
|
|
switch (type) {
|
|
case WSMOUSE_REL_X:
|
|
value += input->motion.pos.x; /* fall through */
|
|
case WSMOUSE_ABS_X:
|
|
wsmouse_position(sc, value, input->motion.pos.y);
|
|
return;
|
|
case WSMOUSE_REL_Y:
|
|
value += input->motion.pos.y; /* fall through */
|
|
case WSMOUSE_ABS_Y:
|
|
wsmouse_position(sc, input->motion.pos.x, value);
|
|
return;
|
|
case WSMOUSE_PRESSURE:
|
|
wsmouse_touch(sc, value, input->touch.contacts);
|
|
return;
|
|
case WSMOUSE_CONTACTS:
|
|
/* Contact counts can be overridden by wsmouse_touch. */
|
|
if (value != input->touch.contacts) {
|
|
input->touch.contacts = value;
|
|
input->touch.sync |= SYNC_CONTACTS;
|
|
}
|
|
return;
|
|
case WSMOUSE_TOUCH_WIDTH:
|
|
if (value != input->touch.width) {
|
|
input->touch.width = value;
|
|
input->touch.sync |= SYNC_TOUCH_WIDTH;
|
|
}
|
|
return;
|
|
case WSMOUSE_MT_REL_X:
|
|
value += mts->pos.x; /* fall through */
|
|
case WSMOUSE_MT_ABS_X:
|
|
wsmouse_mtstate(sc, aux, value, mts->pos.y, mts->pressure);
|
|
return;
|
|
case WSMOUSE_MT_REL_Y:
|
|
value += mts->pos.y; /* fall through */
|
|
case WSMOUSE_MT_ABS_Y:
|
|
wsmouse_mtstate(sc, aux, mts->pos.x, value, mts->pressure);
|
|
return;
|
|
case WSMOUSE_MT_PRESSURE:
|
|
wsmouse_mtstate(sc, aux, mts->pos.x, mts->pos.y, value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Make touch and motion state consistent. */
|
|
void
|
|
wsmouse_touch_update(struct wsmouseinput *input)
|
|
{
|
|
struct motion_state *motion = &input->motion;
|
|
struct touch_state *touch = &input->touch;
|
|
|
|
if (touch->pressure == 0) {
|
|
/*
|
|
* There may be zero coordinates, or coordinates of
|
|
* touches with pressure values below min_pressure.
|
|
*/
|
|
if (motion->sync & SYNC_POSITION) {
|
|
/* Restore valid coordinates. */
|
|
motion->pos.x -= motion->pos.dx;
|
|
motion->pos.y -= motion->pos.dy;
|
|
motion->sync &= ~SYNC_POSITION;
|
|
}
|
|
|
|
if (touch->prev_contacts == 0)
|
|
touch->sync &= ~SYNC_PRESSURE;
|
|
|
|
}
|
|
|
|
if (touch->sync & SYNC_CONTACTS)
|
|
/* Suppress pointer movement. */
|
|
cleardeltas(&motion->pos);
|
|
|
|
if ((touch->sync & SYNC_PRESSURE) && touch->min_pressure) {
|
|
if (touch->pressure >= input->filter.pressure_hi)
|
|
touch->min_pressure = input->filter.pressure_lo;
|
|
else if (touch->pressure < input->filter.pressure_lo)
|
|
touch->min_pressure = input->filter.pressure_hi;
|
|
}
|
|
}
|
|
|
|
/* Normalize multitouch state. */
|
|
void
|
|
wsmouse_mt_update(struct wsmouseinput *input)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* The same as above: There may be arbitrary coordinates if
|
|
* (pressure == 0). Clear the sync flags for touches that have
|
|
* been released.
|
|
*/
|
|
if (input->mt.frame & ~input->mt.touches) {
|
|
for (i = MTS_X; i < MTS_SIZE; i++)
|
|
input->mt.sync[i] &= input->mt.touches;
|
|
}
|
|
}
|
|
|
|
/* Return TRUE if a coordinate update may be noise. */
|
|
int
|
|
wsmouse_hysteresis(struct wsmouseinput *input, struct position *pos)
|
|
{
|
|
return (abs(pos->acc_dx) < input->filter.h.hysteresis
|
|
&& abs(pos->acc_dy) < input->filter.v.hysteresis);
|
|
}
|
|
|
|
/*
|
|
* Select the pointer-controlling MT slot.
|
|
*
|
|
* Pointer-control is assigned to slots with non-zero motion deltas if
|
|
* at least one such slot exists. This function doesn't impose any
|
|
* restrictions on the way drivers use wsmouse_mtstate(), it covers
|
|
* partial, unordered, and "delta-filtered" input.
|
|
*
|
|
* The "cycle" is the set of slots with X/Y updates in previous sync
|
|
* operations; it will be cleared and rebuilt whenever a slot that is
|
|
* being updated is already a member. If a cycle ends that doesn't
|
|
* contain the pointer-controlling slot, a new slot will be selected.
|
|
*/
|
|
void
|
|
wsmouse_ptr_ctrl(struct wsmouseinput *input)
|
|
{
|
|
struct mt_state *mt = &input->mt;
|
|
u_int updates;
|
|
int select, slot;
|
|
|
|
mt->prev_ptr = mt->ptr;
|
|
|
|
if (mt->num_touches <= 1) {
|
|
mt->ptr = mt->touches;
|
|
mt->ptr_cycle = mt->ptr;
|
|
return;
|
|
}
|
|
|
|
updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH];
|
|
FOREACHBIT(updates, slot) {
|
|
/*
|
|
* Touches that just produce noise are no problem if the
|
|
* frequency of zero deltas is high enough, but there might
|
|
* be no guarantee for that.
|
|
*/
|
|
if (wsmouse_hysteresis(input, &mt->slots[slot].pos))
|
|
updates ^= (1 << slot);
|
|
}
|
|
|
|
/*
|
|
* If there is no pointer-controlling slot, or if it should be
|
|
* masked, select a new one.
|
|
*/
|
|
select = ((mt->ptr & mt->touches & ~mt->ptr_mask) == 0);
|
|
|
|
/* Remove slots without coordinate deltas from the cycle. */
|
|
mt->ptr_cycle &= ~(mt->frame ^ updates);
|
|
|
|
if (mt->ptr_cycle & updates) {
|
|
select |= ((mt->ptr_cycle & mt->ptr) == 0);
|
|
mt->ptr_cycle = updates;
|
|
} else {
|
|
mt->ptr_cycle |= updates;
|
|
}
|
|
if (select) {
|
|
if (mt->ptr_cycle & ~mt->ptr_mask)
|
|
slot = ffs(mt->ptr_cycle & ~mt->ptr_mask) - 1;
|
|
else if (mt->touches & ~mt->ptr_mask)
|
|
slot = ffs(mt->touches & ~mt->ptr_mask) - 1;
|
|
else
|
|
slot = ffs(mt->touches) - 1;
|
|
mt->ptr = (1 << slot);
|
|
}
|
|
}
|
|
|
|
/* Derive touch and motion state from MT state. */
|
|
void
|
|
wsmouse_mt_convert(struct device *sc)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
struct mt_state *mt = &input->mt;
|
|
struct mt_slot *mts;
|
|
int slot, pressure;
|
|
|
|
wsmouse_ptr_ctrl(input);
|
|
|
|
if (mt->ptr) {
|
|
slot = ffs(mt->ptr) - 1;
|
|
mts = &mt->slots[slot];
|
|
if (mts->pos.x != input->motion.pos.x)
|
|
input->motion.sync |= SYNC_X;
|
|
if (mts->pos.y != input->motion.pos.y)
|
|
input->motion.sync |= SYNC_Y;
|
|
if (mt->ptr != mt->prev_ptr)
|
|
/* Suppress pointer movement. */
|
|
mts->pos.dx = mts->pos.dy = 0;
|
|
memcpy(&input->motion.pos, &mts->pos, sizeof(struct position));
|
|
|
|
pressure = mts->pressure;
|
|
} else {
|
|
pressure = 0;
|
|
}
|
|
|
|
wsmouse_touch(sc, pressure, mt->num_touches);
|
|
}
|
|
|
|
void
|
|
wsmouse_evq_put(struct evq_access *evq, int ev_type, int ev_value)
|
|
{
|
|
struct wscons_event *ev;
|
|
int space;
|
|
|
|
space = evq->evar->ws_get - evq->put;
|
|
if (space != 1 && space != 1 - WSEVENT_QSIZE) {
|
|
ev = &evq->evar->ws_q[evq->put++];
|
|
evq->put %= WSEVENT_QSIZE;
|
|
ev->type = ev_type;
|
|
ev->value = ev_value;
|
|
memcpy(&ev->time, &evq->ts, sizeof(struct timespec));
|
|
evq->result |= EVQ_RESULT_SUCCESS;
|
|
} else {
|
|
evq->result = EVQ_RESULT_OVERFLOW;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
wsmouse_btn_sync(struct btn_state *btn, struct evq_access *evq)
|
|
{
|
|
int button, ev_type;
|
|
u_int bit, sync;
|
|
|
|
for (sync = btn->sync; sync; sync ^= bit) {
|
|
button = ffs(sync) - 1;
|
|
bit = (1 << button);
|
|
ev_type = (btn->buttons & bit) ? BTN_DOWN_EV : BTN_UP_EV;
|
|
wsmouse_evq_put(evq, ev_type, button);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scale with a [*.12] fixed-point factor and a remainder:
|
|
*/
|
|
static inline int
|
|
scale(int val, int factor, int *rmdr)
|
|
{
|
|
val = val * factor + *rmdr;
|
|
if (val >= 0) {
|
|
*rmdr = val & 0xfff;
|
|
return (val >> 12);
|
|
} else {
|
|
*rmdr = -(-val & 0xfff);
|
|
return -(-val >> 12);
|
|
}
|
|
}
|
|
|
|
void
|
|
wsmouse_motion_sync(struct wsmouseinput *input, struct evq_access *evq)
|
|
{
|
|
struct motion_state *motion = &input->motion;
|
|
struct axis_filter *h = &input->filter.h;
|
|
struct axis_filter *v = &input->filter.v;
|
|
int x, y, dx, dy, dz, dw;
|
|
|
|
if (motion->sync & SYNC_DELTAS) {
|
|
dx = h->inv ? -motion->dx : motion->dx;
|
|
dy = v->inv ? -motion->dy : motion->dy;
|
|
if (h->scale)
|
|
dx = scale(dx, h->scale, &h->rmdr);
|
|
if (v->scale)
|
|
dy = scale(dy, v->scale, &v->rmdr);
|
|
if (dx)
|
|
wsmouse_evq_put(evq, DELTA_X_EV(input), dx);
|
|
if (dy)
|
|
wsmouse_evq_put(evq, DELTA_Y_EV(input), dy);
|
|
if (motion->dz) {
|
|
dz = (input->flags & REVERSE_SCROLLING)
|
|
? -motion->dz : motion->dz;
|
|
if (IS_TOUCHPAD(input))
|
|
wsmouse_evq_put(evq, VSCROLL_EV, dz);
|
|
else
|
|
wsmouse_evq_put(evq, DELTA_Z_EV, dz);
|
|
}
|
|
if (motion->dw) {
|
|
dw = (input->flags & REVERSE_SCROLLING)
|
|
? -motion->dw : motion->dw;
|
|
if (IS_TOUCHPAD(input))
|
|
wsmouse_evq_put(evq, HSCROLL_EV, dw);
|
|
else
|
|
wsmouse_evq_put(evq, DELTA_W_EV, dw);
|
|
}
|
|
}
|
|
if (motion->sync & SYNC_POSITION) {
|
|
if (motion->sync & SYNC_X) {
|
|
x = (h->inv ? h->inv - motion->pos.x : motion->pos.x);
|
|
wsmouse_evq_put(evq, ABS_X_EV(input), x);
|
|
}
|
|
if (motion->sync & SYNC_Y) {
|
|
y = (v->inv ? v->inv - motion->pos.y : motion->pos.y);
|
|
wsmouse_evq_put(evq, ABS_Y_EV(input), y);
|
|
}
|
|
if (motion->pos.dx == 0 && motion->pos.dy == 0
|
|
&& (input->flags & TPAD_NATIVE_MODE ))
|
|
/* Suppress pointer motion. */
|
|
wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_RESET, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
wsmouse_touch_sync(struct wsmouseinput *input, struct evq_access *evq)
|
|
{
|
|
struct touch_state *touch = &input->touch;
|
|
|
|
if (touch->sync & SYNC_PRESSURE)
|
|
wsmouse_evq_put(evq, ABS_Z_EV, touch->pressure);
|
|
if (touch->sync & SYNC_CONTACTS)
|
|
wsmouse_evq_put(evq, ABS_W_EV, touch->contacts);
|
|
if ((touch->sync & SYNC_TOUCH_WIDTH)
|
|
&& (input->flags & TPAD_NATIVE_MODE))
|
|
wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_WIDTH, touch->width);
|
|
}
|
|
|
|
void
|
|
wsmouse_log_input(struct wsmouseinput *input, struct timespec *ts)
|
|
{
|
|
struct motion_state *motion = &input->motion;
|
|
int t_sync, mt_sync;
|
|
|
|
t_sync = (input->touch.sync & SYNC_CONTACTS);
|
|
mt_sync = (input->mt.frame && (input->mt.sync[MTS_TOUCH]
|
|
|| input->mt.ptr != input->mt.prev_ptr));
|
|
|
|
if (motion->sync || mt_sync || t_sync || input->btn.sync)
|
|
printf("[%s-in][%04d]", DEVNAME(input), LOGTIME(ts));
|
|
else
|
|
return;
|
|
|
|
if (motion->sync & SYNC_POSITION)
|
|
printf(" abs:%d,%d", motion->pos.x, motion->pos.y);
|
|
if (motion->sync & SYNC_DELTAS)
|
|
printf(" rel:%d,%d,%d,%d", motion->dx, motion->dy,
|
|
motion->dz, motion->dw);
|
|
if (mt_sync)
|
|
printf(" mt:0x%02x:%d", input->mt.touches,
|
|
ffs(input->mt.ptr) - 1);
|
|
else if (t_sync)
|
|
printf(" t:%d", input->touch.contacts);
|
|
if (input->btn.sync)
|
|
printf(" btn:0x%02x", input->btn.buttons);
|
|
printf("\n");
|
|
}
|
|
|
|
void
|
|
wsmouse_log_events(struct wsmouseinput *input, struct evq_access *evq)
|
|
{
|
|
struct wscons_event *ev;
|
|
int n = evq->evar->ws_put;
|
|
|
|
if (n != evq->put) {
|
|
printf("[%s-ev][%04d]", DEVNAME(input), LOGTIME(&evq->ts));
|
|
while (n != evq->put) {
|
|
ev = &evq->evar->ws_q[n++];
|
|
n %= WSEVENT_QSIZE;
|
|
printf(" %d:%d", ev->type, ev->value);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
clear_sync_flags(struct wsmouseinput *input)
|
|
{
|
|
int i;
|
|
|
|
input->btn.sync = 0;
|
|
input->sbtn.sync = 0;
|
|
input->motion.sync = 0;
|
|
input->touch.sync = 0;
|
|
input->touch.prev_contacts = input->touch.contacts;
|
|
if (input->mt.frame) {
|
|
input->mt.frame = 0;
|
|
for (i = 0; i < MTS_SIZE; i++)
|
|
input->mt.sync[i] = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
wsmouse_input_sync(struct device *sc)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
struct evq_access evq;
|
|
|
|
evq.evar = *input->evar;
|
|
if (evq.evar == NULL)
|
|
return;
|
|
evq.put = evq.evar->ws_put;
|
|
evq.result = EVQ_RESULT_NONE;
|
|
getnanotime(&evq.ts);
|
|
|
|
enqueue_randomness(input->btn.buttons
|
|
^ input->motion.dx ^ input->motion.dy
|
|
^ input->motion.pos.x ^ input->motion.pos.y
|
|
^ input->motion.dz ^ input->motion.dw);
|
|
|
|
if (input->mt.frame) {
|
|
wsmouse_mt_update(input);
|
|
wsmouse_mt_convert(sc);
|
|
}
|
|
if (input->touch.sync)
|
|
wsmouse_touch_update(input);
|
|
|
|
if (input->flags & LOG_INPUT)
|
|
wsmouse_log_input(input, &evq.ts);
|
|
|
|
if (input->flags & TPAD_COMPAT_MODE)
|
|
wstpad_compat_convert(input, &evq);
|
|
|
|
if (input->flags & RESYNC) {
|
|
input->flags &= ~RESYNC;
|
|
input->motion.sync &= SYNC_POSITION;
|
|
}
|
|
|
|
if (input->btn.sync)
|
|
wsmouse_btn_sync(&input->btn, &evq);
|
|
if (input->sbtn.sync)
|
|
wsmouse_btn_sync(&input->sbtn, &evq);
|
|
if (input->motion.sync)
|
|
wsmouse_motion_sync(input, &evq);
|
|
if (input->touch.sync)
|
|
wsmouse_touch_sync(input, &evq);
|
|
/* No MT events are generated yet. */
|
|
|
|
if (evq.result == EVQ_RESULT_SUCCESS) {
|
|
wsmouse_evq_put(&evq, WSCONS_EVENT_SYNC, 0);
|
|
if (evq.result == EVQ_RESULT_SUCCESS) {
|
|
if (input->flags & LOG_EVENTS) {
|
|
wsmouse_log_events(input, &evq);
|
|
}
|
|
evq.evar->ws_put = evq.put;
|
|
WSEVENT_WAKEUP(evq.evar);
|
|
}
|
|
}
|
|
|
|
if (evq.result != EVQ_RESULT_OVERFLOW)
|
|
clear_sync_flags(input);
|
|
else
|
|
input->flags |= RESYNC;
|
|
}
|
|
|
|
int
|
|
wsmouse_id_to_slot(struct device *sc, int id)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
struct mt_state *mt = &input->mt;
|
|
int slot;
|
|
|
|
if (mt->num_slots == 0)
|
|
return (-1);
|
|
|
|
FOREACHBIT(mt->touches, slot) {
|
|
if (mt->slots[slot].id == id)
|
|
return slot;
|
|
}
|
|
slot = ffs(~(mt->touches | mt->frame)) - 1;
|
|
if (slot >= 0 && slot < mt->num_slots) {
|
|
mt->frame |= 1 << slot;
|
|
mt->slots[slot].id = id;
|
|
return (slot);
|
|
} else {
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find a minimum-weight matching for an m-by-n matrix.
|
|
*
|
|
* m must be greater than or equal to n. The size of the buffer must be
|
|
* at least 3m + 3n.
|
|
*
|
|
* On return, the first m elements of the buffer contain the row-to-
|
|
* column mappings, i.e., buffer[i] is the column index for row i, or -1
|
|
* if there is no assignment for that row (which may happen if n < m).
|
|
*
|
|
* Wrong results because of overflows will not occur with input values
|
|
* in the range of 0 to INT_MAX / 2 inclusive.
|
|
*
|
|
* The function applies the Dinic-Kronrod algorithm. It is not modern or
|
|
* popular, but it seems to be a good choice for small matrices at least.
|
|
* The original form of the algorithm is modified as follows: There is no
|
|
* initial search for row minima, the initial assignments are in a
|
|
* "virtual" column with the index -1 and zero values. This permits inputs
|
|
* with n < m, and it simplifies the reassignments.
|
|
*/
|
|
void
|
|
wsmouse_matching(int *matrix, int m, int n, int *buffer)
|
|
{
|
|
int i, j, k, d, e, row, col, delta;
|
|
int *p;
|
|
int *r2c = buffer; /* row-to-column assignments */
|
|
int *red = r2c + m; /* reduced values of the assignments */
|
|
int *mc = red + m; /* row-wise minimal elements of cs */
|
|
int *cs = mc + m; /* the column set */
|
|
int *c2r = cs + n; /* column-to-row assignments in cs */
|
|
int *cd = c2r + n; /* column deltas (reduction) */
|
|
|
|
for (p = r2c; p < red; *p++ = -1) {}
|
|
for (; p < mc; *p++ = 0) {}
|
|
for (col = 0; col < n; col++) {
|
|
delta = INT_MAX;
|
|
row = 0;
|
|
for (i = 0, p = matrix + col; i < m; i++, p += n) {
|
|
d = *p - red[i];
|
|
if (d < delta || (d == delta && r2c[i] < 0)) {
|
|
delta = d;
|
|
row = i;
|
|
}
|
|
}
|
|
cd[col] = delta;
|
|
if (r2c[row] < 0) {
|
|
r2c[row] = col;
|
|
continue;
|
|
}
|
|
for (p = mc; p < cs; *p++ = col) {}
|
|
for (k = 0; (j = r2c[row]) >= 0;) {
|
|
cs[k++] = j;
|
|
c2r[j] = row;
|
|
mc[row] -= n;
|
|
delta = INT_MAX;
|
|
for (i = 0, p = matrix; i < m; i++, p += n)
|
|
if (mc[i] >= 0) {
|
|
d = p[mc[i]] - cd[mc[i]];
|
|
e = p[j] - cd[j];
|
|
if (e < d) {
|
|
d = e;
|
|
mc[i] = j;
|
|
}
|
|
d -= red[i];
|
|
if (d < delta || (d == delta
|
|
&& r2c[i] < 0)) {
|
|
delta = d;
|
|
row = i;
|
|
}
|
|
}
|
|
cd[col] += delta;
|
|
for (i = 0; i < k; i++) {
|
|
cd[cs[i]] += delta;
|
|
red[c2r[cs[i]]] -= delta;
|
|
}
|
|
}
|
|
for (j = mc[row]; (r2c[row] = j) != col;) {
|
|
row = c2r[j];
|
|
j = mc[row] + n;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Assign slot numbers to the points in the pt array, and update all slots by
|
|
* calling wsmouse_mtstate internally. The slot numbers are passed to the
|
|
* caller in the pt->slot fields.
|
|
*
|
|
* The slot assignment pairs the points with points of the previous frame in
|
|
* such a way that the sum of the squared distances is minimal. Using
|
|
* squares instead of simple distances favours assignments with more uniform
|
|
* distances, and it is faster.
|
|
*/
|
|
void
|
|
wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int size)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
struct mt_state *mt = &input->mt;
|
|
int i, j, m, n, dx, dy, slot, maxdist;
|
|
int *p, *r2c, *c2r;
|
|
u_int touches;
|
|
|
|
if (mt->num_slots == 0 || mt->matrix == NULL)
|
|
return;
|
|
|
|
size = imax(0, imin(size, mt->num_slots));
|
|
p = mt->matrix;
|
|
touches = mt->touches;
|
|
if (mt->num_touches >= size) {
|
|
FOREACHBIT(touches, slot)
|
|
for (i = 0; i < size; i++) {
|
|
dx = pt[i].x - mt->slots[slot].pos.x;
|
|
dy = pt[i].y - mt->slots[slot].pos.y;
|
|
*p++ = dx * dx + dy * dy;
|
|
}
|
|
m = mt->num_touches;
|
|
n = size;
|
|
} else {
|
|
for (i = 0; i < size; i++)
|
|
FOREACHBIT(touches, slot) {
|
|
dx = pt[i].x - mt->slots[slot].pos.x;
|
|
dy = pt[i].y - mt->slots[slot].pos.y;
|
|
*p++ = dx * dx + dy * dy;
|
|
}
|
|
m = size;
|
|
n = mt->num_touches;
|
|
}
|
|
wsmouse_matching(mt->matrix, m, n, p);
|
|
|
|
r2c = p;
|
|
c2r = p + m;
|
|
maxdist = input->filter.tracking_maxdist;
|
|
maxdist = (maxdist ? maxdist * maxdist : INT_MAX);
|
|
for (i = 0, p = mt->matrix; i < m; i++, p += n)
|
|
if ((j = r2c[i]) >= 0) {
|
|
if (p[j] <= maxdist)
|
|
c2r[j] = i;
|
|
else
|
|
c2r[j] = r2c[i] = -1;
|
|
}
|
|
|
|
p = (n == size ? c2r : r2c);
|
|
for (i = 0; i < size; i++)
|
|
if (*p++ < 0) {
|
|
slot = ffs(~(mt->touches | mt->frame)) - 1;
|
|
if (slot < 0 || slot >= mt->num_slots)
|
|
break;
|
|
wsmouse_mtstate(sc, slot,
|
|
pt[i].x, pt[i].y, pt[i].pressure);
|
|
pt[i].slot = slot;
|
|
}
|
|
|
|
p = (n == size ? r2c : c2r);
|
|
FOREACHBIT(touches, slot)
|
|
if ((i = *p++) >= 0) {
|
|
wsmouse_mtstate(sc, slot,
|
|
pt[i].x, pt[i].y, pt[i].pressure);
|
|
pt[i].slot = slot;
|
|
} else {
|
|
wsmouse_mtstate(sc, slot, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
free_mt_slots(struct wsmouseinput *input)
|
|
{
|
|
int n, size;
|
|
|
|
if ((n = input->mt.num_slots)) {
|
|
size = n * sizeof(struct mt_slot);
|
|
if (input->flags & MT_TRACKING)
|
|
size += MATRIX_SIZE(n);
|
|
input->mt.num_slots = 0;
|
|
free(input->mt.slots, M_DEVBUF, size);
|
|
input->mt.slots = NULL;
|
|
input->mt.matrix = NULL;
|
|
}
|
|
}
|
|
|
|
/* Allocate the MT slots and, if necessary, the buffers for MT tracking. */
|
|
int
|
|
wsmouse_mt_init(struct device *sc, int num_slots, int tracking)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
int n, size;
|
|
|
|
if (num_slots == input->mt.num_slots
|
|
&& (!tracking == ((input->flags & MT_TRACKING) == 0)))
|
|
return (0);
|
|
|
|
free_mt_slots(input);
|
|
|
|
if (tracking)
|
|
input->flags |= MT_TRACKING;
|
|
else
|
|
input->flags &= ~MT_TRACKING;
|
|
n = imin(imax(num_slots, 0), WSMOUSE_MT_SLOTS_MAX);
|
|
if (n) {
|
|
size = n * sizeof(struct mt_slot);
|
|
if (input->flags & MT_TRACKING)
|
|
size += MATRIX_SIZE(n);
|
|
input->mt.slots = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
|
|
if (input->mt.slots != NULL) {
|
|
if (input->flags & MT_TRACKING)
|
|
input->mt.matrix = (int *)
|
|
(input->mt.slots + n);
|
|
input->mt.num_slots = n;
|
|
return (0);
|
|
}
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
int
|
|
wsmouse_get_params(struct device *sc,
|
|
struct wsmouse_param *params, u_int nparams)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
int i, key, error = 0;
|
|
|
|
for (i = 0; i < nparams; i++) {
|
|
key = params[i].key;
|
|
switch (key) {
|
|
case WSMOUSECFG_DX_SCALE:
|
|
params[i].value = input->filter.h.scale;
|
|
break;
|
|
case WSMOUSECFG_DY_SCALE:
|
|
params[i].value = input->filter.v.scale;
|
|
break;
|
|
case WSMOUSECFG_PRESSURE_LO:
|
|
params[i].value = input->filter.pressure_lo;
|
|
break;
|
|
case WSMOUSECFG_PRESSURE_HI:
|
|
params[i].value = input->filter.pressure_hi;
|
|
break;
|
|
case WSMOUSECFG_TRKMAXDIST:
|
|
params[i].value = input->filter.tracking_maxdist;
|
|
break;
|
|
case WSMOUSECFG_SWAPXY:
|
|
params[i].value = input->filter.swapxy;
|
|
break;
|
|
case WSMOUSECFG_X_INV:
|
|
params[i].value = input->filter.h.inv;
|
|
break;
|
|
case WSMOUSECFG_Y_INV:
|
|
params[i].value = input->filter.v.inv;
|
|
break;
|
|
case WSMOUSECFG_REVERSE_SCROLLING:
|
|
params[i].value = !!(input->flags & REVERSE_SCROLLING);
|
|
break;
|
|
case WSMOUSECFG_DX_MAX:
|
|
params[i].value = input->filter.h.dmax;
|
|
break;
|
|
case WSMOUSECFG_DY_MAX:
|
|
params[i].value = input->filter.v.dmax;
|
|
break;
|
|
case WSMOUSECFG_X_HYSTERESIS:
|
|
params[i].value = input->filter.h.hysteresis;
|
|
break;
|
|
case WSMOUSECFG_Y_HYSTERESIS:
|
|
params[i].value = input->filter.v.hysteresis;
|
|
break;
|
|
case WSMOUSECFG_DECELERATION:
|
|
params[i].value = input->filter.dclr;
|
|
break;
|
|
case WSMOUSECFG_STRONG_HYSTERESIS:
|
|
params[i].value = 0; /* The feature has been removed. */
|
|
break;
|
|
case WSMOUSECFG_SMOOTHING:
|
|
params[i].value =
|
|
input->filter.mode & SMOOTHING_MASK;
|
|
break;
|
|
case WSMOUSECFG_LOG_INPUT:
|
|
params[i].value = !!(input->flags & LOG_INPUT);
|
|
break;
|
|
case WSMOUSECFG_LOG_EVENTS:
|
|
params[i].value = !!(input->flags & LOG_EVENTS);
|
|
break;
|
|
default:
|
|
error = wstpad_get_param(input, key, ¶ms[i].value);
|
|
if (error != 0)
|
|
return (error);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
wsmouse_set_params(struct device *sc,
|
|
const struct wsmouse_param *params, u_int nparams)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
int i, val, key, needreset = 0, error = 0;
|
|
|
|
for (i = 0; i < nparams; i++) {
|
|
key = params[i].key;
|
|
val = params[i].value;
|
|
switch (params[i].key) {
|
|
case WSMOUSECFG_PRESSURE_LO:
|
|
input->filter.pressure_lo = val;
|
|
if (val > input->filter.pressure_hi)
|
|
input->filter.pressure_hi = val;
|
|
input->touch.min_pressure = input->filter.pressure_hi;
|
|
break;
|
|
case WSMOUSECFG_PRESSURE_HI:
|
|
input->filter.pressure_hi = val;
|
|
if (val < input->filter.pressure_lo)
|
|
input->filter.pressure_lo = val;
|
|
input->touch.min_pressure = val;
|
|
break;
|
|
case WSMOUSECFG_X_HYSTERESIS:
|
|
input->filter.h.hysteresis = val;
|
|
break;
|
|
case WSMOUSECFG_Y_HYSTERESIS:
|
|
input->filter.v.hysteresis = val;
|
|
break;
|
|
case WSMOUSECFG_DECELERATION:
|
|
input->filter.dclr = val;
|
|
wstpad_init_deceleration(input);
|
|
break;
|
|
case WSMOUSECFG_DX_SCALE:
|
|
input->filter.h.scale = val;
|
|
break;
|
|
case WSMOUSECFG_DY_SCALE:
|
|
input->filter.v.scale = val;
|
|
break;
|
|
case WSMOUSECFG_TRKMAXDIST:
|
|
input->filter.tracking_maxdist = val;
|
|
break;
|
|
case WSMOUSECFG_SWAPXY:
|
|
input->filter.swapxy = val;
|
|
break;
|
|
case WSMOUSECFG_X_INV:
|
|
input->filter.h.inv = val;
|
|
break;
|
|
case WSMOUSECFG_Y_INV:
|
|
input->filter.v.inv = val;
|
|
break;
|
|
case WSMOUSECFG_REVERSE_SCROLLING:
|
|
if (val)
|
|
input->flags |= REVERSE_SCROLLING;
|
|
else
|
|
input->flags &= ~REVERSE_SCROLLING;
|
|
break;
|
|
case WSMOUSECFG_DX_MAX:
|
|
input->filter.h.dmax = val;
|
|
break;
|
|
case WSMOUSECFG_DY_MAX:
|
|
input->filter.v.dmax = val;
|
|
break;
|
|
case WSMOUSECFG_SMOOTHING:
|
|
input->filter.mode &= ~SMOOTHING_MASK;
|
|
input->filter.mode |= (val & SMOOTHING_MASK);
|
|
break;
|
|
case WSMOUSECFG_LOG_INPUT:
|
|
if (val)
|
|
input->flags |= LOG_INPUT;
|
|
else
|
|
input->flags &= ~LOG_INPUT;
|
|
break;
|
|
case WSMOUSECFG_LOG_EVENTS:
|
|
if (val)
|
|
input->flags |= LOG_EVENTS;
|
|
else
|
|
input->flags &= ~LOG_EVENTS;
|
|
break;
|
|
default:
|
|
needreset = 1;
|
|
error = wstpad_set_param(input, key, val);
|
|
if (error != 0)
|
|
return (error);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Reset soft-states if touchpad parameters changed */
|
|
if (needreset) {
|
|
wstpad_reset(input);
|
|
return (wstpad_configure(input));
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
wsmouse_set_mode(struct device *sc, int mode)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
|
|
if (mode == WSMOUSE_COMPAT) {
|
|
input->flags &= ~TPAD_NATIVE_MODE;
|
|
input->flags |= TPAD_COMPAT_MODE;
|
|
return (0);
|
|
} else if (mode == WSMOUSE_NATIVE) {
|
|
input->flags &= ~TPAD_COMPAT_MODE;
|
|
input->flags |= TPAD_NATIVE_MODE;
|
|
return (0);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
struct wsmousehw *
|
|
wsmouse_get_hw(struct device *sc)
|
|
{
|
|
return &((struct wsmouse_softc *) sc)->sc_input.hw;
|
|
}
|
|
|
|
/*
|
|
* Create a default configuration based on the hardware infos in the 'hw'
|
|
* fields. The 'params' argument is optional, hardware drivers can use it
|
|
* to modify the generic defaults. Up to now this function is only useful
|
|
* for touchpads.
|
|
*/
|
|
int
|
|
wsmouse_configure(struct device *sc,
|
|
struct wsmouse_param *params, u_int nparams)
|
|
{
|
|
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
|
|
int error;
|
|
|
|
if (!(input->flags & CONFIGURED)) {
|
|
if (input->hw.x_max && input->hw.y_max) {
|
|
if (input->hw.flags & WSMOUSEHW_LR_DOWN) {
|
|
input->filter.v.inv =
|
|
input->hw.y_max + input->hw.y_min;
|
|
}
|
|
}
|
|
input->filter.ratio = 1 << 12;
|
|
if (input->hw.h_res > 0 && input->hw.v_res > 0) {
|
|
input->filter.ratio *= input->hw.h_res;
|
|
input->filter.ratio /= input->hw.v_res;
|
|
}
|
|
if (wsmouse_mt_init(sc, input->hw.mt_slots,
|
|
(input->hw.flags & WSMOUSEHW_MT_TRACKING))) {
|
|
printf("wsmouse_configure: "
|
|
"MT initialization failed.\n");
|
|
return (-1);
|
|
}
|
|
if (IS_TOUCHPAD(input) && wstpad_configure(input)) {
|
|
printf("wstpad_configure: "
|
|
"Initialization failed.\n");
|
|
return (-1);
|
|
}
|
|
input->flags |= CONFIGURED;
|
|
if (params != NULL) {
|
|
if ((error = wsmouse_set_params(sc, params, nparams)))
|
|
return (error);
|
|
}
|
|
}
|
|
if (IS_TOUCHPAD(input))
|
|
wsmouse_set_mode(sc, WSMOUSE_COMPAT);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
void
|
|
wsmouse_input_reset(struct wsmouseinput *input)
|
|
{
|
|
int num_slots, *matrix;
|
|
struct mt_slot *slots;
|
|
|
|
memset(&input->btn, 0, sizeof(struct btn_state));
|
|
memset(&input->motion, 0, sizeof(struct motion_state));
|
|
memset(&input->touch, 0, sizeof(struct touch_state));
|
|
input->touch.min_pressure = input->filter.pressure_hi;
|
|
if ((num_slots = input->mt.num_slots)) {
|
|
slots = input->mt.slots;
|
|
matrix = input->mt.matrix;
|
|
memset(&input->mt, 0, sizeof(struct mt_state));
|
|
memset(slots, 0, num_slots * sizeof(struct mt_slot));
|
|
input->mt.num_slots = num_slots;
|
|
input->mt.slots = slots;
|
|
input->mt.matrix = matrix;
|
|
}
|
|
if (input->tp != NULL)
|
|
wstpad_reset(input);
|
|
}
|
|
|
|
void
|
|
wsmouse_input_cleanup(struct wsmouseinput *input)
|
|
{
|
|
if (input->tp != NULL)
|
|
wstpad_cleanup(input);
|
|
|
|
free_mt_slots(input);
|
|
}
|