Implement RemapButton feature
Many changes were made here, too many to list here.
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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 <sstream>
|
||||
#include "../Device.h"
|
||||
#include "RemapButton.h"
|
||||
#include "../backend/hidpp20/Error.h"
|
||||
|
||||
using namespace logid::features;
|
||||
using namespace logid::backend;
|
||||
using namespace logid::actions;
|
||||
|
||||
#define HIDPP20_REPROG_REBIND (hidpp20::ReprogControls::ChangeTemporaryDivert \
|
||||
| hidpp20::ReprogControls::ChangeRawXYDivert)
|
||||
|
||||
#define EVENTHANDLER_NAME "REMAP_BUTTON"
|
||||
|
||||
RemapButton::RemapButton(Device *dev): DeviceFeature(dev), _config (dev),
|
||||
_reprog_controls (hidpp20::ReprogControls::autoVersion(&dev->hidpp20()))
|
||||
{
|
||||
}
|
||||
|
||||
RemapButton::~RemapButton()
|
||||
{
|
||||
_device->hidpp20().removeEventHandler(EVENTHANDLER_NAME);
|
||||
}
|
||||
|
||||
void RemapButton::configure()
|
||||
{
|
||||
///TODO: DJ reporting trickery if cannot be remapped
|
||||
for(auto& i : _config.buttons()) {
|
||||
hidpp20::ReprogControls::ControlInfo info{};
|
||||
try {
|
||||
info = _reprog_controls->getControlIdInfo(i.first);
|
||||
} catch(hidpp20::Error& e) {
|
||||
if(e.code() == hidpp20::Error::InvalidArgument) {
|
||||
logPrintf(WARN, "%s: CID 0x%02x does not exist.",
|
||||
_device->name().c_str(), i.first);
|
||||
continue;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
if((i.second->reprogFlags() & hidpp20::ReprogControls::RawXYDiverted) &&
|
||||
(!_reprog_controls->supportsRawXY() || (info.additionalFlags &
|
||||
hidpp20::ReprogControls::RawXY)))
|
||||
logPrintf(WARN, "%s: Cannot divert raw XY movements for CID "
|
||||
"0x%02x", _device->name().c_str(), i.first);
|
||||
|
||||
hidpp20::ReprogControls::ControlInfo report{};
|
||||
report.controlID = i.first;
|
||||
report.flags = HIDPP20_REPROG_REBIND;
|
||||
report.flags |= i.second->reprogFlags();
|
||||
_reprog_controls->setControlReporting(i.first, report);
|
||||
}
|
||||
}
|
||||
|
||||
void RemapButton::listen()
|
||||
{
|
||||
if(_device->hidpp20().eventHandlers().find(EVENTHANDLER_NAME) ==
|
||||
_device->hidpp20().eventHandlers().end()) {
|
||||
auto handler = std::make_shared<hidpp::EventHandler>();
|
||||
handler->condition = [index=_reprog_controls->featureIndex()]
|
||||
(hidpp::Report& report)->bool {
|
||||
return (report.feature() == index) && ((report.function() ==
|
||||
hidpp20::ReprogControls::DivertedButtonEvent) || (report
|
||||
.function() == hidpp20::ReprogControls::DivertedRawXYEvent));
|
||||
};
|
||||
|
||||
handler->callback = [this](hidpp::Report& report)->void {
|
||||
if(report.function() ==
|
||||
hidpp20::ReprogControls::DivertedButtonEvent)
|
||||
this->_buttonEvent(_reprog_controls->divertedButtonEvent(
|
||||
report));
|
||||
else { // RawXY
|
||||
auto divertedXY = _reprog_controls->divertedRawXYEvent(report);
|
||||
for(auto& button : this->_config.buttons())
|
||||
if(button.second->pressed())
|
||||
button.second->move(divertedXY.x, divertedXY.y);
|
||||
}
|
||||
};
|
||||
|
||||
_device->hidpp20().addEventHandler(EVENTHANDLER_NAME, handler);
|
||||
};
|
||||
}
|
||||
|
||||
void RemapButton::_buttonEvent(std::set<uint16_t> new_state)
|
||||
{
|
||||
// Ensure I/O doesn't occur while updating button state
|
||||
std::lock_guard<std::mutex> lock(_button_lock);
|
||||
|
||||
// Press all added buttons
|
||||
for(auto& i : new_state) {
|
||||
auto old_i = _pressed_buttons.find(i);
|
||||
if(old_i != _pressed_buttons.end()) {
|
||||
_pressed_buttons.erase(old_i);
|
||||
} else {
|
||||
auto action = _config.buttons().find(i);
|
||||
if(action != _config.buttons().end())
|
||||
action->second->press();
|
||||
}
|
||||
}
|
||||
|
||||
// Release all removed buttons
|
||||
for(auto& i : _pressed_buttons) {
|
||||
auto action = _config.buttons().find(i);
|
||||
if(action != _config.buttons().end())
|
||||
action->second->release();
|
||||
}
|
||||
|
||||
_pressed_buttons = new_state;
|
||||
}
|
||||
|
||||
RemapButton::Config::Config(Device *dev) : DeviceFeature::Config(dev)
|
||||
{
|
||||
try {
|
||||
auto& config_root = dev->config().getSetting("buttons");
|
||||
if(!config_root.isList()) {
|
||||
logPrintf(WARN, "Line %d: buttons must be a list.",
|
||||
config_root.getSourceLine());
|
||||
return;
|
||||
}
|
||||
int button_count = config_root.getLength();
|
||||
for(int i = 0; i < button_count; i++)
|
||||
_parseButton(config_root[i]);
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// buttons not configured, use default
|
||||
}
|
||||
}
|
||||
|
||||
void RemapButton::Config::_parseButton(libconfig::Setting &setting)
|
||||
{
|
||||
if(!setting.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: button must be an object, ignoring.",
|
||||
setting.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t cid;
|
||||
try {
|
||||
auto& cid_setting = setting.lookup("cid");
|
||||
if(!cid_setting.isNumber()) {
|
||||
logPrintf(WARN, "Line %d: cid must be a number, ignoring.",
|
||||
cid_setting.getSourceLine());
|
||||
return;
|
||||
}
|
||||
cid = (int)cid_setting;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: cid is required, ignoring.",
|
||||
setting.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_buttons.emplace(cid, Action::makeAction(_device,
|
||||
setting.lookup("action")));
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: action is required, ignoring.",
|
||||
setting.getSourceLine());
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<uint8_t, std::shared_ptr<Action>>& RemapButton::Config::buttons()
|
||||
{
|
||||
return _buttons;
|
||||
}
|
||||
Reference in New Issue
Block a user