From 3fabbecabddfd77b583ab21d1b2bf68bd57b5775 Mon Sep 17 00:00:00 2001 From: Cooper Knaak Date: Thu, 5 May 2022 00:45:00 +0000 Subject: [PATCH] Add Support for EVGA X20 Gaming Mouse Commit amended to remove udev rules (which is now autogenerated) by Adam Honse --- .../EVGAUSBController/EVGAMouseController.cpp | 346 ++++++++++++++++++ .../EVGAUSBController/EVGAMouseController.h | 207 +++++++++++ .../EVGAUSBControllerDetect.cpp | 41 ++- .../RGBController_EVGAMouse.cpp | 253 +++++++++++++ .../RGBController_EVGAMouse.h | 43 +++ OpenRGB.pro | 12 +- 6 files changed, 895 insertions(+), 7 deletions(-) create mode 100644 Controllers/EVGAUSBController/EVGAMouseController.cpp create mode 100644 Controllers/EVGAUSBController/EVGAMouseController.h create mode 100644 Controllers/EVGAUSBController/RGBController_EVGAMouse.cpp create mode 100644 Controllers/EVGAUSBController/RGBController_EVGAMouse.h diff --git a/Controllers/EVGAUSBController/EVGAMouseController.cpp b/Controllers/EVGAUSBController/EVGAMouseController.cpp new file mode 100644 index 000000000..273c64761 --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAMouseController.cpp @@ -0,0 +1,346 @@ +/*-------------------------------------------------*\ +| EVGAMouseController.cpp | +| | +| Driver for EVGA X20 Gaming Mouse RGB Controller. | +| | +| Cooper Knaak 1/23/2022 | +\*-------------------------------------------------*/ + +#include "EVGAMouseController.h" +#include "LogManager.h" + +#include +#include +#include +#include + + +#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) +{ + SetLed(index, brightness, speed, std::vector({color}), false); +} + +void EVGAMouseController::SetLed(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector& colors) +{ + SetLed(index, brightness, speed, colors, false); +} + +void EVGAMouseController::SetLedAndActivate(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector& 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& 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& 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& 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(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::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(connection_type), 0x1D, + 0x02, 0x80, 0x02 + }; + buffer[EVGA_PERIPHERAL_LED_INDEX_BYTE] = static_cast(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); +} + diff --git a/Controllers/EVGAUSBController/EVGAMouseController.h b/Controllers/EVGAUSBController/EVGAMouseController.h new file mode 100644 index 000000000..750b7a3fc --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAMouseController.h @@ -0,0 +1,207 @@ +/*-----------------------------------*\ +| EVGAMouseController.h | +| | +| Definitions and types for EVGA X20 | +| Gaming Mouse. | +| | +| Cooper Knaak 1/23/2022 | +\*-----------------------------------*/ + +#pragma once + +#include +#include +#include +#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 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& 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& colors); + /*---------------------------------*\ + | Set all LEDs to a list of colors. | + \*---------------------------------*/ + void SetAllLeds(uint8_t brightness, uint8_t speed, const std::vector& colors); + /*---------------------------------------------------------*\ + | Set all LEDs to a list of colors, then activate the mode. | + \*---------------------------------------------------------*/ + void SetAllLedsAndActivate(uint8_t brightness, uint8_t speed, const std::vector& 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& 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 led_states; +}; + diff --git a/Controllers/EVGAUSBController/EVGAUSBControllerDetect.cpp b/Controllers/EVGAUSBController/EVGAUSBControllerDetect.cpp index 9f0561f21..abae564c8 100644 --- a/Controllers/EVGAUSBController/EVGAUSBControllerDetect.cpp +++ b/Controllers/EVGAUSBController/EVGAUSBControllerDetect.cpp @@ -2,6 +2,7 @@ #include "LogManager.h" #include "RGBController.h" #include "RGBController_EVGAKeyboard.h" +#include "RGBController_EVGAMouse.h" #include /*-----------------------------------------------------*\ @@ -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); diff --git a/Controllers/EVGAUSBController/RGBController_EVGAMouse.cpp b/Controllers/EVGAUSBController/RGBController_EVGAMouse.cpp new file mode 100644 index 000000000..eb9414aec --- /dev/null +++ b/Controllers/EVGAUSBController/RGBController_EVGAMouse.cpp @@ -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* 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(); +} + diff --git a/Controllers/EVGAUSBController/RGBController_EVGAMouse.h b/Controllers/EVGAUSBController/RGBController_EVGAMouse.h new file mode 100644 index 000000000..4da0a1c45 --- /dev/null +++ b/Controllers/EVGAUSBController/RGBController_EVGAMouse.h @@ -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 + +#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; + +}; + diff --git a/OpenRGB.pro b/OpenRGB.pro index a35b617b0..5b2384426 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -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 \