# Handling Input in NodGL Learn how to make your programs respond to keyboard and mouse! ## Getting Mouse Input ModuOS provides mouse events through the events system: ```c #include "NodGL.h" #include "libc.h" #include "../include/moduos/kernel/events/events.h" int md_main(long argc, char **argv) { NodGL_Device device; NodGL_Context ctx; NodGL_CreateDevice(NodGL_FEATURE_LEVEL_1_0, &device, &ctx, NULL); // Get event queue int queue_fd = open("/dev/event0", 0); if (queue_fd < 0) { printf("Failed to open event queue\n"); return 1; } int mouse_x = 0, mouse_y = 0; int mouse_buttons = 0; while (1) { event_t ev; // Read events (non-blocking) while (read(queue_fd, &ev, sizeof(ev)) == sizeof(ev)) { if (ev.type == EVENT_MOUSE_MOVE) { mouse_x = ev.mouse.x; mouse_y = ev.mouse.y; } if (ev.type == EVENT_MOUSE_BUTTON) { mouse_buttons = ev.mouse.buttons; } } // Draw NodGL_ClearContext(ctx, NodGL_CLEAR_COLOR, 0xFF000000, 1.0f, 0); // Draw a square following the mouse NodGL_FillRectContext(ctx, mouse_x - 10, mouse_y - 10, 20, 20, 0xFFFF0000); // Change color if button pressed if (mouse_buttons & 1) { // Left button NodGL_FillRectContext(ctx, mouse_x - 10, mouse_y - 10, 20, 20, 0xFF00FF00); } NodGL_PresentContext(ctx, 1); yield(); // Let other programs run } close(queue_fd); NodGL_ReleaseDevice(device); return 0; } ``` ## Mouse Button Checking Mouse buttons are stored as bits in the `buttons` field: ```c // Check individual buttons int left_button = mouse_buttons & 1; // Bit 0 int right_button = mouse_buttons & 2; // Bit 1 int middle_button = mouse_buttons & 4; // Bit 2 // Detect button press (was up, now down) static int prev_buttons = 0; int left_pressed = (mouse_buttons & 1) && !(prev_buttons & 1); int right_pressed = (mouse_buttons & 2) && !(prev_buttons & 2); prev_buttons = mouse_buttons; // Save for next frame ``` ## Complete Mouse Example: Paint Program ```c #include "NodGL.h" #include "libc.h" #include "../include/moduos/kernel/events/events.h" int md_main(long argc, char **argv) { NodGL_Device device; NodGL_Context ctx; NodGL_CreateDevice(NodGL_FEATURE_LEVEL_1_0, &device, &ctx, NULL); uint32_t screen_w, screen_h; NodGL_GetScreenResolution(device, &screen_w, &screen_h); // Create canvas backbuffer NodGL_TextureDesc desc = {0}; desc.width = screen_w; desc.height = screen_h; desc.format = NodGL_FORMAT_R8G8B8A8_UNORM; desc.mip_levels = 1; NodGL_Texture canvas; NodGL_CreateTexture(device, &desc, &canvas); uint32_t *pixels; uint32_t pitch; NodGL_MapResource(ctx, canvas, (void**)&pixels, &pitch); // Clear canvas to white for (int i = 0; i < screen_h * (pitch / 4); i++) { pixels[i] = 0xFFFFFFFF; } int queue_fd = open("/dev/event0", 0); int mx = 0, my = 0, buttons = 0; uint32_t brush_color = 0xFF000000; // Black while (1) { event_t ev; while (read(queue_fd, &ev, sizeof(ev)) == sizeof(ev)) { if (ev.type == EVENT_MOUSE_MOVE) { mx = ev.mouse.x; my = ev.mouse.y; } if (ev.type == EVENT_MOUSE_BUTTON) { buttons = ev.mouse.buttons; } } // Draw on canvas when left button held if (buttons & 1) { // Draw a small circle at mouse position for (int dy = -3; dy <= 3; dy++) { for (int dx = -3; dx <= 3; dx++) { if (dx * dx + dy * dy <= 9) { int px = mx + dx; int py = my + dy; if (px >= 0 && px < screen_w && py >= 0 && py < screen_h) { pixels[py * (pitch / 4) + px] = brush_color; } } } } } // Clear canvas on right button if (buttons & 2) { for (int i = 0; i < screen_h * (pitch / 4); i++) { pixels[i] = 0xFFFFFFFF; } } // Display NodGL_ClearContext(ctx, NodGL_CLEAR_COLOR, 0xFF808080, 1.0f, 0); NodGL_DrawTexture(ctx, canvas, 0, 0, 0, 0, screen_w, screen_h); // Draw cursor NodGL_FillRectContext(ctx, mx - 2, my - 2, 4, 4, 0xFFFF0000); NodGL_PresentContext(ctx, 1); yield(); } return 0; } ``` ## Keyboard Input Keyboard events work similarly: ```c while (read(queue_fd, &ev, sizeof(ev)) == sizeof(ev)) { if (ev.type == EVENT_KEYBOARD) { int keycode = ev.keyboard.keycode; int pressed = ev.keyboard.pressed; // 1 = down, 0 = up if (pressed) { // Key was just pressed if (keycode == KEY_SPACE) { printf("Space pressed!\n"); } } } } ``` ## Common Key Codes ```c // Arrow keys #define KEY_UP 72 #define KEY_DOWN 80 #define KEY_LEFT 75 #define KEY_RIGHT 77 // Letters (scan codes) #define KEY_W 17 #define KEY_A 30 #define KEY_S 31 #define KEY_D 32 // Special keys #define KEY_SPACE 57 #define KEY_ENTER 28 #define KEY_ESC 1 ``` ## Movement Example: WASD Controls ```c int player_x = 100, player_y = 100; int keys_held[256] = {0}; // Track which keys are down while (1) { event_t ev; while (read(queue_fd, &ev, sizeof(ev)) == sizeof(ev)) { if (ev.type == EVENT_KEYBOARD) { keys_held[ev.keyboard.keycode] = ev.keyboard.pressed; } } // Move based on held keys if (keys_held[KEY_W]) player_y -= 2; if (keys_held[KEY_S]) player_y += 2; if (keys_held[KEY_A]) player_x -= 2; if (keys_held[KEY_D]) player_x += 2; // Keep in bounds if (player_x < 0) player_x = 0; if (player_y < 0) player_y = 0; // Draw NodGL_ClearContext(ctx, NodGL_CLEAR_COLOR, 0xFF000000, 1.0f, 0); NodGL_FillRectContext(ctx, player_x, player_y, 32, 32, 0xFF00FF00); NodGL_PresentContext(ctx, 1); yield(); } ``` ## Collision Detection Check if two rectangles overlap: ```c int rectangles_overlap(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { return x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2; } // Usage: if (rectangles_overlap(player_x, player_y, 32, 32, enemy_x, enemy_y, 32, 32)) { printf("Hit!\n"); } ``` ## Point in Rectangle Check if mouse is over a button: ```c int point_in_rect(int px, int py, int rx, int ry, int rw, int rh) { return px >= rx && px < rx + rw && py >= ry && py < ry + rh; } // Button example if (point_in_rect(mouse_x, mouse_y, 100, 100, 200, 50)) { // Mouse is over button if (mouse_buttons & 1) { printf("Button clicked!\n"); } } ``` ## Complete Game Example: Catch the Squares ```c #include "NodGL.h" #include "libc.h" #include "../include/moduos/kernel/events/events.h" #define MAX_SQUARES 10 typedef struct { int x, y; int speed; int active; } Square; int md_main(long argc, char **argv) { NodGL_Device device; NodGL_Context ctx; NodGL_CreateDevice(NodGL_FEATURE_LEVEL_1_0, &device, &ctx, NULL); uint32_t screen_w, screen_h; NodGL_GetScreenResolution(device, &screen_w, &screen_h); int queue_fd = open("/dev/event0", 0); int mx = screen_w / 2, my = screen_h - 50; int score = 0; Square squares[MAX_SQUARES] = {0}; // Spawn squares for (int i = 0; i < MAX_SQUARES; i++) { squares[i].x = (rand() % (screen_w - 30)); squares[i].y = -30 - (i * 50); squares[i].speed = 2 + (rand() % 3); squares[i].active = 1; } while (1) { event_t ev; while (read(queue_fd, &ev, sizeof(ev)) == sizeof(ev)) { if (ev.type == EVENT_MOUSE_MOVE) { mx = ev.mouse.x; } } // Update squares for (int i = 0; i < MAX_SQUARES; i++) { if (!squares[i].active) continue; squares[i].y += squares[i].speed; // Check collision with player if (rectangles_overlap(mx - 20, my - 20, 40, 40, squares[i].x, squares[i].y, 30, 30)) { squares[i].active = 0; score++; // Respawn at top squares[i].x = rand() % (screen_w - 30); squares[i].y = -30; squares[i].active = 1; } // Reset if off screen if (squares[i].y > screen_h) { squares[i].y = -30; squares[i].x = rand() % (screen_w - 30); } } // Draw NodGL_ClearContext(ctx, NodGL_CLEAR_COLOR, 0xFF001020, 1.0f, 0); // Draw falling squares for (int i = 0; i < MAX_SQUARES; i++) { if (squares[i].active) { NodGL_FillRectContext(ctx, squares[i].x, squares[i].y, 30, 30, 0xFFFF0000); } } // Draw player (follows mouse X) NodGL_FillRectContext(ctx, mx - 20, my - 20, 40, 40, 0xFF00FF00); // Draw score (simple digit display) char score_text[32]; snprintf(score_text, sizeof(score_text), "Score: %d", score); for (int i = 0; score_text[i]; i++) { NodGL_FillRectContext(ctx, 10 + i * 8, 10, 6, 10, 0xFFFFFFFF); } NodGL_PresentContext(ctx, 1); yield(); } return 0; } ``` ## Tips 1. **Always use `yield()`** - Lets other programs run 2. **Track previous state** - For detecting button presses vs holds 3. **Clamp positions** - Keep objects in screen bounds 4. **Use non-blocking reads** - Event queue returns 0 when empty Next: Check out [Simple Examples](Simple-Examples.md) for complete programs!