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