]> git.tdb.fi Git - libs/gui.git/blob - source/input/linux/gamecontroller.cpp
Sort game controller devices for consistency
[libs/gui.git] / source / input / linux / gamecontroller.cpp
1 #include <fcntl.h>
2 #include <linux/joystick.h>
3 #include <msp/core/systemerror.h>
4 #include <msp/fs/dir.h>
5 #include <msp/io/handle_private.h>
6 #include <msp/strings/format.h>
7 #include "gamecontroller.h"
8 #include "gamecontroller_platform.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace Input {
14
15 vector<string> GameController::Private::detected_controllers;
16
17 GameController::GameController(unsigned index):
18         event_disp(0)
19 {
20         if(!detect_done)
21                 detect();
22         if(index>=Private::detected_controllers.size())
23                 throw device_not_available(format("GameController(%d)", index));
24
25         JsDevice *device = new JsDevice(Private::detected_controllers[index]);
26
27         priv = new Private;
28         priv->dev = device;
29         priv->dev->signal_data_available.connect(sigc::mem_fun(this, static_cast<void (GameController::*)()>(&GameController::tick)));
30         name = priv->dev->get_name();
31         tick(Time::zero);
32 }
33
34 GameController::~GameController()
35 {
36         delete priv->dev;
37         delete priv;
38 }
39
40 unsigned GameController::detect()
41 {
42         Private::detected_controllers.clear();
43
44         FS::Path dev_input = "/dev/input";
45         list<string> devices = FS::list_filtered(dev_input, "^js[0-9]+");
46         devices.sort();
47         for(list<string>::const_iterator i=devices.begin(); i!=devices.end(); ++i)
48                 // TODO check permissions
49                 Private::detected_controllers.push_back((dev_input / *i).str());
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->dev);
61         event_disp = ed;
62         if(event_disp)
63                 event_disp->add(*priv->dev);
64 }
65
66 void GameController::tick()
67 {
68         js_event events[16];
69         bool first = true;
70         while(1)
71         {
72                 if(!first && !IO::poll(*priv->dev, IO::P_INPUT, Time::zero))
73                         break;
74
75                 first = false;
76                 unsigned len = priv->dev->read(reinterpret_cast<char *>(events), sizeof(events));
77
78                 unsigned count = len/sizeof(js_event);
79                 for(unsigned i=0; i<count; ++i)
80                 {
81                         unsigned type = events[i].type&0x7F;
82                         bool init = events[i].type&JS_EVENT_INIT;
83                         if(type==JS_EVENT_AXIS)
84                                 set_axis_value(events[i].number, events[i].value/32768.0f, !init);
85                         else if(type==JS_EVENT_BUTTON)
86                                 set_button_state(events[i].number, events[i].value, !init);
87                 }
88
89                 if(len<sizeof(events))
90                         break;
91         }
92 }
93
94
95 void GameController::tick(const Time::TimeDelta &timeout)
96 {
97         if(IO::poll(*priv->dev, IO::P_INPUT, timeout))
98                 tick();
99 }
100
101
102 JsDevice::JsDevice(const string &fn)
103 {
104         mode = IO::M_READ;
105         *handle = open(fn.c_str(), O_RDONLY);
106         if(!handle)
107                 throw system_error(format("open(%s)", fn));
108         set_events(IO::P_INPUT);
109 }
110
111 JsDevice::~JsDevice()
112 {
113         sys_close(handle);
114 }
115
116 string JsDevice::get_name() const
117 {
118         char buf[128] = { 0 };
119         int ret = ioctl(*handle, JSIOCGNAME(sizeof(buf)), buf);
120         if(ret<0)
121                 throw system_error("ioctl(JSIOCGNAME)");
122         return buf;
123 }
124
125 unsigned JsDevice::do_read(char *buf, unsigned size)
126 {
127         return IO::sys_read(handle, buf, size);
128 }
129
130 unsigned JsDevice::do_write(const char *, unsigned)
131 {
132         throw IO::invalid_access(IO::M_WRITE);
133 }
134
135 } // namespace Input
136 } // namespace Msp