blob: a4cc22d45145bca266b97422a669e8979fa14760 [file] [log] [blame]
Jeff Browna3477c82010-11-10 16:03:06 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "Keyboard"
18
19#include <stdlib.h>
20#include <unistd.h>
21#include <limits.h>
22
23#include <ui/Keyboard.h>
24#include <ui/KeycodeLabels.h>
25#include <utils/Errors.h>
26#include <utils/Log.h>
27#include <cutils/properties.h>
28
29namespace android {
30
Jeff Browna3477c82010-11-10 16:03:06 -080031static bool probeKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) {
Jeff Brown66888372010-11-29 17:37:49 -080032 bool foundOne = false;
33 if (keyMapInfo.keyLayoutFile.isEmpty()) {
34 keyMapInfo.keyLayoutFile.setTo(getInputDeviceConfigurationFilePath(keyMapName,
35 INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
36 if (!keyMapInfo.keyLayoutFile.isEmpty()) {
37 foundOne = true;
Jeff Browna3477c82010-11-10 16:03:06 -080038 }
39 }
40
Jeff Brown66888372010-11-29 17:37:49 -080041 if (keyMapInfo.keyCharacterMapFile.isEmpty()) {
42 keyMapInfo.keyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(keyMapName,
43 INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
44 if (!keyMapInfo.keyCharacterMapFile.isEmpty()) {
45 foundOne = true;
Jeff Browna3477c82010-11-10 16:03:06 -080046 }
47 }
48
Jeff Brown66888372010-11-29 17:37:49 -080049 if (foundOne && defaultKeyMap) {
50 keyMapInfo.isDefaultKeyMap = true;
Jeff Browna3477c82010-11-10 16:03:06 -080051 }
Jeff Brown66888372010-11-29 17:37:49 -080052 return keyMapInfo.isComplete();
Jeff Browna3477c82010-11-10 16:03:06 -080053}
54
Jeff Brown66888372010-11-29 17:37:49 -080055status_t resolveKeyMap(const String8& deviceName,
56 const PropertyMap* deviceConfiguration, KeyMapInfo& outKeyMapInfo) {
57 // Use the configured key layout if available.
58 if (deviceConfiguration) {
59 String8 keyLayoutName;
60 if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
61 keyLayoutName)) {
62 outKeyMapInfo.keyLayoutFile.setTo(getInputDeviceConfigurationFilePath(
63 keyLayoutName, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
64 if (outKeyMapInfo.keyLayoutFile.isEmpty()) {
65 LOGW("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
66 "it was not found.",
67 deviceName.string(), keyLayoutName.string());
68 }
69 }
70
71 String8 keyCharacterMapName;
72 if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
73 keyCharacterMapName)) {
74 outKeyMapInfo.keyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(
75 keyCharacterMapName, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
76 if (outKeyMapInfo.keyCharacterMapFile.isEmpty()) {
77 LOGW("Configuration for keyboard device '%s' requested keyboard character "
78 "map '%s' but it was not found.",
79 deviceName.string(), keyCharacterMapName.string());
80 }
81 }
82
83 if (outKeyMapInfo.isComplete()) {
84 return OK;
85 }
Jeff Browna3477c82010-11-10 16:03:06 -080086 }
Jeff Browna3477c82010-11-10 16:03:06 -080087
Jeff Brown66888372010-11-29 17:37:49 -080088 // Try searching by device name.
89 if (probeKeyMap(outKeyMapInfo, deviceName, false)) {
90 return OK;
91 }
Jeff Browna3477c82010-11-10 16:03:06 -080092
Jeff Brown66888372010-11-29 17:37:49 -080093 // Fall back on the Generic key map.
Jeff Browna3477c82010-11-10 16:03:06 -080094 // TODO Apply some additional heuristics here to figure out what kind of
95 // generic key map to use (US English, etc.).
Jeff Brown66888372010-11-29 17:37:49 -080096 if (probeKeyMap(outKeyMapInfo, String8("Generic"), true)) {
97 return OK;
98 }
Jeff Browna3477c82010-11-10 16:03:06 -080099
100 // Give up!
Jeff Brown66888372010-11-29 17:37:49 -0800101 LOGE("Could not determine key map for device '%s' and the Generic key map was not found!",
102 deviceName.string());
103 outKeyMapInfo.isDefaultKeyMap = true;
Jeff Browna3477c82010-11-10 16:03:06 -0800104 return NAME_NOT_FOUND;
105}
106
107void setKeyboardProperties(int32_t deviceId, const String8& deviceName,
108 const KeyMapInfo& keyMapInfo) {
109 char propName[PROPERTY_KEY_MAX];
110 snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
111 property_set(propName, deviceName.string());
Jeff Browna3477c82010-11-10 16:03:06 -0800112 snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
113 property_set(propName, keyMapInfo.keyLayoutFile.string());
114 snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
115 property_set(propName, keyMapInfo.keyCharacterMapFile.string());
116}
117
118void clearKeyboardProperties(int32_t deviceId) {
119 char propName[PROPERTY_KEY_MAX];
120 snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
121 property_set(propName, "");
Jeff Browna3477c82010-11-10 16:03:06 -0800122 snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
123 property_set(propName, "");
124 snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
125 property_set(propName, "");
126}
127
128status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) {
Jeff Brown66888372010-11-29 17:37:49 -0800129 if (deviceId == DEVICE_ID_VIRTUAL_KEYBOARD) {
130 outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(String8("Virtual"),
131 INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
132 if (!outKeyCharacterMapFile.isEmpty()) {
133 return OK;
134 }
135 }
136
Jeff Browna3477c82010-11-10 16:03:06 -0800137 char propName[PROPERTY_KEY_MAX];
138 char fn[PROPERTY_VALUE_MAX];
139 snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
140 if (property_get(propName, fn, "") > 0) {
141 outKeyCharacterMapFile.setTo(fn);
142 return OK;
143 }
144
Jeff Brown66888372010-11-29 17:37:49 -0800145 outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePath(String8("Generic"),
146 INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
147 if (!outKeyCharacterMapFile.isEmpty()) {
Jeff Browna3477c82010-11-10 16:03:06 -0800148 return OK;
149 }
150
Jeff Brown66888372010-11-29 17:37:49 -0800151 LOGE("Can't find any key character map files (also tried Virtual and Generic key maps)");
Jeff Browna3477c82010-11-10 16:03:06 -0800152 return NAME_NOT_FOUND;
153}
154
155static int lookupLabel(const char* literal, const KeycodeLabel *list) {
156 while (list->literal) {
157 if (strcmp(literal, list->literal) == 0) {
158 return list->value;
159 }
160 list++;
161 }
162 return list->value;
163}
164
165int32_t getKeyCodeByLabel(const char* label) {
166 return int32_t(lookupLabel(label, KEYCODES));
167}
168
169uint32_t getKeyFlagByLabel(const char* label) {
170 return uint32_t(lookupLabel(label, FLAGS));
171}
172
173static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
174 int32_t newMetaState;
175 if (down) {
176 newMetaState = oldMetaState | mask;
177 } else {
178 newMetaState = oldMetaState &
179 ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
180 }
181
182 if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
183 newMetaState |= AMETA_ALT_ON;
184 }
185
186 if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
187 newMetaState |= AMETA_SHIFT_ON;
188 }
189
190 if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
191 newMetaState |= AMETA_CTRL_ON;
192 }
193
194 if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
195 newMetaState |= AMETA_META_ON;
196 }
197 return newMetaState;
198}
199
200static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
201 if (down) {
202 return oldMetaState;
203 } else {
204 return oldMetaState ^ mask;
205 }
206}
207
208int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
209 int32_t mask;
210 switch (keyCode) {
211 case AKEYCODE_ALT_LEFT:
212 return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
213 case AKEYCODE_ALT_RIGHT:
214 return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
215 case AKEYCODE_SHIFT_LEFT:
216 return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
217 case AKEYCODE_SHIFT_RIGHT:
218 return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
219 case AKEYCODE_SYM:
220 return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
221 case AKEYCODE_FUNCTION:
222 return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
223 case AKEYCODE_CTRL_LEFT:
224 return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
225 case AKEYCODE_CTRL_RIGHT:
226 return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
227 case AKEYCODE_META_LEFT:
228 return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
229 case AKEYCODE_META_RIGHT:
230 return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
231 case AKEYCODE_CAPS_LOCK:
232 return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
233 case AKEYCODE_NUM_LOCK:
234 return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
235 case AKEYCODE_SCROLL_LOCK:
236 return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
237 default:
238 return oldMetaState;
239 }
240}
241
242
243} // namespace android