diff --git a/Controllers/RoccatController/RGBController_RoccatKova.cpp b/Controllers/RoccatController/RGBController_RoccatKova.cpp new file mode 100644 index 00000000..559b4cff --- /dev/null +++ b/Controllers/RoccatController/RGBController_RoccatKova.cpp @@ -0,0 +1,144 @@ +/*-----------------------------------------*\ +| RGBController_RoccatKova.cpp | +| | +| RGB Controller for Roccat Kova | +| | +| Gustash 01/12/2022 | +\*-----------------------------------------*/ + +#include "RGBController_RoccatKova.h" + +/**------------------------------------------------------------------*\ + @name Roccat Kova + @category Mouse + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors RoccatControllerDetect + @comment Color Flow mode is only supported starting at the first + preset color in the mouse's memory, and color offsets for each LED + are not supported. You'd need to use Swarm if you intend to use that + specific feature. +*/ + +RGBController_RoccatKova::RGBController_RoccatKova(RoccatKovaController* controller_ptr) +{ + controller = controller_ptr; + + type = DEVICE_TYPE_MOUSE; + name = "Roccat Kova"; + vendor = "Roccat"; + description = "Controller compatible with the Roccat Kova gaming mouse"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + version = controller->GetFirmwareVersion(); + + mode Static; + Static.name = "Static"; + Static.value = ROCCAT_KOVA_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode ColorFlow; + ColorFlow.name = "Color Flow"; + ColorFlow.value = ROCCAT_KOVA_MODE_COLOR_FLOW; + ColorFlow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + ColorFlow.color_mode = MODE_COLORS_RANDOM; + ColorFlow.speed_min = ROCCAT_KOVA_SPEED_MIN; + ColorFlow.speed_max = ROCCAT_KOVA_SPEED_MAX; + modes.push_back(ColorFlow); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = ROCCAT_KOVA_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Flashing.color_mode = MODE_COLORS_PER_LED; + Flashing.speed_min = ROCCAT_KOVA_SPEED_MIN; + Flashing.speed_max = ROCCAT_KOVA_SPEED_MAX; + modes.push_back(Flashing); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ROCCAT_KOVA_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = ROCCAT_KOVA_SPEED_MIN; + Breathing.speed_max = ROCCAT_KOVA_SPEED_MAX; + modes.push_back(Breathing); + + mode Off; + Off.name = "Off"; + Off.value = 0x00; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_RoccatKova::~RGBController_RoccatKova() +{ + delete controller; +} + +void RGBController_RoccatKova::SetupZones() +{ + zone Mouse; + Mouse.name = "Mouse"; + Mouse.type = ZONE_TYPE_LINEAR; + Mouse.leds_count = ROCCAT_KOVA_LED_COUNT; + Mouse.leds_min = ROCCAT_KOVA_LED_COUNT; + Mouse.leds_max = ROCCAT_KOVA_LED_COUNT; + Mouse.matrix_map = NULL; + zones.push_back(Mouse); + + led WheelLED; + WheelLED.name = "Wheel LED"; + WheelLED.value = ROCCAT_KOVA_WHEEL_IDX; + leds.push_back(WheelLED); + + led StripeLED; + StripeLED.name = "Stripe LED"; + StripeLED.value = ROCCAT_KOVA_PIPE_IDX; + leds.push_back(StripeLED); + + SetupColors(); +} + +void RGBController_RoccatKova::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatKova::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_RoccatKova::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_RoccatKova::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_RoccatKova::DeviceUpdateMode() +{ + mode &active = modes[active_mode]; + int mode = active.value; + bool is_color_flow = active.color_mode == MODE_COLORS_RANDOM; + if(active.value == ROCCAT_KOVA_MODE_COLOR_FLOW) + { + mode = ROCCAT_KOVA_MODE_STATIC; + is_color_flow = true; + } + + controller->SetColor(colors[0], colors[1], mode, active.speed, is_color_flow); +} diff --git a/Controllers/RoccatController/RGBController_RoccatKova.h b/Controllers/RoccatController/RGBController_RoccatKova.h new file mode 100644 index 00000000..d3a551ee --- /dev/null +++ b/Controllers/RoccatController/RGBController_RoccatKova.h @@ -0,0 +1,32 @@ +/*-----------------------------------------*\ +| RGBController_RoccatKova.h | +| | +| RGB Controller for Roccat Kova | +| | +| Gustash 01/12/2022 | +\*-----------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatKovaController.h" + +class RGBController_RoccatKova : public RGBController +{ +public: + RGBController_RoccatKova(RoccatKovaController *controller_ptr); + ~RGBController_RoccatKova(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatKovaController *controller; +}; diff --git a/Controllers/RoccatController/RoccatControllerDetect.cpp b/Controllers/RoccatController/RoccatControllerDetect.cpp index c9fa6a8d..5ebc3a05 100644 --- a/Controllers/RoccatController/RoccatControllerDetect.cpp +++ b/Controllers/RoccatController/RoccatControllerDetect.cpp @@ -10,11 +10,13 @@ #include "RoccatBurstController.h" #include "RoccatKoneAimoController.h" #include "RoccatVulcanAimoController.h" +#include "RoccatKovaController.h" #include "RGBController.h" #include "RGBController_RoccatBurst.h" #include "RGBController_RoccatHordeAimo.h" #include "RGBController_RoccatKoneAimo.h" #include "RGBController_RoccatVulcanAimo.h" +#include "RGBController_RoccatKova.h" #include #include @@ -26,6 +28,7 @@ #define ROCCAT_HORDE_AIMO_PID 0x303E #define ROCCAT_BURST_CORE_PID 0x2DE6 #define ROCCAT_BURST_PRO_PID 0x2DE1 +#define ROCCAT_KOVA_PID 0x2CEE void DetectRoccatMouseControllers(hid_device_info* info, const std::string& name) { @@ -157,6 +160,19 @@ void DetectRoccatBurstProControllers(hid_device_info* info, const std::string& n } } +void DetectRoccatKovaControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatKovaController * controller = new RoccatKovaController(dev, info->path); + RGBController_RoccatKova * rgb_controller = new RGBController_RoccatKova(controller); + rgb_controller->name = name; + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + REGISTER_PRE_DETECTION_HOOK(ResetRoccatVulcanAimoControllersPaths); REGISTER_HID_DETECTOR_IPU("Roccat Kone Aimo", DetectRoccatMouseControllers, ROCCAT_VID, ROCCAT_KONE_AIMO_PID, 0, 0x0B, 0 ); @@ -165,3 +181,4 @@ REGISTER_HID_DETECTOR_IP ("Roccat Vulcan 120-Series Aimo", DetectRoccatVulcanAi REGISTER_HID_DETECTOR_IPU("Roccat Horde Aimo", DetectRoccatHordeAimoKeyboardControllers, ROCCAT_VID, ROCCAT_HORDE_AIMO_PID, 1, 0x0B, 0); REGISTER_HID_DETECTOR_IPU("Roccat Burst Core", DetectRoccatBurstCoreControllers, ROCCAT_VID, ROCCAT_BURST_CORE_PID, 3, 0xFF01, 1); REGISTER_HID_DETECTOR_IPU("Roccat Burst Pro", DetectRoccatBurstProControllers, ROCCAT_VID, ROCCAT_BURST_PRO_PID, 3, 0xFF01, 1); +REGISTER_HID_DETECTOR_IPU("Roccat Kova", DetectRoccatKovaControllers, ROCCAT_VID, ROCCAT_KOVA_PID, 0, 0x0B, 0); diff --git a/Controllers/RoccatController/RoccatKovaController.cpp b/Controllers/RoccatController/RoccatKovaController.cpp new file mode 100644 index 00000000..d967635f --- /dev/null +++ b/Controllers/RoccatController/RoccatKovaController.cpp @@ -0,0 +1,143 @@ +/*-----------------------------------------*\ +| RoccatKovaController.cpp | +| | +| Controller for Roccat Kova | +| | +| Gustash 01/12/2022 | +\*-----------------------------------------*/ + +#include "RoccatKovaController.h" +#include "LogManager.h" +#include + +RoccatKovaController::RoccatKovaController(hid_device* dev_handle, char *path) +{ + dev = dev_handle; + location = path; + + SendInitialPacket(); + FetchFirmwareVersion(); +} + +RoccatKovaController::~RoccatKovaController() +{ + hid_close(dev); +} + +std::string RoccatKovaController::GetLocation() +{ + return ("HID: " + location); +} + +std::string RoccatKovaController::GetSerial() +{ + const uint8_t sz = ROCCAT_KOVA_HID_MAX_STR; + wchar_t tmp[sz]; + + uint8_t ret = hid_get_serial_number_string(dev, tmp, sz); + + if(ret != 0) + { + LOG_DEBUG("[Roccat Kova] Get HID Serial string failed"); + return ""; + } + + std::wstring w_tmp = std::wstring(tmp); + std::string serial = std::string(w_tmp.begin(), w_tmp.end()); + + return serial; +} + +std::string RoccatKovaController::GetFirmwareVersion() +{ + return firmware_version; +} + +void RoccatKovaController::SetColor(RGBColor color_wheel, + RGBColor color_stripe, + uint8_t mode, + uint8_t speed, + bool color_flow) +{ + bool is_off = mode == ROCCAT_KOVA_MODE_OFF; + uint8_t report_buf[ROCCAT_KOVA_PROFILE_WRITE_PACKET_SIZE] {00}; + FetchProfileData(report_buf); + + report_buf[0x0] = ROCCAT_KOVA_PROFILE_REPORT_ID; + report_buf[0x1] = ROCCAT_KOVA_PROFILE_WRITE_PACKET_SIZE; + + report_buf[ROCCAT_KOVA_FLAGS_IDX] |= ROCCAT_KOVA_USE_CUSTOM_COLORS_MASK; + if(is_off) + { + report_buf[ROCCAT_KOVA_FLAGS_IDX] &= ~ROCCAT_KOVA_LIGHTS_ON_MASK; + } + else + { + report_buf[ROCCAT_KOVA_FLAGS_IDX] |= ROCCAT_KOVA_LIGHTS_ON_MASK; + } + + /*-------------------------------------------------*\ + | Set colors for each LED and reset the selected | + | preset color to ensure consistency | + \*-------------------------------------------------*/ + report_buf[ROCCAT_KOVA_WHEEL_IDX] = 0x0; + report_buf[ROCCAT_KOVA_WHEEL_IDX + ROCCAT_KOVA_R_OFFSET] = RGBGetRValue(color_wheel); + report_buf[ROCCAT_KOVA_WHEEL_IDX + ROCCAT_KOVA_G_OFFSET] = RGBGetGValue(color_wheel); + report_buf[ROCCAT_KOVA_WHEEL_IDX + ROCCAT_KOVA_B_OFFSET] = RGBGetBValue(color_wheel); + report_buf[ROCCAT_KOVA_PIPE_IDX] = 0x0; + report_buf[ROCCAT_KOVA_PIPE_IDX + ROCCAT_KOVA_R_OFFSET] = RGBGetRValue(color_stripe); + report_buf[ROCCAT_KOVA_PIPE_IDX + ROCCAT_KOVA_G_OFFSET] = RGBGetGValue(color_stripe); + report_buf[ROCCAT_KOVA_PIPE_IDX + ROCCAT_KOVA_B_OFFSET] = RGBGetBValue(color_stripe); + + report_buf[ROCCAT_KOVA_COLOR_FLOW_IDX] = color_flow; + if(!is_off) + { + report_buf[ROCCAT_KOVA_MODE_IDX] = mode; + } + report_buf[ROCCAT_KOVA_EFFECT_SPEED_IDX] = speed; + + uint16_t checksum = GenerateChecksum(report_buf, sizeof(report_buf) - 2); + + report_buf[ROCCAT_KOVA_CHECKSUM_IDX] = checksum & 0xFF; + report_buf[ROCCAT_KOVA_CHECKSUM_IDX + 1] = checksum >> 8; + + hid_send_feature_report(dev, report_buf, ROCCAT_KOVA_PROFILE_WRITE_PACKET_SIZE); +} + +void RoccatKovaController::SendInitialPacket() +{ + uint8_t buf[ROCCAT_KOVA_INIT_WRITE_PACKET_SIZE] {00}; + buf[0x00] = ROCCAT_KOVA_INIT_REPORT_ID; + buf[0x01] = 0x00; + buf[0x02] = 0x80; + hid_send_feature_report(dev, buf, ROCCAT_KOVA_INIT_WRITE_PACKET_SIZE); +} + +void RoccatKovaController::FetchFirmwareVersion() +{ + uint8_t buf[ROCCAT_KOVA_VERSION_READ_PACKET_SIZE] {00}; + buf[0x0] = ROCCAT_KOVA_VERSION_REPORT_ID; + + hid_get_feature_report(dev, buf, ROCCAT_KOVA_VERSION_READ_PACKET_SIZE); + + uint8_t version = buf[ROCCAT_KOVA_FIRMWARE_VERSION_IDX]; + char version_str[5] {00}; + sprintf(version_str, "%.2f", version / 100.); + firmware_version = version_str; +} + +void RoccatKovaController::FetchProfileData(uint8_t *buf) +{ + buf[0x00] = ROCCAT_KOVA_PROFILE_REPORT_ID; + hid_get_feature_report(dev, buf, ROCCAT_KOVA_PROFILE_WRITE_PACKET_SIZE); +} + +uint16_t RoccatKovaController::GenerateChecksum(uint8_t *buf, size_t length) +{ + uint16_t checksum = 0x0; + for (uint8_t idx = 0; idx < length; idx++) + { + checksum += buf[idx]; + } + return checksum; +} diff --git a/Controllers/RoccatController/RoccatKovaController.h b/Controllers/RoccatController/RoccatKovaController.h new file mode 100644 index 00000000..f7516169 --- /dev/null +++ b/Controllers/RoccatController/RoccatKovaController.h @@ -0,0 +1,88 @@ +/*-----------------------------------------*\ +| RoccatKovaController.h | +| | +| Controller for Roccat Kova | +| | +| Gustash 01/12/2022 | +\*-----------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include + +#define ROCCAT_KOVA_HID_MAX_STR 255 +#define ROCCAT_KOVA_LED_COUNT 2 +#define ROCCAT_KOVA_SPEED_MIN 1 +#define ROCCAT_KOVA_SPEED_MAX 3 +#define ROCCAT_KOVA_INIT_REPORT_ID 4 +#define ROCCAT_KOVA_INIT_WRITE_PACKET_SIZE 3 +#define ROCCAT_KOVA_PROFILE_REPORT_ID 6 +#define ROCCAT_KOVA_PROFILE_WRITE_PACKET_SIZE 28 +#define ROCCAT_KOVA_VERSION_REPORT_ID 9 +#define ROCCAT_KOVA_VERSION_READ_PACKET_SIZE 8 +/*#define NUM_OF_DPI_SWITCHES 5*/ + +enum +{ + ROCCAT_KOVA_FIRMWARE_VERSION_IDX = 2, + /*ROCCAT_KOVA_SELECTED_PROFILE_IDX = 2,*/ + /*ROCCAT_KOVA_UNKNOWN_3_IDX = 3,*/ + /*ROCCAT_KOVA_UNKNOWN_4_IDX = 4,*/ + /*ROCCAT_KOVA_ORIENTATION_IDX = 5,*/ + /*ROCCAT_KOVA_DPI_SWITCHER_IDX = 6,*/ + /*ROCCAT_KOVA_DPI_SPEED_IDX = 7,*/ + /*ROCCAT_KOVA_SELECTED_DPI_IDX = 12,*/ + /*ROCCAT_KOVA_POLLING_RATE_IDX = 13,*/ + ROCCAT_KOVA_FLAGS_IDX = 14, + ROCCAT_KOVA_COLOR_FLOW_IDX = 15, + ROCCAT_KOVA_MODE_IDX = 16, + ROCCAT_KOVA_EFFECT_SPEED_IDX = 17, + ROCCAT_KOVA_PIPE_IDX = 18, + ROCCAT_KOVA_WHEEL_IDX = 22, + ROCCAT_KOVA_CHECKSUM_IDX = 26, +}; + +#define ROCCAT_KOVA_R_OFFSET 1 +#define ROCCAT_KOVA_G_OFFSET 2 +#define ROCCAT_KOVA_B_OFFSET 3 + +#define ROCCAT_KOVA_USE_CUSTOM_COLORS_MASK 0b00110000 +#define ROCCAT_KOVA_LIGHTS_ON_MASK 0b00000011 + +enum +{ + ROCCAT_KOVA_MODE_OFF = 0x00, + ROCCAT_KOVA_MODE_STATIC = 0x01, + ROCCAT_KOVA_MODE_FLASHING = 0x02, + ROCCAT_KOVA_MODE_BREATHING = 0x03, + ROCCAT_KOVA_MODE_COLOR_FLOW = 0xFF, +}; + +class RoccatKovaController +{ +public: + RoccatKovaController(hid_device* dev_handle, char *path); + ~RoccatKovaController(); + + std::string GetLocation(); + std::string GetSerial(); + std::string GetFirmwareVersion(); + + void SetColor(RGBColor color_wheel, + RGBColor color_stripe, + uint8_t mode, + uint8_t speed, + bool color_flow); + +private: + hid_device* dev; + std::string location; + std::string firmware_version; + + void SendInitialPacket(); + void FetchProfileData(uint8_t *buf); + void FetchFirmwareVersion(); + + uint16_t GenerateChecksum(uint8_t *buf, size_t length); +}; diff --git a/OpenRGB.pro b/OpenRGB.pro index 4aea62b2..b4736f29 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -556,10 +556,12 @@ HEADERS += Controllers/RoccatController/RGBController_RoccatBurst.h \ Controllers/RoccatController/RGBController_RoccatHordeAimo.h \ Controllers/RoccatController/RGBController_RoccatKoneAimo.h \ + Controllers/RoccatController/RGBController_RoccatKova.h \ Controllers/RoccatController/RGBController_RoccatVulcanAimo.h \ Controllers/RoccatController/RoccatBurstController.h \ Controllers/RoccatController/RoccatHordeAimoController.h \ Controllers/RoccatController/RoccatKoneAimoController.h \ + Controllers/RoccatController/RoccatKovaController.h \ Controllers/RoccatController/RoccatVulcanAimoController.h \ Controllers/RoccatController/RoccatVulcanAimoLayouts.h \ Controllers/SapphireGPUController/SapphireNitroGlowV1Controller.h \ @@ -1146,10 +1148,12 @@ SOURCES += Controllers/RoccatController/RGBController_RoccatBurst.cpp \ Controllers/RoccatController/RGBController_RoccatHordeAimo.cpp \ Controllers/RoccatController/RGBController_RoccatKoneAimo.cpp \ + Controllers/RoccatController/RGBController_RoccatKova.cpp \ Controllers/RoccatController/RGBController_RoccatVulcanAimo.cpp \ Controllers/RoccatController/RoccatBurstController.cpp \ Controllers/RoccatController/RoccatHordeAimoController.cpp \ Controllers/RoccatController/RoccatKoneAimoController.cpp \ + Controllers/RoccatController/RoccatKovaController.cpp \ Controllers/RoccatController/RoccatVulcanAimoController.cpp \ Controllers/RoccatController/RoccatControllerDetect.cpp \ Controllers/SapphireGPUController/SapphireNitroGlowV1Controller.cpp \