our
@EXPORT
=
qw[attach define deprecate has enum ffi is threads_wrapped load_lib]
;
use
Ref::Util
qw( is_ref is_plain_arrayref is_plain_hashref )
;
sub
deprecate (
$str
) {
warnings::
warn
(
'deprecated'
,
$str
)
if
warnings::enabled(
'deprecated'
);
}
sub
threads_wrapped () {
1;
}
my
$loaded_libs
;
sub
loaded_libs (
$name
) {
defined
$loaded_libs
->{
$name
} ? 1 : 0;
}
my
%libs
;
my
%prereq
= (
SDL2_image
=> [
qw[SDL2 jpeg png16 tiff webp zlib1]
],
SDL2_ttf
=> [
qw[SDL2 freetype]
],
api_wrapper
=> [
qw[SDL2 SDL2_mixer]
],
SDL2_mixer
=> [
qw[]
],
freetype
=> [
qw[zlib1]
]
);
sub
load_lib (
$name
) {
$libs
{all}{
$name
} //
return
;
load_lib(
$_
)
for
@{
$prereq
{
$name
} // [] };
my
$_cdd
=
"\0"
x 1024;
CORE::state
$SetDllDirectoryA
;
CORE::state
$GetDllDirectoryA
;
if
( $^O eq
'MSWin32'
) {
if
( !
defined
$SetDllDirectoryA
) {
ffi()->lib(
'Kernel32.dll'
,
undef
);
$SetDllDirectoryA
= ffi()->function(
SetDllDirectoryA
=> [
'string'
] =>
'bool'
);
}
if
( !
defined
$GetDllDirectoryA
) {
ffi()->lib(
'Kernel32.dll'
,
undef
);
$GetDllDirectoryA
= ffi()->function(
GetDllDirectoryA
=> [
'int'
,
'string'
] =>
'int'
);
}
$_cdd
=
undef
if
!
$GetDllDirectoryA
->call(
length
$_cdd
,
$_cdd
);
$SetDllDirectoryA
->call(
Path::Tiny->new(
$libs
{all}{
$name
} )->parent->absolute->stringify );
}
ffi()->lib(
$libs
{all}{
$name
} );
$SetDllDirectoryA
->call(
$_cdd
)
if
$^O eq
'MSWin32'
&&
defined
$_cdd
;
$loaded_libs
= {
map
{
path(
$_
)->basename(
qw[.so .dylib .dll]
) =~ m[^(?:lib)?(.+)(\-.+)?$];
$
1
=>
$_
}
grep
{
defined
} ffi()->lib()
};
}
sub
ffi () {
CORE::state
$ffi
;
if
( !
defined
$ffi
) {
my
$distdir
= Path::Tiny->new( dist_dir(
'SDL2-FFI'
) );
my
$root
= path(__FILE__)->absolute->parent(3)->realpath;
my
@libs
= (
$distdir
->child(
'lib'
)->children(
qr[\.(so|dylib|dll)$]
),
$distdir
->child(
'bin'
)->children(
qr[\.(so|dylib|dll)$]
)
);
my
%loaded_libs
=
map
{
path(
$_
)->basename(
qw[.so .dylib .dll]
) =~ m[^((?:lib)?(.+?)(\-\d+)?)$];
$
2
=>
$_
}
@libs
;
%libs
= (
sdl
=> {
map
{
path(
$_
)->basename(
qw[.so .dylib .dll]
) =~ m[^((?:lib)?(.+?)(\-\d+)?)$];
$
2
=>
$_
}
map
{ /SDL/ ?
$loaded_libs
{
$_
} : () }
keys
%loaded_libs
},
api_wrapper
=>
[
sort
map
{ /api_wrapper/ ?
$loaded_libs
{
$_
} : () }
keys
%loaded_libs
],
pre
=> [
sort
map
{ /^(?:lib)?(?!.*(SDL|thread).+).+$/ ?
$loaded_libs
{
$_
} : () }
keys
%loaded_libs
],
all
=> \
%loaded_libs
);
if
(
defined
(
&Test2::V0::diag
) ) {
my
$lines
=
$distdir
->child(
'config.ini'
);
my
(
$cflags
,
$lflags
)
=
$lines
->is_file ?
$lines
->lines_raw( {
chomp
=> 1 } ) :
(
''
,
''
);
$cflags
=
'-I'
.
$distdir
->child(
'include'
,
'SDL2'
)->relative .
' '
.
$cflags
;
$lflags
=
'-L'
.
$distdir
->child(
'lib'
)->absolute .
' '
.
$lflags
;
my
$Win32
= $^O eq
'MSWin32'
;
eval
{ Test2::V0::diag(
'dist_dir: '
.
$distdir
) };
warn
$@
if
$@;
eval
{ Test2::V0::diag(
'libs: '
.
$lflags
) };
warn
$@
if
$@;
eval
{ Test2::V0::diag(
'cflags: '
.
$cflags
) };
warn
$@
if
$@;
eval
{ Test2::V0::diag(
'libs: '
.
join
'; '
,
@libs
); };
}
{
$ffi
= FFI::Platypus->new(
api
=> 2,
experimental
=> 2,
lib
=> [
]
);
FFI::C->ffi(
$ffi
);
}
}
$ffi
;
}
sub
enumX {
(
undef
) =
shift
;
my
$name
=
defined
$_
[0] && !is_ref
$_
[0] ?
shift
:
undef
;
my
@values
=
defined
$_
[0] && is_plain_arrayref
$_
[0] ? @{
shift
() } : ();
my
%config
=
defined
$_
[0] && is_plain_hashref
$_
[0] ? %{
shift
() } : ();
my
(
$class
,
$filename
) =
caller
;
unless
(
defined
$name
) {
$name
=
lcfirst
[
split
/::/,
$class
]->[-1];
$name
=~ s/([A-Z]+)/
'_'
.
lc
($1)/ge;
$name
.=
"_t"
;
}
my
$ffi
= FFI::C::_ffi_get(
$filename
),
$config
{
package
} ||=
$class
;
my
@maps
;
$config
{maps} = \
@maps
;
my
$rev
=
$config
{rev} ||=
'str'
;
ffi->load_custom_type(
'SDL2::Utils::Type::Enum'
,
$name
, \
%config
,
@values
);
my
(
$str_lookup
,
$int_lookup
,
$type
) =
@maps
;
ffi->def(
'FFI::C::EnumDef'
,
$name
,
FFI::C::EnumDef->new(
str_lookup
=>
$str_lookup
,
int_lookup
=>
$int_lookup
,
type
=>
$type
,
rev
=>
$rev
,
)
);
}
sub
enum (
%args
) {
my
(
$package
) =
caller
();
$package
=
'SDL2::FFI'
unless
$package
eq
'SDL2::Image'
||
$package
eq
'SDL2::TTF'
||
$package
eq
'SDL2::Mixer'
||
$package
eq
'SDL2::GFX'
;
for
my
$tag
(
keys
%args
) {
enumX(
ffi,
$tag
=>
$args
{
$tag
},
{
rev
=>
'dualvar'
,
package
=>
$package
,
type
=>
'int'
,
casing
=>
'keep'
}
);
my
$_tag
=
$tag
;
$_tag
=~ s[^SDL_][];
$_tag
=
lcfirst
$_tag
unless
$_tag
=~ m[^.[A-Z]];
no
strict
'refs'
;
push
@{ ${
"${package}::EXPORT_TAGS"
}{
$_tag
} },
sort
map
{
ref
$_
?
ref
$_
eq
'CODE'
?
$_
->() :
$_
->[0] :
$_
} @{
$args
{
$tag
} };
}
}
sub
attach (
%args
) {
my
(
$package
) =
caller
();
$package
=
'SDL2::FFI'
unless
$package
eq
'SDL2::Image'
||
$package
eq
'SDL2::TTF'
||
$package
eq
'SDL2::Mixer'
||
$package
eq
'SDL2::GFX'
;
for
my
$tag
(
sort
keys
%args
) {
for
my
$func
(
sort
keys
%{
$args
{
$tag
} } ) {
my
$perl
=
$func
;
$perl
=~ s[^Bundle_][];
ffi->attach( [
$func
=>
$package
.
'::'
.
$perl
] => @{
$args
{
$tag
}{
$func
} } );
no
strict
'refs'
;
push
@{ ${
"${package}::EXPORT_TAGS"
}{
$tag
} },
$perl
}
}
}
my
%is
;
sub
is (
$is
) {
my
(
$package
) =
caller
;
$is
{
$package
} =
$is
;
}
sub
get_is (
$package
) {
$is
{
$package
} //
''
}
sub
has
(
%args
) {
my
(
$package
) =
caller
;
my
$type
=
$package
;
$type
=~ s[^SDL2::][SDL_];
$type
=~ s[::][_]g;
my
@args
= (
ffi,
name
=>
$type
,
class
=>
$package
,
nullable
=> 1,
trim_string
=> 1,
members
=> \
@_
);
get_is(
$package
) eq
'Union'
? FFI::C::UnionDef->new(
@args
) : FFI::C::StructDef->new(
@args
);
}
sub
define (
%args
) {
my
(
$package
) =
caller
();
$package
=
'SDL2::FFI'
unless
$package
eq
'SDL2::Image'
||
$package
eq
'SDL2::TTF'
||
$package
eq
'SDL2::Mixer'
||
$package
eq
'SDL2::GFX'
;
for
my
$tag
(
keys
%args
) {
ref
$_
->[1] eq
'CODE'
?
sub
{
no
strict
'refs'
; *{
$package
.
'::'
.
$_
->[0] } =
$_
->[1] }
->() :
constant->
import
(
$package
.
'::'
.
$_
->[0] =>
$_
->[1] )
for
@{
$args
{
$tag
} };
no
strict
'refs'
;
push
@{ ${
"${package}::EXPORT_TAGS"
}{
$tag
} },
sort
map
{
ref
$_
?
$_
->[0] :
$_
} @{
$args
{
$tag
} };
}
}
sub
_tokenize_in (
$str
,
$pointers
= 1 ) {
my
@ret
;
$str
=~ s{
\%
\d*?
(?<type> (?:[\+-]?[di]|0|x|X|u|f|F|e|E|a|A|g|G|n|p|s|c|
lc
|\d\[.+?\]))
}{
push
@ret
, _conversion($+{type},
$pointers
)}gexsm;
@ret
;
}
sub
_tokenize_out (
$str
,
$pointers
= 1 ) {
my
@ret
;
$str
=~ s[
%[\^\-\+\d\.
][
push
@ret
, _conversion($+{type},
$pointers
, $+{len} // ())]gexsm;
@ret
;
}
sub
_conversion (
$conv
,
$pointers
= 1,
$len
=
''
) {
my
$count
= () =
$len
=~ m[(\*)]msg;
my
$retval
=
$conv
eq
'c'
?
'char'
:
$conv
eq
's'
?
'string'
:
$conv
eq
'['
?
'string'
:
$conv
eq
'd'
? (
$pointers
?
'int*'
:
'int'
) :
$conv
eq
'i'
? (
$pointers
?
'int*'
:
'int'
) :
$conv
eq
'u'
? (
$pointers
?
'uint*'
:
'uint'
) :
$conv
eq
'o'
? (
$pointers
?
'int*'
:
'int'
) :
$conv
eq
'x'
? (
$pointers
?
'string'
:
'int'
) :
$conv
eq
'X'
? (
$pointers
?
'string'
:
'int'
) :
$conv
eq
'n'
? (
$pointers
?
'int*'
:
'int'
) :
$conv
eq
'a'
? (
$pointers
?
'float*'
:
'float'
) :
$conv
eq
'A'
? (
$pointers
?
'float*'
:
'float'
) :
$conv
eq
'e'
? (
$pointers
?
'float*'
:
'float'
) :
$conv
eq
'E'
? (
$pointers
?
'float*'
:
'float'
) :
$conv
eq
'f'
? (
$pointers
?
'float*'
:
'float'
) :
$conv
eq
'F'
? (
$pointers
?
'float*'
:
'float'
) :
$conv
eq
'g'
? (
$pointers
?
'float*'
:
'float'
) :
$conv
eq
'G'
? (
$pointers
?
'float*'
:
'float'
) :
$conv
eq
'p'
?
'opaque'
:
$conv
eq
'lc'
?
'wide_string'
:
$conv
=~ /^\d/ ?
'string'
:
'broken: '
.
$conv
;
$len
? ( (
map
{
'int'
} 1 ..
$count
),
$retval
) :
$retval
;
}
ffi();
};
1;