Subject: kern/12132: wsmouse emulate3button patch
To: None <gnats-bugs@gnats.netbsd.org>
From: None <takashi.yamamoto@bigfoot.com>
List: netbsd-bugs
Date: 02/04/2001 06:50:28
>Number:         12132
>Category:       kern
>Synopsis:       wsmouse emulate3button patch
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sun Feb 04 06:53:00 PST 2001
>Closed-Date:
>Last-Modified:
>Originator:     YAMAMOTO Takashi
>Release:        current
>Organization:
>Environment:
NetBSD capybara 1.5R NetBSD 1.5R (535) #86: Sun Feb  4 21:26:14 JST 2001     takashi@capybara:/usr/src/sys/arch/i386/compile/535 i386


>Description:
I'm using trackpoint on my thinkpad, which has 2 buttons, and usb mouse, which has 3 buttons.
I want to use Emulation3Button for trackpoint and not for usb one.

I tried XInput additional mouse feature, but not happy because it 
needs to reboot xserver after hot-plug a usb mouse.

so I want wsmouse to emulate 3buttons.


>How-To-Repeat:

>Fix:
apply following patches and

# wsconsctl -mw emu3btn_mask=5

5 means combination of buttons.(101b, left & right)




Index: wsmouse.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/wscons/wsmouse.c,v
retrieving revision 1.12
diff -u -r1.12 wsmouse.c
--- wsmouse.c	2000/05/01 07:36:58	1.12
+++ wsmouse.c	2001/02/04 14:40:41
@@ -80,6 +80,15 @@
 /*
  * Mouse driver.
  */
+#if 1
+#define WSMOUSE_EMULATE3BUTTON
+#endif
+
+#ifdef WSMOUSE_DEBUG
+#define DPRINTF(a) printf a
+#else
+#define DPRINTF(a)
+#endif
 
 #include <sys/param.h>
 #include <sys/conf.h>
@@ -93,6 +102,9 @@
 #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>
@@ -135,6 +147,15 @@
 #if NWSMUX > 0
 	struct wsmux_softc *sc_mux;
 #endif
+
+#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
 };
 
 int	wsmouse_match __P((struct device *, struct cfdata *, void *));
@@ -149,6 +170,8 @@
 int	wsmousedoioctl __P((struct device *, u_long, caddr_t, int, 
 			    struct proc *));
 
+static int wsmouse_input_button(struct wsmouse_softc* sc, u_int mb);
+
 struct cfattach wsmouse_ca = {
 	sizeof (struct wsmouse_softc), wsmouse_match, wsmouse_attach,
 	wsmouse_detach, wsmouse_activate
@@ -166,6 +189,29 @@
 };
 #endif
 
+#ifdef WSMOUSE_EMULATE3BUTTON
+static void
+establish_delayed_event(struct wsmouse_softc* sc)
+{
+	u_int d, mb;
+
+	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;
+
+	s = spltty();
+	establish_delayed_event((struct wsmouse_softc*)arg);
+	splx(s);
+}
+#endif
+
 /*
  * Print function (for parent devices).
  */
@@ -204,6 +250,15 @@
 	sc->sc_accesscookie = ap->accesscookie;
 	sc->sc_ready = 0;				/* sanity */
 
+#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
 	mux = sc->sc_dv.dv_cfdata->wsmousedevcf_mux;
 	if (mux != WSMOUSEDEVCF_MUX_DEFAULT) {
@@ -290,10 +345,15 @@
 	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;
-
+	u_int mb;
+	int any;
+#ifdef WSMOUSE_EMULATE3BUTTON
+	int emu3btn_mask;
+	int emu3btn_btn;
+	u_int ub;
+	u_int d;
+#endif
         /*
          * Discard input if not ready.
          */
@@ -323,116 +383,175 @@
 	 * mark them `unchanged'.
 	 */
 	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;
 		}
 	}
 
 	mb = sc->sc_mb;
+
+#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);
+
+			/*
+			 * 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) {
+		WSEVENT_WAKEUP(evar);
+	}
+}
+
+/*
+ * 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;
+
+#if NWSMUX > 0
+	if (sc->sc_mux)
+		evar = &sc->sc_mux->sc_events;
+	else
+#endif
+		evar = &sc->sc_events;
+
 	ub = sc->sc_ub;
+
 	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;
+		u_int type;
+		int value;
 
-		KASSERT(ev->value >= 0);
+		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
 
-		d = 1 << ev->value;
-		ev->type =
-		    (mb & d) ? WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP;
-		TIMESTAMP;
-		ADVANCE;
 		ub ^= d;
-	}
-out:
-	if (any) {
-		sc->sc_ub = ub;
-		evar->put = put;
-		WSEVENT_WAKEUP(evar);
 	}
+
+	sc->sc_ub = ub;
+
+	return count;
 }
 
 int
@@ -594,6 +713,10 @@
 {
 	int error;
 
+#ifdef WSMOUSE_EMULATE3BUTTON
+	u_int count, t;
+#endif
+
 	if (sc->sc_dying)
 		return (EIO);
 
@@ -612,6 +735,57 @@
 		if (*(int *)data != sc->sc_events.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
 	}
 
 	/*
Index: wsevent.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/wscons/wsevent.c,v
retrieving revision 1.5
diff -u -r1.5 wsevent.c
--- wsevent.c	2000/03/30 12:45:44	1.5
+++ wsevent.c	2001/02/04 14:40:41
@@ -89,6 +89,8 @@
 #include <sys/vnode.h>
 #include <sys/select.h>
 #include <sys/poll.h>
+#include <sys/syslog.h>
+#include <sys/kernel.h>
 
 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wseventvar.h>
@@ -199,3 +201,36 @@
 	splx(s);
 	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) % WSEVENT_QSIZE;
+    if (put == evar->get) {
+        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;
+}
+
+
Index: wsconsio.h
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/wscons/wsconsio.h,v
retrieving revision 1.37
diff -u -r1.37 wsconsio.h
--- wsconsio.h	2001/02/02 05:58:04	1.37
+++ wsconsio.h	2001/02/04 14:40:45
@@ -194,6 +194,27 @@
 #define	WSMOUSEIO_SCALIBCOORDS	_IOW('W', 36, struct wsmouse_calibcoords)
 #define	WSMOUSEIO_GCALIBCOORDS	_IOR('W', 37, struct wsmouse_calibcoords)
 
+/* set/get emulate3button mask. 0 means no emulation */
+#define	WSMOUSEIO_SEMU3BTN_MASK		_IOW('W', 38, u_int)
+#define	WSMOUSEIO_GEMU3BTN_MASK		_IOR('W', 39, 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', 40, u_int)
+#define	WSMOUSEIO_GEMU3BTN_BTN		_IOR('W', 41, 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', 42, u_int)
+#define	WSMOUSEIO_GEMU3BTN_TIMEOUT		_IOR('W', 43, 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: mouse.c
===================================================================
RCS file: /cvsroot/basesrc/sbin/wsconsctl/mouse.c,v
retrieving revision 1.3
diff -u -r1.3 mouse.c
--- mouse.c	1999/11/15 13:47:30	1.3
+++ mouse.c	2001/02/04 14:41:51
@@ -45,11 +45,17 @@
 static int mstype;
 static int resolution;
 static int samplerate;
+static int emu3btn_mask;
+static int emu3btn_btn;
+static int emu3btn_timeout;
 
 struct field mouse_field_tab[] = {
     { "resolution",		&resolution,	FMT_UINT,	FLG_WRONLY },
     { "samplerate",		&samplerate,	FMT_UINT,	FLG_WRONLY },
     { "type",			&mstype,	FMT_MSTYPE,	FLG_RDONLY },
+    { "emu3btn_mask",			&emu3btn_mask,	FMT_UINT,	0 },
+    { "emu3btn_btn",			&emu3btn_btn,	FMT_UINT,	0 },
+    { "emu3btn_timeout",			&emu3btn_timeout,	FMT_UINT,	0 },
 };
 
 int mouse_field_tab_len = sizeof(mouse_field_tab)/
@@ -62,6 +68,15 @@
 	if (field_by_value(&mstype)->flags & FLG_GET)
 		if (ioctl(fd, WSMOUSEIO_GTYPE, &mstype) < 0)
 			err(1, "WSMOUSEIO_GTYPE");
+	if (field_by_value(&emu3btn_mask)->flags & FLG_GET)
+		if (ioctl(fd, WSMOUSEIO_GEMU3BTN_MASK, &emu3btn_mask) < 0)
+			err(1, "WSMOUSEIO_GEMU3BTN_MASK");
+	if (field_by_value(&emu3btn_btn)->flags & FLG_GET)
+		if (ioctl(fd, WSMOUSEIO_GEMU3BTN_BTN, &emu3btn_btn) < 0)
+			err(1, "WSMOUSEIO_GEMU3BTN_BTN");
+	if (field_by_value(&emu3btn_timeout)->flags & FLG_GET)
+		if (ioctl(fd, WSMOUSEIO_GEMU3BTN_TIMEOUT, &emu3btn_timeout) < 0)
+			err(1, "WSMOUSEIO_GEMU3BTN_TIMEOUT");
 }
 
 void
@@ -80,6 +95,24 @@
 		tmp = samplerate;
 		if (ioctl(fd, WSMOUSEIO_SRATE, &tmp) < 0)
 			err(1, "WSMOUSEIO_SRES");
-		pr_field(field_by_value(&tmp), " -> ");
+		pr_field(field_by_value(&samplerate), " -> ");
+	}
+	if (field_by_value(&emu3btn_mask)->flags & FLG_SET) {
+		tmp = emu3btn_mask;
+		if (ioctl(fd, WSMOUSEIO_SEMU3BTN_MASK, &tmp) < 0)
+			err(1, "WSMOUSEIO_SEMU3BTN_MASK");
+		pr_field(field_by_value(&emu3btn_mask), " -> ");
+	}
+	if (field_by_value(&emu3btn_btn)->flags & FLG_SET) {
+		tmp = emu3btn_btn;
+		if (ioctl(fd, WSMOUSEIO_SEMU3BTN_BTN, &tmp) < 0)
+			err(1, "WSMOUSEIO_SEMU3BTN_BTN");
+		pr_field(field_by_value(&emu3btn_btn), " -> ");
+	}
+	if (field_by_value(&emu3btn_timeout)->flags & FLG_SET) {
+		tmp = emu3btn_timeout;
+		if (ioctl(fd, WSMOUSEIO_SEMU3BTN_TIMEOUT, &tmp) < 0)
+			err(1, "WSMOUSEIO_SEMU3BTN_TIMEOUT");
+		pr_field(field_by_value(&emu3btn_timeout), " -> ");
 	}
 }


>Release-Note:
>Audit-Trail:
>Unformatted: