summaryrefslogtreecommitdiff
path: root/kon/konprogram.c
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-09 17:51:49 +0800
committerKonAki <konaki@anokonaki.org>2026-10-09 17:51:49 +0800
commiteca47efac07ffa6c4e35708031dea8a7da4e7ed8 (patch)
tree5d16ccd0309b97c77a3960c20c1a3137753678bd /kon/konprogram.c
parent3e4acddeb4df4d21ad89b8926aa2eafc634daa25 (diff)
Add konprogram and use it in engine.c
Diffstat (limited to 'kon/konprogram.c')
-rw-r--r--kon/konprogram.c227
1 files changed, 227 insertions, 0 deletions
diff --git a/kon/konprogram.c b/kon/konprogram.c
new file mode 100644
index 0000000..33e1f15
--- /dev/null
+++ b/kon/konprogram.c
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * konprogram.c
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+/* the debug functions only exist with the debug module, without KON_DEVELOPER the header gives empty ones instead */
+#if defined(KON_DEVELOPER) && !defined(KON_MODULE_DEBUG)
+#undef KON_DEVELOPER
+#endif
+
+#include "konprogram.h"
+#include "kontime.h"
+
+/*** definition ***/
+
+typedef struct kon_programState {
+ kon_program_t pub;
+ kon_programConfig_t config;
+ kon_time last;
+ float shakeLeft;
+ int shakeX, shakeY;
+} kon_programState_t;
+
+/*** console commands ***/
+
+#ifdef KON_DEVELOPER
+
+static void kon_cmdOpacity_(kon_debug_t *debug, void *user, int argc, char **argv) {
+ kon_programState_t *state = user;
+
+ (void)debug;
+ if (argc > 1) kon_setWindowOpacity(state->pub.window, (float)atof(argv[1]));
+}
+
+static void kon_cmdTopmost_(kon_debug_t *debug, void *user, int argc, char **argv) {
+ kon_programState_t *state = user;
+
+ (void)debug;
+ if (argc > 1) kon_setWindowTopmost(state->pub.window, atoi(argv[1]) != 0);
+}
+
+static void kon_cmdBorderless_(kon_debug_t *debug, void *user, int argc, char **argv) {
+ kon_programState_t *state = user;
+
+ (void)debug;
+ if (argc > 1) kon_setWindowDecorated(state->pub.window, atoi(argv[1]) == 0);
+}
+
+static void kon_cmdClickThrough_(kon_debug_t *debug, void *user, int argc, char **argv) {
+ kon_programState_t *state = user;
+
+ (void)debug;
+ if (argc > 1) kon_setWindowClickThrough(state->pub.window, atoi(argv[1]) != 0);
+}
+
+static void kon_cmdMessage_(kon_debug_t *debug, void *user, int argc, char **argv) {
+ (void)debug; (void)user;
+ kon_messageBox("konengine", argc > 1 ? argv[1] : "hello", KON_MESSAGE_INFO);
+}
+
+static void kon_cmdShake_(kon_debug_t *debug, void *user, int argc, char **argv) {
+ kon_programState_t *state = user;
+
+ (void)debug; (void)argc; (void)argv;
+ kon_getWindowPos(state->pub.window, &state->shakeX, &state->shakeY);
+ state->shakeLeft = 0.5f;
+}
+
+static void kon_addWindowCommands_(kon_programState_t *state) {
+ kon_debug_t *debug = state->pub.debug;
+
+ kon_debugCommand(debug, "opacity", "window opacity 0 to 1", kon_cmdOpacity_, state);
+ kon_debugCommand(debug, "topmost", "topmost 0 or 1", kon_cmdTopmost_, state);
+ kon_debugCommand(debug, "borderless", "borderless 0 or 1", kon_cmdBorderless_, state);
+ kon_debugCommand(debug, "clickthrough", "clickthrough 0 or 1", kon_cmdClickThrough_, state);
+ kon_debugCommand(debug, "message", "message \"text\"", kon_cmdMessage_, state);
+ kon_debugCommand(debug, "shake", "shake the window", kon_cmdShake_, state);
+}
+
+#endif
+
+/*** loop ***/
+
+static void kon_frame_(void *data) {
+ kon_programState_t *state = data;
+ kon_program_t *program = &state->pub;
+ const kon_programConfig_t *config = &state->config;
+ kon_display_t *display = program->display;
+ kon_event_t ev;
+ kon_time now, dt;
+
+ while (kon_pollEvent(program->window, &ev)) {
+ if (kon_debugFeedEvent(program->debug, &ev)) continue;
+ if (ev.type == KON_EVENT_RESIZE) kon_resizeDisplay(display, ev.width, ev.height);
+ if (config->event) config->event(program, &ev, config->user);
+ }
+
+ now = kon_getTime();
+ dt = now - state->last;
+ state->last = now;
+ /* a long stall, like dragging the window, shouldn't make the game jump */
+ if (dt > 0.25) dt = 0.25;
+ program->time += dt;
+
+ if (state->shakeLeft > 0.0f) {
+ state->shakeLeft -= (float)dt;
+ if (state->shakeLeft > 0.0f) kon_setWindowPos(program->window, state->shakeX + rand() % 21 - 10, state->shakeY + rand() % 21 - 10);
+ else kon_setWindowPos(program->window, state->shakeX, state->shakeY);
+ }
+
+ if (config->update) config->update(program, dt, config->user);
+ kon_clearFramebuffer(display->game, KON_BACKGROUND_COLOR);
+ if (config->draw) config->draw(program, display->game, config->user);
+
+ kon_presentDisplay(display);
+ if (config->drawScreen) config->drawScreen(program, display->screen, config->user);
+ kon_debugFrame(program->debug);
+ if (kon_debugQuitRequested(program->debug)) kon_setShouldClose(program->window, true);
+ kon_debugDraw(program->debug, display->screen);
+ kon_blitPixels(program->window, display->screen->data, display->screen->width, display->screen->height);
+ kon_sleep(0.008);
+}
+
+void kon_programQuit(kon_program_t *program) {
+ if (program) kon_setShouldClose(program->window, true);
+}
+
+int kon_programRun(const kon_programConfig_t *config) {
+ kon_programState_t *state;
+ kon_program_t *program;
+ int gameWidth, gameHeight, windowWidth, windowHeight;
+
+ if (!config) return 1;
+ state = calloc(1, sizeof *state);
+ if (!state) return 1;
+ program = &state->pub;
+ state->config = *config;
+
+ gameWidth = config->gameWidth > 0 ? config->gameWidth : 320;
+ gameHeight = config->gameHeight > 0 ? config->gameHeight : 180;
+ windowWidth = config->windowWidth > 0 ? config->windowWidth : gameWidth * 3;
+ windowHeight = config->windowHeight > 0 ? config->windowHeight : gameHeight * 3;
+
+ if (!kon_init()) goto fail;
+ program->window = kon_createWindow(config->title ? config->title : "konengine", 0, 0, windowWidth, windowHeight, KON_WINDOW_CENTER | KON_WINDOW_RESIZABLE);
+ if (!program->window) goto fail;
+#ifdef KON_DEVELOPER
+ kon_setExitKey(program->window, KON_KEY_F10);
+#else
+ kon_setExitKey(program->window, KON_KEY_ESCAPE);
+#endif
+ program->display = kon_createDisplay(gameWidth, gameHeight, windowWidth, windowHeight);
+ if (!program->display) goto fail;
+
+#ifdef KON_MODULE_PACK
+ if (config->packPath) program->pack = kon_packOpen(config->packPath, NULL);
+#endif
+#ifdef KON_MODULE_ASSETS
+ program->assets = kon_assetsCreate(program->pack, config->assetDir ? config->assetDir : "assets");
+#endif
+#ifdef KON_MODULE_FONT
+ {
+ kon_blob_t *ttf = program->assets ? kon_assetBlob(program->assets, "font.ttf") : NULL;
+
+ if (ttf) program->font = kon_loadFont(ttf->data, ttf->size, 14.0f);
+ if (!program->font) program->font = kon_loadBuiltinFont(1);
+ }
+#endif
+#ifdef KON_MODULE_DEBUG
+ program->debug = kon_debugCreate(program->font);
+ kon_debugSetAssets(program->debug, program->assets);
+#ifdef KON_DEVELOPER
+ kon_addWindowCommands_(state);
+#endif
+#endif
+
+ state->last = kon_getTime();
+ if (config->init && !config->init(program, config->user)) goto fail;
+
+ kon_runMainLoop(program->window, kon_frame_, state);
+
+ if (config->shutdown) config->shutdown(program, config->user);
+ kon_debugDestroy(program->debug);
+#ifdef KON_MODULE_FONT
+ kon_freeFont(program->font);
+#endif
+#ifdef KON_MODULE_ASSETS
+ kon_assetsDestroy(program->assets);
+#endif
+#ifdef KON_MODULE_PACK
+ if (program->pack) kon_packClose(program->pack);
+#endif
+ kon_freeDisplay(program->display);
+ kon_destroyWindow(program->window);
+ kon_deinit();
+ free(state);
+ return 0;
+
+fail:
+ if (program->display) kon_freeDisplay(program->display);
+ if (program->window) kon_destroyWindow(program->window);
+ free(state);
+ return 1;
+}