1
0
Fork 0
forked from denis/a811
a811/src/a811.cpp

536 lines
16 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)
{
mahbytes[i] = ((unsigned char *)&arr)[i];
}
}
else if (id >= 7 && id < 15)
{
// 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.side[id - 7];
for (int i = 0; i < (int)sizeof(_conf12->c2.mouse_buttons[id]); ++i)
{
mahbytes[i] = ((unsigned char *)&arr)[i];
}
}
else
{
logError("ERROR: Invalid button id.", __FILE__, __LINE__, __func__);
return ssize_t(1);
}
return ssize_t(0);
}
ssize_t A811::setMacroButton(int id, uint8_t macro_id, uint8_t cycle_type, uint8_t 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 - 7], 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);
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::readMacrosFromDevice(int cnt)
{
if (cnt > 255)
{
std::cout << "No more than 255 macros on device" << std::endl;
return ssize_t(1);
}
_macros = (macro **)malloc(cnt * sizeof(macro *));
_connection->open();
_connection->keepConnection(true);
for (int i = 1; i <= cnt; i++)
{
// get macro
// ist das bissl kacke weil immer neuer handle und detach und attach hmm
_macros[i - 1] = (macro *)malloc(sizeof(macro));
readMacroFromDevice(_macros[i - 1], i);
}
_connection->keepConnection(false);
_connection->close();
// check if any macros are read or if any empty macros are read
// maybe just read all possible macros
// or this function is actually completely useless just keep track of the macros in the gui code
return ssize_t(0);
}
ssize_t A811::readMacroFromDevice(macro *_macro, int id)
{
if (id > (uint8_t)255)
{
std::cout << "No more than 255 macros on device" << std::endl;
return ssize_t(0);
}
memset(_macro, 0, 520 * sizeof(unsigned char));
unsigned char _data[8] = {0x05, 0x31, (uint8_t)id, 0, 0, 0, 0, 0};
ssize_t r;
CHK(_connection->open());
CHK(_connection->setReport(0x0305, (unsigned char *)_data, 8));
CHK(_connection->getReport(0x0308, (unsigned char *)_macro, 520));
CHK(_connection->close());
// check if empty macro, if valid macro
return ssize_t(0);
}
ssize_t A811::writeMacroToDevice(macro *p_macro)
{
// conf_1
p_macro->set_report_req[0] = 0x80;
p_macro->set_report_req[1] = 0x30;
p_macro->set_report_req[2] = 0x02;
ssize_t r;
CHK(_connection->open());
CHK(_connection->setReport(0x0308, (unsigned char *)p_macro, 520));
CHK(_connection->close());
// check if macro is valid
return ssize_t(0);
}
bool A811::isIdle()
{
unsigned char conf[8];
memset(conf, 0, 8);
unsigned char _data[8] = {0x05, 0x80, 0, 0, 0, 0, 0, 0};
ssize_t r;
CHK(_connection->open());
CHK(_connection->setReport(0x0305, _data, 8));
CHK(_connection->getReport(0x0305, (unsigned char *)conf, 8));
CHK(_connection->close());
if (conf[2] == 0x0 && conf[3] == 0x1)
return true;
else if (conf[2] == 0x1 && conf[3] == 0x1)
return false;
logError("ERROR: Reading idle status.", __FILE__, __LINE__, __func__);
return false;
}
ssize_t A811::restoreConfig()
{
unsigned char _data[8] = {0x05, 0x40, 0x01, 0, 0, 0, 0, 0};
ssize_t r;
CHK(_connection->open());
CHK(_connection->setReport(0x0305, (unsigned char *)_data, 8));
CHK(_connection->close());
return ssize_t(0);
}
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);
}