fbsim

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

main.c (6905B)


      1 /***********************************************************
      2 Copyright 2020 Morel BĂ©renger
      3 
      4 Permission is hereby granted, free of charge, to any person
      5 obtaining a copy of this software and associated
      6 documentation files (the "Software"), to deal in the Software
      7 without restriction, including without limitation the rights
      8 to use, copy, modify, merge, publish, distribute, sublicense,
      9 and/or sell copies of the Software, and to permit persons to
     10 whom the Software is furnished to do so, subject to the
     11 following conditions:
     12 
     13 The above copyright notice and this permission notice shall
     14 be included in all copies or substantial portions of the
     15 Software.
     16 
     17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
     18 KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
     19 WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
     20 PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
     21 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     22 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
     23 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     24 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25 ***********************************************************/
     26 
     27 #include <stdlib.h>
     28 #include <stdio.h>
     29 #include <stdint.h>
     30 #include <errno.h>
     31 #include <string.h>
     32 
     33 #include <unistd.h>
     34 #include <fcntl.h>
     35 #include <sys/types.h>
     36 #include <sys/stat.h>
     37 #include <sys/mman.h>
     38 #include <time.h>
     39 
     40 #include <SDL/SDL.h>
     41 
     42 void usage_and_quit( int argc, char** argv );
     43 void shmem_deinit( void );
     44 uint16_t get_u16( int argc, char** argv, char const* const str, char const* const label );
     45 
     46 struct shared_mem_t
     47 {
     48 	void* mem;
     49 	size_t size;
     50 	char const* path;
     51 	int fd;
     52 };
     53 
     54 static struct shared_mem_t fbsim;
     55 
     56 void usage_and_quit( int argc, char** argv )
     57 {
     58 	fprintf( stderr,
     59 			"Usage: %s path width height bpp freq\n"
     60 			"\tpath: (%s) where to share memory\n"
     61 			"\twidth: (%s)\n"
     62 			"\theight: (%s)\n"
     63 			"\tbpp: (%s) bits per pixel\n"
     64 			"\tfreq: (%s) refresh frequency, in Hertz\n"
     65 			, argv[0]
     66 			, argc < 1 ? "NEEDED" : argv[1]
     67 			, argc < 2 ? "NEEDED" : argv[2]
     68 			, argc < 3 ? "NEEDED" : argv[3]
     69 			, argc < 4 ? "NEEDED" : argv[4]
     70 			, argc < 5 ? "NEEDED" : argv[5]
     71 	);
     72 	exit( EXIT_FAILURE );
     73 }
     74 
     75 void shmem_deinit( void )
     76 {
     77 	if( MAP_FAILED != fbsim.mem && 0 != fbsim.size )
     78 	{
     79 		if( -1 == munmap( fbsim.mem, fbsim.size ) )
     80 		{
     81 			fprintf( stderr, "munmap( %p, %lu ): %s (%d)\n",
     82 					fbsim.mem, fbsim.size,
     83 					strerror( errno ), errno );
     84 		}
     85 	}
     86 	close( fbsim.fd );
     87 	if( -1 == unlink( fbsim.path ) )
     88 	{
     89 		fprintf( stderr, "unlink( %s ): %s (%d)\n",
     90 				fbsim.path, strerror( errno ), errno );
     91 	}
     92 }
     93 
     94 uint16_t get_u16( int argc, char** argv, char const* const str, char const* const label )
     95 {
     96 	unsigned long tmp_ul;
     97 	char *tmp_ptr;
     98 	tmp_ul = strtoul( str, &tmp_ptr, 10 );
     99 	if( errno )
    100 	{
    101 		fprintf( stderr, "%s error: %s (%d)\n",
    102 				label, strerror( errno ), errno );
    103 		usage_and_quit( argc, argv );
    104 	}
    105 	if( tmp_ul > UINT16_MAX || *tmp_ptr )
    106 	{
    107 		fprintf( stderr, "%s error: invalid value\n",
    108 				label );
    109 		usage_and_quit( argc, argv );
    110 	}
    111 	return (uint16_t) tmp_ul;
    112 }
    113 
    114 int main(int argc, char *argv[])
    115 {
    116 	uint16_t xres, yres, bpp, freq;
    117 	char *file_name;
    118 	SDL_Surface* screen;
    119 
    120 	if( argc < 6 )
    121 	{
    122 		usage_and_quit( argc, argv );
    123 	}
    124 
    125 	file_name = argv[1];
    126 	xres = get_u16( argc, argv, argv[2], "X_RESOLUTION" );
    127 	yres = get_u16( argc, argv, argv[3], "Y_RESOLUTION" );
    128 	bpp  = get_u16( argc, argv, argv[4], "BITS_PER_PIXEL" );
    129 	freq = get_u16( argc, argv, argv[5], "REFRESH_RATE" );
    130 
    131 	fprintf( stdout,
    132 			"width: %u height: %u, depth: %u, frequency: %u, shared_on: %s\n",
    133 			xres, yres, bpp,
    134 			freq, file_name
    135 	);
    136 
    137 	if( 0 == freq )
    138 	{
    139 		fprintf( stderr, "frequencies of 0Hz are not supported\n" );
    140 		exit( EXIT_FAILURE );
    141 	}
    142 
    143 	switch( bpp )
    144 	{
    145 		case 24:
    146 			fprintf( stdout, "Using RGB colors\n" );
    147 			break;
    148 		case 32:
    149 			fprintf( stdout, "Using RGBn colors, with n not used\n" );
    150 			break;
    151 		case 8:
    152 			fprintf( stdout, "Using 256 colors\n" );
    153 			//pass through
    154 		case 16:
    155 			fprintf( stdout, "Using 65536 colors\n" );
    156 			//pass through
    157 		default:
    158 			fprintf( stderr, "Using bpp=%d is not supported (yet?).\n", bpp );
    159 			exit( EXIT_FAILURE );
    160 	}
    161 
    162 	fbsim.mem  = MAP_FAILED;
    163 	fbsim.size = xres * yres * bpp / 8;
    164 	fbsim.path = file_name;
    165 	if( -1 == ( fbsim.fd = open( fbsim.path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR ) ) )
    166 	{
    167 		fprintf( stderr, "open( %s, O_RDWR ): %s (%d)\n",
    168 				fbsim.path, strerror( errno ), errno );
    169 		exit( EXIT_FAILURE );
    170 	}
    171 	atexit( shmem_deinit );
    172 	if( -1 == ftruncate( fbsim.fd, (off_t) fbsim.size ) )
    173 	{
    174 		fprintf( stderr, "ftruncate( %s, %zu ): %s (%d)\n",
    175 				fbsim.path, fbsim.size, strerror( errno ), errno );
    176 		exit( EXIT_FAILURE );
    177 	}
    178 	fprintf( stderr, "Using %zu of shared memory\n", fbsim.size );
    179 	fbsim.mem = mmap( NULL, fbsim.size, PROT_READ | PROT_WRITE, MAP_SHARED, fbsim.fd, 0 );
    180 	if( MAP_FAILED == fbsim.mem )
    181 	{
    182 		fprintf( stderr, "mmap( 0, %zu, flags ): %s (%d)\n",
    183 				fbsim.size, strerror( errno ), errno );
    184 		exit( EXIT_FAILURE );
    185 	}
    186 
    187 	if( SDL_Init( SDL_INIT_VIDEO ) == -1 )
    188 	{
    189 		fprintf( stderr, "Could not initialize SDL: %s.\n", SDL_GetError() );
    190 		exit( EXIT_FAILURE );
    191 	}
    192 	atexit( SDL_Quit );
    193 
    194 	//Notes:
    195 	//not using SDL_DOUBLEBUF: goal is to mmap the window after all
    196 	//not using SDL_FULLSCREEN: goal is to "emulate" rendering on different devices
    197 	//not using SDL_OPENGL: goal is to "emulate" a framebuffer
    198 	//not using SDL_RESIZABLE: goal is to "emulate" a framebuffer
    199 	//using SDL_NOFRAME: goal is to "emulate" a framebuffer (no decoration)
    200 	//       ^ this was done originally, but is more annoying than useful
    201 	screen = SDL_SetVideoMode( xres, yres, bpp, 0 /*SDL_NOFRAME*/ );
    202 	if( NULL == screen )
    203 	{
    204 		fprintf( stderr, "Failed to initialize window: %s.\n", SDL_GetError() );
    205 		usage_and_quit( argc, argv );
    206 	}
    207 
    208 	struct timespec timer;
    209 	timer.tv_sec = 0;
    210 	timer.tv_nsec = 1000000000 / freq;
    211 
    212 	const uint16_t scanline = screen->pitch;
    213 	const uint8_t * const endsrcy = (uint8_t*) fbsim.mem + fbsim.size;
    214 	const size_t srcw = xres * bpp / 8;
    215 
    216 	fprintf( stdout, "updates screen every %zu nano seconds (10^-9)\n", timer.tv_nsec );
    217 
    218 	//unlikely case
    219 	if( SDL_MUSTLOCK( screen ) )
    220 	{
    221 		fprintf( stdout, "using locks\n" );
    222 		while( -1 != nanosleep( &timer, NULL ) )
    223 		{
    224 			if( -1 == SDL_LockSurface( screen ) )
    225 			{
    226 				fprintf( stderr, "lock screen: %s.\n", SDL_GetError() );
    227 				exit( EXIT_FAILURE );
    228 			}
    229 
    230 			uint8_t const* srcy = fbsim.mem;
    231 			uint8_t * dsty = screen->pixels;
    232 			for( ; srcy < endsrcy; srcy += srcw, dsty += scanline )
    233 			{
    234 				memcpy( dsty, srcy, srcw );
    235 			}
    236 
    237 			SDL_UnlockSurface( screen );
    238 			SDL_Flip( screen );
    239 		}
    240 		exit( EXIT_SUCCESS );
    241 	}
    242 
    243 	//most likely case
    244 	while( -1 != nanosleep( &timer, NULL ) )
    245 	{
    246 		uint8_t const* srcy = fbsim.mem;
    247 		uint8_t * dsty = screen->pixels;
    248 		for( ; srcy < endsrcy; srcy += srcw, dsty += scanline )
    249 		{
    250 			memcpy( dsty, srcy, srcw );
    251 		}
    252 
    253 		SDL_Flip( screen );
    254 	}
    255 	exit( EXIT_SUCCESS );
    256 }