Steam Deck input (SOLVED)

Interesting idea! Unfortunately it didn’t work - when I remove the trailing 0 I’m back to only raw input.

With the trailing 0, I do get mapped input, but it’s all jumbled. Did you think the trailing zero could have that effect?

What do you mean with jumbled? :face_with_monocle:

I get mapped input, but it’s incomplete and in the wrong position. E.g. the left trigger might display as rpad down, and rpad left might not be detected at all.

My app would show e.g. for rpad down that it was a button of index 1. That’s what I put in my mapping:

map { input: GAMEPAD_RPAD_DOWN type: GAMEPAD_TYPE_BUTTON index: 1 }

But then when I use the same data to match an action_id I get nothing or the wrong result. And this is for a .gamepads file with only the new mapping.

The screenshot with the different axis and index look strange.

You have LPAD_RIGHT, LPAD_LEFT, LSHOULDER and many others with type AXIS using the same index (3). Surely that can’t be right?

You’re right, that’s a bit wonky and long story short I’ve simplified my script to make it less likely I’m confusing things.

I’m quite confident in this screenshot.

What’s happening here is that the two triggers are continuously firing on axis 3 and 6. My mappings for these are:

map { input: GAMEPAD_LTRIGGER type: GAMEPAD_TYPE_AXIS index: 3 mod { mod: GAMEPAD_MODIFIER_SCALE } }
map { input: GAMEPAD_RTRIGGER type: GAMEPAD_TYPE_AXIS index: 6 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_SCALE } }

However they output as GAMEPAD_LSTICK_DOWN and GAMEPAD_RSTICK_DOWN.

I was also holding down RPAD_LEFT (not when I took the picture though, but the output mapping is stored). This is showing button index 3 in my raw output on the screenshot. The mapping for RPAD_LEFT:

map { input: GAMEPAD_RPAD_LEFT type: GAMEPAD_TYPE_BUTTON index: 3 }

What’s actually showing instead is GAMEPAD_RPAD_RIGHT, which is mapped as:

map { input: GAMEPAD_RPAD_RIGHT type: GAMEPAD_TYPE_BUTTON index: 2 }
My input capture script

--pre-hash
local GAMEPAD_LSTICK_LEFT = hash("gamepad_lstick_left")
local GAMEPAD_LSTICK_RIGHT = hash("gamepad_lstick_right")
local GAMEPAD_LSTICK_DOWN = hash("gamepad_lstick_down")
local GAMEPAD_LSTICK_UP = hash("gamepad_lstick_up")
local GAMEPAD_LSTICK_CLICK = hash("gamepad_lstick_click")
local GAMEPAD_LTRIGGER = hash("gamepad_ltrigger")
local GAMEPAD_LSHOULDER = hash("gamepad_lshoulder")
local GAMEPAD_LPAD_LEFT = hash("gamepad_lpad_left")
local GAMEPAD_LPAD_RIGHT = hash("gamepad_lpad_right")
local GAMEPAD_LPAD_DOWN = hash("gamepad_lpad_down")
local GAMEPAD_LPAD_UP = hash("gamepad_lpad_up")
local GAMEPAD_RSTICK_LEFT = hash("gamepad_rstick_left")
local GAMEPAD_RSTICK_RIGHT = hash("gamepad_rstick_right")
local GAMEPAD_RSTICK_DOWN = hash("gamepad_rstick_down")
local GAMEPAD_RSTICK_UP = hash("gamepad_rstick_up")
local GAMEPAD_RSTICK_CLICK = hash("gamepad_rstick_click")
local GAMEPAD_RTRIGGER = hash("gamepad_rtrigger")
local GAMEPAD_RSHOULDER = hash("gamepad_rshoulder")
local GAMEPAD_RPAD_LEFT = hash("gamepad_rpad_left")
local GAMEPAD_RPAD_RIGHT = hash("gamepad_rpad_right")
local GAMEPAD_RPAD_DOWN = hash("gamepad_rpad_down")
local GAMEPAD_RPAD_UP = hash("gamepad_rpad_up")
local GAMEPAD_START = hash("gamepad_start")
local GAMEPAD_BACK = hash("gamepad_back")
local GAMEPAD_GUIDE = hash("gamepad_guide")

local hash_to_text = {
	[GAMEPAD_LSTICK_LEFT] = "GAMEPAD_LSTICK_LEFT",
	[GAMEPAD_LSTICK_RIGHT] = "GAMEPAD_LSTICK_RIGHT",
	[GAMEPAD_LSTICK_DOWN] = "GAMEPAD_LSTICK_DOWN",
	[GAMEPAD_LSTICK_UP] = "GAMEPAD_LSTICK_UP",
	[GAMEPAD_LSTICK_CLICK] = "GAMEPAD_LSTICK_CLICK",
	[GAMEPAD_LTRIGGER] = "GAMEPAD_LTRIGGER",
	[GAMEPAD_LSHOULDER] = "GAMEPAD_LSHOULDER",
	[GAMEPAD_LPAD_LEFT] = "GAMEPAD_LPAD_LEFT",
	[GAMEPAD_LPAD_RIGHT] = "GAMEPAD_LPAD_RIGHT",
	[GAMEPAD_LPAD_DOWN] = "GAMEPAD_LPAD_DOWN",
	[GAMEPAD_LPAD_UP] = "GAMEPAD_LPAD_UP",
	[GAMEPAD_RSTICK_LEFT] = "GAMEPAD_RSTICK_LEFT",
	[GAMEPAD_RSTICK_RIGHT] = "GAMEPAD_RSTICK_RIGHT",
	[GAMEPAD_RSTICK_DOWN] = "GAMEPAD_RSTICK_DOWN",
	[GAMEPAD_RSTICK_UP] = "GAMEPAD_RSTICK_UP",
	[GAMEPAD_RSTICK_CLICK] = "GAMEPAD_RSTICK_CLICK",
	[GAMEPAD_RTRIGGER] = "GAMEPAD_RTRIGGER",
	[GAMEPAD_RSHOULDER] = "GAMEPAD_RSHOULDER",
	[GAMEPAD_RPAD_LEFT] = "GAMEPAD_RPAD_LEFT",
	[GAMEPAD_RPAD_RIGHT] = "GAMEPAD_RPAD_RIGHT",
	[GAMEPAD_RPAD_DOWN] = "GAMEPAD_RPAD_DOWN",
	[GAMEPAD_RPAD_UP] = "GAMEPAD_RPAD_UP",
	[GAMEPAD_START] = "GAMEPAD_START",
	[GAMEPAD_BACK] = "GAMEPAD_BACK",
	[GAMEPAD_GUIDE] = "GAMEPAD_GUIDE",
}

function init(self)
	msg.post(".", "acquire_input_focus")

	self.mappings = {}
end

function update(self, dt)
	
	--display gamepad name
	if self.gamepad_name then
		label.set_text("#gamepad_name", "Gamepad name: " .. self.gamepad_name)
	else
		label.set_text("#gamepad_name", "No gamepad detected.")
	end

	--display raw input
	if self.previous_raw then
		local txt = ""
		for category, data in pairs(self.previous_raw) do
			if category == "gamepad_buttons" then
				for id, value in pairs(data) do
					if value ~= 0 then
						txt = txt .. "Button: " .. id .. "\n"
					end
				end
			end
			if category == "gamepad_hats" then
				for id, value in pairs(data) do
					if value ~= 0 then
						txt = txt .. "Hat: " .. id .. "\n"
					end
				end
			end
			if category == "gamepad_axis" then
				for id, value in pairs(data) do
					if math.abs(value) > 0.5 then
						txt = txt .. "Axis: " .. id .. " Value: " .. math.ceil(value*100)/100 .. "\n"
					end
				end
			end
		end
		label.set_text("#unmapped", txt)
	end

	--display mapped input
	local txt = ""
	for name, _ in pairs(self.mappings) do
		txt = txt .. name .. "\n"
	end
	label.set_text("#mapping", txt)
end

function on_input(self, action_id, action)
	
	--capture gamepad name
	if action_id == hash("gamepad_connected") then
		self.gamepad_name = action.gamepad_name
	end


	if action_id == hash("gamepad_raw") then

		--capture raw data to display
		self.previous_raw = action

	elseif action_id then

		if action.pressed then

			--try to map action to a known action_id and convert it to text
			local name = hash_to_text[action_id]
			
			--store mapping to be displayed
			if name then
				self.mappings[name] = true
			end

		end
	end
end

My .gamepads file, with no other bindings included

driver
{
    device: "Microsoft X-Box 360 pad 0"
    platform: "linux"
    dead_zone: 0.2
    map { input: GAMEPAD_LSTICK_LEFT type: GAMEPAD_TYPE_AXIS index: 1 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LSTICK_RIGHT type: GAMEPAD_TYPE_AXIS index: 1 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LSTICK_DOWN type: GAMEPAD_TYPE_AXIS index: 2 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LSTICK_UP type: GAMEPAD_TYPE_AXIS index: 2 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LSTICK_CLICK type: GAMEPAD_TYPE_BUTTON index: 10 }
    map { input: GAMEPAD_LTRIGGER type: GAMEPAD_TYPE_AXIS index: 3 mod { mod: GAMEPAD_MODIFIER_SCALE } }
    map { input: GAMEPAD_LSHOULDER type: GAMEPAD_TYPE_BUTTON index: 5 }
    map { input: GAMEPAD_LPAD_LEFT type: GAMEPAD_TYPE_AXIS index: 7 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LPAD_RIGHT type: GAMEPAD_TYPE_AXIS index: 7 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LPAD_DOWN type: GAMEPAD_TYPE_AXIS index: 8 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LPAD_UP type: GAMEPAD_TYPE_AXIS index: 8 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_RSTICK_LEFT type: GAMEPAD_TYPE_AXIS index: 4 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_RSTICK_RIGHT type: GAMEPAD_TYPE_AXIS index: 4 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_RSTICK_DOWN type: GAMEPAD_TYPE_AXIS index: 5 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_RSTICK_UP type: GAMEPAD_TYPE_AXIS index: 5 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_RSTICK_CLICK type: GAMEPAD_TYPE_BUTTON index: 11 }
    map { input: GAMEPAD_RTRIGGER type: GAMEPAD_TYPE_AXIS index: 6 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_SCALE } }
    map { input: GAMEPAD_RSHOULDER type: GAMEPAD_TYPE_BUTTON index: 6 }
    map { input: GAMEPAD_RPAD_LEFT type: GAMEPAD_TYPE_BUTTON index: 3 }
    map { input: GAMEPAD_RPAD_RIGHT type: GAMEPAD_TYPE_BUTTON index: 2 }
    map { input: GAMEPAD_RPAD_DOWN type: GAMEPAD_TYPE_BUTTON index: 1 }
    map { input: GAMEPAD_RPAD_UP type: GAMEPAD_TYPE_BUTTON index: 4 }
    map { input: GAMEPAD_START type: GAMEPAD_TYPE_BUTTON index: 8 }
    map { input: GAMEPAD_BACK type: GAMEPAD_TYPE_BUTTON index: 7 }
}

Ok, what kind of value are you seeing?

I don’t understand this. If the .gamepads file you created is anything to go by GAMEPAD_LSTICK_DOWN and GAMEPAD_RSTICK_DOWN are mapped to AXIS and index 2 and 5. How can they output to something else then?

Keep in mind that the raw input is giving you values for buttons, axis and hats in an order that is fixed for the controller. It is not guaranteed that all controllers have their buttons and sticks mapped to the same indices. This is why the .gamepad file mappings exist. To translate the raw values to a predefined set of buttons and axis such that the mapping of gamepad input in a game is controller agnostic.

What you see in the screenshot. If I don’t touch the triggers they constantly output -1 on axis 3 and 1 on axis 6. Do you mean something else? If I press the triggers, the values invert (1 and -1, respectively).

When I receive raw input, I store the entire action table in self.previous_raw. I then iterate over that entire table and display each value on the left of the screenshot.

Yes, I don’t understand this either!

Yes - I created this mapping myself by pushing each of the buttons on the Deck and noting down the button/axis/hat.

Either something very weird is going on, or I have made a fundamental mistake in my script.

What if you try your script with a gamepad that is known to work and where there are existing bindings you can compare with? Are you able to reproduce the gamepad binding from the raw input?

Good idea. I get exactly the same behaviour from my own Xbox controller that normally works. I guess user error? But I really have no idea what I’m doing wrong.

Ok, let me take a look later this evening.

Thank you. I imagine you’ll end up rolling your own, but here is my project for reference. I’m very curious to understand what I’m doing wrong.

SteamDeckInput.zip (6.2 KB)

Oh, I think I’ve found your problem. The .gamepads file is 0-indexed, although the raw inputs you get in Lua are 1-indexed (for easy iteration with ipairs). You need to subtract 1 from the values when you create your definitions in the .gamepads file.

3 Likes

Lua, love it or hate it, you’re gonna have to use it.

2 Likes

Yep. That’s it. We’ve done it!!! Thanks for the help.

Here’s a working Steam Deck mapping:

driver
{
    device: "Microsoft X-Box 360 pad 0"
    platform: "linux"
    dead_zone: 0.2
    map { input: GAMEPAD_LSTICK_LEFT type: GAMEPAD_TYPE_AXIS index: 0 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LSTICK_RIGHT type: GAMEPAD_TYPE_AXIS index: 0 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LSTICK_DOWN type: GAMEPAD_TYPE_AXIS index: 1 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LSTICK_UP type: GAMEPAD_TYPE_AXIS index: 1 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LSTICK_CLICK type: GAMEPAD_TYPE_BUTTON index: 9 }
    map { input: GAMEPAD_LTRIGGER type: GAMEPAD_TYPE_AXIS index: 2 mod { mod: GAMEPAD_MODIFIER_SCALE } }
    map { input: GAMEPAD_LSHOULDER type: GAMEPAD_TYPE_BUTTON index: 4 }
    map { input: GAMEPAD_LPAD_LEFT type: GAMEPAD_TYPE_AXIS index: 6 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LPAD_RIGHT type: GAMEPAD_TYPE_AXIS index: 6 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LPAD_DOWN type: GAMEPAD_TYPE_AXIS index: 7 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_LPAD_UP type: GAMEPAD_TYPE_AXIS index: 7 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_RSTICK_LEFT type: GAMEPAD_TYPE_AXIS index: 3 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_RSTICK_RIGHT type: GAMEPAD_TYPE_AXIS index: 3 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_RSTICK_DOWN type: GAMEPAD_TYPE_AXIS index: 4 mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_RSTICK_UP type: GAMEPAD_TYPE_AXIS index: 4 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_CLAMP } }
    map { input: GAMEPAD_RSTICK_CLICK type: GAMEPAD_TYPE_BUTTON index: 10 }
    map { input: GAMEPAD_RTRIGGER type: GAMEPAD_TYPE_AXIS index: 5 mod { mod: GAMEPAD_MODIFIER_NEGATE } mod { mod: GAMEPAD_MODIFIER_SCALE } }
    map { input: GAMEPAD_RSHOULDER type: GAMEPAD_TYPE_BUTTON index: 5 }
    map { input: GAMEPAD_RPAD_LEFT type: GAMEPAD_TYPE_BUTTON index: 2 }
    map { input: GAMEPAD_RPAD_RIGHT type: GAMEPAD_TYPE_BUTTON index: 1 }
    map { input: GAMEPAD_RPAD_DOWN type: GAMEPAD_TYPE_BUTTON index: 0 }
    map { input: GAMEPAD_RPAD_UP type: GAMEPAD_TYPE_BUTTON index: 3 }
    map { input: GAMEPAD_START type: GAMEPAD_TYPE_BUTTON index: 7 }
    map { input: GAMEPAD_BACK type: GAMEPAD_TYPE_BUTTON index: 6 }
}

Can it be included in the default project bindings somehow? If yes, perhaps a good idea to wait a bit just in case there’s something wrong with it.

As far as I’m concerned, Lua is right and everyone else is wrong.

6 Likes

Yes, we should definitely include it! I’m prepare a pull request.

3 Likes

I am having some issues with Steam Deck when external input is being used, for example when the Deck is in a dock or an external controller is connected. The core of the issue is that input is duplicated.

One user found a similar issue on a Linux desktop. They found that disabling SteamInput solved the issue in that context. The same user then tried to reproduce the issue on the Steam Deck, and provided me with this:

I tried on the Steam Deck to see how it behaves.

I don’t have a “dock”, but I have a basic usb-c hub with an hdmi output which should be similar enough. And indeed, I have the same behaviour when I use a controller instead of the embedded controls : the menu skips a step, and the screens turns black when I try to enter any menu. However, I didn’t see any option to disable SteamInput on the Deck… so I don’t think it’s possible to use the same workaround as on desktop.

It doesn’t happen with the Deck embedded controls, even when using an external monitor. In that case, it works fine.

For SteamInput, I think Steam sets environment variables so SDL will ignore controllers except the virtual one created for SteamInput. I don’t know which engine your game is built on, does it rely on SDL for controller input ? If so, maybe it ignores the environment variables Steam sets, and we get one input for the “real” controller, and one for the virtual one.

The key here seems to be the last paragraph, and something about SDL. I am not familiar with it - seems to be a low level library for handling multiple things, including input. https://wiki.libsdl.org/

Ping @britzl - does any of this make sense to you? Thanks for any help!

Let me take a look, perhaps tomorrow! I also have the other steam input issue we’ve discussed in the past.

1 Like

I was going to post this issue on the steamworks repo, but of course it’s not actually related to the steamworks extension, so I’m bumping here instead. I’m still getting bug reports from my players about this. My game is not playable when docked on the Steam Deck.

Yeah, I’m not sure this is related to the Steamworks SDK to be honest. It’s more likely related to our controller implementation. Please create a ticket in the main Defold repo!

1 Like

Duh, of course that’s what I should have done!

Issue here:

2 Likes