From d4f78bec6374e8abc3cdec0e9ee98284da8f8f1b Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sat, 9 May 2026 00:08:55 +0200 Subject: [PATCH] Add ASUS ROG Azoth X keyboard support (PID 0x1C24) Implements all 10 lighting modes via the 65-byte 51 2C HID command: - Static, Breathing (Single/Double/Random), Color Cycle - Reactive (Single/Double/Random), Rainbow Wave, Ripple - Starry Night, Quicksand (6-colour gradient), Current, Rain Drop Multi-packet modes (Rainbow Wave, Ripple, Quicksand) send 2-3 sub-pages per update. Direct mode excluded (firmware NAKs it). All changes are gated on PID check to leave existing Azoth/TUF keyboard behaviour unchanged. --- .../AsusAuraTUFKeyboardController.cpp | 358 ++++++++++++++++++ .../AsusAuraTUFKeyboardController.h | 11 + .../RGBController_AsusAuraTUFKeyboard.cpp | 126 ++++-- .../AsusAuraUSBControllerDetect.cpp | 2 + 4 files changed, 474 insertions(+), 23 deletions(-) diff --git a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.cpp b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.cpp index f4911fd92..1f3726c0e 100644 --- a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.cpp +++ b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.cpp @@ -17,6 +17,7 @@ #include #include #include "AsusAuraTUFKeyboardController.h" +#include "LogManager.h" #include "StringUtils.h" #define HID_MAX_STR 128 @@ -99,6 +100,7 @@ std::string AuraTUFKeyboardController::GetVersion() { case AURA_ROG_AZOTH_USB_PID: case AURA_ROG_AZOTH_2_4_PID: + case AURA_ROG_AZOTH_X_USB_PID: case AURA_TUF_K3_GAMING_PID: case AURA_TUF_K3_GAMING_GEN_II_PID: case AURA_ROG_STRIX_FLARE_II_ANIMATE_PID: @@ -491,6 +493,357 @@ void AuraTUFKeyboardController::UpdateScopeIIQuicksand AwaitResponse(20); } +/*---------------------------------------------------------------------------*\ +| Azoth X 51 2C protocol — named protocol constants. | +| Kept in an anonymous namespace so they stay file-local. | +\*---------------------------------------------------------------------------*/ +namespace +{ + /*-----------------------------------------------------------------------*\ + | [07] color_mode byte values | + \*-----------------------------------------------------------------------*/ + constexpr uint8_t AZX_CM_SINGLE = 0x00; + constexpr uint8_t AZX_CM_DOUBLE = 0x10; + constexpr uint8_t AZX_CM_RANDOM = 0x01; + constexpr uint8_t AZX_CM_RANDOM_ALT = 0x11; // Breathing / Starry Night / Rain Drop + + /*-----------------------------------------------------------------------*\ + | Sub-packet page index byte [04] for multi-packet effects | + \*-----------------------------------------------------------------------*/ + constexpr uint8_t AZX_PAGE_PRIMARY = 0x02; // main params + first stops + constexpr uint8_t AZX_PAGE_SECONDARY = 0x01; // continued stops + constexpr uint8_t AZX_PAGE_COMMIT = 0x00; // last stop / commit + + /*-----------------------------------------------------------------------*\ + | Wave / Ripple thickness byte [09] | + \*-----------------------------------------------------------------------*/ + constexpr uint8_t AZX_THICKNESS_THICK = 0x02; // hardware default + + /*-----------------------------------------------------------------------*\ + | Gradient stop position bytes | + \*-----------------------------------------------------------------------*/ + constexpr uint8_t AZX_POS_MAX = 0x64; // 100% + constexpr uint8_t AZX_RIPPLE_LEFT_POS = 0x07; // hardware-fixed Ripple left stop + + /*-----------------------------------------------------------------------*\ + | Sentinel speed used for Static and out-of-range mode indices | + \*-----------------------------------------------------------------------*/ + constexpr uint8_t AZX_SPEED_NONE = 0xFF; + + /*-----------------------------------------------------------------------*\ + | OpenRGB color_mode byte semantics passed in by DeviceUpdateMode | + \*-----------------------------------------------------------------------*/ + constexpr unsigned char OPENRGB_CM_RANDOM = 1; + + constexpr size_t AZX_PACKET_SIZE = 65; + + /*-----------------------------------------------------------------------*\ + | Initialise a fresh 65-byte Azoth X multi-packet header in `buf`. | + | Zeroes the buffer, then writes the 51 2C command, mode byte and page. | + \*-----------------------------------------------------------------------*/ + inline void azx_init_packet(uint8_t* buf, uint8_t mode_byte, uint8_t page) + { + memset(buf, 0, AZX_PACKET_SIZE); + buf[1] = 0x51; + buf[2] = 0x2C; + buf[3] = mode_byte; + buf[4] = page; + } +} + +/*---------------------------------------------------------------------------*\ +| Azoth X lighting packet — 51 2C command, single-packet modes. | +| Fixed fields (cmd, param1/2) are set by default initialisers; the caller | +| only fills the variable fields (mode, speed, brightness, colour). | +\*---------------------------------------------------------------------------*/ +struct AzothXLightingPkt +{ + uint8_t report_id = 0x00; + uint8_t cmd1 = 0x51; + uint8_t cmd2 = 0x2C; + uint8_t mode = 0x00; // [03] + uint8_t reserved1 = 0x00; // [04] + uint8_t speed = 0x00; // [05] + uint8_t brightness = 0x00; // [06] + uint8_t color_mode = AZX_CM_SINGLE; // [07] + uint8_t param1 = 0xFF; // [08] fixed 0xFF + uint8_t param2 = 0xFF; // [09] fixed 0xFF + uint8_t r1 = 0x00; // [0A] primary colour R + uint8_t g1 = 0x00; // [0B] primary colour G + uint8_t b1 = 0x00; // [0C] primary colour B + uint8_t r2 = 0x00; // [0D] secondary colour R (Double) or blend (Single/Current) + uint8_t g2 = 0x00; // [0E] secondary colour G + uint8_t b2 = 0x00; // [0F] secondary colour B + uint8_t bg_r = 0x00; // [10] background R (Current / Raindrop; 0=OFF) + uint8_t bg_g = 0x00; // [11] background G + uint8_t bg_b = 0x00; // [12] background B + uint8_t padding[46] = {}; // [13-40] zeros +}; +static_assert(sizeof(AzothXLightingPkt) == AZX_PACKET_SIZE, "AzothXLightingPkt size mismatch"); + +void AuraTUFKeyboardController::UpdateAzothXDevice + ( + unsigned char mode, + std::vector colors, + unsigned char direction, + unsigned char color_mode, + unsigned char speed, + unsigned char brightness + ) +{ + // Mode-specific speed range: {fast_hex, slow_hex} for mode indices 0x00-0x09. + // Static (0x00) uses 0xFF (no speed). Linear interpolation maps the OpenRGB + // speed value [0=fastest .. 255=slowest] to each mode's USB speed range. + static const uint8_t speed_fast[10] = { 0xFF, 0x0F, 0x12, 0x08, 0x23, 0x50, 0x0A, 0x0C, 0x07, 0x0E }; + static const uint8_t speed_slow[10] = { 0xFF, 0x2A, 0x3E, 0x16, 0x55, 0x78, 0x19, 0x14, 0x0C, 0x1B }; + + AzothXLightingPkt pkt; + pkt.mode = mode; + pkt.brightness = brightness; + + /*-----------------------------------------------------------------------*\ + | Map OpenRGB speed [0=fastest .. 255=slowest] onto each mode's USB | + | speed range. Static (0x00) and out-of-range modes use the no-speed | + | sentinel. | + \*-----------------------------------------------------------------------*/ + if(mode == AURA_KEYBOARD_MODE_STATIC || mode >= 10) + { + pkt.speed = AZX_SPEED_NONE; + } + else + { + const uint8_t fast = speed_fast[mode]; + const uint8_t slow = speed_slow[mode]; + pkt.speed = static_cast(fast + (static_cast(speed) * (slow - fast)) / 255); + } + + /*-----------------------------------------------------------------------*\ + | Rainbow Wave: 3-packet multi-page format. | + | Gradient is the hardware default rainbow (7 stops, 2+4+1 split across | + | the three packets); only speed, brightness, and direction are exposed. | + \*-----------------------------------------------------------------------*/ + if(mode == AURA_KEYBOARD_MODE_WAVE) + { + // Stops 1-2 (chartreuse @ 7%, yellow-green @ 16%) — type 0x02 buf[0A+] + static const uint8_t wave_stops_1_2[] = { + 0x07, 0x08, 0xFF, 0x00, + 0x10, 0x17, 0xE8, 0x00, + 0xFF, 0x00, 0x00 + }; + // Stops 3-6 (blue, cyan, green, yellow) — type 0x01 buf[05+] + static const uint8_t wave_stops_3_6[] = { + 0x27, 0x08, 0x08, 0xFF, + 0x36, 0x00, 0xFF, 0xF9, + 0x46, 0x00, 0xFF, 0x08, + 0x56, 0xE9, 0xFF, 0x00 + }; + // Stop 7 (orange-red @ 100%) — type 0x00 buf[05+] + static const uint8_t wave_stop_7[] = { + AZX_POS_MAX, 0xFF, 0x14, 0x00 + }; + + uint8_t buf[AZX_PACKET_SIZE]; + + // Packet 1 — main parameters + gradient stops 1-2 + azx_init_packet(buf, AURA_KEYBOARD_MODE_WAVE, AZX_PAGE_PRIMARY); + buf[5] = pkt.speed; + buf[6] = pkt.brightness; + buf[8] = direction; + buf[9] = AZX_THICKNESS_THICK; + memcpy(&buf[10], wave_stops_1_2, sizeof(wave_stops_1_2)); + + LOG_DEBUG("[%s] AzothX Wave pkt02 spd=%02X bri=%02X dir=%02X", + name.c_str(), pkt.speed, pkt.brightness, direction); + ClearResponses(); + hid_write(dev, buf, sizeof(buf)); + AwaitResponse(20); + + // Packet 2 — gradient stops 3-6 + azx_init_packet(buf, AURA_KEYBOARD_MODE_WAVE, AZX_PAGE_SECONDARY); + memcpy(&buf[5], wave_stops_3_6, sizeof(wave_stops_3_6)); + ClearResponses(); + hid_write(dev, buf, sizeof(buf)); + AwaitResponse(20); + + // Packet 3 — gradient stop 7 + commit + azx_init_packet(buf, AURA_KEYBOARD_MODE_WAVE, AZX_PAGE_COMMIT); + memcpy(&buf[5], wave_stop_7, sizeof(wave_stop_7)); + ClearResponses(); + hid_write(dev, buf, sizeof(buf)); + AwaitResponse(20); + SaveMode(); + return; + } + + /*-----------------------------------------------------------------------*\ + | Quicksand: 2-packet format. 6 user-selectable colour stops are split | + | as stops 1-3 in the secondary page and stops 4-6 in the commit page. | + \*-----------------------------------------------------------------------*/ + if(mode == AURA_KEYBOARD_MODE_QUICKSAND) + { + const uint8_t qs_cm = (color_mode == OPENRGB_CM_RANDOM) ? AZX_CM_RANDOM : AZX_CM_SINGLE; + const bool is_rand = (qs_cm == AZX_CM_RANDOM); + + uint8_t buf[AZX_PACKET_SIZE]; + + // Packet 1 — main params + gradient stops 1-3 + azx_init_packet(buf, AURA_KEYBOARD_MODE_QUICKSAND, AZX_PAGE_SECONDARY); + buf[5] = pkt.speed; + buf[6] = pkt.brightness; + buf[7] = qs_cm; + buf[8] = direction; + if(!is_rand) + { + for(size_t i = 0; i < 3 && i < colors.size(); i++) + { + buf[10 + i * 3] = RGBGetRValue(colors[i]); + buf[11 + i * 3] = RGBGetGValue(colors[i]); + buf[12 + i * 3] = RGBGetBValue(colors[i]); + } + } + + LOG_DEBUG("[%s] AzothX Quicksand pkt01 spd=%02X bri=%02X cm=%02X dir=%02X", + name.c_str(), pkt.speed, pkt.brightness, qs_cm, direction); + ClearResponses(); + hid_write(dev, buf, sizeof(buf)); + AwaitResponse(20); + + // Packet 2 — commit + gradient stops 4-6 + azx_init_packet(buf, AURA_KEYBOARD_MODE_QUICKSAND, AZX_PAGE_COMMIT); + if(!is_rand) + { + for(size_t i = 3; i < 6 && i < colors.size(); i++) + { + buf[5 + (i - 3) * 3] = RGBGetRValue(colors[i]); + buf[6 + (i - 3) * 3] = RGBGetGValue(colors[i]); + buf[7 + (i - 3) * 3] = RGBGetBValue(colors[i]); + } + } + + ClearResponses(); + hid_write(dev, buf, sizeof(buf)); + AwaitResponse(20); + SaveMode(); + return; + } + + /*-----------------------------------------------------------------------*\ + | Ripple: 3-packet format. Left stop is hardware-fixed (black at pos 7); | + | only the right stop colour is user-selectable. Pattern uses [pos R G | + | B] for the right stop; Random sends zeros and the firmware picks. | + \*-----------------------------------------------------------------------*/ + if(mode == AURA_KEYBOARD_MODE_RIPPLE) + { + const uint8_t rp_cm = (color_mode == OPENRGB_CM_RANDOM) ? AZX_CM_RANDOM : AZX_CM_SINGLE; + const bool is_rand = (rp_cm == AZX_CM_RANDOM); + + uint8_t buf[AZX_PACKET_SIZE]; + + // Packet 1 — main params + right gradient stop + azx_init_packet(buf, AURA_KEYBOARD_MODE_RIPPLE, AZX_PAGE_PRIMARY); + buf[5] = pkt.speed; + buf[6] = pkt.brightness; + buf[7] = rp_cm; + buf[9] = AZX_THICKNESS_THICK; + buf[10] = AZX_RIPPLE_LEFT_POS; + if(!is_rand && !colors.empty()) + { + buf[11] = AZX_POS_MAX; + buf[12] = RGBGetRValue(colors[0]); + buf[13] = RGBGetGValue(colors[0]); + buf[14] = RGBGetBValue(colors[0]); + } + + LOG_DEBUG("[%s] AzothX Ripple pkt02 spd=%02X bri=%02X cm=%02X", + name.c_str(), pkt.speed, pkt.brightness, rp_cm); + ClearResponses(); + hid_write(dev, buf, sizeof(buf)); + AwaitResponse(20); + + // Packet 2 — secondary page (all zeros) + azx_init_packet(buf, AURA_KEYBOARD_MODE_RIPPLE, AZX_PAGE_SECONDARY); + ClearResponses(); + hid_write(dev, buf, sizeof(buf)); + AwaitResponse(20); + + // Packet 3 — commit (all zeros = black left stop) + azx_init_packet(buf, AURA_KEYBOARD_MODE_RIPPLE, AZX_PAGE_COMMIT); + ClearResponses(); + hid_write(dev, buf, sizeof(buf)); + AwaitResponse(20); + SaveMode(); + return; + } + + /*-----------------------------------------------------------------------*\ + | Color mode encoding for single-packet modes. | + | DeviceUpdateMode delivers: 0=Single, 0x10=Double, 1=Random. | + | Breathing / Starry Night / Rain Drop use 0x11 for Random; others 0x01. | + \*-----------------------------------------------------------------------*/ + const bool is_random_alt_mode = (mode == AURA_KEYBOARD_MODE_BREATHING + || mode == AURA_KEYBOARD_MODE_STARRY_NIGHT + || mode == AURA_KEYBOARD_MODE_RAIN_DROP); + + if(color_mode == OPENRGB_CM_RANDOM && is_random_alt_mode) + { + pkt.color_mode = AZX_CM_RANDOM_ALT; + } + else + { + pkt.color_mode = color_mode; + } + + /*-----------------------------------------------------------------------*\ + | Primary colour [0A-0C] | + \*-----------------------------------------------------------------------*/ + if(!colors.empty()) + { + pkt.r1 = RGBGetRValue(colors[0]); + pkt.g1 = RGBGetGValue(colors[0]); + pkt.b1 = RGBGetBValue(colors[0]); + } + + /*-----------------------------------------------------------------------*\ + | Secondary colour / blend [0D-0F] | + \*-----------------------------------------------------------------------*/ + if(pkt.color_mode == AZX_CM_DOUBLE && colors.size() >= 2) + { + pkt.r2 = RGBGetRValue(colors[1]); + pkt.g2 = RGBGetGValue(colors[1]); + pkt.b2 = RGBGetBValue(colors[1]); + } + else if(mode == AURA_KEYBOARD_MODE_CURRENT && pkt.color_mode == AZX_CM_SINGLE) + { + // Current Single-mode blend pattern: min(R,G) / G / B + pkt.r2 = std::min(pkt.r1, pkt.g1); + pkt.g2 = pkt.g1; + pkt.b2 = pkt.b1; + } + + /*-----------------------------------------------------------------------*\ + | Background colour [10-12] (Current and Raindrop only). | + | Passed as colors[2]; black (0,0,0) = Background OFF. | + \*-----------------------------------------------------------------------*/ + if((mode == AURA_KEYBOARD_MODE_CURRENT || mode == AURA_KEYBOARD_MODE_RAIN_DROP) + && colors.size() >= 3) + { + pkt.bg_r = RGBGetRValue(colors[2]); + pkt.bg_g = RGBGetGValue(colors[2]); + pkt.bg_b = RGBGetBValue(colors[2]); + } + + LOG_DEBUG("[%s] AzothX mode=%02X spd=%02X bri=%02X cm=%02X " + "c1=(%02X,%02X,%02X) c2=(%02X,%02X,%02X) bg=(%02X,%02X,%02X)", + name.c_str(), pkt.mode, pkt.speed, pkt.brightness, pkt.color_mode, + pkt.r1, pkt.g1, pkt.b1, pkt.r2, pkt.g2, pkt.b2, + pkt.bg_r, pkt.bg_g, pkt.bg_b); + + ClearResponses(); + hid_write(dev, reinterpret_cast(&pkt), sizeof(pkt)); + AwaitResponse(20); + SaveMode(); +} + void AuraTUFKeyboardController::UpdateDevice ( unsigned char mode, @@ -506,6 +859,11 @@ void AuraTUFKeyboardController::UpdateDevice return UpdateK1Wave(colors, direction, speed, brightness); } + if(device_pid == AURA_ROG_AZOTH_X_USB_PID) + { + return UpdateAzothXDevice(mode, colors, direction, color_mode, speed, brightness); + } + if(device_pid == AURA_ROG_AZOTH_USB_PID || device_pid == AURA_ROG_AZOTH_2_4_PID || device_pid == AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_USB_PID diff --git a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.h b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.h index 323e9bdad..b3b884f20 100644 --- a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.h +++ b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.h @@ -36,6 +36,7 @@ enum { AURA_ROG_AZOTH_USB_PID = 0x1A83, AURA_ROG_AZOTH_2_4_PID = 0x1A85, + AURA_ROG_AZOTH_X_USB_PID = 0x1C24, AURA_ROG_CLAYMORE_PID = 0x184D, AURA_ROG_FALCHION_WIRED_PID = 0x193C, AURA_ROG_FALCHION_WIRELESS_PID = 0x193E, @@ -123,6 +124,16 @@ class AuraTUFKeyboardController unsigned char brightness ); + void UpdateAzothXDevice + ( + unsigned char mode, + std::vector colors, + unsigned char direction, + unsigned char color_mode, + unsigned char speed, + unsigned char brightness + ); + void UpdateDevice ( unsigned char mode, diff --git a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.cpp b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.cpp index 1d4c216cc..3e0c348b2 100644 --- a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.cpp +++ b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.cpp @@ -30,6 +30,12 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont pid = controller->device_pid; + /*-----------------------------------------------------------------*\ + | Azoth X uses a different lighting protocol and gets visible | + | default colors. Other PIDs keep their pre-existing behaviour. | + \*-----------------------------------------------------------------*/ + const bool is_azoth_x = (pid == AURA_ROG_AZOTH_X_USB_PID); + if(pid != AURA_ROG_CLAYMORE_PID) { name = controller->GetName(); @@ -63,6 +69,12 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont AURA_KEYBOARD_SPEED_DEFAULT = 8; break; + case AURA_ROG_AZOTH_X_USB_PID: + AURA_KEYBOARD_SPEED_MIN = 255; + AURA_KEYBOARD_SPEED_MAX = 0; + AURA_KEYBOARD_SPEED_DEFAULT = 128; + break; + case AURA_ROG_AZOTH_USB_PID: case AURA_ROG_AZOTH_2_4_PID: case AURA_ROG_FALCHION_WIRED_PID: @@ -91,12 +103,15 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont break; } - mode Direct; - Direct.name = "Direct"; - Direct.value = AURA_KEYBOARD_MODE_DIRECT; - Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; - Direct.color_mode = MODE_COLORS_PER_LED; - modes.push_back(Direct); + if(!is_azoth_x) + { + mode Direct; + Direct.name = "Direct"; + Direct.value = AURA_KEYBOARD_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + } mode Static; Static.name = "Static"; @@ -109,6 +124,7 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Static.colors_min = 1; Static.colors_max = 1; Static.colors.resize(1); + if(is_azoth_x) Static.colors[0] = ToRGBColor(0xFF, 0xFF, 0xFF); modes.push_back(Static); mode Breathing; @@ -121,14 +137,15 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont } Breathing.speed_min = AURA_KEYBOARD_SPEED_MIN; Breathing.speed_max = AURA_KEYBOARD_SPEED_MAX; - Breathing.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Breathing.speed = is_azoth_x ? 142 : AURA_KEYBOARD_SPEED_DEFAULT; Breathing.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; Breathing.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; Breathing.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; - Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.color_mode = is_azoth_x ? MODE_COLORS_RANDOM : MODE_COLORS_MODE_SPECIFIC; Breathing.colors_min = 1; Breathing.colors_max = 2; Breathing.colors.resize(1); + if(is_azoth_x) Breathing.colors[0] = ToRGBColor(0xFF, 0xFF, 0xFF); modes.push_back(Breathing); mode Color_Cycle; @@ -137,7 +154,7 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Color_Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; Color_Cycle.speed_min = AURA_KEYBOARD_SPEED_MIN; Color_Cycle.speed_max = AURA_KEYBOARD_SPEED_MAX; - Color_Cycle.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Color_Cycle.speed = is_azoth_x ? 75 : AURA_KEYBOARD_SPEED_DEFAULT; Color_Cycle.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; Color_Cycle.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; Color_Cycle.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; @@ -147,11 +164,15 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont mode Wave; Wave.name = "Rainbow Wave"; Wave.value = AURA_KEYBOARD_MODE_WAVE; - Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; if(controller->is_per_led_keyboard) { Wave.flags |= MODE_FLAG_HAS_DIRECTION_UD; } + if(!is_azoth_x) + { + Wave.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + } Wave.speed_min = AURA_KEYBOARD_SPEED_MIN; Wave.speed_max = AURA_KEYBOARD_SPEED_MAX; Wave.speed = AURA_KEYBOARD_SPEED_DEFAULT; @@ -159,20 +180,40 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Wave.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; Wave.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; Wave.direction = MODE_DIRECTION_LEFT; - Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; - if(!controller->is_per_led_keyboard) + if(is_azoth_x) + { + // Fixed hardware gradient — no user colour selection + Wave.color_mode = MODE_COLORS_NONE; + Wave.colors_min = 0; + Wave.colors_max = 0; + } + else if(!controller->is_per_led_keyboard) { + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; Wave.colors_min = 5; Wave.colors_max = 5; } else { + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; Wave.colors_min = 1; Wave.colors_max = 7; } Wave.colors.resize(Wave.colors_max); + if(is_azoth_x) + { + // Azoth X gets the rainbow gradient stored as visible defaults; other + // devices keep their pre-existing default-constructed (black) colours + // to preserve historical behaviour on untested hardware. + if(Wave.colors.size() > 0) Wave.colors[0] = ToRGBColor(0xFF, 0x00, 0x00); + if(Wave.colors.size() > 1) Wave.colors[1] = ToRGBColor(0xFF, 0x80, 0x00); + if(Wave.colors.size() > 2) Wave.colors[2] = ToRGBColor(0xFF, 0xFF, 0x00); + if(Wave.colors.size() > 3) Wave.colors[3] = ToRGBColor(0x00, 0xFF, 0x00); + if(Wave.colors.size() > 4) Wave.colors[4] = ToRGBColor(0x00, 0x00, 0xFF); + if(Wave.colors.size() > 5) Wave.colors[5] = ToRGBColor(0x80, 0x00, 0xFF); + } modes.push_back(Wave); if(controller->is_per_led_keyboard) @@ -191,6 +232,7 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Reactive.colors_min = 1; Reactive.colors_max = 2; Reactive.colors.resize(1); + if(is_azoth_x) Reactive.colors[0] = ToRGBColor(0xFF, 0xFF, 0xFF); modes.push_back(Reactive); mode Ripple; @@ -205,8 +247,12 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Ripple.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; Ripple.colors_min = 1; - Ripple.colors_max = 8; - Ripple.colors.resize(7); + Ripple.colors_max = is_azoth_x ? 1 : 8; + Ripple.colors.resize(is_azoth_x ? 1 : 7); + if(is_azoth_x) + { + Ripple.colors[0] = ToRGBColor(0xFF, 0xFF, 0xFF); + } modes.push_back(Ripple); mode Starry_Night; @@ -215,14 +261,15 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Starry_Night.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; Starry_Night.speed_min = AURA_KEYBOARD_SPEED_MIN; Starry_Night.speed_max = AURA_KEYBOARD_SPEED_MAX; - Starry_Night.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Starry_Night.speed = is_azoth_x ? 85 : AURA_KEYBOARD_SPEED_DEFAULT; Starry_Night.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; Starry_Night.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; Starry_Night.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; Starry_Night.color_mode = MODE_COLORS_MODE_SPECIFIC; Starry_Night.colors_min = 1; - Starry_Night.colors_max = 3; + Starry_Night.colors_max = is_azoth_x ? 2 : 3; Starry_Night.colors.resize(1); + if(is_azoth_x) Starry_Night.colors[0] = ToRGBColor(0xFF, 0xFF, 0xFF); modes.push_back(Starry_Night); mode Quicksand; @@ -232,7 +279,7 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Quicksand.direction = MODE_DIRECTION_DOWN; Quicksand.speed_min = AURA_KEYBOARD_SPEED_MIN; Quicksand.speed_max = AURA_KEYBOARD_SPEED_MAX; - Quicksand.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Quicksand.speed = is_azoth_x ? 96 : AURA_KEYBOARD_SPEED_DEFAULT; Quicksand.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; Quicksand.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; Quicksand.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; @@ -240,6 +287,15 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Quicksand.colors_min = 6; Quicksand.colors_max = 6; Quicksand.colors.resize(6); + if(is_azoth_x) + { + Quicksand.colors[0] = ToRGBColor(0xFF, 0x00, 0x00); + Quicksand.colors[1] = ToRGBColor(0xFF, 0x80, 0x00); + Quicksand.colors[2] = ToRGBColor(0xFF, 0xFF, 0x00); + Quicksand.colors[3] = ToRGBColor(0x00, 0xFF, 0x00); + Quicksand.colors[4] = ToRGBColor(0x00, 0x00, 0xFF); + Quicksand.colors[5] = ToRGBColor(0x80, 0x00, 0xFF); + } modes.push_back(Quicksand); mode Current; @@ -248,7 +304,7 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Current.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; Current.speed_min = AURA_KEYBOARD_SPEED_MIN; Current.speed_max = AURA_KEYBOARD_SPEED_MAX; - Current.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Current.speed = is_azoth_x ? 153 : AURA_KEYBOARD_SPEED_DEFAULT; Current.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; Current.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; Current.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; @@ -256,6 +312,7 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Current.colors_min = 1; Current.colors_max = 3; Current.colors.resize(1); + if(is_azoth_x) Current.colors[0] = ToRGBColor(0xFF, 0xFF, 0xFF); modes.push_back(Current); mode Rain_Drop; @@ -264,7 +321,7 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Rain_Drop.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; Rain_Drop.speed_min = AURA_KEYBOARD_SPEED_MIN; Rain_Drop.speed_max = AURA_KEYBOARD_SPEED_MAX; - Rain_Drop.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Rain_Drop.speed = is_azoth_x ? 118 : AURA_KEYBOARD_SPEED_DEFAULT; Rain_Drop.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; Rain_Drop.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; Rain_Drop.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; @@ -272,6 +329,7 @@ RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardCont Rain_Drop.colors_min = 1; Rain_Drop.colors_max = 3; Rain_Drop.colors.resize(1); + if(is_azoth_x) Rain_Drop.colors[0] = ToRGBColor(0xFF, 0xFF, 0xFF); modes.push_back(Rain_Drop); } } @@ -454,6 +512,7 @@ void RGBController_AuraTUFKeyboard::SetupZones() break; case AURA_ROG_AZOTH_USB_PID: case AURA_ROG_AZOTH_2_4_PID: + case AURA_ROG_AZOTH_X_USB_PID: keyboard_ptr = &AsusROGAzothLayouts; break; case AURA_ROG_FALCHION_WIRED_PID: @@ -609,11 +668,32 @@ void RGBController_AuraTUFKeyboard::DeviceUpdateMode() break; } - bool color_is_black = (modes[active_mode].colors.size() > 1 && modes[active_mode].colors[1] == 000); - - if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC && !color_is_black) + if(pid == AURA_ROG_AZOTH_X_USB_PID) + { + /*-------------------------------------------------------*\ + | Azoth X: send Double only when a non-black second | + | colour is actually present. | + \*-------------------------------------------------------*/ + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC + && modes[active_mode].colors.size() >= 2 + && modes[active_mode].colors[1] != 0) + { + color_mode = 16; + } + } + else { - color_mode = 16; + /*-------------------------------------------------------*\ + | Legacy logic preserved for the original Azoth and other | + | per-LED keyboards. Behaviour unchanged from before the | + | Azoth X work. | + \*-------------------------------------------------------*/ + bool color_is_black = (modes[active_mode].colors.size() > 1 && modes[active_mode].colors[1] == 000); + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC && !color_is_black) + { + color_mode = 16; + } } break; } diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBControllerDetect.cpp b/Controllers/AsusAuraUSBController/AsusAuraUSBControllerDetect.cpp index 6a0977668..8cd85f03e 100644 --- a/Controllers/AsusAuraUSBController/AsusAuraUSBControllerDetect.cpp +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBControllerDetect.cpp @@ -57,6 +57,7 @@ \*-----------------------------------------------------------------*/ #define AURA_ROG_AZOTH_USB_PID 0x1A83 #define AURA_ROG_AZOTH_2_4_PID 0x1A85 +#define AURA_ROG_AZOTH_X_USB_PID 0x1C24 #define AURA_ROG_CLAYMORE_PID 0x184D #define AURA_ROG_FALCHION_WIRED_PID 0x193C #define AURA_ROG_FALCHION_WIRELESS_PID 0x193E @@ -359,6 +360,7 @@ REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope RX TKL Wireless Deluxe", Dete REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope TKL PNK LTD", DetectAsusAuraUSBKeyboards, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_TKL_PNK_LTD_PID, 1, 0xFF00); REGISTER_HID_DETECTOR_IP("ASUS ROG Azoth USB", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_AZOTH_USB_PID, 1, 0xFF00); REGISTER_HID_DETECTOR_IP("ASUS ROG Azoth 2.4GHz", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_AZOTH_2_4_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Azoth X USB", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_AZOTH_X_USB_PID, 1, 0xFF00); REGISTER_HID_DETECTOR_IP("ASUS ROG Claymore", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_CLAYMORE_PID, 1, 0xFF00); REGISTER_HID_DETECTOR_IP("ASUS ROG Falchion (Wired)", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_FALCHION_WIRED_PID, 1, 0xFF00); REGISTER_HID_DETECTOR_IP("ASUS ROG Falchion (Wireless)", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_FALCHION_WIRELESS_PID, 1, 0xFF00);