Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(behaviors): Add a send string behavior #1893

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

joelspadin
Copy link
Collaborator

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, and 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.

@joelspadin joelspadin requested a review from a team as a code owner August 6, 2023 04:39
@caksoylar
Copy link
Contributor

I will do a second pass later but the docs look great, thank you for the effort you put into it! One suggestion I'd have is to put a pointer in the macros page pointing to this behavior for string sending purposes, similar to the pointer we have for modifier functions.

@jhorology
Copy link
Contributor

I've been waiting for this function. If there is a printf-style format function, I would be more than happy. Once I read the zephyr's code related to printf, It's difficult to understand to me.

@joelspadin
Copy link
Collaborator Author

One suggestion I'd have is to put a pointer in the macros page pointing to this behavior for string sending purposes, similar to the pointer we have for modifier functions.

Good idea. I'll add that.

I've been waiting for this function. If there is a printf-style format function, I would be more than happy. Once I read the zephyr's code related to printf, It's difficult to understand to me.

You could use the zmk_send_string() function with printf by printing to a string buffer and then sending that to the function, for example:

struct zmk_send_string_config config = {
    .character_map = DEVICE_DT_GET(DT_CHOSEN(zmk_character_map)),
    .wait_ms = CONFIG_ZMK_SEND_STRING_DEFAULT_WAIT_MS,
    .tap_ms = CONFIG_ZMK_SEND_STRING_DEFAULT_TAP_MS,
};

char string[32];
snprintf(string, sizeof(string), "Battery level is %u%%", zmk_battery_state_of_charge());

zmk_send_string(&config, position, string);

@joelspadin joelspadin force-pushed the send-string-behavior branch from 6a931b7 to 4ec46cd Compare August 7, 2023 15:41
@joelspadin
Copy link
Collaborator Author

I added some macros for creating the config structs, so the code example above can now be simplified to

ZMK_BUILD_ASSERT_CHARACTER_MAP_CHOSEN();
...

char string[32];
snprintf(string, sizeof(string), "Battery level is %u%%", zmk_battery_state_of_charge());

zmk_send_string(&ZMK_SEND_STRING_CONFIG_DEFAULT, position, string);

docs/docs/config/behaviors.md Show resolved Hide resolved
docs/docs/behaviors/send-string.md Outdated Show resolved Hide resolved
docs/docs/behaviors/send-string.md Outdated Show resolved Hide resolved
@caksoylar
Copy link
Contributor

LGTM from a docs perspective 👍

@minhe7735
Copy link

pr broken because of this? 690bc1b

@dryqin
Copy link

dryqin commented Dec 15, 2023

Will be great if this gets merged!

@joelspadin joelspadin force-pushed the send-string-behavior branch from 320c327 to 7ce38da Compare March 21, 2024 04:57
@joelspadin
Copy link
Collaborator Author

Rebased and fixed everything to work with current ZMK.

@joelspadin joelspadin force-pushed the send-string-behavior branch from 7ce38da to 76ffce6 Compare April 7, 2024 20:05
@prez
Copy link

prez commented May 27, 2024

I've been eagerly waiting for this for almost a year now. Is there any chance we'll get to see this merged soon? Thank you!

@joelspadin joelspadin requested a review from a team as a code owner September 15, 2024 00:13
@joelspadin
Copy link
Collaborator Author

Rebased and cleaned up a few things. Added a workaround to repurpose \f as the new line character, since \n gets inserted into the generated .h file as an actual line break and breaks the build.

@joelspadin
Copy link
Collaborator Author

It looks like while \" and \\ do work correctly in generated C code, they break the generated cmake code... I'll see if I can contribute a patch to Zephyr to fix that.

@joelspadin
Copy link
Collaborator Author

Removed the \f workaround. This now requires the fix at zephyrproject-rtos/zephyr#78433 to use any of \" \\ \r \n in a string.

@joelspadin joelspadin requested a review from a team September 20, 2024 23:45
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.
@joelspadin
Copy link
Collaborator Author

joelspadin commented Sep 29, 2024

The fact that the previous version still built despite passing a uint32_t to the first parameter of zmk_behavior_queue_add, which had been changed from a uint32_t to a pointer, is a bit concerning...

@joelspadin
Copy link
Collaborator Author

We will need to cherry-pick zephyrproject-rtos/zephyr@c82799b and zephyrproject-rtos/zephyr@6edefd8 for all strings to work correctly.

@caksoylar
Copy link
Contributor

@zhiayang
Copy link
Contributor

Has this been tested to work correctly for "super-long" strings (>200 chars)? In my experience it seems like something gets stuck at around 100-150 characters, even if I set the queue size to 256.

I got it to work (locally) by queueing the zmk_behavior_queue_add press & release calls into a work queue with an additional k_sleep after sending one character. Suboptimal, but I don't really have time to probe further here.

@joelspadin
Copy link
Collaborator Author

I have not tested it with sizes that large, but the behavior you described seems expected given how it currently works. The queue size needs to be 2x the number of characters, because each character has a press and a release. If you want to send 200 characters, you will need a queue size of 400.

I do think the way it works currently is suboptimal, but the only better solution I can think of is to add a way to insert a message into the behavior queue which does not trigger a behavior press/release but instead calls a callback so a behavior can queue another chunk of items.

@ssbb
Copy link

ssbb commented Dec 6, 2024

Should it be KERNEL_INIT_PRIORITY_DEFAULT instead of APPLICATION_INIT_PRIORITY? All the existing behaviors using KERNEL_INIT_PRIORITY_DEFAULT now and it fails when trying to call send-string behavior from them (because higher priority depends on lower).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants