The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl
#
# App.pm
#

package SDLx::App;

use strict;
use warnings;
use Carp;
use SDL;

use SDL::Rect;
use SDL::Video;
use SDL::Event;
use SDL::Events;
use SDL::Surface;
use SDL::PixelFormat;
use SDLx::Surface;
use base 'SDLx::Surface';

sub new {
    my $proto   = shift;
    my $class   = ref($proto) || $proto;
    my %options = @_;

    # SDL_INIT_VIDEO() is 0, so check defined instead of truth.
    unless ( exists( $options{-noinit} ) )    # we shouldn't do init always
    {
        my $init =
          defined $options{-init}
          ? $options{-init}
          : SDL::SDL_INIT_EVERYTHING;

        SDL::init($init);
    }
    my $t  = $options{-title}      || $options{-t}  || $0;
    my $it = $options{-icon_title} || $options{-it} || $t;
    my $ic = $options{-icon}       || $options{-i}  || "";
    my $w  = $options{-width}      || $options{-w}  || 800;
    my $h  = $options{-height}     || $options{-h}  || 600;
    my $d  = $options{-depth}      || $options{-d}  || 16;
    my $f = $options{-flags}      || $options{-f} || SDL::Video::SDL_ANYFORMAT;
    my $r = $options{-red_size}   || $options{-r} || 5;
    my $g = $options{-green_size} || $options{-g} || 5;
    my $b = $options{-blue_size}  || $options{-b} || 5;
    my $a = $options{-alpha_size} || $options{-a} || 0;
    my $ras = $options{-red_accum_size}   || $options{-ras} || 0;
    my $gas = $options{-green_accum_size} || $options{-gas} || 0;
    my $bas = $options{-blue_accum_size}  || $options{-bas} || 0;
    my $aas = $options{-alpha_accum_size} || $options{-aas} || 0;
    my $db  = $options{-double_buffer}    || $options{-db}  || 0;

    my $bs = $options{-buffer_size}  || $options{-bs} || 0;
    my $st = $options{-stencil_size} || $options{-st} || 0;
    my $async = $options{-asyncblit} || 0;

    $f |= SDL::Video::SDL_OPENGL if ( $options{-gl} || $options{-opengl} );
    $f |= SDL::Video::SDL_FULLSCREEN
      if ( $options{-fullscreen} || $options{-full} );
    $f |= SDL::Video::SDL_RESIZABLE if ( $options{-resizeable} );
    $f |= SDL::Video::SDL_DOUBLEBUF if ($db);
    $f |= SDL::Video::SDL_ASYNCBLIT if ($async);

    if ( $f & SDL::Video::SDL_OPENGL ) {
        $SDLx::App::USING_OPENGL = 1;
        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_RED_SIZE(), $r )
          if ($r);
        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_GREEN_SIZE(), $g )
          if ($g);
        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_BLUE_SIZE(), $b )
          if ($b);
        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_ALPHA_SIZE(), $a )
          if ($a);

        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_RED_ACCUM_SIZE(),
            $ras )
          if ($ras);
        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_GREEN_ACCUM_SIZE(),
            $gas )
          if ($gas);
        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_BLUE_ACCUM_SIZE(),
            $bas )
          if ($bas);
        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_ALPHA_ACCUM_SIZE(),
            $aas )
          if ($aas);

        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_DOUBLEBUFFER(),
            $db )
          if ($db);
        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_BUFFER_SIZE(),
            $bs )
          if ($bs);
        SDL::Video::GL_set_attribute( SDL::Constants::SDL_GL_DEPTH_SIZE(), $d );
    }
    else {
        $SDLx::App::USING_OPENGL = 0;
    }

    my $self = SDL::Video::set_video_mode( $w, $h, $d, $f )
      or croak SDL::get_error();
    $self = SDLx::Surface->new( surface => $self );
    if ( $ic and -e $ic ) {
        my $icon = SDL::Video::load_BMP($ic);
        SDL::Video::wm_set_icon($$icon);
    }

    SDL::Video::wm_set_caption( $t, $it );

    bless $self, $class;
    return $self;
}

sub resize ($$$) {
    my ( $self, $w, $h ) = @_;
    my $flags = $self->flags;
    if ( $flags & SDL::Video::SDL_RESIZABLE ) {
        my $bpp = $self->format->BitsPerPixel;
        $self = SDL::Video::set_video_mode( $w, $h, $bpp, $flags )
          or die "SDL cannot set video:" . SDL::get_error;
    }
    else {
        die "Application surface not resizable";
    }
}

sub title ($;$) {
    my $self = shift;
    my ( $title, $icon );
    if (@_) {
        $title = shift;
        $icon = shift || $title;
        SDL::Video::wm_set_caption( $title, $icon );
    }
    return SDL::Video::wm_get_caption();
}

sub delay ($$) {
    my $self  = shift;
    my $delay = shift;
    SDL::delay($delay);
}

sub ticks {
    return SDL::get_ticks();
}

sub error {
    return SDL::get_error();
}

sub warp ($$$) {
    my $self = shift;
    SDL::Mouse::warp_mouse(@_);
}

sub fullscreen ($) {
    my $self = shift;
    SDL::Video::wm_toggle_fullscreen($self);
}

sub iconify ($) {
    my $self = shift;
    SDL::Video::wm_iconify_window();
}

sub grab_input ($$) {
    my ( $self, $mode ) = @_;
    SDL::Video::wm_grab_input($mode);
}

sub loop ($$) {
    my ( $self, $href ) = @_;
    my $event = SDL::Event->new();
    while ( SDL::Events::wait_event($event) ) {
        if ( ref( $$href{ $event->type() } ) eq "CODE" ) {
            &{ $$href{ $event->type() } }($event);
        }
    }
}

sub sync ($) {
    my $self = shift;
    if ($SDLx::App::USING_OPENGL) {
        SDL::Video::GL_swap_buffers();
    }
    else {
        $self->flip();
    }
}

sub attribute ($$;$) {
    my ( $self, $mode, $value ) = @_;
    return undef unless ($SDLx::App::USING_OPENGL);
    if ( defined $value ) {
        SDL::Video::GL_set_attribute( $mode, $value );
    }
    my $returns = SDL::Video::GL_get_attribute($mode);
    croak "SDLx::App::attribute failed to get GL attribute"
      if ( $$returns[0] < 0 );
    $$returns[1];
}

1;