-
-
Notifications
You must be signed in to change notification settings - Fork 272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Action type: "Axis" support for mapping buttons press to axis changes #243
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
/* | ||
* 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 "AxisAction.h" | ||
#include "../util/log.h" | ||
#include "../InputDevice.h" | ||
#include "../backend/hidpp20/features/ReprogControls.h" | ||
#include "../util/task.h" | ||
#include "../util/workqueue.h" | ||
|
||
using namespace logid::actions; | ||
using namespace logid::backend; | ||
|
||
AxisAction::AxisAction(Device *device, libconfig::Setting& config) : | ||
Action(device), _config (device, config) | ||
{ | ||
_config.repeatMutex().lock(); | ||
std::shared_ptr<task> repeatTask = std::make_shared<task>([this]() { | ||
int axis = _config.axis(); | ||
int move = _config.move(); | ||
uint rate = _config.rate(); | ||
logPrintf(DEBUG, "Started repeat task for axis %d with move %d at rate %d", | ||
axis, move, rate); | ||
int low_res_axis = InputDevice::getLowResAxis(axis); | ||
int lowres_movement = move / _config.hiResMoveMultiplier(); | ||
int hires_remainder = move % _config.hiResMoveMultiplier(); | ||
while (true) { | ||
_config.repeatMutex().lock(); | ||
if(low_res_axis != -1) { | ||
// logPrintf(DEBUG, "Moving axis %d to %d:%d", axis, lowres_movement, hires_remainder); | ||
virtual_input->moveAxis(low_res_axis, lowres_movement); | ||
virtual_input->moveAxis(axis, hires_remainder); | ||
} else { | ||
// logPrintf(DEBUG, "Moving axis %d to %d", axis, move); | ||
virtual_input->moveAxis(axis, move); | ||
} | ||
_config.repeatMutex().unlock(); | ||
std::this_thread::sleep_for(std::chrono::milliseconds(rate)); | ||
} | ||
}, [this](std::exception& e){ | ||
logPrintf(ERROR, "Error during repeat: %s", | ||
e.what()); | ||
}); | ||
global_workqueue->queue(repeatTask); | ||
repeatTask->waitStart(); | ||
} | ||
|
||
void AxisAction::press() | ||
{ | ||
_pressed = true; | ||
_config.repeatMutex().unlock(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A |
||
|
||
} | ||
|
||
void AxisAction::release() | ||
{ | ||
_pressed = false; | ||
_config.repeatMutex().lock(); | ||
} | ||
|
||
uint8_t AxisAction::reprogFlags() const | ||
{ | ||
return hidpp20::ReprogControls::TemporaryDiverted; | ||
} | ||
|
||
AxisAction::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& axis = config["axis"]; | ||
if(axis.isNumber()) { | ||
_axis = axis; | ||
virtual_input->registerAxis(axis); | ||
registerLowResAxis(axis); | ||
if (registerLowResAxis(axis)) { | ||
_move *= _hiResMoveMultiplier; | ||
} | ||
} else if(axis.getType() == libconfig::Setting::TypeString) { | ||
try { | ||
_axis = virtual_input->toAxisCode(axis); | ||
virtual_input->registerAxis(_axis); | ||
if (registerLowResAxis(_axis)) { | ||
_move *= _hiResMoveMultiplier; | ||
} | ||
} catch(InputDevice::InvalidEventCode& e) { | ||
logPrintf(WARN, "Line %d: Invalid axis %s, skipping.", | ||
axis.getSourceLine(), axis.c_str()); | ||
return; | ||
} | ||
} else { | ||
logPrintf(WARN, "Line %d: axis must be string or int", | ||
axis.getSourceLine(), axis.c_str()); | ||
return; | ||
} | ||
} catch (libconfig::SettingNotFoundException& e) { | ||
logPrintf(WARN, "Line %d: axis is a required field, skipping.", | ||
config.getSourceLine()); | ||
} | ||
|
||
try { | ||
auto& move = config["move"]; | ||
if(move.isNumber()) { | ||
_move = move; | ||
} else { | ||
logPrintf(WARN, "Line %d: move must be int", | ||
move.getSourceLine(), move.c_str()); | ||
throw InvalidAction(); | ||
} | ||
} catch (libconfig::SettingNotFoundException& e) { | ||
logPrintf(WARN, "Line %d: move is not defined, using default value %d.", | ||
config.getSourceLine(), _move); | ||
} | ||
if (_move == 0) { | ||
logPrintf(WARN, "Line %d: move == 0 - this is pointless, but continue with it.", | ||
config.getSourceLine()); | ||
} | ||
|
||
|
||
try { | ||
auto& rate = config["rate"]; | ||
if(rate.isNumber()) { | ||
if ((int)rate > 0) { | ||
_rate = rate; | ||
} else { | ||
logPrintf(WARN, "Line %d: rate is invalid, using default value %ums.", | ||
config.getSourceLine(), _rate); | ||
} | ||
} else { | ||
logPrintf(WARN, "Line %d: rate must be int", | ||
rate.getSourceLine(), rate.c_str()); | ||
throw InvalidAction(); | ||
} | ||
} catch (libconfig::SettingNotFoundException& e) { | ||
logPrintf(WARN, "Line %d: rate is not defined, using default value %ums.", | ||
config.getSourceLine(), _rate); | ||
} | ||
logPrintf(DEBUG, "Axis: configured axis %d with move %d at rate %d", | ||
_axis, _move, _rate); | ||
} | ||
|
||
bool AxisAction::Config::registerLowResAxis(const uint axis_code) { | ||
bool registered = false; | ||
int low_res_axis = InputDevice::getLowResAxis(axis_code); | ||
if (low_res_axis != -1) { | ||
virtual_input->registerAxis(low_res_axis); | ||
registered = true; | ||
} | ||
return registered; | ||
} | ||
|
||
uint AxisAction::Config::axis() | ||
{ | ||
return _axis; | ||
} | ||
|
||
int AxisAction::Config::move() | ||
{ | ||
return _move; | ||
} | ||
|
||
uint AxisAction::Config::rate() | ||
{ | ||
return _rate; | ||
} | ||
|
||
int AxisAction::Config::hiResMoveMultiplier() | ||
{ | ||
return _hiResMoveMultiplier; | ||
} | ||
|
||
std::mutex& AxisAction::Config::repeatMutex() | ||
{ | ||
return _repeatMutex; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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_ACTION_AXIS_H | ||
#define LOGID_ACTION_AXIS_H | ||
|
||
#include <mutex> | ||
#include <vector> | ||
#include <libconfig.h++> | ||
#include "Action.h" | ||
|
||
namespace logid { | ||
namespace actions { | ||
class AxisAction : public Action | ||
{ | ||
public: | ||
AxisAction(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); | ||
uint axis(); | ||
int move(); | ||
uint rate(); | ||
int hiResMoveMultiplier(); | ||
std::mutex& repeatMutex(); | ||
protected: | ||
bool registerLowResAxis(const uint axis_code); | ||
uint _axis; | ||
int _move = 1; | ||
uint _rate = 100; | ||
int _hiResMoveMultiplier = 120; | ||
std::mutex _repeatMutex; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't have the repeat mutex in the config class. Regardless, in the solution I described, it is not needed, so this can be removed. |
||
}; | ||
protected: | ||
Config _config; | ||
}; | ||
}} | ||
|
||
#endif //LOGID_ACTION_KEYPRESS_H |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code basically reserves an entire worker to the axis action regardless of the button state,
which is not ideal. We should instead:
condition_variable::wait_until
instead of sleepingThat way, we are only using a worker when necessary.