#!/usr/bin/bash # # Simple frontend for MAME using BASH # # (c) Peter van Eerten 2006, 2007, 2008 GPL license # # Tested with: # -BASH 3.1 on SLAMD 12 and GTK-server 2.2.3 # -BASH 3.1 on Zenwalk 4.8, 5.2 and GTK-server 2.2.5 # # October 14, 2006: Initial release # October 17, 2006: Improved configpanel # November 2, 2006: Improved start sequence # November 19, 2006: # -Fixed bug during startup # -Fixed bug counting games # -Improved window placement # January 10, 2007: # -Fixed small bug in configpanel # January 16, 2007: # -Improved embedded GTK4Bash # -Added signalling sync # August 20, 2007: # -Improved embedded GTK for GTK-server 2.1.4 # September 9, 2007: # -Improved creating gamelist # February 9, 2008: # -Improved GTK-server configfile lookup # -Compatible with SDLmame now # -Some improvements in GUI # -Improved error handling # -Implemented doubleclick to start game #-------------------------------------------------------------------------------------------------Setup embedded GTK # Set the extending globbing option in BASH shopt -s extglob declare PIPE="/tmp/gtk.bash.\$$" # Find GTK-server configfile first if [[ -f gtk-server.cfg ]]; then CFG=gtk-server.cfg elif [[ -f /etc/gtk-server.cfg ]]; then CFG=/etc/gtk-server.cfg elif [[ -f /usr/local/etc/gtk-server.cfg ]]; then CFG=/usr/local/etc/gtk-server.cfg else echo "No GTK-server configfile found! Please install GTK-server..." exit 1 fi # Now create global functionnames from GTK API if [[ ! -f $HOME/.gtk4bash || $CFG -nt $HOME/.gtk4bash ]] then echo "#!/usr/bin/bash" > $HOME/.gtk4bash echo "gtk-server fifo=$PIPE &" >> $HOME/.gtk4bash echo "while [ ! -p $PIPE ]; do continue; done" >> $HOME/.gtk4bash while read LINE do if [[ $LINE = FUNCTION_NAME* && $LINE = +(*gtk_*|*gdk_*|*g_*|*glade_*) ]] then LINE=${LINE#*= } printf "\nfunction ${LINE%%,*}\n" >> $HOME/.gtk4bash printf "{\n/bin/echo ${LINE%%,*} \$@ > $PIPE" >> $HOME/.gtk4bash printf "\nread GTK < $PIPE\n}\n" >> $HOME/.gtk4bash fi done < $CFG printf "\nfunction gtk()\n{\necho \$1 > $PIPE; read GTK < $PIPE;\n}\n" >> $HOME/.gtk4bash fi # Declare global variables declare GTK NULL="NULL" # Make these available again unset CFG PIPE LINE # Include the generated '.gtk4bash'-file in the shellscript to use embedded GTK functions . $HOME/.gtk4bash #-------------------------------------------------------------------------------------------------The launch procedure function Launch_Game { # BASH mechanism to declare some local variables typeset PTH ROW ZIP VALSIZE MODE="soft" VGA=0 NR=0 while [[ $NR -lt $TOTAL ]] do gtk_tree_path_new_from_string $NR PTH=$GTK gtk_tree_selection_path_is_selected $SEL $PTH ROW=$GTK gtk_tree_path_free $PTH if [[ $ROW = "1" ]] then break else ((NR+=1)) fi done if [[ $NR == $TOTAL ]] then gtk_widget_show $WARNING else if [[ ${GAME[$NR]##*.} != "zip" ]] then gtk_widget_show $NOGAME else ZIP=`$UNZIP -l ${DIR##* }/${GAME[$NR]} 2>&1 | wc -l` gtk_progress_configure $BAR 0.0 0.0 $ZIP gtk_server_callback "update" gtk_range_get_value $SCALE let VOL=32-${GTK%%.*} gtk_spin_button_get_value_as_int $SIZE VALSIZE=$GTK gtk_toggle_button_get_active $RADIO1 if [[ $GTK -ne 1 ]] then gtk_toggle_button_get_active $RADIO2 if [[ $GTK -ne 1 ]] then MODE="opengl" VGA=2 else VGA=1 fi fi gtk_toggle_button_get_active $CHECK if [[ $GTK -eq 1 ]] then if [[ $MAME = *x11* ]] then ($MAME -volume -$VOL -vidmod $VGA -fullscreen -grabmouse ${GAME[$NR]%%.zip} > $MAMELOG 2>&1 &) else ($MAME -volume -$VOL -video $MODE ${GAME[$NR]%%.zip} > $MAMELOG 2>&1 &) fi else if [[ $MAME = *x11* ]] then ($MAME -volume -$VOL -vidmod $VGA -nofullscreen -nograbmouse ${GAME[$NR]%%.zip} -scale $VALSIZE > $MAMELOG 2>&1 &) else ($MAME -volume -$VOL -video $MODE -window ${GAME[$NR]%%.zip} > $MAMELOG 2>&1 &) fi fi # Only xmame writes extensive logging if [[ $MAME = *x11* ]] then gtk_widget_show_all $PROGRESS while [[ `cat $MAMELOG` != *done* && `cat $MAMELOG` != *mode*not*available* && `cat $MAMELOG` != *game*cannot*be*run* ]] do NR=`wc -l $MAMELOG` gtk_progress_set_value $BAR ${NR%% *} gtk_server_callback "update" done fi gtk_widget_hide $PROGRESS gtk_server_callback "update" fi fi } #-------------------------------------------------------------------------------------------------Main program # Output of mame binaries to this file MAMELOG=/tmp/mame.log # Check if UNZIP is installed UNZIP=`which unzip 2>/dev/null` if [[ -z $UNZIP ]] then echo "UNZIP not found on your system!" exit 1 fi # Check if MAME is installed MAME=`which xmame.x11 2>/dev/null` if [[ -z $MAME ]] then MAME=`which mame 2>/dev/null` if [[ -z $MAME ]] then echo "(x)MAME not found on your system!" echo "Download and install xMAME from http://x.mame.net or MAME from http://rbelmont.mameworld.info!" exit 1 else CFGDIR=$HOME/.mame/ CFGFILE=mame.ini fi else CFGDIR=$HOME/.xmame/ CFGFILE=xmamerc fi # Check existence of configfile in case of Xmame if [[ ! -f $CFGDIR$CFGFILE ]] then echo "No MAME configfile found at $CFGDIR$CFGFILE!" echo "Please setup a configfile as follows:" echo echo "xmame.x11 -showconfig > $HOME/.xmame/xmamerc" echo "-or-" echo "mame -showconfig > $HOME/.mame/mame.ini" echo echo "Edit the settings to your needs and run this frontend again." echo exit 1 fi # Check if list needs to be regenerated if [[ -f $CFGDIR/gamelist ]] then if [[ $MAME = *x11* && `head -1 $CFGDIR/gamelist` != `$MAME -version 2>/dev/null` ]] then echo "New version of MAME detected!" rm $CFGDIR/gamelist elif [[ $MAME != *x11* && `head -1 $CFGDIR/gamelist` != `$MAME -help | head -1` ]] then echo "New version of MAME detected!" rm $CFGDIR/gamelist fi fi # Create list with titles of all known games if [[ ! -f $CFGDIR/gamelist ]] then echo "Regenerating gamelist..." if [[ $MAME = *x11* ]] then $MAME -version > $CFGDIR/gamelist 2>/dev/null else $MAME -help | head -1 > $CFGDIR/gamelist 2>&1 fi $MAME -listfull >> $CFGDIR/gamelist 2>&1 fi # Find the directory where the ROMS are stored while read DIR do if [[ $DIR = rompath* ]] then DIR=${DIR##* } break fi done < $CFGDIR$CFGFILE # Check if there are new ROMS LS=`ls -l $DIR/*.zip | wc -l` if [[ -f $CFGDIR/titles ]] then WC=`wc -l $CFGDIR/titles` if [[ ${WC% *} -ne $LS ]] then echo "ROM list has changed!" rm -f $CFGDIR/titles fi fi # Create progress dialog gtk_init $NULL $NULL gtk_window_new 0 PROGRESS=$GTK gtk_widget_set_size_request $PROGRESS 330 30 gtk_window_set_resizable $PROGRESS 0 gtk_window_set_position $PROGRESS 1 gtk_window_set_icon_name $PROGRESS "joystick" gtk_progress_bar_new BAR=$GTK gtk_container_add $PROGRESS $BAR # Setup existing titles if [[ ! -f $CFGDIR/titles ]] then gtk_window_set_title $PROGRESS "'ROM list changed!'" gtk_progress_configure $BAR 0.0 0.0 $LS gtk_progress_bar_set_text $BAR "'Creating index for changed ROM list...'" gtk_widget_show_all $PROGRESS gtk_server_callback "update" NR=0 LS=`ls $DIR` for WHICH in $LS do FOUND=0 while read GLIST do if [[ ${WHICH%%.*} = ${GLIST%% *} ]] then echo ${GLIST//\'} >> $CFGDIR/titles ((NR+=1)) FOUND=1 break fi done < $CFGDIR/gamelist if [[ $FOUND -eq 0 ]] then echo "*** Game not in database: ${WHICH} ***" >> $CFGDIR/titles fi gtk_progress_set_value $BAR $NR gtk_server_callback "update" done gtk_widget_hide $PROGRESS gtk_server_callback "update" fi # Define GUI gtk_window_new 0 WIN=$GTK gtk_window_set_title $WIN "'BASH (x)Mame Frontend'" gtk_window_set_position $WIN 1 gtk_window_set_resizable $WIN 1 gtk_widget_set_size_request $WIN 425 370 gtk_window_set_icon_name $WIN "joystick" # Use async functionality for errors. Signal every 250 msecs, max time of doubleclick gtk_server_connect $WIN "show" "check-error-and-doubleclick" gtk_server_timeout 250 $WIN "show" # Presets gtk_frame_new NULL ITER=$GTK gtk_list_store_new 1 64 LST=$GTK gtk_tree_view_new_with_model $LST CHOICE=$GTK gtk_server_connect $CHOICE "button-press-event" $CHOICE 1 gtk_tree_view_set_headers_visible $CHOICE 1 gtk_tree_view_get_selection $CHOICE SEL=$GTK gtk_cell_renderer_text_new CELL=$GTK gtk_tree_view_column_new_with_attributes "'Games in $DIR - doubleclick to start'" $CELL "text" 0 $NULL COLUMN=$GTK gtk_tree_view_append_column $CHOICE $COLUMN gtk_tree_view_column_set_resizable $COLUMN 1 gtk_tree_view_column_set_clickable $COLUMN 1 gtk_scrolled_window_new $NULL $NULL SW=$GTK gtk_scrolled_window_set_policy $SW 1 1 gtk_scrolled_window_set_shadow_type $SW 1 gtk_container_add $SW $CHOICE TOTAL=0 while read TITLE do if [[ ${TITLE%% *} != "***" ]] then gtk_list_store_append $LST $ITER gtk_list_store_set $LST $ITER 0 "${TITLE#* }" -1 GAME[$TOTAL]=${TITLE%% *}.zip ((TOTAL+=1)) fi done < $CFGDIR/titles gtk_tree_view_get_hadjustment $CHOICE ADJ=$GTK gtk_button_new_with_label "Launch!" LAUNCH=$GTK gtk_widget_set_size_request $LAUNCH 50 25 gtk_button_new_with_label "About" ABOUT=$GTK gtk_widget_set_size_request $ABOUT 50 25 gtk_vseparator_new VSEP=$GTK gtk_check_button_new_with_label "Fullscreen" CHECK=$GTK gtk_hscale_new_with_range 0 32 1 SCALE=$GTK gtk_scale_set_draw_value $SCALE 1 gtk_scale_set_value_pos $SCALE 1 gtk_widget_set_size_request $SCALE 80 20 gtk_label_new "'Startup volume: '" LABEL2=$GTK gtk_hseparator_new HSEP=$GTK gtk_spin_button_new_with_range 1 5 1 SIZE=$GTK gtk_spin_button_set_value $SIZE 2 gtk_label_new "'Scale: '" LABEL1=$GTK gtk_radio_button_new_with_label_from_widget $NULL "'Software video'" RADIO1=$GTK gtk_radio_button_new_with_label_from_widget $RADIO1 "'Xvideo layer'" RADIO2=$GTK gtk_radio_button_new_with_label_from_widget $RADIO1 "'OpenGL renderer'" RADIO3=$GTK while read CONFIG do if [[ $CONFIG = volume* ]] then if [[ $MAME = *x11* ]] then gtk_range_set_value $SCALE ${CONFIG##* -} else gtk_range_set_value $SCALE ${CONFIG##* } fi fi if [[ $CONFIG = fullscreen* ]] then gtk_toggle_button_set_active $CHECK ${CONFIG##* } if [[ ${CONFIG##* } -eq 1 ]] then gtk_widget_set_sensitive $SIZE 0 gtk_widget_set_sensitive $LABEL1 0 fi fi if [[ $CONFIG = window* ]] then let TMP=1-${CONFIG##* } gtk_toggle_button_set_active $CHECK $TMP if [[ $TMP -eq 1 ]] then gtk_widget_set_sensitive $SIZE 0 gtk_widget_set_sensitive $LABEL1 0 fi fi if [[ $CONFIG = video* ]] then case ${CONFIG##* } in 0) gtk_toggle_button_set_active $RADIO1 1;; 1) gtk_toggle_button_set_active $RADIO2 1;; 2) gtk_toggle_button_set_active $RADIO3 1;; "soft") gtk_toggle_button_set_active $RADIO1 1;; "opengl") gtk_toggle_button_set_active $RADIO3 1;; esac fi done < $CFGDIR$CFGFILE gtk_frame_new NULL FRAME1=$GTK gtk_widget_set_size_request $FRAME1 200 70 gtk_frame_new NULL FRAME2=$GTK gtk_widget_set_size_request $FRAME2 155 70 gtk_frame_new NULL FRAME3=$GTK gtk_widget_set_size_request $FRAME3 60 70 # Window layout gtk_expander_new "Options" EXP=$GTK gtk_vbox_new 0 0 VBOX1=$GTK gtk_box_pack_start $VBOX1 $SW 1 1 1 gtk_box_pack_start $VBOX1 $HSEP 0 0 1 gtk_box_pack_start $VBOX1 $EXP 0 0 1 gtk_container_add $WIN $VBOX1 # Configpanel construction gtk_fixed_new FIXED=$GTK gtk_fixed_put $FIXED $FRAME1 1 1 gtk_fixed_put $FIXED $CHECK 5 10 gtk_fixed_put $FIXED $SIZE 140 10 gtk_fixed_put $FIXED $LABEL1 100 13 gtk_fixed_put $FIXED $LABEL2 5 45 gtk_fixed_put $FIXED $SCALE 105 45 gtk_fixed_put $FIXED $FRAME2 205 1 gtk_fixed_put $FIXED $RADIO1 210 5 gtk_fixed_put $FIXED $RADIO2 210 25 gtk_fixed_put $FIXED $RADIO3 210 45 gtk_fixed_put $FIXED $FRAME3 365 1 gtk_fixed_put $FIXED $ABOUT 370 10 gtk_fixed_put $FIXED $LAUNCH 370 40 gtk_container_add $EXP $FIXED gtk_widget_show_all $WIN gtk_widget_grab_focus $LAUNCH gtk_adjustment_set_value $ADJ 0.0 gtk_window_set_transient_for $PROGRESS $WIN gtk_window_set_position $PROGRESS 4 # Create warning gtk_message_dialog_new $WIN 0 3 2 "'\nERROR: select a game to launch!'" "''" WARNING=$GTK gtk_window_set_title $WARNING "'Error occured'" # Create error gtk_message_dialog_new $WIN 0 3 2 "'\nERROR: the game cannot be run!\n\nCheck \"$MAMELOG\" for details.'" "''" ERROR=$GTK gtk_window_set_title $ERROR "'Error occured'" # Create error gtk_message_dialog_new $WIN 0 3 2 "'\nERROR: not a game file!'" "''" NOGAME=$GTK gtk_window_set_title $NOGAME "'Error occured'" # Create error gtk_message_dialog_new $WIN 0 3 2 "'\nERROR: selected videomode not supported by \"${MAME##*/}\"!'" "''" NOMODE=$GTK gtk_window_set_title $NOMODE "'Error occured'" # Create ABOUT box gtk_server_version TMP="\t\t*** BASH Xmame Launcher ***\r\rBASH ${BASH_VERSION} with GTK-server $GTK.\r\r\tUsing \"${MAME##*/}\" to launch your games!\r\rVisit http://www.gtk-server.org/ for info on GTK-server!" gtk_message_dialog_new $WIN 0 0 2 "'$TMP'" "''" DIALOG=$GTK gtk_window_set_title $DIALOG "'About this program...'" gtk_window_set_title $PROGRESS "'Launching in progress!'" gtk_progress_bar_set_text $BAR "'Please wait...'" # Disable some options for MAME if [[ $MAME != *x11* ]] then gtk_widget_set_sensitive $RADIO2 0 gtk_widget_set_sensitive $SIZE 0 fi # Initialize variables EVENT=0 # Mainloop while [[ $EVENT != $EXIT && $EVENT != $WIN ]] do gtk_server_callback "wait" EVENT=$GTK case $EVENT in $LAUNCH) Launch_Game;; $CHOICE) gtk_server_mouse 2 if [[ $GTK -eq 1 ]] then ((DOUBLE+=1)) if [[ $DOUBLE -eq 2 ]] then let DOUBLE=0 Launch_Game fi fi;; $WARNING) gtk_widget_hide $WARNING;; $ABOUT) gtk_widget_show $DIALOG;; $DIALOG) gtk_widget_hide $DIALOG;; $ERROR) rm -f $MAMELOG gtk_widget_hide $ERROR;; $NOGAME) gtk_widget_hide $NOGAME;; $NOMODE) rm -f $MAMELOG gtk_widget_hide $NOMODE;; $CHECK) gtk_toggle_button_get_active $CHECK if [[ $GTK -eq 1 ]] then gtk_widget_set_sensitive $SIZE 0 gtk_widget_set_sensitive $LABEL1 0 else if [[ $MAME = *x11* ]] then gtk_widget_set_sensitive $SIZE 1 gtk_widget_set_sensitive $LABEL1 1 fi fi;; "check-error-and-doubleclick") # Here the doubleclick counter is reset as the timeout has passed let DOUBLE=0 # Use async to check if there are errors, (x)mame does not continue in such a situation if [[ -f $MAMELOG ]] then if [[ `cat $MAMELOG` = *game*cannot*be*run* ]] then gtk_widget_show $ERROR elif [[ `cat $MAMELOG` = *mode*not*available* ]] then gtk_widget_show $NOMODE fi fi;; esac done # Remove temp file rm -f $MAMELOG # Exit GTK gtk_server_exit # Exit shell exit 1