aboutsummaryrefslogblamecommitdiffstats
path: root/libgamepad.h
blob: f77e16b36393b1a5d0342b9b4a6fc1070d1ba0e0 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526



                                                         

                        



                   
                   
                 
                   
 
 






                                                    




                                                                           




                                                                       

















                                       





























































































                                                      
                                                       






























































































































                                                                                           
































































































































































































































                                                                                              























                                                                             
































                                                               
                                      
           
















                                                                       







                                                

                                                                  







                                           
















                                                                          







                                          







                                                                

















                                                                         


                                                   








                                                


                                                                                               




                                                                                  

























                                                                                  
                             
















































































































































                                                                                     

                                                                     






                                              




















                                                                       
           



                                                                                   























                                                                                 
                                                                                                             






















































































                                                                                  
                                  












                                                                                          
                            
                               
                              















                                                                                         
                                            
                                             
                                          
































                                                                     

                                  










                                                                      

                           
                                                                                   

                                
           
                                  


















                                                                                                
                            
                            
                            






































                                                                              

                           


                                                                
                                  












                                                                        
                            

                                                                      
                                                     
           






                                                                             
                  
                                                                                         


                                                                                              



                                                           

                                                                                    

                                                                                     



                                                   
















                                                                                 



                                                                              



                                         

                                                                        


                                                                         






                                                                               
                  
                                                                                         

                                                                                              



                                                               

                                                                                    

                                                                                     



                                                   




































                                                                                              

                                                 

































                                                       






















                                                        





















                                                                  
                     




                                                               




                          





                                                               




                                 





                                                            









                                  
                                  



                                        


                                                        





                                                   

                                                        




                          


                                                     

















































































                                                                         






                                                                  


                                                                
                              



                                                         
                            



                                                          
                             



                                                                  
                             

           
                                
           
                                               


                                                           

                                 
           
                   



                                                          

                                 
           
                        


                                     

                                 
           
                                

           















                                                        














                                                                




















                                                         
                                                     
           
                                                         











                                                             
  
 
 

                                                                                                                            



































                                                                               








                                                                      

















































                                                                                                                                 




                                                       














                                                                             
                                              





                                                                               

                                                                 
   
                                                        



                                    


                                                       

                                                




                                                                   

                                                      
                                     

                                                                  
                                                            
                                                                  
   











































                                                                                                   




                               








                                                      





                                                                   
   
                                                                                                     



                                   








                                                      





                                                               
   
                                                                                                            



                                  








                                                      





                                                               
   
                                                                                                            










                                                    
                                           















                                                           
 

























































                                                                                                            

                                                  
   








                                                    



                                                   




                                                              
                                                                           


                                         
   








                                                   



                                                   

                                                              





                                                                     
   

                                                                                                    












                                               
                                                     
                                                     





                                                      
                                                       






                                                   



                                                   
                                                       





                                                           









                                                                 


                                                         



                                                                         






































































































































































































































































































































































































































































                                                                                                                                  



      
/* See LICENSE file for copyright and license details. */
#ifndef LIBGAMEPAD_H
#define LIBGAMEPAD_H

#include <linux/input.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <unistd.h>


#if defined(__GNUC__)
# define LIBGAMEPAD_PURE__ __attribute__((__pure__))
#else
# define LIBGAMEPAD_PURE__
#endif


/**
 * Opaque structure for monitoring device attachment changes
 */
typedef struct libgamepad_attachment_monitor LIBGAMEPAD_ATTACHMENT_MONITOR;

/**
 * Opaque structure for internal data in `struct libgamepad_device`
 */
typedef struct libgamepad_device_internals LIBGAMEPAD_DEVICE_INTERNALS;


/**
 * Device attachment event type
 */
enum libgamepad_attachment_event_type {
	/**
	 * Device has been added
	 */
	LIBGAMEPAD_ADDED,

	/**
	 * Device has been removed
	 */
	LIBGAMEPAD_REMOVED
};


/**
 * Device categorisation
 */
enum libgamepad_class {
	/**
	 * Unknown category
	 */
	LIBGAMEPAD_CLASS_UNKNOWN,

	/**
	 * Unlisted category
	 */
	LIBGAMEPAD_CLASS_OTHER,

	/**
	 * Game controller
	 */
	LIBGAMEPAD_CLASS_GAME_CONTROLLER,

	/**
	 * Joystick
	 */
	LIBGAMEPAD_CLASS_JOYSTICK,

	/**
	 * Steering wheel
	 */
	LIBGAMEPAD_CLASS_STEERING_WHEEL,

	/**
	 * TV remote controller
	 */
	LIBGAMEPAD_CLASS_TV_CONTROLLER,

	/**
	 * 3D modelling mouse
	 */
	LIBGAMEPAD_CLASS_3D_MOUSE,

	/**
	 * Motion sensor (typically camera)
	 */
	LIBGAMEPAD_CLASS_MOTION_SENSOR,

	/**
	 * Piano or similar instrument
	 */
	LIBGAMEPAD_CLASS_PIANO,

	/**
	 * Guitar
	 */
	LIBGAMEPAD_CLASS_GUITAR,

	/**
	 * Drum set
	 */
	LIBGAMEPAD_CLASS_DRUMS,

	/**
	 * Gun
	 */
	LIBGAMEPAD_CLASS_GUN,

	/**
	 * Pen
	 */
	LIBGAMEPAD_CLASS_PEN,

	/**
	 * Non-interactive sensor,
	 * e.g. accelerometer built into laptop
	 */
	LIBGAMEPAD_CLASS_SENSOR
};


/**
 * Game controller profiles
 * 
 * This list only contains well-known profiles, mainly
 * the primary controllers from video gaming consoles
 * and handheld gaming consoles
 */
enum libgamepad_game_controller {
	/**
	 * Unknown controller profile
	 */
	LIBGAMEPAD_CONTROLLER_UNKNOWN,

	/**
	 * Unlisted controller profile
	 */
	LIBGAMEPAD_CONTROLLER_OTHER,

	/* TODO Document force feedback capabilities */

	/**
	 * Amazon Luna Controller
	 * 
	 * Released 2022-03-01 for Amazon Luna
	 * 
	 * Layout:
	 *     Clickable analogue stick at upper left thumb position
	 *     Digital D-pad at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position:
	 *     - North = "Y"
	 *     - West  = "X"
	 *     - South = "A"
	 *     - East  = "B"
	 *     Digital button "LB" at upper left index finger position
	 *     Digital button "RB" at upper right index finger position
	 *     Analogue button "LT" at lower left index finger position
	 *     Analogue button "RT" at lower right index finger position
	 *     Four buttons at upper centre position:
	 *     - West   = Home (icon: circle)
	 *     - Centre = Action (icon: brand)
	 *     - East   = Menu (icon: three vertically stacked horizontal line)
	 *     - South  = Microphone (icon: microphone)
	 * 
	 * Other features:
	 * - Built in microphone
	 * - Stereo headset port
	 * - Wireless via WiFi
	 * - Wireless via Bluetooth Low Energi 4.2
	 * - RGB LED on Action button (??)
	 */
	LIBGAMEPAD_CONTROLLER_AMAZON_LUNA,

	/**
	 * DualSense
	 * 
	 * Released 2020-11-12 for PlayStation 5
	 * 
	 * Layout:
	 *     Digital D-pad at upper left thumb position
	 *     Clickable analogue stick at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position:
	 *     - North = Triangle
	 *     - West  = Square
	 *     - South = Cross
	 *     - East  = Circle
	 *     Digital button "L1" at upper left index finger position
	 *     Digital button "R1" at upper right index finger position
	 *     Analogue button "L2" at lower left index finger position
	 *     Analogue button "R2" at lower right index finger position
	 *     Digital Create (logo: \|/) button north east of D-pad
	 *     Digital Options (logo: three vertically stacked horizontal line)
	 *       button north west of upper right thumb position
	 *     Two digital buttons between analogue stick:
	 *     - North = PS (icon: brand)
	 *     - South = Microphone mute (icon: NW-to-SE diagonally crossed out microphone)
	 *     Clickable 2-point capacitive touchpad between upper thumb positions
	 * 
	 * Other features:
	 * - Built in dual-microphone array
	 * - Built in single-channel speaker
	 * - Stereo headset port
	 * - Extension port
	 * - Wireless via WiFi
	 * - Wireless via Bluetooth 5.1
	 * - RGB LED
	 * - 5 digital LEDs
	 * - 3 axis accelerometer
	 * - 3 axis gyroscope
	 */
	LIBGAMEPAD_CONTROLLER_DUAL_SENSE,

	/**
	 * Google Stadia Controller
	 * 
	 * Released 2019-11-19 for Google Stadia
	 *
	 * Layout:
	 *     Digital D-pad at upper left thumb position
	 *     Clickable analogue stick at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position:
	 *     - North = "Y"
	 *     - West  = "X"
	 *     - South = "A"
	 *     - East  = "B"
	 *     Digital button "L1" at upper left index finger position
	 *     Digital button "R1" at upper right index finger position
	 *     Analogue button "L2" at lower left index finger position
	 *     Analogue button "R2" at lower right index finger position
	 *     Digital Stadia (icon: brand) button between analogue sticks
	 *     2 digital buttons at inner left thumb position:
	 *     - North = Options (icon: three horizontally stacked dots)
	 *     - East  = Google Assistant (icon: four uniquely arrayed dots)
	 *     2 digital buttons at inner right thumb position:
	 *     - North = Menu (icon: three vertically stacked horizontal lines)
	 *     - West  = Capture (icon: corners of a square)
	 * 
	 * Other features:
	 * - Built in microphone (??)
	 * - Stereo headset port
	 * - Wireless via WiFi
	 * - Wireless via Bluetooth Low Energi 4.2
	 * - LED (unknown capabilities)
	 */
	LIBGAMEPAD_CONTROLLER_GOOGLE_STADIA,

	/**
	 * Nintendo Switch Pro Controller
	 * 
	 * Released 2017-03-03 for Nintendo Switch
	 * 
	 * Layout:
	 *     Clickable analogue stick at upper left thumb position
	 *     Digital D-pad at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position:
	 *     - North = "X"
	 *     - West  = "Y"
	 *     - South = "B"
	 *     - East  = "A"
	 *     Digital button "L" at upper left index finger position
	 *     Digital button "R" at upper right index finger position
	 *     Digital button "ZL" at lower left index finger position
	 *     Digital button "ZR" at lower right index finger position
	 *     2 digital buttons at inner left thumb position:
	 *     - North = "−"
	 *     - East  = Capture (icon: circle; shape: square)
	 *     2 digital buttons at inner right thumb position:
	 *     - North = "+"
	 *     - West  = Home (icon: house)
	 * 
	 * Other features:
	 * - Wireless via Bluetooth
	 * - Wireless via NFC
	 * - Accelerometer (3 axis??)
	 * - Gyroscope (3 axis??)
	 * - 4 digital LEDs (??)
	 * 
	 * Some versions seem to have additional features
	 */
	LIBGAMEPAD_CONTROLLER_NINTENDO_SWITCH_PRO,

	/**
	 * Joy-Con L
	 * 
	 * Released 2017-03-03 for Nintendo Switch
	 * 
	 * Layout (when rotated 90 degrees counter-clockwise to become horizontal):
	 *     Clickable analogue stick at left thumb position
	 *     4 digital directional buttons stick a bit inwards of right thumb position
	 *     Digital "−" button north west of analogue stick
	 *     Digital Capture (icon: circle; shape: square)
	 *       button north east of directional buttons
	 *     On front side (hidden when attached):
	 *       Digital "SL" at left index finger position
	 *       Digital "SR" at right index finger position
	 *       Digital synchronisaton button left of "SR"
	 * 
	 * Additional layout (described from attached state):
	 *     Digital "L" button at upper left index finger position
	 *     Digital "ZL" button at lower left index finger position
	 * 
	 * Other features:
	 * - Wireless via Bluetooth 3.0
	 * - Accelerometer (3 axis??)
	 * - Gyroscope (3 axis??)
	 * - 4 digital LEDs, hidden when attached (??)
	 */
	LIBGAMEPAD_CONTROLLER_JOY_CON_L,

	/**
	 * Joy-Con R
	 * 
	 * Released 2017-03-03 for Nintendo Switch
	 * 
	 * Layout (when rotated 90 degrees clockwise to become horizontal):
	 *     Clickable analogue stick a bit inwards of left thumb position
	 *     4 digital buttons stick at right thumb position
	 *     - North = "Y" ("X" when not rotated)
	 *     - West  = "B" ("Y" when not rotated)
	 *     - South = "A" ("B" when not rotated)
	 *     - East  = "X" ("A" when not rotated)
	 *     Digital "+" button north east of group of 4 digital buttons
	 *     Digital Home (icon: house) button north west of analogue stick
	 *     On front side (hidden when attached):
	 *       Digital "SL" at left index finger position
	 *       Digital "SR" at right index finger position
	 *       Digital synchronisaton button left of "SR"
	 * 
	 * Additional layout (described from attached state):
	 *     Digital "R" button at upper right index finger position
	 *     Digital "ZR" button at lower right index finger position
	 * 
	 * Other features:
	 * - Wireless via Bluetooth 3.0
	 * - Wireless via NFC
	 * - Accelerometer (3 axis??)
	 * - Gyroscope (3 axis??)
	 * - 4 digital LEDs, hidden when attached (??)
	 * - Infrared depth camera for motion sensoring
	 */
	LIBGAMEPAD_CONTROLLER_JOY_CON_R,

	/**
	 * Joy-Con
	 * 
	 * Released 2017-03-03 for Nintendo Switch
	 * 
	 * Joy-Con L and Joy-Con R together in attached state
	 */
	LIBGAMEPAD_CONTROLLER_JOY_CON,

	/**
	 * Steam Controller
	 * 
	 * Released 2015-11-10 for Steam
	 * 
	 * TODO Document Steam Controller
	 */
	LIBGAMEPAD_CONTROLLER_STEAM,

	/**
	 * Xbox Wireless Controller
	 * 
	 * Released 2013-11-22 for Xbox One, also primary
	 * controller for Xbox Series X and Xbox Series S
	 * 
	 * Layout:
	 *     Clickable analogue stick at upper left thumb position
	 *     Digital D-pad at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position
	 *     - North = "Y" (colour: yellow)
	 *     - West  = "X" (colour: blue)
	 *     - South = "A" (colour: green)
	 *     - East  = "B" (colour: red)
	 *     3 or 4 digital buttons at centre position
	 *     - North = Xbox (icon: brand)
	 *     - West  = View (icon: two z-stacked windows)
	 *     - East  = Menu (icon: three vertically stacked horizontal lines)
	 * 
	 * Other features:
	 * - Wireless via proprietary protocol
	 * - Wireless via Bluetooth 4.0 (second revision)
	 * - Stereo headset port (second revision)
	 * - Extension port
	 * 
	 * Some versions have additional buttons.
	 * "Y", "X", "A", and "B" are not coloured on some versions.
	 */
	LIBGAMEPAD_CONTROLLER_XBOX_WIRELESS,

	/**
	 * Xbox Wireless Controller, third revision
	 * 
	 * Released 2020-11-10 for Xbox Series X and Xbox Series S
	 * 
	 * Layout:
	 *     Clickable analogue stick at upper left thumb position
	 *     Digital D-pad at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position
	 *     - North = "Y" (colour: yellow)
	 *     - West  = "X" (colour: blue)
	 *     - South = "A" (colour: green)
	 *     - East  = "B" (colour: red)
	 *     3 or 4 digital buttons at centre position
	 *     - North = Xbox (icon: brand)
	 *     - West  = View (icon: two z-stacked windows)
	 *     - South = Share (icon: flat rectangle with upper edge replace with an up-arrow)
	 *     - East  = Menu (icon: three vertically stacked horizontal lines)
	 * 
	 * Other features:
	 * - Wireless via proprietary protocol
	 * - Wireless via Bluetooth Low Energy
	 * - Stereo headset port
	 * - Extension port
	 * 
	 * Some versions have additional buttons.
	 * "Y", "X", "A", and "B" are not coloured on some versions.
	 */
	LIBGAMEPAD_CONTROLLER_XBOX_WIRELESS_REV_3,

	/**
	 * DualShock 4
	 * 
	 * Released 2013-11-15 for PlayStation 4
	 * 
	 * Layout:
	 *     Digital D-pad at upper left thumb position
	 *     Clickable analogue stick at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position:
	 *     - North = Triangle (colour: green)
	 *     - West  = Square (colour: pink)
	 *     - South = Cross (colour: blue)
	 *     - East  = Circle (colour: red)
	 *     Digital button "L1" at upper left index finger position
	 *     Digital button "R1" at upper right index finger position
	 *     Analogue button "L2" at lower left index finger position
	 *     Analogue button "R2" at lower right index finger position
	 *     Digital "SHARE" button north east of D-pad
	 *     Digital "OPTIONS" button north west of upper right thumb position
	 *     Digital PS (icon: brand) button between analogue stick
	 *     Clickable 2-point capacitive touchpad between upper thumb positions
	 * 
	 * Other features:
	 * - Stereo headset port
	 * - Extension port
	 * - Wireless via Bluetooth 2.1+EDR
	 * - RGB LED
	 * - 3 axis accelerometer
	 * - 3 axis gyroscope
	 * - Built in single-channel speaker
	 */
	LIBGAMEPAD_CONTROLLER_DUAL_SHOCK_4,

	/**
	 * Ouya controller
	 * 
	 * Released 2013-06-25 for Ouya
	 *
	 * Layout:
	 *     Clickable analogue stick at upper left thumb position
	 *     Digital D-pad at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position:
	 *     - North = "Y" (colour: orange–yellow)
	 *     - West  = "U" (colour: blue)
	 *     - South = "O" (colour: lime)
	 *     - East  = "A" (colour: red)
	 *     Digital Menu (icon: U inscribed in a ring) button between,
	 *        but slightly below, the D-pad and right stick
	 *     Touchpad (details missing) between upper thumb positions
	 *     Digital button at upper left index finger position
	 *     Digital button at upper right index finger position
	 *     Analogue button at lower left index finger position
	 *     Analogue button at lower right index finger position
	 * 
	 * Other features:
	 * - Wireless via Bluetooth
	 */
	LIBGAMEPAD_CONTROLLER_OUYA,

	/**
	 * Wii U Pro Controller
	 * 
	 * Released 2012-11-18 for Wii U
	 * 
	 * Layout:
	 *     Clickable analogue stick at upper left thumb position
	 *     Clickable analogue stick at upper right thumb position
	 *     Digital D-pad at lower left thumb position
	 *     4 digital buttons at lower right thumb position:
	 *     - North = "X"
	 *     - West  = "Y"
	 *     - South = "B"
	 *     - East  = "A"
	 *     Digital button "L" at upper left index finger position
	 *     Digital button "R" at upper right index finger position
	 *     Digital button "ZL" at lower left index finger position
	 *     Digital button "ZR" at lower right index finger position
	 *     3 digital buttons at centre thumb position:
	 *     - West   = "SELECT" (icon: minus symbol)
	 *     - Centre = "HOME" (icon: house)
	 *     - East   = "START" (icon: plus symbol)
	 *     Digital "POWER" (icon: power symbol with the 1 over top
	 *       of the 0, colour: red) button at lower center thumb position
	 * 
	 * Other features:
	 * - Wireless via Bluetooth
	 * - 4 digital LEDs numbered 1 through 4 (??)
	 * - 1 digital LEDs labelled "BATTERY" (??)
	 */
	LIBGAMEPAD_CONTROLLER_WII_U_PRO,

	/**
	 * Wii U GamePad
	 * 
	 * Released 2012-11-18 for Wii U
	 * 
	 * TODO Document Wii U GamePad
	 */
	LIBGAMEPAD_CONTROLLER_WII_U,

	/**
	 * PlayStation Move Navigation controller
	 * 
	 * Released 2010-09-15 for PlayStation 3
	 * 
	 * TODO Document PlayStation Move Navigation controller
	 */
	LIBGAMEPAD_CONTROLLER_PLAYSTATION_MOVE_NAVIGATION,

	/**
	 * PlayStation Move
	 * 
	 * Released 2010-09-15 for PlayStation 3
	 * 
	 * TODO Document PlayStation Move
	 */
	LIBGAMEPAD_CONTROLLER_PLAYSTATION_MOVE,

	/**
	 * Classic Controller Pro
	 * 
	 * Released 2009-08-01 for Wii
	 * 
	 * Layout:
	 *     Digital D-pad at upper left thumb position
	 *     Analogue stick at lower left thumb position
	 *     Analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position:
	 *     - North = "x"
	 *     - West  = "y"
	 *     - South = "b"
	 *     - East  = "a"
	 *     Digital button "L" at upper left index finger position
	 *     Digital button "R" at upper right index finger position
	 *     Digital button "ZL" at lower left index finger position
	 *     Digital button "ZR" at lower right index finger position
	 *     3 digital buttons at centre thumb position:
	 *     - West   = "SELECT" (icon: minus symbol)
	 *     - Centre = "HOME" (icon: house, colour: blue)
	 *     - East   = "START" (icon: plus symbol)
	 */
	LIBGAMEPAD_CONTROLLER_WII_CLASSIC_PRO,

	/**
	 * DualShock 3
	 * 
	 * Released 2007-11-11 for PlayStation 3
	 * 
	 * Adds rumble support to `LIBGAMEPAD_CONTROLLER_SIXAXIS`,
	 * no other differences
	 */
	LIBGAMEPAD_CONTROLLER_DUAL_SHOCK_3,

	/**
	 * Classic Controller
	 * 
	 * Released 2006-11-19 for Wii
	 * 
	 * Layout:
	 *     Digital D-pad at upper left thumb position
	 *     Analogue stick at lower left thumb position
	 *     Analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position:
	 *     - North = "x"
	 *     - West  = "y"
	 *     - South = "b"
	 *     - East  = "a"
	 *     Analogue button "L" at left index finger position
	 *     Analogue button "R" at right index finger position
	 *     Digital button "ZL" inward from left index finger position
	 *     Digital button "ZR" inward from right index finger position
	 *     3 digital buttons at centre thumb position:
	 *     - West   = "SELECT" (icon: minus symbol)
	 *     - Centre = "HOME" (icon: house, colour: blue)
	 *     - East   = "START" (icon: plus symbol)
	 */
	LIBGAMEPAD_CONTROLLER_WII_CLASSIC,

	/**
	 * Nunchuk
	 * 
	 * Released 2006-11-19 for Wii
	 * 
	 * Layout:
	 *     Designed to be held in the user's dominant hand
	 *     Analogue stick at thumb position
	 *     Digital button "C" at upper index finger position
	 *     Digital button "Z" at lower index finger position
	 * 
	 * Other features:
	 * - Accelerometer
	 */
	LIBGAMEPAD_CONTROLLER_NUNCHUK,

	/**
	 * Wii Remote
	 * 
	 * Released 2006-11-19 for Wii
	 * 
	 * TODO Document Wii Remote
	 */
	LIBGAMEPAD_CONTROLLER_WII_REMOTE,

	/**
	 * Wii Remote Plus
	 * 
	 * Released 2010-10-28 for Wii
	 * 
	 * Adds (3 axis?) gyroscope to `LIBGAMEPAD_CONTROLLER_WII_REMOTE`
	 * 
	 * Conforming controllers:
	 * - Wii Remote with Wii MotionPlus adapter
	 */
	LIBGAMEPAD_CONTROLLER_WII_REMOTE_PLUS,

	/**
	 * Sixaxis
	 * 
	 * Released 2006-11-11 for PlayStation 3
	 * 
	 * Note:
	 * - Newer Linux drivers no longer report pressure on the D-pad, L1, and R1
	 *   (reason: "there is no good way for reporting them", a standard is however defined)
	 * - Newer Linux drivers no longer report pressure Triangle, Square, Cross, or Circle
	 *   (reason: "there is no good way for reporting them")
	 * - Newer Linux drivers no longer report gyroscope readings
	 *   (reason: "very difficult to manage from within the driver even to get
	 *             data, the sensor is inaccurate and the behavior is very
	 *             different between hardware revisions")
	 * - Older Linux drivers failed to report left pressure on the D-pad
	 * 
	 * Layout:
	 *     Pressure-sensitive D-pad at upper left thumb position
	 *     Clickable analogue stick at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 Pressure-sensitive buttons at upper right thumb position:
	 *     - North = Triangle (colour: green)
	 *     - West  = Square (colour: pink)
	 *     - South = Cross (colour: blue)
	 *     - East  = Circle (colour: red)
	 *     Pressure-sensitive button "L1" at upper left index finger position
	 *     Pressure-sensitive button "R1" at upper right index finger position
	 *     Analogue button "L2" at lower left index finger position
	 *     Analogue button "R2" at lower right index finger position
	 *     3 digital buttons at centre position:
	 *     - West  = "SELECT" (shape: flat rectangle)
	 *     - East  = "START"  (shape: flat play icon)
	 *     - South = PS (icon: brand)
	 * 
	 * Other features:
	 * - Stereo headset port
	 * - Extension port
	 * - Wireless via Bluetooth 2.1+EDR
	 * - 4 digital LEDs numbered 1 through 4
	 * - 3 axis accelerometer
	 * - 1 axis gyroscope
	 * - Built in single-channel speaker
	 */
	LIBGAMEPAD_CONTROLLER_SIXAXIS,

	/**
	 * Xbox 360 Controller
	 * 
	 * Released 2005-11-22 for Xbox 360
	 * 
	 * TODO Document Xbox 360 Controller
	 */
	LIBGAMEPAD_CONTROLLER_XBOX_360,

	/**
	 * Xbox Controller S
	 * 
	 * Released 2002-??-?? for Xbox
	 * 
	 * Layout:
	 *     Clickable analogue stick at upper left thumb position
	 *     Digital D-pad at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 Pressure-sensitive buttons at upper right thumb position:
	 *     - North = "Y" (colour: orange)
	 *     - West  = "X" (colour: blue)
	 *     - South = "A" (colour: green)
	 *     - East  = "B" (colour: red)
	 *     Digital button "L" at left index finger position
	 *     Digital button "R" at right index finger position
	 *     Analogue button "LT" at left long finger position
	 *     Analogue button "RT" at right long finger position
	 *     2 digital buttons west of lower left thumb position
	 *     - North = "BACK" (icon: mirrored play icon)
	 *     - East  = "START"  (icon: play icon)
	 *     2 Pressure-sensitive  buttons east of lower right thumb position
	 *     - East  = Black (colour: black)
	 *     - South = White  (colour: white)
	 */
	LIBGAMEPAD_CONTROLLER_XBOX_S,

	/**
	 * Xbox Controller
	 * 
	 * Released 2001-11-15 for Xbox
	 * 
	 * Layout:
	 *     Clickable analogue stick at upper left thumb position
	 *     Digital D-pad at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     6 Pressure-sensitive buttons at upper right thumb position:
	 *     - North = "Y" (colour: orange)
	 *     - West  = "X" (colour: blue)
	 *     - South = "A" (colour: green)
	 *     - East  = "B" (colour: red)
	 *     - North east (slightly off) of "Y" = White (colour: white)
	 *     - North east (slightly off) of "B" = Black (colour: black)
	 *     Digital button "L" at left index finger position
	 *     Digital button "R" at right index finger position
	 *     Analogue button "LT" at left long finger position
	 *     Analogue button "RT" at right long finger position
	 *     2 digital buttons at lower centre thumb position
	 *     - East = "BACK" 
	 *     - West = "START"
	 */
	LIBGAMEPAD_CONTROLLER_XBOX,

	/**
	 * Nintendo GameCube controller
	 * 
	 * Released 2001-09-14 for Nintendo GameCube
	 * 
	 * Layout:
	 *     Analogue stick at upper left thumb position
	 *     Digital D-pad at lower left thumb position
	 *     Analogue "C" (colour: yellow) stick at lower right thumb position
	 *     4 digit buttons at upper right thumb position:
	 *     - Centre           = "A" (colour: green; extra large)
	 *     - South west       = "B" (colour: red)
	 *     - North north west = "Y" (shape: circle arc)
	 *     - North east east  = "X" (shape: circle arc)
	 *     Digital button "Z" (colour: blue) at upper right index finger position
	 *     Analogue button "L" at lower left index finger position
	 *     Analogue button "R" at lower right index finger position
	 *     Digital "START/PAUSE" button at centre thumb position
	 * 
	 * Other features:
	 * - "L" and "R" sends digital signal when fully depressed
	 */
	LIBGAMEPAD_CONTROLLER_NINTENDO_GAME_CUBE,

	/**
	 * DualShock 2 Analog Controller
	 * 
	 * Released 2000-03-04 for PlayStation 2
	 * 
	 * Layout:
	 *     Pressure-sensitive D-pad at upper left thumb position
	 *     Clickable analogue stick at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 Pressure-sensitive buttons at upper right thumb position:
	 *     - North = Triangle (colour: green)
	 *     - West  = Square (colour: pink)
	 *     - South = Cross (colour: blue)
	 *     - East  = Circle (colour: red)
	 *     Pressure-sensitive button "L1" at upper left index finger position
	 *     Pressure-sensitive button "R1" at upper right index finger position
	 *     Pressure-sensitive button "L2" at lower left index finger position
	 *     Pressure-sensitive button "R2" at lower right index finger position
	 *     2 digital buttons at centre position:
	 *     - West = "SELECT" (shape: flat rectangle)
	 *     - East = "START"  (shape: flat play icon)
	 * 
	 * Other features:
	 * - LED-indicated digital "ANALOG" button at south part of center position
	 *   for toggling between analogue and digital mode
	 *   (TODO What does digital mode on DualShock 2 do?)
	 */
	LIBGAMEPAD_CONTROLLER_DUAL_SHOCK_2,

	/**
	 * Dreamcast controller
	 * 
	 * Released 1998-11-27 for Dreamcast
	 * 
	 * Layout:
	 *     Analogue stick at upper left thumb position
	 *     Digital D-pad at lower left thumb position
	 *     4 digital buttons at upper right thumb position:
	 *     - North = "Y" (colour: green)
	 *     - West  = "X" (colour: yellow)
	 *     - South = "A" (colour: red)
	 *     - East  = "B" (colour: blue)
	 *     Digital "START" (shape: up-pointing equilateral trangle)
	 *       button at lower centre thumb position
	 * 
	 * Other features:
	 * - 2 expansion sockets
	 */
	LIBGAMEPAD_CONTROLLER_DREAMCAST,

	/**
	 * DualShock Analog Controller
	 * 
	 * Released 1997-11-20 for PlayStation
	 * 
	 * Adds rubble support to `LIBGAMEPAD_CONTROLLER_DUAL_ANALOG`
	 * (TODO What does digital mode on DualShock do?)
	 */
	LIBGAMEPAD_CONTROLLER_DUAL_SHOCK,

	/**
	 * Dual Analog Controller
	 * 
	 * Released 1997-04-25 for PlayStation
	 *
	 * The first Japanese version of this controller
	 * had rumble support and is therefore better
	 * reported as `LIBGAMEPAD_CONTROLLER_DUAL_SHOCK`
	 * 
	 * Layout:
	 *     Digital D-pad at upper left thumb position
	 *     Clickable analogue stick at lower left thumb position
	 *     Clickable analogue stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position
	 *     - North = Triangle (colour: green)
	 *     - West  = Square (colour: pink)
	 *     - South = Cross (colour: blue)
	 *     - East  = Circle (colour: red)
	 *     Digital button "L1" at upper left index finger position
	 *     Digital button "R1" at upper right index finger position
	 *     Digital button "L2" at lower left index finger position
	 *     Digital button "R2" at lower right index finger position
	 *     2 digital buttons at centre position:
	 *     - West = "SELECT" (shape: flat rectangle)
	 *     - East = "START"  (shape: flat play icon)
	 * 
	 * Other features:
	 * - LED-indicated digital "ANALOG" button at south part of center position
	 *   for toggling between analogue and digital mode
	 *   (TODO What does digital mode on Dual Analog do?)
	 */
	LIBGAMEPAD_CONTROLLER_DUAL_ANALOG,

	/**
	 * 3D Pad
	 * 
	 * Released 1996-07-05 for Sega Saturn
	 * 
	 * TODO Document 3D Pad
	 */
	LIBGAMEPAD_CONTROLLER_3D_PAD,

	/**
	 * Nintendo 64 controller
	 * 
	 * Released 1996-06-23 for Nintendo 64
	 * 
	 * Layout:
	 *     M-shaped, allowing it to be hold one-handed as
	 *       a gun or two-handed as a regular game controller
	 *     Digital D-pad at left thumb position
	 *     Analogue stick at middle thumb position
	 *     4 directional "C" (colour: yellow) buttons at right thumb position
	 *     Digital "L" button at left index finger position
	 *     Digital "Z" button at middle index finger position (we select BTN_TRIGGER_HAPPY1 for this one)
	 *     Digital "R" button at right index finger position
	 *     Digital "START" (colour: red) button north of middle thumb position
	 *     2 digital buttons south east of right thumb position
	 *     - North = "B" (colour: green)
	 *     - West  = "A" (colour: blue)
	 * 
	 * Other features:
	 * - Expension slot
	 */
	LIBGAMEPAD_CONTROLLER_NINTENDO_64,

	/**
	 * Virtual Boy controller
	 *
	 * Released 1995-07-21 for Virtual Boy
	 *
	 * Layout:
	 *     Digital D-pad at outer left thumb position
	 *     Digital D-pad at outer right thumb position
	 *     Digital "SELECT" button at middle left thumb position
	 *     Digital "A" button at middle right thumb position
	 *     Digital "START" button at inner left thumb position
	 *     Digital "B" button at inner right thumb position
	 *     Digital button at rear left index finger position
	 *     Digital button at rear right index finger position
	 */
	LIBGAMEPAD_CONTROLLER_VIRTUAL_BOY,

	/**
	 * PlayStation Controller
	 * 
	 * Released 1994-12-03 for PlayStation
	 * 
	 * Layout:
	 *     Digital D-pad at left thumb position
	 *     4 digital buttons at right thumb position:
	 *     - North = Triangle (colour: green)
	 *     - West  = Square (colour: pink)
	 *     - South = Cross (colour: blue)
	 *     - East  = Circle (colour: red)
	 *     Digital button "L1" at upper left index finger position
	 *     Digital button "R1" at upper right index finger position
	 *     Digital button "L2" at lower left index finger position
	 *     Digital button "R2" at lower right index finger position
	 *     2 digital buttons at centre position:
	 *     - West = "SELECT" (shape: flat rectangle)
	 *     - East = "START"  (shape: flat play icon)
	 */
	LIBGAMEPAD_CONTROLLER_PLAY_STATION,

	/**
	 * Sega Saturn controller
	 * 
	 * Released 1994-11-22 for Sega Saturn
	 * 
	 * Layout:
	 *     Digital D-pad at left thumb position
	 *     6 digital buttons at right thumb position:
	 *     - Lower left   = "A"
	 *     - Lower middle = "B"
	 *     - Lower right  = "C"
	 *     - Upper left   = "X"
	 *     - Upper middle = "Y"
	 *     - Upper right  = "Z"
	 *     Digital button left index finger position
	 *     Digital button right index finger position
	 *     Digital "START" (shape: horizontal line) button at centre position
	 */
	LIBGAMEPAD_CONTROLLER_SEGA_SATURN,

	/**
	 * 6-Button Arcade Pad
	 * 
	 * Released 1993-??-?? for Sega Mega Drive
	 * 
	 * Layout:
	 *     Digital D-pad at left thumb position
	 *     6 digital buttons at right thumb position:
	 *     - Lower left   = "A"
	 *     - Lower middle = "B"
	 *     - Lower right  = "C"
	 *     - Upper left   = "X"
	 *     - Upper middle = "Y"
	 *     - Upper right  = "Z"
	 *     Digital "MODE" button right index finger position
	 *     Digital "START" (shape: horizontal line) button at centre position
	 * 
	 * Conforming controllers:
	 * - Sega Nomad (however "MODE" is east of "START" which is far south east of "A")
	 */
	LIBGAMEPAD_CONTROLLER_6_BUTTON_ARCADE_PAD,

	/**
	 * Gravis PC GamePad
	 * 
	 * Released 1992-??-?? for PC
	 *
	 * Layout:
	 *     Digital D-pad at left thumb position
	 *     4 digital buttons at right thumb position
	 *     - North = blue
	 *     - West  = red
	 *     - South = yellow
	 *     - East  = green
	 * 
	 * Other features:
	 * - Has a switch for rotating the layout 180 degrees
	 * - Has a switch for turning two buttons into autofire versions of the other two
	 */
	LIBGAMEPAD_CONTROLLER_GRAVIS_PC,

	/**
	 * SNES controller
	 * 
	 * Released 1990-11-21 for SNES (Super Nintendo Entertainment System)
	 * 
	 * Layout:
	 *     Digital D-pad at left thumb position
	 *     4 digital buttons at right thumb position:
	 *     - North = "X" (colour: blue)
	 *     - West  = "Y" (colour: green)
	 *     - South = "B" (colour: yellow)
	 *     - East  = "A" (colour: red)
	 *     Digital "L" button at left index finger position
	 *     Digital "R" button at right index finger position
	 *     2 digital (/ shaped) buttons at centre thumb position:
	 *     - East = "SELECT"
	 *     - West = "START"
	 *
	 * The buttons are uncoloured on some versions
	 */
	LIBGAMEPAD_CONTROLLER_SNES,

	/**
	 * Sega Mega Drive controller
	 * 
	 * Released 1988-10-29 for Sega Mega Drive
	 * 
	 * Layout:
	 *     Digital D-pad at left thumb position
	 *     3 digital buttons at right thumb position:
	 *     - Left   = "A"
	 *     - Middle = "B"
	 *     - Right  = "C"
	 *     Digital "START" (shape: horizontal line) above "B"
	 */
	LIBGAMEPAD_CONTROLLER_SEGA_MEGA_DRIVE,

	/**
	 * Master System controller
	 * 
	 * Released 1986-09-?? for Master System
	 * 
	 * Layout:
	 *     Digital D-pad at left thumb position
	 *     2 digital buttons at right thumb position:
	 *     - West  = "1 START"
	 *     - East  = "2"
	 */
	LIBGAMEPAD_CONTROLLER_MASTER_SYSTEM,

	/**
	 * NES controller
	 * 
	 * Released 1983-07-15 for NES (Nintendo Entertainment System)
	 * 
	 * Layout:
	 *     Digital D-pad at left thumb position
	 *     2 digital buttons at right thumb position:
	 *     - West = "B"
	 *     - East = "A"
	 *     2 digital buttons (shape: horizontal line) at centre thumb position:
	 *     - West = "SELECT"
	 *     - East = "START"
	 * 
	 * Conforming controllers:
	 * - Nintendo Game Boy
	 * - Supervision (however "START" is also labelled "PAUSE" and the "SELECT" and "START"
	 *                buttons are horizontal and north (slightly east) for "A" and "B" which
	 *                are with a / slant)
	 * - Mega Duck (however "SELECT" and "START" are south of "A" and "B")
	 */
	LIBGAMEPAD_CONTROLLER_NES,

	/**
	 * Steam Deck
	 * 
	 * Handheld gaming PC released 2022-02-25
	 * 
	 * Layout:
	 *     Digital D-pad at outer left thumb position
	 *     Analogue stick with capacitive touch at natural left thumb position
	 *     Analogue stick with capacitive touch at natural right thumb position
	 *     4 digital buttons at outer right thumb position:
	 *     - North = "Y"
	 *     - West  = "X"
	 *     - South = "A"
	 *     - East  = "B"
	 *     Multi-touch trackpad at lower left thumb position
	 *     Multi-touch trackpad at lower right thumb position
	 *     Digital "L1" button at upper left index finger position
	 *     Digital "R1" button at upper right index finger position
	 *     Analogue "L2" button at lower left index finger position
	 *     Analogue "R2" button at lower right index finger position
	 *     Digital "L4" button at left long finger position
	 *     Digital "R4" button at right long finger position
	 *     Digital "L5" button at left ring finger position
	 *     Digital "R5" button at right ring finger position
	 *     Digital "STEAM" button beneath lower left thumb position
	 *     Digital Quick Access (icon: three horizontally stacked dots)
	 *       button beneath lower right thumb position
	 *     Digital View (icon: two z-stacked windows) button
	 *       north west of natural left thumb position
	 *     Digital Menu (icon: three vertically stacked horizontal lines)
	 *       button north east of natural right thumb position
	 *     2 digital buttons inward from upper left index finger position:
	 *     - Outer = Volume "−"
	 *     - Inner = Volume "+"
	 * 
	 * Other features:
	 * - 3 axis accelerometer
	 * - 3 axis gyroscope
	 * - Ambient light sensor
	 */
	LIBGAMEPAD_CONTROLLER_STEAM_DECK,

	/**
	 * Game Boy Advanced
	 *
	 * Handheld gaming console released 2001-03-21
	 *
	 * Layout:
	 *     Digital D-pad at left thumb position
	 *     2 digital buttons at south south west of D-pad
	 *     - North = "START"
	 *     - South = "SELECT"
	 *     2 digital buttons at right thumb position:
	 *     - West = "B"
	 *     - East = "A"
	 *     Digital "L" button at left index finger position
	 *     Digital "R" button at right index finger position
	 *
	 * Conforming controllers:
	 * - Game King (however "SELECT" and "START" are north of D-pad,
	 *              "SELECT" to east and "START to west)
	 */
	LIBGAMEPAD_CONTROLLER_GAME_BOY_ADVANCED,

	/**
	 * Game Gear
	 * 
	 * Handheld gaming console released 1990-10-06
	 * 
	 * Layout:
	 *     Digital D-pad at left thumb position
	 *     2 digital buttons at right thumb position:
	 *     - West  = "1"
	 *     - North = "2"
	 *     Digital "START" (colour: blue, shape: corda-cut circle)
	 *       button north of right thumb position
	 */
	LIBGAMEPAD_CONTROLLER_GAME_GEAR,

	/**
	 * Linux reference gamepad layout
	 * 
	 * Version documented since Linux 4.12 in Documentation/input/gamepad
	 * 
	 * Layout:
	 *     Analogue ABS_HAT0X/ABS_HAT0Y/BTN_DPAD_* D-pad at upper left thumb position
	 *     Clickable analogue BTN_THUMBL/ABS_X/ABS_Y stick at lower left thumb position
	 *     Clickable analogue BTN_THUMBR/ABS_RX/ABS_RY stick at lower right thumb position
	 *     4 digital buttons at upper right thumb position:
	 *     - North or upper left  = BTN_NORTH (= BTN_X)
	 *     - West  or lower left  = BTN_WEST  (= BTN_Y)
	 *     - South or lower right = BTN_SOUTH (= BTN_A)
	 *     - East  or upper right = BTN_EAST  (= BTN_B)
	 *     Analogue button BTN_TL/ABS_HAT1Y at upper left index finger position
	 *     Analogue button BTN_TR/ABS_HAT1X at upper right index finger position
	 *     Analogue button BTN_TL2/ABS_HAT2Y at lower left index finger position
	 *     Analogue button BTN_TR2/ABS_HAT2X at lower right index finger position
	 *     3 digital button north of the centre
	 *     - West  = BTN_SELECT
	 *     - East  = BTN_START
	 *     - South = BTN_MODE
	 * 
	 * Applications shall be aware that kernel drivers, even written for
	 * specific controllers, do not always use the correct codes. For example
	 * the Sony drivers currently (Linux 5.18) use ABS_Z and ABS_RZ instead
	 * of ABS_HAT2Y and ABS_HAT2X respectively.
	 * 
	 * There are multiple substandard, this one includes all features,
	 * however notably, there cannot be a BTN_SELECT without a BTN_START,
	 * and all gamepads must have at least 2 action buttons: BTN_SOUTH
	 * and BTN_EAST, and if there is a third action button, it is mapped
	 * to BTN_WEST. The action buttons, in the case that there is either
	 * 2 or 3 of them, ordered from left to right: BTN_WEST (if present),
	 * BTN_SOUTH, BTN_EAST, with the caveat that if they buttons are
	 * perfectly vertically aligned the order, is from bottom up, BTN_SOUTH,
	 * BTN_EAST if there are 2 action buttons but reversed if there are 3
	 * action buttons: (from bottom up) BTN_EAST, BTN_SOUTH, BTN_WEST
	 * (`LIBGAMEPAD_CONTROLLER_LINUX_4_12_RECTIFIED_3BTN` is added to deal
	 * with this problem). In the event that a controller has only one
	 * action button (and truly is a gamepad and not a joystick), it shall
	 * be reported as BTN_SOUTH as this one is designated as mandatory
	 * (BTN_GAMEPAD) is an alias of it.
	 */
	LIBGAMEPAD_CONTROLLER_LINUX_4_12,

	/**
	 * libgamepad defined amended version of the 3-button subversion
	 * of `LIBGAMEPAD_CONTROLLER_LINUX_4_12`
	 * 
	 * There are multiple substandard, this one includes all features
	 * 
	 * The amended version flips the order of BTN_WEST, BTN_SOUTH, BTN_EAST
	 * (the action buttons) when they are perfectly vertically aligned. A
	 * layout that conforms to either `LIBGAMEPAD_CONTROLLER_LINUX_4_12` or
	 * `LIBGAMEPAD_CONTROLLER_LINUX_4_12_RECTIFIED_3BTN`, and has
	 * exactly 3 actions button that are not perfectly vertically aligned,
	 * conforms to both.
	 * 
	 * Layout:
	 *     Analogue ABS_HAT0X/ABS_HAT0Y/BTN_DPAD_* D-pad at upper left thumb position
	 *     Clickable analogue BTN_THUMBL/ABS_X/ABS_Y stick at lower left thumb position
	 *     Clickable analogue BTN_THUMBR/ABS_RX/ABS_RY stick at lower right thumb position
	 *     3 digital buttons at upper right thumb position:
	 *     - Left/lower  = BTN_WEST  (= BTN_Y)
	 *     - Middle      = BTN_SOUTH (= BTN_A)
	 *     - Right/upper = BTN_EAST  (= BTN_B)
	 *     Analogue button BTN_TL/ABS_HAT1Y at upper left index finger position
	 *     Analogue button BTN_TR/ABS_HAT1X at upper right index finger position
	 *     Analogue button BTN_TL2/ABS_HAT2Y at lower left index finger position
	 *     Analogue button BTN_TR2/ABS_HAT2X at lower right index finger position
	 *     3 digital button north of the centre
	 *     - West  = BTN_SELECT
	 *     - East  = BTN_START
	 *     - South = BTN_MODE
	 * 
	 * Applications shall be aware that kernel drivers, even written for
	 * specific controllers, do not always use the correct codes. For example
	 * the Sony drivers currently (Linux 5.18) use ABS_Z and ABS_RZ instead
	 * of ABS_HAT2Y and ABS_HAT2X respectively.
	 */
	LIBGAMEPAD_CONTROLLER_LINUX_4_12_RECTIFIED_3BTN,

	/**
	 * libgamepad defined 6-button extension of `LIBGAMEPAD_CONTROLLER_LINUX_4_12`
	 * 
	 * There are multiple substandard, this one includes all features
	 * 
	 * Layout:
	 *     Analogue ABS_HAT0X/ABS_HAT0Y/BTN_DPAD_* D-pad at upper left thumb position
	 *     Clickable analogue BTN_THUMBL/ABS_X/ABS_Y stick at lower left thumb position
	 *     Clickable analogue BTN_THUMBR/ABS_RX/ABS_RY stick at lower right thumb position
	 *     6 digital buttons at upper right thumb position:
	 *     - Upper right  = BTN_X (= BTN_NORTH)
	 *     - Upper middle = BTN_Y (= BTN_WEST)
	 *     - Upper left   = BTN_Z
	 *     - Lower left   = BTN_A (= BTN_SOUTH)
	 *     - Lower middle = BTN_B (= BTN_EAST)
	 *     - Lower right  = BTN_C
	 *     Analogue button BTN_TL/ABS_HAT1Y at upper left index finger position
	 *     Analogue button BTN_TR/ABS_HAT1X at upper right index finger position
	 *     Analogue button BTN_TL2/ABS_HAT2Y at lower left index finger position
	 *     Analogue button BTN_TR2/ABS_HAT2X at lower right index finger position
	 *     3 digital button north of the centre
	 *     - West  = BTN_SELECT
	 *     - East  = BTN_START
	 *     - South = BTN_MODE
	 * 
	 * Applications shall be aware that kernel drivers, even written for
	 * specific controllers, do not always use the correct codes. For example
	 * the Sony drivers currently (Linux 5.18) use ABS_Z and ABS_RZ instead
	 * of ABS_HAT2Y and ABS_HAT2X respectively.
	 */
	LIBGAMEPAD_CONTROLLER_LINUX_4_12_6BTN_EXT
};


/**
 * Joystick profiles
 * 
 * This list only contains well-known profiles, mainly
 * the primary controllers from video gaming consoles
 */
enum libgamepad_joystick {
	/**
	 * Unknown joystick profile
	 */
	LIBGAMEPAD_JOYSTICK_UNKNOWN,

	/**
	 * Unlisted joystick profile
	 */
	LIBGAMEPAD_JOYSTICK_OTHER,

	/**
	 * Atari CX40 joystick
	 *
	 * Released 1978 for Atari 2600
	 *
	 * Layout:
	 *     8-directional digital joystick in centre
	 *     Digital button at north east
	 */
	LIBGAMEPAD_JOYSTICK_ATARI_CX40
};


/**
 * Device type
 *
 * A device can have any number of these applied to
 * it, it may also have unknown types beyond these
 */
enum libgamepad_type {
	/**
	 * Gamepad, joystick, steering wheel, or similar
	 */
	LIBGAMEPAD_GAMEPAD = 0x0001,

	/**
	 * Computer mouse
	 */
	LIBGAMEPAD_MOUSE = 0x0002
};


/**
 * Gamepad input type
 */
enum libgamepad_input_type {
	/**
	 * Signals thatevents where dropped because
	 * the application did not read fast enough
	 * 
	 * When received, the next call to `libgamepad_next_event`
	 * will drain the event queue and generate synchronisation
	 * events (which have the timestamp set to zero) to bring
	 * the applications state of the buttons/keys and absolute
	 * axes up to date
	 */
	LIBGAMEPAD_EVENT_DROP,

	/**
	 * Signals that all previous events, from the
	 * last `LIBGAMEPAD_EVENT_END` or `LIBGAMEPAD_EVENT_DROP`
	 * where part of the same report from the input device
	 * 
	 * This event will always be sent, however it may have
	 * been dropped when a `LIBGAMEPAD_EVENT_DROP` is sent
	 */
	LIBGAMEPAD_EVENT_END,

	/**
	 * Button/key
	 * 
	 * Expectation when `LIBGAMEPAD_EVENT_DROP` has been
	 * received: a full press and release, or release and
	 * press, may be missing the if read interval is longer
	 * than such an event would span
	 */
	LIBGAMEPAD_BUTTON,

	/**
	 * Absolute axis
	 * 
	 * Expectation when `LIBGAMEPAD_EVENT_DROP` has been
	 * received: less smooth transition between two states,
	 * and quick movements in one direction and back may
	 * be missing, but only such movements that are sorter
	 * than the read duration
	 */
	LIBGAMEPAD_ABSOLUTE_AXIS,

	/**
	 * Relative axis
	 * 
	 * Expectation when `LIBGAMEPAD_EVENT_DROP` has been
	 * received: movement details are lost and calculate
	 * position is off (it is possible that the driver
	 * sends synchronisations event to adjust for the
	 * position)
	 */
	LIBGAMEPAD_RELATIVE_AXIS
};


/**
 * Subdevice input event structure
 */
struct libgamepad_input_event {
	/**
	 * The affected input type
	 */
	enum libgamepad_input_type type;

	/**
	 * The button/key or axis affected by the event
	 * 
	 * Not set if `.type` is `LIBGAMEPAD_EVENT_DROP`
	 */
	uint16_t code;

	/**
	 * The new value on the button/key or axis,
	 * or the delta if on a relative axis
	 * 
	 * Not set if `.type` is `LIBGAMEPAD_EVENT_DROP`
	 */
	int32_t value;

	/**
	 * Event timestamp
	 *
	 * Set to zero if not supported by the driver
	 * or if the event generated by the library
	 */
	struct timeval time;
};


/**
 * Subdevice on a device
 * 
 * For example a modern gamepad may be split into
 * a gamepad, computer mouse, and motion sensor
 *
 * Structure is always deallocated by the library
 * using free(3)
 */
struct libgamepad_subdevice {
	/**
	 * Device type
	 */
	enum libgamepad_type type;

	/**
	 * Device path
	 */
	char path[];
};


/**
 * Physical device
 * 
 * Contents of structure is deallocated with
 * `libgamepad_close_superdevice`
 */
struct libgamepad_superdevice {
	/**
	 * Device path in /sys
	 */
	char *syspath;

	/**
	 * Number of subdevices
	 */
	size_t ndevices;

	/**
	 * Subdevices
	 */
	struct libgamepad_subdevice **devices;

	/**
	 * Number of LEDs
	 */
	size_t nleds;

	/**
	 * Paths to LEDs
	 */
	char **leds;

	/**
	 * Number of power supplies (batteries)
	 */
	size_t npower_supplies;

	/**
	 * Paths of power supplies (batteries)
	 */
	char **power_supplies;
};


/**
 * Subdevice structure for detailed information listening on input events
 */
struct libgamepad_device {
	/**
	 * File descriptor to the device, the application
	 * may use it to poll for read-readyness
	 */
	int fd;

	/**
	 * Specifies whether the library shall automatically call
	 * `libgamepad_generate_sync_events` if events are dropped
	 * or the event queue is manually drained
	 */
	int auto_sync;

	/**
	 * Bus type the device is connect via, see BUS_-prefixed
	 * constants in <linux/input.h>
	 */
	unsigned int bus_type;

	/**
	 * Vendor ID for the device (sub- or superdevice)
	 */
	unsigned int vendor;

	/**
	 * Product ID for the device (sub- or superdevice)
	 */
	unsigned int product;

	/**
	 * Product version ID for the device (sub- or superdevice)
	 */
	unsigned int version;

	/**
	 * Data for internal use
	 */
	LIBGAMEPAD_DEVICE_INTERNALS *internals;

	/**
	 * Human-readable device (sub- or superdevice) name
	 * 
	 * Empty if not available
	 */
	char *name;

	/**
	 * ID that is supposted to be unique to the device
	 * (sub- or superdevice)
	 * 
	 * Empty if not available
	 */
	char *unique_id;

	/**
	 * The location if the device
	 * 
	 * Empty if not available
	 */
	char *physical_location;

	/**
	 * Number of (digital) buttons/keys present
	 * on the device
	 */
	size_t nbuttons;

	/**
	 * Number of absolute axes present on the device
	 */
	size_t nabsolute_axes;

	/**
	 * Number of relative axes present on the device
	 */
	size_t nrelative_axes;

	/**
	 * Map from button/key indices to button/key codes
	 */
	uint16_t *buttons;

	/**
	 * Map from absolute axis indices to absolute axis codes
	 */
	uint16_t *absolute_axes;

	/**
	 * Map from relative axis indices to relative axis codes
	 */
	uint16_t *relative_axes;

	/**
	 * Maps from button/key codes to button indices,
	 * non-present buttons/keys map to -1, other
	 * values are in [0, `.nbuttons`[.
	 */
	int16_t button_map[KEY_CNT];

	/**
	 * Maps from absolute axis codes to absolute axis
	 * indices, non-present axes map to -1, other
	 * values are in [0, `.nabsolute_axes`[.
	 */
	int16_t absolute_axis_map[ABS_CNT];

	/**
	 * Maps from relative axis codes to absolute axis
	 * indices, non-present axes map to -1, other
	 * values are in [0, `.nrelative_axes`[.
	 */
	int16_t relative_axis_map[REL_CNT];

	/**
	 * Bitmap of supported force feedback effects
	 */
	uint8_t force_feedback_support[(FF_CNT + 7) / 8];

	/**
	 * Device fingerprint that does not take `.unique_id`
	 * into account
	 */
	char fingerprint[65];

	/**
	 * Device fingerprint that does take `.unique_id`
	 * into account
	 */
	char fingerprint_unique[65];
};


/* FOR INTERNAL USE */
void libgamepad_construct_force_feedback_effect__(struct ff_effect *, const struct ff_effect *, double, uint16_t, uint16_t);


/**
 * Get a list of all available physical devices
 * 
 * @param   devicesp   Output parameter for the list of devices
 * @param   ndevicesp  Output parameter for the number of listed devices
 * @return             0 on success, -1 on failure
 * 
 * This function may fail for any reason specified for
 * realloc(3), open(3), fdopendir(3), or readdir(3)
 */
int libgamepad_list_superdevices(char ***, size_t *);

/**
 * Get information about a physical device
 * 
 * @param   devicep  Output parameter for the device information,
 *                   allocated memory shall be deallocated with
 *                   `libgamepad_close_superdevice(devicep)`
 * @param   syspath  The path of the device, in /sys
 * @return           0 on success, -1 on failure
 * 
 * This function may fail for any reason specified for
 * realloc(3), openat(3), fdopendir(3), or readdir(3)
 */
int libgamepad_open_superdevice(struct libgamepad_superdevice *, const char *);

/**
 * Deallocate the contents of a `struct libgamepad_superdevice`
 * 
 * @param  device  The structure whose nested memory allocations
 *                 shall be deallocated, may be `NULL`
 */
void libgamepad_close_superdevice(struct libgamepad_superdevice *);

/**
 * Search for sound devices built into or attached a device
 * 
 * @param   syspath  The superdevice's path in /sys
 * @param   cardsp   Output parameter for a list of sound card indices
 * @return           The number of found sound cards, -1 on failure
 */
ssize_t libgamepad_find_sound_devices(const char *, size_t **);


/**
 * Create a device attachment monitor
 * 
 * The user shall poll the returned file descriptor
 * for read-readiness, and whenever it is ready,
 * call `libgamepad_get_attachment_event`
 * 
 * @param   monitorp  Output parameter for the monitor, shall deallocated with
 *                    `libgamepad_destroy_attachment_monitor` when no longer used
 * @return            A file descriptor on successful completion, -1 on failure
 *
 * This function may fail for any reason specified by
 * malloc(3), udev_new(3), udev_monitor_new_from_netlink(3), or
 * udev_monitor_enable_receiving(3)
 */
int libgamepad_create_attachment_monitor(LIBGAMEPAD_ATTACHMENT_MONITOR **);

/**
 * Deallocate a device attachment monitor
 * 
 * @param  monitor  The monitor to deallocate; the file descriptor returned
 *                  with it will also be closed and become invalid, may be `NULL`
 */
void libgamepad_destroy_attachment_monitor(LIBGAMEPAD_ATTACHMENT_MONITOR *);

/**
 * Get the next device attachment event
 * 
 * @param   monitor   Monitor created with `libgamepad_create_attachment_monitor`
 * @param   syspathp  Pointer to output buffer for the device's path in /sys;
 *                    the buffer may be reallocated by the function
 * @param   sizep     Pointer to the allocation size of `*syspath`; may be
 *                    updated by the function
 * @param   typep     Output parameter for the attachment event type; in the
 *                    event that this value is set to `LIBGAMEPAD_REMOVED`, the
 *                    device is not necessarily on supported by this library
 * @return            1 on success, 0 if the received event was suppressed
 *                    by the library, -1 on failure
 * 
 * This function may fail for any reason specified for
 * realloc(3) or udev_monitor_receive_device(3); notable,
 * this function may set `errno` to `EAGAIN` or `EINTR`
 */
int libgamepad_get_attachment_event(LIBGAMEPAD_ATTACHMENT_MONITOR *, char **, size_t *, enum libgamepad_attachment_event_type *);


/**
 * Open a subdevice
 * 
 * This function may temporarily modify the file status
 * flags (which affects all duplicate file descriptors
 * share the same open file descriptor) of `dirfd` if
 * `path` is `NULL` or empty
 * 
 * @param   devicep  Output parameter for the device information and handles;
 *                   deallocated with `libgamepad_close_device`
 * @param   dirfd    File descriptor to path that `path` is relative to
 *                   (unless it is an absolute path), or `AT_FDCWD` for the
 *                   current working directory
 * @param   path     The path to the device, if `NULL` or empty, `dirfd`
 *                   will be used as the file descriptor to the device,
 *                   in which case the application must keep it open until
 *                   the device is closed, and then close it manually, and
 *                   `mode` is ignored
 * @param   mode     Mode to open the device in, normally `O_RDONLY`,
 *                   `O_RDONLY|O_NONBLOCK`, `O_RDWR`, or `O_RDWR|O_NONBLOCK`
 * @return           0 on success, -1 on failure
 * 
 * This function may fail for any reason specified for
 * realloc(3), openat(3), ioctl(3), or read(3)
 */
int libgamepad_open_device(struct libgamepad_device *, int, const char *, int);

/**
 * Close a subdevice
 * 
 * @param   device  Device information and handles, may be `NULL`
 * @return          Normally 0, -1 if there was a deferred error
 */
int libgamepad_close_device(struct libgamepad_device *);

/**
 * Get the next event on a subdevice
 * 
 * This function may temporarily modify the file status
 * flags of the open file decriptor it uses to access
 * the device
 * 
 * @param   device  Device to read an event from
 * @param   events  Output buffer for the events (will be in order)
 * @param   max     The number of elements `events` fit
 *                  (may be 0 for error condition checking)
 * @return          The number of returned events (may be more or
 *                  less than actually read), -1 on failure
 * 
 * This function may fail for any reason specified for
 * realloc(3) and read(3), including:
 * -  EAGAIN  no more data currently available (non-blocking mode)
 * -  EINTR   system call was interrupted, try again
 *            (may be deferred to `libgamepad_close_device`)
 * -  ENODEV  device has been unplugged or access has been revoked
 */
ssize_t libgamepad_next_event(struct libgamepad_device *, struct libgamepad_input_event *, size_t);

/**
 * Remove all events on a subdevice queued by kernel
 * and queue synchronisation events so that the application
 * will have (after reading them) up to date button/key and
 * absolute axis states
 * 
 * This function is normally used internally, but can be
 * useful after calling `libgamepad_set_clock`
 * 
 * This function may temporarily modify the file status
 * flags of the open file decriptor it uses to access
 * the device
 * 
 * @param   device  Device to drain queue for
 * @return          0 on success, -1 on failure
 * 
 * This function may fail for any reason specified for
 * realloc(3) and read(3), including:
 * -  ENODEV  device has been unplugged or access has been revoked
 * except for EINTR which is deferred to the next call to
 * `libgamepad_next_event` or `libgamepad_close_device`
 * (whichever comes first) for the same device, and EAGAIN
 */
int libgamepad_drain_events(struct libgamepad_device *);

/**
 * Generates any events needed to keep the applications
 * button/key and absolute axes states up to date
 * 
 * This function is normally used internally, but can be
 * useful after calling `libgamepad_drain_events` if
 * automatic generation of synchronisation events have
 * been disabled by modifying `device->auto_sync`
 * 
 * This function may temporarily modify the file status
 * flags of the open file decriptor it uses to access
 * the device
 * 
 * @param   device  Device to synchronise
 * @return          0 on success, -1 on failure
 */
int libgamepad_generate_sync_events(struct libgamepad_device *);


/**
 * Get the name of a button/key
 * 
 * If `device` is `NULL`, the returned name will
 * have a prefix , that does not include an underscore
 * except (mandatorily) as the very last character
 * in the prefix, however if the key/button is valid
 * but does not have a name a numerical representation
 * is given (currently "0x" prefixed upper case
 * hexadecimal); there are currently two prefixes:
 * "KEY_" and "BTN_"
 * 
 * @param   device  If `NULL`, a generic name will be returned,
 *                  otherwise the canonical name for the button/key
 *                  on the specific device will be returned
 *                  (`NULL` if not information is unavailable)
 * @param   code    The button/key
 * @return          The button/key's name, `NULL` if not found
 */
LIBGAMEPAD_PURE__ const char *libgamepad_get_button_name(const struct libgamepad_device *, uint16_t);

/**
 * Get the name of an absolute axis
 * 
 * If `device` is `NULL`, the returned name will
 * have a prefix , that does not include an underscore
 * except (mandatorily) as the very last character
 * in the prefix, however if the axis is valid but
 * does not have a name a numerical representation
 * is given (currently "0x" prefixed upper case
 * hexadecimal); there is currently only one prefix:
 * "ABS_"
 * 
 * @param   device  If `NULL`, a generic name will be returned,
 *                  otherwise the canonical name for the axis
 *                  on the specific device will be returned
 *                  (`NULL` if not information is unavailable)
 * @param   code    The axis
 * @return          The axis' name, `NULL` if not found
 */
LIBGAMEPAD_PURE__ const char *libgamepad_get_absolute_axis_name(const struct libgamepad_device *, uint16_t);

/**
 * Get the name of a relative axis
 * 
 * If `device` is `NULL`, the returned name will
 * have a prefix , that does not include an underscore
 * except (mandatorily) as the very last character
 * in the prefix, however if the axis is valid but
 * does not have a name a numerical representation
 * is given (currently "0x" prefixed upper case
 * hexadecimal); there is currently only one prefix:
 * "REL_"
 * 
 * @param   device  If `NULL`, a generic name will be returned,
 *                  otherwise the canonical name for the axis
 *                  on the specific device will be returned
 *                  (`NULL` if not information is unavailable)
 * @param   code    The axis
 * @return          The axis' name, `NULL` if not found
 */
LIBGAMEPAD_PURE__ const char *libgamepad_get_relative_axis_name(const struct libgamepad_device *, uint16_t);

/**
 * Get a button/key code from it's name
 * 
 * @param   name  The button/key's name or textual
 *                representation of its code number
 * @return        The button/key, -1 if not found
 */
int16_t libgamepad_get_button_by_name(const char *);

/**
 * Get an absolute axis code from it's name
 * 
 * @param   name  The absolute axis' name or textual
 *                representation of its code number
 * @return        The absolute axis, -1 if not found
 */
int16_t libgamepad_get_absolute_axis_by_name(const char *);

/**
 * Get a relative axis code from it's name
 * 
 * @param   name  The relative axis' name or textual
 *                representation of its code number
 * @return        The relative axis, -1 if not found
 */
int16_t libgamepad_get_relative_axis_by_name(const char *);


/**
 * Get whether a device has a specific button or key
 * 
 * @param   device  The device
 * @param   code    The button/key
 * @return          1 if the button/key is reported as present, 0 otherwise
 */
inline int
libgamepad_get_device_has_button(const struct libgamepad_device *device, uint16_t code)
{
	return code < sizeof(device->button_map) / sizeof(*device->button_map) &&
	       device->button_map[code] >= 0;
}

/**
 * Get whether a device has a specific absolute axis
 * 
 * @param   device  The device
 * @param   code    The axis
 * @return          1 if the axis is reported as present, 0 otherwise
 */
inline int
libgamepad_get_device_has_absolute_axis(const struct libgamepad_device *device, uint16_t code)
{
	return code < sizeof(device->absolute_axis_map) / sizeof(*device->absolute_axis_map) &&
	       device->absolute_axis_map[code] >= 0;
}

/**
 * Get whether a device has a specific relative axis
 * 
 * @param   device  The device
 * @param   code    The axis
 * @return          1 if the axis is reported as present, 0 otherwise
 */
inline int
libgamepad_get_device_has_relative_axis(const struct libgamepad_device *device, uint16_t code)
{
	return code < sizeof(device->relative_axis_map) / sizeof(*device->relative_axis_map) &&
	       device->relative_axis_map[code] >= 0;
}

/**
 * Get whether a device has a specific force feedback effect
 * 
 * @param   device  The device
 * @param   code    The force feedback effect or waveform
 * @return          1 if the axis is reported as present, 0 otherwise
 */
inline int
libgamepad_get_device_has_ff_effect(const struct libgamepad_device *device, uint16_t code)
{
	return code < sizeof(device->force_feedback_support) * 8 &&
	       (1 & (device->force_feedback_support[code / (8 * sizeof(*device->force_feedback_support))] >>
	             (code % (8 * sizeof(*device->force_feedback_support)))));
}


/**
 * Get whether a button/key is pressed down or not
 * 
 * libgamepad caches the last read button/key state,
 * and will return the cached .state The underlaying
 * ioctl(3) will has the ability to read a state
 * even when the device is grabbed, but even if the
 * device is not grabbed, the state may be out of
 * that. Because the state may be out of date,
 * libgamepad reads the current state when a device
 * is opened.
 * 
 * This information is not necessarily up to date
 * before the first `LIBGAMEPAD_EVENT_END` has been
 * reported
 * 
 * @param   device  The device to retrieve the information for
 * @param   code    The button/key
 * @return          1 if the button/key is pressed down,
 *                  0 otherwise
 */
int libgamepad_get_button_is_pressed(struct libgamepad_device *, uint16_t);

/**
 * Get information about an absolute axis
 * 
 * libgamepad caches the last read axis value, and
 * will return the cached value. The underlaying
 * ioctl(3) will has the ability to read a value
 * even when the device is grabbed, but even if the
 * device is not grabbed, the value may be out of
 * that. Because the value may be out of date,
 * libgamepad reads the current state when a device
 * is opened.
 * 
 * The value of `.value` in the returned pointer is
 * not necessarily up to date before the first
 * `LIBGAMEPAD_EVENT_END` has been reported
 * 
 * @param   device  The device to retrieve the information for
 * @param   code    The axis
 * @return          Information about the axis; this is a pointer
 *                  stored inside `device`, some function calls
 *                  may alter the contents of invalidate the pointer,
 *                  `libgamepad_close_device(device)` will always
 *                  invalidate the pointer and it is expected that
 *                  `libgamepad_next_event` often alters `.value`
 */
LIBGAMEPAD_PURE__ /* `struct input_absinfo` is defined in <linux/input.h> */
const struct input_absinfo *libgamepad_get_absolute_axis_info(struct libgamepad_device *, uint16_t);


/**
 * Grab a subdevice, if not already grabbed
 *
 * This is supposed to block out clients from
 * retrieving events for the device, but it
 * does not necessarily stop them from reading
 * the device state
 * 
 * @param   device  The device to grab
 * @return          0 on success, -1 on failure
 * 
 * In the event that the device is already grabbed by
 * via another open file descriptor (duplicates share
 * grab), this function will return -1 and set `errno`
 * to `EBUSY`
 */
inline int
libgamepad_grab(struct libgamepad_device *device)
{
	return ioctl(device->fd, EVIOCGRAB, (void *)1);
}

/**
 * Ungrab a subdevice, unless not currently grabbed
 * 
 * @param   device  The device to ungrab
 * @return          0 on success, -1 on failure
 */
inline int
libgamepad_ungrab(struct libgamepad_device *device)
{
	return ioctl(device->fd, EVIOCGRAB, (void *)0);
}


/**
 * Set the clock that event timestamps shall be reported in
 * 
 * If your application depends on new events using the
 * selected clock (if any at all), call `libgamepad_drain_events`
 * after this function
 * 
 * Support clocks are (in Linux 5.18):
 * - CLOCK_REALTIME
 * - CLOCK_MONOTONIC
 * - CLOCK_BOOTTIME
 * As usual, CLOCK_MONOTONIC_RAW is not supported
 * 
 * @param   device   The device to configure
 * @param   clockid  The clock to use on event timestamps
 * @return           0 on success, -1 on failure
 */
inline int
libgamepad_set_clock(struct libgamepad_device *device, clockid_t clockid)
{
	return ioctl(device->fd, EVIOCSCLOCKID, &clockid);
}


/**
 * Install a force feedback effect that can be played back later
 * 
 * @param   device  The device to configure
 * @param   effect  The effect to install; will be edited, specifically the ID
 *                  will be set (need not be set before calling this function)
 * @return          0 on success, -1 on failure
 */
inline int /* `struct ff_effect` is defined in <linux/input.h> */
libgamepad_install_force_feedback_effect(struct libgamepad_device *device, struct ff_effect *effect)
{
	effect->id = -1;
	return ioctl(device->fd, EVIOCSFF, effect);
}

/**
 * Reconfigure an already installed force feedback effect
 * 
 * @param   device  The device to configure
 * @param   effect  The effect to update, the ID must be set to the ID of a
 *                  previously installed effect
 * @return          0 on success, -1 on failure
 */
inline int
libgamepad_update_force_feedback_effect(struct libgamepad_device *device, struct ff_effect *effect)
{
	return ioctl(device->fd, EVIOCSFF, effect);
}

/**
 * Uninstall an installed force feedback effect
 * 
 * @param   device  The device to configure
 * @param   effect  The effect to remove, the ID must be set to the ID of a
 *                  previously installed effect and will be reset
 * @return          0 on success, -1 on failure
 */
inline int
libgamepad_uninstall_force_feedback_effect(struct libgamepad_device *device, struct ff_effect *effect)
{
	int ret = ioctl(device->fd, EVIOCRMFF, effect);
	effect->id = -1;
	return ret;
}

/**
 * Get the number of force feedback effects that can be played concurrently
 * 
 * @param   device  The device to configure
 * @return          The number of maximum number concurrent effects, -1 on failure
 */
inline int
libgamepad_get_force_feedback_max_concurrency(struct libgamepad_device *device)
{
	int num;
	return ioctl(device->fd, EVIOCGEFFECTS, &num) ? -1 : num;
}

/**
 * Play a force feedback effect
 * 
 * @param   device  The device to play the force feedback effect on
 * @param   effect  The force feedback effect to play, must already be installed
 *                  with `libgamepad_install_force_feedback_effect`
 * @return          0 on success, -1 on failure
 */
inline int
libgamepad_play_force_feedback_effect(struct libgamepad_device *device, const struct ff_effect *effect)
{
	struct input_event message;
	message.type = EV_FF;
	message.code = (uint16_t)effect->id;
	message.value = 1;
	return write(device->fd, &message, sizeof(message)) < 0 ? -1 : 0;
}

/**
 * Stop playing a force feedback effect
 * 
 * @param   device  The device playing the force feedback effect on
 * @param   effect  The force feedback effect to play, must already be installed
 *                  with `libgamepad_install_force_feedback_effect`
 * @return          0 on success, -1 on failure
 */
inline int
libgamepad_stop_force_feedback_effect(struct libgamepad_device *device, const struct ff_effect *effect)
{
	struct input_event message;
	message.type = EV_FF;
	message.code = (uint16_t)effect->id;
	message.value = 0;
	return write(device->fd, &message, sizeof(message)) < 0 ? -1 : 0;
}

/**
 * Check if a force feedback effect or waveform is supported
 *
 * @param   device  The device to check force feedback support on
 * @param   effect  The force feedback effect or waveform
 * @return          1 if the effect/waveform is supported, 0 otherwise
 */
inline int
libgamepad_is_force_feedback_effect_supported(struct libgamepad_device *device, uint16_t effect)
{
	return effect < sizeof(device->force_feedback_support) * 8 &&
	       ((device->force_feedback_support[effect / 8] >> (effect % 8)) & 1);
}

/**
 * Set the force feedback master gain on a device
 * 
 * Requires support for `FF_GAIN` (check with
 * `libgamepad_is_force_feedback_effect_supported`)
 * 
 * @param   device  The device to configure
 * @param   gain    The master gain, shall be a value in [0, 0xFFFF]
 * @return          0 on success, -1 on failure
 */
inline int
libgamepad_set_force_feedback_master_gain(struct libgamepad_device *device, uint16_t gain)
{
	struct input_event message;
	message.type = EV_FF;
	message.code = FF_GAIN;
	message.value = (int16_t)gain;
	return write(device->fd, &message, sizeof(message)) < 0 ? -1 : 0;
}

/**
 * Set the autocenter force feedback on a device
 * 
 * Requires support for `FF_AUTOCENTER` (check with
 * `libgamepad_is_force_feedback_effect_supported`)
 * 
 * @param   device      The device to configure
 * @param   autocenter  Autocenter strength, shall be a value in [0, 0xFFFF], select 0 to
 *                      disable (you can also use `libgamepad_disable_force_feedback_autocenter`)
 * @return              0 on success, -1 on failure
 */
inline int
libgamepad_set_force_feedback_autocenter(struct libgamepad_device *device, uint16_t autocenter)
{
	struct input_event message;
	message.type = EV_FF;
	message.code = FF_AUTOCENTER;
	message.value = (int16_t)autocenter;
	return write(device->fd, &message, sizeof(message)) < 0 ? -1 : 0;
}

/**
 * Disable the autocenter force feedback on a device
 * 
 * Requires support for `FF_AUTOCENTER` (check with
 * `libgamepad_is_force_feedback_effect_supported`)
 * 
 * @param   device  device to configure
 * @return          0 on success, -1 on failure
 */
inline int
libgamepad_disable_force_feedback_autocenter(struct libgamepad_device *device)
{
	struct input_event message;
	message.type = EV_FF;
	message.code = FF_AUTOCENTER;
	message.value = 0;
	return write(device->fd, &message, sizeof(message)) < 0 ? -1 : 0;
}

/**
 * Construct a rumble force-feedback effect
 * 
 * Requires support for `FF_RUMBLE` (check with
 * `libgamepad_is_force_feedback_effect_supported`)
 * 
 * This function will set the following values to
 * zero if `from` is `NULL`, see <linux/input.h>
 * for more details:
 * -  effectp->trigger.button
 * -  effectp->trigger.interval
 * -  effectp->replay.delay
 * 
 * @param  effectp    Output parameter for the effect
 * @param  from       Effect to reconfigure, `NULL` to create a new effect
 * @param  direction  The direction of the force feedback (if directional), where
 *                    values are measure clockwise and both 0 and 1 are upwards
 * @param  length     The number of milliseconds the effect is played
 * @param  effect     Effect specific details:
 *                    - .strong_magnitude  Heavy motor strength, unsigned 16-bit integer
 *                    - .weak_magnitude    Light motor strength, unsigned 16-bit integer
 */
inline void
libgamepad_construct_rumble_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
                                                  double direction, uint16_t length, const struct ff_rumble_effect *effect)
{
	libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_RUMBLE);
	memcpy(&effectp->u.rumble, effect, sizeof(effectp->u.rumble));
}

/**
 * Construct a constant force-feedback effect
 * 
 * Requires support for `FF_CONSTANT` (check with
 * `libgamepad_is_force_feedback_effect_supported`)
 * 
 * This function will set the following values to
 * zero if `from` is `NULL`, see <linux/input.h>
 * for more details:
 * -  effectp->trigger.button
 * -  effectp->trigger.interval
 * -  effectp->replay.delay
 * 
 * @param  effectp    Output parameter for the effect
 * @param  from       Effect to reconfigure, `NULL` to create a new effect
 * @param  direction  The direction of the force feedback (if directional), where
 *                    values are measure clockwise and both 0 and 1 are upwards
 * @param  length     The number of milliseconds the effect is played
 * @param  effect     Effect specific details:
 *                    - .level                     Strength, signed 16-bit integer
 *                    - .envelope.attack_strength  Strength at beginning, unsigned 16-bit integer
 *                    - .envelope.fade_strength    Strength at end, unsigned 16-bit integer
 *                    - .envelope.attack_length    The number of milliseconds the strength shall
 *                                                 transition from `.envelope.attack_strength`
 *                                                 to `.level`, unsigned 16-bit integer
 *                    - .envelope.fade_length      The number of milliseconds the strength shall
 *                                                 transition to `.envelope.fade_strength`
 *                                                 from `.level`, unsigned 16-bit integer
 */
inline void
libgamepad_construct_constant_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
                                                    double direction, uint16_t length, const struct ff_constant_effect *effect)
{
	libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_CONSTANT);
	memcpy(&effectp->u.constant, effect, sizeof(effectp->u.constant));
}

/**
 * Construct a ramp force-feedback effect
 * 
 * Requires support for `FF_RAMP` (check with
 * `libgamepad_is_force_feedback_effect_supported`)
 * 
 * This function will set the following values to
 * zero if `from` is `NULL`, see <linux/input.h>
 * for more details:
 * -  effectp->trigger.button
 * -  effectp->trigger.interval
 * -  effectp->replay.delay
 * 
 * @param  effectp    Output parameter for the effect
 * @param  from       Effect to reconfigure, `NULL` to create a new effect
 * @param  direction  The direction of the force feedback (if directional), where
 *                    values are measure clockwise and both 0 and 1 are upwards
 * @param  length     The number of milliseconds the effect is played
 * @param  effect     Effect specific details:
 *                    - .start_level               Strength at beginning, signed 16-bit integer
 *                    - .end_level                 Strength at end, signed 16-bit integer
 *                    - .envelope.attack_strength  Strength at beginning, unsigned 16-bit integer
 *                    - .envelope.fade_strength    Strength at end, unsigned 16-bit integer
 *                    - .envelope.attack_length    The number of milliseconds the strength shall
 *                                                 transition from `.envelope.attack_strength`,
 *                                                 unsigned 16-bit integer
 *                    - .envelope.fade_length      The number of milliseconds the strength shall
 *                                                 transition to `.envelope.fade_strength`,
 *                                                 unsigned 16-bit integer
 */
inline void
libgamepad_construct_ramp_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
                                                double direction, uint16_t length, const struct ff_ramp_effect *effect)
{
	libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_RUMBLE);
	memcpy(&effectp->u.ramp, effect, sizeof(effectp->u.ramp));
}

/**
 * Construct a periodic force-feedback effect
 * 
 * Requires support for `FF_PERIODIC` and the selected waveform
 * (check with `libgamepad_is_force_feedback_effect_supported`)
 * 
 * This function will set the following values to
 * zero if `from` is `NULL`, see <linux/input.h>
 * for more details:
 * -  effectp->trigger.button
 * -  effectp->trigger.interval
 * -  effectp->replay.delay
 * 
 * @param  effectp    Output parameter for the effect
 * @param  from       Effect to reconfigure, `NULL` to create a new effect
 * @param  direction  The direction of the force feedback (if directional), where
 *                    values are measure clockwise and both 0 and 1 are upwards
 * @param  length     The number of milliseconds the effect is played
 * @param  effect     Effect specific details:
 *                    - .waveform                  `FF_SQUARE`, `FF_TRIANGLE`, `FF_SINE`, `FF_SAW_UP`,
 *                                                 `FF_SAW_DOWN`, or `FF_CUSTOM`
 *                    - .period                    The period of the wave, in milliseconds (unsigned 16-bit integer)
 *                    - .magnitude                 The peak value, signed 16-bit integer
 *                    - .offset                    The rough mean value, signed 16-bit integer
 *                    - .phase                     Horiztonal wave-shift, in milliseconds (unsigned 16-bit integer)
 *                    - .envelope.attack_strength  Strength at beginning, unsigned 16-bit integer
 *                    - .envelope.fade_strength    Strength at end, unsigned 16-bit integer
 *                    - .envelope.attack_length    The number of milliseconds the strength shall
 *                                                 transition from `.envelope.attack_strength`,
 *                                                 unsigned 16-bit integer
 *                    - .envelope.fade_length      The number of milliseconds the strength shall
 *                                                 transition to `.envelope.fade_strength`,
 *                                                 unsigned 16-bit integer
 *                    - .custom_len                Set to zero unless you are using (`FF_CUSTOM`)
 *                    - .custom_data               Set to `NULL` unless you are using (`FF_CUSTOM`)
 */
inline void
libgamepad_construct_periodic_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
                                                   double direction, uint16_t length, const struct ff_periodic_effect *effect)
{
	libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_PERIODIC);
	memcpy(&effectp->u.periodic, effect, sizeof(effectp->u.periodic));
}

/**
 * Construct a spring force-feedback effect
 * 
 * Requires support for `FF_SPRING` (check with
 * `libgamepad_is_force_feedback_effect_supported`)
 * 
 * This function will set the following values to
 * zero if `from` is `NULL`, see <linux/input.h>
 * for more details:
 * -  effectp->trigger.button
 * -  effectp->trigger.interval
 * -  effectp->replay.delay
 * 
 * @param  effectp    Output parameter for the effect
 * @param  from       Effect to reconfigure, `NULL` to create a new effect
 * @param  direction  The direction of the force feedback (if directional), where
 *                    values are measure clockwise and both 0 and 1 are upwards
 * @param  length     The number of milliseconds the effect is played
 * @param  effect     Effect specific details (one of each axis):
 *                    - .right_saturation  Maximum level at end of axis, unsigned 16-bit integer
 *                    - .left_saturation   Maximum level at beginning of axis, unsigned 16-bit integer
 *                    - .right_coeff       Growth coefficient after the dead zone, signed 16-bit integer
 *                    - .left_coeff        Growth coefficient before the dead zone, signed 16-bit integer
 *                    - .deadband          Size of the dead zone
 *                    - .center            The position of the center of the dead zone
 */
inline void
libgamepad_construct_spring_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
                                                  double direction, uint16_t length, const struct ff_condition_effect effect[2])
{
	libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_SPRING);
	memcpy(effectp->u.condition, effect, sizeof(effectp->u.condition));
}

/**
 * Construct a friction force-feedback effect
 * 
 * Requires support for `FF_FRICTION` (check with
 * `libgamepad_is_force_feedback_effect_supported`)
 * 
 * This function will set the following values to
 * zero if `from` is `NULL`, see <linux/input.h>
 * for more details:
 * -  effectp->trigger.button
 * -  effectp->trigger.interval
 * -  effectp->replay.delay
 * 
 * @param  effectp    Output parameter for the effect
 * @param  from       Effect to reconfigure, `NULL` to create a new effect
 * @param  direction  The direction of the force feedback (if directional), where
 *                    values are measure clockwise and both 0 and 1 are upwards
 * @param  length     The number of milliseconds the effect is played
 * @param  effect     Effect specific details (one of each axis):
 *                    - .right_saturation  Maximum level at end of axis, unsigned 16-bit integer
 *                    - .left_saturation   Maximum level at beginning of axis, unsigned 16-bit integer
 *                    - .right_coeff       Growth coefficient after the dead zone, signed 16-bit integer
 *                    - .left_coeff        Growth coefficient before the dead zone, signed 16-bit integer
 *                    - .deadband          Size of the dead zone
 *                    - .center            The position of the center of the dead zone
 */
inline void
libgamepad_construct_friction_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
                                                    double direction, uint16_t length, const struct ff_condition_effect effect[2])
{
	libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_FRICTION);
	memcpy(effectp->u.condition, effect, sizeof(effectp->u.condition));
}

/**
 * Construct a damper force-feedback effect
 * 
 * Requires support for `FF_DAMPER` (check with
 * `libgamepad_is_force_feedback_effect_supported`)
 * 
 * This function will set the following values to
 * zero if `from` is `NULL`, see <linux/input.h>
 * for more details:
 * -  effectp->trigger.button
 * -  effectp->trigger.interval
 * -  effectp->replay.delay
 * 
 * @param  effectp    Output parameter for the effect
 * @param  from       Effect to reconfigure, `NULL` to create a new effect
 * @param  direction  The direction of the force feedback (if directional), where
 *                    values are measure clockwise and both 0 and 1 are upwards
 * @param  length     The number of milliseconds the effect is played
 * @param  effect     Effect specific details (one of each axis):
 *                    - .right_saturation  Maximum level at end of axis, unsigned 16-bit integer
 *                    - .left_saturation   Maximum level at beginning of axis, unsigned 16-bit integer
 *                    - .right_coeff       Growth coefficient after the dead zone, signed 16-bit integer
 *                    - .left_coeff        Growth coefficient before the dead zone, signed 16-bit integer
 *                    - .deadband          Size of the dead zone
 *                    - .center            The position of the center of the dead zone
 */
inline void
libgamepad_construct_damper_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
                                                  double direction, uint16_t length, const struct ff_condition_effect effect[2])
{
	libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_DAMPER);
	memcpy(effectp->u.condition, effect, sizeof(effectp->u.condition));
}

/**
 * Construct a inertia force-feedback effect
 * 
 * Requires support for `FF_INERTIA` (check with
 * `libgamepad_is_force_feedback_effect_supported`)
 * 
 * This function will set the following values to
 * zero if `from` is `NULL`, see <linux/input.h>
 * for more details:
 * -  effectp->trigger.button
 * -  effectp->trigger.interval
 * -  effectp->replay.delay
 * 
 * @param  effectp    Output parameter for the effect
 * @param  from       Effect to reconfigure, `NULL` to create a new effect
 * @param  direction  The direction of the force feedback (if directional), where
 *                    values are measure clockwise and both 0 and 1 are upwards
 * @param  length     The number of milliseconds the effect is played
 * @param  effect     Effect specific details (one of each axis):
 *                    - .right_saturation  Maximum level at end of axis, unsigned 16-bit integer
 *                    - .left_saturation   Maximum level at beginning of axis, unsigned 16-bit integer
 *                    - .right_coeff       Growth coefficient after the dead zone, signed 16-bit integer
 *                    - .left_coeff        Growth coefficient before the dead zone, signed 16-bit integer
 *                    - .deadband          Size of the dead zone
 *                    - .center            The position of the center of the dead zone
 */
inline void
libgamepad_construct_inertia_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
                                                   double direction, uint16_t length, const struct ff_condition_effect effect[2])
{
	libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_INERTIA);
	memcpy(effectp->u.condition, effect, sizeof(effectp->u.condition));
}


#endif