summaryrefslogtreecommitdiff
path: root/src/xplorer.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2024-05-04 16:51:29 -0700
committer3gg <3gg@shellblade.net>2024-05-04 16:51:29 -0700
commit8222bfe56d4dabe8d92fc4b25ea1b0163b16f3e1 (patch)
tree763389e42276035ac134d94eb1dc32293b88d807 /src/xplorer.c
Initial commit.
Diffstat (limited to 'src/xplorer.c')
-rw-r--r--src/xplorer.c245
1 files changed, 245 insertions, 0 deletions
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 @@
1#include <ui.h>
2
3#include <SDL.h>
4#include <cstring.h>
5#include <tinydir.h>
6
7#include <assert.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13static const char* WindowTitle = "XPLORER";
14static const int DefaultWidth = 960;
15static const int DefaultHeight = 600;
16
17// #define DEBUG_EVENT_LOOP 1
18
19#ifdef DEBUG_EVENT_LOOP
20#define EVENT_LOOP_PRINT printf
21#else
22#define EVENT_LOOP_PRINT(...)
23#endif // DEBUG_EVENT_LOOP
24
25typedef struct State {
26 SDL_Window* window;
27 uiFrame* frame;
28 uiTable* table;
29 string current_dir;
30} State;
31
32void SetDirectory(State* state, string path) {
33 assert(state);
34
35 state->current_dir = path;
36
37 uiTable* table = state->table;
38 assert(table);
39
40 tinydir_dir dir;
41 tinydir_open(&dir, string_data(path));
42 while (dir.has_next) {
43 tinydir_file file;
44 tinydir_readfile(&dir, &file);
45
46 const string file_size = string_format_size(file._s.st_size);
47
48 const char* row[3] = {file.name, string_data(file_size), "<date>"};
49 uiTableAddRow(table, row);
50
51 tinydir_next(&dir);
52 }
53}
54
55void CreateUi(State* state) {
56 assert(state);
57
58 uiFrame* frame = uiMakeFrame();
59
60 const char* header[] = {"Name", "Size", "Modified"};
61 uiTable* table = uiMakeTable(0, sizeof(header) / sizeof(char*), header);
62 assert(table);
63 uiWidgetSetParent(uiMakeTablePtr(table), uiMakeFramePtr(frame));
64
65 // uiLabel* label = uiMakeLabel("Hello world, what is going on!?");
66 // uiWidgetSetParent(label, frame);
67
68 state->frame = frame;
69 state->table = table;
70}
71
72static bool Render(State* state) {
73 assert(state);
74 assert(state->window);
75
76 SDL_Surface* window_surface = SDL_GetWindowSurface(state->window);
77 assert(window_surface);
78
79#ifdef DEBUG_EVENT_LOOP
80 const uiSize frame_size = uiGetFrameSize(state->frame);
81 EVENT_LOOP_PRINT(
82 "Render; surface: %dx%d; window surface; %dx%d\n", frame_size.width,
83 frame_size.height, window_surface->w, window_surface->h);
84#endif
85
86 // Locking/unlocking SDL software surfaces is not necessary.
87 //
88 // Probably also best to avoid SDL_BlitSurface(); it does pixel format
89 // conversion while blitting one pixel at a time. Instead, make the UI pixel
90 // format match the SDL window's and write to SDL's back buffer directly.
91 uiRender(
92 state->frame, &(uiSurface){
93 .width = window_surface->w,
94 .height = window_surface->h,
95 .pixels = window_surface->pixels,
96 });
97
98 if (SDL_UpdateWindowSurface(state->window) != 0) {
99 return false;
100 }
101
102 return true;
103}
104
105static bool Resize(State* state) {
106 assert(state);
107
108 // int width, height;
109 // SDL_GetWindowSize(state->window, &width, &height);
110
111 const SDL_Surface* window_surface = SDL_GetWindowSurface(state->window);
112 if (!window_surface) {
113 return false;
114 }
115 const int width = window_surface->w;
116 const int height = window_surface->h;
117
118 EVENT_LOOP_PRINT("Resize: %dx%d\n", width, height);
119
120 // TODO: Fix the white 1-pixel vertical/horizontal line that appears at odd
121 // sizes when resizing the window.
122 uiResizeFrame(state->frame, width, height);
123
124 return true;
125}
126
127bool Initialize(State* state) {
128 assert(state);
129
130 if ((state->window = SDL_CreateWindow(
131 WindowTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
132 DefaultWidth, DefaultHeight,
133 SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE)) == NULL) {
134 return false;
135 }
136
137 CreateUi(state);
138
139 const char* home = getenv("HOME");
140 SetDirectory(state, string_new(home));
141
142 return true;
143}
144
145int main(
146 __attribute__((unused)) int argc,
147 __attribute__((unused)) const char** argv) {
148 bool success = true;
149
150 State state = {0};
151
152 if (SDL_Init(SDL_INIT_VIDEO) != 0) {
153 return false;
154 }
155
156 if (!uiInit()) {
157 return false;
158 }
159
160 if (!Initialize(&state)) {
161 success = false;
162 goto cleanup;
163 }
164
165 if (!Resize(&state)) {
166 success = false;
167 goto cleanup;
168 }
169
170 // Controls whether we should keep running.
171 bool running = true;
172
173 // Controls whether a redraw is required.
174 // Initially true to perform an initial draw before the window is displayed.
175 bool redraw = true;
176
177 while (running) {
178 EVENT_LOOP_PRINT("loop\n");
179
180 // Draw if needed.
181 if (redraw && !Render(&state)) {
182 success = false;
183 break;
184 }
185 redraw = false;
186
187 // Handle events.
188 SDL_Event event = {0};
189 if (SDL_WaitEvent(&event) == 0) {
190 success = false;
191 break;
192 } else if (event.type == SDL_QUIT) {
193 break;
194 } else {
195 if (event.type == SDL_WINDOWEVENT) {
196 // When the window is maximized, an SDL_WINDOWEVENT_MOVED comes in
197 // before an SDL_WINDOWEVENT_SIZE_CHANGED with the window already
198 // resized. This is unfortunate because we cannot rely on the latter
199 // event alone to handle resizing.
200 if ((event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) ||
201 (event.window.event == SDL_WINDOWEVENT_RESIZED) ||
202 (event.window.event == SDL_WINDOWEVENT_MOVED)) {
203 if (!Resize(&state)) {
204 success = false;
205 break;
206 }
207 redraw = true;
208 }
209 } else if (event.type == SDL_KEYDOWN) {
210 if (event.key.keysym.mod & KMOD_LCTRL) {
211 switch (event.key.keysym.sym) {
212 // Exit.
213 case SDLK_c:
214 case SDLK_d:
215 running = false;
216 break;
217 default:
218 break;
219 }
220 }
221
222 } else {
223 EVENT_LOOP_PRINT("event.window.event = %d\n", event.window.event);
224 }
225 }
226 }
227
228cleanup:
229 if (!success) {
230 fprintf(stderr, "%s\n", SDL_GetError());
231 }
232
233 if (state.frame) {
234 uiDestroyFrame(&state.frame);
235 }
236 if (state.window) {
237 SDL_DestroyWindow(state.window);
238 state.window = 0;
239 }
240
241 uiShutdown();
242 SDL_Quit();
243
244 return success ? 0 : 1;
245}