Gamepad and Keyboard Input
?
?

Keyboard Navigation

Global Keys

[, < / ], > Jump to previous / next episode
W, K, P / S, J, N Jump to previous / next timestamp
t / T Toggle theatre / SUPERtheatre mode
V Revert filter to original state Y Select link (requires manual Ctrl-c)

Menu toggling

q Quotes r References f Filter y Link c Credits

In-Menu and Index Controls

a
w
s
d
h j k l


Esc Close menu / unfocus timestamp

Quotes and References Menus and Index

Enter Jump to timestamp

Quotes, References and Credits Menus

o Open URL (in new tab)

Filter Menu

x, Space Toggle category and focus next
X, ShiftSpace Toggle category and focus previous
v Invert topics / media as per focus

Filter and Link Menus

z Toggle filter / linking mode

Credits Menu

Enter Open URL (in new tab)
0:51Getting input from a gamepad: XInput
0:51Getting input from a gamepad: XInput
0:51Getting input from a gamepad: XInput
3:20Using XInput in the codebase
3:20Using XInput in the codebase
3:20Using XInput in the codebase
6:26A look at the XInput API more in depth.
6:26A look at the XInput API more in depth.
6:26A look at the XInput API more in depth.
7:40Programming using XInput API
7:40Programming using XInput API
7:40Programming using XInput API
9:09Discussing the controller's state: using XINPUT_STATE struct
9:09Discussing the controller's state: using XINPUT_STATE struct
9:09Discussing the controller's state: using XINPUT_STATE struct
11:30Using XINPUT_GAMEPAD struct
11:30Using XINPUT_GAMEPAD struct
11:30Using XINPUT_GAMEPAD struct
16:57Handling unresolved external symbol XInputGetState
16:57Handling unresolved external symbol XInputGetState
16:57Handling unresolved external symbol XInputGetState
26:03Discussing access violations
26:03Discussing access violations
26:03Discussing access violations
26:52Safeguarding against access violations
26:52Safeguarding against access violations
26:52Safeguarding against access violations
31:50Defining and implementing Win32LoadXinput function
31:50Defining and implementing Win32LoadXinput function
31:50Defining and implementing Win32LoadXinput function
37:22Playing with the X and Y offset
37:22Playing with the X and Y offset
37:22Playing with the X and Y offset
38:00Casey looking for his gamepad
38:00Casey looking for his gamepad
38:00Casey looking for his gamepad
38:50Casey showcases pink controller
38:50Casey showcases pink controller
38:50Casey showcases pink controller
41:09XINPUT_VIBRATION
41:09XINPUT_VIBRATION
41:09XINPUT_VIBRATION
43:05#milkhit
43:05#milkhit
43:05#milkhit
46:55Testingand handling button/key presses
46:55Testingand handling button/key presses
46:55Testingand handling button/key presses
55:10(Extra) ANSI String Handling
55:10(Extra) ANSI String Handling
55:10(Extra) ANSI String Handling
56:10(Extra) Not passing something by value anymore
56:10(Extra) Not passing something by value anymore
56:10(Extra) Not passing something by value anymore
58:10Summary and things to come before beginning Q&A
58:10Summary and things to come before beginning Q&A
58:10Summary and things to come before beginning Q&A
59:08LParam 30 is always up for WM_KEYUP
59:08LParam 30 is always up for WM_KEYUP
59:08LParam 30 is always up for WM_KEYUP
59:57Why uppercase variable names?
59:57Why uppercase variable names?
59:57Why uppercase variable names?
1:00:21Can you explain your macro and referencing XInput functions again?
1:00:21Can you explain your macro and referencing XInput functions again?
1:00:21Can you explain your macro and referencing XInput functions again?
1:03:15What's the size when copying becomes problematic for a struct?
1:03:15What's the size when copying becomes problematic for a struct?
1:03:15What's the size when copying becomes problematic for a struct?
1:04:27About (premature) optimisation
1:04:27About (premature) optimisation
1:04:27About (premature) optimisation
1:08:27How does the linker find the XInput DLL on the users machine? [...] Can we distribute the DLL?
1:08:27How does the linker find the XInput DLL on the users machine? [...] Can we distribute the DLL?
1:08:27How does the linker find the XInput DLL on the users machine? [...] Can we distribute the DLL?
1:12:52Is [the dynamic linker] allowed to link XInput in compile time?
1:12:52Is [the dynamic linker] allowed to link XInput in compile time?
1:12:52Is [the dynamic linker] allowed to link XInput in compile time?
1:14:02Why you chose an if-else cascade for key input instead of switch statement? It's up for grabs.
1:14:02Why you chose an if-else cascade for key input instead of switch statement? It's up for grabs.
1:14:02Why you chose an if-else cascade for key input instead of switch statement? It's up for grabs.
1:15:13How do you know the XInput stub things work without testing it on XP computer without XInput installed?
1:15:13How do you know the XInput stub things work without testing it on XP computer without XInput installed?
1:15:13How do you know the XInput stub things work without testing it on XP computer without XInput installed?
1:17:03Is it a waste of time to check if GetProcAddress() returns zero?
1:17:03Is it a waste of time to check if GetProcAddress() returns zero?
1:17:03Is it a waste of time to check if GetProcAddress() returns zero?
1:19:12Modify code to use the controller stick
1:19:12Modify code to use the controller stick
1:19:12Modify code to use the controller stick
1:22:14Where do the WParam and LParam come from?
1:22:14Where do the WParam and LParam come from?
1:22:14Where do the WParam and LParam come from?
1:24:17Why did MS decide to use link and import library instead of true dynamic linking?
1:24:17Why did MS decide to use link and import library instead of true dynamic linking?
1:24:17Why did MS decide to use link and import library instead of true dynamic linking?
1:25:13When are we going to get naysayer88 and cmuratori combination game?
1:25:13When are we going to get naysayer88 and cmuratori combination game?
1:25:13When are we going to get naysayer88 and cmuratori combination game?
1:26:19On Unix you don't need to link to a .so file at all to use a .so file where as on MS platforms you need a .lib file
1:26:19On Unix you don't need to link to a .so file at all to use a .so file where as on MS platforms you need a .lib file
1:26:19On Unix you don't need to link to a .so file at all to use a .so file where as on MS platforms you need a .lib file
1:27:33In the far future will you use DirectX or OpenGL or what?
1:27:33In the far future will you use DirectX or OpenGL or what?
1:27:33In the far future will you use DirectX or OpenGL or what?
1:28:05We will be drawing the whole screen every frame.
1:28:05We will be drawing the whole screen every frame.
1:28:05We will be drawing the whole screen every frame.
1:28:47Why does Valgrind make it look so scary when you don't free()?
1:28:47Why does Valgrind make it look so scary when you don't free()?
1:28:47Why does Valgrind make it look so scary when you don't free()?
1:29:48It looks like WParam and LParam are 32bit on XP
1:29:48It looks like WParam and LParam are 32bit on XP
1:29:48It looks like WParam and LParam are 32bit on XP
1:30:12Is it possible to keep your framerate consistent while dragging the window around? Doing repaint on WM_MOVE is slow.
1:30:12Is it possible to keep your framerate consistent while dragging the window around? Doing repaint on WM_MOVE is slow.
1:30:12Is it possible to keep your framerate consistent while dragging the window around? Doing repaint on WM_MOVE is slow.

Gamepad and Keyboard Input

So what's with all that "#define" and "typedef" mumbo-jumbo there?

You may have found yourself confused when looking at this code:

#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
typedef X_INPUT_GET_STATE(x_input_get_state);
X_INPUT_GET_STATE(XInputGetStateStub)
{
        return(0);
}
global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub;
#define XInputGetState XInputGetState_

What on God's green earth is happening here? Well, don't despair. It's really quite simple, we just need to unwrap a few things and break it down line by line.

#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)

In C (as well as C++ and Objective C), the first step of the compilation process is a code transformation pass by the preprocessor. As we have covered previously in the series, any command which begins with a "#" is what is known as a preprocessor directive. This is just a command which will tell the preprocessor to do something, such as #include a file. In this case, "#define" is a directive which creates a macro. A macro can be thought of as a code substitution, anytime the processor finds the macro used in your code, it will swap it out for whatever you defined.

The two types of macros

There are two types of macros, the first type is very simple and defines a constant value:

#define PI 3.14159265359

This macro will cause the preprocessor to convert any instances of "PI" in the code directly into "3.14159265359". If we break it down a bit, on the left hand side we have what is called the identifier ("PI"), and on the right hand side we have what is known as the token string ("3.14159265359"). The preprocessor searches the code for the identifier, and replaces it with the token string. This means that the literal text "PI" will no longer appear in our code, having been replaced with "3.14159265359". This can be useful for constant values that we may want to use repeatedly, but not type in full all the time. Note that the preprocessor will not convert PI if it appears in a comment or as part of a longer identifier such as "PieceOfPI".

So let's go back to the macro we were looking at earlier.

#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)

It is somewhat more complex and appears to have a function definition on the right for a function called "name". This is the second type of macro, which uses a token string as part of it's definition. As a simpler, but somewhat different example:

#define ADD(argument1, argument2) argument1 + argument2

Looking at this, we can see that it can be broken down into two parts just the same as the previous macro. First, on the left side we have the identifier "ADD(argument1, argument2)". This works similarly to the first kind of macro, but because we have written it like a function, the preprocessor will save whatever it finds in place of "argument1" and "argument2", and then plug those in for where they were found in the token string.

For example, if later on in the code we were to type:

int c = ADD(a,b);

Our macro would expand this out to:

int c = a + b;

So, again we will go back to our original macro, but this time we are going to pair it with its first use:

#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
typedef X_INPUT_GET_STATE(x_input_get_state);

Based on what we learned earlier we can see that the second line will expand out into the following:

typedef DWORD WINAPI x_input_get_state(DWORD dwUserIndex, XINPUT_STATE *pState);

What's a typedef?

So that's easy enough to see, but what does 'typedef' mean?

In C, typedef declares a different name for a type. This can almost be thought of as similar to #define, but it is limited to type names only and is performed by the compiler instead of the preprocessor.

As a basic example, if we were to look at the following code:

typedef char CHARACTER;
CHARACTER x;

The compiler will treat the type of x as though it were 'char.' Why would we want to do this? Well, because declaring our own types allows us to make use of the compilers strict type checking. This means that if we were to declare a function which took a CHARACTER as the type of one of it's arguments then we would not be able to pass it a char without the compiler warning us about it, even though a CHARACTER was defined as a char and they are both signed one byte values.

typedef DWORD WINAPI x_input_get_state(DWORD dwUserIndex, XINPUT_STATE *pState);

But in the Handmade Hero code, we are using typedef with a function declaration. This declares a function signature as a type. It is important to understand that although we can use that type to declare a function like this:

x_input_get_state _XInputGetState()

Due to the rules of C, we are not allowed to then go on to define the function as follows:

//INVALID C CODE FOLLOWS
x_input_get_state _XInputGetState()
{
    //Do some things.
}

We can define a function using our typedef and also declare the same function by using the original function type information, however, this makes the typedef itself unnecessary.

Using typedef for function pointers

So why are we using a typedef with a function declaration here? Because we are planning on using it to create a function pointer. A function pointer is just what it sounds like, a pointer to a function. The main benefit of function pointers is that they can be treated like variables, that is, they can be reassigned or passed as arguments.

So this is how we would use our "x_input_get_state" typedef to declare a function pointer:

x_input_get_state *PointerToXInputGetStateFunction;

We can then assign that pointer to any function which matches the signature of x_input_get_state:

typedef DWORD WINAPI x_input_get_state(DWORD dwUserIndex, XINPUT_STATE *pState);
DWORD XInputGetStateStub(DWORD dwUserIndex, XINPUT_STATE *pState);
XInputGetStateStub
{
        return(0);
}
global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub;

Which if we were to unwrap our macros from the original code, is almost exactly what we would have:

#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
typedef X_INPUT_GET_STATE(x_input_get_state);
X_INPUT_GET_STATE(XInputGetStateStub)
{
        return(0);
}
global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub;

Now all we have left is the last line:

#define XInputGetState XInputGetState_

This is another simple macro which will substitute "XInputGetState_" for "XInputGetState" any time we use it in the code. We do this because XInputGetState is a function that has already been declared in xinput.h, and C does not allow us to declare functions multiple times. However, by using the preprocessor, we can type out our code as though we were just using XInputGetState we are actually using a function pointer which will either point to 'XInputGetStateStub' in the case that we are unable to dynamically load in the XInput library, or will point to the correct 'XInputGetState' function as defined by Microsoft in the case that we can load it.

Hope that's helped clear some things up for you. Happy Heroing!