/* XBubble - init.c Copyright (C) 2002 Ivan Djelic This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include "setting.h" #include "cell.h" #include "rgba.h" #include "dialog.h" #include "frame.h" #include "bubble.h" #include "board.h" #include "gettext.h" extern Display *display; extern Visual *visual; extern Window root; extern Window win; extern int screen; extern int depth; extern Colormap colormap; extern char * data_dir; extern char * theme; extern int scale; extern int win_width; extern int win_height; extern int nb_levels; extern int level[MAX_NB_LEVELS][NB_CELLS]; XFontStruct *title_font; XFontStruct *dialog_font; XFontStruct *menu_font; GC dialog_gc; GC dialog_text_gc; GC menu_gc; GC selected_gc; Pixmap win_bg; Pixmap board_decoration; Pixmap board_decoration_mask; Pixmap cup_on; Pixmap cup_off; Pixmap cup_on_mask; Pixmap cup_off_mask; Set alert_animation; Set canon_animation; Set countdown_animation; Set bubble_animation[NB_COLORS][NB_BUBBLE_STATES]; char *game_fonts[] = FONTS; int titlefont_color[3] = TITLEFONT_COLOR; int menufont_color[3] = MENUFONT_COLOR; int menufont_selected_color[3] = MENUFONT_SELECTED_COLOR; int notitle = 0; char * data_file( char * file, int themed ) { int path_max; int theme_len; char * abs_data_dir; static int offset = 0; static char buffer[1024]; if ( ! offset ) { #ifdef PATH_MAX path_max = PATH_MAX; #else path_max = pathconf (path, _PC_PATH_MAX); if (path_max <= 0) path_max = 4096; #endif abs_data_dir = (char *) xmalloc( path_max * sizeof(char)); realpath( data_dir, abs_data_dir ); offset = strlen(abs_data_dir); if ( offset > 1024-128 ) offset = 1024-128; strncpy( buffer, abs_data_dir, offset ); buffer[offset++] = '/'; buffer[1023] = '\0'; free( abs_data_dir ); } if (themed) { theme_len=strlen(theme); strncpy( buffer + offset, "themes/", 126); strncpy( buffer + offset + 7, theme, 119); strncpy( buffer + offset + theme_len + 7, "/", 119 - theme_len); strncpy( buffer + offset + theme_len + 8, file, 118 - theme_len); } else { strncpy( buffer + offset, file, 126); } return buffer; } void load_levels( char * file ) { FILE *fd; char line[128]; unsigned char r[8]; int i, cell = 0; int line_counter = 0; int long_row = 1; int color, parse_error = 0; nb_levels = 0; file = data_file(file, 0); fd = fopen( file, "r" ); if ( fd == NULL ) { warn( _("cannot open levels file %s."), file); return; } while (( fgets( line, 128, fd ) != NULL )&&( ! parse_error )) { line_counter++; if (( isalnum(line[0]) )||( line[0] == '-' )||( line[0] == ' ' )) { /* parse row */ if ( long_row ) parse_error = ( sscanf( line, "%c %c %c %c %c %c %c %c", r, r+1, r+2, r+3, r+4, r+5, r+6, r+7 ) != 8); else parse_error = ( sscanf( line, " %c %c %c %c %c %c %c", r+1, r+2, r+3, r+4, r+5, r+6, r+7 ) != 7 ); if ( ! parse_error ) { for ( i = 0; i < 8; i++ ) { switch( r[i] ) { default: /* the shift in values read and stored comes from the compatibility with fb */ case '-': color = 0; break; case '0': color = 1; break; case '1': color = 2; break; case '2': color = 3; break; case '3': color = 4; break; case '4': color = 5; break; case '5': color = 6; break; case '6': color = 7; break; case '7': color = 8; break; } level[nb_levels][cell++] = color; } long_row = 1 - long_row; } else warn( _("parse error in file %s at line %d."), file, line_counter ); } if ( cell >= (ROWS-1)*8 ) { nb_levels++; cell = 0; long_row = 1; } if ( nb_levels >= MAX_NB_LEVELS ) { warn( _("too many levels in file %s."), file ); break; } } } void init_display( char * display_name ) { int i; XColor colors[256]; display = XOpenDisplay( display_name ); if ( display == NULL ) fail( _("cannot open display.") ); screen = DefaultScreen( display); visual = DefaultVisual( display, screen); root = RootWindow( display, screen); depth = DefaultDepth( display, screen); if ( depth < 8 ) { XCloseDisplay(display); fail( _("sorry, your display color depth is not supported.") ); } if ( depth == 8 ) { /* allocate our own RRRGGGBB palette */ for ( i = 0; i < 256; i++ ) { colors[i].pixel = i; colors[i].red = (( i & 0xe0 ) << 8 ); colors[i].green = (( i & 0x1c ) << 11 ); colors[i].blue = (( i & 0x03 ) << 14 ); colors[i].flags = DoRed | DoGreen | DoBlue; } colormap = XCreateColormap( display, root, visual, AllocAll ); XStoreColors( display, colormap, colors, 256 ); } } void add_font_path( char * path ) { int i; int npaths; char ** fontpath; char ** new_fontpath; fontpath = XGetFontPath( display, &npaths ); if ( fontpath != NULL ) { /* check if path is already present */ for ( i = 0; i < npaths; i++ ) if ( strncmp( path, fontpath[i], strlen(path) ) == 0 ) { XFreeFontPath(fontpath); return; } new_fontpath = (char **) xmalloc( sizeof(char *)*( npaths + 1 )); memcpy( new_fontpath, fontpath, npaths*sizeof(char *)); new_fontpath[npaths] = path; XSetFontPath( display, new_fontpath, npaths+1 ); XFreeFontPath(fontpath); free(new_fontpath); } } XFontStruct * load_font( int pixel_size ) { XFontStruct * xfont; char name[128]; int i; static int added_font_path = 0; /* try to load each font in turn */ for ( i = 0; game_fonts[i] != NULL; i++ ) { sprintf( name, game_fonts[i], pixel_size ); xfont = XLoadQueryFont( display, name ); if ( xfont != NULL ) return xfont; } /* let's see if we can supply a font */ if ( ! added_font_path ) { add_font_path(data_file("",0)); added_font_path = 1; } sprintf( name, SUPPLIED_FONT, pixel_size ); xfont = XLoadQueryFont( display, name ); if ( xfont != NULL ) return xfont; /* at least we should have the "fixed" font ... uurgh */ xfont = XLoadQueryFont( display, "fixed" ); if ( xfont == NULL ) fail( _("couldn't load any font !") ); return xfont; } static GC create_copy_gc() { XGCValues gcv; unsigned long gcm; gcm = GCFunction | GCGraphicsExposures; gcv.graphics_exposures = False; gcv.function = GXcopy; return XCreateGC( display, root, gcm, &gcv ); } Frame load_frame( char *file, double zoom ) { Pixmap pixmap; Pixmap mask; Pixmap *pmask; RgbaImage ri1; RgbaImage ri2; Frame frame = (Frame) xmalloc( sizeof( struct _Frame )); ri1 = create_rgba_image_from_png_file(data_file(file,1)); /* ri2 = ( zoom < 1.0 )? scale_rgba_image( ri1, zoom, zoom ) : ri1; */ ri2 = scale_rgba_image( ri1, zoom, zoom ) ; pmask = ( ri2->has_alpha )? &mask : NULL; create_pixmaps_from_rgba_image( ri2, &pixmap, pmask ); frame->width = ri2->width; frame->height = ri2->height; /* center sprite */ frame->cx = ri2->width/2; frame->cy = ri2->height/2; /* default frame duration = 40 ms */ frame->delay = 40; frame->has_mask = ri2->has_alpha; frame->pixmap = pixmap; if ( frame->has_mask ) { frame->gc = create_copy_gc(); /* precompute clipmask */ XSetClipMask( display, frame->gc, mask ); XFreePixmap( display, mask ); } if ( ri1 != ri2 ) delete_rgba_image(ri1); delete_rgba_image(ri2); return frame; } void delete_frame( Frame frame ) { XFreePixmap( display, frame->pixmap); if ( frame->has_mask ) XFreeGC( display, frame->gc ); free(frame); } Set load_animation( char *filename, double zoom ) { Frame frame; Set animation = NULL; int cx; int cy; int delay; int max_nb_frames = 0; int lc = 0; char line[128]; char framename[128]; FILE *fd; /* open animation description file */ fd = fopen( data_file(filename,1), "r" ); if ( fd == NULL ) fail( _("cannot open animation file %s."), data_file(filename,1) ); /* count frames */ while ( fgets( line, 128, fd ) != NULL ) { if ( isalnum(line[0])) max_nb_frames++; } rewind(fd); /* load frames */ if ( max_nb_frames > 0 ) { animation = set_new(max_nb_frames); while ( fgets( line, 128, fd ) != NULL ) { lc++; /* parse line */ if ( isalnum(line[0])) { if ( sscanf( line, "%127s%d%d%d", framename, &cx, &cy, &delay) == 4 ) { frame = load_frame( framename, zoom ); set_add( animation, frame ); frame->cx = cx*zoom; frame->cy = cy*zoom; frame->delay = delay; } else warn( _("error parsing line %d of file %s."), lc, filename ); } } if ( animation->size == 0 ) fail( _("animation %s has no valid frames."), filename ); } fclose(fd); return animation; } void load_scaled_image( char *file, Pixmap *pix, Pixmap *mask, double zoom ) { RgbaImage ri1; RgbaImage ri2; ri1 = create_rgba_image_from_png_file(data_file(file,1)); ri2 = ( zoom < 1.0 )? scale_rgba_image( ri1, zoom, zoom ) : ri1; create_pixmaps_from_rgba_image( ri2, pix, mask ); delete_rgba_image(ri1); if ( ri2 != ri1 ) delete_rgba_image(ri2); } void delete_animation( Set animation ) { int i; if (animation) { for ( i = 0; i < animation->size; i++ ) delete_frame( animation->element[i] ); set_free( animation ); } } void make_canon_animation( double zoom ) { RgbaImage ri; RgbaImage ri2; Pixmap mask; Frame frame; int i; ri2 = create_rgba_image_from_png_file(data_file("canon.png",1)); ri2->hotx = CANON_CX; ri2->hoty = CANON_CY; ri = scale_rgba_image( ri2, zoom, zoom ); delete_rgba_image(ri2); canon_animation = set_new( NB_ANGLES ); for ( i = -CANON_ANGLE_MAX; i <= CANON_ANGLE_MAX; i++ ) { frame = (Frame) xmalloc( sizeof( struct _Frame )); ri2 = rotate_rgba_image( ri, -i*ANGLE_STEP, True ); create_pixmaps_from_rgba_image( ri2, &frame->pixmap, &mask ); frame->width = ri2->width; frame->height = ri2->height; frame->cx = ri2->hotx; frame->cy = ri2->hoty; frame->delay = 0; frame->has_mask = ri2->has_alpha; if ( frame->has_mask ) { frame->gc = create_copy_gc(); /* precompute clipmask */ XSetClipMask( display, frame->gc, mask ); XFreePixmap( display, mask ); } delete_rgba_image(ri2); set_add( canon_animation, frame ); } delete_rgba_image(ri); } /* Determine whether it is a valid bubble name or not. The following Naming convention is used : Bubble___.png */ int parse_bubble_name(const char *name,int *color_val, int *state_val) { char *copy = NULL, *copy_orig = NULL; char *token = NULL; int color,state; int return_val = 0; copy = copy_orig = calloc(strlen(name) + 1, sizeof(char)); strcpy(copy,name); token = strtok(copy,"_"); if(strcmp(token,"Bubble")==0) { /* Let's determine the color */ token = strtok(NULL,"_"); for ( color = 0; color < NB_COLORS; color++ ) { if(strcmp(name_color_get(color),token)==0) { break; } } if(color==NB_COLORS) { warn( _("%s : %s is not a valid color."), name, token ); } else { /* Let's determine the state now */ *color_val = color; token = strtok(NULL,"_"); for ( state = 0; state < NB_BUBBLE_STATES; state++ ) { if(strcmp(name_state_get(state),token)==0) { break; } } if((state == NB_BUBBLE_STATES) && (strcmp(DEFAULT_STATE_NAME,token)!=0)) { warn( _("%s : %s is not a valid state."), name, token ); } else { return_val = 1; *state_val = ((state==NB_BUBBLE_STATES)?DEFAULT_STATE:state); } } } free(copy_orig); return return_val; } int diren_select_bubbles (struct dirent *d) { int color; int state; /* Check whether this is a .png file or not */ if(strcmp(d->d_name + strlen(d->d_name) - 4,".png")!=0) { return 0; } return(parse_bubble_name(d->d_name,&color,&state)); } void load_animations( double zoom ) { int color=-1; int state=-1; int bubble_animation_size[NB_COLORS][NB_BUBBLE_STATES]; Set default_animation[NB_COLORS]; int default_animation_size[NB_COLORS]; struct dirent **namelist = NULL; int n = 0; int i; char *dir = NULL; alert_animation = load_animation( "frame_light.txt", zoom ); countdown_animation = load_animation( "countdown.txt", zoom ); for ( color = 0; color < NB_COLORS; color++ ) { for ( state = 0; state < NB_BUBBLE_STATES; state++ ) { bubble_animation[color][state] = NULL; bubble_animation_size[color][state] = 0; } } for ( color = 0; color < NB_COLORS; color++ ) { default_animation[color] = NULL; default_animation_size[color] = 0; } color=state=-1; /* bubble animation */ dir = data_file("./",1); n = scandir(dir, &namelist, diren_select_bubbles , alphasort); if (n < 0) fail(_("Error while scanning %s"),dir); for(i=0; id_name,&color,&state); if(state!=DEFAULT_STATE) { bubble_animation_size[color][state]++; } else { default_animation_size[color]++; } } for ( color = 0; color < NB_COLORS; color++ ) { if(default_animation_size[color]) { default_animation[color]= set_new(default_animation_size[color]); } for ( state = 0; state < NB_BUBBLE_STATES; state++ ) { if(bubble_animation_size[color][state]==0) { bubble_animation[color][state] = default_animation[color]; } else { bubble_animation[color][state] = set_new(bubble_animation_size[color][state]); } } } for(i=0; id_name,&color,&state); if(state!=DEFAULT_STATE) { set_add( bubble_animation[color][state], load_frame(namelist[i]->d_name, zoom)); } else { set_add( default_animation[color], load_frame(namelist[i]->d_name, zoom)); } } for ( color = 0; color < NB_COLORS; color++ ) { for ( state = 0; state < NB_BUBBLE_STATES; state++ ) { if(bubble_animation[color][state]==NULL) { fail(_("Badly formed theme : no image for %s bubbles in state %s"), name_color_get(color),name_state_get(state)); } } } for(i=0; ielement[0]; XGetGeometry( display, frame->pixmap, &w, &x, &y, &width, &height, &d, &d ); x = ( board_win_width(scale) - width )/2; put_pixmap( board_decoration, board_decoration_mask, frame->pixmap, None, 0, 0, frame->width, frame->height, x, 0 ); /* cleanup */ XFreeGC( display, gc ); XFreeGC( display, gc_mask ); XFreePixmap( display, bar ); XFreePixmap( display, tile ); XFreePixmap( display, tile_mask ); XFreePixmap( display, side ); XFreePixmap( display, side_mask ); XFreePixmap( display, bottom ); XFreePixmap( display, bottom_mask ); XFreePixmap( display, tee ); XFreePixmap( display, tee_mask ); } void init_gcs() { /* load dialog font */ dialog_font = load_font( 3*scale/2 ); /* yellow for hilighting menu selection */ selected_gc = create_copy_gc(); XSetFont( display, selected_gc, menu_font->fid ); XSetForeground( display, selected_gc, get_pixel( menufont_selected_color[0], menufont_selected_color[1], menufont_selected_color[2] )); /* grey for menu items */ menu_gc = create_copy_gc(); XSetFont( display, menu_gc, menu_font->fid ); XSetForeground( display, menu_gc, get_pixel( menufont_color[0], menufont_color[1], menufont_color[2] )); } void cleanup_graphics() { int color; delete_animation( alert_animation ); delete_animation( canon_animation ); delete_animation( countdown_animation ); /* FIXME: cleanup animations properly ! delete_frame( bubble_animation[0][DEAD]->element[1] ); */ for ( color = 0; color < NB_COLORS; color++ ) { delete_animation( bubble_animation[color][STUCK] ); delete_animation( bubble_animation[color][EXPLODING] ); delete_animation( bubble_animation[color][DEAD] ); } XFreeGC( display, dialog_gc ); XFreeGC( display, dialog_text_gc ); XFreeGC( display, menu_gc ); XFreeGC( display, selected_gc ); XFreePixmap( display, win_bg ); XFreePixmap( display, board_decoration ); XFreePixmap( display, board_decoration_mask ); XFreePixmap( display, cup_on ); XFreePixmap( display, cup_off ); XFreePixmap( display, cup_on_mask ); XFreePixmap( display, cup_off_mask ); XFreeFont( display, title_font ); XFreeFont( display, dialog_font ); XFreeFont( display, menu_font ); } void splash_screen( double zoom ) { Window splash1=(Window)NULL; Window splash2; int height1; int height2; char * title = "XBubble"; char * subtitle = _("Loading graphics ..."); char *config_file = "config.txt"; FILE *fd = NULL ; char line[128]; char *copy = NULL; char *copy_orig = NULL; char *token = NULL; int lc=0; int value = 0; int *font_color = NULL; int font_nb=0; double newzoom = zoom; int i; fd = fopen( data_file(config_file,1), "r" ); if(fd) { /* time to change default values... */ while ( fgets( line, 128, fd ) != NULL ) { lc++; copy = copy_orig = calloc(strlen(line) + 1, sizeof(char)); strcpy(copy,line); copy[strlen(line)-1]=' '; token = strtok(copy," :"); if(token==NULL) continue; if(strcmp(token,"BUBBLE_SIZE")==0) { /* We need to scale */ token = strtok(NULL," :"); if(sscanf(token,"%d",&value)!=1) warn( _("error parsing line %d of file %s."), lc, config_file ); else newzoom = zoom * MAX_SCALE / value; } else if (((strcmp(token,"TITLE_COLOR")==0)&&((font_nb=1))) || ((strcmp(token,"MENU_COLOR")==0)&&((font_nb=2))) || ((strcmp(token,"MENU_SELECTED_COLOR")==0)&&((font_nb=3)))) { switch (font_nb) { case 1: font_color = titlefont_color; break; case 2: font_color = menufont_color; break; case 3: font_color = menufont_selected_color; break; default: break; } for(i=0; i<3; i++) { token = strtok(NULL," :"); if(sscanf(token,"%d",&font_color[i])!=1) { warn( _("error parsing line %d of file %s."), lc, config_file ); break; } } } else if (strcmp(token,"NO_TITLE")==0) { notitle = 1; } else warn( _("error parsing line %d of file %s."), lc, config_file ); free(copy_orig); } fclose(fd); } load_scaled_image( "xbubble_bg.png", &win_bg, NULL, zoom); XSetWindowBackgroundPixmap( display, win, win_bg ); XMapWindow( display, win ); /* load title & menu fonts */ title_font = load_font( 3*scale ); menu_font = load_font( scale ); height1 = title_font->ascent + title_font->descent; height2 = menu_font->ascent + menu_font->descent; /* make GCs */ dialog_gc = create_copy_gc(); dialog_text_gc = create_copy_gc(); /* GC for tiling dialogs background */ XSetFillStyle( display, dialog_gc, FillTiled ); XSetTile( display, dialog_gc, win_bg ); if (!notitle) splash1 = create_dialog( win_width/2, ( win_height - height2 )/2, gettext(title), title_font, get_pixel( titlefont_color[0], titlefont_color[1], titlefont_color[2] ), 0, 1.0 ); splash2 = create_dialog( win_width/2, ( win_height + height1 )/2, gettext(subtitle), menu_font, get_pixel( menufont_color[0], menufont_color[1], menufont_color[2] ), 0, 1.0 ); load_levels( "levels.txt" ); load_animations(newzoom); make_canon_animation(zoom); load_images(zoom); init_gcs(); if(!notitle) XDestroyWindow( display, splash1 ); XDestroyWindow( display, splash2 ); }