Skip to content

Commit

Permalink
Initial PoC for Dynamic lighting support
Browse files Browse the repository at this point in the history
  • Loading branch information
zvecr committed Dec 26, 2023
1 parent 592a2d2 commit 90f9743
Show file tree
Hide file tree
Showing 13 changed files with 904 additions and 0 deletions.
1 change: 1 addition & 0 deletions builddefs/generic_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ GENERIC_FEATURES = \
HAPTIC \
KEY_LOCK \
KEY_OVERRIDE \
LAMPARRAY \
LEADER \
MAGIC \
MOUSEKEY \
Expand Down
23 changes: 23 additions & 0 deletions lib/python/qmk/cli/generate/config_h.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,27 @@ def generate_matrix_masked(kb_info_json, config_h_lines):
config_h_lines.append(generate_define('MATRIX_MASKED'))


def generate_estimated_dimensions(kb_info_json, config_h_lines):
"""Try and guess physical keyboard dimensions from the declared layouts
"""
if 'layouts' in kb_info_json:
width = 0
height = 0
for layout_data in kb_info_json['layouts'].values():
for key in layout_data['layout']:
x = key.get('x', 0)
y = key.get('y', 0)
w = key.get('w', 1)
h = key.get('h', 1)

width = max(width, x + w)
height = max(height, y + h)

# sizes are in micrometers - assume 1u = 19.05mm
config_h_lines.append(generate_define('ESTIMATED_KEYBOARD_WIDTH', width * 19050))
config_h_lines.append(generate_define('ESTIMATED_KEYBOARD_HEIGHT', height * 19050))


def generate_config_items(kb_info_json, config_h_lines):
"""Iterate through the info_config map to generate basic config values.
"""
Expand Down Expand Up @@ -202,6 +223,8 @@ def generate_config_h(cli):

generate_matrix_masked(kb_info_json, config_h_lines)

generate_estimated_dimensions(kb_info_json, config_h_lines)

if 'matrix_pins' in kb_info_json:
config_h_lines.append(matrix_pins(kb_info_json['matrix_pins']))

Expand Down
10 changes: 10 additions & 0 deletions quantum/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef WPM_ENABLE
# include "wpm.h"
#endif
#ifdef LAMPARRAY_ENABLE
# include "lamparray.h"
#endif

static uint32_t last_input_modification_time = 0;
uint32_t last_input_activity_time(void) {
Expand Down Expand Up @@ -407,6 +410,9 @@ void keyboard_init(void) {
#ifdef SPLIT_KEYBOARD
split_pre_init();
#endif
#ifdef LAMPARRAY_ENABLE
lamparray_init();
#endif
#ifdef ENCODER_ENABLE
encoder_init();
#endif
Expand Down Expand Up @@ -629,6 +635,10 @@ void quantum_task(void) {
#ifdef SECURE_ENABLE
secure_task();
#endif

#ifdef LAMPARRAY_ENABLE
lamparray_task();
#endif
}

/** \brief Main task that is repeatedly called as fast as possible. */
Expand Down
219 changes: 219 additions & 0 deletions quantum/lamparray/lamparray.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// Copyright 2024 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string.h> // for memcpy
#include "lamparray.h"
#include "lamparray_surface.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

#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)
#endif

//****************************************************************************
// utils

/**
* \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 infrequently.
* This is currently accepted as a limitation as there is no method to invalidate the hosts view of the data.
*/
uint8_t lamparray_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);
(void)keycode;
#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 + 1;
}
#endif
return 0;
}

//****************************************************************************
// cache

static uint8_t input_binding_cache[LAMPARRAY_LAMP_COUNT];

void lamparray_update_cache(void) {
for (uint8_t lamp_id = 0; lamp_id < LAMPARRAY_LAMP_COUNT; lamp_id++) {
input_binding_cache[lamp_id] = lamparray_get_lamp_binding_impl(lamp_id);
}
}

uint8_t lamparray_get_lamp_binding(uint16_t lamp_id) {
return input_binding_cache[lamp_id];
}

//****************************************************************************
// queue

#ifndef LAMPARRAY_REQUEST_QUEUE_SIZE
# define LAMPARRAY_REQUEST_QUEUE_SIZE 5
#endif

universal_lamparray_response_t request_queue[LAMPARRAY_REQUEST_QUEUE_SIZE] = {0};
uint8_t queue_size = 0;

void lamparray_queue_request(universal_lamparray_response_t* report) {
// get next slot
universal_lamparray_response_t* target = &request_queue[queue_size++];

// copy data
memcpy(target, report, sizeof(universal_lamparray_response_t));
}

void lamparray_handle_queue(void) {
for (uint8_t id = 0; id < queue_size; id++) {
universal_lamparray_response_t* report = &request_queue[id];
switch (report->report_id) {
case LAMPARRAY_REPORT_ID_RANGE_UPDATE:
lamparray_set_range(&report->range_update);
break;
case LAMPARRAY_REPORT_ID_MULTI_UPDATE:
lamparray_set_items(&report->multi_update);
break;
case LAMPARRAY_REPORT_ID_CONTROL:
lamparray_set_control_response(report->autonomous);
break;
}
}
queue_size = 0;
}

//****************************************************************************
// impl

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_attributes_response(lamparray_attributes_response_t* data) {
data->lamp_id = cur_lamp_id;
data->update_latency = 1000;
data->is_programmable = 1;
data->input_binding = lamparray_get_lamp_binding(cur_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_impl(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_surface_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_surface_set_item(index, data->color);
}

// Batch update complete - 26.11 Updating Lamp State
if (data->flags & LAMP_UPDATE_FLAG_COMPLETE) {
lamparray_surface_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_surface_set_item(data->ids[i], data->colors[i]);
}

// Batch update complete - 26.11 Updating Lamp State
if (data->flags & LAMP_UPDATE_FLAG_COMPLETE) {
lamparray_surface_update_finished();
}
}

//****************************************************************************
// feature hooks

void lamparray_init(void) {
lamparray_update_cache();
}

void lamparray_task(void) {
lamparray_handle_queue();

// TODO: regen cache if dynamic keymap updated?
uint16_t temp = 0;
if (!++temp) lamparray_update_cache();
}

// TODO: SRC += ...
#ifdef RGB_MATRIX_ENABLE
# include "lamparray_rgb_matrix.c"
#endif
Loading

0 comments on commit 90f9743

Please sign in to comment.