1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/window.c Wed Nov 01 20:09:49 2023 +0100 1.3 @@ -0,0 +1,200 @@ 1.4 +/* 1.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 1.6 + * Copyright 2023 Mike Becker. All rights reserved. 1.7 + * 1.8 + * Redistribution and use in source and binary forms, with or without 1.9 + * modification, are permitted provided that the following conditions are met: 1.10 + * 1.11 + * 1. Redistributions of source code must retain the above copyright 1.12 + * notice, this list of conditions and the following disclaimer. 1.13 + * 1.14 + * 2. Redistributions in binary form must reproduce the above copyright 1.15 + * notice, this list of conditions and the following disclaimer in the 1.16 + * documentation and/or other materials provided with the distribution. 1.17 + * 1.18 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 1.19 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1.20 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1.21 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 1.22 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1.23 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1.24 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 1.25 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 1.26 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1.27 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 1.28 + * POSSIBILITY OF SUCH DAMAGE. 1.29 + */ 1.30 + 1.31 +#include "ascension/window.h" 1.32 + 1.33 +#include <cx/linked_list.h> 1.34 +#include <cx/printf.h> 1.35 + 1.36 +#include <GL/glew.h> 1.37 + 1.38 +static void asc_gl_debug_callback( 1.39 + GLenum source, GLenum type, GLuint id, GLenum severity, 1.40 + GLsizei length, const GLchar* message, 1.41 + const void* userParam 1.42 +) { 1.43 + cxmutstr buf = cx_asprintf( 1.44 + "source = %d, id = %u, type = %d, severity= %d, message = %.*s", 1.45 + source, id, type, severity, length, message); 1.46 + if (type == GL_DEBUG_TYPE_ERROR) { 1.47 + asc_error(buf.ptr); 1.48 + } else { 1.49 + asc_dprintf("GL debug: %*.s", (int)buf.length, buf.ptr); 1.50 + } 1.51 + cx_strfree(&buf); 1.52 +} 1.53 + 1.54 + 1.55 +static void asc_event_window_resized(Uint32 id, Sint32 width, Sint32 height) { 1.56 + CxIterator iter = cxListIterator(asc_context.windows); 1.57 + cx_foreach(AscWindow*, w, iter) { 1.58 + if (w->id == id) { 1.59 + w->dimensions.width = width; 1.60 + w->dimensions.height = height; 1.61 + return; 1.62 + } 1.63 + } 1.64 +} 1.65 + 1.66 +bool asc_loop_next(void) { 1.67 + // dispatch SDL events 1.68 + SDL_Event event; 1.69 + while (SDL_PollEvent(&event)) { 1.70 + switch (event.type) { 1.71 + case SDL_QUIT:return false; 1.72 + case SDL_WINDOWEVENT: { 1.73 + if (event.window.type == SDL_WINDOWEVENT_RESIZED) 1.74 + asc_event_window_resized( 1.75 + event.window.windowID, 1.76 + event.window.data1, 1.77 + event.window.data2 1.78 + ); 1.79 + break; 1.80 + } 1.81 + case SDL_KEYDOWN: 1.82 + // TODO: remove this code and implement key press map instead 1.83 + if (event.key.keysym.sym == SDLK_ESCAPE) 1.84 + return false; 1.85 + break; 1.86 + case SDL_KEYUP: 1.87 + // TODO: implement key press map 1.88 + break; 1.89 + } 1.90 + } 1.91 + 1.92 + // sync the windows 1.93 + CxMutIterator windows = cxListMutIterator(asc_context.windows); 1.94 + cx_foreach(AscWindow*, w, windows) { 1.95 + asc_window_sync(w); 1.96 + } 1.97 + return true; 1.98 +} 1.99 + 1.100 +void asc_window_settings_init_defaults(AscWindowSettings* settings) { 1.101 + settings->depth_size = 24; 1.102 + settings->vsync = 1; 1.103 + settings->dimensions.width = 800; 1.104 + settings->dimensions.height = 600; 1.105 + settings->fullscreen = 0; 1.106 + settings->gl_major_version = 3; 1.107 + settings->gl_minor_version = 3; 1.108 + settings->title = "Ascended Window"; 1.109 +} 1.110 + 1.111 +void asc_window_initialize(AscWindow* window, AscWindowSettings const* settings) { 1.112 + Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; 1.113 + flags |= settings->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE; 1.114 + 1.115 + window->window = SDL_CreateWindow( 1.116 + settings->title, 1.117 + SDL_WINDOWPOS_CENTERED, 1.118 + SDL_WINDOWPOS_CENTERED, 1.119 + settings->dimensions.width, 1.120 + settings->dimensions.height, 1.121 + flags 1.122 + ); 1.123 + if (window->window == NULL) { 1.124 + asc_error(SDL_GetError()); 1.125 + return; 1.126 + } 1.127 + 1.128 + window->id = SDL_GetWindowID(window->window); 1.129 + SDL_GetWindowSize(window->window, 1.130 + &window->dimensions.width, 1.131 + &window->dimensions.height 1.132 + ); 1.133 + 1.134 + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 1.135 + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, settings->gl_major_version); 1.136 + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, settings->gl_minor_version); 1.137 + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, settings->depth_size); 1.138 + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 1.139 + window->glctx = SDL_GL_CreateContext(window->window); 1.140 + if (window->glctx == NULL) { 1.141 + asc_dprintf("Creating GL context failed for window %u", window->id); 1.142 + } else { 1.143 + glewExperimental = GL_TRUE; 1.144 + GLenum err = glewInit(); 1.145 + if (err == GLEW_OK) { 1.146 + SDL_GL_SetSwapInterval(settings->vsync); 1.147 + glEnable(GL_DEPTH_TEST); 1.148 + glEnable(GL_BLEND); 1.149 + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1.150 + glEnable(GL_DEBUG_OUTPUT); 1.151 + glDebugMessageCallback(asc_gl_debug_callback, NULL); 1.152 + asc_dprintf("Window %u initialized", window->id); 1.153 + cxListAdd(asc_context.windows, window); 1.154 + return; 1.155 + } else { 1.156 + asc_error(glewGetErrorString(err)); 1.157 + } 1.158 + } 1.159 + 1.160 + // cleanup on error 1.161 + if (window->glctx != NULL) { 1.162 + SDL_GL_DeleteContext(window->glctx); 1.163 + } 1.164 + window->glctx = NULL; 1.165 + SDL_DestroyWindow(window->window); 1.166 + window->window = NULL; 1.167 + window->id = 0; 1.168 +} 1.169 + 1.170 +void asc_window_destroy_impl(AscWindow* window) { 1.171 + // destory the GL context and the window 1.172 + if (window->glctx != NULL) { 1.173 + SDL_GL_DeleteContext(window->glctx); 1.174 + } 1.175 + if (window->window != NULL) { 1.176 + SDL_DestroyWindow(window->window); 1.177 + } 1.178 + 1.179 + // clean the data 1.180 + asc_dprintf("Window %u and its OpenGL context destroyed.", window->id); 1.181 + memset(window, 0, sizeof(AscWindow)); 1.182 +} 1.183 + 1.184 +void asc_window_destroy(AscWindow* window) { 1.185 + // find the window in the context and remove it 1.186 + bool found = false; 1.187 + CxMutIterator iter = cxListMutIterator(asc_context.windows); 1.188 + cx_foreach(AscWindow*, w, iter) { 1.189 + if (w == window) { 1.190 + found = true; 1.191 + cxIteratorFlagRemoval(iter); 1.192 + } 1.193 + } 1.194 + if (!found) asc_window_destroy_impl(window); 1.195 +} 1.196 + 1.197 +void asc_window_sync(AscWindow const* window) { 1.198 + SDL_GL_MakeCurrent(window->window, window->glctx); 1.199 + SDL_GL_SwapWindow(window->window); 1.200 + glViewport(0, 0, window->dimensions.width, window->dimensions.height); 1.201 + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 1.202 + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 1.203 +}