/* sf.c * * Generate snowflakes * * Gtk/X Windows interface * * IPC between Control, Generation, and View * Image Display * Control Dialog * Generation process * */ #include #include #include #include #include #include #include #include #include #include #include "getopt.h" #include "sf.h" /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ /* globals */ static char* program_name = "snowflake"; /* program name */ static char* version = "0.01A"; /* version id */ static char* image; /* shared memory XPM data */ static int image_shmid; /* image handle */ static int image_semid; /* image mutex */ static msg_pipe control_g; /* Generation <-> Control pipe*/ static msg_pipe control_v; /* View <-> Control pipe*/ static msg_pipe generation; /* Control <-> Generation pipe */ static msg_pipe view; /* Control <-> View pipe */ /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ struct option long_options[] = { { "version", 0, 0, c_VERSION }, { "view-geometry", 1, 0, c_VIEW_GEOMETRY }, { "control-geometry", 1, 0, c_CONTROL_GEOMETRY }, { "minimize", 0, 0, c_MINIMIZE }, { "size", 1, 0, c_SIZE }, { "symmetry", 1, 0, c_SYMMETRY }, { "edges", 1, 0, c_EDGES }, { "colour_scheme", 1, 0, c_COLOUR_SCHEME }, { "banding", 1, 0, c_BANDING }, { "centre", 1, 0, c_CENTRE }, { "name", 1, 0, c_NAME }, { "key", 1, 0, c_KEY }, { "list", 1, 0, c_LIST }, { "speed", 1, 0, c_SPEED }, { "generation", 1, 0, c_GENERATION }, { "loop", 1, 0, c_LOOP }, { 0, 0, 0, 0 } }; char short_options[2*COMMAND_OPTIONS+1]; typedef struct { char* long_name; char short_name; char* value; char* description; } help_info_line; help_info_line help_info[] = { { "version", c_VERSION, " ", "show program version" }, { "view-geometry", c_VIEW_GEOMETRY, "+x+y", "Control window placement" }, { "control-geometry", c_CONTROL_GEOMETRY, "+x+y", "View window placement" }, { "minimize", c_MINIMIZE, " ", "minimize Control window" }, { "size", c_SIZE, "32|64|128|256|512", "image Size" }, { "symmetry", c_SYMMETRY, "vertical|horizontal", "snowflake symmetry" }, { "edges", c_EDGES, "yes|gray|solid gray|no", "enable gray edges" }, { "colour_scheme", c_COLOUR_SCHEME, "white|black", "snow colour" }, { "banding", c_BANDING, "2..9", "random banding interval" }, { "centre", c_CENTRE, "0..24", "random centre size" }, { "name", c_NAME, "", "name" }, { "key", c_KEY, "", "key" }, { "list", c_LIST, "", "saved design file name" }, { "speed", c_SPEED, "1..120", "generation speed" }, { "generation", c_GENERATION, "?|(-n..)+m|n", "generation operation" }, { "loop", c_LOOP, "yes|no", "design file loop" } }; /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ /* prototypes */ static void main_cleanup( void ); static int duplex_pipe ( msg_pipe* parent,msg_pipe* child ); static void duplex_close( msg_pipe mp ); static int create_shared_image( int base, char** image, size_t size, int* shmid, int* semid ); static int free_shared_image( char* image,int shmid,int semid ); static int malloc_shm( key_t shm_key,char** shm_pp,size_t size ); static void free_shm( int shm_id,char*shm_p ); static int open_sem( key_t sem_key ); static void close_sem( int sem_id ); /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ int main( int argc,char* argv[] ) { int i; char* shop; /* initialize short_options[] */ shop = short_options; for ( i = 0; i < COMMAND_OPTIONS; i++ ) { *shop++ = (char)long_options[i].val; if ( long_options[i].has_arg ) *shop++ = ':'; } *shop = '\0'; /* asking for help? */ if ( argc == 2 ) { if ( ( strncmp( argv[1], "-h", 2 ) == 0 ) || ( strncmp( argv[1],"--help",3 ) == 0 ) ) { printf( "%s - %s\n",program_name,version ); printf( "usage:\n" ); printf( " %s [ --switch=value ] ..\n",program_name ); printf( " or\n" ); printf( " %s [ -s value ] ..\n",program_name ); printf( " --%-16s -%c %-22s %s\n", "switch",'s',"value","description" ); printf( " ------------------ -- ----------------------" " ------------------------\n" ); printf( " --%-16s -%c %-22s %s\n", "help",'h'," ","this help message" ); for ( i = 0; i < COMMAND_OPTIONS; i++ ) printf( " --%-16s -%c %-22s %s\n", help_info[i].long_name, help_info[i].short_name, help_info[i].value, help_info[i].description ); exit( 0 ); } /* asking for version? */ if ( ( strncmp( argv[1], "-V", 2 ) == 0 ) || ( strncmp( argv[1],"--version",3 ) == 0 ) ) { printf( "%s - %s\n",program_name,version ); exit( 0 ); } } /* create shared memory image and mutex */ if ( !create_shared_image( getpid(), &image, sizeof( image_data ), &image_shmid, &image_semid ) ) exit ( 2 ); /* initialize Control <-> Generation pipes */ if ( !duplex_pipe( &control_g,&generation ) ) exit ( 1 ); /* fork Generation process */ switch ( fork() ) { case (-1) : /* fork() error */ duplex_close( control_g ); duplex_close( generation ); fprintf( stderr,"Unable to fork Generation process: %s", strerror( errno ) ); exit( 3 ); case (0) : /* child process */ /* close unnecessary pipes */ duplex_close( generation ); generation_main( argc,argv,control_g,image_semid,image ); exit( 0 ); break; default: /* parent process */ break; } /* initialize Control <-> View pipes */ if ( !duplex_pipe( &control_v,&view ) ) exit ( 4 ); /* fork View process */ switch ( fork() ) { case (-1) : /* fork() error */ duplex_close( control_g ); duplex_close( generation ); duplex_close( control_v ); duplex_close( view ); fprintf( stderr,"Unable to fork View process: %s", strerror( errno ) ); exit( 5 ); case (0) : /* child process */ /* close unnecessary pipes */ duplex_close( view ); view_main( argc,argv,control_v,image_semid,image ); exit( 0 ); break; default: /* parent process */ break; } /* start Control process */ /* close unnecessary pipes */ duplex_close( control_g ); duplex_close( control_v ); atexit( main_cleanup ); control_main( argc,argv,generation,view,image_semid,image ); exit( 0 ); } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ /* to be called on exit() */ static void main_cleanup( void ) { duplex_close( generation ); duplex_close( view ); free_shared_image( image,image_shmid,image_semid ); } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ static int duplex_pipe( msg_pipe* parent,msg_pipe* child ) { int snd[2]; /* send pipe */ int rcv[2]; /* receive pipe */ if ( pipe( snd ) != 0 ) { fprintf( stderr,"Unable to open send pipe: %s", strerror( errno ) ); return 0; } if ( pipe( rcv ) != 0 ) { fprintf( stderr,"Unable to open receive pipe: %s", strerror( errno ) ); return 0; } parent->snd = rcv[MP_WRITE]; parent->rcv = snd[MP_READ]; child->snd = snd[MP_WRITE]; child->rcv = rcv[MP_READ]; return 1; } static void duplex_close( msg_pipe mp ) { close( mp.snd ); close( mp.rcv ); } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ static int create_shared_image( int base, char** image, size_t size, int* shmid, int* semid ) { key_t semkey; key_t shmkey; base = base << KEY_SHIFT; /* initialize semaphore set * the image_semid is set ready for use ( 'v()' ) */ semkey = (key_t)( base + SEM_KEY + IMAGE_SUBKEY ); *semid = open_sem( semkey ); if ( *semid < 0 ) return 0; /* create and attach shared memory segment */ shmkey = (key_t)( base + SHM_KEY + IMAGE_SUBKEY ); *shmid = malloc_shm( shmkey,image,size ); if ( *shmid < 0 ) return 0; return 1; } static int free_shared_image( char* image,int shmid,int semid ) { close_sem( semid ); free_shm( shmid,image ); } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ static int malloc_shm( key_t shm_key,char** shm_pp,size_t size ) { int shm_id; /* create shared memory segment */ if ( ( shm_id = shmget( shm_key,size,SHM_RW|IFLAGS ) ) < 0 ) { fprintf( stderr,"Unable to get shared memory for key %d: %s", shm_key,strerror( errno ) ); return -1; } /* attach shared memory segment */ if ( ( *shm_pp = (char*)shmat( shm_id,0,0 ) ) == (char*)-1 ) { fprintf( stderr,"Unable to attach to shared memory for key %d: %s", shm_key,strerror( errno ) ); return -1; } return shm_id; } static void free_shm( int shm_id,char* shm_p ) { shmdt( shm_p ); shm_p = (char*)0; if ( shmctl( shm_id,IPC_RMID,(struct shmid_ds*)0 ) < 0 ) { fprintf( stderr,"Unable to remove shared memory id %d: %s", shm_id,strerror( errno ) ); } } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ static int open_sem( key_t sem_key ) { int sem_id; int arg; /* create two semaphore set */ if ( ( sem_id = semget( sem_key,1,SHM_RW|IFLAGS ) ) < 0 ) { fprintf( stderr,"Unable to get semaphore for key %d: %s", sem_key,strerror( errno ) ); return -1; } /* set initial values */ arg = 1; if ( semctl( sem_id,0,SETVAL,arg ) < 0 ) { fprintf( stderr,"Unable to set semaphore 0 for key %d: %s", sem_key,strerror( errno ) ); return -1; } return sem_id; } static void close_sem( int sem_id ) { int arg; arg = 0; if ( semctl( sem_id,0,IPC_RMID,arg ) < 0 ) { fprintf( stderr,"Unable to remove semaphore id %d: %s", sem_id,strerror( errno ) ); } } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ /* Shared Routines */ /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ void sem_wait( int sem_id ) /* p() */ { struct sembuf p_sem; p_sem.sem_num = 0; p_sem.sem_op = -1; p_sem.sem_flg = SEM_UNDO; semop( sem_id,&p_sem,1 ); } int sem_signal( int sem_id ) /* v() */ { struct sembuf v_sem; v_sem.sem_num = 0; v_sem.sem_op = 1; v_sem.sem_flg = SEM_UNDO; semop( sem_id,&v_sem,1 ); } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ /* is the child's parent still alive? */ int is_alive( int parent_pid ) { int ppid = getppid(); if ( ppid != parent_pid ) return FALSE; return TRUE; } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ /* send a message */ int snd_msg( msg_pipe mp,snowflake_message* sf_msg_p ) { /* write the command to the pipe */ if ( write( mp.snd,sf_msg_p,SNOWFLAKE_MSG_LEN ) < 0 ) { fprintf( stderr,"Unable to write message: %s", strerror( errno ) ); return 0; } return 1; } /* receive an incoming message */ int rcv_msg( msg_pipe mp,snowflake_message* sf_msg_p ) { /* read the command from the pipe */ if ( read( mp.rcv,sf_msg_p,SNOWFLAKE_MSG_LEN ) < 0 ) { fprintf( stderr,"Unable to read message: %s", strerror( errno ) ); return 0; } return 1; } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ /* get the image size in pixels */ int get_image_size( size_e size ) { switch ( size ) { case size_032x032 : return 32; case size_064x064 : return 64; case size_128x128 : return 128; case size_256x256 : return 256; case size_512x512 : return 512; } return 0; } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ /* write the XPM header to the image data */ void write_image_header( char* data,int size ) { image_data* image = (image_data*)data; int i; char* offset; /* clear line and data areas */ memset( (void*)data,0,sizeof( image_data ) ); /* set image size */ image->size = size; offset = image->data; image->line[IMAGE_SPEC_LINE] = offset; /* line 1 : width height number_of_colours chars/pixel */ sprintf( image->line[IMAGE_SPEC_LINE],IMAGE_SPEC_FORMAT,size,size,COLOURS,1 ); offset += strlen( image->line[IMAGE_SPEC_LINE] ) + 1; image->line[COLOURS_SPEC_LINE] = offset; /* line 2 : colours, line 1 */ sprintf( image->line[COLOURS_SPEC_LINE],COLOURS_SPEC_FORMAT, WHITE_PIXEL,WHITE_COLOUR_NAME,WHITE_COLOUR_NAME ); offset += strlen( image->line[COLOURS_SPEC_LINE] ) + 1; image->line[COLOURS_SPEC_LINE+1] = offset; /* line 3 : colours, line 2 */ sprintf( image->line[COLOURS_SPEC_LINE+1],COLOURS_SPEC_FORMAT, GRAY_PIXEL,GRAY_COLOUR_NAME,GRAY_COLOUR_NAME ); offset += strlen( image->line[COLOURS_SPEC_LINE+1] ) + 1; image->line[COLOURS_SPEC_LINE+2] = offset; /* line 4 : colours, line 3 */ sprintf( image->line[COLOURS_SPEC_LINE+2],COLOURS_SPEC_FORMAT, BLACK_PIXEL,BLACK_COLOUR_NAME,BLACK_COLOUR_NAME ); offset += strlen( image->line[COLOURS_SPEC_LINE+2] ) + 1; /* assign remaining line->data pointers */ for ( i = 0; i < size; i++ ) image->line[IMAGE_DATA_LINE+i] = offset + i * ( size+1 ); } /* write one pixel to the image data */ void write_image_pixel( char* data,int x,int y,char pixel ) { image_data* image = (image_data*)data; char* line; line = image->line[IMAGE_DATA_LINE+y]; line[x] = pixel; } /* write the image data to a file */ int write_image_to_file( char* data,char* filespec,char* key ) { image_data* image = (image_data*)data; int i; FILE* xpm_file; time_t walltime; struct tm* walltm_p; char timestamp[64]; xpm_file = fopen( filespec,"w" ); if ( !xpm_file ) return errno; walltime = time( (time_t*)0 ); walltm_p = localtime( &walltime ); strftime( timestamp,64,"%A %B %e %Y %T %Z",walltm_p ); fprintf( xpm_file,"/* XPM */\n" ); fprintf( xpm_file,"/* snowflake key: %s */\n",key ); fprintf( xpm_file,"/* %s */\n",timestamp ); fprintf( xpm_file,"static char* snowflake_%s[] = {\n",key ); fprintf( xpm_file," \"%s\",\n",image->line[IMAGE_SPEC_LINE] ); fprintf( xpm_file," \"%s\",\n",image->line[COLOURS_SPEC_LINE] ); fprintf( xpm_file," \"%s\",\n",image->line[COLOURS_SPEC_LINE+1] ); fprintf( xpm_file," \"%s\",\n",image->line[COLOURS_SPEC_LINE+2] ); for ( i = 0; i < image->size-1; i++ ) fprintf( xpm_file," \"%s\",\n",image->line[IMAGE_DATA_LINE+i] ); fprintf( xpm_file," \"%s\"\n",image->line[IMAGE_DATA_LINE+image->size-1] ); fprintf( xpm_file,"};\n" ); fclose( xpm_file ); return 0; } /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */