summaryrefslogblamecommitdiffstats
path: root/gui/console.cpp
blob: bb70400c91b7df9c511cd0ad835a3225feadcf32 (plain) (tree)



















                                  
                        
                               






                                         
                                              
                      
 
                                                         
 
                           





                                                                                             
                                                



                                     
                                                  
                                                       
 
                                       
                 

                               
         
 
                                                                     
                    
                                          

                                               



                                               




























                                                                                     
               

 




                                     
                                                          
 











                                                      
                        
                                
                       












                                                                                                       
                                                                




                                                  


                                                                                                










                                                                                                             
                                                                          


                                                                            

                                                                         



                         
                                         

                                                             



                                    

                                                              
 

                                                                                                     



                                   










                                                                                              
                                                       
                                      
                                     
                        


                                                          


                                                                                                                                   
                                                         
                                               
                                                     
                         
                                                                                                       

                                                                                         
                                                                    
                                                                              

                                                              
                                                                    





                                      


                                                                                        
                                         
         
                                               


            

                                                    




                                                 

                                                   
         


                                                               


                                                                                                                                      
                                                                         



                                                                                                              
                                                                                                                                            
                 

                                                  



                            


                              



                                                  



                            


                              





















                                                                         
                         


                                                       


                         



                                                        

















                                                        


                                                               
                                                         

                                        










                                                                                                                      
                                                                                                                    





                                                                         


                              


















                                                                                                                                               
                                                    






                                


                        















                                                                                                             
                         


                                                              



                           
                                 

                          


                      
 
// 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)
		{
			mFont = LoadAttrFont(child, "resource");
		}

		child = node->first_node("color");
		if (child)
		{
			mForegroundColor = LoadAttrColor(child, "foreground", mForegroundColor);
			mBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor);
			mScrollColor = LoadAttrColor(child, "scroll", 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);

			mSlideoutImage = LoadAttrImage(child, "resource");

			if (mSlideoutImage && mSlideoutImage->GetResource())
			{
				mSlideoutW = mSlideoutImage->GetWidth();
				mSlideoutH = mSlideoutImage->GetHeight();
			}
		}
	}

	mFontHeight = mFont->GetHeight();
	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
	SetRenderPos(mConsoleX, mConsoleY);
}

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
	size_t 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 (size_t i = prevCount; i < mLastCount; i++) {
		string curr_line = gConsole[i];
		string curr_color = gConsoleColor[i];
		for(;;) {
			size_t 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) // follow tail
	{
		start = RenderCount - mMaxRows;
	}
	else
	{
		if (curLine > (int) RenderCount)
			curLine = (int) RenderCount;
		if ((int) mMaxRows > curLine)
			curLine = (int) mMaxRows;
		start = curLine - mMaxRows;
	}

	// note: start can be negative here
	for (int line = 0; line < mMaxRows; line++)
	{
		int index = start + line;
		if (index >= 0 && index < (int) RenderCount) {
			if (rConsoleColor[index] == "normal") {
				gr_color(mForegroundColor.red, mForegroundColor.green, mForegroundColor.blue, mForegroundColor.alpha);
			} else {
				COLOR mFontColor;
				std::string color = rConsoleColor[index];
				ConvertStrToColor(color, &mFontColor);
				mFontColor.alpha = 255;
				gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
			}
			gr_textExW(mConsoleX, mStartY + (line * mFontHeight), rConsole[index].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 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 (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 ((int)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;
}