linux.c (21834B)
1 #define RTS_API_IMPL 1 2 3 #include "linux.h" 4 5 internal struct linux_state state; 6 7 enum debug_level loglevel; 8 9 int 10 main(int argc, char **argv) 11 { 12 #ifdef RTS_LOGLEVEL 13 loglevel = RTS_LOGLEVEL; 14 #else 15 loglevel = INFO; // WARN; 16 #endif 17 18 memset(&state, 0, sizeof state); 19 20 load_platform_api(&state.platform_api); 21 load_renderer_api(&state.renderer_api); 22 23 char game_lib_path[PATH_MAX]; 24 strcpy(game_lib_path, argv[0]); 25 strcpy(strrchr(game_lib_path, '/'), "/rts.so"); 26 27 if (!load_game_api(game_lib_path, &state.rts_api)) { 28 dbglog(ERROR, "Failed to load game api: %s\n", game_lib_path); 29 exit(EXIT_FAILURE); 30 } 31 32 struct thread thread = { 33 .id = 0, 34 }; 35 36 u64 total_memory = PLATFORM_MEMORY + GAME_MEMORY; 37 if (!init_memory(&state.memory, total_memory)) { 38 dbglog(ERROR, "Failed to initialise platform and game memory\n"); 39 exit(EXIT_FAILURE); 40 } 41 42 struct arena platform_arena = { 43 .ptr = state.memory.ptr, 44 .cap = PLATFORM_MEMORY, 45 .len = 0, 46 }; 47 48 struct memory game_memory = { 49 .ptr = (u8 *) state.memory.ptr + PLATFORM_MEMORY, 50 .len = GAME_MEMORY, 51 }; 52 53 f32 target_render_rate = 60.0, target_physics_rate = 60.0; 54 u32 target_us_per_frame = USECS / target_render_rate; 55 u32 target_us_per_tick = USECS / target_physics_rate; 56 57 (void) target_us_per_tick; 58 59 u32 period_samples = RTS_AUDIO_SAMPLE_RATE / target_render_rate; 60 u32 buffer_samples = RTS_AUDIO_SAMPLE_RATE; 61 if (!init_audio(&platform_arena, &state.audio, 62 period_samples, buffer_samples)) { 63 dbglog(ERROR, "Failed to initialise platform audio\n"); 64 exit(EXIT_FAILURE); 65 } 66 67 char const *title = "RTS Demo"; 68 if (!init_video(&platform_arena, &state.video, title)) { 69 dbglog(ERROR, "Failed to initialise platform video\n"); 70 exit(EXIT_FAILURE); 71 } 72 73 if (!state.rts_api.init(&thread, &game_memory, &state.platform_api)) { 74 dbglog(ERROR, "Failed to initialise rts game\n"); 75 exit(EXIT_FAILURE); 76 } 77 78 state.running = 1; 79 80 f32 dt = 1 / target_render_rate; 81 while (state.running) { 82 struct timespec frame_start, frame_end; 83 clock_gettime(CLOCK_MONOTONIC, &frame_start); 84 85 struct timespec update_start, update_end; 86 clock_gettime(CLOCK_MONOTONIC, &update_start); 87 88 handle_pending_events(&state); 89 90 #ifdef RTS_DEBUG 91 dbglog(INFO, "input: W/A/S/D: %d/%d/%d/%d , X/Y/Z: %03d/%03d/%03d\n", 92 state.video.input.keyboard.buttons[KEYBOARD_UP].was_pressed, 93 state.video.input.keyboard.buttons[KEYBOARD_LEFT].was_pressed, 94 state.video.input.keyboard.buttons[KEYBOARD_DOWN].was_pressed, 95 state.video.input.keyboard.buttons[KEYBOARD_RIGHT].was_pressed, 96 state.video.input.mouse.x, 97 state.video.input.mouse.y, 98 state.video.input.mouse.z); 99 #endif 100 101 state.rts_api.update(&thread, &game_memory, 102 &state.platform_api, 103 &state.video.input, 104 state.video.width, state.video.height, dt); 105 106 clock_gettime(CLOCK_MONOTONIC, &update_end); 107 108 struct timespec sample_start, sample_end; 109 clock_gettime(CLOCK_MONOTONIC, &sample_start); 110 111 if (state.audio.enabled) { 112 update_expected_frames(&state.audio, dt); 113 114 state.rts_api.sample(&thread, &game_memory, 115 &state.platform_api, 116 &state.audio.audio_buffer, 117 dt); 118 119 play_audio(&state.audio); 120 } else { 121 reset_audio(&state.audio); 122 } 123 124 clock_gettime(CLOCK_MONOTONIC, &sample_end); 125 126 struct timespec render_start, render_end; 127 clock_gettime(CLOCK_MONOTONIC, &render_start); 128 129 state.rts_api.render(&thread, &game_memory, 130 &state.platform_api, 131 &state.renderer_api, state.video.renderer, 132 state.video.width, state.video.height, dt); 133 134 draw_frame(&state.video); 135 136 clock_gettime(CLOCK_MONOTONIC, &render_end); 137 138 clock_gettime(CLOCK_MONOTONIC, &frame_end); 139 140 u32 elapsed_us = linux_elapsed_ns(&frame_start, &frame_end) / 1000; 141 if (elapsed_us < target_us_per_frame) { 142 struct timespec delay = { 143 .tv_sec = 0, 144 .tv_nsec = (target_us_per_frame - elapsed_us) * 1000, 145 }; 146 147 while (nanosleep(&delay, &delay) < 0); 148 149 clock_gettime(CLOCK_MONOTONIC, &frame_end); 150 elapsed_us = linux_elapsed_ns(&frame_start, &frame_end) / 1000; 151 } else { 152 /* TODO: missed frame */ 153 } 154 155 dt = (f32) elapsed_us / USECS; 156 157 #ifdef RTS_DEBUG 158 dbglog(INFO, "timings: " 159 "update: %" PRIu64 " ns, " 160 "sample: %" PRIu64 " ns, " 161 "render: %" PRIu64 " ns, " 162 "total: %" PRIu64 " ns, " 163 "\n", 164 linux_elapsed_ns(&update_start, &update_end), 165 linux_elapsed_ns(&sample_start, &sample_end), 166 linux_elapsed_ns(&render_start, &render_end), 167 linux_elapsed_ns(&frame_start, &frame_end)); 168 #endif 169 } 170 171 exit(EXIT_SUCCESS); 172 } 173 174 internal u64 175 linux_elapsed_ns(struct timespec *restrict start, struct timespec *restrict end) 176 { 177 return (end->tv_sec - start->tv_sec) * NSECS + (end->tv_nsec - start->tv_nsec); 178 } 179 180 #define LOADSYM(val, T, lib, sym) ((val) = (T *) dlsym(lib, sym)) 181 182 internal b32 183 load_game_api(char const *path, struct rts_api *api) 184 { 185 void *library = dlopen(path, RTLD_NOW); 186 if (!library) 187 return false; 188 189 LOADSYM(api->init, rts_api_init_t, library, "rts_init"); 190 LOADSYM(api->update, rts_api_update_t, library, "rts_update"); 191 LOADSYM(api->render, rts_api_render_t, library, "rts_render"); 192 LOADSYM(api->sample, rts_api_sample_t, library, "rts_sample"); 193 LOADSYM(api->free, rts_api_free_t, library, "rts_free"); 194 195 return true; 196 } 197 198 internal b32 199 init_memory(struct memory *memory, u64 capacity) 200 { 201 #ifdef RTS_DEBUG 202 void *base = (void *) (2 * TiB); 203 #else 204 void *base = NULL; 205 #endif 206 207 int prot = PROT_READ | PROT_WRITE; 208 int flags = MAP_PRIVATE | MAP_ANONYMOUS; 209 210 #ifdef RTS_DEBUG 211 flags |= MAP_FIXED; 212 #endif 213 214 memory->len = capacity; 215 memory->ptr = mmap(base, memory->len, prot, flags, -1, 0); 216 if (memory->ptr == MAP_FAILED) 217 return false; 218 219 madvise(memory->ptr, memory->len, MADV_HUGEPAGE); 220 221 return true; 222 } 223 224 internal void 225 handle_pending_events(struct linux_state *state) 226 { 227 wl_display_dispatch_pending(state->video.display); 228 } 229 230 internal b32 231 init_audio(struct arena *arena, struct linux_audio *audio, 232 u32 period_samples, u32 buffer_samples) 233 { 234 audio->enabled = false; 235 236 int err; 237 if ((err = snd_pcm_open(&audio->handle, "default", 238 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { 239 dbglog(WARN, "snd_pcm_open: %s\n", snd_strerror(err)); 240 return false; 241 } 242 243 snd_pcm_hw_params_t *hw_params = alloca(snd_pcm_hw_params_sizeof()); 244 assert(hw_params); 245 246 snd_pcm_hw_params_any(audio->handle, hw_params); 247 snd_pcm_hw_params_set_access(audio->handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); 248 snd_pcm_hw_params_set_format(audio->handle, hw_params, SND_PCM_FORMAT_S32_LE); 249 snd_pcm_hw_params_set_channels(audio->handle, hw_params, RTS_AUDIO_CHANNELS); 250 snd_pcm_hw_params_set_rate(audio->handle, hw_params, RTS_AUDIO_SAMPLE_RATE, 0); 251 snd_pcm_hw_params_set_period_size(audio->handle, hw_params, period_samples, 0); 252 snd_pcm_hw_params_set_buffer_size(audio->handle, hw_params, buffer_samples); 253 254 if ((err = snd_pcm_hw_params(audio->handle, hw_params)) < 0) { 255 dbglog(WARN, "and_pcm_hw_params: %s\n", snd_strerror(err)); 256 return false; 257 } 258 259 int dir; 260 snd_pcm_hw_params_get_period_time(hw_params, &audio->period_us, &dir); 261 snd_pcm_hw_params_get_period_size(hw_params, &audio->period_frames, &dir); 262 snd_pcm_hw_params_get_buffer_size(hw_params, &audio->buffer_frames); 263 264 dbglog(INFO, "Audio period frames: %" PRIu64 ", %" PRIu32 " us\n", 265 audio->period_frames, audio->period_us); 266 dbglog(INFO, "Audio buffer frames: %" PRIu64 "\n", audio->buffer_frames); 267 268 audio->audio_buffer.buffer = ALLOC_ARRAY(arena, u8, RTS_AUDIO_FRAME_BYTES * audio->buffer_frames); 269 assert(audio->audio_buffer.buffer); 270 271 audio->audio_buffer.channels = RTS_AUDIO_CHANNELS; 272 audio->audio_buffer.sample_rate = RTS_AUDIO_SAMPLE_RATE; 273 audio->audio_buffer.sample_bits = RTS_AUDIO_SAMPLE_BITS; 274 audio->audio_buffer.frames = audio->buffer_frames; 275 276 audio->enabled = true; 277 278 return true; 279 } 280 281 internal void 282 reset_audio(struct linux_audio *audio) 283 { 284 if (snd_pcm_prepare(audio->handle) == 0) 285 audio->enabled = true; 286 } 287 288 internal void 289 update_expected_frames(struct linux_audio *audio, f32 dt) 290 { 291 snd_pcm_sframes_t total = audio->buffer_frames; 292 snd_pcm_sframes_t expected = RTS_AUDIO_SAMPLE_RATE * dt; 293 294 snd_pcm_sframes_t avail, delay; 295 if (snd_pcm_avail_delay(audio->handle, &avail, &delay) < 0) { 296 audio->enabled = false; 297 return; 298 } 299 300 dbglog(DEBUG, "total: %ld, expected: %ld, avail: %ld, pending: %ld\n", 301 total, expected, avail, delay); 302 303 audio->audio_buffer.frames = avail; 304 } 305 306 internal void 307 play_audio(struct linux_audio *audio) 308 { 309 s32 written_frames = snd_pcm_writei(audio->handle, 310 audio->audio_buffer.buffer, 311 audio->audio_buffer.frames); 312 313 if (written_frames < 0) { 314 written_frames = snd_pcm_recover(audio->handle, written_frames, 0); 315 } if ((u32) written_frames < audio->audio_buffer.frames) { 316 dbglog(DEBUG, "underrun! wrote %d/%" PRIu32 " samples\n", 317 written_frames, audio->audio_buffer.frames); 318 } 319 320 snd_pcm_sframes_t total = audio->buffer_frames, avail, delay; 321 snd_pcm_avail_delay(audio->handle, &avail, &delay); 322 dbglog(DEBUG, "total: %ld, written: %d, new avail: %ld, new pending: %ld\n", 323 total, written_frames, avail, delay); 324 } 325 326 internal void 327 process_button_input(struct button_input *buttons, s32 id, b32 is_pressed) 328 { 329 struct button_input *input = buttons + id; 330 331 if (input->was_pressed != is_pressed) 332 input->half_transition_count++; 333 334 input->was_pressed = is_pressed; 335 } 336 337 internal void 338 process_button(struct input *input, u32 button, b32 is_pressed) 339 { 340 switch (button) { 341 case BTN_LEFT: 342 process_button_input(input->mouse.buttons, MOUSE_L, is_pressed); 343 break; 344 345 case BTN_RIGHT: 346 process_button_input(input->mouse.buttons, MOUSE_R, is_pressed); 347 break; 348 349 case BTN_MIDDLE: 350 process_button_input(input->mouse.buttons, MOUSE_M, is_pressed); 351 break; 352 353 case BTN_X: 354 process_button_input(input->mouse.buttons, MOUSE_X, is_pressed); 355 break; 356 357 case BTN_Y: 358 process_button_input(input->mouse.buttons, MOUSE_Y, is_pressed); 359 break; 360 } 361 } 362 363 internal void 364 wl_pointer_enter(void *data, struct wl_pointer *pointer, u32 serial, 365 struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) 366 { 367 struct linux_video *video = data; 368 video->input.mouse.x = wl_fixed_to_int(x); 369 video->input.mouse.y = wl_fixed_to_int(y); 370 } 371 372 internal void 373 wl_pointer_leave(void *data, struct wl_pointer *pointer, u32 serial, 374 struct wl_surface *surface) 375 { 376 struct linux_video *video = data; 377 video->input.mouse.x = video->input.mouse.y = video->input.mouse.z = 0; 378 } 379 380 internal void 381 wl_pointer_motion(void *data, struct wl_pointer *pointer, u32 serial, 382 wl_fixed_t x, wl_fixed_t y) 383 { 384 struct linux_video *video = data; 385 video->input.mouse.x = wl_fixed_to_int(x); 386 video->input.mouse.y = wl_fixed_to_int(y); 387 } 388 389 internal void 390 wl_pointer_button(void *data, struct wl_pointer *pointer, u32 serial, 391 u32 time, u32 button, u32 state) 392 { 393 struct linux_video *video = data; 394 process_button(&video->input, button, state); 395 } 396 397 internal void 398 wl_pointer_axis(void *data, struct wl_pointer *pointer, u32 serial, 399 u32 axis, wl_fixed_t value) 400 { 401 struct linux_video *video = data; 402 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) 403 video->input.mouse.z = wl_fixed_to_int(value); 404 } 405 406 internal const struct wl_pointer_listener wl_pointer_listener = { 407 .enter = wl_pointer_enter, 408 .leave = wl_pointer_leave, 409 .motion = wl_pointer_motion, 410 .button = wl_pointer_button, 411 .axis = wl_pointer_axis, 412 }; 413 414 internal void 415 process_keysym(struct input *input, xkb_keysym_t keysym, b32 is_pressed) 416 { 417 switch (keysym) { 418 case XKB_KEY_w: 419 case XKB_KEY_Up: 420 process_button_input(input->keyboard.buttons, KEYBOARD_UP, is_pressed); 421 break; 422 423 case XKB_KEY_a: 424 case XKB_KEY_Left: 425 process_button_input(input->keyboard.buttons, KEYBOARD_LEFT, is_pressed); 426 break; 427 428 case XKB_KEY_s: 429 case XKB_KEY_Down: 430 process_button_input(input->keyboard.buttons, KEYBOARD_DOWN, is_pressed); 431 break; 432 433 case XKB_KEY_d: 434 case XKB_KEY_Right: 435 process_button_input(input->keyboard.buttons, KEYBOARD_RIGHT, is_pressed); 436 break; 437 438 case XKB_KEY_q: 439 process_button_input(input->keyboard.buttons, KEYBOARD_Q, is_pressed); 440 break; 441 442 case XKB_KEY_e: 443 process_button_input(input->keyboard.buttons, KEYBOARD_E, is_pressed); 444 break; 445 446 case XKB_KEY_r: 447 process_button_input(input->keyboard.buttons, KEYBOARD_R, is_pressed); 448 break; 449 450 case XKB_KEY_t: 451 process_button_input(input->keyboard.buttons, KEYBOARD_T, is_pressed); 452 break; 453 454 case XKB_KEY_i: 455 process_button_input(input->keyboard.buttons, KEYBOARD_I, is_pressed); 456 break; 457 458 case XKB_KEY_j: 459 process_button_input(input->keyboard.buttons, KEYBOARD_J, is_pressed); 460 break; 461 462 case XKB_KEY_k: 463 process_button_input(input->keyboard.buttons, KEYBOARD_K, is_pressed); 464 break; 465 466 case XKB_KEY_l: 467 process_button_input(input->keyboard.buttons, KEYBOARD_L, is_pressed); 468 break; 469 470 case XKB_KEY_space: 471 process_button_input(input->keyboard.buttons, KEYBOARD_SPACE, is_pressed); 472 break; 473 474 case XKB_KEY_Escape: 475 process_button_input(input->keyboard.buttons, KEYBOARD_ESCAPE, is_pressed); 476 break; 477 } 478 } 479 480 internal void 481 wl_keyboard_keymap(void *data ,struct wl_keyboard *keyboard, u32 format, 482 int fd, u32 size) 483 { 484 assert(format = WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); 485 486 struct linux_video *video = data; 487 488 char *mapped = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); 489 assert(mapped != MAP_FAILED); 490 491 xkb_keymap_unref(video->xkb_keymap); 492 video->xkb_keymap = xkb_keymap_new_from_string(video->xkb_context, mapped, 493 XKB_KEYMAP_FORMAT_TEXT_V1, 494 XKB_KEYMAP_COMPILE_NO_FLAGS); 495 496 munmap(mapped, size); 497 close(fd); 498 499 xkb_state_unref(video->xkb_state); 500 video->xkb_state = xkb_state_new(video->xkb_keymap); 501 } 502 503 internal void 504 wl_keyboard_enter(void *data, struct wl_keyboard *keyboard, u32 serial, 505 struct wl_surface *surface, struct wl_array *keys) 506 { 507 struct linux_video *video = data; 508 509 u32 *key; 510 wl_array_for_each(key, keys) { 511 xkb_keysym_t sym = xkb_state_key_get_one_sym(video->xkb_state, *key + 8); 512 process_keysym(&video->input, sym, 1); 513 } 514 } 515 516 internal void 517 wl_keyboard_leave(void *data, struct wl_keyboard *keyboard, u32 serial, 518 struct wl_surface *surface) 519 { 520 } 521 522 internal void 523 wl_keyboard_key(void *data, struct wl_keyboard *keyboard, u32 serial, 524 u32 time, u32 key, u32 state) 525 { 526 struct linux_video *video = data; 527 xkb_keysym_t sym = xkb_state_key_get_one_sym(video->xkb_state, key + 8); 528 process_keysym(&video->input, sym, state); 529 } 530 531 internal void 532 wl_keyboard_modifiers(void *data, struct wl_keyboard *keyboard, u32 serial, 533 u32 depressed, u32 latched, u32 locked, u32 group) 534 { 535 struct linux_video *video = data; 536 xkb_state_update_mask(video->xkb_state, depressed, latched, locked, 0, 0, group); 537 } 538 539 internal const struct wl_keyboard_listener wl_keyboard_listener = { 540 .keymap = wl_keyboard_keymap, 541 .enter = wl_keyboard_enter, 542 .leave = wl_keyboard_leave, 543 .key = wl_keyboard_key, 544 .modifiers = wl_keyboard_modifiers, 545 }; 546 547 internal void 548 wl_seat_capabilities(void *data, struct wl_seat *seat, u32 caps) 549 { 550 struct linux_video *video = data; 551 552 b32 has_pointer = TESTBITS(caps, WL_SEAT_CAPABILITY_POINTER); 553 if (has_pointer && !video->pointer) { 554 video->pointer = wl_seat_get_pointer(seat); 555 wl_pointer_add_listener(video->pointer, &wl_pointer_listener, video); 556 } else if (!has_pointer && video->pointer) { 557 wl_pointer_release(video->pointer); 558 video->pointer = NULL; 559 } 560 561 b32 has_keyboard = TESTBITS(caps, WL_SEAT_CAPABILITY_KEYBOARD); 562 if (has_keyboard && !video->keyboard) { 563 video->keyboard = wl_seat_get_keyboard(seat); 564 wl_keyboard_add_listener(video->keyboard, &wl_keyboard_listener, video); 565 } else if (!has_keyboard && video->keyboard) { 566 wl_keyboard_release(video->keyboard); 567 video->keyboard = NULL; 568 } 569 } 570 571 internal void 572 wl_seat_name(void *data, struct wl_seat *seat, char const *name) 573 { 574 } 575 576 internal const struct wl_seat_listener wl_seat_listener = { 577 .capabilities = wl_seat_capabilities, 578 .name = wl_seat_name, 579 }; 580 581 internal void 582 xdg_wm_base_handle_ping(void *data, struct xdg_wm_base *wm_base, u32 serial) 583 { 584 xdg_wm_base_pong(wm_base, serial); 585 } 586 587 internal const struct xdg_wm_base_listener xdg_wm_base_listener = { 588 .ping = xdg_wm_base_handle_ping, 589 }; 590 591 internal void 592 xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, u32 serial) 593 { 594 xdg_surface_ack_configure(xdg_surface, serial); 595 } 596 597 internal const struct xdg_surface_listener xdg_surface_listener = { 598 .configure = xdg_surface_handle_configure, 599 }; 600 601 internal void 602 xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel, s32 width, s32 height, struct wl_array *states) 603 { 604 struct linux_video *video = data; 605 video->width = width ? width : RTS_VIDEO_HRES; 606 video->height = height ? height : RTS_VIDEO_HRES; 607 wl_egl_window_resize(video->egl_window, video->width, video->height, 0, 0); 608 } 609 610 internal void 611 xdg_toplevel_handle_close(void *data, struct xdg_toplevel *toplevel) 612 { 613 state.running = false; 614 } 615 616 internal void 617 xdg_toplevel_handle_configure_bounds(void *data, struct xdg_toplevel *toplevel, s32 width, s32 height) 618 { 619 struct linux_video *video = data; 620 video->width = width ? width : RTS_VIDEO_HRES; 621 video->height = height ? height : RTS_VIDEO_HRES; 622 wl_egl_window_resize(video->egl_window, video->width, video->height, 0, 0); 623 } 624 625 internal void 626 xdg_toplevel_handle_wm_capabilities(void *data, struct xdg_toplevel *toplevel, struct wl_array *caps) 627 { 628 // TODO: handle me, do we care about any special capabilities? 629 } 630 631 internal const struct xdg_toplevel_listener xdg_toplevel_listener = { 632 .configure = xdg_toplevel_handle_configure, 633 .close = xdg_toplevel_handle_close, 634 .configure_bounds = xdg_toplevel_handle_configure_bounds, 635 .wm_capabilities = xdg_toplevel_handle_wm_capabilities, 636 }; 637 638 internal void 639 wl_registry_handle_global(void *data, struct wl_registry *registry, u32 name, char const *interface, u32 version) 640 { 641 struct linux_video *video = data; 642 643 dbglog(DEBUG, "wl: interface: %u, name: %s, version: %u\n", name, interface, version); 644 645 if (strcmp(interface, wl_compositor_interface.name) == 0) { 646 video->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 6); 647 } else if (strcmp(interface, wl_seat_interface.name) == 0) { 648 video->seat = wl_registry_bind(registry, name, &wl_seat_interface, 3); 649 wl_seat_add_listener(video->seat, &wl_seat_listener, data); 650 } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { 651 video->wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 6); 652 xdg_wm_base_add_listener(video->wm_base, &xdg_wm_base_listener, data); 653 } 654 } 655 656 internal void 657 wl_registry_handle_global_remove(void *data, struct wl_registry *registry, u32 name) 658 { 659 } 660 661 internal const struct wl_registry_listener wl_registry_listener = { 662 .global = wl_registry_handle_global, 663 .global_remove = wl_registry_handle_global_remove, 664 }; 665 666 internal b32 667 init_video(struct arena *arena, struct linux_video *video, char const *title) 668 { 669 video->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 670 671 if (!(video->display = wl_display_connect(NULL))) { 672 dbglog(WARN, "wl_display_connect: could not connect\n"); 673 return false; 674 } 675 676 video->registry = wl_display_get_registry(video->display); 677 wl_registry_add_listener(video->registry, &wl_registry_listener, video); 678 wl_display_roundtrip(video->display); 679 680 assert(video->compositor); 681 assert(video->seat); 682 assert(video->wm_base); 683 684 video->surface = wl_compositor_create_surface(video->compositor); 685 video->egl_window = wl_egl_window_create(video->surface, RTS_VIDEO_HRES, RTS_VIDEO_VRES); 686 687 video->xdg_surface = xdg_wm_base_get_xdg_surface(video->wm_base, video->surface); 688 xdg_surface_add_listener(video->xdg_surface, &xdg_surface_listener, NULL); 689 690 video->xdg_toplevel = xdg_surface_get_toplevel(video->xdg_surface); 691 xdg_toplevel_add_listener(video->xdg_toplevel, &xdg_toplevel_listener, video); 692 693 xdg_toplevel_set_app_id(video->xdg_toplevel, title); 694 xdg_toplevel_set_title(video->xdg_toplevel, title); 695 696 wl_surface_commit(video->surface); 697 wl_display_roundtrip(video->display); 698 699 video->egl_display = eglGetDisplay(video->display); 700 701 EGLint major, minor; 702 eglInitialize(video->egl_display, &major, &minor); 703 704 dbglog(INFO, "EGL version: %d.%d\n", major, minor); 705 706 eglBindAPI(EGL_OPENGL_API); 707 708 EGLint attrs[] = { 709 EGL_RED_SIZE, 8, 710 EGL_GREEN_SIZE, 8, 711 EGL_BLUE_SIZE, 8, 712 EGL_ALPHA_SIZE, 8, 713 EGL_NONE, 714 }; 715 716 EGLConfig config; 717 EGLint num_config; 718 eglChooseConfig(video->egl_display, attrs, &config, 1, &num_config); 719 720 video->egl_context = eglCreateContext(video->egl_display, config, EGL_NO_CONTEXT, NULL); 721 722 video->egl_surface = eglCreateWindowSurface(video->egl_display, config, video->egl_window, NULL); 723 if (video->egl_surface == EGL_NO_SURFACE) { 724 dbglog(WARN, "Failed to create surface!\n"); 725 return false; 726 } 727 728 eglMakeCurrent(video->egl_display, video->egl_surface, video->egl_surface, video->egl_context); 729 eglSwapInterval(video->egl_display, 0); // make eglSwapBuffers non-blocking, at cost of frame tearing 730 731 if (!(video->renderer = create_renderer(arena, video->width, video->height))) { 732 dbglog(DEBUG, "Failed to initialise egl renderer\n"); 733 return false; 734 } 735 736 return true; 737 } 738 739 internal void 740 draw_frame(struct linux_video *video) 741 { 742 eglSwapBuffers(video->egl_display, video->egl_surface); 743 } 744 745 /* platform api 746 * =========================================================================== 747 */ 748 749 #ifdef RTS_DEBUG 750 751 DEBUG_PLATFORM_READ_FILE(DEBUG_platform_read_file) 752 { 753 return (struct DEBUG_platform_read_file_result) {0}; 754 } 755 756 DEBUG_PLATFORM_WRITE_FILE(DEBUG_platform_write_file) 757 { 758 return false; 759 } 760 761 DEBUG_PLATFORM_FREE_FILE(DEBUG_platform_free_file) 762 { 763 } 764 765 #endif /* RTS_DEBUG */ 766 767 PLATFORM_PRINTF(platform_printf) 768 { 769 va_list ap; 770 va_start(ap, fmt); 771 size_t res = vprintf(fmt, ap); 772 va_end(ap); 773 774 return res; 775 } 776 777 internal void 778 load_platform_api(struct platform_api *api) 779 { 780 #ifdef RTS_DEBUG 781 api->DEBUG_read_file = DEBUG_platform_read_file; 782 api->DEBUG_write_file = DEBUG_platform_write_file; 783 api->DEBUG_free_file = DEBUG_platform_free_file; 784 #endif 785 api->printf = platform_printf; 786 } 787 788 #include "renderer.c"