initial commit with demo in main.c

This commit is contained in:
2026-10-04 20:27:59 +02:00
commit 1bd0508bf3
10 changed files with 955 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
.clangd
+19
View File
@@ -0,0 +1,19 @@
CC := gcc
LIBS := `pkg-config --libs sdl3`
INC := `pkg-config --cflags sdl3` -I./include
SRC := src/main.c \
src/engine/engine.c \
src/engine/engine_init.c \
src/engine/render.c
OUT := build/main
build:
$(CC) -o $(OUT) $(SRC) $(INC) $(LIBS)
run:
$(OUT)
clean:
rm $(OUT)
.PHONY: build run
+8
View File
@@ -0,0 +1,8 @@
{
"folders":
[
{
"path": "."
}
]
}
+529
View File
@@ -0,0 +1,529 @@
{
"auto_complete":
{
"selected_items":
[
[
"SDL_EVENT",
"SDL_EVENT_KEY_DOWN"
],
[
"engine_sdl_",
"engine_sdl_destroy"
],
[
"clie",
"client_update_framebuffer"
],
[
"en",
"engine_update_framebuffer"
],
[
"engine",
"engine_poll_events"
],
[
"int",
"internal_state"
],
[
"inter",
"engine_internal_state_t"
],
[
"engine_",
"engine_update_framebuffer"
],
[
"RCO",
"RCODE_SUCCESS"
],
[
"game_s",
"game_state_t"
],
[
"sdl_state",
"engine_sdl_state_t"
],
[
"SDL_Des",
"SDL_DestroyWindow"
],
[
"R",
"RCODE_SUCCESS"
],
[
"W",
"window_name"
],
[
"window",
"window_name"
],
[
"WINDOW",
"WINDOW_NAME_LENGTH"
],
[
"RC",
"RCODE_FAILURE"
],
[
"eng",
"engine_sdl_state_t"
],
[
"SLD",
"SDL_Init"
],
[
"target",
"target_frame"
]
]
},
"buffers":
[
{
"file": ".clangd",
"settings":
{
"buffer_size": 77,
"encoding": "UTF-8",
"line_ending": "Unix"
},
"undo_stack":
[
[
8,
1,
"insert",
{
"characters": "dangerzone"
},
"CgAAAC0AAAAAAAAALgAAAAAAAAAQAAAAY19zZGxfcGxheWdyb3VuZC4AAAAAAAAALwAAAAAAAAAAAAAALwAAAAAAAAAwAAAAAAAAAAAAAAAwAAAAAAAAADEAAAAAAAAAAAAAADEAAAAAAAAAMgAAAAAAAAAAAAAAMgAAAAAAAAAzAAAAAAAAAAAAAAAzAAAAAAAAADQAAAAAAAAAAAAAADQAAAAAAAAANQAAAAAAAAAAAAAANQAAAAAAAAA2AAAAAAAAAAAAAAA2AAAAAAAAADcAAAAAAAAAAAAAAA",
"AQAAAAAAAAABAAAALQAAAAAAAAA9AAAAAAAAAAAAAAAAAPC/"
],
[
11,
14,
"left_delete",
null,
"DgAAACsAAAAAAAAAKwAAAAAAAAABAAAAZSoAAAAAAAAAKgAAAAAAAAABAAAAZCkAAAAAAAAAKQAAAAAAAAABAAAAbygAAAAAAAAAKAAAAAAAAAABAAAAQycAAAAAAAAAJwAAAAAAAAABAAAALyYAAAAAAAAAJgAAAAAAAAABAAAAcCUAAAAAAAAAJQAAAAAAAAABAAAAbSQAAAAAAAAAJAAAAAAAAAABAAAAaSMAAAAAAAAAIwAAAAAAAAABAAAALyIAAAAAAAAAIgAAAAAAAAABAAAAZSEAAAAAAAAAIQAAAAAAAAABAAAAbSAAAAAAAAAAIAAAAAAAAAABAAAAbx8AAAAAAAAAHwAAAAAAAAABAAAAaB4AAAAAAAAAHgAAAAAAAAABAAAALw",
"AQAAAAAAAAABAAAALAAAAAAAAAAsAAAAAAAAAAAAAAAAAPC/"
],
[
12,
1,
"insert",
{
"characters": "$HOME/Code"
},
"CgAAAB4AAAAAAAAAHwAAAAAAAAAAAAAAHwAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAACEAAAAAAAAAAAAAACEAAAAAAAAAIgAAAAAAAAAAAAAAIgAAAAAAAAAjAAAAAAAAAAAAAAAjAAAAAAAAACQAAAAAAAAAAAAAACQAAAAAAAAAJQAAAAAAAAAAAAAAJQAAAAAAAAAmAAAAAAAAAAAAAAAmAAAAAAAAACcAAAAAAAAAAAAAACcAAAAAAAAAKAAAAAAAAAAAAAAA",
"AQAAAAAAAAABAAAAHgAAAAAAAAAeAAAAAAAAAAAAAAAAAPC/"
]
]
}
],
"build_system": "",
"build_system_choices":
[
[
[
[
"Packages/Makefile/Make.sublime-build",
""
],
[
"Packages/Makefile/Make.sublime-build",
"Clean"
]
],
[
"Packages/Makefile/Make.sublime-build",
""
]
],
[
[
[
"Packages/User/Custom Make.sublime-build",
""
],
[
"Packages/User/Custom Make.sublime-build",
"Run"
],
[
"Packages/User/Custom Make.sublime-build",
"Clean"
]
],
[
"Packages/User/Custom Make.sublime-build",
"Clean"
]
]
],
"build_varint": "",
"command_palette":
{
"height": 0.0,
"last_filter": "",
"selected_items":
[
[
"log",
"LSP: Toggle Log Panel"
],
[
"log panel",
"LSP: Toggle Log Panel"
],
[
"toggle lo",
"LSP: Toggle Log Panel"
],
[
"globally",
"LSP: Enable Language Server Globally"
],
[
"lsp enable",
"LSP: Enable Language Server Globally"
],
[
"list packages",
"Package Control: List Packages"
],
[
"satisfy",
"Package Control: Satisfy Libraries"
],
[
"instal",
"Package Control: Install Package"
],
[
"branch",
"git: branch"
],
[
"status",
"git: status"
],
[
"commit",
"git: commit"
],
[
"git: status",
"git: status"
],
[
"install",
"Package Control: Install Package"
],
[
"remove",
"Package Control: Remove Package"
],
[
"git gui",
"Git: Gui"
],
[
"git",
"Git: Gitk"
],
[
"Package Control: install",
"Package Control: Install Package"
],
[
"Package Control: insta",
"Package Control: Install Package"
],
[
"Package Control: remove",
"Package Control: Remove Package"
],
[
"zeal:",
"Zeal: Search"
],
[
"zeal",
"Zeal: Search"
],
[
"Package Control: ",
"Package Control: Remove Package"
],
[
"clangd",
"Preferences: LSP-clangd Settings"
],
[
"install package",
"Package Control: Install Package"
],
[
"package",
"Install Package Control"
]
],
"width": 0.0
},
"console":
{
"height": 0.0,
"history":
[
]
},
"distraction_free":
{
"menu_visible": true,
"show_minimap": false,
"show_open_files": false,
"show_tabs": false,
"side_bar_visible": false,
"status_bar_visible": false
},
"expanded_folders":
[
"/home/imp/Code/dangerzone",
"/home/imp/Code/dangerzone/include",
"/home/imp/Code/dangerzone/src"
],
"file_history":
[
"/home/imp/Code/c_sdl_playground/include/error.h",
"/home/imp/Code/c_sdl_playground/src/engine/render.c",
"/home/imp/Code/c_sdl_playground/src/engine/engine_init.c",
"/home/imp/Code/c_sdl_playground/Makefile",
"/home/imp/Code/c_sdl_playground/include/engine.h",
"/home/imp/Code/c_sdl_playground/src/engine/engine.c",
"/home/imp/Code/c_sdl_playground/src/main.c",
"/home/imp/Code/c_sdl_playground/include/render.h",
"/home/imp/Code/c_sdl_playground/.clangd",
"/home/imp/Code/c_sdl_playground/src/error.c",
"/home/imp/Code/c_sdl_playground/compile_options.txt",
"/home/imp/.config/sublime-text/Packages/User/zeal_unfiltered.py",
"/home/imp/Dokumenty/sdlwiki/SDL3/SDL_Window.html",
"/home/imp/.config/sublime-text/Packages/User/open-offline-doc.py",
"/home/imp/.config/sublime-text/Packages/User/open-url.py",
"/home/imp/.local/share/Zeal/Zeal/docsets/import_docset.py",
"/home/imp/.local/share/Zeal/Zeal/docsets/SDL3.docset/Contents/Info.plist",
"/home/imp/.local/share/Zeal/Zeal/docsets/SDL.docset/Contents/Resources/docSet.dsidx",
"/home/imp/.config/sublime-text/Packages/User/LSP-clangd.sublime-settings",
"/home/imp/.config/sublime-text/Packages/LSP-clangd/LSP-clangd.sublime-settings",
"/home/imp/Code/c_sdl_playground/src/compile_options.txt",
"/home/imp/sdl-builder/SDL/build/build.sh",
"/home/imp/sdl-builder/Dockerfile",
"/home/imp/sdl-builder/build-sdl.sh",
"/home/imp/.config/sublime-text/Packages/User/Custom Make.sublime-build",
"/home/imp/Code/c_sdl_playground/build/main",
"/home/imp/.config/sublime-text/Packages/User/Make.sublime-build",
"/home/imp/.config/sublime-text/Packages/User/make.sublime-build",
"/home/imp/xzlevor/s1/d_2048.py"
],
"find":
{
"height": 45.0
},
"find_in_files":
{
"height": 0.0,
"where_history":
[
]
},
"find_state":
{
"case_sensitive": false,
"find_history":
[
],
"highlight": true,
"in_selection": false,
"preserve_case": false,
"regex": false,
"replace_history":
[
],
"reverse": false,
"scrollbar_highlights": true,
"show_context": true,
"use_buffer2": true,
"use_gitignore": true,
"whole_word": false,
"wrap": true
},
"groups":
[
{
"sheets":
[
{
"buffer": 0,
"file": ".clangd",
"selected": true,
"semi_transient": false,
"settings":
{
"buffer_size": 77,
"regions":
{
},
"selection":
[
[
41,
41
]
],
"settings":
{
"lsp_uri": "file:///home/imp/Code/dangerzone/.clangd",
"syntax": "Packages/Text/Plain text.tmLanguage",
"tab_size": 2,
"translate_tabs_to_spaces": true
},
"translation.x": 0.0,
"translation.y": 0.0,
"zoom_level": 1.0
},
"stack_index": 0,
"stack_multiselect": false,
"type": "text"
}
]
}
],
"incremental_find":
{
"height": 0.0
},
"input":
{
"height": 47.0
},
"layout":
{
"cells":
[
[
0,
0,
1,
1
]
],
"cols":
[
0.0,
1.0
],
"rows":
[
0.0,
1.0
]
},
"menu_visible": true,
"output.LSP Log Panel":
{
"height": 335.0,
"history":
[
]
},
"output.diagnostics":
{
"height": 0.0,
"history":
[
]
},
"output.exec":
{
"height": 151.0,
"history":
[
]
},
"output.find_results":
{
"height": 0.0,
"history":
[
]
},
"output.git":
{
"height": 178.0,
"history":
[
]
},
"output.mdpopups":
{
"height": 0.0,
"history":
[
]
},
"pinned_build_system": "Packages/User/Custom Make.sublime-build",
"project": "dangerzone.sublime-project",
"replace":
{
"height": 0.0
},
"save_all_on_build": true,
"select_file":
{
"height": 0.0,
"last_filter": "",
"selected_items":
[
],
"width": 0.0
},
"select_project":
{
"height": 0.0,
"last_filter": "",
"selected_items":
[
],
"width": 0.0
},
"select_symbol":
{
"height": 0.0,
"last_filter": "",
"selected_items":
[
],
"width": 0.0
},
"selected_group": 0,
"settings":
{
"lsp_show_inlay_hints": false
},
"show_minimap": true,
"show_open_files": false,
"show_tabs": true,
"side_bar_visible": true,
"side_bar_width": 267.0,
"status_bar_visible": true,
"template_settings":
{
}
}
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#include <SDL3/SDL.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720
#define DEFAULT_WINDOW_NAME "SDL Playground"
#define WINDOW_NAME_LENGTH 128
#define TARGET_FRAME 1.0 / 60.0
typedef struct {
char window_name[WINDOW_NAME_LENGTH];
uint16_t width, height;
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *texture;
} engine_sdl_state_t;
typedef struct {
/**
* this function runs inside of the SDL_PollEvent loop
* useful for user input, quitting the program and so on
*/
void (*engine_poll_events)(void);
/**
* meant as the "area" for manupulating the framebuffer;
* after this is executed, the contents of
* game_state->framebuffer get drawn
*/
void (*engine_update_framebuffer)(void);
engine_sdl_state_t *sdl_state;
} engine_internal_state_t;
typedef struct {
uint32_t framebuffer[WINDOW_WIDTH * WINDOW_HEIGHT];
SDL_Event event;
bool is_running;
uint16_t width, height;
} game_state_t;
extern engine_internal_state_t *internal_state;
extern game_state_t *game_state;
engine_sdl_state_t*
engine_sdl_init();
void
engine_sdl_destroy(
engine_sdl_state_t *state
);
bool
engine_init(
void (*engine_poll_events)(void),
void (*engine_update_framebuffer)(void)
);
void
engine_start();
void
engine_stop();
void
put_pixel(
unsigned int x,
unsigned int y,
uint32_t color
);
void
clear_framebuffer(
uint32_t color
);
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <stdio.h>
#include <stdbool.h>
#define eprintf(fmt, ...) \
fprintf(stderr, "[ERROR] " fmt "\n", ##__VA_ARGS__)
#define RCODE_FAILURE false
#define RCODE_SUCCESS true
+59
View File
@@ -0,0 +1,59 @@
#include "engine.h"
engine_internal_state_t *internal_state = NULL;
game_state_t *game_state = NULL;
void
engine_start() {
// main game loop
while(game_state->is_running) {
uint64_t start = SDL_GetPerformanceCounter();
// poll events
while (SDL_PollEvent(&game_state->event)) {
internal_state->engine_poll_events();
}
// call the user-defined framebuffer manipulation function
internal_state->engine_update_framebuffer();
// update the texture with the modified framebuffer
SDL_UpdateTexture(
internal_state->sdl_state->texture,
NULL, game_state->framebuffer,
internal_state->sdl_state->width * sizeof(uint32_t)
);
SDL_RenderClear(
internal_state->sdl_state->renderer
);
SDL_RenderTexture(
internal_state->sdl_state->renderer,
internal_state->sdl_state->texture,
NULL, NULL
);
SDL_RenderPresent(
internal_state->sdl_state->renderer
);
uint64_t end = SDL_GetPerformanceCounter();
double elapsed =
(double)(end - start) /
(double)SDL_GetPerformanceFrequency();
// cap the framerate at '1.0/TARGET_FRAME' fps
if (elapsed < TARGET_FRAME) {
SDL_Delay((TARGET_FRAME - elapsed) * 1000);
}
}
}
void
engine_stop() {
engine_sdl_destroy(internal_state->sdl_state);
free(internal_state);
free(game_state);
}
+148
View File
@@ -0,0 +1,148 @@
#include "engine.h"
#include "error.h"
#include <SDL3/SDL_render.h>
#include <SDL3/SDL_video.h>
bool
engine_sdl_init_state(engine_sdl_state_t **state) {
*state = (engine_sdl_state_t *)
malloc(sizeof(engine_sdl_state_t));
if (*state == NULL) {
eprintf("failed to initialize engine_sdl_state");
return RCODE_FAILURE;
}
(*state)->width = WINDOW_WIDTH;
(*state)->height = WINDOW_HEIGHT;
strncpy(
(*state)->window_name, DEFAULT_WINDOW_NAME,
WINDOW_NAME_LENGTH
);
return RCODE_SUCCESS;
}
bool
engine_sdl_init_root()
{
if (!SDL_Init(SDL_INIT_VIDEO)) {
eprintf("SDL_Init failed! %s\n", SDL_GetError());
return RCODE_FAILURE;
} else {
return RCODE_SUCCESS;
}
}
bool
engine_sdl_init_window(
engine_sdl_state_t* state
) {
state->window = SDL_CreateWindow(
state->window_name,
state->width, state->height, 0
);
if (state->window == NULL) {
eprintf("SDL_CreateWindow failed! %s\n", SDL_GetError());
SDL_Quit();
return RCODE_FAILURE;
}
return RCODE_SUCCESS;
}
bool
engine_sdl_init_renderer(
engine_sdl_state_t *state
) {
state->renderer = SDL_CreateRenderer(state->window, NULL);
if (state->renderer == NULL) {
eprintf("SDL_CreateRenderer failed! %s\n", SDL_GetError());
SDL_DestroyWindow(state->window);
SDL_Quit();
return RCODE_FAILURE;
}
return RCODE_SUCCESS;
}
bool
engine_sdl_init_texture(
engine_sdl_state_t *state
) {
state->texture = SDL_CreateTexture(
state->renderer, SDL_PIXELFORMAT_XRGB8888,
SDL_TEXTUREACCESS_STREAMING,
state->width, state->height
);
if (state->texture == NULL) {
eprintf("SDL_CreateTexture failed! %s\n", SDL_GetError());
SDL_DestroyWindow(state->window);
SDL_DestroyRenderer(state->renderer);
SDL_Quit();
return RCODE_FAILURE;
}
return RCODE_SUCCESS;
}
engine_sdl_state_t*
engine_sdl_init()
{
engine_sdl_state_t *state;
if (!engine_sdl_init_state(&state) ||
!engine_sdl_init_root() ||
!engine_sdl_init_window(state) ||
!engine_sdl_init_renderer(state) ||
!engine_sdl_init_texture(state)
) {
return NULL;
}
return state;
}
/**
* only call intentinally, not when engine_sdl_init fails
* -> it cleans up after itself already
*/
void
engine_sdl_destroy(
engine_sdl_state_t *state
) {
SDL_DestroyTexture(state->texture);
SDL_DestroyRenderer(state->renderer);
SDL_DestroyWindow(state->window);
SDL_Quit();
free(state);
}
bool
engine_init(
void (*engine_poll_events)(void),
void (*engine_update_framebuffer)(void)
) {
internal_state = (engine_internal_state_t *)
malloc(sizeof(engine_internal_state_t));
game_state = (game_state_t *)
malloc(sizeof(game_state_t));
internal_state->engine_poll_events
= engine_poll_events;
internal_state->engine_update_framebuffer
= engine_update_framebuffer;
internal_state->sdl_state = engine_sdl_init();
game_state->is_running = true;
game_state->width = internal_state->sdl_state->width;
game_state->height = internal_state->sdl_state->height;
return (game_state == NULL || internal_state == NULL)
? RCODE_FAILURE : RCODE_SUCCESS;
}
+37
View File
@@ -0,0 +1,37 @@
#include "engine.h"
#include "error.h"
// alter a pixel at an imaginary x & y coordinates
void
put_pixel(
unsigned int x,
unsigned int y,
uint32_t color
) {
uint16_t w = game_state->width;
uint16_t h = game_state->height;
if (x < 0 || x >= w || y < 0 || y >= h) {
eprintf(
"CRITICAL: attempted buffer overflow in put_pixel\n"
"at x = %d, y = %d", x, y
);
} else {
game_state->framebuffer[game_state->width * y + x]
= color;
}
}
// fill the entire framebuffer with a given color
void
clear_framebuffer(
uint32_t color
) {
uint16_t w = game_state->width;
uint16_t h = game_state->height;
for (int i = 0; i < w * h; i++) {
game_state->framebuffer[i] = color;
}
}
+66
View File
@@ -0,0 +1,66 @@
#include "engine.h"
bool left = true;
// polling events every frame
void
client_poll_event() {
SDL_Event ev = game_state->event;
if (ev.type == SDL_EVENT_QUIT) {
game_state->is_running = false;
}
if (ev.type == SDL_EVENT_KEY_DOWN) {
switch (ev.key.key) {
case SDLK_Q:
game_state->is_running = false;
}
}
}
// performed every frame
void
client_update_framebuffer() {
// draw a green square
// clear_framebuffer(0x000000);
// for (int i = 0; i < 20; i++) {
// for (int j = 0; j < 20; j++) {
// put_pixel(i, j, 0x00FF00);
// }
// }
// draw alternating green squares
if (left) {
clear_framebuffer(0x000000);
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 20; j++) {
put_pixel(i, j, 0x00FF00);
}
}
left = false;
} else {
clear_framebuffer(0x000000);
for (int i = 20; i < 40; i++) {
for (int j = 20; j < 40; j++) {
put_pixel(i, j, 0x00FFFF);
}
}
left = true;
}
SDL_Delay(100);
}
int main() {
// initialize the engine
if (engine_init(
client_poll_event,
client_update_framebuffer
)) {
engine_start();
engine_stop();
return 0;
} else {
return 1;
}
}