programmable buttons

This commit is contained in:
apewo 2024-05-12 00:53:35 +02:00
parent 4dfc45d4ab
commit d3065d7325
18 changed files with 416 additions and 0 deletions

View file

@ -33,6 +33,7 @@ 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/sensor_event.c)
target_sources(app PRIVATE src/events/mouse_button_state_changed.c)
target_sources_ifdef(CONFIG_ZMK_PROGRAMMABLE_BUTTONS app PRIVATE src/events/programmable_button_state_changed.c)
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_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)
@ -41,6 +42,7 @@ target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SOFT_OFF app PRIVATE src/behaviors/beha
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/hid.c)
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse.c)
target_sources_ifdef(CONFIG_ZMK_PROGRAMMABLE_BUTTONS app PRIVATE src/programmable_buttons.c)
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c)
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
@ -59,6 +61,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR app PRIVATE src/behaviors/behavior_sensor_rotate_var.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON app PRIVATE src/behaviors/behavior_sensor_rotate_common.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_MOUSE_KEY_PRESS app PRIVATE src/behaviors/behavior_mouse_key_press.c)
target_sources_ifdef(CONFIG_ZMK_PROGRAMMABLE_BUTTONS app PRIVATE src/behaviors/behavior_programmable_buttons.c)
target_sources(app PRIVATE src/combo.c)
target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c)
target_sources(app PRIVATE src/behavior_queue.c)

View file

@ -213,6 +213,10 @@ config ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE
int "Max number of mouse HID reports to queue for sending over BLE"
default 20
config ZMK_BLE_PROGRAMMABLE_BUTTONS_REPORT_QUEUE_SIZE
int "Max number of programmable buttons HID reports to queue for sending over BLE"
default 20
config ZMK_BLE_CLEAR_BONDS_ON_START
bool "Configuration that clears all bond information from the keyboard on startup."
@ -367,6 +371,15 @@ endif
#Display/LED Options
endmenu
menu "Programmable Buttons"
config ZMK_PROGRAMMABLE_BUTTONS
bool "Enable programmable buttons"
default n
#Mouse Options
endmenu
menu "Mouse Options"
config ZMK_MOUSE

View file

@ -21,3 +21,4 @@
#include <behaviors/macros.dtsi>
#include <behaviors/mouse_key_press.dtsi>
#include <behaviors/soft_off.dtsi>
#include <behaviors/programmable_buttons.dtsi>

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
/ {
behaviors {
/omit-if-no-ref/ pb: programmable_buttons {
compatible = "zmk,behavior-programmable-buttons";
#binding-cells = <1>;
};
};
};

View file

@ -0,0 +1,8 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Programmable buttons press/release behavior
compatible: "zmk,behavior-programmable-buttons"
include: one_param.yaml

View file

@ -70,6 +70,11 @@ struct zmk_endpoint_instance zmk_endpoints_selected(void);
int zmk_endpoints_send_report(uint16_t usage_page);
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
int zmk_endpoints_send_programmable_buttons_report();
#endif // IS_ENABLE(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_endpoints_send_mouse_report();
#endif // IS_ENABLE(CONFIG_ZMK_MOUSE)

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zmk/hid.h>
#include <zmk/event_manager.h>
struct zmk_programmable_button_state_changed {
uint8_t index;
bool state;
int64_t timestamp;
};
ZMK_EVENT_DECLARE(zmk_programmable_button_state_changed);
static inline int raise_zmk_programmable_button_state_changed_from_encoded(uint8_t index, bool pressed,
int64_t timestamp) {
return raise_zmk_programmable_button_state_changed((struct zmk_programmable_button_state_changed){
.index = index, .state = pressed, .timestamp = timestamp});
}

View file

@ -60,6 +60,7 @@
#define ZMK_HID_REPORT_ID_LEDS 0x01
#define ZMK_HID_REPORT_ID_CONSUMER 0x02
#define ZMK_HID_REPORT_ID_MOUSE 0x03
#define ZMK_HID_REPORT_ID_PROGRAMMABLE_BUTTONS 0x04
static const uint8_t zmk_hid_report_desc[] = {
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP),
@ -145,6 +146,25 @@ static const uint8_t zmk_hid_report_desc[] = {
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_ARRAY | ZMK_HID_MAIN_VAL_ABS),
HID_END_COLLECTION,
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
HID_USAGE_PAGE(HID_USAGE_CONSUMER),
HID_USAGE(HID_USAGE_CONSUMER_CONSUMER_CONTROL),
HID_COLLECTION(HID_COLLECTION_APPLICATION),
HID_REPORT_ID(ZMK_HID_REPORT_ID_PROGRAMMABLE_BUTTONS),
HID_USAGE(HID_USAGE_CONSUMER_PROGRAMMABLE_BUTTONS),
HID_COLLECTION(HID_COLLECTION_NAMED_ARRAY),
HID_USAGE_PAGE(HID_USAGE_BUTTON),
HID_USAGE_MIN8(0x1),
HID_USAGE_MAX8(0x20),
HID_LOGICAL_MIN8(0x00),
HID_LOGICAL_MAX8(0x01),
HID_REPORT_SIZE(0x01),
HID_REPORT_COUNT(0x20),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),
HID_END_COLLECTION,
HID_END_COLLECTION,
#endif //IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
HID_USAGE_PAGE(HID_USAGE_GD),
HID_USAGE(HID_USAGE_GD_MOUSE),
@ -239,6 +259,13 @@ struct zmk_hid_consumer_report {
struct zmk_hid_consumer_report_body body;
} __packed;
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
struct zmk_hid_programmable_buttons_report {
uint8_t report_id;
uint32_t body;
} __packed;
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
struct zmk_hid_mouse_report_body {
zmk_mouse_button_flags_t buttons;
@ -280,6 +307,12 @@ int zmk_hid_press(uint32_t usage);
int zmk_hid_release(uint32_t usage);
bool zmk_hid_is_pressed(uint32_t usage);
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
int zmk_hid_programmable_button_press(uint8_t index);
int zmk_hid_programmable_button_release(uint8_t index);
void zmk_hid_programmable_buttons_clear(void);
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_hid_mouse_button_press(zmk_mouse_button_t button);
int zmk_hid_mouse_button_release(zmk_mouse_button_t button);
@ -295,6 +328,10 @@ struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(void);
zmk_hid_boot_report_t *zmk_hid_get_boot_report();
#endif
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
struct zmk_hid_programmable_buttons_report *zmk_hid_get_programmable_buttons_report(void);
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
struct zmk_hid_mouse_report *zmk_hid_get_mouse_report();
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

View file

@ -12,6 +12,10 @@
int zmk_hog_send_keyboard_report(struct zmk_hid_keyboard_report_body *body);
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body);
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
int zmk_hog_send_programmable_buttons_report(uint32_t body);
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body);
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

View file

@ -10,6 +10,9 @@
int zmk_usb_hid_send_keyboard_report(void);
int zmk_usb_hid_send_consumer_report(void);
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
int zmk_usb_hid_send_programmable_buttons_report(void);
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_usb_hid_send_mouse_report(void);
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_programmable_buttons
// Dependencies
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
#include <zmk/behavior.h>
#include <zmk/event_manager.h>
#include <zmk/events/programmable_button_state_changed.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
LOG_DBG("programmable button : position %d keycode 0x%02X", event.position, binding->param1);
return raise_zmk_programmable_button_state_changed_from_encoded(binding->param1, true,
event.timestamp);
}
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
LOG_DBG("programmable button : position %d keycode 0x%02X", event.position, binding->param1);
return raise_zmk_programmable_button_state_changed_from_encoded(binding->param1, false,
event.timestamp);
}
// Initialization Function
static int behavior_programmable_button_init(const struct device *dev) {
return 0;
};
// API Structure
static const struct behavior_driver_api behavior_programmable_button_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
#define KP_INST(n) \
BEHAVIOR_DT_INST_DEFINE(n, behavior_programmable_button_init, NULL, NULL, NULL, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_programmable_button_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -203,6 +203,37 @@ int zmk_endpoints_send_report(uint16_t usage_page) {
return -ENOTSUP;
}
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
int zmk_endpoints_send_programmable_buttons_report() {
switch (current_instance.transport) {
#if IS_ENABLED(CONFIG_ZMK_USB)
case ZMK_TRANSPORT_USB: {
int err = zmk_usb_hid_send_programmable_buttons_report();
if (err) {
LOG_ERR("FAILED TO SEND OVER USB: %d", err);
}
return err;
}
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
#if IS_ENABLED(CONFIG_ZMK_BLE)
case ZMK_TRANSPORT_BLE: {
struct zmk_hid_programmable_buttons_report *programmable_buttons_report = zmk_hid_get_programmable_buttons_report();
int err = zmk_hog_send_programmable_buttons_report(programmable_buttons_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
return err;
}
#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
}
LOG_ERR("Unsupported endpoint transport %d", current_instance.transport);
return -ENOTSUP;
}
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_endpoints_send_mouse_report() {
switch (current_instance.transport) {
@ -343,6 +374,9 @@ static int zmk_endpoints_init(void) {
void zmk_endpoints_clear_current(void) {
zmk_hid_keyboard_clear();
zmk_hid_consumer_clear();
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
zmk_hid_programmable_buttons_clear();
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
zmk_hid_mouse_clear();
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

View file

@ -0,0 +1,9 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zmk/events/programmable_button_state_changed.h>
ZMK_EVENT_IMPL(zmk_programmable_button_state_changed);

View file

@ -18,6 +18,11 @@ static struct zmk_hid_keyboard_report keyboard_report = {
static struct zmk_hid_consumer_report consumer_report = {.report_id = ZMK_HID_REPORT_ID_CONSUMER,
.body = {.keys = {0}}};
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
static struct zmk_hid_programmable_buttons_report programmable_buttons_report = {.report_id = ZMK_HID_REPORT_ID_PROGRAMMABLE_BUTTONS,
.body = 0};
#endif /* IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS) */
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
static zmk_hid_boot_report_t boot_report = {.modifiers = 0, ._reserved = 0, .keys = {0}};
@ -369,6 +374,27 @@ bool zmk_hid_is_pressed(uint32_t usage) {
return false;
}
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
int zmk_hid_programmable_button_press(uint8_t index){
if (index < 1 || index > 32) {
return -EINVAL;
}
WRITE_BIT(programmable_buttons_report.body, index - 1, true);
return 0;
}
int zmk_hid_programmable_button_release(uint8_t index){
if (index < 1 || index > 32) {
return -EINVAL;
}
WRITE_BIT(programmable_buttons_report.body, index -1, false);
return 0;
}
void zmk_hid_programmable_buttons_clear(void) { programmable_buttons_report.body = 0; }
#endif /* IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS) */
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
// Keep track of how often a button was pressed.
@ -442,6 +468,12 @@ struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(void) {
return &consumer_report;
}
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
struct zmk_hid_programmable_buttons_report *zmk_hid_get_programmable_buttons_report(void) {
return &programmable_buttons_report;
}
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
struct zmk_hid_mouse_report *zmk_hid_get_mouse_report(void) {

View file

@ -69,6 +69,15 @@ static struct hids_report consumer_input = {
.type = HIDS_INPUT,
};
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
static struct hids_report programmable_buttons_input = {
.id = ZMK_HID_REPORT_ID_PROGRAMMABLE_BUTTONS,
.type = HIDS_INPUT,
};
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
static struct hids_report mouse_input = {
@ -143,6 +152,15 @@ static ssize_t read_hids_consumer_input_report(struct bt_conn *conn,
sizeof(struct zmk_hid_consumer_report_body));
}
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
static ssize_t read_hids_programmable_buttons_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset) {
uint32_t report_body = zmk_hid_get_programmable_buttons_report()->body;
return bt_gatt_attr_read(conn, attr, buf, len, offset, &report_body,
sizeof(uint32_t));
}
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
static ssize_t read_hids_mouse_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset) {
@ -200,6 +218,14 @@ BT_GATT_SERVICE_DEFINE(
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
NULL, &consumer_input),
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ_ENCRYPT, read_hids_programmable_buttons_input_report, NULL, NULL),
BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
NULL, &programmable_buttons_input),
#endif // IS_ENABLED(CONFIG_ZMK_PROGAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ_ENCRYPT, read_hids_mouse_input_report, NULL, NULL),
@ -343,6 +369,60 @@ int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) {
return 0;
};
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
K_MSGQ_DEFINE(zmk_hog_programmable_buttons_msgq, sizeof(uint32_t),
CONFIG_ZMK_BLE_PROGRAMMABLE_BUTTONS_REPORT_QUEUE_SIZE, 4);
void send_programmable_buttons_report_callback(struct k_work *work) {
uint32_t report;
while (k_msgq_get(&zmk_hog_programmable_buttons_msgq, &report, K_NO_WAIT) == 0) {
struct bt_conn *conn = destination_connection();
if (conn == NULL) {
return;
}
struct bt_gatt_notify_params notify_params = {
.attr = &hog_svc.attrs[13],
.data = &report,
.len = sizeof(report),
};
int err = bt_gatt_notify_cb(conn, &notify_params);
if (err == -EPERM) {
bt_conn_set_security(conn, BT_SECURITY_L2);
} else if (err) {
LOG_DBG("Error notifying %d", err);
}
bt_conn_unref(conn);
}
};
K_WORK_DEFINE(hog_programmable_buttons_work, send_programmable_buttons_report_callback);
int zmk_hog_send_programmable_buttons_report(uint32_t report) {
int err = k_msgq_put(&zmk_hog_programmable_buttons_msgq, &report, K_MSEC(100));
if (err) {
switch (err) {
case -EAGAIN: {
LOG_WRN("Consumer message queue full, popping first message and queueing again");
uint32_t discarded_report;
k_msgq_get(&zmk_hog_programmable_buttons_msgq, &discarded_report, K_NO_WAIT);
return zmk_hog_send_programmable_buttons_report(report);
}
default:
LOG_WRN("Failed to queue mouse report to send (%d)", err);
return err;
}
}
k_work_submit_to_queue(&hog_work_q, &hog_programmable_buttons_work);
return 0;
};
#endif //IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
K_MSGQ_DEFINE(zmk_hog_mouse_msgq, sizeof(struct zmk_hid_mouse_report_body),

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/events/programmable_button_state_changed.h>
#include <zmk/hid.h>
#include <zmk/endpoints.h>
static void listener_programmable_button_pressed(const struct zmk_programmable_button_state_changed *ev) {
LOG_DBG("programmable button event press: 0x%02X", ev->index);
zmk_hid_programmable_button_press(ev->index);
zmk_endpoints_send_programmable_buttons_report();
}
static void listener_programmable_button_released(const struct zmk_programmable_button_state_changed *ev) {
LOG_DBG("programmable button event released: 0x%02X", ev->index);
zmk_hid_programmable_button_release(ev->index);
zmk_endpoints_send_programmable_buttons_report();
}
int programmable_buttons_listener(const zmk_event_t *eh) {
const struct zmk_programmable_button_state_changed *pb_ev = as_zmk_programmable_button_state_changed(eh);
if (pb_ev) {
if (pb_ev->state) {
listener_programmable_button_pressed(pb_ev);
} else {
listener_programmable_button_released(pb_ev);
}
return 0;
}
return 0;
}
ZMK_LISTENER(programmable_buttons_listener, programmable_buttons_listener);
ZMK_SUBSCRIPTION(programmable_buttons_listener, zmk_programmable_button_state_changed);

View file

@ -164,6 +164,19 @@ int zmk_usb_hid_send_consumer_report(void) {
return zmk_usb_hid_send_report((uint8_t *)report, sizeof(*report));
}
#if IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
int zmk_usb_hid_send_programmable_buttons_report(void) {
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
if (hid_protocol == HID_PROTOCOL_BOOT) {
return -ENOTSUP;
}
#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */
struct zmk_hid_programmable_buttons_report *report = zmk_hid_get_programmable_buttons_report();
return zmk_usb_hid_send_report((uint8_t *)report, sizeof(*report));
}
#endif // IS_ENABLED(CONFIG_ZMK_PROGRAMMABLE_BUTTONS)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_usb_hid_send_mouse_report() {
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)

View file

@ -0,0 +1,42 @@
---
title: Programmable Buttons Behaviors
sidebar_label: Programmable Buttons
---
## Summary
TODO: Programmable Buttons can be used to configure shortcuts that will not overlap with other softwares inputs. As per hid specification they are typically used to
control software applications or GUI objects.
:::warning[OS Compatibility]
Only Linux is known to have programmable buttons compatibility
:::
## Configuration Option
This feature can be enabled or disabled explicitly via a config option:
```
CONFIG_ZMK_PROGRAMMABLE_BUTTONS=y
```
## Programmable Button Press
This behavior can press/release up to 32 programmable buttons.
### Behavior Binding
- Reference: `&pb`
- Parameter: A `uint8` from 1 up to 32
### Examples
The following will send the programmable button 1
```
&pb 1
```