Wed, 02 Jul 2025 23:21:17 +0200
resolve TODOs regarding input.h
a) mouse position must be integer, because it can be negative (though rarely)
b) we should not trade "access complexity" for space in the scancodes array
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * Copyright 2023 Mike Becker. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "ascension/error.h" #include "ascension/window.h" #include "ascension/context.h" #include <GL/glew.h> void asc_window_settings_init_defaults(AscWindowSettings *settings) { settings->dimensions.width = 800; settings->dimensions.height = 600; settings->fullscreen = false; settings->glsettings.depth_size = 24; settings->glsettings.vsync = 1; settings->glsettings.gl_major_version = 4; settings->glsettings.gl_minor_version = 0; settings->title = "Ascended Window"; } void asc_window_initialize(unsigned int index, const AscWindowSettings *settings) { if (index >= ASC_MAX_WINDOWS) { asc_error("Maximum number of windows exceeded (%u/%u).", index, ASC_MAX_WINDOWS); return; } AscWindow *window = &asc_context.windows[index]; if (window->id > 0) { asc_error("Cannot create window - slot %u already occupied.", index); return; } Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; flags |= settings->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE; window->window = SDL_CreateWindow( settings->title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, settings->dimensions.width, settings->dimensions.height, flags ); if (window->window == NULL) { asc_error("Creating Window failed: %s", SDL_GetError()); return; } window->id = SDL_GetWindowID(window->window); { Sint32 w, h; SDL_GetWindowSize(window->window, &w, &h); window->dimensions = asc_vec2u_new(w, h); } window->resized = true; // count initial sizing as resize // default UI scale window->ui_scale = 1.0f; if (asc_gl_context_initialize(&window->glctx, window->window, &settings->glsettings)) { asc_scene_init(&window->ui, .type = ASC_CAMERA_ORTHO, .ortho.rect = asc_recti_new(0, 0, window->dimensions.width, window->dimensions.height), .projection_update_func = asc_camera_ortho_update_size ); asc_dprintf("Window %u initialized at index %u", window->id, index); asc_context.active_window = index; } else { asc_error("Creating GL context failed for window %u at index %u", window->id, index); // cleanup on error SDL_DestroyWindow(window->window); window->window = NULL; window->id = 0; } } void asc_window_destroy(unsigned int index) { // safeguard if (asc_context.windows[index].id == 0) return; // this window cannot be active anymore if (asc_context.active_window == index) { asc_context.active_window = ASC_MAX_WINDOWS; } // pointer to the window AscWindow *window = &asc_context.windows[index]; // for releasing OpenGL resources, we need to make the context current asc_gl_context_activate(&window->glctx); // destroy all scenes for (unsigned int i = 0; i < ASC_MAX_SCENES; i++) { asc_scene_destroy(&window->scenes[i]); } asc_scene_destroy(&window->ui); // release context related data asc_gl_context_destroy(&window->glctx); // destroy window if (window->window != NULL) { SDL_DestroyWindow(window->window); } // if another window was active, make the other context current again if (asc_context.active_window < ASC_MAX_WINDOWS) { asc_gl_context_activate(&asc_active_window->glctx); } // clean the data asc_dprintf("Window %u and its OpenGL context destroyed.", window->id); memset(window, 0, sizeof(AscWindow)); } void asc_window_sync(unsigned int index) { if (index > ASC_MAX_WINDOWS) { asc_error("Index for syncing window out of bounds (%u/%u).", index, ASC_MAX_WINDOWS); return; } AscWindow *window = &asc_context.windows[index]; // necessary safeguard if (window->id == 0) return; // active the window that shall be synced temporarily unsigned int active_index = asc_context.active_window; if (index != active_index) { asc_window_activate(index); } // Clear the color buffer for the window frame int window_width = window->dimensions.width; int window_height = window->dimensions.height; glViewport(0, 0, window_width, window_height); glClear(GL_COLOR_BUFFER_BIT); // Execute all behaviors // TODO: this can eventually be parallelized for (unsigned int i = 0; i < ASC_MAX_SCENES; i++) { asc_scene_execute_behaviors(&window->scenes[i]); } asc_scene_execute_behaviors(&window->ui); // Draw all scenes for (unsigned int i = 0; i < ASC_MAX_SCENES; i++) { asc_scene_draw(&window->scenes[i]); } asc_scene_draw(&window->ui); // Swap Buffers SDL_GL_SwapWindow(window->window); // Clear Flags window->resized = false; if (index != active_index) { asc_window_activate(active_index); } } void asc_window_activate(unsigned int index) { asc_context.active_window = index; asc_gl_context_activate(&asc_active_window->glctx); } unsigned int asc_window_index(Uint32 id) { unsigned int i = 0; for (; i < ASC_MAX_WINDOWS ; i++) { if (asc_context.windows[i].id == id) break; } return i; } AscScene *asc_window_scene(unsigned int index) { return &asc_active_window->scenes[index]; } asc_vec2u asc_window_display_resolution(void) { const AscWindow *window = asc_active_window; SDL_DisplayMode dm; const int display = SDL_GetWindowDisplayIndex(window->window); if (SDL_GetDesktopDisplayMode(display, &dm)) { asc_error("Failed to get display mode for window %u on display %d: %s", window->id, display, SDL_GetError()); } return asc_vec2u_new(dm.w, dm.h); }