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 }