Steam Input API core support#1695
Conversation
src/game/game.cpp
Outdated
| if(d) | ||
| { | ||
| float scale = (focus == player1 && inzoom() && zoomsensitivity > 0 ? (1.f-((zoomlevel+1)/float(zoomlevels+2)))*zoomsensitivity : 1.f)*sensitivity; | ||
| float scale = zoomsens()*sensitivity; |
There was a problem hiding this comment.
The zoom scaling code got factored out into a helper function so that it could be used inside the controller code.
For this, Looks like you need to add |
|
There's been some more stuff since I posted |
There was a problem hiding this comment.
Settings menu item for glyph prompts. 'Automatic' is probably what most people want (dynamically changes glyphs based on last input), but - as is considered good practice - there are options to explicitly lock glyphs if needed. Because Steam Input handles different hardware types for us, the only glyph distinction we need make is KB/M vs controller. This is technically an interface thing, but I feel like it makes more sense to have it together with the controls options.
KNOWN ISSUE 1: The box for this is weirdly not as wide as the other boxes in the menu. I probably copied a setting I didn't intend.
KNOWN ISSUE 2: This box needs to be hidden for non-Steam builds since it will be connected to a variable that will do nothing without SIAPI support.
It may not be worth fiddling with this much because I still need to add a button to open up the controller configurator interface provided by Steam.
config/keymap.cfg
Outdated
| // Steam Input API controller actions | ||
| keymap -20 SIAPI_PRIMARY | ||
| keymap -21 SIAPI_SECONDARY | ||
| keymap -22 SIAPI_RELOAD | ||
| keymap -23 SIAPI_USE | ||
| keymap -24 SIAPI_JUMP | ||
| keymap -25 SIAPI_WALK | ||
| keymap -26 SIAPI_CROUCH | ||
| keymap -27 SIAPI_SPECIAL | ||
| keymap -28 SIAPI_DROP | ||
| keymap -29 SIAPI_AFFINITY | ||
| keymap -30 SIAPI_DASH | ||
| keymap -31 SIAPI_NEXT_WEAPON | ||
| keymap -32 SIAPI_PREVIOUS_WEAPON | ||
| keymap -33 SIAPI_PRIMARY_WEAPON | ||
| keymap -34 SIAPI_SECONDARY_WEAPON | ||
| keymap -35 SIAPI_WHEEL_SELECT | ||
| keymap -36 SIAPI_CHANGE_LOADOUT | ||
| keymap -37 SIAPI_SCOREBOARD | ||
| keymap -38 SIAPI_SUICIDE | ||
| keymap -39 SIAPI_MENU |
There was a problem hiding this comment.
I added the SIAPI actions to the keymap, as requested. Because only the SIAPI support code should hit these 'keys' in the keymap and there is an enum to map the codes to nice names in the code, this keymap range can be moved theoretically at any time without risk of breaking anything.
| // Steam Input controller binds; these actions _should not be rebound_ - do it | ||
| // through the Steam Input configuration interface | ||
| bind SIAPI_PRIMARY [ primary ] | ||
| specbind SIAPI_PRIMARY [ spectate 0 ] | ||
| bind SIAPI_SECONDARY [ secondary ] | ||
| specbind SIAPI_SECONDARY [ spectate 0 ] | ||
| bind SIAPI_WHEEL_SELECT [ game_hud_piemenu_open_weapsel_key ] // Currently broken | ||
| bind SIAPI_NEXT_WEAPON [ universaldelta 1 ] | ||
| bind SIAPI_PREVIOUS_WEAPON [ universaldelta -1 ] | ||
| bind SIAPI_PRIMARY_WEAPON [ weapon (weapload 0) 1 ] | ||
| bind SIAPI_SECONDARY_WEAPON [ weapon (weapload 1) 1 ] | ||
| bind SIAPI_MENU [ uitoggle ] | ||
| bind SIAPI_JUMP [ jump ] | ||
| specbind SIAPI_JUMP [ specmodeswitch ] | ||
| bind SIAPI_SPECIAL [ special ] | ||
| bind SIAPI_CROUCH [ crouch ] | ||
| bind SIAPI_USE [ use ] | ||
| specbind SIAPI_USE [ spectate 0 ] | ||
| bind SIAPI_RELOAD [ reload ] | ||
| specbind SIAPI_RELOAD [ specmodeswitch ] | ||
| bind SIAPI_DROP [ drop ] | ||
| bind SIAPI_AFFINITY [ affinity ] | ||
| bind SIAPI_SCOREBOARD [ showscores ] | ||
| bind SIAPI_CHANGE_LOADOUT [ gameui_player_show_loadout ] | ||
| bind SIAPI_SUICIDE [ suicide ] | ||
|
|
There was a problem hiding this comment.
Bind the SIAPI actions. You are not supposed to rebind these actions in-engine; you are intended to use the Steam Input configurator tool and change things there.
| struct textkey | ||
| { | ||
| char *name, *file; | ||
| Texture *tex; | ||
| textkey() : name(NULL), file(NULL), tex(NULL) {} | ||
| textkey(char *n, char *f, Texture *t) : name(newstring(n)), file(newstring(f)), tex(t) {} | ||
| ~textkey() | ||
| { | ||
| DELETEA(name); | ||
| DELETEA(file); | ||
| } | ||
| }; |
There was a problem hiding this comment.
This moved to rendertext.h because I needed the struct to be available elsewhere.
| bool shouldkeepkey(const char *str) | ||
| { | ||
| bool is_siapi_textkey = controller::is_siapi_textkey(str); | ||
| switch (textkeyimagepreference) { | ||
| case tkip_automatic: | ||
| return controller::lastinputwassiapi ? is_siapi_textkey : !is_siapi_textkey; | ||
| case tkip_kbm: | ||
| return !is_siapi_textkey; | ||
| case tkip_controller: | ||
| return is_siapi_textkey; | ||
| case tkip_both: | ||
| return true; | ||
| }; | ||
| } | ||
|
|
There was a problem hiding this comment.
Pursuant to above variable, there is now support in the textkey system to filter out glyphs
src/game/controller.cpp
Outdated
| das->tk->file = NULL; // we don't use this here | ||
| das->tk->name = newstring(str); | ||
| } | ||
| // We have to check if the origin has changed, and if so, reload the texture |
There was a problem hiding this comment.
https://partner.steamgames.com/doc/features/steam_controller/getting_started_for_devs says that
Please note that the user can change their configuration at any time. To help with this, we've ensured the ISteamInput::GetDigitalActionOrigins and ISteamInput::GetAnalogActionOrigins functions are extremely cheap to call.
When you display an onscreen prompt, don't cache the origins and keep displaying the first results. Instead, we recommend you re-gather the origins each frame, and display the matching prompts. That way, if a user decides to change their configuration as a result of seeing the prompt, when they return from the configuration screen the prompts will automatically update to match the new origins.
In order to honor this best practice, textkeys related to SIAPI actions are handled differently and not cached in the same way that regular KB/M ones are.
src/game/controller.cpp
Outdated
| public: | ||
| InputDigitalActionHandle_t handle = -1; | ||
| int keymap_id = -1; | ||
| // Should be more than one origin eventually! |
There was a problem hiding this comment.
SIAPI actions can have up to 8 origins (which is SIAPIese for 'the button this action is bound to'), but this implementation doesn't yet support actually using them all. It's going to require a non-trivial refactoring of the textkey rendering system to support this.
Inspiration and encouragement courtesy of Mennenth
| vector<textkey *> findtextkeys(const char *str) | ||
| { | ||
| // SIAPI actions have special handling because there are several fundamental | ||
| // differences between SIAPI textkeys and KB/M textkeys | ||
| if(controller::is_siapi_textkey(str)) return controller::get_siapi_textkeys(str); | ||
|
|
||
| textkey *tk = findtextkey_common(str, textkeys, NULL); | ||
|
|
||
| // Should probably just arrange to have this vector be 1 long at | ||
| // initialization, but I don't know how to do this... | ||
| if(!_findtextkeys_container.capacity()) _findtextkeys_container.add(tk); | ||
| else _findtextkeys_container[0] = tk; | ||
|
|
||
| return _findtextkeys_container; | ||
|
|
||
| } |
There was a problem hiding this comment.
This function was redone to return a vector of textkeys instead of a single textkey. This is necessary because Steam Input allows a single action to have multiple origins, so that codepath could potentially have more than one textkey it needs to return. The pure KB/M path only uses its vector as a wrapper and will only ever return 1 textkey.
| }; | ||
|
|
||
| #define WINSTYLE_ENUM(en, um) en(um, Normal, NORMAL) en(um, Tool Tip, TOOLTIP) en(um, Popup, POPUP) en(um, Crosshair, CROSSHAIR) en(um, Cursor, CURSOR) en(um, Max, MAX) | ||
| #define WINSTYLE_ENUM(en, um) en(um, Normal, NORMAL) en(um, Pie, PIE) en(um, Gameplay, GAMEPLAY) en(um, Tool Tip, TOOLTIP) en(um, Popup, POPUP) en(um, Crosshair, CROSSHAIR) en(um, Cursor, CURSOR) en(um, Max, MAX) |
There was a problem hiding this comment.
There are new window styles for 'gameplay' and 'pie'. These window styles aren't supposed to affect rendering/handling, but are context markers that tell the controller code how to handle certain scenarios.
| bool menuisgameplay() | ||
| { | ||
| if(surfaceinput == type) loopwindows(w, | ||
| { | ||
| if(!w->visible || !checkexclusive(w) || w->winstyle >= WINSTYLE_CROSSHAIR) continue; | ||
| if(!(w->state&STATE_HIDDEN) && (w->winstyle == WINSTYLE_PIE || w->winstyle == WINSTYLE_GAMEPLAY)) return true; | ||
| }); | ||
| return false; | ||
| } |
There was a problem hiding this comment.
This function and its sibling are possibly too dumb, but any more complicated logic and they would just not work correctly. This implementation is good enough for my needs, but making it smarter would be good.
| VAR(0, mouseoverride, 0, 0, 3); | ||
| // FIXME: We take x and y parameters but don't use them. A relic of an | ||
| // earlier implementation? Delete if possible. | ||
| bool mousemove(float dx, float dy, int x, int y, int w, int h, bool fromcontroller) |
There was a problem hiding this comment.
This function was changed to distinguish input coming from the mouse and input coming from controllers. The biggest notable difference is that controller inputs deliberately ignore in-engine sensitivity settings and rely entirely on SIAPI sensitivity settings (see the various comments explaining why I do this).
| void resetplayerpitch() | ||
| { | ||
| if(!gs_waiting(gamestate) && (mouseoverride&1 || (!mouseoverride && !tvmode()))) | ||
| { | ||
| if((!gs_playing(gamestate) || player1->state >= CS_SPECTATOR && (focus == player1 || followaim()))) return; | ||
| if(allowmove(player1)) player1->pitch = 0.0f; | ||
| } | ||
| } |
There was a problem hiding this comment.
Resetting the player pitch was implemented as a 'proper' interface instead of directly accessing the player, which I was told I shouldn't have been doing.
| return data.bState; | ||
| } | ||
|
|
||
| class digital_action_state |
There was a problem hiding this comment.
Maybe this can be a struct instead?
|
|
||
| digital_action_state *get_das_for_keymap_name(const char *str) | ||
| { | ||
| // This function is awful, redo to be smarter |
There was a problem hiding this comment.
There has got to be a better way. Is there a hashmap or something we can use?
| #else /* defined(USE_STEAM) */ | ||
| void update_from_controller() | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| bool is_siapi_textkey(const char *str) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| vector <textkey *> get_siapi_textkeys(const char *str) | ||
| { | ||
| return textkeyvec; | ||
| } | ||
|
|
||
| ICOMMAND(0, showsiapibindpanel, "", (), { return; }); | ||
| #endif /* defined(USE_STEAM) */ |
There was a problem hiding this comment.
There are suitable dummy functions exported in non-Steam builds
WIP for Steam Input support, as requested on Discord.
There are several things that don't work, aren't implemented the way I'm supposed to, etc.
Perhaps the most important thing to note is that I broke the build for the Red Eclipse server. That needs addressing but it's not short-term important.