430 lines
13 KiB
C++
430 lines
13 KiB
C++
#include <cstring>
|
|
#include <string.h>
|
|
|
|
#include "a811.h"
|
|
#include "usb_thingy.h"
|
|
|
|
#define CHK(X) \
|
|
if ((r = (X)) != 0) \
|
|
return r;
|
|
|
|
ssize_t logError(const std::string &errorMessage, const char *file, int line,
|
|
const char *function)
|
|
{
|
|
std::cerr << "ERROR: " << file << ":" << line << " - " << function << ": "
|
|
<< errorMessage << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
A811::A811()
|
|
{
|
|
// Init usb lib
|
|
_connection = &(Connection::getInstance());
|
|
|
|
// init other vars
|
|
_conf12 = (conf_12 *)malloc(sizeof(conf_12));
|
|
memset(_conf12, 0, sizeof(conf_12));
|
|
|
|
readConfigFromDevice(CONF_TYPE::LIGHT);
|
|
|
|
_macros = nullptr;
|
|
}
|
|
|
|
ssize_t A811::setLightMode(LIGHT_MODE mode, int p1, int p2)
|
|
{
|
|
|
|
switch (mode)
|
|
{
|
|
case LIGHT_MODE::COLORFUL_STREAMING:
|
|
if (p1 <= 0 || p1 > 4 || p2 <= 0 || p2 > 4)
|
|
logError("ERROR: Invalid brightness or speed.", __FILE__, __LINE__,
|
|
__func__);
|
|
_conf12->c1.light_mode = (uint8_t)mode;
|
|
_conf12->c1.col_brightness_speed =
|
|
(uint8_t)((p1 & 0x0F) | ((p2 & 0x0F) << 4));
|
|
std::cout << std::hex << (int)_conf12->c1.light_mode << "-2\n";
|
|
break;
|
|
case LIGHT_MODE::STEADY:
|
|
if (p1 <= 0 || p1 > 4 || p2 <= 0 || p2 > 4)
|
|
logError("ERROR: Invalid brightness or speed.", __FILE__, __LINE__,
|
|
__func__);
|
|
_conf12->c1.col_brightness_speed =
|
|
(uint8_t)((p1 & 0x0F) | ((p2 & 0x0F) << 4));
|
|
_conf12->c1.light_mode = (uint8_t)mode;
|
|
break;
|
|
case LIGHT_MODE::BREATHING:
|
|
if (p1 <= 0 || p1 > 4 || p2 <= 0 || p2 > 6)
|
|
logError("ERROR: Invalid brightness or color number.", __FILE__, __LINE__,
|
|
__func__);
|
|
_conf12->c1.light_mode = (uint8_t)mode;
|
|
_conf12->c1.br_brightness_speed =
|
|
(uint8_t)((p1 & 0x0F) | ((p2 & 0x0F) << 4));
|
|
break;
|
|
default:
|
|
logError("ERROR: Invalid LIGHT_MODE.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1);
|
|
}
|
|
return ssize_t(0);
|
|
}
|
|
|
|
LIGHT_MODE A811::getLightMode() { return LIGHT_MODE(_conf12->c1.light_mode); }
|
|
|
|
ssize_t A811::setLightModeColors(LIGHT_MODE mode, unsigned char colors[21])
|
|
{
|
|
switch (mode)
|
|
{
|
|
case LIGHT_MODE::STEADY:
|
|
std::memcpy(&_conf12->c1.col_steady, colors, sizeof(_conf12->c1.col_steady));
|
|
break;
|
|
case LIGHT_MODE::BREATHING:
|
|
std::memcpy(&_conf12->c1.col_breathing, colors,
|
|
sizeof(_conf12->c1.col_breathing));
|
|
break;
|
|
default:
|
|
logError("ERROR: Invalid LIGHT_MODE.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1);
|
|
}
|
|
return ssize_t(0);
|
|
}
|
|
|
|
unsigned char *A811::getLightModeColors(LIGHT_MODE mode)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case LIGHT_MODE::STEADY:
|
|
return _conf12->c1.col_steady;
|
|
case LIGHT_MODE::BREATHING:
|
|
return _conf12->c1.col_breathing;
|
|
default:
|
|
logError("ERROR: Invalid LIGHT_MODE.", __FILE__, __LINE__, __func__);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
ssize_t A811::setUSBPollingRate(POLLING_RATE pr)
|
|
{
|
|
|
|
if ((uint8_t)pr <= 0 || (uint8_t)pr > 4)
|
|
logError("ERROR: Invalid POLLING_RATE.", __FILE__, __LINE__, __func__);
|
|
_conf12->c1.polling_rate = (uint8_t)pr;
|
|
return ssize_t();
|
|
}
|
|
|
|
POLLING_RATE A811::getUSBPollingRate()
|
|
{
|
|
return POLLING_RATE(_conf12->c1.polling_rate);
|
|
}
|
|
|
|
ssize_t A811::setDPI(DPI_MODE mode, unsigned char color[3], int dpi, int active)
|
|
{
|
|
|
|
if ((uint8_t)mode < (uint8_t)DPI_MODE::DPI_1 ||
|
|
(uint8_t)mode > (uint8_t)DPI_MODE::DPI_5)
|
|
{
|
|
logError("ERROR: Invalid DPI_MODE.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1);
|
|
}
|
|
if (dpi > 26000 || dpi < 50)
|
|
{
|
|
logError("ERROR: Invalid dpi.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(2);
|
|
}
|
|
if (active > 5 || active < 1)
|
|
{
|
|
logError("ERROR: Invalid active dpi modes.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(2);
|
|
}
|
|
// Calculate the DPI value according to the formula
|
|
uint16_t dpi_value = (dpi - 50) / 50;
|
|
|
|
// Split the value into two bytes (least significant byte first)
|
|
uint8_t lsb = dpi_value & 0xFF; // Extract the least significant byte
|
|
uint8_t msb = (dpi_value >> 8) & 0xFF; // Extract the most significant byte
|
|
|
|
|
|
_conf12->c1.dpi_modes = // number of active modes
|
|
(1 & 0x0f) << 4 | ((active & 0x0F));
|
|
|
|
_conf12->c1.hdpi[((uint8_t)mode - 1) * 2] = lsb;
|
|
_conf12->c1.hdpi[((uint8_t)mode - 1) * 2 + 1] = msb;
|
|
_conf12->c1.col_dpi[((uint8_t)mode - 1) * 3] = color[0];
|
|
_conf12->c1.col_dpi[((uint8_t)mode - 1) * 3 + 1] = color[1];
|
|
_conf12->c1.col_dpi[((uint8_t)mode - 1) * 3 + 2] = color[2];
|
|
|
|
return ssize_t(0);
|
|
}
|
|
|
|
int A811::getDPI(DPI_MODE mode)
|
|
{
|
|
if ((uint8_t)mode < (uint8_t)DPI_MODE::DPI_1 ||
|
|
(uint8_t)mode > (uint8_t)DPI_MODE::DPI_5)
|
|
{
|
|
logError("ERROR: Invalid DPI_MODE.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1);
|
|
}
|
|
// Assume lsb and msb are given or extracted from your configuration structure
|
|
uint8_t lsb = _conf12->c1.hdpi[((uint8_t)mode - 1) * 2];
|
|
uint8_t msb = _conf12->c1.hdpi[((uint8_t)mode - 1) * 2 + 1];
|
|
|
|
// Reconstruct the dpi_value
|
|
uint16_t dpi_value = msb << 8 | lsb & 0xFF;
|
|
|
|
std::cout << std::hex << dpi_value << " : " << (int)lsb << " : " << (int)msb << std::endl;
|
|
|
|
// Reverse the formula to get the original dpi
|
|
uint16_t dpi = (dpi_value * 50) + 50;
|
|
|
|
return dpi;
|
|
}
|
|
|
|
BATTERY_STAT A811::getBatteryStatus()
|
|
{
|
|
if (_batStat.wired == 0x10)
|
|
{
|
|
if (_batStat.capacity == 0x01)
|
|
return BATTERY_STAT::CHARGING;
|
|
else if (_batStat.capacity == 0x02)
|
|
return BATTERY_STAT::NOT_CHARGING;
|
|
}
|
|
return BATTERY_STAT::DISCHARGING;
|
|
}
|
|
|
|
int A811::getBatteryCapacity()
|
|
{
|
|
if (_batStat.wired == 0x11)
|
|
return (int)_batStat.capacity;
|
|
//@comment thats weird what to do i cant read the battery level if it is
|
|
// plugged???
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
ssize_t A811::setMultimediaButton(int id, std::string keyname)
|
|
{
|
|
if (id >= 0 && id < 7)
|
|
{
|
|
// Assuming keyname is a valid key in the _keycodes map
|
|
if (_keycodes.find(keyname) == _keycodes.end())
|
|
{
|
|
logError("ERROR: Keyname not found in keycodes.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1); // or handle the error appropriately
|
|
}
|
|
auto arr = _keycodes[keyname];
|
|
unsigned char *mahbytes = (unsigned char *)&_conf12->c2.mouse_buttons[id];
|
|
|
|
for (int i = 0; i < (int)sizeof(_conf12->c2.mouse_buttons[id]); ++i)
|
|
{
|
|
std::cout << std::hex << (int)mahbytes[i] << "|";
|
|
mahbytes[i] = ((unsigned char *)&arr)[i];
|
|
std::cout << std::hex << (int)mahbytes[i] << std::endl;
|
|
}
|
|
}
|
|
else if (id >= 7 && id < 15)
|
|
{
|
|
// Assuming keyname is a valid key in the _keycodes map
|
|
auto it = _keycodes.find(keyname);
|
|
if (it != _keycodes.end())
|
|
{
|
|
const std::array<uint8_t, 4> &keycode_array = it->second; // Get the array from the map
|
|
// std::copy(keycode_array.begin(), keycode_array.end(), _conf12->c2.side[id - 7].keycode);
|
|
}
|
|
else
|
|
{
|
|
logError("ERROR: Keyname not found in keycodes.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1); // or handle the error appropriately
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logError("ERROR: Invalid button id.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1);
|
|
}
|
|
|
|
return ssize_t(0);
|
|
}
|
|
|
|
ssize_t A811::setMacroButton(int id, int macro_id, int cycle_type, int cycle_cnt)
|
|
{
|
|
|
|
unsigned char keyConf[4] = {0x70, macro_id, cycle_type, cycle_cnt};
|
|
|
|
// maybe check if macro is on mouse
|
|
// check validity of data
|
|
// also i am not sure at all how the macro ids work like why is the 1st key alway macro nr 1 and so on
|
|
if (id >= 0 && id < 7)
|
|
{
|
|
memcpy(&_conf12->c2.mouse_buttons[id], keyConf, sizeof(_conf12->c2.mouse_buttons[id]));
|
|
}
|
|
else if (id >= 7 && id < 15)
|
|
{
|
|
memcpy(&_conf12->c2.side[id - 7], keyConf, sizeof(_conf12->c2.side[id]));
|
|
}
|
|
else
|
|
{
|
|
logError("ERROR: Invalid button id.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1);
|
|
}
|
|
|
|
return ssize_t(0);
|
|
}
|
|
|
|
ssize_t A811::setButton(int id, std::string keyboardKey, int mod)
|
|
{
|
|
unsigned char key[4];
|
|
key[0] = 0x21;
|
|
key[1] = mod;
|
|
|
|
if (id >= 0 && id < 7)
|
|
{
|
|
// Assuming keyname is a valid key in the _keycodes map
|
|
auto it = _keyboardKeys.find(keyboardKey);
|
|
if (it != _keyboardKeys.end())
|
|
{
|
|
key[2] = it->second;
|
|
key[3] = 0x00; // optional button
|
|
memcpy(&_conf12->c2.mouse_buttons[id], key, sizeof(_conf12->c2.mouse_buttons[id]));
|
|
}
|
|
else
|
|
{
|
|
logError("ERROR: Keyname not found in _keyboardKeys.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1); // or handle the error appropriately
|
|
}
|
|
}
|
|
else if (id >= 7 && id < 15)
|
|
{
|
|
// Assuming keyname is a valid key in the _keycodes map
|
|
auto it = _keyboardKeys.find(keyboardKey);
|
|
if (it != _keyboardKeys.end())
|
|
{
|
|
key[2] = it->second;
|
|
key[3] = 0x00; // optional button
|
|
memcpy(&_conf12->c2.side[id], key, sizeof(_conf12->c2.side[id]));
|
|
}
|
|
else
|
|
{
|
|
logError("ERROR: Keyname not found in _keyboardKeys.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1); // or handle the error appropriately
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logError("ERROR: Invalid button id.", __FILE__, __LINE__, __func__);
|
|
return ssize_t(1);
|
|
}
|
|
|
|
return ssize_t(0);
|
|
}
|
|
|
|
_r_button A811::getButton(int id)
|
|
{
|
|
|
|
_r_button b;
|
|
/*
|
|
if (id >= 0 && id < 7){
|
|
|
|
// Assuming keyname is a valid key in the _keycodes map
|
|
auto it = _buttonNames.find(id);
|
|
if (it != _buttonNames.end()) {
|
|
b.name = it->second;
|
|
} else {
|
|
logError("ERROR: Button name not found in _buttonNames.", __FILE__, __LINE__, __func__);
|
|
// Handle the error appropriately, e.g., returning an empty _r_button
|
|
return _r_button(); // Return empty or error _r_button
|
|
}
|
|
}
|
|
else if (id >= 7 && id < 15)
|
|
{
|
|
// Assuming keyname is a valid key in the _keycodes map
|
|
auto it = _buttonNames.find(id-7);
|
|
if (it != _buttonNames.end()) {
|
|
b.name = it->second;
|
|
} else {
|
|
logError("ERROR: Button name not found in _buttonNames.", __FILE__, __LINE__, __func__);
|
|
// Handle the error appropriately, e.g., returning an empty _r_button
|
|
return _r_button(); // Return empty or error _r_button
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logError("ERROR: Invalid button id.", __FILE__, __LINE__, __func__);
|
|
}
|
|
*/
|
|
return _r_button();
|
|
}
|
|
|
|
ssize_t A811::readConfigFromDevice(CONF_TYPE t_conf)
|
|
{
|
|
unsigned char _data[8] = {0x05, 0x21, 0, 0, 0, 0, 0, 0};
|
|
unsigned char _datas[520];
|
|
memset(_datas, 0, 520);
|
|
/*if(!connection->isWireless())
|
|
_data[1] = 0x11;*/
|
|
// not sure if this means anything
|
|
ssize_t r;
|
|
CHK(_connection->open());
|
|
|
|
switch (t_conf)
|
|
{
|
|
case CONF_TYPE::LIGHT:
|
|
_data[1] = 0x21;
|
|
CHK(_connection->setReport(0x0305, _data, 8));
|
|
CHK(_connection->getReport(0x0308, (unsigned char *)&_conf12->c1, 520));
|
|
break;
|
|
case CONF_TYPE::KEY:
|
|
_data[1] = 0x22;
|
|
CHK(_connection->setReport(0x0305, (unsigned char *)_data, 8));
|
|
CHK(_connection->getReport(0x0308, (unsigned char *)&_conf12->c2, 520));
|
|
break;
|
|
case CONF_TYPE::MACRO:
|
|
logError("ERROR: Macros not implemented.", __FILE__, __LINE__, __func__);
|
|
break;
|
|
case CONF_TYPE::BATTERY:
|
|
_data[1] = 0x90;
|
|
CHK(_connection->setReport(0x0305, (unsigned char *)_data, 8));
|
|
CHK(_connection->getReport(0x0305, (unsigned char *)&_batStat, 8));
|
|
break;
|
|
default:
|
|
logError("ERROR: Invalid CONF_TYPE.", __FILE__, __LINE__, __func__);
|
|
CHK(_connection->close());
|
|
return ssize_t(1);
|
|
}
|
|
|
|
CHK(_connection->close());
|
|
|
|
return ssize_t(0);
|
|
}
|
|
|
|
ssize_t A811::writeConfigToDevice(CONF_TYPE t_conf)
|
|
{
|
|
ssize_t r;
|
|
CHK(_connection->open());
|
|
|
|
switch (t_conf)
|
|
{
|
|
case CONF_TYPE::LIGHT:
|
|
_conf12->c1.req_type = 0x92;
|
|
std::cout << std::hex << (int)_conf12->c1.light_mode << "-3\n";
|
|
CHK(_connection->setReport(0x0308, (unsigned char *)&_conf12->c1, 520));
|
|
break;
|
|
case CONF_TYPE::KEY:
|
|
_conf12->c2.req_type = 0x50;
|
|
CHK(_connection->setReport(0x0308, (unsigned char *)&_conf12->c2, 520));
|
|
break;
|
|
case CONF_TYPE::MACRO:
|
|
logError("ERROR: Macros not implemented.", __FILE__, __LINE__, __func__);
|
|
break;
|
|
case CONF_TYPE::BATTERY:
|
|
logError("ERROR: Invalid write to CONF_TYPE::BATTERY.", __FILE__, __LINE__,
|
|
__func__);
|
|
break;
|
|
default:
|
|
logError("ERROR: Invalid CONF_TYPE.", __FILE__, __LINE__, __func__);
|
|
CHK(_connection->close());
|
|
return ssize_t(1);
|
|
}
|
|
|
|
CHK(_connection->close());
|
|
|
|
return ssize_t(0);
|
|
}
|