Files
2016-02-24 21:36:25 +02:00

932 lines
29 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/sockios.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/vfs.h>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include <SDL/SDL_screenkeyboard.h>
#include <SDL/SDL_android.h>
#include <android/log.h>
#include "gfx.h"
static TTF_Font* sFont;
static int unpackFinished = 0;
enum { UPGRADE_WARNING_NONE, UPGRADE_WARNING_ASK, UPGRADE_WARNING_PROCEED, UPGRADE_WARNING_CANCEL };
// Mutex here would be nice, but I'm lazy and don't care
static int upgradeWarning = UPGRADE_WARNING_NONE;
static char unpackLog[4][256];
static int freeSpaceRequiredMb = 0;
static void renderString(const char *c, int x, int y);
static void renderStringColor(const char *c, int x, int y, int r, int g, int b, SDL_Surface * surf);
static void renderStringScaled(const char *c, int size, int x, int y, int r, int g, int b, SDL_Surface * surf);
static void * unpackFilesThread(void * unused);
static void showErrorMessage(const char *msg);
static int unpackFiles(const char *archive, const char *script, const char *deleteOldDataMarkerFile, const char *pathsToDelete)
{
int unpackProgressMb;
int unpackProgressMbTotal = 1;
char fname[PATH_MAX*3];
char fname2[PATH_MAX*2];
char buf[1024 * 4];
struct stat st;
const char *tarExtractCommand = "tar xz -C";
char tarxz[PATH_MAX];
if( stat( archive, &st ) == 0 )
{
unpackProgressMbTotal = st.st_size / 1024 / 1024;
if( unpackProgressMbTotal <= 0 )
unpackProgressMbTotal = 1;
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Unpacking data: total size %d Mb", unpackProgressMbTotal);
}
else
{
if (strstr(archive, ".tar.gz") == archive + strlen(archive) - strlen(".tar.gz"))
{
strcpy(tarxz, archive);
strstr(tarxz, ".tar.gz")[5] = 'x'; // .tar.gz -> .tar.xz
if( stat( tarxz, &st ) == 0 )
{
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Found .tar.xz archive: %s", tarxz);
archive = tarxz;
tarExtractCommand = "tar xJ -C";
unpackProgressMbTotal = st.st_size / 1024 / 1024;
if( unpackProgressMbTotal <= 0 )
unpackProgressMbTotal = 1;
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Unpacking data: total size %d Mb", unpackProgressMbTotal);
}
else
return 1;
}
}
unpackProgressMb = 0;
strcpy( fname2, getenv("SECURE_STORAGE_DIR") );
strcat( fname2, "/" );
strcat( fname2, deleteOldDataMarkerFile );
if( strlen(deleteOldDataMarkerFile) > 0 && stat( fname2, &st ) == 0 )
{
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Upgrade detected, showing warning dialog");
upgradeWarning = UPGRADE_WARNING_ASK;
while( upgradeWarning == UPGRADE_WARNING_ASK )
SDL_Delay(200);
if( upgradeWarning == UPGRADE_WARNING_CANCEL )
return 1;
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Deleting old installation...");
sprintf(unpackLog[0], "Deleting old installation...");
strcpy( fname, getenv("SECURE_STORAGE_DIR") );
strcat( fname, "/busybox" );
strcat( fname, " sh -c 'cd " );
strcat( fname, getenv("SECURE_STORAGE_DIR") );
strcat( fname, " ; rm -rf " );
strcat( fname, pathsToDelete );
strcat( fname, "'" );
__android_log_print(ANDROID_LOG_INFO, "XSDL", "%s", fname);
system( fname );
}
for (;;)
{
struct statfs freeSpace;
memset(&freeSpace, 0, sizeof(freeSpace));
if( statfs(getenv("SECURE_STORAGE_DIR"), &freeSpace) == 0 )
{
if( (uint64_t)freeSpace.f_bsize * (uint64_t)freeSpace.f_bavail < (uint64_t)freeSpaceRequiredMb * 1024 * 1024 )
{
sprintf(unpackLog[0], "Error: not enough free space on internal storage");
sprintf(unpackLog[1], "Available %llu Mb, required %d Mb", (uint64_t)freeSpace.f_bsize * freeSpace.f_bavail / 1024 / 1024, freeSpaceRequiredMb);
sprintf(unpackLog[2], "Uninstall large apps to free more space on internal storage");
sleep(1);
continue;
}
}
sprintf(unpackLog[1], " ");
sprintf(unpackLog[2], " ");
break;
}
sprintf(unpackLog[0], "Unpacking data: %s", archive);
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Unpacking data: %s", archive);
strcpy( fname, getenv("SECURE_STORAGE_DIR") );
strcat( fname, "/busybox" );
strcat( fname, " " );
strcat( fname, tarExtractCommand );
strcat( fname, " " );
strcat( fname, getenv("SECURE_STORAGE_DIR") );
FILE * fo = popen(fname, "w");
FILE * ff = fopen(archive, "rb");
if( !ff || !fo )
{
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Error extracting data");
sprintf(unpackLog[0], "Error extracting data");
return 0;
}
__android_log_print(ANDROID_LOG_INFO, "XSDL", "POPEN OK");
int unpackProgressKb = 0;
for(;;)
{
//__android_log_print(ANDROID_LOG_INFO, "XSDL", "FREAD %d", unpackProgressKb);
int cnt = fread( buf, 1, sizeof(buf), ff );
//__android_log_print(ANDROID_LOG_INFO, "XSDL", "FREAD %d READ %d", unpackProgressKb, cnt);
if( cnt < 0 )
{
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Error extracting data");
sprintf(unpackLog[0], "Error extracting data");
return 1;
}
fwrite( buf, 1, cnt, fo );
if( cnt < sizeof(buf) )
break;
unpackProgressKb += 4;
if( unpackProgressKb >= 1024 )
{
unpackProgressKb = 0;
unpackProgressMb++;
sprintf(unpackLog[0], "Unpacking data: %d/%d Mb, %d%%", unpackProgressMb, unpackProgressMbTotal, unpackProgressMb * 100 / (unpackProgressMbTotal > 0 ? unpackProgressMbTotal : 1));
}
}
__android_log_print(ANDROID_LOG_INFO, "XSDL", "FREAD %d Mb DONE", unpackProgressMb);
fclose(ff);
if( pclose(fo) != 0 ) // Returns error on Android 2.3 emulator!
{
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Error extracting data - pclose() returned error, ignoring that error");
//return 0;
}
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Extracting data finished");
remove(archive);
if( strlen(script) == 0 )
{
__android_log_print(ANDROID_LOG_INFO, "XSDL", "No postinstall script");
return 1;
}
sprintf(unpackLog[0], "Running postinstall script: %s", script);
strcpy( fname, getenv("SECURE_STORAGE_DIR") );
strcat( fname, "/" );
strcat( fname, script );
if( stat( fname, &st ) != 0 )
{
strcpy( fname2, getenv("UNSECURE_STORAGE_DIR") );
strcat( fname2, "/" );
strcat( fname2, script );
if( stat( fname2, &st ) != 0 )
{
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Cannot find postinstall script");
return 1;
}
else
{
strcpy( fname2, "cat " );
strcat( fname2, getenv("UNSECURE_STORAGE_DIR") );
strcat( fname2, "/" );
strcat( fname2, script );
strcat( fname2, " > " );
strcat( fname2, fname );
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Copying postinstall script from SD card: %s", fname2);
system( fname2 );
}
}
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Setting executable permissions on postinstall script");
strcpy( fname2, "chmod 755 " );
strcat( fname2, fname );
system( fname2 );
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Running postinstall scipt: %s", script);
fo = popen(fname, "r");
if( !fo )
{
__android_log_print(ANDROID_LOG_INFO, "XSDL", "ERROR: Cannot launch postinstall script");
return 0;
}
for(;;)
{
if( !fgets(buf, sizeof(buf), fo) )
break;
if( strchr(buf, '\n') != NULL )
strchr(buf, '\n')[0] = 0;
__android_log_print(ANDROID_LOG_INFO, "XSDL", "> %s", buf);
strncpy(unpackLog[3], unpackLog[2], sizeof(unpackLog[0]) - 4);
strncpy(unpackLog[2], unpackLog[1], sizeof(unpackLog[0]) - 4);
strncpy(unpackLog[1], unpackLog[0], sizeof(unpackLog[0]) - 4);
strncpy(unpackLog[0], buf, sizeof(unpackLog[0]) - 4);
}
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Postinstall script exited with status %d", pclose(fo));
sprintf(unpackLog[0], "Running postinstall script finished");
return 1;
}
static void * unpackFilesThread(void * unused)
{
const char *unpack[][4] =
{
{ "data.tar.gz", "postinstall.sh", "usr/lib/xorg/protocol.txt", "img img-* postinstall.sh update*.sh" },
{ "xfonts.tar.gz", "", "", "" },
{ "update1.tar.gz", "update1.sh", "", "" },
{ "update2.tar.gz", "update2.sh", "", "" },
{ "update3.tar.gz", "update3.sh", "", "" },
{ "update4.tar.gz", "update4.sh", "", "" },
{ "update5.tar.gz", "update5.sh", "", "" },
{ "update6.tar.gz", "update6.sh", "", "" },
{ "update7.tar.gz", "update7.sh", "", "" },
{ "update8.tar.gz", "update8.sh", "", "" },
{ "update9.tar.gz", "update9.sh", "", "" },
{NULL, NULL, NULL, NULL}
};
int i;
for( i = 0; unpack[i][0] != NULL; i++ )
{
int status = unpackFiles(unpack[i][0], unpack[i][1], unpack[i][2], unpack[i][3]);
if( status == 0 && i == 0 ) // Only the first archive is mandatory
{
unpackFinished = 1;
return (void *)0;
}
}
unpackFinished = 1;
return (void *)1;
}
void XSDL_unpackFiles(int _freeSpaceRequiredMb)
{
pthread_t thread_id;
void * status;
memset(unpackLog, 0, sizeof(unpackLog));
freeSpaceRequiredMb = _freeSpaceRequiredMb;
pthread_create(&thread_id, NULL, &unpackFilesThread, NULL);
int progress = 0;
enum {PROGRESS_WHEEL_NUM = 14};
const char *progressWheel[PROGRESS_WHEEL_NUM] =
{
"[<=>=======]",
"[=<=>======]",
"[==<=>=====]",
"[===<=>====]",
"[====<=>===]",
"[=====<=>==]",
"[======<=>=]",
"[=======<=>]",
"[======<=>=]",
"[=====<=>==]",
"[====<=>===]",
"[===<=>====]",
"[==<=>=====]",
"[=<=>======]",
};
/*
const char *progressWheel[PROGRESS_WHEEL_NUM] =
{
"°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´",
"´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚",
"˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´",
"´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°",
"°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤",
"¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø",
"ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,",
",ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸",
"¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳",
"˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸",
"¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,",
",¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø",
"ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤",
"¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°¤ø,¸˳¸,ø¤°´˚´°",
};
*/
SDL_Joystick * j0 = SDL_JoystickOpen(0);
while (!unpackFinished)
{
SDL_Delay(300);
SDL_FillRect(SDL_GetVideoSurface(), NULL, 0);
renderString(unpackLog[0], VID_X/2, VID_Y*2/8);
renderString(unpackLog[1], VID_X/2, VID_Y*3/8);
renderString(unpackLog[2], VID_X/2, VID_Y*4/8);
renderString(unpackLog[3], VID_X/2, VID_Y*5/8);
progress++;
renderString(progressWheel[progress % PROGRESS_WHEEL_NUM], VID_X/2, VID_Y*6/8);
renderString("You may put this app to background while it's unpacking", VID_X/2, VID_Y*7/8);
SDL_Flip(SDL_GetVideoSurface());
int x = 0, y = 0;
while( upgradeWarning == UPGRADE_WARNING_ASK )
{
SDL_Delay(100);
SDL_FillRect(SDL_GetVideoSurface(), NULL, 0);
char s[PATH_MAX];
sprintf(s, "New update available for %s", getenv("ANDROID_APP_NAME"));
renderString(s, VID_X/2, VID_Y*2/8);
sprintf(s, "Please move all your %s files to SD card", getenv("ANDROID_APP_NAME"));
renderString(s, VID_X/2, VID_Y*3/8);
renderString("or they will be deleted during upgrade", VID_X/2, VID_Y*4/8);
renderString("――――――――――――――――――――", VID_X/4, VID_Y*11/16);
renderString("| |", VID_X/4, VID_Y*45/64);
renderString("| |", VID_X/4, VID_Y*48/64);
renderString("| |", VID_X/4, VID_Y*101/128);
renderString("――――――――――――――――――――", VID_X/4, VID_Y*13/16);
renderString("Install now", VID_X/4, VID_Y*6/8);
renderString("――――――――――――――――――――", VID_X*3/4, VID_Y*11/16);
renderString("| |", VID_X*3/4, VID_Y*45/64);
renderString("| |", VID_X*3/4, VID_Y*48/64);
renderString("| |", VID_X*3/4, VID_Y*101/128);
renderString("――――――――――――――――――――", VID_X*3/4, VID_Y*13/16);
renderString("Install later", VID_X*3/4, VID_Y*6/8);
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_HELP)
upgradeWarning = UPGRADE_WARNING_CANCEL;
break;
case SDL_MOUSEBUTTONUP:
if( y > VID_Y*5/8 && y < VID_Y*7/8 )
upgradeWarning = (x > VID_X/2) ? UPGRADE_WARNING_CANCEL : UPGRADE_WARNING_PROCEED;
break;
case SDL_JOYBALLMOTION:
x = event.jball.xrel;
y = event.jball.yrel;
break;
}
}
SDL_Flip(SDL_GetVideoSurface());
}
}
pthread_join(thread_id, &status);
if( status == 0 )
{
showErrorMessage("Cannot unpack data files, please reinstall the app");
exit(1);
}
SDL_JoystickClose(j0);
}
void XSDL_showConfigMenu(int * resolutionW, int * displayW, int * resolutionH, int * displayH, int * builtinKeyboard, int * ctrlAltShiftKeys)
{
int x = 0, y = 0, i, ii;
SDL_Event event;
int res = -1, dpi = -1;
int customX = 1000, customY = 1000;
enum { MODE_CUSTOM = 11 };
char native[32] = "0x0", native56[32], native46[32], native36[32], native26[32];
char custom[32] = "1000x1000";
int vertical = SDL_ListModes(NULL, 0)[0]->w < SDL_ListModes(NULL, 0)[0]->h;
char cfgpath[PATH_MAX];
FILE * cfgfile;
int okay = 0;
if( vertical )
{
x = *resolutionW;
*resolutionW = *resolutionH;
*resolutionH = x;
x = *displayW;
*displayW = *displayH;
*displayH = x;
}
const char * resStr[] =
{
native, native56, native46, native36,
native26, "1280x1024", "1280x960", "1280x720",
"1024x768", "800x600", "800x480", custom
};
const int resVal[][2] = {
{*resolutionW, *resolutionH},
{(*resolutionW * 5 / 6) & ~0x3, (*resolutionH * 5 / 6) & ~0x3},
{(*resolutionW * 4 / 6) & ~0x3, (*resolutionH * 4 / 6) & ~0x3},
{(*resolutionW * 3 / 6) & ~0x3, (*resolutionH * 3 / 6) & ~0x3},
{(*resolutionW * 2 / 6) & ~0x3, (*resolutionH * 2 / 6) & ~0x3},
{1280,1024}, {1280,960}, {1280,720},
{1024,768}, {800,600}, {800,480}, {customX,customY}
};
const char * fontsStr[] = {
"X2.5", "X2", "X1.7", "X1.5",
"X1.3", "X1", "X0.9", "X0.8",
"X0.7", "X0.6", "X0.5", "X0.4",
"X0.3", "X0.2", "X0.15", "X0.1"
};
const float fontsVal[] = {
2.5f, 2.0f, 1.7f, 1.5f,
1.3f, 1.0f, 0.9f, 0.8f,
0.7f, 0.6f, 0.5f, 0.4f,
0.3f, 0.2f, 0.15f, 0.1f
};
sprintf(native, "%dx%d", resVal[0][0], resVal[0][1]);
sprintf(native56, "%dx%d", resVal[1][0], resVal[1][1]);
sprintf(native46, "%dx%d", resVal[2][0], resVal[2][1]);
sprintf(native36, "%dx%d", resVal[3][0], resVal[3][1]);
sprintf(native26, "%dx%d", resVal[4][0], resVal[4][1]);
int savedRes = 0;
int savedDpi = 8;
sprintf(cfgpath, "%s/.xsdl.cfg", getenv("SECURE_STORAGE_DIR"));
cfgfile = fopen(cfgpath, "r");
if( cfgfile )
{
fscanf(cfgfile, "%d %d %d %d %d %d", &savedRes, &savedDpi, &customX, &customY, builtinKeyboard, ctrlAltShiftKeys);
fclose(cfgfile);
}
sprintf(custom, "%dx%d", customX, customY);
int counter = 3000, config = 0;
Uint32 curtime = SDL_GetTicks();
while ( counter > 0 && !config )
{
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_HELP)
counter = 0;
break;
case SDL_MOUSEBUTTONUP:
config = 1;
break;
}
}
SDL_FillRect(SDL_GetVideoSurface(), NULL, 0);
y = VID_Y/4;
renderString("Tap the screen to change", vertical ? VID_Y / 2 : VID_X/2, y);
y += 30;
renderString("display resolution and font scale (DPI)", vertical ? VID_Y / 2 : VID_X/2, y);
char buf[100];
y += 30;
sprintf(buf, "Resolution: %s", resStr[savedRes]);
renderString(buf, vertical ? VID_Y / 2 : VID_X/2, y);
y += 30;
sprintf(buf, "Font scale: %s", fontsStr[savedDpi]);
renderString(buf, vertical ? VID_Y / 2 : VID_X/2, y);
y += 30;
sprintf(buf, "Keyboard: %s", *builtinKeyboard == 0 ? "System" : *builtinKeyboard == 1 ? "Builtin QWERTY" : "System + Builtin");
renderString(buf, vertical ? VID_Y / 2 : VID_X/2, y);
y += 40;
sprintf(buf, "Starting in %d seconds", counter / 1000 + 1);
renderString(buf, vertical ? VID_Y / 2 : VID_X/2, y);
SDL_Delay(100);
SDL_Flip(SDL_GetVideoSurface());
counter -= SDL_GetTicks() - curtime;
curtime = SDL_GetTicks();
}
if( !config )
{
res = savedRes;
dpi = savedDpi;
}
SDL_Joystick * j0 = SDL_JoystickOpen(0);
while ( res < 0 )
{
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_HELP)
return;
break;
case SDL_MOUSEBUTTONUP:
{
//SDL_GetMouseState(&x, &y);
if( vertical )
{
int z = x;
x = y;
y = z;
}
i = (y / (VID_Y/3));
ii = (x / (VID_X/4));
res = i * 4 + ii;
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Screen coords %d %d res %d\n", x, y, res);
}
break;
case SDL_JOYBALLMOTION:
x = event.jball.xrel;
y = event.jball.yrel;
break;
}
}
//__android_log_print(ANDROID_LOG_INFO, "XSDL", "Screen coords %d %d\n", x, y, res);
SDL_FillRect(SDL_GetVideoSurface(), NULL, 0);
renderString("Select display resolution", vertical ? VID_Y / 2 : VID_X/2, VID_Y/3);
for(i = 0; i < 3; i++)
for(ii = 0; ii < 4; ii++)
{
if( vertical )
renderString(resStr[i*4+ii], VID_Y/6 + (i*VID_Y/3), VID_X/8 + (ii*VID_X/4));
else
renderString(resStr[i*4+ii], VID_X/8 + (ii*VID_X/4), VID_Y/6 + (i*VID_Y/3));
if( i == 0 && ii == 0 && !vertical )
renderString("native", VID_X/8, VID_Y/6 - VID_Y/12);
if( i == 2 && ii == 3 && !vertical )
renderString("custom", VID_X/8 + (ii*VID_X/4), VID_Y/6 - VID_Y/12 + (i*VID_Y/3));
}
//SDL_GetMouseState(&x, &y);
//renderString("X", x, y);
SDL_Delay(100);
SDL_Flip(SDL_GetVideoSurface());
if (res == MODE_CUSTOM)
{
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Selected custom display resolution");
SDL_ANDROID_ToggleScreenKeyboardWithoutTextInput();
customX = 0;
customY = 0;
custom[0] = 0;
while (customX == 0 || customY == 0)
{
while (SDL_PollEvent(&event))
{
if (event.type == SDL_KEYDOWN)
{
switch (event.key.keysym.sym)
{
case SDLK_HELP:
return;
case SDLK_RETURN:
if (customX == 0)
customX = atoi(custom);
else
customY = atoi(custom);
custom[0] = 0;
break;
case SDLK_BACKSPACE:
if (strlen(custom) > 0)
custom[strlen(custom) - 1] = 0;
break;
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
custom[strlen(custom) + 1] = 0;
custom[strlen(custom)] = event.key.keysym.sym;
break;
default:
break;
}
}
}
SDL_FillRect(SDL_GetVideoSurface(), NULL, 0);
if (customX == 0)
renderString("Enter width:", VID_X/8, VID_Y/6);
else
renderString("Enter height:", VID_X/8, VID_Y/6);
renderString("Press Enter when done", VID_X*3/4, VID_Y/6);
renderString(custom, VID_X/8 + VID_X/4, VID_Y/6);
SDL_Delay(100);
SDL_Flip(SDL_GetVideoSurface());
}
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Selected custom display resolution: %s = %d %d", custom, customX, customY);
}
}
*resolutionW = resVal[res][0];
*resolutionH = resVal[res][1];
if (res == MODE_CUSTOM)
{
*resolutionW = customX;
*resolutionH = customY;
}
while ( dpi < 0 )
{
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_HELP)
return;
break;
case SDL_MOUSEBUTTONUP:
{
//SDL_GetMouseState(&x, &y);
if( vertical )
{
int z = x;
x = y;
y = z;
}
i = (y / (VID_Y/4));
ii = (x / (VID_X/4));
dpi = i * 4 + ii;
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Screen coords %d %d dpi %d\n", x, y, res);
}
break;
case SDL_JOYBALLMOTION:
x = event.jball.xrel;
y = event.jball.yrel;
break;
}
}
SDL_FillRect(SDL_GetVideoSurface(), NULL, 0);
renderString("Select font scale (DPI)", vertical ? VID_Y / 2 : VID_X/2, VID_Y/2);
for(i = 0; i < 4; i++)
for(ii = 0; ii < 4; ii++)
{
int scale = (float)(*displayH) * (float)(*resolutionH) / 3500.0f * fontsVal[i*4+ii];
if( vertical )
renderStringScaled(fontsStr[i*4+ii], scale < VID_Y/12 ? scale : VID_Y/12, VID_Y/8 + (i*VID_Y/4), VID_X/8 + (ii*VID_X/4), 255, 255, 255, SDL_GetVideoSurface());
else
renderStringScaled(fontsStr[i*4+ii], scale, VID_X/8 + (ii*VID_X/4), VID_Y/8 + (i*VID_Y/4), 255, 255, 255, SDL_GetVideoSurface());
}
//SDL_GetMouseState(&x, &y);
//renderString("X", x, y);
SDL_Delay(100);
SDL_Flip(SDL_GetVideoSurface());
}
*displayW = *displayW / fontsVal[dpi];
*displayH = *displayH / fontsVal[dpi];
okay = !config;
while ( !okay )
{
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_HELP)
return;
break;
case SDL_MOUSEBUTTONUP:
{
//SDL_GetMouseState(&x, &y);
if( vertical )
{
int z = x;
x = y;
y = z;
}
if( y > 0 && y < VID_Y * 2 / 6 )
*builtinKeyboard = (*builtinKeyboard + 1) % 3;
if( y > VID_Y * 2 / 6 && y < VID_Y * 4 / 6 )
*ctrlAltShiftKeys = !*ctrlAltShiftKeys;
if( y > VID_Y * 4 / 6 && y < VID_Y * 6 / 6 )
okay = 1;
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Screen coords %d %d dpi %d\n", x, y, res);
}
break;
case SDL_JOYBALLMOTION:
x = event.jball.xrel;
y = event.jball.yrel;
break;
}
}
SDL_FillRect(SDL_GetVideoSurface(), NULL, 0);
char buf[100];
sprintf(buf, "Keyboard: %s", *builtinKeyboard == 0 ? "System" : *builtinKeyboard == 1 ? "Builtin QWERTY" : "System + Builtin");
renderString(buf, VID_X/2, VID_Y * 1 / 6);
sprintf(buf, "Separate Ctrl/Alt/Shift keys: %s", *ctrlAltShiftKeys == 0 ? "No" : "Yes");
renderString(buf, VID_X/2, VID_Y * 3 / 6);
sprintf(buf, "Okay");
renderString(buf, VID_X/2, VID_Y * 5 / 6);
SDL_Delay(100);
SDL_Flip(SDL_GetVideoSurface());
}
SDL_JoystickClose(j0);
if( config )
{
cfgfile = fopen(cfgpath, "w");
if( cfgfile )
{
fprintf(cfgfile, "%d %d %d %d %d %d\n", res, dpi, customX, customY, *builtinKeyboard, *ctrlAltShiftKeys);
fclose(cfgfile);
}
}
}
void XSDL_generateBackground(const char * port, int showHelp, int resolutionW, int resolutionH)
{
int sd, addr, ifc_num, i;
struct ifconf ifc;
struct ifreq ifr[20];
SDL_Surface * surf;
int y = resolutionH * 1 / 6;
char msg[128];
if (resolutionH > resolutionW)
resolutionH = resolutionW;
if( !showHelp )
{
surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 16, 16, 24, 0x0000ff, 0x00ff00, 0xff0000, 0);
SDL_FillRect(surf, NULL, 0x00002f);
SDL_SaveBMP(surf, "background.bmp");
SDL_FreeSurface(surf);
return;
}
surf = SDL_CreateRGBSurface(SDL_SWSURFACE, resolutionW, resolutionH, 24, 0x0000ff, 0x00ff00, 0xff0000, 0);
SDL_FillRect(surf, NULL, 0x00002f);
renderStringScaled("Launch these commands on your Linux PC:", 12 * resolutionH / VID_Y, resolutionW/2, y, 255, 255, 255, surf);
y += resolutionH * 30 / VID_Y;
sd = socket(PF_INET, SOCK_DGRAM, 0);
if (sd > 0)
{
ifc.ifc_len = sizeof(ifr);
ifc.ifc_ifcu.ifcu_buf = (caddr_t)ifr;
if (ioctl(sd, SIOCGIFCONF, &ifc) == 0)
{
ifc_num = ifc.ifc_len / sizeof(struct ifreq);
__android_log_print(ANDROID_LOG_INFO, "XSDL", "%d network interfaces found", ifc_num);
for (i = 0; i < ifc_num; ++i)
{
int addr = 0;
char saddr[32];
if (ifr[i].ifr_addr.sa_family != AF_INET)
continue;
if (ioctl(sd, SIOCGIFADDR, &ifr[i]) == 0)
addr = ((struct sockaddr_in *)(&ifr[i].ifr_addr))->sin_addr.s_addr;
if (addr == 0)
continue;
sprintf (saddr, "%d.%d.%d.%d", (addr & 0xFF), (addr >> 8 & 0xFF), (addr >> 16 & 0xFF), (addr >> 24 & 0xFF));
__android_log_print(ANDROID_LOG_INFO, "XSDL", "interface: %s address: %s\n", ifr[i].ifr_name, saddr);
if (strcmp(saddr, "127.0.0.1") == 0)
continue;
sprintf (msg, "export DISPLAY=%s%s", saddr, port);
renderStringScaled(msg, 12 * resolutionH / VID_Y, resolutionW/2, y, 255, 255, 255, surf);
y += resolutionH * 15 / VID_Y;
sprintf (msg, "export PULSE_SERVER=tcp:%s:4712", saddr);
renderStringScaled(msg, 12 * resolutionH / VID_Y, resolutionW/2, y, 255, 255, 255, surf);
y += resolutionH * 15 / VID_Y;
sprintf (msg, "metacity & gimp");
renderStringScaled(msg, 12 * resolutionH / VID_Y, resolutionW/2, y, 255, 255, 255, surf);
y += resolutionH * 20 / VID_Y;
}
}
close(sd);
}
y += resolutionH * 10 / VID_Y;
sprintf (msg, "To tunnel X over SSH, forward port %d", atoi(port+1) + 6000);
renderStringScaled(msg, 12 * resolutionH / VID_Y, resolutionW/2, y, 255, 255, 255, surf);
y += resolutionH * 15 / VID_Y;
sprintf (msg, "in your SSH client");
renderStringScaled(msg, 12 * resolutionH / VID_Y, resolutionW/2, y, 255, 255, 255, surf);
y += resolutionH * 20 / VID_Y;
sprintf (msg, "If you run Linux in chroot on this device, run:");
renderStringScaled(msg, 12 * resolutionH / VID_Y, resolutionW/2, y, 255, 255, 255, surf);
y += resolutionH * 15 / VID_Y;
sprintf (msg, "export DISPLAY=:0 PULSE_SERVER=tcp:127.0.0.1:4712");
renderStringScaled(msg, 12 * resolutionH / VID_Y, resolutionW/2, y, 255, 255, 255, surf);
SDL_SaveBMP(surf, "background.bmp");
SDL_FreeSurface(surf);
}
void XSDL_showServerLaunchErrorMessage()
{
showErrorMessage( "Error: X server failed to launch.\n\n"
"This may happen because of SELinux,\n"
"or because installation was corrupted.\n"
"Either way, this app will not work, which is sad.");
}
void showErrorMessage(const char *msg)
{
SDL_Event event;
const char * s;
int y = VID_Y/3;
SDL_FillRect(SDL_GetVideoSurface(), NULL, 0);
for( s = msg; s && s[0]; s = strchr(s, '\n'), s += (s ? 1 : 0), y += 30 )
{
const char * s1 = strchr(s, '\n');
int len = s1 ? s1 - s : strlen(s);
char buf[512];
strncpy(buf, s, len);
buf[len] = 0;
if( len > 0 )
renderString(buf, VID_X/2, y);
}
SDL_Flip(SDL_GetVideoSurface());
while (1)
{
while (SDL_WaitEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_HELP)
return;
break;
}
}
}
}
void XSDL_initSDL()
{
char fontpath[PATH_MAX];
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Current video mode: %d %d", SDL_ListModes(NULL, 0)[0]->w, SDL_ListModes(NULL, 0)[0]->h);
if( SDL_ListModes(NULL, 0)[0]->w > SDL_ListModes(NULL, 0)[0]->h )
SDL_SetVideoMode(VID_X, VID_Y, 0, SDL_SWSURFACE);
else
SDL_SetVideoMode(VID_Y, VID_X, 0, SDL_SWSURFACE);
TTF_Init();
strcpy( fontpath, getenv("UNSECURE_STORAGE_DIR") );
strcat( fontpath, "/DroidSansMono.ttf" );
sFont = TTF_OpenFont(fontpath, 14);
if (!sFont)
{
__android_log_print(ANDROID_LOG_INFO, "XSDL", "Error: cannot open font file, please reinstall the app");
exit(1);
}
}
void XSDL_deinitSDL()
{
TTF_CloseFont(sFont);
sFont = NULL;
TTF_Quit();
// Do NOT call SDL_Quit(), it crashes!
}
void renderStringColor(const char *c, int x, int y, int r, int g, int b, SDL_Surface * surf)
{
if (!c || !c[0])
return;
SDL_Color fColor = {r, g, b};
SDL_Rect fontRect = {0, 0, 0, 0};
SDL_Surface* fontSurface = TTF_RenderUTF8_Solid(sFont, c, fColor);
fontRect.w = fontSurface->w;
fontRect.h = fontSurface->h;
fontRect.x = x - fontRect.w / 2;
fontRect.y = y - fontRect.h / 2;
SDL_BlitSurface(fontSurface, NULL, surf, &fontRect);
SDL_FreeSurface(fontSurface);
}
void renderString(const char *c, int x, int y)
{
renderStringColor(c, x, y, 255, 255, 255, SDL_GetVideoSurface());
}
void renderStringScaled(const char *c, int size, int x, int y, int r, int g, int b, SDL_Surface * surf)
{
if (!c || !c[0])
return;
SDL_Color fColor = {r, g, b};
SDL_Rect fontRect = {0, 0, 0, 0};
TTF_Font* font = TTF_OpenFont("DroidSansMono.ttf", size);
SDL_Surface* fontSurface = TTF_RenderUTF8_Solid(font, c, fColor);
TTF_CloseFont(font);
fontRect.w = fontSurface->w;
fontRect.h = fontSurface->h;
fontRect.x = x - fontRect.w / 2;
fontRect.y = y - fontRect.h / 2;
SDL_BlitSurface(fontSurface, NULL, surf, &fontRect);
SDL_FreeSurface(fontSurface);
}