Add Support for EVGA X20 Gaming Mouse

Commit amended to remove udev rules (which is now autogenerated) and fix build by Adam Honse <calcprogrammer1@gmail.com>
This commit is contained in:
Cooper Knaak 2022-05-05 00:45:00 +00:00 committed by Adam Honse
parent 3aa5e2648f
commit 0d7e947575
6 changed files with 898 additions and 7 deletions

View file

@ -0,0 +1,349 @@
/*-------------------------------------------------*\
| EVGAMouseController.cpp |
| |
| Driver for EVGA X20 Gaming Mouse RGB Controller. |
| |
| Cooper Knaak 1/23/2022 |
\*-------------------------------------------------*/
#include "EVGAMouseController.h"
#include "LogManager.h"
#include <algorithm>
#include <iostream>
#include <thread>
#include <chrono>
#define HID_MAX_STR 255
#define EVGA_PERIPHERAL_LED_SOURCE_OF_TRUTH EVGA_PERIPHERAL_LED_LOGO
/*----------------------------------------------------------------*\
| Maximum number of attempts to read from a device before failing. |
\*----------------------------------------------------------------*/
#define EVGA_PERIPHERAL_MAX_ATTEMPTS 100
/*-----------------------------------------------------------------*\
| The delay between sending packets to the device in wireless mode. |
| In wireless mode, sending packets too close to each other causes |
| them to have no effect, despite the device responding properly. |
\*-----------------------------------------------------------------*/
#define EVGA_PERIPHERAL_PACKET_DELAY std::chrono::milliseconds(10)
/*--------------------------------------------------------------------------------*\
| Returns true if both buffers have equal bytes at each position, false otherwise. |
| Each buffer must be an array of bytes at least size bytes long. |
\*--------------------------------------------------------------------------------*/
static bool BuffersAreEqual(unsigned char *buffer1, unsigned char *buffer2, int size)
{
for(int i = 0; i < size; i++)
{
if(buffer1[i] != buffer2[i])
{
return false;
}
}
return true;
}
EVGAMouseController::EVGAMouseController(hid_device* dev_handle, char *_path, int connection_type)
{
dev = dev_handle;
location = _path;
this->connection_type = connection_type;
const int szTemp = HID_MAX_STR;
wchar_t tmpName[szTemp];
hid_get_manufacturer_string(dev, tmpName, szTemp);
std::wstring wName = std::wstring(tmpName);
device_name = std::string(wName.begin(), wName.end());
hid_get_product_string(dev, tmpName, szTemp);
wName = std::wstring(tmpName);
device_name.append(" ").append(std::string(wName.begin(), wName.end()));
hid_get_indexed_string(dev, 2, tmpName, szTemp);
wName = std::wstring(tmpName);
serial = std::string(wName.begin(), wName.end());
led_states.resize(EVGA_PERIPHERAL_LED_COUNT);
for(EVGAMouseControllerDeviceState &led_state : led_states)
{
led_state.mode = EVGA_PERIPHERAL_MODE_STATIC;
led_state.brightness = 255;
led_state.speed = 100;
led_state.colors.resize(1);
led_state.colors[0] = ToRGBColor(255, 255, 255);
}
}
EVGAMouseController::~EVGAMouseController()
{
}
std::string EVGAMouseController::GetDeviceName()
{
return device_name;
}
std::string EVGAMouseController::GetSerial()
{
return serial;
}
std::string EVGAMouseController::GetLocation()
{
return location;
}
uint8_t EVGAMouseController::GetMode()
{
return GetState().mode;
}
EVGAMouseControllerDeviceState EVGAMouseController::GetState()
{
RefreshDeviceState(EVGA_PERIPHERAL_LED_SOURCE_OF_TRUTH);
return led_states[EVGA_PERIPHERAL_LED_SOURCE_OF_TRUTH];
}
RGBColor EVGAMouseController::GetColorOfLed(int led)
{
RefreshDeviceState(led);
return led_states[led].colors[0];
}
void EVGAMouseController::SetMode(uint8_t mode, uint8_t index)
{
unsigned char buffer[EVGA_PERIPHERAL_PACKET_SIZE] =
{
0x00, /* report id - must be 0x00 according to hid_send_feature_report */
0x00, 0x00, 0x00, 0x1D, /* header bits - always the same */
0x02, 0x81, 0x01 /* 0x81 sets the mode, which is specified below. */
};
buffer[EVGA_PERIPHERAL_LED_INDEX_BYTE] = index;
buffer[EVGA_PERIPHERAL_MODE_BYTE] = mode;
int err = hid_send_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE);
if(err == -1)
{
const wchar_t* err_str = hid_error(dev);
LOG_DEBUG("[%s] Error writing buffer %s", device_name.c_str(), err_str);
}
led_states[index].mode = mode;
err = hid_get_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE);
if(err == -1)
{
const wchar_t* err_str = hid_error(dev);
LOG_DEBUG("[%s] Error reading buffer %s", device_name.c_str(), err_str);
}
}
void EVGAMouseController::SetLed(uint8_t index, uint8_t brightness, uint8_t speed, RGBColor color)
{
std::vector<RGBColor> colors;
colors.push_back(color);
SetLed(index, brightness, speed, colors, false);
}
void EVGAMouseController::SetLed(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector<RGBColor>& colors)
{
SetLed(index, brightness, speed, colors, false);
}
void EVGAMouseController::SetLedAndActivate(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector<RGBColor>& colors)
{
/*------------------------------------------------------------------------------------------------------------------------------*\
| Activating some modes requires two identical packets: one for setting the color, and one for setting the color AND activating. |
\*------------------------------------------------------------------------------------------------------------------------------*/
SetLed(index, brightness, speed, colors, false);
SetLed(index, brightness, speed, colors, true);
}
void EVGAMouseController::SetAllLeds(uint8_t brightness, uint8_t speed, const std::vector<RGBColor>& colors)
{
for(unsigned int i = 0; i < EVGA_PERIPHERAL_LED_COUNT; i++)
{
SetLed(i, brightness, speed, colors);
}
}
void EVGAMouseController::SetAllLedsAndActivate(uint8_t brightness, uint8_t speed, const std::vector<RGBColor>& colors)
{
for(unsigned int i = 0; i < EVGA_PERIPHERAL_LED_COUNT; i++)
{
SetLedAndActivate(i, brightness, speed, colors);
}
}
void EVGAMouseController::SetLed(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector<RGBColor>& colors, bool activate)
{
unsigned char buffer[EVGA_PERIPHERAL_PACKET_SIZE] =
{
0x00, /* report id - must be 0x00 according to hid_send_feature_report */
0x00, 0x00, static_cast<unsigned char>(connection_type), 0x1D,
0x02, 0x00, 0x02 /* header bits - always the same */
};
/*---------------------------------------------------------------------------------------------------------------*\
| Setting the mode to breathing sends 3 packets: first to activate the mode, second to set the list of colors and |
| third to send a packet identical to the second but with the first byte set ot 0xA1. This "activates" the mode. |
\*---------------------------------------------------------------------------------------------------------------*/
if(activate)
{
buffer[1] = 0xA1;
}
buffer[EVGA_PERIPHERAL_LED_INDEX_BYTE] = index;
/*-----------------------------------------------------------------------------------------*\
| Unleash RGB supports individual modes on the LEDs, but OpenRGB does not. Use one specific |
| LED's mode for any LED. |
\*-----------------------------------------------------------------------------------------*/
buffer[EVGA_PERIPHERAL_MODE_BYTE] = led_states[EVGA_PERIPHERAL_LED_SOURCE_OF_TRUTH].mode;
buffer[EVGA_PERIPHERAL_BRIGHTNESS_BYTE] = brightness;
buffer[EVGA_PERIPHERAL_SPEED_BYTE] = speed;
/*-----------------------------------------------------------------------*\
| 7 is the maximum number of colors that can be set from the vendor's UI. |
\*-----------------------------------------------------------------------*/
unsigned char color_count = std::min(colors.size(), static_cast<std::vector<RGBColor>::size_type>(7));
buffer[EVGA_PERIPHERAL_COLOR_COUNT_BYTE] = color_count;
for(unsigned char i = 0; i < color_count; i++)
{
buffer[15 + i * 3] = RGBGetRValue(colors[i]);
buffer[16 + i * 3] = RGBGetGValue(colors[i]);
buffer[17 + i * 3] = RGBGetBValue(colors[i]);
}
int err = hid_send_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE);
if(err == -1)
{
const wchar_t* err_str = hid_error(dev);
LOG_DEBUG("[%s] Error writing buffer %s", device_name.c_str(), err_str);
}
led_states[index].brightness = brightness;
led_states[index].speed = speed;
led_states[index].colors = colors;
/*------------------------------------------------------------------------------------*\
| If the device returns a response not ready packet, future writes will silently fail. |
| Wait until the device sends a valid packet to proceed. |
\*------------------------------------------------------------------------------------*/
ReadPacketOrLogErrors(buffer, EVGA_PERIPHERAL_MAX_ATTEMPTS);
}
void EVGAMouseController::RefreshDeviceState()
{
RefreshDeviceState(EVGA_PERIPHERAL_LED_FRONT);
RefreshDeviceState(EVGA_PERIPHERAL_LED_WHEEL);
RefreshDeviceState(EVGA_PERIPHERAL_LED_LOGO);
}
void EVGAMouseController::RefreshDeviceState(int led)
{
unsigned char buffer[EVGA_PERIPHERAL_PACKET_SIZE] =
{
0x00,
0x00, 0x00, static_cast<unsigned char>(connection_type), 0x1D,
0x02, 0x80, 0x02
};
buffer[EVGA_PERIPHERAL_LED_INDEX_BYTE] = static_cast<unsigned char>(led);
int err = hid_send_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE);
if(err == -1)
{
const wchar_t* err_str = hid_error(dev);
LOG_DEBUG("[%s] Error writing buffer %s", device_name.c_str(), err_str);
}
/*------------------------------------------------------------------------------*\
| Wait in wireless mode or else packets might be sent too quickly to take effect |
\*------------------------------------------------------------------------------*/
Wait();
if(ReadPacketOrLogErrors(buffer, EVGA_PERIPHERAL_MAX_ATTEMPTS))
{
int color_count = buffer[EVGA_PERIPHERAL_COLOR_COUNT_BYTE];
if(color_count == 0)
{
LOG_VERBOSE("[%s] No colors read from response. The device is likely asleep.", device_name.c_str());
return;
}
led_states[led].mode = buffer[EVGA_PERIPHERAL_MODE_BYTE];
led_states[led].brightness = buffer[EVGA_PERIPHERAL_BRIGHTNESS_BYTE];
led_states[led].speed = buffer[EVGA_PERIPHERAL_SPEED_BYTE];
led_states[led].colors.resize(std::max(color_count, 1));
for(int i = 0; i < color_count; i++)
{
uint8_t r = buffer[EVGA_PERIPHERAL_RED_BYTE + i * 3];
uint8_t g = buffer[EVGA_PERIPHERAL_GREEN_BYTE + i * 3];
uint8_t b = buffer[EVGA_PERIPHERAL_BLUE_BYTE + i * 3];
led_states[led].colors[i] = ToRGBColor(r, g, b);
}
}
}
bool EVGAMouseController::ReadPacketOrLogErrors(unsigned char *buffer, int max_attempts)
{
int bytes_read = ReadPacketOrWait(buffer, max_attempts);
if(bytes_read == -1)
{
const wchar_t* err_str = hid_error(dev);
LOG_DEBUG("[%s] Error reading buffer %s", device_name.c_str(), err_str);
return false;
}
else if(IsResponseNotReadyPacket(buffer))
{
LOG_VERBOSE("[%s] Retries exhausted reading from device. Write may have failed.", device_name.c_str());
return false;
}
else if(IsAsleepPacket(buffer))
{
LOG_VERBOSE("[%s] Device is asleep. Cannot send or receive packets until the device is awoken.", device_name.c_str());
return false;
}
return true;
}
int EVGAMouseController::ReadPacketOrWait(unsigned char *buffer, int max_attempts)
{
int attempts = 1;
Wait();
int bytes_read = hid_get_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE);
while(bytes_read == EVGA_PERIPHERAL_PACKET_SIZE && attempts < max_attempts && IsResponseNotReadyPacket(buffer))
{
Wait();
bytes_read = hid_get_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE);
attempts++;
}
return bytes_read;
}
void EVGAMouseController::Wait()
{
if(connection_type == EVGA_PERIPHERAL_CONNECTION_TYPE_WIRELESS)
{
std::this_thread::sleep_for(EVGA_PERIPHERAL_PACKET_DELAY);
}
}
bool EVGAMouseController::IsAsleepPacket(unsigned char *buffer)
{
const int expected_packet_size = 8;
unsigned char expected_buffer[expected_packet_size] =
{
0x00,
0xA4, 0x00, 0x02, 0x1D,
0x02, 0x80, 0x02
};
return BuffersAreEqual(buffer, expected_buffer, expected_packet_size);
}
bool EVGAMouseController::IsResponseNotReadyPacket(unsigned char *buffer)
{
const int expected_packet_size = 8;
unsigned char expected_buffer[expected_packet_size] =
{
0x00,
0xA0, 0x00, 0x02, 0x1D,
0x02, 0x80, 0x02
};
return BuffersAreEqual(buffer, expected_buffer, expected_packet_size);
}

View file

@ -0,0 +1,207 @@
/*-----------------------------------*\
| EVGAMouseController.h |
| |
| Definitions and types for EVGA X20 |
| Gaming Mouse. |
| |
| Cooper Knaak 1/23/2022 |
\*-----------------------------------*/
#pragma once
#include <string>
#include <array>
#include <hidapi/hidapi.h>
#include "RGBController.h"
#define EVGA_PERIPHERAL_PACKET_SIZE 65
#define EVGA_PERIPHERAL_LED_COUNT 3
#define HID_MAX_STR 255
enum
{
EVGA_PERIPHERAL_MODE_STATIC = 1,
EVGA_PERIPHERAL_MODE_BREATHING = 2,
EVGA_PERIPHERAL_MODE_RAINBOW = 3,
EVGA_PERIPHERAL_MODE_PULSE = 4,
EVGA_PERIPHERAL_MODE_TRIGGER = 6
};
enum
{
EVGA_PERIPHERAL_LED_FRONT = 0,
EVGA_PERIPHERAL_LED_WHEEL = 1,
EVGA_PERIPHERAL_LED_LOGO = 2
};
/*----------------------------------------------------------------------*\
| All values in this enum account for the required 0x0 byte at index 0 |
| when using hid* APIs. The byte controlling the red value of an LED is |
| at index 15 in the buffer passed to hid* APIs because there is a 0x0 |
| byte at the beginning of said buffer. It would only be index 14 in the |
| raw packet received by the peripheral. |
\*----------------------------------------------------------------------*/
enum
{
EVGA_PERIPHERAL_CONNECTION_TYPE_BYTE = 3,
EVGA_PERIPHERAL_LED_INDEX_BYTE = 8,
EVGA_PERIPHERAL_MODE_BYTE = 9,
/*--------------*\
| Range [0, 100] |
\*--------------*/
EVGA_PERIPHERAL_BRIGHTNESS_BYTE = 10,
/*--------------*\
| Range [0, 100] |
\*--------------*/
EVGA_PERIPHERAL_SPEED_BYTE = 11,
/*-------------------------------------------------------*\
| Determines when the lights initiate and terminate: |
| immediately, on key press, or on key release. |
| Currently unused because OpenRGB does not support this. |
\*-------------------------------------------------------*/
EVGA_PERIPHERAL_EFFECT_DURATION_BYTE = 13,
/*---------------------------------------------------------------------------------*\
| The byte at this index specifies how many colors are being passed in this packet. |
| The colors are a sequence of 3 bytes per color: red, green, then blue. Thus, when |
| passing 1 for this byte, the device will read the 3 next bytes as the color. When |
| passing 7, the device will read the next 21 bytes in sets of 3. |
\*---------------------------------------------------------------------------------*/
EVGA_PERIPHERAL_COLOR_COUNT_BYTE = 14,
EVGA_PERIPHERAL_RED_BYTE = 15,
EVGA_PERIPHERAL_GREEN_BYTE = 16,
EVGA_PERIPHERAL_BLUE_BYTE = 17,
};
enum
{
EVGA_PERIPHERAL_CONNECTION_TYPE_WIRED = 0,
EVGA_PERIPHERAL_CONNECTION_TYPE_WIRELESS = 2,
};
struct EVGAMouseControllerDeviceState
{
uint8_t mode;
uint8_t brightness;
uint8_t speed;
std::vector<RGBColor> colors;
};
class EVGAMouseController
{
public:
EVGAMouseController(hid_device* dev_handle, char *_path, int connection_type);
~EVGAMouseController();
std::string GetDeviceName();
std::string GetSerial();
std::string GetLocation();
/*---------------------------------------------------------------*\
| Gets the mode, colors, or entire state currently on the device. |
| OpenRGB does not support per-zone modes. All zones must be set |
| to the same mode. It's possible to use the vendor's software |
| to set each LED to separate states. These methods use the logo |
| LED (#2) as the source of truth. |
\*---------------------------------------------------------------*/
uint8_t GetMode();
EVGAMouseControllerDeviceState GetState();
/*-------------------------------------------------------------------------*\
| Gets the color of the given LED from the device. If a device is in a mode |
| with multiple colors, returns the first color in the list. |
\*-------------------------------------------------------------------------*/
RGBColor GetColorOfLed(int led);
inline void SetMode(uint8_t mode)
{
SetMode(mode, 0);
SetMode(mode, 1);
SetMode(mode, 2);
}
void SetMode(uint8_t mode, uint8_t index);
/*-----------------------------------*\
| Set a single LED to a single color. |
\*-----------------------------------*/
void SetLed(uint8_t index, uint8_t brightness, uint8_t speed, RGBColor color);
/*---------------------------------------------------*\
| Set the LED at the given index to a list of colors. |
\*---------------------------------------------------*/
void SetLed(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector<RGBColor>& colors);
/*---------------------------------------------------------------------------*\
| Set the LED at the given index to a list of colors, then activate the mode. |
\*---------------------------------------------------------------------------*/
void SetLedAndActivate(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector<RGBColor>& colors);
/*---------------------------------*\
| Set all LEDs to a list of colors. |
\*---------------------------------*/
void SetAllLeds(uint8_t brightness, uint8_t speed, const std::vector<RGBColor>& colors);
/*---------------------------------------------------------*\
| Set all LEDs to a list of colors, then activate the mode. |
\*---------------------------------------------------------*/
void SetAllLedsAndActivate(uint8_t brightness, uint8_t speed, const std::vector<RGBColor>& colors);
private:
/*----------------------------------------------------------------------------------------------------------------*\
| Sets the led to the given colors with the given brightness and speed. if activate is true, activates the current |
| mode. If false, just sets the colors. |
\*----------------------------------------------------------------------------------------------------------------*/
void SetLed(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector<RGBColor>& colors, bool activate);
/*-----------------------------------------------------------------------------*\
| Requests and stores the current mode and colors for all leds from the device. |
\*-----------------------------------------------------------------------------*/
void RefreshDeviceState();
/*----------------------------------------------------------------------------------*\
| Requests and stores the current mode and colors for the given led from the device. |
\*----------------------------------------------------------------------------------*/
void RefreshDeviceState(int led);
/*-----------------------------------------------------------------------------------*\
| Repeatedly reads a packet from the device until a valid packet is received. If no |
| such packet is received after max_attempts tries, returns false. Otherwise, returns |
| true. buffer must be an array with size EVGA_PERIPHERAL_PACKET_SIZE. |
\*-----------------------------------------------------------------------------------*/
bool ReadPacketOrLogErrors(unsigned char *buffer, int max_attempts);
/*-----------------------------------------------------------------------------------*\
| Repeatedly reads a packet from the device until a valid packet is received. If a |
| "response not ready" packet is returned, try again, up to a maximum number of tries |
| max_attempts. buffer must be an array with size EVGA_PERIPHERAL_PACKET_SIZE. |
| Returns the number of bytes read or -1 on error. |
\*-----------------------------------------------------------------------------------*/
int ReadPacketOrWait(unsigned char *buffer, int max_attempts);
/*-----------------------------------------------------------------------------*\
| Waits a predetermined amount of time to avoid sending packets to frequently. |
| In wireless mode, packets sent too close together might overwrite each other, |
| causing earlier ones to silently not propagate. Does not wait in connection |
| types that do not have this problem. |
\*-----------------------------------------------------------------------------*/
void Wait();
/*------------------------------------------------------------------------------*\
| Returns true if the packet received from the device signals that the device is |
| asleep and will not send or receive other packets. |
\*------------------------------------------------------------------------------*/
bool IsAsleepPacket(unsigned char *buffer);
/*------------------------------------------------------------------------------*\
| Returns true if the packet received from the device signals that the device is |
| still processing a request device state packet. In this case, the request to |
| read from the device should be retried at a later time. |
\*------------------------------------------------------------------------------*/
bool IsResponseNotReadyPacket(unsigned char *buffer);
std::string device_name;
std::string serial;
std::string location;
hid_device* dev;
int connection_type;
std::vector<EVGAMouseControllerDeviceState> led_states;
};

View file

@ -2,6 +2,7 @@
#include "LogManager.h"
#include "RGBController.h"
#include "RGBController_EVGAKeyboard.h"
#include "RGBController_EVGAMouse.h"
#include <hidapi/hidapi.h>
/*-----------------------------------------------------*\
@ -16,6 +17,12 @@
#define Z15_ANSI_PID 0x2608
#define Z20_ANSI_PID 0x260A
/*-----------------------------------------------------*\
| Mouse product IDs |
\*-----------------------------------------------------*/
#define X20_WIRED_PID 0x2420
#define X20_WIRELESS_ADAPTER_PID 0x2402
void DetectEVGAKeyboardControllers(hid_device_info* info, const std::string& name)
{
static const char* controller_name = "EVGA Keyboard Controller";
@ -32,6 +39,34 @@ void DetectEVGAKeyboardControllers(hid_device_info* info, const std::string& nam
}
}
REGISTER_HID_DETECTOR_IPU("EVGA Z15 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z15_ISO_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA Z15 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z15_ANSI_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA Z20 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z20_ANSI_PID, 1, 0x08, 0x4B);
void DetectEVGAMouse(hid_device_info* info, const std::string &, int connection_type)
{
hid_device* dev = hid_open_path(info->path);
if (dev)
{
EVGAMouseController* controller = new EVGAMouseController(dev, info->path, connection_type);
RGBController_EVGAMouse *rgb_controller = new RGBController_EVGAMouse(controller);
/*-------------------------*\
| Constructor sets the name |
\*-------------------------*/
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
void DetectWiredEVGAMouse(hid_device_info* info, const std::string &name)
{
DetectEVGAMouse(info, name, EVGA_PERIPHERAL_CONNECTION_TYPE_WIRED);
}
void DetectWirelessEVGAMouse(hid_device_info* info, const std::string &name)
{
DetectEVGAMouse(info, name, EVGA_PERIPHERAL_CONNECTION_TYPE_WIRELESS);
}
REGISTER_HID_DETECTOR_IPU("EVGA Z15 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z15_ISO_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA Z15 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z15_ANSI_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA Z20 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z20_ANSI_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA X20 Gaming Mouse", DetectWiredEVGAMouse, EVGA_USB_VID, X20_WIRED_PID, 2, 0xFFFF, 0);
REGISTER_HID_DETECTOR_IPU("EVGA X20 USB Receiver", DetectWirelessEVGAMouse, EVGA_USB_VID, X20_WIRELESS_ADAPTER_PID, 2, 0xFFFF, 0);

View file

@ -0,0 +1,253 @@
/*----------------------------------------------*\
| RGBController_EVGAMouse.cpp |
| |
| RGB Implementation for EVGA X20 Gaming Mouse. |
| |
| Cooper Knaak 1/23/2022 |
\*----------------------------------------------*/
#include "RGBController_EVGAMouse.h"
#include "Colors.h"
RGBController_EVGAMouse::RGBController_EVGAMouse(EVGAMouseController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetDeviceName();
vendor = "EVGA";
type = DEVICE_TYPE_MOUSE;
description = controller->GetDeviceName();
serial = controller->GetSerial();
location = controller->GetLocation();
mode Static;
Static.name = "Static";
Static.value = EVGA_PERIPHERAL_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
Static.brightness_min = EVGA_PERIPHERAL_BRIGHTNESS_MIN;
Static.brightness_max = EVGA_PERIPHERAL_BRIGHTNESS_MAX;
Static.brightness = EVGA_PERIPHERAL_BRIGHTNESS_MAX;
Static.colors_min = 1;
Static.colors_max = 1;
Static.colors.resize(Static.colors_max);
Static.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Static);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = EVGA_PERIPHERAL_MODE_BREATHING;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Breathing.brightness_min = EVGA_PERIPHERAL_BRIGHTNESS_MIN;
Breathing.brightness_max = EVGA_PERIPHERAL_BRIGHTNESS_MAX;
Breathing.brightness = EVGA_PERIPHERAL_BRIGHTNESS_MAX;
Breathing.colors_min = 2;
Breathing.colors_max = 2;
Breathing.colors = {COLOR_GREEN, COLOR_BLUE};
Breathing.speed_min = EVGA_PERIPHERAL_SPEED_SLOWEST;
Breathing.speed_max = EVGA_PERIPHERAL_SPEED_FASTEST;
Breathing.speed = EVGA_PERIPHERAL_SPEED_FASTEST;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Breathing);
mode Rainbow;
Rainbow.name = "Spectrum Cycle";
Rainbow.value = EVGA_PERIPHERAL_MODE_RAINBOW;
Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Rainbow.brightness_min = EVGA_PERIPHERAL_BRIGHTNESS_MIN;
Rainbow.brightness_max = EVGA_PERIPHERAL_BRIGHTNESS_MAX;
Rainbow.brightness = EVGA_PERIPHERAL_BRIGHTNESS_MAX;
Rainbow.speed_min = EVGA_PERIPHERAL_SPEED_SLOWEST;
Rainbow.speed_max = EVGA_PERIPHERAL_SPEED_FASTEST;
Rainbow.speed = EVGA_PERIPHERAL_SPEED_FASTEST;
Rainbow.colors = {COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_MAGENTA, COLOR_WHITE};
Rainbow.color_mode = MODE_COLORS_NONE;
modes.push_back(Rainbow);
mode Pulse;
Pulse.name = "Pulse";
Pulse.value = EVGA_PERIPHERAL_MODE_PULSE;
Pulse.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Pulse.brightness_min = EVGA_PERIPHERAL_BRIGHTNESS_MIN;
Pulse.brightness_max = EVGA_PERIPHERAL_BRIGHTNESS_MAX;
Pulse.brightness = EVGA_PERIPHERAL_BRIGHTNESS_MAX;
Pulse.speed_min = EVGA_PERIPHERAL_SPEED_SLOWEST;
Pulse.speed_max = EVGA_PERIPHERAL_SPEED_FASTEST;
Pulse.speed = EVGA_PERIPHERAL_SPEED_FASTEST;
Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC;
Pulse.colors_min = 2;
Pulse.colors_max = 7;
Pulse.colors = {COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_MAGENTA, COLOR_WHITE};
modes.push_back(Pulse);
mode Trigger;
Trigger.name = "Trigger";
/*-----------------------------------*\
| Pulse to Trigger skips from 4 to 6. |
\*-----------------------------------*/
Trigger.value = EVGA_PERIPHERAL_MODE_TRIGGER;
Trigger.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
Trigger.brightness_min = EVGA_PERIPHERAL_BRIGHTNESS_MIN;
Trigger.brightness_max = EVGA_PERIPHERAL_BRIGHTNESS_MAX;
Trigger.brightness = EVGA_PERIPHERAL_BRIGHTNESS_MAX;
Trigger.color_mode = MODE_COLORS_MODE_SPECIFIC;
Trigger.colors_min = 7;
Trigger.colors_max = 7;
Trigger.colors = {COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_MAGENTA, COLOR_WHITE};
modes.push_back(Trigger);
active_mode = EVGA_PERIPHERAL_MODE_STATIC;
Init_Controller();
SetupZones();
EVGAMouseControllerDeviceState current_state = controller->GetState();
for(unsigned int i = 0; i < modes.size(); i++)
{
if(modes[i].value == current_state.mode)
{
active_mode = i;
break;
}
}
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
{
modes[active_mode].colors = current_state.colors;
}
modes[active_mode].brightness = current_state.brightness;
modes[active_mode].speed = current_state.speed;
colors.resize(EVGA_PERIPHERAL_LED_COUNT);
for(unsigned int i = 0; i < colors.size(); i++)
{
colors[i] = controller->GetColorOfLed(i);
}
}
RGBController_EVGAMouse::~RGBController_EVGAMouse()
{
delete controller;
}
void RGBController_EVGAMouse::Init_Controller()
{
/*------------------------------------------------------------------------------------------*\
| Since each LED can have its own mode, each one needs to be its own zone with a single LED. |
\*------------------------------------------------------------------------------------------*/
zone front_zone;
front_zone.name = "Front";
front_zone.type = ZONE_TYPE_SINGLE;
front_zone.leds_min = 1;
front_zone.leds_max = 1;
front_zone.leds_count = 1;
front_zone.matrix_map = NULL;
zones.push_back(front_zone);
zone wheel_zone;
wheel_zone.name = "Scroll Wheel";
wheel_zone.type = ZONE_TYPE_SINGLE;
wheel_zone.leds_min = 1;
wheel_zone.leds_max = 1;
wheel_zone.leds_count = 1;
wheel_zone.matrix_map = NULL;
zones.push_back(wheel_zone);
zone logo_zone;
logo_zone.name = "Logo";
logo_zone.type = ZONE_TYPE_SINGLE;
logo_zone.leds_min = 1;
logo_zone.leds_max = 1;
logo_zone.leds_count = 1;
logo_zone.matrix_map = NULL;
zones.push_back(logo_zone);
led front_led;
front_led.name = "Front LED";
front_led.value = 0;
leds.push_back(front_led);
led wheel_led;
wheel_led.name = "Scroll Wheel LED";
wheel_led.value = 1;
leds.push_back(wheel_led);
led back_led;
back_led.name = "Back LED";
back_led.value = 2;
leds.push_back(back_led);
}
void RGBController_EVGAMouse::SetupZones()
{
SetupColors();
}
void RGBController_EVGAMouse::ResizeZone(int /* zone */, int /* new_size */)
{
/*--------------------------------------*\
| This device does not support resizing. |
\*--------------------------------------*/
}
void RGBController_EVGAMouse::DeviceUpdateLEDs()
{
for(unsigned int i = 0; i < colors.size(); i++)
{
controller->SetLed(i, modes[active_mode].brightness, modes[active_mode].speed, colors[i]);
}
}
void RGBController_EVGAMouse::UpdateZoneLEDs(int zone)
{
controller->SetLed(zone, modes[active_mode].brightness, modes[active_mode].speed, colors[zone]);
}
void RGBController_EVGAMouse::UpdateSingleLED(int led)
{
controller->SetLed(led, modes[active_mode].brightness, modes[active_mode].speed, colors[led]);
}
void RGBController_EVGAMouse::SetCustomMode()
{
/*-------------------------------------------------*\
| Static lets OpenRGB set device LEDs individually. |
\*-------------------------------------------------*/
active_mode = EVGA_PERIPHERAL_MODE_STATIC;
}
void RGBController_EVGAMouse::DeviceUpdateMode()
{
controller->SetMode(modes[active_mode].value);
/*--------------------------------------------------------------------*\
| Modes with specific colors should use their mode's colors. All other |
| modes should use the colors stored in this controller. |
\*--------------------------------------------------------------------*/
std::vector<RGBColor>* temp_colors = &colors;
/*-------------------------------------------------------------------------------------------------*\
| Rainbow does not have mode specific colors that can be controlled by OpenRGB, so it does not have |
| the corresponding flag. However, to properly activate it, you still must pass the correct list of |
| colors in the LED packet. Specifying the wrong number of colors causes the effect to restart the |
| cycle earlier in the sequence than it is supposed to. |
\*-------------------------------------------------------------------------------------------------*/
if((modes[active_mode].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) || modes[active_mode].value == EVGA_PERIPHERAL_MODE_RAINBOW)
{
temp_colors = &(modes[active_mode].colors);
}
if(modes[active_mode].value == EVGA_PERIPHERAL_MODE_BREATHING)
{
controller->SetAllLedsAndActivate(modes[active_mode].brightness, modes[active_mode].speed, *temp_colors);
}
else
{
controller->SetAllLeds(modes[active_mode].brightness, modes[active_mode].speed, *temp_colors);
}
}
void RGBController_EVGAMouse::DeviceSaveMode()
{
}
int RGBController_EVGAMouse::GetDeviceMode()
{
return controller->GetMode();
}

View file

@ -0,0 +1,43 @@
/*-----------------------------------------*\
| RGBController_EVGAMouse.h |
| |
| RGB Interface for EVGA X20 Gaming Mouse. |
| |
| Cooper Knaak 1/23/2022 |
\*-----------------------------------------*/
#pragma once
#include "RGBController.h"
#include "EVGAMouseController.h"
#include <vector>
#define EVGA_PERIPHERAL_BRIGHTNESS_MIN 0
#define EVGA_PERIPHERAL_BRIGHTNESS_MAX 100
#define EVGA_PERIPHERAL_SPEED_SLOWEST 0
#define EVGA_PERIPHERAL_SPEED_FASTEST 100
class RGBController_EVGAMouse : public RGBController
{
public:
RGBController_EVGAMouse(EVGAMouseController* evga);
~RGBController_EVGAMouse();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void SetCustomMode();
void DeviceUpdateMode();
void DeviceSaveMode();
private:
void Init_Controller();
int GetDeviceMode();
EVGAMouseController* controller;
};

View file

@ -341,8 +341,10 @@ HEADERS +=
Controllers/EVGATuringGPUController/RGBController_EVGAGPUv2.h \
Controllers/EVGAAmpereGPUController/EVGAGPUv3Controller.h \
Controllers/EVGAAmpereGPUController/RGBController_EVGAGPUv3.h \
Controllers/EVGAUSBController/EVGAKeyboardController.h \
Controllers/EVGAUSBController/RGBController_EVGAKeyboard.h \
Controllers/EVGAUSBController/EVGAKeyboardController.h \
Controllers/EVGAUSBController/EVGAMouseController.h \
Controllers/EVGAUSBController/RGBController_EVGAKeyboard.h \
Controllers/EVGAUSBController/RGBController_EVGAMouse.h \
Controllers/EVisionKeyboardController/EVisionKeyboardController.h \
Controllers/EVisionKeyboardController/RGBController_EVisionKeyboard.h \
Controllers/FanBusController/FanBusController.h \
@ -800,9 +802,11 @@ SOURCES +=
Controllers/EVGAAmpereGPUController/EVGAGPUv3Controller.cpp \
Controllers/EVGAAmpereGPUController/EVGAAmpereGPUControllerDetect.cpp \
Controllers/EVGAAmpereGPUController/RGBController_EVGAGPUv3.cpp \
Controllers/EVGAUSBController/EVGAKeyboardController.cpp \
Controllers/EVGAUSBController/EVGAKeyboardController.cpp \
Controllers/EVGAUSBController/EVGAMouseController.cpp \
Controllers/EVGAUSBController/EVGAUSBControllerDetect.cpp \
Controllers/EVGAUSBController/RGBController_EVGAKeyboard.cpp \
Controllers/EVGAUSBController/RGBController_EVGAKeyboard.cpp \
Controllers/EVGAUSBController/RGBController_EVGAMouse.cpp \
Controllers/EVisionKeyboardController/EVisionKeyboardController.cpp \
Controllers/EVisionKeyboardController/EVisionKeyboardControllerDetect.cpp \
Controllers/EVisionKeyboardController/RGBController_EVisionKeyboard.cpp \