#!/bin/dash
# Fatdog Qemu VM Manager
# (C) James Budiono 2014
#
# License: GNU GPL Version 3 or later
#
# 1.0 - Sep 2014 - first release
# 1.1 - Oct 2014 - combine lib and program into one main file
#
# Note: the bits in Global Inits, which is part of GTK-server bash init code
#       is (C) Peter van Eerten. Everything else is (C) James Budiono
#
# Note to myself: gtk_tree_model_get must always be defined before use
# Note to myself: gtk_tree_store_set must always be defined before use

# std localisation stanza
export TEXTDOMAIN=fatdog
. gettext.sh
# performance tweak - use "C" if there is no localisation
! [ -e $TEXTDOMAINDIR/${LANG%.*}/LC_MESSAGES/$TEXTDOMAIN.mo ] &&
! [ -e $TEXTDOMAINDIR/${LANG%_*}/LC_MESSAGES/$TEXTDOMAIN.mo ] && LANG=C

### configuration
APPTITLE="$(gettext 'Qemu VM Manager')"
CONFIG_DIR=$HOME/.qemu-vm-manager
VM_DIR=$CONFIG_DIR/vm
[ -e /etc/qemu-vm-manager.conf ] && . /etc/qemu-vm-manager.conf
[ -e $CONFIG_DIR/local.conf ] && . $CONFIG_DIR/local.conf

#####################################################################
# run-time function library, called by the vm itself
#####################################################################
#$1-text, $2-backtitle
x_info() {
	if [ "$2" ]; then
		Xdialog --title "$APPTITLE" --backtitle "$2" --infobox "$1" 0 0 10000 
	else
		Xdialog --title "$APPTITLE" --infobox "$1" 0 0 10000 
	fi
}

#$1-prompt, $2-backtitle
x_inputbox() {
	if [ "$2" ]; then
		Xdialog --title "$APPTITLE" --stdout --backtitle "$2" --inputbox "$1" 0 0 
	else
		Xdialog --title "$APPTITLE" --stdout --inputbox "$1" 0 0 
	fi
}

#$1-prompt, $2-backtitle
x_yesno() {
	if [ "$2" ]; then
		Xdialog --title "$APPTITLE" --backtitle "$2" --yesno "$1" 0 0 
	else
		Xdialog --title "$APPTITLE" --yesno "$1" 0 0
	fi	
}

x_fselect() {
	if [ "$2" ]; then
		Xdialog --title "$APPTITLE" --no-buttons --stdout --backtitle "$2" --fselect "$1" 0 0 
	else
		Xdialog --title "$APPTITLE" --no-buttons --stdout --fselect "$1" 0 0
	fi	
}

PIDFILE="/tmp/qemu-vm.${0##*/}.pid"
lib_start_vm() {
	if ! lib_is_running > /dev/null; then
		if type qemu-system-$OPT_ARCH > /dev/null; then
			echo Starting "${0##/*}"
			
			# setup params
			[ $OPT_CPU -gt 1 ] && OPT_CPU="-smp cpus=$OPT_CPU" || OPT_CPU=""
			[ $OPT_KVM -eq 1 ] && OPT_KVM="-enable-kvm" || OPT_KVM=""
			[ "$OPT_CDROM" ] && OPT_CDROM="-cdrom $OPT_CDROM"
			[ "$OPT_HARDDISK" ] && OPT_HARDDISK="-hda $OPT_HARDDISK"
			[ "$OPT_SDCARD" ] && OPT_SDCARD="-sd $OPT_SDCARD"
			[ "$OPT_KERNEL" ] && OPT_KERNEL="-kernel $OPT_KERNEL"
			[ "$OPT_INITRD" ] && OPT_INITRD="-initrd $OPT_INITRD"
			[ $OPT_CDROM_BOOT -eq 1 ] && OPT_CDROM_BOOT="-boot d" || OPT_CDROM_BOOT=""
			[ $OPT_HARDDISK_BOOT -eq 1 ] && OPT_HARDDISK_BOOT="-boot c" || OPT_HARDDISK_BOOT=""
			[ $OPT_UEFI_BOOT -eq 1 ] && OPT_UEFI_BOOT="-bios uefi-bios.bin" || OPT_UEFI_BOOT=""

			case "$OPT_NET" in
				Normal) OPT_NET="" ;;
				Tap*)   OPT_NET="-net tap,script=no,downscript=no -net nic,model=e1000" 
				        OPT_PORTFWD=0 ;;
			esac
			
			case "$OPT_DPY" in
				Normal) OPT_DPY="" ;;
				VNC) find_free_display; OPT_DPY="-vnc :$disp";
				     echo "VNC display: $disp" ;;
			esac
			
			if [ $OPT_PORTFWD -eq 1 ] && [ "$OPT_GUEST_PORT" ] && [ "$OPT_HOST_PORT" ]; 
			then
				OPT_PORTFWD="-redir tcp:$OPT_HOST_PORT::$OPT_GUEST_PORT \
				             -redir udp:$OPT_HOST_PORT::$OPT_GUEST_PORT"
			else
				OPT_PORTFWD=""
			fi

			# run it
			qemu-system-$OPT_ARCH -m $OPT_RAM $OPT_CPU $OPT_KVM \
			$OPT_HARDDISK $OPT_CDROM $OPT_SDCARD \
			$OPT_CDROM_BOOT $OPT_HARDDISK_BOOT $OPT_UEFI_BOOT $OPT_DPY \
			$OPT_NET $OPT_PORTFWD $OPT_OTHER_PARAMS \
			$OPT_KERNEL $OPT_INITRD -append "$OPT_APPEND" &
			echo $! > $PIDFILE
		fi
	fi
}

lib_stop_vm() {
	if lib_is_running > /dev/null; then
		kill $pid
		rm -f "$PIDFILE"
	fi
}

lib_is_running() {
	if [ -e "$PIDFILE" ]; then
		read pid < "$PIDFILE"
		if kill -0 $pid 2>/dev/null; then
			echo "Running"
			return 0
		else
			rm -f "$PIDFILE"
		fi
	fi
	echo "Stopped"
	return 1
}

#returns: disp
lib_find_free_display() {
	for disp in $(seq 1 100); do
		! [ -e /tmp/.X${disp}-lock ] && break
	done
}

# input: $@
lib_command() {
	case "$1" in
		start) lib_start_vm   ;;
		stop)  lib_stop_vm    ;;
		state) lib_is_running ;;
	esac
}
[ "$RUNTIME_LIB" ] && return
#####################################################################
# end of function library, start of main program
#####################################################################

### configurations ###
# VM_LIST contents: 0-vmname 1-state
RUNTIME_LIB="$(readlink -f $0)"
VML_NAME=0 VML_STATE=1 VML_ARCH=2 VML_CPU=3 VML_RAM=4 VML_DISK=5
ARCH_OPTIONS="x86_64 arm"
DPY_OPTIONS="Normal VNC"
NET_OPTIONS="Normal Tap-device"

### run-time variables
SELECTED_VM=
SELECTED_ITER=
SELECTED_STATE=

#---------------------------------------------------------- Global Inits
### global variables for proper GTK-server
GTK= NULL="NULL"
PIPE=/tmp/gtk.bash.$$
trap 'stop-gtk-server -fifo $PIPE; cleanup; exit' HUP TERM INT # stop GTK-server in case of an error

### start GTK-server
gtk-server -fifo=$PIPE &
while [ ! -p $PIPE ]; do sleep 1; continue; done

### GTK-server helper functions
# Assignment function
define() { $2 $3 $4 $5 $6 $7 $8 $9; eval $1=\'"$GTK"\'; }

# Communicate with GTK-server
gtk()
{
/bin/echo "$@" > $PIPE
read GTK < $PIPE
}

#---------------------------------------------------------- GUI realization

init_gtk() {
	# init gtk
	gtk gtk_init
	
	# add GTK+ API not defined in gtk-server.cfg
	gtk gtk_server_define gtk_builder_new NONE WIDGET 0
	gtk gtk_server_define gtk_builder_add_from_file NONE INT 3 WIDGET STRING NULL
	gtk gtk_server_define gtk_builder_add_from_string NONE INT 4 WIDGET STRING INT NULL
	gtk gtk_server_define gtk_builder_get_object NONE WIDGET 2 WIDGET STRING
	gtk gtk_server_define gtk_builder_connect_signals NONE NONE 2 WIDGET NULL	
	
	gtk gtk_server_define gtk_tree_store_append NONE NONE 3 WIDGET WIDGET WIDGET
	gtk gtk_server_define gtk_tree_view_expand_all NONE NONE 1 WIDGET
	gtk gtk_server_define gtk_tree_store_clear NONE NONE 1 WIDGET
	
	gtk gtk_server_define gtk_tree_view_get_cursor NONE NONE 3 WIDGET PTR_WIDGET NULL
	gtk gtk_server_define gtk_tree_path_to_string NONE STRING 1 WIDGET	
	gtk gtk_server_define gtk_tree_model_get_iter_first NONE BOOL 2 WIDGET WIDGET
	gtk gtk_server_define gtk_tree_model_iter_children NONE BOOL 3 WIDGET WIDGET WIDGET
	gtk gtk_server_define gtk_tree_model_get_path NONE WIDGET 2 WIDGET WIDGET
	gtk gtk_server_define gtk_tree_path_free NONE NONE 1 WIDGET 
	gtk gtk_server_define gtk_tree_view_set_cursor NONE NONE 4 WIDGET WIDGET WIDGET BOOL
	gtk gtk_server_define gtk_tree_iter_copy NONE WIDGET 1 WIDGET
	
	gtk gtk_server_define gtk_notebook_next_page NONE NONE 1 WIDGET	
	gtk gtk_server_define gtk_notebook_prev_page NONE NONE 1 WIDGET	
	gtk gtk_server_define gtk_label_set_markup NONE NONE 2 WIDGET STRING
	gtk gtk_server_define gtk_button_get_label NONE STRING 1 WIDGET
	
	gtk gtk_server_define gtk_hseparator_new  NONE WIDGET 0
	
	# hack for getting unlimited string length and leak-free operation with gtk_tree_get_model
	# see below
	gtk gtk_server_define strcat NONE STRING 2 POINTER STRING
	gtk gtk_server_define memcpy NONE NONE 3 PTR_LONG POINTER INT		
}

###################### GUI helpers ########################

### helper - leak-free, unlimited string length from gtk_tree_model_get with unpatched gtk-server
# $1-tree $2-iter $3-index $4-result name
# static variable to speed up stuff
STATIC_BUF=
STATIC_ADDRESS=
tree_model_get_string() {
	[ -z "$STATIC_BUF" ] && define STATIC_BUF gtk malloc 32 # gtk-server MAX_DIG is 32 bytes
	
	gtk gtk_server_define gtk_tree_model_get NONE NONE 5 WIDGET WIDGET INT POINTER INT # ensure correct definition
	gtk gtk_tree_model_get $1 $2 $3 $STATIC_BUF -1
	
	gtk memcpy NULL $STATIC_BUF 32	# print address stored in $BUF --> *buf
	STATIC_ADDRESS=$GTK
	if [ $STATIC_ADDRESS -ne 0 ]; then
		gtk strcat $STATIC_ADDRESS \"\" # get string point by address --> **buf	
		eval $4=\'"$GTK"\'				# this will be our return value	
		gtk free $STATIC_ADDRESS		# this was dynamically alloc-ed by gtk_tree_model_get, now free it
	else
		eval $4=\"\"
	fi
}

### helper - display progress
# $1-text
flush_gtk() {
	gtk gtk_events_pending
	while [ $GTK -eq 1 ]; do
		gtk gtk_main_iteration
		gtk gtk_events_pending
	done	
}
#display_progress() {
#	gtk gtk_label_set_text $INFO_LABEL \"$1\"
#	gtk gtk_widget_show $INFO_WINDOW
#	flush_gtk
#}
#hide_progress() {
#	gtk gtk_widget_hide $INFO_WINDOW
#	flush_gtk
#}

cleanup() {
	:
}

# $1-combobox name, will be populated from $1_OPTIONS
populate_combo_box() {
	while [ "$1" ]; do
		eval pp=\$${1}_OPTIONS
		for p in $pp; do
			eval gtk gtk_combo_box_append_text \$${1}_COMBO $p
		done
		shift
	done
}

# $1-combo_box, $2-entry (entry list is $1_OPTIONS)
set_combo_box_entry() {
	local n entry combo
	combo="$1" entry="$2" n=0
	eval set -- \$${1}_OPTIONS
	while [ "$1" ]; do
		[ "$1" = "$entry" ] && break
		n=$((n+1))
		shift
	done
	eval gtk gtk_combo_box_set_active \$${combo}_COMBO $n
}


### load list of available VMs
load_vm_list() {
	local vmfile vmname state disksize ITER
	
	define ITER gtk gtk_server_opaque
	gtk gtk_tree_model_get_iter_first $VM_LIST $ITER

	# 5 fields to add including type and sentinel
	gtk gtk_server_define gtk_list_store_set NONE NONE 15 WIDGET WIDGET \
		INT STRING INT STRING INT STRING INT STRING INT STRING INT STRING INT 
	
	gtk gtk_list_store_clear $VM_LIST
	ls $VM_DIR/* 2>/dev/null | sed '/.pre$/d;/.post$/d; s|.*/||; p; y/-/ /;' | 
	while read -r vmfile; do
		read -r vmname
		state=$(sh $VM_DIR/${vmfile} state)
		[ -z "$state" ] && state="Stopped"
		
		. $VM_DIR/${vmfile}
		disksize=""
		[ "$OPT_HARDDISK" ] && disksize="HD: $(du -h "$OPT_HARDDISK" | awk '{print $1}') "
		[ "$OPT_SDCARD" ] && disksize="${disksize}SD: $(du -h "$OPT_SDCARD" | awk '{print $1}')"
		
		gtk gtk_list_store_append $VM_LIST $ITER		
		gtk gtk_list_store_set $VM_LIST $ITER \
		    $VML_NAME \""$vmname"\"   $VML_STATE \""$state"\" \
		    $VML_ARCH \""$OPT_ARCH"\" $VML_CPU \""$OPT_CPU"\" \
		    $VML_RAM  \""$OPT_RAM"\"  $VML_DISK \""$disksize"\" -1
	done
	gtk free $ITER
	
	# set pointer to the first item
	define TPATH gtk gtk_tree_path_new_from_string 0
	gtk gtk_tree_view_set_cursor $VM_TREEVIEW $TPATH NULL 0
	gtk gtk_tree_path_free $TPATH	
}


###################### config dialog helpers ########################


# $1-vmname
vmname_to_path() {
	echo "${1}" | sed 'y/ /-/'
}

set_default_vm_params() {
	# clear all
	for p in ARCH DPY NET HARDDISK CDROM SDCARD KERNEL INITRD \
			 CPU RAM HOST_PORT GUEST_PORT OTHER_PARAMS APPEND \
			 KVM CDROM_BOOT HARDDISK_BOOT PORTFWD; do
		eval unset OPT_\$p
	done
	# default values
	OPT_ARCH=x86_64 OPT_DPY="Normal" OPT_NET="Normal"
	OPT_CPU=1 OPT_RAM=512 OPT_KVM=1 OPT_CDROM_BOOT=1 OPT_UEFI_BOOT=0
	OPT_KVM=1 OPT_CDROM_BOOT=1 OPT_HARDDISK_BOOT=1 OPT_PORTFWD=0
}

# set vmconfig dialog values from OPT_*, $1-VM Name
set_vmconfig_dialog_param() {
	local p pp
	gtk gtk_window_set_title $VM_CONFIG "\"Editing $1\""
	for p in ARCH DPY NET; do
		eval set_combo_box_entry $p \$OPT_$p;
	done
	
	for p in HARDDISK CDROM SDCARD KERNEL INITRD; do
		eval pp=\$OPT_$p
		eval gtk gtk_file_chooser_set_filename \$${p}_CHOOSER "\\\"$pp\\\""
	done

	for p in CPU RAM HOST_PORT GUEST_PORT OTHER_PARAMS APPEND; do
		eval pp=\$OPT_$p
		eval gtk gtk_entry_set_text \$${p}_ENTRY "\\\"$pp\\\""
	done
	
	for p in KVM CDROM_BOOT HARDDISK_BOOT PORTFWD UEFI_BOOT; do
		eval pp=\$OPT_$p
		eval gtk gtk_toggle_button_set_active \$${p}_CHECK $pp
	done	
}

# get values from vmconfig dialog, output in OPT_*
get_vmconfig_dialog_param() {
	for p in ARCH DPY NET; do
		eval define OPT_$p gtk gtk_combo_box_get_active_text \$${p}_COMBO
	done

	for p in HARDDISK CDROM SDCARD KERNEL INITRD; do
		eval define OPT_$p gtk gtk_file_chooser_get_filename \$${p}_CHOOSER
	done
	! [ -f "$OPT_HARDDISK" ] && OPT_HARDDISK=""
	! [ -f "$OPT_CDROM" ] && OPT_CDROM=""
	! [ -f "$OPT_SDCARD" ] && OPT_SDCARD=""
	! [ -f "$OPT_KERNEL" ] && OPT_KERNEL=""
	! [ -f "$OPT_INITRD" ] && OPT_INITRD=""

	for p in CPU RAM HOST_PORT GUEST_PORT OTHER_PARAMS APPEND; do
		eval define OPT_$p gtk gtk_entry_get_text \$${p}_ENTRY
	done
	
	for p in KVM CDROM_BOOT HARDDISK_BOOT PORTFWD UEFI_BOOT; do
		eval define OPT_$p gtk gtk_toggle_button_get_active \$${p}_CHECK
	done	
}

# $1-full path to file, OPT_* parameters
save_config() {
	{
		# header
		echo "#!/bin/sh"
		# configs
		for p in ARCH DPY NET HARDDISK CDROM SDCARD KERNEL INITRD \
		         CPU RAM HOST_PORT GUEST_PORT OTHER_PARAMS APPEND \
		         KVM CDROM_BOOT HARDDISK_BOOT PORTFWD UEFI_BOOT; do
			eval echo OPT_\$p=\\\'\$OPT_$p\\\'
		done
		# footer
		cat << EOF
RUNTIME_LIB='$RUNTIME_LIB'
. \$RUNTIME_LIB
[ -e "\${0}.pre" ]  && . "\${0}.pre" "\$@"
lib_command "\$@"
[ -e "\${0}.post" ] && . "\${0}.post" "\$@"
EOF
	} > "$1"
	chmod +x "$1"
}


###################### signal handlers ########################


# returns SELECTED_VM, SELECTED_ITER, SELECTED_STATE
get_selected_vm() {
	local BUF SELECTED_PATH
	define BUF gtk malloc 1024	
	
	SELECTED_STATE="Stopped" SELECTED_VM=""
	define SELECTED_PATH gtk gtk_tree_view_get_cursor $VM_TREEVIEW $BUF
	#gtk gtk_tree_path_to_string $SELECTED_PATH; echo $GTK
	if [ "$SELECTED_PATH" -eq 0 ]; then # empty tree
		gtk free $BUF
		return
	fi
		
	gtk gtk_tree_model_get_iter $VM_LIST $SELECTED_ITER $SELECTED_PATH
	gtk gtk_tree_path_free $SELECTED_PATH
	gtk gtk_tree_model_get $VM_LIST $SELECTED_ITER $VML_NAME $BUF -1
	#echo "$GTK"
	SELECTED_VM="$GTK"
	gtk free $BUF
	
	SELECTED_STATE=$(sh "$VM_DIR/$(vmname_to_path "$SELECTED_VM")" state)
	[ -z "$SELECTED_STATE" ] && SELECTED_STATE="Stopped"
}

# $1-selected iter, $2-vmname, returns "STATE"
update_state() {
	local vmfile disksize
	vmfile="$VM_DIR/$(vmname_to_path "$2")"
	
	STATE=$(sh "$vmfile" state)
	[ -z "$STATE" ] && STATE="Stopped"
	. $vmfile
	disksize=""
	[ "$OPT_HARDDISK" ] && disksize="HD: $(du -h "$OPT_HARDDISK" | awk '{print $1}') "
	[ "$OPT_SDCARD" ] && disksize="${disksize}SD: $(du -h "$OPT_SDCARD" | awk '{print $1}')"	

	# 5 fields to add including type and sentinel
	gtk gtk_server_define gtk_list_store_set NONE NONE 13 WIDGET WIDGET \
		INT STRING INT STRING INT STRING INT STRING INT STRING INT 

	gtk gtk_list_store_set $VM_LIST $1 \
	    $VML_STATE \""$STATE"\" \
	    $VML_ARCH \""$OPT_ARCH"\" $VML_CPU \""$OPT_CPU"\" \
	    $VML_RAM  \""$OPT_RAM"\"  $VML_DISK \""$disksize"\" -1
}

# $1-start/stop, $2-vmname, $3-iter
manage_vm() {
	local STATE
	sh "$VM_DIR/$(vmname_to_path "$2")" $1
	update_state $3 "$2"
	SELECTED_STATE="$STATE"
}

#$1-vm name, $2-iter
edit_vm() {
	local vmfile
	vmfile="$VM_DIR/$(vmname_to_path "$1")"
	set_default_vm_params # so missing parameters are initted properly
	. $vmfile
	set_vmconfig_dialog_param "$1"
	define RESULT gtk gtk_dialog_run $VM_CONFIG
	if [ $RESULT -eq 0 ]; then
		get_vmconfig_dialog_param
		save_config "$vmfile"
		[ "$2" ] && update_state "$2" "$1"
	fi
}

create_vm() {
	local vmname vmfile
	if vmname=$(x_inputbox "Please enter the name for the new VM"); 
	then
		vmfile=$VM_DIR/$(vmname_to_path "$vmname")
		set_default_vm_params
		save_config $vmfile
		edit_vm "$vmname"
		if [ $RESULT -eq 0 ]; then
			load_vm_list
		else
			rm -f $vmfile
		fi
	fi
}

#$1-vm name, $2-state
delete_vm() {
	if ! [ "$2" = "Stopped" ]; then
		x_info "Cannot delete $1 becase it is currently running."
		return
	fi
	
	if x_yesno "Are you sure you want to delete $1?"; then
		rm -f "$VM_DIR/$(vmname_to_path "$1")"
		load_vm_list
	fi
}

#$1-vm name, $2-state
rename_vm() {
	local new old
	
	if ! [ "$2" = "Stopped" ]; then
		x_info "Cannot rename $1 becase it is currently running."
		return
	fi
	
	if new=$(x_inputbox "Please enter the new name for the VM" "Renaming $1");
	then
		new="$VM_DIR/$(vmname_to_path "$new")"
		old="$VM_DIR/$(vmname_to_path "$1")"
		mv "$old" "$new"
		load_vm_list
	fi
}

create_new_disk() {
	local newdisk newsize
	if newdisk=$(x_fselect "" "Choose the location and name of the new disk image");
	then
		if [ -e $newdisk ]; then
			x_info "$newdisk already exist. Please choose another name."
		else
			if newsize=$(x_inputbox "Please specify the size in MB" "Creating new disk $newdisk");
			then
				dd if=/dev/zero of=$newdisk bs=1M seek=$newsize count=0
				x_info "$newdisk: $newsize MB" "New disk image has been created."			
			fi
		fi
	fi
}

#############################   main   ##############################

### gtk-server localisation stanza
gtk gtk_server_define setlocale NONE STRING 2 INT STRING
gtk gtk_server_define bindtextdomain NONE STRING 2 STRING STRING
gtk gtk_server_define textdomain NONE STRING 1 STRING
gtk setlocale 6 \"\" # 6 corresponds to LC_ALL
gtk bindtextdomain $TEXTDOMAIN $TEXTDOMAINDIR
gtk textdomain $TEXTDOMAIN

### sanity check
for p in $ARCH_OPTIONS; do
	type qemu-system-$p > /dev/null || 
	x_info "qemu-system-$p is missing. Please install Qemu package."
done

### load GUI
init_gtk
define BUILDER gtk gtk_builder_new
define RESULT gtk gtk_builder_add_from_file $BUILDER "${0}.xml"
gtk gtk_builder_connect_signals $BUILDER

# main window
define MAIN_WINDOW gtk gtk_builder_get_object $BUILDER main_window
define VM_TREEVIEW gtk gtk_builder_get_object $BUILDER vmtreeview
define VM_LIST gtk gtk_builder_get_object $BUILDER vmliststore

define VMEXIT_BUTTON gtk gtk_builder_get_object $BUILDER vmexit_toolbutton
define VMSTART_BUTTON gtk gtk_builder_get_object $BUILDER vmstart_toolbutton
define VMSTOP_BUTTON gtk gtk_builder_get_object $BUILDER vmstop_toolbutton
define VMEDIT_BUTTON gtk gtk_builder_get_object $BUILDER vmedit_toolbutton
define VMDELETE_BUTTON gtk gtk_builder_get_object $BUILDER vmdelete_toolbutton
define VMRENAME_BUTTON gtk gtk_builder_get_object $BUILDER vmrename_toolbutton
define VMCREATE_BUTTON gtk gtk_builder_get_object $BUILDER vmcreate_toolbutton
define VMNEW_DISK_BUTTON gtk gtk_builder_get_object $BUILDER vmnew_disk_toolbutton

# vm config dialog window
define VM_CONFIG gtk gtk_builder_get_object $BUILDER vmconfig_dialog
define ARCH_COMBO gtk gtk_builder_get_object $BUILDER arch_combo
define RAM_ENTRY gtk gtk_builder_get_object $BUILDER ram_entry
define CPU_ENTRY gtk gtk_builder_get_object $BUILDER cpu_entry
define KVM_CHECK gtk gtk_builder_get_object $BUILDER kvm_check
define UEFI_BOOT_CHECK gtk gtk_builder_get_object $BUILDER uefi_check
define HARDDISK_BOOT_CHECK gtk gtk_builder_get_object $BUILDER harddisk_radio
define CDROM_BOOT_CHECK gtk gtk_builder_get_object $BUILDER cdrom_radio
define HARDDISK_CHOOSER gtk gtk_builder_get_object $BUILDER harddisk_chooser
define CDROM_CHOOSER gtk gtk_builder_get_object $BUILDER cdrom_chooser
define SDCARD_CHOOSER gtk gtk_builder_get_object $BUILDER sdcard_chooser
define KERNEL_CHOOSER gtk gtk_builder_get_object $BUILDER kernel_chooser
define INITRD_CHOOSER gtk gtk_builder_get_object $BUILDER initrd_chooser
define DPY_COMBO gtk gtk_builder_get_object $BUILDER display_combo
define NET_COMBO gtk gtk_builder_get_object $BUILDER net_combo
define HOST_PORT_ENTRY gtk gtk_builder_get_object $BUILDER host_port_entry
define GUEST_PORT_ENTRY gtk gtk_builder_get_object $BUILDER guest_port_entry
define PORTFWD_FRAME gtk gtk_builder_get_object $BUILDER portfwd_frame
define PORTFWD_CHECK gtk gtk_builder_get_object $BUILDER portfwd_check
define OTHER_PARAMS_ENTRY gtk gtk_builder_get_object $BUILDER other_params_entry
define APPEND_ENTRY gtk gtk_builder_get_object $BUILDER append_entry

gtk g_object_unref $BUILDER

### connect signals
gtk gtk_server_connect $MAIN_WINDOW delete-event finish
gtk gtk_server_connect $VM_TREEVIEW cursor-changed vm-cursor-changed

gtk gtk_server_connect $VMEXIT_BUTTON clicked finish
gtk gtk_server_connect $VMSTART_BUTTON clicked start-vm
gtk gtk_server_connect $VMSTOP_BUTTON clicked stop-vm
gtk gtk_server_connect $VMEDIT_BUTTON clicked edit-vm
gtk gtk_server_connect $VMCREATE_BUTTON clicked create-vm
gtk gtk_server_connect $VMDELETE_BUTTON clicked delete-vm
gtk gtk_server_connect $VMRENAME_BUTTON clicked rename-vm
gtk gtk_server_connect $VMNEW_DISK_BUTTON clicked create-new-disk

### init
populate_combo_box ARCH DPY NET
define SELECTED_ITER gtk gtk_server_opaque
mkdir -p $CONFIG_DIR $VM_DIR
load_vm_list

### show main window and enter main loop
gtk gtk_widget_show $MAIN_WINDOW
while true; do 
	#hide_progress
	define EVENT gtk gtk_server_callback wait
	case $EVENT in
		finish|quit-clicked) break ;;
		vm-cursor-changed) get_selected_vm ;; # SELECTED_VM SELECTED_ITER SELECTED_STATE
		start-vm) manage_vm start "$SELECTED_VM" "$SELECTED_ITER" ;;
		stop-vm)  manage_vm stop "$SELECTED_VM" "$SELECTED_ITER" ;;
		edit-vm)  edit_vm "$SELECTED_VM" "$SELECTED_ITER" ;;
		create-vm) create_vm ;;
		delete-vm) delete_vm "$SELECTED_VM" "$SELECTED_STATE" ;;
		rename-vm) rename_vm "$SELECTED_VM" "$SELECTED_STATE" ;;
		create-new-disk) create_new_disk ;;
	esac
done
gtk gtk_server_exit 
cleanup
