extended `key_functions__type_string()` to use keycodes

when possible, and "unicode sequences" otherwise.  made the function
enormous, but also much more useful :)
partial-rewrite
Ben Blazak 2013-06-14 15:52:47 -07:00
parent a89e6528f2
commit 952c2792ed
2 changed files with 173 additions and 116 deletions

View File

@ -29,9 +29,9 @@ void key_functions__toggle (uint8_t keycode);
void key_functions__jump_to_bootloader (void);
// special
void key_functions__toggle_capslock (void);
void key_functions__type_byte_hex (uint8_t byte);
void key_functions__send_unicode_sequence (const char * string);
void key_functions__toggle_capslock (void);
void key_functions__type_byte_hex (uint8_t byte);
void key_functions__type_string (const char * string);
// ----------------------------------------------------------------------------
@ -105,25 +105,38 @@ void key_functions__send_unicode_sequence (const char * string);
* - `byte`: The byte to send a representation of
*/
// === key_functions__send_unicode_sequence() ===
/** functions/key_functions__send_unicode_sequence/description
* Send the "unicode sequence" for each character in `string`
*
* This function is, relative to the rest of life, extremely unportable. Sorry
* about that: I looked, but I couldn't find a better way to do it. I'm
* including it in the hope that it will be useful anyway.
// === key_functions__type_string() ===
/** functions/key_functions__type_string/description
* Type the keycode (or "unicode sequence") for each character in `string`
*
* Arguments:
* - `string`: A pointer to a valid UTF-8 string in PROGMEM
*
*
* Operating system considerations:
* Notes:
*
* - This function should work on OS X and Windows (after enabling "hexidecimal
* code input" in the OS), but probably will not work on Linux (2 out of 3 of
* the major OSs is better than nothing...). If you're using Linux and you
* want to flip that around :) please modify the function to send Linux
* friendly start and end sequences. See [this Wikipedia article]
* - A "unicode sequence" is holding down "alt", typing "+", typing the 4
* character hexadecimal unicode code for the specified character, then
* releasing "alt". This is done for every character in `string` for which a
* dedicated USB keycode has not been specified.
*
* - This function is, relative to the rest of life, extremely unportable.
* Sorry about that: I looked for a better way to do things, but I couldn't
* find one. It may be very convenient under certain circumstances though,
* if one is willing to take a little time to understand what it's actually
* doing, so I'm including it in the hope that it will be. Please see the
* implementation (in the '.c' file) for which keycodes are being sent for a
* given character.
*
*
* Operating system considerations (for typing "unicode sequences"):
*
* - "Unicode sequences", as implemented, should work on OS X and Windows
* (after enabling "hexidecimal code input" in the OS), but probably will not
* work on Linux (2 out of 3 of the major OSs is better than nothing...). If
* you're using Linux and you want to flip that around :) please modify the
* function to send Linux friendly start and end sequences. See [this
* Wikipedia article]
* (http://en.wikipedia.org/wiki/Unicode_input#Hexadecimal_code_input) for
* more information.
*
@ -144,34 +157,29 @@ void key_functions__send_unicode_sequence (const char * string);
*
* Usage notes:
*
* - This function disables all modifier keys on entry, and restores their
* state on exit.
*
* - A "unicode sequence" is holding down "alt", typing "+", typing the 4
* character unicode code for the specified character, then releasing "alt".
* This is done for every character in `string`, even the ones with a
* dedicated USB keycode.
*
* - Characters (and strings) sent with this function do not automatically
* repeat (as normal keys do).
*
* - If you're holding down any of `[0-9A-F]` when this function is called, it
* may not do what you want.
* - Please pay very special attention to what this function is actually typing
* if you are using any language besides English as the default in your OS,
* or if you have characters that aren't 7-bit ASCII in your string and have
* not enabled "hexidecimal code input" in your OS.
*
* - This function does not disable modifier keys, or any other keys, on entry:
* keys are pressed and released as they would be if one was typing them
* starting with no keys pressed. If you are holding down any keys when this
* function is called, it *may* not do what you expect, depending on which
* keys are being held down and which keys this function is trying to press
* and release.
*
* - An easy way to pass a PROGMEM string to this function is to use the
* `PSTR()` macro in `<avr/pgmspace.h>`, as in
*
* key_functions__send_unicode_sequence( PSTR ( "" ) );
* key_functions__type_string( PSTR( "" ) );
*
* or
*
* key_functions__send_unicode_sequence( PSTR (
* key_functions__type_string( PSTR(
* "こんにちは世界 γειά σου κόσμε hello world ^_^" ) );
*
* - It's probably better to define a proper macro key than to use this
* function for sending sequences of "normal" characters, despite the
* relative inconvenience. But... if you're not concerned about portability,
* or other factors that might arise because of what this function is
* *actually* typing... it's possible to do it this way to... :) lol
*/

View File

@ -6,13 +6,6 @@
/** description
* Implements the "special" section of "../key-functions.h"
*
* Notes:
* - If the USB keyboard modifier state functions turn out to be something that
* would be generally useful, the functionality should be reimplemented in
* ".../firmware/lib/usb" (and removed from here). For now I'm leaving them
* here because it seems better not to encourage messing with modifiers as a
* special group of keys, except for special purposes.
*/
@ -25,70 +18,18 @@
// ----------------------------------------------------------------------------
/** types/modifier_state_t/description
* A struct representing the state of the keyboard modifier keys
*/
struct modifier_state_t {
bool left_control : 1;
bool left_shift : 1;
bool left_alt : 1;
bool left_gui : 1;
bool right_control : 1;
bool right_shift : 1;
bool right_alt : 1;
bool right_gui : 1;
};
// ----------------------------------------------------------------------------
/** functions/read_modifier_state/description
* Return the state of the modifier keys
*
* Returns:
* - success: A `modifier_state_t`
*/
static struct modifier_state_t read_modifier_state(void) {
return (struct modifier_state_t) {
.left_control = usb__kb__read_key( KEYBOARD__LeftControl ),
.left_shift = usb__kb__read_key( KEYBOARD__LeftShift ),
.left_alt = usb__kb__read_key( KEYBOARD__LeftAlt ),
.left_gui = usb__kb__read_key( KEYBOARD__LeftGUI ),
.right_control = usb__kb__read_key( KEYBOARD__RightControl ),
.right_shift = usb__kb__read_key( KEYBOARD__RightShift ),
.right_alt = usb__kb__read_key( KEYBOARD__RightAlt ),
.right_gui = usb__kb__read_key( KEYBOARD__RightGUI ),
};
}
/** functions/set_modifier_state/description
* Set the state of the modifier keys to `state`
*
* Arguments:
* - `state`: A `modifier_state_t`
*/
static void set_modifier_state(struct modifier_state_t state) {
usb__kb__set_key( state.left_control , KEYBOARD__LeftControl );
usb__kb__set_key( state.left_shift , KEYBOARD__LeftShift );
usb__kb__set_key( state.left_alt , KEYBOARD__LeftAlt );
usb__kb__set_key( state.left_gui , KEYBOARD__LeftGUI );
usb__kb__set_key( state.right_control , KEYBOARD__RightControl );
usb__kb__set_key( state.right_shift , KEYBOARD__RightShift );
usb__kb__set_key( state.right_alt , KEYBOARD__RightAlt );
usb__kb__set_key( state.right_gui , KEYBOARD__RightGUI );
usb__kb__send_report();
}
// ----------------------------------------------------------------------------
void key_functions__toggle_capslock(void) {
struct modifier_state_t state = read_modifier_state();
// -------
struct modifier_state_t temp_state = state;
temp_state.left_shift = false;
temp_state.right_shift = false;
// -------
set_modifier_state(temp_state);
// save the state of both shifts, and disable them
struct {
bool left_shift : 1;
bool right_shift : 1;
} state = {
.left_shift = usb__kb__read_key( KEYBOARD__LeftShift ),
.right_shift = usb__kb__read_key( KEYBOARD__RightShift ),
};
usb__kb__set_key( false, KEYBOARD__LeftShift );
usb__kb__set_key( false, KEYBOARD__RightShift );
usb__kb__send_report();
// toggle capslock
usb__kb__set_key(true, KEYBOARD__CapsLock);
@ -96,7 +37,10 @@ void key_functions__toggle_capslock(void) {
usb__kb__set_key(false, KEYBOARD__CapsLock);
usb__kb__send_report();
set_modifier_state(state);
// restore the state of both shifts
usb__kb__set_key( state.left_shift, KEYBOARD__LeftShift );
usb__kb__set_key( state.right_shift, KEYBOARD__RightShift );
usb__kb__send_report();
}
void key_functions__type_byte_hex(uint8_t byte) {
@ -115,7 +59,7 @@ void key_functions__type_byte_hex(uint8_t byte) {
usb__kb__send_report();
}
/** functions/key_functions__send_unicode_sequence/description
/** functions/key_functions__type_string/description
* Implementation notes:
*
* - We use `uint8_t` instead of `char` when iterating over `string` because
@ -137,14 +81,8 @@ void key_functions__type_byte_hex(uint8_t byte) {
* 0x0800 - 0xFFFF 16 1110xxxx 10xxxxxx 10xxxxxx
* 0x010000 - 0x10FFFF 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
* ----------------------------------------------------------------------
*
* TODO: change the name of this function, and extend it to type printable
* non-extended ASCII directly
*/
void key_functions__send_unicode_sequence(const char * string) {
struct modifier_state_t state = read_modifier_state();
set_modifier_state( (struct modifier_state_t){} );
void key_functions__type_string(const char * string) {
uint8_t c; // for storing the current byte of the character
uint16_t c_full; // for storing the full character
@ -154,7 +92,7 @@ void key_functions__send_unicode_sequence(const char * string) {
// get character
if (c >> 7 == 0b0) {
// a 1-byte utf-8 character
c_full = c & 0x7F;
c_full = c;
} else if (c >> 5 == 0b110) {
// beginning of a 2-byte utf-8 character
@ -182,6 +120,119 @@ void key_functions__send_unicode_sequence(const char * string) {
continue;
}
// --- (if possible) send regular keycode ---
if (c == c_full) {
bool shifted = false;
uint8_t keycode = 0;
if (c == 0x30) {
keycode = KEYBOARD__0_RightParenthesis; // 0
} else if (0x31 <= c && c <= 0x39) {
keycode = KEYBOARD__1_Exclamation + c - 0x31; // 1..9
} else if (0x41 <= c && c <= 0x5A) {
shifted = true;
keycode = KEYBOARD__a_A + c - 0x41; // A..Z
} else if (0x61 <= c && c <= 0x7A) {
keycode = KEYBOARD__a_A + c - 0x61; // a..z
} else switch (c) {
// control characters
case 0x08: keycode = KEYBOARD__DeleteBackspace; break; // BS
case 0x09: keycode = KEYBOARD__Tab; break; // VT
case 0x0A: keycode = KEYBOARD__ReturnEnter; break; // LF
case 0x0D: keycode = KEYBOARD__ReturnEnter; break; // CR
case 0x1B: keycode = KEYBOARD__Escape; break; // ESC
// printable characters
case 0x20: keycode = KEYBOARD__Spacebar; break; // ' '
case 0x21: shifted = true;
keycode = KEYBOARD__1_Exclamation; break; // !
case 0x22: shifted = true;
keycode = KEYBOARD__SingleQuote_DoubleQuote;
break; // "
case 0x23: shifted = true;
keycode = KEYBOARD__3_Pound; break; // #
case 0x24: shifted = true;
keycode = KEYBOARD__4_Dollar; break; // $
case 0x25: shifted = true;
keycode = KEYBOARD__5_Percent; break; // %
case 0x26: shifted = true;
keycode = KEYBOARD__7_Ampersand; break; // &
case 0x27: keycode = KEYBOARD__SingleQuote_DoubleQuote;
break; // '
case 0x28: shifted = true;
keycode = KEYBOARD__9_LeftParenthesis;
break; // (
case 0x29: shifted = true;
keycode = KEYBOARD__0_RightParenthesis;
break; // )
case 0x2A: shifted = true;
keycode = KEYBOARD__8_Asterisk; break; // *
case 0x2B: shifted = true;
keycode = KEYBOARD__Equal_Plus; break; // +
case 0x2C: keycode = KEYBOARD__Comma_LessThan; break; // ,
case 0x2D: keycode = KEYBOARD__Dash_Underscore; break; // -
case 0x2E: keycode = KEYBOARD__Period_GreaterThan;
break; // .
case 0x2F: keycode = KEYBOARD__Slash_Question; break; // /
// ... numbers
case 0x3A: shifted = true;
keycode = KEYBOARD__Semicolon_Colon; break; // :
case 0x3B: keycode = KEYBOARD__Semicolon_Colon; break; // ;
case 0x3C: shifted = true;
keycode = KEYBOARD__Comma_LessThan; break; // <
case 0x3D: keycode = KEYBOARD__Equal_Plus; break; // =
case 0x3E: shifted = true;
keycode = KEYBOARD__Period_GreaterThan;
break; // >
case 0x3F: shifted = true;
keycode = KEYBOARD__Slash_Question; break; // ?
case 0x4D: shifted = true;
keycode = KEYBOARD__2_At; break; // @
// ... uppercase letters
case 0x5B: keycode = KEYBOARD__LeftBracket_LeftBrace;
break; // [
case 0x5C: keycode = KEYBOARD__Backslash_Pipe; break; // '\'
case 0x5D: keycode = KEYBOARD__RightBracket_RightBrace;
break; // ]
case 0x5E: shifted = true;
keycode = KEYBOARD__6_Caret; break; // ^
case 0x5F: shifted = true;
keycode = KEYBOARD__Dash_Underscore; break; // _
case 0x60: keycode = KEYBOARD__GraveAccent_Tilde;
break; // `
// ... lowercase letters
case 0x7B: shifted = true;
keycode = KEYBOARD__LeftBracket_LeftBrace;
break; // {
case 0x7C: shifted = true;
keycode = KEYBOARD__Backslash_Pipe; break; // |
case 0x7D: shifted = true;
keycode = KEYBOARD__RightBracket_RightBrace;
break; // }
case 0x7E: shifted = true;
keycode = KEYBOARD__GraveAccent_Tilde;
break; // ~
case 0x7F: keycode = KEYBOARD__DeleteForward; break; // DEL
}
if (keycode) {
// press keycode
if (shifted) usb__kb__set_key(true, KEYBOARD__LeftShift);
usb__kb__set_key(true, keycode);
usb__kb__send_report();
// release keycode
if (shifted) usb__kb__set_key(false, KEYBOARD__LeftShift);
usb__kb__set_key(false, keycode);
usb__kb__send_report();
continue;
}
}
// --- (otherwise) send unicode sequence ---
// send start sequence
usb__kb__set_key(true, KEYBOARD__LeftAlt ); usb__kb__send_report();
usb__kb__set_key(true, KEYBOARD__Equal_Plus); usb__kb__send_report();
@ -194,7 +245,5 @@ void key_functions__send_unicode_sequence(const char * string) {
// send end sequence
usb__kb__set_key(false, KEYBOARD__LeftAlt); usb__kb__send_report();
}
set_modifier_state(state);
}