diff --git a/Controllers/RoccatController/RoccatControllerDetect.cpp b/Controllers/RoccatController/RoccatControllerDetect.cpp index a3cb3f1a..7cd4cef0 100644 --- a/Controllers/RoccatController/RoccatControllerDetect.cpp +++ b/Controllers/RoccatController/RoccatControllerDetect.cpp @@ -13,6 +13,7 @@ #include "RoccatKoneAimoController.h" #include "RoccatKoneProController.h" #include "RoccatKoneProAirController.h" +#include "RoccatKoneXPController.h" #include "RoccatSenseAimoController.h" #include "RoccatVulcanKeyboardController.h" #include "RoccatKovaController.h" @@ -24,6 +25,7 @@ #include "RGBController_RoccatKoneAimo.h" #include "RGBController_RoccatKonePro.h" #include "RGBController_RoccatKoneProAir.h" +#include "RGBController_RoccatKoneXP.h" #include "RGBController_RoccatSenseAimo.h" #include "RGBController_RoccatVulcanKeyboard.h" #include "RGBController_RoccatKova.h" @@ -50,6 +52,7 @@ #define ROCCAT_KONE_PRO_PID 0x2C88 #define ROCCAT_KONE_PRO_AIR_PID 0x2C8E #define ROCCAT_KONE_PRO_AIR_WIRED_PID 0x2C92 +#define ROCCAT_KONE_XP_PID 0x2C8B #define ROCCAT_KOVA_PID 0x2CEE /*-----------------------------------------------------------------*\ @@ -250,6 +253,19 @@ void DetectRoccatKoneProAirControllers(hid_device_info* info, const std::string& } } +void DetectRoccatKoneXPControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatKoneXPController * controller = new RoccatKoneXPController(dev, info->path); + RGBController_RoccatKoneXP * rgb_controller = new RGBController_RoccatKoneXP(controller); + rgb_controller->name = name; + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + void DetectRoccatKovaControllers(hid_device_info* info, const std::string& name) { hid_device* dev = hid_open_path(info->path); @@ -318,6 +334,8 @@ REGISTER_HID_DETECTOR_IPU("Roccat Kone Pro", DetectRoccatKoneProC REGISTER_HID_DETECTOR_IPU("Roccat Kone Pro Air", DetectRoccatKoneProAirControllers, ROCCAT_VID, ROCCAT_KONE_PRO_AIR_PID, 2, 0xFF00, 1 ); REGISTER_HID_DETECTOR_IPU("Roccat Kone Pro Air (Wired)", DetectRoccatKoneProAirControllers, ROCCAT_VID, ROCCAT_KONE_PRO_AIR_WIRED_PID, 1, 0xFF13, 1 ); +REGISTER_HID_DETECTOR_IPU("Roccat Kone XP", DetectRoccatKoneXPControllers, ROCCAT_VID, ROCCAT_KONE_XP_PID, 3, 0xFF01, 1 ); + REGISTER_HID_DETECTOR_IPU("Roccat Kova", DetectRoccatKovaControllers, ROCCAT_VID, ROCCAT_KOVA_PID, 0, 0x0B, 0 ); /*-----------------------------------------------------------------*\ diff --git a/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.cpp b/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.cpp new file mode 100644 index 00000000..74132039 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.cpp @@ -0,0 +1,287 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKoneXP.cpp | +| | +| RGBController for Roccat Kone XP | +| | +| Mola19 12 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "RGBController_RoccatKoneXP.h" + +/**------------------------------------------------------------------*\ + @name Roccat Kone XP Mouse + @category Mouse + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRoccatKoneXPControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RoccatKoneXP::RGBController_RoccatKoneXP(RoccatKoneXPController* controller_ptr) +{ + controller = controller_ptr; + + name = "Roccat Kone XP"; + vendor = "Roccat"; + type = DEVICE_TYPE_MOUSE; + description = "Roccat Kone XP Mouse"; + version = controller->GetVersion(); + location = controller->GetLocation(); + serial = controller->GetSerial(); + + + mode Direct; + Direct.name = "Direct"; + Direct.value = ROCCAT_KONE_XP_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = ROCCAT_KONE_XP_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = ROCCAT_KONE_XP_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Static.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Static.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = ROCCAT_KONE_XP_MODE_WAVE; + Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Rainbow.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Rainbow.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + Rainbow.speed = ROCCAT_KONE_XP_SPEED_DEFAULT; + Rainbow.speed_min = ROCCAT_KONE_XP_SPEED_MIN; + Rainbow.speed_max = ROCCAT_KONE_XP_SPEED_MAX; + modes.push_back(Rainbow); + + mode Blinking; + Blinking.name = "Blinking"; + Blinking.value = ROCCAT_KONE_XP_MODE_BLINKING; + Blinking.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Blinking.color_mode = MODE_COLORS_PER_LED; + Blinking.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Blinking.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Blinking.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + Blinking.speed = ROCCAT_KONE_XP_SPEED_DEFAULT; + Blinking.speed_min = ROCCAT_KONE_XP_SPEED_MIN; + Blinking.speed_max = ROCCAT_KONE_XP_SPEED_MAX; + modes.push_back(Blinking); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ROCCAT_KONE_XP_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Breathing.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Breathing.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + Breathing.speed = ROCCAT_KONE_XP_SPEED_DEFAULT; + Breathing.speed_min = ROCCAT_KONE_XP_SPEED_MIN; + Breathing.speed_max = ROCCAT_KONE_XP_SPEED_MAX; + modes.push_back(Breathing); + + mode Heartbeat; + Heartbeat.name = "Heartbeat"; + Heartbeat.value = ROCCAT_KONE_XP_MODE_HEARTBEAT; + Heartbeat.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Heartbeat.color_mode = MODE_COLORS_PER_LED; + Heartbeat.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Heartbeat.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Heartbeat.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + Heartbeat.speed = ROCCAT_KONE_XP_SPEED_DEFAULT; + Heartbeat.speed_min = ROCCAT_KONE_XP_SPEED_MIN; + Heartbeat.speed_max = ROCCAT_KONE_XP_SPEED_MAX; + modes.push_back(Heartbeat); + + mode Photon; + Photon.name = "Photon FX"; + Photon.value = ROCCAT_KONE_XP_MODE_PHOTON_FX; + Photon.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Photon.color_mode = MODE_COLORS_NONE; + Photon.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Photon.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Photon.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + modes.push_back(Photon); + + /*---------------------------------------------------------------------*\ + | This is the default mode for software modes, while swarm isn't active | + \*---------------------------------------------------------------------*/ + + mode Default; + Default.name = "Default"; + Default.value = ROCCAT_KONE_XP_MODE_DEFAULT; + Default.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Default.color_mode = MODE_COLORS_NONE; + Default.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Default.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Default.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + modes.push_back(Default); + + SetupZones(); + + uint8_t active_profile = controller->GetActiveProfile(); + controller->SetReadProfile(active_profile); + controller->WaitUntilReady(); + + roccat_kone_xp_mode_struct active = controller->GetMode(); + + for(uint32_t i = 0; i < modes.size(); i++) + { + if(modes[i].value == active.mode) + { + active_mode = i; + break; + } + + /*----------------------------------------------*\ + | If no mode was found, select 0th mode (direct) | + \*----------------------------------------------*/ + if(i == modes.size() - 1) + { + active_mode = 0; + } + } + + modes[active_mode].speed = active.speed; + modes[active_mode].brightness = active.brightness; + + for(uint8_t i = 0; i < 20; i++) + { + colors[i] = active.colors[i].color; + } +} + +RGBController_RoccatKoneXP::~RGBController_RoccatKoneXP() +{ + delete controller; +} + +void RGBController_RoccatKoneXP::SetupZones() +{ + zone left; + left.name = "Left"; + left.type = ZONE_TYPE_LINEAR; + left.leds_min = 9; + left.leds_max = 9; + left.leds_count = 9; + left.matrix_map = NULL; + zones.push_back(left); + + for (uint8_t i = 1; i <= 9; i++) { + led left_led; + left_led.name = "Left LED " + std::to_string(i); + leds.push_back(left_led); + } + + zone right; + right.name = "Right"; + right.type = ZONE_TYPE_LINEAR; + right.leds_min = 9; + right.leds_max = 9; + right.leds_count = 9; + right.matrix_map = NULL; + zones.push_back(right); + + for (uint8_t i = 1; i <= 9; i++) { + led left_led; + left_led.name = "Right LED " + std::to_string(i); + leds.push_back(left_led); + } + + zone wheel; + wheel.name = "Scrollwheel"; + wheel.type = ZONE_TYPE_SINGLE; + wheel.leds_min = 1; + wheel.leds_max = 1; + wheel.leds_count = 1; + wheel.matrix_map = NULL; + zones.push_back(wheel); + + led wheel_led; + wheel_led.name = "Scrollwheel LED"; + leds.push_back(wheel_led); + + zone dpi; + dpi.name = "DPI button"; + dpi.type = ZONE_TYPE_SINGLE; + dpi.leds_min = 1; + dpi.leds_max = 1; + dpi.leds_count = 1; + dpi.matrix_map = NULL; + zones.push_back(dpi); + + led dpi_led; + dpi_led.name = "DPI button LED"; + leds.push_back(dpi_led); + + SetupColors(); +} + +void RGBController_RoccatKoneXP::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatKoneXP::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == ROCCAT_KONE_XP_MODE_DIRECT) + { + controller->SendDirect(colors); + } + else + { + DeviceUpdateMode(); + } +} + +void RGBController_RoccatKoneXP::UpdateZoneLEDs(int /*zone_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatKoneXP::UpdateSingleLED(int /*led_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatKoneXP::DeviceUpdateMode() +{ + mode selected = modes[active_mode]; + + roccat_kone_xp_mode_struct active = controller->GetMode(); + + active.mode = selected.value; + active.speed = selected.speed; + active.brightness = (modes[active_mode].value == ROCCAT_KONE_XP_MODE_DIRECT) ? 0xFF : selected.brightness; + + for(uint8_t i = 0; i < 20; i++) + { + active.colors[i].color = colors[i]; + } + + controller->SetMode(active); + controller->WaitUntilReady(); + + controller->EnableDirect(selected.value == ROCCAT_KONE_XP_MODE_DIRECT); + controller->WaitUntilReady(); +} diff --git a/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.h b/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.h new file mode 100644 index 00000000..dad65cf2 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKoneXP.h | +| | +| RGBController for Roccat Kone XP | +| | +| Mola19 12 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatKoneXPController.h" + +class RGBController_RoccatKoneXP : public RGBController +{ +public: + RGBController_RoccatKoneXP(RoccatKoneXPController* controller_ptr); + ~RGBController_RoccatKoneXP(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatKoneXPController* controller; +}; diff --git a/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.cpp b/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.cpp new file mode 100644 index 00000000..e9a0c15d --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.cpp @@ -0,0 +1,304 @@ +/*---------------------------------------------------------*\ +| RoccatKoneXPController.cpp | +| | +| Driver for Roccat Kone XP | +| | +| Mola19 12 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "LogManager.h" +#include "RoccatKoneXPController.h" + +RoccatKoneXPController::RoccatKoneXPController(hid_device* dev_handle, char *path) +{ + dev = dev_handle; + location = path; +} + +RoccatKoneXPController::~RoccatKoneXPController() +{ + hid_close(dev); +} + +std::string RoccatKoneXPController::GetVersion() +{ + uint8_t buf[9] = { 0x09 }; + int return_length = hid_get_feature_report(dev, buf, 9); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not fetch version. HIDAPI Error: %ls", hid_error(dev)); + return std::string("Unknown"); + } + + char version[5]; + snprintf(version, 5, "%d.%02d", buf[2] / 100, buf[2] % 100); + + return std::string(version); +} + +std::string RoccatKoneXPController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + std::wstring return_wstring = serial_string; + std::string return_string(return_wstring.begin(), return_wstring.end()); + + return(return_string); + +} + +std::string RoccatKoneXPController::GetLocation() +{ + return("HID: " + location); +} + +uint8_t RoccatKoneXPController::GetActiveProfile() +{ + uint8_t buf[4] = { 0x05 }; + int return_length = hid_get_feature_report(dev, buf, 4); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not fetch active profile. HIDAPI Error: %ls", hid_error(dev)); + return 0; + } + + return buf[2]; +} + +#include +roccat_kone_xp_mode_struct RoccatKoneXPController::GetMode() +{ + uint8_t buf[0xAE] = { 0x06 }; + int return_length = hid_get_feature_report(dev, buf, 0xAE); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not fetch mode. HIDAPI Error: %ls", hid_error(dev)); + roccat_kone_xp_mode_struct default_mode; + return default_mode; + } + + roccat_kone_xp_mode_struct active_mode; + + active_mode.profile = buf[2]; + active_mode.byte_3 = buf[3]; + active_mode.byte_4 = buf[4]; + active_mode.dpi_flag = buf[5]; + active_mode.byte_6 = buf[6]; + + for(uint8_t i = 0; i < 10; i++) + { + active_mode.dpi[i] = (buf[8 + i * 2] << 8) + buf[7 + i * 2]; + } + + active_mode.angle_snapping = (bool) buf[27]; + active_mode.byte_28 = buf[28]; + active_mode.polling_rate = buf[29]; + active_mode.mode = buf[30]; + active_mode.speed = buf[31]; + active_mode.brightness = buf[32]; + active_mode.time_until_idle = buf[33]; + active_mode.idle_mode = buf[34]; + active_mode.byte_35 = buf[35]; + + for(uint8_t i = 0; i < 20; i++) + { + active_mode.colors[i].brightness = buf[37 + i * 6]; + active_mode.colors[i].color = ToRGBColor( + buf[38 + i * 6], + buf[39 + i * 6], + buf[40 + i * 6] + ); + } + + active_mode.byte_156 = buf[156]; + active_mode.byte_157 = buf[157]; + active_mode.profile_color_brightness = buf[158]; + active_mode.profile_color_red = buf[159]; + active_mode.profile_color_green = buf[160]; + active_mode.profile_color_blue = buf[161]; + active_mode.byte_162 = buf[162]; + active_mode.theme = buf[163]; + active_mode.auto_dpi_flag = buf[164]; + + active_mode.end_bytes[0] = buf[165]; + active_mode.end_bytes[1] = buf[166]; + active_mode.end_bytes[2] = buf[167]; + active_mode.end_bytes[3] = buf[168]; + active_mode.end_bytes[4] = buf[169]; + active_mode.end_bytes[5] = buf[170]; + active_mode.end_bytes[6] = buf[171]; + + return active_mode; +} + +void RoccatKoneXPController::EnableDirect(bool on_off_switch) +{ + unsigned char usb_buf[6]; + + usb_buf[0x00] = 0x0E; + usb_buf[0x01] = 0x06; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = on_off_switch; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = 0xFF; + + int return_length = hid_send_feature_report(dev, usb_buf, 6); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not send mode. HIDAPI Error: %ls", hid_error(dev)); + } +} + +void RoccatKoneXPController::SetReadProfile(uint8_t profile) +{ + unsigned char usb_buf[4]; + + usb_buf[0x00] = 0x04; + usb_buf[0x01] = profile; + usb_buf[0x02] = 0x80; + usb_buf[0x03] = 0xFF; + + int return_length = hid_send_feature_report(dev, usb_buf, 4); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not set profile to read. HIDAPI Error: %ls", hid_error(dev)); + } +} + +void RoccatKoneXPController::SetMode(roccat_kone_xp_mode_struct mode) +{ + uint8_t buf[0xAE]; + memset(buf, 0x00, 0xAE); + + buf[0x00] = 0x06; + buf[0x01] = 0xAE; + + buf[0x02] = mode.profile; + buf[0x03] = mode.byte_3; + buf[0x04] = mode.byte_4; + buf[0x05] = mode.dpi_flag; + buf[0x06] = mode.byte_6; + + for(uint8_t i = 0; i < 10; i++) + { + buf[0x07 + i * 2] = mode.dpi[i] & 0xFF; + buf[0x08 + i * 2] = mode.dpi[i] >> 8; + } + + buf[0x1B] = mode.angle_snapping; + buf[0x1C] = mode.byte_28; + buf[0x1D] = mode.polling_rate; + buf[0x1E] = mode.mode; + buf[0x1F] = mode.speed; + buf[0x20] = mode.brightness; + buf[0x21] = mode.time_until_idle; + buf[0x22] = mode.idle_mode; + buf[0x23] = mode.byte_35; + + for(uint8_t i = 0; i < 20; i++) + { + buf[0x24 + i * 6] = mode.colors[i].byte_0; + buf[0x25 + i * 6] = 0xFF; + buf[0x26 + i * 6] = RGBGetRValue(mode.colors[i].color); + buf[0x27 + i * 6] = RGBGetGValue(mode.colors[i].color); + buf[0x28 + i * 6] = RGBGetBValue(mode.colors[i].color); + buf[0x29 + i * 6] = mode.colors[i].byte_5; + } + + buf[0x9C] = mode.byte_156; + buf[0x9D] = mode.byte_157; + buf[0x9E] = mode.profile_color_brightness; + buf[0x9F] = mode.profile_color_red; + buf[0xA0] = mode.profile_color_green; + buf[0xA1] = mode.profile_color_blue; + buf[0xA2] = mode.byte_162; + buf[0xA3] = mode.theme & ~0x80; // this stores the swarm intern selected theme (biggest bit is a flag for custom theme) + buf[0xA4] = mode.auto_dpi_flag; + + for(uint8_t i = 0; i < 7; i++) + { + buf[0xA5 + i] = mode.end_bytes[i]; + } + + unsigned short total = 0; + for(int i = 0; i < 0xAE - 2; i++) total += buf[i]; + + buf[0xAE - 2] = total & 0xFF; + buf[0xAE - 1] = total >> 8; + + + int return_length = hid_send_feature_report(dev, buf, 0xAE); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not send mode. HIDAPI Error: %ls", hid_error(dev)); + } +} + +void RoccatKoneXPController::SendDirect(std::vector colors) +{ + uint8_t buf[0x7A]; + memset(buf, 0x00, 0x7A); + + buf[0x00] = 0x0D; + buf[0x01] = 0x7A; + + for(uint8_t i = 0; i < colors.size() && i < 20; i++) + { + /*-----------------------------------------------------------*\ + | This device uses rgbrgb which means that e.g. the red value | + | is calculated by multiplying both given red values. | + | Maybe it is some sort of brightness? | + | For OpenRGBs purpose these aren't usefull, | + | so the second part is always 0xff. | + \*-----------------------------------------------------------*/ + buf[0x02 + i * 6] = RGBGetRValue(colors[i]); + buf[0x03 + i * 6] = RGBGetGValue(colors[i]); + buf[0x04 + i * 6] = RGBGetBValue(colors[i]); + buf[0x05 + i * 6] = 0xFF; + buf[0x06 + i * 6] = 0xFF; + buf[0x07 + i * 6] = 0xFF; + } + + int return_length = hid_send_feature_report(dev, buf, 0x7A); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not send direct. HIDAPI Error: %ls", hid_error(dev)); + } +} + +void RoccatKoneXPController::WaitUntilReady() +{ + uint8_t buf[4]; + memset(buf, 0x00, 4); + + buf[0] = 0x04; + + for(unsigned char i = 0; buf[1] != 1 && i < 100; i++) + { + if(i != 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + + hid_get_feature_report(dev, buf, 4); + } +} diff --git a/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.h b/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.h new file mode 100644 index 00000000..bc75fc1d --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.h @@ -0,0 +1,101 @@ +/*---------------------------------------------------------*\ +| RoccatKoneXPController.h | +| | +| Driver for Roccat Kone XP | +| | +| Mola19 12 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +enum +{ + ROCCAT_KONE_XP_MODE_DIRECT = 0x0B, + ROCCAT_KONE_XP_MODE_OFF = 0x00, + ROCCAT_KONE_XP_MODE_STATIC = 0x01, + ROCCAT_KONE_XP_MODE_BLINKING = 0x02, + ROCCAT_KONE_XP_MODE_BREATHING = 0x03, + ROCCAT_KONE_XP_MODE_HEARTBEAT = 0x04, + ROCCAT_KONE_XP_MODE_PHOTON_FX = 0x05, + ROCCAT_KONE_XP_MODE_DEFAULT = 0x09, + ROCCAT_KONE_XP_MODE_WAVE = 0x0A +}; + +enum +{ + ROCCAT_KONE_XP_SPEED_MIN = 0x0B, + ROCCAT_KONE_XP_SPEED_MAX = 0x01, + ROCCAT_KONE_XP_SPEED_DEFAULT = 0x06, + ROCCAT_KONE_XP_BRIGHTNESS_MIN = 0x00, + ROCCAT_KONE_XP_BRIGHTNESS_MAX = 0xFF, + ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT = 0xFF +}; + +struct roccat_kone_xp_color_struct +{ + uint8_t byte_0 = 0x14; + uint8_t brightness = 0xFF; + RGBColor color = 0; + uint8_t byte_5 = 0x64; +}; + +struct roccat_kone_xp_mode_struct +{ + uint8_t profile = 0; + uint8_t byte_3 = 0x06; + uint8_t byte_4 = 0x06; + uint8_t dpi_flag = 0x1F; + uint8_t byte_6 = 0x01; + uint16_t dpi[10] = { 0x08, 0x10, 0x18, 0x20, 0x40, 0x08, 0x10, 0x18, 0x20, 0x40 }; + bool angle_snapping = false; + uint8_t byte_28 = 0x00; + uint8_t polling_rate = 0x03; + uint8_t mode = ROCCAT_KONE_XP_MODE_STATIC; + uint8_t speed = 0x00; + uint8_t brightness = 0xFF; + uint8_t time_until_idle = 0x0F; + uint8_t idle_mode = 0x00; + uint8_t byte_35 = 0x00; + roccat_kone_xp_color_struct colors[20]; + uint8_t byte_156 = 0x01; + uint8_t byte_157 = 0x64; + uint8_t profile_color_brightness = 0xFF; + uint8_t profile_color_red = 0xFF; + uint8_t profile_color_green = 0x00; + uint8_t profile_color_blue = 0x00; + uint8_t byte_162 = 0x00; + uint8_t theme = 0x80; + uint8_t auto_dpi_flag = 0x00; + uint8_t end_bytes[7] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +}; + +class RoccatKoneXPController +{ +public: + RoccatKoneXPController(hid_device* dev_handle, char *path); + ~RoccatKoneXPController(); + + std::string GetSerial(); + std::string GetLocation(); + std::string GetVersion(); + + uint8_t GetActiveProfile(); + roccat_kone_xp_mode_struct GetMode(); + + void EnableDirect(bool on_off_switch); + void SetReadProfile(uint8_t profile); + void SendDirect(std::vector colors); + void SetMode(roccat_kone_xp_mode_struct mode); + + void WaitUntilReady(); + +private: + hid_device* dev; + std::string location; +};