summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLevent Kaya <levent@dev>2025-11-22 21:35:32 +0300
committerLevent Kaya <levent@dev>2025-11-22 21:35:32 +0300
commit479c3a301c73f12ccab54a45d86ebb20be600663 (patch)
tree4753b270b5df5daa6c77c5e64978a97ee448b071
parent9fe39a2c099f8c4c4f0782716142404e6aad6080 (diff)
[feature] an emulator for viewing fbgl programs
-rw-r--r--docs/emu.pngbin0 -> 254913 bytes
-rw-r--r--emu/.gitignore2
-rw-r--r--emu/Makefile51
-rw-r--r--emu/README.md233
-rw-r--r--emu/emu_demo.c26
-rw-r--r--emu/fbgl_preload.c331
-rw-r--r--emu/fbgl_preload.h41
-rwxr-xr-xemu/fbgl_viewerbin0 -> 17160 bytes
-rw-r--r--emu/fbgl_viewer.c131
-rwxr-xr-xemu/libfbgl_preload.sobin0 -> 16520 bytes
10 files changed, 540 insertions, 275 deletions
diff --git a/docs/emu.png b/docs/emu.png
new file mode 100644
index 0000000..d9a0c03
--- /dev/null
+++ b/docs/emu.png
Binary files differ
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
new file mode 100755
index 0000000..622130b
--- /dev/null
+++ b/emu/fbgl_viewer
Binary files differ
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
new file mode 100755
index 0000000..cdd3bba
--- /dev/null
+++ b/emu/libfbgl_preload.so
Binary files differ