Skip to content

Add support for international layouts #53

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

Merged
merged 6 commits into from
Nov 4, 2021

Conversation

edgar-bonet
Copy link
Collaborator

This pull request adds support for multiple keyboard layouts. Five layouts are initially included: US, French, Italian, German and Spanish. It is expected that users contribute additional layouts (I can add a few if requested). Adding a layout is straightforward by following the instructions in KeyboardLayout.h. It only requires one file defining it, and one line added to Keyboard.h.

Context

Arduino was born in Italy and has become very popular all over the world. The USA is only one among many countries that show interest in Arduino, and yet, the official Arduino Keyboard library supports exclusively the US keyboard layout. This has caused much confusion (#7, #20, #23, #24) and user requests (#8, #21, #25, #50, #52).

In the past, requests to support international layouts have been rejected with two arguments:

  1. this library should stay simple and small
  2. NicoHood's HID library is available for those needing advanced features.

Regarding point 2, it should be noted that:

  • For the majority of Arduino users (mostly everybody outside the USA), support for a keyboard layout other than US is by no means an “advanced feature”: it is the most basic way of using any keyboard-like device.
  • The HID library is very limited in its support of keyboard layouts, as the API does not support the AltGr modifier.

This pull request addresses this need while keeping the code simple and small.

API change

The only change to the public API is that the begin() method now accepts an optional parameter specifying the requested keyboard layout, e.g.

Keyboard.begin(KeyboardLayout_fr_FR);

As an optional parameter, it defaults to KeyboardLayout_en_US, thus preserving compatibility with existing sketches.

Even though calling begin() is mandatory according to the official documentation, this method has been a noop until now. In order to avoid breaking existing sketches that fail to begin(), the press() method now calls begin() if it hasn't been called before.

Resource usage

For a sketch using the default layout, the cost of this change is 100 bytes of flash and 2 bytes of RAM, as measured on an Arduino Micro. This doesn't change as the number of supported layouts increases.

Using a layout other than the default costs an extra 128 bytes of flash. This is because the default layout is always loaded in order to handle the case where the user forgets to begin() before using the library. It could be discussed whether supporting sketches that fail to begin() is worth the cost.

Limitations

Keeping this library simple and small is a must. Because of this, this feature comes with some limitations:

  1. Only ASCII characters are supported. Supporting non-ASCII characters would require parsing UTF-8 input, which would add significant complexity (see, e.g. a library for the german keyboard-layout capable of sending keystrokes for ALL characters #52).
  2. While both the Shift and AltGr modifiers are supported, using both simultaneously for typing a single character is not supported. Supporting this would not add complexity, but it would require growing the ASCII map to 16 bits per cell (as in Ability to use right alt aka. alt gr to reach all characters on non us/uk layouts #50), which would cost 128 extra bytes of flash.
  3. Characters requiring hitting multiple keys consecutively (like dead circumflex + space → ^ on some layouts) are not supported for the same reason. They also break the simple assumption that write(character) equals press(character) followed by release(character).

The Keyboard.begin() method now accepts a keyboard layout as an optional
argument, which defaults to KeyboardLayout_en_US. The layout data has
been moved to a separate file.

Right now, en_US is the only available layout, but extra layouts can be
defined by adding the appropriate definition files and declaring them in
Keyboard.h.
This layout lacks the characters:

 - '`' and '~', which are not part of the standard Italian layout
 - '{' and '}', which require AltGr+Shift (not supported)
This layout lacks the characters '^' and '`', as they require pressing
Space after a dead key, which is not supported.
This layout lacks the characters '^', '`' and '~', as they require
pressing Space after a dead key, which is not supported.
@CLAassistant
Copy link

CLAassistant commented Oct 20, 2021

CLA assistant check
All committers have signed the CLA.

@github-actions
Copy link

Memory usage change @ ac0291b

Board flash % RAM for global variables %
arduino:avr:leonardo 🔺 +100 - +100 +0.35 - +0.35 🔺 +2 - +2 +0.08 - +0.08
arduino:sam:arduino_due_x_dbg 🔺 +72 - +72 +0.01 - +0.01 N/A N/A
arduino:samd:mkrzero 🔺 +76 - +76 +0.03 - +0.03 🔺 +4 - +4 +0.01 - +0.01
Click for full report table
Board examples/Serial
flash
% examples/Serial
RAM for global variables
%
arduino:avr:leonardo 100 0.35 2 0.08
arduino:sam:arduino_due_x_dbg 72 0.01 N/A N/A
arduino:samd:mkrzero 76 0.03 4 0.01
Click for full report CSV
Board,examples/Serial<br>flash,%,examples/Serial<br>RAM for global variables,%
arduino:avr:leonardo,100,0.35,2,0.08
arduino:sam:arduino_due_x_dbg,72,0.01,N/A,N/A
arduino:samd:mkrzero,76,0.03,4,0.01

@edgar-bonet
Copy link
Collaborator Author

Please note that the checks that failed did not fail because of the changes introduced by this pull request (they also fail on master). They fail because the CI pipeline attempts to compile the examples for platforms that do not provide the HID.h header file.

@per1234 per1234 added the type: enhancement Proposed improvement label Oct 20, 2021
@facchinm
Copy link
Contributor

LOVE THIS PR 😍
Really, thank you for picking this up! I think the begin() overload with the default on press() is very sensible and is worth the extra flash occupation.
@agdl , would you mind testing it?

@facchinm facchinm requested a review from agdl October 20, 2021 12:59
This obviates the need to test in press() whether it has been
initialized, saving a few cycles and a few bytes of flash.
@edgar-bonet
Copy link
Collaborator Author

Has the review of this PR started?

I just realized that setting

_asciimap = KeyboardLayout_en_US;

in the constructor obviates the need to test within press() that the pointer is not null. This would save three lines of code and 12 bytes of flash (on AVR). Should I add this change now although the pull request is already open?

@facchinm
Copy link
Contributor

Please, add it to this PR 🙂

@edgar-bonet
Copy link
Collaborator Author

OK, done.

@github-actions
Copy link

Memory usage change @ c609d4a

Board flash % RAM for global variables %
arduino:avr:leonardo 🔺 +88 - +88 +0.31 - +0.31 🔺 +2 - +2 +0.08 - +0.08
arduino:sam:arduino_due_x_dbg 🔺 +64 - +64 +0.01 - +0.01 N/A N/A
arduino:samd:mkrzero 🔺 +64 - +64 +0.02 - +0.02 🔺 +4 - +4 +0.01 - +0.01
Click for full report table
Board examples/Serial
flash
% examples/Serial
RAM for global variables
%
arduino:avr:leonardo 88 0.31 2 0.08
arduino:sam:arduino_due_x_dbg 64 0.01 N/A N/A
arduino:samd:mkrzero 64 0.02 4 0.01
Click for full report CSV
Board,examples/Serial<br>flash,%,examples/Serial<br>RAM for global variables,%
arduino:avr:leonardo,88,0.31,2,0.08
arduino:sam:arduino_due_x_dbg,64,0.01,N/A,N/A
arduino:samd:mkrzero,64,0.02,4,0.01

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: code Related to content of the project itself type: enhancement Proposed improvement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants