Thread local storage API

FYI, I just added a thread local storage API to the SDL RC build, in
SDL_thread.h:
SDL_TLSCreate(), SDL_TLSSet(), SDL_TLSGet()

Please let me know if you run into any problems with it!

Cheers,
–Sam

FYI, I just added a thread local storage API to the SDL RC build, in SDL_thread.h:
SDL_TLSCreate(), SDL_TLSSet(), SDL_TLSGet()

Please let me know if you run into any problems with it!

Do you mean apart from conceptually? :slight_smile:

I’m curious what possible use this could be.
Be interested in an example.

TLS was introduced to cope with legacy software in Unix,
in particular … Posix’s stupid non-renetrant error handling
mechanism, namely errno. However that use is transparent.

Threads already have local storage, its called the stack.
So why do we need more for new software?

[OpenGL has the same design fault, only in that case
there’s no excuse]On 10/07/2013, at 7:42 PM, Sam Lantinga wrote:


john skaller
@john_skaller
http://felix-lang.org

Windows has these too. They’re like globals but thread-specific. The
most common use I presume would be to use them to store a pointer to
memory allocated in the heap (which the program has full control of).

I recall some years ago trying to use them but I don’t remember for
what now (it was for some framework), but yes, they have their use,
even if not that common.

2013/7/10, john skaller :>

On 10/07/2013, at 7:42 PM, Sam Lantinga wrote:

FYI, I just added a thread local storage API to the SDL RC build, in
SDL_thread.h:
SDL_TLSCreate(), SDL_TLSSet(), SDL_TLSGet()

Please let me know if you run into any problems with it!

Do you mean apart from conceptually? :slight_smile:

I’m curious what possible use this could be.
Be interested in an example.

TLS was introduced to cope with legacy software in Unix,
in particular … Posix’s stupid non-renetrant error handling
mechanism, namely errno. However that use is transparent.

Threads already have local storage, its called the stack.
So why do we need more for new software?

[OpenGL has the same design fault, only in that case
there’s no excuse]


john skaller
skaller at users.sourceforge.net
http://felix-lang.org


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

TLS is very useful for implementing own errno. For example:

int error_func()
{
if (rand() %2)
{
TLS_Set(my_errno,E_INVALIDRAND);
TLS_Set(my_errmsg,“Invalid rand() :(”);
return 1;
}
return 0;
}

void main()
{
if (error_func())
{
fprintf(stderr,“error_func() throwed error %s with code %d”,TLS_Get(my_errmsg),TLS_Get(my_errno));
}
}

Another use case:

int is_valid_thread(caller_t *caller)
{
return caller->thread_id == TLS_Get(current_thread_id);
}

Another use case:

void * data = ThreadAlloc();
ThreadFree(data);

where Thread* functions is a custom allocator located in TLS, which, for example, avoids memory fragmentations.

10.07.2013, ? 15:34, Sik the hedgehog ???(?):> Windows has these too. They’re like globals but thread-specific. The

most common use I presume would be to use them to store a pointer to
memory allocated in the heap (which the program has full control of).

I recall some years ago trying to use them but I don’t remember for
what now (it was for some framework), but yes, they have their use,
even if not that common.

2013/7/10, john skaller :

On 10/07/2013, at 7:42 PM, Sam Lantinga wrote:

FYI, I just added a thread local storage API to the SDL RC build, in
SDL_thread.h:
SDL_TLSCreate(), SDL_TLSSet(), SDL_TLSGet()

Please let me know if you run into any problems with it!

Do you mean apart from conceptually? :slight_smile:

I’m curious what possible use this could be.
Be interested in an example.

TLS was introduced to cope with legacy software in Unix,
in particular … Posix’s stupid non-renetrant error handling
mechanism, namely errno. However that use is transparent.

Threads already have local storage, its called the stack.
So why do we need more for new software?

[OpenGL has the same design fault, only in that case
there’s no excuse]


john skaller
skaller at users.sourceforge.net
http://felix-lang.org


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

Windows has these too. They’re like globals but thread-specific.

I know what they are :slight_smile:

As you say, they’re like globals: a design fault.

The
most common use I presume would be to use them to store a pointer to
memory allocated in the heap (which the program has full control of).

What I want to see is a specific example.
Otherwise, why add a design fault to SDL?

SDL is already burdened with design faults it inherits
from the API’s it wraps. This can’t be helped.

However before adding a very serious and extremely bad
API and encouraging programmers to do things the wrong way,
it would be nice to see an example where it is necessary.

AFAICS, it cannot be necessary except where you have callbacks
for a per thread API which are so badly designed they do not allow for a
client data pointer. In that case the callback can get/set data with TLS.

In other contexts, the data can be explicitly passed and should be.
This sometimes involves some work since C doesn’t provide
lexical closures. (This is one of the things my compiler automates).
That’s the price of using an archaic low level language: you have
to do a lot of boring error prone housekeeping.On 10/07/2013, at 10:34 PM, Sik the hedgehog wrote:


john skaller
@john_skaller
http://felix-lang.org

2013/7/10, john skaller :

As you say, they’re like globals: a design fault.

May as well say the heap is a design fault.

Again I still don’t remember well but I think that what I was doing
back then had something to do with resource management, but I suppose
you probably consider having the program doing it instead of the
language to be a design fault.

There’s also the fact that simply you want functions to cope with
thread-specific stuff, but you want them to be able to fetch that
automatically, rather than having to pass around the data all over the
place. So in that sense it helps simplify the program’s APIs by not
having to pass around the thread-specific data all over the place. But
again, that sounds like global-land again.

I wouldn’t refer to TLS in SDL as a design fault. Maybe a cross-platform
API for making your own design faults more robust?

Jonny DOn Wed, Jul 10, 2013 at 9:02 AM, Sik the hedgehog < sik.the.hedgehog at gmail.com> wrote:

2013/7/10, john skaller :

As you say, they’re like globals: a design fault.

May as well say the heap is a design fault.

Again I still don’t remember well but I think that what I was doing
back then had something to do with resource management, but I suppose
you probably consider having the program doing it instead of the
language to be a design fault.

There’s also the fact that simply you want functions to cope with
thread-specific stuff, but you want them to be able to fetch that
automatically, rather than having to pass around the data all over the
place. So in that sense it helps simplify the program’s APIs by not
having to pass around the thread-specific data all over the place. But
again, that sounds like global-land again.


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

Well, I wouldn’t put the burden of forcing the programmers to do the
right thing on any library either. C++ stdlib tried to do this and
look where it ended up.

When one desperately wants to shoot himself, he will settle with just
a foot, if the framework won’t allow him to blow his head off and be
done with it. So why bother? Not forcing shot feet and crutches on
people is good enough.–

./lxnt

I wouldn’t refer to TLS in SDL as a design fault.

That wasn’t my intent: TLS is provided in C/Unix because C/Unix
have a serious design fault: namely errno. (there are a few more
like that but that’s the most important one).

Using TLS for anything OTHER than supporting broken legacy software
like Posix error handling is a design fault.

So my question was: can I please have a single use case in SDL
where it represents good design. I’m not saying it shouldn’t be there.
I’m saying it shouldn’t be there unless there is at least one actual
use case presented.

I even suggested myself how this might arise: broken callbacks
which fail to provide a client data pointer. If I recall SDL_audio
callbacks are broken, presumably because some of the underlying
API’s they have to use are also broken.

In that case the threadID can be used as a pointer.

Maybe a cross-platform API for making your own design faults more robust?

Wouldn’t it be better to fix the design faults than
enshrine them?On 10/07/2013, at 11:55 PM, Jonathan Dearborn wrote:


john skaller
@john_skaller
http://felix-lang.org

2013/7/10, john skaller :

So my question was: can I please have a single use case in SDL
where it represents good design. I’m not saying it shouldn’t be there.
I’m saying it shouldn’t be there unless there is at least one actual
use case presented.

Actually, some other thread showed exactly the reason why TLS was
introduced… which in turn itself introduced a bug (whoops).
Basically SDL’s own error mechanism system behaves like errno, which
means it needs something like this to prevent SDL_GetError from being
useless in a multithreaded environment.

Honestly I just avoid SDL_GetError and only pay attention to the error
codes anyway.

Well, I wouldn’t put the burden of forcing the programmers to do the
right thing on any library either. C++ stdlib tried to do this and
look where it ended up.

I’m not sure I understand to what you are referring.

The STL part of C++ is one of the best generic data structure
libraries available.

When one desperately wants to shoot himself, he will settle with just
a foot, if the framework won’t allow him to blow his head off and be
done with it. So why bother? Not forcing shot feet and crutches on
people is good enough.

The way I see it is that programming languages are there to support
good design. Otherwise we’d just write hex codes (like I used to
in the old days :)On 11/07/2013, at 2:42 AM, Alexander Sabourenkov wrote:


john skaller
@john_skaller
http://felix-lang.org

2013/7/10, john skaller <@john_skaller>:

So my question was: can I please have a single use case in SDL
where it represents good design. I’m not saying it shouldn’t be there.
I’m saying it shouldn’t be there unless there is at least one actual
use case presented.

Actually, some other thread showed exactly the reason why TLS was
introduced… which in turn itself introduced a bug (whoops).

Indeed :slight_smile:

This is much like C++ throwing an exception which unwinds the stack
thereby invoking destructors which can throw exceptions … woops!

Basically SDL’s own error mechanism system behaves like errno, which
means it needs something like this to prevent SDL_GetError from being
useless in a multithreaded environment.

Honestly I just avoid SDL_GetError and only pay attention to the error
codes anyway.

So you understand the benefits of explicit coupling :slight_smile:
In particular an error code on the stack instead of a global
variable is thread safe … because the stack is “thread local” already.

The question is: why doesn’t SDL always do this. Why not GET RID
of the badly designed SDL error mechanism.

The real problem here again comes down to the crappy C language.

The RIGHT way is to return a function result or error is to use a variant
data type but C doesn’t have them (neither does C++). And the emulation
of them is a bit messy.

[For those who have no idea what a variant is, C has one example
of it: a pointer to some type T or NULL. ]On 11/07/2013, at 4:33 AM, Sik the hedgehog wrote:


john skaller
@john_skaller
http://felix-lang.org

Technically SDL uses return values to indicate when a function failed.
That’s the only thing you should pay attention to if you care about
error handling, and that’s even better than the stack because it
doesn’t even need to touch RAM (it can stay entirely within CPU
registers).

SDL_GetError merely shows the same error as a string explaining what
happened. I personally stay away from this kind of error handling
because not only I have no way to tell what it’s going to contain, but
also because they’re horrible when you try to localize the program.
It’s only useful when debugging when put together with a quick fputs
(and even then so far I never had the need to go down this route with
SDL_GetError).

And no, we can’t get rid of SDL_GetError because the ABI was frozen.
Backwards compatibility is more important here.

2013/7/10, john skaller :>

On 11/07/2013, at 4:33 AM, Sik the hedgehog wrote:

2013/7/10, john skaller :

So my question was: can I please have a single use case in SDL
where it represents good design. I’m not saying it shouldn’t be there.
I’m saying it shouldn’t be there unless there is at least one actual
use case presented.

Actually, some other thread showed exactly the reason why TLS was
introduced… which in turn itself introduced a bug (whoops).

Indeed :slight_smile:

This is much like C++ throwing an exception which unwinds the stack
thereby invoking destructors which can throw exceptions … woops!

Basically SDL’s own error mechanism system behaves like errno, which
means it needs something like this to prevent SDL_GetError from being
useless in a multithreaded environment.

Honestly I just avoid SDL_GetError and only pay attention to the error
codes anyway.

So you understand the benefits of explicit coupling :slight_smile:
In particular an error code on the stack instead of a global
variable is thread safe … because the stack is “thread local” already.

The question is: why doesn’t SDL always do this. Why not GET RID
of the badly designed SDL error mechanism.

The real problem here again comes down to the crappy C language.

The RIGHT way is to return a function result or error is to use a variant
data type but C doesn’t have them (neither does C++). And the emulation
of them is a bit messy.

[For those who have no idea what a variant is, C has one example
of it: a pointer to some type T or NULL. ]


john skaller
skaller at users.sourceforge.net
http://felix-lang.org


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

FYI, I just added a thread local storage API to the SDL RC build, in SDL_thread.h:
SDL_TLSCreate(), SDL_TLSSet(), SDL_TLSGet()

Please let me know if you run into any problems with it!

Do you mean apart from conceptually? :slight_smile:

I’m curious what possible use this could be.
Be interested in an example.

Profiling data needs to be stored per-thread.

Cheers,

UliOn Jul 10, 2013, at 2:19 PM, john skaller wrote:

On 10/07/2013, at 7:42 PM, Sam Lantinga wrote:

Any technology distinguishable from magic is insufficiently advanced.

Ulrich von Zadow | +49-172-7872715
Jabber: @Ulrich_von_Zadow
Skype: uzadow

Technically SDL uses return values to indicate when a function failed.

So does Posix. But it doesn’t indicate what kind of error it is.

That’s the only thing you should pay attention to if you care about
error handling,

That’s not so if the kind of error matters.

and that’s even better than the stack because it
doesn’t even need to touch RAM (it can stay entirely within CPU
registers).

In principle, that’s the stack (auto storage in C).
The use of registers for the top of the stack is an optimisation.
Many CPU’s have no such registers (eg SPARC uses a register
file on the stack and relies on the cache instead. The TI 990
only has ONE register and this is one of the best designs ever).

SDL_GetError merely shows the same error as a string explaining what
happened.

Ah yes. So adding the error code as a parameter would and returning the
error code would be the way to go:

int error = function arguments;
if (error) {
	char *estring = SDL_GetErrorString (error);
	printf ("Error %s\n", estring);
	exit(1);
}
...

This code is re-entrant. Even better:

char *estring = SDL_GetUTF8ErrorString (language, error);

so now the language is provided as a key was well, which makes
internationalisation much easier. [Do not use C locales they’re
not re-entrant … broken design again]

And no, we can’t get rid of SDL_GetError because the ABI was frozen.
Backwards compatibility is more important here.

It is unfortunate that backwards compatibility ensures design bugs
are immortal :slight_smile:

The way to do this is clearly mark bad features DEPRECATED
and then actually remove them in the next version.

Breaking bad code is important. It has to be done or the whole project
gets locked into an untenable situation where it cannot be upgraded.

Continuous pain is better than sudden death :slight_smile:
IMHO of course.On 11/07/2013, at 5:14 AM, Sik the hedgehog wrote:


john skaller
@john_skaller
http://felix-lang.org

I’m curious what possible use this could be.
Be interested in an example.

Profiling data needs to be stored per-thread.

OK, that’s a reasonable use, although it’s not SDL specific.

Similarly, TLS can be useful for debugging (where you want
"global variables" rather than “passing data around” because
you do not want to disturb the real interfaces because you’re
debugging)On 11/07/2013, at 5:17 AM, Ulrich von Zadow wrote:


john skaller
@john_skaller
http://felix-lang.org

Well, I wouldn’t put the burden of forcing the programmers to do the
right thing on any library either. C++ stdlib tried to do this and
look where it ended up.

I’m not sure I understand to what you are referring.

The STL part of C++ is one of the best generic data structure
libraries available.

Yes, and it’s fault is exactly in being too generic to end up
promoting a feeling of being good at any job one can think of throwing
at it. Who’s going to dive into allocators and the whole other
gazillion of template parameters when all the books say “you’re fine
with default one”? It’s Easier to just blame the implementation for
the slowness, and when it
doesn’t work, there’s one hell of a rat’s nest that one can’t even
untangle without breaking stuff, even if one’s so lucky
as to get useful results from a profiler.

When one desperately wants to shoot himself, he will settle with just
a foot, if the framework won’t allow him to blow his head off and be
done with it. So why bother? Not forcing shot feet and crutches on
people is good enough.

The way I see it is that programming languages are there to support
good design. Otherwise we’d just write hex codes (like I used to
in the old days :slight_smile:

Well, I did too :slight_smile:

However, I prefer libraries and languages to be helpful and otherwise
stay out of the way.

Thus I fully support what you argue about Uint32 and friends, but
dropping TLS … phew. I just feel indifferent.
If people think in execution paths (as opposed to data paths)
nowadays, well, they just have to learn something,
and it’s not up to library to teach them.On Wed, Jul 10, 2013 at 11:00 PM, john skaller wrote:

On 11/07/2013, at 2:42 AM, Alexander Sabourenkov wrote:

./lxnt

2013/7/10, john skaller :

The TI 990
only has ONE register and this is one of the best designs ever).

Having worked on different systems on assembly before, trust me, lack
of registers hurts, and I don’t mean it just from a performance
viewpoint.

Ah yes. So adding the error code as a parameter would and returning the
error code would be the way to go:

int error = function arguments;
if (error) {
char *estring = SDL_GetErrorString (error);
printf (“Error %s\n”, estring);
exit(1);
}

This code is re-entrant. Even better:

char *estring = SDL_GetUTF8ErrorString (language, error);

so now the language is provided as a key was well, which makes
internationalisation much easier. [Do not use C locales they’re
not re-entrant … broken design again]

Which still isn’t useful when you want to provide your own error
messages (both for the sake of consistency and because the library may
not provide all the languages you need), and even worse, what if you
want to get which error happened to do something specific based on
that? (you’ll prefer an error code over an error message that can
change at any time - though mind you, admittedly I found out that in
the end I just bail out in case of error and report which operation
failed instead)

Seriously. APIs shouldn’t try to report error messages, they don’t
know what the program needs. Leave error reporting to the program. At
the very least provide an easy way for this.

(sidenote: SDL only works with UTF-8 as far as I know, so adding UTF8
to a function name is redundant)

It is unfortunate that backwards compatibility ensures design bugs
are immortal :slight_smile:

The way to do this is clearly mark bad features DEPRECATED
and then actually remove them in the next version.

Breaking bad code is important. It has to be done or the whole project
gets locked into an untenable situation where it cannot be upgraded.

Continuous pain is better than sudden death :slight_smile:
IMHO of course.

There are more serious design bugs which are unavoidable yet I don’t
see you complain about that. I think the prime example is how SDL
programs still rely on the old way of running a program with a main
loop, when phone OSes (which SDL supports) are heavily event-driven
instead, to the point that SDL has to resorts to all sorts of hacks to
work nicely in such an environment. No complaints about that either?
(I guess this is going off-topic though)

Developers are going to want to use TLS at some point, may as well do
the hard work and handle it for them than having everybody reinvent
the wheel in the worst way possible. At least this is something you
can avoid if you think TLS is a bad thing.

Quick question.? Let’s say that someone is using SDL with Delphi, which implements thead-local storage as a language feature and has some data (for exception handling) already set up to use TLS as part of the runtime.? Will having another system that tries to implement TLS loaded into the same process end up clashing with it and breaking stuff?

Mason________________________________
From: Sik the hedgehog <sik.the.hedgehog at gmail.com>
To: SDL Development List
Sent: Wednesday, July 10, 2013 1:29 PM
Subject: Re: [SDL] Thread local storage API

2013/7/10, john skaller :

The TI 990
only has ONE register and this is one of the best designs ever).

Having worked on different systems on assembly before, trust me, lack
of registers hurts, and I don’t mean it just from a performance
viewpoint.

Ah yes. So adding the error code as a parameter would and returning the
error code would be the way to go:

??? int error = function arguments;
??? if (error) {
??? ??? char *estring = SDL_GetErrorString (error);
??? ??? printf (“Error %s\n”, estring);
??? ??? exit(1);
??? }
??? …

This code is re-entrant. Even better:

??? char *estring = SDL_GetUTF8ErrorString (language, error);

so now the language is provided as a key was well, which makes
internationalisation much easier. [Do not use C locales they’re
not re-entrant … broken design again]

Which still isn’t useful when you want to provide your own error
messages (both for the sake of consistency and because the library may
not provide all the languages you need), and even worse, what if you
want to get which error happened to do something specific based on
that? (you’ll prefer an error code over an error message that can
change at any time - though mind you, admittedly I found out that in
the end I just bail out in case of error and report which operation
failed instead)

Seriously. APIs shouldn’t try to report error messages, they don’t
know what the program needs. Leave error reporting to the program. At
the very least provide an easy way for this.

(sidenote: SDL only works with UTF-8 as far as I know, so adding UTF8
to a function name is redundant)

It is unfortunate that backwards compatibility ensures design bugs
are immortal? :slight_smile:

The way to do this is clearly mark bad features DEPRECATED
and then actually remove them in the next version.

Breaking bad code is important. It has to be done or the whole project
gets locked into an untenable situation where it cannot be upgraded.

Continuous pain is better than sudden death :slight_smile:
IMHO of course.

There are more serious design bugs which are unavoidable yet I don’t
see you complain about that. I think the prime example is how SDL
programs still rely on the old way of running a program with a main
loop, when phone OSes (which SDL supports) are heavily event-driven
instead, to the point that SDL has to resorts to all sorts of hacks to
work nicely in such an environment. No complaints about that either?
(I guess this is going off-topic though)

Developers are going to want to use TLS at some point, may as well do
the hard work and handle it for them than having everybody reinvent
the wheel in the worst way possible. At least this is something you
can avoid if you think TLS is a bad thing.


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

Nope. SDL only uses one thread local storage slot for all its thread local
storage and will fall back to a slower system if it can’t get any fast TLS
from the OS.On Wed, Jul 10, 2013 at 3:13 PM, Mason Wheeler wrote:

Quick question. Let’s say that someone is using SDL with Delphi, which
implements thead-local storage as a language feature and has some data (for
exception handling) already set up to use TLS as part of the runtime. Will
having another system that tries to implement TLS loaded into the same
process end up clashing with it and breaking stuff?

Mason


From: Sik the hedgehog <sik.the.hedgehog at gmail.com>
To: SDL Development List
Sent: Wednesday, July 10, 2013 1:29 PM
Subject: Re: [SDL] Thread local storage API

2013/7/10, john skaller :

The TI 990
only has ONE register and this is one of the best designs ever).

Having worked on different systems on assembly before, trust me, lack
of registers hurts, and I don’t mean it just from a performance
viewpoint.

Ah yes. So adding the error code as a parameter would and returning the
error code would be the way to go:

int error = function arguments;
if (error) {
    char *estring = SDL_GetErrorString (error);
    printf ("Error %s\n", estring);
    exit(1);
}
...

This code is re-entrant. Even better:

char *estring = SDL_GetUTF8ErrorString (language, error);

so now the language is provided as a key was well, which makes
internationalisation much easier. [Do not use C locales they’re
not re-entrant … broken design again]

Which still isn’t useful when you want to provide your own error
messages (both for the sake of consistency and because the library may
not provide all the languages you need), and even worse, what if you
want to get which error happened to do something specific based on
that? (you’ll prefer an error code over an error message that can
change at any time - though mind you, admittedly I found out that in
the end I just bail out in case of error and report which operation
failed instead)

Seriously. APIs shouldn’t try to report error messages, they don’t
know what the program needs. Leave error reporting to the program. At
the very least provide an easy way for this.

(sidenote: SDL only works with UTF-8 as far as I know, so adding UTF8
to a function name is redundant)

It is unfortunate that backwards compatibility ensures design bugs
are immortal :slight_smile:

The way to do this is clearly mark bad features DEPRECATED
and then actually remove them in the next version.

Breaking bad code is important. It has to be done or the whole project
gets locked into an untenable situation where it cannot be upgraded.

Continuous pain is better than sudden death :slight_smile:
IMHO of course.

There are more serious design bugs which are unavoidable yet I don’t
see you complain about that. I think the prime example is how SDL
programs still rely on the old way of running a program with a main
loop, when phone OSes (which SDL supports) are heavily event-driven
instead, to the point that SDL has to resorts to all sorts of hacks to
work nicely in such an environment. No complaints about that either?
(I guess this is going off-topic though)

Developers are going to want to use TLS at some point, may as well do
the hard work and handle it for them than having everybody reinvent
the wheel in the worst way possible. At least this is something you
can avoid if you think TLS is a bad thing.


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org