commit 6bb41b5732c02dc61ae0f928902e5c1aa84b705d
Author: Morel Bérenger <berengermorel76@gmail.com>
Date: Fri, 18 Sep 2020 15:15:23 +0200
initial commit
Diffstat:
A | README | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/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 );
+}