Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9c723da
WIP: Initial parts of Steam Input API support
RobertCochran Jan 28, 2026
6859ab8
Remove leftover debug include
RobertCochran Jan 29, 2026
0a13193
Don't require both X and Y movement to consider it a controller move
RobertCochran Jan 29, 2026
d6d88e9
Move to using keymaps instead of directly calling doaction
RobertCochran Jan 29, 2026
57e5cbd
Make the handle and keymap_id public members
RobertCochran Jan 30, 2026
c82ecbe
Deduplicate function
RobertCochran Jan 30, 2026
af30273
Initial glyph implementation
RobertCochran Feb 3, 2026
bf5482d
Add menu action set, do cursor/camera actions 'the right way'
RobertCochran Feb 5, 2026
36f75cb
Add 'real' interface to reset player pitch
RobertCochran Feb 7, 2026
645f108
Accept input from all connected controllers
RobertCochran Feb 7, 2026
86e0406
Be prepared to initialize SIAPI handles after boot
RobertCochran Feb 7, 2026
7a1034c
Fix default state of some handles
RobertCochran Feb 8, 2026
db87ec2
Add 'open controller configurator' button to controls settings
RobertCochran Feb 8, 2026
993a7ef
Add actions for every individual weapon
RobertCochran Feb 8, 2026
6488141
Add editing mode action set
RobertCochran Feb 8, 2026
26619b9
Move handle init check to after controller count check
RobertCochran Feb 8, 2026
9b13b63
Add SIAPI action for 'last weapon'
RobertCochran Feb 8, 2026
32c6e9e
Add gameplay window style and use to sense in vs out of game menus
RobertCochran Feb 11, 2026
cff5332
Factor out common textkey fetch code and also use in SIAPI code
RobertCochran Feb 14, 2026
ffbbac3
Add multiple origins support for SIAPI actions
RobertCochran Feb 14, 2026
2e7aa7f
Add joystick-like mode for pie menu cursor
RobertCochran Feb 14, 2026
dcb8364
Kill tabs in files that already existed
RobertCochran Feb 14, 2026
5d08d26
Format more like the Romans do
RobertCochran Feb 14, 2026
f152889
Add game/controller.o to CLIENT_OBJS
RobertCochran Feb 14, 2026
5d3c690
Fix missed formatting adjustment
RobertCochran Feb 14, 2026
dcf7932
Better return value for dummy get_siapi_textkeys
RobertCochran Feb 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions bin/amd64/game_actions_967460.vdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"In Game Actions"
{
"actions"
{
"InGameControls"
{
"title" "#Set_Ingame"
"StickPadGyro"
{
"move"
{
"title" "#Action_Move"
"input_mode" "joystick_move"
}
"camera"
{
"title" "#Action_Camera"
"input_mode" "absolute_mouse"
}
}
"Button"
{
"primary" "#Action_Primary"
"secondary" "#Action_Secondary"
"reload" "#Action_Reload"
"use" "#Action_Use"
"jump" "#Action_Jump"
"walk" "#Action_Walk"
"crouch" "#Action_Crouch"
"special" "#Action_Special"
"drop" "#Action_Drop"
"affinity" "#Action_Affinity"
"dash" "#Action_Dash"

"next_weapon" "#Action_NextWeapon"
"previous_weapon" "#Action_PreviousWeapon"
"primary_weapon" "#Action_PrimaryWeapon"
"secondary_weapon" "#Action_SecondaryWeapon"
"wheel_select" "#Action_WheelSelect"
"change_loadout" "#Action_ChangeLoadout"

"scoreboard" "#Action_Scoreboard"
"suicide" "#Action_Suicide"
"menu" "#Action_Menu"

"recenter_camera" "#Action_RecenterCamera"

}
}
"MenuControls"
{
"title" "#Set_Menu"
"StickPadGyro"
{
}
"AnalogTrigger"
{
}
"Button"
{
"menu_up" "#Menu_Up"
"menu_down" "#Menu_Down"
"menu_left" "#Menu_Left"
"menu_right" "#Menu_Right"
"menu_select" "#Menu_Select"
"menu_cancel" "#Menu_Cancel"
"pause_menu" "#Action_ReturnToGame"
}
}
}
"localization"
{
"english"
{
"Set_Ingame" "In-Game Controls"
"Set_Menu" "Menu Controls"
"Action_Move" "Move"
"Action_Camera" "Camera"
"Action_Primary" "Primary Fire"
"Action_Secondary" "Secondary Fire"
"Action_Reload" "Reload"
"Action_Use" "Use/Pick Up"
"Action_Jump" "Jump"
"Action_Walk" "Walk"
"Action_Crouch" "Crouch"
"Action_Special" "Special"
"Action_Drop" "Drop Weapon"
"Action_Affinity" "Drop Affinity"
"Action_Dash" "Dash"
"Action_NextWeapon" "Next Weapon"
"Action_PreviousWeapon" "Previous Weapon"
"Action_PrimaryWeapon" "Primary Weapon"
"Action_SecondaryWeapon" "Secondary Weapon"
"Action_WheelSelect" "Wheel Select"
"Action_ChangeLoadout" "Change Loadout"
"Action_Scoreboard" "Scoreboard"
"Action_Suicide" "Suicide"
"Action_Menu" "Menu"

"Action_RecenterCamera" "Recenter Camera"

"Menu_Up" "Up"
"Menu_Down" "Down"
"Menu_Left" "Left"
"Menu_Right" "Right"
"Menu_Select" "Select"
"Menu_Cancel" "Cancel"
}
}
}
22 changes: 22 additions & 0 deletions config/keymap.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,28 @@ keymap -13 MOUSE13
keymap -14 MOUSE14
keymap -15 MOUSE15
keymap -16 MOUSE16
// 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
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.


keymap 8 BACKSPACE
keymap 9 TAB
keymap 13 RETURN
Expand Down
26 changes: 26 additions & 0 deletions config/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,32 @@ bind K [ suicide ]

bind TAB [ showscores ]

// 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 ]

Comment on lines +134 to +176
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

saytextcolour = 0; setpersist saytextcolour 1; setcomplete saytextcolour 1
getsaycolour = [
sc = $saytextcolour
Expand Down
24 changes: 24 additions & 0 deletions config/ui/game/settings.cfg
Copy link
Author

@RobertCochran RobertCochran Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,30 @@ ui_gameui_settings_controls_dims = 0.72
]
]

uivlist 0.025 [
ui_gameui_group [
uistyle clampx

uigrid 2 0.05 0 [
uistyle clampx

uicolourtext "Type" 0xaaaaaa
ui_gameui_switch textkeyimagepreference [
p_options = [
"Automatic"
"Always Keyboard/Mouse"
"Always Controller"
"Both"
]
p_tip = "Which device icons to show in text"
p_id = #(gameui_get_id switch)
]
]
] [
p_label = "Input Icons"
]
]

ui_gameui_vscrollarea [
uiborderedimageclamped $skintex 0x44010101 0 $ui_texborder $ui_screenborder 0.56 0.5 [
uivlist 0 [
Expand Down
6 changes: 6 additions & 0 deletions src/engine/cdpi.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Content Delivery Platform Integrations

#include "engine.h"
#include "controller.h"
#include <stddef.h>
#if defined(USE_STEAM)
#define HAS_STEAM 1
Expand Down Expand Up @@ -40,6 +41,7 @@ namespace cdpi
ISteamUserStats *stats = NULL;
ISteamClient *client = NULL, *sclient = NULL;
ISteamGameServer *serv = NULL;
ISteamInput *input = NULL;
HSteamPipe umpipe = 0, smpipe = 0;
HSteamUser uupipe = 0, supipe = 0;
HAuthTicket authticket = k_HAuthTicketInvalid;
Expand Down Expand Up @@ -184,6 +186,10 @@ namespace cdpi
if(!friends) { conoutf(colourred, "Failed to get Steam friends interface."); cleanup(SWCLIENT); return true; }
stats = (ISteamUserStats *)SteamAPI_ISteamClient_GetISteamUserStats(client, uupipe, umpipe, STEAMUSERSTATS_INTERFACE_VERSION);
if(!stats) { conoutf(colourred, "Failed to get Steam stats interface."); cleanup(SWCLIENT); return true; }
input = (ISteamInput *)SteamAPI_ISteamClient_GetISteamInput(client, uupipe, umpipe, STEAMINPUT_INTERFACE_VERSION);
if (!input) { conoutf(colourred, "Failed to get Steam Input interface."); cleanup(SWCLIENT); return true; }
input->Init(false);
controller::init_action_handles();

const char *name = SteamAPI_ISteamFriends_GetPersonaName(friends);
if(name && *name)
Expand Down
6 changes: 6 additions & 0 deletions src/engine/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#include "version.h"
#include "cube.h"
#if defined(USE_STEAM)
#include "steam_api_flat.h"
#endif

#define LOG_FILE "log.txt"

Expand Down Expand Up @@ -49,6 +52,9 @@ namespace cdpi
namespace steam
{
extern char *steamusername, *steamuserid, *steamserverid;
#if defined(USE_STEAM)
extern ISteamInput *input;
#endif

extern bool clientready();
extern bool clientauthticket(char *token, uint *tokenlen, ENetAddress *addr = NULL);
Expand Down
7 changes: 7 additions & 0 deletions src/engine/main.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// main.cpp: initialisation & main loop

#include "engine.h"
#include "controller.h"
#include <signal.h>

#ifdef SDL_VIDEO_DRIVER_X11
Expand Down Expand Up @@ -737,7 +738,10 @@ void checkinput()
case SDL_KEYDOWN:
case SDL_KEYUP:
if(keyrepeatmask || !event.key.repeat)
{
controller::lastinputwassiapi = false;
processkey(event.key.keysym.sym, event.key.state==SDL_PRESSED);
}
break;

case SDL_WINDOWEVENT:
Expand Down Expand Up @@ -807,10 +811,12 @@ void checkinput()
int button = event.button.button;
if(button >= 6) button += 4; // skip mousewheel X (-4,-5) & Y (-8, 9)
else if(button >= 4) button += 2; // skip mousewheel X (-4,-5)
controller::lastinputwassiapi = false;
processkey(-button, event.button.state==SDL_PRESSED);
break;
}
case SDL_MOUSEWHEEL:
controller::lastinputwassiapi = false;
if(event.wheel.y > 0) { processkey(-4, true); processkey(-4, false); }
else if(event.wheel.y < 0) { processkey(-5, true); processkey(-5, false); }
else if(event.wheel.x > 0) { processkey(-8, true); processkey(-8, false); }
Expand All @@ -824,6 +830,7 @@ void checkinput()
warping = false;
if(grabinput && shouldwarp) resetcursor(true, false);
}
controller::update_from_controller();
}

void swapbuffers(bool overlay)
Expand Down
55 changes: 41 additions & 14 deletions src/engine/rendertext.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
#include "rendertext.h"
#include "engine.h"
#include "controller.h"

enum textkeyimagetype {
tkip_automatic, // Figure out which glyphs to show based on last input
tkip_kbm, // Always show keyboard/mouse glyphs
tkip_controller, // Always show controller glyphs
tkip_both, // Always show *both* keyboard/mouse and controller glyphs
};

VARF(IDF_PERSIST, textsupersample, 0, 1, 2, initwarning("Text Supersampling", INIT_LOAD, CHANGE_SHADERS));

Expand All @@ -14,6 +23,7 @@ FVAR(IDF_PERSIST, textspacescale, 0, 0.5f, 10);

FVAR(IDF_PERSIST, textimagescale, 0, 0.8f, FVAR_MAX);
VAR(IDF_PERSIST, textkeyimages, 0, 1, 1);
VAR(IDF_PERSIST, textkeyimagepreference, tkip_automatic, tkip_automatic, tkip_both);
FVAR(IDF_PERSIST, textkeyimagescale, 0, 0.8f, FVAR_MAX);
SVAR(IDF_PERSIST, textkeyprefix, "<invert>textures/keys/");
VAR(IDF_PERSIST, textkeyseps, 0, 1, 1);
Expand All @@ -34,6 +44,21 @@ font *curfont = NULL;
int curfontpass = 0;
bool wantfontpass = false;

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;
};
}

Comment on lines +47 to +61
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pursuant to above variable, there is now support in the textkey system to filter out glyphs

void fontscale(float *scale)
{
if(!fontdef) return;
Expand Down Expand Up @@ -742,22 +767,12 @@ void text_boundsf(const char *str, float &width, float &height, float xpad, floa
#undef TEXTCHAR
}

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);
}
};
Comment on lines -745 to -756
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This moved to rendertext.h because I needed the struct to be available elsewhere.

vector<textkey *> textkeys;

textkey *findtextkey(const char *str)
{
// SIAPI actions have special handling because we can't cache them
if(controller::is_siapi_textkey(str)) return controller::get_siapi_textkey(str);
loopv(textkeys) if(!strcmp(textkeys[i]->name, str)) return textkeys[i];
static string key;
copystring(key, textkeyprefix);
Expand Down Expand Up @@ -819,9 +834,15 @@ float key_widthf(const char *str)
vector<char *> list;
explodelist(keyn, list);
float width = 0, scale = curfont->scale*curtextscale*textkeyimagescale;
int skippedkeys = 0;
loopv(list)
{
if(i && textkeyseps) width += text_widthf(" or ");
if(!shouldkeepkey(list[i]))
{
skippedkeys++;
continue;
}
if(i && i > skippedkeys && textkeyseps) width += text_widthf(" or ");
if(textkeyimages)
{
textkey *t = findtextkey(list[i]);
Expand All @@ -847,9 +868,15 @@ static float draw_key(Texture *&tex, const char *str, float sx, float sy, bvec4
vector<char *> list;
explodelist(keyn, list);
float width = 0;
int skippedkeys = 0;
loopv(list)
{
if(i && textkeyseps)
if(!shouldkeepkey(list[i]))
{
skippedkeys++;
continue;
}
if(i && i > skippedkeys && textkeyseps)
{
if(!curfontpass)
{
Expand Down
Loading