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 }