Major refactoring, transferring from masterd
This commit is contained in:
parent
2190b527a7
commit
9c092edcf3
14
CMakeLists.txt
Normal file
14
CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(logiops)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -Wall")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
add_subdirectory(src/logid)
|
||||||
|
|
||||||
|
if(NOT EXISTS "${PROJECT_BINARY_DIR}/logid.cfg")
|
||||||
|
configure_file("logid.example.cfg" "logid.cfg" COPYONLY)
|
||||||
|
endif()
|
32
README.md
Normal file
32
README.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# logiops
|
||||||
|
|
||||||
|
This is an unofficial driver for Logitech mice and keyboard.
|
||||||
|
|
||||||
|
This is currently only compatible with HID++ \>2.0 devices.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
This project requires a C++14 compiler, cmake, libevdev, libconfig, and [libhidpp](https://github.com/cvuchener/hidpp)
|
||||||
|
|
||||||
|
To build this project, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
Installation is currently not implemented.
|
||||||
|
|
||||||
|
## Compatible Devices
|
||||||
|
|
||||||
|
| Device | Compatible? |
|
||||||
|
|:---------:|:-----------:|
|
||||||
|
| MX Master | Yes |
|
||||||
|
| T400 | Untested |
|
||||||
|
| K400r | Untested |
|
||||||
|
| K350 | Untested |
|
||||||
|
| M325c | Untested |
|
||||||
|
|
||||||
|
I own the MX Master, T400, K400r, K350, and M325c. Feel free to add to this list if you can test any additional devices.
|
71
logid.example.cfg
Normal file
71
logid.example.cfg
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
devices: (
|
||||||
|
{
|
||||||
|
name: "MX Master";
|
||||||
|
smartshift:
|
||||||
|
{
|
||||||
|
on: true;
|
||||||
|
threshold: 30;
|
||||||
|
};
|
||||||
|
hiresscroll: true;
|
||||||
|
dpi: 1000;
|
||||||
|
|
||||||
|
buttons: (
|
||||||
|
{
|
||||||
|
cid: 0xc3;
|
||||||
|
action =
|
||||||
|
{
|
||||||
|
type: "Gestures";
|
||||||
|
gestures: (
|
||||||
|
{
|
||||||
|
direction: "Up";
|
||||||
|
mode: "OnRelease";
|
||||||
|
action =
|
||||||
|
{
|
||||||
|
type: "Keypress";
|
||||||
|
keys: ["KEY_UP"];
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
direction: "Down";
|
||||||
|
mode: "OnRelease";
|
||||||
|
action =
|
||||||
|
{
|
||||||
|
type: "Keypress";
|
||||||
|
keys: ["KEY_DOWN"];
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
direction: "Left";
|
||||||
|
mode: "OnRelease";
|
||||||
|
action =
|
||||||
|
{
|
||||||
|
type: "CycleDPI";
|
||||||
|
dpis: [400, 600, 800, 1000, 1200, 1400, 1600];
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
direction: "Right";
|
||||||
|
mode: "OnRelease";
|
||||||
|
action =
|
||||||
|
{
|
||||||
|
type = "ToggleSmartshift";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
direction: "None"
|
||||||
|
mode: "NoPress"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cid: 0xc4;
|
||||||
|
action =
|
||||||
|
{
|
||||||
|
type: "Keypress";
|
||||||
|
keys: ["KEY_A"];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
154
src/logid/Actions.cpp
Normal file
154
src/logid/Actions.cpp
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#include <hidpp20/Error.h>
|
||||||
|
#include <hidpp/SimpleDispatcher.h>
|
||||||
|
#include <hidpp20/IAdjustableDPI.h>
|
||||||
|
|
||||||
|
#include "Actions.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "EvdevDevice.h"
|
||||||
|
|
||||||
|
KeyAction::KeyAction(const KeyAction &a, Device* d) : ButtonAction(Action::Keypress)
|
||||||
|
{
|
||||||
|
device = d;
|
||||||
|
std::copy(a.keys.begin(), a.keys.end(), std::back_inserter(keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyAction::press()
|
||||||
|
{
|
||||||
|
//KeyPress event for each in keys
|
||||||
|
for(unsigned int i : keys)
|
||||||
|
global_evdev->send_event(EV_KEY, i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyAction::release()
|
||||||
|
{
|
||||||
|
//KeyRelease event for each in keys
|
||||||
|
for(unsigned int i : keys)
|
||||||
|
global_evdev->send_event(EV_KEY, i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureAction::press()
|
||||||
|
{
|
||||||
|
held = true;
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureAction::move(HIDPP20::IReprogControlsV4::Move m)
|
||||||
|
{
|
||||||
|
x += m.x;
|
||||||
|
y += m.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureAction::release()
|
||||||
|
{
|
||||||
|
held = false;
|
||||||
|
auto direction = get_direction(x, y);
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
if(device->features.find(0x2110) == device->features.end())
|
||||||
|
{
|
||||||
|
log_printf(DEBUG, "Error toggling smart shift, feature is non-existent.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uint8_t f_index = device->features.find(0x2110)->second;
|
||||||
|
std::vector<uint8_t> results;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
results = device->hidpp_dev->callFunction(f_index, 0x00);
|
||||||
|
if(results[0] == 0x02)
|
||||||
|
results = device->hidpp_dev->callFunction(f_index, 0x01, {0x01});
|
||||||
|
else if(results[0] == 0x01)
|
||||||
|
results = device->hidpp_dev->callFunction(f_index, 0x01, {0x02});
|
||||||
|
else
|
||||||
|
results = device->hidpp_dev->callFunction(f_index, 0x01, {0x01});
|
||||||
|
}
|
||||||
|
catch(HIDPP20::Error &e)
|
||||||
|
{
|
||||||
|
log_printf(ERROR, "Error toggling smart shift, code %d: %s\n", e.errorCode(), e.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HiresScrollAction::press()
|
||||||
|
{
|
||||||
|
if(device->features.find(0x2110) == device->features.end())
|
||||||
|
{
|
||||||
|
log_printf(DEBUG, "Error toggling hires scroll, feature is non-existent.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uint8_t f_index = device->features.find(0x2121)->second;
|
||||||
|
std::vector<uint8_t> results;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
results = device->hidpp_dev->callFunction(f_index, 0x01);
|
||||||
|
if(results[0] == 0x02)
|
||||||
|
results = device->hidpp_dev->callFunction(f_index, 0x02, {0x00});
|
||||||
|
else
|
||||||
|
results = device->hidpp_dev->callFunction(f_index, 0x02, {0x02});
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
122
src/logid/Actions.h
Normal file
122
src/logid/Actions.h
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#ifndef LOGIOPS_ACTIONS_H
|
||||||
|
#define LOGIOPS_ACTIONS_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <hidpp20/IReprogControlsV4.h>
|
||||||
|
#include "Device.h"
|
||||||
|
|
||||||
|
enum class Action
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Keypress,
|
||||||
|
Gestures,
|
||||||
|
CycleDPI,
|
||||||
|
ChangeDPI,
|
||||||
|
ToggleSmartshift,
|
||||||
|
ToggleHiresScroll
|
||||||
|
};
|
||||||
|
enum class Direction
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right
|
||||||
|
};
|
||||||
|
enum class GestureMode
|
||||||
|
{
|
||||||
|
NoPress,
|
||||||
|
OnRelease,
|
||||||
|
OnFewPixels
|
||||||
|
};
|
||||||
|
|
||||||
|
class Device;
|
||||||
|
|
||||||
|
class ButtonAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Action type;
|
||||||
|
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 void press() {}
|
||||||
|
virtual void release() {}
|
||||||
|
};
|
||||||
|
class KeyAction : public ButtonAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit KeyAction(std::vector<unsigned int> k) : ButtonAction(Action::Keypress), keys (std::move(k)) {};
|
||||||
|
KeyAction(const KeyAction &a, Device* d);
|
||||||
|
//virtual KeyAction* create_instance(Device* d) { return new KeyAction(*this, d); };
|
||||||
|
virtual void press();
|
||||||
|
virtual void release();
|
||||||
|
private:
|
||||||
|
std::vector<unsigned int> keys;
|
||||||
|
};
|
||||||
|
class Gesture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Gesture(ButtonAction* ba, GestureMode m, int pp=0) : action (ba), mode (m), per_pixel (pp) {};
|
||||||
|
Gesture(const Gesture &g) : action (g.action), mode (g.mode), per_pixel (g.per_pixel) {};
|
||||||
|
|
||||||
|
ButtonAction* action;
|
||||||
|
|
||||||
|
GestureMode mode;
|
||||||
|
int per_pixel;
|
||||||
|
};
|
||||||
|
class GestureAction : public ButtonAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GestureAction(std::map<Direction, Gesture*> g) : ButtonAction(Action::Gestures), gestures (std::move(g)) {};
|
||||||
|
std::map<Direction, Gesture*> gestures;
|
||||||
|
virtual void press();
|
||||||
|
void move(HIDPP20::IReprogControlsV4::Move m);
|
||||||
|
virtual void release();
|
||||||
|
private:
|
||||||
|
bool held;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
class SmartshiftAction : public ButtonAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SmartshiftAction() : ButtonAction(Action::ToggleSmartshift) {};
|
||||||
|
virtual void press();
|
||||||
|
virtual void release() {}
|
||||||
|
};
|
||||||
|
class HiresScrollAction : public ButtonAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HiresScrollAction() : ButtonAction(Action::ToggleHiresScroll) {};
|
||||||
|
virtual void press();
|
||||||
|
virtual void release() {}
|
||||||
|
};
|
||||||
|
class CycleDPIAction : public ButtonAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CycleDPIAction(std::vector<int> d) : ButtonAction(Action::CycleDPI), dpis (d) {};
|
||||||
|
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 void press();
|
||||||
|
virtual void release() {}
|
||||||
|
private:
|
||||||
|
int dpi_inc;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //LOGIOPS_ACTIONS_H
|
41
src/logid/CMakeLists.txt
Normal file
41
src/logid/CMakeLists.txt
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(logid)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../CMake")
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
|
add_executable(logid
|
||||||
|
logid.cpp
|
||||||
|
util.cpp
|
||||||
|
util.h
|
||||||
|
Configuration.cpp
|
||||||
|
Configuration.h
|
||||||
|
Actions.cpp
|
||||||
|
Actions.h
|
||||||
|
Device.cpp
|
||||||
|
Device.h
|
||||||
|
DeviceFinder.cpp
|
||||||
|
DeviceFinder.h
|
||||||
|
EvdevDevice.cpp
|
||||||
|
EvdevDevice.h)
|
||||||
|
set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
|
pkg_check_modules(PC_EVDEV libevdev)
|
||||||
|
pkg_check_modules(libhidpp REQUIRED)
|
||||||
|
pkg_check_modules(libconfig++ REQUIRED)
|
||||||
|
|
||||||
|
find_path(HIDPP_INCLUDE_DIR hidpp)
|
||||||
|
find_library(HIDPP_LIBRARY libhidpp.so)
|
||||||
|
|
||||||
|
find_path(EVDEV_INCLUDE_DIR libevdev/libevdev.h
|
||||||
|
HINTS ${PC_EVDEV_INCLUDE_DIRS} ${PC_EVDEV_INCLUDEDIR})
|
||||||
|
find_library(EVDEV_LIBRARY
|
||||||
|
NAMES evdev libevdev)
|
||||||
|
|
||||||
|
include_directories(${HIDPP_INCLUDE_DIR}/hidpp ${EVDEV_INCLUDE_DIR})
|
||||||
|
|
||||||
|
target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ ${HIDPP_LIBRARY})
|
386
src/logid/Configuration.cpp
Normal file
386
src/logid/Configuration.cpp
Normal file
|
@ -0,0 +1,386 @@
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
#include <libevdev/libevdev.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "Configuration.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
using namespace libconfig;
|
||||||
|
|
||||||
|
Configuration::Configuration(const char *config_file)
|
||||||
|
{
|
||||||
|
//Read config file
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cfg.readFile(config_file);
|
||||||
|
}
|
||||||
|
catch(const FileIOException &e)
|
||||||
|
{
|
||||||
|
log_printf(ERROR, "%s", "I/O Error while reading configuration file!");
|
||||||
|
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();
|
||||||
|
Setting* _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];
|
||||||
|
std::string name;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(!device.lookupValue("name", name))
|
||||||
|
{
|
||||||
|
log_printf(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());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
devices.insert({name, new DeviceConfig(device)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceConfig::DeviceConfig(const libconfig::Setting &root)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int d;
|
||||||
|
if(!root.lookupValue("dpi", d))
|
||||||
|
throw SettingTypeException(root["dpi"]);
|
||||||
|
dpi = new int(d);
|
||||||
|
}
|
||||||
|
catch(const SettingNotFoundException &e)
|
||||||
|
{
|
||||||
|
log_printf(INFO, "Missing dpi option, not setting.");
|
||||||
|
}
|
||||||
|
catch(const SettingTypeException &e)
|
||||||
|
{
|
||||||
|
log_printf(WARN, "Line %d: DPI must me an integer; not setting.", root["dpi"].getSourceLine());
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool b;
|
||||||
|
if(!root.lookupValue("hiresscroll", b))
|
||||||
|
throw SettingTypeException(root["hiresscroll"]);
|
||||||
|
hiresscroll = new bool(b);
|
||||||
|
}
|
||||||
|
catch(const SettingNotFoundException &e)
|
||||||
|
{
|
||||||
|
log_printf(INFO, "Missing hiresscroll option, not setting.");
|
||||||
|
}
|
||||||
|
catch(const SettingTypeException &e)
|
||||||
|
{
|
||||||
|
log_printf(WARN, "Line %d: DPI must me an integer; not setting.", root["hiresscroll"].getSourceLine());
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const Setting& ss = root["smartshift"];
|
||||||
|
smartshift = new smartshift_options;
|
||||||
|
bool on;
|
||||||
|
int threshold;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (ss.lookupValue("on", on)) smartshift->on = 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 0.");
|
||||||
|
}
|
||||||
|
if(threshold >= 100)
|
||||||
|
{
|
||||||
|
threshold = 99;
|
||||||
|
log_printf(INFO, "Smartshift threshold must be > 0 or < 100, setting to 99.");
|
||||||
|
}
|
||||||
|
smartshift->threshold = new uint8_t(threshold);
|
||||||
|
}
|
||||||
|
else log_printf(WARN, "Line %d: threshold must be an integer", ss["threshold"].getSourceLine());
|
||||||
|
}
|
||||||
|
catch(const SettingNotFoundException &e)
|
||||||
|
{
|
||||||
|
log_printf(INFO, "Missing threshold for smartshift, not setting.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const SettingNotFoundException &e) { }
|
||||||
|
catch(const SettingTypeException &e)
|
||||||
|
{
|
||||||
|
log_printf(WARN, "Line %d: smartshift field must be an object", root["hiressscroll"].getSourceLine());
|
||||||
|
}
|
||||||
|
|
||||||
|
Setting* buttons;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
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 = string_to_action(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* 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 = string_to_direction(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 = string_to_gesturemode(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = string_to_action(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;
|
||||||
|
gesture_config.lookupValue("pixels", pp);
|
||||||
|
gestures.insert({direction, new Gesture(ba, mode, pp)});
|
||||||
|
}
|
||||||
|
catch(SettingNotFoundException &e)
|
||||||
|
{
|
||||||
|
log_printf(WARN, "Line %d: OnFewPixels requires a 'pixels' field.", gesture_config.getSourceLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
dpi = nullptr;
|
||||||
|
hiresscroll = nullptr;
|
||||||
|
smartshift = nullptr;
|
||||||
|
actions = {};
|
||||||
|
}
|
41
src/logid/Configuration.h
Normal file
41
src/logid/Configuration.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef MASTEROPTIONS_CONFIGURATION_H
|
||||||
|
#define MASTEROPTIONS_CONFIGURATION_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <libconfig.h++>
|
||||||
|
|
||||||
|
struct smartshift_options
|
||||||
|
{
|
||||||
|
bool* on = nullptr;
|
||||||
|
uint8_t* threshold = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeviceConfig;
|
||||||
|
class ButtonAction;
|
||||||
|
enum class Action;
|
||||||
|
|
||||||
|
class DeviceConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeviceConfig();
|
||||||
|
DeviceConfig(const libconfig::Setting& root);
|
||||||
|
const int* dpi = nullptr;
|
||||||
|
struct smartshift_options* smartshift = nullptr;
|
||||||
|
const bool* hiresscroll = nullptr;
|
||||||
|
std::map<uint16_t, ButtonAction*> actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Configuration
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Configuration(const char* config_file);
|
||||||
|
std::map<std::string, DeviceConfig*> devices;
|
||||||
|
private:
|
||||||
|
libconfig::Config cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
ButtonAction* parse_action(Action action, const libconfig::Setting* action_config, bool is_gesture=false);
|
||||||
|
|
||||||
|
extern Configuration* global_config;
|
||||||
|
|
||||||
|
#endif //MASTEROPTIONS_CONFIGURATION_H
|
292
src/logid/Device.cpp
Normal file
292
src/logid/Device.cpp
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
#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/IReprogControlsV4.h>
|
||||||
|
#include <hidpp20/Device.h>
|
||||||
|
#include <hidpp10/Error.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "Device.h"
|
||||||
|
#include "Actions.h"
|
||||||
|
#include "Configuration.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "EvdevDevice.h"
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
Device::Device(std::string p, const HIDPP::DeviceIndex i) : path(p), index (i)
|
||||||
|
{
|
||||||
|
DeviceRemoved = false;
|
||||||
|
dispatcher = new HIDPP::SimpleDispatcher(path.c_str());
|
||||||
|
hidpp_dev = new HIDPP20::Device(dispatcher, index);
|
||||||
|
features = get_features();
|
||||||
|
|
||||||
|
if(global_config->devices.find(hidpp_dev->name()) == global_config->devices.end())
|
||||||
|
{
|
||||||
|
log_printf(INFO, "Device %s not configured, using default.", hidpp_dev->name().c_str());
|
||||||
|
config = new DeviceConfig();
|
||||||
|
}
|
||||||
|
else config = global_config->devices.find(hidpp_dev->name())->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::configure(bool scanning)
|
||||||
|
{
|
||||||
|
if(config->dpi != nullptr)
|
||||||
|
set_dpi(*config->dpi, scanning);
|
||||||
|
if(config->smartshift != nullptr)
|
||||||
|
set_smartshift(*config->smartshift, scanning);
|
||||||
|
if(config->hiresscroll != nullptr)
|
||||||
|
set_hiresscroll(*config->hiresscroll, scanning);
|
||||||
|
divert_buttons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::divert_buttons(bool scanning)
|
||||||
|
{
|
||||||
|
HIDPP20::IReprogControlsV4 irc4(hidpp_dev);
|
||||||
|
for(auto it = config->actions.begin(); it != config->actions.end(); ++it)
|
||||||
|
{
|
||||||
|
uint8_t flags = 0;
|
||||||
|
flags |= HIDPP20::IReprogControlsV4::ChangeTemporaryDivert;
|
||||||
|
flags |= HIDPP20::IReprogControlsV4::TemporaryDiverted;
|
||||||
|
if(it->second->type == Action::Gestures)
|
||||||
|
{
|
||||||
|
flags |= HIDPP20::IReprogControlsV4::ChangeRawXYDivert;
|
||||||
|
flags |= HIDPP20::IReprogControlsV4::RawXYDiverted;
|
||||||
|
}
|
||||||
|
it->first;
|
||||||
|
irc4.setControlReporting(it->first, flags, it->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::set_smartshift(smartshift_options ops, bool scanning)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> parameters;
|
||||||
|
parameters.push_back(ops.on == nullptr ? 0 : (*ops.on)? 2 : 1);
|
||||||
|
if(ops.threshold != nullptr)
|
||||||
|
{
|
||||||
|
parameters.push_back(*ops.threshold);
|
||||||
|
parameters.push_back(*ops.threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(features.find(0x2110) == features.end())
|
||||||
|
{
|
||||||
|
log_printf(DEBUG, "Error toggling smart shift, feature is non-existent.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uint8_t f_index = features.find(0x2110)->second;
|
||||||
|
|
||||||
|
try { hidpp_dev->callFunction(f_index, 0x01, parameters); }
|
||||||
|
catch (HIDPP20::Error &e)
|
||||||
|
{
|
||||||
|
if(scanning)
|
||||||
|
throw e;
|
||||||
|
log_printf(ERROR, "Error setting smartshift options, code %d: %s\n", e.errorCode(), e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::set_hiresscroll(bool b, bool scanning)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::set_dpi(int dpi, bool scanning)
|
||||||
|
{
|
||||||
|
HIDPP20::IAdjustableDPI iad(hidpp_dev);
|
||||||
|
|
||||||
|
try { for(int i = 0; i < iad.getSensorCount(); i++) iad.setSensorDPI(i, dpi); }
|
||||||
|
catch (HIDPP20::Error &e)
|
||||||
|
{
|
||||||
|
if(scanning)
|
||||||
|
throw e;
|
||||||
|
log_printf(ERROR, "Error while setting DPI: %s", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::start()
|
||||||
|
{
|
||||||
|
configure();
|
||||||
|
|
||||||
|
auto *d = new HIDPP::SimpleDispatcher(path.c_str());
|
||||||
|
listener = new SimpleListener(d, index);
|
||||||
|
listener->addEventHandler( std::make_unique<ButtonHandler>(hidpp_dev, this) );
|
||||||
|
auto listener_thread = std::thread { [=]() { listener->start(); } };
|
||||||
|
listener_thread.detach();
|
||||||
|
while(!DeviceRemoved)
|
||||||
|
{
|
||||||
|
std::mutex m;
|
||||||
|
std::condition_variable cv;
|
||||||
|
std::vector<uint8_t> results;
|
||||||
|
|
||||||
|
std::thread t([&cv, &results, dev=hidpp_dev, removed=&DeviceRemoved]()
|
||||||
|
{
|
||||||
|
try { results = dev->callFunction(0x00, 0x00); }
|
||||||
|
catch(HIDPP10::Error &e) { usleep(500000); }
|
||||||
|
catch(std::system_error &e)
|
||||||
|
{
|
||||||
|
cv.notify_one();
|
||||||
|
if(*removed) printf("REMOVED!\n");
|
||||||
|
*removed = true;
|
||||||
|
}
|
||||||
|
cv.notify_one();
|
||||||
|
});
|
||||||
|
t.detach();
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> l(m);
|
||||||
|
if(cv.wait_for(l, 500ms) == std::cv_status::timeout)
|
||||||
|
{
|
||||||
|
while(!DeviceRemoved)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
configure(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch(std::exception &e) {} // Retry infinitely on failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usleep(200000);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener->stop();
|
||||||
|
listener_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::ButtonHandler::handleEvent (const HIDPP::Report &event)
|
||||||
|
{
|
||||||
|
switch (event.function())
|
||||||
|
{
|
||||||
|
case HIDPP20::IReprogControlsV4::Event::DivertedButtonEvent:
|
||||||
|
{
|
||||||
|
new_states = HIDPP20::IReprogControlsV4::divertedButtonEvent(event);
|
||||||
|
if (states.empty())
|
||||||
|
{
|
||||||
|
for (uint16_t i : new_states)
|
||||||
|
std::thread{[=]() { dev->press_button(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(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->press_button(i); }}.detach();
|
||||||
|
} else
|
||||||
|
std::thread{[=]() { dev->release_button(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->move_diverted(i, raw_xy); }}.detach();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::EventListener::removeEventHandlers ()
|
||||||
|
{
|
||||||
|
for (const auto &p: iterators)
|
||||||
|
dispatcher->unregisterEventHandler(p.second);
|
||||||
|
handlers.clear();
|
||||||
|
iterators.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::EventListener::~EventListener()
|
||||||
|
{
|
||||||
|
removeEventHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::EventListener::addEventHandler(std::unique_ptr<EventHandler> &&handler)
|
||||||
|
{
|
||||||
|
uint8_t feature = handler->feature()->index();
|
||||||
|
EventHandler *ptr = handler.get();
|
||||||
|
handlers.emplace(feature, std::move(handler));
|
||||||
|
dispatcher->registerEventHandler(index, feature, [ptr](const HIDPP::Report &report)
|
||||||
|
{
|
||||||
|
ptr->handleEvent(report);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::SimpleListener::start()
|
||||||
|
{
|
||||||
|
try { dispatcher->listen(); }
|
||||||
|
catch(std::system_error &e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::SimpleListener::stop()
|
||||||
|
{
|
||||||
|
dispatcher->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Device::SimpleListener::event (EventHandler *handler, const HIDPP::Report &report)
|
||||||
|
{
|
||||||
|
handler->handleEvent (report);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::stop()
|
||||||
|
{
|
||||||
|
DeviceRemoved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::press_button(uint16_t cid)
|
||||||
|
{
|
||||||
|
if(config->actions.find(cid) == config->actions.end())
|
||||||
|
{
|
||||||
|
log_printf(WARN, "0x%x was pressed but no action was found.", cid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config->actions.find(cid)->second->press();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::release_button(uint16_t cid)
|
||||||
|
{
|
||||||
|
if(config->actions.find(cid) == config->actions.end())
|
||||||
|
{
|
||||||
|
log_printf(WARN, "0x%x was released but no action was found.", cid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config->actions.find(cid)->second->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::move_diverted(uint16_t cid, HIDPP20::IReprogControlsV4::Move m)
|
||||||
|
{
|
||||||
|
auto action = config->actions.find(cid)->second;
|
||||||
|
switch(action->type)
|
||||||
|
{
|
||||||
|
case Action::Gestures:
|
||||||
|
((GestureAction*)action)->move(m);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<uint16_t, uint8_t> Device::get_features()
|
||||||
|
{
|
||||||
|
std::map<uint16_t, uint8_t> features;
|
||||||
|
|
||||||
|
HIDPP20::IFeatureSet ifs (hidpp_dev);
|
||||||
|
|
||||||
|
unsigned int feature_count = ifs.getCount();
|
||||||
|
|
||||||
|
for(unsigned int i = 1; i <= feature_count; i++)
|
||||||
|
features.insert({ifs.getFeatureID(i), i});
|
||||||
|
|
||||||
|
return features;
|
||||||
|
}
|
109
src/logid/Device.h
Normal file
109
src/logid/Device.h
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
#ifndef LOGIOPS_DEVICE_H
|
||||||
|
#define LOGIOPS_DEVICE_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <hidpp/Dispatcher.h>
|
||||||
|
#include <hidpp/SimpleDispatcher.h>
|
||||||
|
|
||||||
|
#include "Actions.h"
|
||||||
|
#include "Configuration.h"
|
||||||
|
|
||||||
|
class Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Device(std::string p, const HIDPP::DeviceIndex i);
|
||||||
|
|
||||||
|
void configure(bool scanning=false);
|
||||||
|
|
||||||
|
void press_button(uint16_t cid);
|
||||||
|
void release_button(uint16_t cid);
|
||||||
|
void move_diverted(uint16_t cid, HIDPP20::IReprogControlsV4::Move move);
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
std::map<uint16_t, uint8_t> get_features();
|
||||||
|
|
||||||
|
std::map<uint16_t, uint8_t> features;
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
const HIDPP::DeviceIndex index;
|
||||||
|
HIDPP::Dispatcher* dispatcher;
|
||||||
|
HIDPP20::Device* hidpp_dev;
|
||||||
|
|
||||||
|
class EventHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const HIDPP20::FeatureInterface *feature() const = 0;
|
||||||
|
virtual void handleEvent (const HIDPP::Report &event) = 0;
|
||||||
|
};
|
||||||
|
class ButtonHandler : public EventHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ButtonHandler (HIDPP20::Device *hidppdev, Device *d):
|
||||||
|
_irc4 (hidppdev),
|
||||||
|
dev (d),
|
||||||
|
states (0) {}
|
||||||
|
const HIDPP20::FeatureInterface *feature () const
|
||||||
|
{
|
||||||
|
return &_irc4;
|
||||||
|
}
|
||||||
|
void handleEvent (const HIDPP::Report &event);
|
||||||
|
protected:
|
||||||
|
HIDPP20::IReprogControlsV4 _irc4;
|
||||||
|
Device* dev;
|
||||||
|
std::vector<uint16_t> states;
|
||||||
|
std::vector<uint16_t> new_states;
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void start();
|
||||||
|
virtual void stop();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool event (EventHandler* handler, const HIDPP::Report &report);
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DeviceConfig* config;
|
||||||
|
bool DeviceRemoved;
|
||||||
|
EventListener* listener;
|
||||||
|
|
||||||
|
void divert_buttons(bool scanning=false);
|
||||||
|
void set_smartshift(struct smartshift_options ops, bool scanning=false);
|
||||||
|
void set_hiresscroll(bool b, bool scanning=false);
|
||||||
|
void set_dpi(int dpi, bool scanning=false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //LOGIOPS_DEVICE_H
|
143
src/logid/DeviceFinder.cpp
Normal file
143
src/logid/DeviceFinder.cpp
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
#include <hid/DeviceMonitor.h>
|
||||||
|
#include <hidpp/SimpleDispatcher.h>
|
||||||
|
#include <hidpp/Device.h>
|
||||||
|
#include <hidpp10/Error.h>
|
||||||
|
#include <hidpp20/Error.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <future>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "DeviceFinder.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "Device.h"
|
||||||
|
|
||||||
|
void find_device()
|
||||||
|
{
|
||||||
|
auto df = new DeviceFinder();
|
||||||
|
df->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceFinder::addDevice(const char *path)
|
||||||
|
{
|
||||||
|
const int max_tries = 5;
|
||||||
|
const int try_delay = 50000;
|
||||||
|
std::string string_path(path);
|
||||||
|
// Asynchronously scan device
|
||||||
|
std::thread{[=]()
|
||||||
|
{
|
||||||
|
// Check /sys/class/hidraw/hidrawX/device/uevent for device details.
|
||||||
|
// Continue if HIDRAW_NAME contains 'Logitech' or is non-existent/
|
||||||
|
// Otherwise, skip.
|
||||||
|
std::string hidraw_name;
|
||||||
|
std::size_t spos = string_path.rfind('/');
|
||||||
|
if(spos != std::string::npos) hidraw_name = string_path.substr(spos+1, string_path.size()-1);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_printf(ERROR, "Expected file but got directory: %s", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::ifstream info_file("/sys/class/hidraw/" + hidraw_name + "/device/uevent");
|
||||||
|
std::map<std::string, std::string> hidraw_info;
|
||||||
|
std::string line;
|
||||||
|
while(std::getline(info_file, line))
|
||||||
|
{
|
||||||
|
if(line.find('=') == std::string::npos) continue;
|
||||||
|
hidraw_info.insert({line.substr(0, line.find('=')), line.substr(line.find('=') + 1, line.size()-1)});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hidraw_info.find("HID_NAME") != hidraw_info.end())
|
||||||
|
if (hidraw_info.find("HID_NAME")->second.find("Logitech") == std::string::npos) return;
|
||||||
|
|
||||||
|
//Check if device is an HID++ device and handle it accordingly
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HIDPP::SimpleDispatcher dispatcher(string_path.c_str());
|
||||||
|
bool has_receiver_index = false;
|
||||||
|
for(HIDPP::DeviceIndex index: {
|
||||||
|
HIDPP::DefaultDevice, HIDPP::CordedDevice,
|
||||||
|
HIDPP::WirelessDevice1, HIDPP::WirelessDevice2,
|
||||||
|
HIDPP::WirelessDevice3, HIDPP::WirelessDevice4,
|
||||||
|
HIDPP::WirelessDevice5, HIDPP::WirelessDevice6})
|
||||||
|
{
|
||||||
|
/// TODO: CONTINUOUSLY SCAN ALL DEVICES ON RECEIVER
|
||||||
|
//Skip wireless devices if default device (receiver) has failed
|
||||||
|
if(!has_receiver_index && index == HIDPP::WirelessDevice1)
|
||||||
|
break;
|
||||||
|
for(int i = 0; i < max_tries; i++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HIDPP::Device d(&dispatcher, index);
|
||||||
|
auto version = d.protocolVersion();
|
||||||
|
if(index == HIDPP::DefaultDevice && version == std::make_tuple(1, 0))
|
||||||
|
has_receiver_index = true;
|
||||||
|
uint major, minor;
|
||||||
|
std::tie(major, minor) = d.protocolVersion();
|
||||||
|
if(major > 1) // HID++ 2.0 devices only
|
||||||
|
{
|
||||||
|
auto dev = new Device(string_path.c_str(), index);
|
||||||
|
handlers.insert({
|
||||||
|
dev, std::async(std::launch::async, &Device::start, *dev)
|
||||||
|
});
|
||||||
|
log_printf(INFO, "%s detected: device %d on %s", d.name().c_str(), index, string_path.c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch(HIDPP10::Error &e)
|
||||||
|
{
|
||||||
|
if(e.errorCode() != HIDPP10::Error::UnknownDevice)
|
||||||
|
{
|
||||||
|
if(i == max_tries - 1)
|
||||||
|
log_printf(WARN, "Error while querying %s, wireless device %d: %s", string_path.c_str(), index, e.what());
|
||||||
|
else usleep(try_delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(HIDPP20::Error &e)
|
||||||
|
{
|
||||||
|
if(e.errorCode() != HIDPP20::Error::UnknownDevice)
|
||||||
|
{
|
||||||
|
if(i == max_tries - 1)
|
||||||
|
log_printf(WARN, "Error while querying %s, device %d: %s", string_path.c_str(), index, e.what());
|
||||||
|
else usleep(try_delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(HIDPP::Dispatcher::TimeoutError &e)
|
||||||
|
{
|
||||||
|
if(i == max_tries - 1)
|
||||||
|
log_printf(WARN, "Device %s (index %d) timed out.", string_path.c_str(), index);
|
||||||
|
else usleep(try_delay);
|
||||||
|
}
|
||||||
|
catch(std::runtime_error &e)
|
||||||
|
{
|
||||||
|
if(i == max_tries - 1)
|
||||||
|
log_printf(WARN, "Runtime error on device %d on %s: %s", index, string_path.c_str(), e.what());
|
||||||
|
else usleep(try_delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// Iterate through Devices, stop all in path
|
||||||
|
auto it = handlers.begin();
|
||||||
|
while (it != handlers.end())
|
||||||
|
{
|
||||||
|
if(it->first->path == path)
|
||||||
|
{
|
||||||
|
log_printf(INFO, "%s on %s disconnected.", it->first->hidpp_dev->name(), path);
|
||||||
|
it->first->stop();
|
||||||
|
it->second.wait();
|
||||||
|
//handlers.erase(it);
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
18
src/logid/DeviceFinder.h
Normal file
18
src/logid/DeviceFinder.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef MASTEROPTIONS_DEVICEFINDER_H
|
||||||
|
#define MASTEROPTIONS_DEVICEFINDER_H
|
||||||
|
|
||||||
|
#include "Device.h"
|
||||||
|
|
||||||
|
struct handler_pair;
|
||||||
|
|
||||||
|
class DeviceFinder : public HID::DeviceMonitor
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void addDevice(const char* path);
|
||||||
|
void removeDevice(const char* path);
|
||||||
|
std::map<Device*, std::future<void>> handlers;
|
||||||
|
};
|
||||||
|
|
||||||
|
void find_device();
|
||||||
|
|
||||||
|
#endif //MASTEROPTIONS_DEVICEFINDER_H
|
35
src/logid/EvdevDevice.cpp
Normal file
35
src/logid/EvdevDevice.cpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#include <libevdev/libevdev.h>
|
||||||
|
#include <libevdev/libevdev-uinput.h>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#include "EvdevDevice.h"
|
||||||
|
|
||||||
|
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::send_event(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);
|
||||||
|
}
|
19
src/logid/EvdevDevice.h
Normal file
19
src/logid/EvdevDevice.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef MASTEROPTIONS_EVDEVDEVICE_H
|
||||||
|
#define MASTEROPTIONS_EVDEVDEVICE_H
|
||||||
|
|
||||||
|
#include <libevdev/libevdev.h>
|
||||||
|
#include <libevdev/libevdev-uinput.h>
|
||||||
|
|
||||||
|
class EvdevDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EvdevDevice(const char* name);
|
||||||
|
~EvdevDevice();
|
||||||
|
void send_event(unsigned int type, unsigned int code, int value);
|
||||||
|
libevdev* device;
|
||||||
|
libevdev_uinput* ui_device;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern EvdevDevice* global_evdev;
|
||||||
|
|
||||||
|
#endif //MASTEROPTIONS_EVDEVDEVICE_H
|
42
src/logid/logid.cpp
Normal file
42
src/logid/logid.cpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#include <hidpp/SimpleDispatcher.h>
|
||||||
|
#include <hidpp/DispatcherThread.h>
|
||||||
|
#include <hidpp20/Device.h>
|
||||||
|
#include <hidpp20/Error.h>
|
||||||
|
#include <hidpp20/IReprogControlsV4.h>
|
||||||
|
#include <hidpp20/UnsupportedFeature.h>
|
||||||
|
#include <hid/DeviceMonitor.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "Device.h"
|
||||||
|
#include "Actions.h"
|
||||||
|
#include "Configuration.h"
|
||||||
|
#include "EvdevDevice.h"
|
||||||
|
#include "DeviceFinder.h"
|
||||||
|
|
||||||
|
#define evdev_name "logid"
|
||||||
|
|
||||||
|
LogLevel global_verbosity = DEBUG;
|
||||||
|
Configuration* global_config;
|
||||||
|
EvdevDevice* global_evdev;
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
// Read config
|
||||||
|
try { global_config = new Configuration("logid.cfg"); }
|
||||||
|
catch (std::exception &e) { return EXIT_FAILURE; }
|
||||||
|
|
||||||
|
//Create an evdev device called 'logid'
|
||||||
|
try { global_evdev = new EvdevDevice(evdev_name); }
|
||||||
|
catch(std::system_error& e)
|
||||||
|
{
|
||||||
|
log_printf(ERROR, "Could not create evdev device: %s", e.what());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
find_device(); // Scan devices, create listeners, handlers, etc.
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
109
src/logid/util.cpp
Normal file
109
src/logid/util.cpp
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "Actions.h"
|
||||||
|
|
||||||
|
void log_printf(LogLevel level, const char* format, ...)
|
||||||
|
{
|
||||||
|
if(global_verbosity > level) return;
|
||||||
|
|
||||||
|
va_list vargs;
|
||||||
|
va_start(vargs, format);
|
||||||
|
|
||||||
|
FILE* stream = stdout;
|
||||||
|
if(level == ERROR || level == WARN) stream = stderr;
|
||||||
|
|
||||||
|
fprintf(stream, "[%s] ", level_prefix(level));
|
||||||
|
vfprintf(stream, format, vargs);
|
||||||
|
fprintf(stream, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* level_prefix(LogLevel level)
|
||||||
|
{
|
||||||
|
if(level == DEBUG) return "DEBUG";
|
||||||
|
if(level == INFO) return "INFO" ;
|
||||||
|
if(level == WARN) return "WARN";
|
||||||
|
if(level == ERROR) return "ERROR";
|
||||||
|
|
||||||
|
return "DEBUG";
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction get_direction(int x, int y)
|
||||||
|
{
|
||||||
|
if(abs(x) < 50 && abs(y) < 50) return Direction::None;
|
||||||
|
|
||||||
|
double angle;
|
||||||
|
|
||||||
|
if(x == 0 && y > 0) angle = 90; // Y+
|
||||||
|
else if(x == 0 && y < 0) angle = 270; // Y-
|
||||||
|
else if(x > 0 && y == 0) angle = 0; // X+
|
||||||
|
else if(x < 0 && y == 0) angle = 180; // X+
|
||||||
|
else
|
||||||
|
{
|
||||||
|
angle = fabs(atan((double)y/(double)x) * 180.0 / M_PI);
|
||||||
|
|
||||||
|
if(x < 0 && y > 0) angle = 180.0 - angle; //Q2
|
||||||
|
else if(x < 0 && y < 0) angle += 180; // Q3
|
||||||
|
else if(x > 0 && y < 0) angle = 360.0 - angle; // Q4
|
||||||
|
}
|
||||||
|
|
||||||
|
if(315 < angle || angle <= 45) return Direction::Right;
|
||||||
|
else if(45 < angle && angle <= 135) return Direction::Down;
|
||||||
|
else if(135 < angle && angle <= 225) return Direction::Left;
|
||||||
|
else if(225 < angle && angle <= 315) return Direction::Up;
|
||||||
|
|
||||||
|
return Direction::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction string_to_direction(std::string s)
|
||||||
|
{
|
||||||
|
const char* original_str = s.c_str();
|
||||||
|
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||||
|
|
||||||
|
if(s == "none") return Direction::None;
|
||||||
|
if(s == "up") return Direction::Up;
|
||||||
|
if(s == "down") return Direction::Down;
|
||||||
|
if(s == "left") return Direction::Left;
|
||||||
|
if(s == "right") return Direction::Right;
|
||||||
|
|
||||||
|
s = original_str;
|
||||||
|
|
||||||
|
throw std::invalid_argument(s + " is an invalid direction.");
|
||||||
|
}
|
||||||
|
|
||||||
|
GestureMode string_to_gesturemode(std::string s)
|
||||||
|
{
|
||||||
|
const char* original_str = s.c_str();
|
||||||
|
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||||
|
|
||||||
|
if(s == "nopress") return GestureMode::NoPress;
|
||||||
|
if(s == "onrelease") return GestureMode::OnRelease;
|
||||||
|
if(s == "onfewpixels") return GestureMode::OnFewPixels;
|
||||||
|
|
||||||
|
s = original_str;
|
||||||
|
|
||||||
|
log_printf(INFO, "%s is an invalid gesture mode. Defaulting to OnRelease", original_str);
|
||||||
|
|
||||||
|
return GestureMode::OnRelease;
|
||||||
|
}
|
||||||
|
|
||||||
|
Action string_to_action(std::string s)
|
||||||
|
{
|
||||||
|
std::string original_str = s;
|
||||||
|
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||||
|
|
||||||
|
if(s == "none") return Action::None;
|
||||||
|
if(s == "keypress") return Action::Keypress;
|
||||||
|
if(s == "gestures") return Action::Gestures;
|
||||||
|
if(s == "togglesmartshift") return Action::ToggleSmartshift;
|
||||||
|
if(s == "togglehiresscroll") return Action::ToggleHiresScroll;
|
||||||
|
if(s == "cycledpi") return Action::CycleDPI;
|
||||||
|
if(s == "changedpi") return Action::ChangeDPI;
|
||||||
|
|
||||||
|
throw std::invalid_argument(original_str + " is an invalid action.");
|
||||||
|
}
|
25
src/logid/util.h
Normal file
25
src/logid/util.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef MASTEROPTIONS_LOGGER_H
|
||||||
|
#define MASTEROPTIONS_LOGGER_H
|
||||||
|
|
||||||
|
#include "Actions.h"
|
||||||
|
|
||||||
|
enum LogLevel
|
||||||
|
{
|
||||||
|
DEBUG,
|
||||||
|
INFO,
|
||||||
|
WARN,
|
||||||
|
ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
extern LogLevel global_verbosity;
|
||||||
|
|
||||||
|
void log_printf(LogLevel level, const char* format, ...);
|
||||||
|
|
||||||
|
const char* level_prefix(LogLevel level);
|
||||||
|
|
||||||
|
Direction get_direction(int x, int y);
|
||||||
|
Direction string_to_direction(std::string s);
|
||||||
|
GestureMode string_to_gesturemode(std::string s);
|
||||||
|
Action string_to_action(std::string s);
|
||||||
|
|
||||||
|
#endif //MASTEROPTIONS_LOGGER_H
|
Loading…
Reference in New Issue
Block a user