fbsim

framebuffer trivial "simulator"
git clone git://deadbeef.fr/fbsim.git
Log | Files | Refs | README

commit 6bb41b5732c02dc61ae0f928902e5c1aa84b705d
Author: Morel Bérenger <berengermorel76@gmail.com>
Date:   Fri, 18 Sep 2020 15:15:23 +0200

initial commit

Diffstat:
AREADME | 47+++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main.c | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 289 insertions(+), 0 deletions(-)

diff --git a/README b/README @@ -0,0 +1,47 @@ +Description: + +This program only spawns a window and mmap it's content to +a file, so that any other process can draw into that window. + +The initial purpose was to simulate a very, very simple to +use framebuffer "emulator". There is no security built at +all, even if errors should be handled correctly. + +* Users have to handle endianness themselves. +* If multiple processes access the buffer at the same time, + the only guarantee you have is to end with a mess. +* Users must not attempt to read outside of the buffer's + memory. +* Only 32 bpp have actually been tested, 8 and 16 do not + even theoretically work, because I'm lazy. +* The program exits only when receiving a signal that, by + default, imply termination. + +Usage: +./fbsim BUFFER WIDTH HEIGHT BPP REFRESHRATE + +BUFFER: path to a file which will be used as buffer. This + file will be automatically deleted in most cases, but not + if there is a faulty access to memory. +WIDTH: the number of pixels per line. +HEIGHT: the number of pixels per column. +BPP: how many bits are needed to draw a pixel. +REFRESHRATE: how many times per second the image should be + updated. + +Compiling: + +This program requires a POSIX 2008 (probably) compatible +libc and SDL 1.2. +It should be compatible with any compiler supporting C99 or +more recent, and compile with almost no warnings. + +Known warnings: +* implicit-fallthrough (gcc) +* missing-noreturn (clang) +* padded (clang) + +Examples of build line: + +gcc: gcc -Wall -Wextra src/main.c -lSDL -o fbsim +clang: clang -Wall -Weverything src/main.c -lSDL -o fbsim diff --git a/src/main.c b/src/main.c @@ -0,0 +1,242 @@ +/*********************************************************** +Copyright 2020 Morel Bérenger + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to +whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall +be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +***********************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> + +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <time.h> + +#include <SDL/SDL.h> + +void usage_and_quit( int argc, char** argv ); +void shmem_deinit( void ); +uint16_t get_u16( int argc, char** argv, char const* const str, char const* const label ); + +struct shared_mem_t +{ + void* mem; + size_t size; + char const* path; + int fd; +}; + +static struct shared_mem_t fbsim; + +void usage_and_quit( int argc, char** argv ) +{ + fprintf( stderr, + "Usage: %s " + "UNIX_SOCKET (%s) " + "X_RESOLUTION (%s) " + "Y_RESOLUTION (%s) " + "BITS_PER_PIXEl (%s) " + "REFRESH_RATE (In hertz) (%s) " + "\n" + , argv[0] + , argc < 1 ? "NEEDED" : argv[1] + , argc < 2 ? "NEEDED" : argv[2] + , argc < 3 ? "NEEDED" : argv[3] + , argc < 4 ? "NEEDED" : argv[4] + , argc < 5 ? "NEEDED" : argv[5] + ); + exit( EXIT_FAILURE ); +} + +void shmem_deinit( void ) +{ + if( MAP_FAILED != fbsim.mem && 0 != fbsim.size ) + { + if( -1 == munmap( fbsim.mem, fbsim.size ) ) + { + fprintf( stderr, "munmap( %p, %lu ): %s (%d)\n", + fbsim.mem, fbsim.size, + strerror( errno ), errno ); + } + } + close( fbsim.fd ); + if( -1 == unlink( fbsim.path ) ) + { + fprintf( stderr, "unlink( %s ): %s (%d)\n", + fbsim.path, strerror( errno ), errno ); + } +} + +uint16_t get_u16( int argc, char** argv, char const* const str, char const* const label ) +{ + unsigned long tmp_ul; + char *tmp_ptr; + tmp_ul = strtoul( str, &tmp_ptr, 10 ); + if( errno ) + { + fprintf( stderr, "%s error: %s (%d)\n", + label, strerror( errno ), errno ); + usage_and_quit( argc, argv ); + } + if( tmp_ul > UINT16_MAX || *tmp_ptr ) + { + fprintf( stderr, "%s error: invalid value\n", + label ); + usage_and_quit( argc, argv ); + } + return (uint16_t) tmp_ul; +} + +int main(int argc, char *argv[]) +{ + uint16_t xres, yres, bpp, freq; + char *file_name; + SDL_Surface* screen; + + if( argc < 6 ) + { + usage_and_quit( argc, argv ); + } + + file_name = argv[1]; + xres = get_u16( argc, argv, argv[2], "X_RESOLUTION" ); + yres = get_u16( argc, argv, argv[3], "Y_RESOLUTION" ); + bpp = get_u16( argc, argv, argv[4], "BITS_PER_PIXEL" ); + freq = get_u16( argc, argv, argv[5], "REFRESH_RATE" ); + + fprintf( stdout, + "width: %u height: %u, depth: %u, frequency: %u, shared_on: %s\n", + xres, yres, bpp, + freq, file_name + ); + + if( 0 == freq ) + { + fprintf( stderr, "frequencies of 0Hz are not supported\n" ); + exit( EXIT_FAILURE ); + } + + switch( bpp ) + { + case 24: + fprintf( stdout, "Using RGB colors\n" ); + break; + case 32: + fprintf( stdout, "Using RGBn colors, with n not used\n" ); + break; + case 8: + fprintf( stdout, "Using 256 colors\n" ); + //pass through + case 16: + fprintf( stdout, "Using 65536 colors\n" ); + //pass through + default: + fprintf( stderr, "Using bpp=%d is not supported (yet?).\n", bpp ); + exit( EXIT_FAILURE ); + } + + fbsim.mem = MAP_FAILED; + fbsim.size = xres * yres * bpp / 8; + fbsim.path = file_name; + if( -1 == ( fbsim.fd = open( fbsim.path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR ) ) ) + { + fprintf( stderr, "open( %s, O_RDWR ): %s (%d)\n", + fbsim.path, strerror( errno ), errno ); + exit( EXIT_FAILURE ); + } + atexit( shmem_deinit ); + if( -1 == ftruncate( fbsim.fd, (off_t) fbsim.size ) ) + { + fprintf( stderr, "ftruncate( %s, %zu ): %s (%d)\n", + fbsim.path, fbsim.size, strerror( errno ), errno ); + exit( EXIT_FAILURE ); + } + fprintf( stderr, "Using %zu of shared memory\n", fbsim.size ); + fbsim.mem = mmap( NULL, fbsim.size, PROT_READ | PROT_WRITE, MAP_SHARED, fbsim.fd, 0 ); + if( MAP_FAILED == fbsim.mem ) + { + fprintf( stderr, "mmap( 0, %zu, flags ): %s (%d)\n", + fbsim.size, strerror( errno ), errno ); + exit( EXIT_FAILURE ); + } + + if( SDL_Init( SDL_INIT_VIDEO ) == -1 ) + { + fprintf( stderr, "Could not initialize SDL: %s.\n", SDL_GetError() ); + exit( EXIT_FAILURE ); + } + atexit( SDL_Quit ); + + //Notes: + //not using SDL_DOUBLEBUF: goal is to mmap the window after all + //not using SDL_FULLSCREEN: goal is to "emulate" rendering on different devices + //not using SDL_OPENGL: goal is to "emulate" a framebuffer + //not using SDL_RESIZABLE: goal is to "emulate" a framebuffer + //using SDL_NOFRAME: goal is to "emulate" a framebuffer (no decoration) + // ^ this was done originally, but is more annoying than useful + screen = SDL_SetVideoMode( xres, yres, bpp, 0 /*SDL_NOFRAME*/ ); + if( NULL == screen ) + { + fprintf( stderr, "Failed to initialize window: %s.\n", SDL_GetError() ); + usage_and_quit( argc, argv ); + } + + struct timespec timer; + timer.tv_sec = 0; + timer.tv_nsec = 1000000000 / freq; + + const uint16_t scanline = screen->pitch; + const uint8_t * const endsrcy = (uint8_t*) fbsim.mem + fbsim.size; + const size_t srcw = xres * bpp / 8; + + fprintf( stdout, "updates screen every %zu nano seconds (10^-9)\n", timer.tv_nsec ); + + while( -1 != nanosleep( &timer, NULL ) ) + { + if( SDL_MUSTLOCK( screen ) && -1 == SDL_LockSurface( screen ) ) + { + fprintf( stderr, "lock screen: %s.\n", SDL_GetError() ); + exit( EXIT_FAILURE ); + } + + uint8_t const* srcy = fbsim.mem; + uint8_t * dsty = screen->pixels; + for( ; srcy < endsrcy; srcy += srcw, dsty += scanline ) + { + memcpy( dsty, srcy, srcw ); + } + + if( SDL_MUSTLOCK( screen ) ) + { + SDL_UnlockSurface( screen ); + } + + SDL_Flip( screen ); + } + exit( EXIT_SUCCESS ); +}