Skip to content

Handling Input

NtinosTheGamer2324 edited this page Mar 7, 2026 · 1 revision

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:

#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:

// 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

#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:

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

// 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

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:

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:

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

#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 for complete programs!

Clone this wiki locally