From 2721fdf8a94e2e49f907eda24a1406d2225acaf6 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 29 Jun 2015 12:17:35 +1200 Subject: [PATCH 1/7] Linux: Add an evdev based controller backend, to replace SDL. --- CMakeLists.txt | 13 + CMakeTests/FindLibevdev.cmake | 33 +++ CMakeTests/FindLibudev.cmake | 28 ++ Source/Core/InputCommon/CMakeLists.txt | 5 + .../ControllerInterface.cpp | 6 + .../ControllerInterface/ControllerInterface.h | 3 + .../ControllerInterface/evdev/evdev.cpp | 264 ++++++++++++++++++ .../ControllerInterface/evdev/evdev.h | 86 ++++++ 8 files changed, 438 insertions(+) create mode 100644 CMakeTests/FindLibevdev.cmake create mode 100644 CMakeTests/FindLibudev.cmake create mode 100644 Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp create mode 100644 Source/Core/InputCommon/ControllerInterface/evdev/evdev.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 91071aefdb..e64061a6b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -513,6 +513,19 @@ if(USE_EGL) add_definitions(-DUSE_EGL=1) endif() +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + include(FindLibudev OPTIONAL) + include(FindLibevdev OPTIONAL) + if(LIBUDEV_FOUND AND LIBEVDEV_FOUND) + message("libevdev/libudev found, enabling evdev controller backend") + add_definitions(-DHAVE_LIBUDEV=1) + add_definitions(-DHAVE_LIBEVDEV=1) + include_directories(${LIBUDEV_INCLUDE_DIR} ${LIBEVDEV_INCLUDE_DIR}) + else() + message("Could find libevdev/libudev, disabling evdev controller backend") + endif() +endif() + ######################################## # Setup include directories (and make sure they are preferred over the Externals) # diff --git a/CMakeTests/FindLibevdev.cmake b/CMakeTests/FindLibevdev.cmake new file mode 100644 index 0000000000..e89a5f229d --- /dev/null +++ b/CMakeTests/FindLibevdev.cmake @@ -0,0 +1,33 @@ +# - Try to find libevdev +# Once done this will define +# LIBEVDEV_FOUND - System has libevdev +# LIBEVDEV_INCLUDE_DIRS - The libevdev include directories +# LIBEVDEV_LIBRARIES - The libraries needed to use libevdev + +find_package(PkgConfig) +pkg_check_modules(PC_LIBEVDEV QUIET libevdev) + +FIND_PATH( + LIBEVDEV_INCLUDE_DIR libevdev/libevdev.h + HINTS ${PC_LIBEVDEV_INCLUDEDIR} ${PC_LIBEVDEV_INCLUDE_DIRS} + /usr/include + /usr/local/include + ${LIBEVDEV_PATH_INCLUDES} +) + +FIND_LIBRARY( + LIBEVDEV_LIBRARY + NAMES evdev libevdev + HINTS ${PC_LIBEVDEV_LIBDIR} ${PC_LIBEVDEV_LIBRARY_DIRS} + PATHS ${ADDITIONAL_LIBRARY_PATHS} + ${LIBEVDEV_PATH_LIB} +) + +set(LIBEVDEV_LIBRARIES ${LIBEVDEV_LIBRARY} ) +set(LIBEVDEV_INCLUDE_DIRS ${LIBEVDEV_INCLUDE_DIR} ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(libevdev DEFAULT_MSG + LIBEVDEV_LIBRARY LIBEVDEV_INCLUDE_DIR) + +mark_as_advanced(LIBEVDEV_INCLUDE_DIR LIBEVDEV_LIBRARY ) diff --git a/CMakeTests/FindLibudev.cmake b/CMakeTests/FindLibudev.cmake new file mode 100644 index 0000000000..2b71e4e605 --- /dev/null +++ b/CMakeTests/FindLibudev.cmake @@ -0,0 +1,28 @@ +# - Try to find LIBUDEV +# Once done this will define +# LIBUDEV_FOUND - System has LIBUDEV +# LIBUDEV_INCLUDE_DIRS - The LIBUDEV include directories +# LIBUDEV_LIBRARIES - The libraries needed to use LIBUDEV + +FIND_PATH( + LIBUDEV_INCLUDE_DIR libudev.h + /usr/include + /usr/local/include + ${LIBUDEV_PATH_INCLUDES} +) + +FIND_LIBRARY( + LIBUDEV_LIBRARY + NAMES udev libudev + PATHS ${ADDITIONAL_LIBRARY_PATHS} + ${LIBUDEV_PATH_LIB} +) + +set(LIBUDEV_LIBRARIES ${LIBUDEV_LIBRARY} ) +set(LIBUDEV_INCLUDE_DIRS ${LIBUDEV_INCLUDE_DIR} ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LIBUDEV DEFAULT_MSG + LIBUDEV_LIBRARY LIBUDEV_INCLUDE_DIR) + +mark_as_advanced(LIBUDEV_INCLUDE_DIR LIBUDEV_LIBRARY ) diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 75679b6a9f..549865b8e9 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -37,6 +37,11 @@ elseif(ANDROID) ControllerInterface/Android/Android.cpp) endif() +if(LIBEVDEV_FOUND AND LIBUDEV_FOUND) + set(SRCS ${SRCS} ControllerInterface/evdev/evdev.cpp) + set(LIBS ${LIBS} ${LIBEVDEV_LIBRARY} ${LIBUDEV_LIBRARY}) +endif() + if(SDL_FOUND OR SDL2_FOUND) set(SRCS ${SRCS} ControllerInterface/SDL/SDL.cpp) if (SDL2_FOUND) diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 6b8b11fd3a..0631069e16 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -26,6 +26,9 @@ #ifdef CIFACE_USE_ANDROID #include "InputCommon/ControllerInterface/Android/Android.h" #endif +#ifdef CIFACE_USE_EVDEV + #include "InputCommon/ControllerInterface/evdev/evdev.h" +#endif using namespace ciface::ExpressionParser; @@ -69,6 +72,9 @@ void ControllerInterface::Initialize(void* const hwnd) #ifdef CIFACE_USE_ANDROID ciface::Android::Init(m_devices); #endif +#ifdef CIFACE_USE_EVDEV + ciface::evdev::Init(m_devices); +#endif m_is_init = true; } diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 408a36fefa..3aca18e95c 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -35,6 +35,9 @@ #if defined(HAVE_SDL) && HAVE_SDL #define CIFACE_USE_SDL #endif +#if defined(HAVE_LIBEVDEV) && defined(HAVE_LIBUDEV) + #define CIFACE_USE_EVDEV +#endif // // ControllerInterface diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp new file mode 100644 index 0000000000..2f4d53e234 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp @@ -0,0 +1,264 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/Logging/Log.h" +#include "InputCommon/ControllerInterface/evdev/evdev.h" + + +namespace ciface +{ +namespace evdev +{ + +void Init(std::vector &controllerDevices) +{ + int num_controllers = 0; + + // We use Udev to find any devices. In the future this will allow for hotplugging. + // But for now it is essentially iterating over /dev/input/event0 to event31. However if the + // naming scheme is ever updated in the future, this *should* be forwards compatable. + + struct udev* udev = udev_new(); + _assert_msg_(PAD, udev != 0, "Couldn't initilize libudev."); + + // List all input devices + udev_enumerate* enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "input"); + udev_enumerate_scan_devices(enumerate); + udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate); + + // Iterate over all input devices + udev_list_entry* dev_list_entry; + udev_list_entry_foreach(dev_list_entry, devices) + { + const char* path = udev_list_entry_get_name(dev_list_entry); + + udev_device* dev = udev_device_new_from_syspath(udev, path); + + const char* devnode = udev_device_get_devnode(dev); + // We only care about devices which we have read/write access to. + if (access(devnode, W_OK) == 0) + { + // Unfortunately udev gives us no way to filter out the non event device interfaces. + // So we open it and see if it works with evdev ioctls or not. + evdevDevice* input = new evdevDevice(devnode, num_controllers); + + if (input->IsInteresting()) + { + controllerDevices.push_back(input); + num_controllers++; + } + else + { + // Either it wasn't a evdev device, or it didn't have at least 8 buttons or two axis. + delete input; + } + } + udev_device_unref(dev); + } + udev_enumerate_unref(enumerate); + udev_unref(udev); +} + +evdevDevice::evdevDevice(const std::string &devnode, int id) : m_devfile(devnode), m_id(id) +{ + // The device file will be read on one of the main threads, so we open in non-blocking mode. + m_fd = open(devnode.c_str(), O_RDWR|O_NONBLOCK); + int ret = libevdev_new_from_fd(m_fd, &m_dev); + + if (ret != 0) + { + // This useally fails because the device node isn't an evdev device, such as /dev/input/js0 + m_initialized = false; + close(m_fd); + return; + } + + m_name = libevdev_get_name(m_dev); + + // Controller buttons (and keyboard keys) + int num_buttons = 0; + for (int key = 0; key < KEY_MAX; key++) + if (libevdev_has_event_code(m_dev, EV_KEY, key)) + AddInput(new Button(num_buttons++, key, m_dev)); + + // Absolute axis (thumbsticks) + int num_axis = 0; + for (int axis = 0; axis < 0x100; axis++) + if (libevdev_has_event_code(m_dev, EV_ABS, axis)) + { + AddAnalogInputs(new Axis(num_axis, axis, false, m_dev), + new Axis(num_axis, axis, true, m_dev)); + num_axis++; + } + + // Force feedback + if (libevdev_has_event_code(m_dev, EV_FF, FF_PERIODIC)) + { + for (auto type : {FF_SINE, FF_SQUARE, FF_TRIANGLE, FF_SAW_UP, FF_SAW_DOWN}) + if (libevdev_has_event_code(m_dev, EV_FF, type)) + AddOutput(new ForceFeedback(type, m_dev)); + } + if (libevdev_has_event_code(m_dev, EV_FF, FF_RUMBLE)) + { + AddOutput(new ForceFeedback(FF_RUMBLE, m_dev)); + } + + // TODO: Add leds as output devices + + m_initialized = true; + m_interesting = num_axis >= 2 || num_buttons >= 8; +} + +evdevDevice::~evdevDevice() +{ + if (m_initialized) + { + libevdev_free(m_dev); + close(m_fd); + } +} + +void evdevDevice::UpdateInput() +{ + // Run through all evdev events + // libevdev will keep track of the actual controller state internally which can be queried + // later with libevdev_fetch_event_value() + input_event ev; + int rc = LIBEVDEV_READ_STATUS_SUCCESS; + do + { + if (rc == LIBEVDEV_READ_STATUS_SYNC) + rc = libevdev_next_event(m_dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + else + rc = libevdev_next_event(m_dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + } while (rc >= 0); +} + + +std::string evdevDevice::Button::GetName() const +{ + // Buttons below 0x100 are mostly keyboard keys, and the names make sense + if (m_code < 0x100) + { + const char* name = libevdev_event_code_get_name(EV_KEY, m_code); + if (name) + return std::string(name); + } + // But controllers use codes above 0x100, and the standard label often doesn't match. + // We are better off with Button 0 and so on. + return "Button " + std::to_string(m_index); +} + +ControlState evdevDevice::Button::GetState() const +{ + int value = 0; + libevdev_fetch_event_value(m_dev, EV_KEY, m_code, &value); + return value; +} + +evdevDevice::Axis::Axis(u8 index, u16 code, bool upper, libevdev* dev) : + m_code(code), m_index(index), m_upper(upper), m_dev(dev) +{ + m_range = libevdev_get_abs_maximum(m_dev, m_code); +} + +std::string evdevDevice::Axis::GetName() const +{ + return "Axis " + std::to_string(m_index) + (m_upper ? "+" : "-"); +} + +ControlState evdevDevice::Axis::GetState() const +{ + int value = 0; + libevdev_fetch_event_value(m_dev, EV_ABS, m_code, &value); + if (m_upper) + return std::max(0.0, double(value) / double(m_range) - 0.5) * 2.0; + else + return (0.5 - std::min(0.5, double(value) / double(m_range))) * 2.0; +} + +std::string evdevDevice::ForceFeedback::GetName() const +{ + // We have some default names. + switch (m_type) + { + case FF_SINE: + return "Sine"; + case FF_TRIANGLE: + return "Triangle"; + case FF_SQUARE: + return "Square"; + case FF_RUMBLE: + return "LeftRight"; + default: + { + const char* name = libevdev_event_code_get_name(EV_FF, m_type); + if (name) + return std::string(name); + return "Unknown"; + } + } +} + +void evdevDevice::ForceFeedback::SetState(ControlState state) +{ + // libevdev doesn't have nice helpers for forcefeedback + // we will use the file descriptors directly. + + if (state > 0) // Upload and start an effect. + { + ff_effect effect; + + effect.id = -1; + effect.direction = 0; // down + effect.replay.length = 500; // 500ms + effect.replay.delay = 0; + effect.trigger.button = 0; // don't trigger on button press + effect.trigger.interval = 0; + + // This is the the interface that XInput uses, with 2 motors of differing sizes/frequencies that + // are controlled seperatally + if (m_type == FF_RUMBLE) + { + effect.type = FF_RUMBLE; + // max ranges tuned to 'feel' similar in magnitude to triangle/sine on xbox360 controller + effect.u.rumble.strong_magnitude = u16(state * 0x4000); + effect.u.rumble.weak_magnitude = u16(state * 0xFFFF); + } + else // FF_PERIODIC, a more generic interface. + { + effect.type = FF_PERIODIC; + effect.u.periodic.waveform = m_type; + effect.u.periodic.phase = 0x7fff; // 180 degrees + effect.u.periodic.offset = 0; + effect.u.periodic.period = 10; + effect.u.periodic.magnitude = s16(state * 0x7FFF); + effect.u.periodic.envelope.attack_length = 0; // no attack + effect.u.periodic.envelope.attack_level = 0; + effect.u.periodic.envelope.fade_length = 0; + effect.u.periodic.envelope.fade_level = 0; + } + + ioctl(m_fd, EVIOCSFF, &effect); + m_id = effect.id; + + input_event play; + play.type = EV_FF; + play.code = m_id; + play.value = 1; + + write(m_fd, (const void*) &play, sizeof(play)); + } + else if (m_id != -1) // delete the effect (which also stops it) + { + ioctl(m_id, EVIOCRMFF, m_id); + } +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h new file mode 100644 index 0000000000..7290fbc379 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h @@ -0,0 +1,86 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "InputCommon/ControllerInterface/Device.h" + +namespace ciface +{ +namespace evdev +{ + +void Init(std::vector& devices); + +class evdevDevice : public Core::Device +{ +private: + class Button : public Core::Device::Input + { + public: + std::string GetName() const override; + Button(u8 index, u16 code, libevdev* dev) : m_index(index), m_code(code), m_dev(dev) {} + ControlState GetState() const override; + private: + const u8 m_index; + const u16 m_code; + libevdev* m_dev; + }; + + class Axis : public Core::Device::Input + { + public: + std::string GetName() const override; + Axis(u8 index, u16 code, bool upper, libevdev* dev); + ControlState GetState() const override; + private: + const u16 m_code; + const u8 m_index; + const bool m_upper; + int m_range; + libevdev* m_dev; + }; + + class ForceFeedback : public Core::Device::Output + { + public: + std::string GetName() const override; + ForceFeedback(u16 type, libevdev* dev) : m_type(type), m_dev(dev), m_id(-1) { m_fd = libevdev_get_fd(dev); } + void SetState(ControlState state) override; + private: + const u16 m_type; + libevdev* m_dev; + int m_fd; + int m_id; + }; + +public: + void UpdateInput() override; + + evdevDevice(const std::string &devnode, int id); + ~evdevDevice(); + + std::string GetName() const override { return m_name; } + int GetId() const override { return m_id; } + std::string GetSource() const override { return "evdev"; } + + bool IsInteresting() const { return m_initialized && m_interesting; } + +private: + const std::string m_devfile; + int m_fd; + libevdev* m_dev; + std::string m_name; + const int m_id; + bool m_initialized; + bool m_interesting; +}; + +} + +} From 9065ddf5fadf337b6b8cd5ed7a36bf796199cda2 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 29 Jun 2015 13:09:24 +1200 Subject: [PATCH 2/7] Linux: Disable SDL controller backend by default. Not deleted, because it's the only option for some other operating systems such as FreeBSD or any other slightly exotic operating system someone might try and run dolphin on. --- CMakeLists.txt | 19 +++++++++++-------- CMakeTests/FindSDL2.cmake | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e64061a6b7..5162b86dd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,14 @@ option(ENABLE_PCH "Use PCH to speed up compilation" ON) option(ENABLE_PIE "Build a Position-Independent Executable (PIE)" ON) option(ENABLE_LTO "Enables Link Time Optimization" OFF) option(ENABLE_GENERIC "Enables generic build that should run on any little-endian host" OFF) + +# Enable SDL for default on operating systems that aren't OSX, Android, Linux or Windows. +if(NOT APPLE AND NOT ANDROID AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT MSVC) + option(ENABLE_SDL "Enables SDL as a generic controller backend" ON) +else() + option(ENABLE_SDL "Enables SDL as a generic controller backend" OFF) +endif() + if(APPLE) option(OSX_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF) endif() @@ -632,26 +640,21 @@ if(OPENAL_FOUND) endif() endif() -if(NOT ANDROID) - if(NOT APPLE) - include(FindSDL2 OPTIONAL) - endif() +if(ENABLE_SDL) + include(FindSDL2 OPTIONAL) if(SDL2_FOUND) message("Using shared SDL2") add_definitions(-DHAVE_SDL=1) include_directories(${SDL2_INCLUDE_DIR}) else(SDL2_FOUND) # SDL2 not found, try SDL - if(NOT APPLE) - include(FindSDL OPTIONAL) - endif() + include(FindSDL OPTIONAL) if(SDL_FOUND) message("Using shared SDL") add_definitions(-DHAVE_SDL=1) include_directories(${SDL_INCLUDE_DIR}) else(SDL_FOUND) message("SDL NOT found, disabling SDL input") - add_definitions(-DHAVE_SDL=0) endif(SDL_FOUND) endif(SDL2_FOUND) endif() diff --git a/CMakeTests/FindSDL2.cmake b/CMakeTests/FindSDL2.cmake index 614426cccf..aef817ec6a 100644 --- a/CMakeTests/FindSDL2.cmake +++ b/CMakeTests/FindSDL2.cmake @@ -172,9 +172,22 @@ IF(SDL2_LIBRARY_TEMP) SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") SET(SDL2_FOUND "YES") + + # extract the major and minor version numbers from SDL2/SDL_version.h + # we have to handle framework a little bit differently : + if("${SDL2_INCLUDE_DIR}" MATCHES ".framework") + set(SDL2_VERSION_H_INPUT "${SDL2_INCLUDE_DIR}/Headers/SDL_version.h") + else() + set(SDL2_VERSION_H_INPUT "${SDL2_INCLUDE_DIR}/SDL_version.h") + endif() + FILE(READ "${SDL2_VERSION_H_INPUT}" SDL2_VERSION_H_CONTENTS) + STRING(REGEX REPLACE ".*#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+).*#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+).*#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+).*" + "\\1.\\2.\\3" SDL2_VERSION "${SDL2_VERSION_H_CONTENTS}") +#MESSAGE("SDL2 Version is ${SDL2_VERSION}") + ENDIF(SDL2_LIBRARY_TEMP) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 - REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) + REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR VERSION_VAR SDL2_VERSION) From ad714993aaae2826a41fc55691d869e46bb3b479 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 29 Jun 2015 13:57:30 +1200 Subject: [PATCH 3/7] CMake: Explicitly pull in threads. Dolphin uses threads, but never actually pulled them it. Normally some library we depend on would pull threads in, but there are potential builds that forget to pull in threads. --- CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5162b86dd4..347b87c2b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -313,6 +313,14 @@ if(WIN32) add_definitions(-D_CRT_SECURE_NO_DEPRECATE) endif(WIN32) +# Dolphin requires threads. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, we will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type (Release/Debug/RelWithDebInfo/MinSizeRe)" FORCE) From 0dc8763247051797bb4952206bfe6fc99164229b Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 30 Jun 2015 23:57:54 +1200 Subject: [PATCH 4/7] Linux: Don't allow dolphin to be build without evdev support. Unless explicitly requested. --- CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 347b87c2b7..d6c1c4b05f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,10 @@ else() option(ENABLE_SDL "Enables SDL as a generic controller backend" OFF) endif() +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ANDROID) + option(ENABLE_EVDEV "Enables the evdev controller backend" ON) +endif() + if(APPLE) option(OSX_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF) endif() @@ -529,16 +533,16 @@ if(USE_EGL) add_definitions(-DUSE_EGL=1) endif() -if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - include(FindLibudev OPTIONAL) - include(FindLibevdev OPTIONAL) +if(ENABLE_EVDEV) + include(FindLibudev REQUIRED) + include(FindLibevdev REQUIRED) if(LIBUDEV_FOUND AND LIBEVDEV_FOUND) message("libevdev/libudev found, enabling evdev controller backend") add_definitions(-DHAVE_LIBUDEV=1) add_definitions(-DHAVE_LIBEVDEV=1) include_directories(${LIBUDEV_INCLUDE_DIR} ${LIBEVDEV_INCLUDE_DIR}) else() - message("Could find libevdev/libudev, disabling evdev controller backend") + message(FATAL_ERROR "Couldn't find libevdev and/or libudev. Can't build evdev controller backend.\nDisable ENABLE_EVDEV if you wish to build without controller support") endif() endif() From 77183899e0526a343b8429cf04a1c07b31aafc34 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Wed, 1 Jul 2015 00:37:26 +1200 Subject: [PATCH 5/7] evdev: Support axis with ranges which extend below zero. --- .../InputCommon/ControllerInterface/evdev/evdev.cpp | 12 +++++++++--- .../InputCommon/ControllerInterface/evdev/evdev.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp index 2f4d53e234..9b31da73c4 100644 --- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp @@ -164,7 +164,8 @@ ControlState evdevDevice::Button::GetState() const evdevDevice::Axis::Axis(u8 index, u16 code, bool upper, libevdev* dev) : m_code(code), m_index(index), m_upper(upper), m_dev(dev) { - m_range = libevdev_get_abs_maximum(m_dev, m_code); + m_min = libevdev_get_abs_minimum(m_dev, m_code); + m_range = libevdev_get_abs_maximum(m_dev, m_code) + abs(m_min); } std::string evdevDevice::Axis::GetName() const @@ -176,10 +177,15 @@ ControlState evdevDevice::Axis::GetState() const { int value = 0; libevdev_fetch_event_value(m_dev, EV_ABS, m_code, &value); + + // Value from 0.0 to 1.0 + ControlState fvalue = double(value - m_min) / double(m_range); + + // Split into two axis, each covering half the range from 0.0 to 1.0 if (m_upper) - return std::max(0.0, double(value) / double(m_range) - 0.5) * 2.0; + return std::max(0.0, fvalue - 0.5) * 2.0; else - return (0.5 - std::min(0.5, double(value) / double(m_range))) * 2.0; + return (0.5 - std::min(0.5, fvalue)) * 2.0; } std::string evdevDevice::ForceFeedback::GetName() const diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h index 7290fbc379..d1555c8cce 100644 --- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h @@ -43,6 +43,7 @@ private: const u8 m_index; const bool m_upper; int m_range; + int m_min; libevdev* m_dev; }; From 9b95faaed7ca9c50685fe869d59355e771439455 Mon Sep 17 00:00:00 2001 From: Anthony Serna Date: Wed, 1 Jul 2015 15:46:16 +0200 Subject: [PATCH 6/7] Small changes to description to "Store EFB Copies to Texture Only" --- Source/Core/DolphinWX/VideoConfigDiag.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp index 2e132576b3..a5639fdff5 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.cpp +++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp @@ -116,7 +116,7 @@ static wxString borderless_fullscreen_desc = wxTRANSLATE("Implement fullscreen m static wxString internal_res_desc = wxTRANSLATE("Specifies the resolution used to render at. A high resolution greatly improves visual quality, but also greatly increases GPU load and can cause issues in certain games.\n\"Multiple of 640x528\" will result in a size slightly larger than \"Window Size\" but yield fewer issues. Generally speaking, the lower the internal resolution is, the better your performance will be.\n\nIf unsure, select 640x528."); static wxString efb_access_desc = wxTRANSLATE("Ignore any requests from the CPU to read from or write to the EFB.\nImproves performance in some games, but might disable some gameplay-related features or graphical effects.\n\nIf unsure, leave this unchecked."); static wxString efb_emulate_format_changes_desc = wxTRANSLATE("Ignore any changes to the EFB format.\nImproves performance in many games without any negative effect. Causes graphical defects in a small number of other games.\n\nIf unsure, leave this checked."); -static wxString skip_efb_copy_to_ram_desc = wxTRANSLATE("Skip GPU synchronizing on EFB copies. Causes graphical defects in a small number of games.\n\nIf unsure, leave this checked."); +static wxString skip_efb_copy_to_ram_desc = wxTRANSLATE("Stores EFB Copies exclusively on the GPU, bypassing system memory. Causes graphical defects in a small number of games.\n\nEnabled = EFB Copies to Texture\nDisabled = EFB Copies to RAM (and Texture)\n\nIf unsure, leave this checked."); static wxString stc_desc = wxTRANSLATE("The \"Safe\" setting eliminates the likelihood of the GPU missing texture updates from RAM.\nLower accuracies cause in-game text to appear garbled in certain games.\n\nIf unsure, use the rightmost value."); static wxString wireframe_desc = wxTRANSLATE("Render the scene as a wireframe.\n\nIf unsure, leave this unchecked."); static wxString disable_fog_desc = wxTRANSLATE("Makes distant objects more visible by removing fog, thus increasing the overall detail.\nDisabling fog will break some games which rely on proper fog emulation.\n\nIf unsure, leave this unchecked."); From 6a33f174de618dc698ceba02e792cc6b2dba9732 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Wed, 1 Jul 2015 01:28:41 +0200 Subject: [PATCH 7/7] GC Adapter: Do not attempt to claim_interface when libusb_open() returns an error. Fixes a crash / nullptr dereference when the adapter is plugged in but no drivers are installed for it, on Windows. --- Source/Core/Core/HW/SI_GCAdapter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Core/Core/HW/SI_GCAdapter.cpp b/Source/Core/Core/HW/SI_GCAdapter.cpp index 7115dc3e40..b54c07ef74 100644 --- a/Source/Core/Core/HW/SI_GCAdapter.cpp +++ b/Source/Core/Core/HW/SI_GCAdapter.cpp @@ -235,6 +235,7 @@ static bool CheckDeviceAccess(libusb_device* device) if (ret == LIBUSB_ERROR_NOT_SUPPORTED) s_libusb_driver_not_supported = true; } + return false; } else if ((ret = libusb_kernel_driver_active(s_handle, 0)) == 1) {