summaryrefslogblamecommitdiffstats
path: root/gui/gui.cpp
blob: 87556891d9da636ea7927740713ee929f556a665 (plain) (tree)


































                                                                           

          
                        
                               







                                      
                            

                                
 









                                    
                                  
                            
                                
                      





                          
                                                
 

           
 








                                                      

 

                                                             
 

                                                              

                                      

 

             
 





                                                                             

 

                                 
 




















                                                                                  

 

               

     
































                                                                   
     


                                                                             
      
         

 

                           
 




                                                                           
                 
 

                                

                                                           
 

          
 


                                    
 
                                       
 




                                                
 

                                                                  
 





                                                                       
                     
                                                              
      




                                                                      
                     
                                                                
      





                                                                        
                     
                                                            
      






                                                            
                     
                                                              
      


                                                            
                         


                                     
 

                                        
 



                                         
                     
                                                                         
      













                                                                                 
                     
                                                                       
      












                                                                                       
                     
                                                                              
      










                                                                                              
                     
                                                     
      


















                                                                    
                                 
                         












                                                            




                                                                          

                
 





























                                                                    

 

               
 





































                                                           



                                                                 



                  

 

                               
 






































                                                           
                 





                                                             
 

                  

 

                      
 



                                           

 

                                    
 
                                                




                                           

 

                                       
 




                                           

 

                                          
 




                                           

 
                                          
 
































                                                                                       

 

              
 
         
 
             
 
                                                                
         


                                                                                                                        
         
 
                
 

             

 

                    
 



                                                                




                                                                          
                 
                                                            



                             
         
                
                                                                        



                                                              
                                                              


                                        
                                                                                    


                                          
                         
                                                                      
                         
                                                                            

                                                             


                         
                                             
                                                                            
                 
                                                                              
                         
                                                                    
                                     



                         

                                      
 

                      

      
                                              

                      

 

              
 

                       
 


                            
 

                                      
 





                                                        
         
 
                     

 

                                     
 

                       
 


                            
 
                            
                                     
 





                                                        

         
                                           
                             

 

                             
 

















                                               
                                                                  











                                                           

 

                   
 

                       
 

                           
 


                                                  
 
           
 
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <linux/input.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <errno.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 <sys/mount.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>

extern "C"
{
#include "../twcommon.h"
#include "../minuitwrp/minui.h"
#include "../minzip/Zip.h"
#include <pixelflinger/pixelflinger.h>
}

#include "rapidxml.hpp"
#include "objects.hpp"
#include "../data.hpp"
#include "../variables.h"
#include "../partitions.hpp"
#include "../twrp-functions.hpp"
#include "blanktimer.hpp"

const static int CURTAIN_FADE = 32;

using namespace rapidxml;

// Global values
static gr_surface gCurtain = NULL;
static int gGuiInitialized = 0;
static int gGuiConsoleRunning = 0;
static int gGuiConsoleTerminate = 0;
static int gForceRender = 0;
pthread_mutex_t gForceRendermutex;
static int gNoAnimation = 1;
static int gGuiInputRunning = 0;
blanktimer blankTimer;

// Needed by pages.cpp too
int gGuiRunning = 0;

static int gRecorder = -1;

extern "C" void gr_write_frame_to_file (int fd);

void
flip (void)
{
  if (gRecorder != -1)
	{
	  timespec time;
	  clock_gettime (CLOCK_MONOTONIC, &time);
	  write (gRecorder, &time, sizeof (timespec));
	  gr_write_frame_to_file (gRecorder);
	}
  gr_flip ();
  return;
}

void
rapidxml::parse_error_handler (const char *what, void *where)
{
  fprintf (stderr, "Parser error: %s\n", what);
  fprintf (stderr, "  Start of string: %s\n", (char *) where);
  LOGERR("Error parsing XML file.\n");
  //abort ();
}

static void
curtainSet ()
{
  gr_color (0, 0, 0, 255);
  gr_fill (0, 0, gr_fb_width (), gr_fb_height ());
  gr_blit (gCurtain, 0, 0, gr_get_width (gCurtain), gr_get_height (gCurtain),
		   0, 0);
  gr_flip ();
  return;
}

static void
curtainRaise (gr_surface surface)
{
  int sy = 0;
  int h = gr_get_height (gCurtain) - 1;
  int w = gr_get_width (gCurtain);
  int fy = 1;

  int msw = gr_get_width (surface);
  int msh = gr_get_height (surface);
  int CURTAIN_RATE = msh / 30;

  if (gNoAnimation == 0)
	{
	  for (; h > 0; h -= CURTAIN_RATE, sy += CURTAIN_RATE, fy += CURTAIN_RATE)
		{
		  gr_blit (surface, 0, 0, msw, msh, 0, 0);
		  gr_blit (gCurtain, 0, sy, w, h, 0, 0);
		  gr_flip ();
		}
	}
  gr_blit (surface, 0, 0, msw, msh, 0, 0);
  flip ();
  return;
}

void
curtainClose ()
{
#if 0
  int w = gr_get_width (gCurtain);
  int h = 1;
  int sy = gr_get_height (gCurtain) - 1;
  int fbh = gr_fb_height ();
  int CURTAIN_RATE = fbh / 30;

  if (gNoAnimation == 0)
	{
	  for (; h < fbh; h += CURTAIN_RATE, sy -= CURTAIN_RATE)
		{
		  gr_blit (gCurtain, 0, sy, w, h, 0, 0);
		  gr_flip ();
		}
	  gr_blit (gCurtain, 0, 0, gr_get_width (gCurtain),
			   gr_get_height (gCurtain), 0, 0);
	  gr_flip ();

	  if (gRecorder != -1)
		close (gRecorder);

	  int fade;
	  for (fade = 16; fade < 255; fade += CURTAIN_FADE)
		{
		  gr_blit (gCurtain, 0, 0, gr_get_width (gCurtain),
				   gr_get_height (gCurtain), 0, 0);
		  gr_color (0, 0, 0, fade);
		  gr_fill (0, 0, gr_fb_width (), gr_fb_height ());
		  gr_flip ();
		}
	  gr_color (0, 0, 0, 255);
	  gr_fill (0, 0, gr_fb_width (), gr_fb_height ());
	  gr_flip ();
	}
#else
  gr_blit (gCurtain, 0, 0, gr_get_width (gCurtain), gr_get_height (gCurtain),
		   0, 0);
  gr_flip ();
#endif
  return;
}

static void *
input_thread (void *cookie)
{
  int drag = 0;
  static int touch_and_hold = 0, dontwait = 0, touch_repeat = 0, x = 0, y =
	0, lshift = 0, rshift = 0, key_repeat = 0;
  static struct timeval touchStart;
  HardwareKeyboard kb;
  string seconds;

  //start screen timeout threads
  blankTimer.setTimerThread();
  DataManager::GetValue("tw_screen_timeout_secs", seconds);
  blankTimer.setTime(atoi(seconds.c_str()));

  for (;;)
	{

	  // wait for the next event
	  struct input_event ev;
	  int state = 0, ret = 0;

	  ret = ev_get (&ev, dontwait);

	  if (ret < 0)
		{
		  struct timeval curTime;
		  gettimeofday (&curTime, NULL);
		  long mtime, seconds, useconds;

		  seconds = curTime.tv_sec - touchStart.tv_sec;
		  useconds = curTime.tv_usec - touchStart.tv_usec;

		  mtime = ((seconds) * 1000 + useconds / 1000.0) + 0.5;
		  if (touch_and_hold && mtime > 500)
			{
			  touch_and_hold = 0;
			  touch_repeat = 1;
			  gettimeofday (&touchStart, NULL);
#ifdef _EVENT_LOGGING
			  LOGERR("TOUCH_HOLD: %d,%d\n", x, y);
#endif
			  PageManager::NotifyTouch (TOUCH_HOLD, x, y);
                          blankTimer.resetTimerAndUnblank();
			}
		  else if (touch_repeat && mtime > 100)
			{
#ifdef _EVENT_LOGGING
			  LOGERR("TOUCH_REPEAT: %d,%d\n", x, y);
#endif
			  gettimeofday (&touchStart, NULL);
			  PageManager::NotifyTouch (TOUCH_REPEAT, x, y);
                          blankTimer.resetTimerAndUnblank();
			}
		  else if (key_repeat == 1 && mtime > 500)
			{
#ifdef _EVENT_LOGGING
			  LOGERR("KEY_HOLD: %d,%d\n", x, y);
#endif
			  gettimeofday (&touchStart, NULL);
			  key_repeat = 2;
			  kb.KeyRepeat ();
                          blankTimer.resetTimerAndUnblank();
			}
		  else if (key_repeat == 2 && mtime > 100)
			{
#ifdef _EVENT_LOGGING
			  LOGERR("KEY_REPEAT: %d,%d\n", x, y);
#endif
			  gettimeofday (&touchStart, NULL);
			  kb.KeyRepeat ();
                          blankTimer.resetTimerAndUnblank();
			}
		}
	  else if (ev.type == EV_ABS)
		{

		  x = ev.value >> 16;
		  y = ev.value & 0xFFFF;

		  if (ev.code == 0)
			{
			  if (state == 0)
				{
#ifdef _EVENT_LOGGING
				  LOGERR("TOUCH_RELEASE: %d,%d\n", x, y);
#endif
				  PageManager::NotifyTouch (TOUCH_RELEASE, x, y);
				  blankTimer.resetTimerAndUnblank();
				  touch_and_hold = 0;
				  touch_repeat = 0;
				  if (!key_repeat)
					dontwait = 0;
				}
			  state = 0;
			  drag = 0;
			}
		  else
			{
			  if (!drag)
				{
#ifdef _EVENT_LOGGING
				  LOGERR("TOUCH_START: %d,%d\n", x, y);
#endif
				  if (PageManager::NotifyTouch (TOUCH_START, x, y) > 0)
					state = 1;
				  drag = 1;
				  touch_and_hold = 1;
				  dontwait = 1;
				  key_repeat = 0;
				  gettimeofday (&touchStart, NULL);
				  blankTimer.resetTimerAndUnblank();
				}
			  else
				{
				  if (state == 0)
					{
#ifdef _EVENT_LOGGING
					  LOGERR("TOUCH_DRAG: %d,%d\n", x, y);
#endif
					  if (PageManager::NotifyTouch (TOUCH_DRAG, x, y) > 0)
						state = 1;
					  key_repeat = 0;
					  blankTimer.resetTimerAndUnblank();
					}
				}
			}
		}
	  else if (ev.type == EV_KEY)
		{
		  // Handle key-press here
#ifdef _EVENT_LOGGING
		  LOGERR("TOUCH_KEY: %d\n", ev.code);
#endif
		  if (ev.value != 0)
			{
			  // This is a key press
			  if (kb.KeyDown (ev.code))
				{
				  key_repeat = 1;
				  touch_and_hold = 0;
				  touch_repeat = 0;
				  dontwait = 1;
				  gettimeofday (&touchStart, NULL);
				  blankTimer.resetTimerAndUnblank();
				}
			  else
				{
				  key_repeat = 0;
				  touch_and_hold = 0;
				  touch_repeat = 0;
				  dontwait = 0;
				  blankTimer.resetTimerAndUnblank();
				}
			}
		  else
			{
			  // This is a key release
			  kb.KeyUp (ev.code);
			  key_repeat = 0;
			  touch_and_hold = 0;
			  touch_repeat = 0;
			  dontwait = 0;
                          blankTimer.resetTimerAndUnblank();
			}
		}
	}
  return NULL;
}

// This special function will return immediately the first time, but then
// always returns 1/30th of a second (or immediately if called later) from
// the last time it was called
static void
loopTimer (void)
{
  static timespec lastCall;
  static int initialized = 0;

  if (!initialized)
	{
	  clock_gettime (CLOCK_MONOTONIC, &lastCall);
	  initialized = 1;
	  return;
	}

  do
	{
	  timespec curTime;
	  clock_gettime (CLOCK_MONOTONIC, &curTime);

	  timespec diff = TWFunc::timespec_diff (lastCall, curTime);

	  // This is really 30 times per second
	  if (diff.tv_sec || diff.tv_nsec > 33333333)
		{
		  lastCall = curTime;
		  return;
		}

	  // We need to sleep some period time microseconds
	  unsigned int sleepTime = 33333 - (diff.tv_nsec / 1000);
	  usleep (sleepTime);
	}
  while (1);
  return;
}

static int
runPages (void)
{
  // Raise the curtain
  if (gCurtain != NULL)
	{
	  gr_surface surface;

	  PageManager::Render ();
	  gr_get_surface (&surface);
	  curtainRaise (surface);
	  gr_free_surface (surface);
	}

  gGuiRunning = 1;

  DataManager::SetValue ("tw_loaded", 1);

  for (;;)
	{
	  loopTimer ();

	  if (!gForceRender)
		{
		  int ret;

		  ret = PageManager::Update ();
		  if (ret > 1)
			PageManager::Render ();

		  if (ret > 0)
			flip ();
		}
	  else
		{
		  pthread_mutex_lock(&gForceRendermutex);
		  gForceRender = 0;
		  pthread_mutex_unlock(&gForceRendermutex);
		  PageManager::Render ();
		  flip ();
		}
		if (DataManager::GetIntValue("tw_gui_done") != 0)
		{
			break;
		}
	}

  gGuiRunning = 0;
  return 0;
}

static int
runPage (const char *page_name)
{
  gui_changePage (page_name);

  // Raise the curtain
  if (gCurtain != NULL)
	{
	  gr_surface surface;

	  PageManager::Render ();
	  gr_get_surface (&surface);
	  curtainRaise (surface);
	  gr_free_surface (surface);
	}

  gGuiRunning = 1;

  DataManager::SetValue ("tw_loaded", 1);

  for (;;)
	{
	  loopTimer ();

	  if (!gForceRender)
		{
		  int ret;

		  ret = PageManager::Update ();
		  if (ret > 1)
			PageManager::Render ();

		  if (ret > 0)
			flip ();
		}
	  else
		{
		  pthread_mutex_lock(&gForceRendermutex);
		  gForceRender = 0;
		  pthread_mutex_unlock(&gForceRendermutex);
		  PageManager::Render ();
		  flip ();
		}
	  if (DataManager::GetIntValue ("tw_page_done") != 0)
		{
		  gui_changePage ("main");
		  break;
		}
	}

  gGuiRunning = 0;
  return 0;
}

int
gui_forceRender (void)
{
  pthread_mutex_lock(&gForceRendermutex);
  gForceRender = 1;
  pthread_mutex_unlock(&gForceRendermutex);
  return 0;
}

int
gui_changePage (std::string newPage)
{
  LOGINFO("Set page: '%s'\n", newPage.c_str ());
  PageManager::ChangePage (newPage);
  pthread_mutex_lock(&gForceRendermutex);
  gForceRender = 1;
  pthread_mutex_unlock(&gForceRendermutex);
  return 0;
}

int
gui_changeOverlay (std::string overlay)
{
  PageManager::ChangeOverlay (overlay);
  pthread_mutex_lock(&gForceRendermutex);
  gForceRender = 1;
  pthread_mutex_unlock(&gForceRendermutex);
  return 0;
}

int
gui_changePackage (std::string newPackage)
{
  PageManager::SelectPackage (newPackage);
  pthread_mutex_lock(&gForceRendermutex);
  gForceRender = 1;
  pthread_mutex_unlock(&gForceRendermutex);
  return 0;
}

std::string gui_parse_text (string inText)
{
  // Copied from std::string GUIText::parseText(void)
  // This function parses text for DataManager values encompassed by %value% in the XML
  static int counter = 0;
  std::string str = inText;
  size_t pos = 0;
  size_t next = 0, end = 0;

  while (1)
	{
	  next = str.find ('%', pos);
	  if (next == std::string::npos)
		return str;
	  end = str.find ('%', next + 1);
	  if (end == std::string::npos)
		return str;

	  // We have a block of data
	  std::string var = str.substr (next + 1, (end - next) - 1);
	  str.erase (next, (end - next) + 1);

	  if (next + 1 == end)
		{
		  str.insert (next, 1, '%');
		}
	  else
		{
		  std::string value;
		  if (DataManager::GetValue (var, value) == 0)
			str.insert (next, value);
		}

	  pos = next + 1;
	}
}

extern "C" int
gui_init ()
{
  int fd;

  gr_init ();

  if (res_create_surface ("/res/images/curtain.jpg", &gCurtain))
	{
	  printf
		("Unable to locate '/res/images/curtain.jpg'\nDid you set a DEVICE_RESOLUTION in your config files?\n");
	  return -1;
	}

  curtainSet ();

  ev_init ();
  return 0;
}

extern "C" int
gui_loadResources ()
{
//    unlink("/sdcard/video.last");
//    rename("/sdcard/video.bin", "/sdcard/video.last");
//    gRecorder = open("/sdcard/video.bin", O_CREAT | O_WRONLY);

  int check = 0;
  DataManager::GetValue (TW_IS_ENCRYPTED, check);
  if (check)
	{
	  if (PageManager::LoadPackage ("TWRP", "/res/ui.xml", "decrypt"))
		{
		  LOGERR("Failed to load base packages.\n");
		  goto error;
		}
	  else
		check = 1;
	}
  if (check == 0
	  && PageManager::LoadPackage("TWRP", "/script/ui.xml", "main"))
	{
	  std::string theme_path;

	  theme_path = DataManager::GetSettingsStoragePath ();
	  if (!PartitionManager.Mount_Settings_Storage(false))
		{
		  int retry_count = 5;
		  while (retry_count > 0
				 && !PartitionManager.Mount_Settings_Storage(false))
			{
			  usleep (500000);
			  retry_count--;
			}
		  if (!PartitionManager.Mount_Settings_Storage(false))
			{
			  LOGERR("Unable to mount %s during GUI startup.\n",
					theme_path.c_str ());
			  check = 1;
			}
		}

	  theme_path += "/TWRP/theme/ui.zip";
	  if (check || PageManager::LoadPackage("TWRP", theme_path, "main"))
		{
		  if (PageManager::LoadPackage("TWRP", "/res/ui.xml", "main"))
			{
			  LOGERR("Failed to load base packages.\n");
			  goto error;
			}
		}
	}

  // Set the default package
  PageManager::SelectPackage ("TWRP");

  gGuiInitialized = 1;
  return 0;

error:
  LOGERR("An internal error has occurred.\n");
  gGuiInitialized = 0;
  return -1;
}

extern "C" int
gui_start ()
{
  if (!gGuiInitialized)
	return -1;

  gGuiConsoleTerminate = 1;
  while (gGuiConsoleRunning)
	loopTimer ();

  // Set the default package
  PageManager::SelectPackage ("TWRP");

  if (!gGuiInputRunning)
	{
	  // Start by spinning off an input handler.
	  pthread_t t;
	  pthread_create (&t, NULL, input_thread, NULL);
	  gGuiInputRunning = 1;
	}

  return runPages ();
}

extern "C" int
gui_startPage (const char *page_name)
{
  if (!gGuiInitialized)
	return -1;

  gGuiConsoleTerminate = 1;
  while (gGuiConsoleRunning)
	loopTimer ();

  // Set the default package
  PageManager::SelectPackage("TWRP");

  if (!gGuiInputRunning)
	{
	  // Start by spinning off an input handler.
	  pthread_t t;
	  pthread_create (&t, NULL, input_thread, NULL);
	  gGuiInputRunning = 1;
	}

  DataManager::SetValue("tw_page_done", 0);
  return runPage (page_name);
}

static void *
console_thread (void *cookie)
{
  PageManager::SwitchToConsole ();

  while (!gGuiConsoleTerminate)
	{
	  loopTimer ();

	  if (!gForceRender)
		{
		  int ret;

		  ret = PageManager::Update ();
		  if (ret > 1)
			PageManager::Render ();

		  if (ret > 0)
			flip ();

		  if (ret < 0)
			LOGERR("An update request has failed.\n");
		}
	  else
		{
		  pthread_mutex_lock(&gForceRendermutex);
		  gForceRender = 0;
		  pthread_mutex_unlock(&gForceRendermutex);
		  PageManager::Render ();
		  flip ();
		}
	}
  gGuiConsoleRunning = 0;
  return NULL;
}

extern "C" int
gui_console_only ()
{
  if (!gGuiInitialized)
	return -1;

  gGuiConsoleTerminate = 0;
  gGuiConsoleRunning = 1;

  // Start by spinning off an input handler.
  pthread_t t;
  pthread_create (&t, NULL, console_thread, NULL);

  return 0;
}