resolve merge conflicts
This commit is contained in:
commit
8ef471d465
62 changed files with 2499 additions and 1 deletions
5
CODEOWNERS
Normal file
5
CODEOWNERS
Normal file
|
@ -0,0 +1,5 @@
|
|||
* @zmkfirmware/core
|
||||
|
||||
/app/boards @zmkfirmware/boards-shields
|
||||
|
||||
/docs @zmkfirmware/docs
|
|
@ -52,6 +52,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
|||
target_sources(app PRIVATE src/behaviors/behavior_none.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
|
||||
target_sources(app PRIVATE src/combo.c)
|
||||
target_sources(app PRIVATE src/keymap.c)
|
||||
endif()
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)
|
||||
|
|
17
app/Kconfig
17
app/Kconfig
|
@ -251,6 +251,23 @@ config ZMK_EXT_POWER
|
|||
#Power Management
|
||||
endmenu
|
||||
|
||||
menu "Combo options"
|
||||
|
||||
config ZMK_COMBO_MAX_PRESSED_COMBOS
|
||||
int "Maximum number of currently pressed combos"
|
||||
default 4
|
||||
|
||||
config ZMK_COMBO_MAX_COMBOS_PER_KEY
|
||||
int "Maximum number of combos per key"
|
||||
default 5
|
||||
|
||||
config ZMK_COMBO_MAX_KEYS_PER_COMBO
|
||||
int "Maximum number of keys per combo"
|
||||
default 4
|
||||
|
||||
#Display/LED Options
|
||||
endmenu
|
||||
|
||||
menu "Advanced"
|
||||
|
||||
menu "Initialization Priorities"
|
||||
|
|
22
app/dts/bindings/zmk,combos.yaml
Normal file
22
app/dts/bindings/zmk,combos.yaml
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Copyright (c) 2020, The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Combos container
|
||||
|
||||
compatible: "zmk,combos"
|
||||
|
||||
child-binding:
|
||||
description: "A combo"
|
||||
|
||||
properties:
|
||||
bindings:
|
||||
type: phandle-array
|
||||
required: true
|
||||
key-positions:
|
||||
type: array
|
||||
required: true
|
||||
timeout-ms:
|
||||
type: int
|
||||
default: 50
|
||||
slow-release:
|
||||
type: boolean
|
466
app/src/combo.c
Normal file
466
app/src/combo.c
Normal file
|
@ -0,0 +1,466 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_combos
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <logging/log.h>
|
||||
#include <sys/dlist.h>
|
||||
#include <kernel.h>
|
||||
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/position_state_changed.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/matrix.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
struct combo_cfg {
|
||||
int32_t key_positions[CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO];
|
||||
int32_t key_position_len;
|
||||
struct zmk_behavior_binding behavior;
|
||||
int32_t timeout_ms;
|
||||
// if slow release is set, the combo releases when the last key is released.
|
||||
// otherwise, the combo releases when the first key is released.
|
||||
bool slow_release;
|
||||
// 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;
|
||||
};
|
||||
|
||||
struct active_combo {
|
||||
struct combo_cfg *combo;
|
||||
// key_positions_pressed is filled with key_positions when the combo is pressed.
|
||||
// The keys are removed from this array when they are released.
|
||||
// Once this array is empty, the behavior is released.
|
||||
struct position_state_changed *key_positions_pressed[CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO];
|
||||
};
|
||||
|
||||
struct combo_candidate {
|
||||
struct combo_cfg *combo;
|
||||
// the time after which this behavior should be removed from candidates.
|
||||
// by keeping track of when the candidate should be cleared there is no
|
||||
// possibility of accidental releases.
|
||||
int64_t timeout_at;
|
||||
};
|
||||
|
||||
// set of keys pressed
|
||||
struct position_state_changed *pressed_keys[CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO] = {NULL};
|
||||
// the set of candidate combos based on the currently pressed_keys
|
||||
struct combo_candidate candidates[CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY];
|
||||
// the last candidate that was completely pressed
|
||||
struct combo_cfg *fully_pressed_combo = NULL;
|
||||
// a lookup dict that maps a key position to all combos on that position
|
||||
struct combo_cfg *combo_lookup[ZMK_KEYMAP_LEN][CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY] = {NULL};
|
||||
// combos that have been activated and still have (some) keys pressed
|
||||
// this array is always contiguous from 0.
|
||||
struct active_combo active_combos[CONFIG_ZMK_COMBO_MAX_PRESSED_COMBOS] = {NULL};
|
||||
int active_combo_count = 0;
|
||||
|
||||
struct k_delayed_work timeout_task;
|
||||
int64_t timeout_task_timeout_at;
|
||||
|
||||
// Store the combo key pointer in the combos array, one pointer for each key position
|
||||
// The combos are sorted shortest-first, then by virtual-key-position.
|
||||
static int initialize_combo(struct combo_cfg *new_combo) {
|
||||
for (int i = 0; i < new_combo->key_position_len; i++) {
|
||||
int32_t position = new_combo->key_positions[i];
|
||||
if (position >= ZMK_KEYMAP_LEN) {
|
||||
LOG_ERR("Unable to initialize combo, key position %d does not exist", position);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct combo_cfg *insert_combo = new_combo;
|
||||
bool set = false;
|
||||
for (int j = 0; j < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; j++) {
|
||||
struct combo_cfg *combo_at_j = combo_lookup[position][j];
|
||||
if (combo_at_j == NULL) {
|
||||
combo_lookup[position][j] = insert_combo;
|
||||
set = true;
|
||||
break;
|
||||
}
|
||||
if (combo_at_j->key_position_len < insert_combo->key_position_len ||
|
||||
(combo_at_j->key_position_len == insert_combo->key_position_len &&
|
||||
combo_at_j->virtual_key_position < insert_combo->virtual_key_position)) {
|
||||
continue;
|
||||
}
|
||||
// put insert_combo in this spot, move all other combos up.
|
||||
combo_lookup[position][j] = insert_combo;
|
||||
insert_combo = combo_at_j;
|
||||
}
|
||||
if (!set) {
|
||||
LOG_ERR("Too many combos for key position %d, CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY %d.",
|
||||
position, CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_candidates_for_first_keypress(int32_t position, int64_t timestamp) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
static int filter_candidates(int32_t position) {
|
||||
// this code iterates over candidates and the lookup together to filter in O(n)
|
||||
// assuming they are both sorted on key_position_len, virtal_key_position
|
||||
int matches = 0, lookup_idx = 0, candidate_idx = 0;
|
||||
while (lookup_idx < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY &&
|
||||
candidate_idx < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY) {
|
||||
struct combo_cfg *candidate = candidates[candidate_idx].combo;
|
||||
struct combo_cfg *lookup = combo_lookup[position][lookup_idx];
|
||||
if (candidate == NULL || lookup == NULL) {
|
||||
break;
|
||||
}
|
||||
if (candidate->virtual_key_position == lookup->virtual_key_position) {
|
||||
candidates[matches] = candidates[candidate_idx];
|
||||
matches++;
|
||||
candidate_idx++;
|
||||
lookup_idx++;
|
||||
} else if (candidate->key_position_len > lookup->key_position_len) {
|
||||
lookup_idx++;
|
||||
} else if (candidate->key_position_len < lookup->key_position_len) {
|
||||
candidate_idx++;
|
||||
} else if (candidate->virtual_key_position > lookup->virtual_key_position) {
|
||||
lookup_idx++;
|
||||
} else if (candidate->virtual_key_position < lookup->virtual_key_position) {
|
||||
candidate_idx++;
|
||||
}
|
||||
}
|
||||
// clear unmatched candidates
|
||||
for (int i = matches; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) {
|
||||
candidates[i].combo = NULL;
|
||||
}
|
||||
// LOG_DBG("combo matches after filter %d", matches);
|
||||
return matches;
|
||||
}
|
||||
|
||||
static int64_t first_candidate_timeout() {
|
||||
int64_t first_timeout = LONG_MAX;
|
||||
for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) {
|
||||
if (candidates[i].combo == NULL) {
|
||||
break;
|
||||
}
|
||||
if (candidates[i].timeout_at < first_timeout) {
|
||||
first_timeout = candidates[i].timeout_at;
|
||||
}
|
||||
}
|
||||
return first_timeout;
|
||||
}
|
||||
|
||||
static inline bool candidate_is_completely_pressed(struct combo_cfg *candidate) {
|
||||
// this code assumes set(pressed_keys) <= set(candidate->key_positions)
|
||||
// this invariant is enforced by filter_candidates
|
||||
// the only thing we need to do is check if len(pressed_keys) == len(combo->key_positions)
|
||||
return pressed_keys[candidate->key_position_len - 1] != NULL;
|
||||
}
|
||||
|
||||
static void cleanup();
|
||||
|
||||
static int filter_timed_out_candidates(int64_t timestamp) {
|
||||
int num_candidates = 0;
|
||||
for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) {
|
||||
struct combo_candidate *candidate = &candidates[i];
|
||||
if (candidate->combo == NULL) {
|
||||
break;
|
||||
}
|
||||
if (candidate->timeout_at > timestamp) {
|
||||
// reorder candidates so they're contiguous
|
||||
candidates[num_candidates].combo = candidate->combo;
|
||||
candidates[num_candidates].timeout_at = candidates->timeout_at;
|
||||
num_candidates++;
|
||||
} else {
|
||||
candidate->combo = NULL;
|
||||
}
|
||||
}
|
||||
return num_candidates;
|
||||
}
|
||||
|
||||
static int clear_candidates() {
|
||||
for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) {
|
||||
if (candidates[i].combo == NULL) {
|
||||
return i;
|
||||
}
|
||||
candidates[i].combo = NULL;
|
||||
}
|
||||
return CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY;
|
||||
}
|
||||
|
||||
static int capture_pressed_key(struct position_state_changed *ev) {
|
||||
for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) {
|
||||
if (pressed_keys[i] != NULL) {
|
||||
continue;
|
||||
}
|
||||
pressed_keys[i] = ev;
|
||||
return ZMK_EV_EVENT_CAPTURED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct zmk_listener zmk_listener_combo;
|
||||
|
||||
static void release_pressed_keys() {
|
||||
// release the first key that was pressed
|
||||
if (pressed_keys[0] == NULL) {
|
||||
return;
|
||||
}
|
||||
ZMK_EVENT_RELEASE(pressed_keys[0])
|
||||
pressed_keys[0] = NULL;
|
||||
|
||||
// reprocess events (see tests/combo/fully-overlapping-combos-3 for why this is needed)
|
||||
for (int i = 1; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) {
|
||||
if (pressed_keys[i] == NULL) {
|
||||
return;
|
||||
}
|
||||
struct position_state_changed *captured_event = pressed_keys[i];
|
||||
pressed_keys[i] = NULL;
|
||||
ZMK_EVENT_RAISE(captured_event);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int press_combo_behavior(struct combo_cfg *combo, int32_t timestamp) {
|
||||
struct zmk_behavior_binding_event event = {
|
||||
.position = combo->virtual_key_position,
|
||||
.timestamp = timestamp,
|
||||
};
|
||||
|
||||
return behavior_keymap_binding_pressed(&combo->behavior, event);
|
||||
}
|
||||
|
||||
static inline int release_combo_behavior(struct combo_cfg *combo, int32_t timestamp) {
|
||||
struct zmk_behavior_binding_event event = {
|
||||
.position = combo->virtual_key_position,
|
||||
.timestamp = timestamp,
|
||||
};
|
||||
|
||||
return behavior_keymap_binding_released(&combo->behavior, event);
|
||||
}
|
||||
|
||||
static void move_pressed_keys_to_active_combo(struct active_combo *active_combo) {
|
||||
int combo_length = active_combo->combo->key_position_len;
|
||||
for (int i = 0; i < combo_length; i++) {
|
||||
active_combo->key_positions_pressed[i] = pressed_keys[i];
|
||||
pressed_keys[i] = NULL;
|
||||
}
|
||||
// move any other pressed keys up
|
||||
for (int i = 0; i + combo_length < CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO; i++) {
|
||||
if (pressed_keys[i + combo_length] == NULL) {
|
||||
return;
|
||||
}
|
||||
pressed_keys[i] = pressed_keys[i + combo_length];
|
||||
pressed_keys[i + combo_length] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static struct active_combo *store_active_combo(struct combo_cfg *combo) {
|
||||
for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_PRESSED_COMBOS; i++) {
|
||||
if (active_combos[i].combo == NULL) {
|
||||
active_combos[i].combo = combo;
|
||||
active_combo_count++;
|
||||
return &active_combos[i];
|
||||
}
|
||||
}
|
||||
LOG_ERR("Unable to store combo; already %d active. Increase "
|
||||
"CONFIG_ZMK_COMBO_MAX_PRESSED_COMBOS",
|
||||
CONFIG_ZMK_COMBO_MAX_PRESSED_COMBOS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void activate_combo(struct combo_cfg *combo) {
|
||||
struct active_combo *active_combo = store_active_combo(combo);
|
||||
if (active_combo == NULL) {
|
||||
// unable to store combo
|
||||
release_pressed_keys();
|
||||
return;
|
||||
}
|
||||
move_pressed_keys_to_active_combo(active_combo);
|
||||
press_combo_behavior(combo, active_combo->key_positions_pressed[0]->timestamp);
|
||||
}
|
||||
|
||||
static void deactivate_combo(int active_combo_index) {
|
||||
active_combo_count--;
|
||||
if (active_combo_index != active_combo_count) {
|
||||
memcpy(&active_combos[active_combo_index], &active_combos[active_combo_count],
|
||||
sizeof(struct active_combo));
|
||||
}
|
||||
active_combos[active_combo_count].combo = NULL;
|
||||
active_combos[active_combo_count] = (struct active_combo){0};
|
||||
}
|
||||
|
||||
/* returns true if a key was released. */
|
||||
static bool release_combo_key(int32_t position, int64_t timestamp) {
|
||||
for (int combo_idx = 0; combo_idx < active_combo_count; combo_idx++) {
|
||||
struct active_combo *active_combo = &active_combos[combo_idx];
|
||||
|
||||
bool key_released = false;
|
||||
bool all_keys_pressed = true;
|
||||
bool all_keys_released = true;
|
||||
for (int i = 0; i < active_combo->combo->key_position_len; i++) {
|
||||
if (active_combo->key_positions_pressed[i] == NULL) {
|
||||
all_keys_pressed = false;
|
||||
} else if (active_combo->key_positions_pressed[i]->position != position) {
|
||||
all_keys_released = false;
|
||||
} else { // not null and position matches
|
||||
k_free(active_combo->key_positions_pressed[i]);
|
||||
active_combo->key_positions_pressed[i] = NULL;
|
||||
key_released = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (key_released) {
|
||||
if ((active_combo->combo->slow_release && all_keys_released) ||
|
||||
(!active_combo->combo->slow_release && all_keys_pressed)) {
|
||||
release_combo_behavior(active_combo->combo, timestamp);
|
||||
}
|
||||
if (all_keys_released) {
|
||||
deactivate_combo(combo_idx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void cleanup() {
|
||||
k_delayed_work_cancel(&timeout_task);
|
||||
clear_candidates();
|
||||
if (fully_pressed_combo != NULL) {
|
||||
activate_combo(fully_pressed_combo);
|
||||
fully_pressed_combo = NULL;
|
||||
}
|
||||
release_pressed_keys();
|
||||
}
|
||||
|
||||
static void update_timeout_task() {
|
||||
int64_t first_timeout = first_candidate_timeout();
|
||||
if (timeout_task_timeout_at == first_timeout) {
|
||||
return;
|
||||
}
|
||||
if (first_timeout == LLONG_MAX) {
|
||||
timeout_task_timeout_at = 0;
|
||||
k_delayed_work_cancel(&timeout_task);
|
||||
return;
|
||||
}
|
||||
if (k_delayed_work_submit(&timeout_task, K_MSEC(first_timeout - k_uptime_get())) == 0) {
|
||||
timeout_task_timeout_at = first_timeout;
|
||||
}
|
||||
}
|
||||
|
||||
static int position_state_down(struct position_state_changed *ev) {
|
||||
int num_candidates;
|
||||
if (candidates[0].combo == NULL) {
|
||||
num_candidates = setup_candidates_for_first_keypress(ev->position, ev->timestamp);
|
||||
if (num_candidates == 0) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
filter_timed_out_candidates(ev->timestamp);
|
||||
num_candidates = filter_candidates(ev->position);
|
||||
}
|
||||
update_timeout_task();
|
||||
|
||||
struct combo_cfg *candidate_combo = candidates[0].combo;
|
||||
int ret = capture_pressed_key(ev);
|
||||
switch (num_candidates) {
|
||||
case 0:
|
||||
cleanup();
|
||||
return ret;
|
||||
case 1:
|
||||
if (candidate_is_completely_pressed(candidate_combo)) {
|
||||
fully_pressed_combo = candidate_combo;
|
||||
cleanup();
|
||||
}
|
||||
return ret;
|
||||
default:
|
||||
if (candidate_is_completely_pressed(candidate_combo)) {
|
||||
fully_pressed_combo = candidate_combo;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static int position_state_up(struct position_state_changed *ev) {
|
||||
cleanup();
|
||||
if (release_combo_key(ev->position, ev->timestamp)) {
|
||||
return ZMK_EV_EVENT_HANDLED;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void combo_timeout_handler(struct k_work *item) {
|
||||
if (timeout_task_timeout_at == 0 || k_uptime_get() < timeout_task_timeout_at) {
|
||||
// timer was cancelled or rescheduled.
|
||||
return;
|
||||
}
|
||||
if (filter_timed_out_candidates(timeout_task_timeout_at) < 2) {
|
||||
cleanup();
|
||||
}
|
||||
update_timeout_task();
|
||||
}
|
||||
|
||||
static int position_state_changed_listener(const struct zmk_event_header *eh) {
|
||||
if (!is_position_state_changed(eh)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct position_state_changed *ev = cast_position_state_changed(eh);
|
||||
if (ev->state) { // keydown
|
||||
return position_state_down(ev);
|
||||
} else { // keyup
|
||||
return position_state_up(ev);
|
||||
}
|
||||
}
|
||||
|
||||
ZMK_LISTENER(combo, position_state_changed_listener);
|
||||
ZMK_SUBSCRIPTION(combo, position_state_changed);
|
||||
|
||||
// todo: remove this once #506 is merged and #include <zmk/keymap.h>
|
||||
#define KEY_BINDING_TO_STRUCT(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))), \
|
||||
}
|
||||
|
||||
#define COMBO_INST(n) \
|
||||
static struct combo_cfg combo_config_##n = { \
|
||||
.timeout_ms = DT_PROP(n, timeout_ms), \
|
||||
.key_positions = DT_PROP(n, key_positions), \
|
||||
.key_position_len = DT_PROP_LEN(n, key_positions), \
|
||||
.behavior = KEY_BINDING_TO_STRUCT(0, n), \
|
||||
.virtual_key_position = ZMK_KEYMAP_LEN + __COUNTER__, \
|
||||
.slow_release = DT_PROP(n, slow_release), \
|
||||
};
|
||||
|
||||
#define INITIALIZE_COMBO(n) initialize_combo(&combo_config_##n);
|
||||
|
||||
DT_INST_FOREACH_CHILD(0, COMBO_INST)
|
||||
|
||||
static int combo_init() {
|
||||
k_delayed_work_init(&timeout_task, combo_timeout_handler);
|
||||
DT_INST_FOREACH_CHILD(0, INITIALIZE_COMBO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(combo_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
||||
|
||||
#endif
|
2
app/tests/combo/combos-and-holdtaps-0/events.patterns
Normal file
2
app/tests/combo/combos-and-holdtaps-0/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo//p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0xe0 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0xe0 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
47
app/tests/combo/combos-and-holdtaps-0/native_posix.keymap
Normal file
47
app/tests/combo/combos-and-holdtaps-0/native_posix.keymap
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
&mt {
|
||||
flavor = "hold-preferred";
|
||||
};
|
||||
|
||||
/*
|
||||
This test fails if the order of event handlers for hold-taps
|
||||
and combos is wrong. Hold-taps need to process key position events
|
||||
first so the decision to hold or tap can be made.
|
||||
*/
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
|
||||
combo_two {
|
||||
timeout-ms = <100>;
|
||||
key-positions = <1 2>;
|
||||
bindings = <&kp Y>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&mt LEFT_CONTROL A &kp B
|
||||
&kp C &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
>;
|
||||
};
|
2
app/tests/combo/combos-and-holdtaps-1/events.patterns
Normal file
2
app/tests/combo/combos-and-holdtaps-1/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo//p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
42
app/tests/combo/combos-and-holdtaps-1/native_posix.keymap
Normal file
42
app/tests/combo/combos-and-holdtaps-1/native_posix.keymap
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
&mt {
|
||||
flavor = "hold-preferred";
|
||||
};
|
||||
|
||||
/* this test checks if hold-taps can be part of a combo */
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_two {
|
||||
timeout-ms = <100>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp Y>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&mt LEFT_CONTROL A &kp B
|
||||
&kp C &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
>;
|
||||
};
|
2
app/tests/combo/combos-and-holdtaps-2/events.patterns
Normal file
2
app/tests/combo/combos-and-holdtaps-2/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo//p
|
|
@ -0,0 +1,2 @@
|
|||
pressed: usage_page 0x07 keycode 0xe0 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0xe4 mods 0x00
|
45
app/tests/combo/combos-and-holdtaps-2/native_posix.keymap
Normal file
45
app/tests/combo/combos-and-holdtaps-2/native_posix.keymap
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
&mt {
|
||||
flavor = "hold-preferred";
|
||||
};
|
||||
|
||||
/* This test verifies that hold-tap keys can observe
|
||||
* events which were released from combos.
|
||||
*/
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <100>;
|
||||
key-positions = <0 2>;
|
||||
bindings = <&kp Y>;
|
||||
};
|
||||
combo_two {
|
||||
timeout-ms = <100>;
|
||||
key-positions = <1 3>;
|
||||
bindings = <&kp Z>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&mt LEFT_CONTROL A &mt RIGHT_CONTROL B
|
||||
&none &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,0)
|
||||
ZMK_MOCK_PRESS(0,1,300)
|
||||
>;
|
||||
};
|
1
app/tests/combo/multiple-timeouts/events.patterns
Normal file
1
app/tests/combo/multiple-timeouts/events.patterns
Normal file
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x04 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x05 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x05 mods 0x00
|
40
app/tests/combo/multiple-timeouts/native_posix.keymap
Normal file
40
app/tests/combo/multiple-timeouts/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>
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp C>;
|
||||
};
|
||||
combo_two {
|
||||
timeout-ms = <120>;
|
||||
key-positions = <0 1 2>;
|
||||
bindings = <&kp C>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&none &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,100)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
>;
|
||||
};
|
2
app/tests/combo/overlapping-combos-0/events.patterns
Normal file
2
app/tests/combo/overlapping-combos-0/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo//p
|
20
app/tests/combo/overlapping-combos-0/keycode_events.snapshot
Normal file
20
app/tests/combo/overlapping-combos-0/keycode_events.snapshot
Normal file
|
@ -0,0 +1,20 @@
|
|||
pressed: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
117
app/tests/combo/overlapping-combos-0/native_posix.keymap
Normal file
117
app/tests/combo/overlapping-combos-0/native_posix.keymap
Normal file
|
@ -0,0 +1,117 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/*
|
||||
combo 0 timeout inf
|
||||
combo 01 timeout inf
|
||||
combo 0123 timeout inf
|
||||
press 012 in any combination, release any of those keys
|
||||
expected: combo 012 on key-release
|
||||
*/
|
||||
|
||||
/* 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 2>;
|
||||
bindings = <&kp X>;
|
||||
};
|
||||
|
||||
combo_two {
|
||||
timeout-ms = <TIMEOUT>;
|
||||
key-positions = <0 2>;
|
||||
bindings = <&kp Y>;
|
||||
};
|
||||
|
||||
combo_three {
|
||||
timeout-ms = <TIMEOUT>;
|
||||
key-positions = <1>;
|
||||
bindings = <&kp Z>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp C &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
&kscan {
|
||||
events = <
|
||||
/* all permutations of combo one press, combo triggered by release */
|
||||
/* while debugging these, you may want to set the release_timer to a high number */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
|
||||
/* all permutations of combo two press and release, combo triggered by release */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
>;
|
||||
};
|
2
app/tests/combo/overlapping-combos-1/events.patterns
Normal file
2
app/tests/combo/overlapping-combos-1/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo//p
|
|
@ -0,0 +1,8 @@
|
|||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
65
app/tests/combo/overlapping-combos-1/native_posix.keymap
Normal file
65
app/tests/combo/overlapping-combos-1/native_posix.keymap
Normal file
|
@ -0,0 +1,65 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/*
|
||||
combo 01 timeout 50
|
||||
combo 012 timeout 100
|
||||
AB is pressed within 50ms, C is never pressed.
|
||||
expected outcome: AB after 100ms
|
||||
*/
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_two {
|
||||
timeout-ms = <50>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp Y>;
|
||||
};
|
||||
|
||||
combo_three {
|
||||
timeout-ms = <100>;
|
||||
key-positions = <0 1 2>;
|
||||
bindings = <&kp X>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp C &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
/* if you're debugging these, remember that the timer can be triggered between
|
||||
events while stepping through code. */
|
||||
/* all permutations of combo two press and release, combo triggered by timeout */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,100)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,0,100)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,0,100)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,100)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
>;
|
||||
};
|
2
app/tests/combo/overlapping-combos-2/events.patterns
Normal file
2
app/tests/combo/overlapping-combos-2/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo//p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
52
app/tests/combo/overlapping-combos-2/native_posix.keymap
Normal file
52
app/tests/combo/overlapping-combos-2/native_posix.keymap
Normal file
|
@ -0,0 +1,52 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/*
|
||||
combo 01 timeout 100
|
||||
combo 0123 timeout 100
|
||||
press 012, wait until timeout runs out
|
||||
expected: combo 01 after 100ms, immediately followed by key 2.
|
||||
*/
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_two {
|
||||
timeout-ms = <100>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp Y>;
|
||||
};
|
||||
|
||||
combo_four {
|
||||
timeout-ms = <100>;
|
||||
key-positions = <0 1 2 3>;
|
||||
bindings = <&kp W>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp C &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
/* if you're debugging these, remember that the timer can be triggered between
|
||||
events while stepping through code. */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,2,100)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,2,100)
|
||||
>;
|
||||
};
|
2
app/tests/combo/overlapping-combos-3/events.patterns
Normal file
2
app/tests/combo/overlapping-combos-3/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo//p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x04 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
53
app/tests/combo/overlapping-combos-3/native_posix.keymap
Normal file
53
app/tests/combo/overlapping-combos-3/native_posix.keymap
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/*
|
||||
combo 12 timeout 100
|
||||
combo 0123 timeout 100
|
||||
press 012, release 2
|
||||
expected: key pos 0 followed by combo 12
|
||||
*/
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_two {
|
||||
timeout-ms = <100>;
|
||||
key-positions = <1 2>;
|
||||
bindings = <&kp Y>;
|
||||
};
|
||||
|
||||
|
||||
combo_four {
|
||||
timeout-ms = <100>;
|
||||
key-positions = <0 1 2 3>;
|
||||
bindings = <&kp W>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp C &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
/* if you're debugging these, remember that the timer can be triggered between
|
||||
events while stepping through code. */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,2,100)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,2,100)
|
||||
>;
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
|
@ -0,0 +1,16 @@
|
|||
pressed: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1b mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1c mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1c mods 0x00
|
|
@ -0,0 +1,84 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp X>;
|
||||
};
|
||||
|
||||
combo_two {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <0 2>;
|
||||
bindings = <&kp Y>;
|
||||
};
|
||||
|
||||
combo_three {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <3>;
|
||||
bindings = <&kp Z>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp C &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
/* all permutations of combo one press and release */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
|
||||
/* all permutations of combo two press and release */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,2,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
>;
|
||||
};
|
1
app/tests/combo/press-release/events.patterns
Normal file
1
app/tests/combo/press-release/events.patterns
Normal file
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
8
app/tests/combo/press-release/keycode_events.snapshot
Normal file
8
app/tests/combo/press-release/keycode_events.snapshot
Normal file
|
@ -0,0 +1,8 @@
|
|||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
51
app/tests/combo/press-release/native_posix.keymap
Normal file
51
app/tests/combo/press-release/native_posix.keymap
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp C>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&none &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
/* all different combinations of press and release order */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
>;
|
||||
};
|
1
app/tests/combo/press-timeout/events.patterns
Normal file
1
app/tests/combo/press-timeout/events.patterns
Normal file
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
4
app/tests/combo/press-timeout/keycode_events.snapshot
Normal file
4
app/tests/combo/press-timeout/keycode_events.snapshot
Normal file
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x04 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x05 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x05 mods 0x00
|
35
app/tests/combo/press-timeout/native_posix.keymap
Normal file
35
app/tests/combo/press-timeout/native_posix.keymap
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp C>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&none &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,100)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
>;
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo/combo/p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x07 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x07 mods 0x00
|
|
@ -0,0 +1,45 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp C>;
|
||||
};
|
||||
|
||||
combo_two {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <2 3>;
|
||||
bindings = <&kp D>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp Z &kp Y
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_PRESS(1,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,1,10)
|
||||
>;
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo/combo/p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x07 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x07 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
|
@ -0,0 +1,46 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp C>;
|
||||
};
|
||||
|
||||
combo_two {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <2 3>;
|
||||
bindings = <&kp D>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp Z &kp Y
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_PRESS(1,1,10)
|
||||
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
>;
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*combo/combo/p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x07 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x07 mods 0x00
|
|
@ -0,0 +1,46 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp C>;
|
||||
};
|
||||
|
||||
combo_two {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <2 3>;
|
||||
bindings = <&kp D>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp Z &kp Y
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_PRESS(1,1,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,1,10)
|
||||
>;
|
||||
};
|
1
app/tests/combo/slowrelease-disabled/events.patterns
Normal file
1
app/tests/combo/slowrelease-disabled/events.patterns
Normal file
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x07 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x07 mods 0x00
|
38
app/tests/combo/slowrelease-disabled/native_posix.keymap
Normal file
38
app/tests/combo/slowrelease-disabled/native_posix.keymap
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp C>;
|
||||
/* no slow-release! */
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label = "Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp D &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10) /* this should release the combo */
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
>;
|
||||
};
|
1
app/tests/combo/slowrelease-enabled/events.patterns
Normal file
1
app/tests/combo/slowrelease-enabled/events.patterns
Normal file
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x07 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x06 mods 0x00
|
||||
released: usage_page 0x07 keycode 0x07 mods 0x00
|
38
app/tests/combo/slowrelease-enabled/native_posix.keymap
Normal file
38
app/tests/combo/slowrelease-enabled/native_posix.keymap
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan-mock.h>
|
||||
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_one {
|
||||
timeout-ms = <30>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp C>;
|
||||
slow-release;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&kp D &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10) /* this should not release the combo yet */
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
>;
|
||||
};
|
52
docs/docs/behaviors/combos.md
Normal file
52
docs/docs/behaviors/combos.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: Combo Behavior
|
||||
sidebar_label: Combos
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Combo keys are a way to combine multiple keypresses to output a different key. For example, you can hit the Q and W keys on your keyboard to output escape.
|
||||
|
||||
### Configuration
|
||||
|
||||
Combos are specified like this:
|
||||
|
||||
```
|
||||
/ {
|
||||
combos {
|
||||
compatible = "zmk,combos";
|
||||
combo_esc {
|
||||
timeout-ms = <50>;
|
||||
key-positions = <0 1>;
|
||||
bindings = <&kp ESC>;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
- The name of the combo doesn't really matter, but convention is to start the node name with `combo_`.
|
||||
- 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.
|
||||
- `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.
|
||||
|
||||
:::info
|
||||
|
||||
Key positions are numbered like the keys in your keymap, starting at 0. So, if the first key in your keymap is `Q`, this key is in position `0`. The next key (possibly `W`) will have position 1, etcetera.
|
||||
|
||||
:::
|
||||
|
||||
### Advanced usage
|
||||
|
||||
- Partially overlapping combos like `0 1` and `0 2` are supported.
|
||||
- Fully overlapping combos like `0 1` and `0 1 2` are supported.
|
||||
- You are not limited to `&kp` bindings. You can use all ZMK behaviors there, like `&mo`, `&bt`, `&mt`, `<` etc.
|
||||
|
||||
### Advanced configuration
|
||||
|
||||
There are three global combo parameters which are set through KConfig. You can set them in the `<boardname>.conf` file in the same directory as your keymap file.
|
||||
|
||||
- `CONFIG_ZMK_COMBO_MAX_PRESSED_COMBOS` is the number of combos that can be active at the same time. Default 4.
|
||||
- `CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY` is the maximum number of combos that can be active on a key position. Defaults to 5. (So you can have 5 separate combos that use position `3` for example)
|
||||
- `CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO` is the maximum number of keys that need to be pressed to activate a combo. Default 4. If you want a combo that triggers when pressing 5 keys, you'd set this to 5 for example.
|
|
@ -26,7 +26,11 @@ ZMK is currently missing some features found in other popular firmware. This tab
|
|||
| [Display Support](features/displays)[^2] | 🚧 | 🚧 | ✅ |
|
||||
| [RGB Underglow](features/underglow) | ✅ | ✅ | ✅ |
|
||||
| One Shot Keys | ✅ | ✅ | ✅ |
|
||||
<<<<<<< HEAD
|
||||
| [Combo Keys](https://github.com/zmkfirmware/zmk/pull/504) | 🚧 | | ✅ |
|
||||
=======
|
||||
| [Combo Keys](behaviors/combos) | ✅ | | ✅ |
|
||||
>>>>>>> feb0d5b90cbbb1a1026bf356afd788c860824ccf
|
||||
| Macros | 🚧 | ✅ | ✅ |
|
||||
| Mouse Keys | 💡 | ✅ | ✅ |
|
||||
| Low Active Power Usage | ✅ | | |
|
||||
|
|
52
docs/docs/intro.md.orig
Normal file
52
docs/docs/intro.md.orig
Normal file
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: Introduction to ZMK
|
||||
sidebar_label: Introduction
|
||||
slug: /
|
||||
---
|
||||
|
||||
ZMK Firmware is an open source (MIT) keyboard
|
||||
firmware built on the [Zephyr™ Project](https://zephyrproject.org/) Real Time Operating System (RTOS). ZMK's goal is to provide a modern, wireless, and powerful firmware free of licensing issues.
|
||||
|
||||
## Features
|
||||
|
||||
ZMK is currently missing some features found in other popular firmware. This table compares the features supported by ZMK, BlueMicro and QMK:
|
||||
|
||||
| **Feature** | ZMK | BlueMicro | QMK |
|
||||
| ------------------------------------------------------------------------------------------------------------------------- | :-: | :-------: | :-: |
|
||||
| Low Latency BLE Support | ✅ | ✅ | |
|
||||
| Multi-Device BLE Support | ✅ | | |
|
||||
| [USB Connectivity](behaviors/outputs) | ✅ | | ✅ |
|
||||
| User Configuration Repositories | ✅ | | |
|
||||
| Split Keyboard Support | ✅ | ✅ | ✅ |
|
||||
| [Keymaps and Layers](behaviors/layers) | ✅ | ✅ | ✅ |
|
||||
| [Hold-Tap](behaviors/hold-tap) (which includes [Mod-Tap](behaviors/mod-tap) and [Layer-Tap](behaviors/layers/#layer-tap)) | ✅ | ✅ | ✅ |
|
||||
| [Keyboard Codes](codes/#keyboard) | ✅ | ✅ | ✅ |
|
||||
| [Media](codes/#media-controls) & [Consumer](codes/#consumer-controls) Codes | ✅ | ✅ | ✅ |
|
||||
| [Encoders](features/encoders)[^1] | ✅ | | ✅ |
|
||||
| [Display Support](features/displays)[^2] | 🚧 | 🚧 | ✅ |
|
||||
| [RGB Underglow](features/underglow) | ✅ | ✅ | ✅ |
|
||||
| One Shot Keys | ✅ | ✅ | ✅ |
|
||||
<<<<<<< HEAD
|
||||
| [Combo Keys](https://github.com/zmkfirmware/zmk/pull/504) | 🚧 | | ✅ |
|
||||
=======
|
||||
| [Combo Keys](behaviors/combos) | ✅ | | ✅ |
|
||||
>>>>>>> feb0d5b90cbbb1a1026bf356afd788c860824ccf
|
||||
| Macros | 🚧 | ✅ | ✅ |
|
||||
| Mouse Keys | 💡 | ✅ | ✅ |
|
||||
| Low Active Power Usage | ✅ | | |
|
||||
| Low Power Sleep States | ✅ | ✅ | |
|
||||
| [Low Power Mode (VCC Shutoff)](behaviors/power) | ✅ | | |
|
||||
| Battery Reporting | ✅ | ✅ | |
|
||||
| Shell over BLE | 💡 | | |
|
||||
| Realtime Keymap Updating | 💡 | | ✅ |
|
||||
| AVR/8 Bit | | | ✅ |
|
||||
| [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/latest/boards/index.html) | ✅ | | |
|
||||
|
||||
[^2]: Encoders are not currently supported on peripheral side splits.
|
||||
[^1]: OLEDs are currently proof of concept in ZMK.
|
||||
|
||||
## Code Of Conduct
|
||||
|
||||
Please note that this project is released with a
|
||||
[Contributor Code of Conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/).
|
||||
By participating in this project you agree to abide by its terms.
|
|
@ -20,6 +20,7 @@ module.exports = {
|
|||
"behaviors/misc",
|
||||
"behaviors/hold-tap",
|
||||
"behaviors/mod-tap",
|
||||
"behaviors/combos",
|
||||
"behaviors/reset",
|
||||
"behaviors/bluetooth",
|
||||
"behaviors/outputs",
|
||||
|
|
2
docs/static/setup.sh
vendored
2
docs/static/setup.sh
vendored
|
@ -91,7 +91,7 @@ echo ""
|
|||
echo "Keyboard Shield Selection:"
|
||||
|
||||
prompt="Pick an keyboard:"
|
||||
options=("Kyria" "Lily58" "Corne" "Splitreus62" "Sofle" "Iris" "Reviung41" "RoMac" "RoMac+" "makerdiary M60" "Microdox" "TG4X" "QAZ" "NIBBLE" "Jorne" "Jian" "CRBN" "Tidbit" "Eek!" "BF0-9000" "Helix")
|
||||
options=("Kyria" "Lily58" "Corne" "Splitreus62" "Sofle" "Iris" "Reviung41" "RoMac" "RoMac+" "makerdiary M60" "Microdox" "TG4X" "QAZ" "NIBBLE" "Jorne" "Jian" "CRBN" "Tidbit" "Eek!" "BFO-9000" "Helix")
|
||||
|
||||
PS3="$prompt "
|
||||
# TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos.
|
||||
|
|
228
docs/static/setup_BACKUP_1333.sh
vendored
Normal file
228
docs/static/setup_BACKUP_1333.sh
vendored
Normal file
|
@ -0,0 +1,228 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
set -e
|
||||
|
||||
check_exists() {
|
||||
command_to_run=$1
|
||||
error_message=$2
|
||||
local __resultvar=$3
|
||||
|
||||
if ! eval "$command_to_run" &> /dev/null; then
|
||||
if [[ "$__resultvar" != "" ]]; then
|
||||
eval $__resultvar="'false'"
|
||||
else
|
||||
printf "%s\n" "$error_message"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [[ "$__resultvar" != "" ]]; then
|
||||
eval $__resultvar="'true'"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_exists "command -v git" "git is not installed, and is required for this script!"
|
||||
check_exists "command -v curl" "curl is not installed, and is required for this script!" curl_exists
|
||||
check_exists "command -v wget" "wget is not installed, and is required for this script!" wget_exists
|
||||
|
||||
check_exists "git config user.name" "Git username not set!\nRun: git config --global user.name 'My Name'"
|
||||
check_exists "git config user.email" "Git email not set!\nRun: git config --global user.email 'example@myemail.com'"
|
||||
|
||||
# Check to see if the user has write permissions in this directory to prevent a cryptic error later on
|
||||
if [ ! -w `pwd` ]; then
|
||||
echo 'Sorry, you do not have write permissions in this directory.';
|
||||
echo 'Please try running this script again from a directory that you do have write permissions for.';
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse all commandline options
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
-w|--wget) force_wget="true"; break;;
|
||||
*) echo "Unknown parameter: $1"; exit 1;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ $curl_exists == "true" && $wget_exists == "true" ]]; then
|
||||
if [[ $force_wget == "true" ]]; then
|
||||
download_command="wget "
|
||||
else
|
||||
download_command="curl -O "
|
||||
fi
|
||||
elif [[ $curl_exists == "true" ]]; then
|
||||
download_command="curl -O "
|
||||
elif [[ $wget_exists == "true" ]]; then
|
||||
download_command="wget "
|
||||
else
|
||||
echo 'Neither curl nor wget are installed. One of the two is required for this script!'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
repo_path="https://github.com/zmkfirmware/zmk-config-split-template.git"
|
||||
title="ZMK Config Setup:"
|
||||
|
||||
prompt="Pick an MCU board:"
|
||||
options=("nice!nano" "QMK Proton-C" "BlueMicro840 (v1)" "makerdiary nRF52840 M.2")
|
||||
|
||||
echo "$title"
|
||||
echo ""
|
||||
echo "MCU Board Selection:"
|
||||
PS3="$prompt "
|
||||
select opt in "${options[@]}" "Quit"; do
|
||||
|
||||
case "$REPLY" in
|
||||
|
||||
1 ) board="nice_nano"; break;;
|
||||
2 ) board="proton_c"; break;;
|
||||
3 ) board="bluemicro840_v1"; break;;
|
||||
3 ) board="nrf52840_m2"; break;;
|
||||
|
||||
$(( ${#options[@]}+1 )) ) echo "Goodbye!"; exit 1;;
|
||||
*) echo "Invalid option. Try another one."; continue;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Keyboard Shield Selection:"
|
||||
|
||||
prompt="Pick an keyboard:"
|
||||
options=("Kyria" "Lily58" "Corne" "Splitreus62" "Sofle" "Iris" "Reviung41" "RoMac" "RoMac+" "makerdiary M60" "Microdox" "TG4X" "QAZ" "NIBBLE" "Jorne" "Jian" "CRBN" "Tidbit" "Eek!" "BFO-9000" "Helix")
|
||||
|
||||
PS3="$prompt "
|
||||
# TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos.
|
||||
# select opt in "${options[@]}" "Other" "Quit"; do
|
||||
select opt in "${options[@]}" "Quit"; do
|
||||
|
||||
case "$REPLY" in
|
||||
|
||||
1 ) shield_title="Kyria" shield="kyria"; split="y"; break;;
|
||||
2 ) shield_title="Lily58" shield="lily58"; split="y"; break;;
|
||||
3 ) shield_title="Corne" shield="corne"; split="y"; break;;
|
||||
4 ) shield_title="Splitreus62" shield="splitreus62"; split="y"; break;;
|
||||
5 ) shield_title="Sofle" shield="sofle"; split="y"; break;;
|
||||
6 ) shield_title="Iris" shield="iris"; split="y"; break;;
|
||||
7 ) shield_title="Reviung41" shield="reviung41"; split="n"; break;;
|
||||
8 ) shield_title="RoMac" shield="romac"; split="n"; break;;
|
||||
9 ) shield_title="RoMac+" shield="romac_plus"; split="n"; break;;
|
||||
10 ) shield_title="M60" shield="m60"; split="n"; break;;
|
||||
11 ) shield_title="Microdox" shield="microdox"; split="y"; break;;
|
||||
12 ) shield_title="TG4X" shield="tg4x"; split="n"; break;;
|
||||
13 ) shield_title="QAZ" shield="qaz"; split="n"; break;;
|
||||
14 ) shield_title="NIBBLE" shield="nibble"; split="n"; break;;
|
||||
15 ) shield_title="Jorne" shield="jorne"; split="y"; break;;
|
||||
16 ) shield_title="Jian" shield="jian"; split="y"; break;;
|
||||
17 ) shield_title="CRBN" shield="crbn"; split="n"; break;;
|
||||
18 ) shield_title="Tidbit" shield="tidbit"; split="n" break;;
|
||||
19 ) shield_title="Eek!" shield="eek"; split="n" break;;
|
||||
20 ) shield_title="BFO-9000" shield="bfo9000"; split="y"; break;;
|
||||
21 ) shield_title="Helix" shield="helix"; split="y"; break;;
|
||||
|
||||
# Add link to docs on adding your own custom shield in your ZMK config!
|
||||
# $(( ${#options[@]}+1 )) ) echo "Other!"; break;;
|
||||
$(( ${#options[@]}+1 )) ) echo "Goodbye!"; exit 1;;
|
||||
*) echo "Invalid option. Try another one.";continue;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$split" == "n" ]; then
|
||||
repo_path="https://github.com/zmkfirmware/zmk-config-template.git"
|
||||
fi
|
||||
|
||||
read -r -e -p "Copy in the stock keymap for customization? [Yn]: " copy_keymap
|
||||
|
||||
if [ -z "$copy_keymap" ] || [ "$copy_keymap" == "Y" ] || [ "$copy_keymap" == "y" ]; then copy_keymap="yes"; fi
|
||||
|
||||
read -r -e -p "GitHub Username (leave empty to skip GitHub repo creation): " github_user
|
||||
if [ -n "$github_user" ]; then
|
||||
read -r -p "GitHub Repo Name [zmk-config]: " repo_name
|
||||
if [ -z "$repo_name" ]; then repo_name="zmk-config"; fi
|
||||
|
||||
read -r -p "GitHub Repo [https://github.com/${github_user}/${repo_name}.git]: " github_repo
|
||||
|
||||
if [ -z "$github_repo" ]; then github_repo="https://github.com/${github_user}/${repo_name}.git"; fi
|
||||
else
|
||||
repo_name="zmk-config"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Preparing a user config for:"
|
||||
echo "* MCU Board: ${board}"
|
||||
echo "* Shield: ${shield}"
|
||||
|
||||
if [ "$copy_keymap" == "yes" ]; then
|
||||
echo "* Copy Keymap?: ✓"
|
||||
else
|
||||
echo "* Copy Keymap?: ❌"
|
||||
fi
|
||||
|
||||
if [ -n "$github_repo" ]; then
|
||||
echo "* GitHub Repo To Push (please create this in GH first!): ${github_repo}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
read -r -p "Continue? [Yn]: " do_it
|
||||
|
||||
if [ -n "$do_it" ] && [ "$do_it" != "y" ] && [ "$do_it" != "Y" ]; then
|
||||
echo "Aborting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git clone --single-branch $repo_path ${repo_name}
|
||||
cd ${repo_name}
|
||||
|
||||
pushd config
|
||||
|
||||
$download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.conf"
|
||||
|
||||
if [ "$copy_keymap" == "yes" ]; then
|
||||
$download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.keymap"
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
sed -i'.orig' \
|
||||
-e "s/BOARD_NAME/$board/" \
|
||||
-e "s/SHIELD_NAME/$shield/" \
|
||||
-e "s/KEYBOARD_TITLE/$shield_title/" \
|
||||
.github/workflows/build.yml
|
||||
|
||||
if [ "$board" == "proton_c" ]; then
|
||||
# Proton-C board still fa
|
||||
sed -i'.orig' -e "s/uf2/hex/g" .github/workflows/build.yml
|
||||
fi
|
||||
|
||||
rm .github/workflows/*.yml.orig
|
||||
|
||||
rm -rf .git
|
||||
git init .
|
||||
git add .
|
||||
git commit -m "Initial User Config."
|
||||
|
||||
if [ -n "$github_repo" ]; then
|
||||
git remote add origin "$github_repo"
|
||||
git push --set-upstream origin "$(git symbolic-ref --short HEAD)"
|
||||
push_return_code=$?
|
||||
|
||||
# If push failed, assume that the origin was incorrect and give instructions on fixing.
|
||||
if [ ${push_return_code} -ne 0 ]; then
|
||||
echo "Remote repository $github_repo not found..."
|
||||
echo "Check GitHub URL, and try adding again."
|
||||
echo "Run the following: "
|
||||
echo " git remote rm origin"
|
||||
echo " git remote add origin FIXED_URL"
|
||||
echo " git push --set-upstream origin $(git symbolic-ref --short HEAD)"
|
||||
echo "Once pushed, your firmware should be availalbe from GitHub Actions at: ${github_repo%.git}/actions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# TODO: Support determing the actions URL when non-https:// repo URL is used.
|
||||
if [ "${github_repo}" != "${github_repo#https://}" ]; then
|
||||
echo "Your firmware should be available from GitHub Actions shortly: ${github_repo%.git}/actions"
|
||||
fi
|
||||
fi
|
226
docs/static/setup_BASE_1333.sh
vendored
Normal file
226
docs/static/setup_BASE_1333.sh
vendored
Normal file
|
@ -0,0 +1,226 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2020 The ZMK Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
set -e
|
||||
|
||||
check_exists() {
|
||||
command_to_run=$1
|
||||
error_message=$2
|
||||
local __resultvar=$3
|
||||
|
||||
if ! eval "$command_to_run" &> /dev/null; then
|
||||
if [[ "$__resultvar" != "" ]]; then
|
||||
eval $__resultvar="'false'"
|
||||
else
|
||||
printf "%s\n" "$error_message"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [[ "$__resultvar" != "" ]]; then
|
||||
eval $__resultvar="'true'"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_exists "command -v git" "git is not installed, and is required for this script!"
|
||||
check_exists "command -v curl" "curl is not installed, and is required for this script!" curl_exists
|
||||
check_exists "command -v wget" "wget is not installed, and is required for this script!" wget_exists
|
||||
|
||||
check_exists "git config user.name" "Git username not set!\nRun: git config --global user.name 'My Name'"
|
||||
check_exists "git config user.email" "Git email not set!\nRun: git config --global user.email 'example@myemail.com'"
|
||||
|
||||
# Check to see if the user has write permissions in this directory to prevent a cryptic error later on
|
||||
if [ ! -w `pwd` ]; then
|
||||
echo 'Sorry, you do not have write permissions in this directory.';
|
||||
echo 'Please try running this script again from a directory that you do have write permissions for.';
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse all commandline options
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
-w|--wget) force_wget="true"; break;;
|
||||
*) echo "Unknown parameter: $1"; exit 1;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ $curl_exists == "true" && $wget_exists == "true" ]]; then
|
||||
if [[ $force_wget == "true" ]]; then
|
||||
download_command="wget "
|
||||
else
|
||||
download_command="curl -O "
|
||||
fi
|
||||
elif [[ $curl_exists == "true" ]]; then
|
||||
download_command="curl -O "
|
||||
elif [[ $wget_exists == "true" ]]; then
|
||||
download_command="wget "
|
||||
else
|
||||
echo 'Neither curl nor wget are installed. One of the two is required for this script!'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
repo_path="https://github.com/zmkfirmware/zmk-config-split-template.git"
|
||||
title="ZMK Config Setup:"
|
||||
|
||||
prompt="Pick an MCU board:"
|
||||
options=("nice!nano" "QMK Proton-C" "BlueMicro840 (v1)" "makerdiary nRF52840 M.2")
|
||||
|
||||
echo "$title"
|
||||
echo ""
|
||||
echo "MCU Board Selection:"
|
||||
PS3="$prompt "
|
||||
select opt in "${options[@]}" "Quit"; do
|
||||
|
||||
case "$REPLY" in
|
||||
|
||||
1 ) board="nice_nano"; break;;
|
||||
2 ) board="proton_c"; break;;
|
||||
3 ) board="bluemicro840_v1"; break;;
|
||||
3 ) board="nrf52840_m2"; break;;
|
||||
|
||||
$(( ${#options[@]}+1 )) ) echo "Goodbye!"; exit 1;;
|
||||
*) echo "Invalid option. Try another one."; continue;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Keyboard Shield Selection:"
|
||||
|
||||
prompt="Pick an keyboard:"
|
||||
options=("Kyria" "Lily58" "Corne" "Splitreus62" "Sofle" "Iris" "Reviung41" "RoMac" "RoMac+" "makerdiary M60" "Microdox" "TG4X" "QAZ" "Jorne" "Jian" "CRBN" "Tidbit")
|
||||
|
||||
PS3="$prompt "
|
||||
# TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos.
|
||||
# select opt in "${options[@]}" "Other" "Quit"; do
|
||||
select opt in "${options[@]}" "Quit"; do
|
||||
|
||||
case "$REPLY" in
|
||||
|
||||
1 ) shield_title="Kyria" shield="kyria"; split="y"; break;;
|
||||
2 ) shield_title="Lily58" shield="lily58"; split="y"; break;;
|
||||
3 ) shield_title="Corne" shield="corne"; split="y"; break;;
|
||||
4 ) shield_title="Splitreus62" shield="splitreus62"; split="y"; break;;
|
||||
5 ) shield_title="Sofle" shield="sofle"; split="y"; break;;
|
||||
6 ) shield_title="Iris" shield="iris"; split="y"; break;;
|
||||
7 ) shield_title="Reviung41" shield="reviung41"; split="n"; break;;
|
||||
8 ) shield_title="RoMac" shield="romac"; split="n"; break;;
|
||||
9 ) shield_title="RoMac+" shield="romac_plus"; split="n"; break;;
|
||||
10 ) shield_title="M60" shield="m60"; split="n"; break;;
|
||||
11 ) shield_title="Microdox" shield="microdox"; split="y"; break;;
|
||||
12 ) shield_title="TG4X" shield="tg4x"; split="n"; break;;
|
||||
13 ) shield_title="QAZ" shield="qaz"; split="n"; break;;
|
||||
14 ) shield_title="NIBBLE" shield="nibble"; split="n"; break;;
|
||||
15 ) shield_title="Jorne" shield="jorne"; split="y"; break;;
|
||||
16 ) shield_title="Jian" shield="jian"; split="y"; break;;
|
||||
17 ) shield_title="CRBN" shield="crbn"; split="n"; break;;
|
||||
18 ) shield_title="Tidbit" shield="tidbit"; split="n" break;;
|
||||
|
||||
# Add link to docs on adding your own custom shield in your ZMK config!
|
||||
# $(( ${#options[@]}+1 )) ) echo "Other!"; break;;
|
||||
$(( ${#options[@]}+1 )) ) echo "Goodbye!"; exit 1;;
|
||||
*) echo "Invalid option. Try another one.";continue;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$split" == "n" ]; then
|
||||
repo_path="https://github.com/zmkfirmware/zmk-config-template.git"
|
||||
fi
|
||||
|
||||
read -r -e -p "Copy in the stock keymap for customization? [Yn]: " copy_keymap
|
||||
|
||||
if [ -z "$copy_keymap" ] || [ "$copy_keymap" == "Y" ] || [ "$copy_keymap" == "y" ]; then copy_keymap="yes"; fi
|
||||
|
||||
read -r -e -p "GitHub Username (leave empty to skip GitHub repo creation): " github_user
|
||||
if [ -n "$github_user" ]; then
|
||||
read -r -p "GitHub Repo Name [zmk-config]: " repo_name
|
||||
if [ -z "$repo_name" ]; then repo_name="zmk-config"; fi
|
||||
|
||||
read -r -p "GitHub Repo [https://github.com/${github_user}/${repo_name}.git]: " github_repo
|
||||
|
||||
if [ -z "$github_repo" ]; then github_repo="https://github.com/${github_user}/${repo_name}.git"; fi
|
||||
else
|
||||
repo_name="zmk-config"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Preparing a user config for:"
|
||||
echo "* MCU Board: ${board}"
|
||||
echo "* Shield: ${shield}"
|
||||
|
||||
if [ "$copy_keymap" == "yes" ]; then
|
||||
echo "* Copy Keymap?: ✓"
|
||||
else
|
||||
echo "* Copy Keymap?: ❌"
|
||||
fi
|
||||
|
||||
if [ -n "$github_repo" ]; then
|
||||
echo "* GitHub Repo To Push (please create this in GH first!): ${github_repo}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
read -r -p "Continue? [Yn]: " do_it
|
||||
|
||||
if [ -n "$do_it" ] && [ "$do_it" != "y" ] && [ "$do_it" != "Y" ]; then
|
||||
echo "Aborting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git clone --single-branch $repo_path ${repo_name}
|
||||
cd ${repo_name}
|
||||
|
||||
pushd config
|
||||
|
||||
$download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.conf"
|
||||
|
||||
if [ "$copy_keymap" == "yes" ]; then
|
||||
$download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.keymap"
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
sed -i'.orig' \
|
||||
-e "s/BOARD_NAME/$board/" \
|
||||
-e "s/SHIELD_NAME/$shield/" \
|
||||
-e "s/KEYBOARD_TITLE/$shield_title/" \
|
||||
.github/workflows/build.yml
|
||||
|
||||
if [ "$board" == "proton_c" ]; then
|
||||
# Proton-C board still fa
|
||||
sed -i'.orig' -e "s/uf2/hex/g" .github/workflows/build.yml
|
||||
fi
|
||||
|
||||
rm .github/workflows/*.yml.orig
|
||||
|
||||
rm -rf .git
|
||||
git init .
|
||||
git add .
|
||||
git commit -m "Initial User Config."
|
||||
|
||||
if [ -n "$github_repo" ]; then
|
||||
git remote add origin "$github_repo"
|
||||
git push --set-upstream origin "$(git symbolic-ref --short HEAD)"
|
||||
push_return_code=$?
|
||||
|
||||
# If push failed, assume that the origin was incorrect and give instructions on fixing.
|
||||
if [ ${push_return_code} -ne 0 ]; then
|
||||
echo "Remote repository $github_repo not found..."
|
||||
echo "Check GitHub URL, and try adding again."
|
||||
echo "Run the following: "
|
||||
echo " git remote rm origin"
|
||||
echo " git remote add origin FIXED_URL"
|
||||
echo " git push --set-upstream origin $(git symbolic-ref --short HEAD)"
|
||||
echo "Once pushed, your firmware should be availalbe from GitHub Actions at: ${github_repo%.git}/actions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# TODO: Support determing the actions URL when non-https:// repo URL is used.
|
||||
if [ "${github_repo}" != "${github_repo#https://}" ]; then
|
||||
echo "Your firmware should be available from GitHub Actions shortly: ${github_repo%.git}/actions"
|
||||
fi
|
||||
fi
|
228
docs/static/setup_LOCAL_1333.sh
vendored
Normal file
228
docs/static/setup_LOCAL_1333.sh
vendored
Normal file
|
@ -0,0 +1,228 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
set -e
|
||||
|
||||
check_exists() {
|
||||
command_to_run=$1
|
||||
error_message=$2
|
||||
local __resultvar=$3
|
||||
|
||||
if ! eval "$command_to_run" &> /dev/null; then
|
||||
if [[ "$__resultvar" != "" ]]; then
|
||||
eval $__resultvar="'false'"
|
||||
else
|
||||
printf "%s\n" "$error_message"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [[ "$__resultvar" != "" ]]; then
|
||||
eval $__resultvar="'true'"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_exists "command -v git" "git is not installed, and is required for this script!"
|
||||
check_exists "command -v curl" "curl is not installed, and is required for this script!" curl_exists
|
||||
check_exists "command -v wget" "wget is not installed, and is required for this script!" wget_exists
|
||||
|
||||
check_exists "git config user.name" "Git username not set!\nRun: git config --global user.name 'My Name'"
|
||||
check_exists "git config user.email" "Git email not set!\nRun: git config --global user.email 'example@myemail.com'"
|
||||
|
||||
# Check to see if the user has write permissions in this directory to prevent a cryptic error later on
|
||||
if [ ! -w `pwd` ]; then
|
||||
echo 'Sorry, you do not have write permissions in this directory.';
|
||||
echo 'Please try running this script again from a directory that you do have write permissions for.';
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse all commandline options
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
-w|--wget) force_wget="true"; break;;
|
||||
*) echo "Unknown parameter: $1"; exit 1;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ $curl_exists == "true" && $wget_exists == "true" ]]; then
|
||||
if [[ $force_wget == "true" ]]; then
|
||||
download_command="wget "
|
||||
else
|
||||
download_command="curl -O "
|
||||
fi
|
||||
elif [[ $curl_exists == "true" ]]; then
|
||||
download_command="curl -O "
|
||||
elif [[ $wget_exists == "true" ]]; then
|
||||
download_command="wget "
|
||||
else
|
||||
echo 'Neither curl nor wget are installed. One of the two is required for this script!'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
repo_path="https://github.com/zmkfirmware/zmk-config-split-template.git"
|
||||
title="ZMK Config Setup:"
|
||||
|
||||
prompt="Pick an MCU board:"
|
||||
options=("nice!nano" "QMK Proton-C" "BlueMicro840 (v1)" "makerdiary nRF52840 M.2")
|
||||
|
||||
echo "$title"
|
||||
echo ""
|
||||
echo "MCU Board Selection:"
|
||||
PS3="$prompt "
|
||||
select opt in "${options[@]}" "Quit"; do
|
||||
|
||||
case "$REPLY" in
|
||||
|
||||
1 ) board="nice_nano"; break;;
|
||||
2 ) board="proton_c"; break;;
|
||||
3 ) board="bluemicro840_v1"; break;;
|
||||
3 ) board="nrf52840_m2"; break;;
|
||||
|
||||
$(( ${#options[@]}+1 )) ) echo "Goodbye!"; exit 1;;
|
||||
*) echo "Invalid option. Try another one."; continue;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Keyboard Shield Selection:"
|
||||
|
||||
prompt="Pick an keyboard:"
|
||||
options=("Kyria" "Lily58" "Corne" "Splitreus62" "Sofle" "Iris" "Reviung41" "RoMac" "RoMac+" "makerdiary M60" "Microdox" "TG4X" "QAZ" "NIBBLE" "Jorne" "Jian" "CRBN" "Tidbit" "Eek!" "BF0-9000" "Helix")
|
||||
|
||||
PS3="$prompt "
|
||||
# TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos.
|
||||
# select opt in "${options[@]}" "Other" "Quit"; do
|
||||
select opt in "${options[@]}" "Quit"; do
|
||||
|
||||
case "$REPLY" in
|
||||
|
||||
1 ) shield_title="Kyria" shield="kyria"; split="y"; break;;
|
||||
2 ) shield_title="Lily58" shield="lily58"; split="y"; break;;
|
||||
3 ) shield_title="Corne" shield="corne"; split="y"; break;;
|
||||
4 ) shield_title="Splitreus62" shield="splitreus62"; split="y"; break;;
|
||||
5 ) shield_title="Sofle" shield="sofle"; split="y"; break;;
|
||||
6 ) shield_title="Iris" shield="iris"; split="y"; break;;
|
||||
7 ) shield_title="Reviung41" shield="reviung41"; split="n"; break;;
|
||||
8 ) shield_title="RoMac" shield="romac"; split="n"; break;;
|
||||
9 ) shield_title="RoMac+" shield="romac_plus"; split="n"; break;;
|
||||
10 ) shield_title="M60" shield="m60"; split="n"; break;;
|
||||
11 ) shield_title="Microdox" shield="microdox"; split="y"; break;;
|
||||
12 ) shield_title="TG4X" shield="tg4x"; split="n"; break;;
|
||||
13 ) shield_title="QAZ" shield="qaz"; split="n"; break;;
|
||||
14 ) shield_title="NIBBLE" shield="nibble"; split="n"; break;;
|
||||
15 ) shield_title="Jorne" shield="jorne"; split="y"; break;;
|
||||
16 ) shield_title="Jian" shield="jian"; split="y"; break;;
|
||||
17 ) shield_title="CRBN" shield="crbn"; split="n"; break;;
|
||||
18 ) shield_title="Tidbit" shield="tidbit"; split="n" break;;
|
||||
19 ) shield_title="Eek!" shield="eek"; split="n" break;;
|
||||
20 ) shield_title="BFO-9000" shield="bfo9000"; split="y"; break;;
|
||||
21 ) shield_title="Helix" shield="helix"; split="y"; break;;
|
||||
|
||||
# Add link to docs on adding your own custom shield in your ZMK config!
|
||||
# $(( ${#options[@]}+1 )) ) echo "Other!"; break;;
|
||||
$(( ${#options[@]}+1 )) ) echo "Goodbye!"; exit 1;;
|
||||
*) echo "Invalid option. Try another one.";continue;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$split" == "n" ]; then
|
||||
repo_path="https://github.com/zmkfirmware/zmk-config-template.git"
|
||||
fi
|
||||
|
||||
read -r -e -p "Copy in the stock keymap for customization? [Yn]: " copy_keymap
|
||||
|
||||
if [ -z "$copy_keymap" ] || [ "$copy_keymap" == "Y" ] || [ "$copy_keymap" == "y" ]; then copy_keymap="yes"; fi
|
||||
|
||||
read -r -e -p "GitHub Username (leave empty to skip GitHub repo creation): " github_user
|
||||
if [ -n "$github_user" ]; then
|
||||
read -r -p "GitHub Repo Name [zmk-config]: " repo_name
|
||||
if [ -z "$repo_name" ]; then repo_name="zmk-config"; fi
|
||||
|
||||
read -r -p "GitHub Repo [https://github.com/${github_user}/${repo_name}.git]: " github_repo
|
||||
|
||||
if [ -z "$github_repo" ]; then github_repo="https://github.com/${github_user}/${repo_name}.git"; fi
|
||||
else
|
||||
repo_name="zmk-config"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Preparing a user config for:"
|
||||
echo "* MCU Board: ${board}"
|
||||
echo "* Shield: ${shield}"
|
||||
|
||||
if [ "$copy_keymap" == "yes" ]; then
|
||||
echo "* Copy Keymap?: ✓"
|
||||
else
|
||||
echo "* Copy Keymap?: ❌"
|
||||
fi
|
||||
|
||||
if [ -n "$github_repo" ]; then
|
||||
echo "* GitHub Repo To Push (please create this in GH first!): ${github_repo}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
read -r -p "Continue? [Yn]: " do_it
|
||||
|
||||
if [ -n "$do_it" ] && [ "$do_it" != "y" ] && [ "$do_it" != "Y" ]; then
|
||||
echo "Aborting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git clone --single-branch $repo_path ${repo_name}
|
||||
cd ${repo_name}
|
||||
|
||||
pushd config
|
||||
|
||||
$download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.conf"
|
||||
|
||||
if [ "$copy_keymap" == "yes" ]; then
|
||||
$download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.keymap"
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
sed -i'.orig' \
|
||||
-e "s/BOARD_NAME/$board/" \
|
||||
-e "s/SHIELD_NAME/$shield/" \
|
||||
-e "s/KEYBOARD_TITLE/$shield_title/" \
|
||||
.github/workflows/build.yml
|
||||
|
||||
if [ "$board" == "proton_c" ]; then
|
||||
# Proton-C board still fa
|
||||
sed -i'.orig' -e "s/uf2/hex/g" .github/workflows/build.yml
|
||||
fi
|
||||
|
||||
rm .github/workflows/*.yml.orig
|
||||
|
||||
rm -rf .git
|
||||
git init .
|
||||
git add .
|
||||
git commit -m "Initial User Config."
|
||||
|
||||
if [ -n "$github_repo" ]; then
|
||||
git remote add origin "$github_repo"
|
||||
git push --set-upstream origin "$(git symbolic-ref --short HEAD)"
|
||||
push_return_code=$?
|
||||
|
||||
# If push failed, assume that the origin was incorrect and give instructions on fixing.
|
||||
if [ ${push_return_code} -ne 0 ]; then
|
||||
echo "Remote repository $github_repo not found..."
|
||||
echo "Check GitHub URL, and try adding again."
|
||||
echo "Run the following: "
|
||||
echo " git remote rm origin"
|
||||
echo " git remote add origin FIXED_URL"
|
||||
echo " git push --set-upstream origin $(git symbolic-ref --short HEAD)"
|
||||
echo "Once pushed, your firmware should be availalbe from GitHub Actions at: ${github_repo%.git}/actions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# TODO: Support determing the actions URL when non-https:// repo URL is used.
|
||||
if [ "${github_repo}" != "${github_repo#https://}" ]; then
|
||||
echo "Your firmware should be available from GitHub Actions shortly: ${github_repo%.git}/actions"
|
||||
fi
|
||||
fi
|
228
docs/static/setup_REMOTE_1333.sh
vendored
Normal file
228
docs/static/setup_REMOTE_1333.sh
vendored
Normal file
|
@ -0,0 +1,228 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
set -e
|
||||
|
||||
check_exists() {
|
||||
command_to_run=$1
|
||||
error_message=$2
|
||||
local __resultvar=$3
|
||||
|
||||
if ! eval "$command_to_run" &> /dev/null; then
|
||||
if [[ "$__resultvar" != "" ]]; then
|
||||
eval $__resultvar="'false'"
|
||||
else
|
||||
printf "%s\n" "$error_message"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [[ "$__resultvar" != "" ]]; then
|
||||
eval $__resultvar="'true'"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_exists "command -v git" "git is not installed, and is required for this script!"
|
||||
check_exists "command -v curl" "curl is not installed, and is required for this script!" curl_exists
|
||||
check_exists "command -v wget" "wget is not installed, and is required for this script!" wget_exists
|
||||
|
||||
check_exists "git config user.name" "Git username not set!\nRun: git config --global user.name 'My Name'"
|
||||
check_exists "git config user.email" "Git email not set!\nRun: git config --global user.email 'example@myemail.com'"
|
||||
|
||||
# Check to see if the user has write permissions in this directory to prevent a cryptic error later on
|
||||
if [ ! -w `pwd` ]; then
|
||||
echo 'Sorry, you do not have write permissions in this directory.';
|
||||
echo 'Please try running this script again from a directory that you do have write permissions for.';
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse all commandline options
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
-w|--wget) force_wget="true"; break;;
|
||||
*) echo "Unknown parameter: $1"; exit 1;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ $curl_exists == "true" && $wget_exists == "true" ]]; then
|
||||
if [[ $force_wget == "true" ]]; then
|
||||
download_command="wget "
|
||||
else
|
||||
download_command="curl -O "
|
||||
fi
|
||||
elif [[ $curl_exists == "true" ]]; then
|
||||
download_command="curl -O "
|
||||
elif [[ $wget_exists == "true" ]]; then
|
||||
download_command="wget "
|
||||
else
|
||||
echo 'Neither curl nor wget are installed. One of the two is required for this script!'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
repo_path="https://github.com/zmkfirmware/zmk-config-split-template.git"
|
||||
title="ZMK Config Setup:"
|
||||
|
||||
prompt="Pick an MCU board:"
|
||||
options=("nice!nano" "QMK Proton-C" "BlueMicro840 (v1)" "makerdiary nRF52840 M.2")
|
||||
|
||||
echo "$title"
|
||||
echo ""
|
||||
echo "MCU Board Selection:"
|
||||
PS3="$prompt "
|
||||
select opt in "${options[@]}" "Quit"; do
|
||||
|
||||
case "$REPLY" in
|
||||
|
||||
1 ) board="nice_nano"; break;;
|
||||
2 ) board="proton_c"; break;;
|
||||
3 ) board="bluemicro840_v1"; break;;
|
||||
3 ) board="nrf52840_m2"; break;;
|
||||
|
||||
$(( ${#options[@]}+1 )) ) echo "Goodbye!"; exit 1;;
|
||||
*) echo "Invalid option. Try another one."; continue;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Keyboard Shield Selection:"
|
||||
|
||||
prompt="Pick an keyboard:"
|
||||
options=("Kyria" "Lily58" "Corne" "Splitreus62" "Sofle" "Iris" "Reviung41" "RoMac" "RoMac+" "makerdiary M60" "Microdox" "TG4X" "QAZ" "NIBBLE" "Jorne" "Jian" "CRBN" "Tidbit" "Eek!" "BFO-9000" "Helix")
|
||||
|
||||
PS3="$prompt "
|
||||
# TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos.
|
||||
# select opt in "${options[@]}" "Other" "Quit"; do
|
||||
select opt in "${options[@]}" "Quit"; do
|
||||
|
||||
case "$REPLY" in
|
||||
|
||||
1 ) shield_title="Kyria" shield="kyria"; split="y"; break;;
|
||||
2 ) shield_title="Lily58" shield="lily58"; split="y"; break;;
|
||||
3 ) shield_title="Corne" shield="corne"; split="y"; break;;
|
||||
4 ) shield_title="Splitreus62" shield="splitreus62"; split="y"; break;;
|
||||
5 ) shield_title="Sofle" shield="sofle"; split="y"; break;;
|
||||
6 ) shield_title="Iris" shield="iris"; split="y"; break;;
|
||||
7 ) shield_title="Reviung41" shield="reviung41"; split="n"; break;;
|
||||
8 ) shield_title="RoMac" shield="romac"; split="n"; break;;
|
||||
9 ) shield_title="RoMac+" shield="romac_plus"; split="n"; break;;
|
||||
10 ) shield_title="M60" shield="m60"; split="n"; break;;
|
||||
11 ) shield_title="Microdox" shield="microdox"; split="y"; break;;
|
||||
12 ) shield_title="TG4X" shield="tg4x"; split="n"; break;;
|
||||
13 ) shield_title="QAZ" shield="qaz"; split="n"; break;;
|
||||
14 ) shield_title="NIBBLE" shield="nibble"; split="n"; break;;
|
||||
15 ) shield_title="Jorne" shield="jorne"; split="y"; break;;
|
||||
16 ) shield_title="Jian" shield="jian"; split="y"; break;;
|
||||
17 ) shield_title="CRBN" shield="crbn"; split="n"; break;;
|
||||
18 ) shield_title="Tidbit" shield="tidbit"; split="n" break;;
|
||||
19 ) shield_title="Eek!" shield="eek"; split="n" break;;
|
||||
20 ) shield_title="BFO-9000" shield="bfo9000"; split="y"; break;;
|
||||
21 ) shield_title="Helix" shield="helix"; split="y"; break;;
|
||||
|
||||
# Add link to docs on adding your own custom shield in your ZMK config!
|
||||
# $(( ${#options[@]}+1 )) ) echo "Other!"; break;;
|
||||
$(( ${#options[@]}+1 )) ) echo "Goodbye!"; exit 1;;
|
||||
*) echo "Invalid option. Try another one.";continue;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$split" == "n" ]; then
|
||||
repo_path="https://github.com/zmkfirmware/zmk-config-template.git"
|
||||
fi
|
||||
|
||||
read -r -e -p "Copy in the stock keymap for customization? [Yn]: " copy_keymap
|
||||
|
||||
if [ -z "$copy_keymap" ] || [ "$copy_keymap" == "Y" ] || [ "$copy_keymap" == "y" ]; then copy_keymap="yes"; fi
|
||||
|
||||
read -r -e -p "GitHub Username (leave empty to skip GitHub repo creation): " github_user
|
||||
if [ -n "$github_user" ]; then
|
||||
read -r -p "GitHub Repo Name [zmk-config]: " repo_name
|
||||
if [ -z "$repo_name" ]; then repo_name="zmk-config"; fi
|
||||
|
||||
read -r -p "GitHub Repo [https://github.com/${github_user}/${repo_name}.git]: " github_repo
|
||||
|
||||
if [ -z "$github_repo" ]; then github_repo="https://github.com/${github_user}/${repo_name}.git"; fi
|
||||
else
|
||||
repo_name="zmk-config"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Preparing a user config for:"
|
||||
echo "* MCU Board: ${board}"
|
||||
echo "* Shield: ${shield}"
|
||||
|
||||
if [ "$copy_keymap" == "yes" ]; then
|
||||
echo "* Copy Keymap?: ✓"
|
||||
else
|
||||
echo "* Copy Keymap?: ❌"
|
||||
fi
|
||||
|
||||
if [ -n "$github_repo" ]; then
|
||||
echo "* GitHub Repo To Push (please create this in GH first!): ${github_repo}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
read -r -p "Continue? [Yn]: " do_it
|
||||
|
||||
if [ -n "$do_it" ] && [ "$do_it" != "y" ] && [ "$do_it" != "Y" ]; then
|
||||
echo "Aborting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git clone --single-branch $repo_path ${repo_name}
|
||||
cd ${repo_name}
|
||||
|
||||
pushd config
|
||||
|
||||
$download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.conf"
|
||||
|
||||
if [ "$copy_keymap" == "yes" ]; then
|
||||
$download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.keymap"
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
sed -i'.orig' \
|
||||
-e "s/BOARD_NAME/$board/" \
|
||||
-e "s/SHIELD_NAME/$shield/" \
|
||||
-e "s/KEYBOARD_TITLE/$shield_title/" \
|
||||
.github/workflows/build.yml
|
||||
|
||||
if [ "$board" == "proton_c" ]; then
|
||||
# Proton-C board still fa
|
||||
sed -i'.orig' -e "s/uf2/hex/g" .github/workflows/build.yml
|
||||
fi
|
||||
|
||||
rm .github/workflows/*.yml.orig
|
||||
|
||||
rm -rf .git
|
||||
git init .
|
||||
git add .
|
||||
git commit -m "Initial User Config."
|
||||
|
||||
if [ -n "$github_repo" ]; then
|
||||
git remote add origin "$github_repo"
|
||||
git push --set-upstream origin "$(git symbolic-ref --short HEAD)"
|
||||
push_return_code=$?
|
||||
|
||||
# If push failed, assume that the origin was incorrect and give instructions on fixing.
|
||||
if [ ${push_return_code} -ne 0 ]; then
|
||||
echo "Remote repository $github_repo not found..."
|
||||
echo "Check GitHub URL, and try adding again."
|
||||
echo "Run the following: "
|
||||
echo " git remote rm origin"
|
||||
echo " git remote add origin FIXED_URL"
|
||||
echo " git push --set-upstream origin $(git symbolic-ref --short HEAD)"
|
||||
echo "Once pushed, your firmware should be availalbe from GitHub Actions at: ${github_repo%.git}/actions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# TODO: Support determing the actions URL when non-https:// repo URL is used.
|
||||
if [ "${github_repo}" != "${github_repo#https://}" ]; then
|
||||
echo "Your firmware should be available from GitHub Actions shortly: ${github_repo%.git}/actions"
|
||||
fi
|
||||
fi
|
Loading…
Add table
Reference in a new issue