a-game

2D platformer written from scratch.
git clone git://git.amin.space/a-game.git
Log | Files | Refs | README | LICENSE

platform_windows.c (5994B)


      1 // NOTE(amin): On windows, even with clang and -std=c11, assert.h doesn't
      2 // define static_assert.
      3 #define static_assert _Static_assert
      4 
      5 #include <time.h>
      6 
      7 #include "platform_info.h"
      8 #include "am_gl.h"
      9 #include <GLFW/glfw3.h>
     10 
     11 // LOL, Windef.h
     12 #undef near
     13 #undef far
     14 
     15 #include "game.c"
     16 #include "platform.h"
     17 #include "platform_windows.h"
     18 
     19 internal void framebuffer_size_callback(GLFWwindow* window, int width, int height);
     20 internal void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
     21 
     22 // TODO: map this in a nicer way
     23 global_variable int platform_input_map[NUM_GAME_BUTTONS] = {
     24     [BTN_LEFT] = GLFW_KEY_LEFT,
     25     [BTN_RIGHT] = GLFW_KEY_RIGHT,
     26     [BTN_UP] = GLFW_KEY_UP,
     27     [BTN_DOWN] = GLFW_KEY_DOWN,
     28     [BTN_JUMP] = GLFW_KEY_S,
     29     [BTN_DEBUG_FLOAT] = GLFW_KEY_F11,
     30 };
     31 
     32 int main(void)
     33 {
     34     glfwSetErrorCallback(error_callback);
     35 
     36     if (!glfwInit())
     37     {
     38         fprintf(stderr, "GLFW initialization failed\n");
     39         return -1;
     40     }
     41 
     42     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
     43     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
     44     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
     45     glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
     46 
     47     GLFWwindow *window = glfwCreateWindow(PLATFORM_SCR_WIDTH, PLATFORM_SCR_HEIGHT, "A Game", NULL, NULL);
     48     if (!window)
     49     {
     50         fprintf(stderr, "GLFW window creation failed\n");
     51         glfwTerminate();
     52         return -1;
     53     }
     54     glfwMakeContextCurrent(window);
     55     glfwSetKeyCallback(window, key_callback);
     56 
     57     if (!am_gl_init())
     58     {
     59         fprintf(stderr, "OpenGL function loading failed\n.");
     60         glfwDestroyWindow(window);
     61         glfwTerminate();
     62         return -1;
     63     }
     64 
     65 #ifdef USE_TEST_SEED
     66     srand((uint32_t)0);
     67 #else
     68     srand((uint32_t)time(NULL));
     69 #endif
     70 
     71     struct GameMemory game_memory = {
     72         .buffer_size = MEBIBYTES(64),
     73         .platform = {
     74             &windows_read_entire_file,
     75             &windows_memory_free,
     76         },
     77     };
     78     // TODO: replace with VirtualAlloc
     79     game_memory.buffer = malloc(game_memory.buffer_size);
     80     if (!game_memory.buffer)
     81     {
     82         fprintf(stderr, "Game memory allocation failed\n");
     83         glfwTerminate();
     84         return -1;
     85     }
     86 
     87     struct GameInput game_input = {0};
     88     glfwSetWindowUserPointer(window, &game_input);
     89     game_init(&game_memory, (v2u) {PLATFORM_SCR_WIDTH, PLATFORM_SCR_HEIGHT});
     90 
     91     // NOTE(amin): lag should always be very small, so it's fine to use a float
     92     // to store it. We will not run out of precision.
     93     uint64_t previous_wall_clock_time = glfwGetTimerValue();
     94     uint64_t timer_frequency = glfwGetTimerFrequency();
     95 
     96     while (!glfwWindowShouldClose(window))
     97     {
     98         uint64_t current_wall_clock_time = glfwGetTimerValue();
     99 
    100         {
    101             uint64_t previous_frame_duration = current_wall_clock_time - previous_wall_clock_time;
    102             previous_wall_clock_time = current_wall_clock_time;
    103             f32 dt = (float)previous_frame_duration / (float)timer_frequency;
    104             // TODO: Debug periodic dropped frame on linux when fullscreen
    105             //printf("ms per frame: %f\n", dt * 1000.0f);
    106             game_input.dt = dt;
    107         }
    108 
    109         int32_t framebuffer_width = PLATFORM_SCR_WIDTH;
    110         int32_t framebuffer_height = PLATFORM_SCR_HEIGHT;
    111         glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
    112         assert(framebuffer_width >= 0);
    113         assert(framebuffer_height >= 0);
    114         v2u framebuffer = {framebuffer_width, framebuffer_height};
    115 
    116         game_update_and_render(&game_memory, &game_input, framebuffer);
    117 
    118         // TODO: Do something sane when vsync is disabled
    119         glfwSwapBuffers(window);
    120         glfwPollEvents();
    121     }
    122 
    123     game_cleanup(&game_memory);
    124 
    125     // TODO: replace with VirtualFree
    126     free(game_memory.buffer);
    127 
    128     glfwDestroyWindow(window);
    129     glfwTerminate();
    130     return 0;
    131 }
    132 
    133 internal void error_callback(int error, const char *description)
    134 {
    135     fprintf(stderr, "Error: %s\n", description);
    136 }
    137 
    138 internal void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods)
    139 {
    140     struct GameInput *game_input = (struct GameInput *)glfwGetWindowUserPointer(window);
    141 
    142     enum GameButton game_button = NULL_GAME_BUTTON;
    143     // TODO: Determine the button in a nicer way
    144     for (enum GameButton b = 0; b < NUM_GAME_BUTTONS; b++)
    145     {
    146         if (platform_input_map[b] == key)
    147         {
    148             game_button = b;
    149         }
    150     }
    151     if (game_button != NULL_GAME_BUTTON)
    152     {
    153         switch (action)
    154         {
    155             case GLFW_PRESS:
    156                 game_input->button_states |= (1 << game_button);
    157                 break;
    158             case GLFW_RELEASE:
    159                 game_input->button_states &= ~(1 << game_button);
    160                 break;
    161             case GLFW_REPEAT:
    162                 // Do nothing
    163                 break;
    164             default:
    165                 exit(1);
    166                 break;
    167         }
    168     }
    169 }
    170 
    171 PLATFORM_MEMORY_FREE(windows_memory_free)
    172 {
    173     free(ptr);
    174 }
    175 
    176 internal PLATFORM_READ_ENTIRE_FILE(windows_read_entire_file)
    177 {
    178     assert(a);
    179     FILE *handle = fopen(file_path, "rb");
    180     u8 *buffer = NULL;
    181 
    182     if (handle)
    183     {
    184         // get file size
    185         fseek(handle, 0, SEEK_END);
    186         u32 num_bytes_in_file = ftell(handle);
    187         rewind(handle);
    188 
    189         if (out_num_bytes)
    190         {
    191             *out_num_bytes = num_bytes_in_file;
    192         }
    193 
    194         buffer = mem_st_alloc_buffer(a, u8, num_bytes_in_file + 1);
    195 
    196         u32 bytes_read = fread(buffer, sizeof(u8), num_bytes_in_file, handle);
    197         // IMPORTANT! fread() doesn't add the '\0'
    198         buffer[num_bytes_in_file] = '\0';
    199 
    200         // TODO: handle this case more loudly
    201         if (num_bytes_in_file != bytes_read)
    202         {
    203             buffer = NULL;
    204         }
    205 
    206         fclose(handle);
    207     }
    208     else
    209     {
    210         printf("Error: Couldn't open file at path: %s", file_path);
    211         // TODO: handle errors here in a better way
    212         exit(1);
    213     }
    214 
    215     return buffer;
    216 }