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 36extern "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 46namespace WebCore { 47 48class GamepadDeviceLinuxQt : public QObject, public GamepadDeviceLinux { 49 Q_OBJECT 50public: 51 static PassOwnPtr<GamepadDeviceLinuxQt> create(const String& deviceFile) 52 { 53 return adoptPtr(new GamepadDeviceLinuxQt(deviceFile)); 54 } 55 ~GamepadDeviceLinuxQt(); 56 57private: 58 GamepadDeviceLinuxQt(const String&); 59 QSocketNotifier* m_notifier; 60 61private Q_SLOTS: 62 bool readCallback(); 63}; 64 65GamepadDeviceLinuxQt::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 76GamepadDeviceLinuxQt::~GamepadDeviceLinuxQt() 77{ 78} 79 80bool 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 90class GamepadsQt : public QObject { 91 Q_OBJECT 92public: 93 GamepadsQt(unsigned); 94 95 void registerDevice(const String&); 96 void unregisterDevice(const String&); 97 98 void updateGamepadList(GamepadList*); 99 100private Q_SLOTS: 101 void onGamePadChange(); 102 103private: 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 115GamepadsQt::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 143GamepadsQt::~GamepadsQt() 144{ 145 udev_unref(m_udev); 146 udev_monitor_unref(m_gamepadsMonitor); 147} 148 149bool 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 160void 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 172void 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 185void 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 195void 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 218void 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 227