a811/src/a811.cpp
2024-09-02 14:33:45 +02:00

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);
}