New device: Dell G5 SE Alienware LED Keyboard
- 4 region keyboard LED controller (tried to keep controller generic for other setups as well) - Support for Static, Flashing, Morph, Spectrum Cycle, Rainbow Wave, and Breathing modes - Implemented rate limiting for sending reports since the controller crashes if too many reports are sent too quickly. Also implemented functionality to prevent sending frivolous update requests - Implemented support for setting mode, color, tempo, and period independently for each zone (not fully exposed in UI). Morph mode exposes 2 colors per zone. Commit amended for code style by Adam Honse <calcprogrammer1@gmail.com>
This commit is contained in:
parent
4c41a8834d
commit
9062be4462
6 changed files with 1323 additions and 0 deletions
838
Controllers/AlienwareController/AlienwareController.cpp
Normal file
838
Controllers/AlienwareController/AlienwareController.cpp
Normal file
|
|
@ -0,0 +1,838 @@
|
|||
/*-----------------------------------------*\
|
||||
| AlienwareController.cpp |
|
||||
| |
|
||||
| Driver for Alienware lighting controller |
|
||||
| |
|
||||
| Gabriel Marcano (gemarcano) 4/21/2021 |
|
||||
\*-----------------------------------------*/
|
||||
|
||||
#include "RGBController.h"
|
||||
#include "AlienwareController.h"
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
typedef uint32_t alienware_platform_id;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Some devices appear to report the wrong number of zones. |
|
||||
| Record that here. |
|
||||
\*---------------------------------------------------------*/
|
||||
static const std::map<alienware_platform_id, uint8_t> zone_quirks_table =
|
||||
{
|
||||
{ 0x0C01, 4 } // Dell G5 SE 5505
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Add zones for devices here, mapping the platform ID to |
|
||||
| the zone names |
|
||||
\*---------------------------------------------------------*/
|
||||
static const std::map<alienware_platform_id, std::vector<const char*>> zone_names_table =
|
||||
{
|
||||
{ 0x0C01, { "Left", "Middle", "Right", "Numpad" } }
|
||||
};
|
||||
|
||||
static void SendHIDReport(hid_device *dev, const unsigned char* usb_buf, size_t usb_buf_size)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
hid_send_feature_report(dev, usb_buf, usb_buf_size);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| The controller really doesn't like really spammed by |
|
||||
| too many commands at once... the delay may be command |
|
||||
| dependent also. Delay for longer if the command is |
|
||||
| changing animation state |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned char command = usb_buf[2];
|
||||
unsigned char subcommand = usb_buf[3];
|
||||
|
||||
if( ( command == ALIENWARE_COMMAND_USER_ANIM )
|
||||
&& ( ( subcommand == ALIENWARE_COMMAND_USER_ANIM_FINISH_PLAY )
|
||||
|| ( subcommand == ALIENWARE_COMMAND_USER_ANIM_FINISH_SAVE ) ) )
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::this_thread::sleep_for(60ms);
|
||||
}
|
||||
}
|
||||
|
||||
AlienwareController::AlienwareController(hid_device* dev_handle, const hid_device_info& info, std::string name)
|
||||
{
|
||||
HidapiAlienwareReport report;
|
||||
|
||||
dev = dev_handle;
|
||||
device_name = name;
|
||||
location = info.path;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get serial number |
|
||||
\*-----------------------------------------------------*/
|
||||
std::wstring tmp_serial_number;
|
||||
tmp_serial_number = info.serial_number;
|
||||
serial_number = std::string(tmp_serial_number.begin(), tmp_serial_number.end());
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get zone information by checking firmware |
|
||||
| configuration |
|
||||
\*-----------------------------------------------------*/
|
||||
report = Report(ALIENWARE_COMMAND_REPORT_CONFIG);
|
||||
alienware_platform_id platform_id = report.data[4] << 8 | report.data[5];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get firmware version |
|
||||
\*-----------------------------------------------------*/
|
||||
report = Report(ALIENWARE_COMMAND_REPORT_FIRMWARE);
|
||||
|
||||
std::stringstream fw_string;
|
||||
|
||||
fw_string << static_cast<unsigned>(report.data[4]) << '.' << static_cast<unsigned>(report.data[5]) << '.' << static_cast<unsigned>(report.data[6]);
|
||||
version = fw_string.str();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Check if the device reports the wrong number of zones |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned number_of_zones = zone_quirks_table.count(platform_id) ? zone_quirks_table.at(platform_id) : report.data[6];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Initialize Alienware zones |
|
||||
\*-----------------------------------------------------*/
|
||||
zones.resize(number_of_zones);
|
||||
|
||||
if(zone_names_table.count(platform_id))
|
||||
{
|
||||
zone_names = zone_names_table.at(platform_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| If this is an unknown controller, set the name of |
|
||||
| all regions to "Unknown" |
|
||||
\*-------------------------------------------------*/
|
||||
for(size_t i = 0; i < number_of_zones; i++)
|
||||
{
|
||||
zone_names.emplace_back("Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set defaults for all zones |
|
||||
| It doesn't seem possible to read the controller's |
|
||||
| current state, hence the default value being set here.|
|
||||
\*-----------------------------------------------------*/
|
||||
for(unsigned int zone_idx; zone_idx < zones.size(); zone_idx++)
|
||||
{
|
||||
zones[zone_idx].color[0] = 0x000000;
|
||||
zones[zone_idx].color[1] = 0x000000;
|
||||
zones[zone_idx].mode = ALIENWARE_MODE_COLOR;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Default period value from ACC |
|
||||
\*-------------------------------------------------*/
|
||||
zones[zone_idx].period = 2000;
|
||||
zones[zone_idx].tempo = ALIENWARE_TEMPO_MAX;
|
||||
zones[zone_idx].dim = 0;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Initialize dirty flags |
|
||||
\*-----------------------------------------------------*/
|
||||
dirty = true;
|
||||
dirty_dim = true;
|
||||
}
|
||||
|
||||
AlienwareController::~AlienwareController()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
unsigned int AlienwareController::GetZoneCount()
|
||||
{
|
||||
return(zones.size());
|
||||
}
|
||||
|
||||
std::vector<const char*> AlienwareController::GetZoneNames()
|
||||
{
|
||||
return(zone_names);
|
||||
}
|
||||
|
||||
std::string AlienwareController::GetDeviceLocation()
|
||||
{
|
||||
return("HID: " + location);
|
||||
}
|
||||
|
||||
std::string AlienwareController::GetDeviceName()
|
||||
{
|
||||
return(device_name);
|
||||
}
|
||||
|
||||
std::string AlienwareController::GetSerialString()
|
||||
{
|
||||
return(serial_number);
|
||||
}
|
||||
|
||||
std::string AlienwareController::GetFirmwareVersion()
|
||||
{
|
||||
return(version);
|
||||
}
|
||||
|
||||
AlienwareController::HidapiAlienwareReport AlienwareController::GetResponse()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero init. This is not updated if there's a problem. |
|
||||
\*-----------------------------------------------------*/
|
||||
HidapiAlienwareReport result;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(result.data, 0x00, sizeof(result.data));
|
||||
|
||||
|
||||
hid_get_feature_report(dev, result.data, HIDAPI_ALIENWARE_REPORT_SIZE);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
AlienwareController::HidapiAlienwareReport AlienwareController::Report(uint8_t subcommand)
|
||||
{
|
||||
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up message packet with leading 00, per hidapi |
|
||||
\*-----------------------------------------------------*/
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x03;
|
||||
usb_buf[0x02] = ALIENWARE_COMMAND_REPORT;
|
||||
usb_buf[0x03] = subcommand;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
|
||||
|
||||
return(GetResponse());
|
||||
}
|
||||
|
||||
AlienwareReport AlienwareController::GetStatus(uint8_t subcommand)
|
||||
{
|
||||
HidapiAlienwareReport data = Report(subcommand);
|
||||
AlienwareReport result = AlienwareReport{};
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Skip first byte, as that's the report number, which |
|
||||
| should be 0 |
|
||||
\*-----------------------------------------------------*/
|
||||
memcpy(result.data, &data.data[1], sizeof(result.data));
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
bool AlienwareController::Dim(std::vector<uint8_t> zones, double percent)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Bail out if there are no zones to update |
|
||||
\*-----------------------------------------------------*/
|
||||
if(!zones.size())
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up message packet with leading 00, per hidapi |
|
||||
\*-----------------------------------------------------*/
|
||||
uint16_t num_zones = zones.size();
|
||||
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x03;
|
||||
usb_buf[0x02] = ALIENWARE_COMMAND_DIM;
|
||||
usb_buf[0x03] = static_cast<uint8_t>(percent * 0x64);
|
||||
usb_buf[0x04] = num_zones >> 8;
|
||||
usb_buf[0x05] = num_zones & 0xFF;
|
||||
|
||||
for(size_t i = 0; i < num_zones; i++)
|
||||
{
|
||||
usb_buf[0x06+i] = zones[i];
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
|
||||
|
||||
HidapiAlienwareReport response;
|
||||
response = GetResponse();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| For this command, error is if the output equals the |
|
||||
| input |
|
||||
\*-----------------------------------------------------*/
|
||||
return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE));
|
||||
}
|
||||
|
||||
bool AlienwareController::UserAnimation(uint16_t subcommand, uint16_t animation, uint16_t duration)
|
||||
{
|
||||
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up message packet with leading 00 per hidapi |
|
||||
\*-----------------------------------------------------*/
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x03;
|
||||
usb_buf[0x02] = ALIENWARE_COMMAND_USER_ANIM;
|
||||
usb_buf[0x03] = subcommand >> 8;
|
||||
usb_buf[0x04] = subcommand & 0xFF;
|
||||
usb_buf[0x05] = animation >> 8;
|
||||
usb_buf[0x06] = animation & 0xFF;
|
||||
usb_buf[0x07] = duration >> 8;
|
||||
usb_buf[0x08] = duration & 0xFF;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Every subcommand appears to report its result on a |
|
||||
| different byte |
|
||||
\*-----------------------------------------------------*/
|
||||
HidapiAlienwareReport response;
|
||||
response = GetResponse();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| The only time the 0x03 byte is zero is if the |
|
||||
| controller has crashed |
|
||||
\*-----------------------------------------------------*/
|
||||
if(response.data[1] == 0)
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
switch(subcommand)
|
||||
{
|
||||
case ALIENWARE_COMMAND_USER_ANIM_FINISH_SAVE:
|
||||
return(!response.data[7]);
|
||||
case ALIENWARE_COMMAND_USER_ANIM_FINISH_PLAY:
|
||||
return(!response.data[5]);
|
||||
case ALIENWARE_COMMAND_USER_ANIM_PLAY:
|
||||
return(!response.data[7]);
|
||||
default:
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool AlienwareController::SelectZones(const std::vector<uint8_t>& zones)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Bail if zones is empty, and return false to indicate |
|
||||
| nothing has changed |
|
||||
\*-----------------------------------------------------*/
|
||||
if(!zones.size())
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up message packet with leading 00, per hidapi |
|
||||
\*-----------------------------------------------------*/
|
||||
uint16_t num_zones = zones.size();
|
||||
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x03;
|
||||
usb_buf[0x02] = ALIENWARE_COMMAND_SELECT_ZONES;
|
||||
usb_buf[0x03] = 1; // loop?
|
||||
usb_buf[0x04] = num_zones >> 8;
|
||||
usb_buf[0x05] = num_zones & 0xFF;
|
||||
|
||||
for(size_t i = 0; i < num_zones; i++)
|
||||
{
|
||||
usb_buf[0x06+i] = zones[i];
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
|
||||
|
||||
HidapiAlienwareReport response;
|
||||
response = GetResponse();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| For this command, error is if the output equals the |
|
||||
| input |
|
||||
\*-----------------------------------------------------*/
|
||||
return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE));
|
||||
}
|
||||
|
||||
bool AlienwareController::ModeAction(uint8_t mode, uint16_t duration, uint16_t tempo, RGBColor color)
|
||||
{
|
||||
return(ModeAction(&mode, &duration, &tempo, &color, 1));
|
||||
}
|
||||
|
||||
bool AlienwareController::ModeAction
|
||||
(
|
||||
const uint8_t* mode,
|
||||
const uint16_t* duration,
|
||||
const uint16_t* tempo,
|
||||
const RGBColor* color,
|
||||
unsigned amount
|
||||
)
|
||||
{
|
||||
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Amount must be 3 or less, as that's how many |
|
||||
| subcommands can fit into one report |
|
||||
\*-----------------------------------------------------*/
|
||||
if(amount > 3)
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up message packet with leading 00, per hidapi |
|
||||
\*-----------------------------------------------------*/
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x03;
|
||||
usb_buf[0x02] = ALIENWARE_COMMAND_ADD_ACTION;
|
||||
|
||||
for(unsigned int i = 0; i < amount; i++)
|
||||
{
|
||||
usb_buf[0x03 + (8 * i)] = mode[i];
|
||||
usb_buf[0x04 + (8 * i)] = duration[i] >> 8;
|
||||
usb_buf[0x05 + (8 * i)] = duration[i] & 0xFF;
|
||||
usb_buf[0x06 + (8 * i)] = tempo[i] >> 8;
|
||||
usb_buf[0x07 + (8 * i)] = tempo[i] & 0xFF;
|
||||
usb_buf[0x08 + (8 * i)] = RGBGetRValue(color[i]);
|
||||
usb_buf[0x09 + (8 * i)] = RGBGetGValue(color[i]);
|
||||
usb_buf[0x0A + (8 * i)] = RGBGetBValue(color[i]);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
|
||||
|
||||
HidapiAlienwareReport response;
|
||||
response = GetResponse();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| For this command, error is if the output equals the |
|
||||
| input |
|
||||
\*-----------------------------------------------------*/
|
||||
return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE));
|
||||
}
|
||||
|
||||
bool AlienwareController::MultiModeAction
|
||||
(
|
||||
const uint8_t* mode,
|
||||
const uint16_t* duration,
|
||||
const uint16_t* tempo,
|
||||
const RGBColor* color,
|
||||
unsigned amount
|
||||
)
|
||||
{
|
||||
bool result = true;
|
||||
unsigned int left = amount;
|
||||
|
||||
while(left && result)
|
||||
{
|
||||
unsigned int tmp_amount;
|
||||
|
||||
tmp_amount = std::min(left, 3u);
|
||||
result &= ModeAction(mode, duration, tempo, color, tmp_amount);
|
||||
mode += tmp_amount;
|
||||
duration += tmp_amount;
|
||||
tempo += tmp_amount;
|
||||
color += tmp_amount;
|
||||
left -= tmp_amount;
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
bool AlienwareController::SetColorDirect(RGBColor color, std::vector<uint8_t> zones)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Bail if zones is empty |
|
||||
\*-----------------------------------------------------*/
|
||||
if(zones.empty())
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up message packet with leading 00, per hidapi |
|
||||
\*-----------------------------------------------------*/
|
||||
uint16_t num_zones = zones.size();
|
||||
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x03;
|
||||
usb_buf[0x02] = ALIENWARE_COMMAND_SET_COLOR;
|
||||
usb_buf[0x03] = RGBGetRValue(color);
|
||||
usb_buf[0x04] = RGBGetGValue(color);
|
||||
usb_buf[0x05] = RGBGetBValue(color);
|
||||
usb_buf[0x06] = num_zones >> 8;
|
||||
usb_buf[0x07] = num_zones & 0xFF;
|
||||
|
||||
for(size_t i = 0; i < num_zones; i++)
|
||||
{
|
||||
usb_buf[0x08 + i] = zones[i];
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
|
||||
|
||||
HidapiAlienwareReport response;
|
||||
response = GetResponse();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| For this command, error is if the output equals the |
|
||||
| input |
|
||||
\*-----------------------------------------------------*/
|
||||
return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE));
|
||||
}
|
||||
|
||||
bool AlienwareController::Reset()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Bail if zones is empty |
|
||||
\*-----------------------------------------------------*/
|
||||
if(zones.empty())
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up message packet with leading 00, per hidapi |
|
||||
\*-----------------------------------------------------*/
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x03;
|
||||
usb_buf[0x02] = ALIENWARE_COMMAND_RESET;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
|
||||
|
||||
HidapiAlienwareReport response;
|
||||
response = GetResponse();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| For this command, error is if the output equals the |
|
||||
| input |
|
||||
\*-----------------------------------------------------*/
|
||||
return(response.data[1] == 0x03);
|
||||
}
|
||||
|
||||
void AlienwareController::SetMode(uint8_t zone, uint8_t mode)
|
||||
{
|
||||
if(mode != zones[zone].mode)
|
||||
{
|
||||
zones[zone].mode = mode;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AlienwareController::SetColor(uint8_t zone, RGBColor color)
|
||||
{
|
||||
SetColor(zone, color, zones[zone].color[1]);
|
||||
}
|
||||
|
||||
void AlienwareController::SetColor(uint8_t zone, RGBColor color1, RGBColor color2)
|
||||
{
|
||||
if((color1 != zones[zone].color[0]))
|
||||
{
|
||||
zones[zone].color[0] = color1;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if((color2 != zones[zone].color[1]))
|
||||
{
|
||||
zones[zone].color[1] = color2;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AlienwareController::SetPeriod(uint8_t zone, uint16_t period)
|
||||
{
|
||||
if(period != zones[zone].period)
|
||||
{
|
||||
zones[zone].period = period;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AlienwareController::SetTempo(uint8_t zone, uint16_t tempo)
|
||||
{
|
||||
if(tempo != zones[zone].tempo)
|
||||
{
|
||||
zones[zone].tempo = tempo;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AlienwareController::SetDim(uint8_t zone, uint8_t dim)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Clamp dim to values between 0 and 100 |
|
||||
\*-----------------------------------------------------*/
|
||||
if(dim > 100)
|
||||
{
|
||||
dim = 100;
|
||||
}
|
||||
|
||||
if(dim != zones[zone].dim)
|
||||
{
|
||||
zones[zone].dim = dim;
|
||||
dirty_dim = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AlienwareController::UpdateDim()
|
||||
{
|
||||
if(!dirty_dim)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Collect all zones that share dim settings, and update |
|
||||
| them together |
|
||||
\*-----------------------------------------------------*/
|
||||
std::map<uint8_t, std::vector<uint8_t>> dim_zone_map;
|
||||
|
||||
for(size_t i = 0; i < zones.size(); i++)
|
||||
{
|
||||
dim_zone_map[zones[i].dim].emplace_back(i);
|
||||
}
|
||||
|
||||
for(std::pair<const uint8_t, std::vector<uint8_t>> &pair : dim_zone_map)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Bail on an error... |
|
||||
\*-------------------------------------------------*/
|
||||
if(!Dim(pair.second, pair.first))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dirty_dim = false;
|
||||
}
|
||||
|
||||
bool AlienwareController::UpdateDirect()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Collect all zones that share dim settings, and update |
|
||||
| them together |
|
||||
\*-----------------------------------------------------*/
|
||||
std::map<RGBColor, std::vector<uint8_t>> color_zone_map;
|
||||
|
||||
for(size_t i = 0; i < zones.size(); i++)
|
||||
{
|
||||
color_zone_map[zones[i].color[0]].emplace_back(i);
|
||||
}
|
||||
|
||||
for(std::pair<const RGBColor, std::vector<uint8_t>> &pair : color_zone_map)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Bail on an error... |
|
||||
\*-------------------------------------------------*/
|
||||
if(!SetColorDirect(pair.first, pair.second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const RGBColor rainbow_colors[4][7] =
|
||||
{
|
||||
{ 0xFF0000, 0xFFA500, 0xFFFF00, 0x008000, 0x00BFFF, 0x0000FF, 0x800080 },
|
||||
{ 0x800080, 0xFF0000, 0xFFA500, 0xFFFF00, 0x008000, 0x00BFFF, 0x0000FF },
|
||||
{ 0x0000FF, 0x800080, 0xFF0000, 0xFFA500, 0xFFFF00, 0x008000, 0x00BFFF },
|
||||
{ 0x00BFFF, 0x0000FF, 0x800080, 0xFF0000, 0xFFA500, 0xFFFF00, 0x008000 }
|
||||
};
|
||||
|
||||
void AlienwareController::UpdateMode()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| If there are no updates, don't bother running this |
|
||||
\*-----------------------------------------------------*/
|
||||
if(!dirty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool result = UserAnimation(ALIENWARE_COMMAND_USER_ANIM_NEW, ALIENWARE_COMMAND_USER_ANIM_KEYBOARD, 0);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
|
||||
{
|
||||
alienware_zone zone = zones[zone_idx];
|
||||
|
||||
result = SelectZones({static_cast<uint8_t>(zone_idx)});
|
||||
|
||||
if(!result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Some modes use 0x07D0 for their duration as sent |
|
||||
| by AWCC traces, maybe 2000ms? |
|
||||
\*-------------------------------------------------*/
|
||||
switch (zone.mode)
|
||||
{
|
||||
case ALIENWARE_MODE_COLOR:
|
||||
result = ModeAction(zone.mode, 2000, ALIENWARE_TEMPO_MAX, zone.color[0]);
|
||||
break;
|
||||
|
||||
case ALIENWARE_MODE_PULSE:
|
||||
result = ModeAction(zone.mode, zone.period, zone.tempo, zone.color[0]);
|
||||
break;
|
||||
|
||||
case ALIENWARE_MODE_MORPH:
|
||||
{
|
||||
uint8_t zones[2] = { zone.mode, zone.mode };
|
||||
uint16_t periods[2] = { zone.period, zone.period };
|
||||
uint16_t tempos[2] = { zone.tempo, zone.tempo };
|
||||
RGBColor colors[2] = { zone.color[0], zone.color[1] };
|
||||
|
||||
result = MultiModeAction(zones, periods, tempos, colors, 2);
|
||||
}
|
||||
break;
|
||||
|
||||
case ALIENWARE_MODE_SPECTRUM:
|
||||
{
|
||||
uint8_t zones[7] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
|
||||
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
|
||||
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
|
||||
ALIENWARE_MODE_MORPH };
|
||||
uint16_t periods[7] = { zone.period, zone.period,
|
||||
zone.period, zone.period,
|
||||
zone.period, zone.period,
|
||||
zone.period };
|
||||
uint16_t tempos[7] = { zone.tempo, zone.tempo,
|
||||
zone.tempo, zone.tempo,
|
||||
zone.tempo, zone.tempo,
|
||||
zone.tempo };
|
||||
|
||||
result = MultiModeAction(zones, periods, tempos, rainbow_colors[0], 7);
|
||||
}
|
||||
break;
|
||||
|
||||
case ALIENWARE_MODE_RAINBOW:
|
||||
{
|
||||
uint8_t zones[7] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
|
||||
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
|
||||
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
|
||||
ALIENWARE_MODE_MORPH };
|
||||
uint16_t periods[7] = { zone.period, zone.period,
|
||||
zone.period, zone.period,
|
||||
zone.period, zone.period,
|
||||
zone.period };
|
||||
uint16_t tempos[7] = { zone.tempo, zone.tempo,
|
||||
zone.tempo, zone.tempo,
|
||||
zone.tempo, zone.tempo,
|
||||
zone.tempo };
|
||||
|
||||
result = MultiModeAction(zones, periods, tempos, rainbow_colors[zone_idx], 7);
|
||||
}
|
||||
break;
|
||||
|
||||
case ALIENWARE_MODE_BREATHING:
|
||||
{
|
||||
uint8_t zones[2] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH };
|
||||
uint16_t periods[2] = { zone.period, zone.period };
|
||||
uint16_t tempos[2] = { zone.tempo, zone.tempo };
|
||||
RGBColor colors[2] = { zone.color[0], 0x0 };
|
||||
|
||||
result = MultiModeAction(zones, periods, tempos, colors, 2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
result = false;
|
||||
}
|
||||
|
||||
if(!result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
result = UserAnimation(ALIENWARE_COMMAND_USER_ANIM_FINISH_PLAY, ALIENWARE_COMMAND_USER_ANIM_KEYBOARD, 0);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Don't update dirty flag if there's an error |
|
||||
\*-------------------------------------------------*/
|
||||
if(!result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
void AlienwareController::UpdateController()
|
||||
{
|
||||
UpdateMode();
|
||||
UpdateDim();
|
||||
}
|
||||
161
Controllers/AlienwareController/AlienwareController.h
Normal file
161
Controllers/AlienwareController/AlienwareController.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*-----------------------------------------*\
|
||||
| AlienwareController.h |
|
||||
| |
|
||||
| Driver for Dell Alienware RGB USB |
|
||||
| controller |
|
||||
| |
|
||||
| Gabriel Marcano (gemarcano) 4/19/2021 |
|
||||
\*-----------------------------------------*/
|
||||
|
||||
#include "RGBController.h"
|
||||
#include <hidapi/hidapi.h>
|
||||
#include <string>
|
||||
|
||||
#pragma once
|
||||
|
||||
/*----------------------------------------------------------------------------------------------*\
|
||||
| Definitions for Alienware Controller |
|
||||
\*----------------------------------------------------------------------------------------------*/
|
||||
|
||||
#define ALIENWARE_REPORT_SIZE 33
|
||||
#define HIDAPI_ALIENWARE_REPORT_SIZE (ALIENWARE_REPORT_SIZE + 1)
|
||||
|
||||
enum
|
||||
{
|
||||
ALIENWARE_COMMAND_REPORT = 0x20, /* Set report type to get */
|
||||
ALIENWARE_COMMAND_USER_ANIM = 0x21, /* Set user animation settings */
|
||||
ALIENWARE_COMMAND_POWER_ANIM = 0x22, /* Set power animation settings */
|
||||
ALIENWARE_COMMAND_SELECT_ZONES = 0x23, /* Select zones to apply actions to */
|
||||
ALIENWARE_COMMAND_ADD_ACTION = 0x24, /* Set actions to apply */
|
||||
ALIENWARE_COMMAND_UNKNOWN1 = 0x25, /* Supposedly set event? */
|
||||
ALIENWARE_COMMAND_DIM = 0x26, /* Set dim percentage */
|
||||
ALIENWARE_COMMAND_SET_COLOR = 0x27, /* Unclear (causes color flash) */
|
||||
ALIENWARE_COMMAND_RESET = 0x28, /* Reset */
|
||||
ALIENWARE_COMMAND_ERASE_FLASH = 0xFF, /* Erases flash memory on controller */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ALIENWARE_COMMAND_REPORT_FIRMWARE = 0x00, /* Get firmware verion */
|
||||
ALIENWARE_COMMAND_REPORT_STATUS = 0x01, /* Get status */
|
||||
ALIENWARE_COMMAND_REPORT_CONFIG = 0x02, /* Get firmware config */
|
||||
ALIENWARE_COMMAND_REPORT_ANIMATION = 0x03, /* Get animation count and last id */
|
||||
ALIENWARE_COMMAND_REPORT_UNKNOWN1 = 0x04, /* Get ELC animation by ID */
|
||||
ALIENWARE_COMMAND_REPORT_UNKNOWN2 = 0x05, /* Read series??? */
|
||||
ALIENWARE_COMMAND_REPORT_UNKNOWN3 = 0x06, /* Get action??? */
|
||||
ALIENWARE_COMMAND_REPORT_UNKNOWN4 = 0x07, /* Get Caldera status??? */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ALIENWARE_COMMAND_USER_ANIM_NEW = 0x0001, /* Start new animation */
|
||||
ALIENWARE_COMMAND_USER_ANIM_FINISH_SAVE = 0x0002, /* Finish and save animation */
|
||||
ALIENWARE_COMMAND_USER_ANIM_FINISH_PLAY = 0x0003, /* Finish and play animation */
|
||||
ALIENWARE_COMMAND_USER_ANIM_REMOVE = 0x0004, /* Remove/erase animation */
|
||||
ALIENWARE_COMMAND_USER_ANIM_PLAY = 0x0005, /* Play animation */
|
||||
ALIENWARE_COMMAND_USER_ANIM_DEFAULT = 0x0006, /* Set default animation */
|
||||
ALIENWARE_COMMAND_USER_ANIM_STARTUP = 0x0007, /* Set startup animation */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ALIENWARE_ANIM_DEFAULT_STARTUP = 0x0008, /* Default slot for startup */
|
||||
ALIENWARE_ANIM_DEFAULT = 0x0061, /* Default slot */
|
||||
ALIENWARE_COMMAND_USER_ANIM_KEYBOARD = 0xFFFF, /* Non-saved animation slot */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ALIENWARE_MODE_COLOR = 0x00, /* Action to set color mode */
|
||||
ALIENWARE_MODE_PULSE = 0x01, /* Action to set pulse mode */
|
||||
ALIENWARE_MODE_MORPH = 0x02, /* Action to set morph mode */
|
||||
ALIENWARE_MODE_SPECTRUM, /* Abitrary code for spectrum mode */
|
||||
ALIENWARE_MODE_RAINBOW, /* Arbitrary code for rainbow wave mode */
|
||||
ALIENWARE_MODE_BREATHING, /* Arbitrary code for rainbow wave mode */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ALIENWARE_TEMPO_MIN = 0x0064, /* Min tempo (as used by AWCC) */
|
||||
ALIENWARE_TEMPO_MAX = 0x00FA, /* Max tempo (as used by AWCC) */
|
||||
ALIENWARE_TEMPO_SPECTRUM = 0x000F, /* Used by Spectrum mode */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ALIENWARE_DURATION_LONG = 0x09C4, /* Min tempo (as used by AWCC) */
|
||||
ALIENWARE_DURATION_MED = 0x05DC, /* Max tempo (as used by AWCC) */
|
||||
ALIENWARE_DURATION_SHORT = 0x01F3, /* Max tempo (as used by AWCC) */
|
||||
ALIENWARE_DURATION_SPECTRUM = 0x01AC, /* Used by Spectrum mode */
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char data[ALIENWARE_REPORT_SIZE];
|
||||
} AlienwareReport;
|
||||
|
||||
class AlienwareController
|
||||
{
|
||||
public:
|
||||
AlienwareController(hid_device* dev_handle, const hid_device_info& info, std::string name);
|
||||
~AlienwareController();
|
||||
|
||||
std::string GetSerialString();
|
||||
std::string GetDeviceLocation();
|
||||
std::string GetDeviceName();
|
||||
std::string GetFirmwareVersion();
|
||||
unsigned GetZoneCount();
|
||||
std::vector<const char*> GetZoneNames();
|
||||
|
||||
void SetColor(uint8_t zone, RGBColor color);
|
||||
void SetColor(uint8_t zone, RGBColor color1, RGBColor color2);
|
||||
void SetMode(uint8_t zone, uint8_t mode);
|
||||
void SetPeriod(uint8_t zone, uint16_t period);
|
||||
void SetTempo(uint8_t zone, uint16_t tempo);
|
||||
void SetDim(uint8_t zone, uint8_t dim);
|
||||
AlienwareReport GetStatus(uint8_t subcommand);
|
||||
|
||||
void UpdateDim();
|
||||
void UpdateMode();
|
||||
void UpdateController();
|
||||
|
||||
protected:
|
||||
hid_device* dev;
|
||||
|
||||
private:
|
||||
typedef struct
|
||||
{
|
||||
RGBColor color[2];
|
||||
uint8_t mode;
|
||||
uint16_t period;
|
||||
uint16_t tempo;
|
||||
uint8_t dim;
|
||||
} alienware_zone;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char data[HIDAPI_ALIENWARE_REPORT_SIZE];
|
||||
} HidapiAlienwareReport;
|
||||
|
||||
std::string device_name;
|
||||
std::string location;
|
||||
std::vector<alienware_zone> zones;
|
||||
std::string serial_number;
|
||||
std::string version;
|
||||
std::vector<const char*> zone_names;
|
||||
bool dirty;
|
||||
bool dirty_dim;
|
||||
|
||||
HidapiAlienwareReport GetResponse();
|
||||
HidapiAlienwareReport Report(uint8_t subcommand);
|
||||
|
||||
bool Dim(std::vector<uint8_t> zones, double percent);
|
||||
bool UserAnimation(uint16_t subcommand, uint16_t animation, uint16_t duration);
|
||||
bool SelectZones(const std::vector<uint8_t>& zones);
|
||||
bool ModeAction(uint8_t mode, uint16_t duration, uint16_t tempo, RGBColor color);
|
||||
bool ModeAction(const uint8_t *mode, const uint16_t *duration, const uint16_t *tempo, const RGBColor *color, unsigned amount);
|
||||
bool MultiModeAction(const uint8_t *mode, const uint16_t *duration, const uint16_t *tempo, const RGBColor *color, unsigned amount);
|
||||
bool SetColorDirect(RGBColor color, std::vector<uint8_t> zones);
|
||||
bool UpdateDirect();
|
||||
bool Reset();
|
||||
};
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#include "Detector.h"
|
||||
#include "AlienwareController.h"
|
||||
#include "RGBController.h"
|
||||
#include "RGBController_Alienware.h"
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Alienware vendor ID |
|
||||
\*---------------------------------------------------------*/
|
||||
#define ALIENWARE_VID 0x187C
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Alienware product ID |
|
||||
\*---------------------------------------------------------*/
|
||||
#define ALIENWARE_G5_SE_PID 0x0550
|
||||
|
||||
void DetectAlienwareControllers(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(dev)
|
||||
{
|
||||
AlienwareController* controller = new AlienwareController(dev, *info, name);
|
||||
RGBController_Alienware* rgb_controller = new RGBController_Alienware(controller);
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_HID_DETECTOR("Alienware G5 SE LED Controller", DetectAlienwareControllers, ALIENWARE_VID, ALIENWARE_G5_SE_PID);
|
||||
252
Controllers/AlienwareController/RGBController_Alienware.cpp
Normal file
252
Controllers/AlienwareController/RGBController_Alienware.cpp
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/*-----------------------------------------*\
|
||||
| RGBController_Alienware.cpp |
|
||||
| |
|
||||
| Generic RGB Interface for OpenRGB |
|
||||
| Alienware USB Driver |
|
||||
| |
|
||||
| Gabriel Marcano (gemarcano) 4/19/2021 |
|
||||
\*-----------------------------------------*/
|
||||
|
||||
#include "RGBController_Alienware.h"
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
RGBController_Alienware::RGBController_Alienware(AlienwareController* alienware_ptr)
|
||||
{
|
||||
alienware = alienware_ptr;
|
||||
|
||||
name = alienware->GetDeviceName();
|
||||
vendor = "Alienware Corporation";
|
||||
type = DEVICE_TYPE_KEYBOARD;
|
||||
description = "Alienware USB Device";
|
||||
location = alienware->GetDeviceLocation();
|
||||
serial = alienware->GetSerialString();
|
||||
version = alienware->GetFirmwareVersion();
|
||||
|
||||
mode Color;
|
||||
Color.name = "Static";
|
||||
Color.value = ALIENWARE_MODE_COLOR;
|
||||
Color.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Color.color_mode = MODE_COLORS_PER_LED;
|
||||
Color.colors_min = 1;
|
||||
Color.colors_max = 1;
|
||||
modes.push_back(Color);
|
||||
|
||||
mode Pulse;
|
||||
Pulse.name = "Flashing";
|
||||
Pulse.value = ALIENWARE_MODE_PULSE;
|
||||
Pulse.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
|
||||
Pulse.color_mode = MODE_COLORS_PER_LED;
|
||||
Pulse.colors_min = 1;
|
||||
Pulse.colors_max = 1;
|
||||
Pulse.speed_min = ALIENWARE_TEMPO_MIN;
|
||||
Pulse.speed_max = ALIENWARE_TEMPO_MAX;
|
||||
Pulse.speed = ALIENWARE_TEMPO_MIN;
|
||||
modes.push_back(Pulse);
|
||||
|
||||
mode Morph;
|
||||
Morph.name = "Morph";
|
||||
Morph.value = ALIENWARE_MODE_MORPH;
|
||||
Morph.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
|
||||
Morph.color_mode = MODE_COLORS_MODE_SPECIFIC;
|
||||
Morph.colors_min = 2 * alienware->GetZoneCount();
|
||||
Morph.colors_max = Morph.colors_min;
|
||||
Morph.colors.resize(Morph.colors_max);
|
||||
Morph.speed_min = ALIENWARE_TEMPO_MIN;
|
||||
Morph.speed_max = ALIENWARE_TEMPO_MAX;
|
||||
Morph.speed = ALIENWARE_TEMPO_MIN;
|
||||
modes.push_back(Morph);
|
||||
|
||||
mode Spectrum;
|
||||
Spectrum.name = "Spectrum Cycle";
|
||||
Spectrum.value = ALIENWARE_MODE_SPECTRUM;
|
||||
Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
|
||||
Spectrum.color_mode = MODE_COLORS_NONE;
|
||||
Spectrum.speed_min = ALIENWARE_TEMPO_SPECTRUM;
|
||||
Spectrum.speed_max = ALIENWARE_TEMPO_MAX;
|
||||
Spectrum.speed = ALIENWARE_TEMPO_SPECTRUM;
|
||||
modes.push_back(Spectrum);
|
||||
|
||||
mode Rainbow;
|
||||
Rainbow.name = "Rainbow Wave";
|
||||
Rainbow.value = ALIENWARE_MODE_RAINBOW;
|
||||
Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
|
||||
Rainbow.color_mode = MODE_COLORS_NONE;
|
||||
Rainbow.speed_min = ALIENWARE_TEMPO_SPECTRUM;
|
||||
Rainbow.speed_max = ALIENWARE_TEMPO_MAX;
|
||||
Rainbow.speed = ALIENWARE_TEMPO_SPECTRUM;
|
||||
modes.push_back(Rainbow);
|
||||
|
||||
mode Breathing;
|
||||
Breathing.name = "Breathing";
|
||||
Breathing.value = ALIENWARE_MODE_BREATHING;
|
||||
Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
|
||||
Breathing.color_mode = MODE_COLORS_PER_LED;
|
||||
Pulse.colors_min = 1;
|
||||
Pulse.colors_max = 1;
|
||||
Breathing.speed_min = ALIENWARE_TEMPO_MIN;
|
||||
Breathing.speed_max = ALIENWARE_TEMPO_MAX;
|
||||
Breathing.speed = ALIENWARE_TEMPO_MIN;
|
||||
modes.push_back(Breathing);
|
||||
|
||||
SetupZones();
|
||||
|
||||
alienware->UpdateDim();
|
||||
}
|
||||
|
||||
void RGBController_Alienware::SetupZones()
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Set zones and leds |
|
||||
\*-------------------------------------------------*/
|
||||
std::vector<const char*> zone_names = alienware->GetZoneNames();
|
||||
|
||||
for(unsigned int zone_idx = 0; zone_idx < alienware->GetZoneCount(); zone_idx++)
|
||||
{
|
||||
zone new_zone;
|
||||
|
||||
new_zone.name = zone_names[zone_idx];
|
||||
new_zone.type = ZONE_TYPE_SINGLE;
|
||||
new_zone.leds_min = 1;
|
||||
new_zone.leds_max = 1;
|
||||
new_zone.leds_count = 1;
|
||||
new_zone.matrix_map = nullptr;
|
||||
|
||||
zones.emplace_back(new_zone);
|
||||
}
|
||||
|
||||
for(unsigned int led_idx = 0; led_idx < zones.size(); led_idx++)
|
||||
{
|
||||
led new_led;
|
||||
|
||||
new_led.name = zones[led_idx].name + std::string(" LED");
|
||||
|
||||
leds.emplace_back(new_led);
|
||||
}
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
void RGBController_Alienware::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| This device does not support resizing zones |
|
||||
\*---------------------------------------------------------*/
|
||||
}
|
||||
|
||||
void RGBController_Alienware::DeviceUpdateLEDs()
|
||||
{
|
||||
DeviceUpdateMode();
|
||||
}
|
||||
|
||||
void RGBController_Alienware::UpdateZoneLEDs(int /*zone*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_Alienware::UpdateSingleLED(int led)
|
||||
{
|
||||
UpdateZoneLEDs(led);
|
||||
}
|
||||
|
||||
void RGBController_Alienware::SetCustomMode()
|
||||
{
|
||||
active_mode = 0;
|
||||
}
|
||||
|
||||
static bool modes_eq(const mode& mode1, const mode& mode2)
|
||||
{
|
||||
return( ( mode1.name == mode2.name )
|
||||
&& ( mode1.value == mode2.value )
|
||||
&& ( mode1.flags == mode2.flags )
|
||||
&& ( mode1.speed_min == mode2.speed_min )
|
||||
&& ( mode1.speed_max == mode2.speed_max )
|
||||
&& ( mode1.colors_min == mode2.colors_min )
|
||||
&& ( mode1.colors_max == mode2.colors_max )
|
||||
&& ( mode1.speed == mode2.speed )
|
||||
&& ( mode1.direction == mode2.direction )
|
||||
&& ( mode1.color_mode == mode2.color_mode )
|
||||
&& ( mode1.colors == mode2.colors ) );
|
||||
}
|
||||
|
||||
void RGBController_Alienware::DeviceUpdateMode()
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Copy mode to get the current state-- this is racy, as the |
|
||||
| UI thread can be actively modifying this variable |
|
||||
\*---------------------------------------------------------*/
|
||||
int current_mode_idx = active_mode;
|
||||
mode current_mode = modes[current_mode_idx];
|
||||
|
||||
bool done = false;
|
||||
|
||||
while(!done)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Setup state per zone |
|
||||
\*-----------------------------------------------------*/
|
||||
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
|
||||
{
|
||||
zone current_zone = zones[zone_idx];
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Some modes use 2000ms (0x07D0) for their duration,|
|
||||
| per traces |
|
||||
\*-------------------------------------------------*/
|
||||
uint16_t period = 0x07d0;
|
||||
|
||||
alienware->SetMode(zone_idx, current_mode.value);
|
||||
|
||||
switch(current_mode_idx)
|
||||
{
|
||||
case ALIENWARE_MODE_COLOR:
|
||||
alienware->SetPeriod(zone_idx, period);
|
||||
alienware->SetColor( zone_idx, colors[current_zone.start_idx]);
|
||||
alienware->SetTempo( zone_idx, ALIENWARE_TEMPO_MAX);
|
||||
break;
|
||||
|
||||
case ALIENWARE_MODE_PULSE:
|
||||
alienware->SetPeriod(zone_idx, period);
|
||||
alienware->SetColor( zone_idx, colors[current_zone.start_idx]);
|
||||
alienware->SetTempo( zone_idx, current_mode.speed);
|
||||
break;
|
||||
|
||||
case ALIENWARE_MODE_MORPH:
|
||||
alienware->SetPeriod(zone_idx, period);
|
||||
alienware->SetColor( zone_idx, current_mode.colors[zone_idx * 2], current_mode.colors[(zone_idx * 2) + 1]);
|
||||
alienware->SetTempo( zone_idx, current_mode.speed);
|
||||
break;
|
||||
|
||||
case ALIENWARE_MODE_SPECTRUM:
|
||||
case ALIENWARE_MODE_RAINBOW:
|
||||
alienware->SetPeriod(zone_idx, ALIENWARE_DURATION_SPECTRUM);
|
||||
alienware->SetTempo( zone_idx, current_mode.speed);
|
||||
break;
|
||||
|
||||
case ALIENWARE_MODE_BREATHING:
|
||||
alienware->SetPeriod(zone_idx, period);
|
||||
alienware->SetColor( zone_idx, colors[current_zone.start_idx], 0x0);
|
||||
alienware->SetTempo( zone_idx, current_mode.speed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Due to rate-limiting, this can take more than one |
|
||||
| second to execute |
|
||||
\*-----------------------------------------------------*/
|
||||
alienware->UpdateController();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Re-run update if there's anything that's changed from |
|
||||
| under us... |
|
||||
\*-----------------------------------------------------*/
|
||||
int new_current_mode_idx = active_mode;
|
||||
mode new_current_mode = modes[current_mode_idx];
|
||||
|
||||
done = (current_mode_idx == new_current_mode_idx && modes_eq(new_current_mode, current_mode));
|
||||
current_mode_idx = new_current_mode_idx;
|
||||
current_mode = new_current_mode;
|
||||
}
|
||||
}
|
||||
37
Controllers/AlienwareController/RGBController_Alienware.h
Normal file
37
Controllers/AlienwareController/RGBController_Alienware.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*-----------------------------------------*\
|
||||
| RGBController_Alienware.h |
|
||||
| |
|
||||
| Generic RGB Interface for OpenRGB |
|
||||
| Alienware RGB USB Driver |
|
||||
| |
|
||||
| Gabriel Marcano (gemarcano) 4/19/2021 |
|
||||
\*-----------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RGBController.h"
|
||||
#include "AlienwareController.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
class RGBController_Alienware : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_Alienware(AlienwareController* alienware_ptr);
|
||||
|
||||
void SetupZones();
|
||||
|
||||
void ResizeZone(int zone, int new_size);
|
||||
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
|
||||
void DeviceUpdateMode();
|
||||
|
||||
void SetCustomMode();
|
||||
|
||||
private:
|
||||
AlienwareController* alienware;
|
||||
std::chrono::steady_clock::time_point last_packet_ts;
|
||||
};
|
||||
|
|
@ -62,6 +62,7 @@ INCLUDEPATH +=
|
|||
serial_port/ \
|
||||
super_io/ \
|
||||
AutoStart/ \
|
||||
Controllers/AlienwareController/ \
|
||||
Controllers/AMDWraithPrismController/ \
|
||||
Controllers/ASRockPolychromeSMBusController/ \
|
||||
Controllers/ASRockPolychromeUSBController/ \
|
||||
|
|
@ -174,6 +175,8 @@ HEADERS +=
|
|||
serial_port/serial_port.h \
|
||||
super_io/super_io.h \
|
||||
AutoStart/AutoStart.h \
|
||||
Controllers/AlienwareController/AlienwareController.h \
|
||||
Controllers/AlienwareController/RGBController_Alienware.h \
|
||||
Controllers/AMDWraithPrismController/AMDWraithPrismController.h \
|
||||
Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.h \
|
||||
Controllers/AnnePro2Controller/AnnePro2Controller.h \
|
||||
|
|
@ -464,6 +467,9 @@ SOURCES +=
|
|||
serial_port/serial_port.cpp \
|
||||
super_io/super_io.cpp \
|
||||
AutoStart/AutoStart.cpp \
|
||||
Controllers/AlienwareController/AlienwareController.cpp \
|
||||
Controllers/AlienwareController/AlienwareControllerDetect.cpp \
|
||||
Controllers/AlienwareController/RGBController_Alienware.cpp \
|
||||
Controllers/AMDWraithPrismController/AMDWraithPrismController.cpp \
|
||||
Controllers/AMDWraithPrismController/AMDWraithPrismControllerDetect.cpp \
|
||||
Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.cpp \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue