C++ is one of the most powerful programming languages that we can use to create games that are playable with mouse, keyboard and gamepads or joysticks. We also use C++ in commerce, industry, in robotics, and in the control of IoT devices. Despite competition from other programming languages the games market is still very much dominated by C++. Many games seek to go beyond keyboards to control the gameplay by using specialized controllers either in addition to or instead of keypresses on the computer’s keys. Two of the most well-known controllers in these areas are joysticks or gamepads. The simplest examples of interfacing with game controllers on Windows is by using the XInput library. Even though XInput has been around for a long time it can still be used with the latest C++ Editor and compiler for Windows. In this post, we explain how you can use gamepad or joystick controller in C++ using with Xinput library.
Table of Contents
Is there a component for using a gamepad or joystick in C++ on Windows?
If you are looking for a component library, there are Delphi and C++ Builder-compatible components built on the XInput library. The Controller library is a Delphi and C++ Builder component that allows applications to receive input from the Xbox Controller. The main features of this library are:
- Uses Windows XInput API
- Available for Delphi and C++ Builder from versions 6 right up to the current modern version 11.
- Source code included in the registered version.
- Royalty-free distribution.
You can download the trial version of the XInput library, or you can buy a professional edition from here.
How can I control a gamepad or joystick in C++ with Xinput Library?
XInput library is deprecated but is still a supported library by Microsoft. They recommend moving towards the GameInput library or Windows.Game.Input
for Windows applications.
If we look at these kinds of libraries (i.e GamePad.h library), like MS’ own DirectXTK
, we can see that the toolkit allows one to define USING_XINPUT
vs. USING_GAMEINPUT
vs. USING_WINDOWS_GAMING_INPUT
to pick which underlying library is used.
If we compare XInput compared to GameInput:
- XInput library is old and easy to implement Windows applications.
- XInput library is limited to 4 controllers
- XInput library has no uniform interface to other input (like mouse/keyboard)
- XInput library occurs with higher latency
- XInput library is not friendly with other controllers. i.e. no support for Xbox One Rumble Motors.
Briefly, DirectInput is better than XInput. If you still want to use XInput, you can check more about https://learn.microsoft.com/en-us/windows/win32/xinput/getting-started-with-xinput
XINPUT_GAMEPAD
structure (declared in <xinput.h>
) is explained here : https://learn.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_gamepad
As in there, this structure has these members: wButtons
, bLeftTrigger
, bRightTrigger
, sThumbLX
, sThumbLY
, sThumbRX
, sThumbRY
. Here wButtons
member is used as a bitmask of the device digital buttons, it can be used as below,
wButtons Device digital button flags | Gamepad Bitmask |
---|---|
XINPUT_GAMEPAD_DPAD_UP | 0x0001 |
XINPUT_GAMEPAD_DPAD_DOWN | 0x0002 |
XINPUT_GAMEPAD_DPAD_LEFT | 0x0004 |
XINPUT_GAMEPAD_DPAD_RIGHT | 0x0008 |
XINPUT_GAMEPAD_START | 0x0010 |
XINPUT_GAMEPAD_BACK | 0x0020 |
XINPUT_GAMEPAD_LEFT_THUMB | 0x0040 |
XINPUT_GAMEPAD_RIGHT_THUMB | 0x0080 |
XINPUT_GAMEPAD_LEFT_SHOULDER | 0x0100 |
XINPUT_GAMEPAD_RIGHT_SHOULDER | 0x0200 |
XINPUT_GAMEPAD_A | 0x1000 |
XINPUT_GAMEPAD_B | 0x2000 |
XINPUT_GAMEPAD_X | 0x4000 |
XINPUT_GAMEPAD_Y | 0x8000 |
How to use a gamepad or joystick in a C++ Builder FMX application?
Here are the steps to use gamepad or joystick in C++ Builder FMX app,
- Create a new Multi-Device C++ Builder FMX application
- Drag a Timer (
TTimer
) component on to Form
In Object Inspector window, set its interval to 15 and be sure it is enabled - Drag a shape Circle (
TCircle
) component on to Form
In Object Inspector window, change Fill color something that makes you happy - Double click to Timer1 to create
Timer1Timer()
Now we can add XInput library and a custom TController
class as below,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include <XInput.h> #pragma comment(lib, "XInput.lib") class TController { private: int n; XINPUT_STATE state; public: TController(int num) { n = num; } XINPUT_STATE GetState() { ZeroMemory(&state, sizeof(XINPUT_STATE)); XInputGetState(n, &state); return state; } bool IsConnected() { ZeroMemory(&state, sizeof(XINPUT_STATE)); DWORD statenow = XInputGetState(n, &state); if(statenow == ERROR_SUCCESS) return true; return false; } void vibrate(int LV=0, int RV=0) { XINPUT_VIBRATION vibration; ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION)); vibration.wLeftMotorSpeed = LV; vibration.wRightMotorSpeed = RV; XInputSetState(n, &vibration); } }; TController* controller0 = new TController(0); |
In Timer1Timer() procedure now we can check if our controller is connected, and if it is true, then we can check state of wButtons. for example for the A button we can check it with XINPUT_GAMEPAD_A flag
. If this condition is provided, then we can move our shape Circle1 (for example we can increase Top value) and we can vibrate it as in example below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void __fastcall TForm1::Timer1Timer(TObject *Sender) { if(controller0->IsConnected()) { if(controller0->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_A) { Circle1->Position->Y++; controller0->vibrate(65535, 0); } } } |
Here, we can use any device digital button flags as given in the table above. Note that, we should disable Timer1 and delete our controller0
when the Form is destroyed. So, In Object Inspector, double click the OnDestroy()
event of Form and write these lines below,
1 2 3 4 5 6 7 |
void __fastcall TForm1::FormDestroy(TObject *Sender) { Timer1->Enabled=false; delete(controller0); } |
Now, let’s see full FMX example in C++ Builder.
Is there a full C++ Builder FMX example of how to use a gamepad or joystick in C++?
The example above works well with my son’s Logitech F710 Wireless on Windows Console application in C++ Builder. This controller class works with an FMX app and VCL apps too. I think this example is compatible with XBOX series as well. Please let me know if anyone tested on an XBOX. Here is a full FMX example that uses XInput library in C++.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
#include <fmx.h> #include <XInput.h> #pragma hdrstop #include "Gamepad_TController_FMX_Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.fmx" TForm1 *Form1; #pragma comment(lib, "XInput.lib") class TController { private: int n; XINPUT_STATE state; public: TController(int num) { n = num; } XINPUT_STATE GetState() { ZeroMemory(&state, sizeof(XINPUT_STATE)); XInputGetState(n, &state); return state; } bool IsConnected() { ZeroMemory(&state, sizeof(XINPUT_STATE)); DWORD statenow = XInputGetState(n, &state); if(statenow == ERROR_SUCCESS) return true; return false; } void vibrate(int LV=0, int RV=0) { XINPUT_VIBRATION vibration; ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION)); vibration.wLeftMotorSpeed = LV; vibration.wRightMotorSpeed = RV; XInputSetState(n, &vibration); } }; TController* controller0 = new TController(0); //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { Timer1->Interval = 15; Timer1->Enabled = true; } //--------------------------------------------------------------------------- void __fastcall TForm1::Timer1Timer(TObject *Sender) { if(controller0->IsConnected()) { if(controller0->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_A) { Circle1->Position->Y++; controller0->vibrate(65535, 0); } if(controller0->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_B) { Circle1->Position->X++; controller0->vibrate(0, 65535); } if(controller0->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_X) { Circle1->Position->X--; controller0->vibrate(65535, 65535); } if(controller0->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_Y) { Circle1->Position->Y--; controller0->vibrate(); } if(controller0->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_BACK) { controller0->vibrate(); Timer1->Enabled=false; Form1->Close(); } } } //--------------------------------------------------------------------------- void __fastcall TForm1::FormDestroy(TObject *Sender) { Timer1->Enabled=false; delete(controller0); } |
Note that you can declare this TController
class under the TForm1
class in header.
C++ Builder is the easiest and fastest C and C++ IDE for building simple or professional applications on the Windows, MacOS, iOS & Android operating systems. It is also easy for beginners to learn with its wide range of samples, tutorials, help files, and LSP support for code. RAD Studio’s C++ Builder version comes with the award-winning VCL framework for high-performance native Windows apps and the powerful FireMonkey (FMX) framework for cross-platform UIs.
There is a free C++ Builder Community Edition for students, beginners, and startups; it can be downloaded from here. For professional developers, there are Professional, Architect, or Enterprise version.