Subject: Re: kern/12132 (wsmouse emulate3button patch)
To: None <gnats-bugs@netbsd.org>
From: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
List: netbsd-bugs
Date: 02/07/2006 07:57:10
--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--