Files
android_bootable_recovery/gui/keyboard.cpp
bigbiff d58ba18272 AOSP10 TWRP Merge: fix conflicts and update libraries needed
This allows flame to boot TWRP. Still will need to work on
super partition for vendor and system access.

The plan will be to cherry-pick any updates to android-9.0
through gerrit.twrp.me to this branch as a WIP.
2020-03-23 11:18:29 -04:00

643 lines
18 KiB
C++
Executable File

/*
Copyright 2012 to 2020 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 "../minuitwrp/truetype.hpp"
#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 && labelImage->GetResource())
{
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() && labelFont && labelFont->GetResource())
{
void* fontResource = labelFont->GetResource();
int textW = twrpTruetype::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 && mLongpressFont->GetResource()) {
void* fontResource = mLongpressFont->GetResource();
gr_color(mLongpressFontColor.red, mLongpressFontColor.green, mLongpressFontColor.blue, mLongpressFontColor.alpha);
string text(1, keychar);
int textW = twrpTruetype::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) {
#ifndef TW_NO_HAPTICS
DataManager::Vibrate("tw_keyboard_vibrate");
#endif
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
#ifndef TW_NO_HAPTICS
DataManager::Vibrate("tw_keyboard_vibrate");
#endif
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;
}