diff --git a/Controllers/MSIMysticLightController/MSIMysticLightController.cpp b/Controllers/MSIMysticLightController/MSIMysticLightController.cpp new file mode 100644 index 00000000..2d1ce378 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLightController.cpp @@ -0,0 +1,156 @@ +/*-----------------------------------------*\ +| MSIMysticLightController.cpp | +| | +| Driver for MSI Mystic Light USB | +| lighting controller | +| | +| T-bond 3/4/2020 | +\*-----------------------------------------*/ + +#include "MSIMysticLightController.h" +#include +#include +#include + +MSIMysticLightController::MSIMysticLightController(hid_device* handle, const char *path) +{ + dev = handle; + + if( dev ) + { + loc = path; + } +} + +MSIMysticLightController::~MSIMysticLightController() +{ + if( dev ) + { + hid_close(dev); + } +} + +unsigned int MSIMysticLightController::GetZoneMinLedCount(ZONE zone) +{ + return 1; +} + +unsigned int MSIMysticLightController::GetZoneMaxLedCount(ZONE zone) +{ + switch (zone) + { + case ZONE::J_RAINBOW_1: + case ZONE::J_RAINBOW_2: + case ZONE::J_CORSAIR: + return 1; // TODO: Handle multiple leds + break; + default: + return 1; + } +} + +unsigned int MSIMysticLightController::GetZoneLedCount(ZONE zone) +{ + return GetZoneMaxLedCount(zone); // TODO: Handle multiple leds +} + +void MSIMysticLightController::SetMode(ZONE zone, EFFECT mode, SPEED speed, BRIGHTNESS brightness, bool rainbow_color) +{ + ZoneData* zoneData = GetZoneData(zone); + if(!zoneData) + { + return; + } + + zoneData->effect = mode; + zoneData->speedAndBrightnessFlags = (zoneData->speedAndBrightnessFlags & 128) | brightness << 2 | speed; + zoneData->colorFlags = static_cast(std::bitset<8>(zoneData->colorFlags).set(7, !rainbow_color).to_ulong()); +} + +std::string MSIMysticLightController::GetDeviceName() +{ + return(name); +} + +std::string MSIMysticLightController::GetFWVersion() +{ + return(version); +} + +std::string MSIMysticLightController::GetDeviceLocation() +{ + return(loc); +} + +std::string MSIMysticLightController::GetSerial() +{ + return(chip_id); +} + +bool MSIMysticLightController::Update() +{ + return hid_send_feature_report(dev, reinterpret_cast(&data), sizeof data) == sizeof data; +} + +void MSIMysticLightController::SetSendLedSettings(bool send) +{ + data.send_led_data = send; +} + +void MSIMysticLightController::SetZoneColor(ZONE zone, unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2) +{ + ZoneData* zoneData = GetZoneData(zone); + if(!zoneData) + { + return; + } + + zoneData->color.R = r1; + zoneData->color.G = g1; + zoneData->color.B = b1; + + zoneData->color2.R = r2; + zoneData->color2.G = g2; + zoneData->color2.B = b2; +} + +ZoneData *MSIMysticLightController::GetZoneData(ZONE zone) +{ + switch (zone) + { + case ZONE::J_RGB_1: + return &data.j_rgb_1; + case ZONE::J_RGB_2: + return &data.j_rgb_2; + case ZONE::J_RAINBOW_1: + return &data.j_rainbow_1; + case ZONE::J_RAINBOW_2: + return &data.j_rainbow_2; + case ZONE::J_PIPE_1: + return &data.j_pipe_1; + case ZONE::J_PIPE_2: + return &data.j_pipe_2; + case ZONE::ON_BOARD_LED: + return &data.on_board_led; + case ZONE::ON_BOARD_LED_1: + return &data.on_board_led_1; + case ZONE::ON_BOARD_LED_2: + return &data.on_board_led_2; + case ZONE::ON_BOARD_LED_3: + return &data.on_board_led_3; + case ZONE::ON_BOARD_LED_4: + return &data.on_board_led_4; + case ZONE::ON_BOARD_LED_5: + return &data.on_board_led_5; + case ZONE::ON_BOARD_LED_6: + return &data.on_board_led_6; + case ZONE::ON_BOARD_LED_7: + return &data.on_board_led_7; + case ZONE::ON_BOARD_LED_8: + return &data.on_board_led_8; + case ZONE::ON_BOARD_LED_9: + return &data.on_board_led_9; + } + + return nullptr; +} \ No newline at end of file diff --git a/Controllers/MSIMysticLightController/MSIMysticLightController.h b/Controllers/MSIMysticLightController/MSIMysticLightController.h new file mode 100644 index 00000000..1cc949e5 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLightController.h @@ -0,0 +1,191 @@ +/*-----------------------------------------*\ +| MSIMysticLightController.h | +| | +| Definitions and types for MSI Mystic | +| Light USB lighting controllers | +| | +| T-bond 3/4/2020 | +\*-----------------------------------------*/ + +#include "RGBController.h" +#include +#include +#include + +#pragma once + +enum ZONE +{ + J_RGB_1 = 1, + J_RGB_2 = 174, + J_PIPE_1 = 11, + J_PIPE_2 = 21, + J_RAINBOW_1 = 31, + J_RAINBOW_2 = 42, + J_CORSAIR = 53, + J_CORSAIR_OUTERLL120 = 64, + ON_BOARD_LED = 74, + ON_BOARD_LED_1 = 84, + ON_BOARD_LED_2 = 94, + ON_BOARD_LED_3 = 104, + ON_BOARD_LED_4 = 114, + ON_BOARD_LED_5 = 124, + ON_BOARD_LED_6 = 134, + ON_BOARD_LED_7 = 144, + ON_BOARD_LED_8 = 154, + ON_BOARD_LED_9 = 164 +}; + +struct ZoneDescription +{ + std::string name; + ZONE value; +}; + +enum EFFECT +{ + DISABLE, + STATIC, + BREATHING, + Flashing, + DoubleFlashing, + Lighting, + MSIMarquee, + Meteor, + WaterDrop, + MSIRainbow, + POP, + RAP, + JAZZ, + Play, + Movie, + ColorRing, + Planetary, + DoubleMeteor, + Energy, + Blink, + Clock, + ColorPulse, + ColorShift, + ColorWave, + Marquee, + Rainbow, + RainbowWave, + Visor, + JRainbow, + RainbowFlahing, + RainbowDoubleFlashing, + Random, + FANControl, + Disable2, + ColorRingFlashing, + ColorRingDoubleFlashing, + Stack, + CorsairiQUE, + Fire, + Lava +}; + +enum SPEED +{ + LOW, + MEDIUM, + HIGH +}; + +enum BRIGHTNESS +{ + OFF, + LEVEL_10, + LEVEL_20, + LEVEL_30, + LEVEL_40, + LEVEL_50, + LEVEL_60, + LEVEL_70, + LEVEL_80, + LEVEL_90, + LEVEL_100 +}; + +struct Color +{ + unsigned char R; + unsigned char G; + unsigned char B; +}; + +struct ZoneData +{ + unsigned char effect = EFFECT::STATIC; + Color color { std::numeric_limits::max(), 0, 0 }; + unsigned char speedAndBrightnessFlags = 40; + Color color2 { 0, std::numeric_limits::max(), 0 }; + unsigned char colorFlags = 128; + + const unsigned char padding = 0; +}; + +struct RainbowZoneData : ZoneData +{ + unsigned char cycleNum = 20; +}; + +struct FeaturePacket +{ + const unsigned char report_id = 82; // Report ID + ZoneData j_rgb_1; // 1 + ZoneData j_pipe_1; // 11 + ZoneData j_pipe_2; // 21 + RainbowZoneData j_rainbow_1; // 31 + RainbowZoneData j_rainbow_2; // 42 + RainbowZoneData j_corsair; // 53 + ZoneData j_corsair_outerll120; // 64 + ZoneData on_board_led; // 74 + ZoneData on_board_led_1; // 84 + ZoneData on_board_led_2; // 94 + ZoneData on_board_led_3; // 104 + ZoneData on_board_led_4; // 114 + ZoneData on_board_led_5; // 124 + ZoneData on_board_led_6; // 134 + ZoneData on_board_led_7; // 144 + ZoneData on_board_led_8; // 154 + ZoneData on_board_led_9; // 164 + ZoneData j_rgb_2; // 174 + + unsigned char send_led_data = 0; // 184 +}; + + +class MSIMysticLightController +{ +public: + MSIMysticLightController(hid_device* handle, const char *path); + ~MSIMysticLightController(); + + unsigned int GetZoneMinLedCount(ZONE zone); + unsigned int GetZoneMaxLedCount(ZONE zone); + unsigned int GetZoneLedCount(ZONE zone); + + void SetMode(ZONE zone, EFFECT mode, SPEED speed, BRIGHTNESS brightness, bool rainbow_color); + void SetZoneColor(ZONE zone, unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2); + bool Update(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + std::string GetFWVersion(); + std::string GetSerial(); + +private: + bool UpdateController(); + void SetSendLedSettings(bool send); + ZoneData* GetZoneData(ZONE zone); + + hid_device* dev; + std::string name; + std::string loc; + std::string version; + std::string chip_id; + + FeaturePacket data; +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLightControllerDetect.cpp b/Controllers/MSIMysticLightController/MSIMysticLightControllerDetect.cpp new file mode 100644 index 00000000..3e41c274 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLightControllerDetect.cpp @@ -0,0 +1,90 @@ +#include "MSIMysticLightController.h" +#include "RGBController_MSIMysticLight.h" +#include + +#define MSI_VID 0x1462 + +constexpr unsigned short SupportedPIDs[41] = +{ + 31847, // MS_7C67 + 31504, // MS_7B10 + 31879, // MS_7C87 + 31635, // MS_7B93 + 31796, // MS_7C34 + 31797, // MS_7C35 + 31798, // MS_7C36 + 31799, // MS_7C37 + 31810, // MS_7C42 + 31876, // MS_7C84 + 31636, // MS_7B94 + 31638, // MS_7B96 + 31833, // MS_7C59 + 31840, // MS_7C60 + 31856, // MS_7C70 + 31857, // MS_7C71 + 31859, // MS_7C73 + 31861, // MS_7C75 + 31862, // MS_7C76 + 31863, // MS_7C77 + 31865, // MS_7C79 + 31872, // MS_7C80 + 31896, // MS_7C98 + 31897, // MS_7C99 + 31873, // MS_7C81 + 31874, // MS_7C82 + 31875, // MS_7C83 + 31877, // MS_7C85 + 31878, // MS_7C86 + 31880, // MS_7C88 + 31881, // MS_7C89 + 17497, // MS_4459 + 16036, // MS_3EA4 + 36957, // MS_905D + 31888, // MS_7C90 + 31889, // MS_7C91 + 31890, // MS_7C92 + 31892, // MS_7C94 + 31893, // MS_7C95 + 31894, // MS_7C96 + 31830 // MS_7C56 +}; + +/******************************************************************************************\ +* * +* DetectMSIMysticLightControllers * +* * +* Detect MSI Mystic Light devices that use NCT6775 RGB controllers * +* * +\******************************************************************************************/ + +void DetectMSIMysticLightControllers(std::vector &rgb_controllers) +{ + if(hid_init() < 0) + { + return; + } + + hid_device_info * device_list = hid_enumerate(MSI_VID, 0); + if(!device_list) + { + return; + } + + hid_device_info * device = device_list; + while(device) + { + if(std::find(std::begin(SupportedPIDs), std::end(SupportedPIDs), device->product_id) != std::end(SupportedPIDs)) + { + hid_device * dev = hid_open_path(device->path); + if(dev) + { + MSIMysticLightController * controller = new MSIMysticLightController(dev, device_list->path); + RGBController_MSIMysticLight * rgb_controller = new RGBController_MSIMysticLight(controller); + rgb_controllers.push_back(rgb_controller); + } + } + device = device->next; + } + + hid_free_enumeration(device_list); +} diff --git a/OpenRGB.cpp b/OpenRGB.cpp index 9bdd9c54..449bd3e6 100644 --- a/OpenRGB.cpp +++ b/OpenRGB.cpp @@ -293,6 +293,7 @@ void DetectPatriotViperControllers(std::vector &busses, st void DetectPolychromeControllers(std::vector& busses, std::vector& rgb_controllers); void DetectRGBFusionControllers(std::vector& busses, std::vector& rgb_controllers); void DetectRGBFusionGPUControllers(std::vector& busses, std::vector& rgb_controllers); +void DetectMSIMysticLightControllers(std::vector &rgb_controllers); void DetectMSIRGBControllers(std::vector &rgb_controllers); void DetectAuraAddressableControllers(std::vector &rgb_controllers); void DetectLEDStripControllers(std::vector &rgb_controllers); @@ -335,6 +336,7 @@ void DetectRGBControllers(void) DetectRGBFusionGPUControllers(busses, rgb_controllers); DetectRGBFusionControllers(busses, rgb_controllers); + DetectMSIMysticLightControllers(rgb_controllers); DetectMSIRGBControllers(rgb_controllers); DetectAuraAddressableControllers(rgb_controllers); diff --git a/OpenRGB.pro b/OpenRGB.pro index d53fa2e7..5379ab75 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -45,6 +45,7 @@ INCLUDEPATH += \ Controllers/HyperXKeyboardController/ \ Controllers/LEDStripController/ \ Controllers/MSI3ZoneController/ \ + Controllers/MSIMysticLightController/ \ Controllers/MSIRGBController/ \ Controllers/NZXTKrakenController/ \ Controllers/PatriotViperController/ \ @@ -110,6 +111,8 @@ SOURCES += \ Controllers/LEDStripController/LEDStripControllerDetect.cpp \ Controllers/MSI3ZoneController/MSI3ZoneController.cpp \ Controllers/MSI3ZoneController/MSI3ZoneControllerDetect.cpp \ + Controllers/MSIMysticLightController/MSIMysticLightController.cpp \ + Controllers/MSIMysticLightController/MSIMysticLightControllerDetect.cpp \ Controllers/MSIRGBController/MSIRGBController.cpp \ Controllers/MSIRGBController/MSIRGBControllerDetect.cpp \ Controllers/NZXTKrakenController/NZXTKrakenController.cpp \ @@ -150,6 +153,7 @@ SOURCES += \ RGBController/RGBController_E131.cpp \ RGBController/RGBController_LEDStrip.cpp \ RGBController/RGBController_MSI3Zone.cpp \ + RGBController/RGBController_MSIMysticLight.cpp \ RGBController/RGBController_MSIRGB.cpp \ RGBController/RGBController_NZXTKraken.cpp \ RGBController/RGBController_PatriotViper.cpp \ @@ -194,6 +198,7 @@ HEADERS += \ Controllers/HyperXKeyboardController/HyperXKeyboardController.h \ Controllers/LEDStripController/LEDStripController.h \ Controllers/MSI3ZoneController/MSI3ZoneController.h \ + Controllers/MSIMysticLightController/MSIMysticLightController.h \ Controllers/MSIRGBController/MSIRGBController.h \ Controllers/PatriotViperController/PatriotViperController.h \ Controllers/PolychromeController/PolychromeController.h \ @@ -222,6 +227,7 @@ HEADERS += \ RGBController/RGBController_HyperXKeyboard.h \ RGBController/RGBController_LEDStrip.h \ RGBController/RGBController_MSI3Zone.h \ + RGBController/RGBController_MSIMysticLight.h \ RGBController/RGBController_MSIRGB.h \ RGBController/RGBController_PatriotViper.h \ RGBController/RGBController_Polychrome.h \ diff --git a/RGBController/RGBController_MSIMysticLight.cpp b/RGBController/RGBController_MSIMysticLight.cpp new file mode 100644 index 00000000..d44e2bf3 --- /dev/null +++ b/RGBController/RGBController_MSIMysticLight.cpp @@ -0,0 +1,173 @@ +/*-----------------------------------------*\ +| RGBController_MSIMysticLight.cpp | +| | +| Generic RGB Interface for OpenRGB | +| MSI Mystic Light USB Driver | +| | +| T-bond 3/4/2020 | +\*-----------------------------------------*/ + +#include "RGBController_MSIMysticLight.h" +#include + +static const std::array led_zones +{ + ZoneDescription{"JRGB1", J_RGB_1}, + ZoneDescription{"JRGB2", J_RGB_2}, + ZoneDescription{"JRAINBOW1", J_RAINBOW_1}, + ZoneDescription{"JRAINBOW2", J_RAINBOW_2}, + ZoneDescription{"JPIPE1", J_PIPE_1}, + ZoneDescription{"JPIPE2", J_PIPE_2}, + ZoneDescription{"JCORSAIR", J_CORSAIR}, + ZoneDescription{"JCORSAIR Outer", J_CORSAIR_OUTERLL120}, + ZoneDescription{"OnboardLED", ON_BOARD_LED}, + ZoneDescription{"OnboardLED1", ON_BOARD_LED_1}, + ZoneDescription{"OnboardLED2", ON_BOARD_LED_2}, + ZoneDescription{"OnboardLED3", ON_BOARD_LED_3}, + ZoneDescription{"OnboardLED4", ON_BOARD_LED_4}, + ZoneDescription{"OnboardLED5", ON_BOARD_LED_5}, + ZoneDescription{"OnboardLED6", ON_BOARD_LED_6}, + ZoneDescription{"OnboardLED7", ON_BOARD_LED_7}, + ZoneDescription{"OnboardLED8", ON_BOARD_LED_8}, + ZoneDescription{"OnboardLED9", ON_BOARD_LED_9}, +}; + +RGBController_MSIMysticLight::RGBController_MSIMysticLight(MSIMysticLightController* controller_ptr) +{ + controller = controller_ptr; + + name = "MSI Mystic Light Controller"; + type = DEVICE_TYPE_MOTHERBOARD; + description = controller->GetDeviceName(); + version = controller->GetFWVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + + SetupModes(); + SetupZones(); + SetupColors(); +} + +void RGBController_MSIMysticLight::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < led_zones.size(); zone_idx++) + { + ZoneDescription zd = led_zones[zone_idx]; + zone new_zone; + new_zone.name = zd.name; + new_zone.type = ZONE_TYPE_LINEAR; + + new_zone.leds_min = controller->GetZoneMinLedCount(zd.value); + new_zone.leds_min = controller->GetZoneMaxLedCount(zd.value); + new_zone.leds_count = controller->GetZoneLedCount(zd.value); + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_zone.name + " LED "; + if(new_zone.leds_count > 1) + { + new_led.name.append(std::to_string(led_idx + 1)); + } + + new_led.value = zone_idx; + + leds.push_back(new_led); + } + } +} + +void RGBController_MSIMysticLight::ResizeZone(int zone, int new_size) +{ + ZONE zon = ZoneFromPos(zone); + unsigned int max_count = controller->GetZoneMaxLedCount(zon), + min_count = controller->GetZoneMinLedCount(zon), + new_siz = new_size; + new_siz = std::min(std::max(new_siz, min_count), max_count); // std::clamp only from C++17 + /// TODO: Update LED count +} + +void RGBController_MSIMysticLight::SetCustomMode() +{ + active_mode = 0; +} + +void RGBController_MSIMysticLight::UpdateLEDs() +{ + for(size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + UpdateZoneLEDs(zone_idx); + } +} + +void RGBController_MSIMysticLight::UpdateZoneLEDs(int zone) +{ + for(int led_idx = zones[zone].leds_count - 1; led_idx >= 0; led_idx--) + { + UpdateLed(zone, led_idx); + } + controller->Update(); +} + +void RGBController_MSIMysticLight::UpdateSingleLED(int led) +{ + UpdateLed(leds[led].value, led); + controller->Update(); +} + +void RGBController_MSIMysticLight::UpdateMode() +{ +} + +void RGBController_MSIMysticLight::SetupModes() +{ + mode Off; + Off.name = "Off"; + Off.value = EFFECT::DISABLE; + Off.color_mode = MODE_COLORS_PER_LED; // Only needed to show the Zones and Leds on the GUI + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = EFFECT::STATIC; + Static.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EFFECT::BREATHING; + Breathing.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed = SPEED::MEDIUM; + Breathing.speed_max = SPEED::HIGH; + Breathing.speed_min = SPEED::LOW; + modes.push_back(Breathing); +} + +void RGBController_MSIMysticLight::UpdateLed(int zone, int led) +{ + bool random = modes[active_mode].color_mode == MODE_COLORS_RANDOM; + unsigned char red = RGBGetRValue(zones[zone].colors[led]); + unsigned char grn = RGBGetGValue(zones[zone].colors[led]); + unsigned char blu = RGBGetBValue(zones[zone].colors[led]); + EFFECT mode = static_cast(modes[active_mode].value); + SPEED speed = static_cast(modes[active_mode].speed); + ZONE zon = ZoneFromPos(zone); + + controller->SetMode(zon, mode, speed, BRIGHTNESS::LEVEL_100, random); + controller->SetZoneColor(zon, red, grn, blu, red, grn, blu); +} + +ZONE RGBController_MSIMysticLight::ZoneFromPos(int zone) +{ + return led_zones[zone].value; +} \ No newline at end of file diff --git a/RGBController/RGBController_MSIMysticLight.h b/RGBController/RGBController_MSIMysticLight.h new file mode 100644 index 00000000..00a2a4d4 --- /dev/null +++ b/RGBController/RGBController_MSIMysticLight.h @@ -0,0 +1,46 @@ +/*-----------------------------------------*\ +| RGBController_MSIMysticLight.h | +| | +| Generic RGB Interface for OpenRGB | +| MSI Mystic Light USB Driver | +| | +| T-bond 3/4/2020 | +\*-----------------------------------------*/ + +#pragma once +#include "RGBController.h" +#include "MSIMysticLightController.h" +#include +#include + +struct LedPort +{ + const char* name; + int header; +}; + +typedef std::vector< std::vector > ZoneLeds; + +class RGBController_MSIMysticLight: public RGBController +{ +public: + RGBController_MSIMysticLight(MSIMysticLightController* controller_ptr); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void UpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void SetCustomMode(); + void UpdateMode(); + +private: + void SetupModes(); + void UpdateLed(int zone, int led); + ZONE ZoneFromPos(int zone); + + MSIMysticLightController* controller; +};