diff options
| author | lvntky <klevent1903@gmail.com> | 2024-11-28 12:30:22 +0300 |
|---|---|---|
| committer | lvntky <klevent1903@gmail.com> | 2024-11-28 12:30:22 +0300 |
| commit | fd7e67ca4d46b767482da9e3f26f39f4909bf9de (patch) | |
| tree | 7f92f71eb6944b615ce1b56b2ddaa15ef40f8f0f | |
| parent | 82d24368330114f10b34f622da9fb6706ba06cc1 (diff) | |
[feature] keyboard and raw mode implementation
| -rw-r--r-- | CMakeLists.txt | 1 | ||||
| -rw-r--r-- | examples/player.c | 119 | ||||
| -rw-r--r-- | fbgl.h | 163 |
3 files changed, 276 insertions, 7 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index fad5d22..7739254 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,3 +31,4 @@ add_example(framebuf_info) add_example(text) add_example(texture_show_fps) add_example(circle) +add_example(player) diff --git a/examples/player.c b/examples/player.c new file mode 100644 index 0000000..6eed8e7 --- /dev/null +++ b/examples/player.c @@ -0,0 +1,119 @@ +#define FBGL_IMPLEMENTATION +#include "fbgl.h" + +#include <stdio.h> +#include <unistd.h> + +#define PLAYER_SPEED 1 + +typedef struct { + int x; + int y; +} Player; + +int main(int argc, char *argv[]) +{ + // Check for font file argument + if (argc < 2) { + fprintf(stderr, "Usage: %s <psf1_font_file>\n", argv[0]); + return 1; + } + + // Load PSF1 font + fbgl_psf1_font_t *font = fbgl_load_psf1_font(argv[1]); + if (!font) { + fprintf(stderr, "Failed to load PSF1 font from %s\n", argv[1]); + return 1; + } + + // Initialize framebuffer + fbgl_t fb; + if (fbgl_init(NULL, &fb) != 0) { + fprintf(stderr, "Failed to initialize framebuffer\n"); + fbgl_destroy_psf1_font(font); + return 1; + } + + // Initialize keyboard + if (fbgl_keyboard_init() != 0) { + fprintf(stderr, "Failed to initialize keyboard\n"); + fbgl_destroy(&fb); + fbgl_destroy_psf1_font(font); + return 1; + } + + // Create a player + Player player = { .x = fb.width / 2, .y = fb.height / 2 }; + + // Game loop + while (1) { + // Clear the screen + fbgl_set_bg(&fb, 0x000000); + + // Get key input + fbgl_key_t key = fbgl_get_key(); + + // Handle player movement + switch (key) { + case FBGL_KEY_UP: + player.y = (player.y - PLAYER_SPEED < 0) ? + 0 : + player.y - PLAYER_SPEED; + break; + case FBGL_KEY_DOWN: + player.y = (player.y + PLAYER_SPEED >= fb.height) ? + fb.height - 1 : + player.y + PLAYER_SPEED; + break; + case FBGL_KEY_LEFT: + player.x = (player.x - PLAYER_SPEED < 0) ? + 0 : + player.x - PLAYER_SPEED; + break; + case FBGL_KEY_RIGHT: + player.x = (player.x + PLAYER_SPEED >= fb.width) ? + fb.width - 1 : + player.x + PLAYER_SPEED; + break; + case FBGL_KEY_ESCAPE: + // Exit the program + goto cleanup; + default: + break; + } + + // Draw the player (as a small white rectangle) + fbgl_point_t top_left = { .x = player.x - 5, + .y = player.y - 5 }; + fbgl_point_t bottom_right = { .x = player.x + 5, + .y = player.y + 5 }; + fbgl_draw_rectangle_filled(top_left, bottom_right, + FBGL_RGB(255, 255, 255), &fb); + + // Display debug info + char fps_text[32]; + char pos_text[32]; + float fps = fbgl_get_fps(); + + snprintf(fps_text, sizeof(fps_text), "FPS: %.2f", fps); + snprintf(pos_text, sizeof(pos_text), "POS: %d, %d", player.x, + player.y); + + // Render text using the loaded PSF1 font + fbgl_render_psf1_text(&fb, font, fps_text, 10, 10, + FBGL_RGB(0, 255, 0)); + fbgl_render_psf1_text(&fb, font, pos_text, 10, 30, + FBGL_RGB(255, 0, 0)); + + // Small delay to control frame rate + usleep(16666); // ~60 FPS + } + +cleanup: + // Cleanup + + fbgl_destroy(&fb); + fbgl_destroy_psf1_font(font); + + return 0; +} @@ -27,7 +27,6 @@ #define VERSION "0.1.0" #define NAME "FBGL" #define DEFAULT_FB "/dev/fb0" -#define FBGL_MAX_KEYS 256 // Maximum number of keys to track #include <fcntl.h> #include <linux/fb.h> @@ -45,11 +44,6 @@ #include <time.h> #include <unistd.h> -#ifdef FBGL_USE_FREETYPE -#include <ft2build.h> -#include FT_FREETYPE_H -#endif // FBGL_USE_FREETYPE - /** * Structs */ @@ -91,11 +85,31 @@ typedef struct fbgl_psf1_font { uint16_t char_width; // Character width in pixels (always 8 for PSF1) } fbgl_psf1_font_t; +typedef enum fbgl_key { + FBGL_KEY_NONE = 0, + FBGL_KEY_UP, + FBGL_KEY_DOWN, + FBGL_KEY_LEFT, + FBGL_KEY_RIGHT, + FBGL_KEY_ESCAPE, + FBGL_KEY_ENTER, + FBGL_KEY_SPACE, + +} fbgl_key_t; + +typedef struct fbgl_keyboard_state { + bool is_key_down; + fbgl_key_t current_key; + bool special_key_pressed; +} fbgl_keyboard_state_t; + /** * Key state function and variables * */ static struct timespec previous_frame_time = { 0 }; +static struct termios orig_termios; +static fbgl_keyboard_state_t g_keyboard_state = { 0 }; #ifdef __cplusplus extern "C" { @@ -158,7 +172,10 @@ void fbgl_render_psf1_text(fbgl_t *fb, fbgl_psf1_font_t *font, const char *text, /** * Keyboard */ -// Will refactor +int fbgl_keyboard_init(void); +void fbgl_destroy_keyboard(void); +fbgl_key_t fbgl_get_key(void); +bool fbgl_is_key_pressed(fbgl_key_t key); /** * Color Utilities @@ -172,6 +189,42 @@ void fbgl_render_psf1_text(fbgl_t *fb, fbgl_psf1_font_t *font, const char *text, (uint8_t)(b * 255))) #define FBGL_F32RGBA_TO_U32(r, g, b, a) ((uint32_t)(((uint8_t)(a * 255) << 24) | ((uint8_t)(r * 255) << 16) | ((uint8_t)(g * 255) << 8) | (uint8_t)(b * 255)) +// Inside functions +static void i_fbgl_die(const char *s); +static void i_fbgl_disable_raw_mode(); +static void i_fbgl_enable_raw_mode(); + +static void i_fbgl_die(const char *s) +{ + perror(s); + exit(1); +} + +static void i_fbgl_enable_raw_mode() +{ + if (tcgetattr(STDIN_FILENO, &orig_termios) == -1) { + i_fbgl_die("tcgetattr"); + } + atexit(i_fbgl_disable_raw_mode); + struct termios raw = orig_termios; + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + raw.c_oflag &= ~(OPOST); + raw.c_cflag |= (CS8); + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 1; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) { + i_fbgl_die("tcsetattr"); + } +} + +static void i_fbgl_disable_raw_mode() +{ + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios) == -1) { + i_fbgl_die("tcesetattr"); + } +} + #ifdef FBGL_IMPLEMENTATION char const *fbgl_name_info(void) @@ -693,6 +746,102 @@ void fbgl_render_psf1_text(fbgl_t *fb, fbgl_psf1_font_t *font, const char *text, } } +int fbgl_keyboard_init(void) +{ + i_fbgl_enable_raw_mode(); + + // Initialize keyboard state + g_keyboard_state.is_key_down = false; + g_keyboard_state.current_key = FBGL_KEY_NONE; + g_keyboard_state.special_key_pressed = false; + + return 0; +} + +void fbgl_destroy_keyboard(void) +{ + i_fbgl_disable_raw_mode(); +} +fbgl_key_t fbgl_get_key(void) +{ + char c; + ssize_t bytes_read = read(STDIN_FILENO, &c, 1); + + if (bytes_read <= 0) { + return FBGL_KEY_NONE; + } + + // Handle escape sequences for special keys + if (c == 27) { + char seq[3]; + if (read(STDIN_FILENO, &seq[0], 1) != 1) + return FBGL_KEY_ESCAPE; + if (read(STDIN_FILENO, &seq[1], 1) != 1) + return FBGL_KEY_ESCAPE; + + if (seq[0] == '[') { + switch (seq[1]) { + case 'A': + return FBGL_KEY_UP; + case 'B': + return FBGL_KEY_DOWN; + case 'C': + return FBGL_KEY_RIGHT; + case 'D': + return FBGL_KEY_LEFT; + } + } + + return FBGL_KEY_NONE; + } + + // Handle direct key presses + switch (c) { + case 10: // Enter key + return FBGL_KEY_ENTER; + case 32: // Space key + return FBGL_KEY_SPACE; + case 'w': + case 'W': + return FBGL_KEY_UP; + case 's': + case 'S': + return FBGL_KEY_DOWN; + case 'a': + case 'A': + return FBGL_KEY_LEFT; + case 'd': + case 'D': + return FBGL_KEY_RIGHT; + case 27: // Escape key + return FBGL_KEY_ESCAPE; + } + + return FBGL_KEY_NONE; +} + +bool fbgl_is_key_pressed(fbgl_key_t key) +{ + // Use select() for non-blocking input check + fd_set read_fds; + struct timeval timeout; + + FD_ZERO(&read_fds); + FD_SET(STDIN_FILENO, &read_fds); + + // Set a very short timeout + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + // Check if there's input available + if (select(STDIN_FILENO + 1, &read_fds, NULL, NULL, &timeout) > 0) { + fbgl_key_t pressed_key = fbgl_get_key(); + return pressed_key == key; + } + + return false; +} + #endif // FBGL_IMPLEMENTATION #ifdef __cplusplus |
