Import of ManyMouse library

This commit is contained in:
pagefault
2006-03-23 20:45:51 +00:00
parent 2219632c9e
commit 29637130ff
8 changed files with 7745 additions and 6439 deletions

View File

@@ -51,10 +51,11 @@ JMAOBJ=${JMADIR}/7zlzma.o ${JMADIR}/crc32.o ${JMADIR}/iiostrm.o\
${JMADIR}/winout.o ${JMADIR}/zsnesjma.o
MAINOBJ=cfgload.o cfg.o endmem.o init.o initc.o md.o uic.o patch.o ui.o\
vcache.o version.o zmovie.o zstate.o debug.o zloader.o
vcache.o version.o zmovie.o zstate.o debug.o zloader.o manymouse.o\
linux_evdev.o
NETOBJ=
#${NETDIR}/ztcp.o
#${NETDIR}/ztcp.os
TOOLSOBJ=${TOOLSDIR}/fileutil.o ${TOOLSDIR}/strutil.o
@@ -154,6 +155,8 @@ version.o: version.c
zloader.o: zloader.c gblhdr.h
zmovie.o: zmovie.c asm_call.h gblhdr.h gblvars.h numconv.h ${WINDIR}/safelib.h md.o
zstate.o: zstate.c asm_call.h gblhdr.h gblvars.h numconv.h
manymouse.o: manymouse.c
linux_evdev.o: linux_evdev.c
${CHIPDIR}/c4emu.o: ${CHIPDIR}/c4emu.c gblhdr.h
${CHIPDIR}/dsp1emu.o: ${CHIPDIR}/dsp1emu.c gblhdr.h

View File

@@ -682,6 +682,9 @@ CombContDatN dd 08000000h,04000000h,02000000h,01000000h,00800000h,80000000h
dd 00400000h,40000000h,00200000h,00100000h,10000000h,20000000h
CombContDatR dd 08000000h,04000000h,01000000h,02000000h,00800000h,80000000h
dd 00400000h,40000000h,00200000h,00100000h,10000000h,20000000h
EXTSYM MouseToRead,multimouse
SECTION .text
%macro PlayerDeviceFix 1
@@ -715,8 +718,12 @@ NEWSYM ReadInputDevice
; Process Data
mov dword[JoyAOrig],0
; Get Player1 input device
cmp byte[multimouse],1
je .multimouse1
cmp byte[snesmouse],1
jne .nomouse1
.multimouse1
mov byte[MouseToRead],1
call processmouse
ProcSNESMouse JoyAOrig
jmp .noinput1
@@ -765,8 +772,12 @@ NEWSYM ReadInputDevice
and dword[JoyAOrig],7FFFFFFFh
.noinput1
mov dword[JoyBOrig],0
cmp byte[multimouse],1
je .multimouse2
cmp byte[snesmouse],2
jne .nomouse2
.multimouse2
mov byte[MouseToRead],2
call processmouse
ProcSNESMouse JoyBOrig
jmp .noinput2

337
zsnes/src/linux_evdev.c Normal file
View File

@@ -0,0 +1,337 @@
/*
* Support for Linux evdevs...the /dev/input/event* devices.
*
* Please see the file LICENSE in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#ifdef __linux__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h> /* evdev interface... */
#include "manymouse.h"
#define test_bit(array, bit) (array[bit/8] & (1<<(bit%8)))
/* linux allows 32 evdev nodes currently. */
#define MAX_MICE 32
typedef struct
{
int fd;
int min_x;
int min_y;
int max_x;
int max_y;
char name[64];
} MouseStruct;
static MouseStruct mice[MAX_MICE];
static unsigned int available_mice = 0;
static int poll_mouse(MouseStruct *mouse, ManyMouseEvent *outevent)
{
int unhandled = 1;
while (unhandled) /* read until failure or valid event. */
{
struct input_event event;
int br = read(mouse->fd, &event, sizeof (event));
if (br == -1)
{
if (errno == EAGAIN)
return(0); /* just no new data at the moment. */
/* mouse was unplugged? */
close(mouse->fd); /* stop reading from this mouse. */
mouse->fd = -1;
outevent->type = MANYMOUSE_EVENT_DISCONNECT;
return(1);
} /* if */
if (br != sizeof (event))
return(0); /* oh well. */
unhandled = 0; /* will reset if necessary. */
outevent->value = event.value;
if (event.type == EV_REL)
{
outevent->type = MANYMOUSE_EVENT_RELMOTION;
if ((event.code == REL_X) || (event.code == REL_DIAL))
outevent->item = 0;
else if (event.code == REL_Y)
outevent->item = 1;
else if (event.code == REL_WHEEL)
{
outevent->type = MANYMOUSE_EVENT_SCROLL;
outevent->item = 0;
} /* else if */
else if (event.code == REL_HWHEEL)
{
outevent->type = MANYMOUSE_EVENT_SCROLL;
outevent->item = 1;
} /* else if */
else
{
unhandled = 1;
} /* else */
} /* if */
else if (event.type == EV_ABS)
{
outevent->type = MANYMOUSE_EVENT_ABSMOTION;
if (event.code == ABS_X)
{
outevent->item = 0;
outevent->minval = mouse->min_x;
outevent->maxval = mouse->max_x;
} /* if */
else if (event.code == ABS_Y)
{
outevent->item = 1;
outevent->minval = mouse->min_y;
outevent->maxval = mouse->max_y;
} /* if */
else
{
unhandled = 1;
} /* else */
} /* else if */
else if (event.type == EV_KEY)
{
outevent->type = MANYMOUSE_EVENT_BUTTON;
if ((event.code >= BTN_LEFT) && (event.code <= BTN_BACK))
outevent->item = event.code - BTN_MOUSE;
/* just in case some device uses this block of events instead... */
else if ((event.code >= BTN_MISC) && (event.code <= BTN_LEFT))
outevent->item = (event.code - BTN_MISC);
else if (event.code == BTN_TOUCH) /* tablet... */
outevent->item = 0;
else if (event.code == BTN_STYLUS) /* tablet... */
outevent->item = 1;
else if (event.code == BTN_STYLUS2) /* tablet... */
outevent->item = 2;
else
{
/*printf("unhandled mouse button: 0x%X\n", event.code);*/
unhandled = 1;
} /* else */
} /* else if */
else
{
unhandled = 1;
} /* else */
} /* while */
return(1); /* got a valid event */
} /* poll_mouse */
static int init_mouse(const char *fname, int fd)
{
MouseStruct *mouse = &mice[available_mice];
int has_absolutes = 0;
int is_mouse = 0;
unsigned char relcaps[(REL_MAX / 8) + 1];
unsigned char abscaps[(ABS_MAX / 8) + 1];
unsigned char keycaps[(KEY_MAX / 8) + 1];
memset(relcaps, '\0', sizeof (relcaps));
memset(abscaps, '\0', sizeof (abscaps));
memset(keycaps, '\0', sizeof (keycaps));
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof (keycaps)), keycaps) == -1)
return 0; /* gotta have some buttons! :) */
if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof (relcaps)), relcaps) != -1)
{
if ( (test_bit(relcaps, REL_X)) && (test_bit(relcaps, REL_Y)) )
{
if (test_bit(keycaps, BTN_MOUSE))
is_mouse = 1;
} /* if */
#if ALLOW_DIALS_TO_BE_MICE
if (test_bit(relcaps, REL_DIAL))
is_mouse = 1; // griffin powermate?
#endif
} /* if */
if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof (abscaps)), abscaps) != -1)
{
if ( (test_bit(abscaps, ABS_X)) && (test_bit(abscaps, ABS_Y)) )
{
/* might be a touchpad... */
if (test_bit(keycaps, BTN_TOUCH))
{
is_mouse = 1; /* touchpad, touchscreen, or tablet. */
has_absolutes = 1;
} /* if */
} /* if */
} /* if */
if (!is_mouse)
return 0;
mouse->min_x = mouse->min_y = mouse->max_x = mouse->max_y = 0;
if (has_absolutes)
{
struct input_absinfo absinfo;
if (ioctl(fd, EVIOCGABS(ABS_X), &absinfo) == -1)
return 0;
mouse->min_x = absinfo.minimum;
mouse->max_x = absinfo.maximum;
if (ioctl(fd, EVIOCGABS(ABS_Y), &absinfo) == -1)
return 0;
mouse->min_y = absinfo.minimum;
mouse->max_y = absinfo.maximum;
} /* if */
if (ioctl(fd, EVIOCGNAME(sizeof (mouse->name)), mouse->name) == -1)
snprintf(mouse->name, sizeof (mouse->name), "Unknown device");
mouse->fd = fd;
return 1; /* we're golden. */
} /* init_mouse */
/* Return a file descriptor if this is really a mouse, -1 otherwise. */
static int open_if_mouse(const char *fname)
{
struct stat statbuf;
int fd;
int devmajor, devminor;
if (stat(fname, &statbuf) == -1)
return 0;
if (S_ISCHR(statbuf.st_mode) == 0)
return 0; /* not a character device... */
/* evdev node ids are major 13, minor 64-96. Is this safe to check? */
devmajor = (statbuf.st_rdev & 0xFF00) >> 8;
devminor = (statbuf.st_rdev & 0x00FF);
if ( (devmajor != 13) || (devminor < 64) || (devminor > 96) )
return 0; /* not an evdev. */
if ((fd = open(fname, O_RDONLY | O_NONBLOCK)) == -1)
return 0;
if (init_mouse(fname, fd))
return 1;
close(fd);
return 0;
} /* open_if_mouse */
static int linux_evdev_init(void)
{
DIR *dirp;
struct dirent *dent;
int i;
for (i = 0; i < MAX_MICE; i++)
mice[i].fd = -1;
dirp = opendir("/dev/input");
if (!dirp)
return 0;
while ((dent = readdir(dirp)) != NULL)
{
char fname[128];
snprintf(fname, sizeof (fname), "/dev/input/%s", dent->d_name);
if (open_if_mouse(fname))
available_mice++;
} /* while */
closedir(dirp);
return available_mice;
} /* linux_evdev_init */
static void linux_evdev_quit(void)
{
while (available_mice)
{
int fd = mice[available_mice--].fd;
if (fd != -1)
close(fd);
} /* while */
} /* linux_evdev_quit */
static const char *linux_evdev_name(unsigned int index)
{
if (index < available_mice)
return(mice[index].name);
return(NULL);
} /* linux_evdev_name */
static int linux_evdev_poll(ManyMouseEvent *event)
{
/*
* (i) is static so we iterate through all mice round-robin. This
* prevents a chatty mouse from dominating the queue.
*/
static unsigned int i = 0;
if (i >= available_mice)
i = 0; /* handle reset condition. */
if (event != NULL)
{
while (i < available_mice)
{
MouseStruct *mouse = &mice[i];
if (mouse->fd != -1)
{
if (poll_mouse(mouse, event))
{
event->device = i;
return(1);
} /* if */
} /* if */
i++;
} /* while */
} /* if */
return(0); /* no new events */
} /* linux_evdev_poll */
ManyMouseDriver ManyMouseDriver_evdev =
{
linux_evdev_init,
linux_evdev_quit,
linux_evdev_name,
linux_evdev_poll
};
#endif /* defined __linux__ */
/* end of linux_evdev.c ... */

92
zsnes/src/manymouse.c Normal file
View File

@@ -0,0 +1,92 @@
/*
* ManyMouse foundation code; apps talks to this and it talks to the lowlevel
* code for various platforms.
*
* Please see the file LICENSE in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#include <stdlib.h>
#include "manymouse.h"
static const char *manymouse_copyright =
"ManyMouse " MANYMOUSE_VERSION " (c) 2005 Ryan C. Gordon.";
extern const ManyMouseDriver ManyMouseDriver_windows;
extern const ManyMouseDriver ManyMouseDriver_evdev;
extern const ManyMouseDriver ManyMouseDriver_mousedev;
extern const ManyMouseDriver ManyMouseDriver_hidmanager;
extern const ManyMouseDriver ManyMouseDriver_xinput;
static const ManyMouseDriver *mice_drivers[] =
{
#if SUPPORT_XINPUT
&ManyMouseDriver_xinput,
#endif
#if ((defined _WIN32) || defined(__CYGWIN__))
&ManyMouseDriver_windows,
#endif
#ifdef __linux__
&ManyMouseDriver_evdev,
/*&ManyMouseDriver_mousedev,*/
#endif
#if ( (defined(__MACH__)) && (defined(__APPLE__)) )
&ManyMouseDriver_hidmanager,
#endif
NULL
};
static const ManyMouseDriver *driver = NULL;
int ManyMouse_Init(void)
{
int i;
/* impossible test to keep manymouse_copyright linked into the binary. */
if ((char *) driver == (const char *) manymouse_copyright)
return(-1);
if (driver != NULL)
return(-1);
for (i = 0; mice_drivers[i]; i++)
{
int mice = mice_drivers[i]->init();
if (mice >= 0)
{
driver = mice_drivers[i];
return(mice);
} /* if */
} /* for */
return(-1);
} /* ManyMouse_Init */
void ManyMouse_Quit(void)
{
if (driver != NULL)
driver->quit();
driver = NULL;
} /* ManyMouse_Quit */
const char *ManyMouse_DeviceName(unsigned int index)
{
if (driver != NULL)
return(driver->name(index));
return(NULL);
} /* ManyMouse_PollEvent */
int ManyMouse_PollEvent(ManyMouseEvent *event)
{
if (driver != NULL)
return(driver->poll(event));
return(0);
} /* ManyMouse_PollEvent */
/* end of manymouse.c ... */

61
zsnes/src/manymouse.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* ManyMouse main header. Include this from your app.
*
* Please see the file LICENSE in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#ifndef _INCLUDE_MANYMOUSE_H_
#define _INCLUDE_MANYMOUSE_H_
#ifdef __cplusplus
extern "C" {
#endif
#define MANYMOUSE_VERSION "0.0.1"
typedef enum
{
MANYMOUSE_EVENT_ABSMOTION = 0,
MANYMOUSE_EVENT_RELMOTION,
MANYMOUSE_EVENT_BUTTON,
MANYMOUSE_EVENT_SCROLL,
MANYMOUSE_EVENT_DISCONNECT,
MANYMOUSE_EVENT_MAX
} ManyMouseEventType;
typedef struct
{
ManyMouseEventType type;
unsigned int device;
unsigned int item;
int value;
int minval;
int maxval;
} ManyMouseEvent;
/* internal use only. */
typedef struct
{
int (*init)(void);
void (*quit)(void);
const char *(*name)(unsigned int index);
int (*poll)(ManyMouseEvent *event);
} ManyMouseDriver;
int ManyMouse_Init(void);
void ManyMouse_Quit(void);
const char *ManyMouse_DeviceName(unsigned int index);
int ManyMouse_PollEvent(ManyMouseEvent *event);
#ifdef __cplusplus
}
#endif
#endif /* !defined _INCLUDE_MANYMOUSE_H_ */
/* end of manymouse.h ... */

View File

@@ -30,6 +30,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#endif
#include "asm_call.h"
#include "manymouse.h"
//C++ style code in C
#define bool unsigned char
@@ -101,11 +102,11 @@ unsigned char previdmode; // previous video mode
unsigned char cbitmode; // bit mode, 0=8bit, 1=16bit
unsigned char opexec268 = 155; // # of opcodes/scanline in 2.68Mhz mode
unsigned char opexec358 = 172; // # of opcodes/scanline in 3.58Mhz mode (228/180)
unsigned char opexec358 = 142; // # of opcodes/scanline in 3.58Mhz mode (228/180)
unsigned char opexec268cph = 42; // # of opcodes/hblank in 2.68Mhz mode
unsigned char opexec358cph = 45; // # of opcodes/hblank in 3.58Mhz mode (56/50)
unsigned char opexec268b = 155; // # of opcodes/scanline in 2.68Mhz mode
unsigned char opexec358b = 172; // # of opcodes/scanline in 3.58Mhz mode (228/180)
unsigned char opexec268b = 142; // # of opcodes/scanline in 2.68Mhz mode
unsigned char opexec358b = 155; // # of opcodes/scanline in 3.58Mhz mode (228/180)
unsigned char opexec268cphb = 42; // # of opcodes/hblank in 2.68Mhz mode
unsigned char opexec358cphb = 45; // # of opcodes/hblank in 3.58Mhz mode (56/50)
unsigned char debugdisble = 1; // debugger disable. 0 = no, 1 = yes
@@ -149,6 +150,7 @@ void allocmem();
void InitSPC();
void SystemInit();
void StartUp();
void MultiMouseInit();
void *doMemAlloc(size_t size)
{
@@ -269,6 +271,10 @@ void zstart ()
puts("May or may not be complete");
#endif
#ifndef __MSDOS__
MultiMouseInit();
#endif
asm_call(SystemInit);
@@ -358,6 +364,8 @@ static char *seconds_to_asc(unsigned int seconds)
return(buffer);
}
unsigned char multiMouseMode = 0;
extern unsigned int MessageOn;
extern unsigned int MsgCount;
extern char CSStatus[70];
@@ -415,3 +423,45 @@ void DisplayBatteryStatus()
#endif
}
void MultiMouseShutdown()
{
multiMouseMode = 0;
ManyMouse_Quit();
}
// Make use of multiple mice.
int numMice = 0;
int Mouse1MoveX = 0;
int Mouse2MoveX = 0;
int Mouse1MoveY = 0;
int Mouse2MoveY = 0;
unsigned char MouseToRead = 0;
unsigned char multimouse = 1; // Enabled
extern void WriteLine();
void MultiMouseInit()
{
int mice = ManyMouse_Init();
printf("ManyMouse: %d mice detected.", mice);
multiMouseMode = 1;
}
void MultiMouseProcess()
{
ManyMouseEvent event;
ManyMouse_PollEvent(&event);
if (event.type == MANYMOUSE_EVENT_RELMOTION)
{
if (event.device == 0)
if (event.item == 0) { Mouse1MoveX = event.value; } else { Mouse1MoveY = event.value; }
else
if (event.item == 0) { Mouse2MoveX = event.value; } else { Mouse2MoveY = event.value; }
}
}

View File

@@ -143,7 +143,9 @@ NEWSYM showvideo
pop esi
ret
%ifndef __MSDOS__
EXTSYM multiMouseMode,Mouse1MoveX,Mouse1MoveY,Mouse2MoveX,Mouse2MoveY,MultiMouseProcess,MouseToRead
%endif
NEWSYM processmouse
push esi
@@ -171,7 +173,23 @@ NEWSYM processmouse
.noautosw
mov byte[ssautoswb],0
.ss
; cmp byte[multiMouseMode],0
; je .nomultimouse
pushad
call MultiMouseProcess
popad
cmp byte[MouseToRead],2
; je .getmouse2
mov cx,[Mouse1MoveX]
mov dx,[Mouse1MoveY]
jmp .mousestuff
.getmouse2
mov cx,[Mouse2MoveX]
mov dx,[Mouse2MoveY]
jmp .mousestuff
.nomultimouse
call Get_MousePositionDisplacement
.mousestuff
cmp byte[snesmouse],4
je .le
cmp byte[snesmouse],3

734
zsnes/src/windows_wminput.c Normal file
View File

@@ -0,0 +1,734 @@
/*
* Support for Windows via the WM_INPUT message.
*
* Please see the file LICENSE in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#if (defined(_WIN32) || defined(__CYGWIN__))
/* WinUser.h won't include rawinput stuff without this... */
#if (_WIN32_WINNT < 0x0501)
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <windows.h>
#include <malloc.h> /* needed for alloca(). */
/* Cygwin's headers don't have WM_INPUT right now... */
#ifndef WM_INPUT
#define WM_INPUT 0x00FF
#endif
#include "manymouse.h"
/* that should be enough, knock on wood. */
#define MAX_MICE 32
/*
* Just trying to avoid malloc() here...we statically allocate a buffer
* for events and treat it as a ring buffer.
*/
/* !!! FIXME: tweak this? */
#define MAX_EVENTS 1024
static ManyMouseEvent input_events[MAX_EVENTS];
static volatile int input_events_read = 0;
static volatile int input_events_write = 0;
static int available_mice = 0;
static int did_api_lookup = 0;
static HWND raw_hwnd = NULL;
static const char *class_name = "ManyMouseRawInputCatcher";
static const char *win_name = "ManyMouseRawInputMsgWindow";
static ATOM class_atom = 0;
static CRITICAL_SECTION mutex;
typedef struct
{
HANDLE handle;
char name[256];
} MouseStruct;
static MouseStruct mice[MAX_MICE];
/*
* The RawInput APIs only exist in Windows XP and later, so you want this
* to fail gracefully on earlier systems instead of refusing to start the
* process due to missing symbols. To this end, we do a symbol lookup on
* User32.dll, etc to get the entry points.
*
* A lot of these are available all the way back to the start of win32 in
* Windows 95 and WinNT 3.1, but just so you don't have to track down any
* import libraries, I've added those here, too. That fits well with the
* idea of just adding the sources to your build and going forward.
*/
static UINT (WINAPI *pGetRawInputDeviceList)(
PRAWINPUTDEVICELIST pRawInputDeviceList,
PUINT puiNumDevices,
UINT cbSize
);
/* !!! FIXME: use unicode version */
static UINT (WINAPI *pGetRawInputDeviceInfoA)(
HANDLE hDevice,
UINT uiCommand,
LPVOID pData,
PUINT pcbSize
);
static BOOL (WINAPI *pRegisterRawInputDevices)(
PCRAWINPUTDEVICE pRawInputDevices,
UINT uiNumDevices,
UINT cbSize
);
static LRESULT (WINAPI *pDefRawInputProc)(
PRAWINPUT *paRawInput,
INT nInput,
UINT cbSizeHeader
);
static UINT (WINAPI *pGetRawInputBuffer)(
PRAWINPUT pData,
PUINT pcbSize,
UINT cbSizeHeader
);
static UINT (WINAPI *pGetRawInputData)(
HRAWINPUT hRawInput,
UINT uiCommand,
LPVOID pData,
PUINT pcbSize,
UINT cbSizeHeader
);
static LONG (WINAPI *pRegQueryValueExA)(
HKEY hKey,
LPCTSTR lpValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData
);
static LONG (WINAPI *pRegOpenKeyExA)(
HKEY hKey,
LPCTSTR lpSubKey,
DWORD ulOptions,
REGSAM samDesired,
PHKEY phkResult
);
static LONG (WINAPI *pRegCloseKey)(
HKEY hKey
);
static HWND (WINAPI *pCreateWindowExA)(
DWORD dwExStyle,
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
static ATOM (WINAPI *pRegisterClassExA)(
CONST WNDCLASSEX *lpwcx
);
static LRESULT (WINAPI *pDefWindowProcA)(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
static BOOL (WINAPI *pUnregisterClassA)(
LPCTSTR lpClassName,
HINSTANCE hInstance
);
static HMODULE (WINAPI *pGetModuleHandleA)(
LPCTSTR lpModuleName
);
static BOOL (WINAPI *pPeekMessageA)(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
);
static BOOL (WINAPI *pTranslateMessage)(
const MSG *lpMsg
);
static LRESULT (WINAPI *pDispatchMessageA)(
const MSG *lpmsg
);
static BOOL (WINAPI *pDestroyWindow)(
HWND hWnd
);
static void (WINAPI *pInitializeCriticalSection)(
LPCRITICAL_SECTION lpCriticalSection
);
static void (WINAPI *pEnterCriticalSection)(
LPCRITICAL_SECTION lpCriticalSection
);
static void (WINAPI *pLeaveCriticalSection)(
LPCRITICAL_SECTION lpCriticalSection
);
static void (WINAPI *pDeleteCriticalSection)(
LPCRITICAL_SECTION lpCriticalSection
);
static int symlookup(HMODULE dll, void **addr, const char *sym)
{
*addr = GetProcAddress(dll, sym);
if (*addr == NULL)
{
FreeLibrary(dll);
return(0);
} /* if */
return(1);
} /* symlookup */
static int find_api_symbols(void)
{
HMODULE dll;
if (did_api_lookup)
return(1);
#define LOOKUP(x) { if (!symlookup(dll, (void **) &p##x, #x)) return(0); }
dll = LoadLibrary("user32.dll");
if (dll == NULL)
return(0);
LOOKUP(GetRawInputDeviceInfoA);
LOOKUP(RegisterRawInputDevices);
LOOKUP(GetRawInputDeviceList);
LOOKUP(DefRawInputProc);
LOOKUP(GetRawInputBuffer);
LOOKUP(GetRawInputData);
LOOKUP(CreateWindowExA);
LOOKUP(RegisterClassExA);
LOOKUP(UnregisterClassA);
LOOKUP(DefWindowProcA);
LOOKUP(PeekMessageA);
LOOKUP(TranslateMessage);
LOOKUP(DispatchMessageA);
LOOKUP(DestroyWindow);
dll = LoadLibrary("advapi32.dll");
if (dll == NULL)
return(0);
LOOKUP(RegOpenKeyExA);
LOOKUP(RegQueryValueExA);
LOOKUP(RegCloseKey);
dll = LoadLibrary("kernel32.dll");
if (dll == NULL)
return(0);
LOOKUP(GetModuleHandleA);
LOOKUP(InitializeCriticalSection);
LOOKUP(EnterCriticalSection);
LOOKUP(LeaveCriticalSection);
LOOKUP(DeleteCriticalSection);
#undef LOOKUP
did_api_lookup = 1;
return(1);
} /* find_api_symbols */
static void queue_event(const ManyMouseEvent *event)
{
/* copy the event info. We'll process it in ManyMouse_PollEvent(). */
CopyMemory(&input_events[input_events_write], event, sizeof (ManyMouseEvent));
input_events_write = ((input_events_write + 1) % MAX_EVENTS);
/* Ring buffer full? Lose oldest event. */
if (input_events_write == input_events_read)
{
/* !!! FIXME: we need to not lose mouse buttons here. */
input_events_read = ((input_events_read + 1) % MAX_EVENTS);
} /* if */
} /* queue_event */
static void queue_from_rawinput(const RAWINPUT *raw)
{
int i;
const RAWINPUTHEADER *header = &raw->header;
const RAWMOUSE *mouse = &raw->data.mouse;
ManyMouseEvent event;
if (raw->header.dwType != RIM_TYPEMOUSE)
return;
for (i = 0; i < available_mice; i++) /* find the device for event. */
{
if (mice[i].handle == header->hDevice)
break;
} /* for */
if (i == available_mice)
return; /* not found?! */
/*
* RAWINPUT packs a bunch of events into one, so we split it up into
* a bunch of ManyMouseEvents here and store them in an internal queue.
* Then ManyMouse_PollEvent() just shuffles items off that queue
* without any complicated processing.
*/
event.device = i;
pEnterCriticalSection(&mutex);
if (mouse->usFlags & MOUSE_MOVE_ABSOLUTE)
{
/* !!! FIXME: How do we get the min and max values for absmotion? */
event.type = MANYMOUSE_EVENT_ABSMOTION;
event.item = 0;
event.value = mouse->lLastX;
queue_event(&event);
event.item = 1;
event.value = mouse->lLastY;
queue_event(&event);
} /* if */
else /*if (mouse->usFlags & MOUSE_MOVE_RELATIVE)*/
{
event.type = MANYMOUSE_EVENT_RELMOTION;
if (mouse->lLastX != 0)
{
event.item = 0;
event.value = mouse->lLastX;
queue_event(&event);
} /* if */
if (mouse->lLastY != 0)
{
event.item = 1;
event.value = mouse->lLastY;
queue_event(&event);
} /* if */
} /* else if */
event.type = MANYMOUSE_EVENT_BUTTON;
#define QUEUE_BUTTON(x) { \
if (mouse->usButtonFlags & RI_MOUSE_BUTTON_##x##_DOWN) { \
event.item = x-1; \
event.value = 1; \
queue_event(&event); \
} \
if (mouse->usButtonFlags & RI_MOUSE_BUTTON_##x##_UP) { \
event.item = x-1; \
event.value = 0; \
queue_event(&event); \
} \
}
QUEUE_BUTTON(1);
QUEUE_BUTTON(2);
QUEUE_BUTTON(3);
QUEUE_BUTTON(4);
QUEUE_BUTTON(5);
#undef QUEUE_BUTTON
if (mouse->usButtonFlags & RI_MOUSE_WHEEL)
{
if (mouse->usButtonData != 0) /* !!! FIXME: can this ever be zero? */
{
event.type = MANYMOUSE_EVENT_SCROLL;
event.item = 0; /* !!! FIXME: horizontal wheel? */
event.value = (mouse->usButtonData > 0) ? 1 : -1;
queue_event(&event);
} /* if */
} /* if */
pLeaveCriticalSection(&mutex);
} /* queue_from_rawinput */
static void wminput_handler(WPARAM wParam, LPARAM lParam)
{
UINT dwSize = 0;
LPBYTE lpb;
pGetRawInputData((HRAWINPUT) lParam, RID_INPUT, NULL, &dwSize,
sizeof (RAWINPUTHEADER));
if (dwSize < sizeof (RAWINPUT))
return; /* unexpected packet? */
lpb = (LPBYTE) alloca(dwSize);
if (lpb == NULL)
return;
if (pGetRawInputData((HRAWINPUT) lParam, RID_INPUT, lpb, &dwSize,
sizeof (RAWINPUTHEADER)) != dwSize)
return;
queue_from_rawinput((RAWINPUT *) lpb);
} /* wminput_handler */
static LRESULT CALLBACK RawWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
if (Msg == WM_INPUT)
wminput_handler(wParam, lParam);
else if (Msg == WM_DESTROY)
return(0);
return pDefWindowProcA(hWnd, Msg, wParam, lParam);
} /* RawWndProc */
static int init_event_queue(void)
{
HINSTANCE hInstance = pGetModuleHandleA(NULL);
WNDCLASSEX wce;
RAWINPUTDEVICE rid;
ZeroMemory(input_events, sizeof (input_events));
input_events_read = input_events_write = 0;
ZeroMemory(&wce, sizeof (wce));
wce.cbSize = sizeof(WNDCLASSEX);
wce.lpfnWndProc = RawWndProc;
wce.lpszClassName = class_name;
wce.hInstance = hInstance;
class_atom = pRegisterClassExA(&wce);
if (class_atom == 0)
return(0);
raw_hwnd = pCreateWindowExA(0, class_name, win_name, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, HWND_MESSAGE, NULL, hInstance, NULL);
if (raw_hwnd == NULL)
return(0);
pInitializeCriticalSection(&mutex);
ZeroMemory(&rid, sizeof (rid));
rid.usUsagePage = 1; /* GenericDesktop page */
rid.usUsage = 2; /* GeneralDestop Mouse usage. */
rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = raw_hwnd;
if (!pRegisterRawInputDevices(&rid, 1, sizeof (rid)))
{
pDeleteCriticalSection(&mutex);
return(0);
} /* if */
return(1);
} /* init_event_queue */
static void cleanup_window(void)
{
if (raw_hwnd)
{
MSG Msg;
pDestroyWindow(raw_hwnd);
while (pPeekMessageA(&Msg, raw_hwnd, 0, 0, PM_REMOVE))
{
pTranslateMessage(&Msg);
pDispatchMessageA(&Msg);
} /* while */
raw_hwnd = 0;
} /* if */
if (class_atom)
{
pUnregisterClassA(class_name, pGetModuleHandleA(NULL));
class_atom = 0;
} /* if */
} /* cleanup_window */
static int accept_device(const RAWINPUTDEVICELIST *dev)
{
const char rdp_ident[] = "\\??\\Root#RDP_MOU#0000#";
char *buf = NULL;
UINT ct = 0;
if (dev->dwType != RIM_TYPEMOUSE)
return(0); /* keyboard or some other fruity thing. */
if (pGetRawInputDeviceInfoA(dev->hDevice, RIDI_DEVICENAME, NULL, &ct) < 0)
return(0);
/* ct == is chars, not bytes, but we used the ASCII version. */
buf = (char *) alloca(ct);
if (buf == NULL)
return(0);
if (pGetRawInputDeviceInfoA(dev->hDevice, RIDI_DEVICENAME, buf, &ct) < 0)
return(0);
/*
* Apparently there's a fake "RDP" device...I guess this is
* "Remote Desktop Protocol" for controlling the system pointer
* remotely via Windows Remote Desktop, but that's just a guess.
* At any rate, we don't want that device, so skip it if detected.
*
* Idea for this found here:
* http://link.mywwwserver.com/~jstookey/arcade/rawmouse/raw_mouse.c
*/
/* avoiding memcmp here so we don't get a C runtime dependency... */
if (ct >= sizeof (rdp_ident) - 1)
{
int i;
for (i = 0; i < sizeof (rdp_ident) - 1; i++)
{
if (buf[i] != rdp_ident[i])
break;
} /* for */
if (i == sizeof (rdp_ident) - 1)
return(0); /* this is an RDP thing. Skip this device. */
} /* if */
return(1); /* we want this device. */
} /* accept_device */
/* !!! FIXME: this code sucks. */
static void get_device_product_name(char *name, size_t namesize,
const RAWINPUTDEVICELIST *dev)
{
const char regkeyroot[] = "System\\CurrentControlSet\\Enum\\";
const char default_device_name[] = "Unidentified input device";
DWORD outsize = namesize;
DWORD regtype = REG_SZ;
char *buf = NULL;
char *ptr = NULL;
char *keyname = NULL;
UINT i = 0;
UINT ct = 0;
LONG rc = 0;
HKEY hkey;
*name = '\0'; /* really insane default. */
if (sizeof (default_device_name) >= namesize)
return;
/* in case we can't stumble upon something better... */
CopyMemory(name, default_device_name, sizeof (default_device_name));
if (pGetRawInputDeviceInfoA(dev->hDevice, RIDI_DEVICENAME, NULL, &ct) < 0)
return;
/* ct == is chars, not bytes, but we used the ASCII version. */
buf = (char *) alloca(ct+1);
keyname = (char *) alloca(ct + sizeof (regkeyroot));
if ((buf == NULL) || (keyname == NULL))
return;
if (pGetRawInputDeviceInfoA(dev->hDevice, RIDI_DEVICENAME, buf, &ct) < 0)
return;
/*
* This string tap dancing gets us a registry keyname in this form:
* SYSTEM\CurrentControlSet\Enum\BUSTYPE\DEVICECLASS\DEVICEID
* (those are my best-guess for the actual elements, but the format
* appears to be sound.)
*/
ct -= 4;
buf += 4; /* skip the "\\??\\" on the front of the string. */
for (i = 0, ptr = buf; i < ct; i++, ptr++) /* convert '#' to '\\' ... */
{
if (*ptr == '#')
*ptr = '\\';
else if (*ptr == '{') /* hit the GUID part of the string. */
break;
} /* for */
*ptr = '\0';
CopyMemory(keyname, regkeyroot, sizeof (regkeyroot) - 1);
CopyMemory(keyname + (sizeof (regkeyroot) - 1), buf, i + 1);
rc = pRegOpenKeyExA(HKEY_LOCAL_MACHINE, keyname, 0, KEY_READ, &hkey);
if (rc != ERROR_SUCCESS)
return;
rc = pRegQueryValueExA(hkey, "DeviceDesc", NULL, &regtype, name, &outsize);
pRegCloseKey(hkey);
if (rc != ERROR_SUCCESS)
{
/* msdn says failure may mangle the buffer, so default it again. */
CopyMemory(name, default_device_name, sizeof (default_device_name));
return;
} /* if */
name[namesize-1] = '\0'; /* just in case. */
} /* get_device_product_name */
static void init_mouse(const RAWINPUTDEVICELIST *dev)
{
MouseStruct *mouse = &mice[available_mice];
if (accept_device(dev))
{
ZeroMemory(mouse, sizeof (MouseStruct));
get_device_product_name(mouse->name, sizeof (mouse->name), dev);
mouse->handle = dev->hDevice;
available_mice++; /* we're good. */
} /* if */
} /* init_mouse */
static int windows_wminput_init(void)
{
RAWINPUTDEVICELIST *devlist = NULL;
UINT ct = 0;
UINT i;
available_mice = 0;
if (!find_api_symbols()) /* only supported on WinXP and later. */
return(0);
pGetRawInputDeviceList(NULL, &ct, sizeof (RAWINPUTDEVICELIST));
if (ct == 0) /* no devices. */
return(0);
devlist = (PRAWINPUTDEVICELIST) alloca(sizeof (RAWINPUTDEVICELIST) * ct);
pGetRawInputDeviceList(devlist, &ct, sizeof (RAWINPUTDEVICELIST));
for (i = 0; i < ct; i++)
init_mouse(&devlist[i]);
if (!init_event_queue())
{
cleanup_window();
available_mice = 0;
} /* if */
return(available_mice);
} /* windows_wminput_init */
static void windows_wminput_quit(void)
{
/* unregister WM_INPUT devices... */
RAWINPUTDEVICE rid;
ZeroMemory(&rid, sizeof (rid));
rid.usUsagePage = 1; /* GenericDesktop page */
rid.usUsage = 2; /* GeneralDestop Mouse usage. */
rid.dwFlags |= RIDEV_REMOVE;
pRegisterRawInputDevices(&rid, 1, sizeof (rid));
cleanup_window();
available_mice = 0;
pDeleteCriticalSection(&mutex);
} /* windows_wminput_quit */
static const char *windows_wminput_name(unsigned int index)
{
if (index < available_mice)
return(mice[index].name);
return(NULL);
} /* windows_wminput_name */
/*
* Windows doesn't send a WM_INPUT event when you unplug a mouse,
* so we try to do a basic query by device handle here; if the
* query fails, we assume the device has vanished and generate a
* disconnect.
*/
static int check_for_disconnects(ManyMouseEvent *ev)
{
/*
* (i) is static so we iterate through all mice round-robin and check
* one mouse per call to ManyMouse_PollEvent(). This makes this test O(1).
*/
static unsigned int i = 0;
MouseStruct *mouse = NULL;
if (++i >= available_mice) /* check first in case of redetect */
i = 0;
mouse = &mice[i];
if (mouse->handle != NULL) /* not NULL == still plugged in. */
{
UINT size = 0;
UINT rc = pGetRawInputDeviceInfoA(mouse->handle, RIDI_DEVICEINFO,
NULL, &size);
if (rc == (UINT) -1) /* failed...probably unplugged... */
{
mouse->handle = NULL;
ev->type = MANYMOUSE_EVENT_DISCONNECT;
ev->device = i;
return(1);
} /* if */
} /* if */
return(0); /* no disconnect event this time. */
} /* check_for_disconnects */
static int windows_wminput_poll(ManyMouseEvent *ev)
{
MSG Msg; /* run the queue for WM_INPUT messages, etc ... */
int found = 0;
/* ...favor existing events in the queue... */
pEnterCriticalSection(&mutex);
if (input_events_read != input_events_write) /* no events if equal. */
{
CopyMemory(ev, &input_events[input_events_read], sizeof (*ev));
input_events_read = ((input_events_read + 1) % MAX_EVENTS);
found = 1;
} /* if */
pLeaveCriticalSection(&mutex);
if (!found)
{
/* pump Windows for new hardware events... */
while (pPeekMessageA(&Msg, raw_hwnd, 0, 0, PM_REMOVE))
{
pTranslateMessage(&Msg);
pDispatchMessageA(&Msg);
} /* while */
/* In case something new came in, give it to the app... */
pEnterCriticalSection(&mutex);
if (input_events_read != input_events_write) /* no events if equal. */
{
CopyMemory(ev, &input_events[input_events_read], sizeof (*ev));
input_events_read = ((input_events_read + 1) % MAX_EVENTS);
found = 1;
} /* if */
pLeaveCriticalSection(&mutex);
} /* if */
/*
* Check for disconnects if queue is totally empty and Windows didn't
* report anything new at this time. This ensures that we don't send a
* disconnect event through ManyMouse and then later give a valid
* event to the app for a device that is now missing.
*/
if (!found)
found = check_for_disconnects(ev);
return(found);
} /* windows_wminput_poll */
ManyMouseDriver ManyMouseDriver_windows =
{
windows_wminput_init,
windows_wminput_quit,
windows_wminput_name,
windows_wminput_poll
};
#endif /* ifdef WINDOWS blocker */
/* end of windows_wminput.c ... */