331 lines
8.9 KiB
C
331 lines
8.9 KiB
C
#include <fcntl.h>
|
|
#include <linux/input.h>
|
|
#include <poll.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#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;
|
|
}
|