Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merged experimental into master

  • Loading branch information...
commit 3512b1b6790a2e6beea76316aba8583ec45c7fc2 2 parents 7e80853 + c13693c
@kthakore kthakore authored
Showing with 2,051 additions and 854 deletions.
  1. +1 −1  Build.PL
  2. +31 −0 CHANGELOG
  3. +0 −8 examples/SDLx/SDLx_C_Interface.pl
  4. +0 −1  examples/SDLx/SDLx_Sound.pl
  5. +0 −2  examples/SDLx/SDLx_controller_two_squares.pl
  6. +1 −1  examples/SDLx/SDLx_text.pl
  7. +1 −1  examples/SDLx/SDLx_text_shadow.pl
  8. +1 −1  examples/SDLx/SDLx_text_styles.pl
  9. +1 −1  examples/SDLx/SDLx_text_wordwrap.pl
  10. +1 −1  examples/SDLx/SDLx_text_zoom.pl
  11. +4 −8 examples/SDLx/app.pl
  12. +1 −1  examples/SDLx/music.pl
  13. +1 −3 examples/SDLx/pong.pl
  14. +1 −1  lib/SDL.pm
  15. +5 −2 lib/SDL/Constants.pm
  16. +2 −1  lib/SDL/Event.pm
  17. +2 −1  lib/SDL/Events.pm
  18. +2 −0  lib/SDL/GFX/ImageFilter.pm
  19. +13 −0 lib/SDL/Version.pm
  20. +299 −165 lib/SDLx/App.pm
  21. +185 −117 lib/SDLx/Controller.pm
  22. +48 −7 lib/SDLx/Surface.pm
  23. +30 −0 lib/pods/SDL/Deprecated.pod
  24. +56 −51 lib/pods/SDL/Events.pod
  25. +117 −0 lib/pods/SDL/Platform.pod
  26. +6 −0 lib/pods/SDL/Surface.pod
  27. +367 −105 lib/pods/SDLx/App.pod
  28. +382 −130 lib/pods/SDLx/Controller.pod
  29. +5 −5 src/Mixer/Channels.xs
  30. +107 −125 src/SDLx/Controller/Interface.xs
  31. +4 −0 t/core_cd.t
  32. +2 −3 t/gfx_imagefilter.t
  33. +1 −2  t/gfx_primitives.t
  34. +2 −4 t/gfx_primitives2.t
  35. +3 −6 t/gfx_rotozoom.t
  36. +1 −2  t/image.t
  37. +2 −4 t/mixer.t
  38. +16 −5 t/mixer_channels.t
  39. +27 −25 t/mixer_music.t
  40. +2 −5 t/mixer_samples.t
  41. +9 −5 t/sdlx_app.t
  42. +288 −29 t/sdlx_controller.t
  43. +20 −15 t/sdlx_controller_interface.t
  44. +1 −1  t/sdlx_fps.t
  45. +2 −2 t/sdlx_surface.t
  46. +1 −7 t/ttf.t
  47. BIN  test/data/sample.ogg
  48. BIN  test/data/silence.ogg
View
2  Build.PL
@@ -606,7 +606,7 @@ my $build = $package->new(
#create_readme => 1, ### make sense only if there is some POD doc in the file specified by dist_version_from
meta_merge => {
resources => {
- bugtracker => 'http://sdlperl.ath.cx/projects/SDLPerl',
+ bugtracker => 'http://github.com/PerlGameDev/SDL/issues',
repository => 'http://github.com/PerlGameDev/SDL'
}
},
View
31 CHANGELOG
@@ -2,6 +2,37 @@ Revision history for Perl extension SDL_perl.
Versioning rule: public releases are even numbers, dev releases are odd. (same like perl dist)
+* 2.537_03 Apr 12 2012
+ - SDLx::App made the docs a lot better [Blaizer]
+ - SDLx::App changed around shortcut names in the constructor [Blaizer]
+ - SDLx::App added and improved parameters of the constructor, see docs [Blaizer]
+ - SDLx::App updated methods resize, title, icon, error, show_cursor, grab_input [Blaizer]
+ - SDLx::App fullscreen method works better [Blaizer]
+ - SDLx::App new init method does our initializing right [Blaizer]
+ - SDLx::App new set_video_mode method does set_video_mode for SDLx::App [Blaizer]
+ - SDLx::App new screen_size method returns the user's screen size [Blaizer]
+ - SDLx::App warp method renamed to warp_cursor, attribute renamed to gl_attribute [Blaizer]
+ - SDLx::App fix to return the user's resolution to normal when a fullscreen app closes [FROGGS]
+ - SDLx::App removed delay method and deprecated get_ticks [Blaizer]
+ - SDLx::Controller removed eoq, its action is on by default and implemented by stop_handler [Blaizer]
+ - SDLx::Controller made the docs a lot better, even proofread them [Blaizer]
+ - SDLx::Controller pause works by stopping the app [Blaizer]
+ - SDLx::Controller added stopped and paused methods to tell what the app is doing [Blaizer]
+ - SDLx::Controller added max_t param, by default slows down apps going at less than 10 FPS [Blaizer]
+ - SDLx::Controller added time and sleep methods to replace get_ticks and delay [Blaizer]
+ - SDLx::Controller added some tests for pausing and events [Blaizer]
+ - SDLx::Controller removed current_time parameter [Blaizer]
+
+* 2.537_02 Feb 13 2012
+ - t/core_cd.t: gnu hurd 0.3 handles devices like cdrom strange (skipping tests) [FROGGS]
+ - t/sdlx_fps.t: seems better to try to get 5 fps (slow vm's) [FROGGS]
+ - SDLx::Controller::Interface: weaken tests [FROGGS]
+ - SDL::Mixer::Channels: skipping callback test [FROGGS]
+ - SDL::Version: we can now compare SDL::Version objects like: $version >= 1.2.10 [FROGGS]
+ - SDLx::App::DESTROY: fixed function name for set_video_mode [FROGGS]
+ - SDLx::App: fix for issue 144, setting original screen res when app ends [FROGGS]
+ - t/sdlx_controller_interface.t: adding delay so that slow machines doesnt get a hickup [FROGGS]
+
* 2.536 Jan 04 2011
- using INT2PTR to convert stored pointers to right size and hide warnings [FROGGS]
- SDL::Mixer::Channels: using malloc instead of safemalloc [FROGGS]
View
8 examples/SDLx/SDLx_C_Interface.pl
@@ -34,14 +34,6 @@
$app->draw_rect( [ 100 - $state->x, $state->y, 2, 2 ], 0xFF0FFF );
};
-#an event handler to exit
-my $event = sub {
- $_[1]->stop if $_[0]->type == SDL_QUIT;
-};
-
-
-$app->add_event_handler($event);
-
#clear the screen
$app->add_show_handler( sub { $app->draw_rect( [ 0, 0, $app->w, $app->h ], 0x000000 ) } );
View
1  examples/SDLx/SDLx_Sound.pl
@@ -44,7 +44,6 @@
# pause or resume on keydown
$app->add_event_handler( sub{
my $e = $_[0];
- $_[1]->stop() if $e->type == SDL_QUIT;
if( $e->type == SDL_KEYDOWN )
{
print "Ai\n";
View
2  examples/SDLx/SDLx_controller_two_squares.pl
@@ -101,8 +101,6 @@ sub on_event {
$ball->{x_vel} += $ball->{vel} if $key == SDLK_LEFT;
$ball->{x_vel} -= $ball->{vel} if $key == SDLK_RIGHT;
- } elsif ( $event->type == SDL_QUIT ) {
- $_[0]->stop;
}
}
View
2  examples/SDLx/SDLx_text.pl
@@ -5,7 +5,7 @@
use SDLx::App;
use SDLx::Text;
-my $app = SDLx::App->new( eoq => 1 );
+my $app = SDLx::App->new();
my $text = SDLx::Text->new;
View
2  examples/SDLx/SDLx_text_shadow.pl
@@ -7,7 +7,7 @@
use SDLx::App;
use SDLx::Text;
-my $app = SDLx::App->new( eoq => 1 );
+my $app = SDLx::App->new();
my $normal = SDLx::Text->new;
my $shadow = SDLx::Text->new( shadow => 1 );
View
2  examples/SDLx/SDLx_text_styles.pl
@@ -5,7 +5,7 @@
use SDLx::App;
use SDLx::Text;
-my $app = SDLx::App->new( eoq => 1 );
+my $app = SDLx::App->new();
my $text = SDLx::Text->new;
View
2  examples/SDLx/SDLx_text_wordwrap.pl
@@ -5,7 +5,7 @@
use SDLx::App;
use SDLx::Text;
-my $app = SDLx::App->new( eoq => 1 );
+my $app = SDLx::App->new();
my $text = SDLx::Text->new( word_wrap => 450 );
View
2  examples/SDLx/SDLx_text_zoom.pl
@@ -7,7 +7,7 @@
use SDLx::App;
use SDLx::Text;
-my $app = SDLx::App->new( eoq => 1, width => 400, height => 100 );
+my $app = SDLx::App->new( width => 400, height => 100 );
my $text = SDLx::Text->new;
View
12 examples/SDLx/app.pl
@@ -7,15 +7,11 @@
height => 480,
);
+sub draw_lines {
+ $app->draw_line( [ rand $app->w, rand $app->h ], [ rand $app->w, rand $app->h ], 0xFFFFFFFF );
+ $app->update();
+}
-
-sub draw_lines { $app->draw_line( [ 0, 0 ], [ rand( $app->w ), rand( $app->h ) ], 0xFFFFFFFF ); $app->update(); }
-
-sub event_handle { my $e = shift; $_[0]->stop if ( $e->type == SDL_QUIT ); }
-
-$app->add_event_handler( \&event_handle );
$app->add_show_handler( \&draw_lines );
$app->run();
-
-
View
2  examples/SDLx/music.pl
@@ -4,4 +4,4 @@
$music->data( sam => "test/data/sample.wav" );
$sam = $music->data("sam");
$music->play($sam);
-while ( $music->playing ) { print "playing\n" }
+while ( $music->playing ) { print "playing\n"; sleep 1; }
View
4 examples/SDLx/pong.pl
@@ -29,7 +29,7 @@
y => 0,
w => 20,
h => 80,
- vel => 250,
+ vel => 130,
y_vel => 0,
};
@@ -143,8 +143,6 @@ sub on_event {
my $key = $event->key_sym;
$paddle->{y_vel} += $paddle->{vel} if $key == SDLK_UP;
$paddle->{y_vel} -= $paddle->{vel} if $key == SDLK_DOWN;
- } elsif ( $event->type == SDL_QUIT ) {
- exit;
}
}
View
2  lib/SDL.pm
@@ -54,7 +54,7 @@ our %EXPORT_TAGS = (
defaults => $SDL::Constants::EXPORT_TAGS{'SDL/defaults'}
);
-our $VERSION = '2.536';
+our $VERSION = '2.537_02';
$VERSION = eval $VERSION;
print "$VERSION" if ( defined( $ARGV[0] ) && ( $ARGV[0] eq '--SDLperl' ) );
View
7 lib/SDL/Constants.pm
@@ -1031,6 +1031,11 @@ use constant {
}; # SDL::Events/keymod
use constant {
+ SDL_DEFAULT_REPEAT_DELAY => 500,
+ SDL_DEFAULT_REPEAT_INTERVAL => 30,
+}; # SDL::Events/repeat
+
+use constant {
SMOOTHING_OFF => 0,
SMOOTHING_ON => 1,
}; # SDL::GFX/smoothing
@@ -1217,8 +1222,6 @@ use constant {
FPS_LOWER_LIMIT => 1,
FPS_DEFAULT => 30,
SDL_ALL_HOTKEYS => 0xFFFFFFFF,
- SDL_DEFAULT_REPEAT_DELAY => 500,
- SDL_DEFAULT_REPEAT_INTERVAL => 30,
};
use constant {
View
3  lib/SDL/Event.pm
@@ -24,7 +24,8 @@ our %EXPORT_TAGS = (
app => $SDL::Constants::EXPORT_TAGS{'SDL::Events/app'},
button => $SDL::Constants::EXPORT_TAGS{'SDL::Events/button'},
keysym => $SDL::Constants::EXPORT_TAGS{'SDL::Events/keysym'},
- keymod => $SDL::Constants::EXPORT_TAGS{'SDL::Events/keymod'}
+ keymod => $SDL::Constants::EXPORT_TAGS{'SDL::Events/keymod'},
+ repeat => $SDL::Constants::EXPORT_TAGS{'SDL::Events/repeat'}
);
1;
View
3  lib/SDL/Events.pm
@@ -24,7 +24,8 @@ our %EXPORT_TAGS = (
app => $SDL::Constants::EXPORT_TAGS{'SDL::Events/app'},
button => $SDL::Constants::EXPORT_TAGS{'SDL::Events/button'},
keysym => $SDL::Constants::EXPORT_TAGS{'SDL::Events/keysym'},
- keymod => $SDL::Constants::EXPORT_TAGS{'SDL::Events/keymod'}
+ keymod => $SDL::Constants::EXPORT_TAGS{'SDL::Events/keymod'},
+ repeat => $SDL::Constants::EXPORT_TAGS{'SDL::Events/repeat'}
);
1;
View
2  lib/SDL/GFX/ImageFilter.pm
@@ -19,4 +19,6 @@ our %EXPORT_TAGS = (
smoothing => $SDL::Constants::EXPORT_TAGS{'SDL::GFX/smoothing'}
);
+MMX_on();
+
1;
View
13 lib/SDL/Version.pm
@@ -8,6 +8,19 @@ our @ISA = qw(Exporter DynaLoader);
use SDL::Internal::Loader;
internal_load_dlls(__PACKAGE__);
+use overload '<=>' => \&my_cmp,
+ '""' => \&stringify;
+
bootstrap SDL::Version;
+sub stringify {
+ my $self = shift;
+ return sprintf "%s%s%s", chr($self->major), chr($self->minor), chr($self->patch);
+}
+
+sub my_cmp {
+ my ($left, $right) = @_;
+ return "$left" cmp "$right";
+}
+
1;
View
464 lib/SDLx/App.pm
@@ -1,216 +1,350 @@
-#!/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 Data::Dumper;
-use Scalar::Util 'refaddr';
+
+# SDL modules actually used here
+use SDL ();
+use SDL::Video ();
+use SDL::Mouse ();
+use SDL::Event ();
+use SDL::Surface ();
+use SDL::VideoInfo ();
+use SDLx::Validate ();
use base qw/SDLx::Surface SDLx::Controller/;
+# SDL modules used for other reasons
+# Please verify their usefulness here
+use SDL::Rect ();
+use SDL::Events ();
+use SDL::PixelFormat ();
+
+use Carp ();
+use Scalar::Util qw/refaddr/;
+
+my %_stash;
+my $_screen_w;
+my $_screen_h;
+my $_screen_d;
+
+$SDLx::App::USING_OPENGL = 0;
+
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 ) {
+ my $class = shift;
+
+ my %o = @_;
+
+ # undef is not a valid input
+ my $w = defined $o{width} ? $o{width} : defined $o{w} ? $o{w} : 640;
+ my $h = defined $o{height} ? $o{height} : defined $o{h} ? $o{h} : 480;
+ my $d = defined $o{depth} ? $o{depth} : defined $o{d} ? $o{d} : undef;
+ my $f = defined $o{flags} ? $o{flags} : defined $o{f} ? $o{f} : 0;
+ my $pos = defined $o{position} ? $o{position} : defined $o{pos} ? $o{pos} : undef;
+ my $ico = $o{icon};
+
+ # undef is a valid input
+ my $t = $o{title};
+ my $it = $o{icon_title};
+ my $init = exists $o{initialize} ? $o{initialize} : $o{init};
+ my $s = exists $o{stash} ? $o{stash} : {};
+ my $icc = $o{icon_color_key};
+
+ # boolean
+ my $sw = $o{software_surface} || $o{sw_surface} || $o{sw};
+ my $hw = $o{hardware_surface} || $o{hw_surface} || $o{hw};
+ my $ab = $o{asynchronous_blit} || $o{async_blit};
+ my $af = $o{any_format};
+ my $hwp = $o{hardware_palette} || $o{hw_palette};
+ my $db = $o{double_buffer} || $o{double_buf} || $o{dbl_buf};
+ my $fs = $o{full_screen} || $o{fullscreen} || $o{full};
+ my $gl = $o{open_gl} || $o{opengl} || $o{gl};
+ my $rs = $o{resizable} || $o{resizeable}; # it's a hard word to spell :-)
+ my $nf = $o{no_frame};
+ my $ncur = $o{hide_cursor} || $o{no_cursor};
+ my $cen = $o{centered} || $o{center};
+ my $gi = $o{grab_input};
+ my $nc = $o{no_controller};
+
+ unless ( defined $d ) {
+ # specify SDL_ANYFORMAT flag if depth isn't defined
+ $d = 32;
+ $af = 1;
+ }
+
+ # used to say unless no_init here. I don't think we need it anymore
+ if( !defined $init ) {
+ SDLx::App->init( SDL::SDL_INIT_EVERYTHING );
+ }
+ else {
+ if( ref $init ) {
+ push @$init, "video";
+ }
+ else {
+ $init |= SDL::SDL_INIT_VIDEO
+ }
+ SDLx::App->init( $init );
+ }
+
+ # keep the screen's original res so we can set the app to that when we're done
+ unless(defined $_screen_w && defined $_screen_h && defined $_screen_d) {
+ my $video_info = SDL::Video::get_video_info();
+ if($video_info) {
+ $_screen_w = $video_info->current_w;
+ $_screen_h = $video_info->current_h;
+ $_screen_d = $video_info->vfmt->BitsPerPixel;
+ }
+ }
+
+ $f |= SDL::Video::SDL_SWSURFACE if $sw;
+ $f |= SDL::Video::SDL_HWSURFACE if $hw;
+ $f |= SDL::Video::SDL_ASYNCBLIT if $ab;
+ $f |= SDL::Video::SDL_ANYFORMAT if $af;
+ $f |= SDL::Video::SDL_HWPALETTE if $hwp;
+ $f |= SDL::Video::SDL_DOUBLEBUF if $db;
+ $f |= SDL::Video::SDL_FULLSCREEN if $fs;
+ $f |= SDL::Video::SDL_OPENGL if $gl;
+ $f |= SDL::Video::SDL_RESIZABLE if $rs;
+ $f |= SDL::Video::SDL_NOFRAME if $nf;
+
+ # we'll let SDL decide which takes priority and set both if both are specified
+ $ENV{SDL_VIDEO_CENTERED} = $cen if $cen;
+ $ENV{SDL_VIDEO_WINDOW_POS} = $pos if $pos;
+
+ if ( $gl ) {
$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 $r = defined $o{gl_red_size} ? $o{gl_red_size} : defined $o{gl_red} ? $o{gl_red} : 5;
+ my $g = defined $o{gl_green_size} ? $o{gl_green_size} : defined $o{gl_green} ? $o{gl_green} : 5;
+ my $b = defined $o{gl_blue_size} ? $o{gl_blue_size} : defined $o{gl_blue} ? $o{gl_blue} : 5;
+ my $a = defined $o{gl_alpha_size} ? $o{gl_alpha_size} : defined $o{gl_alpha} ? $o{gl_alpha} : undef;
+ my $ra = defined $o{gl_accum_red_size} ? $o{gl_accum_red_size} : defined $o{gl_accum_red} ? $o{gl_accum_red} : undef;
+ my $ga = defined $o{gl_accum_green_size} ? $o{gl_accum_green_size} : defined $o{gl_accum_green} ? $o{gl_accum_green} : undef;
+ my $ba = defined $o{gl_accum_blue_size} ? $o{gl_accum_blue_size} : defined $o{gl_accum_blue} ? $o{gl_accum_blue} : undef;
+ my $aa = defined $o{gl_accum_alpha_size} ? $o{gl_accum_alpha_size} : defined $o{gl_accum_alpha} ? $o{gl_accum_alpha} : undef;
+ my $bs = defined $o{gl_buffer_size} ? $o{gl_buffer_size} : defined $o{gl_buffer} ? $o{gl_buffer} : undef;
+ my $ss = defined $o{gl_stencil_size} ? $o{gl_stencil_size} : defined $o{gl_stencil} ? $o{gl_stencil} : undef;
+ my $msb = defined $o{gl_multi_sample_buffers} ? $o{gl_multi_sample_buffers} : undef;
+ my $mss = defined $o{gl_multi_sample_samples} ? $o{gl_multi_sample_samples} : undef;
+
+ # boolean
+ my $s = $o{gl_stereo};
+ my $sc = $o{gl_swap_control};
+ my $av = $o{gl_accelerated_visual};
+
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_RED_SIZE, $r ) if defined $r;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_GREEN_SIZE, $g ) if defined $g;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_BLUE_SIZE, $b ) if defined $b;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_ALPHA_SIZE, $a ) if defined $a;
+
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_ACCUM_RED_SIZE, $ra ) if defined $ra;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_ACCUM_GREEN_SIZE, $ga ) if defined $ga;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_ACCUM_BLUE_SIZE, $ba ) if defined $ba;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_ACCUM_ALPHA_SIZE, $aa ) if defined $aa;
+
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_BUFFER_SIZE, $bs ) if defined $bs;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_DOUBLEBUFFER, $db ) if defined $db;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_DEPTH_SIZE, $d );
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_STENCIL_SIZE, $ss ) if defined $ss;
+
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_STEREO, $s ) if defined $s;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_MULTISAMPLEBUFFERS, $msb ) if defined $msb;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_MULTISAMPLESAMPLES, $mss ) if defined $mss;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_SWAP_CONTROL, $sc ) if defined $sc;
+ SDL::Video::GL_set_attribute( SDL::Video::SDL_GL_ACCELERATED_VISUAL, $av ) if defined $av;
}
+ # icon must be set before set_video_mode
+ SDLx::App->icon( $ico, $icc ) if defined $ico;
+
+ my $self = $class->set_video_mode( $w, $h, $d, $f );
+ $self->SDLx::Controller::new( %o ) unless $nc;
+
+ $t = defined $it ? $it : $0 unless defined $t;
+ $it = $t unless defined $it;
+ $self->title( $t, $it );
+
+ $self->show_cursor( 0 ) if $ncur;
+ $self->grab_input( $gi ) if $gi;
+ $self->stash = $s;
+
+ $self;
+}
+
+sub set_video_mode {
+ my ( $self, $w, $h, $d, $f ) = @_;
my $surface = SDL::Video::set_video_mode( $w, $h, $d, $f )
- or Carp::confess SDL::get_error();
- $options{surface} = $surface;
+ or Carp::confess( "set_video_mode failed: ", SDL::get_error() );
+ $surface = SDLx::Surface->new( surface => $surface );
+
+ # if we already have an app
+ if( ref $self ) {
+ return $self;
+ }
+ return bless $surface, $self;
+}
- my $self = SDLx::Surface->new(%options);
+sub DESTROY {
+ my ( $self ) = @_;
- if ( $ic and -e $ic ) {
- my $icon = SDL::Video::load_BMP($ic);
- SDL::Video::wm_set_icon($icon);
+ # set original screen size when app ends
+ if(defined $_screen_w && defined $_screen_h && defined $_screen_d) {
+ SDL::Video::set_video_mode( $_screen_w, $_screen_h, $_screen_d, $self->flags );
}
- SDL::Video::wm_set_caption( $t, $it );
- $self = $self->SDLx::Controller::new(%options);
- bless $self, $class;
+ my $ref = refaddr($self);
+ delete $_stash{ $ref };
+}
-
- return $self;
+sub stash :lvalue {
+ $_stash{ refaddr( $_[0] ) };
+}
+
+sub init {
+ my ( undef, $init ) = @_;
+
+ if ( ref $init ) {
+ # make a hash with keys of the values in the init array
+ my %init = map { $_ => 1 } @$init;
+ undef $init;
+
+ $init |= SDL::SDL_INIT_TIMER if $init{timer};
+ $init |= SDL::SDL_INIT_AUDIO if $init{audio};
+ $init |= SDL::SDL_INIT_VIDEO if $init{video};
+ $init |= SDL::SDL_INIT_CDROM if $init{cd_rom} || $init{cdrom};
+ $init |= SDL::SDL_INIT_JOYSTICK if $init{joystick};
+ $init |= SDL::SDL_INIT_EVERYTHING if $init{everything} || $init{all};
+ $init |= SDL::SDL_INIT_NOPARACHUTE if $init{no_parachute};
+ $init |= SDL::SDL_INIT_EVENTTHREAD if $init{event_thread};
+ }
+
+ # if anything is already inited, only init specified extra subsystems
+ if ( SDL::was_init( SDL::SDL_INIT_EVERYTHING ) ) {
+ SDL::init_sub_system( $init )
+ and Carp::cluck( "SDL init_sub_system failed: ", SDL::get_error() );
+ }
+ else {
+ SDL::init( $init )
+ and Carp::confess( "SDL init failed: ", SDL::get_error() );
+ }
+}
+
+sub screen_size {
+ SDLx::App->init( SDL::SDL_INIT_VIDEO );
+
+ my $video_info = SDL::Video::get_video_info();
+
+ return( $video_info->current_w, $video_info->current_h, $video_info->vfmt->BitsPerPixel );
}
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";
- }
+
+ my $d = $self->format->BitsPerPixel;
+ my $f = $self->flags;
+
+ $self->set_video_mode( $w, $h, $d, $f );
}
sub title {
- my $self = shift;
- my ( $title, $icon );
- if (@_) {
- $title = shift;
- $icon = shift || $title;
- SDL::Video::wm_set_caption( $title, $icon );
+ my ( undef, $title, $icon_title ) = @_;
+ if ( @_ > 1 ) {
+ my ($t, $it) = SDL::Video::wm_get_caption;
+ $title = $t unless defined $title;
+ $icon_title = $it unless defined $icon_title;
+ return SDL::Video::wm_set_caption( $title, $icon_title );
}
- return SDL::Video::wm_get_caption();
+ SDL::Video::wm_get_caption;
}
-sub delay {
- my $self = shift;
- my $delay = shift;
- SDL::delay($delay);
+sub icon {
+ my ( undef, $icon, $color ) = @_;
+ SDLx::App->init( SDL::SDL_INIT_VIDEO );
+ unless( UNIVERSAL::isa( $icon, "SDL::Surface" ) ) {
+ $icon = SDL::Video::load_BMP( $icon );
+ $icon or Carp::confess( "Could not load_BMP icon '$icon': ", SDL::get_error() );
+ }
+ if( defined $color ) {
+ $color = SDLx::Validate::map_rgb( $color, $icon->format );
+ SDL::Video::set_color_key( $icon, SDL::Video::SDL_SRCCOLORKEY, $color );
+ }
+ SDL::Video::wm_set_icon( $icon );
}
-sub ticks {
- return SDL::get_ticks();
+sub error {
+ shift;
+ if ( @_ == 1 and !defined $_[0] ) {
+ return SDL::clear_error;
+ }
+ if ( @_ ) {
+ return SDL::set_error_real( @_ );
+ }
+ SDL::get_error;
}
-sub error {
- return SDL::get_error();
+sub warp_cursor {
+ my ( undef, $x, $y ) = @_;
+ SDL::Mouse::warp_mouse( $x || 0, $y || 0 );
}
-sub warp {
- my $self = shift;
- SDL::Mouse::warp_mouse(@_);
+sub show_cursor {
+ my ( undef, $show ) = @_;
+ if ( @_ > 1 ) {
+ return SDL::Mouse::show_cursor( SDL::Event::SDL_ENABLE ) if $show and $show ne SDL::Event::SDL_QUERY;
+ return SDL::Mouse::show_cursor( SDL::Event::SDL_DISABLE ) unless $show;
+ }
+ SDL::Mouse::show_cursor( SDL::Event::SDL_QUERY );
}
sub fullscreen {
- my $self = shift;
- SDL::Video::wm_toggle_fullscreen($self);
+ my ( $self ) = @_;
+ return 1 if SDL::Video::wm_toggle_fullscreen( $self );
+
+ # fallback to doing it with set_video_mode()
+ my $d = $self->format->BitsPerPixel;
+ my $f = $self->flags;
+
+ eval { $self->set_video_mode( 0, 0, $d, $f ^ SDL::Video::SDL_FULLSCREEN ) };
+ return 1 unless $@;
+
+ # failed going fullscreen, let's revert back
+ $self->set_video_mode( 0, 0, $d, $f );
+
+ # report failure to go fullscreen
+ return 0;
}
sub iconify {
- my $self = shift;
- SDL::Video::wm_iconify_window();
+ SDL::Video::wm_iconify_window;
}
sub grab_input {
- my ( $self, $mode ) = @_;
- SDL::Video::wm_grab_input($mode);
-}
-
-sub sync {
- my $self = shift;
- if ($SDLx::App::USING_OPENGL) {
- SDL::Video::GL_swap_buffers();
- } else {
- $self->flip();
+ my ( undef, $grab ) = @_;
+ if (@_ > 1) {
+ return SDL::Video::wm_grab_input( SDL::Video::SDL_GRAB_ON ) if $grab and $grab ne SDL::Video::SDL_GRAB_QUERY;
+ return SDL::Video::wm_grab_input( SDL::Video::SDL_GRAB_OFF ) unless $grab;
}
+ SDL::Video::wm_grab_input( SDL::Video::SDL_GRAB_QUERY );
}
-sub attribute {
- my ( $self, $mode, $value ) = @_;
- return undef unless ($SDLx::App::USING_OPENGL);
- if ( defined $value ) {
- SDL::Video::GL_set_attribute( $mode, $value );
+sub sync {
+ my ( $self ) = @_;
+ if ( $SDLx::App::USING_OPENGL ) {
+ return SDL::Video::GL_swap_buffers;
}
- my $returns = SDL::Video::GL_get_attribute($mode);
- Carp::confess "SDLx::App::attribute failed to get GL attribute"
- if ( $$returns[0] < 0 );
- $$returns[1];
+ SDL::Video::flip( $self );
}
+sub gl_attribute {
+ my ( undef, $mode, $value ) = @_;
-
-my %_stash;
-sub stash :lvalue{
- my $ref = refaddr($_[0]);
- $_stash{ $ref } = {} unless $_stash{ $ref };
- return $_stash{ $ref }
+ return unless $SDLx::App::USING_OPENGL;
+ if ( @_ > 2 ) {
+ return SDL::Video::GL_set_attribute( $mode, $value );
+ }
+ my $returns = SDL::Video::GL_get_attribute( $mode );
+ Carp::cluck( "SDL::Video::GL_get_attribute failed to get GL attribute" )
+ if $returns->[0] < 0;
+ $returns->[1];
}
1;
-
View
302 lib/SDLx/Controller.pm
@@ -1,180 +1,215 @@
package SDLx::Controller;
use strict;
use warnings;
-use Carp;
-use Time::HiRes;
-use SDL;
-use SDL::Event;
-use SDL::Events;
-use SDL::Video;
+use Carp ();
+use Time::HiRes ();
+use SDL ();
+use SDL::Event ();
+use SDL::Events ();
+use SDL::Video ();
use SDLx::Controller::Interface;
use SDLx::Controller::State;
use Scalar::Util 'refaddr';
-# inside out, so this can work as the superclass of another
-# SDL::Surface subclass
+# inside out, so this can work as the superclass of another class
my %_dt;
my %_min_t;
-my %_current_time;
+my %_max_t;
my %_stop;
my %_event;
my %_event_handlers;
my %_move_handlers;
my %_show_handlers;
-my %_sleep_cycle;
-my %_eoq;
+my %_delay;
my %_paused;
+my %_time;
+my %_stop_handler;
sub new {
my ($self, %args) = @_;
- if(ref $self) {
- bless $self, ref $self;
- }
- else {
+
+ # if $self is blessed then it has to isa controller, so let's not even bless it to this class
+ unless(ref $self) {
my $a;
$self = bless \$a, $self;
}
my $ref = refaddr $self;
- $_dt{ $ref } = defined $args{dt} ? $args{dt} : 0.1;
- $_min_t{ $ref } = defined $args{min_t} ? $args{min_t} : 1 / 60;
-# $_current_time{ $ref } = $args{current_time} || 0; #no point
- $_stop{ $ref } = $args{stop};
- $_event{ $ref } = $args{event} || SDL::Event->new();
- $_event_handlers{ $ref } = $args{event_handlers} || [];
- $_move_handlers{ $ref } = $args{move_handlers} || [];
- $_show_handlers{ $ref } = $args{show_handlers} || [];
- $_sleep_cycle{ $ref } = $args{delay};
- $_eoq{$ref} = $args{exit_on_quit} || $args{eoq} || 0;
-# $_paused{ $ref } = $args{paused}; #read only
+ $_dt{ $ref } = defined $args{dt} ? $args{dt} : 0.1;
+ $_min_t{ $ref } = defined $args{min_t} ? $args{min_t} : 1 / 60;
+ $_max_t{ $ref } = defined $args{max_t} ? $args{max_t} : 0.1;
+ $_stop{ $ref } = 1;
+ $_event{ $ref } = $args{event} || SDL::Event->new();
+ $_event_handlers{ $ref } = $args{event_handlers} || [];
+ $_move_handlers{ $ref } = $args{move_handlers} || [];
+ $_show_handlers{ $ref } = $args{show_handlers} || [];
+ $_delay{ $ref } = defined $args{delay} && $args{delay} >= 1 ? $args{delay} / 1000 : $args{delay} || 0; # accepts seconds or ticks
+# $_paused{ $ref } = undef;
+ $_time{ $ref } = $args{time} || 0;
+ $_stop_handler{ $ref } = exists $args{stop_handler} ? $args{stop_handler} : \&default_stop_handler;
return $self;
}
-
-sub delay {
- my $self = shift;
- my $delay = shift;
- my $ref = refaddr $self;
-
- $_sleep_cycle{ $ref } = $delay if $delay;
- return $self;
-}
-
sub DESTROY {
my $self = shift;
my $ref = refaddr $self;
- delete $_dt{ $ref};
- delete $_min_t{ $ref};
- delete $_current_time{ $ref};
- delete $_stop{ $ref};
- delete $_event{ $ref};
- delete $_event_handlers{ $ref};
- delete $_move_handlers{ $ref};
- delete $_show_handlers{ $ref};
- delete $_sleep_cycle { $ref };
- delete $_eoq{$ref};
- delete $_paused{$ref};
+ delete $_dt{ $ref };
+ delete $_min_t{ $ref };
+ delete $_max_t{ $ref };
+ delete $_stop{ $ref };
+ delete $_event{ $ref };
+ delete $_event_handlers{ $ref };
+ delete $_move_handlers{ $ref };
+ delete $_show_handlers{ $ref };
+ delete $_delay { $ref };
+ delete $_paused{ $ref };
+ delete $_time{ $ref };
+ delete $_stop_handler{ $ref };
}
sub run {
- my ($self) = @_;
- my $ref = refaddr $self;
- my $dt = $_dt{ $ref };
- my $min_t = $_min_t{ $ref };
- my $t = 0.0;
+ my ($self) = @_;
+ my $ref = refaddr $self;
+ my $dt = $_dt{ $ref };
+ my $delay = $_delay{ $ref };
- #Allows us to do stop and run
- $_stop{ $ref } = 0;
+ # these should never change
+ my $event_handlers = $_event_handlers{ $ref };
+ my $move_handlers = $_move_handlers{ $ref };
+ my $show_handlers = $_show_handlers{ $ref };
- $_current_time{ $ref } = Time::HiRes::time;
- while ( !$_stop{ $ref } ) {
- $self->_event($ref);
+ # alows us to do stop and run
+ delete $_stop{ $ref };
+ delete $_paused{ $ref };
+
+ my $current_time = Time::HiRes::time;
+ Time::HiRes::sleep( $delay ) if $delay;
+ while ( !$_stop{ $ref } ) {
my $new_time = Time::HiRes::time;
- my $delta_time = $new_time - $_current_time{ $ref };
- next if $delta_time < $min_t;
- $_current_time{ $ref} = $new_time;
+ my $delta_time = $new_time - $current_time;
+ if( $delta_time < $_min_t{ $ref } ) {
+ Time::HiRes::sleep( 0.001 ); # sleep at least a millisecond
+ redo;
+ }
+ $current_time = $new_time;
+ $delta_time = $_max_t{ $ref } if $delta_time > $_max_t{ $ref };
my $delta_copy = $delta_time;
+ my $time_ref = \$_time{ $ref };
+
+ $self->_event( $ref, $event_handlers );
while ( $delta_copy > $dt ) {
- $self->_move( $ref, 1, $t ); #a full move
+ $self->_move( $move_handlers, 1, $$time_ref ); # a full move
$delta_copy -= $dt;
- $t += $dt;
+ $$time_ref += $dt;
}
my $step = $delta_copy / $dt;
- $self->_move( $ref, $step, $t ); #a partial move
- $t += $dt * $step;
+ $self->_move( $move_handlers, $step, $$time_ref ); # a partial move
+ $$time_ref += $dt * $step;
- $self->_show( $ref, $delta_time );
+ $self->_show( $show_handlers, $delta_time );
- $dt = $_dt{ $ref}; #these can change
- $min_t = $_min_t{ $ref}; #during the cycle
- SDL::delay( $_sleep_cycle{ $ref } ) if $_sleep_cycle{ $ref };
+ Time::HiRes::sleep( $delay ) if $delay;
+
+ # these can change during the cycle
+ $dt = $_dt{ $ref };
+ $delay = $_delay{ $ref };
}
+ # pause works by stopping the app and running it again
+ if( $_paused{ $ref } ) {
+ delete $_stop{ $ref };
+
+ $self->_pause($ref);
+
+ delete $_paused{ $ref };
+
+ # exit out of this sub before going back in so we don't recurse deeper and deeper
+ goto $self->can('run')
+ unless $_stop{ $ref };
+ }
}
-sub exit_on_quit {
- my ($self, $value) = @_;
+sub stop {
+ my $ref = refaddr $_[0];
- my $ref = refaddr $self;
- if (defined $value) {
- $_eoq{$ref} = $value;
- }
+ $_stop{ $ref } = 1;
- return $_eoq{$ref};
+ # if we're going to stop we don't want to pause
+ delete $_paused{ $ref };
+}
+sub stopped {
+ # returns true if the app is stopped or about to stop
+ $_stop{ refaddr $_[0] };
}
-*eoq = \&exit_on_quit; # alias
+sub _pause {
+ my ($self, $ref) = @_;
+ my $event = $_event{ $ref };
+ my $stop_handler = $_stop_handler{ $ref };
+ my $callback = $_paused{ $ref };
+
+ do {
+ SDL::Events::pump_events(); # don't know if we need this
+ SDL::Events::wait_event( $event ) or Carp::confess("pause failed waiting for an event");
+ $stop_handler->( $event, $self ) if $stop_handler;
+ }
+ until
+ $_stop{ $ref } # stop set by stop_handler
+ or !$callback or $callback->( $event, $self )
+ or $_stop{ $ref } # stop set by callback
+ ;
+}
sub pause {
my ($self, $callback) = @_;
my $ref = refaddr $self;
- $callback ||= sub {1};
- my $event = SDL::Event->new();
- $_paused{ $ref} = 1;
- while(1) {
- SDL::Events::wait_event($event) or Carp::confess("pause failed waiting for an event");
- if($callback->($event, $self)) {
- $_current_time{ $ref} = Time::HiRes::time; #so run doesn't catch up with the time paused
- last;
- }
- }
- delete $_paused{ $ref};
+
+ # if we're going to stop we don't want to pause
+ return if !$_paused{ $ref } and $_stop{ $ref };
+
+ $_stop{ $ref } = 1;
+ $_paused{ $ref } = $callback;
+}
+sub paused {
+ # returns the callback (always true) if the app is paused or about to pause
+ $_paused{ refaddr $_[0] };
}
sub _event {
- my ($self, $ref) = @_;
+ my ($self, $ref, $event_handlers) = @_;
+ my $stop_handler = $_stop_handler{ $ref };
+ my $event = $_event{ $ref };
+
SDL::Events::pump_events();
- while ( SDL::Events::poll_event( $_event{ $ref} ) ) {
- $self->_exit_on_quit( $_event{ $ref} ) if $_eoq{$ref};
- foreach my $event_handler ( @{ $_event_handlers{ $ref} } ) {
+ while ( SDL::Events::poll_event( $event ) ) {
+ $stop_handler->( $event, $self ) if $stop_handler;
+ foreach my $event_handler ( @$event_handlers ) {
next unless $event_handler;
- $event_handler->( $_event{ $ref}, $self );
+ $event_handler->( $event, $self );
}
}
}
sub _move {
- my ($self, $ref, $move_portion, $t) = @_;
- foreach my $move_handler ( @{ $_move_handlers{ $ref} } ) {
+ my ($self, $move_handlers, $move_portion, $t) = @_;
+ foreach my $move_handler ( @$move_handlers ) {
next unless $move_handler;
$move_handler->( $move_portion, $self, $t );
}
}
sub _show {
- my ($self, $ref, $delta_ticks) = @_;
- foreach my $show_handler ( @{ $_show_handlers{ $ref} } ) {
+ my ($self, $show_handlers, $delta_ticks) = @_;
+ foreach my $show_handler ( @$show_handlers ) {
next unless $show_handler;
$show_handler->( $delta_ticks, $self );
}
}
-sub stop { $_stop{ refaddr $_[0] } = 1 }
-
sub _add_handler {
my ( $arr_ref, $handler ) = @_;
push @{$arr_ref}, $handler;
@@ -183,27 +218,27 @@ sub _add_handler {
sub add_move_handler {
my $ref = refaddr $_[0];
- return _add_handler( $_move_handlers{ $ref}, $_[1] );
+ return _add_handler( $_move_handlers{ $ref }, $_[1] );
}
sub add_event_handler {
my $ref = refaddr $_[0];
Carp::confess 'SDLx::App or a Display (SDL::Video::get_video_mode) must be made'
unless SDL::Video::get_video_surface();
- return _add_handler( $_event_handlers{ $ref}, $_[1] );
+ return _add_handler( $_event_handlers{ $ref }, $_[1] );
}
sub add_show_handler {
my $ref = refaddr $_[0];
- return _add_handler( $_show_handlers{ $ref}, $_[1] );
+ return _add_handler( $_show_handlers{ $ref }, $_[1] );
}
sub _remove_handler {
my ( $arr_ref, $id ) = @_;
if ( ref $id ) {
($id) = grep {
- $id eq $arr_ref->[$_]
- } 0..$#{$arr_ref};
+ $id eq $arr_ref->[$_]
+ } 0..$#{$arr_ref};
if ( !defined $id ) {
Carp::cluck("$id is not currently a handler of this type");
@@ -254,39 +289,72 @@ sub show_handlers { $_show_handlers{ refaddr $_[0] } }
sub dt {
my ($self, $arg) = @_;
my $ref = refaddr $self;
- $_dt{ $ref} = $arg if defined $arg;
+ $_dt{ $ref } = $arg if defined $arg;
- $_dt{ $ref};
+ $_dt{ $ref };
}
sub min_t {
my ($self, $arg) = @_;
my $ref = refaddr $self;
- $_min_t{ $ref} = $arg if defined $arg;
+ $_min_t{ $ref } = $arg if defined $arg;
- $_min_t{ $ref};
+ $_min_t{ $ref };
}
-sub current_time {
+sub max_t {
my ($self, $arg) = @_;
my $ref = refaddr $self;
- $_current_time{ $ref} = $arg if defined $arg;
+ $_max_t{ $ref } = $arg if defined $arg;
- $_current_time{ $ref};
+ $_max_t{ $ref };
}
-sub paused {
- $_paused{ refaddr $_[0]};
+sub delay {
+ my ($self, $arg) = @_;
+ my $ref = refaddr $self;
+ $_delay{ $ref } = $arg if defined $arg;
+
+ $_delay{ $ref };
}
-sub _exit_on_quit {
- my ($self, $event) = @_;
+sub stop_handler {
+ my ($self, $arg) = @_;
+ my $ref = refaddr $self;
+ $_stop_handler{ $ref } = $arg if @_ > 1;
- $self->stop() if $event->type == SDL_QUIT;
+ $_stop_handler{ $ref };
}
-1;
+sub default_stop_handler {
+ my ($event, $self) = @_;
-__END__
+ $self->stop() if $event->type == SDL::Events::SDL_QUIT;
+}
+sub event {
+ my ($self, $arg) = @_;
+ my $ref = refaddr $self;
+ $_event{ $ref } = $arg if defined $arg;
+
+ $_event{ $ref };
+}
+
+# replacements for SDLx::App::get_ticks() and SDLx::App::delay()
+sub time {
+ my ($self, $arg) = @_;
+ my $ref = refaddr $self;
+ $_time{ $ref } = $arg if defined $arg;
+ $_time{ $ref };
+}
+sub sleep {
+ return Time::HiRes::sleep( $_[1] );
+}
+
+# deprecated
+sub ticks {
+ return SDL::get_ticks();
+}
+
+1;
View
55 lib/SDLx/Surface.pm
@@ -123,9 +123,7 @@ sub get_pixel {
sub set_pixel {
my ( $self, $y, $x, $new_value ) = @_;
-
$new_value = SDLx::Validate::num_rgba($new_value);
-
SDLx::Surface::set_pixel_xs( $self, $x, $y, $new_value );
}
@@ -190,6 +188,8 @@ sub load {
}
}
+
+
my $formated_surface = $surface;
if( SDL::Video::get_video_surface )
{
@@ -201,6 +201,33 @@ sub load {
#EXTENSTIONS
+sub blit {
+ my ( $src, $dest, $src_rect, $dest_rect ) = @_;
+
+ $src = SDLx::Validate::surface($src);
+ $dest = SDLx::Validate::surface($dest);
+
+ if(defined $src_rect) {
+ $src_rect = SDLx::Validate::rect($src_rect);
+ }
+ else {
+ $src_rect = SDL::Rect->new( 0, 0, $src->w, $src->h );
+ }
+ if(defined $dest_rect) {
+ $dest_rect = SDLx::Validate::rect($dest_rect);
+ }
+ else {
+ $dest_rect = SDL::Rect->new( 0, 0, $dest->w, $dest->h );
+ }
+
+ SDL::Video::blit_surface(
+ $src, $src_rect,
+ $dest, $dest_rect
+ );
+
+ return $src;
+}
+
sub blit_by {
my ( $dest, $src, $src_rect, $dest_rect ) = @_;
SDLx::Surface::blit( $src, $dest, $src_rect, $dest_rect );
@@ -218,13 +245,13 @@ sub update {
my ( $surface, $rects ) = @_;
if ( !defined($rects) || ( ref($rects) eq 'ARRAY' && !ref( $rects->[0] ) ) ) {
- my @rect;
- @rect = @{$rects} if $rects;
+ my @rect;
+ @rect = @{$rects} if $rects;
$rect[0] ||= 0;
$rect[1] ||= 0;
$rect[2] ||= $surface->w;
$rect[3] ||= $surface->h;
-
+
SDL::Video::update_rect( $surface, @rect );
} else {
SDL::Video::update_rects( $surface, map { SDLx::Validate::rect($_) } @{$rects} );
@@ -233,6 +260,20 @@ sub update {
return $surface;
}
+sub draw_rect {
+ my ( $self, $rect, $color ) = @_;
+ $color = SDLx::Validate::map_rgba( $color, $self->format );
+ if ( defined $rect ) {
+ $rect = SDLx::Validate::rect($rect);
+ } else {
+ $rect = SDL::Rect->new( 0, 0, $self->w, $self->h );
+ }
+
+ SDL::Video::fill_rect( $self, $rect, $color )
+ and Carp::confess "Error drawing rect: " . SDL::get_error();
+ return $self;
+}
+
sub draw_line {
my ( $self, $start, $end, $color, $antialias ) = @_;
@@ -273,7 +314,7 @@ sub draw_circle {
unless( $antialias )
{
- SDL::GFX::Primitives::circle_color( $self, @{$center}, $radius, $color );
+ SDL::GFX::Primitives::circle_color( $self, @{$center}, $radius, $color );
}
else
{
@@ -283,7 +324,7 @@ sub draw_circle {
}
sub draw_circle_filled {
- my ( $self, $center, $radius, $color) = @_;
+ my ( $self, $center, $radius, $color ) = @_;
unless ( SDL::Config->has('SDL_gfx_primitives') ) {
Carp::cluck("SDL_gfx_primitives support has not been compiled");
View
30 lib/pods/SDL/Deprecated.pod
@@ -11,6 +11,36 @@ Core
=head1 RELEASES
+=head2 2.xxx
+
+SDLx::App had a full rewrite and SDLx::Controller was updated.
+
+=over
+
+=item SDLx::App
+
+Shortcut aliases in the constructor have been changed and removed. The main ones people use are still there like
+C<w>, C<h> and C<d>. These lesser used ones no longer work: C<t>, C<it>, C<i>, C<s>, C<ab>, C<db>, C<bs>, C<st>, C<asyncblit>.
+
+C<warp> was renamed to C<warp_cursor>, but this method wasn't documented before anyway.
+
+All OpenGL parameters for the constructor now have a C<gl_> prefix. These were never documented,
+so they're not listed here.
+
+The OpenGL method C<attribute> is now called C<gl_attribute>.
+
+C<delay> has been removed and replaced with SDLx::Controller's C<sleep>. C<ticks> has been deprecated, but left in
+for backcompat.
+
+=item SDLx::Controller
+
+The C<eoq> parameter and method have been removed. The exit on quit (stop on quit) action is now enabled by default.
+The C<stop_handler> parameter and method are now used to change and disable the quit action of the app.
+
+C<max_t> will now slow the application down if it runs at less than 10 FPS, by default.
+
+=back
+
=head2 2.517
Major changes to C<SDLx::Controller>.
View
107 lib/pods/SDL/Events.pod
@@ -15,7 +15,7 @@ Most likely you just want to know how to get events for you app.
use SDL::Event;
use SDL::Events ':all';
- SDL::init(SDL_INIT_VIDEO); # Event can only be grabbed in the same thread as this
+ SDL::init(SDL_INIT_VIDEO); # Event can only be grabbed in the same thread as this
...
@@ -27,7 +27,7 @@ Most likely you just want to know how to get events for you app.
while( SDL::Events::poll_event($event) )
{
#check by event type
- on_active() if $event->type == SDL_ACTIVEEVENT;
+ on_active() if $event->type == SDL_ACTIVEEVENT;
...
}
}
@@ -198,6 +198,11 @@ Export tag ':keymod'
KMOD_MODE
KMOD_RESERVED
+Export tag ':repeat'
+
+ SDL_DEFAULT_REPEAT_DELAY
+ SDL_DEFAULT_REPEAT_INTERVAL
+
=head1 METHODS
=head2 pump_events
@@ -206,15 +211,15 @@ Pumps the event loop, gathering events from the input devices.
pump_events();
-pump_events gathers all the pending input information from devices and places it on the event queue. Without calls to pump_events no events would
-ever be placed on the queue.
-Often the need for calls to pump_events is hidden from the user since L</poll_event> and L</wait_event> implicitly call pump_events.
+pump_events gathers all the pending input information from devices and places it on the event queue. Without calls to pump_events no events would
+ever be placed on the queue.
+Often the need for calls to pump_events is hidden from the user since L</poll_event> and L</wait_event> implicitly call pump_events.
However, if you are not polling or waiting for events (e.g. you are filtering them), then you must call pump_events to force an event queue update.
-=head2 peep_events (event, num_events, action, mask)
+=head2 peep_events (event, num_events, action, mask)
-Checks the event queue for messages and optionally returns them.
+Checks the event queue for messages and optionally returns them.
my $num_peep_events = SDL::Events::peep_events($event, 127, SDL_PEEKEVENT, SDL_ALLEVENTS);
@@ -239,14 +244,14 @@ Examples of mask:
=item SDL_EVENTMASK (SDL_KEYUP)
=item (SDL_EVENTMASK (SDL_MOUSEBUTTONDOWN) | SDL_EVENTMASK (SDL_MOUSEBUTTONUP))
-
+
=item SDL_ALLEVENTS
-
+
=item SDL_KEYUPMASK
-
+
=item SDL_ALLEVENTS ^ SDL_QUITMASK
-=back
+=back
=head3 RETURN
@@ -254,26 +259,26 @@ Number of Events actually stored or -1 if there was an error
=head2 poll_event($event)
-Polls for currently pending events.
+Polls for currently pending events.
If $event is not NULL, the next event is removed from the queue and stored in the L<SDL::Event> structure pointed to by $event.
-As this function implicitly calls pump_events, you can only call this function in the thread that set the video mode with
-L<SDL::Video::set_video_mode|SDL::Video/"set_video_mode">.
+As this function implicitly calls pump_events, you can only call this function in the thread that set the video mode with
+L<SDL::Video::set_video_mode|SDL::Video/"set_video_mode">.
=head3 RETURN
-Returns 1 if there are any pending events, or 0 if there are none available.
+Returns 1 if there are any pending events, or 0 if there are none available.
=head2 push_event($event)
-Pushes an event onto the event queue
+Pushes an event onto the event queue
-The event queue can actually be used as a two way communication channel. Not only can events be read from the queue, but the user can also push
-their own events onto it. event is a pointer to the event structure you wish to push onto the queue.
+The event queue can actually be used as a two way communication channel. Not only can events be read from the queue, but the user can also push
+their own events onto it. event is a pointer to the event structure you wish to push onto the queue.
The event is copied into the queue, and the caller may dispose of the memory pointed to after push_event returns.
-Note: Pushing device input events onto the queue doesn't modify the state of the device within SDL.
+Note: Pushing device input events onto the queue doesn't modify the state of the device within SDL.
This function is thread safe, and can be called from other threads safely.
@@ -287,8 +292,8 @@ Waits indefinitely for the next available $event, returning 0 if there was an er
If $event is not NULL, the next event is removed from the queue and stored in $event.
-As this function implicitly calls SDL_PumpEvents, you can only call this function in the thread that
-L<SDL::Video::set_video_mode|SDL::Video/"set_video_mode">.
+As this function implicitly calls SDL_PumpEvents, you can only call this function in the thread that
+L<SDL::Video::set_video_mode|SDL::Video/"set_video_mode">.
=head3 RETURN
@@ -296,7 +301,7 @@ L<SDL::Video::set_video_mode|SDL::Video/"set_video_mode">.
=head2 set_event_filter
-Sets up a filter to process all events
+Sets up a filter to process all events
my $filter = sub { if($_[0]->type == SDL_ACTIVEEVENT){ return 0} else{ return 1; }};
@@ -308,15 +313,15 @@ set_event_filter takes a coderef that it checks all events again. The callback g
sub { my $event_to_test = shift; ...}
-to filter the event return a 0, to pass the filter return a 1.
+to filter the event return a 0, to pass the filter return a 1.
One B<Caveat> is if you are filtering SDL_QUIT the event will be filtered if it is non-interrupt call ( Window closes normally ). If it is a
interrupt SDL_QUIT it will be process on the next event poll.
-Events pushed onto to the queue with L<SDL::Events::push_events|SDL::Events/"push_events"> or L<SDL::Events::peep_events|SDL::Events/"peep_events">
+Events pushed onto to the queue with L<SDL::Events::push_events|SDL::Events/"push_events"> or L<SDL::Events::peep_events|SDL::Events/"peep_events">
do not get filtered.
-This callback may run in a different thread.
+This callback may run in a different thread.
=head2 get_key_state
@@ -328,7 +333,7 @@ Get a snapshot of the current keyboard state
Use L<SDL::Events::pump_events|SDL::Events/"pump_events"> to update the state array.
-This function gives you the current state after all events have been processed, so if a key or button has been pressed and released before you
+This function gives you the current state after all events have been processed, so if a key or button has been pressed and released before you
process events, then the pressed state will never show up in the get_key_state call.
This function doesn't take into account whether shift has been pressed or not.
@@ -345,19 +350,19 @@ Return value is an OR'd combination of KMOD_*
my $mod_state = SDL::Events::get_mod_state();
- # Check which ones are pressed with
+ # Check which ones are pressed with
# no mod pressed?
-
+
print 'no_mod' if ( $mod_state & KMOD_NONE );
- # CTRL or ALT
+ # CTRL or ALT
print 'ctrl alt' if ($mod_state & KMOD_CTRL || $mod_state & KMOD_ALT );
=head3 MOD VALUES
-=over
+=over
=item KMOD_NONE
@@ -383,7 +388,7 @@ Return value is an OR'd combination of KMOD_*
=item KMOD_MODE
-=item KMOD_CTRL
+=item KMOD_CTRL
same as KMOD_LCTRL|KMOD_RCTRL
@@ -414,7 +419,7 @@ Simply pass your desired modifier states into $modstate. This value can be a OR'
Any KMOD_* constant see L<SDL::Events::get_mod_state|SDL::Events/"get_mod_state"> for constants.
SDL::Events::set_mod_state( $modstate );
-=head2 event_state
+=head2 event_state
Allows you to set the state of processing certain events
@@ -454,7 +459,7 @@ Gets the name of the a SDL virtual keysym
Returns a string with the name of the key sym.
-=head2 enable_unicode
+=head2 enable_unicode
Enable/Disable UNICODE translation
@@ -472,7 +477,7 @@ Only key press events will be translated not release events.
Returns the previous translation mode as (1,0).
-=head2 enable_key_repeat
+=head2 enable_key_repeat
Sets keyboard repeat rate
@@ -485,40 +490,40 @@ Setting $delay to 0 disables key repeating completely. Good default values are S
Return 0 on success and -1 on fail.
-=head2 get_mouse_state
+=head2 get_mouse_state
Retrieves the current state of the mouse
my ($mask,$x,$y) = @{ SDL::Events::get_mouse_state( ) };
- print 'Button Left pressed' if ($mask & SDL_BUTTON_LMASK);
+ print 'Button Left pressed' if ($mask & SDL_BUTTON_LMASK);
- print 'Button Right pressed' if ($mask & SDL_BUTTON_RMASK);
+ print 'Button Right pressed' if ($mask & SDL_BUTTON_RMASK);
- print 'Button Middle pressed' if ($mask & SDL_BUTTON_MMASK);
+ print 'Button Middle pressed' if ($mask & SDL_BUTTON_MMASK);
print $x.','.$y;
-The current button state is returned as a button $bitmask, which can be tested using the the above constants
+The current button state is returned as a button $bitmask, which can be tested using the the above constants
-=head2 get_relative_mouse_state
+=head2 get_relative_mouse_state
Retrieves the current relative state of the mouse
my ($mask,$x,$y) = @{ SDL::Events::get_mouse_state( ) };
- print 'Button Left pressed' if ($mask & SDL_BUTTON_LMASK);
+ print 'Button Left pressed' if ($mask & SDL_BUTTON_LMASK);
- print 'Button Right pressed' if ($mask & SDL_BUTTON_RMASK);
+ print 'Button Right pressed' if ($mask & SDL_BUTTON_RMASK);
- print 'Button Middle pressed' if ($mask & SDL_BUTTON_MMASK);
+ print 'Button Middle pressed' if ($mask & SDL_BUTTON_MMASK);
print $x.','.$y; # this is relative to the last position of the mouse
-The current button state is returned as a button $bitmask, which can be tested using the the above constants
+The current button state is returned as a button $bitmask, which can be tested using the the above constants
-=head2 get_app_state
+=head2 get_app_state
Gets the state of the application
@@ -526,7 +531,7 @@ Gets the state of the application
The $app_state is a bitwise combination of:
-=over
+=over
=item SDL_APPMOUSEFOCUS
@@ -547,22 +552,22 @@ Application is visible
warn 'Application Visible' if $app_state & SDL_APPACTIVE
-=back
+=back
-=head2 joystick_event_state
+=head2 joystick_event_state
Enable/disable joystick event polling
my $status = SDL::Events::joystick_event_state( $state );
-This function is used to enable or disable joystick event processing. With joystick event processing disabled you will have to update joystick
+This function is used to enable or disable joystick event processing. With joystick event processing disabled you will have to update joystick
states with L<SDL::Joystick::update|SDL::Joystick/"update"> and read the joystick information manually. $state can be:
-=over
+=over
=item SDL_QUERY
-=item SDL_ENABLE
+=item SDL_ENABLE
=item SDL_IGNORE
View
117 lib/pods/SDL/Platform.pod
@@ -0,0 +1,117 @@
+
+=pod
+
+=head1 NAME
+
+SDL-Platform - Platform Specific Informations about SDL Perl
+
+=head1 CATEGORY
+
+Documentation
+
+
+=head1 DESCRIPTION
+
+This document describes OS specific informations regading the installation and use of L<SDL>.
+
+=head2 General (all OS)
+
+You need to install L<Alien::SDL> to acquire all prerequisites of L<SDL>.
+
+On Windows, L<Alien::SDL> will get you zip-files containing prebuilt libs.
+
+On Unixes you can choose between compiling libs or use libs provided by the dist.
+The different options on Unixes are availale when requirements are met. Like having specific libs installed.
+
+There is some additional documentation in the L<Github-Wiki|https://github.com/PerlGameDev/SDL/wiki/Testing>.
+You definitively want to look there, if you want to know how to install L<SDL> from the latest sources (e.g. in an unfinished, unreleased state).
+
+
+
+=head1 Windows
+
+=head2 Installation
+
+We recommend Strawberry Perl. You can get it L<here|http://strawberryperl.com/>.
+
+Once you installed Strawberry Perl, you can access the cpan shell via the start menu.
+
+Open up the cpan shell and type C<install Alien::SDL SDL>. Please follow the dialog and answer the questions to the best of your knowledge.
+
+
+
+=head1 Mac OS X
+
+=head2 Installation
+
+You will need a newer version of Perl that you can install with L<App::perlbrew>.
+
+Once you have a newer Perl installed please use C<cpan> to install L<Alien::SDL> (and of course L<SDL>).
+
+=head2 Using SDL on Mac OS X
+
+You can't use the C<perl> executable to run SDL scripts on Mac OS X, you need to use C<SDLPerl>.
+When you install L<SDL>, a program named C<SDLPerl> is installed. It should be in your path.
+
+Using Mac OS X, your SDL Perl script have to look like this:
+
+ #!SDLPerl
+
+ use strict;
+ use warnings;
+ use SDL;
+ # your code here ...
+
+Using the wrong Perl executable, you might encounter a lot of error messages resulting in C<terminate called after throwing an instance of 'NSException'>.
+cf. the corresponding L<github issue|https://github.com/PerlGameDev/SDL/issues/208>.
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<Alien::SDL>
+
+=item * L<SDL>
+
+=back
+
+=head1 AUTHORS
+
+See list of module authors in L<SDL>.
+
+If you would like to contribute to SDL Perl, please post a message on the mailing list:
+
+sdl-devel@perl.org
+
+And request access to the github repository. Or drop us a line on #sdl over at irc.perl.org
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright 2002-2010 SDL Authors as listed above, all rights reserved.
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
View
6 lib/pods/SDL/Surface.pod
@@ -83,6 +83,12 @@ SDL::Surface width is defined at construction so this is read-only.
Returns the height of the surface.
SDL::Surface height is defined at construction so this is read-only.
+=head2 flags
+
+ my $flags = $surface->flags;
+
+Returns the flags of the surface (bitwise OR-ed L<SDL::Video constants|SDL::Video/CONSTANTS>).
+
=head2 format
my $format = $surface->format;
View
472 lib/pods/SDLx/App.pod
@@ -3,7 +3,7 @@
=head1 NAME
-SDLx::App - a SDL perl extension
+SDLx::App - The root window of an SDL application
=head1 CATEGORY
@@ -11,169 +11,431 @@ Extension
=head1 SYNOPSIS
- use SDL;
- use SDLx::App;
- use SDL::Event;
- use SDL::Events;
-
- my $app = SDLx::App->new(
- title => 'Application Title',
- width => 640,
- height => 480,
- depth => 32
- );
-
-This is the manual way of doing things
-
- my $event = SDL::Event->new; # create a new event
-
- SDL::Events::pump_events();
-
- while ( SDL::Events::poll_event($event) ) {
- my $type = $event->type(); # get event type
- print $type;
- exit if $type == SDL_QUIT;
- }
-
-An alternative to the manual Event processing is through the L<SDLx::Controller> module. L<SDLx::App> is a Controller so see the CALLBACKS section below.
+ use SDL;
+ use SDLx::App;
+ use SDL::Event;
+ use SDL::Events;
+
+ # this is all the code we need to have a working app!
+ my $app = SDLx::App->new;
+
+ # we can also specify many useful things in the constructor
+ my $app = SDLx::App->new(
+ title => 'My Great Game',
+ width => 1024,
+ height => 600,
+ dt => 0.05,
+ centered => 1,
+ no_frame => 1,
+ async_blit => 1,
+ );
+
+ # our app also comes with an SDLx::Controller
+
+ # add a handler handle events such as keypresses
+ $app->add_event_handler(
+ sub {
+ my ($event) = @_;
+ # handle the event here
+ }
+ );
+
+ # add a handler to move our objects in response to time
+ $app->add_move_handler(
+ sub {
+ # handle moving objects here
+ }
+ );
+
+ # add a handler to show our objects on the screen
+ $app->add_show_handler(
+ sub {
+ # handle drawing objects here
+ }
+ );
+
+ # finally, start the app run loop with the added handlers
+ $app->run();
+
+For a full explanation of the L<run|SDLx::Controller/run> loop and other Controller related methods,
+please see L<SDLx::Controller>.
=head1 DESCRIPTION
-L<SDLx::App> controls the root window of the of your SDL based application.
-It extends the L<SDL::Surface> class, and provides an interface to the window
-manager oriented functions.
+The C<SDLx::App> provides methods for the root window of your SDL application,
+as well as many related convenience methods.
+It is a subclass of both L<SDLx::Surface> and L<SDLx::Controller>,
+providing all the methods they both provide.
=head1 METHODS
=head2 new
-C<SDLx::App::new> initializes the SDL, creates a new screen,
-and initializes some of the window manager properties.
-C<SDLx::App::new> takes a series of named parameters:
+ my $app = SDLx::App->new(
+ width => 640,
+ height => 480,
+ depth => 32,
-=over 4
+ title => "Application Title",
+ icon_title => "App Title",
+ icon => "icon.bmp",
+ icon_color_key => [255, 255, 0],
-=item * title
-the window title. Defaults to the file name. Shorter alias: 't'
+ # two ways to do init. init video is assumed regardless
+ init => SDL_INIT_AUDIO | SDL_INIT_JOYSTICK,
+ init => [ 'audio', 'joystick' ],
-=item * icon_title
-the icon title. Defaults to file name. Shortcut: 'it'
+ # normal libsdl access to flags
+ flags => SDL_SWSURFACE | SDL_ANYFORMAT,
+ # or parameter access to flags
+ sw_surface => 1,
+ hw_surface => 1,
+ async_blit => 1,
+ any_format => 1,
+ hw_palette => 1,
+ double_buf => 1,
+ fullscreen => 1,
+ open_gl => 1,
+ resizable => 1,
+ no_frame => 1,
-=item * icon
-the icon itself. Defaults to none. Shortcut: 'i'
+ # window position
+ centered => 1,
+ position => [ 150, 100 ],
-=item * width
-Window width, in pixels. Defaults to 800. Shortcut: 'w'
+ hide_cursor => 1,
+ grab_input => 1,
-=item * height
-Window height, in pixels. Defaults to 600. Shortcut: 'h'
+ # don't initialize an SDLx::Controller for the app
+ no_controller => 1,
-=item * depth
-Screen depth. Defaults to 16. Shortcut: 'd'.
+ # store your goodies in the app if you like
+ stash => {},
-=item * flags
-Any flags you want to pass to L<SDL::Video> upon initialization. Defaults to SDL_ANYFORMAT. Flags should be I<or'ed> together if you're passing more than one (flags => FOO|BAR). Shortcut: 'f'.
+ # and everything from SDLx::Controller
+ dt => 0.1,
+ min_t => 1 / 60,
+ delay => 0,
+ stop_handler => \&SDLx::Controller::default_stop_handler,
+ event => SDL::Event->new(),
+ event_handlers => [],
+ move_handlers => [],
+ show_handlers => [],
+ time => 0,
+ );
-=item * resizeable
-Set this to a true value to make the window resizeable by the user. Default is off.
+Initializes SDL with L<SDLx::App::init|/init>,
+creates the root window with L<SDL::Video::set_video_mode|SDL::Video/set_video_mode>,
+initializes an L<SDLx::Controller> belonging to the app,
+and performs many other app management tasks all as specified by named parameters.
+Returns an L<SDLx::Surface> of the new app.
-=item * exit_on_quit
-Set this to a true value to make the app exit if a SDL_QUIT event is triggered. Shortcut: 'eoq'.
+The complete set of parameters are shown in the code above and explained in detail below.
+When constructing the app's L<SDLx::Controller> all parameters specified are also given to
+L<< SDLx::Controller->new|SDLx::Controller/new >>, so should be specified here. See
+L<SDLx::Controller>.
+
+=over
+
+=item width
+
+The window width, in pixels. Defaults to 640. Alias: C<w>.
+
+=item height
+
+The window height, in pixels. Defaults to 480. Alias: C<h>.
+
+=item depth
+
+The surface's color depth, in bits per pixel. Should be 8, 16, 24 or 32.
+If not defined, defaults to 32 and specifies the
+L<SDL_ANYFORMAT|SDL::Video/CONSTANTS> flag. Alias: C<d>.
+
+=item title
+
+The window's title, as a string. Defaults to the L</icon_title> if defined, or the file name.
+
+=item icon_title
+
+The application's icon title, as a string. Defaults to the L</title> if defined, or the file name.
+This will only have an effect under certain operating systems.
+
+=item icon
+
+The application's icon. Set with the L</icon> method.
+
+=item icon_color_key
+
+You can optionally use this when specifying the C<icon> parameter to set its color key (transparent pixel).
+
+=item init
+
+The SDL subsystems to initialize, as a product of the L<SDL init constants|SDL/CONSTANTS> or an array ref.
+The video subsystem is initialized no matter what, and does not need to be specified.
+The specified value is passed to L<SDLx::App::init|/init>, so see that for more details.
+Defaults to L<SDL_INIT_EVERYTHING|SDL/CONSTANTS>. Alias: C<initialize>.
+
+=item flags
+
+L<SDL::Video flags|SDL::Video/CONSTANTS> to specify when calling
+L<SDL::Video::set_video_mode|SDL::Video/set_video_mode>.
+Flags should be bitwise I<or'ed> together when passing more than one (C<flags => FOO|BAR>).
+All flags have a corresponding named parameter which can be used instead of specifying them here,
+and are explained below.
+Defaults to no flags, or to L<SDL_ANYFORMAT|SDL::Video/CONSTANTS> if the C<depth> parameter was undefined.
+Alias: C<f>.
+
+=item sw_surface
+
+The L<SDL_SWSURFACE|SDL::Video/CONSTANTS> flag, as a boolean. If true, creates the surface in
+system memory. This is best used when you plan to do per-pixel manipulations,
+or blit surfaces with alpha channels. Aliases: C<software_surface>, C<sw>.
+
+=item hw_surface
+
+The L<SDL_HWSURFACE|SDL::Video/CONSTANTS> flag, as a boolean. If true, creates the surface in
+video memory. This is best used when the surfaces you'll be blitting are also hardware surfaces.
+SDL copies the surfaces from video memory to system memory when you L<lock|SDL::Video/lock_surface> them,
+and back when you L<unlock|SDL::Video/unlock_surface> them, which can cause a major performance hit.
+If the video driver does not support hardware surfaces, a software surface will be returned instead.
+Many platforms can only provide hardware surfaces when using L<SDL_FULLSCREEN|SDL::Video/CONSTANTS>.
+Aliases: C<hardware_surface>, C<hw>.
+
+=item async_blit
+
+The L<SDL_ASYNCBLIT|SDL::Video/CONSTANTS> flag, as a boolean. If true, enables the use of asynchronous updates
+of the display surface. This will usually slow down blitting on single-CPU machines, but can speed up blitting
+on multi-CPU machines. Alias: C<asynchronous_blit>.
+
+=item any_format
+
+The L<SDL_ANYFORMAT|SDL::Video/CONSTANTS> flag, as a boolean. If a video surface of the requested
+bits-per-pixel (bpp) is not available SDL will normally emulate one with a shadow surface.
+Passing a true value prevents this and causes SDL to use the video surface, regardless of its pixel depth.
+This flag is specified automatically when the L</depth> parameter is undefined.
+
+=item hw_palette
+
+The L<SDL_HWPALETTE|SDL::Video/CONSTANTS> flag, as a boolean. If true, gives SDL exclusive palette access.
+Without this flag you may not always get the exact colors you request with
+L<SDL::Video::set_colors|SDL::Video/set_colors> or L<SDL::Video::set_palette|SDL::Video/set_palette>.
+Alias: C<hardware_palette>.
+
+=item double_buf
+
+The L<SDL_DOUBLEBUF|SDL::Video/CONSTANTS> flag, as a boolean. If true, enables hardware double buffering;
+faster, but only valid with a hardware surface. L<flip|SDLx::Surface/flip> should be used to flip the buffers
+and update the screen. All drawing will take place on the surface that is not being displayed.
+If double buffering could not be enabled then L<flip|SDLx::Surface/flip> will just perform an
+L<update_rect|SDLx::Surface/update_rect> on the entire screen. Aliases: C<double_buffer>, C<dbl_buf>.
+
+=item fullscreen
+
+The L<SDL_FULLSCREEN|SDL::Video/CONSTANTS> flag, as a boolean. If true, SDL will attempt to use a fullscreen mode,
+changing the hardware resolution to the resolution of the display surface.
+If, for whatever reason, this change is not possible the next higher resolution will be used
+and the display surface centered on a black background. Aliases: C<full_screen>, C<full>.
+
+=item open_gl
+
+The L<SDL_OPENGL|SDL::Video/CONSTANTS> flag, as a boolean. If true, creates an OpenGL rendering context.
+This uses any C<gl> attributes specified and any others set with L</gl_attribute>. Aliases: C<opengl>, C<gl>.
+
+=item resizable
+
+The L<SDL_RESIZABLE|SDL::Video/CONSTANTS> flag, as a boolean. If true, creates a resizable window.
+When the window is resized by the user a L<SDL_VIDEORESIZE|SDL::Events/CONSTANTS> event
+is generated and L</resize> should be called with the new size.
+
+=item no_frame
+
+The L<SDL_NOFRAME|SDL::Video/CONSTANTS> flag, as a boolean. If true, SDL attempts to create a window
+with no title bar or frame decoration. Fullscreen modes automatically have this flag set.
+
+=item centered
+
+A boolean value. If true, creates the window centered on the screen.
+SDL does this with the C<SDL_VIDEO_CENTERED> environment variable which is set using C<$ENV{SDL_VIDEO_CENTERED}>.
+Alias: C<center>.
+
+=item position
+
+The position of the window on the screen, as an array ref. The array ref should be two elements long,
+specifying C<x> and C<y> values, in pixels, used to position the window on the screen.
+Implemented using C<$ENV{SDL_VIDEO_WINDOW_POS}>. Alias: C<pos>.
+
+=item hide_cursor
+
+A boolean value. If true, hides the cursor on the video surface using L</show_cursor>.
+A surface can then instead be blitted to the display at the location of the cursor.
+Alias: C<no_cursor>.
+
+=item grab_input
+
+A boolean value. If true, SDL attempts to confine the cursor to the window using L</grab_input>.
+Also, nearly all keyboard input will be passed directly to the application,
+and not interpreted by any window manager present.
+
+=item no_controller
+
+A boolean value. If true, does not initialize an L<SDLx::Controller> for the app.
+Care should then be taken to not use any of L<SDLx::Controller>s methods.
+
+=item stash
+
+A place to store any information you need the app to hold. This can then be returned and set with
+L</stash>. If not specified, defaults to an empty hash ref.
=back
-=head1 METHODS
+=head2 init
-=head2 title()
+ SDLx::App->init( SDL::SDL_INIT_TIMER | SDL::SDL_INIT_AUDIO | ... );
+ SDLx::App->init( [ 'timer', 'audio', 'video', 'cd_rom', 'cdrom', 'joystick',