summaryrefslogtreecommitdiff
path: root/src/linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/linux.c')
-rw-r--r--src/linux.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/src/linux.c b/src/linux.c
new file mode 100644
index 0000000..474d430
--- /dev/null
+++ b/src/linux.c
@@ -0,0 +1,348 @@
+#include "GL/glew.h"
+#include "SDL3/SDL.h"
+
+#include "prge.h"
+
+#include "crrn.h"
+#include "crrn.c"
+
+typedef struct {
+ SDL_AudioSpec spec;
+ SDL_AudioStream *stream;
+} SDLSound;
+
+typedef struct {
+ SDLSound sdlsounds[MAX_SOUNDS_PLAYING];
+ SDL_AudioDeviceID audio_device;
+ SDL_Window *window;
+ SDL_GLContext glctx;
+} SDLContext;
+
+static SDLContext sdlctx;
+
+i32 init_sdl(prge_window_t *window)
+{
+ if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
+ printf("error: sdl init: %s\n", SDL_GetError());
+ return 0;
+ }
+
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+ if (window->flags & WINDOW_MULTISAMPLE)
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
+ u32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
+ sdlctx.window = SDL_CreateWindow(window->name, window->width, window->height, flags);
+ if (!sdlctx.window) {
+ printf("error: sdl create window: %s\n", SDL_GetError());
+ return 0;
+ }
+ SDL_GetWindowSize(sdlctx.window, &window->width, &window->height);
+
+ if (!(sdlctx.glctx = SDL_GL_CreateContext(sdlctx.window))) {
+ printf("error: sdl create gl context: %s\n", SDL_GetError());
+ return 0;
+ }
+ if (!SDL_GL_MakeCurrent(sdlctx.window, sdlctx.glctx)) {
+ printf("error: sdl make context current: %s\n", SDL_GetError());
+ return 0;
+ }
+
+ if (SDL_GL_SetSwapInterval(0))
+ printf("info: vsync disabled\n");
+ else
+ printf("warning: failed to disable vsync\n");
+
+ u32 error = glewInit();
+ if (error != GLEW_OK) {
+ printf("error: glew: %s\n", glewGetErrorString(error));
+ return 0;
+ }
+ if (window->flags & WINDOW_DEPTH_TEST)
+ glEnable(GL_DEPTH_TEST);
+ if (window->flags & WINDOW_MULTISAMPLE)
+ glEnable(GL_MULTISAMPLE);
+
+ sdlctx.audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, 0);
+ if (!sdlctx.audio_device) {
+ printf("error: sdl: couldn't open audio device\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+void process_mouse_pos(input_t *input, SDL_MouseMotionEvent *event, prge_window_t window)
+{
+ input->mouse.pos = (v2){(f32)event->x, window.height - (f32)event->y};
+ if (input->mouse.first)
+ input->mouse.first = 0;
+ v2 offset = {(f32)event->xrel, (f32)event->yrel};
+ input->mouse.offset = v2_add(input->mouse.offset, offset);
+}
+
+void process_key(i32 down, prge_key_t *key)
+{
+ key->state = (down ? key_state_press : key_state_release);
+}
+
+void process_keyboard(input_t *input, SDL_KeyboardEvent *keyev)
+{
+ switch (keyev->scancode) {
+ case SDL_SCANCODE_D:
+ process_key(keyev->down, &input->move_right);
+ break;
+ case SDL_SCANCODE_W:
+ process_key(keyev->down, &input->move_forward);
+ break;
+ case SDL_SCANCODE_A:
+ process_key(keyev->down, &input->move_left);
+ break;
+ case SDL_SCANCODE_S:
+ process_key(keyev->down, &input->move_backward);
+ break;
+ case SDL_SCANCODE_E:
+ process_key(keyev->down, &input->move_up);
+ break;
+ case SDL_SCANCODE_Q:
+ process_key(keyev->down, &input->move_down);
+ break;
+ case SDL_SCANCODE_SPACE:
+ process_key(keyev->down, &input->jump);
+ break;
+ case SDL_SCANCODE_RIGHT:
+ process_key(keyev->down, &input->action_right);
+ break;
+ case SDL_SCANCODE_UP:
+ process_key(keyev->down, &input->action_up);
+ break;
+ case SDL_SCANCODE_LEFT:
+ process_key(keyev->down, &input->action_left);
+ break;
+ case SDL_SCANCODE_DOWN:
+ process_key(keyev->down, &input->action_down);
+ break;
+ case SDL_SCANCODE_ESCAPE:
+ process_key(keyev->down, &input->exit);
+ break;
+ default:
+ break;
+ }
+}
+
+void process_mouse_buttons(input_t *input, SDL_MouseButtonEvent *buttonev)
+{
+ if (buttonev->button == SDL_BUTTON_LEFT)
+ process_key(buttonev->down, &input->mouse.left);
+ else if (buttonev->button == SDL_BUTTON_RIGHT)
+ process_key(buttonev->down, &input->mouse.right);
+}
+
+void handle_events(prge_context_t *ctx)
+{
+ SDL_Event event;
+
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_EVENT_KEY_DOWN:
+ case SDL_EVENT_KEY_UP:
+ process_keyboard(&ctx->input, &event.key);
+ break;
+ case SDL_EVENT_MOUSE_MOTION:
+ process_mouse_pos(&ctx->input, &event.motion, ctx->window);
+ break;
+ case SDL_EVENT_MOUSE_BUTTON_DOWN:
+ case SDL_EVENT_MOUSE_BUTTON_UP:
+ process_mouse_buttons(&ctx->input, &event.button);
+ break;
+ case SDL_EVENT_WINDOW_RESIZED:
+ ctx->window.width = event.window.data1;
+ ctx->window.height = event.window.data2;
+ break;
+ case SDL_EVENT_QUIT:
+ ctx->should_close = 1;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+f32 get_elapsed_seconds(u64 current, u64 last)
+{
+ f32 elapsed = ((f32)current-(f32)last)/1000.0f;
+ return elapsed;
+}
+
+f32 lock_framerate(u64 last, f32 target)
+{
+ f32 elapsed = get_elapsed_seconds(SDL_GetTicks(), last)*1000.0f;
+ if (elapsed < target) {
+ f32 sleep = target-elapsed;
+ if (sleep > 0.0f)
+ SDL_Delay(sleep);
+ do {
+ elapsed = get_elapsed_seconds(SDL_GetTicks(), last)*1000.0f;
+ } while (elapsed < target);
+ }
+ f32 dt = get_elapsed_seconds(SDL_GetTicks(), last);
+ return dt;
+}
+
+#include "stb_vorbis.c"
+
+i32 load_vorbis(arena_t *arena, sound_t *sounds, const char *filename)
+{
+ sound_t sound;
+ sound.name = filename;
+
+ i32 i = find_sound(sounds, sound.name);
+ if (i != -1) {
+ printf("info: \"%s\" already loaded\n", sound.name);
+ return i;
+ }
+
+ /* TODO(pryazha): Custom loader with arenas (or provide allocator to stb) */
+ i16 *output;
+ i32 samples = stb_vorbis_decode_filename(filename, &sound.channels,
+ &sound.freq, &output);
+ if (samples == -1) {
+ printf("error: \"%s\" failed to open\n", filename);
+ return -1;
+ }
+
+ sound.bps = sizeof(i16);
+ sound.size = sound.channels*samples*sound.bps;
+ sound.data = push_arena(arena, sound.size);
+ prb_memmove(sound.data, output, sound.size);
+ free(output);
+
+ i = load_sound(sounds, sound);
+ if (i == -1) {
+ pop_arena(arena, sound.size);
+ return i;
+ }
+
+ printf("info: \"%s\" loaded successfully\n", filename);
+
+ return i;
+}
+
+void play_sound(sound_t *sounds, sound_queue_t *queue, sound_queue_node_t *nodes, i32 id)
+{
+ if ((id == -1) && (id >= MAX_SOUNDS_LOADED)) {
+ printf("error: invalid sound id = %d\n", id);
+ return;
+ }
+
+ if (!sounds[id].data) {
+ printf("error: sound with id = %d doesn't exist\n", id);
+ return;
+ }
+
+ sound_t *sound = sounds+id;
+
+ if (!enqueue_sound(queue, nodes, sound)) {
+ printf("error: failed to enqueue the sound \"%s\"\n", sound->name);
+ return;
+ }
+
+ printf("info: queue content:\n");
+ for (sound_queue_node_t *node = queue->first; node; node = node->next) {
+ printf("%s%s", node->sound->name, (node->next ? " -> " : "\n"));
+ }
+}
+
+SDLSound *get_empty_sdl_sound()
+{
+ for (i32 i = 0; i < MAX_SOUNDS_PLAYING; i++) {
+ SDLSound *sdlsound = sdlctx.sdlsounds+i;
+ if (!sdlsound->stream)
+ return sdlsound;
+ if (SDL_GetAudioStreamAvailable(sdlsound->stream) == 0)
+ return sdlsound;
+ }
+ return 0;
+}
+
+void update_sdl_sounds(sound_queue_t *queue)
+{
+ SDLSound *sdlsound;
+ sound_t *sound;
+ SDL_AudioSpec spec;
+
+ while ((sdlsound = get_empty_sdl_sound()) && (sound = dequeue_sound(queue))) {
+ switch (sound->bps) {
+ case 2:
+ spec.format = SDL_AUDIO_S16;
+ break;
+ default:
+ printf("error: sound: \"%s\". Unsupported audio format\n", sound->name);
+ return;
+ }
+ spec.channels = sound->channels;
+ spec.freq = sound->freq;
+
+ if (sdlsound->stream) {
+ if (!prb_memeq(&sdlsound->spec, &spec, sizeof(SDL_AudioSpec))) {
+ sdlsound->spec = spec;
+ SDL_SetAudioStreamFormat(sdlsound->stream, &sdlsound->spec, 0);
+ }
+ } else {
+ sdlsound->spec = spec;
+ sdlsound->stream = SDL_CreateAudioStream(&sdlsound->spec, 0);
+ if (!sdlsound->stream) {
+ printf("error: sailed to create audio stream\n");
+ return;
+ }
+ if (!SDL_BindAudioStream(sdlctx.audio_device, sdlsound->stream)) {
+ SDL_DestroyAudioStream(sdlsound->stream);
+ printf("error: failed to bind audio stream to device\n");
+ return;
+ }
+ }
+ printf("info: playing: \"%s\"\n", sound->name);
+ SDL_PutAudioStreamData(sdlsound->stream, sound->data, sound->size);
+ }
+}
+
+i32 main(void)
+{
+ // engine init
+ prge_context_t prgectx = init_prge();
+ u32 flags = WINDOW_DEPTH_TEST | WINDOW_MULTISAMPLE;
+ prgectx.window = (prge_window_t){800, 600, "prge example", flags};
+
+ f32 fps = 60.0f;
+ f32 mspf = 1000.0f/fps;
+
+ // sdl and opengl init
+ if (!init_sdl(&prgectx.window))
+ return 1;
+
+ // game init
+ game_context_t gctx = {0};
+ init(&prgectx, &gctx);
+
+ u64 last = SDL_GetTicks();
+ while (!prgectx.should_close) {
+ SDL_SetWindowRelativeMouseMode(sdlctx.window, prgectx.input.mouse.capture);
+
+ handle_events(&prgectx);
+
+ prgectx.input.dt = lock_framerate(last, mspf);
+ last = SDL_GetTicks();
+
+ pop_arena(&prgectx.frame_arena, prgectx.frame_arena.used);
+
+ glViewport(0, 0, prgectx.window.width, prgectx.window.height);
+ update_and_render(&prgectx, &gctx);
+ update_sdl_sounds(&prgectx.sound_queue);
+ update_input(&prgectx.input);
+
+ SDL_GL_SwapWindow(sdlctx.window);
+ }
+
+ return 0;
+}