Subject: Re: kern/12132 (wsmouse emulate3button patch)
To: None <jmmv@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org,>
From: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
List: netbsd-bugs
Date: 02/06/2006 23:00:05
The following reply was made to PR kern/12132; it has been noted by GNATS.

From: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
To: gnats-bugs@netbsd.org
Cc: jmmv@netbsd.org, kern-bug-people@netbsd.org,
	netbsd-bugs@netbsd.org, gnats-admin@netbsd.org, jmmv@netbsd.org,
	takashi.yamamoto@bigfoot.com
Subject: Re: kern/12132 (wsmouse emulate3button patch)
Date: Tue, 07 Feb 2006 07:57:10 +0900

 --NextPart-20060207075223-0364900
 Content-Type: Text/Plain; charset=us-ascii
 
 > Synopsis: wsmouse emulate3button patch
 
 i digged a patch which seems newer than the one in the PR.  (attached)
 no idea which is better, tho. :-)
 
 YAMAMOTO Takashi
 
 --NextPart-20060207075223-0364900
 Content-Type: Text/Plain; charset=us-ascii
 Content-Disposition: attachment; filename="emu3btn.diff"
 
 Index: wsconsio.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/wscons/wsconsio.h,v
 retrieving revision 1.76
 diff -u -p -r1.76 wsconsio.h
 --- wsconsio.h	23 Nov 2005 09:38:02 -0000	1.76
 +++ wsconsio.h	27 Nov 2005 23:14:45 -0000
 @@ -241,6 +241,27 @@ struct wsmouse_id {
  };
  #define	WSMOUSEIO_GETID		_IOWR('W', 38, struct wsmouse_id)
  
 +/* set/get emulate3button mask. 0 means no emulation */
 +#define	WSMOUSEIO_SEMU3BTN_MASK		_IOW('W', 58, u_int)
 +#define	WSMOUSEIO_GEMU3BTN_MASK		_IOR('W', 59, u_int)
 +#define		WSMOUSE_EMU3BTN_MASK_MIN	0
 +#define		WSMOUSE_EMU3BTN_MASK_DEFAULT	0
 +#define		WSMOUSE_EMU3BTN_MASK_MAX	0xffff
 +
 +/* set/get emulate3button button. */
 +#define	WSMOUSEIO_SEMU3BTN_BTN		_IOW('W', 60, u_int)
 +#define	WSMOUSEIO_GEMU3BTN_BTN		_IOR('W', 61, u_int)
 +#define		WSMOUSE_EMU3BTN_BTN_MIN	0
 +#define		WSMOUSE_EMU3BTN_BTN_DEFAULT	1 /* middle button */
 +#define		WSMOUSE_EMU3BTN_BTN_MAX	31
 +
 +/* set/get emulate3button timeout. */
 +#define	WSMOUSEIO_SEMU3BTN_TIMEOUT		_IOW('W', 62, u_int)
 +#define	WSMOUSEIO_GEMU3BTN_TIMEOUT		_IOR('W', 63, u_int)
 +#define		WSMOUSE_EMU3BTN_TIMEOUT_MIN	0
 +#define		WSMOUSE_EMU3BTN_TIMEOUT_DEFAULT	100
 +#define		WSMOUSE_EMU3BTN_TIMEOUT_MAX	500
 +
  /*
   * Display ioctls (64 - 95)
   */
 Index: wsevent.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/wscons/wsevent.c,v
 retrieving revision 1.16
 diff -u -p -r1.16 wsevent.c
 --- wsevent.c	7 Aug 2003 16:31:29 -0000	1.16
 +++ wsevent.c	27 Nov 2005 23:14:45 -0000
 @@ -85,6 +85,9 @@ __KERNEL_RCSID(0, "$NetBSD: wsevent.c,v 
  #include <sys/vnode.h>
  #include <sys/select.h>
  #include <sys/poll.h>
 +#include <sys/syslog.h>
 +#include <sys/kernel.h>
 +#include <sys/time.h>
  
  #include <dev/wscons/wsconsio.h>
  #include <dev/wscons/wseventvar.h>
 @@ -199,6 +202,40 @@ wsevent_poll(struct wseventvar *ev, int 
  	return (revents);
  }
  
 +/*
 + *  return -1 if can't enqueue (full of queue)
 + */ 
 +int
 +wsevent_write(struct wseventvar* evar, u_int type, int value)
 +{   
 +    int put;
 +    struct wscons_event* ev;
 +    int s;
 +
 +    put = evar->put;
 +    ev = &evar->q[put];
 + 
 +    put = (put + 1) % WSEVENT_QSIZE;
 +    if (put == evar->get) {
 +		static struct timeval last;
 +		struct timeval interval = {5, 0};
 +		if (ratecheck(&last, &interval))
 +			log(LOG_WARNING, "wscons event queue overflow\n");
 +		return -1;
 +    }
 +
 +    ev->type = type;
 +    ev->value = value;
 + 
 +    s = splhigh();
 +    TIMEVAL_TO_TIMESPEC(&time, &ev->time);
 +    splx(s);
 +        
 +    evar->put = put;
 + 
 +    return 0;
 +}
 +
  static void
  filt_wseventrdetach(struct knote *kn)
  {
 Index: wseventvar.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/wscons/wseventvar.h,v
 retrieving revision 1.8
 diff -u -p -r1.8 wseventvar.h
 --- wseventvar.h	7 Aug 2003 16:31:29 -0000	1.8
 +++ wseventvar.h	27 Nov 2005 23:14:45 -0000
 @@ -105,6 +105,7 @@ void	wsevent_init(struct wseventvar *);
  void	wsevent_fini(struct wseventvar *);
  int	wsevent_read(struct wseventvar *, struct uio *, int);
  int	wsevent_poll(struct wseventvar *, int, struct proc *);
 +int	wsevent_write(struct wseventvar *, u_int, int);
  int	wsevent_kqfilter(struct wseventvar *ev, struct knote *kn);
  
  /*
 Index: wsmouse.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/wscons/wsmouse.c,v
 retrieving revision 1.37
 diff -u -p -r1.37 wsmouse.c
 --- wsmouse.c	23 Nov 2005 09:38:02 -0000	1.37
 +++ wsmouse.c	27 Nov 2005 23:14:46 -0000
 @@ -73,6 +73,10 @@
  /*
   * Mouse driver.
   */
 +#if 1
 +#define WSMOUSE_EMULATE3BUTTON
 +/*#define WSMOUSE_DEBUG*/
 +#endif
  
  #include <sys/cdefs.h>
  __KERNEL_RCSID(0, "$NetBSD: wsmouse.c,v 1.37 2005/11/23 09:38:02 augustss Exp $");
 @@ -94,6 +98,9 @@ __KERNEL_RCSID(0, "$NetBSD: wsmouse.c,v 
  #include <sys/signalvar.h>
  #include <sys/device.h>
  #include <sys/vnode.h>
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +#include <sys/callout.h>
 +#endif
  
  #include <dev/wscons/wsconsio.h>
  #include <dev/wscons/wsmousevar.h>
 @@ -132,6 +139,15 @@ struct wsmouse_softc {
  
  	int		sc_refcnt;
  	u_char		sc_dying;	/* device is being detached */
 +
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +	struct callout sc_emu3btn_callout;
 +	u_int sc_delayed_event;
 +	u_int sc_emu3btn_mask;
 +	u_int sc_emu3btn_btn;
 +	u_int sc_emu3btn_timeout;
 +	u_char sc_emulating;
 +#endif
  };
  
  static int  wsmouse_match(struct device *, struct cfdata *, void *);
 @@ -149,6 +165,11 @@ static int  wsmouse_mux_close(struct wse
  
  static int  wsmousedoioctl(struct device *, u_long, caddr_t, int, struct proc *);
  
 +static int wsmouse_input_button(struct wsmouse_softc *, u_int);
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +static void establish_delayed_event(struct wsmouse_softc *);
 +static void wsmouse_emu3btn_callout(void *);
 +#endif
  static int  wsmousedoopen(struct wsmouse_softc *, struct wseventvar *);
  
  CFATTACH_DECL(wsmouse, sizeof (struct wsmouse_softc),
 @@ -175,6 +196,39 @@ struct wssrcops wsmouse_srcops = {
  };
  #endif
  
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +static void
 +establish_delayed_event(struct wsmouse_softc* sc)
 +{
 +	u_int d, mb;
 +/*
 +	if (sc->sc_delayed_event == 0) {
 +		DPRINTF(("wsmouse: no delayed events to establish\n"));
 +	}
 +	else {
 +*/
 +		DPRINTF(("wsmouse: establish delayed event(%x)\n", sc->sc_delayed_event));
 +		d = sc->sc_delayed_event;
 +		sc->sc_delayed_event = 0;
 +		mb = sc->sc_ub ^ d;	
 +		wsmouse_input_button(sc, mb);
 +/*
 +	}
 +*/
 +}
 +
 +static void
 +wsmouse_emu3btn_callout(void *arg)
 +{
 +	int s;
 +
 +	DPRINTF(("wsmouse: callout\n"));
 +	s = spltty();
 +	establish_delayed_event((struct wsmouse_softc*)arg);
 +	splx(s);
 +}
 +#endif
 +
  /*
   * Print function (for parent devices).
   */
 @@ -205,6 +259,15 @@ wsmouse_attach(struct device *parent, st
  	sc->sc_accessops = ap->accessops;
  	sc->sc_accesscookie = ap->accesscookie;
  
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +	sc->sc_delayed_event = 0;
 +	sc->sc_emu3btn_mask = WSMOUSE_EMU3BTN_MASK_DEFAULT;
 +	sc->sc_emu3btn_btn = WSMOUSE_EMU3BTN_BTN_DEFAULT;
 +	sc->sc_emu3btn_timeout = WSMOUSE_EMU3BTN_TIMEOUT_DEFAULT;
 +	sc->sc_emulating = 0;
 +	callout_init(&sc->sc_emu3btn_callout);
 +#endif
 +
  #if NWSMUX > 0
  	sc->sc_base.me_ops = &wsmouse_srcops;
  	mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux;
 @@ -289,10 +352,15 @@ wsmouse_input_xyzw(struct device *wsmous
  	int x, int y, int z, int w, u_int flags)
  {
  	struct wsmouse_softc *sc = (struct wsmouse_softc *)wsmousedev;
 -	struct wscons_event *ev;
  	struct wseventvar *evar;
 -	int mb, ub, d, get, put, any;
 +	int any;
 +	u_int mb;
  
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +	u_int d, ub;
 +	int emu3btn_mask;
 +	int emu3btn_btn;
 +#endif
          /*
           * Discard input if not open.
           */
 @@ -329,89 +397,56 @@ wsmouse_input_xyzw(struct device *wsmous
  	 * of changes or out of room.  As events get delivered,
  	 * mark them `unchanged'.
  	 */
 -	ub = sc->sc_ub;
  	any = 0;
 -	get = evar->get;
 -	put = evar->put;
 -	ev = &evar->q[put];
 -
 -	/* NEXT prepares to put the next event, backing off if necessary */
 -#define	NEXT								\
 -	if ((++put) % WSEVENT_QSIZE == get) {				\
 -		put--;							\
 -		goto out;						\
 -	}
 -	/* ADVANCE completes the `put' of the event */
 -#define	ADVANCE								\
 -	ev++;								\
 -	if (put >= WSEVENT_QSIZE) {					\
 -		put = 0;						\
 -		ev = &evar->q[0];				\
 -	}								\
 -	any = 1
 -	/* TIMESTAMP sets `time' field of the event to the current time */
 -#define TIMESTAMP							\
 -	do {								\
 -		int s;							\
 -		s = splhigh();						\
 -		TIMEVAL_TO_TIMESPEC(&time, &ev->time);			\
 -		splx(s);						\
 -	} while (0)
  
  	if (flags & WSMOUSE_INPUT_ABSOLUTE_X) {
  		if (sc->sc_x != x) {
 -			NEXT;
 -			ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_X;
 -			ev->value = x;
 -			TIMESTAMP;
 -			ADVANCE;
 +			if (wsevent_write(evar, WSCONS_EVENT_MOUSE_ABSOLUTE_X, x)) {
 +				goto out;
 +			}
 +			any = 1;
  			sc->sc_x = x;
  		}
  	} else {
  		if (sc->sc_dx) {
 -			NEXT;
 -			ev->type = WSCONS_EVENT_MOUSE_DELTA_X;
 -			ev->value = sc->sc_dx;
 -			TIMESTAMP;
 -			ADVANCE;
 +			if (wsevent_write(evar, WSCONS_EVENT_MOUSE_DELTA_X, sc->sc_dx)) {
 +				goto out;
 +			}
 +			any = 1;
  			sc->sc_dx = 0;
  		}
  	}
  	if (flags & WSMOUSE_INPUT_ABSOLUTE_Y) {
  		if (sc->sc_y != y) {
 -			NEXT;
 -			ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_Y;
 -			ev->value = y;
 -			TIMESTAMP;
 -			ADVANCE;
 +			if (wsevent_write(evar, WSCONS_EVENT_MOUSE_ABSOLUTE_Y, y)) {
 +				goto out;
 +			}
 +			any = 1;
  			sc->sc_y = y;
  		}
  	} else {
  		if (sc->sc_dy) {
 -			NEXT;
 -			ev->type = WSCONS_EVENT_MOUSE_DELTA_Y;
 -			ev->value = sc->sc_dy;
 -			TIMESTAMP;
 -			ADVANCE;
 +			if (wsevent_write(evar, WSCONS_EVENT_MOUSE_DELTA_Y, sc->sc_dy)) {
 +				goto out;
 +			}	
 +			any = 1;
  			sc->sc_dy = 0;
  		}
  	}
  	if (flags & WSMOUSE_INPUT_ABSOLUTE_Z) {
  		if (sc->sc_z != z) {
 -			NEXT;
 -			ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_Z;
 -			ev->value = z;
 -			TIMESTAMP;
 -			ADVANCE;
 +			if (wsevent_write(evar, WSCONS_EVENT_MOUSE_ABSOLUTE_Z, z)) {
 +				goto out;
 +			}
 +			any = 1;
  			sc->sc_z = z;
  		}
  	} else {
  		if (sc->sc_dz) {
 -			NEXT;
 -			ev->type = WSCONS_EVENT_MOUSE_DELTA_Z;
 -			ev->value = sc->sc_dz;
 -			TIMESTAMP;
 -			ADVANCE;
 +			if (wsevent_write(evar, WSCONS_EVENT_MOUSE_DELTA_Z, sc->sc_dz)) {
 +				goto out;
 +			}
 +			any = 1;
  			sc->sc_dz = 0;
  		}
  	}
 @@ -436,27 +471,67 @@ wsmouse_input_xyzw(struct device *wsmous
  	}
  
  	mb = sc->sc_mb;
 -	while ((d = mb ^ ub) != 0) {
 -		/*
 -		 * Mouse button change.  Find the first change and drop
 -		 * it into the event queue.
 -		 */
 -		NEXT;
 -		ev->value = ffs(d) - 1;
  
 -		KASSERT(ev->value >= 0);
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +	ub = sc->sc_ub ^ sc->sc_delayed_event;
 +	emu3btn_mask = sc->sc_emu3btn_mask;
 +	emu3btn_btn = sc->sc_emu3btn_btn;
 +	if ((d = (mb ^ ub) & emu3btn_mask) != 0) {
 +		if ((mb & emu3btn_mask) == emu3btn_mask
 +			&& (sc->sc_delayed_event || sc->sc_emulating || d == emu3btn_mask)) {
 +			/* emulate press */
 +			sc->sc_delayed_event = 0; /* clear delayed event */
 +			sc->sc_emulating = 1; /* start emulating */
 +			DPRINTF(("wsmouse: start emulating\n"));
 +			if (wsevent_write(evar, WSCONS_EVENT_MOUSE_DOWN, emu3btn_btn)) {
 +				goto out;
 +			}
 +			any = 1;
 +			DPRINTF(("wsmouse: emu3btn down\n"));
 +		}
 +		else if ((ub & emu3btn_mask) == emu3btn_mask && sc->sc_emulating) {
 +			/* emulate release */
 +			if (wsevent_write(evar, WSCONS_EVENT_MOUSE_UP, emu3btn_btn)) {
 +				goto out;
 +			}
 +			any = 1;
 +			DPRINTF(("wsmouse: emu3btn up\n"));
 +		}
 +		else {
 +			establish_delayed_event(sc);
  
 -		d = 1 << ev->value;
 -		ev->type =
 -		    (mb & d) ? WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP;
 -		TIMESTAMP;
 -		ADVANCE;
 -		ub ^= d;
 +			/*
 +			 * if emulating == 0, press event is delayed.
 +			 */
 +			if (sc->sc_emulating == 0 && d & mb) {
 +				/*
 +				 * delay event
 +				 */
 +				sc->sc_delayed_event ^= d;
 +				DPRINTF(("wsmouse: set callout delay_ev= %u ub=%u mb=%u\n", d, ub, mb));
 +				callout_reset(&sc->sc_emu3btn_callout, hz*sc->sc_emu3btn_timeout/1000,
 +							wsmouse_emu3btn_callout, (void*)sc);
 +			}
 +		}
 +	}
 +
 +	mb ^= sc->sc_delayed_event; /* restrict button events for delayed event */
 +
 +#endif
 +
 +	if (wsmouse_input_button(sc, mb) > 0) {
 +		any = 1;
 +	}
 +
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +	if (sc->sc_emulating && (sc->sc_ub & sc->sc_emu3btn_mask) == 0) {
 +		sc->sc_emulating = 0;
 +		DPRINTF(("wsmouse: end emulating\n"));
  	}
 +#endif
 +
  out:
  	if (any) {
 -		sc->sc_ub = ub;
 -		evar->put = put;
  		WSEVENT_WAKEUP(evar);
  #if NWSMUX > 0
  		DPRINTFN(5,("wsmouse_input: %s wakeup evar=%p\n",
 @@ -465,6 +540,54 @@ out:
  	}
  }
  
 +/*
 + * return num of enqueued events
 + */
 +static int
 +wsmouse_input_button(struct wsmouse_softc* sc, u_int mb)
 +{
 +	struct wseventvar* evar;
 +	u_int d;
 +	u_int ub;
 +	int count = 0;
 +
 +	evar = sc->sc_base.me_evp;
 +
 +	ub = sc->sc_ub;
 +
 +	while ((d = mb ^ ub) != 0) {
 +		/*
 +		 * Mouse button change.  Find the first change and drop
 +		 * it into the event queue.
 +		 */
 +		u_int type;
 +		int value;
 +
 +		type = (mb & d) ? WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP;
 +		value = ffs(d) - 1;
 +		KASSERT(value >= 0);
 +		d = 1 << value;
 +		
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +		if (sc->sc_emulating == 0 || (d & sc->sc_emu3btn_mask) == 0) {
 +#endif
 +			if (wsevent_write(evar, type, value)) {
 +				break;
 +			}
 +			count ++;
 +			DPRINTF(("wsmouse: event: ub %u->%u\n", ub, ub^d)); 
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +		}
 +#endif
 +
 +		ub ^= d;
 +	}
 +
 +	sc->sc_ub = ub;
 +
 +	return count;
 +}
 +
  int
  wsmouseopen(dev_t dev, int flags, int mode, struct proc *p)
  {
 @@ -589,6 +712,10 @@ wsmouse_do_ioctl(struct wsmouse_softc *s
  {
  	int error;
  
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +	u_int count, t;
 +#endif
 +
  	if (sc->sc_dying)
  		return (EIO);
  
 @@ -619,6 +746,57 @@ wsmouse_do_ioctl(struct wsmouse_softc *s
  		if (*(int *)data != sc->sc_base.me_evp->io->p_pgid)
  			return (EPERM);
  		return (0);
 +
 +#ifdef WSMOUSE_EMULATE3BUTTON
 +	/*
 +	 * do emulate 3 button things
 +	 */
 +	case WSMOUSEIO_SEMU3BTN_MASK:
 +		t = (uint)*(int*)data;
 +		if (t > WSMOUSE_EMU3BTN_MASK_MAX)
 +			return (EINVAL);
 +
 +		/*
 +		 * count bits
 +		 */
 +		count = 0;
 +		for (; t; t &= t-1) {
 +			count++;
 +		}
 +		/*
 +		 * more than 2 buttons are not supported.
 +		 */
 +		if (count > 2) {
 +			return (EINVAL);
 +		}
 +
 +		sc->sc_emu3btn_mask = *(int*)data;
 +		return (0);
 +
 +	case WSMOUSEIO_GEMU3BTN_MASK:
 +		*(int*)data = sc->sc_emu3btn_mask;
 +		return (0);
 +
 +	case WSMOUSEIO_SEMU3BTN_BTN:
 +		if ((u_int)*(int*)data > WSMOUSE_EMU3BTN_BTN_MAX)
 +			return (EINVAL);
 +		sc->sc_emu3btn_btn = *(int*)data;
 +		return (0);
 +
 +	case WSMOUSEIO_GEMU3BTN_BTN:
 +		*(int*)data = sc->sc_emu3btn_btn;
 +		return (0);
 +
 +	case WSMOUSEIO_SEMU3BTN_TIMEOUT:
 +		if ((u_int)*(int*)data > WSMOUSE_EMU3BTN_TIMEOUT_MAX)
 +			return (EINVAL);
 +		sc->sc_emu3btn_timeout = *(int*)data;
 +		return (0);
 +
 +	case WSMOUSEIO_GEMU3BTN_TIMEOUT:
 +		*(int*)data = sc->sc_emu3btn_timeout;
 +		return (0);
 +#endif
  	}
  
  	/*
 
 --NextPart-20060207075223-0364900--