rebase
This commit is contained in:
parent
265d9a7b1c
commit
25b8d223c0
67 changed files with 570 additions and 171 deletions
|
@ -337,11 +337,9 @@ config ZMK_KSCAN_EVENT_QUEUE_SIZE
|
|||
|
||||
config ZMK_KSCAN_MOCK_DRIVER
|
||||
bool "Enable mock kscan driver to simulate key presses"
|
||||
default n
|
||||
|
||||
config ZMK_KSCAN_COMPOSITE_DRIVER
|
||||
bool "Enable composite kscan driver to combine kscan devices"
|
||||
default n
|
||||
|
||||
#KSCAN Settings
|
||||
endmenu
|
||||
|
|
|
@ -10,11 +10,9 @@ if ZMK_KSCAN_GPIO_DRIVER
|
|||
|
||||
config ZMK_KSCAN_MATRIX_POLLING
|
||||
bool "Poll for key event triggers instead of using interrupts on matrix boards."
|
||||
default n
|
||||
|
||||
config ZMK_KSCAN_DIRECT_POLLING
|
||||
bool "Poll for key event triggers instead of using interrupts on direct wired boards."
|
||||
default n
|
||||
|
||||
endif
|
||||
|
||||
|
|
|
@ -51,6 +51,11 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
}
|
||||
#endif
|
||||
|
||||
#define COND_POLLING(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (code), ())
|
||||
#define COND_INTERRUPTS(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), (code))
|
||||
#define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, pollcode, intcode)
|
||||
|
||||
#define INST_MATRIX_ROWS(n) DT_INST_PROP_LEN(n, row_gpios)
|
||||
#define INST_MATRIX_COLS(n) DT_INST_PROP_LEN(n, col_gpios)
|
||||
#define INST_OUTPUT_LEN(n) \
|
||||
|
@ -61,19 +66,21 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
(INST_MATRIX_ROWS(n)))
|
||||
|
||||
#define GPIO_INST_INIT(n) \
|
||||
COND_INTERRUPTS( \
|
||||
struct kscan_gpio_irq_callback_##n { \
|
||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * work; \
|
||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * \
|
||||
work; \
|
||||
struct gpio_callback callback; \
|
||||
const struct device *dev; \
|
||||
}; \
|
||||
static struct kscan_gpio_irq_callback_##n irq_callbacks_##n[INST_INPUT_LEN(n)]; \
|
||||
static struct kscan_gpio_irq_callback_##n irq_callbacks_##n[INST_INPUT_LEN(n)];) \
|
||||
struct kscan_gpio_config_##n { \
|
||||
struct kscan_gpio_item_config rows[INST_MATRIX_ROWS(n)]; \
|
||||
struct kscan_gpio_item_config cols[INST_MATRIX_COLS(n)]; \
|
||||
}; \
|
||||
struct kscan_gpio_data_##n { \
|
||||
kscan_callback_t callback; \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (struct k_timer poll_timer;), ()) \
|
||||
COND_POLLING(struct k_timer poll_timer;) \
|
||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) work; \
|
||||
bool matrix_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
|
||||
const struct device *rows[INST_MATRIX_ROWS(n)]; \
|
||||
|
@ -102,17 +109,16 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
return ( \
|
||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->rows), (cfg->cols))); \
|
||||
} \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), \
|
||||
( \
|
||||
COND_INTERRUPTS( \
|
||||
static int kscan_gpio_enable_interrupts_##n(const struct device *dev) { \
|
||||
return kscan_gpio_config_interrupts( \
|
||||
kscan_gpio_input_devices_##n(dev), kscan_gpio_input_configs_##n(dev), \
|
||||
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), \
|
||||
kscan_gpio_input_configs_##n(dev), \
|
||||
INST_INPUT_LEN(n), GPIO_INT_LEVEL_ACTIVE); \
|
||||
} static int kscan_gpio_disable_interrupts_##n(const struct device *dev) { \
|
||||
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), \
|
||||
kscan_gpio_input_configs_##n(dev), \
|
||||
INST_INPUT_LEN(n), GPIO_INT_DISABLE); \
|
||||
})) \
|
||||
}) \
|
||||
static void kscan_gpio_set_output_state_##n(const struct device *dev, int value) { \
|
||||
int err; \
|
||||
for (int i = 0; i < INST_OUTPUT_LEN(n); i++) { \
|
||||
|
@ -132,17 +138,22 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
(output_index))] = value; \
|
||||
} \
|
||||
static int kscan_gpio_read_##n(const struct device *dev) { \
|
||||
bool submit_follow_up_read = false; \
|
||||
COND_INTERRUPTS(bool submit_follow_up_read = false;) \
|
||||
struct kscan_gpio_data_##n *data = dev->data; \
|
||||
static bool read_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
|
||||
int err; \
|
||||
/* Disable our interrupts temporarily while we scan, to avoid */ \
|
||||
/* re-entry while we iterate columns and set them active one by one */ \
|
||||
/* to get pressed state for each matrix cell. */ \
|
||||
kscan_gpio_set_output_state_##n(dev, 0); \
|
||||
COND_INTERRUPTS(kscan_gpio_set_output_state_##n(dev, 0);) \
|
||||
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \
|
||||
const struct device *out_dev = kscan_gpio_output_devices_##n(dev)[o]; \
|
||||
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
|
||||
gpio_pin_set(out_dev, out_cfg->pin, 1); \
|
||||
err = gpio_pin_set(out_dev, out_cfg->pin, 1); \
|
||||
if (err) { \
|
||||
LOG_ERR("Failed to set output active (err %d)", err); \
|
||||
return err; \
|
||||
} \
|
||||
for (int i = 0; i < INST_INPUT_LEN(n); i++) { \
|
||||
const struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \
|
||||
const struct kscan_gpio_item_config *in_cfg = \
|
||||
|
@ -150,16 +161,20 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
kscan_gpio_set_matrix_state_##n(read_state, i, o, \
|
||||
gpio_pin_get(in_dev, in_cfg->pin) > 0); \
|
||||
} \
|
||||
gpio_pin_set(out_dev, out_cfg->pin, 0); \
|
||||
err = gpio_pin_set(out_dev, out_cfg->pin, 0); \
|
||||
if (err) { \
|
||||
LOG_ERR("Failed to set output inactive (err %d)", err); \
|
||||
return err; \
|
||||
} \
|
||||
} \
|
||||
/* Set all our outputs as active again. */ \
|
||||
kscan_gpio_set_output_state_##n(dev, 1); \
|
||||
COND_INTERRUPTS(kscan_gpio_set_output_state_##n(dev, 1);) \
|
||||
for (int r = 0; r < INST_MATRIX_ROWS(n); r++) { \
|
||||
for (int c = 0; c < INST_MATRIX_COLS(n); c++) { \
|
||||
bool pressed = read_state[r][c]; \
|
||||
/* Follow up reads needed because further interrupts won't fire on already tripped \
|
||||
* input GPIO pins */ \
|
||||
submit_follow_up_read = (submit_follow_up_read || pressed); \
|
||||
COND_INTERRUPTS(submit_follow_up_read = (submit_follow_up_read || pressed);) \
|
||||
if (pressed != data->matrix_state[r][c]) { \
|
||||
LOG_DBG("Sending event at %d,%d state %s", r, c, (pressed ? "on" : "off")); \
|
||||
data->matrix_state[r][c] = pressed; \
|
||||
|
@ -167,33 +182,31 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
} \
|
||||
} \
|
||||
} \
|
||||
COND_INTERRUPTS( \
|
||||
if (submit_follow_up_read) { \
|
||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(&data->work); }), ({ \
|
||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(&data->work); }), \
|
||||
({ \
|
||||
k_delayed_work_cancel(&data->work); \
|
||||
k_delayed_work_submit(&data->work, K_MSEC(5)); \
|
||||
})) \
|
||||
} else { \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), \
|
||||
(kscan_gpio_enable_interrupts_##n(dev);)) \
|
||||
} \
|
||||
} else { kscan_gpio_enable_interrupts_##n(dev); }) \
|
||||
return 0; \
|
||||
} \
|
||||
static void kscan_gpio_work_handler_##n(struct k_work *work) { \
|
||||
struct kscan_gpio_data_##n *data = CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \
|
||||
kscan_gpio_read_##n(data->dev); \
|
||||
} \
|
||||
static void kscan_gpio_irq_callback_handler_##n( \
|
||||
COND_INTERRUPTS(static void kscan_gpio_irq_callback_handler_##n( \
|
||||
const struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pin) { \
|
||||
struct kscan_gpio_irq_callback_##n *data = \
|
||||
CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), \
|
||||
(kscan_gpio_disable_interrupts_##n(data->dev);)) \
|
||||
kscan_gpio_disable_interrupts_##n(data->dev); \
|
||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(data->work); }), ({ \
|
||||
k_delayed_work_cancel(data->work); \
|
||||
k_delayed_work_submit(data->work, \
|
||||
K_MSEC(DT_INST_PROP(n, debounce_period))); \
|
||||
})) \
|
||||
} \
|
||||
}) \
|
||||
\
|
||||
static struct kscan_gpio_data_##n kscan_gpio_data_##n = { \
|
||||
.rows = {[INST_MATRIX_ROWS(n) - 1] = NULL}, .cols = {[INST_MATRIX_COLS(n) - 1] = NULL}}; \
|
||||
|
@ -207,25 +220,22 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
return 0; \
|
||||
}; \
|
||||
static int kscan_gpio_enable_##n(const struct device *dev) { \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, \
|
||||
(struct kscan_gpio_data_##n *data = dev->data; \
|
||||
k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10)); return 0;), \
|
||||
COND_POLL_OR_INTERRUPTS((struct kscan_gpio_data_##n *data = dev->data; \
|
||||
k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10)); \
|
||||
return 0;), \
|
||||
(int err = kscan_gpio_enable_interrupts_##n(dev); \
|
||||
if (err) { return err; } return kscan_gpio_read_##n(dev);)) \
|
||||
}; \
|
||||
static int kscan_gpio_disable_##n(const struct device *dev) { \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, \
|
||||
(struct kscan_gpio_data_##n *data = dev->data; \
|
||||
COND_POLL_OR_INTERRUPTS((struct kscan_gpio_data_##n *data = dev->data; \
|
||||
k_timer_stop(&data->poll_timer); return 0;), \
|
||||
(return kscan_gpio_disable_interrupts_##n(dev);)) \
|
||||
}; \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, \
|
||||
(static void kscan_gpio_timer_handler(struct k_timer *timer) { \
|
||||
COND_POLLING(static void kscan_gpio_timer_handler_##n(struct k_timer *timer) { \
|
||||
struct kscan_gpio_data_##n *data = \
|
||||
CONTAINER_OF(timer, struct kscan_gpio_data_##n, poll_timer); \
|
||||
k_work_submit(&data->work.work); \
|
||||
}), \
|
||||
()) \
|
||||
}) \
|
||||
static int kscan_gpio_init_##n(const struct device *dev) { \
|
||||
struct kscan_gpio_data_##n *data = dev->data; \
|
||||
int err; \
|
||||
|
@ -244,15 +254,15 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
} else { \
|
||||
LOG_DBG("Configured pin %d on %s for input", in_cfg->pin, in_cfg->label); \
|
||||
} \
|
||||
irq_callbacks_##n[i].work = &data->work; \
|
||||
irq_callbacks_##n[i].dev = dev; \
|
||||
COND_INTERRUPTS( \
|
||||
irq_callbacks_##n[i].work = &data->work; irq_callbacks_##n[i].dev = dev; \
|
||||
gpio_init_callback(&irq_callbacks_##n[i].callback, \
|
||||
kscan_gpio_irq_callback_handler_##n, BIT(in_cfg->pin)); \
|
||||
err = gpio_add_callback(input_devices[i], &irq_callbacks_##n[i].callback); \
|
||||
if (err) { \
|
||||
LOG_ERR("Error adding the callback to the column device"); \
|
||||
LOG_ERR("Error adding the callback to the input device"); \
|
||||
return err; \
|
||||
} \
|
||||
}) \
|
||||
} \
|
||||
const struct device **output_devices = kscan_gpio_output_devices_##n(dev); \
|
||||
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \
|
||||
|
@ -262,8 +272,8 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
LOG_ERR("Unable to find output GPIO device"); \
|
||||
return -EINVAL; \
|
||||
} \
|
||||
err = gpio_pin_configure(output_devices[o], out_cfg->pin, \
|
||||
GPIO_OUTPUT_ACTIVE | out_cfg->flags); \
|
||||
err = \
|
||||
gpio_pin_configure(output_devices[o], out_cfg->pin, GPIO_OUTPUT | out_cfg->flags); \
|
||||
if (err) { \
|
||||
LOG_ERR("Unable to configure pin %d on %s for output", out_cfg->pin, \
|
||||
out_cfg->label); \
|
||||
|
@ -271,10 +281,12 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
} \
|
||||
} \
|
||||
data->dev = dev; \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, \
|
||||
(k_timer_init(&data->poll_timer, kscan_gpio_timer_handler, NULL);), ()) \
|
||||
(COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work_init), (k_delayed_work_init)))( \
|
||||
&data->work, kscan_gpio_work_handler_##n); \
|
||||
COND_POLL_OR_INTERRUPTS( \
|
||||
(k_timer_init(&data->poll_timer, kscan_gpio_timer_handler_##n, NULL); \
|
||||
kscan_gpio_set_output_state_##n(dev, 0);), \
|
||||
(kscan_gpio_set_output_state_##n(dev, 1);)) \
|
||||
return 0; \
|
||||
} \
|
||||
static const struct kscan_driver_api gpio_driver_api_##n = { \
|
||||
|
|
|
@ -77,6 +77,9 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
|||
LOG_DBG("Failed to enable ADC power GPIO: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// wait for any capacitance to charge up
|
||||
k_sleep(K_MSEC(10));
|
||||
}
|
||||
|
||||
// Read ADC
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
bt: behavior_bluetooth {
|
||||
/omit-if-no-ref/ bt: behavior_bluetooth {
|
||||
compatible = "zmk,behavior-bluetooth";
|
||||
label = "BLUETOOTH";
|
||||
#binding-cells = <2>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
ext_power: behavior_ext_power {
|
||||
/omit-if-no-ref/ ext_power: behavior_ext_power {
|
||||
compatible = "zmk,behavior-ext-power";
|
||||
label = "EXT_POWER_BEHAVIOR";
|
||||
#binding-cells = <1>;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
/ {
|
||||
behaviors {
|
||||
/* DEPRECATED: `cp` will be removed in the future */
|
||||
cp: kp: behavior_key_press {
|
||||
/omit-if-no-ref/ cp: kp: behavior_key_press {
|
||||
compatible = "zmk,behavior-key-press";
|
||||
label = "KEY_PRESS";
|
||||
#binding-cells = <1>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
lt: behavior_layer_tap {
|
||||
/omit-if-no-ref/ lt: behavior_layer_tap {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
label = "LAYER_TAP";
|
||||
#binding-cells = <2>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
mt: behavior_mod_tap {
|
||||
/omit-if-no-ref/ mt: behavior_mod_tap {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
label = "MOD_TAP";
|
||||
#binding-cells = <2>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
mo: behavior_momentary_layer {
|
||||
/omit-if-no-ref/ mo: behavior_momentary_layer {
|
||||
compatible = "zmk,behavior-momentary-layer";
|
||||
label = "MO";
|
||||
#binding-cells = <1>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
none: behavior_none {
|
||||
/omit-if-no-ref/ none: behavior_none {
|
||||
compatible = "zmk,behavior-none";
|
||||
label = "NONE";
|
||||
#binding-cells = <0>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
out: behavior_outputs {
|
||||
/omit-if-no-ref/ out: behavior_outputs {
|
||||
compatible = "zmk,behavior-outputs";
|
||||
label = "OUTPUTS";
|
||||
#binding-cells = <1>;
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
reset: behavior_reset {
|
||||
/omit-if-no-ref/ reset: behavior_reset {
|
||||
compatible = "zmk,behavior-reset";
|
||||
label = "RESET";
|
||||
#binding-cells = <0>;
|
||||
};
|
||||
|
||||
bootloader: behavior_reset_dfu {
|
||||
/omit-if-no-ref/ bootloader: behavior_reset_dfu {
|
||||
compatible = "zmk,behavior-reset";
|
||||
label = "BOOTLOADER_RESET";
|
||||
type = <RST_UF2>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
rgb_ug: behavior_rgb_underglow {
|
||||
/omit-if-no-ref/ rgb_ug: behavior_rgb_underglow {
|
||||
compatible = "zmk,behavior-rgb-underglow";
|
||||
label = "RGB_UNDERGLOW";
|
||||
#binding-cells = <2>;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
/ {
|
||||
behaviors {
|
||||
/* DEPRECATED: `inc_dec_cp` will be removed in the future */
|
||||
inc_dec_cp: inc_dec_kp: behavior_sensor_rotate_key_press {
|
||||
/omit-if-no-ref/ inc_dec_cp: inc_dec_kp: behavior_sensor_rotate_key_press {
|
||||
compatible = "zmk,behavior-sensor-rotate-key-press";
|
||||
label = "ENC_KEY_PRESS";
|
||||
#sensor-binding-cells = <2>;
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
sk: behavior_sticky_key {
|
||||
/omit-if-no-ref/ sk: behavior_sticky_key {
|
||||
compatible = "zmk,behavior-sticky-key";
|
||||
label = "STICKY_KEY";
|
||||
#binding-cells = <1>;
|
||||
release-after-ms = <1000>;
|
||||
bindings = <&kp>;
|
||||
};
|
||||
sl: behavior_sticky_layer {
|
||||
/omit-if-no-ref/ sl: behavior_sticky_layer {
|
||||
compatible = "zmk,behavior-sticky-key";
|
||||
label = "STICKY_LAYER";
|
||||
#binding-cells = <1>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
to: behavior_to_layer {
|
||||
/omit-if-no-ref/ to: behavior_to_layer {
|
||||
compatible = "zmk,behavior-to-layer";
|
||||
label = "TO_LAYER";
|
||||
#binding-cells = <1>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
tog: behavior_toggle_layer {
|
||||
/omit-if-no-ref/ tog: behavior_toggle_layer {
|
||||
compatible = "zmk,behavior-toggle-layer";
|
||||
label = "TOGGLE_LAYER";
|
||||
#binding-cells = <1>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/ {
|
||||
behaviors {
|
||||
trans: behavior_transparent {
|
||||
/omit-if-no-ref/ trans: behavior_transparent {
|
||||
compatible = "zmk,behavior-transparent";
|
||||
label = "TRANS";
|
||||
#binding-cells = <0>;
|
||||
|
|
|
@ -13,6 +13,9 @@ properties:
|
|||
required: true
|
||||
tapping_term_ms:
|
||||
type: int
|
||||
quick_tap_ms:
|
||||
type: int
|
||||
default: -1
|
||||
flavor:
|
||||
type: string
|
||||
required: false
|
||||
|
|
|
@ -20,3 +20,6 @@ child-binding:
|
|||
default: 50
|
||||
slow-release:
|
||||
type: boolean
|
||||
layers:
|
||||
type: array
|
||||
default: [-1]
|
|
@ -19,3 +19,12 @@ int zmk_keymap_layer_to(uint8_t layer);
|
|||
const char *zmk_keymap_layer_label(uint8_t layer);
|
||||
|
||||
int zmk_keymap_position_state_changed(uint32_t position, bool pressed, int64_t timestamp);
|
||||
|
||||
#define ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst) \
|
||||
{ \
|
||||
.behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(drv_inst, bindings, idx)), \
|
||||
.param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(drv_inst, bindings, idx, param1), (0), \
|
||||
(DT_PHA_BY_IDX(drv_inst, bindings, idx, param1))), \
|
||||
.param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(drv_inst, bindings, idx, param2), (0), \
|
||||
(DT_PHA_BY_IDX(drv_inst, bindings, idx, param2))), \
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
|
||||
#include <zmk/ble.h>
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
switch (binding->param1) {
|
||||
|
@ -49,3 +51,5 @@ static const struct behavior_driver_api behavior_bt_driver_api = {
|
|||
|
||||
DEVICE_AND_API_INIT(behavior_bt, DT_INST_LABEL(0), behavior_bt_init, NULL, NULL, APPLICATION,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_bt_driver_api);
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
|
@ -16,6 +16,8 @@
|
|||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
const struct device *ext_power = device_get_binding("EXT_POWER");
|
||||
|
@ -55,3 +57,5 @@ static const struct behavior_driver_api behavior_ext_power_driver_api = {
|
|||
|
||||
DEVICE_AND_API_INIT(behavior_ext_power, DT_INST_LABEL(0), behavior_ext_power_init, NULL, NULL,
|
||||
APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api);
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
#include <zmk/events/position_state_changed.h>
|
||||
#include <zmk/events/keycode_state_changed.h>
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/keymap.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_NODE_EXISTS(DT_DRV_INST(0))
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
#define ZMK_BHV_HOLD_TAP_MAX_HELD 10
|
||||
#define ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS 40
|
||||
|
@ -35,14 +36,11 @@ enum flavor {
|
|||
ZMK_BHV_HOLD_TAP_FLAVOR_TAP_PREFERRED = 2,
|
||||
};
|
||||
|
||||
struct behavior_hold_tap_behaviors {
|
||||
struct zmk_behavior_binding tap;
|
||||
struct zmk_behavior_binding hold;
|
||||
};
|
||||
|
||||
struct behavior_hold_tap_config {
|
||||
int tapping_term_ms;
|
||||
struct behavior_hold_tap_behaviors *behaviors;
|
||||
char *hold_behavior_dev;
|
||||
char *tap_behavior_dev;
|
||||
int quick_tap_ms;
|
||||
enum flavor flavor;
|
||||
};
|
||||
|
||||
|
@ -70,6 +68,24 @@ struct active_hold_tap active_hold_taps[ZMK_BHV_HOLD_TAP_MAX_HELD] = {};
|
|||
// We capture most position_state_changed events and some modifiers_state_changed events.
|
||||
const zmk_event_t *captured_events[ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS] = {};
|
||||
|
||||
// Keep track of which key was tapped most recently for 'quick_tap_ms'
|
||||
struct last_tapped {
|
||||
int32_t position;
|
||||
int64_t tap_deadline;
|
||||
};
|
||||
|
||||
struct last_tapped last_tapped;
|
||||
|
||||
static void store_last_tapped(struct active_hold_tap *hold_tap) {
|
||||
last_tapped.position = hold_tap->position;
|
||||
last_tapped.tap_deadline = hold_tap->timestamp + hold_tap->config->quick_tap_ms;
|
||||
}
|
||||
|
||||
static bool is_quick_tap(struct active_hold_tap *hold_tap) {
|
||||
return last_tapped.position == hold_tap->position &&
|
||||
last_tapped.tap_deadline > hold_tap->timestamp;
|
||||
}
|
||||
|
||||
static int capture_event(const zmk_event_t *event) {
|
||||
for (int i = 0; i < ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS; i++) {
|
||||
if (captured_events[i] == NULL) {
|
||||
|
@ -194,6 +210,7 @@ enum decision_moment {
|
|||
HT_OTHER_KEY_DOWN = 1,
|
||||
HT_OTHER_KEY_UP = 2,
|
||||
HT_TIMER_EVENT = 3,
|
||||
HT_QUICK_TAP = 4,
|
||||
};
|
||||
|
||||
static void decide_balanced(struct active_hold_tap *hold_tap, enum decision_moment event) {
|
||||
|
@ -207,6 +224,10 @@ static void decide_balanced(struct active_hold_tap *hold_tap, enum decision_mome
|
|||
hold_tap->is_hold = 1;
|
||||
hold_tap->is_decided = true;
|
||||
break;
|
||||
case HT_QUICK_TAP:
|
||||
hold_tap->is_hold = 0;
|
||||
hold_tap->is_decided = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
@ -222,6 +243,10 @@ static void decide_tap_preferred(struct active_hold_tap *hold_tap, enum decision
|
|||
hold_tap->is_hold = 1;
|
||||
hold_tap->is_decided = true;
|
||||
break;
|
||||
case HT_QUICK_TAP:
|
||||
hold_tap->is_hold = 0;
|
||||
hold_tap->is_decided = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
@ -238,6 +263,10 @@ static void decide_hold_preferred(struct active_hold_tap *hold_tap, enum decisio
|
|||
hold_tap->is_hold = 1;
|
||||
hold_tap->is_decided = true;
|
||||
break;
|
||||
case HT_QUICK_TAP:
|
||||
hold_tap->is_hold = 0;
|
||||
hold_tap->is_decided = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
@ -289,13 +318,14 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_mome
|
|||
|
||||
struct zmk_behavior_binding binding;
|
||||
if (hold_tap->is_hold) {
|
||||
binding.behavior_dev = hold_tap->config->behaviors->hold.behavior_dev;
|
||||
binding.behavior_dev = hold_tap->config->hold_behavior_dev;
|
||||
binding.param1 = hold_tap->param_hold;
|
||||
binding.param2 = 0;
|
||||
} else {
|
||||
binding.behavior_dev = hold_tap->config->behaviors->tap.behavior_dev;
|
||||
binding.behavior_dev = hold_tap->config->tap_behavior_dev;
|
||||
binding.param1 = hold_tap->param_tap;
|
||||
binding.param2 = 0;
|
||||
store_last_tapped(hold_tap);
|
||||
}
|
||||
behavior_keymap_binding_pressed(&binding, event);
|
||||
release_captured_events();
|
||||
|
@ -323,6 +353,10 @@ static int on_hold_tap_binding_pressed(struct zmk_behavior_binding *binding,
|
|||
LOG_DBG("%d new undecided hold_tap", event.position);
|
||||
undecided_hold_tap = hold_tap;
|
||||
|
||||
if (is_quick_tap(hold_tap)) {
|
||||
decide_hold_tap(hold_tap, HT_QUICK_TAP);
|
||||
}
|
||||
|
||||
// if this behavior was queued we have to adjust the timer to only
|
||||
// wait for the remaining time.
|
||||
int32_t tapping_term_ms_left = (hold_tap->timestamp + cfg->tapping_term_ms) - k_uptime_get();
|
||||
|
@ -350,7 +384,8 @@ static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding,
|
|||
|
||||
decide_hold_tap(hold_tap, HT_KEY_UP);
|
||||
|
||||
// todo: set up the binding and data items inside of the active_hold_tap struct
|
||||
// todo: set up the binding and data items inside of the
|
||||
// active_hhold_tap->config->behaviors->tap.behavior_dev;old_tap struct
|
||||
struct zmk_behavior_binding_event sub_behavior_data = {
|
||||
.position = hold_tap->position,
|
||||
.timestamp = hold_tap->timestamp,
|
||||
|
@ -358,11 +393,11 @@ static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding,
|
|||
|
||||
struct zmk_behavior_binding sub_behavior_binding;
|
||||
if (hold_tap->is_hold) {
|
||||
sub_behavior_binding.behavior_dev = hold_tap->config->behaviors->hold.behavior_dev;
|
||||
sub_behavior_binding.behavior_dev = hold_tap->config->hold_behavior_dev;
|
||||
sub_behavior_binding.param1 = hold_tap->param_hold;
|
||||
sub_behavior_binding.param2 = 0;
|
||||
} else {
|
||||
sub_behavior_binding.behavior_dev = hold_tap->config->behaviors->tap.behavior_dev;
|
||||
sub_behavior_binding.behavior_dev = hold_tap->config->tap_behavior_dev;
|
||||
sub_behavior_binding.param1 = hold_tap->param_tap;
|
||||
sub_behavior_binding.param2 = 0;
|
||||
}
|
||||
|
@ -489,22 +524,12 @@ static int behavior_hold_tap_init(const struct device *dev) {
|
|||
struct behavior_hold_tap_data {};
|
||||
static struct behavior_hold_tap_data behavior_hold_tap_data;
|
||||
|
||||
/* todo: get rid of unused param1 and param2. */
|
||||
#define _TRANSFORM_ENTRY(idx, node) \
|
||||
{ \
|
||||
.behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \
|
||||
.param1 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param1), (0), \
|
||||
(DT_INST_PHA_BY_IDX(node, bindings, idx, param1))), \
|
||||
.param2 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param2), (0), \
|
||||
(DT_INST_PHA_BY_IDX(node, bindings, idx, param2))), \
|
||||
},
|
||||
|
||||
#define KP_INST(n) \
|
||||
static struct behavior_hold_tap_behaviors behavior_hold_tap_behaviors_##n = { \
|
||||
.hold = _TRANSFORM_ENTRY(0, n).tap = _TRANSFORM_ENTRY(1, n)}; \
|
||||
static struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \
|
||||
.behaviors = &behavior_hold_tap_behaviors_##n, \
|
||||
.tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \
|
||||
.hold_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 0)), \
|
||||
.tap_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 1)), \
|
||||
.quick_tap_ms = DT_INST_PROP(n, quick_tap_ms), \
|
||||
.flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \
|
||||
}; \
|
||||
DEVICE_AND_API_INIT(behavior_hold_tap_##n, DT_INST_LABEL(n), behavior_hold_tap_init, \
|
||||
|
@ -513,4 +538,4 @@ static struct behavior_hold_tap_data behavior_hold_tap_data;
|
|||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||
|
||||
#endif
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
struct behavior_none_config {};
|
||||
struct behavior_none_data {};
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int behavior_none_init(const struct device *dev) { return 0; };
|
||||
|
||||
|
@ -35,10 +34,7 @@ static const struct behavior_driver_api behavior_none_driver_api = {
|
|||
.binding_released = on_keymap_binding_released,
|
||||
};
|
||||
|
||||
static const struct behavior_none_config behavior_none_config = {};
|
||||
DEVICE_AND_API_INIT(behavior_none, DT_INST_LABEL(0), behavior_none_init, NULL, NULL, APPLICATION,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_none_driver_api);
|
||||
|
||||
static struct behavior_none_data behavior_none_data;
|
||||
|
||||
DEVICE_AND_API_INIT(behavior_none, DT_INST_LABEL(0), behavior_none_init, &behavior_none_data,
|
||||
&behavior_none_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||
&behavior_none_driver_api);
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
|
@ -18,6 +18,8 @@
|
|||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
switch (binding->param1) {
|
||||
|
@ -42,3 +44,5 @@ static const struct behavior_driver_api behavior_outputs_driver_api = {
|
|||
|
||||
DEVICE_AND_API_INIT(behavior_out, DT_INST_LABEL(0), behavior_out_init, NULL, NULL, APPLICATION,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_outputs_driver_api);
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
struct behavior_reset_config {
|
||||
int type;
|
||||
};
|
||||
|
@ -45,3 +46,5 @@ static const struct behavior_driver_api behavior_reset_driver_api = {
|
|||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_reset_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(RST_INST)
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int behavior_rgb_underglow_init(const struct device *dev) { return 0; }
|
||||
|
||||
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
|
@ -64,3 +66,5 @@ static const struct behavior_driver_api behavior_rgb_underglow_driver_api = {
|
|||
DEVICE_AND_API_INIT(behavior_rgb_underglow, DT_INST_LABEL(0), behavior_rgb_underglow_init, NULL,
|
||||
NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||
&behavior_rgb_underglow_driver_api);
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int behavior_sensor_rotate_key_press_init(const struct device *dev) { return 0; };
|
||||
|
||||
static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding,
|
||||
|
@ -63,3 +65,5 @@ static const struct behavior_driver_api behavior_sensor_rotate_key_press_driver_
|
|||
&behavior_sensor_rotate_key_press_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <zmk/events/keycode_state_changed.h>
|
||||
#include <zmk/events/modifiers_state_changed.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/keymap.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
|
@ -260,18 +261,10 @@ static int behavior_sticky_key_init(const struct device *dev) {
|
|||
struct behavior_sticky_key_data {};
|
||||
static struct behavior_sticky_key_data behavior_sticky_key_data;
|
||||
|
||||
#define _TRANSFORM_ENTRY(idx, node) \
|
||||
{ \
|
||||
.behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \
|
||||
.param1 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param1), (0), \
|
||||
(DT_INST_PHA_BY_IDX(node, bindings, idx, param1))), \
|
||||
.param2 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param2), (0), \
|
||||
(DT_INST_PHA_BY_IDX(node, bindings, idx, param2))), \
|
||||
},
|
||||
|
||||
#define KP_INST(n) \
|
||||
static struct behavior_sticky_key_config behavior_sticky_key_config_##n = { \
|
||||
.behavior = _TRANSFORM_ENTRY(0, n).release_after_ms = DT_INST_PROP(n, release_after_ms), \
|
||||
.behavior = ZMK_KEYMAP_EXTRACT_BINDING(0, DT_DRV_INST(n)), \
|
||||
.release_after_ms = DT_INST_PROP(n, release_after_ms), \
|
||||
.quick_release = DT_INST_PROP(n, quick_release), \
|
||||
}; \
|
||||
DEVICE_AND_API_INIT(behavior_sticky_key_##n, DT_INST_LABEL(n), behavior_sticky_key_init, \
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int behavior_to_init(const struct device *dev) { return 0; };
|
||||
|
||||
static int to_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
|
@ -37,3 +39,5 @@ static const struct behavior_driver_api behavior_to_driver_api = {
|
|||
|
||||
DEVICE_AND_API_INIT(behavior_to, DT_INST_LABEL(0), behavior_to_init, NULL, NULL, APPLICATION,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_to_driver_api);
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
struct behavior_tog_config {};
|
||||
struct behavior_tog_data {};
|
||||
|
||||
|
@ -44,3 +46,5 @@ static struct behavior_tog_data behavior_tog_data;
|
|||
DEVICE_AND_API_INIT(behavior_tog, DT_INST_LABEL(0), behavior_tog_init, &behavior_tog_data,
|
||||
&behavior_tog_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||
&behavior_tog_driver_api);
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
struct behavior_transparent_config {};
|
||||
struct behavior_transparent_data {};
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int behavior_transparent_init(const struct device *dev) { return 0; };
|
||||
|
||||
|
@ -35,10 +34,8 @@ static const struct behavior_driver_api behavior_transparent_driver_api = {
|
|||
.binding_released = on_keymap_binding_released,
|
||||
};
|
||||
|
||||
static const struct behavior_transparent_config behavior_transparent_config = {};
|
||||
DEVICE_AND_API_INIT(behavior_transparent, DT_INST_LABEL(0), behavior_transparent_init, NULL, NULL,
|
||||
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||
&behavior_transparent_driver_api);
|
||||
|
||||
static struct behavior_transparent_data behavior_transparent_data;
|
||||
|
||||
DEVICE_AND_API_INIT(behavior_transparent, DT_INST_LABEL(0), behavior_transparent_init,
|
||||
&behavior_transparent_data, &behavior_transparent_config, APPLICATION,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_transparent_driver_api);
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
|
@ -63,6 +63,8 @@ static uint8_t active_profile;
|
|||
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
|
||||
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
|
||||
|
||||
BUILD_ASSERT(DEVICE_NAME_LEN <= 16, "ERROR: BLE device name is too long. Max length: 16");
|
||||
|
||||
#define IS_HOST_PERIPHERAL \
|
||||
(!IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
|
||||
#define IS_SPLIT_PERIPHERAL \
|
||||
|
@ -429,7 +431,7 @@ static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t l
|
|||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
LOG_DBG("%s: interval %d latency %d timeout %d", addr, interval, latency, timeout);
|
||||
LOG_DBG("%s: interval %d latency %d timeout %d", log_strdup(addr), interval, latency, timeout);
|
||||
}
|
||||
|
||||
static struct bt_conn_cb conn_callbacks = {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <zmk/events/position_state_changed.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/matrix.h>
|
||||
#include <zmk/keymap.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
|
@ -33,6 +34,8 @@ struct combo_cfg {
|
|||
// the virtual key position is a key position outside the range used by the keyboard.
|
||||
// it is necessary so hold-taps can uniquely identify a behavior.
|
||||
int32_t virtual_key_position;
|
||||
int32_t layers_len;
|
||||
int8_t layers[];
|
||||
};
|
||||
|
||||
struct active_combo {
|
||||
|
@ -104,17 +107,35 @@ static int initialize_combo(struct combo_cfg *new_combo) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool combo_active_on_layer(struct combo_cfg *combo, uint8_t layer) {
|
||||
if (combo->layers[0] == -1) {
|
||||
// -1 in the first layer position is global layer scope
|
||||
return true;
|
||||
}
|
||||
for (int j = 0; j < combo->layers_len; j++) {
|
||||
if (combo->layers[j] == layer) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int setup_candidates_for_first_keypress(int32_t position, int64_t timestamp) {
|
||||
int number_of_combo_candidates = 0;
|
||||
uint8_t highest_active_layer = zmk_keymap_highest_layer_active();
|
||||
for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) {
|
||||
struct combo_cfg *combo = combo_lookup[position][i];
|
||||
if (combo == NULL) {
|
||||
return i;
|
||||
return number_of_combo_candidates;
|
||||
}
|
||||
if (combo_active_on_layer(combo, highest_active_layer)) {
|
||||
candidates[number_of_combo_candidates].combo = combo;
|
||||
candidates[number_of_combo_candidates].timeout_at = timestamp + combo->timeout_ms;
|
||||
number_of_combo_candidates++;
|
||||
}
|
||||
candidates[i].combo = combo;
|
||||
candidates[i].timeout_at = timestamp + combo->timeout_ms;
|
||||
// LOG_DBG("combo timeout %d %d %d", position, i, candidates[i].timeout_at);
|
||||
}
|
||||
return CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY;
|
||||
return number_of_combo_candidates;
|
||||
}
|
||||
|
||||
static int filter_candidates(int32_t position) {
|
||||
|
@ -451,6 +472,8 @@ ZMK_SUBSCRIPTION(combo, zmk_position_state_changed);
|
|||
.behavior = KEY_BINDING_TO_STRUCT(0, n), \
|
||||
.virtual_key_position = ZMK_KEYMAP_LEN + __COUNTER__, \
|
||||
.slow_release = DT_PROP(n, slow_release), \
|
||||
.layers = DT_PROP(n, layers), \
|
||||
.layers_len = DT_PROP_LEN(n, layers), \
|
||||
};
|
||||
|
||||
#define INITIALIZE_COMBO(n) initialize_combo(&combo_config_##n);
|
||||
|
|
|
@ -28,18 +28,10 @@ static uint8_t _zmk_keymap_layer_default = 0;
|
|||
#define ZMK_KEYMAP_NODE DT_DRV_INST(0)
|
||||
#define ZMK_KEYMAP_LAYERS_LEN (DT_INST_FOREACH_CHILD(0, LAYER_CHILD_LEN) 0)
|
||||
|
||||
#define LAYER_NODE(l) DT_PHANDLE_BY_IDX(ZMK_KEYMAP_NODE, layers, l)
|
||||
#define BINDING_WITH_COMMA(idx, drv_inst) ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst),
|
||||
|
||||
#define _TRANSFORM_ENTRY(idx, layer) \
|
||||
{ \
|
||||
.behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(layer, bindings, idx)), \
|
||||
.param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, bindings, idx, param1), (0), \
|
||||
(DT_PHA_BY_IDX(layer, bindings, idx, param1))), \
|
||||
.param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, bindings, idx, param2), (0), \
|
||||
(DT_PHA_BY_IDX(layer, bindings, idx, param2))), \
|
||||
},
|
||||
|
||||
#define TRANSFORMED_LAYER(node) {UTIL_LISTIFY(DT_PROP_LEN(node, bindings), _TRANSFORM_ENTRY, node)},
|
||||
#define TRANSFORMED_LAYER(node) \
|
||||
{UTIL_LISTIFY(DT_PROP_LEN(node, bindings), BINDING_WITH_COMMA, node)},
|
||||
|
||||
#if ZMK_KEYMAP_HAS_SENSORS
|
||||
#define _TRANSFORM_SENSOR_ENTRY(idx, layer) \
|
||||
|
|
2
app/tests/combo/layer-filter-0/events.patterns
Normal file
2
app/tests/combo/layer-filter-0/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo//p
|
8
app/tests/combo/layer-filter-0/keycode_events.snapshot
Normal file
8
app/tests/combo/layer-filter-0/keycode_events.snapshot
Normal file
|
@ -0,0 +1,8 @@
|
|||
pressed: usage_page 0x07 keycode 0x1b implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
78
app/tests/combo/layer-filter-0/native_posix.keymap
Normal file
78
app/tests/combo/layer-filter-0/native_posix.keymap
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/* it is useful to set timeout to a large value when attaching a debugger. */
|
||||
#define TIMEOUT (60*60*1000)
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <TIMEOUT>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp X>;
|
||||
layers = <0>;
|
||||
};
|
||||
|
||||
combo_two {
|
||||
timeout-ms = <TIMEOUT>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp Y>;
|
||||
layers = <1>;
|
||||
};
|
||||
|
||||
combo_three {
|
||||
timeout-ms = <TIMEOUT>;
|
||||
key-positions = <0 2>;
|
||||
bindings = <&kp Z>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp C &tog 1
|
||||
>;
|
||||
};
|
||||
|
||||
filtered_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp C &tog 0
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
/* Combo One */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
/* Combo Three */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(1,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(1,1,10)
|
||||
/* Toggle Layer */
|
||||
ZMK_MOCK_PRESS(1,1,10)
|
||||
ZMK_MOCK_RELEASE(1,1,10)
|
||||
/* Combo Two */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
/* Combo Three */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(1,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(1,1,10)
|
||||
>;
|
||||
};
|
2
app/tests/combo/layer-filter-1/events.patterns
Normal file
2
app/tests/combo/layer-filter-1/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo//p
|
4
app/tests/combo/layer-filter-1/keycode_events.snapshot
Normal file
4
app/tests/combo/layer-filter-1/keycode_events.snapshot
Normal file
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
40
app/tests/combo/layer-filter-1/native_posix.keymap
Normal file
40
app/tests/combo/layer-filter-1/native_posix.keymap
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/* it is useful to set timeout to a large value when attaching a debugger. */
|
||||
#define TIMEOUT (60*60*1000)
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <TIMEOUT>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp X>;
|
||||
layers = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp C &tog 1
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
/* Combo One */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
>;
|
||||
};
|
4
app/tests/hold-tap/balanced/5-quick-tap/events.patterns
Normal file
4
app/tests/hold-tap/balanced/5-quick-tap/events.patterns
Normal file
|
@ -0,0 +1,4 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*mo_keymap_binding/mo/p
|
||||
s/.*on_hold_tap_binding/ht_binding/p
|
||||
s/.*decide_hold_tap/ht_decide/p
|
|
@ -0,0 +1,10 @@
|
|||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided tap (balanced event 0)
|
||||
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
||||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided tap (balanced event 4)
|
||||
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
14
app/tests/hold-tap/balanced/5-quick-tap/native_posix.keymap
Normal file
14
app/tests/hold-tap/balanced/5-quick-tap/native_posix.keymap
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap.dtsi"
|
||||
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,400)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
>;
|
||||
};
|
|
@ -10,6 +10,7 @@
|
|||
#binding-cells = <2>;
|
||||
flavor = "balanced";
|
||||
tapping_term_ms = <300>;
|
||||
quick_tap_ms = <200>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*mo_keymap_binding/mo/p
|
||||
s/.*on_hold_tap_binding/ht_binding/p
|
||||
s/.*decide_hold_tap/ht_decide/p
|
|
@ -0,0 +1,10 @@
|
|||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided tap (hold-preferred event 0)
|
||||
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
||||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided tap (hold-preferred event 4)
|
||||
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
|
@ -0,0 +1,14 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap.dtsi"
|
||||
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,400)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
>;
|
||||
};
|
|
@ -12,6 +12,7 @@
|
|||
#binding-cells = <2>;
|
||||
flavor = "hold-preferred";
|
||||
tapping_term_ms = <300>;
|
||||
quick_tap_ms = <200>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*mo_keymap_binding/mo/p
|
||||
s/.*on_hold_tap_binding/ht_binding/p
|
||||
s/.*decide_hold_tap/ht_decide/p
|
|
@ -0,0 +1,10 @@
|
|||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided tap (tap-preferred event 0)
|
||||
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
||||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided tap (tap-preferred event 4)
|
||||
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
|
@ -0,0 +1,14 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap.dtsi"
|
||||
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,400)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
>;
|
||||
};
|
|
@ -10,6 +10,7 @@
|
|||
#binding-cells = <2>;
|
||||
flavor = "tap-preferred";
|
||||
tapping_term_ms = <300>;
|
||||
quick_tap_ms = <200>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -13,6 +13,10 @@ computer/laptop/keyboard should receive the keyboard input; many of the commands
|
|||
Please note there are only five available Bluetooth profiles by default. If you need to increase the number of available profiles you can set `CONFIG_BT_MAX_CONN` in your `zmk-config` `.conf` file.
|
||||
:::
|
||||
|
||||
:::note Connection Management
|
||||
A ZMK device may show as "connected" on multiple hosts at the same time. This is working as intended, and only the host associated with the active profile will receive keystrokes.
|
||||
:::
|
||||
|
||||
## Bluetooth Command Defines
|
||||
|
||||
Bluetooth command defines are provided through the [`dt-bindings/zmk/bt.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/bt.h) header,
|
||||
|
@ -71,7 +75,7 @@ The bluetooth behavior completes an bluetooth action given on press.
|
|||
|
||||
## Bluetooth Pairing and Profiles
|
||||
|
||||
ZMK support bluetooth “profiles” which allows connection to multiple devices (5 by default, or 4 if you are using split keyboards). Each profile stores the bluetooth MAC address of a peer, which can be empty if a profile has not been paired with a device yet. Upon switching to a profile, ZMK does the following:
|
||||
ZMK support bluetooth “profiles” which allows connection to multiple devices (5 by default). Each profile stores the bluetooth MAC address of a peer, which can be empty if a profile has not been paired with a device yet. Upon switching to a profile, ZMK does the following:
|
||||
|
||||
- If a profile has not been paired with a peer yet, ZMK automatically advertise itself as connectable. You can discover you keyboard from bluetooth scanning on your laptop / tablet. If you try to connect, it will trigger the _pairing_ procedure. After pairing, the bluetooth MAC address of the peer device will be stored in the current profile. Pairing also negotiate a random key for secure communication between the device and the keyboard.
|
||||
- If a profile has been paired but the peer is not connected yet, ZMK will also advertise itself as connectable. In the future, the behavior might change to _direct advertising_ which only target the peer with the stored bluetooth MAC address. In this state, if the peer is powered on and moved within the distance of bluetooth signal coverage, it should automatically connect to the keyboard.
|
||||
|
|
|
@ -11,7 +11,7 @@ Simply put, the hold-tap key will output the 'hold' behavior if it's held for a
|
|||
|
||||
### Hold-Tap
|
||||
|
||||
The `tapping_term_ms` parameter decides between a 'tap' and a 'hold'.
|
||||
The graph below shows how the hold-tap decides between a 'tap' and a 'hold'.
|
||||
|
||||

|
||||
|
||||
|
@ -37,6 +37,18 @@ For basic usage, please see [mod-tap](./mod-tap.md) and [layer-tap](./layers.md)
|
|||
|
||||
### Advanced Configuration
|
||||
|
||||
#### `tapping_term_ms`
|
||||
|
||||
Defines how long a key must be pressed to trigger Hold behavior.
|
||||
|
||||
#### `quick_tap_ms`
|
||||
|
||||
If you press a tapped hold-tap again within `quick_tap_ms` milliseconds, it will always trigger the tap behavior. This is useful for things like a backspace, where a quick tap+hold holds backspace pressed. Set this to a negative value to disable. The default is -1 (disabled).
|
||||
|
||||
In QMK, unlike ZMK, this functionality is enabled by default, and you turn it off using `TAPPING_FORCE_HOLD`.
|
||||
|
||||
#### Home row mods
|
||||
|
||||
This example configures a hold-tap that works well for homerow mods:
|
||||
|
||||
```
|
||||
|
@ -50,6 +62,7 @@ This example configures a hold-tap that works well for homerow mods:
|
|||
label = "HOMEROW_MODS";
|
||||
#binding-cells = <2>;
|
||||
tapping_term_ms = <150>;
|
||||
quick_tap_ms = <0>;
|
||||
flavor = "tap-preferred";
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
|
|
|
@ -118,11 +118,17 @@ Now start VSCode and rebuild the container after being prompted. You should be a
|
|||
|
||||
## Flashing
|
||||
|
||||
Once built, the previously supplied parameters will be remembered so you can run the following to flash your
|
||||
board with it in bootloader mode:
|
||||
The above build commands generate a UF2 file in `build/zephyr` (or
|
||||
`build/left|right/zephyr` if you followed the instructions for splits) and is by
|
||||
default named `zmk.uf2`. If your board supports USB Flashing Format (UF2), copy
|
||||
that file onto the root of the USB mass storage device for your board. The
|
||||
controller should flash your built firmware and automatically restart once
|
||||
flashing is complete.
|
||||
|
||||
Alternatively, if your board supports flashing and you're not developing from
|
||||
within a Dockerized environment, enable Device Firmware Upgrade (DFU) mode on
|
||||
your board and run the following command to flash:
|
||||
|
||||
```
|
||||
west flash
|
||||
```
|
||||
|
||||
For boards that have drag and drop .uf2 flashing capability, the .uf2 file to flash can be found in `build/zephyr` (or `build/left|right/zephyr` if you followed the instructions for splits) and is by default named `zmk.uf2`.
|
||||
|
|
57
docs/docs/development/documentation.md
Normal file
57
docs/docs/development/documentation.md
Normal file
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
title: Documentation
|
||||
sidebar_label: Documentation
|
||||
---
|
||||
|
||||
This document outlines how to test your documentation changes locally and prepare the changes for a pull request.
|
||||
|
||||
The documentation is written with [Docusaurus](https://docusaurus.io/). The ZMK source code has all of the necessary Docusaurus dependencies included but referencing their documentation can be helpful at times.
|
||||
|
||||
The general process for updating the ZMK documentation is:
|
||||
|
||||
1. Update the documentation
|
||||
2. Test the changes locally
|
||||
3. Ensure the sources are formatted properly and linted
|
||||
4. Create a Pull Request for review and inclusion into the ZMK sources
|
||||
|
||||
:::note
|
||||
If you are working with the documentation from within VS Code+Docker please be aware the documentation will not be auto-generated when making changes while the server is running. You'll need to restart the server when saving changes to the documentation.
|
||||
:::
|
||||
|
||||
:::note
|
||||
You will need `Node.js` and `npm` installed to update the documentation. If you're using the ZMK dev container (Docker) the necessary dependencies are already installed.
|
||||
:::
|
||||
|
||||
## Testing Documentation Updates Locally
|
||||
|
||||
To verify documentation updates locally, follow the following procedure. The `npm` commands and first step will need to be run from a terminal.
|
||||
|
||||
1. Navigate to the `docs` folder
|
||||
2. Run `npm ci` to build the necessary tools if you haven't run it before or the ZMK sources were updated
|
||||
3. Run `npm start` to start the local server that will let you see your documentation updates via a web browser
|
||||
4. If a web browser doesn't open automatically: you'll need to open a browser window or tab and navigate to `http://localhost:3000` to view your changes
|
||||
5. Verify the changes look good
|
||||
|
||||
## Formatting and Linting Your Changes
|
||||
|
||||
Prior to submitting a documentation pull request, you'll want to run the format and check commands. These commands are run as part of the verification process on pull requests so it's good to run them ahead of submitting documentation updates.
|
||||
|
||||
The format command can be run with the following procedure in a terminal that's inside the ZMK source directory.
|
||||
|
||||
1. Navigate to the `docs` folder
|
||||
2. Run `npm run prettier:format`
|
||||
|
||||
The check commands can be run with the following procedure in a terminal that's inside the ZMK source directory.
|
||||
|
||||
1. Navigate to the `docs` folder
|
||||
2. Run `npm run prettier:check`
|
||||
3. Run `npm run lint`
|
||||
4. Run `npm run build`
|
||||
|
||||
:::warning
|
||||
If any of the above steps throw an error, they need to be addressed and all of the checks re-run prior to submitting a pull request.
|
||||
:::
|
||||
|
||||
## Submitting a Pull Request
|
||||
|
||||
Once the above sections are complete the documentation updates are ready to submit as a pull request.
|
|
@ -463,12 +463,22 @@ you should be able to test with a build command like:
|
|||
west build --pristine -b proton_c -- -DSHIELD=my_board
|
||||
```
|
||||
|
||||
and then flash with:
|
||||
The above build command generates `build/zephyr/zmk.uf2`. If your board
|
||||
supports USB Flashing Format (UF2), copy that file onto the root of the USB mass
|
||||
storage device for your board. The controller should flash your built firmware
|
||||
and automatically restart once flashing is complete.
|
||||
|
||||
Alternatively, if your board supports flashing and you're not developing from
|
||||
within a Dockerized environment, enable Device Firmware Upgrade (DFU) mode on
|
||||
your board and run the following command to test your build:
|
||||
|
||||
```
|
||||
west flash
|
||||
```
|
||||
|
||||
Please have a look at documentation specific to
|
||||
[building and flashing](build-flash) for additional information.
|
||||
|
||||
:::note
|
||||
Further testing your keyboard shield without altering the root keymap file can be done with the use of `-DZMK_CONFIG` in your `west build` command,
|
||||
shown [here](build-flash#building-from-zmk-config-folder)
|
||||
|
|
|
@ -18,6 +18,7 @@ Combos configured in your `.keymap` file, but are separate from the `keymap` nod
|
|||
timeout-ms = <50>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp ESC>;
|
||||
layers = <-1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -27,6 +28,7 @@ Combos configured in your `.keymap` file, but are separate from the `keymap` nod
|
|||
- The `compatible` property should always be `"zmk,combos"` for combos.
|
||||
- `timeout-ms` is the number of milliseconds that all keys of the combo must be pressed.
|
||||
- `key-positions` is an array of key positions. See the info section below about how to figure out the positions on your board.
|
||||
- `layers = <0 1...>` will allow limiting a combo to specific layers. this is an _optional_ parameter and defaults to `-1` which is global scope.
|
||||
- `bindings` is the behavior that is activated when the behavior is pressed.
|
||||
- (advanced) you can specify `slow-release` if you want the combo binding to be released when all key-positions are released. The default is to release the combo as soon as any of the keys in the combo is released.
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ Since then, a much simpler procedure of performing a bluetooth reset for split k
|
|||
|
||||
**New Procedure:**
|
||||
|
||||
1. [Open the GitHub `Actions` tab and select the `Build` workflow](https://github.com/zmkfirmware/zmk/actions?query=workflow%3ABuild+branch%3Amain).
|
||||
1. [Open the GitHub `Actions` tab and select the `Build` workflow](https://github.com/zmkfirmware/zmk/actions?query=workflow%3ABuild+branch%3Amain+event%3Apush).
|
||||
1. Select the top 'result' on that page.
|
||||
1. From the next page under "Artifacts", download the `$boardname-settings_reset-zmk` zip file.
|
||||
1. Unzip the downloaded file.
|
||||
|
|
|
@ -23,7 +23,7 @@ module.exports = {
|
|||
},
|
||||
items: [
|
||||
{
|
||||
to: "docs/",
|
||||
to: "docs",
|
||||
activeBasePath: "docs",
|
||||
label: "Docs",
|
||||
position: "left",
|
||||
|
|
|
@ -42,6 +42,7 @@ module.exports = {
|
|||
],
|
||||
Development: [
|
||||
"development/clean-room",
|
||||
"development/documentation",
|
||||
"development/setup",
|
||||
"development/build-flash",
|
||||
"development/boards-shields-keymaps",
|
||||
|
|
|
@ -59,7 +59,7 @@ function Home() {
|
|||
return (
|
||||
<Layout
|
||||
title={`Hello from ${siteConfig.title}`}
|
||||
description="Description will go into a meta tag in <head />"
|
||||
description="Modern, open source keyboard firmware."
|
||||
>
|
||||
<header className={classnames("hero hero--primary", styles.heroBanner)}>
|
||||
<div className="container">
|
||||
|
|
2
docs/static/setup.sh
vendored
2
docs/static/setup.sh
vendored
|
@ -79,7 +79,7 @@ select opt in "${options[@]}" "Quit"; do
|
|||
1 ) board="nice_nano"; break;;
|
||||
2 ) board="proton_c"; break;;
|
||||
3 ) board="bluemicro840_v1"; break;;
|
||||
3 ) board="nrf52840_m2"; break;;
|
||||
4 ) board="nrf52840_m2"; break;;
|
||||
|
||||
$(( ${#options[@]}+1 )) ) echo "Goodbye!"; exit 1;;
|
||||
*) echo "Invalid option. Try another one."; continue;;
|
||||
|
|
Loading…
Add table
Reference in a new issue