From 7e4b9c0cb03d5a32aa396812442cbacf0a605668 Mon Sep 17 00:00:00 2001 From: Lukas Niederbremer Date: Mon, 29 Jun 2020 16:12:17 +0200 Subject: [PATCH] Add experimental Cooler Master MasterKeys support --- .gitmodules | 3 + .../CoolerMasterController/CMMKController.cpp | 160 +++++++ .../CoolerMasterController/CMMKController.h | 60 +++ .../CoolerMasterControllerDetect.cpp | 2 +- .../RGBController_CMMKController.cpp | 391 ++++++++++++++++++ RGBController/RGBController_CMMKController.h | 43 ++ dependencies/libcmmk | 1 + 7 files changed, 659 insertions(+), 1 deletion(-) create mode 100644 Controllers/CoolerMasterController/CMMKController.cpp create mode 100644 Controllers/CoolerMasterController/CMMKController.h create mode 100644 RGBController/RGBController_CMMKController.cpp create mode 100644 RGBController/RGBController_CMMKController.h create mode 160000 dependencies/libcmmk diff --git a/.gitmodules b/.gitmodules index d2b990b1..9f9bb705 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "razer-drivers-win32"] path = razer-drivers-win32 url = https://github.com/rsandoz/razer-drivers-win32 +[submodule "dependencies/libcmmk"] + path = dependencies/libcmmk + url = https://github.com/chmod222/libcmmk.git diff --git a/Controllers/CoolerMasterController/CMMKController.cpp b/Controllers/CoolerMasterController/CMMKController.cpp new file mode 100644 index 00000000..974d6f3b --- /dev/null +++ b/Controllers/CoolerMasterController/CMMKController.cpp @@ -0,0 +1,160 @@ +/*-------------------------------------------------------------------*\ +| CMMKController.cpp | +| | +| Driver for Coolermaster MasterKeys Keyboards | +| | +| Lukas N (chmod222) 28th Jun 2020 | +| | +\*-------------------------------------------------------------------*/ + +#include "CMMKController.h" + +#include +#include + +CMMKController::CMMKController(hid_device_info* _dev_info, wchar_t *_vendor, wchar_t *_device_name, char *_path) +{ + std::wstring tmp_vendor_str = _vendor; + std::wstring tmp_device_str = _device_name; + + std::wstring full_name = _vendor + std::wstring{L" "} + _device_name; + + std::wstring_convert, wchar_t> converter; + + device_name = converter.to_bytes(full_name); + + cmmk_attach_path(&cmmk_handle, _dev_info->path, _dev_info->product_id, -1); + + char buf[32] = {0}; + + cmmk_get_firmware_version(&cmmk_handle, buf, 32); + + firmware_version = buf; + + location = _path; +} + +CMMKController::~CMMKController() +{ + cmmk_detach(&cmmk_handle); +} + +std::string CMMKController::GetDeviceName() const +{ + return device_name; +} + +std::string CMMKController::GetLocation() const +{ + return location; +} + +std::string CMMKController::GetFirmwareVersion() const +{ + return firmware_version; +} + +void CMMKController::SetFirmwareControl() +{ + ActivateMode(CMMK_FIRMWARE); +} + +void CMMKController::SetManualControl() +{ + ActivateMode(CMMK_MANUAL); +} + +void CMMKController::SetSingle(int row, int col, struct rgb color) +{ + cmmk_set_single_key(&cmmk_handle, row, col, &color); +} + +void CMMKController::SetAll(struct cmmk_color_matrix const& colors) +{ + cmmk_set_leds(&cmmk_handle, &colors); +} + +void CMMKController::SetAllSingle(struct rgb color) +{ + cmmk_set_all_single(&cmmk_handle, &color); +} + +void CMMKController::SetMode(cmmk_effect_fully_lit eff) +{ + ActivateEffect(CMMK_EFFECT_FULLY_LIT); + cmmk_set_effect_fully_lit(&cmmk_handle, &eff); +} + +void CMMKController::SetMode(cmmk_effect_breathe eff) +{ + ActivateEffect(CMMK_EFFECT_BREATHE); + cmmk_set_effect_breathe(&cmmk_handle, &eff); +} + +void CMMKController::SetMode(cmmk_effect_cycle eff) +{ + ActivateEffect(CMMK_EFFECT_CYCLE); + cmmk_set_effect_cycle(&cmmk_handle, &eff); +} + +void CMMKController::SetMode(cmmk_effect_single eff) +{ + ActivateEffect(CMMK_EFFECT_SINGLE); + cmmk_set_effect_single(&cmmk_handle, &eff); +} + +void CMMKController::SetMode(cmmk_effect_wave eff) +{ + ActivateEffect(CMMK_EFFECT_WAVE); + cmmk_set_effect_wave(&cmmk_handle, &eff); +} + +void CMMKController::SetMode(cmmk_effect_ripple eff) +{ + ActivateEffect(CMMK_EFFECT_RIPPLE); + cmmk_set_effect_ripple(&cmmk_handle, &eff); +} + +void CMMKController::SetMode(cmmk_effect_cross eff) +{ + ActivateEffect(CMMK_EFFECT_CROSS); + cmmk_set_effect_cross(&cmmk_handle, &eff); +} + +void CMMKController::SetMode(cmmk_effect_raindrops eff) +{ + ActivateEffect(CMMK_EFFECT_RAINDROPS); + cmmk_set_effect_raindrops(&cmmk_handle, &eff); +} + +void CMMKController::SetMode(cmmk_effect_stars eff) +{ + ActivateEffect(CMMK_EFFECT_STARS); + cmmk_set_effect_stars(&cmmk_handle, &eff); +} + +bool CMMKController::PositionValid(int y, int x) const +{ + return cmmk_lookup_key_id(&cmmk_handle, y, x) >= 0; +} + +void CMMKController::ActivateMode(int mode) +{ + if (current_mode != mode) { + cmmk_set_control_mode(&cmmk_handle, mode); + + current_mode = mode; + current_effect = -1; + } +} + +void CMMKController::ActivateEffect(int effect) +{ + ActivateMode(CMMK_EFFECT); + + if (current_effect != effect) { + cmmk_set_active_effect(&cmmk_handle, (enum cmmk_effect_id)effect); + + current_effect = effect; + } +} \ No newline at end of file diff --git a/Controllers/CoolerMasterController/CMMKController.h b/Controllers/CoolerMasterController/CMMKController.h new file mode 100644 index 00000000..049b01fe --- /dev/null +++ b/Controllers/CoolerMasterController/CMMKController.h @@ -0,0 +1,60 @@ +/*-------------------------------------------------------------------*\ +| CMMKController.h | +| | +| Driver for Coolermaster MasterKeys keyboards | +| | +| Lukas N (chmod222) 28th Jun 2020 | +| | +\*-------------------------------------------------------------------*/ + +#include +#include + +#include + +#include + +#pragma once + +class CMMKController +{ +public: + CMMKController(hid_device_info* _dev_info, wchar_t *_vendor, wchar_t *_device_name, char *_path); + virtual ~CMMKController(); + + std::string GetDeviceName() const; + std::string GetLocation() const; + std::string GetFirmwareVersion() const; + + void SetFirmwareControl(); + void SetManualControl(); + + void SetSingle(int row, int col, struct rgb color); + void SetAll(struct cmmk_color_matrix const& colors); + void SetAllSingle(struct rgb color); + + void SetMode(cmmk_effect_fully_lit eff); + void SetMode(cmmk_effect_breathe eff); + void SetMode(cmmk_effect_cycle eff); + void SetMode(cmmk_effect_single eff); + void SetMode(cmmk_effect_wave eff); + void SetMode(cmmk_effect_ripple eff); + void SetMode(cmmk_effect_cross eff); + void SetMode(cmmk_effect_raindrops eff); + void SetMode(cmmk_effect_stars eff); + + bool PositionValid(int y, int x) const; + +private: + void ActivateMode(int mode); + void ActivateEffect(int effect); + + int current_mode = -1; + int current_effect = -1; + + std::string device_name; + std::string location; + std::string firmware_version; + + mutable struct cmmk cmmk_handle; +}; diff --git a/Controllers/CoolerMasterController/CoolerMasterControllerDetect.cpp b/Controllers/CoolerMasterController/CoolerMasterControllerDetect.cpp index b0698971..02a51dbb 100644 --- a/Controllers/CoolerMasterController/CoolerMasterControllerDetect.cpp +++ b/Controllers/CoolerMasterController/CoolerMasterControllerDetect.cpp @@ -10,7 +10,7 @@ #include "RGBController_CMR6000Controller.h" #include -#define COOLERMASTER_VID 0x2516 +#define COOLERMASTER_VID 0x2516 #define COOLERMASTER_MP750_XL_PID 0x0109 #define COOLERMASTER_MP750_MEDIUM_PID 0x0105 diff --git a/RGBController/RGBController_CMMKController.cpp b/RGBController/RGBController_CMMKController.cpp new file mode 100644 index 00000000..4c3db044 --- /dev/null +++ b/RGBController/RGBController_CMMKController.cpp @@ -0,0 +1,391 @@ +/*-------------------------------------------------------------------*\ +| RGBController_CMMKController.cpp | +| | +| Driver for Coolermaster MasterKeys keyboards | +| | +| Lukas N (chmod222) 28th Jun 2020 | +| | +\*-------------------------------------------------------------------*/ + +#include "RGBController_CMMKController.h" + +#include +#include + +using namespace std::chrono_literals; + +RGBController_CMMKController::RGBController_CMMKController(CMMKController* cmmk_ctrl) +{ + cmmk = cmmk_ctrl; + + name = cmmk->GetDeviceName(); + type = DEVICE_TYPE_KEYBOARD; + description = cmmk->GetDeviceName(); + version = cmmk->GetFirmwareVersion(); + serial = ""; + location = cmmk->GetLocation(); + + mode FirmwareControl; + FirmwareControl.name = "Firmware Controlled"; + FirmwareControl.value = 0xff; + FirmwareControl.flags = 0; + modes.push_back(FirmwareControl); + + mode CustomKeys; + CustomKeys.name = "Customized LEDs"; + CustomKeys.value = 0x7f; + CustomKeys.flags = MODE_FLAG_HAS_PER_LED_COLOR; + CustomKeys.color_mode = MODE_COLORS_PER_LED; + modes.push_back(CustomKeys); + + mode FullLit; + FullLit.name = "Single Color"; + FullLit.value = CMMK_EFFECT_FULLY_LIT; + FullLit.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + FullLit.color_mode = MODE_COLORS_MODE_SPECIFIC; + FullLit.colors_min = 1; + FullLit.colors_max = 1; + FullLit.colors.resize(1); + modes.push_back(FullLit); + + mode Breathe; + Breathe.name = "Breathe"; + Breathe.value = CMMK_EFFECT_BREATHE; + Breathe.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathe.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathe.colors_min = 1; + Breathe.colors_max = 1; + Breathe.colors.resize(1); + Breathe.speed_min = CMMK_SPEED0; + Breathe.speed_max = CMMK_SPEED4; + modes.push_back(Breathe); + + mode Cycle; + Cycle.name = "Cycle"; + Cycle.value = CMMK_EFFECT_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = CMMK_SPEED0; + Cycle.speed_max = CMMK_SPEED4; + modes.push_back(Cycle); + + mode Single; + Single.name = "Single Key Fade-Out"; + Single.value = CMMK_EFFECT_SINGLE; + Single.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Single.color_mode = MODE_COLORS_MODE_SPECIFIC; + Single.colors_min = 2; + Single.colors_max = 2; + Single.colors.resize(2); + Single.speed_min = CMMK_SPEED0; + Single.speed_max = CMMK_SPEED4; + modes.push_back(Single); + + mode Wave; + Wave.name = "Wave"; + Wave.value = CMMK_EFFECT_WAVE; + Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.colors_min = 1; + Wave.colors_max = 1; + Wave.colors.resize(1); + Wave.speed_min = CMMK_SPEED0; + Wave.speed_max = CMMK_SPEED4; + modes.push_back(Wave); + + mode Ripple; + Ripple.name = "Ripple Effect"; + Ripple.value = CMMK_EFFECT_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED; + Ripple.colors_min = 2; + Ripple.colors_max = 2; + Ripple.colors.resize(2); + Ripple.speed_min = CMMK_SPEED0; + Ripple.speed_max = CMMK_SPEED4; + modes.push_back(Ripple); + + mode Cross; + Cross.name = "Cross Effect"; + Cross.value = CMMK_EFFECT_CROSS; + Cross.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Cross.colors_min = 2; + Cross.colors_max = 2; + Cross.colors.resize(2); + Cross.speed_min = CMMK_SPEED0; + Cross.speed_max = CMMK_SPEED4; + modes.push_back(Cross); + + mode Raindrops; + Raindrops.name = "Raindrops Effect"; + Raindrops.value = CMMK_EFFECT_RAINDROPS; + Raindrops.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Raindrops.colors_min = 2; + Raindrops.colors_max = 2; + Raindrops.colors.resize(2); + Raindrops.speed_min = CMMK_SPEED0; + Raindrops.speed_max = CMMK_SPEED4; + modes.push_back(Raindrops); + + mode Stars; + Stars.name = "Starfield Effect"; + Stars.value = CMMK_EFFECT_STARS; + Stars.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Stars.colors_min = 2; + Stars.colors_max = 2; + Stars.colors.resize(2); + Stars.speed_min = CMMK_SPEED0; + Stars.speed_max = CMMK_SPEED4; + modes.push_back(Stars); + + SetupZones(); +} + +RGBController_CMMKController::~RGBController_CMMKController() +{ + delete cmmk; +} + +void RGBController_CMMKController::SetupMatrixMap() +{ + for (int y = 0; y < CMMK_ROWS_MAX; ++y) { + for (int x = 0; x < CMMK_COLS_MAX; ++x) { + matrix_map[y][x] = 0xFFFFFFFF; + } + } + + for (size_t i = 0; i < leds.size(); ++i) { + led const& l = leds[i]; + + int y = (l.value & 0xFF00) >> 8; + int x = l.value & 0xFF; + + matrix_map[y][x] = i; + } +} + +void RGBController_CMMKController::SetupZones() +{ + for (int y = 0; y < CMMK_ROWS_MAX; ++y) { + for (int x = 0; x < CMMK_COLS_MAX; ++x) { + if (!cmmk->PositionValid(y, x)) { + continue; + } + + std::stringstream namestrm; + + led key; + + namestrm << "Key @ Row " << (y + 1) << ", Column" << (x + 1); + + key.name = namestrm.str(); + key.value = (y & 0xFF) << 8 | (x & 0xFF); + + leds.push_back(key); + } + } + + zone KeyboardZone; + KeyboardZone.name = "Keyboard"; + KeyboardZone.type = ZONE_TYPE_MATRIX; + KeyboardZone.leds_min = leds.size(); + KeyboardZone.leds_max = leds.size(); + KeyboardZone.leds_count = leds.size(); + KeyboardZone.matrix_map = new matrix_map_type; + KeyboardZone.matrix_map->height = CMMK_ROWS_MAX; + KeyboardZone.matrix_map->width = CMMK_COLS_MAX; + KeyboardZone.matrix_map->map = (unsigned int *)&matrix_map; + + zones.push_back(KeyboardZone); + + SetupMatrixMap(); + SetupColors(); +} + +void RGBController_CMMKController::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +namespace { + struct rgb map_to_cmmk_rgb(RGBColor input) { + return rgb { + (uint8_t)RGBGetRValue(input), + (uint8_t)RGBGetGValue(input), + (uint8_t)RGBGetBValue(input) + }; + } + + enum cmmk_wave_direction map_to_cmmk_dir(int input) { + switch (input) { + case MODE_DIRECTION_LEFT: return CMMK_RIGHT_TO_LEFT; + case MODE_DIRECTION_RIGHT: return CMMK_LEFT_TO_RIGHT; + case MODE_DIRECTION_UP: return CMMK_FRONT_TO_BACK; + case MODE_DIRECTION_DOWN: return CMMK_BACK_TO_FRONT; + default: assert(false && "unreachable"); + } + } + + void copy_buffers(led* buf, RGBColor* colbuf, size_t n, struct cmmk_color_matrix& mat, std::atomic& dirty) { + dirty.store(false); + + for (size_t i = 0; i < n; ++i) { + led const& selected_led = buf[i]; + + int y = (selected_led.value & 0xFF00) >> 8; + int x = selected_led.value & 0xFF; + + struct rgb col = map_to_cmmk_rgb(colbuf[i]); + struct rgb ecol = mat.data[y][x]; + + if (ecol.R != col.R || ecol.G != col.G || ecol.B != col.B) { + dirty.store(true); + + mat.data[y][x] = col; + } + } + } +} + +void RGBController_CMMKController::DeviceUpdateLEDs() +{ + copy_buffers(leds.data(), colors.data(), leds.size(), current_matrix, dirty); + + if (force_update.load() || dirty.load()) { + cmmk->SetAll(current_matrix); + + force_update.store(false); + } +} + +void RGBController_CMMKController::UpdateZoneLEDs(int _zone) +{ + zone const& z = zones[_zone]; + + copy_buffers(z.leds, z.colors, z.leds_count, current_matrix, dirty); + + if (force_update.load() || dirty.load()) { + cmmk->SetAll(current_matrix); + + force_update.store(false); + } +} + +void RGBController_CMMKController::UpdateSingleLED(int _led) +{ + led const& selected_led = leds[_led]; + + int y = (selected_led.value & 0xFF00) >> 8; + int x = selected_led.value & 0xFF; + + current_matrix.data[y][x] = map_to_cmmk_rgb(colors[_led]); + + cmmk->SetSingle(y, x, map_to_cmmk_rgb(colors[_led])); + dirty.store(false); +} + +void RGBController_CMMKController::SetCustomMode() +{ + force_update.store(true); + + active_mode = 1; +} + +void RGBController_CMMKController::UpdateMode() +{ + force_update.store(true); + + switch(modes[active_mode].value) + { + case 0xff: + cmmk->SetFirmwareControl(); + break; + + case 0x7f: + cmmk->SetManualControl(); + break; + + case CMMK_EFFECT_FULLY_LIT: + cmmk->SetMode(cmmk_effect_fully_lit { + map_to_cmmk_rgb(modes[active_mode].colors[0]) + }); + + break; + + case CMMK_EFFECT_BREATHE: + cmmk->SetMode(cmmk_effect_breathe { + (uint8_t)modes[active_mode].speed, + map_to_cmmk_rgb(modes[active_mode].colors[0]) + }); + + break; + + case CMMK_EFFECT_CYCLE: + cmmk->SetMode(cmmk_effect_cycle { + (uint8_t)modes[active_mode].speed + }); + + break; + + case CMMK_EFFECT_SINGLE: + cmmk->SetMode(cmmk_effect_single { + (uint8_t)modes[active_mode].speed, + + map_to_cmmk_rgb(modes[active_mode].colors[0]), + map_to_cmmk_rgb(modes[active_mode].colors[1]) + }); + + break; + + case CMMK_EFFECT_WAVE: + cmmk->SetMode(cmmk_effect_wave { + (uint8_t)modes[active_mode].speed, + map_to_cmmk_dir(modes[active_mode].direction), + map_to_cmmk_rgb(modes[active_mode].colors[0]) + }); + + break; + + case CMMK_EFFECT_RIPPLE: + cmmk->SetMode(cmmk_effect_ripple { + (uint8_t)modes[active_mode].speed, + modes[active_mode].color_mode == MODE_COLORS_RANDOM + ? CMMK_RIPPLE_RANDOM_COLOR + : CMMK_RIPPLE_GIVEN_COLOR, + map_to_cmmk_rgb(modes[active_mode].colors[0]), + map_to_cmmk_rgb(modes[active_mode].colors[1]) + }); + + break; + + case CMMK_EFFECT_CROSS: + cmmk->SetMode(cmmk_effect_cross { + (uint8_t)modes[active_mode].speed, + map_to_cmmk_rgb(modes[active_mode].colors[0]), + map_to_cmmk_rgb(modes[active_mode].colors[1]) + }); + + break; + + case CMMK_EFFECT_RAINDROPS: + cmmk->SetMode(cmmk_effect_raindrops { + (uint8_t)modes[active_mode].speed, + CMMK_SPEED2, + map_to_cmmk_rgb(modes[active_mode].colors[0]), + map_to_cmmk_rgb(modes[active_mode].colors[1]) + }); + + break; + + case CMMK_EFFECT_STARS: + cmmk->SetMode(cmmk_effect_stars { + (uint8_t)modes[active_mode].speed, + CMMK_SPEED2, + map_to_cmmk_rgb(modes[active_mode].colors[0]), + map_to_cmmk_rgb(modes[active_mode].colors[1]) + }); + + break; + } +} diff --git a/RGBController/RGBController_CMMKController.h b/RGBController/RGBController_CMMKController.h new file mode 100644 index 00000000..c86a30eb --- /dev/null +++ b/RGBController/RGBController_CMMKController.h @@ -0,0 +1,43 @@ +/*-------------------------------------------------------------------*\ +| RGBController_CMMKController.h | +| | +| Driver for Coolermaster MasterKeys keyboards | +| | +| Lukas N (chmod222) 28th Jun 2020 | +| | +\*-------------------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CMMKController.h" + +#include + +class RGBController_CMMKController : public RGBController +{ +public: + RGBController_CMMKController(CMMKController* cmmk_ptr); + ~RGBController_CMMKController(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void SetCustomMode(); + void UpdateMode(); + +private: + void SetupMatrixMap(); + + CMMKController* cmmk; + + int matrix_map[CMMK_ROWS_MAX][CMMK_COLS_MAX]; + struct cmmk_color_matrix current_matrix; + + std::atomic dirty; + std::atomic force_update; +}; diff --git a/dependencies/libcmmk b/dependencies/libcmmk new file mode 160000 index 00000000..a8472d14 --- /dev/null +++ b/dependencies/libcmmk @@ -0,0 +1 @@ +Subproject commit a8472d1494049eb96c5c4fefbb309654d17ac20d