How to know if controller or keyboard?

I wanted to know if the player was using a controller or keyboard so that I can change the UI accordingly.

So how can I know the input device that was used last and how can I know what type of device it is?
Since the game is single player just knowing what device made the last input would be enough.

I’ve seen people recommend InputDeviceChange. But that only receives what devices are being plugged in and out, which is useless in this case since I don’t care how many devices are connected, I want to know which one is being used.

Thanks in advance :smile:

I think you can use InputUser.onChange if you have both scheme for Keyboard and Gamepad available

The InputAction has a property called activeControl which gives you the active control used by activating the action. This InputControl has the device property which tells you what InputDevice was used to activate the action.

I didn’t get any callback for that. I don’t really know how it works, but it didn’t notify me when going back and forth from the controller and keyboard

Thanks! That was really useful!

The best I came up after getting the InputControl as you told me is this messy thing:

isKeyboardAndMouse = lastDevice.description.deviceClass.Equals("Keyboard") || lastDevice.description.deviceClass.Equals("Mouse");

Otherwise it means you are using a gamepad. Which totally works, but there must be a better way… Ideally you could also detect if it’s a generic xbox/pc controller or a sony controller to change the buttons images too. But so far the best I got is “gamepad”. I have a generic Logitech controller, maybe a branded sony or xbox controller return something more useful.

Is there any docs on that? I can’t find any. There must be a way to tell if it’s a nintendo switch, xbox controller or sony one at least

Switch controllers aren’t supported outside of switch development AFAIK. To support that you need to be approved by Nintendo at the moment.

The others are fairly recognizable, consider this InputActionAsset:
7855618--997063--screenshot.png

And this code:

using UnityEngine;
using UnityEngine.InputSystem;

public class Test : MonoBehaviour
{

    [SerializeField]
    private InputActionAsset inputActionAsset;

    private void Awake()
    {
        inputActionAsset.Enable();
        inputActionAsset.FindAction("TestAction").performed += DoTestAction;
    }

    private void OnDestroy() => inputActionAsset.FindAction("TestAction").performed -= DoTestAction;

    // ctx.action.activeControl.device.
    //        name: Keyboard, Mouse, XInputControllerWindows, DualShock4GamepadHID
    //        displayName: Keyboard, Mouse, XBox Controller, Wireless Controller
    //        description: Mouse (RawInput), Keyboard (RawInput),
    //               {"userIndex":0,"type":1,"subType":1,"fla... (XInput),
    //               Sony Interactive Entertainment Wireless Controller (HID)
 
    private static void DoTestAction(InputAction.CallbackContext ctx) =>
            Debug.Log(ctx.action.activeControl.device.name);
}

This part is the results:
ctx.action.activeControl.device.name: Keyboard, Mouse, XInputControllerWindows, DualShock4GamepadHID

ctx.action.activeControl.device.displayName: Keyboard, Mouse, XBox Controller, Wireless Controller

ctx.action.activeControl.device.description: Mouse (RawInput), Keyboard (RawInput), {"userIndex":0,"type":1,"subType":1,"fla... (XInput), Sony Interactive Entertainment Wireless Controller (HID)

Now, I only have my generic Mouse, Keyboard, a wireless XBox controller and a wireless PS4 controller, so I could only test on those. It seems to me that if you want to compare and recognize devices, the name property is your best bet, if you want to display it to users, then the displayName.

Thank you so much, that was very helpful. I think we’ll need to play the string comparison game for now. It’s reliable and works though, so not complaining at all

In case someone is looking for this later on this is my code for now. May not be the best way of doing things, but hey, it works:

private void Start()
        {
            InputSystem.onActionChange += InputActionChangeCallback;
        }

private void InputActionChangeCallback(object obj, InputActionChange change)
        {
            if (change == InputActionChange.ActionPerformed)
            {
                InputAction receivedInputAction = (InputAction) obj;
                InputDevice lastDevice = receivedInputAction.activeControl.device;

                isKeyboardAndMouse = lastDevice.name.Equals("Keyboard") || lastDevice.name.Equals("Mouse");
                //If needed we could check for "XInputControllerWindows" or "DualShock4GamepadHID"
                //Maybe if it Contains "controller" could be xbox layout and "gamepad" sony? More investigation needed
            }
        }

For anyone who finds this, this code gives a bunch of errors lol, it needs two error checks(i believe)

    void Test(object obj, InputActionChange change)
    {
        if (!typeof(InputAction).IsAssignableFrom(obj.GetType())) return;
        InputAction receivedInputAction = (InputAction)obj;
        if (receivedInputAction.activeControl == null) return;
        InputDevice lastDevice = receivedInputAction.activeControl.device;

        print(lastDevice.name);
    }

Not sure why the “object” being passed in doesn’t seem to always be an InputAction(sometimes it’s an InputActionAsset, sometimes it’s an InputActionMap), and also the “activeControl” is sometimes null.

Using more modern C# additions, I instead did:

input.actions["test"].performed += (context) => {
    if ( context.control.device is Keyboard or Mouse ) this.isKeyboardOrMouse = true;
    else this.isKeyboardOrMouse = false;
};

and it works how I expected it to. (My use case was detecting if a sprint input came from Keyboard vs Controller to change how it worked depending on the input.) Of course, this is a modified version to fit their use case a bit better.

and modifying Mashimaro7’s code to fit these additions would be:

void Test(object obj, InputActionChange change)
{
    if ( obj != null && obj is InputAction action) { // Modern C# is usable because we're checking the type, not for null
        if ( action.activeControl == null ) return; // Can't use modern C# here because Destroy exists and does weird things with the memory behind the scenes
        InputDevice lastDevice = action.activeControl.device;

        print(lastDevice.name);
    }
}