revolver

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

glfw.c (16518B)


      1 //////////////////////////////////////////////////////////////////
      2 // glfw.c
      3 //
      4 // On linux, requires
      5 // -lX11 -lXi
      6 
      7 #define GLFW_IMPL
      8 #include "../../external/glfw/glfw_impl.h"
      9 
     10 void glfw_print_if_err()
     11 {
     12     const char* description;
     13     int code = glfwGetError(&description);
     14 
     15     if (description)
     16         printf("%d : %s\n", code, description);
     17 
     18 }
     19 
     20 #define glfw_maybe_window(w) ((w) ? (w) : (glfw_state.window_list))
     21 
     22 struct rv_window_handle_t {
     23     rv_window_handle_t* next;
     24     GLFWwindow* window;
     25     rv_vec2 last_size;
     26     bool32 window_has_posted_initial_events;
     27 };
     28 
     29 struct {
     30     bool32 is_init;
     31     rv_window_handle_t* window_list;
     32 
     33     rv_event_t* event_first;
     34     rv_event_t* event_last;
     35     rv_arena* event_arena;
     36     u32 mods;
     37 } glfw_state;
     38 bool32 glad_loaded = false;
     39 
     40 
     41 
     42 
     43 #define XKB_KEYSYM_UTF8_MAX_SIZE 5
     44 
     45 int
     46 utf32_to_utf8(uint32_t unichar, char *buffer)
     47 {
     48     int count, shift, length;
     49     uint8_t head;
     50 
     51     /* NOLINTBEGIN(bugprone-branch-clone) */
     52     if (unichar <= 0x007f) {
     53         buffer[0] = (char) unichar;
     54 
     55         buffer[1] = '\0';
     56         return 2;
     57     }
     58     else if (unichar <= 0x07FF) {
     59         length = 2;
     60         head = 0xc0;
     61     }
     62     /* Handle surrogates */
     63     else if (0xd800 <= unichar && unichar <= 0xdfff) {
     64         goto ill_formed_code_unit_subsequence;
     65     }
     66     else if (unichar <= 0xffff) {
     67         length = 3;
     68         head = 0xe0;
     69     }
     70     else if (unichar <= 0x10ffff) {
     71         length = 4;
     72         head = 0xf0;
     73     }
     74     else {
     75         goto ill_formed_code_unit_subsequence;
     76     }
     77     /* NOLINTEND(bugprone-branch-clone) */
     78 
     79     for (count = length - 1, shift = 0; count > 0; count--, shift += 6)
     80         buffer[count] = (char)(0x80 | ((unichar >> shift) & 0x3f));
     81 
     82     buffer[0] = (char)(head | ((unichar >> shift) & 0x3f));
     83     buffer[length] = '\0';
     84 
     85     return length + 1;
     86 
     87 ill_formed_code_unit_subsequence:
     88     buffer[0] = '\0';
     89     return 0;
     90 }
     91 
     92 
     93 
     94 
     95 
     96 
     97 
     98 
     99 
    100 
    101 
    102 
    103 
    104 
    105 
    106 
    107 
    108 void rv_framebuffer_size_callback(GLFWwindow* win, int width, int height) {
    109     rv_window_handle_t*  w = glfw_state.window_list;
    110     for (; w; w = w->next) {
    111         if (w->window == win) {
    112             w->last_size = rv_v2(width, height);
    113         }
    114     }
    115     rv_event_t new_ev = {.window_resize = w, .type = RV_EVENT_WINDOW_RESIZE};
    116     rv_event_t* n = rv_push_no_zero(glfw_state.event_arena, rv_event_t);
    117     *n = new_ev;
    118     rv_llist_queue_push(glfw_state.event_first, glfw_state.event_last, n);
    119 }
    120 
    121 void rv_key_callback(GLFWwindow* window, s32 key, s32 scancode, s32 action, s32 mods)
    122 {
    123     rv_window_handle_t*  w = glfw_state.window_list;
    124     for (; w; w = w->next) {
    125         if (w->window == window) {
    126             break;
    127         }
    128     }
    129     s32 kc = RV_KEYCODE_INVALID;
    130 
    131     switch (key) {
    132         // TODO(Samdal): add some missing keycodes...
    133     case GLFW_KEY_A:                kc = RV_KEYCODE_A; break;
    134     case GLFW_KEY_B:                kc = RV_KEYCODE_B; break;
    135     case GLFW_KEY_C:                kc = RV_KEYCODE_C; break;
    136     case GLFW_KEY_D:                kc = RV_KEYCODE_D; break;
    137     case GLFW_KEY_E:                kc = RV_KEYCODE_E; break;
    138     case GLFW_KEY_F:                kc = RV_KEYCODE_F; break;
    139     case GLFW_KEY_G:                kc = RV_KEYCODE_G; break;
    140     case GLFW_KEY_H:                kc = RV_KEYCODE_H; break;
    141     case GLFW_KEY_I:                kc = RV_KEYCODE_I; break;
    142     case GLFW_KEY_J:                kc = RV_KEYCODE_J; break;
    143     case GLFW_KEY_K:                kc = RV_KEYCODE_K; break;
    144     case GLFW_KEY_L:                kc = RV_KEYCODE_L; break;
    145     case GLFW_KEY_M:                kc = RV_KEYCODE_M; break;
    146     case GLFW_KEY_N:                kc = RV_KEYCODE_N; break;
    147     case GLFW_KEY_O:                kc = RV_KEYCODE_O; break;
    148     case GLFW_KEY_P:                kc = RV_KEYCODE_P; break;
    149     case GLFW_KEY_Q:                kc = RV_KEYCODE_Q; break;
    150     case GLFW_KEY_R:                kc = RV_KEYCODE_R; break;
    151     case GLFW_KEY_S:                kc = RV_KEYCODE_S; break;
    152     case GLFW_KEY_T:                kc = RV_KEYCODE_T; break;
    153     case GLFW_KEY_U:                kc = RV_KEYCODE_U; break;
    154     case GLFW_KEY_V:                kc = RV_KEYCODE_V; break;
    155     case GLFW_KEY_W:                kc = RV_KEYCODE_W; break;
    156     case GLFW_KEY_X:                kc = RV_KEYCODE_X; break;
    157     case GLFW_KEY_Y:                kc = RV_KEYCODE_Y; break;
    158     case GLFW_KEY_Z:                kc = RV_KEYCODE_Z; break;
    159     case GLFW_KEY_LEFT_SHIFT:       kc = RV_KEYCODE_LEFT_SHIFT; break;
    160     case GLFW_KEY_RIGHT_SHIFT:      kc = RV_KEYCODE_RIGHT_SHIFT; break;
    161     case GLFW_KEY_LEFT_ALT:         kc = RV_KEYCODE_LEFT_ALT; break;
    162     case GLFW_KEY_RIGHT_ALT:        kc = RV_KEYCODE_RIGHT_ALT; break;
    163     case GLFW_KEY_LEFT_CONTROL:     kc = RV_KEYCODE_LEFT_CONTROL; break;
    164     case GLFW_KEY_RIGHT_CONTROL:    kc = RV_KEYCODE_RIGHT_CONTROL; break;
    165     case GLFW_KEY_BACKSPACE:        kc = RV_KEYCODE_BACKSPACE; break;
    166     case GLFW_KEY_BACKSLASH:        kc = RV_KEYCODE_BACKSLASH; break;
    167     case GLFW_KEY_SLASH:            kc = RV_KEYCODE_SLASH; break;
    168     case GLFW_KEY_GRAVE_ACCENT:     kc = RV_KEYCODE_GRAVE_ACCENT; break;
    169     case GLFW_KEY_COMMA:            kc = RV_KEYCODE_COMMA; break;
    170     case GLFW_KEY_PERIOD:           kc = RV_KEYCODE_PERIOD; break;
    171     case GLFW_KEY_ESCAPE:           kc = RV_KEYCODE_ESC; break;
    172     case GLFW_KEY_SPACE:            kc = RV_KEYCODE_SPACE; break;
    173     case GLFW_KEY_LEFT:             kc = RV_KEYCODE_LEFT; break;
    174     case GLFW_KEY_UP:               kc = RV_KEYCODE_UP; break;
    175     case GLFW_KEY_RIGHT:            kc = RV_KEYCODE_RIGHT; break;
    176     case GLFW_KEY_DOWN:             kc = RV_KEYCODE_DOWN; break;
    177     case GLFW_KEY_0:                kc = RV_KEYCODE_0; break;
    178     case GLFW_KEY_1:                kc = RV_KEYCODE_1; break;
    179     case GLFW_KEY_2:                kc = RV_KEYCODE_2; break;
    180     case GLFW_KEY_3:                kc = RV_KEYCODE_3; break;
    181     case GLFW_KEY_4:                kc = RV_KEYCODE_4; break;
    182     case GLFW_KEY_5:                kc = RV_KEYCODE_5; break;
    183     case GLFW_KEY_6:                kc = RV_KEYCODE_6; break;
    184     case GLFW_KEY_7:                kc = RV_KEYCODE_7; break;
    185     case GLFW_KEY_8:                kc = RV_KEYCODE_8; break;
    186     case GLFW_KEY_9:                kc = RV_KEYCODE_9; break;
    187     case GLFW_KEY_KP_0:             kc = RV_KEYCODE_KP_0; break;
    188     case GLFW_KEY_KP_1:             kc = RV_KEYCODE_KP_1; break;
    189     case GLFW_KEY_KP_2:             kc = RV_KEYCODE_KP_2; break;
    190     case GLFW_KEY_KP_3:             kc = RV_KEYCODE_KP_3; break;
    191     case GLFW_KEY_KP_4:             kc = RV_KEYCODE_KP_4; break;
    192     case GLFW_KEY_KP_5:             kc = RV_KEYCODE_KP_5; break;
    193     case GLFW_KEY_KP_6:             kc = RV_KEYCODE_KP_6; break;
    194     case GLFW_KEY_KP_7:             kc = RV_KEYCODE_KP_7; break;
    195     case GLFW_KEY_KP_8:             kc = RV_KEYCODE_KP_8; break;
    196     case GLFW_KEY_KP_9:             kc = RV_KEYCODE_KP_9; break;
    197     case GLFW_KEY_CAPS_LOCK:        kc = RV_KEYCODE_CAPS_LOCK; break;
    198     case GLFW_KEY_DELETE:           kc = RV_KEYCODE_DELETE; break;
    199     case GLFW_KEY_END:              kc = RV_KEYCODE_END; break;
    200     case GLFW_KEY_F1:               kc = RV_KEYCODE_F1; break;
    201     case GLFW_KEY_F2:               kc = RV_KEYCODE_F2; break;
    202     case GLFW_KEY_F3:               kc = RV_KEYCODE_F3; break;
    203     case GLFW_KEY_F4:               kc = RV_KEYCODE_F4; break;
    204     case GLFW_KEY_F5:               kc = RV_KEYCODE_F5; break;
    205     case GLFW_KEY_F6:               kc = RV_KEYCODE_F6; break;
    206     case GLFW_KEY_F7:               kc = RV_KEYCODE_F7; break;
    207     case GLFW_KEY_F8:               kc = RV_KEYCODE_F8; break;
    208     case GLFW_KEY_F9:               kc = RV_KEYCODE_F9; break;
    209     case GLFW_KEY_F10:              kc = RV_KEYCODE_F10; break;
    210     case GLFW_KEY_F11:              kc = RV_KEYCODE_F11; break;
    211     case GLFW_KEY_F12:              kc = RV_KEYCODE_F12; break;
    212     case GLFW_KEY_HOME:             kc = RV_KEYCODE_HOME; break;
    213     case GLFW_KEY_EQUAL:            kc = RV_KEYCODE_EQUAL; break;
    214     case GLFW_KEY_MINUS:            kc = RV_KEYCODE_MINUS; break;
    215     case GLFW_KEY_LEFT_BRACKET:     kc = RV_KEYCODE_LEFT_BRACKET; break;
    216     case GLFW_KEY_RIGHT_BRACKET:    kc = RV_KEYCODE_RIGHT_BRACKET; break;
    217     case GLFW_KEY_SEMICOLON:        kc = RV_KEYCODE_SEMICOLON; break;
    218     case GLFW_KEY_ENTER:            kc = RV_KEYCODE_ENTER; break;
    219     case GLFW_KEY_INSERT:           kc = RV_KEYCODE_INSERT; break;
    220     case GLFW_KEY_PAGE_UP:          kc = RV_KEYCODE_PAGE_UP; break;
    221     case GLFW_KEY_PAGE_DOWN:        kc = RV_KEYCODE_PAGE_DOWN; break;
    222     case GLFW_KEY_NUM_LOCK:         kc = RV_KEYCODE_NUM_LOCK; break;
    223     case GLFW_KEY_TAB:              kc = RV_KEYCODE_TAB; break;
    224     case GLFW_KEY_KP_MULTIPLY:      kc = RV_KEYCODE_KP_MULTIPLY; break;
    225     case GLFW_KEY_KP_DIVIDE:        kc = RV_KEYCODE_KP_DIVIDE; break;
    226     case GLFW_KEY_KP_ADD:           kc = RV_KEYCODE_KP_ADD; break;
    227     case GLFW_KEY_KP_SUBTRACT:      kc = RV_KEYCODE_KP_SUBTRACT; break;
    228     case GLFW_KEY_KP_ENTER:         kc = RV_KEYCODE_KP_ENTER; break;
    229     case GLFW_KEY_KP_DECIMAL:       kc = RV_KEYCODE_KP_DECIMAL; break;
    230     case GLFW_KEY_PAUSE:            kc = RV_KEYCODE_PAUSE; break;
    231     case GLFW_KEY_PRINT_SCREEN:     kc = RV_KEYCODE_PRINT_SCREEN; break;
    232     default:                        kc = RV_KEYCODE_COUNT; break;
    233     }
    234 
    235     rv_event_t new_ev = {.key_press.window = w};
    236 
    237     if (action == GLFW_PRESS) {
    238         new_ev.type = RV_EVENT_KEY_PRESS;
    239     } else if (action == GLFW_RELEASE) {
    240         new_ev.type = RV_EVENT_KEY_RELEASE;
    241     }
    242 
    243     if (new_ev.type != RV_EVENT_INVALID && kc != RV_KEYCODE_INVALID) {
    244         glfw_state.mods = mods;
    245         new_ev.key_press.mods = mods;
    246         new_ev.key_press.keycode = kc;
    247         rv_event_t* n = rv_push_copy(glfw_state.event_arena, rv_event_t, &new_ev);
    248         rv_llist_queue_push(glfw_state.event_first, glfw_state.event_last, n);
    249     }
    250 }
    251 
    252 void rv_character_callback(GLFWwindow* window, unsigned int codepoint)
    253 {
    254     rv_window_handle_t*  w = glfw_state.window_list;
    255     for (; w; w = w->next) {
    256         if (w->window == window) {
    257             break;
    258         }
    259     }
    260 
    261     char buf[XKB_KEYSYM_UTF8_MAX_SIZE];
    262     s32 len = utf32_to_utf8(codepoint, buf);
    263 
    264     rv_event_t new_ev = {.key_press.window = w};
    265     new_ev.type = RV_EVENT_KEY_PRESS;
    266 
    267     if (len) {
    268         new_ev.key_press.string = rv_str8_from_cstr(buf);
    269         new_ev.key_press.string = rv_str8_copy(glfw_state.event_arena, new_ev.key_press.string);
    270         if (new_ev.key_press.string.len == 1) {
    271             if (new_ev.key_press.string.str[0] == '\r') {
    272                 new_ev.key_press.string.str[0] = '\n';
    273             }
    274         }
    275         new_ev.key_press.mods = glfw_state.mods;
    276         rv_event_t* n = rv_push_copy(glfw_state.event_arena, rv_event_t, &new_ev);
    277         rv_llist_queue_push(glfw_state.event_first, glfw_state.event_last, n);
    278     }
    279 }
    280 
    281 void rv_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
    282 {
    283     rv_window_handle_t*  w = glfw_state.window_list;
    284     for (; w; w = w->next) {
    285         if (w->window == window) {
    286             break;
    287         }
    288     }
    289 
    290     rv_event_t new_ev = {.key_press.window = w};
    291 
    292     if (action == GLFW_PRESS) {
    293         new_ev.type = RV_EVENT_BUTTON_PRESS;
    294     } else if (action == GLFW_RELEASE) {
    295         new_ev.type = RV_EVENT_BUTTON_RELEASE;
    296     }
    297 
    298     // remap buttons
    299     switch (button) {
    300         case 0:
    301         case 1:
    302         case 2:
    303             new_ev.button_press.button = button;
    304             break;
    305         default:
    306             new_ev.button_press.button = button + 4;
    307             break;
    308     }
    309 
    310     if (new_ev.type != RV_EVENT_INVALID) {
    311         rv_event_t* n = rv_push_copy(glfw_state.event_arena, rv_event_t, &new_ev);
    312         rv_llist_queue_push(glfw_state.event_first, glfw_state.event_last, n);
    313     }
    314 }
    315 
    316 
    317 void rv_cursor_enter_callback(GLFWwindow* window, int entered)
    318 {
    319     rv_window_handle_t*  w = glfw_state.window_list;
    320     for (; w; w = w->next) {
    321         if (w->window == window) {
    322             break;
    323         }
    324     }
    325     if (entered) {
    326         rv_event_t new_ev = {.type = RV_EVENT_WINDOW_ENTER, .window_enter = w};
    327         rv_event_t* n = rv_push_copy(glfw_state.event_arena, rv_event_t, &new_ev);
    328         rv_llist_queue_push(glfw_state.event_first, glfw_state.event_last, n);
    329     } else {
    330         rv_event_t new_ev = {.type = RV_EVENT_WINDOW_LEAVE, .window_leave = w};
    331         rv_event_t* n = rv_push_copy(glfw_state.event_arena, rv_event_t, &new_ev);
    332         rv_llist_queue_push(glfw_state.event_first, glfw_state.event_last, n);
    333     }
    334 }
    335 
    336 
    337 RV_GLOBAL rv_event_t* rv_get_events(rv_arena* arena, rv_event_flags_t flags)
    338 {
    339     glfw_state.event_first = glfw_state.event_first = NULL;
    340     glfw_state.event_arena = arena;
    341 
    342     // send initial events
    343     for (rv_window_handle_t*  w = glfw_state.window_list; w; w = w->next) {
    344         if (!w->window_has_posted_initial_events) {
    345             w->window_has_posted_initial_events = true;
    346 
    347             int width, height;
    348             glfwGetFramebufferSize(w->window, &width, &height);
    349             rv_framebuffer_size_callback(w->window, width, height);
    350         }
    351     }
    352 
    353     // check for close
    354     for (rv_window_handle_t*  w = glfw_state.window_list; w; w = w->next) {
    355         if (glfwWindowShouldClose(w->window)) {
    356             rv_event_t new_ev = {.type = RV_EVENT_WINDOW_CLOSE, .window_close = w};
    357             rv_event_t* n = rv_push_copy(glfw_state.event_arena, rv_event_t, &new_ev);
    358             rv_llist_queue_push(glfw_state.event_first, glfw_state.event_last, n);
    359         }
    360     }
    361 
    362     glfwPollEvents(); // events are added through callbacks
    363 
    364     rv_keyboard_context_update(&rv_keyboard_context, glfw_state.event_first);
    365 
    366     return glfw_state.event_first;
    367 }
    368 
    369 
    370 
    371 
    372 
    373 
    374 
    375 
    376 
    377 
    378 
    379 
    380 
    381 
    382 
    383 
    384 RV_GLOBAL rv_window_handle_t* rv_create_window(rv_window_desc_t desc)
    385 {
    386     rv_window_handle_t* res = rv_mem_global_alloc(rv_window_handle_t, 1);
    387 
    388     bool32 just_init = false;
    389     if (!glfw_state.is_init) {
    390         glfw_state.is_init = true;
    391 
    392         glfwInit();
    393 #if RV_RENDER_OPENGL
    394         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    395         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    396         glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    397 #endif
    398         glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
    399     }
    400 
    401     { // make window
    402         rv_temp_arena scratch = rv_scratch_begin(0, 0);
    403         rv_str8 title = rv_str8_copy(scratch.arena, desc.name);
    404 
    405         rv_vec2 size = desc.rect.wh;
    406         if (size.x == 0) size.x = 150;
    407         if (size.y == 0) size.y = 150;
    408 
    409         res->window = glfwCreateWindow(size.x, size.y, (const char*)title.str, NULL, NULL);
    410 
    411         glfw_print_if_err();
    412         rv_assert(res->window);
    413 
    414         rv_scratch_end(scratch);
    415     }
    416 
    417     glfwMakeContextCurrent(res->window);
    418 
    419     glfwSwapInterval(desc.vsync ? 1 : 0);
    420 
    421     { // event callbacks
    422         glfwSetFramebufferSizeCallback(res->window, rv_framebuffer_size_callback);
    423         glfwSetKeyCallback(res->window, rv_key_callback);
    424         glfwSetCharCallback(res->window, rv_character_callback);
    425         glfwSetMouseButtonCallback(res->window, rv_mouse_button_callback);
    426         glfwSetCursorEnterCallback(res->window, rv_cursor_enter_callback);
    427     }
    428 
    429 #if RV_RENDER_OPENGL
    430     if (!glad_loaded) {
    431         glad_loaded = true;
    432         rv_assert(gladLoadGLLoader((GLADloadproc)glfwGetProcAddress));
    433     }
    434 #endif
    435 
    436     rv_llist_stack_push(glfw_state.window_list, res);
    437     return res;
    438 }
    439 
    440 RV_GLOBAL void rv_destroy_window(rv_window_handle_t** handle)
    441 {
    442     if (!*handle) {
    443         rv_unreachable();
    444         return;
    445     }
    446 
    447 #if RV_RENDER_OPENGL
    448     {
    449         glfwMakeContextCurrent(NULL);
    450     }
    451 #endif
    452 
    453     glfwDestroyWindow(handle[0]->window);
    454 
    455     for (rv_window_handle_t** window_iter = &glfw_state.window_list; *window_iter; window_iter = &window_iter[0]->next) {
    456         if (*window_iter == *handle) {
    457             *window_iter = handle[0]->next;
    458             break;
    459         }
    460     }
    461 
    462     rv_mem_global_alloc_free(*handle, sizeof(**handle));
    463     *handle = 0;
    464 }
    465 
    466 
    467 
    468 
    469 
    470 RV_GLOBAL rv_vec2 rv_window_size(rv_window_handle_t* window)
    471 {
    472     return glfw_maybe_window(window)->last_size;
    473 }
    474 RV_GLOBAL rv_vec2 rv_window_mouse_pos(rv_window_handle_t* window)
    475 {
    476     f64 xpos, ypos;
    477     glfwGetCursorPos(glfw_maybe_window(window)->window, &xpos, &ypos);
    478     return rv_v2(xpos, ypos);
    479 }
    480 
    481 RV_GLOBAL void  rv_window_render_begin(rv_window_handle_t* window)
    482 {
    483     glfwMakeContextCurrent(glfw_maybe_window(window)->window);
    484 }
    485 RV_GLOBAL void  rv_window_render_end(rv_window_handle_t* window)
    486 {
    487     glfwSwapBuffers(glfw_maybe_window(window)->window);
    488 }