MSIMonitorController: 0x72 layout support (MPG 322URX QD-OLED + siblings)

This commit is contained in:
Ken Sanislo
2026-07-21 04:22:02 +00:00
committed by Adam Honse
parent b35e119325
commit 7fc8aea930
7 changed files with 955 additions and 24 deletions

View File

@@ -1,9 +1,10 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| MSIMonitorController.cpp |
| |
| RGBController for MSI monitor (gaming controller) |
| Driver for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 June 4 |
| Ken Sanislo 2026 July 20 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -119,3 +120,123 @@ void MSIMonitorController::Set(uint8_t mode_value, const std::vector<RGBColor> c
hid_send_feature_report(dev, data, MSI_MONITOR_PACKET_SIZE);
}
uint8_t MSIMonitorController::GetLayoutVersion()
{
/*-----------------------------------------------------*\
| Ask the MCU which buffer layout it speaks. Request |
| is [0x01][opcode] zero padded, reply is [0x01][0x5A] |
| then data with the version at byte 3. |
\*-----------------------------------------------------*/
uint8_t request[MSI_MONITOR_QUERY_PACKET_SIZE];
uint8_t reply[MSI_MONITOR_QUERY_PACKET_SIZE];
/*-----------------------------------------------------*\
| Unimplemented opcodes never answer, so a reply left |
| queued by an earlier exchange would be read here and |
| parsed as this query's answer. Drain first. |
\*-----------------------------------------------------*/
while(hid_read_timeout(dev, reply, MSI_MONITOR_QUERY_PACKET_SIZE, 0) > 0)
{
continue;
}
memset(request, 0x00, MSI_MONITOR_QUERY_PACKET_SIZE);
request[0] = MSI_MONITOR_QUERY_REPORT_ID;
request[1] = MSI_MONITOR_OPCODE_GET_VERSION;
if(hid_write(dev, request, MSI_MONITOR_QUERY_PACKET_SIZE) < 0)
{
return(MSI_MONITOR_VERSION_UNKNOWN);
}
memset(reply, 0x00, MSI_MONITOR_QUERY_PACKET_SIZE);
if(hid_read_timeout(dev, reply, MSI_MONITOR_QUERY_PACKET_SIZE, 500) <= 0)
{
return(MSI_MONITOR_VERSION_UNKNOWN);
}
if(reply[1] != MSI_MONITOR_QUERY_ACK)
{
return(MSI_MONITOR_VERSION_UNKNOWN);
}
return(reply[3]);
}
void MSIMonitorController::SetMode72(uint8_t mode_value, uint8_t speed, uint8_t brightness, RGBColor color1, RGBColor color2, bool user_palette, bool save, const std::vector<RGBColor>& led_colors, bool fill_both_arrays)
{
uint8_t data[MSI_MONITOR_72_PACKET_SIZE];
memset(data, 0x00, MSI_MONITOR_72_PACKET_SIZE);
data[0] = 0x72;
/*-----------------------------------------------------*\
| Identical 11 byte control blocks at index 1 and 12 |
\*-----------------------------------------------------*/
for(unsigned int base = 1; base <= 12; base += 11)
{
data[base + 0] = mode_value;
data[base + 1] = RGBGetRValue(color1);
data[base + 2] = RGBGetGValue(color1);
data[base + 3] = RGBGetBValue(color1);
data[base + 4] = speed;
data[base + 5] = brightness;
data[base + 6] = RGBGetRValue(color2);
data[base + 7] = RGBGetGValue(color2);
data[base + 8] = RGBGetBValue(color2);
data[base + 9] = user_palette ? 0x80 : 0x00;
data[base + 10] = 0x00;
}
/*-----------------------------------------------------*\
| The engine paints from the second inline colour array |
| at index 95, not from Colour1 and not from the first |
| array at index 23: on a 322URX a buffer carrying only |
| Colour1 renders black, and walking one lit slot |
| through the buffer only lights LEDs for the second |
| array. That pairs with the control block at index 12 |
| being the operative one. Built in palette modes |
| leave the array clear so the firmware effect is not |
| painted over. |
| |
| fill_both_arrays also writes the first array at index |
| 23, for the dual block versions (11/13/15) whose two |
| control blocks each drive their own run. On a single |
| block layout the first array is inert, so filling it |
| is harmless. Those versions also route colour index |
| above 15 to a separate DragonShield emblem region not |
| handled here - one reason they stay disabled. |
\*-----------------------------------------------------*/
if(user_palette)
{
for(unsigned int slot = 0; slot < MSI_MONITOR_72_ARRAY_SLOTS && slot < led_colors.size(); slot++)
{
unsigned int offset_b = MSI_MONITOR_72_ARRAY_B_INDEX + (slot * 3);
data[offset_b + 0] = RGBGetRValue(led_colors[slot]);
data[offset_b + 1] = RGBGetGValue(led_colors[slot]);
data[offset_b + 2] = RGBGetBValue(led_colors[slot]);
if(fill_both_arrays)
{
unsigned int offset_a = MSI_MONITOR_72_ARRAY_A_INDEX + (slot * 3);
data[offset_a + 0] = RGBGetRValue(led_colors[slot]);
data[offset_a + 1] = RGBGetGValue(led_colors[slot]);
data[offset_a + 2] = RGBGetBValue(led_colors[slot]);
}
}
}
/*-----------------------------------------------------*\
| Final byte is store to flash: 0x01 persists across |
| power cycles, 0x00 applies without a flash write. |
| Keep 0x00 for streaming to avoid flicker and wear. |
\*-----------------------------------------------------*/
data[MSI_MONITOR_72_STORE_INDEX] = save ? 0x01 : 0x00;
hid_send_feature_report(dev, data, MSI_MONITOR_72_PACKET_SIZE);
}

View File

@@ -1,9 +1,10 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| MSIMonitorController.h |
| |
| RGBController for MSI monitor (gaming controller) |
| Driver for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 June 1 |
| Ken Sanislo 2026 July 20 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -18,6 +19,42 @@
#define MSI_MONITOR_LEDS 9
#define MSI_MONITOR_PACKET_SIZE 78
/*---------------------------------------------------------*\
| 0x72 layout (dual control block, e.g. MPG 322URX QD-OLED) |
\*---------------------------------------------------------*/
#define MSI_MONITOR_72_PACKET_SIZE 168
#define MSI_MONITOR_72_STORE_INDEX 167
#define MSI_MONITOR_72_ARRAY_A_INDEX 23
#define MSI_MONITOR_72_ARRAY_B_INDEX 95
#define MSI_MONITOR_72_ARRAY_SLOTS 24
/*---------------------------------------------------------*\
| Fill order the firmware expects (vendor Direction enum). |
| Only the physical origin differs; index 0 is that end. |
| The controller mirrors RightToLeft into left origin so |
| OpenRGB LED 0 is always the left/start. Circle and Ctype |
| are non linear shapes: driven as a plain line for now, |
| they want a custom layout once someone maps the shape. |
\*---------------------------------------------------------*/
enum
{
MSI_MONITOR_72_DIR_RIGHT_TO_LEFT = 0,
MSI_MONITOR_72_DIR_LEFT_TO_RIGHT = 1,
MSI_MONITOR_72_DIR_UP_TO_DOWN = 2,
MSI_MONITOR_72_DIR_CIRCLE = 3,
MSI_MONITOR_72_DIR_CTYPE = 4,
};
/*---------------------------------------------------------*\
| Layout version query rides the short report channel: |
| write [0x01][opcode], read [0x01][0x5A][data] |
\*---------------------------------------------------------*/
#define MSI_MONITOR_QUERY_PACKET_SIZE 64
#define MSI_MONITOR_QUERY_REPORT_ID 0x01
#define MSI_MONITOR_QUERY_ACK 0x5A
#define MSI_MONITOR_OPCODE_GET_VERSION 0xB0
#define MSI_MONITOR_VERSION_UNKNOWN 0
enum
{
MSI_MONITOR_OFF_MODE_VALUE = 0x00,
@@ -31,6 +68,53 @@ enum
MSI_MONITOR_RANDOM_MODE_VALUE = 0x1F
};
/*---------------------------------------------------------*\
| 0x72 layout mode values. These match the MSI Mystic |
| Light motherboard mode enum value for value (see |
| MSIMysticLightController/MSIMysticLightCommon.h): the |
| monitor runs the same Nuvoton Mystic Light engine, so the |
| effect numbering is shared with its motherboard sibling. |
| Values 10-14 and 32 are the audio modes, not exposed |
| here (they need the music level feed). |
\*---------------------------------------------------------*/
enum
{
MSI_MONITOR_72_MODE_OFF = 0,
MSI_MONITOR_72_MODE_STATIC = 1,
MSI_MONITOR_72_MODE_BREATHING = 2,
MSI_MONITOR_72_MODE_FLASHING = 3,
MSI_MONITOR_72_MODE_DOUBLE_FLASHING = 4,
MSI_MONITOR_72_MODE_LIGHTNING = 5,
MSI_MONITOR_72_MODE_SCANNER = 6,
MSI_MONITOR_72_MODE_METEOR = 7,
MSI_MONITOR_72_MODE_WATER_DROP = 8,
MSI_MONITOR_72_MODE_RAINBOW_MSI = 9,
MSI_MONITOR_72_MODE_COLOR_RING = 15,
MSI_MONITOR_72_MODE_PLANETARY = 16,
MSI_MONITOR_72_MODE_DOUBLE_METEOR = 17,
MSI_MONITOR_72_MODE_ENERGY = 18,
MSI_MONITOR_72_MODE_BLINK = 19,
MSI_MONITOR_72_MODE_CLOCK = 20,
MSI_MONITOR_72_MODE_COLOR_PULSE = 21,
MSI_MONITOR_72_MODE_COLOR_SHIFT = 22,
MSI_MONITOR_72_MODE_COLOR_WAVE = 23,
MSI_MONITOR_72_MODE_MARQUEE = 24,
MSI_MONITOR_72_MODE_RAINBOW = 25,
MSI_MONITOR_72_MODE_RAINBOW_WAVE = 26,
MSI_MONITOR_72_MODE_VISOR = 27,
MSI_MONITOR_72_MODE_JRAINBOW = 28,
MSI_MONITOR_72_MODE_RAINBOW_FLASH = 29,
MSI_MONITOR_72_MODE_RAINBOW_DBLFLASH = 30,
MSI_MONITOR_72_MODE_RAINBOW_LINE = 33,
};
enum
{
MSI_MONITOR_72_SPEED_LOW = 0,
MSI_MONITOR_72_SPEED_MEDIUM = 1,
MSI_MONITOR_72_SPEED_HIGH = 2,
};
class MSIMonitorController
{
public:
@@ -44,6 +128,9 @@ public:
void Set(uint8_t mode_value, const std::vector<RGBColor> colors, uint8_t last_bit);
uint8_t GetLayoutVersion();
void SetMode72(uint8_t mode_value, uint8_t speed, uint8_t brightness, RGBColor color1, RGBColor color2, bool user_palette, bool save, const std::vector<RGBColor>& led_colors, bool fill_both_arrays);
private:
hid_device *dev;
std::string description;

View File

@@ -1,5 +1,5 @@
/*---------------------------------------------------------*\
| MSIKeyboardControllerDetect.cpp |
| MSIMonitorControllerDetect.cpp |
| |
| Detector for MSI monitor (MSI Gaming Controller) |
| |
@@ -10,10 +10,107 @@
#include "DetectionManager.h"
#include "MSIMonitorController.h"
#include "RGBController_MSIMonitor.h"
#include "RGBController_MSIMonitor72.h"
#define MSI_USB_VID 0x1462
#define MSI_USB_PID 0x3FA4
/*---------------------------------------------------------*\
| Vendor layout table (from the version -> layout map). |
| The firmware reports its version to the 0xB0 query but |
| not a LED count, so count/direction/block-count come |
| from here. Fields: version, LED count, fill direction |
| (MSI_MONITOR_72_DIR_*), dual_block. |
| |
| Every entry here is a single linear run driven through |
| the 168 byte 0x72 buffer. Direction mirrors the fill so |
| OpenRGB LED 0 is the left/start on every model. Circle |
| and Ctype have no simple line mapping - they are driven |
| as a plain line here and should get a custom per model |
| layout once an owner maps the physical shape. |
| |
| Only version 34 is hardware verified (MPG 322URX). The |
| rest are derived from the vendor table: same buffer, LED |
| count and direction are the only differences, so effect |
| and single colour modes are correct by construction. |
| |
| Left out on purpose: |
| - dual_block versions 11/13/15 are stubbed but disabled |
| (see the commented block); their two control blocks |
| drive two runs and the per LED split is unconfirmed. |
| - Advanced/0x82 versions 31/47 need the 0x30 bulk report |
| this controller does not implement. |
| A version not matched here falls through to the legacy |
| 0x71 controller, home of the MAG272 class. |
\*---------------------------------------------------------*/
struct MSIMonitor72Layout
{
uint8_t version;
unsigned int leds;
unsigned int direction;
bool dual_block;
};
static const MSIMonitor72Layout msi_monitor_72_layouts[] =
{
{ 34, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG271QRXQD_OLED (MPG 322URX reports this) - verified */
{ 5, 12, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MAG272R */
{ 8, 24, MSI_MONITOR_72_DIR_CIRCLE, false }, /* MAG301CR - Circle, wants custom layout */
{ 10, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG273R */
{ 12, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG271VCR */
{ 14, 14, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* NXG253R */
{ 17, 8, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MAG2x5R */
{ 20, 12, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG301RF */
{ 21, 18, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG341QR */
{ 22, 24, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG321QRF */
{ 24, 12, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG321QR */
{ 25, 24, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG321UR */
{ 28, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG273CQR */
{ 29, 24, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MAG401QR */
{ 30, 21, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MAG325CQRF */
{ 32, 7, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG274QRF */
{ 33, 7, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG274URFQD */
{ 36, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG275CQRXF */
{ 37, 22, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG345CQRD */
{ 38, 20, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG325CQRFQDE2 */
{ 39, 8, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MPG321CURX_QD_OLED */
{ 40, 12, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG322URDFE16 */
{ 41, 14, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MPG242RX60N */
{ 42, 22, MSI_MONITOR_72_DIR_CTYPE, false }, /* MPG274URDFWE16 - Ctype, wants custom layout */
{ 43, 21, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG346CQRFX24 */
{ 44, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG271QRQD_OLEDX50 */
{ 46, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG341CQRQD_OLEDX36 */
/*-----------------------------------------------------------------*\
| Dual block - stubbed but DISABLED. The dual_block path fills |
| both colour arrays so uniform colour modes are correct, but each |
| block drives its own run and the per LED split is unconfirmed, so |
| Direct is a guess. These versions also map colour index above 15 |
| to a separate DragonShield emblem region (unhandled). An owner |
| should enable, test, and ideally give it a custom layout: |
| { 11, 16, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, true }, MPG273CQR |
| { 13, 21, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, true }, MPG343CQR |
| { 15, 18, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, true }, MPG323CQR |
| |
| Advanced/0x82 - not addable here, need the 0x30 bulk report: |
| version 31, 40 LEDs, MEG342CQD_OLED |
| version 47, 83 LEDs, 4 areas, MEG_X |
\*-----------------------------------------------------------------*/
};
static const MSIMonitor72Layout* MSIMonitorLayout(uint8_t version)
{
for(unsigned int i = 0; i < (sizeof(msi_monitor_72_layouts) / sizeof(msi_monitor_72_layouts[0])); i++)
{
if(msi_monitor_72_layouts[i].version == version)
{
return(&msi_monitor_72_layouts[i]);
}
}
return(nullptr);
}
DetectedControllers DetectMSIMonitorController(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
@@ -23,10 +120,41 @@ DetectedControllers DetectMSIMonitorController(hid_device_info* info, const std:
if(dev)
{
MSIMonitorController* controller = new MSIMonitorController(dev, *info, name);
RGBController_MSIMonitor* rgb_controller = new RGBController_MSIMonitor(controller);
/*-------------------------------------------------*\
| One VID/PID covers several buffer layouts, so ask |
| the MCU which one it speaks. A version in the |
| 0x72 table selects the 168 byte layout and its |
| LED count; anything else keeps the legacy 0x71 |
| path, which is what the older monitors answer to. |
\*-------------------------------------------------*/
MSIMonitorController* controller = new MSIMonitorController(dev, *info, name);
detected_controllers.push_back(rgb_controller);
uint8_t version = controller->GetLayoutVersion();
const MSIMonitor72Layout* layout = MSIMonitorLayout(version);
if(layout != nullptr)
{
detected_controllers.push_back(new RGBController_MSIMonitor72(controller, layout->leds, layout->direction, layout->dual_block));
}
else
{
/*-------------------------------------------------*\
| 0x71 family (78 byte buffer), handled by the |
| legacy controller as a plain line. For an owner |
| who wants to bring one onto this controller: same |
| operative block 12, but the second colour array |
| is at index 50 (not 95) and store-flash at 77. |
| RightToLeft mirroring applies. version, LEDs, |
| direction: |
| 1, 9, RightToLeft (early/generic) |
| 2, 7, LeftToRight (malformed stub, skip) |
| 3, 9, RightToLeft (early/generic) |
| 4, 9, LeftToRight (early/generic) |
| 6, 9, UpToDown MAG270CR |
| 7, 7, LeftToRight MAG251RX |
\*-------------------------------------------------*/
detected_controllers.push_back(new RGBController_MSIMonitor(controller));
}
}
return(detected_controllers);

View File

@@ -9,12 +9,8 @@
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <chrono>
#include <thread>
#include "RGBController_MSIMonitor.h"
using namespace std::chrono_literals;
/**------------------------------------------------------------------*\
@name MSIMonitor
@category Accessory
@@ -112,10 +108,6 @@ RGBController_MSIMonitor::RGBController_MSIMonitor(MSIMonitorController* control
RGBController_MSIMonitor::~RGBController_MSIMonitor()
{
keepalive_thread_run = 0;
keepalive_thread->join();
delete keepalive_thread;
Shutdown();
delete controller;
@@ -145,7 +137,6 @@ void RGBController_MSIMonitor::SetupZones()
void RGBController_MSIMonitor::DeviceUpdateLEDs()
{
last_update_time = std::chrono::steady_clock::now();
controller->Set(modes[active_mode].value, colors, active_mode == 0 ? 0x00 : 0x01);
}

View File

@@ -1,5 +1,5 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| RGBController_MSIMonitor.h |
| |
| RGBController for MSI monitor (gaming controller) |
| |
@@ -11,7 +11,6 @@
#pragma once
#include <chrono>
#include "RGBController.h"
#include "MSIMonitorController.h"
@@ -31,9 +30,4 @@ public:
private:
MSIMonitorController* controller;
std::thread* keepalive_thread;
std::atomic<bool> keepalive_thread_run;
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
void KeepaliveThread();
};

View File

@@ -0,0 +1,570 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor72.cpp |
| |
| RGBController for MSI monitors using the 0x72 dual |
| control block layout (MPG 322URX QD-OLED) |
| |
| Ken Sanislo 19 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <algorithm>
#include "RGBController_MSIMonitor72.h"
/**------------------------------------------------------------------*\
@name MSIMonitor72
@category Accessory
@type USB
@save :robot:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectMSIMonitorController
@comment Developed with MSI MPG 322URX QD-OLED
\*-------------------------------------------------------------------*/
RGBController_MSIMonitor72::RGBController_MSIMonitor72(MSIMonitorController* controller_ptr, unsigned int count, unsigned int direction, bool dual)
{
controller = controller_ptr;
led_count = count;
reverse_order = (direction == MSI_MONITOR_72_DIR_RIGHT_TO_LEFT);
dual_block = dual;
name = controller->GetNameString();
vendor = "MSI";
type = DEVICE_TYPE_MONITOR;
description = "MSI Monitor (Gaming Controller)";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
/*-----------------------------------------------------*\
| This layout has no Advanced mode, so Direct is Static |
| driven from the inline per LED colour arrays. |
\*-----------------------------------------------------*/
mode Direct;
Direct.name = "Direct";
Direct.value = MSI_MONITOR_72_MODE_STATIC;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Direct.brightness_min = 0;
Direct.brightness_max = 100;
Direct.brightness = 100;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = MSI_MONITOR_72_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Static.colors_min = 1;
Static.colors_max = 1;
Static.colors.resize(1);
Static.brightness_min = 0;
Static.brightness_max = 100;
Static.brightness = 100;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Static);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = MSI_MONITOR_72_MODE_BREATHING;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.colors.resize(1);
Breathing.speed_min = MSI_MONITOR_72_SPEED_LOW;
Breathing.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Breathing.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Breathing.brightness_min = 0;
Breathing.brightness_max = 100;
Breathing.brightness = 100;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Breathing);
mode Flashing;
Flashing.name = "Flashing";
Flashing.value = MSI_MONITOR_72_MODE_FLASHING;
Flashing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Flashing.colors_min = 2;
Flashing.colors_max = 2;
Flashing.colors.resize(2);
Flashing.speed_min = MSI_MONITOR_72_SPEED_LOW;
Flashing.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Flashing.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Flashing.brightness_min = 0;
Flashing.brightness_max = 100;
Flashing.brightness = 100;
Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Flashing);
mode DoubleFlashing;
DoubleFlashing.name = "Double Flashing";
DoubleFlashing.value = MSI_MONITOR_72_MODE_DOUBLE_FLASHING;
DoubleFlashing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
DoubleFlashing.colors_min = 2;
DoubleFlashing.colors_max = 2;
DoubleFlashing.colors.resize(2);
DoubleFlashing.speed_min = MSI_MONITOR_72_SPEED_LOW;
DoubleFlashing.speed_max = MSI_MONITOR_72_SPEED_HIGH;
DoubleFlashing.speed = MSI_MONITOR_72_SPEED_MEDIUM;
DoubleFlashing.brightness_min = 0;
DoubleFlashing.brightness_max = 100;
DoubleFlashing.brightness = 100;
DoubleFlashing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(DoubleFlashing);
mode Lightning;
Lightning.name = "Lightning";
Lightning.value = MSI_MONITOR_72_MODE_LIGHTNING;
Lightning.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Lightning.colors_min = 1;
Lightning.colors_max = 1;
Lightning.colors.resize(1);
Lightning.speed_min = MSI_MONITOR_72_SPEED_LOW;
Lightning.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Lightning.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Lightning.brightness_min = 0;
Lightning.brightness_max = 100;
Lightning.brightness = 100;
Lightning.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Lightning);
mode Scanner;
Scanner.name = "Scanner";
Scanner.value = MSI_MONITOR_72_MODE_SCANNER;
Scanner.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Scanner.colors_min = 1;
Scanner.colors_max = 1;
Scanner.colors.resize(1);
Scanner.speed_min = MSI_MONITOR_72_SPEED_LOW;
Scanner.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Scanner.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Scanner.brightness_min = 0;
Scanner.brightness_max = 100;
Scanner.brightness = 100;
Scanner.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Scanner);
mode Meteor;
Meteor.name = "Meteor";
Meteor.value = MSI_MONITOR_72_MODE_METEOR;
Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Meteor.colors_min = 1;
Meteor.colors_max = 1;
Meteor.colors.resize(1);
Meteor.speed_min = MSI_MONITOR_72_SPEED_LOW;
Meteor.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Meteor.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Meteor.brightness_min = 0;
Meteor.brightness_max = 100;
Meteor.brightness = 100;
Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Meteor);
mode WaterDrop;
WaterDrop.name = "Water Drop";
WaterDrop.value = MSI_MONITOR_72_MODE_WATER_DROP;
WaterDrop.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
WaterDrop.colors_min = 1;
WaterDrop.colors_max = 1;
WaterDrop.colors.resize(1);
WaterDrop.speed_min = MSI_MONITOR_72_SPEED_LOW;
WaterDrop.speed_max = MSI_MONITOR_72_SPEED_HIGH;
WaterDrop.speed = MSI_MONITOR_72_SPEED_MEDIUM;
WaterDrop.brightness_min = 0;
WaterDrop.brightness_max = 100;
WaterDrop.brightness = 100;
WaterDrop.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(WaterDrop);
mode RainbowMSI;
RainbowMSI.name = "Rainbow";
RainbowMSI.value = MSI_MONITOR_72_MODE_RAINBOW_MSI;
RainbowMSI.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
RainbowMSI.speed_min = MSI_MONITOR_72_SPEED_LOW;
RainbowMSI.speed_max = MSI_MONITOR_72_SPEED_HIGH;
RainbowMSI.speed = MSI_MONITOR_72_SPEED_MEDIUM;
RainbowMSI.brightness_min = 0;
RainbowMSI.brightness_max = 100;
RainbowMSI.brightness = 100;
RainbowMSI.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowMSI);
mode ColorRing;
ColorRing.name = "Color Ring";
ColorRing.value = MSI_MONITOR_72_MODE_COLOR_RING;
ColorRing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
ColorRing.colors_min = 1;
ColorRing.colors_max = 1;
ColorRing.colors.resize(1);
ColorRing.speed_min = MSI_MONITOR_72_SPEED_LOW;
ColorRing.speed_max = MSI_MONITOR_72_SPEED_HIGH;
ColorRing.speed = MSI_MONITOR_72_SPEED_MEDIUM;
ColorRing.brightness_min = 0;
ColorRing.brightness_max = 100;
ColorRing.brightness = 100;
ColorRing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(ColorRing);
mode Planetary;
Planetary.name = "Planetary";
Planetary.value = MSI_MONITOR_72_MODE_PLANETARY;
Planetary.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Planetary.colors_min = 1;
Planetary.colors_max = 1;
Planetary.colors.resize(1);
Planetary.speed_min = MSI_MONITOR_72_SPEED_LOW;
Planetary.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Planetary.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Planetary.brightness_min = 0;
Planetary.brightness_max = 100;
Planetary.brightness = 100;
Planetary.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Planetary);
mode DoubleMeteor;
DoubleMeteor.name = "Double Meteor";
DoubleMeteor.value = MSI_MONITOR_72_MODE_DOUBLE_METEOR;
DoubleMeteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
DoubleMeteor.colors_min = 1;
DoubleMeteor.colors_max = 1;
DoubleMeteor.colors.resize(1);
DoubleMeteor.speed_min = MSI_MONITOR_72_SPEED_LOW;
DoubleMeteor.speed_max = MSI_MONITOR_72_SPEED_HIGH;
DoubleMeteor.speed = MSI_MONITOR_72_SPEED_MEDIUM;
DoubleMeteor.brightness_min = 0;
DoubleMeteor.brightness_max = 100;
DoubleMeteor.brightness = 100;
DoubleMeteor.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(DoubleMeteor);
mode Energy;
Energy.name = "Energy";
Energy.value = MSI_MONITOR_72_MODE_ENERGY;
Energy.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Energy.colors_min = 1;
Energy.colors_max = 1;
Energy.colors.resize(1);
Energy.speed_min = MSI_MONITOR_72_SPEED_LOW;
Energy.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Energy.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Energy.brightness_min = 0;
Energy.brightness_max = 100;
Energy.brightness = 100;
Energy.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Energy);
mode Blink;
Blink.name = "Blink";
Blink.value = MSI_MONITOR_72_MODE_BLINK;
Blink.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Blink.colors_min = 1;
Blink.colors_max = 1;
Blink.colors.resize(1);
Blink.speed_min = MSI_MONITOR_72_SPEED_LOW;
Blink.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Blink.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Blink.brightness_min = 0;
Blink.brightness_max = 100;
Blink.brightness = 100;
Blink.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Blink);
mode Clock;
Clock.name = "Clock";
Clock.value = MSI_MONITOR_72_MODE_CLOCK;
Clock.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Clock.colors_min = 1;
Clock.colors_max = 1;
Clock.colors.resize(1);
Clock.speed_min = MSI_MONITOR_72_SPEED_LOW;
Clock.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Clock.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Clock.brightness_min = 0;
Clock.brightness_max = 100;
Clock.brightness = 100;
Clock.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Clock);
mode ColorPulse;
ColorPulse.name = "Color Pulse";
ColorPulse.value = MSI_MONITOR_72_MODE_COLOR_PULSE;
ColorPulse.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
ColorPulse.colors_min = 1;
ColorPulse.colors_max = 1;
ColorPulse.colors.resize(1);
ColorPulse.speed_min = MSI_MONITOR_72_SPEED_LOW;
ColorPulse.speed_max = MSI_MONITOR_72_SPEED_HIGH;
ColorPulse.speed = MSI_MONITOR_72_SPEED_MEDIUM;
ColorPulse.brightness_min = 0;
ColorPulse.brightness_max = 100;
ColorPulse.brightness = 100;
ColorPulse.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(ColorPulse);
mode ColorShift;
ColorShift.name = "Color Shift";
ColorShift.value = MSI_MONITOR_72_MODE_COLOR_SHIFT;
ColorShift.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
ColorShift.colors_min = 2;
ColorShift.colors_max = 2;
ColorShift.colors.resize(2);
ColorShift.speed_min = MSI_MONITOR_72_SPEED_LOW;
ColorShift.speed_max = MSI_MONITOR_72_SPEED_HIGH;
ColorShift.speed = MSI_MONITOR_72_SPEED_MEDIUM;
ColorShift.brightness_min = 0;
ColorShift.brightness_max = 100;
ColorShift.brightness = 100;
ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(ColorShift);
mode ColorWave;
ColorWave.name = "Color Wave";
ColorWave.value = MSI_MONITOR_72_MODE_COLOR_WAVE;
ColorWave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
ColorWave.colors_min = 2;
ColorWave.colors_max = 2;
ColorWave.colors.resize(2);
ColorWave.speed_min = MSI_MONITOR_72_SPEED_LOW;
ColorWave.speed_max = MSI_MONITOR_72_SPEED_HIGH;
ColorWave.speed = MSI_MONITOR_72_SPEED_MEDIUM;
ColorWave.brightness_min = 0;
ColorWave.brightness_max = 100;
ColorWave.brightness = 100;
ColorWave.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(ColorWave);
/*-----------------------------------------------------*\
| Mode 24 marquee behaviour is model dependent. On the |
| dual block / richer boards it wants the marker bytes |
| plus a per LED array of separate colours (a user |
| defined marquee). The vendor app does not even offer |
| it for version 34, but the firmware still runs it as |
| a one colour rolling band, so we expose that. |
\*-----------------------------------------------------*/
mode Marquee;
Marquee.name = "Marquee";
Marquee.value = MSI_MONITOR_72_MODE_MARQUEE;
Marquee.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Marquee.colors_min = 1;
Marquee.colors_max = 1;
Marquee.colors.resize(1);
Marquee.speed_min = MSI_MONITOR_72_SPEED_LOW;
Marquee.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Marquee.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Marquee.brightness_min = 0;
Marquee.brightness_max = 100;
Marquee.brightness = 100;
Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Marquee);
mode SpectrumCycle;
SpectrumCycle.name = "Spectrum Cycle";
SpectrumCycle.value = MSI_MONITOR_72_MODE_RAINBOW;
SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
SpectrumCycle.speed_min = MSI_MONITOR_72_SPEED_LOW;
SpectrumCycle.speed_max = MSI_MONITOR_72_SPEED_HIGH;
SpectrumCycle.speed = MSI_MONITOR_72_SPEED_MEDIUM;
SpectrumCycle.brightness_min = 0;
SpectrumCycle.brightness_max = 100;
SpectrumCycle.brightness = 100;
SpectrumCycle.color_mode = MODE_COLORS_NONE;
modes.push_back(SpectrumCycle);
mode RainbowWave;
RainbowWave.name = "Rainbow Wave";
RainbowWave.value = MSI_MONITOR_72_MODE_RAINBOW_WAVE;
RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
RainbowWave.speed_min = MSI_MONITOR_72_SPEED_LOW;
RainbowWave.speed_max = MSI_MONITOR_72_SPEED_HIGH;
RainbowWave.speed = MSI_MONITOR_72_SPEED_MEDIUM;
RainbowWave.brightness_min = 0;
RainbowWave.brightness_max = 100;
RainbowWave.brightness = 100;
RainbowWave.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowWave);
mode Visor;
Visor.name = "Visor";
Visor.value = MSI_MONITOR_72_MODE_VISOR;
Visor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Visor.colors_min = 1;
Visor.colors_max = 1;
Visor.colors.resize(1);
Visor.speed_min = MSI_MONITOR_72_SPEED_LOW;
Visor.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Visor.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Visor.brightness_min = 0;
Visor.brightness_max = 100;
Visor.brightness = 100;
Visor.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Visor);
mode JRainbow;
JRainbow.name = "JRainbow";
JRainbow.value = MSI_MONITOR_72_MODE_JRAINBOW;
JRainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
JRainbow.speed_min = MSI_MONITOR_72_SPEED_LOW;
JRainbow.speed_max = MSI_MONITOR_72_SPEED_HIGH;
JRainbow.speed = MSI_MONITOR_72_SPEED_MEDIUM;
JRainbow.brightness_min = 0;
JRainbow.brightness_max = 100;
JRainbow.brightness = 100;
JRainbow.color_mode = MODE_COLORS_NONE;
modes.push_back(JRainbow);
mode RainbowFlash;
RainbowFlash.name = "Rainbow Flashing";
RainbowFlash.value = MSI_MONITOR_72_MODE_RAINBOW_FLASH;
RainbowFlash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
RainbowFlash.speed_min = MSI_MONITOR_72_SPEED_LOW;
RainbowFlash.speed_max = MSI_MONITOR_72_SPEED_HIGH;
RainbowFlash.speed = MSI_MONITOR_72_SPEED_MEDIUM;
RainbowFlash.brightness_min = 0;
RainbowFlash.brightness_max = 100;
RainbowFlash.brightness = 100;
RainbowFlash.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowFlash);
mode RainbowDblFlash;
RainbowDblFlash.name = "Rainbow Double Flashing";
RainbowDblFlash.value = MSI_MONITOR_72_MODE_RAINBOW_DBLFLASH;
RainbowDblFlash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
RainbowDblFlash.speed_min = MSI_MONITOR_72_SPEED_LOW;
RainbowDblFlash.speed_max = MSI_MONITOR_72_SPEED_HIGH;
RainbowDblFlash.speed = MSI_MONITOR_72_SPEED_MEDIUM;
RainbowDblFlash.brightness_min = 0;
RainbowDblFlash.brightness_max = 100;
RainbowDblFlash.brightness = 100;
RainbowDblFlash.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowDblFlash);
mode RainbowLine;
RainbowLine.name = "Rainbow Line";
RainbowLine.value = MSI_MONITOR_72_MODE_RAINBOW_LINE;
RainbowLine.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
RainbowLine.speed_min = MSI_MONITOR_72_SPEED_LOW;
RainbowLine.speed_max = MSI_MONITOR_72_SPEED_HIGH;
RainbowLine.speed = MSI_MONITOR_72_SPEED_MEDIUM;
RainbowLine.brightness_min = 0;
RainbowLine.brightness_max = 100;
RainbowLine.brightness = 100;
RainbowLine.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowLine);
mode Off;
Off.name = "Off";
Off.value = MSI_MONITOR_72_MODE_OFF;
Off.flags = MODE_FLAG_MANUAL_SAVE;
Off.color_mode = MODE_COLORS_NONE;
modes.push_back(Off);
SetupZones();
}
RGBController_MSIMonitor72::~RGBController_MSIMonitor72()
{
Shutdown();
delete controller;
}
void RGBController_MSIMonitor72::SetupZones()
{
zone new_zone;
new_zone.name = "Rear";
new_zone.type = ZONE_TYPE_LINEAR;
new_zone.leds_min = led_count;
new_zone.leds_max = led_count;
new_zone.leds_count = led_count;
zones.emplace_back(new_zone);
for(unsigned int led_idx = 0; led_idx < led_count; led_idx++)
{
led new_led;
new_led.name = "LED " + std::to_string(led_idx + 1);
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_MSIMonitor72::DeviceUpdateLEDs()
{
DeviceUpdateMode();
}
void RGBController_MSIMonitor72::DeviceUpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSIMonitor72::DeviceUpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSIMonitor72::ApplyMode(bool save)
{
const mode& active = modes[active_mode];
RGBColor color1 = 0;
RGBColor color2 = 0;
bool user_palette = false;
std::vector<RGBColor> led_colors;
if(active.color_mode == MODE_COLORS_PER_LED)
{
user_palette = true;
led_colors = colors;
if(led_colors.size() > 0)
{
color1 = led_colors[0];
}
}
else if(active.color_mode == MODE_COLORS_MODE_SPECIFIC)
{
user_palette = true;
if(active.colors.size() > 0)
{
color1 = active.colors[0];
if(active.colors.size() > 1)
{
color2 = active.colors[1];
}
}
led_colors.assign(led_count, color1);
}
/*-----------------------------------------------------*\
| RightToLeft layouts start at the right end, so mirror |
| the per LED order to keep OpenRGB LED 0 on the left. |
| Uniform colour modes are unaffected by the order. |
\*-----------------------------------------------------*/
if(reverse_order)
{
std::reverse(led_colors.begin(), led_colors.end());
}
controller->SetMode72((uint8_t)active.value, (uint8_t)active.speed, (uint8_t)active.brightness, color1, color2, user_palette, save, led_colors, dual_block);
}
void RGBController_MSIMonitor72::DeviceUpdateMode()
{
/*-----------------------------------------------------*\
| Updates are volatile. Persisting to flash is the |
| Save To Device button (DeviceSaveMode), so live edits |
| do not burn flash write cycles. |
\*-----------------------------------------------------*/
ApplyMode(false);
}
void RGBController_MSIMonitor72::DeviceSaveMode()
{
ApplyMode(true);
}

View File

@@ -0,0 +1,40 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor72.h |
| |
| RGBController for MSI monitors using the 0x72 dual |
| control block layout (MPG 322URX QD-OLED) |
| |
| Ken Sanislo 19 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "MSIMonitorController.h"
class RGBController_MSIMonitor72 : public RGBController
{
public:
RGBController_MSIMonitor72(MSIMonitorController* controller_ptr, unsigned int count, unsigned int direction, bool dual);
~RGBController_MSIMonitor72();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
void ApplyMode(bool save);
MSIMonitorController* controller;
unsigned int led_count;
bool reverse_order;
bool dual_block;
};