diff options
| -rw-r--r-- | .clangd | 3 | ||||
| -rw-r--r-- | examples/sample4.cpp | 241 | ||||
| -rw-r--r-- | lib/graphics.cpp | 789 | ||||
| -rw-r--r-- | lib/graphics.h | 72 |
4 files changed, 1093 insertions, 12 deletions
diff --git a/.clangd b/.clangd deleted file mode 100644 index 3a8dc0a..0000000 --- a/.clangd +++ /dev/null @@ -1,3 +0,0 @@ -CompileFlags: - Add: - - "-Ilib/"
\ No newline at end of file diff --git a/examples/sample4.cpp b/examples/sample4.cpp new file mode 100644 index 0000000..df9203e --- /dev/null +++ b/examples/sample4.cpp @@ -0,0 +1,241 @@ +#include "graphics.h" +#include <cmath> +#include <vector> +#include <cstdlib> +#include <ctime> + +struct Vec3 { + float x, y, z; +}; + +Vec3 operator+(const Vec3& a, const Vec3& b) { return {a.x+b.x,a.y+b.y,a.z+b.z}; } +Vec3 operator-(const Vec3& a, const Vec3& b) { return {a.x-b.x,a.y-b.y,a.z-b.z}; } +Vec3 operator*(const Vec3& a, float s) { return {a.x*s,a.y*s,a.z*s}; } + +float length(const Vec3& v) { + return std::sqrt(v.x*v.x + v.y*v.y + v.z*v.z); +} + +Vec3 normalize(const Vec3& v) { + float l = length(v); + if (l == 0) return {0,0,0}; + return {v.x/l, v.y/l, v.z/l}; +} + +Vec3 rotateX(const Vec3& v, float a) { + float c = std::cos(a), s = std::sin(a); + return { v.x, v.y*c - v.z*s, v.y*s + v.z*c }; +} + +Vec3 rotateY(const Vec3& v, float a) { + float c = std::cos(a), s = std::sin(a); + return { v.x*c + v.z*s, v.y, -v.x*s + v.z*c }; +} + +// Camera +struct Camera { + Vec3 position; + float yaw; + float pitch; +}; + +void project(const Vec3& v, int& x2d, int& y2d, + int width, int height, float fov) +{ + if (v.z <= 0.1f) return; + + float factor = fov / v.z; + x2d = static_cast<int>(v.x * factor + width/2); + y2d = static_cast<int>(v.y * factor + height/2); +} + +struct Star { + Vec3 pos; +}; + +struct Cube { + Vec3 pos; + float size; +}; + +struct Planet { + Vec3 pos; + float radius; +}; + +int main() { + std::srand(static_cast<unsigned>(std::time(nullptr))); + + const int width = 1280; + const int height = 720; + + WindowHandle* window = createWindow("SAMPLE4 - SPACE NAVIGATION", width, height); + if (!window) return -1; + + setMouseLocked(window, true); + + Camera cam; + cam.position = {0,0,-5}; + cam.yaw = 0; + cam.pitch = 0; + + const float moveSpeed = 0.3f; + const float mouseSensitivity = 0.002f; + + // ----- STARFIELD ----- + const int starCount = 1500; + std::vector<Star> stars; + for (int i = 0; i < starCount; ++i) { + stars.push_back({ + { + (rand()%2000 - 1000) / 10.0f, + (rand()%2000 - 1000) / 10.0f, + (rand()%2000 - 1000) / 10.0f + } + }); + } + + // ----- CUBES ----- + std::vector<Vec3> cubeVertices = { + {-1,-1,-1},{1,-1,-1},{1,1,-1},{-1,1,-1}, + {-1,-1,1},{1,-1,1},{1,1,1},{-1,1,1} + }; + + std::vector<std::pair<int,int>> cubeEdges = { + {0,1},{1,2},{2,3},{3,0}, + {4,5},{5,6},{6,7},{7,4}, + {0,4},{1,5},{2,6},{3,7} + }; + + std::vector<Cube> cubes; + for (int i = 0; i < 40; ++i) { + cubes.push_back({ + { + (rand()%2000 - 1000) / 10.0f, + (rand()%2000 - 1000) / 10.0f, + (rand()%2000 - 1000) / 10.0f + }, + (rand()%50)/10.0f + 1.0f + }); + } + + // ----- PLANETS ----- + std::vector<Planet> planets; + for (int i = 0; i < 5; ++i) { + planets.push_back({ + { + (rand()%3000 - 1500) / 10.0f, + (rand()%3000 - 1500) / 10.0f, + (rand()%3000 - 1500) / 10.0f + }, + (rand()%200)/10.0f + 5.0f + }); + } + + while (!windowShouldClose(window)) { + + pollEvents(window); + + // ----- MOUSE LOOK ----- + int dx, dy; + getMouseDelta(window, dx, dy); + + cam.yaw += dx * mouseSensitivity; + cam.pitch += dy * mouseSensitivity; + + if (cam.pitch > 1.5f) cam.pitch = 1.5f; + if (cam.pitch < -1.5f) cam.pitch = -1.5f; + + // Forward vector + Vec3 forward = { + std::sin(cam.yaw), + 0, + std::cos(cam.yaw) + }; + + Vec3 right = { + std::cos(cam.yaw), + 0, + -std::sin(cam.yaw) + }; + + // ----- MOVEMENT ----- + if (keyDown(window, KEY_UP)) + cam.position = cam.position + forward * moveSpeed; + if (keyDown(window, KEY_DOWN)) + cam.position = cam.position - forward * moveSpeed; + if (keyDown(window, KEY_LEFT)) + cam.position = cam.position - right * moveSpeed; + if (keyDown(window, KEY_RIGHT)) + cam.position = cam.position + right * moveSpeed; + + clearScreen(window, Color(0,0,10)); + + float fov = 500.0f; + + // ----- DRAW STARS ----- + for (auto& star : stars) { + + Vec3 p = star.pos - cam.position; + p = rotateY(p, -cam.yaw); + p = rotateX(p, -cam.pitch); + + if (p.z > 0) { + int sx, sy; + project(p, sx, sy, width, height, fov); + if (sx>=0 && sx<width && sy>=0 && sy<height) + drawPixel(window, sx, sy, Color(255,255,255)); + } + } + + // ----- DRAW CUBES ----- + for (auto& cube : cubes) { + + std::vector<Vec3> transformed; + + for (auto v : cubeVertices) { + v = v * cube.size; + v = v + cube.pos; + v = v - cam.position; + v = rotateY(v, -cam.yaw); + v = rotateX(v, -cam.pitch); + transformed.push_back(v); + } + + for (auto& e : cubeEdges) { + if (transformed[e.first].z > 0 && + transformed[e.second].z > 0) { + + int x1,y1,x2,y2; + project(transformed[e.first], x1,y1,width,height,fov); + project(transformed[e.second],x2,y2,width,height,fov); + drawLine(window,x1,y1,x2,y2,Color(0,255,200)); + } + } + } + + // ----- DRAW PLANETS (wireframe sphere imitation) ----- + for (auto& planet : planets) { + + Vec3 p = planet.pos - cam.position; + p = rotateY(p, -cam.yaw); + p = rotateX(p, -cam.pitch); + + if (p.z <= 0) continue; + + int cx, cy; + project(p, cx, cy, width, height, fov); + + float scale = fov / p.z; + int radius2D = static_cast<int>(planet.radius * scale); + + drawCircle(window, cx, cy, radius2D, Color(100,100,255)); + } + + swapBuffers(window); + delay(16); + } + + destroyWindow(window); + return 0; +} diff --git a/lib/graphics.cpp b/lib/graphics.cpp index c41ac0e..cdf0b53 100644 --- a/lib/graphics.cpp +++ b/lib/graphics.cpp @@ -4,6 +4,7 @@ #ifdef USE_SDL #include <SDL2/SDL.h> +#include <cstring> // Platform-specific window handle structure struct WindowHandle { @@ -11,7 +12,30 @@ struct WindowHandle { SDL_Renderer* renderer; bool shouldClose; - WindowHandle() : window(nullptr), renderer(nullptr), shouldClose(false) {} + // Input state + bool keyState[KEY_COUNT]; + bool prevKeyState[KEY_COUNT]; + + bool mouseState[MOUSE_BUTTON_COUNT]; + bool prevMouseState[MOUSE_BUTTON_COUNT]; + + int mouseX, mouseY; + int prevMouseX, prevMouseY; + int mouseDeltaX, mouseDeltaY; + + int mouseWheelDelta; + + bool mouseLocked; + + WindowHandle() : window(nullptr), renderer(nullptr), shouldClose(false), + mouseX(0), mouseY(0), prevMouseX(0), prevMouseY(0), + mouseDeltaX(0), mouseDeltaY(0), mouseWheelDelta(0), + mouseLocked(false) { + memset(keyState, 0, sizeof(keyState)); + memset(prevKeyState, 0, sizeof(prevKeyState)); + memset(mouseState, 0, sizeof(mouseState)); + memset(prevMouseState, 0, sizeof(prevMouseState)); + } }; // Initialize SDL (called once) @@ -27,6 +51,85 @@ static void ensureSDLInit() { } } +// SDL key mapping +static KeyCode mapSDLKey(SDL_Keycode sdlKey) { + switch (sdlKey) { + case SDLK_UP: return KEY_UP; + case SDLK_DOWN: return KEY_DOWN; + case SDLK_LEFT: return KEY_LEFT; + case SDLK_RIGHT: return KEY_RIGHT; + + case SDLK_a: return KEY_A; + case SDLK_b: return KEY_B; + case SDLK_c: return KEY_C; + case SDLK_d: return KEY_D; + case SDLK_e: return KEY_E; + case SDLK_f: return KEY_F; + case SDLK_g: return KEY_G; + case SDLK_h: return KEY_H; + case SDLK_i: return KEY_I; + case SDLK_j: return KEY_J; + case SDLK_k: return KEY_K; + case SDLK_l: return KEY_L; + case SDLK_m: return KEY_M; + case SDLK_n: return KEY_N; + case SDLK_o: return KEY_O; + case SDLK_p: return KEY_P; + case SDLK_q: return KEY_Q; + case SDLK_r: return KEY_R; + case SDLK_s: return KEY_S; + case SDLK_t: return KEY_T; + case SDLK_u: return KEY_U; + case SDLK_v: return KEY_V; + case SDLK_w: return KEY_W; + case SDLK_x: return KEY_X; + case SDLK_y: return KEY_Y; + case SDLK_z: return KEY_Z; + + case SDLK_0: return KEY_0; + case SDLK_1: return KEY_1; + case SDLK_2: return KEY_2; + case SDLK_3: return KEY_3; + case SDLK_4: return KEY_4; + case SDLK_5: return KEY_5; + case SDLK_6: return KEY_6; + case SDLK_7: return KEY_7; + case SDLK_8: return KEY_8; + case SDLK_9: return KEY_9; + + case SDLK_F1: return KEY_F1; + case SDLK_F2: return KEY_F2; + case SDLK_F3: return KEY_F3; + case SDLK_F4: return KEY_F4; + case SDLK_F5: return KEY_F5; + case SDLK_F6: return KEY_F6; + case SDLK_F7: return KEY_F7; + case SDLK_F8: return KEY_F8; + case SDLK_F9: return KEY_F9; + case SDLK_F10: return KEY_F10; + case SDLK_F11: return KEY_F11; + case SDLK_F12: return KEY_F12; + + case SDLK_SPACE: return KEY_SPACE; + case SDLK_RETURN: return KEY_ENTER; + case SDLK_ESCAPE: return KEY_ESCAPE; + case SDLK_BACKSPACE: return KEY_BACKSPACE; + case SDLK_TAB: return KEY_TAB; + case SDLK_LSHIFT: + case SDLK_RSHIFT: return KEY_SHIFT; + case SDLK_LCTRL: + case SDLK_RCTRL: return KEY_CONTROL; + case SDLK_LALT: + case SDLK_RALT: return KEY_ALT; + + case SDLK_PLUS: + case SDLK_EQUALS: return KEY_PLUS; + case SDLK_MINUS: return KEY_MINUS; + + default: return KEY_UNKNOWN; + } +} + WindowHandle* createWindow(const char* title, int width, int height) { ensureSDLInit(); @@ -92,16 +195,73 @@ bool windowShouldClose(WindowHandle* window) { void pollEvents(WindowHandle* window) { if (!window) return; + // Save previous state + memcpy(window->prevKeyState, window->keyState, sizeof(window->keyState)); + memcpy(window->prevMouseState, window->mouseState, sizeof(window->mouseState)); + + window->prevMouseX = window->mouseX; + window->prevMouseY = window->mouseY; + window->mouseWheelDelta = 0; + SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { window->shouldClose = true; } else if (event.type == SDL_KEYDOWN) { + KeyCode key = mapSDLKey(event.key.keysym.sym); + if (key != KEY_UNKNOWN && key < KEY_COUNT) { + window->keyState[key] = true; + } if (event.key.keysym.sym == SDLK_ESCAPE) { window->shouldClose = true; } } + else if (event.type == SDL_KEYUP) { + KeyCode key = mapSDLKey(event.key.keysym.sym); + if (key != KEY_UNKNOWN && key < KEY_COUNT) { + window->keyState[key] = false; + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) { + if (event.button.button == SDL_BUTTON_LEFT) + window->mouseState[MOUSE_LEFT] = true; + else if (event.button.button == SDL_BUTTON_RIGHT) + window->mouseState[MOUSE_RIGHT] = true; + else if (event.button.button == SDL_BUTTON_MIDDLE) + window->mouseState[MOUSE_MIDDLE] = true; + } + else if (event.type == SDL_MOUSEBUTTONUP) { + if (event.button.button == SDL_BUTTON_LEFT) + window->mouseState[MOUSE_LEFT] = false; + else if (event.button.button == SDL_BUTTON_RIGHT) + window->mouseState[MOUSE_RIGHT] = false; + else if (event.button.button == SDL_BUTTON_MIDDLE) + window->mouseState[MOUSE_MIDDLE] = false; + } + else if (event.type == SDL_MOUSEMOTION) { + window->mouseX = event.motion.x; + window->mouseY = event.motion.y; + } + else if (event.type == SDL_MOUSEWHEEL) { + window->mouseWheelDelta = event.wheel.y; + } + } + + // Calculate mouse delta + window->mouseDeltaX = window->mouseX - window->prevMouseX; + window->mouseDeltaY = window->mouseY - window->prevMouseY; + + // Handle mouse locking + if (window->mouseLocked) { + int centerX, centerY; + SDL_GetWindowSize(window->window, ¢erX, ¢erY); + centerX /= 2; + centerY /= 2; + + SDL_WarpMouseInWindow(window->window, centerX, centerY); + window->mouseX = centerX; + window->mouseY = centerY; } } @@ -207,12 +367,95 @@ void delay(uint32_t milliseconds) { SDL_Delay(milliseconds); } +// ============================================================================ +// INPUT HANDLING - SDL +// ============================================================================ + +bool keyDown(WindowHandle* window, KeyCode key) { + if (!window || key >= KEY_COUNT) return false; + return window->keyState[key]; +} + +bool keyPressed(WindowHandle* window, KeyCode key) { + if (!window || key >= KEY_COUNT) return false; + return window->keyState[key] && !window->prevKeyState[key]; +} + +bool keyReleased(WindowHandle* window, KeyCode key) { + if (!window || key >= KEY_COUNT) return false; + return !window->keyState[key] && window->prevKeyState[key]; +} + +bool mouseDown(WindowHandle* window, MouseButton button) { + if (!window || button >= MOUSE_BUTTON_COUNT) return false; + return window->mouseState[button]; +} + +bool mousePressed(WindowHandle* window, MouseButton button) { + if (!window || button >= MOUSE_BUTTON_COUNT) return false; + return window->mouseState[button] && !window->prevMouseState[button]; +} + +bool mouseReleased(WindowHandle* window, MouseButton button) { + if (!window || button >= MOUSE_BUTTON_COUNT) return false; + return !window->mouseState[button] && window->prevMouseState[button]; +} + +void getMousePosition(WindowHandle* window, int& x, int& y) { + if (!window) { + x = y = 0; + return; + } + x = window->mouseX; + y = window->mouseY; +} + +void getMouseDelta(WindowHandle* window, int& dx, int& dy) { + if (!window) { + dx = dy = 0; + return; + } + dx = window->mouseDeltaX; + dy = window->mouseDeltaY; +} + +void setMousePosition(WindowHandle* window, int x, int y) { + if (!window || !window->window) return; + SDL_WarpMouseInWindow(window->window, x, y); + window->mouseX = x; + window->mouseY = y; +} + +void setMouseLocked(WindowHandle* window, bool locked) { + if (!window || !window->window) return; + window->mouseLocked = locked; + + if (locked) { + SDL_SetRelativeMouseMode(SDL_TRUE); + SDL_ShowCursor(SDL_DISABLE); + } else { + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_ShowCursor(SDL_ENABLE); + } +} + +bool isMouseLocked(WindowHandle* window) { + if (!window) return false; + return window->mouseLocked; +} + +int getMouseWheelDelta(WindowHandle* window) { + if (!window) return 0; + return window->mouseWheelDelta; +} + #endif // USE_SDL #ifdef USE_WIN32 #include <windows.h> #include <vector> +#include <cstring> // Platform-specific window handle structure for Win32 struct WindowHandle { @@ -226,16 +469,114 @@ struct WindowHandle { bool shouldClose; COLORREF currentColor; + // Input state + bool keyState[KEY_COUNT]; + bool prevKeyState[KEY_COUNT]; + + bool mouseState[MOUSE_BUTTON_COUNT]; + bool prevMouseState[MOUSE_BUTTON_COUNT]; + + int mouseX, mouseY; + int prevMouseX, prevMouseY; + int mouseDeltaX, mouseDeltaY; + + int mouseWheelDelta; + + bool mouseLocked; + WindowHandle() : hwnd(nullptr), hdc(nullptr), memDC(nullptr), memBitmap(nullptr), oldBitmap(nullptr), width(0), height(0), shouldClose(false), - currentColor(RGB(255, 255, 255)) {} + currentColor(RGB(255, 255, 255)), + mouseX(0), mouseY(0), prevMouseX(0), prevMouseY(0), + mouseDeltaX(0), mouseDeltaY(0), mouseWheelDelta(0), + mouseLocked(false) { + memset(keyState, 0, sizeof(keyState)); + memset(prevKeyState, 0, sizeof(prevKeyState)); + memset(mouseState, 0, sizeof(mouseState)); + memset(prevMouseState, 0, sizeof(prevMouseState)); + } }; // Global window class name static const char* WINDOW_CLASS_NAME = "LibGraffikWindowClass"; static bool classRegistered = false; +// Win32 key mapping +static KeyCode mapWin32Key(WPARAM wParam) { + switch (wParam) { + case VK_UP: return KEY_UP; + case VK_DOWN: return KEY_DOWN; + case VK_LEFT: return KEY_LEFT; + case VK_RIGHT: return KEY_RIGHT; + + case 'A': return KEY_A; + case 'B': return KEY_B; + case 'C': return KEY_C; + case 'D': return KEY_D; + case 'E': return KEY_E; + case 'F': return KEY_F; + case 'G': return KEY_G; + case 'H': return KEY_H; + case 'I': return KEY_I; + case 'J': return KEY_J; + case 'K': return KEY_K; + case 'L': return KEY_L; + case 'M': return KEY_M; + case 'N': return KEY_N; + case 'O': return KEY_O; + case 'P': return KEY_P; + case 'Q': return KEY_Q; + case 'R': return KEY_R; + case 'S': return KEY_S; + case 'T': return KEY_T; + case 'U': return KEY_U; + case 'V': return KEY_V; + case 'W': return KEY_W; + case 'X': return KEY_X; + case 'Y': return KEY_Y; + case 'Z': return KEY_Z; + + case '0': return KEY_0; + case '1': return KEY_1; + case '2': return KEY_2; + case '3': return KEY_3; + case '4': return KEY_4; + case '5': return KEY_5; + case '6': return KEY_6; + case '7': return KEY_7; + case '8': return KEY_8; + case '9': return KEY_9; + + case VK_F1: return KEY_F1; + case VK_F2: return KEY_F2; + case VK_F3: return KEY_F3; + case VK_F4: return KEY_F4; + case VK_F5: return KEY_F5; + case VK_F6: return KEY_F6; + case VK_F7: return KEY_F7; + case VK_F8: return KEY_F8; + case VK_F9: return KEY_F9; + case VK_F10: return KEY_F10; + case VK_F11: return KEY_F11; + case VK_F12: return KEY_F12; + + case VK_SPACE: return KEY_SPACE; + case VK_RETURN: return KEY_ENTER; + case VK_ESCAPE: return KEY_ESCAPE; + case VK_BACK: return KEY_BACKSPACE; + case VK_TAB: return KEY_TAB; + case VK_SHIFT: return KEY_SHIFT; + case VK_CONTROL: return KEY_CONTROL; + case VK_MENU: return KEY_ALT; + + case VK_OEM_PLUS: return KEY_PLUS; + case VK_OEM_MINUS: return KEY_MINUS; + + default: return KEY_UNKNOWN; + } +} + // Window procedure LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WindowHandle* handle = (WindowHandle*)GetWindowLongPtr(hwnd, GWLP_USERDATA); @@ -248,8 +589,64 @@ LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return 0; case WM_KEYDOWN: - if (wParam == VK_ESCAPE && handle) { - handle->shouldClose = true; + case WM_SYSKEYDOWN: { + if (handle) { + KeyCode key = mapWin32Key(wParam); + if (key != KEY_UNKNOWN && key < KEY_COUNT) { + handle->keyState[key] = true; + } + if (wParam == VK_ESCAPE) { + handle->shouldClose = true; + } + } + return 0; + } + + case WM_KEYUP: + case WM_SYSKEYUP: { + if (handle) { + KeyCode key = mapWin32Key(wParam); + if (key != KEY_UNKNOWN && key < KEY_COUNT) { + handle->keyState[key] = false; + } + } + return 0; + } + + case WM_LBUTTONDOWN: + if (handle) handle->mouseState[MOUSE_LEFT] = true; + return 0; + + case WM_LBUTTONUP: + if (handle) handle->mouseState[MOUSE_LEFT] = false; + return 0; + + case WM_RBUTTONDOWN: + if (handle) handle->mouseState[MOUSE_RIGHT] = true; + return 0; + + case WM_RBUTTONUP: + if (handle) handle->mouseState[MOUSE_RIGHT] = false; + return 0; + + case WM_MBUTTONDOWN: + if (handle) handle->mouseState[MOUSE_MIDDLE] = true; + return 0; + + case WM_MBUTTONUP: + if (handle) handle->mouseState[MOUSE_MIDDLE] = false; + return 0; + + case WM_MOUSEMOVE: + if (handle) { + handle->mouseX = LOWORD(lParam); + handle->mouseY = HIWORD(lParam); + } + return 0; + + case WM_MOUSEWHEEL: + if (handle) { + handle->mouseWheelDelta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA; } return 0; @@ -359,11 +756,38 @@ bool windowShouldClose(WindowHandle* window) { void pollEvents(WindowHandle* window) { if (!window) return; + // Save previous state + memcpy(window->prevKeyState, window->keyState, sizeof(window->keyState)); + memcpy(window->prevMouseState, window->mouseState, sizeof(window->mouseState)); + + window->prevMouseX = window->mouseX; + window->prevMouseY = window->mouseY; + window->mouseWheelDelta = 0; + MSG msg; while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } + + // Calculate mouse delta + window->mouseDeltaX = window->mouseX - window->prevMouseX; + window->mouseDeltaY = window->mouseY - window->prevMouseY; + + // Handle mouse locking + if (window->mouseLocked && window->hwnd) { + RECT rect; + GetClientRect(window->hwnd, &rect); + int centerX = rect.right / 2; + int centerY = rect.bottom / 2; + + POINT pt = {centerX, centerY}; + ClientToScreen(window->hwnd, &pt); + SetCursorPos(pt.x, pt.y); + + window->mouseX = centerX; + window->mouseY = centerY; + } } void swapBuffers(WindowHandle* window) { @@ -484,6 +908,90 @@ void delay(uint32_t milliseconds) { Sleep(milliseconds); } +// ============================================================================ +// INPUT HANDLING - WIN32 +// ============================================================================ + +bool keyDown(WindowHandle* window, KeyCode key) { + if (!window || key >= KEY_COUNT) return false; + return window->keyState[key]; +} + +bool keyPressed(WindowHandle* window, KeyCode key) { + if (!window || key >= KEY_COUNT) return false; + return window->keyState[key] && !window->prevKeyState[key]; +} + +bool keyReleased(WindowHandle* window, KeyCode key) { + if (!window || key >= KEY_COUNT) return false; + return !window->keyState[key] && window->prevKeyState[key]; +} + +bool mouseDown(WindowHandle* window, MouseButton button) { + if (!window || button >= MOUSE_BUTTON_COUNT) return false; + return window->mouseState[button]; +} + +bool mousePressed(WindowHandle* window, MouseButton button) { + if (!window || button >= MOUSE_BUTTON_COUNT) return false; + return window->mouseState[button] && !window->prevMouseState[button]; +} + +bool mouseReleased(WindowHandle* window, MouseButton button) { + if (!window || button >= MOUSE_BUTTON_COUNT) return false; + return !window->mouseState[button] && window->prevMouseState[button]; +} + +void getMousePosition(WindowHandle* window, int& x, int& y) { + if (!window) { + x = y = 0; + return; + } + x = window->mouseX; + y = window->mouseY; +} + +void getMouseDelta(WindowHandle* window, int& dx, int& dy) { + if (!window) { + dx = dy = 0; + return; + } + dx = window->mouseDeltaX; + dy = window->mouseDeltaY; +} + +void setMousePosition(WindowHandle* window, int x, int y) { + if (!window || !window->hwnd) return; + + POINT pt = {x, y}; + ClientToScreen(window->hwnd, &pt); + SetCursorPos(pt.x, pt.y); + + window->mouseX = x; + window->mouseY = y; +} + +void setMouseLocked(WindowHandle* window, bool locked) { + if (!window) return; + window->mouseLocked = locked; + + if (locked) { + ShowCursor(FALSE); + } else { + ShowCursor(TRUE); + } +} + +bool isMouseLocked(WindowHandle* window) { + if (!window) return false; + return window->mouseLocked; +} + +int getMouseWheelDelta(WindowHandle* window) { + if (!window) return 0; + return window->mouseWheelDelta; +} + #endif // USE_WIN32 #ifdef USE_X11 @@ -505,10 +1013,33 @@ struct WindowHandle { Atom wmDeleteMessage; unsigned long currentColor; + // Input state + bool keyState[KEY_COUNT]; + bool prevKeyState[KEY_COUNT]; + + bool mouseState[MOUSE_BUTTON_COUNT]; + bool prevMouseState[MOUSE_BUTTON_COUNT]; + + int mouseX, mouseY; + int prevMouseX, prevMouseY; + int mouseDeltaX, mouseDeltaY; + + int mouseWheelDelta; + + bool mouseLocked; + WindowHandle() : display(nullptr), window(0), gc(nullptr), backBuffer(0), width(0), height(0), shouldClose(false), wmDeleteMessage(0), - currentColor(0xFFFFFF) {} + currentColor(0xFFFFFF), + mouseX(0), mouseY(0), prevMouseX(0), prevMouseY(0), + mouseDeltaX(0), mouseDeltaY(0), mouseWheelDelta(0), + mouseLocked(false) { + memset(keyState, 0, sizeof(keyState)); + memset(prevKeyState, 0, sizeof(prevKeyState)); + memset(mouseState, 0, sizeof(mouseState)); + memset(prevMouseState, 0, sizeof(prevMouseState)); + } }; // Helper to convert Color to X11 pixel value @@ -526,6 +1057,85 @@ static unsigned long colorToPixel(Display* display, const Color& color) { return xcolor.pixel; } +// X11 key mapping +static KeyCode mapX11Key(KeySym keysym) { + switch (keysym) { + case XK_Up: return KEY_UP; + case XK_Down: return KEY_DOWN; + case XK_Left: return KEY_LEFT; + case XK_Right: return KEY_RIGHT; + + case XK_a: case XK_A: return KEY_A; + case XK_b: case XK_B: return KEY_B; + case XK_c: case XK_C: return KEY_C; + case XK_d: case XK_D: return KEY_D; + case XK_e: case XK_E: return KEY_E; + case XK_f: case XK_F: return KEY_F; + case XK_g: case XK_G: return KEY_G; + case XK_h: case XK_H: return KEY_H; + case XK_i: case XK_I: return KEY_I; + case XK_j: case XK_J: return KEY_J; + case XK_k: case XK_K: return KEY_K; + case XK_l: case XK_L: return KEY_L; + case XK_m: case XK_M: return KEY_M; + case XK_n: case XK_N: return KEY_N; + case XK_o: case XK_O: return KEY_O; + case XK_p: case XK_P: return KEY_P; + case XK_q: case XK_Q: return KEY_Q; + case XK_r: case XK_R: return KEY_R; + case XK_s: case XK_S: return KEY_S; + case XK_t: case XK_T: return KEY_T; + case XK_u: case XK_U: return KEY_U; + case XK_v: case XK_V: return KEY_V; + case XK_w: case XK_W: return KEY_W; + case XK_x: case XK_X: return KEY_X; + case XK_y: case XK_Y: return KEY_Y; + case XK_z: case XK_Z: return KEY_Z; + + case XK_0: return KEY_0; + case XK_1: return KEY_1; + case XK_2: return KEY_2; + case XK_3: return KEY_3; + case XK_4: return KEY_4; + case XK_5: return KEY_5; + case XK_6: return KEY_6; + case XK_7: return KEY_7; + case XK_8: return KEY_8; + case XK_9: return KEY_9; + + case XK_F1: return KEY_F1; + case XK_F2: return KEY_F2; + case XK_F3: return KEY_F3; + case XK_F4: return KEY_F4; + case XK_F5: return KEY_F5; + case XK_F6: return KEY_F6; + case XK_F7: return KEY_F7; + case XK_F8: return KEY_F8; + case XK_F9: return KEY_F9; + case XK_F10: return KEY_F10; + case XK_F11: return KEY_F11; + case XK_F12: return KEY_F12; + + case XK_space: return KEY_SPACE; + case XK_Return: return KEY_ENTER; + case XK_Escape: return KEY_ESCAPE; + case XK_BackSpace: return KEY_BACKSPACE; + case XK_Tab: return KEY_TAB; + case XK_Shift_L: + case XK_Shift_R: return KEY_SHIFT; + case XK_Control_L: + case XK_Control_R: return KEY_CONTROL; + case XK_Alt_L: + case XK_Alt_R: return KEY_ALT; + + case XK_plus: + case XK_equal: return KEY_PLUS; + case XK_minus: return KEY_MINUS; + + default: return KEY_UNKNOWN; + } +} + WindowHandle* createWindow(const char* title, int width, int height) { WindowHandle* handle = new WindowHandle(); handle->width = width; @@ -563,7 +1173,9 @@ WindowHandle* createWindow(const char* title, int width, int height) { // Select input events XSelectInput(handle->display, handle->window, - ExposureMask | KeyPressMask | StructureNotifyMask); + ExposureMask | KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | StructureNotifyMask); // Handle window close event handle->wmDeleteMessage = XInternAtom(handle->display, "WM_DELETE_WINDOW", False); @@ -619,6 +1231,14 @@ bool windowShouldClose(WindowHandle* window) { void pollEvents(WindowHandle* window) { if (!window || !window->display) return; + // Save previous state + memcpy(window->prevKeyState, window->keyState, sizeof(window->keyState)); + memcpy(window->prevMouseState, window->mouseState, sizeof(window->mouseState)); + + window->prevMouseX = window->mouseX; + window->prevMouseY = window->mouseY; + window->mouseWheelDelta = 0; + XEvent event; while (XPending(window->display) > 0) { XNextEvent(window->display, &event); @@ -631,14 +1251,70 @@ void pollEvents(WindowHandle* window) { break; case KeyPress: { - KeySym key = XLookupKeysym(&event.xkey, 0); - if (key == XK_Escape) { + KeySym keysym = XLookupKeysym(&event.xkey, 0); + KeyCode key = mapX11Key(keysym); + if (key != KEY_UNKNOWN && key < ::KEY_COUNT) { + window->keyState[key] = true; + } + if (keysym == XK_Escape) { window->shouldClose = true; } break; } + + case KeyRelease: { + KeySym keysym = XLookupKeysym(&event.xkey, 0); + KeyCode key = mapX11Key(keysym); + if (key != KEY_UNKNOWN && key < ::KEY_COUNT) { + window->keyState[key] = false; + } + break; + } + + case ButtonPress: + if (event.xbutton.button == Button1) + window->mouseState[MOUSE_LEFT] = true; + else if (event.xbutton.button == Button2) + window->mouseState[MOUSE_MIDDLE] = true; + else if (event.xbutton.button == Button3) + window->mouseState[MOUSE_RIGHT] = true; + else if (event.xbutton.button == Button4) + window->mouseWheelDelta = 1; + else if (event.xbutton.button == Button5) + window->mouseWheelDelta = -1; + break; + + case ButtonRelease: + if (event.xbutton.button == Button1) + window->mouseState[MOUSE_LEFT] = false; + else if (event.xbutton.button == Button2) + window->mouseState[MOUSE_MIDDLE] = false; + else if (event.xbutton.button == Button3) + window->mouseState[MOUSE_RIGHT] = false; + break; + + case MotionNotify: + window->mouseX = event.xmotion.x; + window->mouseY = event.xmotion.y; + break; } } + + // Calculate mouse delta + window->mouseDeltaX = window->mouseX - window->prevMouseX; + window->mouseDeltaY = window->mouseY - window->prevMouseY; + + // Handle mouse locking + if (window->mouseLocked) { + int centerX = window->width / 2; + int centerY = window->height / 2; + + XWarpPointer(window->display, None, window->window, 0, 0, 0, 0, centerX, centerY); + XFlush(window->display); + + window->mouseX = centerX; + window->mouseY = centerY; + } } void swapBuffers(WindowHandle* window) { @@ -752,4 +1428,99 @@ void delay(uint32_t milliseconds) { usleep(milliseconds * 1000); } -#endif // USE_X11 +// ============================================================================ +// INPUT HANDLING - X11 +// ============================================================================ + +bool keyDown(WindowHandle* window, KeyCode key) { + if (!window || key >= KEY_COUNT) return false; + return window->keyState[key]; +} + +bool keyPressed(WindowHandle* window, KeyCode key) { + if (!window || key >= KEY_COUNT) return false; + return window->keyState[key] && !window->prevKeyState[key]; +} + +bool keyReleased(WindowHandle* window, KeyCode key) { + if (!window || key >= KEY_COUNT) return false; + return !window->keyState[key] && window->prevKeyState[key]; +} + +bool mouseDown(WindowHandle* window, MouseButton button) { + if (!window || button >= MOUSE_BUTTON_COUNT) return false; + return window->mouseState[button]; +} + +bool mousePressed(WindowHandle* window, MouseButton button) { + if (!window || button >= MOUSE_BUTTON_COUNT) return false; + return window->mouseState[button] && !window->prevMouseState[button]; +} + +bool mouseReleased(WindowHandle* window, MouseButton button) { + if (!window || button >= MOUSE_BUTTON_COUNT) return false; + return !window->mouseState[button] && window->prevMouseState[button]; +} + +void getMousePosition(WindowHandle* window, int& x, int& y) { + if (!window) { + x = y = 0; + return; + } + x = window->mouseX; + y = window->mouseY; +} + +void getMouseDelta(WindowHandle* window, int& dx, int& dy) { + if (!window) { + dx = dy = 0; + return; + } + dx = window->mouseDeltaX; + dy = window->mouseDeltaY; +} + +void setMousePosition(WindowHandle* window, int x, int y) { + if (!window || !window->display) return; + + XWarpPointer(window->display, None, window->window, 0, 0, 0, 0, x, y); + XFlush(window->display); + + window->mouseX = x; + window->mouseY = y; +} + +void setMouseLocked(WindowHandle* window, bool locked) { + if (!window || !window->display) return; + window->mouseLocked = locked; + + if (locked) { + // Hide cursor + Cursor invisibleCursor; + Pixmap bitmapNoData; + XColor black; + static char noData[] = {0,0,0,0,0,0,0,0}; + black.red = black.green = black.blue = 0; + + bitmapNoData = XCreateBitmapFromData(window->display, window->window, noData, 8, 8); + invisibleCursor = XCreatePixmapCursor(window->display, bitmapNoData, bitmapNoData, + &black, &black, 0, 0); + XDefineCursor(window->display, window->window, invisibleCursor); + XFreeCursor(window->display, invisibleCursor); + XFreePixmap(window->display, bitmapNoData); + } else { + XUndefineCursor(window->display, window->window); + } +} + +bool isMouseLocked(WindowHandle* window) { + if (!window) return false; + return window->mouseLocked; +} + +int getMouseWheelDelta(WindowHandle* window) { + if (!window) return 0; + return window->mouseWheelDelta; +} + +#endif // USE_X11
\ No newline at end of file diff --git a/lib/graphics.h b/lib/graphics.h index cc0bd63..411f1aa 100644 --- a/lib/graphics.h +++ b/lib/graphics.h @@ -53,4 +53,76 @@ void drawPixel(WindowHandle* window, int x, int y, const Color& color); void setDrawColor(WindowHandle* window, const Color& color); void delay(uint32_t milliseconds); +// ============================================================================ +// INPUT HANDLING +// ============================================================================ + +// Key codes (cross-platform) +enum KeyCode { + KEY_UNKNOWN = 0, + + // Arrow keys + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + + // Letter keys + KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, + KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, + KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, + KEY_Y, KEY_Z, + + // Number keys + KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, + KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, + + // Function keys + KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, + KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, + + // Special keys + KEY_SPACE, + KEY_ENTER, + KEY_ESCAPE, + KEY_BACKSPACE, + KEY_TAB, + KEY_SHIFT, + KEY_CONTROL, + KEY_ALT, + + // Other + KEY_PLUS, + KEY_MINUS, + + KEY_COUNT // Total number of keys +}; + +// Mouse buttons +enum MouseButton { + MOUSE_LEFT = 0, + MOUSE_RIGHT, + MOUSE_MIDDLE, + MOUSE_BUTTON_COUNT +}; + +// Keyboard input functions +bool keyDown(WindowHandle* window, KeyCode key); +bool keyPressed(WindowHandle* window, KeyCode key); // True only on the frame the key was pressed +bool keyReleased(WindowHandle* window, KeyCode key); // True only on the frame the key was released + +// Mouse input functions +bool mouseDown(WindowHandle* window, MouseButton button); +bool mousePressed(WindowHandle* window, MouseButton button); +bool mouseReleased(WindowHandle* window, MouseButton button); + +void getMousePosition(WindowHandle* window, int& x, int& y); +void getMouseDelta(WindowHandle* window, int& dx, int& dy); +void setMousePosition(WindowHandle* window, int x, int y); +void setMouseLocked(WindowHandle* window, bool locked); // Locks mouse to window center (for FPS games) +bool isMouseLocked(WindowHandle* window); + +// Mouse wheel +int getMouseWheelDelta(WindowHandle* window); + #endif // GRAPHICS_H
\ No newline at end of file |
