summaryrefslogtreecommitdiffstats
path: root/gui/keyboard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gui/keyboard.cpp')
-rw-r--r--gui/keyboard.cpp633
1 files changed, 633 insertions, 0 deletions
diff --git a/gui/keyboard.cpp b/gui/keyboard.cpp
new file mode 100644
index 000000000..849cf19d8
--- /dev/null
+++ b/gui/keyboard.cpp
@@ -0,0 +1,633 @@
+/*
+ Copyright 2012 bigbiff/Dees_Troy TeamWin
+ This file is part of TWRP/TeamWin Recovery Project.
+
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <linux/input.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../data.hpp"
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "gui.h"
+}
+#include "../minuitwrp/minui.h"
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+bool GUIKeyboard::CtrlActive = false;
+
+GUIKeyboard::GUIKeyboard(xml_node<>* node)
+ : GUIObject(node)
+{
+ int layoutindex, rowindex, keyindex, Xindex, Yindex, keyHeight = 0, keyWidth = 0;
+ currentKey = NULL;
+ highlightRenderCount = 0;
+ hasHighlight = hasCapsHighlight = hasCtrlHighlight = false;
+ char resource[10], layout[8], row[5], key[6], longpress[7];
+ xml_attribute<>* attr;
+ xml_node<>* child;
+ xml_node<>* keylayout;
+ xml_node<>* keyrow;
+
+ for (layoutindex=0; layoutindex<MAX_KEYBOARD_LAYOUTS; layoutindex++) {
+ layouts[layoutindex].keyboardImg = NULL;
+ memset(layouts[layoutindex].keys, 0, sizeof(Layout::keys));
+ memset(layouts[layoutindex].row_end_y, 0, sizeof(Layout::row_end_y));
+ }
+
+ mRendered = false;
+ currentLayout = 1;
+ CapsLockOn = false;
+
+ if (!node) return;
+
+ mHighlightColor = LoadAttrColor(FindNode(node, "highlight"), "color", &hasHighlight);
+ mCapsHighlightColor = LoadAttrColor(FindNode(node, "capshighlight"), "color", &hasCapsHighlight);
+ mCtrlHighlightColor = LoadAttrColor(FindNode(node, "ctrlhighlight"), "color", &hasCtrlHighlight);
+
+ child = FindNode(node, "keymargin");
+ mKeyMarginX = LoadAttrIntScaleX(child, "x", 0);
+ mKeyMarginY = LoadAttrIntScaleY(child, "y", 0);
+
+ child = FindNode(node, "background");
+ mBackgroundColor = LoadAttrColor(child, "color", COLOR(32,32,32,255));
+
+ child = FindNode(node, "key-alphanumeric");
+ mFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel"));
+ mFontColor = LoadAttrColor(child, "textcolor", COLOR(255,255,255,255));
+ mKeyColorAlphanumeric = LoadAttrColor(child, "color", COLOR(0,0,0,0));
+
+ child = FindNode(node, "key-other");
+ mSmallFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel-small"));
+ mFontColorSmall = LoadAttrColor(child, "textcolor", COLOR(192,192,192,255));
+ mKeyColorOther = LoadAttrColor(child, "color", COLOR(0,0,0,0));
+
+ child = FindNode(node, "longpress");
+ mLongpressFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel-longpress"));
+ mLongpressFontColor = LoadAttrColor(child, "textcolor", COLOR(128,128,128,255));
+ LoadPlacement(child, &longpressOffsetX, &longpressOffsetY);
+
+ LoadKeyLabels(node, 0); // load global key labels
+
+ // compatibility ugliness: resources should be specified in the layouts themselves instead
+ // Load the images for the different layouts
+ child = FindNode(node, "layout");
+ if (child)
+ {
+ layoutindex = 1;
+ strcpy(resource, "resource1");
+ attr = child->first_attribute(resource);
+ while (attr && layoutindex < (MAX_KEYBOARD_LAYOUTS + 1)) {
+ layouts[layoutindex - 1].keyboardImg = LoadAttrImage(child, resource);
+
+ layoutindex++;
+ resource[8] = (char)(layoutindex + 48);
+ attr = child->first_attribute(resource);
+ }
+ }
+
+ // Check the first image to get height and width
+ if (layouts[0].keyboardImg && layouts[0].keyboardImg->GetResource())
+ {
+ mRenderW = layouts[0].keyboardImg->GetWidth();
+ mRenderH = layouts[0].keyboardImg->GetHeight();
+ }
+
+ // Load all of the layout maps
+ layoutindex = 1;
+ strcpy(layout, "layout1");
+ keylayout = FindNode(node, layout);
+ while (keylayout)
+ {
+ if (layoutindex > MAX_KEYBOARD_LAYOUTS) {
+ LOGERR("Too many layouts defined in keyboard.\n");
+ return;
+ }
+
+ LoadKeyLabels(keylayout, layoutindex); // load per-layout key labels
+
+ Layout& lay = layouts[layoutindex - 1];
+
+ child = keylayout->first_node("keysize");
+ keyHeight = LoadAttrIntScaleY(child, "height", 0);
+ keyWidth = LoadAttrIntScaleX(child, "width", 0);
+ // compatibility ugliness: capslock="0" means that this is the caps layout. Also it has nothing to do with keysize.
+ lay.is_caps = (LoadAttrInt(child, "capslock", 1) == 0);
+ // compatibility ugliness: revert_layout has nothing to do with keysize.
+ lay.revert_layout = LoadAttrInt(child, "revert_layout", -1);
+
+ rowindex = 1;
+ Yindex = 0;
+ strcpy(row, "row1");
+ keyrow = keylayout->first_node(row);
+ while (keyrow)
+ {
+ if (rowindex > MAX_KEYBOARD_ROWS) {
+ LOGERR("Too many rows defined in keyboard.\n");
+ return;
+ }
+
+ Yindex += keyHeight;
+ lay.row_end_y[rowindex - 1] = Yindex;
+
+ keyindex = 1;
+ Xindex = 0;
+ strcpy(key, "key01");
+ attr = keyrow->first_attribute(key);
+
+ while (attr) {
+ if (keyindex > MAX_KEYBOARD_KEYS) {
+ LOGERR("Too many keys defined in a keyboard row.\n");
+ return;
+ }
+
+ const char* keyinfo = attr->value();
+
+ if (strlen(keyinfo) == 0) {
+ LOGERR("No key info on layout%i, row%i, key%dd.\n", layoutindex, rowindex, keyindex);
+ return;
+ }
+
+ if (ParseKey(keyinfo, lay.keys[rowindex - 1][keyindex - 1], Xindex, keyWidth, false))
+ LOGERR("Invalid key info on layout%i, row%i, key%02i.\n", layoutindex, rowindex, keyindex);
+
+
+ // PROCESS LONG PRESS INFO IF EXISTS
+ sprintf(longpress, "long%02i", keyindex);
+ attr = keyrow->first_attribute(longpress);
+ if (attr) {
+ const char* keyinfo = attr->value();
+
+ if (strlen(keyinfo) == 0) {
+ LOGERR("No long press info on layout%i, row%i, long%dd.\n", layoutindex, rowindex, keyindex);
+ return;
+ }
+
+ if (ParseKey(keyinfo, lay.keys[rowindex - 1][keyindex - 1], Xindex, keyWidth, true))
+ LOGERR("Invalid long press key info on layout%i, row%i, long%02i.\n", layoutindex, rowindex, keyindex);
+ }
+ keyindex++;
+ sprintf(key, "key%02i", keyindex);
+ attr = keyrow->first_attribute(key);
+ }
+ rowindex++;
+ row[3] = (char)(rowindex + 48);
+ keyrow = keylayout->first_node(row);
+ }
+ layoutindex++;
+ layout[6] = (char)(layoutindex + 48);
+ keylayout = FindNode(node, layout);
+ }
+
+ int x, y;
+ // Load the placement
+ LoadPlacement(FindNode(node, "placement"), &x, &y, &mRenderW, &mRenderH);
+ SetRenderPos(x, y, mRenderW, mRenderH);
+ return;
+}
+
+GUIKeyboard::~GUIKeyboard()
+{
+}
+
+int GUIKeyboard::ParseKey(const char* keyinfo, Key& key, int& Xindex, int keyWidth, bool longpress)
+{
+ key.layout = 0;
+ int keychar = 0;
+ if (strlen(keyinfo) == 1) {
+ // This is a single key, simple definition
+ keychar = keyinfo[0];
+ } else {
+ // This key has extra data: {keywidth}:{what_the_key_does}
+ keyWidth = scale_theme_x(atoi(keyinfo));
+
+ const char* ptr = keyinfo;
+ while (*ptr > 32 && *ptr != ':')
+ ptr++;
+ if (*ptr != ':')
+ return -1; // no colon is an error
+ ptr++;
+
+ if (*ptr == 0) { // This is an empty area
+ keychar = 0;
+ } else if (strlen(ptr) == 1) { // This is the character that this key uses
+ keychar = *ptr;
+ } else if (*ptr == 'c') { // This is an ASCII character code: "c:{number}"
+ keychar = atoi(ptr + 2);
+ } else if (*ptr == 'k') { // This is a Linux keycode from input.h: "k:{number}"
+ keychar = -atoi(ptr + 2);
+ } else if (*ptr == 'l') { // This is a different layout: "layout{number}"
+ key.layout = atoi(ptr + 6);
+ } else if (*ptr == 'a') { // This is an action: "action" (the Enter key)
+ keychar = KEYBOARD_ACTION;
+ } else
+ return -1;
+ }
+
+ if (longpress) {
+ key.longpresskey = keychar;
+ } else {
+ key.key = keychar;
+ Xindex += keyWidth;
+ key.end_x = Xindex - 1;
+ }
+
+ return 0;
+}
+
+void GUIKeyboard::LoadKeyLabels(xml_node<>* parent, int layout)
+{
+ for (xml_node<>* child = parent->first_node(); child; child = child->next_sibling()) {
+ std::string name = child->name();
+ if (name == "keylabel") {
+ std::string keydef = LoadAttrString(child, "key", "");
+ Key tempkey;
+ int dummyX;
+ if (ParseKey(keydef.c_str(), tempkey, dummyX, 0, false) == 0) {
+ KeyLabel keylabel;
+ keylabel.key = tempkey.key;
+ keylabel.layout_from = layout;
+ keylabel.layout_to = tempkey.layout;
+ keylabel.text = LoadAttrString(child, "text", "");
+ keylabel.image = LoadAttrImage(child, "resource");
+ mKeyLabels.push_back(keylabel);
+ } else {
+ LOGERR("Ignoring invalid keylabel in layout %d: '%s'.\n", layout, keydef.c_str());
+ }
+ }
+ }
+}
+
+void GUIKeyboard::DrawKey(Key& key, int keyX, int keyY, int keyW, int keyH)
+{
+ int keychar = key.key;
+ if (!keychar && !key.layout)
+ return;
+
+ // key background
+ COLOR& c = (keychar >= 32 && keychar < 127) ? mKeyColorAlphanumeric : mKeyColorOther;
+ gr_color(c.red, c.green, c.blue, c.alpha);
+ keyX += mKeyMarginX;
+ keyY += mKeyMarginY;
+ keyW -= mKeyMarginX * 2;
+ keyH -= mKeyMarginY * 2;
+ gr_fill(keyX, keyY, keyW, keyH);
+
+ // key label
+ FontResource* labelFont = mFont;
+ string labelText;
+ ImageResource* labelImage = NULL;
+ if (keychar > 32 && keychar < 127) {
+ // TODO: this will eventually need UTF-8 support
+ labelText = (char) keychar;
+ if (CtrlActive) {
+ int ctrlchar = KeyCharToCtrlChar(keychar);
+ if (ctrlchar != keychar)
+ labelText = std::string("^") + (char)(ctrlchar + 64);
+ }
+ gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
+ }
+ else {
+ // search for a special key label
+ for (std::vector<KeyLabel>::iterator it = mKeyLabels.begin(); it != mKeyLabels.end(); ++it) {
+ if (it->layout_from > 0 && it->layout_from != currentLayout)
+ continue; // this label is for another layout
+ if (it->key == key.key && it->layout_to == key.layout)
+ {
+ // found a label
+ labelText = it->text;
+ labelImage = it->image;
+ break;
+ }
+ }
+ labelFont = mSmallFont;
+ gr_color(mFontColorSmall.red, mFontColorSmall.green, mFontColorSmall.blue, mFontColorSmall.alpha);
+ }
+
+ if (labelImage)
+ {
+ int w = labelImage->GetWidth();
+ int h = labelImage->GetHeight();
+ int x = keyX + (keyW - w) / 2;
+ int y = keyY + (keyH - h) / 2;
+ gr_blit(labelImage->GetResource(), 0, 0, w, h, x, y);
+ }
+ else if (!labelText.empty())
+ {
+ void* fontResource = labelFont->GetResource();
+ int textW = gr_ttf_measureEx(labelText.c_str(), fontResource);
+ int textH = labelFont->GetHeight();
+ int textX = keyX + (keyW - textW) / 2;
+ int textY = keyY + (keyH - textH) / 2;
+ gr_textEx_scaleW(textX, textY, labelText.c_str(), fontResource, keyW, TOP_LEFT, 0);
+ }
+
+ // longpress key label (only if font is defined)
+ keychar = key.longpresskey;
+ if (keychar > 32 && keychar < 127 && mLongpressFont->GetResource()) {
+ void* fontResource = mLongpressFont->GetResource();
+ gr_color(mLongpressFontColor.red, mLongpressFontColor.green, mLongpressFontColor.blue, mLongpressFontColor.alpha);
+ string text(1, keychar);
+ int textW = gr_ttf_measureEx(text.c_str(), fontResource);
+ int textX = keyX + keyW - longpressOffsetX - textW;
+ int textY = keyY + longpressOffsetY;
+ gr_textEx_scaleW(textX, textY, text.c_str(), fontResource, keyW, TOP_LEFT, 0);
+ }
+}
+
+int GUIKeyboard::KeyCharToCtrlChar(int key)
+{
+ // convert upper and lower case to ctrl chars
+ // Ctrl+A to Ctrl+_ (we don't support entering null bytes)
+ if (key >= 65 && key <= 127 && key != 96)
+ return key & 0x1f;
+ return key; // pass on others (already ctrl chars, numbers, etc.) unchanged
+}
+
+int GUIKeyboard::Render(void)
+{
+ if (!isConditionTrue())
+ {
+ mRendered = false;
+ return 0;
+ }
+
+ Layout& lay = layouts[currentLayout - 1];
+
+ bool drawKeys = false;
+ if (lay.keyboardImg && lay.keyboardImg->GetResource())
+ // keyboard is image based
+ gr_blit(lay.keyboardImg->GetResource(), 0, 0, mRenderW, mRenderH, mRenderX, mRenderY);
+ else {
+ // keyboard is software drawn
+ // fill background
+ gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha);
+ gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
+ drawKeys = true;
+ }
+
+ // draw keys
+ int y1 = 0;
+ for (int row = 0; row < MAX_KEYBOARD_ROWS; ++row) {
+ int rowY = mRenderY + y1;
+ int rowH = lay.row_end_y[row] - y1;
+ y1 = lay.row_end_y[row];
+ int x1 = 0;
+ for (int col = 0; col < MAX_KEYBOARD_KEYS; ++col) {
+ Key& key = lay.keys[row][col];
+ int keyY = rowY;
+ int keyH = rowH;
+ int keyX = mRenderX + x1;
+ int keyW = key.end_x - x1;
+ x1 = key.end_x;
+
+ // Draw key for software drawn keyboard
+ if (drawKeys)
+ DrawKey(key, keyX, keyY, keyW, keyH);
+
+ // Draw highlight for capslock
+ if (hasCapsHighlight && lay.is_caps && CapsLockOn && key.layout > 0 && key.layout == lay.revert_layout) {
+ gr_color(mCapsHighlightColor.red, mCapsHighlightColor.green, mCapsHighlightColor.blue, mCapsHighlightColor.alpha);
+ gr_fill(keyX, keyY, keyW, keyH);
+ }
+
+ // Draw highlight for control
+ if (hasCtrlHighlight && key.key == -KEY_LEFTCTRL && CtrlActive) {
+ gr_color(mCtrlHighlightColor.red, mCtrlHighlightColor.green, mCtrlHighlightColor.blue, mCtrlHighlightColor.alpha);
+ gr_fill(keyX, keyY, keyW, keyH);
+ }
+
+ // Highlight current key
+ if (hasHighlight && &key == currentKey && highlightRenderCount != 0) {
+ gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
+ gr_fill(keyX, keyY, keyW, keyH);
+ }
+ }
+ }
+
+ if (!hasHighlight || highlightRenderCount == 0)
+ mRendered = true;
+ else if (highlightRenderCount > 0)
+ highlightRenderCount--;
+ return 0;
+}
+
+int GUIKeyboard::Update(void)
+{
+ if (!isConditionTrue()) return (mRendered ? 2 : 0);
+ if (!mRendered) return 2;
+
+ return 0;
+}
+
+int GUIKeyboard::SetRenderPos(int x, int y, int w, int h)
+{
+ RenderObject::SetRenderPos(x, y, w, h);
+ SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+ return 0;
+}
+
+GUIKeyboard::Key* GUIKeyboard::HitTestKey(int x, int y)
+{
+ if (!IsInRegion(x, y))
+ return NULL;
+
+ int rely = y - mRenderY;
+ int relx = x - mRenderX;
+
+ Layout& lay = layouts[currentLayout - 1];
+
+ // Find the correct row
+ int row;
+ for (row = 0; row < MAX_KEYBOARD_ROWS; ++row) {
+ if (lay.row_end_y[row] > rely)
+ break;
+ }
+ if (row == MAX_KEYBOARD_ROWS)
+ return NULL;
+
+ // Find the correct key (column)
+ int col;
+ int x1 = 0;
+ for (col = 0; col < MAX_KEYBOARD_KEYS; ++col) {
+ Key& key = lay.keys[row][col];
+ if (x1 <= relx && relx < key.end_x && (key.key != 0 || key.layout != 0)) {
+ // This is the key that was pressed!
+ return &key;
+ }
+ x1 = key.end_x;
+ }
+ return NULL;
+}
+
+int GUIKeyboard::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+ static int was_held = 0, startX = 0;
+
+ if (!isConditionTrue()) return -1;
+
+ switch (state)
+ {
+ case TOUCH_START:
+ was_held = 0;
+ startX = x;
+ currentKey = HitTestKey(x, y);
+ if (currentKey)
+ highlightRenderCount = -1;
+ else
+ highlightRenderCount = 0;
+ mRendered = false;
+ break;
+
+ case TOUCH_DRAG:
+ break;
+
+ case TOUCH_RELEASE:
+ // TODO: we might want to notify of key releases here
+ if (x < startX - (mRenderW * 0.5)) {
+ if (highlightRenderCount != 0) {
+ highlightRenderCount = 0;
+ mRendered = false;
+ }
+ PageManager::NotifyCharInput(KEYBOARD_SWIPE_LEFT);
+ return 0;
+ } else if (x > startX + (mRenderW * 0.5)) {
+ if (highlightRenderCount != 0) {
+ highlightRenderCount = 0;
+ mRendered = false;
+ }
+ PageManager::NotifyCharInput(KEYBOARD_SWIPE_RIGHT);
+ return 0;
+ }
+ // fall through
+ case TOUCH_HOLD:
+ case TOUCH_REPEAT:
+ if (!currentKey) {
+ if (highlightRenderCount != 0) {
+ highlightRenderCount = 0;
+ mRendered = false;
+ }
+ return 0;
+ }
+
+ if (highlightRenderCount != 0) {
+ if (state == TOUCH_RELEASE)
+ highlightRenderCount = 2;
+ else
+ highlightRenderCount = -1;
+ mRendered = false;
+ }
+
+ if (HitTestKey(x, y) != currentKey) {
+ // We dragged off of the starting key
+ currentKey = NULL;
+ if (highlightRenderCount != 0) {
+ highlightRenderCount = 0;
+ mRendered = false;
+ }
+ return 0;
+ } else {
+ Key& key = *currentKey;
+ bool repeatKey = false;
+ Layout& lay = layouts[currentLayout - 1];
+ if (state == TOUCH_RELEASE && was_held == 0) {
+ DataManager::Vibrate("tw_keyboard_vibrate");
+ if (key.layout > 0) {
+ // Switch layouts
+ if (lay.is_caps && key.layout == lay.revert_layout && !CapsLockOn) {
+ CapsLockOn = true; // Set the caps lock
+ } else {
+ CapsLockOn = false; // Unset the caps lock and change layouts
+ currentLayout = key.layout;
+ }
+ mRendered = false;
+ } else if (key.key == KEYBOARD_ACTION) {
+ // Action
+ highlightRenderCount = 0;
+ // Send action notification
+ PageManager::NotifyCharInput(key.key);
+ } else if (key.key == -KEY_LEFTCTRL) {
+ CtrlActive = !CtrlActive; // toggle Control key state
+ mRendered = false; // render Ctrl key highlight
+ } else if (key.key != 0) {
+ // Regular key
+ if (key.key > 0) {
+ // ASCII code or character
+ int keycode = key.key;
+ if (CtrlActive) {
+ CtrlActive = false;
+ mRendered = false;
+ keycode = KeyCharToCtrlChar(key.key);
+ }
+ PageManager::NotifyCharInput(keycode);
+ } else {
+ // Linux key code
+ PageManager::NotifyKey(-key.key, true);
+ PageManager::NotifyKey(-key.key, false);
+ }
+ if (!CapsLockOn && lay.is_caps) {
+ // caps lock was not set, change layouts
+ currentLayout = lay.revert_layout;
+ mRendered = false;
+ }
+ }
+ } else if (state == TOUCH_HOLD) {
+ was_held = 1;
+ if (key.longpresskey > 0) {
+ // Long Press Key
+ DataManager::Vibrate("tw_keyboard_vibrate");
+ PageManager::NotifyCharInput(key.longpresskey);
+ }
+ else
+ repeatKey = true;
+ } else if (state == TOUCH_REPEAT) {
+ was_held = 1;
+ repeatKey = true;
+ }
+ if (repeatKey) {
+ if (key.key == KEYBOARD_BACKSPACE) {
+ // Repeat backspace
+ PageManager::NotifyCharInput(key.key);
+ }
+ switch (key.key)
+ {
+ // Repeat arrows
+ case -KEY_LEFT:
+ case -KEY_RIGHT:
+ case -KEY_UP:
+ case -KEY_DOWN:
+ PageManager::NotifyKey(-key.key, true);
+ PageManager::NotifyKey(-key.key, false);
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ return 0;
+}
+
+void GUIKeyboard::SetPageFocus(int inFocus)
+{
+ if (inFocus)
+ CtrlActive = false;
+}