Merge remote-tracking branch 'upstream/main' into split_battery_service
This commit is contained in:
commit
efe06502c5
48 changed files with 1501 additions and 468 deletions
22
.github/workflows/build-user-config.yml
vendored
22
.github/workflows/build-user-config.yml
vendored
|
@ -18,6 +18,11 @@ on:
|
|||
default: "bin"
|
||||
required: false
|
||||
type: string
|
||||
artifact_name:
|
||||
description: 'Artifact output file name'
|
||||
default: 'firmware'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
matrix:
|
||||
|
@ -32,9 +37,10 @@ jobs:
|
|||
- name: Install yaml2json
|
||||
run: python3 -m pip install remarshal
|
||||
|
||||
- id: set-matrix
|
||||
name: Fetch Build Matrix
|
||||
- name: Fetch Build Matrix
|
||||
id: set-matrix
|
||||
run: |
|
||||
set -x
|
||||
matrix=$(yaml2json ${{ inputs.build_matrix_path }} | jq -c .)
|
||||
yaml2json ${{ inputs.build_matrix_path }}
|
||||
echo "::set-output name=matrix::${matrix}"
|
||||
|
@ -52,7 +58,9 @@ jobs:
|
|||
- name: Prepare variables
|
||||
id: variables
|
||||
run: |
|
||||
if [ -n "${{ matrix.shield }}" ]; then
|
||||
set -x
|
||||
if [ -n "${{ matrix.shield }}" ]
|
||||
then
|
||||
EXTRA_CMAKE_ARGS="-DSHIELD=${{ matrix.shield }}"
|
||||
ARTIFACT_NAME="${{ matrix.shield }}-${{ matrix.board }}-zmk"
|
||||
DISPLAY_NAME="${{ matrix.shield }} - ${{ matrix.board }}"
|
||||
|
@ -70,7 +78,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
|
||||
- name: Cache west modules
|
||||
uses: actions/cache@v3.0.1
|
||||
uses: actions/cache@v3.0.2
|
||||
continue-on-error: true
|
||||
env:
|
||||
cache-name: cache-zephyr-${{ steps.variables.outputs.zephyr-version }}-modules
|
||||
|
@ -98,13 +106,15 @@ jobs:
|
|||
|
||||
- name: West Build (${{ steps.variables.outputs.display-name }})
|
||||
run: |
|
||||
set -x
|
||||
west build -s zmk/app -b ${{ matrix.board }} -- -DZMK_CONFIG=${GITHUB_WORKSPACE}/${{ inputs.config_path }} ${{ steps.variables.outputs.extra-cmake-args }} ${{ matrix.cmake-args }}
|
||||
|
||||
- name: ${{ steps.variables.outputs.display-name }} Kconfig file
|
||||
run: cat build/zephyr/.config | grep -v "^#" | grep -v "^$" | sort
|
||||
run: grep -v -e "^#" -e "^$" build/zephyr/.config | sort
|
||||
|
||||
- name: Rename artifacts
|
||||
run: |
|
||||
set -x
|
||||
mkdir build/artifacts
|
||||
if [ -f build/zephyr/zmk.uf2 ]
|
||||
then
|
||||
|
@ -117,5 +127,5 @@ jobs:
|
|||
- name: Archive (${{ steps.variables.outputs.display-name }})
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: firmware
|
||||
name: ${{ inputs.artifact_name }}
|
||||
path: build/artifacts
|
||||
|
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Cache west modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3.0.2
|
||||
env:
|
||||
cache-name: cache-zephyr-modules
|
||||
with:
|
||||
|
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -38,7 +38,7 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Cache west modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3.0.2
|
||||
env:
|
||||
cache-name: cache-zephyr-modules
|
||||
with:
|
||||
|
|
|
@ -26,25 +26,19 @@ target_sources(app PRIVATE src/stdlib.c)
|
|||
target_sources(app PRIVATE src/activity.c)
|
||||
target_sources(app PRIVATE src/kscan.c)
|
||||
target_sources(app PRIVATE src/matrix_transform.c)
|
||||
target_sources(app PRIVATE src/hid.c)
|
||||
target_sources(app PRIVATE src/sensors.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c)
|
||||
target_sources(app PRIVATE src/event_manager.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c)
|
||||
target_sources(app PRIVATE src/events/activity_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/position_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/layer_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/keycode_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/modifiers_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/endpoint_selection_changed.c)
|
||||
target_sources(app PRIVATE src/events/sensor_event.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
|
||||
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
target_sources(app PRIVATE src/hid.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c)
|
||||
|
@ -63,28 +57,48 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
|||
target_sources(app PRIVATE src/combo.c)
|
||||
target_sources(app PRIVATE src/behavior_queue.c)
|
||||
target_sources(app PRIVATE src/conditional_layer.c)
|
||||
target_sources(app PRIVATE src/endpoints.c)
|
||||
target_sources(app PRIVATE src/events/endpoint_selection_changed.c)
|
||||
target_sources(app PRIVATE src/hid_listener.c)
|
||||
target_sources(app PRIVATE src/keymap.c)
|
||||
target_sources(app PRIVATE src/events/layer_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/modifiers_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/keycode_state_changed.c)
|
||||
|
||||
if (CONFIG_ZMK_BLE)
|
||||
target_sources(app PRIVATE src/events/ble_active_profile_changed.c)
|
||||
|
||||
target_sources(app PRIVATE src/behaviors/behavior_bt.c)
|
||||
target_sources(app PRIVATE src/ble.c)
|
||||
target_sources(app PRIVATE src/hog.c)
|
||||
if (CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
target_sources(app PRIVATE src/battery_split.c)
|
||||
else()
|
||||
target_sources(app PRIVATE src/battery.c)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/behaviors/behavior_backlight.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/behaviors/behavior_bt.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble.c)
|
||||
if (CONFIG_ZMK_SPLIT_BLE AND (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
|
||||
target_sources(app PRIVATE src/split_listener.c)
|
||||
target_sources(app PRIVATE src/split/bluetooth/service.c)
|
||||
target_sources(app PRIVATE src/battery.c)
|
||||
endif()
|
||||
if (CONFIG_ZMK_SPLIT_BLE AND CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
target_sources(app PRIVATE src/split/bluetooth/central.c)
|
||||
target_sources(app PRIVATE src/battery_split.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c)
|
||||
|
||||
if (CONFIG_ZMK_SPLIT_BLE)
|
||||
if (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
target_sources(app PRIVATE src/split_listener.c)
|
||||
target_sources(app PRIVATE src/split/bluetooth/service.c)
|
||||
target_sources(app PRIVATE src/split/bluetooth/peripheral.c)
|
||||
target_sources(app PRIVATE src/events/split_peripheral_status_changed.c)
|
||||
endif()
|
||||
if (CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
target_sources(app PRIVATE src/split/bluetooth/central.c)
|
||||
endif()
|
||||
endif()
|
||||
target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/usb.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_USB app PRIVATE src/usb_hid.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/hog.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/backlight.c)
|
||||
target_sources(app PRIVATE src/endpoints.c)
|
||||
target_sources(app PRIVATE src/hid_listener.c)
|
||||
target_sources(app PRIVATE src/main.c)
|
||||
|
||||
add_subdirectory(src/display/)
|
||||
|
|
|
@ -523,6 +523,11 @@ config ZMK_SETTINGS_SAVE_DEBOUNCE
|
|||
#SETTINGS
|
||||
endif
|
||||
|
||||
config ZMK_BATTERY_REPORT_INTERVAL
|
||||
depends on ZMK_BLE
|
||||
int "Battery level report interval in seconds"
|
||||
default 60
|
||||
|
||||
#Advanced
|
||||
endmenu
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
&spi2 {
|
||||
status = "okay";
|
||||
pinctrl-0 = <&spi2_sck_pb13 &spi2_miso_pb14 &spi2_mosi_pb15>;
|
||||
pinctrl-0 = <&spi2_sck_pb13 &spi2_mosi_pb15>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
led_strip: ws2812@0 {
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
vbatt: vbatt {
|
||||
compatible = "zmk,battery-voltage-divider";
|
||||
label = "BATTERY";
|
||||
io-channels = <&adc 2>;
|
||||
io-channels = <&adc 1>;
|
||||
output-ohms = <10000000>;
|
||||
full-ohms = <(10000000 + 4000000)>;
|
||||
};
|
||||
|
|
|
@ -6,3 +6,10 @@ CONFIG_UART_INTERRUPT_DRIVEN=n
|
|||
CONFIG_ZMK_USB=y
|
||||
CONFIG_ZMK_BLE=y
|
||||
|
||||
|
||||
CONFIG_MPU_ALLOW_FLASH_WRITE=y
|
||||
CONFIG_NVS=y
|
||||
CONFIG_SETTINGS_NVS=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_PAGE_LAYOUT=y
|
||||
CONFIG_FLASH_MAP=y
|
|
@ -4,83 +4,297 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_kscan_gpio_direct
|
||||
#include "debounce.h"
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/kscan.h>
|
||||
#include <devicetree.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <drivers/kscan.h>
|
||||
#include <kernel.h>
|
||||
#include <logging/log.h>
|
||||
#include <sys/util.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
struct kscan_gpio_item_config {
|
||||
char *label;
|
||||
gpio_pin_t pin;
|
||||
gpio_flags_t flags;
|
||||
};
|
||||
#define DT_DRV_COMPAT zmk_kscan_gpio_direct
|
||||
|
||||
union work_reference {
|
||||
struct k_work_delayable delayed;
|
||||
struct k_work direct;
|
||||
};
|
||||
#if CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS >= 0
|
||||
#define INST_DEBOUNCE_PRESS_MS(n) CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS
|
||||
#else
|
||||
#define INST_DEBOUNCE_PRESS_MS(n) \
|
||||
DT_INST_PROP_OR(n, debounce_period, DT_INST_PROP(n, debounce_press_ms))
|
||||
#endif
|
||||
|
||||
struct kscan_gpio_config {
|
||||
uint8_t num_of_inputs;
|
||||
uint8_t debounce_period;
|
||||
struct kscan_gpio_item_config inputs[];
|
||||
};
|
||||
#if CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS >= 0
|
||||
#define INST_DEBOUNCE_RELEASE_MS(n) CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS
|
||||
#else
|
||||
#define INST_DEBOUNCE_RELEASE_MS(n) \
|
||||
DT_INST_PROP_OR(n, debounce_period, DT_INST_PROP(n, debounce_release_ms))
|
||||
#endif
|
||||
|
||||
struct kscan_gpio_data {
|
||||
#if defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
|
||||
struct k_timer poll_timer;
|
||||
#endif /* defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING) */
|
||||
kscan_callback_t callback;
|
||||
union work_reference work;
|
||||
#define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
|
||||
#define USE_INTERRUPTS (!USE_POLLING)
|
||||
|
||||
#define COND_INTERRUPTS(code) COND_CODE_1(CONFIG_ZMK_KSCAN_DIRECT_POLLING, (), code)
|
||||
#define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_DIRECT_POLLING, pollcode, intcode)
|
||||
|
||||
#define INST_INPUTS_LEN(n) DT_INST_PROP_LEN(n, input_gpios)
|
||||
#define KSCAN_DIRECT_INPUT_CFG_INIT(idx, inst_idx) \
|
||||
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), input_gpios, idx),
|
||||
|
||||
struct kscan_direct_irq_callback {
|
||||
const struct device *dev;
|
||||
uint32_t pin_state;
|
||||
const struct device *inputs[];
|
||||
};
|
||||
|
||||
static const struct device **kscan_gpio_input_devices(const struct device *dev) {
|
||||
struct kscan_gpio_data *data = dev->data;
|
||||
return data->inputs;
|
||||
}
|
||||
|
||||
static const struct kscan_gpio_item_config *kscan_gpio_input_configs(const struct device *dev) {
|
||||
const struct kscan_gpio_config *cfg = dev->config;
|
||||
return cfg->inputs;
|
||||
}
|
||||
|
||||
static void kscan_gpio_direct_queue_read(union work_reference *work, uint8_t debounce_period) {
|
||||
if (debounce_period > 0) {
|
||||
k_work_reschedule(&work->delayed, K_MSEC(debounce_period));
|
||||
} else {
|
||||
k_work_submit(&work->direct);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
|
||||
|
||||
struct kscan_gpio_irq_callback {
|
||||
const struct device *dev;
|
||||
union work_reference *work;
|
||||
uint8_t debounce_period;
|
||||
struct gpio_callback callback;
|
||||
};
|
||||
|
||||
static int kscan_gpio_config_interrupts(const struct device *dev, gpio_flags_t flags) {
|
||||
const struct kscan_gpio_config *cfg = dev->config;
|
||||
const struct device **devices = kscan_gpio_input_devices(dev);
|
||||
const struct kscan_gpio_item_config *configs = kscan_gpio_input_configs(dev);
|
||||
struct kscan_direct_data {
|
||||
const struct device *dev;
|
||||
kscan_callback_t callback;
|
||||
struct k_work_delayable work;
|
||||
#if USE_INTERRUPTS
|
||||
/** Array of length config->inputs.len */
|
||||
struct kscan_direct_irq_callback *irqs;
|
||||
#endif
|
||||
/** Timestamp of the current or scheduled scan. */
|
||||
int64_t scan_time;
|
||||
/** Current state of the inputs as an array of length config->inputs.len */
|
||||
struct debounce_state *pin_state;
|
||||
};
|
||||
|
||||
for (int i = 0; i < cfg->num_of_inputs; i++) {
|
||||
const struct device *dev = devices[i];
|
||||
const struct kscan_gpio_item_config *cfg = &configs[i];
|
||||
struct kscan_gpio_list {
|
||||
const struct gpio_dt_spec *gpios;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags);
|
||||
/** Define a kscan_gpio_list from a compile-time GPIO array. */
|
||||
#define KSCAN_GPIO_LIST(gpio_array) \
|
||||
((struct kscan_gpio_list){.gpios = gpio_array, .len = ARRAY_SIZE(gpio_array)})
|
||||
|
||||
struct kscan_direct_config {
|
||||
struct kscan_gpio_list inputs;
|
||||
struct debounce_config debounce_config;
|
||||
int32_t debounce_scan_period_ms;
|
||||
int32_t poll_period_ms;
|
||||
bool toggle_mode;
|
||||
};
|
||||
|
||||
#if USE_INTERRUPTS
|
||||
static int kscan_direct_interrupt_configure(const struct device *dev, const gpio_flags_t flags) {
|
||||
const struct kscan_direct_config *config = dev->config;
|
||||
|
||||
for (int i = 0; i < config->inputs.len; i++) {
|
||||
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
|
||||
|
||||
int err = gpio_pin_interrupt_configure_dt(gpio, flags);
|
||||
if (err) {
|
||||
LOG_ERR("Unable to configure interrupt for pin %u on %s", gpio->pin, gpio->port->name);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_INTERRUPTS
|
||||
static int kscan_direct_interrupt_enable(const struct device *dev) {
|
||||
return kscan_direct_interrupt_configure(dev, GPIO_INT_LEVEL_ACTIVE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_INTERRUPTS
|
||||
static int kscan_direct_interrupt_disable(const struct device *dev) {
|
||||
return kscan_direct_interrupt_configure(dev, GPIO_INT_DISABLE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_INTERRUPTS
|
||||
static void kscan_direct_irq_callback_handler(const struct device *port, struct gpio_callback *cb,
|
||||
const gpio_port_pins_t pin) {
|
||||
struct kscan_direct_irq_callback *irq_data =
|
||||
CONTAINER_OF(cb, struct kscan_direct_irq_callback, callback);
|
||||
struct kscan_direct_data *data = irq_data->dev->data;
|
||||
|
||||
// Disable our interrupts temporarily to avoid re-entry while we scan.
|
||||
kscan_direct_interrupt_disable(data->dev);
|
||||
|
||||
data->scan_time = k_uptime_get();
|
||||
|
||||
k_work_reschedule(&data->work, K_NO_WAIT);
|
||||
}
|
||||
#endif
|
||||
|
||||
static gpio_flags_t kscan_gpio_get_extra_flags(const struct gpio_dt_spec *gpio, bool active) {
|
||||
if (!active) {
|
||||
return ((BIT(0) & gpio->dt_flags) ? GPIO_PULL_UP : GPIO_PULL_DOWN);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_inputs_set_flags(const struct kscan_gpio_list *inputs,
|
||||
const struct gpio_dt_spec *active_gpio) {
|
||||
gpio_flags_t extra_flags;
|
||||
for (int i = 0; i < inputs->len; i++) {
|
||||
extra_flags = GPIO_INPUT | kscan_gpio_get_extra_flags(&inputs->gpios[i],
|
||||
&inputs->gpios[i] == active_gpio);
|
||||
LOG_DBG("Extra flags equal to: %d", extra_flags);
|
||||
|
||||
int err = gpio_pin_configure_dt(&inputs->gpios[i], extra_flags);
|
||||
if (err) {
|
||||
LOG_ERR("Unable to configure flags on pin %d on %s", inputs->gpios[i].pin,
|
||||
inputs->gpios[i].port->name);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kscan_direct_read_continue(const struct device *dev) {
|
||||
const struct kscan_direct_config *config = dev->config;
|
||||
struct kscan_direct_data *data = dev->data;
|
||||
|
||||
data->scan_time += config->debounce_scan_period_ms;
|
||||
|
||||
k_work_reschedule(&data->work, K_TIMEOUT_ABS_MS(data->scan_time));
|
||||
}
|
||||
|
||||
static void kscan_direct_read_end(const struct device *dev) {
|
||||
#if USE_INTERRUPTS
|
||||
// Return to waiting for an interrupt.
|
||||
kscan_direct_interrupt_enable(dev);
|
||||
#else
|
||||
struct kscan_direct_data *data = dev->data;
|
||||
const struct kscan_direct_config *config = dev->config;
|
||||
|
||||
data->scan_time += config->poll_period_ms;
|
||||
|
||||
// Return to polling slowly.
|
||||
k_work_reschedule(&data->work, K_TIMEOUT_ABS_MS(data->scan_time));
|
||||
#endif
|
||||
}
|
||||
|
||||
static int kscan_direct_read(const struct device *dev) {
|
||||
struct kscan_direct_data *data = dev->data;
|
||||
const struct kscan_direct_config *config = dev->config;
|
||||
|
||||
// Read the inputs.
|
||||
for (int i = 0; i < config->inputs.len; i++) {
|
||||
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
|
||||
|
||||
const bool active = gpio_pin_get_dt(gpio);
|
||||
|
||||
debounce_update(&data->pin_state[i], active, config->debounce_scan_period_ms,
|
||||
&config->debounce_config);
|
||||
}
|
||||
|
||||
// Process the new state.
|
||||
bool continue_scan = false;
|
||||
|
||||
for (int i = 0; i < config->inputs.len; i++) {
|
||||
struct debounce_state *state = &data->pin_state[i];
|
||||
|
||||
if (debounce_get_changed(state)) {
|
||||
const bool pressed = debounce_is_pressed(state);
|
||||
|
||||
LOG_DBG("Sending event at 0,%i state %s", i, pressed ? "on" : "off");
|
||||
data->callback(dev, 0, i, pressed);
|
||||
if (config->toggle_mode && pressed) {
|
||||
kscan_inputs_set_flags(&config->inputs, &config->inputs.gpios[i]);
|
||||
}
|
||||
}
|
||||
|
||||
continue_scan = continue_scan || debounce_is_active(state);
|
||||
}
|
||||
|
||||
if (continue_scan) {
|
||||
// At least one key is pressed or the debouncer has not yet decided if
|
||||
// it is pressed. Poll quickly until everything is released.
|
||||
kscan_direct_read_continue(dev);
|
||||
} else {
|
||||
// All keys are released. Return to normal.
|
||||
kscan_direct_read_end(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kscan_direct_work_handler(struct k_work *work) {
|
||||
struct k_work_delayable *dwork = CONTAINER_OF(work, struct k_work_delayable, work);
|
||||
struct kscan_direct_data *data = CONTAINER_OF(dwork, struct kscan_direct_data, work);
|
||||
kscan_direct_read(data->dev);
|
||||
}
|
||||
|
||||
static int kscan_direct_configure(const struct device *dev, kscan_callback_t callback) {
|
||||
struct kscan_direct_data *data = dev->data;
|
||||
|
||||
if (!callback) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->callback = callback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_direct_enable(const struct device *dev) {
|
||||
struct kscan_direct_data *data = dev->data;
|
||||
|
||||
data->scan_time = k_uptime_get();
|
||||
|
||||
// Read will automatically start interrupts/polling once done.
|
||||
return kscan_direct_read(dev);
|
||||
}
|
||||
|
||||
static int kscan_direct_disable(const struct device *dev) {
|
||||
struct kscan_direct_data *data = dev->data;
|
||||
|
||||
k_work_cancel_delayable(&data->work);
|
||||
|
||||
#if USE_INTERRUPTS
|
||||
return kscan_direct_interrupt_disable(dev);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int kscan_direct_init_input_inst(const struct device *dev, const struct gpio_dt_spec *gpio,
|
||||
const int index, bool toggle_mode) {
|
||||
if (!device_is_ready(gpio->port)) {
|
||||
LOG_ERR("GPIO is not ready: %s", gpio->port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
int err = gpio_pin_configure_dt(
|
||||
gpio, GPIO_INPUT | (toggle_mode ? kscan_gpio_get_extra_flags(gpio, false) : 0));
|
||||
if (err) {
|
||||
LOG_ERR("Unable to configure pin %u on %s for input", gpio->pin, gpio->port->name);
|
||||
return err;
|
||||
}
|
||||
|
||||
LOG_DBG("Configured pin %u on %s for input", gpio->pin, gpio->port->name);
|
||||
|
||||
#if USE_INTERRUPTS
|
||||
struct kscan_direct_data *data = dev->data;
|
||||
struct kscan_direct_irq_callback *irq = &data->irqs[index];
|
||||
|
||||
irq->dev = dev;
|
||||
gpio_init_callback(&irq->callback, kscan_direct_irq_callback_handler, BIT(gpio->pin));
|
||||
err = gpio_add_callback(gpio->port, &irq->callback);
|
||||
if (err) {
|
||||
LOG_ERR("Error adding the callback to the input device: %i", err);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_direct_init_inputs(const struct device *dev) {
|
||||
const struct kscan_direct_config *config = dev->config;
|
||||
|
||||
for (int i = 0; i < config->inputs.len; i++) {
|
||||
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
|
||||
int err = kscan_direct_init_input_inst(dev, gpio, i, config->toggle_mode);
|
||||
if (err) {
|
||||
LOG_ERR("Unable to enable direct GPIO interrupt");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
@ -88,155 +302,55 @@ static int kscan_gpio_config_interrupts(const struct device *dev, gpio_flags_t f
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_gpio_direct_enable(const struct device *dev) {
|
||||
return kscan_gpio_config_interrupts(dev, GPIO_INT_LEVEL_ACTIVE);
|
||||
}
|
||||
static int kscan_gpio_direct_disable(const struct device *dev) {
|
||||
return kscan_gpio_config_interrupts(dev, GPIO_INT_DISABLE);
|
||||
}
|
||||
static int kscan_direct_init(const struct device *dev) {
|
||||
struct kscan_direct_data *data = dev->data;
|
||||
|
||||
static void kscan_gpio_irq_callback_handler(const struct device *dev, struct gpio_callback *cb,
|
||||
gpio_port_pins_t pin) {
|
||||
struct kscan_gpio_irq_callback *data =
|
||||
CONTAINER_OF(cb, struct kscan_gpio_irq_callback, callback);
|
||||
data->dev = dev;
|
||||
|
||||
kscan_gpio_direct_disable(data->dev);
|
||||
kscan_gpio_direct_queue_read(data->work, data->debounce_period);
|
||||
}
|
||||
kscan_direct_init_inputs(dev);
|
||||
|
||||
#else /* !defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING) */
|
||||
|
||||
static void kscan_gpio_timer_handler(struct k_timer *timer) {
|
||||
struct kscan_gpio_data *data = CONTAINER_OF(timer, struct kscan_gpio_data, poll_timer);
|
||||
|
||||
kscan_gpio_direct_queue_read(&data->work, 0);
|
||||
}
|
||||
|
||||
static int kscan_gpio_direct_enable(const struct device *dev) {
|
||||
struct kscan_gpio_data *data = dev->data;
|
||||
k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10));
|
||||
return 0;
|
||||
}
|
||||
static int kscan_gpio_direct_disable(const struct device *dev) {
|
||||
struct kscan_gpio_data *data = dev->data;
|
||||
k_timer_stop(&data->poll_timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING) */
|
||||
|
||||
static int kscan_gpio_direct_configure(const struct device *dev, kscan_callback_t callback) {
|
||||
struct kscan_gpio_data *data = dev->data;
|
||||
if (!callback) {
|
||||
return -EINVAL;
|
||||
}
|
||||
data->callback = callback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_gpio_read(const struct device *dev) {
|
||||
struct kscan_gpio_data *data = dev->data;
|
||||
const struct kscan_gpio_config *cfg = dev->config;
|
||||
uint32_t read_state = data->pin_state;
|
||||
bool submit_follow_up_read = false;
|
||||
for (int i = 0; i < cfg->num_of_inputs; i++) {
|
||||
const struct device *in_dev = kscan_gpio_input_devices(dev)[i];
|
||||
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs(dev)[i];
|
||||
WRITE_BIT(read_state, i, gpio_pin_get(in_dev, in_cfg->pin) > 0);
|
||||
}
|
||||
for (int i = 0; i < cfg->num_of_inputs; i++) {
|
||||
bool prev_pressed = BIT(i) & data->pin_state;
|
||||
bool pressed = (BIT(i) & read_state) != 0;
|
||||
submit_follow_up_read = (submit_follow_up_read || pressed);
|
||||
if (pressed != prev_pressed) {
|
||||
LOG_DBG("Sending event at %d,%d state %s", 0, i, (pressed ? "on" : "off"));
|
||||
WRITE_BIT(data->pin_state, i, pressed);
|
||||
data->callback(dev, 0, i, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
|
||||
if (submit_follow_up_read) {
|
||||
kscan_gpio_direct_queue_read(&data->work, cfg->debounce_period);
|
||||
} else {
|
||||
kscan_gpio_direct_enable(dev);
|
||||
}
|
||||
#endif
|
||||
k_work_init_delayable(&data->work, kscan_direct_work_handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kscan_gpio_work_handler(struct k_work *work) {
|
||||
struct kscan_gpio_data *data = CONTAINER_OF(work, struct kscan_gpio_data, work);
|
||||
kscan_gpio_read(data->dev);
|
||||
}
|
||||
|
||||
static const struct kscan_driver_api gpio_driver_api = {
|
||||
.config = kscan_gpio_direct_configure,
|
||||
.enable_callback = kscan_gpio_direct_enable,
|
||||
.disable_callback = kscan_gpio_direct_disable,
|
||||
static const struct kscan_driver_api kscan_direct_api = {
|
||||
.config = kscan_direct_configure,
|
||||
.enable_callback = kscan_direct_enable,
|
||||
.disable_callback = kscan_direct_disable,
|
||||
};
|
||||
|
||||
#define KSCAN_DIRECT_INPUT_ITEM(i, n) \
|
||||
{ \
|
||||
.label = DT_INST_GPIO_LABEL_BY_IDX(n, input_gpios, i), \
|
||||
.pin = DT_INST_GPIO_PIN_BY_IDX(n, input_gpios, i), \
|
||||
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, input_gpios, i), \
|
||||
},
|
||||
#define KSCAN_DIRECT_INIT(n) \
|
||||
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(n) <= DEBOUNCE_COUNTER_MAX, \
|
||||
"ZMK_KSCAN_DEBOUNCE_PRESS_MS or debounce-press-ms is too large"); \
|
||||
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \
|
||||
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
|
||||
\
|
||||
static const struct gpio_dt_spec kscan_direct_inputs_##n[] = { \
|
||||
UTIL_LISTIFY(INST_INPUTS_LEN(n), KSCAN_DIRECT_INPUT_CFG_INIT, n)}; \
|
||||
\
|
||||
static struct debounce_state kscan_direct_state_##n[INST_INPUTS_LEN(n)]; \
|
||||
\
|
||||
COND_INTERRUPTS( \
|
||||
(static struct kscan_direct_irq_callback kscan_direct_irqs_##n[INST_INPUTS_LEN(n)];)) \
|
||||
\
|
||||
static struct kscan_direct_data kscan_direct_data_##n = { \
|
||||
.pin_state = kscan_direct_state_##n, COND_INTERRUPTS((.irqs = kscan_direct_irqs_##n, ))}; \
|
||||
\
|
||||
static struct kscan_direct_config kscan_direct_config_##n = { \
|
||||
.inputs = KSCAN_GPIO_LIST(kscan_direct_inputs_##n), \
|
||||
.debounce_config = \
|
||||
{ \
|
||||
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \
|
||||
.debounce_release_ms = INST_DEBOUNCE_RELEASE_MS(n), \
|
||||
}, \
|
||||
.debounce_scan_period_ms = DT_INST_PROP(n, debounce_scan_period_ms), \
|
||||
.poll_period_ms = DT_INST_PROP(n, poll_period_ms), \
|
||||
.toggle_mode = DT_INST_PROP(n, toggle_mode), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, &kscan_direct_init, NULL, &kscan_direct_data_##n, \
|
||||
&kscan_direct_config_##n, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, \
|
||||
&kscan_direct_api);
|
||||
|
||||
#define INST_INPUT_LEN(n) DT_INST_PROP_LEN(n, input_gpios)
|
||||
|
||||
#define GPIO_INST_INIT(n) \
|
||||
COND_CODE_0(IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING), \
|
||||
(static struct kscan_gpio_irq_callback irq_callbacks_##n[INST_INPUT_LEN(n)];), ()) \
|
||||
static struct kscan_gpio_data kscan_gpio_data_##n = { \
|
||||
.inputs = {[INST_INPUT_LEN(n) - 1] = NULL}}; \
|
||||
static int kscan_gpio_init_##n(const struct device *dev) { \
|
||||
struct kscan_gpio_data *data = dev->data; \
|
||||
const struct kscan_gpio_config *cfg = dev->config; \
|
||||
int err; \
|
||||
const struct device **input_devices = kscan_gpio_input_devices(dev); \
|
||||
for (int i = 0; i < cfg->num_of_inputs; i++) { \
|
||||
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs(dev)[i]; \
|
||||
input_devices[i] = device_get_binding(in_cfg->label); \
|
||||
if (!input_devices[i]) { \
|
||||
LOG_ERR("Unable to find input GPIO device"); \
|
||||
return -EINVAL; \
|
||||
} \
|
||||
err = gpio_pin_configure(input_devices[i], in_cfg->pin, GPIO_INPUT | in_cfg->flags); \
|
||||
if (err) { \
|
||||
LOG_ERR("Unable to configure pin %d on %s for input", in_cfg->pin, in_cfg->label); \
|
||||
return err; \
|
||||
} \
|
||||
COND_CODE_0( \
|
||||
IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING), \
|
||||
(irq_callbacks_##n[i].work = &data->work; irq_callbacks_##n[i].dev = dev; \
|
||||
irq_callbacks_##n[i].debounce_period = cfg->debounce_period; \
|
||||
gpio_init_callback(&irq_callbacks_##n[i].callback, \
|
||||
kscan_gpio_irq_callback_handler, BIT(in_cfg->pin)); \
|
||||
err = gpio_add_callback(input_devices[i], &irq_callbacks_##n[i].callback); \
|
||||
if (err) { \
|
||||
LOG_ERR("Error adding the callback to the column device"); \
|
||||
return err; \
|
||||
}), \
|
||||
()) \
|
||||
} \
|
||||
data->dev = dev; \
|
||||
COND_CODE_1(IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING), \
|
||||
(k_timer_init(&data->poll_timer, kscan_gpio_timer_handler, NULL);), ()) \
|
||||
if (cfg->debounce_period > 0) { \
|
||||
k_work_init_delayable(&data->work.delayed, kscan_gpio_work_handler); \
|
||||
} else { \
|
||||
k_work_init(&data->work.direct, kscan_gpio_work_handler); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
static const struct kscan_gpio_config kscan_gpio_config_##n = { \
|
||||
.inputs = {UTIL_LISTIFY(INST_INPUT_LEN(n), KSCAN_DIRECT_INPUT_ITEM, n)}, \
|
||||
.num_of_inputs = INST_INPUT_LEN(n), \
|
||||
.debounce_period = DT_INST_PROP(n, debounce_period)}; \
|
||||
DEVICE_DT_INST_DEFINE(n, kscan_gpio_init_##n, NULL, &kscan_gpio_data_##n, \
|
||||
&kscan_gpio_config_##n, POST_KERNEL, CONFIG_ZMK_KSCAN_INIT_PRIORITY, \
|
||||
&gpio_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
|
||||
DT_INST_FOREACH_STATUS_OKAY(KSCAN_DIRECT_INIT);
|
||||
|
|
|
@ -418,46 +418,46 @@ static const struct kscan_driver_api kscan_matrix_api = {
|
|||
.disable_callback = kscan_matrix_disable,
|
||||
};
|
||||
|
||||
#define KSCAN_MATRIX_INIT(index) \
|
||||
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(index) <= DEBOUNCE_COUNTER_MAX, \
|
||||
#define KSCAN_MATRIX_INIT(n) \
|
||||
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(n) <= DEBOUNCE_COUNTER_MAX, \
|
||||
"ZMK_KSCAN_DEBOUNCE_PRESS_MS or debounce-press-ms is too large"); \
|
||||
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(index) <= DEBOUNCE_COUNTER_MAX, \
|
||||
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \
|
||||
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
|
||||
\
|
||||
static const struct gpio_dt_spec kscan_matrix_rows_##index[] = { \
|
||||
UTIL_LISTIFY(INST_ROWS_LEN(index), KSCAN_GPIO_ROW_CFG_INIT, index)}; \
|
||||
static const struct gpio_dt_spec kscan_matrix_rows_##n[] = { \
|
||||
UTIL_LISTIFY(INST_ROWS_LEN(n), KSCAN_GPIO_ROW_CFG_INIT, n)}; \
|
||||
\
|
||||
static const struct gpio_dt_spec kscan_matrix_cols_##index[] = { \
|
||||
UTIL_LISTIFY(INST_COLS_LEN(index), KSCAN_GPIO_COL_CFG_INIT, index)}; \
|
||||
static const struct gpio_dt_spec kscan_matrix_cols_##n[] = { \
|
||||
UTIL_LISTIFY(INST_COLS_LEN(n), KSCAN_GPIO_COL_CFG_INIT, n)}; \
|
||||
\
|
||||
static struct debounce_state kscan_matrix_state_##index[INST_MATRIX_LEN(index)]; \
|
||||
static struct debounce_state kscan_matrix_state_##n[INST_MATRIX_LEN(n)]; \
|
||||
\
|
||||
COND_INTERRUPTS((static struct kscan_matrix_irq_callback \
|
||||
kscan_matrix_irqs_##index[INST_INPUTS_LEN(index)];)) \
|
||||
COND_INTERRUPTS( \
|
||||
(static struct kscan_matrix_irq_callback kscan_matrix_irqs_##n[INST_INPUTS_LEN(n)];)) \
|
||||
\
|
||||
static struct kscan_matrix_data kscan_matrix_data_##index = { \
|
||||
.matrix_state = kscan_matrix_state_##index, \
|
||||
COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##index, ))}; \
|
||||
static struct kscan_matrix_data kscan_matrix_data_##n = { \
|
||||
.matrix_state = kscan_matrix_state_##n, \
|
||||
COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##n, ))}; \
|
||||
\
|
||||
static struct kscan_matrix_config kscan_matrix_config_##index = { \
|
||||
.rows = KSCAN_GPIO_LIST(kscan_matrix_rows_##index), \
|
||||
.cols = KSCAN_GPIO_LIST(kscan_matrix_cols_##index), \
|
||||
.inputs = KSCAN_GPIO_LIST( \
|
||||
COND_DIODE_DIR(index, (kscan_matrix_cols_##index), (kscan_matrix_rows_##index))), \
|
||||
.outputs = KSCAN_GPIO_LIST( \
|
||||
COND_DIODE_DIR(index, (kscan_matrix_rows_##index), (kscan_matrix_cols_##index))), \
|
||||
static struct kscan_matrix_config kscan_matrix_config_##n = { \
|
||||
.rows = KSCAN_GPIO_LIST(kscan_matrix_rows_##n), \
|
||||
.cols = KSCAN_GPIO_LIST(kscan_matrix_cols_##n), \
|
||||
.inputs = \
|
||||
KSCAN_GPIO_LIST(COND_DIODE_DIR(n, (kscan_matrix_cols_##n), (kscan_matrix_rows_##n))), \
|
||||
.outputs = \
|
||||
KSCAN_GPIO_LIST(COND_DIODE_DIR(n, (kscan_matrix_rows_##n), (kscan_matrix_cols_##n))), \
|
||||
.debounce_config = \
|
||||
{ \
|
||||
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(index), \
|
||||
.debounce_release_ms = INST_DEBOUNCE_RELEASE_MS(index), \
|
||||
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \
|
||||
.debounce_release_ms = INST_DEBOUNCE_RELEASE_MS(n), \
|
||||
}, \
|
||||
.debounce_scan_period_ms = DT_INST_PROP(index, debounce_scan_period_ms), \
|
||||
.poll_period_ms = DT_INST_PROP(index, poll_period_ms), \
|
||||
.diode_direction = INST_DIODE_DIR(index), \
|
||||
.debounce_scan_period_ms = DT_INST_PROP(n, debounce_scan_period_ms), \
|
||||
.poll_period_ms = DT_INST_PROP(n, poll_period_ms), \
|
||||
.diode_direction = INST_DIODE_DIR(n), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(index, &kscan_matrix_init, NULL, &kscan_matrix_data_##index, \
|
||||
&kscan_matrix_config_##index, APPLICATION, \
|
||||
CONFIG_APPLICATION_INIT_PRIORITY, &kscan_matrix_api);
|
||||
DEVICE_DT_INST_DEFINE(n, &kscan_matrix_init, NULL, &kscan_matrix_data_##n, \
|
||||
&kscan_matrix_config_##n, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, \
|
||||
&kscan_matrix_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KSCAN_MATRIX_INIT);
|
||||
|
|
|
@ -12,5 +12,26 @@ properties:
|
|||
type: phandle-array
|
||||
required: true
|
||||
debounce-period:
|
||||
type: int
|
||||
required: false
|
||||
deprecated: true
|
||||
description: Deprecated. Use debounce-press-ms and debounce-release-ms instead.
|
||||
debounce-press-ms:
|
||||
type: int
|
||||
default: 5
|
||||
description: Debounce time for key press in milliseconds. Use 0 for eager debouncing.
|
||||
debounce-release-ms:
|
||||
type: int
|
||||
default: 5
|
||||
description: Debounce time for key release in milliseconds.
|
||||
debounce-scan-period-ms:
|
||||
type: int
|
||||
default: 1
|
||||
description: Time between reads in milliseconds when any key is pressed.
|
||||
poll-period-ms:
|
||||
type: int
|
||||
default: 10
|
||||
description: Time between reads in milliseconds when no key is pressed and ZMK_KSCAN_DIRECT_POLLING is enabled.
|
||||
toggle-mode:
|
||||
type: boolean
|
||||
description: Enable toggle-switch mode.
|
||||
|
|
|
@ -930,7 +930,7 @@
|
|||
|
||||
/* Consumer Closed Caption */
|
||||
#define C_CAPTIONS (ZMK_HID_USAGE(HID_USAGE_CONSUMER, HID_USAGE_CONSUMER_CLOSED_CAPTION))
|
||||
#define C_SUBTITILES (C_CAPTIONS)
|
||||
#define C_SUBTITLES (C_CAPTIONS)
|
||||
|
||||
/* Consumer Snapshot */
|
||||
#define C_SNAPSHOT (ZMK_HID_USAGE(HID_USAGE_CONSUMER, HID_USAGE_CONSUMER_SNAPSHOT))
|
||||
|
|
19
app/include/zmk/display/widgets/peripheral_status.h
Normal file
19
app/include/zmk/display/widgets/peripheral_status.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <kernel.h>
|
||||
|
||||
struct zmk_widget_peripheral_status {
|
||||
sys_snode_t node;
|
||||
lv_obj_t *obj;
|
||||
};
|
||||
|
||||
int zmk_widget_peripheral_status_init(struct zmk_widget_peripheral_status *widget,
|
||||
lv_obj_t *parent);
|
||||
lv_obj_t *zmk_widget_peripheral_status_obj(struct zmk_widget_peripheral_status *widget);
|
16
app/include/zmk/events/split_peripheral_status_changed.h
Normal file
16
app/include/zmk/events/split_peripheral_status_changed.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <zmk/event_manager.h>
|
||||
|
||||
struct zmk_split_peripheral_status_changed {
|
||||
bool connected;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(zmk_split_peripheral_status_changed);
|
9
app/include/zmk/split/bluetooth/peripheral.h
Normal file
9
app/include/zmk/split/bluetooth/peripheral.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
bool zmk_split_bt_peripheral_is_connected(void);
|
|
@ -68,9 +68,9 @@ void activity_work_handler(struct k_work *work) {
|
|||
int32_t inactive_time = current - activity_last_uptime;
|
||||
#if IS_ENABLED(CONFIG_ZMK_SLEEP)
|
||||
if (inactive_time > MAX_SLEEP_MS && !is_usb_power_present()) {
|
||||
// Put devices in low power mode before sleeping
|
||||
// Put devices in suspend power mode before sleeping
|
||||
set_state(ZMK_ACTIVITY_SLEEP);
|
||||
pm_power_state_set((struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
|
||||
pm_power_state_force(0U, (struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
|
||||
} else
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SLEEP) */
|
||||
if (inactive_time > MAX_IDLE_MS) {
|
||||
|
|
|
@ -104,7 +104,7 @@ static int zmk_battery_init(const struct device *_arg) {
|
|||
return rc;
|
||||
}
|
||||
|
||||
k_timer_start(&battery_timer, K_MINUTES(1), K_MINUTES(1));
|
||||
k_timer_start(&battery_timer, K_MINUTES(1), K_SECONDS(CONFIG_ZMK_BATTERY_REPORT_INTERVAL));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -36,14 +36,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/ble_active_profile_changed.h>
|
||||
|
||||
#define IS_HOST_PERIPHERAL \
|
||||
(!IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
|
||||
#define IS_SPLIT_PERIPHERAL \
|
||||
(IS_ENABLED(CONFIG_ZMK_SPLIT) && !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
|
||||
|
||||
#define DO_PASSKEY_ENTRY (IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY) && !IS_SPLIT_PERIPHERAL)
|
||||
|
||||
#if DO_PASSKEY_ENTRY
|
||||
#if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY)
|
||||
#include <zmk/events/keycode_state_changed.h>
|
||||
|
||||
#define PASSKEY_DIGITS 6
|
||||
|
@ -52,7 +45,7 @@ static struct bt_conn *auth_passkey_entry_conn;
|
|||
static uint8_t passkey_entries[PASSKEY_DIGITS] = {};
|
||||
static uint8_t passkey_digit = 0;
|
||||
|
||||
#endif
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY) */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
#define PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - 1)
|
||||
|
@ -81,20 +74,12 @@ static uint8_t active_profile;
|
|||
BUILD_ASSERT(DEVICE_NAME_LEN <= 16, "ERROR: BLE device name is too long. Max length: 16");
|
||||
|
||||
static const struct bt_data zmk_ble_ad[] = {
|
||||
#if IS_HOST_PERIPHERAL
|
||||
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
|
||||
BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE, 0xC1, 0x03),
|
||||
#endif
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
BT_DATA_BYTES(BT_DATA_UUID16_SOME,
|
||||
#if IS_HOST_PERIPHERAL
|
||||
0x12, 0x18, /* HID Service */
|
||||
#endif
|
||||
0x0f, 0x18 /* Battery Service */
|
||||
BT_DATA_BYTES(BT_DATA_UUID16_SOME, 0x12, 0x18, /* HID Service */
|
||||
0x0f, 0x18 /* Battery Service */
|
||||
),
|
||||
#if IS_SPLIT_PERIPHERAL
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL, ZMK_SPLIT_BT_SERVICE_UUID)
|
||||
#endif
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
|
@ -473,7 +458,7 @@ static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey) {
|
|||
}
|
||||
*/
|
||||
|
||||
#if DO_PASSKEY_ENTRY
|
||||
#if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY)
|
||||
|
||||
static void auth_passkey_entry(struct bt_conn *conn) {
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
@ -492,7 +477,7 @@ static void auth_cancel(struct bt_conn *conn) {
|
|||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
#if DO_PASSKEY_ENTRY
|
||||
#if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY)
|
||||
if (auth_passkey_entry_conn) {
|
||||
bt_conn_unref(auth_passkey_entry_conn);
|
||||
auth_passkey_entry_conn = NULL;
|
||||
|
@ -504,7 +489,6 @@ static void auth_cancel(struct bt_conn *conn) {
|
|||
LOG_DBG("Pairing cancelled: %s", log_strdup(addr));
|
||||
}
|
||||
|
||||
#if IS_HOST_PERIPHERAL
|
||||
static enum bt_security_err auth_pairing_accept(struct bt_conn *conn,
|
||||
const struct bt_conn_pairing_feat *const feat) {
|
||||
struct bt_conn_info info;
|
||||
|
@ -518,7 +502,6 @@ static enum bt_security_err auth_pairing_accept(struct bt_conn *conn,
|
|||
|
||||
return BT_SECURITY_ERR_SUCCESS;
|
||||
};
|
||||
#endif /* IS_HOST_PERIPHERAL */
|
||||
|
||||
static void auth_pairing_complete(struct bt_conn *conn, bool bonded) {
|
||||
struct bt_conn_info info;
|
||||
|
@ -533,26 +516,22 @@ static void auth_pairing_complete(struct bt_conn *conn, bool bonded) {
|
|||
return;
|
||||
}
|
||||
|
||||
#if IS_HOST_PERIPHERAL
|
||||
if (!zmk_ble_active_profile_is_open()) {
|
||||
LOG_ERR("Pairing completed but current profile is not open: %s", log_strdup(addr));
|
||||
bt_unpair(BT_ID_DEFAULT, dst);
|
||||
return;
|
||||
}
|
||||
#endif /* IS_HOST_PERIPHERAL */
|
||||
|
||||
set_profile_address(active_profile, dst);
|
||||
update_advertising();
|
||||
};
|
||||
|
||||
static struct bt_conn_auth_cb zmk_ble_auth_cb_display = {
|
||||
#if IS_HOST_PERIPHERAL
|
||||
.pairing_accept = auth_pairing_accept,
|
||||
#endif /* IS_HOST_PERIPHERAL */
|
||||
.pairing_complete = auth_pairing_complete,
|
||||
// .passkey_display = auth_passkey_display,
|
||||
|
||||
#if DO_PASSKEY_ENTRY
|
||||
#if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY)
|
||||
.passkey_entry = auth_passkey_entry,
|
||||
#endif
|
||||
.cancel = auth_cancel,
|
||||
|
@ -595,9 +574,7 @@ static int zmk_ble_init(const struct device *_arg) {
|
|||
#if IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START)
|
||||
LOG_WRN("Clearing all existing BLE bond information from the keyboard");
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
bt_unpair(i, NULL);
|
||||
}
|
||||
bt_unpair(BT_ID_DEFAULT, NULL);
|
||||
|
||||
for (int i = 0; i < ZMK_BLE_PROFILE_COUNT; i++) {
|
||||
char setting_name[15];
|
||||
|
@ -618,21 +595,7 @@ static int zmk_ble_init(const struct device *_arg) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int zmk_ble_unpair_all() {
|
||||
int resp = 0;
|
||||
for (int i = BT_ID_DEFAULT; i < CONFIG_BT_ID_MAX; i++) {
|
||||
|
||||
int err = bt_unpair(BT_ID_DEFAULT, NULL);
|
||||
if (err) {
|
||||
resp = err;
|
||||
LOG_ERR("Failed to unpair devices (err %d)", err);
|
||||
}
|
||||
}
|
||||
|
||||
return resp;
|
||||
};
|
||||
|
||||
#if DO_PASSKEY_ENTRY
|
||||
#if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY)
|
||||
|
||||
static bool zmk_ble_numeric_usage_to_value(const zmk_key_t key, const zmk_key_t one,
|
||||
const zmk_key_t zero, uint32_t *value) {
|
||||
|
@ -705,6 +668,6 @@ static int zmk_ble_listener(const zmk_event_t *eh) {
|
|||
|
||||
ZMK_LISTENER(zmk_ble, zmk_ble_listener);
|
||||
ZMK_SUBSCRIPTION(zmk_ble, zmk_keycode_state_changed);
|
||||
#endif /* DO_PASSKEY_ENTRY */
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY) */
|
||||
|
||||
SYS_INIT(zmk_ble_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <zmk/display/widgets/output_status.h>
|
||||
#include <zmk/display/widgets/peripheral_status.h>
|
||||
#include <zmk/display/widgets/battery_status.h>
|
||||
#include <zmk/display/widgets/layer_status.h>
|
||||
#include <zmk/display/widgets/wpm_status.h>
|
||||
|
@ -21,6 +22,10 @@ static struct zmk_widget_battery_status battery_status_widget;
|
|||
static struct zmk_widget_output_status output_status_widget;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS)
|
||||
static struct zmk_widget_peripheral_status peripheral_status_widget;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_WIDGET_LAYER_STATUS)
|
||||
static struct zmk_widget_layer_status layer_status_widget;
|
||||
#endif
|
||||
|
@ -46,6 +51,12 @@ lv_obj_t *zmk_display_status_screen() {
|
|||
0);
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS)
|
||||
zmk_widget_peripheral_status_init(&peripheral_status_widget, screen);
|
||||
lv_obj_align(zmk_widget_peripheral_status_obj(&peripheral_status_widget), NULL,
|
||||
LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_WIDGET_LAYER_STATUS)
|
||||
zmk_widget_layer_status_init(&layer_status_widget, screen);
|
||||
lv_obj_set_style_local_text_font(zmk_widget_layer_status_obj(&layer_status_widget),
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
|
||||
target_sources_ifdef(CONFIG_ZMK_WIDGET_BATTERY_STATUS app PRIVATE battery_status.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_WIDGET_OUTPUT_STATUS app PRIVATE output_status.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS app PRIVATE peripheral_status.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_WIDGET_LAYER_STATUS app PRIVATE layer_status.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_WIDGET_WPM_STATUS app PRIVATE wpm_status.c)
|
||||
|
|
|
@ -17,8 +17,14 @@ config ZMK_WIDGET_BATTERY_STATUS
|
|||
|
||||
config ZMK_WIDGET_OUTPUT_STATUS
|
||||
bool "Widget for keyboard output status icons"
|
||||
depends on BT
|
||||
default y if BT
|
||||
depends on BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
default y if BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
select LVGL_USE_LABEL
|
||||
|
||||
config ZMK_WIDGET_PERIPHERAL_STATUS
|
||||
bool "Widget for split peripheral status icons"
|
||||
depends on BT && ZMK_SPLIT_BLE && !ZMK_SPLIT_BLE_ROLE_CENTRAL
|
||||
default y if BT && ZMK_SPLIT_BLE && !ZMK_SPLIT_BLE_ROLE_CENTRAL
|
||||
select LVGL_USE_LABEL
|
||||
|
||||
config ZMK_WIDGET_WPM_STATUS
|
||||
|
|
60
app/src/display/widgets/peripheral_status.c
Normal file
60
app/src/display/widgets/peripheral_status.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <bluetooth/services/bas.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/display.h>
|
||||
#include <zmk/display/widgets/peripheral_status.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/split/bluetooth/peripheral.h>
|
||||
#include <zmk/events/split_peripheral_status_changed.h>
|
||||
|
||||
static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets);
|
||||
|
||||
struct peripheral_status_state {
|
||||
bool connected;
|
||||
};
|
||||
|
||||
static struct peripheral_status_state get_state(const zmk_event_t *_eh) {
|
||||
return (struct peripheral_status_state){.connected = zmk_split_bt_peripheral_is_connected()};
|
||||
}
|
||||
|
||||
static void set_status_symbol(lv_obj_t *label, struct peripheral_status_state state) {
|
||||
const char *text =
|
||||
state.connected ? (LV_SYMBOL_WIFI " " LV_SYMBOL_OK) : (LV_SYMBOL_WIFI " " LV_SYMBOL_CLOSE);
|
||||
|
||||
LOG_DBG("connected? %s", state.connected ? "true" : "false");
|
||||
lv_label_set_text(label, text);
|
||||
}
|
||||
|
||||
static void output_status_update_cb(struct peripheral_status_state state) {
|
||||
struct zmk_widget_peripheral_status *widget;
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_status_symbol(widget->obj, state); }
|
||||
}
|
||||
|
||||
ZMK_DISPLAY_WIDGET_LISTENER(widget_peripheral_status, struct peripheral_status_state,
|
||||
output_status_update_cb, get_state)
|
||||
ZMK_SUBSCRIPTION(widget_peripheral_status, zmk_split_peripheral_status_changed);
|
||||
|
||||
int zmk_widget_peripheral_status_init(struct zmk_widget_peripheral_status *widget,
|
||||
lv_obj_t *parent) {
|
||||
widget->obj = lv_label_create(parent, NULL);
|
||||
|
||||
lv_obj_set_size(widget->obj, 40, 15);
|
||||
|
||||
sys_slist_append(&widgets, &widget->node);
|
||||
|
||||
widget_peripheral_status_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
lv_obj_t *zmk_widget_peripheral_status_obj(struct zmk_widget_peripheral_status *widget) {
|
||||
return widget->obj;
|
||||
}
|
10
app/src/events/split_peripheral_status_changed.c
Normal file
10
app/src/events/split_peripheral_status_changed.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <zmk/events/split_peripheral_status_changed.h>
|
||||
|
||||
ZMK_EVENT_IMPL(zmk_split_peripheral_status_changed);
|
|
@ -176,10 +176,10 @@ static int ext_power_generic_init(const struct device *dev) {
|
|||
#ifdef CONFIG_PM_DEVICE
|
||||
static int ext_power_generic_pm_action(const struct device *dev, enum pm_device_action action) {
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_TURN_ON:
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
ext_power_generic_enable(dev);
|
||||
return 0;
|
||||
case PM_DEVICE_ACTION_TURN_OFF:
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
ext_power_generic_disable(dev);
|
||||
return 0;
|
||||
default:
|
||||
|
|
128
app/src/split/bluetooth/peripheral.c
Normal file
128
app/src/split/bluetooth/peripheral.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <device.h>
|
||||
#include <init.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <settings/settings.h>
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/conn.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/uuid.h>
|
||||
#include <bluetooth/gatt.h>
|
||||
#include <bluetooth/hci_err.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
|
||||
#include <settings/settings.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/split_peripheral_status_changed.h>
|
||||
#include <zmk/ble.h>
|
||||
#include <zmk/split/bluetooth/uuid.h>
|
||||
|
||||
static const struct bt_data zmk_ble_ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
BT_DATA_BYTES(BT_DATA_UUID16_SOME, 0x0f, 0x18 /* Battery Service */
|
||||
),
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL, ZMK_SPLIT_BT_SERVICE_UUID)};
|
||||
|
||||
static bool is_connected = false;
|
||||
|
||||
static int start_advertising() {
|
||||
return bt_le_adv_start(BT_LE_ADV_CONN, zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad), NULL, 0);
|
||||
};
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t err) {
|
||||
is_connected = (err == 0);
|
||||
|
||||
ZMK_EVENT_RAISE(new_zmk_split_peripheral_status_changed(
|
||||
(struct zmk_split_peripheral_status_changed){.connected = is_connected}));
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason) {
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
LOG_DBG("Disconnected from %s (reason 0x%02x)", log_strdup(addr), reason);
|
||||
|
||||
is_connected = false;
|
||||
|
||||
ZMK_EVENT_RAISE(new_zmk_split_peripheral_status_changed(
|
||||
(struct zmk_split_peripheral_status_changed){.connected = is_connected}));
|
||||
}
|
||||
|
||||
static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) {
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
if (!err) {
|
||||
LOG_DBG("Security changed: %s level %u", log_strdup(addr), level);
|
||||
} else {
|
||||
LOG_ERR("Security failed: %s level %u err %d", log_strdup(addr), level, err);
|
||||
}
|
||||
}
|
||||
|
||||
static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency,
|
||||
uint16_t timeout) {
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
LOG_DBG("%s: interval %d latency %d timeout %d", log_strdup(addr), interval, latency, timeout);
|
||||
}
|
||||
|
||||
static struct bt_conn_cb conn_callbacks = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
.security_changed = security_changed,
|
||||
.le_param_updated = le_param_updated,
|
||||
};
|
||||
|
||||
bool zmk_split_bt_peripheral_is_connected() { return is_connected; }
|
||||
|
||||
static int zmk_peripheral_ble_init(const struct device *_arg) {
|
||||
int err = bt_enable(NULL);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("BLUETOOTH FAILED (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
settings_subsys_init();
|
||||
|
||||
settings_load_subtree("ble");
|
||||
settings_load_subtree("bt");
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START)
|
||||
LOG_WRN("Clearing all existing BLE bond information from the keyboard");
|
||||
|
||||
bt_unpair(BT_ID_DEFAULT, NULL);
|
||||
#endif
|
||||
|
||||
bt_conn_cb_register(&conn_callbacks);
|
||||
|
||||
start_advertising();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(zmk_peripheral_ble_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY);
|
|
@ -10,10 +10,10 @@ kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
|||
ht_binding_released: 0 cleaning up hold-tap
|
||||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided hold-timer (balanced decision moment timer)
|
||||
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
||||
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided hold-timer (balanced decision moment timer)
|
||||
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_pressed: 1 new undecided hold_tap
|
||||
ht_decide: 1 decided hold-timer (balanced decision moment timer)
|
||||
kp_pressed: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
||||
kp_released: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 1 cleaning up hold-tap
|
||||
|
|
|
@ -10,10 +10,10 @@ kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
|||
ht_binding_released: 0 cleaning up hold-tap
|
||||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided hold-interrupt (hold-preferred decision moment other-key-down)
|
||||
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
||||
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided hold-timer (hold-preferred decision moment timer)
|
||||
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_pressed: 1 new undecided hold_tap
|
||||
ht_decide: 1 decided hold-timer (hold-preferred decision moment timer)
|
||||
kp_pressed: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
||||
kp_released: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 1 cleaning up hold-tap
|
||||
|
|
|
@ -10,10 +10,10 @@ kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
|||
ht_binding_released: 0 cleaning up hold-tap
|
||||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided hold-timer (tap-preferred decision moment timer)
|
||||
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
||||
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_decide: 0 decided hold-timer (tap-preferred decision moment timer)
|
||||
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_pressed: 1 new undecided hold_tap
|
||||
ht_decide: 1 decided hold-timer (tap-preferred decision moment timer)
|
||||
kp_pressed: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
||||
kp_released: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 1 cleaning up hold-tap
|
||||
|
|
|
@ -3,10 +3,10 @@ ht_decide: 0 decided tap (tap-unless-interrupted decision moment timer)
|
|||
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_pressed: 1 new undecided hold_tap
|
||||
ht_decide: 1 decided tap (tap-unless-interrupted decision moment timer)
|
||||
kp_pressed: usage_page 0x07 keycode 0x0d implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x0D implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
||||
kp_released: usage_page 0x07 keycode 0x0d implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x0D implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 1 cleaning up hold-tap
|
||||
|
|
479
docs/docs/development/new-behavior.md
Normal file
479
docs/docs/development/new-behavior.md
Normal file
|
@ -0,0 +1,479 @@
|
|||
---
|
||||
title: New Behavior
|
||||
sidebar_label: New Behavior
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines how to develop a behavior for ZMK and prepare the changes for a pull request.
|
||||
|
||||
Behaviors are assigned to key positions and determine what happens when they are pressed and released. They are implemented in Zephyr as "devices": they consist of a devicetree binding file, which specifies the properties of the behavior, and a driver written in C code. This allows for the ability to create unique instances of these behaviors in [keymaps](../features/keymaps.md) or devicetree-source-include files (`.dtsi`). While instances of behaviors stored in keymaps are created by end-users for their personal needs, the instances that live in the .dtsi files are stored and documented in ZMK directly, which removes the need for end-users to set up common use-cases of these behaviors in their personal keymaps.
|
||||
|
||||
The general process for developing behaviors is:
|
||||
|
||||
1. [Create the behavior](#creating-the-behavior)
|
||||
1. [Create the devicetree binding (`.yaml`)](#creating-the-devicetree-binding-yaml)
|
||||
1. [Create the driver (`.c`)](#creating-the-driver-c)
|
||||
1. [Update `app/CmakeLists.txt` to include the new driver](#updating-appcmakeliststxt-to-include-the-new-driver)
|
||||
1. [Define common use-cases for the behavior (`.dtsi`) (Optional)](#defining-common-use-cases-for-the-behavior-dtsi-optional)
|
||||
1. [Test changes locally](#testing-changes-locally)
|
||||
1. [Document behavior functionality](#documenting-behavior-functionality)
|
||||
1. [Create a pull request for review and inclusion into the ZMK sources](#submitting-a-pull-request)
|
||||
|
||||
:::info
|
||||
Before developing new behaviors, developers should have a working knowledge of the Embedded Linux Devicetree.
|
||||
The following resources are provided for those seeking further understanding:
|
||||
|
||||
- [Embedded Linux Wiki - Device Tree Usage](https://elinux.org/Device_Tree_Usage)
|
||||
- [Zephyr Devicetree API](https://docs.zephyrproject.org/latest/build/dts/api/api.html)
|
||||
- [Zephyr Device Driver Model](https://docs.zephyrproject.org/latest/kernel/drivers/index.html)
|
||||
|
||||
:::
|
||||
|
||||
## Creating the Behavior
|
||||
|
||||
### Creating the devicetree binding (`.yaml`)
|
||||
|
||||
The properties of the behavior are listed in the behavior's devicetree binding, which comes in the form of a `.yaml` file. Devicetree bindings are stored in the directory `app/dts/bindings/behaviors/` and are labelled in lowercase, beginning with the prefix `zmk,behavior-`, and ending with the behavior's name, using dashes to separate multiple words. For example, the directory for the hold-tap's devicetree binding would be located at `app/dts/bindings/behaviors/zmk,behavior-hold-tap.yaml`, which is shown below as a reference:
|
||||
|
||||
```yaml title="app/dts/bindings/behaviors/zmk,behavior-hold-tap.yaml"
|
||||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
// highlight-next-line
|
||||
description: Hold or Tap behavior
|
||||
|
||||
// highlight-next-line
|
||||
compatible: "zmk,behavior-hold-tap"
|
||||
|
||||
// highlight-next-line
|
||||
include: two_param.yaml
|
||||
|
||||
// highlight-next-line
|
||||
properties:
|
||||
bindings:
|
||||
type: phandles
|
||||
required: true
|
||||
tapping-term-ms:
|
||||
type: int
|
||||
tapping_term_ms: # deprecated
|
||||
type: int
|
||||
quick-tap-ms:
|
||||
type: int
|
||||
default: -1
|
||||
quick_tap_ms: # deprecated
|
||||
type: int
|
||||
flavor:
|
||||
type: string
|
||||
required: false
|
||||
default: "hold-preferred"
|
||||
enum:
|
||||
- "hold-preferred"
|
||||
- "balanced"
|
||||
- "tap-preferred"
|
||||
- "tap-unless-interrupted"
|
||||
retro-tap:
|
||||
type: boolean
|
||||
hold-trigger-key-positions:
|
||||
type: array
|
||||
required: false
|
||||
default: []
|
||||
```
|
||||
|
||||
We see that the `.yaml` files used for new behaviors' devicetree bindings consist of the following properties:
|
||||
|
||||
#### `description`
|
||||
|
||||
A brief statement of what the behavior is. The value of this property is not seen by end-users; as such, the `description` value should be kept less than a sentence long, leaving explanations for end-users of how the behavior works for its documentation.
|
||||
|
||||
#### `compatible`
|
||||
|
||||
Allows ZMK to assign the correct driver to the behavior extracted from the keymap or `.dtsi`. The value of the `compatible` property is equal to the name of the [devicetree binding file](#creating-the-devicetree-binding-yaml) as a `string`.
|
||||
|
||||
As shown in the example above, `compatible: "zmk,behavior-hold-tap"` is the value of the `compatible` property of `zmk,behavior-hold-tap.yaml`.
|
||||
|
||||
#### `include`
|
||||
|
||||
Choose between `zero_param.yaml`, `one_param.yaml`, or `two_param.yaml` depending on how many additional parameters are required to complete the behavior's binding in a keymap. For example, we `include: two_param.yaml` in `zmk,behavior-hold-tap.yaml` because any user-defined or pre-defined instances of the hold-tap behavior take in two cells as inputs: one for the hold behavior and one for the tap behavior.
|
||||
|
||||
#### `properties` (Optional)
|
||||
|
||||
These are additional variables required to configure a particular instance of a behavior. `properties` can be of the following types:
|
||||
|
||||
- `path`
|
||||
- `compound`
|
||||
- `array`
|
||||
- `string`
|
||||
- `string-array`
|
||||
- `boolean`
|
||||
- `int`
|
||||
- `uint8-array`
|
||||
- `phandle`.
|
||||
- `phandle-array`
|
||||
- `phandles`
|
||||
|
||||
:::info
|
||||
For more information on additional `properties`, refer to [Zephyr's documentation on Devicetree bindings](https://docs.zephyrproject.org/latest/build/dts/bindings.html#properties).
|
||||
:::
|
||||
|
||||
### Creating the driver (`.c`)
|
||||
|
||||
:::info
|
||||
Developing drivers for behaviors in ZMK makes extensive use of the Zephyr Devicetree API and Device Driver Model. Links to the Zephyr Project Documentation for both of these concepts can be found below:
|
||||
|
||||
- [Zephyr Devicetree API](https://docs.zephyrproject.org/latest/build/dts/api/api.html)
|
||||
- [Zephyr Device Driver Model](https://docs.zephyrproject.org/latest/kernel/drivers/index.html)
|
||||
|
||||
:::
|
||||
|
||||
Driver files are stored in `app/src/behaviors/` and are labelled in lowercase, beginning with the prefix `behavior_`, and ending with the behavior's name, using underscores to separate multiple words. For example, the directory for the hold-tap's driver would be located at `app/src/behaviors/behavior_hold_tap.c`.
|
||||
|
||||
The code snippet below shows the essential components of a new driver.
|
||||
|
||||
```c
|
||||
#define DT_DRV_COMPAT zmk_<behavior_name>
|
||||
|
||||
// Dependencies
|
||||
#include <device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#include <zmk/behavior.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
// Instance-Unique Data Struct (Optional)
|
||||
struct behavior_<behavior_name>_data {
|
||||
bool example_data_param1;
|
||||
bool example_data_param2;
|
||||
bool example_data_param3;
|
||||
};
|
||||
|
||||
// Instance-Unique Config Struct (Optional)
|
||||
struct behavior_<behavior_name>_config {
|
||||
bool example_config_param1;
|
||||
bool example_config_param2;
|
||||
bool example_config_param3;
|
||||
};
|
||||
|
||||
// Initialization Function
|
||||
static int <behavior_name>_init(const struct device *dev) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// API Structure
|
||||
static const struct behavior_driver_api <behavior_name>_driver_api = {
|
||||
|
||||
};
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, // Instance Number (Equal to 0 for behaviors that don't require multiple instances,
|
||||
// Equal to n for behaviors that do make use of multiple instances)
|
||||
<behavior_name>_init, NULL, // Initialization Function, Power Management Device Pointer
|
||||
&<behavior_name>_data, &<behavior_name>_config, // Behavior Data Pointer, Behavior Configuration Pointer (Both Optional)
|
||||
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, // Initialization Level, Device Priority
|
||||
&<behavior_name>_driver_api); // API Structure
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
||||
```
|
||||
|
||||
#### `DT_DRV_COMPAT`
|
||||
|
||||
Replace `zmk_<behavior_name>` in the `#define DT_DRV_COMPAT` statement with the name of your behavior. (e.g. `zmk_behavior_caps_word`)
|
||||
|
||||
#### Dependencies
|
||||
|
||||
The dependencies required for any ZMK behavior are:
|
||||
|
||||
- `device.h`: [Zephyr Device APIs](https://docs.zephyrproject.org/apidoc/latest/group__device__model.html)
|
||||
- `drivers/behavior.h`: ZMK Behavior Functions (e.g. [`locality`](#api-structure), `behavior_keymap_binding_pressed`, `behavior_keymap_binding_released`, `behavior_sensor_keymap_binding_triggered`)
|
||||
- `logging/log.h`: [Zephyr Logging APIs](https://docs.zephyrproject.org/latest/services/logging/index.html) (for more information on USB Logging in ZMK, see [USB Logging](usb-logging.md)).
|
||||
- `zmk/behavior.h`: ZMK Behavior Information (e.g. parameters, position and timestamp of events)
|
||||
- `return` values:
|
||||
- `ZMK_BEHAVIOR_OPAQUE`: Used to terminate `on_<behavior_name>_binding_pressed` and `on_<behavior_name>_binding_released` functions that accept `(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event)` as parameters
|
||||
- `ZMK_BEHAVIOR_TRANSPARENT`: Used in the `binding_pressed` and `binding_released` functions for the transparent (`&trans`) behavior
|
||||
- `struct`s:
|
||||
- `zmk_behavior_binding`: Stores the name of the behavior device (`char *behavior_dev`) as a `string` and up to two additional parameters (`uint32_t param1`, `uint32_t param2`)
|
||||
- `zmk_behavior_binding_event`: Contains layer, position, and timestamp data for an active `zmk_behavior_binding`
|
||||
|
||||
Other common dependencies include `zmk/keymap.h`, which allows behaviors to access layer information and extract behavior bindings from keymaps, and `zmk/event_manager.h` which is detailed below.
|
||||
|
||||
##### ZMK Event Manager
|
||||
|
||||
Including `zmk/event_manager.h` is required for the following dependencies to function properly.
|
||||
|
||||
- `zmk/events/position_state_changed.h`: Position events' state (on/off), source, position, and timestamps
|
||||
- `zmk/events/keycode_state_changed.h`: Keycode events' state (on/off), usage page, keycode value, modifiers, and timestamps
|
||||
- `zmk/events/modifiers_state_changed.h`: Modifier events' state (on/off) and modifier value
|
||||
|
||||
Events can be used similarly to hardware interrupts, through the use of [listeners](#listeners-and-subscriptions).
|
||||
|
||||
###### Listeners and Subscriptions
|
||||
|
||||
The condensed form of lines 192-225 of the tap-dance driver, shown below, does an excellent job of showcasing the function of listeners and subscriptions with respect to the [ZMK Event Manager](#zmk-event-manager).
|
||||
|
||||
```c title="app/src/behaviors/behavior_tap_dance.c (Lines 192-197, 225)"
|
||||
static int tap_dance_position_state_changed_listener(const zmk_event_t *eh);
|
||||
|
||||
ZMK_LISTENER(behavior_tap_dance, tap_dance_position_state_changed_listener);
|
||||
ZMK_SUBSCRIPTION(behavior_tap_dance, zmk_position_state_changed);
|
||||
|
||||
static int tap_dance_position_state_changed_listener(const zmk_event_t *eh){
|
||||
// Do stuff...
|
||||
}
|
||||
```
|
||||
|
||||
Listeners, defined by the `ZMK_LISTENER(mod, cb)` function, take in a listener name (`mod`) and a callback function (`cb`) as their parameters. On the other hand subscriptions are defined by the `ZMK_SUBSCRIPTION(mod, ev_type)`, and determine what kind of event (`ev_type`) should invoke the callback function from the listener. In the tap-dance example, this listener executes code depending on a `zmk_position_state_changed` event, or simply, a change in key position. Other types of ZMK events can be found as the name of the `struct` inside each of the files located at `app/include/zmk/events/<Event Type>.h`. All control paths in a listener should `return` one of the [`ZMK_EV_EVENT_*` values](#return-values), which are shown below.
|
||||
|
||||
###### `return` values:
|
||||
|
||||
- `ZMK_EV_EVENT_BUBBLE`: Keep propagating the event `struct` to the next listener.
|
||||
- `ZMK_EV_EVENT_HANDLED`: Stop propagating the event `struct` to the next listener. The event manager still owns the `struct`'s memory, so it will be `free`d automatically. Do **not** free the memory in this function.
|
||||
- `ZMK_EV_EVENT_CAPTURED`: Stop propagating the event `struct` to the next listener. The event `struct`'s memory is now owned by your code, so the event manager will not free the event `struct` memory. Make sure your code will release or free the event at some point in the future. (Use the [`ZMK_EVENT_*` macros](#macros) described below.)
|
||||
|
||||
###### Macros:
|
||||
|
||||
- `ZMK_EVENT_RAISE(ev)`: Start handling this event (`ev`) with the first registered event listener.
|
||||
- `ZMK_EVENT_RAISE_AFTER(ev, mod)`: Start handling this event (`ev`) after the event is captured by the named [event listener](#listeners-and-subscriptions) (`mod`). The named event listener will be skipped as well.
|
||||
- `ZMK_EVENT_RAISE_AT(ev, mod)`: Start handling this event (`ev`) at the named [event listener](#listeners-and-subscriptions) (`mod`). The named event listener is the first handler to be invoked.
|
||||
- `ZMK_EVENT_RELEASE(ev)`: Continue handling this event (`ev`) at the next registered event listener.
|
||||
- `ZMK_EVENT_FREE(ev)`: Free the memory associated with the event (`ev`).
|
||||
|
||||
#### `DEVICE_DT_INST_DEFINE`
|
||||
|
||||
:::info
|
||||
For more information on this function, refer to [Zephyr's documentation on the Device Driver Model](https://docs.zephyrproject.org/latest/kernel/drivers/index.html#c.DEVICE_DT_INST_DEFINE).
|
||||
:::
|
||||
|
||||
The example `DEVICE_DT_INST_DEFINE` call can be left as is with the first parameter, the instance number, equal to `0` for behaviors that only require a single instance (e.g. external power, backlighting, accessing layers). For behaviors that can have multiple instances (e.g. hold-taps, tap-dances, sticky-keys), `DEVICE_DT_INST_DEFINE` can be placed inside a `#define` statement, usually formatted as `#define <ABBREVIATED BEHAVIOR NAME>_INST(n)`, that sets up any [data pointers](#data-pointers-optional) and/or [configuration pointers](#configuration-pointers-optional) that are unique to each instance.
|
||||
|
||||
An example of this can be seen below, taking the `#define KP_INST(n)` from the hold-tap driver.
|
||||
|
||||
```c
|
||||
#define KP_INST(n) \
|
||||
static struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \
|
||||
.tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \
|
||||
.hold_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 0)), \
|
||||
.tap_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 1)), \
|
||||
.quick_tap_ms = DT_INST_PROP(n, quick_tap_ms), \
|
||||
.flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \
|
||||
.retro_tap = DT_INST_PROP(n, retro_tap), \
|
||||
.hold_trigger_key_positions = DT_INST_PROP(n, hold_trigger_key_positions), \
|
||||
.hold_trigger_key_positions_len = DT_INST_PROP_LEN(n, hold_trigger_key_positions), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, behavior_hold_tap_init, NULL, NULL, &behavior_hold_tap_config_##n, \
|
||||
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&behavior_hold_tap_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||
```
|
||||
|
||||
Note that in the hold-tap example, the instance number, `0`, has been replaced by `n`, signifying the unique `node_id` of each instance of a behavior. Furthermore, the DT_INST_FOREACH_STATUS_OKAY(KP_INST) macro iterates through each compatible, non-disabled devicetree node, creating and applying the proper values to any instance-specific configurations or data by invoking the KP_INST macro for each instance of the new behavior.
|
||||
|
||||
Behaviors also require the following parameters of `DEVICE_DT_INST_DEFINE` to be changed:
|
||||
|
||||
##### Initialization Function
|
||||
|
||||
Comes in the form `static int <behavior_name>_init(const struct device *dev)`. Initialization functions preconfigure any data, like resetting timers and position for hold-taps and tap-dances. All initialization functions `return 0;` once complete.
|
||||
|
||||
##### API Structure
|
||||
|
||||
Comes in the form `static const struct behavior_driver_api <behavior_name>_driver_api)`. Common items to include in the API Structure are:
|
||||
|
||||
- `.binding_pressed`: Used for behaviors that invoke an action on its keybind press. Set `.binding_pressed` equal to the function typically named [`on_<behavior_name>_binding_pressed`](#dependencies).
|
||||
- `.binding_released`: Same as above, except for activating on keybind release events. Set `.binding_released` equal to the function typically named [`on_<behavior_name>_binding_released`](#dependencies).
|
||||
- `.locality`: Defined in `<drivers/behavior.h>`. Describes how the behavior affects parts of a _split_ keyboard.
|
||||
- `BEHAVIOR_LOCALITY_CENTRAL`: Behavior only affects the central half, which is the case for most keymap-related behavior.
|
||||
- `BEHAVIOR_LOCALITY_EVENT_SOURCE`: Behavior affects only the central _or_ peripheral half depending on which side invoked the behavior binding, such as [reset behaviors](../behaviors/reset.md).
|
||||
- `BEHAVIOR_LOCALITY_GLOBAL`: Behavior affects the entire keyboard, such as [external power](../behaviors/power.md) and lighting-related behaviors that need to be synchronized across halves.
|
||||
:::note
|
||||
For unibody keyboards, all locality values perform the same as `BEHAVIOR_LOCALITY_GLOBAL`.
|
||||
:::
|
||||
|
||||
##### Data Pointers (Optional)
|
||||
|
||||
The data `struct` stores additional data required for **each new instance** of the behavior. Regardless of the instance number, `n`, `behavior_<behavior_name>_data_##n` is typically initialized as an empty `struct`. The data respective to each instance of the behavior can be accessed in functions like [`on_<behavior_name>_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event)`](#dependencies) by extracting the behavior device from the keybind like so:
|
||||
|
||||
```c
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
struct behavior_<behavior_name>_data *data = dev->data;
|
||||
```
|
||||
|
||||
The variables stored inside the data `struct`, `data`, can be then modified as necessary.
|
||||
|
||||
The fourth cell of `DEVICE_DT_INST_DEFINE` can be set to `NULL` instead if instance-specific data is not required.
|
||||
|
||||
##### Configuration Pointers (Optional)
|
||||
|
||||
The configuration `struct` stores the properties declared from the behavior's `.yaml` for **each new instance** of the behavior. As seen in the `#define KP_INST(n)` of the hold-tap example, the configuration `struct`, `behavior_<behavior_name>_config_##n`, for each instance number, `n`, can be initialized using the [Zephyr Devicetree Instance-based APIs](https://docs.zephyrproject.org/latest/build/dts/api/api.html#instance-based-apis), which extract the values from the `properties` of each instance of the [devicetree binding](#creating-the-devicetree-binding-yaml) from a user's keymap or [predefined use-case `.dtsi` files](#defining-common-use-cases-for-the-behavior-dtsi-optional) stored in `app/dts/behaviors/`. We illustrate this further by comparing the [`#define KP_INST(n)` from the hold-tap driver](#device_dt_inst_define) and the [`properties` of the hold-tap devicetree binding.](#creating-the-devicetree-binding-yaml)
|
||||
|
||||
The fifth cell of `DEVICE_DT_INST_DEFINE` can be set to `NULL` instead if instance-specific configurations are not required.
|
||||
|
||||
:::caution
|
||||
Remember that `.c` files should be formatted according to `clang-format` to ensure that checks run smoothly once the pull request is submitted.
|
||||
:::
|
||||
|
||||
### Updating `app/CmakeLists.txt` to include the new driver
|
||||
|
||||
Most behavior drivers' are invoked according to the central half's [locality](#api-structure), and are therefore stored after the line `if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)` in the form, `target_sources(app PRIVATE src/behaviors/<behavior_name>.c)`, as shown below.
|
||||
|
||||
```txt title="app/CmakeLists.txt"
|
||||
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_caps_word.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_key_repeat.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_mod_morph.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_outputs.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_to_layer.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_none.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c)
|
||||
target_sources(app PRIVATE src/combo.c)
|
||||
target_sources(app PRIVATE src/conditional_layer.c)
|
||||
target_sources(app PRIVATE src/keymap.c)
|
||||
endif()
|
||||
```
|
||||
|
||||
For behaviors that do not require central locality, the following options for updating `app/CmakeLists.txt` also exist:
|
||||
|
||||
- Behavior applies to unibody, or central or peripheral half of keyboard: place `target_sources(app PRIVATE <behavior_name>.c)` line _before_ `if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)`
|
||||
- Behavior applies to _only_ central half of split keyboard: place `target_sources(app PRIVATE <behavior_name>.c)` after `if (CONFIG_ZMK_SPLIT_BLE AND CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)`
|
||||
- Behavior applies to _only_ peripheral half of split keyboard: place `target_sources(app PRIVATE <behavior_name>.c)` after `if (CONFIG_ZMK_SPLIT_BLE AND (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))`
|
||||
- Behavior requires certain condition in a keyboard's `.conf` file to be met: use `target_sources_ifdef(CONFIG_<Configuration Requirement> app PRIVATE <behavior_name>.c)` instead of `target_sources(<behavior_name>.c)`
|
||||
|
||||
### Defining common use-cases for the behavior (`.dtsi`) (Optional)
|
||||
|
||||
`.dtsi` files, found in the directory `app/dts/behaviors/`, are only necessary for behaviors with more common use-cases. A common example is the mod-tap (`&mt`), which is a predefined type of hold-tap that takes a modifier key as the hold parameter and another key as the tap parameter.
|
||||
|
||||
For the purpose of this section, we will discuss the structure of `app/dts/behaviors/gresc.dtsi` below.
|
||||
|
||||
```dtsi title="app/dts/behaviors/gresc.dtsi"
|
||||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
/omit-if-no-ref/ gresc: grave_escape {
|
||||
compatible = "zmk,behavior-mod-morph";
|
||||
label = "GRAVE_ESCAPE";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kp ESC>, <&kp GRAVE>;
|
||||
mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
The format of a behavior's `.dtsi` file is identical to declaring an instance of the behavior in a user's keymap. The only major difference is that the value `/omit-if-no-ref/` should be placed adjacent to the name of the behavior, as seen in line 11 of the `gresc` example.
|
||||
|
||||
After creating the `.dtsi` from above, update `app/dts/behaviors.dtsi` to include your newly predefined behavior instance, making it accessible by the devicetree.
|
||||
|
||||
```dtsi title="app/dts/behaviors.dtsi"
|
||||
#include <behaviors/key_press.dtsi>
|
||||
#include <behaviors/transparent.dtsi>
|
||||
#include <behaviors/none.dtsi>
|
||||
#include <behaviors/mod_tap.dtsi>
|
||||
#include <behaviors/layer_tap.dtsi>
|
||||
#include <behaviors/gresc.dtsi>
|
||||
#include <behaviors/sticky_key.dtsi>
|
||||
#include <behaviors/momentary_layer.dtsi>
|
||||
#include <behaviors/toggle_layer.dtsi>
|
||||
#include <behaviors/to_layer.dtsi>
|
||||
#include <behaviors/reset.dtsi>
|
||||
#include <behaviors/sensor_rotate_key_press.dtsi>
|
||||
#include <behaviors/rgb_underglow.dtsi>
|
||||
#include <behaviors/bluetooth.dtsi>
|
||||
#include <behaviors/ext_power.dtsi>
|
||||
#include <behaviors/outputs.dtsi>
|
||||
#include <behaviors/caps_word.dtsi>
|
||||
#include <behaviors/key_repeat.dtsi>
|
||||
#include <behaviors/backlight.dtsi>
|
||||
#include <behaviors/macros.dtsi>
|
||||
// highlight-next-line
|
||||
#include <behaviors/new_behavior_instance.dtsi>
|
||||
```
|
||||
|
||||
## Testing changes locally
|
||||
|
||||
Create a new folder in `app/tests/` to develop virtual test sets for all common use cases of the behavior. Behaviors should be tested thoroughly on both virtual testing environments using `west test` and real hardware.
|
||||
|
||||
:::note
|
||||
Zephyr currently does not support logging over Bluetooth, so any use of the serial monitor for hardware testing must be done over hardware UART or USB virtual UART.
|
||||
:::
|
||||
|
||||
:::info
|
||||
|
||||
- See [Tests](tests.md) for more information on how to create virtual test sets.
|
||||
- For hardware-based testing, see [USB Logging](usb-logging.md).
|
||||
|
||||
:::
|
||||
|
||||
## Documenting behavior functionality
|
||||
|
||||
Consider the following prompts when writing documentation for new behaviors:
|
||||
|
||||
- What does it do? Describe some general use-cases for the behavior.
|
||||
- Which properties included in the [devicetree binding](#creating-the-devicetree-binding-yaml) should be configured manually by the user? What do they do, and if applicable, what are their default values?
|
||||
- What does an example implementation in a keymap look like? Include a code-snippet of the example implementation in the keymap file's `behaviors` node.
|
||||
- Are there any [common use-cases of the behavior](#defining-common-use-cases-for-the-behavior-dtsi-optional)? Consider making a separate documentation page for these predefined variations, like how the [mod-tap](../behaviors/mod-tap.md) has a separate page from the [hold-tap](../behaviors/hold-tap.md).
|
||||
- How does the behavior perform in edge cases? For example, tap-dances invoke the last binding in its list of `bindings` once the maximum number of keypresses has been reached.
|
||||
|
||||
Consider also including visual aids alongside written documentation if it adds clarity.
|
||||
|
||||
:::info
|
||||
See [Documentation](documentation.md) for more information on writing, testing, and formatting ZMK documentation.
|
||||
:::
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
Once the above sections are complete, the behavior is almost ready to submit as a pull request. New [devicetree bindings](#creating-the-devicetree-binding-yaml), new [drivers](#creating-the-driver-c), and [predefined use-cases](#defining-common-use-cases-for-the-behavior-dtsi-optional) of the new behavior must contain the appropriate copyright headers, which can be copied and pasted from the tabs below.
|
||||
|
||||
<Tabs
|
||||
defaultValue="yaml"
|
||||
values={[
|
||||
{label: 'Devicetree Bindings (.yaml)', value: 'yaml'},
|
||||
{label: 'Drivers (.c) and predefined use-cases (.dtsi)', value: 'c'},
|
||||
]}>
|
||||
<TabItem value="yaml">
|
||||
|
||||
```yaml
|
||||
// highlight-next-line
|
||||
# Copyright (c) XXXX The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="c">
|
||||
|
||||
```c
|
||||
/*
|
||||
// highlight-next-line
|
||||
* Copyright (c) XXXX The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::caution
|
||||
Remember to change the copyright year (`XXXX`) to the current year when adding the copyright headers to your newly created files.
|
||||
:::
|
||||
|
||||
While you wait for your PR to become approved and merged into the main repository, please stay up to date for any code reviews and check in with users testing your new behavior. For more detailed information on contributing to ZMK, it is recommended to thoroughly review the [documentation for contributors](https://github.com/zmkfirmware/zmk/blob/main/CONTRIBUTING.md).
|
|
@ -21,18 +21,18 @@ apt install -y gcc-multilib
|
|||
## Building
|
||||
|
||||
To do this, you can build ZMK targeting the
|
||||
`native_posix` board.
|
||||
`native_posix_64` board.
|
||||
|
||||
```
|
||||
west build --pristine --board native_posix
|
||||
west build --pristine --board native_posix_64 -- -DZMK_CONFIG=tests/none/normal/
|
||||
```
|
||||
|
||||
Once built, you can run the firmware locally:
|
||||
|
||||
```
|
||||
./build/zephyr/zephyr.exe
|
||||
./build/zephyr/zmk.exe
|
||||
```
|
||||
|
||||
## Virtual Key Events
|
||||
|
||||
The virtual key presses are hardcoded in `boards/native_posix.overlay` file, should you want to change the sequence to test various actions like Mod-Tap, etc.
|
||||
The virtual key presses are hardcoded in `boards/native_posix_64.overlay` file, should you want to change the sequence to test various actions like Mod-Tap, etc.
|
||||
|
|
|
@ -4,7 +4,7 @@ sidebar_label: Tests
|
|||
---
|
||||
|
||||
- Running tests requires [native posix support](posix-board.md).
|
||||
- Any folder under `/app/tests` containing `native_posix.keymap` will be selected when running `west test`.
|
||||
- Any folder under `/app/tests` containing `native_posix_64.keymap` will be selected when running `west test`.
|
||||
- Run tests from within the `/zmk/app` directory.
|
||||
- Run a single test with `west test <testname>`, like `west test tests/toggle-layer/normal`.
|
||||
|
||||
|
@ -13,7 +13,7 @@ sidebar_label: Tests
|
|||
1. Copy the test set that most closely resembles the tests you will be creating.
|
||||
2. Rename the newly created test set to the behavior you're testing e.g, toggle-layer
|
||||
3. Modify `behavior_keymap.dtsi` to create a keymap using the behavior and related behaviors
|
||||
4. Modify `test_case/native_posix.keymap` for a simulated use case
|
||||
4. Modify `test_case/native_posix_64.keymap` for a simulated use case
|
||||
5. Modify `test_case/events.patterns` to collect relevant logs to the test
|
||||
- See: [sed manual](https://www.gnu.org/software/sed/manual/sed.html) and
|
||||
[tutorial](https://www.digitalocean.com/community/tutorials/the-basics-of-using-the-sed-stream-editor-to-manipulate-text-in-linux)
|
||||
|
|
|
@ -6,6 +6,7 @@ module.exports = {
|
|||
url: "https://zmk.dev",
|
||||
baseUrl: "/",
|
||||
favicon: "img/favicon.ico",
|
||||
trailingSlash: "false",
|
||||
organizationName: "zmkfirmware", // Usually your GitHub org/user name.
|
||||
projectName: "zmk", // Usually your repo name.
|
||||
plugins: [
|
||||
|
|
400
docs/package-lock.json
generated
400
docs/package-lock.json
generated
|
@ -5193,9 +5193,9 @@
|
|||
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="
|
||||
},
|
||||
"@tsconfig/docusaurus": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/docusaurus/-/docusaurus-1.0.2.tgz",
|
||||
"integrity": "sha512-x4rRVb346vjyym6QbeB1Tv01XXwhbkujOmvDmtf0bT20oc2gbDhbmwpskKqZ5Of2Q/Vk4jNk1WMiLsZdJ9t7Dw==",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/docusaurus/-/docusaurus-1.0.5.tgz",
|
||||
"integrity": "sha512-KM/TuJa9fugo67dTGx+ktIqf3fVc077J6jwHu845Hex4EQf7LABlNonP/mohDKT0cmncdtlYVHHF74xR/YpThg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/body-parser": {
|
||||
|
@ -5395,9 +5395,9 @@
|
|||
}
|
||||
},
|
||||
"@types/react-helmet": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.0.tgz",
|
||||
"integrity": "sha512-PYRoU1XJFOzQ3BHvWL1T8iDNbRjdMDJMT5hFmZKGbsq09kbSqJy61uwEpTrbTNWDopVphUT34zUSVLK9pjsgYQ==",
|
||||
"version": "6.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.5.tgz",
|
||||
"integrity": "sha512-/ICuy7OHZxR0YCAZLNg9r7I9aijWUWvxaPR6uTuyxe8tAj5RL4Sw1+R6NhXUtOsarkGYPmaHdBDvuXh2DIN/uA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
|
@ -5830,16 +5830,28 @@
|
|||
"integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ=="
|
||||
},
|
||||
"array-includes": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz",
|
||||
"integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==",
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz",
|
||||
"integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.19.1",
|
||||
"define-properties": "^1.1.4",
|
||||
"es-abstract": "^1.19.5",
|
||||
"get-intrinsic": "^1.1.1",
|
||||
"is-string": "^1.0.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"define-properties": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
|
||||
"integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"object-keys": "^1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"array-union": {
|
||||
|
@ -5848,14 +5860,15 @@
|
|||
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="
|
||||
},
|
||||
"array.prototype.flatmap": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz",
|
||||
"integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz",
|
||||
"integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.0",
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.19.0"
|
||||
"es-abstract": "^1.19.2",
|
||||
"es-shim-unscopables": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"asap": {
|
||||
|
@ -7380,31 +7393,42 @@
|
|||
}
|
||||
},
|
||||
"es-abstract": {
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz",
|
||||
"integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==",
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz",
|
||||
"integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"function.prototype.name": "^1.1.5",
|
||||
"get-intrinsic": "^1.1.1",
|
||||
"get-symbol-description": "^1.0.0",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.2",
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"has-symbols": "^1.0.3",
|
||||
"internal-slot": "^1.0.3",
|
||||
"is-callable": "^1.2.4",
|
||||
"is-negative-zero": "^2.0.1",
|
||||
"is-negative-zero": "^2.0.2",
|
||||
"is-regex": "^1.1.4",
|
||||
"is-shared-array-buffer": "^1.0.1",
|
||||
"is-shared-array-buffer": "^1.0.2",
|
||||
"is-string": "^1.0.7",
|
||||
"is-weakref": "^1.0.1",
|
||||
"object-inspect": "^1.11.0",
|
||||
"is-weakref": "^1.0.2",
|
||||
"object-inspect": "^1.12.0",
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.2",
|
||||
"string.prototype.trimend": "^1.0.4",
|
||||
"string.prototype.trimstart": "^1.0.4",
|
||||
"unbox-primitive": "^1.0.1"
|
||||
"regexp.prototype.flags": "^1.4.3",
|
||||
"string.prototype.trimend": "^1.0.5",
|
||||
"string.prototype.trimstart": "^1.0.5",
|
||||
"unbox-primitive": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"es-module-lexer": {
|
||||
|
@ -7412,6 +7436,15 @@
|
|||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
|
||||
"integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
|
||||
},
|
||||
"es-shim-unscopables": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
|
||||
"integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"es-to-primitive": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
|
||||
|
@ -7575,9 +7608,9 @@
|
|||
}
|
||||
},
|
||||
"eslint-config-prettier": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz",
|
||||
"integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
|
||||
"integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
|
||||
"dev": true
|
||||
},
|
||||
"eslint-mdx": {
|
||||
|
@ -7647,25 +7680,25 @@
|
|||
}
|
||||
},
|
||||
"eslint-plugin-react": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz",
|
||||
"integrity": "sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==",
|
||||
"version": "7.30.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.0.tgz",
|
||||
"integrity": "sha512-RgwH7hjW48BleKsYyHK5vUAvxtE9SMPDKmcPRQgtRCYaZA0XQPt5FSkrU3nhz5ifzMZcA8opwmRJ2cmOO8tr5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-includes": "^3.1.4",
|
||||
"array.prototype.flatmap": "^1.2.5",
|
||||
"array-includes": "^3.1.5",
|
||||
"array.prototype.flatmap": "^1.3.0",
|
||||
"doctrine": "^2.1.0",
|
||||
"estraverse": "^5.3.0",
|
||||
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"minimatch": "^3.1.2",
|
||||
"object.entries": "^1.1.5",
|
||||
"object.fromentries": "^2.0.5",
|
||||
"object.hasown": "^1.1.0",
|
||||
"object.hasown": "^1.1.1",
|
||||
"object.values": "^1.1.5",
|
||||
"prop-types": "^15.7.2",
|
||||
"prop-types": "^15.8.1",
|
||||
"resolve": "^2.0.0-next.3",
|
||||
"semver": "^6.3.0",
|
||||
"string.prototype.matchall": "^4.0.6"
|
||||
"string.prototype.matchall": "^4.0.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"doctrine": {
|
||||
|
@ -7683,6 +7716,26 @@
|
|||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"resolve": {
|
||||
"version": "2.0.0-next.3",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz",
|
||||
|
@ -8273,12 +8326,30 @@
|
|||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"function.prototype.name": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
|
||||
"integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.19.0",
|
||||
"functions-have-names": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"functional-red-black-tree": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
|
||||
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
|
||||
"dev": true
|
||||
},
|
||||
"functions-have-names": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
|
||||
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
|
||||
"dev": true
|
||||
},
|
||||
"gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
|
@ -8506,9 +8577,9 @@
|
|||
}
|
||||
},
|
||||
"has-bigints": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
|
||||
"integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
|
||||
"integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
|
@ -8516,6 +8587,15 @@
|
|||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"has-property-descriptors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
|
||||
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"get-intrinsic": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
|
||||
|
@ -9014,10 +9094,13 @@
|
|||
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
|
||||
},
|
||||
"is-bigint": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz",
|
||||
"integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==",
|
||||
"dev": true
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
|
||||
"integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-bigints": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
|
@ -9028,12 +9111,13 @@
|
|||
}
|
||||
},
|
||||
"is-boolean-object": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz",
|
||||
"integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
|
||||
"integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2"
|
||||
"call-bind": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-buffer": {
|
||||
|
@ -9064,10 +9148,13 @@
|
|||
}
|
||||
},
|
||||
"is-date-object": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz",
|
||||
"integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==",
|
||||
"dev": true
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
|
||||
"integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-tostringtag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-decimal": {
|
||||
"version": "1.0.4",
|
||||
|
@ -9117,9 +9204,9 @@
|
|||
}
|
||||
},
|
||||
"is-negative-zero": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
|
||||
"integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
|
||||
"integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
|
||||
"dev": true
|
||||
},
|
||||
"is-npm": {
|
||||
|
@ -9133,10 +9220,13 @@
|
|||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
|
||||
},
|
||||
"is-number-object": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz",
|
||||
"integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==",
|
||||
"dev": true
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
|
||||
"integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-tostringtag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-obj": {
|
||||
"version": "1.0.1",
|
||||
|
@ -9193,10 +9283,13 @@
|
|||
"integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg=="
|
||||
},
|
||||
"is-shared-array-buffer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz",
|
||||
"integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==",
|
||||
"dev": true
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
|
||||
"integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"is-stream": {
|
||||
"version": "2.0.1",
|
||||
|
@ -9413,12 +9506,12 @@
|
|||
}
|
||||
},
|
||||
"jsx-ast-utils": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz",
|
||||
"integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.0.tgz",
|
||||
"integrity": "sha512-XzO9luP6L0xkxwhIJMTJQpZo/eeN60K08jHdexfD569AGxeNug6UketeHXEhROoM8aR7EcUoOQmIhcJQjcuq8Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-includes": "^3.1.2",
|
||||
"array-includes": "^3.1.4",
|
||||
"object.assign": "^4.1.2"
|
||||
}
|
||||
},
|
||||
|
@ -10073,9 +10166,9 @@
|
|||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
||||
},
|
||||
"object-inspect": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
|
||||
"integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==",
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.1.tgz",
|
||||
"integrity": "sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA==",
|
||||
"dev": true
|
||||
},
|
||||
"object-keys": {
|
||||
|
@ -10117,13 +10210,25 @@
|
|||
}
|
||||
},
|
||||
"object.hasown": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz",
|
||||
"integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz",
|
||||
"integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.19.1"
|
||||
"define-properties": "^1.1.4",
|
||||
"es-abstract": "^1.19.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"define-properties": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
|
||||
"integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"object-keys": "^1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"object.values": {
|
||||
|
@ -11523,13 +11628,14 @@
|
|||
}
|
||||
},
|
||||
"regexp.prototype.flags": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz",
|
||||
"integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==",
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
|
||||
"integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3"
|
||||
"define-properties": "^1.1.3",
|
||||
"functions-have-names": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"regexpp": {
|
||||
|
@ -12612,9 +12718,9 @@
|
|||
"integrity": "sha512-mC1Ps9l77/97qeOZc+HrOL7TIaOboHqMZ24dGVQrlxFcpPpfCHpH+qfUT7Dz+6mlG8+JPA1KfBQo19iC/+Ngcw=="
|
||||
},
|
||||
"string-replace-loader": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string-replace-loader/-/string-replace-loader-3.0.3.tgz",
|
||||
"integrity": "sha512-8c26Dl6H9XmKNj3mFBvaUYR7ImOxQ4YRBFuUju78wXpa1cDpyDYvKmqGg8mfkxdYexQ/BBogB7PELlLnmR08nw==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-replace-loader/-/string-replace-loader-3.1.0.tgz",
|
||||
"integrity": "sha512-5AOMUZeX5HE/ylKDnEa/KKBqvlnFmRZudSOjVJHxhoJg9QYTwl1rECx7SLR8BBH7tfxb4Rp7EM2XVfQFxIhsbQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loader-utils": "^2.0.0",
|
||||
|
@ -12639,39 +12745,73 @@
|
|||
}
|
||||
},
|
||||
"string.prototype.matchall": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz",
|
||||
"integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==",
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz",
|
||||
"integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.19.1",
|
||||
"get-intrinsic": "^1.1.1",
|
||||
"has-symbols": "^1.0.2",
|
||||
"has-symbols": "^1.0.3",
|
||||
"internal-slot": "^1.0.3",
|
||||
"regexp.prototype.flags": "^1.3.1",
|
||||
"regexp.prototype.flags": "^1.4.1",
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"string.prototype.trimend": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
|
||||
"integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
|
||||
"integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3"
|
||||
"define-properties": "^1.1.4",
|
||||
"es-abstract": "^1.19.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"define-properties": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
|
||||
"integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"object-keys": "^1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"string.prototype.trimstart": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
|
||||
"integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz",
|
||||
"integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3"
|
||||
"define-properties": "^1.1.4",
|
||||
"es-abstract": "^1.19.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"define-properties": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
|
||||
"integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"object-keys": "^1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
|
@ -13062,15 +13202,23 @@
|
|||
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
|
||||
},
|
||||
"unbox-primitive": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
|
||||
"integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
|
||||
"integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has-bigints": "^1.0.1",
|
||||
"has-symbols": "^1.0.2",
|
||||
"call-bind": "^1.0.2",
|
||||
"has-bigints": "^1.0.2",
|
||||
"has-symbols": "^1.0.3",
|
||||
"which-boxed-primitive": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"unherit": {
|
||||
|
@ -13423,13 +13571,13 @@
|
|||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.58.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.58.2.tgz",
|
||||
"integrity": "sha512-3S6e9Vo1W2ijk4F4PPWRIu6D/uGgqaPmqw+av3W3jLDujuNkdxX5h5c+RQ6GkjVR+WwIPOfgY8av+j5j4tMqJw==",
|
||||
"version": "5.72.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.72.1.tgz",
|
||||
"integrity": "sha512-dXG5zXCLspQR4krZVR6QgajnZOjW2K/djHvdcRaDQvsjV9z9vaW6+ja5dZOYbqBBjF6kGXka/2ZyxNdc+8Jung==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.0",
|
||||
"@types/estree": "^0.0.50",
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^0.0.51",
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/wasm-edit": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1",
|
||||
|
@ -13437,34 +13585,44 @@
|
|||
"acorn-import-assertions": "^1.7.6",
|
||||
"browserslist": "^4.14.5",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.8.3",
|
||||
"enhanced-resolve": "^5.9.3",
|
||||
"es-module-lexer": "^0.9.0",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"json-parse-better-errors": "^1.0.2",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"json-parse-even-better-errors": "^2.3.1",
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.1.0",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"watchpack": "^2.2.0",
|
||||
"webpack-sources": "^3.2.0"
|
||||
"watchpack": "^2.3.1",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz",
|
||||
"integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==",
|
||||
"@types/estree": {
|
||||
"version": "0.0.51",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
|
||||
"integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-import-assertions": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
|
||||
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
|
||||
"acorn": {
|
||||
"version": "8.7.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
|
||||
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
|
||||
"dev": true
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
|
||||
"integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -45,22 +45,22 @@
|
|||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^2.0.0-beta.18",
|
||||
"@docusaurus/types": "^2.0.0-beta.18",
|
||||
"@tsconfig/docusaurus": "^1.0.2",
|
||||
"@tsconfig/docusaurus": "^1.0.5",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/react": "^17.0.3",
|
||||
"@types/react-helmet": "^6.1.0",
|
||||
"@types/react-helmet": "^6.1.5",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-mdx": "^1.17.0",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react": "^7.30.0",
|
||||
"json-schema-to-typescript": "^10.1.5",
|
||||
"mustache": "^4.2.0",
|
||||
"null-loader": "^4.0.0",
|
||||
"prebuild-webpack-plugin": "^1.1.1",
|
||||
"prettier": "2.3.1",
|
||||
"string-replace-loader": "^3.0.3",
|
||||
"string-replace-loader": "^3.1.0",
|
||||
"typescript": "^4.6.3",
|
||||
"webpack": "^5.46.0"
|
||||
"webpack": "^5.72.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ module.exports = {
|
|||
items: [
|
||||
"development/new-shield",
|
||||
"development/hardware-metadata-files",
|
||||
"development/new-behavior",
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -4989,7 +4989,7 @@ export default [
|
|||
footnotes: {},
|
||||
},
|
||||
{
|
||||
names: ["C_CAPTIONS", "C_SUBTITILES"],
|
||||
names: ["C_CAPTIONS", "C_SUBTITLES"],
|
||||
description: "Closed Caption",
|
||||
context: "Consumer",
|
||||
clarify: true,
|
||||
|
|
Loading…
Add table
Reference in a new issue