summaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
authorWilba <wilba@wilba.tech>2022-11-10 07:46:44 +1100
committerGitHub <noreply@github.com>2022-11-10 07:46:44 +1100
commitbc6f8dc8b0822e5e03893eacffa42a7badb4c2fa (patch)
treeb1e8a219803f30b7347e479734a5c6e7352a5f30 /quantum
parent575b0e33fa47034f5cfaf6f7cd98570268efa0a2 (diff)
VIA V3 - The Custom UI Update (#18222)
Diffstat (limited to 'quantum')
-rw-r--r--quantum/dynamic_keymap.c56
-rw-r--r--quantum/dynamic_keymap.h6
-rw-r--r--quantum/via.c479
-rw-r--r--quantum/via.h94
4 files changed, 452 insertions, 183 deletions
diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c
index e9529ed14e..c406be4585 100644
--- a/quantum/dynamic_keymap.c
+++ b/quantum/dynamic_keymap.c
@@ -279,9 +279,8 @@ void dynamic_keymap_macro_send(uint8_t id) {
279 p = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR); 279 p = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR);
280 void *end = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE); 280 void *end = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE);
281 while (id > 0) { 281 while (id > 0) {
282 // If we are past the end of the buffer, then the buffer 282 // If we are past the end of the buffer, then there is
283 // contents are garbage, i.e. there were not DYNAMIC_KEYMAP_MACRO_COUNT 283 // no Nth macro in the buffer.
284 // nulls in the buffer.
285 if (p == end) { 284 if (p == end) {
286 return; 285 return;
287 } 286 }
@@ -291,9 +290,8 @@ void dynamic_keymap_macro_send(uint8_t id) {
291 ++p; 290 ++p;
292 } 291 }
293 292
294 // Send the macro string one or three chars at a time 293 // Send the macro string by making a temporary string.
295 // by making temporary 1 or 3 char strings 294 char data[8] = {0};
296 char data[4] = {0, 0, 0, 0};
297 // We already checked there was a null at the end of 295 // We already checked there was a null at the end of
298 // the buffer, so this cannot go past the end 296 // the buffer, so this cannot go past the end
299 while (1) { 297 while (1) {
@@ -303,14 +301,44 @@ void dynamic_keymap_macro_send(uint8_t id) {
303 if (data[0] == 0) { 301 if (data[0] == 0) {
304 break; 302 break;
305 } 303 }
306 // If the char is magic (tap, down, up), 304 if (data[0] == SS_QMK_PREFIX) {
307 // add the next char (key to use) and send a 3 char string. 305 // Get the code
308 if (data[0] == SS_TAP_CODE || data[0] == SS_DOWN_CODE || data[0] == SS_UP_CODE) { 306 data[1] = eeprom_read_byte(p++);
309 data[1] = data[0]; 307 // Unexpected null, abort.
310 data[0] = SS_QMK_PREFIX; 308 if (data[1] == 0) {
311 data[2] = eeprom_read_byte(p++); 309 return;
312 if (data[2] == 0) { 310 }
313 break; 311 if (data[1] == SS_TAP_CODE || data[1] == SS_DOWN_CODE || data[1] == SS_UP_CODE) {
312 // Get the keycode
313 data[2] = eeprom_read_byte(p++);
314 // Unexpected null, abort.
315 if (data[2] == 0) {
316 return;
317 }
318 // Null terminate
319 data[3] = 0;
320 } else if (data[1] == SS_DELAY_CODE) {
321 // Get the number and '|'
322 // At most this is 4 digits plus '|'
323 uint8_t i = 2;
324 while (1) {
325 data[i] = eeprom_read_byte(p++);
326 // Unexpected null, abort
327 if (data[i] == 0) {
328 return;
329 }
330 // Found '|', send it
331 if (data[i] == '|') {
332 data[i + 1] = 0;
333 break;
334 }
335 // If haven't found '|' by i==6 then
336 // number too big, abort
337 if (i == 6) {
338 return;
339 }
340 ++i;
341 }
314 } 342 }
315 } 343 }
316 send_string_with_delay(data, DYNAMIC_KEYMAP_MACRO_DELAY); 344 send_string_with_delay(data, DYNAMIC_KEYMAP_MACRO_DELAY);
diff --git a/quantum/dynamic_keymap.h b/quantum/dynamic_keymap.h
index 459b48d07a..806342efa3 100644
--- a/quantum/dynamic_keymap.h
+++ b/quantum/dynamic_keymap.h
@@ -54,6 +54,12 @@ void dynamic_keymap_set_buffer(uint16_t offset, uint16_t size, uint8_t *data);
54// strings, the last byte must be a null when at maximum capacity, 54// strings, the last byte must be a null when at maximum capacity,
55// and it not being null means the buffer can be considered in an 55// and it not being null means the buffer can be considered in an
56// invalid state. 56// invalid state.
57//
58// The buffer *may* contain less macro strings than the maximum.
59// This allows a higher maximum number of macros without requiring that
60// number of nulls to be in the buffer.
61// Note: dynamic_keymap_macro_get_count() returns the maximum that *can* be
62// stored, not the current count of macros in the buffer.
57 63
58uint8_t dynamic_keymap_macro_get_count(void); 64uint8_t dynamic_keymap_macro_get_count(void);
59uint16_t dynamic_keymap_macro_get_buffer_size(void); 65uint16_t dynamic_keymap_macro_get_buffer_size(void);
diff --git a/quantum/via.c b/quantum/via.c
index 5afbee5a30..12ef6c6b2f 100644
--- a/quantum/via.c
+++ b/quantum/via.c
@@ -22,26 +22,6 @@
22# error "DYNAMIC_KEYMAP_ENABLE is not enabled" 22# error "DYNAMIC_KEYMAP_ENABLE is not enabled"
23#endif 23#endif
24 24
25// If VIA_CUSTOM_LIGHTING_ENABLE is not defined, then VIA_QMK_BACKLIGHT_ENABLE is set
26// if BACKLIGHT_ENABLE is set, so handling of QMK Backlight values happens here by default.
27// if VIA_CUSTOM_LIGHTING_ENABLE is defined, then VIA_QMK_BACKLIGHT_ENABLE must be explicitly
28// set in keyboard-level config.h, so handling of QMK Backlight values happens here
29#if defined(BACKLIGHT_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE)
30# define VIA_QMK_BACKLIGHT_ENABLE
31#endif
32
33// If VIA_CUSTOM_LIGHTING_ENABLE is not defined, then VIA_QMK_RGBLIGHT_ENABLE is set
34// if RGBLIGHT_ENABLE is set, so handling of QMK RGBLIGHT values happens here by default.
35// If VIA_CUSTOM_LIGHTING_ENABLE is defined, then VIA_QMK_RGBLIGHT_ENABLE must be explicitly
36// set in keyboard-level config.h, so handling of QMK RGBLIGHT values happens here
37#if defined(RGBLIGHT_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE)
38# define VIA_QMK_RGBLIGHT_ENABLE
39#endif
40
41#if defined(RGB_MATRIX_ENABLE) && !defined(VIA_QMK_RGBLIGHT_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE)
42# define VIA_QMK_RGB_MATRIX_ENABLE
43#endif
44
45#include "quantum.h" 25#include "quantum.h"
46 26
47#include "via.h" 27#include "via.h"
@@ -51,22 +31,8 @@
51#include "eeprom.h" 31#include "eeprom.h"
52#include "version.h" // for QMK_BUILDDATE used in EEPROM magic 32#include "version.h" // for QMK_BUILDDATE used in EEPROM magic
53 33
54// Forward declare some helpers. 34#if defined(RGB_MATRIX_ENABLE)
55#if defined(VIA_QMK_BACKLIGHT_ENABLE)
56void via_qmk_backlight_set_value(uint8_t *data);
57void via_qmk_backlight_get_value(uint8_t *data);
58#endif
59
60#if defined(VIA_QMK_RGBLIGHT_ENABLE)
61void via_qmk_rgblight_set_value(uint8_t *data);
62void via_qmk_rgblight_get_value(uint8_t *data);
63#endif
64
65#if defined(VIA_QMK_RGB_MATRIX_ENABLE)
66# include <lib/lib8tion/lib8tion.h> 35# include <lib/lib8tion/lib8tion.h>
67void via_qmk_rgb_matrix_set_value(uint8_t *data);
68void via_qmk_rgb_matrix_get_value(uint8_t *data);
69void eeconfig_update_rgb_matrix(void);
70#endif 36#endif
71 37
72// Can be called in an overriding via_init_kb() to test if keyboard level code usage of 38// Can be called in an overriding via_init_kb() to test if keyboard level code usage of
@@ -155,6 +121,34 @@ void via_set_layout_options(uint32_t value) {
155 } 121 }
156} 122}
157 123
124#if defined(AUDIO_ENABLE)
125float via_device_indication_song[][2] = SONG(STARTUP_SOUND);
126#endif // AUDIO_ENABLE
127
128// Used by VIA to tell a device to flash LEDs (or do something else) when that
129// device becomes the active device being configured, on startup or switching
130// between devices. This function will be called six times, at 200ms interval,
131// with an incrementing value starting at zero. Since this function is called
132// an even number of times, it can call a toggle function and leave things in
133// the original state.
134__attribute__((weak)) void via_set_device_indication(uint8_t value) {
135#if defined(BACKLIGHT_ENABLE)
136 backlight_toggle();
137#endif // BACKLIGHT_ENABLE
138#if defined(RGBLIGHT_ENABLE)
139 rgblight_toggle_noeeprom();
140#endif // RGBLIGHT_ENABLE
141#if defined(RGB_MATRIX_ENABLE)
142 rgb_matrix_toggle_noeeprom();
143#endif // RGB_MATRIX_ENABLE
144#if defined(AUDIO_ENABLE)
145 if (value == 0) {
146 wait_ms(10);
147 PLAY_SONG(via_device_indication_song);
148 }
149#endif // AUDIO_ENABLE
150}
151
158// Called by QMK core to process VIA-specific keycodes. 152// Called by QMK core to process VIA-specific keycodes.
159bool process_record_via(uint16_t keycode, keyrecord_t *record) { 153bool process_record_via(uint16_t keycode, keyrecord_t *record) {
160 // Handle macros 154 // Handle macros
@@ -194,24 +188,96 @@ bool process_record_via(uint16_t keycode, keyrecord_t *record) {
194 return true; 188 return true;
195} 189}
196 190
197// Keyboard level code can override this to handle custom messages from VIA. 191//
198// See raw_hid_receive() implementation. 192// via_custom_value_command() has the default handling of custom values for Core modules.
193// If a keyboard is using the default Core modules, it does not need to be overridden,
194// the VIA keyboard definition will have matching channel/IDs.
195//
196// If a keyboard has some extra custom values, then via_custom_value_command_kb() can be
197// overridden to handle the extra custom values, leaving via_custom_value_command() to
198// handle the custom values for Core modules.
199//
200// If a keyboard has custom values and code that are overlapping with Core modules,
201// then via_custom_value_command() can be overridden and call the same functions
202// as the default implementation, or do whatever else is required.
203//
199// DO NOT call raw_hid_send() in the override function. 204// DO NOT call raw_hid_send() in the override function.
200__attribute__((weak)) void raw_hid_receive_kb(uint8_t *data, uint8_t length) { 205//
206
207// This is the default handler for "extra" custom values, i.e. keyboard-specific custom values
208// that are not handled by via_custom_value_command().
209__attribute__((weak)) void via_custom_value_command_kb(uint8_t *data, uint8_t length) {
210 // data = [ command_id, channel_id, value_id, value_data ]
201 uint8_t *command_id = &(data[0]); 211 uint8_t *command_id = &(data[0]);
202 *command_id = id_unhandled; 212 // Return the unhandled state
213 *command_id = id_unhandled;
203} 214}
204 215
205// VIA handles received HID messages first, and will route to 216// This is the default handler for custom value commands.
206// raw_hid_receive_kb() for command IDs that are not handled here. 217// It routes commands with channel IDs to command handlers as such:
207// This gives the keyboard code level the ability to handle the command
208// specifically.
209// 218//
210// raw_hid_send() is called at the end, with the same buffer, which was 219// id_qmk_backlight_channel -> via_qmk_backlight_command()
211// possibly modified with returned values. 220// id_qmk_rgblight_channel -> via_qmk_rgblight_command()
221// id_qmk_rgb_matrix_channel -> via_qmk_rgb_matrix_command()
222// id_qmk_audio_channel -> via_qmk_audio_command()
223//
224__attribute__((weak)) void via_custom_value_command(uint8_t *data, uint8_t length) {
225 // data = [ command_id, channel_id, value_id, value_data ]
226 uint8_t *channel_id = &(data[1]);
227
228#if defined(BACKLIGHT_ENABLE)
229 if (*channel_id == id_qmk_backlight_channel) {
230 via_qmk_backlight_command(data, length);
231 return;
232 }
233#endif // BACKLIGHT_ENABLE
234
235#if defined(RGBLIGHT_ENABLE)
236 if (*channel_id == id_qmk_rgblight_channel) {
237 via_qmk_rgblight_command(data, length);
238 return;
239 }
240#endif // RGBLIGHT_ENABLE
241
242#if defined(RGB_MATRIX_ENABLE)
243 if (*channel_id == id_qmk_rgb_matrix_channel) {
244 via_qmk_rgb_matrix_command(data, length);
245 return;
246 }
247#endif // RGBLIGHT_ENABLE
248
249#if defined(AUDIO_ENABLE)
250 if (*channel_id == id_qmk_audio_channel) {
251 via_qmk_audio_command(data, length);
252 return;
253 }
254#endif // AUDIO_ENABLE
255
256 (void)channel_id; // force use of variable
257
258 // If we haven't returned before here, then let the keyboard level code
259 // handle this, if it is overridden, otherwise by default, this will
260 // return the unhandled state.
261 via_custom_value_command_kb(data, length);
262}
263
264// Keyboard level code can override this, but shouldn't need to.
265// Controlling custom features should be done by overriding
266// via_custom_value_command_kb() instead.
267__attribute__((weak)) bool via_command_kb(uint8_t *data, uint8_t length) {
268 return false;
269}
270
212void raw_hid_receive(uint8_t *data, uint8_t length) { 271void raw_hid_receive(uint8_t *data, uint8_t length) {
213 uint8_t *command_id = &(data[0]); 272 uint8_t *command_id = &(data[0]);
214 uint8_t *command_data = &(data[1]); 273 uint8_t *command_data = &(data[1]);
274
275 // If via_command_kb() returns true, the command was fully
276 // handled, including calling raw_hid_send()
277 if (via_command_kb(data, length)) {
278 return;
279 }
280
215 switch (*command_id) { 281 switch (*command_id) {
216 case id_get_protocol_version: { 282 case id_get_protocol_version: {
217 command_data[0] = VIA_PROTOCOL_VERSION >> 8; 283 command_data[0] = VIA_PROTOCOL_VERSION >> 8;
@@ -237,7 +303,10 @@ void raw_hid_receive(uint8_t *data, uint8_t length) {
237 break; 303 break;
238 } 304 }
239 case id_switch_matrix_state: { 305 case id_switch_matrix_state: {
240#if ((MATRIX_COLS / 8 + 1) * MATRIX_ROWS <= 28) 306// Round up to the nearest number of bytes required to hold row state.
307// Multiply by number of rows to get the required size in bytes.
308// Guard against this being too big for the HID message.
309#if (((MATRIX_COLS + 7) / 8) * MATRIX_ROWS <= 28)
241 uint8_t i = 1; 310 uint8_t i = 1;
242 for (uint8_t row = 0; row < MATRIX_ROWS; row++) { 311 for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
243 matrix_row_t value = matrix_get_row(row); 312 matrix_row_t value = matrix_get_row(row);
@@ -255,8 +324,18 @@ void raw_hid_receive(uint8_t *data, uint8_t length) {
255#endif 324#endif
256 break; 325 break;
257 } 326 }
327 case id_firmware_version: {
328 uint32_t value = VIA_FIRMWARE_VERSION;
329 command_data[1] = (value >> 24) & 0xFF;
330 command_data[2] = (value >> 16) & 0xFF;
331 command_data[3] = (value >> 8) & 0xFF;
332 command_data[4] = value & 0xFF;
333 break;
334 }
258 default: { 335 default: {
259 raw_hid_receive_kb(data, length); 336 // The value ID is not known
337 // Return the unhandled state
338 *command_id = id_unhandled;
260 break; 339 break;
261 } 340 }
262 } 341 }
@@ -269,8 +348,15 @@ void raw_hid_receive(uint8_t *data, uint8_t length) {
269 via_set_layout_options(value); 348 via_set_layout_options(value);
270 break; 349 break;
271 } 350 }
351 case id_device_indication: {
352 uint8_t value = command_data[1];
353 via_set_device_indication(value);
354 break;
355 }
272 default: { 356 default: {
273 raw_hid_receive_kb(data, length); 357 // The value ID is not known
358 // Return the unhandled state
359 *command_id = id_unhandled;
274 break; 360 break;
275 } 361 }
276 } 362 }
@@ -290,61 +376,10 @@ void raw_hid_receive(uint8_t *data, uint8_t length) {
290 dynamic_keymap_reset(); 376 dynamic_keymap_reset();
291 break; 377 break;
292 } 378 }
293 case id_lighting_set_value: { 379 case id_custom_set_value:
294#if defined(VIA_QMK_BACKLIGHT_ENABLE) 380 case id_custom_get_value:
295 via_qmk_backlight_set_value(command_data); 381 case id_custom_save: {
296#endif 382 via_custom_value_command(data, length);
297#if defined(VIA_QMK_RGBLIGHT_ENABLE)
298 via_qmk_rgblight_set_value(command_data);
299#endif
300#if defined(VIA_QMK_RGB_MATRIX_ENABLE)
301 via_qmk_rgb_matrix_set_value(command_data);
302#endif
303#if defined(VIA_CUSTOM_LIGHTING_ENABLE)
304 raw_hid_receive_kb(data, length);
305#endif
306#if !defined(VIA_QMK_BACKLIGHT_ENABLE) && !defined(VIA_QMK_RGBLIGHT_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE) && !defined(VIA_QMK_RGB_MATRIX_ENABLE)
307 // Return the unhandled state
308 *command_id = id_unhandled;
309#endif
310 break;
311 }
312 case id_lighting_get_value: {
313#if defined(VIA_QMK_BACKLIGHT_ENABLE)
314 via_qmk_backlight_get_value(command_data);
315#endif
316#if defined(VIA_QMK_RGBLIGHT_ENABLE)
317 via_qmk_rgblight_get_value(command_data);
318#endif
319#if defined(VIA_QMK_RGB_MATRIX_ENABLE)
320 via_qmk_rgb_matrix_get_value(command_data);
321#endif
322#if defined(VIA_CUSTOM_LIGHTING_ENABLE)
323 raw_hid_receive_kb(data, length);
324#endif
325#if !defined(VIA_QMK_BACKLIGHT_ENABLE) && !defined(VIA_QMK_RGBLIGHT_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE) && !defined(VIA_QMK_RGB_MATRIX_ENABLE)
326 // Return the unhandled state
327 *command_id = id_unhandled;
328#endif
329 break;
330 }
331 case id_lighting_save: {
332#if defined(VIA_QMK_BACKLIGHT_ENABLE)
333 eeconfig_update_backlight_current();
334#endif
335#if defined(VIA_QMK_RGBLIGHT_ENABLE)
336 eeconfig_update_rgblight_current();
337#endif
338#if defined(VIA_QMK_RGB_MATRIX_ENABLE)
339 eeconfig_update_rgb_matrix();
340#endif
341#if defined(VIA_CUSTOM_LIGHTING_ENABLE)
342 raw_hid_receive_kb(data, length);
343#endif
344#if !defined(VIA_QMK_BACKLIGHT_ENABLE) && !defined(VIA_QMK_RGBLIGHT_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE) && !defined(VIA_QMK_RGB_MATRIX_ENABLE)
345 // Return the unhandled state
346 *command_id = id_unhandled;
347#endif
348 break; 383 break;
349 } 384 }
350#ifdef VIA_EEPROM_ALLOW_RESET 385#ifdef VIA_EEPROM_ALLOW_RESET
@@ -421,13 +456,39 @@ void raw_hid_receive(uint8_t *data, uint8_t length) {
421 raw_hid_send(data, length); 456 raw_hid_send(data, length);
422} 457}
423 458
424#if defined(VIA_QMK_BACKLIGHT_ENABLE) 459#if defined(BACKLIGHT_ENABLE)
460
461void via_qmk_backlight_command(uint8_t *data, uint8_t length) {
462 // data = [ command_id, channel_id, value_id, value_data ]
463 uint8_t *command_id = &(data[0]);
464 uint8_t *value_id_and_data = &(data[2]);
465
466 switch (*command_id) {
467 case id_custom_set_value: {
468 via_qmk_backlight_set_value(value_id_and_data);
469 break;
470 }
471 case id_custom_get_value: {
472 via_qmk_backlight_get_value(value_id_and_data);
473 break;
474 }
475 case id_custom_save: {
476 via_qmk_backlight_save();
477 break;
478 }
479 default: {
480 *command_id = id_unhandled;
481 break;
482 }
483 }
484}
425 485
426# if BACKLIGHT_LEVELS == 0 486# if BACKLIGHT_LEVELS == 0
427# error BACKLIGHT_LEVELS == 0 487# error BACKLIGHT_LEVELS == 0
428# endif 488# endif
429 489
430void via_qmk_backlight_get_value(uint8_t *data) { 490void via_qmk_backlight_get_value(uint8_t *data) {
491 // data = [ value_id, value_data ]
431 uint8_t *value_id = &(data[0]); 492 uint8_t *value_id = &(data[0]);
432 uint8_t *value_data = &(data[1]); 493 uint8_t *value_data = &(data[1]);
433 switch (*value_id) { 494 switch (*value_id) {
@@ -448,6 +509,7 @@ void via_qmk_backlight_get_value(uint8_t *data) {
448} 509}
449 510
450void via_qmk_backlight_set_value(uint8_t *data) { 511void via_qmk_backlight_set_value(uint8_t *data) {
512 // data = [ value_id, value_data ]
451 uint8_t *value_id = &(data[0]); 513 uint8_t *value_id = &(data[0]);
452 uint8_t *value_data = &(data[1]); 514 uint8_t *value_data = &(data[1]);
453 switch (*value_id) { 515 switch (*value_id) {
@@ -469,14 +531,44 @@ void via_qmk_backlight_set_value(uint8_t *data) {
469 } 531 }
470} 532}
471 533
472#endif // #if defined(VIA_QMK_BACKLIGHT_ENABLE) 534void via_qmk_backlight_save(void) {
535 eeconfig_update_backlight_current();
536}
537
538#endif // BACKLIGHT_ENABLE
473 539
474#if defined(VIA_QMK_RGBLIGHT_ENABLE) 540#if defined(RGBLIGHT_ENABLE)
475# ifndef RGBLIGHT_LIMIT_VAL 541# ifndef RGBLIGHT_LIMIT_VAL
476# define RGBLIGHT_LIMIT_VAL 255 542# define RGBLIGHT_LIMIT_VAL 255
477# endif 543# endif
478 544
545void via_qmk_rgblight_command(uint8_t *data, uint8_t length) {
546 // data = [ command_id, channel_id, value_id, value_data ]
547 uint8_t *command_id = &(data[0]);
548 uint8_t *value_id_and_data = &(data[2]);
549
550 switch (*command_id) {
551 case id_custom_set_value: {
552 via_qmk_rgblight_set_value(value_id_and_data);
553 break;
554 }
555 case id_custom_get_value: {
556 via_qmk_rgblight_get_value(value_id_and_data);
557 break;
558 }
559 case id_custom_save: {
560 via_qmk_rgblight_save();
561 break;
562 }
563 default: {
564 *command_id = id_unhandled;
565 break;
566 }
567 }
568}
569
479void via_qmk_rgblight_get_value(uint8_t *data) { 570void via_qmk_rgblight_get_value(uint8_t *data) {
571 // data = [ value_id, value_data ]
480 uint8_t *value_id = &(data[0]); 572 uint8_t *value_id = &(data[0]);
481 uint8_t *value_data = &(data[1]); 573 uint8_t *value_data = &(data[1]);
482 switch (*value_id) { 574 switch (*value_id) {
@@ -485,7 +577,7 @@ void via_qmk_rgblight_get_value(uint8_t *data) {
485 break; 577 break;
486 } 578 }
487 case id_qmk_rgblight_effect: { 579 case id_qmk_rgblight_effect: {
488 value_data[0] = rgblight_get_mode(); 580 value_data[0] = rgblight_is_enabled() ? rgblight_get_mode() : 0;
489 break; 581 break;
490 } 582 }
491 case id_qmk_rgblight_effect_speed: { 583 case id_qmk_rgblight_effect_speed: {
@@ -501,6 +593,7 @@ void via_qmk_rgblight_get_value(uint8_t *data) {
501} 593}
502 594
503void via_qmk_rgblight_set_value(uint8_t *data) { 595void via_qmk_rgblight_set_value(uint8_t *data) {
596 // data = [ value_id, value_data ]
504 uint8_t *value_id = &(data[0]); 597 uint8_t *value_id = &(data[0]);
505 uint8_t *value_data = &(data[1]); 598 uint8_t *value_data = &(data[1]);
506 switch (*value_id) { 599 switch (*value_id) {
@@ -509,11 +602,11 @@ void via_qmk_rgblight_set_value(uint8_t *data) {
509 break; 602 break;
510 } 603 }
511 case id_qmk_rgblight_effect: { 604 case id_qmk_rgblight_effect: {
512 rgblight_mode_noeeprom(value_data[0]);
513 if (value_data[0] == 0) { 605 if (value_data[0] == 0) {
514 rgblight_disable_noeeprom(); 606 rgblight_disable_noeeprom();
515 } else { 607 } else {
516 rgblight_enable_noeeprom(); 608 rgblight_enable_noeeprom();
609 rgblight_mode_noeeprom(value_data[0]);
517 } 610 }
518 break; 611 break;
519 } 612 }
@@ -528,92 +621,168 @@ void via_qmk_rgblight_set_value(uint8_t *data) {
528 } 621 }
529} 622}
530 623
531#endif // #if defined(VIA_QMK_RGBLIGHT_ENABLE) 624void via_qmk_rgblight_save(void) {
625 eeconfig_update_rgblight_current();
626}
627
628#endif // QMK_RGBLIGHT_ENABLE
532 629
533#if defined(VIA_QMK_RGB_MATRIX_ENABLE) 630#if defined(RGB_MATRIX_ENABLE)
534 631
535# if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX 632# if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX
536# undef RGB_MATRIX_MAXIMUM_BRIGHTNESS 633# undef RGB_MATRIX_MAXIMUM_BRIGHTNESS
537# define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX 634# define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX
538# endif 635# endif
539 636
540// VIA supports only 4 discrete values for effect speed; map these to some 637void via_qmk_rgb_matrix_command(uint8_t *data, uint8_t length) {
541// useful speed values for RGB Matrix. 638 // data = [ command_id, channel_id, value_id, value_data ]
542enum speed_values { 639 uint8_t *command_id = &(data[0]);
543 RGBLIGHT_SPEED_0 = UINT8_MAX / 16, // not 0 to avoid really slow effects 640 uint8_t *value_id_and_data = &(data[2]);
544 RGBLIGHT_SPEED_1 = UINT8_MAX / 4,
545 RGBLIGHT_SPEED_2 = UINT8_MAX / 2, // matches the default value
546 RGBLIGHT_SPEED_3 = UINT8_MAX / 4 * 3, // UINT8_MAX is really fast
547};
548
549static uint8_t speed_from_rgblight(uint8_t rgblight_speed) {
550 switch (rgblight_speed) {
551 case 0:
552 return RGBLIGHT_SPEED_0;
553 case 1:
554 return RGBLIGHT_SPEED_1;
555 case 2:
556 default:
557 return RGBLIGHT_SPEED_2;
558 case 3:
559 return RGBLIGHT_SPEED_3;
560 }
561}
562 641
563static uint8_t speed_to_rgblight(uint8_t rgb_matrix_speed) { 642 switch (*command_id) {
564 if (rgb_matrix_speed < ((RGBLIGHT_SPEED_0 + RGBLIGHT_SPEED_1) / 2)) { 643 case id_custom_set_value: {
565 return 0; 644 via_qmk_rgb_matrix_set_value(value_id_and_data);
566 } else if (rgb_matrix_speed < ((RGBLIGHT_SPEED_1 + RGBLIGHT_SPEED_2) / 2)) { 645 break;
567 return 1; 646 }
568 } else if (rgb_matrix_speed < ((RGBLIGHT_SPEED_2 + RGBLIGHT_SPEED_3) / 2)) { 647 case id_custom_get_value: {
569 return 2; 648 via_qmk_rgb_matrix_get_value(value_id_and_data);
570 } else { 649 break;
571 return 3; 650 }
651 case id_custom_save: {
652 via_qmk_rgb_matrix_save();
653 break;
654 }
655 default: {
656 *command_id = id_unhandled;
657 break;
658 }
572 } 659 }
573} 660}
574 661
575void via_qmk_rgb_matrix_get_value(uint8_t *data) { 662void via_qmk_rgb_matrix_get_value(uint8_t *data) {
663 // data = [ value_id, value_data ]
576 uint8_t *value_id = &(data[0]); 664 uint8_t *value_id = &(data[0]);
577 uint8_t *value_data = &(data[1]); 665 uint8_t *value_data = &(data[1]);
666
578 switch (*value_id) { 667 switch (*value_id) {
579 case id_qmk_rgblight_brightness: 668 case id_qmk_rgb_matrix_brightness: {
580 value_data[0] = ((uint16_t)rgb_matrix_get_val() * UINT8_MAX) / RGB_MATRIX_MAXIMUM_BRIGHTNESS; 669 value_data[0] = ((uint16_t)rgb_matrix_get_val() * UINT8_MAX) / RGB_MATRIX_MAXIMUM_BRIGHTNESS;
581 break; 670 break;
582 case id_qmk_rgblight_effect: 671 }
583 value_data[0] = rgb_matrix_get_mode(); 672 case id_qmk_rgb_matrix_effect: {
673 value_data[0] = rgb_matrix_is_enabled() ? rgb_matrix_get_mode() : 0;
584 break; 674 break;
585 case id_qmk_rgblight_effect_speed: 675 }
586 value_data[0] = speed_to_rgblight(rgb_matrix_get_speed()); 676 case id_qmk_rgb_matrix_effect_speed: {
677 value_data[0] = rgb_matrix_get_speed();
587 break; 678 break;
588 case id_qmk_rgblight_color: 679 }
680 case id_qmk_rgb_matrix_color: {
589 value_data[0] = rgb_matrix_get_hue(); 681 value_data[0] = rgb_matrix_get_hue();
590 value_data[1] = rgb_matrix_get_sat(); 682 value_data[1] = rgb_matrix_get_sat();
591 break; 683 break;
684 }
592 } 685 }
593} 686}
594 687
595void via_qmk_rgb_matrix_set_value(uint8_t *data) { 688void via_qmk_rgb_matrix_set_value(uint8_t *data) {
689 // data = [ value_id, value_data ]
596 uint8_t *value_id = &(data[0]); 690 uint8_t *value_id = &(data[0]);
597 uint8_t *value_data = &(data[1]); 691 uint8_t *value_data = &(data[1]);
598 switch (*value_id) { 692 switch (*value_id) {
599 case id_qmk_rgblight_brightness: 693 case id_qmk_rgb_matrix_brightness: {
600 rgb_matrix_sethsv_noeeprom(rgb_matrix_get_hue(), rgb_matrix_get_sat(), scale8(value_data[0], RGB_MATRIX_MAXIMUM_BRIGHTNESS)); 694 rgb_matrix_sethsv_noeeprom(rgb_matrix_get_hue(), rgb_matrix_get_sat(), scale8(value_data[0], RGB_MATRIX_MAXIMUM_BRIGHTNESS));
601 break; 695 break;
602 case id_qmk_rgblight_effect: 696 }
603 rgb_matrix_mode_noeeprom(value_data[0]); 697 case id_qmk_rgb_matrix_effect: {
604 if (value_data[0] == 0) { 698 if (value_data[0] == 0) {
605 rgb_matrix_disable_noeeprom(); 699 rgb_matrix_disable_noeeprom();
606 } else { 700 } else {
607 rgb_matrix_enable_noeeprom(); 701 rgb_matrix_enable_noeeprom();
702 rgb_matrix_mode_noeeprom(value_data[0]);
608 } 703 }
609 break; 704 break;
610 case id_qmk_rgblight_effect_speed: 705 }
611 rgb_matrix_set_speed_noeeprom(speed_from_rgblight(value_data[0])); 706 case id_qmk_rgb_matrix_effect_speed: {
707 rgblight_set_speed_noeeprom(value_data[0]);
708 break;
709 }
710 case id_qmk_rgb_matrix_color: {
711 rgblight_sethsv_noeeprom(value_data[0], value_data[1], rgb_matrix_get_val());
712 break;
713 }
714 }
715}
716
717void via_qmk_rgb_matrix_save(void) {
718 eeconfig_update_rgb_matrix();
719}
720
721#endif // RGB_MATRIX_ENABLE
722
723#if defined(AUDIO_ENABLE)
724
725extern audio_config_t audio_config;
726
727void via_qmk_audio_command(uint8_t *data, uint8_t length) {
728 // data = [ command_id, channel_id, value_id, value_data ]
729 uint8_t *command_id = &(data[0]);
730 uint8_t *value_id_and_data = &(data[2]);
731
732 switch (*command_id) {
733 case id_custom_set_value: {
734 via_qmk_audio_set_value(value_id_and_data);
735 break;
736 }
737 case id_custom_get_value: {
738 via_qmk_audio_get_value(value_id_and_data);
739 break;
740 }
741 case id_custom_save: {
742 via_qmk_audio_save();
743 break;
744 }
745 default: {
746 *command_id = id_unhandled;
747 break;
748 }
749 }
750}
751
752void via_qmk_audio_get_value(uint8_t *data) {
753 // data = [ value_id, value_data ]
754 uint8_t *value_id = &(data[0]);
755 uint8_t *value_data = &(data[1]);
756 switch (*value_id) {
757 case id_qmk_audio_enable: {
758 value_data[0] = audio_config.enable ? 1 : 0;
612 break; 759 break;
613 case id_qmk_rgblight_color: 760 }
614 rgb_matrix_sethsv_noeeprom(value_data[0], value_data[1], rgb_matrix_get_val()); 761 case id_qmk_audio_clicky_enable: {
762 value_data[0] = audio_config.clicky_enable ? 1 : 0;
615 break; 763 break;
764 }
616 } 765 }
617} 766}
618 767
619#endif // #if defined(VIA_QMK_RGB_MATRIX_ENABLE) 768void via_qmk_audio_set_value(uint8_t *data) {
769 // data = [ value_id, value_data ]
770 uint8_t *value_id = &(data[0]);
771 uint8_t *value_data = &(data[1]);
772 switch (*value_id) {
773 case id_qmk_audio_enable: {
774 audio_config.enable = value_data[0] ? 1 : 0;
775 break;
776 }
777 case id_qmk_audio_clicky_enable: {
778 audio_config.clicky_enable = value_data[0] ? 1 : 0;
779 break;
780 }
781 }
782}
783
784void via_qmk_audio_save(void) {
785 eeconfig_update_audio(audio_config.raw);
786}
787
788#endif // QMK_AUDIO_ENABLE
diff --git a/quantum/via.h b/quantum/via.h
index 84b264903f..eca0733525 100644
--- a/quantum/via.h
+++ b/quantum/via.h
@@ -60,6 +60,16 @@
60// so VIA Configurator can detect compatible firmware. 60// so VIA Configurator can detect compatible firmware.
61#define VIA_PROTOCOL_VERSION 0x000B 61#define VIA_PROTOCOL_VERSION 0x000B
62 62
63// This is a version number for the firmware for the keyboard.
64// It can be used to ensure the VIA keyboard definition and the firmware
65// have the same version, especially if there are changes to custom values.
66// Define this in config.h to override and bump this number.
67// This is *not* required if the keyboard is only using basic functionality
68// and not using custom values for lighting, rotary encoders, etc.
69#ifndef VIA_FIRMWARE_VERSION
70# define VIA_FIRMWARE_VERSION 0x00000000
71#endif
72
63enum via_command_id { 73enum via_command_id {
64 id_get_protocol_version = 0x01, // always 0x01 74 id_get_protocol_version = 0x01, // always 0x01
65 id_get_keyboard_value = 0x02, 75 id_get_keyboard_value = 0x02,
@@ -67,9 +77,9 @@ enum via_command_id {
67 id_dynamic_keymap_get_keycode = 0x04, 77 id_dynamic_keymap_get_keycode = 0x04,
68 id_dynamic_keymap_set_keycode = 0x05, 78 id_dynamic_keymap_set_keycode = 0x05,
69 id_dynamic_keymap_reset = 0x06, 79 id_dynamic_keymap_reset = 0x06,
70 id_lighting_set_value = 0x07, 80 id_custom_set_value = 0x07,
71 id_lighting_get_value = 0x08, 81 id_custom_get_value = 0x08,
72 id_lighting_save = 0x09, 82 id_custom_save = 0x09,
73 id_eeprom_reset = 0x0A, 83 id_eeprom_reset = 0x0A,
74 id_bootloader_jump = 0x0B, 84 id_bootloader_jump = 0x0B,
75 id_dynamic_keymap_macro_get_count = 0x0C, 85 id_dynamic_keymap_macro_get_count = 0x0C,
@@ -86,21 +96,43 @@ enum via_command_id {
86}; 96};
87 97
88enum via_keyboard_value_id { 98enum via_keyboard_value_id {
89 id_uptime = 0x01, // 99 id_uptime = 0x01,
90 id_layout_options = 0x02, 100 id_layout_options = 0x02,
91 id_switch_matrix_state = 0x03 101 id_switch_matrix_state = 0x03,
102 id_firmware_version = 0x04,
103 id_device_indication = 0x05,
92}; 104};
93 105
94enum via_lighting_value { 106enum via_channel_id {
95 // QMK BACKLIGHT 107 id_custom_channel = 0,
96 id_qmk_backlight_brightness = 0x09, 108 id_qmk_backlight_channel = 1,
97 id_qmk_backlight_effect = 0x0A, 109 id_qmk_rgblight_channel = 2,
110 id_qmk_rgb_matrix_channel = 3,
111 id_qmk_audio_channel = 4,
112};
113
114enum via_qmk_backlight_value {
115 id_qmk_backlight_brightness = 1,
116 id_qmk_backlight_effect = 2,
117};
118
119enum via_qmk_rgblight_value {
120 id_qmk_rgblight_brightness = 1,
121 id_qmk_rgblight_effect = 2,
122 id_qmk_rgblight_effect_speed = 3,
123 id_qmk_rgblight_color = 4,
124};
125
126enum via_qmk_rgb_matrix_value {
127 id_qmk_rgb_matrix_brightness = 1,
128 id_qmk_rgb_matrix_effect = 2,
129 id_qmk_rgb_matrix_effect_speed = 3,
130 id_qmk_rgb_matrix_color = 4,
131};
98 132
99 // QMK RGBLIGHT 133enum via_qmk_audio_value {
100 id_qmk_rgblight_brightness = 0x80, 134 id_qmk_audio_enable = 1,
101 id_qmk_rgblight_effect = 0x81, 135 id_qmk_audio_clicky_enable = 2,
102 id_qmk_rgblight_effect_speed = 0x82,
103 id_qmk_rgblight_color = 0x83,
104}; 136};
105 137
106enum via_keycodes { 138enum via_keycodes {
@@ -160,5 +192,39 @@ uint32_t via_get_layout_options(void);
160void via_set_layout_options(uint32_t value); 192void via_set_layout_options(uint32_t value);
161void via_set_layout_options_kb(uint32_t value); 193void via_set_layout_options_kb(uint32_t value);
162 194
195// Used by VIA to tell a device to flash LEDs (or do something else) when that
196// device becomes the active device being configured, on startup or switching
197// between devices.
198void via_set_device_indication(uint8_t value);
199
163// Called by QMK core to process VIA-specific keycodes. 200// Called by QMK core to process VIA-specific keycodes.
164bool process_record_via(uint16_t keycode, keyrecord_t *record); 201bool process_record_via(uint16_t keycode, keyrecord_t *record);
202
203// These are made external so that keyboard level custom value handlers can use them.
204#if defined(BACKLIGHT_ENABLE)
205void via_qmk_backlight_command(uint8_t *data, uint8_t length);
206void via_qmk_backlight_set_value(uint8_t *data);
207void via_qmk_backlight_get_value(uint8_t *data);
208void via_qmk_backlight_save(void);
209#endif
210
211#if defined(RGBLIGHT_ENABLE)
212void via_qmk_rgblight_command(uint8_t *data, uint8_t length);
213void via_qmk_rgblight_set_value(uint8_t *data);
214void via_qmk_rgblight_get_value(uint8_t *data);
215void via_qmk_rgblight_save(void);
216#endif
217
218#if defined(RGB_MATRIX_ENABLE)
219void via_qmk_rgb_matrix_command(uint8_t *data, uint8_t length);
220void via_qmk_rgb_matrix_set_value(uint8_t *data);
221void via_qmk_rgb_matrix_get_value(uint8_t *data);
222void via_qmk_rgb_matrix_save(void);
223#endif
224
225#if defined(AUDIO_ENABLE)
226void via_qmk_audio_command(uint8_t *data, uint8_t length);
227void via_qmk_audio_set_value(uint8_t *data);
228void via_qmk_audio_get_value(uint8_t *data);
229void via_qmk_audio_save(void);
230#endif \ No newline at end of file