diff --git a/Readme_release.txt b/Readme_release.txt index 647ffcb..c0d11e0 100644 --- a/Readme_release.txt +++ b/Readme_release.txt @@ -91,14 +91,62 @@ The default value is simply a number that represents the default value for the s Support for CPY steam_api(64).dll cracks: See the build in the experimental folder. - Notes: -You must all be on the same LAN for it to work. This is an early work so a lot of games will likely not work. +You must all be on the same LAN for it to work. IMPORTANT: Do not run more than one steam game with the same appid at the same time on the same computer with my emu or there might be network issues (dedicated servers should be fine though). +Controller (Note: at the moment this feature is only enabled in the windows experimental builds): +SteamController/SteamInput support is limited to XInput controllers. If your controller is not XInput, there are many tools (at least for windows) that you can use to make it emulate an XInput one. +Steam uses things called action sets for controller configuration. An action set is a group of action names. Action names are bound to buttons, triggers or joysticks. +The emulator needs to know for each action set, which button is linked to which action name. Create a ACTION_SET_NAME.txt file in the steam_settings\controller folder for every action set the game uses. +To see an example for the game Crystar see: steam_settings.EXAMPLE\controller.EXAMPLE +In the action set txt files the format is: +For digital actions (buttons, on or off): ACTION_NAME=BUTTON_NAME +For analog actions (joysticks, triggers): ACTION_NAME=ANALOG_NAME=input source mode +Actions can be bound to more than one button by separating the buttons with , like this: ACTION_NAME=A,B + +If you want to configure a game yourself, find the xbox360 or xbox one vdf file for the game and you should be able to figure things out. + +For example to get the vdf file for the game Crystar: https://steamdb.info/app/981750/config/ +If you look at: steamcontrollerconfigdetails, you will see something like: 1779660455/controller_type: controller_xbox360 +1779660455 refers to a file id that you can dl using your favorite steam workshop downloader site. +The url would be: https://steamcommunity.com/sharedfiles/filedetails/?id=1779660455 + +Valid digital button names: +DUP +DDOWN +DLEFT +DRIGHT +START +BACK +LSTICK +RSTICK +LBUMPER +RBUMPER +A +B +X +Y +DLTRIGGER (emulated buttons, the joy ones are used by games in menus for example. When the game wants to know if the trigger is pressed without the intensity) +DRTRIGGER +DLJOYUP +DLJOYDOWN +DLJOYLEFT +DLJOYRIGHT +DRJOYUP +DRJOYDOWN +DRJOYLEFT +DRJOYRIGHT + +Valid analog names: +LTRIGGER +RTRIGGER +LJOY +RJOY + List of valid steam languages: diff --git a/build_win_debug_experimental.bat b/build_win_debug_experimental.bat index e0cf5be..6d5ffc4 100644 --- a/build_win_debug_experimental.bat +++ b/build_win_debug_experimental.bat @@ -4,13 +4,13 @@ call build_set_protobuf_directories.bat "%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto call build_env_x86.bat cl dll/rtlgenrandom.c dll/rtlgenrandom.def -cl /LD /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ /DGLEW_STATIC /DEMU_EXPERIMENTAL_BUILD dll/*.cpp dll/*.cc detours/*.cpp overlay_experimental/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\lib\Release\Win32\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api.dll +cl /LD /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ /DGLEW_STATIC /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c overlay_experimental/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\lib\Release\Win32\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api.dll cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient.dll cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets.dll "%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto call build_env_x64.bat cl dll/rtlgenrandom.c dll/rtlgenrandom.def -cl /LD /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ /DGLEW_STATIC /DEMU_EXPERIMENTAL_BUILD dll/*.cpp dll/*.cc detours/*.cpp overlay_experimental/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\lib\Release\x64\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api64.dll +cl /LD /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ /DGLEW_STATIC /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c overlay_experimental/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\lib\Release\x64\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api64.dll cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient64.dll cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets64.dll diff --git a/build_win_release_experimental.bat b/build_win_release_experimental.bat index 3d9ec40..34b38d7 100644 --- a/build_win_release_experimental.bat +++ b/build_win_release_experimental.bat @@ -6,11 +6,11 @@ call build_set_protobuf_directories.bat "%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto call build_env_x86.bat cl dll/rtlgenrandom.c dll/rtlgenrandom.def -cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DNDEBUG /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp ImGui/*.cpp ImGui/impls/*.cpp overlay_experimental/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\lib\Release\Win32\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api.dll +cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DCONTROLLER_SUPPORT /DNDEBUG /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp overlay_experimental/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\lib\Release\Win32\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api.dll cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG steamclient.cpp /EHsc /MP12 /Ox /link /OUT:release\experimental\steamclient.dll "%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto call build_env_x64.bat cl dll/rtlgenrandom.c dll/rtlgenrandom.def -cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DNDEBUG /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp ImGui/*.cpp ImGui/impls/*.cpp overlay_experimental/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\lib\Release\x64\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api64.dll +cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DCONTROLLER_SUPPORT /DNDEBUG /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp overlay_experimental/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\lib\Release\x64\glew32s.lib opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api64.dll cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG steamclient.cpp /EHsc /MP12 /Ox /link /OUT:release\experimental\steamclient64.dll copy Readme_experimental.txt release\experimental\Readme.txt diff --git a/controller/gamepad.c b/controller/gamepad.c new file mode 100644 index 0000000..f1e7893 --- /dev/null +++ b/controller/gamepad.c @@ -0,0 +1,603 @@ +/** + * Gamepad Input Library + * Sean Middleditch + * Copyright (C) 2010 Sean Middleditch + * LICENSE: MIT/X + */ + +#include +#include +#include +#include + +#define GAMEPAD_EXPORT 1 +#include "gamepad.h" + +/* Platform-specific includes */ +#if defined(_WIN32) +# define WIN32_LEAN_AND_MEAN 1 +# undef UNICODE +# include "windows.h" +# include "xinput.h" +# pragma comment(lib, "XINPUT9_1_0.lib") +#elif defined(__linux__) +# include +# include +# include +# include +# include +#else +# error "Unknown platform in gamepad.c" +#endif + +#define BUTTON_TO_FLAG(b) (1 << (b)) + +/* Axis information */ +typedef struct GAMEPAD_AXIS GAMEPAD_AXIS; +struct GAMEPAD_AXIS { + int x, y; + float nx, ny; + float length; + float angle; + GAMEPAD_STICKDIR dirLast, dirCurrent; +}; + +/* Trigger value information */ +typedef struct GAMEPAD_TRIGINFO GAMEPAD_TRIGINFO; +struct GAMEPAD_TRIGINFO { + int value; + float length; + GAMEPAD_BOOL pressedLast, pressedCurrent; +}; + +/* Structure for state of a particular gamepad */ +typedef struct GAMEPAD_STATE GAMEPAD_STATE; +struct GAMEPAD_STATE { + GAMEPAD_AXIS stick[STICK_COUNT]; + GAMEPAD_TRIGINFO trigger[TRIGGER_COUNT]; + int bLast, bCurrent, flags; +#if defined(__linux__) + char* device; + int fd; + int effect; +#endif +}; + +/* State of the four gamepads */ +static GAMEPAD_STATE STATE[4]; + +/* Note whether a gamepad is currently connected */ +#define FLAG_CONNECTED (1<<0) +#define FLAG_RUMBLE (1<<1) + +/* Prototypes for utility functions */ +static void GamepadResetState (GAMEPAD_DEVICE gamepad); +static void GamepadUpdateCommon (void); +static void GamepadUpdateDevice (GAMEPAD_DEVICE gamepad); +static void GamepadUpdateStick (GAMEPAD_AXIS* axis, float deadzone); +static void GamepadUpdateTrigger (GAMEPAD_TRIGINFO* trig); + +/* Various values of PI */ +#define PI_1_4 0.78539816339744f +#define PI_1_2 1.57079632679489f +#define PI_3_4 2.35619449019234f +#define PI 3.14159265358979f + +/* Platform-specific implementation code */ +#if defined(_WIN32) + +void GamepadInit(void) { + int i; + for (i = 0; i != GAMEPAD_COUNT; ++i) { + STATE[i].flags = 0; + } +} + +void GamepadUpdate(void) { + GamepadUpdateCommon(); +} + +static void GamepadUpdateDevice(GAMEPAD_DEVICE gamepad) { + XINPUT_STATE xs; + if (XInputGetState(gamepad, &xs) == 0) { + /* reset if the device was not already connected */ + if ((STATE[gamepad].flags & FLAG_CONNECTED) == 0) { + GamepadResetState(gamepad); + } + + /* mark that we are connected w/ rumble support */ + STATE[gamepad].flags |= FLAG_CONNECTED|FLAG_RUMBLE; + + /* update state */ + STATE[gamepad].bCurrent = xs.Gamepad.wButtons; + STATE[gamepad].trigger[TRIGGER_LEFT].value = xs.Gamepad.bLeftTrigger; + STATE[gamepad].trigger[TRIGGER_RIGHT].value = xs.Gamepad.bRightTrigger; + STATE[gamepad].stick[STICK_LEFT].x = xs.Gamepad.sThumbLX; + STATE[gamepad].stick[STICK_LEFT].y = xs.Gamepad.sThumbLY; + STATE[gamepad].stick[STICK_RIGHT].x = xs.Gamepad.sThumbRX; + STATE[gamepad].stick[STICK_RIGHT].y = xs.Gamepad.sThumbRY; + } else { + /* disconnected */ + STATE[gamepad].flags &= ~FLAG_CONNECTED; + } +} + +void GamepadShutdown(void) { + /* no Win32 shutdown required */ +} + +void GamepadSetRumble(GAMEPAD_DEVICE gamepad, float left, float right) { + if ((STATE[gamepad].flags & FLAG_RUMBLE) != 0) { + XINPUT_VIBRATION vib; + ZeroMemory(&vib, sizeof(vib)); + vib.wLeftMotorSpeed = (WORD)(left * 65535); + vib.wRightMotorSpeed = (WORD)(right * 65535); + XInputSetState(gamepad, &vib); + } +} + +#elif defined(__linux__) + +/* UDev handles */ +static struct udev* UDEV = NULL; +static struct udev_monitor* MON = NULL; + +static void GamepadAddDevice(const char* devPath); +static void GamepadRemoveDevice(const char* devPath); + +/* Helper to add a new device */ +static void GamepadAddDevice(const char* devPath) { + int i; + + /* try to find a free controller */ + for (i = 0; i != GAMEPAD_COUNT; ++i) { + if ((STATE[i].flags & FLAG_CONNECTED) == 0) { + break; + } + } + if (i == GAMEPAD_COUNT) { + return; + } + + /* copy the device path */ + STATE[i].device = strdup(devPath); + if (STATE[i].device == NULL) { + return; + } + + /* reset device state */ + GamepadResetState((GAMEPAD_DEVICE)i); + + /* attempt to open the device in read-write mode, which we need fo rumble */ + STATE[i].fd = open(STATE[i].device, O_RDWR|O_NONBLOCK); + if (STATE[i].fd != -1) { + STATE[i].flags = FLAG_CONNECTED|FLAG_RUMBLE; + return; + } + + /* attempt to open in read-only mode if access was denied */ + if (errno == EACCES) { + STATE[i].fd = open(STATE[i].device, O_RDONLY|O_NONBLOCK); + if (STATE[i].fd != -1) { + STATE[i].flags = FLAG_CONNECTED; + return; + } + } + + /* could not open the device at all */ + free(STATE[i].device); + STATE[i].device = NULL; +} + +/* Helper to remove a device */ +static void GamepadRemoveDevice(const char* devPath) { + int i; + for (i = 0; i != GAMEPAD_COUNT; ++i) { + if (STATE[i].device != NULL && strcmp(STATE[i].device, devPath) == 0) { + if (STATE[i].fd != -1) { + close(STATE[i].fd); + STATE[i].fd = -1; + } + free(STATE[i].device); + STATE[i].device = 0; + STATE[i].flags = 0; + break; + } + } +} + +void GamepadInit(void) { + struct udev_list_entry* devices; + struct udev_list_entry* item; + struct udev_enumerate* enu; + int i; + + /* initialize connection state */ + for (i = 0; i != GAMEPAD_COUNT; ++i) { + STATE[i].flags = 0; + STATE[i].fd = STATE[i].effect = -1; + } + + /* open the udev handle */ + UDEV = udev_new(); + if (UDEV == NULL) { + /* FIXME: flag error? */ + return; + } + + /* open monitoring device (safe to fail) */ + MON = udev_monitor_new_from_netlink(UDEV, "udev"); + /* FIXME: flag error if hot-plugging can't be supported? */ + if (MON != NULL) { + udev_monitor_enable_receiving(MON); + udev_monitor_filter_add_match_subsystem_devtype(MON, "input", NULL); + } + + /* enumerate joypad devices */ + enu = udev_enumerate_new(UDEV); + udev_enumerate_add_match_subsystem(enu, "input"); + udev_enumerate_scan_devices(enu); + devices = udev_enumerate_get_list_entry(enu); + + udev_list_entry_foreach(item, devices) { + const char* name; + const char* sysPath; + const char* devPath; + struct udev_device* dev; + + name = udev_list_entry_get_name(item); + dev = udev_device_new_from_syspath(UDEV, name); + sysPath = udev_device_get_syspath(dev); + devPath = udev_device_get_devnode(dev); + + if (sysPath != NULL && devPath != NULL && strstr(sysPath, "/js") != 0) { + GamepadAddDevice(devPath); + } + + udev_device_unref(dev); + } + + /* cleanup */ + udev_enumerate_unref(enu); +} + +void GamepadUpdate(void) { + if (MON != NULL) { + fd_set r; + struct timeval tv; + int fd = udev_monitor_get_fd(MON); + + /* set up a poll on the udev device */ + FD_ZERO(&r); + FD_SET(fd, &r); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + select(fd + 1, &r, 0, 0, &tv); + + /* test if we have a device change */ + if (FD_ISSET(fd, &r)) { + struct udev_device* dev = udev_monitor_receive_device(MON); + if (dev) { + const char* devNode = udev_device_get_devnode(dev); + const char* sysPath = udev_device_get_syspath(dev); + const char* action = udev_device_get_action(dev); + sysPath = udev_device_get_syspath(dev); + action = udev_device_get_action(dev); + + if (strstr(sysPath, "/js") != 0) { + if (strcmp(action, "remove") == 0) { + GamepadRemoveDevice(devNode); + } else if (strcmp(action, "add") == 0) { + GamepadAddDevice(devNode); + } + } + + udev_device_unref(dev); + } + } + } + + GamepadUpdateCommon(); +} + +static void GamepadUpdateDevice(GAMEPAD_DEVICE gamepad) { + if (STATE[gamepad].flags & FLAG_CONNECTED) { + struct js_event je; + while (read(STATE[gamepad].fd, &je, sizeof(je)) > 0) { + int button; + switch (je.type) { + case JS_EVENT_BUTTON: + /* determine which button the event is for */ + switch (je.number) { + case 0: button = BUTTON_A; break; + case 1: button = BUTTON_B; break; + case 2: button = BUTTON_X; break; + case 3: button = BUTTON_Y; break; + case 4: button = BUTTON_LEFT_SHOULDER; break; + case 5: button = BUTTON_RIGHT_SHOULDER; break; + case 6: button = BUTTON_BACK; break; + case 7: button = BUTTON_START; break; + case 8: button = 0; break; /* XBOX button */ + case 9: button = BUTTON_LEFT_THUMB; break; + case 10: button = BUTTON_RIGHT_THUMB; break; + default: button = 0; break; + } + + /* set or unset the button */ + if (je.value) { + STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(button); + } else { + STATE[gamepad].bCurrent ^= BUTTON_TO_FLAG(button); + } + + break; + case JS_EVENT_AXIS: + /* normalize and store the axis */ + switch (je.number) { + case 0: STATE[gamepad].stick[STICK_LEFT].x = je.value; break; + case 1: STATE[gamepad].stick[STICK_LEFT].y = -je.value; break; + case 2: STATE[gamepad].trigger[TRIGGER_LEFT].value = (je.value + 32768) >> 8; break; + case 3: STATE[gamepad].stick[STICK_RIGHT].x = je.value; break; + case 4: STATE[gamepad].stick[STICK_RIGHT].y = -je.value; break; + case 5: STATE[gamepad].trigger[TRIGGER_RIGHT].value = (je.value + 32768) >> 8; break; + case 6: + if (je.value == -32767) { + STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_LEFT); + STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_RIGHT); + } else if (je.value == 32767) { + STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_RIGHT); + STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_LEFT); + } else { + STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_LEFT) & ~BUTTON_TO_FLAG(BUTTON_DPAD_RIGHT); + } + break; + case 7: + if (je.value == -32767) { + STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_UP); + STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_DOWN); + } else if (je.value == 32767) { + STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_DOWN); + STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_UP); + } else { + STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_UP) & ~BUTTON_TO_FLAG(BUTTON_DPAD_DOWN); + } + break; + default: break; + } + + break; + default: + break; + } + } + } +} + +void GamepadShutdown(void) { + int i; + + /* cleanup udev */ + udev_monitor_unref(MON); + udev_unref(UDEV); + + /* cleanup devices */ + for (i = 0; i != GAMEPAD_COUNT; ++i) { + if (STATE[i].device != NULL) { + free(STATE[i].device); + } + + if (STATE[i].fd != -1) { + close(STATE[i].fd); + } + } +} + +void GamepadSetRumble(GAMEPAD_DEVICE gamepad, float left, float right) { + if (STATE[gamepad].fd != -1) { + struct input_event play; + + /* delete any existing effect */ + if (STATE[gamepad].effect != -1) { + /* stop the effect */ + play.type = EV_FF; + play.code = STATE[gamepad].effect; + play.value = 0; + + write(STATE[gamepad].fd, (const void*)&play, sizeof(play)); + + /* delete the effect */ + ioctl(STATE[gamepad].fd, EVIOCRMFF, STATE[gamepad].effect); + } + + /* if rumble parameters are non-zero, start the new effect */ + if (left != 0.f || right != 0.f) { + struct ff_effect ff; + + /* define an effect for this rumble setting */ + ff.type = FF_RUMBLE; + ff.id = -1; + ff.u.rumble.strong_magnitude = (unsigned short)(left * 65535); + ff.u.rumble.weak_magnitude = (unsigned short)(right * 65535); + ff.replay.length = 5; + ff.replay.delay = 0; + + /* upload the effect */ + if (ioctl(STATE[gamepad].fd, EVIOCSFF, &ff) != -1) { + STATE[gamepad].effect = ff.id; + } + + /* play the effect */ + play.type = EV_FF; + play.code = STATE[gamepad].effect; + play.value = 1; + + write(STATE[gamepad].fd, (const void*)&play, sizeof(play)); + } + } +} + +#else /* !defined(_WIN32) && !defined(__linux__) */ + +# error "Unknown platform in gamepad.c" + +#endif /* end of platform implementations */ + +GAMEPAD_BOOL GamepadIsConnected(GAMEPAD_DEVICE device) { + return (STATE[device].flags & FLAG_CONNECTED) != 0 ? GAMEPAD_TRUE : GAMEPAD_FALSE; +} + +GAMEPAD_BOOL GamepadButtonDown(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button) { + return (STATE[device].bCurrent & BUTTON_TO_FLAG(button)) != 0 ? GAMEPAD_TRUE : GAMEPAD_FALSE; +} + +GAMEPAD_BOOL GamepadButtonTriggered(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button) { + return ((STATE[device].bLast & BUTTON_TO_FLAG(button)) == 0 && + (STATE[device].bCurrent & BUTTON_TO_FLAG(button)) != 0) ? GAMEPAD_TRUE : GAMEPAD_FALSE; +} + +GAMEPAD_BOOL GamepadButtonReleased(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button) { + return ((STATE[device].bCurrent & BUTTON_TO_FLAG(button)) == 0 && + (STATE[device].bLast & BUTTON_TO_FLAG(button)) != 0) ? GAMEPAD_TRUE : GAMEPAD_FALSE; +} + +int GamepadTriggerValue(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) { + return STATE[device].trigger[trigger].value; +} + +float GamepadTriggerLength(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) { + return STATE[device].trigger[trigger].length; +} + +GAMEPAD_BOOL GamepadTriggerDown(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) { + return STATE[device].trigger[trigger].pressedCurrent; +} + +GAMEPAD_BOOL GamepadTriggerTriggered(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) { + return (STATE[device].trigger[trigger].pressedCurrent && + !STATE[device].trigger[trigger].pressedLast) ? GAMEPAD_TRUE : GAMEPAD_FALSE; +} + +GAMEPAD_BOOL GamepadTriggerReleased(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) { + return (!STATE[device].trigger[trigger].pressedCurrent && + STATE[device].trigger[trigger].pressedLast) ? GAMEPAD_TRUE : GAMEPAD_FALSE; +} + +void GamepadStickXY(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, int *outX, int *outY) { + *outX = STATE[device].stick[stick].x; + *outY = STATE[device].stick[stick].y; +} + +float GamepadStickLength(GAMEPAD_DEVICE device, GAMEPAD_STICK stick) { + return STATE[device].stick[stick].length; +} + +void GamepadStickNormXY(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, float *outX, float *outY) { + *outX = STATE[device].stick[stick].nx; + *outY = STATE[device].stick[stick].ny; +} + +float GamepadStickAngle(GAMEPAD_DEVICE device, GAMEPAD_STICK stick) { + return STATE[device].stick[stick].angle; +} + +GAMEPAD_STICKDIR GamepadStickDir(GAMEPAD_DEVICE device, GAMEPAD_STICK stick) { + return STATE[device].stick[stick].dirCurrent; +} + +GAMEPAD_BOOL GamepadStickDirTriggered(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, GAMEPAD_STICKDIR dir) { + return (STATE[device].stick[stick].dirCurrent == dir && + STATE[device].stick[stick].dirCurrent != STATE[device].stick[stick].dirLast) ? GAMEPAD_TRUE : GAMEPAD_FALSE; +} + +/* initialize common gamepad state */ +static void GamepadResetState(GAMEPAD_DEVICE gamepad) { + memset(STATE[gamepad].stick, 0, sizeof(STATE[gamepad].stick)); + memset(STATE[gamepad].trigger, 0, sizeof(STATE[gamepad].trigger)); + STATE[gamepad].bLast = STATE[gamepad].bCurrent = 0; +} + +/* Update individual sticks */ +static void GamepadUpdateCommon(void) { + int i; + for (i = 0; i != GAMEPAD_COUNT; ++i) { + /* store previous button state */ + STATE[i].bLast = STATE[i].bCurrent; + + /* per-platform update routines */ + GamepadUpdateDevice((GAMEPAD_DEVICE)i); + + /* calculate refined stick and trigger values */ + if ((STATE[i].flags & FLAG_CONNECTED) != 0) { + GamepadUpdateStick(&STATE[i].stick[STICK_LEFT], GAMEPAD_DEADZONE_LEFT_STICK); + GamepadUpdateStick(&STATE[i].stick[STICK_RIGHT], GAMEPAD_DEADZONE_RIGHT_STICK); + + GamepadUpdateTrigger(&STATE[i].trigger[TRIGGER_LEFT]); + GamepadUpdateTrigger(&STATE[i].trigger[TRIGGER_RIGHT]); + } + } +} + +/* Update stick info */ +static void GamepadUpdateStick(GAMEPAD_AXIS* axis, float deadzone) { + // determine magnitude of stick + axis->length = sqrtf((float)(axis->x*axis->x) + (float)(axis->y*axis->y)); + + if (axis->length > deadzone) { + // clamp length to maximum value + if (axis->length > 32767.0f) { + axis->length = 32767.0f; + } + + // normalized X and Y values + axis->nx = axis->x / axis->length; + axis->ny = axis->y / axis->length; + + //fix special case + if (axis->nx < -1.0) axis->nx = -1.0; + if (axis->ny < -1.0) axis->ny = -1.0; + + // adjust length for deadzone and find normalized length + axis->length -= deadzone; + axis->length /= (32767.0f - deadzone); + + // find angle of stick in radians + axis->angle = atan2f((float)axis->y, (float)axis->x); + } else { + axis->x = axis->y = 0; + axis->nx = axis->ny = 0.0f; + axis->length = axis->angle = 0.0f; + } + + /* update the stick direction */ + axis->dirLast = axis->dirCurrent; + axis->dirCurrent = STICKDIR_CENTER; + + /* check direction to see if it's non-centered */ + if (axis->length != 0.f) { + if (axis->angle >= PI_1_4 && axis->angle < PI_3_4) { + axis->dirCurrent = STICKDIR_UP; + } else if (axis->angle >= -PI_3_4 && axis->angle < -PI_1_4) { + axis->dirCurrent = STICKDIR_DOWN; + } else if (axis->angle >= PI_3_4 || axis->angle < -PI_3_4) { + axis->dirCurrent = STICKDIR_LEFT; + } else /* if (axis->angle < PI_1_4 && axis->angle >= -PI_1_4) */ { + axis->dirCurrent = STICKDIR_RIGHT; + } + } +} + +/* Update trigger info */ +static void GamepadUpdateTrigger(GAMEPAD_TRIGINFO* trig) { + trig->pressedLast = trig->pressedCurrent; + + if (trig->value > GAMEPAD_DEADZONE_TRIGGER) { + trig->length = ((trig->value - GAMEPAD_DEADZONE_TRIGGER) / (255.0f - GAMEPAD_DEADZONE_TRIGGER)); + trig->pressedCurrent = GAMEPAD_TRUE; + } else { + trig->value = 0; + trig->length = 0.0f; + trig->pressedCurrent = GAMEPAD_FALSE; + } +} diff --git a/controller/gamepad.h b/controller/gamepad.h new file mode 100644 index 0000000..4883a3f --- /dev/null +++ b/controller/gamepad.h @@ -0,0 +1,324 @@ +/** + * Gamepad Input Library + * Sean Middleditch + * Copyright (C) 2010,2011 Sean Middleditch + * LICENSE: MIT/X + */ + +#if !defined(GAMEPAD_H) +#define GAMEPAD_H 1 + +#if defined(__cplusplus) +extern "C" { +#endif + +#define GAMEPAD_STATIC_LIB +#if defined(GAMEPAD_STATIC_LIB) +# define GAMEPAD_API +#else +# if defined(_WIN32) +# if defined(GAMEPAD_EXPORT) +# define GAMEPAD_API __declspec(dllexport) +# else +# define GAMEPAD_API __declspec(dllimport) +# endif +# elif defined(__GNUC__) && defined(GAMEPAD_EXPORT) +# define GAMEPAD_API __attribute__((visibility("default"))) +# else +# define GAMEPAD_API extern +# endif +#endif + +/** + * Enumeration of the possible devices. + * + * Only four devices are supported as this is the limit of Windows. + */ +enum GAMEPAD_DEVICE { + GAMEPAD_0 = 0, /**< First gamepad */ + GAMEPAD_1 = 1, /**< Second gamepad */ + GAMEPAD_2 = 2, /**< Third gamepad */ + GAMEPAD_3 = 3, /**< Fourth gamepad */ + + GAMEPAD_COUNT /**< Maximum number of supported gamepads */ +}; + +/** + * Enumeration of the possible buttons. + */ +enum GAMEPAD_BUTTON { + BUTTON_DPAD_UP = 0, /**< UP on the direction pad */ + BUTTON_DPAD_DOWN = 1, /**< DOWN on the direction pad */ + BUTTON_DPAD_LEFT = 2, /**< LEFT on the direction pad */ + BUTTON_DPAD_RIGHT = 3, /**< RIGHT on the direction pad */ + BUTTON_START = 4, /**< START button */ + BUTTON_BACK = 5, /**< BACK button */ + BUTTON_LEFT_THUMB = 6, /**< Left analog stick button */ + BUTTON_RIGHT_THUMB = 7, /**< Right analog stick button */ + BUTTON_LEFT_SHOULDER = 8, /**< Left bumper button */ + BUTTON_RIGHT_SHOULDER = 9, /**< Right bumper button */ + BUTTON_A = 12, /**< A button */ + BUTTON_B = 13, /**< B button */ + BUTTON_X = 14, /**< X button */ + BUTTON_Y = 15, /**< Y button */ + + BUTTON_COUNT /**< Maximum number of supported buttons */ +}; + +/** + * Enumeration of the possible pressure/trigger buttons. + */ +enum GAMEPAD_TRIGGER { + TRIGGER_LEFT = 0, /**< Left trigger */ + TRIGGER_RIGHT = 1, /**< Right trigger */ + + TRIGGER_COUNT /**< Number of triggers */ +}; + +/** + * Enumeration of the analog sticks. + */ +enum GAMEPAD_STICK { + STICK_LEFT = 0, /**< Left stick */ + STICK_RIGHT = 1, /**< Right stick */ + + STICK_COUNT /**< Number of analog sticks */ +}; + +/** + * Enumeration of main stick directions. + * + * This is used for some of the convenience routines in the library. + */ +enum GAMEPAD_STICKDIR { + STICKDIR_CENTER = 0, /**< CENTER, no direction */ + STICKDIR_UP = 1, /**< UP direction */ + STICKDIR_DOWN = 2, /**< DOWN direction */ + STICKDIR_LEFT = 3, /**< LEFT direction */ + STICKDIR_RIGHT = 4, /**< RIGHT direction */ + + STICKDIR_COUNT +}; + +/** + * Enumeration for true/false values + */ +enum GAMEPAD_BOOL { + GAMEPAD_FALSE = 0, /**< FALSE value for boolean parameters */ + GAMEPAD_TRUE = 1 /**< TRUE value for boolean parameters */ +}; + +typedef enum GAMEPAD_DEVICE GAMEPAD_DEVICE; +typedef enum GAMEPAD_BUTTON GAMEPAD_BUTTON; +typedef enum GAMEPAD_TRIGGER GAMEPAD_TRIGGER; +typedef enum GAMEPAD_STICK GAMEPAD_STICK; +typedef enum GAMEPAD_STICKDIR GAMEPAD_STICKDIR; +typedef enum GAMEPAD_BOOL GAMEPAD_BOOL; + +#define GAMEPAD_DEADZONE_LEFT_STICK 7849 /**< Suggested deadzone magnitude for left analog stick */ +#define GAMEPAD_DEADZONE_RIGHT_STICK 8689 /**< Suggested deadzone magnitude for right analog stick */ +#define GAMEPAD_DEADZONE_TRIGGER 30 /**< Suggested deadzone for triggers */ + +/** + * Initialize the library. + * + * This is critical on non-Windows platforms. + */ +GAMEPAD_API void GamepadInit(void); + +/** + * Shutdown the library. + * + * This will release resources allocated by the library internally. + * + * This should be called after forking as well. + */ +GAMEPAD_API void GamepadShutdown(void); + +/** + * Updates the state of the gamepads. + * + * This must be called (at least) once per game loop. + */ +GAMEPAD_API void GamepadUpdate(void); + +/** + * Test if a particular gamepad is connected. + * + * \param device The device to check. + * \returns GAMEPAD_TRUE if the device is connected, GAMEPAD_FALSE if it is not. + */ +GAMEPAD_API GAMEPAD_BOOL GamepadIsConnected(GAMEPAD_DEVICE device); + +/** + * Test if a particular button is being pressed. + * + * \param device The device to check. + * \param button The button to check. + * \returns GAMEPAD_TRUE if the button is down, GAMEPAD_FALSE if it is not. + */ +GAMEPAD_API GAMEPAD_BOOL GamepadButtonDown(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button); + +/** + * Test if a particular button has been depressed since the previous call to GamepadUpdate. + * + * \param device The device to check. + * \param button The button to check. + * \returns GAMEPAD_TRUE if the button has been pressed, GAMEPAD_FALSE if it is not or if it was depressed the previous frame. + */ +GAMEPAD_API GAMEPAD_BOOL GamepadButtonTriggered(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button); + +/** + * Test if a particular button has been released since the previous call to GamepadUpdate. + * + * \param device The device to check. + * \param button The button to check. + * \returns GAMEPAD_TRUE if the button has been released, GAMEPAD_FALSE if it is down or if it was not down the previous frame. + */ +GAMEPAD_API GAMEPAD_BOOL GamepadButtonReleased(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button); + +/** + * Get the trigger value (depression magnitude) in its raw form. + * + * \param device The device to check. + * \param trigger The trigger to check. + * \returns Trigger depression magnitude (0 to 32767). + */ +GAMEPAD_API int GamepadTriggerValue(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger); + +/** + * Get the trigger value (depression magnitude) in normalized form. + * + * \param device The device to check. + * \param trigger The trigger to check. + * \returns Trigger depression magnitude (0 to 1). + */ +GAMEPAD_API float GamepadTriggerLength(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger); + +/** + * Test if a trigger is depressed + * + * \param device The device to check. + * \param trigger The trigger to check. + * \returns GAMEPAD_TRUE if down, GAMEPAD_FALSE otherwise. + */ +GAMEPAD_API GAMEPAD_BOOL GamepadTriggerDown(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger); + +/** + * Test if a trigger is depressed + * + * \param device The device to check. + * \param trigger The trigger to check. + * \returns GAMEPAD_TRUE if triggered, GAMEPAD_FALSE otherwise. + */ +GAMEPAD_API GAMEPAD_BOOL GamepadTriggerTriggered(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger); + +/** + * Test if a trigger is depressed + * + * \param device The device to check. + * \param trigger The trigger to check. + * \returns GAMEPAD_TRUE if released, GAMEPAD_FALSE otherwise. + */ +GAMEPAD_API GAMEPAD_BOOL GamepadTriggerReleased(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger); + +/** + * Set the rumble motors on/off. + * + * To turn off the rumble effect, set values to 0 for both motors. + * + * The left motor is the low-frequency/strong motor, and the right motor is the high-frequency/weak motor. + * + * \param device The device to update. + * \param left Left motor strengh (0 to 1). + * \param right Right motor strengh (0 to 1). + */ +GAMEPAD_API void GamepadSetRumble(GAMEPAD_DEVICE device, float left, float right); + +/** + * Query the position of an analog stick as raw values. + * + * The values retrieved by this function represent the magnitude of the analog + * stick in each direction. Note that it shouldn't be possible to get full + * magnitude in one direction unless the other direction has a magnitude of + * zero, as the stick has a circular movement range. + * + * \param device The device to check. + * \param stick The stick to check. + * \param outX Pointer to integer to store the X magnitude in (-32767 to 32767). + * \param outX Pointer to integer to store the Y magnitude in (-32767 to 32767). + */ +GAMEPAD_API void GamepadStickXY(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, int* outX, int* outY); + +/** + * Query the position of an analog stick as normalized values. + * + * The values retrieved by this function represent the magnitude of the analog + * stick in each direction. Note that it shouldn't be possible to get full + * magnitude in one direction unless the other direction has a magnitude of + * zero, as the stick has a circular movement range. + * + * \param device The device to check. + * \param stick The stick to check. + * \param outX Pointer to float to store the X magnitude in (-1 to 1). + * \param outX Pointer to float to store the Y magnitude in (-1 to 1). + */ +GAMEPAD_API void GamepadStickNormXY(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, float* outX, float* outY); + +/** + * Query the magnitude of an analog stick. + * + * This returns the normalized value of the magnitude of the stick. That is, + * if the stick is pushed all the way in any direction, it returns 1.0. + * + * \param device The device to check. + * \param stick The stick to check. + * \returns The magnitude of the stick (0 to 1). + */ +GAMEPAD_API float GamepadStickLength(GAMEPAD_DEVICE device, GAMEPAD_STICK stick); + +/** + * Query the direction of a stick (in radians). + * + * This returns the direction of the stick. This value is in radians, not + * degrees. Zero is to the right, and the angle increases in a + * counter-clockwise direction. + * + * \param device The device to check. + * \param stick The stick to check. + * \returns The angle of the stick (0 to 2*PI). + */ +GAMEPAD_API float GamepadStickAngle(GAMEPAD_DEVICE device, GAMEPAD_STICK stick); + +/** + * Get the direction the stick is pushed in (if any). + * + * This is a useful utility function for when the stick should be treated as a simple + * directional pad, such as for menu UIs. + * + * \param device The device to check. + * \param stick The trigger to check. + * \returns The stick's current direction. + */ +GAMEPAD_API GAMEPAD_STICKDIR GamepadStickDir(GAMEPAD_DEVICE device, GAMEPAD_STICK stick); + +/** + * Test whether a stick has been pressed in a particular direction since the last update. + * + * This only returns true if the stick was centered last frame. + * + * This is a useful utility function for when the stick should be treated as a simple + * directional pad, such as for menu UIs. + * + * \param device The device to check. + * \param stick The trigger to check. + * \param stickdir The direction to check for. + * \returns GAMEPAD_TRUE if the stick is pressed in the specified direction, GAMEPAD_FALSE otherwise. + */ +GAMEPAD_API GAMEPAD_BOOL GamepadStickDirTriggered(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, GAMEPAD_STICKDIR dir); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif diff --git a/dll/dll.cpp b/dll/dll.cpp index 4232761..eec37d9 100644 --- a/dll/dll.cpp +++ b/dll/dll.cpp @@ -477,7 +477,7 @@ STEAMAPI_API ISteamHTMLSurface *SteamHTMLSurface() { PRINT_DEBUG("SteamHTMLSu STEAMAPI_API ISteamInventory *SteamInventory() { PRINT_DEBUG("SteamInventory()\n");return get_steam_client_old()->GetISteamInventory(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_inventory); } STEAMAPI_API ISteamVideo *SteamVideo() { PRINT_DEBUG("SteamVideo()\n");return get_steam_client_old()->GetISteamVideo(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_video); } STEAMAPI_API ISteamParentalSettings *SteamParentalSettings() { PRINT_DEBUG("SteamParentalSettings()\n");return get_steam_client_old()->GetISteamParentalSettings(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), ""); } - +STEAMAPI_API ISteamUnifiedMessages *SteamUnifiedMessages() { PRINT_DEBUG("SteamUnifiedMessages()\n");return get_steam_client_old()->GetISteamUnifiedMessages(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_unified_messages); } //Gameserver stuff diff --git a/dll/network.cpp b/dll/network.cpp index eff9746..e011827 100644 --- a/dll/network.cpp +++ b/dll/network.cpp @@ -71,16 +71,15 @@ static void get_broadcast_info(uint16 port) IP_ADAPTER_INFO *pAdapter = pAdapterInfo; while (pAdapter) { - unsigned long gateway = 0, subnet_mask = 0; + unsigned long iface_ip = 0, subnet_mask = 0; if (inet_pton(AF_INET, pAdapter->IpAddressList.IpMask.String, &subnet_mask) == 1 - && inet_pton(AF_INET, pAdapter->GatewayList.IpAddress.String, &gateway) == 1) { + && inet_pton(AF_INET, pAdapter->IpAddressList.IpAddress.String, &iface_ip) == 1) { IP_PORT *ip_port = &broadcasts[number_broadcasts]; //ip_port->ip.family = AF_INET; - uint32 gateway_ip = ntohl(gateway), subnet_ip = ntohl(subnet_mask); - uint32 broadcast_ip = gateway_ip + ~subnet_ip - 1; - ip_port->ip = htonl(broadcast_ip); + uint32 broadcast_ip = iface_ip | ~subnet_mask; + ip_port->ip = broadcast_ip; ip_port->port = port; number_broadcasts++; diff --git a/dll/settings.cpp b/dll/settings.cpp index 46771a4..95ab05a 100644 --- a/dll/settings.cpp +++ b/dll/settings.cpp @@ -202,4 +202,15 @@ void Settings::setLeaderboard(std::string leaderboard, enum ELeaderboardSortMeth leader.display_type = display_type; leaderboards[leaderboard] = leader; -} \ No newline at end of file +} + +int Settings::add_image(std::string data, uint32 width, uint32 height) +{ + int last = images.size() + 1; + struct Image_Data dt; + dt.width = width; + dt.height = height; + dt.data = data; + images[last] = dt; + return last; +} diff --git a/dll/settings.h b/dll/settings.h index cd6d1de..a5efb5b 100644 --- a/dll/settings.h +++ b/dll/settings.h @@ -52,6 +52,18 @@ struct Stat_config { }; }; +struct Image_Data { + uint32 width; + uint32 height; + std::string data; +}; + +struct Controller_Settings { + std::map, std::string>>> action_sets; + std::map action_set_layer_parents; + std::map, std::string>>> action_set_layers; +}; + class Settings { CSteamID steam_id; CGameID game_id; @@ -116,6 +128,13 @@ public: //stats std::map getStats() { return stats; } void setStatDefiniton(std::string name, struct Stat_config stat_config) {stats[name] = stat_config; } + + //images + std::map images; + int add_image(std::string data, uint32 width, uint32 height); + + //controller + struct Controller_Settings controller_settings; }; #endif diff --git a/dll/settings_parser.cpp b/dll/settings_parser.cpp index b2a377a..42849d3 100644 --- a/dll/settings_parser.cpp +++ b/dll/settings_parser.cpp @@ -18,6 +18,8 @@ #include "settings_parser.h" #include #include +#include +#include static void load_custom_broadcasts(std::string broadcasts_filepath, std::set &custom_broadcasts) { @@ -32,6 +34,77 @@ static void load_custom_broadcasts(std::string broadcasts_filepath, std::set +static void split_string(const std::string &s, char delim, Out result) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + *(result++) = item; + } +} + +static void load_gamecontroller_settings(Settings *settings) +{ + std::string path = Local_Storage::get_game_settings_path() + "controller"; + std::vector paths = Local_Storage::get_filenames_path(path); + + for (auto & p: paths) { + size_t length = p.length(); + if (length < 4) continue; + if ( std::toupper(p.back()) != 'T') continue; + if ( std::toupper(p[length - 2]) != 'X') continue; + if ( std::toupper(p[length - 3]) != 'T') continue; + if (p[length - 4] != '.') continue; + + PRINT_DEBUG("controller config %s\n", p.c_str()); + std::string action_set_name = p.substr(0, length - 4); + std::transform(action_set_name.begin(), action_set_name.end(), action_set_name.begin(),[](unsigned char c){ return std::toupper(c); }); + + std::string controller_config_path = path + PATH_SEPARATOR + p; + std::ifstream input( controller_config_path ); + if (input.is_open()) { + std::map, std::string>> button_pairs; + + for( std::string line; getline( input, line ); ) { + if (!line.empty() && line[line.length()-1] == '\n') { + line.pop_back(); + } + + if (!line.empty() && line[line.length()-1] == '\r') { + line.pop_back(); + } + + std::string action_name; + std::string button_name; + std::string source_mode; + + std::size_t deliminator = line.find("="); + if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) { + action_name = line.substr(0, deliminator); + std::size_t deliminator2 = line.find("=", deliminator + 1); + + if (deliminator2 != std::string::npos && deliminator2 != line.size()) { + button_name = line.substr(deliminator + 1, deliminator2 - (deliminator + 1)); + source_mode = line.substr(deliminator2 + 1); + } else { + button_name = line.substr(deliminator + 1); + source_mode = ""; + } + } + + std::transform(action_name.begin(), action_name.end(), action_name.begin(),[](unsigned char c){ return std::toupper(c); }); + std::transform(button_name.begin(), button_name.end(), button_name.begin(),[](unsigned char c){ return std::toupper(c); }); + std::pair, std::string> button_config = {{}, source_mode}; + split_string(button_name, ',', std::inserter(button_config.first, button_config.first.begin())); + button_pairs[action_name] = button_config; + PRINT_DEBUG("Added %s %s %s\n", action_name.c_str(), button_name.c_str(), source_mode.c_str()); + } + + settings->controller_settings.action_sets[action_set_name] = button_pairs; + PRINT_DEBUG("Added %u action names to %s\n", button_pairs.size(), action_set_name.c_str()); + } + } +} uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out) { @@ -386,6 +459,8 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s } } + load_gamecontroller_settings(settings_client); + *settings_client_out = settings_client; *settings_server_out = settings_server; *local_storage_out = local_storage; diff --git a/dll/steam_client.cpp b/dll/steam_client.cpp index 78c0cce..052b456 100644 --- a/dll/steam_client.cpp +++ b/dll/steam_client.cpp @@ -69,7 +69,7 @@ Steam_Client::Steam_Client() steam_remote_storage = new Steam_Remote_Storage(settings_client, local_storage, callback_results_client); steam_screenshots = new Steam_Screenshots(); steam_http = new Steam_HTTP(settings_client, network, callback_results_client, callbacks_client); - steam_controller = new Steam_Controller(); + steam_controller = new Steam_Controller(settings_client, callback_results_client, callbacks_client, run_every_runcb); steam_ugc = new Steam_UGC(settings_client, callback_results_client, callbacks_client); steam_applist = new Steam_Applist(); steam_music = new Steam_Music(callbacks_client); diff --git a/dll/steam_client.h b/dll/steam_client.h index 4284189..75c0f58 100644 --- a/dll/steam_client.h +++ b/dll/steam_client.h @@ -27,7 +27,11 @@ #include "steam_remote_storage.h" #include "steam_screenshots.h" #include "steam_http.h" +#ifdef CONTROLLER_SUPPORT #include "steam_controller.h" +#else +#include "steam_controller_disabled.h" +#endif #include "steam_ugc.h" #include "steam_applist.h" #include "steam_music.h" diff --git a/dll/steam_controller.h b/dll/steam_controller.h index 51333b6..926013d 100644 --- a/dll/steam_controller.h +++ b/dll/steam_controller.h @@ -16,6 +16,57 @@ . */ #include "base.h" +#include "../controller/gamepad.h" +#include + +struct Controller_Map { + std::map> active_digital; + std::map, enum EInputSourceMode>> active_analog; +}; + +struct Controller_Action { + ControllerHandle_t controller_handle; + struct Controller_Map active_map; + ControllerDigitalActionHandle_t active_set; + + Controller_Action(ControllerHandle_t controller_handle) { + this->controller_handle = controller_handle; + } + + void activate_action_set(ControllerDigitalActionHandle_t active_set, std::map &controller_maps) { + auto map = controller_maps.find(active_set); + if (map == controller_maps.end()) return; + this->active_set = active_set; + this->active_map = map->second; + } + + std::set button_id(ControllerDigitalActionHandle_t handle) { + auto a = active_map.active_digital.find(handle); + if (a == active_map.active_digital.end()) return {}; + return a->second; + } + + std::pair, enum EInputSourceMode> analog_id(ControllerAnalogActionHandle_t handle) { + auto a = active_map.active_analog.find(handle); + if (a == active_map.active_analog.end()) return std::pair, enum EInputSourceMode>({}, k_EInputSourceMode_None); + return a->second; + } +}; + +enum EXTRA_GAMEPAD_BUTTONS { + BUTTON_LTRIGGER = BUTTON_COUNT + 1, + BUTTON_RTRIGGER = BUTTON_COUNT + 2, + BUTTON_STICK_LEFT_UP = BUTTON_COUNT + 3, + BUTTON_STICK_LEFT_DOWN = BUTTON_COUNT + 4, + BUTTON_STICK_LEFT_LEFT = BUTTON_COUNT + 5, + BUTTON_STICK_LEFT_RIGHT = BUTTON_COUNT + 6, + BUTTON_STICK_RIGHT_UP = BUTTON_COUNT + 7, + BUTTON_STICK_RIGHT_DOWN = BUTTON_COUNT + 8, + BUTTON_STICK_RIGHT_LEFT = BUTTON_COUNT + 9, + BUTTON_STICK_RIGHT_RIGHT = BUTTON_COUNT + 10, +}; + +#define JOY_ID_START 10 class Steam_Controller : public ISteamController001, @@ -26,12 +77,171 @@ public ISteamController006, public ISteamController, public ISteamInput { + class Settings *settings; + class SteamCallResults *callback_results; + class SteamCallBacks *callbacks; + class RunEveryRunCB *run_every_runcb; + + std::map button_strings = { + {"DUP", BUTTON_DPAD_UP}, + {"DDOWN", BUTTON_DPAD_DOWN}, + {"DLEFT", BUTTON_DPAD_LEFT}, + {"DRIGHT", BUTTON_DPAD_RIGHT}, + {"START", BUTTON_START}, + {"BACK", BUTTON_BACK}, + {"LSTICK", BUTTON_LEFT_THUMB}, + {"RSTICK", BUTTON_RIGHT_THUMB}, + {"LBUMPER", BUTTON_LEFT_SHOULDER}, + {"RBUMPER", BUTTON_RIGHT_SHOULDER}, + {"A", BUTTON_A}, + {"B", BUTTON_B}, + {"X", BUTTON_X}, + {"Y", BUTTON_Y}, + {"DLTRIGGER", BUTTON_LTRIGGER}, + {"DRTRIGGER", BUTTON_RTRIGGER}, + {"DLJOYUP", BUTTON_STICK_LEFT_UP}, + {"DLJOYDOWN", BUTTON_STICK_LEFT_DOWN}, + {"DLJOYLEFT", BUTTON_STICK_LEFT_LEFT}, + {"DLJOYRIGHT", BUTTON_STICK_LEFT_RIGHT}, + {"DRJOYUP", BUTTON_STICK_RIGHT_UP}, + {"DRJOYDOWN", BUTTON_STICK_RIGHT_DOWN}, + {"DRJOYLEFT", BUTTON_STICK_RIGHT_LEFT}, + {"DRJOYRIGHT", BUTTON_STICK_RIGHT_RIGHT}, + }; + + std::map analog_strings = { + {"LTRIGGER", TRIGGER_LEFT}, + {"RTRIGGER", TRIGGER_RIGHT}, + {"LJOY", STICK_LEFT + JOY_ID_START}, + {"RJOY", STICK_RIGHT + JOY_ID_START}, + }; + + std::map analog_input_modes = { + {"joystick_move", k_EInputSourceMode_JoystickMove}, + {"joystick_camera", k_EInputSourceMode_JoystickCamera}, + {"trigger", k_EInputSourceMode_Trigger}, + }; + + std::map action_handles; + std::map digital_action_handles; + std::map analog_action_handles; + + std::map controller_maps; + std::map controllers; + + bool disabled; + + void set_handles(std::map, std::string>>> action_sets) { + uint64 handle_num = 1; + for (auto & set : action_sets) { + ControllerActionSetHandle_t action_handle_num = handle_num; + ++handle_num; + + action_handles[set.first] = action_handle_num; + for (auto & config_key : set.second) { + uint64 current_handle_num = handle_num; + ++handle_num; + + for (auto & button_string : config_key.second.first) { + auto digital = button_strings.find(button_string); + if (digital != button_strings.end()) { + ControllerDigitalActionHandle_t digital_handle_num = current_handle_num; + + if (digital_action_handles.find(config_key.first) == digital_action_handles.end()) { + digital_action_handles[config_key.first] = digital_handle_num; + } else { + digital_handle_num = digital_action_handles[config_key.first]; + } + + controller_maps[action_handle_num].active_digital[digital_handle_num].insert(digital->second); + } else { + auto analog = analog_strings.find(button_string); + if (analog != analog_strings.end()) { + ControllerAnalogActionHandle_t analog_handle_num = current_handle_num; + + enum EInputSourceMode source_mode; + if (analog->second == TRIGGER_LEFT || analog->second == TRIGGER_RIGHT) { + source_mode = k_EInputSourceMode_Trigger; + } else { + source_mode = k_EInputSourceMode_JoystickMove; + } + + auto input_mode = analog_input_modes.find(config_key.second.second); + if (input_mode != analog_input_modes.end()) { + source_mode = input_mode->second; + } + + if (analog_action_handles.find(config_key.first) == analog_action_handles.end()) { + analog_action_handles[config_key.first] = analog_handle_num; + } else { + analog_handle_num = analog_action_handles[config_key.first]; + } + + controller_maps[action_handle_num].active_analog[analog_handle_num].first.insert(analog->second); + controller_maps[action_handle_num].active_analog[analog_handle_num].second = source_mode; + + } else { + PRINT_DEBUG("Did not recognize controller button %s\n", button_string.c_str()); + continue; + } + } + } + } + } + } + public: - + +static void steam_run_every_runcb(void *object) +{ + PRINT_DEBUG("steam_controller_run_every_runcb\n"); + + Steam_Controller *steam_controller = (Steam_Controller *)object; + steam_controller->RunCallbacks(); +} + +Steam_Controller(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb) +{ + this->settings = settings; + this->run_every_runcb = run_every_runcb; + this->run_every_runcb->add(&Steam_Controller::steam_run_every_runcb, this); + + this->callback_results = callback_results; + this->callbacks = callbacks; + + set_handles(settings->controller_settings.action_sets); + disabled = !action_handles.size(); +} + +~Steam_Controller() +{ + //TODO rm network callbacks + this->run_every_runcb->remove(&Steam_Controller::steam_run_every_runcb, this); +} + // Init and Shutdown must be called when starting/ending use of this interface bool Init() { PRINT_DEBUG("Steam_Controller::Init()\n"); + std::lock_guard lock(global_mutex); + if (disabled) { + return true; + } + + GamepadInit(); + GamepadUpdate(); + + for (int i = 1; i < 5; ++i) { + struct Controller_Action cont_action(i); + //Activate the action set if there is only one present. + //TODO: I don't know if one gets activated by default when there's more than one + if (action_handles.size() == 1) { + cont_action.activate_action_set(action_handles.begin()->second, controller_maps); + } + + controllers.insert(std::pair(i, cont_action)); + } + return true; } @@ -44,6 +254,12 @@ bool Init( const char *pchAbsolutePathToControllerConfigVDF ) bool Shutdown() { PRINT_DEBUG("Steam_Controller::Shutdown()\n"); + std::lock_guard lock(global_mutex); + if (disabled) { + return true; + } + + GamepadShutdown(); return true; } @@ -58,6 +274,11 @@ void SetOverrideMode( const char *pchMode ) void RunFrame() { PRINT_DEBUG("Steam_Controller::RunFrame()\n"); + if (disabled) { + return; + } + + GamepadUpdate(); } bool GetControllerState( uint32 unControllerIndex, SteamControllerState001_t *pState ) @@ -71,8 +292,20 @@ bool GetControllerState( uint32 unControllerIndex, SteamControllerState001_t *pS // Returns the number of handles written to handlesOut int GetConnectedControllers( ControllerHandle_t *handlesOut ) { - PRINT_DEBUG("GetConnectedControllers\n"); - return 0; + PRINT_DEBUG("Steam_Controller::GetConnectedControllers\n"); + if (!handlesOut) return 0; + if (disabled) { + return 0; + } + + int count = 0; + if (GamepadIsConnected(GAMEPAD_0)) {*handlesOut = GAMEPAD_0 + 1; ++handlesOut; ++count;}; + if (GamepadIsConnected(GAMEPAD_1)) {*handlesOut = GAMEPAD_1 + 1; ++handlesOut; ++count;}; + if (GamepadIsConnected(GAMEPAD_2)) {*handlesOut = GAMEPAD_2 + 1; ++handlesOut; ++count;}; + if (GamepadIsConnected(GAMEPAD_3)) {*handlesOut = GAMEPAD_3 + 1; ++handlesOut; ++count;}; + + PRINT_DEBUG("returned %i connected controllers\n", count); + return count; } @@ -80,7 +313,7 @@ int GetConnectedControllers( ControllerHandle_t *handlesOut ) // Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode bool ShowBindingPanel( ControllerHandle_t controllerHandle ) { - PRINT_DEBUG("ShowBindingPanel\n"); + PRINT_DEBUG("Steam_Controller::ShowBindingPanel\n"); return false; } @@ -89,8 +322,15 @@ bool ShowBindingPanel( ControllerHandle_t controllerHandle ) // Lookup the handle for an Action Set. Best to do this once on startup, and store the handles for all future API calls. ControllerActionSetHandle_t GetActionSetHandle( const char *pszActionSetName ) { - PRINT_DEBUG("GetActionSetHandle %s\n", pszActionSetName); - return 124; + PRINT_DEBUG("Steam_Controller::GetActionSetHandle %s\n", pszActionSetName); + if (!pszActionSetName) return 0; + std::string upper_action_name(pszActionSetName); + std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); }); + + auto set_handle = action_handles.find(upper_action_name); + if (set_handle == action_handles.end()) return 0; + + return set_handle->second; } @@ -99,34 +339,47 @@ ControllerActionSetHandle_t GetActionSetHandle( const char *pszActionSetName ) // your state loops, instead of trying to place it in all of your state transitions. void ActivateActionSet( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle ) { - PRINT_DEBUG("ActivateActionSet\n"); + PRINT_DEBUG("Steam_Controller::ActivateActionSet %llu %llu\n", controllerHandle, actionSetHandle); + if (controllerHandle == STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS) { + for (auto & c: controllers) { + c.second.activate_action_set(actionSetHandle, controller_maps); + } + } + + auto controller = controllers.find(controllerHandle); + if (controller == controllers.end()) return; + + controller->second.activate_action_set(actionSetHandle, controller_maps); } ControllerActionSetHandle_t GetCurrentActionSet( ControllerHandle_t controllerHandle ) { - PRINT_DEBUG("GetCurrentActionSet\n"); - return 124; + PRINT_DEBUG("Steam_Controller::GetCurrentActionSet %llu\n", controllerHandle); + auto controller = controllers.find(controllerHandle); + if (controller == controllers.end()) return 0; + + return controller->second.active_set; } void ActivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle ) { - PRINT_DEBUG("ActivateActionSetLayer\n"); + PRINT_DEBUG("Steam_Controller::ActivateActionSetLayer\n"); } void DeactivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle ) { - PRINT_DEBUG("DeactivateActionSetLayer\n"); + PRINT_DEBUG("Steam_Controller::DeactivateActionSetLayer\n"); } void DeactivateAllActionSetLayers( ControllerHandle_t controllerHandle ) { - PRINT_DEBUG("DeactivateAllActionSetLayers\n"); + PRINT_DEBUG("Steam_Controller::DeactivateAllActionSetLayers\n"); } int GetActiveActionSetLayers( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t *handlesOut ) { - PRINT_DEBUG("GetActiveActionSetLayers\n"); + PRINT_DEBUG("Steam_Controller::GetActiveActionSetLayers\n"); return 0; } @@ -136,17 +389,72 @@ int GetActiveActionSetLayers( ControllerHandle_t controllerHandle, ControllerAct // Lookup the handle for a digital action. Best to do this once on startup, and store the handles for all future API calls. ControllerDigitalActionHandle_t GetDigitalActionHandle( const char *pszActionName ) { - PRINT_DEBUG("GetDigitalActionHandle %s\n", pszActionName); - return 123; + PRINT_DEBUG("Steam_Controller::GetDigitalActionHandle %s\n", pszActionName); + if (!pszActionName) return 0; + std::string upper_action_name(pszActionName); + std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); }); + + auto handle = digital_action_handles.find(upper_action_name); + if (handle == digital_action_handles.end()) return 0; + + return handle->second; } // Returns the current state of the supplied digital game action ControllerDigitalActionData_t GetDigitalActionData( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle ) { - PRINT_DEBUG("GetDigitalActionData\n"); + PRINT_DEBUG("Steam_Controller::GetDigitalActionData %llu %llu\n", controllerHandle, digitalActionHandle); ControllerDigitalActionData_t digitalData; digitalData.bActive = false; + digitalData.bState = false; + + auto controller = controllers.find(controllerHandle); + if (controller == controllers.end()) return digitalData; + + std::set buttons = controller->second.button_id(digitalActionHandle); + if (!buttons.size()) return digitalData; + digitalData.bActive = true; + + GAMEPAD_DEVICE device = (GAMEPAD_DEVICE)(controllerHandle - 1); + + for (auto button : buttons) { + bool pressed = false; + if (button < BUTTON_COUNT) { + pressed = GamepadButtonDown(device, (GAMEPAD_BUTTON)button); + } else { + switch (button) { + case BUTTON_LTRIGGER: + pressed = GamepadTriggerLength(device, TRIGGER_LEFT) > 0.8; + break; + case BUTTON_RTRIGGER: + pressed = GamepadTriggerLength(device, TRIGGER_RIGHT) > 0.8; + break; + case BUTTON_STICK_LEFT_UP: + case BUTTON_STICK_LEFT_DOWN: + case BUTTON_STICK_LEFT_LEFT: + case BUTTON_STICK_LEFT_RIGHT: + pressed = GamepadStickLength(device, STICK_LEFT) > 0.1 && + ((int)GamepadStickDir(device, STICK_LEFT) == ((button - BUTTON_STICK_LEFT_UP) + 1)); + break; + case BUTTON_STICK_RIGHT_UP: + case BUTTON_STICK_RIGHT_DOWN: + case BUTTON_STICK_RIGHT_LEFT: + case BUTTON_STICK_RIGHT_RIGHT: + pressed = GamepadStickLength(device, STICK_RIGHT) > 0.1 && + ((int)GamepadStickDir(device, STICK_RIGHT) == ((button - BUTTON_STICK_RIGHT_UP) + 1)); + break; + default: + break; + } + } + + if (pressed) { + digitalData.bState = true; + break; + } + } + return digitalData; } @@ -155,32 +463,170 @@ ControllerDigitalActionData_t GetDigitalActionData( ControllerHandle_t controlle // originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles int GetDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, EControllerActionOrigin *originsOut ) { - PRINT_DEBUG("GetDigitalActionOrigins\n"); - return 0; + PRINT_DEBUG("Steam_Controller::GetDigitalActionOrigins\n"); + EInputActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS]; + int ret = GetDigitalActionOrigins(controllerHandle, actionSetHandle, digitalActionHandle, origins ); + for (int i = 0; i < ret; ++i) { + originsOut[i] = (EControllerActionOrigin)(origins[i] - (k_EInputActionOrigin_XBox360_A - k_EControllerActionOrigin_XBox360_A)); + } + + return ret; } int GetDigitalActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputDigitalActionHandle_t digitalActionHandle, EInputActionOrigin *originsOut ) { - PRINT_DEBUG("GetDigitalActionOrigins steaminput\n"); - return 0; + PRINT_DEBUG("Steam_Controller::GetDigitalActionOrigins steaminput\n"); + auto controller = controllers.find(inputHandle); + if (controller == controllers.end()) return 0; + + auto map = controller_maps.find(actionSetHandle); + if (map == controller_maps.end()) return 0; + + auto a = map->second.active_digital.find(digitalActionHandle); + if (a == map->second.active_digital.end()) return 0; + + int count = 0; + for (auto button: a->second) { + switch (button) { + case BUTTON_A: + originsOut[count] = k_EInputActionOrigin_XBox360_A; + break; + case BUTTON_B: + originsOut[count] = k_EInputActionOrigin_XBox360_B; + break; + case BUTTON_X: + originsOut[count] = k_EInputActionOrigin_XBox360_X; + break; + case BUTTON_Y: + originsOut[count] = k_EInputActionOrigin_XBox360_Y; + break; + case BUTTON_LEFT_SHOULDER: + originsOut[count] = k_EInputActionOrigin_XBox360_LeftBumper; + break; + case BUTTON_RIGHT_SHOULDER: + originsOut[count] = k_EInputActionOrigin_XBox360_RightBumper; + break; + case BUTTON_START: + originsOut[count] = k_EInputActionOrigin_XBox360_Start; + break; + case BUTTON_BACK: + originsOut[count] = k_EInputActionOrigin_XBox360_Back; + break; + case BUTTON_LTRIGGER: + originsOut[count] = k_EInputActionOrigin_XBox360_LeftTrigger_Click; + break; + case BUTTON_RTRIGGER: + originsOut[count] = k_EInputActionOrigin_XBox360_RightTrigger_Click; + break; + case BUTTON_LEFT_THUMB: + originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_Click; + break; + case BUTTON_RIGHT_THUMB: + originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_Click; + break; + + case BUTTON_STICK_LEFT_UP: + originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadNorth; + break; + case BUTTON_STICK_LEFT_DOWN: + originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadSouth; + break; + case BUTTON_STICK_LEFT_LEFT: + originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadWest; + break; + case BUTTON_STICK_LEFT_RIGHT: + originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadEast; + break; + + case BUTTON_STICK_RIGHT_UP: + originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadNorth; + break; + case BUTTON_STICK_RIGHT_DOWN: + originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadSouth; + break; + case BUTTON_STICK_RIGHT_LEFT: + originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadWest; + break; + case BUTTON_STICK_RIGHT_RIGHT: + originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadEast; + break; + + case BUTTON_DPAD_UP: + originsOut[count] = k_EInputActionOrigin_XBox360_DPad_North; + break; + case BUTTON_DPAD_DOWN: + originsOut[count] = k_EInputActionOrigin_XBox360_DPad_South; + break; + case BUTTON_DPAD_LEFT: + originsOut[count] = k_EInputActionOrigin_XBox360_DPad_West; + break; + case BUTTON_DPAD_RIGHT: + originsOut[count] = k_EInputActionOrigin_XBox360_DPad_East; + break; + + default: + originsOut[count] = k_EInputActionOrigin_None; + break; + } + + ++count; + if (count >= STEAM_INPUT_MAX_ORIGINS) { + break; + } + } + + return count; } // Lookup the handle for an analog action. Best to do this once on startup, and store the handles for all future API calls. ControllerAnalogActionHandle_t GetAnalogActionHandle( const char *pszActionName ) { - PRINT_DEBUG("GetAnalogActionHandle %s\n", pszActionName); - return 125; + PRINT_DEBUG("Steam_Controller::GetAnalogActionHandle %s\n", pszActionName); + if (!pszActionName) return 0; + std::string upper_action_name(pszActionName); + std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); }); + + auto handle = analog_action_handles.find(upper_action_name); + if (handle == analog_action_handles.end()) return 0; + + return handle->second; } // Returns the current state of these supplied analog game action ControllerAnalogActionData_t GetAnalogActionData( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle ) { - PRINT_DEBUG("GetAnalogActionData\n"); + PRINT_DEBUG("Steam_Controller::GetAnalogActionData %llu %llu\n", controllerHandle, analogActionHandle); ControllerAnalogActionData_t data; data.eMode = k_EInputSourceMode_None; data.x = data.y = 0; data.bActive = false; + + auto controller = controllers.find(controllerHandle); + if (controller == controllers.end()) return data; + + auto analog = controller->second.analog_id(analogActionHandle); + if (!analog.first.size()) return data; + + data.bActive = true; + data.eMode = analog.second; + + for (auto a : analog.first) { + if (a >= JOY_ID_START) { + int joystick_id = a - JOY_ID_START; + GamepadStickNormXY((GAMEPAD_DEVICE)(controllerHandle - 1), (GAMEPAD_STICK) joystick_id, &data.x, &data.y); + float length = GamepadStickLength((GAMEPAD_DEVICE)(controllerHandle - 1), (GAMEPAD_STICK) joystick_id); + data.x = data.x * length; + data.y = data.y * length; + } else { + data.x = GamepadTriggerLength((GAMEPAD_DEVICE)(controllerHandle - 1), (GAMEPAD_TRIGGER) a); + } + + if (data.x || data.y) { + break; + } + } + return data; } @@ -189,32 +635,72 @@ ControllerAnalogActionData_t GetAnalogActionData( ControllerHandle_t controllerH // originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles int GetAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, EControllerActionOrigin *originsOut ) { - PRINT_DEBUG("GetAnalogActionOrigins\n"); - return 0; + PRINT_DEBUG("Steam_Controller::GetAnalogActionOrigins\n"); + EInputActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS]; + int ret = GetAnalogActionOrigins(controllerHandle, actionSetHandle, analogActionHandle, origins ); + for (int i = 0; i < ret; ++i) { + originsOut[i] = (EControllerActionOrigin)(origins[i] - (k_EInputActionOrigin_XBox360_A - k_EControllerActionOrigin_XBox360_A)); + } + + return ret; } int GetAnalogActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputAnalogActionHandle_t analogActionHandle, EInputActionOrigin *originsOut ) { - PRINT_DEBUG("GetAnalogActionOrigins steaminput\n"); + PRINT_DEBUG("Steam_Controller::GetAnalogActionOrigins steaminput\n"); + auto controller = controllers.find(inputHandle); + if (controller == controllers.end()) return 0; + + auto map = controller_maps.find(actionSetHandle); + if (map == controller_maps.end()) return 0; + + auto a = map->second.active_analog.find(analogActionHandle); + if (a == map->second.active_analog.end()) return 0; + + int count = 0; + for (auto a: a->second.first) { + switch (a) { + case TRIGGER_LEFT: + originsOut[count] = k_EInputActionOrigin_XBox360_LeftTrigger_Pull; + break; + case TRIGGER_RIGHT: + originsOut[count] = k_EInputActionOrigin_XBox360_RightTrigger_Pull; + break; + case STICK_LEFT + JOY_ID_START: + originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_Move; + break; + case STICK_RIGHT + JOY_ID_START: + originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_Move; + break; + default: + originsOut[count] = k_EInputActionOrigin_None; + break; + } + + ++count; + if (count >= STEAM_INPUT_MAX_ORIGINS) { + break; + } + } return 0; } void StopAnalogActionMomentum( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction ) { - PRINT_DEBUG("StopAnalogActionMomentum\n"); + PRINT_DEBUG("Steam_Controller::StopAnalogActionMomentum\n"); } // Trigger a haptic pulse on a controller void TriggerHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec ) { - PRINT_DEBUG("TriggerHapticPulse\n"); + PRINT_DEBUG("Steam_Controller::TriggerHapticPulse\n"); } void TriggerHapticPulse( uint32 unControllerIndex, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec ) { - PRINT_DEBUG("TriggerHapticPulse old\n"); + PRINT_DEBUG("Steam_Controller::TriggerHapticPulse old\n"); TriggerHapticPulse(unControllerIndex, eTargetPad, usDurationMicroSec ); } @@ -222,28 +708,32 @@ void TriggerHapticPulse( uint32 unControllerIndex, ESteamControllerPad eTargetPa // nFlags is currently unused and reserved for future use. void TriggerRepeatedHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags ) { - PRINT_DEBUG("TriggerRepeatedHapticPulse\n"); + PRINT_DEBUG("Steam_Controller::TriggerRepeatedHapticPulse\n"); } // Tigger a vibration event on supported controllers. void TriggerVibration( ControllerHandle_t controllerHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed ) { - PRINT_DEBUG("TriggerVibration\n"); + PRINT_DEBUG("Steam_Controller::TriggerVibration %hu %hu\n", usLeftSpeed, usRightSpeed); + auto controller = controllers.find(controllerHandle); + if (controller == controllers.end()) return; + + GamepadSetRumble((GAMEPAD_DEVICE)(controllerHandle - 1), ((double)usLeftSpeed) / 65535.0, ((double)usRightSpeed) / 65535.0); } // Set the controller LED color on supported controllers. void SetLEDColor( ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nColorG, uint8 nColorB, unsigned int nFlags ) { - PRINT_DEBUG("SetLEDColor\n"); + PRINT_DEBUG("Steam_Controller::SetLEDColor\n"); } // Returns the associated gamepad index for the specified controller, if emulating a gamepad int GetGamepadIndexForController( ControllerHandle_t ulControllerHandle ) { - PRINT_DEBUG("GetGamepadIndexForController\n"); + PRINT_DEBUG("Steam_Controller::GetGamepadIndexForController\n"); return 0; } @@ -251,7 +741,7 @@ int GetGamepadIndexForController( ControllerHandle_t ulControllerHandle ) // Returns the associated controller handle for the specified emulated gamepad ControllerHandle_t GetControllerForGamepadIndex( int nIndex ) { - PRINT_DEBUG("GetControllerForGamepadIndex\n"); + PRINT_DEBUG("Steam_Controller::GetControllerForGamepadIndex\n"); return 0; } @@ -259,7 +749,7 @@ ControllerHandle_t GetControllerForGamepadIndex( int nIndex ) // Returns raw motion data from the specified controller ControllerMotionData_t GetMotionData( ControllerHandle_t controllerHandle ) { - PRINT_DEBUG("GetMotionData\n"); + PRINT_DEBUG("Steam_Controller::GetMotionData\n"); ControllerMotionData_t data = {}; return data; } @@ -269,13 +759,13 @@ ControllerMotionData_t GetMotionData( ControllerHandle_t controllerHandle ) // Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode bool ShowDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle, float flScale, float flXPosition, float flYPosition ) { - PRINT_DEBUG("ShowDigitalActionOrigins\n"); + PRINT_DEBUG("Steam_Controller::ShowDigitalActionOrigins\n"); return true; } bool ShowAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle, float flScale, float flXPosition, float flYPosition ) { - PRINT_DEBUG("ShowAnalogActionOrigins\n"); + PRINT_DEBUG("Steam_Controller::ShowAnalogActionOrigins\n"); return true; } @@ -283,13 +773,13 @@ bool ShowAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerAna // Returns a localized string (from Steam's language setting) for the specified origin const char *GetStringForActionOrigin( EControllerActionOrigin eOrigin ) { - PRINT_DEBUG("GetStringForActionOrigin\n"); + PRINT_DEBUG("Steam_Controller::GetStringForActionOrigin\n"); return "Button String"; } const char *GetStringForActionOrigin( EInputActionOrigin eOrigin ) { - PRINT_DEBUG("GetStringForActionOrigin steaminput\n"); + PRINT_DEBUG("Steam_Controller::GetStringForActionOrigin steaminput\n"); return "Button String"; } @@ -297,75 +787,82 @@ const char *GetStringForActionOrigin( EInputActionOrigin eOrigin ) // Get a local path to art for on-screen glyph for a particular origin const char *GetGlyphForActionOrigin( EControllerActionOrigin eOrigin ) { - PRINT_DEBUG("GetGlyphForActionOrigin\n"); + PRINT_DEBUG("Steam_Controller::GetGlyphForActionOrigin\n"); return ""; } const char *GetGlyphForActionOrigin( EInputActionOrigin eOrigin ) { - PRINT_DEBUG("GetGlyphForActionOrigin steaminput\n"); + PRINT_DEBUG("Steam_Controller::GetGlyphForActionOrigin steaminput\n"); return ""; } // Returns the input type for a particular handle ESteamInputType GetInputTypeForHandle( ControllerHandle_t controllerHandle ) { - PRINT_DEBUG("GetInputTypeForHandle\n"); - return k_ESteamInputType_Unknown; + PRINT_DEBUG("Steam_Controller::GetInputTypeForHandle\n"); + auto controller = controllers.find(controllerHandle); + if (controller == controllers.end()) return k_ESteamInputType_Unknown; + return k_ESteamInputType_XBox360Controller; } const char *GetStringForXboxOrigin( EXboxOrigin eOrigin ) { - PRINT_DEBUG("GetStringForXboxOrigin\n"); + PRINT_DEBUG("Steam_Controller::GetStringForXboxOrigin\n"); return ""; } const char *GetGlyphForXboxOrigin( EXboxOrigin eOrigin ) { - PRINT_DEBUG("GetGlyphForXboxOrigin\n"); + PRINT_DEBUG("Steam_Controller::GetGlyphForXboxOrigin\n"); return ""; } EControllerActionOrigin GetActionOriginFromXboxOrigin_( ControllerHandle_t controllerHandle, EXboxOrigin eOrigin ) { - PRINT_DEBUG("GetActionOriginFromXboxOrigin\n"); + PRINT_DEBUG("Steam_Controller::GetActionOriginFromXboxOrigin\n"); return k_EControllerActionOrigin_None; } EInputActionOrigin GetActionOriginFromXboxOrigin( InputHandle_t inputHandle, EXboxOrigin eOrigin ) { - PRINT_DEBUG("GetActionOriginFromXboxOrigin steaminput\n"); + PRINT_DEBUG("Steam_Controller::GetActionOriginFromXboxOrigin steaminput\n"); return k_EInputActionOrigin_None; } EControllerActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EControllerActionOrigin eSourceOrigin ) { - PRINT_DEBUG("TranslateActionOrigin\n"); + PRINT_DEBUG("Steam_Controller::TranslateActionOrigin\n"); return k_EControllerActionOrigin_None; } EInputActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EInputActionOrigin eSourceOrigin ) { - PRINT_DEBUG("TranslateActionOrigin steaminput\n"); + PRINT_DEBUG("Steam_Controller::TranslateActionOrigin steaminput\n"); return k_EInputActionOrigin_None; } bool GetControllerBindingRevision( ControllerHandle_t controllerHandle, int *pMajor, int *pMinor ) { - PRINT_DEBUG("GetControllerBindingRevision\n"); + PRINT_DEBUG("Steam_Controller::GetControllerBindingRevision\n"); return false; } bool GetDeviceBindingRevision( InputHandle_t inputHandle, int *pMajor, int *pMinor ) { - PRINT_DEBUG("GetDeviceBindingRevision\n"); + PRINT_DEBUG("Steam_Controller::GetDeviceBindingRevision\n"); return false; } uint32 GetRemotePlaySessionID( InputHandle_t inputHandle ) { - PRINT_DEBUG("GetRemotePlaySessionID\n"); + PRINT_DEBUG("Steam_Controller::GetRemotePlaySessionID\n"); return 0; } +void RunCallbacks() +{ + RunFrame(); +} + }; diff --git a/dll/steam_controller_disabled.h b/dll/steam_controller_disabled.h new file mode 100644 index 0000000..825ff0b --- /dev/null +++ b/dll/steam_controller_disabled.h @@ -0,0 +1,375 @@ +/* Copyright (C) 2019 Mr Goldberg + This file is part of the Goldberg Emulator + + The Goldberg Emulator is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + The Goldberg Emulator 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the Goldberg Emulator; if not, see + . */ + +#include "base.h" + +class Steam_Controller : +public ISteamController001, +public ISteamController003, +public ISteamController004, +public ISteamController005, +public ISteamController006, +public ISteamController, +public ISteamInput +{ +public: + +Steam_Controller(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb) +{ +} + +// Init and Shutdown must be called when starting/ending use of this interface +bool Init() +{ + PRINT_DEBUG("Steam_Controller::Init()\n"); + return true; +} + +bool Init( const char *pchAbsolutePathToControllerConfigVDF ) +{ + PRINT_DEBUG("Steam_Controller::Init() old\n"); + return Init(); +} + +bool Shutdown() +{ + PRINT_DEBUG("Steam_Controller::Shutdown()\n"); + return true; +} + +void SetOverrideMode( const char *pchMode ) +{ + PRINT_DEBUG("Steam_Controller::SetOverrideMode\n"); +} + +// Synchronize API state with the latest Steam Controller inputs available. This +// is performed automatically by SteamAPI_RunCallbacks, but for the absolute lowest +// possible latency, you call this directly before reading controller state. +void RunFrame() +{ + PRINT_DEBUG("Steam_Controller::RunFrame()\n"); +} + +bool GetControllerState( uint32 unControllerIndex, SteamControllerState001_t *pState ) +{ + PRINT_DEBUG("Steam_Controller::GetControllerState()\n"); + return false; +} + +// Enumerate currently connected controllers +// handlesOut should point to a STEAM_CONTROLLER_MAX_COUNT sized array of ControllerHandle_t handles +// Returns the number of handles written to handlesOut +int GetConnectedControllers( ControllerHandle_t *handlesOut ) +{ + PRINT_DEBUG("GetConnectedControllers\n"); + return 0; +} + + +// Invokes the Steam overlay and brings up the binding screen +// Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode +bool ShowBindingPanel( ControllerHandle_t controllerHandle ) +{ + PRINT_DEBUG("ShowBindingPanel\n"); + return false; +} + + +// ACTION SETS +// Lookup the handle for an Action Set. Best to do this once on startup, and store the handles for all future API calls. +ControllerActionSetHandle_t GetActionSetHandle( const char *pszActionSetName ) +{ + PRINT_DEBUG("GetActionSetHandle %s\n", pszActionSetName); + return 124; +} + + +// Reconfigure the controller to use the specified action set (ie 'Menu', 'Walk' or 'Drive') +// This is cheap, and can be safely called repeatedly. It's often easier to repeatedly call it in +// your state loops, instead of trying to place it in all of your state transitions. +void ActivateActionSet( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle ) +{ + PRINT_DEBUG("ActivateActionSet\n"); +} + +ControllerActionSetHandle_t GetCurrentActionSet( ControllerHandle_t controllerHandle ) +{ + PRINT_DEBUG("GetCurrentActionSet\n"); + return 124; +} + + +void ActivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle ) +{ + PRINT_DEBUG("ActivateActionSetLayer\n"); +} + +void DeactivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle ) +{ + PRINT_DEBUG("DeactivateActionSetLayer\n"); +} + +void DeactivateAllActionSetLayers( ControllerHandle_t controllerHandle ) +{ + PRINT_DEBUG("DeactivateAllActionSetLayers\n"); +} + +int GetActiveActionSetLayers( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t *handlesOut ) +{ + PRINT_DEBUG("GetActiveActionSetLayers\n"); + return 0; +} + + + +// ACTIONS +// Lookup the handle for a digital action. Best to do this once on startup, and store the handles for all future API calls. +ControllerDigitalActionHandle_t GetDigitalActionHandle( const char *pszActionName ) +{ + PRINT_DEBUG("GetDigitalActionHandle %s\n", pszActionName); + return 123; +} + + +// Returns the current state of the supplied digital game action +ControllerDigitalActionData_t GetDigitalActionData( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle ) +{ + PRINT_DEBUG("GetDigitalActionData\n"); + ControllerDigitalActionData_t digitalData; + digitalData.bActive = false; + return digitalData; +} + + +// Get the origin(s) for a digital action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action. +// originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles +int GetDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, EControllerActionOrigin *originsOut ) +{ + PRINT_DEBUG("GetDigitalActionOrigins\n"); + return 0; +} + +int GetDigitalActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputDigitalActionHandle_t digitalActionHandle, EInputActionOrigin *originsOut ) +{ + PRINT_DEBUG("GetDigitalActionOrigins steaminput\n"); + return 0; +} + +// Lookup the handle for an analog action. Best to do this once on startup, and store the handles for all future API calls. +ControllerAnalogActionHandle_t GetAnalogActionHandle( const char *pszActionName ) +{ + PRINT_DEBUG("GetAnalogActionHandle %s\n", pszActionName); + return 125; +} + + +// Returns the current state of these supplied analog game action +ControllerAnalogActionData_t GetAnalogActionData( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle ) +{ + PRINT_DEBUG("GetAnalogActionData\n"); + ControllerAnalogActionData_t data; + data.eMode = k_EInputSourceMode_None; + data.x = data.y = 0; + data.bActive = false; + return data; +} + + +// Get the origin(s) for an analog action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action. +// originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles +int GetAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, EControllerActionOrigin *originsOut ) +{ + PRINT_DEBUG("GetAnalogActionOrigins\n"); + return 0; +} + +int GetAnalogActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputAnalogActionHandle_t analogActionHandle, EInputActionOrigin *originsOut ) +{ + PRINT_DEBUG("GetAnalogActionOrigins steaminput\n"); + return 0; +} + + +void StopAnalogActionMomentum( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction ) +{ + PRINT_DEBUG("StopAnalogActionMomentum\n"); +} + + +// Trigger a haptic pulse on a controller +void TriggerHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec ) +{ + PRINT_DEBUG("TriggerHapticPulse\n"); +} + +void TriggerHapticPulse( uint32 unControllerIndex, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec ) +{ + PRINT_DEBUG("TriggerHapticPulse old\n"); + TriggerHapticPulse(unControllerIndex, eTargetPad, usDurationMicroSec ); +} + +// Trigger a pulse with a duty cycle of usDurationMicroSec / usOffMicroSec, unRepeat times. +// nFlags is currently unused and reserved for future use. +void TriggerRepeatedHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags ) +{ + PRINT_DEBUG("TriggerRepeatedHapticPulse\n"); +} + + +// Tigger a vibration event on supported controllers. +void TriggerVibration( ControllerHandle_t controllerHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed ) +{ + PRINT_DEBUG("TriggerVibration\n"); +} + + +// Set the controller LED color on supported controllers. +void SetLEDColor( ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nColorG, uint8 nColorB, unsigned int nFlags ) +{ + PRINT_DEBUG("SetLEDColor\n"); +} + + +// Returns the associated gamepad index for the specified controller, if emulating a gamepad +int GetGamepadIndexForController( ControllerHandle_t ulControllerHandle ) +{ + PRINT_DEBUG("GetGamepadIndexForController\n"); + return 0; +} + + +// Returns the associated controller handle for the specified emulated gamepad +ControllerHandle_t GetControllerForGamepadIndex( int nIndex ) +{ + PRINT_DEBUG("GetControllerForGamepadIndex\n"); + return 0; +} + + +// Returns raw motion data from the specified controller +ControllerMotionData_t GetMotionData( ControllerHandle_t controllerHandle ) +{ + PRINT_DEBUG("GetMotionData\n"); + ControllerMotionData_t data = {}; + return data; +} + + +// Attempt to display origins of given action in the controller HUD, for the currently active action set +// Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode +bool ShowDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle, float flScale, float flXPosition, float flYPosition ) +{ + PRINT_DEBUG("ShowDigitalActionOrigins\n"); + return true; +} + +bool ShowAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle, float flScale, float flXPosition, float flYPosition ) +{ + PRINT_DEBUG("ShowAnalogActionOrigins\n"); + return true; +} + + +// Returns a localized string (from Steam's language setting) for the specified origin +const char *GetStringForActionOrigin( EControllerActionOrigin eOrigin ) +{ + PRINT_DEBUG("GetStringForActionOrigin\n"); + return "Button String"; +} + +const char *GetStringForActionOrigin( EInputActionOrigin eOrigin ) +{ + PRINT_DEBUG("GetStringForActionOrigin steaminput\n"); + return "Button String"; +} + + +// Get a local path to art for on-screen glyph for a particular origin +const char *GetGlyphForActionOrigin( EControllerActionOrigin eOrigin ) +{ + PRINT_DEBUG("GetGlyphForActionOrigin\n"); + return ""; +} + +const char *GetGlyphForActionOrigin( EInputActionOrigin eOrigin ) +{ + PRINT_DEBUG("GetGlyphForActionOrigin steaminput\n"); + return ""; +} + +// Returns the input type for a particular handle +ESteamInputType GetInputTypeForHandle( ControllerHandle_t controllerHandle ) +{ + PRINT_DEBUG("GetInputTypeForHandle\n"); + return k_ESteamInputType_Unknown; +} + +const char *GetStringForXboxOrigin( EXboxOrigin eOrigin ) +{ + PRINT_DEBUG("GetStringForXboxOrigin\n"); + return ""; +} + +const char *GetGlyphForXboxOrigin( EXboxOrigin eOrigin ) +{ + PRINT_DEBUG("GetGlyphForXboxOrigin\n"); + return ""; +} + +EControllerActionOrigin GetActionOriginFromXboxOrigin_( ControllerHandle_t controllerHandle, EXboxOrigin eOrigin ) +{ + PRINT_DEBUG("GetActionOriginFromXboxOrigin\n"); + return k_EControllerActionOrigin_None; +} + +EInputActionOrigin GetActionOriginFromXboxOrigin( InputHandle_t inputHandle, EXboxOrigin eOrigin ) +{ + PRINT_DEBUG("GetActionOriginFromXboxOrigin steaminput\n"); + return k_EInputActionOrigin_None; +} + +EControllerActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EControllerActionOrigin eSourceOrigin ) +{ + PRINT_DEBUG("TranslateActionOrigin\n"); + return k_EControllerActionOrigin_None; +} + +EInputActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EInputActionOrigin eSourceOrigin ) +{ + PRINT_DEBUG("TranslateActionOrigin steaminput\n"); + return k_EInputActionOrigin_None; +} + +bool GetControllerBindingRevision( ControllerHandle_t controllerHandle, int *pMajor, int *pMinor ) +{ + PRINT_DEBUG("GetControllerBindingRevision\n"); + return false; +} + +bool GetDeviceBindingRevision( InputHandle_t inputHandle, int *pMajor, int *pMinor ) +{ + PRINT_DEBUG("GetDeviceBindingRevision\n"); + return false; +} + +uint32 GetRemotePlaySessionID( InputHandle_t inputHandle ) +{ + PRINT_DEBUG("GetRemotePlaySessionID\n"); + return 0; +} + +}; diff --git a/dll/steam_friends.h b/dll/steam_friends.h index d12a680..a74931f 100644 --- a/dll/steam_friends.h +++ b/dll/steam_friends.h @@ -23,6 +23,12 @@ #define SEND_FRIEND_RATE 4.0 +struct Avatar_Numbers { + int smallest; + int medium; + int large; +}; + class Steam_Friends : public ISteamFriends004, public ISteamFriends005, @@ -50,7 +56,7 @@ public ISteamFriends bool modified; std::vector friends; - unsigned img_count; + std::map avatars; CSteamID lobby_id; std::chrono::high_resolution_clock::time_point last_sent_friends; @@ -87,6 +93,29 @@ bool isAppIdCompatible(Friend *f) return settings->get_local_game_id().AppID() == f->appid(); } +struct Avatar_Numbers add_friend_avatars(CSteamID id) +{ + uint64 steam_id = id.ConvertToUint64(); + auto avatar_ids = avatars.find(steam_id); + if (avatar_ids != avatars.end()) { + return avatar_ids->second; + } + + //TODO: get real image data from self/other peers + struct Avatar_Numbers avatar_numbers; + char zero_array[184 * 184 * 4] = {}; + std::string small_avatar(zero_array, 32 * 32 * 4); + std::string medium_avatar(zero_array, 64 * 64 * 4); + std::string large_avatar(zero_array, 184 * 184 * 4); + + avatar_numbers.smallest = settings->add_image(small_avatar, 32, 32); + avatar_numbers.medium = settings->add_image(medium_avatar, 64, 64); + avatar_numbers.large = settings->add_image(large_avatar, 184, 184); + + avatars[steam_id] = avatar_numbers; + return avatar_numbers; +} + public: static void steam_friends_callback(void *object, Common_Message *msg) { @@ -559,8 +588,10 @@ void ActivateGameOverlayInviteDialog( CSteamID steamIDLobby ) int GetSmallFriendAvatar( CSteamID steamIDFriend ) { PRINT_DEBUG("Steam_Friends::GetSmallFriendAvatar\n"); - ++img_count; - return (img_count * 3) + 1; + //IMPORTANT NOTE: don't change friend avatar numbers for the same friend or else some games endlessly allocate stuff. + std::lock_guard lock(global_mutex); + struct Avatar_Numbers numbers = add_friend_avatars(steamIDFriend); + return numbers.smallest; } @@ -568,8 +599,9 @@ int GetSmallFriendAvatar( CSteamID steamIDFriend ) int GetMediumFriendAvatar( CSteamID steamIDFriend ) { PRINT_DEBUG("Steam_Friends::GetMediumFriendAvatar\n"); - ++img_count; - return (img_count * 3) + 2; + std::lock_guard lock(global_mutex); + struct Avatar_Numbers numbers = add_friend_avatars(steamIDFriend); + return numbers.medium; } @@ -578,8 +610,9 @@ int GetMediumFriendAvatar( CSteamID steamIDFriend ) int GetLargeFriendAvatar( CSteamID steamIDFriend ) { PRINT_DEBUG("Steam_Friends::GetLargeFriendAvatar\n"); - ++img_count; - return (img_count * 3) + 0; + std::lock_guard lock(global_mutex); + struct Avatar_Numbers numbers = add_friend_avatars(steamIDFriend); + return numbers.large; } int GetFriendAvatar( CSteamID steamIDFriend, int eAvatarSize ) @@ -678,19 +711,19 @@ bool SetRichPresence( const char *pchKey, const char *pchValue ) PRINT_DEBUG("Steam_Friends::SetRichPresence %s %s\n", pchKey, pchValue ? pchValue : "NULL"); std::lock_guard lock(global_mutex); if (pchValue) { - #ifdef SHOW_DIALOG_RICH_CONNECT - if (std::string(pchKey) == std::string("connect")) - MessageBox(0, pchValue, pchKey, MB_OK); - #endif - (*us.mutable_rich_presence())[pchKey] = pchValue; + auto prev_value = (*us.mutable_rich_presence()).find(pchKey); + if (prev_value == (*us.mutable_rich_presence()).end() || prev_value->second != pchValue) { + (*us.mutable_rich_presence())[pchKey] = pchValue; + modified = true; + } } else { auto to_remove = us.mutable_rich_presence()->find(pchKey); if (to_remove != us.mutable_rich_presence()->end()) { us.mutable_rich_presence()->erase(to_remove); + modified = true; } } - modified = true; - + return true; } diff --git a/dll/steam_utils.h b/dll/steam_utils.h index 26c2702..fd8061f 100644 --- a/dll/steam_utils.h +++ b/dll/steam_utils.h @@ -84,39 +84,39 @@ const char *GetIPCountry() return "US"; } -static uint32 width_image(int iImage) -{ - if ((iImage % 3) == 1) return 32; - if ((iImage % 3) == 2) return 64; - return 184; -} - // returns true if the image exists, and valid sizes were filled out bool GetImageSize( int iImage, uint32 *pnWidth, uint32 *pnHeight ) { - PRINT_DEBUG("GetImageSize\n"); + PRINT_DEBUG("GetImageSize %i\n", iImage); if (!iImage || !pnWidth || !pnHeight) return false; + std::lock_guard lock(global_mutex); - *pnWidth = width_image(iImage); - *pnHeight = width_image(iImage);; + auto image = settings->images.find(iImage); + if (image == settings->images.end()) return false; + + *pnWidth = image->second.width; + *pnHeight = image->second.height; return true; } - // returns true if the image exists, and the buffer was successfully filled out // results are returned in RGBA format // the destination buffer size should be 4 * height * width * sizeof(char) bool GetImageRGBA( int iImage, uint8 *pubDest, int nDestBufferSize ) { - PRINT_DEBUG("GetImageRGBA\n"); + PRINT_DEBUG("GetImageRGBA %i\n", iImage); if (!iImage || !pubDest || !nDestBufferSize) return false; - unsigned size = width_image(iImage) * width_image(iImage) * 4; + std::lock_guard lock(global_mutex); + + auto image = settings->images.find(iImage); + if (image == settings->images.end()) return false; + + unsigned size = image->second.data.size(); if (nDestBufferSize < size) size = nDestBufferSize; - memset(pubDest, 0xFF, size); + image->second.data.copy((char *)pubDest, nDestBufferSize); return true; } - // returns the IP of the reporting server for valve - currently only used in Source engine games bool GetCSERIPPort( uint32 *unIP, uint16 *usPort ) { diff --git a/files_example/steam_settings.EXAMPLE/controller.EXAMPLE/InGameControls.txt b/files_example/steam_settings.EXAMPLE/controller.EXAMPLE/InGameControls.txt new file mode 100644 index 0000000..e53d558 --- /dev/null +++ b/files_example/steam_settings.EXAMPLE/controller.EXAMPLE/InGameControls.txt @@ -0,0 +1,17 @@ +Move=LJOY=joystick_move +Camera=RJOY=joystick_move +Dash=RTRIGGER=trigger +LockOn=LTRIGGER=trigger +pause_menu=START +LightAttack=X +HeavyAttack=Y +Jump=A +check=B +ActivateSkillAndMagic=RBUMPER +IdeaRelease=LBUMPER +AllMap=BACK +ResetCamera=RSTICK +ChangeCharacterUp=DUP +ChangeCharacterDown=DDOWN +ChangeCharacterRight=DRIGHT +ChangeCharacterLeft=DLEFT diff --git a/files_example/steam_settings.EXAMPLE/controller.EXAMPLE/MenuControls.txt b/files_example/steam_settings.EXAMPLE/controller.EXAMPLE/MenuControls.txt new file mode 100644 index 0000000..181c107 --- /dev/null +++ b/files_example/steam_settings.EXAMPLE/controller.EXAMPLE/MenuControls.txt @@ -0,0 +1,16 @@ +MenuDash=RTRIGGER=trigger +MenuLockOn=LTRIGGER=trigger +MenuUp=DUP,DLJOYUP +MenuDown=DDOWN,DLJOYDOWN +MenuRight=DRIGHT,DLJOYRIGHT +MenuLeft=DLEFT,DLJOYLEFT +SELECT=A +Cancel=B +MenuX=X +MenuY=Y +MenuRTrigger=RBUMPER +MenuLTrigger=LBUMPER +Sort=BACK +PauseMenu=START +ScrollUp=DRJOYUP +ScrollDown=DRJOYDOWN