mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2026-09-14 14:17:46 -04:00
254 lines
7.4 KiB
C++
254 lines
7.4 KiB
C++
/*---------------------------------------------------------*\
|
|
| EVGAX12Controller.cpp |
|
|
| |
|
|
| Driver for the EVGA X12 gaming mouse |
|
|
| |
|
|
| QuickMythril 08 Aug 2026 |
|
|
| |
|
|
| This file is part of the OpenRGB project |
|
|
| SPDX-License-Identifier: GPL-2.0-or-later |
|
|
\*---------------------------------------------------------*/
|
|
|
|
#include <chrono>
|
|
#include <cstring>
|
|
#include <thread>
|
|
#include "EVGAX12Controller.h"
|
|
#include "LogManager.h"
|
|
#include "StringUtils.h"
|
|
|
|
#define EVGA_X12_HEADER 0xEA
|
|
#define EVGA_X12_DIRECTION_GET 0x01
|
|
#define EVGA_X12_DIRECTION_SET 0x02
|
|
#define EVGA_X12_COMMAND_MODE 0x01
|
|
#define EVGA_X12_COMMAND_STATIC 0x02
|
|
#define EVGA_X12_START_WITH_PROFILE 0x01
|
|
#define EVGA_X12_READ_ATTEMPTS 5
|
|
#define EVGA_X12_READ_DELAY_MS 5
|
|
#define EVGA_X12_WRITE_ATTEMPTS 3
|
|
|
|
EVGAX12Controller::EVGAX12Controller(hid_device* dev_handle, const hid_device_info& info, const std::string& dev_name)
|
|
{
|
|
dev = dev_handle;
|
|
location = info.path;
|
|
name = dev_name;
|
|
}
|
|
|
|
EVGAX12Controller::~EVGAX12Controller()
|
|
{
|
|
hid_close(dev);
|
|
}
|
|
|
|
std::string EVGAX12Controller::GetDeviceLocation()
|
|
{
|
|
return("HID: " + location);
|
|
}
|
|
|
|
std::string EVGAX12Controller::GetNameString()
|
|
{
|
|
return(name);
|
|
}
|
|
|
|
std::string EVGAX12Controller::GetSerialString()
|
|
{
|
|
wchar_t serial_string[128];
|
|
int ret = hid_get_serial_number_string(dev, serial_string, 128);
|
|
|
|
if(ret != 0)
|
|
{
|
|
return("");
|
|
}
|
|
|
|
return(StringUtils::wstring_to_string(serial_string));
|
|
}
|
|
|
|
EVGAX12LightingState EVGAX12Controller::GetLightingState()
|
|
{
|
|
std::lock_guard<std::mutex> lock(device_mutex);
|
|
EVGAX12LightingState state = {};
|
|
unsigned char packet[EVGA_X12_REPORT_SIZE];
|
|
|
|
state.modes_valid = GetReport(EVGA_X12_COMMAND_MODE, packet);
|
|
|
|
if(state.modes_valid)
|
|
{
|
|
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
|
|
{
|
|
state.modes[led_idx] = packet[4 + led_idx];
|
|
}
|
|
}
|
|
|
|
state.static_valid = GetReport(EVGA_X12_COMMAND_STATIC, packet);
|
|
|
|
if(state.static_valid)
|
|
{
|
|
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
|
|
{
|
|
unsigned int offset = 4 + (led_idx * 4);
|
|
state.brightness[led_idx] = packet[offset];
|
|
state.colors[led_idx] = ToRGBColor(packet[offset + 1], packet[offset + 2], packet[offset + 3]);
|
|
}
|
|
}
|
|
|
|
return(state);
|
|
}
|
|
|
|
bool EVGAX12Controller::SetMode(unsigned char mode)
|
|
{
|
|
std::lock_guard<std::mutex> lock(device_mutex);
|
|
return(SetModeReport(mode));
|
|
}
|
|
|
|
bool EVGAX12Controller::SetModeReport(unsigned char mode)
|
|
{
|
|
unsigned char packet[EVGA_X12_REPORT_SIZE] = {};
|
|
|
|
packet[0] = EVGA_X12_REPORT_ID;
|
|
packet[1] = EVGA_X12_HEADER;
|
|
packet[2] = EVGA_X12_DIRECTION_SET;
|
|
packet[3] = EVGA_X12_COMMAND_MODE;
|
|
|
|
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
|
|
{
|
|
packet[4 + led_idx] = mode;
|
|
}
|
|
|
|
return(SendReport(packet));
|
|
}
|
|
|
|
bool EVGAX12Controller::SetStatic(const std::vector<RGBColor>& colors, unsigned char brightness)
|
|
{
|
|
if(colors.size() < EVGA_X12_LED_COUNT)
|
|
{
|
|
LOG_ERROR("[%s] Cannot set static lighting with only %u colors", name.c_str(), (unsigned int)colors.size());
|
|
return(false);
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(device_mutex);
|
|
return(SetStaticReport(colors, brightness));
|
|
}
|
|
|
|
bool EVGAX12Controller::SetStaticAndActivate(const std::vector<RGBColor>& colors, unsigned char brightness)
|
|
{
|
|
if(colors.size() < EVGA_X12_LED_COUNT)
|
|
{
|
|
LOG_ERROR("[%s] Cannot set static lighting with only %u colors", name.c_str(), (unsigned int)colors.size());
|
|
return(false);
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(device_mutex);
|
|
|
|
if(!SetStaticReport(colors, brightness))
|
|
{
|
|
return(false);
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(EVGA_X12_READ_DELAY_MS));
|
|
return(SetModeReport(EVGA_X12_MODE_STATIC));
|
|
}
|
|
|
|
bool EVGAX12Controller::SetStaticReport(const std::vector<RGBColor>& colors, unsigned char brightness)
|
|
{
|
|
unsigned char packet[EVGA_X12_REPORT_SIZE] = {};
|
|
|
|
packet[0] = EVGA_X12_REPORT_ID;
|
|
packet[1] = EVGA_X12_HEADER;
|
|
packet[2] = EVGA_X12_DIRECTION_SET;
|
|
packet[3] = EVGA_X12_COMMAND_STATIC;
|
|
|
|
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
|
|
{
|
|
unsigned int offset = 4 + (led_idx * 4);
|
|
packet[offset] = brightness;
|
|
packet[offset + 1] = RGBGetRValue(colors[led_idx]);
|
|
packet[offset + 2] = RGBGetGValue(colors[led_idx]);
|
|
packet[offset + 3] = RGBGetBValue(colors[led_idx]);
|
|
packet[20 + led_idx] = EVGA_X12_START_WITH_PROFILE;
|
|
}
|
|
|
|
for(unsigned int attempt = 0; attempt < EVGA_X12_WRITE_ATTEMPTS; attempt++)
|
|
{
|
|
if(!SendReport(packet))
|
|
{
|
|
return(false);
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(EVGA_X12_READ_DELAY_MS));
|
|
|
|
unsigned char reply[EVGA_X12_REPORT_SIZE];
|
|
|
|
if(GetReport(EVGA_X12_COMMAND_STATIC, reply))
|
|
{
|
|
bool matches = true;
|
|
|
|
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
|
|
{
|
|
unsigned int offset = 4 + (led_idx * 4);
|
|
|
|
if(std::memcmp(&packet[offset], &reply[offset], 4) != 0)
|
|
{
|
|
matches = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(matches)
|
|
{
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG_VERBOSE("[%s] Retries exhausted setting static lighting", name.c_str());
|
|
return(false);
|
|
}
|
|
|
|
bool EVGAX12Controller::GetReport(unsigned char command, unsigned char* packet)
|
|
{
|
|
std::memset(packet, 0, EVGA_X12_REPORT_SIZE);
|
|
packet[0] = EVGA_X12_REPORT_ID;
|
|
packet[1] = EVGA_X12_HEADER;
|
|
packet[2] = EVGA_X12_DIRECTION_GET;
|
|
packet[3] = command;
|
|
|
|
if(!SendReport(packet))
|
|
{
|
|
return(false);
|
|
}
|
|
|
|
for(unsigned int attempt = 0; attempt < EVGA_X12_READ_ATTEMPTS; attempt++)
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(EVGA_X12_READ_DELAY_MS));
|
|
std::memset(packet, 0, EVGA_X12_REPORT_SIZE);
|
|
packet[0] = EVGA_X12_REPORT_ID;
|
|
|
|
int bytes_read = hid_get_feature_report(dev, packet, EVGA_X12_REPORT_SIZE);
|
|
|
|
if(bytes_read == EVGA_X12_REPORT_SIZE && packet[1] == EVGA_X12_HEADER && packet[3] == command)
|
|
{
|
|
return(true);
|
|
}
|
|
|
|
if(bytes_read < 0)
|
|
{
|
|
LOG_DEBUG("[%s] Error reading feature report: %ls", name.c_str(), hid_error(dev));
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
LOG_VERBOSE("[%s] Retries exhausted reading command 0x%02X", name.c_str(), command);
|
|
return(false);
|
|
}
|
|
|
|
bool EVGAX12Controller::SendReport(const unsigned char* packet)
|
|
{
|
|
int bytes_written = hid_send_feature_report(dev, packet, EVGA_X12_REPORT_SIZE);
|
|
|
|
if(bytes_written != EVGA_X12_REPORT_SIZE)
|
|
{
|
|
LOG_DEBUG("[%s] Error writing feature report: %ls", name.c_str(), hid_error(dev));
|
|
return(false);
|
|
}
|
|
|
|
return(true);
|
|
}
|