Skip to content

Instantly share code, notes, and snippets.

@tsutsui
Created November 29, 2020 14:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tsutsui/3859b59488d58da242d27ac15130e630 to your computer and use it in GitHub Desktop.
Save tsutsui/3859b59488d58da242d27ac15130e630 to your computer and use it in GitHub Desktop.
pseudo code to confirm how to apply emulation 3rd button code derived from xf86-input-mouse/dist/src/mouse.c to xorg-server/dist/hw/netbsd/x68k (and other DDX) server mouse drivers.
/*
*
* Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
* Copyright 1993 by David Dawes <dawes@xfree86.org>
* Copyright 2002 by SuSE Linux AG, Author: Egbert Eich
* Copyright 1994-2002 by The XFree86 Project, Inc.
* Copyright 2002 by Paul Elliott
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the names of copyright holders not be
* used in advertising or publicity pertaining to distribution of the
* software without specific, written prior permission. The copyright holders
* make no representations about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, 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.
*
*/
#include "x68k.h"
#include "mi.h"
#include "input.h"
#include "inpututils.h"
#include "exevents.h"
#include "events.h"
#include "eventstr.h"
#include <X11/Xatom.h>
#include "xserver-properties.h"
#ifdef __NetBSD__
#include <time.h>
#include <dev/wscons/wsconsio.h>
#include <sys/ioctl.h>
#endif
static CARD32 buttonTimer(DeviceInfoPtr device);
static void Emulate3ButtonsSetEnabled(DeviceInfoPtr device, Bool enable);
static Bool Emulate3ButtonsSoft(DeviceInfoPtr device);
static void MouseBlockHandler(void *data, void *waitTime);
static void MouseWakeupHandler(void *data, int i);
static void MouseDoPostEvent(DeviceInfoPtr device, int buttons, int dx, int dy);
/**********************************************************************
*
* Emulate3Button support code
*
**********************************************************************/
/*
* Lets create a simple finite-state machine for 3 button emulation:
*
* We track buttons 1 and 3 (left and right). There are 11 states:
* 0 ground - initial state
* 1 delayed left - left pressed, waiting for right
* 2 delayed right - right pressed, waiting for left
* 3 pressed middle - right and left pressed, emulated middle sent
* 4 pressed left - left pressed and sent
* 5 pressed right - right pressed and sent
* 6 released left - left released after emulated middle
* 7 released right - right released after emulated middle
* 8 repressed left - left pressed after released left
* 9 repressed right - right pressed after released right
* 10 pressed both - both pressed, not emulating middle
*
* At each state, we need handlers for the following events
* 0: no buttons down
* 1: left button down
* 2: right button down
* 3: both buttons down
* 4: emulate3Timeout passed without a button change
* Note that button events are not deltas, they are the set of buttons being
* pressed now. It's possible (ie, mouse hardware does it) to go from (eg)
* left down to right down without anything in between, so all cases must be
* handled.
*
* a handler consists of three values:
* 0: action1
* 1: action2
* 2: new emulation state
*
* action > 0: ButtonPress
* action = 0: nothing
* action < 0: ButtonRelease
*
* The comment preceeding each section is the current emulation state.
* The comments to the right are of the form
* <button state> (<events>) -> <new emulation state>
* which should be read as
* If the buttons are in <button state>, generate <events> then go to
* <new emulation state>.
*/
static const signed char stateTab[11][5][3] = {
/* 0 ground */
{
{ 0, 0, 0 }, /* nothing -> ground (no change) */
{ 0, 0, 1 }, /* left -> delayed left */
{ 0, 0, 2 }, /* right -> delayed right */
{ 2, 0, 3 }, /* left & right (middle press) -> pressed middle */
{ 0, 0, -1 } /* timeout N/A */
},
/* 1 delayed left */
{
{ 1, -1, 0 }, /* nothing (left event) -> ground */
{ 0, 0, 1 }, /* left -> delayed left (no change) */
{ 1, -1, 2 }, /* right (left event) -> delayed right */
{ 2, 0, 3 }, /* left & right (middle press) -> pressed middle */
{ 1, 0, 4 }, /* timeout (left press) -> pressed left */
},
/* 2 delayed right */
{
{ 3, -3, 0 }, /* nothing (right event) -> ground */
{ 3, -3, 1 }, /* left (right event) -> delayed left (no change) */
{ 0, 0, 2 }, /* right -> delayed right (no change) */
{ 2, 0, 3 }, /* left & right (middle press) -> pressed middle */
{ 3, 0, 5 }, /* timeout (right press) -> pressed right */
},
/* 3 pressed middle */
{
{ -2, 0, 0 }, /* nothing (middle release) -> ground */
{ 0, 0, 7 }, /* left -> released right */
{ 0, 0, 6 }, /* right -> released left */
{ 0, 0, 3 }, /* left & right -> pressed middle (no change) */
{ 0, 0, -1 }, /* timeout N/A */
},
/* 4 pressed left */
{
{ -1, 0, 0 }, /* nothing (left release) -> ground */
{ 0, 0, 4 }, /* left -> pressed left (no change) */
{ -1, 0, 2 }, /* right (left release) -> delayed right */
{ 3, 0, 10 }, /* left & right (right press) -> pressed both */
{ 0, 0, -1 }, /* timeout N/A */
},
/* 5 pressed right */
{
{ -3, 0, 0 }, /* nothing (right release) -> ground */
{ -3, 0, 1 }, /* left (right release) -> delayed left */
{ 0, 0, 5 }, /* right -> pressed right (no change) */
{ 1, 0, 10 }, /* left & right (left press) -> pressed both */
{ 0, 0, -1 }, /* timeout N/A */
},
/* 6 released left */
{
{ -2, 0, 0 }, /* nothing (middle release) -> ground */
{ -2, 0, 1 }, /* left (middle release) -> delayed left */
{ 0, 0, 6 }, /* right -> released left (no change) */
{ 1, 0, 8 }, /* left & right (left press) -> repressed left */
{ 0, 0, -1 }, /* timeout N/A */
},
/* 7 released right */
{
{ -2, 0, 0 }, /* nothing (middle release) -> ground */
{ 0, 0, 7 }, /* left -> released right (no change) */
{ -2, 0, 2 }, /* right (middle release) -> delayed right */
{ 3, 0, 9 }, /* left & right (right press) -> repressed right */
{ 0, 0, -1 }, /* timeout N/A */
},
/* 8 repressed left */
{
{ -2, -1, 0 }, /* nothing (middle release, left release) -> ground */
{ -2, 0, 4 }, /* left (middle release) -> pressed left */
{ -1, 0, 6 }, /* right (left release) -> released left */
{ 0, 0, 8 }, /* left & right -> repressed left (no change) */
{ 0, 0, -1 }, /* timeout N/A */
},
/* 9 repressed right */
{
{ -2, -3, 0 }, /* nothing (middle release, right release) -> ground */
{ -3, 0, 7 }, /* left (right release) -> released right */
{ -2, 0, 5 }, /* right (middle release) -> pressed right */
{ 0, 0, 9 }, /* left & right -> repressed right (no change) */
{ 0, 0, -1 }, /* timeout N/A */
},
/* 10 pressed both */
{
{ -1, -3, 0 }, /* nothing (left release, right release) -> ground */
{ -3, 0, 4 }, /* left (right release) -> pressed left */
{ -1, 0, 5 }, /* right (left release) -> pressed right */
{ 0, 0, 10 }, /* left & right -> pressed both (no change) */
{ 0, 0, -1 }, /* timeout N/A */
},
};
static CARD32
buttonTimer(DeviceInfoPtr device)
{
X68kMousePrivPtr pPriv;
sigset_t sigmask;
int id;
int type, button, flag;
ValuatorMask mask;
pPriv = device->public.devicePrivate;
(void)sigemptyset(&sigmask);
(void)sigaddset(&sigmask, SIGIO);
(void)sigprocmask(SIG_BLOCK, &sigmask, NULL);
pPriv->emulate3Pending = FALSE;
if ((id = stateTab[pPriv->emulateState][4][0]) != 0) {
button = abs(id);
type = (id >= 0) ? ButtonPress : ButtonRelease;
flag = POINTER_RELATIVE;
valuator_mask_zero(&mask);
QueuePointerEvents(device, type, button, flag, &mask);
pPriv->emulateState = stateTab[pPriv->emulateState][4][2];
} else {
LogMessageVerbSigSafe(X_WARNING, -1,
"Got unexpected buttonTimer in state %d\n", pPriv->emulateState);
}
(void)sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
return 0;
}
static void
Emulate3ButtonsSetEnabled(DeviceInfoPtr device, Bool enable)
{
X68kMousePrivPtr pPriv = device->public.devicePrivate;
if (pPriv->emulate3Buttons == enable)
return;
pPriv->emulate3Buttons = enable;
if (enable) {
pPriv->emulateState = 0;
pPriv->emulate3Pending = FALSE;
pPriv->emulate3ButtonsSoft = FALSE; /* specifically requested now */
RegisterBlockAndWakeupHandlers(MouseBlockHandler, MouseWakeupHandler,
(void *)device);
} else {
if (pPriv->emulate3Pending)
buttonTimer(device);
RemoveBlockAndWakeupHandlers(MouseBlockHandler, MouseWakeupHandler,
(void *)device);
}
}
static Bool
Emulate3ButtonsSoft(DeviceInfoPtr device)
{
X68kMousePrivPtr pPriv;
pPriv = device->public.devicePrivate;
if (!pPriv->emulate3ButtonsSoft)
return TRUE;
#if defined(__NetBSD__) && defined(WSCONS_SUPPORT)
/*
* On NetBSD a wsmouse is a multiplexed device. Imagine a notebook
* with two-button mousepad, and an external USB mouse plugged in
* temporarily. After using button 3 on the external mouse and
* unplugging it again, the mousepad will still need to emulate
* 3 buttons.
*/
return TRUE;
#else
LogMessageVerbSigSafe(X_INFO, 4,
"mouse: 3rd Button detected: disabling emulate3Button\n");
Emulate3ButtonsSetEnabled(device, FALSE);
return FALSE;
#endif
}
static void
MouseBlockHandler(void *data, void *waitTime)
{
DeviceInfoPtr device = data;
X68kMousePrivPtr pPriv = device->public.devicePrivate;
int ms;
if (pPriv->emulate3Pending) {
ms = pPriv->emulate3Expires - GetTimeInMillis();
if (ms <= 0)
ms = 0;
AdjustWaitForDelay(waitTime, ms);
}
}
static void
MouseWakeupHandler(void *data, int i)
{
DeviceInfoPtr device = data;
X68kMousePrivPtr pPriv = device->public.devicePrivate;
int ms;
if (pPriv->emulate3Pending) {
ms = pPriv->emulate3Expires - GetTimeInMillis();
if (ms <= 0)
buttonTimer(device);
}
}
/*******************************************************************
*
* Post mouse events
*
*******************************************************************/
static void
MouseDoPostEvent(DeviceInfoPtr device, int buttons, int dx, int dy)
{
X68kMousePrivPtr pPriv;
int emulateButtons;
int id, change;
int ms;
int type, button;
ValuatorMask mask;
pPriv = device->public.devicePrivate;
change = buttons ^ pPriv->lastMappedButtons;
pPriv->lastMappedButtons = buttons;
if (pPriv->emulate3ButtonsSoft && pPriv->emulate3Pending && (dx || dy))
buttonTimer(device);
if (dx || dy) {
type = MotionNotify;
flag = POINTER_RELATIVE | POINTER_ACCELERATE;
valuator[0] = dx;
valuator[1] = dy;
valuator_mask_set_range(&mask, 0, 2, valuators);
QueuePointerEvents(device, type, 0, flag, &mask);
}
if (change) {
if (pPriv->emulate3Buttons
&& (!(buttons & 0x02) || Emulate3ButtonsSoft(device))) {
/* handle all but buttons 1 & 3 normally */
change &= ~(0x01 | 0x04);
/* emulate the third button by the other two */
emulateButtons = (buttons & 0x01) | ((buttons & 0x04) >> 1);
if ((id = stateTab[pPriv->emulateState][emulateButtons][0]) != 0) {
button = abs(id);
type = (id >= 0) ? ButtonPress : ButtonRelease;
flag = POINTER_RELATIVE;
valuator_mask_zero(&mask);
QueuePointerEvents(device, type, button, flag, &mask);
}
if ((id = stateTab[pPriv->emulateState][emulateButtons][1]) != 0) {
button = abs(id);
type = (id >= 0) ? ButtonPress : ButtonRelease;
flag = POINTER_RELATIVE;
valuator_mask_zero(&mask);
QueuePointerEvents(device, type, button, flag, &mask);
}
pPriv->emulateState =
stateTab[pPriv->emulateState][emulateButtons][2];
if (stateTab[pPriv->emulateState][4][0] != 0) {
pPriv->emulate3Expires =
GetTimeInMillis() + pPriv->emulate3Timeout;
pPriv->emulate3Pending = TRUE;
} else {
pPriv->emulate3Pending = FALSE;
}
}
while (change) {
id = ffs(change);
change &= ~(1 << (id - 1));
button = id;
type = (buttons & (1 << (id - 1))) ? ButtonPress : ButtonRelease;
flag = POINTER_RELATIVE;
valuator_mask_zero(&mask);
QueuePointerEvents(device, type, button, flag, &mask);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment