diff --git a/Controllers/QMKOpenRGBController/QMKOpenRGBController.cpp b/Controllers/QMKOpenRGBController/QMKOpenRGBController.cpp new file mode 100644 index 00000000..879629af --- /dev/null +++ b/Controllers/QMKOpenRGBController/QMKOpenRGBController.cpp @@ -0,0 +1,454 @@ +/*-------------------------------------------------------------------*\ +| QMKOpenRGBController.cpp | +| | +| Driver for QMK keyboards using OpenRGB Protocol | +| | +| Kasper 10th Octobber 2020 | +| Jath03 28th May 2021 | +\*-------------------------------------------------------------------*/ + +#include "QMKOpenRGBController.h" + +static std::map QMKKeycodeToKeynameMap +{ + { 0, "" }, { 1, "Right Fn" }, { 2, "" }, { 3, "" }, + { 4, "A" }, { 5, "B" }, { 6, "C" }, { 7, "D" }, + { 8, "E" }, { 9, "F" }, { 10, "G" }, { 11, "H" }, + { 12, "I" }, { 13, "J" }, { 14, "K" }, { 15, "L" }, + { 16, "M" }, { 17, "N" }, { 18, "O" }, { 19, "P" }, + { 20, "Q" }, { 21, "R" }, { 22, "S" }, { 23, "T" }, + { 24, "U" }, { 25, "V" }, { 26, "W" }, { 27, "X" }, + { 28, "Y" }, { 29, "Z" }, { 30, "1" }, { 31, "2" }, + { 32, "3" }, { 33, "4" }, { 34, "5" }, { 35, "6" }, + { 36, "7" }, { 37, "8" }, { 38, "9" }, { 39, "0" }, + { 40, "Enter" }, { 41, "Escape" }, { 42, "Backspace" }, { 43, "Tab" }, + { 44, "Space" }, { 45, "-" }, { 46, "=" }, { 47, "[" }, + { 48, "]" }, { 49, "\\ (ANSI)" }, { 50, "" }, { 51, ";" }, + { 52, "'" }, { 53, "`" }, { 54, "," }, { 55, "." }, + { 56, "/" }, { 57, "Caps Lock" }, { 58, "F1" }, { 59, "F2" }, + { 60, "F3" }, { 61, "F4" }, { 62, "F5" }, { 63, "F6" }, + { 64, "F7" }, { 65, "F8" }, { 66, "F9" }, { 67, "F10" }, + { 68, "F11" }, { 69, "F12" }, { 70, "Print Screen" }, { 71, "Scroll Lock" }, + { 72, "Pause/Break" }, { 73, "Insert" }, { 74, "Home" }, { 75, "Page Up" }, + { 76, "Delete" }, { 77, "End" }, { 78, "Page Down" }, { 79, "Right Arrow" }, + { 80, "Left Arrow" }, { 81, "Down Arrow" }, { 82, "Up Arrow" }, { 83, "Num Lock" }, + { 84, "Number Pad /" }, { 85, "Number Pad *" }, { 86, "Number Pad -" }, { 87, "Number Pad +" }, + { 88, "Number Pad Enter" }, { 89, "Number Pad 1" }, { 90, "Number Pad 2" }, { 91, "Number Pad 3" }, + { 92, "Number Pad 4" }, { 93, "Number Pad 5" }, { 94, "Number Pad 6" }, { 95, "Number Pad 7" }, + { 96, "Number Pad 8" }, { 97, "Number Pad 9" }, { 98, "Number Pad 0" }, { 99, "Number Pad ." }, + { 100, "" }, { 101, "Menu" }, { 102, "" }, { 103, "" }, + { 104, "" }, { 105, "" }, { 106, "" }, { 107, "" }, + { 108, "" }, { 109, "" }, { 110, "" }, { 111, "" }, + { 112, "" }, { 113, "" }, { 114, "" }, { 115, "" }, + { 116, "" }, { 117, "" }, { 118, "" }, { 119, "" }, + { 120, "" }, { 121, "" }, { 122, "" }, { 123, "" }, + { 124, "" }, { 125, "" }, { 126, "" }, { 127, "" }, + { 128, "" }, { 129, "" }, { 130, "" }, { 131, "" }, + { 132, "" }, { 133, "" }, { 134, "" }, { 135, "" }, + { 136, "" }, { 137, "" }, { 138, "" }, { 139, "" }, + { 140, "" }, { 141, "" }, { 142, "" }, { 143, "" }, + { 144, "" }, { 145, "" }, { 146, "" }, { 147, "" }, + { 148, "" }, { 149, "" }, { 150, "" }, { 151, "" }, + { 152, "" }, { 153, "" }, { 154, "" }, { 155, "" }, + { 156, "" }, { 157, "" }, { 158, "" }, { 159, "" }, + { 160, "" }, { 161, "" }, { 162, "" }, { 163, "" }, { 164, "" }, + /*Space Cadet Left Shift*/ { 216, "Left Shift"}, /*Space Cadet Right Shift*/ { 217, "Right Shift"}, + { 224, "Left Control" }, { 225, "Left Shift" }, { 226, "Left Alt" }, { 227, "Left Windows" }, + { 228, "Right Control" }, { 229, "Right Shift" }, { 230, "Right Alt" }, { 231, "Right Windows" }, +}; + +QMKOpenRGBController::QMKOpenRGBController(hid_device *dev_handle, const char *path) +{ + /*-------------------------------------------------*\ + | Get QMKOpenRGB settings | + \*-------------------------------------------------*/ + json qmk_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("QMKOpenRGBDevices"); + if(qmk_settings.contains("leds_per_update")) + { + if(qmk_settings["leds_per_update"] > 20) + { + qmk_settings["leds_per_update"] = 20; + } + else if(qmk_settings["leds_per_update"] < 1) + { + qmk_settings["leds_per_update"] = 1; + } + SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager(); + settings_manager->SetSettings("QMKOpenRGBDevices", qmk_settings); + settings_manager->SaveSettings(); + leds_per_update = qmk_settings["leds_per_update"]; + } + else + { + leds_per_update = 20; + } + + dev = dev_handle; + location = path; + + GetDeviceInfo(); + GetModeInfo(); +} + +QMKOpenRGBController::~QMKOpenRGBController() +{ + hid_close(dev); +} + +std::string QMKOpenRGBController::GetLocation() +{ + return location; +} + +std::string QMKOpenRGBController::GetDeviceName() +{ + return device_name; +} + +std::string QMKOpenRGBController::GetDeviceVendor() +{ + return device_vendor; +} + +unsigned int QMKOpenRGBController::GetTotalNumberOfLEDs() +{ + return total_number_of_leds; +} + +unsigned int QMKOpenRGBController::GetTotalNumberOfLEDsWithEmptySpace() +{ + return total_number_of_leds_with_empty_space; +} + +unsigned int QMKOpenRGBController::GetMode() +{ + return mode; +} + +unsigned int QMKOpenRGBController::GetModeSpeed() +{ + return mode_speed; +} + +unsigned int QMKOpenRGBController::GetModeColor() +{ + return mode_color; +} + +std::vector QMKOpenRGBController::GetLEDPoints() +{ + return led_points; +} + +std::vector QMKOpenRGBController::GetLEDFlags() +{ + return led_flags; +} + +std::vector QMKOpenRGBController::GetLEDNames() +{ + return led_names; +} + +std::vector QMKOpenRGBController::GetLEDColors() +{ + return led_colors; +} + +unsigned int QMKOpenRGBController::GetProtocolVersion() +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_PROTOCOL_VERSION; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + return usb_buf[1]; +} + +std::string QMKOpenRGBController::GetQMKVersion() +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_QMK_VERSION; + + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + hid_read(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + + std::string qmk_version; + int i = 1; + while (usb_buf[i] != 0) + { + qmk_version.push_back(usb_buf[i]); + i++; + } + + return qmk_version; +} + +void QMKOpenRGBController::GetDeviceInfo() +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_DEVICE_INFO; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + total_number_of_leds = usb_buf[QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_BYTE]; + total_number_of_leds_with_empty_space = usb_buf[QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE]; + + int i = QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE + 1; + while (usb_buf[i] != 0) + { + device_name.push_back(usb_buf[i]); + i++; + } + + i++; + while (usb_buf[i] != 0) + { + device_vendor.push_back(usb_buf[i]); + i++; + } +} + +void QMKOpenRGBController::GetModeInfo() +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_MODE_INFO; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, 65); + bytes_read = hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + mode = usb_buf[QMK_OPENRGB_MODE_BYTE]; + mode_speed = usb_buf[QMK_OPENRGB_SPEED_BYTE]; + + /*-----------------------------------------------------*\ + | QMK hue range is between 0-255 so hue needs to be | + | converted | + \*-----------------------------------------------------*/ + unsigned int oldRange = 255; + unsigned int newRange = 359; + unsigned int convertedHue = (usb_buf[QMK_OPENRGB_HUE_BYTE] * newRange / oldRange); + + hsv_t hsv; + hsv.hue = convertedHue; + hsv.saturation = usb_buf[QMK_OPENRGB_SATURATION_BYTE]; + hsv.value = usb_buf[QMK_OPENRGB_VALUE_BYTE]; + + mode_color = hsv2rgb(&hsv); +} + +void QMKOpenRGBController::GetLEDInfo(unsigned int led) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_LED_INFO; + usb_buf[0x02] = led; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + if(usb_buf[62] != QMK_OPENRGB_FAILURE) + { + led_points.push_back(point_t{usb_buf[QMK_OPENRGB_POINT_X_BYTE], usb_buf[QMK_OPENRGB_POINT_Y_BYTE]}); + led_flags.push_back(usb_buf[QMK_OPENRGB_FLAG_BYTE]); + led_colors.push_back(ToRGBColor(usb_buf[QMK_OPENRGB_R_COLOR_BYTE], usb_buf[QMK_OPENRGB_G_COLOR_BYTE], usb_buf[QMK_OPENRGB_B_COLOR_BYTE])); + } + + if(usb_buf[QMK_OPENRGB_KEYCODE_BYTE] != 0) + { + if (QMKKeycodeToKeynameMap.count(usb_buf[QMK_OPENRGB_KEYCODE_BYTE]) > 0) + { + led_names.push_back("Key: " + QMKKeycodeToKeynameMap[usb_buf[QMK_OPENRGB_KEYCODE_BYTE]]); + } + else + { + led_names.push_back("Key: "); + } + } +} + +bool QMKOpenRGBController::GetIsModeEnabled(unsigned int mode) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_IS_MODE_ENABLED; + usb_buf[0x02] = mode; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + return usb_buf[1] == QMK_OPENRGB_SUCCESS ? true : false; +} + +void QMKOpenRGBController::SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_SET_MODE; + usb_buf[0x02] = hsv_color.hue * 255 / 359; + usb_buf[0x03] = hsv_color.saturation; + usb_buf[0x04] = hsv_color.value; + usb_buf[0x05] = mode; + usb_buf[0x06] = speed; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT); +} + +void QMKOpenRGBController::DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED; + usb_buf[0x02] = led; + usb_buf[0x03] = red; + usb_buf[0x04] = green; + usb_buf[0x05] = blue; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT); +} + +void QMKOpenRGBController::DirectModeSetLEDs(std::vector colors, unsigned int leds_count) +{ + unsigned int leds_sent = 0; + unsigned int tmp_leds_per_update = leds_per_update; + + while (leds_sent < leds_count) + { + if ((leds_count - leds_sent) < tmp_leds_per_update) + { + tmp_leds_per_update = leds_count - leds_sent; + } + + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_LEDS; + usb_buf[0x02] = leds_sent; + usb_buf[0x03] = tmp_leds_per_update; + + for (unsigned int led_idx = 0; led_idx < tmp_leds_per_update; led_idx++) + { + usb_buf[(led_idx * 3) + 4] = RGBGetRValue(colors[led_idx + leds_sent]); + usb_buf[(led_idx * 3) + 5] = RGBGetGValue(colors[led_idx + leds_sent]); + usb_buf[(led_idx * 3) + 6] = RGBGetBValue(colors[led_idx + leds_sent]); + } + + hid_write(dev, usb_buf, 65); + + leds_sent += tmp_leds_per_update; + } +} diff --git a/Controllers/QMKOpenRGBController/QMKOpenRGBController.h b/Controllers/QMKOpenRGBController/QMKOpenRGBController.h new file mode 100644 index 00000000..0de6ee38 --- /dev/null +++ b/Controllers/QMKOpenRGBController/QMKOpenRGBController.h @@ -0,0 +1,182 @@ +/*-------------------------------------------------------------------*\ +| QMKOpenRGBController.h | +| | +| Driver for QMK keyboards using OpenRGB Protocol | +| | +| Kasper 10th Octobber 2020 | +| Jath03 28th May 2021 | +\*-------------------------------------------------------------------*/ + +#pragma once + +#include "ResourceManager.h" +#include "RGBController.h" +#include "hsv.h" +#include +#include +#include + +#define QMK_OPENRGB_PACKET_SIZE 65 +#define QMK_OPENRGB_HID_READ_TIMEOUT 50 + +enum CommandsId +{ + QMK_OPENRGB_GET_PROTOCOL_VERSION = 1, + QMK_OPENRGB_GET_QMK_VERSION, + QMK_OPENRGB_GET_DEVICE_INFO, + QMK_OPENRGB_GET_MODE_INFO, + QMK_OPENRGB_GET_LED_INFO, + QMK_OPENRGB_GET_IS_MODE_ENABLED, + + QMK_OPENRGB_SET_MODE, + QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED, + QMK_OPENRGB_DIRECT_MODE_SET_LEDS, +}; + +enum Modes +{ + QMK_OPENRGB_MODE_OPENRGB_DIRECT = 1, + QMK_OPENRGB_MODE_SOLID_COLOR, + QMK_OPENRGB_MODE_ALPHA_MOD, + QMK_OPENRGB_MODE_GRADIENT_UP_DOWN, + QMK_OPENRGB_MODE_GRADIENT_LEFT_RIGHT, + QMK_OPENRGB_MODE_BREATHING, + QMK_OPENRGB_MODE_BAND_SAT, + QMK_OPENRGB_MODE_BAND_VAL, + QMK_OPENRGB_MODE_BAND_PINWHEEL_SAT, + QMK_OPENRGB_MODE_BAND_PINWHEEL_VAL, + QMK_OPENRGB_MODE_BAND_SPIRAL_SAT, + QMK_OPENRGB_MODE_BAND_SPIRAL_VAL, + QMK_OPENRGB_MODE_CYCLE_ALL, + QMK_OPENRGB_MODE_CYCLE_LEFT_RIGHT, + QMK_OPENRGB_MODE_CYCLE_UP_DOWN, + QMK_OPENRGB_MODE_CYCLE_OUT_IN, + QMK_OPENRGB_MODE_CYCLE_OUT_IN_DUAL, + QMK_OPENRGB_MODE_RAINBOW_MOVING_CHEVRON, + QMK_OPENRGB_MODE_CYCLE_PINWHEEL, + QMK_OPENRGB_MODE_CYCLE_SPIRAL, + QMK_OPENRGB_MODE_DUAL_BEACON, + QMK_OPENRGB_MODE_RAINBOW_BEACON, + QMK_OPENRGB_MODE_RAINBOW_PINWHEELS, + QMK_OPENRGB_MODE_RAINDROPS, + QMK_OPENRGB_MODE_JELLYBEAN_RAINDROPS, + QMK_OPENRGB_MODE_HUE_BREATHING, + QMK_OPENRGB_MODE_HUE_PENDULUM, + QMK_OPENRGB_MODE_HUE_WAVE, + QMK_OPENRGB_MODE_TYPING_HEATMAP, + QMK_OPENRGB_MODE_DIGITAL_RAIN, + QMK_OPENRGB_MODE_SOLID_REACTIVE_SIMPLE, + QMK_OPENRGB_MODE_SOLID_REACTIVE, + QMK_OPENRGB_MODE_SOLID_REACTIVE_WIDE, + QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTIWIDE, + QMK_OPENRGB_MODE_SOLID_REACTIVE_CROSS, + QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTICROSS, + QMK_OPENRGB_MODE_SOLID_REACTIVE_NEXUS, + QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTINEXUS, + QMK_OPENRGB_MODE_SPLASH, + QMK_OPENRGB_MODE_MULTISPLASH, + QMK_OPENRGB_MODE_SOLID_SPLASH, + QMK_OPENRGB_MODE_SOLID_MULTISPLASH, +}; + +enum SpeedCommands +{ + QMK_OPENRGB_SPEED_SLOWEST = 0x00, /* Slowest speed */ + QMK_OPENRGB_SPEED_NORMAL = 0x7F, /* Normal speed */ + QMK_OPENRGB_SPEED_FASTEST = 0xFF, /* Fastest speed */ +}; + +enum +{ + QMK_OPENRGB_FAILURE = 25, /* Failure status code */ + QMK_OPENRGB_SUCCESS = 50, /* Success status code */ + QMK_OPENRGB_END_OF_MESSAGE = 100, /* End of Message status code */ +}; + +enum +{ + QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_BYTE = 1, + QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE = 2 +}; + +enum +{ + QMK_OPENRGB_MODE_BYTE = 1, + QMK_OPENRGB_SPEED_BYTE = 2, + QMK_OPENRGB_HUE_BYTE = 3, + QMK_OPENRGB_SATURATION_BYTE = 4, + QMK_OPENRGB_VALUE_BYTE = 5, +}; + +enum +{ + QMK_OPENRGB_POINT_X_BYTE = 1, + QMK_OPENRGB_POINT_Y_BYTE = 2, + QMK_OPENRGB_FLAG_BYTE = 3, + QMK_OPENRGB_R_COLOR_BYTE = 4, + QMK_OPENRGB_G_COLOR_BYTE = 5, + QMK_OPENRGB_B_COLOR_BYTE = 6, + QMK_OPENRGB_KEYCODE_BYTE = 7 +}; + +typedef struct +{ + uint8_t x; + uint8_t y; +} point_t; + +class QMKOpenRGBController +{ +public: + QMKOpenRGBController(hid_device *dev_handle, const char *path); + ~QMKOpenRGBController(); + + std::string GetLocation(); + std::string GetDeviceName(); + std::string GetDeviceVendor(); + + unsigned int GetTotalNumberOfLEDs(); + unsigned int GetTotalNumberOfLEDsWithEmptySpace(); + unsigned int GetMode(); + unsigned int GetModeSpeed(); + unsigned int GetModeColor(); + + std::vector GetLEDPoints(); + std::vector GetLEDFlags(); + std::vector GetLEDNames(); + std::vector GetLEDColors(); + + unsigned int GetProtocolVersion(); + std::string GetQMKVersion(); + void GetDeviceInfo(); + void GetModeInfo(); + void GetLEDInfo(unsigned int led); + bool GetIsModeEnabled(unsigned int mode); + + void SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed); + void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void DirectModeSetLEDs(std::vector colors, unsigned int num_colors); + +protected: + hid_device *dev; + +private: + unsigned int leds_per_update; + + std::string location; + + std::string device_name; + std::string device_vendor; + + unsigned int total_number_of_leds; + unsigned int total_number_of_leds_with_empty_space; + unsigned int mode; + unsigned int mode_speed; + + RGBColor mode_color; + + std::vector led_points; + std::vector led_flags; + std::vector led_names; + std::vector led_colors; +}; diff --git a/Controllers/QMKOpenRGBController/RGBController_QMKOpenRGB.cpp b/Controllers/QMKOpenRGBController/RGBController_QMKOpenRGB.cpp new file mode 100644 index 00000000..444e5f03 --- /dev/null +++ b/Controllers/QMKOpenRGBController/RGBController_QMKOpenRGB.cpp @@ -0,0 +1,727 @@ +/*-------------------------------------------------------------------*\ +| RGBController_QMKOpenRGB.cpp | +| | +| Driver for QMK keyboards using OpenRGB Protocol | +| | +| Kasper 10th Octobber 2020 | +| Jath03 28th May 2021 | +\*-------------------------------------------------------------------*/ + +#include "hsv.h" +#include "RGBController_QMKOpenRGB.h" + +RGBController_QMKOpenRGB::RGBController_QMKOpenRGB(QMKOpenRGBController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = controller->GetDeviceVendor(); + description = "QMK OpenRGB Device (Protocol Version " + std::to_string(controller->GetProtocolVersion()) + ")"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->GetLocation(); + version = controller->GetQMKVersion(); + + unsigned int current_mode = 1; + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_OPENRGB_DIRECT)) + { + InitializeMode("Direct", current_mode, MODE_FLAG_HAS_PER_LED_COLOR, MODE_COLORS_PER_LED); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_COLOR)) + { + InitializeMode("Static", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_ALPHA_MOD)) + { + InitializeMode("Alpha Mod", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_GRADIENT_UP_DOWN)) + { + InitializeMode("Gradient Up Down", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_GRADIENT_LEFT_RIGHT)) + { + InitializeMode("Gradient Left Right", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BREATHING)) + { + InitializeMode("Breathing", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_SAT)) + { + InitializeMode("Band Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_VAL)) + { + InitializeMode("Band Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_PINWHEEL_SAT)) + { + InitializeMode("Band Pinwheel Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_PINWHEEL_VAL)) + { + InitializeMode("Band Pinwheel Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_SPIRAL_SAT)) + { + InitializeMode("Band Spiral Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_SPIRAL_VAL)) + { + InitializeMode("Band Spiral Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_ALL)) + { + InitializeMode("Cycle All", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_LEFT_RIGHT)) + { + InitializeMode("Cycle Left Right", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_UP_DOWN)) + { + InitializeMode("Cycle Up Down", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_OUT_IN)) + { + InitializeMode("Cycle Out In", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_OUT_IN_DUAL)) + { + InitializeMode("Cycle Out In Dual", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_RAINBOW_MOVING_CHEVRON)) + { + InitializeMode("Rainbow Moving Chevron", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_PINWHEEL)) + { + InitializeMode("Cycle Pinwheel", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_SPIRAL)) + { + InitializeMode("Cycle Spiral", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_DUAL_BEACON)) + { + InitializeMode("Dual Beacon", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_RAINBOW_BEACON)) + { + InitializeMode("Rainbow Beacon", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_RAINBOW_PINWHEELS)) + { + InitializeMode("Rainbow Pinwheels", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_RAINDROPS)) + { + InitializeMode("Raindrops", current_mode, 0, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_JELLYBEAN_RAINDROPS)) + { + InitializeMode("Jellybean Raindrops", current_mode, 0, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_HUE_BREATHING)) + { + InitializeMode("Hue Breathing", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_HUE_PENDULUM)) + { + InitializeMode("Hue Pendulum", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_HUE_WAVE)) + { + InitializeMode("Hue Wave", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_TYPING_HEATMAP)) + { + InitializeMode("Typing Heatmap", current_mode, 0, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_DIGITAL_RAIN)) + { + InitializeMode("Digital Rain", current_mode, 0, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_SIMPLE)) + { + InitializeMode("Solid Reactive Simple", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE)) + { + InitializeMode("Solid Reactive", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_WIDE)) + { + InitializeMode("Solid Reactive Wide", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTIWIDE)) + { + InitializeMode("Solid Reactive Multi Wide", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_CROSS)) + { + InitializeMode("Solid Reactive Cross", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTICROSS)) + { + InitializeMode("Solid Reactive Multi Cross", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_NEXUS)) + { + InitializeMode("Solid Reactive Nexus", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTINEXUS)) + { + InitializeMode("Solid Reactive Multi Nexus", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SPLASH)) + { + InitializeMode("Rainbow Reactive Splash", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_MULTISPLASH)) + { + InitializeMode("Rainbow Reactive Multi Splash", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_SPLASH)) + { + InitializeMode("Solid Reactive Splash", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_MULTISPLASH)) + { + InitializeMode("Solid Reactive Multi Splash", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + active_mode = controller->GetMode() - 1; + + SetupZones(); +} + +RGBController_QMKOpenRGB::~RGBController_QMKOpenRGB() +{ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } +} + +void RGBController_QMKOpenRGB::SetupZones() +{ + /*---------------------------------------------------------*\ + | Get the number of LEDs from the device | + \*---------------------------------------------------------*/ + const unsigned int total_number_of_leds = controller->GetTotalNumberOfLEDs(); + const unsigned int total_number_of_leds_with_empty_space = controller->GetTotalNumberOfLEDsWithEmptySpace(); + + /*---------------------------------------------------------*\ + | Get information for each LED | + \*---------------------------------------------------------*/ + for(unsigned int i = 0; i < std::max(total_number_of_leds, total_number_of_leds_with_empty_space); i++) + { + controller->GetLEDInfo(i); + } + + /*---------------------------------------------------------*\ + | Get LED vectors from controller | + \*---------------------------------------------------------*/ + std::vector led_points = controller->GetLEDPoints(); + std::vector led_flags = controller->GetLEDFlags(); + std::vector led_names = controller->GetLEDNames(); + + /*---------------------------------------------------------*\ + | Count key LEDs and underglow LEDs | + \*---------------------------------------------------------*/ + unsigned int number_of_key_leds; + unsigned int number_of_underglow_leds; + + CountKeyTypes(led_flags, total_number_of_leds, number_of_key_leds, number_of_underglow_leds); + + /*---------------------------------------------------------*\ + | Add LED names for underglow zone | + \*---------------------------------------------------------*/ + unsigned int number_of_leds = number_of_key_leds + number_of_underglow_leds; + bool has_underglow = number_of_underglow_leds > 0; + + for(unsigned int i = 0; i < number_of_underglow_leds; i++) + { + led_names.push_back("Underglow: " + std::to_string(number_of_key_leds + i)); + } + + /*---------------------------------------------------------*\ + | Create sets for row and column position values | + \*---------------------------------------------------------*/ + std::set rows, columns; + for (unsigned int i = 0; i < number_of_leds; i++) + { + rows.insert(led_points[i].y); + columns.insert(led_points[i].x); + } + + /*---------------------------------------------------------*\ + | Calculate matrix map from QMK positions | + \*---------------------------------------------------------*/ + unsigned int divisor = CalculateDivisor(led_points, rows, columns); + + VectorMatrix matrix_map; + VectorMatrix underglow_map; + + PlaceLEDsInMaps(rows, columns, divisor, led_points, led_flags, matrix_map, underglow_map); + CleanMatrixMaps(matrix_map, underglow_map, rows.size(), has_underglow); + + /*---------------------------------------------------------*\ + | These vectors are class members because if they go out of | + | scope, the underlying array (used by each zones' | + | matrix_map) is unallocated. | + \*---------------------------------------------------------*/ + flat_matrix_map = FlattenMatrixMap(matrix_map); + flat_underglow_map = FlattenMatrixMap(underglow_map); + + /*---------------------------------------------------------*\ + | Create Keyboard zone | + \*---------------------------------------------------------*/ + zone keys_zone; + keys_zone.name = "Keyboard"; + keys_zone.type = ZONE_TYPE_MATRIX; + keys_zone.leds_min = number_of_key_leds; + keys_zone.leds_max = keys_zone.leds_min; + keys_zone.leds_count = keys_zone.leds_min; + keys_zone.matrix_map = new matrix_map_type; + keys_zone.matrix_map->width = matrix_map[0].size(); + keys_zone.matrix_map->height = matrix_map.size(); + keys_zone.matrix_map->map = flat_matrix_map.data(); + zones.push_back(keys_zone); + + /*---------------------------------------------------------*\ + | Create Underglow zone if it exists | + \*---------------------------------------------------------*/ + if(has_underglow) + { + zone underglow_zone; + underglow_zone.name = "Underglow"; + underglow_zone.type = ZONE_TYPE_MATRIX; + underglow_zone.leds_min = number_of_underglow_leds; + underglow_zone.leds_max = underglow_zone.leds_min; + underglow_zone.leds_count = underglow_zone.leds_min; + underglow_zone.matrix_map = new matrix_map_type; + underglow_zone.matrix_map->width = underglow_map[0].size(); + underglow_zone.matrix_map->height = underglow_map.size(); + underglow_zone.matrix_map->map = flat_underglow_map.data(); + zones.push_back(underglow_zone); + } + + /*---------------------------------------------------------*\ + | Create LEDs | + \*---------------------------------------------------------*/ + for(int led_idx = 0; led_idx < number_of_leds; led_idx++) + { + led keyboard_led; + + if(led_idx < led_names.size()) + { + keyboard_led.name = led_names[led_idx]; + } + keyboard_led.value = led_idx; + + leds.push_back(keyboard_led); + } + + /*---------------------------------------------------------*\ + | Setup Colors | + \*---------------------------------------------------------*/ + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize colors from device values | + \*---------------------------------------------------------*/ + for(unsigned int i = 0; i < leds.size(); i++) + { + colors[i] = controller->GetLEDColors()[i]; + } +} + +void RGBController_QMKOpenRGB::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_QMKOpenRGB::DeviceUpdateLEDs() +{ + controller->DirectModeSetLEDs(colors, controller->GetTotalNumberOfLEDs()); +} + +void RGBController_QMKOpenRGB::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_QMKOpenRGB::UpdateSingleLED(int led) +{ + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->DirectModeSetSingleLED(led, red, grn, blu); +} + +void RGBController_QMKOpenRGB::SetCustomMode() +{ + active_mode = 0; +} + +void RGBController_QMKOpenRGB::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, 127); + } + else if(modes[active_mode].color_mode == MODE_COLORS_NONE) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, modes[active_mode].speed); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + RGBColor rgb_color = modes[active_mode].colors[0]; + hsv_t hsv_color; + rgb2hsv(rgb_color, &hsv_color); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->SetMode(hsv_color, modes[active_mode].value, modes[active_mode].speed); + } + else + { + controller->SetMode(hsv_color, modes[active_mode].value, 127); + } + } +} + +void RGBController_QMKOpenRGB::InitializeMode + ( + std::string name, + unsigned int ¤t_mode, + unsigned int flags, + unsigned int color_mode + ) +{ + mode qmk_mode; + qmk_mode.name = name; + qmk_mode.value = current_mode++; + qmk_mode.flags = flags; + qmk_mode.color_mode = color_mode; + + if(flags & MODE_FLAG_HAS_SPEED) + { + qmk_mode.speed_min = QMK_OPENRGB_SPEED_SLOWEST; + qmk_mode.speed_max = QMK_OPENRGB_SPEED_FASTEST; + qmk_mode.speed = QMK_OPENRGB_SPEED_NORMAL; + } + if(flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + qmk_mode.colors_min = 1; + qmk_mode.colors_max = 1; + qmk_mode.colors.resize(1); + qmk_mode.colors[0] = controller->GetModeColor(); + } + + modes.push_back(qmk_mode); +} + +unsigned int RGBController_QMKOpenRGB::CalculateDivisor + ( + std::vector led_points, + std::set rows, + std::set columns + ) +{ + std::vector< std::vector > row_points(rows.size()); + for(const point_t &pt : led_points) + { + for(const int &i : rows) + { + if(pt.y == i) + { + row_points[std::distance(rows.begin(), rows.find(i))].push_back(pt); + } + } + } + + int last_pos; + std::vector distances; + for(const std::vector &row : row_points) + { + last_pos = 0; + std::for_each(row.begin(), row.end(), [&distances, &last_pos](const point_t &pt) + { + distances.push_back(pt.x - last_pos); + last_pos = pt.x; + }); + } + std::map counts; + for(const int &i : distances) + { + counts[i]++; + } + + unsigned int divisor = distances[0]; + for(const std::pair &i : counts) + { + if(counts[divisor] < i.second) + { + divisor = i.first; + } + } + return divisor; +} + +void RGBController_QMKOpenRGB::CountKeyTypes + ( + std::vector led_flags, + unsigned int total_led_count, + unsigned int& key_leds, + unsigned int& underglow_leds + ) +{ + underglow_leds = 0; + key_leds = 0; + + for(unsigned int i = 0; i < total_led_count; i++) + { + if(led_flags[i] & 2) + { + underglow_leds++; + } + else if(led_flags[i] != 0) + { + key_leds++; + } + } +} + +void RGBController_QMKOpenRGB::PlaceLEDsInMaps + ( + std::set unique_rows, + std::set unique_cols, + unsigned int divisor, + std::vector led_points, + std::vector led_flags, + VectorMatrix& matrix_map_xl, + VectorMatrix& underglow_map_xl + ) +{ + matrix_map_xl = MakeEmptyMatrixMap(unique_rows.size(), std::round(255/divisor) + 10); + underglow_map_xl = MakeEmptyMatrixMap(unique_rows.size(), std::round(255/divisor) + 10); + + unsigned int x = 0; + unsigned int y = 0; + unsigned int openrgb_idx = 0; + unsigned int underglow_counter = 0; + + for(unsigned int i = 0; i < controller->GetTotalNumberOfLEDs(); i++) + { + if(led_points[i].x != 255 && led_points[i].y != 255) + { + bool underglow = led_flags[i] & 2; + + x = std::round(led_points[i].x/divisor); + y = std::distance(unique_rows.begin(), unique_rows.find(led_points[i].y)); + + if(!underglow) + { + while(matrix_map_xl[y][x] != NO_LED) + { + x++; + } + matrix_map_xl[y][x] = i; + } + else + { + while(underglow_map_xl[y][x] != NO_LED) + { + x++; + } + underglow_map_xl[y][x] = underglow_counter; + underglow_counter++; + } + } + } +} + +VectorMatrix RGBController_QMKOpenRGB::MakeEmptyMatrixMap + ( + unsigned int height, + unsigned int width + ) +{ + std::vector > matrix_map(height); + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + matrix_map[i].push_back(NO_LED); + } + } + return matrix_map; +} + +void RGBController_QMKOpenRGB::CleanMatrixMaps + ( + VectorMatrix& matrix_map, + VectorMatrix& underglow_map, + unsigned int height, + bool has_underglow + ) +{ + bool empty_col = true; + bool empty_col_udg = true; + bool empty_row = true; + int width = 0; + int width_udg = 0; + + std::vector empty_rows; + + bool can_break; + bool can_break_udg; + + for(unsigned int i = 0; i < height; i++) + { + empty_row = true; + can_break = false; + can_break_udg = false; + + for(std::size_t j = matrix_map[i].size() - 1; j --> 0; ) + { + if(matrix_map[i][j] != NO_LED && width < (j + 1) && !can_break) + { + width = (j + 1); + can_break = true; + empty_row = false; + } + else if(matrix_map[i][j] != NO_LED) + { + empty_row = false; + } + if(underglow_map[i][j] != NO_LED && width_udg < (j + 1) && !can_break_udg) + { + width_udg = (j + 1); + can_break_udg = true; + } + if (can_break && can_break_udg) break; + } + + if(matrix_map[i][0] != NO_LED) + { + empty_col = false; + } + + if(underglow_map[i][0] != NO_LED) + { + empty_col_udg = false; + } + + if(empty_row) + { + empty_rows.push_back(i); + } + } + + unsigned int new_height = height - empty_rows.size(); + width = empty_col ? width - 1 : width; + width_udg = empty_col_udg && empty_col ? width_udg - 1 : width_udg; + + for(unsigned int i = empty_rows.size(); i --> 0; ) + { + matrix_map.erase(matrix_map.begin()+empty_rows[i]); + } + + for(unsigned int i = 0; i < new_height; i++) + { + if(empty_col) + { + matrix_map[i].erase(matrix_map[i].begin(), matrix_map[i].begin() + 1); + } + + if(empty_col_udg && empty_col) + { + underglow_map[i].erase(underglow_map[i].begin(), underglow_map[i].begin() + 1); + } + + matrix_map[i].erase(matrix_map[i].begin()+width, matrix_map[i].end()); + + if(has_underglow) + { + underglow_map[i].erase(underglow_map[i].begin()+width_udg, underglow_map[i].end()); + } + } + + if(has_underglow) + { + for(unsigned int i = new_height; i < height; i++) + { + underglow_map[i].erase(underglow_map[i].begin()+width_udg, underglow_map[i].end()); + } + } +} + +std::vector RGBController_QMKOpenRGB::FlattenMatrixMap + ( + VectorMatrix matrix_map + ) +{ + std::vector flat_map; + + for(const std::vector &row : matrix_map) + { + for(const unsigned int &item : row) + { + flat_map.push_back(item); + } + } + return flat_map; +} diff --git a/Controllers/QMKOpenRGBController/RGBController_QMKOpenRGB.h b/Controllers/QMKOpenRGBController/RGBController_QMKOpenRGB.h new file mode 100644 index 00000000..3aed25e8 --- /dev/null +++ b/Controllers/QMKOpenRGBController/RGBController_QMKOpenRGB.h @@ -0,0 +1,96 @@ +/*-------------------------------------------------------------------*\ +| RGBController_QMKOpenRGB.h | +| | +| Driver for QMK keyboards using OpenRGB Protocol | +| | +| Kasper 10th Octobber 2020 | +| Jath03 28th May 2021 | +\*-------------------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "QMKOpenRGBController.h" +#include +#include +#include +#include + +#define NO_LED 0xFFFFFFFF + +typedef std::vector> VectorMatrix; + +class RGBController_QMKOpenRGB : public RGBController +{ +public: + RGBController_QMKOpenRGB(QMKOpenRGBController* controller_ptr); + ~RGBController_QMKOpenRGB(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void SetCustomMode(); + void DeviceUpdateMode(); + +private: + QMKOpenRGBController* controller; + std::vector flat_matrix_map; + std::vector flat_underglow_map; + + void InitializeMode + ( + std::string name, + unsigned int ¤t_mode, + unsigned int flags, + unsigned int color_mode + ); + + unsigned int CalculateDivisor + ( + std::vector led_points, + std::set rows, + std::set columns + ); + + void CountKeyTypes + ( + std::vector led_flags, + unsigned int total_led_count, + unsigned int& key_leds, + unsigned int& underglow_leds + ); + + void PlaceLEDsInMaps + ( + std::set unique_rows, + std::set unique_cols, + unsigned int divisor, + std::vector led_points, + std::vector led_flags, + VectorMatrix& matrix_map_xl, + VectorMatrix& underglow_map_xl + ); + + VectorMatrix MakeEmptyMatrixMap + ( + unsigned int height, + unsigned int width + ); + + void CleanMatrixMaps + ( + VectorMatrix& matrix_map, + VectorMatrix& underglow_map, + unsigned int height, + bool has_underglow + ); + + std::vector FlattenMatrixMap + ( + VectorMatrix matrix_map + ); +};