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