summaryrefslogtreecommitdiff
path: root/examples/platform
diff options
context:
space:
mode:
authorkkard2 <[email protected]>2025-09-22 17:21:30 +0200
committerkkard2 <[email protected]>2025-09-22 17:21:30 +0200
commita17e7369bfe9cb4a23225084a396ec92823bad2b (patch)
treeb229b07c1a1c131467f61010eec985b62293897e /examples/platform
parent2d0894f9887f9e100d7546b6ca93934eced93e39 (diff)
reorganize examples
Diffstat (limited to 'examples/platform')
-rw-r--r--examples/platform/platform_win32.c219
-rw-r--r--examples/platform/platform_x11.c114
2 files changed, 333 insertions, 0 deletions
diff --git a/examples/platform/platform_win32.c b/examples/platform/platform_win32.c
new file mode 100644
index 0000000..5984e75
--- /dev/null
+++ b/examples/platform/platform_win32.c
@@ -0,0 +1,219 @@
+#define UNICODE
+#include <windows.h>
+#include <windowsx.h>
+#include <assert.h>
+#include <stdio.h>
+#include <io.h>
+
+#include <fcntl.h>
+
+#define SPONGE_IMPLEMENTATION
+#include "../../sponge.h"
+
+#define STB_IMAGE_IMPLEMENTATION
+#include "../vendor/stb_image.h"
+
+#include "../example.h"
+
+#define DEFAULT_WIDTH 1280
+#define DEFAULT_HEIGHT 720
+
+#define TARGET_FRAME_TIME_US 16666
+
+static size_t pixels_buffer_count;
+static sponge_Texture canvas;
+static float *depths;
+static BITMAPINFO bmi;
+
+int update_canvas(sponge_Texture *canvas, float **depths, uint32_t new_width, uint32_t new_height) {
+ size_t new_count = new_width * new_height;
+ if (new_count > pixels_buffer_count) {
+ sponge_Color32 *new_pixels_buffer = malloc(new_count * sizeof(uint32_t));
+ float *new_depths = malloc(new_count * sizeof(float));
+ if (!new_pixels_buffer || !new_depths) {
+ free(new_pixels_buffer);
+ free(new_pixels_buffer);
+ return 0;
+ }
+ free(canvas->pixels);
+ free(*depths);
+ canvas->pixels = new_pixels_buffer;
+ *depths = new_depths;
+ pixels_buffer_count = new_count;
+ }
+ canvas->width = new_width;
+ canvas->height = new_height;
+ canvas->stride_pixels = new_width; // TODO(kard): think about this if we want image to stay the same without redraw
+
+ ZeroMemory(&bmi, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = new_width;
+ bmi.bmiHeader.biHeight = -(int32_t)new_height; // negative = top-down
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ return 1;
+}
+
+// TODO(kard): MSVC complains about freopen (should be freopen_s apparently)
+void redirect_io_to_console() {
+ AllocConsole();
+ FILE *fp;
+
+ assert(!freopen_s(&fp, "CONOUT$", "w", stdout));
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ assert(!freopen_s(&fp, "CONOUT$", "w", stderr));
+ setvbuf(stderr, NULL, _IONBF, 0);
+
+ assert(!freopen_s(&fp, "CONIN$", "r", stdin));
+ setvbuf(stdin, NULL, _IONBF, 0);
+}
+
+LRESULT __stdcall WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ switch (uMsg) {
+ case WM_DESTROY: {
+ PostQuitMessage(0);
+ return 0;
+ }
+ case WM_SIZE: {
+ uint32_t width = LOWORD(lParam);
+ uint32_t height = HIWORD(lParam);
+ // TODO(kard): handle properly? (what does that even mean)
+ assert(update_canvas(&canvas, &depths, width, height));
+ }
+ case WM_MOUSEMOVE: {
+ int32_t x = GET_X_LPARAM(lParam);
+ int32_t y = GET_Y_LPARAM(lParam);
+ mouse_move(x, y);
+ }
+ default:
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+ }
+}
+
+int __stdcall WinMain(
+ HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nShowCmd
+) {
+ redirect_io_to_console();
+
+ const wchar_t CLASS_NAME[] = L"sponge";
+
+ WNDCLASS wc = { 0 };
+
+ wc.lpfnWndProc = WindowProc;
+ wc.hInstance = hInstance;
+ wc.lpszClassName = CLASS_NAME;
+
+ RegisterClass(&wc);
+
+ HWND hwnd = CreateWindowEx(
+ 0,
+ CLASS_NAME,
+ L"sponge",
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ NULL,
+ NULL,
+ hInstance,
+ NULL
+ );
+
+ if (hwnd == NULL)
+ return 1;
+
+ update_canvas(&canvas, &depths, DEFAULT_WIDTH, DEFAULT_HEIGHT);
+
+ ShowWindow(hwnd, nShowCmd);
+
+ MSG msg = {0};
+ BOOL running = TRUE;
+
+ init();
+
+ LARGE_INTEGER qpc_freq;
+ LARGE_INTEGER qpc_start;
+ LARGE_INTEGER qpc_end;
+ LARGE_INTEGER qpc_draw;
+ LARGE_INTEGER qpc_frame;
+
+ LARGE_INTEGER qpc_running_draw = { 0 };
+ size_t qpc_running_draw_count = 0;
+
+ QueryPerformanceFrequency(&qpc_freq);
+ QueryPerformanceCounter(&qpc_start);
+
+ while (running) {
+ QueryPerformanceCounter(&qpc_start);
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ if (msg.message == WM_QUIT) {
+ running = FALSE;
+ break;
+ }
+
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ if (!running) break;
+
+ if (sponge_texture_valid(canvas))
+ draw_frame_3d(canvas, depths);
+
+ QueryPerformanceCounter(&qpc_end);
+ qpc_draw.QuadPart = qpc_end.QuadPart - qpc_start.QuadPart;
+ qpc_draw.QuadPart *= 1000000; // microseconds
+ qpc_draw.QuadPart /= qpc_freq.QuadPart;
+ qpc_running_draw.QuadPart += qpc_draw.QuadPart;
+ qpc_running_draw_count++;
+
+ HDC hdc = GetDC(hwnd);
+ StretchDIBits(
+ hdc,
+ // TODO(kard): check what happens if canvas is bigger than window (it shouldn't differ currently)
+ 0, 0, canvas.width, canvas.height, // dest rectangle
+ 0, 0, canvas.width, canvas.height, // src rectangle
+ canvas.pixels,
+ &bmi,
+ DIB_RGB_COLORS,
+ SRCCOPY
+ );
+
+ InvalidateRect(hwnd, NULL, FALSE);
+ UpdateWindow(hwnd);
+
+ QueryPerformanceCounter(&qpc_end);
+ qpc_frame.QuadPart = qpc_end.QuadPart - qpc_start.QuadPart;
+ qpc_frame.QuadPart *= 1000000; // microseconds
+ qpc_frame.QuadPart /= qpc_freq.QuadPart;
+
+ fprintf(stderr, "draw time: %3lld.%03lldms (avg: %3lld.%03lldms), frame time: %3lld.%03lldms\n",
+ qpc_draw.QuadPart / 1000, qpc_draw.QuadPart % 1000,
+ qpc_running_draw.QuadPart / qpc_running_draw_count / 1000, qpc_running_draw.QuadPart / qpc_running_draw_count % 1000,
+ qpc_frame.QuadPart / 1000, qpc_frame.QuadPart % 1000);
+
+ // TODO(kard):
+ // 1. this can probably be done cleaner
+ // 2. resetting after every frame is wrong, it should be a value across frames (see 1.)
+ if (qpc_frame.QuadPart < TARGET_FRAME_TIME_US) {
+ LARGE_INTEGER remaining;
+ LARGE_INTEGER actual_end;
+ actual_end.QuadPart = qpc_start.QuadPart + (TARGET_FRAME_TIME_US * qpc_freq.QuadPart / 1000000);
+ remaining.QuadPart = (actual_end.QuadPart - qpc_end.QuadPart) * 1000000 / qpc_freq.QuadPart;
+ if (remaining.QuadPart > 2000) {
+ Sleep((DWORD)((remaining.QuadPart - 1000) / 1000));
+ }
+
+ while (qpc_end.QuadPart < actual_end.QuadPart) {
+ QueryPerformanceCounter(&qpc_end);
+ }
+ }
+
+ Sleep(16);
+ }
+
+ return (int) msg.wParam;
+}
diff --git a/examples/platform/platform_x11.c b/examples/platform/platform_x11.c
new file mode 100644
index 0000000..7887d11
--- /dev/null
+++ b/examples/platform/platform_x11.c
@@ -0,0 +1,114 @@
+// TODO(kard): destroy this file, i don't care about platform layer rn this is some gpt trash
+// it flickers so bad
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+// TODO(kard): we should probably put more thought into this
+#include "example.c"
+
+#define DEFAULT_WIDTH 256
+#define DEFAULT_HEIGHT 256
+
+static sponge_Canvas canvas = { 0 };
+static Display *dpy;
+static XImage *ximg = 0;
+
+
+void init_canvas(uint32_t width, uint32_t height) {
+ if (ximg) {
+ XDestroyImage(ximg); // also frees data (why??)
+ // TODO(kard): you can set ximg->data to null before XDestroyImage
+ ximg = NULL;
+ }
+
+ canvas.pixels = (uint32_t*)malloc(width * height * 4);
+ if (!canvas.pixels) {
+ fprintf(stderr, "failed to allocate pixel buffer\n");
+ exit(1);
+ }
+ canvas.width = width;
+ canvas.height = height;
+ canvas.stride = width;
+
+ // TODO(kard): i assume this can fail
+ ximg = XCreateImage(
+ dpy,
+ DefaultVisual(dpy, 0),
+ DefaultDepth(dpy, 0),
+ ZPixmap,
+ 0,
+ (char*)canvas.pixels,
+ width,
+ height,
+ 32,
+ 0
+ );
+}
+
+int main() {
+ dpy = XOpenDisplay(NULL);
+ if (!dpy) {
+ fprintf(stderr, "failed to open display\n");
+ return 1;
+ }
+
+ int screen = DefaultScreen(dpy);
+
+ // TODO(kard): i assume all of the following x functions can fail
+ // clanker only checked XOpenDisplay for some reason
+
+ Window win = XCreateSimpleWindow(
+ dpy,
+ RootWindow(dpy, screen),
+ 10, 10, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ 1,
+ BlackPixel(dpy, screen),
+ BlackPixel(dpy, screen)
+ );
+
+ XStoreName(dpy, win, "sponge");
+ XSelectInput(dpy, win, ExposureMask | KeyPressMask | StructureNotifyMask);
+
+ GC gc = DefaultGC(dpy, screen);
+
+ XMapWindow(dpy, win);
+
+ init_canvas(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+
+ int running = 1;
+ while (running) {
+ while (XPending(dpy)) {
+ XEvent ev;
+ XNextEvent(dpy, &ev);
+ switch (ev.type) {
+ case Expose:
+ //draw_frame(canvas);
+ break;
+ case ConfigureNotify:
+ // TODO(kard): this is some implicit signed to unsigned conversion
+ init_canvas(ev.xconfigure.width, ev.xconfigure.height);
+ break;
+ case KeyPress:
+ break;
+ }
+ }
+
+ draw_frame(canvas);
+ XPutImage(dpy, win, gc, ximg, 0, 0, 0, 0, canvas.width, canvas.height);
+
+ usleep(16000);
+ }
+
+ if (ximg) {
+ ximg->data = NULL; // prevent double free
+ XDestroyImage(ximg);
+ }
+
+ XDestroyWindow(dpy, win);
+ XCloseDisplay(dpy);
+ return 0;
+}