624fde947f678c514cb7ddffa62864cedb73e0f0
[WebKit-https.git] / Source / WebCore / platform / qt / GamepadsQt.cpp
1 /*
2  * Copyright (C) 2012 INdT - Instituto Nokia de Tecnologia
3  * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "Gamepads.h"
29
30 #include "GamepadDeviceLinux.h"
31 #include "GamepadList.h"
32
33 #include <QObject>
34 #include <QSocketNotifier>
35
36 extern "C" {
37     #include <libudev.h>
38 }
39
40 #include <unistd.h>
41 #include <wtf/HashMap.h>
42 #include <wtf/PassOwnPtr.h>
43 #include <wtf/text/CString.h>
44 #include <wtf/text/StringHash.h>
45
46 namespace WebCore {
47
48 class GamepadDeviceLinuxQt : public QObject, public GamepadDeviceLinux {
49     Q_OBJECT
50 public:
51     static PassOwnPtr<GamepadDeviceLinuxQt> create(const String& deviceFile)
52     {
53         return adoptPtr(new GamepadDeviceLinuxQt(deviceFile));
54     }
55     ~GamepadDeviceLinuxQt();
56
57 private:
58     GamepadDeviceLinuxQt(const String&);
59     QSocketNotifier* m_notifier;
60
61 private Q_SLOTS:
62     bool readCallback();
63 };
64
65 GamepadDeviceLinuxQt::GamepadDeviceLinuxQt(const String& deviceFile)
66     : QObject()
67     , GamepadDeviceLinux(deviceFile)
68 {
69     if (m_fileDescriptor == -1)
70         return;
71
72     m_notifier = new QSocketNotifier(m_fileDescriptor, QSocketNotifier::Read, this);
73     connect(m_notifier, SIGNAL(activated(int)), this, SLOT(readCallback()));
74 }
75
76 GamepadDeviceLinuxQt::~GamepadDeviceLinuxQt()
77 {
78 }
79
80 bool GamepadDeviceLinuxQt::readCallback()
81 {
82     js_event event;
83     int len = read(m_fileDescriptor, &event, sizeof(js_event));
84     if (len != sizeof(event))
85         return false;
86     updateForEvent(event);
87     return true;
88 }
89
90 class GamepadsQt : public QObject {
91     Q_OBJECT
92 public:
93     GamepadsQt(unsigned);
94
95     void registerDevice(const String&);
96     void unregisterDevice(const String&);
97
98     void updateGamepadList(GamepadList*);
99
100 private Q_SLOTS:
101     void onGamePadChange();
102
103 private:
104     ~GamepadsQt();
105     bool isGamepadDevice(struct udev_device*);
106
107     Vector<OwnPtr<GamepadDeviceLinuxQt> > m_slots;
108     HashMap<String, GamepadDeviceLinuxQt*> m_deviceMap;
109
110     struct udev* m_udev;
111     struct udev_monitor* m_gamepadsMonitor;
112     QSocketNotifier* m_gamepadsNotifier;
113 };
114
115 GamepadsQt::GamepadsQt(unsigned length)
116     : QObject()
117     , m_slots(length)
118 {
119     m_udev = udev_new();
120     m_gamepadsMonitor = udev_monitor_new_from_netlink(m_udev, "udev");
121     udev_monitor_enable_receiving(m_gamepadsMonitor);
122     udev_monitor_filter_add_match_subsystem_devtype(m_gamepadsMonitor, "input", 0);
123     m_gamepadsNotifier = new QSocketNotifier(udev_monitor_get_fd(m_gamepadsMonitor), QSocketNotifier::Read, this);
124     connect(m_gamepadsNotifier, SIGNAL(activated(int)), this, SLOT(onGamePadChange()));
125
126     struct udev_enumerate* enumerate = udev_enumerate_new(m_udev);
127     udev_enumerate_add_match_subsystem(enumerate, "input");
128     udev_enumerate_add_match_property(enumerate, "ID_INPUT_JOYSTICK", "1");
129     udev_enumerate_scan_devices(enumerate);
130     struct udev_list_entry* cur;
131     struct udev_list_entry* devs = udev_enumerate_get_list_entry(enumerate);
132     udev_list_entry_foreach(cur, devs)
133     {
134         const char* devname = udev_list_entry_get_name(cur);
135         struct udev_device* device = udev_device_new_from_syspath(m_udev, devname);
136         if (isGamepadDevice(device))
137             registerDevice(String::fromUTF8(udev_device_get_devnode(device)));
138         udev_device_unref(device);
139     }
140     udev_enumerate_unref(enumerate);
141 }
142
143 GamepadsQt::~GamepadsQt()
144 {
145     udev_unref(m_udev);
146     udev_monitor_unref(m_gamepadsMonitor);
147 }
148
149 bool GamepadsQt::isGamepadDevice(struct udev_device* device)
150 {
151     const char* deviceFile = udev_device_get_devnode(device);
152     const char* sysfsPath = udev_device_get_syspath(device);
153     if (!deviceFile || !sysfsPath)
154         return false;
155     if (!udev_device_get_property_value(device, "ID_INPUT") || !udev_device_get_property_value(device, "ID_INPUT_JOYSTICK"))
156         return false;
157     return QByteArray(deviceFile).startsWith("/dev/input/js");
158 }
159
160 void GamepadsQt::onGamePadChange()
161 {
162     struct udev_device* device = udev_monitor_receive_device(m_gamepadsMonitor);
163     if (!isGamepadDevice(device))
164         return;
165     QByteArray action(udev_device_get_action(device));
166     if (action == "add")
167         registerDevice(udev_device_get_devnode(device));
168     else if (action == "remove")
169         unregisterDevice(udev_device_get_devnode(device));
170 }
171
172 void GamepadsQt::registerDevice(const String& deviceFile)
173 {
174     ASSERT(!m_deviceMap.contains(deviceFile));
175
176     for (unsigned index = 0; index < m_slots.size(); index++) {
177         if (!m_slots[index]) {
178             m_slots[index] = GamepadDeviceLinuxQt::create(deviceFile);
179             m_deviceMap.add(deviceFile, m_slots[index].get());
180             break;
181         }
182     }
183 }
184
185 void GamepadsQt::unregisterDevice(const String& deviceFile)
186 {
187     ASSERT(m_deviceMap.contains(deviceFile));
188
189     GamepadDeviceLinuxQt* gamepadDevice = m_deviceMap.take(deviceFile);
190     unsigned index = m_slots.find(gamepadDevice);
191
192     m_slots[index].clear();
193 }
194
195 void GamepadsQt::updateGamepadList(GamepadList* into)
196 {
197     ASSERT(m_slots.size() == into->length());
198
199     for (unsigned i = 0; i < m_slots.size(); i++) {
200         if (m_slots[i] && m_slots[i]->connected()) {
201             GamepadDeviceLinuxQt* gamepadDevice = m_slots[i].get();
202             RefPtr<Gamepad> gamepad = into->item(i);
203             if (!gamepad)
204                 gamepad = Gamepad::create();
205
206             gamepad->index(i);
207             gamepad->id(gamepadDevice->id());
208             gamepad->timestamp(gamepadDevice->timestamp());
209             gamepad->axes(gamepadDevice->axesCount(), gamepadDevice->axesData());
210             gamepad->buttons(gamepadDevice->buttonsCount(), gamepadDevice->buttonsData());
211
212             into->set(i, gamepad);
213         } else
214             into->set(i, 0);
215     }
216 }
217
218 void sampleGamepads(GamepadList* into)
219 {
220     DEFINE_STATIC_LOCAL(GamepadsQt, gamepadsQt, (into->length()));
221     gamepadsQt.updateGamepadList(into);
222 }
223
224 #include "GamepadsQt.moc"
225
226 } // namespace WebCore