blob: b1f16c48a80b5c5c9d9e9ca3213819f09dc46e81 [file] [log] [blame]
Dees_Troy51a0e822012-09-05 15:24:24 -04001// console.cpp - GUIConsole object
2
3#include <stdarg.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <fcntl.h>
8#include <sys/reboot.h>
9#include <sys/stat.h>
10#include <sys/time.h>
11#include <sys/mman.h>
12#include <sys/types.h>
13#include <sys/ioctl.h>
14#include <time.h>
15#include <unistd.h>
16#include <stdlib.h>
17
18#include <string>
19
20extern "C" {
Dees_Troy2673cec2013-04-02 20:22:16 +000021#include "../twcommon.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040022#include "../minuitwrp/minui.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040023}
24
25#include "rapidxml.hpp"
26#include "objects.hpp"
27
28
29static std::vector<std::string> gConsole;
Ethan Yonkerbf2cb1c2014-07-02 10:15:54 -050030static std::vector<std::string> gConsoleColor;
Dees_Troy51a0e822012-09-05 15:24:24 -040031
Ethan Yonkerbf2cb1c2014-07-02 10:15:54 -050032extern "C" void __gui_print(const char *color, char *buf)
Dees_Troy51a0e822012-09-05 15:24:24 -040033{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020034 char *start, *next;
Dees_Troy51a0e822012-09-05 15:24:24 -040035
36 if (buf[0] == '\n' && strlen(buf) < 2) {
37 // This prevents the double lines bug seen in the console during zip installs
38 return;
39 }
40
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010041 for (start = next = buf; *next != '\0';)
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020042 {
43 if (*next == '\n')
44 {
45 *next = '\0';
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010046 gConsole.push_back(start);
Ethan Yonkerbf2cb1c2014-07-02 10:15:54 -050047 gConsoleColor.push_back(color);
Dees_Troy51a0e822012-09-05 15:24:24 -040048
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010049 start = ++next;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020050 }
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010051 else
52 ++next;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020053 }
Dees_Troy51a0e822012-09-05 15:24:24 -040054
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010055 // The text after last \n (or whole string if there is no \n)
Ethan Yonkerbf2cb1c2014-07-02 10:15:54 -050056 if(*start) {
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010057 gConsole.push_back(start);
Ethan Yonkerbf2cb1c2014-07-02 10:15:54 -050058 gConsoleColor.push_back(color);
59 }
60}
61
62extern "C" void gui_print(const char *fmt, ...)
63{
64 char buf[512]; // We're going to limit a single request to 512 bytes
65
66 va_list ap;
67 va_start(ap, fmt);
68 vsnprintf(buf, 512, fmt, ap);
69 va_end(ap);
70
71 fputs(buf, stdout);
72
73 __gui_print("normal", buf);
74 return;
75}
76
77extern "C" void gui_print_color(const char *color, const char *fmt, ...)
78{
79 char buf[512]; // We're going to limit a single request to 512 bytes
80
81 va_list ap;
82 va_start(ap, fmt);
83 vsnprintf(buf, 512, fmt, ap);
84 va_end(ap);
85
86 fputs(buf, stdout);
87
88 __gui_print(color, buf);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020089 return;
Dees_Troy51a0e822012-09-05 15:24:24 -040090}
91
Vojtech Bocekede51c52014-02-07 23:58:09 +010092GUIConsole::GUIConsole(xml_node<>* node) : GUIObject(node)
Dees_Troy51a0e822012-09-05 15:24:24 -040093{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020094 xml_attribute<>* attr;
95 xml_node<>* child;
Dees_Troy51a0e822012-09-05 15:24:24 -040096
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020097 mFont = NULL;
98 mCurrentLine = -1;
99 memset(&mForegroundColor, 255, sizeof(COLOR));
100 memset(&mBackgroundColor, 0, sizeof(COLOR));
101 mBackgroundColor.alpha = 255;
102 memset(&mScrollColor, 0x08, sizeof(COLOR));
103 mScrollColor.alpha = 255;
104 mLastCount = 0;
105 mSlideout = 0;
Dees Troy31218ec2014-02-25 20:35:56 +0000106 RenderCount = 0;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200107 mSlideoutState = hidden;
Dees Troy31218ec2014-02-25 20:35:56 +0000108 mRender = true;
Dees_Troy51a0e822012-09-05 15:24:24 -0400109
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200110 mRenderX = 0; mRenderY = 0; mRenderW = gr_fb_width(); mRenderH = gr_fb_height();
Dees_Troy51a0e822012-09-05 15:24:24 -0400111
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200112 if (!node)
113 {
114 mSlideoutX = 0; mSlideoutY = 0; mSlideoutW = 0; mSlideoutH = 0;
115 mConsoleX = 0; mConsoleY = 0; mConsoleW = gr_fb_width(); mConsoleH = gr_fb_height();
116 }
117 else
118 {
119 child = node->first_node("font");
120 if (child)
121 {
122 attr = child->first_attribute("resource");
123 if (attr)
124 mFont = PageManager::FindResource(attr->value());
125 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400126
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200127 child = node->first_node("color");
128 if (child)
129 {
130 attr = child->first_attribute("foreground");
131 if (attr)
132 {
133 std::string color = attr->value();
134 ConvertStrToColor(color, &mForegroundColor);
135 }
136 attr = child->first_attribute("background");
137 if (attr)
138 {
139 std::string color = attr->value();
140 ConvertStrToColor(color, &mBackgroundColor);
141 }
142 attr = child->first_attribute("scroll");
143 if (attr)
144 {
145 std::string color = attr->value();
146 ConvertStrToColor(color, &mScrollColor);
147 }
148 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400149
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200150 // Load the placement
151 LoadPlacement(node->first_node("placement"), &mConsoleX, &mConsoleY, &mConsoleW, &mConsoleH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400152
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200153 child = node->first_node("slideout");
154 if (child)
155 {
156 mSlideout = 1;
157 LoadPlacement(child, &mSlideoutX, &mSlideoutY);
Dees_Troy51a0e822012-09-05 15:24:24 -0400158
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200159 attr = child->first_attribute("resource");
160 if (attr) mSlideoutImage = PageManager::FindResource(attr->value());
Dees_Troy51a0e822012-09-05 15:24:24 -0400161
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200162 if (mSlideoutImage && mSlideoutImage->GetResource())
163 {
164 mSlideoutW = gr_get_width(mSlideoutImage->GetResource());
165 mSlideoutH = gr_get_height(mSlideoutImage->GetResource());
166 }
167 }
168 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400169
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200170 gr_getFontDetails(mFont, &mFontHeight, NULL);
171 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
172 SetRenderPos(mConsoleX, mConsoleY);
173 return;
Dees_Troy51a0e822012-09-05 15:24:24 -0400174}
175
176int GUIConsole::RenderSlideout(void)
177{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200178 if (!mSlideoutImage || !mSlideoutImage->GetResource())
179 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400180
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200181 gr_blit(mSlideoutImage->GetResource(), 0, 0, mSlideoutW, mSlideoutH, mSlideoutX, mSlideoutY);
182 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400183}
184
185int GUIConsole::RenderConsole(void)
186{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200187 void* fontResource = NULL;
188 if (mFont)
189 fontResource = mFont->GetResource();
Dees_Troy51a0e822012-09-05 15:24:24 -0400190
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200191 // We fill the background
192 gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
193 gr_fill(mConsoleX, mConsoleY, mConsoleW, mConsoleH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400194
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200195 gr_color(mScrollColor.red, mScrollColor.green, mScrollColor.blue, mScrollColor.alpha);
196 gr_fill(mConsoleX + (mConsoleW * 9 / 10), mConsoleY, (mConsoleW / 10), mConsoleH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400197
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200198 // Don't try to continue to render without data
Dees Troy31218ec2014-02-25 20:35:56 +0000199 int prevCount = mLastCount;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200200 mLastCount = gConsole.size();
Dees Troy31218ec2014-02-25 20:35:56 +0000201 mRender = false;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200202 if (mLastCount == 0)
203 return (mSlideout ? RenderSlideout() : 0);
Dees_Troy51a0e822012-09-05 15:24:24 -0400204
Dees Troy31218ec2014-02-25 20:35:56 +0000205 // Due to word wrap, figure out what / how the newly added text needs to be added to the render vector that is word wrapped
206 // Note, that multiple consoles on different GUI pages may be different widths or use different fonts, so the word wrapping
207 // may different in different console windows
208 for (int i = prevCount; i < mLastCount; i++) {
209 string curr_line = gConsole[i];
Ethan Yonkerbf2cb1c2014-07-02 10:15:54 -0500210 string curr_color = gConsoleColor[i];
Dees Troy31218ec2014-02-25 20:35:56 +0000211 int line_char_width;
212 for(;;) {
213 line_char_width = gr_maxExW(curr_line.c_str(), fontResource, mConsoleW);
214 if (line_char_width < curr_line.size()) {
215 rConsole.push_back(curr_line.substr(0, line_char_width));
Ethan Yonkerbf2cb1c2014-07-02 10:15:54 -0500216 rConsoleColor.push_back(curr_color);
Ethan Yonker6b4276f2014-03-07 06:48:45 -0600217 curr_line = curr_line.substr(line_char_width);
Dees Troy31218ec2014-02-25 20:35:56 +0000218 } else {
219 rConsole.push_back(curr_line);
Ethan Yonkerbf2cb1c2014-07-02 10:15:54 -0500220 rConsoleColor.push_back(curr_color);
Dees Troy31218ec2014-02-25 20:35:56 +0000221 break;
222 }
223 }
224 }
225 RenderCount = rConsole.size();
226
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200227 // Find the start point
228 int start;
229 int curLine = mCurrentLine; // Thread-safing (Another thread updates this value)
230 if (curLine == -1)
231 {
Dees Troy31218ec2014-02-25 20:35:56 +0000232 start = RenderCount - mMaxRows;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200233 }
234 else
235 {
Dees Troy31218ec2014-02-25 20:35:56 +0000236 if (curLine > (int) RenderCount)
237 curLine = (int) RenderCount;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200238 if ((int) mMaxRows > curLine)
239 curLine = (int) mMaxRows;
240 start = curLine - mMaxRows;
241 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400242
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200243 unsigned int line;
244 for (line = 0; line < mMaxRows; line++)
245 {
Ethan Yonkerbf2cb1c2014-07-02 10:15:54 -0500246 if ((start + (int) line) >= 0 && (start + (int) line) < (int) RenderCount) {
247 if (rConsoleColor[start + line] == "normal") {
248 gr_color(mForegroundColor.red, mForegroundColor.green, mForegroundColor.blue, mForegroundColor.alpha);
249 } else {
250 COLOR mFontColor;
251 std::string color = rConsoleColor[start + line];
252 ConvertStrToColor(color, &mFontColor);
253 mFontColor.alpha = 255;
254 gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
255 }
Dees Troy31218ec2014-02-25 20:35:56 +0000256 gr_textExW(mConsoleX, mStartY + (line * mFontHeight), rConsole[start + line].c_str(), fontResource, mConsoleW + mConsoleX);
Ethan Yonkerbf2cb1c2014-07-02 10:15:54 -0500257 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200258 }
259 return (mSlideout ? RenderSlideout() : 0);
Dees_Troy51a0e822012-09-05 15:24:24 -0400260}
261
262int GUIConsole::Render(void)
263{
Vojtech Bocekede51c52014-02-07 23:58:09 +0100264 if(!isConditionTrue())
265 return 0;
266
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200267 if (mSlideout && mSlideoutState == hidden)
268 return RenderSlideout();
269
270 return RenderConsole();
Dees_Troy51a0e822012-09-05 15:24:24 -0400271}
272
273int GUIConsole::Update(void)
274{
Vojtech Bocekede51c52014-02-07 23:58:09 +0100275 if(!isConditionTrue())
276 return 0;
277
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200278 if (mSlideout && mSlideoutState != visible)
279 {
280 if (mSlideoutState == hidden)
281 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400282
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200283 if (mSlideoutState == request_hide)
284 mSlideoutState = hidden;
Dees_Troy51a0e822012-09-05 15:24:24 -0400285
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200286 if (mSlideoutState == request_show)
287 mSlideoutState = visible;
Dees_Troy51a0e822012-09-05 15:24:24 -0400288
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200289 // Any time we activate the slider, we reset the position
290 mCurrentLine = -1;
291 return 2;
292 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400293
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200294 if (mCurrentLine == -1 && mLastCount != gConsole.size())
295 {
296 // We can use Render, and return for just a flip
297 Render();
298 return 2;
299 }
Dees Troy31218ec2014-02-25 20:35:56 +0000300 else if (mRender)
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200301 {
302 // They're still touching, so re-render
303 Render();
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200304 return 2;
305 }
306 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400307}
308
309int GUIConsole::SetRenderPos(int x, int y, int w, int h)
310{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200311 // Adjust the stub position accordingly
312 mSlideoutX += (x - mConsoleX);
313 mSlideoutY += (y - mConsoleY);
Dees_Troy51a0e822012-09-05 15:24:24 -0400314
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200315 mConsoleX = x;
316 mConsoleY = y;
317 if (w || h)
318 {
319 mConsoleW = w;
320 mConsoleH = h;
321 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400322
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200323 // Calculate the max rows
324 mMaxRows = mConsoleH / mFontHeight;
Dees_Troy51a0e822012-09-05 15:24:24 -0400325
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200326 // Adjust so we always fit to bottom
327 mStartY = mConsoleY + (mConsoleH % mFontHeight);
328 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400329}
330
331// IsInRegion - Checks if the request is handled by this object
332// Return 0 if this object handles the request, 1 if not
333int GUIConsole::IsInRegion(int x, int y)
334{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200335 if (mSlideout)
336 {
337 // Check if they tapped the slideout button
338 if (x >= mSlideoutX && x <= mSlideoutX + mSlideoutW && y >= mSlideoutY && y < mSlideoutY + mSlideoutH)
339 return 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400340
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200341 // If we're only rendering the slideout, bail now
342 if (mSlideoutState == hidden)
343 return 0;
344 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400345
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200346 return (x < mConsoleX || x > mConsoleX + mConsoleW || y < mConsoleY || y > mConsoleY + mConsoleH) ? 0 : 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400347}
348
349// NotifyTouch - Notify of a touch event
350// Return 0 on success, >0 to ignore remainder of touch, and <0 on error
351int GUIConsole::NotifyTouch(TOUCH_STATE state, int x, int y)
352{
Vojtech Bocekede51c52014-02-07 23:58:09 +0100353 if(!isConditionTrue())
354 return -1;
355
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200356 if (mSlideout && mSlideoutState == hidden)
357 {
358 if (state == TOUCH_START)
359 {
360 mSlideoutState = request_show;
361 return 1;
362 }
363 }
364 else if (mSlideout && mSlideoutState == visible)
365 {
366 // Are we sliding it back in?
367 if (state == TOUCH_START && x > mSlideoutX && x < (mSlideoutX + mSlideoutW) && y > mSlideoutY && y < (mSlideoutY + mSlideoutH))
368 {
369 mSlideoutState = request_hide;
370 return 1;
371 }
372 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400373
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200374 // If we don't have enough lines to scroll, throw this away.
Dees Troy31218ec2014-02-25 20:35:56 +0000375 if (RenderCount < mMaxRows) return 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400376
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200377 // We are scrolling!!!
378 switch (state)
379 {
380 case TOUCH_START:
381 mLastTouchX = x;
382 mLastTouchY = y;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200383 break;
Dees_Troy51a0e822012-09-05 15:24:24 -0400384
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200385 case TOUCH_DRAG:
Dees Troy31218ec2014-02-25 20:35:56 +0000386 if (x < mConsoleX || x > mConsoleX + mConsoleW || y < mConsoleY || y > mConsoleY + mConsoleH)
387 break; // touch is outside of the console area -- do nothing
388 if (y > mLastTouchY + mFontHeight) {
389 while (y > mLastTouchY + mFontHeight) {
390 if (mCurrentLine == -1)
391 mCurrentLine = RenderCount - 1;
392 else if (mCurrentLine > mMaxRows)
393 mCurrentLine--;
394 mLastTouchY += mFontHeight;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200395 }
Dees Troy31218ec2014-02-25 20:35:56 +0000396 mRender = true;
397 } else if (y < mLastTouchY - mFontHeight) {
398 while (y < mLastTouchY - mFontHeight) {
399 if (mCurrentLine >= 0)
400 mCurrentLine++;
401 mLastTouchY -= mFontHeight;
402 }
403 if (mCurrentLine >= (int) RenderCount)
404 mCurrentLine = -1;
405 mRender = true;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200406 }
407 break;
Dees_Troy51a0e822012-09-05 15:24:24 -0400408
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200409 case TOUCH_RELEASE:
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200410 mLastTouchY = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400411 case TOUCH_REPEAT:
412 case TOUCH_HOLD:
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200413 break;
414 }
415 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400416}