Files
android_bootable_recovery/gui/input.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

571 lines
14 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/>.
*/
// input.cpp - GUIInput object
#include <linux/input.h>
#include <pthread.h>
#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 <string>
extern "C" {
#include "../twcommon.h"
}
#include "../minuitwrp/minui.h"
#include "../minuitwrp/truetype.hpp"
#include "rapidxml.hpp"
#include "objects.hpp"
#include "../data.hpp"
#define TW_INPUT_NO_UPDATE -1000 // Magic value for HandleTextLocation when no change in scrolling has occurred
GUIInput::GUIInput(xml_node<>* node)
: GUIObject(node)
{
xml_attribute<>* attr;
xml_node<>* child;
mInputText = NULL;
mAction = NULL;
mBackground = NULL;
mCursor = NULL;
mFont = NULL;
mRendered = false;
HasMask = false;
DrawCursor = false;
isLocalChange = true;
HasAllowed = false;
HasDisabled = false;
cursorX = textWidth = scrollingX = mFontHeight = mFontY = lastX = 0;
mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = MinLen = MaxLen = 0;
mCursorLocation = -1; // -1 is always the end of the string
CursorWidth = 3;
ConvertStrToColor("black", &mBackgroundColor);
ConvertStrToColor("white", &mCursorColor);
mValue = "";
displayValue = "";
if (!node)
return;
// Load text directly from the node
mInputText = new GUIText(node);
// Load action directly from the node
mAction = new GUIAction(node);
if (mInputText->Render() < 0)
{
delete mInputText;
mInputText = NULL;
}
// Load the background
child = FindNode(node, "background");
if (child)
{
mBackground = LoadAttrImage(child, "resource");
mBackgroundColor = LoadAttrColor(child, "color", mBackgroundColor);
}
if (mBackground && mBackground->GetResource())
{
mBackgroundW = mBackground->GetWidth();
mBackgroundH = mBackground->GetHeight();
}
// Load the cursor color
child = FindNode(node, "cursor");
if (child)
{
mCursor = LoadAttrImage(child, "resource");
mCursorColor = LoadAttrColor(child, "color", mCursorColor);
attr = child->first_attribute("hasfocus");
if (attr)
{
std::string focus = attr->value();
SetInputFocus(atoi(focus.c_str()));
}
CursorWidth = LoadAttrIntScaleX(child, "width", CursorWidth);
}
DrawCursor = HasInputFocus;
// Load the font
child = FindNode(node, "font");
if (child)
{
mFont = LoadAttrFont(child, "resource");
if (mFont && mFont->GetResource())
mFontHeight = mFont->GetHeight();
}
child = FindNode(node, "data");
if (child)
{
attr = child->first_attribute("name");
if (attr)
mVariable = attr->value();
attr = child->first_attribute("default");
if (attr)
DataManager::SetValue(mVariable, attr->value());
mMask = LoadAttrString(child, "mask");
HasMask = !mMask.empty();
}
// Load input restrictions
child = FindNode(node, "restrict");
if (child)
{
MinLen = LoadAttrInt(child, "minlen", MinLen);
MaxLen = LoadAttrInt(child, "maxlen", MaxLen);
AllowedList = LoadAttrString(child, "allow");
HasAllowed = !AllowedList.empty();
DisabledList = LoadAttrString(child, "disable");
HasDisabled = !DisabledList.empty();
}
// Load the placement
LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
if (mInputText && mFontHeight && mFontHeight < (unsigned)mRenderH) {
mFontY = ((mRenderH - mFontHeight) / 2) + mRenderY;
mInputText->SetRenderPos(mRenderX, mFontY);
} else
mFontY = mRenderY;
if (mInputText)
mInputText->SetMaxWidth(0);
}
GUIInput::~GUIInput()
{
delete mInputText;
delete mAction;
}
void GUIInput::HandleTextLocation(int x) {
mRendered = false;
if (textWidth <= mRenderW) {
if (x != TW_INPUT_NO_UPDATE)
lastX = x;
scrollingX = 0;
return;
}
if (scrollingX + textWidth < mRenderW) {
scrollingX = mRenderW - textWidth;
}
if (x == TW_INPUT_NO_UPDATE)
return;
scrollingX += x - lastX;
if (scrollingX > 0)
scrollingX = 0;
else if (scrollingX + textWidth < mRenderW)
scrollingX = mRenderW - textWidth;
lastX = x;
}
void GUIInput::UpdateDisplayText() {
void* fontResource = NULL;
if (mFont) {
fontResource = mFont->GetResource();
} else {
textWidth = 0;
return;
}
DataManager::GetValue(mVariable, mValue);
if (HasMask) {
int index, string_size = mValue.size();
string maskedValue;
for (index=0; index<string_size; index++)
maskedValue += mMask;
displayValue = maskedValue;
} else {
displayValue = mValue;
}
textWidth = twrpTruetype::gr_ttf_measureEx(displayValue.c_str(), fontResource);
}
void GUIInput::HandleCursorByTouch(int x) {
// Uses x to find mCursorLocation and cursorX
if (displayValue.size() == 0) {
mCursorLocation = -1;
cursorX = mRenderX;
return;
}
void* fontResource = NULL;
if (mFont) {
fontResource = mFont->GetResource();
} else {
return;
}
string cursorString;
unsigned index = 0, displaySize = displayValue.size();
int prevX = mRenderX + scrollingX;
for (index = 0; index <= displaySize; index++) {
cursorString = displayValue.substr(0, index);
cursorX = twrpTruetype::gr_ttf_measureEx(cursorString.c_str(), fontResource) + mRenderX + scrollingX;
if (cursorX > x) {
if (index > 0 && x <= cursorX - ((x - prevX) / 2) && prevX >= mRenderX) {
// This helps make sure that we can place the cursor before the very first char if the first char is
// is fully visible while also still letting us place the cursor after the last char if fully visible
mCursorLocation = index - 1;
cursorX = prevX;
return;
}
mCursorLocation = index;
if (cursorX > mRenderX + mRenderW) {
cursorX = prevX; // This makes sure that the cursor doesn't get placed after the end of the input box
mCursorLocation--;
return;
}
if (cursorX >= mRenderX) {
return; // This makes sure that the cursor doesn't get placed before the beginning of the input box
}
}
prevX = cursorX;
}
mCursorLocation = -1; // x is at or past the end of the string
}
void GUIInput::HandleCursorByText() {
// Uses mCursorLocation to find cursorX
if (!DrawCursor)
return;
void* fontResource = NULL;
if (mFont) {
fontResource = mFont->GetResource();
} else {
return;
}
int cursorTextWidth = textWidth; // width of text to the left of the cursor
if (mCursorLocation != -1) {
string cursorDisplay = displayValue;
cursorDisplay.resize(mCursorLocation);
cursorTextWidth = twrpTruetype::gr_ttf_measureEx(cursorDisplay.c_str(), fontResource);
}
cursorX = mRenderX + cursorTextWidth + scrollingX;
if (cursorX >= mRenderX + mRenderW) {
scrollingX = mRenderW - cursorTextWidth;
cursorX = mRenderX + mRenderW - CursorWidth;
} else if (cursorX < mRenderX) {
scrollingX = cursorTextWidth * -1;
cursorX = mRenderX;
}
}
int GUIInput::Render(void)
{
if (!isConditionTrue())
{
mRendered = false;
return 0;
}
// First step, fill background
gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
// Next, render the background resource (if it exists)
if (mBackground && mBackground->GetResource())
{
mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
}
int ret = 0;
// Render the text
if (mInputText) {
mInputText->SetRenderPos(mRenderX + scrollingX, mFontY);
mInputText->SetText(displayValue);
gr_clip(mRenderX, mRenderY, mRenderW, mRenderH);
ret = mInputText->Render();
gr_noclip();
}
if (ret < 0)
return ret;
if (HasInputFocus && DrawCursor) {
// Render the cursor
gr_color(mCursorColor.red, mCursorColor.green, mCursorColor.blue, 255);
gr_fill(cursorX, mFontY, CursorWidth, mFontHeight);
}
mRendered = true;
return ret;
}
int GUIInput::Update(void)
{
if (!isConditionTrue()) return (mRendered ? 2 : 0);
if (!mRendered) return 2;
int ret = 0;
if (mInputText) ret = mInputText->Update();
if (ret < 0) return ret;
return ret;
}
int GUIInput::GetSelection(int x, int y)
{
if (x < mRenderX || x - mRenderX > mRenderW || y < mRenderY || y - mRenderY > mRenderH) return -1;
return (x - mRenderX);
}
int GUIInput::NotifyTouch(TOUCH_STATE state, int x, int y)
{
static int startSelection = -1;
if (!isConditionTrue())
return -1;
if (!HasInputFocus) {
if (state != TOUCH_RELEASE)
return 0; // Only change focus if touch releases within the input box
if (GetSelection(x, y) >= 0) {
// When changing focus, we don't scroll or change the cursor location
PageManager::SetKeyBoardFocus(0);
PageManager::NotifyCharInput(0);
SetInputFocus(1);
DrawCursor = true;
mRendered = false;
}
} else {
switch (state) {
case TOUCH_HOLD:
case TOUCH_REPEAT:
break;
case TOUCH_START:
startSelection = GetSelection(x,y);
lastX = x;
DrawCursor = false;
mRendered = false;
break;
case TOUCH_DRAG:
// Check if we dragged out of the selection window
if (GetSelection(x, y) == -1) {
lastX = 0;
break;
}
DrawCursor = false;
// Provide some debounce on initial touches
if (startSelection != -1 && abs(x - lastX) < 6) {
break;
}
startSelection = -1;
if (lastX != x)
HandleTextLocation(x);
break;
case TOUCH_RELEASE:
// We've moved the cursor location
mRendered = false;
DrawCursor = true;
HandleCursorByTouch(x);
break;
}
}
return 0;
}
int GUIInput::NotifyVarChange(const std::string& varName, const std::string& value)
{
GUIObject::NotifyVarChange(varName, value);
if (varName == mVariable) {
if (!isLocalChange) {
UpdateDisplayText();
HandleTextLocation(TW_INPUT_NO_UPDATE);
} else
isLocalChange = false;
return 0;
}
if (varName.empty()) {
UpdateDisplayText();
HandleTextLocation(TW_INPUT_NO_UPDATE);
HandleCursorByText();
}
return 0;
}
int GUIInput::NotifyKey(int key, bool down)
{
if (!HasInputFocus || !down)
return 1;
switch (key)
{
case KEY_LEFT:
if (mCursorLocation == 0)
return 0; // we're already at the beginning
if (mCursorLocation == -1) {
if (displayValue.size() == 0) {
cursorX = mRenderX;
return 0;
}
mCursorLocation = displayValue.size() - 1;
} else {
mCursorLocation--;
}
mRendered = false;
HandleCursorByText();
return 0;
case KEY_RIGHT:
if (mCursorLocation == -1)
return 0; // we're already at the end
mCursorLocation++;
if ((int)displayValue.size() <= mCursorLocation)
mCursorLocation = -1;
HandleCursorByText();
mRendered = false;
return 0;
case KEY_HOME:
case KEY_UP:
if (displayValue.size() == 0)
return 0;
mCursorLocation = 0;
mRendered = false;
cursorX = mRenderX;
return 0;
case KEY_END:
case KEY_DOWN:
mCursorLocation = -1;
mRendered = false;
HandleCursorByText();
return 0;
}
return 1;
}
int GUIInput::NotifyCharInput(int key)
{
if (HasInputFocus) {
if (key == KEYBOARD_BACKSPACE) {
//Backspace
if (mValue.size() > 0 && mCursorLocation != 0) {
if (mCursorLocation == -1) {
mValue.resize(mValue.size() - 1);
} else {
mValue.erase(mCursorLocation - 1, 1);
mCursorLocation--;
}
isLocalChange = true;
DataManager::SetValue(mVariable, mValue);
UpdateDisplayText();
HandleTextLocation(TW_INPUT_NO_UPDATE);
HandleCursorByText();
}
} else if (key == KEYBOARD_SWIPE_LEFT) {
// Delete all
isLocalChange = true;
if (mCursorLocation == -1) {
DataManager::SetValue (mVariable, "");
mValue = "";
textWidth = 0;
mCursorLocation = -1;
} else {
mValue.erase(0, mCursorLocation);
DataManager::SetValue(mVariable, mValue);
mCursorLocation = 0;
}
UpdateDisplayText();
cursorX = mRenderX;
scrollingX = 0;
mRendered = false;
return 0;
} else if (key >= 32) {
// Regular key
if (HasAllowed && AllowedList.find((char)key) == string::npos) {
return 0;
}
if (HasDisabled && DisabledList.find((char)key) != string::npos) {
return 0;
}
if (MaxLen != 0 && mValue.size() >= MaxLen) {
return 0;
}
if (mCursorLocation == -1) {
mValue += key;
} else {
mValue.insert(mCursorLocation, 1, key);
mCursorLocation++;
}
isLocalChange = true;
DataManager::SetValue(mVariable, mValue);
UpdateDisplayText();
HandleTextLocation(TW_INPUT_NO_UPDATE);
HandleCursorByText();
} else if (key == KEYBOARD_ACTION) {
// Action
if (mAction) {
unsigned inputLen = mValue.length();
if (inputLen < MinLen)
return 0;
else if (MaxLen != 0 && inputLen > MaxLen)
return 0;
else
return (mAction ? mAction->NotifyTouch(TOUCH_RELEASE, mRenderX, mRenderY) : 1);
}
}
return 0;
} else {
if (key == 0) {
// Somewhat ugly hack-ish way to tell the box to redraw after losing focus to remove the cursor
mRendered = false;
return 1;
}
}
return 1;
}