diff options
| author | Levent Kaya <levent@dev> | 2025-11-22 21:35:32 +0300 |
|---|---|---|
| committer | Levent Kaya <levent@dev> | 2025-11-22 21:35:32 +0300 |
| commit | 479c3a301c73f12ccab54a45d86ebb20be600663 (patch) | |
| tree | 4753b270b5df5daa6c77c5e64978a97ee448b071 | |
| parent | 9fe39a2c099f8c4c4f0782716142404e6aad6080 (diff) | |
[feature] an emulator for viewing fbgl programs
| -rw-r--r-- | docs/emu.png | bin | 0 -> 254913 bytes | |||
| -rw-r--r-- | emu/.gitignore | 2 | ||||
| -rw-r--r-- | emu/Makefile | 51 | ||||
| -rw-r--r-- | emu/README.md | 233 | ||||
| -rw-r--r-- | emu/emu_demo.c | 26 | ||||
| -rw-r--r-- | emu/fbgl_preload.c | 331 | ||||
| -rw-r--r-- | emu/fbgl_preload.h | 41 | ||||
| -rwxr-xr-x | emu/fbgl_viewer | bin | 0 -> 17160 bytes | |||
| -rw-r--r-- | emu/fbgl_viewer.c | 131 | ||||
| -rwxr-xr-x | emu/libfbgl_preload.so | bin | 0 -> 16520 bytes |
10 files changed, 540 insertions, 275 deletions
diff --git a/docs/emu.png b/docs/emu.png Binary files differnew file mode 100644 index 0000000..d9a0c03 --- /dev/null +++ b/docs/emu.png diff --git a/emu/.gitignore b/emu/.gitignore deleted file mode 100644 index cfd5574..0000000 --- a/emu/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -fbgl_viewer -libfbgl_preload.so diff --git a/emu/Makefile b/emu/Makefile index 339b85e..14b9465 100644 --- a/emu/Makefile +++ b/emu/Makefile @@ -1,47 +1,24 @@ CC = gcc CFLAGS = -Wall -Wextra -O2 -LDFLAGS = -lSDL2 -ldl -lm +LDFLAGS = -ldl +SDL_FLAGS = $(shell pkg-config --cflags --libs sdl2) -.PHONY: all clean test +all: libfbgl_preload.so fbgl_viewer -all: libfbgl_preload.so fbgl_viewer fbgl_run.sh - -libfbgl_preload.so: fbgl_preload.c - @echo "Building preload library..." - $(CC) -shared -fPIC $(CFLAGS) $< -ldl -o $@ +libfbgl_preload.so: fbgl_preload.c fbgl_preload.h + $(CC) -shared -fPIC $(CFLAGS) -o $@ fbgl_preload.c $(LDFLAGS) fbgl_viewer: fbgl_viewer.c - @echo "Building SDL viewer..." - $(CC) $(CFLAGS) $< $(LDFLAGS) -o $@ - -fbgl_run.sh: - @echo "Making runner script executable..." - @chmod +x fbgl_run.sh - -test: all test_fbgl - @echo "Running test program..." - ./fbgl_run.sh ./test_fbgl - -test_fbgl: test_fbgl.c fbgl.h - @echo "Compiling test program..." - $(CC) $(CFLAGS) test_fbgl.c -lm -o test_fbgl + $(CC) $(CFLAGS) -o $@ $< $(SDL_FLAGS) clean: - @echo "Cleaning up..." - rm -f libfbgl_preload.so fbgl_viewer test_fbgl - @# Clean up shared memory - @ipcrm -M $$(ftok /tmp F 2>/dev/null | awk '{print $$NF}') 2>/dev/null || true + rm -f libfbgl_preload.so fbgl_viewer -install: - @echo "Installing to /usr/local/bin..." - sudo cp libfbgl_preload.so /usr/local/lib/ - sudo cp fbgl_viewer /usr/local/bin/ - sudo cp fbgl_run.sh /usr/local/bin/fbgl-run - @echo "Done! You can now run: fbgl-run ./your_program" +install: all + @echo "Built successfully!" + @echo "" + @echo "Usage:" + @echo " 1. Terminal 1: ./fbgl_viewer" + @echo " 2. Terminal 2: LD_PRELOAD=./libfbgl_preload.so ./your_program" -uninstall: - @echo "Uninstalling..." - sudo rm -f /usr/local/lib/libfbgl_preload.so - sudo rm -f /usr/local/bin/fbgl_viewer - sudo rm -f /usr/local/bin/fbgl-run - @echo "Done!" +.PHONY: all clean install diff --git a/emu/README.md b/emu/README.md index 5033baf..4d0e013 100644 --- a/emu/README.md +++ b/emu/README.md @@ -1,230 +1,57 @@ -# FBGL Virtual Framebuffer Emulator +# FBGL Framebuffer Emulator -Test your FBGL programs without actual framebuffer hardware using SDL2! +Professional LD_PRELOAD-based framebuffer emulator with clean architecture. -## Quick Start +## Structure -```bash -# 1. Install dependencies -sudo apt-get install libsdl2-dev build-essential - -# 2. Build the emulator -make - -# 3. Run your FBGL program -./fbgl_run.sh ./your_fbgl_program -``` - -## Components +- `fbgl_preload.h` - Public header with configuration and types +- `fbgl_preload.c` - Implementation with hook functions +- `sdl_viewer.c` - SDL-based viewer application -1. **libfbgl_preload.so** - LD_PRELOAD library that intercepts framebuffer calls -2. **fbgl_viewer** - SDL2 window that displays the virtual framebuffer -3. **fbgl_run.sh** - Convenient wrapper script to run everything +## Features -## Installation +- Clean struct-based state management +- Proper header/source separation +- Statistics tracking +- Error handling +- FPS counter in viewer -### Option 1: Using Makefile +## Building ```bash -# Download/create these files in the same directory: -# - fbgl_preload.c -# - fbgl_viewer.c -# - fbgl_run.sh -# - Makefile (below) - -make all +make ``` -### Option 2: Manual compilation - +Or manually: ```bash -# Compile the preload library -gcc -shared -fPIC fbgl_preload.c -ldl -o libfbgl_preload.so - -# Compile the viewer -gcc fbgl_viewer.c -lSDL2 -o fbgl_viewer - -# Make runner script executable -chmod +x fbgl_run.sh +gcc -shared -fPIC -o libfbgl_preload.so fbgl_preload.c -ldl +gcc -o sdl_viewer sdl_viewer.c $(pkg-config --cflags --libs sdl2) ``` ## Usage -### Basic Usage +Terminal 2: ```bash -./fbgl_run.sh ./my_fbgl_app +LD_PRELOAD=./libfbgl_preload.so ./your_fbgl_program ``` -### With Arguments - +Terminal 1: ```bash -./fbgl_run.sh ./my_fbgl_app --arg1 value1 --arg2 value2 -``` - -### Manual Usage (without script) - -```bash -# Terminal 1: Start the viewer -./fbgl_viewer & - -# Terminal 2: Run your program with LD_PRELOAD -LD_PRELOAD=./libfbgl_preload.so ./my_fbgl_app -``` - -## Example Test Program - -Create a simple test program (`test_fbgl.c`): - -```c -#define FBGL_IMPLEMENTATION -#include "fbgl.h" -#include <unistd.h> - -int main() { - fbgl_t fb; - - if (fbgl_init(NULL, &fb) < 0) { - return 1; - } - - // Draw some shapes - for (int i = 0; i < 100; i++) { - fbgl_set_bg(&fb, FBGL_RGB(0, 0, 0)); - - // Draw moving circle - int x = 400 + (int)(200 * cos(i * 0.1)); - int y = 300 + (int)(200 * sin(i * 0.1)); - fbgl_draw_circle_filled(x, y, 50, FBGL_RGB(255, 0, 0), &fb); - - // Draw rectangle - fbgl_point_t tl = {100, 100}; - fbgl_point_t br = {300, 300}; - fbgl_draw_rectangle_outline(tl, br, FBGL_RGB(0, 255, 0), &fb); - - usleep(16666); // ~60 FPS - } - - fbgl_destroy(&fb); - return 0; -} -``` - -Compile and run: - -```bash -gcc test_fbgl.c -o test_fbgl -lm -./fbgl_run.sh ./test_fbgl -``` - -## Makefile - -Create a file named `Makefile`: - -```makefile -CC = gcc -CFLAGS = -Wall -Wextra -O2 -LDFLAGS = -lSDL2 -ldl -lm - -.PHONY: all clean test - -all: libfbgl_preload.so fbgl_viewer fbgl_run.sh - -libfbgl_preload.so: fbgl_preload.c - @echo "Building preload library..." - $(CC) -shared -fPIC $(CFLAGS) $< -ldl -o $@ - -fbgl_viewer: fbgl_viewer.c - @echo "Building SDL viewer..." - $(CC) $(CFLAGS) $< $(LDFLAGS) -o $@ - -fbgl_run.sh: - @echo "Making runner script executable..." - @chmod +x fbgl_run.sh - -test: all test_fbgl - @echo "Running test program..." - ./fbgl_run.sh ./test_fbgl - -test_fbgl: test_fbgl.c fbgl.h - @echo "Compiling test program..." - $(CC) $(CFLAGS) test_fbgl.c -lm -o test_fbgl - -clean: - @echo "Cleaning up..." - rm -f libfbgl_preload.so fbgl_viewer test_fbgl - @# Clean up shared memory - @ipcrm -M $$(ftok /tmp F 2>/dev/null | awk '{print $$NF}') 2>/dev/null || true - -install: - @echo "Installing to /usr/local/bin..." - sudo cp libfbgl_preload.so /usr/local/lib/ - sudo cp fbgl_viewer /usr/local/bin/ - sudo cp fbgl_run.sh /usr/local/bin/fbgl-run - @echo "Done! You can now run: fbgl-run ./your_program" - -uninstall: - @echo "Uninstalling..." - sudo rm -f /usr/local/lib/libfbgl_preload.so - sudo rm -f /usr/local/bin/fbgl_viewer - sudo rm -f /usr/local/bin/fbgl-run - @echo "Done!" +./fbgl_viewer ``` -## Troubleshooting -### "Shared memory not found" error - -The viewer needs to be started before the FBGL program. Use the `fbgl_run.sh` script which handles this automatically. - -### Black screen - -Make sure your FBGL program is actually drawing to the framebuffer and not exiting immediately. - -### Viewer doesn't start - -Check that SDL2 is installed: -```bash -sudo apt-get install libsdl2-dev -``` +## Architecture -### Program crashes - -Check stderr output for preload messages. Make sure your program is compiled with `-lm` for math functions. +1. Hook library intercepts framebuffer syscalls +2. Redirects to shared memory segment +3. SDL viewer displays shared memory contents +4. Program thinks it's using real /dev/fb0 ## Configuration -Edit the following constants in the source files to change resolution: - -In `fbgl_preload.c` and `fbgl_viewer.c`: -```c -#define VIRTUAL_FB_WIDTH 800 -#define VIRTUAL_FB_HEIGHT 600 -``` - -Then recompile: -```bash -make clean && make -``` - -## How It Works - -1. **LD_PRELOAD** intercepts system calls (`open`, `ioctl`, `mmap`, etc.) that your FBGL program makes -2. Instead of real framebuffer device, it provides a shared memory buffer -3. The SDL viewer reads from this shared memory and displays it in a window -4. Your FBGL program thinks it's writing to `/dev/fb0`, but it's actually writing to shared memory - -## Performance - -The emulator adds minimal overhead: -- Shared memory for zero-copy data transfer -- VSync enabled in SDL for smooth rendering -- ~60 FPS typical performance - -## License - -This emulator is public domain. Use it however you want! - -## Credits - -Created for testing FBGL applications without hardware framebuffer access. +Edit `fbgl_preload.h` to change: +- Screen resolution (FBGL_WIDTH, FBGL_HEIGHT) +- Color depth (FBGL_BPP) +- Shared memory key (FBGL_SHM_KEY) diff --git a/emu/emu_demo.c b/emu/emu_demo.c new file mode 100644 index 0000000..a003d6a --- /dev/null +++ b/emu/emu_demo.c @@ -0,0 +1,26 @@ +#define FBGL_IMPLEMENTATION +#include "../fbgl.h" + +int main() +{ + fbgl_t buffer; + if (fbgl_init(NULL, &buffer) == -1) { + fprintf(stdout, "Error: could not open framebuffer device\n"); + return -1; + } + + fbgl_set_bg(&buffer, 0x00FF0000); + + fbgl_point_t start = { 0, 0 }; + fbgl_point_t end = { 799, 599 }; + + for (int i = 0; i < buffer.vinfo.xres; i++) { + start.x = i; + fbgl_draw_line(start, end, 0xFFFFFF, &buffer); + nanosleep((struct timespec[]){ { 0, 10000000 } }, NULL); + } + + sleep(2); + + return 0; +} diff --git a/emu/fbgl_preload.c b/emu/fbgl_preload.c index e8771e2..485f6a6 100644 --- a/emu/fbgl_preload.c +++ b/emu/fbgl_preload.c @@ -1,9 +1,336 @@ #define _GNU_SOURCE +#include "fbgl_preload.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <dlfcn.h> #include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <linux/fb.h> +#include <sys/stat.h> +#include <errno.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/shm.h> +#include <stdarg.h> + +// ============================================================================ +// Global State +// ============================================================================ + +// Framebuffer state (internal to this file) +static fbgl_state_t g_fbgl_state = { + .virtual_fd = -1, + .shared_mem = NULL, + .shm_id = -1, + .is_initialized = 0, + .total_writes = 0, + .bytes_written = 0 +}; + +// Original function pointers (internal to this file) +static fbgl_original_funcs_t g_original = { + .open = NULL, + .close = NULL, + .ioctl = NULL, + .mmap = NULL, + .munmap = NULL, + .write = NULL, + .read = NULL +}; + +// ============================================================================ +// Internal Helper Functions +// ============================================================================ + +// Initialize original function pointers +static void fbgl_init_hooks(void) { + if (!g_original.open) { + g_original.open = dlsym(RTLD_NEXT, "open"); + g_original.close = dlsym(RTLD_NEXT, "close"); + g_original.ioctl = dlsym(RTLD_NEXT, "ioctl"); + g_original.mmap = dlsym(RTLD_NEXT, "mmap"); + g_original.munmap = dlsym(RTLD_NEXT, "munmap"); + g_original.write = dlsym(RTLD_NEXT, "write"); + g_original.read = dlsym(RTLD_NEXT, "read"); + + if (!g_original.open || !g_original.close || !g_original.ioctl || + !g_original.mmap || !g_original.munmap || !g_original.write) { + fprintf(stderr, "[FBGL] ERROR: Failed to load original functions\n"); + } + } +} + +// Initialize shared memory for framebuffer emulation +static int fbgl_init_shared_memory(void) { + if (g_fbgl_state.is_initialized) { + return 0; // Already initialized + } + + fprintf(stderr, "[FBGL] Attempting to create shared memory: %zu bytes\n", (size_t)FBGL_SIZE); + + // First, try to remove any existing segment with this key + int old_id = shmget(FBGL_SHM_KEY, 0, 0); + if (old_id >= 0) { + fprintf(stderr, "[FBGL] Removing old shared memory segment (ID: %d)\n", old_id); + shmctl(old_id, IPC_RMID, NULL); + } + + // Create shared memory segment (no extra space needed, shmat returns aligned memory) + g_fbgl_state.shm_id = shmget(FBGL_SHM_KEY, FBGL_SIZE, IPC_CREAT | IPC_EXCL | 0666); + if (g_fbgl_state.shm_id < 0) { + fprintf(stderr, "[FBGL] ERROR: Failed to create shared memory: %s (errno=%d)\n", + strerror(errno), errno); + fprintf(stderr, "[FBGL] Requested size: %zu bytes (%.2f MB)\n", + (size_t)FBGL_SIZE, FBGL_SIZE / (1024.0 * 1024.0)); + + // Try to get system limits + struct shminfo shm_info; + if (shmctl(0, IPC_INFO, (struct shmid_ds *)&shm_info) >= 0) { + fprintf(stderr, "[FBGL] System max shared memory: %lu bytes (%.2f MB)\n", + shm_info.shmmax, shm_info.shmmax / (1024.0 * 1024.0)); + } + return -1; + } + + // Attach shared memory + g_fbgl_state.shared_mem = shmat(g_fbgl_state.shm_id, NULL, 0); + if (g_fbgl_state.shared_mem == (void*)-1) { + fprintf(stderr, "[FBGL] ERROR: Failed to attach shared memory: %s\n", + strerror(errno)); + shmctl(g_fbgl_state.shm_id, IPC_RMID, NULL); + g_fbgl_state.shared_mem = NULL; + return -1; + } + + fprintf(stderr, "[FBGL] Shared memory attached at: %p\n", g_fbgl_state.shared_mem); + + // Clear the framebuffer to black + memset(g_fbgl_state.shared_mem, 0, FBGL_SIZE); + + g_fbgl_state.is_initialized = 1; + + fprintf(stderr, "[FBGL] Initialized: %dx%d @ %d bpp, SHM ID: %d, Size: %zu bytes\n", + FBGL_WIDTH, FBGL_HEIGHT, FBGL_BPP, g_fbgl_state.shm_id, (size_t)FBGL_SIZE); + + return 0; +} + +// Check if a path is a framebuffer device +static int fbgl_is_fb_device(const char* pathname) { + if (!pathname) return 0; + + return (strcmp(pathname, "/dev/fb0") == 0 || + strcmp(pathname, "/dev/fb") == 0 || + strncmp(pathname, "/dev/fb", 7) == 0); +} + +// Check if a file descriptor is our virtual framebuffer +static int fbgl_is_virtual_fd(int fd) { + return (fd == g_fbgl_state.virtual_fd && fd != -1); +} + +// Fill fb_var_screeninfo structure +static void fbgl_fill_var_screeninfo(struct fb_var_screeninfo* vinfo) { + memset(vinfo, 0, sizeof(*vinfo)); + + vinfo->xres = FBGL_WIDTH; + vinfo->yres = FBGL_HEIGHT; + vinfo->xres_virtual = FBGL_WIDTH; + vinfo->yres_virtual = FBGL_HEIGHT; + vinfo->bits_per_pixel = FBGL_BPP; + + // ARGB8888 format + vinfo->red.offset = 16; + vinfo->red.length = 8; + vinfo->green.offset = 8; + vinfo->green.length = 8; + vinfo->blue.offset = 0; + vinfo->blue.length = 8; + vinfo->transp.offset = 24; + vinfo->transp.length = 8; +} + +// Fill fb_fix_screeninfo structure +static void fbgl_fill_fix_screeninfo(struct fb_fix_screeninfo* finfo) { + memset(finfo, 0, sizeof(*finfo)); + + strncpy(finfo->id, "FBGL_EMU", sizeof(finfo->id) - 1); + finfo->smem_len = FBGL_SIZE; + finfo->type = FB_TYPE_PACKED_PIXELS; + finfo->visual = FB_VISUAL_TRUECOLOR; + finfo->line_length = FBGL_PITCH; +} -int open(const char *pathname, int flags); +// ============================================================================ +// Hooked Functions (Public - must NOT be static!) +// ============================================================================ -int open(const char *pathname, int flags) { +int open(const char* pathname, int flags, ...) { + fbgl_init_hooks(); + + // Check if opening framebuffer device + if (fbgl_is_fb_device(pathname)) { + if (fbgl_init_shared_memory() < 0) { + errno = EIO; + return -1; + } + + g_fbgl_state.virtual_fd = FBGL_VIRTUAL_FD; + fprintf(stderr, "[FBGL] open(\"%s\") -> virtual fd %d\n", + pathname, g_fbgl_state.virtual_fd); + return g_fbgl_state.virtual_fd; + } + + // Pass through to original open + mode_t mode = 0; + if (flags & O_CREAT) { + va_list args; + va_start(args, flags); + mode = va_arg(args, mode_t); + va_end(args); + return g_original.open(pathname, flags, mode); + } + return g_original.open(pathname, flags); +} + +int close(int fd) { + fbgl_init_hooks(); + + if (fbgl_is_virtual_fd(fd)) { + fprintf(stderr, "[FBGL] close(fb) - keeping shared memory alive\n"); + g_fbgl_state.virtual_fd = -1; + return 0; + } + + return g_original.close(fd); +} + +int ioctl(int fd, unsigned long request, ...) { + fbgl_init_hooks(); + + if (fbgl_is_virtual_fd(fd)) { + va_list args; + va_start(args, request); + void* argp = va_arg(args, void*); + va_end(args); + + fprintf(stderr, "[FBGL] ioctl(fb, 0x%lx)\n", request); + + switch (request) { + case FBIOGET_VSCREENINFO: + fbgl_fill_var_screeninfo((struct fb_var_screeninfo*)argp); + return 0; + + case FBIOGET_FSCREENINFO: + fbgl_fill_fix_screeninfo((struct fb_fix_screeninfo*)argp); + return 0; + + case FBIOPUT_VSCREENINFO: + // Ignore changes to screen info + fprintf(stderr, "[FBGL] ioctl: FBIOPUT_VSCREENINFO ignored\n"); + return 0; + + default: + fprintf(stderr, "[FBGL] ioctl: Unknown request 0x%lx\n", request); + return 0; + } + } + + // Pass through to original ioctl + va_list args; + va_start(args, request); + void* argp = va_arg(args, void*); + va_end(args); + return g_original.ioctl(fd, request, argp); +} + +void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) { + fbgl_init_hooks(); + + if (fbgl_is_virtual_fd(fd)) { + if (!g_fbgl_state.shared_mem) { + fprintf(stderr, "[FBGL] ERROR: mmap called but shared memory not initialized\n"); + errno = ENOMEM; + return MAP_FAILED; + } + + fprintf(stderr, "[FBGL] mmap(fb, %zu bytes, offset=%ld) -> %p\n", + length, offset, g_fbgl_state.shared_mem); + return g_fbgl_state.shared_mem; + } + + return g_original.mmap(addr, length, prot, flags, fd, offset); +} + +int munmap(void* addr, size_t length) { + fbgl_init_hooks(); + + if (addr == g_fbgl_state.shared_mem) { + fprintf(stderr, "[FBGL] munmap(fb) - keeping shared memory attached\n"); + return 0; + } + + return g_original.munmap(addr, length); +} + +ssize_t write(int fd, const void* buf, size_t count) { + fbgl_init_hooks(); + + if (fbgl_is_virtual_fd(fd) && g_fbgl_state.shared_mem && buf) { + size_t to_write = (count < FBGL_SIZE) ? count : FBGL_SIZE; + memcpy(g_fbgl_state.shared_mem, buf, to_write); + + g_fbgl_state.total_writes++; + g_fbgl_state.bytes_written += to_write; + + fprintf(stderr, "[FBGL] write(fb, %zu bytes) - total writes: %zu\n", + to_write, g_fbgl_state.total_writes); + return to_write; + } + + return g_original.write(fd, buf, count); +} + +ssize_t read(int fd, void* buf, size_t count) { + fbgl_init_hooks(); + + if (fbgl_is_virtual_fd(fd) && g_fbgl_state.shared_mem && buf) { + size_t to_read = (count < FBGL_SIZE) ? count : FBGL_SIZE; + memcpy(buf, g_fbgl_state.shared_mem, to_read); + return to_read; + } + + return g_original.read(fd, buf, count); +} + +// ============================================================================ +// Constructor/Destructor +// ============================================================================ + +__attribute__((constructor)) +static void fbgl_constructor(void) { + fprintf(stderr, "========================================\n"); + fprintf(stderr, "FBGL Framebuffer Emulator v1.0\n"); + fprintf(stderr, "Configuration: %dx%d @ %d bpp\n", + FBGL_WIDTH, FBGL_HEIGHT, FBGL_BPP); + fprintf(stderr, "Shared Memory Key: 0x%08X\n", FBGL_SHM_KEY); + fprintf(stderr, "========================================\n"); +} +__attribute__((destructor)) +static void fbgl_destructor(void) { + fprintf(stderr, "\n========================================\n"); + fprintf(stderr, "FBGL Statistics:\n"); + fprintf(stderr, " Total writes: %zu\n", g_fbgl_state.total_writes); + fprintf(stderr, " Bytes written: %zu\n", g_fbgl_state.bytes_written); + fprintf(stderr, "========================================\n"); + + if (g_fbgl_state.shared_mem && g_fbgl_state.shared_mem != (void*)-1) { + shmdt(g_fbgl_state.shared_mem); + g_fbgl_state.shared_mem = NULL; + } } diff --git a/emu/fbgl_preload.h b/emu/fbgl_preload.h new file mode 100644 index 0000000..e736943 --- /dev/null +++ b/emu/fbgl_preload.h @@ -0,0 +1,41 @@ +#ifndef FBGL_PRELOAD_H +#define FBGL_PRELOAD_H + +#include <stddef.h> +#include <sys/types.h> + +// Framebuffer configuration +#define FBGL_SHM_KEY 0x1234FBCD +#define FBGL_WIDTH 800 +#define FBGL_HEIGHT 600 +#define FBGL_BPP 32 +#define FBGL_BYTES_PP (FBGL_BPP / 8) +#define FBGL_PITCH (FBGL_WIDTH * FBGL_BYTES_PP) +#define FBGL_SIZE (FBGL_PITCH * FBGL_HEIGHT) + +// Virtual framebuffer file descriptor magic number +#define FBGL_VIRTUAL_FD 1000 + +// Framebuffer state structure +typedef struct { + int virtual_fd; // Virtual file descriptor for /dev/fb0 + void* shared_mem; // Pointer to shared memory + int shm_id; // Shared memory ID + int is_initialized; // Initialization flag + size_t total_writes; // Statistics: number of writes + size_t bytes_written; // Statistics: total bytes written +} fbgl_state_t; + +// Original function pointers structure +typedef struct { + int (*open)(const char*, int, ...); + int (*close)(int); + int (*ioctl)(int, unsigned long, ...); + void* (*mmap)(void*, size_t, int, int, int, off_t); + int (*munmap)(void*, size_t); + ssize_t (*write)(int, const void*, size_t); + ssize_t (*read)(int, void*, size_t); +} fbgl_original_funcs_t; + + +#endif // FBGL_PRELOAD_H diff --git a/emu/fbgl_viewer b/emu/fbgl_viewer Binary files differnew file mode 100755 index 0000000..622130b --- /dev/null +++ b/emu/fbgl_viewer diff --git a/emu/fbgl_viewer.c b/emu/fbgl_viewer.c index c6e077d..4e31f6d 100644 --- a/emu/fbgl_viewer.c +++ b/emu/fbgl_viewer.c @@ -1,35 +1,104 @@ -#include <stdio.h> #include <SDL2/SDL.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> -#define SCREEN_WIDTH 800 -#define SCREEN_HEIGHT 600 -#define SCREEN_TITLE "FBGL Viewer" - -int main() -{ - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - printf("Couldn't initialize SDL: %s\n", SDL_GetError()); - return EXIT_FAILURE; - } - - SDL_Window *window = SDL_CreateWindow(SCREEN_TITLE, - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - SCREEN_WIDTH, SCREEN_HEIGHT, 0); - if (!window) { - printf("Failed to open %d x %d window: %s\n", SCREEN_WIDTH, - SCREEN_HEIGHT, SDL_GetError()); - return EXIT_FAILURE; - } - - SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0); - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 250); - SDL_RenderClear(renderer); - SDL_RenderPresent(renderer); - SDL_Delay(2000); - SDL_DestroyWindow(window); - SDL_DestroyRenderer(renderer); - SDL_Quit(); +#define FBGL_SHM_KEY 0x1234FBCD +#define FBGL_WIDTH 800 +#define FBGL_HEIGHT 600 +#define FBGL_SIZE (FBGL_WIDTH * FBGL_HEIGHT * 4) - return 0; +int main(void) { + printf("FBGL Viewer - Connecting to shared memory...\n"); + + // Attach to shared memory + int shm_id = shmget(FBGL_SHM_KEY, FBGL_SIZE, 0666); + if (shm_id < 0) { + fprintf(stderr, "Error: Cannot access shared memory (key: 0x%08X)\n", FBGL_SHM_KEY); + fprintf(stderr, "Make sure the FBGL program is running first!\n"); + return 1; + } + + void* shared_mem = shmat(shm_id, NULL, SHM_RDONLY); + if (shared_mem == (void*)-1) { + fprintf(stderr, "Error: Cannot attach to shared memory\n"); + return 1; + } + + printf("Connected to shared memory (SHM ID: %d)\n", shm_id); + + // Initialize SDL + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "SDL Init failed: %s\n", SDL_GetError()); + shmdt(shared_mem); + return 1; + } + + SDL_Window* window = SDL_CreateWindow("FBGL Viewer", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + FBGL_WIDTH, FBGL_HEIGHT, SDL_WINDOW_SHOWN); + + if (!window) { + fprintf(stderr, "Window creation failed: %s\n", SDL_GetError()); + SDL_Quit(); + shmdt(shared_mem); + return 1; + } + + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, + SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + + SDL_Texture* texture = SDL_CreateTexture(renderer, + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, + FBGL_WIDTH, FBGL_HEIGHT); + + printf("FBGL Viewer running at %dx%d\n", FBGL_WIDTH, FBGL_HEIGHT); + printf("Press ESC or close window to exit.\n"); + + int running = 1; + SDL_Event event; + Uint32 frame_count = 0; + Uint32 last_time = SDL_GetTicks(); + + while (running) { + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT || + (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE)) { + running = 0; + } + } + + // Update texture with framebuffer data + SDL_UpdateTexture(texture, NULL, shared_mem, FBGL_WIDTH * 4); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + + // Calculate FPS every second + frame_count++; + Uint32 current_time = SDL_GetTicks(); + if (current_time - last_time >= 1000) { + char title[256]; + snprintf(title, sizeof(title), "FBGL Viewer - %u FPS", frame_count); + SDL_SetWindowTitle(window, title); + frame_count = 0; + last_time = current_time; + } + + SDL_Delay(16); // ~60 FPS + } + + printf("Shutting down...\n"); + + SDL_DestroyTexture(texture); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + + shmdt(shared_mem); + + return 0; } diff --git a/emu/libfbgl_preload.so b/emu/libfbgl_preload.so Binary files differnew file mode 100755 index 0000000..cdd3bba --- /dev/null +++ b/emu/libfbgl_preload.so |
