#!/usr/bin/perl -w
# This tests the various methods of the Graphics::Framebuffer module
# and shows the only way threads will work with it.

use strict;

use threads;
use threads::shared;
use Graphics::Framebuffer;
use Sys::CPU;
use Term::ANSIScreen qw(:cursor :screen :color);

$| = 1;

our $VERSION = 2.02;

my $file          = 0;
my $hold : shared = 0;
my @waiting : shared;
my $RUNNING : shared = 1;
my $arg = join('', @ARGV);
my $Threads = $arg =~ /(\d+)/ ? $1 : Sys::CPU::cpu_count();

$file = 1 if ($arg =~ /file/i);    # File Handle mode

print "\nRunning on a ", Sys::CPU::cpu_type(), " with $Threads threads\n";
sleep 2;

my @framebuffer;
my $splash = 1;
my $two = 1;
$two = 2 if (-e "/dev/fb0" && -e "/dev/fb1");
my $device = 0;
foreach my $thr (0 .. $Threads) {
    $framebuffer[$thr] = Graphics::Framebuffer->new('FILE_MODE' => $file, 'SPLASH' => $splash,'FB_DEVICE' => sprintf('/dev/fb%d',$device));
    if ($two == 2) {
		$device++;
		$device = 0 if ($device==2);
    }
    $splash = 0 if ($thr >= ($two - 1));
}
my ($screen_width, $screen_height) = $framebuffer[0]->screen_dimensions();

my $top = 16 * $Threads;    # Font height * # of threads

$framebuffer[0]->cls();
cls;

$SIG{'QUIT'} = \&finish;
$SIG{'INT'}  = \&finish;
my @thrd;
foreach my $page (1 .. $Threads) {
    $waiting[$page] = 0;
    $thrd[$page]    = threads->create(
        sub {
            my $Page = shift;
            my $top  = shift;
            my ($screen_width, $screen_height) = $framebuffer[$Page]->screen_dimensions();
            if (defined($framebuffer[$Page]->{'ERROR'})) {
                print locate($Page, 1), clline, "ERROR: Thread $Page, $framebuffer[$Page]->{'ERROR'}" if ($screen_width > 255);
            }
            $framebuffer[$page]->clip_set({'x' => 0, 'y' => $top, 'xx' => $screen_width, 'yy' => $screen_height}) if ($screen_width > 255);
            while ($RUNNING) {
                attract($Page,$screen_width,$screen_height);
                while ($hold && $RUNNING) {
                    print locate($Page, 1), clline, colored(['white on_yellow'], "Thread $Page"), ' Waiting for other threads to finish drawing before clearing screen...' if ($screen_width > 255);
                    threads->yield();
                    $waiting[$Page] = 0;
                    sleep 1;
                    select(STDOUT);$|=1;
                }
                $waiting[$Page] = 1;
                select(STDOUT);$|=1;
            }
            print locate($Page + 1, 1), clline, colored(['black on_green'], "Thread $Page"), ' Finished' if ($screen_width > 255);
            select(STDOUT);$|=1;
        },
        $page,
        $top
    );
} ## end foreach my $page (1 .. $Threads)
while (1) {
    $hold = 0;
    sleep 30;
    $hold = 1;
    foreach my $page (1 .. $Threads) {
        while ($waiting[$page] && $thrd[$page]->is_running()) {
            sleep 1;
        }
    }
    foreach my $c (0..$Threads) {
        $framebuffer[$c]->cls();
    }
} ## end while (1)

##############################################################################
##                             ATTRACT MODE                                 ##
##############################################################################
# Remeniscent of the "Atract Mode" of the old Atari 8 bit computers, this    #
# mode merely puts random patterns on the screen.                            #
##############################################################################

sub attract {
    my $page   = shift;
    my $screen_width = shift;
    my $screen_height = shift;
    my $red    = int(rand(256));
    my $grn    = int(rand(256));
    my $blu    = int(rand(256));
    my $x      = int(rand($screen_width));
    my $y      = int(rand($screen_height));
    my $w      = int(rand($screen_width / 3));
    my $h      = int(rand($screen_height / 3));
    my $rx     = int(rand($screen_width / 5));
    my $ry     = int(rand($screen_height / 5));
    my $sd     = int(rand(360));
    my $ed     = int(rand(360));
    my $gr     = (rand(10) / 10);
    my $mode   = int(rand(4));
    my $type   = int(rand(13));
    my $size   = int(rand(3));
    my $arc    = int(rand(3));
    my $filled = int(rand(2));

    $framebuffer[$page]->set_color({'red' => $red, 'green' => $grn, 'blue' => $blu});
    $framebuffer[$page]->draw_mode($mode);

    if ($type == 0) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Plotting point at %04d,%04d with pixel size %02d', $x, $y, $size);
        $framebuffer[$page]->plot({'x' => $x, 'y' => $y, 'pixel_size' => $size});
    } elsif ($type == 1) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing from %04d,%04d to %04d,%04d with pixel size %02d', $page, $x, $y, $w, $h, $size);
        $framebuffer[$page]->plot({'x' => $x, 'y' => $y, 'pixel_size' => $size});
        $framebuffer[$page]->drawto({'x' => $w, 'y' => $h, 'pixel_size' => $size});
    } elsif ($type == 2) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Circle at %04d,%04d with Radius %04d with pixel size %d, filled? %s', $x, $y, $rx, $size, ($filled) ? 'Yes' : 'No');
        $framebuffer[$page]->circle({'x' => $x, 'y' => $y, 'radius' => $rx, 'filled' => $filled, 'pixel_size' => $size});
    } elsif ($type == 3) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Ellipse at %04d,%04d with Radii %04d,%04d with pixel size %d, filled? %s', $x, $y, $rx, $ry, $size, ($filled) ? 'Yes' : 'No');
        $framebuffer[$page]->ellipse({'x' => $x, 'y' => $y, 'xradius' => $rx, 'yradius' => $ry, 'filled' => $filled, 'factor' => 1, 'pixel_size' => $size});
    } elsif ($type == 4) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Box at %04d,%04d and %04d,%04d with pixel size %d, filled? %s', $x, $y, $w, $h, $size, ($filled) ? 'Yes' : 'No');
        $framebuffer[$page]->rbox({'x' => $x, 'y' => $y, 'width' => $w, 'height' => $h, 'filled' => $filled, 'pixel_size' => $size});
    } elsif ($type == 5) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Arc at %04d,%04d with Radius %04d, Start %03d, End %03d with pixel size %d, Type? %s', $x, $y, $ry, $sd, $ed, $size, arctype($arc));
        $framebuffer[$page]->draw_arc({'x' => $x, 'y' => $y, 'radius' => $ry, 'start_degrees' => $sd, 'end_degrees' => $ed, 'granularity' => $gr, 'mode' => $arc, 'pixel_size' => $size});
    } elsif ($type == 6) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Gradiant Box at %04d,%04d and %04d,%04d', $x, $y, $w, $h);
        $framebuffer[$page]->rbox(
            {
                'x'          => $x,
                  'y'          => $y,
                  'width'      => $w,
                  'height'     => $h,
                  'filled'     => 1,
                  'pixel_size' => 1,
                  'gradient'   => {
                      'start' => {
                          'red'   => rand(256),
                          'green' => rand(256),
                          'blue'  => rand(256)
                      },
                      'end' => {
                          'red'   => rand(256),
                          'green' => rand(256),
                          'blue'  => rand(256)
                      }
                  }
            }
        );
    } elsif ($type == 7) {
        my @poly;
        foreach my $count (0 .. int(rand(6))) {
            push(@poly, int(rand($screen_width)));
            push(@poly, int(rand($screen_height)));
        }
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Polygon at points %s with pixel size %d, filled? %s', join(',', @poly), $size, ($filled) ? 'Yes' : 'No');
        $framebuffer[$page]->polygon({'pixel_size' => $size, 'coordinates' => \@poly, 'filled' => (int(rand(2)))});
    } elsif ($type == 8) {
        my @poly;
        foreach my $count (0 .. int(rand(6))) {
            push(@poly, int(rand($screen_width)));
            push(@poly, int(rand($screen_height)));
        }
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Gradient Polygon at points %s', join(',', @poly));
        $framebuffer[$page]->polygon(
            {
                'pixel_size'  => 1,
                  'coordinates' => \@poly,
                  'filled'      => 1,
                  'gradient'    => {
                      'start' => {
                          'red'   => rand(256),
                          'green' => rand(256),
                          'blue'  => rand(256)
                      },
                      'end' => {
                          'red'   => rand(256),
                          'green' => rand(256),
                          'blue'  => rand(256)
                      }
                  }
            }
        );
    } elsif ($type == 9) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Gradient Circle at %04d,%04d with Radius %04d', $x, $y, $rx);
        $framebuffer[$page]->circle(
            {
                'x'        => $x,
                  'y'        => $y,
                  'radius'   => $rx,
                  'filled'   => 1,
                  'gradient' => {
                      'start' => {
                          'red'   => rand(256),
                          'green' => rand(256),
                          'blue'  => rand(256)
                      },
                      'end' => {
                          'red'   => rand(256),
                          'green' => rand(256),
                          'blue'  => rand(256)
                      }
                  }
            }
        );
    } elsif ($type == 10) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Gradient Ellipse at %04d,%04d with Radii %04d,%04d', $x, $y, $rx, $ry);
        $framebuffer[$page]->ellipse(
            {
                'x'        => $x,
                  'y'        => $y,
                  'xradius'  => $rx,
                  'yradius'  => $ry,
                  'filled'   => 1,
                  'factor'   => 1,
                  'gradient' => {
                      'start' => {
                          'red'   => rand(256),
                          'green' => rand(256),
                          'blue'  => rand(256)
                      },
                      'end' => {
                          'red'   => rand(256),
                          'green' => rand(256),
                          'blue'  => rand(256)
                      }
                  }
            }
        );
    } elsif ($type == 11) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Rounded Box at %04d,%04d and %04d,%04d with pixel size %d, filled? %s', $x, $y, $w, $h, $size, ($filled) ? 'Yes' : 'No');
        $framebuffer[$page]->rbox({'x' => $x, 'y' => $y, 'width' => $w, 'height' => $h, 'filled' => $filled, 'radius' => rand(30) + 2, 'pixel_size' => $size});
    } elsif ($type == 12) {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Drawing Rounded Gradiant Box at %04d,%04d and %04d,%04d', $x, $y, $w, $h);
        $framebuffer[$page]->rbox(
            {
                'x'          => $x,
                  'y'          => $y,
                  'width'      => $w,
                  'height'     => $h,
                  'filled'     => 1,
                  'pixel_size' => 1,
                  'radius'     => rand(30) + 2,
                  'gradient'   => {
                      'start' => {
                          'red'   => rand(256),
                          'green' => rand(256),
                          'blue'  => rand(256)
                      },
                      'end' => {
                          'red'   => rand(256),
                          'green' => rand(256),
                          'blue'  => rand(256)
                      }
                  }
            }
        );
    } else {
        print locate($page, 1), clline, colored(['black on_green'], "Thread $page"), sprintf(' Flood Fill starting at %04d,%04d', $x, $y);
        $framebuffer[$page]->fill({'x' => $x, 'y' => $y});
    }
} ## end sub attract

sub arctype {
    my $type = shift;
    if ($type == 0) {
        return ('Arc');
    } elsif ($type == 1) {
        return ('Pie');
    } else {
        return ('Poly Arc');
    }
} ## end sub arctype

sub finish {
    $RUNNING = 0;
    print locate(1, 1), "SHUTTING DOWN THREADS...";
    foreach my $thr (threads->list()) {
        $thr->detach();
    }
    sleep 1;
    cls;
    exec('reset');
} ## end sub finish

__END__

=head1 NAME

Threads Test

=head1 DESCRIPTION

Demonstrates how to use threads with "Graphics::Framebuffer"

=head1 SYNOPSIS

perl threadstest.pl [mali] [file] [# of threads]

=head1 OPTIONS

The options can be passed in any order and all are optional.

=over 1
=item B< mali >

Causes the Graphics::Framebuffer module to correct for driver errors
with some MALI GPUs in ARM chipsets.  While it is mainly designed to
override some of the calculations for Mali chipsets, it may work with
others.  Only use if you are either getting errors, or weirdness in
drawing.

Contact the Graphics::Framebuffer author if you are having troubles
getting it to work.

=item B< file >

Changes the internal logic of Graphics::Framebuffer to instead draw
using a file handle, instead of using a Mmapped variable.

Only use if you are having stability issues, as it is slower.

=item B< # of threads >

This is an integer (yes, just a number) indicating how many threads
to spawn to draw.

Typically this is done automatically with the number of cores the
script detects, but this overrides the detection.

=back
=cut
