summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authordawidg81 <dawidgorski.m@gmail.com>2026-02-13 10:26:29 +0100
committerdawidg81 <dawidgorski.m@gmail.com>2026-02-13 10:26:29 +0100
commit6415fc25141eef3d3efc7d7ac67bb564c499e7e0 (patch)
tree1363475b1e07ab499ae10fcc6679d207b3a58c96 /lib
parentc95e1596016060c7a13ff831cd3e59efb9232fc1 (diff)
Updated graphics.cpp
Diffstat (limited to 'lib')
-rw-r--r--lib/graphics.cpp549
1 files changed, 541 insertions, 8 deletions
diff --git a/lib/graphics.cpp b/lib/graphics.cpp
index 827303e..c41ac0e 100644
--- a/lib/graphics.cpp
+++ b/lib/graphics.cpp
@@ -210,13 +210,546 @@ void delay(uint32_t milliseconds) {
#endif // USE_SDL
#ifdef USE_WIN32
-// Win32 implementation would go here
-// For now, this is a placeholder
-#error "Win32 backend not yet implemented"
-#endif
+
+#include <windows.h>
+#include <vector>
+
+// Platform-specific window handle structure for Win32
+struct WindowHandle {
+ HWND hwnd;
+ HDC hdc;
+ HDC memDC;
+ HBITMAP memBitmap;
+ HBITMAP oldBitmap;
+ int width;
+ int height;
+ bool shouldClose;
+ COLORREF currentColor;
+
+ WindowHandle() : hwnd(nullptr), hdc(nullptr), memDC(nullptr),
+ memBitmap(nullptr), oldBitmap(nullptr),
+ width(0), height(0), shouldClose(false),
+ currentColor(RGB(255, 255, 255)) {}
+};
+
+// Global window class name
+static const char* WINDOW_CLASS_NAME = "LibGraffikWindowClass";
+static bool classRegistered = false;
+
+// Window procedure
+LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ WindowHandle* handle = (WindowHandle*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch (uMsg) {
+ case WM_CLOSE:
+ if (handle) {
+ handle->shouldClose = true;
+ }
+ return 0;
+
+ case WM_KEYDOWN:
+ if (wParam == VK_ESCAPE && handle) {
+ handle->shouldClose = true;
+ }
+ return 0;
+
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(hwnd, &ps);
+ // Will be handled by swapBuffers
+ EndPaint(hwnd, &ps);
+ return 0;
+ }
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0;
+ }
+
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+WindowHandle* createWindow(const char* title, int width, int height) {
+ // Register window class if not already registered
+ if (!classRegistered) {
+ WNDCLASSA wc = {};
+ wc.lpfnWndProc = WindowProc;
+ wc.hInstance = GetModuleHandle(nullptr);
+ wc.lpszClassName = WINDOW_CLASS_NAME;
+ wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+
+ if (!RegisterClassA(&wc)) {
+ return nullptr;
+ }
+ classRegistered = true;
+ }
+
+ WindowHandle* handle = new WindowHandle();
+ handle->width = width;
+ handle->height = height;
+
+ // Create window
+ handle->hwnd = CreateWindowExA(
+ 0,
+ WINDOW_CLASS_NAME,
+ title,
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ width, height,
+ nullptr,
+ nullptr,
+ GetModuleHandle(nullptr),
+ nullptr
+ );
+
+ if (!handle->hwnd) {
+ delete handle;
+ return nullptr;
+ }
+
+ // Store handle in window user data
+ SetWindowLongPtr(handle->hwnd, GWLP_USERDATA, (LONG_PTR)handle);
+
+ // Get device context
+ handle->hdc = GetDC(handle->hwnd);
+
+ // Create memory DC for double buffering
+ handle->memDC = CreateCompatibleDC(handle->hdc);
+ handle->memBitmap = CreateCompatibleBitmap(handle->hdc, width, height);
+ handle->oldBitmap = (HBITMAP)SelectObject(handle->memDC, handle->memBitmap);
+
+ // Show window
+ ShowWindow(handle->hwnd, SW_SHOW);
+ UpdateWindow(handle->hwnd);
+
+ return handle;
+}
+
+void destroyWindow(WindowHandle* window) {
+ if (!window) return;
+
+ if (window->memDC) {
+ if (window->oldBitmap) {
+ SelectObject(window->memDC, window->oldBitmap);
+ }
+ DeleteDC(window->memDC);
+ }
+
+ if (window->memBitmap) {
+ DeleteObject(window->memBitmap);
+ }
+
+ if (window->hdc) {
+ ReleaseDC(window->hwnd, window->hdc);
+ }
+
+ if (window->hwnd) {
+ DestroyWindow(window->hwnd);
+ }
+
+ delete window;
+}
+
+bool windowShouldClose(WindowHandle* window) {
+ if (!window) return true;
+ return window->shouldClose;
+}
+
+void pollEvents(WindowHandle* window) {
+ if (!window) return;
+
+ MSG msg;
+ while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+void swapBuffers(WindowHandle* window) {
+ if (!window || !window->hdc || !window->memDC) return;
+
+ // Copy from memory DC to window DC
+ BitBlt(window->hdc, 0, 0, window->width, window->height,
+ window->memDC, 0, 0, SRCCOPY);
+}
+
+void clearScreen(WindowHandle* window, const Color& color) {
+ if (!window || !window->memDC) return;
+
+ RECT rect = {0, 0, window->width, window->height};
+ HBRUSH brush = CreateSolidBrush(RGB(color.r, color.g, color.b));
+ FillRect(window->memDC, &rect, brush);
+ DeleteObject(brush);
+}
+
+void setDrawColor(WindowHandle* window, const Color& color) {
+ if (!window) return;
+ window->currentColor = RGB(color.r, color.g, color.b);
+}
+
+void drawLine(WindowHandle* window, int x1, int y1, int x2, int y2, const Color& color) {
+ if (!window || !window->memDC) return;
+
+ HPEN pen = CreatePen(PS_SOLID, 1, RGB(color.r, color.g, color.b));
+ HPEN oldPen = (HPEN)SelectObject(window->memDC, pen);
+
+ MoveToEx(window->memDC, x1, y1, nullptr);
+ LineTo(window->memDC, x2, y2);
+
+ SelectObject(window->memDC, oldPen);
+ DeleteObject(pen);
+}
+
+void drawRectangle(WindowHandle* window, int x, int y, int width, int height, const Color& color) {
+ if (!window || !window->memDC) return;
+
+ HPEN pen = CreatePen(PS_SOLID, 1, RGB(color.r, color.g, color.b));
+ HPEN oldPen = (HPEN)SelectObject(window->memDC, pen);
+ HBRUSH oldBrush = (HBRUSH)SelectObject(window->memDC, GetStockObject(NULL_BRUSH));
+
+ Rectangle(window->memDC, x, y, x + width, y + height);
+
+ SelectObject(window->memDC, oldPen);
+ SelectObject(window->memDC, oldBrush);
+ DeleteObject(pen);
+}
+
+void drawFilledRectangle(WindowHandle* window, int x, int y, int width, int height, const Color& color) {
+ if (!window || !window->memDC) return;
+
+ RECT rect = {x, y, x + width, y + height};
+ HBRUSH brush = CreateSolidBrush(RGB(color.r, color.g, color.b));
+ FillRect(window->memDC, &rect, brush);
+ DeleteObject(brush);
+}
+
+void drawPixel(WindowHandle* window, int x, int y, const Color& color) {
+ if (!window || !window->memDC) return;
+
+ SetPixel(window->memDC, x, y, RGB(color.r, color.g, color.b));
+}
+
+// Helper function for drawing circles using midpoint circle algorithm
+static void drawCirclePoints(HDC dc, int xc, int yc, int x, int y, COLORREF color) {
+ SetPixel(dc, xc + x, yc + y, color);
+ SetPixel(dc, xc - x, yc + y, color);
+ SetPixel(dc, xc + x, yc - y, color);
+ SetPixel(dc, xc - x, yc - y, color);
+ SetPixel(dc, xc + y, yc + x, color);
+ SetPixel(dc, xc - y, yc + x, color);
+ SetPixel(dc, xc + y, yc - x, color);
+ SetPixel(dc, xc - y, yc - x, color);
+}
+
+void drawCircle(WindowHandle* window, int centerX, int centerY, int radius, const Color& color) {
+ if (!window || !window->memDC) return;
+
+ COLORREF col = RGB(color.r, color.g, color.b);
+ int x = 0;
+ int y = radius;
+ int d = 3 - 2 * radius;
+
+ drawCirclePoints(window->memDC, centerX, centerY, x, y, col);
+
+ while (y >= x) {
+ x++;
+
+ if (d > 0) {
+ y--;
+ d = d + 4 * (x - y) + 10;
+ } else {
+ d = d + 4 * x + 6;
+ }
+
+ drawCirclePoints(window->memDC, centerX, centerY, x, y, col);
+ }
+}
+
+void drawFilledCircle(WindowHandle* window, int centerX, int centerY, int radius, const Color& color) {
+ if (!window || !window->memDC) return;
+
+ COLORREF col = RGB(color.r, color.g, color.b);
+
+ for (int y = -radius; y <= radius; y++) {
+ for (int x = -radius; x <= radius; x++) {
+ if (x * x + y * y <= radius * radius) {
+ SetPixel(window->memDC, centerX + x, centerY + y, col);
+ }
+ }
+ }
+}
+
+void delay(uint32_t milliseconds) {
+ Sleep(milliseconds);
+}
+
+#endif // USE_WIN32
#ifdef USE_X11
-// X11 implementation would go here
-// For now, this is a placeholder
-#error "X11 backend not yet implemented"
-#endif \ No newline at end of file
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <unistd.h>
+#include <cstring>
+
+// Platform-specific window handle structure for X11
+struct WindowHandle {
+ Display* display;
+ Window window;
+ GC gc;
+ Pixmap backBuffer;
+ int width;
+ int height;
+ bool shouldClose;
+ Atom wmDeleteMessage;
+ unsigned long currentColor;
+
+ WindowHandle() : display(nullptr), window(0), gc(nullptr),
+ backBuffer(0), width(0), height(0),
+ shouldClose(false), wmDeleteMessage(0),
+ currentColor(0xFFFFFF) {}
+};
+
+// Helper to convert Color to X11 pixel value
+static unsigned long colorToPixel(Display* display, const Color& color) {
+ int screen = DefaultScreen(display);
+ Colormap colormap = DefaultColormap(display, screen);
+ XColor xcolor;
+
+ xcolor.red = color.r * 257; // Convert 0-255 to 0-65535
+ xcolor.green = color.g * 257;
+ xcolor.blue = color.b * 257;
+ xcolor.flags = DoRed | DoGreen | DoBlue;
+
+ XAllocColor(display, colormap, &xcolor);
+ return xcolor.pixel;
+}
+
+WindowHandle* createWindow(const char* title, int width, int height) {
+ WindowHandle* handle = new WindowHandle();
+ handle->width = width;
+ handle->height = height;
+
+ // Open connection to X server
+ handle->display = XOpenDisplay(nullptr);
+ if (!handle->display) {
+ delete handle;
+ return nullptr;
+ }
+
+ int screen = DefaultScreen(handle->display);
+ Window root = RootWindow(handle->display, screen);
+
+ // Create window
+ handle->window = XCreateSimpleWindow(
+ handle->display,
+ root,
+ 0, 0,
+ width, height,
+ 1,
+ BlackPixel(handle->display, screen),
+ BlackPixel(handle->display, screen)
+ );
+
+ if (!handle->window) {
+ XCloseDisplay(handle->display);
+ delete handle;
+ return nullptr;
+ }
+
+ // Set window title
+ XStoreName(handle->display, handle->window, title);
+
+ // Select input events
+ XSelectInput(handle->display, handle->window,
+ ExposureMask | KeyPressMask | StructureNotifyMask);
+
+ // Handle window close event
+ handle->wmDeleteMessage = XInternAtom(handle->display, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(handle->display, handle->window, &handle->wmDeleteMessage, 1);
+
+ // Create graphics context
+ handle->gc = XCreateGC(handle->display, handle->window, 0, nullptr);
+
+ // Create back buffer for double buffering
+ handle->backBuffer = XCreatePixmap(handle->display, handle->window,
+ width, height,
+ DefaultDepth(handle->display, screen));
+
+ // Map window to screen
+ XMapWindow(handle->display, handle->window);
+
+ // Wait for window to be mapped
+ XEvent event;
+ do {
+ XNextEvent(handle->display, &event);
+ } while (event.type != MapNotify);
+
+ return handle;
+}
+
+void destroyWindow(WindowHandle* window) {
+ if (!window) return;
+
+ if (window->display) {
+ if (window->backBuffer) {
+ XFreePixmap(window->display, window->backBuffer);
+ }
+
+ if (window->gc) {
+ XFreeGC(window->display, window->gc);
+ }
+
+ if (window->window) {
+ XDestroyWindow(window->display, window->window);
+ }
+
+ XCloseDisplay(window->display);
+ }
+
+ delete window;
+}
+
+bool windowShouldClose(WindowHandle* window) {
+ if (!window) return true;
+ return window->shouldClose;
+}
+
+void pollEvents(WindowHandle* window) {
+ if (!window || !window->display) return;
+
+ XEvent event;
+ while (XPending(window->display) > 0) {
+ XNextEvent(window->display, &event);
+
+ switch (event.type) {
+ case ClientMessage:
+ if ((Atom)event.xclient.data.l[0] == window->wmDeleteMessage) {
+ window->shouldClose = true;
+ }
+ break;
+
+ case KeyPress: {
+ KeySym key = XLookupKeysym(&event.xkey, 0);
+ if (key == XK_Escape) {
+ window->shouldClose = true;
+ }
+ break;
+ }
+ }
+ }
+}
+
+void swapBuffers(WindowHandle* window) {
+ if (!window || !window->display || !window->gc) return;
+
+ // Copy back buffer to window
+ XCopyArea(window->display, window->backBuffer, window->window, window->gc,
+ 0, 0, window->width, window->height, 0, 0);
+
+ // Flush the output buffer
+ XFlush(window->display);
+}
+
+void clearScreen(WindowHandle* window, const Color& color) {
+ if (!window || !window->display || !window->gc) return;
+
+ unsigned long pixel = colorToPixel(window->display, color);
+ XSetForeground(window->display, window->gc, pixel);
+ XFillRectangle(window->display, window->backBuffer, window->gc,
+ 0, 0, window->width, window->height);
+}
+
+void setDrawColor(WindowHandle* window, const Color& color) {
+ if (!window || !window->display || !window->gc) return;
+
+ window->currentColor = colorToPixel(window->display, color);
+ XSetForeground(window->display, window->gc, window->currentColor);
+}
+
+void drawLine(WindowHandle* window, int x1, int y1, int x2, int y2, const Color& color) {
+ if (!window || !window->display || !window->gc) return;
+
+ setDrawColor(window, color);
+ XDrawLine(window->display, window->backBuffer, window->gc, x1, y1, x2, y2);
+}
+
+void drawRectangle(WindowHandle* window, int x, int y, int width, int height, const Color& color) {
+ if (!window || !window->display || !window->gc) return;
+
+ setDrawColor(window, color);
+ XDrawRectangle(window->display, window->backBuffer, window->gc, x, y, width, height);
+}
+
+void drawFilledRectangle(WindowHandle* window, int x, int y, int width, int height, const Color& color) {
+ if (!window || !window->display || !window->gc) return;
+
+ setDrawColor(window, color);
+ XFillRectangle(window->display, window->backBuffer, window->gc, x, y, width, height);
+}
+
+void drawPixel(WindowHandle* window, int x, int y, const Color& color) {
+ if (!window || !window->display || !window->gc) return;
+
+ setDrawColor(window, color);
+ XDrawPoint(window->display, window->backBuffer, window->gc, x, y);
+}
+
+// Helper function for drawing circles using midpoint circle algorithm
+static void drawCirclePoints(Display* display, Drawable drawable, GC gc,
+ int xc, int yc, int x, int y) {
+ XDrawPoint(display, drawable, gc, xc + x, yc + y);
+ XDrawPoint(display, drawable, gc, xc - x, yc + y);
+ XDrawPoint(display, drawable, gc, xc + x, yc - y);
+ XDrawPoint(display, drawable, gc, xc - x, yc - y);
+ XDrawPoint(display, drawable, gc, xc + y, yc + x);
+ XDrawPoint(display, drawable, gc, xc - y, yc + x);
+ XDrawPoint(display, drawable, gc, xc + y, yc - x);
+ XDrawPoint(display, drawable, gc, xc - y, yc - x);
+}
+
+void drawCircle(WindowHandle* window, int centerX, int centerY, int radius, const Color& color) {
+ if (!window || !window->display || !window->gc) return;
+
+ setDrawColor(window, color);
+
+ int x = 0;
+ int y = radius;
+ int d = 3 - 2 * radius;
+
+ drawCirclePoints(window->display, window->backBuffer, window->gc,
+ centerX, centerY, x, y);
+
+ while (y >= x) {
+ x++;
+
+ if (d > 0) {
+ y--;
+ d = d + 4 * (x - y) + 10;
+ } else {
+ d = d + 4 * x + 6;
+ }
+
+ drawCirclePoints(window->display, window->backBuffer, window->gc,
+ centerX, centerY, x, y);
+ }
+}
+
+void drawFilledCircle(WindowHandle* window, int centerX, int centerY, int radius, const Color& color) {
+ if (!window || !window->display || !window->gc) return;
+
+ setDrawColor(window, color);
+
+ // X11 has XFillArc which is more efficient
+ XFillArc(window->display, window->backBuffer, window->gc,
+ centerX - radius, centerY - radius,
+ radius * 2, radius * 2,
+ 0, 360 * 64); // Angles in X11 are in 1/64ths of a degree
+}
+
+void delay(uint32_t milliseconds) {
+ usleep(milliseconds * 1000);
+}
+
+#endif // USE_X11