aboutsummaryrefslogtreecommitdiff
path: root/src/graveyard
diff options
context:
space:
mode:
authorfschildt <florian.schildt@protonmail.com>2025-08-22 15:23:11 +0200
committerfschildt <florian.schildt@protonmail.com>2025-08-22 15:23:11 +0200
commit2050c0e0576f05156f192aa4caf48834d2f28b14 (patch)
treeee58bd35b0df0a1bacfbc9700ed99ce80c99294e /src/graveyard
first commitHEADmaster
Diffstat (limited to 'src/graveyard')
-rw-r--r--src/graveyard/gl_renderer.cpp389
-rw-r--r--src/graveyard/gl_renderer.h52
-rw-r--r--src/graveyard/lin_file.c85
-rw-r--r--src/graveyard/lin_glx_modern.c243
-rw-r--r--src/graveyard/linux_net_stream.c192
-rw-r--r--src/graveyard/win32/Win32WindowService.cpp375
-rw-r--r--src/graveyard/win32/Win32WindowService.h33
-rw-r--r--src/graveyard/win32/win32_florilia.c111
-rw-r--r--src/graveyard/win32/win32_fscord.cpp181
-rw-r--r--src/graveyard/win32/win32_net.c79
-rw-r--r--src/graveyard/win32/win32_network_stream.cpp90
-rw-r--r--src/graveyard/win32/win32_window.c389
-rw-r--r--src/graveyard/win32/win32_window.cpp77
-rw-r--r--src/graveyard/win32_wgl_modern.c206
14 files changed, 2502 insertions, 0 deletions
diff --git a/src/graveyard/gl_renderer.cpp b/src/graveyard/gl_renderer.cpp
new file mode 100644
index 0000000..f6ed6cb
--- /dev/null
+++ b/src/graveyard/gl_renderer.cpp
@@ -0,0 +1,389 @@
+#include "../../platform/platform.h"
+#include "../../external/stb_truetype.h"
+#include "../../external/khronos/glcorearb.h"
+
+#include "../renderer.h"
+#include "gl_renderer.h"
+
+#include <stdlib.h>
+
+// misc
+static PFNGLCLEARCOLORPROC glClearColor;
+static PFNGLCLEARPROC glClear;
+static PFNGLVIEWPORTPROC glViewport;
+static PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
+static PFNGLENABLEPROC glEnable;
+static PFNGLBLENDFUNCPROC glBlendFunc;
+static PFNGLDRAWELEMENTSPROC glDrawElements;
+static PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
+static PFNGLGETERRORPROC glGetError;
+static PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback;
+static PFNGLACTIVETEXTUREPROC glActiveTexture;
+static PFNGLUNIFORM1IPROC glUniform1i;
+static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
+static PFNGLDRAWARRAYSPROC glDrawArrays;
+static PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
+static PFNGLUNIFORM3FPROC glUniform3f;
+// framebuffer
+static PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
+static PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers;
+static PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
+static PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
+static PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
+// vertex array
+static PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
+static PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
+static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
+static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
+// buffers
+static PFNGLGENBUFFERSPROC glGenBuffers;
+static PFNGLBINDBUFFERPROC glBindBuffer;
+static PFNGLBUFFERDATAPROC glBufferData;
+static PFNGLBUFFERSUBDATAPROC glBufferSubData;
+// textures
+static PFNGLGENTEXTURESPROC glGenTextures;
+static PFNGLDELETETEXTURESPROC glDeleteTextures;
+static PFNGLBINDTEXTUREPROC glBindTexture;
+static PFNGLTEXIMAGE2DPROC glTexImage2D;
+static PFNGLTEXPARAMETERIPROC glTexParameteri;
+// shader
+static PFNGLCREATESHADERPROC glCreateShader;
+static PFNGLSHADERSOURCEPROC glShaderSource;
+static PFNGLCOMPILESHADERPROC glCompileShader;
+static PFNGLGETSHADERIVPROC glGetShaderiv;
+static PFNGLATTACHSHADERPROC glAttachShader;
+static PFNGLDELETESHADERPROC glDeleteShader;
+static PFNGLCREATEPROGRAMPROC glCreateProgram;
+static PFNGLUSEPROGRAMPROC glUseProgram;
+static PFNGLLINKPROGRAMPROC glLinkProgram;
+static PFNGLVALIDATEPROGRAMPROC glValidateProgram;
+static PFNGLGETPROGRAMIVPROC glGetProgramiv;
+
+void gl_debug_message_callback(GLenum src, GLenum type, GLuint id,
+ GLenum severity, GLsizei length,
+ const GLchar *message, const void *user_param) {
+ printf("====================\n"
+ "gl error detected\n"
+ "--------------------\n"
+ "src = %d\n"
+ "type = %d\n"
+ "id = %d\n"
+ "severity = %d\n"
+ "length = %d\n"
+ "message = %s\n"
+ "====================\n",
+ src, type, id, severity, length, message);
+}
+
+
+Gl_Renderer::Gl_Renderer() {
+}
+
+// Todo: check if function pointers are valid
+bool Gl_Renderer::get_functions() {
+ // only 4.3+ thing
+ glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)platform_get_gl_proc("glDebugMessageCallback");
+
+ // misc
+ glClear = (PFNGLCLEARPROC)platform_get_gl_proc("glClear");
+ glClearColor = (PFNGLCLEARCOLORPROC)platform_get_gl_proc("glClearColor");
+ glViewport = (PFNGLVIEWPORTPROC)platform_get_gl_proc("glViewport");
+ glDrawElements = (PFNGLDRAWELEMENTSPROC)platform_get_gl_proc("glDrawElements");
+ glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)platform_get_gl_proc("glGenerateMipmap");
+ glGetError = (PFNGLGETERRORPROC)platform_get_gl_proc("glGetError");
+ glActiveTexture = (PFNGLACTIVETEXTUREPROC)platform_get_gl_proc("glActiveTexture");
+ glUniform1i = (PFNGLUNIFORM1IPROC)platform_get_gl_proc("glUniform1i");
+ glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)platform_get_gl_proc("glGetUniformLocation");
+ glDrawArrays = (PFNGLDRAWARRAYSPROC)platform_get_gl_proc("glDrawArrays");
+ glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)platform_get_gl_proc("glUniformMatrix4fv");
+ glUniform3f = (PFNGLUNIFORM3FPROC)platform_get_gl_proc("glUniform3f");
+ glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)platform_get_gl_proc("glDeleteFramebuffers");
+ glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)platform_get_gl_proc("glGenFramebuffers");
+ glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)platform_get_gl_proc("glBindFramebuffer");
+ glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)platform_get_gl_proc("glFramebufferTexture2D");
+ glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)platform_get_gl_proc("glCheckFramebufferStatus");
+
+ // vertex array
+ glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)platform_get_gl_proc("glGenVertexArrays");
+ glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)platform_get_gl_proc("glBindVertexArray");
+
+ // buffers
+ glGenBuffers = (PFNGLGENBUFFERSPROC)platform_get_gl_proc("glGenBuffers");
+ glBindBuffer = (PFNGLBINDBUFFERPROC)platform_get_gl_proc("glBindBuffer");
+ glBufferData = (PFNGLBUFFERDATAPROC)platform_get_gl_proc("glBufferData");
+ glBufferSubData = (PFNGLBUFFERSUBDATAPROC)platform_get_gl_proc("glBufferSubData");
+ glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)platform_get_gl_proc("glEnableVertexAttribArray");
+ glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)platform_get_gl_proc("glVertexAttribPointer");
+
+ // textures
+ glGenTextures = (PFNGLGENTEXTURESPROC)platform_get_gl_proc("glGenTextures");
+ glDeleteTextures = (PFNGLDELETETEXTURESPROC)platform_get_gl_proc("glDeleteTextures");
+ glBindTexture = (PFNGLBINDTEXTUREPROC)platform_get_gl_proc("glBindTexture");
+ glTexImage2D = (PFNGLTEXIMAGE2DPROC)platform_get_gl_proc("glTexImage2D");
+ glTexParameteri = (PFNGLTEXPARAMETERIPROC)platform_get_gl_proc("glTexParameteri");
+
+ // shader
+ glCreateShader = (PFNGLCREATESHADERPROC)platform_get_gl_proc("glCreateShader");
+ glShaderSource = (PFNGLSHADERSOURCEPROC)platform_get_gl_proc("glShaderSource");
+ glCompileShader = (PFNGLCOMPILESHADERPROC)platform_get_gl_proc("glCompileShader");
+ glGetShaderiv = (PFNGLGETSHADERIVPROC)platform_get_gl_proc("glGetShaderiv");
+ glAttachShader = (PFNGLATTACHSHADERPROC)platform_get_gl_proc("glAttachShader");
+ glDeleteShader = (PFNGLDELETESHADERPROC)platform_get_gl_proc("glDeleteShader");
+ glCreateProgram = (PFNGLCREATEPROGRAMPROC)platform_get_gl_proc("glCreateProgram");
+ glUseProgram = (PFNGLUSEPROGRAMPROC)platform_get_gl_proc("glUseProgram");
+ glLinkProgram = (PFNGLLINKPROGRAMPROC)platform_get_gl_proc("glLinkProgram");
+ glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)platform_get_gl_proc("glValidateProgram");
+ glGetProgramiv = (PFNGLGETPROGRAMIVPROC)platform_get_gl_proc("glGetProgramiv");
+
+ // misc
+ glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)platform_get_gl_proc("glGetShaderInfoLog");
+ glEnable = (PFNGLENABLEPROC)platform_get_gl_proc("glEnable");
+ glBlendFunc = (PFNGLBLENDFUNCPROC)platform_get_gl_proc("glBlendFunc");
+
+ return true;
+}
+
+bool Gl_Renderer::compile_shader(u32 *id, const char *src, GLenum type) {
+ u32 shader = glCreateShader(type);
+ glShaderSource(shader, 1, &src, NULL);
+ glCompileShader(shader);
+
+ int success;
+ char info_log[512];
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
+ if(!success)
+ {
+ glGetShaderInfoLog(shader, 512, NULL, info_log);
+ printf("error %s shader compilation failed\n%s\n", type == GL_VERTEX_SHADER ? "vertex" : "fragment", info_log);
+
+ glDeleteShader(shader);
+ return false;
+ }
+
+ *id = shader;
+ return true;
+}
+
+bool Gl_Renderer::create_shader(u32 *shader, const char *vs_src, const char *fs_src) {
+ u32 vs_id;
+ u32 fs_id;
+ if (!compile_shader(&vs_id, vs_src, GL_VERTEX_SHADER) ||
+ !compile_shader(&fs_id, fs_src, GL_FRAGMENT_SHADER)) {
+ return false;
+ }
+
+ u32 program_id = glCreateProgram();
+ glAttachShader(program_id, vs_id);
+ glAttachShader(program_id, fs_id);
+ glLinkProgram(program_id);
+
+ glDeleteShader(vs_id);
+ glDeleteShader(fs_id);
+
+ int success;
+ char info_log[512];
+ glGetProgramiv(program_id, GL_LINK_STATUS, &success);
+ if(!success) {
+ printf("error linking shader program\n%s\n", info_log);
+ return false;
+ }
+
+ *shader = program_id;
+ return true;
+}
+
+// set the OpenGL orthographic projection matrix in vertex shader
+void Gl_Renderer::ortho(f32 mat44[4][4], f32 x0, f32 x1, f32 y0, f32 y1, f32 z0, f32 z1) {
+ mat44[0][0] = 2 / (x1 - x0);
+ mat44[0][1] = 0;
+ mat44[0][2] = 0;
+ mat44[0][3] = 0;
+
+ mat44[1][0] = 0;
+ mat44[1][1] = 2 / (y1 - y0);
+ mat44[1][2] = 0;
+ mat44[1][3] = 0;
+
+ mat44[2][0] = 0;
+ mat44[2][1] = 0;
+ mat44[2][2] = 2 / (z1 - z0);
+ mat44[2][3] = 0;
+
+ mat44[3][0] = -(x1 + x0) / (x1 - x0);
+ mat44[3][1] = -(y1 + y0) / (y1 - y0);
+ mat44[3][2] = -(z1 + z0) / (z1 - z0);
+ mat44[3][3] = 1;
+}
+
+void Gl_Renderer::draw_rect(Rect rect, V3 color) {
+ Gl_Render_Rect *render_rect = &m_render_rect;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glBindVertexArray(render_rect->vertex_array);
+ glBindBuffer(GL_ARRAY_BUFFER, render_rect->vertex_buffer);
+ glUseProgram(render_rect->shader);
+
+ i32 location;
+
+ location = glGetUniformLocation(render_rect->shader, "u_color");
+ glUniform3f(location, color.r, color.g, color.b);
+
+ location = glGetUniformLocation(render_rect->shader, "u_projection");
+ glUniformMatrix4fv(location, 1, GL_FALSE, &m_projection[0][0]);
+
+ f32 x0 = rect.x0;
+ f32 y0 = rect.y0;
+ f32 x1 = rect.x1;
+ f32 y1 = rect.y1;
+
+ float vertices[] = {
+ x0, y0,
+ x1, y0,
+ x1, y1,
+ x0, y1,
+ x0, y0,
+ x1, y1
+ };
+
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+}
+
+void Gl_Renderer::draw_text(const char *text, f32 x, f32 y) {
+ y = m_height - y; // transforming to stb_truetype coordinates where the
+ // origin is top-left instead of bottom-left
+
+ struct Gl_Render_Font *font = &m_render_font;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glUseProgram(font->shader);
+ glBindVertexArray(font->vertex_array);
+ glBindBuffer(GL_ARRAY_BUFFER, font->vertex_buffer);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, font->texture);
+
+ GLint loc = glGetUniformLocation(font->shader, "u_projection");
+ glUniformMatrix4fv(loc, 1, GL_FALSE, &m_projection_stbtt[0][0]);
+
+ loc = glGetUniformLocation(font->shader, "u_texture");
+ glUniform1i(loc, 0);
+
+ while (*text) {
+ if (*text >= 32 && *text <= 127) {
+ stbtt_aligned_quad q;
+ stbtt_GetBakedQuad(font->baked_chars, 512, 512, *text-32, &x, &y, &q, 1);
+ float vertices[] = {
+ q.x0, q.y0, q.s0, q.t0,
+ q.x1, q.y0, q.s1, q.t0,
+ q.x1, q.y1, q.s1, q.t1,
+ q.x0, q.y1, q.s0, q.t1,
+ q.x0, q.y0, q.s0, q.t0,
+ q.x1, q.y1, q.s1, q.t1,
+ };
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ }
+ text++;
+ }
+}
+
+// NOTE: ttf_buff must be kept in memory
+bool Gl_Renderer::init_text_drawing(u8 *ttf_buff, const char *vs_src, const char *fs_src, f32 font_size) {
+ struct Gl_Render_Font *font = &m_render_font;
+
+ if (!create_shader(&font->shader, vs_src, fs_src))
+ return false;
+
+ // vao
+ glGenVertexArrays(1, &font->vertex_array);
+ glBindVertexArray(font->vertex_array);
+
+ // vbo
+ u32 cnt_vertices = 6;
+ u32 bytes_per_vertex = 4 * sizeof(f32);
+ glGenBuffers(1, &font->vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, font->vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, bytes_per_vertex*cnt_vertices, 0, GL_DYNAMIC_DRAW);
+
+ // 2 position, 2 texture
+ u32 stride = 4 *sizeof(f32);
+ void *offset_texture = (void*)(2*sizeof(f32));
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, 0);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, offset_texture);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+
+ // font texture
+ glActiveTexture(GL_TEXTURE0);
+ u8 tmp_bitmap[512*512];
+ stbtt_BakeFontBitmap(ttf_buff, 0, font_size, tmp_bitmap, 512, 512, 32, 96, font->baked_chars);
+ glGenTextures(1, &font->texture);
+ glBindTexture(GL_TEXTURE_2D, font->texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 512, 512, 0, GL_RED, GL_UNSIGNED_BYTE, tmp_bitmap);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ glBindVertexArray(0);
+
+ return true;
+}
+
+bool Gl_Renderer::init_rect_drawing(const char *vs_src, const char *fs_src) {
+ struct Gl_Render_Rect *rect = &m_render_rect;
+ if (!create_shader(&rect->shader, vs_src, fs_src))
+ return false;
+
+ // vao
+ glGenVertexArrays(1, &rect->vertex_array);
+ glBindVertexArray(rect->vertex_array);
+
+ // vbo
+ u32 cnt_vertices = 6;
+ u32 bytes_per_vertex = 2 * sizeof(f32);
+ glGenBuffers(1, &rect->vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, rect->vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, bytes_per_vertex*cnt_vertices, 0, GL_DYNAMIC_DRAW);
+
+ // 2 position
+ u32 stride = 2 *sizeof(f32);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, 0);
+ glEnableVertexAttribArray(0);
+
+ glBindVertexArray(0);
+ return true;
+}
+
+void Gl_Renderer::draw_background(V3 color) {
+ glClearColor(color.r, color.g, color.b, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void Gl_Renderer::viewport(i32 x, i32 y, i32 width, i32 height) {
+ ortho(m_projection_stbtt, 0, width, height, 0, 0, 1);
+ ortho(m_projection, 0, width, 0, height, 0, 1);
+ glViewport(x, y, width, height);
+
+ m_width = width;
+ m_height = height;
+}
+
+bool Gl_Renderer::init(f32 font_size, u8 *font, const char *font_vs, const char *font_fs, const char *rect_vs, const char *rect_fs) {
+ if (!get_functions() ||
+ !init_text_drawing(font, font_vs, font_fs, font_size) ||
+ !init_rect_drawing(rect_vs, rect_fs)) {
+ return false;
+ }
+
+ glDebugMessageCallback(gl_debug_message_callback, 0);
+
+ return true;
+}
+
diff --git a/src/graveyard/gl_renderer.h b/src/graveyard/gl_renderer.h
new file mode 100644
index 0000000..9c70c04
--- /dev/null
+++ b/src/graveyard/gl_renderer.h
@@ -0,0 +1,52 @@
+struct Gl_Render_Rect {
+ u32 vertex_array;
+ u32 vertex_buffer;
+ u32 shader;
+};
+
+struct Gl_Render_Font {
+ u32 vertex_array;
+ u32 vertex_buffer;
+ u32 index_buffer;
+ u32 texture;
+ u32 shader;
+ stbtt_bakedchar baked_chars[96];
+};
+
+class Gl_Renderer : public Renderer {
+private:
+ Gl_Render_Rect m_render_rect;
+ Gl_Render_Font m_render_font;
+
+ f32 m_projection_stbtt[4][4];
+ f32 m_projection[4][4];
+
+ i32 m_width;
+ i32 m_height;
+
+
+public:
+ Gl_Renderer();
+
+public:
+ bool init(f32 font_size, u8 *ttf_buff, const char *font_vs, const char *font_fs, const char *rect_vs, const char *rect_fs);
+
+ void viewport(i32 x, i32 y, i32 width, i32 height) override;
+ void draw_background(V3 color) override;
+ void draw_rect(Rect rect, V3 color) override;
+ void draw_text(const char *text, f32 x, f32 y) override;
+
+
+private:
+ bool get_functions();
+ bool init_text_drawing(u8 *ttf_buff, const char *vs_src, const char *fs_src, f32 font_size);
+ bool init_rect_drawing(const char *vs_src, const char *fs_src);
+
+ bool create_shader(u32 *shader, const char *vs_src, const char *fs_src);
+ bool compile_shader(u32 *id, const char *src, GLenum type);
+
+
+ // updates
+ void ortho(f32 mat44[4][4], f32 x0, f32 y0, f32 x1, f32 y1, f32 z0, f32 z1);
+};
+
diff --git a/src/graveyard/lin_file.c b/src/graveyard/lin_file.c
new file mode 100644
index 0000000..5f0e003
--- /dev/null
+++ b/src/graveyard/lin_file.c
@@ -0,0 +1,85 @@
+void platform_free_buffer(Platform_Buffer *buff)
+{
+ free(buff->mem);
+ buff->size = 0;
+}
+
+bool platform_read_file(Platform_Buffer *buff, const char *pathname)
+{
+ int fd = open(pathname, O_RDONLY);
+ if (fd == -1)
+ {
+ printf("error reading file %s\n", pathname);
+ return false;
+ }
+
+ struct stat statbuff;
+ if (fstat(fd, &statbuff) == -1)
+ {
+ printf("cant fstat file %s\n", pathname);
+ close(fd);
+ return false;
+ }
+
+ void *file_content = malloc(statbuff.st_size);
+ if (!file_content)
+ {
+ printf("error: out of memory\n");
+ close(fd);
+ return false;
+ }
+
+ ssize_t bytes_read = read(fd, file_content, statbuff.st_size);
+ if (bytes_read != statbuff.st_size)
+ {
+ printf("error: only read %ld/%ld bytes from file %s\n", bytes_read, statbuff.st_size, pathname);
+ close(fd);
+ free(file_content);
+ return false;
+ }
+
+ buff->size = statbuff.st_size;
+ buff->mem = file_content;
+ return true;
+}
+
+bool platform__change_to_runtime_dir()
+{
+ // prepare home
+ char *home = getenv("HOME");
+ int home_len = strlen(home);
+
+ if (home_len == 0 || home_len >= 4096) {
+ printf("home dir invalid\n");
+ return false;
+ }
+
+
+ // prepare subdir
+ // Note: it might never end with '/', but let's be sure
+ bool home_ends_with_slash = home[home_len-1] == '/';
+
+ const char *subdir = home_ends_with_slash ? ".local/share/florilia" : "/.local/share/florilia";
+ int subdir_len = strlen(subdir);
+
+ if (home_len + subdir_len >= 4096) {
+ printf("home/subdir has invalid length\n");
+ return false;
+ }
+
+
+ // set run_tree
+ char run_tree[4096];
+ memcpy(run_tree, home, home_len);
+ strcpy(run_tree + home_len, subdir);
+
+
+ // change to run_tree
+ int changed = chdir(run_tree);
+ if (changed == -1) {
+ perror("chdir");
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/graveyard/lin_glx_modern.c b/src/graveyard/lin_glx_modern.c
new file mode 100644
index 0000000..853e3c7
--- /dev/null
+++ b/src/graveyard/lin_glx_modern.c
@@ -0,0 +1,243 @@
+
+static bool g_glx_context_error_occured = false;
+
+static int glx_context_error_handler(Display *display, XErrorEvent *event)
+{
+ printf("glx_context_error_handler received error\n");
+ g_glx_context_error_occured = true;
+ return 0;
+}
+
+// Helper to check for extension string presence. Copied from:
+// http://www.opengl.org/resources/features/OGLextensions/
+static bool gl_extension_supported(const char *extList, const char *extension)
+{
+ const char *start;
+ const char *where, *terminator;
+
+ // Extension names should not have spaces.
+ where = strchr(extension, ' ');
+ if (where || *extension == '\0')
+ return false;
+
+ for (start=extList;;)
+ {
+ where = strstr(start, extension);
+ if (!where)
+ break;
+
+ terminator = where + strlen(extension);
+
+ if (where == start || *(where - 1) == ' ')
+ if (*terminator == ' ' || *terminator == '\0')
+ return true;
+
+ start = terminator;
+ }
+
+ return false;
+}
+
+static GLXContext create_current_glx_context(Display *display, Window window, GLXFBConfig fbc)
+{
+ // get glXCreateContextAttribsARB
+ const char *glx_extensions = glXQueryExtensionsString(display, DefaultScreen( display));
+
+ glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
+ glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");
+
+ if (!gl_extension_supported(glx_extensions, "GLX_ARB_create_context") ||
+ !glXCreateContextAttribsARB)
+ {
+ printf("glXCreateContextAttribsARB() not found\n");
+ return 0;
+ }
+
+ // Install an X error handler so the application won't exit if GL 3.0
+ // context allocation fails.
+ //
+ // Note this error handler is global. All display connections in all threads
+ // of a process use the same error handler, so be sure to guard against other
+ // threads issuing X commands while this code is running.
+ g_glx_context_error_occured = false;
+ int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&glx_context_error_handler);
+
+ // create glx context
+ GLXContext glx_context = 0;
+ int context_attribs[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
+ GLX_CONTEXT_MINOR_VERSION_ARB, 3,
+ GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | GLX_CONTEXT_DEBUG_BIT_ARB,
+ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+ None
+ };
+
+ glx_context = glXCreateContextAttribsARB(display, fbc, 0, True, context_attribs);
+
+ // check for errors
+ XSync(display, False);
+ if (g_glx_context_error_occured || !glx_context)
+ {
+ printf("failed to create glx context\n");
+ return 0;
+ }
+ XSetErrorHandler(oldHandler);
+
+ glXMakeCurrent(display, window, glx_context);
+ return glx_context;
+}
+
+// Note: special case when debugging
+// stepping over glXChooseFBConfig or glXGetFBConfigs cause
+// _dl_catch_exception in libc
+// stepping more leads to
+// ?? () from /usr/lib/libnvidia-glcore.so.510.68.02
+//
+// Originally I used glXChooseFBConfig, then tried to fix it with glXGetFBConfigs
+// and now I leave it at that for the time being
+//
+// You can still debug by setting breakpoint later and continuing (c) until
+// that breakpoint is hit
+static bool get_framebuffer_config(Display *display, GLXFBConfig *config)
+{
+ int screen = XDefaultScreen(display);
+
+ int fbcs_count;
+ GLXFBConfig *fbcs = glXGetFBConfigs(display, screen, &fbcs_count);
+ if (!fbcs)
+ {
+ printf("no framebuffer configs received\n");
+ return 0;
+ }
+
+ int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
+ for (int i = 0; i < fbcs_count; i++)
+ {
+ int fbc_attribs[11];
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_X_RENDERABLE, &fbc_attribs[0]);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_DRAWABLE_TYPE, &fbc_attribs[1]);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_RENDER_TYPE, &fbc_attribs[2]);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_X_VISUAL_TYPE, &fbc_attribs[3]);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_RED_SIZE, &fbc_attribs[4]);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_GREEN_SIZE, &fbc_attribs[5]);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_BLUE_SIZE, &fbc_attribs[6]);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_ALPHA_SIZE, &fbc_attribs[7]);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_DEPTH_SIZE, &fbc_attribs[8]);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_STENCIL_SIZE, &fbc_attribs[9]);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_DOUBLEBUFFER, &fbc_attribs[10]);
+
+ if (fbc_attribs[0] == True &&
+ fbc_attribs[1] & GLX_WINDOW_BIT &&
+ fbc_attribs[2] & GLX_RGBA_BIT &&
+ fbc_attribs[3] == GLX_TRUE_COLOR &&
+ fbc_attribs[4] == 8 &&
+ fbc_attribs[5] == 8 &&
+ fbc_attribs[6] == 8 &&
+ fbc_attribs[7] == 8 &&
+ fbc_attribs[8] == 24 &&
+ fbc_attribs[9] == 8 &&
+ fbc_attribs[10] == True)
+ {
+ XVisualInfo *visual_info = glXGetVisualFromFBConfig(display, fbcs[i]);
+ if (visual_info)
+ {
+ int samp_buf, samples;
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+ glXGetFBConfigAttrib(display, fbcs[i], GLX_SAMPLES , &samples);
+
+ if (best_fbc < 0 || (samp_buf && samples) > best_num_samp)
+ {
+ best_fbc = i;
+ best_num_samp = samples;
+ }
+ if (worst_fbc < 0 || !samp_buf || samples < worst_num_samp)
+ {
+ worst_fbc = i;
+ worst_num_samp = samples;
+ }
+ }
+ XFree(visual_info);
+ }
+ }
+
+ if (best_fbc == -1)
+ {
+ return false;
+ }
+
+ *config = fbcs[best_fbc];
+ XFree(fbcs);
+
+ return true;
+}
+
+struct Platform_Window* platform_create_window(const char *name, int width, int height)
+{
+ Display *display = XOpenDisplay(0);
+ if (!display)
+ {
+ printf("XOpenDisplay(0) failed\n");
+ return 0;
+ }
+
+ Window root_window = XDefaultRootWindow(display);
+ if (!root_window)
+ {
+ printf("XDefaultRootWindow(display) failed\n");
+ XCloseDisplay(display);
+ return 0;
+ }
+
+ // glx version 1.3 required (for framebuffer configs and modern context creation)
+ int glx_major, glx_minor;
+ if (!glXQueryVersion(display, &glx_major, &glx_minor) ||
+ (glx_major == 1 && glx_minor < 3) ||
+ (glx_major < 1))
+ {
+ printf("invalid glx version\n");
+ return 0;
+ }
+
+ GLXFBConfig fbc;
+ if (!get_framebuffer_config(display, &fbc))
+ {
+ printf("no framebuffer config found\n");
+ return 0;
+ }
+
+ // set window attributes
+ XVisualInfo *visual_info = glXGetVisualFromFBConfig(display, fbc);
+ Colormap colormap = XCreateColormap(display, root_window, visual_info->visual, AllocNone);
+ long event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask;
+ int attribute_mask = CWEventMask | CWColormap;
+
+ XSetWindowAttributes attributes = {};
+ attributes.colormap = colormap;
+ attributes.event_mask = event_mask;
+
+ // create window
+ Window window = XCreateWindow(display, root_window, 0, 0, width, height, 0, visual_info->depth, InputOutput, visual_info->visual, attribute_mask, &attributes);
+ XFree(visual_info);
+ XStoreName(display, window, name);
+ XMapWindow(display, window);
+
+ GLXContext glx_context = create_current_glx_context(display, window, fbc);
+ if (!glx_context)
+ {
+ return 0;
+ }
+
+ // create platform window
+ struct Platform_Window *platform_window = (struct Platform_Window*)malloc(sizeof(struct Platform_Window));
+ if (!platform_window)
+ {
+ printf("malloc failed\n");
+ XCloseDisplay(display);
+ return 0;
+ }
+
+ platform_window->display = display;
+ platform_window->window = window;
+
+ return platform_window;
+}
diff --git a/src/graveyard/linux_net_stream.c b/src/graveyard/linux_net_stream.c
new file mode 100644
index 0000000..f08cdc5
--- /dev/null
+++ b/src/graveyard/linux_net_stream.c
@@ -0,0 +1,192 @@
+#include <basic/basic.h>
+#include <os/os.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+
+typedef struct {
+ int fd;
+} OSNetStream;
+
+
+internal_var u32 s_max_stream_count;
+internal_var OSNetStream *s_streams;
+
+internal_var u32 s_free_id_count;
+internal_var u32 *s_free_ids;
+
+
+
+internal_fn void
+os_net_stream_free(u32 stream_id)
+{
+ s_free_ids[s_free_id_count] = stream_id;
+ s_free_id_count += 1;
+}
+
+internal_fn u32
+os_net_stream_alloc()
+{
+ if (s_free_id_count == 0) {
+ return OS_NET_STREAM_ID_INVALID;
+ }
+
+ u32 id = s_free_ids[s_free_id_count-1];
+ s_free_id_count -= 1;
+
+ return id;
+}
+
+
+
+b32
+os_net_stream_send(u32 stream_id, void *buffer, size_t size)
+{
+ OSNetStream *stream = &s_streams[stream_id];
+
+ size_t sent = send(stream->fd, buffer, size, 0);
+ if (sent == -1) {
+ printf("send failed\n");
+ return false;
+ } else if (sent < size) {
+ printf("send only sent %ld/%ld bytes\n", sent, size);
+ return false;
+ }
+ return true;
+}
+
+b32
+os_net_stream_recv(u32 stream_id, void *buffer, size_t size)
+{
+ OSNetStream *stream = &s_streams[stream_id];
+
+ ssize_t recvd = recv(stream->fd, buffer, size, 0);
+ if (unlikely(recvd == -1 || recvd != size)) {
+ return false;
+ }
+
+ return true;
+}
+
+int
+os_net_stream_get_fd(u32 stream_id)
+{
+ OSNetStream *stream = &s_streams[stream_id];
+ return stream->fd;
+}
+
+void
+os_net_stream_close(u32 stream_id)
+{
+ OSNetStream *stream = &s_streams[stream_id];
+ close(stream->fd);
+}
+
+u32
+os_net_stream_accept(u32 listener_id)
+{
+ int fd;
+ struct sockaddr_in addr;
+ socklen_t addr_size = sizeof(addr);
+
+ OSNetStream *listener = &s_streams[listener_id];
+ fd = accept(listener->fd, (struct sockaddr*)&addr, &addr_size);
+ if (fd == -1) {
+ printf("accept failed\n");
+ return 0;
+ }
+
+ u32 stream_id = os_net_stream_alloc();
+ OSNetStream *stream = &s_streams[stream_id];
+ stream->fd = fd;
+ return stream_id;
+}
+
+u32
+os_net_stream_listen(u16 port)
+{
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ perror("socket()");
+ return 0;
+ }
+
+ int enable_reuse = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable_reuse, sizeof(int)) < 0) {
+ perror("setsockopt(SO_REUSEADDR)");
+ close(fd);
+ return 0;
+ }
+
+ 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 0;
+ }
+
+ int backlog = 128;
+ if (listen(fd, backlog) == -1) {
+ perror("listen()");
+ close(fd);
+ return 0;
+ }
+
+ u32 listener_id = os_net_stream_alloc();
+ OSNetStream *stream = &s_streams[listener_id];
+ stream->fd = fd;
+
+ return listener_id;
+}
+
+u32
+os_net_stream_connect(char *address, u16 port)
+{
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ printf("cant open socket\n");
+ return 0;
+ }
+
+ // note: connect binds a local address automatically
+ struct sockaddr_in target_addr;
+ memset(&target_addr, 0, sizeof(target_addr));
+ target_addr.sin_family = AF_INET;
+ target_addr.sin_port = htons(1338);
+ 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 0;
+ }
+
+ u32 stream_id = os_net_stream_alloc();
+ OSNetStream *stream = &s_streams[stream_id];
+ stream->fd = fd;
+
+ return stream_id;
+}
+
+void
+os_net_streams_init(MemArena *arena, size_t max_stream_count)
+{
+ s_max_stream_count = max_stream_count;
+ s_streams = mem_arena_push(arena, max_stream_count * sizeof(OSNetStream));
+
+ s_free_id_count = max_stream_count;
+ s_free_ids = mem_arena_push(arena, max_stream_count * sizeof(u32));
+ for (size_t i = 0; i < max_stream_count; i++) {
+ s_free_ids[i] = i;
+ }
+}
+
diff --git a/src/graveyard/win32/Win32WindowService.cpp b/src/graveyard/win32/Win32WindowService.cpp
new file mode 100644
index 0000000..80d17c0
--- /dev/null
+++ b/src/graveyard/win32/Win32WindowService.cpp
@@ -0,0 +1,375 @@
+#include <stdio.h>
+
+static DWORD g_main_thread_id;
+static PIXELFORMATDESCRIPTOR g_pfd;
+
+internal LRESULT
+win32__window_proc(HWND window, UINT message, WPARAM w_param, LPARAM l_param)
+{
+ LRESULT result = 0;
+ switch (message)
+ {
+ case WM_CLOSE:
+ case WM_QUIT:
+ case WM_CHAR:
+ case WM_SIZE:
+ {
+ PostThreadMessage(g_main_thread_id, message, w_param, l_param);
+ }
+ break;
+
+ case WM_KEYDOWN:
+ {
+ if (w_param >= VK_LEFT && w_param <= VK_DOWN)
+ {
+ PostThreadMessage(g_main_thread_id, message, w_param, l_param);
+ }
+ else
+ {
+ // let windows turn this into a WM_CHAR
+ result = DefWindowProcA(window, message, w_param, l_param);
+ }
+ }
+ break;
+
+ default:
+ {
+ result = DefWindowProcA(window, message, w_param, l_param);
+ }
+ break;
+ }
+
+ return result;
+}
+
+internal HWND
+win32__create_window(const char *name, int width, int height)
+{
+ const char *classname = "win32_window_class";
+
+ WNDCLASSEX window_class = {};
+ window_class.style = CS_OWNDC;
+ window_class.cbSize = sizeof(window_class);
+ window_class.lpfnWndProc = &win32__window_proc;
+ window_class.hInstance = GetModuleHandle(NULL);
+ window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
+ window_class.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ window_class.lpszClassName = classname;
+ if (RegisterClassExA(&window_class) == 0)
+ {
+ printf("RegisterClassEx() failed\n");
+ return 0;
+ }
+
+ HWND window = CreateWindowExA(0,
+ classname,
+ name,
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ width,
+ height,
+ 0,
+ 0,
+ window_class.hInstance,
+ 0);
+ if (!window)
+ {
+ printf("CreateWindowEx() failed\n");
+ }
+
+ return window;
+}
+
+internal void
+win32__destroy_window(HWND window)
+{
+ DestroyWindow(window);
+}
+
+internal LRESULT
+win32__service_window_proc(HWND window, UINT message, WPARAM w_param, LPARAM l_param)
+{
+ LRESULT result = 0;
+ switch (message)
+ {
+ case Win32WindowService::CREATE_WINDOW:
+ {
+ Win32WindowSettings *settings = (Win32WindowSettings*)w_param;
+
+ HWND handle = win32__create_window(settings->name, settings->width, settings->height);
+ result = (LRESULT)handle;
+ }
+ break;
+
+ case Win32WindowService::DESTROY_WINDOW:
+ {
+ win32__destroy_window(window);
+ }
+ break;
+
+ default:
+ {
+ result = DefWindowProc(window, message, w_param, l_param);
+ }
+ break;
+ }
+ return result;
+}
+
+HWND
+win32__create_service_window()
+{
+ PIXELFORMATDESCRIPTOR *pfd = &g_pfd;
+ memset(pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
+ pfd->nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ pfd->nVersion = 1;
+ pfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+ pfd->iPixelType = PFD_TYPE_RGBA;
+ pfd->cColorBits = 32;
+ pfd->cDepthBits = 24;
+ pfd->cStencilBits = 8;
+ pfd->iLayerType = PFD_MAIN_PLANE;
+
+ WNDCLASSEX swc = {};
+ swc.style = CS_OWNDC;
+ swc.cbSize = sizeof(swc);
+ swc.lpfnWndProc = &win32__service_window_proc;
+ swc.hInstance = GetModuleHandle(NULL);
+ swc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ swc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ swc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ swc.lpszClassName = "win32_service_window_class";
+ if (RegisterClassEx(&swc) == 0)
+ {
+ printf("RegisterClassEx() failed\n");
+ return 0;
+ }
+
+ const char *name = "win32_service_window";
+ HWND handle = CreateWindowEx(0,
+ swc.lpszClassName,
+ name,
+ 0,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ 0,
+ 0,
+ swc.hInstance,
+ 0);
+ if (!handle)
+ {
+ printf("CreateWindowEx() for service window failed\n");
+ return 0;
+ }
+
+ HDC dc = GetDC(handle);
+ if (!dc)
+ {
+ printf("GetDC() for service window failed\n");
+ DestroyWindow(handle);
+ return 0;
+ }
+
+ int pixel_format = ChoosePixelFormat(dc, &g_pfd);
+ if (pixel_format == 0)
+ {
+ printf("ChoosePixelFormat() for service window failed\n");
+ DestroyWindow(handle);
+ return 0;
+ }
+
+ BOOL pixel_format_set = SetPixelFormat(dc, pixel_format, &g_pfd);
+ if (pixel_format_set == FALSE)
+ {
+ printf("SetPixelFormat() for service window failed\n");
+ DestroyWindow(handle);
+ return 0;
+ }
+
+ return handle;
+}
+
+DWORD WINAPI
+win32__window_service_thread(LPVOID data)
+{
+ Win32WindowService *window_service = (Win32WindowService*)data;
+
+ // create service window and report back to main thread
+ HWND service_window = win32__create_service_window();
+ window_service->m_ServiceWindow = service_window;
+ if (!service_window)
+ {
+ PostThreadMessage(g_main_thread_id, Win32WindowService::SERVICE_WINDOW_NOT_CREATED, 0, 0);
+ return 0;
+ }
+ PostThreadMessage(g_main_thread_id, Win32WindowService::SERVICE_WINDOW_CREATED, 0, 0);
+
+ // get events for all windows, redirect to service window proc, let it report back
+ for (;;)
+ {
+ MSG message;
+ BOOL recvd = GetMessageA(&message, 0, 0, 0);
+ if (recvd == -1)
+ {
+ printf("GetMessage failed\n");
+ return 0;
+ }
+
+ TranslateMessage(&message);
+ DispatchMessage(&message);
+ }
+}
+
+bool
+Win32WindowService::Init()
+{
+ DWORD main_thread_id = GetCurrentThreadId();
+ g_main_thread_id = main_thread_id;
+
+ DWORD tid;
+ HANDLE thread = CreateThread(0, 0, win32__window_service_thread, this, 0, &tid);
+ if (!thread)
+ {
+ printf("CreatThread() when starting window manager failed\n");
+ return false;
+ }
+
+ // wait until service window is ready
+ for (;;)
+ {
+ MSG message;
+ GetMessageA(&message, 0, 0, 0);
+
+ if (message.message == Win32WindowService::SERVICE_WINDOW_CREATED)
+ return true;
+ else if (message.message == Win32WindowService::SERVICE_WINDOW_NOT_CREATED)
+ return false;
+ else assert(0);
+ }
+}
+
+Win32Window *
+Win32WindowService::CreateWin32Window(const char *name, int width, int height)
+{
+ static int counter = 0;
+ assert(counter++ == 0);
+
+ // let the service (other thread) create the window
+ Win32WindowSettings settings = {};
+ settings.name = name;
+ settings.width = width;
+ settings.height = height;
+
+ HWND handle = (HWND)SendMessage(m_ServiceWindow, Win32WindowService::CREATE_WINDOW, (WPARAM)&settings, 0);
+ if (!handle) return 0;
+
+
+ // do remaining initialization
+ HDC dc = GetDC(handle);
+ if (!dc)
+ {
+ printf("GetDC failed\n");
+ return 0;
+ }
+
+ PIXELFORMATDESCRIPTOR *pfd = &g_pfd;
+ int pixel_format = ChoosePixelFormat(dc, pfd);
+ if (pixel_format == 0)
+ {
+ printf("ChoosePixelFormat failed\n");
+ return false;
+ }
+
+ BOOL pixel_format_set = SetPixelFormat(dc, pixel_format, &g_pfd);
+ if (pixel_format_set == FALSE)
+ {
+ printf("SetPixelFormat() failed\n");
+ return false;
+ }
+
+ Win32Window *window = new Win32Window(handle, dc);
+ if (!window->RecreateOffscreenBuffer(width, height))
+ {
+ delete window;
+ window = 0;
+ }
+ return window;
+}
+
+void
+Win32WindowService::DestroyWin32Window(Win32Window *window)
+{
+ SendMessage(m_ServiceWindow, Win32WindowService::DESTROY_WINDOW, (WPARAM)window, 0);
+ delete window;
+}
+
+i32
+Win32WindowService::GetWindowEvents(Win32Window *window, Sys_Event *events, i32 max_event_count)
+{
+ i32 event_count = 0;
+
+ // This receives messages for all windows, so we can only have 1 window atm.
+ MSG msg;
+ while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE) && event_count < max_event_count)
+ {
+ Sys_Event *event = events + event_count;
+ switch (msg.message)
+ {
+#define ADD_KEY_EVENT(keycode) \
+ event->type = SYS_EVENT_KEY; \
+ event->key.c = (char)keycode; \
+ event_count += 1;
+ case WM_CHAR:
+ {
+ char keycode = (char)msg.wParam;
+ bool is_down = (msg.lParam & (1<<31)) == 0;
+ if (is_down)
+ {
+ ADD_KEY_EVENT(keycode);
+ }
+ }
+ break;
+
+ case WM_KEYDOWN:
+ {
+ if (msg.wParam == VK_LEFT) {ADD_KEY_EVENT(SYS_KEY_LEFT)}
+ else if (msg.wParam == VK_UP) {ADD_KEY_EVENT(SYS_KEY_UP)}
+ else if (msg.wParam == VK_RIGHT) {ADD_KEY_EVENT(SYS_KEY_RIGHT)}
+ else if (msg.wParam == VK_DOWN) {ADD_KEY_EVENT(SYS_KEY_DOWN)}
+ else if (msg.wParam == VK_TAB) {ADD_KEY_EVENT('\t')}
+ else if (msg.wParam == VK_RETURN){ADD_KEY_EVENT('\r')}
+ else {}
+ }
+ break;
+#undef ADD_KEY_EVENT
+
+ case WM_SIZE:
+ {
+ i32 width = (i32)LOWORD(msg.lParam);
+ i32 height = (i32)HIWORD(msg.lParam);
+ window->RecreateOffscreenBuffer(width, height);
+ }
+ break;
+
+ case WM_CLOSE:
+ case WM_QUIT:
+ {
+ return -1;
+ }
+ break;
+
+
+ default:
+ {
+ printf("unhandled window event %d\n", msg.message);
+ }
+ }
+ }
+
+ return event_count;
+}
+
diff --git a/src/graveyard/win32/Win32WindowService.h b/src/graveyard/win32/Win32WindowService.h
new file mode 100644
index 0000000..a02ef2b
--- /dev/null
+++ b/src/graveyard/win32/Win32WindowService.h
@@ -0,0 +1,33 @@
+struct Win32WindowSettings
+{
+ const char *name;
+ int width;
+ int height;
+};
+
+class Win32WindowService
+{
+public:
+ bool Init();
+
+ Win32Window *CreateWin32Window(const char *name, int width, int height);
+ void DestroyWin32Window(Win32Window *window);
+ i32 GetWindowEvents(Win32Window *window, Sys_Event *events, i32 max_event_count);
+ void ShowOffscreenBuffer(Win32Window *window);
+
+ enum
+ {
+ SERVICE_WINDOW_CREATED = (WM_USER + 0),
+ SERVICE_WINDOW_NOT_CREATED = (WM_USER + 1),
+ CREATE_WINDOW = (WM_USER + 2),
+ DESTROY_WINDOW = (WM_USER + 3),
+ WINDOW_CREATED = (WM_USER + 4),
+ WINDOW_DESTROYED = (WM_USER + 5)
+ };
+
+private:
+ void RecreateOffscreenBuffer(Sys_Offscreen_Buffer *buff, i32 width, i32 height);
+
+public:
+ HWND m_ServiceWindow;
+};
diff --git a/src/graveyard/win32/win32_florilia.c b/src/graveyard/win32/win32_florilia.c
new file mode 100644
index 0000000..0c2b807
--- /dev/null
+++ b/src/graveyard/win32/win32_florilia.c
@@ -0,0 +1,111 @@
+// Note: This is a minimal port to win32, the main target is linux.
+
+#include "../florilia_platform.h"
+#include <windows.h>
+
+
+#include <stdio.h>
+
+typedef struct {
+ BITMAPINFO bitmap_info;
+ i32 width;
+ i32 height;
+ u32 *pixels;
+} Win32_Offscreen_Buffer;
+
+typedef struct {
+ HWND window;
+ HDC dc;
+ Win32_Offscreen_Buffer offscreen_buffer;
+} Win32_Window;
+
+typedef struct {
+ const char *name;
+ int width;
+ int height;
+} Win32_Window_Settings;
+
+#include "win32_window.c"
+#include "win32_net.c"
+
+internal
+PLATFORM_GET_TIME(win32_get_time)
+{
+ florilia_time->seconds = 0;
+ florilia_time->nanoseconds = 0;
+#if 0
+ LARGE_INTEGER frequency;
+ if (QueryPerformanceFrequency(&frequency) == FALSE)
+ {
+ printf("frequency.QuadPart = %lld\n", frequency.QuadPart);
+ }
+
+ LARGE_INTEGER perf_counter;
+ if (QueryPerformanceCounter(&perf_counter) == FALSE)
+ {
+ printf("perf_counter.QuadPart = %lld\n", perf_counter.QuadPart);
+ }
+#endif
+}
+
+
+internal bool
+win32_init_florilia_memory(Florilia_Memory *florilia_memory, u64 permanent_size)
+{
+ void *storage = VirtualAlloc(0, permanent_size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+ if (!storage)
+ {
+ printf("VirtualAlloc failed\n");
+ return false;
+ }
+
+ florilia_memory->permanent_storage = storage;
+ florilia_memory->permanent_storage_size = permanent_size;
+ florilia_memory->platform.get_time = win32_get_time;
+ florilia_memory->platform.connect = win32_connect;
+ florilia_memory->platform.disconnect = win32_disconnect;
+ florilia_memory->platform.send = win32_send;
+ florilia_memory->platform.recv = win32_recv;
+ return true;
+}
+
+int WINAPI
+WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line, int cmd_show)
+{
+ Win32_Window window = {};
+
+ if (!win32_init_windowing()) return 0;
+ if (!win32_create_window(&window, "Florilia", 1024, 720)) return 0;
+ if (!win32_init_networking()) return 0;
+
+ Florilia_Memory florilia_memory = {};
+ Florilia_Event events[64];
+
+ if (!win32_init_florilia_memory(&florilia_memory, Megabytes(1))) return 0;
+ florilia_init(&florilia_memory);
+
+ for (;;)
+ {
+ u32 event_count = sizeof(events) / sizeof(events[0]);
+ if (!win32_process_window_events(&window, events, &event_count))
+ {
+ break;
+ }
+
+ Florilia_Offscreen_Buffer offscreen_buffer = {};
+
+ offscreen_buffer.red_shift = 16;
+ offscreen_buffer.green_shift = 8;
+ offscreen_buffer.blue_shift = 0;
+ offscreen_buffer.width = window.offscreen_buffer.width;
+ offscreen_buffer.height = window.offscreen_buffer.height;
+ offscreen_buffer.pixels = window.offscreen_buffer.pixels;
+ Florilia_Sound_Buffer sound_buffer = {};
+
+ florilia_update(&florilia_memory, &offscreen_buffer, &sound_buffer, events, event_count);
+
+ win32_show_offscreen_buffer(&window);
+ }
+
+ return 0;
+}
diff --git a/src/graveyard/win32/win32_fscord.cpp b/src/graveyard/win32/win32_fscord.cpp
new file mode 100644
index 0000000..b390e29
--- /dev/null
+++ b/src/graveyard/win32/win32_fscord.cpp
@@ -0,0 +1,181 @@
+#define WIN32_LEAN_AND_MEAN
+
+// disable annoying warnings
+#pragma warning(disable: 4820) // padding in structs
+#pragma warning(disable: 5045) // ???
+#define _WINSOCK_DEPRECATED_NO_WARNINGS // inet_addr
+
+#include <common/fscord_defs.h>
+#include <sys/sys.h>
+
+#include <windows.h>
+#include <winsock2.h>
+#include <malloc.h>
+
+#include "Win32Window.h"
+#include "Win32WindowService.h"
+#include "Win32Tcp.h"
+
+typedef struct
+{
+ Sys_Offscreen_Buffer sys_ob;
+} Win32_Offscreen_Buffer;
+
+typedef struct
+{
+ HWND handle;
+ HDC dc;
+ Win32_Offscreen_Buffer offscreen_buffer;
+} Win32_Window;
+
+typedef struct
+{
+ HWND service_window;
+ PIXELFORMATDESCRIPTOR pfd;
+} Win32Data;
+
+static Win32Data g_win32_data;
+static Win32WindowService g_win32_window_service;
+
+#include "Win32Window.cpp"
+#include "Win32WindowService.cpp"
+#include "Win32Tcp.cpp"
+
+void *
+sys_allocate_memory(size_t size)
+{
+ LPVOID addr = VirtualAlloc(0, size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+ if (!addr)
+ {
+ // GetLastError...
+ printf("VirtualAlloc failed\n");
+ }
+ return addr;
+}
+
+void
+sys_free_memory(void *address, size_t size)
+{
+ BOOL freed = VirtualFree(address, 0, MEM_RELEASE);
+ if (!freed)
+ {
+ printf("VirtualFree failed\n");
+ }
+}
+
+Sys_Time
+sys_get_time()
+{
+ // @Incomplete
+ Sys_Time time = {};
+ return time;
+}
+
+Sys_Window *
+sys_create_window(const char *name, i32 width, i32 height)
+{
+ static int counter = 0;
+ assert(counter++ == 0);
+ if (!g_win32_window_service.Init())
+ {
+ return 0;
+ }
+
+ Win32Window *win32_window = g_win32_window_service.CreateWin32Window(name, width, height);
+
+ Sys_Window *sys_window = (Sys_Window*)win32_window;
+ return sys_window;
+}
+
+void
+sys_destroy_window(Sys_Window *window)
+{
+ Win32Window *win32_window = (Win32Window*)window;
+ g_win32_window_service.DestroyWin32Window(win32_window);
+}
+
+i32
+sys_get_window_events(Sys_Window *sys_window, Sys_Event *events, i32 max_event_count)
+{
+ Win32Window *win32_window = (Win32Window*)sys_window;
+ i32 event_count = g_win32_window_service.GetWindowEvents(win32_window, events, max_event_count);
+ return event_count;
+}
+
+Sys_Offscreen_Buffer *
+sys_get_offscreen_buffer(Sys_Window *sys_window)
+{
+ Win32Window *window = (Win32Window*)sys_window;
+ Sys_Offscreen_Buffer *screen = window->GetOffscreenBuffer();
+ return screen;
+}
+
+void
+sys_show_offscreen_buffer(Sys_Window *sys_window)
+{
+ Win32Window *window = (Win32Window*)sys_window;
+ window->ShowOffscreenBuffer();
+}
+
+Sys_Sound_Device *
+sys_open_sound_device()
+{
+ return 0;
+}
+
+void
+sys_close_sound_device(Sys_Sound_Device *device)
+{
+}
+
+Sys_Sound_Buffer *
+sys_get_sound_buffer(Sys_Sound_Device *device)
+{
+ static Sys_Sound_Buffer buff = {};
+ return &buff;
+}
+
+void
+sys_play_sound_buffer(Sys_Sound_Device *device)
+{
+}
+
+Sys_Tcp *
+sys_connect(const char *address, u16 port)
+{
+ Win32Tcp *tcp = new Win32Tcp();
+ if (tcp->Connect(address, port)) {
+ return (Sys_Tcp*)tcp;
+ } else {
+ return 0;
+ }
+}
+
+void
+sys_disconnect(Sys_Tcp *sys_tcp)
+{
+ Win32Tcp *win32_tcp = (Win32Tcp*)sys_tcp;
+ delete win32_tcp;
+}
+
+bool
+sys_send(Sys_Tcp *sys_tcp, void *buffer, i64 size)
+{
+ assert(size <= S32_MAX);
+
+ Win32Tcp *win32_tcp = (Win32Tcp*)sys_tcp;
+ bool sent = win32_tcp->Send(buffer, (i32)size);
+ return sent;
+}
+
+i64
+sys_recv(Sys_Tcp *sys_tcp, void *buffer, i64 size)
+{
+ assert(size <= S32_MAX);
+
+ Win32Tcp *win32_tcp = (Win32Tcp*)sys_tcp;
+ int recvd = win32_tcp->Recv(buffer, (i32)size);
+ return recvd;
+}
+
+
diff --git a/src/graveyard/win32/win32_net.c b/src/graveyard/win32/win32_net.c
new file mode 100644
index 0000000..84a9373
--- /dev/null
+++ b/src/graveyard/win32/win32_net.c
@@ -0,0 +1,79 @@
+DEBUG_PLATFORM_CONNECT(win32_connect)
+{
+ SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == INVALID_SOCKET)
+ {
+ int error = WSAGetLastError();
+ printf("socket() failed, error = %d\n", error);
+ return -1;
+ }
+ printf("socket() success\n");
+
+ struct sockaddr_in server_addr;
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(port);
+ server_addr.sin_addr.s_addr = inet_addr(address);
+
+ int connected = connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
+ if (connected != 0)
+ {
+ int error = WSAGetLastError();
+ printf("connect() failed, error = %d\n", error);
+ closesocket(sock);
+ return -1;
+ }
+ printf("connect(...) success\n");
+
+ u_long mode = 1;
+ int result = ioctlsocket(sock, FIONBIO, &mode);
+ if (result != NO_ERROR)
+ {
+ int error = WSAGetLastError();
+ printf("ioctlsocket failed, error = %d\n", error);
+ closesocket(sock);
+ return -1;
+ }
+ printf("fcntl() success\n");
+
+ return sock;
+}
+
+DEBUG_PLATFORM_DISCONNECT(win32_disconnect)
+{
+ closesocket(netid);
+}
+
+internal
+DEBUG_PLATFORM_SEND(win32_send)
+{
+ printf("sending...\n");
+ int sent = send(netid, (const char*)buffer, size, 0);
+ printf("sent %d/%u bytes\n", sent, size);
+ return sent;
+}
+
+internal
+DEBUG_PLATFORM_RECV(win32_recv)
+{
+ printf("receving...\n");
+ int recvd = recv(netid, (char*)buffer, size, 0);
+ printf("recvd %d bytes\n", recvd);
+ if (recvd == SOCKET_ERROR)
+ {
+ return -1;
+ }
+ return recvd;
+}
+
+internal b32
+win32_init_networking()
+{
+ static WSADATA wsaData;
+ int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
+ if (iResult != 0) {
+ wprintf(L"WSAStartup failed: %d\n", iResult);
+ return false;
+ }
+ printf("WSAStartup done\n");
+ return true;
+}
diff --git a/src/graveyard/win32/win32_network_stream.cpp b/src/graveyard/win32/win32_network_stream.cpp
new file mode 100644
index 0000000..11a180e
--- /dev/null
+++ b/src/graveyard/win32/win32_network_stream.cpp
@@ -0,0 +1,90 @@
+#include <platform/platform.h>
+#include <windows.h>
+#include <stdlib.h>
+
+static b32 g_wsa_started;
+
+struct PlatformNetworkStream {
+ SOCKET socket;
+};
+
+PlatformNetworkStream *platform_network_stream_open_connection() {
+ PlatformNetworkStream *stream = (PlatformNetworkStream*)malloc(sizeof(*stream));
+ int err;
+
+ // start wsa
+ if (!g_wsa_started) {
+ WSADATA wsa_data;
+ WORD wsa_version = MAKEWORD(2, 2);
+ err = WSAStartup(wsa_version, &wsa_data);
+ if (err) {
+ printf("WSAStartup error %d\n", err);
+ return false;
+ }
+ g_wsa_started = true;
+ }
+
+ // open socket
+ SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == INVALID_SOCKET) {
+ printf("socket() wsa error: %d\n", WSAGetLastError());
+ return false;
+ }
+
+ // connect
+ struct sockaddr_in sa = {};
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(port);
+ sa.sin_addr.s_addr = inet_addr(address);
+
+ err = connect(sock, (SOCKADDR*)&sa, sizeof(sa));
+ if (err) {
+ printf("connect() wsa error: %d\n", WSAGetLastError());
+ closesocket(sock);
+ return false;
+ }
+
+ // make socket non-blocking
+ u_long io_mode = 1;
+ err = ioctlsocket(sock, FIONBIO, &io_mode);
+ if (err) {
+ printf("ioctlsocket() wsa error: %d\n", WSAGetLastError());
+ closesocket(sock);
+ return false;
+ }
+
+ m_Socket = sock;
+ return true;
+}
+
+void platform_network_stream_close(PlatformNetworkStream *stream) {
+ int err = closesocket(m_Socket);
+ if (err) {
+ printf("closesocket() wsa error: %d\n", WSAGetLastError());
+ }
+}
+
+bool platform_network_stream_send(PlatformNetworkStream *stream, void *buff, i32 size) {
+ int sent = send(m_Socket, (const char*)buff, size, 0);
+ if (sent == SOCKET_ERROR) {
+ int error_code = WSAGetLastError();
+ printf("send() wsa error = %d\n", error_code);
+ return false;
+ }
+ return true;
+}
+
+i64 platform_network_stream_recv(PlatformNetworkStream *stream, void *buff, i64 size) {
+ int recvd = recv(m_Socket, (char*)buff, size, 0);
+ if (recvd == SOCKET_ERROR) {
+ int error_code = WSAGetLastError();
+ if (error_code == WSAEWOULDBLOCK) {
+ return 0;
+ } else {
+ printf("recv() wsa error: %d\n", error_code);
+ return -1;
+ }
+ }
+ return recvd;
+}
+
diff --git a/src/graveyard/win32/win32_window.c b/src/graveyard/win32/win32_window.c
new file mode 100644
index 0000000..8a25fed
--- /dev/null
+++ b/src/graveyard/win32/win32_window.c
@@ -0,0 +1,389 @@
+#define WIN32_SERVICE_WINDOW_CREATED (WM_USER + 0)
+#define WIN32_SERVICE_WINDOW_NOT_CREATED (WM_USER + 1)
+#define WIN32_CREATE_WINDOW (WM_USER + 2)
+#define WIN32_DESTROY_WINDOW (WM_USER + 3)
+
+static HWND g_service_window;
+static DWORD g_main_thread_id;
+
+internal void
+init_pfd(PIXELFORMATDESCRIPTOR *pfd)
+{
+ memset(pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
+ pfd->nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ pfd->nVersion = 1;
+ pfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+ pfd->iPixelType = PFD_TYPE_RGBA;
+ pfd->cColorBits = 32;
+ pfd->cDepthBits = 24;
+ pfd->cStencilBits = 8;
+ pfd->iLayerType = PFD_MAIN_PLANE;
+}
+
+internal LRESULT
+window_proc(HWND window, UINT message, WPARAM w_param, LPARAM l_param)
+{
+ LRESULT result = 0;
+ switch (message)
+ {
+ case WM_CLOSE:
+ {
+ PostThreadMessage(g_main_thread_id, message, (WPARAM)window, l_param);
+ }
+ break;
+
+ case WM_CHAR:
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_QUIT:
+ case WM_SIZE:
+ {
+ PostThreadMessage(g_main_thread_id, message, w_param, l_param);
+ }
+ break;
+
+ default:
+ {
+ result = DefWindowProcA(window, message, w_param, l_param);
+ }
+ break;
+ }
+
+ return result;
+}
+
+internal HWND
+create_window(const char *name, int width, int height)
+{
+ const char *classname = "win32_window_class";
+
+ WNDCLASSEX window_class = {};
+ window_class.style = CS_OWNDC;
+ window_class.cbSize = sizeof(window_class);
+ window_class.lpfnWndProc = &window_proc;
+ window_class.hInstance = GetModuleHandle(NULL);
+ window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
+ window_class.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ window_class.lpszClassName = classname;
+ if (RegisterClassExA(&window_class) == 0)
+ {
+ printf("RegisterClassEx() failed\n");
+ return 0;
+ }
+
+ HWND window = CreateWindowExA(0,
+ classname,
+ name,
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ width,
+ height,
+ 0,
+ 0,
+ window_class.hInstance,
+ 0);
+ if (!window)
+ {
+ printf("CreateWindowEx() failed\n");
+ return 0;
+ }
+ return window;
+}
+
+LRESULT
+win32_service_window_proc(HWND window, UINT message, WPARAM w_param, LPARAM l_param)
+{
+ LRESULT result = 0;
+ switch (message)
+ {
+ case WIN32_CREATE_WINDOW:
+ {
+ Win32_Window_Settings *settings = (Win32_Window_Settings*)w_param;
+ return (LRESULT)create_window(settings->name, settings->width, settings->height);
+ }
+ break;
+
+ case WIN32_DESTROY_WINDOW:
+ {
+ DestroyWindow(window);
+ }
+ break;
+
+ default:
+ {
+ result = DefWindowProc(window, message, w_param, l_param);
+ }
+ break;
+ }
+ return result;
+}
+
+internal bool
+create_service_window()
+{
+ WNDCLASSEX service_window_class = {};
+ service_window_class.style = CS_OWNDC;
+ service_window_class.cbSize = sizeof(service_window_class);
+ service_window_class.lpfnWndProc = &win32_service_window_proc;
+ service_window_class.hInstance = GetModuleHandle(NULL);
+ service_window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ service_window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
+ service_window_class.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ service_window_class.lpszClassName = "win32_service_window_class";
+ if (RegisterClassEx(&service_window_class) == 0)
+ {
+ printf("RegisterClassEx() failed\n");
+ return 0;
+ }
+
+ const char *service_window_name = "win32_service_window";
+ HWND service_window = CreateWindowEx(0,
+ service_window_class.lpszClassName,
+ service_window_name,
+ 0,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ 0,
+ 0,
+ service_window_class.hInstance,
+ 0);
+ if (!service_window)
+ {
+ printf("CreateWindowEx() failed\n");
+ return 0;
+ }
+
+ HDC dc = GetDC(service_window);
+ if (!dc)
+ {
+ printf("GetDC() failed\n");
+ return false;
+ }
+
+ PIXELFORMATDESCRIPTOR pfd;
+ init_pfd(&pfd);
+
+ int pixel_format = ChoosePixelFormat(dc, &pfd);
+ if (pixel_format == 0)
+ {
+ printf("ChoosePixelFormat failed\n");
+ return false;
+ }
+
+ BOOL pixel_format_set = SetPixelFormat(dc, pixel_format, &pfd);
+ if (pixel_format_set == FALSE)
+ {
+ printf("SetPixelFormat() failed\n");
+ return false;
+ }
+
+ g_service_window = service_window;
+ return true;
+}
+
+DWORD WINAPI win32_service_window_thread(LPVOID lpParameter)
+{
+ if (!create_service_window())
+ {
+ PostThreadMessage(g_main_thread_id, WIN32_SERVICE_WINDOW_NOT_CREATED, 0, 0);
+ return 0;
+ }
+
+ PostThreadMessage(g_main_thread_id, WIN32_SERVICE_WINDOW_CREATED, 0, 0);
+
+ for (;;)
+ {
+ // receive messages for all windows (created by this thread)
+ MSG message;
+ BOOL recvd = GetMessageA(&message, 0, 0, 0);
+ if (recvd == -1)
+ {
+ // handle error
+ printf("GetMessage failed\n");
+ return 0;
+ }
+
+ TranslateMessage(&message);
+ DispatchMessage(&message);
+ }
+}
+
+internal void
+win32_show_offscreen_buffer(Win32_Window *window)
+{
+ Win32_Offscreen_Buffer *buff = &window->offscreen_buffer;
+ StretchDIBits(window->dc,
+ 0, 0, buff->width, buff->height,
+ 0, 0, buff->width, buff->height,
+ buff->pixels,
+ &buff->bitmap_info,
+ DIB_RGB_COLORS,
+ SRCCOPY);
+
+ SwapBuffers(window->dc);
+}
+
+internal b32
+win32_recreate_offscreen_buffer(Win32_Offscreen_Buffer *buff, i32 width, i32 height)
+{
+ i32 bytes_per_pixel = 4;
+ i32 pixels_size = width * height * bytes_per_pixel;
+
+ u32 *pixels = realloc(buff->pixels, pixels_size);
+
+ BITMAPINFO *bitmap_info = &buff->bitmap_info;
+ memset(bitmap_info->bmiColors, 0, sizeof(bitmap_info->bmiColors));
+ bitmap_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bitmap_info->bmiHeader.biWidth = width;
+ bitmap_info->bmiHeader.biHeight = height;
+ bitmap_info->bmiHeader.biPlanes = 1;
+ bitmap_info->bmiHeader.biBitCount = 32;
+ bitmap_info->bmiHeader.biCompression = BI_RGB;
+ bitmap_info->bmiHeader.biSizeImage = 0;
+ bitmap_info->bmiHeader.biXPelsPerMeter = 0;
+ bitmap_info->bmiHeader.biYPelsPerMeter = 0;
+ bitmap_info->bmiHeader.biClrUsed = 0;
+ bitmap_info->bmiHeader.biClrImportant = 0;
+
+ buff->width = width;
+ buff->height = height;
+ buff->pixels = pixels;
+ return true;
+}
+
+#define ADD_KEY_EVENT(_c) \
+ event->type = FLORILIA_EVENT_KEY; \
+ event->key.c = _c; \
+ *event_count += 1;
+
+internal bool
+win32_process_window_events(Win32_Window *window, Florilia_Event *events, u32 *event_count)
+{
+ u32 event_count_max = *event_count;
+ *event_count = 0;
+
+ MSG message;
+ while (*event_count < event_count_max && PeekMessage(&message, 0, 0, 0, PM_REMOVE))
+ {
+ Florilia_Event *event = events + *event_count;
+
+ switch (message.message)
+ {
+ case WM_SIZE:
+ {
+ UINT width = LOWORD(message.lParam);
+ UINT height = HIWORD(message.lParam);
+ win32_recreate_offscreen_buffer(&window->offscreen_buffer, width, height);
+ }
+ break;
+
+ case WM_CHAR:
+ {
+ if (message.wParam < 0 || message.wParam > 127)
+ break;
+
+ char c = (char)message.wParam;
+ ADD_KEY_EVENT(c);
+ }
+ break;
+
+ case WM_KEYDOWN:
+ {
+ switch (message.wParam)
+ {
+ case VK_LEFT: { ADD_KEY_EVENT(FLORILIA_KEY_LEFT); } break;
+ case VK_RIGHT: { ADD_KEY_EVENT(FLORILIA_KEY_RIGHT); } break;
+ case VK_UP: { ADD_KEY_EVENT(FLORILIA_KEY_UP); } break;
+ case VK_DOWN: { ADD_KEY_EVENT(FLORILIA_KEY_DOWN); } break;
+ case VK_DELETE: { ADD_KEY_EVENT(127 /* in ascii */); } break;
+ }
+ }
+ break;
+
+ case WM_CLOSE:
+ case WM_DESTROY:
+ {
+ return false;
+ }
+ break;
+
+ default:
+ {
+ return true;
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+#undef ADD_KEY_EVENT
+
+internal bool
+win32_create_window(Win32_Window *window, const char *name, int width, int height)
+{
+ // SendMessage returns after the request has been processed
+ Win32_Window_Settings settings = {name, width, height};
+ HWND handle = (HWND)SendMessage(g_service_window, WIN32_CREATE_WINDOW, (WPARAM)&settings, 0);
+ if (!handle)
+ return 0;
+
+ // We are back in our thread
+ HDC dc = GetDC(handle);
+ if (!dc)
+ {
+ printf("GetDC failed\n");
+ return 0;
+ }
+
+ PIXELFORMATDESCRIPTOR pfd;
+ init_pfd(&pfd);
+
+ int pixel_format = ChoosePixelFormat(dc, &pfd);
+ if (pixel_format == 0)
+ {
+ printf("ChoosePixelFormat failed\n");
+ return false;
+ }
+
+ BOOL pixel_format_set = SetPixelFormat(dc, pixel_format, &pfd);
+ if (pixel_format_set == FALSE)
+ {
+ printf("SetPixelFormat() failed\n");
+ return false;
+ }
+
+ window->window = handle;
+ window->dc = dc;
+ return window;
+}
+
+internal bool
+win32_init_windowing()
+{
+ g_main_thread_id = GetCurrentThreadId();
+
+ DWORD tid;
+ HANDLE thread = CreateThread(0, 0, win32_service_window_thread, 0, 0, &tid);
+ if (!thread)
+ {
+ printf("error: CreateThread(...) failed\n");
+ return false;
+ }
+
+ // wait until service window is ready
+ for (;;)
+ {
+ MSG message;
+ GetMessageA(&message, 0, 0, 0);
+
+ if (message.message == WIN32_SERVICE_WINDOW_CREATED)
+ return true;
+ else
+ return false;
+ }
+}
diff --git a/src/graveyard/win32/win32_window.cpp b/src/graveyard/win32/win32_window.cpp
new file mode 100644
index 0000000..6ec0891
--- /dev/null
+++ b/src/graveyard/win32/win32_window.cpp
@@ -0,0 +1,77 @@
+#include <platform/platform.h>
+#include <windows.h>
+
+struct Win32OffscreenBuffer {
+ PlatformOffscreenBuffer platform_offscreen_buffer;
+ BITMAPINFO bitmap_info;
+};
+
+struct PlatformWindow {
+ HWND wnd;
+ HDC dc;
+ Win32OffscreenBuffer offscreen_buffer;
+};
+
+Win32Window::Win32Window(HWND handle, HDC dc)
+{
+ m_Wnd = handle;
+ m_Dc = dc;
+ memset((void*)&m_OffscreenBuffer, 0, sizeof(m_OffscreenBuffer));
+}
+
+bool
+Win32Window::RecreateOffscreenBuffer(i32 width, i32 height)
+{
+ Sys_Offscreen_Buffer *sys_screen = &m_OffscreenBuffer.sys_offscreen_buffer;
+
+ sys_screen->red_shift = 16;
+ sys_screen->green_shift = 8;
+ sys_screen->blue_shift = 0;
+ sys_screen->alpha_shift = 24;
+
+ u32 *new_pixels = (u32*)malloc(sizeof(sys_screen->pixels[0]) * width * height);
+ if (!new_pixels)
+ {
+ return false;
+ }
+ free(sys_screen->pixels);
+
+ sys_screen->width = width;
+ sys_screen->height = height;
+ sys_screen->pixels = new_pixels;
+
+
+ BITMAPINFO *bmi = &m_OffscreenBuffer.bitmap_info;
+ memset(bmi, 0, sizeof(*bmi));
+ bmi->bmiHeader.biSize = sizeof(bmi->bmiHeader);
+ bmi->bmiHeader.biWidth = width;
+ bmi->bmiHeader.biHeight = height;
+ bmi->bmiHeader.biPlanes = 1;
+ bmi->bmiHeader.biBitCount = 32;
+ bmi->bmiHeader.biCompression = BI_RGB;
+
+ return true;
+}
+
+Sys_Offscreen_Buffer *
+Win32Window::GetOffscreenBuffer()
+{
+ Sys_Offscreen_Buffer *screen = &m_OffscreenBuffer.sys_offscreen_buffer;
+ return screen;
+}
+
+void
+Win32Window::ShowOffscreenBuffer()
+{
+ i32 width = m_OffscreenBuffer.sys_offscreen_buffer.width;
+ i32 height = m_OffscreenBuffer.sys_offscreen_buffer.height;
+ u32 *pixels = m_OffscreenBuffer.sys_offscreen_buffer.pixels;
+ BITMAPINFO *bmi = &m_OffscreenBuffer.bitmap_info;
+
+ StretchDIBits(m_Dc,
+ 0, 0, width, height,
+ 0, 0, width, height,
+ pixels, bmi, DIB_RGB_COLORS, SRCCOPY);
+
+}
+
diff --git a/src/graveyard/win32_wgl_modern.c b/src/graveyard/win32_wgl_modern.c
new file mode 100644
index 0000000..479dbfe
--- /dev/null
+++ b/src/graveyard/win32_wgl_modern.c
@@ -0,0 +1,206 @@
+internal bool
+create_service_window()
+{
+ WNDCLASSEX service_window_class = {};
+ service_window_class.style = CS_OWNDC;
+ service_window_class.cbSize = sizeof(service_window_class);
+ service_window_class.lpfnWndProc = &win32_service_window_proc;
+ service_window_class.hInstance = GetModuleHandle(NULL);
+ service_window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ service_window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
+ service_window_class.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ service_window_class.lpszClassName = "win32_service_window_class";
+ if (RegisterClassEx(&service_window_class) == 0)
+ {
+ printf("RegisterClassEx() failed\n");
+ return 0;
+ }
+
+ const char *service_window_name = "win32_service_window";
+ HWND service_window = CreateWindowEx(0,
+ service_window_class.lpszClassName,
+ service_window_name,
+ 0,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ 0,
+ 0,
+ service_window_class.hInstance,
+ 0);
+ if (!service_window)
+ {
+ printf("CreateWindowEx() failed\n");
+ return 0;
+ }
+
+ /*
+ * Create fake gl context, so I can create window with modern opengl context.
+ * Doing this with the service window prevents creating another 'fake' window.
+ */
+ HDC dc = GetDC(service_window);
+ if (!dc)
+ {
+ printf("GetDC() failed\n");
+ return false;
+ }
+
+ PIXELFORMATDESCRIPTOR pfd;
+ init_pfd(&pfd);
+
+ int pixel_format = ChoosePixelFormat(dc, &pfd);
+ if (pixel_format == 0)
+ {
+ printf("ChoosePixelFormat failed\n");
+ return false;
+ }
+
+ BOOL pixel_format_set = SetPixelFormat(dc, pixel_format, &pfd);
+ if (pixel_format_set == FALSE)
+ {
+ printf("SetPixelFormat() failed\n");
+ return false;
+ }
+
+ HGLRC glrc = wglCreateContext(dc);
+ if (!glrc)
+ {
+ printf("wglCreateContext() failed\n");
+ return false;
+ }
+
+ BOOL made_current = wglMakeCurrent(dc, glrc);
+ if (made_current == FALSE)
+ {
+ printf("wglMakeCurrent() failed\n");
+ return false;
+ }
+
+ // TODO: check if extensions are actually supported
+ // "WGL_ARB_pixel_format"
+ // "WGL_ARB_create_context"
+
+ // TODO: check all return values indicating invalid function from wglGetProcAddress
+ // msdn: 0 https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-wglgetprocaddress
+ // khronos: (void*){0,1,2,3,-1} https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions#Windows_2
+ wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
+ wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
+ if (!wglChoosePixelFormatARB || !wglCreateContextAttribsARB)
+ {
+ printf("wgl functions to create context not received\n");
+ return false;
+ }
+
+ g_service_window = service_window;
+ return true;
+}
+
+Win32_Window* platform_create_window(const char *name, int width, int height)
+{
+ Win32_Window *window = (Win32_Window*)malloc(sizeof(Platform_Window));
+ if (!window)
+ {
+ printf("out of memory to create window\n");
+ return 0;
+ }
+
+ // Note: SendMessage returns when execution is finished, so settings can be on stack
+ Win32_Window_Settings settings = {name, width, height};
+ HWND handle = (HWND)SendMessage(g_service_window, WIN32_CREATE_WINDOW, (WPARAM)&settings, 0);
+ if (!handle)
+ return 0;
+
+ HDC dc = GetDC(handle);
+ if (!dc)
+ {
+ printf("GetDC failed\n");
+ return 0;
+ }
+
+ const int pixel_format_attribs[] =
+ {
+ WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
+ WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
+ WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
+ WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
+ WGL_COLOR_BITS_ARB, 32,
+ WGL_DEPTH_BITS_ARB, 24,
+ WGL_STENCIL_BITS_ARB, 8,
+ 0, // End
+ };
+
+ int pixel_format;
+ UINT cnt_pixel_formats;
+ if (wglChoosePixelFormatARB(dc, pixel_format_attribs, NULL, 1, &pixel_format, &cnt_pixel_formats) == FALSE)
+ {
+ printf("wglChoosePixelFormat return false\n");
+ return 0;
+ }
+
+ PIXELFORMATDESCRIPTOR pfd;
+ init_pfd(&pfd);
+
+ if (SetPixelFormat(dc, pixel_format, &pfd) == FALSE)
+ {
+ printf("SetPixelFormat return false\n");
+ return 0;
+ }
+
+ int context_attribs[] =
+ {
+ WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
+ WGL_CONTEXT_MINOR_VERSION_ARB, 3,
+ WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
+ 0
+ };
+
+ HGLRC glrc = wglCreateContextAttribsARB(dc, 0, context_attribs);
+ if (!glrc)
+ {
+ printf("wglCreateContextAttribsARB failed\n");
+ return 0;
+ }
+
+ if (wglMakeCurrent(dc, glrc) == FALSE)
+ {
+ printf("wglMakeCurrent failed\n");
+ return 0;
+ }
+
+ window->window = handle;
+ window->dc = dc;
+ window->glrc = glrc;
+ return window;
+}
+
+bool platform_init_windowing()
+{
+ g_main_thread_id = GetCurrentThreadId();
+ g_opengl_module = LoadLibraryA("opengl32.dll");
+ if (!g_opengl_module)
+ {
+ printf("can't open opengl32.dll\n");
+ return false;
+ }
+
+ DWORD tid;
+ HANDLE thread = CreateThread(0, 0, win32_service_window_thread, 0, 0, &tid);
+ if (!thread)
+ {
+ printf("error: CreateThread(...) failed\n");
+ return false;
+ }
+
+ // wait until service window is ready
+ for (;;)
+ {
+ MSG message;
+ GetMessageA(&message, 0, 0, 0);
+
+ if (message.message == WIN32_SERVICE_WINDOW_CREATED)
+ return true;
+ else
+ return false;
+ }
+}