src/core.c

Mon, 30 Oct 2023 18:09:27 +0100

author
Mike Becker <universe@uap-core.de>
date
Mon, 30 Oct 2023 18:09:27 +0100
changeset 3
1efd6da2ad53
parent 2
bb2bfff31f1d
child 6
302971e8599b
permissions
-rw-r--r--

add datatypes.h

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  * Copyright 2023 Mike Becker. All rights reserved.
     4  *
     5  * Redistribution and use in source and binary forms, with or without
     6  * modification, are permitted provided that the following conditions are met:
     7  *
     8  *   1. Redistributions of source code must retain the above copyright
     9  *      notice, this list of conditions and the following disclaimer.
    10  *
    11  *   2. Redistributions in binary form must reproduce the above copyright
    12  *      notice, this list of conditions and the following disclaimer in the
    13  *      documentation and/or other materials provided with the distribution.
    14  *
    15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    25  * POSSIBILITY OF SUCH DAMAGE.
    26  */
    28 #include "ascension/core.h"
    29 #include "ascension/utils.h"
    31 #include <cx/linked_list.h>
    32 #include <cx/printf.h>
    34 static void asc_gl_debug_callback(
    35         GLenum source, GLenum type, GLuint id, GLenum severity,
    36         GLsizei length, const GLchar* message,
    37         const void* userParam
    38 ) {
    39     cxmutstr buf = cx_asprintf(
    40             "source = %d, id = %u, type = %d, severity= %d, message = %.*s",
    41             source, id, type, severity, length, message);
    42     if (type == GL_DEBUG_TYPE_ERROR) {
    43         asc_error(buf.ptr);
    44     } else {
    45         asc_dprintf("GL debug: %*.s", (int)buf.length, buf.ptr);
    46     }
    47     cx_strfree(&buf);
    48 }
    50 AscContext asc_context;
    52 // forward declarations
    53 static void asc_window_destroy_impl(AscWindow* window);
    55 void asc_context_initialize(void) {
    56     if (asc_test_flag(asc_context.flags, ASC_FLAG_INITILIZED))
    57         return;
    58     asc_clear_flag(&asc_context.flags, ASC_FLAG_HAS_ERROR);
    60     // initialize error buffer
    61     cxBufferInit(
    62             &asc_context.error_buffer,
    63             NULL,
    64             256,
    65             NULL,
    66             CX_BUFFER_AUTO_EXTEND
    67     );
    69     // initialize lists
    70     asc_context.windows = cxLinkedListCreateSimple(CX_STORE_POINTERS);
    71     asc_context.windows->simple_destructor =
    72             (cx_destructor_func)asc_window_destroy_impl;
    74     // initialize SDL
    75     if (SDL_Init(SDL_INIT_VIDEO) < 0) {
    76         asc_error(SDL_GetError());
    77     } else {
    78         if (TTF_Init() < 0) {
    79             asc_error(TTF_GetError());
    80         }
    81     }
    82     SDL_ClearError();
    83     asc_set_flag(&asc_context.flags, ASC_FLAG_INITILIZED);
    84     asc_dprintf("Ascension context initialized.");
    85 }
    87 void asc_context_destroy(void) {
    88     // destroy lists
    89     cxListDestroy(asc_context.windows);
    91     // quit SDL
    92     if (TTF_WasInit())
    93         TTF_Quit();
    94     SDL_Quit();
    96     // destroy the error buffer
    97     cxBufferDestroy(&asc_context.error_buffer);
    98     asc_context.flags = 0;
    99     asc_dprintf("Ascension context destroyed.");
   100 }
   102 void asc_error_cchar(char const* text) {
   103     asc_error_cxstr(cx_str(text));
   104 }
   106 void asc_error_cuchar(unsigned char const* text) {
   107     asc_error_cxstr(cx_str((char const*)text));
   108 }
   110 void asc_error_cxstr(cxstring text) {
   111     if (text.length == 0) return;
   113     // write error to debug output
   114     asc_dprintf("ERROR: %*.s", (int)text.length, text.ptr);
   116     // write error to buffer
   117     CxBuffer* buf = &asc_context.error_buffer;
   118     cxBufferWrite(text.ptr, 1, text.length, buf);
   119     cxBufferPut(buf, '\n');
   121     asc_set_flag(&asc_context.flags, ASC_FLAG_HAS_ERROR);
   122 }
   124 bool asc_has_error(void) {
   125     return asc_test_flag(asc_context.flags, ASC_FLAG_HAS_ERROR);
   126 }
   128 char const* asc_get_error(void) {
   129     // we zero-terminate the current buffer contents before providing them
   130     cxBufferPut(&asc_context.error_buffer, 0);
   131     --asc_context.error_buffer.pos;
   132     --asc_context.error_buffer.size;
   133     return asc_context.error_buffer.space;
   134 }
   136 void asc_clear_error(void) {
   137     cxBufferClear(&asc_context.error_buffer);
   138     asc_clear_flag(&asc_context.flags, ASC_FLAG_HAS_ERROR);
   139 }
   141 static void asc_event_window_resized(Uint32 id, Sint32 width, Sint32 height) {
   142     CxIterator iter = cxListIterator(asc_context.windows);
   143     cx_foreach(AscWindow*, w, iter) {
   144         if (w->id == id) {
   145             w->dimensions.width = width;
   146             w->dimensions.height = height;
   147             return;
   148         }
   149     }
   150 }
   152 bool asc_loop_next(void) {
   153     // dispatch SDL events
   154     SDL_Event event;
   155     while (SDL_PollEvent(&event)) {
   156         switch (event.type) {
   157         case SDL_QUIT:return false;
   158         case SDL_WINDOWEVENT: {
   159             if (event.window.type == SDL_WINDOWEVENT_RESIZED)
   160                 asc_event_window_resized(
   161                         event.window.windowID,
   162                         event.window.data1,
   163                         event.window.data2
   164                 );
   165             break;
   166         }
   167         case SDL_KEYDOWN:
   168             // TODO: remove this code and implement key press map instead
   169             if (event.key.keysym.sym == SDLK_ESCAPE)
   170                 return false;
   171             break;
   172         case SDL_KEYUP:
   173             // TODO: implement key press map
   174             break;
   175         }
   176     }
   178     // sync the windows
   179     CxMutIterator windows = cxListMutIterator(asc_context.windows);
   180     cx_foreach(AscWindow*, w, windows) {
   181         asc_window_sync(w);
   182     }
   183     return true;
   184 }
   186 void asc_window_settings_init_defaults(AscWindowSettings* settings) {
   187     settings->depth_size = 24;
   188     settings->vsync = 1;
   189     settings->dimensions.width = 800;
   190     settings->dimensions.height = 600;
   191     settings->fullscreen = 0;
   192     settings->gl_major_version = 3;
   193     settings->gl_minor_version = 3;
   194     settings->title = "Ascended Window";
   195 }
   197 void asc_window_initialize(AscWindow* window, AscWindowSettings const* settings) {
   198     Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
   199     flags |= settings->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE;
   201     window->window = SDL_CreateWindow(
   202             settings->title,
   203             SDL_WINDOWPOS_CENTERED,
   204             SDL_WINDOWPOS_CENTERED,
   205             settings->dimensions.width,
   206             settings->dimensions.height,
   207             flags
   208     );
   209     if (window->window == NULL) {
   210         asc_error(SDL_GetError());
   211         return;
   212     }
   214     window->id = SDL_GetWindowID(window->window);
   215     SDL_GetWindowSize(window->window,
   216             &window->dimensions.width,
   217             &window->dimensions.height
   218     );
   220     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
   221     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, settings->gl_major_version);
   222     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, settings->gl_minor_version);
   223     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, settings->depth_size);
   224     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
   225     window->glctx = SDL_GL_CreateContext(window->window);
   226     if (window->glctx == NULL) {
   227         asc_dprintf("Creating GL context failed for window %u", window->id);
   228     } else {
   229         glewExperimental = GL_TRUE;
   230         GLenum err = glewInit();
   231         if (err == GLEW_OK) {
   232             SDL_GL_SetSwapInterval(settings->vsync);
   233             glEnable(GL_DEPTH_TEST);
   234             glEnable(GL_BLEND);
   235             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   236             glEnable(GL_DEBUG_OUTPUT);
   237             glDebugMessageCallback(asc_gl_debug_callback, NULL);
   238             asc_dprintf("Window %u initialized", window->id);
   239             cxListAdd(asc_context.windows, window);
   240             return;
   241         } else {
   242             asc_error(glewGetErrorString(err));
   243         }
   244     }
   246     // cleanup on error
   247     if (window->glctx != NULL) {
   248         SDL_GL_DeleteContext(window->glctx);
   249     }
   250     window->glctx = NULL;
   251     SDL_DestroyWindow(window->window);
   252     window->window = NULL;
   253     window->id = 0;
   254 }
   256 void asc_window_destroy_impl(AscWindow* window) {
   257     // destory the GL context and the window
   258     if (window->glctx != NULL) {
   259         SDL_GL_DeleteContext(window->glctx);
   260     }
   261     if (window->window != NULL) {
   262         SDL_DestroyWindow(window->window);
   263     }
   265     // clean the data
   266     asc_dprintf("Window %u and its OpenGL context destroyed.", window->id);
   267     memset(window, 0, sizeof(AscWindow));
   268 }
   270 void asc_window_destroy(AscWindow* window) {
   271     // find the window in the context and remove it
   272     bool found = false;
   273     CxMutIterator iter = cxListMutIterator(asc_context.windows);
   274     cx_foreach(AscWindow*, w, iter) {
   275         if (w == window) {
   276             found = true;
   277             cxIteratorFlagRemoval(iter);
   278         }
   279     }
   280     if (!found) asc_window_destroy_impl(window);
   281 }
   283 void asc_window_sync(AscWindow const* window) {
   284     SDL_GL_MakeCurrent(window->window, window->glctx);
   285     SDL_GL_SwapWindow(window->window);
   286     glViewport(0, 0, window->dimensions.width, window->dimensions.height);
   287     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
   288     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   289 }

mercurial