diff --git a/Controllers/LGMonitorController/LGMonitorController.cpp b/Controllers/LGMonitorController/LGMonitorController.cpp new file mode 100644 index 00000000..1b5cf06d --- /dev/null +++ b/Controllers/LGMonitorController/LGMonitorController.cpp @@ -0,0 +1,248 @@ +/*-----------------------------------------*\ +| LGMonitorController.cpp | +| | +| Driver for LG monitor lighting | +| controller | +| | +| Guimard Morgan (morg) 10/11/2023 | +\*-----------------------------------------*/ +#include "LGMonitorController.h" +#include + +LGMonitorController::LGMonitorController(hid_device* dev_handle, const hid_device_info& info) +{ + dev = dev_handle; + location = info.path; + version = ""; + + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + serial_number = ""; + } + else + { + std::wstring return_wstring = serial_string; + serial_number = std::string(return_wstring.begin(), return_wstring.end()); + } +} + +LGMonitorController::~LGMonitorController() +{ + hid_close(dev); +} + +std::string LGMonitorController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LGMonitorController::GetSerialString() +{ + return(serial_number); +} + +std::string LGMonitorController::GetFirmwareVersion() +{ + return(version); +} + +void LGMonitorController::SetDirect(const std::vector colors) +{ + /*---------------------------------------------------------*\ + | Make sure the device is set to on | + \*---------------------------------------------------------*/ + if(!on) + { + TurnOn(true); + } + + /*---------------------------------------------------------*\ + | Make sure the direct mode is enabled | + \*---------------------------------------------------------*/ + if(!direct_mode_enabled) + { + EnableDirectMode(); + } + + /*---------------------------------------------------------*\ + | Prepare the colors data | + \*---------------------------------------------------------*/ + uint8_t data[192]; + memset(data, 0x00, 192); + + unsigned int offset = 0; + + data[offset++] = LG_MONITOR_START_CMD_1; + data[offset++] = LG_MONITOR_START_CMD_2; + data[offset++] = LG_MONITOR_DIRECT_CTL; + data[offset++] = 0x02; + data[offset++] = 0x91; + data[offset++] = 0x00; + + for(const RGBColor color: colors) + { + data[offset++] = RGBGetRValue(color); + data[offset++] = RGBGetGValue(color); + data[offset++] = RGBGetBValue(color); + } + + data[offset] = crc(data, 0, offset++); + data[offset++] = LG_MONITOR_END_CMD_1; + data[offset] = LG_MONITOR_END_CMD_2; + + /*---------------------------------------------------------*\ + | Send the data (3 packets of 64 bytes) | + \*---------------------------------------------------------*/ + uint8_t buf[LG_MONITOR_PACKET_SIZE]; + memset(buf, 0x00, LG_MONITOR_PACKET_SIZE); + + for(unsigned int i = 0; i < 3; i++) + { + memcpy(&buf[1], &data[64 * i], 64); + hid_write(dev, buf, LG_MONITOR_PACKET_SIZE); + } +} + +void LGMonitorController::SetMode(uint8_t mode_value, uint8_t brightness, const std::vector colors) +{ + switch(mode_value) + { + case LG_MONITOR_OFF_MODE_VALUE: + /*---------------------------------------------------------*\ + | Turn off lighting | + \*---------------------------------------------------------*/ + TurnOn(false); + break; + + case LG_MONITOR_STATIC_SLOT_1_MODE_VALUE: + /*---------------------------------------------------------*\ + | Set slot 1 active | + \*---------------------------------------------------------*/ + EnableMode(LG_MONITOR_STATIC_SLOT_1_MODE_VALUE); + + SetBrightness(brightness); + /*---------------------------------------------------------*\ + | Send color in slot 1 | + \*---------------------------------------------------------*/ + SetSlotColor(LG_MONITOR_STATIC_SLOT_1_MODE_VALUE, colors[0]); + + break; + + case LG_MONITOR_SPECTRUM_CYCLE_MODE_VALUE: + case LG_MONITOR_RAINBOW_MODE_VALUE: + /*---------------------------------------------------------*\ + | Enable given mode | + \*---------------------------------------------------------*/ + EnableMode(mode_value); + SetBrightness(brightness); + break; + + default: + break; + } + + direct_mode_enabled = false; +} + +void LGMonitorController::EnableDirectMode() +{ + EnableMode(LG_MONITOR_DIRECT_MODE_VALUE); + direct_mode_enabled = true; +} + +void LGMonitorController::EnableMode(uint8_t mode_value) +{ + uint8_t buf[LG_MONITOR_PACKET_SIZE]; + memset(buf, 0x00, LG_MONITOR_PACKET_SIZE); + + buf[1] = LG_MONITOR_START_CMD_1; + buf[2] = LG_MONITOR_START_CMD_2; + buf[3] = LG_MONITOR_SET_MODE; + buf[4] = 0x02; + buf[5] = 0x02; + buf[6] = LG_MONITOR_MODE_CTL; + buf[7] = mode_value; + buf[8] = crc(buf, 1, 8); + buf[9] = LG_MONITOR_END_CMD_1; + buf[10] = LG_MONITOR_END_CMD_2; + + hid_write(dev, buf, LG_MONITOR_PACKET_SIZE); +} + +void LGMonitorController::SetBrightness(uint8_t brightness) +{ + uint8_t buf[LG_MONITOR_PACKET_SIZE]; + memset(buf, 0x00, LG_MONITOR_PACKET_SIZE); + + buf[1] = LG_MONITOR_START_CMD_1; + buf[2] = LG_MONITOR_START_CMD_2; + buf[3] = LG_MONITOR_SET_POWER_STATE; + buf[4] = 0x02; + buf[5] = 0x02; + buf[6] = LG_MONITOR_BRIGHTNESS_CTL; + buf[7] = brightness; + buf[8] = crc(buf, 1, 8); + buf[9] = LG_MONITOR_END_CMD_1; + buf[10] = LG_MONITOR_END_CMD_2; + + hid_write(dev, buf, LG_MONITOR_PACKET_SIZE); +} + +void LGMonitorController::TurnOn(bool value) +{ + uint8_t buf[LG_MONITOR_PACKET_SIZE]; + memset(buf, 0x00, LG_MONITOR_PACKET_SIZE); + + buf[1] = LG_MONITOR_START_CMD_1; + buf[2] = LG_MONITOR_START_CMD_2; + buf[3] = LG_MONITOR_SET_POWER_STATE; + buf[4] = 0x02; + buf[5] = 0x02; + buf[6] = value ? LG_MONITOR_POWER_ON : LG_MONITOR_POWER_OFF; + buf[8] = crc(buf, 1, 8); + buf[9] = LG_MONITOR_END_CMD_1; + buf[10] = LG_MONITOR_END_CMD_2; + + hid_write(dev, buf, LG_MONITOR_PACKET_SIZE); + + on = value; +} + +void LGMonitorController::SetSlotColor(uint8_t slot, const RGBColor color) +{ + uint8_t buf[LG_MONITOR_PACKET_SIZE]; + memset(buf, 0x00, LG_MONITOR_PACKET_SIZE); + + buf[1] = LG_MONITOR_START_CMD_1; + buf[2] = LG_MONITOR_START_CMD_2; + buf[3] = LG_MONITOR_SET_COLOR; + + buf[4] = 0x02; + buf[5] = 0x04; + buf[6] = slot; + + buf[7] = RGBGetRValue(color); + buf[8] = RGBGetGValue(color); + buf[9] = RGBGetBValue(color); + + buf[10] = crc(buf, 1, 10); + buf[11] = LG_MONITOR_END_CMD_1; + buf[12] = LG_MONITOR_END_CMD_2; + + hid_write(dev, buf, LG_MONITOR_PACKET_SIZE); +} + +uint8_t LGMonitorController::crc(const uint8_t data[], uint8_t start, uint8_t end) +{ + uint8_t crc = 0; + + for(unsigned int i = start; i < end; i++) + { + crc = crc ^ data[i]; + } + + return crc; +} diff --git a/Controllers/LGMonitorController/LGMonitorController.h b/Controllers/LGMonitorController/LGMonitorController.h new file mode 100644 index 00000000..9e9591a6 --- /dev/null +++ b/Controllers/LGMonitorController/LGMonitorController.h @@ -0,0 +1,69 @@ +/*-----------------------------------------*\ +| LGMonitorController.h | +| | +| Driver for LG monitor lighting | +| controller - header file | +| | +| Guimard Morgan (morg) 10/11/2023 | +\*-----------------------------------------*/ +#pragma once + +#include +#include +#include "RGBController.h" + +#define LG_MONITOR_LEDS 48 +#define LG_MONITOR_PACKET_SIZE 65 +#define LG_MONITOR_READ_CMD 0x52 // R (Read) +#define LG_MONITOR_START_CMD_1 0x53 // S (Send) +#define LG_MONITOR_START_CMD_2 0x43 // C (Command) +#define LG_MONITOR_END_CMD_1 0x45 // E (End) +#define LG_MONITOR_END_CMD_2 0x44 // D (Data) +#define LG_MONITOR_READ_POWER_STATE 0xCE +#define LG_MONITOR_SET_POWER_STATE 0xCF +#define LG_MONITOR_SET_COLOR 0xCD +#define LG_MONITOR_SET_MODE 0xCA +#define LG_MONITOR_DIRECT_CTL 0xC1 +#define LG_MONITOR_POWER_ON 0x01 +#define LG_MONITOR_POWER_OFF 0x02 +#define LG_MONITOR_MODE_CTL 0x03 +#define LG_MONITOR_BRIGHTNESS_CTL 0x01 + +enum +{ + LG_MONITOR_DIRECT_MODE_VALUE = 0x08, + LG_MONITOR_STATIC_SLOT_1_MODE_VALUE = 0x01, + LG_MONITOR_SPECTRUM_CYCLE_MODE_VALUE = 0x05, + LG_MONITOR_RAINBOW_MODE_VALUE = 0x06, + LG_MONITOR_OFF_MODE_VALUE = 0x00 +}; + +class LGMonitorController +{ +public: + LGMonitorController(hid_device* dev_handle, const hid_device_info& info); + ~LGMonitorController(); + + std::string GetSerialString(); + std::string GetDeviceLocation(); + std::string GetFirmwareVersion(); + + void SetDirect(const std::vector colors); + void SetMode(uint8_t mode_value, uint8_t brightness, const std::vector colors); + +private: + hid_device* dev; + std::string description; + std::string location; + std::string version; + std::string serial_number; + bool on = false; + bool direct_mode_enabled = false; + + static uint8_t crc(const uint8_t data[], uint8_t start, uint8_t end); + void SetBrightness(uint8_t value); + void TurnOn(bool value); + void EnableDirectMode(); + void EnableMode(uint8_t mode_value); + void SetSlotColor(uint8_t slot, const RGBColor color); +}; diff --git a/Controllers/LGMonitorController/LGMonitorControllerDetect.cpp b/Controllers/LGMonitorController/LGMonitorControllerDetect.cpp new file mode 100644 index 00000000..cf78e764 --- /dev/null +++ b/Controllers/LGMonitorController/LGMonitorControllerDetect.cpp @@ -0,0 +1,37 @@ +#include "Detector.h" +#include "LGMonitorController.h" +#include "RGBController.h" +#include "RGBController_LGMonitor.h" +#include "dependencies/dmiinfo.h" + +/*---------------------------------------------------------*\ +| vendor ID | +\*---------------------------------------------------------*/ +#define LG_MONITOR_VID 0x043E + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define LG_27GN950_B_PID 0x9A8A +#define LG_38GL950G_PID 0x9A57 + +void DetectLGMonitorControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + DMIInfo dmi; + + LGMonitorController* controller = new LGMonitorController(dev, *info); + RGBController_LGMonitor* rgb_controller = new RGBController_LGMonitor(controller); + rgb_controller->name = name; + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("LG 27GN950-B Monitor", DetectLGMonitorControllers, LG_MONITOR_VID, LG_27GN950_B_PID, 1, 0xFF01, 0x01); + +// Untested +//REGISTER_HID_DETECTOR("LG 38GL950G Monitor", DetectLGMonitorControllers, LG_MONITOR_VID, LG_38GL950G_PID); diff --git a/Controllers/LGMonitorController/RGBController_LGMonitor.cpp b/Controllers/LGMonitorController/RGBController_LGMonitor.cpp new file mode 100644 index 00000000..314d6839 --- /dev/null +++ b/Controllers/LGMonitorController/RGBController_LGMonitor.cpp @@ -0,0 +1,161 @@ +/*-----------------------------------------*\ +| RGBController_LGMonitor.cpp | +| | +| Generic RGB Interface for OpenRGB | +| LG monitor RGB USB Driver | +| | +| Guimard Morgan (morg) 10/11/2023 | +\*-----------------------------------------*/ + +#include "RGBController_LGMonitor.h" + +#include +#include + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name LGMonitor + @category Monitor + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLGMonitorControllers + @comment +\*-------------------------------------------------------------------*/ +RGBController_LGMonitor::RGBController_LGMonitor(LGMonitorController* controller_ptr) +{ + controller = controller_ptr; + vendor = "LG"; + type = DEVICE_TYPE_LEDSTRIP; + description = "LG Monitor"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LG_MONITOR_DIRECT_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LG_MONITOR_STATIC_SLOT_1_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness_min = 1; + Static.brightness_max = 12; + Static.brightness = 12; + modes.push_back(Static); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = LG_MONITOR_SPECTRUM_CYCLE_MODE_VALUE; + SpectrumCycle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = 1; + SpectrumCycle.brightness_max = 12; + SpectrumCycle.brightness = 12; + modes.push_back(SpectrumCycle); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = LG_MONITOR_RAINBOW_MODE_VALUE; + RainbowWave.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.brightness_min = 1; + RainbowWave.brightness_max = 12; + RainbowWave.brightness = 12; + modes.push_back(RainbowWave); + + mode Off; + Off.name = "Off"; + Off.value = LG_MONITOR_OFF_MODE_VALUE; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_LGMonitor::KeepaliveThread, this); +} + +RGBController_LGMonitor::~RGBController_LGMonitor() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_LGMonitor::SetupZones() +{ + zone new_zone; + + new_zone.name = "Screen"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 48; + new_zone.leds_max = 48; + new_zone.leds_count = 48; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + for(unsigned int i = 0 ; i < 48; i ++) + { + led new_led; + new_led.name = "LED " + std::to_string(i + 1); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_LGMonitor::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LGMonitor::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + controller->SetDirect(colors); +} + +void RGBController_LGMonitor::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LGMonitor::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LGMonitor::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].colors); +} + +void RGBController_LGMonitor::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((modes[active_mode].value == LG_MONITOR_DIRECT_MODE_VALUE) && (std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(500)) + { + UpdateLEDs(); + } + + std::this_thread::sleep_for(15ms); + } +} diff --git a/Controllers/LGMonitorController/RGBController_LGMonitor.h b/Controllers/LGMonitorController/RGBController_LGMonitor.h new file mode 100644 index 00000000..12f418a9 --- /dev/null +++ b/Controllers/LGMonitorController/RGBController_LGMonitor.h @@ -0,0 +1,36 @@ +/*-----------------------------------------*\ +| RGBController_LGMonitor.h | +| | +| Generic RGB Interface for OpenRGB | +| LG monitor RGB USB Driver | +| | +| Guimard Morgan (morg) 10/11/2023 | +\*-----------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LGMonitorController.h" +#include + +class RGBController_LGMonitor : public RGBController +{ +public: + RGBController_LGMonitor(LGMonitorController* controller_ptr); + ~RGBController_LGMonitor(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + +private: + LGMonitorController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; + + void KeepaliveThread(); +}; diff --git a/OpenRGB.pro b/OpenRGB.pro index 493acca6..f41aa9a2 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -182,6 +182,7 @@ INCLUDEPATH += Controllers/LegoDimensionsToypadBaseController/ \ Controllers/LenovoControllers/ \ Controllers/LenovoMotherboardController/ \ + Controllers/LGMonitorController/ \ Controllers/LianLiController/ \ Controllers/LIFXController/ \ Controllers/LogitechController/ \ @@ -587,6 +588,8 @@ HEADERS += Controllers/LenovoMotherboardController/RGBController_LenovoMotherboard.h \ Controllers/LexipMouseController/LexipMouseController.h \ Controllers/LexipMouseController/RGBController_LexipMouse.h \ + Controllers/LGMonitorController/LGMonitorController.h \ + Controllers/LGMonitorController/RGBController_LGMonitor.h \ Controllers/LIFXController/LIFXController.h \ Controllers/LIFXController/RGBController_LIFX.h \ Controllers/LianLiController/LianLiUniHubController.h \ @@ -1267,6 +1270,9 @@ SOURCES += Controllers/LexipMouseController/LexipMouseController.cpp \ Controllers/LexipMouseController/LexipMouseControllerDetect.cpp \ Controllers/LexipMouseController/RGBController_LexipMouse.cpp \ + Controllers/LGMonitorController/LGMonitorController.cpp \ + Controllers/LGMonitorController/LGMonitorControllerDetect.cpp \ + Controllers/LGMonitorController/RGBController_LGMonitor.cpp \ Controllers/LIFXController/LIFXController.cpp \ Controllers/LIFXController/LIFXControllerDetect.cpp \ Controllers/LIFXController/RGBController_LIFX.cpp \