-
-
Notifications
You must be signed in to change notification settings - Fork 39.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial PoC for Dynamic lighting support
- Loading branch information
Showing
10 changed files
with
788 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
// Copyright 2023 QMK | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
#include "lamparray.h" | ||
#include "keycodes.h" | ||
#include "keymap_introspection.h" | ||
#include "action_layer.h" | ||
|
||
// Defaults are generated from info.json layout content | ||
#ifndef LAMPARRAY_WIDTH | ||
# define LAMPARRAY_WIDTH ESTIMATED_KEYBOARD_WIDTH | ||
#endif | ||
#ifndef LAMPARRAY_HEIGHT | ||
# define LAMPARRAY_HEIGHT ESTIMATED_KEYBOARD_HEIGHT | ||
#endif | ||
#ifndef LAMPARRAY_DEPTH | ||
# define LAMPARRAY_DEPTH 30000 | ||
#endif | ||
#ifndef LAMPARRAY_KIND | ||
# define LAMPARRAY_KIND LAMPARRAY_KIND_KEYBOARD | ||
#endif | ||
|
||
// TODO: Implement backing API | ||
void lamparray_backing_enable(bool enable); | ||
void lamparray_backing_set_item(uint16_t index, lamp_state_t color); | ||
void lamparray_backing_update_finished(void); | ||
|
||
/** | ||
* \brief Query a HID usage for a given location | ||
* | ||
* This can be requested while the user is changing layers. This is mitigated somewhat by assuming the default layer changes less frequently. | ||
* This is currently accepted as a limitation as there is no method to invalidate the hosts view of the data. | ||
*/ | ||
static inline uint16_t binding_at_keymap_location(uint8_t row, uint8_t col) { | ||
uint16_t keycode = keycode_at_keymap_location(get_highest_layer(default_layer_state), row, col); | ||
#if LAMPARRAY_KIND == LAMPARRAY_KIND_KEYBOARD | ||
// Basic QMK keycodes currently map directly to Keyboard UsagePage so safe to return without added indirection | ||
// Mousekeys are ignored due to values overlap Keyboard UsagePage | ||
if (IS_BASIC_KEYCODE(keycode) || IS_MODIFIER_KEYCODE(keycode)) { | ||
return keycode; | ||
} | ||
#elif LAMPARRAY_KIND == LAMPARRAY_KIND_MOUSE | ||
// Usages from the Button UsagePage (0x09) in the range of Button1 (0x01) to Button5 (0x05) inclusive | ||
if ((code) >= KC_MS_BTN1 && (code) <= KC_MS_BTN5) { | ||
return keycode - KC_MS_BTN1; | ||
} | ||
#endif | ||
return 0; | ||
} | ||
|
||
#ifdef RGB_MATRIX_ENABLE | ||
# include "rgb_matrix.h" | ||
|
||
# define LAMPARRAY_RED_LEVELS 255 | ||
# define LAMPARRAY_GREEN_LEVELS 255 | ||
# define LAMPARRAY_BLUE_LEVELS 255 | ||
# define LAMPARRAY_INTENSITY_LEVELS 1 | ||
# define LAMPARRAY_LAMP_COUNT RGB_MATRIX_LED_COUNT | ||
# define LAMPARRAY_UPDATE_INTERVAL (RGB_MATRIX_LED_FLUSH_LIMIT * 1000) | ||
|
||
/** | ||
* \brief Get feature specific lamp info. | ||
* | ||
* Scales the LED config with the assumed RGB Matrix dimensions (224x64), for simplicity, as a completely flat device. | ||
* Assumes all keys are either on the top or bottom of the resulting rectangle. | ||
*/ | ||
__attribute__((weak)) void lamparray_get_lamp_info_data(uint16_t lamp_id, lamparray_attributes_response_t* data) { | ||
data->position.x = (LAMPARRAY_WIDTH / 224) * g_led_config.point[lamp_id].x; | ||
data->position.y = (LAMPARRAY_HEIGHT / 64) * (64 - g_led_config.point[lamp_id].y); | ||
data->position.z = (g_led_config.flags[lamp_id] & LED_FLAG_UNDERGLOW) ? LAMPARRAY_DEPTH : 0; | ||
data->purposes = (g_led_config.flags[lamp_id] & LED_FLAG_UNDERGLOW) ? LAMP_PURPOSE_ACCENT : LAMP_PURPOSE_CONTROL; | ||
} | ||
|
||
/** | ||
* \brief Query a HID usage for a given lamp | ||
*/ | ||
__attribute__((weak)) uint8_t lamparray_get_lamp_binding(uint16_t lamp_id) { | ||
for (uint8_t i_row = 0; i_row < MATRIX_ROWS; i_row++) { | ||
for (uint8_t i_col = 0; i_col < MATRIX_COLS; i_col++) { | ||
if (g_led_config.matrix_co[i_row][i_col] == lamp_id) { | ||
return binding_at_keymap_location(i_row, i_col); | ||
} | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
// TODO: temporay binding of storage and render | ||
# include "rgb_matrix/overlay.c" | ||
|
||
void lamparray_backing_enable(bool enable) { | ||
rgb_matrix_overlay_enable(enable); | ||
} | ||
|
||
void lamparray_backing_set_item(uint16_t index, lamp_state_t color) { | ||
rgb_matrix_overlay_set(index, (rgba_t){color.red, color.green, color.blue, color.intensity ? 0 : 0xFF}); | ||
} | ||
|
||
void lamparray_backing_update_finished(void) { | ||
rgb_matrix_overlay_flush(); | ||
} | ||
#endif | ||
|
||
static uint16_t cur_lamp_id = 0; | ||
static bool is_autonomous = true; | ||
|
||
void lamparray_get_attributes(lamparray_attributes_t* data) { | ||
data->lamp_count = LAMPARRAY_LAMP_COUNT; | ||
data->update_interval = LAMPARRAY_UPDATE_INTERVAL; | ||
data->kind = LAMPARRAY_KIND; | ||
data->bounds.width = LAMPARRAY_WIDTH; | ||
data->bounds.height = LAMPARRAY_HEIGHT; | ||
data->bounds.depth = LAMPARRAY_DEPTH; | ||
} | ||
|
||
void lamparray_get_lamp_info(uint16_t lamp_id, lamparray_attributes_response_t* data) { | ||
data->lamp_id = lamp_id; | ||
data->update_latency = 1000; | ||
data->is_programmable = 1; | ||
data->input_binding = lamparray_get_lamp_binding(lamp_id); | ||
|
||
data->levels.red = LAMPARRAY_RED_LEVELS; | ||
data->levels.green = LAMPARRAY_GREEN_LEVELS; | ||
data->levels.blue = LAMPARRAY_BLUE_LEVELS; | ||
data->levels.intensity = LAMPARRAY_INTENSITY_LEVELS; | ||
|
||
lamparray_get_lamp_info_data(lamp_id, data); | ||
} | ||
|
||
void lamparray_get_attributes_response(lamparray_attributes_response_t* data) { | ||
lamparray_get_lamp_info(cur_lamp_id, data); | ||
|
||
// Automatic address pointer incrementing - 26.8.1 LampAttributesRequestReport | ||
cur_lamp_id++; | ||
if (cur_lamp_id >= LAMPARRAY_LAMP_COUNT) cur_lamp_id = 0; | ||
} | ||
|
||
void lamparray_set_attributes_response(uint16_t lamp_id) { | ||
cur_lamp_id = lamp_id; | ||
} | ||
|
||
void lamparray_set_control_response(uint8_t autonomous) { | ||
is_autonomous = !!autonomous; | ||
|
||
lamparray_backing_enable(!autonomous); | ||
} | ||
|
||
void lamparray_set_range(lamparray_range_update_t* data) { | ||
// Any Lamp*UpdateReports can be ignored - 26.10.1 AutonomousMode | ||
if (is_autonomous) { | ||
return; | ||
} | ||
|
||
// Ensure IDs are within bounds | ||
if ((data->start >= LAMPARRAY_LAMP_COUNT) || (data->end >= LAMPARRAY_LAMP_COUNT)) { | ||
return; | ||
} | ||
|
||
for (uint16_t index = data->start; index <= data->end; index++) { | ||
lamparray_backing_set_item(index, data->color); | ||
} | ||
|
||
// Batch update complete - 26.11 Updating Lamp State | ||
if (data->flags & LAMP_UPDATE_FLAG_COMPLETE) { | ||
lamparray_backing_update_finished(); | ||
} | ||
} | ||
|
||
void lamparray_set_items(lamparray_multi_update_t* data) { | ||
// Any Lamp*UpdateReports can be ignored - 26.10.1 AutonomousMode | ||
if (is_autonomous) { | ||
return; | ||
} | ||
|
||
// Ensure data is within bounds | ||
if (data->count > LAMP_MULTI_UPDATE_LAMP_COUNT) { | ||
return; | ||
} | ||
|
||
for (uint8_t i = 0; i < data->count; i++) { | ||
// Ensure IDs are within bounds | ||
if (data->ids[i] >= LAMPARRAY_LAMP_COUNT) { | ||
continue; | ||
} | ||
lamparray_backing_set_item(data->ids[i], data->colors[i]); | ||
} | ||
|
||
// Batch update complete - 26.11 Updating Lamp State | ||
if (data->flags & LAMP_UPDATE_FLAG_COMPLETE) { | ||
lamparray_backing_update_finished(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright 2023 QMK | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
#pragma once | ||
|
||
#include "util.h" // PACKED | ||
|
||
// 26.2.1 LampArrayKind Values | ||
#define LAMPARRAY_KIND_UNDEFINED 0x00 | ||
#define LAMPARRAY_KIND_KEYBOARD 0x01 | ||
#define LAMPARRAY_KIND_MOUSE 0x02 | ||
#define LAMPARRAY_KIND_GAMECONTROLLER 0x03 | ||
#define LAMPARRAY_KIND_PERIPHERAL 0x04 | ||
#define LAMPARRAY_KIND_SCENE 0x05 | ||
#define LAMPARRAY_KIND_NOTIFICATION 0x06 | ||
#define LAMPARRAY_KIND_CHASSIS 0x07 | ||
#define LAMPARRAY_KIND_WEARABLE 0x08 | ||
#define LAMPARRAY_KIND_FURNITURE 0x09 | ||
#define LAMPARRAY_KIND_ART 0x0A | ||
|
||
// 26.3.1 LampPurposes Flags | ||
#define LAMP_PURPOSE_CONTROL 0x01 | ||
#define LAMP_PURPOSE_ACCENT 0x02 | ||
#define LAMP_PURPOSE_BRANDING 0x04 | ||
#define LAMP_PURPOSE_STATUS 0x08 | ||
#define LAMP_PURPOSE_ILLUMINATION 0x10 | ||
#define LAMP_PURPOSE_PRESENTATION 0x20 | ||
|
||
// 26.4.1 LampUpdate Flags | ||
#define LAMP_UPDATE_FLAG_COMPLETE 0x01 | ||
|
||
typedef struct PACKED { | ||
uint8_t red; | ||
uint8_t green; | ||
uint8_t blue; | ||
uint8_t intensity; | ||
} lamp_state_t; | ||
|
||
typedef struct PACKED { | ||
uint16_t lamp_count; | ||
struct { | ||
uint32_t width; | ||
uint32_t height; | ||
uint32_t depth; | ||
} bounds; | ||
uint32_t kind; | ||
uint32_t update_interval; | ||
} lamparray_attributes_t; | ||
|
||
typedef struct PACKED { | ||
uint16_t lamp_id; | ||
struct { | ||
int32_t x; | ||
int32_t y; | ||
int32_t z; | ||
} position; | ||
int32_t update_latency; | ||
int32_t purposes; | ||
lamp_state_t levels; | ||
uint8_t is_programmable; | ||
uint8_t input_binding; | ||
} lamparray_attributes_response_t; | ||
|
||
typedef struct PACKED { | ||
uint8_t flags; | ||
uint16_t start; | ||
uint16_t end; | ||
lamp_state_t color; | ||
} lamparray_range_update_t; | ||
|
||
#define LAMP_MULTI_UPDATE_LAMP_COUNT 8 | ||
typedef struct PACKED { | ||
uint8_t count; | ||
uint8_t flags; | ||
uint16_t ids[LAMP_MULTI_UPDATE_LAMP_COUNT]; | ||
lamp_state_t colors[LAMP_MULTI_UPDATE_LAMP_COUNT]; | ||
} lamparray_multi_update_t; | ||
|
||
/** | ||
* \brief Gets LampArrayAttributesReport data | ||
*/ | ||
void lamparray_get_attributes(lamparray_attributes_t* data); | ||
|
||
/** | ||
* \brief Sets LampAttributesRequestReport data | ||
*/ | ||
void lamparray_set_attributes_response(uint16_t lamp_id); | ||
|
||
/** | ||
* \brief Gets LampAttributesResponseReport data | ||
*/ | ||
void lamparray_get_attributes_response(lamparray_attributes_response_t* data); | ||
|
||
/** | ||
* \brief Sets LampRangeUpdateReport data | ||
*/ | ||
void lamparray_set_range(lamparray_range_update_t* data); | ||
|
||
/** | ||
* \brief Sets LampMultiUpdateReport data | ||
*/ | ||
void lamparray_set_items(lamparray_multi_update_t* data); | ||
|
||
/** | ||
* \brief Sets LampArrayControlReport data | ||
*/ | ||
void lamparray_set_control_response(uint8_t autonomous); |
Oops, something went wrong.