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