diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt
index aa9570d..a1904c3 100644
--- a/src/logid/CMakeLists.txt
+++ b/src/logid/CMakeLists.txt
@@ -37,6 +37,7 @@ add_executable(logid
backend/hidpp20/features/Reset.cpp
backend/hidpp20/features/AdjustableDPI.cpp
backend/hidpp20/features/SmartShift.cpp
+ backend/hidpp20/features/ReprogControls.cpp
backend/dj/Report.cpp
util/mutex_queue.h
util/thread.cpp
diff --git a/src/logid/backend/hidpp20/features/ReprogControls.cpp b/src/logid/backend/hidpp20/features/ReprogControls.cpp
new file mode 100644
index 0000000..d9835bd
--- /dev/null
+++ b/src/logid/backend/hidpp20/features/ReprogControls.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 .
+ *
+ */
+#include
+#include "ReprogControls.h"
+
+using namespace logid::backend::hidpp20;
+
+#define DEFINE_REPROG(x, base) \
+x::x(Device* dev) : base(dev, ID) \
+{ \
+} \
+x::x(Device* dev, uint16_t _id) : base(dev, _id) \
+{ \
+}
+
+#define MAKE_REPROG(x, dev) \
+try { \
+ return x(dev); \
+} catch(UnsupportedFeature &e) {\
+}
+
+// Define all of the ReprogControls versions
+DEFINE_REPROG(ReprogControls, Feature);
+DEFINE_REPROG(ReprogControlsV2, ReprogControls);
+DEFINE_REPROG(ReprogControlsV2_2, ReprogControlsV2);
+DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2);
+DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3);
+
+ReprogControls ReprogControls::autoVersion(Device *dev)
+{
+ MAKE_REPROG(ReprogControlsV4, dev);
+ MAKE_REPROG(ReprogControlsV3, dev);
+ MAKE_REPROG(ReprogControlsV2_2, dev);
+ MAKE_REPROG(ReprogControlsV2, dev);
+
+ // If base version cannot be made, throw error
+ return ReprogControls(dev);
+}
+
+uint8_t ReprogControls::getControlCount()
+{
+ std::vector params(0);
+ auto response = callFunction(GetControlCount, params);
+ return response[0];
+}
+
+ReprogControls::ControlInfo ReprogControls::getControlInfo(uint8_t index)
+{
+ std::vector params(1);
+ ControlInfo info{};
+ params[0] = index;
+ auto response = callFunction(GetControlInfo, params);
+
+ info.controlID = response[1];
+ info.controlID |= response[0] << 8;
+ info.taskID = response[3];
+ info.taskID |= response[2] << 8;
+ info.flags = response[4];
+ info.position = response[5];
+ info.group = response[6];
+ info.groupMask = response[7];
+ info.additionalFlags = response[8];
+ return info;
+}
+
+ReprogControls::ControlInfo ReprogControls::getControlReporting(uint16_t cid)
+{
+ std::vector params(2);
+ ControlInfo info{};
+ params[0] = (cid >> 8) & 0xff;
+ params[1] = cid & 0xff;
+ auto response = callFunction(GetControlReporting, params);
+
+ info.controlID = response[1];
+ info.controlID |= response[0] << 8;
+ info.flags = response[2];
+ return info;
+}
+
+void ReprogControls::setControlReporting(uint8_t cid, ControlInfo info)
+{
+ std::vector params(5);
+ params[0] = (cid >> 8) & 0xff;
+ params[1] = cid & 0xff;
+ params[2] = info.flags;
+ params[3] = (info.controlID >> 8) & 0xff;
+ params[4] = info.controlID & 0xff;
+ callFunction(SetControlReporting, params);
+}
+
+std::set ReprogControls::divertedButtonEvent(
+ const hidpp::Report& report)
+{
+ assert(report.function() == DivertedButtonEvent);
+ std::set buttons;
+ uint8_t cids = std::distance(report.paramBegin(), report.paramEnd())/2;
+ for(uint8_t i = 0; i < cids; i++) {
+ uint16_t cid = report.paramBegin()[2*i + 1];
+ cid |= report.paramBegin()[2*i] << 8;
+ if(cid)
+ buttons.insert(cid);
+ else
+ break;
+ }
+ return buttons;
+}
+
+ReprogControls::Move ReprogControls::divertedRawXYEvent(const hidpp::Report
+ &report)
+{
+ assert(report.function() == DivertedRawXYEvent);
+ Move move{};
+ move.x = report.paramBegin()[1];
+ move.x |= report.paramBegin()[0] << 8;
+ move.y = report.paramBegin()[3];
+ move.y |= report.paramBegin()[2] << 8;
+ return move;
+}
diff --git a/src/logid/backend/hidpp20/features/ReprogControls.h b/src/logid/backend/hidpp20/features/ReprogControls.h
new file mode 100644
index 0000000..3197040
--- /dev/null
+++ b/src/logid/backend/hidpp20/features/ReprogControls.h
@@ -0,0 +1,158 @@
+/*
+ * 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 .
+ *
+ */
+#ifndef LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
+#define LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
+
+#include "../feature_defs.h"
+#include "../Feature.h"
+
+namespace logid {
+namespace backend {
+namespace hidpp20
+{
+ class ReprogControls : public Feature
+ {
+ public:
+ static const uint16_t ID = FeatureID::REPROG_CONTROLS;
+ virtual uint16_t getID() { return ID; }
+
+ virtual bool supportsRawXY() { return false; }
+
+ enum Function {
+ GetControlCount = 0,
+ GetControlInfo = 1,
+ GetControlReporting = 2,
+ SetControlReporting = 3
+ };
+
+ enum Event {
+ DivertedButtonEvent = 0,
+ DivertedRawXYEvent = 1
+ };
+
+ explicit ReprogControls(Device* dev);
+
+ virtual uint8_t getControlCount();
+
+ struct ControlInfo
+ {
+ uint16_t controlID;
+ uint16_t taskID;
+ uint8_t flags;
+ uint8_t position; // F key position, 0 if not an Fx key
+ uint8_t group;
+ uint8_t groupMask;
+ uint8_t additionalFlags;
+ };
+
+ enum ControlInfoFlags: uint8_t
+ {
+ MouseButton = 1, //Mouse button
+ FKey = 1<<1, //Fx key
+ Hotkey = 1<<2,
+ FnToggle = 1<<3,
+ ReprogHint = 1<<4,
+ TemporaryDivertable = 1<<5,
+ PerisentlyDiverable = 1<<6,
+ Virtual = 1<<7
+ };
+ enum ControlInfoAdditionalFlags: uint8_t {
+ RawXY = 1<<0
+ };
+
+ virtual ControlInfo getControlInfo(uint8_t index);
+
+ enum ControlReportingFlags: uint8_t {
+ TemporaryDiverted = 1<<0,
+ ChangeTemporaryDivert = 1<<1,
+ PersistentDiverted = 1<<2,
+ ChangePersistentDivert = 1<<3,
+ RawXYDiverted = 1<<4,
+ ChangeRawXYDivert = 1<<5
+ };
+
+ // Onlu controlId and flags will be set
+ virtual ControlInfo getControlReporting(uint16_t cid);
+
+ // Only controlId (for remap) and flags will be read
+ virtual void setControlReporting(uint8_t cid, ControlInfo info);
+
+ static std::set divertedButtonEvent(const hidpp::Report&
+ report);
+
+ struct Move
+ {
+ int16_t x;
+ int16_t y;
+ };
+
+ static Move divertedRawXYEvent(const hidpp::Report& report);
+
+ static ReprogControls autoVersion(Device *dev);
+ protected:
+ ReprogControls(Device* dev, uint16_t _id);
+ };
+
+ class ReprogControlsV2 : public ReprogControls
+ {
+ public:
+ static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2;
+ uint16_t getID() override { return ID; }
+
+ explicit ReprogControlsV2(Device* dev);
+ protected:
+ ReprogControlsV2(Device* dev, uint16_t _id);
+ };
+
+ class ReprogControlsV2_2 : public ReprogControlsV2
+ {
+ public:
+ static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2_2;
+ uint16_t getID() override { return ID; }
+
+ explicit ReprogControlsV2_2(Device* dev);
+ protected:
+ ReprogControlsV2_2(Device* dev, uint16_t _id);
+ };
+
+ class ReprogControlsV3 : public ReprogControlsV2_2
+ {
+ public:
+ static const uint16_t ID = FeatureID::REPROG_CONTROLS_V3;
+ uint16_t getID() override { return ID; }
+
+ explicit ReprogControlsV3(Device* dev);
+ protected:
+ ReprogControlsV3(Device* dev, uint16_t _id);
+ };
+
+ class ReprogControlsV4 : public ReprogControlsV3
+ {
+ public:
+ static const uint16_t ID = FeatureID::REPROG_CONTROLS_V4;
+ uint16_t getID() override { return ID; }
+
+ bool supportsRawXY() override { return true; }
+
+ explicit ReprogControlsV4(Device* dev);
+ protected:
+ ReprogControlsV4(Device* dev, uint16_t _id);
+ };
+}}}
+
+#endif //LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H