diff options
| author | fschildt <florian.schildt@protonmail.com> | 2025-08-22 15:23:11 +0200 | 
|---|---|---|
| committer | fschildt <florian.schildt@protonmail.com> | 2025-10-15 11:33:23 +0200 | 
| commit | 04e4627e6c11254ee6f49edf5feb1b8d711da41a (patch) | |
| tree | e28f2e62d3e1b83f9686cdeb102e3f47379e6793 /src/os | |
Diffstat (limited to 'src/os')
| -rw-r--r-- | src/os/linux/linux_file.c | 36 | ||||
| -rw-r--r-- | src/os/linux/linux_library.c | 25 | ||||
| -rw-r--r-- | src/os/linux/linux_memory.c | 38 | ||||
| -rw-r--r-- | src/os/linux/linux_net_secure_stream.c | 579 | ||||
| -rw-r--r-- | src/os/linux/linux_sound.c | 269 | ||||
| -rw-r--r-- | src/os/linux/linux_time.c | 23 | ||||
| -rw-r--r-- | src/os/linux/linux_window.c | 304 | ||||
| -rw-r--r-- | src/os/os.h | 153 | ||||
| -rw-r--r-- | src/os/sdl/sdl_window.c | 171 | 
9 files changed, 1598 insertions, 0 deletions
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 <os/os.h> + +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + + +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 <dlfcn.h> +#include <stdio.h> +#include <SDL2/SDL.h> + +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 <os/os.h> +#include <basic/basic.h> + +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/mman.h> + +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 <os/os.h> +#include <basic/basic.h> +#include <basic/arena.h> +#include <crypto/rsa.h> +#include <crypto/aes_gcm.h> + +#include <string.h> + +#include <openssl/aes.h> +#include <openssl/rsa.h> +#include <openssl/rand.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +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 <os/os.h> +#include <basic/basic.h> +#include <basic/arena.h> +#include <alsa/asoundlib.h> + + +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 <os/os.h> + +#include <string.h> +#include <time.h> + +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 <basic/basic.h> +#include <os/os.h> + +#include <SDL3/SDL.h> +#include <X11/Xlib.h> +#include <GL/gl.h> +#include <GL/glx.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +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 <basic/arena.h> +#include <basic/basic.h> +#include <openssl/rsa.h> + +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 <os/os.h> +#include <SDL3/SDL_events.h> +#include <SDL3/SDL.h> +#include <GL/gl.h> + + +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); +} +  | 
