Files
goldeneye-pc-port/reference/mouse-injector/device.c
T
James Dansereau 6938d5644d docs: add vendored GEPD Mouse Injector reference + input-overhaul plan
Both were already documented as standing repo content (AGENTS.md's
critical-files table, several findings.md entries citing D194/D300's
ground truth) but never actually committed -- add them so the repo
matches what's already written about it. GPLv2, read-only reference,
never compiled as part of this build (see reference/mouse-injector/README.md
for provenance).
2026-09-17 19:31:33 -04:00

320 lines
13 KiB
C

//==========================================================================
// Mouse Injector Plugin
//==========================================================================
// Copyright (C) 2016-2021 Carnivorous
// All rights reserved.
//
// Mouse Injector is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, visit http://www.gnu.org/licenses/gpl-2.0.html
//==========================================================================
#include <stdlib.h>
#include <windows.h>
#include "global.h"
#include "device.h"
#include "maindll.h"
#include "./manymouse/manymouse.h"
#include "./games/game.h"
#define VK_LBUTTON 0x01 // IDs from vkey.h
#define VK_RBUTTON 0x02
#define VK_MBUTTON 0x04
#define VK_XBUTTON1 0x05
#define VK_XBUTTON2 0x06
#define VK_WHEELUP 0x0A
#define VK_WHEELDOWN 0x0B
#define VK_WHEELLEFT 0x0E
#define VK_WHEELRIGHT 0x0F
static int alreadyexec = 0; // has init already exec?
static int connected = 0; // number of devices connected
static POINT lockpos = {0}; // coords for mouse lock position
int windowactive = 1; // is emulator window active?
int DEV_Init(void);
void DEV_Quit(void);
DWORD WINAPI DEV_InjectThread();
int DEV_ReturnKey(void);
int DEV_ReturnDeviceID(const int devicetype);
const char *DEV_Name(const int id);
int DEV_Type(const int id);
int DEV_TypeIndex(const int id);
int DEV_TypeID(const int id, const int devicetype);
//==========================================================================
// Purpose: First called upon device launch, returns total connected ms/kb
// Changes Globals: alreadyexec, connected
//==========================================================================
int DEV_Init(void)
{
if(!alreadyexec)
{
alreadyexec = 1;
connected = ManyMouse_Init();
int activemice = 0, activekeyboards = 0;
for(int index = 0; index < connected; index++)
{
if(DEV_Type(index) == MOUSETYPE)
activemice = 1;
else
activekeyboards = 1;
}
if(!activemice || !activekeyboards) // fail if mouse or keyboard not detected (mouse and keyboard are required for this plugin)
connected = 0;
}
if(connected >= 2) // if at least one mouse and keyboard are detected
return connected;
return 0;
}
//==========================================================================
// Purpose: Safely close ManyMouse
// Changes Globals: alreadyexec, connected
//==========================================================================
void DEV_Quit(void)
{
ManyMouse_Quit();
alreadyexec = 0;
connected = 0;
}
//==========================================================================
// Purpose: Polls ManyMouse for input and injects into game
// Changes Globals: a lot
//==========================================================================
DWORD WINAPI DEV_InjectThread()
{
ManyMouseEvent event; // hold current input event (movement, buttons, ect)
memset(&DEVICE, 0, sizeof(DEVICE)); // clear device struct
int checkwindowtick = 0; // check if emulator window is in focus
int togglebuffer = 0; // buffer cool down for mouse toggle
int acceptalldevices = 0; // accept all device input (used if only 1 player is active)
unsigned char lockmousecounter = 0; // limit SetCursorPos execution
while(ManyMouse_PollEvent(&event)) // flush message pump (max messages is 1024)
{
continue;
}
while(!stopthread)
{
if(mousetoggle && lockmousecounter % (emuoverclock ? TICKRATE_STOCK : TICKRATE_OC) == 0) // don't execute every tick
SetCursorPos(lockpos.x, lockpos.y); // set mouse position to lock position
lockmousecounter++; // overflow pseudo-counter
if(togglebuffer > 0)
togglebuffer--;
for(int player = PLAYER1; player < ALLPLAYERS; player++)
{
DEVICE[player].XPOS = 0, DEVICE[player].YPOS = 0; // reset mouse input
if(DEVICE[player].WHEEL <= 1) // mouse wheel is not an instant key - treat with cooldown and turn button off once cooled off
{
DEVICE[player].WHEEL = 0;
for(int button = 0; button < TOTALBUTTONS; button++) // reset wheel scroll once cooled down
{
if(PROFILE[player].BUTTONPRIM[button] == VK_WHEELUP || PROFILE[player].BUTTONPRIM[button] == VK_WHEELDOWN || PROFILE[player].BUTTONPRIM[button] == VK_WHEELLEFT || PROFILE[player].BUTTONPRIM[button] == VK_WHEELRIGHT)
DEVICE[player].BUTTONPRIM[button] = 0;
if(PROFILE[player].BUTTONSEC[button] == VK_WHEELUP || PROFILE[player].BUTTONSEC[button] == VK_WHEELDOWN || PROFILE[player].BUTTONSEC[button] == VK_WHEELLEFT || PROFILE[player].BUTTONSEC[button] == VK_WHEELRIGHT)
DEVICE[player].BUTTONSEC[button] = 0;
}
}
else
DEVICE[player].WHEEL--;
}
while(ManyMouse_PollEvent(&event))
{
for(int player = PLAYER1; player < ALLPLAYERS; player++)
{
if(PROFILE[player].SETTINGS[CONFIG] == DISABLED) // don't check for disabled players
continue;
acceptalldevices = ONLY1PLAYERACTIVE; // do not filter devices if only one player is active
if(PROFILE[player].SETTINGS[MOUSE] == (int)event.device || acceptalldevices) // mouse movement
{
if(event.type == MANYMOUSE_EVENT_RELMOTION && mousetoggle)
{
if(event.item == 0)
DEVICE[player].XPOS += event.value;
else
DEVICE[player].YPOS += event.value;
}
if(event.type == MANYMOUSE_EVENT_BUTTON) // key presses
{
for(int button = 0; button < TOTALBUTTONS; button++)
{
int offset = event.item > 1 ? 2 : 1; // mouse button needs offset to sync with VK table (left click + offset = 0x01 aka VK_LBUTTON and right click + offset = 0x02 aka VK_RBUTTON)
if(PROFILE[player].BUTTONPRIM[button] == (int)event.item + offset)
DEVICE[player].BUTTONPRIM[button] = event.value;
if(PROFILE[player].BUTTONSEC[button] == (int)event.item + offset)
DEVICE[player].BUTTONSEC[button] = event.value;
}
}
if(event.type == MANYMOUSE_EVENT_SCROLL) // mouse wheel
{
for(int button = 0; button < TOTALBUTTONS; button++)
{
if(event.item == 0) // if VK_WHEELUP/VK_WHEELDOWN
{
if(event.value > 0 && PROFILE[player].BUTTONPRIM[button] == VK_WHEELUP || event.value < 0 && PROFILE[player].BUTTONPRIM[button] == VK_WHEELDOWN)
DEVICE[player].BUTTONPRIM[button] = event.value != 0;
if(event.value > 0 && PROFILE[player].BUTTONSEC[button] == VK_WHEELUP || event.value < 0 && PROFILE[player].BUTTONSEC[button] == VK_WHEELDOWN)
DEVICE[player].BUTTONSEC[button] = event.value != 0;
}
else // if VK_WHEELRIGHT/VK_WHEELLEFT
{
if(event.value > 0 && PROFILE[player].BUTTONPRIM[button] == VK_WHEELLEFT || event.value < 0 && PROFILE[player].BUTTONPRIM[button] == VK_WHEELRIGHT)
DEVICE[player].BUTTONPRIM[button] = event.value != 0;
if(event.value > 0 && PROFILE[player].BUTTONSEC[button] == VK_WHEELLEFT || event.value < 0 && PROFILE[player].BUTTONSEC[button] == VK_WHEELRIGHT)
DEVICE[player].BUTTONSEC[button] = event.value != 0;
}
}
DEVICE[player].WHEEL = 16; // hold button down for 32ms/64ms (non-oc emu need longer buffer because they run at lower framerate)
}
}
if(event.type == MANYMOUSE_EVENT_KEYBOARD && (PROFILE[player].SETTINGS[KEYBOARD] == (int)event.device || acceptalldevices))
{
for(int button = 0; button < TOTALBUTTONS; button++) // check for key presses
{
if(PROFILE[player].BUTTONPRIM[button] == (int)event.item)
DEVICE[player].BUTTONPRIM[button] = event.value;
if(PROFILE[player].BUTTONSEC[button] == (int)event.item)
DEVICE[player].BUTTONSEC[button] = event.value;
}
if(mousetogglekey == (int)event.item && event.value && !togglebuffer && windowactive) // only toggle on press and if window is active
{
mousetoggle = !mousetoggle;
GetCursorPos(&lockpos);
togglebuffer = 100 / TICKRATE; // buffer for 100ms
if(mouselockonfocus) // if user presses mouse toggle key while lock on focus is on, increase buffer size before mouse is relocked so user can temporarily control mouse to close game/change settings
togglebuffer *= 15;
}
}
}
}
if(checkwindowtick > (500 / TICKRATE)) // poll every 500ms
{
checkwindowtick = 0;
if((mouseunlockonloss || !mousetoggle) && emulatorwindow != GetForegroundWindow()) // window is inactive
{
memset(&DEVICE, 0, sizeof(DEVICE)); // reset player input
GAME_Inject(); // ship empty input to game
windowactive = 0, mousetoggle = 0, togglebuffer = 0;
}
else // emu window is in focus
{
windowactive = 1;
if(mouselockonfocus && !mousetoggle && !togglebuffer) // if mouselockonfocus is on and window was refocused/mouse toggle is off, turn on mouse toggle
{
mousetoggle = 1;
GetCursorPos(&lockpos);
}
}
}
else
checkwindowtick++;
if(windowactive && GAME_Status() && !configdialogopen) // if emulator is focused, game is valid and config dialog isn't open
GAME_Inject(); // send input to game driver
Sleep(TICKRATE);
}
GAME_Quit(); // reset game driver's global variables
return 0;
}
//==========================================================================
// Purpose: Returns a single key
//==========================================================================
int DEV_ReturnKey(void)
{
int key = 0;
ManyMouseEvent event; // hold current input event (movement, buttons, ect)
while(ManyMouse_PollEvent(&event))
{
if(event.type == MANYMOUSE_EVENT_BUTTON)
{
if(event.item < 2)
key = event.item ? VK_RBUTTON : VK_LBUTTON;
else if(event.item == 2)
key = VK_MBUTTON;
else if(event.item == 3)
key = VK_XBUTTON1;
else
key = VK_XBUTTON2;
}
else if(event.type == MANYMOUSE_EVENT_SCROLL)
{
if(event.item == 0)
key = event.value > 0 ? VK_WHEELUP : VK_WHEELDOWN;
else
key = event.value > 0 ? VK_WHEELRIGHT : VK_WHEELLEFT;
}
else if(event.type == MANYMOUSE_EVENT_KEYBOARD)
key = event.item;
}
return ClampInt(key, 0, 0xFF); // sanity check key return
}
//==========================================================================
// Purpose: Returns device id of detected input
//==========================================================================
int DEV_ReturnDeviceID(const int devicetype)
{
ManyMouseEvent event; // hold current input event (movement, buttons, ect)
while(ManyMouse_PollEvent(&event))
if(devicetype == MOUSETYPE && (event.type == MANYMOUSE_EVENT_BUTTON || event.type == MANYMOUSE_EVENT_SCROLL) || devicetype == KEYBOARDTYPE && event.type == MANYMOUSE_EVENT_KEYBOARD)
return event.device;
return -1;
}
//==========================================================================
// Purpose: Returns the device name given a type
//==========================================================================
const char *DEV_Name(const int id)
{
if(ManyMouse_DeviceName(id, MOUSETYPE) != NULL)
return ManyMouse_DeviceName(id, MOUSETYPE);
if(ManyMouse_DeviceName(id, KEYBOARDTYPE) != NULL)
return ManyMouse_DeviceName(id, KEYBOARDTYPE);
return NULL;
}
//==========================================================================
// Purpose: Wraps ManyMouse device name and returns the device type
//==========================================================================
int DEV_Type(const int id)
{
return ManyMouse_DeviceName(id, MOUSETYPE) != NULL ? MOUSETYPE : KEYBOARDTYPE; // 0 mouse : 1 keyboard
}
//==========================================================================
// Purpose: Returns the index id within a type (for converting to device combobox)
//==========================================================================
int DEV_TypeIndex(const int id)
{
int kbcount = -1, mscount = -1;
for(int index = 0; index < DEV_Init(); index++)
{
if(DEV_Type(index) == MOUSETYPE)
mscount++;
else
kbcount++;
if(index == id)
return DEV_Type(index) == MOUSETYPE ? mscount : kbcount;
}
return 0;
}
//==========================================================================
// Purpose: Converts a type index id to its real id (for converting from device combobox)
//==========================================================================
int DEV_TypeID(const int id, const int devicetype)
{
int kbcount = -1, mscount = -1;
for(int index = 0; index < DEV_Init(); index++)
{
if(DEV_Type(index) == MOUSETYPE)
mscount++;
else
kbcount++;
if(id == mscount && devicetype == MOUSETYPE || id == kbcount && devicetype == KEYBOARDTYPE)
return index;
}
return 0;
}