Files
android_bootable_recovery/gui/keyboard.cpp
Ethan Yonker 63e414fc8a Scale the GUI to fit the screen
With this patch set, if needed, we scale the images during early
boot. TTF support is needed to properly scale the font. No font
scaling is done on the old style fixed width font used in the
console.

Special thanks to _that for figuring out the scaling and blending
function calls to make this possible.

Change-Id: If2f79bef16d6db2e1298bfc3d00c9bcca2bee37a
2015-02-10 14:11:50 -06:00

522 lines
15 KiB
C++

/*
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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include "../data.hpp"
#include <string>
extern "C" {
#include "../twcommon.h"
#include "../minuitwrp/minui.h"
#include "gui.h"
}
#include "rapidxml.hpp"
#include "objects.hpp"
GUIKeyboard::GUIKeyboard(xml_node<>* node)
: GUIObject(node)
{
int layoutindex, rowindex, keyindex, Xindex, Yindex, keyHeight = 0, keyWidth = 0;
rowY = colX = -1;
highlightRenderCount = hasHighlight = hasCapsHighlight = 0;
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++)
keyboardImg[layoutindex] = NULL;
mRendered = false;
currentLayout = 1;
mAction = NULL;
KeyboardHeight = KeyboardWidth = 0;
if (!node) return;
// Load the action
child = node->first_node("action");
if (child)
{
mAction = new GUIAction(node);
}
memset(&mHighlightColor, 0, sizeof(COLOR));
child = node->first_node("highlight");
if (child) {
attr = child->first_attribute("color");
if (attr) {
hasHighlight = 1;
std::string color = attr->value();
ConvertStrToColor(color, &mHighlightColor);
}
}
memset(&mCapsHighlightColor, 0, sizeof(COLOR));
child = node->first_node("capshighlight");
if (child) {
attr = child->first_attribute("color");
if (attr) {
hasCapsHighlight = 1;
std::string color = attr->value();
ConvertStrToColor(color, &mCapsHighlightColor);
}
}
// Load the images for the different layouts
child = node->first_node("layout");
if (child)
{
layoutindex = 1;
strcpy(resource, "resource1");
attr = child->first_attribute(resource);
while (attr && layoutindex < (MAX_KEYBOARD_LAYOUTS + 1)) {
keyboardImg[layoutindex - 1] = PageManager::FindResource(attr->value());
layoutindex++;
resource[8] = (char)(layoutindex + 48);
attr = child->first_attribute(resource);
}
}
// Check the first image to get height and width
if (keyboardImg[0] && keyboardImg[0]->GetResource())
{
KeyboardWidth = gr_get_width(keyboardImg[0]->GetResource());
KeyboardHeight = gr_get_height(keyboardImg[0]->GetResource());
}
// Load all of the layout maps
layoutindex = 1;
strcpy(layout, "layout1");
keylayout = node->first_node(layout);
while (keylayout)
{
if (layoutindex > MAX_KEYBOARD_LAYOUTS) {
LOGERR("Too many layouts defined in keyboard.\n");
return;
}
child = keylayout->first_node("keysize");
if (child) {
attr = child->first_attribute("height");
if (attr)
keyHeight = scale_theme_y(atoi(attr->value()));
else
keyHeight = 0;
attr = child->first_attribute("width");
if (attr)
keyWidth = scale_theme_x(atoi(attr->value()));
else
keyWidth = 0;
attr = child->first_attribute("capslock");
if (attr)
caps_tracking[layoutindex - 1].capslock = atoi(attr->value());
else
caps_tracking[layoutindex - 1].capslock = 1;
attr = child->first_attribute("revert_layout");
if (attr)
caps_tracking[layoutindex - 1].revert_layout = atoi(attr->value());
else
caps_tracking[layoutindex - 1].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;
row_heights[layoutindex - 1][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, keyboard_keys[layoutindex - 1][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, keyboard_keys[layoutindex - 1][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 = node->first_node(layout);
}
int x, y, w, h;
// Load the placement
LoadPlacement(node->first_node("placement"), &x, &y, &w, &h);
SetActionPos(x, y, KeyboardWidth, KeyboardHeight);
SetRenderPos(x, y, w, h);
return;
}
GUIKeyboard::~GUIKeyboard()
{
}
int GUIKeyboard::ParseKey(const char* keyinfo, keyboard_key_class& key, int& Xindex, int keyWidth, bool longpress)
{
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 == 'l') { // This is a different layout: "layout{number}"
keychar = KEYBOARD_LAYOUT;
key.layout = atoi(ptr + 6);
} else if (*ptr == 'a') { // This is an action: "action"
keychar = KEYBOARD_ACTION;
} else
return -1;
}
if (longpress) {
key.longpresskey = keychar;
} else {
key.key = keychar;
Xindex += keyWidth;
key.end_x = Xindex - 1;
}
return 0;
}
int GUIKeyboard::Render(void)
{
if (!isConditionTrue())
{
mRendered = false;
return 0;
}
int ret = 0;
if (keyboardImg[currentLayout - 1] && keyboardImg[currentLayout - 1]->GetResource())
gr_blit(keyboardImg[currentLayout - 1]->GetResource(), 0, 0, KeyboardWidth, KeyboardHeight, mRenderX, mRenderY);
// Draw highlight for capslock
if (hasCapsHighlight && caps_tracking[currentLayout - 1].capslock == 0 && caps_tracking[currentLayout - 1].set_capslock) {
int boxheight, boxwidth, x;
gr_color(mCapsHighlightColor.red, mCapsHighlightColor.green, mCapsHighlightColor.blue, mCapsHighlightColor.alpha);
for (int indexy=0; indexy<MAX_KEYBOARD_ROWS; indexy++) {
for (int indexx=0; indexx<MAX_KEYBOARD_KEYS; indexx++) {
if ((int)keyboard_keys[currentLayout - 1][indexy][indexx].key == KEYBOARD_LAYOUT && (int)keyboard_keys[currentLayout - 1][indexy][indexx].layout == caps_tracking[currentLayout - 1].revert_layout) {
if (indexy == 0)
boxheight = row_heights[currentLayout - 1][indexy];
else
boxheight = row_heights[currentLayout - 1][indexy] - row_heights[currentLayout - 1][indexy - 1];
if (indexx == 0) {
x = mRenderX;
boxwidth = keyboard_keys[currentLayout - 1][indexy][indexx].end_x;
} else {
x = mRenderX + keyboard_keys[currentLayout - 1][indexy][indexx - 1].end_x;
boxwidth = keyboard_keys[currentLayout - 1][indexy][indexx].end_x - keyboard_keys[currentLayout - 1][indexy][indexx - 1].end_x;
}
gr_fill(x, mRenderY + row_heights[currentLayout - 1][indexy - 1], boxwidth, boxheight);
}
}
}
}
if (hasHighlight && highlightRenderCount != 0) {
int boxheight, boxwidth, x;
if (rowY == 0)
boxheight = row_heights[currentLayout - 1][rowY];
else
boxheight = row_heights[currentLayout - 1][rowY] - row_heights[currentLayout - 1][rowY - 1];
if (colX == 0) {
x = mRenderX;
boxwidth = keyboard_keys[currentLayout - 1][rowY][colX].end_x;
} else {
x = mRenderX + keyboard_keys[currentLayout - 1][rowY][colX - 1].end_x;
boxwidth = keyboard_keys[currentLayout - 1][rowY][colX].end_x - keyboard_keys[currentLayout - 1][rowY][colX - 1].end_x;
}
gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
gr_fill(x, mRenderY + row_heights[currentLayout - 1][rowY - 1], boxwidth, boxheight);
if (highlightRenderCount > 0)
highlightRenderCount--;
} else
mRendered = true;
return ret;
}
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)
{
mRenderX = x;
mRenderY = y;
if (w || h)
{
mRenderW = KeyboardWidth;
mRenderH = KeyboardHeight;
}
if (mAction) mAction->SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
return 0;
}
int GUIKeyboard::GetSelection(int x, int y)
{
if (x < mRenderX || x - mRenderX > (int)KeyboardWidth || y < mRenderY || y - mRenderY > (int)KeyboardHeight) return -1;
return 0;
}
int GUIKeyboard::NotifyTouch(TOUCH_STATE state, int x, int y)
{
static int startSelection = -1, was_held = 0, startX = 0;
static unsigned char initial_key = 0;
int indexy, indexx, rely, relx, rowIndex = 0;
rely = y - mRenderY;
relx = x - mRenderX;
if (!isConditionTrue()) return -1;
switch (state)
{
case TOUCH_START:
if (GetSelection(x, y) == 0) {
startSelection = -1;
was_held = 0;
startX = x;
// Find the correct row
for (indexy=0; indexy<MAX_KEYBOARD_ROWS; indexy++) {
if (row_heights[currentLayout - 1][indexy] > rely) {
rowIndex = indexy;
rowY = indexy;
indexy = MAX_KEYBOARD_ROWS;
}
}
// Find the correct key (column)
for (indexx=0; indexx<MAX_KEYBOARD_KEYS; indexx++) {
if (keyboard_keys[currentLayout - 1][rowIndex][indexx].end_x > relx) {
// This is the key that was pressed!
initial_key = keyboard_keys[currentLayout - 1][rowIndex][indexx].key;
colX = indexx;
indexx = MAX_KEYBOARD_KEYS;
}
}
if (initial_key != 0)
highlightRenderCount = -1;
else
highlightRenderCount = 0;
mRendered = false;
} else {
if (highlightRenderCount != 0)
mRendered = false;
highlightRenderCount = 0;
startSelection = 0;
}
break;
case TOUCH_DRAG:
break;
case TOUCH_RELEASE:
if (x < startX - (KeyboardWidth * 0.5)) {
if (highlightRenderCount != 0) {
highlightRenderCount = 0;
mRendered = false;
}
PageManager::NotifyKeyboard(KEYBOARD_SWIPE_LEFT);
return 0;
} else if (x > startX + (KeyboardWidth * 0.5)) {
if (highlightRenderCount != 0) {
highlightRenderCount = 0;
mRendered = false;
}
PageManager::NotifyKeyboard(KEYBOARD_SWIPE_RIGHT);
return 0;
}
case TOUCH_HOLD:
case TOUCH_REPEAT:
if (startSelection == 0 || GetSelection(x, y) == -1) {
if (highlightRenderCount != 0) {
highlightRenderCount = 0;
mRendered = false;
}
return 0;
} else if (highlightRenderCount != 0) {
if (state == TOUCH_RELEASE)
highlightRenderCount = 2;
else
highlightRenderCount = -1;
mRendered = false;
}
// Find the correct row
for (indexy=0; indexy<MAX_KEYBOARD_ROWS; indexy++) {
if (row_heights[currentLayout - 1][indexy] > rely) {
rowIndex = indexy;
indexy = MAX_KEYBOARD_ROWS;
}
}
// Find the correct key (column)
for (indexx=0; indexx<MAX_KEYBOARD_KEYS; indexx++) {
keyboard_key_class& key = keyboard_keys[currentLayout - 1][rowIndex][indexx];
if (key.end_x > relx) {
// This is the key that was pressed!
if (key.key != initial_key) {
// We dragged off of the starting key
startSelection = 0;
break;
} else if (state == TOUCH_RELEASE && was_held == 0) {
DataManager::Vibrate("tw_keyboard_vibrate");
if ((int)key.key < KEYBOARD_SPECIAL_KEYS && (int)key.key > 0) {
// Regular key
PageManager::NotifyKeyboard(key.key);
if (caps_tracking[currentLayout - 1].capslock == 0 && !caps_tracking[currentLayout - 1].set_capslock) {
// caps lock was not set, change layouts
currentLayout = caps_tracking[currentLayout - 1].revert_layout;
mRendered = false;
}
} else if ((int)key.key == KEYBOARD_LAYOUT) {
// Switch layouts
if (caps_tracking[currentLayout - 1].capslock == 0 && key.layout == caps_tracking[currentLayout - 1].revert_layout) {
if (!caps_tracking[currentLayout - 1].set_capslock) {
caps_tracking[currentLayout - 1].set_capslock = 1; // Set the caps lock
} else {
caps_tracking[currentLayout - 1].set_capslock = 0; // Unset the caps lock and change layouts
currentLayout = key.layout;
}
} else {
currentLayout = key.layout;
}
mRendered = false;
} else if ((int)key.key == KEYBOARD_ACTION) {
// Action
highlightRenderCount = 0;
if (mAction) {
// Keyboard has its own action defined
return (mAction ? mAction->NotifyTouch(state, x, y) : 1);
} else {
// Send action notification
PageManager::NotifyKeyboard(key.key);
}
}
} else if (state == TOUCH_HOLD) {
was_held = 1;
if ((int)key.key == KEYBOARD_BACKSPACE) {
// Repeat backspace
PageManager::NotifyKeyboard(key.key);
} else if ((int)key.longpresskey < KEYBOARD_SPECIAL_KEYS && (int)key.longpresskey > 0) {
// Long Press Key
DataManager::Vibrate("tw_keyboard_vibrate");
PageManager::NotifyKeyboard(key.longpresskey);
}
} else if (state == TOUCH_REPEAT) {
was_held = 1;
if ((int)key.key == KEYBOARD_BACKSPACE) {
// Repeat backspace
PageManager::NotifyKeyboard(key.key);
}
}
indexx = MAX_KEYBOARD_KEYS;
}
}
break;
}
return 0;
}