183 lines
6.3 KiB
C++
183 lines
6.3 KiB
C++
/*
|
|
* 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());
|
|
} 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;
|
|
} |