commit
c83739beb9
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -1,4 +0,0 @@
|
|||
[submodule "src/logid/hidpp"]
|
||||
path = src/logid/hidpp
|
||||
url = https://github.com/PixlOne/hidpp.git
|
||||
branch = master
|
|
@ -11,4 +11,41 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -Wall -Wextra")
|
|||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(Git)
|
||||
|
||||
# Set version number
|
||||
if(EXISTS ${CMAKE_SOURCE_DIR}/version.txt)
|
||||
file(READ version.txt LOGIOPS_VERSION)
|
||||
string(REGEX REPLACE "\n$" "" LOGIOPS_VERSION ${LOGIOPS_VERSION})
|
||||
endif()
|
||||
|
||||
if(GIT_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/.git)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE}
|
||||
rev-parse --abbrev-ref HEAD
|
||||
OUTPUT_VARIABLE LOGIOPS_GIT_BRANCH)
|
||||
string(REGEX REPLACE "\n$" "" LOGIOPS_GIT_BRANCH ${LOGIOPS_GIT_BRANCH})
|
||||
if(LOGIOPS_GIT_BRANCH MATCHES "^tags/?")
|
||||
STRING(REGEX REPLACE "^tags/" ""
|
||||
LOGIOPS_VERSION ${LOGIOPS_GIT_BRANCH})
|
||||
else()
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE}
|
||||
rev-parse --short HEAD
|
||||
OUTPUT_VARIABLE LOGIOPS_COMMIT_HASH)
|
||||
string(REGEX REPLACE "\n$" "" LOGIOPS_COMMIT_HASH ${LOGIOPS_COMMIT_HASH})
|
||||
if(LOGIOPS_VERSION)
|
||||
string(APPEND LOGIOPS_VERSION -${LOGIOPS_COMMIT_HASH})
|
||||
else()
|
||||
set(LOGIOPS_VERSION git-${LOGIOPS_COMMIT_HASH})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LOGIOPS_VERSION)
|
||||
message("LogiOps Version Number: ${LOGIOPS_VERSION}")
|
||||
else()
|
||||
set(LOGIOPS_VERSION "null")
|
||||
endif()
|
||||
|
||||
add_definitions( -DLOGIOPS_VERSION="${LOGIOPS_VERSION}")
|
||||
|
||||
add_subdirectory(src/logid)
|
||||
|
|
|
@ -42,3 +42,11 @@ I'm also looking for contributors to help in my project; feel free to submit a p
|
|||
## Compatible Devices
|
||||
|
||||
[For a list of tested devices, check TESTED.md](TESTED.md)
|
||||
|
||||
## Special Thanks
|
||||
Thanks to the following people for contributing to this repository.
|
||||
|
||||
- [Clément Vuchener & contributors for creating the old HID++ library](https://github.com/cvuchener/hidpp)
|
||||
- [Developers of Solaar for providing information on HID++](https://github.com/pwr-Solaar/Solaar)
|
||||
- [Nestor Lopez Casado for providing Logitech documentation on the HID++ protocol](http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28)
|
||||
- Everyone listed in the contributors page
|
||||
|
|
|
@ -1,238 +0,0 @@
|
|||
#include <unistd.h>
|
||||
#include <future>
|
||||
#include <hidpp20/Error.h>
|
||||
#include <hidpp/SimpleDispatcher.h>
|
||||
#include <hidpp20/IAdjustableDPI.h>
|
||||
#include <hidpp20/ISmartShift.h>
|
||||
#include <hidpp20/IHiresScroll.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "Actions.h"
|
||||
#include "util.h"
|
||||
#include "EvdevDevice.h"
|
||||
|
||||
using namespace logid;
|
||||
|
||||
Gesture::Gesture(ButtonAction* ba, GestureMode m, void* aux) : action (ba), mode (m)
|
||||
{
|
||||
switch(m)
|
||||
{
|
||||
case GestureMode::OnFewPixels:
|
||||
per_pixel = *(int*)aux;
|
||||
break;
|
||||
case GestureMode::Axis:
|
||||
axis = *(axis_info*)aux;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NoAction* NoAction::copy(Device *dev)
|
||||
{
|
||||
auto action = new NoAction();
|
||||
action->device = dev;
|
||||
return action;
|
||||
}
|
||||
KeyAction* KeyAction::copy(Device *dev)
|
||||
{
|
||||
auto action = new KeyAction(keys);
|
||||
action->device = dev;
|
||||
return action;
|
||||
}
|
||||
GestureAction* GestureAction::copy(Device* dev)
|
||||
{
|
||||
auto action = new GestureAction({});
|
||||
action->device = dev;
|
||||
for(auto it : gestures)
|
||||
action->gestures.insert({it.first, new Gesture(*it.second, dev)});
|
||||
return action;
|
||||
}
|
||||
SmartshiftAction* SmartshiftAction::copy(Device* dev)
|
||||
{
|
||||
auto action = new SmartshiftAction();
|
||||
action->device = dev;
|
||||
return action;
|
||||
}
|
||||
HiresScrollAction* HiresScrollAction::copy(Device* dev)
|
||||
{
|
||||
auto action = new HiresScrollAction();
|
||||
action->device = dev;
|
||||
return action;
|
||||
}
|
||||
CycleDPIAction* CycleDPIAction::copy(Device* dev)
|
||||
{
|
||||
auto action = new CycleDPIAction(dpis);
|
||||
action->device = dev;
|
||||
return action;
|
||||
}
|
||||
ChangeDPIAction* ChangeDPIAction::copy(Device* dev)
|
||||
{
|
||||
auto action = new ChangeDPIAction(dpi_inc);
|
||||
action->device = dev;
|
||||
return action;
|
||||
}
|
||||
|
||||
void KeyAction::press()
|
||||
{
|
||||
//KeyPress event for each in keys
|
||||
for(unsigned int i : keys)
|
||||
global_evdev->sendEvent(EV_KEY, i, 1);
|
||||
}
|
||||
|
||||
void KeyAction::release()
|
||||
{
|
||||
//KeyRelease event for each in keys
|
||||
for(unsigned int i : keys)
|
||||
global_evdev->sendEvent(EV_KEY, i, 0);
|
||||
}
|
||||
|
||||
void GestureAction::press()
|
||||
{
|
||||
for(auto g : gestures)
|
||||
g.second->per_pixel_mod = 0;
|
||||
|
||||
held = true;
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
void GestureAction::move(HIDPP20::IReprogControlsV4::Move m)
|
||||
{
|
||||
x += m.x;
|
||||
y += m.y;
|
||||
if(m.y != 0 && abs(y) > 50)
|
||||
{
|
||||
auto g = gestures.find(m.y > 0 ? Direction::Down : Direction::Up);
|
||||
if(g != gestures.end())
|
||||
{
|
||||
if (g->second->mode == GestureMode::Axis)
|
||||
global_evdev->moveAxis(g->second->axis.code, abs(m.y) * g->second->axis.multiplier);
|
||||
if (g->second->mode == GestureMode::OnFewPixels)
|
||||
{
|
||||
g->second->per_pixel_mod += abs(m.y);
|
||||
if(g->second->per_pixel_mod >= g->second->per_pixel)
|
||||
{
|
||||
g->second->per_pixel_mod -= g->second->per_pixel;
|
||||
g->second->action->press();
|
||||
g->second->action->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(m.x != 0 && abs(x) > 50)
|
||||
{
|
||||
auto g = gestures.find(m.x > 0 ? Direction::Right : Direction::Left);
|
||||
if(g != gestures.end())
|
||||
{
|
||||
if (g->second->mode == GestureMode::Axis)
|
||||
global_evdev->moveAxis(g->second->axis.code, abs(m.x) * g->second->axis.multiplier);
|
||||
if (g->second->mode == GestureMode::OnFewPixels)
|
||||
{
|
||||
g->second->per_pixel_mod += abs(m.x);
|
||||
if (g->second->per_pixel_mod >= g->second->per_pixel)
|
||||
{
|
||||
g->second->per_pixel_mod -= g->second->per_pixel;
|
||||
g->second->action->press();
|
||||
g->second->action->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GestureAction::release()
|
||||
{
|
||||
held = false;
|
||||
Direction direction;
|
||||
if(abs(x) < 50 && abs(y) < 50) direction = Direction::None;
|
||||
else direction = getDirection(x, y);
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
auto g = gestures.find(direction);
|
||||
|
||||
if(g != gestures.end() && g->second->mode == GestureMode::OnRelease)
|
||||
{
|
||||
g->second->action->press();
|
||||
g->second->action->release();
|
||||
}
|
||||
}
|
||||
|
||||
void SmartshiftAction::press()
|
||||
{
|
||||
try
|
||||
{
|
||||
HIDPP20::ISmartShift iss(device->hidpp_dev);
|
||||
auto s = iss.getStatus();
|
||||
iss.setStatus({new bool(!*s.Active)});
|
||||
}
|
||||
catch(HIDPP20::Error &e)
|
||||
{
|
||||
log_printf(ERROR, "Error toggling smart shift, code %d: %s\n", e.errorCode(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void HiresScrollAction::press()
|
||||
{
|
||||
try
|
||||
{
|
||||
HIDPP20::IHiresScroll ihs(device->hidpp_dev);
|
||||
auto mode = ihs.getMode();
|
||||
mode ^= HIDPP20::IHiresScroll::Mode::HiRes;
|
||||
ihs.setMode(mode);
|
||||
}
|
||||
catch(HIDPP20::Error &e)
|
||||
{
|
||||
log_printf(ERROR, "Error toggling hires scroll, code %d: %s\n", e.errorCode(), e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CycleDPIAction::press()
|
||||
{
|
||||
HIDPP20::IAdjustableDPI iad(device->hidpp_dev);
|
||||
|
||||
try
|
||||
{
|
||||
for (uint i = 0; i < iad.getSensorCount(); i++)
|
||||
{
|
||||
int current_dpi = std::get<0>(iad.getSensorDPI(i));
|
||||
bool found = false;
|
||||
for (uint j = 0; j < dpis.size(); j++)
|
||||
{
|
||||
if (dpis[j] == current_dpi)
|
||||
{
|
||||
if (j == dpis.size() - 1)
|
||||
iad.setSensorDPI(i, dpis[0]);
|
||||
else
|
||||
iad.setSensorDPI(i, dpis[j + 1]);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
if (dpis.empty()) iad.setSensorDPI(i, std::get<1>(iad.getSensorDPI(i)));
|
||||
else iad.setSensorDPI(i, dpis[0]);
|
||||
}
|
||||
}
|
||||
catch(HIDPP20::Error &e) { log_printf(ERROR, "Error while cycling DPI: %s", e.what()); }
|
||||
}
|
||||
|
||||
void ChangeDPIAction::press()
|
||||
{
|
||||
HIDPP20::IAdjustableDPI iad(device->hidpp_dev);
|
||||
|
||||
try
|
||||
{
|
||||
for(uint i = 0; i < iad.getSensorCount(); i++)
|
||||
{
|
||||
int current_dpi = std::get<0>(iad.getSensorDPI(i));
|
||||
iad.setSensorDPI(i, current_dpi + dpi_inc);
|
||||
}
|
||||
}
|
||||
catch(HIDPP20::Error &e)
|
||||
{
|
||||
log_printf(ERROR, "Error while incrementing DPI: %s", e.what());
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
#ifndef LOGID_ACTIONS_H
|
||||
#define LOGID_ACTIONS_H
|
||||
#include "Device.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <hidpp20/IReprogControls.h>
|
||||
|
||||
namespace logid
|
||||
{
|
||||
enum class Action
|
||||
{
|
||||
None,
|
||||
Keypress,
|
||||
Gestures,
|
||||
CycleDPI,
|
||||
ChangeDPI,
|
||||
ToggleSmartshift,
|
||||
ToggleHiresScroll
|
||||
};
|
||||
enum class Direction
|
||||
{
|
||||
None,
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right
|
||||
};
|
||||
enum class GestureMode
|
||||
{
|
||||
NoPress,
|
||||
OnRelease,
|
||||
OnFewPixels,
|
||||
Axis
|
||||
};
|
||||
|
||||
class Device;
|
||||
|
||||
class ButtonAction
|
||||
{
|
||||
public:
|
||||
virtual ~ButtonAction() = default;
|
||||
|
||||
Action type;
|
||||
virtual ButtonAction* copy(Device* dev) = 0;
|
||||
virtual void press() = 0;
|
||||
virtual void release() = 0;
|
||||
// ButtonAction(const ButtonAction &a, Device* d) : type (a.type), device (d) {}
|
||||
// ButtonAction* create_instance(Device* d);
|
||||
protected:
|
||||
ButtonAction(Action a) : type (a) {};
|
||||
Device* device;
|
||||
};
|
||||
class NoAction : public ButtonAction
|
||||
{
|
||||
public:
|
||||
NoAction() : ButtonAction(Action::None) {}
|
||||
virtual NoAction* copy(Device* dev);
|
||||
virtual void press() {}
|
||||
virtual void release() {}
|
||||
};
|
||||
class KeyAction : public ButtonAction
|
||||
{
|
||||
public:
|
||||
explicit KeyAction(std::vector<unsigned int> k) : ButtonAction(Action::Keypress), keys (std::move(k)) {};
|
||||
virtual KeyAction* copy(Device* dev);
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
private:
|
||||
std::vector<unsigned int> keys;
|
||||
};
|
||||
class Gesture
|
||||
{
|
||||
public:
|
||||
struct axis_info {
|
||||
uint code;
|
||||
float multiplier;
|
||||
};
|
||||
|
||||
Gesture(ButtonAction* ba, GestureMode m, void* aux=nullptr);
|
||||
Gesture(const Gesture &g, Device* dev)
|
||||
: action (g.action->copy(dev)), mode (g.mode), per_pixel (g.per_pixel),
|
||||
axis (g.axis)
|
||||
{
|
||||
}
|
||||
|
||||
ButtonAction* action;
|
||||
GestureMode mode;
|
||||
int per_pixel;
|
||||
int per_pixel_mod;
|
||||
axis_info axis;
|
||||
};
|
||||
|
||||
class GestureAction : public ButtonAction
|
||||
{
|
||||
public:
|
||||
GestureAction(std::map<Direction, Gesture*> g) : ButtonAction(Action::Gestures), gestures (std::move(g)) {};
|
||||
std::map<Direction, Gesture*> gestures;
|
||||
virtual GestureAction* copy(Device* dev);
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
void move(HIDPP20::IReprogControlsV4::Move m);
|
||||
private:
|
||||
bool held;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
};
|
||||
class SmartshiftAction : public ButtonAction
|
||||
{
|
||||
public:
|
||||
SmartshiftAction() : ButtonAction(Action::ToggleSmartshift) {};
|
||||
virtual SmartshiftAction* copy(Device* dev);
|
||||
virtual void press();
|
||||
virtual void release() {}
|
||||
};
|
||||
class HiresScrollAction : public ButtonAction
|
||||
{
|
||||
public:
|
||||
HiresScrollAction() : ButtonAction(Action::ToggleHiresScroll) {};
|
||||
virtual HiresScrollAction* copy(Device* dev);
|
||||
virtual void press();
|
||||
virtual void release() {}
|
||||
};
|
||||
class CycleDPIAction : public ButtonAction
|
||||
{
|
||||
public:
|
||||
CycleDPIAction(std::vector<int> d) : ButtonAction(Action::CycleDPI), dpis (d) {};
|
||||
virtual CycleDPIAction* copy(Device* dev);
|
||||
virtual void press();
|
||||
virtual void release() {}
|
||||
private:
|
||||
const std::vector<int> dpis;
|
||||
};
|
||||
class ChangeDPIAction : public ButtonAction
|
||||
{
|
||||
public:
|
||||
ChangeDPIAction(int i) : ButtonAction(Action::ChangeDPI), dpi_inc (i) {};
|
||||
virtual ChangeDPIAction* copy(Device* dev);
|
||||
virtual void press();
|
||||
virtual void release() {}
|
||||
private:
|
||||
int dpi_inc;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //LOGID_ACTIONS_H
|
|
@ -10,69 +10,75 @@ find_package(PkgConfig REQUIRED)
|
|||
|
||||
add_executable(logid
|
||||
logid.cpp
|
||||
util.cpp
|
||||
util.h
|
||||
Configuration.cpp
|
||||
Configuration.h
|
||||
Actions.cpp
|
||||
Actions.h
|
||||
util/log.cpp
|
||||
InputDevice.cpp
|
||||
DeviceManager.cpp
|
||||
Device.cpp
|
||||
Device.h
|
||||
DeviceFinder.cpp
|
||||
DeviceFinder.h
|
||||
EvdevDevice.cpp
|
||||
EvdevDevice.h)
|
||||
Receiver.cpp
|
||||
Configuration.cpp
|
||||
features/DPI.cpp
|
||||
features/SmartShift.cpp
|
||||
features/HiresScroll.cpp
|
||||
features/RemapButton.cpp
|
||||
actions/Action.cpp
|
||||
actions/NullAction.cpp
|
||||
actions/KeypressAction.cpp
|
||||
actions/ToggleHiresScroll.cpp
|
||||
actions/ToggleSmartShift.cpp
|
||||
actions/CycleDPI.cpp
|
||||
actions/ChangeDPI.cpp
|
||||
actions/GestureAction.cpp
|
||||
actions/gesture/Gesture.cpp
|
||||
actions/gesture/ReleaseGesture.cpp
|
||||
actions/gesture/IntervalGesture.cpp
|
||||
actions/gesture/AxisGesture.cpp
|
||||
actions/gesture/NullGesture.cpp
|
||||
backend/Error.cpp
|
||||
backend/raw/DeviceMonitor.cpp
|
||||
backend/raw/RawDevice.cpp
|
||||
backend/dj/Receiver.cpp
|
||||
backend/dj/ReceiverMonitor.cpp
|
||||
backend/dj/Error.cpp
|
||||
backend/hidpp/Device.cpp
|
||||
backend/hidpp/Report.cpp
|
||||
backend/hidpp10/Error.cpp
|
||||
backend/hidpp10/Device.cpp
|
||||
backend/hidpp20/Device.cpp
|
||||
backend/hidpp20/Error.cpp
|
||||
backend/hidpp20/Feature.cpp
|
||||
backend/hidpp20/EssentialFeature.cpp
|
||||
backend/hidpp20/features/Root.cpp
|
||||
backend/hidpp20/features/FeatureSet.cpp
|
||||
backend/hidpp20/features/DeviceName.cpp
|
||||
backend/hidpp20/features/Reset.cpp
|
||||
backend/hidpp20/features/AdjustableDPI.cpp
|
||||
backend/hidpp20/features/SmartShift.cpp
|
||||
backend/hidpp20/features/ReprogControls.cpp
|
||||
backend/hidpp20/features/HiresScroll.cpp
|
||||
backend/dj/Report.cpp
|
||||
util/mutex_queue.h
|
||||
util/workqueue.cpp
|
||||
util/worker_thread.cpp
|
||||
util/task.cpp
|
||||
util/thread.cpp
|
||||
util/ExceptionHandler.cpp)
|
||||
|
||||
set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
pkg_check_modules(PC_EVDEV libevdev)
|
||||
pkg_check_modules(PC_EVDEV libevdev REQUIRED)
|
||||
pkg_check_modules(SYSTEMD "systemd")
|
||||
pkg_check_modules(LIBCONFIG libconfig)
|
||||
|
||||
find_path(HIDPP_INCLUDE_DIR hidpp)
|
||||
find_library(HIDPP_LIBRARY libhidpp.so)
|
||||
pkg_check_modules(LIBCONFIG libconfig REQUIRED)
|
||||
pkg_check_modules(LIBUDEV libudev REQUIRED)
|
||||
|
||||
find_path(EVDEV_INCLUDE_DIR libevdev/libevdev.h
|
||||
HINTS ${PC_EVDEV_INCLUDE_DIRS} ${PC_EVDEV_INCLUDEDIR})
|
||||
find_library(EVDEV_LIBRARY
|
||||
NAMES evdev libevdev)
|
||||
|
||||
if((NOT HIDPP_INCLUDE_DIR) OR (NOT EXISTS ${HIDPP_INCLUDE_DIR}) OR (NOT HIDPP_LIBRARY) OR FORCE_BUILD_HIDPP)
|
||||
message("Could not find libhidpp include dir, getting submodule")
|
||||
include_directories(${EVDEV_INCLUDE_DIR} ${LIBUDEV_INCLUDE_DIRECTORIES})
|
||||
|
||||
execute_process(COMMAND git submodule update --init -- hidpp
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
set(DEFAULT_HID_BACKEND "linux")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(DEFAULT_HID_BACKEND "windows")
|
||||
else()
|
||||
message(WARNING "System is not supported")
|
||||
endif()
|
||||
|
||||
set(HID_BACKEND "${DEFAULT_HID_BACKEND}" CACHE STRING "Backend used for accessing HID devices")
|
||||
set_property(CACHE HID_BACKEND PROPERTY STRINGS linux windows)
|
||||
|
||||
if("${HID_BACKEND}" STREQUAL "linux")
|
||||
pkg_check_modules(LIBUDEV libudev REQUIRED)
|
||||
elseif("${HID_BACKEND}" STREQUAL "windows")
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
add_definitions(-D_WIN32_WINNT=0x0600) # Use vista or later
|
||||
else()
|
||||
message(FATAL_ERROR "HID_BACKEND is invalid.")
|
||||
endif()
|
||||
|
||||
add_subdirectory(hidpp/src/libhidpp)
|
||||
|
||||
set(HIDPP_INCLUDE_DIR "hidpp/src/libhidpp/")
|
||||
set(HIDPP_LIBRARY hidpp)
|
||||
else()
|
||||
set(HIDPP_INCLUDE_DIR ${HIDPP_INCLUDE_DIR}/hidpp)
|
||||
endif()
|
||||
|
||||
include_directories(${HIDPP_INCLUDE_DIR} ${EVDEV_INCLUDE_DIR})
|
||||
|
||||
target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ ${HIDPP_LIBRARY})
|
||||
target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++
|
||||
${LIBUDEV_LIBRARIES})
|
||||
|
||||
install(TARGETS logid DESTINATION bin)
|
||||
|
||||
|
|
|
@ -1,518 +1,178 @@
|
|||
#include <cstdint>
|
||||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <hidpp20/IHiresScroll.h>
|
||||
|
||||
#include "Configuration.h"
|
||||
#include "util.h"
|
||||
#include "util/log.h"
|
||||
|
||||
using namespace logid;
|
||||
using namespace libconfig;
|
||||
using namespace std::chrono;
|
||||
|
||||
Configuration::Configuration(const char *config_file)
|
||||
Configuration::Configuration(const std::string& config_file)
|
||||
{
|
||||
//Read config file
|
||||
try
|
||||
{
|
||||
cfg.readFile(config_file);
|
||||
}
|
||||
catch(const FileIOException &e)
|
||||
{
|
||||
log_printf(ERROR, "I/O Error while reading %s: %s", config_file, e.what());
|
||||
try {
|
||||
_config.readFile(config_file.c_str());
|
||||
} catch(const FileIOException &e) {
|
||||
logPrintf(ERROR, "I/O Error while reading %s: %s", config_file.c_str(),
|
||||
e.what());
|
||||
throw e;
|
||||
} catch(const ParseException &e) {
|
||||
logPrintf(ERROR, "Parse error in %s, line %d: %s", e.getFile(),
|
||||
e.getLine(), e.getError());
|
||||
throw e;
|
||||
}
|
||||
catch(const ParseException &e)
|
||||
{
|
||||
log_printf(ERROR, "Parse error in %s, line %d: %s", e.getFile(), e.getLine(), e.getError());
|
||||
throw e;
|
||||
}
|
||||
const Setting &root = cfg.getRoot();
|
||||
|
||||
try
|
||||
{
|
||||
auto& _blacklist = root.lookup("blacklist");
|
||||
if(_blacklist.isArray() || _blacklist.isList())
|
||||
{
|
||||
int len = _blacklist.getLength();
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
if(!_blacklist[i].isNumber()) {
|
||||
log_printf(WARN, "Line %d: blacklist must only contain "
|
||||
"PIDs", _blacklist[i].getSourceLine());
|
||||
if(_blacklist.isArray())
|
||||
break;
|
||||
if(_blacklist.isList())
|
||||
continue;
|
||||
}
|
||||
blacklist.push_back((int)_blacklist[i]);
|
||||
const Setting &root = _config.getRoot();
|
||||
|
||||
try {
|
||||
auto& worker_count = root["workers"];
|
||||
if(worker_count.getType() == Setting::TypeInt) {
|
||||
_worker_threads = worker_count;
|
||||
if(_worker_threads < 0)
|
||||
logPrintf(WARN, "Line %d: workers cannot be negative.",
|
||||
worker_count.getSourceLine());
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: workers must be an integer.",
|
||||
worker_count.getSourceLine());
|
||||
}
|
||||
} catch(const SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
try {
|
||||
auto& timeout = root["io_timeout"];
|
||||
if(timeout.isNumber()) {
|
||||
if(timeout.getType() == Setting::TypeFloat)
|
||||
_io_timeout = duration_cast<milliseconds>(
|
||||
duration<double, std::milli>(timeout));
|
||||
else
|
||||
{
|
||||
log_printf(WARN, "Line %d: blacklist must be an array or list, "
|
||||
"ignnoring.", _blacklist.getSourceLine());
|
||||
}
|
||||
}
|
||||
catch(const SettingNotFoundException &e)
|
||||
{
|
||||
_io_timeout = milliseconds((int)timeout);
|
||||
} else
|
||||
logPrintf(WARN, "Line %d: io_timeout must be a number.",
|
||||
timeout.getSourceLine());
|
||||
} catch(const SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
Setting* _devices;
|
||||
try {
|
||||
auto& devices = root["devices"];
|
||||
|
||||
try { _devices = &root["devices"]; }
|
||||
catch(const SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "No devices listed in config file.");
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < _devices->getLength(); i++)
|
||||
{
|
||||
const Setting &device = (*_devices)[i];
|
||||
for(int i = 0; i < devices.getLength(); i++) {
|
||||
const Setting& device = devices[i];
|
||||
std::string name;
|
||||
try
|
||||
{
|
||||
if(!device.lookupValue("name", name))
|
||||
{
|
||||
log_printf(WARN, "Line %d: 'name' must be a string, skipping device.", device["name"].getSourceLine());
|
||||
try {
|
||||
if(!device.lookupValue("name", name)) {
|
||||
logPrintf(WARN, "Line %d: 'name' must be a string, skipping"
|
||||
" device.", device["name"].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: Missing 'name' field, skipping device.", device.getSourceLine());
|
||||
} catch(SettingNotFoundException &e) {
|
||||
logPrintf(WARN, "Line %d: Missing name field, skipping device."
|
||||
, device.getSourceLine());
|
||||
continue;
|
||||
}
|
||||
devices.insert({name, new DeviceConfig(device)});
|
||||
_device_paths.insert({name, device.getPath()});
|
||||
}
|
||||
}
|
||||
catch(const SettingNotFoundException &e) {
|
||||
logPrintf(WARN, "No devices listed in config file.");
|
||||
}
|
||||
|
||||
try {
|
||||
auto& ignore = root.lookup("ignore");
|
||||
if(ignore.getType() == libconfig::Setting::TypeInt) {
|
||||
_ignore_list.insert((int)ignore);
|
||||
} else if(ignore.isList() || ignore.isArray()) {
|
||||
int ignore_count = ignore.getLength();
|
||||
for(int i = 0; i < ignore_count; i++) {
|
||||
if(ignore[i].getType() != libconfig::Setting::TypeInt) {
|
||||
logPrintf(WARN, "Line %d: ignore must refer to device PIDs",
|
||||
ignore[i].getSourceLine());
|
||||
if(ignore.isArray())
|
||||
break;
|
||||
} else
|
||||
_ignore_list.insert((int)ignore[i]);
|
||||
}
|
||||
}
|
||||
} catch(const SettingNotFoundException& e) {
|
||||
// May be called blacklist
|
||||
try {
|
||||
auto& ignore = root.lookup("blacklist");
|
||||
if(ignore.getType() == libconfig::Setting::TypeInt) {
|
||||
_ignore_list.insert((int)ignore);
|
||||
} else if(ignore.isList() || ignore.isArray()) {
|
||||
int ignore_count = ignore.getLength();
|
||||
for(int i = 0; i < ignore_count; i++) {
|
||||
if(ignore[i].getType() != libconfig::Setting::TypeInt) {
|
||||
logPrintf(WARN, "Line %d: blacklist must refer to "
|
||||
"device PIDs",
|
||||
ignore[i].getSourceLine());
|
||||
if(ignore.isArray())
|
||||
break;
|
||||
} else
|
||||
_ignore_list.insert((int)ignore[i]);
|
||||
}
|
||||
}
|
||||
} catch(const SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeviceConfig::DeviceConfig(const libconfig::Setting &root)
|
||||
libconfig::Setting& Configuration::getSetting(const std::string& path)
|
||||
{
|
||||
try
|
||||
{
|
||||
int d;
|
||||
if(!root.lookupValue("dpi", d))
|
||||
throw SettingTypeException(root["dpi"]);
|
||||
dpi = new int(d);
|
||||
}
|
||||
catch(const SettingNotFoundException &e) { }
|
||||
catch(const SettingTypeException &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: DPI must me an integer; not setting.", root["dpi"].getSourceLine());
|
||||
return _config.lookup(path);
|
||||
}
|
||||
|
||||
try
|
||||
std::string Configuration::getDevice(const std::string& name)
|
||||
{
|
||||
const Setting& hr = root["hiresscroll"];
|
||||
uint8_t hss = 0;
|
||||
try
|
||||
{
|
||||
bool b;
|
||||
if(!hr.lookupValue("hires", b))
|
||||
throw SettingTypeException(root["hires"]);
|
||||
if(b) hss |= HIDPP20::IHiresScroll::HiRes;
|
||||
}
|
||||
catch(SettingNotFoundException &e) { }
|
||||
catch(SettingTypeException &e)
|
||||
{
|
||||
log_printf(INFO, "Line %d: hires field must be a boolean", hr["hires"].getSourceLine());
|
||||
auto it = _device_paths.find(name);
|
||||
if(it == _device_paths.end())
|
||||
throw DeviceNotFound(name);
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
try
|
||||
bool Configuration::isIgnored(uint16_t pid) const
|
||||
{
|
||||
bool b;
|
||||
if(!hr.lookupValue("invert", b))
|
||||
throw SettingTypeException(root["invert"]);
|
||||
if(b) hss |= HIDPP20::IHiresScroll::Inverted;
|
||||
}
|
||||
catch(SettingNotFoundException &e) { }
|
||||
catch(SettingTypeException &e)
|
||||
{
|
||||
log_printf(INFO, "Line %d: invert field must be a boolean", hr["invert"].getSourceLine());
|
||||
return _ignore_list.find(pid) != _ignore_list.end();
|
||||
}
|
||||
|
||||
try
|
||||
Configuration::DeviceNotFound::DeviceNotFound(std::string name) :
|
||||
_name (std::move(name))
|
||||
{
|
||||
bool b;
|
||||
if(!hr.lookupValue("target", b))
|
||||
throw SettingTypeException(root["target"]);
|
||||
if(b) hss |= HIDPP20::IHiresScroll::Target;
|
||||
}
|
||||
catch(SettingNotFoundException &e) { }
|
||||
catch(SettingTypeException &e)
|
||||
{
|
||||
log_printf(INFO, "Line %d: target field must be a boolean", hr["target"].getSourceLine());
|
||||
}
|
||||
|
||||
hiresscroll = new uint8_t(hss);
|
||||
}
|
||||
catch(const SettingNotFoundException &e)
|
||||
const char * Configuration::DeviceNotFound::what() const noexcept
|
||||
{
|
||||
log_printf(INFO, "Missing hiresscroll option, not setting.");
|
||||
}
|
||||
catch(const SettingTypeException &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: hiresscroll should be an object", root["hiresscroll"].getSourceLine());
|
||||
return _name.c_str();
|
||||
}
|
||||
|
||||
try
|
||||
int Configuration::workerCount() const
|
||||
{
|
||||
const Setting& ss = root["smartshift"];
|
||||
smartshift = new HIDPP20::ISmartShift::SmartshiftStatus {};
|
||||
bool on;
|
||||
int threshold;
|
||||
try
|
||||
{
|
||||
if (ss.lookupValue("on", on)) smartshift->Active = new bool(on);
|
||||
else log_printf(WARN, "Line %d: on field must be a boolean", ss["on"].getSourceLine());
|
||||
}
|
||||
catch(const SettingNotFoundException &e) { }
|
||||
|
||||
try
|
||||
{
|
||||
if (ss.lookupValue("threshold", threshold))
|
||||
{
|
||||
if(threshold < 0)
|
||||
{
|
||||
threshold = 1;
|
||||
log_printf(INFO, "Smartshift threshold must be > 0 or < 100, setting to 1.");
|
||||
}
|
||||
if(threshold >= 100)
|
||||
{
|
||||
threshold = 99;
|
||||
log_printf(INFO, "Smartshift threshold must be > 0 or < 100, setting to 99.");
|
||||
}
|
||||
smartshift->AutoDisengage = new uint8_t(threshold);
|
||||
smartshift->DefaultAutoDisengage = new uint8_t(threshold);
|
||||
}
|
||||
else log_printf(WARN, "Line %d: threshold must be an integer", ss["threshold"].getSourceLine());
|
||||
}
|
||||
catch(const SettingNotFoundException &e) { }
|
||||
}
|
||||
catch(const SettingNotFoundException &e) { }
|
||||
catch(const SettingTypeException &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: smartshift field must be an object", root["smartshift"].getSourceLine());
|
||||
return _worker_threads;
|
||||
}
|
||||
|
||||
Setting* buttons;
|
||||
try
|
||||
std::chrono::milliseconds Configuration::ioTimeout() const
|
||||
{
|
||||
buttons = &root["buttons"];
|
||||
}
|
||||
catch(const SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "No button configuration found, reverting to null config.");
|
||||
new std::map<uint16_t, ButtonAction*>();
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < buttons->getLength(); i++)
|
||||
{
|
||||
const Setting &button = (*buttons)[i];
|
||||
|
||||
int cid;
|
||||
try { button.lookupValue("cid", cid); }
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "Entry on line %d is missing a cid", button.getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
if(actions.find(cid) != actions.end())
|
||||
{
|
||||
log_printf(WARN, "Duplicate entries for cid 0x%x, skipping entry on line %d", cid, button.getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
Setting* action_config;
|
||||
try { action_config = &button["action"]; }
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "cid 0x%x is missing an action, not diverting!", cid);
|
||||
continue;
|
||||
}
|
||||
|
||||
Action action_type;
|
||||
try
|
||||
{
|
||||
std::string action_type_str;
|
||||
action_config->lookupValue("type", action_type_str);
|
||||
action_type = stringToAction(action_type_str);
|
||||
}
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "cid 0x%x is missing an action type, not diverting!", cid);
|
||||
continue;
|
||||
}
|
||||
catch(std::invalid_argument &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: %s", (*action_config)["type"].getSourceLine(), e.what());
|
||||
continue;
|
||||
}
|
||||
|
||||
try { actions.insert({cid, parse_action(action_type, action_config)}); }
|
||||
catch(std::exception &e) { log_printf(ERROR, "%s", e.what()); }
|
||||
}
|
||||
}
|
||||
|
||||
ButtonAction* logid::parse_action(Action type, const Setting* action_config, bool is_gesture)
|
||||
{
|
||||
if(type == Action::None) return new NoAction();
|
||||
if(type == Action::Keypress)
|
||||
{
|
||||
std::vector<unsigned int> keys;
|
||||
try
|
||||
{
|
||||
const Setting &keys_config = (*action_config)["keys"];
|
||||
for (int i = 0; i < keys_config.getLength(); i++)
|
||||
{
|
||||
int keycode = libevdev_event_code_from_name(EV_KEY, keys_config[i]);
|
||||
if(keycode == -1)
|
||||
{
|
||||
const char* keyname = keys_config[i];
|
||||
log_printf(WARN, "%s is not a valid keycode, skipping", keyname);
|
||||
}
|
||||
else keys.push_back(keycode);
|
||||
}
|
||||
}
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "Expected keys parameter on line %d", action_config->getSourceLine());
|
||||
}
|
||||
|
||||
return new KeyAction(keys);
|
||||
}
|
||||
else if(type == Action::Gestures)
|
||||
{
|
||||
if(is_gesture)
|
||||
{
|
||||
log_printf(WARN, "Line %d: Recursive gesture, defaulting to no action.", action_config->getSourceLine());
|
||||
return new NoAction();
|
||||
}
|
||||
std::map<Direction, Gesture*> gestures;
|
||||
|
||||
Setting* gestures_config;
|
||||
|
||||
try { gestures_config = &(*action_config)["gestures"]; }
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "No gestures parameter for line %d, skipping", action_config->getSourceLine());
|
||||
throw e;
|
||||
}
|
||||
|
||||
for(int i = 0; i < gestures_config->getLength(); i++)
|
||||
{
|
||||
const Setting &gesture_config = (*gestures_config)[i];
|
||||
|
||||
std::string direction_str;
|
||||
Direction direction;
|
||||
try
|
||||
{
|
||||
gesture_config.lookupValue("direction", direction_str);
|
||||
direction = stringToDirection(direction_str);
|
||||
}
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "No direction set on line %d", gesture_config.getSourceLine());
|
||||
continue;
|
||||
}
|
||||
catch(std::invalid_argument &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: %s", gesture_config["direction"].getSourceLine(), e.what());
|
||||
continue;
|
||||
}
|
||||
|
||||
if(gestures.find(direction) != gestures.end())
|
||||
{
|
||||
log_printf(WARN, "Entry on line %d is a duplicate, skipping...", gesture_config["direction"].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
GestureMode mode;
|
||||
try
|
||||
{
|
||||
std::string mode_str;
|
||||
gesture_config.lookupValue("mode", mode_str);
|
||||
mode = stringToGestureMode(mode_str);
|
||||
}
|
||||
catch (SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(INFO, "Gesture mode on line %d not found, defaulting to OnRelease", gesture_config.getSourceLine());
|
||||
mode = GestureMode::OnRelease;
|
||||
}
|
||||
|
||||
if(mode == GestureMode::NoPress)
|
||||
{
|
||||
gestures.insert({direction, new Gesture(new NoAction(), mode)});
|
||||
continue;
|
||||
}
|
||||
|
||||
if(mode == GestureMode::Axis)
|
||||
{
|
||||
Gesture::axis_info axis;
|
||||
try
|
||||
{
|
||||
std::string axis_str;
|
||||
if(!gesture_config.lookupValue("axis", axis_str))
|
||||
throw SettingTypeException(gesture_config["axis"]);
|
||||
axis.code = libevdev_event_code_from_name(EV_REL, axis_str.c_str());
|
||||
}
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: No axis found, defaulting to no action.", gesture_config.getSourceLine());
|
||||
gestures.insert({direction, new Gesture(new NoAction(), GestureMode::NoPress)});
|
||||
continue;
|
||||
}
|
||||
catch(SettingTypeException &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: Axis must be a string (e.g. 'REL_WHEEL')", gesture_config["axis"].getSourceLine());
|
||||
gestures.insert({direction, new Gesture(new NoAction(), GestureMode::NoPress)});
|
||||
continue;
|
||||
}
|
||||
|
||||
axis.multiplier = 1;
|
||||
try
|
||||
{
|
||||
if(!gesture_config.lookupValue("axis_multiplier", axis.multiplier))
|
||||
{
|
||||
int im = 1;
|
||||
if(!gesture_config.lookupValue("axis_multiplier", im))
|
||||
throw SettingTypeException(gesture_config["axis_multiplier"]);
|
||||
axis.multiplier = (float)im;
|
||||
}
|
||||
}
|
||||
catch(SettingNotFoundException &e) { }
|
||||
catch(SettingTypeException &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: axis_multiplier must be a float/integer", gesture_config["axis_multiplier"].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
gestures.insert({direction, new Gesture(new NoAction(), GestureMode::Axis, &axis)});
|
||||
continue;
|
||||
}
|
||||
|
||||
Setting* g_action;
|
||||
try { g_action = &gesture_config["action"]; }
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "No action set for %s", direction_str.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
Action g_type;
|
||||
try
|
||||
{
|
||||
std::string type_str;
|
||||
g_action->lookupValue("type", type_str);
|
||||
g_type = stringToAction(type_str);
|
||||
}
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(INFO, "Missing an action type on line %d, skipping", g_action->getSourceLine());
|
||||
continue;
|
||||
}
|
||||
catch(std::invalid_argument &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: %s", (*g_action)["type"].getSourceLine(), e.what());
|
||||
continue;
|
||||
}
|
||||
|
||||
ButtonAction* ba;
|
||||
|
||||
try { ba = parse_action(g_type, g_action, true); }
|
||||
catch(std::exception &e) { continue; }
|
||||
|
||||
if(mode == GestureMode::OnFewPixels)
|
||||
{
|
||||
try
|
||||
{
|
||||
int pp;
|
||||
if(!gesture_config.lookupValue("pixels", pp))
|
||||
throw SettingTypeException(gesture_config["pixels"]);
|
||||
gestures.insert({direction, new Gesture(ba, mode, &pp)});
|
||||
}
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: OnFewPixels requires a 'pixels' field.", gesture_config.getSourceLine());
|
||||
}
|
||||
catch(SettingTypeException &e)
|
||||
{
|
||||
log_printf(WARN, "Line %d: pixels must be an integer", gesture_config["pixels"].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else gestures.insert({direction, new Gesture(ba, mode)});
|
||||
}
|
||||
|
||||
return new GestureAction(gestures);
|
||||
}
|
||||
else if(type == Action::ToggleSmartshift) return new SmartshiftAction();
|
||||
else if(type == Action::ToggleHiresScroll) return new HiresScrollAction();
|
||||
else if(type == Action::CycleDPI)
|
||||
{
|
||||
std::vector<int> dpis;
|
||||
try
|
||||
{
|
||||
const Setting &keys_config = (*action_config)["dpis"];
|
||||
for (int i = 0; i < keys_config.getLength(); i++)
|
||||
{
|
||||
dpis.push_back((int)keys_config[i]);
|
||||
}
|
||||
}
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(ERROR, "Line %d: CycleDPI action is missing 'dpis' field, defaulting to NoAction.", action_config->getSourceLine());
|
||||
}
|
||||
|
||||
return new CycleDPIAction(dpis);
|
||||
}
|
||||
else if(type == Action::ChangeDPI)
|
||||
{
|
||||
int inc;
|
||||
try
|
||||
{
|
||||
action_config->lookupValue("inc", inc);
|
||||
}
|
||||
catch(SettingNotFoundException &e)
|
||||
{
|
||||
log_printf(ERROR, "Line %d: ChangeDPI action is missing an 'inc' field, defaulting to NoAction.",action_config->getSourceLine());
|
||||
return new NoAction();
|
||||
}
|
||||
|
||||
return new ChangeDPIAction(inc);
|
||||
}
|
||||
|
||||
log_printf(ERROR, "This shouldn't have happened. Unhandled action type? Defaulting to NoAction");
|
||||
return new NoAction();
|
||||
}
|
||||
|
||||
DeviceConfig::DeviceConfig(DeviceConfig* dc, Device* dev) : baseConfig (false)
|
||||
{
|
||||
dpi = dc->dpi;
|
||||
smartshift = dc->smartshift;
|
||||
hiresscroll = dc->hiresscroll;
|
||||
for(auto it : dc->actions)
|
||||
actions.insert( { it.first, it.second->copy(dev) } );
|
||||
}
|
||||
|
||||
DeviceConfig::DeviceConfig()
|
||||
{
|
||||
dpi = nullptr;
|
||||
hiresscroll = nullptr;
|
||||
smartshift = nullptr;
|
||||
actions = {};
|
||||
}
|
||||
|
||||
DeviceConfig::~DeviceConfig()
|
||||
{
|
||||
for(auto it : this->actions)
|
||||
delete(it.second);
|
||||
return _io_timeout;
|
||||
}
|
||||
|
|
|
@ -1,45 +1,64 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_CONFIGURATION_H
|
||||
#define LOGID_CONFIGURATION_H
|
||||
|
||||
#include <map>
|
||||
#include <libconfig.h++>
|
||||
#include <hidpp20/ISmartShift.h>
|
||||
#include "Actions.h"
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
|
||||
#define LOGID_DEFAULT_IO_TIMEOUT std::chrono::seconds(2)
|
||||
#define LOGID_DEFAULT_WORKER_COUNT 4
|
||||
|
||||
namespace logid
|
||||
{
|
||||
class DeviceConfig;
|
||||
class ButtonAction;
|
||||
enum class Action;
|
||||
|
||||
class DeviceConfig
|
||||
{
|
||||
public:
|
||||
DeviceConfig();
|
||||
~DeviceConfig();
|
||||
DeviceConfig(DeviceConfig* dc, Device* dev);
|
||||
DeviceConfig(const libconfig::Setting& root);
|
||||
const int* dpi = nullptr;
|
||||
HIDPP20::ISmartShift::SmartshiftStatus* smartshift = nullptr;
|
||||
const uint8_t* hiresscroll = nullptr;
|
||||
std::map<uint16_t, ButtonAction*> actions;
|
||||
const bool baseConfig = true;
|
||||
};
|
||||
|
||||
class Configuration
|
||||
{
|
||||
public:
|
||||
Configuration(const char* config_file);
|
||||
Configuration() {}
|
||||
std::map<std::string, DeviceConfig*> devices;
|
||||
std::vector<uint16_t> blacklist;
|
||||
explicit Configuration(const std::string& config_file);
|
||||
Configuration() = default;
|
||||
libconfig::Setting& getSetting(const std::string& path);
|
||||
std::string getDevice(const std::string& name);
|
||||
bool isIgnored(uint16_t pid) const;
|
||||
|
||||
class DeviceNotFound : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit DeviceNotFound(std::string name);
|
||||
const char* what() const noexcept override;
|
||||
private:
|
||||
libconfig::Config cfg;
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
ButtonAction* parse_action(Action action, const libconfig::Setting* action_config, bool is_gesture=false);
|
||||
std::chrono::milliseconds ioTimeout() const;
|
||||
int workerCount() const;
|
||||
private:
|
||||
std::map<std::string, std::string> _device_paths;
|
||||
std::set<uint16_t> _ignore_list;
|
||||
std::chrono::milliseconds _io_timeout = LOGID_DEFAULT_IO_TIMEOUT;
|
||||
int _worker_threads = LOGID_DEFAULT_WORKER_COUNT;
|
||||
libconfig::Config _config;
|
||||
};
|
||||
|
||||
extern Configuration* global_config;
|
||||
extern std::shared_ptr<Configuration> global_config;
|
||||
}
|
||||
|
||||
#endif //LOGID_CONFIGURATION_H
|
|
@ -1,551 +1,139 @@
|
|||
#include <cstdint>
|
||||
#include <future>
|
||||
#include <unistd.h>
|
||||
#include <hidpp/SimpleDispatcher.h>
|
||||
#include <hidpp20/IAdjustableDPI.h>
|
||||
#include <hidpp20/IFeatureSet.h>
|
||||
#include <hidpp20/Error.h>
|
||||
#include <hidpp20/IReprogControls.h>
|
||||
#include <hidpp20/IReset.h>
|
||||
#include <hidpp20/ISmartShift.h>
|
||||
#include <hidpp20/Device.h>
|
||||
#include <hidpp10/Error.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <set>
|
||||
#include <hidpp20/UnsupportedFeature.h>
|
||||
#include <hidpp20/IHiresScroll.h>
|
||||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "util/log.h"
|
||||
#include "features/DPI.h"
|
||||
#include "Device.h"
|
||||
#include "util.h"
|
||||
#include "EvdevDevice.h"
|
||||
#include "features/SmartShift.h"
|
||||
#include "features/RemapButton.h"
|
||||
#include "backend/hidpp20/features/Reset.h"
|
||||
#include "features/HiresScroll.h"
|
||||
|
||||
using namespace logid;
|
||||
using namespace logid::backend;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
Device::Device(std::string p, const HIDPP::DeviceIndex i) : path(std::move(p)), index (i)
|
||||
Device::Device(std::string path, backend::hidpp::DeviceIndex index) :
|
||||
_hidpp20 (path, index), _path (std::move(path)), _index (index),
|
||||
_config (global_config, this)
|
||||
{
|
||||
disconnected = true;
|
||||
dispatcher = new HIDPP::SimpleDispatcher(path.c_str());
|
||||
listener = new SimpleListener(new HIDPP::SimpleDispatcher(path.c_str()), index);
|
||||
config = new DeviceConfig();
|
||||
_init();
|
||||
}
|
||||
|
||||
bool Device::init()
|
||||
Device::Device(const std::shared_ptr<backend::raw::RawDevice>& raw_device,
|
||||
hidpp::DeviceIndex index) : _hidpp20(raw_device, index), _path
|
||||
(raw_device->hidrawPath()), _index (index),
|
||||
_config (global_config, this)
|
||||
{
|
||||
// Initialise variables
|
||||
disconnected = false;
|
||||
try
|
||||
{
|
||||
hidpp_dev = new HIDPP20::Device(dispatcher, index);
|
||||
}
|
||||
catch(HIDPP10::Error &e) { return false; }
|
||||
catch(HIDPP20::Error &e) { return false; }
|
||||
|
||||
name = hidpp_dev->name();
|
||||
|
||||
if(std::find(global_config->blacklist.begin(), global_config->blacklist.end(),
|
||||
hidpp_dev->productID()) != global_config->blacklist.end())
|
||||
{
|
||||
log_printf(INFO, "Ignored blacklisted device %s", name.c_str());
|
||||
throw BlacklistedDevice();
|
||||
_init();
|
||||
}
|
||||
|
||||
features = getFeatures();
|
||||
// Set config, if none is found for this device then use default
|
||||
if(global_config->devices.find(name) == global_config->devices.end())
|
||||
log_printf(INFO, "Device %s not configured, using default config.", hidpp_dev->name().c_str());
|
||||
else
|
||||
void Device::_init()
|
||||
{
|
||||
delete(config);
|
||||
config = global_config->devices.find(name)->second;
|
||||
logPrintf(INFO, "Device found: %s on %s:%d", name().c_str(),
|
||||
hidpp20().devicePath().c_str(), _index);
|
||||
|
||||
_addFeature<features::DPI>("dpi");
|
||||
_addFeature<features::SmartShift>("smartshift");
|
||||
_addFeature<features::HiresScroll>("hiresscroll");
|
||||
_addFeature<features::RemapButton>("remapbutton");
|
||||
|
||||
_makeResetMechanism();
|
||||
reset();
|
||||
|
||||
for(auto& feature: _features) {
|
||||
feature.second->configure();
|
||||
feature.second->listen();
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
return true;
|
||||
_hidpp20.listen();
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
std::string Device::name()
|
||||
{
|
||||
if(!disconnected)
|
||||
this->reset();
|
||||
if(!config->baseConfig)
|
||||
delete(this->config);
|
||||
return _hidpp20.name();
|
||||
}
|
||||
|
||||
void Device::configure()
|
||||
uint16_t Device::pid()
|
||||
{
|
||||
if(config->baseConfig)
|
||||
config = new DeviceConfig(config, this);
|
||||
|
||||
if(!configuring.try_lock())
|
||||
{
|
||||
log_printf(DEBUG, "%s %d: skip config task", path.c_str(), index);
|
||||
return;
|
||||
}
|
||||
usleep(500000);
|
||||
|
||||
try
|
||||
{
|
||||
if(disconnected)
|
||||
goto ret;
|
||||
// Divert buttons
|
||||
divert_buttons();
|
||||
|
||||
if(disconnected)
|
||||
goto ret;
|
||||
// Set DPI if it is configured
|
||||
if(config->dpi != nullptr)
|
||||
setDPI(*config->dpi);
|
||||
|
||||
if(disconnected)
|
||||
goto ret;
|
||||
// Set Smartshift if it is configured
|
||||
if(config->smartshift != nullptr)
|
||||
setSmartShift(*config->smartshift);
|
||||
|
||||
if(disconnected)
|
||||
goto ret;
|
||||
// Set Hires Scroll if it is configured
|
||||
if(config->hiresscroll != nullptr)
|
||||
setHiresScroll(*config->hiresscroll);
|
||||
}
|
||||
catch(HIDPP10::Error &e)
|
||||
{
|
||||
log_printf(ERROR, "HID++ 1.0 Error whjle configuring %s: %s", name.c_str(), e.what());
|
||||
return _hidpp20.pid();
|
||||
}
|
||||
|
||||
ret:
|
||||
configuring.unlock();
|
||||
void Device::sleep()
|
||||
{
|
||||
logPrintf(INFO, "%s:%d fell asleep.", _path.c_str(), _index);
|
||||
}
|
||||
|
||||
void Device::wakeup()
|
||||
{
|
||||
logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
reset();
|
||||
|
||||
for(auto& feature: _features)
|
||||
feature.second->configure();
|
||||
}
|
||||
|
||||
void Device::reset()
|
||||
{
|
||||
try
|
||||
{
|
||||
HIDPP20::IReset iReset(hidpp_dev);
|
||||
iReset.reset();
|
||||
}
|
||||
catch(HIDPP20::UnsupportedFeature &e) { }
|
||||
catch(HIDPP10::Error &e)
|
||||
{
|
||||
log_printf(ERROR, "Failed to reset %s: %s", name.c_str(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void Device::divert_buttons()
|
||||
{
|
||||
try
|
||||
{
|
||||
HIDPP20::IReprogControls irc = HIDPP20::IReprogControls::auto_version(hidpp_dev);
|
||||
if(disconnected)
|
||||
return;
|
||||
int controlCount = irc.getControlCount();
|
||||
for(int i = 0; i < controlCount; i++)
|
||||
{
|
||||
if(disconnected)
|
||||
return;
|
||||
uint16_t cid = irc.getControlInfo(i).control_id;
|
||||
uint8_t flags = 0;
|
||||
flags |= HIDPP20::IReprogControls::ChangeTemporaryDivert;
|
||||
flags |= HIDPP20::IReprogControls::ChangeRawXYDivert;
|
||||
|
||||
auto action = config->actions.find(cid);
|
||||
if(action != config->actions.end())
|
||||
{
|
||||
flags |= HIDPP20::IReprogControls::ChangeTemporaryDivert;
|
||||
flags |= HIDPP20::IReprogControls::TemporaryDiverted;
|
||||
if(action->second->type == Action::Gestures)
|
||||
flags |= HIDPP20::IReprogControls::RawXYDiverted;
|
||||
}
|
||||
if(disconnected)
|
||||
return;
|
||||
irc.setControlReporting(cid, flags, cid);
|
||||
}
|
||||
}
|
||||
catch(HIDPP20::UnsupportedFeature &e)
|
||||
{
|
||||
log_printf(DEBUG, "%s does not support Reprog controls, not diverting!", name.c_str());
|
||||
}
|
||||
catch(HIDPP20::Error &e)
|
||||
{
|
||||
if(e.errorCode() == HIDPP20::Error::InvalidFunctionID) {
|
||||
// Not really an error, the device does not support diverting buttons
|
||||
}
|
||||
else {
|
||||
log_printf(ERROR, "Could not divert buttons: HID++ 2.0 Error %s!", e.what());
|
||||
}
|
||||
}
|
||||
catch(HIDPP10::Error &e)
|
||||
{
|
||||
log_printf(DEBUG, "Could not divert buttons: HID++ 1.0 Error %s!", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void Device::setSmartShift(HIDPP20::ISmartShift::SmartshiftStatus ops)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(disconnected) return;
|
||||
HIDPP20::ISmartShift ss(hidpp_dev);
|
||||
if(disconnected) return;
|
||||
ss.setStatus(ops);
|
||||
}
|
||||
catch (HIDPP20::UnsupportedFeature &e)
|
||||
{
|
||||
log_printf(ERROR, "Device does not support SmartShift");
|
||||
}
|
||||
catch (HIDPP20::Error &e)
|
||||
{
|
||||
log_printf(ERROR, "Error setting SmartShift options, code %d: %s\n", e.errorCode(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void Device::setHiresScroll(uint8_t ops)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(disconnected) return;
|
||||
HIDPP20::IHiresScroll hs(hidpp_dev);
|
||||
if(disconnected) return;
|
||||
hs.setMode(ops);
|
||||
}
|
||||
catch (HIDPP20::UnsupportedFeature &e)
|
||||
{
|
||||
log_printf(ERROR, "Device does not support Hires Scrolling");
|
||||
}
|
||||
catch (HIDPP20::Error &e)
|
||||
{
|
||||
log_printf(ERROR, "Error setting Hires Scrolling options, code %d: %s\n", e.errorCode(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void Device::setDPI(int dpi)
|
||||
{
|
||||
if(disconnected) return;
|
||||
HIDPP20::IAdjustableDPI iad(hidpp_dev);
|
||||
if(disconnected) return;
|
||||
try { for(unsigned int i = 0; i < iad.getSensorCount(); i++) iad.setSensorDPI(i, dpi); }
|
||||
catch (HIDPP20::Error &e) { log_printf(ERROR, "Error while setting DPI: %s", e.what()); }
|
||||
}
|
||||
|
||||
void Device::waitForReceiver()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
waiting_for_receiver = true;
|
||||
listener->addEventHandler(std::make_unique<ReceiverHandler>(this));
|
||||
listener->start();
|
||||
// Listener stopped, check if stopped or ReceiverHandler event
|
||||
if (waiting_for_receiver)
|
||||
return;
|
||||
|
||||
usleep(200000);
|
||||
|
||||
try
|
||||
{
|
||||
if(this->init()) break;
|
||||
}
|
||||
catch(BlacklistedDevice& e) { return; }
|
||||
|
||||
log_printf(ERROR, "Failed to initialize device %d on %s, waiting for receiver");
|
||||
delete(listener);
|
||||
listener = new SimpleListener(new HIDPP::SimpleDispatcher(path.c_str()), index);
|
||||
}
|
||||
log_printf(INFO, "%s detected: device %d on %s", name.c_str(), index, path.c_str());
|
||||
this->start();
|
||||
}
|
||||
|
||||
void Device::printCIDs() {
|
||||
try
|
||||
{
|
||||
HIDPP20::IReprogControls irc = HIDPP20::IReprogControls::auto_version(hidpp_dev);
|
||||
if(disconnected)
|
||||
return;
|
||||
int controlCount = irc.getControlCount();
|
||||
for(int i = 0; i < controlCount; i++)
|
||||
{
|
||||
if(disconnected)
|
||||
return;
|
||||
uint16_t cid = irc.getControlInfo(i).control_id;
|
||||
log_printf(DEBUG, "Available CID: 0x%x", cid);
|
||||
}
|
||||
}
|
||||
catch(HIDPP20::UnsupportedFeature &e)
|
||||
{
|
||||
log_printf(DEBUG, "%s does not support Reprog controls, not diverting!", name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Device::start()
|
||||
{
|
||||
printCIDs();
|
||||
configure();
|
||||
try { listener->addEventHandler(std::make_unique<ButtonHandler>(this)); }
|
||||
catch(HIDPP20::UnsupportedFeature &e) { }
|
||||
|
||||
if(index == HIDPP::DefaultDevice || index == HIDPP::CordedDevice)
|
||||
{
|
||||
try { listener->addEventHandler( std::make_unique<WirelessStatusHandler>(this) ); }
|
||||
catch(HIDPP20::UnsupportedFeature &e) { }
|
||||
}
|
||||
listener->start();
|
||||
}
|
||||
|
||||
bool Device::testConnection()
|
||||
{
|
||||
int i = MAX_CONNECTION_TRIES;
|
||||
do {
|
||||
try
|
||||
{
|
||||
HIDPP20::Device _hpp20dev(dispatcher, index);
|
||||
return true;
|
||||
}
|
||||
catch(HIDPP10::Error &e)
|
||||
{
|
||||
if(e.errorCode() == HIDPP10::Error::ResourceError) // Asleep, wait for next event
|
||||
return false;
|
||||
if(i == MAX_CONNECTION_TRIES-1)
|
||||
return false;
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
if(i == MAX_CONNECTION_TRIES-1)
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
} while(i < MAX_CONNECTION_TRIES);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ButtonHandler::handleEvent (const HIDPP::Report &event)
|
||||
{
|
||||
switch (event.function())
|
||||
{
|
||||
case HIDPP20::IReprogControls::Event::DivertedButtonEvent:
|
||||
{
|
||||
new_states = HIDPP20::IReprogControls::divertedButtonEvent(event);
|
||||
if (states.empty())
|
||||
{
|
||||
for (uint16_t i : new_states)
|
||||
std::thread{[=]() { dev->pressButton(i); }}.detach();
|
||||
states = new_states;
|
||||
break;
|
||||
}
|
||||
std::vector<uint16_t>::iterator it;
|
||||
std::vector<uint16_t> cids(states.size() + new_states.size());
|
||||
it = std::set_union(states.begin(), states.end(), new_states.begin(), new_states.end(), cids.begin());
|
||||
cids.resize((ulong)(it - cids.begin()));
|
||||
for (uint16_t i : cids)
|
||||
{
|
||||
if (std::find(new_states.begin(), new_states.end(), i) != new_states.end())
|
||||
{
|
||||
if (std::find(states.begin(), states.end(), i) == states.end())
|
||||
std::thread{[=]() { dev->pressButton(i); }}.detach();
|
||||
} else
|
||||
std::thread{[=]() { dev->releaseButton(i); }}.detach();
|
||||
}
|
||||
states = new_states;
|
||||
break;
|
||||
}
|
||||
case HIDPP20::IReprogControlsV4::Event::DivertedRawXYEvent:
|
||||
{
|
||||
auto raw_xy = HIDPP20::IReprogControlsV4::divertedRawXYEvent(event);
|
||||
|
||||
for(uint16_t i : states)
|
||||
std::thread{[=]() { dev->moveDiverted(i, raw_xy); }}.detach();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiverHandler::handleEvent(const HIDPP::Report &event)
|
||||
{
|
||||
switch(event.featureIndex())
|
||||
{
|
||||
case HIDPP10::IReceiver::DeviceUnpaired:
|
||||
{
|
||||
log_printf(INFO, "%s (Device %d on %s) unpaired from receiver", dev->name.c_str(), dev->index, dev->path.c_str());
|
||||
std::thread {[=]()
|
||||
{
|
||||
finder->stopAndDeleteDevice(dev->path, dev->index);
|
||||
finder->insertNewReceiverDevice(dev->path, dev->index);
|
||||
}}.detach();
|
||||
break;
|
||||
}
|
||||
case HIDPP10::IReceiver::DevicePaired:
|
||||
{
|
||||
log_printf(DEBUG, "Receiver on %s: Device %d paired", dev->path.c_str(), event.deviceIndex());
|
||||
if(dev->waiting_for_receiver)
|
||||
{
|
||||
if(!dev->testConnection()) return;
|
||||
dev->waiting_for_receiver = false;
|
||||
dev->stop();
|
||||
}
|
||||
//else: Likely an enumeration event, ignore.
|
||||
break;
|
||||
}
|
||||
case HIDPP10::IReceiver::ConnectionStatus:
|
||||
{
|
||||
auto status = HIDPP10::IReceiver::connectionStatusEvent(event);
|
||||
if(status == HIDPP10::IReceiver::LinkLoss)
|
||||
{
|
||||
log_printf(INFO, "Link lost to %s", dev->name.c_str());
|
||||
dev->disconnected = true;
|
||||
}
|
||||
else if (status == HIDPP10::IReceiver::ConnectionEstablished)
|
||||
{
|
||||
if(dev->waiting_for_receiver)
|
||||
{
|
||||
log_printf(DEBUG, "Receiver on %s: Connection established to device %d", dev->path.c_str(), event.deviceIndex());
|
||||
if(!dev->testConnection()) return;
|
||||
dev->waiting_for_receiver = false;
|
||||
std::thread { [=]() { dev->stop(); } }.detach();
|
||||
}
|
||||
if(_reset_mechanism)
|
||||
(*_reset_mechanism)();
|
||||
else
|
||||
{
|
||||
if(!dev->initialized) return;
|
||||
dev->disconnected = false;
|
||||
dev->configure();
|
||||
log_printf(INFO, "Connection established to %s", dev->name.c_str());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
logPrintf(DEBUG, "%s:%d tried to reset, but no reset mechanism was "
|
||||
"available.", _path.c_str(), _index);
|
||||
}
|
||||
|
||||
void WirelessStatusHandler::handleEvent(const HIDPP::Report &event)
|
||||
DeviceConfig& Device::config()
|
||||
{
|
||||
switch(event.function())
|
||||
{
|
||||
case HIDPP20::IWirelessDeviceStatus::StatusBroadcast:
|
||||
{
|
||||
auto status = HIDPP20::IWirelessDeviceStatus::statusBroadcastEvent(event);
|
||||
if(status.ReconfNeeded)
|
||||
dev->configure();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
log_printf(DEBUG, "Undocumented event %02x from WirelessDeviceStatus", event.function());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return _config;
|
||||
}
|
||||
|
||||
void EventListener::removeEventHandlers ()
|
||||
hidpp20::Device& Device::hidpp20()
|
||||
{
|
||||
for (const auto &p: iterators)
|
||||
dispatcher->unregisterEventHandler(p.second);
|
||||
handlers.clear();
|
||||
iterators.clear();
|
||||
return _hidpp20;
|
||||
}
|
||||
|
||||
EventListener::~EventListener()
|
||||
void Device::_makeResetMechanism()
|
||||
{
|
||||
removeEventHandlers();
|
||||
}
|
||||
|
||||
void EventListener::addEventHandler(std::unique_ptr<EventHandler> &&handler)
|
||||
{
|
||||
EventHandler *ptr = handler.get();
|
||||
for(uint8_t feature : handler->featureIndices())
|
||||
{
|
||||
handlers.emplace(feature, std::move(handler));
|
||||
dispatcher->registerEventHandler(index, feature, [=](const HIDPP::Report &report)
|
||||
{
|
||||
ptr->handleEvent(report);
|
||||
return true;
|
||||
try {
|
||||
hidpp20::Reset reset(&_hidpp20);
|
||||
_reset_mechanism = std::make_unique<std::function<void()>>(
|
||||
[dev=&this->_hidpp20]{
|
||||
hidpp20::Reset reset(dev);
|
||||
reset.reset(reset.getProfile());
|
||||
});
|
||||
} catch(hidpp20::UnsupportedFeature& e) {
|
||||
// Reset unsupported, ignore.
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleListener::start()
|
||||
DeviceConfig::DeviceConfig(const std::shared_ptr<Configuration>& config, Device*
|
||||
device) : _device (device), _config (config)
|
||||
{
|
||||
bool retry;
|
||||
do
|
||||
{
|
||||
retry = false;
|
||||
try { dispatcher->listen(); }
|
||||
catch(std::system_error &e)
|
||||
{
|
||||
retry = true;
|
||||
usleep(250000);
|
||||
}
|
||||
} while(retry && !stopped);
|
||||
|
||||
}
|
||||
|
||||
void SimpleListener::stop()
|
||||
{
|
||||
this->stopped = true;
|
||||
dispatcher->stop();
|
||||
}
|
||||
|
||||
bool SimpleListener::event (EventHandler *handler, const HIDPP::Report &report)
|
||||
{
|
||||
handler->handleEvent (report);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Device::stop()
|
||||
{
|
||||
disconnected = true;
|
||||
listener->stop();
|
||||
}
|
||||
|
||||
void Device::pressButton(uint16_t cid)
|
||||
{
|
||||
if(config->actions.find(cid) == config->actions.end())
|
||||
{
|
||||
log_printf(DEBUG, "0x%x was pressed but no action was found.", cid);
|
||||
return;
|
||||
}
|
||||
config->actions.find(cid)->second->press();
|
||||
}
|
||||
|
||||
void Device::releaseButton(uint16_t cid)
|
||||
{
|
||||
if(config->actions.find(cid) == config->actions.end())
|
||||
{
|
||||
log_printf(DEBUG, "0x%x was released but no action was found.", cid);
|
||||
return;
|
||||
}
|
||||
config->actions.find(cid)->second->release();
|
||||
}
|
||||
|
||||
void Device::moveDiverted(uint16_t cid, HIDPP20::IReprogControlsV4::Move m)
|
||||
{
|
||||
auto action = config->actions.find(cid);
|
||||
if(action == config->actions.end())
|
||||
return;
|
||||
switch(action->second->type)
|
||||
{
|
||||
case Action::Gestures:
|
||||
((GestureAction*)action->second)->move(m);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
try {
|
||||
_root_setting = config->getDevice(device->name());
|
||||
} catch(Configuration::DeviceNotFound& e) {
|
||||
logPrintf(INFO, "Device %s not configured, using default config.",
|
||||
device->name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::map<uint16_t, uint8_t> Device::getFeatures()
|
||||
libconfig::Setting& DeviceConfig::getSetting(const std::string& path)
|
||||
{
|
||||
std::map<uint16_t, uint8_t> _features;
|
||||
HIDPP20::IFeatureSet ifs (hidpp_dev);
|
||||
uint8_t feature_count = ifs.getCount();
|
||||
|
||||
for(uint8_t i = 0; i < feature_count; i++)
|
||||
_features.insert( {i, ifs.getFeatureID(i) } );
|
||||
|
||||
return _features;
|
||||
return _config->getSetting(_root_setting + '/' + path);
|
||||
}
|
||||
|
|
|
@ -1,172 +1,106 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_DEVICE_H
|
||||
#define LOGID_DEVICE_H
|
||||
|
||||
#include "Actions.h"
|
||||
#include "DeviceFinder.h"
|
||||
#include "backend/hidpp/defs.h"
|
||||
#include "backend/hidpp20/Device.h"
|
||||
#include "backend/hidpp20/Feature.h"
|
||||
#include "features/DeviceFeature.h"
|
||||
#include "Configuration.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <hidpp/Dispatcher.h>
|
||||
#include <hidpp/SimpleDispatcher.h>
|
||||
#include <hidpp10/IReceiver.h>
|
||||
#include <hidpp20/IWirelessDeviceStatus.h>
|
||||
#include "util/log.h"
|
||||
|
||||
namespace logid
|
||||
{
|
||||
class EventListener;
|
||||
class DeviceConfig;
|
||||
class Device;
|
||||
|
||||
class BlacklistedDevice : public std::exception
|
||||
class DeviceConfig
|
||||
{
|
||||
public:
|
||||
BlacklistedDevice() = default;
|
||||
virtual const char* what()
|
||||
{
|
||||
return "Blacklisted device";
|
||||
}
|
||||
DeviceConfig(const std::shared_ptr<Configuration>& config, Device*
|
||||
device);
|
||||
libconfig::Setting& getSetting(const std::string& path);
|
||||
private:
|
||||
Device* _device;
|
||||
std::string _root_setting;
|
||||
std::shared_ptr<Configuration> _config;
|
||||
};
|
||||
|
||||
/* TODO: Implement HID++ 1.0 support
|
||||
* Currently, the logid::Device class has a hardcoded requirement
|
||||
* for an HID++ 2.0 device.
|
||||
*/
|
||||
class Device
|
||||
{
|
||||
public:
|
||||
Device(std::string p, const HIDPP::DeviceIndex i);
|
||||
~Device();
|
||||
Device(std::string path, backend::hidpp::DeviceIndex index);
|
||||
Device(const std::shared_ptr<backend::raw::RawDevice>& raw_device,
|
||||
backend::hidpp::DeviceIndex index);
|
||||
|
||||
std::string name;
|
||||
std::string name();
|
||||
uint16_t pid();
|
||||
|
||||
DeviceConfig& config();
|
||||
backend::hidpp20::Device& hidpp20();
|
||||
|
||||
void wakeup();
|
||||
void sleep();
|
||||
|
||||
bool init();
|
||||
void configure();
|
||||
void reset();
|
||||
|
||||
void pressButton(uint16_t cid);
|
||||
void releaseButton(uint16_t cid);
|
||||
void moveDiverted(uint16_t cid, HIDPP20::IReprogControlsV4::Move move);
|
||||
|
||||
void waitForReceiver();
|
||||
void start();
|
||||
void stop();
|
||||
bool testConnection();
|
||||
|
||||
std::map<uint16_t, uint8_t> getFeatures();
|
||||
|
||||
std::map<uint16_t, uint8_t> features;
|
||||
|
||||
const std::string path;
|
||||
const HIDPP::DeviceIndex index;
|
||||
HIDPP::Dispatcher* dispatcher;
|
||||
HIDPP20::Device* hidpp_dev;
|
||||
|
||||
std::mutex configuring;
|
||||
std::atomic_bool disconnected;
|
||||
bool initialized = false;
|
||||
bool waiting_for_receiver = false;
|
||||
|
||||
protected:
|
||||
DeviceConfig* config;
|
||||
EventListener* listener;
|
||||
|
||||
void divert_buttons();
|
||||
void printCIDs();
|
||||
void setSmartShift(HIDPP20::ISmartShift::SmartshiftStatus ops);
|
||||
void setHiresScroll(uint8_t flags);
|
||||
void setDPI(int dpi);
|
||||
};
|
||||
|
||||
class EventHandler
|
||||
{
|
||||
public:
|
||||
virtual const HIDPP20::FeatureInterface *feature() const = 0;
|
||||
virtual const std::vector<uint8_t> featureIndices() const
|
||||
{
|
||||
return {feature()->index()};
|
||||
};
|
||||
virtual void handleEvent (const HIDPP::Report &event) = 0;
|
||||
};
|
||||
class ButtonHandler : public EventHandler
|
||||
{
|
||||
public:
|
||||
ButtonHandler (Device *d) : dev (d), _irc (HIDPP20::IReprogControls::auto_version(d->hidpp_dev)) { }
|
||||
const HIDPP20::FeatureInterface *feature () const
|
||||
{
|
||||
return &_irc;
|
||||
template<typename T>
|
||||
std::shared_ptr<T> getFeature(std::string name) {
|
||||
auto it = _features.find(name);
|
||||
if(it == _features.end())
|
||||
return nullptr;
|
||||
try {
|
||||
return std::dynamic_pointer_cast<T>(it->second);
|
||||
} catch(std::bad_cast& e) {
|
||||
logPrintf(ERROR, "bad_cast while getting device feature %s: %s",
|
||||
name.c_str(), e.what());
|
||||
return nullptr;
|
||||
}
|
||||
void handleEvent (const HIDPP::Report &event);
|
||||
protected:
|
||||
Device* dev;
|
||||
HIDPP20::IReprogControls _irc;
|
||||
std::vector<uint16_t> states;
|
||||
std::vector<uint16_t> new_states;
|
||||
};
|
||||
class ReceiverHandler : public EventHandler
|
||||
{
|
||||
public:
|
||||
ReceiverHandler (Device *d) : dev (d) { }
|
||||
const HIDPP20::FeatureInterface *feature () const
|
||||
{
|
||||
return nullptr; // This sounds like a horrible idea
|
||||
}
|
||||
virtual const std::vector<uint8_t> featureIndices() const
|
||||
{
|
||||
return HIDPP10::IReceiver::Events;
|
||||
}
|
||||
void handleEvent (const HIDPP::Report &event);
|
||||
protected:
|
||||
Device* dev;
|
||||
};
|
||||
class WirelessStatusHandler : public EventHandler
|
||||
{
|
||||
public:
|
||||
WirelessStatusHandler (Device *d) : dev (d), _iws (d->hidpp_dev) { }
|
||||
const HIDPP20::FeatureInterface *feature () const
|
||||
{
|
||||
return &_iws;
|
||||
}
|
||||
void handleEvent (const HIDPP::Report &event);
|
||||
protected:
|
||||
Device* dev;
|
||||
HIDPP20::IWirelessDeviceStatus _iws;
|
||||
};
|
||||
|
||||
class EventListener
|
||||
{
|
||||
HIDPP::Dispatcher *dispatcher;
|
||||
HIDPP::DeviceIndex index;
|
||||
std::map<uint8_t, std::unique_ptr<EventHandler>> handlers;
|
||||
std::map<uint8_t, HIDPP::Dispatcher::listener_iterator> iterators;
|
||||
public:
|
||||
EventListener (HIDPP::Dispatcher *dispatcher, HIDPP::DeviceIndex index): dispatcher (dispatcher), index (index) {}
|
||||
|
||||
virtual void removeEventHandlers ();
|
||||
virtual ~EventListener();
|
||||
virtual void addEventHandler (std::unique_ptr<EventHandler> &&handler);
|
||||
|
||||
virtual void start () = 0;
|
||||
virtual void stop () = 0;
|
||||
|
||||
protected:
|
||||
virtual bool event (EventHandler* handler, const HIDPP::Report &report) = 0;
|
||||
};
|
||||
class SimpleListener : public EventListener
|
||||
{
|
||||
HIDPP::SimpleDispatcher *dispatcher;
|
||||
|
||||
public:
|
||||
SimpleListener (HIDPP::SimpleDispatcher* dispatcher, HIDPP::DeviceIndex index):
|
||||
EventListener (dispatcher, index),
|
||||
dispatcher (dispatcher)
|
||||
{
|
||||
}
|
||||
|
||||
bool stopped = false;
|
||||
virtual void start();
|
||||
virtual void stop();
|
||||
private:
|
||||
void _init();
|
||||
|
||||
protected:
|
||||
virtual bool event (EventHandler* handler, const HIDPP::Report &report);
|
||||
/* Adds a feature without calling an error if unsupported */
|
||||
template<typename T>
|
||||
void _addFeature(std::string name)
|
||||
{
|
||||
try {
|
||||
_features.emplace(name, std::make_shared<T>(this));
|
||||
} catch (backend::hidpp20::UnsupportedFeature& e) {
|
||||
}
|
||||
}
|
||||
|
||||
backend::hidpp20::Device _hidpp20;
|
||||
std::string _path;
|
||||
backend::hidpp::DeviceIndex _index;
|
||||
std::map<std::string, std::shared_ptr<features::DeviceFeature>>
|
||||
_features;
|
||||
DeviceConfig _config;
|
||||
|
||||
void _makeResetMechanism();
|
||||
std::unique_ptr<std::function<void()>> _reset_mechanism;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LOGID_DEVICE_H
|
||||
|
|
|
@ -1,212 +0,0 @@
|
|||
#include <hid/DeviceMonitor.h>
|
||||
#include <hidpp/SimpleDispatcher.h>
|
||||
#include <hidpp/Device.h>
|
||||
#include <hidpp10/Error.h>
|
||||
#include <hidpp10/IReceiver.h>
|
||||
#include <hidpp20/Error.h>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
|
||||
#include "DeviceFinder.h"
|
||||
#include "util.h"
|
||||
#include "Device.h"
|
||||
|
||||
#define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded"
|
||||
|
||||
using namespace logid;
|
||||
|
||||
void stopAndDeleteConnectedDevice (ConnectedDevice &connected_device)
|
||||
{
|
||||
if(!connected_device.device->waiting_for_receiver)
|
||||
log_printf(INFO, "%s (Device %d on %s) disconnected", connected_device.device->name.c_str(),
|
||||
connected_device.device->index, connected_device.device->path.c_str());
|
||||
connected_device.device->stop();
|
||||
connected_device.associatedThread.join();
|
||||
delete(connected_device.device);
|
||||
}
|
||||
|
||||
DeviceFinder::~DeviceFinder()
|
||||
{
|
||||
this->devices_mutex.lock();
|
||||
for (auto it = this->devices.begin(); it != this->devices.end(); it++) {
|
||||
for (auto jt = it->second.begin(); jt != it->second.end(); jt++) {
|
||||
stopAndDeleteConnectedDevice(jt->second);
|
||||
}
|
||||
}
|
||||
this->devices_mutex.unlock();
|
||||
}
|
||||
|
||||
///TODO: Unused return variable?
|
||||
Device* DeviceFinder::insertNewDevice(const std::string &path, HIDPP::DeviceIndex index)
|
||||
{
|
||||
auto device = new Device(path, index);
|
||||
try
|
||||
{
|
||||
device->init();
|
||||
}
|
||||
catch(BlacklistedDevice& e) { return nullptr; }
|
||||
|
||||
this->devices_mutex.lock();
|
||||
log_printf(INFO, "%s detected: device %d on %s", device->name.c_str(), index, path.c_str());
|
||||
auto path_bucket = this->devices.emplace(path, std::map<HIDPP::DeviceIndex, ConnectedDevice>()).first;
|
||||
path_bucket->second.emplace(index, ConnectedDevice{
|
||||
device,
|
||||
std::thread([device]() {
|
||||
device->start();
|
||||
})
|
||||
});
|
||||
this->devices_mutex.unlock();
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
Device* DeviceFinder::insertNewReceiverDevice(const std::string &path, HIDPP::DeviceIndex index)
|
||||
{
|
||||
auto *device = new Device(path, index);
|
||||
|
||||
this->devices_mutex.lock();
|
||||
auto path_bucket = this->devices.emplace(path, std::map<HIDPP::DeviceIndex, ConnectedDevice>()).first;
|
||||
path_bucket->second.emplace(index, ConnectedDevice{
|
||||
device,
|
||||
std::thread([device]() {
|
||||
device->waitForReceiver();
|
||||
})
|
||||
});
|
||||
this->devices_mutex.unlock();
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
void DeviceFinder::stopAndDeleteAllDevicesIn (const std::string &path)
|
||||
{
|
||||
this->devices_mutex.lock();
|
||||
auto path_bucket = this->devices.find(path);
|
||||
if (path_bucket != this->devices.end())
|
||||
{
|
||||
for (auto& index_bucket : path_bucket->second) {
|
||||
stopAndDeleteConnectedDevice(index_bucket.second);
|
||||
}
|
||||
this->devices.erase(path_bucket);
|
||||
}
|
||||
this->devices_mutex.unlock();
|
||||
}
|
||||
|
||||
void DeviceFinder::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index)
|
||||
{
|
||||
this->devices_mutex.lock();
|
||||
auto path_bucket = this->devices.find(path);
|
||||
if (path_bucket != this->devices.end())
|
||||
{
|
||||
auto index_bucket = path_bucket->second.find(index);
|
||||
if (index_bucket != path_bucket->second.end())
|
||||
{
|
||||
stopAndDeleteConnectedDevice(index_bucket->second);
|
||||
path_bucket->second.erase(index_bucket);
|
||||
}
|
||||
}
|
||||
this->devices_mutex.unlock();
|
||||
|
||||
log_printf(WARN, "Attempted to disconnect not previously connected device %d on %s", index, path.c_str());
|
||||
}
|
||||
|
||||
void DeviceFinder::addDevice(const char *path)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
std::string string_path(path);
|
||||
// Asynchronously scan device
|
||||
std::thread{[=]()
|
||||
{
|
||||
//Check if device is an HID++ device and handle it accordingly
|
||||
try
|
||||
{
|
||||
HIDPP::SimpleDispatcher dispatcher(string_path.c_str());
|
||||
for(HIDPP::DeviceIndex index: { HIDPP::DefaultDevice, HIDPP::CordedDevice })
|
||||
{
|
||||
bool device_not_connected = true;
|
||||
bool device_unknown = false;
|
||||
int remaining_tries = MAX_CONNECTION_TRIES;
|
||||
do {
|
||||
try
|
||||
{
|
||||
HIDPP::Device d(&dispatcher, index);
|
||||
auto version = d.protocolVersion();
|
||||
uint major, minor;
|
||||
std::tie(major, minor) = version;
|
||||
if(index == HIDPP::DefaultDevice && version == std::make_tuple(1, 0))
|
||||
{
|
||||
HIDPP10::Device receiver(&dispatcher, index);
|
||||
HIDPP10::IReceiver irecv(&receiver);
|
||||
log_printf(INFO, "Found %s on %s", receiver.name().c_str(), string_path.c_str());
|
||||
for(HIDPP::DeviceIndex recv_index : { HIDPP::WirelessDevice1, HIDPP::WirelessDevice2,
|
||||
HIDPP::WirelessDevice3, HIDPP::WirelessDevice4,
|
||||
HIDPP::WirelessDevice5, HIDPP::WirelessDevice6 })
|
||||
this->insertNewReceiverDevice(string_path, recv_index);
|
||||
irecv.getPairedDevices();
|
||||
return;
|
||||
}
|
||||
if(major > 1) // HID++ 2.0 devices only
|
||||
{
|
||||
this->insertNewDevice(string_path, index);
|
||||
}
|
||||
device_not_connected = false;
|
||||
}
|
||||
catch(HIDPP10::Error &e)
|
||||
{
|
||||
if (e.errorCode() == HIDPP10::Error::ResourceError)
|
||||
{
|
||||
if(remaining_tries == 1)
|
||||
{
|
||||
log_printf(DEBUG, "While querying %s (possibly asleep), %s device: %s", string_path.c_str(), NON_WIRELESS_DEV(index), e.what());
|
||||
remaining_tries += MAX_CONNECTION_TRIES; // asleep devices may raise a resource error, so do not count this try
|
||||
}
|
||||
}
|
||||
else if(e.errorCode() != HIDPP10::Error::UnknownDevice)
|
||||
{
|
||||
if(remaining_tries == 1)
|
||||
log_printf(ERROR, "While querying %s, %s device: %s", string_path.c_str(), NON_WIRELESS_DEV(index), e.what());
|
||||
}
|
||||
else device_unknown = true;
|
||||
}
|
||||
catch(HIDPP20::Error &e)
|
||||
{
|
||||
if(e.errorCode() != HIDPP20::Error::UnknownDevice)
|
||||
{
|
||||
if(remaining_tries == 1)
|
||||
log_printf(ERROR, "Error while querying %s, device %d: %s", string_path.c_str(), NON_WIRELESS_DEV(index), e.what());
|
||||
}
|
||||
else device_unknown = true;
|
||||
}
|
||||
catch(HIDPP::Dispatcher::TimeoutError &e)
|
||||
{
|
||||
if(remaining_tries == 1)
|
||||
{
|
||||
log_printf(DEBUG, "Time out on %s device: %s (possibly asleep)", NON_WIRELESS_DEV(index), string_path.c_str());
|
||||
remaining_tries += MAX_CONNECTION_TRIES; // asleep devices may raise a timeout error, so do not count this try
|
||||
}
|
||||
}
|
||||
catch(std::runtime_error &e)
|
||||
{
|
||||
if(remaining_tries == 1)
|
||||
log_printf(ERROR, "Runtime error on %s device on %s: %s", NON_WIRELESS_DEV(index), string_path.c_str(), e.what());
|
||||
}
|
||||
|
||||
remaining_tries--;
|
||||
std::this_thread::sleep_for(TIME_BETWEEN_CONNECTION_TRIES);
|
||||
|
||||
} while (device_not_connected && !device_unknown && remaining_tries > 0);
|
||||
}
|
||||
}
|
||||
catch(HIDPP::Dispatcher::NoHIDPPReportException &e) { }
|
||||
catch(std::system_error &e) { log_printf(WARN, "Failed to open %s: %s", string_path.c_str(), e.what()); }
|
||||
}}.detach();
|
||||
}
|
||||
|
||||
void DeviceFinder::removeDevice(const char* path)
|
||||
{
|
||||
this->stopAndDeleteAllDevicesIn(std::string(path));
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
#ifndef LOGID_DEVICEFINDER_H
|
||||
#define LOGID_DEVICEFINDER_H
|
||||
|
||||
#include <hid/DeviceMonitor.h>
|
||||
#include <hidpp/SimpleDispatcher.h>
|
||||
#include <hidpp10/Device.h>
|
||||
#include <hidpp10/IReceiver.h>
|
||||
#include <hidpp20/IReprogControls.h>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include "Device.h"
|
||||
|
||||
#define MAX_CONNECTION_TRIES 10
|
||||
#define TIME_BETWEEN_CONNECTION_TRIES 500ms
|
||||
|
||||
namespace logid
|
||||
{
|
||||
|
||||
class Device;
|
||||
|
||||
struct ConnectedDevice {
|
||||
Device *device;
|
||||
std::thread associatedThread;
|
||||
};
|
||||
|
||||
class DeviceFinder : public HID::DeviceMonitor
|
||||
{
|
||||
public:
|
||||
~DeviceFinder();
|
||||
|
||||
Device* insertNewDevice (const std::string &path, HIDPP::DeviceIndex index);
|
||||
Device* insertNewReceiverDevice (const std::string &path, HIDPP::DeviceIndex index);
|
||||
void stopAndDeleteAllDevicesIn (const std::string &path);
|
||||
void stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index);
|
||||
protected:
|
||||
void addDevice(const char* path);
|
||||
void removeDevice(const char* path);
|
||||
private:
|
||||
std::mutex devices_mutex;
|
||||
std::map<std::string, std::map<HIDPP::DeviceIndex, ConnectedDevice>> devices;
|
||||
};
|
||||
|
||||
extern DeviceFinder* finder;
|
||||
}
|
||||
|
||||
#endif //LOGID_DEVICEFINDER_H
|
110
src/logid/DeviceManager.cpp
Normal file
110
src/logid/DeviceManager.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <thread>
|
||||
#include <sstream>
|
||||
|
||||
#include "DeviceManager.h"
|
||||
#include "Receiver.h"
|
||||
#include "util/log.h"
|
||||
#include "backend/hidpp10/Error.h"
|
||||
#include "backend/Error.h"
|
||||
|
||||
using namespace logid;
|
||||
using namespace logid::backend;
|
||||
|
||||
void DeviceManager::addDevice(std::string path)
|
||||
{
|
||||
bool defaultExists = true;
|
||||
bool isReceiver = false;
|
||||
|
||||
// Check if device is ignored before continuing
|
||||
{
|
||||
raw::RawDevice raw_dev(path);
|
||||
if(global_config->isIgnored(raw_dev.productId())) {
|
||||
logPrintf(DEBUG, "%s: Device 0x%04x ignored.",
|
||||
path.c_str(), raw_dev.productId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
hidpp::Device device(path, hidpp::DefaultDevice);
|
||||
isReceiver = device.version() == std::make_tuple(1, 0);
|
||||
} catch(hidpp10::Error &e) {
|
||||
if(e.code() != hidpp10::Error::UnknownDevice)
|
||||
throw;
|
||||
} catch(hidpp::Device::InvalidDevice &e) { // Ignore
|
||||
defaultExists = false;
|
||||
} catch(std::system_error &e) {
|
||||
logPrintf(WARN, "I/O error on %s: %s, skipping device.",
|
||||
path.c_str(), e.what());
|
||||
return;
|
||||
} catch (TimeoutError &e) {
|
||||
logPrintf(WARN, "Device %s timed out.", path.c_str());
|
||||
defaultExists = false;
|
||||
}
|
||||
|
||||
if(isReceiver) {
|
||||
logPrintf(INFO, "Detected receiver at %s", path.c_str());
|
||||
auto receiver = std::make_shared<Receiver>(path);
|
||||
receiver->run();
|
||||
_receivers.emplace(path, receiver);
|
||||
} else {
|
||||
/* TODO: Can non-receivers only contain 1 device?
|
||||
* If the device exists, it is guaranteed to be an HID++ 2.0 device */
|
||||
if(defaultExists) {
|
||||
auto device = std::make_shared<Device>(path, hidpp::DefaultDevice);
|
||||
_devices.emplace(path, device);
|
||||
} else {
|
||||
try {
|
||||
auto device = std::make_shared<Device>(path,
|
||||
hidpp::CordedDevice);
|
||||
_devices.emplace(path, device);
|
||||
} catch(hidpp10::Error &e) {
|
||||
if(e.code() != hidpp10::Error::UnknownDevice)
|
||||
throw;
|
||||
else
|
||||
logPrintf(WARN,
|
||||
"HID++ 1.0 error while trying to initialize %s:"
|
||||
"%s", path.c_str(), e.what());
|
||||
} catch(hidpp::Device::InvalidDevice &e) { // Ignore
|
||||
} catch(std::system_error &e) {
|
||||
// This error should have been thrown previously
|
||||
logPrintf(WARN, "I/O error on %s: %s", path.c_str(),
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceManager::removeDevice(std::string path)
|
||||
{
|
||||
auto receiver = _receivers.find(path);
|
||||
|
||||
if(receiver != _receivers.end()) {
|
||||
_receivers.erase(receiver);
|
||||
logPrintf(INFO, "Receiver on %s disconnected", path.c_str());
|
||||
} else {
|
||||
auto device = _devices.find(path);
|
||||
if(device != _devices.find(path)) {
|
||||
_devices.erase(device);
|
||||
logPrintf(INFO, "Device on %s disconnected", path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
50
src/logid/DeviceManager.h
Normal file
50
src/logid/DeviceManager.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_DEVICEMANAGER_H
|
||||
#define LOGID_DEVICEMANAGER_H
|
||||
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include "backend/raw/DeviceMonitor.h"
|
||||
#include "backend/hidpp/Device.h"
|
||||
#include "Device.h"
|
||||
#include "Receiver.h"
|
||||
|
||||
namespace logid
|
||||
{
|
||||
|
||||
class DeviceManager : public backend::raw::DeviceMonitor
|
||||
{
|
||||
public:
|
||||
DeviceManager() = default;
|
||||
protected:
|
||||
void addDevice(std::string path) override;
|
||||
void removeDevice(std::string path) override;
|
||||
private:
|
||||
|
||||
std::map<std::string, std::shared_ptr<Device>> _devices;
|
||||
std::map<std::string, std::shared_ptr<Receiver>> _receivers;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<DeviceManager> device_manager;
|
||||
}
|
||||
|
||||
#endif //LOGID_DEVICEMANAGER_H
|
|
@ -1,43 +0,0 @@
|
|||
#include <libevdev/libevdev.h>
|
||||
#include <libevdev/libevdev-uinput.h>
|
||||
#include <system_error>
|
||||
|
||||
#include "EvdevDevice.h"
|
||||
|
||||
using namespace logid;
|
||||
|
||||
EvdevDevice::EvdevDevice(const char* name)
|
||||
{
|
||||
device = libevdev_new();
|
||||
libevdev_set_name(device, name);
|
||||
|
||||
libevdev_enable_event_type(device, EV_KEY);
|
||||
for(int i = 0; i < KEY_CNT; i++)
|
||||
libevdev_enable_event_code(device, EV_KEY, i, nullptr);
|
||||
libevdev_enable_event_type(device, EV_REL);
|
||||
for(int i = 0; i < REL_CNT; i++)
|
||||
libevdev_enable_event_code(device, EV_REL, i, nullptr);
|
||||
|
||||
int err = libevdev_uinput_create_from_device(device, LIBEVDEV_UINPUT_OPEN_MANAGED, &ui_device);
|
||||
|
||||
if(err != 0)
|
||||
throw std::system_error(-err, std::generic_category());
|
||||
}
|
||||
|
||||
void EvdevDevice::moveAxis(unsigned int axis, int movement)
|
||||
{
|
||||
libevdev_uinput_write_event(ui_device, EV_REL, axis, movement);
|
||||
libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0);
|
||||
}
|
||||
|
||||
void EvdevDevice::sendEvent(unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
libevdev_uinput_write_event(ui_device, type, code, value);
|
||||
libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0);
|
||||
}
|
||||
|
||||
EvdevDevice::~EvdevDevice()
|
||||
{
|
||||
libevdev_uinput_destroy(ui_device);
|
||||
libevdev_free(device);
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
#ifndef LOGID_EVDEVDEVICE_H
|
||||
#define LOGID_EVDEVDEVICE_H
|
||||
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <libevdev/libevdev-uinput.h>
|
||||
|
||||
namespace logid
|
||||
{
|
||||
class EvdevDevice
|
||||
{
|
||||
public:
|
||||
EvdevDevice(const char *name);
|
||||
|
||||
~EvdevDevice();
|
||||
|
||||
void moveAxis(unsigned int axis, int movement);
|
||||
|
||||
void sendEvent(unsigned int type, unsigned int code, int value);
|
||||
|
||||
libevdev *device;
|
||||
libevdev_uinput *ui_device;
|
||||
};
|
||||
|
||||
extern EvdevDevice* global_evdev;
|
||||
}
|
||||
|
||||
#endif //LOGID_EVDEVDEVICE_H
|
106
src/logid/InputDevice.cpp
Normal file
106
src/logid/InputDevice.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#include "InputDevice.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <libevdev/libevdev-uinput.h>
|
||||
}
|
||||
|
||||
using namespace logid;
|
||||
|
||||
InputDevice::InvalidEventCode::InvalidEventCode(const std::string& name) :
|
||||
_what ("Invalid event code " + name)
|
||||
{
|
||||
}
|
||||
|
||||
const char* InputDevice::InvalidEventCode::what() const noexcept
|
||||
{
|
||||
return _what.c_str();
|
||||
}
|
||||
|
||||
InputDevice::InputDevice(const char* name)
|
||||
{
|
||||
device = libevdev_new();
|
||||
libevdev_set_name(device, name);
|
||||
|
||||
///TODO: Is it really a good idea to enable all events?
|
||||
libevdev_enable_event_type(device, EV_KEY);
|
||||
for(unsigned int i = 0; i < KEY_CNT; i++)
|
||||
libevdev_enable_event_code(device, EV_KEY, i, nullptr);
|
||||
libevdev_enable_event_type(device, EV_REL);
|
||||
for(unsigned int i = 0; i < REL_CNT; i++)
|
||||
libevdev_enable_event_code(device, EV_REL, i, nullptr);
|
||||
|
||||
int err = libevdev_uinput_create_from_device(device,
|
||||
LIBEVDEV_UINPUT_OPEN_MANAGED, &ui_device);
|
||||
|
||||
if(err != 0)
|
||||
throw std::system_error(-err, std::generic_category());
|
||||
}
|
||||
|
||||
InputDevice::~InputDevice()
|
||||
{
|
||||
libevdev_uinput_destroy(ui_device);
|
||||
libevdev_free(device);
|
||||
}
|
||||
|
||||
void InputDevice::moveAxis(uint axis, int movement)
|
||||
{
|
||||
_sendEvent(EV_REL, axis, movement);
|
||||
}
|
||||
|
||||
void InputDevice::pressKey(uint code)
|
||||
{
|
||||
_sendEvent(EV_KEY, code, 1);
|
||||
}
|
||||
|
||||
void InputDevice::releaseKey(uint code)
|
||||
{
|
||||
_sendEvent(EV_KEY, code, 0);
|
||||
}
|
||||
|
||||
uint InputDevice::toKeyCode(const std::string& name)
|
||||
{
|
||||
return _toEventCode(EV_KEY, name);
|
||||
}
|
||||
|
||||
uint InputDevice::toAxisCode(const std::string& name)
|
||||
{
|
||||
return _toEventCode(EV_REL, name);
|
||||
}
|
||||
|
||||
uint InputDevice::_toEventCode(uint type, const std::string& name)
|
||||
{
|
||||
int code = libevdev_event_code_from_name(type, name.c_str());
|
||||
|
||||
if(code == -1)
|
||||
throw InvalidEventCode(name);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
void InputDevice::_sendEvent(uint type, uint code, int value)
|
||||
{
|
||||
libevdev_uinput_write_event(ui_device, type, code, value);
|
||||
libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0);
|
||||
}
|
64
src/logid/InputDevice.h
Normal file
64
src/logid/InputDevice.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_INPUTDEVICE_H
|
||||
#define LOGID_INPUTDEVICE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <libevdev/libevdev-uinput.h>
|
||||
}
|
||||
|
||||
namespace logid
|
||||
{
|
||||
class InputDevice
|
||||
{
|
||||
public:
|
||||
class InvalidEventCode : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit InvalidEventCode(const std::string& name);
|
||||
const char* what() const noexcept override;
|
||||
private:
|
||||
const std::string _what;
|
||||
};
|
||||
explicit InputDevice(const char *name);
|
||||
~InputDevice();
|
||||
|
||||
void moveAxis(uint axis, int movement);
|
||||
void pressKey(uint code);
|
||||
void releaseKey(uint code);
|
||||
|
||||
static uint toKeyCode(const std::string& name);
|
||||
static uint toAxisCode(const std::string& name);
|
||||
private:
|
||||
void _sendEvent(uint type, uint code, int value);
|
||||
|
||||
static uint _toEventCode(uint type, const std::string& name);
|
||||
|
||||
libevdev* device;
|
||||
libevdev_uinput* ui_device{};
|
||||
};
|
||||
|
||||
extern std::unique_ptr<InputDevice> virtual_input;
|
||||
}
|
||||
|
||||
#endif //LOGID_INPUTDEVICE_H
|
90
src/logid/Receiver.cpp
Normal file
90
src/logid/Receiver.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Receiver.h"
|
||||
#include "util/log.h"
|
||||
#include "backend/hidpp10/Error.h"
|
||||
#include "backend/hidpp20/Error.h"
|
||||
#include "backend/Error.h"
|
||||
|
||||
using namespace logid;
|
||||
using namespace logid::backend;
|
||||
|
||||
Receiver::Receiver(const std::string& path) :
|
||||
dj::ReceiverMonitor(path), _path (path)
|
||||
{
|
||||
}
|
||||
|
||||
void Receiver::addDevice(hidpp::DeviceConnectionEvent event)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_devices_change);
|
||||
try {
|
||||
// Check if device is ignored before continuing
|
||||
if(global_config->isIgnored(event.pid)) {
|
||||
logPrintf(DEBUG, "%s:%d: Device 0x%04x ignored.",
|
||||
_path.c_str(), event.index, event.pid);
|
||||
return;
|
||||
}
|
||||
|
||||
auto dev = _devices.find(event.index);
|
||||
if(dev != _devices.end()) {
|
||||
if(event.linkEstablished)
|
||||
dev->second->wakeup();
|
||||
else
|
||||
dev->second->sleep();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!event.linkEstablished)
|
||||
return;
|
||||
|
||||
hidpp::Device hidpp_device(receiver(), event);
|
||||
|
||||
auto version = hidpp_device.version();
|
||||
|
||||
if(std::get<0>(version) < 2) {
|
||||
logPrintf(INFO, "Unsupported HID++ 1.0 device on %s:%d connected.",
|
||||
_path.c_str(), event.index);
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<Device> device = std::make_shared<Device>(
|
||||
receiver()->rawDevice(), event.index);
|
||||
|
||||
_devices.emplace(event.index, device);
|
||||
|
||||
} catch(hidpp10::Error &e) {
|
||||
logPrintf(ERROR,
|
||||
"Caught HID++ 1.0 error while trying to initialize "
|
||||
"%s:%d: %s", _path.c_str(), event.index, e.what());
|
||||
} catch(hidpp20::Error &e) {
|
||||
logPrintf(ERROR, "Caught HID++ 2.0 error while trying to initialize "
|
||||
"%s:%d: %s", _path.c_str(), event.index, e.what());
|
||||
} catch(TimeoutError &e) {
|
||||
if(!event.fromTimeoutCheck)
|
||||
logPrintf(DEBUG, "%s:%d timed out, waiting for input from device to"
|
||||
" initialize.", _path.c_str(), event.index);
|
||||
waitForDevice(event.index);
|
||||
}
|
||||
}
|
||||
|
||||
void Receiver::removeDevice(hidpp::DeviceIndex index)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_devices_change);
|
||||
_devices.erase(index);
|
||||
}
|
43
src/logid/Receiver.h
Normal file
43
src/logid/Receiver.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_RECEIVER_H
|
||||
#define LOGID_RECEIVER_H
|
||||
|
||||
#include <string>
|
||||
#include "backend/dj/ReceiverMonitor.h"
|
||||
#include "Device.h"
|
||||
|
||||
namespace logid
|
||||
{
|
||||
class Receiver : public backend::dj::ReceiverMonitor
|
||||
{
|
||||
public:
|
||||
Receiver(const std::string& path);
|
||||
|
||||
protected:
|
||||
void addDevice(backend::hidpp::DeviceConnectionEvent event) override;
|
||||
void removeDevice(backend::hidpp::DeviceIndex index) override;
|
||||
private:
|
||||
std::mutex _devices_change;
|
||||
std::map<backend::hidpp::DeviceIndex, std::shared_ptr<Device>> _devices;
|
||||
std::string _path;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //LOGID_RECEIVER_H
|
76
src/logid/actions/Action.cpp
Normal file
76
src/logid/actions/Action.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include "Action.h"
|
||||
#include "../util/log.h"
|
||||
#include "KeypressAction.h"
|
||||
#include "ToggleSmartShift.h"
|
||||
#include "ToggleHiresScroll.h"
|
||||
#include "GestureAction.h"
|
||||
#include "NullAction.h"
|
||||
#include "CycleDPI.h"
|
||||
#include "ChangeDPI.h"
|
||||
|
||||
using namespace logid;
|
||||
using namespace logid::actions;
|
||||
|
||||
std::shared_ptr<Action> Action::makeAction(Device *device, libconfig::Setting
|
||||
&setting)
|
||||
{
|
||||
if(!setting.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: Action is not a group, ignoring.",
|
||||
setting.getSourceLine());
|
||||
throw InvalidAction();
|
||||
}
|
||||
|
||||
try {
|
||||
auto& action_type = setting.lookup("type");
|
||||
|
||||
if(action_type.getType() != libconfig::Setting::TypeString) {
|
||||
logPrintf(WARN, "Line %d: Action type must be a string",
|
||||
action_type.getSourceLine());
|
||||
throw InvalidAction();
|
||||
}
|
||||
|
||||
std::string type = action_type;
|
||||
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
||||
|
||||
if(type == "keypress")
|
||||
return std::make_shared<KeypressAction>(device, setting);
|
||||
else if(type == "togglesmartshift")
|
||||
return std::make_shared<ToggleSmartShift>(device);
|
||||
else if(type == "togglehiresscroll")
|
||||
return std::make_shared<ToggleHiresScroll>(device);
|
||||
else if(type == "gestures")
|
||||
return std::make_shared<GestureAction>(device, setting);
|
||||
else if(type == "cycledpi")
|
||||
return std::make_shared<CycleDPI>(device, setting);
|
||||
else if(type == "changedpi")
|
||||
return std::make_shared<ChangeDPI>(device, setting);
|
||||
else if(type == "none")
|
||||
return std::make_shared<NullAction>(device);
|
||||
else
|
||||
throw InvalidAction(type);
|
||||
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: Action type is missing, ignoring.",
|
||||
setting.getSourceLine());
|
||||
throw InvalidAction();
|
||||
}
|
||||
}
|
83
src/logid/actions/Action.h
Normal file
83
src/logid/actions/Action.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_H
|
||||
#define LOGID_ACTION_H
|
||||
|
||||
#include <atomic>
|
||||
#include <libconfig.h++>
|
||||
#include <memory>
|
||||
|
||||
namespace logid {
|
||||
class Device;
|
||||
namespace actions {
|
||||
class InvalidAction : public std::exception
|
||||
{
|
||||
public:
|
||||
InvalidAction()
|
||||
{
|
||||
}
|
||||
explicit InvalidAction(std::string& action) : _action (action)
|
||||
{
|
||||
}
|
||||
const char* what() const noexcept override
|
||||
{
|
||||
return _action.c_str();
|
||||
}
|
||||
private:
|
||||
std::string _action;
|
||||
};
|
||||
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<Action> makeAction(Device* device,
|
||||
libconfig::Setting& setting);
|
||||
|
||||
virtual void press() = 0;
|
||||
virtual void release() = 0;
|
||||
virtual void move(int16_t x, int16_t y)
|
||||
{
|
||||
// Suppress unused warning
|
||||
(void)x; (void)y;
|
||||
}
|
||||
|
||||
virtual bool pressed()
|
||||
{
|
||||
return _pressed;
|
||||
}
|
||||
|
||||
virtual uint8_t reprogFlags() const = 0;
|
||||
|
||||
class Config
|
||||
{
|
||||
protected:
|
||||
explicit Config(Device* device) : _device (device)
|
||||
{
|
||||
}
|
||||
Device* _device;
|
||||
};
|
||||
protected:
|
||||
explicit Action(Device* device) : _device (device), _pressed (false)
|
||||
{
|
||||
}
|
||||
Device* _device;
|
||||
std::atomic<bool> _pressed;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_H
|
109
src/logid/actions/ChangeDPI.cpp
Normal file
109
src/logid/actions/ChangeDPI.cpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "ChangeDPI.h"
|
||||
#include "../Device.h"
|
||||
#include "../util/task.h"
|
||||
#include "../util/log.h"
|
||||
#include "../backend/hidpp20/Error.h"
|
||||
#include "../backend/hidpp20/features/ReprogControls.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
ChangeDPI::ChangeDPI(Device *device, libconfig::Setting &setting) :
|
||||
Action(device), _config(device, setting)
|
||||
{
|
||||
_dpi = _device->getFeature<features::DPI>("dpi");
|
||||
if(!_dpi)
|
||||
logPrintf(WARN, "%s:%d: DPI feature not found, cannot use "
|
||||
"ChangeDPI action.",
|
||||
_device->hidpp20().devicePath().c_str(),
|
||||
_device->hidpp20().deviceIndex());
|
||||
}
|
||||
|
||||
void ChangeDPI::press()
|
||||
{
|
||||
_pressed = true;
|
||||
if(_dpi) {
|
||||
task::spawn([this]{
|
||||
try {
|
||||
uint16_t last_dpi = _dpi->getDPI(_config.sensor());
|
||||
_dpi->setDPI(last_dpi + _config.interval(), _config.sensor());
|
||||
} catch (backend::hidpp20::Error& e) {
|
||||
if(e.code() == backend::hidpp20::Error::InvalidArgument)
|
||||
logPrintf(WARN, "%s:%d: Could not get/set DPI for sensor "
|
||||
"%d",
|
||||
_device->hidpp20().devicePath().c_str(),
|
||||
_device->hidpp20().deviceIndex(),
|
||||
_config.sensor());
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeDPI::release()
|
||||
{
|
||||
_pressed = false;
|
||||
}
|
||||
|
||||
uint8_t ChangeDPI::reprogFlags() const
|
||||
{
|
||||
return backend::hidpp20::ReprogControls::TemporaryDiverted;
|
||||
}
|
||||
|
||||
ChangeDPI::Config::Config(Device *device, libconfig::Setting &config) :
|
||||
Action::Config(device), _interval (0), _sensor (0)
|
||||
{
|
||||
if(!config.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: action must be an object, skipping.",
|
||||
config.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto& inc = config.lookup("inc");
|
||||
if(inc.getType() != libconfig::Setting::TypeInt)
|
||||
logPrintf(WARN, "Line %d: inc must be an integer",
|
||||
inc.getSourceLine());
|
||||
_interval = (int)inc;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: inc is a required field, skipping.",
|
||||
config.getSourceLine());
|
||||
}
|
||||
|
||||
try {
|
||||
auto& sensor = config.lookup("sensor");
|
||||
if(sensor.getType() != libconfig::Setting::TypeInt)
|
||||
logPrintf(WARN, "Line %d: sensor must be an integer",
|
||||
sensor.getSourceLine());
|
||||
_sensor = (int)sensor;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t ChangeDPI::Config::interval() const
|
||||
{
|
||||
return _interval;
|
||||
}
|
||||
|
||||
uint8_t ChangeDPI::Config::sensor() const
|
||||
{
|
||||
return _sensor;
|
||||
}
|
54
src/logid/actions/ChangeDPI.h
Normal file
54
src/logid/actions/ChangeDPI.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_CHANGEDPI_H
|
||||
#define LOGID_ACTION_CHANGEDPI_H
|
||||
|
||||
#include <libconfig.h++>
|
||||
#include "Action.h"
|
||||
#include "../features/DPI.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions {
|
||||
class ChangeDPI : public Action
|
||||
{
|
||||
public:
|
||||
explicit ChangeDPI(Device* device, libconfig::Setting& setting);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
|
||||
class Config : public Action::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& setting);
|
||||
uint16_t interval() const;
|
||||
uint8_t sensor() const;
|
||||
private:
|
||||
uint16_t _interval;
|
||||
uint8_t _sensor;
|
||||
};
|
||||
|
||||
protected:
|
||||
Config _config;
|
||||
std::shared_ptr<features::DPI> _dpi;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_CHANGEDPI_H
|
133
src/logid/actions/CycleDPI.cpp
Normal file
133
src/logid/actions/CycleDPI.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "CycleDPI.h"
|
||||
#include "../Device.h"
|
||||
#include "../util/task.h"
|
||||
#include "../util/log.h"
|
||||
#include "../backend/hidpp20/Error.h"
|
||||
#include "../backend/hidpp20/features/ReprogControls.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
using namespace libconfig;
|
||||
|
||||
CycleDPI::CycleDPI(Device* device, libconfig::Setting& setting) :
|
||||
Action (device), _config (device, setting)
|
||||
{
|
||||
_dpi = _device->getFeature<features::DPI>("dpi");
|
||||
if(!_dpi)
|
||||
logPrintf(WARN, "%s:%d: DPI feature not found, cannot use "
|
||||
"CycleDPI action.",
|
||||
_device->hidpp20().devicePath().c_str(),
|
||||
_device->hidpp20().deviceIndex());
|
||||
}
|
||||
|
||||
void CycleDPI::press()
|
||||
{
|
||||
_pressed = true;
|
||||
if(_dpi && !_config.empty()) {
|
||||
task::spawn([this](){
|
||||
uint16_t dpi = _config.nextDPI();
|
||||
try {
|
||||
_dpi->setDPI(dpi, _config.sensor());
|
||||
} catch (backend::hidpp20::Error& e) {
|
||||
if(e.code() == backend::hidpp20::Error::InvalidArgument)
|
||||
logPrintf(WARN, "%s:%d: Could not set DPI to %d for "
|
||||
"sensor %d", _device->hidpp20().devicePath().c_str(),
|
||||
_device->hidpp20().deviceIndex(), dpi,
|
||||
_config.sensor());
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CycleDPI::release()
|
||||
{
|
||||
_pressed = false;
|
||||
}
|
||||
|
||||
uint8_t CycleDPI::reprogFlags() const
|
||||
{
|
||||
return backend::hidpp20::ReprogControls::TemporaryDiverted;
|
||||
}
|
||||
|
||||
CycleDPI::Config::Config(Device *device, libconfig::Setting &config) :
|
||||
Action::Config(device), _current_index (0), _sensor (0)
|
||||
{
|
||||
if(!config.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: action must be an object, skipping.",
|
||||
config.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto& sensor = config.lookup("sensor");
|
||||
if(sensor.getType() != Setting::TypeInt)
|
||||
logPrintf(WARN, "Line %d: sensor must be an integer",
|
||||
sensor.getSourceLine());
|
||||
_sensor = (int)sensor;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
try {
|
||||
auto& dpis = config.lookup("dpis");
|
||||
if(!dpis.isList() && !dpis.isArray()) {
|
||||
logPrintf(WARN, "Line %d: dpis must be a list or array, skipping.",
|
||||
dpis.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
int dpi_count = dpis.getLength();
|
||||
for(int i = 0; i < dpi_count; i++) {
|
||||
if(dpis[i].getType() != Setting::TypeInt) {
|
||||
logPrintf(WARN, "Line %d: dpis must be integers, skipping.",
|
||||
dpis[i].getSourceLine());
|
||||
if(dpis.isList())
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
_dpis.push_back((int)(dpis[i]));
|
||||
}
|
||||
|
||||
} catch (libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: dpis is a required field, skipping.",
|
||||
config.getSourceLine());
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t CycleDPI::Config::nextDPI()
|
||||
{
|
||||
uint16_t dpi = _dpis[_current_index++];
|
||||
if(_current_index >= _dpis.size())
|
||||
_current_index = 0;
|
||||
return dpi;
|
||||
}
|
||||
|
||||
bool CycleDPI::Config::empty() const
|
||||
{
|
||||
return _dpis.empty();
|
||||
}
|
||||
|
||||
uint8_t CycleDPI::Config::sensor() const
|
||||
{
|
||||
return _sensor;
|
||||
}
|
56
src/logid/actions/CycleDPI.h
Normal file
56
src/logid/actions/CycleDPI.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_CYCLEDPI_H
|
||||
#define LOGID_ACTION_CYCLEDPI_H
|
||||
|
||||
#include <libconfig.h++>
|
||||
#include "Action.h"
|
||||
#include "../features/DPI.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions {
|
||||
class CycleDPI : public Action
|
||||
{
|
||||
public:
|
||||
explicit CycleDPI(Device* device, libconfig::Setting& setting);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
|
||||
class Config : public Action::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& setting);
|
||||
uint16_t nextDPI();
|
||||
bool empty() const;
|
||||
uint8_t sensor() const;
|
||||
private:
|
||||
std::size_t _current_index;
|
||||
std::vector<uint16_t> _dpis;
|
||||
uint8_t _sensor;
|
||||
};
|
||||
|
||||
protected:
|
||||
Config _config;
|
||||
std::shared_ptr<features::DPI> _dpi;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_CYCLEDPI_H
|
269
src/logid/actions/GestureAction.cpp
Normal file
269
src/logid/actions/GestureAction.cpp
Normal file
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include "GestureAction.h"
|
||||
#include "../Device.h"
|
||||
#include "../backend/hidpp20/features/ReprogControls.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
using namespace logid;
|
||||
using namespace logid::backend;
|
||||
|
||||
GestureAction::Direction GestureAction::toDirection(std::string direction)
|
||||
{
|
||||
std::transform(direction.begin(), direction.end(), direction.begin(),
|
||||
::tolower);
|
||||
if(direction == "up")
|
||||
return Up;
|
||||
else if(direction == "down")
|
||||
return Down;
|
||||
else if(direction == "left")
|
||||
return Left;
|
||||
else if(direction == "right")
|
||||
return Right;
|
||||
else if(direction == "none")
|
||||
return None;
|
||||
else
|
||||
throw std::invalid_argument("direction");
|
||||
}
|
||||
|
||||
GestureAction::Direction GestureAction::toDirection(int16_t x, int16_t y)
|
||||
{
|
||||
if(x >= 0 && y >= 0)
|
||||
return x >= y ? Right : Down;
|
||||
else if(x < 0 && y >= 0)
|
||||
return -x <= y ? Down : Left;
|
||||
else if(x <= 0 && y < 0)
|
||||
return x <= y ? Left : Up;
|
||||
else
|
||||
return x <= -y ? Up : Right;
|
||||
}
|
||||
|
||||
GestureAction::GestureAction(Device* dev, libconfig::Setting& config) :
|
||||
Action (dev), _config (dev, config)
|
||||
{
|
||||
}
|
||||
|
||||
void GestureAction::press()
|
||||
{
|
||||
_pressed = true;
|
||||
_x = 0, _y = 0;
|
||||
for(auto& gesture : _config.gestures())
|
||||
gesture.second->press();
|
||||
}
|
||||
|
||||
void GestureAction::release()
|
||||
{
|
||||
_pressed = false;
|
||||
bool threshold_met = false;
|
||||
|
||||
auto d = toDirection(_x, _y);
|
||||
auto primary_gesture = _config.gestures().find(d);
|
||||
if(primary_gesture != _config.gestures().end()) {
|
||||
threshold_met = primary_gesture->second->metThreshold();
|
||||
primary_gesture->second->release(true);
|
||||
}
|
||||
|
||||
for(auto& gesture : _config.gestures()) {
|
||||
if(gesture.first == d)
|
||||
continue;
|
||||
if(!threshold_met) {
|
||||
if(gesture.second->metThreshold()) {
|
||||
// If the primary gesture did not meet its threshold, use the
|
||||
// secondary one.
|
||||
threshold_met = true;
|
||||
gesture.second->release(true);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
gesture.second->release(false);
|
||||
}
|
||||
}
|
||||
|
||||
if(!threshold_met) {
|
||||
if(_config.noneAction()) {
|
||||
_config.noneAction()->press();
|
||||
_config.noneAction()->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GestureAction::move(int16_t x, int16_t y)
|
||||
{
|
||||
auto new_x = _x + x, new_y = _y + y;
|
||||
|
||||
if(abs(x) > 0) {
|
||||
if(_x < 0 && new_x >= 0) { // Left -> Origin/Right
|
||||
auto left = _config.gestures().find(Left);
|
||||
if(left != _config.gestures().end())
|
||||
left->second->move(_x);
|
||||
if(new_x) { // Ignore to origin
|
||||
auto right = _config.gestures().find(Right);
|
||||
if(right != _config.gestures().end())
|
||||
right->second->move(new_x);
|
||||
}
|
||||
} else if(_x > 0 && new_x <= 0) { // Right -> Origin/Left
|
||||
auto right = _config.gestures().find(Right);
|
||||
if(right != _config.gestures().end())
|
||||
right->second->move(-_x);
|
||||
if(new_x) { // Ignore to origin
|
||||
auto left = _config.gestures().find(Left);
|
||||
if(left != _config.gestures().end())
|
||||
left->second->move(-new_x);
|
||||
}
|
||||
} else if(new_x < 0) { // Origin/Left to Left
|
||||
auto left = _config.gestures().find(Left);
|
||||
if(left != _config.gestures().end())
|
||||
left->second->move(-x);
|
||||
} else if(new_x > 0) { // Origin/Right to Right
|
||||
auto right = _config.gestures().find(Right);
|
||||
if(right != _config.gestures().end())
|
||||
right->second->move(x);
|
||||
}
|
||||
}
|
||||
|
||||
if(abs(y) > 0) {
|
||||
if(_y > 0 && new_y <= 0) { // Up -> Origin/Down
|
||||
auto up = _config.gestures().find(Up);
|
||||
if(up != _config.gestures().end())
|
||||
up->second->move(_y);
|
||||
if(new_y) { // Ignore to origin
|
||||
auto down = _config.gestures().find(Down);
|
||||
if(down != _config.gestures().end())
|
||||
down->second->move(new_y);
|
||||
}
|
||||
} else if(_y < 0 && new_y >= 0) { // Down -> Origin/Up
|
||||
auto down = _config.gestures().find(Down);
|
||||
if(down != _config.gestures().end())
|
||||
down->second->move(-_y);
|
||||
if(new_y) { // Ignore to origin
|
||||
auto up = _config.gestures().find(Up);
|
||||
if(up != _config.gestures().end())
|
||||
up->second->move(-new_y);
|
||||
}
|
||||
} else if(new_y < 0) { // Origin/Up to Up
|
||||
auto up = _config.gestures().find(Up);
|
||||
if(up != _config.gestures().end())
|
||||
up->second->move(-y);
|
||||
} else if(new_y > 0) {// Origin/Down to Down
|
||||
auto down = _config.gestures().find(Down);
|
||||
if(down != _config.gestures().end())
|
||||
down->second->move(y);
|
||||
}
|
||||
}
|
||||
|
||||
_x = new_x; _y = new_y;
|
||||
}
|
||||
|
||||
uint8_t GestureAction::reprogFlags() const
|
||||
{
|
||||
return (hidpp20::ReprogControls::TemporaryDiverted |
|
||||
hidpp20::ReprogControls::RawXYDiverted);
|
||||
}
|
||||
|
||||
GestureAction::Config::Config(Device* device, libconfig::Setting &root) :
|
||||
Action::Config(device)
|
||||
{
|
||||
try {
|
||||
auto& gestures = root.lookup("gestures");
|
||||
|
||||
if(!gestures.isList()) {
|
||||
logPrintf(WARN, "Line %d: gestures must be a list, ignoring.",
|
||||
gestures.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
int gesture_count = gestures.getLength();
|
||||
|
||||
for(int i = 0; i < gesture_count; i++) {
|
||||
if(!gestures[i].isGroup()) {
|
||||
logPrintf(WARN, "Line %d: gesture must be a group, skipping.",
|
||||
gestures[i].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
Direction d;
|
||||
try {
|
||||
auto& direction = gestures[i].lookup("direction");
|
||||
if(direction.getType() != libconfig::Setting::TypeString) {
|
||||
logPrintf(WARN, "Line %d: direction must be a string, "
|
||||
"skipping.", direction.getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
d = toDirection(direction);
|
||||
} catch(std::invalid_argument& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid direction %s",
|
||||
direction.getSourceLine(), (const char*)direction);
|
||||
continue;
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: direction is a required field, "
|
||||
"skipping.", gestures[i].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
if(_gestures.find(d) != _gestures.end() || (d == None && _none_action)) {
|
||||
logPrintf(WARN, "Line %d: Gesture is already defined for "
|
||||
"this direction, duplicate ignored.",
|
||||
gestures[i].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
if(d == None) {
|
||||
try {
|
||||
_none_action = Action::makeAction(_device,
|
||||
gestures[i].lookup("action"));
|
||||
} catch (InvalidAction& e) {
|
||||
logPrintf(WARN, "Line %d: %s is not a valid action, "
|
||||
"skipping.", gestures[i].lookup("action")
|
||||
.getSourceLine(), e.what());
|
||||
} catch (libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: action is a required field, "
|
||||
"skipping.", gestures[i].getSourceLine(),
|
||||
e.what());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
_gestures.emplace(d, Gesture::makeGesture(_device,
|
||||
gestures[i]));
|
||||
} catch(InvalidGesture& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid gesture: %s",
|
||||
gestures[i].getSourceLine(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: gestures is a required field, ignoring.",
|
||||
root.getSourceLine());
|
||||
}
|
||||
}
|
||||
|
||||
std::map<GestureAction::Direction, std::shared_ptr<Gesture>>&
|
||||
GestureAction::Config::gestures()
|
||||
{
|
||||
return _gestures;
|
||||
}
|
||||
|
||||
std::shared_ptr<Action> GestureAction::Config::noneAction()
|
||||
{
|
||||
return _none_action;
|
||||
}
|
67
src/logid/actions/GestureAction.h
Normal file
67
src/logid/actions/GestureAction.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_GESTUREACTION_H
|
||||
#define LOGID_ACTION_GESTUREACTION_H
|
||||
|
||||
#include <map>
|
||||
#include <libconfig.h++>
|
||||
#include "Action.h"
|
||||
#include "gesture/Gesture.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions {
|
||||
class GestureAction : public Action
|
||||
{
|
||||
public:
|
||||
enum Direction
|
||||
{
|
||||
None,
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right
|
||||
};
|
||||
static Direction toDirection(std::string direction);
|
||||
static Direction toDirection(int16_t x, int16_t y);
|
||||
|
||||
GestureAction(Device* dev, libconfig::Setting& config);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
virtual void move(int16_t x, int16_t y);
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
|
||||
class Config : public Action::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& root);
|
||||
std::map<Direction, std::shared_ptr<Gesture>>& gestures();
|
||||
std::shared_ptr<Action> noneAction();
|
||||
protected:
|
||||
std::map<Direction, std::shared_ptr<Gesture>> _gestures;
|
||||
std::shared_ptr<Action> _none_action;
|
||||
};
|
||||
|
||||
protected:
|
||||
int16_t _x, _y;
|
||||
Config _config;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_GESTUREACTION_H
|
89
src/logid/actions/KeypressAction.cpp
Normal file
89
src/logid/actions/KeypressAction.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "KeypressAction.h"
|
||||
#include "../util/log.h"
|
||||
#include "../InputDevice.h"
|
||||
#include "../backend/hidpp20/features/ReprogControls.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
using namespace logid::backend;
|
||||
|
||||
KeypressAction::KeypressAction(Device *device, libconfig::Setting& config) :
|
||||
Action(device), _config (device, config)
|
||||
{
|
||||
}
|
||||
|
||||
void KeypressAction::press()
|
||||
{
|
||||
_pressed = true;
|
||||
for(auto& key : _config.keys())
|
||||
virtual_input->pressKey(key);
|
||||
}
|
||||
|
||||
void KeypressAction::release()
|
||||
{
|
||||
_pressed = false;
|
||||
for(auto& key : _config.keys())
|
||||
virtual_input->releaseKey(key);
|
||||
}
|
||||
|
||||
uint8_t KeypressAction::reprogFlags() const
|
||||
{
|
||||
return hidpp20::ReprogControls::TemporaryDiverted;
|
||||
}
|
||||
|
||||
KeypressAction::Config::Config(Device* device, libconfig::Setting& config) :
|
||||
Action::Config(device)
|
||||
{
|
||||
if(!config.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: action must be an object, skipping.",
|
||||
config.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto &keys = config.lookup("keys");
|
||||
if(keys.isArray() || keys.isList()) {
|
||||
int key_count = keys.getLength();
|
||||
for(int i = 0; i < key_count; i++) {
|
||||
auto& key = keys[i];
|
||||
if(key.isNumber()) {
|
||||
_keys.push_back(key);
|
||||
} else if(key.getType() == libconfig::Setting::TypeString) {
|
||||
try {
|
||||
_keys.push_back(virtual_input->toKeyCode(key));
|
||||
} catch(InputDevice::InvalidEventCode& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid keycode %s, skipping."
|
||||
, key.getSourceLine(), key.c_str());
|
||||
}
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: keycode must be string or int",
|
||||
key.getSourceLine(), key.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: keys is a required field, skipping.",
|
||||
config.getSourceLine());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint>& KeypressAction::Config::keys()
|
||||
{
|
||||
return _keys;
|
||||
}
|
50
src/logid/actions/KeypressAction.h
Normal file
50
src/logid/actions/KeypressAction.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_KEYPRESS_H
|
||||
#define LOGID_ACTION_KEYPRESS_H
|
||||
|
||||
#include <vector>
|
||||
#include <libconfig.h++>
|
||||
#include "Action.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions {
|
||||
class KeypressAction : public Action
|
||||
{
|
||||
public:
|
||||
KeypressAction(Device* dev, libconfig::Setting& config);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
|
||||
class Config : public Action::Config
|
||||
{
|
||||
public:
|
||||
explicit Config(Device* device, libconfig::Setting& root);
|
||||
std::vector<uint>& keys();
|
||||
protected:
|
||||
std::vector<uint> _keys;
|
||||
};
|
||||
protected:
|
||||
Config _config;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_KEYPRESS_H
|
41
src/logid/actions/NullAction.cpp
Normal file
41
src/logid/actions/NullAction.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "NullAction.h"
|
||||
#include "../Device.h"
|
||||
#include "../backend/hidpp20/features/ReprogControls.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
NullAction::NullAction(Device* device) : Action(device)
|
||||
{
|
||||
}
|
||||
|
||||
void NullAction::press()
|
||||
{
|
||||
_pressed = true;
|
||||
}
|
||||
|
||||
void NullAction::release()
|
||||
{
|
||||
_pressed = false;
|
||||
}
|
||||
|
||||
uint8_t NullAction::reprogFlags() const
|
||||
{
|
||||
return backend::hidpp20::ReprogControls::TemporaryDiverted;
|
||||
}
|
39
src/logid/actions/NullAction.h
Normal file
39
src/logid/actions/NullAction.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_NULL_H
|
||||
#define LOGID_ACTION_NULL_H
|
||||
|
||||
#include "Action.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions
|
||||
{
|
||||
class NullAction : public Action
|
||||
{
|
||||
public:
|
||||
explicit NullAction(Device* device);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
};
|
||||
}}
|
||||
|
||||
|
||||
#endif //LOGID_ACTION_NULL_H
|
57
src/logid/actions/ToggleHiresScroll.cpp
Normal file
57
src/logid/actions/ToggleHiresScroll.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "ToggleHiresScroll.h"
|
||||
#include "../Device.h"
|
||||
#include "../util/task.h"
|
||||
#include "../backend/hidpp20/features/ReprogControls.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
using namespace logid::backend;
|
||||
|
||||
ToggleHiresScroll::ToggleHiresScroll(Device *dev) : Action (dev)
|
||||
{
|
||||
_hires_scroll = _device->getFeature<features::HiresScroll>("hiresscroll");
|
||||
if(!_hires_scroll)
|
||||
logPrintf(WARN, "%s:%d: HiresScroll feature not found, cannot use "
|
||||
"ToggleHiresScroll action.",
|
||||
_device->hidpp20().devicePath().c_str(),
|
||||
_device->hidpp20().devicePath().c_str());
|
||||
}
|
||||
|
||||
void ToggleHiresScroll::press()
|
||||
{
|
||||
_pressed = true;
|
||||
if(_hires_scroll)
|
||||
{
|
||||
task::spawn([hires=this->_hires_scroll](){
|
||||
auto mode = hires->getMode();
|
||||
mode ^= backend::hidpp20::HiresScroll::HiRes;
|
||||
hires->setMode(mode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ToggleHiresScroll::release()
|
||||
{
|
||||
_pressed = false;
|
||||
}
|
||||
|
||||
uint8_t ToggleHiresScroll::reprogFlags() const
|
||||
{
|
||||
return hidpp20::ReprogControls::TemporaryDiverted;
|
||||
}
|
41
src/logid/actions/ToggleHiresScroll.h
Normal file
41
src/logid/actions/ToggleHiresScroll.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_TOGGLEHIRESSCROLL_H
|
||||
#define LOGID_ACTION_TOGGLEHIRESSCROLL_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "../features/HiresScroll.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions
|
||||
{
|
||||
class ToggleHiresScroll : public Action
|
||||
{
|
||||
public:
|
||||
explicit ToggleHiresScroll(Device* dev);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
protected:
|
||||
std::shared_ptr<features::HiresScroll> _hires_scroll;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_TOGGLEHIRESSCROLL_H
|
57
src/logid/actions/ToggleSmartShift.cpp
Normal file
57
src/logid/actions/ToggleSmartShift.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "ToggleSmartShift.h"
|
||||
#include "../Device.h"
|
||||
#include "../backend/hidpp20/features/ReprogControls.h"
|
||||
#include "../util/task.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
using namespace logid::backend;
|
||||
|
||||
ToggleSmartShift::ToggleSmartShift(Device *dev) : Action (dev)
|
||||
{
|
||||
_smartshift = _device->getFeature<features::SmartShift>("smartshift");
|
||||
if(!_smartshift)
|
||||
logPrintf(WARN, "%s:%d: SmartShift feature not found, cannot use "
|
||||
"ToggleSmartShift action.",
|
||||
_device->hidpp20().devicePath().c_str(),
|
||||
_device->hidpp20().deviceIndex());
|
||||
}
|
||||
|
||||
void ToggleSmartShift::press()
|
||||
{
|
||||
_pressed = true;
|
||||
if(_smartshift) {
|
||||
task::spawn([ss=this->_smartshift](){
|
||||
auto status = ss->getStatus();
|
||||
status.setActive = true;
|
||||
status.active = !status.active;
|
||||
ss->setStatus(status);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ToggleSmartShift::release()
|
||||
{
|
||||
_pressed = false;
|
||||
}
|
||||
|
||||
uint8_t ToggleSmartShift::reprogFlags() const
|
||||
{
|
||||
return hidpp20::ReprogControls::TemporaryDiverted;
|
||||
}
|
41
src/logid/actions/ToggleSmartShift.h
Normal file
41
src/logid/actions/ToggleSmartShift.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_TOGGLESMARTSHIFT_H
|
||||
#define LOGID_ACTION_TOGGLESMARTSHIFT_H
|
||||
|
||||
#include <libconfig.h++>
|
||||
#include "Action.h"
|
||||
#include "../features/SmartShift.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions {
|
||||
class ToggleSmartShift : public Action
|
||||
{
|
||||
public:
|
||||
explicit ToggleSmartShift(Device* dev);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
protected:
|
||||
std::shared_ptr<features::SmartShift> _smartshift;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_TOGGLESMARTSHIFT_H
|
126
src/logid/actions/gesture/AxisGesture.cpp
Normal file
126
src/logid/actions/gesture/AxisGesture.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <cmath>
|
||||
#include "AxisGesture.h"
|
||||
#include "../../InputDevice.h"
|
||||
#include "../../util/log.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
AxisGesture::AxisGesture(Device *device, libconfig::Setting &root) :
|
||||
Gesture (device), _config (device, root)
|
||||
{
|
||||
}
|
||||
|
||||
void AxisGesture::press()
|
||||
{
|
||||
_axis = 0;
|
||||
_axis_remainder = 0;
|
||||
}
|
||||
|
||||
void AxisGesture::release(bool primary)
|
||||
{
|
||||
// Do nothing
|
||||
(void)primary; // Suppress unused warning
|
||||
}
|
||||
|
||||
void AxisGesture::move(int16_t axis)
|
||||
{
|
||||
int16_t new_axis = _axis + axis;
|
||||
if(new_axis > _config.threshold()) {
|
||||
double move = axis;
|
||||
if(_axis < _config.threshold())
|
||||
move = new_axis - _config.threshold();
|
||||
bool negative_multiplier = _config.multiplier() < 0;
|
||||
if(negative_multiplier)
|
||||
move *= -_config.multiplier();
|
||||
else
|
||||
move *= _config.multiplier();
|
||||
|
||||
double move_floor = floor(move);
|
||||
_axis_remainder = move - move_floor;
|
||||
if(_axis_remainder >= 1) {
|
||||
double int_remainder = floor(_axis_remainder);
|
||||
move_floor += int_remainder;
|
||||
_axis_remainder -= int_remainder;
|
||||
}
|
||||
|
||||
if(negative_multiplier)
|
||||
move_floor = -move_floor;
|
||||
|
||||
virtual_input->moveAxis(_config.axis(), move_floor);
|
||||
}
|
||||
_axis = new_axis;
|
||||
}
|
||||
|
||||
bool AxisGesture::metThreshold() const
|
||||
{
|
||||
return _axis >= _config.threshold();
|
||||
}
|
||||
|
||||
AxisGesture::Config::Config(Device *device, libconfig::Setting &setting) :
|
||||
Gesture::Config(device, setting, false)
|
||||
{
|
||||
try {
|
||||
auto& axis = setting.lookup("axis");
|
||||
if(axis.isNumber()) {
|
||||
_axis = axis;
|
||||
} else if(axis.getType() == libconfig::Setting::TypeString) {
|
||||
try {
|
||||
_axis = virtual_input->toAxisCode(axis);
|
||||
} catch(InputDevice::InvalidEventCode& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid axis %s, skipping."
|
||||
, axis.getSourceLine(), axis.c_str());
|
||||
}
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: axis must be string or int, skipping.",
|
||||
axis.getSourceLine(), axis.c_str());
|
||||
throw InvalidGesture();
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: axis is a required field, skippimg.",
|
||||
setting.getSourceLine());
|
||||
throw InvalidGesture();
|
||||
}
|
||||
|
||||
try {
|
||||
auto& multiplier = setting.lookup("axis_multiplier");
|
||||
if(multiplier.isNumber()) {
|
||||
if(multiplier.getType() == libconfig::Setting::TypeFloat)
|
||||
_multiplier = multiplier;
|
||||
else
|
||||
_multiplier = (int)multiplier;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: axis_multiplier must be a number, "
|
||||
"setting to default (1).",
|
||||
multiplier.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int AxisGesture::Config::axis() const
|
||||
{
|
||||
return _axis;
|
||||
}
|
||||
|
||||
double AxisGesture::Config::multiplier() const
|
||||
{
|
||||
return _multiplier;
|
||||
}
|
55
src/logid/actions/gesture/AxisGesture.h
Normal file
55
src/logid/actions/gesture/AxisGesture.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_AXISGESTURE_H
|
||||
#define LOGID_ACTION_AXISGESTURE_H
|
||||
|
||||
#include "Gesture.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions
|
||||
{
|
||||
class AxisGesture : public Gesture
|
||||
{
|
||||
public:
|
||||
AxisGesture(Device* device, libconfig::Setting& root);
|
||||
|
||||
virtual void press();
|
||||
virtual void release(bool primary=false);
|
||||
virtual void move(int16_t axis);
|
||||
|
||||
virtual bool metThreshold() const;
|
||||
|
||||
class Config : public Gesture::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& setting);
|
||||
unsigned int axis() const;
|
||||
double multiplier() const;
|
||||
private:
|
||||
unsigned int _axis;
|
||||
double _multiplier = 1;
|
||||
};
|
||||
|
||||
protected:
|
||||
int16_t _axis;
|
||||
double _axis_remainder;
|
||||
Config _config;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_AXISGESTURE_H
|
118
src/logid/actions/gesture/Gesture.cpp
Normal file
118
src/logid/actions/gesture/Gesture.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include "Gesture.h"
|
||||
#include "../../util/log.h"
|
||||
#include "ReleaseGesture.h"
|
||||
#include "../../backend/hidpp20/features/ReprogControls.h"
|
||||
#include "IntervalGesture.h"
|
||||
#include "AxisGesture.h"
|
||||
#include "NullGesture.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
Gesture::Gesture(Device *device) : _device (device)
|
||||
{
|
||||
}
|
||||
|
||||
Gesture::Config::Config(Device* device, libconfig::Setting& root,
|
||||
bool action_required) : _device (device)
|
||||
{
|
||||
if(action_required) {
|
||||
try {
|
||||
_action = Action::makeAction(_device,
|
||||
root.lookup("action"));
|
||||
} catch (libconfig::SettingNotFoundException &e) {
|
||||
throw InvalidGesture("action is missing");
|
||||
}
|
||||
|
||||
if(_action->reprogFlags() & backend::hidpp20::ReprogControls::RawXYDiverted)
|
||||
throw InvalidGesture("gesture cannot require RawXY");
|
||||
}
|
||||
|
||||
_threshold = LOGID_GESTURE_DEFAULT_THRESHOLD;
|
||||
try {
|
||||
auto& threshold = root.lookup("threshold");
|
||||
if(threshold.getType() == libconfig::Setting::TypeInt) {
|
||||
_threshold = (int)threshold;
|
||||
if(_threshold <= 0) {
|
||||
_threshold = LOGID_GESTURE_DEFAULT_THRESHOLD;
|
||||
logPrintf(WARN, "Line %d: threshold must be positive, setting "
|
||||
"to default (%d)", threshold.getSourceLine(),
|
||||
_threshold);
|
||||
}
|
||||
} else
|
||||
logPrintf(WARN, "Line %d: threshold must be an integer, setting "
|
||||
"to default (%d).", threshold.getSourceLine());
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Gesture> Gesture::makeGesture(Device *device,
|
||||
libconfig::Setting &setting)
|
||||
{
|
||||
if(!setting.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: Gesture is not a group, ignoring.",
|
||||
setting.getSourceLine());
|
||||
throw InvalidGesture();
|
||||
}
|
||||
|
||||
try {
|
||||
auto& gesture_mode = setting.lookup("mode");
|
||||
|
||||
if(gesture_mode.getType() != libconfig::Setting::TypeString) {
|
||||
logPrintf(WARN, "Line %d: Gesture mode must be a string,"
|
||||
"defaulting to OnRelease.",
|
||||
gesture_mode.getSourceLine());
|
||||
return std::make_shared<ReleaseGesture>(device, setting);
|
||||
}
|
||||
|
||||
std::string type = gesture_mode;
|
||||
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
||||
|
||||
if(type == "onrelease")
|
||||
return std::make_shared<ReleaseGesture>(device, setting);
|
||||
else if(type == "oninterval" || type == "onfewpixels")
|
||||
return std::make_shared<IntervalGesture>(device, setting);
|
||||
else if(type == "axis")
|
||||
return std::make_shared<AxisGesture>(device, setting);
|
||||
else if(type == "nopress")
|
||||
return std::make_shared<NullGesture>(device, setting);
|
||||
else {
|
||||
logPrintf(WARN, "Line %d: Unknown gesture mode %s, defaulting to "
|
||||
"OnRelease.", gesture_mode.getSourceLine(),
|
||||
(const char*)gesture_mode);
|
||||
return std::make_shared<ReleaseGesture>(device, setting);
|
||||
}
|
||||
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
return std::make_shared<ReleaseGesture>(device, setting);
|
||||
}
|
||||
}
|
||||
|
||||
int16_t Gesture::Config::threshold() const
|
||||
{
|
||||
return _threshold;
|
||||
}
|
||||
|
||||
std::shared_ptr<Action> Gesture::Config::action()
|
||||
{
|
||||
return _action;
|
||||
}
|
73
src/logid/actions/gesture/Gesture.h
Normal file
73
src/logid/actions/gesture/Gesture.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_GESTURE_H
|
||||
#define LOGID_ACTION_GESTURE_H
|
||||
|
||||
#include "../Action.h"
|
||||
|
||||
#define LOGID_GESTURE_DEFAULT_THRESHOLD 50
|
||||
|
||||
namespace logid {
|
||||
namespace actions
|
||||
{
|
||||
class InvalidGesture : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit InvalidGesture(std::string what="") : _what (what)
|
||||
{
|
||||
}
|
||||
virtual const char* what()
|
||||
{
|
||||
return _what.c_str();
|
||||
}
|
||||
private:
|
||||
std::string _what;
|
||||
};
|
||||
|
||||
class Gesture
|
||||
{
|
||||
public:
|
||||
virtual void press() = 0;
|
||||
virtual void release(bool primary=false) = 0;
|
||||
virtual void move(int16_t axis) = 0;
|
||||
|
||||
virtual bool metThreshold() const = 0;
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& root,
|
||||
bool action_required=true);
|
||||
virtual int16_t threshold() const;
|
||||
virtual std::shared_ptr<Action> action();
|
||||
protected:
|
||||
Device* _device;
|
||||
std::shared_ptr<Action> _action;
|
||||
int16_t _threshold;
|
||||
};
|
||||
|
||||
static std::shared_ptr<Gesture> makeGesture(Device* device,
|
||||
libconfig::Setting& setting);
|
||||
|
||||
protected:
|
||||
explicit Gesture(Device* device);
|
||||
Device* _device;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_GESTURE_H
|
91
src/logid/actions/gesture/IntervalGesture.cpp
Normal file
91
src/logid/actions/gesture/IntervalGesture.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "IntervalGesture.h"
|
||||
#include "../../util/log.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
IntervalGesture::IntervalGesture(Device *device, libconfig::Setting &root) :
|
||||
Gesture (device), _config (device, root)
|
||||
{
|
||||
}
|
||||
|
||||
void IntervalGesture::press()
|
||||
{
|
||||
_axis = 0;
|
||||
_interval_pass_count = 0;
|
||||
}
|
||||
|
||||
void IntervalGesture::release(bool primary)
|
||||
{
|
||||
// Do nothing
|
||||
(void)primary; // Suppress unused warning
|
||||
}
|
||||
|
||||
void IntervalGesture::move(int16_t axis)
|
||||
{
|
||||
_axis += axis;
|
||||
if(_axis < _config.threshold())
|
||||
return;
|
||||
|
||||
int16_t new_interval_count = (_axis - _config.threshold())/
|
||||
_config.interval();
|
||||
if(new_interval_count > _interval_pass_count) {
|
||||
_config.action()->press();
|
||||
_config.action()->release();
|
||||
}
|
||||
_interval_pass_count = new_interval_count;
|
||||
}
|
||||
|
||||
bool IntervalGesture::metThreshold() const
|
||||
{
|
||||
return _axis >= _config.threshold();
|
||||
}
|
||||
|
||||
IntervalGesture::Config::Config(Device *device, libconfig::Setting &setting) :
|
||||
Gesture::Config(device, setting)
|
||||
{
|
||||
try {
|
||||
auto& interval = setting.lookup("interval");
|
||||
if(interval.getType() != libconfig::Setting::TypeInt) {
|
||||
logPrintf(WARN, "Line %d: interval must be an integer, skipping.",
|
||||
interval.getSourceLine());
|
||||
throw InvalidGesture();
|
||||
}
|
||||
_interval = (int)interval;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
try {
|
||||
// pixels is an alias for interval
|
||||
auto& interval = setting.lookup("pixels");
|
||||
if(interval.getType() != libconfig::Setting::TypeInt) {
|
||||
logPrintf(WARN, "Line %d: pixels must be an integer, skipping.",
|
||||
interval.getSourceLine());
|
||||
throw InvalidGesture();
|
||||
}
|
||||
_interval = (int)interval;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: interval is a required field, skipping.",
|
||||
setting.getSourceLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t IntervalGesture::Config::interval() const
|
||||
{
|
||||
return _interval;
|
||||
}
|
53
src/logid/actions/gesture/IntervalGesture.h
Normal file
53
src/logid/actions/gesture/IntervalGesture.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_INTERVALGESTURE_H
|
||||
#define LOGID_ACTION_INTERVALGESTURE_H
|
||||
|
||||
#include "Gesture.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions
|
||||
{
|
||||
class IntervalGesture : public Gesture
|
||||
{
|
||||
public:
|
||||
IntervalGesture(Device* device, libconfig::Setting& root);
|
||||
|
||||
virtual void press();
|
||||
virtual void release(bool primary=false);
|
||||
virtual void move(int16_t axis);
|
||||
|
||||
virtual bool metThreshold() const;
|
||||
|
||||
class Config : public Gesture::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& setting);
|
||||
int16_t interval() const;
|
||||
private:
|
||||
int16_t _interval;
|
||||
};
|
||||
|
||||
protected:
|
||||
int16_t _axis;
|
||||
int16_t _interval_pass_count;
|
||||
Config _config;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_INTERVALGESTURE_H
|
46
src/logid/actions/gesture/NullGesture.cpp
Normal file
46
src/logid/actions/gesture/NullGesture.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "NullGesture.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
NullGesture::NullGesture(Device *device, libconfig::Setting& setting) :
|
||||
Gesture (device), _config (device, setting, false)
|
||||
{
|
||||
}
|
||||
|
||||
void NullGesture::press()
|
||||
{
|
||||
_axis = 0;
|
||||
}
|
||||
|
||||
void NullGesture::release(bool primary)
|
||||
{
|
||||
// Do nothing
|
||||
(void)primary; // Suppress unused warning
|
||||
}
|
||||
|
||||
void NullGesture::move(int16_t axis)
|
||||
{
|
||||
_axis += axis;
|
||||
}
|
||||
|
||||
bool NullGesture::metThreshold() const
|
||||
{
|
||||
return _axis > _config.threshold();
|
||||
}
|
42
src/logid/actions/gesture/NullGesture.h
Normal file
42
src/logid/actions/gesture/NullGesture.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_NULLGESTURE_H
|
||||
#define LOGID_ACTION_NULLGESTURE_H
|
||||
|
||||
#include "Gesture.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions
|
||||
{
|
||||
class NullGesture : public Gesture
|
||||
{
|
||||
public:
|
||||
NullGesture(Device* device, libconfig::Setting& setting);
|
||||
|
||||
virtual void press();
|
||||
virtual void release(bool primary=false);
|
||||
virtual void move(int16_t axis);
|
||||
|
||||
virtual bool metThreshold() const;
|
||||
protected:
|
||||
int16_t _axis;
|
||||
Gesture::Config _config;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_NULLGESTURE_H
|
48
src/logid/actions/gesture/ReleaseGesture.cpp
Normal file
48
src/logid/actions/gesture/ReleaseGesture.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "ReleaseGesture.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
ReleaseGesture::ReleaseGesture(Device *device, libconfig::Setting &root) :
|
||||
Gesture (device), _config (device, root)
|
||||
{
|
||||
}
|
||||
|
||||
void ReleaseGesture::press()
|
||||
{
|
||||
_axis = 0;
|
||||
}
|
||||
|
||||
void ReleaseGesture::release(bool primary)
|
||||
{
|
||||
if(metThreshold() && primary) {
|
||||
_config.action()->press();
|
||||
_config.action()->release();
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseGesture::move(int16_t axis)
|
||||
{
|
||||
_axis += axis;
|
||||
}
|
||||
|
||||
bool ReleaseGesture::metThreshold() const
|
||||
{
|
||||
return _axis >= _config.threshold();
|
||||
}
|
43
src/logid/actions/gesture/ReleaseGesture.h
Normal file
43
src/logid/actions/gesture/ReleaseGesture.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_ACTION_RELEASEGESTURE_H
|
||||
#define LOGID_ACTION_RELEASEGESTURE_H
|
||||
|
||||
#include "Gesture.h"
|
||||
|
||||
namespace logid {
|
||||
namespace actions
|
||||
{
|
||||
class ReleaseGesture : public Gesture
|
||||
{
|
||||
public:
|
||||
ReleaseGesture(Device* device, libconfig::Setting& root);
|
||||
|
||||
virtual void press();
|
||||
virtual void release(bool primary=false);
|
||||
virtual void move(int16_t axis);
|
||||
|
||||
virtual bool metThreshold() const;
|
||||
|
||||
protected:
|
||||
int16_t _axis;
|
||||
Gesture::Config _config;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_ACTION_RELEASEGESTURE_H
|
24
src/logid/backend/Error.cpp
Normal file
24
src/logid/backend/Error.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Error.h"
|
||||
|
||||
const char *logid::backend::TimeoutError::what() const noexcept
|
||||
{
|
||||
return "Device timed out";
|
||||
}
|
34
src/logid/backend/Error.h
Normal file
34
src/logid/backend/Error.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_ERROR_H
|
||||
#define LOGID_BACKEND_ERROR_H
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
class TimeoutError: public std::exception
|
||||
{
|
||||
public:
|
||||
TimeoutError() = default;
|
||||
const char* what() const noexcept override;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_BACKEND_ERROR_H
|
42
src/logid/backend/dj/Error.cpp
Normal file
42
src/logid/backend/dj/Error.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Error.h"
|
||||
|
||||
using namespace logid::backend::dj;
|
||||
|
||||
Error::Error(uint8_t code) : _code (code)
|
||||
{
|
||||
}
|
||||
|
||||
const char* Error::what() const noexcept
|
||||
{
|
||||
switch(_code) {
|
||||
case Unknown:
|
||||
return "Unknown";
|
||||
case KeepAliveTimeout:
|
||||
return "Keep-alive timeout";
|
||||
default:
|
||||
return "Reserved";
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Error::code() const noexcept
|
||||
{
|
||||
return _code;
|
||||
}
|
48
src/logid/backend/dj/Error.h
Normal file
48
src/logid/backend/dj/Error.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_DJ_ERROR_H
|
||||
#define LOGID_BACKEND_DJ_ERROR_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace dj
|
||||
{
|
||||
class Error : public std::exception
|
||||
{
|
||||
public:
|
||||
enum ErrorCode : uint8_t
|
||||
{
|
||||
Unknown = 0x00,
|
||||
KeepAliveTimeout = 0x01
|
||||
};
|
||||
|
||||
explicit Error(uint8_t code);
|
||||
|
||||
const char* what() const noexcept override;
|
||||
uint8_t code() const noexcept;
|
||||
|
||||
private:
|
||||
uint8_t _code;
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_DJ_ERROR_H
|
383
src/logid/backend/dj/Receiver.cpp
Normal file
383
src/logid/backend/dj/Receiver.cpp
Normal file
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include "Report.h"
|
||||
#include "Receiver.h"
|
||||
#include "Error.h"
|
||||
#include "../../util/thread.h"
|
||||
|
||||
using namespace logid::backend::dj;
|
||||
using namespace logid::backend;
|
||||
|
||||
InvalidReceiver::InvalidReceiver(Reason reason) : _reason (reason)
|
||||
{
|
||||
}
|
||||
|
||||
const char* InvalidReceiver::what() const noexcept
|
||||
{
|
||||
switch(_reason) {
|
||||
case NoDJReports:
|
||||
return "No DJ reports";
|
||||
default:
|
||||
return "Invalid receiver";
|
||||
}
|
||||
}
|
||||
|
||||
InvalidReceiver::Reason InvalidReceiver::code() const noexcept
|
||||
{
|
||||
return _reason;
|
||||
}
|
||||
|
||||
Receiver::Receiver(std::string path) :
|
||||
_raw_device (std::make_shared<raw::RawDevice>(std::move(path))),
|
||||
_hidpp10_device (_raw_device, hidpp::DefaultDevice)
|
||||
{
|
||||
if(!supportsDjReports(_raw_device->reportDescriptor()))
|
||||
throw InvalidReceiver(InvalidReceiver::NoDJReports);
|
||||
}
|
||||
|
||||
void Receiver::enumerateDj()
|
||||
{
|
||||
_sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{});
|
||||
}
|
||||
|
||||
Receiver::NotificationFlags Receiver::getHidppNotifications()
|
||||
{
|
||||
auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {},
|
||||
hidpp::ReportType::Short);
|
||||
|
||||
NotificationFlags flags{};
|
||||
flags.deviceBatteryStatus = response[0] & (1 << 4);
|
||||
flags.receiverWirelessNotifications = response[1] & (1 << 0);
|
||||
flags.receiverSoftwarePresent = response[1] & (1 << 3);
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
void Receiver::enableHidppNotifications(NotificationFlags flags)
|
||||
{
|
||||
std::vector<uint8_t> request(3);
|
||||
|
||||
if(flags.deviceBatteryStatus)
|
||||
request[0] |= (1 << 4);
|
||||
if(flags.receiverWirelessNotifications)
|
||||
request[1] |= 1;
|
||||
if(flags.receiverSoftwarePresent)
|
||||
request[1] |= (1 << 3);
|
||||
|
||||
_hidpp10_device.setRegister(EnableHidppNotifications, request,
|
||||
hidpp::ReportType::Short);
|
||||
}
|
||||
|
||||
void Receiver::enumerateHidpp()
|
||||
{
|
||||
/* This isn't in the documentation but this is how solaar does it
|
||||
* All I know is that when (p0 & 2), devices are enumerated
|
||||
*/
|
||||
_hidpp10_device.setRegister(ConnectionState, {2},
|
||||
hidpp::ReportType::Short);
|
||||
}
|
||||
|
||||
///TODO: Investigate usage
|
||||
uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index)
|
||||
{
|
||||
auto response = _hidpp10_device.getRegister(ConnectionState, {index},
|
||||
hidpp::ReportType::Short);
|
||||
|
||||
return response[0];
|
||||
}
|
||||
|
||||
void Receiver::startPairing(uint8_t timeout)
|
||||
{
|
||||
///TODO: Device number == Device index?
|
||||
std::vector<uint8_t> request(3);
|
||||
|
||||
request[0] = 1;
|
||||
request[1] = hidpp::DefaultDevice;
|
||||
request[2] = timeout;
|
||||
|
||||
_hidpp10_device.setRegister(DevicePairing, request,
|
||||
hidpp::ReportType::Short);
|
||||
}
|
||||
|
||||
void Receiver::stopPairing()
|
||||
{
|
||||
///TODO: Device number == Device index?
|
||||
std::vector<uint8_t> request(3);
|
||||
|
||||
request[0] = 2;
|
||||
request[1] = hidpp::DefaultDevice;
|
||||
|
||||
_hidpp10_device.setRegister(DevicePairing, request,
|
||||
hidpp::ReportType::Short);
|
||||
}
|
||||
|
||||
void Receiver::disconnect(hidpp::DeviceIndex index)
|
||||
{
|
||||
///TODO: Device number == Device index?
|
||||
std::vector<uint8_t> request(3);
|
||||
|
||||
request[0] = 3;
|
||||
request[1] = index;
|
||||
|
||||
_hidpp10_device.setRegister(DevicePairing, request,
|
||||
hidpp::ReportType::Short);
|
||||
}
|
||||
|
||||
std::map<hidpp::DeviceIndex, uint8_t> Receiver::getDeviceActivity()
|
||||
{
|
||||
auto response = _hidpp10_device.getRegister(DeviceActivity, {},
|
||||
hidpp::ReportType::Long);
|
||||
|
||||
std::map<hidpp::DeviceIndex, uint8_t> device_activity;
|
||||
for(uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++)
|
||||
device_activity[static_cast<hidpp::DeviceIndex>(i)] = response[i];
|
||||
|
||||
return device_activity;
|
||||
}
|
||||
|
||||
struct Receiver::PairingInfo
|
||||
Receiver::getPairingInfo(hidpp::DeviceIndex index)
|
||||
{
|
||||
std::vector<uint8_t> request(1);
|
||||
request[0] = index;
|
||||
request[0] += 0x1f;
|
||||
|
||||
auto response = _hidpp10_device.getRegister(PairingInfo, request,
|
||||
hidpp::ReportType::Long);
|
||||
|
||||
struct PairingInfo info{};
|
||||
info.destinationId = response[1];
|
||||
info.reportInterval = response[2];
|
||||
info.pid = response[4];
|
||||
info.pid |= (response[3] << 8);
|
||||
info.deviceType = static_cast<DeviceType::DeviceType>(response[7]);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct Receiver::ExtendedPairingInfo
|
||||
Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index)
|
||||
{
|
||||
std::vector<uint8_t> request(1);
|
||||
request[0] = index;
|
||||
request[0] += 0x2f;
|
||||
|
||||
auto response = _hidpp10_device.getRegister(PairingInfo, request,
|
||||
hidpp::ReportType::Long);
|
||||
|
||||
ExtendedPairingInfo info{};
|
||||
|
||||
info.serialNumber = 0;
|
||||
for(uint8_t i = 0; i < 4; i++)
|
||||
info.serialNumber |= (response[i+1] << 8*i);
|
||||
|
||||
for(uint8_t i = 0; i < 4; i++)
|
||||
info.reportTypes[i] = response[i + 5];
|
||||
|
||||
uint8_t psl = response[8] & 0xf;
|
||||
if(psl > 0xc)
|
||||
info.powerSwitchLocation = PowerSwitchLocation::Reserved;
|
||||
else
|
||||
info.powerSwitchLocation = static_cast<PowerSwitchLocation>(psl);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
std::string Receiver::getDeviceName(hidpp::DeviceIndex index)
|
||||
{
|
||||
std::vector<uint8_t> request(1);
|
||||
request[0] = index;
|
||||
request[0] += 0x3f;
|
||||
|
||||
auto response = _hidpp10_device.getRegister(PairingInfo, request,
|
||||
hidpp::ReportType::Long);
|
||||
|
||||
uint8_t size = response[1];
|
||||
assert(size <= 14);
|
||||
|
||||
std::string name(size, ' ');
|
||||
for(std::size_t i = 0; i < size; i++)
|
||||
name[i] = response[i + 2];
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(const hidpp::Report&
|
||||
report)
|
||||
{
|
||||
assert(report.subId() == DeviceDisconnection);
|
||||
return report.deviceIndex();
|
||||
}
|
||||
|
||||
hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(const
|
||||
hidpp::Report &report)
|
||||
{
|
||||
assert(report.subId() == DeviceConnection);
|
||||
|
||||
hidpp::DeviceConnectionEvent event{};
|
||||
|
||||
event.index = report.deviceIndex();
|
||||
event.unifying = ((report.address() & 0b111) == 0x04);
|
||||
|
||||
event.deviceType = static_cast<DeviceType::DeviceType>(
|
||||
report.paramBegin()[0] & 0x0f);
|
||||
event.softwarePresent = report.paramBegin()[0] & (1<<4);
|
||||
event.encrypted = report.paramBegin()[0] & (1<<5);
|
||||
event.linkEstablished = !(report.paramBegin()[0] & (1<<6));
|
||||
event.withPayload = report.paramBegin()[0] & (1<<7);
|
||||
event.fromTimeoutCheck = false;
|
||||
|
||||
event.pid =(report.paramBegin()[2] << 8);
|
||||
event.pid |= report.paramBegin()[1];
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void Receiver::_handleDjEvent(Report& report)
|
||||
{
|
||||
for(auto& handler : _dj_event_handlers)
|
||||
if(handler.second->condition(report))
|
||||
handler.second->callback(report);
|
||||
}
|
||||
|
||||
void Receiver::_handleHidppEvent(hidpp::Report &report)
|
||||
{
|
||||
for(auto& handler : _hidpp_event_handlers)
|
||||
if(handler.second->condition(report))
|
||||
handler.second->callback(report);
|
||||
}
|
||||
|
||||
void Receiver::addDjEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<EventHandler>& handler)
|
||||
{
|
||||
auto it = _dj_event_handlers.find(nickname);
|
||||
assert(it == _dj_event_handlers.end());
|
||||
_dj_event_handlers.emplace(nickname, handler);
|
||||
}
|
||||
|
||||
void Receiver::removeDjEventHandler(const std::string &nickname)
|
||||
{
|
||||
_dj_event_handlers.erase(nickname);
|
||||
}
|
||||
|
||||
const std::map<std::string, std::shared_ptr<EventHandler>>&
|
||||
Receiver::djEventHandlers()
|
||||
{
|
||||
return _dj_event_handlers;
|
||||
}
|
||||
|
||||
void Receiver::addHidppEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<hidpp::EventHandler>& handler)
|
||||
{
|
||||
auto it = _hidpp_event_handlers.find(nickname);
|
||||
assert(it == _hidpp_event_handlers.end());
|
||||
_hidpp_event_handlers.emplace(nickname, handler);
|
||||
}
|
||||
|
||||
void Receiver::removeHidppEventHandler(const std::string &nickname)
|
||||
{
|
||||
_hidpp_event_handlers.erase(nickname);
|
||||
}
|
||||
|
||||
const std::map<std::string, std::shared_ptr<hidpp::EventHandler>>&
|
||||
Receiver::hidppEventHandlers()
|
||||
{
|
||||
return _hidpp_event_handlers;
|
||||
}
|
||||
|
||||
void Receiver::_sendDjRequest(hidpp::DeviceIndex index, uint8_t function,
|
||||
const std::vector<uint8_t>&& params)
|
||||
{
|
||||
assert(params.size() <= LongParamLength);
|
||||
|
||||
Report::Type type = params.size() <= ShortParamLength ?
|
||||
ReportType::Short : ReportType::Long;
|
||||
|
||||
Report request(type, index, function);
|
||||
|
||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
||||
|
||||
_raw_device->sendReportNoResponse(request.rawData());
|
||||
}
|
||||
|
||||
Receiver::ConnectionStatusEvent Receiver::connectionStatusEvent(Report& report)
|
||||
{
|
||||
assert(report.feature() == ConnectionStatus);
|
||||
ConnectionStatusEvent event{};
|
||||
event.index = report.index();
|
||||
event.linkLost = report.paramBegin()[0];
|
||||
return event;
|
||||
}
|
||||
|
||||
void Receiver::listen()
|
||||
{
|
||||
if(!_raw_device->isListening())
|
||||
_raw_device->listenAsync();
|
||||
|
||||
if(_raw_device->eventHandlers().find("RECV_HIDPP") ==
|
||||
_raw_device->eventHandlers().end()) {
|
||||
// Pass all HID++ events on DefaultDevice to handleHidppEvent
|
||||
std::shared_ptr<raw::RawEventHandler> hidpp_handler =
|
||||
std::make_shared<raw::RawEventHandler>();
|
||||
hidpp_handler->condition = [](std::vector<uint8_t>& report)->bool
|
||||
{
|
||||
return (report[hidpp::Offset::Type] == hidpp::Report::Type::Short ||
|
||||
report[hidpp::Offset::Type] == hidpp::Report::Type::Long);
|
||||
};
|
||||
hidpp_handler->callback = [this](std::vector<uint8_t>& report)
|
||||
->void {
|
||||
hidpp::Report _report(report);
|
||||
this->_handleHidppEvent(_report);
|
||||
};
|
||||
_raw_device->addEventHandler("RECV_HIDPP", hidpp_handler);
|
||||
}
|
||||
|
||||
if(_raw_device->eventHandlers().find("RECV_DJ") ==
|
||||
_raw_device->eventHandlers().end()) {
|
||||
// Pass all DJ events with device index to handleDjEvent
|
||||
std::shared_ptr<raw::RawEventHandler> dj_handler =
|
||||
std::make_shared<raw::RawEventHandler>();
|
||||
dj_handler->condition = [](std::vector<uint8_t>& report)->bool
|
||||
{
|
||||
return (report[Offset::Type] == Report::Type::Short ||
|
||||
report[Offset::Type] == Report::Type::Long);
|
||||
};
|
||||
dj_handler->callback = [this](std::vector<uint8_t>& report)->void
|
||||
{
|
||||
Report _report(report);
|
||||
this->_handleDjEvent(_report);
|
||||
};
|
||||
_raw_device->addEventHandler("RECV_DJ", dj_handler);
|
||||
}
|
||||
}
|
||||
|
||||
void Receiver::stopListening()
|
||||
{
|
||||
_raw_device->removeEventHandler("RECV_HIDPP");
|
||||
_raw_device->removeEventHandler("RECV_DJ");
|
||||
|
||||
if(_raw_device->eventHandlers().empty())
|
||||
_raw_device->stopListener();
|
||||
}
|
||||
|
||||
std::shared_ptr<raw::RawDevice> Receiver::rawDevice() const
|
||||
{
|
||||
return _raw_device;
|
||||
}
|
213
src/logid/backend/dj/Receiver.h
Normal file
213
src/logid/backend/dj/Receiver.h
Normal file
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_DJ_RECEIVER_H
|
||||
#define LOGID_BACKEND_DJ_RECEIVER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "../raw/RawDevice.h"
|
||||
#include "Report.h"
|
||||
#include "../hidpp/Report.h"
|
||||
#include "../hidpp10/Device.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace dj
|
||||
{
|
||||
struct EventHandler
|
||||
{
|
||||
std::function<bool(Report&)> condition;
|
||||
std::function<void(Report&)> callback;
|
||||
};
|
||||
|
||||
class InvalidReceiver : public std::exception
|
||||
{
|
||||
public:
|
||||
enum Reason
|
||||
{
|
||||
NoDJReports
|
||||
};
|
||||
explicit InvalidReceiver(Reason reason);
|
||||
const char* what() const noexcept override;
|
||||
Reason code() const noexcept;
|
||||
private:
|
||||
Reason _reason;
|
||||
};
|
||||
|
||||
class Receiver
|
||||
{
|
||||
public:
|
||||
explicit Receiver(std::string path);
|
||||
|
||||
enum DjEvents : uint8_t
|
||||
{
|
||||
DeviceDisconnection = 0x40,
|
||||
DeviceConnection = 0x41,
|
||||
ConnectionStatus = 0x42
|
||||
};
|
||||
|
||||
enum DjCommands : uint8_t
|
||||
{
|
||||
SwitchAndKeepAlive = 0x80,
|
||||
GetPairedDevices = 0x81
|
||||
};
|
||||
|
||||
void enumerateDj();
|
||||
|
||||
struct ConnectionStatusEvent
|
||||
{
|
||||
hidpp::DeviceIndex index;
|
||||
bool linkLost;
|
||||
};
|
||||
|
||||
ConnectionStatusEvent connectionStatusEvent(dj::Report& report);
|
||||
|
||||
/* The following functions deal with HID++ 1.0 features.
|
||||
* While these are not technically DJ functions, it is redundant
|
||||
* to have a separate hidpp10::Receiver class for these functions.
|
||||
*/
|
||||
|
||||
enum HidppEvents : uint8_t
|
||||
{
|
||||
// These events are identical to their DJ counterparts
|
||||
// DeviceDisconnection = 0x40,
|
||||
// DeviceConnection = 0x41,
|
||||
LockingChange = 0x4a
|
||||
};
|
||||
|
||||
enum HidppRegisters : uint8_t
|
||||
{
|
||||
EnableHidppNotifications = 0x00,
|
||||
ConnectionState = 0x02,
|
||||
DevicePairing = 0xb2,
|
||||
DeviceActivity = 0xb3,
|
||||
PairingInfo = 0xb5
|
||||
};
|
||||
|
||||
struct NotificationFlags
|
||||
{
|
||||
bool deviceBatteryStatus;
|
||||
bool receiverWirelessNotifications;
|
||||
bool receiverSoftwarePresent;
|
||||
};
|
||||
|
||||
NotificationFlags getHidppNotifications();
|
||||
void enableHidppNotifications(NotificationFlags flags);
|
||||
|
||||
void enumerateHidpp();
|
||||
uint8_t getConnectionState(hidpp::DeviceIndex index);
|
||||
|
||||
void startPairing(uint8_t timeout = 0);
|
||||
void stopPairing();
|
||||
void disconnect(hidpp::DeviceIndex index);
|
||||
|
||||
std::map<hidpp::DeviceIndex, uint8_t> getDeviceActivity();
|
||||
|
||||
struct PairingInfo
|
||||
{
|
||||
uint8_t destinationId;
|
||||
uint8_t reportInterval;
|
||||
uint16_t pid;
|
||||
DeviceType::DeviceType deviceType;
|
||||
};
|
||||
|
||||
enum class PowerSwitchLocation : uint8_t
|
||||
{
|
||||
Reserved = 0x0,
|
||||
Base = 0x1,
|
||||
TopCase = 0x2,
|
||||
TopRightEdge = 0x3,
|
||||
Other = 0x4,
|
||||
TopLeft = 0x5,
|
||||
BottomLeft = 0x6,
|
||||
TopRight = 0x7,
|
||||
BottomRight = 0x8,
|
||||
TopEdge = 0x9,
|
||||
RightEdge = 0xa,
|
||||
LeftEdge = 0xb,
|
||||
BottomEdge = 0xc
|
||||
};
|
||||
|
||||
struct ExtendedPairingInfo
|
||||
{
|
||||
uint32_t serialNumber;
|
||||
uint8_t reportTypes[4];
|
||||
PowerSwitchLocation powerSwitchLocation;
|
||||
};
|
||||
|
||||
struct PairingInfo getPairingInfo(hidpp::DeviceIndex index);
|
||||
struct ExtendedPairingInfo getExtendedPairingInfo(hidpp::DeviceIndex
|
||||
index);
|
||||
|
||||
std::string getDeviceName(hidpp::DeviceIndex index);
|
||||
|
||||
static hidpp::DeviceIndex deviceDisconnectionEvent(
|
||||
const hidpp::Report& report);
|
||||
static hidpp::DeviceConnectionEvent deviceConnectionEvent(
|
||||
const hidpp::Report& report);
|
||||
|
||||
void listen();
|
||||
void stopListening();
|
||||
|
||||
void addDjEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<EventHandler>& handler);
|
||||
void removeDjEventHandler(const std::string& nickname);
|
||||
const std::map<std::string, std::shared_ptr<EventHandler>>&
|
||||
djEventHandlers();
|
||||
|
||||
void addHidppEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<hidpp::EventHandler>& handler);
|
||||
void removeHidppEventHandler(const std::string& nickname);
|
||||
const std::map<std::string, std::shared_ptr<hidpp::EventHandler>>&
|
||||
hidppEventHandlers();
|
||||
|
||||
std::shared_ptr<raw::RawDevice> rawDevice() const;
|
||||
private:
|
||||
void _sendDjRequest(hidpp::DeviceIndex index, uint8_t function,
|
||||
const std::vector<uint8_t>&& params);
|
||||
|
||||
void _handleDjEvent(dj::Report& report);
|
||||
void _handleHidppEvent(hidpp::Report& report);
|
||||
|
||||
std::map<std::string, std::shared_ptr<EventHandler>>
|
||||
_dj_event_handlers;
|
||||
std::map<std::string, std::shared_ptr<hidpp::EventHandler>>
|
||||
_hidpp_event_handlers;
|
||||
|
||||
std::shared_ptr<raw::RawDevice> _raw_device;
|
||||
hidpp10::Device _hidpp10_device;
|
||||
};
|
||||
}
|
||||
|
||||
namespace hidpp
|
||||
{
|
||||
struct DeviceConnectionEvent
|
||||
{
|
||||
hidpp::DeviceIndex index;
|
||||
uint16_t pid;
|
||||
dj::DeviceType::DeviceType deviceType;
|
||||
bool unifying;
|
||||
bool softwarePresent;
|
||||
bool encrypted;
|
||||
bool linkEstablished;
|
||||
bool withPayload;
|
||||
bool fromTimeoutCheck = false; // Fake field
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_DJ_RECEIVER_H
|
137
src/logid/backend/dj/ReceiverMonitor.cpp
Normal file
137
src/logid/backend/dj/ReceiverMonitor.cpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ReceiverMonitor.h"
|
||||
#include "../../util/task.h"
|
||||
#include "../../util/log.h"
|
||||
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
|
||||
using namespace logid::backend::dj;
|
||||
|
||||
ReceiverMonitor::ReceiverMonitor(std::string path) : _receiver (
|
||||
std::make_shared<Receiver>(std::move(path)))
|
||||
{
|
||||
assert(_receiver->hidppEventHandlers().find("RECVMON") ==
|
||||
_receiver->hidppEventHandlers().end());
|
||||
assert(_receiver->djEventHandlers().find("RECVMON") ==
|
||||
_receiver->djEventHandlers().end());
|
||||
|
||||
Receiver::NotificationFlags notification_flags{
|
||||
true,
|
||||
true,
|
||||
true};
|
||||
_receiver->enableHidppNotifications(notification_flags);
|
||||
}
|
||||
|
||||
ReceiverMonitor::~ReceiverMonitor()
|
||||
{
|
||||
this->stop();
|
||||
}
|
||||
|
||||
void ReceiverMonitor::run()
|
||||
{
|
||||
_receiver->listen();
|
||||
|
||||
if(_receiver->hidppEventHandlers().find("RECVMON") ==
|
||||
_receiver->hidppEventHandlers().end()) {
|
||||
std::shared_ptr<hidpp::EventHandler> event_handler =
|
||||
std::make_shared<hidpp::EventHandler>();
|
||||
event_handler->condition = [](hidpp::Report &report) -> bool {
|
||||
return (report.subId() == Receiver::DeviceConnection ||
|
||||
report.subId() == Receiver::DeviceDisconnection);
|
||||
};
|
||||
|
||||
event_handler->callback = [this](hidpp::Report &report) -> void {
|
||||
/* Running in a new thread prevents deadlocks since the
|
||||
* receiver may be enumerating.
|
||||
*/
|
||||
task::spawn({[this, report]() {
|
||||
if (report.subId() == Receiver::DeviceConnection)
|
||||
this->addDevice(this->_receiver->deviceConnectionEvent
|
||||
(report));
|
||||
else if (report.subId() == Receiver::DeviceDisconnection)
|
||||
this->removeDevice(this->_receiver->
|
||||
deviceDisconnectionEvent(report));
|
||||
}}, {[report, path=this->_receiver->rawDevice()->hidrawPath()]
|
||||
(std::exception& e) {
|
||||
if(report.subId() == Receiver::DeviceConnection)
|
||||
logPrintf(ERROR, "Failed to add device %d to receiver "
|
||||
"on %s: %s", report.deviceIndex(),
|
||||
path.c_str(), e.what());
|
||||
else if(report.subId() == Receiver::DeviceDisconnection)
|
||||
logPrintf(ERROR, "Failed to remove device %d from "
|
||||
"receiver on %s: %s", report.deviceIndex()
|
||||
,path.c_str(), e.what());
|
||||
}});
|
||||
};
|
||||
|
||||
_receiver->addHidppEventHandler("RECVMON", event_handler);
|
||||
}
|
||||
|
||||
enumerate();
|
||||
}
|
||||
|
||||
void ReceiverMonitor::stop()
|
||||
{
|
||||
_receiver->removeHidppEventHandler("RECVMON");
|
||||
|
||||
_receiver->stopListening();
|
||||
}
|
||||
|
||||
void ReceiverMonitor::enumerate()
|
||||
{
|
||||
_receiver->enumerateHidpp();
|
||||
}
|
||||
|
||||
void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index)
|
||||
{
|
||||
std::string nickname = "WAIT_DEV_" + std::to_string(index);
|
||||
auto handler = std::make_shared<raw::RawEventHandler>();
|
||||
handler->condition = [index](std::vector<uint8_t>& report)->bool {
|
||||
return report[Offset::DeviceIndex] == index;
|
||||
};
|
||||
|
||||
handler->callback = [this, index, nickname](std::vector<uint8_t>& report) {
|
||||
(void)report; // Suppress unused warning
|
||||
|
||||
hidpp::DeviceConnectionEvent event{};
|
||||
event.withPayload = false;
|
||||
event.linkEstablished = true;
|
||||
event.index = index;
|
||||
event.fromTimeoutCheck = true;
|
||||
|
||||
task::spawn({[this, event, nickname]() {
|
||||
_receiver->rawDevice()->removeEventHandler(nickname);
|
||||
this->addDevice(event);
|
||||
}}, {[path=_receiver->rawDevice()->hidrawPath(), event]
|
||||
(std::exception& e) {
|
||||
logPrintf(ERROR, "Failed to add device %d to receiver "
|
||||
"on %s: %s", event.index,
|
||||
path.c_str(), e.what());
|
||||
}});
|
||||
};
|
||||
|
||||
_receiver->rawDevice()->addEventHandler(nickname, handler);
|
||||
}
|
||||
|
||||
std::shared_ptr<Receiver> ReceiverMonitor::receiver() const
|
||||
{
|
||||
return _receiver;
|
||||
}
|
61
src/logid/backend/dj/ReceiverMonitor.h
Normal file
61
src/logid/backend/dj/ReceiverMonitor.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_DJ_RECEIVERMONITOR_H
|
||||
#define LOGID_BACKEND_DJ_RECEIVERMONITOR_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include "Receiver.h"
|
||||
#include "../hidpp/defs.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace dj
|
||||
{
|
||||
// This class will run on the RawDevice thread,
|
||||
class ReceiverMonitor
|
||||
{
|
||||
public:
|
||||
explicit ReceiverMonitor(std::string path);
|
||||
~ReceiverMonitor();
|
||||
|
||||
void enumerate();
|
||||
void run();
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
virtual void addDevice(hidpp::DeviceConnectionEvent event) = 0;
|
||||
virtual void removeDevice(hidpp::DeviceIndex index) = 0;
|
||||
|
||||
void waitForDevice(hidpp::DeviceIndex index);
|
||||
|
||||
// Internal methods for derived class
|
||||
void _pair(uint8_t timeout = 0);
|
||||
void _stopPairing();
|
||||
|
||||
void _unpair();
|
||||
|
||||
std::shared_ptr<Receiver> receiver() const;
|
||||
private:
|
||||
std::shared_ptr<Receiver> _receiver;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_DJ_RECEIVERMONITOR_H
|
135
src/logid/backend/dj/Report.cpp
Normal file
135
src/logid/backend/dj/Report.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include "Report.h"
|
||||
|
||||
using namespace logid::backend::dj;
|
||||
using namespace logid::backend;
|
||||
|
||||
static const std::array<uint8_t, 34> DJReportDesc = {
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x20, // Report ID (32)
|
||||
0x95, 0x0E, // Report Count (14)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x41, // Usage (0x41)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x41, // Usage (0x41)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0x85, 0x21, // Report ID (33)
|
||||
0x95, 0x1F, // Report Count (31)
|
||||
0x09, 0x42, // Usage (0x42)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x42, // Usage (0x42)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
static const std::array<uint8_t, 39> DJReportDesc2 = {
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x20, // Report ID (32)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x0E, // Report Count (14)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x41, // Usage (0x41)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x41, // Usage (0x41)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0x85, 0x21, // Report ID (33)
|
||||
0x95, 0x1F, // Report Count (31)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x42, // Usage (0x42)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x42, // Usage (0x42)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
bool dj::supportsDjReports(std::vector<uint8_t>&& rdesc)
|
||||
{
|
||||
auto it = std::search(rdesc.begin(), rdesc.end(),
|
||||
DJReportDesc.begin(), DJReportDesc.end());
|
||||
if(it == rdesc.end())
|
||||
it = std::search(rdesc.begin(), rdesc.end(),
|
||||
DJReportDesc2.begin(), DJReportDesc2.end());
|
||||
return it != rdesc.end();
|
||||
}
|
||||
|
||||
Report::Report(std::vector<uint8_t>& data) : _data (data)
|
||||
{
|
||||
switch(data[Offset::Type]) {
|
||||
case ReportType::Short:
|
||||
_data.resize(HeaderLength+ShortParamLength);
|
||||
break;
|
||||
case ReportType::Long:
|
||||
_data.resize(HeaderLength+LongParamLength);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
Report::Report(Report::Type type, hidpp::DeviceIndex index, uint8_t feature)
|
||||
{
|
||||
switch(type) {
|
||||
case ReportType::Short:
|
||||
_data.resize(HeaderLength+ShortParamLength);
|
||||
break;
|
||||
case ReportType::Long:
|
||||
_data.resize(HeaderLength+LongParamLength);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
_data[Offset::Type] = type;
|
||||
_data[Offset::DeviceIndex] = index;
|
||||
_data[Offset::Feature] = feature;
|
||||
}
|
||||
|
||||
|
||||
Report::Type Report::type() const
|
||||
{
|
||||
return static_cast<Type>(_data[Offset::Type]);
|
||||
}
|
||||
|
||||
hidpp::DeviceIndex Report::index() const
|
||||
{
|
||||
return static_cast<hidpp::DeviceIndex>(_data[Offset::DeviceIndex]);
|
||||
}
|
||||
|
||||
uint8_t Report::feature() const
|
||||
{
|
||||
return _data[Offset::Feature];
|
||||
}
|
||||
|
||||
std::vector<uint8_t>::iterator Report::paramBegin()
|
||||
{
|
||||
return _data.begin() + Offset::Parameters;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Report::rawData() const
|
||||
{
|
||||
return _data;
|
||||
}
|
58
src/logid/backend/dj/Report.h
Normal file
58
src/logid/backend/dj/Report.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_DJ_REPORT_H
|
||||
#define LOGID_BACKEND_DJ_REPORT_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "../raw/RawDevice.h"
|
||||
#include "defs.h"
|
||||
#include "../hidpp/defs.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace dj
|
||||
{
|
||||
namespace Offset
|
||||
{
|
||||
static constexpr uint8_t Type = 0;
|
||||
static constexpr uint8_t DeviceIndex = 1;
|
||||
static constexpr uint8_t Feature = 2;
|
||||
static constexpr uint8_t Parameters = 3;
|
||||
}
|
||||
|
||||
bool supportsDjReports(std::vector<uint8_t>&& rdesc);
|
||||
class Report
|
||||
{
|
||||
public:
|
||||
typedef ReportType::ReportType Type;
|
||||
|
||||
explicit Report(std::vector<uint8_t>& data);
|
||||
Report(Type type, hidpp::DeviceIndex index, uint8_t feature);
|
||||
|
||||
Type type() const;
|
||||
hidpp::DeviceIndex index() const;
|
||||
uint8_t feature() const;
|
||||
std::vector<uint8_t>::iterator paramBegin();
|
||||
std::vector<uint8_t> rawData() const;
|
||||
private:
|
||||
std::vector<uint8_t> _data;
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_DJ_REPORT_H
|
59
src/logid/backend/dj/defs.h
Normal file
59
src/logid/backend/dj/defs.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_DJ_DEFS_H
|
||||
#define LOGID_BACKEND_DJ_DEFS_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace dj
|
||||
{
|
||||
namespace ReportType
|
||||
{
|
||||
enum ReportType : uint8_t
|
||||
{
|
||||
Short = 0x20,
|
||||
Long = 0x21
|
||||
};
|
||||
}
|
||||
|
||||
namespace DeviceType
|
||||
{
|
||||
enum DeviceType : uint8_t
|
||||
{
|
||||
Unknown = 0x00,
|
||||
Keyboard = 0x01,
|
||||
Mouse = 0x02,
|
||||
Numpad = 0x03,
|
||||
Presenter = 0x04,
|
||||
/* 0x05-0x07 is reserved */
|
||||
Trackball = 0x08,
|
||||
Touchpad = 0x09
|
||||
};
|
||||
}
|
||||
|
||||
static constexpr uint8_t ErrorFeature = 0x7f;
|
||||
|
||||
static constexpr std::size_t HeaderLength = 3;
|
||||
static constexpr std::size_t ShortParamLength = 12;
|
||||
static constexpr std::size_t LongParamLength = 29;
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_DJ_DEFS_H
|
234
src/logid/backend/hidpp/Device.cpp
Normal file
234
src/logid/backend/hidpp/Device.cpp
Normal file
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include "../../util/thread.h"
|
||||
#include "Device.h"
|
||||
#include "Report.h"
|
||||
#include "../hidpp20/features/Root.h"
|
||||
#include "../hidpp20/features/DeviceName.h"
|
||||
#include "../hidpp20/Error.h"
|
||||
#include "../hidpp10/Error.h"
|
||||
#include "../dj/Receiver.h"
|
||||
|
||||
using namespace logid::backend;
|
||||
using namespace logid::backend::hidpp;
|
||||
|
||||
const char* Device::InvalidDevice::what() const noexcept
|
||||
{
|
||||
switch(_reason) {
|
||||
case NoHIDPPReport:
|
||||
return "Invalid HID++ device";
|
||||
case InvalidRawDevice:
|
||||
return "Invalid raw device";
|
||||
case Asleep:
|
||||
return "Device asleep";
|
||||
default:
|
||||
return "Invalid device";
|
||||
}
|
||||
}
|
||||
|
||||
Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept
|
||||
{
|
||||
return _reason;
|
||||
}
|
||||
|
||||
Device::Device(const std::string& path, DeviceIndex index):
|
||||
_raw_device (std::make_shared<raw::RawDevice>(path)), _receiver (nullptr),
|
||||
_path (path), _index (index)
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index) :
|
||||
_raw_device (std::move(raw_device)), _receiver (nullptr),
|
||||
_path (_raw_device->hidrawPath()), _index (index)
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
hidpp::DeviceConnectionEvent event) :
|
||||
_raw_device (receiver->rawDevice()), _index (event.index)
|
||||
{
|
||||
// Device will throw an error soon, just do it now
|
||||
if(!event.linkEstablished)
|
||||
throw InvalidDevice(InvalidDevice::Asleep);
|
||||
|
||||
if(!event.fromTimeoutCheck)
|
||||
_pid = event.pid;
|
||||
else
|
||||
_pid = receiver->getPairingInfo(_index).pid;
|
||||
_init();
|
||||
}
|
||||
|
||||
std::string Device::devicePath() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
DeviceIndex Device::deviceIndex() const
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
|
||||
std::tuple<uint8_t, uint8_t> Device::version() const
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
void Device::_init()
|
||||
{
|
||||
_listening = false;
|
||||
_supported_reports = getSupportedReports(_raw_device->reportDescriptor());
|
||||
if(!_supported_reports)
|
||||
throw InvalidDevice(InvalidDevice::NoHIDPPReport);
|
||||
|
||||
try {
|
||||
hidpp20::EssentialRoot root(this);
|
||||
_version = root.getVersion();
|
||||
} catch(hidpp10::Error &e) {
|
||||
// Valid HID++ 1.0 devices should send an InvalidSubID error
|
||||
if(e.code() != hidpp10::Error::InvalidSubID)
|
||||
throw;
|
||||
|
||||
// HID++ 2.0 is not supported, assume HID++ 1.0
|
||||
_version = std::make_tuple(1, 0);
|
||||
}
|
||||
|
||||
if(!_receiver) {
|
||||
_pid = _raw_device->productId();
|
||||
if(std::get<0>(_version) >= 2) {
|
||||
try {
|
||||
hidpp20::EssentialDeviceName deviceName(this);
|
||||
_name = deviceName.getName();
|
||||
} catch(hidpp20::UnsupportedFeature &e) {
|
||||
_name = _raw_device->name();
|
||||
}
|
||||
} else {
|
||||
_name = _raw_device->name();
|
||||
}
|
||||
} else {
|
||||
_name = _receiver->getDeviceName(_index);
|
||||
}
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
if(_listening)
|
||||
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
|
||||
}
|
||||
|
||||
void Device::addEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<EventHandler>& handler)
|
||||
{
|
||||
auto it = _event_handlers.find(nickname);
|
||||
assert(it == _event_handlers.end());
|
||||
|
||||
_event_handlers.emplace(nickname, handler);
|
||||
}
|
||||
|
||||
void Device::removeEventHandler(const std::string& nickname)
|
||||
{
|
||||
_event_handlers.erase(nickname);
|
||||
}
|
||||
|
||||
const std::map<std::string, std::shared_ptr<EventHandler>>&
|
||||
Device::eventHandlers()
|
||||
{
|
||||
return _event_handlers;
|
||||
}
|
||||
|
||||
void Device::handleEvent(Report& report)
|
||||
{
|
||||
for(auto& handler : _event_handlers)
|
||||
if(handler.second->condition(report))
|
||||
handler.second->callback(report);
|
||||
}
|
||||
|
||||
Report Device::sendReport(Report& report)
|
||||
{
|
||||
switch(report.type())
|
||||
{
|
||||
case Report::Type::Short:
|
||||
if(!(_supported_reports & HIDPP_REPORT_SHORT_SUPPORTED))
|
||||
report.setType(Report::Type::Long);
|
||||
break;
|
||||
case Report::Type::Long:
|
||||
/* Report can be truncated, but that isn't a good idea. */
|
||||
assert(_supported_reports & HIDPP_REPORT_LONG_SUPPORTED);
|
||||
}
|
||||
|
||||
auto raw_response = _raw_device->sendReport(report.rawReport());
|
||||
|
||||
Report response(raw_response);
|
||||
|
||||
Report::Hidpp10Error hidpp10_error{};
|
||||
if(response.isError10(&hidpp10_error))
|
||||
throw hidpp10::Error(hidpp10_error.error_code);
|
||||
|
||||
Report::Hidpp20Error hidpp20_error{};
|
||||
if(response.isError20(&hidpp20_error))
|
||||
throw hidpp20::Error(hidpp20_error.error_code);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::string Device::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
uint16_t Device::pid() const
|
||||
{
|
||||
return _pid;
|
||||
}
|
||||
|
||||
void Device::listen()
|
||||
{
|
||||
if(!_raw_device->isListening())
|
||||
_raw_device->listenAsync();
|
||||
|
||||
// Pass all HID++ events with device index to this device.
|
||||
auto handler = std::make_shared<raw::RawEventHandler>();
|
||||
handler->condition = [index=this->_index](std::vector<uint8_t>& report)
|
||||
->bool {
|
||||
return (report[Offset::Type] == Report::Type::Short ||
|
||||
report[Offset::Type] == Report::Type::Long) &&
|
||||
(report[Offset::DeviceIndex] == index);
|
||||
};
|
||||
handler->callback = [this](std::vector<uint8_t>& report)->void {
|
||||
Report _report(report);
|
||||
this->handleEvent(_report);
|
||||
};
|
||||
|
||||
_raw_device->addEventHandler("DEV_" + std::to_string(_index), handler);
|
||||
_listening = true;
|
||||
}
|
||||
|
||||
void Device::stopListening()
|
||||
{
|
||||
if(_listening)
|
||||
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
|
||||
|
||||
_listening = false;
|
||||
|
||||
if(!_raw_device->eventHandlers().empty())
|
||||
_raw_device->stopListener();
|
||||
}
|
109
src/logid/backend/hidpp/Device.h
Normal file
109
src/logid/backend/hidpp/Device.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP_DEVICE_H
|
||||
#define LOGID_BACKEND_HIDPP_DEVICE_H
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include "../raw/RawDevice.h"
|
||||
#include "Report.h"
|
||||
#include "defs.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace dj
|
||||
{
|
||||
// Need to define here for a constructor
|
||||
class Receiver;
|
||||
}
|
||||
namespace hidpp
|
||||
{
|
||||
struct DeviceConnectionEvent;
|
||||
struct EventHandler
|
||||
{
|
||||
std::function<bool(Report&)> condition;
|
||||
std::function<void(Report&)> callback;
|
||||
};
|
||||
class Device
|
||||
{
|
||||
public:
|
||||
class InvalidDevice : std::exception
|
||||
{
|
||||
public:
|
||||
enum Reason
|
||||
{
|
||||
NoHIDPPReport,
|
||||
InvalidRawDevice,
|
||||
Asleep
|
||||
};
|
||||
InvalidDevice(Reason reason) : _reason (reason) {}
|
||||
virtual const char* what() const noexcept;
|
||||
virtual Reason code() const noexcept;
|
||||
private:
|
||||
Reason _reason;
|
||||
};
|
||||
|
||||
explicit Device(const std::string& path, DeviceIndex index);
|
||||
explicit Device(std::shared_ptr<raw::RawDevice> raw_device,
|
||||
DeviceIndex index);
|
||||
explicit Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
hidpp::DeviceConnectionEvent event);
|
||||
~Device();
|
||||
|
||||
std::string devicePath() const;
|
||||
DeviceIndex deviceIndex() const;
|
||||
std::tuple<uint8_t, uint8_t> version() const;
|
||||
|
||||
std::string name() const;
|
||||
uint16_t pid() const;
|
||||
|
||||
void listen(); // Runs asynchronously
|
||||
void stopListening();
|
||||
|
||||
void addEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<EventHandler>& handler);
|
||||
void removeEventHandler(const std::string& nickname);
|
||||
const std::map<std::string, std::shared_ptr<EventHandler>>&
|
||||
eventHandlers();
|
||||
|
||||
Report sendReport(Report& report);
|
||||
|
||||
void handleEvent(Report& report);
|
||||
private:
|
||||
void _init();
|
||||
|
||||
std::shared_ptr<raw::RawDevice> _raw_device;
|
||||
std::shared_ptr<dj::Receiver> _receiver;
|
||||
std::string _path;
|
||||
DeviceIndex _index;
|
||||
uint8_t _supported_reports;
|
||||
|
||||
std::tuple<uint8_t, uint8_t> _version;
|
||||
uint16_t _pid;
|
||||
std::string _name;
|
||||
|
||||
std::atomic<bool> _listening;
|
||||
|
||||
std::map<std::string, std::shared_ptr<EventHandler>> _event_handlers;
|
||||
};
|
||||
} } }
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP_DEVICE_H
|
322
src/logid/backend/hidpp/Report.cpp
Normal file
322
src/logid/backend/hidpp/Report.cpp
Normal file
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include "Report.h"
|
||||
#include "../hidpp10/Error.h"
|
||||
#include "../hidpp20/Error.h"
|
||||
|
||||
using namespace logid::backend::hidpp;
|
||||
using namespace logid::backend;
|
||||
|
||||
/* Report descriptors were sourced from cvuchener/hidpp */
|
||||
static const std::array<uint8_t, 22> ShortReportDesc = {
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x10, // Report ID (16)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x06, // Report Count (6)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x01, // Usage (0001 - Vendor)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x01, // Usage (0001 - Vendor)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
static const std::array<uint8_t, 22> LongReportDesc = {
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x11, // Report ID (17)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x13, // Report Count (19)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x02, // Usage (0002 - Vendor)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x02, // Usage (0002 - Vendor)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
/* Alternative versions from the G602 */
|
||||
static const std::array<uint8_t, 22> ShortReportDesc2 = {
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x10, // Report ID (16)
|
||||
0x95, 0x06, // Report Count (6)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x01, // Usage (0001 - Vendor)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x01, // Usage (0001 - Vendor)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
static const std::array<uint8_t, 22> LongReportDesc2 = {
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x11, // Report ID (17)
|
||||
0x95, 0x13, // Report Count (19)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x02, // Usage (0002 - Vendor)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x02, // Usage (0002 - Vendor)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
uint8_t hidpp::getSupportedReports(std::vector<uint8_t>&& rdesc)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
|
||||
auto it = std::search(rdesc.begin(), rdesc.end(),
|
||||
ShortReportDesc.begin(), ShortReportDesc.end());
|
||||
if(it == rdesc.end())
|
||||
it = std::search(rdesc.begin(), rdesc.end(),
|
||||
ShortReportDesc2.begin(), ShortReportDesc2.end());
|
||||
if(it != rdesc.end())
|
||||
ret |= HIDPP_REPORT_SHORT_SUPPORTED;
|
||||
|
||||
it = std::search(rdesc.begin(), rdesc.end(),
|
||||
LongReportDesc.begin(), LongReportDesc.end());
|
||||
if(it == rdesc.end())
|
||||
it = std::search(rdesc.begin(), rdesc.end(),
|
||||
LongReportDesc2.begin(), LongReportDesc2.end());
|
||||
if(it != rdesc.end())
|
||||
ret |= HIDPP_REPORT_LONG_SUPPORTED;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *Report::InvalidReportID::what() const noexcept
|
||||
{
|
||||
return "Invalid report ID";
|
||||
}
|
||||
|
||||
const char *Report::InvalidReportLength::what() const noexcept
|
||||
{
|
||||
return "Invalid report length";
|
||||
}
|
||||
|
||||
Report::Report(Report::Type type, DeviceIndex device_index,
|
||||
uint8_t sub_id, uint8_t address)
|
||||
{
|
||||
switch(type) {
|
||||
case Type::Short:
|
||||
_data.resize(HeaderLength + ShortParamLength);
|
||||
break;
|
||||
case Type::Long:
|
||||
_data.resize(HeaderLength + LongParamLength);
|
||||
break;
|
||||
default:
|
||||
throw InvalidReportID();
|
||||
}
|
||||
|
||||
_data[Offset::Type] = type;
|
||||
_data[Offset::DeviceIndex] = device_index;
|
||||
_data[Offset::SubID] = sub_id;
|
||||
_data[Offset::Address] = address;
|
||||
}
|
||||
|
||||
Report::Report(Report::Type type, DeviceIndex device_index,
|
||||
uint8_t feature_index, uint8_t function, uint8_t sw_id)
|
||||
{
|
||||
assert(function <= 0x0f);
|
||||
assert(sw_id <= 0x0f);
|
||||
|
||||
switch(type) {
|
||||
case Type::Short:
|
||||
_data.resize(HeaderLength + ShortParamLength);
|
||||
break;
|
||||
case Type::Long:
|
||||
_data.resize(HeaderLength + LongParamLength);
|
||||
break;
|
||||
default:
|
||||
throw InvalidReportID();
|
||||
}
|
||||
|
||||
_data[Offset::Type] = type;
|
||||
_data[Offset::DeviceIndex] = device_index;
|
||||
_data[Offset::Feature] = feature_index;
|
||||
_data[Offset::Function] = (function & 0x0f) << 4 |
|
||||
(sw_id & 0x0f);
|
||||
}
|
||||
|
||||
Report::Report(const std::vector<uint8_t>& data) :
|
||||
_data (data)
|
||||
{
|
||||
_data.resize(HeaderLength + LongParamLength);
|
||||
|
||||
// Truncating data is entirely valid here.
|
||||
switch(_data[Offset::Type]) {
|
||||
case Type::Short:
|
||||
_data.resize(HeaderLength + ShortParamLength);
|
||||
break;
|
||||
case Type::Long:
|
||||
_data.resize(HeaderLength + LongParamLength);
|
||||
break;
|
||||
default:
|
||||
throw InvalidReportID();
|
||||
}
|
||||
}
|
||||
|
||||
Report::Type Report::type() const
|
||||
{
|
||||
return static_cast<Report::Type>(_data[Offset::Type]);
|
||||
}
|
||||
|
||||
void Report::setType(Report::Type type)
|
||||
{
|
||||
switch(type) {
|
||||
case Type::Short:
|
||||
_data.resize(HeaderLength + ShortParamLength);
|
||||
break;
|
||||
case Type::Long:
|
||||
_data.resize(HeaderLength + LongParamLength);
|
||||
break;
|
||||
default:
|
||||
throw InvalidReportID();
|
||||
}
|
||||
|
||||
_data[Offset::Type] = type;
|
||||
}
|
||||
|
||||
hidpp::DeviceIndex Report::deviceIndex() const
|
||||
{
|
||||
return static_cast<hidpp::DeviceIndex>(_data[Offset::DeviceIndex]);
|
||||
}
|
||||
|
||||
void Report::setDeviceIndex(hidpp::DeviceIndex index)
|
||||
{
|
||||
_data[Offset::DeviceIndex] = index;
|
||||
}
|
||||
|
||||
uint8_t Report::feature() const
|
||||
{
|
||||
return _data[Offset::Feature];
|
||||
}
|
||||
|
||||
void Report::setFeature(uint8_t feature)
|
||||
{
|
||||
_data[Offset::Parameters] = feature;
|
||||
}
|
||||
|
||||
uint8_t Report::subId() const
|
||||
{
|
||||
return _data[Offset::SubID];
|
||||
}
|
||||
|
||||
void Report::setSubId(uint8_t sub_id)
|
||||
{
|
||||
_data[Offset::SubID] = sub_id;
|
||||
}
|
||||
|
||||
uint8_t Report::function() const
|
||||
{
|
||||
return (_data[Offset::Function] >> 4) & 0x0f;
|
||||
}
|
||||
|
||||
void Report::setFunction(uint8_t function)
|
||||
{
|
||||
_data[Offset::Function] &= 0x0f;
|
||||
_data[Offset::Function] |= (function & 0x0f) << 4;
|
||||
}
|
||||
|
||||
uint8_t Report::swId() const
|
||||
{
|
||||
return _data[Offset::Function] & 0x0f;
|
||||
}
|
||||
|
||||
void Report::setSwId(uint8_t sub_id)
|
||||
{
|
||||
_data[Offset::Function] &= 0x0f;
|
||||
_data[Offset::Function] |= sub_id & 0x0f;
|
||||
}
|
||||
|
||||
uint8_t Report::address() const
|
||||
{
|
||||
return _data[Offset::Address];
|
||||
}
|
||||
|
||||
void Report::setAddress(uint8_t address)
|
||||
{
|
||||
_data[Offset::Address] = address;
|
||||
}
|
||||
|
||||
std::vector<uint8_t>::iterator Report::paramBegin()
|
||||
{
|
||||
return _data.begin() + Offset::Parameters;
|
||||
}
|
||||
|
||||
std::vector<uint8_t>::iterator Report::paramEnd()
|
||||
{
|
||||
return _data.end();
|
||||
}
|
||||
|
||||
std::vector<uint8_t>::const_iterator Report::paramBegin() const
|
||||
{
|
||||
return _data.begin() + Offset::Parameters;
|
||||
}
|
||||
|
||||
std::vector<uint8_t>::const_iterator Report::paramEnd() const
|
||||
{
|
||||
return _data.end();
|
||||
}
|
||||
|
||||
void Report::setParams(const std::vector<uint8_t>& _params)
|
||||
{
|
||||
assert(_params.size() <= _data.size()-HeaderLength);
|
||||
|
||||
for(std::size_t i = 0; i < _params.size(); i++)
|
||||
_data[Offset::Parameters + i] = _params[i];
|
||||
}
|
||||
|
||||
bool Report::isError10(Report::Hidpp10Error *error)
|
||||
{
|
||||
assert(error != nullptr);
|
||||
|
||||
if(_data[Offset::Type] != Type::Short ||
|
||||
_data[Offset::SubID] != hidpp10::ErrorID)
|
||||
return false;
|
||||
|
||||
error->sub_id = _data[3];
|
||||
error->address = _data[4];
|
||||
error->error_code = _data[5];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Report::isError20(Report::Hidpp20Error* error)
|
||||
{
|
||||
assert(error != nullptr);
|
||||
|
||||
if(_data[Offset::Type] != Type::Long ||
|
||||
_data[Offset::Feature] != hidpp20::ErrorID)
|
||||
return false;
|
||||
|
||||
error->feature_index= _data[3];
|
||||
error->function = (_data[4] >> 4) & 0x0f;
|
||||
error->software_id = _data[4] & 0x0f;
|
||||
error->error_code = _data[5];
|
||||
|
||||
return true;
|
||||
}
|
125
src/logid/backend/hidpp/Report.h
Normal file
125
src/logid/backend/hidpp/Report.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP_REPORT_H
|
||||
#define LOGID_BACKEND_HIDPP_REPORT_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "../raw/RawDevice.h"
|
||||
#include "defs.h"
|
||||
|
||||
/* Some devices only support a subset of these reports */
|
||||
#define HIDPP_REPORT_SHORT_SUPPORTED 1U
|
||||
#define HIDPP_REPORT_LONG_SUPPORTED 1U<<1U
|
||||
/* Very long reports exist, however they have not been encountered so far */
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp
|
||||
{
|
||||
uint8_t getSupportedReports(std::vector<uint8_t>&& rdesc);
|
||||
|
||||
namespace Offset
|
||||
{
|
||||
static constexpr uint8_t Type = 0;
|
||||
static constexpr uint8_t DeviceIndex = 1;
|
||||
static constexpr uint8_t SubID = 2;
|
||||
static constexpr uint8_t Feature = 2;
|
||||
static constexpr uint8_t Address = 3;
|
||||
static constexpr uint8_t Function = 3;
|
||||
static constexpr uint8_t Parameters = 4;
|
||||
}
|
||||
|
||||
class Report
|
||||
{
|
||||
public:
|
||||
typedef ReportType::ReportType Type;
|
||||
|
||||
class InvalidReportID: public std::exception
|
||||
{
|
||||
public:
|
||||
InvalidReportID() = default;
|
||||
const char* what() const noexcept override;
|
||||
};
|
||||
|
||||
class InvalidReportLength: public std::exception
|
||||
{
|
||||
public:
|
||||
InvalidReportLength() = default;;
|
||||
const char* what() const noexcept override;
|
||||
};
|
||||
|
||||
static constexpr std::size_t MaxDataLength = 20;
|
||||
|
||||
Report(Report::Type type, DeviceIndex device_index,
|
||||
uint8_t sub_id,
|
||||
uint8_t address);
|
||||
Report(Report::Type type, DeviceIndex device_index,
|
||||
uint8_t feature_index,
|
||||
uint8_t function,
|
||||
uint8_t sw_id);
|
||||
explicit Report(const std::vector<uint8_t>& data);
|
||||
|
||||
Report::Type type() const;
|
||||
void setType(Report::Type type);
|
||||
|
||||
logid::backend::hidpp::DeviceIndex deviceIndex() const;
|
||||
void setDeviceIndex(hidpp::DeviceIndex index);
|
||||
|
||||
uint8_t feature() const;
|
||||
void setFeature(uint8_t feature);
|
||||
|
||||
uint8_t subId() const;
|
||||
void setSubId(uint8_t sub_id);
|
||||
|
||||
uint8_t function() const;
|
||||
void setFunction(uint8_t function);
|
||||
|
||||
uint8_t swId() const;
|
||||
void setSwId(uint8_t sw_id);
|
||||
|
||||
uint8_t address() const;
|
||||
void setAddress(uint8_t address);
|
||||
|
||||
std::vector<uint8_t>::iterator paramBegin();
|
||||
std::vector<uint8_t>::iterator paramEnd();
|
||||
std::vector<uint8_t>::const_iterator paramBegin() const;
|
||||
std::vector<uint8_t>::const_iterator paramEnd() const;
|
||||
void setParams(const std::vector<uint8_t>& _params);
|
||||
|
||||
struct Hidpp10Error
|
||||
{
|
||||
uint8_t sub_id, address, error_code;
|
||||
};
|
||||
bool isError10(Hidpp10Error* error);
|
||||
|
||||
struct Hidpp20Error
|
||||
{
|
||||
uint8_t feature_index, function, software_id, error_code;
|
||||
};
|
||||
bool isError20(Hidpp20Error* error);
|
||||
|
||||
std::vector<uint8_t> rawReport () const { return _data; }
|
||||
|
||||
static constexpr std::size_t HeaderLength = 4;
|
||||
private:
|
||||
std::vector<uint8_t> _data;
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP_REPORT_H
|
55
src/logid/backend/hidpp/defs.h
Normal file
55
src/logid/backend/hidpp/defs.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP_DEFS_H
|
||||
#define LOGID_BACKEND_HIDPP_DEFS_H
|
||||
|
||||
#define LOGID_HIDPP_SOFTWARE_ID 0
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp
|
||||
{
|
||||
namespace ReportType
|
||||
{
|
||||
enum ReportType : uint8_t
|
||||
{
|
||||
Short = 0x10,
|
||||
Long = 0x11
|
||||
};
|
||||
}
|
||||
|
||||
enum DeviceIndex: uint8_t
|
||||
{
|
||||
DefaultDevice = 0xff,
|
||||
CordedDevice = 0,
|
||||
WirelessDevice1 = 1,
|
||||
WirelessDevice2 = 2,
|
||||
WirelessDevice3 = 3,
|
||||
WirelessDevice4 = 4,
|
||||
WirelessDevice5 = 5,
|
||||
WirelessDevice6 = 6,
|
||||
};
|
||||
|
||||
static constexpr std::size_t ShortParamLength = 3;
|
||||
static constexpr std::size_t LongParamLength = 16;
|
||||
} } }
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP_DEFS_H
|
76
src/logid/backend/hidpp10/Device.cpp
Normal file
76
src/logid/backend/hidpp10/Device.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include "Device.h"
|
||||
#include "defs.h"
|
||||
|
||||
using namespace logid::backend;
|
||||
using namespace logid::backend::hidpp10;
|
||||
|
||||
Device::Device(const std::string &path, hidpp::DeviceIndex index) :
|
||||
hidpp::Device(path, index)
|
||||
{
|
||||
assert(version() == std::make_tuple(1, 0));
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<raw::RawDevice> raw_dev,
|
||||
hidpp::DeviceIndex index) : hidpp::Device(std::move(raw_dev), index)
|
||||
{
|
||||
assert(version() == std::make_tuple(1, 0));
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Device::getRegister(uint8_t address,
|
||||
const std::vector<uint8_t>& params, hidpp::Report::Type type)
|
||||
{
|
||||
assert(params.size() <= hidpp::LongParamLength);
|
||||
|
||||
uint8_t sub_id = type == hidpp::Report::Type::Short ?
|
||||
GetRegisterShort : GetRegisterLong;
|
||||
|
||||
return accessRegister(sub_id, address, params);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Device::setRegister(uint8_t address,
|
||||
const std::vector<uint8_t>& params,
|
||||
hidpp::Report::Type type)
|
||||
{
|
||||
assert(params.size() <= hidpp::LongParamLength);
|
||||
|
||||
uint8_t sub_id = type == hidpp::Report::Type::Short ?
|
||||
SetRegisterShort : SetRegisterLong;
|
||||
|
||||
return accessRegister(sub_id, address, params);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Device::accessRegister(uint8_t sub_id, uint8_t address,
|
||||
const std::vector<uint8_t> ¶ms)
|
||||
{
|
||||
hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ?
|
||||
hidpp::Report::Type::Short : hidpp::Report::Type::Long;
|
||||
|
||||
hidpp::Report request(type, deviceIndex(), sub_id, address);
|
||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
||||
|
||||
auto response = sendReport(request);
|
||||
return std::vector<uint8_t>(response.paramBegin(), response.paramEnd());
|
||||
}
|
||||
|
||||
|
||||
|
46
src/logid/backend/hidpp10/Device.h
Normal file
46
src/logid/backend/hidpp10/Device.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP10_DEVICE_H
|
||||
#define LOGID_BACKEND_HIDPP10_DEVICE_H
|
||||
|
||||
#include "../hidpp/Device.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp10
|
||||
{
|
||||
class Device : public hidpp::Device
|
||||
{
|
||||
public:
|
||||
Device(const std::string& path, hidpp::DeviceIndex index);
|
||||
Device(std::shared_ptr<raw::RawDevice> raw_dev,
|
||||
hidpp::DeviceIndex index);
|
||||
|
||||
std::vector<uint8_t> getRegister(uint8_t address,
|
||||
const std::vector<uint8_t>& params, hidpp::Report::Type type);
|
||||
|
||||
std::vector<uint8_t> setRegister(uint8_t address,
|
||||
const std::vector<uint8_t>& params, hidpp::Report::Type type);
|
||||
private:
|
||||
std::vector<uint8_t> accessRegister(uint8_t sub_id,
|
||||
uint8_t address, const std::vector<uint8_t>& params);
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP10_DEVICE_H
|
67
src/logid/backend/hidpp10/Error.cpp
Normal file
67
src/logid/backend/hidpp10/Error.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include "Error.h"
|
||||
|
||||
using namespace logid::backend::hidpp10;
|
||||
|
||||
Error::Error(uint8_t code): _code(code)
|
||||
{
|
||||
assert(code != Success);
|
||||
}
|
||||
|
||||
const char* Error::what() const noexcept
|
||||
{
|
||||
switch(_code) {
|
||||
case Success:
|
||||
return "Success";
|
||||
case InvalidSubID:
|
||||
return "Invalid sub ID";
|
||||
case InvalidAddress:
|
||||
return "Invalid address";
|
||||
case InvalidValue:
|
||||
return "Invalid value";
|
||||
case ConnectFail:
|
||||
return "Connection failure";
|
||||
case TooManyDevices:
|
||||
return "Too many devices";
|
||||
case AlreadyExists:
|
||||
return "Already exists";
|
||||
case Busy:
|
||||
return "Busy";
|
||||
case UnknownDevice:
|
||||
return "Unknown device";
|
||||
case ResourceError:
|
||||
return "Resource error";
|
||||
case RequestUnavailable:
|
||||
return "Request unavailable";
|
||||
case InvalidParameterValue:
|
||||
return "Invalid parameter value";
|
||||
case WrongPINCode:
|
||||
return "Wrong PIN code";
|
||||
default:
|
||||
return "Unknown error code";
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Error::code() const noexcept
|
||||
{
|
||||
return _code;
|
||||
}
|
59
src/logid/backend/hidpp10/Error.h
Normal file
59
src/logid/backend/hidpp10/Error.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP10_ERROR_H
|
||||
#define LOGID_BACKEND_HIDPP10_ERROR_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp10 {
|
||||
static constexpr uint8_t ErrorID = 0x8f;
|
||||
|
||||
class Error: public std::exception
|
||||
{
|
||||
public:
|
||||
enum ErrorCode: uint8_t
|
||||
{
|
||||
Success = 0x00,
|
||||
InvalidSubID = 0x01,
|
||||
InvalidAddress = 0x02,
|
||||
InvalidValue = 0x03,
|
||||
ConnectFail = 0x04,
|
||||
TooManyDevices = 0x05,
|
||||
AlreadyExists = 0x06,
|
||||
Busy = 0x07,
|
||||
UnknownDevice = 0x08,
|
||||
ResourceError = 0x09,
|
||||
RequestUnavailable = 0x0A,
|
||||
InvalidParameterValue = 0x0B,
|
||||
WrongPINCode = 0x0C
|
||||
};
|
||||
|
||||
explicit Error(uint8_t code);
|
||||
|
||||
const char* what() const noexcept override;
|
||||
uint8_t code() const noexcept;
|
||||
|
||||
private:
|
||||
uint8_t _code;
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP10_ERROR_H
|
35
src/logid/backend/hidpp10/defs.h
Normal file
35
src/logid/backend/hidpp10/defs.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP10_DEFS_H
|
||||
#define LOGID_BACKEND_HIDPP10_DEFS_H
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp10
|
||||
{
|
||||
enum SubID: uint8_t
|
||||
{
|
||||
SetRegisterShort = 0x80,
|
||||
GetRegisterShort = 0x81,
|
||||
SetRegisterLong = 0x82,
|
||||
GetRegisterLong = 0x83
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP10_DEFS_H
|
57
src/logid/backend/hidpp20/Device.cpp
Normal file
57
src/logid/backend/hidpp20/Device.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "Device.h"
|
||||
#include "../hidpp/defs.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
Device::Device(std::string path, hidpp::DeviceIndex index)
|
||||
: hidpp::Device(path, index)
|
||||
{
|
||||
assert(std::get<0>(version()) >= 2);
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<raw::RawDevice> raw_device, hidpp::DeviceIndex index)
|
||||
: hidpp::Device(raw_device, index)
|
||||
{
|
||||
assert(std::get<0>(version()) >= 2);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Device::callFunction(uint8_t feature_index,
|
||||
uint8_t function, std::vector<uint8_t>& params)
|
||||
{
|
||||
hidpp::Report::Type type;
|
||||
|
||||
assert(params.size() <= hidpp::LongParamLength);
|
||||
if(params.size() <= hidpp::ShortParamLength)
|
||||
type = hidpp::Report::Type::Short;
|
||||
else if(params.size() <= hidpp::LongParamLength)
|
||||
type = hidpp::Report::Type::Long;
|
||||
else
|
||||
throw hidpp::Report::InvalidReportID();
|
||||
|
||||
hidpp::Report request(type, deviceIndex(), feature_index, function,
|
||||
LOGID_HIDPP_SOFTWARE_ID);
|
||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
||||
|
||||
auto response = this->sendReport(request);
|
||||
return std::vector<uint8_t>(response.paramBegin(), response.paramEnd());
|
||||
}
|
40
src/logid/backend/hidpp20/Device.h
Normal file
40
src/logid/backend/hidpp20/Device.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP20_DEVICE_H
|
||||
#define LOGID_BACKEND_HIDPP20_DEVICE_H
|
||||
|
||||
#include "../hidpp/Device.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20 {
|
||||
class Device : public hidpp::Device
|
||||
{
|
||||
public:
|
||||
Device(std::string path, hidpp::DeviceIndex index);
|
||||
Device(std::shared_ptr<raw::RawDevice> raw_device, hidpp::DeviceIndex index);
|
||||
|
||||
std::vector<uint8_t> callFunction(uint8_t feature_index,
|
||||
uint8_t function,
|
||||
std::vector<uint8_t>& params);
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_DEVICE_H
|
62
src/logid/backend/hidpp20/Error.cpp
Normal file
62
src/logid/backend/hidpp20/Error.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include "Error.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
Error::Error(uint8_t code) : _code (code)
|
||||
{
|
||||
assert(_code != NoError);
|
||||
}
|
||||
|
||||
const char* Error::what() const noexcept
|
||||
{
|
||||
switch(_code) {
|
||||
case NoError:
|
||||
return "No error";
|
||||
case Unknown:
|
||||
return "Unknown";
|
||||
case InvalidArgument:
|
||||
return "Invalid argument";
|
||||
case OutOfRange:
|
||||
return "Out of range";
|
||||
case HardwareError:
|
||||
return "Hardware error";
|
||||
case LogitechInternal:
|
||||
return "Logitech internal feature";
|
||||
case InvalidFeatureIndex:
|
||||
return "Invalid feature index";
|
||||
case InvalidFunctionID:
|
||||
return "Invalid function ID";
|
||||
case Busy:
|
||||
return "Busy";
|
||||
case Unsupported:
|
||||
return "Unsupported";
|
||||
case UnknownDevice:
|
||||
return "Unknown device";
|
||||
default:
|
||||
return "Unknown error code";
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Error::code() const noexcept
|
||||
{
|
||||
return _code;
|
||||
}
|
57
src/logid/backend/hidpp20/Error.h
Normal file
57
src/logid/backend/hidpp20/Error.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP20_ERROR_H
|
||||
#define LOGID_BACKEND_HIDPP20_ERROR_H
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20 {
|
||||
static constexpr uint8_t ErrorID = 0xFF;
|
||||
|
||||
class Error: public std::exception
|
||||
{
|
||||
public:
|
||||
enum ErrorCode: uint8_t {
|
||||
NoError = 0,
|
||||
Unknown = 1,
|
||||
InvalidArgument = 2,
|
||||
OutOfRange = 3,
|
||||
HardwareError = 4,
|
||||
LogitechInternal = 5,
|
||||
InvalidFeatureIndex = 6,
|
||||
InvalidFunctionID = 7,
|
||||
Busy = 8,
|
||||
Unsupported = 9,
|
||||
UnknownDevice = 10
|
||||
};
|
||||
|
||||
explicit Error(uint8_t code);
|
||||
|
||||
const char* what() const noexcept override;
|
||||
uint8_t code() const noexcept;
|
||||
|
||||
private:
|
||||
uint8_t _code;
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_ERROR_H
|
72
src/logid/backend/hidpp20/EssentialFeature.cpp
Normal file
72
src/logid/backend/hidpp20/EssentialFeature.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include "EssentialFeature.h"
|
||||
#include "feature_defs.h"
|
||||
#include "features/Root.h"
|
||||
#include "Error.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
std::vector<uint8_t> EssentialFeature::callFunction(uint8_t function_id,
|
||||
std::vector<uint8_t>& params)
|
||||
{
|
||||
hidpp::Report::Type type;
|
||||
|
||||
assert(params.size() <= hidpp::LongParamLength);
|
||||
if(params.size() <= hidpp::ShortParamLength)
|
||||
type = hidpp::Report::Type::Short;
|
||||
else if(params.size() <= hidpp::LongParamLength)
|
||||
type = hidpp::Report::Type::Long;
|
||||
else
|
||||
throw hidpp::Report::InvalidReportID();
|
||||
|
||||
hidpp::Report request(type, _device->deviceIndex(), _index, function_id,
|
||||
LOGID_HIDPP_SOFTWARE_ID);
|
||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
||||
|
||||
auto response = _device->sendReport(request);
|
||||
return std::vector<uint8_t>(response.paramBegin(), response.paramEnd());
|
||||
}
|
||||
|
||||
EssentialFeature::EssentialFeature(hidpp::Device* dev, uint16_t _id) :
|
||||
_device (dev)
|
||||
{
|
||||
_index = hidpp20::FeatureID::ROOT;
|
||||
|
||||
if(_id)
|
||||
{
|
||||
std::vector<uint8_t> getFunc_req(2);
|
||||
getFunc_req[0] = (_id >> 8) & 0xff;
|
||||
getFunc_req[1] = _id & 0xff;
|
||||
try {
|
||||
auto getFunc_resp = this->callFunction(Root::GetFeature,
|
||||
getFunc_req);
|
||||
_index = getFunc_resp[0];
|
||||
} catch(Error& e) {
|
||||
if(e.code() == Error::InvalidFeatureIndex)
|
||||
throw UnsupportedFeature(_id);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// 0 if not found
|
||||
if(!_index)
|
||||
throw UnsupportedFeature(_id);
|
||||
}
|
||||
}
|
50
src/logid/backend/hidpp20/EssentialFeature.h
Normal file
50
src/logid/backend/hidpp20/EssentialFeature.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP20_ESSENTIAL_FEATURE_H
|
||||
#define LOGID_BACKEND_HIDPP20_ESSENTIAL_FEATURE_H
|
||||
|
||||
// WARNING: UNSAFE
|
||||
|
||||
/* This class is only meant to provide essential HID++ 2.0 features to the
|
||||
* hidpp::Device class. No version checks are provided here
|
||||
*/
|
||||
|
||||
#include "Device.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20
|
||||
{
|
||||
class EssentialFeature
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID;
|
||||
virtual uint16_t getID() = 0;
|
||||
|
||||
protected:
|
||||
EssentialFeature(hidpp::Device* dev, uint16_t _id);
|
||||
std::vector<uint8_t> callFunction(uint8_t function_id,
|
||||
std::vector<uint8_t>& params);
|
||||
private:
|
||||
hidpp::Device* _device;
|
||||
uint8_t _index;
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_ESSENTIAL_FEATURE_H
|
70
src/logid/backend/hidpp20/Feature.cpp
Normal file
70
src/logid/backend/hidpp20/Feature.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Error.h"
|
||||
#include "Feature.h"
|
||||
#include "feature_defs.h"
|
||||
#include "features/Root.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
const char* UnsupportedFeature::what() const noexcept
|
||||
{
|
||||
return "Unsupported feature";
|
||||
}
|
||||
|
||||
uint16_t UnsupportedFeature::code() const noexcept
|
||||
{
|
||||
return _f_id;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Feature::callFunction(uint8_t function_id, std::vector<uint8_t>& params)
|
||||
{
|
||||
return _device->callFunction(_index, function_id, params);
|
||||
}
|
||||
|
||||
Feature::Feature(Device* dev, uint16_t _id) : _device (dev)
|
||||
{
|
||||
_index = hidpp20::FeatureID::ROOT;
|
||||
|
||||
if(_id)
|
||||
{
|
||||
std::vector<uint8_t> getFunc_req(2);
|
||||
getFunc_req[0] = (_id >> 8) & 0xff;
|
||||
getFunc_req[1] = _id & 0xff;
|
||||
|
||||
try {
|
||||
auto getFunc_resp = this->callFunction(Root::GetFeature,
|
||||
getFunc_req);
|
||||
_index = getFunc_resp[0];
|
||||
} catch(Error& e) {
|
||||
if(e.code() == Error::InvalidFeatureIndex)
|
||||
throw UnsupportedFeature(_id);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// 0 if not found
|
||||
if(!_index)
|
||||
throw UnsupportedFeature(_id);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Feature::featureIndex()
|
||||
{
|
||||
return _index;
|
||||
}
|
54
src/logid/backend/hidpp20/Feature.h
Normal file
54
src/logid/backend/hidpp20/Feature.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_H
|
||||
#define LOGID_BACKEND_HIDPP20_FEATURE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "Device.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20 {
|
||||
class UnsupportedFeature : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit UnsupportedFeature(uint16_t ID) : _f_id (ID) {}
|
||||
const char* what() const noexcept override;
|
||||
uint16_t code() const noexcept;
|
||||
private:
|
||||
uint16_t _f_id;
|
||||
};
|
||||
|
||||
class Feature
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID;
|
||||
virtual uint16_t getID() = 0;
|
||||
uint8_t featureIndex();
|
||||
protected:
|
||||
explicit Feature(Device* dev, uint16_t _id);
|
||||
std::vector<uint8_t> callFunction(uint8_t function_id,
|
||||
std::vector<uint8_t>& params);
|
||||
private:
|
||||
Device* _device;
|
||||
uint8_t _index;
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_FEATURE_H
|
131
src/logid/backend/hidpp20/feature_defs.h
Normal file
131
src/logid/backend/hidpp20/feature_defs.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP20_FEATUREDEFS
|
||||
#define LOGID_BACKEND_HIDPP20_FEATUREDEFS
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20 {
|
||||
struct feature_info {
|
||||
uint16_t feature_id;
|
||||
bool obsolete;
|
||||
bool internal;
|
||||
bool hidden;
|
||||
};
|
||||
|
||||
namespace FeatureID
|
||||
{
|
||||
enum FeatureID : uint16_t
|
||||
{
|
||||
ROOT = 0x0000,
|
||||
FEATURE_SET = 0x0001,
|
||||
FEATURE_INFO = 0x0002,
|
||||
FW_VERSION = 0x0003,
|
||||
DEVICE_NAME = 0x0005,
|
||||
DEVICE_GROUPS = 0x0006,
|
||||
DEVICE_FRIENDLY_NAME = 0x0007,
|
||||
RESET = 0x0020,
|
||||
CRYPTO_IDENTIFIER = 0x0021,
|
||||
DFUCONTROL = 0x00c0,
|
||||
DFUCONTROL_V2 = 0x00c1,
|
||||
DFUCONTROL_V3 = 0x00c2,
|
||||
DFU = 0xd000,
|
||||
BATTERY_STATUS = 0x1000,
|
||||
BATTERY_VOLTAGE = 0x1001,
|
||||
CHARGING_CONTROL = 0x1010,
|
||||
LED_CONTROL = 0x1300,
|
||||
GENERIC_TEST = 0x1800,
|
||||
DEVICE_RESET = 0x1802,
|
||||
OOB_STATE = 0x1805,
|
||||
CONFIGURABLE_DEVICE_PROPERTIES = 0x1806,
|
||||
CHANGE_HOST = 0x1814,
|
||||
HOSTS_INFO = 0x1815,
|
||||
BACKLIGHT = 0x1981,
|
||||
BACKLIGHT_V2 = 0x1982,
|
||||
BACKLIGHT_V3 = 0x1983,
|
||||
PRESENTER_CONTROL = 0x1a00,
|
||||
SENSOR_3D = 0x1a01,
|
||||
REPROG_CONTROLS = 0x1b00,
|
||||
REPROG_CONTROLS_V2 = 0x1b01,
|
||||
REPROG_CONTROLS_V2_2 = 0x1b02,
|
||||
REPROG_CONTROLS_V3 = 0x1b03,
|
||||
REPROG_CONTROLS_V4 = 0x1b04,
|
||||
PERSISTENT_REMAPPABLE_ACTION = 0x1bc0,
|
||||
WIRELESS_DEVICE_STATUS = 0x1d4b,
|
||||
ENABLE_HIDDEN_FEATURE = 0x1e00,
|
||||
FIRMWARE_PROPERTIES = 0x1f1f,
|
||||
ADC_MEASUREMENT = 0x1f20,
|
||||
LEFT_RIGHT_SWAP = 0x2001,
|
||||
SWAP_BUTTON = 0x2005,
|
||||
POINTER_AXES_ORIENTATION = 0x2006,
|
||||
VERTICAL_SCROLLING = 0x2100,
|
||||
SMART_SHIFT = 0x2110,
|
||||
HIRES_SCROLLING = 0x2120,
|
||||
HIRES_SCROLLING_V2 = 0x2121, // Referred to as Hi-res wheel in cvuchener/hidpp, seems to be V2?
|
||||
LORES_SCROLLING = 0x2130,
|
||||
MOUSE_POINTER = 0x2200, // Possibly predecessor to 0x2201?
|
||||
ADJUSTABLE_DPI = 0x2201,
|
||||
ANGLE_SNAPPING = 0x2230,
|
||||
SURFACE_TUNING = 0x2240,
|
||||
HYBRID_TRACKING = 0x2400,
|
||||
FN_INVERSION = 0x40a0,
|
||||
FN_INVERSION_V2 = 0x40a2, // Is 0x40a1 skipped?
|
||||
FN_INVERSION_V3 = 0x40a3,
|
||||
ENCRYPTION = 0x4100,
|
||||
LOCK_KEY_STATE = 0x4220,
|
||||
SOLAR_DASHBOARD = 0x4301,
|
||||
KEYBOARD_LAYOUT = 0x4520,
|
||||
KEYBOARD_DISABLE = 0x4521,
|
||||
DISABLE_KEYS = 0x4522,
|
||||
MULTIPLATFORM = 0x4530, // Dual platform only?
|
||||
MULTIPLATFORM_V2 = 0x4531,
|
||||
KEYBOARD_LAYOUT_V2 = 0x4540,
|
||||
CROWN = 0x4600,
|
||||
TOUCHPAD_FW = 0x6010,
|
||||
TOUCHPAD_SW = 0x6011,
|
||||
TOUCHPAD_FW_WIN8 = 0x6012,
|
||||
TOUCHMOUSE_RAW = 0x6100,
|
||||
// TOUCHMOUSE_6120 = 0x6120, (Keeping this commented out until a better name is found)
|
||||
GESTURE = 0x6500,
|
||||
GESTURE_V2 = 0x6501,
|
||||
G_KEY = 0x8010,
|
||||
M_KEY = 0x8020,
|
||||
// MR = 0x8030, (Keeping this commented out until a better name is found)
|
||||
BRIGHTNESS_CONTROL = 0x8040,
|
||||
REPORT_RATE = 0x8060,
|
||||
RGB_EFFECTS = 0x8070,
|
||||
RGB_EFFECTS_V2 = 0x8071,
|
||||
PER_KEY_LIGHTING = 0x8080,
|
||||
PER_KEY_LIGHTING_V2 = 0x8081,
|
||||
MODE_STATUS = 0x8100,
|
||||
MOUSE_BUTTON_SPY = 0x8110,
|
||||
LATENCY_MONITORING = 0x8111,
|
||||
GAMING_ATTACHMENTS = 0x8120,
|
||||
FORCE_FEEDBACK = 0x8123,
|
||||
SIDETONE = 0x8300,
|
||||
EQUALIZER = 0x8310,
|
||||
HEADSET_OUT = 0x8320
|
||||
};
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_FEATUREDEFS
|
88
src/logid/backend/hidpp20/features/AdjustableDPI.cpp
Normal file
88
src/logid/backend/hidpp20/features/AdjustableDPI.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "AdjustableDPI.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
AdjustableDPI::AdjustableDPI(Device* dev) : Feature(dev, ID)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t AdjustableDPI::getSensorCount()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
auto response = callFunction(GetSensorCount, params);
|
||||
return response[0];
|
||||
}
|
||||
|
||||
AdjustableDPI::SensorDPIList AdjustableDPI::getSensorDPIList(uint8_t sensor)
|
||||
{
|
||||
SensorDPIList dpi_list{};
|
||||
std::vector<uint8_t> params(1);
|
||||
params[0] = sensor;
|
||||
auto response = callFunction(GetSensorDPIList, params);
|
||||
|
||||
dpi_list.dpiStep = false;
|
||||
for(std::size_t i = 1; i < response.size(); i+=2) {
|
||||
uint16_t dpi = response[i + 1];
|
||||
dpi |= (response[i] << 8);
|
||||
if(!dpi)
|
||||
break;
|
||||
if(dpi >= 0xe000) {
|
||||
dpi_list.isRange = true;
|
||||
dpi_list.dpiStep = dpi - 0xe000;
|
||||
} else {
|
||||
dpi_list.dpis.push_back(dpi);
|
||||
}
|
||||
}
|
||||
|
||||
return dpi_list;
|
||||
}
|
||||
|
||||
uint16_t AdjustableDPI::getDefaultSensorDPI(uint8_t sensor)
|
||||
{
|
||||
std::vector<uint8_t> params(1);
|
||||
params[0] = sensor;
|
||||
auto response = callFunction(GetSensorDPI, params);
|
||||
|
||||
uint16_t default_dpi = response[4];
|
||||
default_dpi |= (response[3] << 8);
|
||||
|
||||
return default_dpi;
|
||||
}
|
||||
|
||||
uint16_t AdjustableDPI::getSensorDPI(uint8_t sensor)
|
||||
{
|
||||
std::vector<uint8_t> params(1);
|
||||
params[0] = sensor;
|
||||
auto response = callFunction(GetSensorDPI, params);
|
||||
|
||||
uint16_t dpi = response[2];
|
||||
dpi |= (response[1] << 8);
|
||||
|
||||
return dpi;
|
||||
}
|
||||
|
||||
void AdjustableDPI::setSensorDPI(uint8_t sensor, uint16_t dpi)
|
||||
{
|
||||
std::vector<uint8_t> params(3);
|
||||
params[0] = sensor;
|
||||
params[1] = (dpi >> 8);
|
||||
params[2] = (dpi & 0xFF);
|
||||
callFunction(SetSensorDPI, params);
|
||||
}
|
60
src/logid/backend/hidpp20/features/AdjustableDPI.h
Normal file
60
src/logid/backend/hidpp20/features/AdjustableDPI.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_ADJUSTABLEDPI_H
|
||||
#define LOGID_BACKEND_HIDPP20_FEATURE_ADJUSTABLEDPI_H
|
||||
|
||||
#include "../feature_defs.h"
|
||||
#include "../Feature.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20
|
||||
{
|
||||
class AdjustableDPI : public Feature
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::ADJUSTABLE_DPI;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
enum Function {
|
||||
GetSensorCount = 0,
|
||||
GetSensorDPIList = 1,
|
||||
GetSensorDPI = 2,
|
||||
SetSensorDPI = 3
|
||||
};
|
||||
|
||||
AdjustableDPI(Device* dev);
|
||||
|
||||
uint8_t getSensorCount();
|
||||
|
||||
struct SensorDPIList
|
||||
{
|
||||
std::vector<uint16_t> dpis;
|
||||
bool isRange;
|
||||
uint16_t dpiStep;
|
||||
};
|
||||
SensorDPIList getSensorDPIList(uint8_t sensor);
|
||||
|
||||
uint16_t getDefaultSensorDPI(uint8_t sensor);
|
||||
uint16_t getSensorDPI(uint8_t sensor);
|
||||
|
||||
void setSensorDPI(uint8_t sensor, uint16_t dpi);
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_FEATURE_ADJUSTABLEDPI_H
|
86
src/logid/backend/hidpp20/features/DeviceName.cpp
Normal file
86
src/logid/backend/hidpp20/features/DeviceName.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include "DeviceName.h"
|
||||
|
||||
using namespace logid::backend;
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
DeviceName::DeviceName(Device* dev) : Feature(dev, ID)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t DeviceName::getNameLength()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
|
||||
auto response = this->callFunction(Function::GetLength, params);
|
||||
return response[0];
|
||||
}
|
||||
|
||||
std::string _getName(uint8_t length,
|
||||
const std::function<std::vector<uint8_t>(std::vector<uint8_t>)>& fcall)
|
||||
{
|
||||
uint8_t function_calls = length/hidpp::LongParamLength;
|
||||
if(length % hidpp::LongParamLength)
|
||||
function_calls++;
|
||||
std::vector<uint8_t> params(1);
|
||||
std::string name;
|
||||
|
||||
for(uint8_t i = 0; i < function_calls; i++) {
|
||||
params[0] = i*hidpp::LongParamLength;
|
||||
auto name_section = fcall(params);
|
||||
for(std::size_t j = 0; j < hidpp::LongParamLength; j++) {
|
||||
if(params[0] + j >= length)
|
||||
return name;
|
||||
name += name_section[j];
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string DeviceName::getName()
|
||||
{
|
||||
return _getName(getNameLength(), [this]
|
||||
(std::vector<uint8_t> params)->std::vector<uint8_t> {
|
||||
return this->callFunction(Function::GetDeviceName, params);
|
||||
});
|
||||
}
|
||||
|
||||
EssentialDeviceName::EssentialDeviceName(hidpp::Device* dev) :
|
||||
EssentialFeature(dev, ID)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t EssentialDeviceName::getNameLength()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
|
||||
auto response = this->callFunction(DeviceName::Function::GetLength, params);
|
||||
return response[0];
|
||||
}
|
||||
|
||||
std::string EssentialDeviceName::getName()
|
||||
{
|
||||
return _getName(getNameLength(), [this]
|
||||
(std::vector<uint8_t> params)->std::vector<uint8_t> {
|
||||
return this->callFunction(DeviceName::Function::GetDeviceName, params);
|
||||
});
|
||||
}
|
61
src/logid/backend/hidpp20/features/DeviceName.h
Normal file
61
src/logid/backend/hidpp20/features/DeviceName.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H
|
||||
#define LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H
|
||||
|
||||
#include "../Feature.h"
|
||||
#include "../feature_defs.h"
|
||||
#include "../EssentialFeature.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20
|
||||
{
|
||||
class DeviceName : public Feature
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::DEVICE_NAME;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
enum Function : uint8_t
|
||||
{
|
||||
GetLength = 0,
|
||||
GetDeviceName = 1
|
||||
};
|
||||
|
||||
explicit DeviceName(Device* device);
|
||||
|
||||
uint8_t getNameLength();
|
||||
std::string getName();
|
||||
};
|
||||
|
||||
class EssentialDeviceName : public EssentialFeature
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::DEVICE_NAME;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
explicit EssentialDeviceName(hidpp::Device* device);
|
||||
|
||||
uint8_t getNameLength();
|
||||
std::string getName();
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H
|
51
src/logid/backend/hidpp20/features/FeatureSet.cpp
Normal file
51
src/logid/backend/hidpp20/features/FeatureSet.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "FeatureSet.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
FeatureSet::FeatureSet(Device *device) : Feature(device, ID)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t FeatureSet::getFeatureCount()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
auto response = callFunction(GetFeatureCount, params);
|
||||
return response[0];
|
||||
}
|
||||
|
||||
uint16_t FeatureSet::getFeature(uint8_t feature_index)
|
||||
{
|
||||
std::vector<uint8_t> params(1);
|
||||
params[0] = feature_index;
|
||||
auto response = callFunction(GetFeature, params);
|
||||
|
||||
uint16_t feature_id = (response[0] << 8);
|
||||
feature_id |= response[1];
|
||||
return feature_id;
|
||||
}
|
||||
|
||||
std::map<uint8_t, uint16_t> FeatureSet::getFeatures()
|
||||
{
|
||||
uint8_t feature_count = getFeatureCount();
|
||||
std::map<uint8_t, uint16_t> features;
|
||||
for(uint8_t i = 0; i < feature_count; i++)
|
||||
features[i] = getFeature(i);
|
||||
return features;
|
||||
}
|
48
src/logid/backend/hidpp20/features/FeatureSet.h
Normal file
48
src/logid/backend/hidpp20/features/FeatureSet.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_FEATURESET_H
|
||||
#define LOGID_BACKEND_HIDPP20_FEATURE_FEATURESET_H
|
||||
|
||||
#include "../Feature.h"
|
||||
#include "../feature_defs.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20
|
||||
{
|
||||
class FeatureSet : public Feature
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::FEATURE_SET;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
enum Function : uint8_t
|
||||
{
|
||||
GetFeatureCount = 0,
|
||||
GetFeature = 1
|
||||
};
|
||||
|
||||
explicit FeatureSet(Device* device);
|
||||
|
||||
uint8_t getFeatureCount();
|
||||
uint16_t getFeature(uint8_t feature_index);
|
||||
std::map<uint8_t, uint16_t> getFeatures();
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_FEATURE_FEATURESET_H
|
76
src/logid/backend/hidpp20/features/HiresScroll.cpp
Normal file
76
src/logid/backend/hidpp20/features/HiresScroll.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <cassert>
|
||||
#include "HiresScroll.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
HiresScroll::HiresScroll(Device *device) : Feature(device, ID)
|
||||
{
|
||||
}
|
||||
|
||||
HiresScroll::Capabilities HiresScroll::getCapabilities()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
auto response = callFunction(GetCapabilities, params);
|
||||
|
||||
Capabilities capabilities{};
|
||||
capabilities.multiplier = response[0];
|
||||
capabilities.flags = response[1];
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
uint8_t HiresScroll::getMode()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
auto response = callFunction(GetMode, params);
|
||||
return response[0];
|
||||
}
|
||||
|
||||
void HiresScroll::setMode(uint8_t mode)
|
||||
{
|
||||
std::vector<uint8_t> params(1);
|
||||
params[0] = mode;
|
||||
callFunction(SetMode, params);
|
||||
}
|
||||
|
||||
bool HiresScroll::getRatchetState()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
auto response = callFunction(GetRatchetState, params);
|
||||
return params[0];
|
||||
}
|
||||
|
||||
HiresScroll::WheelStatus HiresScroll::wheelMovementEvent(const hidpp::Report
|
||||
&report)
|
||||
{
|
||||
assert(report.function() == WheelMovement);
|
||||
WheelStatus status{};
|
||||
status.hiRes = report.paramBegin()[0] & 1<<4;
|
||||
status.periods = report.paramBegin()[0] & 0x0F;
|
||||
status.deltaV = report.paramBegin()[1] << 8 | report.paramBegin()[2];
|
||||
return status;
|
||||
}
|
||||
|
||||
HiresScroll::RatchetState HiresScroll::ratchetSwitchEvent(const hidpp::Report
|
||||
&report)
|
||||
{
|
||||
assert(report.function() == WheelMovement);
|
||||
// Possible bad cast
|
||||
return static_cast<RatchetState>(report.paramBegin()[0]);
|
||||
}
|
93
src/logid/backend/hidpp20/features/HiresScroll.h
Normal file
93
src/logid/backend/hidpp20/features/HiresScroll.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_HIRESSCROLL_H
|
||||
#define LOGID_BACKEND_HIDPP20_FEATURE_HIRESSCROLL_H
|
||||
|
||||
#include "../Feature.h"
|
||||
#include "../feature_defs.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20
|
||||
{
|
||||
class HiresScroll : public Feature
|
||||
{
|
||||
public:
|
||||
///TODO: Hires scroll V1?
|
||||
static const uint16_t ID = FeatureID::HIRES_SCROLLING_V2;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
enum Function : uint8_t
|
||||
{
|
||||
GetCapabilities = 0,
|
||||
GetMode = 1,
|
||||
SetMode = 2,
|
||||
GetRatchetState = 3
|
||||
};
|
||||
|
||||
enum Event : uint8_t
|
||||
{
|
||||
WheelMovement = 0,
|
||||
RatchetSwitch = 1,
|
||||
};
|
||||
|
||||
enum Capability : uint8_t
|
||||
{
|
||||
Invertable = 1<<3,
|
||||
HasRatchet = 1<<2
|
||||
};
|
||||
|
||||
enum Mode : uint8_t
|
||||
{
|
||||
Inverted = 1<<2,
|
||||
HiRes = 1<<1,
|
||||
Target = 1
|
||||
};
|
||||
|
||||
enum RatchetState : uint8_t
|
||||
{
|
||||
FreeWheel = 0,
|
||||
Ratchet = 1
|
||||
};
|
||||
|
||||
struct Capabilities
|
||||
{
|
||||
uint8_t multiplier;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
struct WheelStatus
|
||||
{
|
||||
bool hiRes;
|
||||
uint8_t periods;
|
||||
uint16_t deltaV;
|
||||
};
|
||||
|
||||
explicit HiresScroll(Device* device);
|
||||
|
||||
Capabilities getCapabilities();
|
||||
uint8_t getMode();
|
||||
void setMode(uint8_t mode);
|
||||
bool getRatchetState();
|
||||
|
||||
static WheelStatus wheelMovementEvent(const hidpp::Report& report);
|
||||
static RatchetState ratchetSwitchEvent(const hidpp::Report& report);
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_FEATURE_HIRESSCROLL_H
|
189
src/logid/backend/hidpp20/features/ReprogControls.cpp
Normal file
189
src/logid/backend/hidpp20/features/ReprogControls.cpp
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <cassert>
|
||||
#include "../Error.h"
|
||||
#include "ReprogControls.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
#define DEFINE_REPROG(x, base) \
|
||||
x::x(Device* dev) : base(dev, ID) \
|
||||
{ \
|
||||
} \
|
||||
x::x(Device* dev, uint16_t _id) : base(dev, _id) \
|
||||
{ \
|
||||
}
|
||||
|
||||
#define MAKE_REPROG(x, dev) \
|
||||
try { \
|
||||
return std::make_shared<x>(dev); \
|
||||
} catch(UnsupportedFeature &e) {\
|
||||
}
|
||||
|
||||
// Define all of the ReprogControls versions
|
||||
DEFINE_REPROG(ReprogControls, Feature)
|
||||
DEFINE_REPROG(ReprogControlsV2, ReprogControls)
|
||||
DEFINE_REPROG(ReprogControlsV2_2, ReprogControlsV2)
|
||||
DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2)
|
||||
DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3)
|
||||
|
||||
std::shared_ptr<ReprogControls> ReprogControls::autoVersion(Device *dev)
|
||||
{
|
||||
MAKE_REPROG(ReprogControlsV4, dev)
|
||||
MAKE_REPROG(ReprogControlsV3, dev)
|
||||
MAKE_REPROG(ReprogControlsV2_2, dev)
|
||||
MAKE_REPROG(ReprogControlsV2, dev)
|
||||
|
||||
// If base version cannot be made, throw error
|
||||
return std::make_shared<ReprogControls>(dev);
|
||||
}
|
||||
|
||||
uint8_t ReprogControls::getControlCount()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
auto response = callFunction(GetControlCount, params);
|
||||
return response[0];
|
||||
}
|
||||
|
||||
ReprogControls::ControlInfo ReprogControls::getControlInfo(uint8_t index)
|
||||
{
|
||||
std::vector<uint8_t> params(1);
|
||||
ControlInfo info{};
|
||||
params[0] = index;
|
||||
auto response = callFunction(GetControlInfo, params);
|
||||
|
||||
info.controlID = response[1];
|
||||
info.controlID |= response[0] << 8;
|
||||
info.taskID = response[3];
|
||||
info.taskID |= response[2] << 8;
|
||||
info.flags = response[4];
|
||||
info.position = response[5];
|
||||
info.group = response[6];
|
||||
info.groupMask = response[7];
|
||||
info.additionalFlags = response[8];
|
||||
return info;
|
||||
}
|
||||
|
||||
void ReprogControls::initCidMap()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_cids_populating);
|
||||
if(_cids_initialized)
|
||||
return;
|
||||
uint8_t controls = getControlCount();
|
||||
for(uint8_t i = 0; i < controls; i++) {
|
||||
auto info = getControlInfo(i);
|
||||
_cids.emplace(info.controlID, info);
|
||||
}
|
||||
_cids_initialized = true;
|
||||
}
|
||||
|
||||
const std::map<uint16_t, ReprogControls::ControlInfo>&
|
||||
ReprogControls::getControls() const
|
||||
{
|
||||
return _cids;
|
||||
}
|
||||
|
||||
ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid)
|
||||
{
|
||||
if(!_cids_initialized)
|
||||
initCidMap();
|
||||
|
||||
auto it = _cids.find(cid);
|
||||
if(it == _cids.end())
|
||||
throw Error(Error::InvalidArgument);
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ReprogControls::ControlInfo ReprogControls::getControlReporting(uint16_t cid)
|
||||
{
|
||||
// Emulate this function, only Reprog controls v4 supports this
|
||||
auto info = getControlIdInfo(cid);
|
||||
|
||||
ControlInfo report{};
|
||||
report.controlID = cid;
|
||||
report.flags = 0;
|
||||
if(info.flags & TemporaryDivertable)
|
||||
report.flags |= TemporaryDiverted;
|
||||
if(info.flags & PersisentlyDivertable)
|
||||
report.flags |= PersistentlyDiverted;
|
||||
if(info.additionalFlags & RawXY)
|
||||
report.flags |= RawXYDiverted;
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
void ReprogControls::setControlReporting(uint8_t cid, ControlInfo info)
|
||||
{
|
||||
// This function does not exist pre-v4 and cannot be emulated, ignore.
|
||||
(void)cid; (void)info; // Suppress unused warnings
|
||||
}
|
||||
|
||||
std::set<uint16_t> ReprogControls::divertedButtonEvent(
|
||||
const hidpp::Report& report)
|
||||
{
|
||||
assert(report.function() == DivertedButtonEvent);
|
||||
std::set<uint16_t> buttons;
|
||||
uint8_t cids = std::distance(report.paramBegin(), report.paramEnd())/2;
|
||||
for(uint8_t i = 0; i < cids; i++) {
|
||||
uint16_t cid = report.paramBegin()[2*i + 1];
|
||||
cid |= report.paramBegin()[2*i] << 8;
|
||||
if(cid)
|
||||
buttons.insert(cid);
|
||||
else
|
||||
break;
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
ReprogControls::Move ReprogControls::divertedRawXYEvent(const hidpp::Report
|
||||
&report)
|
||||
{
|
||||
assert(report.function() == DivertedRawXYEvent);
|
||||
Move move{};
|
||||
move.x = report.paramBegin()[1];
|
||||
move.x |= report.paramBegin()[0] << 8;
|
||||
move.y = report.paramBegin()[3];
|
||||
move.y |= report.paramBegin()[2] << 8;
|
||||
return move;
|
||||
}
|
||||
|
||||
ReprogControls::ControlInfo ReprogControlsV4::getControlReporting(uint16_t cid)
|
||||
{
|
||||
std::vector<uint8_t> params(2);
|
||||
ControlInfo info{};
|
||||
params[0] = (cid >> 8) & 0xff;
|
||||
params[1] = cid & 0xff;
|
||||
auto response = callFunction(GetControlReporting, params);
|
||||
|
||||
info.controlID = response[1];
|
||||
info.controlID |= response[0] << 8;
|
||||
info.flags = response[2];
|
||||
return info;
|
||||
}
|
||||
|
||||
void ReprogControlsV4::setControlReporting(uint8_t cid, ControlInfo info)
|
||||
{
|
||||
std::vector<uint8_t> params(5);
|
||||
params[0] = (cid >> 8) & 0xff;
|
||||
params[1] = cid & 0xff;
|
||||
params[2] = info.flags;
|
||||
params[3] = (info.controlID >> 8) & 0xff;
|
||||
params[4] = info.controlID & 0xff;
|
||||
callFunction(SetControlReporting, params);
|
||||
}
|
172
src/logid/backend/hidpp20/features/ReprogControls.h
Normal file
172
src/logid/backend/hidpp20/features/ReprogControls.h
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
|
||||
#define LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../feature_defs.h"
|
||||
#include "../Feature.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20
|
||||
{
|
||||
class ReprogControls : public Feature
|
||||
{
|
||||
public:
|
||||
enum Function {
|
||||
GetControlCount = 0,
|
||||
GetControlInfo = 1,
|
||||
GetControlReporting = 2,
|
||||
SetControlReporting = 3
|
||||
};
|
||||
enum Event {
|
||||
DivertedButtonEvent = 0,
|
||||
DivertedRawXYEvent = 1
|
||||
};
|
||||
|
||||
struct ControlInfo
|
||||
{
|
||||
uint16_t controlID;
|
||||
uint16_t taskID;
|
||||
uint8_t flags;
|
||||
uint8_t position; // F key position, 0 if not an Fx key
|
||||
uint8_t group;
|
||||
uint8_t groupMask;
|
||||
uint8_t additionalFlags;
|
||||
};
|
||||
|
||||
enum ControlInfoFlags: uint8_t
|
||||
{
|
||||
MouseButton = 1, //Mouse button
|
||||
FKey = 1<<1, //Fx key
|
||||
Hotkey = 1<<2,
|
||||
FnToggle = 1<<3,
|
||||
ReprogHint = 1<<4,
|
||||
TemporaryDivertable = 1<<5,
|
||||
PersisentlyDivertable = 1<<6,
|
||||
Virtual = 1<<7
|
||||
};
|
||||
enum ControlInfoAdditionalFlags: uint8_t {
|
||||
RawXY = 1<<0
|
||||
};
|
||||
|
||||
enum ControlReportingFlags: uint8_t {
|
||||
TemporaryDiverted = 1<<0,
|
||||
ChangeTemporaryDivert = 1<<1,
|
||||
PersistentlyDiverted = 1<<2,
|
||||
ChangePersistentDivert = 1<<3,
|
||||
RawXYDiverted = 1<<4,
|
||||
ChangeRawXYDivert = 1<<5
|
||||
};
|
||||
|
||||
struct Move
|
||||
{
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
};
|
||||
|
||||
static const uint16_t ID = FeatureID::REPROG_CONTROLS;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
virtual bool supportsRawXY() { return false; }
|
||||
|
||||
explicit ReprogControls(Device* dev);
|
||||
|
||||
virtual uint8_t getControlCount();
|
||||
|
||||
virtual ControlInfo getControlInfo(uint8_t cid);
|
||||
|
||||
virtual ControlInfo getControlIdInfo(uint16_t cid);
|
||||
|
||||
virtual void initCidMap();
|
||||
|
||||
const std::map<uint16_t, ControlInfo>& getControls() const;
|
||||
|
||||
// Onlu controlId and flags will be set
|
||||
virtual ControlInfo getControlReporting(uint16_t cid);
|
||||
|
||||
// Only controlId (for remap) and flags will be read
|
||||
virtual void setControlReporting(uint8_t cid, ControlInfo info);
|
||||
|
||||
static std::set<uint16_t> divertedButtonEvent(const hidpp::Report&
|
||||
report);
|
||||
|
||||
static Move divertedRawXYEvent(const hidpp::Report& report);
|
||||
|
||||
static std::shared_ptr<ReprogControls> autoVersion(Device *dev);
|
||||
protected:
|
||||
ReprogControls(Device* dev, uint16_t _id);
|
||||
std::map<uint16_t, ControlInfo> _cids;
|
||||
bool _cids_initialized = false;
|
||||
std::mutex _cids_populating;
|
||||
};
|
||||
|
||||
class ReprogControlsV2 : public ReprogControls
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
explicit ReprogControlsV2(Device* dev);
|
||||
protected:
|
||||
ReprogControlsV2(Device* dev, uint16_t _id);
|
||||
};
|
||||
|
||||
class ReprogControlsV2_2 : public ReprogControlsV2
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2_2;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
explicit ReprogControlsV2_2(Device* dev);
|
||||
protected:
|
||||
ReprogControlsV2_2(Device* dev, uint16_t _id);
|
||||
};
|
||||
|
||||
class ReprogControlsV3 : public ReprogControlsV2_2
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V3;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
explicit ReprogControlsV3(Device* dev);
|
||||
protected:
|
||||
ReprogControlsV3(Device* dev, uint16_t _id);
|
||||
};
|
||||
|
||||
class ReprogControlsV4 : public ReprogControlsV3
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V4;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
bool supportsRawXY() override { return true; }
|
||||
|
||||
ControlInfo getControlReporting(uint16_t cid) override;
|
||||
|
||||
void setControlReporting(uint8_t cid, ControlInfo info) override;
|
||||
|
||||
explicit ReprogControlsV4(Device* dev);
|
||||
protected:
|
||||
ReprogControlsV4(Device* dev, uint16_t _id);
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
|
42
src/logid/backend/hidpp20/features/Reset.cpp
Normal file
42
src/logid/backend/hidpp20/features/Reset.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "Reset.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
Reset::Reset(Device *device) : Feature(device, ID)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t Reset::getProfile()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
auto results = callFunction(GetProfile, params);
|
||||
|
||||
uint16_t profile = results[1];
|
||||
profile |= (results[0] << 8);
|
||||
return profile;
|
||||
}
|
||||
|
||||
void Reset::reset(uint16_t profile)
|
||||
{
|
||||
std::vector<uint8_t> params(2);
|
||||
params[0] = (profile >> 8) & 0xff;
|
||||
params[1] = profile & 0xff;
|
||||
callFunction(ResetToProfile, params);
|
||||
}
|
47
src/logid/backend/hidpp20/features/Reset.h
Normal file
47
src/logid/backend/hidpp20/features/Reset.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_RESET_H
|
||||
#define LOGID_BACKEND_HIDPP20_FEATURE_RESET_H
|
||||
|
||||
#include "../Feature.h"
|
||||
#include "../feature_defs.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20
|
||||
{
|
||||
class Reset : public Feature
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::RESET;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
enum Function : uint8_t
|
||||
{
|
||||
GetProfile = 0,
|
||||
ResetToProfile = 1
|
||||
};
|
||||
|
||||
explicit Reset(Device* device);
|
||||
|
||||
uint16_t getProfile();
|
||||
void reset(uint16_t profile = 0);
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_FEATURE_RESET_H
|
96
src/logid/backend/hidpp20/features/Root.cpp
Normal file
96
src/logid/backend/hidpp20/features/Root.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Root.h"
|
||||
#include "../Error.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
Root::Root(Device* dev) : Feature(dev, ID)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<uint8_t> _genGetFeatureParams(uint16_t feature_id)
|
||||
{
|
||||
std::vector<uint8_t> params(2);
|
||||
params[0] = feature_id & 0xff;
|
||||
params[1] = (feature_id >> 8) & 0xff;
|
||||
return params;
|
||||
}
|
||||
|
||||
feature_info _genGetFeatureInfo(uint16_t feature_id,
|
||||
std::vector<uint8_t> response)
|
||||
{
|
||||
feature_info info{};
|
||||
info.feature_id = response[0];
|
||||
|
||||
if(!info.feature_id)
|
||||
throw UnsupportedFeature(feature_id);
|
||||
|
||||
info.hidden = response[1] & Root::FeatureFlag::Hidden;
|
||||
info.obsolete = response[1] & Root::FeatureFlag::Obsolete;
|
||||
info.internal = response[1] & Root::FeatureFlag::Internal;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
feature_info Root::getFeature(uint16_t feature_id)
|
||||
{
|
||||
auto params = _genGetFeatureParams(feature_id);
|
||||
try {
|
||||
auto response = this->callFunction(Root::Function::GetFeature, params);
|
||||
return _genGetFeatureInfo(feature_id, response);
|
||||
} catch(Error& e) {
|
||||
if(e.code() == Error::InvalidFeatureIndex)
|
||||
throw UnsupportedFeature(feature_id);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<uint8_t, uint8_t> Root::getVersion()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
auto response = this->callFunction(Function::Ping, params);
|
||||
|
||||
return std::make_tuple(response[0], response[1]);
|
||||
}
|
||||
|
||||
EssentialRoot::EssentialRoot(hidpp::Device* dev) : EssentialFeature(dev, ID)
|
||||
{
|
||||
}
|
||||
|
||||
feature_info EssentialRoot::getFeature(uint16_t feature_id)
|
||||
{
|
||||
auto params = _genGetFeatureParams(feature_id);
|
||||
try {
|
||||
auto response = this->callFunction(Root::Function::GetFeature, params);
|
||||
return _genGetFeatureInfo(feature_id, response);
|
||||
} catch(Error& e) {
|
||||
if(e.code() == Error::InvalidFeatureIndex)
|
||||
throw UnsupportedFeature(feature_id);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<uint8_t, uint8_t> EssentialRoot::getVersion()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
auto response = this->callFunction(Root::Function::Ping, params);
|
||||
|
||||
return std::make_tuple(response[0], response[1]);
|
||||
}
|
68
src/logid/backend/hidpp20/features/Root.h
Normal file
68
src/logid/backend/hidpp20/features/Root.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H
|
||||
#define LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H
|
||||
|
||||
#include "../Feature.h"
|
||||
#include "../EssentialFeature.h"
|
||||
#include "../feature_defs.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20
|
||||
{
|
||||
class Root : public Feature
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::ROOT;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
enum Function : uint8_t
|
||||
{
|
||||
GetFeature = 0,
|
||||
Ping = 1
|
||||
};
|
||||
|
||||
explicit Root(Device* device);
|
||||
|
||||
feature_info getFeature (uint16_t feature_id);
|
||||
std::tuple<uint8_t, uint8_t> getVersion();
|
||||
|
||||
enum FeatureFlag : uint8_t
|
||||
{
|
||||
Obsolete = 1<<7,
|
||||
Hidden = 1<<6,
|
||||
Internal = 1<<5
|
||||
};
|
||||
};
|
||||
|
||||
class EssentialRoot : public EssentialFeature
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::ROOT;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
explicit EssentialRoot(hidpp::Device* device);
|
||||
|
||||
feature_info getFeature(uint16_t feature_id);
|
||||
std::tuple<uint8_t, uint8_t> getVersion();
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H
|
47
src/logid/backend/hidpp20/features/SmartShift.cpp
Normal file
47
src/logid/backend/hidpp20/features/SmartShift.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "SmartShift.h"
|
||||
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
SmartShift::SmartShift(Device* dev) : Feature(dev, ID)
|
||||
{
|
||||
}
|
||||
|
||||
SmartShift::SmartshiftStatus SmartShift::getStatus()
|
||||
{
|
||||
std::vector<uint8_t> params(0);
|
||||
SmartshiftStatus status{};
|
||||
auto response = callFunction(GetStatus, params);
|
||||
status.active = response[0]-1;
|
||||
status.autoDisengage = response[1];
|
||||
status.defaultAutoDisengage = response[2];
|
||||
return status;
|
||||
}
|
||||
|
||||
void SmartShift::setStatus(SmartshiftStatus status)
|
||||
{
|
||||
std::vector<uint8_t> params(3);
|
||||
if(status.setActive)
|
||||
params[0] = status.active + 1;
|
||||
if(status.setAutoDisengage)
|
||||
params[1] = status.autoDisengage;
|
||||
if(status.setDefaultAutoDisengage)
|
||||
params[2] = status.defaultAutoDisengage;
|
||||
callFunction(SetStatus, params);
|
||||
}
|
54
src/logid/backend/hidpp20/features/SmartShift.h
Normal file
54
src/logid/backend/hidpp20/features/SmartShift.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_SMARTSHIFT_H
|
||||
#define LOGID_BACKEND_HIDPP20_FEATURE_SMARTSHIFT_H
|
||||
|
||||
#include "../feature_defs.h"
|
||||
#include "../Feature.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp20
|
||||
{
|
||||
class SmartShift : public Feature
|
||||
{
|
||||
public:
|
||||
static const uint16_t ID = FeatureID::SMART_SHIFT;
|
||||
virtual uint16_t getID() { return ID; }
|
||||
|
||||
enum Function {
|
||||
GetStatus = 0,
|
||||
SetStatus = 1
|
||||
};
|
||||
|
||||
explicit SmartShift(Device* dev);
|
||||
|
||||
struct SmartshiftStatus
|
||||
{
|
||||
bool active;
|
||||
uint8_t autoDisengage;
|
||||
uint8_t defaultAutoDisengage;
|
||||
bool setActive, setAutoDisengage, setDefaultAutoDisengage;
|
||||
};
|
||||
|
||||
SmartshiftStatus getStatus();
|
||||
void setStatus(SmartshiftStatus status);
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_HIDPP20_FEATURE_SMARTSHIFT_H
|
183
src/logid/backend/raw/DeviceMonitor.cpp
Normal file
183
src/logid/backend/raw/DeviceMonitor.cpp
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "DeviceMonitor.h"
|
||||
#include "../../util/task.h"
|
||||
#include "../../util/log.h"
|
||||
#include "RawDevice.h"
|
||||
#include "../hidpp/Device.h"
|
||||
|
||||
#include <thread>
|
||||
#include <system_error>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <unistd.h>
|
||||
#include <libudev.h>
|
||||
}
|
||||
|
||||
using namespace logid::backend::raw;
|
||||
|
||||
DeviceMonitor::DeviceMonitor()
|
||||
{
|
||||
if(-1 == pipe(_pipe))
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"pipe creation failed");
|
||||
|
||||
_udev_context = udev_new();
|
||||
if(!_udev_context)
|
||||
throw std::runtime_error("udev_new failed");
|
||||
}
|
||||
|
||||
DeviceMonitor::~DeviceMonitor()
|
||||
{
|
||||
this->stop();
|
||||
|
||||
udev_unref(_udev_context);
|
||||
|
||||
for(int i : _pipe)
|
||||
close(i);
|
||||
}
|
||||
|
||||
void DeviceMonitor::run()
|
||||
{
|
||||
int ret;
|
||||
std::lock_guard<std::mutex> lock(_running);
|
||||
|
||||
struct udev_monitor* monitor = udev_monitor_new_from_netlink(_udev_context,
|
||||
"udev");
|
||||
if(!monitor)
|
||||
throw std::runtime_error("udev_monitor_new_from_netlink failed");
|
||||
|
||||
ret = udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw",
|
||||
nullptr);
|
||||
if (0 != ret)
|
||||
throw std::system_error (-ret, std::system_category(),
|
||||
"udev_monitor_filter_add_match_subsystem_devtype");
|
||||
|
||||
ret = udev_monitor_enable_receiving(monitor);
|
||||
if(0 != ret)
|
||||
throw std::system_error(-ret, std::system_category(),
|
||||
"udev_moniotr_enable_receiving");
|
||||
|
||||
this->enumerate();
|
||||
|
||||
int fd = udev_monitor_get_fd(monitor);
|
||||
|
||||
_run_monitor = true;
|
||||
while (_run_monitor) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(_pipe[0], &fds);
|
||||
FD_SET(fd, &fds);
|
||||
|
||||
if (-1 == select (std::max (_pipe[0], fd)+1, &fds, nullptr,
|
||||
nullptr, nullptr)) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
throw std::system_error (errno, std::system_category(),
|
||||
"udev_monitor select");
|
||||
}
|
||||
|
||||
if (FD_ISSET(fd, &fds)) {
|
||||
struct udev_device *device = udev_monitor_receive_device(monitor);
|
||||
std::string action = udev_device_get_action(device);
|
||||
std::string devnode = udev_device_get_devnode(device);
|
||||
|
||||
if (action == "add")
|
||||
task::spawn([this, name=devnode]() {
|
||||
auto supported_reports = backend::hidpp::getSupportedReports(
|
||||
RawDevice::getReportDescriptor(name));
|
||||
if(supported_reports)
|
||||
this->addDevice(name);
|
||||
else
|
||||
logPrintf(DEBUG, "Unsupported device %s ignored",
|
||||
name.c_str());
|
||||
}, [name=devnode](std::exception& e){
|
||||
logPrintf(WARN, "Error adding device %s: %s",
|
||||
name.c_str(), e.what());
|
||||
});
|
||||
else if (action == "remove")
|
||||
task::spawn([this, name=devnode]() {
|
||||
this->removeDevice(name);
|
||||
}, [name=devnode](std::exception& e){
|
||||
logPrintf(WARN, "Error removing device %s: %s",
|
||||
name.c_str(), e.what());
|
||||
});
|
||||
|
||||
udev_device_unref (device);
|
||||
}
|
||||
if (FD_ISSET(_pipe[0], &fds)) {
|
||||
char c;
|
||||
if (-1 == read(_pipe[0], &c, sizeof (char)))
|
||||
throw std::system_error (errno, std::system_category(),
|
||||
"read pipe");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceMonitor::stop()
|
||||
{
|
||||
_run_monitor = false;
|
||||
std::lock_guard<std::mutex> lock(_running);
|
||||
}
|
||||
|
||||
void DeviceMonitor::enumerate()
|
||||
{
|
||||
int ret;
|
||||
struct udev_enumerate* udev_enum = udev_enumerate_new(_udev_context);
|
||||
ret = udev_enumerate_add_match_subsystem(udev_enum, "hidraw");
|
||||
if(0 != ret)
|
||||
throw std::system_error(-ret, std::system_category(),
|
||||
"udev_enumerate_add_match_subsystem");
|
||||
|
||||
ret = udev_enumerate_scan_devices(udev_enum);
|
||||
if(0 != ret)
|
||||
throw std::system_error(-ret, std::system_category(),
|
||||
"udev_enumerate_scan_devices");
|
||||
|
||||
struct udev_list_entry* udev_enum_entry;
|
||||
udev_list_entry_foreach(udev_enum_entry,
|
||||
udev_enumerate_get_list_entry(udev_enum)) {
|
||||
const char* name = udev_list_entry_get_name(udev_enum_entry);
|
||||
|
||||
struct udev_device* device = udev_device_new_from_syspath(_udev_context,
|
||||
name);
|
||||
if(!device)
|
||||
throw std::runtime_error("udev_device_new_from_syspath failed");
|
||||
|
||||
std::string devnode = udev_device_get_devnode(device);
|
||||
udev_device_unref(device);
|
||||
|
||||
task::spawn([this, name=devnode]() {
|
||||
auto supported_reports = backend::hidpp::getSupportedReports(
|
||||
RawDevice::getReportDescriptor(name));
|
||||
if(supported_reports)
|
||||
this->addDevice(name);
|
||||
else
|
||||
logPrintf(DEBUG, "Unsupported device %s ignored",
|
||||
name.c_str());
|
||||
}, [name=devnode](std::exception& e){
|
||||
logPrintf(WARN, "Error adding device %s: %s",
|
||||
name.c_str(), e.what());
|
||||
});
|
||||
}
|
||||
|
||||
udev_enumerate_unref(udev_enum);
|
||||
}
|
54
src/logid/backend/raw/DeviceMonitor.h
Normal file
54
src/logid/backend/raw/DeviceMonitor.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_RAW_DEVICEMONITOR_H
|
||||
#define LOGID_BACKEND_RAW_DEVICEMONITOR_H
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <libudev.h>
|
||||
}
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace raw
|
||||
{
|
||||
class DeviceMonitor
|
||||
{
|
||||
public:
|
||||
void enumerate();
|
||||
void run();
|
||||
void stop();
|
||||
protected:
|
||||
DeviceMonitor();
|
||||
~DeviceMonitor();
|
||||
virtual void addDevice(std::string device) = 0;
|
||||
virtual void removeDevice(std::string device) = 0;
|
||||
private:
|
||||
struct udev* _udev_context;
|
||||
int _pipe[2];
|
||||
std::atomic<bool> _run_monitor;
|
||||
std::mutex _running;
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_RAW_DEVICEMONITOR_H
|
497
src/logid/backend/raw/RawDevice.cpp
Normal file
497
src/logid/backend/raw/RawDevice.cpp
Normal file
|
@ -0,0 +1,497 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "RawDevice.h"
|
||||
#include "../Error.h"
|
||||
#include "../hidpp/defs.h"
|
||||
#include "../dj/defs.h"
|
||||
#include "../../util/log.h"
|
||||
#include "../hidpp/Report.h"
|
||||
#include "../../Configuration.h"
|
||||
#include "../../util/thread.h"
|
||||
#include "../../util/task.h"
|
||||
#include "../../util/workqueue.h"
|
||||
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
#define MAX_DATA_LENGTH 32
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <cassert>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/hidraw.h>
|
||||
}
|
||||
|
||||
using namespace logid::backend::raw;
|
||||
using namespace logid::backend;
|
||||
using namespace std::chrono;
|
||||
|
||||
bool RawDevice::supportedReport(uint8_t id, uint8_t length)
|
||||
{
|
||||
switch(id) {
|
||||
case hidpp::ReportType::Short:
|
||||
return length == (hidpp::ShortParamLength +
|
||||
hidpp::Report::HeaderLength);
|
||||
case hidpp::ReportType::Long:
|
||||
return length == (hidpp::LongParamLength +
|
||||
hidpp::Report::HeaderLength);
|
||||
case dj::ReportType::Short:
|
||||
return length == (dj::ShortParamLength + dj::HeaderLength);
|
||||
case dj::ReportType::Long:
|
||||
return length == (dj::LongParamLength + dj::HeaderLength);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RawDevice::RawDevice(std::string path) : _path (std::move(path)),
|
||||
_continue_listen (false), _continue_respond (false)
|
||||
{
|
||||
int ret;
|
||||
|
||||
_fd = ::open(_path.c_str(), O_RDWR);
|
||||
if (_fd == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"RawDevice open failed");
|
||||
|
||||
hidraw_devinfo devinfo{};
|
||||
if (-1 == ::ioctl(_fd, HIDIOCGRAWINFO, &devinfo)) {
|
||||
int err = errno;
|
||||
::close(_fd);
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice HIDIOCGRAWINFO failed");
|
||||
}
|
||||
_vid = devinfo.vendor;
|
||||
_pid = devinfo.product;
|
||||
|
||||
char name_buf[256];
|
||||
if (-1 == (ret = ::ioctl(_fd, HIDIOCGRAWNAME(sizeof(name_buf)), name_buf)
|
||||
)) {
|
||||
int err = errno;
|
||||
::close(_fd);
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice HIDIOCGRAWNAME failed");
|
||||
}
|
||||
_name.assign(name_buf, ret - 1);
|
||||
|
||||
_rdesc = getReportDescriptor(_fd);
|
||||
|
||||
if (-1 == ::pipe(_pipe)) {
|
||||
int err = errno;
|
||||
close(_fd);
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice pipe open failed");
|
||||
}
|
||||
|
||||
_continue_listen = false;
|
||||
}
|
||||
|
||||
RawDevice::~RawDevice()
|
||||
{
|
||||
if(_fd != -1)
|
||||
{
|
||||
::close(_fd);
|
||||
::close(_pipe[0]);
|
||||
::close(_pipe[1]);
|
||||
}
|
||||
}
|
||||
std::string RawDevice::hidrawPath() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
std::string RawDevice::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
uint16_t RawDevice::vendorId() const
|
||||
{
|
||||
return _vid;
|
||||
}
|
||||
|
||||
uint16_t RawDevice::productId() const
|
||||
{
|
||||
return _pid;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::getReportDescriptor(std::string path)
|
||||
{
|
||||
int fd = ::open(path.c_str(), O_RDWR);
|
||||
if (fd == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"open failed");
|
||||
|
||||
auto rdesc = getReportDescriptor(fd);
|
||||
::close(fd);
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::getReportDescriptor(int fd)
|
||||
{
|
||||
hidraw_report_descriptor rdesc{};
|
||||
if (-1 == ::ioctl(fd, HIDIOCGRDESCSIZE, &rdesc.size)) {
|
||||
int err = errno;
|
||||
::close(fd);
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice HIDIOCGRDESCSIZE failed");
|
||||
}
|
||||
if (-1 == ::ioctl(fd, HIDIOCGRDESC, &rdesc)) {
|
||||
int err = errno;
|
||||
::close(fd);
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice HIDIOCGRDESC failed");
|
||||
}
|
||||
return std::vector<uint8_t>(rdesc.value, rdesc.value + rdesc.size);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::reportDescriptor() const
|
||||
{
|
||||
return _rdesc;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::sendReport(const std::vector<uint8_t>& report)
|
||||
{
|
||||
/* If the listener will stop, handle I/O manually.
|
||||
* Otherwise, push to queue and wait for result. */
|
||||
if(_continue_listen) {
|
||||
std::mutex send_report;
|
||||
std::unique_lock<std::mutex> lock(send_report);
|
||||
std::condition_variable cv;
|
||||
bool top_of_queue = false;
|
||||
auto task = std::make_shared<std::packaged_task<std::vector<uint8_t>()>>
|
||||
( [this, report, &cv, &top_of_queue] () {
|
||||
top_of_queue = true;
|
||||
cv.notify_all();
|
||||
return this->_respondToReport(report);
|
||||
});
|
||||
auto f = task->get_future();
|
||||
_io_queue.push(task);
|
||||
cv.wait(lock, [&top_of_queue]{ return top_of_queue; });
|
||||
auto status = f.wait_for(global_config->ioTimeout());
|
||||
if(status == std::future_status::timeout) {
|
||||
_continue_respond = false;
|
||||
interruptRead();
|
||||
return f.get(); // Expecting an error, but it could work
|
||||
}
|
||||
return f.get();
|
||||
}
|
||||
else {
|
||||
std::vector<uint8_t> response;
|
||||
std::exception_ptr _exception;
|
||||
std::shared_ptr<task> t = std::make_shared<task>(
|
||||
[this, report, &response]() {
|
||||
response = _respondToReport(report);
|
||||
}, [&_exception](std::exception& e) {
|
||||
try {
|
||||
throw e;
|
||||
} catch(std::exception& e) {
|
||||
_exception = std::make_exception_ptr(e);
|
||||
}
|
||||
});
|
||||
global_workqueue->queue(t);
|
||||
t->waitStart();
|
||||
auto status = t->waitFor(global_config->ioTimeout());
|
||||
if(_exception)
|
||||
std::rethrow_exception(_exception);
|
||||
if(status == std::future_status::timeout) {
|
||||
_continue_respond = false;
|
||||
interruptRead();
|
||||
t->wait();
|
||||
if(_exception)
|
||||
std::rethrow_exception(_exception);
|
||||
throw TimeoutError();
|
||||
} else
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
// DJ commands are not systematically acknowledged, do not expect a result.
|
||||
void RawDevice::sendReportNoResponse(const std::vector<uint8_t>& report)
|
||||
{
|
||||
/* If the listener will stop, handle I/O manually.
|
||||
* Otherwise, push to queue and wait for result. */
|
||||
if(_continue_listen) {
|
||||
auto task = std::make_shared<std::packaged_task<std::vector<uint8_t>()>>
|
||||
([this, report]() {
|
||||
this->_sendReport(report);
|
||||
return std::vector<uint8_t>();
|
||||
});
|
||||
auto f = task->get_future();
|
||||
_io_queue.push(task);
|
||||
f.get();
|
||||
}
|
||||
else
|
||||
_sendReport(report);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::_respondToReport
|
||||
(const std::vector<uint8_t>& request)
|
||||
{
|
||||
_sendReport(request);
|
||||
_continue_respond = true;
|
||||
|
||||
auto start_point = std::chrono::steady_clock::now();
|
||||
|
||||
while(_continue_respond) {
|
||||
std::vector<uint8_t> response;
|
||||
auto current_point = std::chrono::steady_clock::now();
|
||||
auto timeout = global_config->ioTimeout() - std::chrono::duration_cast
|
||||
<std::chrono::milliseconds>(current_point - start_point);
|
||||
if(timeout.count() <= 0)
|
||||
throw TimeoutError();
|
||||
_readReport(response, MAX_DATA_LENGTH, timeout);
|
||||
|
||||
if(!_continue_respond)
|
||||
throw TimeoutError();
|
||||
|
||||
// All reports have the device index at byte 2
|
||||
if(response[1] != request[1]) {
|
||||
if(_continue_listen)
|
||||
this->_handleEvent(response);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(hidpp::ReportType::Short == request[0] ||
|
||||
hidpp::ReportType::Long == request[0]) {
|
||||
if(hidpp::ReportType::Short != response[0] &&
|
||||
hidpp::ReportType::Long != response[0]) {
|
||||
if(_continue_listen)
|
||||
this->_handleEvent(response);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Error; leave to device to handle
|
||||
if(response[2] == 0x8f || response[2] == 0xff)
|
||||
return response;
|
||||
|
||||
bool others_match = true;
|
||||
for(int i = 2; i < 4; i++)
|
||||
if(response[i] != request[i])
|
||||
others_match = false;
|
||||
|
||||
if(others_match)
|
||||
return response;
|
||||
} else if(dj::ReportType::Short == request[0] ||
|
||||
dj::ReportType::Long == request[0]) {
|
||||
//Error; leave to device ot handle
|
||||
if(0x7f == response[2])
|
||||
return response;
|
||||
else if(response[2] == request[2])
|
||||
return response;
|
||||
}
|
||||
|
||||
if(_continue_listen)
|
||||
this->_handleEvent(response);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int RawDevice::_sendReport(const std::vector<uint8_t>& report)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_dev_io);
|
||||
if(logid::global_loglevel <= LogLevel::RAWREPORT) {
|
||||
printf("[RAWREPORT] %s OUT: ", _path.c_str());
|
||||
for(auto &i : report)
|
||||
printf("%02x ", i);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
assert(supportedReport(report[0], report.size()));
|
||||
|
||||
int ret = ::write(_fd, report.data(), report.size());
|
||||
if(ret == -1) {
|
||||
///TODO: This seems like a hacky solution
|
||||
// Try again before failing
|
||||
ret = ::write(_fd, report.data(), report.size());
|
||||
if(ret == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"_sendReport write failed");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int RawDevice::_readReport(std::vector<uint8_t> &report,
|
||||
std::size_t maxDataLength)
|
||||
{
|
||||
return _readReport(report, maxDataLength, global_config->ioTimeout());
|
||||
}
|
||||
|
||||
int RawDevice::_readReport(std::vector<uint8_t> &report,
|
||||
std::size_t maxDataLength, std::chrono::milliseconds timeout)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_dev_io);
|
||||
int ret;
|
||||
report.resize(maxDataLength);
|
||||
|
||||
timeval timeout_tv{};
|
||||
timeout_tv.tv_sec = duration_cast<seconds>(global_config->ioTimeout())
|
||||
.count();
|
||||
timeout_tv.tv_usec = duration_cast<microseconds>(
|
||||
global_config->ioTimeout()).count() %
|
||||
duration_cast<microseconds>(seconds(1)).count();
|
||||
|
||||
auto timeout_ms = duration_cast<milliseconds>(timeout).count();
|
||||
|
||||
fd_set fds;
|
||||
do {
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(_fd, &fds);
|
||||
FD_SET(_pipe[0], &fds);
|
||||
|
||||
ret = select(std::max(_fd, _pipe[0]) + 1,
|
||||
&fds, nullptr, nullptr,
|
||||
(timeout_ms > 0 ? nullptr : &timeout_tv));
|
||||
} while(ret == -1 && errno == EINTR);
|
||||
|
||||
if(ret == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"_readReport select failed");
|
||||
|
||||
if(FD_ISSET(_fd, &fds)) {
|
||||
ret = read(_fd, report.data(), report.size());
|
||||
if(ret == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"_readReport read failed");
|
||||
report.resize(ret);
|
||||
}
|
||||
|
||||
if(FD_ISSET(_pipe[0], &fds)) {
|
||||
char c;
|
||||
ret = read(_pipe[0], &c, sizeof(char));
|
||||
if(ret == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"_readReport read pipe failed");
|
||||
}
|
||||
|
||||
if(0 == ret)
|
||||
throw backend::TimeoutError();
|
||||
|
||||
if(logid::global_loglevel <= LogLevel::RAWREPORT) {
|
||||
printf("[RAWREPORT] %s IN: ", _path.c_str());
|
||||
for(auto &i : report)
|
||||
printf("%02x ", i);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RawDevice::interruptRead()
|
||||
{
|
||||
char c = 0;
|
||||
if(-1 == write(_pipe[1], &c, sizeof(char)))
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"interruptRead write pipe failed");
|
||||
|
||||
// Ensure I/O has halted
|
||||
std::lock_guard<std::mutex> lock(_dev_io);
|
||||
}
|
||||
|
||||
void RawDevice::listen()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_listening);
|
||||
_continue_listen = true;
|
||||
_listen_condition.notify_all();
|
||||
while(_continue_listen) {
|
||||
while(!_io_queue.empty()) {
|
||||
auto task = _io_queue.front();
|
||||
(*task)();
|
||||
_io_queue.pop();
|
||||
}
|
||||
std::vector<uint8_t> report;
|
||||
_readReport(report, MAX_DATA_LENGTH);
|
||||
|
||||
this->_handleEvent(report);
|
||||
}
|
||||
|
||||
// Listener is stopped, handle I/O queue
|
||||
while(!_io_queue.empty()) {
|
||||
auto task = _io_queue.front();
|
||||
(*task)();
|
||||
_io_queue.pop();
|
||||
}
|
||||
|
||||
_continue_listen = false;
|
||||
}
|
||||
|
||||
void RawDevice::listenAsync()
|
||||
{
|
||||
std::mutex listen_check;
|
||||
std::unique_lock<std::mutex> check_lock(listen_check);
|
||||
thread::spawn({[this]() { listen(); }});
|
||||
|
||||
// Block until RawDevice is listening
|
||||
_listen_condition.wait(check_lock, [this](){
|
||||
return (bool)_continue_listen;
|
||||
});
|
||||
}
|
||||
|
||||
void RawDevice::stopListener()
|
||||
{
|
||||
_continue_listen = false;
|
||||
interruptRead();
|
||||
}
|
||||
|
||||
void RawDevice::addEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<raw::RawEventHandler>& handler)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_event_handler_lock);
|
||||
auto it = _event_handlers.find(nickname);
|
||||
assert(it == _event_handlers.end());
|
||||
assert(handler);
|
||||
_event_handlers.emplace(nickname, handler);
|
||||
}
|
||||
|
||||
void RawDevice::removeEventHandler(const std::string &nickname)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_event_handler_lock);
|
||||
_event_handlers.erase(nickname);
|
||||
}
|
||||
|
||||
const std::map<std::string, std::shared_ptr<raw::RawEventHandler>>&
|
||||
RawDevice::eventHandlers()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_event_handler_lock);
|
||||
return _event_handlers;
|
||||
}
|
||||
|
||||
void RawDevice::_handleEvent(std::vector<uint8_t> &report)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_event_handler_lock);
|
||||
for(auto& handler : _event_handlers)
|
||||
if(handler.second->condition(report))
|
||||
handler.second->callback(report);
|
||||
}
|
||||
|
||||
bool RawDevice::isListening()
|
||||
{
|
||||
bool ret = _listening.try_lock();
|
||||
|
||||
if(ret)
|
||||
_listening.unlock();
|
||||
|
||||
return !ret;
|
||||
}
|
102
src/logid/backend/raw/RawDevice.h
Normal file
102
src/logid/backend/raw/RawDevice.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_RAWDEVICE_H
|
||||
#define LOGID_BACKEND_RAWDEVICE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <set>
|
||||
|
||||
#include "defs.h"
|
||||
#include "../../util/mutex_queue.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace raw
|
||||
{
|
||||
class RawDevice
|
||||
{
|
||||
public:
|
||||
static bool supportedReport(uint8_t id, uint8_t length);
|
||||
|
||||
explicit RawDevice(std::string path);
|
||||
~RawDevice();
|
||||
std::string hidrawPath() const;
|
||||
|
||||
std::string name() const;
|
||||
uint16_t vendorId() const;
|
||||
uint16_t productId() const;
|
||||
|
||||
static std::vector<uint8_t> getReportDescriptor(std::string path);
|
||||
static std::vector<uint8_t> getReportDescriptor(int fd);
|
||||
std::vector<uint8_t> reportDescriptor() const;
|
||||
|
||||
std::vector<uint8_t> sendReport(const std::vector<uint8_t>& report);
|
||||
void sendReportNoResponse(const std::vector<uint8_t>& report);
|
||||
void interruptRead();
|
||||
|
||||
void listen();
|
||||
void listenAsync();
|
||||
void stopListener();
|
||||
bool isListening();
|
||||
|
||||
void addEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<RawEventHandler>& handler);
|
||||
void removeEventHandler(const std::string& nickname);
|
||||
const std::map<std::string, std::shared_ptr<RawEventHandler>>&
|
||||
eventHandlers();
|
||||
|
||||
private:
|
||||
std::mutex _dev_io, _listening;
|
||||
std::string _path;
|
||||
int _fd;
|
||||
int _pipe[2];
|
||||
uint16_t _vid;
|
||||
uint16_t _pid;
|
||||
std::string _name;
|
||||
std::vector<uint8_t> _rdesc;
|
||||
|
||||
std::atomic<bool> _continue_listen;
|
||||
std::atomic<bool> _continue_respond;
|
||||
std::condition_variable _listen_condition;
|
||||
|
||||
std::map<std::string, std::shared_ptr<RawEventHandler>>
|
||||
_event_handlers;
|
||||
std::mutex _event_handler_lock;
|
||||
void _handleEvent(std::vector<uint8_t>& report);
|
||||
|
||||
/* These will only be used internally and processed with a queue */
|
||||
int _sendReport(const std::vector<uint8_t>& report);
|
||||
int _readReport(std::vector<uint8_t>& report, std::size_t maxDataLength);
|
||||
int _readReport(std::vector<uint8_t>& report, std::size_t maxDataLength,
|
||||
std::chrono::milliseconds timeout);
|
||||
|
||||
std::vector<uint8_t> _respondToReport(const std::vector<uint8_t>&
|
||||
request);
|
||||
|
||||
mutex_queue<std::shared_ptr<std::packaged_task<std::vector<uint8_t>()>>>
|
||||
_io_queue;
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_RAWDEVICE_H
|
37
src/logid/backend/raw/defs.h
Normal file
37
src/logid/backend/raw/defs.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_BACKEND_RAW_DEFS_H
|
||||
#define LOGID_BACKEND_RAW_DEFS_H
|
||||
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace raw
|
||||
{
|
||||
struct RawEventHandler
|
||||
{
|
||||
std::function<bool(std::vector<uint8_t>& )> condition;
|
||||
std::function<void(std::vector<uint8_t>& )> callback;
|
||||
};
|
||||
}}}
|
||||
|
||||
#endif //LOGID_BACKEND_RAW_DEFS_H
|
140
src/logid/features/DPI.cpp
Normal file
140
src/logid/features/DPI.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "DPI.h"
|
||||
#include "../Device.h"
|
||||
#include "../util/log.h"
|
||||
|
||||
using namespace logid::features;
|
||||
using namespace logid::backend;
|
||||
|
||||
uint16_t getClosestDPI(hidpp20::AdjustableDPI::SensorDPIList& dpi_list,
|
||||
uint16_t dpi)
|
||||
{
|
||||
if(dpi_list.isRange) {
|
||||
const uint16_t min = *std::min_element(dpi_list.dpis.begin(),
|
||||
dpi_list.dpis.end());
|
||||
const uint16_t max = *std::max_element(dpi_list.dpis.begin(),
|
||||
dpi_list.dpis.end());
|
||||
if(!((dpi-min) % dpi_list.dpiStep) && dpi >= min && dpi <= max)
|
||||
return dpi;
|
||||
else if(dpi > max)
|
||||
return max;
|
||||
else if(dpi < min)
|
||||
return min;
|
||||
else
|
||||
return min + round((double)(dpi-min)/dpi_list.dpiStep)*dpi_list
|
||||
.dpiStep;
|
||||
} else {
|
||||
if(std::find(dpi_list.dpis.begin(), dpi_list.dpis.end(), dpi)
|
||||
!= dpi_list.dpis.end())
|
||||
return dpi;
|
||||
else {
|
||||
auto it = std::min_element(dpi_list.dpis.begin(), dpi_list.dpis
|
||||
.end(), [dpi](uint16_t a, uint16_t b) {
|
||||
return (dpi - a) < (dpi - b);
|
||||
});
|
||||
if(it == dpi_list.dpis.end())
|
||||
return 0;
|
||||
else
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DPI::DPI(Device* device) : DeviceFeature(device), _config (device),
|
||||
_adjustable_dpi (&device->hidpp20())
|
||||
{
|
||||
}
|
||||
|
||||
void DPI::configure()
|
||||
{
|
||||
const uint8_t sensors = _adjustable_dpi.getSensorCount();
|
||||
for(uint8_t i = 0; i < _config.getSensorCount(); i++) {
|
||||
hidpp20::AdjustableDPI::SensorDPIList dpi_list;
|
||||
if(_dpi_lists.size() <= i) {
|
||||
dpi_list = _adjustable_dpi.getSensorDPIList(i);
|
||||
_dpi_lists.push_back(dpi_list);
|
||||
} else {
|
||||
dpi_list = _dpi_lists[i];
|
||||
}
|
||||
if(i < sensors) {
|
||||
auto dpi = _config.getDPI(i);
|
||||
if(dpi) {
|
||||
_adjustable_dpi.setSensorDPI(i, getClosestDPI(dpi_list,
|
||||
dpi));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DPI::listen()
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t DPI::getDPI(uint8_t sensor)
|
||||
{
|
||||
return _adjustable_dpi.getSensorDPI(sensor);
|
||||
}
|
||||
|
||||
void DPI::setDPI(uint16_t dpi, uint8_t sensor)
|
||||
{
|
||||
hidpp20::AdjustableDPI::SensorDPIList dpi_list;
|
||||
if(_dpi_lists.size() <= sensor) {
|
||||
dpi_list = _adjustable_dpi.getSensorDPIList(sensor);
|
||||
for(std::size_t i = _dpi_lists.size()-1; i <= sensor; i++) {
|
||||
_dpi_lists.push_back(_adjustable_dpi.getSensorDPIList(i));
|
||||
}
|
||||
}
|
||||
dpi_list = _dpi_lists[sensor];
|
||||
_adjustable_dpi.setSensorDPI(sensor, getClosestDPI(dpi_list, dpi));
|
||||
}
|
||||
|
||||
/* Some devices have multiple sensors, but an older config format
|
||||
* only supports a single DPI. The dpi setting can be an array or
|
||||
* an integer.
|
||||
*/
|
||||
DPI::Config::Config(Device *dev) : DeviceFeature::Config(dev)
|
||||
{
|
||||
try {
|
||||
auto& config_root = dev->config().getSetting("dpi");
|
||||
if(config_root.isNumber()) {
|
||||
int dpi = config_root;
|
||||
_dpis.push_back(dpi);
|
||||
} else if(config_root.isArray()) {
|
||||
for(int i = 0; i < config_root.getLength(); i++)
|
||||
_dpis.push_back((int)config_root[i]);
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: dpi is improperly formatted",
|
||||
config_root.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// DPI not configured, use default
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t DPI::Config::getSensorCount()
|
||||
{
|
||||
return _dpis.size();
|
||||
}
|
||||
|
||||
uint16_t DPI::Config::getDPI(uint8_t sensor)
|
||||
{
|
||||
return _dpis[sensor];
|
||||
}
|
53
src/logid/features/DPI.h
Normal file
53
src/logid/features/DPI.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_FEATURE_DPI_H
|
||||
#define LOGID_FEATURE_DPI_H
|
||||
|
||||
#include "../backend/hidpp20/features/AdjustableDPI.h"
|
||||
#include "DeviceFeature.h"
|
||||
|
||||
namespace logid {
|
||||
namespace features
|
||||
{
|
||||
class DPI : public DeviceFeature
|
||||
{
|
||||
public:
|
||||
explicit DPI(Device* dev);
|
||||
virtual void configure();
|
||||
virtual void listen();
|
||||
|
||||
uint16_t getDPI(uint8_t sensor=0);
|
||||
void setDPI(uint16_t dpi, uint8_t sensor=0);
|
||||
|
||||
class Config : public DeviceFeature::Config
|
||||
{
|
||||
public:
|
||||
explicit Config(Device* dev);
|
||||
uint16_t getDPI(uint8_t sensor);
|
||||
uint8_t getSensorCount();
|
||||
protected:
|
||||
std::vector<uint16_t> _dpis;
|
||||
};
|
||||
private:
|
||||
Config _config;
|
||||
backend::hidpp20::AdjustableDPI _adjustable_dpi;
|
||||
std::vector<backend::hidpp20::AdjustableDPI::SensorDPIList> _dpi_lists;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_FEATURE_DPI_H
|
51
src/logid/features/DeviceFeature.h
Normal file
51
src/logid/features/DeviceFeature.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2019-2020 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOGID_FEATURES_DEVICEFEATURE_H
|
||||
#define LOGID_FEATURES_DEVICEFEATURE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace logid {
|
||||
class Device;
|
||||
namespace features
|
||||
{
|
||||
class DeviceFeature
|
||||
{
|
||||
public:
|
||||
explicit DeviceFeature(Device* dev) : _device (dev)
|
||||
{
|
||||
}
|
||||
virtual void configure() = 0;
|
||||
virtual void listen() = 0;
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
explicit Config(Device* dev) : _device (dev)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
Device* _device;
|
||||
};
|
||||
|
||||
protected:
|
||||
Device* _device;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_FEATURES_DEVICEFEATURE_H
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user