summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/graphics.cpp789
-rw-r--r--lib/graphics.h72
2 files changed, 852 insertions, 9 deletions
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, &centerX, &centerY);
+ 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