diff options
author | Jeff Brown <jeffbrown@google.com> | 2010-11-10 16:03:06 -0800 |
---|---|---|
committer | Alex Ray <aray@google.com> | 2013-07-30 13:56:55 -0700 |
commit | 647925ddf053989b641b4c5c8a51efd55c931f22 (patch) | |
tree | 3747458d4bf0306bea06b3910ef24a38c9fd78e5 | |
parent | ba0165bef09729a33ab8e0ca329342be05e0d859 (diff) | |
download | system_core-647925ddf053989b641b4c5c8a51efd55c931f22.zip system_core-647925ddf053989b641b4c5c8a51efd55c931f22.tar.gz system_core-647925ddf053989b641b4c5c8a51efd55c931f22.tar.bz2 |
Added support for full PC-style keyboards.
BREAKING CHANGE: Redesigned the key character map format to
accomodate full keyboards with more comprehensive suite of modifiers.
Old key character maps will not work anymore and must be updated.
The new format is plain text only and it not compiled to a binary
file (so the "kcm" tool will be removed in a subsequent check-in).
Added FULL keyboard type to support full PC-style keyboards.
Added SPECIAL_FUNCTION keyboard type to support special function
keypads that do not have any printable keys suitable for typing
and only have keys like HOME and POWER
Added a special VIRTUAL_KEYBOARD device id convention that maps
to a virtual keyboard with a fixed known layout. This is designed
to work around issues injecting input events on devices whose
built-in keyboard does not have a useful key character map (ie.
when the built-in keyboard is a special function keyboard only.)
Modified several places where events were being synthesized
to use the virtual keyboard.
Removed support for the "qwerty" default layout.
The new default layout is "Generic". For the most part "qwerty"
was being used as a backstop in case the built-in keyboard did
not have a key character map (probably because it was a special
function keypad) and the framework needed to be able to inject
key events anyways. The latter issue is resolved by using the
special VIRTUAL_KEYBOARD device instead of BUILT_IN_KEYBOARD.
Added the concept of a key modifier behavior so that
MetaKeyKeyListener can distinguish between keyboards that use
chorded vs. toggled modifiers.
Wrote more robust key layout and key character map parsers
to enable support for new keyboard features and user installable
key maps.
Fixed a bug in InputReader generating key ups when keys
are released out of sequence.
Updated tons of documentation.
Currently QwertyKeyListener is being used for full keyboards
with autotext and capitalization disabled. This mostly works
but causes some problems with character pickers, etc.
These issues will be resolved in subsequent changes.
Change-Id: Ica48f6097a551141c215bc0d2c6f7b3fb634d354
-rw-r--r-- | include/utils/String8.h | 2 | ||||
-rw-r--r-- | include/utils/Tokenizer.h | 123 | ||||
-rw-r--r-- | libs/utils/Android.mk | 1 | ||||
-rw-r--r-- | libs/utils/String8.cpp | 18 | ||||
-rw-r--r-- | libs/utils/Tokenizer.cpp | 150 |
5 files changed, 288 insertions, 6 deletions
diff --git a/include/utils/String8.h b/include/utils/String8.h index b36f128..6abfb06 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -22,6 +22,7 @@ #include <utils/Unicode.h> #include <string.h> // for strcmp +#include <stdarg.h> // --------------------------------------------------------------------------- @@ -70,6 +71,7 @@ public: status_t appendFormat(const char* fmt, ...) __attribute__((format (printf, 2, 3))); + status_t appendFormatV(const char* fmt, va_list args); // Note that this function takes O(N) time to calculate the value. // No cache value is stored. diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h new file mode 100644 index 0000000..bfe8923 --- /dev/null +++ b/include/utils/Tokenizer.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UTILS_TOKENIZER_H +#define _UTILS_TOKENIZER_H + +#include <assert.h> +#include <utils/Errors.h> +#include <utils/String8.h> + +namespace android { + +/** + * A simple tokenizer for loading and parsing ASCII text files line by line. + */ +class Tokenizer { + Tokenizer(const String8& filename, const char* buffer, size_t length); + +public: + ~Tokenizer(); + + /** + * Opens a file and maps it into memory. + * + * Returns NO_ERROR and a tokenizer for the file, if successful. + * Otherwise returns an error and sets outTokenizer to NULL. + */ + static status_t open(const String8& filename, Tokenizer** outTokenizer); + + /** + * Returns true if at the end of the file. + */ + inline bool isEof() const { return mCurrent == getEnd(); } + + /** + * Returns true if at the end of the line or end of the file. + */ + inline bool isEol() const { return isEof() || *mCurrent == '\n'; } + + /** + * Gets the name of the file. + */ + inline String8 getFilename() const { return mFilename; } + + /** + * Gets a 1-based line number index for the current position. + */ + inline int32_t getLineNumber() const { return mLineNumber; } + + /** + * Formats a location string consisting of the filename and current line number. + * Returns a string like "MyFile.txt:33". + */ + String8 getLocation() const; + + /** + * Gets the character at the current position. + * Returns null at end of file. + */ + inline char peekChar() const { return isEof() ? '\0' : *mCurrent; } + + /** + * Gets the remainder of the current line as a string, excluding the newline character. + */ + String8 peekRemainderOfLine() const; + + /** + * Gets the character at the current position and advances past it. + * Returns null at end of file. + */ + inline char nextChar() { return isEof() ? '\0' : *(mCurrent++); } + + /** + * Gets the next token on this line stopping at the specified delimiters + * or the end of the line whichever comes first and advances past it. + * Also stops at embedded nulls. + * Returns the token or an empty string if the current character is a delimiter + * or is at the end of the line. + */ + String8 nextToken(const char* delimiters); + + /** + * Advances to the next line. + * Does nothing if already at the end of the file. + */ + void nextLine(); + + /** + * Skips over the specified delimiters in the line. + * Also skips embedded nulls. + */ + void skipDelimiters(const char* delimiters); + +private: + Tokenizer(const Tokenizer& other); // not copyable + + String8 mFilename; + const char* mBuffer; + size_t mLength; + + const char* mCurrent; + int32_t mLineNumber; + + inline const char* getEnd() const { return mBuffer + mLength; } + +}; + +} // namespace android + +#endif // _UTILS_TOKENIZER_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 05a9674..9c01aea 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -41,6 +41,7 @@ commonSources:= \ TextOutput.cpp \ Threads.cpp \ Timers.cpp \ + Tokenizer.cpp \ Unicode.cpp \ VectorImpl.cpp \ ZipFileCRO.cpp \ diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index c8dc083..e531a2a 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -282,22 +282,28 @@ status_t String8::append(const char* other, size_t otherLen) status_t String8::appendFormat(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); + va_list args; + va_start(args, fmt); + status_t result = appendFormatV(fmt, args); + + va_end(args); + return result; +} + +status_t String8::appendFormatV(const char* fmt, va_list args) +{ int result = NO_ERROR; - int n = vsnprintf(NULL, 0, fmt, ap); + int n = vsnprintf(NULL, 0, fmt, args); if (n != 0) { size_t oldLength = length(); char* buf = lockBuffer(oldLength + n); if (buf) { - vsnprintf(buf + oldLength, n + 1, fmt, ap); + vsnprintf(buf + oldLength, n + 1, fmt, args); } else { result = NO_MEMORY; } } - - va_end(ap); return result; } diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp new file mode 100644 index 0000000..19dadf0 --- /dev/null +++ b/libs/utils/Tokenizer.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Tokenizer" + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <utils/Log.h> +#include <utils/Tokenizer.h> + +// Enables debug output for the tokenizer. +#define DEBUG_TOKENIZER 0 + + +namespace android { + +static inline bool isDelimiter(char ch, const char* delimiters) { + return strchr(delimiters, ch) != NULL; +} + + +Tokenizer::Tokenizer(const String8& filename, const char* buffer, size_t length) : + mFilename(filename), mBuffer(buffer), mLength(length), + mCurrent(buffer), mLineNumber(1) { +} + +Tokenizer::~Tokenizer() { + munmap((void*)mBuffer, mLength); +} + +status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { + *outTokenizer = NULL; + + int result = NO_ERROR; + int fd = ::open(filename.string(), O_RDONLY); + if (fd < 0) { + result = -errno; + LOGE("Error opening file '%s', %s.", filename.string(), strerror(errno)); + } else { + struct stat64 stat; + if (fstat64(fd, &stat)) { + result = -errno; + LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); + } else { + size_t length = size_t(stat.st_size); + void* buffer = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if (buffer == MAP_FAILED) { + result = -errno; + LOGE("Error mapping file '%s', %s.", filename.string(), strerror(errno)); + } else { + if (madvise(buffer, length, MADV_SEQUENTIAL)) { + LOGW("Error calling madvise for mmapped file '%s', %s.", filename.string(), + strerror(errno)); + } + + *outTokenizer = new Tokenizer(filename, static_cast<const char*>(buffer), length); + if (!*outTokenizer) { + result = NO_MEMORY; + LOGE("Error allocating tokenizer for file=%s.", filename.string()); + munmap(buffer, length); + } + } + } + close(fd); + } + return result; +} + +String8 Tokenizer::getLocation() const { + String8 result; + result.appendFormat("%s:%d", mFilename.string(), mLineNumber); + return result; +} + +String8 Tokenizer::peekRemainderOfLine() const { + const char* end = getEnd(); + const char* eol = mCurrent; + while (eol != end) { + char ch = *eol; + if (ch == '\n') { + break; + } + eol += 1; + } + return String8(mCurrent, eol - mCurrent); +} + +String8 Tokenizer::nextToken(const char* delimiters) { +#if DEBUG_TOKENIZER + LOGD("nextToken"); +#endif + const char* end = getEnd(); + const char* tokenStart = mCurrent; + while (mCurrent != end) { + char ch = *mCurrent; + if (ch == '\n' || isDelimiter(ch, delimiters)) { + break; + } + mCurrent += 1; + } + return String8(tokenStart, mCurrent - tokenStart); +} + +void Tokenizer::nextLine() { +#if DEBUG_TOKENIZER + LOGD("nextLine"); +#endif + const char* end = getEnd(); + while (mCurrent != end) { + char ch = *(mCurrent++); + if (ch == '\n') { + mLineNumber += 1; + break; + } + } +} + +void Tokenizer::skipDelimiters(const char* delimiters) { +#if DEBUG_TOKENIZER + LOGD("skipDelimiters"); +#endif + const char* end = getEnd(); + while (mCurrent != end) { + char ch = *mCurrent; + if (ch == '\n' || !isDelimiter(ch, delimiters)) { + break; + } + mCurrent += 1; + } +} + +} // namespace android |