summaryrefslogtreecommitdiffstats
path: root/gui/console.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gui/console.cpp413
1 files changed, 413 insertions, 0 deletions
diff --git a/gui/console.cpp b/gui/console.cpp
new file mode 100644
index 000000000..03628ec30
--- /dev/null
+++ b/gui/console.cpp
@@ -0,0 +1,413 @@
+/*
+ Copyright 2015 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/>.
+*/
+
+// console.cpp - GUIConsole object
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+}
+#include "../minuitwrp/minui.h"
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "gui.hpp"
+#include "twmsg.h"
+
+#define GUI_CONSOLE_BUFFER_SIZE 512
+
+static pthread_mutex_t console_lock;
+static size_t last_message_count = 0;
+static std::vector<Message> gMessages;
+
+static std::vector<std::string> gConsole;
+static std::vector<std::string> gConsoleColor;
+static FILE* ors_file = NULL;
+
+struct InitMutex
+{
+ InitMutex() { pthread_mutex_init(&console_lock, NULL); }
+} initMutex;
+
+static void internal_gui_print(const char *color, char *buf)
+{
+ // make sure to flush any outstanding messages first to preserve order of outputs
+ GUIConsole::Translate_Now();
+
+ fputs(buf, stdout);
+ if (ors_file) {
+ fprintf(ors_file, "%s", buf);
+ fflush(ors_file);
+ }
+
+ char *start, *next;
+
+ if (buf[0] == '\n' && strlen(buf) < 2) {
+ // This prevents the double lines bug seen in the console during zip installs
+ return;
+ }
+
+ pthread_mutex_lock(&console_lock);
+ 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);
+ }
+ pthread_mutex_unlock(&console_lock);
+}
+
+extern "C" void gui_print(const char *fmt, ...)
+{
+ char buf[GUI_CONSOLE_BUFFER_SIZE]; // We're going to limit a single request to 512 bytes
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, GUI_CONSOLE_BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ internal_gui_print("normal", buf);
+}
+
+extern "C" void gui_print_color(const char *color, const char *fmt, ...)
+{
+ char buf[GUI_CONSOLE_BUFFER_SIZE]; // We're going to limit a single request to 512 bytes
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, GUI_CONSOLE_BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ internal_gui_print(color, buf);
+}
+
+extern "C" void gui_set_FILE(FILE* f)
+{
+ ors_file = f;
+}
+
+void gui_msg(const char* text)
+{
+ if (text) {
+ Message msg = Msg(text);
+ gui_msg(msg);
+ }
+}
+
+void gui_warn(const char* text)
+{
+ if (text) {
+ Message msg = Msg(msg::kWarning, text);
+ gui_msg(msg);
+ }
+}
+
+void gui_err(const char* text)
+{
+ if (text) {
+ Message msg = Msg(msg::kError, text);
+ gui_msg(msg);
+ }
+}
+
+void gui_highlight(const char* text)
+{
+ if (text) {
+ Message msg = Msg(msg::kHighlight, text);
+ gui_msg(msg);
+ }
+}
+
+void gui_msg(Message msg)
+{
+ std::string output = msg;
+ output += "\n";
+ fputs(output.c_str(), stdout);
+ if (ors_file) {
+ fprintf(ors_file, "%s", output.c_str());
+ fflush(ors_file);
+ }
+ pthread_mutex_lock(&console_lock);
+ gMessages.push_back(msg);
+ pthread_mutex_unlock(&console_lock);
+}
+
+void GUIConsole::Translate_Now()
+{
+ pthread_mutex_lock(&console_lock);
+ size_t message_count = gMessages.size();
+ if (message_count <= last_message_count)
+ {
+ pthread_mutex_unlock(&console_lock);
+ return;
+ }
+
+ for (size_t m = last_message_count; m < message_count; m++) {
+ std::string message = gMessages[m];
+ std::string color = "normal";
+ if (gMessages[m].GetKind() == msg::kError)
+ color = "error";
+ else if (gMessages[m].GetKind() == msg::kHighlight)
+ color = "highlight";
+ else if (gMessages[m].GetKind() == msg::kWarning)
+ color = "warning";
+ gConsole.push_back(message);
+ gConsoleColor.push_back(color);
+ }
+ last_message_count = message_count;
+ pthread_mutex_unlock(&console_lock);
+}
+
+void GUIConsole::Clear_For_Retranslation()
+{
+ pthread_mutex_lock(&console_lock);
+ last_message_count = 0;
+ gConsole.clear();
+ gConsoleColor.clear();
+ pthread_mutex_unlock(&console_lock);
+}
+
+GUIConsole::GUIConsole(xml_node<>* node) : GUIScrollList(node)
+{
+ xml_node<>* child;
+
+ mLastCount = 0;
+ scrollToEnd = true;
+ mSlideoutX = mSlideoutY = mSlideoutW = mSlideoutH = 0;
+ mSlideout = 0;
+ mSlideoutState = visible;
+
+ allowSelection = false; // console doesn't support list item selections
+
+ if (!node)
+ {
+ mRenderX = 0;
+ mRenderY = 0;
+ mRenderW = gr_fb_width();
+ mRenderH = gr_fb_height();
+ }
+ else
+ {
+ child = FindNode(node, "color");
+ if (child)
+ {
+ mFontColor = LoadAttrColor(child, "foreground", mFontColor);
+ mBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor);
+ //mScrollColor = LoadAttrColor(child, "scroll", mScrollColor);
+ }
+
+ child = FindNode(node, "slideout");
+ if (child)
+ {
+ mSlideout = 1;
+ mSlideoutState = hidden;
+ LoadPlacement(child, &mSlideoutX, &mSlideoutY, &mSlideoutW, &mSlideoutH, &mPlacement);
+
+ mSlideoutImage = LoadAttrImage(child, "resource");
+
+ if (mSlideoutImage && mSlideoutImage->GetResource())
+ {
+ mSlideoutW = mSlideoutImage->GetWidth();
+ mSlideoutH = mSlideoutImage->GetHeight();
+ if (mPlacement == CENTER || mPlacement == CENTER_X_ONLY) {
+ mSlideoutX = mSlideoutX - (mSlideoutW / 2);
+ if (mPlacement == CENTER) {
+ mSlideoutY = mSlideoutY - (mSlideoutH / 2);
+ }
+ }
+ }
+ }
+ }
+}
+
+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)
+{
+ Translate_Now();
+ pthread_mutex_lock(&console_lock);
+ AddLines(&gConsole, &gConsoleColor, &mLastCount, &rConsole, &rConsoleColor);
+ pthread_mutex_unlock(&console_lock);
+ GUIScrollList::Render();
+
+ // if last line is fully visible, keep tracking the last line when new lines are added
+ int bottom_offset = GetDisplayRemainder() - actualItemHeight;
+ bool isAtBottom = firstDisplayedItem == (int)GetItemCount() - GetDisplayItemCount() - (bottom_offset != 0) && y_offset == bottom_offset;
+ if (isAtBottom)
+ scrollToEnd = true;
+#if 0
+ // debug - show if we are tracking the last line
+ if (scrollToEnd) {
+ gr_color(0,255,0,255);
+ gr_fill(mRenderX+mRenderW-5, mRenderY+mRenderH-5, 5, 5);
+ } else {
+ gr_color(255,0,0,255);
+ gr_fill(mRenderX+mRenderW-5, mRenderY+mRenderH-5, 5, 5);
+ }
+#endif
+ 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 (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 console, we reset the position
+ SetVisibleListLocation(rConsole.size() - 1);
+ mUpdate = 1;
+ scrollToEnd = true;
+ }
+
+ pthread_mutex_lock(&console_lock);
+ bool addedNewText = AddLines(&gConsole, &gConsoleColor, &mLastCount, &rConsole, &rConsoleColor);
+ pthread_mutex_unlock(&console_lock);
+ if (addedNewText) {
+ // someone added new text
+ // at least the scrollbar must be updated, even if the new lines are currently not visible
+ mUpdate = 1;
+ }
+
+ if (scrollToEnd) {
+ // keep the last line in view
+ SetVisibleListLocation(rConsole.size() - 1);
+ }
+
+ GUIScrollList::Update();
+
+ if (mUpdate) {
+ mUpdate = 0;
+ if (Render() == 0)
+ return 2;
+ }
+ return 0;
+}
+
+// IsInRegion - Checks if the request is handled by this object
+// Return 1 if this object handles the request, 0 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 GUIScrollList::IsInRegion(x, y);
+}
+
+// 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 && x >= mSlideoutX && x < mSlideoutX + mSlideoutW && y >= mSlideoutY && y < mSlideoutY + mSlideoutH) {
+ if (state == TOUCH_START) {
+ if (mSlideoutState == hidden)
+ mSlideoutState = request_show;
+ else if (mSlideoutState == visible)
+ mSlideoutState = request_hide;
+ }
+ return 1;
+ }
+ scrollToEnd = false;
+ return GUIScrollList::NotifyTouch(state, x, y);
+}
+
+size_t GUIConsole::GetItemCount()
+{
+ return rConsole.size();
+}
+
+void GUIConsole::RenderItem(size_t itemindex, int yPos, bool selected __unused)
+{
+ // Set the color for the font
+ if (rConsoleColor[itemindex] == "normal") {
+ gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
+ } else {
+ COLOR FontColor;
+ std::string color = rConsoleColor[itemindex];
+ ConvertStrToColor(color, &FontColor);
+ FontColor.alpha = 255;
+ gr_color(FontColor.red, FontColor.green, FontColor.blue, FontColor.alpha);
+ }
+
+ // render text
+ const char* text = rConsole[itemindex].c_str();
+ gr_textEx_scaleW(mRenderX, yPos, text, mFont->GetResource(), mRenderW, TOP_LEFT, 0);
+}
+
+void GUIConsole::NotifySelect(size_t item_selected __unused)
+{
+ // do nothing - console ignores selections
+}