diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-07 19:03:04 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-07 19:03:04 +0800 |
| commit | dfa9a616c3d89a935a4c0bf68f7801581d8b5037 (patch) | |
| tree | dbedd97ded9a227751555744fc246943af6e6c77 /src | |
| parent | 5eaa99c67ca44bd48d8e2eae7f906c942d565d67 (diff) | |
Move headers to kon/ and add per-lib implementation stubs
Diffstat (limited to 'src')
| -rw-r--r-- | src/konfont.h | 197 | ||||
| -rw-r--r-- | src/koninput.h | 142 | ||||
| -rw-r--r-- | src/konsofren.h | 1012 | ||||
| -rw-r--r-- | src/kontime.h | 85 | ||||
| -rw-r--r-- | src/kontoml.h | 649 | ||||
| -rw-r--r-- | src/konwinlib.h | 1357 |
6 files changed, 0 insertions, 3442 deletions
diff --git a/src/konfont.h b/src/konfont.h deleted file mode 100644 index 822e1f3..0000000 --- a/src/konfont.h +++ /dev/null @@ -1,197 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * konfont.h - * - * Copyright (c) 2026 KonAki - * - * 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. - */ - -#ifndef KONFONT_H -#define KONFONT_H - -#include <stdint.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdlib.h> - -#include "konsofren.h" - -#define KON_FONT_FIRST_CHAR 32 -#define KON_FONT_NUM_CHARS 95 - -typedef struct kon_font kon_font_t; - -/*** font declarations ***/ - -kon_font_t *kon_loadFont(const uint8_t *ttf_data, size_t size, float pixelHeight); -void kon_freeFont(kon_font_t *font); - -kon_image *kon_fontGetAtlas(kon_font_t *font); - -/*** text draw declarations ***/ - -void kon_drawText(kon_framebuffer_t *fb, kon_font_t *font, int x, int y, const char *text, uint32_t color); -void kon_measureText(kon_font_t *font, const char *text, int *outWidth, int *outHeight); - -/*** implementatoin ***/ - -#ifdef KONFONT_IMPLEMENTATION - -#include "stb_truetype.h" - -struct kon_font { - stbtt_bakedchar chardata[KON_FONT_NUM_CHARS]; - uint8_t *atlasPixels; - int atlasSize; - float pixelHeight; -}; - -/*** private helpers ***/ - -static bool kon_fontBakeAtlas(kon_font_t *font, const uint8_t *ttf_data, float pixelHeight) { - int sizes[] = {256, 512, 1024, 2048}; - - for (int i = 0; i < 4; i++) { - int atlasSize = sizes[i]; - - uint8_t *bitmap = malloc((size_t)atlasSize * (size_t)atlasSize); - if (!bitmap) return false; - - int result = stbtt_BakeFontBitmap(ttf_data, 0, pixelHeight, bitmap, atlasSize, atlasSize, KON_FONT_FIRST_CHAR, KON_FONT_NUM_CHARS, font->chardata); - - if (result > 0) { - font->atlasPixels = bitmap; - font->atlasSize = atlasSize; - return true; - } - - free(bitmap); - } - - return false; -} - -/*** font implementation ***/ - -kon_font_t *kon_loadFont(const uint8_t *ttf_data, size_t size, float pixelHeight) { - (void)size; - - if (!ttf_data || pixelHeight <= 0.0f) return NULL; - - kon_font_t *font = malloc(sizeof(kon_font_t)); - if (!font) return NULL; - - font->atlasPixels = NULL; - font->pixelHeight = pixelHeight; - - if (!kon_fontBakeAtlas(font, ttf_data, pixelHeight)) { - free(font); - return NULL; - } - - return font; -} - -void kon_freeFont(kon_font_t *font) { - if (!font) return; - - free(font->atlasPixels); - free(font); -} - -kon_image *kon_fontGetAtlas(kon_font_t *font) { - if (!font) return NULL; - - int n = font->atlasSize * font->atlasSize; - uint8_t *rgba = malloc((size_t)n * 4); - if (!rgba) return NULL; - - for (int i = 0; i < n; i++) { - uint8_t a = font->atlasPixels[i]; - rgba[i * 4 + 0] = 255; - rgba[i * 4 + 1] = 255; - rgba[i * 4 + 2] = 255; - rgba[i * 4 + 3] = a; - } - - kon_image *img = kon_loadImage(rgba, font->atlasSize, font->atlasSize, konFormatRGBA8); - free(rgba); - return img; -} - -/*** text draw implementation ***/ - -void kon_drawText(kon_framebuffer_t *fb, kon_font_t *font, int x, int y, const char *text, uint32_t color) { - if (!fb || !font || !text) return; - - uint32_t baseColor = color & 0x00FFFFFF; - - float cursorX = (float)x; - float cursorY = (float)y; - - for (const char *p = text; *p; p++) { - unsigned char c = (unsigned char)*p; - - if (c < KON_FONT_FIRST_CHAR || c >= KON_FONT_FIRST_CHAR + KON_FONT_NUM_CHARS) continue; - - stbtt_aligned_quad q; - stbtt_GetBakedQuad(font->chardata, font->atlasSize, font->atlasSize, c - KON_FONT_FIRST_CHAR, &cursorX, &cursorY, &q, 0); - - int src_x0 = (int)(q.s0 * font->atlasSize); - int src_y0 = (int)(q.t0 * font->atlasSize); - int glyphW = (int)(q.x1 - q.x0); - int glyphH = (int)(q.y1 - q.y0); - - for (int gy = 0; gy < glyphH; gy++) { - for (int gx = 0; gx < glyphW; gx++) { - uint8_t alpha = font->atlasPixels[(src_y0 + gy) * font->atlasSize + (src_x0 + gx)]; - if (alpha == 0) continue; - - kon_putPixel(fb, (int)q.x0 + gx, (int)q.y0 + gy, ((uint32_t)alpha << 24) | baseColor); - } - } - } -} - -void kon_measureText(kon_font_t *font, const char *text, int *outWidth, int *outHeight) { - if (!font || !text) return; - - float cursorX = 0.0f; - float cursorY = 0.0f; - float maxX = 0.0f; - - for (const char *p = text; *p; p++) { - unsigned char c = (unsigned char)*p; - - if (c < KON_FONT_FIRST_CHAR || c >= KON_FONT_FIRST_CHAR + KON_FONT_NUM_CHARS) continue; - - stbtt_aligned_quad q; - stbtt_GetBakedQuad(font->chardata, font->atlasSize, font->atlasSize, c - KON_FONT_FIRST_CHAR, &cursorX, &cursorY, &q, 0); - - if (cursorX > maxX) maxX = cursorX; - } - - if (outWidth) *outWidth = (int)maxX; - if (outHeight) *outHeight = (int)font->pixelHeight; -} - -#endif /* KONFONT_IMPLEMENTATION */ - -#endif /* KONFONT_H */ diff --git a/src/koninput.h b/src/koninput.h deleted file mode 100644 index 5008c06..0000000 --- a/src/koninput.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - -MIT License - -Copyright (c) 2026 KonAki - -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. - -*/ - -#ifndef KONINPUT_H -#define KONINPUT_H - -#include "konwinlib.h" - -typedef struct kon_input { - bool down[KON_KEY_COUNT]; - bool downPrev[KON_KEY_COUNT]; - - bool mouseDown[3]; - bool mouseDownPrev[3]; - - int mouseX, mouseY; -} kon_input_t; - -void kon_inputBeginFrame(kon_input_t *input); - -void kon_inputFeedEvent(kon_input_t *input, const kon_event_t *event); - -bool kon_isKeyDown(const kon_input_t *input, kon_key_t key); -bool kon_isKeyPressed(const kon_input_t *input, kon_key_t key); -bool kon_isKeyReleased(const kon_input_t *input, kon_key_t key); - -bool kon_isMouseDown(const kon_input_t *input, kon_mouseButton_t button); -bool kon_isMousePressed(const kon_input_t *input, kon_mouseButton_t button); -bool kon_isMouseReleased(const kon_input_t *input, kon_mouseButton_t button); - -void kon_getMousePos(const kon_input_t *input, int *x, int *y); - -/*** implementation ***/ - -#ifdef KONINPUT_IMPLEMENTATION - -void kon_inputBeginFrame(kon_input_t *input) { - if (!input) return; - - for (int i = 0; i < KON_KEY_COUNT; i++) { - input->downPrev[i] = input->down[i]; - } - for (int i = 0; i < 3; i++) { - input->mouseDownPrev[i] = input->mouseDown[i]; - } -} - -void kon_inputFeedEvent(kon_input_t *input, const kon_event_t *event) { - if (!input || !event) return; - - switch (event->type) { - case KON_EVENT_KEY_DOWN: - if (event->key >= 0 && event->key < KON_KEY_COUNT) - input->down[event->key] = true; - break; - case KON_EVENT_KEY_UP: - if (event->key >= 0 && event->key < KON_KEY_COUNT) - input->down[event->key] = false; - break; - case KON_EVENT_MOUSE_DOWN: - if (event->mouseButton >= 0 && event->mouseButton < 3) - input->mouseDown[event->mouseButton] = true; - input->mouseX = event->mouseX; - input->mouseY = event->mouseY; - break; - case KON_EVENT_MOUSE_UP: - if (event->mouseButton >= 0 && event->mouseButton < 3) - input->mouseDown[event->mouseButton] = false; - input->mouseX = event->mouseX; - input->mouseY = event->mouseY; - break; - case KON_EVENT_MOUSE_MOVE: - input->mouseX = event->mouseX; - input->mouseY = event->mouseY; - break; - default: - break; - } -} - -bool kon_isKeyDown(const kon_input_t *input, kon_key_t key) { - if (!input || key < 0 || key >= KON_KEY_COUNT) return false; - return input->down[key]; -} - -bool kon_isKeyPressed(const kon_input_t *input, kon_key_t key) { - if (!input || key < 0 || key >= KON_KEY_COUNT) return false; - return input->down[key] && !input->downPrev[key]; -} - -bool kon_isKeyReleased(const kon_input_t *input, kon_key_t key) { - if (!input || key < 0 || key >= KON_KEY_COUNT) return false; - return !input->down[key] && input->downPrev[key]; -} - -bool kon_isMouseDown(const kon_input_t *input, kon_mouseButton_t button) { - if (!input || button < 0 || button >= 3) return false; - return input->mouseDown[button]; -} - -bool kon_isMousePressed(const kon_input_t *input, kon_mouseButton_t button) { - if (!input || button < 0 || button >= 3) return false; - return input->mouseDown[button] && !input->mouseDownPrev[button]; -} - -bool kon_isMouseReleased(const kon_input_t *input, kon_mouseButton_t button) { - if (!input || button < 0 || button >= 3) return false; - return !input->mouseDown[button] && input->mouseDownPrev[button]; -} - -void kon_getMousePos(const kon_input_t *input, int *x, int *y) { - if (!input) return; - if (x) *x = input->mouseX; - if (y) *y = input->mouseY; -} - -#endif /* KONINPUT_IMPLEMENTATION */ - -#endif /* KONINPUT_H */ diff --git a/src/konsofren.h b/src/konsofren.h deleted file mode 100644 index b7ab033..0000000 --- a/src/konsofren.h +++ /dev/null @@ -1,1012 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * konsofren.h - * - * Copyright (c) 2026 KonAki - * - * 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. - */ - -#ifndef KONSOFREN_H -#define KONSOFREN_H - -#include <stdint.h> -#include <stdlib.h> -#include <math.h> - -#define KON_BACKGROUND_COLOR 0xFF05050A - -typedef struct kon_framebuffer { - int width, height; - uint32_t *data; -} kon_framebuffer_t; - -typedef kon_framebuffer_t kon_image; - -typedef enum kon_imageFormat { - konFormatRGBA8 = 0, - konFormatABGR8, - konFormatARGB8, - konFormatBGRA8 -} kon_imageFormat_t; - -#define KON_PI 3.14159265358979323846f -#define KON_TAU (2.0f * KON_PI) - -#define KON_DEG2RAD(d) ((float)(d) * (KON_PI / 180.0f)) -#define KON_RAD2DEG(r) ((float)(r) * (180.0f / KON_PI)) - -/* kon_vec3_t built from degrees, handy for rotations: KON_DEG3(0, 45, 0) */ -#define KON_DEG3(x, y, z) ((kon_vec3_t){KON_DEG2RAD(x), KON_DEG2RAD(y), KON_DEG2RAD(z)}) - -#define KON_VEC2(x, y) ((kon_vec2_t){(float)(x), (float)(y)}) -#define KON_VEC3(x, y, z) ((kon_vec3_t){(float)(x), (float)(y), (float)(z)}) -#define KON_VEC4(x, y, z, w) ((kon_vec4_t){(float)(x), (float)(y), (float)(z), (float)(w)}) - -#define KON_VEC3_ZERO KON_VEC3(0, 0, 0) -#define KON_VEC3_ONE KON_VEC3(1, 1, 1) -#define KON_VEC3_UP KON_VEC3(0, 1, 0) - -/* right-handed, +Y up, camera looks down -Z, column-major: m[column * 4 + row] */ - -typedef struct kon_vec2 { float x, y; } kon_vec2_t; -typedef struct kon_vec3 { float x, y, z; } kon_vec3_t; -typedef struct kon_vec4 { float x, y, z, w; } kon_vec4_t; -typedef struct kon_mat4 { float m[16]; } kon_mat4_t; - -/* game is the fixed-size framebuffer you draw into, screen always matches the window */ -typedef struct kon_display { - kon_framebuffer_t *game; - kon_framebuffer_t *screen; - int integerOnly; - - /* where the game image sits inside screen, kept up to date by the functions below */ - int viewX, viewY, viewWidth, viewHeight; - int *columnMap; -} kon_display_t; - -typedef enum kon_projection { - KON_PROJECTION_PERSPECTIVE = 0, - KON_PROJECTION_ORTHOGRAPHIC -} kon_projection_t; - -typedef struct kon_camera { - kon_vec3_t position; - kon_vec3_t target; - kon_vec3_t up; - kon_projection_t projection; - float fov; /* vertical field of view in radians, perspective only */ - float orthoSize; /* half the visible height in world units, orthographic only */ - float nearZ, farZ; -} kon_camera_t; - -/* edges holds pairs of indices into vertices */ -typedef struct kon_wireMesh { - const kon_vec3_t *vertices; - const uint16_t *edges; - int edgeCount; -} kon_wireMesh_t; - -/*** 2D declarations ***/ - -/*** framebuffer declarations ***/ - -kon_framebuffer_t *kon_createFramebuffer(int width, int height); -void kon_freeFramebuffer(kon_framebuffer_t *fb); -void kon_putPixel(kon_framebuffer_t *fb, int x, int y, uint32_t color); -void kon_clearFramebuffer(kon_framebuffer_t *fb, uint32_t color); -void kon_resizeFramebuffer(kon_framebuffer_t *fb, int width, int height); -void kon_exportPixels(kon_framebuffer_t *fb, kon_imageFormat_t format, uint8_t *out); - -/*** image declarations ***/ - -kon_image *kon_loadImage(const uint8_t *pixels, int width, int height, kon_imageFormat_t format); -void kon_freeImage(kon_image *image); - -void kon_drawImage(kon_framebuffer_t *fb, int x, int y, int width, int height, kon_image *image); - -/*** draw function declarations ***/ - -void kon_drawRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color); -void kon_fillRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color); -void kon_drawLine(kon_framebuffer_t *fb, int x0, int y0, int x1, int y1, uint32_t color); -void kon_drawCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color); -void kon_fillCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color); - -/*** math declarations ***/ - -/*** scalar declarations ***/ - -float kon_clamp(float v, float min, float max); -float kon_lerp(float a, float b, float t); - -/*** vec2 declarations ***/ - -kon_vec2_t kon_vec2Add(kon_vec2_t a, kon_vec2_t b); -kon_vec2_t kon_vec2Sub(kon_vec2_t a, kon_vec2_t b); -kon_vec2_t kon_vec2Scale(kon_vec2_t a, float s); -float kon_vec2Dot(kon_vec2_t a, kon_vec2_t b); - -/*** vec3 declarations ***/ - -kon_vec3_t kon_vec3Add(kon_vec3_t a, kon_vec3_t b); -kon_vec3_t kon_vec3Sub(kon_vec3_t a, kon_vec3_t b); -kon_vec3_t kon_vec3Scale(kon_vec3_t a, float s); -float kon_vec3Dot(kon_vec3_t a, kon_vec3_t b); -kon_vec3_t kon_vec3Cross(kon_vec3_t a, kon_vec3_t b); -float kon_vec3Length(kon_vec3_t a); -kon_vec3_t kon_vec3Normalize(kon_vec3_t a); -kon_vec3_t kon_vec3Lerp(kon_vec3_t a, kon_vec3_t b, float t); - -/*** mat4 declarations ***/ - -kon_mat4_t kon_mat4Identity(void); -kon_mat4_t kon_mat4Mul(kon_mat4_t a, kon_mat4_t b); -kon_vec4_t kon_mat4MulVec4(kon_mat4_t m, kon_vec4_t v); -kon_vec3_t kon_mat4MulPoint(kon_mat4_t m, kon_vec3_t p); - -/* like blender's local/global: the plain versions work along the model's own axes, - the World versions along the world's axes. neither rotate nor scale ever moves the model's position. - angles are euler angles in radians (x = pitch, y = yaw, z = roll), see KON_DEG3 */ -kon_mat4_t kon_mat4Translate(kon_mat4_t m, kon_vec3_t v); -kon_mat4_t kon_mat4Rotate(kon_mat4_t m, kon_vec3_t angles); -kon_mat4_t kon_mat4Scale(kon_mat4_t m, kon_vec3_t v); -kon_mat4_t kon_mat4TranslateWorld(kon_mat4_t m, kon_vec3_t v); -kon_mat4_t kon_mat4RotateWorld(kon_mat4_t m, kon_vec3_t angles); -kon_mat4_t kon_mat4ScaleWorld(kon_mat4_t m, kon_vec3_t v); - -kon_mat4_t kon_mat4FromTranslation(kon_vec3_t v); -kon_mat4_t kon_mat4FromRotation(kon_vec3_t angles); -kon_mat4_t kon_mat4FromAxisAngle(kon_vec3_t axis, float angle); -kon_mat4_t kon_mat4FromScale(kon_vec3_t v); -kon_mat4_t kon_mat4FromTransform(kon_vec3_t position, kon_vec3_t angles, kon_vec3_t scale); - -kon_mat4_t kon_mat4LookAt(kon_vec3_t eye, kon_vec3_t target, kon_vec3_t up); -kon_mat4_t kon_mat4Perspective(float fovY, float aspect, float nearZ, float farZ); -kon_mat4_t kon_mat4Ortho(float left, float right, float bottom, float top, float nearZ, float farZ); - -/*** display declarations ***/ - - -kon_display_t *kon_createDisplay(int gameWidth, int gameHeight, int windowWidth, int windowHeight); -void kon_freeDisplay(kon_display_t *display); - -void kon_setGameResolution(kon_display_t *display, int gameWidth, int gameHeight); -void kon_setIntegerScaling(kon_display_t *display, int integerOnly); -void kon_resizeDisplay(kon_display_t *display, int windowWidth, int windowHeight); - -/* scales game into screen, then blit display->screen to the window */ -void kon_presentDisplay(kon_display_t *display); - -/* returns 0 if the window position is on a bar, outside the game image */ -int kon_windowToGame(const kon_display_t *display, int windowX, int windowY, int *gameX, int *gameY); - -/*** 3D declarations ***/ - -/*** camera declarations ***/ - -kon_camera_t kon_cameraDefault(void); -kon_mat4_t kon_cameraView(const kon_camera_t *camera); -kon_mat4_t kon_cameraProjection(const kon_camera_t *camera, int width, int height); -kon_mat4_t kon_cameraViewProjection(const kon_camera_t *camera, int width, int height); - -/*** 3D draw declarations ***/ - -/* returns 0 if the point is behind the camera */ -int kon_worldToScreen(const kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vec3_t point, kon_vec2_t *out); - -void kon_drawLine3D(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vec3_t a, kon_vec3_t b, uint32_t color); -void kon_drawWireMesh(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_mat4_t model, const kon_wireMesh_t *mesh, uint32_t color); - -/*** implementation ***/ - -#ifdef KONSOFREN_IMPLEMENTATION - - -/*** private helper ***/ - -static inline uint32_t kon_blendColor(uint32_t dst, uint32_t src) { - - uint8_t src_a = (uint8_t)((src >> 24) & 0xFF); - uint8_t src_r = (uint8_t)((src >> 16) & 0xFF); - uint8_t src_g = (uint8_t)((src >> 8) & 0xFF); - uint8_t src_b = (uint8_t)((src >> 0) & 0xFF); - - if (src_a == 0xFF) { - return src; - } - - uint8_t dst_a = (uint8_t)((dst >> 24) & 0xFF); - uint8_t dst_r = (uint8_t)((dst >> 16) & 0xFF); - uint8_t dst_g = (uint8_t)((dst >> 8) & 0xFF); - uint8_t dst_b = (uint8_t)((dst >> 0) & 0xFF); - - uint8_t inv_a = (uint8_t)(255 - src_a); - - uint8_t out_r = (uint8_t)((src_r * src_a + dst_r * inv_a) / 255); - uint8_t out_g = (uint8_t)((src_g * src_a + dst_g * inv_a) / 255); - uint8_t out_b = (uint8_t)((src_b * src_a + dst_b * inv_a) / 255); - - return ((uint32_t)dst_a << 24) | ((uint32_t)out_r << 16) | ((uint32_t)out_g << 8) | (uint32_t)out_b; -} - -/* Cohen-Sutherland: clips the line to the framebuffer, returns 0 if nothing is visible */ -static int kon_outCode_(double x, double y, int width, int height) { - int code = 0; - if (x < 0) code |= 1; else if (x > width - 1) code |= 2; - if (y < 0) code |= 4; else if (y > height - 1) code |= 8; - return code; -} - -static int kon_clipLine_(int width, int height, int *x0, int *y0, int *x1, int *y1) { - double ax = *x0, ay = *y0, bx = *x1, by = *y1; - double maxX = width - 1, maxY = height - 1; - int ca = kon_outCode_(ax, ay, width, height); - int cb = kon_outCode_(bx, by, width, height); - - while (ca | cb) { - if (ca & cb) return 0; - - int out = ca ? ca : cb; - double x, y; - - if (out & 8) { - x = ax + (bx - ax) * (maxY - ay) / (by - ay); - y = maxY; - } else if (out & 4) { - x = ax + (bx - ax) * (0 - ay) / (by - ay); - y = 0; - } else if (out & 2) { - y = ay + (by - ay) * (maxX - ax) / (bx - ax); - x = maxX; - } else { - y = ay + (by - ay) * (0 - ax) / (bx - ax); - x = 0; - } - - if (out == ca) { - ax = x; ay = y; - ca = kon_outCode_(ax, ay, width, height); - } else { - bx = x; by = y; - cb = kon_outCode_(bx, by, width, height); - } - } - - *x0 = (int)(ax + 0.5); *y0 = (int)(ay + 0.5); - *x1 = (int)(bx + 0.5); *y1 = (int)(by + 0.5); - return 1; -} - -/*** framebuffer implementation ***/ - -kon_framebuffer_t *kon_createFramebuffer(int width, int height) { - kon_framebuffer_t *fb = malloc(sizeof(kon_framebuffer_t)); - if (!fb) return NULL; - - fb->data = calloc((size_t)width * (size_t)height, sizeof(uint32_t)); - if (!fb->data) { - free(fb); - return NULL; - } - - fb->width = width; - fb->height = height; - return fb; -} - -void kon_freeFramebuffer(kon_framebuffer_t *fb) { - if (!fb) return; - - free(fb->data); - free(fb); -} - -void kon_putPixel(kon_framebuffer_t *fb, int x, int y, uint32_t color) { - if (!fb) return; - - if (x < 0 || x >= fb->width || y < 0 || y >= fb->height) return; - - uint32_t dst = fb->data[y * fb->width + x]; - fb->data[y * fb->width + x] = kon_blendColor(dst, color); -} - -void kon_clearFramebuffer(kon_framebuffer_t *fb, uint32_t color) { - if (!fb) return; - - for (int i = 0; i < fb->width * fb->height; i++) { - /* did it directly to not check overhead because of the if in bounds check */ - fb->data[i] = color; - } -} - -void kon_resizeFramebuffer(kon_framebuffer_t *fb, int width, int height) { - if (!fb) return; - - uint32_t *tmp = realloc(fb->data, (size_t)width * (size_t)height * sizeof(uint32_t)); - if (!tmp) return; - - fb->data = tmp; - fb->width = width; - fb->height = height; - - kon_clearFramebuffer(fb, KON_BACKGROUND_COLOR); -} - -void kon_exportPixels(kon_framebuffer_t *fb, kon_imageFormat_t format, uint8_t *out) { - if (!fb || !out) return; - - int fbSize = fb->width * fb->height; - - for (int i = 0; i < fbSize; i++) { - uint32_t color = fb->data[i]; - - uint8_t a = (color >> 24) & 0xFF; - uint8_t r = (color >> 16) & 0xFF; - uint8_t g = (color >> 8) & 0xFF; - uint8_t b = (color >> 0) & 0xFF; - - switch (format) { - case konFormatRGBA8: - out[i * 4 + 0] = r; - out[i * 4 + 1] = g; - out[i * 4 + 2] = b; - out[i * 4 + 3] = a; - break; - case konFormatABGR8: - out[i * 4 + 0] = a; - out[i * 4 + 1] = b; - out[i * 4 + 2] = g; - out[i * 4 + 3] = r; - break; - case konFormatARGB8: - out[i * 4 + 0] = a; - out[i * 4 + 1] = r; - out[i * 4 + 2] = g; - out[i * 4 + 3] = b; - break; - case konFormatBGRA8: - out[i * 4 + 0] = b; - out[i * 4 + 1] = g; - out[i * 4 + 2] = r; - out[i * 4 + 3] = a; - break; - default: - return; - } - } -} - -/*** draw functions implementation ***/ - -void kon_drawRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color) { - if (!fb) return; - - kon_drawLine(fb, x, y, x + width, y, color); - kon_drawLine(fb, x, y + height, x + width, y + height, color); - kon_drawLine(fb, x, y, x, y + height, color); - kon_drawLine(fb, x + width, y, x + width, y + height, color); -} - -void kon_fillRectangle(kon_framebuffer_t *fb, int x, int y, int width, int height, uint32_t color) { - if (!fb) return; - - if (width < 0 || height < 0) return; - - if (x < 0) { - width += x; - x = 0; - } - if (y < 0) { - height += y; - y = 0; - } - - if (x + width > fb->width ) width = fb->width - x; - if (y + height > fb->height) height = fb->height - y; - - for (int offset_y = 0; offset_y < height; offset_y++) { - for (int offset_x = 0; offset_x < width; offset_x++) { - uint32_t dst = fb->data[(offset_y + y) * fb->width + (offset_x + x)]; - fb->data[(y + offset_y) * fb->width + (x + offset_x)] = kon_blendColor(dst, color); - - } - } -} - -void kon_drawLine(kon_framebuffer_t *fb, int x0, int y0, int x1, int y1, uint32_t color) { - if (!fb) return; - - if (!kon_clipLine_(fb->width, fb->height, &x0, &y0, &x1, &y1)) return; - - int dx = abs(x1 - x0); - int dy = abs(y1 - y0); - - int sx = (x0 < x1) ? 1 : -1; - int sy = (y0 < y1) ? 1 : -1; - - int err = dx - dy; - - for (;;) { - kon_putPixel(fb, x0, y0, color); - - if (x0 == x1 && y0 == y1) break; - - int e2 = 2 * err; - - if (e2 > -dy) { - err -= dy; - x0 += sx; - } - - if (e2 < dx) { - err += dx; - y0 += sy; - } - } -} - -void kon_drawCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color) { - if (!fb) return; - if (radius <= 0) return; - - int x = radius; - int y = 0; - int err = 1 - radius; - - while (x >= y) { - kon_putPixel(fb, center_x + x, center_y + y, color); - kon_putPixel(fb, center_x + y, center_y + x, color); - kon_putPixel(fb, center_x - y, center_y + x, color); - kon_putPixel(fb, center_x - x, center_y + y, color); - kon_putPixel(fb, center_x - x, center_y - y, color); - kon_putPixel(fb, center_x - y, center_y - x, color); - kon_putPixel(fb, center_x + y, center_y - x, color); - kon_putPixel(fb, center_x + x, center_y - y, color); - - y += 1; - - if (err < 0) { - err += 2 * y + 1; - } else { - x -= 1; - err += 2 * (y - x) + 1; - } - } -} - -void kon_fillCircle(kon_framebuffer_t *fb, int center_x, int center_y, int radius, uint32_t color) { - if (!fb) return; - if (radius <= 0) return; - - int x = radius; - int y = 0; - int err = 1 - radius; - - while (x >= y) { - kon_drawLine(fb, center_x - x, center_y + y, center_x + x, center_y + y, color); - kon_drawLine(fb, center_x - x, center_y - y, center_x + x, center_y - y, color); - kon_drawLine(fb, center_x - y, center_y + x, center_x + y, center_y + x, color); - kon_drawLine(fb, center_x - y, center_y - x, center_x + y, center_y - x, color); - - y += 1; - - if (err < 0) { - err += 2 * y + 1; - } else { - x -= 1; - err += 2 * (y - x) + 1; - } - } -} - -/*** image implementation ***/ - -kon_image *kon_loadImage(const uint8_t *pixels, int width, int height, kon_imageFormat_t format) { - kon_image *image = malloc(sizeof(kon_image)); - if (!image) return NULL; - - image->data = malloc((size_t)width * (size_t)height * sizeof(uint32_t)); - if (!image->data) { - free(image); - return NULL; - } - - image->width = width; - image->height = height; - - for (int i = 0; i < width * height; i++) { - uint8_t r, g, b, a; - - switch(format) { - case konFormatRGBA8: - r = pixels[i * 4 + 0]; - g = pixels[i * 4 + 1]; - b = pixels[i * 4 + 2]; - a = pixels[i * 4 + 3]; - break; - case konFormatABGR8: - a = pixels[i * 4 + 0]; - b = pixels[i * 4 + 1]; - g = pixels[i * 4 + 2]; - r = pixels[i * 4 + 3]; - break; - case konFormatARGB8: - a = pixels[i * 4 + 0]; - r = pixels[i * 4 + 1]; - g = pixels[i * 4 + 2]; - b = pixels[i * 4 + 3]; - break; - case konFormatBGRA8: - b = pixels[i * 4 + 0]; - g = pixels[i * 4 + 1]; - r = pixels[i * 4 + 2]; - a = pixels[i * 4 + 3]; - break; - default: - free(image->data); - free(image); - return NULL; - } - - image->data[i] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b; - } - - return image; -} -void kon_freeImage(kon_image *image) { - kon_freeFramebuffer(image); -} - -void kon_drawImage(kon_framebuffer_t *fb, int x, int y, int width, int height, kon_image *image) { - if (!fb || !image) return; - - /* just trying to not draw when it's unnecessary */ - if (image->width <= 0 || image->height <= 0) return; - if (width <= 0 || height <= 0) return; - if (x >= fb->width || y >= fb->height) return; - - /* using this loop to make sure we only the parts of the image that are on screen */ - for (int iy = 0; iy < height; iy++) { - for (int ix = 0; ix < width; ix++) { - int src_x = ix * image->width / width; - int src_y = iy * image->height / height; - - uint32_t color = image->data[src_y * image->width + src_x]; - kon_putPixel(fb, x+ix, y+iy, color); - } - } -} - -/*** math implementation ***/ - - -/*** scalar ***/ - -float kon_clamp(float v, float min, float max) { return v < min ? min : (v > max ? max : v); } -float kon_lerp(float a, float b, float t) { return a + (b - a) * t; } - -/*** vec2 ***/ - -kon_vec2_t kon_vec2Add(kon_vec2_t a, kon_vec2_t b) { return (kon_vec2_t){a.x + b.x, a.y + b.y}; } -kon_vec2_t kon_vec2Sub(kon_vec2_t a, kon_vec2_t b) { return (kon_vec2_t){a.x - b.x, a.y - b.y}; } -kon_vec2_t kon_vec2Scale(kon_vec2_t a, float s) { return (kon_vec2_t){a.x * s, a.y * s}; } -float kon_vec2Dot(kon_vec2_t a, kon_vec2_t b) { return a.x * b.x + a.y * b.y; } - -/*** vec3 ***/ - -kon_vec3_t kon_vec3Add(kon_vec3_t a, kon_vec3_t b) { return (kon_vec3_t){a.x + b.x, a.y + b.y, a.z + b.z}; } -kon_vec3_t kon_vec3Sub(kon_vec3_t a, kon_vec3_t b) { return (kon_vec3_t){a.x - b.x, a.y - b.y, a.z - b.z}; } -kon_vec3_t kon_vec3Scale(kon_vec3_t a, float s) { return (kon_vec3_t){a.x * s, a.y * s, a.z * s}; } -float kon_vec3Dot(kon_vec3_t a, kon_vec3_t b) { return a.x * b.x + a.y * b.y + a.z * b.z; } - -kon_vec3_t kon_vec3Cross(kon_vec3_t a, kon_vec3_t b) { - return (kon_vec3_t){ - a.y * b.z - a.z * b.y, - a.z * b.x - a.x * b.z, - a.x * b.y - a.y * b.x - }; -} - -float kon_vec3Length(kon_vec3_t a) { return sqrtf(kon_vec3Dot(a, a)); } - -kon_vec3_t kon_vec3Normalize(kon_vec3_t a) { - float len = kon_vec3Length(a); - return len > 0.0f ? kon_vec3Scale(a, 1.0f / len) : a; -} - -kon_vec3_t kon_vec3Lerp(kon_vec3_t a, kon_vec3_t b, float t) { - return (kon_vec3_t){kon_lerp(a.x, b.x, t), kon_lerp(a.y, b.y, t), kon_lerp(a.z, b.z, t)}; -} - -/*** mat4 ***/ - -kon_mat4_t kon_mat4Identity(void) { - kon_mat4_t r = {{0}}; - r.m[0] = r.m[5] = r.m[10] = r.m[15] = 1.0f; - return r; -} - -/* returns a * b, b is applied first */ -kon_mat4_t kon_mat4Mul(kon_mat4_t a, kon_mat4_t b) { - kon_mat4_t r; - for (int c = 0; c < 4; c++) { - for (int row = 0; row < 4; row++) { - float sum = 0.0f; - for (int k = 0; k < 4; k++) { - sum += a.m[k * 4 + row] * b.m[c * 4 + k]; - } - r.m[c * 4 + row] = sum; - } - } - return r; -} - -kon_vec4_t kon_mat4MulVec4(kon_mat4_t m, kon_vec4_t v) { - return (kon_vec4_t){ - m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12] * v.w, - m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13] * v.w, - m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z + m.m[14] * v.w, - m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15] * v.w - }; -} - -kon_vec3_t kon_mat4MulPoint(kon_mat4_t m, kon_vec3_t p) { - kon_vec4_t r = kon_mat4MulVec4(m, (kon_vec4_t){p.x, p.y, p.z, 1.0f}); - return (kon_vec3_t){r.x, r.y, r.z}; -} - -kon_mat4_t kon_mat4FromTranslation(kon_vec3_t v) { - kon_mat4_t r = kon_mat4Identity(); - r.m[12] = v.x; - r.m[13] = v.y; - r.m[14] = v.z; - return r; -} - -kon_mat4_t kon_mat4FromScale(kon_vec3_t v) { - kon_mat4_t r = kon_mat4Identity(); - r.m[0] = v.x; - r.m[5] = v.y; - r.m[10] = v.z; - return r; -} - -kon_mat4_t kon_mat4FromAxisAngle(kon_vec3_t axis, float angle) { - kon_vec3_t a = kon_vec3Normalize(axis); - float c = cosf(angle), s = sinf(angle), t = 1.0f - c; - kon_mat4_t r = kon_mat4Identity(); - r.m[0] = t * a.x * a.x + c; - r.m[1] = t * a.x * a.y + s * a.z; - r.m[2] = t * a.x * a.z - s * a.y; - r.m[4] = t * a.x * a.y - s * a.z; - r.m[5] = t * a.y * a.y + c; - r.m[6] = t * a.y * a.z + s * a.x; - r.m[8] = t * a.x * a.z + s * a.y; - r.m[9] = t * a.y * a.z - s * a.x; - r.m[10] = t * a.z * a.z + c; - return r; -} - -/* applied in order roll (z), pitch (x), yaw (y) */ -kon_mat4_t kon_mat4FromRotation(kon_vec3_t angles) { - kon_mat4_t x = kon_mat4FromAxisAngle(KON_VEC3(1, 0, 0), angles.x); - kon_mat4_t y = kon_mat4FromAxisAngle(KON_VEC3(0, 1, 0), angles.y); - kon_mat4_t z = kon_mat4FromAxisAngle(KON_VEC3(0, 0, 1), angles.z); - return kon_mat4Mul(y, kon_mat4Mul(x, z)); -} - -kon_mat4_t kon_mat4FromTransform(kon_vec3_t position, kon_vec3_t angles, kon_vec3_t scale) { - kon_mat4_t r = kon_mat4Mul(kon_mat4FromRotation(angles), kon_mat4FromScale(scale)); - return kon_mat4Mul(kon_mat4FromTranslation(position), r); -} - -kon_mat4_t kon_mat4Translate(kon_mat4_t m, kon_vec3_t v) { return kon_mat4Mul(m, kon_mat4FromTranslation(v)); } -kon_mat4_t kon_mat4Rotate(kon_mat4_t m, kon_vec3_t angles) { return kon_mat4Mul(m, kon_mat4FromRotation(angles)); } -kon_mat4_t kon_mat4Scale(kon_mat4_t m, kon_vec3_t v) { return kon_mat4Mul(m, kon_mat4FromScale(v)); } -kon_mat4_t kon_mat4TranslateWorld(kon_mat4_t m, kon_vec3_t v) { return kon_mat4Mul(kon_mat4FromTranslation(v), m); } - -/* pre-multiplying would also swing the position around the world origin, so put it back */ -kon_mat4_t kon_mat4RotateWorld(kon_mat4_t m, kon_vec3_t angles) { - kon_mat4_t r = kon_mat4Mul(kon_mat4FromRotation(angles), m); - r.m[12] = m.m[12]; - r.m[13] = m.m[13]; - r.m[14] = m.m[14]; - return r; -} - -kon_mat4_t kon_mat4ScaleWorld(kon_mat4_t m, kon_vec3_t v) { - kon_mat4_t r = kon_mat4Mul(kon_mat4FromScale(v), m); - r.m[12] = m.m[12]; - r.m[13] = m.m[13]; - r.m[14] = m.m[14]; - return r; -} - -kon_mat4_t kon_mat4LookAt(kon_vec3_t eye, kon_vec3_t target, kon_vec3_t up) { - kon_vec3_t f = kon_vec3Normalize(kon_vec3Sub(target, eye)); - kon_vec3_t s = kon_vec3Normalize(kon_vec3Cross(f, up)); - kon_vec3_t u = kon_vec3Cross(s, f); - - kon_mat4_t r = kon_mat4Identity(); - r.m[0] = s.x; r.m[4] = s.y; r.m[8] = s.z; r.m[12] = -kon_vec3Dot(s, eye); - r.m[1] = u.x; r.m[5] = u.y; r.m[9] = u.z; r.m[13] = -kon_vec3Dot(u, eye); - r.m[2] = -f.x; r.m[6] = -f.y; r.m[10] = -f.z; r.m[14] = kon_vec3Dot(f, eye); - return r; -} - -/* fovY in radians, aspect = width / height */ -kon_mat4_t kon_mat4Perspective(float fovY, float aspect, float nearZ, float farZ) { - kon_mat4_t r = {{0}}; - float f = 1.0f / tanf(fovY * 0.5f); - r.m[0] = f / aspect; - r.m[5] = f; - r.m[10] = (farZ + nearZ) / (nearZ - farZ); - r.m[11] = -1.0f; - r.m[14] = (2.0f * farZ * nearZ) / (nearZ - farZ); - return r; -} - -kon_mat4_t kon_mat4Ortho(float left, float right, float bottom, float top, float nearZ, float farZ) { - kon_mat4_t r = kon_mat4Identity(); - r.m[0] = 2.0f / (right - left); - r.m[5] = 2.0f / (top - bottom); - r.m[10] = -2.0f / (farZ - nearZ); - r.m[12] = -(right + left) / (right - left); - r.m[13] = -(top + bottom) / (top - bottom); - r.m[14] = -(farZ + nearZ) / (farZ - nearZ); - return r; -} - - -/*** display implementation ***/ - -static void kon_updateView_(kon_display_t *display) { - int gameWidth = display->game->width, gameHeight = display->game->height; - int windowWidth = display->screen->width, windowHeight = display->screen->height; - - int scaleX = windowWidth / gameWidth; - int scaleY = windowHeight / gameHeight; - int scale = (scaleX < scaleY) ? scaleX : scaleY; - - if (display->integerOnly && scale >= 1) { - display->viewWidth = gameWidth * scale; - display->viewHeight = gameHeight * scale; - } else if ((int64_t)windowWidth * gameHeight <= (int64_t)windowHeight * gameWidth) { - /* window is narrower than the game: bars on top and bottom */ - display->viewWidth = windowWidth; - display->viewHeight = (int)((int64_t)windowWidth * gameHeight / gameWidth); - } else { - /* window is wider than the game: bars on the sides */ - display->viewHeight = windowHeight; - display->viewWidth = (int)((int64_t)windowHeight * gameWidth / gameHeight); - } - - display->viewX = (windowWidth - display->viewWidth) / 2; - display->viewY = (windowHeight - display->viewHeight) / 2; - - /* which game column each view column shows, worked out once instead of for every pixel */ - int *map = realloc(display->columnMap, (size_t)windowWidth * sizeof(int)); - if (!map) { - display->viewWidth = 0; - return; - } - display->columnMap = map; - - for (int x = 0; x < display->viewWidth; x++) { - map[x] = (int)((int64_t)x * gameWidth / display->viewWidth); - } -} - -kon_display_t *kon_createDisplay(int gameWidth, int gameHeight, int windowWidth, int windowHeight) { - if (gameWidth <= 0 || gameHeight <= 0 || windowWidth <= 0 || windowHeight <= 0) return NULL; - - kon_display_t *display = calloc(1, sizeof(kon_display_t)); - if (!display) return NULL; - - display->game = kon_createFramebuffer(gameWidth, gameHeight); - display->screen = kon_createFramebuffer(windowWidth, windowHeight); - if (!display->game || !display->screen) { - kon_freeDisplay(display); - return NULL; - } - - kon_clearFramebuffer(display->game, KON_BACKGROUND_COLOR); - kon_updateView_(display); - return display; -} - -void kon_freeDisplay(kon_display_t *display) { - if (!display) return; - - kon_freeFramebuffer(display->game); - kon_freeFramebuffer(display->screen); - free(display->columnMap); - free(display); -} - -void kon_setGameResolution(kon_display_t *display, int gameWidth, int gameHeight) { - if (!display || gameWidth <= 0 || gameHeight <= 0) return; - - kon_resizeFramebuffer(display->game, gameWidth, gameHeight); - kon_updateView_(display); -} - -void kon_setIntegerScaling(kon_display_t *display, int integerOnly) { - if (!display) return; - - display->integerOnly = integerOnly; - kon_updateView_(display); -} - -void kon_resizeDisplay(kon_display_t *display, int windowWidth, int windowHeight) { - if (!display || windowWidth <= 0 || windowHeight <= 0) return; - - kon_resizeFramebuffer(display->screen, windowWidth, windowHeight); - kon_updateView_(display); -} - -void kon_presentDisplay(kon_display_t *display) { - if (!display) return; - - const uint32_t barColor = 0xFF000000; - kon_framebuffer_t *game = display->game; - kon_framebuffer_t *screen = display->screen; - int rightX = display->viewX + display->viewWidth; - - for (int y = 0; y < screen->height; y++) { - uint32_t *row = &screen->data[(size_t)y * (size_t)screen->width]; - - if (display->viewWidth <= 0 || y < display->viewY || y >= display->viewY + display->viewHeight) { - for (int x = 0; x < screen->width; x++) row[x] = barColor; - continue; - } - - int srcY = (int)((int64_t)(y - display->viewY) * game->height / display->viewHeight); - const uint32_t *srcRow = &game->data[(size_t)srcY * (size_t)game->width]; - - for (int x = 0; x < display->viewX; x++) row[x] = barColor; - for (int x = 0; x < display->viewWidth; x++) row[display->viewX + x] = srcRow[display->columnMap[x]]; - for (int x = rightX; x < screen->width; x++) row[x] = barColor; - } -} - -int kon_windowToGame(const kon_display_t *display, int windowX, int windowY, int *gameX, int *gameY) { - if (!display || display->viewWidth <= 0 || display->viewHeight <= 0) return 0; - - int localX = windowX - display->viewX; - int localY = windowY - display->viewY; - if (localX < 0 || localX >= display->viewWidth || localY < 0 || localY >= display->viewHeight) return 0; - - if (gameX) *gameX = (int)((int64_t)localX * display->game->width / display->viewWidth); - if (gameY) *gameY = (int)((int64_t)localY * display->game->height / display->viewHeight); - return 1; -} - -/*** 3D implementation ***/ - - -/*** private helper ***/ - -static kon_vec2_t kon_clipToScreen_(const kon_framebuffer_t *fb, kon_vec4_t clip) { - /* clamped so the later float to int conversion can't overflow */ - float ndcX = kon_clamp(clip.x / clip.w, -1.0e5f, 1.0e5f); - float ndcY = kon_clamp(clip.y / clip.w, -1.0e5f, 1.0e5f); - - /* screen +Y points down, NDC +Y points up */ - return KON_VEC2((ndcX + 1.0f) * 0.5f * (float)fb->width, (1.0f - ndcY) * 0.5f * (float)fb->height); -} - -/*** camera implementation ***/ - -kon_camera_t kon_cameraDefault(void) { - kon_camera_t camera; - camera.position = KON_VEC3(0, 0, 5); - camera.target = KON_VEC3_ZERO; - camera.up = KON_VEC3_UP; - camera.projection = KON_PROJECTION_PERSPECTIVE; - camera.fov = KON_DEG2RAD(60); - camera.orthoSize = 5.0f; - camera.nearZ = 0.1f; - camera.farZ = 100.0f; - return camera; -} - -kon_mat4_t kon_cameraView(const kon_camera_t *camera) { - return kon_mat4LookAt(camera->position, camera->target, camera->up); -} - -kon_mat4_t kon_cameraProjection(const kon_camera_t *camera, int width, int height) { - float aspect = (height > 0) ? (float)width / (float)height : 1.0f; - - if (camera->projection == KON_PROJECTION_ORTHOGRAPHIC) { - float halfH = camera->orthoSize; - float halfW = halfH * aspect; - return kon_mat4Ortho(-halfW, halfW, -halfH, halfH, camera->nearZ, camera->farZ); - } - - return kon_mat4Perspective(camera->fov, aspect, camera->nearZ, camera->farZ); -} - -kon_mat4_t kon_cameraViewProjection(const kon_camera_t *camera, int width, int height) { - return kon_mat4Mul(kon_cameraProjection(camera, width, height), kon_cameraView(camera)); -} - -/*** 3D draw implementation ***/ - -int kon_worldToScreen(const kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vec3_t point, kon_vec2_t *out) { - if (!fb || !out) return 0; - - kon_vec4_t clip = kon_mat4MulVec4(viewProjection, KON_VEC4(point.x, point.y, point.z, 1)); - - /* in front of the near plane when z >= -w */ - if (clip.z < -clip.w || clip.w <= 0.0f) return 0; - - *out = kon_clipToScreen_(fb, clip); - return 1; -} - -void kon_drawLine3D(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vec3_t a, kon_vec3_t b, uint32_t color) { - if (!fb) return; - - kon_vec4_t ca = kon_mat4MulVec4(viewProjection, KON_VEC4(a.x, a.y, a.z, 1)); - kon_vec4_t cb = kon_mat4MulVec4(viewProjection, KON_VEC4(b.x, b.y, b.z, 1)); - - /* clip against the near plane (z + w >= 0) before dividing, so points behind the camera never get projected */ - float da = ca.z + ca.w; - float db = cb.z + cb.w; - - if (da < 0.0f && db < 0.0f) return; - - if (da < 0.0f || db < 0.0f) { - float t = da / (da - db); - kon_vec4_t hit = KON_VEC4( - ca.x + (cb.x - ca.x) * t, - ca.y + (cb.y - ca.y) * t, - ca.z + (cb.z - ca.z) * t, - ca.w + (cb.w - ca.w) * t - ); - - if (da < 0.0f) ca = hit; - else cb = hit; - } - - if (ca.w <= 0.0f || cb.w <= 0.0f) return; - - kon_vec2_t sa = kon_clipToScreen_(fb, ca); - kon_vec2_t sb = kon_clipToScreen_(fb, cb); - - kon_drawLine(fb, (int)sa.x, (int)sa.y, (int)sb.x, (int)sb.y, color); -} - -void kon_drawWireMesh(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_mat4_t model, const kon_wireMesh_t *mesh, uint32_t color) { - if (!fb || !mesh) return; - - kon_mat4_t mvp = kon_mat4Mul(viewProjection, model); - - for (int i = 0; i < mesh->edgeCount; i++) { - kon_vec3_t a = mesh->vertices[mesh->edges[i * 2 + 0]]; - kon_vec3_t b = mesh->vertices[mesh->edges[i * 2 + 1]]; - kon_drawLine3D(fb, mvp, a, b, color); - } -} - -#endif /* end of KONSOFREN_IMPLEMENTATION */ - -#endif diff --git a/src/kontime.h b/src/kontime.h deleted file mode 100644 index da46ddd..0000000 --- a/src/kontime.h +++ /dev/null @@ -1,85 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * kontime.h - * - * Copyright (c) 2026 KonAki - * - * 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. - */ - -#ifndef KONTIME_H -#define KONTIME_H - -/*** definition ***/ - -typedef double kon_time; - -kon_time kon_getTime(void); - -void kon_sleep(kon_time seconds); - -/*** implementation ***/ - -#ifdef KONTIME_IMPLEMENTATION - -#if defined(__linux__) || defined(__unix__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - -#define _POSIX_C_SOURCE 200809L -#include <time.h> - -kon_time kon_getTime(void) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - - return (kon_time)ts.tv_sec + (kon_time)ts.tv_nsec / 1e9; -} - -void kon_sleep(kon_time seconds) { - struct timespec ts; - ts.tv_sec = (time_t)seconds; - ts.tv_nsec = (long)((seconds - ts.tv_sec) * 1e9); - - nanosleep(&ts, NULL); -} - -#elif defined(_WIN32) - -#include <windows.h> - -kon_time kon_getTime(void) { - LARGE_INTEGER freq, now; - QueryPerformanceFrequency(&freq); - QueryPerformanceCounter(&now); - - return (kon_time)now.QuadPart / (kon_time)freq.QuadPart; -} - -void kon_sleep(kon_time seconds) { - Sleep((DWORD)(seconds * 1000.0)); -} - -#else -#error "kontime.h: unsupported platform!" - -#endif - -#endif /* end of KONTIME_IMPLEMENTATION */ - - -#endif diff --git a/src/kontoml.h b/src/kontoml.h deleted file mode 100644 index 88a7e68..0000000 --- a/src/kontoml.h +++ /dev/null @@ -1,649 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * kontoml.h - * - * Copyright (c) 2026 KonAki - * - * 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. - */ - -#ifndef KONTOML_H -#define KONTOML_H - -#include <stdint.h> -#include <stdlib.h> -#include <stdbool.h> - -typedef enum kon_tomlType { - konTomlString, - konTomlInt, - konTomlFloat, - konTomlBool, - konTomlArray -} kon_tomlType_t; - -typedef struct kon_tomlEntry { - char *section; - char *key; - kon_tomlType_t type; - char *str_value; - long long int_value; - double float_value; - bool bool_value; - - kon_tomlType_t array_type; - int array_count; - char **array_str; - long long *array_int; - double *array_float; - bool *array_bool; -} kon_tomlEntry_t; - -typedef struct kon_toml { - kon_tomlEntry_t *entries; - int count; - int capacity; -} kon_toml_t; - -kon_toml_t *kon_parseToml(const char *text); -kon_toml_t *kon_parseTomlFile(const char *path); -void kon_freeToml(kon_toml_t *toml); - -const char *kon_tomlGetString(kon_toml_t *toml, const char *section, const char *key, const char *def); -long long kon_tomlGetInt(kon_toml_t *toml, const char *section, const char *key, long long def); -double kon_tomlGetFloat(kon_toml_t *toml, const char *section, const char *key, double def); -bool kon_tomlGetBool(kon_toml_t *toml, const char *section, const char *key, bool def); - -const char *const *kon_tomlGetStringArray(kon_toml_t *toml, const char *section, const char *key, int *out_count); -const long long *kon_tomlGetIntArray(kon_toml_t *toml, const char *section, const char *key, int *out_count); -const double *kon_tomlGetFloatArray(kon_toml_t *toml, const char *section, const char *key, int *out_count); -const bool *kon_tomlGetBoolArray(kon_toml_t *toml, const char *section, const char *key, int *out_count); - -/*** implementation ***/ - -#ifdef KONTOML_IMPLEMENTATION - -#include <string.h> -#include <ctype.h> -#include <stdio.h> - -static char *kon__tomlStrdup(const char *s) { - size_t len = strlen(s) + 1; - char *out = malloc(len); - if (!out) return NULL; - memcpy(out, s, len); - return out; -} - -static char *kon__tomlStrndup(const char *s, size_t n) { - char *out = malloc(n + 1); - if (!out) return NULL; - memcpy(out, s, n); - out[n] = '\0'; - return out; -} - -static char *kon__tomlTrim(char *s) { - char *end; - - while (*s == ' ' || *s == '\t') s++; - if (*s == '\0') return s; - - end = s + strlen(s) - 1; - while (end > s && (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n')) { - *end = '\0'; - end--; - } - - return s; -} - -static kon_tomlEntry_t *kon__tomlPushEntry(kon_toml_t *toml) { - if (toml->count == toml->capacity) { - int newCap = toml->capacity ? toml->capacity * 2 : 16; - kon_tomlEntry_t *tmp = realloc(toml->entries, (size_t)newCap * sizeof(kon_tomlEntry_t)); - if (!tmp) return NULL; - - toml->entries = tmp; - toml->capacity = newCap; - } - - kon_tomlEntry_t *entry = &toml->entries[toml->count]; - memset(entry, 0, sizeof(kon_tomlEntry_t)); - toml->count++; - - return entry; -} - -static int kon__tomlParseString(const char **p, char **out) { - const char *s = *p; - char quote = *s; - int literal = (quote == '\''); - s++; - - char *buf = malloc(strlen(s) + 1); - if (!buf) return -1; - - size_t bi = 0; - - while (*s && *s != quote) { - if (!literal && *s == '\\') { - s++; - switch (*s) { - case 'n': - buf[bi++] = '\n'; - break; - case 't': - buf[bi++] = '\t'; - break; - case 'r': - buf[bi++] = '\r'; - break; - case '"': - buf[bi++] = '"'; - break; - case '\\': - buf[bi++] = '\\'; - break; - case '\0': - free(buf); - return -1; - default: - buf[bi++] = *s; - break; - } - s++; - } else { - buf[bi++] = *s; - s++; - } - } - - if (*s != quote) { - free(buf); - return -1; - } - s++; - - buf[bi] = '\0'; - *out = buf; - *p = s; - - return 0; -} - -static char *kon__tomlStripUnderscores(const char *s) { - char *out = malloc(strlen(s) + 1); - if (!out) return NULL; - - size_t oi = 0; - for (size_t i = 0; s[i]; i++) { - if (s[i] != '_') out[oi++] = s[i]; - } - out[oi] = '\0'; - - return out; -} - -static char **kon__tomlSplitArrayElements(const char *content, int *outCount) { - int cap = 8; - int count = 0; - char **elems = malloc((size_t)cap * sizeof(char *)); - if (!elems) return NULL; - - const char *start = content; - const char *p = content; - char inQuote = 0; - - while (*p) { - if (inQuote) { - if (inQuote == '"' && *p == '\\' && *(p + 1) != '\0') { - p += 2; - continue; - } - if (*p == inQuote) inQuote = 0; - p++; - continue; - } - - if (*p == '"' || *p == '\'') { - inQuote = *p; - p++; - continue; - } - - if (*p == ',') { - if (count == cap) { - cap *= 2; - char **tmp = realloc(elems, (size_t)cap * sizeof(char *)); - if (!tmp) { free(elems); return NULL; } - elems = tmp; - } - elems[count++] = kon__tomlStrndup(start, (size_t)(p - start)); - p++; - start = p; - continue; - } - - p++; - } - - { - char *raw = kon__tomlStrndup(start, (size_t)(p - start)); - char *checkTrim = raw ? kon__tomlTrim(raw) : NULL; - - if (raw && *checkTrim != '\0') { - if (count == cap) { - cap += 1; - char **tmp = realloc(elems, (size_t)cap * sizeof(char *)); - if (!tmp) { free(raw); free(elems); return NULL; } - elems = tmp; - } - elems[count++] = raw; - } else { - free(raw); - } - } - - *outCount = count; - return elems; -} - -static int kon__tomlParseArrayValue(kon_tomlEntry_t *entry, const char *valueStr) { - const char *open = strchr(valueStr, '['); - const char *close = strchr(valueStr, ']'); - if (!open || !close || close <= open) return -1; - - char *content = kon__tomlStrndup(open + 1, (size_t)(close - open - 1)); - if (!content) return -1; - - int rawCount = 0; - char **rawElems = kon__tomlSplitArrayElements(content, &rawCount); - free(content); - if (!rawElems) return -1; - - if (rawCount == 0) { - free(rawElems); - entry->type = konTomlArray; - entry->array_type = konTomlInt; - entry->array_count = 0; - return 0; - } - - kon_tomlType_t elemType = konTomlInt; - char **strArr = NULL; - long long *intArr = NULL; - double *floatArr = NULL; - bool *boolArr = NULL; - int ok = 1; - - for (int i = 0; i < rawCount && ok; i++) { - char *trimmed = kon__tomlTrim(rawElems[i]); - - kon_tomlType_t thisType; - char *decodedStr = NULL; - long long thisInt = 0; - double thisFloat = 0.0; - bool thisBool = false; - - if (*trimmed == '"' || *trimmed == '\'') { - const char *vp = trimmed; - thisType = konTomlString; - if (kon__tomlParseString(&vp, &decodedStr) != 0) { ok = 0; break; } - } else if (strcmp(trimmed, "true") == 0) { - thisType = konTomlBool; - thisBool = true; - } else if (strcmp(trimmed, "false") == 0) { - thisType = konTomlBool; - thisBool = false; - } else { - char *clean = kon__tomlStripUnderscores(trimmed); - if (!clean) { ok = 0; break; } - - int isFloat = (strchr(clean, '.') || strchr(clean, 'e') || strchr(clean, 'E')); - if (isFloat) { - thisType = konTomlFloat; - thisFloat = strtod(clean, NULL); - } else { - thisType = konTomlInt; - thisInt = strtoll(clean, NULL, 10); - } - free(clean); - } - - if (i == 0) { - elemType = thisType; - switch (elemType) { - case konTomlString: - strArr = malloc((size_t)rawCount * sizeof(char *)); - break; - case konTomlInt: - intArr = malloc((size_t)rawCount * sizeof(long long)); - break; - case konTomlFloat: - floatArr = malloc((size_t)rawCount * sizeof(double)); - break; - case konTomlBool: - boolArr = malloc((size_t)rawCount * sizeof(bool)); - break; - default: - break; - } - } else if (thisType != elemType) { - free(decodedStr); - ok = 0; - break; - } - - switch (elemType) { - case konTomlString: - strArr[i] = decodedStr; - break; - case konTomlInt: - intArr[i] = thisInt; - break; - case konTomlFloat: - floatArr[i] = thisFloat; - break; - case konTomlBool: - boolArr[i] = thisBool; - break; - default: - break; - } - } - - for (int i = 0; i < rawCount; i++) free(rawElems[i]); - free(rawElems); - - if (!ok) { - if (strArr) { - for (int i = 0; i < rawCount; i++) - free(strArr[i]); - - free(strArr); - } - free(intArr); - free(floatArr); - free(boolArr); - return -1; - } - - entry->type = konTomlArray; - entry->array_type = elemType; - entry->array_count = rawCount; - entry->array_str = strArr; - entry->array_int = intArr; - entry->array_float = floatArr; - entry->array_bool = boolArr; - - return 0; -} - -static int kon__tomlParseLine(kon_toml_t *toml, char *line, char **currentSection) { - char *trimmed = kon__tomlTrim(line); - - if (*trimmed == '\0' || *trimmed == '#') return 0; - - if (*trimmed == '[') { - char *close = strchr(trimmed, ']'); - if (!close) return -1; - - free(*currentSection); - *currentSection = kon__tomlStrndup(trimmed + 1, (size_t)(close - trimmed - 1)); - - return 0; - } - - char *eq = strchr(trimmed, '='); - if (!eq) return -1; - - *eq = '\0'; - char *key = kon__tomlTrim(trimmed); - char *valueStr = kon__tomlTrim(eq + 1); - - if (*valueStr != '"' && *valueStr != '\'') { - char *hash = strchr(valueStr, '#'); - if (hash) { - *hash = '\0'; - valueStr = kon__tomlTrim(valueStr); - } - } - - if (*key == '\0' || *valueStr == '\0') return -1; - - kon_tomlEntry_t *entry = kon__tomlPushEntry(toml); - if (!entry) return -1; - - entry->section = *currentSection ? kon__tomlStrdup(*currentSection) : kon__tomlStrdup(""); - entry->key = kon__tomlStrdup(key); - - if (*valueStr == '[') { - if (kon__tomlParseArrayValue(entry, valueStr) != 0) return -1; - } else if (*valueStr == '"' || *valueStr == '\'') { - const char *vp = valueStr; - char *decoded = NULL; - - if (kon__tomlParseString(&vp, &decoded) != 0) return -1; - - entry->type = konTomlString; - entry->str_value = decoded; - } else if (strcmp(valueStr, "true") == 0) { - entry->type = konTomlBool; - entry->bool_value = true; - } else if (strcmp(valueStr, "false") == 0) { - entry->type = konTomlBool; - entry->bool_value = false; - } else { - char *clean = kon__tomlStripUnderscores(valueStr); - if (!clean) return -1; - - int isFloat = (strchr(clean, '.') || strchr(clean, 'e') || strchr(clean, 'E')); - - if (isFloat) { - entry->type = konTomlFloat; - entry->float_value = strtod(clean, NULL); - } else { - entry->type = konTomlInt; - entry->int_value = strtoll(clean, NULL, 10); - } - - free(clean); - } - - return 0; -} - -kon_toml_t *kon_parseToml(const char *text) { - if (!text) return NULL; - - kon_toml_t *toml = malloc(sizeof(kon_toml_t)); - if (!toml) return NULL; - - toml->entries = NULL; - toml->count = 0; - toml->capacity = 0; - - char *currentSection = NULL; - - const char *lineStart = text; - while (*lineStart) { - const char *lineEnd = strchr(lineStart, '\n'); - size_t len = lineEnd ? (size_t)(lineEnd - lineStart) : strlen(lineStart); - - char *line = kon__tomlStrndup(lineStart, len); - if (line) { - kon__tomlParseLine(toml, line, ¤tSection); - free(line); - } - - if (!lineEnd) break; - lineStart = lineEnd + 1; - } - - free(currentSection); - - return toml; -} - -kon_toml_t *kon_parseTomlFile(const char *path) { - FILE *f = fopen(path, "rb"); - if (!f) return NULL; - - fseek(f, 0, SEEK_END); - long size = ftell(f); - fseek(f, 0, SEEK_SET); - - if (size < 0) { - fclose(f); - return NULL; - } - - char *buf = malloc((size_t)size + 1); - if (!buf) { - fclose(f); - return NULL; - } - - size_t read = fread(buf, 1, (size_t)size, f); - buf[read] = '\0'; - fclose(f); - - kon_toml_t *toml = kon_parseToml(buf); - free(buf); - - return toml; -} - -void kon_freeToml(kon_toml_t *toml) { - if (!toml) return; - - for (int i = 0; i < toml->count; i++) { - kon_tomlEntry_t *e = &toml->entries[i]; - - free(e->section); - free(e->key); - - if (e->type == konTomlString) { - free(e->str_value); - } else if (e->type == konTomlArray) { - if (e->array_str) { - for (int j = 0; j < e->array_count; j++) free(e->array_str[j]); - free(e->array_str); - } - free(e->array_int); - free(e->array_float); - free(e->array_bool); - } - } - - free(toml->entries); - free(toml); -} - -static kon_tomlEntry_t *kon__tomlFind(kon_toml_t *toml, const char *section, const char *key) { - if (!toml) return NULL; - - const char *sec = section ? section : ""; - - for (int i = 0; i < toml->count; i++) { - kon_tomlEntry_t *e = &toml->entries[i]; - if (strcmp(e->section, sec) == 0 && strcmp(e->key, key) == 0) { - return e; - } - } - - return NULL; -} - -const char *kon_tomlGetString(kon_toml_t *toml, const char *section, const char *key, const char *def) { - kon_tomlEntry_t *e = kon__tomlFind(toml, section, key); - if (!e || e->type != konTomlString) return def; - return e->str_value; -} - -long long kon_tomlGetInt(kon_toml_t *toml, const char *section, const char *key, long long def) { - kon_tomlEntry_t *e = kon__tomlFind(toml, section, key); - if (!e) return def; - - if (e->type == konTomlInt) return e->int_value; - if (e->type == konTomlFloat) return (long long)e->float_value; - - return def; -} - -double kon_tomlGetFloat(kon_toml_t *toml, const char *section, const char *key, double def) { - kon_tomlEntry_t *e = kon__tomlFind(toml, section, key); - if (!e) return def; - - if (e->type == konTomlFloat) return e->float_value; - if (e->type == konTomlInt) return (double)e->int_value; - - return def; -} - -bool kon_tomlGetBool(kon_toml_t *toml, const char *section, const char *key, bool def) { - kon_tomlEntry_t *e = kon__tomlFind(toml, section, key); - if (!e || e->type != konTomlBool) return def; - return e->bool_value; -} - -const char *const *kon_tomlGetStringArray(kon_toml_t *toml, const char *section, const char *key, int *out_count) { - kon_tomlEntry_t *e = kon__tomlFind(toml, section, key); - if (!e || e->type != konTomlArray || e->array_type != konTomlString) { - if (out_count) *out_count = 0; - return NULL; - } - if (out_count) *out_count = e->array_count; - return (const char *const *)e->array_str; -} - -const long long *kon_tomlGetIntArray(kon_toml_t *toml, const char *section, const char *key, int *out_count) { - kon_tomlEntry_t *e = kon__tomlFind(toml, section, key); - if (!e || e->type != konTomlArray || e->array_type != konTomlInt) { - if (out_count) *out_count = 0; - return NULL; - } - if (out_count) *out_count = e->array_count; - return e->array_int; -} - -const double *kon_tomlGetFloatArray(kon_toml_t *toml, const char *section, const char *key, int *out_count) { - kon_tomlEntry_t *e = kon__tomlFind(toml, section, key); - if (!e || e->type != konTomlArray || e->array_type != konTomlFloat) { - if (out_count) *out_count = 0; - return NULL; - } - if (out_count) *out_count = e->array_count; - return e->array_float; -} - -const bool *kon_tomlGetBoolArray(kon_toml_t *toml, const char *section, const char *key, int *out_count) { - kon_tomlEntry_t *e = kon__tomlFind(toml, section, key); - if (!e || e->type != konTomlArray || e->array_type != konTomlBool) { - if (out_count) *out_count = 0; - return NULL; - } - if (out_count) *out_count = e->array_count; - return e->array_bool; -} - -#endif /* END of KONTOML_IMPLEMENTATION */ - -#endif diff --git a/src/konwinlib.h b/src/konwinlib.h deleted file mode 100644 index c50f828..0000000 --- a/src/konwinlib.h +++ /dev/null @@ -1,1357 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * konwinlib.h - * - * Copyright (c) 2026 KonAki - * - * 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. - */ - -#ifndef KONWINLIB_H -#define KONWINLIB_H - -#include <stdint.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> - -/*** definitions ***/ - -typedef struct kon_context kon_context_t; - -bool kon_init(void); -void kon_deinit(void); - -typedef struct kon_window kon_window_t; - -typedef enum kon_windowFlags { - KON_WINDOW_NONE = 0, - KON_WINDOW_TRANSPARENT = 1 << 0, - KON_WINDOW_NO_DECOR = 1 << 1, - KON_WINDOW_ALWAYS_ONTOP = 1 << 2, - KON_WINDOW_CENTER = 1 << 3, - KON_WINDOW_RESIZABLE = 1 << 4 -} kon_windowFlags_t; - -typedef enum kon_eventType { - KON_EVENT_NONE = 0, - KON_EVENT_CLOSE, - KON_EVENT_RESIZE, - KON_EVENT_KEY_DOWN, - KON_EVENT_KEY_UP, - KON_EVENT_MOUSE_DOWN, - KON_EVENT_MOUSE_UP, - KON_EVENT_MOUSE_MOVE -} kon_eventType_t; - -typedef enum kon_mouseButton { - KON_MOUSE_LEFT = 0, - KON_MOUSE_MIDDLE, - KON_MOUSE_RIGHT -} kon_mouseButton_t; - -typedef enum kon_key { - KON_KEY_UNKNOWN = 0, - - KON_KEY_A, KON_KEY_B, KON_KEY_C, KON_KEY_D, KON_KEY_E, KON_KEY_F, KON_KEY_G, - KON_KEY_H, KON_KEY_I, KON_KEY_J, KON_KEY_K, KON_KEY_L, KON_KEY_M, KON_KEY_N, KON_KEY_O, KON_KEY_P, - KON_KEY_Q, KON_KEY_R, KON_KEY_S, KON_KEY_T, KON_KEY_U, KON_KEY_V, - KON_KEY_W, KON_KEY_X, KON_KEY_Y, KON_KEY_Z, - - KON_KEY_0, KON_KEY_1, KON_KEY_2, KON_KEY_3, KON_KEY_4, KON_KEY_5, KON_KEY_6, KON_KEY_7, KON_KEY_8, KON_KEY_9, - - KON_KEY_F1, KON_KEY_F2, KON_KEY_F3, KON_KEY_F4, KON_KEY_F5, KON_KEY_F6, - KON_KEY_F7, KON_KEY_F8, KON_KEY_F9, KON_KEY_F10, KON_KEY_F11, KON_KEY_F12, - - KON_KEY_LEFT, KON_KEY_RIGHT, KON_KEY_UP, KON_KEY_DOWN, - - KON_KEY_SPACE, KON_KEY_ENTER, KON_KEY_TAB, KON_KEY_BACKSPACE, - KON_KEY_ESCAPE, KON_KEY_DELETE, - - KON_KEY_LEFT_SHIFT, KON_KEY_RIGHT_SHIFT, - KON_KEY_LEFT_CTRL, KON_KEY_RIGHT_CTRL, - KON_KEY_LEFT_ALT, KON_KEY_RIGHT_ALT, - - KON_KEY_MINUS, KON_KEY_EQUAL, - KON_KEY_LEFT_BRACKET, KON_KEY_RIGHT_BRACKET, - KON_KEY_SEMICOLON, KON_KEY_APOSTROPHE, - KON_KEY_COMMA, KON_KEY_PERIOD, KON_KEY_SLASH, KON_KEY_BACKSLASH, - KON_KEY_GRAVE, - - KON_KEY_COUNT -} kon_key_t; - -typedef struct kon_event { - kon_eventType_t type; - int width, height; /* will be used to store data for resize events */ - int key; - int mouseX, mouseY; - kon_mouseButton_t mouseButton; -} kon_event_t; - -kon_window_t *kon_createWindow(const char *title, int x, int y, int width, int height, kon_windowFlags_t flags); -void kon_destroyWindow(kon_window_t *window); - -bool kon_windowShouldClose(kon_window_t *window); - -void kon_setExitKey(kon_window_t *window, int key); - -void kon_setWindowPos(kon_window_t *window, int x, int y); -void kon_setWindowSize(kon_window_t *window, int width, int height); - -void kon_getWindowPos(kon_window_t *window, int *x, int *y); -void kon_getWindowSize(kon_window_t *window, int *width, int *height); - -int kon_pollEvent(kon_window_t *window, kon_event_t *event); -void kon_blitPixels(kon_window_t *window, const uint32_t *pixels, int width, int height); - -// added these for web dev -typedef void (*kon_mainLoopFn)(void *userData); -void kon_runMainLoop(kon_window_t *window, kon_mainLoopFn callback, void *userData); - -/*** implementation ***/ - -#ifdef KONWINLIB_IMPLEMENTATION - -static kon_context_t *kon_ctx; - -#define KON_EVENT_QUEUE_SIZE 256 - -typedef struct kon_eventQueue { - kon_event_t events[KON_EVENT_QUEUE_SIZE]; - int head, count; -} kon_eventQueue_t; - -static void kon_pushEvent_(kon_eventQueue_t *q, const kon_event_t *event) { - /* only the newest mouse move or resize matters, so overwrite instead of piling up */ - if (q->count > 0 && (event->type == KON_EVENT_MOUSE_MOVE || event->type == KON_EVENT_RESIZE)) { - kon_event_t *last = &q->events[(q->head + q->count - 1) % KON_EVENT_QUEUE_SIZE]; - if (last->type == event->type) { - *last = *event; - return; - } - } - - if (q->count == KON_EVENT_QUEUE_SIZE) return; - q->events[(q->head + q->count) % KON_EVENT_QUEUE_SIZE] = *event; - q->count++; -} - -static bool kon_popEvent_(kon_eventQueue_t *q, kon_event_t *event) { - if (q->count == 0) return false; - *event = q->events[q->head]; - q->head = (q->head + 1) % KON_EVENT_QUEUE_SIZE; - q->count--; - return true; -} - -#ifdef __EMSCRIPTEN__ - -#include <emscripten/emscripten.h> -#include <emscripten/html5.h> -#include <string.h> - -struct kon_context { - int initialized; -}; - -struct kon_window { - char canvasID[64]; - bool shouldClose; - int exitKey; - - bool hasKeyEvent; - kon_eventType_t keyEventType; - int keyEventKey; - - bool hasResizeEvent; - int resizeWidth, resizeHeight; - - bool hasMouseEvent; - kon_eventType_t mouseEventType; - int mouseEventX, mouseEventY; - kon_mouseButton_t mouseEventButton; -}; - -static kon_key_t kon_translateEmKey_(int keyCode) { - switch (keyCode) { - case 65: return KON_KEY_A; - case 66: return KON_KEY_B; - case 67: return KON_KEY_C; - case 68: return KON_KEY_D; - case 69: return KON_KEY_E; - case 70: return KON_KEY_F; - case 71: return KON_KEY_G; - case 72: return KON_KEY_H; - case 73: return KON_KEY_I; - case 74: return KON_KEY_J; - case 75: return KON_KEY_K; - case 76: return KON_KEY_L; - case 77: return KON_KEY_M; - case 78: return KON_KEY_N; - case 79: return KON_KEY_O; - case 80: return KON_KEY_P; - case 81: return KON_KEY_Q; - case 82: return KON_KEY_R; - case 83: return KON_KEY_S; - case 84: return KON_KEY_T; - case 85: return KON_KEY_U; - case 86: return KON_KEY_V; - case 87: return KON_KEY_W; - case 88: return KON_KEY_X; - case 89: return KON_KEY_Y; - case 90: return KON_KEY_Z; - - case 48: return KON_KEY_0; - case 49: return KON_KEY_1; - case 50: return KON_KEY_2; - case 51: return KON_KEY_3; - case 52: return KON_KEY_4; - case 53: return KON_KEY_5; - case 54: return KON_KEY_6; - case 55: return KON_KEY_7; - case 56: return KON_KEY_8; - case 57: return KON_KEY_9; - - case 112: return KON_KEY_F1; - case 113: return KON_KEY_F2; - case 114: return KON_KEY_F3; - case 115: return KON_KEY_F4; - case 116: return KON_KEY_F5; - case 117: return KON_KEY_F6; - case 118: return KON_KEY_F7; - case 119: return KON_KEY_F8; - case 120: return KON_KEY_F9; - case 121: return KON_KEY_F10; - case 122: return KON_KEY_F11; - case 123: return KON_KEY_F12; - - case 37: return KON_KEY_LEFT; - case 39: return KON_KEY_RIGHT; - case 38: return KON_KEY_UP; - case 40: return KON_KEY_DOWN; - - case 32: return KON_KEY_SPACE; - case 13: return KON_KEY_ENTER; - case 9: return KON_KEY_TAB; - case 8: return KON_KEY_BACKSPACE; - case 27: return KON_KEY_ESCAPE; - case 46: return KON_KEY_DELETE; - - case 16: return KON_KEY_LEFT_SHIFT; - case 17: return KON_KEY_LEFT_CTRL; - case 18: return KON_KEY_LEFT_ALT; - - case 189: return KON_KEY_MINUS; - case 187: return KON_KEY_EQUAL; - case 219: return KON_KEY_LEFT_BRACKET; - case 221: return KON_KEY_RIGHT_BRACKET; - case 186: return KON_KEY_SEMICOLON; - case 222: return KON_KEY_APOSTROPHE; - case 188: return KON_KEY_COMMA; - case 190: return KON_KEY_PERIOD; - case 191: return KON_KEY_SLASH; - case 220: return KON_KEY_BACKSLASH; - case 192: return KON_KEY_GRAVE; - - default: return KON_KEY_UNKNOWN; - } -} -bool kon_init(void) { - kon_context_t *ctx = malloc(sizeof(kon_context_t)); - if (!ctx) return false; - - ctx->initialized = 1; - kon_ctx = ctx; - return true; -} - -void kon_deinit(void) { - if (!kon_ctx) return; - - free(kon_ctx); - kon_ctx = NULL; -} - -static EM_BOOL kon_keyDownCallback_(int eventType, const EmscriptenKeyboardEvent *e, void *userData) { - (void)eventType; - kon_window_t *window = (kon_window_t *)userData; - - window->hasKeyEvent = true; - window->keyEventType = KON_EVENT_KEY_DOWN; - window->keyEventKey = kon_translateEmKey_(e->keyCode); - - if (window->exitKey != 0 && window->keyEventKey == window->exitKey) { - window->shouldClose = true; - } - - return EM_TRUE; -} - -static EM_BOOL kon_keyUpCallback_(int eventType, const EmscriptenKeyboardEvent *e, void *userData) { - (void)eventType; - kon_window_t *window = (kon_window_t *)userData; - - window->hasKeyEvent = true; - window->keyEventType = KON_EVENT_KEY_UP; - window->keyEventKey = kon_translateEmKey_(e->keyCode); - - return EM_TRUE; -} - -static EM_BOOL kon_mouseDownCallback_(int eventType, const EmscriptenMouseEvent *e, void *userData) { - (void)eventType; - kon_window_t *window = (kon_window_t *)userData; - - if (e->button > KON_MOUSE_RIGHT) return EM_FALSE; - - window->hasMouseEvent = true; - window->mouseEventType = KON_EVENT_MOUSE_DOWN; - window->mouseEventButton = (kon_windowButton_t)e->button; - window->mouseEventX = e->targetX; - window->mouseEventY = e->targetY; - - return EM_TRUE; -} - -static EM_BOOL kon_mouseUpCallback_(int eventType, const EmscriptenMouseEvent *e, void *userData) { - (void)eventType; - kon_window_t *window = (kon_window_t *)userData; - - if (e->button > KON_MOUSE_RIGHT) return EM_FALSE: - - window->hasMouseEvent = true; - window->mouseEventType = KON_EVENT_MOUSE_UP; - window->mouseEventButton = (kon_windowButton_t)e->button; - window->mouseEventX = e->targetX; - window->mouseEventY = e->targetY; - - return EM_TRUE; -} - -static EM_BOOL kon_mouseMoveCallback_(int eventType, const EmscriptenMouseEvent *e, void *userData) { - (void)eventType; - kon_window_t *window = (kon_window_t *)userData; - - window->hasMouseEvent = true; - window->mouseEventType = KON_EVENT_MOUSE_MOVE; - window->mouseEventX = e->targetX; - window->mouseEventY = e->targetY; - - return EM_TRUE; -} - -kon_window_t *kon_createWindow(const char *title, int x, int y, int width, int height, kon_windowFlags_t flags) { - (void)x; (void)y; (void)flags; - - if (!kon_ctx) { - fprintf(stderr, "kon_createWindow: error creating window, no ctx\n"); - return NULL; - } - - kon_window_t *window = malloc(sizeof(kon_window_t)); - if (!window) return NULL; - - strncpy(window->canvasID, "#canvas", sizeof(window->canvasID) - 1); - window->canvasID[sizeof(window->canvasID) - 1] = '\0'; - - window->shouldClose = false; - window->exitKey = 0; - window->hasKeyEvent = false; - window->hasResizeEvent = false; - window->hasMouseEvent = false; - - emscripten_set_canvas_element_size(window->canvasID, width, height); - - EM_ASM({ - try { document.title = UTF8ToString($0); } catch (e) {} - }, title); - - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, window, EM_FALSE, kon_keyDownCallback_); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, window, EM_FALSE, kon_keyUpCallback_); - emscripten_set_mousedown_callback(window->canvasID, window, EM_FALSE, kon_mouseDownCallback_); - emscripten_set_mouseup_callback(window->canvasID, window, EM_FALSE, kon_mouseUpCallback_); - emscripten_set_mousemove_callback(window->canvasID, window, EM_FALSE, kon_mouseMoveCallback_); - - return window; -} - -void kon_destroyWindow(kon_window_t *window) { - if (!window) return; - - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, NULL); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, NULL); - emscripten_set_mousedown_callback(window->canvasID, NULL, EM_FALSE, NULL); - emscripten_set_mouseup_callback(window->canvasID, NULL, EM_FALSE, NULL); - emscripten_set_mousemove_callback(window->canvasID, NULL, EM_FALSE, NULL); - - free(window); -} - -bool kon_windowShouldClose(kon_window_t *window) { - if (!window) return true; - return window->shouldClose; -} - -void kon_setExitKey(kon_window_t *window, int key) { - if (!window) return; - window->exitKey = key; -} - -void kon_setWindowPos(kon_window_t *window, int x, int y) { - (void)window; (void)x; (void)y; -} - -void kon_setWindowSize(kon_window_t *window, int width, int height) { - if (!window) return; - emscripten_set_canvas_element_size(window->canvasID, width, height); -} - -void kon_getWindowPos(kon_window_t *window, int *x, int *y) { - (void)window; - if (x) *x = 0; - if (y) *y = 0; -} - -void kon_getWindowSize(kon_window_t *window, int *width, int *height) { - if (!window || !width || !height) return; - - double w, h; - emscripten_get_element_css_size(window->canvasID, &w, &h); - *width = (int)w; - *height = (int)h; -} - -int kon_pollEvent(kon_window_t *window, kon_event_t *event) { - if (!window || !event) return 0; - - event->type = KON_EVENT_NONE; - - if (window->hasResizeEvent) { - event->type = KON_EVENT_RESIZE; - event->width = window->resizeWidth; - event->height = window->resizeHeight; - window->hasResizeEvent = false; - return 1; - } - - if (window->hasKeyEvent) { - event->type = window->keyEventType; - event->key = window->keyEventKey; - window->hasKeyEvent = false; - return 1; - } - - if (window->hasMouseEvent) { - event->type = window->mouseEventType; - event->mouseX = window->mouseEventX; - event->mouseY = window-<mouseEventY; - event->mouseButton = window->mouseEventButton; - window->hasMouseEvent = false; - return 1; - } - - return 0; -} - -EM_JS(void, kon_jsBlit_, (const char *canvasID, uint32_t *pixels, int width, int height), { - var sel = UTF8ToString(canvasID); - var canvas = document.querySelector(sel); - if (!canvas) return; - - if (!canvas.__konCtx) canvas.__konCtx = canvas.getContext('2d'); - var ctx = canvas.__konCtx; - - if (!canvas.__konImgData || canvas.__konImgData.width !== width || canvas.__konImgData.height !== height) { - canvas.__konImgData = ctx.createImageData(width, height); - } - var imgData = canvas.__konImgData; - - var dst = new Uint32Array(imgData.data.buffer); - var srcIdx = pixels >> 2; - var count = width * height; - - for (var i = 0; i < count; i++) { - var px = HEAPU32[srcIdx + i]; - dst[i] = (px & 0xFF00FF00) | ((px & 0x00FF0000) >>> 16) | ((px & 0x000000FF) << 16); - } - - ctx.putImageData(imgData, 0, 0); -}); - -void kon_blitPixels(kon_window_t *window, const uint32_t *pixels, int width, int height) { - if (!window || !pixels) return; - kon_jsBlit_(window->canvasID, (uint32_t *)pixels, width, height); -} - -#elif defined(__linux__) || defined(__unix__) - -#include <X11/Xlib.h> -#include <X11/Xutil.h> -#include <X11/Xatom.h> -#include <X11/extensions/Xrandr.h> -#include <X11/keysym.h> -#include <string.h> - -static kon_key_t kon_translateX11Key_(KeySym sym) { - switch (sym) { - case XK_a: return KON_KEY_A; - case XK_b: return KON_KEY_B; - case XK_c: return KON_KEY_C; - case XK_d: return KON_KEY_D; - case XK_e: return KON_KEY_E; - case XK_f: return KON_KEY_F; - case XK_g: return KON_KEY_G; - case XK_h: return KON_KEY_H; - case XK_i: return KON_KEY_I; - case XK_j: return KON_KEY_J; - case XK_k: return KON_KEY_K; - case XK_l: return KON_KEY_L; - case XK_m: return KON_KEY_M; - case XK_n: return KON_KEY_N; - case XK_o: return KON_KEY_O; - case XK_p: return KON_KEY_P; - case XK_q: return KON_KEY_Q; - case XK_r: return KON_KEY_R; - case XK_s: return KON_KEY_S; - case XK_t: return KON_KEY_T; - case XK_u: return KON_KEY_U; - case XK_v: return KON_KEY_V; - case XK_w: return KON_KEY_W; - case XK_x: return KON_KEY_X; - case XK_y: return KON_KEY_Y; - case XK_z: return KON_KEY_Z; - - case XK_0: return KON_KEY_0; - case XK_1: return KON_KEY_1; - case XK_2: return KON_KEY_2; - case XK_3: return KON_KEY_3; - case XK_4: return KON_KEY_4; - case XK_5: return KON_KEY_5; - case XK_6: return KON_KEY_6; - case XK_7: return KON_KEY_7; - case XK_8: return KON_KEY_8; - case XK_9: return KON_KEY_9; - - case XK_F1: return KON_KEY_F1; - case XK_F2: return KON_KEY_F2; - case XK_F3: return KON_KEY_F3; - case XK_F4: return KON_KEY_F4; - case XK_F5: return KON_KEY_F5; - case XK_F6: return KON_KEY_F6; - case XK_F7: return KON_KEY_F7; - case XK_F8: return KON_KEY_F8; - case XK_F9: return KON_KEY_F9; - case XK_F10: return KON_KEY_F10; - case XK_F11: return KON_KEY_F11; - case XK_F12: return KON_KEY_F12; - - case XK_Left: return KON_KEY_LEFT; - case XK_Right: return KON_KEY_RIGHT; - case XK_Up: return KON_KEY_UP; - case XK_Down: return KON_KEY_DOWN; - - case XK_space: return KON_KEY_SPACE; - case XK_Return: return KON_KEY_ENTER; - case XK_Tab: return KON_KEY_TAB; - case XK_BackSpace: return KON_KEY_BACKSPACE; - case XK_Escape: return KON_KEY_ESCAPE; - case XK_Delete: return KON_KEY_DELETE; - - case XK_Shift_L: return KON_KEY_LEFT_SHIFT; - case XK_Shift_R: return KON_KEY_RIGHT_SHIFT; - case XK_Control_L: return KON_KEY_LEFT_CTRL; - case XK_Control_R: return KON_KEY_RIGHT_CTRL; - case XK_Alt_L: return KON_KEY_LEFT_ALT; - case XK_Alt_R: return KON_KEY_RIGHT_ALT; - - case XK_minus: return KON_KEY_MINUS; - case XK_equal: return KON_KEY_EQUAL; - case XK_bracketleft: return KON_KEY_LEFT_BRACKET; - case XK_bracketright: return KON_KEY_RIGHT_BRACKET; - case XK_semicolon: return KON_KEY_SEMICOLON; - case XK_apostrophe: return KON_KEY_APOSTROPHE; - case XK_comma: return KON_KEY_COMMA; - case XK_period: return KON_KEY_PERIOD; - case XK_slash: return KON_KEY_SLASH; - case XK_backslash: return KON_KEY_BACKSLASH; - case XK_grave: return KON_KEY_GRAVE; - - default: return KON_KEY_UNKNOWN; - } -} - -struct kon_context { - Display *display; -}; - -bool kon_init(void) { - kon_context_t *ctx = malloc(sizeof(kon_context_t)); - if (!ctx) return false; - ctx->display = XOpenDisplay(NULL); - if (!ctx->display) return false; - kon_ctx = ctx; - return true; -} - -void kon_deinit(void) { - if (!kon_ctx) return; - - XCloseDisplay(kon_ctx->display); - free(kon_ctx); - kon_ctx=NULL; -} - -struct kon_window { - Window window; - GC gc; - Visual *visual; - int depth; - Colormap colormap; - bool owns_colormap; - Atom wm_delete; - bool shouldClose; - int exitKey; -}; - -kon_window_t *kon_createWindow(const char *title, int x, int y, int width, int height, kon_windowFlags_t flags) { - if (!kon_ctx) { - fprintf(stderr, "kon_createWindow: There appears to be no kon_ctx. \nPlease, do kon_init(); before doing anything with konwinlib, thank you.\n"); - return NULL; - } - - kon_window_t *window = malloc(sizeof(kon_window_t)); - if (!window) return NULL; - - int screen = DefaultScreen(kon_ctx->display); - - if (flags & KON_WINDOW_CENTER) { - Window root = RootWindow(kon_ctx->display, screen); - - int mon_x = 0, mon_y = 0; - int mon_width = DisplayWidth(kon_ctx->display, screen); - int mon_height = DisplayHeight(kon_ctx->display, screen); - - Window root_ret, child_ret; - int root_x, root_y, win_x, win_y; - unsigned int mask; - XQueryPointer(kon_ctx->display, root, &root_ret, &child_ret, &root_x, &root_y, &win_x, &win_y, &mask); - - XRRScreenResources *res = XRRGetScreenResourcesCurrent(kon_ctx->display, root); - if (res) { - for (int i = 0; i < res->noutput; i++) { - XRROutputInfo *out_info = XRRGetOutputInfo(kon_ctx->display, res, res->outputs[i]); - - if (out_info && out_info->crtc) { - XRRCrtcInfo *crtc = XRRGetCrtcInfo(kon_ctx->display, res, out_info->crtc); - if (crtc) { - - if (root_x >= crtc->x && root_x < crtc->x + (int)crtc->width && - root_y >= crtc->y && root_y < crtc->y + (int)crtc->height) { - mon_x = crtc->x; - mon_y = crtc->y; - mon_width = crtc->width; - mon_height = crtc->height; - XRRFreeCrtcInfo(crtc); - if (out_info) XRRFreeOutputInfo(out_info); - break; - } - XRRFreeCrtcInfo(crtc); - } - } - if (out_info) XRRFreeOutputInfo(out_info); - } - XRRFreeScreenResources(res); - } - - x = mon_x + (mon_width - width ) / 2; - y = mon_y + (mon_height - height) / 2; - } - - if (flags & KON_WINDOW_TRANSPARENT) { - XVisualInfo vinfo; - if (!XMatchVisualInfo(kon_ctx->display, screen, 32, TrueColor, &vinfo)) { - free(window); - return NULL; - } - - window->visual = vinfo.visual; - window->depth = vinfo.depth; - window->colormap = XCreateColormap(kon_ctx->display, RootWindow(kon_ctx->display, screen), window->visual, AllocNone); - - XSetWindowAttributes attrs; - attrs.colormap = window->colormap; - attrs.background_pixel = 0; - attrs.border_pixel = 0; - - window->window = XCreateWindow( - kon_ctx->display, RootWindow(kon_ctx->display, screen), - x, y, width, height, 0, window->depth, InputOutput, - window->visual, CWColormap | CWBackPixel | CWBorderPixel, &attrs); - - window->owns_colormap = true; - } else { - window->visual = DefaultVisual(kon_ctx->display, screen); - window->depth = DefaultDepth(kon_ctx->display, screen); - window->colormap = DefaultColormap(kon_ctx->display, screen); - - window->window = XCreateSimpleWindow( - kon_ctx->display, RootWindow(kon_ctx->display, screen), - x, y, width, height, 0, 0, 0); - - window->owns_colormap = false; - } - - if (!window->window) { - if (window->owns_colormap) { - XFreeColormap(kon_ctx->display, window->colormap); - } - free(window); - return NULL; - } - - XStoreName(kon_ctx->display, window->window, title); - XSelectInput(kon_ctx->display, window->window, ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask); - - if (flags & KON_WINDOW_NO_DECOR) { - Atom motif_hints = XInternAtom(kon_ctx->display, "_MOTIF_WM_HINTS", False); - struct { unsigned long flags, functions, decorations; long input_mode; unsigned long status; } hints; - memset(&hints, 0, sizeof(hints)); - hints.flags = 2; - hints.decorations = 0; - XChangeProperty(kon_ctx->display, window->window, motif_hints, - motif_hints, 32, PropModeReplace, (unsigned char *)&hints, 5); - } - - if (flags & KON_WINDOW_ALWAYS_ONTOP) { - Atom wm_state = XInternAtom(kon_ctx->display, "_NET_WM_STATE", False); - Atom wm_above = XInternAtom(kon_ctx->display, "_NET_WM_STATE_ABOVE", False); - XChangeProperty(kon_ctx->display, window->window, wm_state, XA_ATOM, 32, - PropModeReplace, (unsigned char *)&wm_above, 1); - } - - if (!(flags & KON_WINDOW_RESIZABLE)) { - XSizeHints hints; - hints.flags = PMinSize | PMaxSize; - hints.min_width = hints.max_width = width; - hints.min_height = hints.max_height = height; - XSetWMNormalHints(kon_ctx->display, window->window, &hints); - } - - window->wm_delete = XInternAtom(kon_ctx->display, "WM_DELETE_WINDOW", False); - XSetWMProtocols(kon_ctx->display, window->window, &window->wm_delete, 1); - - window->gc = XCreateGC(kon_ctx->display, window->window, 0, NULL); - - XMapWindow(kon_ctx->display, window->window); - XFlush(kon_ctx->display); - - window->exitKey = 0; - - window->shouldClose = false; - - return window; -} - -void kon_destroyWindow(kon_window_t *window) { - if (!window) return; - if (!kon_ctx) { free(window); return; } - - if (window->gc) XFreeGC(kon_ctx->display, window->gc); - if (window->window) XDestroyWindow(kon_ctx->display, window->window); - if (window->owns_colormap && window->colormap) XFreeColormap(kon_ctx->display, window->colormap); - - XFlush(kon_ctx->display); - - free(window); -} - -void kon_setExitKey(kon_window_t *window, int key) { - if (!window) return; - window->exitKey = key; -} - -void kon_setWindowPos(kon_window_t *window, int x, int y) { - if (!window || !kon_ctx) return; - - XMoveWindow(kon_ctx->display, window->window, x, y); - XFlush(kon_ctx->display); -} - -void kon_setWindowSize(kon_window_t *window, int width, int height) { - if (!window || !kon_ctx) return; - - XResizeWindow(kon_ctx->display, window->window, width, height); - XFlush(kon_ctx->display); -} - -void kon_getWindowPos(kon_window_t *window, int *x, int *y) { - if (!window || !kon_ctx || !x || !y) return; - - Window child; - XTranslateCoordinates(kon_ctx->display, window->window, RootWindow(kon_ctx->display, DefaultScreen(kon_ctx->display)), - 0, 0, x, y, &child); -} - -void kon_getWindowSize(kon_window_t *window, int *width, int *height) { - if (!window || !kon_ctx || !width || !height) return; - - XWindowAttributes attrs; - XGetWindowAttributes(kon_ctx->display, window->window, &attrs); - *width = attrs.width; - *height = attrs.height; -} - -int kon_pollEvent(kon_window_t *window, kon_event_t *event) { - if (!window || !kon_ctx || !event) return 0; - - event->type = KON_EVENT_NONE; - - if (!XPending(kon_ctx->display)) return 0; - - XEvent xev; - XNextEvent(kon_ctx->display, &xev); - - if (xev.type == ClientMessage) { - if ((Atom)xev.xclient.data.l[0] == window->wm_delete) { - event->type = KON_EVENT_CLOSE; - window->shouldClose = true; - } - } - - if (xev.type == ConfigureNotify) { - event->type = KON_EVENT_RESIZE; - event->width = xev.xconfigure.width; - event->height = xev.xconfigure.height; - return 1; - } - - if (xev.type == KeyPress) { - event->type = KON_EVENT_KEY_DOWN; - event->key = (int)kon_translateX11Key_(XLookupKeysym(&xev.xkey, 0)); - if (window->exitKey != 0 && event->key == window->exitKey) { - window->shouldClose = true; - } - return 1; - } - - if (xev.type == KeyRelease) { - event->type = KON_EVENT_KEY_UP; - event->key = (int)kon_translateX11Key_(XLookupKeysym(&xev.xkey, 0)); - return 1; - } - - if (xev.type == ButtonPress || xev.type == ButtonRelease) { - int konButton = -1; - switch (xev.xbutton.button) { - case Button1: - konButton = KON_MOUSE_LEFT; - break; - case Button2: - konButton = KON_MOUSE_MIDDLE; - break; - case Button3: - konButton = KON_MOUSE_RIGHT; - break; - default: - break; - } - - if (konButton != -1) { - event->type = (xev.type == ButtonPress) ? KON_EVENT_MOUSE_DOWN : KON_EVENT_MOUSE_UP; - event->mouseButton = (kon_mouseButton_t)konButton; - event->mouseX = xev.xbutton.x; - event->mouseY = xev.xbutton.y; - } - return 1; - } - - if (xev.type == MotionNotify) { - event->type = KON_EVENT_MOUSE_MOVE; - event->mouseX = xev.xmotion.x; - event->mouseY = xev.xmotion.y; - return 1; - } - - return 1; -} - -bool kon_windowShouldClose(kon_window_t *window) { - if (!window || !kon_ctx) return true; - return window->shouldClose; -} - -/* TODO: implement the rest of these */ - -void kon_blitPixels(kon_window_t *window, const uint32_t *pixels, int width, int height) { - if (!window || !kon_ctx || !pixels) return; - - XImage *ximg = XCreateImage(kon_ctx->display, window->visual, window->depth, ZPixmap, 0, (char *)pixels, width, height, 32, 0); - if (!ximg) return; - - XPutImage(kon_ctx->display, window->window, window->gc, ximg, 0, 0, 0, 0, width, height); - - ximg->data = NULL; - XDestroyImage(ximg); - - XFlush(kon_ctx->display); -} - -/*** windows implementation ***/ - -#elif defined(_WIN32) - -#include <windows.h> -#include <string.h> - -struct kon_context { - HINSTANCE hInstance; -}; - -struct kon_window { - HWND hwnd; - bool shouldClose; - bool isTransparent; - int exitKey; - kon_eventQueue_t queue; -}; - -static kon_key_t kon_translateWin32Key_(WPARAM vk, LPARAM lParam) { - if (vk == VK_SHIFT) { - UINT scancode = (lParam >> 16) & 0xFF; - UINT lshift = MapVirtualKey(VK_LSHIFT, MAPVK_VK_TO_VSC); - return (scancode == lshift) ? KON_KEY_LEFT_SHIFT : KON_KEY_RIGHT_SHIFT; - } - if (vk == VK_CONTROL) { - return (lParam & 0x01000000) ? KON_KEY_RIGHT_CTRL : KON_KEY_LEFT_CTRL; - } - if (vk == VK_MENU) { - return (lParam & 0x01000000) ? KON_KEY_RIGHT_ALT : KON_KEY_LEFT_ALT; - } - - switch (vk) { - case 'A': return KON_KEY_A; - case 'B': return KON_KEY_B; - case 'C': return KON_KEY_C; - case 'D': return KON_KEY_D; - case 'E': return KON_KEY_E; - case 'F': return KON_KEY_F; - case 'G': return KON_KEY_G; - case 'H': return KON_KEY_H; - case 'I': return KON_KEY_I; - case 'J': return KON_KEY_J; - case 'K': return KON_KEY_K; - case 'L': return KON_KEY_L; - case 'M': return KON_KEY_M; - case 'N': return KON_KEY_N; - case 'O': return KON_KEY_O; - case 'P': return KON_KEY_P; - case 'Q': return KON_KEY_Q; - case 'R': return KON_KEY_R; - case 'S': return KON_KEY_S; - case 'T': return KON_KEY_T; - case 'U': return KON_KEY_U; - case 'V': return KON_KEY_V; - case 'W': return KON_KEY_W; - case 'X': return KON_KEY_X; - case 'Y': return KON_KEY_Y; - case 'Z': return KON_KEY_Z; - - case '0': return KON_KEY_0; - case '1': return KON_KEY_1; - case '2': return KON_KEY_2; - case '3': return KON_KEY_3; - case '4': return KON_KEY_4; - case '5': return KON_KEY_5; - case '6': return KON_KEY_6; - case '7': return KON_KEY_7; - case '8': return KON_KEY_8; - case '9': return KON_KEY_9; - - case VK_F1: return KON_KEY_F1; - case VK_F2: return KON_KEY_F2; - case VK_F3: return KON_KEY_F3; - case VK_F4: return KON_KEY_F4; - case VK_F5: return KON_KEY_F5; - case VK_F6: return KON_KEY_F6; - case VK_F7: return KON_KEY_F7; - case VK_F8: return KON_KEY_F8; - case VK_F9: return KON_KEY_F9; - case VK_F10: return KON_KEY_F10; - case VK_F11: return KON_KEY_F11; - case VK_F12: return KON_KEY_F12; - - case VK_LEFT: return KON_KEY_LEFT; - case VK_RIGHT: return KON_KEY_RIGHT; - case VK_UP: return KON_KEY_UP; - case VK_DOWN: return KON_KEY_DOWN; - - case VK_SPACE: return KON_KEY_SPACE; - case VK_RETURN: return KON_KEY_ENTER; - case VK_TAB: return KON_KEY_TAB; - case VK_BACK: return KON_KEY_BACKSPACE; - case VK_ESCAPE: return KON_KEY_ESCAPE; - case VK_DELETE: return KON_KEY_DELETE; - - case VK_OEM_MINUS: return KON_KEY_MINUS; - case VK_OEM_PLUS: return KON_KEY_EQUAL; - case VK_OEM_4: return KON_KEY_LEFT_BRACKET; - case VK_OEM_6: return KON_KEY_RIGHT_BRACKET; - case VK_OEM_1: return KON_KEY_SEMICOLON; - case VK_OEM_7: return KON_KEY_APOSTROPHE; - case VK_OEM_COMMA: return KON_KEY_COMMA; - case VK_OEM_PERIOD: return KON_KEY_PERIOD; - case VK_OEM_2: return KON_KEY_SLASH; - case VK_OEM_5: return KON_KEY_BACKSLASH; - case VK_OEM_3: return KON_KEY_GRAVE; - - default: return KON_KEY_UNKNOWN; - } -} - -LRESULT CALLBACK kon_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - kon_window_t *window = (kon_window_t *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - - switch (msg) { - case WM_NCCREATE: { - CREATESTRUCT *cs = (CREATESTRUCT *)lParam; - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams); - break; - } - case WM_KEYDOWN: - if (window) { - kon_event_t ev = {0}; - ev.type = KON_EVENT_KEY_DOWN; - ev.key = (int)kon_translateWin32Key_(wParam, lParam); - kon_pushEvent_(&window->queue, &ev); - if (window->exitKey != 0 && ev.key == window->exitKey) { - window->shouldClose = true; - ev.type = KON_EVENT_CLOSE; - kon_pushEvent_(&window->queue, &ev); - } - } - break; - case WM_KEYUP: - if (window) { - kon_event_t ev = {0}; - ev.type = KON_EVENT_KEY_UP; - ev.key = (int)kon_translateWin32Key_(wParam, lParam); - kon_pushEvent_(&window->queue, &ev); - } - break; - case WM_LBUTTONDOWN: case WM_LBUTTONUP: - case WM_RBUTTONDOWN: case WM_RBUTTONUP: - case WM_MBUTTONDOWN: case WM_MBUTTONUP: - if (window) { - kon_event_t ev = {0}; - ev.type = (msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_MBUTTONDOWN) - ? KON_EVENT_MOUSE_DOWN : KON_EVENT_MOUSE_UP; - ev.mouseButton = (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONUP) ? KON_MOUSE_LEFT - : (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONUP) ? KON_MOUSE_RIGHT - : KON_MOUSE_MIDDLE; - ev.mouseX = (int)(short)LOWORD(lParam); - ev.mouseY = (int)(short)HIWORD(lParam); - kon_pushEvent_(&window->queue, &ev); - } - break; - case WM_MOUSEMOVE: - if (window) { - kon_event_t ev = {0}; - ev.type = KON_EVENT_MOUSE_MOVE; - ev.mouseX = (int)(short)LOWORD(lParam); - ev.mouseY = (int)(short)HIWORD(lParam); - kon_pushEvent_(&window->queue, &ev); - } - break; - case WM_CLOSE: - if (window) { - kon_event_t ev = {0}; - ev.type = KON_EVENT_CLOSE; - window->shouldClose = true; - kon_pushEvent_(&window->queue, &ev); - } - break; - case WM_SIZE: - /* minimizing reports 0x0, which would wreck the framebuffer */ - if (window && wParam != SIZE_MINIMIZED && LOWORD(lParam) > 0 && HIWORD(lParam) > 0) { - kon_event_t ev = {0}; - ev.type = KON_EVENT_RESIZE; - ev.width = LOWORD(lParam); - ev.height = HIWORD(lParam); - kon_pushEvent_(&window->queue, &ev); - } - break; - case WM_DESTROY: - PostQuitMessage(0); - return 0; - break; - case WM_ERASEBKGND: - return 1; - } - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -bool kon_init(void) { - kon_context_t *ctx = malloc(sizeof(kon_context_t)); - if (!ctx) return false; - - ctx->hInstance = GetModuleHandle(NULL); - - WNDCLASSEX wc = {0}; - wc.cbSize = sizeof(WNDCLASSEX); - wc.lpfnWndProc = kon_wndProc; - wc.hInstance = ctx->hInstance; - wc.lpszClassName = "KonWinLibClass"; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - - if (!RegisterClassEx(&wc)) { - free(ctx); - return false; - } - - kon_ctx = ctx; - return true; -} - -void kon_deinit(void) { - if (!kon_ctx) return; - - free(kon_ctx); - kon_ctx = NULL; -} - - -kon_window_t *kon_createWindow(const char *title, int x, int y, int width, int height, kon_windowFlags_t flags) { - if (!kon_ctx) return NULL; - - kon_window_t *window = calloc(1, sizeof(kon_window_t)); - if (!window) return NULL; - - window->isTransparent = (flags & KON_WINDOW_TRANSPARENT); - window->exitKey = 0; - - DWORD style; - if (flags & KON_WINDOW_NO_DECOR) { - style = WS_POPUP; - } else { - style = WS_OVERLAPPEDWINDOW; - if (!(flags & KON_WINDOW_RESIZABLE)) { - style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); - } - } - - DWORD exStyle = 0; - if (flags & KON_WINDOW_ALWAYS_ONTOP) exStyle |= WS_EX_TOPMOST; - if (flags & KON_WINDOW_TRANSPARENT) exStyle |= WS_EX_LAYERED; - - RECT adj = { 0, 0, width, height }; - AdjustWindowRectEx(&adj, style, FALSE, exStyle); - int winW = adj.right - adj.left; - int winH = adj.bottom - adj.top; - - if (flags & KON_WINDOW_CENTER) { - POINT cursor; - GetCursorPos(&cursor); - - HMONITOR hmon = MonitorFromPoint(cursor, MONITOR_DEFAULTTONEAREST); - - MONITORINFO mi; - mi.cbSize = sizeof(MONITORINFO); - GetMonitorInfo(hmon, &mi); - - int mon_x = mi.rcMonitor.left; - int mon_y = mi.rcMonitor.top; - int mon_width = mi.rcMonitor.right - mi.rcMonitor.left; - int mon_height = mi.rcMonitor.bottom - mi.rcMonitor.top; - - x = mon_x + (mon_width - winW) / 2; - y = mon_y + (mon_height - winH) / 2; - } - - window->hwnd = CreateWindowEx(exStyle, "KonWinLibClass", title, style, x, y, winW, winH, NULL, NULL, kon_ctx->hInstance, window); - - if (!window->hwnd) { - free(window); - return NULL; - } - - ShowWindow(window->hwnd, SW_SHOW); - - return window; -} - -void kon_destroyWindow(kon_window_t *window) { - if (!window) return; - if (window->hwnd) DestroyWindow(window->hwnd); - free(window); -} - -bool kon_windowShouldClose(kon_window_t *window) { - if (!window) return true; - return window->shouldClose; -} - - -void kon_setExitKey(kon_window_t *window, int key) { - if (!window) return; - window->exitKey = key; -} - -void kon_setWindowPos(kon_window_t *window, int x, int y) { - if (!window || !kon_ctx) return; - - SetWindowPos(window->hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); -} - -void kon_setWindowSize(kon_window_t *window, int width, int height) { - if (!window || !kon_ctx) return; - - RECT r = { 0, 0, width, height }; - AdjustWindowRectEx(&r, (DWORD)GetWindowLongPtr(window->hwnd, GWL_STYLE), FALSE, (DWORD)GetWindowLongPtr(window->hwnd, GWL_EXSTYLE)); - SetWindowPos(window->hwnd, NULL, 0, 0, r.right - r.left, r.bottom - r.top, SWP_NOMOVE | SWP_NOZORDER); -} - - -void kon_getWindowPos(kon_window_t *window, int *x, int *y) { - if (!window || !x || !y) return; - - RECT rect; - GetWindowRect(window->hwnd, &rect); - *x = rect.left; - *y = rect.top; -} - -void kon_getWindowSize(kon_window_t *window, int *width, int *height) { - if (!window || !width || !height) return; - - RECT rect; - GetClientRect(window->hwnd, &rect); - *width = rect.right - rect.left; - *height = rect.bottom - rect.top; -} - -int kon_pollEvent(kon_window_t *window, kon_event_t *event) { - if (!window || !event) return 0; - - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - if (!kon_popEvent_(&window->queue, event)) { - event->type = KON_EVENT_NONE; - return 0; - } - return 1; -} - -void kon_blitPixels(kon_window_t *window, const uint32_t *pixels, int width, int height) { - if (!window || !pixels) return; - - BITMAPINFO bmi = {0}; - bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = width; - bmi.bmiHeader.biHeight = -height; /* top-down */ - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - - if (window->isTransparent) { - HDC screenDC = GetDC(NULL); - HDC memDC = CreateCompatibleDC(screenDC); - - void *dibPixels; - HBITMAP hbm = CreateDIBSection(screenDC, &bmi, DIB_RGB_COLORS, &dibPixels, NULL, 0); - if (hbm) { - memcpy(dibPixels, pixels, (size_t)width * height * 4); - HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, hbm); - - RECT winRect; - GetWindowRect(window->hwnd, &winRect); - POINT dstPos = { winRect.left, winRect.top }; - POINT srcPos = { 0, 0 }; - SIZE size = { width, height }; - BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - - UpdateLayeredWindow(window->hwnd, screenDC, &dstPos, &size, memDC, &srcPos, 0, &blend, ULW_ALPHA); - - SelectObject(memDC, oldBmp); - DeleteObject(hbm); - } - - DeleteDC(memDC); - ReleaseDC(NULL, screenDC); - } else { - HDC winDC = GetDC(window->hwnd); - SetDIBitsToDevice(winDC, 0, 0, width, height, 0, 0, 0, height, pixels, &bmi, DIB_RGB_COLORS); - ReleaseDC(window->hwnd, winDC); - } -} - -#else - #error "konwinlib.h: unsupported platform" -#endif /* end of platform switch */ - -/*** main loop ***/ - -#ifdef __EMSCRIPTEN__ - -typedef struct { - kon_window_t *window; - kon_mainLoopFn callback; - void *userData; -} kon_mainLoopCtx_; - -static void kon_mainLoopTrampoline_(void *arg) { - kon_mainLoopCtx_ *ctx = (kon_mainLoopCtx_ *)arg; - - if (kon_windowShouldClose(ctx->window)) { - emscripten_cancel_main_loop(); - free(ctx); - return; - } - - ctx->callback(ctx->userData); -} - -void kon_runMainLoop(kon_window_t *window, kon_mainLoopFn callback, void *userData) { - kon_mainLoopCtx_ *ctx = malloc(sizeof(kon_mainLoopCtx_)); - if (!ctx) return; - - ctx->window = window; - ctx->callback = callback; - ctx->userData = userData; - - emscripten_set_main_loop_arg(kon_mainLoopTrampoline_, ctx, 0, 1); -} - -#else - -void kon_runMainLoop(kon_window_t *window, kon_mainLoopFn callback, void *userData) { - while (!kon_windowShouldClose(window)) { - callback(userData); - } -} - -#endif /* __EMSCRIPTEN__ */ - -#endif /* end of KONWINLIB_IMPLEMENTATION */ - -#endif |
