From 8222bfe56d4dabe8d92fc4b25ea1b0163b16f3e1 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 4 May 2024 16:51:29 -0700 Subject: Initial commit. --- src/xplorer.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 src/xplorer.c (limited to 'src/xplorer.c') diff --git a/src/xplorer.c b/src/xplorer.c new file mode 100644 index 0000000..8a190af --- /dev/null +++ b/src/xplorer.c @@ -0,0 +1,245 @@ +#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; +} -- cgit v1.2.3