commit 1bd0508bf37a37f44f1216b3acb86bf36e7c017d Author: odweta Date: Sun Oct 4 20:27:59 2026 +0200 initial commit with demo in main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b8e3f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.clangd \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..96a5a37 --- /dev/null +++ b/Makefile @@ -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 \ No newline at end of file diff --git a/dangerzone.sublime-project b/dangerzone.sublime-project new file mode 100644 index 0000000..24db303 --- /dev/null +++ b/dangerzone.sublime-project @@ -0,0 +1,8 @@ +{ + "folders": + [ + { + "path": "." + } + ] +} diff --git a/dangerzone.sublime-workspace b/dangerzone.sublime-workspace new file mode 100644 index 0000000..d9c8515 --- /dev/null +++ b/dangerzone.sublime-workspace @@ -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": + { + } +} diff --git a/include/engine.h b/include/engine.h new file mode 100644 index 0000000..ff0fda0 --- /dev/null +++ b/include/engine.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include + +#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 +); \ No newline at end of file diff --git a/include/error.h b/include/error.h new file mode 100644 index 0000000..f5de78b --- /dev/null +++ b/include/error.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +#define eprintf(fmt, ...) \ + fprintf(stderr, "[ERROR] " fmt "\n", ##__VA_ARGS__) + +#define RCODE_FAILURE false +#define RCODE_SUCCESS true \ No newline at end of file diff --git a/src/engine/engine.c b/src/engine/engine.c new file mode 100644 index 0000000..7e12fc0 --- /dev/null +++ b/src/engine/engine.c @@ -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); +} \ No newline at end of file diff --git a/src/engine/engine_init.c b/src/engine/engine_init.c new file mode 100644 index 0000000..28e9444 --- /dev/null +++ b/src/engine/engine_init.c @@ -0,0 +1,148 @@ +#include "engine.h" +#include "error.h" +#include +#include + +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; +} \ No newline at end of file diff --git a/src/engine/render.c b/src/engine/render.c new file mode 100644 index 0000000..3f07353 --- /dev/null +++ b/src/engine/render.c @@ -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; + } +} + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..dd79fa2 --- /dev/null +++ b/src/main.c @@ -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; + } +} \ No newline at end of file