fbgl 0.1.0
Loading...
Searching...
No Matches
fbgl.h
1// fbgl.h - v0.1.0 - public domain Levent Kaya 2024
2// FBGL - Framebuffer Graphics Library
3//
4// This file provides both the interface and the implementation.
5// To instantiate the implementation,
6// #define FBGL_IMPLEMENTATION
7// in *ONE* source file, before #including this file.
8//
9//
10// History:
11// - 0.1.0 First public release
12//
13// Status:
14// 24/11/2024 texture rendering implemented
15//
16// Contributors:
17// @lvntky
18// @dario-loi
19//
20// LICENSE
21//
22// See end of file for license information.
23
24#ifndef __FBGL_H__
25#define __FBGL_H__
26
27#define VERSION "0.1.0"
28#define NAME "FBGL"
29#define DEFAULT_FB "/dev/fb0"
30
31#include <fcntl.h>
32#include <linux/fb.h>
33#include <math.h>
34#include <signal.h>
35#include <stdbool.h>
36#include <stddef.h>
37#include <stdint.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <sys/ioctl.h>
42#include <sys/mman.h>
43#include <termios.h>
44#include <time.h>
45#include <unistd.h>
46
50typedef struct fbgl {
51 int32_t width;
52 int32_t height;
53 int32_t fd;
54 uint32_t screen_size;
55 uint32_t *pixels;
56 struct fb_var_screeninfo vinfo; // Variable screen information
57 struct fb_fix_screeninfo finfo; // Fixed screen information
58} fbgl_t;
59
60typedef struct fbgl_window {
61 int32_t x; // Top-left x-coordinate of the window
62 int32_t y; // Top-left y-coordinate of the window
63 uint32_t width; // Width of the window
64 uint32_t height; // Height of the window
65 fbgl_t *fb; // Pointer to the framebuffer context
67
68typedef struct fbgl_point {
69 int32_t x;
70 int32_t y;
72
73typedef struct fbgl_tga_texture {
74 uint16_t width;
75 uint16_t height;
76 uint32_t *data;
78
79typedef struct fbgl_psf1_font {
80 uint8_t magic[2]; // Magic number (0x36, 0x04 for PSF1)
81 uint8_t mode; // Mode (0 = 256 glyphs, 1 = 512 glyphs)
82 uint8_t char_height; // Character height in pixels
83 uint8_t *glyphs; // Pointer to glyph data
84 uint16_t glyph_count; // Number of glyphs (calculated from mode)
85 uint16_t char_width; // Character width in pixels (always 8 for PSF1)
87
88typedef enum fbgl_key {
89 FBGL_KEY_NONE = 0,
90 FBGL_KEY_UP,
91 FBGL_KEY_DOWN,
92 FBGL_KEY_LEFT,
93 FBGL_KEY_RIGHT,
94 FBGL_KEY_ESCAPE,
95 FBGL_KEY_ENTER,
96 FBGL_KEY_SPACE,
97
98} fbgl_key_t;
99
100typedef struct fbgl_keyboard_state {
101 bool is_key_down;
102 fbgl_key_t current_key;
103 bool special_key_pressed;
105
110static struct timespec previous_frame_time = { 0 };
111static struct termios orig_termios;
112static fbgl_keyboard_state_t g_keyboard_state = { 0 };
113
114#ifdef __cplusplus
115extern "C" {
116#endif
117
121char const *fbgl_name_info(void);
122char const *fbgl_version_info(void);
123float fbgl_get_fps(void);
124
125/*Create and destroy methods*/
126int fbgl_init(const char *device, fbgl_t *fb);
127void fbgl_destroy(fbgl_t *fb);
128
132void fbgl_clear(uint32_t color);
133void fbgl_put_pixel(int x, int y, uint32_t color, fbgl_t *fb);
134void fbgl_draw_line(fbgl_point_t x, fbgl_point_t y, uint32_t color, fbgl_t *fb);
135
139uint32_t *fb_get_data(fbgl_t const *fb);
140uint32_t fb_get_width(fbgl_t const *fb);
141uint32_t fb_get_height(fbgl_t const *fb);
142
146void fbgl_draw_rectangle_outline(fbgl_point_t top_left,
147 fbgl_point_t bottom_right, uint32_t color,
148 fbgl_t *fb);
149void fbgl_draw_rectangle_filled(fbgl_point_t top_left,
150 fbgl_point_t bottom_right, uint32_t color,
151 fbgl_t *fb);
152void fbgl_draw_circle_outline(int x, int y, int radius, uint32_t color,
153 fbgl_t *fb);
154void fbgl_draw_circle_filled(int x, int y, int radius, uint32_t color,
155 fbgl_t *fb);
156
160fbgl_tga_texture_t *fbgl_load_tga_texture(const char *path);
161void fbgl_destroy_texture(fbgl_tga_texture_t *texture);
162void fbgl_draw_texture(fbgl_t *fb, fbgl_tga_texture_t const *texture, int32_t x,
163 int32_t y);
164
168fbgl_psf1_font_t *fbgl_load_psf1_font(const char *path);
169void fbgl_destroy_psf1_font(fbgl_psf1_font_t *font);
170void fbgl_render_psf1_text(fbgl_t *fb, fbgl_psf1_font_t *font, const char *text,
171 int x, int y, uint32_t color);
175int fbgl_keyboard_init(void);
176void fbgl_destroy_keyboard(void);
177fbgl_key_t fbgl_get_key(void);
178bool fbgl_is_key_pressed(fbgl_key_t key);
179
184#define FBGL_RGB(r, g, b) ((uint32_t)(((r) << 16) | ((g) << 8) | (b)))
185#define FBGL_RGBA(r, g, b, a) \
186 ((uint32_t)(((a) << 24) | ((r) << 16) | ((g) << 8) | (b)))
187#define FBGL_F32RGB_TO_U32(r, g, b) \
188 ((uint32_t)(((uint8_t)(r * 255) << 16) | ((uint8_t)(g * 255) << 8) | \
189 (uint8_t)(b * 255)))
190#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))
191
192// Inside functions
193static void i_fbgl_die(const char *s);
194static void i_fbgl_disable_raw_mode();
195static void i_fbgl_enable_raw_mode();
196
197static void i_fbgl_die(const char *s)
198{
199 perror(s);
200 exit(1);
201}
202
203static void i_fbgl_enable_raw_mode()
204{
205 if (tcgetattr(STDIN_FILENO, &orig_termios) == -1) {
206 i_fbgl_die("tcgetattr");
207 }
208 atexit(i_fbgl_disable_raw_mode);
209 struct termios raw = orig_termios;
210 raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
211 raw.c_oflag &= ~(OPOST);
212 raw.c_cflag |= (CS8);
213 raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
214 raw.c_cc[VMIN] = 0;
215 raw.c_cc[VTIME] = 1;
216 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) {
217 i_fbgl_die("tcsetattr");
218 }
219}
220
221static void i_fbgl_disable_raw_mode()
222{
223 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios) == -1) {
224 i_fbgl_die("tcesetattr");
225 }
226}
227
228#ifdef FBGL_IMPLEMENTATION
229
230char const *fbgl_name_info(void)
231{
232 return NAME;
233}
234
235char const *fbgl_version_info(void)
236{
237 return VERSION;
238}
239
240int fbgl_init(const char *device, fbgl_t *fb)
241{
242 if (!fb) {
243 fprintf(stderr, "Error: fbgl_t pointer is NULL.");
244 return -1;
245 }
246
247 fb->fd = device == NULL ? open(DEFAULT_FB, O_RDWR) :
248 open(device, O_RDWR);
249 if (fb->fd == -1) {
250 perror("Error openning framebuffer device");
251 return -1;
252 }
253
254 if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->finfo) == -1) {
255 perror("Error: Reading fixed information.");
256 close(fb->fd);
257 return -1;
258 }
259 if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vinfo) == -1) {
260 perror("Error reading variable information");
261 close(fb->fd);
262 return -1;
263 }
264
265 fb->width = fb->vinfo.xres;
266 fb->height = fb->vinfo.yres;
267 fb->screen_size = fb->finfo.smem_len;
268
269 // Map framebuffer to memory
270 fb->pixels = (uint32_t *)mmap(NULL, fb->screen_size,
271 PROT_READ | PROT_WRITE, MAP_SHARED,
272 fb->fd, 0);
273 if (fb->pixels == MAP_FAILED) {
274 perror("Error mapping framebuffer device to memory");
275 close(fb->fd);
276 return -1;
277 }
278
279 return 0;
280}
281
282void fbgl_destroy(fbgl_t *fb)
283{
284 if (!fb || fb->fd == -1) {
285 fprintf(stderr,
286 "Error: framebuffer not initialized or already destroyed.\n");
287 return;
288 }
289
290 if (fb->pixels && fb->pixels != MAP_FAILED) {
291 munmap(fb->pixels, fb->screen_size);
292 }
293
294 close(fb->fd);
295 fb->fd = -1;
296}
297
298void fbgl_set_bg(fbgl_t *fb, uint32_t color)
299{
300#ifdef DEBUG
301 if (!fb || fb->fd == -1) {
302 fprintf(stderr, "Error: framebuffer not initialized.\n");
303 return;
304 }
305#endif // DEBUG
306
307 // Fill the entire framebuffer with the specified color
308 for (int32_t i = 0; i < fb->width * fb->height; i++) {
309 fb->pixels[i] = color;
310 }
311}
312
313void fbgl_put_pixel(int x, int y, uint32_t color, fbgl_t *fb)
314{
315#ifdef FBGL_VALIDATE_PUT_PIXEL
316 if (!fb || !fb->pixels) {
317 fprintf(stderr, "Error: framebuffer not initialized.\n");
318 return;
319 }
320
321 if (x < 0 || x >= fb->width || y < 0 || y >= fb->height) {
322 return; // Ignore out-of-bound coordinates
323 }
324#endif // FBGL_VALIDATE_PUT_PIXEL
325
326 const size_t index = y * fb->width + x;
327 fb->pixels[index] = color;
328}
329
330void fbgl_draw_line(fbgl_point_t x, fbgl_point_t y, uint32_t color,
331 fbgl_t *buffer)
332{
333 const int32_t dx = abs(y.x - x.x);
334 const int32_t dy = abs(y.y - x.y);
335
336 const int32_t sx = (x.x < y.x) ? 1 : -1;
337 const int32_t sy = (x.y < y.y) ? 1 : -1;
338
339 int32_t err = dx - dy;
340
341 while (1) {
342 // Set the pixel at the current position
343 fbgl_put_pixel(x.x, x.y, color, buffer);
344
345 // If we've reached the end point, break
346 if (x.x >= y.x && x.y >= y.y)
347 break;
348
349 const int32_t e2 = 2 * err;
350
351 if (e2 > -dy) {
352 err -= dy;
353 x.x += sx;
354 }
355
356 if (e2 < dx) {
357 err += dx;
358 x.y += sy;
359 }
360 }
361}
362void fbgl_draw_rectangle_outline(fbgl_point_t top_left,
363 fbgl_point_t bottom_right, uint32_t color,
364 fbgl_t *fb)
365{
366 // Top horizontal line
367 for (int x = top_left.x; x < bottom_right.x; x++) {
368 fbgl_put_pixel(x, top_left.y, color, fb);
369 }
370
371 // Bottom horizontal line
372 for (int x = top_left.x; x < bottom_right.x; x++) {
373 fbgl_put_pixel(x, bottom_right.y - 1, color, fb);
374 }
375
376 // Left vertical line
377 for (int y = top_left.y; y < bottom_right.y; y++) {
378 fbgl_put_pixel(top_left.x, y, color, fb);
379 }
380
381 // Right vertical line
382 for (int y = top_left.y; y < bottom_right.y; y++) {
383 fbgl_put_pixel(bottom_right.x - 1, y, color, fb);
384 }
385}
386
387void fbgl_draw_rectangle_filled(fbgl_point_t top_left,
388 fbgl_point_t bottom_right, uint32_t color,
389 fbgl_t *fb)
390{
391 for (int32_t y = top_left.y; y < bottom_right.y; y++) {
392 // Manually set each pixel in the row
393 for (int32_t x = top_left.x; x < bottom_right.x; x++) {
394 fbgl_put_pixel(x, y, color, fb);
395 }
396 }
397}
398
399void fbgl_draw_circle_outline(int x, int y, int radius, uint32_t color,
400 fbgl_t *fb)
401{
402 int f = 1 - radius;
403 int ddF_x = 1;
404 int ddF_y = -2 * radius;
405 int xx = 0;
406 int yy = radius;
407
408 fbgl_put_pixel(x, y + radius, color, fb);
409 fbgl_put_pixel(x, y - radius, color, fb);
410 fbgl_put_pixel(x + radius, y, color, fb);
411 fbgl_put_pixel(x - radius, y, color, fb);
412
413 while (xx < yy) {
414 if (f >= 0) {
415 yy--;
416 ddF_y += 2;
417 f += ddF_y;
418 }
419 xx++;
420 ddF_x += 2;
421 f += ddF_x;
422
423 fbgl_put_pixel(x + xx, y + yy, color, fb);
424 fbgl_put_pixel(x - xx, y + yy, color, fb);
425 fbgl_put_pixel(x + xx, y - yy, color, fb);
426 fbgl_put_pixel(x - xx, y - yy, color, fb);
427 fbgl_put_pixel(x + yy, y + xx, color, fb);
428 fbgl_put_pixel(x - yy, y + xx, color, fb);
429 fbgl_put_pixel(x + yy, y - xx, color, fb);
430 fbgl_put_pixel(x - yy, y - xx, color, fb);
431 }
432}
433
434void fbgl_draw_circle_filled(int x, int y, int radius, uint32_t color,
435 fbgl_t *fb)
436{
437 for (int yy = -radius; yy <= radius; ++yy) {
438 int half_width = (int)sqrt(radius * radius - yy * yy);
439
440 int row_start = x - half_width;
441 int row_end = x + half_width;
442
443 if (y + yy < 0 || y + yy >= fb->height)
444 continue;
445 if (row_start < 0)
446 row_start = 0;
447 if (row_end >= fb->width)
448 row_end = fb->width - 1;
449
450 int pixel_offset = (y + yy) * fb->width + row_start;
451 int num_pixels = row_end - row_start + 1;
452
453 uint32_t *row_start_ptr = fb->pixels + pixel_offset;
454 for (int i = 0; i < num_pixels; ++i) {
455 row_start_ptr[i] = color;
456 }
457 }
458}
459
460fbgl_tga_texture_t *fbgl_load_tga_texture(const char *path)
461{
462 FILE *file = fopen(path, "rb");
463 if (!file) {
464 perror("Unable to open texture file");
465 return NULL;
466 }
467
468 // TGA header structure
469 uint8_t header[18];
470 if (fread(header, 1, sizeof(header), file) != sizeof(header)) {
471 perror("Error reading TGA header");
472 fclose(file);
473 return NULL;
474 }
475
476 // Allocate texture structure
477 fbgl_tga_texture_t *texture =
478 (fbgl_tga_texture_t *)malloc(sizeof(fbgl_tga_texture_t));
479 if (!texture) {
480 perror("Failed to allocate texture structure");
481 fclose(file);
482 return NULL;
483 }
484
485 // Extract dimensions from header
486 texture->width = header[12] | (header[13] << 8);
487 texture->height = header[14] | (header[15] << 8);
488 uint8_t bits_per_pixel = header[16];
489 uint8_t image_descriptor = header[17];
490
491 // Verify format support
492 if (bits_per_pixel != 24 && bits_per_pixel != 32) {
493 fprintf(stderr,
494 "Unsupported TGA bit depth: %d (only 24 and 32-bit supported)\n",
495 bits_per_pixel);
496 free(texture);
497 fclose(file);
498 return NULL;
499 }
500
501 // Skip image ID field
502 if (header[0]) {
503 fseek(file, header[0], SEEK_CUR);
504 }
505
506 // Allocate pixel data
507 size_t pixel_count = texture->width * texture->height;
508 texture->data = (uint32_t *)malloc(pixel_count * sizeof(uint32_t));
509 if (!texture->data) {
510 perror("Failed to allocate pixel data");
511 free(texture);
512 fclose(file);
513 return NULL;
514 }
515
516 // Read pixel data
517 uint8_t *pixel_buffer = (uint8_t *)malloc(bits_per_pixel / 8);
518 if (!pixel_buffer) {
519 perror("Failed to allocate pixel buffer");
520 free(texture->data);
521 free(texture);
522 fclose(file);
523 return NULL;
524 }
525
526 // Determine if image is flipped (based on image descriptor)
527 bool bottom_up = !(image_descriptor & 0x20);
528
529 for (size_t i = 0; i < pixel_count; i++) {
530 size_t pixel_index =
531 bottom_up ?
532 (texture->height - 1 - (i / texture->width)) *
533 texture->width +
534 (i % texture->width) :
535 i;
536
537 if (fread(pixel_buffer, 1, bits_per_pixel / 8, file) !=
538 bits_per_pixel / 8) {
539 perror("Error reading pixel data");
540 free(pixel_buffer);
541 free(texture->data);
542 free(texture);
543 fclose(file);
544 return NULL;
545 }
546
547 // Convert BGR(A) to RGBA
548 uint32_t pixel = 0xFF000000; // Default alpha to 255
549 pixel |= pixel_buffer[2] << 16; // R
550 pixel |= pixel_buffer[1] << 8; // G
551 pixel |= pixel_buffer[0]; // B
552 if (bits_per_pixel == 32) {
553 pixel = (pixel & 0x00FFFFFF) |
554 (pixel_buffer[3] << 24); // A
555 }
556
557 texture->data[pixel_index] = pixel;
558 }
559
560 free(pixel_buffer);
561 fclose(file);
562 return texture;
563}
564
565void fbgl_destroy_texture(fbgl_tga_texture_t *texture)
566{
567 if (texture) {
568 free(texture->data);
569 free(texture);
570 }
571}
572
573void fbgl_draw_texture(fbgl_t *fb, fbgl_tga_texture_t const *texture, int32_t x,
574 int32_t y)
575{
576 if (!fb || !texture || !texture->data) {
577 return;
578 }
579
580 for (int ty = 0; ty < texture->height; ty++) {
581 for (int tx = 0; tx < texture->width; tx++) {
582 int screen_x = x + tx;
583 int screen_y = y + ty;
584
585 // Skip if outside screen bounds
586 if (screen_x < 0 || screen_x >= fb->width ||
587 screen_y < 0 || screen_y >= fb->height) {
588 continue;
589 }
590
591 uint32_t pixel =
592 texture->data[ty * texture->width + tx];
593 // Only draw if pixel is not fully transparent
594 if ((pixel & 0xFF000000) != 0) {
595 fbgl_put_pixel(screen_x, screen_y, pixel, fb);
596 }
597 }
598 }
599}
600
601uint32_t fb_get_width(fbgl_t const *fb)
602{
603 return fb->width;
604}
605
606uint32_t fb_get_height(fbgl_t const *fb)
607{
608 return fb->height;
609}
610
611uint32_t *fb_get_data(fbgl_t const *fb)
612{
613 return fb->pixels;
614}
615
616float fbgl_get_fps(void)
617{
618 struct timespec current_time;
619 clock_gettime(CLOCK_MONOTONIC, &current_time);
620
621 if (previous_frame_time.tv_sec == 0 &&
622 previous_frame_time.tv_nsec == 0) {
623 previous_frame_time = current_time;
624 return 0.0f;
625 }
626
627 double time_diff =
628 (current_time.tv_sec - previous_frame_time.tv_sec) +
629 (current_time.tv_nsec - previous_frame_time.tv_nsec) / 1e9;
630
631 previous_frame_time = current_time;
632
633 if (time_diff > 0.0) {
634 return 1.0 / time_diff;
635 } else {
636 return 0.0f; // Avoid division by zero
637 }
638}
639
640fbgl_psf1_font_t *fbgl_load_psf1_font(const char *path)
641{
642 FILE *file = fopen(path, "rb");
643 if (!file) {
644 perror("Failed to open font file");
645 return NULL;
646 }
647
648 // Allocate memory for the font structure
649 fbgl_psf1_font_t *font = malloc(sizeof(fbgl_psf1_font_t));
650 if (!font) {
651 perror("Failed to allocate memory for font");
652 fclose(file);
653 return NULL;
654 }
655
656 // Read the header (4 bytes)
657 uint8_t header[4];
658 if (fread(header, 1, sizeof(header), file) != sizeof(header)) {
659 perror("Failed to read font header");
660 free(font);
661 fclose(file);
662 return NULL;
663 }
664
665 // Verify magic number
666 if (header[0] != 0x36 || header[1] != 0x04) {
667 fprintf(stderr, "Invalid PSF1 magic number\n");
668 free(font);
669 fclose(file);
670 return NULL;
671 }
672
673 // Populate the font structure
674 font->magic[0] = header[0];
675 font->magic[1] = header[1];
676 font->mode = header[2];
677 font->char_height = header[3];
678 font->glyph_count = (font->mode & 0x01) ? 512 :
679 256; // Determine glyph count
680 font->char_width = 8; // PSF1 glyphs are always 8 pixels wide
681
682 // Allocate memory for glyphs
683 size_t glyph_data_size = font->glyph_count * font->char_height;
684 font->glyphs = malloc(glyph_data_size);
685 if (!font->glyphs) {
686 perror("Failed to allocate memory for glyphs");
687 free(font);
688 fclose(file);
689 return NULL;
690 }
691
692 // Read glyph data
693 if (fread(font->glyphs, 1, glyph_data_size, file) != glyph_data_size) {
694 perror("Failed to read glyph data");
695 free(font->glyphs);
696 free(font);
697 fclose(file);
698 return NULL;
699 }
700
701 fclose(file);
702 return font;
703}
704
705void fbgl_destroy_psf1_font(fbgl_psf1_font_t *font)
706{
707 if (font) {
708 free(font->glyphs);
709 free(font);
710 }
711}
712
713void fbgl_render_psf1_text(fbgl_t *fb, fbgl_psf1_font_t *font, const char *text,
714 int x, int y, uint32_t color)
715{
716 if (!fb || !font || !text)
717 return;
718
719 int cursor_x = x;
720 int cursor_y = y;
721
722 for (const char *c = text; *c; c++) {
723 uint8_t glyph_index = (uint8_t)*c;
724
725 // Ensure glyph index is within range
726 if (glyph_index >= font->glyph_count)
727 glyph_index = 0; // Default to space or undefined glyph
728
729 // Locate the glyph in the glyph table
730 uint8_t *glyph = font->glyphs + glyph_index * font->char_height;
731
732 // Render the glyph
733 for (int row = 0; row < font->char_height; row++) {
734 for (int col = 0; col < font->char_width; col++) {
735 // Check if the bit is set in the glyph
736 if (glyph[row] & (0x80 >> col)) {
737 fbgl_put_pixel(cursor_x + col,
738 cursor_y + row, color,
739 fb);
740 }
741 }
742 }
743
744 // Move to the next character position
745 cursor_x += font->char_width;
746 }
747}
748
749int fbgl_keyboard_init(void)
750{
751 i_fbgl_enable_raw_mode();
752
753 // Initialize keyboard state
754 g_keyboard_state.is_key_down = false;
755 g_keyboard_state.current_key = FBGL_KEY_NONE;
756 g_keyboard_state.special_key_pressed = false;
757
758 return 0;
759}
760
761void fbgl_destroy_keyboard(void)
762{
763 i_fbgl_disable_raw_mode();
764}
765fbgl_key_t fbgl_get_key(void)
766{
767 char c;
768 ssize_t bytes_read = read(STDIN_FILENO, &c, 1);
769
770 if (bytes_read <= 0) {
771 return FBGL_KEY_NONE;
772 }
773
774 // Handle escape sequences for special keys
775 if (c == 27) {
776 char seq[3];
777 if (read(STDIN_FILENO, &seq[0], 1) != 1)
778 return FBGL_KEY_ESCAPE;
779 if (read(STDIN_FILENO, &seq[1], 1) != 1)
780 return FBGL_KEY_ESCAPE;
781
782 if (seq[0] == '[') {
783 switch (seq[1]) {
784 case 'A':
785 return FBGL_KEY_UP;
786 case 'B':
787 return FBGL_KEY_DOWN;
788 case 'C':
789 return FBGL_KEY_RIGHT;
790 case 'D':
791 return FBGL_KEY_LEFT;
792 }
793 }
794
795 return FBGL_KEY_NONE;
796 }
797
798 // Handle direct key presses
799 switch (c) {
800 case 10: // Enter key
801 return FBGL_KEY_ENTER;
802 case 32: // Space key
803 return FBGL_KEY_SPACE;
804 case 'w':
805 case 'W':
806 return FBGL_KEY_UP;
807 case 's':
808 case 'S':
809 return FBGL_KEY_DOWN;
810 case 'a':
811 case 'A':
812 return FBGL_KEY_LEFT;
813 case 'd':
814 case 'D':
815 return FBGL_KEY_RIGHT;
816 case 27: // Escape key
817 return FBGL_KEY_ESCAPE;
818 }
819
820 return FBGL_KEY_NONE;
821}
822
823bool fbgl_is_key_pressed(fbgl_key_t key)
824{
825 // Use select() for non-blocking input check
826 fd_set read_fds;
827 struct timeval timeout;
828
829 FD_ZERO(&read_fds);
830 FD_SET(STDIN_FILENO, &read_fds);
831
832 // Set a very short timeout
833 timeout.tv_sec = 0;
834 timeout.tv_usec = 0;
835
836 // Check if there's input available
837 if (select(STDIN_FILENO + 1, &read_fds, NULL, NULL, &timeout) > 0) {
838 fbgl_key_t pressed_key = fbgl_get_key();
839 return pressed_key == key;
840 }
841
842 return false;
843}
844
845#endif // FBGL_IMPLEMENTATION
846
847#ifdef __cplusplus
848} // extern "C"
849#endif
850#endif // __FBGL_H__
851
852/*
853------------------------------------------------------------------------------
854This software is available under 2 licenses -- choose whichever you prefer.
855------------------------------------------------------------------------------
856ALTERNATIVE A - MIT License
857Copyright (c) 2024 Levent Kaya
858Permission is hereby granted, free of charge, to any person obtaining a copy of
859this software and associated documentation files (the "Software"), to deal in
860the Software without restriction, including without limitation the rights to
861use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
862of the Software, and to permit persons to whom the Software is furnished to do
863so, subject to the following conditions:
864The above copyright notice and this permission notice shall be included in all
865copies or substantial portions of the Software.
866THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
867IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
868FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
869AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
870LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
871OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
872SOFTWARE.
873------------------------------------------------------------------------------
874ALTERNATIVE B - Public Domain (www.unlicense.org)
875This is free and unencumbered software released into the public domain.
876Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
877software, either in source code form or as a compiled binary, for any purpose,
878commercial or non-commercial, and by any means.
879In jurisdictions that recognize copyright laws, the author or authors of this
880software dedicate any and all copyright interest in the software to the public
881domain. We make this dedication for the benefit of the public at large and to
882the detriment of our heirs and successors. We intend this dedication to be an
883overt act of relinquishment in perpetuity of all present and future rights to
884this software under copyright law.
885THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
886IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
887FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
888AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
889ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
890WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
891------------------------------------------------------------------------------
892*/
Definition fbgl.h:100
Definition fbgl.h:68
Definition fbgl.h:79
Definition fbgl.h:73
Definition fbgl.h:60
Definition fbgl.h:50