Files
android_bootable_recovery/gui/console.cpp
Ethan Yonker 03a42f6c6a Add command line capabilities
Allows sending openrecoveryscript commands to TWRP via shell.
This may be handy for visually impaired users, for various one
click utilities to drive TWRP commands from a computer, for using
TWRP when a catastrophic hardware failure like a shattered screen
prevents you from being able to use touch, or even on devices like
a TV stick where touch and USB mouse input is unavailable.

This patch also includes a few minor changes to openrecoveryscript
including proper support for rebooting via the script and for
decrypting the device via the command line.

Change-Id: I796ad168efdd2da9c25334ac93d1079daaa7b0bc
2014-08-09 07:49:35 -05:00

427 lines
10 KiB
C++

// console.cpp - GUIConsole object
#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 "rapidxml.hpp"
#include "objects.hpp"
static std::vector<std::string> gConsole;
static std::vector<std::string> gConsoleColor;
static FILE* ors_file;
extern "C" void __gui_print(const char *color, char *buf)
{
char *start, *next;
if (buf[0] == '\n' && strlen(buf) < 2) {
// This prevents the double lines bug seen in the console during zip installs
return;
}
for (start = next = buf; *next != '\0';)
{
if (*next == '\n')
{
*next = '\0';
gConsole.push_back(start);
gConsoleColor.push_back(color);
start = ++next;
}
else
++next;
}
// The text after last \n (or whole string if there is no \n)
if(*start) {
gConsole.push_back(start);
gConsoleColor.push_back(color);
}
if (ors_file) {
fprintf(ors_file, "%s\n", buf);
fflush(ors_file);
}
}
extern "C" void gui_print(const char *fmt, ...)
{
char buf[512]; // We're going to limit a single request to 512 bytes
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, 512, fmt, ap);
va_end(ap);
fputs(buf, stdout);
__gui_print("normal", buf);
return;
}
extern "C" void gui_print_color(const char *color, const char *fmt, ...)
{
char buf[512]; // We're going to limit a single request to 512 bytes
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, 512, fmt, ap);
va_end(ap);
fputs(buf, stdout);
__gui_print(color, buf);
return;
}
extern "C" void gui_set_FILE(FILE* f)
{
ors_file = f;
}
GUIConsole::GUIConsole(xml_node<>* node) : GUIObject(node)
{
xml_attribute<>* attr;
xml_node<>* child;
mFont = NULL;
mCurrentLine = -1;
memset(&mForegroundColor, 255, sizeof(COLOR));
memset(&mBackgroundColor, 0, sizeof(COLOR));
mBackgroundColor.alpha = 255;
memset(&mScrollColor, 0x08, sizeof(COLOR));
mScrollColor.alpha = 255;
mLastCount = 0;
mSlideout = 0;
RenderCount = 0;
mSlideoutState = hidden;
mRender = true;
mRenderX = 0; mRenderY = 0; mRenderW = gr_fb_width(); mRenderH = gr_fb_height();
if (!node)
{
mSlideoutX = 0; mSlideoutY = 0; mSlideoutW = 0; mSlideoutH = 0;
mConsoleX = 0; mConsoleY = 0; mConsoleW = gr_fb_width(); mConsoleH = gr_fb_height();
}
else
{
child = node->first_node("font");
if (child)
{
attr = child->first_attribute("resource");
if (attr)
mFont = PageManager::FindResource(attr->value());
}
child = node->first_node("color");
if (child)
{
attr = child->first_attribute("foreground");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mForegroundColor);
}
attr = child->first_attribute("background");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mBackgroundColor);
}
attr = child->first_attribute("scroll");
if (attr)
{
std::string color = attr->value();
ConvertStrToColor(color, &mScrollColor);
}
}
// Load the placement
LoadPlacement(node->first_node("placement"), &mConsoleX, &mConsoleY, &mConsoleW, &mConsoleH);
child = node->first_node("slideout");
if (child)
{
mSlideout = 1;
LoadPlacement(child, &mSlideoutX, &mSlideoutY);
attr = child->first_attribute("resource");
if (attr) mSlideoutImage = PageManager::FindResource(attr->value());
if (mSlideoutImage && mSlideoutImage->GetResource())
{
mSlideoutW = gr_get_width(mSlideoutImage->GetResource());
mSlideoutH = gr_get_height(mSlideoutImage->GetResource());
}
}
}
gr_getFontDetails(mFont, &mFontHeight, NULL);
SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
SetRenderPos(mConsoleX, mConsoleY);
return;
}
int GUIConsole::RenderSlideout(void)
{
if (!mSlideoutImage || !mSlideoutImage->GetResource())
return -1;
gr_blit(mSlideoutImage->GetResource(), 0, 0, mSlideoutW, mSlideoutH, mSlideoutX, mSlideoutY);
return 0;
}
int GUIConsole::RenderConsole(void)
{
void* fontResource = NULL;
if (mFont)
fontResource = mFont->GetResource();
// We fill the background
gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
gr_fill(mConsoleX, mConsoleY, mConsoleW, mConsoleH);
gr_color(mScrollColor.red, mScrollColor.green, mScrollColor.blue, mScrollColor.alpha);
gr_fill(mConsoleX + (mConsoleW * 9 / 10), mConsoleY, (mConsoleW / 10), mConsoleH);
// Don't try to continue to render without data
int prevCount = mLastCount;
mLastCount = gConsole.size();
mRender = false;
if (mLastCount == 0)
return (mSlideout ? RenderSlideout() : 0);
// Due to word wrap, figure out what / how the newly added text needs to be added to the render vector that is word wrapped
// Note, that multiple consoles on different GUI pages may be different widths or use different fonts, so the word wrapping
// may different in different console windows
for (int i = prevCount; i < mLastCount; i++) {
string curr_line = gConsole[i];
string curr_color = gConsoleColor[i];
int line_char_width;
for(;;) {
line_char_width = gr_maxExW(curr_line.c_str(), fontResource, mConsoleW);
if (line_char_width < curr_line.size()) {
rConsole.push_back(curr_line.substr(0, line_char_width));
rConsoleColor.push_back(curr_color);
curr_line = curr_line.substr(line_char_width);
} else {
rConsole.push_back(curr_line);
rConsoleColor.push_back(curr_color);
break;
}
}
}
RenderCount = rConsole.size();
// Find the start point
int start;
int curLine = mCurrentLine; // Thread-safing (Another thread updates this value)
if (curLine == -1)
{
start = RenderCount - mMaxRows;
}
else
{
if (curLine > (int) RenderCount)
curLine = (int) RenderCount;
if ((int) mMaxRows > curLine)
curLine = (int) mMaxRows;
start = curLine - mMaxRows;
}
unsigned int line;
for (line = 0; line < mMaxRows; line++)
{
if ((start + (int) line) >= 0 && (start + (int) line) < (int) RenderCount) {
if (rConsoleColor[start + line] == "normal") {
gr_color(mForegroundColor.red, mForegroundColor.green, mForegroundColor.blue, mForegroundColor.alpha);
} else {
COLOR mFontColor;
std::string color = rConsoleColor[start + line];
ConvertStrToColor(color, &mFontColor);
mFontColor.alpha = 255;
gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
}
gr_textExW(mConsoleX, mStartY + (line * mFontHeight), rConsole[start + line].c_str(), fontResource, mConsoleW + mConsoleX);
}
}
return (mSlideout ? RenderSlideout() : 0);
}
int GUIConsole::Render(void)
{
if(!isConditionTrue())
return 0;
if (mSlideout && mSlideoutState == hidden)
return RenderSlideout();
return RenderConsole();
}
int GUIConsole::Update(void)
{
if(!isConditionTrue())
return 0;
if (mSlideout && mSlideoutState != visible)
{
if (mSlideoutState == hidden)
return 0;
if (mSlideoutState == request_hide)
mSlideoutState = hidden;
if (mSlideoutState == request_show)
mSlideoutState = visible;
// Any time we activate the slider, we reset the position
mCurrentLine = -1;
return 2;
}
if (mCurrentLine == -1 && mLastCount != gConsole.size())
{
// We can use Render, and return for just a flip
Render();
return 2;
}
else if (mRender)
{
// They're still touching, so re-render
Render();
return 2;
}
return 0;
}
int GUIConsole::SetRenderPos(int x, int y, int w, int h)
{
// Adjust the stub position accordingly
mSlideoutX += (x - mConsoleX);
mSlideoutY += (y - mConsoleY);
mConsoleX = x;
mConsoleY = y;
if (w || h)
{
mConsoleW = w;
mConsoleH = h;
}
// Calculate the max rows
mMaxRows = mConsoleH / mFontHeight;
// Adjust so we always fit to bottom
mStartY = mConsoleY + (mConsoleH % mFontHeight);
return 0;
}
// IsInRegion - Checks if the request is handled by this object
// Return 0 if this object handles the request, 1 if not
int GUIConsole::IsInRegion(int x, int y)
{
if (mSlideout)
{
// Check if they tapped the slideout button
if (x >= mSlideoutX && x <= mSlideoutX + mSlideoutW && y >= mSlideoutY && y < mSlideoutY + mSlideoutH)
return 1;
// If we're only rendering the slideout, bail now
if (mSlideoutState == hidden)
return 0;
}
return (x < mConsoleX || x > mConsoleX + mConsoleW || y < mConsoleY || y > mConsoleY + mConsoleH) ? 0 : 1;
}
// NotifyTouch - Notify of a touch event
// Return 0 on success, >0 to ignore remainder of touch, and <0 on error
int GUIConsole::NotifyTouch(TOUCH_STATE state, int x, int y)
{
if(!isConditionTrue())
return -1;
if (mSlideout && mSlideoutState == hidden)
{
if (state == TOUCH_START)
{
mSlideoutState = request_show;
return 1;
}
}
else if (mSlideout && mSlideoutState == visible)
{
// Are we sliding it back in?
if (state == TOUCH_START && x > mSlideoutX && x < (mSlideoutX + mSlideoutW) && y > mSlideoutY && y < (mSlideoutY + mSlideoutH))
{
mSlideoutState = request_hide;
return 1;
}
}
// If we don't have enough lines to scroll, throw this away.
if (RenderCount < mMaxRows) return 1;
// We are scrolling!!!
switch (state)
{
case TOUCH_START:
mLastTouchX = x;
mLastTouchY = y;
break;
case TOUCH_DRAG:
if (x < mConsoleX || x > mConsoleX + mConsoleW || y < mConsoleY || y > mConsoleY + mConsoleH)
break; // touch is outside of the console area -- do nothing
if (y > mLastTouchY + mFontHeight) {
while (y > mLastTouchY + mFontHeight) {
if (mCurrentLine == -1)
mCurrentLine = RenderCount - 1;
else if (mCurrentLine > mMaxRows)
mCurrentLine--;
mLastTouchY += mFontHeight;
}
mRender = true;
} else if (y < mLastTouchY - mFontHeight) {
while (y < mLastTouchY - mFontHeight) {
if (mCurrentLine >= 0)
mCurrentLine++;
mLastTouchY -= mFontHeight;
}
if (mCurrentLine >= (int) RenderCount)
mCurrentLine = -1;
mRender = true;
}
break;
case TOUCH_RELEASE:
mLastTouchY = -1;
case TOUCH_REPEAT:
case TOUCH_HOLD:
break;
}
return 0;
}