diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/engineapi.c | 4 | ||||
| -rw-r--r-- | src/engineapi.h | 1 | ||||
| -rw-r--r-- | src/nosleep.c | 10 | ||||
| -rw-r--r-- | src/rinput.c | 135 | ||||
| -rw-r--r-- | src/sst.c | 63 | 
5 files changed, 145 insertions, 68 deletions
diff --git a/src/engineapi.c b/src/engineapi.c index 8cc3dd6..b2272f9 100644 --- a/src/engineapi.c +++ b/src/engineapi.c @@ -40,6 +40,8 @@ struct VEngineServer *engserver;  DECL_VFUNC(void *, GetGlobalVars, 1)  void *globalvars; +void *inputsystem; +  DECL_VFUNC_DYN(void *, GetAllServerClasses)  DECL_VFUNC(int, GetEngineBuildNumber_newl4d2, 99) // duping gamedata entry, yuck @@ -71,6 +73,8 @@ bool engineapi_init(int pluginver) {  	void *pim = factory_server("PlayerInfoManager002", 0);  	if (pim) globalvars = GetGlobalVars(pim); +	inputsystem = factory_inputsystem("InputSystemVersion001", 0); +  	void *srvdll;  	// TODO(compat): add this back when there's gamedata for 009 (no point atm)  	/*if (srvdll = factory_engine("ServerGameDLL009", 0)) { diff --git a/src/engineapi.h b/src/engineapi.h index 0f4fa8f..6394ec2 100644 --- a/src/engineapi.h +++ b/src/engineapi.h @@ -124,6 +124,7 @@ struct ServerClass {  extern struct VEngineClient *engclient;  extern struct VEngineServer *engserver;  extern void *globalvars; +extern void *inputsystem;  /*   * Called on plugin init to attempt to initialise various core interfaces. diff --git a/src/nosleep.c b/src/nosleep.c index 07a5500..e75c6e6 100644 --- a/src/nosleep.c +++ b/src/nosleep.c @@ -20,12 +20,13 @@  #include "feature.h"  #include "gamedata.h"  #include "hook.h" +#include "mem.h"  #include "os.h"  #include "vcall.h"  FEATURE("inactive window sleep adjustment")  REQUIRE_GAMEDATA(vtidx_SleepUntilInput) -REQUIRE_GLOBAL(factory_inputsystem) +REQUIRE_GLOBAL(inputsystem)  DEF_CVAR_UNREG(engine_no_focus_sleep,  		"Delay while tabbed out (SST reimplementation)", 50, @@ -46,12 +47,7 @@ PREINIT {  }  INIT { -	void *insys = factory_inputsystem("InputSystemVersion001", 0); -	if (!insys) { -		errmsg_errorx("couldn't get input system interface"); -		return false; -	} -	vtable = *(void ***)insys; +	vtable = mem_loadptr(inputsystem);  	if (!os_mprot(vtable + vtidx_SleepUntilInput, sizeof(void *),  			PAGE_READWRITE)) {  		errmsg_errorx("couldn't make virtual table writable"); diff --git a/src/rinput.c b/src/rinput.c index a7ad8d8..e24fc1c 100644 --- a/src/rinput.c +++ b/src/rinput.c @@ -16,16 +16,22 @@  // NOTE: compiled on Windows only. All Linux Source releases are new enough to  // have raw input already. +// TODO(linux): actually, we DO want the scaling on Linux, so we need offsets +// for GetRawMouseAccumulators, etc.  #include <Windows.h>  #include "con_.h" +#include "gamedata.h"  #include "hook.h" +#include "engineapi.h"  #include "errmsg.h"  #include "feature.h"  #include "intdefs.h" +#include "mem.h" +#include "vcall.h" -FEATURE("raw mouse input") +FEATURE("scalable raw mouse input")  // We reimplement m_rawinput by hooking cursor functions in the same way as  // RInput (it's way easier than replacing all the mouse-handling internals of @@ -33,16 +39,31 @@ FEATURE("raw mouse input")  // either block it from being loaded redundantly, or be blocked if it's already  // loaded. If m_rawinput already exists, we do nothing; people should use the  // game's native raw input instead in that case. +// +// As an *additional* feature, we also implement hardware input scaling, meaning +// that some number of counts are required from the mouse in order to move the +// cursor a single unit in game. This is useful for doing "minisnaps" (imprecise +// but accurate mouse movements at high sensitivity) on mice which don't allow +// their CPI to be lowered very far. It's implemented either in our own raw +// input functionality, or by hooking the game's, as required.  #define USAGEPAGE_MOUSE 1  #define USAGE_MOUSE 2 -static long dx = 0, dy = 0; -static void *inwin; +static int cx, cy, rx = 0, ry = 0; // cursor xy, remainder xy +static union { // cheeky space saving +	void *inwin; +	void **vtable_insys; +} u1; +#define inwin u1.inwin +#define vtable_insys u1.vtable_insys  DEF_CVAR_UNREG(m_rawinput, "Use Raw Input for mouse input (SST reimplementation)",  		0, CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR_MINMAX(sst_mouse_factor, "Number of hardware mouse counts per step", +		1, 1, 20, /*CON_ARCHIVE |*/ CON_HIDDEN) +  static ssize __stdcall inproc(void *wnd, uint msg, ssize wp, ssize lp) {  	switch (msg) {  		case WM_INPUT:; @@ -52,8 +73,10 @@ static ssize __stdcall inproc(void *wnd, uint msg, ssize wp, ssize lp) {  					sizeof(RAWINPUTHEADER)) != -1) {  				RAWINPUT *ri = (RAWINPUT *)buf;  				if (ri->header.dwType == RIM_TYPEMOUSE) { -					dx += ri->data.mouse.lLastX; -					dy += ri->data.mouse.lLastY; +					int d = con_getvari(sst_mouse_factor); +					int dx = rx + ri->data.mouse.lLastX; +					int dy = ry + ri->data.mouse.lLastY; +					cx += dx / d; cy += dy / d; rx = dx % d; ry = dy % d;  				}  			}  			return 0; @@ -65,27 +88,59 @@ static ssize __stdcall inproc(void *wnd, uint msg, ssize wp, ssize lp) {  }  typedef int (*__stdcall GetCursorPos_func)(POINT *p); -static GetCursorPos_func orig_GetCursorPos; +typedef uint (*VCALLCONV GetRawMouseAccumulators_func)(void *, int *, int *); +static union { // more cheeky space saving +	GetCursorPos_func orig_GetCursorPos; +	GetRawMouseAccumulators_func orig_GetRawMouseAccumulators; +} u2; +#define orig_GetCursorPos u2.orig_GetCursorPos +#define orig_GetRawMouseAccumulators u2.orig_GetRawMouseAccumulators +  static int __stdcall hook_GetCursorPos(POINT *p) {  	if (!con_getvari(m_rawinput)) return orig_GetCursorPos(p); -	p->x = dx; p->y = dy; +	p->x = cx; p->y = cy;  	return 1;  } +  typedef int (*__stdcall SetCursorPos_func)(int x, int y); -static SetCursorPos_func orig_SetCursorPos; +static SetCursorPos_func orig_SetCursorPos = 0;  static int __stdcall hook_SetCursorPos(int x, int y) { -	dx = x; dy = y; +	cx = x; cy = y;  	return orig_SetCursorPos(x, y);  } -PREINIT { -	if (con_findvar("m_rawinput")) return false; // no need! -	// create cvar hidden so if we fail to init, setting can still be preserved -	con_reg(m_rawinput); -	return true; +static uint VCALLCONV hook_GetRawMouseAccumulators(void *this, int *x, int *y) { +	int dx, dy; +	uint ret = orig_GetRawMouseAccumulators(this, &dx, &dy); +	int d = con_getvari(sst_mouse_factor); +	dx += rx; dy += ry; +	*x = dx / d; *y = dy / d; rx = dx % d; ry = dy % d; +	// NOTE! This is usually void, but apparently returns a bool in the 2013 +	// SDK, for reasons I didn't bother researching. In any case, we can just +	// unconditionally preserve EAX and it won't do any harm. +	return ret;  }  INIT { +	bool has_rawinput = !!con_findvar("m_rawinput"); +	if (has_rawinput) { +		if (!has_vtidx_GetRawMouseAccumulators) return false; +		if (!inputsystem) return false; +		vtable_insys = mem_loadptr(inputsystem); +		// XXX: this is kind of duping nosleep, but that won't always init... +		if (!os_mprot(vtable_insys + vtidx_GetRawMouseAccumulators, +				sizeof(void *), PAGE_READWRITE)) { +			errmsg_errorx("couldn't make virtual table writable"); +			return false; +		} +		orig_GetRawMouseAccumulators = (GetRawMouseAccumulators_func)hook_vtable( +				vtable_insys, vtidx_GetRawMouseAccumulators, +				(void *)&hook_GetRawMouseAccumulators); +	} +	else { +		// create cvar hidden so config is still preserved if we fail to init +		con_reg(m_rawinput); +	}  	WNDCLASSEXW wc = {  		.cbSize = sizeof(wc),  		// cast because inproc is binary-compatible but doesn't use stupid @@ -102,10 +157,26 @@ INIT {  				"Consider launching without that and using ");  		con_colourmsg(&gold, "m_rawinput 1");  		con_colourmsg(&blue, " instead!\n"); -		con_colourmsg(&white, "This option carries over to newer game versions " -				"that have it built-in. No need for external programs :)\n"); +		if (has_rawinput) { +			con_colourmsg(&white, "This is built into this version of game, and" +					" will also get provided by SST in older versions. "); +		} +		else { +			con_colourmsg(&white, "This option carries over to newer game " +				"versions that have it built-in. "); +		} +		con_colourmsg(&white, "No need for external programs :)\n"); +		con_colourmsg(&gold, "Additionally"); +		con_colourmsg(&blue, ", you can scale down the sensor input with "); +		con_colourmsg(&gold, "sst_mouse_factor"); +		con_colourmsg(&blue, "!\n");  		return false;  	} +	if (has_rawinput) { +		// no real reason to keep this around receiving useless window messages +		UnregisterClassW(L"RInput", 0); +		goto ok; +	}  	orig_GetCursorPos = (GetCursorPos_func)hook_inline((void *)&GetCursorPos,  			(void *)&hook_GetCursorPos); @@ -134,7 +205,8 @@ INIT {  		goto e3;  	} -	m_rawinput->base.flags &= ~CON_HIDDEN; +ok:	m_rawinput->base.flags &= ~CON_HIDDEN; +	sst_mouse_factor->base.flags &= ~CON_HIDDEN;  	return true;  e3:	DestroyWindow(inwin); @@ -145,18 +217,23 @@ e0:	UnregisterClassW(L"RInput", 0);  }  END { -	RAWINPUTDEVICE rd = { -		.dwFlags = RIDEV_REMOVE, -		.hwndTarget = 0, -		.usUsagePage = USAGEPAGE_MOUSE, -		.usUsage = USAGE_MOUSE -	}; -	RegisterRawInputDevices(&rd, 1, sizeof(rd)); -	DestroyWindow(inwin); -	UnregisterClassW(L"RInput", 0); - -	unhook_inline((void *)orig_GetCursorPos); -	unhook_inline((void *)orig_SetCursorPos); +	if (orig_SetCursorPos) { // if null, we didn't init our own implementation +		RAWINPUTDEVICE rd = { +			.dwFlags = RIDEV_REMOVE, +			.hwndTarget = 0, +			.usUsagePage = USAGEPAGE_MOUSE, +			.usUsage = USAGE_MOUSE +		}; +		RegisterRawInputDevices(&rd, 1, sizeof(rd)); +		DestroyWindow(inwin); +		UnregisterClassW(L"RInput", 0); +		unhook_inline((void *)orig_GetCursorPos); +		unhook_inline((void *)orig_SetCursorPos); +	} +	else { +		unhook_vtable(vtable_insys, vtidx_GetRawMouseAccumulators, +				(void *)orig_GetRawMouseAccumulators); +	}  }  // vi: sw=4 ts=4 noet tw=80 cc=80 @@ -266,22 +266,23 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) {  	}  	factory_engine = enginef; factory_server = serverf; +#ifdef _WIN32 +	void *inputsystemlib = GetModuleHandleW(L"inputsystem.dll"); +#else +	// TODO(linux): assuming the above doesn't apply to this; check if it does! +	// ... actually, there's a good chance this assumption is now wrong! +	void *inputsystemlib = dlopen("bin/libinputsystem.so", +			RTLD_NOW | RLTD_NOLOAD); +	if (inputsystemlib) dlclose(inputsystemlib); // blegh +#endif +	if (!inputsystemlib) { +		errmsg_warndl("couldn't get the input system library"); +	} +	else if (!(factory_inputsystem = (ifacefactory)os_dlsym(inputsystemlib, +			"CreateInterface"))) { +		errmsg_warndl("couldn't get input system's CreateInterface"); +	}  	if (!engineapi_init(ifacever)) return false; - -	const void **p = vtable_firstdiff; -	if (GAMETYPE_MATCHES(Portal2)) *p++ = (void *)&nop_p_v; // ClientFullyConnect -	*p++ = (void *)&nop_p_v;		  // ClientDisconnect -	*p++ = (void *)&nop_pp_v;		  // ClientPutInServer -	*p++ = (void *)&SetCommandClient; // SetCommandClient -	*p++ = (void *)&nop_p_v;		  // ClientSettingsChanged -	*p++ = (void *)&nop_5pi_i;		  // ClientConnect -	*p++ = ifacever > 1 ? (void *)&nop_pp_i : (void *)&nop_p_i; // ClientCommand -	// remaining stuff here is backwards compatible, so added unconditionally -	*p++ = (void *)&nop_pp_i;		  // NetworkIDValidated -	*p++ = (void *)&nop_ipipp_v;	  // OnQueryCvarValueFinished (002+) -	*p++ = (void *)&nop_p_v;		  // OnEdictAllocated -	*p   = (void *)&nop_p_v;		  // OnEdictFreed -  #ifdef _WIN32  	clientlib = GetModuleHandleW(gameinfo_clientlib);  #else @@ -298,22 +299,20 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) {  			"CreateInterface"))) {  		errmsg_warndl("couldn't get client's CreateInterface");  	} -#ifdef _WIN32 -	void *inputsystemlib = GetModuleHandleW(L"inputsystem.dll"); -#else -	// TODO(linux): assuming the above doesn't apply to this; check if it does! -	// ... actually, there's a good chance this assumption is now wrong! -	void *inputsystemlib = dlopen("bin/libinputsystem.so", -			RTLD_NOW | RLTD_NOLOAD); -	if (inputsystemlib) dlclose(inputsystemlib); // blegh -#endif -	if (!inputsystemlib) { -		errmsg_warndl("couldn't get the input system library"); -	} -	else if (!(factory_inputsystem = (ifacefactory)os_dlsym(inputsystemlib, -			"CreateInterface"))) { -		errmsg_warndl("couldn't get input system's CreateInterface"); -	} + +	const void **p = vtable_firstdiff; +	if (GAMETYPE_MATCHES(Portal2)) *p++ = (void *)&nop_p_v; // ClientFullyConnect +	*p++ = (void *)&nop_p_v;		  // ClientDisconnect +	*p++ = (void *)&nop_pp_v;		  // ClientPutInServer +	*p++ = (void *)&SetCommandClient; // SetCommandClient +	*p++ = (void *)&nop_p_v;		  // ClientSettingsChanged +	*p++ = (void *)&nop_5pi_i;		  // ClientConnect +	*p++ = ifacever > 1 ? (void *)&nop_pp_i : (void *)&nop_p_i; // ClientCommand +	// remaining stuff here is backwards compatible, so added unconditionally +	*p++ = (void *)&nop_pp_i;		  // NetworkIDValidated +	*p++ = (void *)&nop_ipipp_v;	  // OnQueryCvarValueFinished (002+) +	*p++ = (void *)&nop_p_v;		  // OnEdictAllocated +	*p   = (void *)&nop_p_v;		  // OnEdictFreed  	// NOTE: this is technically redundant for early versions but I CBA writing  	// a version check; it's easier to just do this unilaterally. @@ -467,7 +466,7 @@ static const void *vtable[MAX_VTABLE_FUNCS] = {  	(void *)&GetPluginDescription,  	(void *)&nop_p_v,	// LevelInit  	(void *)&nop_pii_v,	// ServerActivate -	(void *)&GameFrame,	// GameFrame +	(void *)&GameFrame,  	(void *)&nop_v_v,	// LevelShutdown  	(void *)&ClientActive  	// At this point, Alien Swarm and Portal 2 add ClientFullyConnect, so we  | 
