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