+2012-07-01 Christophe Dumez <christophe.dumez@intel.com>
+
+ [EFL] Add Gamepad support
+ https://bugs.webkit.org/show_bug.cgi?id=90170
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Add support for the Gamepad feature on the EFL port.
+
+ The implementation of this class relies on the Linux
+ kernel joystick API.
+
+ Gamepad devices are recognized through the GamepadsEfl
+ class, of which implementation is based on Eeze
+ library. This way devices are properly registered on
+ connection as objects of the GamepadDeviceEfl class
+ which inherits GamepadDeviceLinux. GamepadDeviceEfl
+ reads the joystick data through an Ecore_Fd_Handler
+ and updates the device state accordingly. The
+ GamepadsEfl object is then polled for gamepads data
+ through the sampleGamepads method.
+
+ No new tests - already tested by gamepad/*
+
+ * CMakeLists.txt:
+ * PlatformEfl.cmake:
+ * platform/efl/GamepadsEfl.cpp: Added.
+ (WebCore):
+ (GamepadDeviceEfl):
+ (WebCore::GamepadDeviceEfl::create):
+ (WebCore::GamepadDeviceEfl::GamepadDeviceEfl):
+ (WebCore::GamepadDeviceEfl::~GamepadDeviceEfl):
+ (WebCore::GamepadDeviceEfl::readCallback):
+ (GamepadsEfl):
+ (WebCore::GamepadsEfl::onGamePadChange):
+ (WebCore::GamepadsEfl::GamepadsEfl):
+ (WebCore::GamepadsEfl::~GamepadsEfl):
+ (WebCore::GamepadsEfl::registerDevice):
+ (WebCore::GamepadsEfl::unregisterDevice):
+ (WebCore::GamepadsEfl::updateGamepadList):
+ (WebCore::sampleGamepads):
+
2012-07-01 James Robinson <jamesr@chromium.org>
Unreviewed, rolling out r121635.
--- /dev/null
+/*
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Gamepads.h"
+
+#if ENABLE(GAMEPAD)
+
+#include "GamepadDeviceLinux.h"
+#include "GamepadList.h"
+#include <Ecore.h>
+#include <Eeze.h>
+#include <Eina.h>
+#include <unistd.h>
+#include <wtf/HashMap.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+static const char joystickPrefix[] = "/dev/input/js";
+
+class GamepadDeviceEfl : public GamepadDeviceLinux {
+public:
+ static PassOwnPtr<GamepadDeviceEfl> create(const String& deviceFile)
+ {
+ return adoptPtr(new GamepadDeviceEfl(deviceFile));
+ }
+ ~GamepadDeviceEfl();
+ void resetFdHandler() { m_fdHandler = 0; }
+
+private:
+ GamepadDeviceEfl(const String& deviceFile);
+ static Eina_Bool readCallback(void* userData, Ecore_Fd_Handler*);
+
+ Ecore_Fd_Handler* m_fdHandler;
+};
+
+GamepadDeviceEfl::GamepadDeviceEfl(const String& deviceFile)
+ : GamepadDeviceLinux(deviceFile)
+ , m_fdHandler(0)
+{
+ if (m_fileDescriptor < 0)
+ return;
+
+ m_fdHandler = ecore_main_fd_handler_add(m_fileDescriptor, ECORE_FD_READ, readCallback, this, 0, 0);
+ if (!m_fdHandler)
+ LOG_ERROR("Failed to create the Ecore_Fd_Handler.");
+}
+
+GamepadDeviceEfl::~GamepadDeviceEfl()
+{
+ if (m_fdHandler)
+ ecore_main_fd_handler_del(m_fdHandler);
+}
+
+Eina_Bool GamepadDeviceEfl::readCallback(void* userData, Ecore_Fd_Handler* fdHandler)
+{
+ GamepadDeviceEfl* gamepadDevice = static_cast<GamepadDeviceEfl*>(userData);
+
+ if (ecore_main_fd_handler_active_get(fdHandler, ECORE_FD_ERROR)) {
+ LOG_ERROR("An error occurred while watching the joystick file descriptor, aborting.");
+ gamepadDevice->resetFdHandler();
+ return ECORE_CALLBACK_CANCEL;
+ }
+
+ int fdDevice = ecore_main_fd_handler_fd_get(fdHandler);
+ struct js_event event;
+ const ssize_t len = read(fdDevice, &event, sizeof(event));
+
+ if (len <= 0) {
+ LOG_ERROR("Failed to read joystick file descriptor, aborting.");
+ gamepadDevice->resetFdHandler();
+ return ECORE_CALLBACK_CANCEL;
+ }
+ if (len != sizeof(event)) {
+ LOG_ERROR("Wrong js_event size read on file descriptor, ignoring.");
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ gamepadDevice->updateForEvent(event);
+ return ECORE_CALLBACK_RENEW;
+}
+
+class GamepadsEfl {
+public:
+ GamepadsEfl(size_t length);
+
+ void registerDevice(const String& syspath);
+ void unregisterDevice(const String& syspath);
+
+ void updateGamepadList(GamepadList*);
+
+private:
+ ~GamepadsEfl();
+ static void onGamePadChange(const char* syspath, Eeze_Udev_Event, void* userData, Eeze_Udev_Watch* watcher);
+
+ Vector<OwnPtr<GamepadDeviceEfl> > m_slots;
+ HashMap<String, GamepadDeviceEfl*> m_deviceMap;
+
+ Eeze_Udev_Watch* m_gamepadsWatcher;
+};
+
+void GamepadsEfl::onGamePadChange(const char* syspath, Eeze_Udev_Event event, void* userData, Eeze_Udev_Watch* watcher)
+{
+ GamepadsEfl* gamepadsEfl = static_cast<GamepadsEfl*>(userData);
+
+ switch (event) {
+ case EEZE_UDEV_EVENT_ADD:
+ gamepadsEfl->registerDevice(String::fromUTF8(syspath));
+ break;
+ case EEZE_UDEV_EVENT_REMOVE:
+ gamepadsEfl->unregisterDevice(String::fromUTF8(syspath));
+ break;
+ default:
+ break;
+ }
+}
+
+GamepadsEfl::GamepadsEfl(size_t length)
+ : m_slots(length)
+ , m_gamepadsWatcher(0)
+{
+ if (eeze_init() < 0) {
+ LOG_ERROR("Failed to initialize eeze library.");
+ return;
+ }
+
+ // Watch for gamepads additions / removals.
+ m_gamepadsWatcher = eeze_udev_watch_add(EEZE_UDEV_TYPE_JOYSTICK, (EEZE_UDEV_EVENT_ADD | EEZE_UDEV_EVENT_REMOVE), onGamePadChange, this);
+
+ // List available gamepads.
+ Eina_List* gamepads = eeze_udev_find_by_type(EEZE_UDEV_TYPE_JOYSTICK, 0);
+ void* data;
+ EINA_LIST_FREE(gamepads, data) {
+ char* syspath = static_cast<char*>(data);
+ registerDevice(String::fromUTF8(syspath));
+ eina_stringshare_del(syspath);
+ }
+}
+
+GamepadsEfl::~GamepadsEfl()
+{
+ if (m_gamepadsWatcher)
+ eeze_udev_watch_del(m_gamepadsWatcher);
+ eeze_shutdown();
+}
+
+void GamepadsEfl::registerDevice(const String& syspath)
+{
+ if (m_deviceMap.contains(syspath))
+ return;
+
+ // Make sure it is a valid joystick.
+ const char* deviceFile = eeze_udev_syspath_get_devpath(syspath.utf8().data());
+ if (!deviceFile || !eina_str_has_prefix(deviceFile, joystickPrefix))
+ return;
+
+ LOG(Gamepad, "Registering gamepad at %s, deviceFile: %s", syspath.utf8().data(), deviceFile);
+
+ const size_t slotCount = m_slots.size();
+ for (size_t index = 0; index < slotCount; ++index) {
+ if (!m_slots[index]) {
+ m_slots[index] = GamepadDeviceEfl::create(String::fromUTF8(deviceFile));
+ m_deviceMap.add(syspath, m_slots[index].get());
+ break;
+ }
+ }
+}
+
+void GamepadsEfl::unregisterDevice(const String& syspath)
+{
+ if (!m_deviceMap.contains(syspath))
+ return;
+
+ LOG(Gamepad, "Registering gamepad at %s,", syspath.utf8().data());
+
+ GamepadDeviceEfl* gamepadDevice = m_deviceMap.take(syspath);
+ const size_t index = m_slots.find(gamepadDevice);
+ ASSERT(index != notFound);
+
+ m_slots[index].clear();
+}
+
+void GamepadsEfl::updateGamepadList(GamepadList* into)
+{
+ ASSERT(m_slots.size() == into->length());
+
+ const size_t slotCount = m_slots.size();
+ for (size_t i = 0; i < slotCount; ++i) {
+ if (m_slots[i].get() && m_slots[i]->connected()) {
+ GamepadDeviceEfl* gamepadDevice = m_slots[i].get();
+ RefPtr<Gamepad> gamepad = into->item(i);
+ if (!gamepad)
+ gamepad = Gamepad::create();
+
+ gamepad->index(i);
+ gamepad->id(gamepadDevice->id());
+ gamepad->timestamp(gamepadDevice->timestamp());
+ gamepad->axes(gamepadDevice->axesCount(), gamepadDevice->axesData());
+ gamepad->buttons(gamepadDevice->buttonsCount(), gamepadDevice->buttonsData());
+
+ into->set(i, gamepad);
+ } else
+ into->set(i, 0);
+ }
+}
+
+void sampleGamepads(GamepadList* into)
+{
+ DEFINE_STATIC_LOCAL(GamepadsEfl, gamepadsEfl, (into->length()));
+ gamepadsEfl.updateGamepadList(into);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(GAMEPAD)