Publish LumaOps source

This commit is contained in:
LumaOps release export
2026-09-03 01:18:36 +02:00
commit 7f1c0e5f71
2363 changed files with 501543 additions and 0 deletions
@@ -0,0 +1,59 @@
/*---------------------------------------------------------*\
| Lenovo4ZoneUSBController.cpp |
| |
| Driver for Lenovo 4-Zone devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <iostream>
#include "Lenovo4ZoneUSBController.h"
#include "LogManager.h"
#include "StringUtils.h"
Lenovo4ZoneUSBController::Lenovo4ZoneUSBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name)
{
dev = dev_handle;
location = path;
pid = in_pid;
name = dev_name;
}
Lenovo4ZoneUSBController::~Lenovo4ZoneUSBController()
{
hid_close(dev);
}
void Lenovo4ZoneUSBController::setMode(const KeyboardState &in_mode)
{
uint8_t buffer[LENOVO_4_ZONE_HID_PACKET_SIZE] =
{
in_mode.header[0], in_mode.header[1],
in_mode.effect,
in_mode.speed,
in_mode.brightness,
in_mode.zone0_rgb[0], in_mode.zone0_rgb[1], in_mode.zone0_rgb[2],
in_mode.zone1_rgb[0], in_mode.zone1_rgb[1], in_mode.zone1_rgb[2],
in_mode.zone2_rgb[0], in_mode.zone2_rgb[1], in_mode.zone2_rgb[2],
in_mode.zone3_rgb[0], in_mode.zone3_rgb[1], in_mode.zone3_rgb[2],
0x00,
in_mode.wave_ltr, in_mode.wave_rtl
};
hid_send_feature_report(dev, buffer, LENOVO_4_ZONE_HID_PACKET_SIZE);
}
uint16_t Lenovo4ZoneUSBController::getPid()
{
return pid;
}
std::string Lenovo4ZoneUSBController::getName()
{
return name;
}
std::string Lenovo4ZoneUSBController::getLocation()
{
return location;
}
@@ -0,0 +1,59 @@
/*---------------------------------------------------------*\
| Lenovo4ZoneUSBController.h |
| |
| Driver for Lenovo 4-Zone devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <array>
#include <string>
#include <utility>
#include <vector>
#include <hidapi.h>
#include "RGBController.h"
#include "LogManager.h"
#include "LenovoDevices4Zone.h"
#ifndef HID_MAX_STR
#define HID_MAX_STR 255
#endif
#define LENOVO_4_ZONE_HID_PACKET_SIZE 33
class Lenovo4ZoneUSBController
{
public:
/*--------------*\
|ctor(s) and dtor|
\*--------------*/
Lenovo4ZoneUSBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name);
~Lenovo4ZoneUSBController();
void setMode(const KeyboardState &in_mode);
/*--------------*\
|device functions|
\*--------------*/
uint16_t getPid();
std::string getName();
std::string getLocation();
private:
/*--------------*\
|data members |
\*--------------*/
std::string name;
hid_device *dev;
std::string location;
uint16_t pid;
KeyboardState mode;
/*--------------*\
|device functions|
\*--------------*/
void sendBasicInstruction(uint8_t instruction);
};
@@ -0,0 +1,50 @@
/*---------------------------------------------------------*\
| Lenovo4ZoneUSBControllerDetect.cpp |
| |
| Detector for Lenovo 4-Zone devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "Detector.h"
#include "Lenovo4ZoneUSBController.h"
#include "LenovoDevices4Zone.h"
#include "RGBController_Lenovo4ZoneUSB.h"
/*-----------------------------------------------------*\
| vendor IDs |
\*-----------------------------------------------------*/
#define ITE_VID 0x048D
/*-----------------------------------------------------*\
| Interface, Usage, and Usage Page |
\*-----------------------------------------------------*/
enum
{
LENOVO_PAGE = 0xFF89,
LENOVO_USAGE = 0xCC
};
void DetectLenovo4ZoneUSBControllers(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
Lenovo4ZoneUSBController* controller = new Lenovo4ZoneUSBController(dev, info->path, info->product_id, name);
RGBController_Lenovo4ZoneUSB* rgb_controller = new RGBController_Lenovo4ZoneUSB(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_HID_DETECTOR_PU("Lenovo Ideapad 3-15ach6", DetectLenovo4ZoneUSBControllers, ITE_VID, IDEAPAD_315ACH6, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo 5 2023", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2023_PID, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo 5 2023 Ideapad", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2023_IDEAPAD_PID, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo 5 2022", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2022_PID, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo 5 2022 Ideapad", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2022_IDEAPAD_PID, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo 5 2021", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2021_PID, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo 5 2021 Ideapad", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2021_IDEAPAD_PID, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo 5 2020", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2020_PID, LENOVO_PAGE, LENOVO_USAGE);
@@ -0,0 +1,107 @@
/*---------------------------------------------------------*\
| LenovoDevices4Zone.h |
| |
| Device list for Lenovo 4-Zone devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include "RGBControllerKeyNames.h"
#include "RGBController.h"
#include "LenovoDevices.h"
/*-----------------------------------------------------*\
| Keyboard product IDs |
\*-----------------------------------------------------*/
#define IDEAPAD_315ACH6 0xC963
#define LEGION_5_2023_PID 0xC985
#define LEGION_5_2023_IDEAPAD_PID 0xC984
#define LEGION_5_2022_PID 0xC975
#define LEGION_5_2022_IDEAPAD_PID 0xC973
#define LEGION_5_2021_PID 0xC965
#define LEGION_5_2021_IDEAPAD_PID 0xC963
#define LEGION_5_2020_PID 0xC955
enum LENOVO_4_ZONE_EFFECT
{
LENOVO_4_ZONE_EFFECT_STATIC = 1,
LENOVO_4_ZONE_EFFECT_BREATH = 3,
LENOVO_4_ZONE_EFFECT_WAVE = 4,
LENOVO_4_ZONE_EFFECT_SMOOTH = 6,
};
enum LENOVO_4_ZONE_BRIGHTNESS
{
LENOVO_4_ZONE_BRIGHTNESS_LOW = 1,
LENOVO_4_ZONE_BRIGHTNESS_HIGH = 2,
};
enum LENOVO_4_ZONE_SPEED
{
LENOVO_4_ZONE_SPEED_SLOWEST = 1,
LENOVO_4_ZONE_SPEED_SLOW = 2,
LENOVO_4_ZONE_SPEED_FAST = 3,
LENOVO_4_ZONE_SPEED_FASTEST = 4,
};
/// struct a USB packet for set the keyboard LEDs
class KeyboardState
{
public:
uint8_t header[2] = {0xCC, 0x16};
uint8_t effect = LENOVO_4_ZONE_EFFECT_STATIC;
uint8_t speed = LENOVO_4_ZONE_SPEED_SLOWEST;
uint8_t brightness = LENOVO_4_ZONE_BRIGHTNESS_LOW;
uint8_t zone0_rgb[3] = {0xFF, 0xFF, 0xFF};
uint8_t zone1_rgb[3] = {0xFF, 0xFF, 0xFF};
uint8_t zone2_rgb[3] = {0xFF, 0xFF, 0xFF};
uint8_t zone3_rgb[3] = {0xFF, 0xFF, 0xFF};
uint8_t wave_ltr = 0;
uint8_t wave_rtl = 0;
void Reset()
{
header[0] = 0xCC, header[1] = 0x16;
effect = LENOVO_4_ZONE_EFFECT_STATIC;
speed = LENOVO_4_ZONE_SPEED_SLOWEST;
brightness = LENOVO_4_ZONE_BRIGHTNESS_LOW;
zone0_rgb[0] = 0xFF, zone0_rgb[1] = 0xFF, zone0_rgb[2] = 0xFF;
zone1_rgb[0] = 0xFF, zone1_rgb[1] = 0xFF, zone1_rgb[2] = 0xFF;
zone2_rgb[0] = 0xFF, zone2_rgb[1] = 0xFF, zone2_rgb[2] = 0xFF;
zone3_rgb[0] = 0xFF, zone3_rgb[1] = 0xFF, zone3_rgb[2] = 0xFF;
wave_ltr = 0;
wave_rtl = 0;
}
void SetColors(std::vector<RGBColor> group_colors)
{
zone0_rgb[0] = RGBGetRValue(group_colors[0]);
zone0_rgb[1] = RGBGetGValue(group_colors[0]);
zone0_rgb[2] = RGBGetBValue(group_colors[0]);
zone1_rgb[0] = RGBGetRValue(group_colors[1]);
zone1_rgb[1] = RGBGetGValue(group_colors[1]);
zone1_rgb[2] = RGBGetBValue(group_colors[1]);
zone2_rgb[0] = RGBGetRValue(group_colors[2]);
zone2_rgb[1] = RGBGetGValue(group_colors[2]);
zone2_rgb[2] = RGBGetBValue(group_colors[2]);
zone3_rgb[0] = RGBGetRValue(group_colors[3]);
zone3_rgb[1] = RGBGetGValue(group_colors[3]);
zone3_rgb[2] = RGBGetBValue(group_colors[3]);
}
};
/*-------------------------*\
| 4-Zone keyboard |
\*-------------------------*/
static const lenovo_led lenovo_4_zone_leds[]
{
{0x00, "Left side"},
{0x01, "Left center"},
{0x02, "Right center"},
{0x03, "Right side"},
};
@@ -0,0 +1,176 @@
/*---------------------------------------------------------*\
| RGBController_Lenovo4ZoneUSB.cpp |
| |
| Device list for Lenovo 4-Zone devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "Lenovo4ZoneUSBController.h"
#include "LenovoDevices4Zone.h"
#include "RGBController_Lenovo4ZoneUSB.h"
#include "LogManager.h"
/**------------------------------------------------------------------*\
@name Lenovo 4 Zone USB
@category Keyboard
@type USB
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectLenovo4ZoneUSBControllers
@comment Tested on Lenovo Legion 5 2021
\*-------------------------------------------------------------------*/
#define LENOVO_4_ZONE_NUM_LEDS 4
RGBController_Lenovo4ZoneUSB::RGBController_Lenovo4ZoneUSB(Lenovo4ZoneUSBController* controller_ptr)
{
controller = controller_ptr;
name = controller->getName();
type = DEVICE_TYPE_LAPTOP;
vendor = "Lenovo";
description = "Lenovo 4-Zone device";
mode Direct;
Direct.name = "Direct";
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.brightness_min = 1;
Direct.brightness_max = 2;
modes.push_back(Direct);
mode Breath;
Breath.name = "Breathing";
Breath.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Breath.color_mode = MODE_COLORS_PER_LED;
Breath.brightness_min = 1;
Breath.brightness_max = 2;
Breath.speed_min = 1;
Breath.speed_max = 4;
modes.push_back(Breath);
mode Wave;
Wave.name = "Rainbow Wave";
Wave.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR;
Wave.color_mode = MODE_COLORS_RANDOM;
Wave.brightness_min = 1;
Wave.brightness_max = 2;
Wave.speed_min = 1;
Wave.speed_max = 4;
Wave.direction = MODE_DIRECTION_LEFT | MODE_DIRECTION_RIGHT;
modes.push_back(Wave);
mode Smooth;
Smooth.name = "Spectrum Cycle";
Smooth.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Smooth.color_mode = MODE_COLORS_RANDOM;
Smooth.brightness_min = 1;
Smooth.brightness_max = 2;
Smooth.speed_min = 1;
Smooth.speed_max = 4;
modes.push_back(Smooth);
SetupZones();
}
RGBController_Lenovo4ZoneUSB::~RGBController_Lenovo4ZoneUSB()
{
delete controller;
}
void RGBController_Lenovo4ZoneUSB::SetupZones()
{
zone new_zone;
new_zone.name = ZONE_EN_KEYBOARD;
new_zone.type = ZONE_TYPE_LINEAR;
new_zone.leds_count = LENOVO_4_ZONE_NUM_LEDS;
new_zone.leds_max = new_zone.leds_count;
new_zone.leds_min = new_zone.leds_count;
new_zone.matrix_map = NULL;
zones.push_back(new_zone);
for(unsigned int led_idx = 0; led_idx < LENOVO_4_ZONE_NUM_LEDS; led_idx++ )
{
led new_led;
new_led.name = lenovo_4_zone_leds[led_idx].name;
new_led.value = lenovo_4_zone_leds[led_idx].led_num;
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_Lenovo4ZoneUSB::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_Lenovo4ZoneUSB::UpdateSingleLED(int /*led*/)
{
}
void RGBController_Lenovo4ZoneUSB::UpdateZoneLEDs(int /*zone*/)
{
}
void RGBController_Lenovo4ZoneUSB::DeviceUpdateLEDs()
{
state.SetColors(colors);
controller->setMode(state);
}
void RGBController_Lenovo4ZoneUSB::DeviceUpdateMode()
{
state.Reset();
state.SetColors(colors);
switch (active_mode)
{
case 0:
state.effect = LENOVO_4_ZONE_EFFECT_STATIC;
break;
case 1:
state.effect = LENOVO_4_ZONE_EFFECT_BREATH;
break;
case 2:
state.effect = LENOVO_4_ZONE_EFFECT_WAVE;
state.wave_ltr = modes[active_mode].direction?0:1;
state.wave_rtl = modes[active_mode].direction?1:0;
break;
case 3:
state.effect = LENOVO_4_ZONE_EFFECT_SMOOTH;
break;
}
if(active_mode != (LENOVO_4_ZONE_EFFECT_STATIC - 1)) // mode number from 0, but in mode from 1
{
state.speed = modes[active_mode].speed;
}
state.brightness = modes[active_mode].brightness;
controller->setMode(state);
}
void RGBController_Lenovo4ZoneUSB::DeviceSaveMode()
{
/*---------------------------------------------------------*\
| This device does not support saving or multiple modes |
\*---------------------------------------------------------*/
}
@@ -0,0 +1,39 @@
/*---------------------------------------------------------*\
| RGBController_Lenovo4ZoneUSB.h |
| |
| RGBController for Lenovo 4-Zone devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <vector>
#include "LenovoDevices.h"
#include "Lenovo4ZoneUSBController.h"
#include "RGBController.h"
#define NA 0xFFFFFFFF
class RGBController_Lenovo4ZoneUSB : public RGBController
{
public:
RGBController_Lenovo4ZoneUSB(Lenovo4ZoneUSBController* controller_ptr);
~RGBController_Lenovo4ZoneUSB();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
KeyboardState state;
Lenovo4ZoneUSBController *controller;
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,101 @@
/*---------------------------------------------------------*\
| LenovoK510Controller.cpp |
| |
| Driver for Lenovo Legion K510 keyboard |
| |
| Bnyro 27 Oct 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include <vector>
#include "LenovoK510Controller.h"
using namespace std;
LenovoK510Controller::LenovoK510Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name)
{
device = dev_handle;
location = info.path;
name = dev_name;
}
LenovoK510Controller::~LenovoK510Controller()
{
hid_close(device);
}
std::string LenovoK510Controller::GetDeviceLocation()
{
return("HID: " + location);
}
std::string LenovoK510Controller::GetDeviceName()
{
return(name);
}
void LenovoK510Controller::SetMode(unsigned int color_mode, RGBColor color, unsigned char mode_value, unsigned int brigthness, unsigned int speed, unsigned int direction)
{
unsigned char usb_buf[K510_DATA_SIZE];
memset(usb_buf, 0x00, K510_DATA_SIZE);
usb_buf[0x00] = 0x04; // ReportID
// magic bytes to trigger an LED update
usb_buf[0x03] = 0x06;
usb_buf[0x04] = 0x38;
usb_buf[0x09] = mode_value;
usb_buf[0x0A] = static_cast<unsigned char>(brigthness);
// speed behaves contrary to normal expectations: the lower the value, the higher the speed
usb_buf[0x0B] = static_cast<unsigned char>(speed);
if(direction == MODE_DIRECTION_UP || direction == MODE_DIRECTION_LEFT)
{
// 0x01 reverses the direction of the animation
usb_buf[0x0C] = 0x01;
}
if(color_mode == MODE_COLORS_MODE_SPECIFIC)
{
usb_buf[0x0D] = 0x00;
usb_buf[0x0E] = static_cast<unsigned char>(RGBGetRValue(color));
usb_buf[0x0F] = static_cast<unsigned char>(RGBGetGValue(color));
usb_buf[0x10] = static_cast<unsigned char>(RGBGetBValue(color));
}
else
{
// sets color to automatic
usb_buf[0x0D] = 0x01;
}
hid_write(device, usb_buf, K510_DATA_SIZE);
}
mode LenovoK510Controller::GetCurrentState()
{
unsigned char usb_buf[K510_DATA_SIZE];
memset(usb_buf, 0x00, K510_DATA_SIZE);
usb_buf[0x00] = 0x04; // ReportID
// magic bytes to get a response containing the current configuration
usb_buf[0x03] = 0x05;
usb_buf[0x04] = 0x38;
hid_write(device, usb_buf, K510_DATA_SIZE);
unsigned char res_buf[K510_DATA_SIZE];
hid_read_timeout(device, res_buf, K510_DATA_SIZE, 50);
mode current_mode;
current_mode.value = res_buf[0x09];
current_mode.brightness = res_buf[0x0A];
current_mode.speed = res_buf[0x0B];
current_mode.direction = res_buf[0x0C];
current_mode.color_mode = res_buf[0x0D] ? MODE_COLORS_RANDOM : MODE_COLORS_MODE_SPECIFIC;
current_mode.colors.push_back(ToRGBColor(res_buf[0x0E], res_buf[0x0F], res_buf[0x10]));
return(current_mode);
}
@@ -0,0 +1,68 @@
/*---------------------------------------------------------*\
| LenovoK510Controller.h |
| |
| Driver for Lenovo Legion K510 keyboard |
| |
| Bnyro 27 Oct 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#define K510_DATA_SIZE 64
#define K510_BRIGHTNESS_DEFAULT 2
#define K510_BRIGHTNESS_MIN 0
#define K510_BRIGHTNESS_MAX 2
// the lower the speed value, the faster the animation
#define K510_SPEED_DEFAULT 2
#define K510_SPEED_MIN 4
#define K510_SPEED_MAX 0
enum
{
K510_MODE_CORRUGATED = 0x01,
K510_MODE_CLOUD = 0x02,
K510_MODE_SERPENTINE = 0x03,
K510_MODE_SPECTRUM = 0x04,
K510_MODE_BREATH = 0x05,
K510_MODE_NORMAL = 0x06,
K510_MODE_REACTION = 0x07,
K510_MODE_RIPPLES = 0x08,
K510_MODE_TRAVERSE = 0x09,
K510_MODE_STARS = 0x0A,
K510_MODE_FLOWERS = 0x0B,
K510_MODE_ROLL = 0x0C,
K510_MODE_WAVE = 0x0D,
K510_MODE_CARTOON = 0x0E,
K510_MODE_RAIN = 0x0F,
K510_MODE_SCAN = 0x10,
K510_MODE_SURMOUNT = 0x11,
K510_MODE_SPEED = 0x12,
};
class LenovoK510Controller
{
public:
LenovoK510Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name);
~LenovoK510Controller();
std::string GetDeviceLocation();
std::string GetDeviceName();
void SetMode(unsigned int color_mode, RGBColor color, unsigned char mode_value, unsigned int brightness, unsigned int speed, unsigned int direction);
mode GetCurrentState();
;
private:
hid_device* device;
std::string location;
std::string name;
};
@@ -0,0 +1,38 @@
/*---------------------------------------------------------*\
| LenovoK510ControllerDetect.cpp |
| |
| Detector for Lenovo Legion K510 keyboard |
| |
| Bnyro 27 Oct 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "RGBController_LenovoK510.h"
#include "LenovoK510Controller.h"
/*---------------------------------------------------------*\
| Lenovo vendor, product, usage and page IDs |
\*---------------------------------------------------------*/
#define LENOVO_VID 0x17EF
#define LEGION_K510_PID 0x619A
#define LENOVO_IFACE_NUM 0x01
#define LENOVO_PAGE 0xFF1C
#define LENOVO_USAGE 0x0092
void DetectLenovoLegionK510Controllers(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
LenovoK510Controller* controller = new LenovoK510Controller(dev, *info, name);
RGBController_LenovoK510* rgb_controller = new RGBController_LenovoK510(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_HID_DETECTOR_IPU("Lenovo Legion K510 Mini Pro", DetectLenovoLegionK510Controllers, LENOVO_VID, LEGION_K510_PID, LENOVO_IFACE_NUM, LENOVO_PAGE, LENOVO_USAGE);
@@ -0,0 +1,352 @@
/*---------------------------------------------------------*\
| RGBController_LenovoM300.cpp |
| |
| RGBController for Lenovo Legion K510 keyboard |
| |
| Bnyro 27 Oct 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_LenovoK510.h"
/**------------------------------------------------------------------*\
@name Lenovo Legion K510
@category Keyboard
@type USB
@save :robot:
@direct :x:
@effects :white_check_mark:
@detectors DetectLenovoLegionK510Controllers
@comment
\*-------------------------------------------------------------------*/
RGBController_LenovoK510::RGBController_LenovoK510(LenovoK510Controller* controller_ptr)
{
controller = controller_ptr;
name = controller->GetDeviceName();
vendor = "Lenovo";
type = DEVICE_TYPE_KEYBOARD;
description = "Lenovo Legion K510 Mini Pro";
location = controller->GetDeviceLocation();
mode Corrugated;
Corrugated.name = "Corrugated";
Corrugated.value = K510_MODE_CORRUGATED;
Corrugated.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_LR;
Corrugated.color_mode = MODE_COLORS_RANDOM;
Corrugated.brightness = K510_BRIGHTNESS_DEFAULT;
Corrugated.brightness_min = K510_BRIGHTNESS_MIN;
Corrugated.brightness_max = K510_BRIGHTNESS_MAX;
Corrugated.speed = K510_SPEED_DEFAULT;
Corrugated.speed_min = K510_SPEED_MIN;
Corrugated.speed_max = K510_SPEED_MAX;
Corrugated.colors.resize(1);
modes.push_back(Corrugated);
mode Cloud;
Cloud.name = "Cloud";
Cloud.value = K510_MODE_CLOUD;
Cloud.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_LR;
Cloud.color_mode = MODE_COLORS_RANDOM;
Cloud.brightness = K510_BRIGHTNESS_DEFAULT;
Cloud.brightness_min = K510_BRIGHTNESS_MIN;
Cloud.brightness_max = K510_BRIGHTNESS_MAX;
Cloud.speed = K510_SPEED_DEFAULT;
Cloud.speed_min = K510_SPEED_MIN;
Cloud.speed_max = K510_SPEED_MAX;
Cloud.colors.resize(1);
modes.push_back(Cloud);
mode Serpentine;
Serpentine.name = "Serpentine";
Serpentine.value = K510_MODE_SERPENTINE;
Serpentine.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_LR;
Serpentine.color_mode = MODE_COLORS_RANDOM;
Serpentine.brightness = K510_BRIGHTNESS_DEFAULT;
Serpentine.brightness_min = K510_BRIGHTNESS_MIN;
Serpentine.brightness_max = K510_BRIGHTNESS_MAX;
Serpentine.speed = K510_SPEED_DEFAULT;
Serpentine.speed_min = K510_SPEED_MIN;
Serpentine.speed_max = K510_SPEED_MAX;
Serpentine.colors.resize(1);
modes.push_back(Serpentine);
mode Spectrum;
Spectrum.name = "Spectrum Cycle";
Spectrum.value = K510_MODE_SPECTRUM;
Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Spectrum.color_mode = MODE_COLORS_NONE;
Spectrum.brightness = K510_BRIGHTNESS_DEFAULT;
Spectrum.brightness_min = K510_BRIGHTNESS_MIN;
Spectrum.brightness_max = K510_BRIGHTNESS_MAX;
Spectrum.speed = K510_SPEED_DEFAULT;
Spectrum.speed_min = K510_SPEED_MIN;
Spectrum.speed_max = K510_SPEED_MAX;
modes.push_back(Spectrum);
mode Breath;
Breath.name = "Breathing";
Breath.value = K510_MODE_BREATH;
Breath.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Breath.color_mode = MODE_COLORS_RANDOM;
Breath.brightness = K510_BRIGHTNESS_DEFAULT;
Breath.brightness_min = K510_BRIGHTNESS_MIN;
Breath.brightness_max = K510_BRIGHTNESS_MAX;
Breath.speed = K510_SPEED_DEFAULT;
Breath.speed_min = K510_SPEED_MIN;
Breath.speed_max = K510_SPEED_MAX;
Breath.colors.resize(1);
modes.push_back(Breath);
mode Normal;
Normal.name = "Static";
Normal.value = K510_MODE_NORMAL;
Normal.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
Normal.color_mode = MODE_COLORS_RANDOM;
Normal.brightness = K510_BRIGHTNESS_DEFAULT;
Normal.brightness_min = K510_BRIGHTNESS_MIN;
Normal.brightness_max = K510_BRIGHTNESS_MAX;
Normal.colors.resize(1);
modes.push_back(Normal);
mode Reaction;
Reaction.name = "Reaction";
Reaction.value = K510_MODE_REACTION;
Reaction.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Reaction.color_mode = MODE_COLORS_RANDOM;
Reaction.brightness = K510_BRIGHTNESS_DEFAULT;
Reaction.brightness_min = K510_BRIGHTNESS_MIN;
Reaction.brightness_max = K510_BRIGHTNESS_MAX;
Reaction.speed = K510_SPEED_DEFAULT;
Reaction.speed_min = K510_SPEED_MIN;
Reaction.speed_max = K510_SPEED_MAX;
Reaction.colors.resize(1);
modes.push_back(Reaction);
mode Ripples;
Ripples.name = "Ripples";
Ripples.value = K510_MODE_RIPPLES;
Ripples.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Ripples.color_mode = MODE_COLORS_RANDOM;
Ripples.brightness = K510_BRIGHTNESS_DEFAULT;
Ripples.brightness_min = K510_BRIGHTNESS_MIN;
Ripples.brightness_max = K510_BRIGHTNESS_MAX;
Ripples.speed = K510_SPEED_DEFAULT;
Ripples.speed_min = K510_SPEED_MIN;
Ripples.speed_max = K510_SPEED_MAX;
Ripples.colors.resize(1);
modes.push_back(Ripples);
mode Traverse;
Traverse.name = "Traverse";
Traverse.value = K510_MODE_TRAVERSE;
Traverse.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Traverse.color_mode = MODE_COLORS_RANDOM;
Traverse.brightness = K510_BRIGHTNESS_DEFAULT;
Traverse.brightness_min = K510_BRIGHTNESS_MIN;
Traverse.brightness_max = K510_BRIGHTNESS_MAX;
Traverse.speed = K510_SPEED_DEFAULT;
Traverse.speed_min = K510_SPEED_MIN;
Traverse.speed_max = K510_SPEED_MAX;
Traverse.colors.resize(1);
modes.push_back(Traverse);
mode Stars;
Stars.name = "Stars";
Stars.value = K510_MODE_STARS;
Stars.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Stars.color_mode = MODE_COLORS_RANDOM;
Stars.brightness = K510_BRIGHTNESS_DEFAULT;
Stars.brightness_min = K510_BRIGHTNESS_MIN;
Stars.brightness_max = K510_BRIGHTNESS_MAX;
Stars.speed = K510_SPEED_DEFAULT;
Stars.speed_min = K510_SPEED_MIN;
Stars.speed_max = K510_SPEED_MAX;
Stars.colors.resize(1);
modes.push_back(Stars);
mode Flowers;
Flowers.name = "Flowers";
Flowers.value = K510_MODE_FLOWERS;
Flowers.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Flowers.color_mode = MODE_COLORS_RANDOM;
Flowers.brightness = K510_BRIGHTNESS_DEFAULT;
Flowers.brightness_min = K510_BRIGHTNESS_MIN;
Flowers.brightness_max = K510_BRIGHTNESS_MAX;
Flowers.speed = K510_SPEED_DEFAULT;
Flowers.speed_min = K510_SPEED_MIN;
Flowers.speed_max = K510_SPEED_MAX;
modes.push_back(Flowers);
mode Roll;
Roll.name = "Rainbow Wave";
Roll.value = K510_MODE_ROLL;
Roll.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_UD;
Roll.color_mode = MODE_COLORS_NONE;
Roll.brightness = K510_BRIGHTNESS_DEFAULT;
Roll.brightness_min = K510_BRIGHTNESS_MIN;
Roll.brightness_max = K510_BRIGHTNESS_MAX;
Roll.speed = K510_SPEED_DEFAULT;
Roll.speed_min = K510_SPEED_MIN;
Roll.speed_max = K510_SPEED_MAX;
modes.push_back(Roll);
mode Wave;
Wave.name = "Wave";
Wave.value = K510_MODE_WAVE;
Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Wave.color_mode = MODE_COLORS_RANDOM;
Wave.brightness = K510_BRIGHTNESS_DEFAULT;
Wave.brightness_min = K510_BRIGHTNESS_MIN;
Wave.brightness_max = K510_BRIGHTNESS_MAX;
Wave.speed = K510_SPEED_DEFAULT;
Wave.speed_min = K510_SPEED_MIN;
Wave.speed_max = K510_SPEED_MAX;
Wave.colors.resize(1);
modes.push_back(Wave);
mode Cartoon;
Cartoon.name = "Cartoon";
Cartoon.value = K510_MODE_CARTOON;
Cartoon.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Cartoon.color_mode = MODE_COLORS_RANDOM;
Cartoon.brightness = K510_BRIGHTNESS_DEFAULT;
Cartoon.brightness_min = K510_BRIGHTNESS_MIN;
Cartoon.brightness_max = K510_BRIGHTNESS_MAX;
Cartoon.speed = K510_SPEED_DEFAULT;
Cartoon.speed_min = K510_SPEED_MIN;
Cartoon.speed_max = K510_SPEED_MAX;
Cartoon.colors.resize(1);
modes.push_back(Cartoon);
mode Rain;
Rain.name = "Rain";
Rain.value = K510_MODE_RAIN;
Rain.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Rain.color_mode = MODE_COLORS_RANDOM;
Rain.brightness = K510_BRIGHTNESS_DEFAULT;
Rain.brightness_min = K510_BRIGHTNESS_MIN;
Rain.brightness_max = K510_BRIGHTNESS_MAX;
Rain.speed = K510_SPEED_DEFAULT;
Rain.speed_min = K510_SPEED_MIN;
Rain.speed_max = K510_SPEED_MAX;
Rain.colors.resize(1);
modes.push_back(Rain);
mode Scan;
Scan.name = "Scan";
Scan.value = K510_MODE_SCAN;
Scan.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
Scan.color_mode = MODE_COLORS_NONE;
Scan.brightness = K510_BRIGHTNESS_DEFAULT;
Scan.brightness_min = K510_BRIGHTNESS_MIN;
Scan.brightness_max = K510_BRIGHTNESS_MAX;
modes.push_back(Scan);
mode Surmount;
Surmount.name = "Surmount";
Surmount.value = K510_MODE_SURMOUNT;
Surmount.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Surmount.color_mode = MODE_COLORS_NONE;
Surmount.brightness = K510_BRIGHTNESS_DEFAULT;
Surmount.brightness_min = K510_BRIGHTNESS_MIN;
Surmount.brightness_max = K510_BRIGHTNESS_MAX;
modes.push_back(Surmount);
mode Speed;
Speed.name = "Speed";
Speed.value = K510_MODE_SPEED;
Speed.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_LR;
Speed.color_mode = MODE_COLORS_NONE;
Speed.brightness = K510_BRIGHTNESS_DEFAULT;
Speed.brightness_min = K510_BRIGHTNESS_MIN;
Speed.brightness_max = K510_BRIGHTNESS_MAX;
Speed.speed = K510_SPEED_DEFAULT;
Speed.speed_min = K510_SPEED_MIN;
Speed.speed_max = K510_SPEED_MAX;
modes.push_back(Speed);
SetupZones();
ReadAndUpdateCurrentDeviceState();
}
RGBController_LenovoK510::~RGBController_LenovoK510()
{
delete controller;
}
void RGBController_LenovoK510::SetupZones()
{
zone default_zone;
default_zone.name = "Keyboard";
default_zone.type = ZONE_TYPE_SINGLE;
default_zone.leds_min = 1;
default_zone.leds_max = 1;
default_zone.leds_count = 1;
default_zone.matrix_map = nullptr;
zones.emplace_back(default_zone);
leds.resize(1);
leds[0].name = "Keyboard";
SetupColors();
}
void RGBController_LenovoK510::ReadAndUpdateCurrentDeviceState()
{
mode current_active_mode = controller->GetCurrentState();
for(std::vector<mode>::size_type i = 0; i < modes.size(); ++i)
{
if(modes[i].value == current_active_mode.value)
{
// override the default config of the mode with the current one
modes[i].brightness = current_active_mode.brightness;
modes[i].speed = current_active_mode.speed;
modes[i].color_mode = current_active_mode.color_mode;
zones[0].colors[0] = current_active_mode.colors[0];
if(modes[i].flags & MODE_FLAG_HAS_DIRECTION_LR)
{
current_active_mode.direction = current_active_mode.direction ? MODE_DIRECTION_LEFT : MODE_DIRECTION_RIGHT;
}
else if(modes[i].flags & MODE_FLAG_HAS_DIRECTION_UD)
{
current_active_mode.direction = current_active_mode.direction ? MODE_DIRECTION_UP : MODE_DIRECTION_DOWN;
}
active_mode = (int)i;
break;
}
}
}
void RGBController_LenovoK510::ResizeZone(int /*zone*/, int /*new_size*/)
{
// Not Supported
}
void RGBController_LenovoK510::DeviceUpdateLEDs()
{
// Not Supported
}
void RGBController_LenovoK510::UpdateZoneLEDs(int /*zone*/)
{
// Not Supported
}
void RGBController_LenovoK510::UpdateSingleLED(int /*led*/)
{
// Not Supported
}
void RGBController_LenovoK510::DeviceUpdateMode()
{
const mode& active = modes[active_mode];
RGBColor color = active.colors.size() > 0 ? active.colors[0] : 0x00;
controller->SetMode(active.color_mode, color, active.value, active.brightness, active.speed, active.direction);
}
@@ -0,0 +1,36 @@
/*---------------------------------------------------------*\
| RGBController_LenovoK510.h |
| |
| RGBController for Lenovo Legion K510 keyboard |
| |
| Bnyro 27 Oct 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "LenovoK510Controller.h"
class RGBController_LenovoK510 : public RGBController
{
public:
RGBController_LenovoK510(LenovoK510Controller* controller_ptr);
~RGBController_LenovoK510();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
LenovoK510Controller* controller;
void ReadAndUpdateCurrentDeviceState();
};
@@ -0,0 +1,94 @@
/*---------------------------------------------------------*\
| LenovoM300Controller.cpp |
| |
| Driver for Lenovo Legion M300 mouse |
| |
| Wayne Riordan 09 Jan 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include "LenovoM300Controller.h"
using namespace std;
LenovoM300Controller::LenovoM300Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name)
{
device = dev_handle;
location = info.path;
name = dev_name;
}
LenovoM300Controller::~LenovoM300Controller()
{
hid_close(device);
}
std::string LenovoM300Controller::GetDeviceLocation()
{
return("HID: " + location);
}
std::string LenovoM300Controller::GetDeviceName()
{
return(name);
}
void LenovoM300Controller::SetMode(std::vector<RGBColor> colors, unsigned char mode_value, unsigned int brigthness)
{
unsigned char usb_buf[M300_DATA_SIZE];
memset(usb_buf, 0x00, M300_DATA_SIZE);
usb_buf[0x01] = 0x25;
usb_buf[0x03] = 0x01;
usb_buf[0x04] = 0x01;
usb_buf[0x05] = 0x0C;
switch(mode_value)
{
case M300_MODE_RAINBOW:
usb_buf[0x06] = 0x01;
usb_buf[0x07] = 0x64;
usb_buf[0x09] = 0x0A;
usb_buf[0x40] = CalculateFinalByte(usb_buf, 0x0A);
break;
case M300_MODE_BREATHING:
usb_buf[0x06] = 0x02;
usb_buf[0x07] = 0x64;
usb_buf[0x09] = 0x02;
usb_buf[0x0A] = 0x03;
usb_buf[0x0C] = RGBGetRValue(colors[0]);
usb_buf[0x0D] = RGBGetGValue(colors[0]);
usb_buf[0x0E] = RGBGetBValue(colors[0]);
usb_buf[0x0F] = RGBGetRValue(colors[1]);
usb_buf[0x10] = RGBGetGValue(colors[1]);
usb_buf[0x11] = RGBGetBValue(colors[1]);
usb_buf[0x40] = CalculateFinalByte(usb_buf, 0x12);
break;
case M300_MODE_STATIC:
usb_buf[0x06] = 0x03;
usb_buf[0x07] = brigthness;
usb_buf[0x09] = RGBGetRValue(colors[0]);
usb_buf[0x0A] = RGBGetGValue(colors[0]);
usb_buf[0x0B] = RGBGetBValue(colors[0]);
usb_buf[0x40] = CalculateFinalByte(usb_buf, 0x0C);
break;
case M300_MODE_OFF:
default:
usb_buf[0x06] = 0x03;
usb_buf[0x40] = 0x36;
}
hid_write(device, usb_buf, M300_DATA_SIZE);
}
unsigned char LenovoM300Controller::CalculateFinalByte(unsigned char* ptr, int count)
{
unsigned char final_byte = 0;
for(int i = 0; i < count; i++)
{
final_byte += ptr[i];
}
return final_byte;
}
@@ -0,0 +1,49 @@
/*---------------------------------------------------------*\
| LenovoM300Controller.h |
| |
| Driver for Lenovo Legion M300 mouse |
| |
| Wayne Riordan 09 Jan 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#define M300_DATA_SIZE 0x41
#define M300_MAX_BRIGTH 0x64
#define M300_MIN_BRIGHT 0x01
enum
{
M300_MODE_OFF = 0x00,
M300_MODE_RAINBOW = 0x01,
M300_MODE_BREATHING = 0x02,
M300_MODE_STATIC = 0X03
};
class LenovoM300Controller
{
public:
LenovoM300Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name);
~LenovoM300Controller();
std::string GetDeviceLocation();
std::string GetDeviceName();
void SetMode(std::vector<RGBColor> colors, unsigned char mode_value, unsigned int brightness);
protected:
hid_device* device;
private:
std::string location;
std::string name;
unsigned char CalculateFinalByte(unsigned char* ptr, int count);
};
@@ -0,0 +1,37 @@
/*---------------------------------------------------------*\
| LenovoM300ControllerDetect.cpp |
| |
| Detector for Lenovo Legion M300 mouse |
| |
| Wayne Riordan 09 Jan 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "RGBController_LenovoM300.h"
#include "LenovoM300Controller.h"
/*---------------------------------------------------------*\
| Lenovo vendor, product, usage and page IDs |
\*---------------------------------------------------------*/
#define LENOVO_VID 0x17EF
#define LEGION_M300_PID 0x60E4
#define LENOVO_USAGE 0X01
#define LENOVO_PAGE 0XFF01
void DetectLenovoLegionM300Controllers(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
LenovoM300Controller* controller = new LenovoM300Controller(dev, *info, name);
RGBController_LenovoM300* rgb_controller = new RGBController_LenovoM300(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_HID_DETECTOR_PU("Lenovo Legion M300", DetectLenovoLegionM300Controllers, LENOVO_VID, LEGION_M300_PID, LENOVO_PAGE, LENOVO_USAGE);
@@ -0,0 +1,120 @@
/*---------------------------------------------------------*\
| RGBController_LenovoM300.cpp |
| |
| RGBController for Lenovo Legion M300 mouse |
| |
| Wayne Riordan 09 Jan 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_LenovoM300.h"
/**------------------------------------------------------------------*\
@name Lenovo Legion M300
@category Mouse
@type USB
@save :robot:
@direct :x:
@effects :white_check_mark:
@detectors DetectLenovoLegionM300Controllers
@comment
\*-------------------------------------------------------------------*/
RGBController_LenovoM300::RGBController_LenovoM300(LenovoM300Controller* controller_ptr)
{
controller = controller_ptr;
name = controller->GetDeviceName();
vendor = "Lenovo";
type = DEVICE_TYPE_MOUSE;
description = "Lenovo M300 Device";
location = controller->GetDeviceLocation();
mode Static;
Static.name = "Static";
Static.value = M300_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
Static.brightness_max = M300_MAX_BRIGTH;
Static.brightness_min = M300_MIN_BRIGHT;
Static.brightness = M300_MAX_BRIGTH;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.colors.resize(1);
modes.push_back(Static);
mode Off;
Off.name = "Off";
Off.value = M300_MODE_OFF;
Off.flags = MODE_FLAG_AUTOMATIC_SAVE;
Off.color_mode = MODE_COLORS_NONE;
modes.push_back(Off);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = M300_MODE_BREATHING;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
Breathing.brightness_max = M300_MAX_BRIGTH;
Breathing.brightness_min = M300_MIN_BRIGHT;
Breathing.brightness = M300_MAX_BRIGTH;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
Breathing.colors.resize(2);
modes.push_back(Breathing);
mode Spectrum;
Spectrum.name = "Spectrum Cycle";
Spectrum.value = M300_MODE_RAINBOW;
Spectrum.flags = MODE_FLAG_AUTOMATIC_SAVE;
Spectrum.color_mode = MODE_COLORS_NONE;
modes.push_back(Spectrum);
SetupZones();
}
RGBController_LenovoM300::~RGBController_LenovoM300()
{
delete controller;
}
void RGBController_LenovoM300::SetupZones()
{
zone default_zone;
default_zone.name = "Mouse";
default_zone.type = ZONE_TYPE_SINGLE;
default_zone.leds_min = 1;
default_zone.leds_max = 1;
default_zone.leds_count = 1;
default_zone.matrix_map = nullptr;
zones.emplace_back(default_zone);
leds.resize(1);
leds[0].name = "LED 1";
SetupColors();
}
void RGBController_LenovoM300::ResizeZone(int /*zone*/, int /*new_size*/)
{
// Not Supported
}
void RGBController_LenovoM300::DeviceUpdateLEDs()
{
const mode& active = modes[active_mode];
controller->SetMode(active.colors, active.value, active.brightness);
}
void RGBController_LenovoM300::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_LenovoM300::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_LenovoM300::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
@@ -0,0 +1,34 @@
/*---------------------------------------------------------*\
| RGBController_LenovoM300.h |
| |
| RGBController for Lenovo Legion M300 mouse |
| |
| Wayne Riordan 09 Jan 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "LenovoM300Controller.h"
class RGBController_LenovoM300 : public RGBController
{
public:
RGBController_LenovoM300(LenovoM300Controller* controller_ptr);
~RGBController_LenovoM300();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
LenovoM300Controller* controller;
};
@@ -0,0 +1,125 @@
/*---------------------------------------------------------*\
| LenovoUSBController.cpp |
| |
| Driver for Lenovo USB devices |
| |
| Cooper Hall (geobot19) 17 Apr 2022 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "LenovoUSBController.h"
#include "LogManager.h"
#include "StringUtils.h"
using namespace std;
LenovoUSBController::LenovoUSBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name)
{
dev = dev_handle;
location = path;
pid = in_pid;
name = dev_name;
setDeviceSoftwareMode();
}
LenovoUSBController::~LenovoUSBController()
{
hid_close(dev);
}
uint16_t LenovoUSBController::getPid()
{
return pid;
}
string LenovoUSBController::getName()
{
return name;
}
string LenovoUSBController::getLocation()
{
return location;
}
void LenovoUSBController::setZoneLeds(uint8_t zone_num, vector<pair<uint8_t, RGBColor>> &led_colors)
{
for(size_t curr = 0; curr < led_colors.size();)
{
uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, (uint8_t) (LENOVO_ZONE_ID_0+zone_num), 0, 0};
/*---------------------------------------------------------*\
| Set the colour bytes in the packet |
| buffer[2] is required to know the amount of leds in packet|
| so it is set here as well |
\*---------------------------------------------------------*/
for(; buffer[2] < LENOVO_MAX_LEDS_PER_PACKET && curr < led_colors.size(); ++buffer[2])
{
uint8_t offset = (buffer[2] * 4) + 4;
/*------------------*\
|write the led number|
\*------------------*/
buffer[offset] = led_colors[curr].first;
/*--------------*\
|write the colors|
\*--------------*/
buffer[offset + 1] = RGBGetRValue(led_colors[curr].second);
buffer[offset + 2] = RGBGetGValue(led_colors[curr].second);
buffer[offset + 3] = RGBGetBValue(led_colors[curr].second);
++curr;
}
hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE);
}
}
void LenovoUSBController::setSingleLED(uint8_t zone_num, uint8_t led_num, RGBColor color)
{
uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, (uint8_t) (LENOVO_ZONE_ID_0+zone_num), 1, 0, led_num, (uint8_t) (RGBGetRValue(color)), (uint8_t) (RGBGetGValue(color)), (uint8_t) (RGBGetBValue(color))};
hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE);
}
void LenovoUSBController::sendBasicInstruction(uint8_t instruction)
{
uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, instruction};
hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE);
}
vector<uint8_t> LenovoUSBController::getInformation(uint8_t information_id)
{
uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, information_id};
hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE);
uint8_t read_buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START};
int num_bytes = hid_get_feature_report(dev, read_buffer, LENOVO_HID_PACKET_SIZE);
if(num_bytes > 0)
{
vector<uint8_t> response(&read_buffer[0], &read_buffer[num_bytes]);
return response;
}
return vector<uint8_t>();
}
void LenovoUSBController::setDeviceSoftwareMode()
{
/*---------------------------------------*\
| this is required for the device listen |
| to the software protocol |
\*---------------------------------------*/
sendBasicInstruction(0xB2);
}
void LenovoUSBController::setDeviceHardwareMode()
{
/*---------------------------------------*\
|releases the device from sofware mode so |
|that onboard controlls can be used |
|this has not been shown to happen between|
|reboots |
\*---------------------------------------*/
sendBasicInstruction(0xB1);
}
@@ -0,0 +1,69 @@
/*---------------------------------------------------------*\
| LenovoUSBController.h |
| |
| Driver for Lenovo USB devices |
| |
| Cooper Hall (geobot19) 17 Apr 2022 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <array>
#include <string>
#include <utility>
#include <vector>
#include <hidapi.h>
#include "RGBController.h"
#include "LogManager.h"
#ifndef HID_MAX_STR
#define HID_MAX_STR 255
#endif
#ifndef LENOVOUSBCONTROLLER_H
#define LENOVOUSBCONTROLLER_H
#define LENOVO_INSTRUCTION_START 0x07
#define LENOVO_ZONE_ID_0 0xA0
#define LENOVO_HID_PACKET_SIZE 192
#define LENOVO_MAX_LEDS_PER_PACKET 0x2F
class LenovoUSBController
{
public:
/*--------------*\
|ctor(s) and dtor|
\*--------------*/
LenovoUSBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name);
~LenovoUSBController();
/*--------------*\
|device functions|
\*--------------*/
void setZoneLeds(uint8_t zone_num, std::vector<std::pair<uint8_t, RGBColor>> &led_colors);
void setSingleLED(uint8_t zone_num, uint8_t led_num, RGBColor color);
uint16_t getPid();
std::string getName();
std::string getLocation();
std::vector<uint8_t> getInformation(uint8_t information_id);
void setDeviceSoftwareMode();
void setDeviceHardwareMode();
private:
/*--------------*\
|data members |
\*--------------*/
std::string name;
hid_device *dev;
std::string location;
uint16_t pid;
/*--------------*\
|device functions|
\*--------------*/
void sendBasicInstruction(uint8_t instruction);
};
#endif
@@ -0,0 +1,477 @@
/*---------------------------------------------------------*\
| RGBController_Lenovo_USB.cpp |
| |
| RGBController for Lenovo USB devices |
| |
| Cooper Hall (geobot19) 17 Apr 2022 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <iomanip>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "LenovoDevices.h"
#include "RGBController_LenovoUSB.h"
#include "LogManager.h"
using namespace std;
/*--------------------------------------------------------------------------------------*\
|note: the RGBController_LenovoUSB constructor determines which list of leds to pull from|
\*--------------------------------------------------------------------------------------*/
/**------------------------------------------------------------------*\
@name Lenovo USB
@category Keyboard
@type USB
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectLenovoLegionUSBControllers
@comment Tested on Lenovo Legion 7/7i gen. 6 ANSI and ISO models
Hardware modes are not implented
PLEASE UPDATE YOUR BIOS IF YOU HAVE ISSUES WITH HARDWARE MODES AFTER CLOSING OPENRGB
If you have other models beside the Legion 7 gen 6 and want to test if RGB can be added to OpenRGB,
you can do so if you're on Windows by running
[this Powershell script](https://gitlab.com/pvazny/legion7-rgb-ps) with the `-test` parameter:
```powershell
legion7-rgb.ps1 -test -v 0x048D -p 0xC935 -up 0xFF89 -u 0x07 -c 0xa1 -start 0x15 -stop 0xa1
```
This script will iterate through each LED one by one, in bank `$c = 0xa1`, between IDs
`-start 0x15` and `-stop 0xa1`, on the HID device with VID `-v 0x048D` PID `-p 0xC935` usage page
`-up 0xFF89` and usage `-u 0x07`. It will default to the Legion 7 Gen 6 description, however you can
enter a new description for each LED and at the end it will generate the differences.
To check if you have an eligible HID device that could potentially be iterated by the script please
open the powershell command prompt and executre the follow:
```powershell
Get-PnpDevice -Class 'HIDClass' | ForEach-Object { [PSCustomObject]@{Name = $_.FriendlyName; InstanceId = $_.InstanceId; HardwareId = ($_.HardwareId | Where-Object {$_ -like 'HID_DEVICE_UP:*' })}} | Where-Object { $_.HardwareId -ne $null } | Sort-Object InstanceId
```
If the script is successful please create a [new device issue](https://gitlab.com/CalcProgrammer1/OpenRGB/-/issues/new?issuable_template=New%20Device#)
and attach the relevant details to request support for your device.
\*-------------------------------------------------------------------*/
RGBController_LenovoUSB::RGBController_LenovoUSB(LenovoUSBController* controller_ptr)
{
controller = controller_ptr;
mode Direct;
Direct.name = "Direct";
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
name = controller->getName();
type = DEVICE_TYPE_KEYBOARD;
vendor = "Lenovo";
if(LogManager::get()->getLoglevel() >= LL_TRACE)
{
DumpControllerInformation();
}
std::vector<uint8_t> response;
/*-----------------------*\
|Default to ANSI keyboard |
\*-----------------------*/
keyboard_type = ANSI;
switch(controller->getPid())
{
case LEGION_Y740:
response = controller->getInformation(0x01);
if(response.size() > 4 && response[4] <= 100)
{
chasis_size = FIFTEEN;
}
else
{
chasis_size = SEVENTEEN;
}
response = controller->getInformation(0x04);
if(response.size() > 4)
{
if(response[4] >= 16 && response[4] <=48)
{
keyboard_type = ISO;
}
}
description = "Lenovo Y740 " + sizeToString(chasis_size) + " " + keyboardToString(keyboard_type);
break;
case LEGION_Y750:
response = controller->getInformation(0x04);
if(response.size() > 4)
{
if(response[4] == 41)
{
keyboard_type = JAPAN;
}
else if(response[4] >= 16 && response[4] <=40)
{
keyboard_type = ISO;
}
}
description = "Lenovo Y750 " + keyboardToString(keyboard_type);
break;
case LEGION_Y750S:
response = controller->getInformation(0x01);
if(response.size() > 4)
{
if(response[4] == 0x97)
{
keyboard_type = JAPAN;
}
else if(response[4] == 0x91)
{
keyboard_type = ISO;
}
}
description = "Lenovo Y750S " + keyboardToString(keyboard_type);
break;
case LEGION_Y760:
response = controller->getInformation(0x07);
if(response.size() > 4)
{
if(response[4] == 41)
{
keyboard_type = JAPAN;
}
else if(response[4] >= 16 && response[4] <=40)
{
keyboard_type = ISO;
}
}
description = "Lenovo Y760 " + keyboardToString(keyboard_type);
break;
case LEGION_Y760S:
response = controller->getInformation(0x01);
if(response.size() > 4)
{
if(response[4] == 0x97)
{
keyboard_type = JAPAN;
}
else if(response[4] == 0x8F)
{
keyboard_type = ISO;
}
}
description = "Lenovo Y760S " + keyboardToString(keyboard_type);
}
LOG_DEBUG("[Lenovo Controller] detected: %s", description.c_str());
SetupZones();
}
RGBController_LenovoUSB::~RGBController_LenovoUSB()
{
/*--------------------------------*\
| see LenovoUSBController.cpp for |
| details |
\*--------------------------------*/
controller->setDeviceHardwareMode();
delete controller;
}
void RGBController_LenovoUSB::SetupZones()
{
vector<lenovo_zone> lenovo_zones;
switch(controller->getPid())
{
case LEGION_Y740:
switch(chasis_size)
{
case FIFTEEN:
switch(keyboard_type)
{
case ISO:
lenovo_zones.push_back(lenovo_legion_Y740_15_kbd_iso);
break;
default:
lenovo_zones.push_back(lenovo_legion_Y740_15_kbd_ansi);
break;
}
break;
case SEVENTEEN:
default:
switch(keyboard_type)
{
case ISO:
lenovo_zones.push_back(lenovo_legion_Y740_17_kbd_iso);
break;
default:
lenovo_zones.push_back(lenovo_legion_Y740_17_kbd_ansi);
break;
}
break;
}
lenovo_zones.push_back(lenovo_legion_Y740_logo);
lenovo_zones.push_back(lenovo_legion_Y740_pwrbtn);
lenovo_zones.push_back(lenovo_legion_Y740_vents);
lenovo_zones.push_back(lenovo_legion_Y740_ports);
break;
case LEGION_Y750:
switch(keyboard_type)
{
case JAPAN:
lenovo_zones.push_back(lenovo_legion_Y760_kbd_jp);
break;
case ISO:
lenovo_zones.push_back(lenovo_legion_Y760_kbd_iso);
break;
default:
lenovo_zones.push_back(lenovo_legion_Y760_kbd_ansi);
break;
}
lenovo_zones.push_back(lenovo_legion_Y750_logo);
lenovo_zones.push_back(lenovo_legion_Y750_vents);
lenovo_zones.push_back(lenovo_legion_Y750_neon);
break;
case LEGION_Y750S:
case LEGION_Y760S:
switch(keyboard_type)
{
case JAPAN:
lenovo_zones.push_back(lenovo_legion_Y760_kbd_jp);
break;
case ISO:
lenovo_zones.push_back(lenovo_legion_Y760_kbd_iso);
break;
default:
lenovo_zones.push_back(lenovo_legion_Y760_kbd_ansi);
break;
}
break;
case LEGION_Y760:
switch(keyboard_type)
{
case JAPAN:
lenovo_zones.push_back(lenovo_legion_Y760_kbd_jp);
break;
case ISO:
lenovo_zones.push_back(lenovo_legion_Y760_kbd_iso);
break;
default:
lenovo_zones.push_back(lenovo_legion_Y760_kbd_ansi);
break;
}
lenovo_zones.push_back(lenovo_legion_Y760_logo);
lenovo_zones.push_back(lenovo_legion_Y760_vent_left);
lenovo_zones.push_back(lenovo_legion_Y760_vent_right);
lenovo_zones.push_back(lenovo_legion_Y760_vent_back_right);
lenovo_zones.push_back(lenovo_legion_Y760_vent_back_left);
lenovo_zones.push_back(lenovo_legion_Y760_neon);
break;
case LEGION_S7GEN7:
lenovo_zones.push_back(legion7_gen7and8_kbd_ansi);
break;
case LEGION_7GEN7:
lenovo_zones.push_back(legion7_gen7and8_kbd_ansi);
lenovo_zones.push_back(lenovo_legion_7gen7_logo);
lenovo_zones.push_back(lenovo_legion_7gen7_vents);
lenovo_zones.push_back(legion7_gen7and8_neon);
break;
case LEGION_7GEN8:
case LEGION_7GEN9:
case LEGION_7GEN9_H:
lenovo_zones.push_back(legion7_gen7and8_kbd_ansi);
lenovo_zones.push_back(legion7_gen7and8_neon);
break;
case LEGION_S7GEN8:
lenovo_zones.push_back(legion7_gen7and8_kbd_ansi);
break;
}
for(unsigned int i = 0; i < lenovo_zones.size(); i++)
{
zone new_zone;
new_zone.name = lenovo_zones[i].name;
new_zone.type = lenovo_zones[i].type;
new_zone.leds_count = lenovo_zones[i].end - lenovo_zones[i].start + 1;
new_zone.leds_max = new_zone.leds_count;
new_zone.leds_min = new_zone.leds_count;
LOG_DEBUG("[Lenovo Controller] adding zone: %s with %u LEDs", new_zone.name.c_str(), new_zone.leds_count);
if(lenovo_zones[i].type == ZONE_TYPE_MATRIX)
{
new_zone.matrix_map = new matrix_map_type;
new_zone.matrix_map->height = lenovo_zones[i].height;
new_zone.matrix_map->width = lenovo_zones[i].width;
new_zone.matrix_map->map = (unsigned int *) lenovo_zones[i].matrix_map;
}
else
{
new_zone.matrix_map = NULL;
}
zones.push_back(new_zone);
for(unsigned int led_idx = lenovo_zones[i].start; led_idx <= lenovo_zones[i].end; led_idx++ )
{
led new_led;
new_led.name = lenovo_zones[i].leds[led_idx].name;
new_led.value = ( lenovo_zones[i].id << 8 ) + lenovo_zones[i].leds[led_idx].led_num;
leds.push_back(new_led);
}
}
SetupColors();
}
void RGBController_LenovoUSB::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_LenovoUSB::UpdateSingleLED(int led)
{
if(led != (int)NA)
{
controller->setSingleLED(leds[led].value >> 8, leds[led].value & 0xFF, colors[led]);
}
}
void RGBController_LenovoUSB::UpdateZoneLEDs(int zone)
{
uint8_t zone_id = zones[zone].leds_count > 0 ? leds[zones[zone].start_idx].value >> 8 : 0;
vector<pair<uint8_t, RGBColor>> color_map;
for(unsigned int i = 0; i < zones[zone].leds_count; i++)
{
int index = zones[zone].start_idx+i;
color_map.push_back({(uint8_t)leds[index].value & 0xFF, colors[index]});
}
color_map.shrink_to_fit();
controller->setZoneLeds(zone_id, color_map);
}
void RGBController_LenovoUSB::DeviceUpdateLEDs()
{
uint8_t zone_id = 0;
uint8_t prev_zone_id = 0;
vector<pair<uint8_t, RGBColor>> curr_color_map;
for(unsigned int i = 0; i < leds.size(); i++)
{
zone_id = leds[i].value >> 8;
if((zone_id != prev_zone_id) && (prev_zone_id != 0))
{
controller->setZoneLeds(prev_zone_id, curr_color_map);
curr_color_map.clear();
}
prev_zone_id = zone_id;
curr_color_map.push_back({(uint8_t)(leds[i].value & 0xFF), colors[i]});
}
if(curr_color_map.size() > 0)
{
controller->setZoneLeds(prev_zone_id, curr_color_map);
}
}
void RGBController_LenovoUSB::DeviceUpdateMode()
{
/*---------------------------------------------------------*\
| This device does not support multiple modes |
\*---------------------------------------------------------*/
}
void RGBController_LenovoUSB::DeviceSaveMode()
{
/*---------------------------------------------------------*\
| This device does not support saving or multiple modes |
\*---------------------------------------------------------*/
}
std::string RGBController_LenovoUSB::ConvertBytesToHex(const std::vector<uint8_t> &input)
{
std::ostringstream temp_stream;
for(const uint8_t &oneInputByte : input)
{
temp_stream << (temp_stream.tellp()==0 ? "" : " ") << std::setw(2) << std::setfill('0') << std::hex << (int)oneInputByte;
}
return temp_stream.str();
}
std::string RGBController_LenovoUSB::keyboardToString(LENOVO_KEYBOARD kb)
{
switch(kb)
{
case LENOVO_KEYBOARD::ANSI:
return "ANSI";
case LENOVO_KEYBOARD::ISO:
return "ISO";
case LENOVO_KEYBOARD::JAPAN:
return "JAPAN";
default:
return "Unknown";
}
}
std::string RGBController_LenovoUSB::sizeToString(LENOVO_SIZE size)
{
switch(size)
{
case LENOVO_SIZE::FIFTEEN:
return "15\"";
case LENOVO_SIZE::SEVENTEEN:
return "17\"";
default:
return "Unknown";
}
}
void RGBController_LenovoUSB::DumpControllerInformation()
{
for(uint8_t i=1;i<=7;i++)
{
std::vector<uint8_t> response = controller->getInformation(i);
LOG_TRACE("[Lenovo Controller] Read values [%02x]: %s", i, ConvertBytesToHex(response).c_str());
}
}
@@ -0,0 +1,47 @@
/*---------------------------------------------------------*\
| RGBController_Lenovo_USB.h |
| |
| RGBController for Lenovo USB devices |
| |
| Cooper Hall (geobot19) 17 Apr 2022 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <vector>
#include "LenovoDevices.h"
#include "LenovoUSBController.h"
#include "RGBController.h"
#define NA 0xFFFFFFFF
class RGBController_LenovoUSB : public RGBController
{
public:
RGBController_LenovoUSB(LenovoUSBController* controller_ptr);
~RGBController_LenovoUSB();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
std::string ConvertBytesToHex(const std::vector<uint8_t> &input);
std::string keyboardToString(LENOVO_KEYBOARD kb);
std::string sizeToString(LENOVO_SIZE size);
void DumpControllerInformation();
LENOVO_KEYBOARD keyboard_type;
LENOVO_SIZE chasis_size;
LenovoUSBController *controller;
};
@@ -0,0 +1,70 @@
/*---------------------------------------------------------*\
| LenovoUSBControllerDetect.cpp |
| |
| Detector for Lenovo USB devices |
| |
| Cooper Hall (geobot19) 17 Apr 2022 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "Detector.h"
#include "LenovoDevices.h"
#include "RGBController_LenovoUSB.h"
#include "RGBController_Lenovo_Gen7_8.h"
/*-----------------------------------------------------*\
| vendor IDs |
\*-----------------------------------------------------*/
#define ITE_VID 0x048D
/*-----------------------------------------------------*\
| Interface, Usage, and Usage Page |
\*-----------------------------------------------------*/
enum
{
LENOVO_PAGE = 0xFF89,
LENOVO_USAGE = 0x07
};
void DetectLenovoLegionUSBControllers(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
LenovoUSBController* controller = new LenovoUSBController(dev, info->path, info->product_id, name);
RGBController_LenovoUSB* rgb_controller = new RGBController_LenovoUSB(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
void DetectLenovoLegionUSBControllersGen7And8(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
LenovoGen7And8USBController* controller = new LenovoGen7And8USBController(dev, info->path, info->product_id, name);
LenovoRGBController_Gen7_8* rgb_controller = new LenovoRGBController_Gen7_8(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_HID_DETECTOR_PU("Lenovo Legion Y740", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_Y740, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 5", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_Y750, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7S Gen 5", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_Y750S, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 6", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_Y760, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7S Gen 6", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_Y760S, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7S Gen 7", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_S7GEN7, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 7", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN7, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 8", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN8, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7S Gen 8", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_S7GEN8, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 9", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN9, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 9", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN9_H, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 10", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN10, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 5 Gen 10", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_5GEN10, LENOVO_PAGE, LENOVO_USAGE);
@@ -0,0 +1,376 @@
/*---------------------------------------------------------*\
| LenovoUSBController_Gen7_8.cpp |
| |
| Driver for Lenovo Gen7 and Gen8 devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <iomanip>
#include <sstream>
#include "LenovoDevices.h"
#include "LenovoUSBController_Gen7_8.h"
#include "StringUtils.h"
using namespace std;
static void SetGen10PayloadLength(uint16_t pid, uint8_t* buffer, uint16_t payload_length)
{
if(pid != LEGION_7GEN10 && pid != LEGION_5GEN10)
{
return;
}
buffer[2] = payload_length & 0xFF;
buffer[3] = (payload_length >> 8) & 0xFF;
}
static bool UsesGen10PacketFormat(uint16_t pid)
{
return pid == LEGION_7GEN10 || pid == LEGION_5GEN10;
}
LenovoGen7And8USBController::LenovoGen7And8USBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name)
{
dev = dev_handle;
location = path;
pid = in_pid;
name = dev_name;
}
LenovoGen7And8USBController::~LenovoGen7And8USBController()
{
hid_close(dev);
}
uint16_t LenovoGen7And8USBController::getPid()
{
return pid;
}
string LenovoGen7And8USBController::getName()
{
return name;
}
string LenovoGen7And8USBController::getLocation()
{
return location;
}
void LenovoGen7And8USBController::setLedsByGroup(uint8_t profile_id, vector<led_group> led_groups)
{
if(led_groups.empty())
{
return;
}
/*---------------------------------------------------------*\
| Some devices require many groups for per-key updates. |
| Send as many groups as fit in one report, then continue |
| in additional reports. |
\*---------------------------------------------------------*/
size_t group = 0;
while(group < led_groups.size())
{
uint8_t buffer[PACKET_SIZE];
memset(buffer, 0x00, PACKET_SIZE);
size_t i = 0;
buffer[i++] = REPORT_ID;
buffer[i++] = SAVE_PROFILE;
buffer[i++] = 0xC0;
buffer[i++] = 0x03;
buffer[i++] = profile_id;
buffer[i++] = 0x01;
buffer[i++] = 0x01;
for(; group < led_groups.size() && i < PACKET_SIZE - 21; group++)
{
buffer[i++] = (uint8_t)group + 1; //Group index
buffer[i++] = 0x06;
buffer[i++] = 0x01;
buffer[i++] = led_groups[group].mode;
buffer[i++] = 0x02;
buffer[i++] = led_groups[group].speed;
buffer[i++] = 0x03;
buffer[i++] = led_groups[group].spin;
buffer[i++] = 0x04;
buffer[i++] = led_groups[group].direction;
buffer[i++] = 0x05;
buffer[i++] = led_groups[group].color_mode;
buffer[i++] = 0x06;
buffer[i++] = 0x00;
buffer[i++] = (uint8_t)led_groups[group].colors.size();
for(RGBColor c : led_groups[group].colors)
{
buffer[i++] = RGBGetRValue(c);
buffer[i++] = RGBGetGValue(c);
buffer[i++] = RGBGetBValue(c);
}
vector<uint16_t> leds = led_groups[group].leds;
size_t led_count = min(leds.size(), (PACKET_SIZE - i)/2);
buffer[i++] = (uint8_t)led_count;
uint8_t* byte_ptr = reinterpret_cast<uint8_t*>(leds.data());
std::copy(byte_ptr, byte_ptr + led_count * sizeof(uint16_t), buffer + i);
i+= led_count * sizeof(uint16_t);
}
if(UsesGen10PacketFormat(pid))
{
SetGen10PayloadLength(pid, buffer, static_cast<uint16_t>(i - 4));
}
else
{
buffer[2] = (uint8_t)i;
}
sendFeatureReport(buffer, PACKET_SIZE);
}
}
void LenovoGen7And8USBController::setLedsDirectOn(uint8_t profile_id)
{
uint8_t buffer[PACKET_SIZE];
memset(buffer, 0x00, PACKET_SIZE);
size_t i = 0;
buffer[i++] = REPORT_ID;
buffer[i++] = SET_DIRECT_MODE;
buffer[i++] = 0xC0;
buffer[i++] = 0x03;
buffer[i++] = 0x01;
buffer[i++] = profile_id;
SetGen10PayloadLength(pid, buffer, 2);
sendFeatureReport(buffer, PACKET_SIZE);
}
void LenovoGen7And8USBController::setLedsDirectOff(uint8_t profile_id)
{
uint8_t buffer[PACKET_SIZE];
memset(buffer, 0x00, PACKET_SIZE);
size_t i = 0;
buffer[i++] = REPORT_ID;
buffer[i++] = SET_DIRECT_MODE;
buffer[i++] = 0xC0;
buffer[i++] = 0x03;
buffer[i++] = 0x02;
buffer[i++] = profile_id;
SetGen10PayloadLength(pid, buffer, 2);
sendFeatureReport(buffer, PACKET_SIZE);
}
void LenovoGen7And8USBController::setLedsDirect(std::vector<led> &leds, std::vector<RGBColor> &colors)
{
if(UsesGen10PacketFormat(pid))
{
/*---------------------------------------------------------*\
| Gen10 uses 0x07/A1 direct updates, with payload length |
| stored in bytes 2-3. |
\*---------------------------------------------------------*/
uint8_t buffer[PACKET_SIZE];
memset(buffer, 0x00, PACKET_SIZE);
size_t i = 0;
buffer[i++] = REPORT_ID;
buffer[i++] = DIRECT_MODE;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
size_t count = 0;
for(size_t index = 0; index < leds.size() && index < colors.size() && i + 5 <= PACKET_SIZE; index++)
{
buffer[i++] = leds[index].value & 0xFF;
buffer[i++] = leds[index].value >> 8 & 0xFF;
buffer[i++] = RGBGetRValue(colors[index]);
buffer[i++] = RGBGetGValue(colors[index]);
buffer[i++] = RGBGetBValue(colors[index]);
count++;
}
SetGen10PayloadLength(pid, buffer, static_cast<uint16_t>(count * 5));
sendFeatureReport(buffer, PACKET_SIZE);
return;
}
uint8_t buffer[PACKET_SIZE];
memset(buffer, 0x00, PACKET_SIZE);
size_t i = 0;
buffer[i++] = REPORT_ID;
buffer[i++] = DIRECT_MODE;
buffer[i++] = 0xC0;
buffer[i++] = 0x03;
for(size_t index = 0; index < leds.size() && i < PACKET_SIZE; index++)
{
buffer[i++] = leds[index].value & 0xFF;
buffer[i++] = leds[index].value >> 8 & 0xFF;
buffer[i++] = RGBGetRValue(colors[index]);
buffer[i++] = RGBGetGValue(colors[index]);
buffer[i++] = RGBGetBValue(colors[index]);
}
sendFeatureReport(buffer, PACKET_SIZE);
}
void LenovoGen7And8USBController::setLedsAllOff(uint8_t profile_id)
{
uint8_t buffer[PACKET_SIZE] = {REPORT_ID, SAVE_PROFILE, 0xC0, 0x03, profile_id, 0x01, 0x01};
SetGen10PayloadLength(pid, buffer, 3);
sendFeatureReport(buffer, PACKET_SIZE);
}
uint8_t LenovoGen7And8USBController::getCurrentProfileId()
{
uint8_t buffer[PACKET_SIZE] = {REPORT_ID, GET_ACTIVE_PROFILE, 0xC0, 0x03};
SetGen10PayloadLength(pid, buffer, 1);
vector<uint8_t> response = getFeatureReport(buffer, PACKET_SIZE);
return response.size()>4?response[4]:0x01;
}
uint8_t LenovoGen7And8USBController::getCurrentBrightness()
{
uint8_t buffer[PACKET_SIZE] = {REPORT_ID, GET_BRIGHTNESS, 0xC0, 0x03};
SetGen10PayloadLength(pid, buffer, 1);
vector<uint8_t> response = getFeatureReport(buffer, PACKET_SIZE);
return response.size()>4?response[4]:0x00;
}
void LenovoGen7And8USBController::setBrightness(uint8_t brightness)
{
uint8_t buffer[PACKET_SIZE] = {REPORT_ID, SET_BRIGHTNESS, 0xC0, 0x03, brightness};
SetGen10PayloadLength(pid, buffer, 1);
sendFeatureReport(buffer, PACKET_SIZE);
}
void LenovoGen7And8USBController::switchProfileTo(uint8_t profile_id)
{
uint8_t buffer[PACKET_SIZE] = {REPORT_ID, SWITCH_PROFILE, 0xC0, 0x03, profile_id};
SetGen10PayloadLength(pid, buffer, 1);
sendFeatureReport(buffer, PACKET_SIZE);
}
std::vector<led_group> LenovoGen7And8USBController::getProfileSettings(uint8_t profile_id)
{
uint8_t buffer[PACKET_SIZE] = {REPORT_ID, GET_PROFILE, 0xC0, 0x03, profile_id};
SetGen10PayloadLength(pid, buffer, PACKET_SIZE - 4);
vector<uint8_t> response = getFeatureReport(buffer, PACKET_SIZE);
vector<led_group> groups;
size_t i = 7;
while(i < response.size() && response[i] != 0x00){
i++;
led_group group;
/*-----------------*\
|read group settings|
\*-----------------*/
size_t cnt = response[i++];
for(size_t j = 0; j < cnt && i < response.size(); j++, i+=2)
{
switch(response[i])
{
case 0x01:
group.mode = response[i+1];
break;
case 0x02:
group.speed = response[i+1];
break;
case 0x03:
group.spin = response[i+1];
break;
case 0x04:
group.direction = response[i+1];
break;
case 0x05:
group.color_mode = response[i+1];
break;
case 0x06:
//group.mode = response[i+1];
break;
}
}
/*-----------------*\
|read group colors |
\*-----------------*/
cnt = response[i++];
for(size_t j = 0; j < cnt && i < response.size(); j++, i+=3)
{
group.colors.push_back(ToRGBColor(response[i],response[i+1],response[i+2]));
}
/*-----------------*\
|read group LEDs |
\*-----------------*/
cnt = response[i++];
for(size_t j = 0; j < cnt && i < response.size(); j++, i+=2)
{
group.leds.push_back(response[i] | response[i+1] << 8);
}
groups.push_back(group);
}
return groups;
}
void LenovoGen7And8USBController::sendFeatureReport(uint8_t packet[], size_t packet_size)
{
hid_send_feature_report(dev, packet, packet_size);
LOG_TRACE("[Lenovo Gen 7 Controller] Buffer: %s", ConvertBytesToHex(packet, packet_size).c_str());
}
std::vector<uint8_t> LenovoGen7And8USBController::getFeatureReport(uint8_t packet[], size_t packet_size)
{
sendFeatureReport(packet, packet_size);
uint8_t read_buffer[PACKET_SIZE] = {REPORT_ID};
int num_bytes = 0;
num_bytes = hid_get_feature_report(dev, read_buffer, sizeof(read_buffer));
vector<uint8_t> response = {};
if(num_bytes > 0)
{
response.insert(response.begin(), read_buffer, read_buffer + num_bytes);
}
LOG_TRACE("[Lenovo Gen 7 Controller] Read Buffer: %s", ConvertBytesToHex(response).c_str());
return response;
}
std::string LenovoGen7And8USBController::ConvertBytesToHex(uint8_t packet[], size_t packet_size)
{
return ConvertBytesToHex(std::vector<uint8_t>(packet, packet + packet_size));
}
std::string LenovoGen7And8USBController::ConvertBytesToHex(const std::vector<uint8_t> &input)
{
std::ostringstream temp_stream;
for(const uint8_t &oneInputByte : input)
{
temp_stream << (temp_stream.tellp()==0 ? "" : " ") << std::setw(2) << std::setfill('0') << std::hex << (int)oneInputByte;
}
return temp_stream.str();
}
@@ -0,0 +1,92 @@
/*---------------------------------------------------------*\
| LenovoUSBController_Gen7_8.h |
| |
| Driver for Lenovo Gen7 and Gen8 devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <array>
#include <string>
#include <utility>
#include <vector>
#include <hidapi.h>
#include "RGBController.h"
#include "LogManager.h"
#ifndef HID_MAX_STR
#define HID_MAX_STR 255
#endif
#define PACKET_SIZE 960
#define REPORT_ID 0x07
#define DIRECT_MODE 0xA1
#define SWITCH_PROFILE 0xC8
#define GET_ACTIVE_PROFILE 0xCA
#define SAVE_PROFILE 0xCB
#define GET_PROFILE 0xCC
#define GET_BRIGHTNESS 0xCD
#define SET_BRIGHTNESS 0xCE
#define SET_DIRECT_MODE 0xD0
#define GET_DIRECT_MODE_PROFILE 0xD1
struct led_group
{
uint8_t mode;
uint8_t speed;
uint8_t spin;
uint8_t direction;
uint8_t color_mode;
std::vector<RGBColor> colors;
std::vector<uint16_t> leds;
};
class LenovoGen7And8USBController
{
public:
/*--------------*\
|ctor(s) and dtor|
\*--------------*/
LenovoGen7And8USBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name);
~LenovoGen7And8USBController();
/*--------------*\
|device functions|
\*--------------*/
void setLedsByGroup(uint8_t profile_id, std::vector<led_group> led_groups);
void setLedsDirect(std::vector<led> &leds, std::vector<RGBColor> &colors);
void setLedsAllOff(uint8_t profile_id);
void setLedsDirectOn(uint8_t profile_id);
void setLedsDirectOff(uint8_t profile_id);
uint16_t getPid();
std::string getName();
std::string getLocation();
uint8_t getCurrentProfileId();
uint8_t getCurrentBrightness();
void setBrightness(uint8_t brightness);
uint8_t getKeyboardId();
void switchProfileTo(uint8_t profile_id);
std::vector<led_group> getProfileSettings(uint8_t profile_id);
private:
/*--------------*\
|data members |
\*--------------*/
std::string name;
hid_device *dev;
std::string location;
uint16_t pid;
/*--------------*\
|device functions|
\*--------------*/
void sendFeatureReport(uint8_t packet[], size_t packet_size);
std::vector<uint8_t> getFeatureReport(uint8_t packet[], size_t packet_size);
std::string ConvertBytesToHex(uint8_t packet[], size_t packet_size);
std::string ConvertBytesToHex(const std::vector<uint8_t> &input);
};
@@ -0,0 +1,820 @@
/*---------------------------------------------------------*\
| RGBController_Lenovo_Gen7_8.cpp |
| |
| RGBController for Lenovo Gen7 and Gen8 devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <vector>
#include "RGBController_Lenovo_Gen7_8.h"
#include "LenovoDevices.h"
using namespace std;
static bool UsesGen10PacketFormat(uint16_t pid)
{
return pid == LEGION_7GEN10 || pid == LEGION_5GEN10;
}
static bool Is24ZoneDevice(uint16_t pid)
{
return pid == LEGION_5GEN10;
}
static bool IsKeyboardOnlyDevice(uint16_t pid)
{
return pid == LEGION_5GEN10;
}
static const lenovo_led legion_5gen10_24zone_leds[] =
{
{0x01, "Zone R1C1"},
{0x02, "Zone R1C2"},
{0x03, "Zone R1C3"},
{0x04, "Zone R1C4"},
{0x05, "Zone R1C5"},
{0x06, "Zone R1C6"},
{0x07, "Zone R2C1"},
{0x08, "Zone R2C2"},
{0x09, "Zone R2C3"},
{0x0A, "Zone R2C4"},
{0x0B, "Zone R2C5"},
{0x0C, "Zone R2C6"},
{0x0D, "Zone R3C1"},
{0x0E, "Zone R3C2"},
{0x0F, "Zone R3C3"},
{0x10, "Zone R3C4"},
{0x11, "Zone R3C5"},
{0x12, "Zone R3C6"},
{0x13, "Zone R4C1"},
{0x14, "Zone R4C2"},
{0x15, "Zone R4C3"},
{0x16, "Zone R4C4"},
{0x17, "Zone R4C5"},
{0x18, "Zone R4C6"},
};
static const unsigned int legion_5gen10_24zone_matrix_map[] =
{
0, 1, 2, 3, 4, 5,
6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23,
};
static const lenovo_zone legion_5gen10_kbd_24zone =
{
"Keyboard (24-zone)",
ZONE_TYPE_MATRIX,
0,
4,
6,
legion_5gen10_24zone_matrix_map,
legion_5gen10_24zone_leds,
0,
23,
};
static const RGBColor legion_5gen10_zone_visualization_colors[24] =
{
0xFF0000, 0x00FFFF, 0xFFFF00, 0x0000FF, 0x00FF00, 0xFF00FF,
0xFF7F00, 0x007FFF, 0x7FFF00, 0x7F00FF, 0xFF007F, 0x00FF7F,
0xFFD700, 0x0055FF, 0x55FF00, 0xFF0055, 0x00BFFF, 0xBF00FF,
0xFF3B00, 0x00FF3B, 0x3B00FF, 0xFF1493, 0x14FF93, 0x9314FF,
};
LenovoRGBController_Gen7_8::LenovoRGBController_Gen7_8(LenovoGen7And8USBController* controller_ptr)
{
controller = controller_ptr;
mode Screw;
Screw.name = "Screw Rainbow";
Screw.value = LENOVO_LEGION_GEN7_8_MODE_SCREW_RAINBOW;
Screw.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_DIRECTION_LR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Screw.speed_min = 0x01;
Screw.speed_max = 0x03;
Screw.speed = 2;
Screw.color_mode = MODE_COLORS_NONE;
Screw.brightness_min = 0;
Screw.brightness_max = 9;
Screw.brightness = brightness;
Screw.direction = MODE_DIRECTION_RIGHT;
modes.push_back(Screw);
mode Rainbow;
Rainbow.name = "Rainbow Wave";
Rainbow.value = LENOVO_LEGION_GEN7_8_MODE_RAINBOW_WAVE;
Rainbow.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_DIRECTION_LR |
MODE_FLAG_HAS_DIRECTION_UD |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Rainbow.speed_min = 0x01;
Rainbow.speed_max = 0x03;
Rainbow.speed = 2;
Rainbow.color_mode = MODE_COLORS_NONE;
Rainbow.brightness_min = 0;
Rainbow.brightness_max = 9;
Rainbow.brightness = brightness;
Rainbow.direction = MODE_DIRECTION_RIGHT;
modes.push_back(Rainbow);
mode ColorChange;
ColorChange.name = "Color Change";
ColorChange.value = LENOVO_LEGION_GEN7_8_MODE_COLOR_CHANGE;
ColorChange.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
ColorChange.speed_min = 0x01;
ColorChange.speed_max = 0x03;
ColorChange.speed = 2;
ColorChange.colors_min = 1;
ColorChange.colors_max = 4;
ColorChange.colors.resize(4);
ColorChange.colors[0] = 0xFFF500;
ColorChange.color_mode = MODE_COLORS_RANDOM;
ColorChange.brightness_min = 0;
ColorChange.brightness_max = 9;
ColorChange.brightness = brightness;
modes.push_back(ColorChange);
mode ColorPulse;
ColorPulse.name = "Color Pulse";
ColorPulse.value = LENOVO_LEGION_GEN7_8_MODE_COLOR_PULSE;
ColorPulse.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
ColorPulse.speed_min = 0x01;
ColorPulse.speed_max = 0x03;
ColorPulse.speed = 2;
ColorPulse.colors_min = 1;
ColorPulse.colors_max = 4;
ColorPulse.colors.resize(4);
ColorPulse.colors[0] = 0xFFF500;
ColorPulse.color_mode = MODE_COLORS_RANDOM;
ColorPulse.brightness_min = 0;
ColorPulse.brightness_max = 9;
ColorPulse.brightness = brightness;
modes.push_back(ColorPulse);
mode ColorWave;
ColorWave.name = "Color Wave";
ColorWave.value = LENOVO_LEGION_GEN7_8_MODE_COLOR_WAVE;
ColorWave.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_DIRECTION_LR |
MODE_FLAG_HAS_DIRECTION_UD |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
ColorWave.speed_min = 0x01;
ColorWave.speed_max = 0x03;
ColorWave.speed = 2;
ColorWave.colors_min = 1;
ColorWave.colors_max = 4;
ColorWave.colors.resize(4);
ColorWave.colors[0] = 0xFFF500;
ColorWave.color_mode = MODE_COLORS_RANDOM;
ColorWave.brightness_min = 0;
ColorWave.brightness_max = 9;
ColorWave.brightness = brightness;
ColorWave.direction = MODE_DIRECTION_RIGHT;
modes.push_back(ColorWave);
mode Smooth;
Smooth.name = "Smooth";
Smooth.value = LENOVO_LEGION_GEN7_8_MODE_SMOOTH;
Smooth.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Smooth.speed_min = 0x01;
Smooth.speed_max = 0x03;
Smooth.speed = 2;
Smooth.colors_min = 1;
Smooth.colors_max = 4;
Smooth.colors.resize(4);
Smooth.colors[0] = 0xFFF500;
Smooth.color_mode = MODE_COLORS_RANDOM;
Smooth.brightness_min = 0;
Smooth.brightness_max = 9;
Smooth.brightness = brightness;
modes.push_back(Smooth);
if(!Is24ZoneDevice(controller->getPid()))
{
mode Rain;
Rain.name = "Rain";
Rain.value = LENOVO_LEGION_GEN7_8_MODE_RAIN;
Rain.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Rain.speed_min = 0x01;
Rain.speed_max = 0x03;
Rain.speed = 2;
Rain.colors_min = 1;
Rain.colors_max = 4;
Rain.colors.resize(4);
Rain.colors[0] = 0xFFF500;
Rain.color_mode = MODE_COLORS_RANDOM;
Rain.brightness_min = 0;
Rain.brightness_max = 9;
Rain.brightness = brightness;
modes.push_back(Rain);
}
if(!IsKeyboardOnlyDevice(controller->getPid()))
{
mode Ripple;
Ripple.name = "Ripple";
Ripple.value = LENOVO_LEGION_GEN7_8_MODE_RIPPLE;
Ripple.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Ripple.speed_min = 0x01;
Ripple.speed_max = 0x03;
Ripple.speed = 2;
Ripple.colors_min = 1;
Ripple.colors_max = 4;
Ripple.colors.resize(4);
Ripple.colors[0] = 0xFFF500;
Ripple.color_mode = MODE_COLORS_RANDOM;
Ripple.brightness_min = 0;
Ripple.brightness_max = 9;
Ripple.brightness = brightness;
modes.push_back(Ripple);
mode AudioBounce;
AudioBounce.name = "Audio Bounce Lighting";
AudioBounce.value = LENOVO_LEGION_GEN7_8_MODE_AUDIO_BOUNCE;
AudioBounce.flags = MODE_FLAG_HAS_BRIGHTNESS;
AudioBounce.color_mode = MODE_COLORS_NONE;
AudioBounce.brightness_min = 0;
AudioBounce.brightness_max = 9;
AudioBounce.brightness = brightness;
modes.push_back(AudioBounce);
mode AudioRipple;
AudioRipple.name = "Audio Ripple Lighting";
AudioRipple.value = LENOVO_LEGION_GEN7_8_MODE_AUDIO_RIPPLE;
AudioRipple.flags = MODE_FLAG_HAS_BRIGHTNESS;
AudioRipple.color_mode = MODE_COLORS_NONE;
AudioRipple.brightness_min = 0;
AudioRipple.brightness_max = 9;
AudioRipple.brightness = brightness;
modes.push_back(AudioRipple);
}
mode Static;
Static.name = "Static";
Static.value = LENOVO_LEGION_GEN7_8_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_PER_LED_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Static.color_mode = MODE_COLORS_PER_LED;
Static.brightness_min = 0;
Static.brightness_max = 9;
Static.brightness = brightness;
modes.push_back(Static);
if(!Is24ZoneDevice(controller->getPid()))
{
mode Type;
Type.name = "Type Lighting";
Type.value = LENOVO_LEGION_GEN7_8_MODE_TYPE;
Type.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Type.speed_min = 0x01;
Type.speed_max = 0x03;
Type.speed = 2;
Type.colors_min = 1;
Type.colors_max = 4;
Type.colors.resize(4);
Type.colors[0] = 0xFFF500;
Type.color_mode = MODE_COLORS_RANDOM;
Type.brightness_min = 0;
Type.brightness_max = 9;
Type.brightness = brightness;
modes.push_back(Type);
}
if(!IsKeyboardOnlyDevice(controller->getPid()))
{
mode Direct;
Direct.name = "Direct";
Direct.value = LENOVO_LEGION_GEN7_8_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR |
MODE_FLAG_HAS_BRIGHTNESS;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.brightness_min = 0;
Direct.brightness_max = 9;
Direct.brightness = brightness;
modes.push_back(Direct);
}
name = controller->getName();
type = DEVICE_TYPE_KEYBOARD;
vendor = "Lenovo";
switch (controller->getPid())
{
case LEGION_S7GEN7:
description = "Lenovo Legion 7 Slim Generation 7";
break;
case LEGION_7GEN7:
description = "Lenovo Legion 7 Generation 7";
break;
case LEGION_S7GEN8:
description = "Lenovo Legion 7 Slim Generation 8";
break;
case LEGION_7GEN8:
description = "Lenovo Legion 7 Generation 8";
break;
case LEGION_7GEN9:
case LEGION_7GEN9_H:
description = "Lenovo Legion 7 Generation 9";
break;
case LEGION_7GEN10:
description = "Lenovo Legion 7 Generation 10";
break;
case LEGION_5GEN10:
description = "Lenovo Legion 5 Gen 10";
break;
}
brightness = controller->getCurrentBrightness();
profile_id = controller->getCurrentProfileId();
for(mode &m : modes)
{
m.brightness = brightness;
}
SetupZones();
/*-----------------------------------------------------*\
| Initiliaze Static |
\*-----------------------------------------------------*/
active_mode = 0;
for(size_t i = 0; i < modes.size(); i++)
{
if(modes[i].value == LENOVO_LEGION_GEN7_8_MODE_STATIC)
{
active_mode = (int)i;
break;
}
}
ReadDeviceSettings();
last_mode = active_mode;
}
LenovoRGBController_Gen7_8::~LenovoRGBController_Gen7_8()
{
delete controller;
}
void LenovoRGBController_Gen7_8::SetupZones()
{
vector<lenovo_zone> lenovo_zones;
if(Is24ZoneDevice(controller->getPid()))
{
lenovo_zones.push_back(legion_5gen10_kbd_24zone);
}
else
{
lenovo_zones.push_back(legion7_gen7and8_kbd_ansi);
}
if(!IsKeyboardOnlyDevice(controller->getPid()))
{
lenovo_zones.push_back(legion7_gen7and8_neon);
}
if (controller->getPid() == LEGION_7GEN7)
{
lenovo_zones.push_back(lenovo_legion_7gen7_logo);
lenovo_zones.push_back(lenovo_legion_7gen7_vents);
}
if (controller->getPid() == LEGION_7GEN10)
{
lenovo_zones.push_back(lenovo_legion_7gen7_logo);
lenovo_zones.push_back(lenovo_legion_7gen10_vents);
}
for(unsigned int i = 0; i < lenovo_zones.size(); i++)
{
zone new_zone;
new_zone.name = lenovo_zones[i].name;
new_zone.type = lenovo_zones[i].type;
new_zone.leds_count = lenovo_zones[i].end - lenovo_zones[i].start + 1;
new_zone.leds_max = new_zone.leds_count;
new_zone.leds_min = new_zone.leds_count;
LOG_DEBUG("[Lenovo Gen7/8 Controller] adding zone: %s with %u LEDs", new_zone.name.c_str(), new_zone.leds_count);
if(lenovo_zones[i].type == ZONE_TYPE_MATRIX)
{
new_zone.matrix_map = new matrix_map_type;
new_zone.matrix_map->height = lenovo_zones[i].height;
new_zone.matrix_map->width = lenovo_zones[i].width;
new_zone.matrix_map->map = new unsigned int[new_zone.matrix_map->height * new_zone.matrix_map->width];
if(lenovo_zones[i].matrix_map != NULL)
{
new_zone.matrix_map->map = (unsigned int *) lenovo_zones[i].matrix_map;
}
}
else
{
new_zone.matrix_map = NULL;
}
zones.push_back(new_zone);
for(unsigned int led_idx = lenovo_zones[i].start; led_idx <= lenovo_zones[i].end; led_idx++ )
{
led new_led;
new_led.name = lenovo_zones[i].leds[led_idx].name;
new_led.value = lenovo_zones[i].id << 8 | lenovo_zones[i].leds[led_idx].led_num ;
leds.push_back(new_led);
/*---------------------------------------------*\
| create led id to index map for fast look up |
\*---------------------------------------------*/
led_id_to_index[new_led.value]=leds.size() - 1;
}
}
SetupColors();
}
void LenovoRGBController_Gen7_8::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void LenovoRGBController_Gen7_8::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void LenovoRGBController_Gen7_8::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void LenovoRGBController_Gen7_8::DeviceUpdateMode()
{
uint8_t hw_profile_id = controller->getCurrentProfileId();
if(hw_profile_id != profile_id)
{
profile_id = hw_profile_id;
ReadDeviceSettings();
last_mode = active_mode;
direct_enabled = false;
}
if(brightness != modes[active_mode].brightness)
{
brightness = modes[active_mode].brightness;
controller->setBrightness(brightness);
for(mode &m : modes)
{
m.brightness = brightness;
}
}
if(last_mode != active_mode)
{
if(modes[last_mode].value == LENOVO_LEGION_GEN7_8_MODE_DIRECT)
{
controller->setLedsDirectOff(profile_id);
direct_enabled = false;
}
if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_DIRECT)
{
controller->setLedsDirectOn(profile_id);
direct_enabled = true;
if(!UsesGen10PacketFormat(controller->getPid()))
{
controller->setLedsByGroup(profile_id, GetLedGroups());
}
}
last_mode = active_mode;
}
else if((modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_DIRECT) && !direct_enabled)
{
controller->setLedsDirectOn(profile_id);
direct_enabled = true;
}
if(modes[active_mode].value != LENOVO_LEGION_GEN7_8_MODE_DIRECT)
{
DeviceUpdateLEDs();
}
}
void LenovoRGBController_Gen7_8::DeviceUpdateLEDs()
{
if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_DIRECT)
{
if(UsesGen10PacketFormat(controller->getPid()))
{
/*---------------------------------------------*\
| Gen10 may ignore A1 updates unless D0 is |
| reasserted. |
\*---------------------------------------------*/
controller->setLedsDirectOn(profile_id);
direct_enabled = true;
}
else if(!direct_enabled)
{
controller->setLedsDirectOn(profile_id);
direct_enabled = true;
}
controller->setLedsDirect(leds, colors);
}
else
{
controller->setLedsByGroup(profile_id, GetLedGroups());
}
}
void LenovoRGBController_Gen7_8::ReadDeviceSettings()
{
vector<led_group> current_settings = controller->getProfileSettings(profile_id);
if(current_settings.size() > 0)
{
for(unsigned int i = 0; i < modes.size(); i++)
{
if(modes[i].value == current_settings[0].mode)
{
switch(current_settings[0].color_mode)
{
case 0x02:
if(modes[i].flags & MODE_FLAG_HAS_PER_LED_COLOR)
{
modes[i].color_mode = MODE_COLORS_PER_LED;
}
else
{
modes[i].color_mode = MODE_COLORS_MODE_SPECIFIC;
}
break;
case 0x01:
modes[i].color_mode = MODE_COLORS_RANDOM;
break;
default:
modes[i].color_mode = MODE_COLORS_NONE;
}
switch(modes[i].color_mode)
{
case MODE_COLORS_PER_LED:
for(size_t j=0; j < colors.size(); j++)
{
colors[j]=0x00;
}
for(const led_group &lg : current_settings)
{
if(lg.colors.size() > 0)
{
for(uint16_t led_id : lg.leds)
{
if(auto search = led_id_to_index.find(led_id); search != led_id_to_index.end())
{
colors[search->second] = lg.colors[0];
}
}
}
}
break;
case MODE_COLORS_MODE_SPECIFIC:
for(size_t j=0; j < modes[i].colors.size(); j++)
{
if(j < current_settings[0].colors.size())
{
modes[i].colors[j] = current_settings[0].colors[j];
}
else
{
modes[i].colors[j] = 0x00;
}
}
}
switch(current_settings[0].direction)
{
case 0x01:
modes[i].direction = MODE_DIRECTION_UP;
break;
case 0x02:
modes[i].direction = MODE_DIRECTION_DOWN;
break;
case 0x03:
modes[i].direction = MODE_DIRECTION_LEFT;
break;
case 0x04:
modes[i].direction = MODE_DIRECTION_RIGHT;
break;
}
switch(current_settings[0].spin)
{
case 0x01:
modes[i].direction = MODE_DIRECTION_RIGHT;
break;
case 0x02:
modes[i].direction = MODE_DIRECTION_LEFT;
break;
}
active_mode = i;
break; //stop for loop
}
}
}
}
std::vector<led_group> LenovoRGBController_Gen7_8::GetLedGroups()
{
if(Is24ZoneDevice(controller->getPid()) &&
modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_STATIC &&
modes[active_mode].color_mode == MODE_COLORS_RANDOM)
{
vector<led_group> led_groups;
led_groups.reserve(leds.size());
for(size_t i = 0; i < leds.size(); i++)
{
led_group group;
group.mode = modes[active_mode].value;
group.speed = modes[active_mode].speed;
group.spin = 0x00;
group.direction = 0x00;
group.color_mode = 0x02;
group.colors.push_back(legion_5gen10_zone_visualization_colors[i % 24]);
group.leds.push_back(leds[i].value);
led_groups.push_back(group);
}
return led_groups;
}
std::unordered_map<RGBColor, vector<uint16_t>> led_map;
if(modes[active_mode].color_mode == MODE_COLORS_PER_LED &&
modes[active_mode].value != LENOVO_LEGION_GEN7_8_MODE_DIRECT)
{
for(size_t i = 0; i < leds.size(); i++)
{
led_map[colors[i]].push_back(leds[i].value);
}
}
else
{
size_t start = 0;
size_t end = leds.size();
/*-------------------------------------------------*\
| Riplle and Type only apply to keyboard |
\*-------------------------------------------------*/
if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_RIPPLE ||
modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_TYPE)
{
for(const zone &z : zones)
{
if(z.name == "Keyboard")
{
start = z.start_idx;
end = start + z.leds_count;
}
}
}
for(size_t i = start; i < end; i++)
{
led_map[0x00].push_back(leds[i].value);
}
}
vector<led_group> led_groups;
for(const auto &pair : led_map)
{
if(pair.first == 0x00 && led_map.size() != 1)
{
continue;
}
led_group group;
group.mode = modes[active_mode].value;
group.speed = modes[active_mode].speed;
group.spin = 0x00;
group.direction = 0x00;
switch(modes[active_mode].direction)
{
case MODE_DIRECTION_UP:
group.direction = 0x01;
break;
case MODE_DIRECTION_DOWN:
group.direction = 0x02;
break;
case MODE_DIRECTION_LEFT:
if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_SCREW_RAINBOW)
{
group.spin = 0x02;
}
else
{
group.direction = 0x03;
}
break;
case MODE_DIRECTION_RIGHT:
if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_SCREW_RAINBOW)
{
group.spin = 0x01;
}
else
{
group.direction = 0x04;
}
break;
}
switch(modes[active_mode].color_mode)
{
default:
case MODE_COLORS_NONE:
group.color_mode = 0x00;
break;
case MODE_COLORS_RANDOM:
group.color_mode = 0x01;
break;
case MODE_COLORS_MODE_SPECIFIC:
group.color_mode = 0x02;
for(RGBColor c : modes[active_mode].colors)
{
if(c)
{
group.colors.push_back(c);
}
}
if(group.colors.size() == 0)
{
group.colors.push_back(0xFFF500);
}
break;
case MODE_COLORS_PER_LED:
group.color_mode = 0x02;
group.colors.push_back(pair.first);
break;
}
group.leds = pair.second;
led_groups.push_back(group);
}
return led_groups;
}
@@ -0,0 +1,57 @@
/*---------------------------------------------------------*\
| RGBController_Lenovo_Gen7_8.h |
| |
| RGBController for Lenovo Gen7 and Gen8 devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "LenovoUSBController_Gen7_8.h"
enum
{
LENOVO_LEGION_GEN7_8_MODE_SCREW_RAINBOW = 0x01,
LENOVO_LEGION_GEN7_8_MODE_RAINBOW_WAVE = 0x02,
LENOVO_LEGION_GEN7_8_MODE_COLOR_CHANGE = 0x03,
LENOVO_LEGION_GEN7_8_MODE_COLOR_PULSE = 0x04,
LENOVO_LEGION_GEN7_8_MODE_COLOR_WAVE = 0x05,
LENOVO_LEGION_GEN7_8_MODE_SMOOTH = 0x06,
LENOVO_LEGION_GEN7_8_MODE_RAIN = 0x07,
LENOVO_LEGION_GEN7_8_MODE_RIPPLE = 0x08,
LENOVO_LEGION_GEN7_8_MODE_AUDIO_BOUNCE = 0x09,
LENOVO_LEGION_GEN7_8_MODE_AUDIO_RIPPLE = 0x0A,
LENOVO_LEGION_GEN7_8_MODE_STATIC = 0x0B,
LENOVO_LEGION_GEN7_8_MODE_TYPE = 0x0C,
LENOVO_LEGION_GEN7_8_MODE_DIRECT = 0x0D,
};
class LenovoRGBController_Gen7_8 : public RGBController
{
public:
LenovoRGBController_Gen7_8(LenovoGen7And8USBController* controller_ptr);
~LenovoRGBController_Gen7_8();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
LenovoGen7And8USBController* controller;
std::vector<led_group> GetLedGroups();
void ReadDeviceSettings();
std::unordered_map<unsigned int, size_t> led_id_to_index;
int last_mode = 0;
bool direct_enabled = false;
uint8_t brightness = 0x00;
uint8_t profile_id = 0x01;
};