From 2050c0e0576f05156f192aa4caf48834d2f28b14 Mon Sep 17 00:00:00 2001 From: fschildt Date: Fri, 22 Aug 2025 15:23:11 +0200 Subject: first commit --- src/os/linux/linux_file.c | 36 ++ src/os/linux/linux_library.c | 25 ++ src/os/linux/linux_memory.c | 38 +++ src/os/linux/linux_net_secure_stream.c | 579 +++++++++++++++++++++++++++++++++ src/os/linux/linux_sound.c | 269 +++++++++++++++ src/os/linux/linux_time.c | 23 ++ src/os/linux/linux_window.c | 304 +++++++++++++++++ src/os/os.h | 153 +++++++++ src/os/sdl/sdl_window.c | 171 ++++++++++ 9 files changed, 1598 insertions(+) create mode 100644 src/os/linux/linux_file.c create mode 100644 src/os/linux/linux_library.c create mode 100644 src/os/linux/linux_memory.c create mode 100644 src/os/linux/linux_net_secure_stream.c create mode 100644 src/os/linux/linux_sound.c create mode 100644 src/os/linux/linux_time.c create mode 100644 src/os/linux/linux_window.c create mode 100644 src/os/os.h create mode 100644 src/os/sdl/sdl_window.c (limited to 'src/os') diff --git a/src/os/linux/linux_file.c b/src/os/linux/linux_file.c new file mode 100644 index 0000000..5071f77 --- /dev/null +++ b/src/os/linux/linux_file.c @@ -0,0 +1,36 @@ +#include + +#include +#include +#include + + +char * +os_file_read_as_string(Arena *arena, char *filepath, size_t *len) +{ + int fd = open(filepath, O_RDONLY); + if (fd == -1) { + printf("can't open file %s for reading\n", filepath); + return 0; + } + + struct stat statbuf; + if (fstat(fd, &statbuf) == -1) { + printf("can't fstat file %s\n", filepath); + return 0; + } + + char *dest = arena_push(arena, statbuf.st_size+1); + ssize_t bytes_read = read(fd, dest, statbuf.st_size); + if (bytes_read != statbuf.st_size) { + printf("only read %ld/%ld bytes\n", statbuf.st_size, bytes_read); + arena->size_used -= statbuf.st_size+1; // Todo: cleanup + return 0; + } + dest[bytes_read] = '\0'; + *len = statbuf.st_size; + + return dest; +} + + diff --git a/src/os/linux/linux_library.c b/src/os/linux/linux_library.c new file mode 100644 index 0000000..bd9efe1 --- /dev/null +++ b/src/os/linux/linux_library.c @@ -0,0 +1,25 @@ +#include +#include +#include + +typedef void PlatformLibrary; + +PlatformLibrary *platform_library_open(const char *path) { + void *handle = dlopen(path, RTLD_NOW); + if (!handle) { + printf("could not open library %s\n", path); + return 0; + } + + return handle; +} + +void platform_library_close(PlatformLibrary *handle) { + dlclose(handle); +} + +void *platform_library_get_proc(PlatformLibrary *handle, const char *name) { + void *addr = dlsym(handle, name); + return addr; +} + diff --git a/src/os/linux/linux_memory.c b/src/os/linux/linux_memory.c new file mode 100644 index 0000000..668604e --- /dev/null +++ b/src/os/linux/linux_memory.c @@ -0,0 +1,38 @@ +#include +#include + +#include +#include +#include +#include +#include + +b32 +os_memory_allocate(OSMemory *memory, size_t size) +{ + // @Performance: Keep the fd of /dev/zero open for the whole runtime? + int fd = open("/dev/zero", O_RDWR); + if (fd == -1) { + printf("open() failed: %s\n", strerror(errno)); + return false; + } + + void *address = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + if (address == MAP_FAILED) { + printf("mmap failed\n"); + return false; + } + + close(fd); + + memory->size = size; + memory->p = address; + return true; +} + +void +os_memory_free(OSMemory *memory) +{ + munmap(memory->p, memory->size); +} + diff --git a/src/os/linux/linux_net_secure_stream.c b/src/os/linux/linux_net_secure_stream.c new file mode 100644 index 0000000..cfacfc3 --- /dev/null +++ b/src/os/linux/linux_net_secure_stream.c @@ -0,0 +1,579 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct { + // space left for 512 - 64*2 - 2 = 382 for 4096-bit rsa and SHA-512 + + AesGcmKey aes_key_client_encrypt; // 32 bytes + AesGcmIv aes_iv_client_encrypt; // 12 bytes + + AesGcmKey aes_key_client_decrypt; // 32 bytes + AesGcmIv aes_iv_client_decrypt; // 12 bytes + + // space left for 382 - 2*32 - 2*12 = 294 for 4096-bit rsa and SHA-512 + + u8 client_random[256]; +} CM_Handshake; + + +typedef struct { + u8 client_random[256]; +} SM_Handshake; + + +typedef struct { + int fd; + OSNetSecureStreamStatus status; + + EVP_PKEY *rsa_key; + + AesGcmKey recv_aes_key; // recvd with CM_Handshake + AesGcmIv recv_aes_iv; // recvd_with CM_Handshake + AesGcmHeader recv_aes_header; // recvd with each aes message + b32 recv_aes_header_initialized; + size_t recv_ciphertext_size_filled; + u8 recv_ciphertext[1408]; + + size_t recv_plaintext_size_filled; + size_t recv_plaintext_size_processed; + u8 recv_plaintext[1408]; + + AesGcmKey send_aes_key; // recvd with CM_Handshake + AesGcmIv send_aes_iv; // recvd with CM_Handshake + size_t send_ciphertext_size_used; + u8 send_ciphertext[1408]; +} OSNetSecureStream; + + +internal_var u32 s_max_secure_stream_count; +internal_var OSNetSecureStream *s_secure_streams; + +internal_var u32 s_free_id_count; +internal_var u32 *s_free_ids; + + + +OSNetSecureStreamStatus +os_net_secure_stream_get_status(u32 id) +{ + return s_secure_streams[id].status; +} + +internal_fn b32 +recv_and_decrypt_aes_package(OSNetSecureStream *secure_stream) +{ + i64 size_to_recv; + i64 size_recvd; + + // recv aes header + if (!secure_stream->recv_aes_header_initialized) { + void *dest = secure_stream->recv_ciphertext + secure_stream->recv_ciphertext_size_filled; + size_to_recv = sizeof(AesGcmHeader) - secure_stream->recv_ciphertext_size_filled; + size_recvd = recv(secure_stream->fd, dest, size_to_recv, 0); + if (size_recvd < 0) { + if (errno != EAGAIN) { + printf("error: recv aes header failed with errno = %d\n", errno); + secure_stream->status = OS_NET_SECURE_STREAM_ERROR; + } + return false; + } + else if (size_recvd == 0) { + secure_stream->status = OS_NET_SECURE_STREAM_DISCONNECTED; + return false; + } + else if (size_recvd < size_to_recv) { + secure_stream->recv_ciphertext_size_filled += size_recvd; + return false; + } + + AesGcmHeader *aes_header = (AesGcmHeader*)secure_stream->recv_ciphertext; + if (aes_header->payload_size > sizeof(secure_stream->recv_ciphertext)) { + printf("error: aes header has invalid payload size\n"); + secure_stream->recv_ciphertext_size_filled = 0; + secure_stream->status = OS_NET_SECURE_STREAM_ERROR; + return false; + } + + secure_stream->recv_aes_header = *(AesGcmHeader*)(secure_stream->recv_ciphertext); + secure_stream->recv_aes_header_initialized = true; + secure_stream->recv_ciphertext_size_filled = 0; + } + + + // recv aes payload + size_to_recv = secure_stream->recv_aes_header.payload_size - secure_stream->recv_ciphertext_size_filled; + size_recvd = recv(secure_stream->fd, secure_stream->recv_ciphertext, size_to_recv, 0); + if (size_recvd < 0) { + if (errno != EAGAIN) { + printf("error: recv aes payload failed with errno = %d\n", errno); + secure_stream->status = OS_NET_SECURE_STREAM_ERROR; + } + return false; + } + else if (size_recvd == 0) { + secure_stream->status = OS_NET_SECURE_STREAM_DISCONNECTED; + return false; + } + else if (size_recvd < size_to_recv) { + secure_stream->recv_ciphertext_size_filled += size_recvd; + return false; + } + + secure_stream->recv_ciphertext_size_filled += size_recvd; + + + // decrypt + b32 decrypted = aes_gcm_decrypt(&secure_stream->recv_aes_key, + &secure_stream->recv_aes_iv, + secure_stream->recv_plaintext, + secure_stream->recv_ciphertext, + secure_stream->recv_ciphertext_size_filled, + secure_stream->recv_aes_header.tag, + sizeof(secure_stream->recv_aes_header.tag)); + if (!decrypted) { + secure_stream->status = OS_NET_SECURE_STREAM_ERROR; + return false; + } + + secure_stream->recv_plaintext_size_filled = secure_stream->recv_ciphertext_size_filled; + secure_stream->recv_aes_header_initialized = false; + secure_stream->recv_plaintext_size_processed = 0; + secure_stream->recv_ciphertext_size_filled = 0; + + return true; +} + + +i64 +os_net_secure_stream_recv(u32 id, u8 *buff, size_t size) +{ + OSNetSecureStream *secure_stream = &s_secure_streams[id]; + if (secure_stream->status != OS_NET_SECURE_STREAM_CONNECTED) { + return -1; + } + + size_t size_delivered = 0; + while (size_delivered < size) { + size_t plaintext_size_avail = secure_stream->recv_plaintext_size_filled - secure_stream->recv_plaintext_size_processed; + if (plaintext_size_avail > 0) { + u8 *src = secure_stream->recv_plaintext + secure_stream->recv_plaintext_size_processed; + u8 *dest = buff + size_delivered; + size_t size_to_copy = MIN(size, plaintext_size_avail); + memcpy(dest, src, size_to_copy); + secure_stream->recv_plaintext_size_processed += size_to_copy; + size_delivered += size_to_copy; + } + else { + b32 recvd_and_decrypted = recv_and_decrypt_aes_package(secure_stream); + if (!recvd_and_decrypted) { + if (secure_stream->status == OS_NET_SECURE_STREAM_ERROR) { + return -1; + } + else { + break; + } + } + } + } + + return size_delivered; +} + +i64 +os_net_secure_stream_send(u32 id, u8 *buff, size_t size) +{ + OSNetSecureStream *secure_stream = &s_secure_streams[id]; + if (secure_stream->status != OS_NET_SECURE_STREAM_CONNECTED) { + return -1; + } + + + size_t size_original = size; + + while (size) { + AesGcmHeader *aes_header = (AesGcmHeader*)secure_stream->send_ciphertext; + void *aes_payload = secure_stream->send_ciphertext + sizeof(*aes_header); + i64 aes_payload_size = MIN(size, sizeof(secure_stream->send_ciphertext) - sizeof(*aes_header)); + + aes_header->payload_size = aes_payload_size; + b32 encrypted = aes_gcm_encrypt(&secure_stream->send_aes_key, + &secure_stream->send_aes_iv, + aes_payload, buff, aes_payload_size, + aes_header->tag, sizeof(aes_header->tag)); + if (!encrypted) { + secure_stream->status = OS_NET_SECURE_STREAM_ERROR; + return -1; + } + + size_t size_to_send = sizeof(*aes_header) + aes_payload_size; + i64 size_sent = send(secure_stream->fd, secure_stream->send_ciphertext, size_to_send, 0); + if (size_sent != size_to_send) { + printf("error: send only sent %ld/%ld bytes\n", size_to_send, size_sent); + secure_stream->status = OS_NET_SECURE_STREAM_ERROR; + return -1; + } + + buff += aes_payload_size; + size -= aes_payload_size; + } + + i64 result = size_original - size; + return result; +} + +int +os_net_secure_stream_get_fd(u32 id) +{ + OSNetSecureStream *secure_stream = &s_secure_streams[id]; + return secure_stream->fd; +} + + +internal_fn void +os_net_secure_stream_free(u32 id) +{ + s_free_ids[s_free_id_count] = id; + s_free_id_count += 1; +} + + +internal_fn u32 +os_net_secure_stream_alloc(void) +{ + assert(s_free_id_count > 0); + if (s_free_id_count == 0) { + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + u32 id = s_free_ids[s_free_id_count-1]; + s_free_id_count -= 1; + + return id; +} + + +void +os_net_secure_stream_close(u32 id) +{ + OSNetSecureStream *secure_stream = &s_secure_streams[id]; + + close(secure_stream->fd); + memset(secure_stream, 0, sizeof(*secure_stream)); + + os_net_secure_stream_free(id); +} + +u32 +os_net_secure_stream_listen(u16 port, EVP_PKEY *rsa_pri) +{ + int fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + perror("socket()"); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + int enable_reuse = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable_reuse, sizeof(int)) < 0) { + perror("setsockopt(SO_REUSEADDR)"); + close(fd); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + struct sockaddr_in local_addr; + local_addr.sin_family = AF_INET; + local_addr.sin_port = htons(port); + local_addr.sin_addr.s_addr = INADDR_ANY; + if (bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) { + perror("bind()"); + close(fd); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + int backlog = 128; + if (listen(fd, backlog) == -1) { + perror("listen()"); + close(fd); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + + u32 id = os_net_secure_stream_alloc(); + + OSNetSecureStream *secure_stream = &s_secure_streams[id]; + secure_stream->fd = fd; + secure_stream->status = OS_NET_SECURE_STREAM_CONNECTED; + secure_stream->rsa_key = rsa_pri; + + + return id; +} + + +internal_fn b32 +set_socket_nonblocking(int fd) +{ + int flags; + if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { + perror("fcntl(F_GETFL)"); + return false; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + perror("fcntl(F_SETFL)"); + return false; + } + return true; +} + +u32 +os_net_secure_stream_accept(u32 listener_id) +{ + OSNetSecureStream *listener = &s_secure_streams[listener_id]; + + + struct sockaddr_in addr; + socklen_t addr_size = sizeof(addr); + + int fd = accept(listener->fd, (struct sockaddr*)&addr, &addr_size); + if (fd == -1) { + printf("accept() failed\n"); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + + u32 secure_stream_id = os_net_secure_stream_alloc(); + if (secure_stream_id == OS_NET_SECURE_STREAM_ID_INVALID) { + close(fd); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + OSNetSecureStream *secure_stream = &s_secure_streams[secure_stream_id]; + secure_stream->fd = fd; + secure_stream->status = OS_NET_SECURE_STREAM_HANDSHAKING; + + + // recv rsa request + u8 encrypted_cm_handshake[512]; // Todo: use secure_stream->recv_buff + i64 recvd_size = recv(secure_stream->fd, encrypted_cm_handshake, sizeof(encrypted_cm_handshake), MSG_WAITALL); + if (recvd_size != sizeof(encrypted_cm_handshake)) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + // decrypt rsa request + CM_Handshake cm_handshake; + if (!rsa_decrypt(listener->rsa_key, &cm_handshake, encrypted_cm_handshake, sizeof(encrypted_cm_handshake))) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + + // process request + memcpy(&secure_stream->recv_aes_key, &cm_handshake.aes_key_client_encrypt, sizeof(cm_handshake.aes_key_client_encrypt)); + memcpy(&secure_stream->recv_aes_iv, &cm_handshake.aes_iv_client_encrypt, sizeof(cm_handshake.aes_iv_client_encrypt)); + memcpy(&secure_stream->send_aes_key, &cm_handshake.aes_key_client_decrypt, sizeof(cm_handshake.aes_key_client_decrypt)); + memcpy(&secure_stream->send_aes_iv, &cm_handshake.aes_iv_client_decrypt, sizeof(cm_handshake.aes_iv_client_decrypt)); + + + // prepare aes response + AesGcmHeader *aes_header = (AesGcmHeader*)secure_stream->send_ciphertext; + + SM_Handshake sm_handshake; + memcpy(sm_handshake.client_random, cm_handshake.client_random, sizeof(cm_handshake.client_random)); + + // encrypt aes response + if (!aes_gcm_encrypt(&secure_stream->send_aes_key, + &secure_stream->send_aes_iv, + secure_stream->send_ciphertext + sizeof(*aes_header), (u8*)&sm_handshake, sizeof(sm_handshake), + aes_header->tag, sizeof(aes_header->tag))) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + aes_header->payload_size = sizeof(sm_handshake); + secure_stream->send_ciphertext_size_used = sizeof(*aes_header) + sizeof(sm_handshake); + + // send response + i64 sent_size = send(secure_stream->fd, secure_stream->send_ciphertext, secure_stream->send_ciphertext_size_used, 0); + if (sent_size != secure_stream->send_ciphertext_size_used) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + + if (!set_socket_nonblocking(fd)) { + close(fd); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + + secure_stream->status = OS_NET_SECURE_STREAM_CONNECTED; + return secure_stream_id; +} + +u32 +os_net_secure_stream_connect(char *address, u16 port, EVP_PKEY *server_rsa_pub) +{ + int fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + printf("cant open socket\n"); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + struct sockaddr_in target_addr; + memset(&target_addr, 0, sizeof(target_addr)); + target_addr.sin_family = AF_INET; + target_addr.sin_port = htons(port); + target_addr.sin_addr.s_addr = inet_addr(address); + if (connect(fd, (struct sockaddr*)&target_addr, sizeof(target_addr)) == -1) { + printf("connect failed\n"); + close(fd); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + + + u32 secure_stream_id = os_net_secure_stream_alloc(); + if (secure_stream_id == OS_NET_SECURE_STREAM_ID_INVALID) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + OSNetSecureStream *secure_stream = &s_secure_streams[secure_stream_id]; + secure_stream->fd = fd; + secure_stream->rsa_key = server_rsa_pub; + + + if (!aes_gcm_key_init_random(&secure_stream->send_aes_key)) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + if (!aes_gcm_iv_init(&secure_stream->send_aes_iv)) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + if (!aes_gcm_key_init_random(&secure_stream->recv_aes_key)) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + if (!aes_gcm_iv_init(&secure_stream->recv_aes_iv)) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + + /* Request */ + + // prepare rsa request + CM_Handshake cm_handshake; + memcpy(&cm_handshake.aes_key_client_encrypt, &secure_stream->send_aes_key, sizeof(secure_stream->send_aes_key)); + memcpy(&cm_handshake.aes_iv_client_encrypt, &secure_stream->send_aes_iv, sizeof(secure_stream->send_aes_iv)); + memcpy(&cm_handshake.aes_key_client_decrypt, &secure_stream->recv_aes_key, sizeof(secure_stream->recv_aes_key)); + memcpy(&cm_handshake.aes_iv_client_decrypt, &secure_stream->recv_aes_iv, sizeof(secure_stream->recv_aes_iv)); + if (RAND_bytes(cm_handshake.client_random, sizeof(cm_handshake.client_random)) != 1) { + printf("RAND_bytes failed at %s:%d", __FILE__, __LINE__); + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + // encrypt rsa request + void *encrypted_req = secure_stream->send_ciphertext; + if (!rsa_encrypt(server_rsa_pub, encrypted_req, &cm_handshake, sizeof(cm_handshake))) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + // send rsa request + i64 sent_size = send(secure_stream->fd, encrypted_req, 512, 0); + if (sent_size != 512) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + + /* Response */ + + // recv aes response + AesGcmHeader *aes_header = (AesGcmHeader*)secure_stream->recv_ciphertext; + void *aes_payload = secure_stream->recv_ciphertext + sizeof(*aes_header); + + size_t response_size = sizeof(*aes_header) + sizeof(SM_Handshake); + + i64 recvd_size = recv(secure_stream->fd, secure_stream->recv_ciphertext, response_size, 0); + if (recvd_size != response_size) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + // decrypt aes response + SM_Handshake sm_handshake; + if (!aes_gcm_decrypt(&secure_stream->recv_aes_key, + &secure_stream->recv_aes_iv, + (u8*)&sm_handshake, aes_payload, sizeof(sm_handshake), + aes_header->tag, sizeof(aes_header->tag))) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + // verify aes response + assert(sizeof(cm_handshake.client_random) == sizeof(sm_handshake.client_random)); + if (memcmp(cm_handshake.client_random, sm_handshake.client_random, sizeof(cm_handshake.client_random)) != 0) { + close(fd); + os_net_secure_stream_free(secure_stream_id); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + + if (!set_socket_nonblocking(fd)) { + close(fd); + return OS_NET_SECURE_STREAM_ID_INVALID; + } + + + secure_stream->status = OS_NET_SECURE_STREAM_CONNECTED; + secure_stream->rsa_key = server_rsa_pub; + return secure_stream_id; +} + +void +os_net_secure_streams_init(Arena *arena, size_t max_count) +{ + s_max_secure_stream_count = max_count; + s_secure_streams = arena_push(arena, max_count * sizeof(OSNetSecureStream)); + memset(s_secure_streams, 0, max_count * sizeof(OSNetSecureStream)); + + s_free_id_count = max_count; + s_free_ids = arena_push(arena, max_count * sizeof(u32)); + for (size_t i = 0; i < max_count; i++) { + s_free_ids[i] = i; + } +} + diff --git a/src/os/linux/linux_sound.c b/src/os/linux/linux_sound.c new file mode 100644 index 0000000..2669d40 --- /dev/null +++ b/src/os/linux/linux_sound.c @@ -0,0 +1,269 @@ +// Todo: Use a high-priority thread for playing the sound buffer. +// Todo: refactor SND_CALL and other snd_... calls to deal with errors/state better. +// Note: Currently, the snd-api's buffer is large enough to write everything at once + +#ifndef _POXIS_C_SOURCE +#define _POSIX_C_SOURCE 200809L // enable POSIX.1-2017 +#endif + +#include +#include +#include +#include + + +struct OSSoundPlayer { + snd_pcm_t *pcm; + u32 frames_per_period; + u32 samples_per_second; + i32 channel_count; + i32 max_sample_count; + OSSoundBuffer sound_buffer; +}; + + +#define SND_CALL(snd_call) \ +{ \ + int error = snd_call; \ + if (error < 0) { \ + const char *error_str = snd_strerror(error); \ + printf(#snd_call" err: %s\n", error_str); \ + snd_pcm_close(pcm); \ + return false; \ + } \ +} + +internal_fn void +os_list_sound_devices(void) +{ + int status; + int card = -1; + char* longname = NULL; + char* shortname = NULL; + + if ((status = snd_card_next(&card)) < 0) { + printf("cannot determine card number: %s", snd_strerror(status)); + return; + } + if (card < 0) { + printf("no sound cards found"); + return; + } + while (card >= 0) { + printf("Card %d:", card); + if ((status = snd_card_get_name(card, &shortname)) < 0) { + printf("cannot determine card shortname: %s", snd_strerror(status)); + break; + } + if ((status = snd_card_get_longname(card, &longname)) < 0) { + printf("cannot determine card longname: %s", snd_strerror(status)); + break; + } + printf("\tLONG NAME: %s\n", longname); + printf("\tSHORT NAME: %s\n", shortname); + + + char cardname[128]; + //sprintf(cardname, "default"); + sprintf(cardname, "hw:%d", card); + printf("cardname = %s\n", cardname); + + snd_ctl_t *ctl; + int mode = SND_PCM_ASYNC; // SND_PCM_NONBLOCK or SND_PCM_ASYNC + if (snd_ctl_open(&ctl, cardname, mode) >= 0) { + printf("cannot snd_stl_open\n"); + int device = -1; + if (snd_ctl_pcm_next_device(ctl, &device) < 0) { + printf("no devices found\n"); + device = -1; + } + + if (device == -1) { + printf("no device\n"); + } + while (device >= 0) { + char devicename[256]; + sprintf(devicename, "%s,%d", cardname, device); + printf("devicename = %s\n", devicename); + + snd_pcm_t *pcm_handle; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; // SND_PCM_STREAM_PLAYBACK or SND_PCM_STREAM_CAPTURE + if (snd_pcm_open(&pcm_handle, devicename, stream, mode) >= 0) { + printf("snd_pcm_open success\n"); + snd_pcm_close(pcm_handle); + } + else { + printf("snd_pcm_open failed\n"); + } + + if (snd_ctl_pcm_next_device(ctl, &device) < 0) { + printf("snd_ctl_pcm_next_device error\n"); + break; + } + } + + snd_ctl_close(ctl); + } + else { + printf("snd_ctl_open failed\n"); + } + + + if ((status = snd_card_next(&card)) < 0) { + printf("cannot determine card number: %s", snd_strerror(status)); + break; + } + } +} + +internal_fn b32 +os_sound_player_recover(snd_pcm_t *pcm, i64 err) +{ + if (err == -EPIPE) { + err = snd_pcm_prepare(pcm); + if (err < 0) { + printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); + return false; + } + return true; + } + else if (err == -ESTRPIPE) { + while ((err = snd_pcm_resume(pcm)) == -EAGAIN) { + struct timespec ts = {0, 100}; + nanosleep(&ts, 0); + } + if (err < 0) { + err = snd_pcm_prepare(pcm); + if (err < 0) { + printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); + return false; + } + return true; + } + return true; + } + printf("Can't recover: %s\n", snd_strerror(err)); + return false; +} + + +void +os_sound_player_play(OSSoundPlayer *player) +{ + OSSoundBuffer *buffer = &player->sound_buffer; + + i16 *samples = player->sound_buffer.samples; + u32 frames_to_write = buffer->sample_count / player->channel_count; + while (frames_to_write) { + snd_pcm_sframes_t frames_written = snd_pcm_writei(player->pcm, samples, frames_to_write); + if (frames_written < 0) { + if (!os_sound_player_recover(player->pcm, frames_written)) { + return; + } + continue; + } + samples += frames_written * player->channel_count; + frames_to_write -= frames_written; + } + + int state = snd_pcm_state(player->pcm); + if (state != SND_PCM_STATE_RUNNING) { + snd_pcm_t *pcm = player->pcm; + snd_pcm_start(pcm); + } +} + +void +os_sound_player_close(OSSoundPlayer *player) +{ + snd_pcm_close(player->pcm); + free(player->sound_buffer.samples); +} + +OSSoundBuffer * +platform_sound_player_get_buffer(OSSoundPlayer *player) +{ + return &player->sound_buffer; +} + +OSSoundBuffer * +os_sound_player_get_buffer(OSSoundPlayer *player) +{ + i64 frames_avail = snd_pcm_avail(player->pcm); + if (frames_avail < 0) { + if (os_sound_player_recover(player->pcm, frames_avail)) { + frames_avail = 0; + } + frames_avail = snd_pcm_avail(player->pcm); + if (frames_avail < 0) { + frames_avail = 0; + } + } + + OSSoundBuffer *buffer = &player->sound_buffer; + buffer->sample_count = frames_avail*2; + memset(buffer->samples, 0, buffer->sample_count * sizeof(i16)); + return buffer; +} + +OSSoundPlayer * +os_sound_player_create(Arena *arena, i32 samples_per_second) +{ + // open pcm + snd_pcm_t *pcm = 0; + const char *device_name = "default"; + int stream = SND_PCM_STREAM_PLAYBACK; + int mode = SND_PCM_ASYNC; + + + SND_CALL(snd_pcm_open(&pcm, device_name, stream, mode)); + + + // set hw params + + snd_pcm_hw_params_t *params; + snd_pcm_hw_params_alloca(¶ms); + + SND_CALL(snd_pcm_hw_params_any(pcm, params)); // get full configuration space + + int access = SND_PCM_ACCESS_RW_INTERLEAVED; + SND_CALL(snd_pcm_hw_params_set_access(pcm, params, access)); + + int format = SND_PCM_FORMAT_S16_LE; + SND_CALL(snd_pcm_hw_params_set_format(pcm, params, format)); + + u32 channels = 2; + SND_CALL(snd_pcm_hw_params_set_channels(pcm, params, channels)); + + u32 sample_rate = 44100; + SND_CALL(snd_pcm_hw_params_set_rate_near(pcm, params, &sample_rate, 0)); + + u32 period_count = 3; + u32 sample_count = period_count * (sample_rate / 30); + u32 frame_count = sample_count / channels; + SND_CALL(snd_pcm_hw_params_set_buffer_size(pcm, params, frame_count)); + + u32 frames_per_period = frame_count / period_count; + SND_CALL(snd_pcm_hw_params_set_period_size(pcm, params, frames_per_period, 0)); + + SND_CALL(snd_pcm_hw_params(pcm, params)); + + + // create player + OSSoundPlayer *player = arena_push(arena, sizeof(OSSoundPlayer)); + memset(player, 0, sizeof(*player)); + player->pcm = pcm; + player->frames_per_period = frames_per_period; + player->samples_per_second = sample_rate; + player->channel_count = channels; + + // Todo: Redesign this player & buffer interaction. + // create buffer + OSSoundBuffer *sound_buffer = &player->sound_buffer; + sound_buffer->sample_count = 0; + sound_buffer->max_sample_count = sample_count; + sound_buffer->samples = arena_push(arena, sample_count * sizeof(i16)); + + return player; +} + diff --git a/src/os/linux/linux_time.c b/src/os/linux/linux_time.c new file mode 100644 index 0000000..9a8f2cf --- /dev/null +++ b/src/os/linux/linux_time.c @@ -0,0 +1,23 @@ +#define _POSIX_C_SOURCE 200809L + +#include + +#include +#include + +OSTime +os_time_get_now(void) +{ + struct timespec ts; + memset(&ts, 0, sizeof(ts)); + i32 err = clock_gettime(CLOCK_REALTIME, &ts); + if (err != 0) { + printf("failed to get time\n"); + } + + OSTime result; + result.seconds = ts.tv_sec; + result.nanoseconds = ts.tv_nsec; + return result; +} + diff --git a/src/os/linux/linux_window.c b/src/os/linux/linux_window.c new file mode 100644 index 0000000..d739b3c --- /dev/null +++ b/src/os/linux/linux_window.c @@ -0,0 +1,304 @@ +// Note: This is probably deprecated and can be deleted. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +struct OSWindow { + Window xid; + Atom wm_delete_window; + u32 event_mask; + + i32 width; + i32 height; + + GLXContext glx_context; + u32 gl_texture_id; + OSOffscreenBuffer offscreen_buffer; + + OSEvent current_event; +}; + + +internal_var Display *g_display; + +internal_fn b32 +os_connect_to_x(void) +{ + const char *display_name = 0; + g_display = XOpenDisplay(display_name); + if (!g_display) + { + printf("XOpenDisplay(%s) failed\n", display_name); + return false; + } + + return true; +} + + +void +os_window_destroy_offscreen_buffer(OSOffscreenBuffer *offscreen_buffer) +{ + free(offscreen_buffer->pixels); +} + +void +os_offscreen_buffer_resize(OSOffscreenBuffer *offscreen, i32 width, i32 height) +{ + u32 *new_pixels = realloc(offscreen->pixels, width*height*4); + if (new_pixels) { + offscreen->width = width; + offscreen->height = height; + offscreen->pixels = new_pixels; + glViewport(0, 0, width, height); + } +} + +void +os_offscreen_buffer_destroy(OSOffscreenBuffer *offscreen_buffer) +{ + free(offscreen_buffer->pixels); + free(offscreen_buffer); +} + +OSOffscreenBuffer * +os_offscreen_buffer_create(i32 width, i32 height) +{ + OSOffscreenBuffer *offscreen_buffer = malloc(sizeof(OSOffscreenBuffer)); + if (offscreen_buffer) { + offscreen_buffer->green_shift = 8; + offscreen_buffer->blue_shift = 16; + offscreen_buffer->red_shift = 0; + offscreen_buffer->alpha_shift = 24; + offscreen_buffer->width = 0; + offscreen_buffer->height = 0; + offscreen_buffer->pixels = 0; + } + os_offscreen_buffer_resize(offscreen_buffer, width, height); + return offscreen_buffer; +} + +void +os_window_swap_buffers(OSWindow *window, OSOffscreenBuffer *offscreen_buffer) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, window->gl_texture_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, offscreen_buffer->width, offscreen_buffer->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, offscreen_buffer->pixels); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); + glTexCoord2f(1.0f, 0.0f); glVertex2f( 1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); glVertex2f( 1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + + glXSwapBuffers(g_display, window->xid); +} + + +#define ADD_KEY_PRESS(c) \ + event->type = OS_EVENT_KEY_PRESS; \ + event->ev.key_press.code = c; \ + event->ev.key_press.is_unicode = true; \ + return true; +#define ADD_SPECIAL_KEY_PRESS(c) \ + event->type = OS_EVENT_KEY_PRESS; \ + event->ev.key_press.code = c; \ + event->ev.key_press.is_unicode = false; \ + return true; + +b32 +os_window_get_event(OSWindow *window, OSEvent *event) +{ + while (XPending(g_display)) { + XEvent xevent; + XNextEvent(g_display, &xevent); + + // Todo: Rework this whole shift/caps-lock logic. + persist_var b32 is_caps; + persist_var b32 is_shift_l; + persist_var b32 is_shift_r; + b32 is_uppercase = ( is_caps && !(is_shift_l || is_shift_r)) || + (!is_caps && (is_shift_l || is_shift_r)); + + switch (xevent.type) { + case ClientMessage: { + if ((Atom)xevent.xclient.data.l[0] == window->wm_delete_window) { + event->type = OS_EVENT_WINDOW_DESTROYED; + return true; + } + continue; + } break; + + case DestroyNotify: { + event->type = OS_EVENT_WINDOW_DESTROYED; + return true; + } break; + + case ConfigureNotify: { + i32 width = xevent.xconfigure.width; + i32 height = xevent.xconfigure.height; + if (width != window->width || height != window->height) { + event->type = OS_EVENT_WINDOW_RESIZE; + event->ev.resize.width = width; + event->ev.resize.height = height; + window->width = width; + window->height = height; + return true; + } + continue; + } break; + + case KeyPress: { + int index = is_uppercase ? 1 : 0; + KeySym keysym = XLookupKeysym(&xevent.xkey, index); + if (keysym >= 32 && keysym <= 126) {ADD_KEY_PRESS(keysym);} + else if (keysym == XK_Tab) {ADD_KEY_PRESS('\t'); } + else if (keysym == XK_Return) {ADD_KEY_PRESS('\r'); } + else if (keysym == XK_BackSpace) {ADD_KEY_PRESS('\b'); } + else if (keysym == XK_Delete) {ADD_KEY_PRESS(127); } + else if (keysym == XK_Left) {ADD_SPECIAL_KEY_PRESS(OS_KEYCODE_LEFT); } + else if (keysym == XK_Right) {ADD_SPECIAL_KEY_PRESS(OS_KEYCODE_RIGHT);} + else if (keysym == XK_Up) {ADD_SPECIAL_KEY_PRESS(OS_KEYCODE_UP); } + else if (keysym == XK_Down) {ADD_SPECIAL_KEY_PRESS(OS_KEYCODE_DOWN); } + else if (keysym == XK_Shift_L) is_shift_l = true; + else if (keysym == XK_Shift_R) is_shift_r = true; + else if (keysym == XK_Caps_Lock) is_caps = true; + } break; + + case KeyRelease: { + int index = 0; + KeySym keysym = XLookupKeysym(&xevent.xkey, index); + if (keysym == XK_Shift_L) is_shift_l = false; + else if (keysym == XK_Shift_R) is_shift_r = false; + else if (keysym == XK_Caps_Lock) is_caps = false; + continue; // ignore this for now i guess + } break; + + case GraphicsExpose: { + printf("graphics exposure happened\n"); + continue; + } break; + + default:; + } + } + return false; +} + +#undef ADD_KEY_PRESS +#undef ADD_SPECIAL_KEY_PRESS + + +void os_window_destroy(OSWindow *window) { + os_window_destroy_offscreen_buffer(&window->offscreen_buffer); + XDestroyWindow(g_display, window->xid); + free(window); +} + +OSWindow *os_window_create(const char *name, i32 width, i32 height) { + if (!g_display) { + if (!os_connect_to_x()) + return 0; + } + + int screen_number = XDefaultScreen(g_display); + Window root_window_id = XRootWindow(g_display, screen_number); + if (!root_window_id) + { + printf("XDefaultRootWindow(display) failed\n"); + XCloseDisplay(g_display); + return 0; + } + + // get visual info + int depth = 24; + int va[] = { + GLX_RGBA, 1, + GLX_DOUBLEBUFFER, 1, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, depth, + None + }; + XVisualInfo* vinfo = glXChooseVisual(g_display, screen_number, va); + if (!vinfo) + { + printf("glXChooseVisual failed\n"); + return 0; + } + + // set window attribs + long swa_mask = CWEventMask | CWColormap | CWBackPixmap |CWBorderPixel; + long swa_event_mask = PropertyChangeMask | SubstructureNotifyMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask; + Colormap colormap = XCreateColormap(g_display, root_window_id, vinfo->visual, AllocNone); + + XSetWindowAttributes swa; + memset(&swa, 0, sizeof(swa)); + swa.background_pixmap = None; + swa.border_pixel = 0; + swa.event_mask = swa_event_mask; + swa.colormap = colormap; + + Window window_id = XCreateWindow(g_display, root_window_id, 0, 0, width, height, 0, depth, InputOutput, vinfo->visual, swa_mask, &swa); + XStoreName(g_display, window_id, name); + XMapWindow(g_display, window_id); + + + // I support the WM_DELETE_WINDOW protocol @Leak? + Atom wm_delete_window = XInternAtom(g_display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(g_display, window_id, &wm_delete_window, 1); + + + // glx context + Bool direct = True; + GLXContext glx_context = glXCreateContext(g_display, vinfo, 0, direct); + if (glx_context == 0) + { + printf("glXCreateContext failed\n"); + return 0; + } + Bool made_current = glXMakeCurrent(g_display, window_id, glx_context); + if (made_current == False) + { + printf("glXMakeCurrent failed\n"); + return 0; + } + + + OSWindow *window = malloc(sizeof(OSWindow)); + window->xid = window_id; + window->wm_delete_window = wm_delete_window; + window->event_mask = swa_event_mask; + window->glx_context = glx_context; + XSync(g_display, False); + + + glEnable(GL_TEXTURE_2D); + + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &window->gl_texture_id); + glBindTexture(GL_TEXTURE_2D, window->gl_texture_id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + + return window; +} + diff --git a/src/os/os.h b/src/os/os.h new file mode 100644 index 0000000..3c9de18 --- /dev/null +++ b/src/os/os.h @@ -0,0 +1,153 @@ +#ifndef OS_H +#define OS_H + +#include +#include +#include + +typedef struct { + size_t size; + u8 *p; +} OSMemory; + +b32 os_memory_allocate(OSMemory *memory, size_t size); +void os_memory_free(OSMemory *memory); + + +typedef struct { + i64 seconds; + i64 nanoseconds; +} OSTime; + +OSTime os_time_get_now(void); + + +typedef void OSLibrary; +OSLibrary* os_library_open(const char *path); +void os_library_close(OSLibrary *lib); +void* os_library_get_proc(OSLibrary *lib, const char *name); + + +char* os_file_read_as_string(Arena *arena, char *path, size_t *outlen); + + +typedef enum { + OS_EVENT_KEY_PRESS, + OS_EVENT_KEY_RELEASE, + OS_EVENT_WINDOW_RESIZE, + OS_EVENT_WINDOW_DESTROYED, +} OSEventType; + +typedef enum { + OS_KEYCODE_LEFT, + OS_KEYCODE_RIGHT, + OS_KEYCODE_UP, + OS_KEYCODE_DOWN, +} OSEventKeySpecial; + +typedef struct { + b32 is_unicode; // else it's "special", see OSKeySpecial + u32 code; // unicode character or some other keyboard keys +} OSEventKeyPress; + +typedef struct { + b32 is_special; + u32 code; // unicode character or some other keyboard keys +} OSEventKeyRelease; + +typedef struct { + i32 width; + i32 height; +} OSEventResize; + +typedef struct OSEvent { + OSEventType type; + union { + OSEventKeyPress key_press; + OSEventKeyRelease key_release; + OSEventResize resize; + } ev; +} OSEvent; + + + +typedef struct { + u8 red_shift; + u8 green_shift; + u8 blue_shift; + u8 alpha_shift; + i32 width; + i32 height; + u32 *pixels; +} OSOffscreenBuffer; + +typedef struct OSWindow OSWindow; +OSWindow* os_window_create(const char *name, i32 width, i32 height); +void os_window_destroy(OSWindow *window); +b32 os_window_get_event(OSWindow *window, OSEvent *event); +OSOffscreenBuffer* os_window_get_offscreen_buffer(OSWindow *window); +void os_window_swap_buffers(OSWindow *window, OSOffscreenBuffer *offscreen_buffer); + + + +typedef enum { + OS_NET_SECURE_STREAM_ERROR, + OS_NET_SECURE_STREAM_DISCONNECTED, + + OS_NET_SECURE_STREAM_CONNECTED, + OS_NET_SECURE_STREAM_HANDSHAKING, +} OSNetSecureStreamStatus; + +#define OS_NET_SECURE_STREAM_ID_INVALID U32_MAX + +void os_net_secure_streams_init(Arena *arena, size_t max_count); + +u32 os_net_secure_stream_listen(u16 port, EVP_PKEY *server_rsa_pri); +u32 os_net_secure_stream_accept(u32 listener_id); +u32 os_net_secure_stream_connect(char *address, u16 port, EVP_PKEY *server_rsa_pub); +void os_net_secure_stream_close(u32 id); + +OSNetSecureStreamStatus os_net_secure_stream_get_status(u32 id); +i64 os_net_secure_stream_error(u32 id); // 0 if no error, else tbd + +i64 os_net_secure_stream_send(u32 id, u8 *buffer, size_t size); +i64 os_net_secure_stream_recv(u32 id, u8 *buffer, size_t size); +int os_net_secure_stream_get_fd(u32 id); + + + +typedef struct OSSoundPlayer OSSoundPlayer; + +typedef struct { + i32 play_cursor; + i32 max_sample_count; + i32 sample_count; + i32 samples_per_second; + i16 *samples; +} OSSoundBuffer; + +// Todo: maybe change api by replacing get_buffer with play_buffer +OSSoundPlayer* os_sound_player_create(Arena *arena, i32 samples_per_second); +OSSoundBuffer* os_sound_player_get_buffer(OSSoundPlayer *player); +void os_sound_player_close(OSSoundPlayer *player); + + + +// Note: api unused and in progress + +typedef struct { + void (*fn_worker)(void *data); + void *data; +} OSWork; + +typedef struct OSThreadPool OSThreadPool; +typedef u32 OSThreadId; + +OSThreadPool* os_thread_pool_create(Arena *arena, u32 thread_count, u32 work_queue_size); +OSThreadId os_thread_pool_start(OSThreadPool *pool, OSWork work); +void os_thread_pool_finish(OSThreadPool *pool, OSThreadId id); + + + +#endif // OS_H + diff --git a/src/os/sdl/sdl_window.c b/src/os/sdl/sdl_window.c new file mode 100644 index 0000000..000ed50 --- /dev/null +++ b/src/os/sdl/sdl_window.c @@ -0,0 +1,171 @@ +#include +#include +#include +#include + + +struct OSWindow { + SDL_Window *sdl_window; + SDL_GLContext gl_context; + u32 gl_texture_id; + OSOffscreenBuffer offscreen_buffer; +}; + + +static void +os_offscreen_buffer_reinit(OSOffscreenBuffer *buffer, i32 w, i32 h) +{ + void *realloced_pixels = realloc(buffer->pixels, w*h*sizeof(buffer->pixels[0])); + if (!realloced_pixels) { + printf("could not resize offscreen buffer\n"); + return; + } + buffer->green_shift = 8; + buffer->blue_shift = 16; + buffer->red_shift = 0; + buffer->alpha_shift = 24; + buffer->width = w; + buffer->height = h; + buffer->pixels = realloced_pixels; +} + +static void +os_offscreen_buffer_deinit(OSOffscreenBuffer *buffer) +{ + buffer->width = 0; + buffer->height = 0; + free(buffer->pixels); +} + + +OSWindow * +os_window_create(const char *name, i32 width, i32 height) +{ + OSWindow *window = malloc(sizeof(*window)); + memset(window, 0, sizeof(*window)); + + SDL_Window *sdl_window = SDL_CreateWindow(name, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + if (!sdl_window) { + printf("SDL_CreateWindow failed\n"); + return 0; + } + + + SDL_GLContext gl_context = SDL_GL_CreateContext(sdl_window); + + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + + glGenTextures(1, &window->gl_texture_id); + glBindTexture(GL_TEXTURE_2D, window->gl_texture_id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + + window->sdl_window = sdl_window; + window->gl_context = gl_context; + os_offscreen_buffer_reinit(&window->offscreen_buffer, width, height); + + return window; +} + +void +os_window_destroy(OSWindow *window) +{ + os_offscreen_buffer_deinit(&window->offscreen_buffer); + SDL_GL_DestroyContext(window->gl_context); + SDL_DestroyWindow(window->sdl_window); + free(window); +} + +b32 +os_window_get_event(OSWindow *window, OSEvent *event) +{ + SDL_Window *sdl_window = window->sdl_window; + SDL_Event sdl_event; + if (SDL_PollEvent(&sdl_event)) { + if (sdl_event.type == SDL_EVENT_WINDOW_DESTROYED) { + event->type = OS_EVENT_WINDOW_DESTROYED; + return true; + } + else if (sdl_event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) { + SDL_DestroyWindow(sdl_window); + event->type = OS_EVENT_WINDOW_DESTROYED; + return true; + } + else if (sdl_event.type == SDL_EVENT_WINDOW_RESIZED) { + event->type = OS_EVENT_WINDOW_RESIZE; + event->ev.resize.width = sdl_event.window.data1; + event->ev.resize.height = sdl_event.window.data2; + os_offscreen_buffer_reinit(&window->offscreen_buffer, sdl_event.window.data1, sdl_event.window.data2); + return true; + } + else if (sdl_event.type == SDL_EVENT_KEY_DOWN) { + if ((sdl_event.key.key >= SDLK_SPACE && sdl_event.key.key <= SDLK_TILDE) || + sdl_event.key.key == '\t' || + sdl_event.key.key == '\r' || + sdl_event.key.key == '\b' || + sdl_event.key.key == SDLK_DELETE) + { + event->type = OS_EVENT_KEY_PRESS; + event->ev.key_press.is_unicode = true; + event->ev.key_press.code = sdl_event.key.key; + return true; + } + else if (sdl_event.key.key == SDLK_LEFT) { + event->type = OS_EVENT_KEY_PRESS; + event->ev.key_press.is_unicode = false; + event->ev.key_press.code = OS_KEYCODE_LEFT; + return true; + } + else if (sdl_event.key.key == SDLK_RIGHT) { + event->type = OS_EVENT_KEY_PRESS; + event->ev.key_press.is_unicode = false; + event->ev.key_press.code = OS_KEYCODE_RIGHT; + return true; + } + else if (sdl_event.key.key == SDLK_UP) { + event->type = OS_EVENT_KEY_PRESS; + event->ev.key_press.is_unicode = false; + event->ev.key_press.code = OS_KEYCODE_UP; + return true; + } + else if (sdl_event.key.key == SDLK_DOWN) { + event->type = OS_EVENT_KEY_PRESS; + event->ev.key_press.is_unicode = false; + event->ev.key_press.code = OS_KEYCODE_DOWN; + return true; + } + } + } + + return false; +} + +OSOffscreenBuffer* +os_window_get_offscreen_buffer(OSWindow *window) +{ + return &window->offscreen_buffer; +} + +void os_window_swap_buffers(OSWindow *window, OSOffscreenBuffer *offscreen_buffer) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, window->gl_texture_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, offscreen_buffer->width, offscreen_buffer->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, offscreen_buffer->pixels); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); + glTexCoord2f(1.0f, 0.0f); glVertex2f( 1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); glVertex2f( 1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + + SDL_GL_SwapWindow(window->sdl_window); +} + -- cgit v1.2.3