diff options
46 files changed, 3890 insertions, 1284 deletions
diff --git a/api/current.xml b/api/current.xml index afe86fb..e2e9b92 100644 --- a/api/current.xml +++ b/api/current.xml @@ -194886,7 +194886,7 @@ > <parameter name="keyCode" type="int"> </parameter> -<parameter name="meta" type="int"> +<parameter name="metaState" type="int"> </parameter> </method> <method name="getDeadChar" @@ -194937,7 +194937,7 @@ synchronized="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="keyCode" type="int"> @@ -194985,9 +194985,20 @@ </parameter> <parameter name="chars" type="char[]"> </parameter> -<parameter name="modifiers" type="int"> +<parameter name="metaState" type="int"> </parameter> </method> +<method name="getModifierBehavior" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getNumber" return="char" abstract="false" @@ -195024,7 +195035,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="keyboard" type="int"> +<parameter name="deviceId" type="int"> </parameter> </method> <field name="ALPHA" @@ -195071,6 +195082,17 @@ visibility="public" > </field> +<field name="FULL" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="HEX_INPUT" type="char" transient="false" @@ -195082,6 +195104,28 @@ visibility="public" > </field> +<field name="MODIFIER_BEHAVIOR_CHORDED" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="NUMERIC" type="int" transient="false" @@ -195115,13 +195159,54 @@ visibility="public" > </field> +<field name="SPECIAL_FUNCTION" + type="int" + transient="false" + volatile="false" + value="5" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="VIRTUAL_KEYBOARD" + type="int" + transient="false" + volatile="false" + value="-1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="KeyCharacterMap.KeyCharacterMapUnavailableException" + extends="android.util.AndroidRuntimeException" + abstract="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="KeyCharacterMap.KeyCharacterMapUnavailableException" + type="android.view.KeyCharacterMap.KeyCharacterMapUnavailableException" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="msg" type="java.lang.String"> +</parameter> +</constructor> </class> <class name="KeyCharacterMap.KeyData" extends="java.lang.Object" abstract="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <constructor name="KeyCharacterMap.KeyData" @@ -195530,6 +195615,17 @@ visibility="public" > </method> +<method name="getKeyCharacterMap" + return="android.view.KeyCharacterMap" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getKeyCode" return="int" abstract="false" @@ -195548,7 +195644,7 @@ synchronized="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="results" type="android.view.KeyCharacterMap.KeyData"> @@ -195579,7 +195675,7 @@ > <parameter name="chars" type="char[]"> </parameter> -<parameter name="modifiers" type="int"> +<parameter name="metaState" type="int"> </parameter> </method> <method name="getMaxKeyCode" @@ -195658,7 +195754,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="meta" type="int"> +<parameter name="metaState" type="int"> </parameter> </method> <method name="isAltPressed" diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java index 3a1accd..df1d0bf 100755 --- a/cmds/input/src/com/android/commands/input/Input.java +++ b/cmds/input/src/com/android/commands/input/Input.java @@ -91,7 +91,7 @@ public class Input { char[] chars = buff.toString().toCharArray(); KeyCharacterMap mKeyCharacterMap = KeyCharacterMap. - load(KeyCharacterMap.BUILT_IN_KEYBOARD); + load(KeyCharacterMap.VIRTUAL_KEYBOARD); KeyEvent[] events = mKeyCharacterMap.getEvents(chars); diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 4d5f36a..1deb9fb 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -832,8 +832,7 @@ public class Instrumentation { if (text == null) { return; } - KeyCharacterMap keyCharacterMap = - KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); + KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); KeyEvent[] events = keyCharacterMap.getEvents(text.toCharArray()); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 8409baa..8150493 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -36,6 +36,7 @@ import android.text.method.MovementMethod; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; +import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -1782,10 +1783,10 @@ public class InputMethodService extends AbstractInputMethodService { if (ic == null) return; long eventTime = SystemClock.uptimeMillis(); ic.sendKeyEvent(new KeyEvent(eventTime, eventTime, - KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0, + KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, - KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0, + KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); } diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java index 6df6a3a..350c9a8 100644 --- a/core/java/android/text/method/BaseKeyListener.java +++ b/core/java/android/text/method/BaseKeyListener.java @@ -22,9 +22,15 @@ import android.text.*; import android.text.method.TextKeyListener.Capitalize; import android.widget.TextView; -public abstract class BaseKeyListener -extends MetaKeyKeyListener -implements KeyListener { +/** + * Abstract base class for key listeners. + * + * Provides a basic foundation for entering and editing text. + * Subclasses should override {@link #onKeyDown} and {@link #onKeyUp} to insert + * characters as keys are pressed. + */ +public abstract class BaseKeyListener extends MetaKeyKeyListener + implements KeyListener { /* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete(); /** @@ -34,7 +40,7 @@ implements KeyListener { * if any; * ALT+DEL deletes everything on the line the cursor is on. * - * @return true if anything was deleted; false otherwise. + * @return true if anything was deleted; false otherwise. */ public boolean backspace(View view, Editable content, int keyCode, KeyEvent event) { @@ -72,7 +78,7 @@ implements KeyListener { private boolean altBackspace(View view, Editable content, int keyCode, KeyEvent event) { - if (getMetaState(content, META_ALT_ON) != 1) { + if (!event.isAltPressed() && getMetaState(content, META_ALT_ON) != 1) { return false; } diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java index 1e1812a..07127b7 100644 --- a/core/java/android/text/method/DialerKeyListener.java +++ b/core/java/android/text/method/DialerKeyListener.java @@ -49,7 +49,7 @@ public class DialerKeyListener extends NumberKeyListener * from the KeyEvent. */ protected int lookup(KeyEvent event, Spannable content) { - int meta = getMetaState(content); + int meta = event.getMetaState() | getMetaState(content); int number = event.getNumber(); /* diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java index e7b36d7..923a555 100644 --- a/core/java/android/text/method/MetaKeyKeyListener.java +++ b/core/java/android/text/method/MetaKeyKeyListener.java @@ -22,14 +22,54 @@ import android.text.Spannable; import android.text.Spanned; import android.view.KeyEvent; import android.view.View; +import android.view.KeyCharacterMap; /** - * This base class encapsulates the behavior for handling the meta keys - * (shift and alt) and the pseudo-meta state of selecting text. - * Key listeners that care about meta state should - * inherit from it; you should not instantiate this class directly in a client. + * This base class encapsulates the behavior for tracking the state of + * meta keys such as SHIFT, ALT and SYM as well as the pseudo-meta state of selecting text. + * <p> + * Key listeners that care about meta state should inherit from this class; + * you should not instantiate this class directly in a client. + * </p><p> + * This class provides two mechanisms for tracking meta state that can be used + * together or independently. + * </p> + * <ul> + * <li>Methods such as {@link #handleKeyDown(long, int, KeyEvent)} and + * {@link #getMetaState(long)} operate on a meta key state bit mask.</li> + * <li>Methods such as {@link #onKeyDown(View, Editable, int, KeyEvent)} and + * {@link #getMetaState(CharSequence, int)} operate on meta key state flags stored + * as spans in an {@link Editable} text buffer. The spans only describe the current + * meta key state of the text editor; they do not carry any positional information.</li> + * </ul> + * <p> + * The behavior of this class varies according to the keyboard capabilities + * described by the {@link KeyCharacterMap} of the keyboard device such as + * the {@link KeyCharacterMap#getModifierBehavior() key modifier behavior}. + * </p><p> + * {@link MetaKeyKeyListener} implements chorded and toggled key modifiers. + * When key modifiers are toggled into a latched or locked state, the state + * of the modifier is stored in the {@link Editable} text buffer or in a + * meta state integer managed by the client. These latched or locked modifiers + * should be considered to be held <b>in addition to</b> those that the + * keyboard already reported as being pressed in {@link KeyEvent#getMetaState()}. + * In other words, the {@link MetaKeyKeyListener} augments the meta state + * provided by the keyboard; it does not replace it. This distinction is important + * to ensure that meta keys not handled by {@link MetaKeyKeyListener} such as + * {@link KeyEvent#KEYCODE_CAPS_LOCK} or {@link KeyEvent#KEYCODE_NUM_LOCK} are + * taken into consideration. + * </p><p> + * To ensure correct meta key behavior, the following pattern should be used + * when mapping key codes to characters: + * </p> + * <code> + * private char getUnicodeChar(TextKeyListener listener, KeyEvent event, Editable textBuffer) { + * // Use the combined meta states from the event and the key listener. + * int metaState = event.getMetaState() | listener.getMetaState(textBuffer); + * return event.getUnicodeChar(metaState); + * } + * </code> */ - public abstract class MetaKeyKeyListener { /** * Flag that indicates that the SHIFT key is on. @@ -227,8 +267,7 @@ public abstract class MetaKeyKeyListener { /** * Handles presses of the meta keys. */ - public boolean onKeyDown(View view, Editable content, - int keyCode, KeyEvent event) { + public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { press(content, CAP); return true; @@ -283,34 +322,41 @@ public abstract class MetaKeyKeyListener { /** * Handles release of the meta keys. */ - public boolean onKeyUp(View view, Editable content, int keyCode, - KeyEvent event) { + public boolean onKeyUp(View view, Editable content, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { - release(content, CAP); + release(content, CAP, event); return true; } if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT || keyCode == KeyEvent.KEYCODE_NUM) { - release(content, ALT); + release(content, ALT, event); return true; } if (keyCode == KeyEvent.KEYCODE_SYM) { - release(content, SYM); + release(content, SYM, event); return true; } return false; // no super to call through to } - private void release(Editable content, Object what) { + private void release(Editable content, Object what, KeyEvent event) { int current = content.getSpanFlags(what); - if (current == USED) - content.removeSpan(what); - else if (current == PRESSED) - content.setSpan(what, 0, 0, RELEASED); + switch (event.getKeyCharacterMap().getModifierBehavior()) { + case KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED: + if (current == USED) + content.removeSpan(what); + else if (current == PRESSED) + content.setSpan(what, 0, 0, RELEASED); + break; + + default: + content.removeSpan(what); + break; + } } public void clearMetaKeyState(View view, Editable content, int states) { @@ -478,28 +524,36 @@ public abstract class MetaKeyKeyListener { public static long handleKeyUp(long state, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { return release(state, META_SHIFT_ON, META_SHIFT_MASK, - META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED); + META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED, event); } if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT || keyCode == KeyEvent.KEYCODE_NUM) { return release(state, META_ALT_ON, META_ALT_MASK, - META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED); + META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED, event); } if (keyCode == KeyEvent.KEYCODE_SYM) { return release(state, META_SYM_ON, META_SYM_MASK, - META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED); + META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED, event); } return state; } private static long release(long state, int what, long mask, - long pressed, long released, long used) { - if ((state & used) != 0) { - state &= ~mask; - } else if ((state & pressed) != 0) { - state |= what | released; + long pressed, long released, long used, KeyEvent event) { + switch (event.getKeyCharacterMap().getModifierBehavior()) { + case KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED: + if ((state & used) != 0) { + state &= ~mask; + } else if ((state & pressed) != 0) { + state |= what | released; + } + break; + + default: + state &= ~mask; + break; } return state; } diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java index 9270ca5..1e72309 100644 --- a/core/java/android/text/method/NumberKeyListener.java +++ b/core/java/android/text/method/NumberKeyListener.java @@ -37,7 +37,7 @@ public abstract class NumberKeyListener extends BaseKeyListener protected abstract char[] getAcceptedChars(); protected int lookup(KeyEvent event, Spannable content) { - return event.getMatch(getAcceptedChars(), getMetaState(content)); + return event.getMatch(getAcceptedChars(), event.getMetaState() | getMetaState(content)); } public CharSequence filter(CharSequence source, int start, int end, diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java index b3926f2..3308172 100644 --- a/core/java/android/text/method/QwertyKeyListener.java +++ b/core/java/android/text/method/QwertyKeyListener.java @@ -83,7 +83,7 @@ public class QwertyKeyListener extends BaseKeyListener { // QWERTY keyboard normal case - int i = event.getUnicodeChar(getMetaState(content)); + int i = event.getUnicodeChar(event.getMetaState() | getMetaState(content)); int count = event.getRepeatCount(); if (count > 0 && selStart == selEnd && selStart > 0) { diff --git a/core/java/android/text/method/TextKeyListener.java b/core/java/android/text/method/TextKeyListener.java index 09cbbb8..8ad6f50 100644 --- a/core/java/android/text/method/TextKeyListener.java +++ b/core/java/android/text/method/TextKeyListener.java @@ -180,13 +180,16 @@ public class TextKeyListener extends BaseKeyListener implements SpanWatcher { } private KeyListener getKeyListener(KeyEvent event) { - KeyCharacterMap kmap = KeyCharacterMap.load(event.getKeyboardDevice()); + KeyCharacterMap kmap = event.getKeyCharacterMap(); int kind = kmap.getKeyboardType(); if (kind == KeyCharacterMap.ALPHA) { return QwertyKeyListener.getInstance(mAutoText, mAutoCap); } else if (kind == KeyCharacterMap.NUMERIC) { return MultiTapKeyListener.getInstance(mAutoText, mAutoCap); + } else if (kind == KeyCharacterMap.FULL + || kind == KeyCharacterMap.SPECIAL_FUNCTION) { + return QwertyKeyListener.getInstance(false, Capitalize.NONE); } return NullKeyListener.getInstance(); diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java index 9eaab17..78cbdcf 100644 --- a/core/java/android/text/method/Touch.java +++ b/core/java/android/text/method/Touch.java @@ -143,10 +143,11 @@ public class Touch { if (ds[0].mFarEnough) { ds[0].mUsed = true; - boolean cap = (MetaKeyKeyListener.getMetaState(buffer, - MetaKeyKeyListener.META_SHIFT_ON) == 1) || - (MetaKeyKeyListener.getMetaState(buffer, - MetaKeyKeyListener.META_SELECTING) != 0); + boolean cap = (event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0 + || MetaKeyKeyListener.getMetaState(buffer, + MetaKeyKeyListener.META_SHIFT_ON) == 1 + || MetaKeyKeyListener.getMetaState(buffer, + MetaKeyKeyListener.META_SELECTING) != 0; float dx; float dy; if (cap) { diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java index 9981d87..fbd9eac 100644 --- a/core/java/android/view/KeyCharacterMap.java +++ b/core/java/android/view/KeyCharacterMap.java @@ -17,36 +17,86 @@ package android.view; import android.text.method.MetaKeyKeyListener; +import android.util.AndroidRuntimeException; import android.util.SparseIntArray; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.util.SparseArray; import java.lang.Character; -import java.lang.ref.WeakReference; /** - * Describes the keys provided by a device and their associated labels. + * Describes the keys provided by a keyboard device and their associated labels. */ -public class KeyCharacterMap -{ +public class KeyCharacterMap { /** * The id of the device's primary built in keyboard is always 0. */ public static final int BUILT_IN_KEYBOARD = 0; - /** A numeric (12-key) keyboard. */ + /** + * The id of a generic virtual keyboard with a full layout that can be used to + * synthesize key events. Typically used with {@link #getEvents}. + */ + public static final int VIRTUAL_KEYBOARD = -1; + + /** + * A numeric (12-key) keyboard. + * <p> + * A numeric keyboard supports text entry using a multi-tap approach. + * It may be necessary to tap a key multiple times to generate the desired letter + * or symbol. + * </p><p> + * This type of keyboard is generally designed for thumb typing. + * </p> + */ public static final int NUMERIC = 1; - /** A keyboard with all the letters, but with more than one letter - * per key. */ + /** + * A keyboard with all the letters, but with more than one letter per key. + * <p> + * This type of keyboard is generally designed for thumb typing. + * </p> + */ public static final int PREDICTIVE = 2; - /** A keyboard with all the letters, and maybe some numbers. */ + /** + * A keyboard with all the letters, and maybe some numbers. + * <p> + * An alphabetic keyboard supports text entry directly but may have a condensed + * layout with a small form factor. In contrast to a {@link #FULL full keyboard}, some + * symbols may only be accessible using special on-screen character pickers. + * In addition, to improve typing speed and accuracy, the framework provides + * special affordances for alphabetic keyboards such as auto-capitalization + * and toggled / locked shift and alt keys. + * </p><p> + * This type of keyboard is generally designed for thumb typing. + * </p> + */ public static final int ALPHA = 3; /** + * A full PC-style keyboard. + * <p> + * A full keyboard behaves like a PC keyboard. All symbols are accessed directly + * by pressing keys on the keyboard without on-screen support or affordances such + * as auto-capitalization. + * </p><p> + * This type of keyboard is generally designed for full two hand typing. + * </p> + */ + public static final int FULL = 4; + + /** + * A keyboard that is only used to control special functions rather than for typing. + * <p> + * A special function keyboard consists only of non-printing keys such as + * HOME and POWER that are not actually used for typing. + * </p> + */ + public static final int SPECIAL_FUNCTION = 5; + + /** * This private-use character is used to trigger Unicode character * input by hex digits. */ @@ -58,39 +108,73 @@ public class KeyCharacterMap */ public static final char PICKER_DIALOG_INPUT = '\uEF01'; - private static Object sLock = new Object(); - private static SparseArray<WeakReference<KeyCharacterMap>> sInstances - = new SparseArray<WeakReference<KeyCharacterMap>>(); + /** + * Modifier keys may be chorded with character keys. + * + * @see {#link #getModifierBehavior()} for more details. + */ + public static final int MODIFIER_BEHAVIOR_CHORDED = 0; + + /** + * Modifier keys may be chorded with character keys or they may toggle + * into latched or locked states when pressed independently. + * + * @see {#link #getModifierBehavior()} for more details. + */ + public static final int MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED = 1; + + private static SparseArray<KeyCharacterMap> sInstances = new SparseArray<KeyCharacterMap>(); + + private final int mDeviceId; + private int mPtr; + + private static native int nativeLoad(int id); + private static native void nativeDispose(int ptr); + + private static native char nativeGetCharacter(int ptr, int keyCode, int metaState); + private static native char nativeGetNumber(int ptr, int keyCode); + private static native char nativeGetMatch(int ptr, int keyCode, char[] chars, int metaState); + private static native char nativeGetDisplayLabel(int ptr, int keyCode); + private static native int nativeGetKeyboardType(int ptr); + private static native KeyEvent[] nativeGetEvents(int ptr, int deviceId, char[] chars); + + private KeyCharacterMap(int deviceId, int ptr) { + mDeviceId = deviceId; + mPtr = ptr; + } + + @Override + protected void finalize() throws Throwable { + if (mPtr != 0) { + nativeDispose(mPtr); + mPtr = 0; + } + } /** * Loads the key character maps for the keyboard with the specified device id. - * @param keyboard The device id of the keyboard. + * + * @param deviceId The device id of the keyboard. * @return The associated key character map. + * @throws {@link KeyCharacterMapUnavailableException} if the key character map + * could not be loaded because it was malformed or the default key character map + * is missing from the system. */ - public static KeyCharacterMap load(int keyboard) - { - synchronized (sLock) { - KeyCharacterMap result; - WeakReference<KeyCharacterMap> ref = sInstances.get(keyboard); - if (ref != null) { - result = ref.get(); - if (result != null) { - return result; - } + public static KeyCharacterMap load(int deviceId) { + synchronized (sInstances) { + KeyCharacterMap map = sInstances.get(deviceId); + if (map == null) { + int ptr = nativeLoad(deviceId); // might throw + map = new KeyCharacterMap(deviceId, ptr); + sInstances.put(deviceId, map); } - result = new KeyCharacterMap(keyboard); - sInstances.put(keyboard, new WeakReference<KeyCharacterMap>(result)); - return result; + return map; } } - private KeyCharacterMap(int keyboardDevice) - { - mKeyboardDevice = keyboardDevice; - mPointer = ctor_native(keyboardDevice); - } - /** + * Gets the Unicode character generated by the specified key and meta + * key state combination. * <p> * Returns the Unicode character that the specified key would produce * when the specified meta bits (see {@link MetaKeyKeyListener}) @@ -104,89 +188,116 @@ public class KeyCharacterMap * actually produce a character -- see {@link #getDeadChar} -- * after masking with {@link #COMBINING_ACCENT_MASK}. * </p> + * + * @param keyCode The key code. + * @param metaState The meta key modifier state. + * @return The associated character or combining accent, or 0 if none. */ - public int get(int keyCode, int meta) - { - if ((meta & MetaKeyKeyListener.META_CAP_LOCKED) != 0) { - meta |= KeyEvent.META_SHIFT_ON; + public int get(int keyCode, int metaState) { + if ((metaState & MetaKeyKeyListener.META_CAP_LOCKED) != 0) { + metaState |= KeyEvent.META_CAPS_LOCK_ON; } - if ((meta & MetaKeyKeyListener.META_ALT_LOCKED) != 0) { - meta |= KeyEvent.META_ALT_ON; + if ((metaState & MetaKeyKeyListener.META_ALT_LOCKED) != 0) { + metaState |= KeyEvent.META_ALT_ON; } - // Ignore caps lock on keys where alt and shift have the same effect. - if ((meta & MetaKeyKeyListener.META_CAP_LOCKED) != 0) { - if (get_native(mPointer, keyCode, KeyEvent.META_SHIFT_ON) == - get_native(mPointer, keyCode, KeyEvent.META_ALT_ON)) { - meta &= ~KeyEvent.META_SHIFT_ON; - } - } - - int ret = get_native(mPointer, keyCode, meta); - int map = COMBINING.get(ret); - + char ch = nativeGetCharacter(mPtr, keyCode, metaState); + int map = COMBINING.get(ch); if (map != 0) { return map; } else { - return ret; + return ch; } } /** - * Gets the number or symbol associated with the key. The character value - * is returned, not the numeric value. If the key is not a number, but is - * a symbol, the symbol is retuned. + * Gets the number or symbol associated with the key. + * <p> + * The character value is returned, not the numeric value. + * If the key is not a number, but is a symbol, the symbol is retuned. + * </p><p> + * This method is intended to to support dial pads and other numeric or + * symbolic entry on keyboards where certain keys serve dual function + * as alphabetic and symbolic keys. This method returns the number + * or symbol associated with the key independent of whether the user + * has pressed the required modifier. + * </p><p> + * For example, on one particular keyboard the keys on the top QWERTY row generate + * numbers when ALT is pressed such that ALT-Q maps to '1'. So for that keyboard + * when {@link #getNumber} is called with {@link KeyEvent#KEYCODE_Q} it returns '1' + * so that the user can type numbers without pressing ALT when it makes sense. + * </p> + * + * @param keyCode The key code. + * @return The associated numeric or symbolic character, or 0 if none. */ - public char getNumber(int keyCode) - { - return getNumber_native(mPointer, keyCode); + public char getNumber(int keyCode) { + return nativeGetNumber(mPtr, keyCode); } /** - * The same as {@link #getMatch(int,char[],int) getMatch(keyCode, chars, 0)}. + * Gets the first character in the character array that can be generated + * by the specified key code. + * <p> + * This is a convenience function that returns the same value as + * {@link #getMatch(int,char[],int) getMatch(keyCode, chars, 0)}. + * </p> + * + * @param keyCode The keycode. + * @param chars The array of matching characters to consider. + * @return The matching associated character, or 0 if none. */ - public char getMatch(int keyCode, char[] chars) - { + public char getMatch(int keyCode, char[] chars) { return getMatch(keyCode, chars, 0); } /** - * If one of the chars in the array can be generated by keyCode, - * return the char; otherwise return '\0'. - * @param keyCode the key code to look at - * @param chars the characters to try to find - * @param modifiers the modifier bits to prefer. If any of these bits - * are set, if there are multiple choices, that could - * work, the one for this modifier will be set. - */ - public char getMatch(int keyCode, char[] chars, int modifiers) - { + * Gets the first character in the character array that can be generated + * by the specified key code. If there are multiple choices, prefers + * the one that would be generated with the specified meta key modifier state. + * + * @param keyCode The key code. + * @param chars The array of matching characters to consider. + * @param metaState The preferred meta key modifier state. + * @return The matching associated character, or 0 if none. + */ + public char getMatch(int keyCode, char[] chars, int metaState) { if (chars == null) { - // catch it here instead of in native - throw new NullPointerException(); + throw new IllegalArgumentException("chars must not be null."); } - return getMatch_native(mPointer, keyCode, chars, modifiers); + return nativeGetMatch(mPtr, keyCode, chars, metaState); } /** - * Get the primary character for this key. In other words, the label - * that is physically printed on it. + * Gets the primary character for this key. + * In other words, the label that is physically printed on it. + * + * @param keyCode The key code. + * @return The display label character, or 0 if none (eg. for non-printing keys). */ - public char getDisplayLabel(int keyCode) - { - return getDisplayLabel_native(mPointer, keyCode); + public char getDisplayLabel(int keyCode) { + return nativeGetDisplayLabel(mPtr, keyCode); } /** - * Get the character that is produced by putting accent on the character - * c. + * Get the character that is produced by putting accent on the character c. * For example, getDeadChar('`', 'e') returns è. + * + * @param accent The accent character. eg. '`' + * @param c The basic character. + * @return The combined character, or 0 if the characters cannot be combined. */ - public static int getDeadChar(int accent, int c) - { + public static int getDeadChar(int accent, int c) { return DEAD.get((accent << 16) | c); } + /** + * Describes the character mappings associated with a key. + * + * @deprecated instead use {@link KeyCharacterMap#getDisplayLabel(int)}, + * {@link KeyCharacterMap#getNumber(int)} and {@link KeyCharacterMap#get(int, int)}. + */ + @Deprecated public static class KeyData { public static final int META_LENGTH = 4; @@ -212,24 +323,37 @@ public class KeyCharacterMap */ public char[] meta = new char[META_LENGTH]; } - + /** - * Get the characters conversion data for a given keyCode. + * Get the character conversion data for a given key code. * - * @param keyCode the keyCode to look for - * @param results a {@link KeyData} that will be filled with the results. + * @param keyCode The keyCode to query. + * @param results A {@link KeyData} instance that will be filled with the results. + * @return True if the key was mapped. If the key was not mapped, results is not modified. * - * @return whether the key was mapped or not. If the key was not mapped, - * results is not modified. + * @deprecated instead use {@link KeyCharacterMap#getDisplayLabel(int)}, + * {@link KeyCharacterMap#getNumber(int)} or {@link KeyCharacterMap#get(int, int)}. */ - public boolean getKeyData(int keyCode, KeyData results) - { - if (results.meta.length >= KeyData.META_LENGTH) { - return getKeyData_native(mPointer, keyCode, results); - } else { - throw new IndexOutOfBoundsException("results.meta.length must be >= " + - KeyData.META_LENGTH); + @Deprecated + public boolean getKeyData(int keyCode, KeyData results) { + if (results.meta.length < KeyData.META_LENGTH) { + throw new IndexOutOfBoundsException( + "results.meta.length must be >= " + KeyData.META_LENGTH); + } + + char displayLabel = nativeGetDisplayLabel(mPtr, keyCode); + if (displayLabel == 0) { + return false; } + + results.displayLabel = displayLabel; + results.number = nativeGetNumber(mPtr, keyCode); + results.meta[0] = nativeGetCharacter(mPtr, keyCode, 0); + results.meta[1] = nativeGetCharacter(mPtr, keyCode, KeyEvent.META_SHIFT_ON); + results.meta[2] = nativeGetCharacter(mPtr, keyCode, KeyEvent.META_ALT_ON); + results.meta[3] = nativeGetCharacter(mPtr, keyCode, + KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON); + return true; } /** @@ -237,102 +361,37 @@ public class KeyCharacterMap * could plausibly generate the provided sequence of characters. It is * not guaranteed that the sequence is the only way to generate these * events or that it is optimal. - * - * @return an array of KeyEvent objects, or null if the given char array + * <p> + * This function is primarily offered for instrumentation and testing purposes. + * It may fail to map characters to key codes. In particular, the key character + * map for the {@link #BUILT_IN_KEYBOARD built-in keyboard} device id may be empty. + * Consider using the key character map associated with the + * {@link #VIRTUAL_KEYBOARD virtual keyboard} device id instead. + * </p><p> + * For robust text entry, do not use this function. Instead construct a + * {@link KeyEvent} with action code {@link KeyEvent#ACTION_MULTIPLE} that contains + * the desired string using {@link KeyEvent#KeyEvent(long, String, int, int)}. + * </p> + * + * @param chars The sequence of characters to generate. + * @return An array of {@link KeyEvent} objects, or null if the given char array * can not be generated using the current key character map. */ - public KeyEvent[] getEvents(char[] chars) - { + public KeyEvent[] getEvents(char[] chars) { if (chars == null) { - throw new NullPointerException(); - } - - long[] keys = getEvents_native(mPointer, chars); - if (keys == null) { - return null; - } - - // how big should the array be - int len = keys.length*2; - int N = keys.length; - for (int i=0; i<N; i++) { - int mods = (int)(keys[i] >> 32); - if ((mods & KeyEvent.META_ALT_ON) != 0) { - len += 2; - } - if ((mods & KeyEvent.META_SHIFT_ON) != 0) { - len += 2; - } - if ((mods & KeyEvent.META_SYM_ON) != 0) { - len += 2; - } - } - - // create the events - KeyEvent[] rv = new KeyEvent[len]; - int index = 0; - long now = SystemClock.uptimeMillis(); - int device = mKeyboardDevice; - for (int i=0; i<N; i++) { - int mods = (int)(keys[i] >> 32); - int meta = 0; - - if ((mods & KeyEvent.META_ALT_ON) != 0) { - meta |= KeyEvent.META_ALT_ON; - rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, - KeyEvent.KEYCODE_ALT_LEFT, 0, meta, device, 0); - index++; - } - if ((mods & KeyEvent.META_SHIFT_ON) != 0) { - meta |= KeyEvent.META_SHIFT_ON; - rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, - KeyEvent.KEYCODE_SHIFT_LEFT, 0, meta, device, 0); - index++; - } - if ((mods & KeyEvent.META_SYM_ON) != 0) { - meta |= KeyEvent.META_SYM_ON; - rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, - KeyEvent.KEYCODE_SYM, 0, meta, device, 0); - index++; - } - - int key = (int)(keys[i]); - rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, - key, 0, meta, device, 0); - index++; - rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP, - key, 0, meta, device, 0); - index++; - - if ((mods & KeyEvent.META_ALT_ON) != 0) { - meta &= ~KeyEvent.META_ALT_ON; - rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP, - KeyEvent.KEYCODE_ALT_LEFT, 0, meta, device, 0); - index++; - } - if ((mods & KeyEvent.META_SHIFT_ON) != 0) { - meta &= ~KeyEvent.META_SHIFT_ON; - rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP, - KeyEvent.KEYCODE_SHIFT_LEFT, 0, meta, device, 0); - index++; - } - if ((mods & KeyEvent.META_SYM_ON) != 0) { - meta &= ~KeyEvent.META_SYM_ON; - rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP, - KeyEvent.KEYCODE_SYM, 0, meta, device, 0); - index++; - } + throw new IllegalArgumentException("chars must not be null."); } - - return rv; + return nativeGetEvents(mPtr, mDeviceId, chars); } /** - * Does this character key produce a glyph? + * Returns true if the specified key produces a glyph. + * + * @param keyCode The key code. + * @return True if the key is a printing key. */ - public boolean isPrintingKey(int keyCode) - { - int type = Character.getType(get(keyCode, 0)); + public boolean isPrintingKey(int keyCode) { + int type = Character.getType(nativeGetDisplayLabel(mPtr, keyCode)); switch (type) { @@ -347,22 +406,68 @@ public class KeyCharacterMap } } - protected void finalize() throws Throwable - { - dtor_native(mPointer); + /** + * Gets the keyboard type. + * Returns {@link #NUMERIC}, {@link #PREDICTIVE}, {@link #ALPHA} or {@link #FULL}. + * <p> + * Different keyboard types have different semantics. Refer to the documentation + * associated with the keyboard type constants for details. + * </p> + * + * @return The keyboard type. + */ + public int getKeyboardType() { + return nativeGetKeyboardType(mPtr); } /** - * Returns {@link #NUMERIC}, {@link #PREDICTIVE} or {@link #ALPHA}. + * Gets a constant that describes the behavior of this keyboard's modifier keys + * such as {@link KeyEvent#KEYCODE_SHIFT_LEFT}. + * <p> + * Currently there are two behaviors that may be combined: + * </p> + * <ul> + * <li>Chorded behavior: When the modifier key is pressed together with one or more + * character keys, the keyboard inserts the modified keys and + * then resets the modifier state when the modifier key is released.</li> + * <li>Toggled behavior: When the modifier key is pressed and released on its own + * it first toggles into a latched state. When latched, the modifier will apply + * to next character key that is pressed and will then reset itself to the initial state. + * If the modifier is already latched and the modifier key is pressed and release on + * its own again, then it toggles into a locked state. When locked, the modifier will + * apply to all subsequent character keys that are pressed until unlocked by pressing + * the modifier key on its own one more time to reset it to the initial state. + * Toggled behavior is useful for small profile keyboards designed for thumb typing. + * </ul> + * <p> + * This function currently returns {@link #MODIFIER_BEHAVIOR_CHORDED} when the + * {@link #getKeyboardType() keyboard type} is {@link #FULL} or {@link #SPECIAL_FUNCTION} and + * {@link #MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED} otherwise. + * In the future, the function may also take into account global keyboard + * accessibility settings, other user preferences, or new device capabilities. + * </p> + * + * @return The modifier behavior for this keyboard. + * + * @see {@link #MODIFIER_BEHAVIOR_CHORDED} + * @see {@link #MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED} */ - public int getKeyboardType() - { - return getKeyboardType_native(mPointer); + public int getModifierBehavior() { + switch (getKeyboardType()) { + case FULL: + case SPECIAL_FUNCTION: + return MODIFIER_BEHAVIOR_CHORDED; + default: + return MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED; + } } /** * Queries the framework about whether any physical keys exist on the - * device that are capable of producing the given key codes. + * any keyboard attached to the device that are capable of producing the given key code. + * + * @param keyCode The key code to query. + * @return True if at least one attached keyboard supports the specified key code. */ public static boolean deviceHasKey(int keyCode) { int[] codeArray = new int[1]; @@ -370,7 +475,17 @@ public class KeyCharacterMap boolean[] ret = deviceHasKeys(codeArray); return ret[0]; } - + + /** + * Queries the framework about whether any physical keys exist on the + * any keyboard attached to the device that are capable of producing the given + * array of key codes. + * + * @param keyCodes The array of key codes to query. + * @return A new array of the same size as the key codes array whose elements + * are set to true if at least one attached keyboard supports the corresponding key code + * at the same index in the key codes array. + */ public static boolean[] deviceHasKeys(int[] keyCodes) { boolean[] ret = new boolean[keyCodes.length]; IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); @@ -382,22 +497,6 @@ public class KeyCharacterMap return ret; } - private int mPointer; - private int mKeyboardDevice; - - private static native int ctor_native(int id); - private static native void dtor_native(int ptr); - private static native char get_native(int ptr, int keycode, - int meta); - private static native char getNumber_native(int ptr, int keycode); - private static native char getMatch_native(int ptr, int keycode, - char[] chars, int modifiers); - private static native char getDisplayLabel_native(int ptr, int keycode); - private static native boolean getKeyData_native(int ptr, int keycode, - KeyData results); - private static native int getKeyboardType_native(int ptr); - private static native long[] getEvents_native(int ptr, char[] str); - /** * Maps Unicode combining diacritical to display-form dead key * (display character shifted left 16 bits). @@ -412,7 +511,7 @@ public class KeyCharacterMap /* * TODO: Change the table format to support full 21-bit-wide - * accent characters and combined characters if ever necessary. + * accent characters and combined characters if ever necessary. */ private static final int ACUTE = '\u00B4' << 16; private static final int GRAVE = '`' << 16; @@ -550,4 +649,13 @@ public class KeyCharacterMap DEAD.put(UMLAUT | 'x', '\u1E8D'); DEAD.put(UMLAUT | 'y', '\u00FF'); } + + /** + * Thrown by {@link KeyCharacterMap#load} when a key character map could not be loaded. + */ + public static class KeyCharacterMapUnavailableException extends AndroidRuntimeException { + public KeyCharacterMapUnavailableException(String msg) { + super(msg); + } + } } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 63c1137..b3277e4 100755 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -18,6 +18,7 @@ package android.view; import android.os.Parcel; import android.os.Parcelable; +import android.text.method.MetaKeyKeyListener; import android.util.Log; import android.util.SparseIntArray; import android.view.KeyCharacterMap; @@ -1170,6 +1171,7 @@ public class KeyEvent extends InputEvent implements Parcelable { mAction = action; mKeyCode = code; mRepeatCount = 0; + mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; } /** @@ -1192,6 +1194,7 @@ public class KeyEvent extends InputEvent implements Parcelable { mAction = action; mKeyCode = code; mRepeatCount = repeat; + mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; } /** @@ -1216,6 +1219,7 @@ public class KeyEvent extends InputEvent implements Parcelable { mKeyCode = code; mRepeatCount = repeat; mMetaState = metaState; + mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; } /** @@ -1794,102 +1798,158 @@ public class KeyEvent extends InputEvent implements Parcelable { * Renamed to {@link #getDeviceId}. * * @hide - * @deprecated + * @deprecated use {@link #getDeviceId()} instead. */ + @Deprecated public final int getKeyboardDevice() { return mDeviceId; } /** - * Get the primary character for this key. In other words, the label - * that is physically printed on it. + * Gets the {@link KeyCharacterMap} associated with the keyboard device. + * + * @return The associated key character map. + * @throws {@link KeyCharacterMapUnavailableException} if the key character map + * could not be loaded because it was malformed or the default key character map + * is missing from the system. + * + * @see {@link KeyCharacterMap#load} + */ + public final KeyCharacterMap getKeyCharacterMap() { + return KeyCharacterMap.load(mDeviceId); + } + + /** + * Gets the primary character for this key. + * In other words, the label that is physically printed on it. + * + * @return The display label character, or 0 if none (eg. for non-printing keys). */ public char getDisplayLabel() { - return KeyCharacterMap.load(mDeviceId).getDisplayLabel(mKeyCode); + return getKeyCharacterMap().getDisplayLabel(mKeyCode); } /** + * Gets the Unicode character generated by the specified key and meta + * key state combination. * <p> - * Returns the Unicode character that the key would produce. + * Returns the Unicode character that the specified key would produce + * when the specified meta bits (see {@link MetaKeyKeyListener}) + * were active. * </p><p> * Returns 0 if the key is not one that is used to type Unicode * characters. * </p><p> - * If the return value has bit - * {@link KeyCharacterMap#COMBINING_ACCENT} - * set, the key is a "dead key" that should be combined with another to - * actually produce a character -- see {@link #getDeadChar} -- - * after masking with - * {@link KeyCharacterMap#COMBINING_ACCENT_MASK}. + * If the return value has bit {@link KeyCharacterMap#COMBINING_ACCENT} set, the + * key is a "dead key" that should be combined with another to + * actually produce a character -- see {@link KeyCharacterMap#getDeadChar} -- + * after masking with {@link KeyCharacterMap#COMBINING_ACCENT_MASK}. * </p> + * + * @return The associated character or combining accent, or 0 if none. */ public int getUnicodeChar() { return getUnicodeChar(mMetaState); } /** + * Gets the Unicode character generated by the specified key and meta + * key state combination. * <p> - * Returns the Unicode character that the key would produce. + * Returns the Unicode character that the specified key would produce + * when the specified meta bits (see {@link MetaKeyKeyListener}) + * were active. * </p><p> * Returns 0 if the key is not one that is used to type Unicode * characters. * </p><p> - * If the return value has bit - * {@link KeyCharacterMap#COMBINING_ACCENT} - * set, the key is a "dead key" that should be combined with another to - * actually produce a character -- see {@link #getDeadChar} -- after masking - * with {@link KeyCharacterMap#COMBINING_ACCENT_MASK}. + * If the return value has bit {@link KeyCharacterMap#COMBINING_ACCENT} set, the + * key is a "dead key" that should be combined with another to + * actually produce a character -- see {@link KeyCharacterMap#getDeadChar} -- + * after masking with {@link KeyCharacterMap#COMBINING_ACCENT_MASK}. * </p> + * + * @param metaState The meta key modifier state. + * @return The associated character or combining accent, or 0 if none. */ - public int getUnicodeChar(int meta) { - return KeyCharacterMap.load(mDeviceId).get(mKeyCode, meta); + public int getUnicodeChar(int metaState) { + return getKeyCharacterMap().get(mKeyCode, metaState); } /** - * Get the characters conversion data for the key event.. + * Get the character conversion data for a given key code. * - * @param results a {@link KeyData} that will be filled with the results. + * @param results A {@link KeyCharacterMap.KeyData} instance that will be + * filled with the results. + * @return True if the key was mapped. If the key was not mapped, results is not modified. * - * @return whether the key was mapped or not. If the key was not mapped, - * results is not modified. + * @deprecated instead use {@link #getDisplayLabel()}, + * {@link #getNumber()} or {@link #getUnicodeChar(int)}. */ + @Deprecated public boolean getKeyData(KeyData results) { - return KeyCharacterMap.load(mDeviceId).getKeyData(mKeyCode, results); + return getKeyCharacterMap().getKeyData(mKeyCode, results); } /** - * The same as {@link #getMatch(char[],int) getMatch(chars, 0)}. + * Gets the first character in the character array that can be generated + * by the specified key code. + * <p> + * This is a convenience function that returns the same value as + * {@link #getMatch(char[],int) getMatch(chars, 0)}. + * </p> + * + * @param chars The array of matching characters to consider. + * @return The matching associated character, or 0 if none. */ public char getMatch(char[] chars) { return getMatch(chars, 0); } /** - * If one of the chars in the array can be generated by the keyCode of this - * key event, return the char; otherwise return '\0'. - * @param chars the characters to try to find - * @param modifiers the modifier bits to prefer. If any of these bits - * are set, if there are multiple choices, that could - * work, the one for this modifier will be set. + * Gets the first character in the character array that can be generated + * by the specified key code. If there are multiple choices, prefers + * the one that would be generated with the specified meta key modifier state. + * + * @param chars The array of matching characters to consider. + * @param metaState The preferred meta key modifier state. + * @return The matching associated character, or 0 if none. */ - public char getMatch(char[] chars, int modifiers) { - return KeyCharacterMap.load(mDeviceId).getMatch(mKeyCode, chars, modifiers); + public char getMatch(char[] chars, int metaState) { + return getKeyCharacterMap().getMatch(mKeyCode, chars, metaState); } /** - * Gets the number or symbol associated with the key. The character value - * is returned, not the numeric value. If the key is not a number, but is - * a symbol, the symbol is retuned. + * Gets the number or symbol associated with the key. + * <p> + * The character value is returned, not the numeric value. + * If the key is not a number, but is a symbol, the symbol is retuned. + * </p><p> + * This method is intended to to support dial pads and other numeric or + * symbolic entry on keyboards where certain keys serve dual function + * as alphabetic and symbolic keys. This method returns the number + * or symbol associated with the key independent of whether the user + * has pressed the required modifier. + * </p><p> + * For example, on one particular keyboard the keys on the top QWERTY row generate + * numbers when ALT is pressed such that ALT-Q maps to '1'. So for that keyboard + * when {@link #getNumber} is called with {@link KeyEvent#KEYCODE_Q} it returns '1' + * so that the user can type numbers without pressing ALT when it makes sense. + * </p> + * + * @return The associated numeric or symbolic character, or 0 if none. */ public char getNumber() { - return KeyCharacterMap.load(mDeviceId).getNumber(mKeyCode); + return getKeyCharacterMap().getNumber(mKeyCode); } /** - * Does the key code of this key produce a glyph? + * Returns true if this key produces a glyph. + * + * @return True if the key is a printing key. */ public boolean isPrintingKey() { - return KeyCharacterMap.load(mDeviceId).isPrintingKey(mKeyCode); + return getKeyCharacterMap().isPrintingKey(mKeyCode); } /** @@ -2063,7 +2123,7 @@ public class KeyEvent extends InputEvent implements Parcelable { return "KeyEvent{action=" + actionToString(mAction) + " keycode=" + keyCodeToString(mKeyCode) + " scancode=" + mScanCode - + " meta=" + metaStateToString(mMetaState) + + " metaState=" + metaStateToString(mMetaState) + " flags=0x" + Integer.toHexString(mFlags) + " repeat=" + mRepeatCount + " device=" + mDeviceId diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index f7c869b..b66fb2c 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -381,11 +381,13 @@ public class BaseInputConnection implements InputConnection { public boolean performEditorAction(int actionCode) { long eventTime = SystemClock.uptimeMillis(); sendKeyEvent(new KeyEvent(eventTime, eventTime, - KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE | KeyEvent.FLAG_EDITOR_ACTION)); sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, - KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE | KeyEvent.FLAG_EDITOR_ACTION)); return true; @@ -522,8 +524,7 @@ public class BaseInputConnection implements InputConnection { // If it's 1 character, we have a chance of being // able to generate normal key events... if (mKeyCharacterMap == null) { - mKeyCharacterMap = KeyCharacterMap.load( - KeyCharacterMap.BUILT_IN_KEYBOARD); + mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); } char[] chars = new char[1]; content.getChars(0, 1, chars, 0); @@ -541,7 +542,7 @@ public class BaseInputConnection implements InputConnection { // Otherwise, revert to the special key event containing // the actual characters. KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), - content.toString(), KeyCharacterMap.BUILT_IN_KEYBOARD, 0); + content.toString(), KeyCharacterMap.VIRTUAL_KEYBOARD, 0); sendKeyEvent(event); content.clear(); } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index f477f8f..1caa707 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -528,8 +528,7 @@ import junit.framework.Assert; // Otherwise, replace the text being changed (including the last // character) in the textfield. TextUtils.getChars(s, start + count - 1, start + count, mCharacter, 0); - KeyCharacterMap kmap = - KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); + KeyCharacterMap kmap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); KeyEvent[] events = kmap.getEvents(mCharacter); boolean cannotUseKeyEvents = null == events; int charactersFromKeyEvents = cannotUseKeyEvents ? 0 : 1; diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index f2988bc..383e977 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -55,6 +55,7 @@ import android.util.AttributeSet; import android.util.EventLog; import android.util.Log; import android.view.Gravity; +import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -7296,7 +7297,7 @@ public class WebView extends AbsoluteLayout 1, (metaState & KeyEvent.META_SHIFT_ON) | (metaState & KeyEvent.META_ALT_ON) | (metaState & KeyEvent.META_SYM_ON) - , 0, 0, 0); + , KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0); mWebViewCore.sendMessage(eventHubAction, event); } diff --git a/core/java/android/widget/DialerFilter.java b/core/java/android/widget/DialerFilter.java index a23887f..20bc114 100644 --- a/core/java/android/widget/DialerFilter.java +++ b/core/java/android/widget/DialerFilter.java @@ -81,18 +81,6 @@ public class DialerFilter extends RelativeLayout // Setup focus & highlight for this view setFocusable(true); - // Default the mode based on the keyboard - KeyCharacterMap kmap - = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); - mIsQwerty = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC; - if (mIsQwerty) { - Log.i("DialerFilter", "This device looks to be QWERTY"); -// setMode(DIGITS_AND_LETTERS); - } else { - Log.i("DialerFilter", "This device looks to be 12-KEY"); -// setMode(DIGITS_ONLY); - } - // XXX Force the mode to QWERTY for now, since 12-key isn't supported mIsQwerty = true; setMode(DIGITS_AND_LETTERS); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index f32ae92..e6503a9 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -93,6 +93,7 @@ import android.view.ContextMenu; import android.view.DragEvent; import android.view.Gravity; import android.view.HapticFeedbackConstants; +import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; @@ -3313,12 +3314,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener long eventTime = SystemClock.uptimeMillis(); h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME, new KeyEvent(eventTime, eventTime, - KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE | KeyEvent.FLAG_EDITOR_ACTION))); h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME, new KeyEvent(SystemClock.uptimeMillis(), eventTime, - KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE | KeyEvent.FLAG_EDITOR_ACTION))); } diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java index 384f7bc..a1a28ac 100644 --- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java +++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java @@ -141,7 +141,7 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener { private void sendKeyEventsToTarget(int character) { Handler handler = mTargetView.getHandler(); - KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.ALPHA).getEvents( + KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD).getEvents( new char[] { (char) character }); if (events != null) { final int N = events.length; @@ -158,10 +158,12 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener { long eventTime = SystemClock.uptimeMillis(); Handler handler = mTargetView.getHandler(); handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME, - new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0, + new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME, - new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0, + new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); } diff --git a/core/jni/android_text_KeyCharacterMap.cpp b/core/jni/android_text_KeyCharacterMap.cpp index 2a23a71..a7e62c1 100644 --- a/core/jni/android_text_KeyCharacterMap.cpp +++ b/core/jni/android_text_KeyCharacterMap.cpp @@ -15,159 +15,140 @@ */ #include <ui/KeyCharacterMap.h> +#include <ui/Input.h> -#include <nativehelper/jni.h> #include <android_runtime/AndroidRuntime.h> +#include <nativehelper/jni.h> #include <nativehelper/JNIHelp.h> +#include "android_view_KeyEvent.h" + namespace android { -static jint -ctor(JNIEnv *env, jobject clazz, jint id) -{ - return reinterpret_cast<int>(KeyCharacterMap::load(id)); +static struct { + jclass clazz; +} gKeyEventClassInfo; + +static jint nativeLoad(JNIEnv *env, jobject clazz, jint deviceId) { + KeyCharacterMap* map; + status_t status = KeyCharacterMap::loadByDeviceId(deviceId, &map); + if (status) { + String8 msg; + msg.appendFormat("Could not load key character map for device %d due to error %d. " + "Refer to the log for details.", deviceId, status); + jniThrowException(env, "android/view/KeyCharacterMap$KeyCharacterMapUnavailableException", + msg.string()); + return 0; + } + return reinterpret_cast<jint>(map); } -static void -dtor(JNIEnv *env, jobject clazz, jint ptr) -{ - delete reinterpret_cast<KeyCharacterMap*>(ptr); +static void nativeDispose(JNIEnv *env, jobject clazz, jint ptr) { + KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr); + delete map; } -static jchar -get(JNIEnv *env, jobject clazz, jint ptr, jint keycode, jint meta) -{ - return reinterpret_cast<KeyCharacterMap*>(ptr)->get(keycode, meta); +static jchar nativeGetCharacter(JNIEnv *env, jobject clazz, jint ptr, + jint keyCode, jint metaState) { + KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr); + return map->getCharacter(keyCode, metaState); } -static jchar -getNumber(JNIEnv *env, jobject clazz, jint ptr, jint keycode) -{ - return reinterpret_cast<KeyCharacterMap*>(ptr)->getNumber(keycode); +static jchar nativeGetNumber(JNIEnv *env, jobject clazz, jint ptr, jint keyCode) { + KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr); + return map->getNumber(keyCode); } -static jchar -getMatch(JNIEnv *env, jobject clazz, jint ptr, jint keycode, jcharArray chars, jint modifiers) -{ - jchar rv; - jchar* ch = env->GetCharArrayElements(chars, NULL); - jsize chsize = env->GetArrayLength(chars); +static jchar nativeGetMatch(JNIEnv *env, jobject clazz, jint ptr, jint keyCode, + jcharArray charsArray, jint metaState) { + KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr); - rv = reinterpret_cast<KeyCharacterMap*>(ptr)->getMatch(keycode, ch, chsize, modifiers); + jsize numChars = env->GetArrayLength(charsArray); + jchar* chars = static_cast<jchar*>(env->GetPrimitiveArrayCritical(charsArray, NULL)); + if (!chars) { + return 0; + } - env->ReleaseCharArrayElements(chars, ch, JNI_ABORT); - return rv; -} + char16_t result = map->getMatch(keyCode, chars, size_t(numChars), metaState); -static jchar -getDisplayLabel(JNIEnv *env, jobject clazz, jint ptr, jint keycode) -{ - return reinterpret_cast<KeyCharacterMap*>(ptr)->getDisplayLabel(keycode); + env->ReleasePrimitiveArrayCritical(charsArray, chars, JNI_ABORT); + return result; } -static jfieldID gKeyDataMetaField; -static jfieldID gKeyDataNumberField; -static jfieldID gKeyDataDisplayLabelField; - -static jboolean -getKeyData(JNIEnv *env, jobject clazz, jint ptr, jint keycode, jobject keydata) -{ - jboolean rv; - - unsigned short displayLabel = env->GetCharField(keydata, gKeyDataDisplayLabelField); - unsigned short number = env->GetCharField(keydata, gKeyDataNumberField); - - jcharArray chars = (jcharArray) env->GetObjectField(keydata, gKeyDataMetaField); - jchar* ch = env->GetCharArrayElements(chars, NULL); - - KeyCharacterMap* kmap = reinterpret_cast<KeyCharacterMap*>(ptr); - rv = kmap->getKeyData(keycode, &displayLabel, &number, ch); - - env->SetCharField(keydata, gKeyDataDisplayLabelField, displayLabel); - env->SetCharField(keydata, gKeyDataNumberField, number); - - env->ReleaseCharArrayElements(chars, ch, 0); - return rv; +static jchar nativeGetDisplayLabel(JNIEnv *env, jobject clazz, jint ptr, jint keyCode) { + KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr); + return map->getDisplayLabel(keyCode); } -static jint -getKeyboardType(JNIEnv *env, jobject clazz, jint ptr) -{ - return reinterpret_cast<KeyCharacterMap*>(ptr)->getKeyboardType(); +static jint nativeGetKeyboardType(JNIEnv *env, jobject clazz, jint ptr) { + KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr); + return map->getKeyboardType(); } -static jlongArray -getEvents(JNIEnv *env, jobject clazz, jint ptr, jcharArray jchars) -{ - KeyCharacterMap* kmap = reinterpret_cast<KeyCharacterMap*>(ptr); - - uint16_t* chars = env->GetCharArrayElements(jchars, NULL); - size_t len = env->GetArrayLength(jchars); - - Vector<int32_t> keys; - Vector<uint32_t> modifiers; - bool success = kmap->getEvents(chars, len, &keys, &modifiers); - - env->ReleaseCharArrayElements(jchars, chars, JNI_ABORT); - - if (success) { - size_t N = keys.size(); +static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jint ptr, jint deviceId, + jcharArray charsArray) { + KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr); - jlongArray rv = env->NewLongArray(N); - uint64_t* results = (uint64_t*)env->GetLongArrayElements(rv, NULL); - - for (size_t i=0; i<N; i++) { - uint64_t v = modifiers[i]; - v <<= 32; - v |= keys[i]; - results[i] = v; - } - - env->ReleaseLongArrayElements(rv, (jlong*)results, 0); - return rv; - } else { + jchar* chars = env->GetCharArrayElements(charsArray, NULL); + if (!chars) { return NULL; } + jsize numChars = env->GetArrayLength(charsArray); + + Vector<KeyEvent> events; + jobjectArray result = NULL; + if (map->getEvents(deviceId, chars, size_t(numChars), events)) { + result = env->NewObjectArray(jsize(events.size()), gKeyEventClassInfo.clazz, NULL); + if (result) { + for (size_t i = 0; i < events.size(); i++) { + jobject keyEventObj = android_view_KeyEvent_fromNative(env, &events.itemAt(i)); + if (!keyEventObj) break; // threw OOM exception + env->SetObjectArrayElement(result, jsize(i), keyEventObj); + env->DeleteLocalRef(keyEventObj); + } + } + } + + env->ReleaseCharArrayElements(charsArray, chars, JNI_ABORT); + return result; } -// ============================================================================ + /* * JNI registration. */ static JNINativeMethod g_methods[] = { /* name, signature, funcPtr */ - { "ctor_native", "(I)I", (void*)ctor }, - { "dtor_native", "(I)V", (void*)dtor }, - { "get_native", "(III)C", (void*)get }, - { "getNumber_native", "(II)C", (void*)getNumber }, - { "getMatch_native", "(II[CI)C", (void*)getMatch }, - { "getDisplayLabel_native", "(II)C", (void*)getDisplayLabel }, - { "getKeyData_native", "(IILandroid/view/KeyCharacterMap$KeyData;)Z", - (void*)getKeyData }, - { "getKeyboardType_native", "(I)I", (void*)getKeyboardType }, - { "getEvents_native", "(I[C)[J", (void*)getEvents } + { "nativeLoad", "(I)I", + (void*)nativeLoad }, + { "nativeDispose", "(I)V", + (void*)nativeDispose }, + { "nativeGetCharacter", "(III)C", + (void*)nativeGetCharacter }, + { "nativeGetNumber", "(II)C", + (void*)nativeGetNumber }, + { "nativeGetMatch", "(II[CI)C", + (void*)nativeGetMatch }, + { "nativeGetDisplayLabel", "(II)C", + (void*)nativeGetDisplayLabel }, + { "nativeGetKeyboardType", "(I)I", + (void*)nativeGetKeyboardType }, + { "nativeGetEvents", "(II[C)[Landroid/view/KeyEvent;", + (void*)nativeGetEvents }, }; +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + int register_android_text_KeyCharacterMap(JNIEnv* env) { - jclass clazz; - - clazz = env->FindClass("android/view/KeyCharacterMap$KeyData"); - if (clazz == NULL) { - LOGE("Can't find android/view/KeyCharacterMap$KeyData"); - return -1; - } - - gKeyDataMetaField = env->GetFieldID(clazz, "meta", "[C"); - gKeyDataNumberField = env->GetFieldID(clazz, "number", "C"); - gKeyDataDisplayLabelField = env->GetFieldID(clazz, "displayLabel", "C"); + FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); return AndroidRuntime::registerNativeMethods(env, "android/view/KeyCharacterMap", g_methods, NELEM(g_methods)); } }; // namespace android - - - diff --git a/data/keyboards/common.mk b/data/keyboards/AVRCP.kl index 7a58b07..4c91ece 100644 --- a/data/keyboards/common.mk +++ b/data/keyboards/AVRCP.kl @@ -12,13 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This is the list of keylayouts and key character maps to build. -# Used by Android.mk and keyboards.mk. +# Key layout used for Bluetooth AVRCP support. -keylayouts := \ - Generic.kl \ - Motorola_Bluetooth_Wireless_Keyboard.kl - -keycharmaps := \ - Generic.kcm \ - Motorola_Bluetooth_Wireless_Keyboard.kcm +key 200 MEDIA_PLAY_PAUSE WAKE +key 201 MEDIA_PLAY_PAUSE WAKE +key 166 MEDIA_STOP WAKE +key 163 MEDIA_NEXT WAKE +key 165 MEDIA_PREVIOUS WAKE +key 168 MEDIA_REWIND WAKE +key 208 MEDIA_FAST_FORWARD WAKE diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk deleted file mode 100644 index 7a0eae0..0000000 --- a/data/keyboards/Android.mk +++ /dev/null @@ -1,27 +0,0 @@ -# -# 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. -# - -# This makefile builds the key character map binary files. (*.kcm.bin) - -LOCAL_PATH := $(call my-dir) -include $(LOCAL_PATH)/common.mk - -$(foreach file,$(keycharmaps), \ - $(eval include $(CLEAR_VARS)) \ - $(eval LOCAL_SRC_FILES := $(file)) \ - $(eval LOCAL_MODULE_TAGS := optional) \ - $(eval include $(BUILD_KEY_CHAR_MAP)) \ -) diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm index f3c52a7..682584c 100644 --- a/data/keyboards/Generic.kcm +++ b/data/keyboards/Generic.kcm @@ -20,81 +20,482 @@ # a new key character map file with the required keyboard configuration. # -[type=QWERTY] - -# keycode display number base caps fn caps_fn - -A 'A' '2' 'a' 'A' 'a' 'A' -B 'B' '2' 'b' 'B' 'b' 'B' -C 'C' '2' 'c' 'C' 'c' 'C' -D 'D' '3' 'd' 'D' 'd' 'D' -E 'E' '3' 'e' 'E' 'e' 'E' -F 'F' '3' 'f' 'F' 'f' 'F' -G 'G' '4' 'g' 'G' 'g' 'G' -H 'H' '4' 'h' 'H' 'h' 'H' -I 'I' '4' 'i' 'I' 'i' 'I' -J 'J' '5' 'j' 'J' 'j' 'J' -K 'K' '5' 'k' 'K' 'k' 'K' -L 'L' '5' 'l' 'L' 'l' 'L' -M 'M' '6' 'm' 'M' 'm' 'M' -N 'N' '6' 'n' 'N' 'n' 'N' -O 'O' '6' 'o' 'O' 'o' 'O' -P 'P' '7' 'p' 'P' 'p' 'P' -Q 'Q' '7' 'q' 'Q' 'q' 'Q' -R 'R' '7' 'r' 'R' 'r' 'R' -S 'S' '7' 's' 'S' 's' 'S' -T 'T' '8' 't' 'T' 't' 'T' -U 'U' '8' 'u' 'U' 'u' 'U' -V 'V' '8' 'v' 'V' 'v' 'V' -W 'W' '9' 'w' 'W' 'w' 'W' -X 'X' '9' 'x' 'X' 'x' 'X' -Y 'Y' '9' 'y' 'Y' 'y' 'Y' -Z 'Z' '9' 'z' 'Z' 'z' 'Z' - -0 '0' '0' '0' ')' '0' ')' -1 '1' '1' '1' '!' '1' '!' -2 '2' '2' '2' '@' '2' '@' -3 '3' '3' '3' '#' '3' '#' -4 '4' '4' '4' '$' '4' '$' -5 '5' '5' '5' '%' '5' '%' -6 '6' '6' '6' '^' '6' '^' -7 '7' '7' '7' '&' '7' '&' -8 '8' '8' '8' '*' '8' '*' -9 '9' '9' '9' '(' '9' '(' - -SPACE 0x20 0x20 0x20 0x20 0x20 0x20 -ENTER 0xa 0xa 0xa 0xa 0xa 0xa -TAB 0x9 0x9 0x9 0x9 0x9 0x9 - -COMMA ',' ',' ',' '<' ',' '<' -PERIOD '.' '.' '.' '>' '.' '>' -SLASH '/' '/' '/' '?' '/' '?' -GRAVE '`' '`' '`' '~' '`' '~' -MINUS '-' '-' '-' '_' '-' '_' -EQUALS '=' '=' '=' '+' '=' '+' -LEFT_BRACKET '[' '[' '[' '{' '[' '{' -RIGHT_BRACKET ']' ']' ']' '}' ']' '}' -BACKSLASH '\' '\' '\' '|' '\' '|' -SEMICOLON ';' ';' ';' ':' ';' ':' -APOSTROPHE ''' ''' ''' '"' ''' '"' - -NUMPAD_0 '0' '0' '0' '0' '0' '0' -NUMPAD_1 '1' '1' '1' '1' '1' '1' -NUMPAD_2 '2' '2' '2' '2' '2' '2' -NUMPAD_3 '3' '3' '3' '3' '3' '3' -NUMPAD_4 '4' '4' '4' '4' '4' '4' -NUMPAD_5 '5' '5' '5' '5' '5' '5' -NUMPAD_6 '6' '6' '6' '6' '6' '6' -NUMPAD_7 '7' '7' '7' '7' '7' '7' -NUMPAD_8 '8' '8' '8' '8' '8' '8' -NUMPAD_9 '9' '9' '9' '9' '9' '9' -NUMPAD_LEFT_PAREN '(' '(' '(' '(' '(' '(' -NUMPAD_RIGHT_PAREN ')' ')' ')' ')' ')' ')' -NUMPAD_DIVIDE '/' '/' '/' '/' '/' '/' -NUMPAD_MULTIPLY '*' '*' '*' '*' '*' '*' -NUMPAD_SUBTRACT '-' '-' '-' '-' '-' '-' -NUMPAD_ADD '+' '+' '+' '+' '+' '+' -NUMPAD_DOT '.' '.' '.' '.' '.' '.' -NUMPAD_COMMA ',' ',' ',' ',' ',' ',' -NUMPAD_EQUALS '=' '=' '=' '=' '=' '=' -NUMPAD_ENTER 0xa 0xa 0xa 0xa 0xa 0xa +type FULL + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' + ctrl, alt, meta: none +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' + ctrl, alt, meta: none +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' + ctrl, alt, meta: none +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' + ctrl, alt, meta: none +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + ctrl, alt, meta: none +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' + ctrl, alt, meta: none +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' + ctrl, alt, meta: none +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' + ctrl, alt, meta: none +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' + ctrl, alt, meta: none +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' + ctrl, alt, meta: none +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' + ctrl, alt, meta: none +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' + ctrl, alt, meta: none +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' + ctrl, alt, meta: none +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' + ctrl, alt, meta: none +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' + ctrl, alt, meta: none +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' + ctrl, alt, meta: none +} + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' + ctrl, alt, meta: none +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' + ctrl, alt, meta: none +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' + ctrl, alt, meta: none +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' + ctrl, alt, meta: none +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' + ctrl, alt, meta: none +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' + ctrl, alt, meta: none +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' + ctrl, alt, meta: none +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' + ctrl, alt, meta: none +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' + ctrl, alt, meta: none +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' + ctrl, alt, meta: none +} + +key 0 { + label, number: '0' + base: '0' + shift: ')' + ctrl, alt, meta: none +} + +key 1 { + label, number: '1' + base: '1' + shift: '!' + ctrl, alt, meta: none +} + +key 2 { + label, number: '2' + base: '2' + shift: '@' + ctrl, alt, meta: none +} + +key 3 { + label, number: '3' + base: '3' + shift: '#' + ctrl, alt, meta: none +} + +key 4 { + label, number: '4' + base: '4' + shift: '$' + ctrl, alt, meta: none +} + +key 5 { + label, number: '5' + base: '5' + shift: '%' + ctrl, alt, meta: none +} + +key 6 { + label, number: '6' + base: '6' + shift: '^' + ctrl, alt, meta: none +} + +key 7 { + label, number: '7' + base: '7' + shift: '&' + ctrl, alt, meta: none +} + +key 8 { + label, number: '8' + base: '8' + shift: '*' + ctrl, alt, meta: none +} + +key 9 { + label, number: '9' + base: '9' + shift: '(' + ctrl, alt, meta: none +} + +key SPACE { + label: ' ' + base: ' ' + ctrl, alt, meta: none +} + +key ENTER { + label: '\n' + base: '\n' + ctrl, alt, meta: none +} + +key TAB { + label: '\t' + base: '\t' + ctrl, alt, meta: none +} + +key COMMA { + label, number: ',' + base: ',' + shift: '<' + ctrl, alt, meta: none +} + +key PERIOD { + label, number: '.' + base: '.' + shift: '>' + ctrl, alt, meta: none +} + +key SLASH { + label, number: '/' + base: '/' + shift: '?' + ctrl, alt, meta: none +} + +key GRAVE { + label, number: '`' + base: '`' + shift: '~' + ctrl, alt, meta: none +} + +key MINUS { + label, number: '-' + base: '-' + shift: '_' + ctrl, alt, meta: none +} + +key EQUALS { + label, number: '=' + base: '=' + shift: '+' + ctrl, alt, meta: none +} + +key LEFT_BRACKET { + label, number: '[' + base: '[' + shift: '{' + ctrl, alt, meta: none +} + +key RIGHT_BRACKET { + label, number: ']' + base: ']' + shift: '}' + ctrl, alt, meta: none +} + +key BACKSLASH { + label, number: '\\' + base: '\\' + shift: '|' + ctrl, alt, meta: none +} + +key SEMICOLON { + label, number: ';' + base: ';' + shift: ':' + ctrl, alt, meta: none +} + +key APOSTROPHE { + label, number: '\'' + base: '\'' + shift: '"' + ctrl, alt, meta: none +} + +key NUMPAD_0 { + label, number: '0' + base: fallback INSERT + numlock: '0' + ctrl, alt, meta: none +} + +key NUMPAD_1 { + label, number: '1' + base: fallback MOVE_END + numlock: '1' + ctrl, alt, meta: none +} + +key NUMPAD_2 { + label, number: '2' + base: fallback DPAD_DOWN + numlock: '2' + ctrl, alt, meta: none +} + +key NUMPAD_3 { + label, number: '3' + base: fallback PAGE_DOWN + numlock: '3' + ctrl, alt, meta: none +} + +key NUMPAD_4 { + label, number: '4' + base: fallback DPAD_LEFT + numlock: '4' + ctrl, alt, meta: none +} + +key NUMPAD_5 { + label, number: '5' + base: fallback DPAD_CENTER + numlock: '5' + ctrl, alt, meta: none +} + +key NUMPAD_6 { + label, number: '6' + base: fallback DPAD_RIGHT + numlock: '6' + ctrl, alt, meta: none +} + +key NUMPAD_7 { + label, number: '7' + base: fallback MOVE_HOME + numlock: '7' + ctrl, alt, meta: none +} + +key NUMPAD_8 { + label, number: '8' + base: fallback DPAD_UP + numlock: '8' + ctrl, alt, meta: none +} + +key NUMPAD_9 { + label, number: '9' + base: fallback PAGE_UP + numlock: '9' + ctrl, alt, meta: none +} + +key NUMPAD_LEFT_PAREN { + label, number: '(' + base: '(' + ctrl, alt, meta: none +} + +key NUMPAD_RIGHT_PAREN { + label, number: ')' + base: ')' + ctrl, alt, meta: none +} + +key NUMPAD_DIVIDE { + label, number: '/' + base: '/' + ctrl, alt, meta: none +} + +key NUMPAD_MULTIPLY { + label, number: '*' + base: '*' + ctrl, alt, meta: none +} + +key NUMPAD_SUBTRACT { + label, number: '-' + base: '-' + ctrl, alt, meta: none +} + +key NUMPAD_ADD { + label, number: '+' + base: '+' + ctrl, alt, meta: none +} + +key NUMPAD_DOT { + label, number: '.' + base: fallback FORWARD_DEL + numlock: '.' + ctrl, alt, meta: none +} + +key NUMPAD_COMMA { + label, number: ',' + base: ',' + ctrl, alt, meta: none +} + +key NUMPAD_EQUALS { + label, number: '=' + base: '=' + ctrl, alt, meta: none +} + +key NUMPAD_ENTER { + label: '\n' + base: '\n' fallback ENTER + ctrl, alt, meta: none fallback ENTER +} diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm index 0f31683..73a04d0 100644 --- a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm +++ b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm @@ -12,60 +12,359 @@ # See the License for the specific language governing permissions and # limitations under the License. -[type=QWERTY] - -# keycode display number base caps fn caps_fn - -A 'A' '2' 'a' 'A' 'a' 'A' -B 'B' '2' 'b' 'B' 'b' 'B' -C 'C' '2' 'c' 'C' 'c' 'C' -D 'D' '3' 'd' 'D' 'd' 'D' -E 'E' '3' 'e' 'E' 'e' 'E' -F 'F' '3' 'f' 'F' 'f' 'F' -G 'G' '4' 'g' 'G' 'g' 'G' -H 'H' '4' 'h' 'H' 'h' 'H' -I 'I' '4' 'i' 'I' 'i' 'I' -J 'J' '5' 'j' 'J' 'j' 'J' -K 'K' '5' 'k' 'K' 'k' 'K' -L 'L' '5' 'l' 'L' 'l' 'L' -M 'M' '6' 'm' 'M' 'm' 'M' -N 'N' '6' 'n' 'N' 'n' 'N' -O 'O' '6' 'o' 'O' 'o' 'O' -P 'P' '7' 'p' 'P' 'p' 'P' -Q 'Q' '7' 'q' 'Q' 'q' 'Q' -R 'R' '7' 'r' 'R' 'r' 'R' -S 'S' '7' 's' 'S' 's' 'S' -T 'T' '8' 't' 'T' 't' 'T' -U 'U' '8' 'u' 'U' 'u' 'U' -V 'V' '8' 'v' 'V' 'v' 'V' -W 'W' '9' 'w' 'W' 'w' 'W' -X 'X' '9' 'x' 'X' 'x' 'X' -Y 'Y' '9' 'y' 'Y' 'y' 'Y' -Z 'Z' '9' 'z' 'Z' 'z' 'Z' - -0 '0' '0' '0' ')' '0' ')' -1 '1' '1' '1' '!' '1' '!' -2 '2' '2' '2' '@' '2' '@' -3 '3' '3' '3' '#' '3' '#' -4 '4' '4' '4' '$' '4' '$' -5 '5' '5' '5' '%' '5' '%' -6 '6' '6' '6' '^' '6' '^' -7 '7' '7' '7' '&' '7' '&' -8 '8' '8' '8' '*' '8' '*' -9 '9' '9' '9' '(' '9' '(' - -SPACE 0x20 0x20 0x20 0x20 0x20 0x20 -ENTER 0xa 0xa 0xa 0xa 0xa 0xa -TAB 0x9 0x9 0x9 0x9 0x9 0x9 - -COMMA ',' ',' ',' '<' ',' '<' -PERIOD '.' '.' '.' '>' '.' '>' -SLASH '/' '/' '/' '?' '/' '?' -GRAVE '`' '`' '`' '~' '`' '~' -MINUS '-' '-' '-' '_' '-' '_' -EQUALS '=' '=' '=' '+' '=' '+' -LEFT_BRACKET '[' '[' '[' '{' '[' '{' -RIGHT_BRACKET ']' ']' ']' '}' ']' '}' -BACKSLASH '\' '\' '\' '|' '\' '|' -SEMICOLON ';' ';' ';' ':' ';' ':' -APOSTROPHE ''' ''' ''' '"' ''' '"' +# +# Generic key character map for full alphabetic US English PC style external keyboards. +# +# This file is intentionally very generic and is intended to support a broad rang of keyboards. +# Do not edit the generic key character map to support a specific keyboard; instead, create +# a new key character map file with the required keyboard configuration. +# + +type FULL + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' + ctrl, alt, meta: none +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' + ctrl, alt, meta: none +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' + ctrl, alt, meta: none +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' + ctrl, alt, meta: none +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + ctrl, alt, meta: none +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' + ctrl, alt, meta: none +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' + ctrl, alt, meta: none +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' + ctrl, alt, meta: none +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' + ctrl, alt, meta: none +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' + ctrl, alt, meta: none +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' + ctrl, alt, meta: none +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' + ctrl, alt, meta: none +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' + ctrl, alt, meta: none +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' + ctrl, alt, meta: none +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' + ctrl, alt, meta: none +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' + ctrl, alt, meta: none +} + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' + ctrl, alt, meta: none +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' + ctrl, alt, meta: none +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' + ctrl, alt, meta: none +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' + ctrl, alt, meta: none +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' + ctrl, alt, meta: none +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' + ctrl, alt, meta: none +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' + ctrl, alt, meta: none +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' + ctrl, alt, meta: none +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' + ctrl, alt, meta: none +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' + ctrl, alt, meta: none +} + +key 0 { + label, number: '0' + base: '0' + shift: ')' + ctrl, alt, meta: none +} + +key 1 { + label, number: '1' + base: '1' + shift: '!' + ctrl, alt, meta: none +} + +key 2 { + label, number: '2' + base: '2' + shift: '@' + ctrl, alt, meta: none +} + +key 3 { + label, number: '3' + base: '3' + shift: '#' + ctrl, alt, meta: none +} + +key 4 { + label, number: '4' + base: '4' + shift: '$' + ctrl, alt, meta: none +} + +key 5 { + label, number: '5' + base: '5' + shift: '%' + ctrl, alt, meta: none +} + +key 6 { + label, number: '6' + base: '6' + shift: '^' + ctrl, alt, meta: none +} + +key 7 { + label, number: '7' + base: '7' + shift: '&' + ctrl, alt, meta: none +} + +key 8 { + label, number: '8' + base: '8' + shift: '*' + ctrl, alt, meta: none +} + +key 9 { + label, number: '9' + base: '9' + shift: '(' + ctrl, alt, meta: none +} + +key SPACE { + label: ' ' + base: ' ' + ctrl, alt, meta: none +} + +key ENTER { + label: '\n' + base: '\n' + ctrl, alt, meta: none +} + +key TAB { + label: '\t' + base: '\t' + ctrl, alt, meta: none +} + +key COMMA { + label, number: ',' + base: ',' + shift: '<' + ctrl, alt, meta: none +} + +key PERIOD { + label, number: '.' + base: '.' + shift: '>' + ctrl, alt, meta: none +} + +key SLASH { + label, number: '/' + base: '/' + shift: '?' + ctrl, alt, meta: none +} + +key GRAVE { + label, number: '`' + base: '`' + shift: '~' + ctrl, alt, meta: none +} + +key MINUS { + label, number: '-' + base: '-' + shift: '_' + ctrl, alt, meta: none +} + +key EQUALS { + label, number: '=' + base: '=' + shift: '+' + ctrl, alt, meta: none +} + +key LEFT_BRACKET { + label, number: '[' + base: '[' + shift: '{' + ctrl, alt, meta: none +} + +key RIGHT_BRACKET { + label, number: ']' + base: ']' + shift: '}' + ctrl, alt, meta: none +} + +key BACKSLASH { + label, number: '\\' + base: '\\' + shift: '|' + ctrl, alt, meta: none +} + +key SEMICOLON { + label, number: ';' + base: ';' + shift: ':' + ctrl, alt, meta: none +} + +key APOSTROPHE { + label, number: '\'' + base: '\'' + shift: '"' + ctrl, alt, meta: none +} diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm new file mode 100644 index 0000000..9ada86a --- /dev/null +++ b/data/keyboards/Virtual.kcm @@ -0,0 +1,498 @@ +# 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. + +# +# Key character map for a built-in generic virtual keyboard primarily used +# for instrumentation and testing purposes. +# + +type FULL + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' + ctrl, alt, meta: none +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' + ctrl, alt, meta: none +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' + ctrl, alt, meta: none +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' + ctrl, alt, meta: none +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + ctrl, alt, meta: none +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' + ctrl, alt, meta: none +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' + ctrl, alt, meta: none +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' + ctrl, alt, meta: none +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' + ctrl, alt, meta: none +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' + ctrl, alt, meta: none +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' + ctrl, alt, meta: none +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' + ctrl, alt, meta: none +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' + ctrl, alt, meta: none +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' + ctrl, alt, meta: none +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' + ctrl, alt, meta: none +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' + ctrl, alt, meta: none +} + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' + ctrl, alt, meta: none +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' + ctrl, alt, meta: none +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' + ctrl, alt, meta: none +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' + ctrl, alt, meta: none +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' + ctrl, alt, meta: none +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' + ctrl, alt, meta: none +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' + ctrl, alt, meta: none +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' + ctrl, alt, meta: none +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' + ctrl, alt, meta: none +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' + ctrl, alt, meta: none +} + +key 0 { + label, number: '0' + base: '0' + shift: ')' + ctrl, alt, meta: none +} + +key 1 { + label, number: '1' + base: '1' + shift: '!' + ctrl, alt, meta: none +} + +key 2 { + label, number: '2' + base: '2' + shift: '@' + ctrl, alt, meta: none +} + +key 3 { + label, number: '3' + base: '3' + shift: '#' + ctrl, alt, meta: none +} + +key 4 { + label, number: '4' + base: '4' + shift: '$' + ctrl, alt, meta: none +} + +key 5 { + label, number: '5' + base: '5' + shift: '%' + ctrl, alt, meta: none +} + +key 6 { + label, number: '6' + base: '6' + shift: '^' + ctrl, alt, meta: none +} + +key 7 { + label, number: '7' + base: '7' + shift: '&' + ctrl, alt, meta: none +} + +key 8 { + label, number: '8' + base: '8' + shift: '*' + ctrl, alt, meta: none +} + +key 9 { + label, number: '9' + base: '9' + shift: '(' + ctrl, alt, meta: none +} + +key SPACE { + label: ' ' + base: ' ' + ctrl, alt, meta: none +} + +key ENTER { + label: '\n' + base: '\n' + ctrl, alt, meta: none +} + +key TAB { + label: '\t' + base: '\t' + ctrl, alt, meta: none +} + +key COMMA { + label, number: ',' + base: ',' + shift: '<' + ctrl, alt, meta: none +} + +key PERIOD { + label, number: '.' + base: '.' + shift: '>' + ctrl, alt, meta: none +} + +key SLASH { + label, number: '/' + base: '/' + shift: '?' + ctrl, alt, meta: none +} + +key GRAVE { + label, number: '`' + base: '`' + shift: '~' + ctrl, alt, meta: none +} + +key MINUS { + label, number: '-' + base: '-' + shift: '_' + ctrl, alt, meta: none +} + +key EQUALS { + label, number: '=' + base: '=' + shift: '+' + ctrl, alt, meta: none +} + +key LEFT_BRACKET { + label, number: '[' + base: '[' + shift: '{' + ctrl, alt, meta: none +} + +key RIGHT_BRACKET { + label, number: ']' + base: ']' + shift: '}' + ctrl, alt, meta: none +} + +key BACKSLASH { + label, number: '\\' + base: '\\' + shift: '|' + ctrl, alt, meta: none +} + +key SEMICOLON { + label, number: ';' + base: ';' + shift: ':' + ctrl, alt, meta: none +} + +key APOSTROPHE { + label, number: '\'' + base: '\'' + shift: '"' + ctrl, alt, meta: none +} + +key NUMPAD_0 { + label, number: '0' + base: fallback INSERT + numlock: '0' + ctrl, alt, meta: none +} + +key NUMPAD_1 { + label, number: '1' + base: fallback MOVE_END + numlock: '1' + ctrl, alt, meta: none +} + +key NUMPAD_2 { + label, number: '2' + base: fallback DPAD_DOWN + numlock: '2' + ctrl, alt, meta: none +} + +key NUMPAD_3 { + label, number: '3' + base: fallback PAGE_DOWN + numlock: '3' + ctrl, alt, meta: none +} + +key NUMPAD_4 { + label, number: '4' + base: fallback DPAD_LEFT + numlock: '4' + ctrl, alt, meta: none +} + +key NUMPAD_5 { + label, number: '5' + base: fallback DPAD_CENTER + numlock: '5' + ctrl, alt, meta: none +} + +key NUMPAD_6 { + label, number: '6' + base: fallback DPAD_RIGHT + numlock: '6' + ctrl, alt, meta: none +} + +key NUMPAD_7 { + label, number: '7' + base: fallback MOVE_HOME + numlock: '7' + ctrl, alt, meta: none +} + +key NUMPAD_8 { + label, number: '8' + base: fallback DPAD_UP + numlock: '8' + ctrl, alt, meta: none +} + +key NUMPAD_9 { + label, number: '9' + base: fallback PAGE_UP + numlock: '9' + ctrl, alt, meta: none +} + +key NUMPAD_LEFT_PAREN { + label, number: '(' + base: '(' + ctrl, alt, meta: none +} + +key NUMPAD_RIGHT_PAREN { + label, number: ')' + base: ')' + ctrl, alt, meta: none +} + +key NUMPAD_DIVIDE { + label, number: '/' + base: '/' + ctrl, alt, meta: none +} + +key NUMPAD_MULTIPLY { + label, number: '*' + base: '*' + ctrl, alt, meta: none +} + +key NUMPAD_SUBTRACT { + label, number: '-' + base: '-' + ctrl, alt, meta: none +} + +key NUMPAD_ADD { + label, number: '+' + base: '+' + ctrl, alt, meta: none +} + +key NUMPAD_DOT { + label, number: '.' + base: fallback FORWARD_DEL + numlock: '.' + ctrl, alt, meta: none +} + +key NUMPAD_COMMA { + label, number: ',' + base: ',' + ctrl, alt, meta: none +} + +key NUMPAD_EQUALS { + label, number: '=' + base: '=' + ctrl, alt, meta: none +} + +key NUMPAD_ENTER { + label: '\n' + base: '\n' fallback ENTER + ctrl, alt, meta: none fallback ENTER +} diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk index 665c8bf..3a0a553 100644 --- a/data/keyboards/keyboards.mk +++ b/data/keyboards/keyboards.mk @@ -14,9 +14,20 @@ # Warning: this is actually a product definition, to be inherited from -include $(LOCAL_PATH)/common.mk +keylayouts := \ + AVRCP.kl \ + Generic.kl \ + Motorola_Bluetooth_Wireless_Keyboard.kl + +keycharmaps := \ + Generic.kcm \ + Virtual.kcm \ + Motorola_Bluetooth_Wireless_Keyboard.kcm PRODUCT_COPY_FILES := $(foreach file,$(keylayouts),\ frameworks/base/data/keyboards/$(file):system/usr/keylayout/$(file)) +PRODUCT_COPY_FILES += $(foreach file,$(keycharmaps),\ + frameworks/base/data/keyboards/$(file):system/usr/keychars/$(file)) + PRODUCT_PACKAGES := $(keycharmaps) diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h index 1431964..f4dc536 100644 --- a/include/ui/EventHub.h +++ b/include/ui/EventHub.h @@ -19,6 +19,7 @@ #define _RUNTIME_EVENT_HUB_H #include <android/input.h> +#include <ui/Keyboard.h> #include <utils/String8.h> #include <utils/threads.h> #include <utils/Log.h> @@ -246,10 +247,7 @@ private: uint32_t classes; uint8_t* keyBitmask; KeyLayoutMap* layoutMap; - String8 keyMapName; - bool defaultKeyMap; - String8 keyLayoutFilename; - String8 keyCharacterMapFilename; + KeyMapInfo keyMapInfo; int fd; device_t* next; @@ -267,8 +265,6 @@ private: const int32_t* keyCodes, uint8_t* outFlags) const; void configureKeyMap(device_t* device); - bool probeKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap); - void selectKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap); void setKeyboardProperties(device_t* device, bool firstKeyboard); void clearKeyboardProperties(device_t* device, bool firstKeyboard); diff --git a/include/ui/KeyCharacterMap.h b/include/ui/KeyCharacterMap.h index bad2cf8..a1ccb37 100644 --- a/include/ui/KeyCharacterMap.h +++ b/include/ui/KeyCharacterMap.h @@ -18,55 +18,166 @@ #define _UI_KEY_CHARACTER_MAP_H #include <stdint.h> -#include <utils/Vector.h> -using namespace android; +#include <ui/Input.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/Tokenizer.h> +#include <utils/String8.h> +#include <utils/Unicode.h> -class KeyCharacterMap -{ +namespace android { + +/** + * Describes a mapping from Android key codes to characters. + * Also specifies other functions of the keyboard such as the keyboard type + * and key modifier semantics. + */ +class KeyCharacterMap { public: + enum KeyboardType { + KEYBOARD_TYPE_UNKNOWN = 0, + KEYBOARD_TYPE_NUMERIC = 1, + KEYBOARD_TYPE_PREDICTIVE = 2, + KEYBOARD_TYPE_ALPHA = 3, + KEYBOARD_TYPE_FULL = 4, + KEYBOARD_TYPE_SPECIAL_FUNCTION = 5, + }; + ~KeyCharacterMap(); - // see the javadoc for android.text.method.KeyCharacterMap for what - // these do - unsigned short get(int keycode, int meta); - unsigned short getNumber(int keycode); - unsigned short getMatch(int keycode, const unsigned short* chars, - int charsize, uint32_t modifiers); - unsigned short getDisplayLabel(int keycode); - bool getKeyData(int keycode, unsigned short *displayLabel, - unsigned short *number, unsigned short* results); - inline unsigned int getKeyboardType() { return m_type; } - bool getEvents(uint16_t* chars, size_t len, - Vector<int32_t>* keys, Vector<uint32_t>* modifiers); - - static KeyCharacterMap* load(int id); - - enum { - NUMERIC = 1, - Q14 = 2, - QWERTY = 3 // or AZERTY or whatever - }; + static status_t load(const String8& filename, KeyCharacterMap** outMap); + static status_t loadByDeviceId(int32_t deviceId, KeyCharacterMap** outMap); + + /* Gets the keyboard type. */ + int32_t getKeyboardType() const; + + /* Gets the primary character for this key as in the label physically printed on it. + * Returns 0 if none (eg. for non-printing keys). */ + char16_t getDisplayLabel(int32_t keyCode) const; + + /* Gets the Unicode character for the number or symbol generated by the key + * when the keyboard is used as a dialing pad. + * Returns 0 if no number or symbol is generated. + */ + char16_t getNumber(int32_t keyCode) const; -#define META_MASK 3 + /* Gets the Unicode character generated by the key and meta key modifiers. + * Returns 0 if no character is generated. + */ + char16_t getCharacter(int32_t keyCode, int32_t metaState) const; + + /* Gets the first matching Unicode character that can be generated by the key, + * preferring the one with the specified meta key modifiers. + * Returns 0 if no matching character is generated. + */ + char16_t getMatch(int32_t keyCode, const char16_t* chars, + size_t numChars, int32_t metaState) const; + + /* Gets a sequence of key events that could plausibly generate the specified + * character sequence. Returns false if some of the characters cannot be generated. + */ + bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, + Vector<KeyEvent>& outEvents) const; private: - struct Key - { - int32_t keycode; - uint16_t display_label; - uint16_t number; - uint16_t data[META_MASK + 1]; + struct Behavior { + Behavior(); + + /* The next behavior in the list, or NULL if none. */ + Behavior* next; + + /* The meta key modifiers for this behavior. */ + int32_t metaState; + + /* The character to insert. */ + char16_t character; + + /* The fallback keycode if the key is not handled. */ + int32_t fallbackKeyCode; + }; + + struct Key { + Key(); + ~Key(); + + /* The single character label printed on the key, or 0 if none. */ + char16_t label; + + /* The number or symbol character generated by the key, or 0 if none. */ + char16_t number; + + /* The list of key behaviors sorted from most specific to least specific + * meta key binding. */ + Behavior* firstBehavior; }; + class Parser { + enum State { + STATE_TOP = 0, + STATE_KEY = 1, + }; + + enum { + PROPERTY_LABEL = 1, + PROPERTY_NUMBER = 2, + PROPERTY_META = 3, + }; + + struct Property { + inline Property(int32_t property = 0, int32_t metaState = 0) : + property(property), metaState(metaState) { } + + int32_t property; + int32_t metaState; + }; + + KeyCharacterMap* mMap; + Tokenizer* mTokenizer; + State mState; + int32_t mKeyCode; + + public: + Parser(KeyCharacterMap* map, Tokenizer* tokenizer); + ~Parser(); + status_t parse(); + + private: + status_t parseType(); + status_t parseKey(); + status_t parseKeyProperty(); + status_t parseModifier(const String8& token, int32_t* outMetaState); + status_t parseCharacterLiteral(char16_t* outCharacter); + }; + + KeyedVector<int32_t, Key*> mKeys; + int mType; + KeyCharacterMap(); - static KeyCharacterMap* try_file(const char* filename); - Key* find_key(int keycode); - bool find_char(uint16_t c, uint32_t* key, uint32_t* mods); - unsigned int m_type; - unsigned int m_keyCount; - Key* m_keys; + bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const; + + static void addKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time); + static void addMetaKeys(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t* currentMetaState); + static bool addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState); + static void addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t leftKeyCode, int32_t leftKeyMetaState, + int32_t rightKeyCode, int32_t rightKeyMetaState, + int32_t eitherKeyMetaState, + int32_t* currentMetaState); + static void addLockedMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState); }; +} // namespace android + #endif // _UI_KEY_CHARACTER_MAP_H diff --git a/include/ui/KeyLayoutMap.h b/include/ui/KeyLayoutMap.h new file mode 100644 index 0000000..f0a6d00 --- /dev/null +++ b/include/ui/KeyLayoutMap.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 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 _UI_KEY_LAYOUT_MAP_H +#define _UI_KEY_LAYOUT_MAP_H + +#include <stdint.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/Tokenizer.h> + +namespace android { + +/** + * Describes a mapping from keyboard scan codes to Android key codes. + */ +class KeyLayoutMap { +public: + ~KeyLayoutMap(); + + static status_t load(const String8& filename, KeyLayoutMap** outMap); + + status_t map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const; + status_t findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const; + +private: + struct Key { + int32_t keyCode; + uint32_t flags; + }; + + KeyedVector<int32_t,Key> mKeys; + + KeyLayoutMap(); + + class Parser { + KeyLayoutMap* mMap; + Tokenizer* mTokenizer; + + public: + Parser(KeyLayoutMap* map, Tokenizer* tokenizer); + ~Parser(); + status_t parse(); + + private: + status_t parseKey(); + }; +}; + +} // namespace android + +#endif // _UI_KEY_LAYOUT_MAP_H diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h new file mode 100644 index 0000000..3b477c7 --- /dev/null +++ b/include/ui/Keyboard.h @@ -0,0 +1,87 @@ +/* + * 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 _UI_KEYBOARD_H +#define _UI_KEYBOARD_H + +#include <ui/Input.h> +#include <utils/Errors.h> +#include <utils/String8.h> + +namespace android { + +enum { + /* Device id of the built in keyboard. */ + DEVICE_ID_BUILT_IN_KEYBOARD = 0, + + /* Device id of a generic virtual keyboard with a full layout that can be used + * to synthesize key events. */ + DEVICE_ID_VIRTUAL_KEYBOARD = -1, +}; + +struct KeyMapInfo { + String8 keyMapName; + String8 keyLayoutFile; + String8 keyCharacterMapFile; + bool isDefaultKeyMap; + + KeyMapInfo() : isDefaultKeyMap(false) { + } +}; + +/** + * Resolves the key map to use for a particular keyboard device. + */ +extern status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo); + +/** + * Sets keyboard system properties. + */ +extern void setKeyboardProperties(int32_t deviceId, const String8& deviceName, + const KeyMapInfo& keyMapInfo); + +/** + * Clears keyboard system properties. + */ +extern void clearKeyboardProperties(int32_t deviceId); + +/** + * Gets the key character map filename for a device using inspecting system properties + * and then falling back on a default key character map if necessary. + * Returns a NAME_NOT_FOUND if none found. + */ +extern status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile); + +/** + * Gets a key code by its short form label, eg. "HOME". + * Returns 0 if unknown. + */ +extern int32_t getKeyCodeByLabel(const char* label); + +/** + * Gets a key flag by its short form label, eg. "WAKE". + * Returns 0 if unknown. + */ +extern uint32_t getKeyFlagByLabel(const char* label); + +/** + * Updates a meta state field when a key is pressed or released. + */ +extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState); + +} // namespace android + +#endif // _UI_KEYBOARD_H 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/ui/Android.mk b/libs/ui/Android.mk index c4a09d6..61d8abd 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -10,6 +10,7 @@ LOCAL_SRC_FILES:= \ GraphicBufferAllocator.cpp \ GraphicBufferMapper.cpp \ GraphicLog.cpp \ + Keyboard.cpp \ KeyLayoutMap.cpp \ KeyCharacterMap.cpp \ Input.cpp \ diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index 9c7e7f4..f468217 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -16,7 +16,6 @@ //#define LOG_NDEBUG 0 #include <ui/EventHub.h> -#include <ui/KeycodeLabels.h> #include <hardware_legacy/power.h> #include <cutils/properties.h> @@ -33,7 +32,7 @@ #include <errno.h> #include <assert.h> -#include "KeyLayoutMap.h" +#include <ui/KeyLayoutMap.h> #include <string.h> #include <stdint.h> @@ -94,7 +93,7 @@ static inline const char* toString(bool value) { EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name) : id(_id), path(_path), name(name), classes(0) - , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), defaultKeyMap(false), fd(-1), next(NULL) { + , keyBitmask(NULL), layoutMap(NULL), fd(-1), next(NULL) { } EventHub::device_t::~device_t() { @@ -204,8 +203,12 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { } int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const { + if (!device->layoutMap) { + return AKEY_STATE_UNKNOWN; + } + Vector<int32_t> scanCodes; - device->layoutMap->findScancodes(keyCode, &scanCodes); + device->layoutMap->findScanCodes(keyCode, &scanCodes); uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; memset(key_bitmask, 0, sizeof(key_bitmask)); @@ -273,7 +276,7 @@ bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes, for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { scanCodes.clear(); - status_t err = device->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes); + status_t err = device->layoutMap->findScanCodes(keyCodes[codeIndex], &scanCodes); if (! err) { // check the possible scan codes identified by the layout map against the // map of codes actually emitted by the driver @@ -448,14 +451,14 @@ bool EventHub::getEvent(RawEvent* outEvent) } outEvent->type = iev.type; outEvent->scanCode = iev.code; + outEvent->flags = 0; if (iev.type == EV_KEY) { - status_t err = device->layoutMap->map(iev.code, - & outEvent->keyCode, & outEvent->flags); - LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", - iev.code, outEvent->keyCode, outEvent->flags, err); - if (err != 0) { - outEvent->keyCode = AKEYCODE_UNKNOWN; - outEvent->flags = 0; + outEvent->keyCode = AKEYCODE_UNKNOWN; + if (device->layoutMap) { + status_t err = device->layoutMap->map(iev.code, + &outEvent->keyCode, &outEvent->flags); + LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", + iev.code, outEvent->keyCode, outEvent->flags, err); } } else { outEvent->keyCode = iev.code; @@ -800,10 +803,11 @@ int EventHub::openDevice(const char *deviceName) { device->name = name; // Configure the keymap for the device. + configureKeyMap(device); // Tell the world about the devname (the descriptive name) - if (!mHaveFirstKeyboard && !device->defaultKeyMap && strstr(name, "-keypad")) { + if (!mHaveFirstKeyboard && !device->keyMapInfo.isDefaultKeyMap && strstr(name, "-keypad")) { // the built-in keyboard has a well-known device ID of 0, // this device better not go away. mHaveFirstKeyboard = true; @@ -819,11 +823,12 @@ int EventHub::openDevice(const char *deviceName) { setKeyboardProperties(device, false); // Load the keylayout. - if (!device->keyLayoutFilename.isEmpty()) { - status_t status = device->layoutMap->load(device->keyLayoutFilename); + if (!device->keyMapInfo.keyLayoutFile.isEmpty()) { + status_t status = KeyLayoutMap::load(device->keyMapInfo.keyLayoutFile, + &device->layoutMap); if (status) { LOGE("Error %d loading key layout file '%s'.", status, - device->keyLayoutFilename.string()); + device->keyMapInfo.keyLayoutFile.string()); } } @@ -851,7 +856,8 @@ int EventHub::openDevice(const char *deviceName) { LOGI("New keyboard: device->id=0x%x devname='%s' keylayout='%s' keycharactermap='%s'\n", device->id, name, - device->keyLayoutFilename.string(), device->keyCharacterMapFilename.string()); + device->keyMapInfo.keyLayoutFile.string(), + device->keyMapInfo.keyCharacterMapFile.string()); } // If the device isn't recognized as something we handle, don't monitor it. @@ -878,106 +884,17 @@ int EventHub::openDevice(const char *deviceName) { } void EventHub::configureKeyMap(device_t* device) { - // As an initial key map name, try using the device name. - String8 keyMapName(device->name); - char* p = keyMapName.lockBuffer(keyMapName.size()); - while (*p) { - if (*p == ' ') *p = '_'; - p++; - } - keyMapName.unlockBuffer(); - - if (probeKeyMap(device, keyMapName, false)) return; - - // TODO Consider allowing the user to configure a specific key map somehow. - - // Try the Generic key map. - // TODO Apply some additional heuristics here to figure out what kind of - // generic key map to use (US English, etc.). - keyMapName.setTo("Generic"); - if (probeKeyMap(device, keyMapName, true)) return; - - // Fall back on the old style catchall qwerty key map. - keyMapName.setTo("qwerty"); - if (probeKeyMap(device, keyMapName, true)) return; - - // Give up! - keyMapName.setTo("unknown"); - selectKeyMap(device, keyMapName, true); - LOGE("Could not determine key map for device '%s'.", device->name.string()); -} - -bool EventHub::probeKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap) { - const char* root = getenv("ANDROID_ROOT"); - - // TODO Consider also looking somewhere in a writeable partition like /data for a - // custom keymap supplied by the user for this device. - bool haveKeyLayout = !device->keyLayoutFilename.isEmpty(); - if (!haveKeyLayout) { - device->keyLayoutFilename.setTo(root); - device->keyLayoutFilename.append("/usr/keylayout/"); - device->keyLayoutFilename.append(keyMapName); - device->keyLayoutFilename.append(".kl"); - if (access(device->keyLayoutFilename.string(), R_OK)) { - device->keyLayoutFilename.clear(); - } else { - haveKeyLayout = true; - } - } - - bool haveKeyCharacterMap = !device->keyCharacterMapFilename.isEmpty(); - if (!haveKeyCharacterMap) { - device->keyCharacterMapFilename.setTo(root); - device->keyCharacterMapFilename.append("/usr/keychars/"); - device->keyCharacterMapFilename.append(keyMapName); - device->keyCharacterMapFilename.append(".kcm.bin"); - if (access(device->keyCharacterMapFilename.string(), R_OK)) { - device->keyCharacterMapFilename.clear(); - } else { - haveKeyCharacterMap = true; - } - } - - if (haveKeyLayout || haveKeyCharacterMap) { - selectKeyMap(device, keyMapName, defaultKeyMap); - } - return haveKeyLayout && haveKeyCharacterMap; -} - -void EventHub::selectKeyMap(device_t* device, - const String8& keyMapName, bool defaultKeyMap) { - if (device->keyMapName.isEmpty()) { - device->keyMapName.setTo(keyMapName); - device->defaultKeyMap = defaultKeyMap; - } + android::resolveKeyMap(device->name, device->keyMapInfo); } void EventHub::setKeyboardProperties(device_t* device, bool firstKeyboard) { int32_t id = firstKeyboard ? 0 : device->id; - - char propName[100]; - sprintf(propName, "hw.keyboards.%u.devname", id); - property_set(propName, device->name.string()); - sprintf(propName, "hw.keyboards.%u.keymap", id); - property_set(propName, device->keyMapName.string()); - sprintf(propName, "hw.keyboards.%u.klfile", id); - property_set(propName, device->keyLayoutFilename.string()); - sprintf(propName, "hw.keyboards.%u.kcmfile", id); - property_set(propName, device->keyCharacterMapFilename.string()); + android::setKeyboardProperties(id, device->name, device->keyMapInfo); } void EventHub::clearKeyboardProperties(device_t* device, bool firstKeyboard) { int32_t id = firstKeyboard ? 0 : device->id; - - char propName[100]; - sprintf(propName, "hw.keyboards.%u.devname", id); - property_set(propName, ""); - sprintf(propName, "hw.keyboards.%u.keymap", id); - property_set(propName, ""); - sprintf(propName, "hw.keyboards.%u.klfile", id); - property_set(propName, ""); - sprintf(propName, "hw.keyboards.%u.kcmfile", id); - property_set(propName, ""); + android::clearKeyboardProperties(id); } bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const @@ -987,7 +904,7 @@ bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const } Vector<int32_t> scanCodes; - device->layoutMap->findScancodes(keycode, &scanCodes); + device->layoutMap->findScanCodes(keycode, &scanCodes); const size_t N = scanCodes.size(); for (size_t i=0; i<N && i<=KEY_MAX; i++) { int32_t sc = scanCodes.itemAt(i); @@ -1139,11 +1056,14 @@ void EventHub::dump(String8& dump) { } dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes); dump.appendFormat(INDENT3 "Path: %s\n", device->path.string()); - dump.appendFormat(INDENT3 "KeyMapName: %s\n", device->keyMapName.string()); - dump.appendFormat(INDENT3 "KeyLayoutFilename: %s\n", - device->keyLayoutFilename.string()); - dump.appendFormat(INDENT3 "KeyCharacterMapFilename: %s\n", - device->keyCharacterMapFilename.string()); + dump.appendFormat(INDENT3 "IsDefaultKeyMap: %s\n", + toString(device->keyMapInfo.isDefaultKeyMap)); + dump.appendFormat(INDENT3 "KeyMapName: %s\n", + device->keyMapInfo.keyMapName.string()); + dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", + device->keyMapInfo.keyLayoutFile.string()); + dump.appendFormat(INDENT3 "KeyCharacterMapFile: %s\n", + device->keyMapInfo.keyCharacterMapFile.string()); } } } // release lock diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index b91e93a..daff2d0 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -24,6 +24,7 @@ #include <cutils/log.h> #include <ui/InputReader.h> +#include <ui/Keyboard.h> #include <stddef.h> #include <stdlib.h> @@ -70,75 +71,6 @@ static inline const char* toString(bool value) { return value ? "true" : "false"; } -int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) { - int32_t newMetaState; - if (down) { - newMetaState = oldMetaState | mask; - } else { - newMetaState = oldMetaState & - ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON); - } - - if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { - newMetaState |= AMETA_ALT_ON; - } - - if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { - newMetaState |= AMETA_SHIFT_ON; - } - - if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { - newMetaState |= AMETA_CTRL_ON; - } - - if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { - newMetaState |= AMETA_META_ON; - } - return newMetaState; -} - -int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) { - if (down) { - return oldMetaState; - } else { - return oldMetaState ^ mask; - } -} - -int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { - int32_t mask; - switch (keyCode) { - case AKEYCODE_ALT_LEFT: - return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState); - case AKEYCODE_ALT_RIGHT: - return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState); - case AKEYCODE_SHIFT_LEFT: - return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState); - case AKEYCODE_SHIFT_RIGHT: - return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState); - case AKEYCODE_SYM: - return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState); - case AKEYCODE_FUNCTION: - return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState); - case AKEYCODE_CTRL_LEFT: - return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState); - case AKEYCODE_CTRL_RIGHT: - return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState); - case AKEYCODE_META_LEFT: - return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState); - case AKEYCODE_META_RIGHT: - return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState); - case AKEYCODE_CAPS_LOCK: - return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState); - case AKEYCODE_NUM_LOCK: - return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState); - case AKEYCODE_SCROLL_LOCK: - return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState); - default: - return oldMetaState; - } -} - static const int32_t keyCodeRotationMap[][4] = { // key codes enumerated counter-clockwise with the original (unrotated) key first // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation @@ -977,7 +909,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, ssize_t keyDownIndex = findKeyDownLocked(scanCode); if (keyDownIndex >= 0) { // key repeat, be sure to use same keycode as before in case of rotation - keyCode = mLocked.keyDowns.top().keyCode; + keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode; } else { // key down mLocked.keyDowns.push(); @@ -992,7 +924,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, ssize_t keyDownIndex = findKeyDownLocked(scanCode); if (keyDownIndex >= 0) { // key up, be sure to use same keycode as before in case of rotation - keyCode = mLocked.keyDowns.top().keyCode; + keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode; mLocked.keyDowns.removeAt(size_t(keyDownIndex)); } else { // key was not actually down diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp index 870a45c..890cc3f 100644 --- a/libs/ui/KeyCharacterMap.cpp +++ b/libs/ui/KeyCharacterMap.cpp @@ -1,275 +1,807 @@ -#define LOG_TAG "KeyCharacterMap" +/* + * Copyright (C) 2008 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. + */ -#include <ui/KeyCharacterMap.h> -#include <cutils/properties.h> +#define LOG_TAG "KeyCharacterMap" -#include <utils/Log.h> -#include <sys/types.h> -#include <unistd.h> #include <stdlib.h> -#include <fcntl.h> -#include <limits.h> #include <string.h> +#include <android/keycodes.h> +#include <ui/Keyboard.h> +#include <ui/KeyCharacterMap.h> +#include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + +// Enables debug output for mapping. +#define DEBUG_MAPPING 0 + + +namespace android { -struct Header -{ - char magic[8]; - unsigned int endian; - unsigned int version; - unsigned int keycount; - unsigned char kbdtype; - char padding[11]; +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:"; + +struct Modifier { + const char* label; + int32_t metaState; +}; +static const Modifier modifiers[] = { + { "shift", AMETA_SHIFT_ON }, + { "lshift", AMETA_SHIFT_LEFT_ON }, + { "rshift", AMETA_SHIFT_RIGHT_ON }, + { "alt", AMETA_ALT_ON }, + { "lalt", AMETA_ALT_LEFT_ON }, + { "ralt", AMETA_ALT_RIGHT_ON }, + { "ctrl", AMETA_CTRL_ON }, + { "lctrl", AMETA_CTRL_LEFT_ON }, + { "rctrl", AMETA_CTRL_RIGHT_ON }, + { "meta", AMETA_META_ON }, + { "lmeta", AMETA_META_LEFT_ON }, + { "rmeta", AMETA_META_RIGHT_ON }, + { "sym", AMETA_SYM_ON }, + { "fn", AMETA_FUNCTION_ON }, + { "capslock", AMETA_CAPS_LOCK_ON }, + { "numlock", AMETA_NUM_LOCK_ON }, + { "scrolllock", AMETA_SCROLL_LOCK_ON }, }; -KeyCharacterMap::KeyCharacterMap() -{ +#if DEBUG_MAPPING +static String8 toString(const char16_t* chars, size_t numChars) { + String8 result; + for (size_t i = 0; i < numChars; i++) { + result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]); + } + return result; } +#endif + + +// --- KeyCharacterMap --- -KeyCharacterMap::~KeyCharacterMap() -{ - free(m_keys); +KeyCharacterMap::KeyCharacterMap() : + mType(KEYBOARD_TYPE_UNKNOWN) { } -unsigned short -KeyCharacterMap::get(int keycode, int meta) -{ - Key* k = find_key(keycode); - if (k != NULL) { - return k->data[meta & META_MASK]; +KeyCharacterMap::~KeyCharacterMap() { + for (size_t i = 0; i < mKeys.size(); i++) { + Key* key = mKeys.editValueAt(i); + delete key; } - return 0; } -unsigned short -KeyCharacterMap::getNumber(int keycode) -{ - Key* k = find_key(keycode); - if (k != NULL) { - return k->number; +status_t KeyCharacterMap::load(const String8& filename, KeyCharacterMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening key character map file %s.", status, filename.string()); + } else { + KeyCharacterMap* map = new KeyCharacterMap(); + if (!map) { + LOGE("Error allocating key character map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; } - return 0; + return status; } -unsigned short -KeyCharacterMap::getMatch(int keycode, const unsigned short* chars, - int charsize, uint32_t modifiers) -{ - Key* k = find_key(keycode); - modifiers &= 3; // ignore the SYM key because we don't have keymap entries for it - if (k != NULL) { - const uint16_t* data = k->data; - for (int j=0; j<charsize; j++) { - uint16_t c = chars[j]; - for (int i=0; i<(META_MASK + 1); i++) { - if ((modifiers == 0) || ((modifiers & i) != 0)) { - if (c == data[i]) { - return c; +status_t KeyCharacterMap::loadByDeviceId(int32_t deviceId, KeyCharacterMap** outMap) { + *outMap = NULL; + + String8 filename; + status_t result = getKeyCharacterMapFile(deviceId, filename); + if (!result) { + result = load(filename, outMap); + } + return result; +} + +int32_t KeyCharacterMap::getKeyboardType() const { + return mType; +} + +char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const { + char16_t result = 0; + ssize_t index = mKeys.indexOfKey(keyCode); + if (index >= 0) { + const Key* key = mKeys.valueAt(index); + result = key->label; + } +#if DEBUG_MAPPING + LOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result); +#endif + return result; +} + +char16_t KeyCharacterMap::getNumber(int32_t keyCode) const { + char16_t result = 0; + ssize_t index = mKeys.indexOfKey(keyCode); + if (index >= 0) { + const Key* key = mKeys.valueAt(index); + result = key->number; + } +#if DEBUG_MAPPING + LOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result); +#endif + return result; +} + +char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const { + char16_t result = 0; + ssize_t index = mKeys.indexOfKey(keyCode); + if (index >= 0) { + const Key* key = mKeys.valueAt(index); + for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { + if ((behavior->metaState & metaState) == behavior->metaState) { + result = behavior->character; + break; + } + } + } +#if DEBUG_MAPPING + LOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result); +#endif + return result; +} + +char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars, + int32_t metaState) const { + char16_t result = 0; + ssize_t index = mKeys.indexOfKey(keyCode); + if (index >= 0) { + const Key* key = mKeys.valueAt(index); + + // Try to find the most general behavior that maps to this character. + // For example, the base key behavior will usually be last in the list. + // However, if we find a perfect meta state match for one behavior then use that one. + for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { + if (behavior->character) { + for (size_t i = 0; i < numChars; i++) { + if (behavior->character == chars[i]) { + result = behavior->character; + if ((behavior->metaState & metaState) == behavior->metaState) { + goto ExactMatch; + } + break; } } } } + ExactMatch: ; } - return 0; +#if DEBUG_MAPPING + LOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.", + keyCode, toString(chars, numChars).string(), metaState, result); +#endif + return result; } -unsigned short -KeyCharacterMap::getDisplayLabel(int keycode) -{ - Key* k = find_key(keycode); - if (k != NULL) { - return k->display_label; +bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, + Vector<KeyEvent>& outEvents) const { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + + for (size_t i = 0; i < numChars; i++) { + int32_t keyCode, metaState; + char16_t ch = chars[i]; + if (!findKey(ch, &keyCode, &metaState)) { +#if DEBUG_MAPPING + LOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.", + deviceId, toString(chars, numChars).string(), ch); +#endif + return false; + } + + int32_t currentMetaState = 0; + addMetaKeys(outEvents, deviceId, metaState, true, now, ¤tMetaState); + addKey(outEvents, deviceId, keyCode, currentMetaState, true, now); + addKey(outEvents, deviceId, keyCode, currentMetaState, false, now); + addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState); + } +#if DEBUG_MAPPING + LOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", + deviceId, toString(chars, numChars).string(), outEvents.size()); + for (size_t i = 0; i < outEvents.size(); i++) { + LOGD(" Key: keyCode=%d, metaState=0x%08x, %s.", + outEvents[i].getKeyCode(), outEvents[i].getMetaState(), + outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up"); } - return 0; +#endif + return true; } -bool -KeyCharacterMap::getKeyData(int keycode, unsigned short *displayLabel, - unsigned short *number, unsigned short* results) -{ - Key* k = find_key(keycode); - if (k != NULL) { - memcpy(results, k->data, sizeof(short)*(META_MASK + 1)); - *number = k->number; - *displayLabel = k->display_label; - return true; - } else { +bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const { + if (!ch) { return false; } -} -bool -KeyCharacterMap::find_char(uint16_t c, uint32_t* key, uint32_t* mods) -{ - uint32_t N = m_keyCount; - for (int j=0; j<(META_MASK + 1); j++) { - Key const* keys = m_keys; - for (uint32_t i=0; i<N; i++) { - if (keys->data[j] == c) { - *key = keys->keycode; - *mods = j; - return true; + for (size_t i = 0; i < mKeys.size(); i++) { + const Key* key = mKeys.valueAt(i); + + // Try to find the most general behavior that maps to this character. + // For example, the base key behavior will usually be last in the list. + const Behavior* found = NULL; + for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { + if (behavior->character == ch) { + found = behavior; } - keys++; + } + if (found) { + *outKeyCode = mKeys.keyAt(i); + *outMetaState = found->metaState; + return true; } } return false; } -bool -KeyCharacterMap::getEvents(uint16_t* chars, size_t len, - Vector<int32_t>* keys, Vector<uint32_t>* modifiers) -{ - for (size_t i=0; i<len; i++) { - uint32_t k, mods; - if (find_char(chars[i], &k, &mods)) { - keys->add(k); - modifiers->add(mods); - } else { - return false; - } +void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { + outEvents.push(); + KeyEvent& event = outEvents.editTop(); + event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + 0, keyCode, 0, metaState, 0, time, time); +} + +void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t* currentMetaState) { + // Add and remove meta keys symmetrically. + if (down) { + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); + + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, + AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, + AMETA_SHIFT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, + AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, + AMETA_ALT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, + AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, + AMETA_CTRL_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, + AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, + AMETA_META_ON, currentMetaState); + + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); + } else { + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); + + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, + AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, + AMETA_META_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, + AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, + AMETA_CTRL_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, + AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, + AMETA_ALT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, + AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, + AMETA_SHIFT_ON, currentMetaState); + + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); } - return true; } -KeyCharacterMap::Key* -KeyCharacterMap::find_key(int keycode) -{ - Key* keys = m_keys; - int low = 0; - int high = m_keyCount - 1; - int mid; - int n; - while (low <= high) { - mid = (low + high) / 2; - n = keys[mid].keycode; - if (keycode < n) { - high = mid - 1; - } else if (keycode > n) { - low = mid + 1; - } else { - return keys + mid; - } +bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState) { + if ((metaState & keyMetaState) == keyMetaState) { + *currentMetaState = updateMetaState(keyCode, down, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time); + return true; } - return NULL; + return false; } -KeyCharacterMap* -KeyCharacterMap::load(int id) -{ - KeyCharacterMap* map; - char path[PATH_MAX]; - char propName[100]; - char dev[PROPERTY_VALUE_MAX]; - char fn[PROPERTY_VALUE_MAX]; - int err; - - // Check whether the EventHub has set a key character map filename for us already. - sprintf(propName, "hw.keyboards.%u.kcmfile", id); - err = property_get(propName, fn, ""); - if (err > 0) { - map = try_file(fn); - if (map) { - return map; - } - LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, fn); - } - - // Try using the device name. - const char* root = getenv("ANDROID_ROOT"); - - sprintf(propName, "hw.keyboards.%u.devname", id); - err = property_get(propName, dev, ""); - if (err > 0) { - // replace all the spaces with underscores - strcpy(fn, dev); - for (char *p = strchr(fn, ' '); p && *p; p = strchr(p + 1, ' ')) - *p = '_'; - snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, fn); - map = try_file(path); - if (map) { - return map; +void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t leftKeyCode, int32_t leftKeyMetaState, + int32_t rightKeyCode, int32_t rightKeyMetaState, + int32_t eitherKeyMetaState, + int32_t* currentMetaState) { + bool specific = false; + specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + leftKeyCode, leftKeyMetaState, currentMetaState); + specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + rightKeyCode, rightKeyMetaState, currentMetaState); + + if (!specific) { + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + leftKeyCode, eitherKeyMetaState, currentMetaState); + } +} + +void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState) { + if ((metaState & keyMetaState) == keyMetaState) { + *currentMetaState = updateMetaState(keyCode, true, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time); + *currentMetaState = updateMetaState(keyCode, false, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time); + } +} + + +// --- KeyCharacterMap::Key --- + +KeyCharacterMap::Key::Key() : + label(0), number(0), firstBehavior(NULL) { +} + +KeyCharacterMap::Key::~Key() { + Behavior* behavior = firstBehavior; + while (behavior) { + Behavior* next = behavior->next; + delete behavior; + behavior = next; + } +} + + +// --- KeyCharacterMap::Behavior --- + +KeyCharacterMap::Behavior::Behavior() : + next(NULL), metaState(0), character(0), fallbackKeyCode(0) { +} + + +// --- KeyCharacterMap::Parser --- + +KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) { +} + +KeyCharacterMap::Parser::~Parser() { +} + +status_t KeyCharacterMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + switch (mState) { + case STATE_TOP: { + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "type") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseType(); + if (status) return status; + } else if (keywordToken == "key") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseKey(); + if (status) return status; + } else { + LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), + keywordToken.string()); + return BAD_VALUE; + } + break; + } + + case STATE_KEY: { + status_t status = parseKeyProperty(); + if (status) return status; + break; + } + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } } - LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev); - } else { - LOGW("No keyboard for id %d", id); + + mTokenizer->nextLine(); } - snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root); - map = try_file(path); - if (map) { - LOGW("Using default keymap: %s", path); - return map; + if (mState != STATE_TOP) { + LOGE("%s: Unterminated key description at end of file.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - LOGE("Can't find any keycharmaps (also tried %s)", path); - return NULL; + if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) { + LOGE("%s: Missing required keyboard 'type' declaration.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + return NO_ERROR; } -KeyCharacterMap* -KeyCharacterMap::try_file(const char* filename) -{ - KeyCharacterMap* rv = NULL; - Key* keys; - int fd; - off_t filesize; - Header header; - int err; - - fd = open(filename, O_RDONLY); - if (fd == -1) { - LOGW("Can't open keycharmap file"); - return NULL; +status_t KeyCharacterMap::Parser::parseType() { + if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) { + LOGE("%s: Duplicate keyboard 'type' declaration.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - filesize = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); + KeyboardType type; + String8 typeToken = mTokenizer->nextToken(WHITESPACE); + if (typeToken == "NUMERIC") { + type = KEYBOARD_TYPE_NUMERIC; + } else if (typeToken == "PREDICTIVE") { + type = KEYBOARD_TYPE_PREDICTIVE; + } else if (typeToken == "ALPHA") { + type = KEYBOARD_TYPE_ALPHA; + } else if (typeToken == "FULL") { + type = KEYBOARD_TYPE_FULL; + } else if (typeToken == "SPECIAL_FUNCTION") { + type = KEYBOARD_TYPE_SPECIAL_FUNCTION; + } else { + LOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), + typeToken.string()); + return BAD_VALUE; + } + +#if DEBUG_PARSER + LOGD("Parsed type: type=%d.", type); +#endif + mMap->mType = type; + return NO_ERROR; +} - // validate the header - if (filesize <= (off_t)sizeof(header)) { - LOGW("Bad keycharmap - filesize=%d\n", (int)filesize); - goto cleanup1; +status_t KeyCharacterMap::Parser::parseKey() { + String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + if (!keyCode) { + LOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; + } + if (mMap->mKeys.indexOfKey(keyCode) >= 0) { + LOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; } - err = read(fd, &header, sizeof(header)); - if (err == -1) { - LOGW("Error reading keycharmap file"); - goto cleanup1; + mTokenizer->skipDelimiters(WHITESPACE); + String8 openBraceToken = mTokenizer->nextToken(WHITESPACE); + if (openBraceToken != "{") { + LOGE("%s: Expected '{' after key code label, got '%s'.", + mTokenizer->getLocation().string(), openBraceToken.string()); + return BAD_VALUE; } - if (0 != memcmp(header.magic, "keychar", 8)) { - LOGW("Bad keycharmap magic token"); - goto cleanup1; +#if DEBUG_PARSER + LOGD("Parsed beginning of key: keyCode=%d.", keyCode); +#endif + mKeyCode = keyCode; + mMap->mKeys.add(keyCode, new Key()); + mState = STATE_KEY; + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseKeyProperty() { + String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + if (token == "}") { + mState = STATE_TOP; + return NO_ERROR; } - if (header.endian != 0x12345678) { - LOGW("Bad keycharmap endians"); - goto cleanup1; + + Vector<Property> properties; + + // Parse all comma-delimited property names up to the first colon. + for (;;) { + if (token == "label") { + properties.add(Property(PROPERTY_LABEL)); + } else if (token == "number") { + properties.add(Property(PROPERTY_NUMBER)); + } else { + int32_t metaState; + status_t status = parseModifier(token, &metaState); + if (status) { + LOGE("%s: Expected a property name or modifier, got '%s'.", + mTokenizer->getLocation().string(), token.string()); + return status; + } + properties.add(Property(PROPERTY_META, metaState)); + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + char ch = mTokenizer->nextChar(); + if (ch == ':') { + break; + } else if (ch == ',') { + mTokenizer->skipDelimiters(WHITESPACE); + token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + continue; + } + } + + LOGE("%s: Expected ',' or ':' after property name.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - if ((header.version & 0xff) != 2) { - LOGW("Only support keycharmap version 2 (got 0x%08x)", header.version); - goto cleanup1; + + // Parse behavior after the colon. + mTokenizer->skipDelimiters(WHITESPACE); + + Behavior behavior; + bool haveCharacter = false; + bool haveFallback = false; + + do { + char ch = mTokenizer->peekChar(); + if (ch == '\'') { + char16_t character; + status_t status = parseCharacterLiteral(&character); + if (status || !character) { + LOGE("%s: Invalid character literal for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + if (haveCharacter) { + LOGE("%s: Cannot combine multiple character literals or 'none'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + behavior.character = character; + haveCharacter = true; + } else { + token = mTokenizer->nextToken(WHITESPACE); + if (token == "none") { + if (haveCharacter) { + LOGE("%s: Cannot combine multiple character literals or 'none'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + haveCharacter = true; + } else if (token == "fallback") { + mTokenizer->skipDelimiters(WHITESPACE); + token = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(token.string()); + if (!keyCode) { + LOGE("%s: Invalid key code label for fallback behavior, got '%s'.", + mTokenizer->getLocation().string(), + token.string()); + return BAD_VALUE; + } + if (haveFallback) { + LOGE("%s: Cannot combine multiple fallback key codes.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + behavior.fallbackKeyCode = keyCode; + haveFallback = true; + } else { + LOGE("%s: Expected a key behavior after ':'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } + + mTokenizer->skipDelimiters(WHITESPACE); + } while (!mTokenizer->isEol()); + + // Add the behavior. + Key* key = mMap->mKeys.valueFor(mKeyCode); + for (size_t i = 0; i < properties.size(); i++) { + const Property& property = properties.itemAt(i); + switch (property.property) { + case PROPERTY_LABEL: + if (key->label) { + LOGE("%s: Duplicate label for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + key->label = behavior.character; +#if DEBUG_PARSER + LOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label); +#endif + break; + case PROPERTY_NUMBER: + if (key->number) { + LOGE("%s: Duplicate number for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + key->number = behavior.character; +#if DEBUG_PARSER + LOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number); +#endif + break; + case PROPERTY_META: { + for (Behavior* b = key->firstBehavior; b; b = b->next) { + if (b->metaState == property.metaState) { + LOGE("%s: Duplicate key behavior for modifier.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } + Behavior* newBehavior = new Behavior(behavior); + newBehavior->metaState = property.metaState; + newBehavior->next = key->firstBehavior; + key->firstBehavior = newBehavior; +#if DEBUG_PARSER + LOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode, + newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode); +#endif + break; + } + } + } + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) { + if (token == "base") { + *outMetaState = 0; + return NO_ERROR; + } + + int32_t combinedMeta = 0; + + const char* str = token.string(); + const char* start = str; + for (const char* cur = str; ; cur++) { + char ch = *cur; + if (ch == '+' || ch == '\0') { + size_t len = cur - start; + int32_t metaState = 0; + for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) { + if (strlen(modifiers[i].label) == len + && strncmp(modifiers[i].label, start, len) == 0) { + metaState = modifiers[i].metaState; + break; + } + } + if (!metaState) { + return BAD_VALUE; + } + if (combinedMeta & metaState) { + LOGE("%s: Duplicate modifier combination '%s'.", + mTokenizer->getLocation().string(), token.string()); + return BAD_VALUE; + } + + combinedMeta |= metaState; + + if (ch == '\0') { + break; + } + } } - if (filesize < (off_t)(sizeof(Header)+(sizeof(Key)*header.keycount))) { - LOGW("Bad keycharmap file size\n"); - goto cleanup1; + *outMetaState = combinedMeta; + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) { + char ch = mTokenizer->nextChar(); + if (ch != '\'') { + goto Error; } - // read the key data - keys = (Key*)malloc(sizeof(Key)*header.keycount); - err = read(fd, keys, sizeof(Key)*header.keycount); - if (err == -1) { - LOGW("Error reading keycharmap file"); - free(keys); - goto cleanup1; + ch = mTokenizer->nextChar(); + if (ch == '\\') { + // Escape sequence. + ch = mTokenizer->nextChar(); + if (ch == 'n') { + *outCharacter = '\n'; + } else if (ch == 't') { + *outCharacter = '\t'; + } else if (ch == '\\') { + *outCharacter = '\\'; + } else if (ch == '\'') { + *outCharacter = '\''; + } else if (ch == '"') { + *outCharacter = '"'; + } else if (ch == 'u') { + *outCharacter = 0; + for (int i = 0; i < 4; i++) { + ch = mTokenizer->nextChar(); + int digit; + if (ch >= '0' && ch <= '9') { + digit = ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + digit = ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'f') { + digit = ch - 'a' + 10; + } else { + goto Error; + } + *outCharacter = (*outCharacter << 4) | digit; + } + } else { + goto Error; + } + } else if (ch >= 32 && ch <= 126 && ch != '\'') { + // ASCII literal character. + *outCharacter = ch; + } else { + goto Error; } - // return the object - rv = new KeyCharacterMap; - rv->m_keyCount = header.keycount; - rv->m_keys = keys; - rv->m_type = header.kbdtype; + ch = mTokenizer->nextChar(); + if (ch != '\'') { + goto Error; + } -cleanup1: - close(fd); + // Ensure that we consumed the entire token. + if (mTokenizer->nextToken(WHITESPACE).isEmpty()) { + return NO_ERROR; + } - return rv; +Error: + LOGE("%s: Malformed character literal.", mTokenizer->getLocation().string()); + return BAD_VALUE; } + +} // namespace android diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/ui/KeyLayoutMap.cpp index 15ae54c..56bc26f 100644 --- a/libs/ui/KeyLayoutMap.cpp +++ b/libs/ui/KeyLayoutMap.cpp @@ -1,234 +1,213 @@ +/* + * Copyright (C) 2008 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 "KeyLayoutMap" -#include "KeyLayoutMap.h" -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> -#include <utils/String8.h> #include <stdlib.h> -#include <ui/KeycodeLabels.h> +#include <android/keycodes.h> +#include <ui/Keyboard.h> +#include <ui/KeyLayoutMap.h> #include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + +// Enables debug output for mapping. +#define DEBUG_MAPPING 0 + namespace android { -KeyLayoutMap::KeyLayoutMap() - :m_status(NO_INIT), - m_keys() -{ -} +static const char* WHITESPACE = " \t\r"; -KeyLayoutMap::~KeyLayoutMap() -{ +// --- KeyLayoutMap --- + +KeyLayoutMap::KeyLayoutMap() { } -static String8 -next_token(char const** p, int *line) -{ - bool begun = false; - const char* begin = *p; - const char* end = *p; - while (true) { - if (*end == '\n') { - (*line)++; - } - switch (*end) - { - case '#': - if (begun) { - *p = end; - return String8(begin, end-begin); - } else { - do { - begin++; - end++; - } while (*begin != '\0' && *begin != '\n'); - } - case '\0': - case ' ': - case '\n': - case '\r': - case '\t': - if (begun || (*end == '\0')) { - *p = end; - return String8(begin, end-begin); - } else { - begin++; - end++; - break; - } - default: - end++; - begun = true; - } - } +KeyLayoutMap::~KeyLayoutMap() { } -static int32_t -token_to_value(const char *literal, const KeycodeLabel *list) -{ - while (list->literal) { - if (0 == strcmp(literal, list->literal)) { - return list->value; +status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening key layout map file %s.", status, filename.string()); + } else { + KeyLayoutMap* map = new KeyLayoutMap(); + if (!map) { + LOGE("Error allocating key layout map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } } - list++; + delete tokenizer; } - return list->value; + return status; } -status_t -KeyLayoutMap::load(const char* filename) -{ - int fd = open(filename, O_RDONLY); - if (fd < 0) { - LOGE("error opening file=%s err=%s\n", filename, strerror(errno)); - m_status = errno; - return errno; +status_t KeyLayoutMap::map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const { + ssize_t index = mKeys.indexOfKey(scanCode); + if (index < 0) { +#if DEBUG_MAPPING + LOGD("map: scanCode=%d ~ Failed.", scanCode); +#endif + *keyCode = AKEYCODE_UNKNOWN; + *flags = 0; + return NAME_NOT_FOUND; } - off_t len = lseek(fd, 0, SEEK_END); - off_t errlen = lseek(fd, 0, SEEK_SET); - if (len < 0 || errlen < 0) { - close(fd); - LOGE("error seeking file=%s err=%s\n", filename, strerror(errno)); - m_status = errno; - return errno; - } + const Key& k = mKeys.valueAt(index); + *keyCode = k.keyCode; + *flags = k.flags; - char* buf = (char*)malloc(len+1); - if (read(fd, buf, len) != len) { - LOGE("error reading file=%s err=%s\n", filename, strerror(errno)); - m_status = errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); - return errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); - } - errno = 0; - buf[len] = '\0'; +#if DEBUG_MAPPING + LOGD("map: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags); +#endif + return NO_ERROR; +} - int32_t scancode = -1; - int32_t keycode = -1; - uint32_t flags = 0; - uint32_t tmp; - char* end; - status_t err = NO_ERROR; - int line = 1; - char const* p = buf; - enum { BEGIN, SCANCODE, KEYCODE, FLAG } state = BEGIN; - while (true) { - String8 token = next_token(&p, &line); - if (*p == '\0') { - break; - } - switch (state) - { - case BEGIN: - if (token == "key") { - state = SCANCODE; - } else { - LOGE("%s:%d: expected key, got '%s'\n", filename, line, - token.string()); - err = BAD_VALUE; - goto done; - } - break; - case SCANCODE: - scancode = strtol(token.string(), &end, 0); - if (*end != '\0') { - LOGE("%s:%d: expected scancode (a number), got '%s'\n", - filename, line, token.string()); - goto done; - } - //LOGI("%s:%d: got scancode %d\n", filename, line, scancode ); - state = KEYCODE; - break; - case KEYCODE: - keycode = token_to_value(token.string(), KEYCODES); - //LOGI("%s:%d: got keycode %d for %s\n", filename, line, keycode, token.string() ); - if (keycode == 0) { - LOGE("%s:%d: expected keycode, got '%s'\n", - filename, line, token.string()); - goto done; - } - state = FLAG; - break; - case FLAG: - if (token == "key") { - if (scancode != -1) { - //LOGI("got key decl scancode=%d keycode=%d" - // " flags=0x%08x\n", scancode, keycode, flags); - Key k = { keycode, flags }; - m_keys.add(scancode, k); - state = SCANCODE; - scancode = -1; - keycode = -1; - flags = 0; - break; - } - } - tmp = token_to_value(token.string(), FLAGS); - //LOGI("%s:%d: got flags %x for %s\n", filename, line, tmp, token.string() ); - if (tmp == 0) { - LOGE("%s:%d: expected flag, got '%s'\n", - filename, line, token.string()); - goto done; - } - flags |= tmp; - break; +status_t KeyLayoutMap::findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const { + const size_t N = mKeys.size(); + for (size_t i=0; i<N; i++) { + if (mKeys.valueAt(i).keyCode == keyCode) { + outScanCodes->add(mKeys.keyAt(i)); } } - if (state == FLAG && scancode != -1 ) { - //LOGI("got key decl scancode=%d keycode=%d" - // " flags=0x%08x\n", scancode, keycode, flags); - Key k = { keycode, flags }; - m_keys.add(scancode, k); - } - -done: - free(buf); - close(fd); - - m_status = err; - return err; + return NO_ERROR; } -status_t -KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags) const -{ - if (m_status != NO_ERROR) { - return m_status; - } +// --- KeyLayoutMap::Parser --- - ssize_t index = m_keys.indexOfKey(scancode); - if (index < 0) { - //LOGW("couldn't map scancode=%d\n", scancode); - return NAME_NOT_FOUND; - } - - const Key& k = m_keys.valueAt(index); +KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} - *keycode = k.keycode; - *flags = k.flags; +KeyLayoutMap::Parser::~Parser() { +} - //LOGD("mapped scancode=%d to keycode=%d flags=0x%08x\n", scancode, - // keycode, flags); +status_t KeyLayoutMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "key") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseKey(); + if (status) return status; + } else { + LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), + keywordToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + } + mTokenizer->nextLine(); + } return NO_ERROR; } -status_t -KeyLayoutMap::findScancodes(int32_t keycode, Vector<int32_t>* outScancodes) const -{ - if (m_status != NO_ERROR) { - return m_status; +status_t KeyLayoutMap::Parser::parseKey() { + String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); + char* end; + int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); + if (*end) { + LOGE("%s: Expected scan code number, got '%s'.", mTokenizer->getLocation().string(), + scanCodeToken.string()); + return BAD_VALUE; } - - const size_t N = m_keys.size(); - for (size_t i=0; i<N; i++) { - if (m_keys.valueAt(i).keycode == keycode) { - outScancodes->add(m_keys.keyAt(i)); + if (mMap->mKeys.indexOfKey(scanCode) >= 0) { + LOGE("%s: Duplicate entry for scan code '%s'.", mTokenizer->getLocation().string(), + scanCodeToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + if (!keyCode) { + LOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; + } + + uint32_t flags = 0; + for (;;) { + mTokenizer->skipDelimiters(WHITESPACE); + if (mTokenizer->isEol()) break; + + String8 flagToken = mTokenizer->nextToken(WHITESPACE); + uint32_t flag = getKeyFlagByLabel(flagToken.string()); + if (!flag) { + LOGE("%s: Expected flag label, got '%s'.", mTokenizer->getLocation().string(), + flagToken.string()); + return BAD_VALUE; } + if (flags & flag) { + LOGE("%s: Duplicate flag '%s'.", mTokenizer->getLocation().string(), + flagToken.string()); + return BAD_VALUE; + } + flags |= flag; } - + +#if DEBUG_PARSER + LOGD("Parsed key: scanCode=%d, keyCode=%d, flags=0x%08x.", scanCode, keyCode, flags); +#endif + Key key; + key.keyCode = keyCode; + key.flags = flags; + mMap->mKeys.add(scanCode, key); return NO_ERROR; } diff --git a/libs/ui/KeyLayoutMap.h b/libs/ui/KeyLayoutMap.h deleted file mode 100644 index 43f84ce..0000000 --- a/libs/ui/KeyLayoutMap.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef KEYLAYOUTMAP_H -#define KEYLAYOUTMAP_H - -#include <utils/KeyedVector.h> - -namespace android { - -class KeyLayoutMap -{ -public: - KeyLayoutMap(); - ~KeyLayoutMap(); - - status_t load(const char* filename); - - status_t map(int32_t scancode, int32_t *keycode, uint32_t *flags) const; - status_t findScancodes(int32_t keycode, Vector<int32_t>* outScancodes) const; - -private: - struct Key { - int32_t keycode; - uint32_t flags; - }; - - status_t m_status; - KeyedVector<int32_t,Key> m_keys; -}; - -}; - -#endif // KEYLAYOUTMAP_H diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp new file mode 100644 index 0000000..de76e25 --- /dev/null +++ b/libs/ui/Keyboard.cpp @@ -0,0 +1,244 @@ +/* + * 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 "Keyboard" + +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> + +#include <ui/Keyboard.h> +#include <ui/KeycodeLabels.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <cutils/properties.h> + +namespace android { + +static void selectKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) { + if (keyMapInfo.keyMapName.isEmpty()) { + keyMapInfo.keyMapName.setTo(keyMapName); + keyMapInfo.isDefaultKeyMap = defaultKeyMap; + } +} + +static bool probeKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) { + const char* root = getenv("ANDROID_ROOT"); + + // TODO Consider also looking somewhere in a writeable partition like /data for a + // custom keymap supplied by the user for this device. + bool haveKeyLayout = !keyMapInfo.keyLayoutFile.isEmpty(); + if (!haveKeyLayout) { + keyMapInfo.keyLayoutFile.setTo(root); + keyMapInfo.keyLayoutFile.append("/usr/keylayout/"); + keyMapInfo.keyLayoutFile.append(keyMapName); + keyMapInfo.keyLayoutFile.append(".kl"); + if (access(keyMapInfo.keyLayoutFile.string(), R_OK)) { + keyMapInfo.keyLayoutFile.clear(); + } else { + haveKeyLayout = true; + } + } + + bool haveKeyCharacterMap = !keyMapInfo.keyCharacterMapFile.isEmpty(); + if (!haveKeyCharacterMap) { + keyMapInfo.keyCharacterMapFile.setTo(root); + keyMapInfo.keyCharacterMapFile.append("/usr/keychars/"); + keyMapInfo.keyCharacterMapFile.append(keyMapName); + keyMapInfo.keyCharacterMapFile.append(".kcm"); + if (access(keyMapInfo.keyCharacterMapFile.string(), R_OK)) { + keyMapInfo.keyCharacterMapFile.clear(); + } else { + haveKeyCharacterMap = true; + } + } + + if (haveKeyLayout || haveKeyCharacterMap) { + selectKeyMap(keyMapInfo, keyMapName, defaultKeyMap); + } + return haveKeyLayout && haveKeyCharacterMap; +} + +status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo) { + // As an initial key map name, try using the device name. + String8 keyMapName(deviceName); + char* p = keyMapName.lockBuffer(keyMapName.size()); + while (*p) { + if (*p == ' ') *p = '_'; + p++; + } + keyMapName.unlockBuffer(); + + if (probeKeyMap(outKeyMapInfo, keyMapName, false)) return OK; + + // TODO Consider allowing the user to configure a specific key map somehow. + + // Try the Generic key map. + // TODO Apply some additional heuristics here to figure out what kind of + // generic key map to use (US English, etc.). + keyMapName.setTo("Generic"); + if (probeKeyMap(outKeyMapInfo, keyMapName, true)) return OK; + + // Give up! + keyMapName.setTo("unknown"); + selectKeyMap(outKeyMapInfo, keyMapName, true); + LOGE("Could not determine key map for device '%s'.", deviceName.string()); + return NAME_NOT_FOUND; +} + +void setKeyboardProperties(int32_t deviceId, const String8& deviceName, + const KeyMapInfo& keyMapInfo) { + char propName[PROPERTY_KEY_MAX]; + snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId); + property_set(propName, deviceName.string()); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId); + property_set(propName, keyMapInfo.keyMapName.string()); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId); + property_set(propName, keyMapInfo.keyLayoutFile.string()); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); + property_set(propName, keyMapInfo.keyCharacterMapFile.string()); +} + +void clearKeyboardProperties(int32_t deviceId) { + char propName[PROPERTY_KEY_MAX]; + snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId); + property_set(propName, ""); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId); + property_set(propName, ""); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId); + property_set(propName, ""); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); + property_set(propName, ""); +} + +status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) { + char propName[PROPERTY_KEY_MAX]; + char fn[PROPERTY_VALUE_MAX]; + snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); + if (property_get(propName, fn, "") > 0) { + outKeyCharacterMapFile.setTo(fn); + return OK; + } + + const char* root = getenv("ANDROID_ROOT"); + char path[PATH_MAX]; + if (deviceId == DEVICE_ID_VIRTUAL_KEYBOARD) { + snprintf(path, sizeof(path), "%s/usr/keychars/Virtual.kcm", root); + if (!access(path, R_OK)) { + outKeyCharacterMapFile.setTo(path); + return OK; + } + } + + snprintf(path, sizeof(path), "%s/usr/keychars/Generic.kcm", root); + if (!access(path, R_OK)) { + outKeyCharacterMapFile.setTo(path); + return OK; + } + + LOGE("Can't find any key character map files (also tried %s)", path); + return NAME_NOT_FOUND; +} + +static int lookupLabel(const char* literal, const KeycodeLabel *list) { + while (list->literal) { + if (strcmp(literal, list->literal) == 0) { + return list->value; + } + list++; + } + return list->value; +} + +int32_t getKeyCodeByLabel(const char* label) { + return int32_t(lookupLabel(label, KEYCODES)); +} + +uint32_t getKeyFlagByLabel(const char* label) { + return uint32_t(lookupLabel(label, FLAGS)); +} + +static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) { + int32_t newMetaState; + if (down) { + newMetaState = oldMetaState | mask; + } else { + newMetaState = oldMetaState & + ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON); + } + + if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { + newMetaState |= AMETA_ALT_ON; + } + + if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { + newMetaState |= AMETA_SHIFT_ON; + } + + if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { + newMetaState |= AMETA_CTRL_ON; + } + + if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { + newMetaState |= AMETA_META_ON; + } + return newMetaState; +} + +static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) { + if (down) { + return oldMetaState; + } else { + return oldMetaState ^ mask; + } +} + +int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { + int32_t mask; + switch (keyCode) { + case AKEYCODE_ALT_LEFT: + return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState); + case AKEYCODE_ALT_RIGHT: + return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState); + case AKEYCODE_SHIFT_LEFT: + return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState); + case AKEYCODE_SHIFT_RIGHT: + return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState); + case AKEYCODE_SYM: + return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState); + case AKEYCODE_FUNCTION: + return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState); + case AKEYCODE_CTRL_LEFT: + return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState); + case AKEYCODE_CTRL_RIGHT: + return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState); + case AKEYCODE_META_LEFT: + return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState); + case AKEYCODE_META_RIGHT: + return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState); + case AKEYCODE_CAPS_LOCK: + return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState); + case AKEYCODE_NUM_LOCK: + return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState); + case AKEYCODE_SCROLL_LOCK: + return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState); + default: + return oldMetaState; + } +} + + +} // namespace android 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 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index aed3dc7..5374957 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -28,6 +28,7 @@ import android.util.Slog; import android.view.HapticFeedbackConstants; import android.view.IWindowManager; import android.view.InputDevice; +import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -133,7 +134,7 @@ public class KeyButtonView extends ImageView { void sendEvent(int action, int flags, long when) { final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, mRepeat, - 0, 0, 0, flags, InputDevice.SOURCE_KEYBOARD); + 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, flags, InputDevice.SOURCE_KEYBOARD); try { //Slog.d(TAG, "injecting event " + ev); mWindowManager.injectInputEventNoWait(ev); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 18815f5..cd8a065 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -349,7 +349,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } // Set the proper keymap - KeyCharacterMap kmap = KeyCharacterMap.load(event != null ? event.getDeviceId() : 0); + KeyCharacterMap kmap = KeyCharacterMap.load( + event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD); st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC; st.menu.setQwertyMode(st.qwertyMode); } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 8c857da..cc391d5 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -67,6 +67,7 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputQueue; import android.view.InputHandler; +import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.WindowOrientationListener; @@ -2126,7 +2127,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // only do it if the showing app doesn't process the key on its own. long when = whenNanos / 1000000; KeyEvent keyEvent = new KeyEvent(when, when, action, keyCode, 0, 0, - 0, scanCode, flags, InputDevice.SOURCE_KEYBOARD); + KeyCharacterMap.VIRTUAL_KEYBOARD, scanCode, flags, + InputDevice.SOURCE_KEYBOARD); mBroadcastWakeLock.acquire(); mHandler.post(new PassHeadsetKey(keyEvent)); } diff --git a/policy/src/com/android/internal/policy/impl/ShortcutManager.java b/policy/src/com/android/internal/policy/impl/ShortcutManager.java index d86ac44..51377d8 100644 --- a/policy/src/com/android/internal/policy/impl/ShortcutManager.java +++ b/policy/src/com/android/internal/policy/impl/ShortcutManager.java @@ -106,7 +106,7 @@ class ShortcutManager extends ContentObserver { * @return The intent that matches the shortcut, or null if not found. */ public Intent getIntent(int keyCode, int modifiers) { - KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); + KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); // First try the exact keycode (with modifiers) int shortcut = kcm.get(keyCode, modifiers); Intent intent = shortcut != 0 ? mShortcutIntents.get(shortcut) : null; |