Skip to content

Commit

Permalink
feat(behaviors): Add a send string behavior
Browse files Browse the repository at this point in the history
This adds the following:

- A character map driver API, which maps Unicode code points to behavior
  bindings.

- A zmk,character-map driver which implements this API. This driver is
  designed for ROM efficiency, so it sends every value defined in the
  map to one behavior and passes any code point not in the map through
  to another. (A more flexible implementation that allows for a unique
  behavior binding per character could be added later if necessary.)

- A zmk,send-string behavior, which users can define and bind to their
  keymaps to send strings.

- A zmk_send_string() function, which queues the necessary behaviors
  to type a UTF-8 string. This is separated from the send string
  behavior since it could be used for other features such as Unicode
  key sequences, behaviors that print dynamic messages, etc.
  • Loading branch information
joelspadin committed Sep 15, 2024
1 parent 67d595f commit 2370be1
Show file tree
Hide file tree
Showing 21 changed files with 1,024 additions and 10 deletions.
4 changes: 4 additions & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ if(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS)
endif()

zephyr_syscall_header(${APPLICATION_SOURCE_DIR}/include/drivers/behavior.h)
zephyr_syscall_header(${APPLICATION_SOURCE_DIR}/include/drivers/character_map.h)
zephyr_syscall_header(${APPLICATION_SOURCE_DIR}/include/drivers/ext_power.h)

# Add your source file to the "app" target. This must come after
Expand All @@ -24,10 +25,12 @@ target_include_directories(app PRIVATE include)
target_sources(app PRIVATE src/stdlib.c)
target_sources(app PRIVATE src/activity.c)
target_sources(app PRIVATE src/behavior.c)
target_sources_ifdef(CONFIG_ZMK_CHARACTER_MAP app PRIVATE src/character_map.c)
target_sources_ifdef(CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS app PRIVATE src/kscan_sideband_behaviors.c)
target_sources(app PRIVATE src/matrix_transform.c)
target_sources(app PRIVATE src/physical_layouts.c)
target_sources(app PRIVATE src/sensors.c)
target_sources_ifdef(CONFIG_ZMK_SEND_STRING app PRIVATE src/send_string.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_PM app PRIVATE src/pm.c)
Expand Down Expand Up @@ -59,6 +62,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
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_ifdef(CONFIG_ZMK_BEHAVIOR_SEND_STRING app PRIVATE src/behaviors/behavior_send_string.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE app PRIVATE src/behaviors/behavior_sensor_rotate.c)
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)
Expand Down
21 changes: 19 additions & 2 deletions app/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -494,12 +494,13 @@ endmenu

menu "Behavior Options"

rsource "Kconfig.behaviors"

config ZMK_BEHAVIORS_QUEUE_SIZE
int "Maximum number of behaviors to allow queueing from a macro or other complex behavior"
default 256 if ZMK_BEHAVIOR_SEND_STRING
default 64

rsource "Kconfig.behaviors"

config ZMK_MACRO_DEFAULT_WAIT_MS
int "Default time to wait (in milliseconds) before triggering the next behavior in macros"
default 15
Expand All @@ -508,6 +509,14 @@ config ZMK_MACRO_DEFAULT_TAP_MS
int "Default time to wait (in milliseconds) between the press and release events of a tapped behavior in macros"
default 30

config ZMK_SEND_STRING_DEFAULT_WAIT_MS
int "Default time to wait (in milliseconds) before pressing the next key in the text"
default 0

config ZMK_SEND_STRING_DEFAULT_TAP_MS
int "Default time to wait (in milliseconds) between the press and release for each key in the text"
default 5

endmenu

menu "Advanced"
Expand Down Expand Up @@ -708,6 +717,14 @@ config USB_DEVICE_STACK
config FPU
default CPU_HAS_FPU

config ZMK_SEND_STRING
bool "Enable send string function"

config ZMK_CHARACTER_MAP
bool
default y
depends on DT_HAS_ZMK_CHARACTER_MAP_ENABLED

config ZMK_WPM
bool "Calculate WPM"

Expand Down
6 changes: 6 additions & 0 deletions app/Kconfig.behaviors
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,9 @@ config ZMK_BEHAVIOR_MACRO
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_MACRO_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_ONE_PARAM_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_TWO_PARAM_ENABLED

config ZMK_BEHAVIOR_SEND_STRING
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_SEND_STRING_ENABLED
select ZMK_SEND_STRING
2 changes: 2 additions & 0 deletions app/dts/behaviors.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@
#include <behaviors/mouse_key_press.dtsi>
#include <behaviors/soft_off.dtsi>
#include <behaviors/studio_unlock.dtsi>
#include <behaviors/character_map.dtsi>
#include <behaviors/send_string.dtsi>
116 changes: 116 additions & 0 deletions app/dts/behaviors/character_map.dtsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#include <dt-bindings/zmk/keys.h>

/ {
// Codepoint to keycode mapping for a US keyboard layout.
/omit-if-no-ref/ charmap_us: character_map_us {
compatible = "zmk,character-map";
behavior = <&kp>;
map = <0x08 BACKSPACE>
, <0x0A RETURN>
, <0x0B TAB>
, <0x20 SPACE>
, <0x21 EXCLAMATION>
, <0x22 DOUBLE_QUOTES>
, <0x23 HASH>
, <0x24 DOLLAR>
, <0x25 PERCENT>
, <0x26 AMPERSAND>
, <0x27 APOSTROPHE>
, <0x28 LEFT_PARENTHESIS>
, <0x29 RIGHT_PARENTHESIS>
, <0x2A ASTERISK>
, <0x2B PLUS>
, <0x2C COMMA>
, <0x2D MINUS>
, <0x2E PERIOD>
, <0x2F SLASH>
, <0x30 N0>
, <0x31 N1>
, <0x32 N2>
, <0x33 N3>
, <0x34 N4>
, <0x35 N5>
, <0x36 N6>
, <0x37 N7>
, <0x38 N8>
, <0x39 N9>
, <0x3A COLON>
, <0x3B SEMICOLON>
, <0x3C LESS_THAN>
, <0x3D EQUAL>
, <0x3E GREATER_THAN>
, <0x3F QUESTION>
, <0x40 AT_SIGN>
, <0x41 LS(A)>
, <0x42 LS(B)>
, <0x43 LS(C)>
, <0x44 LS(D)>
, <0x45 LS(E)>
, <0x46 LS(F)>
, <0x47 LS(G)>
, <0x48 LS(H)>
, <0x49 LS(I)>
, <0x4A LS(J)>
, <0x4B LS(K)>
, <0x4C LS(L)>
, <0x4D LS(M)>
, <0x4E LS(N)>
, <0x4F LS(O)>
, <0x50 LS(P)>
, <0x51 LS(Q)>
, <0x52 LS(R)>
, <0x53 LS(S)>
, <0x54 LS(T)>
, <0x55 LS(U)>
, <0x56 LS(V)>
, <0x57 LS(W)>
, <0x58 LS(X)>
, <0x59 LS(Y)>
, <0x5A LS(Z)>
, <0x5B LEFT_BRACKET>
, <0x5C BACKSLASH>
, <0x5D RIGHT_BRACKET>
, <0x5E CARET>
, <0x5F UNDERSCORE>
, <0x60 GRAVE>
, <0x61 A>
, <0x62 B>
, <0x63 C>
, <0x64 D>
, <0x65 E>
, <0x66 F>
, <0x67 G>
, <0x68 H>
, <0x69 I>
, <0x6A J>
, <0x6B K>
, <0x6C L>
, <0x6D M>
, <0x6E N>
, <0x6F O>
, <0x70 P>
, <0x71 Q>
, <0x72 R>
, <0x73 S>
, <0x74 T>
, <0x75 U>
, <0x76 V>
, <0x77 W>
, <0x78 X>
, <0x79 Y>
, <0x7A Z>
, <0x7B LEFT_BRACE>
, <0x7C PIPE>
, <0x7D RIGHT_BRACE>
, <0x7E TILDE>
, <0x7F DELETE>
;
};
};

13 changes: 13 additions & 0 deletions app/dts/behaviors/send_string.dtsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#define ZMK_SEND_STRING(name, string, ...) \
name: name { \
compatible = "zmk,behavior-send-string"; \
#binding-cells = <0>; \
text = string; \
__VA_ARGS__ \
};
26 changes: 26 additions & 0 deletions app/dts/bindings/behaviors/zmk,behavior-send-string.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: MIT

description: Send String Behavior

compatible: "zmk,behavior-send-string"

include: zero_param.yaml

properties:
text:
type: string
required: true
description: The text to send.

wait-ms:
type: int
description: The time to wait (in milliseconds) before pressing the next key in the text.

tap-ms:
type: int
description: The time to wait (in milliseconds) between the press and release of each key in the text.

charmap:
type: phandle
description: A zmk,character-map instance to use. If omitted, the zmk,charmap chosen node is used.
28 changes: 28 additions & 0 deletions app/dts/bindings/zmk,character-map.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright (c) 2023, The ZMK Contributors
# SPDX-License-Identifier: MIT

description: Unicode codepoint to behavior binding mapping

compatible: "zmk,character-map"

properties:
behavior:
type: phandle
required: true
description: |
Behavior to use for a code point in the mapping (typically <&kp>).
The behavior is given one parameter which is the value for the code point
from the "map" property.
unicode-behavior:
type: phandle
description: |
Optional behavior to use for a code point not in the mapping.
The behavior is given one parameter which is the code point.
map:
type: array
required: true
description: |
List of <codepoint param> pairs. Each pair maps a codepoint to a parameter
given to "behavior" to type that code point.
66 changes: 66 additions & 0 deletions app/include/drivers/character_map.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#pragma once

#include <stddef.h>
#include <sys/errno.h>
#include <zephyr/device.h>
#include <zephyr/toolchain/common.h>
#include <zmk/behavior.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @cond INTERNAL_HIDDEN
*
* Character map driver API definition and system call entry points.
*
* (Internal use only.)
*/

typedef int (*character_map_codepoint_to_binding_t)(const struct device *device, uint32_t codepoint,
struct zmk_behavior_binding *binding);

__subsystem struct character_map_driver_api {
character_map_codepoint_to_binding_t codepoint_to_binding;
};
/**
* @endcond
*/

/**
* @brief Map a Unicode codepoint to a behavior binding.
* @param charmap Pointer to the device structure for the driver instance.
* @param codepoint Unicode codepoint to map.
* @param binding Corresponding behavior binding is written here if successful.
*
* @retval 0 If successful.
* @retval Negative errno code if failure.
*/
__syscall int character_map_codepoint_to_binding(const struct device *charmap, uint32_t codepoint,
struct zmk_behavior_binding *binding);

static inline int z_impl_character_map_codepoint_to_binding(const struct device *charmap,
uint32_t codepoint,
struct zmk_behavior_binding *binding) {
const struct character_map_driver_api *api =
(const struct character_map_driver_api *)charmap->api;

if (api->codepoint_to_binding == NULL) {
return -ENOTSUP;
}

return api->codepoint_to_binding(charmap, codepoint, binding);
}

#ifdef __cplusplus
}
#endif

#include <syscalls/character_map.h>
Loading

0 comments on commit 2370be1

Please sign in to comment.