aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvchtchetkine <vchtchetkine@google.com>2009-09-14 15:29:20 -0700
committervchtchetkine <vchtchetkine@google.com>2009-09-21 08:37:25 -0700
commit9085a28c14f369d231dbae099a690689179f428a (patch)
treeeffe3ef60eeb2982f4c29361c7828174e5bd841b
parent238b4b0ef1a01afa66ef267dae4a96401ad386db (diff)
downloadexternal_qemu-9085a28c14f369d231dbae099a690689179f428a.zip
external_qemu-9085a28c14f369d231dbae099a690689179f428a.tar.gz
external_qemu-9085a28c14f369d231dbae099a690689179f428a.tar.bz2
Implementation for dynamic charmap option in emulator.
Created .kcm parser and added -charmap option to the emulator, so user can specify keyboard layout for emulator session.
-rw-r--r--android/charmap.c588
-rw-r--r--android/charmap.h50
-rw-r--r--android/cmdline-options.h2
-rw-r--r--android/help.c9
-rw-r--r--android/main.c20
-rw-r--r--android/skin/keyboard.c57
-rw-r--r--android/skin/keyboard.h2
-rw-r--r--gen-charmap.py24
8 files changed, 713 insertions, 39 deletions
diff --git a/android/charmap.c b/android/charmap.c
index c8ed2d6..0ccd6dd 100644
--- a/android/charmap.c
+++ b/android/charmap.c
@@ -9,8 +9,126 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
+#include "qemu-common.h"
+#include "android/utils/path.h"
+#include "android/utils/misc.h"
+#include "android/utils/debug.h"
#include "android/charmap.h"
+/* Parses .kcm file producing key characters map.
+ * .kcm file parsed by this module is expected to contain 4 types of
+ * lines:
+ * 1. An empty line (containing no characters, or only space or tab
+ * characters).
+ * 2. A comment line (begins with '#')
+ * 3. A type section line (begins with '[')
+ * 4. Character map line, formatted as such:
+ * Key code value, followed by one or more space or tab characters.
+ * Display value, followed by one or more space or tab characters.
+ * Number value, followed by one or more space or tab characters.
+ * Base value, followed by one or more space or tab characters.
+ * Caps value, followed by one or more space or tab characters.
+ * Fn value, followed by one or more space or tab characters.
+ * Caps_fn value, followed by one or more space or tab characters.
+ * All values, except for the key code value must be either in character
+ * form ('X', where X is the value), or in hexadecimal form (0xXXXX, where
+ * XXXX is hexadecimal representation of the value). Note that if value is
+ * in hexadecimal form, it must not exceed value that can be contained in
+ * variable of 'unsigned short' type.
+ * Bellow are a couple of examples of valid .kcm file lines:
+ * # keycode display number base caps fn caps_fn
+ * A 'A' '2' 'a' 'A' '#' 0x00
+ * PERIOD '.' '.' '.' ':' ':' 0x2026
+ * SPACE 0x20 0x20 0x20 0x20 0xEF01 0xEF01
+*/
+
+/* Maximum length of a line expected in .kcm file. */
+#define KCM_MAX_LINE_LEN 1024
+
+/* Maximum length of a token in a key map line. */
+#define KCM_MAX_TOKEN_LEN 512
+
+/* Maps symbol name from .kcm file to a keycode value. */
+typedef struct AKeycodeMapEntry {
+ /* Symbol name from .kcm file. */
+ const char* key_name;
+
+ /* Key code value for the symbol. */
+ int key_code;
+} AKeycodeMapEntry;
+
+/* Result of parsing a line in a .kcm file. */
+typedef enum {
+ /* Line format was bad. */
+ BAD_FORMAT,
+
+ /* Line had been skipped (an empty line, or a comment, etc.). */
+ SKIP_LINE,
+
+ /* Line represents an entry in the key map. */
+ KEY_ENTRY,
+} ParseStatus;
+
+static const AKeycodeMapEntry keycode_map[] = {
+ /* Symbol Key code */
+
+ { "A", kKeyCodeA },
+ { "B", kKeyCodeB },
+ { "C", kKeyCodeC },
+ { "D", kKeyCodeD },
+ { "E", kKeyCodeE },
+ { "F", kKeyCodeF },
+ { "G", kKeyCodeG },
+ { "H", kKeyCodeH },
+ { "I", kKeyCodeI },
+ { "J", kKeyCodeJ },
+ { "K", kKeyCodeK },
+ { "L", kKeyCodeL },
+ { "M", kKeyCodeM },
+ { "N", kKeyCodeN },
+ { "O", kKeyCodeO },
+ { "P", kKeyCodeP },
+ { "Q", kKeyCodeQ },
+ { "R", kKeyCodeR },
+ { "S", kKeyCodeS },
+ { "T", kKeyCodeT },
+ { "U", kKeyCodeU },
+ { "V", kKeyCodeV },
+ { "W", kKeyCodeW },
+ { "X", kKeyCodeX },
+ { "Y", kKeyCodeY },
+ { "Z", kKeyCodeZ },
+ { "0", kKeyCode0 },
+ { "1", kKeyCode1 },
+ { "2", kKeyCode2 },
+ { "3", kKeyCode3 },
+ { "4", kKeyCode4 },
+ { "5", kKeyCode5 },
+ { "6", kKeyCode6 },
+ { "7", kKeyCode7 },
+ { "8", kKeyCode8 },
+ { "9", kKeyCode9 },
+ { "COMMA", kKeyCodeComma },
+ { "PERIOD", kKeyCodePeriod },
+ { "AT", kKeyCodeAt },
+ { "SLASH", kKeyCodeSlash },
+ { "SPACE", kKeyCodeSpace },
+ { "ENTER", kKeyCodeNewline },
+ { "TAB", kKeyCodeTab },
+ { "GRAVE", kKeyCodeGrave },
+ { "MINUS", kKeyCodeMinus },
+ { "EQUALS", kKeyCodeEquals },
+ { "LEFT_BRACKET", kKeyCodeLeftBracket },
+ { "RIGHT_BRACKET", kKeyCodeRightBracket },
+ { "BACKSLASH", kKeyCodeBackslash },
+ { "SEMICOLON", kKeyCodeSemicolon },
+ { "APOSTROPHE", kKeyCodeApostrophe },
+ { "STAR", kKeyCodeStar },
+ { "POUND", kKeyCodePound },
+ { "PLUS", kKeyCodePlus },
+ { "DEL", kKeyCodeDel },
+};
+
/* the following is automatically generated by the 'gen-charmap.py' script
* do not touch. the generation command was:
* gen-charmap.py qwerty.kcm qwerty2.kcm
@@ -144,5 +262,471 @@ static const AKeyCharmap _qwerty2_charmap =
"qwerty2"
};
-const AKeyCharmap* android_charmaps[2] = { &_qwerty_charmap , &_qwerty2_charmap };
-const int android_charmap_count = 2;
+/* Custom character map created with -charmap option. */
+AKeyCharmap android_custom_charmap = { 0 };
+
+const AKeyCharmap** android_charmaps = 0;
+int android_charmap_count = 0;
+
+/* Checks if a character represents an end of the line.
+ * Returns a non-zero value if ch is an EOL character. Returns
+ * zero value if ch is not an EOL character.
+*/
+static int
+kcm_is_eol(char ch) {
+ // EOLs are 0, \r and \n chars.
+ return ('\0' == ch) || ('\n' == ch) || ('\r' == ch);
+}
+
+/* Checks if a character represents a token separator.
+ * Returns a non-zero value if ch is a token separator.
+ * Returns zero value if ch is not a token separator.
+*/
+static int
+kcm_is_token_separator(char ch) {
+ // Spaces and tabs are the only separators allowed
+ // between tokens in .kcm files.
+ return (' ' == ch) || ('\t' == ch);
+}
+
+/* Checks if a character represents a path separator.
+ * Returns a non-zero value if ch is a path separator.
+ * Returns zero value if ch is not a path separator.
+*/
+static int
+kcm_is_path_separator(char ch) {
+#ifdef _WIN32
+ return '/' == ch || '\\' == ch;
+#else
+ return '/' == ch;
+#endif // _WIN32
+}
+
+/* Skips space separators in a string.
+ * str - string to advance past space separators.
+ * Returns pointer to the first character in the string, that is
+ * not a space separator. Note that this routine may return
+ * pointer to EOL in case if all characters in the string were
+ * space separators.
+*/
+static const char*
+kcm_skip_spaces(const char* str) {
+ while (!kcm_is_eol(*str) && kcm_is_token_separator(*str)) {
+ str++;
+ }
+ return str;
+}
+
+/* Advances string to the first space separator character.
+ * str - string to advance.
+ * Returns pointer to the first space separator character in the string.
+ * Note that this routine may return pointer to EOL in case if all
+ * characters in the string were not space separators.
+*/
+static const char*
+kcm_skip_non_spaces(const char* str) {
+ while (!kcm_is_eol(*str) && !kcm_is_token_separator(*str)) {
+ str++;
+ }
+ return str;
+}
+
+/* Gets first token from a string.
+ * line - String to get token from. End of the string should be
+ * determined using kcm_is_eol() routine.
+ * token - String where to copy token. Token, copied to this
+ * string will be zero-terminated. Note that buffer for the
+ * token string must be large enough to fit token of any size.
+ * max_token_len - character size of the buffer addressed by
+ * the 'token' parameter.
+ * Returns NULL if there were no tokens found in the string, or
+ * a pointer to the line string, advanced past the found token.
+*/
+static const char*
+kcm_get_token(const char* line, char* token, size_t max_token_len) {
+ // Pass spaces and tabs.
+ const char* token_starts = kcm_skip_spaces(line);
+ // Advance to next space.
+ const char* token_ends = kcm_skip_non_spaces(token_starts);
+ // Calc token length
+ size_t token_len = token_ends - token_starts;
+ if ((0 == token_len) || (token_len >= max_token_len)) {
+ return NULL;
+ }
+ memcpy(token, token_starts, token_len);
+ token[token_len] = '\0';
+ return token_ends;
+}
+
+/* Checks if token represents a comment.
+ * Returns non-zero value if token represents a comment, or zero otherwise.
+*/
+static int
+kcm_is_token_comment(const char* token) {
+ return '#' == *token;
+}
+
+/* Converts a key name to a key code as defined by AndroidKeyCode enum.
+ * key_name - Key name to convert.
+ * key_code - Upon success contains key code value for the key_name.
+ * Returns a zero value on success, or -1 if key code was not found
+ * for the given key_name.
+*/
+static int
+kcm_get_key_code(const char* key_name, unsigned short* key_code) {
+ int n;
+ // Iterate through the key code map, matching key names.
+ for (n = 0; n < sizeof(keycode_map) / sizeof(keycode_map[0]); n++) {
+ if (0 == strcmp(key_name, keycode_map[n].key_name)) {
+ *key_code = keycode_map[n].key_code;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/* Gets unsigned short hexadecimal value for a token.
+ * token - Token to get hexadecimal value for. Note that this
+ * routine expects a "clean" (i.e. no "0x" prefix) hex number
+ * represented by the token string.
+ * val - Upon success contains hexadecimal value for the token.
+ * Returns a zero value on success, or -1 on error.
+*/
+static int
+kcm_get_ushort_hex_val(const char* token, unsigned short* val) {
+ int hex_val = hex2int(token, strlen(token));
+ // Make sure token format was ok and value doesn't exceed unsigned short.
+ if (-1 == hex_val || 0 != (hex_val & ~0xFFFF)) {
+ return -1;
+ }
+
+ *val = (unsigned short)hex_val;
+
+ return 0;
+}
+
+/* Gets a character or hexadecimal value represented by a token.
+ * token - Token to get value from.
+ * val - Upon success will contain a character or hexadecimal
+ * value represented by a token.
+ * Returns a zero value on success, or -1 on error.
+*/
+static int
+kcm_get_char_or_hex_val(const char* token, unsigned short* val) {
+ // For chars token must begin with ' followed by character followed by '
+ if ('\'' == *token) {
+ if ('\0' == token[1] || '\'' != token[2] || '\0' != token[3]) {
+ return 0;
+ }
+ *val = token[1];
+ return 0;
+ } else {
+ // Make sure that hex token is prefixed with "0x"
+ if (('0' != *token) || ('x' != token[1])) {
+ return -1;
+ }
+ // Past 0x
+ return kcm_get_ushort_hex_val(token + 2, val);
+ }
+}
+
+/* Gets first token for the line and calculates its value.
+ * line - Line to get token's value from.
+ * val - Upon success will contain a character or hexadecimal
+ * value represented by the first token in the line.
+ * returns NULL on error, or a pointer to the line string,
+ * advanced past the found token.
+*/
+static const char*
+kcm_get_char_or_hex_token_value(const char* line, unsigned short* val) {
+ char token[KCM_MAX_TOKEN_LEN];
+ line = kcm_get_token(line, token, KCM_MAX_TOKEN_LEN);
+ if (NULL != line) {
+ // Token must be a char, or a hex number.
+ if (kcm_get_char_or_hex_val(token, val)) {
+ return NULL;
+ }
+ }
+
+ return line;
+}
+
+/* Parses a line in .kcm file extracting key information.
+ * line - Line in .kcm file to parse.
+ * line_index - Index of the parsing line in .kcm file.
+ * key_entry - Upon success contains key information extracted from
+ * the line.
+ * kcm_file_path - Path to the charmap file, where paresed line was taken from.
+ * returns BAD_FORMAT if line format was not recognized, SKIP_LINE if line
+ * format was ok, but it didn't contain key information, or KEY_ENTRY
+ * if key information was successfuly extracted from the line.
+*/
+static ParseStatus
+kcm_parse_line(const char* line,
+ int line_index,
+ AKeyEntry* key_entry,
+ const char* kcm_file_path) {
+ char token[KCM_MAX_TOKEN_LEN];
+ unsigned short disp;
+
+ // Get first token, and see if it's an empty, or a comment line.
+ line = kcm_get_token(line, token, KCM_MAX_TOKEN_LEN);
+ if ((NULL == line) || kcm_is_token_comment(token)) {
+ // Empty line, or a comment.
+ return SKIP_LINE;
+ }
+
+ // Here we expect either [type=XXXX], or a key string.
+ if ('[' == token[0]) {
+ return SKIP_LINE;
+ }
+
+ // It must be a key string.
+ // First token is key code.
+ if (kcm_get_key_code(token, &key_entry->code)) {
+ derror("Invalid format of charmap file %s. Unknown key %s in line %d",
+ kcm_file_path, token, line_index);
+ return BAD_FORMAT;
+ }
+
+ // 2-nd token is display character, which is ignored.
+ line = kcm_get_char_or_hex_token_value(line, &disp);
+ if (NULL == line) {
+ derror("Invalid format of charmap file %s. Invalid display value in line %d",
+ kcm_file_path, line_index);
+ return BAD_FORMAT;
+ }
+
+ // 3-rd token is number.
+ line = kcm_get_char_or_hex_token_value(line, &key_entry->number);
+ if (NULL == line) {
+ derror("Invalid format of charmap file %s. Invalid number value in line %d",
+ kcm_file_path, line_index);
+ return BAD_FORMAT;
+ }
+
+ // 4-th token is base.
+ line = kcm_get_char_or_hex_token_value(line, &key_entry->base);
+ if (NULL == line) {
+ derror("Invalid format of charmap file %s. Invalid base value in line %d",
+ kcm_file_path, line_index);
+ return BAD_FORMAT;
+ }
+
+ // 5-th token is caps.
+ line = kcm_get_char_or_hex_token_value(line, &key_entry->caps);
+ if (NULL == line) {
+ derror("Invalid format of charmap file %s. Invalid caps value in line %d",
+ kcm_file_path, line_index);
+ return BAD_FORMAT;
+ }
+
+ // 6-th token is fn.
+ line = kcm_get_char_or_hex_token_value(line, &key_entry->fn);
+ if (NULL == line) {
+ derror("Invalid format of charmap file %s. Invalid fn value in line %d",
+ kcm_file_path, line_index);
+ return BAD_FORMAT;
+ }
+
+ // 7-th token is caps_fn.
+ line = kcm_get_char_or_hex_token_value(line, &key_entry->caps_fn);
+ if (NULL == line) {
+ derror("Invalid format of charmap file %s. Invalid caps_fn value in line %d",
+ kcm_file_path, line_index);
+ return BAD_FORMAT;
+ }
+
+ // Make sure that line doesn't contain anything else,
+ // except (may be) a comment token.
+ line = kcm_get_token(line, token, KCM_MAX_TOKEN_LEN);
+ if ((NULL == line) || kcm_is_token_comment(token)) {
+ return KEY_ENTRY;
+ } else {
+ derror("Invalid format of charmap file %s in line %d",
+ kcm_file_path, line_index);
+ return BAD_FORMAT;
+ }
+}
+
+void
+kcm_extract_charmap_name(const char* kcm_file_path,
+ char* charmap_name,
+ int max_len) {
+ const char* ext_separator;
+ size_t to_copy;
+
+ // Initialize charmap name with name of .kcm file.
+ // First, get file name from the full path to .kcm file.
+ const char* file_name = kcm_file_path + strlen(kcm_file_path);
+ while (!kcm_is_path_separator(*file_name) &&
+ (file_name != kcm_file_path)) {
+ file_name--;
+ }
+ if (kcm_is_path_separator(*file_name)) {
+ file_name++;
+ }
+
+ // Cut off file name extension.
+ ext_separator = strrchr(file_name, '.');
+ if (NULL == ext_separator) {
+ // "filename" is legal name.
+ ext_separator = file_name + strlen(file_name);
+ } else if (ext_separator == file_name) {
+ // ".filename" is legal name too. In this case we will use
+ // "filename" as our custom charmap name.
+ file_name++;
+ ext_separator = file_name + strlen(file_name);
+ }
+
+ // Copy file name to charmap name.
+ to_copy = min(max_len - 1, ext_separator - file_name);
+ memcpy(charmap_name, file_name, to_copy);
+ charmap_name[to_copy] = '\0';
+}
+
+/* Extracts charmap name from .kcm file name,
+ * and saves it into char_map as its name.
+*/
+static void
+kcm_get_charmap_name(const char* kcm_file_path, AKeyCharmap* char_map) {
+ kcm_extract_charmap_name(kcm_file_path, char_map->name,
+ sizeof(char_map->name));
+}
+
+/* Parses .kcm file producing key characters map.
+ * See comments to this module for .kcm file format information.
+ * This routine checks format only for character map lines. It will not check
+ * format for empty lines, comments, and type section lines.
+ * Note that line length in .kcm file should not exceed 1024 characters,
+ * including newline character.
+ *
+ * Parameters:
+ * kcm_file_path - Full path to the .kcm file to parse.
+ * char_map - Upon success will contain initialized characters map.
+ * Returns a zero value on success, or -1 on failure.
+*/
+static int
+parse_kcm_file(const char* kcm_file_path, AKeyCharmap* char_map) {
+ // A line read from .kcm file.
+ char line[KCM_MAX_LINE_LEN];
+ // Return code.
+ int err = 0;
+ // Number of the currently parsed line.
+ int cur_line = 1;
+ // Initial size of the charmap's array of keys.
+ int map_size = 52;
+ FILE* kcm_file;
+
+ char_map->num_entries = 0;
+ char_map->entries = 0;
+
+ kcm_file = fopen(kcm_file_path, "r");
+ if (NULL == kcm_file) {
+ derror("Unable to open charmap file %s : %s",
+ kcm_file_path, strerror(errno));
+ return -1;
+ }
+
+ // Calculate charmap name.
+ kcm_get_charmap_name(kcm_file_path, char_map);
+
+ // Preallocate map.
+ char_map->num_entries = 0;
+ char_map->entries = qemu_malloc(sizeof(AKeyEntry) * map_size);
+
+ // Line by line parse the file.
+ for (; 0 != fgets(line, sizeof(line), kcm_file); cur_line++) {
+ AKeyEntry key_entry;
+ ParseStatus parse_res =
+ kcm_parse_line(line, cur_line, &key_entry, kcm_file_path);
+ if (BAD_FORMAT == parse_res) {
+ err = -1;
+ break;
+ } else if (KEY_ENTRY == parse_res) {
+ AKeyEntry* entries;
+ // Key information has been extracted. Add it to the map.
+ // Lets see if we need to reallocate map.
+ if (map_size == char_map->num_entries) {
+ void* new_map;
+ map_size += 10;
+ new_map = qemu_malloc(sizeof(AKeyEntry) * map_size);
+ memcpy(new_map, char_map->entries,
+ char_map->num_entries * sizeof(AKeyEntry));
+ qemu_free((void*)char_map->entries);
+ char_map->entries = new_map;
+ }
+ entries = (AKeyEntry*)char_map->entries;
+ entries[char_map->num_entries] = key_entry;
+ char_map->num_entries++;
+ }
+ }
+
+ if (!err) {
+ // Make sure we exited the loop on EOF condition. Any other
+ // condition is an error.
+ if (0 == feof(kcm_file)) {
+ err = -1;
+ }
+ if (err) {
+ derror("Error reading charmap file %s : %s",
+ kcm_file_path, strerror(errno));
+ }
+ }
+
+ fclose(kcm_file);
+
+ if (err) {
+ // Cleanup on failure.
+ if (0 != char_map->entries) {
+ qemu_free((void*)char_map->entries);
+ char_map->entries = 0;
+ }
+ char_map->num_entries = 0;
+ }
+
+ return err;
+}
+
+int
+android_charmap_setup(const char* kcm_file_path) {
+ if (NULL != kcm_file_path) {
+ if (!parse_kcm_file(kcm_file_path, &android_custom_charmap)) {
+ // Here we have two default charmaps and the custom one.
+ android_charmap_count = 3;
+ android_charmaps = qemu_malloc(sizeof(AKeyCharmap*) *
+ android_charmap_count);
+ android_charmaps[0] = &android_custom_charmap;
+ android_charmaps[1] = &_qwerty_charmap;
+ android_charmaps[2] = &_qwerty2_charmap;
+ } else {
+ derror("Unable to parse kcm file.");
+ return -1;
+ }
+ } else {
+ // Here we have only two default charmaps.
+ android_charmap_count = 2;
+ android_charmaps = qemu_malloc(sizeof(AKeyCharmap*) *
+ android_charmap_count);
+ android_charmaps[0] = &_qwerty_charmap;
+ android_charmaps[1] = &_qwerty2_charmap;
+ }
+
+ return 0;
+}
+
+void
+android_charmap_done(void) {
+ if (NULL != android_charmaps) {
+ int n;
+ for (n = 0; n < android_charmap_count; n++) {
+ // Entries for qwerty and qwerty2 character maps are
+ // static entries defined in charmap.c
+ if ((&_qwerty_charmap != android_charmaps[n]->entries) &&
+ (&_qwerty2_charmap != android_charmaps[n]->entries)) {
+ qemu_free((void*)android_charmaps[n]->entries);
+ }
+ }
+ qemu_free(android_charmaps);
+ }
+}
diff --git a/android/charmap.h b/android/charmap.h
index 5ac1367..7f1e959 100644
--- a/android/charmap.h
+++ b/android/charmap.h
@@ -24,13 +24,55 @@ typedef struct AKeyEntry {
unsigned short number;
} AKeyEntry;
-typedef struct {
+/* Defines size of name buffer in AKeyCharmap entry. */
+#define AKEYCHARMAP_NAME_SIZE 32
+
+typedef struct AKeyCharmap {
const AKeyEntry* entries;
int num_entries;
- char name[ 32 ];
+ char name[ AKEYCHARMAP_NAME_SIZE ];
} AKeyCharmap;
-extern const int android_charmap_count;
-extern const AKeyCharmap* android_charmaps[];
+/* Array of charmaps available in the current emulator session. */
+extern const AKeyCharmap** android_charmaps;
+
+/* Number of entries in android_charmaps array. */
+extern int android_charmap_count;
+
+/* Custom character map created with -charmap option. */
+extern AKeyCharmap android_custom_charmap;
+
+/* Extracts charmap name from .kcm file name.
+ * Charmap name, extracted by this routine is a name of the kcm file, trimmed
+ * of file name extension, and shrinked (if necessary) to fit into the name
+ * buffer. Here are examples on how this routine extracts charmap name:
+ * /a/path/to/kcmfile.kcm -> kcmfile
+ * /a/path/to/kcmfile.ext.kcm -> kcmfile.ext
+ * /a/path/to/kcmfile -> kcmfile
+ * /a/path/to/.kcmfile -> kcmfile
+ * /a/path/to/.kcmfile.kcm -> .kcmfile
+ * kcm_file_path - Path to key charmap file to extract charmap name from.
+ * charmap_name - Buffer, where to save extracted charname.
+ * max_len - charmap_name buffer size.
+*/
+void kcm_extract_charmap_name(const char* kcm_file_path,
+ char* charmap_name,
+ int max_len);
+
+/* Initialzes key charmap array.
+ * Key charmap array always contains two maps: one for qwerty, and
+ * another for qwerty2 keyboard layout. However, a custom layout can
+ * be requested with -charmap option. In tha case kcm_file_path
+ * parameter contains path to a .kcm file that defines that custom
+ * layout, and as the result, key charmap array will contain another
+ * entry built from that file. If -charmap option was not specified,
+ * kcm_file_path is NULL and final key charmap array will contain only
+ * two default entries.
+ * Returns a zero value on success, or -1 on failure.
+*/
+int android_charmap_setup(const char* kcm_file_path);
+
+/* Cleanups initialization performed in android_charmap_setup routine. */
+void android_charmap_done(void);
#endif /* _android_charmap_h */
diff --git a/android/cmdline-options.h b/android/cmdline-options.h
index 0689e83..6a4e7ff 100644
--- a/android/cmdline-options.h
+++ b/android/cmdline-options.h
@@ -131,6 +131,8 @@ OPT_PARAM( tcpdump, "<file>", "capture network packets to file" )
OPT_PARAM( bootchart, "<timeout>", "enable bootcharting")
+OPT_PARAM( charmap, "<file>", "use specific key character map")
+
OPT_LIST( prop, "<name>=<value>", "set system property on boot")
#ifdef CONFIG_NAND_LIMITS
diff --git a/android/help.c b/android/help.c
index d01ccc9..50c9d02 100644
--- a/android/help.c
+++ b/android/help.c
@@ -1283,6 +1283,15 @@ help_tcpdump(stralloc_t *out)
}
static void
+help_charmap(stralloc_t *out)
+{
+ PRINTF(
+ " use '-charmap <file>' to use key character map specified in that file.\n"
+ " <file> must be a full path to a kcm file, containing desired character map.\n\n"
+ );
+}
+
+static void
help_prop(stralloc_t *out)
{
PRINTF(
diff --git a/android/main.c b/android/main.c
index 1024ec2..682cfce 100644
--- a/android/main.c
+++ b/android/main.c
@@ -91,8 +91,6 @@ extern int qemu_milli_needed;
*/
#define DEFAULT_DEVICE_DPI 165
-static const AKeyCharmap* android_charmap;
-
int android_base_port;
#if 0
@@ -372,7 +370,17 @@ qemulator_init( QEmulator* emulator,
emulator->aconfig = aconfig;
emulator->layout_file = skin_file_create_from_aconfig(aconfig, basepath);
emulator->layout = emulator->layout_file->layouts;
- emulator->keyboard = skin_keyboard_create_from_aconfig(aconfig, opts->raw_keys);
+ // If we have a custom charmap use it to initialize keyboard.
+ // Otherwise initialize keyboard from configuration settings.
+ // Another way to configure keyboard to use a custom charmap would
+ // be saving a custom charmap name into AConfig's keyboard->charmap
+ // property, and calling single skin_keyboard_create_from_aconfig
+ // routine to initialize keyboard.
+ if (NULL != opts->charmap) {
+ emulator->keyboard = skin_keyboard_create_from_kcm(opts->charmap, opts->raw_keys);
+ } else {
+ emulator->keyboard = skin_keyboard_create_from_aconfig(aconfig, opts->raw_keys);
+ }
emulator->window = NULL;
emulator->win_x = x;
emulator->win_y = y;
@@ -1097,6 +1105,7 @@ found_a_skin:
if (userConfig)
auserConfig_getWindowPos(userConfig, &win_x, &win_y);
}
+
if ( qemulator_init( qemulator, root, path, win_x, win_y, opts ) < 0 ) {
fprintf(stderr, "### Error: could not load emulator skin '%s'\n", name);
exit(1);
@@ -1869,7 +1878,9 @@ int main(int argc, char **argv)
}
}
- android_charmap = android_charmaps[0];
+ if (android_charmap_setup(opts->charmap)) {
+ exit(1);
+ }
if (opts->version) {
printf("Android emulator version %s\n"
@@ -3026,4 +3037,5 @@ void android_emulation_setup( void )
void android_emulation_teardown( void )
{
+ android_charmap_done();
}
diff --git a/android/skin/keyboard.c b/android/skin/keyboard.c
index 0c499df..e3537f1 100644
--- a/android/skin/keyboard.c
+++ b/android/skin/keyboard.c
@@ -578,35 +578,28 @@ skin_keyboard_process_event( SkinKeyboard* kb, SDL_Event* ev, int down )
}
}
-
-SkinKeyboard*
-skin_keyboard_create_from_aconfig( AConfig* aconfig, int use_raw_keys )
+static SkinKeyboard*
+skin_keyboard_create_from_charmap_name(const char* charmap_name,
+ int use_raw_keys)
{
SkinKeyboard* kb;
- const char* charmap_name = "qwerty";
- AConfig* node;
+ int nn;
ANEW0(kb);
- node = aconfig_find( aconfig, "keyboard" );
- if (node != NULL)
- charmap_name = aconfig_str(node, "charmap", charmap_name);
-
- {
- int nn;
-
- for (nn = 0; nn < android_charmap_count; nn++) {
- if ( !strcmp(android_charmaps[nn]->name, charmap_name) ) {
- kb->charmap = android_charmaps[nn];
- break;
- }
+ // Find charmap by its name in the array of available charmaps.
+ for (nn = 0; nn < android_charmap_count; nn++) {
+ if (!strcmp(android_charmaps[nn]->name, charmap_name)) {
+ kb->charmap = android_charmaps[nn];
+ break;
}
+ }
- if (!kb->charmap) {
- fprintf(stderr, "### warning, skin requires unknown '%s' charmap, reverting to '%s'\n",
- charmap_name, android_charmaps[0]->name );
- kb->charmap = android_charmaps[0];
- }
+ if (!kb->charmap) {
+ // Charmap name was not found. Default to the first charmap in the array.
+ fprintf(stderr, "### warning, skin requires unknown '%s' charmap, reverting to '%s'\n",
+ charmap_name, android_charmaps[0]->name );
+ kb->charmap = android_charmaps[0];
}
kb->raw_keys = use_raw_keys;
kb->enabled = 0;
@@ -620,6 +613,26 @@ skin_keyboard_create_from_aconfig( AConfig* aconfig, int use_raw_keys )
return kb;
}
+SkinKeyboard*
+skin_keyboard_create_from_aconfig( AConfig* aconfig, int use_raw_keys )
+{
+ const char* charmap_name = "qwerty";
+ AConfig* node = aconfig_find( aconfig, "keyboard" );
+ if (node != NULL) {
+ charmap_name = aconfig_str(node, "charmap", charmap_name);
+ }
+ return skin_keyboard_create_from_charmap_name(charmap_name, use_raw_keys);
+}
+
+SkinKeyboard*
+skin_keyboard_create_from_kcm( const char* kcm_file_path, int use_raw_keys )
+{
+ char charmap_name[AKEYCHARMAP_NAME_SIZE];
+ kcm_extract_charmap_name(kcm_file_path, charmap_name,
+ sizeof(charmap_name));
+ return skin_keyboard_create_from_charmap_name(charmap_name, use_raw_keys);
+}
+
void
skin_keyboard_free( SkinKeyboard* keyboard )
{
diff --git a/android/skin/keyboard.h b/android/skin/keyboard.h
index f583298..a86b132 100644
--- a/android/skin/keyboard.h
+++ b/android/skin/keyboard.h
@@ -26,6 +26,8 @@ typedef void (*SkinKeyEventFunc)( void* opaque, AndroidKeyCode code, int down
extern SkinKeyboard* skin_keyboard_create_from_aconfig( AConfig* aconfig, int use_raw_keys );
+extern SkinKeyboard* skin_keyboard_create_from_kcm( const char* kcm_file_path, int use_raw_keys );
+
extern void skin_keyboard_set_keyset( SkinKeyboard* keyboard, SkinKeyset* kset );
extern const char* skin_keyboard_charmap_name( SkinKeyboard* keyboard );
diff --git a/gen-charmap.py b/gen-charmap.py
index 3c86350..418201b 100644
--- a/gen-charmap.py
+++ b/gen-charmap.py
@@ -56,8 +56,12 @@ specials = { 'COMMA': 'Comma',
'SEMICOLON': 'Semicolon',
'APOSTROPHE': 'Apostrophe',
'SPACE': 'Space',
- 'ENTER': 'Enter',
- 'TAB': 'Tab'
+ 'ENTER': 'Newline',
+ 'TAB': 'Tab',
+ 'STAR': 'Star',
+ 'POUND': 'Pound',
+ 'PLUS': 'Plus',
+ 'DEL': 'Del'
}
entries = []
@@ -86,31 +90,37 @@ def process_line(line,result):
if not m:
print "character expected in: " + line
return -1
- base = quote(m.group(1))
+ disp = quote(m.group(1))
line = m.group(2)
m = match_char_or_hex(line)
if not m:
print "character expected in: " + line
return -1
- caps = quote(m.group(1))
+ number = quote(m.group(1))
line = m.group(2)
m = match_char_or_hex(line)
if not m:
print "character expected in: " + line
return -1
- fn = quote(m.group(1))
+ base = quote(m.group(1))
line = m.group(2)
m = match_char_or_hex(line)
if not m:
print "character expected in: " + line
return -1
- caps_fn = quote(m.group(1))
+ caps = quote(m.group(1))
line = m.group(2)
m = match_char_or_hex(line)
if not m:
print "character expected in: " + line
return -1
- number = quote(m.group(1))
+ fn = quote(m.group(1))
+ line = m.group(2)
+ m = match_char_or_hex(line)
+ if not m:
+ print "character expected in: " + line
+ return -1
+ caps_fn = quote(m.group(1))
if specials.has_key(keycode):
keycode = specials[keycode]