diff --git a/android/app-android.cpp b/android/app-android.cpp index b986cc4e53..987f7c4b85 100644 --- a/android/app-android.cpp +++ b/android/app-android.cpp @@ -21,6 +21,7 @@ #include "input/input_state.h" #include "audio/mixer.h" #include "math/math_util.h" +#include "net/resolve.h" #include "android/native_audio.h" #define coord_xres 480 @@ -70,8 +71,8 @@ void LaunchEmail(const char *email_address) { // Remember that all of these need initialization on init! The process // may be reused when restarting the game. Globals are DANGEROUS. -float xscale = 1; -float yscale = 1; +float dp_xscale = 1; +float dp_yscale = 1; InputState input_state; @@ -104,24 +105,6 @@ extern "C" void Java_com_turboviking_libnative_NativeApp_init jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jinstallID, jboolean juseNativeAudio) { jniEnvUI = env; - pixel_xres = xxres; - pixel_yres = yyres; - - g_dpi = dpi; - - // We default to 160 dpi and all our UI code is written to assume it. (DENSITY_MEDIUM). - g_xres = xxres * 160 / dpi; - g_yres = yyres * 160 / dpi; - - use_native_audio = juseNativeAudio; - if (g_xres < g_yres) - { - // Portrait - let's force the imaginary resolution we want - g_xres = pixel_xres; - g_yres = pixel_yres; - } - xscale = (float)g_xres / pixel_xres; - yscale = (float)g_yres / pixel_yres; memset(&input_state, 0, sizeof(input_state)); renderer_inited = false; first_lost = true; @@ -141,10 +124,18 @@ extern "C" void Java_com_turboviking_libnative_NativeApp_init std::string app_nice_name; bool landscape; + net::Init(); + + g_dpi = dpi; + pixel_xres = xxres; + pixel_yres = yyres; + NativeGetAppInfo(&app_name, &app_nice_name, &landscape); + const char *argv[2] = {app_name.c_str(), 0}; NativeInit(1, argv, user_data_path.c_str(), installID.c_str()); + use_native_audio = juseNativeAudio; if (use_native_audio) { AndroidAudio_Init(&NativeMix, library_path); } @@ -164,8 +155,7 @@ extern "C" void Java_com_turboviking_libnative_NativeApp_pause(JNIEnv *, jclass) } } -extern "C" void Java_com_turboviking_libnative_NativeApp_shutdown - (JNIEnv *, jclass) { +extern "C" void Java_com_turboviking_libnative_NativeApp_shutdown(JNIEnv *, jclass) { ILOG("NativeShutdown."); if (use_native_audio) { AndroidAudio_Shutdown(); @@ -177,26 +167,35 @@ extern "C" void Java_com_turboviking_libnative_NativeApp_shutdown NativeShutdown(); ILOG("VFSShutdown."); VFSShutdown(); + net::Shutdown(); } static jmethodID postCommand; extern "C" void Java_com_turboviking_libnative_NativeRenderer_displayInit(JNIEnv * env, jobject obj) { if (!renderer_inited) { - ILOG("Calling NativeInitGraphics();"); + ILOG("Calling NativeInitGraphics(); dpi = %i", g_dpi); + + // We default to 240 dpi and all UI code is written to assume it. (DENSITY_HIGH, like Nexus S). + // Note that we don't compute dp_xscale and dp_yscale until later! This is so that NativeGetAppInfo + // can change the dp resolution if it feels like it. + dp_xres = pixel_xres * 240 / g_dpi; + dp_yres = pixel_yres * 240 / g_dpi; + NativeInitGraphics(); + + dp_xscale = (float)dp_xres / pixel_xres; + dp_yscale = (float)dp_yres / pixel_yres; } else { ILOG("Calling NativeDeviceLost();"); } renderer_inited = true; jclass cls = env->GetObjectClass(obj); - postCommand = env->GetMethodID( - cls, "postCommand", "(Ljava/lang/String;Ljava/lang/String;)V"); + postCommand = env->GetMethodID(cls, "postCommand", "(Ljava/lang/String;Ljava/lang/String;)V"); ILOG("MethodID: %i", (int)postCommand); } -extern "C" void Java_com_turboviking_libnative_NativeRenderer_displayResize - (JNIEnv *, jobject clazz, jint w, jint h) { +extern "C" void Java_com_turboviking_libnative_NativeRenderer_displayResize(JNIEnv *, jobject clazz, jint w, jint h) { ILOG("nativeResize (%i, %i), device lost!", w, h); if (first_lost) { first_lost = false; @@ -205,8 +204,7 @@ extern "C" void Java_com_turboviking_libnative_NativeRenderer_displayResize } } -extern "C" void Java_com_turboviking_libnative_NativeRenderer_displayRender - (JNIEnv *env, jobject obj) { +extern "C" void Java_com_turboviking_libnative_NativeRenderer_displayRender(JNIEnv *env, jobject obj) { if (renderer_inited) { UpdateInputState(&input_state); NativeUpdate(input_state); @@ -252,15 +250,20 @@ extern "C" void JNICALL Java_com_turboviking_libnative_NativeApp_touch ELOG("Too many pointers: %i", pointerId); return; // We ignore 8+ pointers entirely. } - - input_state.mouse_x[pointerId] = (int)(x * xscale); - input_state.mouse_y[pointerId] = (int)(y * yscale); + float scaledX = (int)(x * dp_xscale); // why the (int) cast? + float scaledY = (int)(y * dp_yscale); + input_state.mouse_x[pointerId] = scaledX; + input_state.mouse_y[pointerId] = scaledY; if (code == 1) { input_state.mouse_last[pointerId] = input_state.mouse_down[pointerId]; input_state.mouse_down[pointerId] = true; + NativeTouch(pointerId, scaledX, scaledY, 0, TOUCH_DOWN); } else if (code == 2) { input_state.mouse_last[pointerId] = input_state.mouse_down[pointerId]; input_state.mouse_down[pointerId] = false; + NativeTouch(pointerId, scaledX, scaledY, 0, TOUCH_UP); + } else { + NativeTouch(pointerId, scaledX, scaledY, 0, TOUCH_MOVE); } input_state.mouse_valid = true; } diff --git a/base/NativeApp.h b/base/NativeApp.h index efb24a460b..e6c2dee743 100644 --- a/base/NativeApp.h +++ b/base/NativeApp.h @@ -13,6 +13,8 @@ struct InputState; // You must implement this. The first function to get called, just write strings to the two pointers. // This might get called multiple times in some implementations, you must be able to handle that. +// The detected DP dimensions of the screen are set as dp_xres and dp_yres and you're free to change +// them if you have a fixed-size app that needs to stretch a little to fit. void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, bool *landscape); // For the back button to work right, this should return true on your main or title screen. @@ -28,13 +30,25 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co void NativeInitGraphics(); // Signals that you need to recreate all buffered OpenGL resources, -// like textures, vbo etc. Main thread. +// like textures, vbo etc. Also, if you have modified dp_xres and dp_yres, you have to +// do it again here. Main thread. void NativeDeviceLost(); // Called ~sixty times a second, delivers the current input state. // Main thread. void NativeUpdate(const InputState &input); +// Delivers touch events "instantly", without waiting for the next frame so that NativeUpdate can deliver. +// Useful for triggering audio events, saving a few ms. +// If you don't care about touch latency, just do a no-op implementation of this. +// time is not yet implemented. finger can be from 0 to 7, inclusive. +enum TouchEvent { + TOUCH_DOWN, + TOUCH_MOVE, + TOUCH_UP, +}; +void NativeTouch(int finger, float x, float y, double time, TouchEvent event); + // Called when it's time to render. If the device can keep up, this // will also be called sixty times per second. Main thread. void NativeRender(); diff --git a/base/PCMain.cpp b/base/PCMain.cpp index 72d126fabc..4510d008e2 100644 --- a/base/PCMain.cpp +++ b/base/PCMain.cpp @@ -119,35 +119,34 @@ extern void mixaudio(void *userdata, Uint8 *stream, int len) { #undef main #endif int main(int argc, char *argv[]) { - /* // Xoom resolution. Other common tablet resolutions: 1024x600 , 1366x768 - g_xres = 1280; - g_yres = 800; + /* // Xoom/Nexus 7 resolution. Other common tablet resolutions: 1024x600 , 1366x768 + dp_xres = 1280; + dp_yres = 800; + */ std::string app_name; std::string app_name_nice; - bool landscape; - NativeGetAppInfo(&app_name, &app_name_nice, &landscape); float zoom = 1.0f; const char *zoomenv = getenv("ZOOM"); if (zoomenv) { zoom = atof(zoomenv); } - if (landscape) { + + bool landscape; + NativeGetAppInfo(&app_name, &app_name_nice, &landscape); + + if (landscape) { pixel_xres = 800 * zoom; pixel_yres = 480 * zoom; } else { - // PC development hack - pixel_xres = 1580 * zoom; - pixel_yres = 1000 * zoom; + // PC development hack for more space + //pixel_xres = 1580 * zoom; + //pixel_yres = 1000 * zoom; + pixel_xres = 480 * zoom; + pixel_yres = 800 * zoom; } - float density = 1.0f; - g_xres = (float)pixel_xres * density / zoom; - g_yres = (float)pixel_yres * density / zoom; - - printf("Pixels: %i x %i\n", pixel_xres, pixel_yres); - printf("Virtual pixels: %i x %i\n", g_xres, g_yres); net::Init(); @@ -185,27 +184,35 @@ int main(int argc, char *argv[]) { return 1; } - - #ifdef _MSC_VER // VFSRegister("temp/", new DirectoryAssetReader("E:\\Temp\\")); TCHAR path[MAX_PATH]; SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path); PathAppend(path, (app_name + "\\").c_str()); #else - // Mac - what about linux? Also, ugly hardcoding. + // Mac / Linux const char *path = getenv("HOME"); if (!path) { struct passwd* pwd = getpwuid(getuid()); if (pwd) path = pwd->pw_dir; } - #endif NativeInit(argc, (const char **)argv, path, "BADCOFFEE"); + + float density = 1.0f; + dp_xres = (float)pixel_xres * density / zoom; + dp_yres = (float)pixel_yres * density / zoom; + NativeInitGraphics(); + float dp_xscale = (float)dp_xres / pixel_xres; + float dp_yscale = (float)dp_yres / pixel_yres; + + printf("Pixels: %i x %i\n", pixel_xres, pixel_yres); + printf("Virtual pixels: %i x %i\n", dp_xres, dp_yres); + SDL_AudioSpec fmt; fmt.freq = 44100; fmt.format = AUDIO_S16; @@ -225,44 +232,51 @@ int main(int argc, char *argv[]) { InputState input_state; int framecount = 0; bool nextFrameMD = 0; + float t = 0; while (true) { SDL_Event event; input_state.accelerometer_valid = false; input_state.mouse_valid = true; - int done = 0; + int quitRequested = 0; // input_state.mouse_down[1] = nextFrameMD; while (SDL_PollEvent(&event)) { + float mx = event.motion.x * dp_xscale; + float my = event.motion.y * dp_yscale; + if (event.type == SDL_QUIT) { - done = 1; + quitRequested = 1; } else if (event.type == SDL_KEYDOWN) { if (event.key.keysym.sym == SDLK_ESCAPE) { - done = 1; + quitRequested = 1; } } else if (event.type == SDL_MOUSEMOTION) { - input_state.mouse_x[0] = event.motion.x * density / zoom; - input_state.mouse_y[0] = event.motion.y * density / zoom; + input_state.mouse_x[0] = mx; + input_state.mouse_y[0] = my; + NativeTouch(0, mx, my, 0, TOUCH_MOVE); } else if (event.type == SDL_MOUSEBUTTONDOWN) { if (event.button.button == SDL_BUTTON_LEFT) { //input_state.mouse_buttons_down = 1; input_state.mouse_down[0] = true; nextFrameMD = true; + NativeTouch(0, mx, my, 0, TOUCH_DOWN); } } else if (event.type == SDL_MOUSEBUTTONUP) { if (event.button.button == SDL_BUTTON_LEFT) { input_state.mouse_down[0] = false; nextFrameMD = false; //input_state.mouse_buttons_up = 1; + NativeTouch(0, mx, my, 0, TOUCH_UP); } } } - if (done) + if (quitRequested) break; input_state.mouse_last[0] = input_state.mouse_down[0]; - uint8 *keys = (uint8 *)SDL_GetKeyState(NULL); + const uint8 *keys = (const uint8 *)SDL_GetKeyState(NULL); if (keys[SDLK_ESCAPE]) break; SimulateGamepad(keys, &input_state); @@ -270,14 +284,13 @@ int main(int argc, char *argv[]) { NativeUpdate(input_state); NativeRender(); if (framecount % 60 == 0) { - // glsl_refresh(); // auto-reloads modified GLSL shaders once per second. + // glsl_refresh(); // auto-reloads modified GLSL shaders once per second. } SDL_GL_SwapBuffers(); // Simple framerate limiting - static float t=0; - while (time_now() < t+1.0f/60.0f) { + while (time_now() < t + 1.0f/60.0f) { sleep_ms(0); time_update(); } @@ -293,5 +306,6 @@ int main(int argc, char *argv[]) { NativeShutdown(); SDL_CloseAudio(); SDL_Quit(); + net::Shutdown(); return 0; } diff --git a/base/display.cpp b/base/display.cpp index 1ba39cb992..0955677943 100644 --- a/base/display.cpp +++ b/base/display.cpp @@ -1,7 +1,7 @@ #include "base/display.h" -int g_xres; -int g_yres; +int dp_xres; +int dp_yres; int pixel_xres; int pixel_yres; diff --git a/base/display.h b/base/display.h index ca561e7c63..dc6344cbab 100644 --- a/base/display.h +++ b/base/display.h @@ -3,8 +3,8 @@ // This is meant to be a framework for handling DPI scaling etc. // For now, it just consists of these two ugly globals. -extern int g_xres; -extern int g_yres; +extern int dp_xres; +extern int dp_yres; extern int pixel_xres; extern int pixel_yres; diff --git a/gfx_es2/draw_buffer.cpp b/gfx_es2/draw_buffer.cpp index b4b3694c6f..38ac6f958e 100644 --- a/gfx_es2/draw_buffer.cpp +++ b/gfx_es2/draw_buffer.cpp @@ -359,7 +359,7 @@ void DrawBuffer::EnableBlend(bool enable) { void DrawBuffer::SetClipRect(float x, float y, float w, float h) { // Sigh, OpenGL is upside down. - glScissor(x, g_yres - y, w, h); + glScissor(x, dp_yres - y, w, h); glEnable(GL_SCISSOR_TEST); } diff --git a/net/http_client.cpp b/net/http_client.cpp index 90217e7be7..ba0b1f9c19 100644 --- a/net/http_client.cpp +++ b/net/http_client.cpp @@ -46,6 +46,7 @@ bool Connection::Resolve(const char *host, int port) { CHECK_GE(tmpres, 0); // << "inet_pton failed"; CHECK_NE(0, tmpres); // << ip << " not a valid IP address"; remote_.sin_port = htons(port); + free((void *)ip); return true; } @@ -53,7 +54,6 @@ void Connection::Connect() { CHECK_GE(port_, 0); sock_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); CHECK_GE(sock_, 0); - //VLOG(1) << "Connecting to " << host_ << ":" << port_; // poll once per second.. should find a way to do this blocking. int retval = -1; diff --git a/net/resolve.cpp b/net/resolve.cpp index 0dcd6870c8..9d56f38e54 100644 --- a/net/resolve.cpp +++ b/net/resolve.cpp @@ -16,7 +16,6 @@ namespace net { - void Init() { #ifdef _WIN32 @@ -25,6 +24,13 @@ void Init() #endif } +void Shutdown() +{ +#ifdef _WIN32 + WSACleanup(); +#endif +} + char *DNSResolve(const char *host) { struct hostent *hent; diff --git a/net/resolve.h b/net/resolve.h index 70e8b3e6cc..3f6d3d2f87 100644 --- a/net/resolve.h +++ b/net/resolve.h @@ -3,8 +3,9 @@ namespace net { -// Required on Win32 +// Strictly only required on Win32, but all platforms should call it. void Init(); +void Shutdown(); // use free() to free the returned string. char *DNSResolve(const char *host);