diff options
Diffstat (limited to 'examples/platform')
| -rw-r--r-- | examples/platform/platform_win32.c | 219 | ||||
| -rw-r--r-- | examples/platform/platform_x11.c | 114 |
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; +} |
