Introduction - AderitoSilva/XInputium GitHub Wiki

What is XInputium

XInputium is a .NET library that wraps XInput API and provides Xbox controller support for your .NET based game or application — XInput is the API that allows applications to communicate with an Xbox controller device.

How XInputium compares to other existing .NET XInput libraries

Although XInputium aims to bring XInput interoperation to .NET, it offers features that go beyond just that. XInputium features a full-fledged input system, that does much of the hard work for you. The core goal of XInputium is to make it as easy as possible for you to consume the library, while you're still getting a full set of features at your disposal.

XInputium also provides more than three different ways of consuming the API. If you want a plug-and-play library, that simplifies most of your input related code, by abstracting most of the input logic, you will find classes and types that do just that. On the other hand, if you don't need all this extra functionality and just want to perform basic communication with an XInput device and benefit from this more lightweight approach, XInputium also offers more low-level access to the device.

Key features

XInputium implements INotifyPropertyChanged interface

By implementing System.ComponentModel.INotifyPropertyChanged interface in many types, XInputium enables you to easily bind your UI code — like XAML — directly to a property. Classes like Joystick, Trigger, DigitalButton, XGamepad and XInputDevice all implement this interface. XInputium together with NoesisGUI is a great mix.

XInputium Preview — the main demo application that showcases XInputium — is made mostly in XAML and makes heavy use of bindings that bind directly to XInputium input objects.

Event dispatching

PropertyChanged event and most other events are triggered only when you update the device state, even if you make changes to properties before you call an update method. This is because XInputium uses an event dispatcher, that stacks events and dispatches them at specific moments, mostly when you update the device state. This behavior guarantees that all properties have their new values when you access them. For instance, if you change PropertyA and then change PropertyB, when you handle the PropertyA's PropertyChanged event, PropertyB will already have its new value. The same happens with most other events.

Dynamic events

Regular .NET events are very useful, but they are static (i.e. you cannot specify what conditions will make them trigger). For instance, if you need to get notified when a button is pressed, regular .NET events are useful. However, if you need to get notified when a button is pressed and held by 200 milliseconds (or other specific amount of time), regular .NET events will not be enough. That's what dynamic events solve — they are events you register with specific conditions and get notified only when those conditions are met.

Some examples of interesting dynamic events are the ButtonHold event — which allows you to get notified after a specific button is held by a specific amount of time — and the ButtonRepeat event — that repeatedly notifies you at specific intervals when a specific button is held by a certain amount of time, and is useful for things like menus and lists in game/application UIs.

Mapping Joysticks or Triggers to buttons

You can make a Joystick or a Trigger behave as a digital button. Then, you have access to all the cool features a digital button has, like the dynamic events specific to buttons described above. In this context, mapping a Joystick or a Trigger to a digital button means you can create buttons from joysticks or triggers, and specify the criteria that determines when each button is active (pressed) or inactive (released). For instance, you can make a Joystick behave as a four-button directional pad, that is composed of left, top, right and down buttons; or you can make a Trigger behave as a digital button. There are methods that provide you exactly this functionality, but there are also methods that allow you to specify your own function to determine the button state based on the Joystick or Trigger state using your own criteria.

Joystick movement smoothing

Joystick smoothing is a feature that can smooth fast peaking movements, for precise control. Implementing functionality like this, from scratch and in a performant way, in your code, could be tedious. Joystick objects have opt-in built-in configurable smoothing functionality available, removing this workload from you.

Modifier functions

Modifier functions are mathematical functions you can apply to normal numbers, so you can make cool things with them. They are used primarily for changing the linear behavior of an input axis, but are not limited to that. For instance, you can set a ModifierFunction to the radius axis of a Joystick to make the joystick more sensitive closer to the center and less sensitive closer to the outer edge.

There are many built-in modifier functions available, and it's easy to create your own. You can combine several modifier functions as a single function for a single axis, and you can set modifier functions for any axis. For example, you can set a quantization function to the angle axis of a joystick, that divides the angle in 8 steps, and a boolean function to the radius axis, making the joystick behave as an 8-directional pad. Another example, is when you want to provide a non-circular dead-zone, where you set different dead-zone values to the joystick's X and Y axes; Joysticks already have circular dead-zone properties, but modifier functions enable you to create specific dead-zone configurations based on your criteria.

Joystick and Trigger objects allow you to set a ModifierFunction to each of its axes, but you can also use modifier functions anywhere else. They can be very useful when used with the slim versions of the Joystick and Trigger classes, where you can provide just the functionality you need, so you benefit from the very lightweight nature of these objects.

Feature-rich vs. lightweight

More than one way of consuming the API is available, allowing you to choose between feature-rich objects that abstract most of the work, and more lightweight objects that provide raw, basic access to the device.

Objects like Joystick, Trigger and XGamepad offer many features — like property change notifications and event dispatching — and make use of an input loop, but there may be scenarios where you don't require this functionality. There are more lightweight objects available, that offer just the basic functionality, which you can use to obtain maximum performance. An example of some lightweight alternatives are the SlimJoystick, SlimTrigger and XInputDeviceState objects, which just represent the raw state of the input device.

You can also combine some objects together to use just some features of the API. For instance, you can encapsulate a SlimJoystick object within a Joystick instance, and use Joystick's features in a specific situation or just temporarily when needed.

You can choose to use event based patterns and object-oriented programming for input handling, or you can opt to use just a more functional based programming approach, using static members.

Built-in time measurement and input loop

Many input operations depend on the measurement of time. As an example, a dynamic event that fires after a button is held needs to measure the elapsed time since the button was pressed; Joystick smoothing also depends on time measurement to determine movement speed. XInputium has built-in time measurement functionality that measures time based on the hardware system timer, but also allows you to provide your own time measurement functionality, so you can have dynamic control over time.

Input operations also rely heavily on the concept of a loop. XInputium has its own input loop, where every input operation occurs on each iteration. That's how XInputium can be so straightforward to use, because the only thing you need to do is to update (or notify) the API that the loop can proceed with its next iteration. You would usually do this on each game/application frame, and it only requires calling a single method — i.e. XGamepad.Update(), XInputDevice.Update(), or XInputDeviceManager.Update(), depending on how you prefer to consume the API. Once you update the input loop, the input state is refreshed with fresh data from the underlying device and all related events are triggered accordingly.

Logical devices vs. real devices

To make it easier to handle situations where users connect and disconnect devices, and due to the fact that a hardware device is volatile — i.e. it is not guaranteed to be present at all moments during the game/application lifetime — XInputium uses the concept of logical and real devices. In this context, a logical device is the representation of an unspecific XInput gamepad in your application, and the real device is the object that represents a specific real XInput gamepad.

On XInputium, an XInputDevice object represents a real device, while an XGamepad object represents a logical device and encapsulates an XInputDevice object. This means you can set up an XGamepad object (for example, subscribe to its events, configure joystick dead-zones, etc.), and switch between different XInputDevice objects within it (using XGamepad.Device property), so you don't need to reconfigure your XGamepad object whenever you wish to use a different real device that might be connected at a different XInput user index (user slot) in the system.

One of the advantages of using a logical device (using an XGamepad object), is that you can enable/disable input and/or output (vibration). This means you can make a logical device to appear as if it was not receiving any input from the user, while still being able to access the real device. This approach could be useful in situations where you need to temporarily disable input from the user without having to implement this logic in your code, (for example, while a non-skippable video is playing in a game, or when a game/application window loses focus).

Common math operations

XInputium provides functions and members that can assist you with common input related math operations. These can be useful when working with the slim versions of Joystick and Trigger objects, for instance.

Made to be extensible

Although XInputium, currently, only provides access to XInput devices by default, it is made to be easy to extend. It provides a platform and hardware independent base layer of functionality, and provides XInput as an additional layer, so you can easily extend the base layer. In fact, all XInput functionality within XInputium is just an extension of the base layer.

Objects like InputDevice and LogicalInputDevice provide you the base layer for implementing your own real and logical input devices.

Learning the fundamentals

To learn the fundamental API of XInputium and start creating applications that use XInput, you're encouraged to read The fundamentals page. It will introduce you to some of the features above, applied in practice.