1// tControllerSystem.h 
2// 
3// This file implements the main API for the input system. It manages all attached controllers. 
4// 
5// Copyright (c) 2025 Tristan Grimmer. 
6// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
7// granted, provided that the above copyright notice and this permission notice appear in all copies. 
8// 
9// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
10// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
11// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
12// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
13// PERFORMANCE OF THIS SOFTWARE. 
14 
15#pragma once 
16#include <mutex> 
17#include <thread> 
18#include <condition_variable> 
19#include <vector> 
20#include <Foundation/tStandard.h> 
21#include <Input/tContGamepad.h> 
22namespace tInput 
23
24 
25 
26class tControllerSystem 
27
28public
29 // PollingPeriod is in nanoseconds. A value of 0 means auto-detect the polling period by inspecting the vendor and 
30 // prodict ID of any controller that is plugged in. For example the polling rate of an 8BitDo Ultimate 2 Wireless is 
31 // 1000Hz so it will set the pollingPeriod to 1000ns (1ms -> 1000Hz, 2000Hz -> 500ns). Note that there is a separate 
32 // polling thread for each controller. In auto-mode the polling rate may be different for each controller. If 
33 // controller details can't be determined or pollingPeriod is 0, 8000ns (125Hz) is used. This is the XBoxOne 
34 // controller (gamepad) polling rate. PollingControllerDetectionPeriod is in milliseconds. A value of 0 means use 
35 // the default 1000ms period. 
36 tControllerSystem(int pollingPeriod_ns = 0, int pollingControllerDetectionPeriod_ms = 0); 
37 virtual ~tControllerSystem(); 
38 
39 // Call this periodically from the main thread loop. When this is called any callbacks are executed and all 
40 // controller state is updated. 
41 void Update(); 
42 
43 tContGamepad& GetGetpad(tGamepadID gid) { return Gamepads[int(gid)]; } 
44 
45private
46 // This function runs on a different thread. 
47 void Detect(); 
48 
49 // This mutex protects PollExitRequested, the gamepad Connected state variable, and all tUnit members in the 
50 // components of the gamepads -- ditto for other controller types when we get around to implementing them. 
51 std::mutex Mutex
52 
53 int PollingPeriod_ns = 0; // In nanoseconds. 
54 int DetectPeriod_ms = 0; // In milliseconds. 
55 
56 // To simplify the implementation we are going to support up to precisely 4 gamepads. This matches the maximum 
57 // supported by xinput on windows and restricts the number of gamepads on Linux to 4, which seems perfectly 
58 // reasonable. By simply having an array of gamepads that are always present it also makes reading controller values 
59 // a simple process. Just loop through the controllers and ignore any that are in the disconnected state. 
60 // Parts of tContGamepad mutex protected: the connected bool and tUnit values in the components. 
61 std::vector<tContGamepad> Gamepads
62 
63 // The DetectExitRequested predicate is required to avoid spurious wakeups. Mutex protected. 
64 bool DetectExitRequested = false
65 std::condition_variable DetectExitCondition
66 std::thread DetectThread
67}; 
68 
69 
70
71