/* TE4 - T-Engine 4 Copyright (C) 2009, 2010, 2011, 2012 Nicolas Casalini This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Nicolas Casalini "DarkGod" darkgod@te4.org */ #include "display.h" #include <stdio.h> #include <stdlib.h> #include <time.h> #include <math.h> #include <sys/time.h> #include "lua.h" #include "lauxlib.h" #include "lualib.h" #include "luasocket.h" #include "luasocket/mime.h" #include "SFMT.h" #include "types.h" #include "script.h" #include "physfs.h" #include "physfsrwops.h" #include "core_lua.h" #include "getself.h" #include "music.h" #include "serial.h" #include "profile.h" #include "main.h" #include "lua_externs.h" #include "runner/core.h" #ifdef SELFEXE_WINDOWS #include <windows.h> #endif #define WIDTH 800 #define HEIGHT 600 #define DEFAULT_IDLE_FPS (2) int start_xpos = -1, start_ypos = -1; int g_argc = 0; char **g_argv; SDL_Window *window = NULL; SDL_GLContext maincontext; /* Our opengl context handle */ bool is_fullscreen = FALSE; lua_State *L = NULL; int nb_cpus; bool no_debug = FALSE; int current_mousehandler = LUA_NOREF; int current_keyhandler = LUA_NOREF; int current_game = LUA_NOREF; core_boot_type *core_def = NULL; bool exit_engine = FALSE; bool no_sound = FALSE; bool isActive = TRUE; bool tickPaused = FALSE; int mouse_cursor_ox, mouse_cursor_oy; int mouse_drag_w = 32, mouse_drag_h = 32; int mouse_drag_tex = 0, mouse_drag_tex_ref = LUA_NOREF; int mousex = 0, mousey = 0; float gamma_correction = 1; /* The currently requested fps for the program */ int requested_fps = 30; /* The requested fps for when the program is idle (i.e., doesn't have focus) */ int requested_fps_idle = DEFAULT_IDLE_FPS; /* The currently "saved" fps, used for idle transitions. */ int requested_fps_idle_saved = 0; SDL_TimerID display_timer_id = 0; SDL_TimerID realtime_timer_id = 0; /* OpenGL capabilities */ GLint max_texture_size = 1024; extern bool shaders_active; bool fbo_active; bool multitexture_active; /* Error handling */ lua_err_type *last_lua_error_head = NULL, *last_lua_error_tail = NULL; /* * Locks for thread safety with respect to the rendering and realtime timers. * The locks are used to control access to each timer's respective id and flag. */ SDL_mutex *renderingLock; SDL_mutex *realtimeLock; int redraw_pending = 0; int realtime_pending = 0; /* * Used to clean up a lock and its corresponding timer/flag. * * @param lock * The lock which is used by the timer and its event handler. * * @param timer * The id of the timer to clean up. * * @param timerFlag * The flag variable that timer and its events use. * */ static void cleanupTimerLock(SDL_mutex *lock, SDL_TimerID *timer, int *timerFlag); /* * Handles transitions to and from idle mode. * * A transition is only performed if the game already has a running render timer * and there is an actual idle->normal or normal->idle transition. * * @param goIdle * Return to normal game rendering speed if zero, go idle otherwise. */ static void handleIdleTransition(int goIdle); void del_lua_error() { lua_err_type *cur = last_lua_error_head; while (cur) { if (cur->err_msg) free(cur->err_msg); if (cur->file) free(cur->file); if (cur->func) free(cur->func); lua_err_type *ocur = cur; cur = cur->next; free(ocur); } last_lua_error_head = NULL; last_lua_error_tail = NULL; } static void new_lua_error(const char *err) { del_lua_error(); lua_err_type *cur = calloc(1, sizeof(lua_err_type)); cur->err_msg = strdup(err); cur->next = NULL; last_lua_error_head = cur; last_lua_error_tail = cur; } static void add_lua_error(const char *file, int line, const char *func) { lua_err_type *cur = calloc(1, sizeof(lua_err_type)); cur->err_msg = NULL; cur->file = strdup(file); cur->line = line; cur->func = strdup(func); cur->next = NULL; last_lua_error_tail->next = cur; last_lua_error_tail = cur; } static int traceback (lua_State *L) { lua_Debug ar; int n = 0; printf("Lua Error: %s\n", lua_tostring(L, 1)); while(lua_getstack(L, n++, &ar)) { lua_getinfo(L, "nSl", &ar); printf("\tAt %s:%d %s\n", ar.short_src, ar.currentline, ar.name?ar.name:""); } // Do it again for the lua error popup, if needed if (1) { n = 0; new_lua_error(lua_tostring(L, 1)); while(lua_getstack(L, n++, &ar)) { lua_getinfo(L, "nSl", &ar); add_lua_error(ar.short_src, ar.currentline, ar.name?ar.name:""); } } return 1; } void stackDump (lua_State *L) { int i=lua_gettop(L); printf(" ---------------- Stack Dump ----------------\n" ); while( i ) { int t = lua_type(L, i); switch (t) { case LUA_TSTRING: printf("%d:`%s'\n", i, lua_tostring(L, i)); break; case LUA_TBOOLEAN: printf("%d: %s\n",i,lua_toboolean(L, i) ? "true" : "false"); break; case LUA_TNUMBER: printf("%d: %g\n", i, lua_tonumber(L, i)); break; default: #if defined(__PTRDIFF_TYPE__) if((sizeof(__PTRDIFF_TYPE__) == sizeof(long int))) { printf("%d: %s // %lx\n", i, lua_typename(L, t), (unsigned long int)lua_topointer(L, i)); } else { printf("%d: %s // %x\n", i, lua_typename(L, t), (unsigned int)lua_topointer(L, i)); } #else printf("%d: %s // %x\n", i, lua_typename(L, t), lua_topointer(L, i)); #endif break; } i--; } printf("--------------- Stack Dump Finished ---------------\n" ); } int docall (lua_State *L, int narg, int nret) { #if 1 int status; int base = lua_gettop(L) - narg; /* function index */ // printf("<===%d (%d)\n", base, narg); lua_pushcfunction(L, traceback); /* push traceback function */ lua_insert(L, base); /* put it under chunk and args */ status = lua_pcall(L, narg, nret, base); lua_remove(L, base); /* remove traceback function */ /* force a complete garbage collection in case of errors */ if (status != 0) { lua_pop(L, 1); lua_gc(L, LUA_GCCOLLECT, 0); } // printf(">===%d (%d) [%d]\n", lua_gettop(L), nret, status); if (lua_gettop(L) != nret + (base - 1)) { stackDump(L); // assert(0); lua_settop(L, base); } return status; #else int status=0; int base = lua_gettop(L) - narg; /* function index */ lua_call(L, narg, nret); return status; #endif } /* No print function, does .. nothing */ int noprint(lua_State *L) { return 0; } // define our data that is passed to our redraw function typedef struct { Uint32 color; } MainStateData; int event_filter(void *userdata, SDL_Event* event) { // Do not allow the user to close without asking the game to know about it if (event->type == SDL_QUIT && (current_game != LUA_NOREF)) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); lua_pushstring(L, "onQuit"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); docall(L, 1, 0); return 0; } return 1; } extern SDL_Cursor *mouse_cursor; extern SDL_Cursor *mouse_cursor_down; void on_event(SDL_Event *event) { switch (event->type) { case SDL_TEXTINPUT: if (current_keyhandler != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_keyhandler); lua_pushstring(L, "receiveKey"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_keyhandler); lua_pushnumber(L, 0); SDL_Keymod _pKeyState = SDL_GetModState(); lua_pushboolean(L, (_pKeyState & KMOD_CTRL) ? TRUE : FALSE); lua_pushboolean(L, (_pKeyState & KMOD_SHIFT) ? TRUE : FALSE); lua_pushboolean(L, (_pKeyState & KMOD_ALT) ? TRUE : FALSE); lua_pushboolean(L, (_pKeyState & KMOD_GUI) ? TRUE : FALSE); lua_pushstring(L, event->text.text); lua_pushboolean(L, FALSE); lua_pushnil(L); docall(L, 9, 0); } break; case SDL_KEYDOWN: case SDL_KEYUP: if (current_keyhandler != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_keyhandler); lua_pushstring(L, "receiveKey"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_keyhandler); lua_pushnumber(L, event->key.keysym.scancode); SDL_Keymod _pKeyState = SDL_GetModState(); lua_pushboolean(L, (_pKeyState & KMOD_CTRL) ? TRUE : FALSE); lua_pushboolean(L, (_pKeyState & KMOD_SHIFT) ? TRUE : FALSE); lua_pushboolean(L, (_pKeyState & KMOD_ALT) ? TRUE : FALSE); lua_pushboolean(L, (_pKeyState & KMOD_GUI) ? TRUE : FALSE); lua_pushnil(L); lua_pushboolean(L, (event->type == SDL_KEYUP) ? TRUE : FALSE); /* Convert unicode UCS-2 to UTF8 string */ if (event->key.keysym.sym) { wchar_t wc = event->key.keysym.sym; char buf[4] = {0,0,0,0}; if (wc < 0x80) { buf[0] = wc; } else if (wc < 0x800) { buf[0] = (0xC0 | wc>>6); buf[1] = (0x80 | wc & 0x3F); } else { buf[0] = (0xE0 | wc>>12); buf[1] = (0x80 | wc>>6 & 0x3F); buf[2] = (0x80 | wc & 0x3F); } lua_pushstring(L, buf); } else lua_pushnil(L); docall(L, 9, 0); } break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: if (event->type == SDL_MOUSEBUTTONDOWN) SDL_SetCursor(mouse_cursor_down); else SDL_SetCursor(mouse_cursor); if (current_mousehandler != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_mousehandler); lua_pushstring(L, "receiveMouse"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_mousehandler); switch (event->button.button) { case SDL_BUTTON_LEFT: #if 1 { SDL_Keymod _pKeyState = SDL_GetModState(); if (_pKeyState & KMOD_ALT) lua_pushstring(L, "right"); else lua_pushstring(L, "left"); } #else lua_pushstring(L, "left"); #endif break; case SDL_BUTTON_MIDDLE: lua_pushstring(L, "middle"); break; case SDL_BUTTON_RIGHT: lua_pushstring(L, "right"); break; default: lua_pushstring(L, "button"); lua_pushnumber(L, event->button.button); lua_concat(L, 2); break; } lua_pushnumber(L, event->button.x); lua_pushnumber(L, event->button.y); lua_pushboolean(L, (event->type == SDL_MOUSEBUTTONUP) ? TRUE : FALSE); docall(L, 5, 0); } break; case SDL_MOUSEWHEEL: if (current_mousehandler != LUA_NOREF) { int x = 0, y = 0; SDL_GetMouseState(&x, &y); int i; for (i = 0; i <= 1; i++) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_mousehandler); lua_pushstring(L, "receiveMouse"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_mousehandler); if (event->wheel.y > 0) lua_pushstring(L, "wheelup"); else if (event->wheel.y < 0) lua_pushstring(L, "wheeldown"); else if (event->wheel.x > 0) lua_pushstring(L, "wheelleft"); else if (event->wheel.x < 0) lua_pushstring(L, "wheelright"); else lua_pushstring(L, "wheelnone"); lua_pushnumber(L, x); lua_pushnumber(L, y); lua_pushboolean(L, i); docall(L, 5, 0); } } break; case SDL_MOUSEMOTION: mousex = event->motion.x; mousey = event->motion.y; if (current_mousehandler != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_mousehandler); lua_pushstring(L, "receiveMouseMotion"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_mousehandler); if (event->motion.state & SDL_BUTTON(1)) lua_pushstring(L, "left"); else if (event->motion.state & SDL_BUTTON(2)) lua_pushstring(L, "middle"); else if (event->motion.state & SDL_BUTTON(3)) lua_pushstring(L, "right"); else if (event->motion.state & SDL_BUTTON(4)) lua_pushstring(L, "wheelup"); else if (event->motion.state & SDL_BUTTON(5)) lua_pushstring(L, "wheeldown"); else lua_pushstring(L, "none"); lua_pushnumber(L, event->motion.x); lua_pushnumber(L, event->motion.y); lua_pushnumber(L, event->motion.xrel); lua_pushnumber(L, event->motion.yrel); docall(L, 6, 0); } break; } } // redraw the screen and update game logics, if any void on_tick() { static int Frames = 0; static int T0 = 0; if (current_game != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); lua_pushstring(L, "tick"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); docall(L, 1, 1); tickPaused = lua_toboolean(L, -1); lua_pop(L, 1); } /* Gather our frames per second */ Frames++; { int t = SDL_GetTicks(); if (t - T0 >= 10000) { float seconds = (t - T0) / 1000.0; float fps = Frames / seconds; // printf("%d ticks in %g seconds = %g TPS\n", Frames, seconds, fps); T0 = t; Frames = 0; } } } void call_draw(int nb_keyframes) { if (draw_waiting(L)) return; if (nb_keyframes > 30) nb_keyframes = 30; // Notify the particles threads that there are new keyframes thread_particle_new_keyframes(nb_keyframes); if (current_game != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); lua_pushstring(L, "display"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); lua_pushnumber(L, (nb_keyframes < 0) ? 0 : nb_keyframes); docall(L, 2, 0); } /* Mouse pointer */ if (mouse_drag_tex) { GLfloat texcoords[2*4] = { 0, 0, 0, 1, 1, 1, 1, 0, }; GLfloat colors[4*4] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; glTexCoordPointer(2, GL_FLOAT, 0, texcoords); glColorPointer(4, GL_FLOAT, 0, colors); int x = mousex; int y = mousey; int w = mouse_drag_w / 2; int h = mouse_drag_h / 2; tglBindTexture(GL_TEXTURE_2D, mouse_drag_tex); GLfloat vertices[2*4] = { x - w, y - h, x - w, y + h, x + w, y + h, x + w, y - h, }; glVertexPointer(2, GL_FLOAT, 0, vertices); glDrawArrays(GL_QUADS, 0, 4); } } long total_keyframes = 0; void on_redraw() { static int Frames = 0; static int T0 = 0; static float nb_keyframes = 0; static int last_keyframe = 0; static float reference_fps = 30; static int count_keyframes = 0; /* Gather our frames per second */ Frames++; if (!is_waiting()) { int t = SDL_GetTicks(); if (t - T0 >= 1000) { float seconds = (t - T0) / 1000.0; float fps = Frames / seconds; reference_fps = fps; // printf("%d frames in %g seconds = %g FPS (%d keyframes)\n", Frames, seconds, fps, count_keyframes); T0 = t; Frames = 0; last_keyframe = 0; nb_keyframes = 0; count_keyframes = 0; } } else { // If we are waiting we ignore the fact that we are losing time, this way we never try to "catch up" later T0 = SDL_GetTicks(); Frames = 0; last_keyframe = 0; nb_keyframes = 0; count_keyframes = 0; } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); float step = 30 / reference_fps; nb_keyframes += step; int nb = ceilf(nb_keyframes); count_keyframes += nb - last_keyframe; total_keyframes += nb - last_keyframe; // printf("keyframes: %f / %f by %f => %d\n", nb_keyframes, reference_fps, step, nb - (last_keyframe)); call_draw(nb - last_keyframe); //SDL_GL_SwapBuffers(); SDL_GL_SwapWindow(window); last_keyframe = nb; } void pass_command_args(int argc, char *argv[]) { int i; if (current_game != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); lua_pushstring(L, "commandLineArgs"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); lua_newtable(L); for (i = 1; i <= argc; i++) { lua_pushnumber(L, i); lua_pushstring(L, argv[i]); lua_settable(L, -3); } docall(L, 2, 0); } } Uint32 redraw_timer(Uint32 interval, void *param) { SDL_Event event; SDL_UserEvent userevent; /* In this example, our callback pushes an SDL_USEREVENT event into the queue, and causes ourself to be called again at the same interval: */ userevent.type = SDL_USEREVENT; userevent.code = 0; userevent.data1 = NULL; userevent.data2 = NULL; event.type = SDL_USEREVENT; event.user = userevent; // Grab the rendering lock and see if a redraw should be requested. SDL_mutexP(renderingLock); // If there is no redraw pending, request one. Otherwise, ignore. if (!redraw_pending && isActive) { SDL_PushEvent(&event); redraw_pending = 1; } SDL_mutexV(renderingLock); return(interval); } Uint32 realtime_timer(Uint32 interval, void *param) { SDL_Event event; SDL_UserEvent userevent; /* In this example, our callback pushes an SDL_USEREVENT event into the queue, and causes ourself to be called again at the same interval: */ userevent.type = SDL_USEREVENT; userevent.code = 2; userevent.data1 = NULL; userevent.data2 = NULL; event.type = SDL_USEREVENT; event.user = userevent; // Grab the realtime lock and see if a tick should be requested. SDL_mutexP(realtimeLock); // If there is no realtime tick pending, request one. Otherwise, ignore. if (!realtime_pending && isActive) { SDL_PushEvent(&event); realtime_pending = 1; } SDL_mutexV(realtimeLock); return(interval); } // Calls the lua music callback void on_music_stop() { if (current_game != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); lua_pushstring(L, "onMusicStop"); lua_gettable(L, -2); lua_remove(L, -2); if (lua_isfunction(L, -1)) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); docall(L, 1, 0); } else lua_pop(L, 1); } } // Setup realtime void setupRealtime(float freq) { SDL_mutexP(realtimeLock); if (!freq) { if (realtime_timer_id) SDL_RemoveTimer(realtime_timer_id); realtime_timer_id = 0; printf("[ENGINE] Switching to turn based\n"); } else { float interval = 1000 / freq; realtime_timer_id = SDL_AddTimer((int)interval, realtime_timer, NULL); printf("[ENGINE] Switching to realtime, interval %d ms\n", (int)interval); } SDL_mutexV(realtimeLock); } void setupDisplayTimer(int fps) { SDL_mutexP(renderingLock); if (display_timer_id) SDL_RemoveTimer(display_timer_id); requested_fps = fps; display_timer_id = SDL_AddTimer(1000 / fps, redraw_timer, NULL); printf("[ENGINE] Setting requested FPS to %d (%d ms)\n", fps, 1000 / fps); SDL_mutexV(renderingLock); } /* general OpenGL initialization function */ int initGL() { /* Set the background black */ tglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); /* Depth buffer setup */ glClearDepth( 1.0f ); /* The Type Of Depth Test To Do */ glDepthFunc(GL_LEQUAL); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); return( TRUE ); } int resizeWindow(int width, int height) { /* Height / width ration */ GLfloat ratio; SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); initGL(); /* Protect against a divide by zero */ if ( height == 0 ) height = 1; ratio = ( GLfloat )width / ( GLfloat )height; // tglActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); /* Setup our viewport. */ glViewport( 0, 0, ( GLsizei )width, ( GLsizei )height ); /* change to the projection matrix and set our viewing volume. */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); /* Set our perspective */ //gluPerspective( 45.0f, ratio, 0.1f, 100.0f ); glOrtho(0, width, height, 0, -1001, 1001); /* Make sure we're chaning the model view and not the projection */ glMatrixMode( GL_MODELVIEW ); /* Reset The View */ glLoadIdentity( ); //TSDL2 SDL_SetGamma(gamma_correction, gamma_correction, gamma_correction); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); printf("OpenGL max texture size: %d\n", max_texture_size); return( TRUE ); } void do_resize(int w, int h, bool fullscreen) { /* Temporary width, height (since SDL might reject our resize) */ int aw, ah; int mustPushEvent = 0; SDL_Event fsEvent; printf("[DO RESIZE] Requested: %dx%d (%d)\n", w, h, fullscreen); /* If there is no current window, we have to make one and initialize */ if (!window) { window = SDL_CreateWindow("TE4", (start_xpos == -1) ? SDL_WINDOWPOS_CENTERED : start_xpos, (start_ypos == -1) ? SDL_WINDOWPOS_CENTERED : start_ypos, w, h, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | (fullscreen ? SDL_WINDOW_FULLSCREEN : 0)); if (window==NULL) { printf("error opening screen: %s\n", SDL_GetError()); exit(1); } is_fullscreen = fullscreen; screen = SDL_GetWindowSurface(window); maincontext = SDL_GL_CreateContext(window); SDL_GL_MakeCurrent(window, maincontext); glewInit(); } else { /* SDL won't allow a fullscreen resolution change in one go. Check. */ if (is_fullscreen) { /* Drop out of fullscreen so we can change resolution. */ SDL_SetWindowFullscreen(window, SDL_FALSE); is_fullscreen = 0; mustPushEvent = 1; /* Actually just a maybe for now, confirmed later */ } /* Update window size */ SDL_SetWindowSize(window, w, h); /* Jump [back] into fullscreen if requested */ if (fullscreen) { if (!SDL_SetWindowFullscreen(window, SDL_TRUE)) { /* Fullscreen change successful */ is_fullscreen = SDL_TRUE; } else { /* Error switching fullscreen mode */ printf("[DO RESIZE] Unable to switch window" " to fullscreen mode: %s\n", SDL_GetError()); SDL_ClearError(); } } else if (mustPushEvent) { /* * Our changes will get clobbered by an automatic event from * setWindowFullscreen. Push an event to the event loop to make * sure these changes are applied after whatever that other event * throws. */ /* Create an event to push */ fsEvent.type = SDL_WINDOWEVENT; fsEvent.window.timestamp = SDL_GetTicks(); fsEvent.window.windowID = SDL_GetWindowID(window); // windowId fsEvent.window.event = SDL_WINDOWEVENT_RESIZED; fsEvent.window.data1 = w; fsEvent.window.data2 = h; /* Push the event, but don't bother waiting */ SDL_PushEvent(&fsEvent); printf("[DO RESIZE]: pushed fullscreen compensation event\n"); } /* Finally, update the screen info */ screen = SDL_GetWindowSurface(window); } /* Check and see if SDL honored our resize request */ SDL_GetWindowSize(window, &aw, &ah); printf("[DO RESIZE] Got: %dx%d (%d)\n", aw, ah, is_fullscreen); SDL_GL_MakeCurrent(window, maincontext); resizeWindow(aw, ah); } void boot_lua(int state, bool rebooting, int argc, char *argv[]) { core_def->corenum = 0; if (state == 1) { const char *selfexe; /* When rebooting we destroy the lua state to free memory and we reset physfs */ if (rebooting) { current_mousehandler = LUA_NOREF; current_keyhandler = LUA_NOREF; current_game = LUA_NOREF; lua_close(L); PHYSFS_deinit(); } /***************** Physfs Init *****************/ PHYSFS_init(argv[0]); selfexe = get_self_executable(argc, argv); if (selfexe && PHYSFS_mount(selfexe, "/", 1)) { } else { printf("NO SELFEXE: bootstrapping from CWD\n"); PHYSFS_mount("bootstrap", "/bootstrap", 1); } /***************** Lua Init *****************/ L = lua_open(); /* create state */ luaL_openlibs(L); /* open libraries */ luaopen_physfs(L); luaopen_core(L); luaopen_fov(L); luaopen_socket_core(L); luaopen_mime_core(L); luaopen_struct(L); luaopen_profiler(L); luaopen_bit(L); luaopen_lpeg(L); luaopen_lxp(L); luaopen_md5_core(L); luaopen_map(L); luaopen_particles(L); luaopen_gas(L); luaopen_sound(L); luaopen_noise(L); luaopen_diamond_square(L); luaopen_shaders(L); luaopen_serial(L); luaopen_profile(L); luaopen_zlib(L); luaopen_bit(L); luaopen_wait(L); // Override "print" if requested if (no_debug) { lua_pushcfunction(L, noprint); lua_setglobal(L, "print"); } // Make the uids repository lua_newtable(L); lua_setglobal(L, "__uids"); // Tell the boostrapping code the selfexe path if (selfexe) lua_pushstring(L, selfexe); else lua_pushnil(L); lua_setglobal(L, "__SELFEXE"); // Will be useful #ifdef __APPLE__ lua_pushboolean(L, TRUE); lua_setglobal(L, "__APPLE__"); #endif // Run bootstrapping if (!luaL_loadfile(L, "/bootstrap/boot.lua")) { docall(L, 0, 0); } // Could not load bootstrap! Try to mount the engine from working directory as last resort else { lua_pop(L, 1); printf("WARNING: No bootstrap code found, defaulting to working directory for engine code!\n"); PHYSFS_mount("game/thirdparty", "/", 1); PHYSFS_mount("game/", "/", 1); } // And run the lua engine pre init scripts if (!luaL_loadfile(L, "/loader/pre-init.lua")) docall(L, 0, 0); else lua_pop(L, 1); create_particles_thread(); } else if (state == 2) { SDL_SetWindowTitle(window, "T-Engine4"); // Now we can open lua lanes, the physfs paths are set and it can load it's lanes-keeper.lua file // luaopen_lanes(L); printf("Running lua loader code...\n"); // And run the lua engine scripts if (!luaL_loadfile(L, "/loader/init.lua")) { if (core_def->reboot_engine) lua_pushstring(L, core_def->reboot_engine); else lua_pushnil(L); if (core_def->reboot_engine_version) lua_pushstring(L, core_def->reboot_engine_version); else lua_pushnil(L); if (core_def->reboot_module) lua_pushstring(L, core_def->reboot_module); else lua_pushnil(L); if (core_def->reboot_name) lua_pushstring(L, core_def->reboot_name); else lua_pushnil(L); lua_pushboolean(L, core_def->reboot_new); if (core_def->reboot_einfo) lua_pushstring(L, core_def->reboot_einfo); else lua_pushnil(L); docall(L, 6, 0); } else { lua_pop(L, 1); } } } // Update core to run static void define_core(core_boot_type *core_def, const char *coretype, int id, const char *reboot_engine, const char *reboot_engine_version, const char *reboot_module, const char *reboot_name, int reboot_new, const char *reboot_einfo) { if (core_def->coretype) free(core_def->coretype); if (core_def->reboot_engine) free(core_def->reboot_engine); if (core_def->reboot_engine_version) free(core_def->reboot_engine_version); if (core_def->reboot_module) free(core_def->reboot_module); if (core_def->reboot_name) free(core_def->reboot_name); if (core_def->reboot_einfo) free(core_def->reboot_einfo); core_def->corenum = id; core_def->coretype = coretype ? strdup(coretype) : NULL; core_def->reboot_engine = reboot_engine ? strdup(reboot_engine) : NULL; core_def->reboot_engine_version = reboot_engine_version ? strdup(reboot_engine_version) : NULL; core_def->reboot_module = reboot_module ? strdup(reboot_module) : NULL; core_def->reboot_name = reboot_name ? strdup(reboot_name) : NULL; core_def->reboot_einfo = reboot_einfo ? strdup(reboot_einfo) : NULL; core_def->reboot_new = reboot_new; } // Let some platforms use a different entry point #ifdef USE_TENGINE_MAIN #ifdef main #undef main #endif #define main tengine_main #endif /* Cleans up a timer lock. See function declaration for more info. */ void cleanupTimerLock(SDL_mutex *lock, SDL_TimerID *timer , int *timerFlag) { // Grab the lock and start cleaning up SDL_mutexP(lock); // Cancel the timer (if it is running) if (*timer) SDL_RemoveTimer(*timer); *timer = 0; *timerFlag = -1; SDL_mutexV(lock); /* * Need to get lock once more just in case a timer call was stuck waiting on * the lock when we altered the variables. */ SDL_mutexP(lock); SDL_mutexV(lock); // Can now safely destroy the lock. SDL_DestroyMutex(lock); } /* Handles game idle transition. See function declaration for more info. */ void handleIdleTransition(int goIdle) { /* Only allow if a display timer is already running. */ if (display_timer_id) { if (goIdle) { /* Make sure this isn't an idle->idle transition */ if (requested_fps != requested_fps_idle) { requested_fps_idle_saved = requested_fps; setupDisplayTimer(requested_fps_idle); } } else if (requested_fps_idle_saved && (requested_fps != requested_fps_idle_saved)) { /* Made sure this wasn't a nonidle->nonidle */ setupDisplayTimer(requested_fps_idle_saved); } } } /** * Core entry point. */ int main(int argc, char *argv[]) { core_def = calloc(1, sizeof(core_boot_type)); core_def->define = &define_core; core_def->define(core_def, "te4core", -1, NULL, NULL, NULL, NULL, 0, NULL); #ifdef SELFEXE_WINDOWS FILE *logfile; logfile = freopen ("te4_log.txt", "w", stdout); bool windows_autoflush = FALSE; #endif g_argc = argc; g_argv = argv; // Parse arguments int i; for (i = 1; i < argc; i++) { char *arg = argv[i]; if (!strncmp(arg, "-M", 2)) core_def->reboot_module = strdup(arg+2); if (!strncmp(arg, "-u", 2)) core_def->reboot_name = strdup(arg+2); if (!strncmp(arg, "-E", 2)) core_def->reboot_einfo = strdup(arg+2); if (!strncmp(arg, "-n", 2)) core_def->reboot_new = 1; if (!strncmp(arg, "--flush-stdout", 14)) { setvbuf(stdout, (char *) NULL, _IOLBF, 0); #ifdef SELFEXE_WINDOWS setvbuf(logfile, NULL, _IONBF, 2); windows_autoflush = TRUE; #endif } if (!strncmp(arg, "--no-debug", 10)) no_debug = TRUE; if (!strncmp(arg, "--xpos", 6)) start_xpos = strtol(argv[++i], NULL, 10); if (!strncmp(arg, "--ypos", 6)) start_ypos = strtol(argv[++i], NULL, 10); } // Initialize display lock for thread safety. renderingLock = SDL_CreateMutex(); realtimeLock = SDL_CreateMutex(); // Get cpu cores nb_cpus = get_number_cpus(); printf("[CPU] Detected %d CPUs\n", nb_cpus); init_openal(); // RNG init init_gen_rand(time(NULL)); int vid_drv; for (vid_drv = 0; vid_drv < SDL_GetNumVideoDrivers(); vid_drv++) { printf("Available video driver: %s\n", SDL_GetVideoDriver(vid_drv)); } // initialize engine and set up resolution and depth Uint32 flags=SDL_INIT_TIMER; if (SDL_Init (flags) < 0) { printf("cannot initialize SDL: %s\n", SDL_GetError ()); return 1; } if (SDL_VideoInit(NULL) != 0) { printf("Error initializing SDL video: %s\n", SDL_GetError()); return 2; } // Filter events, to catch the quit event SDL_SetEventFilter(event_filter, NULL); boot_lua(1, FALSE, argc, argv); do_resize(WIDTH, HEIGHT, FALSE); if (screen==NULL) { printf("error opening screen: %s\n", SDL_GetError()); return; } SDL_SetWindowIcon(window, IMG_Load_RW(PHYSFSRWOPS_openRead("/engines/default/data/gfx/te4-icon.png"), TRUE)); SDL_SetWindowTitle(window, "T4Engine"); TTF_Init(); /* Sets up OpenGL double buffering */ resizeWindow(WIDTH, HEIGHT); // Allow screensaver to work SDL_EnableScreenSaver(); SDL_StartTextInput(); // Get OpenGL capabilities multitexture_active = GLEW_ARB_multitexture; shaders_active = GLEW_ARB_shader_objects; fbo_active = GLEW_EXT_framebuffer_object || GLEW_ARB_framebuffer_object; if (!multitexture_active) shaders_active = FALSE; if (!GLEW_VERSION_2_1) { multitexture_active = FALSE; shaders_active = FALSE; fbo_active = FALSE; } printf("===fbo %d\n", fbo_active); // setupDisplayTimer(30); init_blank_surface(); boot_lua(2, FALSE, argc, argv); pass_command_args(argc, argv); SDL_Event event; while (!exit_engine) { if (!isActive || tickPaused) SDL_WaitEvent(NULL); #ifdef SELFEXE_WINDOWS if (windows_autoflush) _commit(_fileno(stdout)); #endif /* handle the events in the queue */ while (SDL_PollEvent(&event)) { switch(event.type) { case SDL_WINDOWEVENT: switch (event.window.event) { case SDL_WINDOWEVENT_RESIZED: /* Note: SDL can't resize a fullscreen window, so don't bother! */ if (!is_fullscreen) { printf("SDL_WINDOWEVENT_RESIZED: %d x %d\n", event.window.data1, event.window.data2); do_resize(event.window.data1, event.window.data2, is_fullscreen); if (current_game != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); lua_pushstring(L, "onResolutionChange"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); docall(L, 1, 0); } } else { printf("SDL_WINDOWEVENT_RESIZED: ignored due to fullscreen\n"); } break; case SDL_WINDOWEVENT_MOVED: { int x, y; SDL_GetWindowPosition(window, &x, &y); printf("move %d x %d\n", x, y); if (current_game != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); lua_pushstring(L, "onWindowMoved"); lua_gettable(L, -2); lua_remove(L, -2); lua_rawgeti(L, LUA_REGISTRYINDEX, current_game); lua_pushnumber(L, x); lua_pushnumber(L, y); docall(L, 3, 0); } break; } case SDL_WINDOWEVENT_CLOSE: event.type = SDL_QUIT; SDL_PushEvent(&event); break; case SDL_WINDOWEVENT_SHOWN: case SDL_WINDOWEVENT_FOCUS_GAINED: SDL_SetModState(KMOD_NONE); /* break from idle */ printf("[EVENT HANDLER]: Got a SHOW/FOCUS_GAINED event, restoring full FPS.\n"); handleIdleTransition(0); break; case SDL_WINDOWEVENT_HIDDEN: case SDL_WINDOWEVENT_FOCUS_LOST: /* go idle */ SDL_SetModState(KMOD_NONE); printf("[EVENT HANDLER]: Got a HIDDEN/FOCUS_LOST event, going idle.\n"); handleIdleTransition(1); break; default: break; } break; case SDL_TEXTINPUT: case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEMOTION: case SDL_MOUSEWHEEL: case SDL_KEYDOWN: case SDL_KEYUP: /* handle key presses */ on_event(&event); tickPaused = FALSE; break; case SDL_QUIT: /* handle quit requests */ exit_engine = TRUE; break; case SDL_USEREVENT: /* TODO: Enumerate user event codes */ switch(event.user.code) { case 0: if (isActive) { on_redraw(); SDL_mutexP(renderingLock); redraw_pending = 0; SDL_mutexV(renderingLock); } break; case 1: on_music_stop(); break; case 2: if (isActive) { on_tick(); SDL_mutexP(realtimeLock); realtime_pending = 0; SDL_mutexV(realtimeLock); } break; default: break; } break; default: break; } } /* draw the scene */ // Note: since realtime_timer_id is accessed, have to lock first int doATick = 0; SDL_mutexP(realtimeLock); if (!realtime_timer_id && isActive && !tickPaused) { doATick = 1; realtime_pending = 1; } SDL_mutexV(realtimeLock); if (doATick) { on_tick(); SDL_mutexP(realtimeLock); realtime_pending = 0; SDL_mutexV(realtimeLock); } /* Reboot the lua engine */ if (core_def->corenum) { // Just reboot the lua VM if (core_def->corenum == TE4CORE_VERSION) { tickPaused = FALSE; setupRealtime(0); boot_lua(1, TRUE, argc, argv); boot_lua(2, TRUE, argc, argv); } // Clean up and tell the runner to run a different core else { lua_close(L); free_particles_thread(); free_profile_thread(); PHYSFS_deinit(); break; } } } // Clean up locks. cleanupTimerLock(renderingLock, &display_timer_id, &redraw_pending); cleanupTimerLock(realtimeLock, &realtime_timer_id, &realtime_pending); SDL_Quit(); deinit_openal(); printf("Thanks for having fun!\n"); #ifdef SELFEXE_WINDOWS fclose(stdout); #endif }