Tested on amd64-current, with a standard Logitech USB mouse and an A4Tech serial mouse (detects as "microsoft type"), seems to work nicely.
Index: wsmoused.8 =================================================================== RCS file: /cvs/src/usr.sbin/wsmoused/wsmoused.8,v retrieving revision 1.18 diff -u -r1.18 wsmoused.8 --- wsmoused.8 5 Jun 2009 06:50:52 -0000 1.18 +++ wsmoused.8 4 Sep 2010 19:22:17 -0000 @@ -40,6 +40,8 @@ .Fl M .Ar N Ns = Ns Ar M .Oc +.Op Fl a Ar accel +.Op Fl r Ar resist .Op Fl p Ar device .Op Fl t Ar type .Sh DESCRIPTION @@ -94,6 +96,17 @@ .Ar file . .It Fl i Print the type and the protocol of the mouse and exit. +.It Fl a Ar accel +The acceleration that should be applied when the mouse is being moved +with high speed. +.Ar accel +must be a floating point value greater than or equal to 1.0 and less than or equal to 10.0. +If omitted, the default value of 2.0 is used. +.It Fl r Ar resist +Resistance to motion, the greater this value, the slower the mouse moves. +.Ar resist +must be a value greater than or equal to 0 and less than or equal to 80. +If omitted, the default value of 15 is used. .It Fl M Ar N Ns = Ns Ar M Assign the physical button .Ar M Index: wsmoused.c =================================================================== RCS file: /cvs/src/usr.sbin/wsmoused/wsmoused.c,v retrieving revision 1.25 diff -u -r1.25 wsmoused.c --- wsmoused.c 21 Jun 2009 16:13:18 -0000 1.25 +++ wsmoused.c 4 Sep 2010 19:22:17 -0000 @@ -74,6 +74,8 @@ #define DEFAULT_TTY "/dev/ttyCcfg" #define DEFAULT_PIDFILE "/var/run/wsmoused.pid" +#define DEFAULT_RESISTANCE 15 +#define DEFAULT_ACCELERATION 2.0 extern char *__progname; extern char *mouse_names[]; @@ -83,6 +85,8 @@ int nodaemon = FALSE; int identify = FALSE; char *pidfile = NULL; +int resistance = -1; +double acceleration = -1.0; mouse_t mouse = { .flags = 0, @@ -303,45 +307,69 @@ ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event); } -/* workaround for cursor speed on serial mice */ -static void -normalize_event(struct wscons_event *event) -{ - int dx, dy; - int two_power = 1; - -/* 2: normal speed, 3: slower cursor, 1: faster cursor */ -#define NORMALIZE_DIVISOR 3 - - switch (event->type) { - case WSCONS_EVENT_MOUSE_DELTA_X: - dx = abs(event->value); - while (dx > 2) { - two_power++; - dx = dx / 2; - } - event->value = event->value / (NORMALIZE_DIVISOR * two_power); - break; - case WSCONS_EVENT_MOUSE_DELTA_Y: - two_power = 1; - dy = abs(event->value); - while (dy > 2) { - two_power++; - dy = dy / 2; - } - event->value = event->value / (NORMALIZE_DIVISOR * two_power); - break; - } -} - /* send a wscons_event to the kernel */ static int treat_event(struct wscons_event *event) { + int i; + enum { + X_ACCUM, + Y_ACCUM, + N_ACCUMS + }; + static int accums[N_ACCUMS]; + /* + * Depending on the console's font dimensions, the "pixels" of the + * screen might be rectangular instead of being square. In fact, they + * are in default configuration, because a 8x16 font is used. + * Compensate for this fact by adding more resistance on Y axis. + * 4/3 is a compromise value, to compensate, but in turn not to make + * using say 8x8 font together with wsmoused too awkward. + * TODO (?) + * make it so that wsmoused can detect when VT is switched and + * so it could find out what the font dimensions on that + * particular VT are (e.g. add VT switch event to wscons, add new + * ioctl to request font dimensions?). + */ + const double resist_mults[N_ACCUMS] = { + 1.0, + 4.0/3.0 + }; + static double max_speed = 1.0; + int resist; struct wscons_event mapped_event; if (IS_MOTION_EVENT(event->type)) { - ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event); + if (event->type == WSCONS_EVENT_MOUSE_DELTA_X) + i = X_ACCUM; + else if (event->type == WSCONS_EVENT_MOUSE_DELTA_Y) + i = Y_ACCUM; + else { + /* + * Delta_{w,z} aka scrollwheel events don't need any + * additional processing such as scaling and/or + * acceleration. + */ + ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event); + return 1; + } + + if (max_speed < abs(event->value)) + max_speed = abs(event->value); + + /* + * Maximum acceleration is applied when + * event->value tends to max_speed. + */ + accums[i] += event->value + event->value * + ((acceleration - 1.0) * + (abs(event->value) / max_speed)); + + resist = resistance * resist_mults[i]; + if ((event->value = accums[i] / resist) != 0) { + accums[i] %= resist; + ioctl(mouse.cfd, WSDISPLAYIO_WSMOUSED, event); + } return 1; } else if (IS_BUTTON_EVENT(event->type) && (uint)event->value < MOUSE_MAXBUTTON) { @@ -365,13 +393,11 @@ if (act->dx != 0) { event.type = WSCONS_EVENT_MOUSE_DELTA_X; event.value = act->dx; - normalize_event(&event); treat_event(&event); } if (act->dy != 0) { event.type = WSCONS_EVENT_MOUSE_DELTA_Y; event.value = 0 - act->dy; - normalize_event(&event); treat_event(&event); } if (act->dz != 0) { @@ -519,7 +545,8 @@ usage(void) { fprintf(stderr, "usage: %s [-2dfi] [-C thresh] [-D device] [-I file]" - " [-M N=M]\n\t[-p device] [-t type]\n", __progname); + " [-M N=M]\n\t[-r resist] [-a accel] [-p device] [-t type]\n", + __progname); exit(1); } @@ -530,14 +557,26 @@ unsigned int type; int opt; int i; + const char *errstr; -#define GETOPT_STRING "2dfhip:t:C:D:I:M:" +#define GETOPT_STRING "2a:dfhip:r:t:C:D:I:M:" while ((opt = (getopt(argc, argv, GETOPT_STRING))) != -1) { switch (opt) { case '2': /* on two button mice, right button pastes */ p2l[MOUSE_BUTTON3] = MOUSE_BUTTON2; break; + case 'a': +#define MAX_ACCELERATION 10.0 + acceleration = atof(optarg); + if (acceleration < 1.0 || + acceleration > MAX_ACCELERATION) { + warnx("invalid acceleration `%s': " + "max value is %.1f", + optarg, MAX_ACCELERATION); + usage(); + } + break; case 'd': ++debug; break; @@ -555,6 +594,17 @@ if ((mouse.portname = strdup(optarg)) == NULL) logerr(1, "out of memory"); break; + case 'r': +#define MAX_RESISTANCE 80 + resistance = + strtonum(optarg, 0, MAX_RESISTANCE, &errstr); + if (errstr) { + warnx("resistance `%s': %s", + optarg, errstr); + usage(); + } + resistance += 1; + break; case 't': if (strcmp(optarg, "auto") == 0) { mouse.proto = P_UNKNOWN; @@ -574,11 +624,11 @@ break; case 'C': #define MAX_CLICKTHRESHOLD 2000 /* max delay for double click */ - mouse.clickthreshold = atoi(optarg); - if (mouse.clickthreshold < 0 || - mouse.clickthreshold > MAX_CLICKTHRESHOLD) { - warnx("invalid threshold `%s': max value is %d", - optarg, MAX_CLICKTHRESHOLD); + mouse.clickthreshold = + strtonum(optarg, 0, MAX_CLICKTHRESHOLD, &errstr); + if (errstr) { + warnx("threshold `%s': %s", + optarg, errstr); usage(); } break; @@ -607,6 +657,11 @@ mouse.portname = WSMOUSE_DEV; if (mouse.ttyname == NULL) mouse.ttyname = DEFAULT_TTY; + + if (resistance == -1) + resistance = DEFAULT_RESISTANCE; + if (acceleration == -1.0) + acceleration = DEFAULT_ACCELERATION; if (!nodaemon) { openlog(__progname, LOG_PID, LOG_DAEMON);