comparison infinite_canvas/main.c @ 276:b55c22cff335

Add interactive infinite canvas prototype
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 16:57:56 -0700
parents
children 8d560f50ed4c
comparison
equal deleted inserted replaced
274:c9be578316a6 276:b55c22cff335
1 #include "infinite_canvas/canvas.h"
2 #include "infinite_canvas/web_surface.h"
3
4 #if defined(INFINITE_CANVAS_DEV_UI)
5 #include "infinite_canvas/dev_ui.h"
6 #endif
7
8 #if defined(PLATFORM_WEB)
9 #include <emscripten/emscripten.h>
10 #endif
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 typedef struct {
17 Dowa_Arena *p_arena;
18 Canvas_Camera camera;
19 Canvas_Scene scene;
20 Canvas_Web_Surface web_surface;
21 Font font;
22 Canvas_Theme theme;
23 boolean owns_font;
24 const char *p_screenshot_path;
25 uint32 frame_count;
26 #if defined(INFINITE_CANVAS_DEV_UI)
27 Canvas_Dev_UI dev_ui;
28 #endif
29 } Canvas_App;
30
31 static Canvas_App *g_app = NULL;
32
33 static void Canvas_App_Draw_Frame(void)
34 {
35 int32 width = GetScreenWidth();
36 int32 height = GetScreenHeight();
37 Canvas_Camera_Set_Viewport(&g_app->camera, width, height);
38
39 boolean block_pointer_input = FALSE;
40 #if defined(INFINITE_CANVAS_DEV_UI)
41 block_pointer_input = Canvas_Dev_UI_Contains_Pointer(&g_app->dev_ui);
42 #endif
43 boolean web_input = Canvas_Web_Surface_Update_Input(
44 &g_app->web_surface,
45 &g_app->camera,
46 &g_app->scene);
47 block_pointer_input = block_pointer_input || web_input;
48 boolean entity_input = Canvas_Scene_Update_Interaction(
49 &g_app->scene,
50 &g_app->camera,
51 block_pointer_input);
52 boolean keyboard_focus =
53 Canvas_Scene_Is_Text_Editing(&g_app->scene) ||
54 Canvas_Web_Surface_Is_Focused(&g_app->web_surface);
55 if (!keyboard_focus && IsKeyPressed(KEY_P)) {
56 Canvas_Scene_Toggle_Selected_Pin(&g_app->scene, &g_app->camera);
57 }
58 Canvas_Update_Camera_Input(
59 &g_app->camera,
60 block_pointer_input || entity_input,
61 keyboard_focus);
62 Canvas_Scene_Sync_Pinned(&g_app->scene, &g_app->camera);
63 Canvas_Scene_Update_Notification_Stack(&g_app->scene, &g_app->camera);
64
65 Canvas_Web_Surface_Sync(&g_app->web_surface, &g_app->camera, &g_app->scene);
66
67 BeginDrawing();
68 ClearBackground(g_app->theme.background);
69 Canvas_Draw_World(
70 &g_app->camera,
71 &g_app->scene,
72 g_app->font,
73 &g_app->theme);
74 Canvas_Web_Surface_Draw(&g_app->web_surface, &g_app->camera, &g_app->scene);
75 #if defined(INFINITE_CANVAS_DEV_UI)
76 Canvas_Dev_UI_Draw(
77 &g_app->dev_ui,
78 &g_app->camera,
79 &g_app->scene,
80 g_app->font,
81 &g_app->theme);
82 #endif
83 EndDrawing();
84 g_app->frame_count++;
85 #if !defined(PLATFORM_WEB)
86 if (g_app->frame_count == 30) {
87 RestoreWindow();
88 SetWindowFocused();
89 }
90 #endif
91 if (g_app->p_screenshot_path &&
92 g_app->frame_count == 180) {
93 TakeScreenshot(g_app->p_screenshot_path);
94 }
95 }
96
97 int main(int argc, char **p_argv)
98 {
99 #if !defined(PLATFORM_WEB)
100 int32 subprocess_result = Canvas_Web_Surface_Execute_Subprocess(argc, p_argv);
101 if (subprocess_result >= 0) return subprocess_result;
102 #else
103 (void)argc;
104 (void)p_argv;
105 #endif
106
107 Dowa_Arena *p_arena = Dowa_Arena_Create(8 * ONE_MEGA_BYTE);
108 if (!p_arena) return 1;
109
110 g_app = (Canvas_App *)Dowa_Arena_Allocate(p_arena, sizeof(*g_app));
111 if (!g_app) {
112 Dowa_Arena_Free(p_arena);
113 return 1;
114 }
115 memset(g_app, 0, sizeof(*g_app));
116 g_app->p_arena = p_arena;
117 g_app->p_screenshot_path = getenv("INFINITE_CANVAS_SCREENSHOT");
118 Canvas_Theme_Resolve(
119 &g_app->theme,
120 Canvas_Theme_Mode_From_String(getenv("INFINITE_CANVAS_THEME")));
121
122 SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT);
123 #if defined(INFINITE_CANVAS_DEV_UI)
124 InitWindow(1920, 1200, "Infinite Canvas");
125 SetWindowMinSize(1200, 800);
126 #else
127 InitWindow(1280, 720, "Infinite Canvas");
128 SetWindowMinSize(640, 360);
129 #endif
130 SetTargetFPS(60);
131 #if !defined(PLATFORM_WEB)
132 SetWindowFocused();
133 #endif
134 if (!Canvas_Web_Surface_Init(&g_app->web_surface, p_arena)) {
135 TraceLog(LOG_WARNING, "Web surface initialization failed");
136 }
137
138 g_app->font = LoadFontEx("infinite_canvas/assets/Inter-Variable.ttf", 48, NULL, 0);
139 g_app->owns_font = g_app->font.texture.id != 0;
140 if (!g_app->owns_font) g_app->font = GetFontDefault();
141 else SetTextureFilter(g_app->font.texture, TEXTURE_FILTER_BILINEAR);
142 Canvas_Camera_Init(&g_app->camera, GetScreenWidth(), GetScreenHeight());
143 Canvas_Scene_Init(&g_app->scene, p_arena);
144 if (getenv("INFINITE_CANVAS_BROWSER_STRESS")) {
145 Canvas_Scene_Add_Browser_Grid(&g_app->scene);
146 } else {
147 Canvas_Scene_Add_Demo(&g_app->scene);
148 }
149 const char *p_image_source = getenv("INFINITE_CANVAS_IMAGE_SOURCE");
150 if (p_image_source) {
151 for (size_t index = 0; index < Dowa_Array_Length(g_app->scene.p_entities); index++) {
152 Canvas_Entity *p_entity = &g_app->scene.p_entities[index];
153 if (p_entity->type == CANVAS_ENTITY_IMAGE) {
154 snprintf(p_entity->text, sizeof(p_entity->text), "%s", p_image_source);
155 break;
156 }
157 }
158 }
159 #if !defined(PLATFORM_WEB)
160 for (size_t index = 0; index < Dowa_Array_Length(g_app->scene.p_entities); index++) {
161 Canvas_Entity *p_entity = &g_app->scene.p_entities[index];
162 if (p_entity->type == CANVAS_ENTITY_WEB_CONTENT) {
163 snprintf(p_entity->text, sizeof(p_entity->text), "https://www.google.com");
164 break;
165 }
166 }
167 #endif
168 #if defined(INFINITE_CANVAS_DEV_UI)
169 Canvas_Dev_UI_Init(&g_app->dev_ui, g_app->font, &g_app->theme);
170 #endif
171
172 #if defined(PLATFORM_WEB)
173 emscripten_set_main_loop(Canvas_App_Draw_Frame, 0, 1);
174 #else
175 while (!WindowShouldClose()) Canvas_App_Draw_Frame();
176 Canvas_Web_Surface_Shutdown(&g_app->web_surface);
177 if (g_app->owns_font) UnloadFont(g_app->font);
178 Dowa_Arena_Free(p_arena);
179 g_app = NULL;
180 CloseWindow();
181 #endif
182
183 return 0;
184 }