-
Notifications
You must be signed in to change notification settings - Fork 0
Handling Input
NtinosTheGamer2324 edited this page Mar 7, 2026
·
1 revision
Learn how to make your programs respond to keyboard and mouse!
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 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#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 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");
}
}
}
}// 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 1int 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();
}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");
}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");
}
}#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;
}-
Always use
yield()- Lets other programs run - Track previous state - For detecting button presses vs holds
- Clamp positions - Keep objects in screen bounds
- Use non-blocking reads - Event queue returns 0 when empty
Next: Check out Simple Examples for complete programs!