]> git.tdb.fi Git - libs/gui.git/blob - source/input/windows/gamecontroller.cpp
Implement game controllers on Windows through XInput
[libs/gui.git] / source / input / windows / gamecontroller.cpp
1 #include <msp/core/application.h>
2 #include <msp/strings/format.h>
3 #include "gamecontroller.h"
4 #include "gamecontroller_platform.h"
5
6 using namespace std;
7
8 namespace Msp {
9 namespace Input {
10
11 vector<unsigned> GameController::Private::detected_controllers;
12
13 GameController::GameController(unsigned index)
14 {
15         if(!detect_done)
16                 detect();
17         if(index>=Private::detected_controllers.size())
18                 throw device_not_available(format("GameController(%d)", index));
19
20         unsigned user_index = Private::detected_controllers[index];
21
22         XINPUT_STATE state;
23         DWORD err = XInputGetState(user_index, &state);
24         if(err!=ERROR_SUCCESS)
25                 throw device_not_available(format("GameController(%d)", index));
26
27         priv = new Private;
28         priv->index = user_index;
29         priv->update_state(*this, state, false);
30
31         name = format("Controller %d", user_index);
32 }
33
34 GameController::~GameController()
35 {
36         use_event_dispatcher(0);
37
38         delete priv;
39 }
40
41 unsigned GameController::detect()
42 {
43         Private::detected_controllers.clear();
44
45         XINPUT_STATE state;
46         for(unsigned i=0; i<XUSER_MAX_COUNT; ++i)
47                 if(XInputGetState(i, &state)==ERROR_SUCCESS)
48                         Private::detected_controllers.push_back(i);
49
50         detect_done = true;
51         n_detected_controllers = Private::detected_controllers.size();
52
53         return Private::detected_controllers.size();
54 }
55
56 void GameController::use_event_dispatcher(IO::EventDispatcher *ed)
57 {
58         if(event_disp)
59                 event_disp->remove(*priv->event_pipe);
60
61         event_disp = ed;
62         if(event_disp)
63         {
64                 if(!priv->event_pipe)
65                 {
66                         priv->event_pipe = new IO::Pipe;
67                         priv->event_pipe->signal_data_available.connect(sigc::mem_fun(this, static_cast<void (GameController::*)()>(&GameController::tick)));
68                         priv->timer_slot = &GameControllerTimerThread::add_slot();
69                         priv->timer_slot->signal_timeout.connect(sigc::mem_fun(priv, &Private::generate_event));
70                 }
71
72                 event_disp->add(*priv->event_pipe);
73         }
74         else if(priv->event_pipe)
75         {
76                 GameControllerTimerThread::remove_slot(*priv->timer_slot);
77                 priv->timer_slot = 0;
78                 delete priv->event_pipe;
79                 priv->event_pipe = 0;
80         }
81 }
82
83 void GameController::tick()
84 {
85         if(priv->event_pipe)
86         {
87                 char buf[64];
88                 priv->event_pipe->read(buf, sizeof(buf));
89         }
90
91         XINPUT_STATE state;
92         DWORD err = XInputGetState(priv->index, &state);
93         if(err==ERROR_SUCCESS)
94                 priv->update_state(*this, state, true);
95 }
96
97 void GameController::tick(const Time::TimeDelta &)
98 {
99         tick();
100 }
101
102
103 GameController::Private::Private():
104         index(0),
105         last_packet_number(0),
106         event_pipe(0),
107         timer_slot(0)
108 { }
109
110 bool GameController::Private::generate_event()
111 {
112         event_pipe->put(1);
113         return true;
114 }
115
116 void GameController::Private::update_state(GameController &ctrl, const XINPUT_STATE &state, bool event)
117 {
118         if(state.dwPacketNumber==last_packet_number)
119                 return;
120         last_packet_number = state.dwPacketNumber;
121
122         ctrl.set_axis_value(0, state.Gamepad.sThumbLX/32768.0, event);
123         ctrl.set_axis_value(1, -state.Gamepad.sThumbLY/32768.0, event);
124         ctrl.set_axis_value(3, state.Gamepad.sThumbRX/32768.0, event);
125         ctrl.set_axis_value(4, -state.Gamepad.sThumbRY/32768.0, event);
126         WORD dpad_x = state.Gamepad.wButtons&(XINPUT_GAMEPAD_DPAD_LEFT|XINPUT_GAMEPAD_DPAD_RIGHT);
127         WORD dpad_y = state.Gamepad.wButtons&(XINPUT_GAMEPAD_DPAD_UP|XINPUT_GAMEPAD_DPAD_DOWN);
128         ctrl.set_axis_value(6, (dpad_x==XINPUT_GAMEPAD_DPAD_LEFT ? -1 : dpad_x==XINPUT_GAMEPAD_DPAD_RIGHT ? 1 : 0), event);
129         ctrl.set_axis_value(7, (dpad_y==XINPUT_GAMEPAD_DPAD_UP ? -1 : dpad_y==XINPUT_GAMEPAD_DPAD_DOWN ? 1 : 0), event);
130         ctrl.set_button_state(0, state.Gamepad.wButtons&XINPUT_GAMEPAD_A, event);
131         ctrl.set_button_state(1, state.Gamepad.wButtons&XINPUT_GAMEPAD_B, event);
132         ctrl.set_button_state(2, state.Gamepad.wButtons&XINPUT_GAMEPAD_X, event);
133         ctrl.set_button_state(3, state.Gamepad.wButtons&XINPUT_GAMEPAD_Y, event);
134         ctrl.set_button_state(4, state.Gamepad.wButtons&XINPUT_GAMEPAD_LEFT_SHOULDER, event);
135         ctrl.set_button_state(5, state.Gamepad.wButtons&XINPUT_GAMEPAD_RIGHT_SHOULDER, event);
136         ctrl.set_button_state(6, state.Gamepad.wButtons&XINPUT_GAMEPAD_BACK, event);
137         ctrl.set_button_state(7, state.Gamepad.wButtons&XINPUT_GAMEPAD_START, event);
138         ctrl.set_button_state(8, state.Gamepad.wButtons&XINPUT_GAMEPAD_LEFT_THUMB, event);
139         ctrl.set_button_state(9, state.Gamepad.wButtons&XINPUT_GAMEPAD_RIGHT_THUMB, event);
140 }
141
142
143 GameControllerTimerThread *GameControllerTimerThread::thread = 0;
144
145 GameControllerTimerThread::GameControllerTimerThread():
146         Thread("GameController"),
147         n_users(0)
148 {
149         launch();
150 }
151
152 GameControllerTimerThread::~GameControllerTimerThread()
153 {
154         timer.add(Time::zero);
155         join();
156 }
157
158 Time::Timer::Slot &GameControllerTimerThread::add_slot()
159 {
160         if(!thread)
161                 thread = new GameControllerTimerThread;
162         ++thread->n_users;
163         return thread->timer.add(100*Time::msec);
164 }
165
166 void GameControllerTimerThread::remove_slot(Time::Timer::Slot &slot)
167 {
168         thread->timer.cancel(slot);
169         if(!--thread->n_users)
170         {
171                 thread->timer.add(Time::zero);
172                 delete thread;
173                 thread = 0;
174         }
175 }
176
177 void GameControllerTimerThread::main()
178 {
179         while(1)
180         {
181                 timer.tick();
182                 if(!n_users)
183                         break;
184         }
185 }
186
187 } // namespace Input
188 } // namespace Msp