You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
6.3 KiB
184 lines
6.3 KiB
/* |
|
* 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())) |
|
{ |
|
_reprog_controls->initCidMap(); |
|
} |
|
|
|
RemapButton::~RemapButton() |
|
{ |
|
_device->hidpp20().removeEventHandler(EVENTHANDLER_NAME); |
|
} |
|
|
|
void RemapButton::configure() |
|
{ |
|
///TODO: DJ reporting trickery if cannot be remapped |
|
for(const 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(const 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(const 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(const 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()); |
|
} catch(InvalidAction& e) { |
|
logPrintf(WARN, "Line %d: %s is not a valid action, ignoring.", |
|
setting["action"].getSourceLine(), e.what()); |
|
} |
|
} |
|
|
|
const std::map<uint8_t, std::shared_ptr<Action>>& RemapButton::Config::buttons() |
|
{ |
|
return _buttons; |
|
} |