#!/usr/bin/env perl

use strict;

# use warnings; # Use just for development, otherwise leave warnings off
use constants {
    TRUE  => 1,
    FALSE => 0,
};

use threads;
use threads::shared;
use Sys::CPU;                 # Finds the number of cores your CPU has
use Graphics::Framebuffer;    # There are things to import, if you want, but they
                              # are usually not needed.

BEGIN {
    our $VERSION = '0.01';
}

##############################################################################
## Initialize any global variables here ######################################

our $threads = Sys::CPU::cpu_count();

##############################################################################
# Initialize any shared variables here

our $RUNNING : shared  = TRUE;
our $OK_START : shared = FALSE;

##############################################################################
# Initialize your threads here, to prevent copying any unneeded large
# variables.
our @THREADS;
for (my $count = 0; $count < $threads; $count++) {
    push(@THREADS, threads->create(\&thread_runner, [$parameter]));
}

##############################################################################

# It's good to have a framebuffer object open in the main thread.
# $FB is your framebuffer object.  See the documentation, if you want to pass
# any parameters when initializing the module, but no parameters should be
# just fine to get started.
my $FB = Graphics::Framebuffer->new('SPLASH' => 0);

$FB->cls('OFF');    # Turn off the console cursor

# You can optionally set graphics mode here, but remember to turn on text mode
# before exiting.

$FB->graphics_mode();    # Shuts off all text and cursors.

# Gathers information on the screen for you to use as global information
our $screen_info = $FB->screen_dimensions();

alarm 0;
$SIG{'ALRM'} = sub {    # Hard exit if a thread is hung and won't play nice
    $FB->text_mode();
    foreach my $t (@THREADS) {
        $t->kill('KILL')->detach();
    }
    exec('reset');
};
foreach my $sig (qw(INT HUP TERM QUIT KILL)) {
    $SIG{$sig} = \&finish;
}
## Do your stuff in here #####################################################

##############################################################################

$RUNNING = FALSE;    # Tell threads to exit
$FB->text_mode();    # Turn text and cursor back on.  You MUST do this if
                     # graphics mode was set.
$FB->cls('ON');      # Turn the console cursor back on
exit(0);

sub finish {
    $RUNNING = FALSE;
    alarm 20;
    foreach my $t (@THREADS) {
        $t->join();
    }
    exec('reset');
} ## end sub finish

# You want your thread to remain running and act as a job processor.  It is
# not good to call and exit threads a lot.  So set up shared variables to
# communicate between threads.
#
# If your thread is going to set a shared variable, then make sure to use
# locking to prevent another thread from stepping on its toes:
#
#    {
#        lock($variable);
#        $variable = "something";
#    }

sub thread_runner {
    my $parameters = shift;

    while ($RUNNING && !$OK_START) {
        threads->yield();    # wait until the main process says it's ok to proceed.
    }

    if ($RUNNING) {          # Always make sure the thread has permission to do more
        my $fb = Graphics::Framebuffer->new('SPLASH' => FALSE);
        foreach my $sig (qw(INT HUP TERM QUIT KILL)) {
            local $SIG{$sig} = sub { $fb->text_mode(); threads->exit; };
        }

        while ($RUNNING) {

            # do your stuff in here
        }
    } ## end if ($RUNNING)
} ## end sub thread_runner

__END__

=head1 NAME

Threaded template file for writing scripts that use Graphics::Framebuffer and
threads.

=head1 SYNOPIS

First, copy this file, and name the copy whatever you want (using "yourscript"
for this example):

 cp threaded_template.pl yourscript.pl

Now edit "yourscript.pl" from now on.  Please do not directly edit
"threaded_template.pl".

=head1 DESCRIPTION

Use this file as a starting point for writing your scripts.  Copy it so as to
not destroy the original template, then edit the copy.

=cut
