[Keyboard] Add Viendi8L (#17943)
Co-authored-by: Ryan <fauxpark@gmail.com>
This commit is contained in:
parent
90c4350af7
commit
9898d6f4b0
65
keyboards/viendi8l/config.h
Normal file
65
keyboards/viendi8l/config.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright 2022 Gondolindrim <gondolindrim@acheronproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "common_config.h"
|
||||
|
||||
/* key matrix size */
|
||||
#define MATRIX_ROWS 6
|
||||
#define MATRIX_COLS 18
|
||||
|
||||
// C0 , C1 , C2 , C3 , C4 , C5 , C6 , C7 , C8 , C9 , C10, C11, C12, C13, C14, C15, C16, C17
|
||||
#define MATRIX_COL_PINS { C6 , C7 , C8 , C9 , A8 , B3 , B4 , A10, B5 , B8 , B9 , C13, C14, C15, A0 , A1 , A2 , A3 }
|
||||
// R0 , R1 , R2 , R3 , R4 , R5
|
||||
#define MATRIX_ROW_PINS { C3 , C2 , C1 , C0 , B14, A7 }
|
||||
#define DIODE_DIRECTION COL2ROW
|
||||
|
||||
/* Set 0 if debouncing isn't needed */
|
||||
#define DEBOUNCE 5
|
||||
|
||||
/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
|
||||
#define LOCKING_SUPPORT_ENABLE
|
||||
/* Locking resynchronize hack */
|
||||
#define LOCKING_RESYNC_ENABLE
|
||||
|
||||
#define ENCODERS_PAD_A { B10 }
|
||||
#define ENCODERS_PAD_B { B12 }
|
||||
#define ENCODER_RESOLUTION 2
|
||||
|
||||
#define TAPPING_TERM 200
|
||||
|
||||
#define I2C_DRIVER I2CD1
|
||||
#define I2C_SCL_PIN B6
|
||||
#define I2C_SDA_PIN B7
|
||||
#define I2C_SCL_PAL_MODE 4
|
||||
#define I2C_SDA_PAL_MODE 4
|
||||
#define EEPROM_I2C_24LC256
|
||||
#define I2C1_CLOCK_SPEED 400000
|
||||
#define I2C1_DUTY_CYCLE FAST_DUTY_CYCLE_2
|
||||
|
||||
#define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE
|
||||
|
||||
#define RGB_DI_PIN B15
|
||||
#define RGBLED_NUM 4
|
||||
#define RGBLIGHT_DEFAULT_VAL 0
|
||||
|
||||
#define WS2812_PWM_COMPLEMENTARY_OUTPUT
|
||||
#define WS2812_PWM_DRIVER PWMD1
|
||||
#define WS2812_PWM_CHANNEL 3
|
||||
#define WS2812_PWM_PAL_MODE 1
|
||||
#define WS2812_DMA_STREAM STM32_DMA2_STREAM5
|
||||
#define WS2812_DMA_CHANNEL 6
|
24
keyboards/viendi8l/halconf.h
Normal file
24
keyboards/viendi8l/halconf.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* Copyright 2020 QMK
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define HAL_USE_PWM TRUE
|
||||
#define HAL_USE_PAL TRUE
|
||||
|
||||
#define HAL_USE_I2C TRUE
|
||||
|
||||
#include_next <halconf.h>
|
103
keyboards/viendi8l/info.json
Normal file
103
keyboards/viendi8l/info.json
Normal file
@ -0,0 +1,103 @@
|
||||
{
|
||||
"keyboard_name": "8L",
|
||||
"manufacturer": "Viendi",
|
||||
"url": "https://viendi.co/",
|
||||
"maintainer": "gondolindrim",
|
||||
"usb": {
|
||||
"vid": "0x8673",
|
||||
"pid": "0x0877",
|
||||
"device_version": "0.0.1"
|
||||
},
|
||||
"layouts": {
|
||||
"LAYOUT_all": {
|
||||
"layout": [
|
||||
{"label":"Push", "x":0, "y":0},
|
||||
{"label":"\u00d7", "x":1, "y":0},
|
||||
{"label":"/", "x":2, "y":0},
|
||||
{"label":"Del", "x":3, "y":0},
|
||||
{"label":"Esc", "x":4.5, "y":0},
|
||||
{"label":"!", "x":5.5, "y":0},
|
||||
{"label":"@", "x":6.5, "y":0},
|
||||
{"label":"#", "x":7.5, "y":0},
|
||||
{"label":"$", "x":8.5, "y":0},
|
||||
{"label":"%", "x":9.5, "y":0},
|
||||
{"label":"^", "x":10.5, "y":0},
|
||||
{"label":"&", "x":11.5, "y":0},
|
||||
{"label":"*", "x":12.5, "y":0},
|
||||
{"label":"(", "x":13.5, "y":0},
|
||||
{"label":")", "x":14.5, "y":0},
|
||||
{"label":"-", "x":15.5, "y":0},
|
||||
{"label":"=", "x":16.5, "y":0},
|
||||
{"label":"Bksp", "x":17.5, "y":0},
|
||||
{"label":"Bksp", "x":18.5, "y":0},
|
||||
{"label":"-", "x":0, "y":1},
|
||||
{"label":"7", "x":1, "y":1},
|
||||
{"label":"8", "x":2, "y":1},
|
||||
{"label":"9", "x":3, "y":1},
|
||||
{"label":"Tab", "x":4.5, "y":1, "w":1.5},
|
||||
{"label":"Q", "x":6, "y":1},
|
||||
{"label":"W", "x":7, "y":1},
|
||||
{"label":"E", "x":8, "y":1},
|
||||
{"label":"R", "x":9, "y":1},
|
||||
{"label":"T", "x":10, "y":1},
|
||||
{"label":"Y", "x":11, "y":1},
|
||||
{"label":"U", "x":12, "y":1},
|
||||
{"label":"I", "x":13, "y":1},
|
||||
{"label":"O", "x":14, "y":1},
|
||||
{"label":"P", "x":15, "y":1},
|
||||
{"label":"{", "x":16, "y":1},
|
||||
{"label":"}", "x":17, "y":1},
|
||||
{"label":"|", "x":18, "y":1, "w":1.5},
|
||||
{"label":"+", "x":0, "y":2},
|
||||
{"label":"4", "x":1, "y":2},
|
||||
{"label":"5", "x":2, "y":2},
|
||||
{"label":"6", "x":3, "y":2},
|
||||
{"label":"Caps Lock", "x":4.5, "y":2, "w":1.75},
|
||||
{"label":"A", "x":6.25, "y":2},
|
||||
{"label":"S", "x":7.25, "y":2},
|
||||
{"label":"D", "x":8.25, "y":2},
|
||||
{"label":"F", "x":9.25, "y":2},
|
||||
{"label":"G", "x":10.25, "y":2},
|
||||
{"label":"H", "x":11.25, "y":2},
|
||||
{"label":"J", "x":12.25, "y":2},
|
||||
{"label":"K", "x":13.25, "y":2},
|
||||
{"label":"L", "x":14.25, "y":2},
|
||||
{"label":":", "x":15.25, "y":2},
|
||||
{"label":"\"", "x":16.25, "y":2},
|
||||
{"x":17.25, "y":2},
|
||||
{"label":"Enter", "x":18.25, "y":2, "w":1.25},
|
||||
{"label":"Enter", "x":0, "y":3},
|
||||
{"label":"1", "x":1, "y":3},
|
||||
{"label":"2", "x":2, "y":3},
|
||||
{"label":"3", "x":3, "y":3},
|
||||
{"label":"Shift", "x":4.5, "y":3, "w":1.25},
|
||||
{"x":5.75, "y":3},
|
||||
{"label":"Z", "x":6.75, "y":3},
|
||||
{"label":"X", "x":7.75, "y":3},
|
||||
{"label":"C", "x":8.75, "y":3},
|
||||
{"label":"V", "x":9.75, "y":3},
|
||||
{"label":"B", "x":10.75, "y":3},
|
||||
{"label":"N", "x":11.75, "y":3},
|
||||
{"label":"M", "x":12.75, "y":3},
|
||||
{"label":"<", "x":13.75, "y":3},
|
||||
{"label":">", "x":14.75, "y":3},
|
||||
{"label":"Shift", "x":15.75, "y":3, "w":1.75},
|
||||
{"label":"\u2191", "x":17.5, "y":3},
|
||||
{"x":18.5, "y":3},
|
||||
{"label":"Enter", "x":0, "y":4},
|
||||
{"label":".", "x":1, "y":4},
|
||||
{"label":".", "x":2, "y":4},
|
||||
{"label":"0", "x":3, "y":4},
|
||||
{"label":"Ctrl", "x":4.5, "y":4, "w":1.25},
|
||||
{"label":"Win", "x":5.75, "y":4, "w":1.25},
|
||||
{"label":"Alt", "x":7, "y":4, "w":1.25},
|
||||
{"x":8.25, "y":4, "w":6.25},
|
||||
{"x":14.5, "y":4},
|
||||
{"x":15.5, "y":4},
|
||||
{"label":"\u2190", "x":16.5, "y":4},
|
||||
{"label":"\u2193", "x":17.5, "y":4},
|
||||
{"label":"\u2192", "x":18.5, "y":4}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
56
keyboards/viendi8l/keymaps/default/keymap.c
Executable file
56
keyboards/viendi8l/keymaps/default/keymap.c
Executable file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2020 Álvaro "Gondolindrim" Volpato <alvaro.volpato@usp.br>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include QMK_KEYBOARD_H
|
||||
|
||||
|
||||
#include QMK_KEYBOARD_H
|
||||
#include "config_common.h"
|
||||
|
||||
#define LTCAPS LT(2, KC_CAPS)
|
||||
#define MTRSFT RSFT_T(KC_UP)
|
||||
|
||||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
|
||||
[0] = LAYOUT_all(
|
||||
KC_MUTE, KC_PSLS, KC_PAST, KC_BSPC, KC_GESC, KC_1 , KC_2 , KC_3 , KC_4 , KC_5 , KC_6 , KC_7 , KC_8 , KC_9 , KC_0 , KC_MINS, KC_EQL , KC_BSPC,
|
||||
KC_PMNS, KC_P7 , KC_P8 , KC_P9 , KC_TAB , KC_Q , KC_W , KC_E , KC_R , KC_T , KC_Y , KC_U , KC_I , KC_O , KC_P , KC_LBRC, KC_RBRC, KC_BSLS,
|
||||
KC_PPLS, KC_P4 , KC_P5 , KC_P6 , LTCAPS , KC_A , KC_S , KC_D , KC_F , KC_G , KC_H , KC_J , KC_K , KC_L , KC_SCLN, KC_QUOT, KC_ENT , KC_BSPC,
|
||||
KC_PENT, KC_P1 , KC_P2 , KC_P3 , KC_LSFT, KC_BSLS, KC_Z , KC_X , KC_C , KC_V , KC_B , KC_N , KC_M , KC_COMM, KC_DOT , KC_SLSH, MTRSFT , KC_NUHS,
|
||||
KC_PENT, KC_P0 , KC_P0 , KC_PDOT, KC_LCTL, KC_LGUI, KC_LALT, KC_SPC , TG(1) , MO(2) , KC_LEFT, KC_DOWN, KC_RGHT, MO(2)
|
||||
),
|
||||
[1] = LAYOUT_all(
|
||||
KC_MUTE, KC_PSLS, KC_PAST, KC_BSPC, KC_GESC, KC_F1 , KC_F2 , KC_F3 , KC_F4 , KC_F5 , KC_F6 , KC_F7 , KC_F8 , KC_F9 , KC_F10 , KC_F11 , KC_F12 , KC_BSPC,
|
||||
KC_PMNS, KC_P7 , KC_P8 , KC_P9 , KC_TAB , KC_Q , KC_W , KC_E , KC_R , KC_T , KC_Y , KC_U , KC_I , KC_O , KC_P , KC_LBRC, KC_RBRC, KC_BSLS,
|
||||
KC_PPLS, KC_P4 , KC_P5 , KC_P6 , LTCAPS , KC_A , KC_S , KC_D , KC_F , KC_G , KC_H , KC_J , KC_K , KC_L , KC_SCLN, KC_QUOT, KC_ENT , KC_BSPC,
|
||||
KC_PENT, KC_P1 , KC_P2 , KC_P3 , KC_LSFT, KC_BSLS, KC_Z , KC_X , KC_C , KC_V , KC_B , KC_N , KC_M , KC_COMM, KC_DOT , KC_SLSH, MTRSFT , KC_NUHS,
|
||||
KC_PENT, KC_P0 , KC_P0 , KC_PDOT, KC_LCTL, KC_LGUI, KC_LALT, KC_SPC , TG(1) , MO(2) , KC_LEFT, KC_DOWN, KC_RGHT, MO(2)
|
||||
),
|
||||
[2] = LAYOUT_all(
|
||||
KC_MUTE, KC_F10 , KC_F11 , KC_NUM , KC_GRV , KC_EXLM, KC_AT , KC_HASH, KC_DLR , KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_UNDS, KC_PLUS, KC_NO ,
|
||||
KC_MINS, KC_F7 , KC_F8 , KC_F9 , KC_TAB , KC_NO , KC_PGUP, KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_INS , KC_NO , KC_PSCR, KC_NO , KC_NO , KC_DEL ,
|
||||
KC_EQL , KC_F4 , KC_F5 , KC_F6 , KC_TRNS, KC_HOME, KC_PGDN, KC_END , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_F1 , KC_F2 , KC_F3 , KC_NO , KC_NO , KC_LPRN, KC_RPRN, KC_NO , KC_NO , KC_NO , KC_NO , KC_MUTE, KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_CALC, KC_CALC, KC_DEL , KC_NO , GUI_TOG, KC_SLEP, KC_NO , KC_NO , KC_TRNS, KC_NO , KC_NO , KC_NO , KC_TRNS
|
||||
),
|
||||
[3] = LAYOUT_all(
|
||||
KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO
|
||||
)
|
||||
};
|
272
keyboards/viendi8l/keymaps/via/keymap.c
Executable file
272
keyboards/viendi8l/keymaps/via/keymap.c
Executable file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
Copyright 2020 Álvaro "Gondolindrim" Volpato <alvaro.volpato@usp.br>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include QMK_KEYBOARD_H
|
||||
|
||||
#define MEDIA_KEY_DELAY 10
|
||||
#define ALT_TAB_DELAY 1000
|
||||
|
||||
// Persistent encoder modes: the current encoder mode is written into EEPROM memory, and retrieved at the keyboard initialization. This means the current encoder mode is persistent even if the keyboard is reset or turned off
|
||||
#define PERSISTENT_ENCODER_MODES
|
||||
|
||||
#define ENCODER_MODE_CHANGE_DELAY 30000 //30 seconds, effectively removing the delay feature
|
||||
|
||||
// Defining special keycodes
|
||||
enum keyboard_keycodes {
|
||||
ECLICK = SAFE_RANGE, // Encoder click
|
||||
ALTTABF , // ALT-TAB forward
|
||||
ALTTABB , // ALT-TAB backwards
|
||||
ALTTABC , // For alt-tab-click
|
||||
ENCMUP , // Encoder mode up
|
||||
ENCMDN , // Encoder mode down
|
||||
NEW_SAFE_RANGE
|
||||
};
|
||||
|
||||
// Creates sample keyevents and keyrecords to be used in the processing of the custom keycodes. Their time should be resampled everytime they are used; their cols and rows are set to be "impossible", that is, outside the normal key matrix bounds.
|
||||
const keyevent_t sample_pressed_keyevent = {
|
||||
.key = (keypos_t){.row = 5, .col = 13},
|
||||
.pressed = true,
|
||||
.time = 0
|
||||
};
|
||||
|
||||
keyrecord_t sample_pressed_keyrecord = {
|
||||
.event = sample_pressed_keyevent
|
||||
};
|
||||
|
||||
const keyevent_t sample_released_keyevent = {
|
||||
.key = (keypos_t){.row = 5, .col = 13},
|
||||
.pressed = false,
|
||||
.time = 0
|
||||
};
|
||||
|
||||
keyrecord_t sample_released_keyrecord = {
|
||||
.event = sample_released_keyevent
|
||||
};
|
||||
|
||||
// tap_process_record_user calls process_record_user with the pressed and released sample keyrecords with a delay of MEDIA_KEY_DELAY. The idea is to allow custom keycodes a simulation of a key press and release, allowing them to be treated in process_record_user. This, in turn, allows the custom keycodes to be used both in the encoder callback and the keyboard keymap.
|
||||
uint16_t temp_timer = 0;
|
||||
void tap_process_record_user(uint16_t keycode)
|
||||
{
|
||||
temp_timer = timer_read();
|
||||
sample_pressed_keyrecord.event.time = timer_read();
|
||||
process_record_user( keycode, &sample_pressed_keyrecord );
|
||||
while (timer_elapsed(temp_timer) < MEDIA_KEY_DELAY);
|
||||
sample_released_keyrecord.event.time = timer_read();
|
||||
process_record_user( keycode, &sample_released_keyrecord );
|
||||
}
|
||||
|
||||
// process_special_keycode is a function that distinguishes between the native keycodes and the ones custom-defined here. Normal keycodes are tapped, while keycodes in the SAFE_RANGE - NEW_SAFE_RANGE interval are treated with tap_process_record_user.
|
||||
void process_special_keycode(uint16_t keycode) {
|
||||
if (SAFE_RANGE <= keycode && keycode < NEW_SAFE_RANGE) tap_process_record_user(keycode);
|
||||
else tap_code(keycode);
|
||||
}
|
||||
|
||||
uint16_t encoder_click_delay = ENCODER_MODE_CHANGE_DELAY;
|
||||
|
||||
uint8_t startup_color[3] = {0xFF,0xFF,0xFF};
|
||||
|
||||
typedef struct _encoder_mode_t {
|
||||
uint8_t indicator_color[3];
|
||||
uint16_t clockwise_key[4];
|
||||
uint16_t counterclockwise_key[4];
|
||||
uint16_t clicked_key[4] ;
|
||||
uint16_t hold_key;
|
||||
} encoder_mode_t;
|
||||
|
||||
encoder_mode_t encoder_modes[] = {
|
||||
{ .indicator_color = {0xFF,0x00,0x00} , .clockwise_key = {KC_VOLU, KC_VOLU, KC_VOLU, ENCMUP}, .counterclockwise_key = {KC_VOLD, KC_VOLD, KC_VOLD , ENCMDN}, .clicked_key = {ENCMUP , ENCMUP , ENCMUP , ENCMUP }, .hold_key = KC_NO },
|
||||
{ .indicator_color = {0x00,0xFF,0x00} , .clockwise_key = {KC_WH_U, KC_VOLU, KC_WH_U, ENCMUP}, .counterclockwise_key = {KC_WH_D, KC_WH_D, KC_WH_D , ENCMDN}, .clicked_key = {ENCMUP , ENCMUP , ENCMUP , ENCMUP }, .hold_key = KC_NO },
|
||||
{ .indicator_color = {0x00,0x00,0xFF} , .clockwise_key = {ALTTABF, KC_VOLU, ALTTABF, ENCMUP}, .counterclockwise_key = {ALTTABB, ALTTABB, ALTTABB , ENCMDN}, .clicked_key = {ENCMUP , ENCMUP , ENCMUP , ENCMUP }, .hold_key = KC_NO }
|
||||
// Insert your custom encoder mode here
|
||||
};
|
||||
|
||||
#define NUM_ENCODER_MODES (sizeof(encoder_modes)/sizeof(encoder_modes[0]))
|
||||
|
||||
// This counter is used to track what encoder mode is being used at a certain time
|
||||
int encoder_mode_count = 0;
|
||||
|
||||
#ifdef PERSISTENT_ENCODER_MODES
|
||||
typedef union {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
int user_encoder_mode_count;
|
||||
};
|
||||
} user_config_t;
|
||||
|
||||
user_config_t user_config;
|
||||
#endif
|
||||
|
||||
void set_indicator_colors(uint8_t color[3]){
|
||||
rgblight_setrgb_at(color[0], color[1], color[2],0);
|
||||
}
|
||||
|
||||
// Board init: RGB indicator is set to startup_color
|
||||
void keyboard_pre_init_user(void){
|
||||
set_indicator_colors(startup_color);
|
||||
};
|
||||
|
||||
void keyboard_post_init_user(void){
|
||||
#ifdef PERSISTENT_ENCODER_MODES
|
||||
user_config.raw = eeconfig_read_user();
|
||||
encoder_mode_count = user_config.user_encoder_mode_count ;
|
||||
#else
|
||||
encoder_mode_count = 0;
|
||||
#endif
|
||||
set_indicator_colors(encoder_modes[ encoder_mode_count ].indicator_color);
|
||||
};
|
||||
|
||||
#define LTCAPS LT(2, KC_CAPS)
|
||||
#define MTRSFT RSFT_T(KC_UP)
|
||||
|
||||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
|
||||
[0] = LAYOUT_all(
|
||||
ECLICK , KC_PSLS, KC_PAST, KC_BSPC, KC_GESC, KC_1 , KC_2 , KC_3 , KC_4 , KC_5 , KC_6 , KC_7 , KC_8 , KC_9 , KC_0 , KC_MINS, KC_EQL , KC_BSPC,
|
||||
KC_PMNS, KC_P7 , KC_P8 , KC_P9 , KC_TAB , KC_Q , KC_W , KC_E , KC_R , KC_T , KC_Y , KC_U , KC_I , KC_O , KC_P , KC_LBRC, KC_RBRC, KC_BSLS,
|
||||
KC_PPLS, KC_P4 , KC_P5 , KC_P6 , LTCAPS , KC_A , KC_S , KC_D , KC_F , KC_G , KC_H , KC_J , KC_K , KC_L , KC_SCLN, KC_QUOT, KC_ENT , KC_BSPC,
|
||||
KC_PENT, KC_P1 , KC_P2 , KC_P3 , KC_LSFT, KC_BSLS, KC_Z , KC_X , KC_C , KC_V , KC_B , KC_N , KC_M , KC_COMM, KC_DOT , KC_SLSH, MTRSFT , KC_NUHS,
|
||||
KC_PENT, KC_P0 , KC_P0 , KC_PDOT, KC_LCTL, KC_LGUI, KC_LALT, KC_SPC , TG(1) , MO(2) , KC_LEFT, KC_DOWN, KC_RGHT, MO(2)
|
||||
),
|
||||
[1] = LAYOUT_all(
|
||||
ECLICK , KC_PSLS, KC_PAST, KC_BSPC, KC_GESC, KC_F1 , KC_F2 , KC_F3 , KC_F4 , KC_F5 , KC_F6 , KC_F7 , KC_F8 , KC_F9 , KC_F10 , KC_F11 , KC_F12 , KC_BSPC,
|
||||
KC_PMNS, KC_P7 , KC_P8 , KC_P9 , KC_TAB , KC_Q , KC_W , KC_E , KC_R , KC_T , KC_Y , KC_U , KC_I , KC_O , KC_P , KC_LBRC, KC_RBRC, KC_BSLS,
|
||||
KC_PPLS, KC_P4 , KC_P5 , KC_P6 , LTCAPS , KC_A , KC_S , KC_D , KC_F , KC_G , KC_H , KC_J , KC_K , KC_L , KC_SCLN, KC_QUOT, KC_ENT , KC_BSPC,
|
||||
KC_PENT, KC_P1 , KC_P2 , KC_P3 , KC_LSFT, KC_BSLS, KC_Z , KC_X , KC_C , KC_V , KC_B , KC_N , KC_M , KC_COMM, KC_DOT , KC_SLSH, MTRSFT , KC_NUHS,
|
||||
KC_PENT, KC_P0 , KC_P0 , KC_PDOT, KC_LCTL, KC_LGUI, KC_LALT, KC_SPC , TG(1) , MO(2) , KC_LEFT, KC_DOWN, KC_RGHT, MO(2)
|
||||
),
|
||||
[2] = LAYOUT_all(
|
||||
ECLICK , KC_F10 , KC_F11 , KC_NUM , KC_GRV , KC_EXLM, KC_AT , KC_HASH, KC_DLR , KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_UNDS, KC_PLUS, KC_NO ,
|
||||
KC_MINS, KC_F7 , KC_F8 , KC_F9 , KC_TAB , KC_NO , KC_PGUP, KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_INS , KC_NO , KC_PSCR, KC_NO , KC_NO , KC_DEL ,
|
||||
KC_EQL , KC_F4 , KC_F5 , KC_F6 , KC_TRNS, KC_HOME, KC_PGDN, KC_END , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_F1 , KC_F2 , KC_F3 , KC_NO , KC_NO , KC_LPRN, KC_RPRN, KC_NO , KC_NO , KC_NO , KC_NO , KC_MUTE, KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_CALC, KC_CALC, KC_DEL , KC_NO , GUI_TOG, KC_SLEP, KC_NO , KC_NO , KC_TRNS, KC_NO , KC_NO , KC_NO , KC_TRNS
|
||||
),
|
||||
[3] = LAYOUT_all(
|
||||
ECLICK , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO ,
|
||||
KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO , KC_NO
|
||||
)
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
bool is_alt_tab_active = false; // Flag to check if alt tab is active
|
||||
uint32_t alt_tab_timer = 0; // Time trigger for alt tab
|
||||
uint16_t mapped_code = 0;
|
||||
uint32_t held_keycode_timer = 0;
|
||||
|
||||
int current_layer = 0 ; // Updated in layer_state_set_user each time a layer change is made
|
||||
|
||||
void cycle_encoder_mode(bool forward){
|
||||
if (forward) encoder_mode_count++ ; // Shifts encoder mode forward
|
||||
else encoder_mode_count-- ;
|
||||
if (encoder_mode_count == -1) encoder_mode_count = NUM_ENCODER_MODES - 1;
|
||||
// Shifts encoder mode backward
|
||||
encoder_mode_count = encoder_mode_count % NUM_ENCODER_MODES ; // This makes sure encoder_mode_count keeps cycling between 0,1,...,NUM_ENCODER_MODES and doesnt eventually overflow
|
||||
#ifdef PERSISTENT_ENCODER_MODES
|
||||
user_config.user_encoder_mode_count = encoder_mode_count ;
|
||||
eeconfig_update_user(user_config.raw);
|
||||
#endif
|
||||
set_indicator_colors( encoder_modes[ encoder_mode_count ].indicator_color ); // Set indicator color to the corresponding defined color
|
||||
}
|
||||
|
||||
bool encoder_update_user(uint8_t index, bool clockwise) {
|
||||
process_special_keycode(clockwise ? encoder_modes[ encoder_mode_count ].clockwise_key[ current_layer ] : encoder_modes[ encoder_mode_count ].counterclockwise_key[ current_layer ]);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t held_click_timer = 0;
|
||||
bool is_click_held = false;
|
||||
bool is_shift_held = false;
|
||||
bool automatic_hold_cycle = false; // This flag registers if the encoder hold was automatically cycled
|
||||
|
||||
// This bool records if LALT is pressed or not. Due to the automatic disabling of the ALT-TAB of the ALTTABS custom keystroke, the automatic disabling can un-register KC_LALT even when the LALT key is phisically pressed. Hence there needs to be two bools: one that keebs track of the ALT-TAB activity and one that keeps track of LALT so that the automatic disabling will not disable LALT if it is phisically pressed.
|
||||
bool is_lalt_pressed = false;
|
||||
|
||||
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
||||
switch (keycode) {
|
||||
case KC_LSFT:
|
||||
case KC_RSFT:
|
||||
if (record->event.pressed) is_shift_held = true;
|
||||
else is_shift_held = false;
|
||||
return true;
|
||||
case ECLICK:
|
||||
// Checks if the encoder has been pressed; if so, sets the corresponding flag and starts the corresponding timer
|
||||
if (record->event.pressed) {
|
||||
is_click_held = true;
|
||||
held_click_timer = timer_read32();
|
||||
// Checks if the encoder has been released: samples the duration of the encoder push; if this time was less than the encoder_click_delay, processes the clicked key. If it was bigger, processes the hold key. This behavior is adjusted using the ENCODER_MODE_CHANGE_DELAY macro. There is an exception made when automatic_hold_cycle is true; this means that the encoder push has been pressed enough to trigger a hold cycle. This case is taken care of in the housekeeping routine, where the held key is triggered and the timer reset. Hence the automatic_hold_cycle needs to be checked because without this check the function will trigger the clicked key after the hold cycle has been cycled more than once.
|
||||
} else {
|
||||
is_click_held = false;
|
||||
if (timer_elapsed32(held_click_timer) < encoder_click_delay && !automatic_hold_cycle ) process_special_keycode( encoder_modes[ encoder_mode_count ].clicked_key[ current_layer ] ) ;
|
||||
automatic_hold_cycle = false;
|
||||
}
|
||||
return true; // Skip all further processing of this key
|
||||
case KC_LALT: // If this is not defined, if the encoder is activated in the alt-tab mode while the LALT key is pressed, the menu goes away.
|
||||
if (record->event.pressed) is_lalt_pressed = true;
|
||||
else is_lalt_pressed = false;
|
||||
return true;
|
||||
case ENCMUP:
|
||||
case ENCMDN:
|
||||
if (record->event.pressed) cycle_encoder_mode(keycode == ENCMUP); // If keycode == ENCMUP the expression returns true and the cycle function cycles the modes forward. If not, then cycles backwards.
|
||||
return false;
|
||||
case ALTTABF:
|
||||
case ALTTABB:
|
||||
if (record->event.pressed) {
|
||||
if (!is_alt_tab_active) {
|
||||
is_alt_tab_active = true;
|
||||
register_code(KC_LALT);
|
||||
|
||||
}
|
||||
tap_code16(keycode == ALTTABF ? KC_TAB : S(KC_TAB)); // Due to S(KC_TAB), the 16-bit tap_code16 is needed.
|
||||
alt_tab_timer = timer_read32();
|
||||
}
|
||||
return true;
|
||||
case ALTTABC:
|
||||
if (record->event.pressed) {
|
||||
if (is_alt_tab_active) {
|
||||
if (!is_lalt_pressed) unregister_code(KC_LALT);
|
||||
is_alt_tab_active = false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return true; // Process all other keycodes normally
|
||||
}
|
||||
}
|
||||
|
||||
void housekeeping_task_user(void) {
|
||||
current_layer = get_highest_layer(layer_state);
|
||||
if (is_alt_tab_active) {
|
||||
if (is_lalt_pressed) alt_tab_timer = timer_read32();
|
||||
else if (timer_elapsed32(alt_tab_timer) > ALT_TAB_DELAY) {
|
||||
unregister_code(KC_LALT);
|
||||
is_alt_tab_active = false;
|
||||
}
|
||||
}
|
||||
/* This piece of the code checks for the encoder push timer. If the encoder push interval was less than encoder_click_delay then it is automatically processed by process_record_user by triggering the current mode's click key. However, if the encoder push is held for more time than the defined delay, then the encoder hold "cycles", that is, gets activated and the timer needs to be reset. This does three things:
|
||||
- (1) Sets the automatic_hold_cycle flag which prevents process_record_user from triggering the click key when the push is released
|
||||
- (2) Processes the current mode's hold key in process_record_user
|
||||
- (3) Resets the click timer
|
||||
*/
|
||||
if (is_click_held && timer_elapsed32(held_click_timer) > encoder_click_delay ){
|
||||
automatic_hold_cycle = true;
|
||||
process_special_keycode( encoder_modes[ encoder_mode_count ].hold_key );
|
||||
held_click_timer = timer_read32();
|
||||
}
|
||||
}
|
1
keyboards/viendi8l/keymaps/via/rules.mk
Normal file
1
keyboards/viendi8l/keymaps/via/rules.mk
Normal file
@ -0,0 +1 @@
|
||||
VIA_ENABLE = yes
|
28
keyboards/viendi8l/mcuconf.h
Normal file
28
keyboards/viendi8l/mcuconf.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* Copyright 2020 QMK
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include_next <mcuconf.h>
|
||||
|
||||
#undef STM32_I2C_USE_I2C1
|
||||
#define STM32_I2C_USE_I2C1 TRUE
|
||||
|
||||
#undef STM32_PWM_USE_ADVANCED
|
||||
#define STM32_PWM_USE_ADVANCED TRUE
|
||||
|
||||
#undef STM32_PWM_USE_TIM1
|
||||
#define STM32_PWM_USE_TIM1 TRUE
|
27
keyboards/viendi8l/readme.md
Normal file
27
keyboards/viendi8l/readme.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Viendi8L PCB firmware
|
||||
|
||||
![8L_image](https://imgur.com/z4cuSYJh.png)
|
||||
|
||||
The 8L 80% (60% plus southpaw numpad) keyboard designed by Viendi, with PCB by Gondolindrim.
|
||||
|
||||
* Keyboard Maintainer: [Gondolindrim](https://github.com/gondolindrim)
|
||||
* Hardware Availability: proprietary PCB
|
||||
* Website: [Viendi](https://viendi.co/)
|
||||
|
||||
## How to flash
|
||||
|
||||
### Enter bootloader
|
||||
|
||||
* **Bootmagic reset**: hold down the key at (0,0) in the matrix (the encoder push) and plug in the keyboard, waiting for five seconds
|
||||
* **Physical reset button**: press the button on the front of the PCB or use pliers to short the reset contacts, for at least five seconds
|
||||
* **Keycode in layout**: press the key mapped to `RESET` if it is available (ESC key at layer 1 in the default layout)
|
||||
|
||||
## Compile firmware
|
||||
|
||||
After accessing the DFU state in your PCB, build the default layout using:
|
||||
|
||||
make viendi8l:default
|
||||
|
||||
Then download the resulting binary `*bin` file using `dfu-util` or QMK Toolbox.
|
||||
|
||||
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
|
28
keyboards/viendi8l/rules.mk
Normal file
28
keyboards/viendi8l/rules.mk
Normal file
@ -0,0 +1,28 @@
|
||||
# MCU name
|
||||
MCU = STM32F401
|
||||
|
||||
BOARD = GENERIC_STM32_F401XC
|
||||
|
||||
# Bootloader selection
|
||||
BOOTLOADER = stm32-dfu
|
||||
|
||||
# Build Options
|
||||
# change yes to no to disable
|
||||
#
|
||||
BOOTMAGIC_ENABLE = yes # Enable Bootmagic Lite
|
||||
MOUSEKEY_ENABLE = yes # Mouse keys
|
||||
EXTRAKEY_ENABLE = yes # Audio control and System control
|
||||
CONSOLE_ENABLE = no # Console for debug
|
||||
COMMAND_ENABLE = no # Commands for debug and configuration
|
||||
NKRO_ENABLE = no # Enable N-Key Rollover
|
||||
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
|
||||
RGBLIGHT_ENABLE = yes # Enable keyboard RGB underglow
|
||||
WS2812_DRIVER = pwm
|
||||
AUDIO_ENABLE = no # Audio output
|
||||
ENCODER_ENABLE = yes
|
||||
LTO_ENABLE = no
|
||||
|
||||
EEPROM_DRIVER = i2c
|
||||
|
||||
# Enter lower-power sleep mode when on the ChibiOS idle thread
|
||||
OPT_DEFS += -DCORTEX_ENABLE_WFI_IDLE=TRUE -DDEBUG_EEPROM_OUTPUT=TRUE
|
56
keyboards/viendi8l/viendi8l.c
Normal file
56
keyboards/viendi8l/viendi8l.c
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2022 Álvaro "Gondolindrim" Volpato <gondolindrim@acheronproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "viendi8l.h"
|
||||
|
||||
|
||||
// Defining indicator colors
|
||||
uint8_t caps_color[3] = {0xFF,0xFF,0xFF};
|
||||
uint8_t num_color[3] = {0xFF,0xFF,0xFF};
|
||||
uint8_t layer_color[3] = {0xFF,0xFF,0xFF};
|
||||
|
||||
bool led_update_kb(led_t led_state) {
|
||||
bool res = led_update_user(led_state);
|
||||
if(res) {
|
||||
led_state.caps_lock ? rgblight_setrgb_at(caps_color[0], caps_color[1], caps_color[2], 2) : rgblight_setrgb_at(0x00,0x00,0x00,2);
|
||||
led_state.num_lock ? rgblight_setrgb_at(num_color[0], num_color[1], num_color[2], 3) : rgblight_setrgb_at(0x00,0x00,0x00,3);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
layer_state_t layer_state_set_kb(layer_state_t state) {
|
||||
switch (get_highest_layer(state)) {
|
||||
case 1:
|
||||
rgblight_setrgb_at(layer_color[0], layer_color[1], layer_color[2], 1);
|
||||
break;
|
||||
default:
|
||||
rgblight_setrgb_at(0x00, 0x00, 0x00, 1);
|
||||
break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
#ifdef ENCODER_ENABLE
|
||||
bool encoder_update_kb(uint8_t index, bool clockwise) {
|
||||
if(!encoder_update_user(index, clockwise)) return false;
|
||||
if (index == 0) {
|
||||
if (clockwise) tap_code_delay(KC_VOLU, 10);
|
||||
else tap_code_delay(KC_VOLD, 10);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
37
keyboards/viendi8l/viendi8l.h
Normal file
37
keyboards/viendi8l/viendi8l.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright 2020 Gondolindrim <gondolindrim@acheronproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "quantum.h"
|
||||
|
||||
#define ___ KC_NO
|
||||
|
||||
#define LAYOUT_all( \
|
||||
K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, K0E, K0F, K0G, K0H, \
|
||||
K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D, K1E, K1F, K1G, K1H, \
|
||||
K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, K2D, K2E, K2F, K2G, K2H, \
|
||||
K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, K3D, K3E, K3F, K3G, K3H, \
|
||||
K40, K41, K42, K43, K44, K46, K47, K5A, K5C, K5D, K5E, K5F, K5G, K5H \
|
||||
) { \
|
||||
{ K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, K0E, K0F, K0G, K0H }, \
|
||||
{ K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D, K1E, K1F, K1G, K1H }, \
|
||||
{ K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, K2D, K2E, K2F, K2G, K2H }, \
|
||||
{ K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, K3D, K3E, K3F, K3G, K3H }, \
|
||||
{ K40, K41, K42, K43, K44, ___, K46, K47, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___ }, \
|
||||
{ ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, K5A, ___, K5C, K5D, K5E, K5F, K5G, K5H } \
|
||||
}
|
Loading…
Reference in New Issue
Block a user