blob: 848a3447232c1e310e1139c43db745c85991d5d6 [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;
30
31extern "C" void gui_print(const char *fmt, ...)
32{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020033 char buf[512]; // We're going to limit a single request to 512 bytes
Dees_Troy51a0e822012-09-05 15:24:24 -040034
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020035 va_list ap;
36 va_start(ap, fmt);
37 vsnprintf(buf, 512, fmt, ap);
38 va_end(ap);
Dees_Troy51a0e822012-09-05 15:24:24 -040039
Dees_Troy32c8eb82012-09-11 15:28:06 -040040 fputs(buf, stdout);
41
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020042 char *start, *next;
Dees_Troy51a0e822012-09-05 15:24:24 -040043
44 if (buf[0] == '\n' && strlen(buf) < 2) {
45 // This prevents the double lines bug seen in the console during zip installs
46 return;
47 }
48
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010049 for (start = next = buf; *next != '\0';)
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020050 {
51 if (*next == '\n')
52 {
53 *next = '\0';
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010054 gConsole.push_back(start);
Dees_Troy51a0e822012-09-05 15:24:24 -040055
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010056 start = ++next;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020057 }
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010058 else
59 ++next;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020060 }
Dees_Troy51a0e822012-09-05 15:24:24 -040061
Vojtech Bocekd5b26d62014-03-06 22:56:13 +010062 // The text after last \n (or whole string if there is no \n)
63 if(*start)
64 gConsole.push_back(start);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020065 return;
Dees_Troy51a0e822012-09-05 15:24:24 -040066}
67
Vojtech Bocekede51c52014-02-07 23:58:09 +010068GUIConsole::GUIConsole(xml_node<>* node) : GUIObject(node)
Dees_Troy51a0e822012-09-05 15:24:24 -040069{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020070 xml_attribute<>* attr;
71 xml_node<>* child;
Dees_Troy51a0e822012-09-05 15:24:24 -040072
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020073 mFont = NULL;
74 mCurrentLine = -1;
75 memset(&mForegroundColor, 255, sizeof(COLOR));
76 memset(&mBackgroundColor, 0, sizeof(COLOR));
77 mBackgroundColor.alpha = 255;
78 memset(&mScrollColor, 0x08, sizeof(COLOR));
79 mScrollColor.alpha = 255;
80 mLastCount = 0;
81 mSlideout = 0;
Dees Troy31218ec2014-02-25 20:35:56 +000082 RenderCount = 0;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020083 mSlideoutState = hidden;
Dees Troy31218ec2014-02-25 20:35:56 +000084 mRender = true;
Dees_Troy51a0e822012-09-05 15:24:24 -040085
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020086 mRenderX = 0; mRenderY = 0; mRenderW = gr_fb_width(); mRenderH = gr_fb_height();
Dees_Troy51a0e822012-09-05 15:24:24 -040087
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020088 if (!node)
89 {
90 mSlideoutX = 0; mSlideoutY = 0; mSlideoutW = 0; mSlideoutH = 0;
91 mConsoleX = 0; mConsoleY = 0; mConsoleW = gr_fb_width(); mConsoleH = gr_fb_height();
92 }
93 else
94 {
95 child = node->first_node("font");
96 if (child)
97 {
98 attr = child->first_attribute("resource");
99 if (attr)
100 mFont = PageManager::FindResource(attr->value());
101 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400102
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200103 child = node->first_node("color");
104 if (child)
105 {
106 attr = child->first_attribute("foreground");
107 if (attr)
108 {
109 std::string color = attr->value();
110 ConvertStrToColor(color, &mForegroundColor);
111 }
112 attr = child->first_attribute("background");
113 if (attr)
114 {
115 std::string color = attr->value();
116 ConvertStrToColor(color, &mBackgroundColor);
117 }
118 attr = child->first_attribute("scroll");
119 if (attr)
120 {
121 std::string color = attr->value();
122 ConvertStrToColor(color, &mScrollColor);
123 }
124 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400125
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200126 // Load the placement
127 LoadPlacement(node->first_node("placement"), &mConsoleX, &mConsoleY, &mConsoleW, &mConsoleH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400128
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200129 child = node->first_node("slideout");
130 if (child)
131 {
132 mSlideout = 1;
133 LoadPlacement(child, &mSlideoutX, &mSlideoutY);
Dees_Troy51a0e822012-09-05 15:24:24 -0400134
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200135 attr = child->first_attribute("resource");
136 if (attr) mSlideoutImage = PageManager::FindResource(attr->value());
Dees_Troy51a0e822012-09-05 15:24:24 -0400137
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200138 if (mSlideoutImage && mSlideoutImage->GetResource())
139 {
140 mSlideoutW = gr_get_width(mSlideoutImage->GetResource());
141 mSlideoutH = gr_get_height(mSlideoutImage->GetResource());
142 }
143 }
144 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400145
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200146 gr_getFontDetails(mFont, &mFontHeight, NULL);
147 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
148 SetRenderPos(mConsoleX, mConsoleY);
149 return;
Dees_Troy51a0e822012-09-05 15:24:24 -0400150}
151
152int GUIConsole::RenderSlideout(void)
153{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200154 if (!mSlideoutImage || !mSlideoutImage->GetResource())
155 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400156
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200157 gr_blit(mSlideoutImage->GetResource(), 0, 0, mSlideoutW, mSlideoutH, mSlideoutX, mSlideoutY);
158 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400159}
160
161int GUIConsole::RenderConsole(void)
162{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200163 void* fontResource = NULL;
164 if (mFont)
165 fontResource = mFont->GetResource();
Dees_Troy51a0e822012-09-05 15:24:24 -0400166
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200167 // We fill the background
168 gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
169 gr_fill(mConsoleX, mConsoleY, mConsoleW, mConsoleH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400170
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200171 gr_color(mScrollColor.red, mScrollColor.green, mScrollColor.blue, mScrollColor.alpha);
172 gr_fill(mConsoleX + (mConsoleW * 9 / 10), mConsoleY, (mConsoleW / 10), mConsoleH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400173
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200174 // Render the lines
175 gr_color(mForegroundColor.red, mForegroundColor.green, mForegroundColor.blue, mForegroundColor.alpha);
Dees_Troy51a0e822012-09-05 15:24:24 -0400176
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200177 // Don't try to continue to render without data
Dees Troy31218ec2014-02-25 20:35:56 +0000178 int prevCount = mLastCount;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200179 mLastCount = gConsole.size();
Dees Troy31218ec2014-02-25 20:35:56 +0000180 mRender = false;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200181 if (mLastCount == 0)
182 return (mSlideout ? RenderSlideout() : 0);
Dees_Troy51a0e822012-09-05 15:24:24 -0400183
Dees Troy31218ec2014-02-25 20:35:56 +0000184 // Due to word wrap, figure out what / how the newly added text needs to be added to the render vector that is word wrapped
185 // Note, that multiple consoles on different GUI pages may be different widths or use different fonts, so the word wrapping
186 // may different in different console windows
187 for (int i = prevCount; i < mLastCount; i++) {
188 string curr_line = gConsole[i];
189 int line_char_width;
190 for(;;) {
191 line_char_width = gr_maxExW(curr_line.c_str(), fontResource, mConsoleW);
192 if (line_char_width < curr_line.size()) {
193 rConsole.push_back(curr_line.substr(0, line_char_width));
Ethan Yonker6b4276f2014-03-07 06:48:45 -0600194 curr_line = curr_line.substr(line_char_width);
Dees Troy31218ec2014-02-25 20:35:56 +0000195 } else {
196 rConsole.push_back(curr_line);
197 break;
198 }
199 }
200 }
201 RenderCount = rConsole.size();
202
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200203 // Find the start point
204 int start;
205 int curLine = mCurrentLine; // Thread-safing (Another thread updates this value)
206 if (curLine == -1)
207 {
Dees Troy31218ec2014-02-25 20:35:56 +0000208 start = RenderCount - mMaxRows;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200209 }
210 else
211 {
Dees Troy31218ec2014-02-25 20:35:56 +0000212 if (curLine > (int) RenderCount)
213 curLine = (int) RenderCount;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200214 if ((int) mMaxRows > curLine)
215 curLine = (int) mMaxRows;
216 start = curLine - mMaxRows;
217 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400218
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200219 unsigned int line;
220 for (line = 0; line < mMaxRows; line++)
221 {
Dees Troy31218ec2014-02-25 20:35:56 +0000222 if ((start + (int) line) >= 0 && (start + (int) line) < (int) RenderCount)
223 gr_textExW(mConsoleX, mStartY + (line * mFontHeight), rConsole[start + line].c_str(), fontResource, mConsoleW + mConsoleX);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200224 }
225 return (mSlideout ? RenderSlideout() : 0);
Dees_Troy51a0e822012-09-05 15:24:24 -0400226}
227
228int GUIConsole::Render(void)
229{
Vojtech Bocekede51c52014-02-07 23:58:09 +0100230 if(!isConditionTrue())
231 return 0;
232
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200233 if (mSlideout && mSlideoutState == hidden)
234 return RenderSlideout();
235
236 return RenderConsole();
Dees_Troy51a0e822012-09-05 15:24:24 -0400237}
238
239int GUIConsole::Update(void)
240{
Vojtech Bocekede51c52014-02-07 23:58:09 +0100241 if(!isConditionTrue())
242 return 0;
243
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200244 if (mSlideout && mSlideoutState != visible)
245 {
246 if (mSlideoutState == hidden)
247 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400248
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200249 if (mSlideoutState == request_hide)
250 mSlideoutState = hidden;
Dees_Troy51a0e822012-09-05 15:24:24 -0400251
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200252 if (mSlideoutState == request_show)
253 mSlideoutState = visible;
Dees_Troy51a0e822012-09-05 15:24:24 -0400254
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200255 // Any time we activate the slider, we reset the position
256 mCurrentLine = -1;
257 return 2;
258 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400259
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200260 if (mCurrentLine == -1 && mLastCount != gConsole.size())
261 {
262 // We can use Render, and return for just a flip
263 Render();
264 return 2;
265 }
Dees Troy31218ec2014-02-25 20:35:56 +0000266 else if (mRender)
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200267 {
268 // They're still touching, so re-render
269 Render();
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200270 return 2;
271 }
272 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400273}
274
275int GUIConsole::SetRenderPos(int x, int y, int w, int h)
276{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200277 // Adjust the stub position accordingly
278 mSlideoutX += (x - mConsoleX);
279 mSlideoutY += (y - mConsoleY);
Dees_Troy51a0e822012-09-05 15:24:24 -0400280
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200281 mConsoleX = x;
282 mConsoleY = y;
283 if (w || h)
284 {
285 mConsoleW = w;
286 mConsoleH = h;
287 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400288
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200289 // Calculate the max rows
290 mMaxRows = mConsoleH / mFontHeight;
Dees_Troy51a0e822012-09-05 15:24:24 -0400291
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200292 // Adjust so we always fit to bottom
293 mStartY = mConsoleY + (mConsoleH % mFontHeight);
294 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400295}
296
297// IsInRegion - Checks if the request is handled by this object
298// Return 0 if this object handles the request, 1 if not
299int GUIConsole::IsInRegion(int x, int y)
300{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200301 if (mSlideout)
302 {
303 // Check if they tapped the slideout button
304 if (x >= mSlideoutX && x <= mSlideoutX + mSlideoutW && y >= mSlideoutY && y < mSlideoutY + mSlideoutH)
305 return 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400306
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200307 // If we're only rendering the slideout, bail now
308 if (mSlideoutState == hidden)
309 return 0;
310 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400311
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200312 return (x < mConsoleX || x > mConsoleX + mConsoleW || y < mConsoleY || y > mConsoleY + mConsoleH) ? 0 : 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400313}
314
315// NotifyTouch - Notify of a touch event
316// Return 0 on success, >0 to ignore remainder of touch, and <0 on error
317int GUIConsole::NotifyTouch(TOUCH_STATE state, int x, int y)
318{
Vojtech Bocekede51c52014-02-07 23:58:09 +0100319 if(!isConditionTrue())
320 return -1;
321
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200322 if (mSlideout && mSlideoutState == hidden)
323 {
324 if (state == TOUCH_START)
325 {
326 mSlideoutState = request_show;
327 return 1;
328 }
329 }
330 else if (mSlideout && mSlideoutState == visible)
331 {
332 // Are we sliding it back in?
333 if (state == TOUCH_START && x > mSlideoutX && x < (mSlideoutX + mSlideoutW) && y > mSlideoutY && y < (mSlideoutY + mSlideoutH))
334 {
335 mSlideoutState = request_hide;
336 return 1;
337 }
338 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400339
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200340 // If we don't have enough lines to scroll, throw this away.
Dees Troy31218ec2014-02-25 20:35:56 +0000341 if (RenderCount < mMaxRows) return 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400342
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200343 // We are scrolling!!!
344 switch (state)
345 {
346 case TOUCH_START:
347 mLastTouchX = x;
348 mLastTouchY = y;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200349 break;
Dees_Troy51a0e822012-09-05 15:24:24 -0400350
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200351 case TOUCH_DRAG:
Dees Troy31218ec2014-02-25 20:35:56 +0000352 if (x < mConsoleX || x > mConsoleX + mConsoleW || y < mConsoleY || y > mConsoleY + mConsoleH)
353 break; // touch is outside of the console area -- do nothing
354 if (y > mLastTouchY + mFontHeight) {
355 while (y > mLastTouchY + mFontHeight) {
356 if (mCurrentLine == -1)
357 mCurrentLine = RenderCount - 1;
358 else if (mCurrentLine > mMaxRows)
359 mCurrentLine--;
360 mLastTouchY += mFontHeight;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200361 }
Dees Troy31218ec2014-02-25 20:35:56 +0000362 mRender = true;
363 } else if (y < mLastTouchY - mFontHeight) {
364 while (y < mLastTouchY - mFontHeight) {
365 if (mCurrentLine >= 0)
366 mCurrentLine++;
367 mLastTouchY -= mFontHeight;
368 }
369 if (mCurrentLine >= (int) RenderCount)
370 mCurrentLine = -1;
371 mRender = true;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200372 }
373 break;
Dees_Troy51a0e822012-09-05 15:24:24 -0400374
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200375 case TOUCH_RELEASE:
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200376 mLastTouchY = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400377 case TOUCH_REPEAT:
378 case TOUCH_HOLD:
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200379 break;
380 }
381 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400382}