From 3909773389e797a18e2c09b9ff1339679b045710 Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Sat, 14 Mar 2026 11:53:08 +1100 Subject: [PATCH] Initial commit --- .gitignore | 1 + Makefile | 16 +++++++++ README.md | 28 +++++++++++++++ example.c | 8 +++++ fb.c | 45 +++++++++++++++++++++++ fb.h | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 202 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 example.c create mode 100644 fb.c create mode 100644 fb.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ee9ddd --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +fb.so diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..060e965 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +TARGET=fb.so +PREFIX=/usr/local +SRCS=fb.c +HEADERS=fb.h +FLAGS=-shared + +${TARGET}: ${SRCS} + gcc ${FLAGS} ${SRCS} -o ${TARGET} + +install: ${TARGET} + mkdir -p ${PREFIX}/lib ${PREFIX}/include + cp ${TARGET} ${PREFIX}/lib/lib${TARGET} + cp ${HEADERS} ${PREFIX}/include/ + +clean: + rm ${TARGET} diff --git a/README.md b/README.md new file mode 100644 index 0000000..93e7e26 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# libfb + +Small library for interfacing with the Linux framebuffer. + +Compile and install: +```sh +make +sudo make install +``` + +Usage example: + +```c +#define FB_IMPLEMENTATION // If linking dynamically with the library, omit this line +#include + +int main() { + Framebuffer fb = newFramebuffer("/dev/fb0"); + writeColourToFramebuffer(&fb, (Pixel){100, 100}, (Colour){255, 0, 0, 0}); + destroyFramebuffer(&fb); +} +``` + +Notes: + +* Don't try to write to the framebuffer while in an X.org or Wayland session +* Your user must be in the `input` group before using, run the command `sudo usermod -a -G video $USER` to get permissions. Otherwise, the library will abort. +* This is intended to be a small library to make it easier for anyone to draw things to the screen. Just pixel rendering. diff --git a/example.c b/example.c new file mode 100644 index 0000000..dd5d1ae --- /dev/null +++ b/example.c @@ -0,0 +1,8 @@ +#define FB_IMPLEMENTATION +#include + +int main() { + Framebuffer fb = newFramebuffer("/dev/fb0"); + writeColourToFramebuffer(&fb, (Pixel){100, 100}, (Colour){255, 0, 0, 0}); + destroyFramebuffer(&fb); +} diff --git a/fb.c b/fb.c new file mode 100644 index 0000000..d6122c0 --- /dev/null +++ b/fb.c @@ -0,0 +1,45 @@ +#include "fb.h" + +#include +#include +#include +#include +#include +#include + +Framebuffer newFramebuffer(const char* device) { + Framebuffer fb; + + // Open the framebuffer file descriptor + fb.fd = open(device, O_RDWR); + assert(fb.fd >= 0 && "Invalid framebuffer device, please ensure your user is in the 'video' group."); + + // Get information about framebuffer + ioctl(fb.fd, FBIOGET_VSCREENINFO, &fb.vinfo); + + // Get size of screen for writing pixels in bounds + fb.screensize = fb.vinfo.xres * fb.vinfo.yres * fb.vinfo.bits_per_pixel / 8; + + fb.data = mmap(0, fb.screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb.fd, 0); + + return fb; +} + +void writeColourToFramebuffer(Framebuffer* fb, Pixel pixel, Colour colour) { + int offset = (pixel.y * fb->vinfo.xres + pixel.x) * (fb->vinfo.bits_per_pixel / 8); + + fb->data[offset + 0] = colour.red; + fb->data[offset + 1] = colour.green; + fb->data[offset + 2] = colour.blue; + fb->data[offset + 3] = colour.alpha; + + + munmap(fb->data, fb->screensize); +} + +void destroyFramebuffer(Framebuffer* fb) { + close(fb->fd); + + // NOTE: Do not free fb->data, this is not memory owned by us. + // Will cause a segfault +} diff --git a/fb.h b/fb.h new file mode 100644 index 0000000..87fff0c --- /dev/null +++ b/fb.h @@ -0,0 +1,104 @@ +#include + +// Represents a colour on the screen. +// For each RGB value, accepts a value between 0-255, where +// 0 is all the way off and 255 is all the way on. +typedef struct Colour { + char red; + char green; + char blue; + char alpha; +} Colour; + +// Represents a pixel on the screen. +typedef struct Pixel { + int x; + int y; +} Pixel; + +// Represents the framebuffer that the user has opened. +// Use newFramebuffer() to construct this struct. +// Get x and y size of screen with .vinfo.xres and .vinfo.yres +typedef struct Framebuffer { + int fd; + struct fb_var_screeninfo vinfo; + long int screensize; + char* data; +} Framebuffer; + +// Constructs a Framebuffer struct. +// Asserts whether device is able to be opened. If unable, program is aborted. +Framebuffer newFramebuffer(const char* device); + +// Writes a colour to the screen. +// fb: Pointer to Framebuffer struct (get one with newFramebuffer("/dev/fb0") ) +// pixel: Which pixel on the screen to apply to +// colour: The colour of the pixel +void writeColourToFramebuffer(Framebuffer* fb, Pixel pixel, Colour colour); + +// Destroys a framebuffer. +void destroyFramebuffer(Framebuffer* fb); + +// Example library usage (puts a red pixel at 100, 100) + +/* + +int main() { + + Framebuffer fb = newFramebuffer("/dev/fb0"); + + writeColourToFramebuffer(&fb, (Pixel){100, 100}, (Colour){0, 0, 255, 0}); + + destroyFramebuffer(&fb); + return 0; +} + +*/ + +// To use as single-header library, define FB_IMPLEMENTATION before including this header. +#ifdef FB_IMPLEMENTATION + +#include +#include +#include +#include +#include +#include + +Framebuffer newFramebuffer(const char* device) { + Framebuffer fb; + + // Open the framebuffer file descriptor + fb.fd = open(device, O_RDWR); + assert(fb.fd >= 0 && "Invalid framebuffer device, please ensure your user is in the 'video' group."); + + // Get information about framebuffer + ioctl(fb.fd, FBIOGET_VSCREENINFO, &fb.vinfo); + + // Get size of screen for writing pixels in bounds + fb.screensize = fb.vinfo.xres * fb.vinfo.yres * fb.vinfo.bits_per_pixel / 8; + + fb.data = mmap(0, fb.screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb.fd, 0); + + return fb; +} + +void writeColourToFramebuffer(Framebuffer* fb, Pixel pixel, Colour colour) { + int offset = (pixel.y * fb->vinfo.xres + pixel.x) * (fb->vinfo.bits_per_pixel / 8); + + fb->data[offset + 0] = colour.red; + fb->data[offset + 1] = colour.green; + fb->data[offset + 2] = colour.blue; + fb->data[offset + 3] = colour.alpha; + + + munmap(fb->data, fb->screensize); +} + +void destroyFramebuffer(Framebuffer* fb) { + close(fb->fd); + + // NOTE: Do not free fb->data, this is not memory owned by us. + // Will cause a segfault +} +#endif