From 17421b0cf76ce7969901dc2b5ce0b916bc112b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 21 Jun 2021 23:00:02 +0100 Subject: [PATCH 01/36] Initial commit --- .gitignore | 3 + Changes | 5 + README.md | 20 + cpanfile | 6 + dist.ini | 33 + examples/.white_noise.pl.swp | Bin 0 -> 12288 bytes examples/events.pl | 226 +++++ examples/hello.pl | 47 + examples/icon.pl | 82 ++ examples/particles.pl | 154 ++++ examples/sprites.pl | 114 +++ examples/syswm.pl | 40 + examples/windowtitle.pl | 64 ++ lib/SDL2/Raw.pm | 1680 ++++++++++++++++++++++++++++++++++ lib/SDL2/Raw/Image.pm | 43 + lib/SDL2/Raw/Mixer.pm | 74 ++ share/hello.bmp | Bin 0 -> 1089334 bytes share/sheet.png | Bin 0 -> 15299 bytes t/wminfo.t | 42 + 19 files changed, 2633 insertions(+) create mode 100644 .gitignore create mode 100644 Changes create mode 100644 README.md create mode 100644 cpanfile create mode 100644 dist.ini create mode 100644 examples/.white_noise.pl.swp create mode 100644 examples/events.pl create mode 100644 examples/hello.pl create mode 100644 examples/icon.pl create mode 100644 examples/particles.pl create mode 100644 examples/sprites.pl create mode 100644 examples/syswm.pl create mode 100644 examples/windowtitle.pl create mode 100644 lib/SDL2/Raw.pm create mode 100644 lib/SDL2/Raw/Image.pm create mode 100644 lib/SDL2/Raw/Mixer.pm create mode 100644 share/hello.bmp create mode 100644 share/sheet.png create mode 100644 t/wminfo.t diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9249fe5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.swp +.build/ +SDL2-Raw* diff --git a/Changes b/Changes new file mode 100644 index 0000000..3098f56 --- /dev/null +++ b/Changes @@ -0,0 +1,5 @@ +Revision history for SDL2::Raw + +{{$NEXT}} + + First version. diff --git a/README.md b/README.md new file mode 100644 index 0000000..97343c7 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# SDL2::Raw + +This is a work-in-progress set of bindings for SDL2 using [FFI::Platypus]. + +The immdiate goal is to implement bindings for a sufficiently complete +subset of SDL2 that is as close as possible to the original to serve as +the building blocks for any syntactic sugar that we might want to build +on top. + +This is still early in development. The names of the modules and the +distribution itself are placeholders for now. + +Until more documentation is written, the examples directory holds samples +of how this library can be used. + +Some of the tests make use of a spritesheet made available in the `share` +directory. That spritesheet is the [Dungeon Tileset II] by 0x72. + +[Dungeon Tileset II]: https://0x72.itch.io/dungeontileset-ii +[FFI::Platypus]: https://metacpan.org/pod/FFI::Platypus diff --git a/cpanfile b/cpanfile new file mode 100644 index 0000000..98133f6 --- /dev/null +++ b/cpanfile @@ -0,0 +1,6 @@ +requires 'enum::hash'; +requires 'FFI::Platypus'; +requires 'FFI::C'; +requires 'Ref::Util'; + +recommends 'File::Share'; diff --git a/dist.ini b/dist.ini new file mode 100644 index 0000000..2b3d321 --- /dev/null +++ b/dist.ini @@ -0,0 +1,33 @@ +name = SDL2-Raw +author = José Joaquín Atria +license = Perl_5 +copyright_holder = José Joaquín Atria +copyright_year = 2021 + +version = 0.001 + +[NextRelease] +[ReadmeAnyFromPod / MarkdownInBuild] +filename = README.md + +[@Starter::Git] +-remove = Pod2Readme +-remove = Git::Push +regenerate = Build.PL +regenerate = META.json +regenerate = LICENSE +regenerate = README.md +revision = 3 +Git::GatherDir.exclude_filename[0] = dist.ini +Git::GatherDir.exclude_filename[1] = cpanfile +Git::GatherDir.exclude_filename[2] = TODO +Git::Commit.commit_msg = Release v%V%t +Git::Tag.tag_message = + +[MinimumPerl] + +[Repository] +[Bugtracker] +web = https://github.com/jjatria/perl-sdl2-raw/issues + +[Git::Contributors] diff --git a/examples/.white_noise.pl.swp b/examples/.white_noise.pl.swp new file mode 100644 index 0000000000000000000000000000000000000000..dbc7037f6272517ae03213b66f5c822023509f94 GIT binary patch literal 12288 zcmeI2O>87b701hbLs){~lI8U588qW%zWl+;wB_A+J+pR8ySABW$BVqyY&kSNN96N++4ZsD2`kbnb0aN%EF)w2_?<1EKT z-O_KnyXsZFk9zg0t(}9)Q|47#DpeS+&ocJod#~R3(VGQ!^DJXGZ`vYsZP|0`H454v zEQA@pp(iZ2`BAyYL$BPZUMXI_Y?k@Z?)E$$l?NSH@TMQQ5ij+;Q?;g@X#~;;Jah#5 z!YMAD*;$yMi?@1aW|}_#xt)g|o(iTBNF$I&AdNs8fiwbX1kwnk5lAEO=tn^Grr0;( z#)q^k-_rM~V|V?U-qHx95lADDMj(wq8i6zdX#~;;q!CCXkVYVlKpKHZFama)vG3uJ z{g*!s0Bfd7QC#z@NZt;3e==@ErIdcpA9C0UYdti=Y7B{tRPpf!~53fbRnj zSl~Q(0-OO;;O#Sv{R;dN+y!?)ADjpO`7~qy2Cst`fDgLhYhVj3ff;ZXOo6{XhWWr- z;1A$;;2szN58MFz;5xVjD&Py?U!P*^UGOS+4*Uc>3%&!s4GzE!&;%yf088L8@a88O z`!#q5>;elkz*oQ&`1>aq`#tytcmdo69=HW;a04_z2^7IUK8`WqH{iSAE)ZZHTn0r@ z0Dt=!V{d@h!Asyp5Q0Up0N(j1V}Ay(fM-Ditb=KA7CZso{Rm_K1b+lS2R{SPgL~i^ zun*3I$H6Pek2psj!5sg8G5HQ7T-_06hqOXb$92T=No^F_BMP~b&kkp)L^*?UxK;aI z{N^Tqo4R(7Zd0~ND;F?R@dBRi(3ALQb~#qy1ufg7Y}FRFVW?4t(Fj6ui;p5Go;yX% z^xNDQuyTber-)fRjJR}WJX;^{lNy&<)IXp$KjxU!XwQ_=iOhoS1FLhrhQhJ{0|s=ie)lA*DAXO{HwBA4Wk4o0F$6 zQMTubR!4cNq$Wqiqk8VJjZp4Qj%o$oKIXX;1YRi&JdZA3q{`57XnGoV9XU0h1C4UY zF@*9|p;C!<&8oFQMVg~o#6~K~P$CuvgG6kjX0C5oV>vAdi(`AB-nWH MYG@@uPM zXpi<>Uo7nxXgk0I4@*8D?b<>u zV>TNhU+s8Z;?78~@4yayo%M2^Fbq9Xy9}p#M-X~;>sFp}t#i4;{he`9ZVv;|#>UW+ ziQ{_Y9xcq1Tth6|3Hq?w$|@wCa;&VEZC}~&V7@1&B??!l?)gp@m0#@{yj5pRpIE1Nx;*bCnA17nl^tYT=3gbF?6P)K$i9@f6`iF1|}) zWas%zNR_sOA<|#OBO3`j z6nY1#tjQ;trp8KRJ=7?=x@vBc>rj>-q3{!Wpm%JJ zK{NPvI&O*dL$%dwo7GyqR&UA$yi%*LTD2-YeZ13Jt*_TCz0_*)igS7qRRL5>n0Rt* z6%*?b!BdS^Q+<%hyXIzfYq#0h*xKDxHk_KEN_=~((Oj!-TJW{11}XpFuQ#`iZSA(O z5gVgO@a}X@d8kZ0=HIS53sin!hpVYg-xbL&HaAVH`IK3$Z4vS~UbOq^<3iB2J^W@| zjXYt=@JA(j@l$id*!FBxj(%hqm*QhnW+tg~Q=XvekS)8&2?mJeLN L%>5&IYI*Fx!PWBi literal 0 HcmV?d00001 diff --git a/examples/events.pl b/examples/events.pl new file mode 100644 index 0000000..817a04c --- /dev/null +++ b/examples/events.pl @@ -0,0 +1,226 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use feature qw( say state ); + +use SDL2::Raw; +use SDL2::Raw::Image; + +say 'This demo should capture all recognised SDL2 events'; + +die 'Could not initialise SDL2: ' . SDL2::GetError + if SDL2::Init( SDL2::INIT_VIDEO | SDL2::INIT_GAMECONTROLLER ); + +my $window = SDL2::CreateWindow( + 'Event handling demo', + SDL2::WINDOWPOS_CENTERED, + SDL2::WINDOWPOS_CENTERED, + 320, + 240, + SDL2::WINDOW_OPENGL | SDL2::WINDOW_RESIZABLE, +) or die 'Error creating SDL window: ' . SDL2::GetError; + +my ( $done, $controller ); +while ( !$done ) { + handle_input(); + SDL2::Delay(10); +} + +SDL2::DestroyWindow($window); + +# Helpers + +sub debug { + use Data::Dumper; + local $Data::Dumper::Terse = 1; + local $Data::Dumper::Indent = 0; + local $Data::Dumper::Sortkeys = 1; + my $e = shift; say Dumper({ map { $_ => $e->$_ } @_ }); +} + +sub handle_input { + state $event = SDL2::Event->new; + + while ( SDL2::PollEvent($event) ) { + my $type = $event->type; + + if ( $type == SDL2::QUIT ) { + $done = 1; + } + + elsif ( $type == SDL2::KEYDOWN || $type == SDL2::KEYUP ) { + say 'KEY ' . ( $type == SDL2::KEYDOWN ? 'DOWN' : 'UP' ); + my $e = $event->key; + + $done = 1 if $e->sym == SDL2::K_ESCAPE; + + debug $e => qw( type timestamp windowID state repeat scancode sym mod ); + } + + elsif ( $type == SDL2::WINDOWEVENT ) { + my $e = $event->window; + my $select = $e->event; + + if ( $select == SDL2::WINDOWEVENT_SHOWN ) { + say 'WINDOW SHOWN'; + debug $e => qw( windowID ); + } + + elsif ( $select == SDL2::WINDOWEVENT_HIDDEN ) { + say 'WINDOW HIDDEN'; + debug $e => qw( windowID ); + } + + elsif ( $select == SDL2::WINDOWEVENT_EXPOSED ) { + say 'WINDOW EXPOSED'; + debug $e => qw( windowID ); + } + + elsif ( $select == SDL2::WINDOWEVENT_MOVED ) { + say 'WINDOW MOVED'; + debug $e => qw( windowID data1 data2 ); + } + + elsif ( $select == SDL2::WINDOWEVENT_RESIZED ) { + say 'WINDOW RESIZED'; + debug $e => qw( windowID data1 data2 ); + } + + elsif ( $select == SDL2::WINDOWEVENT_SIZE_CHANGED ) { + say 'WINDOW SIZE CHANGED'; + debug $e => qw( windowID data1 data2 ); + } + + elsif ( $select == SDL2::WINDOWEVENT_MINIMIZED ) { + say 'WINDOW MINIMIZED'; + debug $e => qw( windowID ); + } + + elsif ( $select == SDL2::WINDOWEVENT_MAXIMIZED ) { + say 'WINDOW MAXIMIZED'; + debug $e => qw( windowID ); + } + + elsif ( $select == SDL2::WINDOWEVENT_RESTORED ) { + say 'WINDOW RESTORED'; + debug $e => qw( windowID ); + } + + elsif ( $select == SDL2::WINDOWEVENT_ENTER ) { + say 'WINDOW ENTER'; + debug $e => qw( windowID ); + } + + elsif ( $select == SDL2::WINDOWEVENT_LEAVE ) { + say 'WINDOW LEAVE'; + debug $e => qw( windowID ); + } + + elsif ( $select == SDL2::WINDOWEVENT_FOCUS_GAINED ) { + say 'WINDOW FOCUS GAINED'; + debug $e => qw( windowID ); + } + + elsif ( $select == SDL2::WINDOWEVENT_FOCUS_LOST ) { + say 'WINDOW FOCUS LOST'; + debug $e => qw( windowID ); + } + + elsif ( $select == SDL2::WINDOWEVENT_CLOSE ) { + say 'WINDOW CLOSE'; + debug $e => qw( windowID ); + } + + else { + say 'WINDOW EVENT UNHANDLED'; + debug $e => qw( type windowID ); + } + } + + elsif ( $type == SDL2::MOUSEMOTION ) { + say 'MOUSE MOTION'; + debug $event->motion => qw( type timestamp which state x y xrel yrel ); + } + + elsif ( $type == SDL2::MOUSEWHEEL ) { + say 'MOUSE WHEEL'; + debug $event->wheel => qw( type timestamp windowID which x y direction ); + } + + elsif ( $type == SDL2::DROPFILE || $type == SDL2::DROPTEXT ) { + say sprintf '%s DROPPED', $type == SDL2::DROPFILE ? 'FILE' : 'TEXT'; + debug $event->drop => qw( type timestamp file ); + } + + elsif ( $type == SDL2::MOUSEBUTTONDOWN || $type == SDL2::MOUSEBUTTONUP ) { + say 'MOUSE ' . ( $type == SDL2::MOUSEBUTTONDOWN ? 'DOWN' : 'UP' ); + debug $event->button => qw( type timestamp windowID which button state clicks padding1 x y ); + } + + elsif ( $type == SDL2::TEXTEDITING ) { + say 'TEXT EDITED'; + debug $event->edit => qw( type timestamp windowID text start length ); + } + + elsif ( $type == SDL2::TEXTINPUT ) { + say 'TEXT INPUT'; + debug $event->text => qw( type timestamp windowID text ); + } + + elsif ( $type == SDL2::CONTROLLERDEVICEADDED ) { + say 'CONTROLLER ADDED'; + my $e = $event->cdevice; + my $index = $e->which; + if ( SDL2::IsGameController($index) ) { + if ( my $pad = SDL2::GameControllerOpen($index) ) { + my $id = SDL2::JoystickInstanceID( + SDL2::GameControllerGetJoystick($pad) ); + + say sprintf 'Opened controller %i (%s)', $id, + SDL2::GameControllerNameForIndex($index); + + $controller = $pad; + } + else { + say 'Could not open game controller: ' . SDL2::GetError; + } + } + } + + elsif ( $type == SDL2::CONTROLLERDEVICEREMOVED ) { + say 'CONTROLLER REMOVED'; + my $e = $event->cdevice; + my $index = $e->which; + say "Controller $index has been removed"; + + if ( $controller ) { + SDL2::GameControllerClose($controller); + undef $controller; + } + else { + say 'Not a known controller!'; + } + } + + elsif ( $type == SDL2::CONTROLLERAXISMOTION ) { + say 'CONTROLLER AXIS MOVED'; + debug $event->caxis => qw( axis value ); + } + + elsif ( $type == SDL2::CONTROLLERDEVICEREMAPPED ) { + say 'CONTROLLER DEVICE REMAPPED'; + } + + elsif ( $type == SDL2::CONTROLLERBUTTONDOWN || $type == SDL2::CONTROLLERBUTTONUP ) { + say 'CONTROLLER BUTTON ' . ( $type == SDL2::CONTROLLERBUTTONDOWN ? 'PRESSED' : 'RELEASED' ); + debug $event->cbutton => qw( button state ); + } + + else { + say 'UNHANDLED'; + debug $event => qw( type timestamp ); + } + } + +} diff --git a/examples/hello.pl b/examples/hello.pl new file mode 100644 index 0000000..487837d --- /dev/null +++ b/examples/hello.pl @@ -0,0 +1,47 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use SDL2::Raw; +use File::Share 'dist_file'; + +# Simple example for using SDL2 directly + +SDL2::Init( SDL2::INIT_VIDEO ) + and die 'Could not initialise SDL2: ' . SDL2::GetError; + +my $window = SDL2::CreateWindow( + 'Hello World', + SDL2::WINDOWPOS_CENTERED, + SDL2::WINDOWPOS_CENTERED, + 592, + 460, + SDL2::WINDOW_SHOWN, +) or die "Error creating SDL window: " . SDL2::GetError; + +my $image = SDL2::LoadBMP( dist_file 'SDL2-Raw', 'hello.bmp' ) + or die 'Error loading image: ' . SDL2::GetError; + +my $surface = SDL2::GetWindowSurface($window) + or die 'Error getting window surface: ' . SDL2::GetError; + +SDL2::BlitSurface( $image, undef, $surface, undef ) + and die 'Could not blit surface: ' . SDL2::GetError; + +SDL2::UpdateWindowSurface($window) + and die 'Could not update window surface: ' . SDL2::GetError; + +SDL2::FreeSurface($image); + +my $event = SDL2::Event->new; +MAIN: while (1) { + while ( SDL2::PollEvent($event) ) { + last MAIN if $event->type == SDL2::QUIT; + } + + SDL2::Delay(10); +} + +SDL2::DestroyWindow($window); +SDL2::Quit; diff --git a/examples/icon.pl b/examples/icon.pl new file mode 100644 index 0000000..e1ad1ab --- /dev/null +++ b/examples/icon.pl @@ -0,0 +1,82 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use feature qw( say state ); + +use SDL2::Raw; +use FFI::Platypus::Buffer 'scalar_to_buffer'; + +say 'This demo should sets the icon in the SDL2 window'; + +die 'Could not initialise SDL2: ' . SDL2::GetError + if SDL2::Init(SDL2::INIT_VIDEO); + +# Open a new window +my $window = SDL2::CreateWindow( + 'SDL2 window icon demo', + SDL2::WINDOWPOS_UNDEFINED, + SDL2::WINDOWPOS_UNDEFINED, + 320, + 240, + SDL2::WINDOW_SHOWN | SDL2::WINDOW_RESIZABLE, +) or die 'Error creating SDL window: ' . SDL2::GetError; + +# Allocate a set of raw pixel data for the icon surface +my ($pixels) = scalar_to_buffer( pack 'S*', + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0aab, 0x0789, 0x0bcc, 0x0eee, 0x09aa, 0x099a, 0x0ddd, + 0x0fff, 0x0eee, 0x0899, 0x0fff, 0x0fff, 0x1fff, 0x0dde, 0x0dee, + 0x0fff, 0xabbc, 0xf779, 0x8cdd, 0x3fff, 0x9bbc, 0xaaab, 0x6fff, + 0x0fff, 0x3fff, 0xbaab, 0x0fff, 0x0fff, 0x6689, 0x6fff, 0x0dee, + 0xe678, 0xf134, 0x8abb, 0xf235, 0xf678, 0xf013, 0xf568, 0xf001, + 0xd889, 0x7abc, 0xf001, 0x0fff, 0x0fff, 0x0bcc, 0x9124, 0x5fff, + 0xf124, 0xf356, 0x3eee, 0x0fff, 0x7bbc, 0xf124, 0x0789, 0x2fff, + 0xf002, 0xd789, 0xf024, 0x0fff, 0x0fff, 0x0002, 0x0134, 0xd79a, + 0x1fff, 0xf023, 0xf000, 0xf124, 0xc99a, 0xf024, 0x0567, 0x0fff, + 0xf002, 0xe678, 0xf013, 0x0fff, 0x0ddd, 0x0fff, 0x0fff, 0xb689, + 0x8abb, 0x0fff, 0x0fff, 0xf001, 0xf235, 0xf013, 0x0fff, 0xd789, + 0xf002, 0x9899, 0xf001, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0xe789, + 0xf023, 0xf000, 0xf001, 0xe456, 0x8bcc, 0xf013, 0xf002, 0xf012, + 0x1767, 0x5aaa, 0xf013, 0xf001, 0xf000, 0x0fff, 0x7fff, 0xf124, + 0x0fff, 0x089a, 0x0578, 0x0fff, 0x089a, 0x0013, 0x0245, 0x0eff, + 0x0223, 0x0dde, 0x0135, 0x0789, 0x0ddd, 0xbbbc, 0xf346, 0x0467, + 0x0fff, 0x4eee, 0x3ddd, 0x0edd, 0x0dee, 0x0fff, 0x0fff, 0x0dee, + 0x0def, 0x08ab, 0x0fff, 0x7fff, 0xfabc, 0xf356, 0x0457, 0x0467, + 0x0fff, 0x0bcd, 0x4bde, 0x9bcc, 0x8dee, 0x8eff, 0x8fff, 0x9fff, + 0xadee, 0xeccd, 0xf689, 0xc357, 0x2356, 0x0356, 0x0467, 0x0467, + 0x0fff, 0x0ccd, 0x0bdd, 0x0cdd, 0x0aaa, 0x2234, 0x4135, 0x4346, + 0x5356, 0x2246, 0x0346, 0x0356, 0x0467, 0x0356, 0x0467, 0x0467, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, +); + +my $surface = SDL2::CreateRGBSurfaceFrom( + $pixels, + 16, 16, 16, 16 * 2, + 0x0f00, 0x00f0, 0x000f, 0xf000 +) or die 'Could not create surface: ' . SDL2::GetError; + +# The icon is attached to the window pointer +SDL2::SetWindowIcon( $window, $surface ); + +# ...and the surface containing the icon pixel data is no longer required. +SDL2::FreeSurface($surface); + +# Loop until the user closes the window or presses any key. +MAIN: while (1) { + state $e = SDL2::Event->new; + while ( SDL2::PollEvent($e) ) { + last MAIN if $e->type == SDL2::QUIT || $e->type == SDL2::KEYDOWN; + } +} + +SDL2::DestroyWindow($window); +SDL2::Quit; diff --git a/examples/particles.pl b/examples/particles.pl new file mode 100644 index 0000000..6865e03 --- /dev/null +++ b/examples/particles.pl @@ -0,0 +1,154 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use feature 'say'; +use experimental 'signatures'; + +use constant { + PARTICLES => 1000, + WIDTH => 800, + HEIGHT => 600, +}; + +use SDL2::Raw; +use Time::HiRes 'time'; +use POSIX 'floor'; + +die "Could not initialise SDL2: " . SDL2::GetError + if SDL2::Init( SDL2::INIT_VIDEO ); + +my $window = SDL2::CreateWindow( + 'Particle System!', + SDL2::WINDOWPOS_CENTERED, + SDL2::WINDOWPOS_CENTERED, + WIDTH, + HEIGHT, + SDL2::WINDOW_SHOWN, +) or die "Error creating SDL window: " . SDL2::GetError; + +my $renderer = SDL2::CreateRenderer( + $window, + -1, + SDL2::RENDERER_ACCELERATED, +) or die "Error creating SDL renderer: " . SDL2::GetError; + +my $info = SDL2::RendererInfo->new; +SDL2::GetRendererInfo( $renderer, $info ) + and die 'Could not get renderer info: ' . SDL2::GetError; + +debug( $info => qw( + flags + max_texture_height + max_texture_width + name + num_texture_formats + texture_formats +)); + +my @points; +my @pos = (0) x ( PARTICLES * 2 ); +my @vel = (0) x ( PARTICLES * 2 ); +my @life = (0) x PARTICLES; + +my @times; +my $df = 0; +MAIN: while (1) { + my $start = time; + my $event = SDL2::Event->new; + + while ( SDL2::PollEvent($event) ) { + last MAIN if $event->type == SDL2::QUIT; + } + + update($df); + render(); + + push @times, $df = time - $start; +} + +@times = sort @times; + +my @timings = ( + $times[ int @times / 50 ], + $times[ int @times / 4 ], + $times[ int @times / 2 ], + $times[ int @times * 3 / 4 ], + $times[ @times - int @times / 100 ], +); + +say 'frames per second:'; +say join ' ', map { sprintf '%3.4f', 1 / $_ } @timings; +say 'timings:'; +say join ' ', map { sprintf '%3.4f', $_ } @timings; +print "\n"; + +# Helpers + +sub update ($df) { + my ( $x, $y ) = ( 0, 1 ); + + my $point = 0; + + for my $i ( 0 .. PARTICLES - 1 ) { + my $will_draw; + + if ( $life[$i] <= 0 ) { + if ( rand() < $df ) { + $life[$i] = rand() * 10; + + @vel[$x] = ( rand() - 0.5 ) * 10; + @vel[$y] = ( rand() - 2 ) * 10; + + @pos[$x] = WIDTH / 20; + @pos[$y] = 3 * HEIGHT / 50; + + $will_draw = 1; + } + } + + else { + $vel[$y] *= -0.6 if $pos[$y] > HEIGHT / 10 && $vel[$y] > 0; + + $vel[$y] += 9.81 * $df; + + $pos[$x] += $vel[$x] * $df; + $pos[$y] += $vel[$y] * $df; + + $life[$i] -= $df; + + $will_draw = 1; + } + + if ($will_draw) { + $points[$point++] = floor $pos[$x] * 10; + $points[$point++] = floor $pos[$y] * 10; + } + + $y = ( $x += 2 ) + 1; + } +} + +sub render { + SDL2::SetRenderDrawColor( $renderer, 0x0, 0x0, 0x0, 0xff ); + SDL2::RenderClear($renderer); + + SDL2::SetRenderDrawColor( $renderer, 0xff, 0xff, 0xff, 0x7f ); + SDL2::RenderDrawPoints( $renderer, \@points, int @points / 2 ); + + SDL2::RenderPresent($renderer); +} + +SDL2::DestroyRenderer($renderer); +SDL2::DestroyWindow($window); + +# Helpers + +sub debug { + use Data::Dumper; + local $Data::Dumper::Terse = 1; + local $Data::Dumper::Indent = 0; + local $Data::Dumper::Sortkeys = 1; + my $x = shift; + say ref($x) . ': ' . Dumper({ map { $_ => $x->$_ } @_ }); +} diff --git a/examples/sprites.pl b/examples/sprites.pl new file mode 100644 index 0000000..fc35cc8 --- /dev/null +++ b/examples/sprites.pl @@ -0,0 +1,114 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use feature qw( say state ); + +use SDL2::Raw; +use SDL2::Raw::Image; +use Getopt::Long; +use File::Share 'dist_file'; + +GetOptions( + 'scale=i' => \( my $scale = 3 ), + 'fps=i' => \( my $fps = 10 ), +); + +die 'Could not initialise SDL2: ' . SDL2::GetError + if SDL2::Init( SDL2::INIT_VIDEO ); + +my $window = SDL2::CreateWindow( + 'Sprite demo', + SDL2::WINDOWPOS_CENTERED, + SDL2::WINDOWPOS_CENTERED, + 16 * 8 * $scale, + 32 * $scale, + SDL2::WINDOW_OPENGL | SDL2::WINDOW_RESIZABLE, +) or die 'Error creating SDL window: ' . SDL2::GetError; + +my $renderer = SDL2::CreateRenderer( $window, -1, 0 ) + or die 'Error creating SDL renderer: ' . SDL2::GetError; + +SDL2::Image::Init( SDL2::Image::INIT_PNG ) + or die 'Could not init PNG: ' . SDL2::GetError; + +my $atlas = do { + my $img = SDL2::Image::Load( dist_file 'SDL2-Raw', 'sheet.png' ) + or die 'Error loading image: ' . SDL2::GetError; + + SDL2::CreateTextureFromSurface($renderer, $img) + or die 'Could not: ' . SDL2::GetError; +}; + +my $frame_length = int 1000 * 1 / $fps; +my @sprites = ( + Sprite->new( 128, 0, 16, 32 ), # elf_b + Sprite->new( 128, 32, 16, 32 ), # elf_a + Sprite->new( 128, 64, 16, 32 ), # knight_a + Sprite->new( 128, 96, 16, 32 ), # knight_b + Sprite->new( 128, 128, 16, 32 ), # wizard_a + Sprite->new( 128, 160, 16, 32 ), # wizard_b + Sprite->new( 128, 192, 16, 32 ), # lizard_a + Sprite->new( 128, 224, 16, 32 ), # lizard_b +); + +MAIN: while (1) { + state $event = SDL2::Event->new; + while ( SDL2::PollEvent($event) ) { + last MAIN if $event->type == SDL2::QUIT; + } + + SDL2::RenderClear($renderer); + + for my $i ( 0 .. $#sprites ) { + my $sprite = $sprites[$i]; + $sprite->draw( $renderer, $i * $sprite->{w} * $scale, 0, $scale ); + } + + SDL2::RenderPresent($renderer); + SDL2::Delay($frame_length); +} + +SDL2::DestroyTexture($atlas); +SDL2::DestroyRenderer($renderer); +SDL2::DestroyWindow($window); + +# Helpers + +package Sprite { + sub new { + my ( $class, $x, $y, $w, $h ) = @_; + + bless { + w => $w, + h => $h, + frame => 0, + frames => [ + map SDL2::Rect->new( + x => $x + $_ * $w, + y => $y, + w => $w, + h => $h, + ), 0 .. 3 + ], + } => $class; + } + + sub draw { + my ( $self, $renderer, $x, $y, $scale ) = @_; + + SDL2::RenderCopy( + $renderer, + $atlas, + $self->{frames}[ $self->{frame} ], + SDL2::Rect->new( + x => $x, + y => $y, + w => $self->{w} * $scale, + h => $self->{h} * $scale, + ), + ) and die 'Could not render texture: ' . SDL2::GetError; + + $self->{frame} = 0 if $self->{frame}++ >= $#{ $self->{frames} }; + } +} diff --git a/examples/syswm.pl b/examples/syswm.pl new file mode 100644 index 0000000..8bed6d4 --- /dev/null +++ b/examples/syswm.pl @@ -0,0 +1,40 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use feature qw( say state ); + +use SDL2::Raw; + +say 'This demo should print some system information'; + +SDL2::Init(0) and die 'Could not initialise SDL2: ' . SDL2::GetError; + +my $window = SDL2::CreateWindow('', 0, 0, 0, 0, SDL2::WINDOW_HIDDEN) + or die "Error creating SDL window: " . SDL2::GetError; + +my $info = SDL2::SysWMinfo->new; +SDL2::GetVersion( $info->version ); + +if ( SDL2::GetWindowWMInfo( $window, $info ) ) { + my $v = $info->version; + printf 'This program is running SDL version %s.%s.%s on ', + $v->major, $v->minor, $v->patch; + + for ( $info->subsystem ) { + print 'an unknown system!' if $_ == SDL2::SYSWM_UNKNOWN; + print 'Microsoft Windows(TM)' if $_ == SDL2::SYSWM_WINDOWS; + print 'X Window System' if $_ == SDL2::SYSWM_X11; + print 'DirectFB' if $_ == SDL2::SYSWM_DIRECTFB; + print 'Apple OS X' if $_ == SDL2::SYSWM_COCOA; + print 'UIKit' if $_ == SDL2::SYSWM_UIKIT; + } + + print "\n"; +} +else { + print "Couldn't get window information: " . SDL2::GetError . "\n"; +} + +SDL2::DestroyWindow($window); +SDL2::Quit; diff --git a/examples/windowtitle.pl b/examples/windowtitle.pl new file mode 100644 index 0000000..5fb3a1b --- /dev/null +++ b/examples/windowtitle.pl @@ -0,0 +1,64 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use feature qw( say state ); + +use SDL2::Raw; + +say 'This demo should dynamically set the window title'; + +SDL2::Init( SDL2::INIT_VIDEO ) + and die 'Could not initialise SDL2: ' . SDL2::GetError; + +my $window = SDL2::CreateWindow( + 'This will surely be overwritten', + SDL2::WINDOWPOS_UNDEFINED, + SDL2::WINDOWPOS_UNDEFINED, + 320, + 240, + SDL2::WINDOW_RESIZABLE, +) or die "Error creating SDL window: " . SDL2::GetError; + +my @titles = ( + 't', + 'thi', + 'this w', + 'this win', + 'this windo', + "this window's", + "this window's ti", + "this dinwos's title", + "chis window's title is", + "chih window's title is ", + "chih wandnw's title is ", + "c h wandnw'g title is ", + "c h a nw'g titln is ", + "c h a n g i n ig!", + '', + 'c h a n g i n g!', + '', + 'c h a n g i n g!', + 'c h a n g i n g!', +); + +my $event = SDL2::Event->new; + +MAIN: while (1) { + state $t = 0; + state $i = 0; + + while ( SDL2::PollEvent($event) ) { + last MAIN if $event->type == SDL2::QUIT; + } + + unless ( ++$t % 9 ) { + SDL2::SetWindowTitle( $window, $titles[$i] ); + $i = 0 if ++$i > $#titles; + } + + SDL2::Delay(10); +} + +SDL2::DestroyWindow($window); +SDL2::Quit; diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm new file mode 100644 index 0000000..2d241f1 --- /dev/null +++ b/lib/SDL2/Raw.pm @@ -0,0 +1,1680 @@ +# ABSTRACT: FFI bindings for SDL2 +package SDL2; + +use strict; +use warnings; + +use FFI::CheckLib; +use FFI::Platypus 1.00; +use FFI::C; +use Ref::Util; + +my $ffi; +BEGIN { + $ffi = FFI::Platypus->new( api => 1 ); + $ffi->lib( find_lib_or_exit lib => 'SDL2' ); + FFI::C->ffi($ffi); +} + +use Config; +use constant { + K_SCANCODE_MASK => 1 << 30, + BYTEORDER => $Config{byteorder}, + BIG_ENDIAN => 4321, + LIL_ENDIAN => 1234, +}; + +sub enum { + require enum::hash; + require constant; + + my %enums = @_; + while ( my ( $name, $values ) = each %enums ) { + my $const = Ref::Util::is_hashref $values ? $values : { enum::hash::enum( @$values ) }; + + constant->import($const); + + my $variable = __PACKAGE__ . '::' . $name; + no strict 'refs'; + %{$variable} = ( %{$variable}, reverse %$const ); + } +} + +sub pixel_format { + my ( $type, $order, $layout, $bits, $bytes ) = @_; + return ( 1 << 28 ) + | ( $type << 24 ) + | ( $order << 20 ) + | ( $layout << 16 ) + | ( $bits << 8 ) + | $bytes; +} + +BEGIN { + enum( + HintPriority => [qw( + HINT_DEFAULT + HINT_NORMAL + HINT_OVERRIDE + )], + LogCategory => [qw( + LOG_CATEGORY_APPLICATION + LOG_CATEGORY_ERROR + LOG_CATEGORY_ASSERT + LOG_CATEGORY_SYSTEM + LOG_CATEGORY_AUDIO + LOG_CATEGORY_VIDEO + LOG_CATEGORY_RENDER + LOG_CATEGORY_INPUT + LOG_CATEGORY_TEST + LOG_CATEGORY_RESERVED1 + LOG_CATEGORY_RESERVED2 + LOG_CATEGORY_RESERVED3 + LOG_CATEGORY_RESERVED4 + LOG_CATEGORY_RESERVED5 + LOG_CATEGORY_RESERVED6 + LOG_CATEGORY_RESERVED7 + LOG_CATEGORY_RESERVED8 + LOG_CATEGORY_RESERVED9 + LOG_CATEGORY_RESERVED10 + LOG_CATEGORY_CUSTOM + )], + LogPriority => [qw( + LOG_PRIORITY_VERBOSE=1 + LOG_PRIORITY_DEBUG + LOG_PRIORITY_INFO + LOG_PRIORITY_WARN + LOG_PRIORITY_ERROR + LOG_PRIORITY_CRITICAL + NUM_LOG_PRIORITIES + )], + WindowFlags => { + WINDOW_FULLSCREEN => 0x00000001, + WINDOW_OPENGL => 0x00000002, + WINDOW_SHOWN => 0x00000004, + WINDOW_HIDDEN => 0x00000008, + WINDOW_BORDERLESS => 0x00000010, + WINDOW_RESIZABLE => 0x00000020, + WINDOW_MINIMIZED => 0x00000040, + WINDOW_MAXIMIZED => 0x00000080, + WINDOW_MOUSE_GRABBED => 0x00000100, + WINDOW_INPUT_FOCUS => 0x00000200, + WINDOW_MOUSE_FOCUS => 0x00000400, + WINDOW_FULLSCREEN_DESKTOP => 0x00001001, + WINDOW_FOREIGN => 0x00000800, + WINDOW_ALLOW_HIGHDPI => 0x00002000, + WINDOW_MOUSE_CAPTURE => 0x00004000, + WINDOW_ALWAYS_ON_TOP => 0x00008000, + WINDOW_SKIP_TASKBAR => 0x00010000, + WINDOW_UTILITY => 0x00020000, + WINDOW_TOOLTIP => 0x00040000, + WINDOW_POPUP_MENU => 0x00080000, + WINDOW_KEYBOARD_GRABBED => 0x00100000, + WINDOW_VULKAN => 0x10000000, + WINDOW_METAL => 0x20000000, + WINDOW_INPUT_GRABBED => 0x00000100, + }, + WindowEventID => [qw( + WINDOWEVENT_NONE + WINDOWEVENT_SHOWN + WINDOWEVENT_HIDDEN + WINDOWEVENT_EXPOSED + WINDOWEVENT_MOVED + WINDOWEVENT_RESIZED + WINDOWEVENT_SIZE_CHANGED + WINDOWEVENT_MINIMIZED + WINDOWEVENT_MAXIMIZED + WINDOWEVENT_RESTORED + WINDOWEVENT_ENTER + WINDOWEVENT_LEAVE + WINDOWEVENT_FOCUS_GAINED + WINDOWEVENT_FOCUS_LOST + WINDOWEVENT_CLOSE + WINDOWEVENT_TAKE_FOCUS + WINDOWEVENT_HIT_TEST + )], + DisplayEventID => [qw( + DISPLAYEVENT_NONE + DISPLAYEVENT_ORIENTATION + DISPLAYEVENT_CONNECTED + DISPLAYEVENT_DISCONNECTED + )], + DisplayOrientation => [qw( + ORIENTATION_UNKNOWN + ORIENTATION_LANDSCAPE + ORIENTATION_LANDSCAPE_FLIPPED + ORIENTATION_PORTRAIT + ORIENTATION_PORTRAIT_FLIPPED + )], + GLattr => [qw( + GL_RED_SIZE + GL_GREEN_SIZE + GL_BLUE_SIZE + GL_ALPHA_SIZE + GL_BUFFER_SIZE + GL_DOUBLEBUFFER + GL_DEPTH_SIZE + GL_STENCIL_SIZE + GL_ACCUM_RED_SIZE + GL_ACCUM_GREEN_SIZE + GL_ACCUM_BLUE_SIZE + GL_ACCUM_ALPHA_SIZE + GL_STEREO + GL_MULTISAMPLEBUFFERS + GL_MULTISAMPLESAMPLES + GL_ACCELERATED_VISUAL + GL_RETAINED_BACKING + GL_CONTEXT_MAJOR_VERSION + GL_CONTEXT_MINOR_VERSION + GL_CONTEXT_EGL + GL_CONTEXT_FLAGS + GL_CONTEXT_PROFILE_MASK + GL_SHARE_WITH_CURRENT_CONTEXT + GL_FRAMEBUFFER_SRGB_CAPABLE + GL_CONTEXT_RELEASE_BEHAVIOR + GL_CONTEXT_RESET_NOTIFICATION + GL_CONTEXT_NO_ERROR + )], + GLprofile => { + GL_CONTEXT_PROFILE_CORE => 0x0001, + GL_CONTEXT_PROFILE_COMPATIBILITY => 0x0002, + GL_CONTEXT_PROFILE_ES => 0x0004, + }, + GLcontextFlag => { + GL_CONTEXT_DEBUG_FLAG => 0x0001, + GL_CONTEXT_FORWARD_COMPATIBLE_FLAG => 0x0002, + GL_CONTEXT_ROBUST_ACCESS_FLAG => 0x0004, + GL_CONTEXT_RESET_ISOLATION_FLAG => 0x0008, + }, + GLcontextReleaseFlag => [qw( + GL_CONTEXT_RELEASE_BEHAVIOR_NONE + GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH + )], + GLContextResetNotification => [qw( + GL_CONTEXT_RESET_NO_NOTIFICATION + GL_CONTEXT_RESET_LOSE_CONTEXT + )], + RendererFlags => { + RENDERER_SOFTWARE => 0x00000001, + RENDERER_ACCELERATED => 0x00000002, + RENDERER_PRESENTVSYNC => 0x00000004, + RENDERER_TARGETTEXTURE => 0x00000008, + }, + ScaleMode => [qw( + SCALEMODENEAREST + SCALEMODELINEAR + SCALEMODEBEST + )], + TextureAccess => [qw( + TEXTUREACCESS_STATIC + TEXTUREACCESS_STREAMING + TEXTUREACCESS_TARGET + )], + TextureModulate => [qw( + TEXTUREMODULATE_NONE + TEXTUREMODULATE_COLOR + TEXTUREMODULATE_ALPHA + )], + RendererFlip => [qw( + FLIP_NONE + FLIP_HORIZONTAL + FLIP_VERTICAL + )], + BlendMode => { + BLENDMODE_NONE => 0x00000000, + BLENDMODE_BLEND => 0x00000001, + BLENDMODE_ADD => 0x00000002, + BLENDMODE_MOD => 0x00000004, + BLENDMODE_MUL => 0x00000008, + BLENDMODE_INVALID => 0x7FFFFFFF, + }, + BlendOperation => [qw( + BLENDOPERATION_ADD + BLENDOPERATION_SUBTRACT + BLENDOPERATION_REV_SUBTRACT + BLENDOPERATION_MINIMUM + BLENDOPERATION_MAXIMUM + )], + BlendFactor => [qw( + BLENDFACTOR_ZERO + BLENDFACTOR_ONE + BLENDFACTOR_SRC_COLOR + BLENDFACTOR_ONE_MINUS_SRC_COLOR + BLENDFACTOR_SRC_ALPHA + BLENDFACTOR_ONE_MINUS_SRC_ALPHA + BLENDFACTOR_DST_COLOR + BLENDFACTOR_ONE_MINUS_DST_COLOR + BLENDFACTOR_DST_ALPHA + BLENDFACTOR_ONE_MINUS_DST_ALPHA + )], + PowerState => [qw( + POWERSTATE_UNKNOWN + POWERSTATE_ON_BATTERY + POWERSTATE_NO_BATTERY + POWERSTATE_CHARGING + POWERSTATE_CHARGED + )], + eventaction => [qw( + ADDEVENT + PEEKEVENT + GETEVENT + )], + SystemCursor => [qw( + SYSTEM_CURSOR_ARROW + SYSTEM_CURSOR_IBEAM + SYSTEM_CURSOR_WAIT + SYSTEM_CURSOR_CROSSHAIR + SYSTEM_CURSOR_WAITARROW + SYSTEM_CURSOR_SIZENWSE + SYSTEM_CURSOR_SIZENESW + SYSTEM_CURSOR_SIZEWE + SYSTEM_CURSOR_SIZENS + SYSTEM_CURSOR_SIZEALL + SYSTEM_CURSOR_NO + SYSTEM_CURSOR_HAND + NUM_SYSTEM_CURSORS + )], + MouseWheelDirection => [qw( + MOUSEWHEEL_NORMAL + MOUSEWHEEL_FLIPPED + )], + PixelType => [qw( + PIXELTYPE_UNKNOWN + PIXELTYPE_INDEX1 + PIXELTYPE_INDEX4 + PIXELTYPE_INDEX8 + PIXELTYPE_PACKED8 + PIXELTYPE_PACKED16 + PIXELTYPE_PACKED32 + PIXELTYPE_ARRAYU8 + PIXELTYPE_ARRAYU16 + PIXELTYPE_ARRAYU32 + PIXELTYPE_ARRAYF16 + PIXELTYPE_ARRAYF32 + )], + BitmapOrder => [qw( + BITMAPORDER_NONE + BITMAPORDER_4321 + BITMAPORDER_1234 + )], + PackedOrder => [qw( + PACKEDORDER_NONE + PACKEDORDER_XRGB + PACKEDORDER_RGBX + PACKEDORDER_ARGB + PACKEDORDER_RGBA + PACKEDORDER_XBGR + PACKEDORDER_BGRX + PACKEDORDER_ABGR + PACKEDORDER_BGRA + )], + ArrayOrder => [qw( + ARRAYORDER_NONE + ARRAYORDER_RGB + ARRAYORDER_RGBA + ARRAYORDER_ARGB + ARRAYORDER_BGR + ARRAYORDER_BGRA + ARRAYORDER_ABGR + )], + PackedLayout => [qw( + PACKEDLAYOUT_NONE + PACKEDLAYOUT_332 + PACKEDLAYOUT_4444 + PACKEDLAYOUT_1555 + PACKEDLAYOUT_5551 + PACKEDLAYOUT_565 + PACKEDLAYOUT_8888 + PACKEDLAYOUT_2101010 + PACKEDLAYOUT_1010102 + )], + ScanCode => [qw( + SCANCODE_UNKNOWN + + SCANCODE_A=4 + SCANCODE_B + SCANCODE_C + SCANCODE_D + SCANCODE_E + SCANCODE_F + SCANCODE_G + SCANCODE_H + SCANCODE_I + SCANCODE_J + SCANCODE_K + SCANCODE_L + SCANCODE_M + SCANCODE_N + SCANCODE_O + SCANCODE_P + SCANCODE_Q + SCANCODE_R + SCANCODE_S + SCANCODE_T + SCANCODE_U + SCANCODE_V + SCANCODE_W + SCANCODE_X + SCANCODE_Y + SCANCODE_Z + SCANCODE_1 + SCANCODE_2 + SCANCODE_3 + SCANCODE_4 + SCANCODE_5 + SCANCODE_6 + SCANCODE_7 + SCANCODE_8 + SCANCODE_9 + SCANCODE_0 + SCANCODE_RETURN + SCANCODE_ESCAPE + SCANCODE_BACKSPACE + SCANCODE_TAB + SCANCODE_SPACE + SCANCODE_MINUS + SCANCODE_EQUALS + SCANCODE_LEFTBRACKET + SCANCODE_RIGHTBRACKET + SCANCODE_BACKSLASH + SCANCODE_NONUSHASH + SCANCODE_SEMICOLON + SCANCODE_APOSTROPHE + SCANCODE_GRAVE + SCANCODE_COMMA + SCANCODE_PERIOD + SCANCODE_SLASH + SCANCODE_CAPSLOCK + SCANCODE_F1 + SCANCODE_F2 + SCANCODE_F3 + SCANCODE_F4 + SCANCODE_F5 + SCANCODE_F6 + SCANCODE_F7 + SCANCODE_F8 + SCANCODE_F9 + SCANCODE_F10 + SCANCODE_F11 + SCANCODE_F12 + SCANCODE_PRINTSCREEN + SCANCODE_SCROLLLOCK + SCANCODE_PAUSE + SCANCODE_INSERT + SCANCODE_HOME + SCANCODE_PAGEUP + SCANCODE_DELETE + SCANCODE_END + SCANCODE_PAGEDOWN + SCANCODE_RIGHT + SCANCODE_LEFT + SCANCODE_DOWN + SCANCODE_UP + SCANCODE_NUMLOCKCLEAR + SCANCODE_KP_DIVIDE + SCANCODE_KP_MULTIPLY + SCANCODE_KP_MINUS + SCANCODE_KP_PLUS + SCANCODE_KP_ENTER + SCANCODE_KP_1 + SCANCODE_KP_2 + SCANCODE_KP_3 + SCANCODE_KP_4 + SCANCODE_KP_5 + SCANCODE_KP_6 + SCANCODE_KP_7 + SCANCODE_KP_8 + SCANCODE_KP_9 + SCANCODE_KP_0 + SCANCODE_KP_PERIOD + SCANCODE_NONUSBACKSLASH + SCANCODE_APPLICATION + SCANCODE_POWER + SCANCODE_KP_EQUALS + SCANCODE_F13 + SCANCODE_F14 + SCANCODE_F15 + SCANCODE_F16 + SCANCODE_F17 + SCANCODE_F18 + SCANCODE_F19 + SCANCODE_F20 + SCANCODE_F21 + SCANCODE_F22 + SCANCODE_F23 + SCANCODE_F24 + SCANCODE_EXECUTE + SCANCODE_HELP + SCANCODE_MENU + SCANCODE_SELECT + SCANCODE_STOP + SCANCODE_AGAIN + SCANCODE_UNDO + SCANCODE_CUT + SCANCODE_COPY + SCANCODE_PASTE + SCANCODE_FIND + SCANCODE_MUTE + SCANCODE_VOLUMEUP + SCANCODE_VOLUMEDOWN + SCANCODE_KP_COMMA + SCANCODE_KP_EQUALSAS400 + SCANCODE_INTERNATIONAL1 + SCANCODE_INTERNATIONAL2 + SCANCODE_INTERNATIONAL3 + SCANCODE_INTERNATIONAL4 + SCANCODE_INTERNATIONAL5 + SCANCODE_INTERNATIONAL6 + SCANCODE_INTERNATIONAL7 + SCANCODE_INTERNATIONAL8 + SCANCODE_INTERNATIONAL9 + SCANCODE_LANG1 + SCANCODE_LANG2 + SCANCODE_LANG3 + SCANCODE_LANG4 + SCANCODE_LANG5 + SCANCODE_LANG6 + SCANCODE_LANG7 + SCANCODE_LANG8 + SCANCODE_LANG9 + SCANCODE_ALTERASE + SCANCODE_SYSREQ + SCANCODE_CANCEL + SCANCODE_CLEAR + SCANCODE_PRIOR + SCANCODE_RETURN2 + SCANCODE_SEPARATOR + SCANCODE_OUT + SCANCODE_OPER + SCANCODE_CLEARAGAIN + SCANCODE_CRSEL + SCANCODE_EXSEL + + SCANCODE_KP_00=176 + SCANCODE_KP_000 + SCANCODE_THOUSANDSSEPARATOR + SCANCODE_DECIMALSEPARATOR + SCANCODE_CURRENCYUNIT + SCANCODE_CURRENCYSUBUNIT + SCANCODE_KP_LEFTPAREN + SCANCODE_KP_RIGHTPAREN + SCANCODE_KP_LEFTBRACE + SCANCODE_KP_RIGHTBRACE + SCANCODE_KP_TAB + SCANCODE_KP_BACKSPACE + SCANCODE_KP_A + SCANCODE_KP_B + SCANCODE_KP_C + SCANCODE_KP_D + SCANCODE_KP_E + SCANCODE_KP_F + SCANCODE_KP_XOR + SCANCODE_KP_POWER + SCANCODE_KP_PERCENT + SCANCODE_KP_LESS + SCANCODE_KP_GREATER + SCANCODE_KP_AMPERSAND + SCANCODE_KP_DBLAMPERSAND + SCANCODE_KP_VERTICALBAR + SCANCODE_KP_DBLVERTICALBAR + SCANCODE_KP_COLON + SCANCODE_KP_HASH + SCANCODE_KP_SPACE + SCANCODE_KP_AT + SCANCODE_KP_EXCLAM + SCANCODE_KP_MEMSTORE + SCANCODE_KP_MEMRECALL + SCANCODE_KP_MEMCLEAR + SCANCODE_KP_MEMADD + SCANCODE_KP_MEMSUBTRACT + SCANCODE_KP_MEMMULTIPLY + SCANCODE_KP_MEMDIVIDE + SCANCODE_KP_PLUSMINUS + SCANCODE_KP_CLEAR + SCANCODE_KP_CLEARENTRY + SCANCODE_KP_BINARY + SCANCODE_KP_OCTAL + SCANCODE_KP_DECIMAL + SCANCODE_KP_HEXADECIMAL + + SCANCODE_LCTRL=224 + SCANCODE_LSHIFT + SCANCODE_LALT + SCANCODE_LGUI + SCANCODE_RCTRL + SCANCODE_RSHIFT + SCANCODE_RALT + SCANCODE_RGUI + + SCANCODE_MODE=257 + SCANCODE_AUDIONEXT + SCANCODE_AUDIOPREV + SCANCODE_AUDIOSTOP + SCANCODE_AUDIOPLAY + SCANCODE_AUDIOMUTE + SCANCODE_MEDIASELECT + SCANCODE_WWW + SCANCODE_MAIL + SCANCODE_CALCULATOR + SCANCODE_COMPUTER + SCANCODE_AC_SEARCH + SCANCODE_AC_HOME + SCANCODE_AC_BACK + SCANCODE_AC_FORWARD + SCANCODE_AC_STOP + SCANCODE_AC_REFRESH + SCANCODE_AC_BOOKMARKS + SCANCODE_BRIGHTNESSDOWN + SCANCODE_BRIGHTNESSUP + SCANCODE_DISPLAYSWITCH + SCANCODE_KBDILLUMTOGGLE + SCANCODE_KBDILLUMDOWN + SCANCODE_KBDILLUMUP + SCANCODE_EJECT + SCANCODE_SLEEP + SCANCODE_APP1 + SCANCODE_APP2 + SCANCODE_AUDIOREWIND + SCANCODE_AUDIOFASTFORWARD + + NUM_SCANCODES=512 + )], + EventType => [qw( + FIRSTEVENT=0 + + QUIT=0x100 + + APP_TERMINATING + APP_LOWMEMORY + APP_WILLENTERBACKGROUND + APP_DIDENTERBACKGROUND + APP_WILLENTERFOREGROUND + APP_DIDENTERFOREGROUND + + LOCALECHANGED + + DISPLAYEVENT=0x150 + + WINDOWEVENT=0x200 + SYSWMEVENT + + KEYDOWN=0x300 + KEYUP + TEXTEDITING + TEXTINPUT + KEYMAPCHANGED + + MOUSEMOTION=0x400 + MOUSEBUTTONDOWN + MOUSEBUTTONUP + MOUSEWHEEL + + JOYAXISMOTION=0x600 + JOYBALLMOTION + JOYHATMOTION + JOYBUTTONDOWN + JOYBUTTONUP + JOYDEVICEADDED + JOYDEVICEREMOVED + + CONTROLLERAXISMOTION=0x650 + CONTROLLERBUTTONDOWN + CONTROLLERBUTTONUP + CONTROLLERDEVICEADDED + CONTROLLERDEVICEREMOVED + CONTROLLERDEVICEREMAPPED + CONTROLLERTOUCHPADDOWN + CONTROLLERTOUCHPADMOTION + CONTROLLERTOUCHPADUP + CONTROLLERSENSORUPDATE + + FINGERDOWN=0x700 + FINGERUP + FINGERMOTION + + DOLLARGESTURE=0x800 + DOLLARRECORD + MULTIGESTURE + + CLIPBOARDUPDATE=0x900 + + DROPFILE=0x1000 + DROPTEXT + DROPBEGIN + DROPCOMPLETE + + AUDIODEVICEADDED=0x1100 + AUDIODEVICEREMOVED + + SENSORUPDATE=0x1200 + + RENDER_TARGETS_RESET=0x2000 + RENDER_DEVICE_RESET + + USEREVENT=0x8000 + + LASTEVENT=0xFFFF + )], + SYSWM_TYPE => [qw( + SYSWM_UNKNOWN + SYSWM_WINDOWS + SYSWM_X11 + SYSWM_DIRECTFB + SYSWM_COCOA + SYSWM_UIKIT + SYSWM_WAYLAND + SYSWM_MIR + SYSWM_WINRT + SYSWM_ANDROID + SYSWM_VIVANTE + SYSWM_OS2 + SYSWM_HAIKU + SYSWM_KMSDRM + )], + ); +} + +BEGIN { + enum( + Keycode => { + K_UNKNOWN => 0, + K_RETURN => ord "\r", + K_ESCAPE => 27, # 033 + K_BACKSPACE => ord "\b", + K_TAB => ord "\t", + K_SPACE => ord ' ', + K_EXCLAIM => ord '!', + K_QUOTEDBL => ord '"', + K_HASH => ord '#', + K_PERCENT => ord '%', + K_DOLLAR => ord '$', + K_AMPERSAND => ord '&', + K_QUOTE => ord "'", + K_LEFTPAREN => ord '(', + K_RIGHTPAREN => ord ')', + K_ASTERISK => ord '*', + K_PLUS => ord '+', + K_COMMA => ord ',', + K_MINUS => ord '-', + K_PERIOD => ord '.', + K_SLASH => ord '/', + K_0 => ord '0', + K_1 => ord '1', + K_2 => ord '2', + K_3 => ord '3', + K_4 => ord '4', + K_5 => ord '5', + K_6 => ord '6', + K_7 => ord '7', + K_8 => ord '8', + K_9 => ord '9', + K_COLON => ord ':', + K_SEMICOLON => ord ';', + K_LESS => ord '<', + K_EQUALS => ord '=', + K_GREATER => ord '>', + K_QUESTION => ord '?', + K_AT => ord '@', + K_LEFTBRACKET => ord '[', + K_BACKSLASH => ord '\\', + K_RIGHTBRACKET => ord ']', + K_CARET => ord '^', + K_UNDERSCORE => ord '_', + K_BACKQUOTE => ord '`', + K_a => ord 'a', + K_b => ord 'b', + K_c => ord 'c', + K_d => ord 'd', + K_e => ord 'e', + K_f => ord 'f', + K_g => ord 'g', + K_h => ord 'h', + K_i => ord 'i', + K_j => ord 'j', + K_k => ord 'k', + K_l => ord 'l', + K_m => ord 'm', + K_n => ord 'n', + K_o => ord 'o', + K_p => ord 'p', + K_q => ord 'q', + K_r => ord 'r', + K_s => ord 's', + K_t => ord 't', + K_u => ord 'u', + K_v => ord 'v', + K_w => ord 'w', + K_x => ord 'x', + K_y => ord 'y', + K_z => ord 'z', + + K_CAPSLOCK => SCANCODE_CAPSLOCK | K_SCANCODE_MASK, + + K_F1 => SCANCODE_F1 | K_SCANCODE_MASK, + K_F2 => SCANCODE_F2 | K_SCANCODE_MASK, + K_F3 => SCANCODE_F3 | K_SCANCODE_MASK, + K_F4 => SCANCODE_F4 | K_SCANCODE_MASK, + K_F5 => SCANCODE_F5 | K_SCANCODE_MASK, + K_F6 => SCANCODE_F6 | K_SCANCODE_MASK, + K_F7 => SCANCODE_F7 | K_SCANCODE_MASK, + K_F8 => SCANCODE_F8 | K_SCANCODE_MASK, + K_F9 => SCANCODE_F9 | K_SCANCODE_MASK, + K_F10 => SCANCODE_F10 | K_SCANCODE_MASK, + K_F11 => SCANCODE_F11 | K_SCANCODE_MASK, + K_F12 => SCANCODE_F12 | K_SCANCODE_MASK, + + K_PRINTSCREEN => SCANCODE_PRINTSCREEN | K_SCANCODE_MASK, + K_SCROLLLOCK => SCANCODE_SCROLLLOCK | K_SCANCODE_MASK, + K_PAUSE => SCANCODE_PAUSE | K_SCANCODE_MASK, + K_INSERT => SCANCODE_INSERT | K_SCANCODE_MASK, + K_HOME => SCANCODE_HOME | K_SCANCODE_MASK, + K_PAGEUP => SCANCODE_PAGEUP | K_SCANCODE_MASK, + K_DELETE => 127, # 0177 + K_END => SCANCODE_END | K_SCANCODE_MASK, + K_PAGEDOWN => SCANCODE_PAGEDOWN | K_SCANCODE_MASK, + K_RIGHT => SCANCODE_RIGHT | K_SCANCODE_MASK, + K_LEFT => SCANCODE_LEFT | K_SCANCODE_MASK, + K_DOWN => SCANCODE_DOWN | K_SCANCODE_MASK, + K_UP => SCANCODE_UP | K_SCANCODE_MASK, + + K_NUMLOCKCLEAR => SCANCODE_NUMLOCKCLEAR | K_SCANCODE_MASK, + K_KP_DIVIDE => SCANCODE_KP_DIVIDE | K_SCANCODE_MASK, + K_KP_MULTIPLY => SCANCODE_KP_MULTIPLY | K_SCANCODE_MASK, + K_KP_MINUS => SCANCODE_KP_MINUS | K_SCANCODE_MASK, + K_KP_PLUS => SCANCODE_KP_PLUS | K_SCANCODE_MASK, + K_KP_ENTER => SCANCODE_KP_ENTER | K_SCANCODE_MASK, + K_KP_1 => SCANCODE_KP_1 | K_SCANCODE_MASK, + K_KP_2 => SCANCODE_KP_2 | K_SCANCODE_MASK, + K_KP_3 => SCANCODE_KP_3 | K_SCANCODE_MASK, + K_KP_4 => SCANCODE_KP_4 | K_SCANCODE_MASK, + K_KP_5 => SCANCODE_KP_5 | K_SCANCODE_MASK, + K_KP_6 => SCANCODE_KP_6 | K_SCANCODE_MASK, + K_KP_7 => SCANCODE_KP_7 | K_SCANCODE_MASK, + K_KP_8 => SCANCODE_KP_8 | K_SCANCODE_MASK, + K_KP_9 => SCANCODE_KP_9 | K_SCANCODE_MASK, + K_KP_0 => SCANCODE_KP_0 | K_SCANCODE_MASK, + K_KP_PERIOD => SCANCODE_KP_PERIOD | K_SCANCODE_MASK, + + K_APPLICATION => SCANCODE_APPLICATION | K_SCANCODE_MASK, + K_POWER => SCANCODE_POWER | K_SCANCODE_MASK, + K_KP_EQUALS => SCANCODE_KP_EQUALS | K_SCANCODE_MASK, + K_F13 => SCANCODE_F13 | K_SCANCODE_MASK, + K_F14 => SCANCODE_F14 | K_SCANCODE_MASK, + K_F15 => SCANCODE_F15 | K_SCANCODE_MASK, + K_F16 => SCANCODE_F16 | K_SCANCODE_MASK, + K_F17 => SCANCODE_F17 | K_SCANCODE_MASK, + K_F18 => SCANCODE_F18 | K_SCANCODE_MASK, + K_F19 => SCANCODE_F19 | K_SCANCODE_MASK, + K_F20 => SCANCODE_F20 | K_SCANCODE_MASK, + K_F21 => SCANCODE_F21 | K_SCANCODE_MASK, + K_F22 => SCANCODE_F22 | K_SCANCODE_MASK, + K_F23 => SCANCODE_F23 | K_SCANCODE_MASK, + K_F24 => SCANCODE_F24 | K_SCANCODE_MASK, + K_EXECUTE => SCANCODE_EXECUTE | K_SCANCODE_MASK, + K_HELP => SCANCODE_HELP | K_SCANCODE_MASK, + K_MENU => SCANCODE_MENU | K_SCANCODE_MASK, + K_SELECT => SCANCODE_SELECT | K_SCANCODE_MASK, + K_STOP => SCANCODE_STOP | K_SCANCODE_MASK, + K_AGAIN => SCANCODE_AGAIN | K_SCANCODE_MASK, + K_UNDO => SCANCODE_UNDO | K_SCANCODE_MASK, + K_CUT => SCANCODE_CUT | K_SCANCODE_MASK, + K_COPY => SCANCODE_COPY | K_SCANCODE_MASK, + K_PASTE => SCANCODE_PASTE | K_SCANCODE_MASK, + K_FIND => SCANCODE_FIND | K_SCANCODE_MASK, + K_MUTE => SCANCODE_MUTE | K_SCANCODE_MASK, + K_VOLUMEUP => SCANCODE_VOLUMEUP | K_SCANCODE_MASK, + K_VOLUMEDOWN => SCANCODE_VOLUMEDOWN | K_SCANCODE_MASK, + K_KP_COMMA => SCANCODE_KP_COMMA | K_SCANCODE_MASK, + K_KP_EQUALSAS400 => SCANCODE_KP_EQUALSAS400 | K_SCANCODE_MASK, + + K_ALTERASE => SCANCODE_ALTERASE | K_SCANCODE_MASK, + K_SYSREQ => SCANCODE_SYSREQ | K_SCANCODE_MASK, + K_CANCEL => SCANCODE_CANCEL | K_SCANCODE_MASK, + K_CLEAR => SCANCODE_CLEAR | K_SCANCODE_MASK, + K_PRIOR => SCANCODE_PRIOR | K_SCANCODE_MASK, + K_RETURN2 => SCANCODE_RETURN2 | K_SCANCODE_MASK, + K_SEPARATOR => SCANCODE_SEPARATOR | K_SCANCODE_MASK, + K_OUT => SCANCODE_OUT | K_SCANCODE_MASK, + K_OPER => SCANCODE_OPER | K_SCANCODE_MASK, + K_CLEARAGAIN => SCANCODE_CLEARAGAIN | K_SCANCODE_MASK, + K_CRSEL => SCANCODE_CRSEL | K_SCANCODE_MASK, + K_EXSEL => SCANCODE_EXSEL | K_SCANCODE_MASK, + + K_KP_00 => SCANCODE_KP_00 | K_SCANCODE_MASK, + K_KP_000 => SCANCODE_KP_000 | K_SCANCODE_MASK, + K_THOUSANDSSEPARATOR => SCANCODE_THOUSANDSSEPARATOR | K_SCANCODE_MASK, + K_DECIMALSEPARATOR => SCANCODE_DECIMALSEPARATOR | K_SCANCODE_MASK, + K_CURRENCYUNIT => SCANCODE_CURRENCYUNIT | K_SCANCODE_MASK, + K_CURRENCYSUBUNIT => SCANCODE_CURRENCYSUBUNIT | K_SCANCODE_MASK, + K_KP_LEFTPAREN => SCANCODE_KP_LEFTPAREN | K_SCANCODE_MASK, + K_KP_RIGHTPAREN => SCANCODE_KP_RIGHTPAREN | K_SCANCODE_MASK, + K_KP_LEFTBRACE => SCANCODE_KP_LEFTBRACE | K_SCANCODE_MASK, + K_KP_RIGHTBRACE => SCANCODE_KP_RIGHTBRACE | K_SCANCODE_MASK, + K_KP_TAB => SCANCODE_KP_TAB | K_SCANCODE_MASK, + K_KP_BACKSPACE => SCANCODE_KP_BACKSPACE | K_SCANCODE_MASK, + K_KP_A => SCANCODE_KP_A | K_SCANCODE_MASK, + K_KP_B => SCANCODE_KP_B | K_SCANCODE_MASK, + K_KP_C => SCANCODE_KP_C | K_SCANCODE_MASK, + K_KP_D => SCANCODE_KP_D | K_SCANCODE_MASK, + K_KP_E => SCANCODE_KP_E | K_SCANCODE_MASK, + K_KP_F => SCANCODE_KP_F | K_SCANCODE_MASK, + K_KP_XOR => SCANCODE_KP_XOR | K_SCANCODE_MASK, + K_KP_POWER => SCANCODE_KP_POWER | K_SCANCODE_MASK, + K_KP_PERCENT => SCANCODE_KP_PERCENT | K_SCANCODE_MASK, + K_KP_LESS => SCANCODE_KP_LESS | K_SCANCODE_MASK, + K_KP_GREATER => SCANCODE_KP_GREATER | K_SCANCODE_MASK, + K_KP_AMPERSAND => SCANCODE_KP_AMPERSAND | K_SCANCODE_MASK, + K_KP_DBLAMPERSAND => SCANCODE_KP_DBLAMPERSAND | K_SCANCODE_MASK, + K_KP_VERTICALBAR => SCANCODE_KP_VERTICALBAR | K_SCANCODE_MASK, + K_KP_DBLVERTICALBAR => SCANCODE_KP_DBLVERTICALBAR | K_SCANCODE_MASK, + K_KP_COLON => SCANCODE_KP_COLON | K_SCANCODE_MASK, + K_KP_HASH => SCANCODE_KP_HASH | K_SCANCODE_MASK, + K_KP_SPACE => SCANCODE_KP_SPACE | K_SCANCODE_MASK, + K_KP_AT => SCANCODE_KP_AT | K_SCANCODE_MASK, + K_KP_EXCLAM => SCANCODE_KP_EXCLAM | K_SCANCODE_MASK, + K_KP_MEMSTORE => SCANCODE_KP_MEMSTORE | K_SCANCODE_MASK, + K_KP_MEMRECALL => SCANCODE_KP_MEMRECALL | K_SCANCODE_MASK, + K_KP_MEMCLEAR => SCANCODE_KP_MEMCLEAR | K_SCANCODE_MASK, + K_KP_MEMADD => SCANCODE_KP_MEMADD | K_SCANCODE_MASK, + K_KP_MEMSUBTRACT => SCANCODE_KP_MEMSUBTRACT | K_SCANCODE_MASK, + K_KP_MEMMULTIPLY => SCANCODE_KP_MEMMULTIPLY | K_SCANCODE_MASK, + K_KP_MEMDIVIDE => SCANCODE_KP_MEMDIVIDE | K_SCANCODE_MASK, + K_KP_PLUSMINUS => SCANCODE_KP_PLUSMINUS | K_SCANCODE_MASK, + K_KP_CLEAR => SCANCODE_KP_CLEAR | K_SCANCODE_MASK, + K_KP_CLEARENTRY => SCANCODE_KP_CLEARENTRY | K_SCANCODE_MASK, + K_KP_BINARY => SCANCODE_KP_BINARY | K_SCANCODE_MASK, + K_KP_OCTAL => SCANCODE_KP_OCTAL | K_SCANCODE_MASK, + K_KP_DECIMAL => SCANCODE_KP_DECIMAL | K_SCANCODE_MASK, + K_KP_HEXADECIMAL => SCANCODE_KP_HEXADECIMAL | K_SCANCODE_MASK, + + K_LCTRL => SCANCODE_LCTRL | K_SCANCODE_MASK, + K_LSHIFT => SCANCODE_LSHIFT | K_SCANCODE_MASK, + K_LALT => SCANCODE_LALT | K_SCANCODE_MASK, + K_LGUI => SCANCODE_LGUI | K_SCANCODE_MASK, + K_RCTRL => SCANCODE_RCTRL | K_SCANCODE_MASK, + K_RSHIFT => SCANCODE_RSHIFT | K_SCANCODE_MASK, + K_RALT => SCANCODE_RALT | K_SCANCODE_MASK, + K_RGUI => SCANCODE_RGUI | K_SCANCODE_MASK, + + K_MODE => SCANCODE_MODE | K_SCANCODE_MASK, + + K_AUDIONEXT => SCANCODE_AUDIONEXT | K_SCANCODE_MASK, + K_AUDIOPREV => SCANCODE_AUDIOPREV | K_SCANCODE_MASK, + K_AUDIOSTOP => SCANCODE_AUDIOSTOP | K_SCANCODE_MASK, + K_AUDIOPLAY => SCANCODE_AUDIOPLAY | K_SCANCODE_MASK, + K_AUDIOMUTE => SCANCODE_AUDIOMUTE | K_SCANCODE_MASK, + K_MEDIASELECT => SCANCODE_MEDIASELECT | K_SCANCODE_MASK, + K_WWW => SCANCODE_WWW | K_SCANCODE_MASK, + K_MAIL => SCANCODE_MAIL | K_SCANCODE_MASK, + K_CALCULATOR => SCANCODE_CALCULATOR | K_SCANCODE_MASK, + K_COMPUTER => SCANCODE_COMPUTER | K_SCANCODE_MASK, + K_AC_SEARCH => SCANCODE_AC_SEARCH | K_SCANCODE_MASK, + K_AC_HOME => SCANCODE_AC_HOME | K_SCANCODE_MASK, + K_AC_BACK => SCANCODE_AC_BACK | K_SCANCODE_MASK, + K_AC_FORWARD => SCANCODE_AC_FORWARD | K_SCANCODE_MASK, + K_AC_STOP => SCANCODE_AC_STOP | K_SCANCODE_MASK, + K_AC_REFRESH => SCANCODE_AC_REFRESH | K_SCANCODE_MASK, + K_AC_BOOKMARKS => SCANCODE_AC_BOOKMARKS | K_SCANCODE_MASK, + + K_BRIGHTNESSDOWN => SCANCODE_BRIGHTNESSDOWN | K_SCANCODE_MASK, + K_BRIGHTNESSUP => SCANCODE_BRIGHTNESSUP | K_SCANCODE_MASK, + K_DISPLAYSWITCH => SCANCODE_DISPLAYSWITCH | K_SCANCODE_MASK, + K_KBDILLUMTOGGLE => SCANCODE_KBDILLUMTOGGLE | K_SCANCODE_MASK, + K_KBDILLUMDOWN => SCANCODE_KBDILLUMDOWN | K_SCANCODE_MASK, + K_KBDILLUMUP => SCANCODE_KBDILLUMUP | K_SCANCODE_MASK, + K_EJECT => SCANCODE_EJECT | K_SCANCODE_MASK, + K_SLEEP => SCANCODE_SLEEP | K_SCANCODE_MASK, + K_APP1 => SCANCODE_APP1 | K_SCANCODE_MASK, + K_APP2 => SCANCODE_APP2 | K_SCANCODE_MASK, + + K_AUDIOREWIND => SCANCODE_AUDIOREWIND | K_SCANCODE_MASK, + K_AUDIOFASTFORWARD => SCANCODE_AUDIOFASTFORWARD | K_SCANCODE_MASK, + }, + PixelFormatEnum => { + PIXELFORMAT_UNKNOWN => 0, + PIXELFORMAT_INDEX1LSB => pixel_format( PIXELTYPE_INDEX1, BITMAPORDER_4321, 0, 1, 0 ), + PIXELFORMAT_INDEX1MSB => pixel_format( PIXELTYPE_INDEX1, BITMAPORDER_1234, 0, 1, 0 ), + PIXELFORMAT_INDEX4LSB => pixel_format( PIXELTYPE_INDEX4, BITMAPORDER_4321, 0, 4, 0 ), + PIXELFORMAT_INDEX4MSB => pixel_format( PIXELTYPE_INDEX4, BITMAPORDER_1234, 0, 4, 0 ), + PIXELFORMAT_INDEX8 => pixel_format( PIXELTYPE_INDEX8, 0, 0, 8, 1 ), + PIXELFORMAT_RGB332 => pixel_format( PIXELTYPE_PACKED8, PACKEDORDER_XRGB, PACKEDLAYOUT_332, 8, 1 ), + PIXELFORMAT_XRGB4444 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_XRGB, PACKEDLAYOUT_4444, 12, 2 ), + PIXELFORMAT_XBGR4444 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_XBGR, PACKEDLAYOUT_4444, 12, 2 ), + PIXELFORMAT_XRGB1555 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_XRGB, PACKEDLAYOUT_1555, 15, 2 ), + PIXELFORMAT_XBGR1555 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_XBGR, PACKEDLAYOUT_1555, 15, 2 ), + PIXELFORMAT_ARGB4444 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_ARGB, PACKEDLAYOUT_4444, 16, 2 ), + PIXELFORMAT_RGBA4444 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_RGBA, PACKEDLAYOUT_4444, 16, 2 ), + PIXELFORMAT_ABGR4444 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_ABGR, PACKEDLAYOUT_4444, 16, 2 ), + PIXELFORMAT_BGRA4444 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_BGRA, PACKEDLAYOUT_4444, 16, 2 ), + PIXELFORMAT_ARGB1555 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_ARGB, PACKEDLAYOUT_1555, 16, 2 ), + PIXELFORMAT_RGBA5551 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_RGBA, PACKEDLAYOUT_5551, 16, 2 ), + PIXELFORMAT_ABGR1555 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_ABGR, PACKEDLAYOUT_1555, 16, 2 ), + PIXELFORMAT_BGRA5551 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_BGRA, PACKEDLAYOUT_5551, 16, 2 ), + PIXELFORMAT_RGB565 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_XRGB, PACKEDLAYOUT_565, 16, 2 ), + PIXELFORMAT_BGR565 => pixel_format( PIXELTYPE_PACKED16, PACKEDORDER_XBGR, PACKEDLAYOUT_565, 16, 2 ), + PIXELFORMAT_RGB24 => pixel_format( PIXELTYPE_ARRAYU8, ARRAYORDER_RGB, 0, 24, 3 ), + PIXELFORMAT_BGR24 => pixel_format( PIXELTYPE_ARRAYU8, ARRAYORDER_BGR, 0, 24, 3 ), + PIXELFORMAT_XRGB8888 => pixel_format( PIXELTYPE_PACKED32, PACKEDORDER_XRGB, PACKEDLAYOUT_8888, 24, 4 ), + PIXELFORMAT_RGBX8888 => pixel_format( PIXELTYPE_PACKED32, PACKEDORDER_RGBX, PACKEDLAYOUT_8888, 24, 4 ), + PIXELFORMAT_XBGR8888 => pixel_format( PIXELTYPE_PACKED32, PACKEDORDER_XBGR, PACKEDLAYOUT_8888, 24, 4 ), + PIXELFORMAT_BGRX8888 => pixel_format( PIXELTYPE_PACKED32, PACKEDORDER_BGRX, PACKEDLAYOUT_8888, 24, 4 ), + PIXELFORMAT_ARGB8888 => pixel_format( PIXELTYPE_PACKED32, PACKEDORDER_ARGB, PACKEDLAYOUT_8888, 32, 4 ), + PIXELFORMAT_RGBA8888 => pixel_format( PIXELTYPE_PACKED32, PACKEDORDER_RGBA, PACKEDLAYOUT_8888, 32, 4 ), + PIXELFORMAT_ABGR8888 => pixel_format( PIXELTYPE_PACKED32, PACKEDORDER_ABGR, PACKEDLAYOUT_8888, 32, 4 ), + PIXELFORMAT_BGRA8888 => pixel_format( PIXELTYPE_PACKED32, PACKEDORDER_BGRA, PACKEDLAYOUT_8888, 32, 4 ), + PIXELFORMAT_ARGB2101010 => pixel_format( PIXELTYPE_PACKED32, PACKEDORDER_ARGB, PACKEDLAYOUT_2101010, 32, 4 ), + # Continued below + }, + ); +} + +BEGIN { + enum( + PixelFormatEnum => { + # Continued from above + PIXELFORMAT_RGB444 => PIXELFORMAT_XRGB4444, + PIXELFORMAT_BGR444 => PIXELFORMAT_XBGR4444, + PIXELFORMAT_RGB555 => PIXELFORMAT_XRGB1555, + PIXELFORMAT_BGR555 => PIXELFORMAT_XBGR1555, + PIXELFORMAT_RGB888 => PIXELFORMAT_XRGB8888, + PIXELFORMAT_BGR888 => PIXELFORMAT_XBGR8888, + # Continued below + } + ); +} + +BEGIN { + my sub define_fourcc { ord(shift) << 0 | ord(shift) << 8 | ord(shift) << 16 | ord(shift) << 24 } + + enum( + PixelFormatEnum => { + # Continued from above + ( BYTEORDER == BIG_ENDIAN ) ? ( + PIXELFORMAT_RGBA32 => PIXELFORMAT_RGBA8888, + PIXELFORMAT_ARGB32 => PIXELFORMAT_ARGB8888, + PIXELFORMAT_BGRA32 => PIXELFORMAT_BGRA8888, + PIXELFORMAT_ABGR32 => PIXELFORMAT_ABGR8888, + ) : ( + PIXELFORMAT_RGBA32 => PIXELFORMAT_ABGR8888, + PIXELFORMAT_ARGB32 => PIXELFORMAT_BGRA8888, + PIXELFORMAT_BGRA32 => PIXELFORMAT_ARGB8888, + PIXELFORMAT_ABGR32 => PIXELFORMAT_RGBA8888, + ), + PIXELFORMAT_YV12 => define_fourcc( 'Y', 'V', '1', '2' ), + PIXELFORMAT_IYUV => define_fourcc( 'I', 'Y', 'U', 'V' ), + PIXELFORMAT_YUY2 => define_fourcc( 'Y', 'U', 'Y', '2' ), + PIXELFORMAT_UYVY => define_fourcc( 'U', 'Y', 'V', 'Y' ), + PIXELFORMAT_YVYU => define_fourcc( 'Y', 'V', 'Y', 'U' ), + PIXELFORMAT_NV12 => define_fourcc( 'N', 'V', '1', '2' ), + PIXELFORMAT_NV21 => define_fourcc( 'N', 'V', '2', '1' ), + PIXELFORMAT_EXTERNAL_OES => define_fourcc( 'O', 'E', 'S', ' ' ), + } + ); +} + +# Init flags +use constant { + INIT_TIMER => 0x000001, + INIT_AUDIO => 0x000010, + INIT_VIDEO => 0x000020, + INIT_JOYSTICK => 0x000200, + INIT_HAPTIC => 0x001000, + INIT_GAMECONTROLLER => 0x002000, + INIT_EVENTS => 0x004000, + INIT_SENSOR => 0x008000, + INIT_NOPARACHUTE => 0x100000, +}; + +use constant INIT_EVERYTHING => ( + INIT_TIMER | INIT_AUDIO | INIT_VIDEO | INIT_EVENTS | + INIT_JOYSTICK | INIT_HAPTIC | INIT_GAMECONTROLLER | INIT_SENSOR +); + +# Window flags +use constant { + WINDOWPOS_UNDEFINED => 0x1FFF0000, + WINDOWPOS_CENTERED => 0x2FFF0000, +}; + +# Mouse buttons +use constant { + BUTTON_LEFT => 1, + BUTTON_MIDDLE => 2, + BUTTON_RIGHT => 3, + BUTTON_X1 => 4, + BUTTON_X2 => 5, +}; + + +$ffi->type( sint32 => 'SDL_BlendMode' ); +$ffi->type( sint32 => 'SDL_JoystickID' ); +$ffi->mangler( sub { 'SDL_' . shift } ); + +package SDL2::WindowEvent { + FFI::C->struct( SDL_WindowEvent => [ + type => 'uint32', + timestamp => 'uint32', + windowID => 'uint32', + event => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + padding3 => 'uint8', + data1 => 'sint32', + data2 => 'sint32', + ]); +} + +package SDL2::KeyboardEvent { + FFI::C->struct( SDL_KeyboardEvent => [ + type => 'uint32', + timestamp => 'uint32', + windowID => 'uint32', + state => 'uint8', + repeat => 'uint8', + scancode => 'sint32', + sym => 'sint32', + mod => 'uint16', + ]); +} + +package SDL2::MouseButtonEvent { + FFI::C->struct( SDL_MouseButtonEvent => [ + type => 'uint32', + timestamp => 'uint32', + windowID => 'uint32', + which => 'uint32', + button => 'uint8', + state => 'uint8', + clicks => 'uint8', + padding1 => 'uint8', + x => 'sint32', + y => 'sint32', + ]); +} + +package SDL2::MouseMotionEvent { + FFI::C->struct( SDL_MouseMotionEvent => [ + type => 'uint32', + timestamp => 'uint32', + windowID => 'uint32', + which => 'uint32', + state => 'uint32', + x => 'sint32', + y => 'sint32', + xrel => 'sint32', + yrel => 'sint32', + ]); +} + +package SDL2::MouseWheelEvent { + FFI::C->struct( SDL_MouseWheelEvent => [ + type => 'uint32', + timestamp => 'uint32', + windowID => 'uint32', + which => 'uint32', + x => 'sint32', + y => 'sint32', + direction => 'uint32', + ]); +} + +package SDL2::ControllerButtonEvent { + FFI::C->struct( SDL_ControllerButtonEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + button => 'uint8', + state => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + ]); +} + +package SDL2::ControllerDeviceEvent { + FFI::C->struct( SDL_ControllerDeviceEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + ]); +} + +package SDL2::ControllerAxisEvent { + FFI::C->struct( SDL_ControllerAxisEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + axis => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + padding3 => 'uint8', + value => 'sint16', + padding4 => 'uint16', + ]); +} + +package SDL2::TextEditingEvent { + FFI::C->struct( SDL_TextEditingEvent => [ + type => 'uint32', + timestamp => 'uint32', + windowID => 'uint32', + _text => 'string(32)', + start => 'sint32', + length => 'sint32', + ]); + + sub text { my $data = shift->_text; substr $data, 0, index $data, "\0" } +} + +package SDL2::TextInputEvent { + FFI::C->struct( SDL_TextInputEvent => [ + type => 'uint32', + timestamp => 'uint32', + windowID => 'uint32', + _text => 'string(32)', + ]); + + sub text { my $data = shift->_text; substr $data, 0, index $data, "\0" } +} + +package SDL2::DropEvent { + FFI::C->struct( SDL_DropEvent => [ + type => 'uint32', + timestamp => 'uint32', + _file => 'opaque', + windowID => 'uint32', + ]); + + sub file { $ffi->cast( opaque => string => shift->_file ) } +} + +package SDL2::UserEvent { + FFI::C->struct( SDL_UserEvent => [ + type => 'uint32', + timestamp => 'uint32', + windowID => 'uint32', + code => 'sint32', + data1 => 'opaque', + data2 => 'opaque', + ]); + + sub file { $ffi->cast( opaque => string => shift->_file ) } +} + +package SDL2::Event { + FFI::C->union( SDL_Event => [ + type => 'uint32', + timestamp => 'uint32', + + button => 'SDL_MouseButtonEvent', + caxis => 'SDL_ControllerAxisEvent', + cbutton => 'SDL_ControllerButtonEvent', + cdevice => 'SDL_ControllerDeviceEvent', + drop => 'SDL_DropEvent', + edit => 'SDL_TextEditingEvent', + key => 'SDL_KeyboardEvent', + motion => 'SDL_MouseMotionEvent', + text => 'SDL_TextInputEvent', + wheel => 'SDL_MouseWheelEvent', + window => 'SDL_WindowEvent', + user => 'SDL_UserEvent', + + padding => 'uint8[56]', + ]); +} + +package SDL2::PixelFormat { + FFI::C->struct( SDL_PixelFormat => [ + format => 'uint32', + palette => 'opaque', + BitsPerPixel => 'uint8', + BytesPerPixel => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + Rmask => 'uint32', + Gmask => 'uint32', + Bmask => 'uint32', + Amask => 'uint32', + Rloss => 'uint8', + Gloss => 'uint8', + Bloss => 'uint8', + Aloss => 'uint8', + Rshift => 'uint8', + Gshift => 'uint8', + Bshift => 'uint8', + Ashift => 'uint8', + refcount => 'int', + _next => 'opaque', + ]); + + sub next { $ffi->cast( opaque => 'SDL_PixelFormat', shift ) } +} + +package SDL2::Surface { + FFI::C->struct( SDL_Surface => [ + flags => 'uint32', + format => 'opaque', + w => 'int', + h => 'int', + pitch => 'int', + ]); +} + +package SDL2::Rect { + use FFI::Platypus::Record; + record_layout_1( int => 'x', int => 'y', int => 'w', int => 'h' ); +} + +package SDL2::Point { + use FFI::Platypus::Record; + record_layout_1( int => 'x', int => 'y' ); +} + +package SDL2::RendererInfo { + FFI::C->struct( SDL_RendererInfo => [ + '_name' => 'opaque', + flags => 'uint32', + 'num_texture_formats' => 'uint32', + 'texture_formats' => 'uint32[16]', + 'max_texture_width' => 'int', + 'max_texture_height' => 'int', + ]); + + sub name { $ffi->cast( opaque => string => shift->_name ) } +} + +package SDL2::Version { + FFI::C->struct( SDL_Version => [ + major => 'uint8', + minor => 'uint8', + patch => 'uint8', + ]); +} + +package SDL2::SysWMinfo { + FFI::C->struct( SDL_SysWMinfo => [ + version => 'SDL_Version', + subsystem => 'int', + ]); +} + +$ffi->type( 'record(SDL2::Rect)' => 'SDL_Rect' ); +$ffi->type( 'record(SDL2::Point)' => 'SDL_Point' ); + +$ffi->type( opaque => 'SDL_Renderer' ); +$ffi->type( opaque => 'SDL_Texture' ); +$ffi->type( opaque => 'SDL_Window' ); +$ffi->type( opaque => 'SDL_GameController' ); +$ffi->type( opaque => 'SDL_Joystick' ); +$ffi->type( opaque => 'SDL_RWops' ); + +## Video + +# SDL_BlendMode +$ffi->attach( CreateWindow => [qw( string sint32 sint32 sint32 sint32 sint32 )] => 'SDL_Window' ); +# SDL_CreateWindowAndRenderer +# SDL_CreateWindowFrom +$ffi->attach( DestroyWindow => ['SDL_Window' ] => 'void' ); +# SDL_DisableScreenSaver +# SDL_DisplayMode +# SDL_EnableScreenSaver +# SDL_GetClosestDisplayMode +# SDL_GetCurrentDisplayMode +# SDL_GetCurrentVideoDriver +# SDL_GetDesktopDisplayMode +# SDL_GetDisplayBounds +# SDL_GetDisplayDPI +# SDL_GetDisplayMode +# SDL_GetDisplayName +# SDL_GetDisplayUsableBounds +# SDL_GetGrabbedWindow +# SDL_GetNumDisplayModes +# SDL_GetNumVideoDisplays +# SDL_GetNumVideoDrivers +# SDL_GetVideoDriver +# SDL_GetWindowBordersSize +# SDL_GetWindowBrightness +# SDL_GetWindowData +# SDL_GetWindowDisplayIndex +# SDL_GetWindowDisplayMode +# SDL_GetWindowFlags +# SDL_GetWindowFromID +# SDL_GetWindowGammaRamp +# SDL_GetWindowGrab +# SDL_GetWindowID +# SDL_GetWindowMaximumSize +# SDL_GetWindowMinimumSize +# SDL_GetWindowOpacity +# SDL_GetWindowPixelFormat +# SDL_GetWindowPosition +# SDL_GetWindowSize +$ffi->attach( GetWindowSurface => ['SDL_Window'] => 'SDL_Surface' ); +# SDL_GetWindowTitle +$ffi->attach( GetWindowWMInfo => ['SDL_Window', 'SDL_SysWMinfo'] => 'int' ); +# SDL_GL_CreateContext +# SDL_GL_DeleteContext +# SDL_GL_ExtensionSupported +# SDL_GL_GetAttribute +# SDL_GL_GetCurrentContext +# SDL_GL_GetCurrentWindow +# SDL_GL_GetDrawableSize +# SDL_GL_GetProcAddress +# SDL_GL_GetSwapInterval +# SDL_GL_LoadLibrary +# SDL_GL_MakeCurrent +# SDL_GL_ResetAttributes +# SDL_GL_SetAttribute +# SDL_GL_SetSwapInterval +# SDL_GL_SwapWindow +# SDL_GL_UnloadLibrary +# SDL_GLattr +# SDL_GLcontextFlag +# SDL_GLprofile +# SDL_HideWindow +# SDL_HitTestResult +# SDL_IsScreenSaverEnabled +# SDL_MaximizeWindow +# SDL_MessageBoxButtonData +# SDL_MessageBoxButtonFlags +# SDL_MessageBoxColor +# SDL_MessageBoxColorScheme +# SDL_MessageBoxColorType +# SDL_MessageBoxData +# SDL_MessageBoxFlags +# SDL_MinimizeWindow +# SDL_RaiseWindow +# SDL_RestoreWindow +# SDL_SetWindowBordered +# SDL_SetWindowBrightness +# SDL_SetWindowData +# SDL_SetWindowDisplayMode +# SDL_SetWindowFullscreen +# SDL_SetWindowGammaRamp +# SDL_SetWindowGrab +# SDL_SetWindowHitTest +$ffi->attach( SetWindowIcon => ['SDL_Window', 'SDL_Surface'] => 'void' ); +# SDL_SetWindowInputFocus +# SDL_SetWindowMaximumSize +# SDL_SetWindowMinimumSize +# SDL_SetWindowModalFor +# SDL_SetWindowOpacity +# SDL_SetWindowPosition +# SDL_SetWindowResizable +# SDL_SetWindowSize +$ffi->attach( SetWindowTitle => ['SDL_Window', 'string'] => 'void' ); +# SDL_ShowMessageBox +# SDL_ShowSimpleMessageBox +# SDL_ShowWindow +$ffi->attach( UpdateWindowSurface => ['SDL_Window' ] => 'int' ); +# SDL_UpdateWindowSurfaceRects +# SDL_VideoInit +# SDL_VideoQuit +# SDL_WindowEvent +# SDL_WindowEventID +# SDL_WindowFlags + +## SDL + +$ffi->attach( Init => ['uint32'] => 'int' ); +$ffi->attach( InitSubSystem => ['uint32'] => 'int' ); +$ffi->attach( Quit => [ ] => 'void' ); +$ffi->attach( QuitSubSystem => ['uint32'] => 'void' ); +$ffi->attach( WasInit => ['uint32'] => 'int' ); +# SDL_SetMainReady +# SDL_WinRTRunApp + +## Timer + +# SDL_AddTimer +$ffi->attach( Delay => ['uint32'] => 'void' ); +# SDL_GetPerformanceCounter +# SDL_GetPerformanceFrequency +$ffi->attach( GetTicks => [ ] => 'uint32' ); +# SDL_RemoveTimer +# SDL_TICKS_PASSED + +## Error + +$ffi->attach( GetError => [ ] => 'string' ); +$ffi->attach( ClearError => [ ] => 'void' ); +$ffi->attach( SetError => ['string'] => 'void' => sub { shift->( sprintf shift, @_ ) }); + +## Render + +# SDL_BlendFactor +# SDL_BlendOperation +# SDL_ComposeCustomBlendMode +$ffi->attach( CreateRenderer => [qw( SDL_Window sint32 sint32 )] => 'SDL_Renderer' ); +# SDL_CreateSoftwareRenderer +$ffi->attach( CreateTexture => [qw( SDL_Renderer uint32 sint32 sint32 sint32 )] => 'SDL_Texture' ); +$ffi->attach( CreateTextureFromSurface => [qw( SDL_Renderer SDL_Surface )] => 'SDL_Texture' ); +# SDL_CreateWindowAndRenderer +$ffi->attach( DestroyRenderer => [qw( SDL_Renderer )] => 'void' ); +$ffi->attach( DestroyTexture => [qw( SDL_Texture )] => 'void' ); +# SDL_GetNumRenderDrivers +# SDL_GetRenderDrawBlendMode +# SDL_GetRenderDrawColor +# SDL_GetRenderDriverInfo +# SDL_GetRenderer +$ffi->attach( GetRendererInfo => [qw( SDL_Renderer SDL_RendererInfo )] => 'int' ); +# SDL_GetRendererOutputSize +$ffi->attach( GetRenderTarget => [qw( SDL_Renderer )] => 'SDL_Texture' ); +$ffi->attach( GetTextureAlphaMod => [qw( SDL_Texture uint8* )] => 'int' ); +$ffi->attach( GetTextureBlendMode => [qw( SDL_Texture SDL_BlendMode )] => 'int' ); +$ffi->attach( GetTextureColorMod => [qw( SDL_Texture uint8* uint8* uint8* )] => 'int' ); +# SDL_GL_BindTexture +# SDL_GL_UnbindTexture +$ffi->attach( LockTexture => [qw( SDL_Texture SDL_Rect* opaque* int* )] => 'int' ); +# SDL_QueryTexture +$ffi->attach( RenderClear => [qw( SDL_Renderer )] => 'int' ); +$ffi->attach( RenderCopy => [qw( SDL_Renderer SDL_Texture SDL_Rect* SDL_Rect* )] => 'int' ); +$ffi->attach( RenderCopyEx => [qw( SDL_Renderer SDL_Texture SDL_Rect* SDL_Rect* double SDL_Rect* SDL_Rect* )] => 'int' ); +# SDL_RenderCopyExF +# SDL_RenderCopyF +# SDL_RenderDrawLine +# SDL_RenderDrawLineF +# SDL_RenderDrawLines +# SDL_RenderDrawLinesF +# SDL_RenderDrawPoint +# SDL_RenderDrawPointF +$ffi->attach( RenderDrawPoints => [qw( SDL_Renderer int[] int )] => 'int' ); +# SDL_RenderDrawPointsF +# SDL_RenderDrawRect +# SDL_RenderDrawRectF +# SDL_RenderDrawRects +# SDL_RenderDrawRectsF +# SDL_Renderer +# SDL_RendererFlags +# SDL_RendererFlip +# SDL_RendererInfo +# SDL_RenderFillRect +# SDL_RenderFillRectF +# SDL_RenderFillRects +# SDL_RenderFillRectsF +# SDL_RenderGetClipRect +# SDL_RenderGetIntegerScale +# SDL_RenderGetLogicalSize +# SDL_RenderGetScale +# SDL_RenderGetViewport +# SDL_RenderIsClipEnabled +$ffi->attach( RenderPresent => ['SDL_Renderer'] => 'void' ); +# SDL_RenderReadPixels +# SDL_RenderSetClipRect +# SDL_RenderSetIntegerScale +# SDL_RenderSetLogicalSize +# SDL_RenderSetScale +# SDL_RenderSetViewport +# SDL_RenderTargetSupported +# SDL_SetRenderDrawBlendMode +$ffi->attach( SetRenderDrawColor => [qw( SDL_Renderer uint8 uint8 uint8 uint8 )] => 'int' ); +$ffi->attach( SetRenderTarget => [qw( SDL_Renderer SDL_Texture )] => 'int' ); +$ffi->attach( SetTextureAlphaMod => [qw( SDL_Texture uint8 )] => 'int' ); +$ffi->attach( SetTextureBlendMode => [qw( SDL_Texture SDL_BlendMode )] => 'int' ); +$ffi->attach( SetTextureColorMod => [qw( SDL_Texture uint8 uint8 uint8 )] => 'int' ); +# SDL_Texture +# SDL_TextureAccess +# SDL_TextureModulate +$ffi->attach( UnlockTexture => ['SDL_Texture'] => 'void' ); +# SDL_UpdateTexture +# SDL_UpdateYUVTexture + +## Surface + +$ffi->attach( UpperBlit => [qw( SDL_Surface SDL_Rect* SDL_Surface SDL_Rect* )] => 'int' ); +# SDL_BlitScaled +sub BlitSurface { goto &UpperBlit } +# SDL_ConvertPixels +# SDL_ConvertSurface +# SDL_ConvertSurfaceFormat +# SDL_CreateRGBSurface +$ffi->attach( CreateRGBSurfaceFrom => [qw( opaque int int int int uint32 uint32 uint32 uint32 )] => 'SDL_Surface' ); +# SDL_CreateRGBSurfaceWithFormat +# SDL_CreateRGBSurfaceWithFormatFrom +# SDL_FillRect +# SDL_FillRects +$ffi->attach( FreeSurface => ['SDL_Surface'] => 'void' ); +# SDL_GetClipRect +# SDL_GetColorKey +# SDL_GetSurfaceAlphaMod +# SDL_GetSurfaceBlendMode +# SDL_GetSurfaceColorMod +$ffi->attach( RWFromFile => [qw( string string )] => 'SDL_RWops' ); +$ffi->attach( LoadBMP_RW => [qw( SDL_RWops int )] => 'SDL_Surface' ); +sub LoadBMP { LoadBMP_RW( RWFromFile( +shift, 'rb' ), 1 ) } +# SDL_LockSurface +# SDL_LowerBlit +# SDL_LowerBlitScaled +# SDL_MUSTLOCK +# SDL_SaveBMP +# SDL_SaveBMP_RW +# SDL_SetClipRect +$ffi->attach( SetColorKey => [qw( SDL_Surface int uint32 )] => 'int' ); +# SDL_SetSurfaceAlphaMod +# SDL_SetSurfaceBlendMode +# SDL_SetSurfaceColorMod +# SDL_SetSurfacePalette +# SDL_SetSurfaceRLE +# SDL_UnlockSurface + +## GameController + +# SDL_GameControllerAddMapping +# SDL_GameControllerAddMappingsFromFile +# SDL_GameControllerAddMappingsFromRW +# SDL_GameControllerAxis +# SDL_GameControllerButton +$ffi->attach( GameControllerClose => ['SDL_GameController'] => 'void' ); +# SDL_GameControllerEventState +# SDL_GameControllerFromInstanceID +# SDL_GameControllerGetAttached +# SDL_GameControllerGetAxis +# SDL_GameControllerGetAxisFromString +# SDL_GameControllerGetBindForAxis +# SDL_GameControllerGetBindForButton +# SDL_GameControllerGetButton +# SDL_GameControllerGetButtonFromString +$ffi->attach( GameControllerGetJoystick => ['SDL_GameController'] => 'SDL_Joystick' ); +# SDL_GameControllerGetStringForAxis +# SDL_GameControllerGetStringForButton +# SDL_GameControllerMapping +# SDL_GameControllerMappingForGUID +# SDL_GameControllerName +$ffi->attach( GameControllerNameForIndex => ['int' ] => 'string' ); +$ffi->attach( GameControllerOpen => ['int' ] => 'SDL_GameController' ); +# SDL_GameControllerUpdate +$ffi->attach( IsGameController => ['int' ] => 'bool' ); + +## Joystick + +# SDL_JoystickClose +# SDL_JoystickCurrentPowerLevel +# SDL_JoystickEventState +# SDL_JoystickFromInstanceID +# SDL_JoystickGetAttached +# SDL_JoystickGetAxis +# SDL_JoystickGetBall +# SDL_JoystickGetButton +# SDL_JoystickGetDeviceGUID +# SDL_JoystickGetGUID +# SDL_JoystickGetGUIDFromString +# SDL_JoystickGetGUIDString +# SDL_JoystickGetHat +$ffi->attach( JoystickInstanceID => ['SDL_Joystick'] => 'SDL_JoystickID' ); +# SDL_JoystickName +# SDL_JoystickNameForIndex +# SDL_JoystickNumAxes +# SDL_JoystickNumBalls +# SDL_JoystickNumButtons +# SDL_JoystickNumHats +# SDL_JoystickOpen +# SDL_JoystickPowerLevel +# SDL_JoystickUpdate +# SDL_NumJoysticks + +## Events + +# SDL_AddEventWatch +# SDL_AudioDeviceEvent +# SDL_ControllerAxisEvent +# SDL_ControllerButtonEvent +# SDL_ControllerDeviceEvent +# SDL_DelEventWatch +# SDL_DollarGestureEvent +# SDL_DropEvent +# SDL_Event +# SDL_EventState +# SDL_EventType +# SDL_FilterEvents +# SDL_Finger +# SDL_FlushEvent +# SDL_FlushEvents +# SDL_GetEventFilter +# SDL_GetEventState +# SDL_GetNumTouchDevices +# SDL_GetNumTouchFingers +# SDL_GetTouchDevice +# SDL_GetTouchFinger +# SDL_HasEvent +# SDL_HasEvents +# SDL_JoyAxisEvent +# SDL_JoyBallEvent +# SDL_JoyButtonEvent +# SDL_JoyDeviceEvent +# SDL_JoyHatEvent +# SDL_KeyboardEvent +# SDL_LoadDollarTemplates +# SDL_MouseButtonEvent +# SDL_MouseMotionEvent +# SDL_MouseWheelEvent +# SDL_MultiGestureEvent +# SDL_PeepEvents +$ffi->attach( PollEvent => ['SDL_Event'] => 'int' ); +# SDL_PumpEvents +$ffi->attach( PushEvent => ['SDL_Event'] => 'int' ); +# SDL_QuitEvent +# SDL_QuitRequested +# SDL_RecordGesture +$ffi->attach( RegisterEvents => ['uint32'] => 'int' ); +# SDL_SaveAllDollarTemplates +# SDL_SaveDollarTemplate +# SDL_SensorEvent +# SDL_SetEventFilter +# SDL_SysWMEvent +# SDL_TextEditingEvent +# SDL_TextInputEvent +# SDL_TouchFingerEvent +# SDL_UserEvent +# SDL_WaitEvent +# SDL_WaitEventTimeout +# SDL_WindowEvent +# SDL_WindowEventID + +## Version + +# SDL_GetRevision +# SDL_GetRevisionNumber +$ffi->attach( GetVersion => ['SDL_Version'] => 'void' ); + +# Clean helper functions +delete $SDL2::{$_} for qw( enum pixel_format ); + +1; diff --git a/lib/SDL2/Raw/Image.pm b/lib/SDL2/Raw/Image.pm new file mode 100644 index 0000000..29c7099 --- /dev/null +++ b/lib/SDL2/Raw/Image.pm @@ -0,0 +1,43 @@ +package SDL2::Image; + +use strict; +use warnings; + +use SDL2::Raw; +use FFI::CheckLib; +use FFI::Platypus 1.00; + +use constant { + INIT_JPG => 0x1, + INIT_PNG => 0x2, + INIT_TIF => 0x4, + INIT_WEBP => 0x8, +}; + +my $ffi = FFI::Platypus->new( api => 1 ); +$ffi->lib( find_lib_or_exit lib => 'SDL2_image' ); + +for my $name (qw( Renderer Texture )) { + my $class = "SDL2::$name"; + $ffi->custom_type( "SDL_$name" => { + native_type => 'opaque', + native_to_perl => sub { + ! defined $_[0] ? undef : bless \$_[0], $class; + }, + }); +} + +$ffi->custom_type( 'SDL_Surface' => { + native_type => 'opaque', + native_to_perl => sub { + # This is a bit sketch + ! defined $_[0] ? undef : bless { ptr => $_[0] }, 'SDL2::Surface'; + }, +}); + +$ffi->attach( [ 'SDL_GetError' => 'GetError' ] => [ ] => 'string' ); +$ffi->attach( [ 'IMG_Init' => 'Init' ] => ['int' ] => 'int' ); +$ffi->attach( [ 'IMG_Load' => 'Load' ] => ['string' ] => 'SDL_Surface' ); +$ffi->attach( [ 'IMG_LoadTexture' => 'LoadTexture' ] => ['SDL_Renderer', 'string'] => 'SDL_Texture' ); + +1; diff --git a/lib/SDL2/Raw/Mixer.pm b/lib/SDL2/Raw/Mixer.pm new file mode 100644 index 0000000..323b579 --- /dev/null +++ b/lib/SDL2/Raw/Mixer.pm @@ -0,0 +1,74 @@ +package SDL2::Mixer; + +use strict; +use warnings; + +use SDL2::Raw; +use FFI::CheckLib; +use FFI::Platypus 1.00; +use Ref::Util; + +use constant { + INIT_JPG => 0x1, + INIT_PNG => 0x2, + INIT_TIF => 0x4, + INIT_WEBP => 0x8, + + NO_FADING => 0, + FADING_OUT => 1, + FADING_IN => 2, +}; + +my $ffi = FFI::Platypus->new( api => 1 ); +$ffi->lib( find_lib_or_exit lib => 'SDL_mixer' ); + +$ffi->mangler( sub { 'Mix_' . shift } ); +$ffi->type( opaque => 'Mix_Chunk' ); + +## General + +$ffi->attach( Init => ['uint32'] => 'int' ); +$ffi->attach( Quit => [] => 'void' ); +$ffi->attach( OpenAudio => ['int', 'uint16', 'int', 'int'] => 'int' ); +$ffi->attach( CloseAudio => [] => 'void' ); +$ffi->attach( QuerySpec => ['int*', 'uint16*', 'int*'] => 'int' ); + +sub SetError { goto &SDL2::SetError } +sub GetError { goto &SDL2::GetError } + +## Samples + +$ffi->attach( GetNumChunkDecoders => [ ] => 'int' ); +$ffi->attach( GetChunkDecoder => ['int' ] => 'string' ); +$ffi->attach( LoadWAV_RW => ['opaque', 'int' ] => 'Mix_Chunk' ); +$ffi->attach( QuickLoad_WAV => ['uint8*' ] => 'Mix_Chunk' ); +$ffi->attach( QuickLoad_RAW => ['uint8*' ] => 'Mix_Chunk' ); +$ffi->attach( VolumeChunk => ['Mix_Chunk', 'int'] => 'Mix_Chunk' ); +$ffi->attach( FreeChunk => ['Mix_Chunk' ] => 'void' ); +sub LoadWav { LoadWAV_RW( SDL2::RWFromFile( shift, 'rb' ), 1 ) } + +## Channels + +$ffi->attach( AllocateChannels => ['int' ] => 'int' ); +$ffi->attach( Volume => ['int', 'int' ] => 'int' ); +$ffi->attach( PlayChannelTimed => ['int', 'Mix_Chunk', 'int', 'int' ] => 'int' ); +$ffi->attach( FadeInChannelTimed => ['int', 'Mix_Chunk', 'int', 'int', 'int'] => 'int' ); +$ffi->attach( Pause => ['int' ] => 'void' ); +$ffi->attach( Resume => ['int' ] => 'void' ); +$ffi->attach( ExpireChannel => ['int', 'int' ] => 'int' ); +$ffi->attach( FadeOutChannel => ['int', 'int' ] => 'int' ); +$ffi->attach( Playing => ['int' ] => 'int' ); +$ffi->attach( Paused => ['int' ] => 'int' ); +$ffi->attach( FadingChannel => ['int' ] => 'int' ); +$ffi->attach( GetChunk => ['int' ] => 'Mix_Chunk' ); +$ffi->attach( ChannelFinished => ['(int)->void' ] => 'void' => sub { + my ( $xsub, $closure ) = @_; + $closure = $ffi->closure($closure) if Ref::Util::is_subref $closure; + $xsub->($closure); +}); +sub PlayChannel { PlayChannelTimed( @_, -1 ) } +sub FadeInChannel { FadeInChannelTimed( @_, -1 ) } + +## Groups + +1; diff --git a/share/hello.bmp b/share/hello.bmp new file mode 100644 index 0000000000000000000000000000000000000000..942b271a64c9d15a865eafd4536f5f7c9d40224b GIT binary patch literal 1089334 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 i*|S|gev)BW@6v(9R&pi1*Iw|C{h#^ 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�Ur#*B{kAyx3EL4>$EDf}jMNv_B4S^I&O|kU_ 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 AE&u=k literal 0 HcmV?d00001 diff --git a/t/wminfo.t b/t/wminfo.t new file mode 100644 index 0000000..7448811 --- /dev/null +++ b/t/wminfo.t @@ -0,0 +1,42 @@ +#!/usr/bin/env perl + +use Test2::V0; +use SDL2::Raw; +use version; + +SDL2::Init(0) and die 'Could not initialise SDL2: ' . SDL2::GetError; + +my $window = SDL2::CreateWindow('', 0, 0, 0, 0, SDL2::WINDOW_HIDDEN) + or die "Error creating SDL window: " . SDL2::GetError; + +END { + SDL2::DestroyWindow($window); + SDL2::Quit; +} + +my $info = SDL2::SysWMinfo->new; +SDL2::GetVersion( $info->version ); + +ok SDL2::GetWindowWMInfo( $window, $info ), 'GetWindowWMInfo'; + +my $v = $info->version; +my $version = eval { + version->parse( sprintf '%s.%s.%s', $v->major, $v->minor, $v->patch ); +} // $@; + +like $version, qr/^2\.\d+\.\d+$/a, + "Running on version $version"; + +my $system = ''; +for ( $info->subsystem ) { + $system = 'an unknown system!' if $_ == SDL2::SYSWM_UNKNOWN; + $system = 'Microsoft Windows(TM)' if $_ == SDL2::SYSWM_WINDOWS; + $system = 'X Window System' if $_ == SDL2::SYSWM_X11; + $system = 'DirectFB' if $_ == SDL2::SYSWM_DIRECTFB; + $system = 'Apple OS X' if $_ == SDL2::SYSWM_COCOA; + $system = 'UIKit' if $_ == SDL2::SYSWM_UIKIT; +} + +ok $system, "Running on $system"; + +done_testing; From 2503b0b08f2bb6555df693f5b75d17577028fd2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 21 Jun 2021 23:01:46 +0100 Subject: [PATCH 02/36] Add META and LICENSE --- LICENSE | 379 ++++++++++++++++++++++++++++++++++++ META.json | 563 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 942 insertions(+) create mode 100644 LICENSE create mode 100644 META.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7ce5566 --- /dev/null +++ b/LICENSE @@ -0,0 +1,379 @@ +This software is copyright (c) 2021 by José Joaquín Atria. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +Terms of the Perl programming language system itself + +a) the GNU General Public License as published by the Free + Software Foundation; either version 1, or (at your option) any + later version, or +b) the "Artistic License" + +--- The GNU General Public License, Version 1, February 1989 --- + +This software is Copyright (c) 2021 by José Joaquín Atria. + +This is free software, licensed under: + + The GNU General Public License, Version 1, February 1989 + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. 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 PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! + + +--- The Artistic License 1.0 --- + +This software is Copyright (c) 2021 by José Joaquín Atria. + +This is free software, licensed under: + + The Artistic License 1.0 + +The Artistic License + +Preamble + +The intent of this document is to state the conditions under which a Package +may be copied, such that the Copyright Holder maintains some semblance of +artistic control over the development of the package, while giving the users of +the package the right to use and distribute the Package in a more-or-less +customary fashion, plus the right to make reasonable modifications. + +Definitions: + + - "Package" refers to the collection of files distributed by the Copyright + Holder, and derivatives of that collection of files created through + textual modification. + - "Standard Version" refers to such a Package if it has not been modified, + or has been modified in accordance with the wishes of the Copyright + Holder. + - "Copyright Holder" is whoever is named in the copyright or copyrights for + the package. + - "You" is you, if you're thinking about copying or distributing this Package. + - "Reasonable copying fee" is whatever you can justify on the basis of media + cost, duplication charges, time of people involved, and so on. (You will + not be required to justify it to the Copyright Holder, but only to the + computing community at large as a market that must bear the fee.) + - "Freely Available" means that no fee is charged for the item itself, though + there may be fees involved in handling the item. It also means that + recipients of the item may redistribute it under the same conditions they + received it. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications derived +from the Public Domain or from the Copyright Holder. A Package modified in such +a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided that +you insert a prominent notice in each changed file stating how and when you +changed that file, and provided that you do at least ONE of the following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or an + equivalent medium, or placing the modifications on a major archive site + such as ftp.uu.net, or by allowing the Copyright Holder to include your + modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict with + standard executables, which must also be provided, and provide a separate + manual page for each non-standard executable that clearly documents how it + differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or executable +form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where to + get the Standard Version. + + b) accompany the distribution with the machine-readable source of the Package + with your modifications. + + c) accompany any non-standard executables with their corresponding Standard + Version executables, giving the non-standard executables non-standard + names, and clearly documenting the differences in manual pages (or + equivalent), together with instructions on where to get the Standard + Version. + + d) make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of this +Package. You may charge any fee you choose for support of this Package. You +may not charge a fee for this Package itself. However, you may distribute this +Package in aggregate with other (possibly commercial) programs as part of a +larger (possibly commercial) software distribution provided that you do not +advertise this Package as a product of your own. + +6. The scripts and library files supplied as input to or produced as output +from the programs of this Package do not automatically fall under the copyright +of this Package, but belong to whomever generated them, and may be sold +commercially, and may be aggregated with this Package. + +7. C or perl subroutines supplied by you and linked into this Package shall not +be considered part of this Package. + +8. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +The End + diff --git a/META.json b/META.json new file mode 100644 index 0000000..940014b --- /dev/null +++ b/META.json @@ -0,0 +1,563 @@ +{ + "abstract" : "FFI bindings for SDL2", + "author" : [ + "Jos\u00e9 Joaqu\u00edn Atria " + ], + "dynamic_config" : 0, + "generated_by" : "Dist::Zilla version 6.020, CPAN::Meta::Converter version 2.150010", + "license" : [ + "perl_5" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : 2 + }, + "name" : "SDL2-Raw", + "no_index" : { + "directory" : [ + "eg", + "examples", + "inc", + "share", + "t", + "xt" + ] + }, + "prereqs" : { + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : "0", + "File::ShareDir::Install" : "0.06", + "perl" : "5.008" + } + }, + "develop" : { + "requires" : { + "File::Spec" : "0", + "IO::Handle" : "0", + "IPC::Open3" : "0", + "Test::More" : "0", + "Test::Pod" : "1.41" + } + }, + "runtime" : { + "requires" : { + "perl" : "5.013010" + } + }, + "test" : { + "recommends" : { + "CPAN::Meta" : "2.120900" + }, + "requires" : { + "ExtUtils::MakeMaker" : "0", + "File::Spec" : "0", + "Test::More" : "0", + "perl" : "5.013010" + } + } + }, + "provides" : { + "SDL2" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::ControllerAxisEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::ControllerButtonEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::ControllerDeviceEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::DropEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::Event" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::Image" : { + "file" : "lib/SDL2/Raw/Image.pm", + "version" : "0.001" + }, + "SDL2::KeyboardEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::Mixer" : { + "file" : "lib/SDL2/Raw/Mixer.pm", + "version" : "0.001" + }, + "SDL2::MouseButtonEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::MouseMotionEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::MouseWheelEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::PixelFormat" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::Point" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::Rect" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::RendererInfo" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::Surface" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::SysWMinfo" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::TextEditingEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::TextInputEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::UserEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::Version" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + }, + "SDL2::WindowEvent" : { + "file" : "lib/SDL2/Raw.pm", + "version" : "0.001" + } + }, + "release_status" : "stable", + "resources" : { + "bugtracker" : { + "web" : "https://github.com/jjatria/perl-sdl2-raw/issues" + }, + "repository" : { + "type" : "git", + "url" : "git://github.com/jjatria/perl-sdl2-raw.git", + "web" : "https://github.com/jjatria/perl-sdl2-raw" + } + }, + "version" : "0.001", + "x_Dist_Zilla" : { + "perl" : { + "version" : "5.034000" + }, + "plugins" : [ + { + "class" : "Dist::Zilla::Plugin::NextRelease", + "name" : "NextRelease", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", + "config" : { + "Dist::Zilla::Role::FileWatcher" : { + "version" : "0.006" + } + }, + "name" : "MarkdownInBuild", + "version" : "0.163250" + }, + { + "class" : "Dist::Zilla::Plugin::Git::GatherDir", + "config" : { + "Dist::Zilla::Plugin::GatherDir" : { + "exclude_filename" : [ + "Build.PL", + "LICENSE", + "META.json", + "README.md", + "TODO", + "cpanfile", + "dist.ini" + ], + "exclude_match" : [], + "follow_symlinks" : 0, + "include_dotfiles" : 0, + "prefix" : "", + "prune_directory" : [], + "root" : "." + }, + "Dist::Zilla::Plugin::Git::GatherDir" : { + "include_untracked" : 0 + } + }, + "name" : "@Starter::Git/Git::GatherDir", + "version" : "2.047" + }, + { + "class" : "Dist::Zilla::Plugin::MetaYAML", + "name" : "@Starter::Git/MetaYAML", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::MetaJSON", + "name" : "@Starter::Git/MetaJSON", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::License", + "name" : "@Starter::Git/License", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::PodSyntaxTests", + "name" : "@Starter::Git/PodSyntaxTests", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs", + "name" : "@Starter::Git/Test::ReportPrereqs", + "version" : "0.028" + }, + { + "class" : "Dist::Zilla::Plugin::Test::Compile", + "config" : { + "Dist::Zilla::Plugin::Test::Compile" : { + "bail_out_on_fail" : 0, + "fail_on_warning" : "author", + "fake_home" : 0, + "filename" : "xt/author/00-compile.t", + "module_finder" : [ + ":InstallModules" + ], + "needs_display" : 0, + "phase" : "develop", + "script_finder" : [ + ":PerlExecFiles" + ], + "skips" : [], + "switch" : [] + } + }, + "name" : "@Starter::Git/Test::Compile", + "version" : "2.058" + }, + { + "class" : "Dist::Zilla::Plugin::MakeMaker", + "config" : { + "Dist::Zilla::Role::TestRunner" : { + "default_jobs" : 1 + } + }, + "name" : "@Starter::Git/MakeMaker", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::Manifest", + "name" : "@Starter::Git/Manifest", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::PruneCruft", + "name" : "@Starter::Git/PruneCruft", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::ManifestSkip", + "name" : "@Starter::Git/ManifestSkip", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::RunExtraTests", + "config" : { + "Dist::Zilla::Role::TestRunner" : { + "default_jobs" : 1 + } + }, + "name" : "@Starter::Git/RunExtraTests", + "version" : "0.029" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Check", + "config" : { + "Dist::Zilla::Plugin::Git::Check" : { + "untracked_files" : "die" + }, + "Dist::Zilla::Role::Git::DirtyFiles" : { + "allow_dirty" : [ + "Changes", + "dist.ini" + ], + "allow_dirty_match" : [], + "changelog" : "Changes" + }, + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.31.1", + "repo_root" : "." + } + }, + "name" : "@Starter::Git/Git::Check", + "version" : "2.047" + }, + { + "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease", + "config" : { + "Dist::Zilla::Plugin::CopyFilesFromRelease" : { + "filename" : [ + "Build.PL", + "LICENSE", + "META.json", + "README.md" + ], + "match" : [] + } + }, + "name" : "@Starter::Git/CopyFilesFromRelease", + "version" : "0.007" + }, + { + "class" : "Dist::Zilla::Plugin::Regenerate::AfterReleasers", + "config" : { + "Dist::Zilla::Plugin::Regenerate::AfterReleasers" : { + "plugins" : [ + "@Starter::Git/CopyFilesFromRelease" + ] + }, + "Dist::Zilla::Role::Regenerator" : { + "$Dist::Zilla::Role::Regenerator::VERSION" : "0.001002" + } + }, + "name" : "@Starter::Git/Regenerate::AfterReleasers", + "version" : "0.001002" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Commit", + "config" : { + "Dist::Zilla::Plugin::Git::Commit" : { + "add_files_in" : [ + "/" + ], + "commit_msg" : "Release v%V%t", + "signoff" : 0 + }, + "Dist::Zilla::Role::Git::DirtyFiles" : { + "allow_dirty" : [ + "Build.PL", + "Changes", + "LICENSE", + "META.json", + "README.md", + "dist.ini" + ], + "allow_dirty_match" : [], + "changelog" : "Changes" + }, + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.31.1", + "repo_root" : "." + }, + "Dist::Zilla::Role::Git::StringFormatter" : { + "time_zone" : "local" + } + }, + "name" : "@Starter::Git/Release_Commit", + "version" : "2.047" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Tag", + "config" : { + "Dist::Zilla::Plugin::Git::Tag" : { + "branch" : null, + "changelog" : "Changes", + "signed" : 0, + "tag" : "0.001", + "tag_format" : "%v", + "tag_message" : "" + }, + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.31.1", + "repo_root" : "." + }, + "Dist::Zilla::Role::Git::StringFormatter" : { + "time_zone" : "local" + } + }, + "name" : "@Starter::Git/Git::Tag", + "version" : "2.047" + }, + { + "class" : "Dist::Zilla::Plugin::TestRelease", + "name" : "@Starter::Git/TestRelease", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::ConfirmRelease", + "name" : "@Starter::Git/ConfirmRelease", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::UploadToCPAN", + "name" : "@Starter::Git/UploadToCPAN", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::MetaConfig", + "name" : "@Starter::Git/MetaConfig", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::MetaNoIndex", + "name" : "@Starter::Git/MetaNoIndex", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::MetaProvides::Package", + "config" : { + "Dist::Zilla::Plugin::MetaProvides::Package" : { + "finder_objects" : [ + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : "@Starter::Git/MetaProvides::Package/AUTOVIV/:InstallModulesPM", + "version" : "6.020" + } + ], + "include_underscores" : 0 + }, + "Dist::Zilla::Role::MetaProvider::Provider" : { + "$Dist::Zilla::Role::MetaProvider::Provider::VERSION" : "2.002004", + "inherit_missing" : 1, + "inherit_version" : 0, + "meta_noindex" : 1 + }, + "Dist::Zilla::Role::ModuleMetadata" : { + "Module::Metadata" : "1.000037", + "version" : "0.006" + } + }, + "name" : "@Starter::Git/MetaProvides::Package", + "version" : "2.004003" + }, + { + "class" : "Dist::Zilla::Plugin::ShareDir", + "name" : "@Starter::Git/ShareDir", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::ExecDir", + "name" : "@Starter::Git/ExecDir", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::MinimumPerl", + "name" : "MinimumPerl", + "version" : "1.006" + }, + { + "class" : "Dist::Zilla::Plugin::Repository", + "name" : "Repository", + "version" : "0.24" + }, + { + "class" : "Dist::Zilla::Plugin::Bugtracker", + "name" : "Bugtracker", + "version" : "1.111080" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Contributors", + "config" : { + "Dist::Zilla::Plugin::Git::Contributors" : { + "git_version" : "2.31.1", + "include_authors" : 0, + "include_releaser" : 1, + "order_by" : "name", + "paths" : [] + } + }, + "name" : "Git::Contributors", + "version" : "0.036" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":InstallModules", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":IncModules", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":TestFiles", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":ExtraTestFiles", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":ExecFiles", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":PerlExecFiles", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":ShareFiles", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":MainModule", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":AllFiles", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":NoFiles", + "version" : "6.020" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : "@Starter::Git/MetaProvides::Package/AUTOVIV/:InstallModulesPM", + "version" : "6.020" + } + ], + "zilla" : { + "class" : "Dist::Zilla::Dist::Builder", + "config" : { + "is_trial" : 0 + }, + "version" : "6.020" + } + }, + "x_generated_by_perl" : "v5.34.0", + "x_serialization_backend" : "Cpanel::JSON::XS version 4.26", + "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" +} + From edcce26779a7a861a9a78b81c82e4e423512fd63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Tue, 22 Jun 2021 00:01:17 +0100 Subject: [PATCH 03/36] Remove editor swap file --- examples/.white_noise.pl.swp | Bin 12288 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 examples/.white_noise.pl.swp diff --git a/examples/.white_noise.pl.swp b/examples/.white_noise.pl.swp deleted file mode 100644 index dbc7037f6272517ae03213b66f5c822023509f94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2O>87b701hbLs){~lI8U588qW%zWl+;wB_A+J+pR8ySABW$BVqyY&kSNN96N++4ZsD2`kbnb0aN%EF)w2_?<1EKT z-O_KnyXsZFk9zg0t(}9)Q|47#DpeS+&ocJod#~R3(VGQ!^DJXGZ`vYsZP|0`H454v zEQA@pp(iZ2`BAyYL$BPZUMXI_Y?k@Z?)E$$l?NSH@TMQQ5ij+;Q?;g@X#~;;Jah#5 z!YMAD*;$yMi?@1aW|}_#xt)g|o(iTBNF$I&AdNs8fiwbX1kwnk5lAEO=tn^Grr0;( z#)q^k-_rM~V|V?U-qHx95lADDMj(wq8i6zdX#~;;q!CCXkVYVlKpKHZFama)vG3uJ z{g*!s0Bfd7QC#z@NZt;3e==@ErIdcpA9C0UYdti=Y7B{tRPpf!~53fbRnj zSl~Q(0-OO;;O#Sv{R;dN+y!?)ADjpO`7~qy2Cst`fDgLhYhVj3ff;ZXOo6{XhWWr- z;1A$;;2szN58MFz;5xVjD&Py?U!P*^UGOS+4*Uc>3%&!s4GzE!&;%yf088L8@a88O z`!#q5>;elkz*oQ&`1>aq`#tytcmdo69=HW;a04_z2^7IUK8`WqH{iSAE)ZZHTn0r@ z0Dt=!V{d@h!Asyp5Q0Up0N(j1V}Ay(fM-Ditb=KA7CZso{Rm_K1b+lS2R{SPgL~i^ zun*3I$H6Pek2psj!5sg8G5HQ7T-_06hqOXb$92T=No^F_BMP~b&kkp)L^*?UxK;aI z{N^Tqo4R(7Zd0~ND;F?R@dBRi(3ALQb~#qy1ufg7Y}FRFVW?4t(Fj6ui;p5Go;yX% z^xNDQuyTber-)fRjJR}WJX;^{lNy&<)IXp$KjxU!XwQ_=iOhoS1FLhrhQhJ{0|s=ie)lA*DAXO{HwBA4Wk4o0F$6 zQMTubR!4cNq$Wqiqk8VJjZp4Qj%o$oKIXX;1YRi&JdZA3q{`57XnGoV9XU0h1C4UY zF@*9|p;C!<&8oFQMVg~o#6~K~P$CuvgG6kjX0C5oV>vAdi(`AB-nWH MYG@@uPM zXpi<>Uo7nxXgk0I4@*8D?b<>u zV>TNhU+s8Z;?78~@4yayo%M2^Fbq9Xy9}p#M-X~;>sFp}t#i4;{he`9ZVv;|#>UW+ ziQ{_Y9xcq1Tth6|3Hq?w$|@wCa;&VEZC}~&V7@1&B??!l?)gp@m0#@{yj5pRpIE1Nx;*bCnA17nl^tYT=3gbF?6P)K$i9@f6`iF1|}) zWas%zNR_sOA<|#OBO3`j z6nY1#tjQ;trp8KRJ=7?=x@vBc>rj>-q3{!Wpm%JJ zK{NPvI&O*dL$%dwo7GyqR&UA$yi%*LTD2-YeZ13Jt*_TCz0_*)igS7qRRL5>n0Rt* z6%*?b!BdS^Q+<%hyXIzfYq#0h*xKDxHk_KEN_=~((Oj!-TJW{11}XpFuQ#`iZSA(O z5gVgO@a}X@d8kZ0=HIS53sin!hpVYg-xbL&HaAVH`IK3$Z4vS~UbOq^<3iB2J^W@| zjXYt=@JA(j@l$id*!FBxj(%hqm*QhnW+tg~Q=XvekS)8&2?mJeLN L%>5&IYI*Fx!PWBi From ed0d6efaa3a254a6bf05f9e4dd1013023b071216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Wed, 23 Jun 2021 00:01:28 +0100 Subject: [PATCH 04/36] Move constants to top of file --- lib/SDL2/Raw.pm | 67 +++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 2d241f1..a65570c 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -22,6 +22,40 @@ use constant { BYTEORDER => $Config{byteorder}, BIG_ENDIAN => 4321, LIL_ENDIAN => 1234, + MIX_MAXVOLUME => 128, +}; + +# Init flags +use constant { + INIT_TIMER => 0x000001, + INIT_AUDIO => 0x000010, + INIT_VIDEO => 0x000020, + INIT_JOYSTICK => 0x000200, + INIT_HAPTIC => 0x001000, + INIT_GAMECONTROLLER => 0x002000, + INIT_EVENTS => 0x004000, + INIT_SENSOR => 0x008000, + INIT_NOPARACHUTE => 0x100000, +}; + +use constant INIT_EVERYTHING => ( + INIT_TIMER | INIT_AUDIO | INIT_VIDEO | INIT_EVENTS | + INIT_JOYSTICK | INIT_HAPTIC | INIT_GAMECONTROLLER | INIT_SENSOR +); + +# Window flags +use constant { + WINDOWPOS_UNDEFINED => 0x1FFF0000, + WINDOWPOS_CENTERED => 0x2FFF0000, +}; + +# Mouse buttons +use constant { + BUTTON_LEFT => 1, + BUTTON_MIDDLE => 2, + BUTTON_RIGHT => 3, + BUTTON_X1 => 4, + BUTTON_X2 => 5, }; sub enum { @@ -1012,39 +1046,6 @@ BEGIN { ); } -# Init flags -use constant { - INIT_TIMER => 0x000001, - INIT_AUDIO => 0x000010, - INIT_VIDEO => 0x000020, - INIT_JOYSTICK => 0x000200, - INIT_HAPTIC => 0x001000, - INIT_GAMECONTROLLER => 0x002000, - INIT_EVENTS => 0x004000, - INIT_SENSOR => 0x008000, - INIT_NOPARACHUTE => 0x100000, -}; - -use constant INIT_EVERYTHING => ( - INIT_TIMER | INIT_AUDIO | INIT_VIDEO | INIT_EVENTS | - INIT_JOYSTICK | INIT_HAPTIC | INIT_GAMECONTROLLER | INIT_SENSOR -); - -# Window flags -use constant { - WINDOWPOS_UNDEFINED => 0x1FFF0000, - WINDOWPOS_CENTERED => 0x2FFF0000, -}; - -# Mouse buttons -use constant { - BUTTON_LEFT => 1, - BUTTON_MIDDLE => 2, - BUTTON_RIGHT => 3, - BUTTON_X1 => 4, - BUTTON_X2 => 5, -}; - $ffi->type( sint32 => 'SDL_BlendMode' ); $ffi->type( sint32 => 'SDL_JoystickID' ); From fdb39ebdb48e1defd56d696fdb1ca93474062915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Wed, 23 Jun 2021 00:02:25 +0100 Subject: [PATCH 05/36] Be consistent with enum definitions --- lib/SDL2/Raw/Image.pm | 26 ++++++++++++++++++++------ lib/SDL2/Raw/Mixer.pm | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/lib/SDL2/Raw/Image.pm b/lib/SDL2/Raw/Image.pm index 29c7099..5548f07 100644 --- a/lib/SDL2/Raw/Image.pm +++ b/lib/SDL2/Raw/Image.pm @@ -7,12 +7,26 @@ use SDL2::Raw; use FFI::CheckLib; use FFI::Platypus 1.00; -use constant { - INIT_JPG => 0x1, - INIT_PNG => 0x2, - INIT_TIF => 0x4, - INIT_WEBP => 0x8, -}; +BEGIN { + require constant; + + my %enums = ( + InitFlags => { + INIT_JPG => 0x1, + INIT_PNG => 0x2, + INIT_TIF => 0x4, + INIT_WEBP => 0x8, + }, + ); + + while ( my ( $name, $values ) = each %enums ) { + constant->import($values); + + my $variable = __PACKAGE__ . '::' . $name; + no strict 'refs'; + %{$variable} = ( %{$variable}, reverse %$values ); + } +} my $ffi = FFI::Platypus->new( api => 1 ); $ffi->lib( find_lib_or_exit lib => 'SDL2_image' ); diff --git a/lib/SDL2/Raw/Mixer.pm b/lib/SDL2/Raw/Mixer.pm index 323b579..cc5bcbf 100644 --- a/lib/SDL2/Raw/Mixer.pm +++ b/lib/SDL2/Raw/Mixer.pm @@ -8,6 +8,47 @@ use FFI::CheckLib; use FFI::Platypus 1.00; use Ref::Util; +BEGIN { + require constant; + + my %enums = ( + InitFlags => { + INIT_FLAC => 0x01, + INIT_MOD => 0x02, + INIT_MP3 => 0x08, + INIT_OGG => 0x10, + INIT_MID => 0x20, + INIT_OPUS => 0x40, + }, + Fading => { + NO_FADING => 0, + FADING_OUT => 1, + FADING_IN => 2, + }, + MusicType => { + MUS_NONE => 0, + MUS_CMD => 1, + MUS_WAV => 2, + MUS_MOD => 3, + MUS_MID => 4, + MUS_OGG => 5, + MUS_MP3 => 6, + MUS_MP3_MAD_UNUSED => 7, + MUS_FLAC => 8, + MUS_MODPLUG_UNUSED => 9, + MUS_OPUS => 10, + }, + ); + + while ( my ( $name, $values ) = each %enums ) { + constant->import($values); + + my $variable = __PACKAGE__ . '::' . $name; + no strict 'refs'; + %{$variable} = ( %{$variable}, reverse %$values ); + } +} + use constant { INIT_JPG => 0x1, INIT_PNG => 0x2, From 8c283885ac86d8cbaf3053fc6eb9194b554b3e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Wed, 23 Jun 2021 00:03:20 +0100 Subject: [PATCH 06/36] Start porting mixer example --- examples/playmus.pl | 75 +++++++++++++++++++++++++++++++++++++++++++ lib/SDL2/Raw.pm | 26 +++++++++++++++ lib/SDL2/Raw/Mixer.pm | 45 ++++++++++++++++++++------ 3 files changed, 136 insertions(+), 10 deletions(-) create mode 100644 examples/playmus.pl diff --git a/examples/playmus.pl b/examples/playmus.pl new file mode 100644 index 0000000..9e2dc64 --- /dev/null +++ b/examples/playmus.pl @@ -0,0 +1,75 @@ +#!/usr/bin/env perl + +use SDL2::Raw; +use SDL2::Raw::Mixer; +use Getopt::Long; + +use feature qw( say state ); + +my $AUDIO_FORMAT = SDL2::Mixer::DEFAULT_FORMAT; + +GetOptions( + 'interactive|i' => \( my $INTERACTIVE = 1 ), + 'loop|l' => \( my $LOOPING = 1 ), + '8' => sub { $AUDIO_FORMAT = SDL2::AUDIO_U8 }, + 'f32' => sub { $AUDIO_FORMAT = SDL2::AUDIO_F32 }, + 'rate|r=i' => \( my $AUDIO_RATE = SDL2::Mixer::DEFAULT_FREQUENCY ), + 'channels|c=i' => \( my $AUDIO_CHANNELS = SDL2::Mixer::DEFAULT_CHANNELS ), + 'buffers|b=i' => \( my $AUDIO_BUFFERS = 4096 ), + 'olumev=i' => \( my $AUDIO_VOLUME = SDL2::Mixer::MAX_VOLUME ), + 'rwops' => \( my $RWOPS = 0 ), + 'mono|m' => sub { $AUDIO_CHANNELS = 1 }, +); + +SDL2::Init(SDL2::INIT_AUDIO) + and die 'Could not initialise SDL2: ' . SDL2::GetError; + +# TODO: INT handler +# TODO: TERM handler + +END { SDL2::Quit } + +SDL2::Mixer::OpenAudio( + $AUDIO_RATE, + $AUDIO_FORMAT, + $AUDIO_CHANNELS, + $AUDIO_BUFFERS, +) and die 'Could not open audio: ' . SDL2::GetError; + +SDL2::Mixer::QuerySpec( \$AUDIO_RATE, \$AUDIO_FORMAT, \$AUDIO_CHANNELS); +say sprintf 'Opened audio at %d Hz %d bit%s %s %d bytes audio buffer', + $AUDIO_RATE, + ( $AUDIO_FORMAT & 0xFF ), + $AUDIO_FORMAT & SDL2::AUDIO_MASK_DATATYPE ? ' (float)' : '', + $AUDIO_CHANNELS > 2 ? 'surround' : $AUDIO_CHANNELS > 1 ? 'stereo' : 'mono', + $AUDIO_BUFFERS; + +SDL2::Mixer::VolumeMusic($AUDIO_VOLUME); + +SDL2::Mixer::SetMusicCMD( $ENV{MUSIC_CMD} ); + +for my $file (@ARGV) { + my $music = $RWOPS + ? SDL2::Mixer::LoadMUS_RW( SDL2::RWFromFile( $file, 'rb' ), 1 ) + : SDL2::Mixer::LoadMUS($file); + + die "Could not load $file: " . SDL2::GetError unless $music; + + my $type = ''; + for ( SDL2::Mixer::GetMusicType($music) ) { + $type + = $_ == SDL2::Mixer::MUS_CMD ? 'CMD' + : $_ == SDL2::Mixer::MUS_WAV ? 'WAV' + : $_ == SDL2::Mixer::MUS_MOD ? 'MOD' + : $_ == SDL2::Mixer::MUS_MODPLUG_UNUSED ? 'MOD' + : $_ == SDL2::Mixer::MUS_FLAC ? 'FLAC' + : $_ == SDL2::Mixer::MUS_MID ? 'MIDI' + : $_ == SDL2::Mixer::MUS_OGG ? 'Ogg Vorbis' + : $_ == SDL2::Mixer::MUS_MP3 ? 'MP3' + : $_ == SDL2::Mixer::MUS_MP3_MAD_UNUSED ? 'MP3' + : $_ == SDL2::Mixer::MUS_OPUS ? 'OPUS' + : 'NONE'; + } + + say "Detected music type: $type"; +} diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index a65570c..1d2c20f 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -58,6 +58,32 @@ use constant { BUTTON_X2 => 5, }; +# Audio flags +use constant { + AUDIO_U8 => 0x0008, # Unsigned 8-bit samples + AUDIO_S8 => 0x8008, # Signed 8-bit samples + AUDIO_U16LSB => 0x0010, # Unsigned 16-bit samples + AUDIO_S16LSB => 0x8010, # Signed 16-bit samples + AUDIO_U16MSB => 0x1010, # As above, but big-endian byte order + AUDIO_S16MSB => 0x9010, # As above, but big-endian byte order + AUDIO_S32LSB => 0x8020, # 32-bit integer samples + AUDIO_S32MSB => 0x9020, # As above, but big-endian byte order + AUDIO_F32LSB => 0x8120, # 32-bit floating point samples + AUDIO_F32MSB => 0x9120, # As above, but big-endian byte order + + AUDIO_MASK_BITSIZE => 0xFF, + AUDIO_MASK_DATATYPE => 1 << 8, + AUDIO_MASK_ENDIAN => 1 << 12, + AUDIO_MASK_SIGNED => 1 << 15, +}; + +use constant { + AUDIO_U16 => AUDIO_U16LSB, + AUDIO_S16 => AUDIO_S16LSB, + AUDIO_S32 => AUDIO_S32LSB, + AUDIO_F32 => AUDIO_F32LSB, +}; + sub enum { require enum::hash; require constant; diff --git a/lib/SDL2/Raw/Mixer.pm b/lib/SDL2/Raw/Mixer.pm index cc5bcbf..e9de1f4 100644 --- a/lib/SDL2/Raw/Mixer.pm +++ b/lib/SDL2/Raw/Mixer.pm @@ -50,21 +50,19 @@ BEGIN { } use constant { - INIT_JPG => 0x1, - INIT_PNG => 0x2, - INIT_TIF => 0x4, - INIT_WEBP => 0x8, - - NO_FADING => 0, - FADING_OUT => 1, - FADING_IN => 2, + DEFAULT_FREQUENCY => 44_100, + DEFAULT_FORMAT => SDL2::BYTEORDER == SDL2::LIL_ENDIAN ? SDL2::AUDIO_S16LSB : SDL2::AUDIO_S16MSB, + DEFAULT_CHANNELS => 2, + MAX_VOLUME => SDL2::MIX_MAXVOLUME, }; my $ffi = FFI::Platypus->new( api => 1 ); $ffi->lib( find_lib_or_exit lib => 'SDL_mixer' ); $ffi->mangler( sub { 'Mix_' . shift } ); -$ffi->type( opaque => 'Mix_Chunk' ); +$ffi->type( opaque => 'Mix_Chunk' ); +$ffi->type( opaque => 'Mix_Music' ); +$ffi->type( int => 'Mix_MusicType' ); ## General @@ -102,7 +100,7 @@ $ffi->attach( Playing => ['int' ] => $ffi->attach( Paused => ['int' ] => 'int' ); $ffi->attach( FadingChannel => ['int' ] => 'int' ); $ffi->attach( GetChunk => ['int' ] => 'Mix_Chunk' ); -$ffi->attach( ChannelFinished => ['(int)->void' ] => 'void' => sub { +$ffi->attach( ChannelFinished => ['(int)->void' ] => 'void' => sub { # Untested my ( $xsub, $closure ) = @_; $closure = $ffi->closure($closure) if Ref::Util::is_subref $closure; $xsub->($closure); @@ -112,4 +110,31 @@ sub FadeInChannel { FadeInChannelTimed( @_, -1 ) } ## Groups +#TODO + +## Music + +$ffi->attach( GetNumMusicDecoders => [ ] => 'int' ); +$ffi->attach( GetMusicDecoder => ['int' ] => 'string' ); +$ffi->attach( LoadMUS => ['string' ] => 'Mix_Music' ); +$ffi->attach( FreeMusic => ['Mix_Music' ] => 'void' ); +$ffi->attach( PlayMusic => ['Mix_Music', 'int' ] => 'int' ); +$ffi->attach( FadeInMusic => ['Mix_Music', 'int', 'int' ] => 'int' ); +$ffi->attach( FadeInMusicPos => ['Mix_Music', 'int', 'int', 'double'] => 'int' ); +$ffi->attach( HookMusic => ['(opaque, uint8)->void', 'opaque' ] => 'void' ); # Untested +$ffi->attach( VolumeMusic => ['int' ] => 'int' ); +$ffi->attach( PauseMusic => [ ] => 'void' ); +$ffi->attach( ResumeMusic => [ ] => 'void' ); +$ffi->attach( RewindMusic => [ ] => 'void' ); +$ffi->attach( SetMusicPosition => ['double' ] => 'int' ); +$ffi->attach( SetMusicCMD => ['string' ] => 'int' ); +$ffi->attach( HaltMusic => [ ] => 'int' ); +$ffi->attach( FadeOutMusic => ['int' ] => 'int' ); +$ffi->attach( HookMusicFinished => ['()->void' ] => 'void' ); # Untested +$ffi->attach( GetMusicType => ['Mix_Music' ] => 'Mix_MusicType' ); +$ffi->attach( PlayingMusic => [ ] => 'int' ); +$ffi->attach( PausedMusic => [ ] => 'int' ); +$ffi->attach( FadingMusic => [ ] => 'int' ); +$ffi->attach( GetMusicHookData => [ ] => 'opaque' ); + 1; From 87e3827678dc60038401871c84af2d0fea9c50d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Wed, 23 Jun 2021 19:22:29 +0100 Subject: [PATCH 07/36] Complete mixer example --- examples/playmus.pl | 99 +++++++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 35 deletions(-) diff --git a/examples/playmus.pl b/examples/playmus.pl index 9e2dc64..e2fb6b0 100644 --- a/examples/playmus.pl +++ b/examples/playmus.pl @@ -1,60 +1,78 @@ #!/usr/bin/env perl +use strict; +use warnings; + use SDL2::Raw; use SDL2::Raw::Mixer; use Getopt::Long; +use Syntax::Keyword::Defer; + +my %OPT = ( + looping => 1, + interactive => 1, + format => SDL2::Mixer::DEFAULT_FORMAT, + rate => SDL2::Mixer::DEFAULT_FREQUENCY, + channels => SDL2::Mixer::DEFAULT_CHANNELS, + buffers => 4096, + volume => SDL2::Mixer::MAX_VOLUME, +); -use feature qw( say state ); - -my $AUDIO_FORMAT = SDL2::Mixer::DEFAULT_FORMAT; - -GetOptions( - 'interactive|i' => \( my $INTERACTIVE = 1 ), - 'loop|l' => \( my $LOOPING = 1 ), - '8' => sub { $AUDIO_FORMAT = SDL2::AUDIO_U8 }, - 'f32' => sub { $AUDIO_FORMAT = SDL2::AUDIO_F32 }, - 'rate|r=i' => \( my $AUDIO_RATE = SDL2::Mixer::DEFAULT_FREQUENCY ), - 'channels|c=i' => \( my $AUDIO_CHANNELS = SDL2::Mixer::DEFAULT_CHANNELS ), - 'buffers|b=i' => \( my $AUDIO_BUFFERS = 4096 ), - 'olumev=i' => \( my $AUDIO_VOLUME = SDL2::Mixer::MAX_VOLUME ), - 'rwops' => \( my $RWOPS = 0 ), - 'mono|m' => sub { $AUDIO_CHANNELS = 1 }, +GetOptions \%OPT, ( + 'interactive|i', + 'loop|l', + 'rate|r=i', + 'channels|c=i', + 'buffers|b=i', + 'volume=i', + 'rwops', + '8' => sub { $OPT{format} = SDL2::AUDIO_U8 }, + 'f32' => sub { $OPT{format} = SDL2::AUDIO_F32 }, + 'mono|m' => sub { $OPT{channels} = 1 }, ); SDL2::Init(SDL2::INIT_AUDIO) and die 'Could not initialise SDL2: ' . SDL2::GetError; -# TODO: INT handler -# TODO: TERM handler +defer { SDL2::Quit } -END { SDL2::Quit } +my $NEXT_TRACK; +local $SIG{INT} = sub { $NEXT_TRACK++ }; -SDL2::Mixer::OpenAudio( - $AUDIO_RATE, - $AUDIO_FORMAT, - $AUDIO_CHANNELS, - $AUDIO_BUFFERS, -) and die 'Could not open audio: ' . SDL2::GetError; +SDL2::Mixer::OpenAudio( @OPT{qw( rate format channels buffers )} ) + and die 'Could not open audio: ' . SDL2::GetError; -SDL2::Mixer::QuerySpec( \$AUDIO_RATE, \$AUDIO_FORMAT, \$AUDIO_CHANNELS); -say sprintf 'Opened audio at %d Hz %d bit%s %s %d bytes audio buffer', - $AUDIO_RATE, - ( $AUDIO_FORMAT & 0xFF ), - $AUDIO_FORMAT & SDL2::AUDIO_MASK_DATATYPE ? ' (float)' : '', - $AUDIO_CHANNELS > 2 ? 'surround' : $AUDIO_CHANNELS > 1 ? 'stereo' : 'mono', - $AUDIO_BUFFERS; +defer { SDL2::Mixer::CloseAudio } -SDL2::Mixer::VolumeMusic($AUDIO_VOLUME); +SDL2::Mixer::QuerySpec( \$OPT{rate}, \$OPT{format}, \$OPT{channels} ); +print sprintf "Opened audio at %d Hz %d bit%s %s %d bytes audio buffer\n", + $OPT{rate}, + $OPT{format} & SDL2::AUDIO_MASK_BITSIZE, + $OPT{format} & SDL2::AUDIO_MASK_DATATYPE ? ' (float)' : '', + $OPT{channels} > 2 ? 'surround' : $OPT{channels} > 1 ? 'stereo' : 'mono', + $OPT{buffers}; -SDL2::Mixer::SetMusicCMD( $ENV{MUSIC_CMD} ); +SDL2::Mixer::VolumeMusic($OPT{volume}); +SDL2::Mixer::SetMusicCMD($ENV{MUSIC_CMD}); + +defer { + if ( SDL2::Mixer::PlayingMusic ) { + SDL2::Mixer::FadeOutMusic(1500); + SDL2::Delay(1500); + } +} for my $file (@ARGV) { - my $music = $RWOPS + $NEXT_TRACK = 0; + + my $music = $OPT{rwops} ? SDL2::Mixer::LoadMUS_RW( SDL2::RWFromFile( $file, 'rb' ), 1 ) : SDL2::Mixer::LoadMUS($file); die "Could not load $file: " . SDL2::GetError unless $music; + defer { SDL2::Mixer::FreeMusic($music) } + my $type = ''; for ( SDL2::Mixer::GetMusicType($music) ) { $type @@ -71,5 +89,16 @@ : 'NONE'; } - say "Detected music type: $type"; + print "Detected music type: $type\n"; + + print "Playing $file\n"; + SDL2::Mixer::FadeInMusic( $music, $OPT{looping}, 2000 ); + + while ( !$NEXT_TRACK && SDL2::Mixer::PlayingMusic || SDL2::Mixer::PausedMusic ) { + SDL2::Delay(100); + } + + SDL2::Delay(500); + + last if $NEXT_TRACK > 1; } From e1779b28d7e027224f2ae5e2e28172b2a8bc71d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Wed, 23 Jun 2021 19:51:44 +0100 Subject: [PATCH 08/36] Fix variable name in mixer example --- examples/playmus.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/playmus.pl b/examples/playmus.pl index e2fb6b0..582d9ea 100644 --- a/examples/playmus.pl +++ b/examples/playmus.pl @@ -9,7 +9,7 @@ use Syntax::Keyword::Defer; my %OPT = ( - looping => 1, + loop => 1, interactive => 1, format => SDL2::Mixer::DEFAULT_FORMAT, rate => SDL2::Mixer::DEFAULT_FREQUENCY, @@ -92,7 +92,7 @@ print "Detected music type: $type\n"; print "Playing $file\n"; - SDL2::Mixer::FadeInMusic( $music, $OPT{looping}, 2000 ); + SDL2::Mixer::FadeInMusic( $music, $OPT{loop}, 2000 ); while ( !$NEXT_TRACK && SDL2::Mixer::PlayingMusic || SDL2::Mixer::PausedMusic ) { SDL2::Delay(100); From 801eb2abdebe1be2a2fbd1b62e7aaee832208eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Wed, 23 Jun 2021 23:33:59 +0100 Subject: [PATCH 09/36] Add logging subs --- lib/SDL2/Raw.pm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 1d2c20f..25529ee 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1701,6 +1701,18 @@ $ffi->attach( RegisterEvents => ['uint32'] => 'int' ); # SDL_GetRevisionNumber $ffi->attach( GetVersion => ['SDL_Version'] => 'void' ); +## Logging + +$ffi->attach( Log => [ 'string'] => 'void' => sub { my $xs = shift; $xs->( sprintf shift, @_ ) } ); +$ffi->attach( LogCritical => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); +$ffi->attach( LogDebug => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); +$ffi->attach( LogError => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); +$ffi->attach( LogInfo => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); +$ffi->attach( LogMessage => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); +$ffi->attach( LogMessageV => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); +$ffi->attach( LogVerbose => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); +$ffi->attach( LogWarn => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); + # Clean helper functions delete $SDL2::{$_} for qw( enum pixel_format ); From 25e4d948cd8689d5f69d4246208cbc03d67b019d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Wed, 23 Jun 2021 23:37:38 +0100 Subject: [PATCH 10/36] Add second mixer example This example did not work with the version of SDL_mixer that ships with Ubuntu. Once I built my own and linked to it, the program behaved as expected (modulo the remaining bugs). --- examples/playwav.pl | 275 ++++++++++++++++++++++++++++++++++++++++++ lib/SDL2/Raw/Mixer.pm | 34 +++++- 2 files changed, 304 insertions(+), 5 deletions(-) create mode 100644 examples/playwav.pl diff --git a/examples/playwav.pl b/examples/playwav.pl new file mode 100644 index 0000000..e87045b --- /dev/null +++ b/examples/playwav.pl @@ -0,0 +1,275 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use SDL2::Raw; +use SDL2::Raw::Mixer; +use Getopt::Long; +use Syntax::Keyword::Defer; +use feature qw( state say ); + +use constant BUFFER => 4096; + +my %tests = ( + decoders => 1, + distance => 1, + hook => 1, + panning => 1, + position => 1, + versions => 1, +); + +my $CHANNEL_DONE; +my %OPT = ( + loop => 0, + format => SDL2::Mixer::DEFAULT_FORMAT, + rate => SDL2::Mixer::DEFAULT_FREQUENCY, + channels => SDL2::Mixer::DEFAULT_CHANNELS, + flip_stereo => 0, + flip_samples => 0, +); + +GetOptions \%OPT => ( + 'help|?', + 'test=s@', + 'rate|r=i', + 'flip_stereo|flip-stereo|f', + 'flip_samples|flip-samples|F', + 'channels|c=i', + '8' => sub { $OPT{format} = SDL2::AUDIO_U8 }, + 'f32' => sub { $OPT{format} = SDL2::AUDIO_F32 }, + 'mono|m' => sub { $OPT{channels} = 1 }, + 'loop|l' => sub { $OPT{loop} = -1 }, +); + +my $filename = shift; + +if ( my @bad = grep { !$tests{ lc $_ } } @{ $OPT{test} } ) { + say "Invalid test case selected: %s\n", join ',', @bad; + $OPT{help} = 1; +} +else { + $OPT{test} = { map { lc $_ => 1 } @{ $OPT{test} } }; +} + +print <<"USAGE" and exit if $OPT{help}; +$0 - A test application for the SDL mixer library + +Usage: + + $0 [OPTIONS] wavefile + +Options: + + -8 + Load sound as unsigned, 8-bit samples + + --f32 + Load sound as 32-bit floating-point samples + + --rate NUMBER, -r NUMBER + Set the sampling rate. Defaults to @{[ SDL2::Mixer::DEFAULT_FREQUENCY ]} Hz + + --channels NUMBER, -c NUMBER + Set the number of channels. Defaults to @{[ SDL2::Mixer::DEFAULT_CHANNELS ]} + + --flip-stereo, -f + Flip channels. Defaults to false. + + --flip-samples, -F + Flip samples. Defaults to false. + + --mono, -m + Play the sound through a single channel. Equivalent to --channels=1 + + --loop, -l + Loop sound + + --test TEST + Enable a specific mixer test. Can be set multiple times. + Possible (case-insensitive) values are + * 'DECODERS' + * 'DISTANCE' + * 'HOOK' + * 'PANNING' + * 'POSITION' + * 'VERSIONS' + + --help, -? + Show this help message +USAGE + +SDL2::Init(SDL2::INIT_AUDIO) + and die 'Could not initialise SDL2: ' . SDL2::GetError; + +defer { SDL2::Quit } + +SDL2::Mixer::OpenAudio( @OPT{qw( rate format channels )}, BUFFER ) + and die 'Could not open audio: ' . SDL2::GetError; + +defer { SDL2::Mixer::CloseAudio } + +SDL2::Mixer::QuerySpec( \$OPT{rate}, \$OPT{format}, \$OPT{channels} ); +say sprintf 'Opened audio at %d Hz %d bit%s %s %s', + $OPT{rate}, + $OPT{format} & SDL2::AUDIO_MASK_BITSIZE, + $OPT{format} & SDL2::AUDIO_MASK_DATATYPE ? ' (float)' : '', + $OPT{channels} > 2 ? 'surround' : $OPT{channels} > 1 ? 'stereo' : 'mono', + $OPT{loop} ? '(looping)' : ''; + +defer { + if ( SDL2::Mixer::PlayingMusic ) { + SDL2::Mixer::FadeOutMusic(1500); + SDL2::Delay(1500); + } +} + +test_version() if $OPT{test}{versions}; + +test_decoders() if $OPT{test}{decoders}; + +my $wave = SDL2::Mixer::LoadWAV($filename) + or die "Could not load $filename: " . SDL2::GetError; + +defer { SDL2::Mixer::FreeChunk($wave) } + +flip_sample($wave) if $OPT{flip_samples}; + +# FIXME: Hooks do not fire +SDL2::Mixer::ChannelFinished( sub { SDL2::Log('Channel finished') } ) if $OPT{test}{hook}; + +if ( ( !SDL2::Mixer::SetReverseStereo( SDL2::Mixer::CHANNEL_POST, 0+!!$OPT{flip_stereo} ) ) && $OPT{flip_stereo} ) { + warn "Failed to set up reverse stereo effect!"; + warn 'Reason: ' . SDL2::GetError; +} + +my $channel = SDL2::Mixer::PlayChannel( 0, $wave, $OPT{loop} ); +die 'Could not play channel: ' . SDL2::GetError if $channel < 0; + +while ( still_playing() ) { + test_panning() if $OPT{test}{panning}; + test_distance() if $OPT{test}{distance}; + test_position() if $OPT{test}{position}; + + SDL2::Delay(1); +} + +# Helpers + +sub still_playing { $OPT{test}{hook} ? $CHANNEL_DONE : SDL2::Mixer::Playing(0) } + +sub test_version { + # TODO: can we report the SDL_mixer version? + SDL2::GetVersion( my $v = SDL2::Version->new ); + say 'This program is running on SDL %d.%d.%d', + $v->major, $v->minor, $v->patch; +} + +sub test_decoders { + for my $i ( 0 .. SDL2::Mixer::GetNumChunkDecoders() - 1 ) { + printf " - chunk decoder: %s\n", SDL2::Mixer::GetChunkDecoder($i); + } + + for my $i ( 0 .. SDL2::Mixer::GetNumMusicDecoders() - 1 ) { + printf " - music decoder: %s\n", SDL2::Mixer::GetMusicDecoder($i); + } +} + +sub flip_sample { + state $warn = do { say STDERR 'Sample flipping not yet implemented' }; +} + +sub test_panning { + state $lvol = 128; + state $rvol = 128; + state $linc = -1; + state $rinc = 1; + state $next = 0; + state $ok = 1; + + if ( $ok && SDL2::GetTicks() >= $next ) { + $ok = SDL2::Mixer::SetPanning( 0, $lvol, $rvol ) + or warn sprintf 'SetPanning( 0, %s, %s ) failed: %s', + $lvol, $rvol, SDL2::GetError; + + if ( $lvol == 255 || $lvol == 0 ) { + say 'All the way to the left speaker' if $lvol == 255; + $linc *= -1; + } + + if ( $rvol == 255 || $rvol == 0 ) { + say 'All the way to the right speaker' if $rvol == 255; + $rinc *= -1; + } + + $rvol += $rinc; + $lvol += $linc; + + $next = SDL2::GetTicks() + 10; + } + +} + +sub test_distance { + state $distance = 1; + state $dist_inc = 1; + state $ok = 1; + state $next = 0; + + if ( $ok && SDL2::GetTicks() >= $next ) { + $ok = SDL2::Mixer::SetDistance( 0, $distance ) + or warn sprintf 'SetDistance( 0, %s ) failed: %s', + $distance, SDL2::GetError; + + if ( $distance == 255 ) { + say 'Distance at furthest point'; + $dist_inc *= -1; + } + elsif ( $distance == 0 ) { + say 'Distance at nearest point'; + $dist_inc *= -1; + } + + $distance += $dist_inc; + $next = SDL2::GetTicks() + 15; + } +} + +sub test_position { + state $distance = 1; + state $dist_inc = 1; + state $angle = 0; + state $angle_inc = 1; + state $ok = 1; + state $next = 0; + + if ( $ok && SDL2::GetTicks() >= $next ) { + $ok = SDL2::Mixer::SetPosition( 0, $angle, $distance ) + or warn sprintf 'SetPosition( 0, %s, %s ) failed: %s', + $angle, $distance, SDL2::GetError; + + if ( $angle == 0 ) { + say 'Due north, now rotating clockwise'; + $angle_inc = 1; + } + elsif ( $angle == 360 ) { + say 'Due north, now rotating counter-clockwise'; + $angle_inc = -1; + } + + $distance += $dist_inc; + + if ( $distance < 0 ) { + say 'Distance is very, very near. Stepping away by threes...'; + ( $distance, $dist_inc ) = ( 0, 3 ); + } + elsif ( $distance > 255 ) { + say 'Distance is very, very far. Stepping towards by threes...'; + ( $distance, $dist_inc ) = ( 255, -3 ); + } + + $angle += $angle_inc; + $next = SDL2::GetTicks() + 30; + } +} diff --git a/lib/SDL2/Raw/Mixer.pm b/lib/SDL2/Raw/Mixer.pm index e9de1f4..f0c80e6 100644 --- a/lib/SDL2/Raw/Mixer.pm +++ b/lib/SDL2/Raw/Mixer.pm @@ -6,6 +6,7 @@ use warnings; use SDL2::Raw; use FFI::CheckLib; use FFI::Platypus 1.00; +use FFI::C; use Ref::Util; BEGIN { @@ -50,6 +51,7 @@ BEGIN { } use constant { + CHANNEL_POST => -2, DEFAULT_FREQUENCY => 44_100, DEFAULT_FORMAT => SDL2::BYTEORDER == SDL2::LIL_ENDIAN ? SDL2::AUDIO_S16LSB : SDL2::AUDIO_S16MSB, DEFAULT_CHANNELS => 2, @@ -59,10 +61,21 @@ use constant { my $ffi = FFI::Platypus->new( api => 1 ); $ffi->lib( find_lib_or_exit lib => 'SDL_mixer' ); +FFI::C->ffi($ffi); $ffi->mangler( sub { 'Mix_' . shift } ); -$ffi->type( opaque => 'Mix_Chunk' ); -$ffi->type( opaque => 'Mix_Music' ); -$ffi->type( int => 'Mix_MusicType' ); + +package SDL2::Mixer::Chunk { + FFI::C->struct( Mix_Chunk => [ + allocated => 'int', + abuf => 'opaque', + alen => 'uint32', + volume => 'uint8', + ]); +} + +$ffi->type( opaque => 'Mix_Music' ); +$ffi->type( int => 'Mix_MusicType' ); +$ffi->type( '(int, opaque, int, opaque)->void' => 'Mix_EffectFunc' ); ## General @@ -84,7 +97,7 @@ $ffi->attach( QuickLoad_WAV => ['uint8*' ] => 'Mix_Chunk' ); $ffi->attach( QuickLoad_RAW => ['uint8*' ] => 'Mix_Chunk' ); $ffi->attach( VolumeChunk => ['Mix_Chunk', 'int'] => 'Mix_Chunk' ); $ffi->attach( FreeChunk => ['Mix_Chunk' ] => 'void' ); -sub LoadWav { LoadWAV_RW( SDL2::RWFromFile( shift, 'rb' ), 1 ) } +sub LoadWAV { LoadWAV_RW( SDL2::RWFromFile( shift, 'rb' ), 1 ) } ## Channels @@ -102,7 +115,7 @@ $ffi->attach( FadingChannel => ['int' ] => $ffi->attach( GetChunk => ['int' ] => 'Mix_Chunk' ); $ffi->attach( ChannelFinished => ['(int)->void' ] => 'void' => sub { # Untested my ( $xsub, $closure ) = @_; - $closure = $ffi->closure($closure) if Ref::Util::is_subref $closure; + $closure = $ffi->closure($closure) if Ref::Util::is_coderef $closure; $xsub->($closure); }); sub PlayChannel { PlayChannelTimed( @_, -1 ) } @@ -137,4 +150,15 @@ $ffi->attach( PausedMusic => [ ] => ' $ffi->attach( FadingMusic => [ ] => 'int' ); $ffi->attach( GetMusicHookData => [ ] => 'opaque' ); +## Effects + +$ffi->attach( RegisterEffect => ['int', 'Mix_EffectFunc' ] => 'void' ); +$ffi->attach( UnregisterEffect => ['int', '(opaque, opaque, int)->void', 'opaque'] => 'void' ); +$ffi->attach( UnregisterAllEffects => ['int' ] => 'int' ); +$ffi->attach( SetPostMix => ['(opaque, opaque, int)->void', 'opaque' ] => 'void' ); +$ffi->attach( SetPanning => ['int', 'uint8', 'uint8' ] => 'int' ); +$ffi->attach( SetDistance => ['int', 'uint8' ] => 'int' ); +$ffi->attach( SetPosition => ['int', 'sint16', 'uint8' ] => 'int' ); +$ffi->attach( SetReverseStereo => ['int', 'int' ] => 'int' ); + 1; From c972c01c21c73ff4b672a6b561d20d8f0bd8ecbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Sat, 26 Jun 2021 19:20:22 +0100 Subject: [PATCH 11/36] Give Rect and Point positional constructors --- lib/SDL2/Raw.pm | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 25529ee..2c8dd38 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1289,11 +1289,39 @@ package SDL2::Surface { package SDL2::Rect { use FFI::Platypus::Record; record_layout_1( int => 'x', int => 'y', int => 'w', int => 'h' ); + + { + # Give rect a positional constructor + no strict 'refs'; + no warnings 'redefine'; + + my $old = __PACKAGE__->can('new') or die; + my $new = sub { + shift->$old({ x => shift, y => shift, w => shift, h => shift }); + }; + my $name = __PACKAGE__ . '::new'; + + require Sub::Util; + *{$name} = Sub::Util::set_subname $name => $new; + } } package SDL2::Point { use FFI::Platypus::Record; record_layout_1( int => 'x', int => 'y' ); + + { + # Give point a positional constructor + no strict 'refs'; + no warnings 'redefine'; + + my $old = __PACKAGE__->can('new') or die; + my $new = sub { shift->$old({ x => shift, y => shift }) }; + my $name = __PACKAGE__ . '::new'; + + require Sub::Util; + *{$name} = Sub::Util::set_subname $name => $new; + } } package SDL2::RendererInfo { From 23d834ff6f684f912ad34780b16eb8c0e0fef9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Sat, 26 Jun 2021 19:20:46 +0100 Subject: [PATCH 12/36] Add software and hardware rendering tests --- lib/SDL2/Raw.pm | 4 ++-- t/hardware.t | 41 +++++++++++++++++++++++++++++++++++++++++ t/software.t | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 t/hardware.t create mode 100644 t/software.t diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 2c8dd38..cacf404 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1533,7 +1533,7 @@ $ffi->attach( RenderCopyEx => [qw( SDL_Renderer SDL_Texture SDL_Rect* SDL_Rect* # SDL_RenderDrawPointF $ffi->attach( RenderDrawPoints => [qw( SDL_Renderer int[] int )] => 'int' ); # SDL_RenderDrawPointsF -# SDL_RenderDrawRect +$ffi->attach( RenderDrawRect => [qw( SDL_Renderer SDL_Rect* )] => 'int' ); # SDL_RenderDrawRectF # SDL_RenderDrawRects # SDL_RenderDrawRectsF @@ -1541,7 +1541,7 @@ $ffi->attach( RenderDrawPoints => [qw( SDL_Renderer int[] int )] => 'int' ); # SDL_RendererFlags # SDL_RendererFlip # SDL_RendererInfo -# SDL_RenderFillRect +$ffi->attach( RenderFillRect => [qw( SDL_Renderer SDL_Rect* )] => 'int' ); # SDL_RenderFillRectF # SDL_RenderFillRects # SDL_RenderFillRectsF diff --git a/t/hardware.t b/t/hardware.t new file mode 100644 index 0000000..5c2ab72 --- /dev/null +++ b/t/hardware.t @@ -0,0 +1,41 @@ +#!/usr/bin/env perl + +use Test2::V0; +use SDL2::Raw; + +skip_all 'No video support' if SDL2::Init( SDL2::INIT_VIDEO ) < 0; + +my $window = SDL2::CreateWindow( + 'FIRST WINDOW', + SDL2::WINDOWPOS_UNDEFINED, + SDL2::WINDOWPOS_UNDEFINED, + 200, 200, + SDL2::WINDOW_HIDDEN | SDL2::WINDOW_OPENGL, +) or die 'Error creating SDL window: ' . SDL2::GetError; + +my $renderer = SDL2::CreateRenderer( $window, -1, SDL2::RENDERER_ACCELERATED ) + or die 'Error creating SDL renderer: ' . SDL2::GetError; + +my $rect = SDL2::Rect->new( 0, 0, 4, 4 ); + +is SDL2::SetRenderDrawColor( $renderer, 0, 0, 0, 0xff ), 0, + 'Can set draw color'; + +is SDL2::RenderClear($renderer), 0, 'Can clear renderer'; + +is SDL2::SetRenderDrawColor( $renderer, 0, 0xff, 0, 0xff ), 0, + 'Can change draw color'; + +is SDL2::RenderFillRect( $renderer, $rect ), 0, 'Can fill rect'; + +my $rect2 = SDL2::Rect->new( 4, 4, 10, 10 ); + +is SDL2::RenderDrawRect( $renderer, $rect2 ), 0, 'Can draw rect'; + +SDL2::RenderPresent($renderer); + +SDL2::Quit; + +diag 'FINISHED: ' . ( SDL2::GetError || 'No error' ); + +done_testing; diff --git a/t/software.t b/t/software.t new file mode 100644 index 0000000..d6fc0fe --- /dev/null +++ b/t/software.t @@ -0,0 +1,41 @@ +#!/usr/bin/env perl + +use Test2::V0; +use SDL2::Raw; + +skip_all 'No video support' if SDL2::Init( SDL2::INIT_VIDEO ) < 0; + +my $window = SDL2::CreateWindow( + 'FIRST WINDOW', + SDL2::WINDOWPOS_UNDEFINED, + SDL2::WINDOWPOS_UNDEFINED, + 200, 200, + SDL2::WINDOW_HIDDEN, +) or die 'Error creating SDL window: ' . SDL2::GetError; + +my $renderer = SDL2::CreateRenderer( $window, -1, SDL2::RENDERER_SOFTWARE ) + or die 'Error creating SDL renderer: ' . SDL2::GetError; + +my $rect = SDL2::Rect->new( 0, 0, 4, 4 ); + +is SDL2::SetRenderDrawColor( $renderer, 0, 0, 0, 0xff ), 0, + 'Can set draw color'; + +is SDL2::RenderClear($renderer), 0, 'Can clear renderer'; + +is SDL2::SetRenderDrawColor( $renderer, 0, 0xff, 0, 0xff ), 0, + 'Can change draw color'; + +is SDL2::RenderFillRect( $renderer, $rect ), 0, 'Can fill rect'; + +my $rect2 = SDL2::Rect->new( 4, 4, 10, 10 ); + +is SDL2::RenderDrawRect( $renderer, $rect2 ), 0, 'Can draw rect'; + +SDL2::RenderPresent($renderer); + +SDL2::Quit; + +diag 'FINISHED: ' . ( SDL2::GetError || 'No error' ); + +done_testing; From 8e732cecf66dc72241e068efa621e5664e4df673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Sat, 26 Jun 2021 19:21:01 +0100 Subject: [PATCH 13/36] Finish implementing log functions and test --- lib/SDL2/Raw.pm | 26 +++++++++++++++++--------- t/log.t | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 t/log.t diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index cacf404..8201591 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1731,15 +1731,23 @@ $ffi->attach( GetVersion => ['SDL_Version'] => 'void' ); ## Logging -$ffi->attach( Log => [ 'string'] => 'void' => sub { my $xs = shift; $xs->( sprintf shift, @_ ) } ); -$ffi->attach( LogCritical => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); -$ffi->attach( LogDebug => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); -$ffi->attach( LogError => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); -$ffi->attach( LogInfo => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); -$ffi->attach( LogMessage => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); -$ffi->attach( LogMessageV => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); -$ffi->attach( LogVerbose => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); -$ffi->attach( LogWarn => ['int', 'string'] => 'void' => sub { my $xs = shift; $xs->( shift, sprintf shift, @_ ) } ); +$ffi->attach( Log => [qw( string )] => 'void' => sub { $_[0]->( sprintf $_[1], @_[ 2 .. $#_ ] ) } ); +$ffi->attach( LogCritical => [qw( int string )] => 'void' => sub { $_[0]->( $_[1], sprintf $_[2], @_[ 3 .. $#_ ] ) } ); +$ffi->attach( LogDebug => [qw( int string )] => 'void' => sub { $_[0]->( $_[1], sprintf $_[2], @_[ 3 .. $#_ ] ) } ); +$ffi->attach( LogError => [qw( int string )] => 'void' => sub { $_[0]->( $_[1], sprintf $_[2], @_[ 3 .. $#_ ] ) } ); +$ffi->attach( LogInfo => [qw( int string )] => 'void' => sub { $_[0]->( $_[1], sprintf $_[2], @_[ 3 .. $#_ ] ) } ); +$ffi->attach( LogVerbose => [qw( int string )] => 'void' => sub { $_[0]->( $_[1], sprintf $_[2], @_[ 3 .. $#_ ] ) } ); +$ffi->attach( LogWarn => [qw( int string )] => 'void' => sub { $_[0]->( $_[1], sprintf $_[2], @_[ 3 .. $#_ ] ) } ); +$ffi->attach( LogMessage => [qw( int int string )] => 'void' => sub { $_[0]->( $_[1], $_[2], sprintf $_[3], @_[ 4 .. $#_ ] ) } ); +$ffi->attach( LogMessageV => [qw( int int string )] => 'void' => sub { $_[0]->( $_[1], $_[2], sprintf $_[3], @_[ 4 .. $#_ ] ) } ); +$ffi->attach( LogSetPriority => [qw( int int )] => 'void' ); +$ffi->attach( LogGetPriority => [qw( int )] => 'int' ); +$ffi->attach( LogSetAllPriority => [qw( int )] => 'void' ); +$ffi->attach( LogResetPriorities => [qw( )] => 'void' ); + +# TODO +sub LogGetOutputFunction { ... } +sub LogSetOutputFunction { ... } # Clean helper functions delete $SDL2::{$_} for qw( enum pixel_format ); diff --git a/t/log.t b/t/log.t new file mode 100644 index 0000000..b27f098 --- /dev/null +++ b/t/log.t @@ -0,0 +1,40 @@ +use Test2::V0; +use SDL2::Raw; +use Capture::Tiny 'capture_stderr'; + +sub test_output (&$$) { + my ( $code, $want, $msg ) = @_; + my ($out) = capture_stderr { $code->() }; + chomp $out; + is $out, $want, $msg // ''; +} + +SDL2::LogSetAllPriority( SDL2::LOG_PRIORITY_VERBOSE ); + +is SDL2::LogGetPriority(0), SDL2::LOG_PRIORITY_VERBOSE, + 'Could set log priority'; + +test_output { SDL2::Log( 'Default %d', 10 ) } 'INFO: Default 10', 'SDL2::Log'; +test_output { SDL2::LogVerbose( 0, 'Verbose %d', 10 ) } 'VERBOSE: Verbose 10', 'SDL2::LogVerbose'; +test_output { SDL2::LogDebug( 0, 'Debug %d', 10 ) } 'DEBUG: Debug 10', 'SDL2::LogDebug'; +test_output { SDL2::LogInfo( 0, 'Info %d', 10 ) } 'INFO: Info 10', 'SDL2::LogInfo'; +test_output { SDL2::LogWarn( 0, 'Warn %d', 10 ) } 'WARN: Warn 10', 'SDL2::LogWarn'; +test_output { SDL2::LogError( 0, 'Error %d', 10 ) } 'ERROR: Error 10', 'SDL2::LogError'; + +SDL2::LogResetPriorities; + +isnt SDL2::LogGetPriority(0), SDL2::LOG_PRIORITY_VERBOSE, + 'Could reset log priority'; + +test_output { SDL2::Log( 'Default %d', 10 ) } 'INFO: Default 10', 'SDL2::Log'; +test_output { SDL2::LogVerbose( 0, 'Verbose %d', 10 ) } '', 'SDL2::LogVerbose'; +test_output { SDL2::LogDebug( 0, 'Debug %d', 10 ) } '', 'SDL2::LogDebug'; +test_output { SDL2::LogInfo( 0, 'Info %d', 10 ) } 'INFO: Info 10', 'SDL2::LogInfo'; +test_output { SDL2::LogWarn( 0, 'Warn %d', 10 ) } 'WARN: Warn 10', 'SDL2::LogWarn'; +test_output { SDL2::LogError( 0, 'Error %d', 10 ) } 'ERROR: Error 10', 'SDL2::LogError'; + +test_output { + SDL2::LogMessage( 0, SDL2::LOG_PRIORITY_CRITICAL, 'Critical %d', 10 ) +} 'CRITICAL: Critical 10', 'SDL2::LogMessage'; + +done_testing; From e7f5aa3b706635cbddb3ac7955cd205e534793f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Sat, 26 Jun 2021 19:28:52 +0100 Subject: [PATCH 14/36] Add namespace test --- lib/SDL2/Raw.pm | 24 +- t/namespace.t | 1002 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1014 insertions(+), 12 deletions(-) create mode 100644 t/namespace.t diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 8201591..7488169 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -4,7 +4,7 @@ package SDL2; use strict; use warnings; -use FFI::CheckLib; +use FFI::CheckLib (); use FFI::Platypus 1.00; use FFI::C; use Ref::Util; @@ -12,7 +12,7 @@ use Ref::Util; my $ffi; BEGIN { $ffi = FFI::Platypus->new( api => 1 ); - $ffi->lib( find_lib_or_exit lib => 'SDL2' ); + $ffi->lib( FFI::CheckLib::find_lib_or_exit lib => 'SDL2' ); FFI::C->ffi($ffi); } @@ -314,7 +314,7 @@ BEGIN { POWERSTATE_CHARGING POWERSTATE_CHARGED )], - eventaction => [qw( + EventAction => [qw( ADDEVENT PEEKEVENT GETEVENT @@ -1044,7 +1044,7 @@ BEGIN { } BEGIN { - my sub define_fourcc { ord(shift) << 0 | ord(shift) << 8 | ord(shift) << 16 | ord(shift) << 24 } + my $define_fourcc = sub { ord(shift) << 0 | ord(shift) << 8 | ord(shift) << 16 | ord(shift) << 24 }; enum( PixelFormatEnum => { @@ -1060,14 +1060,14 @@ BEGIN { PIXELFORMAT_BGRA32 => PIXELFORMAT_ARGB8888, PIXELFORMAT_ABGR32 => PIXELFORMAT_RGBA8888, ), - PIXELFORMAT_YV12 => define_fourcc( 'Y', 'V', '1', '2' ), - PIXELFORMAT_IYUV => define_fourcc( 'I', 'Y', 'U', 'V' ), - PIXELFORMAT_YUY2 => define_fourcc( 'Y', 'U', 'Y', '2' ), - PIXELFORMAT_UYVY => define_fourcc( 'U', 'Y', 'V', 'Y' ), - PIXELFORMAT_YVYU => define_fourcc( 'Y', 'V', 'Y', 'U' ), - PIXELFORMAT_NV12 => define_fourcc( 'N', 'V', '1', '2' ), - PIXELFORMAT_NV21 => define_fourcc( 'N', 'V', '2', '1' ), - PIXELFORMAT_EXTERNAL_OES => define_fourcc( 'O', 'E', 'S', ' ' ), + PIXELFORMAT_YV12 => $define_fourcc->( 'Y', 'V', '1', '2' ), + PIXELFORMAT_IYUV => $define_fourcc->( 'I', 'Y', 'U', 'V' ), + PIXELFORMAT_YUY2 => $define_fourcc->( 'Y', 'U', 'Y', '2' ), + PIXELFORMAT_UYVY => $define_fourcc->( 'U', 'Y', 'V', 'Y' ), + PIXELFORMAT_YVYU => $define_fourcc->( 'Y', 'V', 'Y', 'U' ), + PIXELFORMAT_NV12 => $define_fourcc->( 'N', 'V', '1', '2' ), + PIXELFORMAT_NV21 => $define_fourcc->( 'N', 'V', '2', '1' ), + PIXELFORMAT_EXTERNAL_OES => $define_fourcc->( 'O', 'E', 'S', ' ' ), } ); } diff --git a/t/namespace.t b/t/namespace.t new file mode 100644 index 0000000..705d31c --- /dev/null +++ b/t/namespace.t @@ -0,0 +1,1002 @@ +#!/usr/bin/env perl + +use Test2::V0; +use SDL2::Raw; + +is [ sort keys %SDL2:: ], [qw( + ADDEVENT + APP_DIDENTERBACKGROUND + APP_DIDENTERFOREGROUND + APP_LOWMEMORY + APP_TERMINATING + APP_WILLENTERBACKGROUND + APP_WILLENTERFOREGROUND + ARRAYORDER_ABGR + ARRAYORDER_ARGB + ARRAYORDER_BGR + ARRAYORDER_BGRA + ARRAYORDER_NONE + ARRAYORDER_RGB + ARRAYORDER_RGBA + AUDIODEVICEADDED + AUDIODEVICEREMOVED + AUDIO_F32 + AUDIO_F32LSB + AUDIO_F32MSB + AUDIO_MASK_BITSIZE + AUDIO_MASK_DATATYPE + AUDIO_MASK_ENDIAN + AUDIO_MASK_SIGNED + AUDIO_S16 + AUDIO_S16LSB + AUDIO_S16MSB + AUDIO_S32 + AUDIO_S32LSB + AUDIO_S32MSB + AUDIO_S8 + AUDIO_U16 + AUDIO_U16LSB + AUDIO_U16MSB + AUDIO_U8 + ArrayOrder + BEGIN + BIG_ENDIAN + BITMAPORDER_1234 + BITMAPORDER_4321 + BITMAPORDER_NONE + BLENDFACTOR_DST_ALPHA + BLENDFACTOR_DST_COLOR + BLENDFACTOR_ONE + BLENDFACTOR_ONE_MINUS_DST_ALPHA + BLENDFACTOR_ONE_MINUS_DST_COLOR + BLENDFACTOR_ONE_MINUS_SRC_ALPHA + BLENDFACTOR_ONE_MINUS_SRC_COLOR + BLENDFACTOR_SRC_ALPHA + BLENDFACTOR_SRC_COLOR + BLENDFACTOR_ZERO + BLENDMODE_ADD + BLENDMODE_BLEND + BLENDMODE_INVALID + BLENDMODE_MOD + BLENDMODE_MUL + BLENDMODE_NONE + BLENDOPERATION_ADD + BLENDOPERATION_MAXIMUM + BLENDOPERATION_MINIMUM + BLENDOPERATION_REV_SUBTRACT + BLENDOPERATION_SUBTRACT + BUTTON_LEFT + BUTTON_MIDDLE + BUTTON_RIGHT + BUTTON_X1 + BUTTON_X2 + BYTEORDER + BitmapOrder + BlendFactor + BlendMode + BlendOperation + BlitSurface + CLIPBOARDUPDATE + CONTROLLERAXISMOTION + CONTROLLERBUTTONDOWN + CONTROLLERBUTTONUP + CONTROLLERDEVICEADDED + CONTROLLERDEVICEREMAPPED + CONTROLLERDEVICEREMOVED + CONTROLLERSENSORUPDATE + CONTROLLERTOUCHPADDOWN + CONTROLLERTOUCHPADMOTION + CONTROLLERTOUCHPADUP + ClearError + Config + ControllerAxisEvent:: + ControllerButtonEvent:: + ControllerDeviceEvent:: + CreateRGBSurfaceFrom + CreateRenderer + CreateTexture + CreateTextureFromSurface + CreateWindow + DISPLAYEVENT + DISPLAYEVENT_CONNECTED + DISPLAYEVENT_DISCONNECTED + DISPLAYEVENT_NONE + DISPLAYEVENT_ORIENTATION + DOLLARGESTURE + DOLLARRECORD + DROPBEGIN + DROPCOMPLETE + DROPFILE + DROPTEXT + Delay + DestroyRenderer + DestroyTexture + DestroyWindow + DisplayEventID + DisplayOrientation + DropEvent:: + Event:: + EventAction + EventType + FINGERDOWN + FINGERMOTION + FINGERUP + FIRSTEVENT + FLIP_HORIZONTAL + FLIP_NONE + FLIP_VERTICAL + FreeSurface + GETEVENT + GLContextResetNotification + GL_ACCELERATED_VISUAL + GL_ACCUM_ALPHA_SIZE + GL_ACCUM_BLUE_SIZE + GL_ACCUM_GREEN_SIZE + GL_ACCUM_RED_SIZE + GL_ALPHA_SIZE + GL_BLUE_SIZE + GL_BUFFER_SIZE + GL_CONTEXT_DEBUG_FLAG + GL_CONTEXT_EGL + GL_CONTEXT_FLAGS + GL_CONTEXT_FORWARD_COMPATIBLE_FLAG + GL_CONTEXT_MAJOR_VERSION + GL_CONTEXT_MINOR_VERSION + GL_CONTEXT_NO_ERROR + GL_CONTEXT_PROFILE_COMPATIBILITY + GL_CONTEXT_PROFILE_CORE + GL_CONTEXT_PROFILE_ES + GL_CONTEXT_PROFILE_MASK + GL_CONTEXT_RELEASE_BEHAVIOR + GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH + GL_CONTEXT_RELEASE_BEHAVIOR_NONE + GL_CONTEXT_RESET_ISOLATION_FLAG + GL_CONTEXT_RESET_LOSE_CONTEXT + GL_CONTEXT_RESET_NOTIFICATION + GL_CONTEXT_RESET_NO_NOTIFICATION + GL_CONTEXT_ROBUST_ACCESS_FLAG + GL_DEPTH_SIZE + GL_DOUBLEBUFFER + GL_FRAMEBUFFER_SRGB_CAPABLE + GL_GREEN_SIZE + GL_MULTISAMPLEBUFFERS + GL_MULTISAMPLESAMPLES + GL_RED_SIZE + GL_RETAINED_BACKING + GL_SHARE_WITH_CURRENT_CONTEXT + GL_STENCIL_SIZE + GL_STEREO + GLattr + GLcontextFlag + GLcontextReleaseFlag + GLprofile + GameControllerClose + GameControllerGetJoystick + GameControllerNameForIndex + GameControllerOpen + GetError + GetRenderTarget + GetRendererInfo + GetTextureAlphaMod + GetTextureBlendMode + GetTextureColorMod + GetTicks + GetVersion + GetWindowSurface + GetWindowWMInfo + HINT_DEFAULT + HINT_NORMAL + HINT_OVERRIDE + HintPriority + INIT_AUDIO + INIT_EVENTS + INIT_EVERYTHING + INIT_GAMECONTROLLER + INIT_HAPTIC + INIT_JOYSTICK + INIT_NOPARACHUTE + INIT_SENSOR + INIT_TIMER + INIT_VIDEO + Init + InitSubSystem + IsGameController + JOYAXISMOTION + JOYBALLMOTION + JOYBUTTONDOWN + JOYBUTTONUP + JOYDEVICEADDED + JOYDEVICEREMOVED + JOYHATMOTION + JoystickInstanceID + KEYDOWN + KEYMAPCHANGED + KEYUP + K_0 + K_1 + K_2 + K_3 + K_4 + K_5 + K_6 + K_7 + K_8 + K_9 + K_AC_BACK + K_AC_BOOKMARKS + K_AC_FORWARD + K_AC_HOME + K_AC_REFRESH + K_AC_SEARCH + K_AC_STOP + K_AGAIN + K_ALTERASE + K_AMPERSAND + K_APP1 + K_APP2 + K_APPLICATION + K_ASTERISK + K_AT + K_AUDIOFASTFORWARD + K_AUDIOMUTE + K_AUDIONEXT + K_AUDIOPLAY + K_AUDIOPREV + K_AUDIOREWIND + K_AUDIOSTOP + K_BACKQUOTE + K_BACKSLASH + K_BACKSPACE + K_BRIGHTNESSDOWN + K_BRIGHTNESSUP + K_CALCULATOR + K_CANCEL + K_CAPSLOCK + K_CARET + K_CLEAR + K_CLEARAGAIN + K_COLON + K_COMMA + K_COMPUTER + K_COPY + K_CRSEL + K_CURRENCYSUBUNIT + K_CURRENCYUNIT + K_CUT + K_DECIMALSEPARATOR + K_DELETE + K_DISPLAYSWITCH + K_DOLLAR + K_DOWN + K_EJECT + K_END + K_EQUALS + K_ESCAPE + K_EXCLAIM + K_EXECUTE + K_EXSEL + K_F1 + K_F10 + K_F11 + K_F12 + K_F13 + K_F14 + K_F15 + K_F16 + K_F17 + K_F18 + K_F19 + K_F2 + K_F20 + K_F21 + K_F22 + K_F23 + K_F24 + K_F3 + K_F4 + K_F5 + K_F6 + K_F7 + K_F8 + K_F9 + K_FIND + K_GREATER + K_HASH + K_HELP + K_HOME + K_INSERT + K_KBDILLUMDOWN + K_KBDILLUMTOGGLE + K_KBDILLUMUP + K_KP_0 + K_KP_00 + K_KP_000 + K_KP_1 + K_KP_2 + K_KP_3 + K_KP_4 + K_KP_5 + K_KP_6 + K_KP_7 + K_KP_8 + K_KP_9 + K_KP_A + K_KP_AMPERSAND + K_KP_AT + K_KP_B + K_KP_BACKSPACE + K_KP_BINARY + K_KP_C + K_KP_CLEAR + K_KP_CLEARENTRY + K_KP_COLON + K_KP_COMMA + K_KP_D + K_KP_DBLAMPERSAND + K_KP_DBLVERTICALBAR + K_KP_DECIMAL + K_KP_DIVIDE + K_KP_E + K_KP_ENTER + K_KP_EQUALS + K_KP_EQUALSAS400 + K_KP_EXCLAM + K_KP_F + K_KP_GREATER + K_KP_HASH + K_KP_HEXADECIMAL + K_KP_LEFTBRACE + K_KP_LEFTPAREN + K_KP_LESS + K_KP_MEMADD + K_KP_MEMCLEAR + K_KP_MEMDIVIDE + K_KP_MEMMULTIPLY + K_KP_MEMRECALL + K_KP_MEMSTORE + K_KP_MEMSUBTRACT + K_KP_MINUS + K_KP_MULTIPLY + K_KP_OCTAL + K_KP_PERCENT + K_KP_PERIOD + K_KP_PLUS + K_KP_PLUSMINUS + K_KP_POWER + K_KP_RIGHTBRACE + K_KP_RIGHTPAREN + K_KP_SPACE + K_KP_TAB + K_KP_VERTICALBAR + K_KP_XOR + K_LALT + K_LCTRL + K_LEFT + K_LEFTBRACKET + K_LEFTPAREN + K_LESS + K_LGUI + K_LSHIFT + K_MAIL + K_MEDIASELECT + K_MENU + K_MINUS + K_MODE + K_MUTE + K_NUMLOCKCLEAR + K_OPER + K_OUT + K_PAGEDOWN + K_PAGEUP + K_PASTE + K_PAUSE + K_PERCENT + K_PERIOD + K_PLUS + K_POWER + K_PRINTSCREEN + K_PRIOR + K_QUESTION + K_QUOTE + K_QUOTEDBL + K_RALT + K_RCTRL + K_RETURN + K_RETURN2 + K_RGUI + K_RIGHT + K_RIGHTBRACKET + K_RIGHTPAREN + K_RSHIFT + K_SCANCODE_MASK + K_SCROLLLOCK + K_SELECT + K_SEMICOLON + K_SEPARATOR + K_SLASH + K_SLEEP + K_SPACE + K_STOP + K_SYSREQ + K_TAB + K_THOUSANDSSEPARATOR + K_UNDERSCORE + K_UNDO + K_UNKNOWN + K_UP + K_VOLUMEDOWN + K_VOLUMEUP + K_WWW + K_a + K_b + K_c + K_d + K_e + K_f + K_g + K_h + K_i + K_j + K_k + K_l + K_m + K_n + K_o + K_p + K_q + K_r + K_s + K_t + K_u + K_v + K_w + K_x + K_y + K_z + KeyboardEvent:: + Keycode + LASTEVENT + LIL_ENDIAN + LOCALECHANGED + LOG_CATEGORY_APPLICATION + LOG_CATEGORY_ASSERT + LOG_CATEGORY_AUDIO + LOG_CATEGORY_CUSTOM + LOG_CATEGORY_ERROR + LOG_CATEGORY_INPUT + LOG_CATEGORY_RENDER + LOG_CATEGORY_RESERVED1 + LOG_CATEGORY_RESERVED10 + LOG_CATEGORY_RESERVED2 + LOG_CATEGORY_RESERVED3 + LOG_CATEGORY_RESERVED4 + LOG_CATEGORY_RESERVED5 + LOG_CATEGORY_RESERVED6 + LOG_CATEGORY_RESERVED7 + LOG_CATEGORY_RESERVED8 + LOG_CATEGORY_RESERVED9 + LOG_CATEGORY_SYSTEM + LOG_CATEGORY_TEST + LOG_CATEGORY_VIDEO + LOG_PRIORITY_CRITICAL + LOG_PRIORITY_DEBUG + LOG_PRIORITY_ERROR + LOG_PRIORITY_INFO + LOG_PRIORITY_VERBOSE + LOG_PRIORITY_WARN + LoadBMP + LoadBMP_RW + LockTexture + Log + LogCategory + LogCritical + LogDebug + LogError + LogGetOutputFunction + LogGetPriority + LogInfo + LogMessage + LogMessageV + LogPriority + LogResetPriorities + LogSetAllPriority + LogSetOutputFunction + LogSetPriority + LogVerbose + LogWarn + MIX_MAXVOLUME + MOUSEBUTTONDOWN + MOUSEBUTTONUP + MOUSEMOTION + MOUSEWHEEL + MOUSEWHEEL_FLIPPED + MOUSEWHEEL_NORMAL + MULTIGESTURE + MouseButtonEvent:: + MouseMotionEvent:: + MouseWheelDirection + MouseWheelEvent:: + NUM_LOG_PRIORITIES + NUM_SCANCODES + NUM_SYSTEM_CURSORS + ORIENTATION_LANDSCAPE + ORIENTATION_LANDSCAPE_FLIPPED + ORIENTATION_PORTRAIT + ORIENTATION_PORTRAIT_FLIPPED + ORIENTATION_UNKNOWN + PACKEDLAYOUT_1010102 + PACKEDLAYOUT_1555 + PACKEDLAYOUT_2101010 + PACKEDLAYOUT_332 + PACKEDLAYOUT_4444 + PACKEDLAYOUT_5551 + PACKEDLAYOUT_565 + PACKEDLAYOUT_8888 + PACKEDLAYOUT_NONE + PACKEDORDER_ABGR + PACKEDORDER_ARGB + PACKEDORDER_BGRA + PACKEDORDER_BGRX + PACKEDORDER_NONE + PACKEDORDER_RGBA + PACKEDORDER_RGBX + PACKEDORDER_XBGR + PACKEDORDER_XRGB + PEEKEVENT + PIXELFORMAT_ABGR1555 + PIXELFORMAT_ABGR32 + PIXELFORMAT_ABGR4444 + PIXELFORMAT_ABGR8888 + PIXELFORMAT_ARGB1555 + PIXELFORMAT_ARGB2101010 + PIXELFORMAT_ARGB32 + PIXELFORMAT_ARGB4444 + PIXELFORMAT_ARGB8888 + PIXELFORMAT_BGR24 + PIXELFORMAT_BGR444 + PIXELFORMAT_BGR555 + PIXELFORMAT_BGR565 + PIXELFORMAT_BGR888 + PIXELFORMAT_BGRA32 + PIXELFORMAT_BGRA4444 + PIXELFORMAT_BGRA5551 + PIXELFORMAT_BGRA8888 + PIXELFORMAT_BGRX8888 + PIXELFORMAT_EXTERNAL_OES + PIXELFORMAT_INDEX1LSB + PIXELFORMAT_INDEX1MSB + PIXELFORMAT_INDEX4LSB + PIXELFORMAT_INDEX4MSB + PIXELFORMAT_INDEX8 + PIXELFORMAT_IYUV + PIXELFORMAT_NV12 + PIXELFORMAT_NV21 + PIXELFORMAT_RGB24 + PIXELFORMAT_RGB332 + PIXELFORMAT_RGB444 + PIXELFORMAT_RGB555 + PIXELFORMAT_RGB565 + PIXELFORMAT_RGB888 + PIXELFORMAT_RGBA32 + PIXELFORMAT_RGBA4444 + PIXELFORMAT_RGBA5551 + PIXELFORMAT_RGBA8888 + PIXELFORMAT_RGBX8888 + PIXELFORMAT_UNKNOWN + PIXELFORMAT_UYVY + PIXELFORMAT_XBGR1555 + PIXELFORMAT_XBGR4444 + PIXELFORMAT_XBGR8888 + PIXELFORMAT_XRGB1555 + PIXELFORMAT_XRGB4444 + PIXELFORMAT_XRGB8888 + PIXELFORMAT_YUY2 + PIXELFORMAT_YV12 + PIXELFORMAT_YVYU + PIXELTYPE_ARRAYF16 + PIXELTYPE_ARRAYF32 + PIXELTYPE_ARRAYU16 + PIXELTYPE_ARRAYU32 + PIXELTYPE_ARRAYU8 + PIXELTYPE_INDEX1 + PIXELTYPE_INDEX4 + PIXELTYPE_INDEX8 + PIXELTYPE_PACKED16 + PIXELTYPE_PACKED32 + PIXELTYPE_PACKED8 + PIXELTYPE_UNKNOWN + POWERSTATE_CHARGED + POWERSTATE_CHARGING + POWERSTATE_NO_BATTERY + POWERSTATE_ON_BATTERY + POWERSTATE_UNKNOWN + PackedLayout + PackedOrder + PixelFormat:: + PixelFormatEnum + PixelType + Point:: + PollEvent + PowerState + PushEvent + QUIT + Quit + QuitSubSystem + RENDERER_ACCELERATED + RENDERER_PRESENTVSYNC + RENDERER_SOFTWARE + RENDERER_TARGETTEXTURE + RENDER_DEVICE_RESET + RENDER_TARGETS_RESET + RWFromFile + Rect:: + RegisterEvents + RenderClear + RenderCopy + RenderCopyEx + RenderDrawPoints + RenderDrawRect + RenderFillRect + RenderPresent + RendererFlags + RendererFlip + RendererInfo:: + SCALEMODEBEST + SCALEMODELINEAR + SCALEMODENEAREST + SCANCODE_0 + SCANCODE_1 + SCANCODE_2 + SCANCODE_3 + SCANCODE_4 + SCANCODE_5 + SCANCODE_6 + SCANCODE_7 + SCANCODE_8 + SCANCODE_9 + SCANCODE_A + SCANCODE_AC_BACK + SCANCODE_AC_BOOKMARKS + SCANCODE_AC_FORWARD + SCANCODE_AC_HOME + SCANCODE_AC_REFRESH + SCANCODE_AC_SEARCH + SCANCODE_AC_STOP + SCANCODE_AGAIN + SCANCODE_ALTERASE + SCANCODE_APOSTROPHE + SCANCODE_APP1 + SCANCODE_APP2 + SCANCODE_APPLICATION + SCANCODE_AUDIOFASTFORWARD + SCANCODE_AUDIOMUTE + SCANCODE_AUDIONEXT + SCANCODE_AUDIOPLAY + SCANCODE_AUDIOPREV + SCANCODE_AUDIOREWIND + SCANCODE_AUDIOSTOP + SCANCODE_B + SCANCODE_BACKSLASH + SCANCODE_BACKSPACE + SCANCODE_BRIGHTNESSDOWN + SCANCODE_BRIGHTNESSUP + SCANCODE_C + SCANCODE_CALCULATOR + SCANCODE_CANCEL + SCANCODE_CAPSLOCK + SCANCODE_CLEAR + SCANCODE_CLEARAGAIN + SCANCODE_COMMA + SCANCODE_COMPUTER + SCANCODE_COPY + SCANCODE_CRSEL + SCANCODE_CURRENCYSUBUNIT + SCANCODE_CURRENCYUNIT + SCANCODE_CUT + SCANCODE_D + SCANCODE_DECIMALSEPARATOR + SCANCODE_DELETE + SCANCODE_DISPLAYSWITCH + SCANCODE_DOWN + SCANCODE_E + SCANCODE_EJECT + SCANCODE_END + SCANCODE_EQUALS + SCANCODE_ESCAPE + SCANCODE_EXECUTE + SCANCODE_EXSEL + SCANCODE_F + SCANCODE_F1 + SCANCODE_F10 + SCANCODE_F11 + SCANCODE_F12 + SCANCODE_F13 + SCANCODE_F14 + SCANCODE_F15 + SCANCODE_F16 + SCANCODE_F17 + SCANCODE_F18 + SCANCODE_F19 + SCANCODE_F2 + SCANCODE_F20 + SCANCODE_F21 + SCANCODE_F22 + SCANCODE_F23 + SCANCODE_F24 + SCANCODE_F3 + SCANCODE_F4 + SCANCODE_F5 + SCANCODE_F6 + SCANCODE_F7 + SCANCODE_F8 + SCANCODE_F9 + SCANCODE_FIND + SCANCODE_G + SCANCODE_GRAVE + SCANCODE_H + SCANCODE_HELP + SCANCODE_HOME + SCANCODE_I + SCANCODE_INSERT + SCANCODE_INTERNATIONAL1 + SCANCODE_INTERNATIONAL2 + SCANCODE_INTERNATIONAL3 + SCANCODE_INTERNATIONAL4 + SCANCODE_INTERNATIONAL5 + SCANCODE_INTERNATIONAL6 + SCANCODE_INTERNATIONAL7 + SCANCODE_INTERNATIONAL8 + SCANCODE_INTERNATIONAL9 + SCANCODE_J + SCANCODE_K + SCANCODE_KBDILLUMDOWN + SCANCODE_KBDILLUMTOGGLE + SCANCODE_KBDILLUMUP + SCANCODE_KP_0 + SCANCODE_KP_00 + SCANCODE_KP_000 + SCANCODE_KP_1 + SCANCODE_KP_2 + SCANCODE_KP_3 + SCANCODE_KP_4 + SCANCODE_KP_5 + SCANCODE_KP_6 + SCANCODE_KP_7 + SCANCODE_KP_8 + SCANCODE_KP_9 + SCANCODE_KP_A + SCANCODE_KP_AMPERSAND + SCANCODE_KP_AT + SCANCODE_KP_B + SCANCODE_KP_BACKSPACE + SCANCODE_KP_BINARY + SCANCODE_KP_C + SCANCODE_KP_CLEAR + SCANCODE_KP_CLEARENTRY + SCANCODE_KP_COLON + SCANCODE_KP_COMMA + SCANCODE_KP_D + SCANCODE_KP_DBLAMPERSAND + SCANCODE_KP_DBLVERTICALBAR + SCANCODE_KP_DECIMAL + SCANCODE_KP_DIVIDE + SCANCODE_KP_E + SCANCODE_KP_ENTER + SCANCODE_KP_EQUALS + SCANCODE_KP_EQUALSAS400 + SCANCODE_KP_EXCLAM + SCANCODE_KP_F + SCANCODE_KP_GREATER + SCANCODE_KP_HASH + SCANCODE_KP_HEXADECIMAL + SCANCODE_KP_LEFTBRACE + SCANCODE_KP_LEFTPAREN + SCANCODE_KP_LESS + SCANCODE_KP_MEMADD + SCANCODE_KP_MEMCLEAR + SCANCODE_KP_MEMDIVIDE + SCANCODE_KP_MEMMULTIPLY + SCANCODE_KP_MEMRECALL + SCANCODE_KP_MEMSTORE + SCANCODE_KP_MEMSUBTRACT + SCANCODE_KP_MINUS + SCANCODE_KP_MULTIPLY + SCANCODE_KP_OCTAL + SCANCODE_KP_PERCENT + SCANCODE_KP_PERIOD + SCANCODE_KP_PLUS + SCANCODE_KP_PLUSMINUS + SCANCODE_KP_POWER + SCANCODE_KP_RIGHTBRACE + SCANCODE_KP_RIGHTPAREN + SCANCODE_KP_SPACE + SCANCODE_KP_TAB + SCANCODE_KP_VERTICALBAR + SCANCODE_KP_XOR + SCANCODE_L + SCANCODE_LALT + SCANCODE_LANG1 + SCANCODE_LANG2 + SCANCODE_LANG3 + SCANCODE_LANG4 + SCANCODE_LANG5 + SCANCODE_LANG6 + SCANCODE_LANG7 + SCANCODE_LANG8 + SCANCODE_LANG9 + SCANCODE_LCTRL + SCANCODE_LEFT + SCANCODE_LEFTBRACKET + SCANCODE_LGUI + SCANCODE_LSHIFT + SCANCODE_M + SCANCODE_MAIL + SCANCODE_MEDIASELECT + SCANCODE_MENU + SCANCODE_MINUS + SCANCODE_MODE + SCANCODE_MUTE + SCANCODE_N + SCANCODE_NONUSBACKSLASH + SCANCODE_NONUSHASH + SCANCODE_NUMLOCKCLEAR + SCANCODE_O + SCANCODE_OPER + SCANCODE_OUT + SCANCODE_P + SCANCODE_PAGEDOWN + SCANCODE_PAGEUP + SCANCODE_PASTE + SCANCODE_PAUSE + SCANCODE_PERIOD + SCANCODE_POWER + SCANCODE_PRINTSCREEN + SCANCODE_PRIOR + SCANCODE_Q + SCANCODE_R + SCANCODE_RALT + SCANCODE_RCTRL + SCANCODE_RETURN + SCANCODE_RETURN2 + SCANCODE_RGUI + SCANCODE_RIGHT + SCANCODE_RIGHTBRACKET + SCANCODE_RSHIFT + SCANCODE_S + SCANCODE_SCROLLLOCK + SCANCODE_SELECT + SCANCODE_SEMICOLON + SCANCODE_SEPARATOR + SCANCODE_SLASH + SCANCODE_SLEEP + SCANCODE_SPACE + SCANCODE_STOP + SCANCODE_SYSREQ + SCANCODE_T + SCANCODE_TAB + SCANCODE_THOUSANDSSEPARATOR + SCANCODE_U + SCANCODE_UNDO + SCANCODE_UNKNOWN + SCANCODE_UP + SCANCODE_V + SCANCODE_VOLUMEDOWN + SCANCODE_VOLUMEUP + SCANCODE_W + SCANCODE_WWW + SCANCODE_X + SCANCODE_Y + SCANCODE_Z + SENSORUPDATE + SYSTEM_CURSOR_ARROW + SYSTEM_CURSOR_CROSSHAIR + SYSTEM_CURSOR_HAND + SYSTEM_CURSOR_IBEAM + SYSTEM_CURSOR_NO + SYSTEM_CURSOR_SIZEALL + SYSTEM_CURSOR_SIZENESW + SYSTEM_CURSOR_SIZENS + SYSTEM_CURSOR_SIZENWSE + SYSTEM_CURSOR_SIZEWE + SYSTEM_CURSOR_WAIT + SYSTEM_CURSOR_WAITARROW + SYSWMEVENT + SYSWM_ANDROID + SYSWM_COCOA + SYSWM_DIRECTFB + SYSWM_HAIKU + SYSWM_KMSDRM + SYSWM_MIR + SYSWM_OS2 + SYSWM_TYPE + SYSWM_UIKIT + SYSWM_UNKNOWN + SYSWM_VIVANTE + SYSWM_WAYLAND + SYSWM_WINDOWS + SYSWM_WINRT + SYSWM_X11 + ScaleMode + ScanCode + SetColorKey + SetError + SetRenderDrawColor + SetRenderTarget + SetTextureAlphaMod + SetTextureBlendMode + SetTextureColorMod + SetWindowIcon + SetWindowTitle + Surface:: + SysWMinfo:: + SystemCursor + TEXTEDITING + TEXTINPUT + TEXTUREACCESS_STATIC + TEXTUREACCESS_STREAMING + TEXTUREACCESS_TARGET + TEXTUREMODULATE_ALPHA + TEXTUREMODULATE_COLOR + TEXTUREMODULATE_NONE + TextEditingEvent:: + TextInputEvent:: + TextureAccess + TextureModulate + USEREVENT + UnlockTexture + UpdateWindowSurface + UpperBlit + UserEvent:: + Version:: + WINDOWEVENT + WINDOWEVENT_CLOSE + WINDOWEVENT_ENTER + WINDOWEVENT_EXPOSED + WINDOWEVENT_FOCUS_GAINED + WINDOWEVENT_FOCUS_LOST + WINDOWEVENT_HIDDEN + WINDOWEVENT_HIT_TEST + WINDOWEVENT_LEAVE + WINDOWEVENT_MAXIMIZED + WINDOWEVENT_MINIMIZED + WINDOWEVENT_MOVED + WINDOWEVENT_NONE + WINDOWEVENT_RESIZED + WINDOWEVENT_RESTORED + WINDOWEVENT_SHOWN + WINDOWEVENT_SIZE_CHANGED + WINDOWEVENT_TAKE_FOCUS + WINDOWPOS_CENTERED + WINDOWPOS_UNDEFINED + WINDOW_ALLOW_HIGHDPI + WINDOW_ALWAYS_ON_TOP + WINDOW_BORDERLESS + WINDOW_FOREIGN + WINDOW_FULLSCREEN + WINDOW_FULLSCREEN_DESKTOP + WINDOW_HIDDEN + WINDOW_INPUT_FOCUS + WINDOW_INPUT_GRABBED + WINDOW_KEYBOARD_GRABBED + WINDOW_MAXIMIZED + WINDOW_METAL + WINDOW_MINIMIZED + WINDOW_MOUSE_CAPTURE + WINDOW_MOUSE_FOCUS + WINDOW_MOUSE_GRABBED + WINDOW_OPENGL + WINDOW_POPUP_MENU + WINDOW_RESIZABLE + WINDOW_SHOWN + WINDOW_SKIP_TASKBAR + WINDOW_TOOLTIP + WINDOW_UTILITY + WINDOW_VULKAN + WasInit + WindowEvent:: + WindowEventID + WindowFlags + __ANON__ +)] => 'No unexpected methods in SDL2 namespace'; + +done_testing; From 0ae86f5fb96cce63b9dbe39909ec129131b986c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Sun, 27 Jun 2021 13:08:26 +0100 Subject: [PATCH 15/36] Implement remaining event types and sort in source --- lib/SDL2/Raw.pm | 263 ++++++++++++++++++++++++++++++++++++++++++------ t/namespace.t | 16 +++ 2 files changed, 246 insertions(+), 33 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 7488169..6a397a6 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1072,22 +1072,176 @@ BEGIN { ); } - -$ffi->type( sint32 => 'SDL_BlendMode' ); +$ffi->type( sint32 => 'SDL_BlendMode' ); $ffi->type( sint32 => 'SDL_JoystickID' ); +$ffi->type( sint64 => 'SDL_TouchID' ); +$ffi->type( sint64 => 'SDL_FingerID' ); +$ffi->type( sint64 => 'SDL_GestureID' ); + $ffi->mangler( sub { 'SDL_' . shift } ); -package SDL2::WindowEvent { - FFI::C->struct( SDL_WindowEvent => [ +package SDL2::AudioDeviceEvent { + FFI::C->struct( SDL_AudioDeviceEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'uint32', + iscapture => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + padding3 => 'uint8', + ]); +} + +package SDL2::ControllerAxisEvent { + FFI::C->struct( SDL_ControllerAxisEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + axis => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + padding3 => 'uint8', + value => 'sint16', + padding4 => 'uint16', + ]); +} + +package SDL2::ControllerButtonEvent { + FFI::C->struct( SDL_ControllerButtonEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + button => 'uint8', + state => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + ]); +} + +package SDL2::ControllerDeviceEvent { + FFI::C->struct( SDL_ControllerDeviceEvent => [ type => 'uint32', timestamp => 'uint32', + which => 'SDL_JoystickID', + ]); +} + +package SDL2::ControllerTouchpadEvent { + FFI::C->struct( SDL_ControllerTouchpadEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + touchpad => 'sint32', + finger => 'sint32', + x => 'float', + y => 'float', + pressure => 'float', + ]); +} + +package SDL2::ControllerSensorEvent { + FFI::C->struct( SDL_ControllerSensorEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + data => 'float[3]', + ]); +} + +package SDL2::DisplayEvent { + FFI::C->struct( SDL_DisplayEvent => [ + type => 'uint32', + timestamp => 'uint32', + display => 'uint32', + event => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + padding3 => 'uint8', + data1 => 'sint32', + ]); +} + +package SDL2::DollarGestureEvent { + FFI::C->struct( SDL_DollarGestureEvent => [ + type => 'uint32', + timestamp => 'uint32', + touchId => 'SDL_TouchID', + gestureId => 'SDL_GestureID', + numFingers => 'uint32', + error => 'float', + x => 'float', + y => 'float', + ]); +} + +package SDL2::DropEvent { + FFI::C->struct( SDL_DropEvent => [ + type => 'uint32', + timestamp => 'uint32', + _file => 'opaque', windowID => 'uint32', - event => 'uint8', + ]); + + sub file { $ffi->cast( opaque => string => shift->_file ) } +} + +package SDL2::JoyAxisEvent { + FFI::C->struct( SDL_JoyAxisEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + axis => 'uint8', padding1 => 'uint8', padding2 => 'uint8', padding3 => 'uint8', - data1 => 'sint32', - data2 => 'sint32', + value => 'sint16', + padding4 => 'uint16', + ]); +} + +package SDL2::JoyBallEvent { + FFI::C->struct( SDL_JoyBallEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + ball => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + padding3 => 'uint8', + xrel => 'sint16', + yrel => 'sint16', + ]); +} + +package SDL2::JoyButtonEvent { + FFI::C->struct( SDL_JoyButtonEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + button => 'uint8', + state => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + ]); +} + +package SDL2::JoyDeviceEvent { + FFI::C->struct( SDL_JoyDeviceEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + ]); +} + +package SDL2::JoyHatEvent { + FFI::C->struct( SDL_JoyHatEvent => [ + type => 'uint32', + timestamp => 'uint32', + which => 'SDL_JoystickID', + hat => 'uint8', + value => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', ]); } @@ -1145,37 +1299,48 @@ package SDL2::MouseWheelEvent { ]); } -package SDL2::ControllerButtonEvent { - FFI::C->struct( SDL_ControllerButtonEvent => [ +package SDL2::MultiGestureEvent { + FFI::C->struct( SDL_MultiGestureEvent => [ + type => 'uint32', + timestamp => 'uint32', + touchId => 'SDL_TouchID', + dThetha => 'float', + dHist => 'float', + x => 'float', + y => 'float', + numFingers => 'uint16', + padding => 'uint16', + ]); +} + +package SDL2::OSEvent { + FFI::C->struct( SDL_OSEvent => [ type => 'uint32', timestamp => 'uint32', - which => 'SDL_JoystickID', - button => 'uint8', - state => 'uint8', - padding1 => 'uint8', - padding2 => 'uint8', ]); } -package SDL2::ControllerDeviceEvent { - FFI::C->struct( SDL_ControllerDeviceEvent => [ +package SDL2::QuitEvent { + FFI::C->struct( SDL_QuitEvent => [ type => 'uint32', timestamp => 'uint32', - which => 'SDL_JoystickID', ]); } -package SDL2::ControllerAxisEvent { - FFI::C->struct( SDL_ControllerAxisEvent => [ +package SDL2::SensorEvent { + FFI::C->struct( SDL_SensorEvent => [ type => 'uint32', timestamp => 'uint32', - which => 'SDL_JoystickID', - axis => 'uint8', - padding1 => 'uint8', - padding2 => 'uint8', - padding3 => 'uint8', - value => 'sint16', - padding4 => 'uint16', + which => 'sint32', + data => 'float[6]', + ]); +} + +package SDL2::SysWMEvent { + FFI::C->struct( SDL_SysWMEvent => [ + type => 'uint32', + timestamp => 'uint32', + msg => 'opaque', # TODO: driver dependent data ]); } @@ -1203,15 +1368,18 @@ package SDL2::TextInputEvent { sub text { my $data = shift->_text; substr $data, 0, index $data, "\0" } } -package SDL2::DropEvent { - FFI::C->struct( SDL_DropEvent => [ +package SDL2::TouchFingerEvent { + FFI::C->struct( SDL_TouchFingerEvent => [ type => 'uint32', timestamp => 'uint32', - _file => 'opaque', - windowID => 'uint32', + touchId => 'SDL_TouchID', + fingerID => 'SDL_FingerID', + x => 'float', + y => 'float', + dx => 'float', + dy => 'float', + pressure => 'float', ]); - - sub file { $ffi->cast( opaque => string => shift->_file ) } } package SDL2::UserEvent { @@ -1227,23 +1395,52 @@ package SDL2::UserEvent { sub file { $ffi->cast( opaque => string => shift->_file ) } } +package SDL2::WindowEvent { + FFI::C->struct( SDL_WindowEvent => [ + type => 'uint32', + timestamp => 'uint32', + windowID => 'uint32', + event => 'uint8', + padding1 => 'uint8', + padding2 => 'uint8', + padding3 => 'uint8', + data1 => 'sint32', + data2 => 'sint32', + ]); +} + package SDL2::Event { FFI::C->union( SDL_Event => [ type => 'uint32', timestamp => 'uint32', + adevice => 'SDL_AudioDeviceEvent', button => 'SDL_MouseButtonEvent', caxis => 'SDL_ControllerAxisEvent', cbutton => 'SDL_ControllerButtonEvent', cdevice => 'SDL_ControllerDeviceEvent', + csensor => 'SDL_ControllerSensorEvent', + ctouchpad => 'SDL_ControllerTouchpadEvent', + dgesture => 'SDL_DollarGestureEvent', + display => 'SDL_DisplayEvent', drop => 'SDL_DropEvent', edit => 'SDL_TextEditingEvent', + jaxis => 'SDL_JoyAxisEvent', + jball => 'SDL_JoyBallEvent', + jbutton => 'SDL_JoyButtonEvent', + jdevide => 'SDL_JoyDeviceEvent', + jhat => 'SDL_JoyHatEvent', key => 'SDL_KeyboardEvent', + mgesture => 'SDL_MultiGestureEvent', motion => 'SDL_MouseMotionEvent', + quit => 'SDL_QuitEvent', + sensor => 'SDL_SensorEvent', + syswm => 'SDL_SysWMEvent', text => 'SDL_TextInputEvent', + tfinger => 'SDL_TouchFingerEvent', + user => 'SDL_UserEvent', wheel => 'SDL_MouseWheelEvent', window => 'SDL_WindowEvent', - user => 'SDL_UserEvent', padding => 'uint8[56]', ]); diff --git a/t/namespace.t b/t/namespace.t index 705d31c..1266ee2 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -39,6 +39,7 @@ is [ sort keys %SDL2:: ], [qw( AUDIO_U16MSB AUDIO_U8 ArrayOrder + AudioDeviceEvent:: BEGIN BIG_ENDIAN BITMAPORDER_1234 @@ -92,6 +93,8 @@ is [ sort keys %SDL2:: ], [qw( ControllerAxisEvent:: ControllerButtonEvent:: ControllerDeviceEvent:: + ControllerSensorEvent:: + ControllerTouchpadEvent:: CreateRGBSurfaceFrom CreateRenderer CreateTexture @@ -112,8 +115,10 @@ is [ sort keys %SDL2:: ], [qw( DestroyRenderer DestroyTexture DestroyWindow + DisplayEvent:: DisplayEventID DisplayOrientation + DollarGestureEvent:: DropEvent:: Event:: EventAction @@ -208,6 +213,11 @@ is [ sort keys %SDL2:: ], [qw( JOYDEVICEADDED JOYDEVICEREMOVED JOYHATMOTION + JoyAxisEvent:: + JoyBallEvent:: + JoyButtonEvent:: + JoyDeviceEvent:: + JoyHatEvent:: JoystickInstanceID KEYDOWN KEYMAPCHANGED @@ -516,6 +526,7 @@ is [ sort keys %SDL2:: ], [qw( MouseMotionEvent:: MouseWheelDirection MouseWheelEvent:: + MultiGestureEvent:: NUM_LOG_PRIORITIES NUM_SCANCODES NUM_SYSTEM_CURSORS @@ -524,6 +535,7 @@ is [ sort keys %SDL2:: ], [qw( ORIENTATION_PORTRAIT ORIENTATION_PORTRAIT_FLIPPED ORIENTATION_UNKNOWN + OSEvent:: PACKEDLAYOUT_1010102 PACKEDLAYOUT_1555 PACKEDLAYOUT_2101010 @@ -621,6 +633,7 @@ is [ sort keys %SDL2:: ], [qw( PushEvent QUIT Quit + QuitEvent:: QuitSubSystem RENDERER_ACCELERATED RENDERER_PRESENTVSYNC @@ -918,6 +931,7 @@ is [ sort keys %SDL2:: ], [qw( SYSWM_X11 ScaleMode ScanCode + SensorEvent:: SetColorKey SetError SetRenderDrawColor @@ -928,6 +942,7 @@ is [ sort keys %SDL2:: ], [qw( SetWindowIcon SetWindowTitle Surface:: + SysWMEvent:: SysWMinfo:: SystemCursor TEXTEDITING @@ -942,6 +957,7 @@ is [ sort keys %SDL2:: ], [qw( TextInputEvent:: TextureAccess TextureModulate + TouchFingerEvent:: USEREVENT UnlockTexture UpdateWindowSurface From e336586aaa5e08dfc5d96ee1e372174b09f6906b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Sun, 27 Jun 2021 13:12:46 +0100 Subject: [PATCH 16/36] Implement remaining version functions --- lib/SDL2/Raw.pm | 6 +++--- t/namespace.t | 2 ++ t/version.t | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 t/version.t diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 6a397a6..613871e 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1922,9 +1922,9 @@ $ffi->attach( RegisterEvents => ['uint32'] => 'int' ); ## Version -# SDL_GetRevision -# SDL_GetRevisionNumber -$ffi->attach( GetVersion => ['SDL_Version'] => 'void' ); +$ffi->attach( GetRevision => [ ] => 'string' ); +$ffi->attach( GetRevisionNumber => [ ] => 'int' ); # Deprecated +$ffi->attach( GetVersion => ['SDL_Version'] => 'void' ); ## Logging diff --git a/t/namespace.t b/t/namespace.t index 1266ee2..86ca5b1 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -182,6 +182,8 @@ is [ sort keys %SDL2:: ], [qw( GetError GetRenderTarget GetRendererInfo + GetRevision + GetRevisionNumber GetTextureAlphaMod GetTextureBlendMode GetTextureColorMod diff --git a/t/version.t b/t/version.t new file mode 100644 index 0000000..01a380e --- /dev/null +++ b/t/version.t @@ -0,0 +1,16 @@ +#!/usr/bin/env perl + +use Test2::V0; +use SDL2::Raw; + +my $fmt = sub { my $v = shift; sprintf '%d.%d.%d', $v->major, $v->minor, $v->patch }; + +my $version = SDL2::Version->new; +is $version->$fmt, '0.0.0', 'Can create version object'; + +SDL2::GetVersion($version); +like $version->$fmt, qr/2\.\d+\.\d+/a, 'Version object is populated'; + +ok SDL2::GetRevision, 'GetRevision'; + +done_testing; From 61446e228df3cd4e3ec2f8fd203ada4f0be40725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Sun, 27 Jun 2021 13:51:01 +0100 Subject: [PATCH 17/36] Add some basic constants --- lib/SDL2/Raw.pm | 8 ++++++++ t/namespace.t | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 613871e..ef39627 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -23,6 +23,14 @@ use constant { BIG_ENDIAN => 4321, LIL_ENDIAN => 1234, MIX_MAXVOLUME => 128, + FALSE => 0, + TRUE => 1, + QUERY => -1, + IGNORE => 0, + DISABLE => 0, + ENABLE => 1, + RELEASED => 0, + PRESSED => 1, }; # Init flags diff --git a/t/namespace.t b/t/namespace.t index 86ca5b1..3b42b05 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -100,6 +100,7 @@ is [ sort keys %SDL2:: ], [qw( CreateTexture CreateTextureFromSurface CreateWindow + DISABLE DISPLAYEVENT DISPLAYEVENT_CONNECTED DISPLAYEVENT_DISCONNECTED @@ -120,9 +121,11 @@ is [ sort keys %SDL2:: ], [qw( DisplayOrientation DollarGestureEvent:: DropEvent:: + ENABLE Event:: EventAction EventType + FALSE FINGERDOWN FINGERMOTION FINGERUP @@ -195,6 +198,7 @@ is [ sort keys %SDL2:: ], [qw( HINT_NORMAL HINT_OVERRIDE HintPriority + IGNORE INIT_AUDIO INIT_EVENTS INIT_EVERYTHING @@ -624,6 +628,7 @@ is [ sort keys %SDL2:: ], [qw( POWERSTATE_NO_BATTERY POWERSTATE_ON_BATTERY POWERSTATE_UNKNOWN + PRESSED PackedLayout PackedOrder PixelFormat:: @@ -633,10 +638,12 @@ is [ sort keys %SDL2:: ], [qw( PollEvent PowerState PushEvent + QUERY QUIT Quit QuitEvent:: QuitSubSystem + RELEASED RENDERER_ACCELERATED RENDERER_PRESENTVSYNC RENDERER_SOFTWARE @@ -955,6 +962,7 @@ is [ sort keys %SDL2:: ], [qw( TEXTUREMODULATE_ALPHA TEXTUREMODULATE_COLOR TEXTUREMODULATE_NONE + TRUE TextEditingEvent:: TextInputEvent:: TextureAccess From 92150420a9211fd07bb099c99f12f66b8edfb7d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Sun, 27 Jun 2021 14:26:32 +0100 Subject: [PATCH 18/36] Initial implementation of remaining event functions --- lib/SDL2/Raw.pm | 95 +++++++++++++++++++++---------------------------- t/namespace.t | 23 ++++++++++++ 2 files changed, 63 insertions(+), 55 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index ef39627..9d6ba6c 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1088,6 +1088,15 @@ $ffi->type( sint64 => 'SDL_GestureID' ); $ffi->mangler( sub { 'SDL_' . shift } ); +package SDL2::Finger { + FFI::C->struct( SDL_Finger => [ + id => 'SDL_FingerID', + x => 'float', + y => 'float', + pressure => 'float', + ]); +} + package SDL2::AudioDeviceEvent { FFI::C->struct( SDL_AudioDeviceEvent => [ type => 'uint32', @@ -1872,61 +1881,37 @@ $ffi->attach( JoystickInstanceID => ['SDL_Joystick'] => 'SDL_JoystickID' ); ## Events -# SDL_AddEventWatch -# SDL_AudioDeviceEvent -# SDL_ControllerAxisEvent -# SDL_ControllerButtonEvent -# SDL_ControllerDeviceEvent -# SDL_DelEventWatch -# SDL_DollarGestureEvent -# SDL_DropEvent -# SDL_Event -# SDL_EventState -# SDL_EventType -# SDL_FilterEvents -# SDL_Finger -# SDL_FlushEvent -# SDL_FlushEvents -# SDL_GetEventFilter -# SDL_GetEventState -# SDL_GetNumTouchDevices -# SDL_GetNumTouchFingers -# SDL_GetTouchDevice -# SDL_GetTouchFinger -# SDL_HasEvent -# SDL_HasEvents -# SDL_JoyAxisEvent -# SDL_JoyBallEvent -# SDL_JoyButtonEvent -# SDL_JoyDeviceEvent -# SDL_JoyHatEvent -# SDL_KeyboardEvent -# SDL_LoadDollarTemplates -# SDL_MouseButtonEvent -# SDL_MouseMotionEvent -# SDL_MouseWheelEvent -# SDL_MultiGestureEvent -# SDL_PeepEvents -$ffi->attach( PollEvent => ['SDL_Event'] => 'int' ); -# SDL_PumpEvents -$ffi->attach( PushEvent => ['SDL_Event'] => 'int' ); -# SDL_QuitEvent -# SDL_QuitRequested -# SDL_RecordGesture -$ffi->attach( RegisterEvents => ['uint32'] => 'int' ); -# SDL_SaveAllDollarTemplates -# SDL_SaveDollarTemplate -# SDL_SensorEvent -# SDL_SetEventFilter -# SDL_SysWMEvent -# SDL_TextEditingEvent -# SDL_TextInputEvent -# SDL_TouchFingerEvent -# SDL_UserEvent -# SDL_WaitEvent -# SDL_WaitEventTimeout -# SDL_WindowEvent -# SDL_WindowEventID +$ffi->type( '(opaque, opaque)->void', => 'SDL_EventFilter' ); + +$ffi->attach( AddEventWatch => [qw( SDL_EventFilter opaque )] => 'void' ); # TODO +$ffi->attach( DelEventWatch => [qw( SDL_EventFilter opaque )] => 'void' ); # TODO +$ffi->attach( EventState => [qw( uint32 int )] => 'uint8' ); +$ffi->attach( FilterEvents => [qw( SDL_EventFilter opaque )] => 'void' ); # TODO +$ffi->attach( FlushEvent => [qw( uint32 )] => 'void' ); +$ffi->attach( FlushEvents => [qw( uint32 uint32 )] => 'void' ); +sub GetEventFilter { ... } # TODO +$ffi->attach( GetNumTouchDevices => [qw( )] => 'int' ); +$ffi->attach( GetNumTouchFingers => [qw( SDL_TouchID )] => 'int' ); +$ffi->attach( GetTouchDevice => [qw( int )] => 'SDL_TouchID' ); +$ffi->attach( GetTouchFinger => [qw( SDL_TouchID int )] => 'SDL_Finger' ); +$ffi->attach( HasEvent => [qw( uint32 )] => 'int' ); +$ffi->attach( HasEvents => [qw( uint32 uint32 )] => 'int' ); +$ffi->attach( LoadDollarTemplates => [qw( SDL_TouchID SDL_RWops )] => 'int' ); +$ffi->attach( PeepEvents => [qw( SDL_Event int int uint32 uint32 )] => 'int' ); +$ffi->attach( PollEvent => [qw( SDL_Event )] => 'int' ); +$ffi->attach( PumpEvents => [qw( )] => 'void' ); +$ffi->attach( PushEvent => [qw( opaque )] => 'int' ); +$ffi->attach( RecordGesture => [qw( SDL_TouchID )] => 'int' ); +$ffi->attach( RegisterEvents => [qw( uint32 )] => 'int' ); +$ffi->attach( SaveAllDollarTemplates => [qw( SDL_RWops )] => 'int' ); +$ffi->attach( SaveDollarTemplate => [qw( SDL_GestureID SDL_RWops )] => 'int' ); +$ffi->attach( SetEventFilter => [qw( SDL_EventFilter opaque )] => 'void' ); # TODO +$ffi->attach( WaitEvent => [qw( SDL_Event )] => 'int' ); +$ffi->attach( WaitEventTimeout => [qw( SDL_Event int )] => 'int' ); + +# Not implemented in 2.0.8 +# $ffi->attach( GetEventState => [qw( uint32 )] => 'uint8' ); +# $ffi->attach( QuitRequested => [qw( )] => 'int' ); ## Version diff --git a/t/namespace.t b/t/namespace.t index 3b42b05..d1aa243 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -38,6 +38,7 @@ is [ sort keys %SDL2:: ], [qw( AUDIO_U16LSB AUDIO_U16MSB AUDIO_U8 + AddEventWatch ArrayOrder AudioDeviceEvent:: BEGIN @@ -112,6 +113,7 @@ is [ sort keys %SDL2:: ], [qw( DROPCOMPLETE DROPFILE DROPTEXT + DelEventWatch Delay DestroyRenderer DestroyTexture @@ -124,6 +126,7 @@ is [ sort keys %SDL2:: ], [qw( ENABLE Event:: EventAction + EventState EventType FALSE FINGERDOWN @@ -133,6 +136,10 @@ is [ sort keys %SDL2:: ], [qw( FLIP_HORIZONTAL FLIP_NONE FLIP_VERTICAL + FilterEvents + Finger:: + FlushEvent + FlushEvents FreeSurface GETEVENT GLContextResetNotification @@ -183,6 +190,9 @@ is [ sort keys %SDL2:: ], [qw( GameControllerNameForIndex GameControllerOpen GetError + GetEventFilter + GetNumTouchDevices + GetNumTouchFingers GetRenderTarget GetRendererInfo GetRevision @@ -191,12 +201,16 @@ is [ sort keys %SDL2:: ], [qw( GetTextureBlendMode GetTextureColorMod GetTicks + GetTouchDevice + GetTouchFinger GetVersion GetWindowSurface GetWindowWMInfo HINT_DEFAULT HINT_NORMAL HINT_OVERRIDE + HasEvent + HasEvents HintPriority IGNORE INIT_AUDIO @@ -502,6 +516,7 @@ is [ sort keys %SDL2:: ], [qw( LOG_PRIORITY_WARN LoadBMP LoadBMP_RW + LoadDollarTemplates LockTexture Log LogCategory @@ -631,12 +646,14 @@ is [ sort keys %SDL2:: ], [qw( PRESSED PackedLayout PackedOrder + PeepEvents PixelFormat:: PixelFormatEnum PixelType Point:: PollEvent PowerState + PumpEvents PushEvent QUERY QUIT @@ -651,6 +668,7 @@ is [ sort keys %SDL2:: ], [qw( RENDER_DEVICE_RESET RENDER_TARGETS_RESET RWFromFile + RecordGesture Rect:: RegisterEvents RenderClear @@ -938,11 +956,14 @@ is [ sort keys %SDL2:: ], [qw( SYSWM_WINDOWS SYSWM_WINRT SYSWM_X11 + SaveAllDollarTemplates + SaveDollarTemplate ScaleMode ScanCode SensorEvent:: SetColorKey SetError + SetEventFilter SetRenderDrawColor SetRenderTarget SetTextureAlphaMod @@ -1018,6 +1039,8 @@ is [ sort keys %SDL2:: ], [qw( WINDOW_TOOLTIP WINDOW_UTILITY WINDOW_VULKAN + WaitEvent + WaitEventTimeout WasInit WindowEvent:: WindowEventID From 71a83dd72deb50d7a6ebf72add6efc0e498a94bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Sun, 27 Jun 2021 14:41:50 +0100 Subject: [PATCH 19/36] Use positional rect constructors in sprite example --- examples/sprites.pl | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/examples/sprites.pl b/examples/sprites.pl index fc35cc8..2bf5ddd 100644 --- a/examples/sprites.pl +++ b/examples/sprites.pl @@ -84,12 +84,7 @@ package Sprite { h => $h, frame => 0, frames => [ - map SDL2::Rect->new( - x => $x + $_ * $w, - y => $y, - w => $w, - h => $h, - ), 0 .. 3 + map SDL2::Rect->new( $x + $_ * $w, $y, $w, $h ), 0 .. 3 ], } => $class; } @@ -101,12 +96,7 @@ package Sprite { $renderer, $atlas, $self->{frames}[ $self->{frame} ], - SDL2::Rect->new( - x => $x, - y => $y, - w => $self->{w} * $scale, - h => $self->{h} * $scale, - ), + SDL2::Rect->new( $x, $y, $self->{w} * $scale, $self->{h} * $scale ), ) and die 'Could not render texture: ' . SDL2::GetError; $self->{frame} = 0 if $self->{frame}++ >= $#{ $self->{frames} }; From 87614399f4da1cf5acf905c94a7276b622a0b284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Sun, 27 Jun 2021 14:53:54 +0100 Subject: [PATCH 20/36] Implement more render functions --- examples/events.pl | 13 +++++-------- lib/SDL2/Raw.pm | 24 ++++++++++-------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/examples/events.pl b/examples/events.pl index 817a04c..be8e839 100644 --- a/examples/events.pl +++ b/examples/events.pl @@ -12,14 +12,11 @@ die 'Could not initialise SDL2: ' . SDL2::GetError if SDL2::Init( SDL2::INIT_VIDEO | SDL2::INIT_GAMECONTROLLER ); -my $window = SDL2::CreateWindow( - 'Event handling demo', - SDL2::WINDOWPOS_CENTERED, - SDL2::WINDOWPOS_CENTERED, - 320, - 240, - SDL2::WINDOW_OPENGL | SDL2::WINDOW_RESIZABLE, -) or die 'Error creating SDL window: ' . SDL2::GetError; +my ( $window, $renderer ); +SDL2::CreateWindowAndRenderer( + 320, 240, SDL2::WINDOW_OPENGL | SDL2::WINDOW_RESIZABLE, + \$window, \$renderer, +) and die 'Error creating SDL window: ' . SDL2::GetError; my ( $done, $controller ); while ( !$done ) { diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 9d6ba6c..3321af9 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1578,9 +1578,7 @@ $ffi->type( opaque => 'SDL_RWops' ); ## Video -# SDL_BlendMode $ffi->attach( CreateWindow => [qw( string sint32 sint32 sint32 sint32 sint32 )] => 'SDL_Window' ); -# SDL_CreateWindowAndRenderer # SDL_CreateWindowFrom $ffi->attach( DestroyWindow => ['SDL_Window' ] => 'void' ); # SDL_DisableScreenSaver @@ -1617,8 +1615,8 @@ $ffi->attach( DestroyWindow => ['SDL_Window' ] => 'void' ); # SDL_GetWindowPosition # SDL_GetWindowSize $ffi->attach( GetWindowSurface => ['SDL_Window'] => 'SDL_Surface' ); -# SDL_GetWindowTitle -$ffi->attach( GetWindowWMInfo => ['SDL_Window', 'SDL_SysWMinfo'] => 'int' ); +$ffi->attach( GetWindowTitle => ['SDL_Window'] => 'string' ); +$ffi->attach( GetWindowWMInfo => ['SDL_Window', 'SDL_SysWMinfo'] => 'int' ); # SDL_GL_CreateContext # SDL_GL_DeleteContext # SDL_GL_ExtensionSupported @@ -1709,16 +1707,14 @@ $ffi->attach( SetError => ['string'] => 'void' => sub { shift->( sprintf shift ## Render -# SDL_BlendFactor -# SDL_BlendOperation -# SDL_ComposeCustomBlendMode -$ffi->attach( CreateRenderer => [qw( SDL_Window sint32 sint32 )] => 'SDL_Renderer' ); -# SDL_CreateSoftwareRenderer -$ffi->attach( CreateTexture => [qw( SDL_Renderer uint32 sint32 sint32 sint32 )] => 'SDL_Texture' ); -$ffi->attach( CreateTextureFromSurface => [qw( SDL_Renderer SDL_Surface )] => 'SDL_Texture' ); -# SDL_CreateWindowAndRenderer -$ffi->attach( DestroyRenderer => [qw( SDL_Renderer )] => 'void' ); -$ffi->attach( DestroyTexture => [qw( SDL_Texture )] => 'void' ); +$ffi->attach( ComposeCustomBlendMode => [qw( int int int int int int )] => 'int' ); +$ffi->attach( CreateRenderer => [qw( SDL_Window sint32 sint32 )] => 'SDL_Renderer' ); +$ffi->attach( CreateSoftwareRenderer => [qw( SDL_Surface )] => 'SDL_Renderer' ); +$ffi->attach( CreateTexture => [qw( SDL_Renderer uint32 sint32 sint32 sint32 )] => 'SDL_Texture' ); +$ffi->attach( CreateTextureFromSurface => [qw( SDL_Renderer SDL_Surface )] => 'SDL_Texture' ); +$ffi->attach( CreateWindowAndRenderer => [qw( int int uint32 opaque* opaque* )] => 'int' ); +$ffi->attach( DestroyRenderer => [qw( SDL_Renderer )] => 'void' ); +$ffi->attach( DestroyTexture => [qw( SDL_Texture )] => 'void' ); # SDL_GetNumRenderDrivers # SDL_GetRenderDrawBlendMode # SDL_GetRenderDrawColor From e42b8545ba2cc93826e213304a50003fb8155f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 18:24:37 +0100 Subject: [PATCH 21/36] Fix namespace test --- t/namespace.t | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/t/namespace.t b/t/namespace.t index d1aa243..6109b11 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -90,6 +90,7 @@ is [ sort keys %SDL2:: ], [qw( CONTROLLERTOUCHPADMOTION CONTROLLERTOUCHPADUP ClearError + ComposeCustomBlendMode Config ControllerAxisEvent:: ControllerButtonEvent:: @@ -98,9 +99,11 @@ is [ sort keys %SDL2:: ], [qw( ControllerTouchpadEvent:: CreateRGBSurfaceFrom CreateRenderer + CreateSoftwareRenderer CreateTexture CreateTextureFromSurface CreateWindow + CreateWindowAndRenderer DISABLE DISPLAYEVENT DISPLAYEVENT_CONNECTED @@ -205,6 +208,7 @@ is [ sort keys %SDL2:: ], [qw( GetTouchFinger GetVersion GetWindowSurface + GetWindowTitle GetWindowWMInfo HINT_DEFAULT HINT_NORMAL From 3635b1e17a9b5ca1285257fd72da9c672c2c2015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 18:18:53 +0100 Subject: [PATCH 22/36] Add bindings for Joystick functions --- lib/SDL2/Raw.pm | 74 +++++++++++++++++++++++++++++++++---------------- t/namespace.t | 41 +++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 24 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 3321af9..6a3a019 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -120,11 +120,27 @@ sub pixel_format { BEGIN { enum( + JoystickPowerLevel => [qw( + JOYSTICK_POWER_UNKNOWN + JOYSTICK_POWER_EMPTY + JOYSTICK_POWER_LOW + JOYSTICK_POWER_MEDIUM + JOYSTICK_POWER_FULL + JOYSTICK_POWER_WIRED + JOYSTICK_POWER_MAX + )], HintPriority => [qw( HINT_DEFAULT HINT_NORMAL HINT_OVERRIDE )], + HatPosition => { + HAT_CENTERED => 0x0, + HAT_UP => 0x1, + HAT_RIGHT => 0x2, + HAT_DOWN => 0x4, + HAT_LEFT => 0x8, + }, LogCategory => [qw( LOG_CATEGORY_APPLICATION LOG_CATEGORY_ERROR @@ -744,6 +760,12 @@ BEGIN { BEGIN { enum( + HatPosition => { + HAT_RIGHTUP => HAT_RIGHT | HAT_UP, + HAT_RIGHTDOWN => HAT_RIGHT | HAT_DOWN, + HAT_LEFTUP => HAT_LEFT | HAT_UP, + HAT_LEFTDOWN => HAT_LEFT | HAT_DOWN, + }, Keycode => { K_UNKNOWN => 0, K_RETURN => ord "\r", @@ -1463,6 +1485,10 @@ package SDL2::Event { ]); } +package SDL2::JoystickGUID { + FFI::C->struct( SDL_JoystickGUID => [ data => 'uint8[16]' ]); +} + package SDL2::PixelFormat { FFI::C->struct( SDL_PixelFormat => [ format => 'uint32', @@ -1575,6 +1601,7 @@ $ffi->type( opaque => 'SDL_Window' ); $ffi->type( opaque => 'SDL_GameController' ); $ffi->type( opaque => 'SDL_Joystick' ); $ffi->type( opaque => 'SDL_RWops' ); +$ffi->type( uint8 => 'SDL_bool' ); ## Video @@ -1850,30 +1877,29 @@ $ffi->attach( IsGameController => ['int' ] => 'bool' ## Joystick -# SDL_JoystickClose -# SDL_JoystickCurrentPowerLevel -# SDL_JoystickEventState -# SDL_JoystickFromInstanceID -# SDL_JoystickGetAttached -# SDL_JoystickGetAxis -# SDL_JoystickGetBall -# SDL_JoystickGetButton -# SDL_JoystickGetDeviceGUID -# SDL_JoystickGetGUID -# SDL_JoystickGetGUIDFromString -# SDL_JoystickGetGUIDString -# SDL_JoystickGetHat -$ffi->attach( JoystickInstanceID => ['SDL_Joystick'] => 'SDL_JoystickID' ); -# SDL_JoystickName -# SDL_JoystickNameForIndex -# SDL_JoystickNumAxes -# SDL_JoystickNumBalls -# SDL_JoystickNumButtons -# SDL_JoystickNumHats -# SDL_JoystickOpen -# SDL_JoystickPowerLevel -# SDL_JoystickUpdate -# SDL_NumJoysticks +$ffi->attach( JoystickClose => [qw( SDL_Joystick )] => 'void' ); +$ffi->attach( JoystickCurrentPowerLevel => [qw( SDL_Joystick )] => 'int' ); +$ffi->attach( JoystickEventState => [qw( int )] => 'int' ); +$ffi->attach( JoystickFromInstanceID => [qw( SDL_JoystickID )] => 'SDL_Joystick' ); +$ffi->attach( JoystickGetAttached => [qw( SDL_JoystickID )] => 'SDL_bool' ); +$ffi->attach( JoystickGetAxis => [qw( SDL_JoystickID int )] => 'sint16' ); +$ffi->attach( JoystickGetBall => [qw( SDL_JoystickID int int* int* )] => 'int' ); +$ffi->attach( JoystickGetButton => [qw( SDL_JoystickID int )] => 'uint8' ); +$ffi->attach( JoystickGetDeviceGUID => [qw( int )] => 'SDL_JoystickGUID' ); +$ffi->attach( JoystickGetGUID => [qw( SDL_Joystick )] => 'SDL_JoystickGUID' ); +$ffi->attach( JoystickGetGUIDFromString => [qw( string )] => 'SDL_JoystickGUID' ); +$ffi->attach( JoystickGetGUIDString => [qw( SDL_JoystickGUID opaque int )] => 'void' ); +$ffi->attach( JoystickGetHat => [qw( SDL_JoystickID int )] => 'uint8' ); +$ffi->attach( JoystickInstanceID => [qw( SDL_Joystick )] => 'SDL_JoystickID' ); +$ffi->attach( JoystickName => [qw( SDL_Joystick )] => 'string' ); +$ffi->attach( JoystickNameForIndex => [qw( int )] => 'string' ); +$ffi->attach( JoystickNumAxes => [qw( SDL_Joystick )] => 'int' ); +$ffi->attach( JoystickNumBalls => [qw( SDL_Joystick )] => 'int' ); +$ffi->attach( JoystickNumButtons => [qw( SDL_Joystick )] => 'int' ); +$ffi->attach( JoystickNumHats => [qw( SDL_Joystick )] => 'int' ); +$ffi->attach( JoystickOpen => [qw( int )] => 'SDL_Joystick' ); +$ffi->attach( JoystickUpdate => [ ] => 'void' ); +$ffi->attach( NumJoysticks => [ ] => 'int' ); ## Events diff --git a/t/namespace.t b/t/namespace.t index 6109b11..35d9249 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -210,11 +210,21 @@ is [ sort keys %SDL2:: ], [qw( GetWindowSurface GetWindowTitle GetWindowWMInfo + HAT_CENTERED + HAT_DOWN + HAT_LEFT + HAT_LEFTDOWN + HAT_LEFTUP + HAT_RIGHT + HAT_RIGHTDOWN + HAT_RIGHTUP + HAT_UP HINT_DEFAULT HINT_NORMAL HINT_OVERRIDE HasEvent HasEvents + HatPosition HintPriority IGNORE INIT_AUDIO @@ -237,12 +247,42 @@ is [ sort keys %SDL2:: ], [qw( JOYDEVICEADDED JOYDEVICEREMOVED JOYHATMOTION + JOYSTICK_POWER_EMPTY + JOYSTICK_POWER_FULL + JOYSTICK_POWER_LOW + JOYSTICK_POWER_MAX + JOYSTICK_POWER_MEDIUM + JOYSTICK_POWER_UNKNOWN + JOYSTICK_POWER_WIRED JoyAxisEvent:: JoyBallEvent:: JoyButtonEvent:: JoyDeviceEvent:: JoyHatEvent:: + JoystickClose + JoystickCurrentPowerLevel + JoystickEventState + JoystickFromInstanceID + JoystickGUID:: + JoystickGetAttached + JoystickGetAxis + JoystickGetBall + JoystickGetButton + JoystickGetDeviceGUID + JoystickGetGUID + JoystickGetGUIDFromString + JoystickGetGUIDString + JoystickGetHat JoystickInstanceID + JoystickName + JoystickNameForIndex + JoystickNumAxes + JoystickNumBalls + JoystickNumButtons + JoystickNumHats + JoystickOpen + JoystickPowerLevel + JoystickUpdate KEYDOWN KEYMAPCHANGED KEYUP @@ -555,6 +595,7 @@ is [ sort keys %SDL2:: ], [qw( NUM_LOG_PRIORITIES NUM_SCANCODES NUM_SYSTEM_CURSORS + NumJoysticks ORIENTATION_LANDSCAPE ORIENTATION_LANDSCAPE_FLIPPED ORIENTATION_PORTRAIT From 199e4313e9d85fdb1165b876f4bf9232abf2a2ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 18:19:55 +0100 Subject: [PATCH 23/36] Order structs for easier navigation --- lib/SDL2/Raw.pm | 70 +++++++++++++++++++++++++++++-------------------- t/namespace.t | 1 + 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 6a3a019..54b74ab 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1110,14 +1110,7 @@ $ffi->type( sint64 => 'SDL_GestureID' ); $ffi->mangler( sub { 'SDL_' . shift } ); -package SDL2::Finger { - FFI::C->struct( SDL_Finger => [ - id => 'SDL_FingerID', - x => 'float', - y => 'float', - pressure => 'float', - ]); -} +# Event structs package SDL2::AudioDeviceEvent { FFI::C->struct( SDL_AudioDeviceEvent => [ @@ -1485,6 +1478,27 @@ package SDL2::Event { ]); } +# Non-event structs + +package SDL2::Finger { + FFI::C->struct( SDL_Finger => [ + id => 'SDL_FingerID', + x => 'float', + y => 'float', + pressure => 'float', + ]); +} + +package SDL2::DisplayMode { + FFI::C->struct( SDL_DisplayMode => [ + format => 'uint32', # One of SDL_PixelFormatEnum + w => 'int', + h => 'int', + refresh_rate => 'int', + driverdata => 'opaque', + ]); +} + package SDL2::JoystickGUID { FFI::C->struct( SDL_JoystickGUID => [ data => 'uint8[16]' ]); } @@ -1516,29 +1530,17 @@ package SDL2::PixelFormat { sub next { $ffi->cast( opaque => 'SDL_PixelFormat', shift ) } } -package SDL2::Surface { - FFI::C->struct( SDL_Surface => [ - flags => 'uint32', - format => 'opaque', - w => 'int', - h => 'int', - pitch => 'int', - ]); -} - -package SDL2::Rect { +package SDL2::Point { use FFI::Platypus::Record; - record_layout_1( int => 'x', int => 'y', int => 'w', int => 'h' ); + record_layout_1( int => 'x', int => 'y' ); { - # Give rect a positional constructor + # Give point a positional constructor no strict 'refs'; no warnings 'redefine'; my $old = __PACKAGE__->can('new') or die; - my $new = sub { - shift->$old({ x => shift, y => shift, w => shift, h => shift }); - }; + my $new = sub { shift->$old({ x => shift, y => shift }) }; my $name = __PACKAGE__ . '::new'; require Sub::Util; @@ -1546,17 +1548,19 @@ package SDL2::Rect { } } -package SDL2::Point { +package SDL2::Rect { use FFI::Platypus::Record; - record_layout_1( int => 'x', int => 'y' ); + record_layout_1( int => 'x', int => 'y', int => 'w', int => 'h' ); { - # Give point a positional constructor + # Give rect a positional constructor no strict 'refs'; no warnings 'redefine'; my $old = __PACKAGE__->can('new') or die; - my $new = sub { shift->$old({ x => shift, y => shift }) }; + my $new = sub { + shift->$old({ x => shift, y => shift, w => shift, h => shift }); + }; my $name = __PACKAGE__ . '::new'; require Sub::Util; @@ -1577,6 +1581,16 @@ package SDL2::RendererInfo { sub name { $ffi->cast( opaque => string => shift->_name ) } } +package SDL2::Surface { + FFI::C->struct( SDL_Surface => [ + flags => 'uint32', + format => 'opaque', + w => 'int', + h => 'int', + pitch => 'int', + ]); +} + package SDL2::Version { FFI::C->struct( SDL_Version => [ major => 'uint8', diff --git a/t/namespace.t b/t/namespace.t index 35d9249..70881d0 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -123,6 +123,7 @@ is [ sort keys %SDL2:: ], [qw( DestroyWindow DisplayEvent:: DisplayEventID + DisplayMode:: DisplayOrientation DollarGestureEvent:: DropEvent:: From c5b8fc835d50da884d7c3b4eb72e7df76bbb1c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 18:20:43 +0100 Subject: [PATCH 24/36] Implement bindings for rest of tick functions --- lib/SDL2/Raw.pm | 11 ++++++----- t/namespace.t | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 54b74ab..e16f9bf 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1733,12 +1733,13 @@ $ffi->attach( WasInit => ['uint32'] => 'int' ); ## Timer # SDL_AddTimer -$ffi->attach( Delay => ['uint32'] => 'void' ); -# SDL_GetPerformanceCounter -# SDL_GetPerformanceFrequency -$ffi->attach( GetTicks => [ ] => 'uint32' ); # SDL_RemoveTimer -# SDL_TICKS_PASSED +$ffi->attach( Delay => ['uint32'] => 'void' ); +$ffi->attach( GetPerformanceCounter => [ ] => 'uint64' ); +$ffi->attach( GetPerformanceFrequency => [ ] => 'uint64' ); +$ffi->attach( GetTicks => [ ] => 'uint32' ); + +sub TICKS_PASSED { ( $_[1] - $_[0] ) <= 0 } ## Error diff --git a/t/namespace.t b/t/namespace.t index 70881d0..ebaee55 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -197,6 +197,8 @@ is [ sort keys %SDL2:: ], [qw( GetEventFilter GetNumTouchDevices GetNumTouchFingers + GetPerformanceCounter + GetPerformanceFrequency GetRenderTarget GetRendererInfo GetRevision @@ -1029,6 +1031,7 @@ is [ sort keys %SDL2:: ], [qw( TEXTUREMODULATE_ALPHA TEXTUREMODULATE_COLOR TEXTUREMODULATE_NONE + TICKS_PASSED TRUE TextEditingEvent:: TextInputEvent:: From 2079b805e7999e7328fb4fa62df643ecdcc5f609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 18:21:06 +0100 Subject: [PATCH 25/36] Add bindings for video functions --- lib/SDL2/Raw.pm | 135 ++++++++++++++++++++++++------------------------ t/namespace.t | 53 +++++++++++++++++++ 2 files changed, 120 insertions(+), 68 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index e16f9bf..bbc720f 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1617,47 +1617,48 @@ $ffi->type( opaque => 'SDL_Joystick' ); $ffi->type( opaque => 'SDL_RWops' ); $ffi->type( uint8 => 'SDL_bool' ); +$ffi->type( '( SDL_Window, SDL_Point, opaque )->int' => 'SDL_HitTest' ); + ## Video -$ffi->attach( CreateWindow => [qw( string sint32 sint32 sint32 sint32 sint32 )] => 'SDL_Window' ); -# SDL_CreateWindowFrom -$ffi->attach( DestroyWindow => ['SDL_Window' ] => 'void' ); -# SDL_DisableScreenSaver -# SDL_DisplayMode -# SDL_EnableScreenSaver -# SDL_GetClosestDisplayMode -# SDL_GetCurrentDisplayMode -# SDL_GetCurrentVideoDriver -# SDL_GetDesktopDisplayMode +$ffi->attach( CreateWindow => [qw( string sint32 sint32 sint32 sint32 sint32 )] => 'SDL_Window' ); +$ffi->attach( CreateWindowFrom => [qw( opaque )] => 'SDL_Window' ); +$ffi->attach( DestroyWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( DisableScreenSaver => [ ] => 'void' ); +$ffi->attach( EnableScreenSaver => [ ] => 'void' ); +$ffi->attach( GetClosestDisplayMode => [qw( int SDL_DisplayMode SDL_DisplayMode )] => 'SDL_DisplayMode' ); +$ffi->attach( GetCurrentDisplayMode => [qw( int SDL_DisplayMode )] => 'int' ); +$ffi->attach( GetCurrentVideoDriver => [ ] => 'string' ); +$ffi->attach( GetDesktopDisplayMode => [qw( int SDL_DisplayMode )] => 'int' ); # SDL_GetDisplayBounds -# SDL_GetDisplayDPI -# SDL_GetDisplayMode -# SDL_GetDisplayName +$ffi->attach( GetDisplayDPI => [qw( int float* float* float* )] => 'int' ); +$ffi->attach( GetDisplayMode => [qw( int SDL_DisplayMode )] => 'int' ); +$ffi->attach( GetDisplayName => [qw( int )] => 'string' ); # SDL_GetDisplayUsableBounds -# SDL_GetGrabbedWindow +$ffi->attach( GetGrabbedWindow => [ ] => 'SDL_Window' ); # SDL_GetNumDisplayModes # SDL_GetNumVideoDisplays # SDL_GetNumVideoDrivers -# SDL_GetVideoDriver -# SDL_GetWindowBordersSize -# SDL_GetWindowBrightness -# SDL_GetWindowData -# SDL_GetWindowDisplayIndex -# SDL_GetWindowDisplayMode -# SDL_GetWindowFlags -# SDL_GetWindowFromID -# SDL_GetWindowGammaRamp -# SDL_GetWindowGrab -# SDL_GetWindowID -# SDL_GetWindowMaximumSize -# SDL_GetWindowMinimumSize -# SDL_GetWindowOpacity -# SDL_GetWindowPixelFormat -# SDL_GetWindowPosition -# SDL_GetWindowSize -$ffi->attach( GetWindowSurface => ['SDL_Window'] => 'SDL_Surface' ); -$ffi->attach( GetWindowTitle => ['SDL_Window'] => 'string' ); -$ffi->attach( GetWindowWMInfo => ['SDL_Window', 'SDL_SysWMinfo'] => 'int' ); +$ffi->attach( GetVideoDriver => [qw( int )] => 'string' ); +$ffi->attach( GetWindowBordersSize => [qw( SDL_Window int* int* int* int* )] => 'int' ); # 2.0.5 +$ffi->attach( GetWindowBrightness => [qw( SDL_Window )] => 'float' ); +$ffi->attach( GetWindowData => [qw( SDL_Window string )] => 'opaque' ); +$ffi->attach( GetWindowDisplayIndex => [qw( SDL_Window )] => 'int' ); +$ffi->attach( GetWindowDisplayMode => [qw( SDL_Window SDL_DisplayMode )] => 'int' ); +$ffi->attach( GetWindowFlags => [qw( SDL_Window )] => 'uint32' ); +$ffi->attach( GetWindowFromID => [qw( uint32 )] => 'SDL_Window' ); +$ffi->attach( GetWindowGammaRamp => [qw( SDL_Window int* int* int* )] => 'int' ); +$ffi->attach( GetWindowGrab => [qw( SDL_Window )] => 'SDL_bool' ); +$ffi->attach( GetWindowID => [qw( SDL_Window )] => 'uint32' ); +$ffi->attach( GetWindowMinimumSize => [qw( SDL_Window int* int* )] => 'void' ); +$ffi->attach( GetWindowMaximumSize => [qw( SDL_Window int* int* )] => 'void' ); +$ffi->attach( GetWindowOpacity => [qw( SDL_Window float* )] => 'int' ); # 2.0.5 +$ffi->attach( GetWindowPixelFormat => [qw( SDL_Window )] => 'uint32' ); +$ffi->attach( GetWindowPosition => [qw( SDL_Window int* int* )] => 'void' ); +$ffi->attach( GetWindowSize => [qw( SDL_Window int* int* )] => 'void' ); +$ffi->attach( GetWindowSurface => [qw( SDL_Window )] => 'SDL_Surface' ); +$ffi->attach( GetWindowTitle => [qw( SDL_Window )] => 'string' ); +$ffi->attach( GetWindowWMInfo => [qw( SDL_Window SDL_SysWMinfo )] => 'int' ); # SDL_GL_CreateContext # SDL_GL_DeleteContext # SDL_GL_ExtensionSupported @@ -1677,10 +1678,10 @@ $ffi->attach( GetWindowWMInfo => ['SDL_Window', 'SDL_SysWMinfo'] => 'int' ); # SDL_GLattr # SDL_GLcontextFlag # SDL_GLprofile -# SDL_HideWindow +$ffi->attach( HideWindow => [ qw( SDL_Window )] => 'void' ); # SDL_HitTestResult -# SDL_IsScreenSaverEnabled -# SDL_MaximizeWindow +$ffi->attach( IsScreenSaverEnabled => [ ] => 'SDL_bool' ); +$ffi->attach( MaximizeWindow => [ qw( SDL_Window )] => 'void' ); # SDL_MessageBoxButtonData # SDL_MessageBoxButtonFlags # SDL_MessageBoxColor @@ -1688,37 +1689,35 @@ $ffi->attach( GetWindowWMInfo => ['SDL_Window', 'SDL_SysWMinfo'] => 'int' ); # SDL_MessageBoxColorType # SDL_MessageBoxData # SDL_MessageBoxFlags -# SDL_MinimizeWindow -# SDL_RaiseWindow -# SDL_RestoreWindow -# SDL_SetWindowBordered -# SDL_SetWindowBrightness -# SDL_SetWindowData -# SDL_SetWindowDisplayMode -# SDL_SetWindowFullscreen -# SDL_SetWindowGammaRamp -# SDL_SetWindowGrab -# SDL_SetWindowHitTest -$ffi->attach( SetWindowIcon => ['SDL_Window', 'SDL_Surface'] => 'void' ); -# SDL_SetWindowInputFocus -# SDL_SetWindowMaximumSize -# SDL_SetWindowMinimumSize -# SDL_SetWindowModalFor -# SDL_SetWindowOpacity -# SDL_SetWindowPosition -# SDL_SetWindowResizable -# SDL_SetWindowSize -$ffi->attach( SetWindowTitle => ['SDL_Window', 'string'] => 'void' ); -# SDL_ShowMessageBox -# SDL_ShowSimpleMessageBox -# SDL_ShowWindow -$ffi->attach( UpdateWindowSurface => ['SDL_Window' ] => 'int' ); -# SDL_UpdateWindowSurfaceRects -# SDL_VideoInit -# SDL_VideoQuit -# SDL_WindowEvent -# SDL_WindowEventID -# SDL_WindowFlags + +$ffi->attach( MinimizeWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( RaiseWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( RestoreWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( SetWindowBordered => [qw( SDL_Window SDL_bool )] => 'void' ); +$ffi->attach( SetWindowBrightness => [qw( SDL_Window float )] => 'int' ); +$ffi->attach( SetWindowData => [qw( SDL_Window string opaque )] => 'opaque' ); +$ffi->attach( SetWindowDisplayMode => [qw( SDL_Window SDL_DisplayMode )] => 'int' ); +$ffi->attach( SetWindowFullscreen => [qw( SDL_Window uint32 )] => 'int' ); +$ffi->attach( SetWindowGammaRamp => [qw( SDL_Window uint16 uint16 uint16 )] => 'int' ); +$ffi->attach( SetWindowGrab => [qw( SDL_Window SDL_bool )] => 'void' ); +$ffi->attach( SetWindowHitTest => [qw( SDL_Window SDL_HitTest )] => 'int' ); +$ffi->attach( SetWindowIcon => [qw( SDL_Window SDL_Surface )] => 'void' ); +$ffi->attach( SetWindowInputFocus => [qw( SDL_Window )] => 'int' ); +$ffi->attach( SetWindowMaximumSize => [qw( SDL_Window int int )] => 'void' ); +$ffi->attach( SetWindowMinimumSize => [qw( SDL_Window int int )] => 'void' ); +$ffi->attach( SetWindowModalFor => [qw( SDL_Window SDL_Window )] => 'int' ); # 2.0.5 +$ffi->attach( SetWindowOpacity => [qw( SDL_Window float )] => 'int' ); # 2.0.5 +$ffi->attach( SetWindowPosition => [qw( SDL_Window int int )] => 'void' ); +$ffi->attach( SetWindowResizable => [qw( SDL_Window SDL_bool )] => 'void' ); # 2.0.5 +$ffi->attach( SetWindowSize => [qw( SDL_Window int int )] => 'void' ); +$ffi->attach( SetWindowTitle => [qw( SDL_Window string )] => 'void' ); +# TODO: SDL_ShowMessageBox +# TODO: SDL_ShowSimpleMessageBox +$ffi->attach( ShowWindow => ['SDL_Window' ] => 'void' ); +$ffi->attach( UpdateWindowSurface => ['SDL_Window' ] => 'int' ); +# TODO: SDL_UpdateWindowSurfaceRects +$ffi->attach( VideoInit => ['string'] => 'int' ); +$ffi->attach( VideoQuit => [ ] => 'void' ); ## SDL diff --git a/t/namespace.t b/t/namespace.t index ebaee55..de721ca 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -104,6 +104,7 @@ is [ sort keys %SDL2:: ], [qw( CreateTextureFromSurface CreateWindow CreateWindowAndRenderer + CreateWindowFrom DISABLE DISPLAYEVENT DISPLAYEVENT_CONNECTED @@ -121,6 +122,7 @@ is [ sort keys %SDL2:: ], [qw( DestroyRenderer DestroyTexture DestroyWindow + DisableScreenSaver DisplayEvent:: DisplayEventID DisplayMode:: @@ -128,6 +130,7 @@ is [ sort keys %SDL2:: ], [qw( DollarGestureEvent:: DropEvent:: ENABLE + EnableScreenSaver Event:: EventAction EventState @@ -193,8 +196,16 @@ is [ sort keys %SDL2:: ], [qw( GameControllerGetJoystick GameControllerNameForIndex GameControllerOpen + GetClosestDisplayMode + GetCurrentDisplayMode + GetCurrentVideoDriver + GetDesktopDisplayMode + GetDisplayDPI + GetDisplayMode + GetDisplayName GetError GetEventFilter + GetGrabbedWindow GetNumTouchDevices GetNumTouchFingers GetPerformanceCounter @@ -210,6 +221,23 @@ is [ sort keys %SDL2:: ], [qw( GetTouchDevice GetTouchFinger GetVersion + GetVideoDriver + GetWindowBordersSize + GetWindowBrightness + GetWindowData + GetWindowDisplayIndex + GetWindowDisplayMode + GetWindowFlags + GetWindowFromID + GetWindowGammaRamp + GetWindowGrab + GetWindowID + GetWindowMaximumSize + GetWindowMinimumSize + GetWindowOpacity + GetWindowPixelFormat + GetWindowPosition + GetWindowSize GetWindowSurface GetWindowTitle GetWindowWMInfo @@ -228,6 +256,7 @@ is [ sort keys %SDL2:: ], [qw( HasEvent HasEvents HatPosition + HideWindow HintPriority IGNORE INIT_AUDIO @@ -243,6 +272,7 @@ is [ sort keys %SDL2:: ], [qw( Init InitSubSystem IsGameController + IsScreenSaverEnabled JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN @@ -590,6 +620,8 @@ is [ sort keys %SDL2:: ], [qw( MOUSEWHEEL_FLIPPED MOUSEWHEEL_NORMAL MULTIGESTURE + MaximizeWindow + MinimizeWindow MouseButtonEvent:: MouseMotionEvent:: MouseWheelDirection @@ -716,6 +748,7 @@ is [ sort keys %SDL2:: ], [qw( RENDER_DEVICE_RESET RENDER_TARGETS_RESET RWFromFile + RaiseWindow RecordGesture Rect:: RegisterEvents @@ -729,6 +762,7 @@ is [ sort keys %SDL2:: ], [qw( RendererFlags RendererFlip RendererInfo:: + RestoreWindow SCALEMODEBEST SCALEMODELINEAR SCALEMODENEAREST @@ -1017,8 +1051,25 @@ is [ sort keys %SDL2:: ], [qw( SetTextureAlphaMod SetTextureBlendMode SetTextureColorMod + SetWindowBordered + SetWindowBrightness + SetWindowData + SetWindowDisplayMode + SetWindowFullscreen + SetWindowGammaRamp + SetWindowGrab + SetWindowHitTest SetWindowIcon + SetWindowInputFocus + SetWindowMaximumSize + SetWindowMinimumSize + SetWindowModalFor + SetWindowOpacity + SetWindowPosition + SetWindowResizable + SetWindowSize SetWindowTitle + ShowWindow Surface:: SysWMEvent:: SysWMinfo:: @@ -1044,6 +1095,8 @@ is [ sort keys %SDL2:: ], [qw( UpperBlit UserEvent:: Version:: + VideoInit + VideoQuit WINDOWEVENT WINDOWEVENT_CLOSE WINDOWEVENT_ENTER From bba7d3c5ccbfefe349f89ddf6d19b963be5099ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 18:58:23 +0100 Subject: [PATCH 26/36] Add bindings for remaining controller functions --- lib/SDL2/Raw.pm | 109 +++++++++++++++++++++++++++++++++++++----------- t/namespace.t | 60 ++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 25 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index bbc720f..410d056 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -120,6 +120,51 @@ sub pixel_format { BEGIN { enum( + GameControllerType => [qw( + CONTROLLER_TYPE_UNKNOWN + CONTROLLER_TYPE_XBOX360 + CONTROLLER_TYPE_XBOXONE + CONTROLLER_TYPE_PS3 + CONTROLLER_TYPE_PS4 + CONTROLLER_TYPE_NINTENDO_SWITCH_PRO + CONTROLLER_TYPE_VIRTUAL + CONTROLLER_TYPE_PS5 + )], + GameControllerBindType => [qw( + CONTROLLER_BINDTYPE_NONE + CONTROLLER_BINDTYPE_BUTTON + CONTROLLER_BINDTYPE_AXIS + CONTROLLER_BINDTYPE_HAT + )], + GameControllerAxis => [qw( + CONTROLLER_AXIS_INVALID + CONTROLLER_AXIS_LEFTX + CONTROLLER_AXIS_LEFTY + CONTROLLER_AXIS_RIGHTX + CONTROLLER_AXIS_RIGHTY + CONTROLLER_AXIS_TRIGGERLEFT + CONTROLLER_AXIS_TRIGGERRIGHT + CONTROLLER_AXIS_MAX + )], + GameControllerButton => [qw( + CONTROLLER_BUTTON_INVALID + CONTROLLER_BUTTON_A + CONTROLLER_BUTTON_B + CONTROLLER_BUTTON_X + CONTROLLER_BUTTON_Y + CONTROLLER_BUTTON_BACK + CONTROLLER_BUTTON_GUIDE + CONTROLLER_BUTTON_START + CONTROLLER_BUTTON_LEFTSTICK + CONTROLLER_BUTTON_RIGHTSTICK + CONTROLLER_BUTTON_LEFTSHOULDER + CONTROLLER_BUTTON_RIGHTSHOULDER + CONTROLLER_BUTTON_DPAD_UP + CONTROLLER_BUTTON_DPAD_DOWN + CONTROLLER_BUTTON_DPAD_LEFT + CONTROLLER_BUTTON_DPAD_RIGHT + CONTROLLER_BUTTON_MAX + )], JoystickPowerLevel => [qw( JOYSTICK_POWER_UNKNOWN JOYSTICK_POWER_EMPTY @@ -1480,6 +1525,22 @@ package SDL2::Event { # Non-event structs +package SDL2::GameControllerButtonBind::Hat { + FFI::C->struct( SDL_GameControllerButtonBind_Hat => [ + hat => 'int', + hat_mask => 'int', + ]); +} + +package SDL2::GameControllerButtonBind { + FFI::C->struct( SDL_GameControllerButtonBind => [ + bindtype => 'int', + button => 'int', + axis => 'int', + hat => 'SDL_GameControllerButtonBind_Hat', + ]); +} + package SDL2::Finger { FFI::C->struct( SDL_Finger => [ id => 'SDL_FingerID', @@ -1863,31 +1924,29 @@ $ffi->attach( SetColorKey => [qw( SDL_Surface int uint32 )] => 'int' ); ## GameController -# SDL_GameControllerAddMapping -# SDL_GameControllerAddMappingsFromFile -# SDL_GameControllerAddMappingsFromRW -# SDL_GameControllerAxis -# SDL_GameControllerButton -$ffi->attach( GameControllerClose => ['SDL_GameController'] => 'void' ); -# SDL_GameControllerEventState -# SDL_GameControllerFromInstanceID -# SDL_GameControllerGetAttached -# SDL_GameControllerGetAxis -# SDL_GameControllerGetAxisFromString -# SDL_GameControllerGetBindForAxis -# SDL_GameControllerGetBindForButton -# SDL_GameControllerGetButton -# SDL_GameControllerGetButtonFromString -$ffi->attach( GameControllerGetJoystick => ['SDL_GameController'] => 'SDL_Joystick' ); -# SDL_GameControllerGetStringForAxis -# SDL_GameControllerGetStringForButton -# SDL_GameControllerMapping -# SDL_GameControllerMappingForGUID -# SDL_GameControllerName -$ffi->attach( GameControllerNameForIndex => ['int' ] => 'string' ); -$ffi->attach( GameControllerOpen => ['int' ] => 'SDL_GameController' ); -# SDL_GameControllerUpdate -$ffi->attach( IsGameController => ['int' ] => 'bool' ); +sub GameControllerAddMappingsFromFile { GameControllerAddMappingsFromRW( RWFromFile( shift, 'rb' ), 1 ) } +$ffi->attach( GameControllerAddMapping => [qw( string )] => 'int' ); +$ffi->attach( GameControllerAddMappingsFromRW => [qw( SDL_RWops int )] => 'int' ); # 2.0.2 +$ffi->attach( GameControllerClose => [qw( SDL_GameController )] => 'void' ); +$ffi->attach( GameControllerEventState => [qw( int )] => 'int' ); +$ffi->attach( GameControllerFromInstanceID => [qw( SDL_JoystickID )] => 'SDL_GameController' ); # 2.0.4 +$ffi->attach( GameControllerGetAttached => [qw( SDL_GameController )] => 'SDL_bool' ); +$ffi->attach( GameControllerGetAxis => [qw( SDL_GameController int )] => 'sint16' ); +$ffi->attach( GameControllerGetAxisFromString => [qw( string )] => 'int' ); +$ffi->attach( GameControllerGetBindForAxis => [qw( SDL_GameController int )] => 'SDL_GameControllerButtonBind' ); +$ffi->attach( GameControllerGetBindForButton => [qw( SDL_GameController int )] => 'SDL_GameControllerButtonBind' ); +$ffi->attach( GameControllerGetButton => [qw( SDL_GameController int )] => 'uint8' ); +$ffi->attach( GameControllerGetButtonFromString => [qw( string )] => 'int' ); +$ffi->attach( GameControllerGetJoystick => [qw( SDL_GameController )] => 'SDL_Joystick' ); +$ffi->attach( GameControllerGetStringForAxis => [qw( int )] => 'string' ); +$ffi->attach( GameControllerGetStringForButton => [qw( int )] => 'string' ); +$ffi->attach( GameControllerMapping => [qw( SDL_GameController )] => 'string' ); +$ffi->attach( GameControllerMappingForGUID => [qw( SDL_JoystickGUID )] => 'string' ); +$ffi->attach( GameControllerName => [qw( SDL_GameController )] => 'string' ); +$ffi->attach( GameControllerNameForIndex => [qw( int )] => 'string' ); +$ffi->attach( GameControllerOpen => [qw( int )] => 'SDL_GameController' ); +$ffi->attach( GameControllerUpdate => [ ] => 'void' ); +$ffi->attach( IsGameController => [qw( int )] => 'bool' ); ## Joystick diff --git a/t/namespace.t b/t/namespace.t index de721ca..a8dab02 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -89,6 +89,43 @@ is [ sort keys %SDL2:: ], [qw( CONTROLLERTOUCHPADDOWN CONTROLLERTOUCHPADMOTION CONTROLLERTOUCHPADUP + CONTROLLER_AXIS_INVALID + CONTROLLER_AXIS_LEFTX + CONTROLLER_AXIS_LEFTY + CONTROLLER_AXIS_MAX + CONTROLLER_AXIS_RIGHTX + CONTROLLER_AXIS_RIGHTY + CONTROLLER_AXIS_TRIGGERLEFT + CONTROLLER_AXIS_TRIGGERRIGHT + CONTROLLER_BINDTYPE_AXIS + CONTROLLER_BINDTYPE_BUTTON + CONTROLLER_BINDTYPE_HAT + CONTROLLER_BINDTYPE_NONE + CONTROLLER_BUTTON_A + CONTROLLER_BUTTON_B + CONTROLLER_BUTTON_BACK + CONTROLLER_BUTTON_DPAD_DOWN + CONTROLLER_BUTTON_DPAD_LEFT + CONTROLLER_BUTTON_DPAD_RIGHT + CONTROLLER_BUTTON_DPAD_UP + CONTROLLER_BUTTON_GUIDE + CONTROLLER_BUTTON_INVALID + CONTROLLER_BUTTON_LEFTSHOULDER + CONTROLLER_BUTTON_LEFTSTICK + CONTROLLER_BUTTON_MAX + CONTROLLER_BUTTON_RIGHTSHOULDER + CONTROLLER_BUTTON_RIGHTSTICK + CONTROLLER_BUTTON_START + CONTROLLER_BUTTON_X + CONTROLLER_BUTTON_Y + CONTROLLER_TYPE_NINTENDO_SWITCH_PRO + CONTROLLER_TYPE_PS3 + CONTROLLER_TYPE_PS4 + CONTROLLER_TYPE_PS5 + CONTROLLER_TYPE_UNKNOWN + CONTROLLER_TYPE_VIRTUAL + CONTROLLER_TYPE_XBOX360 + CONTROLLER_TYPE_XBOXONE ClearError ComposeCustomBlendMode Config @@ -192,10 +229,33 @@ is [ sort keys %SDL2:: ], [qw( GLcontextFlag GLcontextReleaseFlag GLprofile + GameControllerAddMapping + GameControllerAddMappingsFromFile + GameControllerAddMappingsFromRW + GameControllerAxis + GameControllerBindType + GameControllerButton + GameControllerButtonBind:: GameControllerClose + GameControllerEventState + GameControllerFromInstanceID + GameControllerGetAttached + GameControllerGetAxis + GameControllerGetAxisFromString + GameControllerGetBindForAxis + GameControllerGetBindForButton + GameControllerGetButton + GameControllerGetButtonFromString GameControllerGetJoystick + GameControllerGetStringForAxis + GameControllerGetStringForButton + GameControllerMapping + GameControllerMappingForGUID + GameControllerName GameControllerNameForIndex GameControllerOpen + GameControllerType + GameControllerUpdate GetClosestDisplayMode GetCurrentDisplayMode GetCurrentVideoDriver From c0a55f1339b1bbdb9ca2bab5a3bd938a46bcbbf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 19:13:51 +0100 Subject: [PATCH 27/36] Sort enums --- lib/SDL2/Raw.pm | 640 ++++++++++++++++++++++++------------------------ 1 file changed, 320 insertions(+), 320 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 410d056..515eeb6 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -120,21 +120,140 @@ sub pixel_format { BEGIN { enum( - GameControllerType => [qw( - CONTROLLER_TYPE_UNKNOWN - CONTROLLER_TYPE_XBOX360 - CONTROLLER_TYPE_XBOXONE - CONTROLLER_TYPE_PS3 - CONTROLLER_TYPE_PS4 - CONTROLLER_TYPE_NINTENDO_SWITCH_PRO - CONTROLLER_TYPE_VIRTUAL - CONTROLLER_TYPE_PS5 + ArrayOrder => [qw( + ARRAYORDER_NONE + ARRAYORDER_RGB + ARRAYORDER_RGBA + ARRAYORDER_ARGB + ARRAYORDER_BGR + ARRAYORDER_BGRA + ARRAYORDER_ABGR )], - GameControllerBindType => [qw( - CONTROLLER_BINDTYPE_NONE - CONTROLLER_BINDTYPE_BUTTON - CONTROLLER_BINDTYPE_AXIS - CONTROLLER_BINDTYPE_HAT + BlendFactor => [qw( + BLENDFACTOR_ZERO + BLENDFACTOR_ONE + BLENDFACTOR_SRC_COLOR + BLENDFACTOR_ONE_MINUS_SRC_COLOR + BLENDFACTOR_SRC_ALPHA + BLENDFACTOR_ONE_MINUS_SRC_ALPHA + BLENDFACTOR_DST_COLOR + BLENDFACTOR_ONE_MINUS_DST_COLOR + BLENDFACTOR_DST_ALPHA + BLENDFACTOR_ONE_MINUS_DST_ALPHA + )], + BlendMode => { + BLENDMODE_NONE => 0x00000000, + BLENDMODE_BLEND => 0x00000001, + BLENDMODE_ADD => 0x00000002, + BLENDMODE_MOD => 0x00000004, + BLENDMODE_MUL => 0x00000008, + BLENDMODE_INVALID => 0x7FFFFFFF, + }, + BlendOperation => [qw( + BLENDOPERATION_ADD + BLENDOPERATION_SUBTRACT + BLENDOPERATION_REV_SUBTRACT + BLENDOPERATION_MINIMUM + BLENDOPERATION_MAXIMUM + )], + BitmapOrder => [qw( + BITMAPORDER_NONE + BITMAPORDER_4321 + BITMAPORDER_1234 + )], + DisplayEventID => [qw( + DISPLAYEVENT_NONE + DISPLAYEVENT_ORIENTATION + DISPLAYEVENT_CONNECTED + DISPLAYEVENT_DISCONNECTED + )], + DisplayOrientation => [qw( + ORIENTATION_UNKNOWN + ORIENTATION_LANDSCAPE + ORIENTATION_LANDSCAPE_FLIPPED + ORIENTATION_PORTRAIT + ORIENTATION_PORTRAIT_FLIPPED + )], + EventAction => [qw( + ADDEVENT + PEEKEVENT + GETEVENT + )], + EventType => [qw( + FIRSTEVENT=0 + + QUIT=0x100 + + APP_TERMINATING + APP_LOWMEMORY + APP_WILLENTERBACKGROUND + APP_DIDENTERBACKGROUND + APP_WILLENTERFOREGROUND + APP_DIDENTERFOREGROUND + + LOCALECHANGED + + DISPLAYEVENT=0x150 + + WINDOWEVENT=0x200 + SYSWMEVENT + + KEYDOWN=0x300 + KEYUP + TEXTEDITING + TEXTINPUT + KEYMAPCHANGED + + MOUSEMOTION=0x400 + MOUSEBUTTONDOWN + MOUSEBUTTONUP + MOUSEWHEEL + + JOYAXISMOTION=0x600 + JOYBALLMOTION + JOYHATMOTION + JOYBUTTONDOWN + JOYBUTTONUP + JOYDEVICEADDED + JOYDEVICEREMOVED + + CONTROLLERAXISMOTION=0x650 + CONTROLLERBUTTONDOWN + CONTROLLERBUTTONUP + CONTROLLERDEVICEADDED + CONTROLLERDEVICEREMOVED + CONTROLLERDEVICEREMAPPED + CONTROLLERTOUCHPADDOWN + CONTROLLERTOUCHPADMOTION + CONTROLLERTOUCHPADUP + CONTROLLERSENSORUPDATE + + FINGERDOWN=0x700 + FINGERUP + FINGERMOTION + + DOLLARGESTURE=0x800 + DOLLARRECORD + MULTIGESTURE + + CLIPBOARDUPDATE=0x900 + + DROPFILE=0x1000 + DROPTEXT + DROPBEGIN + DROPCOMPLETE + + AUDIODEVICEADDED=0x1100 + AUDIODEVICEREMOVED + + SENSORUPDATE=0x1200 + + RENDER_TARGETS_RESET=0x2000 + RENDER_DEVICE_RESET + + USEREVENT=0x8000 + + LASTEVENT=0xFFFF )], GameControllerAxis => [qw( CONTROLLER_AXIS_INVALID @@ -146,6 +265,12 @@ BEGIN { CONTROLLER_AXIS_TRIGGERRIGHT CONTROLLER_AXIS_MAX )], + GameControllerBindType => [qw( + CONTROLLER_BINDTYPE_NONE + CONTROLLER_BINDTYPE_BUTTON + CONTROLLER_BINDTYPE_AXIS + CONTROLLER_BINDTYPE_HAT + )], GameControllerButton => [qw( CONTROLLER_BUTTON_INVALID CONTROLLER_BUTTON_A @@ -165,115 +290,15 @@ BEGIN { CONTROLLER_BUTTON_DPAD_RIGHT CONTROLLER_BUTTON_MAX )], - JoystickPowerLevel => [qw( - JOYSTICK_POWER_UNKNOWN - JOYSTICK_POWER_EMPTY - JOYSTICK_POWER_LOW - JOYSTICK_POWER_MEDIUM - JOYSTICK_POWER_FULL - JOYSTICK_POWER_WIRED - JOYSTICK_POWER_MAX - )], - HintPriority => [qw( - HINT_DEFAULT - HINT_NORMAL - HINT_OVERRIDE - )], - HatPosition => { - HAT_CENTERED => 0x0, - HAT_UP => 0x1, - HAT_RIGHT => 0x2, - HAT_DOWN => 0x4, - HAT_LEFT => 0x8, - }, - LogCategory => [qw( - LOG_CATEGORY_APPLICATION - LOG_CATEGORY_ERROR - LOG_CATEGORY_ASSERT - LOG_CATEGORY_SYSTEM - LOG_CATEGORY_AUDIO - LOG_CATEGORY_VIDEO - LOG_CATEGORY_RENDER - LOG_CATEGORY_INPUT - LOG_CATEGORY_TEST - LOG_CATEGORY_RESERVED1 - LOG_CATEGORY_RESERVED2 - LOG_CATEGORY_RESERVED3 - LOG_CATEGORY_RESERVED4 - LOG_CATEGORY_RESERVED5 - LOG_CATEGORY_RESERVED6 - LOG_CATEGORY_RESERVED7 - LOG_CATEGORY_RESERVED8 - LOG_CATEGORY_RESERVED9 - LOG_CATEGORY_RESERVED10 - LOG_CATEGORY_CUSTOM - )], - LogPriority => [qw( - LOG_PRIORITY_VERBOSE=1 - LOG_PRIORITY_DEBUG - LOG_PRIORITY_INFO - LOG_PRIORITY_WARN - LOG_PRIORITY_ERROR - LOG_PRIORITY_CRITICAL - NUM_LOG_PRIORITIES - )], - WindowFlags => { - WINDOW_FULLSCREEN => 0x00000001, - WINDOW_OPENGL => 0x00000002, - WINDOW_SHOWN => 0x00000004, - WINDOW_HIDDEN => 0x00000008, - WINDOW_BORDERLESS => 0x00000010, - WINDOW_RESIZABLE => 0x00000020, - WINDOW_MINIMIZED => 0x00000040, - WINDOW_MAXIMIZED => 0x00000080, - WINDOW_MOUSE_GRABBED => 0x00000100, - WINDOW_INPUT_FOCUS => 0x00000200, - WINDOW_MOUSE_FOCUS => 0x00000400, - WINDOW_FULLSCREEN_DESKTOP => 0x00001001, - WINDOW_FOREIGN => 0x00000800, - WINDOW_ALLOW_HIGHDPI => 0x00002000, - WINDOW_MOUSE_CAPTURE => 0x00004000, - WINDOW_ALWAYS_ON_TOP => 0x00008000, - WINDOW_SKIP_TASKBAR => 0x00010000, - WINDOW_UTILITY => 0x00020000, - WINDOW_TOOLTIP => 0x00040000, - WINDOW_POPUP_MENU => 0x00080000, - WINDOW_KEYBOARD_GRABBED => 0x00100000, - WINDOW_VULKAN => 0x10000000, - WINDOW_METAL => 0x20000000, - WINDOW_INPUT_GRABBED => 0x00000100, - }, - WindowEventID => [qw( - WINDOWEVENT_NONE - WINDOWEVENT_SHOWN - WINDOWEVENT_HIDDEN - WINDOWEVENT_EXPOSED - WINDOWEVENT_MOVED - WINDOWEVENT_RESIZED - WINDOWEVENT_SIZE_CHANGED - WINDOWEVENT_MINIMIZED - WINDOWEVENT_MAXIMIZED - WINDOWEVENT_RESTORED - WINDOWEVENT_ENTER - WINDOWEVENT_LEAVE - WINDOWEVENT_FOCUS_GAINED - WINDOWEVENT_FOCUS_LOST - WINDOWEVENT_CLOSE - WINDOWEVENT_TAKE_FOCUS - WINDOWEVENT_HIT_TEST - )], - DisplayEventID => [qw( - DISPLAYEVENT_NONE - DISPLAYEVENT_ORIENTATION - DISPLAYEVENT_CONNECTED - DISPLAYEVENT_DISCONNECTED - )], - DisplayOrientation => [qw( - ORIENTATION_UNKNOWN - ORIENTATION_LANDSCAPE - ORIENTATION_LANDSCAPE_FLIPPED - ORIENTATION_PORTRAIT - ORIENTATION_PORTRAIT_FLIPPED + GameControllerType => [qw( + CONTROLLER_TYPE_UNKNOWN + CONTROLLER_TYPE_XBOX360 + CONTROLLER_TYPE_XBOXONE + CONTROLLER_TYPE_PS3 + CONTROLLER_TYPE_PS4 + CONTROLLER_TYPE_NINTENDO_SWITCH_PRO + CONTROLLER_TYPE_VIRTUAL + CONTROLLER_TYPE_PS5 )], GLattr => [qw( GL_RED_SIZE @@ -323,90 +348,84 @@ BEGIN { GL_CONTEXT_RESET_NO_NOTIFICATION GL_CONTEXT_RESET_LOSE_CONTEXT )], - RendererFlags => { - RENDERER_SOFTWARE => 0x00000001, - RENDERER_ACCELERATED => 0x00000002, - RENDERER_PRESENTVSYNC => 0x00000004, - RENDERER_TARGETTEXTURE => 0x00000008, - }, - ScaleMode => [qw( - SCALEMODENEAREST - SCALEMODELINEAR - SCALEMODEBEST - )], - TextureAccess => [qw( - TEXTUREACCESS_STATIC - TEXTUREACCESS_STREAMING - TEXTUREACCESS_TARGET - )], - TextureModulate => [qw( - TEXTUREMODULATE_NONE - TEXTUREMODULATE_COLOR - TEXTUREMODULATE_ALPHA - )], - RendererFlip => [qw( - FLIP_NONE - FLIP_HORIZONTAL - FLIP_VERTICAL - )], - BlendMode => { - BLENDMODE_NONE => 0x00000000, - BLENDMODE_BLEND => 0x00000001, - BLENDMODE_ADD => 0x00000002, - BLENDMODE_MOD => 0x00000004, - BLENDMODE_MUL => 0x00000008, - BLENDMODE_INVALID => 0x7FFFFFFF, + HatPosition => { + HAT_CENTERED => 0x0, + HAT_UP => 0x1, + HAT_RIGHT => 0x2, + HAT_DOWN => 0x4, + HAT_LEFT => 0x8, }, - BlendOperation => [qw( - BLENDOPERATION_ADD - BLENDOPERATION_SUBTRACT - BLENDOPERATION_REV_SUBTRACT - BLENDOPERATION_MINIMUM - BLENDOPERATION_MAXIMUM - )], - BlendFactor => [qw( - BLENDFACTOR_ZERO - BLENDFACTOR_ONE - BLENDFACTOR_SRC_COLOR - BLENDFACTOR_ONE_MINUS_SRC_COLOR - BLENDFACTOR_SRC_ALPHA - BLENDFACTOR_ONE_MINUS_SRC_ALPHA - BLENDFACTOR_DST_COLOR - BLENDFACTOR_ONE_MINUS_DST_COLOR - BLENDFACTOR_DST_ALPHA - BLENDFACTOR_ONE_MINUS_DST_ALPHA + HintPriority => [qw( + HINT_DEFAULT + HINT_NORMAL + HINT_OVERRIDE )], - PowerState => [qw( - POWERSTATE_UNKNOWN - POWERSTATE_ON_BATTERY - POWERSTATE_NO_BATTERY - POWERSTATE_CHARGING - POWERSTATE_CHARGED + JoystickPowerLevel => [qw( + JOYSTICK_POWER_UNKNOWN + JOYSTICK_POWER_EMPTY + JOYSTICK_POWER_LOW + JOYSTICK_POWER_MEDIUM + JOYSTICK_POWER_FULL + JOYSTICK_POWER_WIRED + JOYSTICK_POWER_MAX )], - EventAction => [qw( - ADDEVENT - PEEKEVENT - GETEVENT + LogCategory => [qw( + LOG_CATEGORY_APPLICATION + LOG_CATEGORY_ERROR + LOG_CATEGORY_ASSERT + LOG_CATEGORY_SYSTEM + LOG_CATEGORY_AUDIO + LOG_CATEGORY_VIDEO + LOG_CATEGORY_RENDER + LOG_CATEGORY_INPUT + LOG_CATEGORY_TEST + LOG_CATEGORY_RESERVED1 + LOG_CATEGORY_RESERVED2 + LOG_CATEGORY_RESERVED3 + LOG_CATEGORY_RESERVED4 + LOG_CATEGORY_RESERVED5 + LOG_CATEGORY_RESERVED6 + LOG_CATEGORY_RESERVED7 + LOG_CATEGORY_RESERVED8 + LOG_CATEGORY_RESERVED9 + LOG_CATEGORY_RESERVED10 + LOG_CATEGORY_CUSTOM )], - SystemCursor => [qw( - SYSTEM_CURSOR_ARROW - SYSTEM_CURSOR_IBEAM - SYSTEM_CURSOR_WAIT - SYSTEM_CURSOR_CROSSHAIR - SYSTEM_CURSOR_WAITARROW - SYSTEM_CURSOR_SIZENWSE - SYSTEM_CURSOR_SIZENESW - SYSTEM_CURSOR_SIZEWE - SYSTEM_CURSOR_SIZENS - SYSTEM_CURSOR_SIZEALL - SYSTEM_CURSOR_NO - SYSTEM_CURSOR_HAND - NUM_SYSTEM_CURSORS + LogPriority => [qw( + LOG_PRIORITY_VERBOSE=1 + LOG_PRIORITY_DEBUG + LOG_PRIORITY_INFO + LOG_PRIORITY_WARN + LOG_PRIORITY_ERROR + LOG_PRIORITY_CRITICAL + NUM_LOG_PRIORITIES )], MouseWheelDirection => [qw( MOUSEWHEEL_NORMAL MOUSEWHEEL_FLIPPED )], + PackedLayout => [qw( + PACKEDLAYOUT_NONE + PACKEDLAYOUT_332 + PACKEDLAYOUT_4444 + PACKEDLAYOUT_1555 + PACKEDLAYOUT_5551 + PACKEDLAYOUT_565 + PACKEDLAYOUT_8888 + PACKEDLAYOUT_2101010 + PACKEDLAYOUT_1010102 + )], + PackedOrder => [qw( + PACKEDORDER_NONE + PACKEDORDER_XRGB + PACKEDORDER_RGBX + PACKEDORDER_ARGB + PACKEDORDER_RGBA + PACKEDORDER_XBGR + PACKEDORDER_BGRX + PACKEDORDER_ABGR + PACKEDORDER_BGRA + )], PixelType => [qw( PIXELTYPE_UNKNOWN PIXELTYPE_INDEX1 @@ -421,41 +440,28 @@ BEGIN { PIXELTYPE_ARRAYF16 PIXELTYPE_ARRAYF32 )], - BitmapOrder => [qw( - BITMAPORDER_NONE - BITMAPORDER_4321 - BITMAPORDER_1234 - )], - PackedOrder => [qw( - PACKEDORDER_NONE - PACKEDORDER_XRGB - PACKEDORDER_RGBX - PACKEDORDER_ARGB - PACKEDORDER_RGBA - PACKEDORDER_XBGR - PACKEDORDER_BGRX - PACKEDORDER_ABGR - PACKEDORDER_BGRA + PowerState => [qw( + POWERSTATE_UNKNOWN + POWERSTATE_ON_BATTERY + POWERSTATE_NO_BATTERY + POWERSTATE_CHARGING + POWERSTATE_CHARGED )], - ArrayOrder => [qw( - ARRAYORDER_NONE - ARRAYORDER_RGB - ARRAYORDER_RGBA - ARRAYORDER_ARGB - ARRAYORDER_BGR - ARRAYORDER_BGRA - ARRAYORDER_ABGR + RendererFlags => { + RENDERER_SOFTWARE => 0x00000001, + RENDERER_ACCELERATED => 0x00000002, + RENDERER_PRESENTVSYNC => 0x00000004, + RENDERER_TARGETTEXTURE => 0x00000008, + }, + RendererFlip => [qw( + FLIP_NONE + FLIP_HORIZONTAL + FLIP_VERTICAL )], - PackedLayout => [qw( - PACKEDLAYOUT_NONE - PACKEDLAYOUT_332 - PACKEDLAYOUT_4444 - PACKEDLAYOUT_1555 - PACKEDLAYOUT_5551 - PACKEDLAYOUT_565 - PACKEDLAYOUT_8888 - PACKEDLAYOUT_2101010 - PACKEDLAYOUT_1010102 + ScaleMode => [qw( + SCALEMODENEAREST + SCALEMODELINEAR + SCALEMODEBEST )], ScanCode => [qw( SCANCODE_UNKNOWN @@ -708,98 +714,92 @@ BEGIN { NUM_SCANCODES=512 )], - EventType => [qw( - FIRSTEVENT=0 - - QUIT=0x100 - - APP_TERMINATING - APP_LOWMEMORY - APP_WILLENTERBACKGROUND - APP_DIDENTERBACKGROUND - APP_WILLENTERFOREGROUND - APP_DIDENTERFOREGROUND - - LOCALECHANGED - - DISPLAYEVENT=0x150 - - WINDOWEVENT=0x200 - SYSWMEVENT - - KEYDOWN=0x300 - KEYUP - TEXTEDITING - TEXTINPUT - KEYMAPCHANGED - - MOUSEMOTION=0x400 - MOUSEBUTTONDOWN - MOUSEBUTTONUP - MOUSEWHEEL - - JOYAXISMOTION=0x600 - JOYBALLMOTION - JOYHATMOTION - JOYBUTTONDOWN - JOYBUTTONUP - JOYDEVICEADDED - JOYDEVICEREMOVED - - CONTROLLERAXISMOTION=0x650 - CONTROLLERBUTTONDOWN - CONTROLLERBUTTONUP - CONTROLLERDEVICEADDED - CONTROLLERDEVICEREMOVED - CONTROLLERDEVICEREMAPPED - CONTROLLERTOUCHPADDOWN - CONTROLLERTOUCHPADMOTION - CONTROLLERTOUCHPADUP - CONTROLLERSENSORUPDATE - - FINGERDOWN=0x700 - FINGERUP - FINGERMOTION - - DOLLARGESTURE=0x800 - DOLLARRECORD - MULTIGESTURE - - CLIPBOARDUPDATE=0x900 - - DROPFILE=0x1000 - DROPTEXT - DROPBEGIN - DROPCOMPLETE - - AUDIODEVICEADDED=0x1100 - AUDIODEVICEREMOVED - - SENSORUPDATE=0x1200 - - RENDER_TARGETS_RESET=0x2000 - RENDER_DEVICE_RESET - - USEREVENT=0x8000 - - LASTEVENT=0xFFFF + SystemCursor => [qw( + SYSTEM_CURSOR_ARROW + SYSTEM_CURSOR_IBEAM + SYSTEM_CURSOR_WAIT + SYSTEM_CURSOR_CROSSHAIR + SYSTEM_CURSOR_WAITARROW + SYSTEM_CURSOR_SIZENWSE + SYSTEM_CURSOR_SIZENESW + SYSTEM_CURSOR_SIZEWE + SYSTEM_CURSOR_SIZENS + SYSTEM_CURSOR_SIZEALL + SYSTEM_CURSOR_NO + SYSTEM_CURSOR_HAND + NUM_SYSTEM_CURSORS )], SYSWM_TYPE => [qw( - SYSWM_UNKNOWN - SYSWM_WINDOWS - SYSWM_X11 - SYSWM_DIRECTFB - SYSWM_COCOA - SYSWM_UIKIT - SYSWM_WAYLAND - SYSWM_MIR - SYSWM_WINRT - SYSWM_ANDROID - SYSWM_VIVANTE - SYSWM_OS2 - SYSWM_HAIKU - SYSWM_KMSDRM + SYSWM_UNKNOWN + SYSWM_WINDOWS + SYSWM_X11 + SYSWM_DIRECTFB + SYSWM_COCOA + SYSWM_UIKIT + SYSWM_WAYLAND + SYSWM_MIR + SYSWM_WINRT + SYSWM_ANDROID + SYSWM_VIVANTE + SYSWM_OS2 + SYSWM_HAIKU + SYSWM_KMSDRM + )], + TextureAccess => [qw( + TEXTUREACCESS_STATIC + TEXTUREACCESS_STREAMING + TEXTUREACCESS_TARGET + )], + TextureModulate => [qw( + TEXTUREMODULATE_NONE + TEXTUREMODULATE_COLOR + TEXTUREMODULATE_ALPHA + )], + WindowEventID => [qw( + WINDOWEVENT_NONE + WINDOWEVENT_SHOWN + WINDOWEVENT_HIDDEN + WINDOWEVENT_EXPOSED + WINDOWEVENT_MOVED + WINDOWEVENT_RESIZED + WINDOWEVENT_SIZE_CHANGED + WINDOWEVENT_MINIMIZED + WINDOWEVENT_MAXIMIZED + WINDOWEVENT_RESTORED + WINDOWEVENT_ENTER + WINDOWEVENT_LEAVE + WINDOWEVENT_FOCUS_GAINED + WINDOWEVENT_FOCUS_LOST + WINDOWEVENT_CLOSE + WINDOWEVENT_TAKE_FOCUS + WINDOWEVENT_HIT_TEST )], + WindowFlags => { + WINDOW_FULLSCREEN => 0x00000001, + WINDOW_OPENGL => 0x00000002, + WINDOW_SHOWN => 0x00000004, + WINDOW_HIDDEN => 0x00000008, + WINDOW_BORDERLESS => 0x00000010, + WINDOW_RESIZABLE => 0x00000020, + WINDOW_MINIMIZED => 0x00000040, + WINDOW_MAXIMIZED => 0x00000080, + WINDOW_MOUSE_GRABBED => 0x00000100, + WINDOW_INPUT_FOCUS => 0x00000200, + WINDOW_MOUSE_FOCUS => 0x00000400, + WINDOW_FULLSCREEN_DESKTOP => 0x00001001, + WINDOW_FOREIGN => 0x00000800, + WINDOW_ALLOW_HIGHDPI => 0x00002000, + WINDOW_MOUSE_CAPTURE => 0x00004000, + WINDOW_ALWAYS_ON_TOP => 0x00008000, + WINDOW_SKIP_TASKBAR => 0x00010000, + WINDOW_UTILITY => 0x00020000, + WINDOW_TOOLTIP => 0x00040000, + WINDOW_POPUP_MENU => 0x00080000, + WINDOW_KEYBOARD_GRABBED => 0x00100000, + WINDOW_VULKAN => 0x10000000, + WINDOW_METAL => 0x20000000, + WINDOW_INPUT_GRABBED => 0x00000100, + }, ); } From 5ea25ce4c2e1df1b8a0eafce6a32562182dad675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 19:19:25 +0100 Subject: [PATCH 28/36] Move primitive type definitions closer --- lib/SDL2/Raw.pm | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 515eeb6..53f1a3e 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1147,13 +1147,21 @@ BEGIN { ); } +$ffi->mangler( sub { 'SDL_' . shift } ); + $ffi->type( sint32 => 'SDL_BlendMode' ); $ffi->type( sint32 => 'SDL_JoystickID' ); $ffi->type( sint64 => 'SDL_TouchID' ); $ffi->type( sint64 => 'SDL_FingerID' ); $ffi->type( sint64 => 'SDL_GestureID' ); - -$ffi->mangler( sub { 'SDL_' . shift } ); +$ffi->type( opaque => 'SDL_GLContext' ); +$ffi->type( opaque => 'SDL_GameController' ); +$ffi->type( opaque => 'SDL_Joystick' ); +$ffi->type( opaque => 'SDL_RWops' ); +$ffi->type( opaque => 'SDL_Renderer' ); +$ffi->type( opaque => 'SDL_Texture' ); +$ffi->type( opaque => 'SDL_Window' ); +$ffi->type( uint8 => 'SDL_bool' ); # Event structs @@ -1670,14 +1678,6 @@ package SDL2::SysWMinfo { $ffi->type( 'record(SDL2::Rect)' => 'SDL_Rect' ); $ffi->type( 'record(SDL2::Point)' => 'SDL_Point' ); -$ffi->type( opaque => 'SDL_Renderer' ); -$ffi->type( opaque => 'SDL_Texture' ); -$ffi->type( opaque => 'SDL_Window' ); -$ffi->type( opaque => 'SDL_GameController' ); -$ffi->type( opaque => 'SDL_Joystick' ); -$ffi->type( opaque => 'SDL_RWops' ); -$ffi->type( uint8 => 'SDL_bool' ); - $ffi->type( '( SDL_Window, SDL_Point, opaque )->int' => 'SDL_HitTest' ); ## Video From c26c9de0cc37e6dc380840b7653aba971cce5422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 19:47:18 +0100 Subject: [PATCH 29/36] Implement bindings to remaining video functions --- lib/SDL2/Raw.pm | 252 +++++++++++++++++++++++++++++------------------- t/namespace.t | 55 +++++++++++ 2 files changed, 210 insertions(+), 97 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 53f1a3e..3958f7c 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -360,6 +360,18 @@ BEGIN { HINT_NORMAL HINT_OVERRIDE )], + HitTestResult => [qw( + HITTEST_NORMAL + HITTEST_DRAGGABLE + HITTEST_RESIZE_TOPLEFT + HITTEST_RESIZE_TOP + HITTEST_RESIZE_TOPRIGHT + HITTEST_RESIZE_RIGHT + HITTEST_RESIZE_BOTTOMRIGHT + HITTEST_RESIZE_BOTTOM + HITTEST_RESIZE_BOTTOMLEFT + HITTEST_RESIZE_LEFT + )], JoystickPowerLevel => [qw( JOYSTICK_POWER_UNKNOWN JOYSTICK_POWER_EMPTY @@ -400,6 +412,25 @@ BEGIN { LOG_PRIORITY_CRITICAL NUM_LOG_PRIORITIES )], + MessageBoxFlags => { + MESSAGEBOX_ERROR => 0x010, + MESSAGEBOX_WARNING => 0x020, + MESSAGEBOX_INFORMATION => 0x040, + MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT => 0x080, + MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT => 0x100, + }, + MessageBoxButtonFlags => { + MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT => 1, + MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT => 2, + }, + MessageBoxColorType => [qw( + MESSAGEBOX_COLOR_BACKGROUND + MESSAGEBOX_COLOR_TEXT + MESSAGEBOX_COLOR_BUTTON_BORDER + MESSAGEBOX_COLOR_BUTTON_BACKGROUND + MESSAGEBOX_COLOR_BUTTON_SELECTED + MESSAGEBOX_COLOR_MAX + )], MouseWheelDirection => [qw( MOUSEWHEEL_NORMAL MOUSEWHEEL_FLIPPED @@ -1572,6 +1603,45 @@ package SDL2::JoystickGUID { FFI::C->struct( SDL_JoystickGUID => [ data => 'uint8[16]' ]); } +package SDL2::MessageBoxButtonData { + FFI::C->struct( SDL_MessageBoxButtonData => [ + flags => 'uint32', + buttonid => 'int', + _text => 'opaque', + ]); + + sub text { $ffi->cast( opaque => string => shift->_text ) } +} + +package SDL2::MessageBoxColor { + FFI::C->struct( SDL_MessageBoxColor => [ + r => 'uint8', + g => 'uint8', + b => 'uint8', + ]); +} + +package SDL2::MessageBoxColorScheme { + FFI::C->struct( SDL_MessageBoxColorScheme => [ + colors => 'opaque', # FIXME: 'SDL_MessageBoxColor[' . SDL2::MESSAGEBOX_COLOR_MAX . ']', + ]); +} + +package SDL2::MessageBoxData { + FFI::C->struct( SDL_MessageBoxData => [ + flags => 'uint32', # MessageBoxFlags + window => 'SDL_Window', + _title => 'opaque', + _message => 'opaque', + numbuttons => 'int', + buttons => 'SDL_MessageBoxButtonData', + colorScheme => 'SDL_MessageBoxColorScheme', + ]); + + sub title { $ffi->cast( opaque => string => sfhit->_title ) } + sub message { $ffi->cast( opaque => string => sfhit->_message ) } +} + package SDL2::PixelFormat { FFI::C->struct( SDL_PixelFormat => [ format => 'uint32', @@ -1682,103 +1752,91 @@ $ffi->type( '( SDL_Window, SDL_Point, opaque )->int' => 'SDL_HitTest' ); ## Video -$ffi->attach( CreateWindow => [qw( string sint32 sint32 sint32 sint32 sint32 )] => 'SDL_Window' ); -$ffi->attach( CreateWindowFrom => [qw( opaque )] => 'SDL_Window' ); -$ffi->attach( DestroyWindow => [qw( SDL_Window )] => 'void' ); -$ffi->attach( DisableScreenSaver => [ ] => 'void' ); -$ffi->attach( EnableScreenSaver => [ ] => 'void' ); -$ffi->attach( GetClosestDisplayMode => [qw( int SDL_DisplayMode SDL_DisplayMode )] => 'SDL_DisplayMode' ); -$ffi->attach( GetCurrentDisplayMode => [qw( int SDL_DisplayMode )] => 'int' ); -$ffi->attach( GetCurrentVideoDriver => [ ] => 'string' ); -$ffi->attach( GetDesktopDisplayMode => [qw( int SDL_DisplayMode )] => 'int' ); -# SDL_GetDisplayBounds -$ffi->attach( GetDisplayDPI => [qw( int float* float* float* )] => 'int' ); -$ffi->attach( GetDisplayMode => [qw( int SDL_DisplayMode )] => 'int' ); -$ffi->attach( GetDisplayName => [qw( int )] => 'string' ); -# SDL_GetDisplayUsableBounds -$ffi->attach( GetGrabbedWindow => [ ] => 'SDL_Window' ); -# SDL_GetNumDisplayModes -# SDL_GetNumVideoDisplays -# SDL_GetNumVideoDrivers -$ffi->attach( GetVideoDriver => [qw( int )] => 'string' ); -$ffi->attach( GetWindowBordersSize => [qw( SDL_Window int* int* int* int* )] => 'int' ); # 2.0.5 -$ffi->attach( GetWindowBrightness => [qw( SDL_Window )] => 'float' ); -$ffi->attach( GetWindowData => [qw( SDL_Window string )] => 'opaque' ); -$ffi->attach( GetWindowDisplayIndex => [qw( SDL_Window )] => 'int' ); -$ffi->attach( GetWindowDisplayMode => [qw( SDL_Window SDL_DisplayMode )] => 'int' ); -$ffi->attach( GetWindowFlags => [qw( SDL_Window )] => 'uint32' ); -$ffi->attach( GetWindowFromID => [qw( uint32 )] => 'SDL_Window' ); -$ffi->attach( GetWindowGammaRamp => [qw( SDL_Window int* int* int* )] => 'int' ); -$ffi->attach( GetWindowGrab => [qw( SDL_Window )] => 'SDL_bool' ); -$ffi->attach( GetWindowID => [qw( SDL_Window )] => 'uint32' ); -$ffi->attach( GetWindowMinimumSize => [qw( SDL_Window int* int* )] => 'void' ); -$ffi->attach( GetWindowMaximumSize => [qw( SDL_Window int* int* )] => 'void' ); -$ffi->attach( GetWindowOpacity => [qw( SDL_Window float* )] => 'int' ); # 2.0.5 -$ffi->attach( GetWindowPixelFormat => [qw( SDL_Window )] => 'uint32' ); -$ffi->attach( GetWindowPosition => [qw( SDL_Window int* int* )] => 'void' ); -$ffi->attach( GetWindowSize => [qw( SDL_Window int* int* )] => 'void' ); -$ffi->attach( GetWindowSurface => [qw( SDL_Window )] => 'SDL_Surface' ); -$ffi->attach( GetWindowTitle => [qw( SDL_Window )] => 'string' ); -$ffi->attach( GetWindowWMInfo => [qw( SDL_Window SDL_SysWMinfo )] => 'int' ); -# SDL_GL_CreateContext -# SDL_GL_DeleteContext -# SDL_GL_ExtensionSupported -# SDL_GL_GetAttribute -# SDL_GL_GetCurrentContext -# SDL_GL_GetCurrentWindow -# SDL_GL_GetDrawableSize -# SDL_GL_GetProcAddress -# SDL_GL_GetSwapInterval -# SDL_GL_LoadLibrary -# SDL_GL_MakeCurrent -# SDL_GL_ResetAttributes -# SDL_GL_SetAttribute -# SDL_GL_SetSwapInterval -# SDL_GL_SwapWindow -# SDL_GL_UnloadLibrary -# SDL_GLattr -# SDL_GLcontextFlag -# SDL_GLprofile -$ffi->attach( HideWindow => [ qw( SDL_Window )] => 'void' ); -# SDL_HitTestResult -$ffi->attach( IsScreenSaverEnabled => [ ] => 'SDL_bool' ); -$ffi->attach( MaximizeWindow => [ qw( SDL_Window )] => 'void' ); -# SDL_MessageBoxButtonData -# SDL_MessageBoxButtonFlags -# SDL_MessageBoxColor -# SDL_MessageBoxColorScheme -# SDL_MessageBoxColorType -# SDL_MessageBoxData -# SDL_MessageBoxFlags - -$ffi->attach( MinimizeWindow => [qw( SDL_Window )] => 'void' ); -$ffi->attach( RaiseWindow => [qw( SDL_Window )] => 'void' ); -$ffi->attach( RestoreWindow => [qw( SDL_Window )] => 'void' ); -$ffi->attach( SetWindowBordered => [qw( SDL_Window SDL_bool )] => 'void' ); -$ffi->attach( SetWindowBrightness => [qw( SDL_Window float )] => 'int' ); -$ffi->attach( SetWindowData => [qw( SDL_Window string opaque )] => 'opaque' ); -$ffi->attach( SetWindowDisplayMode => [qw( SDL_Window SDL_DisplayMode )] => 'int' ); -$ffi->attach( SetWindowFullscreen => [qw( SDL_Window uint32 )] => 'int' ); -$ffi->attach( SetWindowGammaRamp => [qw( SDL_Window uint16 uint16 uint16 )] => 'int' ); -$ffi->attach( SetWindowGrab => [qw( SDL_Window SDL_bool )] => 'void' ); -$ffi->attach( SetWindowHitTest => [qw( SDL_Window SDL_HitTest )] => 'int' ); -$ffi->attach( SetWindowIcon => [qw( SDL_Window SDL_Surface )] => 'void' ); -$ffi->attach( SetWindowInputFocus => [qw( SDL_Window )] => 'int' ); -$ffi->attach( SetWindowMaximumSize => [qw( SDL_Window int int )] => 'void' ); -$ffi->attach( SetWindowMinimumSize => [qw( SDL_Window int int )] => 'void' ); -$ffi->attach( SetWindowModalFor => [qw( SDL_Window SDL_Window )] => 'int' ); # 2.0.5 -$ffi->attach( SetWindowOpacity => [qw( SDL_Window float )] => 'int' ); # 2.0.5 -$ffi->attach( SetWindowPosition => [qw( SDL_Window int int )] => 'void' ); -$ffi->attach( SetWindowResizable => [qw( SDL_Window SDL_bool )] => 'void' ); # 2.0.5 -$ffi->attach( SetWindowSize => [qw( SDL_Window int int )] => 'void' ); -$ffi->attach( SetWindowTitle => [qw( SDL_Window string )] => 'void' ); -# TODO: SDL_ShowMessageBox -# TODO: SDL_ShowSimpleMessageBox -$ffi->attach( ShowWindow => ['SDL_Window' ] => 'void' ); -$ffi->attach( UpdateWindowSurface => ['SDL_Window' ] => 'int' ); -# TODO: SDL_UpdateWindowSurfaceRects -$ffi->attach( VideoInit => ['string'] => 'int' ); -$ffi->attach( VideoQuit => [ ] => 'void' ); +$ffi->attach( CreateWindow => [qw( string sint32 sint32 sint32 sint32 sint32 )] => 'SDL_Window' ); +$ffi->attach( CreateWindowFrom => [qw( opaque )] => 'SDL_Window' ); +$ffi->attach( DestroyWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( DisableScreenSaver => [ ] => 'void' ); +$ffi->attach( EnableScreenSaver => [ ] => 'void' ); +$ffi->attach( GetClosestDisplayMode => [qw( int SDL_DisplayMode SDL_DisplayMode )] => 'SDL_DisplayMode' ); +$ffi->attach( GetCurrentDisplayMode => [qw( int SDL_DisplayMode )] => 'int' ); +$ffi->attach( GetCurrentVideoDriver => [ ] => 'string' ); +$ffi->attach( GetDesktopDisplayMode => [qw( int SDL_DisplayMode )] => 'int' ); +$ffi->attach( GetDisplayBounds => [qw( int SDL_Rect* )] => 'int' ); +$ffi->attach( GetDisplayDPI => [qw( int float* float* float* )] => 'int' ); +$ffi->attach( GetDisplayMode => [qw( int SDL_DisplayMode )] => 'int' ); +$ffi->attach( GetDisplayName => [qw( int )] => 'string' ); +$ffi->attach( GetDisplayUsableBounds => [qw( int SDL_Rect* )] => 'int' ); +$ffi->attach( GetGrabbedWindow => [ ] => 'SDL_Window' ); +$ffi->attach( GetNumDisplayModes => [qw( int )] => 'int' ); +$ffi->attach( GetNumVideoDisplays => [ ] => 'int' ); +$ffi->attach( GetNumVideoDrivers => [ ] => 'int' ); +$ffi->attach( GetVideoDriver => [qw( int )] => 'string' ); +$ffi->attach( GetWindowBordersSize => [qw( SDL_Window int* int* int* int* )] => 'int' ); # 2.0.5 +$ffi->attach( GetWindowBrightness => [qw( SDL_Window )] => 'float' ); +$ffi->attach( GetWindowData => [qw( SDL_Window string )] => 'opaque' ); +$ffi->attach( GetWindowDisplayIndex => [qw( SDL_Window )] => 'int' ); +$ffi->attach( GetWindowDisplayMode => [qw( SDL_Window SDL_DisplayMode )] => 'int' ); +$ffi->attach( GetWindowFlags => [qw( SDL_Window )] => 'uint32' ); +$ffi->attach( GetWindowFromID => [qw( uint32 )] => 'SDL_Window' ); +$ffi->attach( GetWindowGammaRamp => [qw( SDL_Window int* int* int* )] => 'int' ); +$ffi->attach( GetWindowGrab => [qw( SDL_Window )] => 'SDL_bool' ); +$ffi->attach( GetWindowID => [qw( SDL_Window )] => 'uint32' ); +$ffi->attach( GetWindowMinimumSize => [qw( SDL_Window int* int* )] => 'void' ); +$ffi->attach( GetWindowMaximumSize => [qw( SDL_Window int* int* )] => 'void' ); +$ffi->attach( GetWindowOpacity => [qw( SDL_Window float* )] => 'int' ); # 2.0.5 +$ffi->attach( GetWindowPixelFormat => [qw( SDL_Window )] => 'uint32' ); +$ffi->attach( GetWindowPosition => [qw( SDL_Window int* int* )] => 'void' ); +$ffi->attach( GetWindowSize => [qw( SDL_Window int* int* )] => 'void' ); +$ffi->attach( GetWindowSurface => [qw( SDL_Window )] => 'SDL_Surface' ); +$ffi->attach( GetWindowTitle => [qw( SDL_Window )] => 'string' ); +$ffi->attach( GetWindowWMInfo => [qw( SDL_Window SDL_SysWMinfo )] => 'int' ); +$ffi->attach( GL_CreateContext => [qw( SDL_Window )] => 'SDL_GLContext' ); +$ffi->attach( GL_DeleteContext => [qw( SDL_Window )] => 'void' ); +$ffi->attach( GL_ExtensionSupported => [qw( string )] => 'SDL_bool' ); +$ffi->attach( GL_GetAttribute => [qw( int int* )] => 'int' ); +$ffi->attach( GL_GetCurrentContext => [ ] => 'SDL_GLContext' ); +$ffi->attach( GL_GetCurrentWindow => [ ] => 'SDL_Window' ); +$ffi->attach( GL_GetDrawableSize => [qw( SDL_Window int* int* )] => 'void' ); +$ffi->attach( GL_GetProcAddress => [qw( string )] => 'opaque' ); +$ffi->attach( GL_GetSwapInterval => [ ] => 'int' ); +$ffi->attach( GL_LoadLibrary => [qw( string )] => 'int' ); +$ffi->attach( GL_MakeCurrent => [qw( SDL_Window SDL_GLContext )] => 'int' ); +$ffi->attach( GL_ResetAttributes => [ ] => 'void' ); +$ffi->attach( GL_SetAttribute => [qw( int int )] => 'int' ); +$ffi->attach( GL_SetSwapInterval => [qw( int )] => 'int' ); +$ffi->attach( GL_SwapWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( GL_UnloadLibrary => [ ] => 'void' ); +$ffi->attach( HideWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( IsScreenSaverEnabled => [ ] => 'SDL_bool' ); +$ffi->attach( MaximizeWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( MinimizeWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( RaiseWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( RestoreWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( SetWindowBordered => [qw( SDL_Window SDL_bool )] => 'void' ); +$ffi->attach( SetWindowBrightness => [qw( SDL_Window float )] => 'int' ); +$ffi->attach( SetWindowData => [qw( SDL_Window string opaque )] => 'opaque' ); +$ffi->attach( SetWindowDisplayMode => [qw( SDL_Window SDL_DisplayMode )] => 'int' ); +$ffi->attach( SetWindowFullscreen => [qw( SDL_Window uint32 )] => 'int' ); +$ffi->attach( SetWindowGammaRamp => [qw( SDL_Window uint16 uint16 uint16 )] => 'int' ); +$ffi->attach( SetWindowGrab => [qw( SDL_Window SDL_bool )] => 'void' ); +$ffi->attach( SetWindowHitTest => [qw( SDL_Window SDL_HitTest )] => 'int' ); +$ffi->attach( SetWindowIcon => [qw( SDL_Window SDL_Surface )] => 'void' ); +$ffi->attach( SetWindowInputFocus => [qw( SDL_Window )] => 'int' ); +$ffi->attach( SetWindowMaximumSize => [qw( SDL_Window int int )] => 'void' ); +$ffi->attach( SetWindowMinimumSize => [qw( SDL_Window int int )] => 'void' ); +$ffi->attach( SetWindowModalFor => [qw( SDL_Window SDL_Window )] => 'int' ); # 2.0.5 +$ffi->attach( SetWindowOpacity => [qw( SDL_Window float )] => 'int' ); # 2.0.5 +$ffi->attach( SetWindowPosition => [qw( SDL_Window int int )] => 'void' ); +$ffi->attach( SetWindowResizable => [qw( SDL_Window SDL_bool )] => 'void' ); # 2.0.5 +$ffi->attach( SetWindowSize => [qw( SDL_Window int int )] => 'void' ); +$ffi->attach( SetWindowTitle => [qw( SDL_Window string )] => 'void' ); +$ffi->attach( ShowMessageBox => [qw( SDL_MessageBoxData int* )] => 'int' ); +$ffi->attach( ShowSimpleMessageBox => [qw( uint32 string string SDL_Window )] => 'int' ); +$ffi->attach( ShowWindow => [qw( SDL_Window )] => 'void' ); +$ffi->attach( UpdateWindowSurface => [qw( SDL_Window )] => 'int' ); +$ffi->attach( UpdateWindowSurfaceRects => [qw( SDL_Window int* int )] => 'int' ); # TODO: SDL_Rect* +$ffi->attach( VideoInit => [qw( string )] => 'int' ); +$ffi->attach( VideoQuit => [ ] => 'void' ); ## SDL diff --git a/t/namespace.t b/t/namespace.t index a8dab02..4e1f709 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -214,17 +214,33 @@ is [ sort keys %SDL2:: ], [qw( GL_CONTEXT_RESET_NOTIFICATION GL_CONTEXT_RESET_NO_NOTIFICATION GL_CONTEXT_ROBUST_ACCESS_FLAG + GL_CreateContext GL_DEPTH_SIZE GL_DOUBLEBUFFER + GL_DeleteContext + GL_ExtensionSupported GL_FRAMEBUFFER_SRGB_CAPABLE GL_GREEN_SIZE + GL_GetAttribute + GL_GetCurrentContext + GL_GetCurrentWindow + GL_GetDrawableSize + GL_GetProcAddress + GL_GetSwapInterval + GL_LoadLibrary GL_MULTISAMPLEBUFFERS GL_MULTISAMPLESAMPLES + GL_MakeCurrent GL_RED_SIZE GL_RETAINED_BACKING + GL_ResetAttributes GL_SHARE_WITH_CURRENT_CONTEXT GL_STENCIL_SIZE GL_STEREO + GL_SetAttribute + GL_SetSwapInterval + GL_SwapWindow + GL_UnloadLibrary GLattr GLcontextFlag GLcontextReleaseFlag @@ -260,14 +276,19 @@ is [ sort keys %SDL2:: ], [qw( GetCurrentDisplayMode GetCurrentVideoDriver GetDesktopDisplayMode + GetDisplayBounds GetDisplayDPI GetDisplayMode GetDisplayName + GetDisplayUsableBounds GetError GetEventFilter GetGrabbedWindow + GetNumDisplayModes GetNumTouchDevices GetNumTouchFingers + GetNumVideoDisplays + GetNumVideoDrivers GetPerformanceCounter GetPerformanceFrequency GetRenderTarget @@ -313,11 +334,22 @@ is [ sort keys %SDL2:: ], [qw( HINT_DEFAULT HINT_NORMAL HINT_OVERRIDE + HITTEST_DRAGGABLE + HITTEST_NORMAL + HITTEST_RESIZE_BOTTOM + HITTEST_RESIZE_BOTTOMLEFT + HITTEST_RESIZE_BOTTOMRIGHT + HITTEST_RESIZE_LEFT + HITTEST_RESIZE_RIGHT + HITTEST_RESIZE_TOP + HITTEST_RESIZE_TOPLEFT + HITTEST_RESIZE_TOPRIGHT HasEvent HasEvents HatPosition HideWindow HintPriority + HitTestResult IGNORE INIT_AUDIO INIT_EVENTS @@ -672,6 +704,19 @@ is [ sort keys %SDL2:: ], [qw( LogSetPriority LogVerbose LogWarn + MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT + MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT + MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT + MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT + MESSAGEBOX_COLOR_BACKGROUND + MESSAGEBOX_COLOR_BUTTON_BACKGROUND + MESSAGEBOX_COLOR_BUTTON_BORDER + MESSAGEBOX_COLOR_BUTTON_SELECTED + MESSAGEBOX_COLOR_MAX + MESSAGEBOX_COLOR_TEXT + MESSAGEBOX_ERROR + MESSAGEBOX_INFORMATION + MESSAGEBOX_WARNING MIX_MAXVOLUME MOUSEBUTTONDOWN MOUSEBUTTONUP @@ -681,6 +726,13 @@ is [ sort keys %SDL2:: ], [qw( MOUSEWHEEL_NORMAL MULTIGESTURE MaximizeWindow + MessageBoxButtonData:: + MessageBoxButtonFlags + MessageBoxColor:: + MessageBoxColorScheme:: + MessageBoxColorType + MessageBoxData:: + MessageBoxFlags MinimizeWindow MouseButtonEvent:: MouseMotionEvent:: @@ -1129,6 +1181,8 @@ is [ sort keys %SDL2:: ], [qw( SetWindowResizable SetWindowSize SetWindowTitle + ShowMessageBox + ShowSimpleMessageBox ShowWindow Surface:: SysWMEvent:: @@ -1152,6 +1206,7 @@ is [ sort keys %SDL2:: ], [qw( USEREVENT UnlockTexture UpdateWindowSurface + UpdateWindowSurfaceRects UpperBlit UserEvent:: Version:: From 7ecda27af7fc6500858daf88372d45312486f457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 22:07:08 +0100 Subject: [PATCH 30/36] Add HINT constants --- lib/SDL2/Raw.pm | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ t/namespace.t | 115 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 3958f7c..bd0f723 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -33,6 +33,125 @@ use constant { PRESSED => 1, }; +# Hints - https://github.com/libsdl-org/SDL/blob/main/include/SDL_hints.h +use constant { + HINT_ACCELEROMETER_AS_JOYSTICK => 'SDL_ACCELEROMETER_AS_JOYSTICK', + HINT_ALLOW_ALT_TAB_WHILE_GRABBED => 'SDL_ALLOW_ALT_TAB_WHILE_GRABBED', + HINT_ALLOW_TOPMOST => 'SDL_ALLOW_TOPMOST', + HINT_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION => 'SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION', + HINT_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION => 'SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION', + HINT_ANDROID_BLOCK_ON_PAUSE => 'SDL_ANDROID_BLOCK_ON_PAUSE', + HINT_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO => 'SDL_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO', + HINT_ANDROID_TRAP_BACK_BUTTON => 'SDL_ANDROID_TRAP_BACK_BUTTON', + HINT_APPLE_TV_CONTROLLER_UI_EVENTS => 'SDL_APPLE_TV_CONTROLLER_UI_EVENTS', + HINT_APPLE_TV_REMOTE_ALLOW_ROTATION => 'SDL_APPLE_TV_REMOTE_ALLOW_ROTATION', + HINT_AUDIO_CATEGORY => 'SDL_AUDIO_CATEGORY', + HINT_AUDIO_DEVICE_APP_NAME => 'SDL_AUDIO_DEVICE_APP_NAME', + HINT_AUDIO_DEVICE_STREAM_NAME => 'SDL_AUDIO_DEVICE_STREAM_NAME', + HINT_AUDIO_RESAMPLING_MODE => 'SDL_AUDIO_RESAMPLING_MODE', + HINT_AUTO_UPDATE_JOYSTICKS => 'SDL_AUTO_UPDATE_JOYSTICKS', + HINT_AUTO_UPDATE_SENSORS => 'SDL_AUTO_UPDATE_SENSORS', + HINT_BMP_SAVE_LEGACY_FORMAT => 'SDL_BMP_SAVE_LEGACY_FORMAT', + HINT_DISPLAY_USABLE_BOUNDS => 'SDL_DISPLAY_USABLE_BOUNDS', + HINT_EMSCRIPTEN_ASYNCIFY => 'SDL_EMSCRIPTEN_ASYNCIFY', + HINT_EMSCRIPTEN_KEYBOARD_ELEMENT => 'SDL_EMSCRIPTEN_KEYBOARD_ELEMENT', + HINT_ENABLE_STEAM_CONTROLLERS => 'SDL_ENABLE_STEAM_CONTROLLERS', + HINT_EVENT_LOGGING => 'SDL_EVENT_LOGGING', + HINT_FRAMEBUFFER_ACCELERATION => 'SDL_FRAMEBUFFER_ACCELERATION', + HINT_GAMECONTROLLERCONFIG => 'SDL_GAMECONTROLLERCONFIG', + HINT_GAMECONTROLLERCONFIG_FILE => 'SDL_GAMECONTROLLERCONFIG_FILE', + HINT_GAMECONTROLLERTYPE => 'SDL_GAMECONTROLLERTYPE', + HINT_GAMECONTROLLER_IGNORE_DEVICES => 'SDL_GAMECONTROLLER_IGNORE_DEVICES', + HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT => 'SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT', + HINT_GAMECONTROLLER_USE_BUTTON_LABELS => 'SDL_GAMECONTROLLER_USE_BUTTON_LABELS', + HINT_GRAB_KEYBOARD => 'SDL_GRAB_KEYBOARD', + HINT_IDLE_TIMER_DISABLED => 'SDL_IOS_IDLE_TIMER_DISABLED', + HINT_IME_INTERNAL_EDITING => 'SDL_IME_INTERNAL_EDITING', + HINT_IOS_HIDE_HOME_INDICATOR => 'SDL_IOS_HIDE_HOME_INDICATOR', + HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS => 'SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS', + HINT_JOYSTICK_HIDAPI => 'SDL_JOYSTICK_HIDAPI', + HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT => 'SDL_JOYSTICK_HIDAPI_CORRELATE_XINPUT', + HINT_JOYSTICK_HIDAPI_GAMECUBE => 'SDL_JOYSTICK_HIDAPI_GAMECUBE', + HINT_JOYSTICK_HIDAPI_JOY_CONS => 'SDL_JOYSTICK_HIDAPI_JOY_CONS', + HINT_JOYSTICK_HIDAPI_PS4 => 'SDL_JOYSTICK_HIDAPI_PS4', + HINT_JOYSTICK_HIDAPI_PS4_RUMBLE => 'SDL_JOYSTICK_HIDAPI_PS4_RUMBLE', + HINT_JOYSTICK_HIDAPI_PS5 => 'SDL_JOYSTICK_HIDAPI_PS5', + HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED => 'SDL_JOYSTICK_HIDAPI_PS5_PLAYER_LED', + HINT_JOYSTICK_HIDAPI_PS5_RUMBLE => 'SDL_JOYSTICK_HIDAPI_PS5_RUMBLE', + HINT_JOYSTICK_HIDAPI_STADIA => 'SDL_JOYSTICK_HIDAPI_STADIA', + HINT_JOYSTICK_HIDAPI_STEAM => 'SDL_JOYSTICK_HIDAPI_STEAM', + HINT_JOYSTICK_HIDAPI_SWITCH => 'SDL_JOYSTICK_HIDAPI_SWITCH', + HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED => 'SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED', + HINT_JOYSTICK_HIDAPI_XBOX => 'SDL_JOYSTICK_HIDAPI_XBOX', + HINT_JOYSTICK_RAWINPUT => 'SDL_JOYSTICK_RAWINPUT', + HINT_JOYSTICK_THREAD => 'SDL_JOYSTICK_THREAD', + HINT_LINUX_JOYSTICK_DEADZONES => 'SDL_LINUX_JOYSTICK_DEADZONES', + HINT_MAC_BACKGROUND_APP => 'SDL_MAC_BACKGROUND_APP', + HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK => 'SDL_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK', + HINT_MOUSE_DOUBLE_CLICK_RADIUS => 'SDL_MOUSE_DOUBLE_CLICK_RADIUS', + HINT_MOUSE_DOUBLE_CLICK_TIME => 'SDL_MOUSE_DOUBLE_CLICK_TIME', + HINT_MOUSE_FOCUS_CLICKTHROUGH => 'SDL_MOUSE_FOCUS_CLICKTHROUGH', + HINT_MOUSE_NORMAL_SPEED_SCALE => 'SDL_MOUSE_NORMAL_SPEED_SCALE', + HINT_MOUSE_RELATIVE_MODE_WARP => 'SDL_MOUSE_RELATIVE_MODE_WARP', + HINT_MOUSE_RELATIVE_SCALING => 'SDL_MOUSE_RELATIVE_SCALING', + HINT_MOUSE_RELATIVE_SPEED_SCALE => 'SDL_MOUSE_RELATIVE_SPEED_SCALE', + HINT_MOUSE_TOUCH_EVENTS => 'SDL_MOUSE_TOUCH_EVENTS', + HINT_NO_SIGNAL_HANDLERS => 'SDL_NO_SIGNAL_HANDLERS', + HINT_OPENGL_ES_DRIVER => 'SDL_OPENGL_ES_DRIVER', + HINT_ORIENTATIONS => 'SDL_IOS_ORIENTATIONS', + HINT_PREFERRED_LOCALES => 'SDL_PREFERRED_LOCALES', + HINT_QTWAYLAND_CONTENT_ORIENTATION => 'SDL_QTWAYLAND_CONTENT_ORIENTATION', + HINT_QTWAYLAND_WINDOW_FLAGS => 'SDL_QTWAYLAND_WINDOW_FLAGS', + HINT_RENDER_BATCHING => 'SDL_RENDER_BATCHING', + HINT_RENDER_DIRECT3D11_DEBUG => 'SDL_RENDER_DIRECT3D11_DEBUG', + HINT_RENDER_DIRECT3D_THREADSAFE => 'SDL_RENDER_DIRECT3D_THREADSAFE', + HINT_RENDER_DRIVER => 'SDL_RENDER_DRIVER', + HINT_RENDER_LOGICAL_SIZE_MODE => 'SDL_RENDER_LOGICAL_SIZE_MODE', + HINT_RENDER_OPENGL_SHADERS => 'SDL_RENDER_OPENGL_SHADERS', + HINT_RENDER_SCALE_QUALITY => 'SDL_RENDER_SCALE_QUALITY', + HINT_RENDER_VSYNC => 'SDL_RENDER_VSYNC', + HINT_RETURN_KEY_HIDES_IME => 'SDL_RETURN_KEY_HIDES_IME', + HINT_RPI_VIDEO_LAYER => 'SDL_RPI_VIDEO_LAYER', + HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL => 'SDL_THREAD_FORCE_REALTIME_TIME_CRITICAL', + HINT_THREAD_PRIORITY_POLICY => 'SDL_THREAD_PRIORITY_POLICY', + HINT_THREAD_STACK_SIZE => 'SDL_THREAD_STACK_SIZE', + HINT_TIMER_RESOLUTION => 'SDL_TIMER_RESOLUTION', + HINT_TOUCH_MOUSE_EVENTS => 'SDL_TOUCH_MOUSE_EVENTS', + HINT_TV_REMOTE_AS_JOYSTICK => 'SDL_TV_REMOTE_AS_JOYSTICK', + HINT_VIDEO_ALLOW_SCREENSAVER => 'SDL_VIDEO_ALLOW_SCREENSAVER', + HINT_VIDEO_DOUBLE_BUFFER => 'SDL_VIDEO_DOUBLE_BUFFER', + HINT_VIDEO_EXTERNAL_CONTEXT => 'SDL_VIDEO_EXTERNAL_CONTEXT', + HINT_VIDEO_HIGHDPI_DISABLED => 'SDL_VIDEO_HIGHDPI_DISABLED', + HINT_VIDEO_MAC_FULLSCREEN_SPACES => 'SDL_VIDEO_MAC_FULLSCREEN_SPACES', + HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS => 'SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS', + HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT => 'SDL_VIDEO_WINDOW_SHARE_PIXEL_FORMAT', + HINT_VIDEO_WIN_D3DCOMPILER => 'SDL_VIDEO_WIN_D3DCOMPILER', + HINT_VIDEO_X11_FORCE_EGL => 'SDL_VIDEO_X11_FORCE_EGL', + HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR => 'SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR', + HINT_VIDEO_X11_NET_WM_PING => 'SDL_VIDEO_X11_NET_WM_PING', + HINT_VIDEO_X11_WINDOW_VISUALID => 'SDL_VIDEO_X11_WINDOW_VISUALID', + HINT_VIDEO_X11_XINERAMA => 'SDL_VIDEO_X11_XINERAMA', + HINT_VIDEO_X11_XRANDR => 'SDL_VIDEO_X11_XRANDR', + HINT_VIDEO_X11_XVIDMODE => 'SDL_VIDEO_X11_XVIDMODE', + HINT_WAVE_FACT_CHUNK => 'SDL_WAVE_FACT_CHUNK', + HINT_WAVE_RIFF_CHUNK_SIZE => 'SDL_WAVE_RIFF_CHUNK_SIZE', + HINT_WAVE_TRUNCATION => 'SDL_WAVE_TRUNCATION', + HINT_WINDOWS_DISABLE_THREAD_NAMING => 'SDL_WINDOWS_DISABLE_THREAD_NAMING', + HINT_WINDOWS_ENABLE_MESSAGELOOP => 'SDL_WINDOWS_ENABLE_MESSAGELOOP', + HINT_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS => 'SDL_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS', + HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL => 'SDL_WINDOWS_FORCE_SEMAPHORE_KERNEL', + HINT_WINDOWS_INTRESOURCE_ICON => 'SDL_WINDOWS_INTRESOURCE_ICON', + HINT_WINDOWS_INTRESOURCE_ICON_SMALL => 'SDL_WINDOWS_INTRESOURCE_ICON_SMALL', + HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 => 'SDL_WINDOWS_NO_CLOSE_ON_ALT_F4', + HINT_WINDOWS_USE_D3D9EX => 'SDL_WINDOWS_USE_D3D9EX', + HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN => 'SDL_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN', + HINT_WINRT_HANDLE_BACK_BUTTON => 'SDL_WINRT_HANDLE_BACK_BUTTON', + HINT_WINRT_PRIVACY_POLICY_LABEL => 'SDL_WINRT_PRIVACY_POLICY_LABEL', + HINT_WINRT_PRIVACY_POLICY_URL => 'SDL_WINRT_PRIVACY_POLICY_URL', + HINT_XINPUT_ENABLED => 'SDL_XINPUT_ENABLED', + HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING => 'SDL_XINPUT_USE_OLD_JOYSTICK_MAPPING', +}; + # Init flags use constant { INIT_TIMER => 0x000001, diff --git a/t/namespace.t b/t/namespace.t index 4e1f709..6952fca 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -331,9 +331,124 @@ is [ sort keys %SDL2:: ], [qw( HAT_RIGHTDOWN HAT_RIGHTUP HAT_UP + HINT_ACCELEROMETER_AS_JOYSTICK + HINT_ALLOW_ALT_TAB_WHILE_GRABBED + HINT_ALLOW_TOPMOST + HINT_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION + HINT_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION + HINT_ANDROID_BLOCK_ON_PAUSE + HINT_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO + HINT_ANDROID_TRAP_BACK_BUTTON + HINT_APPLE_TV_CONTROLLER_UI_EVENTS + HINT_APPLE_TV_REMOTE_ALLOW_ROTATION + HINT_AUDIO_CATEGORY + HINT_AUDIO_DEVICE_APP_NAME + HINT_AUDIO_DEVICE_STREAM_NAME + HINT_AUDIO_RESAMPLING_MODE + HINT_AUTO_UPDATE_JOYSTICKS + HINT_AUTO_UPDATE_SENSORS + HINT_BMP_SAVE_LEGACY_FORMAT HINT_DEFAULT + HINT_DISPLAY_USABLE_BOUNDS + HINT_EMSCRIPTEN_ASYNCIFY + HINT_EMSCRIPTEN_KEYBOARD_ELEMENT + HINT_ENABLE_STEAM_CONTROLLERS + HINT_EVENT_LOGGING + HINT_FRAMEBUFFER_ACCELERATION + HINT_GAMECONTROLLERCONFIG + HINT_GAMECONTROLLERCONFIG_FILE + HINT_GAMECONTROLLERTYPE + HINT_GAMECONTROLLER_IGNORE_DEVICES + HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT + HINT_GAMECONTROLLER_USE_BUTTON_LABELS + HINT_GRAB_KEYBOARD + HINT_IDLE_TIMER_DISABLED + HINT_IME_INTERNAL_EDITING + HINT_IOS_HIDE_HOME_INDICATOR + HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS + HINT_JOYSTICK_HIDAPI + HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT + HINT_JOYSTICK_HIDAPI_GAMECUBE + HINT_JOYSTICK_HIDAPI_JOY_CONS + HINT_JOYSTICK_HIDAPI_PS4 + HINT_JOYSTICK_HIDAPI_PS4_RUMBLE + HINT_JOYSTICK_HIDAPI_PS5 + HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED + HINT_JOYSTICK_HIDAPI_PS5_RUMBLE + HINT_JOYSTICK_HIDAPI_STADIA + HINT_JOYSTICK_HIDAPI_STEAM + HINT_JOYSTICK_HIDAPI_SWITCH + HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED + HINT_JOYSTICK_HIDAPI_XBOX + HINT_JOYSTICK_RAWINPUT + HINT_JOYSTICK_THREAD + HINT_LINUX_JOYSTICK_DEADZONES + HINT_MAC_BACKGROUND_APP + HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK + HINT_MOUSE_DOUBLE_CLICK_RADIUS + HINT_MOUSE_DOUBLE_CLICK_TIME + HINT_MOUSE_FOCUS_CLICKTHROUGH + HINT_MOUSE_NORMAL_SPEED_SCALE + HINT_MOUSE_RELATIVE_MODE_WARP + HINT_MOUSE_RELATIVE_SCALING + HINT_MOUSE_RELATIVE_SPEED_SCALE + HINT_MOUSE_TOUCH_EVENTS HINT_NORMAL + HINT_NO_SIGNAL_HANDLERS + HINT_OPENGL_ES_DRIVER + HINT_ORIENTATIONS HINT_OVERRIDE + HINT_PREFERRED_LOCALES + HINT_QTWAYLAND_CONTENT_ORIENTATION + HINT_QTWAYLAND_WINDOW_FLAGS + HINT_RENDER_BATCHING + HINT_RENDER_DIRECT3D11_DEBUG + HINT_RENDER_DIRECT3D_THREADSAFE + HINT_RENDER_DRIVER + HINT_RENDER_LOGICAL_SIZE_MODE + HINT_RENDER_OPENGL_SHADERS + HINT_RENDER_SCALE_QUALITY + HINT_RENDER_VSYNC + HINT_RETURN_KEY_HIDES_IME + HINT_RPI_VIDEO_LAYER + HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL + HINT_THREAD_PRIORITY_POLICY + HINT_THREAD_STACK_SIZE + HINT_TIMER_RESOLUTION + HINT_TOUCH_MOUSE_EVENTS + HINT_TV_REMOTE_AS_JOYSTICK + HINT_VIDEO_ALLOW_SCREENSAVER + HINT_VIDEO_DOUBLE_BUFFER + HINT_VIDEO_EXTERNAL_CONTEXT + HINT_VIDEO_HIGHDPI_DISABLED + HINT_VIDEO_MAC_FULLSCREEN_SPACES + HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS + HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT + HINT_VIDEO_WIN_D3DCOMPILER + HINT_VIDEO_X11_FORCE_EGL + HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR + HINT_VIDEO_X11_NET_WM_PING + HINT_VIDEO_X11_WINDOW_VISUALID + HINT_VIDEO_X11_XINERAMA + HINT_VIDEO_X11_XRANDR + HINT_VIDEO_X11_XVIDMODE + HINT_WAVE_FACT_CHUNK + HINT_WAVE_RIFF_CHUNK_SIZE + HINT_WAVE_TRUNCATION + HINT_WINDOWS_DISABLE_THREAD_NAMING + HINT_WINDOWS_ENABLE_MESSAGELOOP + HINT_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS + HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL + HINT_WINDOWS_INTRESOURCE_ICON + HINT_WINDOWS_INTRESOURCE_ICON_SMALL + HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 + HINT_WINDOWS_USE_D3D9EX + HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN + HINT_WINRT_HANDLE_BACK_BUTTON + HINT_WINRT_PRIVACY_POLICY_LABEL + HINT_WINRT_PRIVACY_POLICY_URL + HINT_XINPUT_ENABLED + HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING HITTEST_DRAGGABLE HITTEST_NORMAL HITTEST_RESIZE_BOTTOM From b760120cd6e01d27c16e115089b2abc35a2d111b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 22:07:55 +0100 Subject: [PATCH 31/36] Add bindings for renderer functions Since some of these are not implemented until 2.0.10, this commit adds some logic to replace them with stubs that will die with an error message. --- cpanfile | 1 + lib/SDL2/Raw.pm | 250 +++++++++++++++++++++++++++++------------------- t/namespace.t | 42 ++++++++ 3 files changed, 195 insertions(+), 98 deletions(-) diff --git a/cpanfile b/cpanfile index 98133f6..48f4e8a 100644 --- a/cpanfile +++ b/cpanfile @@ -2,5 +2,6 @@ requires 'enum::hash'; requires 'FFI::Platypus'; requires 'FFI::C'; requires 'Ref::Util'; +requires 'version'; recommends 'File::Share'; diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index bd0f723..b3f9118 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -4,10 +4,13 @@ package SDL2; use strict; use warnings; +use Carp (); +use FFI::C; use FFI::CheckLib (); use FFI::Platypus 1.00; -use FFI::C; use Ref::Util; +use Sub::Util (); +use version (); my $ffi; BEGIN { @@ -1793,7 +1796,24 @@ package SDL2::Point { record_layout_1( int => 'x', int => 'y' ); { - # Give point a positional constructor + # Give package a positional constructor + no strict 'refs'; + no warnings 'redefine'; + + my $old = __PACKAGE__->can('new') or die; + my $new = sub { shift->$old({ x => shift, y => shift }) }; + my $name = __PACKAGE__ . '::new'; + + *{$name} = Sub::Util::set_subname $name => $new; + } +} + +package SDL2::FPoint { + use FFI::Platypus::Record; + record_layout_1( float => 'x', float => 'y' ); + + { + # Give package a positional constructor no strict 'refs'; no warnings 'redefine'; @@ -1801,7 +1821,6 @@ package SDL2::Point { my $new = sub { shift->$old({ x => shift, y => shift }) }; my $name = __PACKAGE__ . '::new'; - require Sub::Util; *{$name} = Sub::Util::set_subname $name => $new; } } @@ -1811,7 +1830,26 @@ package SDL2::Rect { record_layout_1( int => 'x', int => 'y', int => 'w', int => 'h' ); { - # Give rect a positional constructor + # Give package a positional constructor + no strict 'refs'; + no warnings 'redefine'; + + my $old = __PACKAGE__->can('new') or die; + my $new = sub { + shift->$old({ x => shift, y => shift, w => shift, h => shift }); + }; + my $name = __PACKAGE__ . '::new'; + + *{$name} = Sub::Util::set_subname $name => $new; + } +} + +package SDL2::FRect { + use FFI::Platypus::Record; + record_layout_1( float => 'x', float => 'y', float => 'w', float => 'h' ); + + { + # Give package a positional constructor no strict 'refs'; no warnings 'redefine'; @@ -1821,19 +1859,18 @@ package SDL2::Rect { }; my $name = __PACKAGE__ . '::new'; - require Sub::Util; *{$name} = Sub::Util::set_subname $name => $new; } } package SDL2::RendererInfo { FFI::C->struct( SDL_RendererInfo => [ - '_name' => 'opaque', - flags => 'uint32', - 'num_texture_formats' => 'uint32', - 'texture_formats' => 'uint32[16]', - 'max_texture_width' => 'int', - 'max_texture_height' => 'int', + _name => 'opaque', + flags => 'uint32', + num_texture_formats => 'uint32', + texture_formats => 'uint32[16]', + max_texture_width => 'int', + max_texture_height => 'int', ]); sub name { $ffi->cast( opaque => string => shift->_name ) } @@ -1864,11 +1901,24 @@ package SDL2::SysWMinfo { ]); } -$ffi->type( 'record(SDL2::Rect)' => 'SDL_Rect' ); -$ffi->type( 'record(SDL2::Point)' => 'SDL_Point' ); +$ffi->type( 'record(SDL2::Rect)' => 'SDL_Rect' ); +$ffi->type( 'record(SDL2::Point)' => 'SDL_Point' ); +$ffi->type( 'record(SDL2::FRect)' => 'SDL_FRect' ); +$ffi->type( 'record(SDL2::FPoint)' => 'SDL_FPoint' ); $ffi->type( '( SDL_Window, SDL_Point, opaque )->int' => 'SDL_HitTest' ); +## Version + +$ffi->attach( GetRevision => [ ] => 'string' ); +$ffi->attach( GetRevisionNumber => [ ] => 'int' ); # Deprecated +$ffi->attach( GetVersion => ['SDL_Version'] => 'void' ); + +GetVersion( my $version = SDL2::Version->new ); +$version = version->parse( + sprintf '%d.%d.%d', $version->major, $version->minor, $version->patch +); + ## Video $ffi->attach( CreateWindow => [qw( string sint32 sint32 sint32 sint32 sint32 )] => 'SDL_Window' ); @@ -1953,7 +2003,7 @@ $ffi->attach( ShowMessageBox => [qw( SDL_MessageBoxData int* $ffi->attach( ShowSimpleMessageBox => [qw( uint32 string string SDL_Window )] => 'int' ); $ffi->attach( ShowWindow => [qw( SDL_Window )] => 'void' ); $ffi->attach( UpdateWindowSurface => [qw( SDL_Window )] => 'int' ); -$ffi->attach( UpdateWindowSurfaceRects => [qw( SDL_Window int* int )] => 'int' ); # TODO: SDL_Rect* +$ffi->attach( UpdateWindowSurfaceRects => [qw( SDL_Window int[] int )] => 'int' ); # TODO: SDL_Rect[] $ffi->attach( VideoInit => [qw( string )] => 'int' ); $ffi->attach( VideoQuit => [ ] => 'void' ); @@ -1986,80 +2036,64 @@ $ffi->attach( SetError => ['string'] => 'void' => sub { shift->( sprintf shift ## Render -$ffi->attach( ComposeCustomBlendMode => [qw( int int int int int int )] => 'int' ); -$ffi->attach( CreateRenderer => [qw( SDL_Window sint32 sint32 )] => 'SDL_Renderer' ); -$ffi->attach( CreateSoftwareRenderer => [qw( SDL_Surface )] => 'SDL_Renderer' ); -$ffi->attach( CreateTexture => [qw( SDL_Renderer uint32 sint32 sint32 sint32 )] => 'SDL_Texture' ); -$ffi->attach( CreateTextureFromSurface => [qw( SDL_Renderer SDL_Surface )] => 'SDL_Texture' ); -$ffi->attach( CreateWindowAndRenderer => [qw( int int uint32 opaque* opaque* )] => 'int' ); -$ffi->attach( DestroyRenderer => [qw( SDL_Renderer )] => 'void' ); -$ffi->attach( DestroyTexture => [qw( SDL_Texture )] => 'void' ); -# SDL_GetNumRenderDrivers -# SDL_GetRenderDrawBlendMode -# SDL_GetRenderDrawColor -# SDL_GetRenderDriverInfo -# SDL_GetRenderer -$ffi->attach( GetRendererInfo => [qw( SDL_Renderer SDL_RendererInfo )] => 'int' ); -# SDL_GetRendererOutputSize -$ffi->attach( GetRenderTarget => [qw( SDL_Renderer )] => 'SDL_Texture' ); -$ffi->attach( GetTextureAlphaMod => [qw( SDL_Texture uint8* )] => 'int' ); -$ffi->attach( GetTextureBlendMode => [qw( SDL_Texture SDL_BlendMode )] => 'int' ); -$ffi->attach( GetTextureColorMod => [qw( SDL_Texture uint8* uint8* uint8* )] => 'int' ); -# SDL_GL_BindTexture -# SDL_GL_UnbindTexture -$ffi->attach( LockTexture => [qw( SDL_Texture SDL_Rect* opaque* int* )] => 'int' ); -# SDL_QueryTexture -$ffi->attach( RenderClear => [qw( SDL_Renderer )] => 'int' ); -$ffi->attach( RenderCopy => [qw( SDL_Renderer SDL_Texture SDL_Rect* SDL_Rect* )] => 'int' ); -$ffi->attach( RenderCopyEx => [qw( SDL_Renderer SDL_Texture SDL_Rect* SDL_Rect* double SDL_Rect* SDL_Rect* )] => 'int' ); -# SDL_RenderCopyExF -# SDL_RenderCopyF -# SDL_RenderDrawLine -# SDL_RenderDrawLineF -# SDL_RenderDrawLines -# SDL_RenderDrawLinesF -# SDL_RenderDrawPoint -# SDL_RenderDrawPointF -$ffi->attach( RenderDrawPoints => [qw( SDL_Renderer int[] int )] => 'int' ); -# SDL_RenderDrawPointsF -$ffi->attach( RenderDrawRect => [qw( SDL_Renderer SDL_Rect* )] => 'int' ); -# SDL_RenderDrawRectF -# SDL_RenderDrawRects -# SDL_RenderDrawRectsF -# SDL_Renderer -# SDL_RendererFlags -# SDL_RendererFlip -# SDL_RendererInfo -$ffi->attach( RenderFillRect => [qw( SDL_Renderer SDL_Rect* )] => 'int' ); -# SDL_RenderFillRectF -# SDL_RenderFillRects -# SDL_RenderFillRectsF -# SDL_RenderGetClipRect -# SDL_RenderGetIntegerScale -# SDL_RenderGetLogicalSize -# SDL_RenderGetScale -# SDL_RenderGetViewport -# SDL_RenderIsClipEnabled -$ffi->attach( RenderPresent => ['SDL_Renderer'] => 'void' ); -# SDL_RenderReadPixels -# SDL_RenderSetClipRect -# SDL_RenderSetIntegerScale -# SDL_RenderSetLogicalSize -# SDL_RenderSetScale -# SDL_RenderSetViewport -# SDL_RenderTargetSupported -# SDL_SetRenderDrawBlendMode -$ffi->attach( SetRenderDrawColor => [qw( SDL_Renderer uint8 uint8 uint8 uint8 )] => 'int' ); -$ffi->attach( SetRenderTarget => [qw( SDL_Renderer SDL_Texture )] => 'int' ); -$ffi->attach( SetTextureAlphaMod => [qw( SDL_Texture uint8 )] => 'int' ); -$ffi->attach( SetTextureBlendMode => [qw( SDL_Texture SDL_BlendMode )] => 'int' ); -$ffi->attach( SetTextureColorMod => [qw( SDL_Texture uint8 uint8 uint8 )] => 'int' ); -# SDL_Texture -# SDL_TextureAccess -# SDL_TextureModulate -$ffi->attach( UnlockTexture => ['SDL_Texture'] => 'void' ); -# SDL_UpdateTexture -# SDL_UpdateYUVTexture +$ffi->attach( ComposeCustomBlendMode => [qw( int int int int int int )] => 'int' ); +$ffi->attach( CreateRenderer => [qw( SDL_Window sint32 sint32 )] => 'SDL_Renderer' ); +$ffi->attach( CreateSoftwareRenderer => [qw( SDL_Surface )] => 'SDL_Renderer' ); +$ffi->attach( CreateTexture => [qw( SDL_Renderer uint32 sint32 sint32 sint32 )] => 'SDL_Texture' ); +$ffi->attach( CreateTextureFromSurface => [qw( SDL_Renderer SDL_Surface )] => 'SDL_Texture' ); +$ffi->attach( CreateWindowAndRenderer => [qw( int int uint32 opaque* opaque* )] => 'int' ); +$ffi->attach( DestroyRenderer => [qw( SDL_Renderer )] => 'void' ); +$ffi->attach( DestroyTexture => [qw( SDL_Texture )] => 'void' ); +$ffi->attach( GetNumRenderDrivers => [ ] => 'int' ); +$ffi->attach( GetRenderDrawBlendMode => [qw( SDL_Renderer int* )] => 'int' ); +$ffi->attach( GetRenderDrawColor => [qw( SDL_Renderer int* int* int* int* )] => 'int' ); +$ffi->attach( GetRenderer => [qw( SDL_Window )] => 'SDL_Renderer' ); +$ffi->attach( GetRenderDriverInfo => [qw( int SDL_RendererInfo )] => 'int' ); +$ffi->attach( GetRendererInfo => [qw( SDL_Renderer SDL_RendererInfo )] => 'int' ); +$ffi->attach( GetRendererOutputSize => [qw( SDL_Renderer int* int* )] => 'int' ); +$ffi->attach( GetRenderTarget => [qw( SDL_Renderer )] => 'SDL_Texture' ); +$ffi->attach( GetTextureAlphaMod => [qw( SDL_Texture uint8* )] => 'int' ); +$ffi->attach( GetTextureBlendMode => [qw( SDL_Texture SDL_BlendMode )] => 'int' ); +$ffi->attach( GetTextureColorMod => [qw( SDL_Texture uint8* uint8* uint8* )] => 'int' ); +$ffi->attach( GL_BindTexture => [qw( SDL_Texture float* float* )] => 'int' ); +$ffi->attach( GL_UnbindTexture => [qw( SDL_Texture )] => 'int' ); +$ffi->attach( LockTexture => [qw( SDL_Texture SDL_Rect* opaque* int* )] => 'int' ); +$ffi->attach( QueryTexture => [qw( SDL_Texture uint32* int* int* int* )] => 'int' ); +$ffi->attach( RenderClear => [qw( SDL_Renderer )] => 'int' ); +$ffi->attach( RenderCopy => [qw( SDL_Renderer SDL_Texture SDL_Rect* SDL_Rect* )] => 'int' ); +$ffi->attach( RenderCopyEx => [qw( SDL_Renderer SDL_Texture SDL_Rect* SDL_Rect* double SDL_Point* int )] => 'int' ); +$ffi->attach( RenderDrawLine => [qw( SDL_Renderer int int int int )] => 'int' ); +$ffi->attach( RenderDrawLines => [qw( SDL_Renderer int[] int )] => 'int' ); # TODO: SDL_Point[] +$ffi->attach( RenderDrawPoint => [qw( SDL_Renderer int int )] => 'int' ); +$ffi->attach( RenderDrawPoints => [qw( SDL_Renderer int[] int )] => 'int' ); +$ffi->attach( RenderDrawRect => [qw( SDL_Renderer SDL_Rect* )] => 'int' ); +$ffi->attach( RenderDrawRects => [qw( SDL_Renderer int[] )] => 'int' ); # TODO: SDL_Rect[] +$ffi->attach( RenderFillRect => [qw( SDL_Renderer SDL_Rect* )] => 'int' ); +$ffi->attach( RenderFillRects => [qw( SDL_Renderer int[] int )] => 'int' ); # TODO: SDL_Rect[] + +$ffi->attach( RenderGetClipRect => [qw( SDL_Renderer SDL_Rect* )] => 'int' ); +$ffi->attach( RenderGetIntegerScale => [qw( SDL_Renderer )] => 'SDL_bool' ); +$ffi->attach( RenderGetLogicalSize => [qw( SDL_Renderer int* int* )] => 'void' ); +$ffi->attach( RenderGetScale => [qw( SDL_Renderer float* float* )] => 'void' ); +$ffi->attach( RenderGetViewport => [qw( SDL_Renderer SDL_Rect* )] => 'void' ); +$ffi->attach( RenderIsClipEnabled => [qw( SDL_Renderer )] => 'SDL_bool' ); +$ffi->attach( RenderPresent => [qw( SDL_Renderer )] => 'void' ); +$ffi->attach( RenderReadPixels => [qw( SDL_Renderer SDL_Rect* uint32 opaque int )] => 'int' ); +$ffi->attach( RenderSetClipRect => [qw( SDL_Renderer SDL_Rect* )] => 'int' ); +$ffi->attach( RenderSetIntegerScale => [qw( SDL_Renderer SDL_bool )] => 'int' ); +$ffi->attach( RenderSetLogicalSize => [qw( SDL_Renderer int int )] => 'int' ); +$ffi->attach( RenderSetScale => [qw( SDL_Renderer float float )] => 'int' ); +$ffi->attach( RenderSetViewport => [qw( SDL_Renderer SDL_Rect* )] => 'int' ); +$ffi->attach( RenderTargetSupported => [qw( SDL_Renderer )] => 'SDL_bool' ); +$ffi->attach( SetRenderDrawBlendMode => [qw( SDL_Renderer SDL_BlendMode )] => 'int' ); +$ffi->attach( SetRenderDrawColor => [qw( SDL_Renderer uint8 uint8 uint8 uint8 )] => 'int' ); +$ffi->attach( SetRenderTarget => [qw( SDL_Renderer SDL_Texture )] => 'int' ); +$ffi->attach( SetTextureAlphaMod => [qw( SDL_Texture uint8 )] => 'int' ); +$ffi->attach( SetTextureBlendMode => [qw( SDL_Texture SDL_BlendMode )] => 'int' ); +$ffi->attach( SetTextureColorMod => [qw( SDL_Texture uint8 uint8 uint8 )] => 'int' ); +$ffi->attach( UnlockTexture => [qw( SDL_Texture )] => 'void' ); +$ffi->attach( UpdateTexture => [qw( SDL_Texture SDL_Rect* opaque int )] => 'int' ); +$ffi->attach( UpdateYUVTexture => [qw( SDL_Texture uint8[] int uint8[] int uint8[] int )] => 'int' ); ## Surface @@ -2181,16 +2215,6 @@ $ffi->attach( SetEventFilter => [qw( SDL_EventFilter opaque )] $ffi->attach( WaitEvent => [qw( SDL_Event )] => 'int' ); $ffi->attach( WaitEventTimeout => [qw( SDL_Event int )] => 'int' ); -# Not implemented in 2.0.8 -# $ffi->attach( GetEventState => [qw( uint32 )] => 'uint8' ); -# $ffi->attach( QuitRequested => [qw( )] => 'int' ); - -## Version - -$ffi->attach( GetRevision => [ ] => 'string' ); -$ffi->attach( GetRevisionNumber => [ ] => 'int' ); # Deprecated -$ffi->attach( GetVersion => ['SDL_Version'] => 'void' ); - ## Logging $ffi->attach( Log => [qw( string )] => 'void' => sub { $_[0]->( sprintf $_[1], @_[ 2 .. $#_ ] ) } ); @@ -2211,6 +2235,36 @@ $ffi->attach( LogResetPriorities => [qw( )] => 'void' ); sub LogGetOutputFunction { ... } sub LogSetOutputFunction { ... } +my %conditional = ( + '2.0.10' => { + RenderCopyExF => [ [qw( SDL_Renderer SDL_Texture SDL_Rect* SDL_FRect* double SDL_FPoint* int )] => 'int' ], + RenderCopyF => [ [qw( SDL_Renderer SDL_Texture SDL_Rect* SDL_Rect* SDL_FRect* )] => 'int' ], + RenderDrawLineF => [ [qw( SDL_Renderer float float float float )] => 'int' ], + RenderDrawLinesF => [ [qw( SDL_Renderer float[] int )] => 'int' ], # TODO: SDL_FPoint[] + RenderDrawPointF => [ [qw( SDL_Renderer float float )] => 'int' ], + RenderDrawPointsF => [ [qw( SDL_Renderer float[] int )] => 'int' ], # TODO: SDL_FPoint[] + RenderDrawRectF => [ [qw( SDL_Renderer SDL_FRect* )] => 'int' ], + RenderDrawRectsF => [ [qw( SDL_Renderer float[] )] => 'int' ], # TODO: SDL_FRect[] + RenderFillRectF => [ [qw( SDL_Renderer SDL_FRect* )] => 'int' ], + RenderFillRectsF => [ [qw( SDL_Renderer float[] int )] => 'int' ], # TODO: SDL_FRect[] + }, +); + +for my $want ( keys %conditional ) { + if ( $version >= version->parse($want) ) { + $ffi->attach( $_ => @{ $conditional{$want}{$_} } ) for keys %{ $conditional{$want} }; + } + else { + no strict 'refs'; + for ( keys %{ $conditional{$want} } ) { + my $name = __PACKAGE__ . '::' . $_; + *{$name} = Sub::Util::set_subname $name => sub { + Carp::croak "$_ requires SDL version 2.0.10+ but this is $version"; + }; + } + } +} + # Clean helper functions delete $SDL2::{$_} for qw( enum pixel_format ); diff --git a/t/namespace.t b/t/namespace.t index 6952fca..350aa81 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -180,6 +180,8 @@ is [ sort keys %SDL2:: ], [qw( FLIP_HORIZONTAL FLIP_NONE FLIP_VERTICAL + FPoint:: + FRect:: FilterEvents Finger:: FlushEvent @@ -195,6 +197,7 @@ is [ sort keys %SDL2:: ], [qw( GL_ALPHA_SIZE GL_BLUE_SIZE GL_BUFFER_SIZE + GL_BindTexture GL_CONTEXT_DEBUG_FLAG GL_CONTEXT_EGL GL_CONTEXT_FLAGS @@ -240,6 +243,7 @@ is [ sort keys %SDL2:: ], [qw( GL_SetAttribute GL_SetSwapInterval GL_SwapWindow + GL_UnbindTexture GL_UnloadLibrary GLattr GLcontextFlag @@ -285,14 +289,20 @@ is [ sort keys %SDL2:: ], [qw( GetEventFilter GetGrabbedWindow GetNumDisplayModes + GetNumRenderDrivers GetNumTouchDevices GetNumTouchFingers GetNumVideoDisplays GetNumVideoDrivers GetPerformanceCounter GetPerformanceFrequency + GetRenderDrawBlendMode + GetRenderDrawColor + GetRenderDriverInfo GetRenderTarget + GetRenderer GetRendererInfo + GetRendererOutputSize GetRevision GetRevisionNumber GetTextureAlphaMod @@ -964,6 +974,7 @@ is [ sort keys %SDL2:: ], [qw( PushEvent QUERY QUIT + QueryTexture Quit QuitEvent:: QuitSubSystem @@ -982,10 +993,38 @@ is [ sort keys %SDL2:: ], [qw( RenderClear RenderCopy RenderCopyEx + RenderCopyExF + RenderCopyF + RenderDrawLine + RenderDrawLineF + RenderDrawLines + RenderDrawLinesF + RenderDrawPoint + RenderDrawPointF RenderDrawPoints + RenderDrawPointsF RenderDrawRect + RenderDrawRectF + RenderDrawRects + RenderDrawRectsF RenderFillRect + RenderFillRectF + RenderFillRects + RenderFillRectsF + RenderGetClipRect + RenderGetIntegerScale + RenderGetLogicalSize + RenderGetScale + RenderGetViewport + RenderIsClipEnabled RenderPresent + RenderReadPixels + RenderSetClipRect + RenderSetIntegerScale + RenderSetLogicalSize + RenderSetScale + RenderSetViewport + RenderTargetSupported RendererFlags RendererFlip RendererInfo:: @@ -1273,6 +1312,7 @@ is [ sort keys %SDL2:: ], [qw( SetColorKey SetError SetEventFilter + SetRenderDrawBlendMode SetRenderDrawColor SetRenderTarget SetTextureAlphaMod @@ -1320,8 +1360,10 @@ is [ sort keys %SDL2:: ], [qw( TouchFingerEvent:: USEREVENT UnlockTexture + UpdateTexture UpdateWindowSurface UpdateWindowSurfaceRects + UpdateYUVTexture UpperBlit UserEvent:: Version:: From 71b06ffa89921ae53625d6c948461aacca738f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 22:34:43 +0100 Subject: [PATCH 32/36] Add bindings to surface functions --- lib/SDL2/Raw.pm | 84 +++++++++++++++++++++++++++++-------------------- t/namespace.t | 34 ++++++++++++++++++++ 2 files changed, 84 insertions(+), 34 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index b3f9118..b2e2372 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -34,6 +34,11 @@ use constant { ENABLE => 1, RELEASED => 0, PRESSED => 1, + SWSURFACE => 0, + PREALLOC => 1, + RLEACCEL => 2, + DONTFREE => 4, + SIMD_ALIGNED => 8, }; # Hints - https://github.com/libsdl-org/SDL/blob/main/include/SDL_hints.h @@ -1764,6 +1769,15 @@ package SDL2::MessageBoxData { sub message { $ffi->cast( opaque => string => sfhit->_message ) } } +package SDL2::Palette { + FFI::C->struct( SDL_Palette => [ + ncolors => 'int', + colors => 'opaque', # SDL_Color[]', + version => 'uint32', + refcount => 'int', + ]); +} + package SDL2::PixelFormat { FFI::C->struct( SDL_PixelFormat => [ format => 'uint32', @@ -2097,41 +2111,43 @@ $ffi->attach( UpdateYUVTexture => [qw( SDL_Texture uint8[] int uint8[] i ## Surface -$ffi->attach( UpperBlit => [qw( SDL_Surface SDL_Rect* SDL_Surface SDL_Rect* )] => 'int' ); -# SDL_BlitScaled -sub BlitSurface { goto &UpperBlit } -# SDL_ConvertPixels -# SDL_ConvertSurface -# SDL_ConvertSurfaceFormat -# SDL_CreateRGBSurface -$ffi->attach( CreateRGBSurfaceFrom => [qw( opaque int int int int uint32 uint32 uint32 uint32 )] => 'SDL_Surface' ); -# SDL_CreateRGBSurfaceWithFormat -# SDL_CreateRGBSurfaceWithFormatFrom -# SDL_FillRect -# SDL_FillRects -$ffi->attach( FreeSurface => ['SDL_Surface'] => 'void' ); -# SDL_GetClipRect -# SDL_GetColorKey -# SDL_GetSurfaceAlphaMod -# SDL_GetSurfaceBlendMode -# SDL_GetSurfaceColorMod -$ffi->attach( RWFromFile => [qw( string string )] => 'SDL_RWops' ); -$ffi->attach( LoadBMP_RW => [qw( SDL_RWops int )] => 'SDL_Surface' ); +$ffi->attach( ConvertPixels => [qw( int int uint32 opaque uint32 opaque int )] => 'int' ); +$ffi->attach( ConvertSurface => [qw( SDL_Surface SDL_PixelFormat uint32 )] => 'SDL_Surface' ); +$ffi->attach( ConvertSurfaceFormat => [qw( SDL_Surface SDL_PixelFormat uint32 )] => 'SDL_Surface' ); +$ffi->attach( CreateRGBSurface => [qw( uint32 int int int uint32 uint32 uint32 uint32 )] => 'SDL_Surface' ); +$ffi->attach( CreateRGBSurfaceFrom => [qw( opaque int int int int uint32 uint32 uint32 uint32 )] => 'SDL_Surface' ); +$ffi->attach( CreateRGBSurfaceWithFormat => [qw( uint32 int int int uint32 )] => 'SDL_Surface' ); +$ffi->attach( CreateRGBSurfaceWithFormatFrom => [qw( opaque uint32 int int int uint32 )] => 'SDL_Surface' ); +$ffi->attach( FillRect => [qw( SDL_Surface SDL_Rect* uint32 )] => 'int' ); +$ffi->attach( FillRects => [qw( SDL_Surface int[] int uint32 )] => 'int' ); # TODO: SDL_Rect[] +$ffi->attach( FreeSurface => [qw( SDL_Surface )] => 'void' ); +$ffi->attach( GetClipRect => [qw( SDL_Surface SDL_Rect* )] => 'void' ); +$ffi->attach( GetColorKey => [qw( SDL_Surface uint32* )] => 'int' ); +$ffi->attach( GetSurfaceAlphaMod => [qw( SDL_Surface uint8* )] => 'int' ); +$ffi->attach( GetSurfaceBlendMode => [qw( SDL_Surface SDL_BlendMode* )] => 'int' ); +$ffi->attach( GetSurfaceColorMod => [qw( SDL_Surface uint8* uint8* uint8* )] => 'int' ); +$ffi->attach( LoadBMP_RW => [qw( SDL_RWops int )] => 'SDL_Surface' ); +$ffi->attach( LockSurface => [qw( SDL_Surface )] => 'int' ); +$ffi->attach( LowerBlit => [qw( SDL_Surface SDL_Rect* SDL_Surface SDL_Rect* )] => 'int' ); +$ffi->attach( LowerBlitScaled => [qw( SDL_Surface SDL_Rect* SDL_Surface SDL_Rect* )] => 'int' ); +$ffi->attach( RWFromFile => [qw( string string )] => 'SDL_RWops' ); +$ffi->attach( SaveBMP_RW => [qw( SDL_Surface SDL_RWops int )] => 'int' ); +$ffi->attach( SetClipRect => [qw( SDL_Surface SDL_Rect* )] => 'SDL_bool' ); +$ffi->attach( SetColorKey => [qw( SDL_Surface int uint32 )] => 'int' ); +$ffi->attach( SetSurfaceAlphaMod => [qw( SDL_Surface uint8 )] => 'int' ); +$ffi->attach( SetSurfaceBlendMode => [qw( SDL_Surface SDL_BlendMode )] => 'int' ); +$ffi->attach( SetSurfaceColorMod => [qw( SDL_Surface uint8 uint8 uint8 )] => 'int' ); +$ffi->attach( SetSurfacePalette => [qw( SDL_Surface SDL_Palette )] => 'int' ); +$ffi->attach( SetSurfaceRLE => [qw( SDL_Surface int )] => 'int' ); +$ffi->attach( UnlockSurface => [qw( SDL_Surface )] => 'void' ); +$ffi->attach( UpperBlit => [qw( SDL_Surface SDL_Rect* SDL_Surface SDL_Rect* )] => 'int' ); +$ffi->attach( UpperBlitScaled => [qw( SDL_Surface SDL_Rect* SDL_Surface SDL_Rect* )] => 'int' ); + +sub SaveBMP { SaveBMP_RW( $_[0], RWFromFile( $_[1], 'wb' ), 1 ) } sub LoadBMP { LoadBMP_RW( RWFromFile( +shift, 'rb' ), 1 ) } -# SDL_LockSurface -# SDL_LowerBlit -# SDL_LowerBlitScaled -# SDL_MUSTLOCK -# SDL_SaveBMP -# SDL_SaveBMP_RW -# SDL_SetClipRect -$ffi->attach( SetColorKey => [qw( SDL_Surface int uint32 )] => 'int' ); -# SDL_SetSurfaceAlphaMod -# SDL_SetSurfaceBlendMode -# SDL_SetSurfaceColorMod -# SDL_SetSurfacePalette -# SDL_SetSurfaceRLE -# SDL_UnlockSurface +sub BlitSurface { goto &UpperBlit } +sub BlitScaled { goto &UpperBlitScaled } +sub MUSTLOCK { ( shift->flags & RLEACCEL ) != 0 } ## GameController diff --git a/t/namespace.t b/t/namespace.t index 350aa81..b038f60 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -77,6 +77,7 @@ is [ sort keys %SDL2:: ], [qw( BlendFactor BlendMode BlendOperation + BlitScaled BlitSurface CLIPBOARDUPDATE CONTROLLERAXISMOTION @@ -134,7 +135,13 @@ is [ sort keys %SDL2:: ], [qw( ControllerDeviceEvent:: ControllerSensorEvent:: ControllerTouchpadEvent:: + ConvertPixels + ConvertSurface + ConvertSurfaceFormat + CreateRGBSurface CreateRGBSurfaceFrom + CreateRGBSurfaceWithFormat + CreateRGBSurfaceWithFormatFrom CreateRenderer CreateSoftwareRenderer CreateTexture @@ -150,6 +157,7 @@ is [ sort keys %SDL2:: ], [qw( DISPLAYEVENT_ORIENTATION DOLLARGESTURE DOLLARRECORD + DONTFREE DROPBEGIN DROPCOMPLETE DROPFILE @@ -182,6 +190,8 @@ is [ sort keys %SDL2:: ], [qw( FLIP_VERTICAL FPoint:: FRect:: + FillRect + FillRects FilterEvents Finger:: FlushEvent @@ -276,7 +286,9 @@ is [ sort keys %SDL2:: ], [qw( GameControllerOpen GameControllerType GameControllerUpdate + GetClipRect GetClosestDisplayMode + GetColorKey GetCurrentDisplayMode GetCurrentVideoDriver GetDesktopDisplayMode @@ -305,6 +317,9 @@ is [ sort keys %SDL2:: ], [qw( GetRendererOutputSize GetRevision GetRevisionNumber + GetSurfaceAlphaMod + GetSurfaceBlendMode + GetSurfaceColorMod GetTextureAlphaMod GetTextureBlendMode GetTextureColorMod @@ -811,6 +826,7 @@ is [ sort keys %SDL2:: ], [qw( LoadBMP LoadBMP_RW LoadDollarTemplates + LockSurface LockTexture Log LogCategory @@ -829,6 +845,8 @@ is [ sort keys %SDL2:: ], [qw( LogSetPriority LogVerbose LogWarn + LowerBlit + LowerBlitScaled MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT @@ -850,6 +868,7 @@ is [ sort keys %SDL2:: ], [qw( MOUSEWHEEL_FLIPPED MOUSEWHEEL_NORMAL MULTIGESTURE + MUSTLOCK MaximizeWindow MessageBoxButtonData:: MessageBoxButtonFlags @@ -960,9 +979,11 @@ is [ sort keys %SDL2:: ], [qw( POWERSTATE_NO_BATTERY POWERSTATE_ON_BATTERY POWERSTATE_UNKNOWN + PREALLOC PRESSED PackedLayout PackedOrder + Palette:: PeepEvents PixelFormat:: PixelFormatEnum @@ -985,6 +1006,7 @@ is [ sort keys %SDL2:: ], [qw( RENDERER_TARGETTEXTURE RENDER_DEVICE_RESET RENDER_TARGETS_RESET + RLEACCEL RWFromFile RaiseWindow RecordGesture @@ -1276,6 +1298,8 @@ is [ sort keys %SDL2:: ], [qw( SCANCODE_Y SCANCODE_Z SENSORUPDATE + SIMD_ALIGNED + SWSURFACE SYSTEM_CURSOR_ARROW SYSTEM_CURSOR_CROSSHAIR SYSTEM_CURSOR_HAND @@ -1305,16 +1329,24 @@ is [ sort keys %SDL2:: ], [qw( SYSWM_WINRT SYSWM_X11 SaveAllDollarTemplates + SaveBMP + SaveBMP_RW SaveDollarTemplate ScaleMode ScanCode SensorEvent:: + SetClipRect SetColorKey SetError SetEventFilter SetRenderDrawBlendMode SetRenderDrawColor SetRenderTarget + SetSurfaceAlphaMod + SetSurfaceBlendMode + SetSurfaceColorMod + SetSurfacePalette + SetSurfaceRLE SetTextureAlphaMod SetTextureBlendMode SetTextureColorMod @@ -1359,12 +1391,14 @@ is [ sort keys %SDL2:: ], [qw( TextureModulate TouchFingerEvent:: USEREVENT + UnlockSurface UnlockTexture UpdateTexture UpdateWindowSurface UpdateWindowSurfaceRects UpdateYUVTexture UpperBlit + UpperBlitScaled UserEvent:: Version:: VideoInit From 2f2fca252b22efceb32e3aaa94e902b0f148030b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 22:42:09 +0100 Subject: [PATCH 33/36] Use SDL_Palette in SDL_PixelFormat struct --- lib/SDL2/Raw.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index b2e2372..36163d9 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1781,7 +1781,7 @@ package SDL2::Palette { package SDL2::PixelFormat { FFI::C->struct( SDL_PixelFormat => [ format => 'uint32', - palette => 'opaque', + palette => 'SDL_Palette', BitsPerPixel => 'uint8', BytesPerPixel => 'uint8', padding1 => 'uint8', From e293863ac2770a09f0d28dc6db8071cb67e1bea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 22:42:37 +0100 Subject: [PATCH 34/36] Fix ->next accessor in SDL_PixelFormat --- lib/SDL2/Raw.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 36163d9..837cdad 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1802,7 +1802,7 @@ package SDL2::PixelFormat { _next => 'opaque', ]); - sub next { $ffi->cast( opaque => 'SDL_PixelFormat', shift ) } + sub next { $ffi->cast( opaque => SDL_PixelFormat => shift->_next ) } } package SDL2::Point { From 5fd39b77c7c9735ce54766006fed69de33b6d8b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Mon, 28 Jun 2021 22:42:58 +0100 Subject: [PATCH 35/36] Simplify definition of positional constructors --- lib/SDL2/Raw.pm | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 837cdad..044f99c 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1815,10 +1815,8 @@ package SDL2::Point { no warnings 'redefine'; my $old = __PACKAGE__->can('new') or die; - my $new = sub { shift->$old({ x => shift, y => shift }) }; my $name = __PACKAGE__ . '::new'; - - *{$name} = Sub::Util::set_subname $name => $new; + *{$name} = Sub::Util::set_subname $name => sub { shift->$old({ x => shift, y => shift }) }; } } @@ -1832,10 +1830,8 @@ package SDL2::FPoint { no warnings 'redefine'; my $old = __PACKAGE__->can('new') or die; - my $new = sub { shift->$old({ x => shift, y => shift }) }; my $name = __PACKAGE__ . '::new'; - - *{$name} = Sub::Util::set_subname $name => $new; + *{$name} = Sub::Util::set_subname $name => sub { shift->$old({ x => shift, y => shift }) }; } } @@ -1849,12 +1845,10 @@ package SDL2::Rect { no warnings 'redefine'; my $old = __PACKAGE__->can('new') or die; - my $new = sub { + my $name = __PACKAGE__ . '::new'; + *{$name} = Sub::Util::set_subname $name => sub { shift->$old({ x => shift, y => shift, w => shift, h => shift }); }; - my $name = __PACKAGE__ . '::new'; - - *{$name} = Sub::Util::set_subname $name => $new; } } @@ -1868,12 +1862,10 @@ package SDL2::FRect { no warnings 'redefine'; my $old = __PACKAGE__->can('new') or die; - my $new = sub { + my $name = __PACKAGE__ . '::new'; + *{$name} = Sub::Util::set_subname $name => sub { shift->$old({ x => shift, y => shift, w => shift, h => shift }); }; - my $name = __PACKAGE__ . '::new'; - - *{$name} = Sub::Util::set_subname $name => $new; } } From 966ae1bb1d754ac9e4d8e74ab8768b1a1fdbfe7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Joaqu=C3=ADn=20Atria?= Date: Tue, 29 Jun 2021 00:03:27 +0100 Subject: [PATCH 36/36] Add initial surface test --- lib/SDL2/Raw.pm | 6 +++- t/namespace.t | 1 + t/surface.t | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 t/surface.t diff --git a/lib/SDL2/Raw.pm b/lib/SDL2/Raw.pm index 044f99c..fb56a5b 100644 --- a/lib/SDL2/Raw.pm +++ b/lib/SDL2/Raw.pm @@ -1885,7 +1885,7 @@ package SDL2::RendererInfo { package SDL2::Surface { FFI::C->struct( SDL_Surface => [ flags => 'uint32', - format => 'opaque', + format => 'SDL_PixelFormat', w => 'int', h => 'int', pitch => 'int', @@ -2101,6 +2101,10 @@ $ffi->attach( UnlockTexture => [qw( SDL_Texture $ffi->attach( UpdateTexture => [qw( SDL_Texture SDL_Rect* opaque int )] => 'int' ); $ffi->attach( UpdateYUVTexture => [qw( SDL_Texture uint8[] int uint8[] int uint8[] int )] => 'int' ); +## Pixels + +$ffi->attach( GetPixelFormatName => [qw( uint32 )] => 'string' ); + ## Surface $ffi->attach( ConvertPixels => [qw( int int uint32 opaque uint32 opaque int )] => 'int' ); diff --git a/t/namespace.t b/t/namespace.t index b038f60..6cb3fb6 100644 --- a/t/namespace.t +++ b/t/namespace.t @@ -308,6 +308,7 @@ is [ sort keys %SDL2:: ], [qw( GetNumVideoDrivers GetPerformanceCounter GetPerformanceFrequency + GetPixelFormatName GetRenderDrawBlendMode GetRenderDrawColor GetRenderDriverInfo diff --git a/t/surface.t b/t/surface.t new file mode 100644 index 0000000..401e306 --- /dev/null +++ b/t/surface.t @@ -0,0 +1,85 @@ +#!/usr/bin/env perl + +use Test2::V0; +use SDL2::Raw; +use FFI::Platypus::Buffer 'scalar_to_buffer'; + +# Allocate a set of raw pixel data for the icon surface +my ($pixels) = scalar_to_buffer( pack 'S*', + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0aab, 0x0789, 0x0bcc, 0x0eee, 0x09aa, 0x099a, 0x0ddd, + 0x0fff, 0x0eee, 0x0899, 0x0fff, 0x0fff, 0x1fff, 0x0dde, 0x0dee, + 0x0fff, 0xabbc, 0xf779, 0x8cdd, 0x3fff, 0x9bbc, 0xaaab, 0x6fff, + 0x0fff, 0x3fff, 0xbaab, 0x0fff, 0x0fff, 0x6689, 0x6fff, 0x0dee, + 0xe678, 0xf134, 0x8abb, 0xf235, 0xf678, 0xf013, 0xf568, 0xf001, + 0xd889, 0x7abc, 0xf001, 0x0fff, 0x0fff, 0x0bcc, 0x9124, 0x5fff, + 0xf124, 0xf356, 0x3eee, 0x0fff, 0x7bbc, 0xf124, 0x0789, 0x2fff, + 0xf002, 0xd789, 0xf024, 0x0fff, 0x0fff, 0x0002, 0x0134, 0xd79a, + 0x1fff, 0xf023, 0xf000, 0xf124, 0xc99a, 0xf024, 0x0567, 0x0fff, + 0xf002, 0xe678, 0xf013, 0x0fff, 0x0ddd, 0x0fff, 0x0fff, 0xb689, + 0x8abb, 0x0fff, 0x0fff, 0xf001, 0xf235, 0xf013, 0x0fff, 0xd789, + 0xf002, 0x9899, 0xf001, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0xe789, + 0xf023, 0xf000, 0xf001, 0xe456, 0x8bcc, 0xf013, 0xf002, 0xf012, + 0x1767, 0x5aaa, 0xf013, 0xf001, 0xf000, 0x0fff, 0x7fff, 0xf124, + 0x0fff, 0x089a, 0x0578, 0x0fff, 0x089a, 0x0013, 0x0245, 0x0eff, + 0x0223, 0x0dde, 0x0135, 0x0789, 0x0ddd, 0xbbbc, 0xf346, 0x0467, + 0x0fff, 0x4eee, 0x3ddd, 0x0edd, 0x0dee, 0x0fff, 0x0fff, 0x0dee, + 0x0def, 0x08ab, 0x0fff, 0x7fff, 0xfabc, 0xf356, 0x0457, 0x0467, + 0x0fff, 0x0bcd, 0x4bde, 0x9bcc, 0x8dee, 0x8eff, 0x8fff, 0x9fff, + 0xadee, 0xeccd, 0xf689, 0xc357, 0x2356, 0x0356, 0x0467, 0x0467, + 0x0fff, 0x0ccd, 0x0bdd, 0x0cdd, 0x0aaa, 0x2234, 0x4135, 0x4346, + 0x5356, 0x2246, 0x0346, 0x0356, 0x0467, 0x0356, 0x0467, 0x0467, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, +); + +subtest CreateRGBSurfaceFrom => sub { + my $surface = SDL2::CreateRGBSurfaceFrom( + $pixels, + 16, 16, 16, 16 * 2, + 0x0f00, 0x00f0, 0x000f, 0xf000 + ) or diag 'Could not create surface: ' . SDL2::GetError; + + is $surface, object { + prop isa => 'SDL2::Surface'; + call format => object { + prop isa => 'SDL2::PixelFormat'; + }; + }; + + is SDL2::GetPixelFormatName( $surface->format->format ), + 'SDL_PIXELFORMAT_UNKNOWN', 'Todo: Unknown?'; + + SDL2::FreeSurface($surface); +}; + +subtest CreateRGBSurfaceWithFormatFrom => sub { + my $surface = SDL2::CreateRGBSurfaceWithFormatFrom( + $pixels, + 16, 16, 16, 16 * 2, + SDL2::PIXELFORMAT_ARGB4444, + ) or diag 'Could not create surface: ' . SDL2::GetError; + + is $surface, object { + prop isa => 'SDL2::Surface'; + call format => object { + prop isa => 'SDL2::PixelFormat'; + # call format => SDL2::PIXELFORMAT_ARGB4444; + }; + }; + + is SDL2::GetPixelFormatName( $surface->format->format ), + 'SDL_PIXELFORMAT_UNKNOWN', 'TODO: Unknown?'; + + SDL2::FreeSurface($surface); +}; + + +done_testing;