#include #include #include #include #include #include #include #include #define WIDTH 720 #define HEIGHT 720 #define BPP 4 // BGRA = 4 bytes per pixel #define MAX_BUTTONS 10 #define BUTTON_HEIGHT 60 #define BUTTON_WIDTH 500 #define BUTTON_MARGIN 10 typedef struct { int fb_fd; uint8_t *framebuffer; } FramebufferDemo; typedef struct { char text[64]; int x, y, width, height; int id; } Button; typedef struct { Button buttons[MAX_BUTTONS]; int count; int selected; } Menu; // Initialize framebuffer int fb_init(FramebufferDemo *fb) { fb->fb_fd = open("/dev/fb0", O_RDWR); if (fb->fb_fd < 0) { fprintf(stderr, "Error: Cannot open framebuffer device /dev/fb0\n"); return -1; } fb->framebuffer = (uint8_t *)malloc(WIDTH * HEIGHT * BPP); if (!fb->framebuffer) { fprintf(stderr, "Error: Cannot allocate framebuffer memory\n"); close(fb->fb_fd); return -1; } printf("Framebuffer initialized: %dx%d BGRA\n", WIDTH, HEIGHT); return 0; } void fb_close(FramebufferDemo *fb) { if (fb->framebuffer) { free(fb->framebuffer); fb->framebuffer = NULL; } if (fb->fb_fd >= 0) { close(fb->fb_fd); fb->fb_fd = -1; } } void set_pixel(FramebufferDemo *fb, int x, int y, uint8_t b, uint8_t g, uint8_t r, uint8_t a) { if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) { return; } int offset = (y * WIDTH + x) * BPP; fb->framebuffer[offset + 0] = b; fb->framebuffer[offset + 1] = g; fb->framebuffer[offset + 2] = r; fb->framebuffer[offset + 3] = a; } void fill_screen(FramebufferDemo *fb, uint8_t b, uint8_t g, uint8_t r, uint8_t a) { for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { set_pixel(fb, x, y, b, g, r, a); } } } void draw_rectangle(FramebufferDemo *fb, int x, int y, int w, int h, uint8_t b, uint8_t g, uint8_t r, uint8_t a) { for (int dy = 0; dy < h; dy++) { for (int dx = 0; dx < w; dx++) { set_pixel(fb, x + dx, y + dy, b, g, r, a); } } } void draw_border(FramebufferDemo *fb, int x, int y, int w, int h, int thickness, uint8_t b, uint8_t g, uint8_t r, uint8_t a) { // Top draw_rectangle(fb, x, y, w, thickness, b, g, r, a); // Bottom draw_rectangle(fb, x, y + h - thickness, w, thickness, b, g, r, a); // Left draw_rectangle(fb, x, y, thickness, h, b, g, r, a); // Right draw_rectangle(fb, x + w - thickness, y, thickness, h, b, g, r, a); } // Simple 8x8 font for characters void draw_char(FramebufferDemo *fb, int x, int y, char c, uint8_t b, uint8_t g, uint8_t r, uint8_t a) { // Simple pixel patterns for some characters static const uint8_t font[][8] = { {0x3C, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x3C, 0x00}, // A {0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00}, // B {0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00}, // C {0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00}, // D {0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00}, // E {0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00}, // F {0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00}, // G {0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00}, // H {0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00}, // I {0x1E, 0x0C, 0x0C, 0x0C, 0x6C, 0x6C, 0x38, 0x00}, // J }; if (c >= 'A' && c <= 'J') { const uint8_t *pattern = font[c - 'A']; for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { if (pattern[row] & (1 << (7 - col))) { set_pixel(fb, x + col * 2, y + row * 2, b, g, r, a); set_pixel(fb, x + col * 2 + 1, y + row * 2, b, g, r, a); set_pixel(fb, x + col * 2, y + row * 2 + 1, b, g, r, a); set_pixel(fb, x + col * 2 + 1, y + row * 2 + 1, b, g, r, a); } } } } } void draw_text(FramebufferDemo *fb, int x, int y, const char *text, uint8_t b, uint8_t g, uint8_t r, uint8_t a) { int offset = 0; for (int i = 0; text[i] != '\0'; i++) { if (text[i] >= 'A' && text[i] <= 'Z') { draw_char(fb, x + offset, y, text[i], b, g, r, a); offset += 18; } else if (text[i] >= 'a' && text[i] <= 'z') { draw_char(fb, x + offset, y, text[i] - 32, b, g, r, a); offset += 18; } else { offset += 10; // Space } } } void draw_button(FramebufferDemo *fb, Button *btn, int selected) { if (selected) { // Selected: bright blue background with yellow border draw_rectangle(fb, btn->x, btn->y, btn->width, btn->height, 200, 100, 50, 255); draw_border(fb, btn->x, btn->y, btn->width, btn->height, 4, 0, 200, 255, 255); } else { // Normal: dark gray background with light border draw_rectangle(fb, btn->x, btn->y, btn->width, btn->height, 80, 80, 80, 255); draw_border(fb, btn->x, btn->y, btn->width, btn->height, 2, 150, 150, 150, 255); } // Draw text int text_x = btn->x + 20; int text_y = btn->y + (btn->height - 16) / 2; draw_text(fb, text_x, text_y, btn->text, 255, 255, 255, 255); } void fb_update(FramebufferDemo *fb) { if (fb->fb_fd < 0) return; lseek(fb->fb_fd, 0, SEEK_SET); write(fb->fb_fd, fb->framebuffer, WIDTH * HEIGHT * BPP); } // Initialize menu void menu_init(Menu *menu) { menu->count = 0; menu->selected = 0; } // Add button to menu void menu_add_button(Menu *menu, const char *text) { if (menu->count >= MAX_BUTTONS) return; Button *btn = &menu->buttons[menu->count]; strncpy(btn->text, text, sizeof(btn->text) - 1); btn->text[sizeof(btn->text) - 1] = '\0'; btn->width = BUTTON_WIDTH; btn->height = BUTTON_HEIGHT; btn->x = (WIDTH - BUTTON_WIDTH) / 2; btn->y = 100 + menu->count * (BUTTON_HEIGHT + BUTTON_MARGIN); btn->id = menu->count; menu->count++; } // Draw entire menu void menu_draw(FramebufferDemo *fb, Menu *menu) { // Clear screen with dark background fill_screen(fb, 30, 30, 30, 255); // Draw title draw_text(fb, 250, 30, "MENU PRINCIPAL", 0, 255, 255, 255); // Draw all buttons for (int i = 0; i < menu->count; i++) { draw_button(fb, &menu->buttons[i], i == menu->selected); } // Draw instructions at bottom draw_text(fb, 200, HEIGHT - 40, "USE DIRECIONAL E BOTAO A", 200, 200, 200, 255); } // Open joystick/gamepad device int joystick_open() { // Try common input device paths const char *devices[] = {"/dev/input/event0", "/dev/input/event1", "/dev/input/event2", "/dev/input/event3", "/dev/input/js0", NULL}; for (int i = 0; devices[i] != NULL; i++) { int fd = open(devices[i], O_RDONLY | O_NONBLOCK); if (fd >= 0) { printf("Opened input device: %s\n", devices[i]); return fd; } } fprintf(stderr, "Warning: Could not open joystick device\n"); fprintf(stderr, "Falling back to keyboard input (arrow keys + Enter)\n"); return -1; } int main(int argc, char *argv[]) { FramebufferDemo fb = {0}; Menu menu = {0}; if (fb_init(&fb) < 0) { return 1; } // Initialize menu menu_init(&menu); menu_add_button(&menu, "INICIAR JOGO"); menu_add_button(&menu, "CONFIGURACOES"); menu_add_button(&menu, "CARREGAR SAVE"); menu_add_button(&menu, "CREDITOS"); menu_add_button(&menu, "SAIR"); // Open joystick int joy_fd = joystick_open(); // Main loop int running = 1; printf("Menu running. Use D-pad to navigate, A button to select.\n"); printf("Press Ctrl+C to exit.\n"); while (running) { // Draw menu menu_draw(&fb, &menu); fb_update(&fb); // Handle input if (joy_fd >= 0) { struct input_event ev; struct pollfd pfd = {.fd = joy_fd, .events = POLLIN}; if (poll(&pfd, 1, 100) > 0) { if (read(joy_fd, &ev, sizeof(ev)) == sizeof(ev)) { if (ev.type == EV_KEY && ev.value == 1) { // Key pressed switch (ev.code) { case BTN_DPAD_UP: case KEY_UP: if (menu.selected > 0) { menu.selected--; printf("Selected: %s\n", menu.buttons[menu.selected].text); } break; case BTN_DPAD_DOWN: case KEY_DOWN: if (menu.selected < menu.count - 1) { menu.selected++; printf("Selected: %s\n", menu.buttons[menu.selected].text); } break; case BTN_SOUTH: // A button case KEY_ENTER: printf("Activated: %s\n", menu.buttons[menu.selected].text); if (menu.selected == 4) { // SAIR running = 0; } break; case KEY_ESC: running = 0; break; } } } } } else { // Fallback: wait a bit usleep(100000); } } // Cleanup if (joy_fd >= 0) { close(joy_fd); } fill_screen(&fb, 0, 0, 0, 255); fb_update(&fb); fb_close(&fb); printf("Menu closed.\n"); return 0; }