#!/usr/bin/env perl
my $ID = q$Id: lyricue_server,v 1.114 2006/11/23 01:04:09 cjdebenh Exp $;
#****** lyricue_server/pod
# NAME
#   Pod documentation
# DESCRIPTION
#   Documentation for lyricue_server that is displayed by perldoc
# SOURCE
#

=head1 NAME

lyricue_server - Lyricue Server

=head1 SYNOPSIS

lyricue_server [ C<-v|-b> C<-m> C<-d> ]

=head1 DESCRIPTION

This script creates a fullscreen window that is used to display song lyrics (normally on a second screen, projector or similar) OR a scaled server window that can be used to preview songs a given screen.

=head1 OPTIONS

=over 4

=item B<-v>

Get lyricue_server version

=item B<-d>

Turn on debugging mode. Prints out debugging information

=item B<-m>

Run in miniserv mode (used for preview window in interface)


=head1 CONFIGURATION

All configuration is done by editing the configuration section in the program

=head1 REQUIRES

Perl 5.6 or later, DBI::MySQL, Gtk2-Perl, MySql database

=head1 AUTHOR

Chris Debenham <chris@adebenham.com>

=head1 COPYRIGHT

This program is released under the GPL (http://www.gnu.org/copyleft/gpl.html)

=head1 VERSION

Lyricue_server Version 1.9.4

=cut

#***

#****** lyricue_server/setup
# NAME
#   Setup section
# DESCRIPTION
#   Loads required modules, sets some global variables,
#   and other global things
# SOURCE
#

#
# Modules we use.
#
use strict;
use DBI;
use POSIX;
use IO::Socket::INET;    #use Socket;
use Encode;
die "The gtk2-perl bindings could not be initialized; we need them to run!\n"
  unless (Gtk2->init_check);
use Gtk2::Pango;
use Gtk2::GladeXML;
use Gtk2::Gdk::Keysyms;
use Glib;
eval { require Locale::gettext };

if ($@) {
    print "Gettext not available, english text only\n";

    sub gettext {
        return $@;
    }
} else {
    import Locale::gettext;
    textdomain('lyricue');

    # Hack to get this working under mandrake
    use lib qw(/usr/lib/libDrakX);
    eval { require c::stuff };
    if ($@) {
        bind_textdomain_codeset('lyricue', "UTF-8");
    } else {
        c::stuff::bind_textdomain_codeset('lyricue', "UTF-8");
    }
}
binmode(STDOUT, ":utf8");
binmode(STDERR, ":utf8");

# convenience variables for true and false
use constant FALSE => 0;
use constant TRUE  => 1;

# Transition types
use constant NORMAL     => 0;
use constant SLIDE_TEXT => 1;
use constant WIPE       => 2;
use constant CLIP       => 3;

# Transition directions
use constant NONE  => 0;
use constant UP    => 1;
use constant DOWN  => 2;
use constant RIGHT => 4;
use constant LEFT  => 8;
use constant WAIT  => 16;

# Server types
use constant CANVAS => 0;
use constant SIMPLE => 1;
use constant TRANS  => 2;

#
# Site Configuration.  You should only have to
# edit this section.
#

my ($globals);
$globals->{'etcdir'}   = "/etc/lyricue/";
$globals->{'basedir'}  = $ENV{'HOME'} . "/.lyricue/";
$globals->{'sharedir'} = "/usr/share/lyricue/";

#
# You shouldn't have to change anything after this line
#

$globals->{'version'}     = "1.9.4";
$globals->{'accessfile'}  = $globals->{'etcdir'} . "access.conf";
$globals->{'defaultconf'} = $globals->{'etcdir'} . "default.conf";
$globals->{'configfile'}  = $globals->{'basedir'} . "config2";
$globals->{'gladefile'}   = $globals->{'sharedir'} . "lyricue.glade";
$globals->{'host'}        = "localhost";
$globals->{'lyricdb'}     = "lyricDb";
$globals->{'mediadb'}     = "mediaDb";
$globals->{'bibledb'}     = "";
$globals->{'biblename'}   = "";
$globals->{'usesword'}    = TRUE;
$globals->{'bg_file'}     = "/tmp/lyricue.bg";
$globals->{'preview'}     = FALSE;
$globals->{'use_port'}      = "2346";    #port used for lyric server socket
$globals->{'server_port'}   = "2346";    #port used for lyric server socket
$globals->{'preview_port'}  = "2347";    #port used for lyric server socket
$globals->{'miniview_port'} = "2348";    #port used for lyric server socket
$globals->{'run_windowed'}  = FALSE;
$globals->{'server_type'}   = CANVAS;
$globals->{'blanked_state'} = TRUE;
$globals->{'diatheke'} = `which diatheke`;
chomp $globals->{'diatheke'};

if ($globals->{'diatheke'} eq "") {
    $globals->{'diatheke'} = "true";
}

my $DEBUG           = FALSE;
my $exec_pid        = 0;
my $MINISERV        = -1;
my $MINISCALE       = 1;       # Default Scaling
my $LOOPPARENT      = -1;      #Absolute parent for looping in sublists
my $current_list    = 1;
my $current_item    = 1;
my $preview_item    = 0;
my $current_point   = -1;
my $steps           = 20;
my $bgimage_changed = FALSE;
my $headerHeight    = 0;
my ($background);
my @points;

my ($errorcodes);
$errorcodes->{'lyricdbopen'} = Encode::decode(
    "utf-8",
    gettext(
"I'm sorry but I could not open the lyric database.\nPlease confirm that Lyricue is installed correctly and MySql is running"
    )
);
$errorcodes->{'bibledbopen'} = Encode::decode(
    "utf-8",
    gettext(
"I'm sorry but I could not open the bible database.\nPlease confirm that Lyricue is installed correctly and the current bible database exists.\nThe requested database was named "
    )
  ),
  $errorcodes->{'sqlprepare'} =
  Encode::decode("utf-8", gettext("Unable to prepare query.\nHas mysql died?"));
$errorcodes->{'sqlexecute'} =
  Encode::decode("utf-8", gettext("Unable to execute query.\nHas mysql died?"));
$errorcodes->{'socketopen'} = Encode::decode(
    "utf-8",
    gettext(
"Sorry, I was unable to listen on the network.\nPlease make sure I am not already running"
    )
);

#***

#****** lyricue_server/main_code
# NAME
#   main_code - main code section, not in subroutine
# SYNOPSIS
#   No output
# FUNCTION
#   Figure out where to go
# INPUTS
#   Commandline
# OUTPUT
#   Everything
# SOURCE
#

# Parse command line arguments
if ($ARGV[0]) {
    foreach (0 .. (@ARGV - 1)) {
        if ($ARGV[$_] eq "-v") {
            print "Lyricue Server version " . $globals->{'version'} . "\n";
            exit;
        } elsif ($ARGV[$_] eq "-t") {
            $globals->{'server_type'} = TRANS;
        } elsif ($ARGV[$_] eq "-f") {
            $globals->{'server_type'} = SIMPLE;
        } elsif ($ARGV[$_] eq "-d") {
            $DEBUG = 1;
            debug ("Lyric Server version " . $globals->{'version'} . "\n".$ID);
        } elsif ($ARGV[$_] eq "-m") {
            $MINISERV = $ARGV[$_ + 1];
            if (!$MINISERV =~ /\d+$/) {
                $MINISERV = 0;
            }
        } elsif ($ARGV[$_] eq "-r") {
            $globals->{'host'} = $ARGV[$_ + 1];
            $ARGV[$_ + 1] = "";
        } elsif ($ARGV[$_] eq "-p") {
            $globals->{'use_port'} = $ARGV[$_ + 1];
        } elsif ($ARGV[$_] eq "-sqlite") {
            $globals->{'force_sqlite'} = TRUE;
        } elsif ($ARGV[$_] eq "-w") {
            $globals->{'run_windowed'} = TRUE;
        } elsif (($ARGV[$_] =~ /^\d+$/) || ($ARGV[$_] eq "")) {
        } else {
            print "\nUsage: lyricue_server <-v> <-m> <-c> <-d> <-r hostname> <-w>\n\n";
            print "\t-v: Prints Lyricue version information & exits\n";
            print "\t-d: Prints debugging messages\n";
            print "\t-r: Remote hostname where sql server is running\n";
            print "\t-m: Runs in miniserv mode (acts as a preview window)\n";
            print "\t-w: Runs in a window (rather than fullscreen\n";
            print "\t-t: Runs with tranparent server\n";
            print "\t-f: Runs with simple server\n";
            print "\t-sqlite: Force usage of sqlite\n";
            exit;
        }
    } ## end foreach (0 .. (@ARGV - 1))
} ## end if ($ARGV[0])

# Read the config file
db_check_app();
if (!-w $globals->{'configfile'}) {
    if (!-d $globals->{'basedir'}) {
        mkdir $globals->{'basedir'}, 0777;
    }
    system("cp " . $globals->{'defaultconf'} . " " . $globals->{'configfile'});
}
my $config = load_config();
if ($config->{'ServerType'} eq "trans") {
    $globals->{'server_type'} = TRANS;
} elsif ($config->{'ServerType'} eq "simple") {
    $globals->{'server_type'} = SIMPLE;
}

my ($contents, @preset);
if (!($config->{'Colour'})) {
    $config->{'Colour'} = "#ffffff";
}

my ($bibleDbh);
foreach (keys %$config) {
    if (/^Preset/) {
        my $num = $_;
        $num =~ s/^Preset//g;
        $preset[$num] = $config->{$_};
    } elsif (/^DefBible/) {
        my @tmp = split(/;/, $config->{$_}, 2);
        my @tmp2 = split(/:/, $tmp[0], 2);
        $globals->{'bibledb'} = $tmp2[0];
        change_to_db($globals->{'bibledb'}, $tmp2[1]);
    }
}

my $pid =
`ps -e -o pid,cmd | grep "Lyricue Server on port $globals->{'use_port'}" | grep -v "grep" |sed -e 's/^ *//g' | cut -f1 -d" " `;
chomp($pid);
if ($pid) {
    debug("Lyricue Server was already running on given port, killing");
    kill 9, $pid;
}

# Open lyricDB and mediaDB
my $lyricDbh = db_connect($globals->{'lyricdb'}, $errorcodes->{'lyricdbopen'});
my $mediaDbh = db_connect($globals->{'mediadb'}, $errorcodes->{'mediadbopen'});

# Set current_page to a valid value
my $query = "SELECT MIN(playorder) FROM playlist";
debug($query);
my $sth = $lyricDbh->prepare($query)
  || display_fatal($errorcodes->{'sqlprepare'}, $!);
my $rv = $sth->execute
  || display_fatal($errorcodes->{'sqlexecute'}, $!);
my @row = $sth->fetchrow_array();
$current_item = $row[0];

# Create/Update tracker field
check_tracker();

# Create the window
debug("Creating window");
create_window();

debug("Loading background");
change_backdrop($config->{'BGImage'});

# init the TCP mode
my ($socket);
my @cnx = ();
my $con = 0;
my ($name, $aliases, $proto) = getprotobyname('tcp');

if ($globals->{'use_port'} !~ /^\d+$/) {
    ($name, $aliases, $globals->{'use_port'}) =
      getservbyport($globals->{'use_port'}, 'tcp');
}

do_pending();

debug("Main program running");

my $sockaddr = 'S n a4 x8';
$socket = IO::Socket::INET->new(
    Listen    => SOMAXCONN,
    LocalPort => $globals->{'use_port'},
    Reuse     => 1,
    Proto     => 'tcp'
);
$socket or display_fatal($errorcodes->{'socketopen'}, $!);
$0 = "Lyricue Server on port " . $globals->{'use_port'};
my $h;
use IO::Select;
my $s = IO::Select->new($socket);
debug("Listening on " . $globals->{'use_port'});

# Gtk event loop
while (1) {
    my @ready = $s->can_read(1);
    foreach $h (@ready) {
        check_socket($socket);
    }
    do_pending();
}

# Should never get here
exit(0);

#***

#****f* lyricue_server/process_input
# NAME
#   process_input
# SYNOPSIS
#   process_input ()
# FUNCTION
#   Reads from the socket and processes whatever is there
# INPUTS
#   text from socket
# OUTPUT
#   Update lyric screen
# SOURCE
#
sub process_input {
    debug("Beginning transaction from socket");
    my ($input) = @_;
    my $status = "";

    debug("Received :" . $input);
    if ($input) {
        Encode::decode("utf-8", $input);
        chomp($input);
        my @line = split(/:/, $input);
        $_ = lc($line[0]);

        if (/^status$/) {
            $status = "Status,W:"
              . $config->{'Width'} . ",H:"
              . $config->{'Height'} . ",F:"
              . $config->{'Main'} . ",B:"
              . $globals->{'bibledb'};
            if ($line[1] eq "previewon") {
                $globals->{'preview'} = TRUE;
            } else {
                $globals->{'preview'} = FALSE;
            }
        } elsif (/^snapshot$/) {
            $status = "";
            if ($globals->{'snapshot_changed'} || ($line[1] eq "force")) {
                open(IMG, "/tmp/lyricue_" . $globals->{'use_port'} . ".png");
                while (<IMG>) {
                    $status .= $_;
                }
                close BGIMG;
                $globals->{'snapshot_changed'} = FALSE;
            }
            $status .= "\nENDRESPONSE";
            debug("Returning image data");
            return $status;
        } elsif (/^reconfig$/) {
            $config = load_config();
            $status = gettext("Configuration reloaded");
        } elsif (/^backdrop$/) {
            $config->{'BGImage'} = $line[1];
            change_backdrop($config->{'BGImage'});
            $status = gettext("Backdrop changed to ") . $config->{'BGImage'};

            #reset_timer($globals->{'snapshot_timer'});
            #$globals->{'snapshot_timer'} = Glib::Idle->add(\&update_snapshot);
        } elsif (/^blank$/) {
            set_header("");
            set_maintext("", NONE, FALSE);
            set_footer("");

            #reset_timer($globals->{'snapshot_timer'});
            #$globals->{'snapshot_timer'} = Glib::Idle->add(\&update_snapshot);
            if ($line[1] ne "") {
                $config->{'BGImage'} = $line[1];
                change_backdrop($config->{'BGImage'});
                $status = gettext("Screen blanked");
            } else {
                $status = gettext("Text cleared");
            }
            $globals->{'blanked_state'} = TRUE;
        } elsif (/^change_to_db$/) {
            change_to_db($line[1], $line[2]);
            $status = gettext("Bible changed to ") . $globals->{'bibledb'};
        } elsif (/^next_point$/) {
            $globals->{'blanked_state'} = FALSE;
            if ($current_point > -1 && scalar(@points) > $current_point + 1) {
                my $pointtext = "";
                $current_point++;

                #Add all points up and including to current point
                for (my $count = 1 ; $count <= $current_point ; $count++) {
                    $pointtext .= $points[$count];
                }

                #Now figure out how many \n's to add
                if (my @original = ((join "", @points) =~ /\n/g)) {
                    if (my @pt = $pointtext =~ /\n/g) {
                        for (
                            my $ns = 0 ;
                            $ns < scalar(@original) - scalar(@pt) ;
                            $ns++
                          )
                        {
                            $pointtext .= "\n";
                        }
                    }
                }

                $pointtext =~ s/<.*?>//g;
                set_maintext($pointtext, NONE, FALSE);
            } else {
                debug("Can not advance one point - no points waiting");
            }
        } elsif (/^preview$/) {
            my $wrap = FALSE;
            if ($line[1] ne "ignore") {
                $line[1] =~ s/#SEMI#/:/g;
                my @extras = split(/#BREAK#/, $line[1]);
                if ((defined $extras[3]) && ($extras[3] eq "wrap")) {
                    $wrap = TRUE;
                }
                set_header($extras[0]);

                if ($extras[2] && ($extras[2] ne "")) {
                    set_footer(
                        gettext("Written by ") . $extras[1] . " - " . $extras[2]);
                } else {
                    if ($extras[1]) {
                        set_footer(gettext("Written by ") . $extras[1]);
                    } else {
                        set_footer("");
                    }
                }
            }
            $line[2] =~ s/#BREAK#/\n/g;
            $line[2] =~ s/#SEMI#/:/g;
            $line[2] =~ s/<.*?>//g;
            set_maintext(Encode::decode("utf-8", $line[2]), NONE, $wrap);

            #reset_timer($globals->{'snapshot_timer'});
            #$globals->{'snapshot_timer'} = Glib::Idle->add(\&update_snapshot);
        } elsif (/^loopparent$/) {
            $LOOPPARENT = $line[1];
            debug("Set loop parent to: " . $LOOPPARENT);
        } elsif (/^get$/) {
            if ($line[1] eq "playlist") {
                $status = get_playlist($line[2]);
            } elsif ($line[1] eq "playlists") {
                $status = get_playlists();
            }
        } elsif (/^display$/ && $line[1] ne "") {

            if (!(($line[1] eq "current") && $globals->{'blanked_state'})) {
                $globals->{'blanked_state'} = FALSE;

                #
                # Change it to lowercase
                $line[1] = lc($line[1]);

                # display new lyrics
                my $query =
                  "SELECT * FROM playlist WHERE playorder=" . $current_item;
                debug($query);
                my $sth = $lyricDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                my $rv = $sth->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);
                my $current_play = $sth->fetchrow_hashref();

                if ($line[1] eq "playlist") {
                    debug("Changing playlist to " . $line[2]);
                    $current_list = $line[2];
                } elsif ($line[1] eq "current") {
                    $globals->{'blanked_state'} = FALSE;

                    # ignore and just display same page
                } elsif ($line[1] eq "next_page") {
                    my $query =
                      "SELECT playlist FROM playlist WHERE playorder="
                      . $current_item;
                    debug($query);
                    my $sth = $lyricDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    my $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);
                    my @row = $sth->fetchrow_array();
                    $query =
                        "SELECT MIN(playorder) FROM playlist WHERE playlist="
                      . $row[0]
                      . " AND playorder>"
                      . $current_item
                      . " ORDER BY playorder";
                    debug($query);
                    $sth = $lyricDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);
                    my @row2 = $sth->fetchrow_array();

                    if ($row2[0]) {

                        #End of this particular list (song/sublist) not reached
                        debug("Looping to next item in list\n");
                        $current_item = $row2[0];
                    } else {

                        #End of this particular list (song/sublist) reached
                        if ($line[2] eq "loop") {
                            debug("Finding next item to display in loop\n");

                            if ($LOOPPARENT == -1) {
                                debug("Looping a song, back to page 1\n");
                                $query =
"SELECT MIN(playorder) FROM playlist WHERE playlist="
                                  . $row[0];
                                debug($query);
                                $sth = $lyricDbh->prepare($query)
                                  || display_fatal($errorcodes->{'sqlprepare'},
                                    $!);
                                $rv = $sth->execute
                                  || display_fatal($errorcodes->{'sqlexecute'},
                                    $!);
                                my @row3 = $sth->fetchrow_array();
                                if ($row3[0]) {
                                    $current_item = $row3[0];
                                }
                            } else {
                                debug("Looping a sublist...\n");

                           #Build a 'context map', by tracing the
                           #steps from the current level, to the top of the list
                                debug("Building the context map...\n");
                                my @contextmap;    #array of playorders
                                my $item    = $current_item;
                                my $plpoint = -2;

                                while ($plpoint != $LOOPPARENT) {
                                    $query =
"SELECT playlist FROM playlist WHERE playorder="
                                      . $item;
                                    debug("\t" . $query);
                                    $sth = $lyricDbh->prepare($query)
                                      || display_fatal(
                                        $errorcodes->{'sqlprepare'}, $!);
                                    $rv = $sth->execute
                                      || display_fatal(
                                        $errorcodes->{'sqlexecute'}, $!);
                                    my @playlist = $sth->fetchrow_array();

                                    #Add playlist id to contextmap
                                    @contextmap = (@contextmap, $playlist[0]);

                                    #Update playlist pointer so we can do
                                    #the test for equality to LOOPPARENT
                                    $plpoint = $playlist[0];

                                    #Change item to this playlist's playorder
                                    $query =
"SELECT playorder FROM playlist WHERE data="
                                      . $playlist[0]
                                      . " and (type='sub' or type='play')";
                                    debug("\t" . $query);
                                    $sth = $lyricDbh->prepare($query)
                                      || display_fatal(
                                        $errorcodes->{'sqlprepare'}, $!);
                                    $rv = $sth->execute
                                      || display_fatal(
                                        $errorcodes->{'sqlexecute'}, $!);
                                    my @playorder = $sth->fetchrow_array();

                                    $item = $playorder[0];
                                    debug("....\n");
                                }
                                debug("Context map built!\n");

                                #Find the next item
                                my $lastpo   = $current_item;
                                my $nextitem = $current_item;

                                foreach (@contextmap) {

                                 #Find next item in the current context map list
                                    $query =
"SELECT MIN(playorder) FROM playlist WHERE "
                                      . " playlist="
                                      . $_
                                      . " and playorder > "
                                      . $lastpo;
                                    debug($query);
                                    $sth = $lyricDbh->prepare($query)
                                      || display_fatal(
                                        $errorcodes->{'sqlprepare'}, $!);
                                    $rv = $sth->execute
                                      || display_fatal(
                                        $errorcodes->{'sqlexecute'}, $!);
                                    my @results = $sth->fetchrow_array();

                                    if ($results[0]) {

                                        #Set nextitem and leave loop
                                        $nextitem = $results[0];
                                        last;
                                    } else {

                                        #prepare for next iteration
                                        $query =
"SELECT playorder FROM playlist WHERE "
                                          . " data="
                                          . $_
                                          . " and (type='sub'"
                                          . " or type='play')";

                                        debug("\t" . $query);
                                        $sth = $lyricDbh->prepare($query)
                                          || display_fatal(
                                            $errorcodes->{'sqlprepare'}, $!);
                                        $rv = $sth->execute
                                          || display_fatal(
                                            $errorcodes->{'sqlexecute'}, $!);
                                        my @poresult = $sth->fetchrow_array();

                                        $lastpo = $poresult[0];
                                    }
                                }

                                if ($current_item != $nextitem) {
                                    debug("Found next item to show\n");
                                    $current_item = $nextitem;
                                } else {

                                    #End of the road, go back to start!
                                    debug("No more items, back to start\n");
                                    $query =
"SELECT MIN(playorder) FROM playlist WHERE "
                                      . " playlist="
                                      . $LOOPPARENT;
                                    debug($query);
                                    $sth = $lyricDbh->prepare($query)
                                      || display_fatal(
                                        $errorcodes->{'sqlprepare'}, $!);
                                    $rv = $sth->execute
                                      || display_fatal(
                                        $errorcodes->{'sqlexecute'}, $!);
                                    my @start = $sth->fetchrow_array();
                                    $current_item = $start[0];
                                }
                            }
                        }
                    }
                } elsif ($line[1] eq "prev_page") {
                    my $query =
                      "SELECT playlist FROM playlist WHERE playorder="
                      . $current_item;
                    debug($query);
                    my $sth = $lyricDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    my $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);
                    my @row = $sth->fetchrow_array();
                    $query =
                        "SELECT MAX(playorder) FROM playlist WHERE playlist="
                      . $row[0]
                      . " AND playorder<"
                      . $current_item
                      . " ORDER BY playorder";
                    debug($query);
                    $sth = $lyricDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);
                    my @row2 = $sth->fetchrow_array();

                    if ($row2[0]) {
                        $current_item = $row2[0];
                    } else {
                        if ($line[2] eq "loop") {
                            $query =
"SELECT MAX(playorder) FROM playlist WHERE playlist="
                              . $row[0];
                            debug($query);
                            $sth = $lyricDbh->prepare($query)
                              || display_fatal($errorcodes->{'sqlprepare'}, $!);
                            $rv = $sth->execute
                              || display_fatal($errorcodes->{'sqlexecute'}, $!);
                            my @row2 = $sth->fetchrow_array();
                            if ($row2[0]) {
                                $current_item = $row2[0];
                            }
                        }
                    }

                } elsif ($line[1] eq "next_song") {
                    my $query =
"SELECT a.playorder,a.playlist FROM playlist AS a, playlist AS b WHERE a.data=b.playlist AND a.type=\"play\" AND b.playorder="
                      . $current_item;
                    debug($query);
                    my $sth = $lyricDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    my $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);
                    my @row;
                    if (@row = $sth->fetchrow_array()) {
                        $current_item = $row[0];
                        $current_list = $row[1];
                    }

                    $query =
                        "SELECT MIN(playorder) FROM playlist WHERE playorder > "
                      . $current_item
                      . " AND playlist="
                      . $current_list;
                    debug($query);
                    $sth = $lyricDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);
                    @row = $sth->fetchrow_array();

                    if (defined $row[0]) {
                        $current_item = $row[0];
                    }
                } elsif ($line[1] eq "prev_song") {
                    my $query =
"SELECT a.playorder,a.playlist FROM playlist AS a, playlist AS b WHERE a.data=b.playlist AND a.type=\"play\" AND b.playorder="
                      . $current_item;
                    debug($query);
                    my $sth = $lyricDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    my $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);
                    my @row;
                    if (@row = $sth->fetchrow_array()) {
                        $current_item = $row[0];
                        $current_list = $row[1];
                    }

                    $query =
                        "SELECT MAX(playorder) FROM playlist WHERE playorder < "
                      . $current_item
                      . " AND playlist="
                      . $current_list;
                    debug($query);
                    $sth = $lyricDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);
                    @row = $sth->fetchrow_array();

                    if (defined $row[0]) {
                        $current_item = $row[0];
                    }
                } elsif ($line[1] eq "page") {
                    my $query =
                      "SELECT playlist FROM playlist WHERE playorder="
                      . $current_item;
                    debug($query);
                    my $sth = $lyricDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    my $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);
                    my @row = $sth->fetchrow_array();

                    $query =
                        "SELECT playorder FROM playlist WHERE playlist="
                      . $row[0]
                      . " ORDER BY playorder";
                    debug($query);
                    $sth = $lyricDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);
                    my $count = 0;

                    while (($count < $line[2])
                        && (@row = $sth->fetchrow_array()))
                    {
                        $count++;
                    }
                    if (defined $row[0]) {
                        $current_item = $row[0];
                    }

                } else {
                    $current_item = $line[1];
                    $preview_item = 0;
                }

                # load lyrics from DB

                if ($preview_item) {
                    my $tmp = $current_item;
                    $current_item = $preview_item;
                    $preview_item = $tmp;
                }
                $query =
                  "SELECT * FROM playlist WHERE playorder=" . $current_item;
                debug($query);
                $sth = $lyricDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                $rv = $sth->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);

                #Check for associate image and, if present, apply it.
                my $imgquery =
                  "SELECT imagename FROM associations WHERE playlist="
                  . $current_item;
                my $imgsth = $lyricDbh->prepare($imgquery)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                $imgsth->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);

                #Reset background if an image was shown
                if ($bgimage_changed) {
                    $bgimage_changed = FALSE;
                    change_backdrop($config->{'BGImage'});
                }

                if (my $imgrow = $imgsth->fetchrow_hashref()) {
                    change_backdrop($imgrow->{'imagename'});
                }

                if ($exec_pid > 0) {
                    debug("Killing " . $exec_pid);
                    system('kill ' . $exec_pid);
                    system('kill -9 ' . $exec_pid);
                    $exec_pid = 0;
                }

                if (my $row = $sth->fetchrow_hashref()) {
                    if ($row->{'type'} eq "back") {
                        my $file = $row->{'data'};
                        $config->{'BGImage'} = $file;
                        change_backdrop($file);
                    } elsif ($row->{'type'} eq "file") {
                        if (-r $row->{'data'}) {
                            my ($fileobj, @fileexts);

                            # Image
                            @fileexts = qw(jpg gif png);
                            for $fileobj (@fileexts) {
                                if ($row->{'data'} =~ /$fileobj/i) {
                                    $bgimage_changed = TRUE;
                                    set_footer("");
                                    set_maintext("", NONE, FALSE);
                                    set_header("");
                                    change_backdrop($row->{'data'});
                                }
                            }

                            # Movie file
                            @fileexts = qw(avi mpg mov wmv);
                            for $fileobj (@fileexts) {
                                if ($row->{'data'} =~ /$fileobj/i) {
                                    $exec_pid = fork;
                                    if ($exec_pid > 0) {

                                        my $command =
$config->{'MPlayer'} . " "
                                          . $row->{'data'};
                                        debug($command);

                                        system($command);
                                    }
                                }
                            }
                        }
                    } elsif ($row->{'type'} eq "imag") {
                        $bgimage_changed = TRUE;
                        set_footer("");
                        set_maintext("", NONE, FALSE);
                        set_header("");
                        change_backdrop($row->{'data'});
                    } elsif ($row->{'type'} eq "vers") {

                        # Kill invalid points from previous songs
                        $current_point = -1;
                        @points        = ();

                        #Reset background if an image was shown
                        if ($bgimage_changed) {
                            $bgimage_changed = FALSE;
                            change_backdrop($config->{'BGImage'});
                        }

                        my $query =
"SELECT * FROM playlist,playlists WHERE playlist.playlist=playlists.id AND playorder="
                          . $current_item;
                        debug($query);
                        my $sth = $lyricDbh->prepare($query)
                          || display_fatal($errorcodes->{'sqlprepare'}, $!);
                        my $rv = $sth->execute
                          || display_fatal($errorcodes->{'sqlexecute'}, $!);

                        my $row        = $sth->fetchrow_hashref();
                        my $transition = $row->{'transition'};

                        my $versetext = $row->{'title'};
                        my @line      = split(/[:-]/, $row->{'title'});
                        my $verses    = $row->{'data'};
                        my $verse     = "";

                        ($line[2], $line[4]) = split(/-/, $row->{'data'});
                        if ($line[4] eq "") {
                            $line[4] = $line[2];
                        }
                        if (!$globals->{'usesword'}) {
                            $query =
                              "SELECT book FROM verse WHERE book LIKE \""
                              . $line[0] . "%\"";
                            $sth = $bibleDbh->prepare($query)
                              || display_fatal($errorcodes->{'sqlprepare'}, $!);
                            $rv = $sth->execute
                              || display_fatal($errorcodes->{'sqlexecute'}, $!);
                            my @bookrow = $sth->fetchrow_array();
                            $line[0] = $bookrow[0];

                            if ($line[1] == $line[3]) {
                                $query =
                                    "SELECT * FROM  verse WHERE book LIKE \""
                                  . $line[0]
                                  . "%\" AND chapternum="
                                  . $line[1]
                                  . " AND versenum>="
                                  . $line[2]
                                  . " AND versenum <= "
                                  . $line[4];
                            } else {
                                $query =
                                    "SELECT * FROM verse WHERE book LIKE \""
                                  . $line[0]
                                  . "%\" AND ((chapternum="
                                  . $line[1]
                                  . " AND versenum>="
                                  . $line[2]
                                  . ") OR (chapternum>"
                                  . $line[1]
                                  . " AND chapternum<"
                                  . $line[3]
                                  . ") OR (chapternum="
                                  . $line[3]
                                  . " AND versenum<="
                                  . $line[4] . "))";
                            } ## end else
                            debug($query);
                            $sth = $bibleDbh->prepare($query)
                              || display_fatal($errorcodes->{'sqlprepare'}, $!);
                            $rv = $sth->execute
                              || display_fatal($errorcodes->{'sqlexecute'}, $!);

                            while ($row = $sth->fetchrow_hashref()) {
                                my $line =
                                    $row->{'chapternum'} . ":"
                                  . $row->{'versenum'} . "   "
                                  . $row->{'verse'};
                                $verse .= $line . "\n";
                            }
                        } else {
                            my $command = "";
                            $command = sprintf(
"%s -b %s -e UTF8 -k '%s' %d:%d-%d:%d |tr \\\\n \' \'",
                                $globals->{'diatheke'}, $globals->{'bibledb'},
                                $line[0],               $line[1],
                                $line[2],               $line[3],
                                $line[4],               $line[0]
                            );
                            debug($command);
                            my $command_out =
                              Encode::decode('utf-8', `$command`);
                            ($line[0], undef) = split(/\s\d/, $command_out, 2);
                            my @command_lines =
                              split(/$line[0]/,, $command_out);

                            foreach (@command_lines) {
                                chomp;
                                my $line2 = $_;
                                $line2 =~ s/^$line[0] //g;
                                if ($line2 ne "") {
                                    $verse .= $line2 . "\n";
                                }
                            }
                        }

                        set_maintext($verse, $transition, TRUE);

                        set_header($versetext);
                        set_footer($globals->{'biblename'});
                        $status =
                            gettext("Displaying ")
                          . $versetext
                          . gettext(" verses ")
                          . $verses;
                    } elsif ($row->{'type'} eq "play" | $row->{'type'} eq "sub")
                    {
                        my $query =
                            "SELECT * FROM playlist WHERE playlist="
                          . $row->{'data'}
                          . " ORDER BY playorder";
                        debug($query);
                        $sth = $lyricDbh->prepare($query)
                          || display_fatal($errorcodes->{'sqlprepare'}, $!);
                        my $rv = $sth->execute
                          || display_fatal($errorcodes->{'sqlexecute'}, $!);
                        my $row = $sth->fetchrow_hashref();
                        update_display("display", $row->{'playorder'}, 0);

                    } else {
                        my $query2 =
"SELECT title,artist,lyrics,copyright,pagenum FROM lyricMain AS l, page AS pa WHERE pa.songid=l.id AND pa.pageid="
                          . $row->{'data'};
                        debug($query2);
                        my $sth2 = $lyricDbh->prepare($query2)
                          || display_fatal($errorcodes->{'sqlprepare'}, $!);
                        my $rv2 = $sth2->execute
                          || display_fatal($errorcodes->{'sqlexecute'}, $!);
                        my $row2 = $sth2->fetchrow_hashref();

                        $row2->{'lyrics'} =~ s/
/\n/g;
                        my $footer = "";
                        if ($row2->{'artist'} ne "") {
                            $footer =
                                gettext("Written by ")
                              . $row2->{'artist'}
                        }
                        if ($row2->{'copyright'} ne "") {
                            if ($row2->{'copyright'} =~ /^Preset/) {
                                $row2->{'copyright'} =~ s/^.*([0-9]).*$/$1/g;
                                $footer .= " - ". $preset[$row2->{'copyright'}];
                            } else {
                                $footer .= " - ". $row2->{'copyright'};
                            }
                        }

                        # Determine if this is a multi-point page
                        if ($row2->{'lyrics'} =~ m/<UL>/) {
                            debug("This page has multiple points...");
                            $current_point = 1;
                            @points = split(/<LI>/, $row2->{'lyrics'});
                            my $pointtext = $points[$current_point];

                            #Now figure out how many \n's to add
                            if (my @o = ($row2->{'lyrics'} =~ /\n/g)) {
                                if (my @n = $pointtext =~ /\n/g) {
                                    my $x;
                                    for (
                                        $x = 0 ;
                                        $x < scalar(@o) - scalar(@n) ;
                                        $x++
                                      )
                                    {
                                        $pointtext .= "\n";
                                    }
                                }
                            }
                            $row2->{'lyrics'} = $pointtext;
                        } else {
                            $current_point = -1;
                            @points        = ();
                        }
                        set_header($row2->{'title'});
                        $row2->{'lyrics'} =~ s/<.*?>//g;
                        set_maintext($row2->{'lyrics'}, $row->{'transition'},
                            FALSE);
                        set_footer($footer);
                        $status =
                            gettext("Displaying ")
                          . $row2->{'title'}
                          . gettext(" page ")
                          . $row2->{'pagenum'};
                    } ## end else
                    refresh_screen();
                } ## end if (my $row = $sth->fetchrow_hashref...
                if ($preview_item) {
                    $current_item = $preview_item;
                    $preview_item = 0;
                }

                #reset_timer($globals->{'snapshot_timer'});
                #$globals->{'snapshot_timer'} =
                #Glib::Idle->add(\&update_snapshot);
            }
        } ## end elsif (/^display$/)
    } ## end if ($_)
    debug("The status message sent is: " . $status);
    update_tracker();
    return $status;
} ## end sub process_input
    #***

#****f* lyricue_server/CloseAppWindow
# NAME
#   CloseAppWindow
# SYNOPSIS
#   CloseAppWindow ()
# FUNCTION
#   Close the lyricue_server window
# INPUTS
#   None
# OUTPUT
#   None
# SOURCE
#
sub CloseAppWindow {
    debug("Exiting Child");
    if ($globals->{'server_type'} == SIMPLE) {
        unlink $globals->{'bg_file'};
        $globals->{'window'}->destroy;
    } elsif ($globals->{'server_type'} == TRANS) {
        $globals->{'header_window'}->destroy;
        $globals->{'headerShadow_window'}->destroy;
        $globals->{'main_window'}->destroy;
        $globals->{'mainShadow_window'}->destroy;
        $globals->{'footer_window'}->destroy;
        $globals->{'footerShadow_window'}->destroy;
    } else {
        $globals->{'window'}->destroy;
    }
    exit;
    return FALSE;
}

#***

#****f* lyricue_server/HandleKey
# NAME
#   HandleKey
# SYNOPSIS
#   HandleKey ( $widget, $data )
# FUNCTION
#   Parses keyboard input
# INPUTS
#   $widget - Calling widget
#   $data   - structure containing event
# OUTPUT
#   calls update_display to update screen
# SOURCE
#
sub HandleKey {
    my ($widget, $data) = @_;

    if (   ($data->keyval == $Gtk2::Gdk::Keysyms{Left})
        || ($data->keyval == $Gtk2::Gdk::Keysyms{KP_Left})
        || ($data->keyval == $Gtk2::Gdk::Keysyms{Page_Up}))
    {
        update_display("display", "prev_page", $config->{'Loop'});
    } elsif (($data->keyval == $Gtk2::Gdk::Keysyms{Up})
        || ($data->keyval == $Gtk2::Gdk::Keysyms{KP_Up}))
    {
        update_display("display", "prev_song", 0);
    } elsif (($data->keyval == $Gtk2::Gdk::Keysyms{Right})
        || ($data->keyval == $Gtk2::Gdk::Keysyms{KP_Right})
        || ($data->keyval == $Gtk2::Gdk::Keysyms{Page_Down}))
    {
        update_display("display", "next_page", $config->{'Loop'});
    } elsif (($data->keyval == $Gtk2::Gdk::Keysyms{Down})
        || ($data->keyval == $Gtk2::Gdk::Keysyms{KP_Down}))
    {
        update_display("display", "next_song", 0);
    } elsif (($data->keyval == $Gtk2::Gdk::Keysyms{KP_0})
        || ($data->keyval == $Gtk2::Gdk::Keysyms{KP_Insert})
        || ($data->keyval == $Gtk2::Gdk::Keysyms{b}))
    {
        update_display("blank", 0, 0);
    } elsif ($data->keyval == $Gtk2::Gdk::Keysyms{Q}) {

        # exit
        my $dialog = Gtk2::Dialog->new(
            'Confirm Exit', undef, 'modal',
            'gtk-ok'     => 'ok',
            'gtk-cancel' => 'cancel',
        );
        $dialog->vbox->pack_start(
            Gtk2::Label->new(
                "Are you sure you want to close the Lyricue Server?"),
            TRUE, TRUE, 0
        );
        $dialog->show_all();
        my $response = $dialog->run;
        print("Response: " . $response . "\n");
        $dialog->destroy;
        if ($response eq "ok") {
            CloseAppWindow();
        }
    } else {
        debug("Unknown key: " . $data->keyval);
        return FALSE;
    }
    return TRUE;
} ## end sub HandleKey

#***

#****f* lyricue_server/change_backdrop
# NAME
#   change_backdrop
# SYNOPSIS
#   change_backdrop ( $type, $backdrop )
# FUNCTION
#   Loads an image and sets it as the screens background
# INPUTS
#   $id - id of image
# OUTPUT
#   Updates screen
# SOURCE
#
sub change_backdrop {
    my ($id) = @_;
    debug("Setting backdrop");
    my $query =
"SELECT format, description, data, textcolour, shadowcolour FROM media WHERE id=\""
      . $id . "\"";
    debug($query);
    my $sth = $mediaDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my $row = $sth->fetchrow_hashref();
    if ($row) {

        if ($row->{'textcolour'} ne "") {
            debug("Changing text colour to " . $row->{'textcolour'});
            $config->{'BackdropTextColour'} = $row->{'textcolour'};
        } else {
            $config->{'BackdropTextColour'} = "";
        }
        if ($row->{'shadowcolour'} ne "") {
            debug("Changing shadow colour to " . $row->{'shadowcolour'});
            $config->{'BackdropShadowColour'} = $row->{'shadowcolour'};
        } else {
            $config->{'BackdropShadowColour'} = "";
        }

        if ($row->{'format'} eq "bg") {
            debug("Changing backdrop colour to " . $row->{'description'});
            if (   ($globals->{'server_type'} == TRANS)
                || ($globals->{'server_type'} == SIMPLE))
            {
                open(BGIMG, ">" . $globals->{'bg_file'});
                print BGIMG
                  sprintf(
'/* XPM */\nstatic char *b[] = { "1 1 1 1", "  c %s", " " };\n',
                    $row->{'description'});
                close BGIMG;
            } else {
                if (defined $background) {
                    $background->destroy;
                }

                $background = Gnome2::Canvas::Item->new(
                    $globals->{'root'}, 'Gnome2::Canvas::Rect',
                    x1          => 0,
                    y1          => 0,
                    x2          => $config->{'Width'},
                    y2          => $config->{'Height'},
                    fill_color  => $row->{'description'},
                    width_units => 0
                );
            }
        } else {
            debug("Changing backdrop image to " . $row->{'description'});
            my $pixbuf_loader = Gtk2::Gdk::PixbufLoader->new();
            $pixbuf_loader->set_size($config->{'Width'}, $config->{'Height'});
            eval { $pixbuf_loader->write($row->{'data'}); };
            eval { $pixbuf_loader->close() };
            if ($@) {
                debug("Unable to load background: $row->{'description'}");
            } else {
                my $pixbuf = $pixbuf_loader->get_pixbuf();
                if (   ($globals->{'server_type'} == TRANS)
                    || ($globals->{'server_type'} == SIMPLE))
                {
                    $pixbuf->save($globals->{'bg_file'}, 'png');
                } else {
                    if (defined $background) {
                        $background->destroy;
                    }
                    $background = Gnome2::Canvas::Item->new(
                        $globals->{'root'}, 'Gnome2::Canvas::Pixbuf',
                        pixbuf     => $pixbuf,
                        x          => 1,
                        y          => 1,
                        width      => $config->{'Width'},
                        height     => $config->{'Height'},
                        width_set  => TRUE,
                        height_set => TRUE,
                    );
                }
            }
        }
        if ($globals->{'server_type'} == TRANS) {
            my $rc_style = Gtk2::RcStyle->new;
            $rc_style->bg_pixmap_name('normal', $globals->{'bg_file'});
            $globals->{'background_window'}->modify_style($rc_style);
            $globals->{'background_window'}->show_all();
        } elsif ($globals->{'server_type'} == SIMPLE) {
            my $rc_style = Gtk2::RcStyle->new;
            $rc_style->bg_pixmap_name('normal', $globals->{'bg_file'});
            $globals->{'window'}->modify_style($rc_style);
        } else {
            $background->lower_to_bottom;
        }
    }
} ## end sub change_backdrop

#***

#****f* lyricue_server/update_display
# NAME
#   update_display
# SYNOPSIS
#   update_display ( $command, $primary, $secondary, $tertiary)
# FUNCTION
#   Prints text to FIFO in predetermined format
# INPUTS
#   $command   - Main command to send
#   $primary   - Primary parameter to command
#   $secondary - Secondary parameter to send
#   $tertiary  - Tertiary parameter to send
# OUTPUT
#   Sends it all to the socket
# SOURCE
#
sub update_display {
    my ($command, $primary, $secondary) = @_;

    if (
        my $server = IO::Socket::INET->new(
            Proto    => "tcp",
            PeerAddr => "localhost",
            PeerPort => $globals->{'use_port'}
        )
      )
    {
        print $server $command . ":" . $primary . ":" . $secondary . "\n";
        close($server);
    }
} ## end sub update_display

sub update_miniview {
    my ($command) = @_;

    if (
        my $server = IO::Socket::INET->new(
            Proto    => "tcp",
            PeerAddr => "localhost",
            PeerPort => $globals->{'miniview_port'}
        )
      )
    {
        print $server $command;
        close($server);
    }
} ## end sub update_miniview

#***

#****f* lyricue_server/debug
# NAME
#   debug
# SYNOPSIS
#   debug ( $text )
# FUNCTION
#   Checks if $DEBUG is set and if so outputs text to STDERR
# INPUTS
#   $text - text to output
# OUTPUT
#   $text to STDERR
# SOURCE
#
sub debug {
    if ($DEBUG) {
        my $text = shift;
        chomp($text);
        if ($text) {
            my ($sec, $min, $hour, undef) = localtime(time);
            print STDERR $hour . ":" . $min . ":" . $sec . "|";
            if ($MINISERV > 0) {
                print STDERR "SERVER: ";
            }
            print STDERR $text . "\n";
        }
    }
} ## end sub debug

#***

#****f* lyricue_server/change_to_db
# NAME
#   change_to_db
# SYNOPSIS
#   change_to_db ( $db )
# FUNCTION
#   Disconnects old bible database and loads a new one
# INPUTS
#   $text - text to output
# OUTPUT
#   No visible output except outputs to STDERR via debug
# SOURCE
#
sub change_to_db {
    my ($db, $type) = @_;
    $globals->{'bibledb'} = $db;
    if (!$globals->{'usesword'}) {
        $bibleDbh->disconnect;
    }
    if ($type eq "db") {
        $bibleDbh =
          db_connect($globals->{'bibledb'},
            $errorcodes->{'bibledbopen'} . $globals->{'bibledb'});
        $globals->{'usesword'} = FALSE;
    } else {
        $globals->{'usesword'} = TRUE;
        $bibleDbh = "";
    }
    $globals->{'biblename'} = $config->{'Bibles'}->{$globals->{'bibledb'}};
    $globals->{'biblename'} =~ s/^.*?://g;
    $globals->{'biblename'} =~ s/^.*?;//g;
    $globals->{'biblename'} =~ s/_//g;
} ## end sub debug

#****f* lyricue_server/check_socket
# NAME
#   check_socket
# SYNOPSIS
#   check_socket ($socket, $condition, $flags)
# FUNCTION
#   Check the socket for new data and handle it
# INPUTS
#   $socket - Socket to listen to
#   $condition - flags if something is on socket
#   $flags - Flags describing socket
# OUTPUT
#   No visible output except outputs to STDERR via debug
# SOURCE
#
sub check_socket {
    my ($socket, $condition, $flags) = @_;
    my $new_sock = $socket->accept();
    while (defined($_ = <$new_sock>)) {
        my $value = $_;
        my $stat  = process_input($value);
        chomp($stat);
        print $new_sock $stat . "\n";

        # Forward onto miniserver (if not the miniserver)
        if ($globals->{'use_port'} == $globals->{'server_port'}) {
            update_miniview($value);
        }
    }
    close($new_sock);

    return TRUE;
}

#***

sub load_config {
    my ($conf);
    debug("Loading Preferences from " . $globals->{'configfile'});
    my $bibleCount = 0;
    my $appCount   = 0;
    open(CONFIG, $globals->{'configfile'})
      || display_fatal("Couldn't open config file", $!);
    $conf->{'Width'}  = 0;
    $conf->{'Height'} = 0;
    while (<CONFIG>) {
        chomp;
        my @line = split(/=/);
        $line[0] =~ s/ *$//g;
        $line[1] =~ s/ *$//g;
        if (!$line[1]) {
            $line[1] = "";
        }
        $line[1] =~ s/^ *//g;
        if ($line[0] eq "App") {
            $conf->{'App'}[$appCount++] = $line[1];
        } else {
            $conf->{$line[0]} = $line[1];
        }
    }
    $conf->{'AppCount'} = $appCount;
    $conf->{'Bibles'}   = get_bibles();
    if (! defined($conf->{'MPlayer'})) {
        $conf->{'MPlayer'} = "mplayer -zoom -really-quiet -fs -noconsolecontrols";
    }
    if (! defined($conf->{'BackdropTextColour'})) {
        $conf->{'BackdropTextColour'} = "";
    }
    return $conf;
    if (! defined($conf->{'BackdropShadowColour'})) {
        $conf->{'BackdropShadowColour'} = "";
    }
    return $conf;
}

sub set_maintext {
    my ($text, $transition, $wrap) = @_;
    if (!defined $text) { $text = " "; }
    if ($globals->{'server_type'} == TRANS) {
        set_maintext_trans($text, $transition, $wrap);
    } elsif ($globals->{'server_type'} == SIMPLE) {
        set_maintext_simple($text, $transition, $wrap);
    } else {
        set_maintext_canvas($text, $transition, $wrap);
    }
}

sub set_maintext_simple {
    my ($text, $transition, $wrap) = @_;
    debug("Set maintext simple");
    my $font_desc =
      Gtk2::Pango::FontDescription->from_string($config->{'Main'});
    my $colour = Gtk2::Gdk::Color->parse($config->{'Colour'});
    if ($config->{'BackdropTextColour'} ne "") {
        $colour = Gtk2::Gdk::Color->parse($config->{'BackdropTextColour'});
    }
    $globals->{'mainText'}->modify_fg('normal', $colour);
    $globals->{'mainText'}->modify_font($font_desc);
    $globals->{'mainText'}->set_text($text);
    $globals->{'mainText'}->set_line_wrap($wrap);

    if ($wrap) {
        $globals->{'mainText'}
          ->set_size_request($config->{'Width'} - ($config->{'OverscanH'} * 2),
            -1);
    }
}

sub set_maintext_canvas {
    my ($text, $transition, $wrap) = @_;
    debug("Set maintext canvas");
    my ($oldText);
    if (!defined $transition) { $transition = 0; }

    # Set the transition values
    my $old_direction = mod($transition, 32);
    $transition = $transition >> 5;
    my $new_direction = mod($transition, 32);
    my $effect        = $transition >> 5;

    # Copy the old mainText to oldText
    $oldText = $globals->{'mainText'};

    my $top_y = ($config->{'Height'} - $config->{'ShadowSize'}) / 2;
    if (defined $config->{'CenterY'} && $config->{'CentreY'} eq "") {
        $top_y = $headerHeight;
    }

    # Create a new mainText;
    $globals->{'mainText'} = Gnome2::Canvas::Item->new(
        $globals->{'root'}, 'Gnome2::Canvas::RichText',
        x      => ($config->{'Width'} - $config->{'ShadowSize'}) / 2,
        y      => $top_y,
        anchor => 'GTK_ANCHOR_CENTER',
        width  => $config->{'Width'} - ($config->{'OverscanH'} * 2) -
          $config->{'ShadowSize'},
        height => $config->{'Height'} - $globals->{'headhigh'} -
          ($config->{'OverscanV'} * 2) - $config->{'ShadowSize'},
        'cursor-visible' => FALSE
    );
    $globals->{'mainText'}->show();
    my $buffer = $globals->{'mainText'}->get_buffer();
    my $colour = $config->{'Colour'};
    my $tag;
    if ($config->{'BackdropTextColour'} ne "") {
        $colour = $config->{'BackdropTextColour'};
    }
    my $tag = $buffer->create_tag(
        "Main",  "font",      $config->{'Main'}, "foreground",
        $colour, "wrap-mode", "word",            "editable-set",
        TRUE,    "editable",  FALSE
    );
    $buffer->apply_tag_by_name("Main", $buffer->get_bounds);

    # Hide the shadow during the transition
    if ($globals->{'mainTextShadow'}) { $globals->{'mainTextShadow'}->hide(); }
    my $widget = Gtk2::Label->new();
    my $layout = $widget->create_pango_layout("");
    $layout->set_wrap('word');
    $layout->set_width(
        (
            $config->{'Width'} - ($config->{'OverscanH'} * 2) -
              $config->{'ShadowSize'}
        ) * PANGO_SCALE
    );
    my $desc = Gtk2::Pango::FontDescription->from_string($config->{'Main'});

    # Find old/new dimensions
    $layout->set_font_description($desc);
    $layout->set_text($oldText->get_buffer()
          ->get_text($oldText->get_buffer()->get_bounds, FALSE));
    my ($ow, $oh) = $layout->get_pixel_size;
    $ow = $ow + 10;
    $oh = $oh + 10;
    $layout->set_text($text);
    my ($w, $h) = $layout->get_pixel_size;
    $w = $w + 10;
    $h = $h + 10;
    my $centreX = $config->{'Width'} / 2;
    my $centreY = $config->{'Height'} / 2;

    if (defined $config->{'CentreY'} && $config->{'CentreY'} == 0) {
        $centreY = ($h / 2) + $headerHeight;
    }

    my @items = ();
    if ($effect == SLIDE_TEXT) {
        my $ostepx = 0;
        my $ostepy = 0;
        my $nstepx = 0;
        my $nstepy = 0;
        my $nx     = $centreX;
        my $ny     = $centreY;
        if ($oldText->get_buffer()
            ->get_text($oldText->get_buffer()->get_bounds, FALSE) eq "")
        {
            $old_direction = WAIT;
        }
        if ($old_direction & RIGHT) {
            $ostepx = (($config->{'Width'} + ($ow / 2)) - $centreX) / $steps;
        }
        if ($old_direction & LEFT) {
            $ostepx = -(($config->{'Width'} + ($ow / 2)) - $centreX) / $steps;
        }
        if ($old_direction & UP) {
            $ostepy = (($config->{'Height'} + ($oh / 2)) - $centreY) / $steps;
        }
        if ($old_direction & DOWN) {
            $ostepy = -(($config->{'Height'} + ($oh / 2)) - $centreY) / $steps;
        }
        if ($new_direction & RIGHT) {
            $nx = -($w / 2);
            $nstepx = (($config->{'Width'} + ($w / 2)) - $centreX) / $steps;
        }
        if ($new_direction & LEFT) {
            $nx = $config->{'Width'} + ($w / 2);
            $nstepx = -(($config->{'Width'} + ($w / 2)) - $centreX) / $steps;
        }
        if ($new_direction & UP) {
            $ny = $config->{'Height'} + ($h / 2);
            $nstepy = -(($config->{'Height'} + ($h / 2)) - $centreY) / $steps;
        }
        if ($new_direction & DOWN) {
            $ny = -($h / 2);
            $nstepy = (($config->{'Height'} + ($h / 2)) - $centreY) / $steps;
        }
        my $ox = $centreX;
        my $oy = $centreY;
        $buffer->set_text($text);
        $buffer->apply_tag_by_name("Main", $buffer->get_bounds);
        $globals->{'mainText'}->set(width => $w, height => $h);
        $globals->{'mainText'}->set(x => $nx, y => $ny);
        $globals->{'mainText'}->set_buffer($buffer);

        if (!(($old_direction & WAIT) && ($new_direction & WAIT))) {
            foreach my $step (1 .. $steps) {
                if (!($old_direction & WAIT)) {
                    $oldText->set(x => ($ox + ($step * $ostepx)));
                    $oldText->set(y => ($oy + ($step * $ostepy)));
                }
                if (!($new_direction & WAIT)) {
                    $globals->{'mainText'}->set(x => ($nx + ($step * $nstepx)));
                    $globals->{'mainText'}->set(y => ($ny + ($step * $nstepy)));
                }
                select(undef, undef, undef, 0.01);
                do_pending();
            }
        }
        if (($old_direction & WAIT) || ($new_direction & WAIT)) {
            foreach my $step (1 .. $steps) {
                if ($old_direction & WAIT) {
                    $oldText->set(x => ($ox + ($step * $ostepx)));
                    $oldText->set(y => ($oy + ($step * $ostepy)));
                }
                if ($new_direction & WAIT) {
                    $globals->{'mainText'}->set(x => ($nx + ($step * $nstepx)));
                    $globals->{'mainText'}->set(y => ($ny + ($step * $nstepy)));
                }
                select(undef, undef, undef, 0.01);
                do_pending();
            }
        }
    } elsif ($effect == WIPE) {
        $items[0] = Gnome2::Canvas::Item->new(
            $globals->{'root'},
            'Gnome2::Canvas::Rect',
            y1         => ($config->{'Height'} - $oh) / 2,
            y2         => ($config->{'Height'} + $oh) / 2,
            x1         => ($config->{'Width'} - $ow) / 2,
            x2         => ($config->{'Width'} - $ow) / 2,
            fill_color => 'black'
        );
        my $stepsize = $ow / 20;
        for (
            my $fx = ($config->{'Width'} - $ow) / 2 ;
            $fx < ($config->{'Width'} + $ow) / 2 ;
            $fx = $fx + $stepsize
          )
        {
            $items[0]->set(x2 => $fx);
            select(undef, undef, undef, 0.01);
            do_pending();
        }
        $buffer->set_text($text);
        $buffer->apply_tag_by_name("Main", $buffer->get_bounds);
        $globals->{'mainText'}->set(width => $w, height => $h);
        $globals->{'mainText'}->set_buffer($buffer);
        $items[0]->set(
            y1 => ($config->{'Height'} - $h) / 2,
            x1 => ($config->{'Width'} - $w) / 2,
            x2 => ($config->{'Width'} + $w) / 2,
            y2 => ($config->{'Height'} + $h) / 2
        );
        $stepsize = $w / 20;
        for (
            my $fx = ($config->{'Width'} - $w) / 2 ;
            $fx < ($config->{'Width'} + $w) / 2 ;
            $fx = $fx + $stepsize
          )
        {
            $items[0]->set(x1 => $fx);
            select(undef, undef, undef, 0.01);
            do_pending();
        }
        $items[0]->destroy;
    } else {
        $buffer->set_text($text);
        $buffer->apply_tag_by_name("Main", $buffer->get_bounds);
        $globals->{'mainText'}->set(width => $w, height => $h);
        $globals->{'mainText'}
          ->set(x => $centreX - ($config->{'ShadowSize'} / 2));
        $globals->{'mainText'}
          ->set(y => $centreY - ($config->{'ShadowSize'} / 2));
        $globals->{'mainText'}->set_buffer($buffer);
    }
    $oldText->destroy;

    # Make sure text in right spot
    $buffer->set_text($text);
    $buffer->apply_tag_by_name("Main", $buffer->get_bounds);
    $globals->{'mainText'}->set(
        x => $centreX - ($config->{'ShadowSize'} / 2),
        y => $centreY - ($config->{'ShadowSize'} / 2),
        width  => $w,
        height => $h,
    );
    $globals->{'mainText'}->set_buffer($buffer);
    $globals->{'mainText'}->show();

    if ($MINISERV < 0) {

        # Add the shadow
        $globals->{'mainTextShadow'} = Gnome2::Canvas::Item->new(
            $globals->{'root'}, 'Gnome2::Canvas::RichText',
            x => $centreX + ($config->{'ShadowSize'} / 2),
            y => $centreY + ($config->{'ShadowSize'} / 2),
            width            => $w,
            height           => $h,
            anchor           => 'GTK_ANCHOR_CENTER',
            'cursor-visible' => FALSE
        );
        my $buffer2      = $globals->{'mainTextShadow'}->get_buffer();
        my $shadowcolour = $config->{'ShadowColour'};
        if ($config->{'BackdropShadowColour'} ne "") {
            $shadowcolour = $config->{'BackdropShadowColour'};
        }
        my $tags = $buffer2->create_tag(
            "MainShadow",      "font",
            $config->{'Main'}, "foreground",
            $shadowcolour,     "wrap-mode",
            "word",            "editable-set",
            TRUE,              "editable",
            FALSE
        );
        $buffer2->set_text($text);
        $buffer2->apply_tag_by_name("MainShadow", $buffer2->get_bounds);
        $globals->{'mainTextShadow'}->set_buffer($buffer2);
        $globals->{'mainText'}->raise_to_top();
        $globals->{'mainTextShadow'}->show();
    }
}

sub set_maintext_trans {
    my ($text, $transition, $wrap) = @_;
    debug("Set maintext trans");
    if ($text eq "") {
        $text = " ";
    }

    $globals->{'main_layout'}->set_width(1400 * Gtk2::Pango->scale);
    $globals->{'main_layout'}->set_wrap('word_char');

    #Set our pango layout, to reflect the text and format that we want.
    #NOTE We delebritely set the background to a certain color to convert
    #that color to a alpha channel, that will give us the "clear" background.
    $globals->{'main_layout'}->set_markup(
"<span background = '#000000' foreground= '#FFFFFF' size='60000' weight = 'ultralight'>$text</span>"
    );

    #Get the size of this layout after the text was set.
    my ($dwMain_w, $dwMain_h) = $globals->{'main_layout'}->get_pixel_size;

    # If string was empty then hide windows
    if ($dwMain_w == 0) {
        $globals->{'mainShadow_window'}->hide();
        $globals->{'main_window'}->hide();
        return;
    }

    #Now we have the size, we can create a pixmap that will be the
    #'Gtk2::Gdk::Drawable' that we will draw upon
    my $dwMain_pixmap = Gtk2::Gdk::Pixmap->new($globals->{'main_text'}->window,
        $dwMain_w, $dwMain_h, -1);
    my $dwMain_blank = Gtk2::Gdk::Pixmap->new($globals->{'main_text'}->window,
        $dwMain_w, $dwMain_h, -1);
    my $dwMainShadow_blank =
      Gtk2::Gdk::Pixmap->new($globals->{'mainShadow_text'}->window,
        $dwMain_w, $dwMain_h, -1);
    $dwMain_blank->draw_rectangle($globals->{'main_text'}->style->white_gc,
        TRUE, 0, 0, $dwMain_w, $dwMain_h);
    $dwMainShadow_blank->draw_rectangle(
        $globals->{'main_text'}->style->black_gc,
        TRUE, 0, 0, $dwMain_w, $dwMain_h);

    #draw the pango layout on the drawable.
    $dwMain_pixmap->draw_rectangle($globals->{'main_text'}->style->black_gc,
        TRUE, 0, 0, 6000, 6000);
    $dwMain_pixmap->draw_layout($globals->{'main_text'}->style->black_gc,
        0, 0, $globals->{'main_layout'});

    #create a Gtk2::Gdk::Pixbuf that we will use to grab the pango text from the
    #drawable (Gtk2::Gdk::Pixmap which is a Gtk2::Gdk::Drawable)
    my $dwMain_pixbuf =
      Gtk2::Gdk::Pixbuf->new('rgb', TRUE, 8, $dwMain_w, $dwMain_h);

    #here we get create a pixbuff from the drawable, this is where
    #we need the colormap
    $dwMain_pixbuf->get_from_drawable($dwMain_pixmap, $globals->{'colourmap'},
        0, 0, 0, 0, $dwMain_w, $dwMain_h);

    #Remove the background (we use a the color we specified as the pango text's
    #background
    $dwMain_pixbuf = $dwMain_pixbuf->add_alpha(TRUE, 0, 0, 0);

    #create a pixmap and mask from the Gtk2::Gdk::Pixbuf
    my ($dwMain_pm, $dwMain_m) = $dwMain_pixbuf->render_pixmap_and_mask(1);

    #replace the old $img with the new pixmap and mask
    $globals->{'main_img'}->set_from_pixmap($dwMain_blank,             undef);
    $globals->{'mainShadow_img'}->set_from_pixmap($dwMainShadow_blank, undef);

    $globals->{'mainShadow_window'}->hide();
    $globals->{'main_window'}->hide();

    #shape our window accordingly
    $globals->{'main_window'}->shape_combine_mask($dwMain_m,       0, 0);
    $globals->{'mainShadow_window'}->shape_combine_mask($dwMain_m, 0, 0);
    $globals->{'main_window'}->set_position('center-always');
    do_pending();
    my ($dwMain_x, $dwMain_y) = $globals->{'main_window'}->get_position();
    my ($dwMain_width, $dwMain_height) = $globals->{'main_window'}->get_size();
    $globals->{'mainShadow_window'}->move($dwMain_x + 2, $dwMain_y + 2);
    $globals->{'mainShadow_window'}->resize($dwMain_width, $dwMain_height);
    $globals->{'mainShadow_window'}->show();
    $globals->{'main_window'}->show();
    return 1;
}

sub Configure_event {

#my ($width, $height) = $globals->{'window'}->get_size();
#$MINISCALE = $config->{'Width'} / $width;
#if (!$globals->{'fast'}) {
#    if ($globals->{'canvas'}) {
#        $globals->{'canvas'}->set_pixels_per_unit(1 / $MINISCALE);
#    }
#}
#$globals->{'window'}->set_size_request($width, $config->{'Height'} / $MINISCALE);

    #debug($width . "x" . ($config->{'Height'} / $MINISCALE));
    #debug("Scale: " . $MINISCALE);
}

sub display_fatal {
    my ($message, $error) = @_;
    print STDERR "\n\n-------------\n";
    print STDERR "FATAL ERROR!!\n";
    print STDERR "-------------\n";
    print STDERR "Error description\n";
    print STDERR $message . "\n";
    print STDERR "------------------\n";

    # Don't want this to show up on projector
    #my $errorxml =
    #  Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogError', 'lyricue');
    #$errorxml->signal_autoconnect_from_package('');
    #$errorxml->get_widget('labelError')->set_text($message);
    #my $confirm = $errorxml->get_widget('dialogError')->run();
    #close_dialog($errorxml->get_widget('dialogError'));
    print STDERR "Full error message\n";
    print STDERR "------------------\n";
    die($error);
}

sub close_dialog {
    my ($widget) = @_;
    debug("Close dialog");
    $widget->get_toplevel->destroy;
}

sub mod {
    my ($inp, $div) = @_;
    my $rem = (($inp / $div) - (int($inp / $div))) * $div;
    return $rem;
}

sub set_footer {
    my ($text) = @_;
    if (!defined $text) { $text = " "; }
    if ($globals->{'server_type'} == TRANS) {
        set_footer_trans($text);
    } elsif ($globals->{'server_type'} == SIMPLE) {
        set_footer_simple($text);
    } else {
        set_footer_canvas($text);
    }
}

sub set_footer_simple {
    my ($text) = @_;
    my $font_desc =
      Gtk2::Pango::FontDescription->from_string($config->{'Footer'});
    $globals->{'footerText'}->modify_font($font_desc);
    my $colour = Gtk2::Gdk::Color->parse($config->{'Colour'});
    if ($config->{'BackdropTextColour'} ne "") {
        $colour = Gtk2::Gdk::Color->parse($config->{'BackdropTextColour'});
    }
    $globals->{'footerText'}->modify_fg('normal', $colour);
    $globals->{'footerText'}->set_text($text);
}

sub set_footer_canvas {
    my ($text) = @_;
    my $widget = Gtk2::Label->new();
    my $layout = $widget->create_pango_layout("");
    $layout->set_wrap('word');
    $layout->set_width(
        (
            $config->{'Width'} - ($config->{'OverscanH'} * 2) -
              $config->{'ShadowSize'}
        ) * PANGO_SCALE
    );
    my $desc = Gtk2::Pango::FontDescription->from_string($config->{'Footer'});
    $layout->set_font_description($desc);
    $layout->set_text($text);
    my ($w, $h) = $layout->get_pixel_size;
    $w = $w + 10;
    $h = $h + 10;
    if ($globals->{'footerText'}) { $globals->{'footerText'}->destroy(); }
    $globals->{'footerText'} = Gnome2::Canvas::Item->new(
        $globals->{'root'}, 'Gnome2::Canvas::RichText',
        x                => $config->{'Width'} / 2,
        y                => $config->{'Height'} - $config->{'OverscanV'},
        anchor           => 'GTK_ANCHOR_S',
        width            => $w,
        height           => $h,
        'cursor-visible' => FALSE
    );
    my $buffer = $globals->{'footerText'}->get_buffer();
    my $colour = $config->{'Colour'};

    if ($config->{'BackdropTextColour'} ne "") {
        $colour = $config->{'BackdropTextColour'};
    }
    my $tag3 = $buffer->create_tag(
        "Footer",            "font",
        $config->{'Footer'}, "foreground",
        $colour,             "wrap-mode",
        "word",              "editable-set",
        TRUE,                "editable",
        FALSE
    );
    $buffer->set_text($text);
    $buffer->apply_tag_by_name("Footer", $buffer->get_bounds);
}

sub set_footer_trans {
    my ($text) = @_;
    debug("Setting Footer text");

    #Set our pango layout, to reflect the text and format that we want.
    #NOTE We delebritely set the background to a certain color to convert
    #that color to a alpha channel, that will give us the "clear" background.
    $globals->{'footer_layout'}->set_markup(
"<span background = '#000000' foreground= '#FFFFFF' size='20000' weight = 'ultralight'><i><u>$text</u></i></span>"
    );

    #Get the size of this layout after the text was set.
    my ($dwFooter_w, $dwFooter_h) = $globals->{'footer_layout'}->get_pixel_size;

    # If string was empty then hide windows
    if ($dwFooter_w == 0) {
        $globals->{'footerShadow_window'}->hide();
        $globals->{'footer_window'}->hide();
        return;
    }

    #Now we have the size, we can create a pixmap that will be the
    #'Gtk2::Gdk::Drawable' that we will draw upon
    my $dwFooter_pixmap =
      Gtk2::Gdk::Pixmap->new($globals->{'footer_text'}->window,
        $dwFooter_w, $dwFooter_h, -1);

    #
    my $dwFooter_blank =
      Gtk2::Gdk::Pixmap->new($globals->{'footer_text'}->window,
        $dwFooter_w, $dwFooter_h, -1);
    my $dwFooterShadow_blank =
      Gtk2::Gdk::Pixmap->new($globals->{'footer_text'}->window,
        $dwFooter_w, $dwFooter_h, -1);
    $dwFooter_blank->draw_rectangle($globals->{'footer_text'}->style->white_gc,
        TRUE, 0, 0, $dwFooter_w, $dwFooter_h);
    $dwFooterShadow_blank->draw_rectangle(
        $globals->{'footer_text'}->style->black_gc,
        TRUE, 0, 0, $dwFooter_w, $dwFooter_h);

    #draw the pango layout on the drawable.
    $dwFooter_pixmap->draw_rectangle($globals->{'footer_text'}->style->black_gc,
        TRUE, 0, 0, 6000, 6000);
    $dwFooter_pixmap->draw_layout($globals->{'footer_text'}->style->black_gc,
        0, 0, $globals->{'footer_layout'});

    #create a Gtk2::Gdk::Pixbuf that we will use to grab the pango text from the
    #drawable (Gtk2::Gdk::Pixmap which is a Gtk2::Gdk::Drawable)
    my $dwFooter_pixbuf =
      Gtk2::Gdk::Pixbuf->new('rgb', TRUE, 8, $dwFooter_w, $dwFooter_h);

#here we get create a pixbuff from the drawable, this is where we need the colormap
    $dwFooter_pixbuf->get_from_drawable($dwFooter_pixmap,
        $globals->{'colourmap'},
        0, 0, 0, 0, $dwFooter_w, $dwFooter_h);

    #Remove the background (we use a the color we specified as the pango text's
    #background
    $dwFooter_pixbuf = $dwFooter_pixbuf->add_alpha(TRUE, 0, 0, 0);

    #create a pixmap and mask from the Gtk2::Gdk::Pixbuf
    my ($dwFooter_pm, $dwFooter_m) =
      $dwFooter_pixbuf->render_pixmap_and_mask(1);

    $globals->{'footerShadow_window'}->hide();
    $globals->{'footer_window'}->hide();

    #replace the old $img with the new pixmap and mask
    $globals->{'footer_img'}->set_from_pixmap($dwFooter_blank, undef);
    $globals->{'footerShadow_img'}
      ->set_from_pixmap($dwFooterShadow_blank, undef);

    #shape our window accordingly
    $globals->{'footer_window'}->shape_combine_mask($dwFooter_m,       0, 0);
    $globals->{'footerShadow_window'}->shape_combine_mask($dwFooter_m, 0, 0);

    my $position = ($config->{'Width'} - $dwFooter_w) / 2;
    $globals->{'footer_window'}->move($position,
        $config->{'Height'} - $dwFooter_h - $config->{'ShadowSize'});
    $globals->{'footerShadow_window'}->move(
        $position + $config->{'ShadowSize'},
        $config->{'Height'} - $dwFooter_h
    );
    $globals->{'footerShadow_window'}->show();
    $globals->{'footer_window'}->show();

    return 1;
}

sub set_header {
    my ($text) = @_;
    if (!defined $text) { $text = " "; }
    if ($globals->{'server_type'} == TRANS) {
        set_header_trans($text);
    } elsif ($globals->{'server_type'} == SIMPLE) {
        set_header_simple($text);
    } else {
        set_header_canvas($text);
    }
}

sub set_header_simple {
    my ($text) = @_;
    my $font_desc =
      Gtk2::Pango::FontDescription->from_string($config->{'Header'});
    $globals->{'headerText'}->modify_font($font_desc);
    my $colour = Gtk2::Gdk::Color->parse($config->{'Colour'});
    if ($config->{'BackdropTextColour'} ne "") {
        $colour = Gtk2::Gdk::Color->parse($config->{'BackdropTextColour'});
    }
    $globals->{'headerText'}->modify_fg('normal', $colour);
    $globals->{'headerText'}->set_text($text);
}

sub set_header_canvas {
    my ($text) = @_;

    my $widget = Gtk2::Label->new();
    my $layout = $widget->create_pango_layout("");
    $layout->set_wrap('word');
    $layout->set_width(
        (
            $config->{'Width'} - ($config->{'OverscanH'} * 2) -
              $config->{'ShadowSize'}
        ) * PANGO_SCALE
    );
    my $desc = Gtk2::Pango::FontDescription->from_string($config->{'Header'});
    $layout->set_font_description($desc);
    $layout->set_text($text);
    my ($w, $h) = $layout->get_pixel_size;
    $w            = $w + 10;
    $h            = $h + 10;
    $headerHeight = $h;
    if ($globals->{'headerText'}) { $globals->{'headerText'}->destroy(); }
    $globals->{'headerText'} = Gnome2::Canvas::Item->new(
        $globals->{'root'}, 'Gnome2::Canvas::RichText',
        x                => $config->{'Width'} / 2,
        y                => $config->{'OverscanV'},
        anchor           => 'GTK_ANCHOR_N',
        width            => $w,
        height           => $h,
        'cursor-visible' => FALSE
    );
    my $buffer = $globals->{'headerText'}->get_buffer();
    my $colour = $config->{'Colour'};

    if ($config->{'BackdropTextColour'} ne "") {
        $colour = $config->{'BackdropTextColour'};
    }
    my $tag3 = $buffer->create_tag(
        "Header",            "font",
        $config->{'Header'}, "foreground",
        $colour,             "wrap-mode",
        "word",              "editable-set",
        TRUE,                "editable",
        FALSE
    );
    $buffer->set_text($text);
    $buffer->apply_tag_by_name("Header", $buffer->get_bounds);
}

sub set_header_trans {
    my ($text) = @_;
    debug("Setting Header text");

    #Set our pango layout, to reflect the text and format that we want.
    #NOTE We delebritely set the background to a certain color to convert
    #that color to a alpha channel, that will give us the "clear" background.
    $globals->{'header_layout'}->set_markup(
"<span background = '#000000' foreground= '#FFFFFF' size='20000' weight = 'ultralight'><i><u>$text</u></i></span>"
    );

    #Get the size of this layout after the text was set.
    my ($dwHeader_w, $dwHeader_h) = $globals->{'header_layout'}->get_pixel_size;

    # If string was empty then hide windows
    if ($dwHeader_w == 0) {
        $globals->{'headerShadow_window'}->hide();
        $globals->{'header_window'}->hide();
        return;
    }

    #Now we have the size, we can create a pixmap that will be the
    #'Gtk2::Gdk::Drawable' that we will draw upon
    my $dwHeader_pixmap =
      Gtk2::Gdk::Pixmap->new($globals->{'header_text'}->window,
        $dwHeader_w, $dwHeader_h, -1);
    my $dwHeader_blank =
      Gtk2::Gdk::Pixmap->new($globals->{'header_text'}->window,
        $dwHeader_w, $dwHeader_h, -1);
    my $dwHeaderShadow_blank =
      Gtk2::Gdk::Pixmap->new($globals->{'headerShadow_text'}->window,
        $dwHeader_w, $dwHeader_h, -1);
    $dwHeader_blank->draw_rectangle($globals->{'header_text'}->style->white_gc,
        TRUE, 0, 0, $dwHeader_w, $dwHeader_h);
    $dwHeaderShadow_blank->draw_rectangle(
        $globals->{'header_text'}->style->black_gc,
        TRUE, 0, 0, $dwHeader_w, $dwHeader_h);

    #draw the pango layout on the drawable.
    $dwHeader_pixmap->draw_rectangle(
        $globals->{'headerShadow_text'}->style->black_gc,
        TRUE, 0, 0, 6000, 6000);
    $dwHeader_pixmap->draw_layout($globals->{'header_text'}->style->black_gc,
        0, 0, $globals->{'header_layout'});

    #create a Gtk2::Gdk::Pixbuf that we will use to grab the pango text from the
    #drawable (Gtk2::Gdk::Pixmap which is a Gtk2::Gdk::Drawable)
    my $dwHeader_pixbuf =
      Gtk2::Gdk::Pixbuf->new('rgb', TRUE, 8, $dwHeader_w, $dwHeader_h);

#here we get create a pixbuff from the drawable, this is where we need the colormap
    $dwHeader_pixbuf->get_from_drawable($dwHeader_pixmap,
        $globals->{'colourmap'},
        0, 0, 0, 0, $dwHeader_w, $dwHeader_h);

    #Remove the background (we use a the color we specified as the pango text's
    #    #background
    $dwHeader_pixbuf = $dwHeader_pixbuf->add_alpha(TRUE, 0, 0, 0);

    #create a pixmap and mask from the Gtk2::Gdk::Pixbuf
    my ($dwHeader_pm, $dwHeader_m) =
      $dwHeader_pixbuf->render_pixmap_and_mask(1);

    $globals->{'headerShadow_window'}->hide();
    $globals->{'header_window'}->hide();

    #replace the old $img with the new pixmap and mask
    $globals->{'header_img'}->set_from_pixmap($dwHeader_blank, undef);
    $globals->{'headerShadow_img'}
      ->set_from_pixmap($dwHeaderShadow_blank, undef);

    #shape our window accordingly
    $globals->{'header_window'}->shape_combine_mask($dwHeader_m,       0, 0);
    $globals->{'headerShadow_window'}->shape_combine_mask($dwHeader_m, 0, 0);

    my $position = ($config->{'Width'} - $dwHeader_w) / 2;
    $globals->{'header_window'}->move($position, 0);
    $globals->{'headerShadow_window'}
      ->move($position + $config->{'ShadowSize'}, $config->{'ShadowSize'});
    $globals->{'headerShadow_window'}->show();
    $globals->{'header_window'}->show();

    return 1;
}

sub get_bibles {
    my $bibles;
    open(CONFIG, $globals->{'configfile'})
      || display_fatal($errorcodes->{'fileopenread'} . $globals->{'configfile'},
        $!);
    while (<CONFIG>) {
        chomp;
        my @line = split(/=/);
        $line[0] =~ s/ *$//g;
        $line[1] =~ s/ *$//g;
        $line[1] =~ s/^ *//g;
        my @line2 = split(/;/, $line[1], 2);
        if ($line[0] eq "Bible") {
            $bibles->{$line2[1]} = "db;" . $line2[0];
        }
    }
    open(SWORD, $globals->{'diatheke'} . " -b system -k modulelist|");
    while (<SWORD>) {
        if (/^Biblical Texts:/) {
            while (<SWORD>) {
                if (/^Commentaries:/) {
                    while (<SWORD>) { }
                } else {
                    chomp;
                    my @bible = split(/:/, $_, 2);
                    $bible[0] =~ s/\s+$//;
                    $bible[1] =~ s/^\s+//;
                    $bibles->{$bible[0]} = "sword;" . $bible[1];
                }
            }
        }
    }
    close SWORD;
    return $bibles;
}

sub update_snapshot {
    debug("Updating snapshot");

    #if ($globals->{'preview'}) {

    if (   ($globals->{'server_type'} == CANVAS)
        || ($globals->{'server_type'} == SIMPLE))
    {
        my $window = $globals->{'window'}->window();
        my $pixmap =
          Gtk2::Gdk::Pixbuf->get_from_drawable($window, $window->get_colormap(),
            0, 0, 0, 0, $window->get_size());
        $pixmap->scale_simple(320, 240, 'nearest')
          ->save("/tmp/lyricue.png", "png");
        $globals->{'snapshot_changed'} = TRUE;
    } else {
        my $command =
          "import -silent -window root -resize 200x150 /tmp/lyricue_"
          . $globals->{'use_port'} . ".png";
        `$command`;
        $globals->{'snapshot_changed'} = TRUE;
    }

    #}
    reset_timer($globals->{'snapshot_timer'});
}

sub reset_timer {
    my ($timer) = @_;
    if ($timer) {
        debug("Cancelling timer");
        Glib::Source->remove($timer);
        $timer = FALSE;
    }
}

sub create_window {
    if ($globals->{'server_type'} == TRANS) {
        create_window_trans();
    } else {
        create_window_normal();
    }
}

sub create_window_trans {
    debug("Using transparent server");

    #create a popup window -> this allow us to modify its size to
    #that of a Gtk::Gdk::Pixmap class.
    $globals->{'header_window'}       = Gtk2::Window->new('popup');
    $globals->{'headerShadow_window'} = Gtk2::Window->new('popup');
    $globals->{'main_window'}         = Gtk2::Window->new('popup');
    $globals->{'mainShadow_window'}   = Gtk2::Window->new('popup');
    $globals->{'footer_window'}       = Gtk2::Window->new('popup');
    $globals->{'footerShadow_window'} = Gtk2::Window->new('popup');
    $globals->{'screen_width'}        =
      $globals->{'main_window'}->get_screen->get_width;

    #standard packing widget my $dwHeader_vbox = Gtk2::VBox->new(FALSE,0);
    my $dwHeader_vbox       = Gtk2::VBox->new(FALSE, 0);
    my $dwHeaderShadow_vbox = Gtk2::VBox->new(FALSE, 0);
    my $dwMain_vbox         = Gtk2::VBox->new(FALSE, 0);
    my $dwMainShadow_vbox   = Gtk2::VBox->new(FALSE, 0);
    my $dwFooter_vbox       = Gtk2::VBox->new(FALSE, 0);
    my $dwFooterShadow_vbox = Gtk2::VBox->new(FALSE, 0);

    #create a image containing nothing
    $globals->{'header_img'}       = Gtk2::Image->new_from_pixmap(undef, undef);
    $globals->{'headerShadow_img'} = Gtk2::Image->new_from_pixmap(undef, undef);
    $globals->{'main_img'}         = Gtk2::Image->new_from_pixmap(undef, undef);
    $globals->{'mainShadow_img'}   = Gtk2::Image->new_from_pixmap(undef, undef);
    $globals->{'footer_img'}       = Gtk2::Image->new_from_pixmap(undef, undef);
    $globals->{'footerShadow_img'} = Gtk2::Image->new_from_pixmap(undef, undef);

    $dwHeader_vbox->pack_start($globals->{'header_img'}, FALSE, FALSE, 0);
    $dwHeaderShadow_vbox->pack_start($globals->{'headerShadow_img'},
        FALSE, FALSE, 0);
    $dwMain_vbox->pack_start($globals->{'main_img'}, FALSE, FALSE, 0);
    $dwMainShadow_vbox->pack_start($globals->{'mainShadow_img'},
        FALSE, FALSE, 0);
    $dwFooter_vbox->pack_start($globals->{'footer_img'}, FALSE, FALSE, 0);
    $dwFooterShadow_vbox->pack_start($globals->{'footerShadow_img'},
        FALSE, FALSE, 0);

    $globals->{'header_window'}->add($dwHeader_vbox);
    $globals->{'headerShadow_window'}->add($dwHeaderShadow_vbox);
    $globals->{'main_window'}->add($dwMain_vbox);
    $globals->{'mainShadow_window'}->add($dwMainShadow_vbox);
    $globals->{'footer_window'}->add($dwFooter_vbox);
    $globals->{'footerShadow_window'}->add($dwFooterShadow_vbox);

    #show all BEFORE we add $drawing_area - we do not want to show the drawing
    #area, we only use it as a working surface.
    $globals->{'headerShadow_window'}->show_all();
    $globals->{'header_window'}->show_all();
    $globals->{'mainShadow_window'}->show_all();
    $globals->{'main_window'}->show_all();
    $globals->{'footerShadow_window'}->show_all();
    $globals->{'footer_window'}->show_all();

    # Now hide the windows
    $globals->{'headerShadow_window'}->hide();
    $globals->{'header_window'}->hide();
    $globals->{'mainShadow_window'}->hide();
    $globals->{'main_window'}->hide();
    $globals->{'footerShadow_window'}->hide();
    $globals->{'footer_window'}->hide();

    # Setup background window
    $globals->{'background_window'} = Gtk2::Window->new('toplevel');
    $globals->{'background_window'}->set_title("Lyricue Server");
    do_pending();
    $globals->{'background_window'}->show();
    if ($MINISERV >= 0) {
        $globals->{'background_window'}
          ->resize($config->{'Width'}, $config->{'Height'});
        debug("Running in MINISERV (preview window) mode");
    } else {
        if ($globals->{'run_windowed'} == FALSE) {
            $globals->{'background_window'}->fullscreen();
        }
        debug("Running in SERVER (normal) mode");
    }

    my $bitmap = Gtk2::Gdk::Bitmap->create_from_data(undef, 0, 1, 1);
    my $cursor = Gtk2::Gdk::Cursor->new_from_pixmap(
        $bitmap, $bitmap,
        Gtk2::Gdk::Color->new(0, 0, 0),
        Gtk2::Gdk::Color->new(0, 0, 0),
        0, 0
    );
    $globals->{'background_window'}->window->set_cursor($cursor);
    do_pending();
    my $aspect = $config->{'Width'} / $config->{'Height'};
    my $geom   = Gtk2::Gdk::Geometry->new;
    $geom->min_aspect($aspect);
    $globals->{'background_window'}
      ->set_geometry_hints($globals->{'background_window'},
        $geom, 'GDK_HINT_ASPECT');

    #Create a drawing area, and set is big enough to hold the maximum size our
    ##text will ever be.
    $globals->{'header_text'} = Gtk2::DrawingArea->new;
    $globals->{'header_text'}->set_size_request(1600, 1600);
    $dwHeader_vbox->pack_start($globals->{'header_text'}, FALSE, FALSE, 0);
    $globals->{'headerShadow_text'} = Gtk2::DrawingArea->new;
    $globals->{'headerShadow_text'}->set_size_request(1600, 1600);
    $dwHeaderShadow_vbox->pack_start($globals->{'headerShadow_text'},
        FALSE, FALSE, 0);

    $globals->{'mainShadow_text'} = Gtk2::DrawingArea->new;
    $globals->{'mainShadow_text'}->set_size_request(1600, 1600);
    $dwMainShadow_vbox->pack_start($globals->{'mainShadow_text'},
        FALSE, FALSE, 0);
    $globals->{'main_text'} = Gtk2::DrawingArea->new;
    $globals->{'main_text'}->set_size_request(1600, 1600);
    $dwMain_vbox->pack_start($globals->{'main_text'}, FALSE, FALSE, 0);

    $globals->{'footer_text'} = Gtk2::DrawingArea->new;
    $globals->{'footer_text'}->set_size_request(1600, 1600);
    $dwFooter_vbox->pack_start($globals->{'footer_text'}, FALSE, FALSE, 0);
    $globals->{'footerShadow_text'} = Gtk2::DrawingArea->new;
    $globals->{'footerShadow_text'}->set_size_request(1600, 1600);
    $dwFooterShadow_vbox->pack_start($globals->{'footerShadow_text'},
        FALSE, FALSE, 0);

    #realize it, since it will not be displayed, but we need to
    #get hold of the Gdk classes that needs it to be shown, or realized
    $globals->{'headerShadow_text'}->realize;
    $globals->{'header_text'}->realize;
    $globals->{'mainShadow_text'}->realize;
    $globals->{'main_text'}->realize;
    $globals->{'footerShadow_text'}->realize;
    $globals->{'footer_text'}->realize;

    #create a new pango layout for this drawing area
    $globals->{'header_layout'} =
      $globals->{'header_text'}->create_pango_layout("");
    $globals->{'main_layout'} =
      $globals->{'main_text'}->create_pango_layout("");
    $globals->{'footer_layout'} =
      $globals->{'footer_text'}->create_pango_layout("");

    #set appropriate alignment
    $globals->{'header_layout'}->set_alignment('center');
    $globals->{'main_layout'}->set_alignment('left');
    $globals->{'footer_layout'}->set_alignment('center');

    #set wrap size/type
    $globals->{'header_layout'}
      ->set_width($globals->{'screen_width'} * PANGO_SCALE);
    $globals->{'main_layout'}
      ->set_width($globals->{'screen_width'} * PANGO_SCALE);
    $globals->{'footer_layout'}
      ->set_width($globals->{'screen_width'} * PANGO_SCALE);
    $globals->{'header_layout'}->set_wrap('word_char');
    $globals->{'main_layout'}->set_wrap('word_char');
    $globals->{'footer_layout'}->set_wrap('word_char');

    #get the defalt colormap (will be needed later on)
    $globals->{'colourmap'} = Gtk2::Gdk::Colormap->get_system;
}

sub create_window_normal {
    my ($pixmap, $textcol);

    if ($MINISERV > 0) {
        $globals->{'window'} = Gtk2::Plug->new($MINISERV);
    } else {
        $globals->{'window'} = new Gtk2::Window->new('toplevel');
    }
    $globals->{'window'}->set_title("Lyric Server");

    do_pending();

    $globals->{'window'}->show();
    if ($MINISERV >= 0) {
        $globals->{'window'}->show();
        $globals->{'window'}->realize();
        my $width  = $config->{'Width'};
        my $height = $config->{'Height'};
        my ($width2, $height2) = $globals->{'window'}->get_size();
        $MINISCALE = $width / $width2;
        $width  /= $MINISCALE;
        $height /= $MINISCALE;

        $globals->{'window'}->resize($width, $height);
        debug("Running in MINISERV (preview window) mode");
        debug("W * H:" . $width . "*" . $height);
    } else {
        $globals->{'window'}
          ->set_default_size($config->{'Width'}, $config->{'Height'});
        $globals->{'window'}->resize($config->{'Width'}, $config->{'Height'});
        if ($globals->{'run_windowed'} == FALSE) {
            $globals->{'window'}->fullscreen();
        }
        debug("Running in SERVER (normal) mode");
    }
    my $bitmap = Gtk2::Gdk::Bitmap->create_from_data(undef, 0, 1, 1);
    my $cursor = Gtk2::Gdk::Cursor->new_from_pixmap(
        $bitmap, $bitmap,
        Gtk2::Gdk::Color->new(0, 0, 0),
        Gtk2::Gdk::Color->new(0, 0, 0),
        0, 0
    );
    $globals->{'window'}->window->set_cursor($cursor);

    do_pending();

    my $aspect = $config->{'Width'} / $config->{'Height'};
    my $geom   = Gtk2::Gdk::Geometry->new;
    $geom->min_aspect($aspect);
    $globals->{'window'}
      ->set_geometry_hints($globals->{'window'}, $geom, 'GDK_HINT_ASPECT');
    eval { require Gnome2::Canvas; };
    if ($@) {
        print
          "Optional module Gnome2::Canvas not available. Using simple server\n";
        $globals->{'server_type'} = SIMPLE;
    } else {
        import Gnome2::Canvas;
    }
    if ($globals->{'server_type'} == SIMPLE) {
        debug("Using simple server");
        my $vbox = Gtk2::VBox->new(FALSE, 0);
        $globals->{'window'}->add($vbox);
        debug("Creating text areas");

        # Create the area for lyrics and set styles
        my $mainStyle   = new Gtk2::Style;
        my $headerStyle = new Gtk2::Style;
        my $footerStyle = new Gtk2::Style;
        $globals->{'headerText'} = Gtk2::Label->new();
        $globals->{'mainText'}   = Gtk2::Label->new();
        $globals->{'footerText'} = Gtk2::Label->new();
        $globals->{'headerText'}->set_line_wrap(TRUE);
        $globals->{'headerText'}
          ->set_size_request($config->{'Width'} - ($config->{'OverscanH'} * 2),
            -1);
        $globals->{'headerText'}->set_justify('center');
        $globals->{'mainText'}->set_line_wrap(TRUE);
        $globals->{'footerText'}->set_line_wrap(TRUE);
        $globals->{'footerText'}
          ->set_size_request($config->{'Width'} - ($config->{'OverscanH'} * 2),
            -1);
        $globals->{'footerText'}->set_justify('center');
        $vbox->pack_start($globals->{'headerText'}, FALSE, FALSE, 0);

        if ($config->{'CentreY'}) {
            $vbox->pack_start($globals->{'mainText'}, TRUE, TRUE, 0);
        } else {
            $vbox->pack_start($globals->{'mainText'}, FALSE, TRUE, 0);
            my $fill = Gtk2::Label->new();
            $vbox->pack_start($fill, TRUE, TRUE, 0);
        }
        $vbox->pack_start($globals->{'footerText'}, FALSE, FALSE, 0);
    } else {
        debug("Using full server");
        $globals->{'canvas'} = Gnome2::Canvas->new();
        $globals->{'canvas'}
          ->signal_connect("button_press_event", \&HandleMouse);
        $globals->{'canvas'}
          ->set_scroll_region(0, 0, $config->{'Width'}, $config->{'Height'});
        $globals->{'canvas'}->set_center_scroll_region(TRUE);
        $globals->{'canvas'}->set_pixels_per_unit(1 / $MINISCALE);

        $globals->{'window'}->add($globals->{'canvas'});
        $globals->{'root'} = $globals->{'canvas'}->root;
        $globals->{'canvas'}->show();

        # Find font sizes
        my $layout  = $globals->{'window'}->create_pango_layout("lj");
        my $tmpdesc =
          Gtk2::Pango::FontDescription->from_string($config->{'Header'});
        $layout->set_font_description($tmpdesc);
        ($globals->{'headhigh'}, $globals->{'headwide'}) =
          $layout->get_pixel_size;

        debug("Creating text areas");

        # Create the area for lyrics and set styles
        # Song lyrics - Middle of screen
        $globals->{'mainText'} = Gnome2::Canvas::Item->new(
            $globals->{'root'}, 'Gnome2::Canvas::RichText',
            x                => 0,
            y                => 0,
            width            => 0,
            height           => 0,
            anchor           => 'GTK_ANCHOR_CENTER',
            'cursor-visible' => FALSE
        );
        my $buffer = $globals->{'mainText'}->get_buffer();
        my $colour = $config->{'Colour'};
        if ($config->{'BackdropTextColour'} ne "") {
            $colour = $config->{'BackdropTextColour'};
        }
        my $tag2 = $buffer->create_tag(
            "Main",            "font",
            $config->{'Main'}, "foreground",
            $colour,           "wrap-mode",
            "word",            "editable-set",
            TRUE,              "editable",
            FALSE
        );
        $buffer->apply_tag_by_name("Main", $buffer->get_bounds);
    }

    # callback registration
    $globals->{'window'}->signal_connect("delete_event", \&CloseAppWindow);
    $globals->{'windowid'} = $globals->{'window'}->window->XWINDOW;
    debug("Window ID: " . $globals->{'windowid'});

    $globals->{'window'}->signal_connect("key_press_event", \&HandleKey);
    if ($config->{'ServerMouse'} ne "") {
        $globals->{'window'}
          ->signal_connect("button_press_event", \&HandleMouse);
    }

    # set window attributes and show it
    debug("Showing window");
    $globals->{'window'}->show_all();
    $globals->{'window'}->signal_connect("configure_event", \&Configure_event);
    Configure_event();

    if ($globals->{'server_type'} == CANVAS) {
        $globals->{'canvas'}->update_now();
    }

    debug("Done");
}

sub refresh_screen {
    if ($globals->{'server_type'} == TRANS) {

        # Not needed
    } elsif ($globals->{'server_type'} == SIMPLE) {
        $globals->{'window'}->show_all();
    } else {
        $globals->{'window'}->show_all();
    }
}

sub do_pending {
    while (Gtk2->events_pending) {
        Gtk2->main_iteration;
    }
}

sub get_playlists {
    my $data  = "";
    my $query =
"SELECT title FROM playlists LEFT JOIN playlist ON BINARY playlist.data=playlists.id AND playlist.data NOT LIKE '%-%' WHERE data IS NULL ORDER BY id";
    debug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my @row;
    while (@row = $sth->fetchrow_array()) {
        $data .= $row[0] . "\n";
    }
    return $data;
}

sub get_playlist {
    my ($playlist, $pre) = @_;
    my $data = "";
    if ($playlist =~ /\D/) {
        my $query =
          "SELECT id FROM playlists WHERE title=\"" . $playlist . "\"";
        my $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my $row = $sth->fetchrow_hashref();
        $playlist = $row->{'id'};
    }
    my $query =
        "SELECT * FROM playlist WHERE playlist=\""
      . $playlist
      . "\" ORDER BY playorder";
    debug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    while (my $row = $sth->fetchrow_hashref()) {
        my $title = "";
        if ($row->{'type'} eq "back") {
            my $query2 =
              "SELECT description FROM media WHERE id=\""
              . $row->{'data'} . "\"";
            debug($query2);
            my $sth2 = $mediaDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2 = $sth2->fetchrow_hashref();
            $title = "Background : " . $row2->{'description'};
        } elsif ($row->{'type'} eq "file") {
            $title = "File: " . $row->{'data'};
        } elsif ($row->{'type'} eq "imag") {
            my $query2 =
              "SELECT description FROM media WHERE id=\""
              . $row->{'data'} . "\"";
            my $sth2 = $mediaDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2 = $sth2->fetchrow_hashref();
            $title = "Image: " . $row2->{'description'};
        } elsif ($row->{'type'} eq "vers") {
            $title = "Verses " . $row->{'data'};
        } elsif ($row->{'type'} eq "song") {
            my $query2 =
              "SELECT lyrics FROM page WHERE pageid=" . $row->{'data'};
            my $sth2 = $lyricDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2   = $sth2->fetchrow_hashref();
            my $lyrics = $row2->{'lyrics'};
            if ($globals->{'invert'}) {
                my @lyricl = split(/\n/, $lyrics);
                $title = $lyricl[@lyricl - 1];
            } else {
                ($title, undef) = split(/\n/, $lyrics);
            }
            if (!$title) {
                $title = "";
            }
        } elsif ($row->{'type'} eq "play" | $row->{'type'} eq "sub") {

            # Leave this to later
            my $query2 = "SELECT * FROM playlists WHERE id=" . $row->{'data'};
            my $sth2   = $lyricDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2 = $sth2->fetchrow_hashref();

            $title = $row2->{'title'};

            if ($row2->{'ref'} && $row2->{'ref'} != 0) {
                $query2 =
                  "SELECT songnum FROM lyricMain WHERE id=" . $row2->{'ref'};
                $sth2 = $lyricDbh->prepare($query2)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                $rv2 = $sth2->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);
                $row2 = $sth2->fetchrow_hashref();
                if ($row2->{'songnum'} != 0) {
                    $title = $row2->{'songnum'} . " - " . $title;
                }
            }
        } else {
            $title = "Unknown type";
        }
        $data .= $pre . $row->{'playorder'} . ":" . $title . "\n";

        # Add sublists/playlists
        if ($row->{'type'} eq "play" | $row->{'type'} eq "sub") {
            $data .= get_playlist($row->{'data'}, $pre . "+");
        }
    }

    return $data;
}

sub update_tracker {
    debug("Updating tracker");

    # only do if this is main server
    if ($globals->{'use_port'} == $globals->{'server_port'}) {
        my $item = $current_item;
        if ($globals->{'blanked_state'}) {
            $item = -1;
        }
        my $query = "UPDATE playlists SET ref = " . $item . " WHERE id=-1";
        debug($query);
        my $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
    }
}

sub check_tracker {
    debug("Checking tracker");
    my $query = "SELECT ref FROM playlists WHERE id=-1";
    debug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    if ($sth->fetchrow_array()) {
        update_tracker();
    } else {
        $query = "INSERT INTO playlists (id,ref) VALUES (-1,-1)";
        debug($query);
        my $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
    }
}

sub db_check_app {
    debug("Checking for database servers");
    if (defined $globals->{'force_sqlite'} && ($globals->{'force_sqlite'})) {
        debug("Forcing usage of SQLite3");
        $globals->{'db_type'} = "SQLite";
        return;
    }
    my @ary    = DBI->available_drivers(1);
    my $mysql  = FALSE;
    my $sqlite = FALSE;
    foreach (@ary) {
        if ($_ eq "mysql") {
            $mysql = TRUE;
        } elsif ($_ eq "SQLite") {
            $sqlite = TRUE;
        }
    }
    if ($mysql) {
        $globals->{'db_type'} = "mysql";
    } elsif ($sqlite) {
        $globals->{'db_type'} = "SQLite";
    } else {
        die("No supported DB found");
    }
}

sub db_connect {
    my ($dbname, $dberror) = @_;
    my ($dbh);
    if ($globals->{'db_type'} eq "SQLite") {
        $dbh =
          DBI->connect("dbi:SQLite:" . $globals->{'basedir'} . $dbname . ".db",
            "", "")
          || display_fatal($dberror, $DBI::errstr);
    } else {
        $dbh =
          DBI->connect(
            "DBI:" . $globals->{'db_type'} . ":$dbname:$globals->{'host'}",
            "lyric", "")
          || display_fatal($dberror, $DBI::errstr);
    }
    return $dbh;
}

sub HandleMouse {
    my ($widget, $data) = @_;
    debug("Handle Mouse click");
    if ($data->button == 1) {
        update_display("display", "next_page", $config->{'Loop'});
    } elsif ($data->button == 2) {
        update_display("display", "next_song", 0);
    } elsif ($data->button == 3) {
        update_display("display", "prev_page", $config->{'Loop'});
    }
}
