#include #include #include #include #include #include #include #include #include static const char* WindowTitle = "XPLORER"; static const int DefaultWidth = 960; static const int DefaultHeight = 600; // #define DEBUG_EVENT_LOOP 1 #ifdef DEBUG_EVENT_LOOP #define EVENT_LOOP_PRINT printf #else #define EVENT_LOOP_PRINT(...) #endif // DEBUG_EVENT_LOOP typedef struct State { SDL_Window* window; uiFrame* frame; uiTable* table; string current_dir; } State; void SetDirectory(State* state, string path) { assert(state); state->current_dir = path; uiTable* table = state->table; assert(table); tinydir_dir dir; tinydir_open(&dir, string_data(path)); while (dir.has_next) { tinydir_file file; tinydir_readfile(&dir, &file); const string file_size = string_format_size(file._s.st_size); const char* row[3] = {file.name, string_data(file_size), ""}; uiTableAddRow(table, row); tinydir_next(&dir); } } void CreateUi(State* state) { assert(state); uiFrame* frame = uiMakeFrame(); const char* header[] = {"Name", "Size", "Modified"}; uiTable* table = uiMakeTable(0, sizeof(header) / sizeof(char*), header); assert(table); uiWidgetSetParent(uiMakeTablePtr(table), uiMakeFramePtr(frame)); // uiLabel* label = uiMakeLabel("Hello world, what is going on!?"); // uiWidgetSetParent(label, frame); state->frame = frame; state->table = table; } static bool Render(State* state) { assert(state); assert(state->window); SDL_Surface* window_surface = SDL_GetWindowSurface(state->window); assert(window_surface); #ifdef DEBUG_EVENT_LOOP const uiSize frame_size = uiGetFrameSize(state->frame); EVENT_LOOP_PRINT( "Render; surface: %dx%d; window surface; %dx%d\n", frame_size.width, frame_size.height, window_surface->w, window_surface->h); #endif // Locking/unlocking SDL software surfaces is not necessary. // // Probably also best to avoid SDL_BlitSurface(); it does pixel format // conversion while blitting one pixel at a time. Instead, make the UI pixel // format match the SDL window's and write to SDL's back buffer directly. uiRender( state->frame, &(uiSurface){ .width = window_surface->w, .height = window_surface->h, .pixels = window_surface->pixels, }); if (SDL_UpdateWindowSurface(state->window) != 0) { return false; } return true; } static bool Resize(State* state) { assert(state); // int width, height; // SDL_GetWindowSize(state->window, &width, &height); const SDL_Surface* window_surface = SDL_GetWindowSurface(state->window); if (!window_surface) { return false; } const int width = window_surface->w; const int height = window_surface->h; EVENT_LOOP_PRINT("Resize: %dx%d\n", width, height); // TODO: Fix the white 1-pixel vertical/horizontal line that appears at odd // sizes when resizing the window. uiResizeFrame(state->frame, width, height); return true; } bool Initialize(State* state) { assert(state); if ((state->window = SDL_CreateWindow( WindowTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DefaultWidth, DefaultHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE)) == NULL) { return false; } CreateUi(state); const char* home = getenv("HOME"); SetDirectory(state, string_new(home)); return true; } int main( __attribute__((unused)) int argc, __attribute__((unused)) const char** argv) { bool success = true; State state = {0}; if (SDL_Init(SDL_INIT_VIDEO) != 0) { return false; } if (!uiInit()) { return false; } if (!Initialize(&state)) { success = false; goto cleanup; } if (!Resize(&state)) { success = false; goto cleanup; } // Controls whether we should keep running. bool running = true; // Controls whether a redraw is required. // Initially true to perform an initial draw before the window is displayed. bool redraw = true; while (running) { EVENT_LOOP_PRINT("loop\n"); // Draw if needed. if (redraw && !Render(&state)) { success = false; break; } redraw = false; // Handle events. SDL_Event event = {0}; if (SDL_WaitEvent(&event) == 0) { success = false; break; } else if (event.type == SDL_QUIT) { break; } else { if (event.type == SDL_WINDOWEVENT) { // When the window is maximized, an SDL_WINDOWEVENT_MOVED comes in // before an SDL_WINDOWEVENT_SIZE_CHANGED with the window already // resized. This is unfortunate because we cannot rely on the latter // event alone to handle resizing. if ((event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) || (event.window.event == SDL_WINDOWEVENT_RESIZED) || (event.window.event == SDL_WINDOWEVENT_MOVED)) { if (!Resize(&state)) { success = false; break; } redraw = true; } } else if (event.type == SDL_KEYDOWN) { if (event.key.keysym.mod & KMOD_LCTRL) { switch (event.key.keysym.sym) { // Exit. case SDLK_c: case SDLK_d: running = false; break; default: break; } } } else { EVENT_LOOP_PRINT("event.window.event = %d\n", event.window.event); } } } cleanup: if (!success) { fprintf(stderr, "%s\n", SDL_GetError()); } if (state.frame) { uiDestroyFrame(&state.frame); } if (state.window) { SDL_DestroyWindow(state.window); state.window = 0; } uiShutdown(); SDL_Quit(); return success ? 0 : 1; }