aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkdx <kikoodx@paranoici.org>2023-12-17 15:03:31 +0100
committerkdx <kikoodx@paranoici.org>2023-12-17 15:04:46 +0100
commit39ff0a1b8b1eb2fe2dc222c8ba6d76891d5b5b88 (patch)
tree06fb74d78b38414da69a5d77c4dac7da4c9e088d
downloadsky-39ff0a1b8b1eb2fe2dc222c8ba6d76891d5b5b88.tar.gz
initial commit
-rw-r--r--.gitignore2
-rw-r--r--LICENSE19
-rw-r--r--README14
-rw-r--r--Sky.c270
-rw-r--r--Sky.h84
-rw-r--r--demo/main.c27
-rw-r--r--demo/tileset.pngbin0 -> 1491 bytes
7 files changed, 416 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ae9f53d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/*.o
+/a.out
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4a85a6d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2023 (:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README b/README
new file mode 100644
index 0000000..09974a0
--- /dev/null
+++ b/README
@@ -0,0 +1,14 @@
+Sky
+===
+
+Framework minimal design pour l'animateur d'à côté.
+
+Utilisation
+-----------
+
+Lire `Sky.h` et `demo/main.c`.
+
+Licence
+-------
+
+Voir `LICENSE`.
diff --git a/Sky.c b/Sky.c
new file mode 100644
index 0000000..a4baa37
--- /dev/null
+++ b/Sky.c
@@ -0,0 +1,270 @@
+/* Licensing informations can be found at the end of the file. */
+
+#include "Sky.h"
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+#include <errno.h>
+#include <unistd.h>
+
+static SDL_Window *_window = NULL;
+static SDL_Renderer *_renderer = NULL;
+static SDL_Texture *_target = NULL;
+static SDL_Texture *_tileset = NULL;
+static int _dwidth = 1;
+static int _dheight = 1;
+static int _tile_size = 16;
+static int _tileset_width = 0;
+static int _tileset_height = 0;
+static Uint64 _min_dt = 0;
+static Uint64 _next_time = 0;
+static bool _should_quit = false;
+static long _tick = 0;
+static bool _input[KEY_X] = {0};
+static const SDL_Scancode _keymap[][8] = {
+ { SDL_SCANCODE_LEFT, SDL_SCANCODE_A, 0 },
+ { SDL_SCANCODE_RIGHT, SDL_SCANCODE_D, 0 },
+ { SDL_SCANCODE_UP, SDL_SCANCODE_W, 0 },
+ { SDL_SCANCODE_DOWN, SDL_SCANCODE_S, 0 },
+ { SDL_SCANCODE_Z, SDL_SCANCODE_J, 0 },
+ { SDL_SCANCODE_X, SDL_SCANCODE_K, 0 }
+};
+
+static void _SkyDeinit(void) {
+ if (_tileset != NULL)
+ SDL_DestroyTexture(_tileset);
+ if (_target != NULL)
+ SDL_DestroyTexture(_target);
+ if (_renderer != NULL)
+ SDL_DestroyRenderer(_renderer);
+ if (_window != NULL)
+ SDL_DestroyWindow(_window);
+ IMG_Quit();
+ SDL_Quit();
+}
+
+void Init(int fps, int width, int height, const char *tset_path, int tsize) {
+ if (atexit(_SkyDeinit))
+ Panic("atexit failed: %s", strerror(errno));
+
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
+ Panic("%s", SDL_GetError());
+
+ if (IMG_Init(IMG_INIT_PNG) == 0)
+ Panic("%s", IMG_GetError());
+
+ char *const basepath = SDL_GetBasePath();
+ if (basepath == NULL)
+ Panic("%s", SDL_GetError());
+ const int chdir_rv = chdir(basepath);
+ SDL_free(basepath);
+ if (chdir_rv < 0)
+ Panic("%s", strerror(errno));
+
+ _window = SDL_CreateWindow("",
+ SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ width, height, SDL_WINDOW_RESIZABLE);
+ if (_window == NULL)
+ Panic("%s", SDL_GetError());
+
+ _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
+ if (_renderer == NULL)
+ Panic("%s", SDL_GetError());
+
+ _target = SDL_CreateTexture(_renderer, SDL_PIXELFORMAT_RGB888,
+ SDL_TEXTUREACCESS_TARGET, width, height);
+ if (_target == NULL)
+ Panic("%s", SDL_GetError());
+
+ SetFPS(fps);
+ SetTileset(tset_path, tsize);
+ _dwidth = width;
+ _dheight = height;
+}
+
+void SetFPS(int fps) {
+ if (fps <= 0)
+ _min_dt = 0;
+ else {
+ if (_min_dt == 0)
+ _next_time = SDL_GetTicks64();
+ _min_dt = 1000 / fps;
+ }
+}
+
+void SetTileset(const char *path, int tile_size) {
+ Assert(path != NULL);
+ Assert(tile_size > 0);
+
+ if (_tileset != NULL)
+ SDL_DestroyTexture(_tileset);
+
+ _tileset = IMG_LoadTexture(_renderer, path);
+ if (_tileset == NULL)
+ Panic("%s", IMG_GetError());
+
+ if (SDL_QueryTexture(_tileset, NULL, NULL,
+ &_tileset_width, &_tileset_height) < 0)
+ Panic("%s", SDL_GetError());
+
+ _tile_size = tile_size;
+ _tileset_width /= _tile_size;
+ _tileset_height /= _tile_size;
+
+ if (SDL_SetRenderTarget(_renderer, _target) < 0)
+ Panic("%s", SDL_GetError());
+}
+
+void SetTitle(const char *title) {
+ SDL_SetWindowTitle(_window, title);
+}
+
+bool ShouldQuit(void) {
+ return _should_quit;
+}
+
+long Tick(void) {
+ return _tick;
+}
+
+bool Keydown(unsigned key) {
+ Assert(key <= KEY_X);
+ return _input[key];
+}
+
+void DrawUpdate(void) {
+ if (SDL_SetRenderTarget(_renderer, NULL) < 0)
+ Panic("%s", SDL_GetError());
+
+ DrawColor(0, 0, 0);
+ DrawClear();
+
+ int win_w, win_h;
+ SDL_GetWindowSize(_window, &win_w, &win_h);
+ const float ratio_w = (float)win_w / _dwidth;
+ const float ratio_h = (float)win_h / _dheight;
+ float scale = (ratio_w < ratio_h) ? ratio_w : ratio_h;
+ if (scale > 1.0)
+ scale = (int)scale;
+
+ const SDL_Rect dst = {
+ (win_w - _dwidth * scale) / 2,
+ (win_h - _dheight * scale) / 2,
+ _dwidth * scale,
+ _dheight * scale
+ };
+ if (SDL_RenderCopy(_renderer, _target, NULL, &dst) < 0)
+ Panic("%s", SDL_GetError());
+
+ /* Limit frames per second. */
+ if (_min_dt) {
+ const Uint64 cur_time = SDL_GetTicks64();
+ if (_next_time + _min_dt <= cur_time)
+ _next_time = cur_time;
+ else
+ SDL_Delay(_next_time + _min_dt - cur_time);
+ }
+
+ SDL_RenderPresent(_renderer);
+
+ if (SDL_SetRenderTarget(_renderer, _target) < 0)
+ Panic("%s", SDL_GetError());
+
+ /* Cycle events. */
+ SDL_Event e;
+ while (SDL_PollEvent(&e)) {
+ switch (e.type) {
+ case SDL_QUIT:
+ _should_quit = true;
+ break;
+ case SDL_KEYDOWN:
+ if (e.key.repeat)
+ break;
+ /* fallthrough */
+ case SDL_KEYUP:
+ for (int i = 0; i < KEY_X; i++) {
+ for (int k = 0; _keymap[i][k]; k++) {
+ const SDL_Scancode sc = _keymap[i][k];
+ if (sc == e.key.keysym.scancode) {
+ _input[i] =
+ (e.type == SDL_KEYDOWN);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ _tick += 1;
+}
+
+void DrawColor(float r, float g, float b) {
+ if (SDL_SetRenderDrawColor(_renderer, r*255, g*255, b*255, 255) < 0)
+ PWarn("%s", SDL_GetError());
+}
+
+void DrawClear(void) {
+ if (SDL_RenderClear(_renderer) < 0)
+ PWarn("%s", SDL_GetError());
+}
+
+void DrawPoint(int x, int y) {
+ if (SDL_RenderDrawPoint(_renderer, x, y) < 0)
+ PWarn("%s", SDL_GetError());
+}
+
+void DrawRect(int x, int y, int w, int h, bool fill) {
+ const SDL_Rect rect = { x, y, w + (w == 0), h + (h == 0) };
+ if (fill) {
+ if (SDL_RenderFillRect(_renderer, &rect) < 0)
+ PWarn("%s", SDL_GetError());
+ } else {
+ if (SDL_RenderDrawRect(_renderer, &rect) < 0)
+ PWarn("%s", SDL_GetError());
+ }
+}
+
+void DrawLine(int x1, int y1, int x2, int y2) {
+ if (SDL_RenderDrawLine(_renderer, x1, y1, x2, y2) < 0)
+ PWarn("%s", SDL_GetError());
+}
+
+void DrawTile(int id, int x, int y) {
+ Assert(id < _tileset_width * _tileset_height);
+
+ const SDL_Rect src = {
+ id % _tileset_width * _tile_size,
+ id / _tileset_width * _tile_size,
+ _tile_size,
+ _tile_size
+ };
+ const SDL_Rect dst = { x, y, _tile_size, _tile_size };
+
+ if (SDL_RenderCopy(_renderer, _tileset, &src, &dst) < 0)
+ PWarn("%s", SDL_GetError());
+}
+
+/*
+ * Copyright (c) 2023 (:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
diff --git a/Sky.h b/Sky.h
new file mode 100644
index 0000000..8875066
--- /dev/null
+++ b/Sky.h
@@ -0,0 +1,84 @@
+/* Licensing informations can be found at the end of the file. */
+
+#pragma once
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* INITIALIZATION
+ * Call this function exactly once to initalize your project.
+ * Deinitialization is taken care of for you.
+ * You don't need to worry about failure either, Panic will be used. */
+void Init(int fps, int width, int height, const char *tset_path, int tsize);
+
+/* CONFIGURATION
+ * These functions can be called any number of time during runtime. */
+void SetFPS(int fps);
+void SetTileset(const char *path, int tile_size);
+void SetTitle(const char *title);
+
+/* SYSTEM */
+bool ShouldQuit(void);
+long Tick(void);
+
+/* INPUT */
+enum {
+ KEY_LEFT,
+ KEY_RIGHT,
+ KEY_UP,
+ KEY_DOWN,
+ KEY_O,
+ KEY_X
+};
+bool Keydown(unsigned key);
+
+/* DRAW
+ * Call DrawUpdate after all your draw calls to refresh the screen. */
+void DrawUpdate(void);
+void DrawColor(float r, float g, float b);
+void DrawClear(void);
+void DrawPoint(int x, int y);
+void DrawRect(int x, int y, int w, int h, bool fill);
+void DrawLine(int x1, int y1, int x2, int y2);
+void DrawTile(int id, int x, int y);
+
+/* MACRO SECTION
+ * Panic prints an error message and crashes the game.
+ * Assert calls panic if given expression is false.
+ * Alloc is a wrapper around calloc that calls Panic on allocation failure. */
+#define STR(X) #X
+#define PGeneric(PREFIX, ...) fprintf(stderr, \
+ "\x1b[" PREFIX " %s:%s:%d\x1b[0m \t", \
+ __FILE_NAME__, __FUNCTION__, __LINE__), \
+ fprintf(stderr, __VA_ARGS__), fputc('\n', stderr)
+#define PLog(...) PGeneric("94mLOG", __VA_ARGS__)
+#define PErr(...) PGeneric("91mERR", __VA_ARGS__)
+#define PWarn(...) PGeneric("93mWRN", __VA_ARGS__)
+#define Panic(...) PErr(__VA_ARGS__), exit(EXIT_FAILURE)
+#define Assert(X) if (!(X)) Panic("assert failed: " STR(X))
+#define Alloc(X) ({ void *___ptr = calloc((X), 1); \
+ if (___ptr == NULL) \
+ perr("calloc failed: %s", strerror(errno)); \
+ ___ptr; })
+
+/*
+ * Copyright (c) 2023 (:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
diff --git a/demo/main.c b/demo/main.c
new file mode 100644
index 0000000..cd73aa8
--- /dev/null
+++ b/demo/main.c
@@ -0,0 +1,27 @@
+/* build with
+ * $ gcc Sky.c demo/main.c -lSDL2 -lSDL2_image */
+
+#include "../Sky.h"
+
+int main(int argc, char **argv) {
+ Init(60, 400, 224, "demo/tileset.png", 16);
+ SetTitle("Sky DEMO");
+
+ int x = 0, y = 0;
+
+ while (!ShouldQuit()) {
+ x += Keydown(KEY_RIGHT);
+ x -= Keydown(KEY_LEFT);
+ y += Keydown(KEY_DOWN);
+ y -= Keydown(KEY_UP);
+
+ DrawColor(0, 0, 1);
+ DrawClear();
+ DrawTile(Tick() % 16, Tick() % 400, Tick() % 224);
+ DrawTile(2, (Tick() + 200) % 400, 223 - (Tick() + 112) % 224);
+ DrawTile(18, x, y);
+ DrawUpdate();
+ }
+
+ return 0;
+}
diff --git a/demo/tileset.png b/demo/tileset.png
new file mode 100644
index 0000000..ea428d6
--- /dev/null
+++ b/demo/tileset.png
Binary files differ