]> git.tdb.fi Git - libs/gui.git/blob - source/input/linux/gamecontroller.cpp
Don't leak memory if JsDevice constructor throws
[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/io/handle_private.h>
5 #include <msp/strings/format.h>
6 #include "gamecontroller.h"
7 #include "gamecontroller_platform.h"
8
9 using namespace std;
10
11 namespace Msp {
12 namespace Input {
13
14 GameController::GameController(unsigned index):
15         event_disp(0)
16 {
17         JsDevice *device = new JsDevice(format("/dev/input/js%d", index));
18
19         priv = new Private;
20         priv->dev = device;
21         priv->dev->signal_data_available.connect(sigc::mem_fun(this, static_cast<void (GameController::*)()>(&GameController::tick)));
22         name = priv->dev->get_name();
23         tick(Time::zero);
24 }
25
26 GameController::~GameController()
27 {
28         delete priv->dev;
29         delete priv;
30 }
31
32 void GameController::use_event_dispatcher(IO::EventDispatcher *ed)
33 {
34         if(event_disp)
35                 event_disp->remove(*priv->dev);
36         event_disp = ed;
37         if(event_disp)
38                 event_disp->add(*priv->dev);
39 }
40
41 void GameController::tick()
42 {
43         js_event events[16];
44         bool first = true;
45         while(1)
46         {
47                 if(!first && !IO::poll(*priv->dev, IO::P_INPUT, Time::zero))
48                         break;
49
50                 first = false;
51                 unsigned len = priv->dev->read(reinterpret_cast<char *>(events), sizeof(events));
52
53                 unsigned count = len/sizeof(js_event);
54                 for(unsigned i=0; i<count; ++i)
55                 {
56                         unsigned type = events[i].type&0x7F;
57                         bool init = events[i].type&JS_EVENT_INIT;
58                         if(type==JS_EVENT_AXIS)
59                                 set_axis_value(events[i].number, events[i].value/32768.0f, !init);
60                         else if(type==JS_EVENT_BUTTON)
61                                 set_button_state(events[i].number, events[i].value, !init);
62                 }
63
64                 if(len<sizeof(events))
65                         break;
66         }
67 }
68
69
70 void GameController::tick(const Time::TimeDelta &timeout)
71 {
72         if(IO::poll(*priv->dev, IO::P_INPUT, timeout))
73                 tick();
74 }
75
76
77 JsDevice::JsDevice(const string &fn)
78 {
79         mode = IO::M_READ;
80         *handle = open(fn.c_str(), O_RDONLY);
81         if(!handle)
82                 throw system_error(format("open(%s)", fn));
83         set_events(IO::P_INPUT);
84 }
85
86 JsDevice::~JsDevice()
87 {
88         sys_close(handle);
89 }
90
91 string JsDevice::get_name() const
92 {
93         char buf[128] = { 0 };
94         int ret = ioctl(*handle, JSIOCGNAME(sizeof(buf)), buf);
95         if(ret<0)
96                 throw system_error("ioctl(JSIOCGNAME)");
97         return buf;
98 }
99
100 unsigned JsDevice::do_read(char *buf, unsigned size)
101 {
102         return IO::sys_read(handle, buf, size);
103 }
104
105 unsigned JsDevice::do_write(const char *, unsigned)
106 {
107         throw IO::invalid_access(IO::M_WRITE);
108 }
109
110 } // namespace Input
111 } // namespace Msp