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,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;
};