universe@0: /* universe@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. universe@0: * Copyright 2023 Mike Becker. All rights reserved. universe@0: * universe@0: * Redistribution and use in source and binary forms, with or without universe@0: * modification, are permitted provided that the following conditions are met: universe@0: * universe@0: * 1. Redistributions of source code must retain the above copyright universe@0: * notice, this list of conditions and the following disclaimer. universe@0: * universe@0: * 2. Redistributions in binary form must reproduce the above copyright universe@0: * notice, this list of conditions and the following disclaimer in the universe@0: * documentation and/or other materials provided with the distribution. universe@0: * universe@0: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@0: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE universe@0: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE universe@0: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR universe@0: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF universe@0: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS universe@0: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN universe@0: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) universe@0: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE universe@0: * POSSIBILITY OF SUCH DAMAGE. universe@0: */ universe@0: universe@6: #include "ascension/window.h" universe@7: #include "ascension/context.h" universe@7: #include "ascension/error.h" universe@9: #include "ascension/utils.h" universe@0: universe@6: #include universe@6: universe@0: void asc_window_settings_init_defaults(AscWindowSettings* settings) { universe@3: settings->dimensions.width = 800; universe@3: settings->dimensions.height = 600; universe@0: settings->fullscreen = 0; universe@44: settings->glsettings.depth_size = 24; universe@44: settings->glsettings.vsync = 1; universe@44: settings->glsettings.gl_major_version = 4; universe@44: settings->glsettings.gl_minor_version = 0; universe@0: settings->title = "Ascended Window"; universe@0: } universe@0: universe@65: void asc_window_initialize(unsigned int index, AscWindowSettings const *settings) { universe@7: if (index >= ASC_MAX_WINDOWS) { universe@7: asc_error("Maximum number of windows exceeded."); universe@65: return; universe@7: } universe@7: AscWindow *window = &asc_context.windows[index]; universe@7: if (window->id > 0) { universe@7: asc_error("Cannot create window - slot already occupied."); universe@47: asc_dprintf("Tried to create window with index %u twice", index); universe@65: return; universe@7: } universe@47: if (window->ui != NULL) { universe@47: asc_dprintf("Window with index %u has a dangling UI pointer", index); universe@47: asc_scene_node_free(window->ui); universe@47: } universe@7: universe@0: Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; universe@0: flags |= settings->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE; universe@0: universe@0: window->window = SDL_CreateWindow( universe@0: settings->title, universe@0: SDL_WINDOWPOS_CENTERED, universe@0: SDL_WINDOWPOS_CENTERED, universe@3: settings->dimensions.width, universe@3: settings->dimensions.height, universe@0: flags universe@0: ); universe@0: if (window->window == NULL) { universe@0: asc_error(SDL_GetError()); universe@65: return; universe@0: } universe@0: universe@0: window->id = SDL_GetWindowID(window->window); universe@3: SDL_GetWindowSize(window->window, universe@3: &window->dimensions.width, universe@3: &window->dimensions.height universe@3: ); universe@37: window->resized = true; // count initial sizing as resize universe@0: universe@44: if (asc_gl_context_initialize(&window->glctx, window->window, &settings->glsettings)) { universe@47: window->ui = asc_scene_node_empty(); universe@44: asc_dprintf("Window %u initialized", window->id); universe@65: asc_context.active_window = index; universe@44: } else { universe@0: asc_dprintf("Creating GL context failed for window %u", window->id); universe@44: // cleanup on error universe@44: SDL_DestroyWindow(window->window); universe@44: window->window = NULL; universe@44: window->id = 0; universe@0: } universe@0: } universe@0: universe@65: void asc_window_destroy(unsigned int index) { universe@7: // safeguard universe@65: if (asc_context.windows[index].id == 0) return; universe@7: universe@16: // this window cannot be active anymore universe@65: if (asc_context.active_window == index) { universe@65: asc_context.active_window = ASC_MAX_WINDOWS; universe@16: } universe@16: universe@65: // pointer to the window universe@65: AscWindow *window = &asc_context.windows[index]; universe@65: universe@65: // for releasing OpenGL resources, we need to make the context current universe@65: asc_gl_context_activate(&window->glctx); universe@65: universe@29: // destroy all scenes universe@47: asc_scene_node_free(window->ui); universe@47: window->ui = NULL; universe@29: universe@44: // release context related data universe@44: asc_gl_context_destroy(&window->glctx); universe@16: universe@44: // destroy window universe@0: if (window->window != NULL) { universe@0: SDL_DestroyWindow(window->window); universe@0: } universe@0: universe@16: // if another window was active, make the other context current again universe@65: if (asc_context.active_window < ASC_MAX_WINDOWS) { universe@65: asc_gl_context_activate(&asc_active_window->glctx); universe@16: } universe@16: universe@0: // clean the data universe@0: asc_dprintf("Window %u and its OpenGL context destroyed.", window->id); universe@0: memset(window, 0, sizeof(AscWindow)); universe@0: } universe@0: universe@65: void asc_window_sync(unsigned int index) { universe@65: // necessary safeguard universe@65: if (asc_context.windows[index].id == 0) return; universe@65: universe@65: // active the window that shall be synced temporarily universe@65: unsigned int active_index = asc_context.active_window; universe@65: if (index != active_index) { universe@65: asc_window_activate(index); universe@16: } universe@29: universe@47: // Clear the color buffer for the window frame universe@65: int window_width = asc_active_window->dimensions.width; universe@65: int window_height = asc_active_window->dimensions.height; universe@47: glViewport(0, 0, window_width, window_height); universe@47: glClear(GL_COLOR_BUFFER_BIT); universe@47: asc_recti viewport = {0, 0, window_width, window_height}; universe@47: universe@29: // Draw the UI universe@47: AscCamera ui_camera; universe@47: asc_camera_ortho(&ui_camera, viewport); universe@65: asc_scene_draw(asc_active_window->ui, viewport, &ui_camera); universe@29: universe@29: // Swap Buffers universe@65: SDL_GL_SwapWindow(asc_active_window->window); universe@29: universe@37: // Clear Flags universe@65: asc_active_window->resized = false; universe@37: universe@65: if (index != active_index) { universe@65: asc_window_activate(active_index); universe@16: } universe@0: } universe@16: universe@65: void asc_window_activate(unsigned int index) { universe@65: asc_context.active_window = index; universe@65: asc_gl_context_activate(&asc_active_window->glctx); universe@16: }