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