Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
977 lines (734 sloc) 21.5 KB
/* ______ ___ ___
* /\ _ \ /\_ \ /\_ \
* \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
* \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
* \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
* \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
* \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
* /\____/
* \_/__/
*
* Thread local storage.
*
* By Trent Gamblin.
*
*/
/* FIXME:
*
* There are several ways to get thread local storage:
*
* 1. pthreads.
* 2. __thread keyword in gcc.
* 3. __declspec(thread) in MSVC.
* 4. TLS API under Windows.
*
* Since pthreads is available from the system everywhere except in
* Windows, this is the only case which is problematic. It appears
* that except for old mingw versions (before gcc 4.2) we can simply
* use __thread, and for MSVC we can always use __declspec(thread):
*
* However there also is a WANT_TLS configuration variable which is on
* by default and forces use of the TLS API instead. At the same time,
* the implementation using the TLS API in this file does not work
* with static linking. Someone should either completely remove
* WANT_TLS, or fix the static linking case...
*/
#include <string.h>
#include "allegro5/allegro.h"
#include "allegro5/internal/aintern.h"
#include "allegro5/internal/aintern_bitmap.h"
#include "allegro5/internal/aintern_display.h"
#include "allegro5/internal/aintern_file.h"
#include "allegro5/internal/aintern_fshook.h"
#include "allegro5/internal/aintern_shader.h"
#include "allegro5/internal/aintern_tls.h"
#ifdef ALLEGRO_ANDROID
#include "allegro5/internal/aintern_android.h"
#endif
#if defined(ALLEGRO_MINGW32) && !defined(ALLEGRO_CFG_DLL_TLS)
/*
* MinGW < 4.2.1 doesn't have builtin thread local storage, so we
* must use the Windows API.
*/
#if __GNUC__ < 4
#define ALLEGRO_CFG_DLL_TLS
#elif __GNUC__ == 4 && __GNUC_MINOR__ < 2
#define ALLEGRO_CFG_DLL_TLS
#elif __GNUC__ == 4 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ < 1
#define ALLEGRO_CFG_DLL_TLS
#endif
#endif
/* Thread local storage for various per-thread state. */
typedef struct thread_local_state {
/* New display parameters */
int new_display_flags;
int new_display_refresh_rate;
int new_display_adapter;
int new_window_x;
int new_window_y;
int new_bitmap_depth;
int new_bitmap_samples;
ALLEGRO_EXTRA_DISPLAY_SETTINGS new_display_settings;
/* Current display */
ALLEGRO_DISPLAY *current_display;
/* Target bitmap */
ALLEGRO_BITMAP *target_bitmap;
/* Blender */
ALLEGRO_BLENDER current_blender;
/* Bitmap parameters */
int new_bitmap_format;
int new_bitmap_flags;
/* Files */
const ALLEGRO_FILE_INTERFACE *new_file_interface;
const ALLEGRO_FS_INTERFACE *fs_interface;
/* Error code */
int allegro_errno;
/* Title to use for a new window/display.
* This is a static buffer for API reasons.
*/
char new_window_title[ALLEGRO_NEW_WINDOW_TITLE_MAX_SIZE + 1];
#ifdef ALLEGRO_ANDROID
JNIEnv *jnienv;
#endif
/* Destructor ownership count */
int dtor_owner_count;
} thread_local_state;
typedef struct INTERNAL_STATE {
thread_local_state tls;
ALLEGRO_BLENDER stored_blender;
ALLEGRO_TRANSFORM stored_transform;
ALLEGRO_TRANSFORM stored_projection_transform;
int flags;
} INTERNAL_STATE;
ALLEGRO_STATIC_ASSERT(tls, sizeof(ALLEGRO_STATE) > sizeof(INTERNAL_STATE));
static void initialize_blender(ALLEGRO_BLENDER *b)
{
b->blend_op = ALLEGRO_ADD;
b->blend_source = ALLEGRO_ONE,
b->blend_dest = ALLEGRO_INVERSE_ALPHA;
b->blend_alpha_op = ALLEGRO_ADD;
b->blend_alpha_source = ALLEGRO_ONE;
b->blend_alpha_dest = ALLEGRO_INVERSE_ALPHA;
b->blend_color = al_map_rgba_f(1.0f, 1.0f, 1.0f, 1.0f);
}
static void initialize_tls_values(thread_local_state *tls)
{
memset(tls, 0, sizeof *tls);
tls->new_display_adapter = ALLEGRO_DEFAULT_DISPLAY_ADAPTER;
tls->new_window_x = INT_MAX;
tls->new_window_y = INT_MAX;
initialize_blender(&tls->current_blender);
tls->new_bitmap_flags = ALLEGRO_CONVERT_BITMAP;
tls->new_bitmap_format = ALLEGRO_PIXEL_FORMAT_ANY_WITH_ALPHA;
tls->new_file_interface = &_al_file_interface_stdio;
tls->fs_interface = &_al_fs_interface_stdio;
memset(tls->new_window_title, 0, ALLEGRO_NEW_WINDOW_TITLE_MAX_SIZE + 1);
_al_fill_display_settings(&tls->new_display_settings);
}
// FIXME: The TLS implementation below only works for dynamic linking
// right now - instead of using DllMain we should simply initialize
// on first request.
#ifdef ALLEGRO_STATICLINK
#undef ALLEGRO_CFG_DLL_TLS
#endif
#if defined(ALLEGRO_CFG_DLL_TLS)
#include "tls_dll.inc"
#elif defined(ALLEGRO_MACOSX) || defined(ALLEGRO_IPHONE) || defined(ALLEGRO_ANDROID) || defined(ALLEGRO_RASPBERRYPI)
#include "tls_pthread.inc"
#else
#include "tls_native.inc"
#endif
void _al_reinitialize_tls_values(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
initialize_tls_values(tls);
}
void _al_set_new_display_settings(ALLEGRO_EXTRA_DISPLAY_SETTINGS *settings)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
memmove(&tls->new_display_settings, settings, sizeof(ALLEGRO_EXTRA_DISPLAY_SETTINGS));
}
ALLEGRO_EXTRA_DISPLAY_SETTINGS *_al_get_new_display_settings(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return 0;
return &tls->new_display_settings;
}
/* Function: al_set_new_window_title
*/
void al_set_new_window_title(const char *title)
{
thread_local_state *tls;
size_t size;
if ((tls = tls_get()) == NULL)
return;
size = strlen(title);
if (size > ALLEGRO_NEW_WINDOW_TITLE_MAX_SIZE) {
size = ALLEGRO_NEW_WINDOW_TITLE_MAX_SIZE;
}
_al_sane_strncpy(tls->new_window_title, title, size + 1);
}
/* Function: al_get_new_window_title
*/
const char *al_get_new_window_title(void)
{
thread_local_state *tls;
/* Return app name in case of error or if not set before. */
if ((tls = tls_get()) == NULL)
return al_get_app_name();
if (strlen(tls->new_window_title) < 1)
return al_get_app_name();
return (const char *)tls->new_window_title;
}
/* Function: al_set_new_display_flags
*/
void al_set_new_display_flags(int flags)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
tls->new_display_flags = flags;
}
/* Function: al_get_new_display_flags
*/
int al_get_new_display_flags(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return 0;
return tls->new_display_flags;
}
/* Function: al_set_new_display_refresh_rate
*/
void al_set_new_display_refresh_rate(int refresh_rate)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
tls->new_display_refresh_rate = refresh_rate;
}
/* Function: al_get_new_display_refresh_rate
*/
int al_get_new_display_refresh_rate(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return 0;
return tls->new_display_refresh_rate;
}
/* Function: al_set_new_display_adapter
*/
void al_set_new_display_adapter(int adapter)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
if (adapter < 0) {
tls->new_display_adapter = ALLEGRO_DEFAULT_DISPLAY_ADAPTER;
}
tls->new_display_adapter = adapter;
}
/* Function: al_get_new_display_adapter
*/
int al_get_new_display_adapter(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return ALLEGRO_DEFAULT_DISPLAY_ADAPTER;
return tls->new_display_adapter;
}
/* Function: al_set_new_window_position
*/
void al_set_new_window_position(int x, int y)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
tls->new_window_x = x;
tls->new_window_y = y;
}
/* Function: al_get_new_window_position
*/
void al_get_new_window_position(int *x, int *y)
{
thread_local_state *tls;
int new_window_x = INT_MAX;
int new_window_y = INT_MAX;
if ((tls = tls_get()) != NULL) {
new_window_x = tls->new_window_x;
new_window_y = tls->new_window_y;
}
if (x)
*x = new_window_x;
if (y)
*y = new_window_y;
}
/* Make the given display current, without changing the target bitmap.
* This is used internally to change the current display transiently.
*/
bool _al_set_current_display_only(ALLEGRO_DISPLAY *display)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return false;
if (tls->current_display &&
tls->current_display->vt &&
tls->current_display->vt->unset_current_display) {
tls->current_display->vt->unset_current_display(tls->current_display);
tls->current_display = NULL;
}
if (display &&
display->vt &&
display->vt->set_current_display) {
if (!display->vt->set_current_display(display))
return false;
}
tls->current_display = display;
return true;
}
/* Function: al_get_current_display
*/
ALLEGRO_DISPLAY *al_get_current_display(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return NULL;
return tls->current_display;
}
/* Function: al_set_target_bitmap
*/
void al_set_target_bitmap(ALLEGRO_BITMAP *bitmap)
{
thread_local_state *tls;
ALLEGRO_DISPLAY *old_display;
ALLEGRO_DISPLAY *new_display;
ALLEGRO_SHADER *old_shader;
ALLEGRO_SHADER *new_shader;
bool same_shader;
int bitmap_flags = bitmap ? al_get_bitmap_flags(bitmap) : 0;
ASSERT(!al_is_bitmap_drawing_held());
if (bitmap) {
if (bitmap->parent) {
bitmap->parent->dirty = true;
}
else {
bitmap->dirty = true;
}
}
if ((tls = tls_get()) == NULL)
return;
old_display = tls->current_display;
if (tls->target_bitmap)
old_shader = tls->target_bitmap->shader;
else
old_shader = NULL;
if (bitmap == NULL) {
/* Explicitly releasing the current rendering context. */
new_display = NULL;
new_shader = NULL;
}
else if (bitmap_flags & ALLEGRO_MEMORY_BITMAP) {
/* Setting a memory bitmap doesn't change the rendering context. */
new_display = old_display;
new_shader = NULL;
}
else {
new_display = _al_get_bitmap_display(bitmap);
new_shader = bitmap->shader;
}
same_shader = (old_shader == new_shader && old_display == new_display);
/* Unset the old shader if necessary. */
if (old_shader && !same_shader) {
old_shader->vt->unuse_shader(old_shader, old_display);
}
/* Change the rendering context if necessary. */
if (old_display != new_display) {
if (old_display &&
old_display->vt &&
old_display->vt->unset_current_display) {
old_display->vt->unset_current_display(old_display);
}
tls->current_display = new_display;
if (new_display &&
new_display->vt &&
new_display->vt->set_current_display) {
new_display->vt->set_current_display(new_display);
}
}
/* Change the target bitmap itself. */
tls->target_bitmap = bitmap;
if (bitmap &&
!(bitmap_flags & ALLEGRO_MEMORY_BITMAP) &&
new_display &&
new_display->vt &&
new_display->vt->set_target_bitmap)
{
new_display->vt->set_target_bitmap(new_display, bitmap);
/* Set the new shader if necessary. This should done before the
* update_transformation call, which will also update the shader's
* al_projview_matrix variable.
*/
if (!same_shader || !new_shader) {
al_use_shader(new_shader);
}
new_display->vt->update_transformation(new_display, bitmap);
}
}
/* Function: al_set_target_backbuffer
*/
void al_set_target_backbuffer(ALLEGRO_DISPLAY *display)
{
al_set_target_bitmap(al_get_backbuffer(display));
}
/* Function: al_get_target_bitmap
*/
ALLEGRO_BITMAP *al_get_target_bitmap(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return 0;
return tls->target_bitmap;
}
/* Function: al_set_blender
*/
void al_set_blender(int op, int src, int dst)
{
al_set_separate_blender(op, src, dst, op, src, dst);
}
/* Function: al_set_blend_color
*/
void al_set_blend_color(ALLEGRO_COLOR color)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
tls->current_blender.blend_color = color;
}
/* Function: al_set_separate_blender
*/
void al_set_separate_blender(int op, int src, int dst,
int alpha_op, int alpha_src, int alpha_dst)
{
thread_local_state *tls;
ALLEGRO_BLENDER *b;
ASSERT(op >= 0 && op < ALLEGRO_NUM_BLEND_OPERATIONS);
ASSERT(src >= 0 && src < ALLEGRO_NUM_BLEND_MODES);
ASSERT(dst >= 0 && src < ALLEGRO_NUM_BLEND_MODES);
ASSERT(alpha_op >= 0 && alpha_op < ALLEGRO_NUM_BLEND_OPERATIONS);
ASSERT(alpha_src >= 0 && alpha_src < ALLEGRO_NUM_BLEND_MODES);
ASSERT(alpha_dst >= 0 && alpha_dst < ALLEGRO_NUM_BLEND_MODES);
if ((tls = tls_get()) == NULL)
return;
b = &tls->current_blender;
b->blend_op = op;
b->blend_source = src;
b->blend_dest = dst;
b->blend_alpha_op = alpha_op;
b->blend_alpha_source = alpha_src;
b->blend_alpha_dest = alpha_dst;
}
/* Function: al_get_blender
*/
void al_get_blender(int *op, int *src, int *dst)
{
al_get_separate_blender(op, src, dst, NULL, NULL, NULL);
}
/* Function: al_get_blend_color
*/
ALLEGRO_COLOR al_get_blend_color(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return al_map_rgba(255, 255, 255, 255);
return tls->current_blender.blend_color;
}
/* Function: al_get_separate_blender
*/
void al_get_separate_blender(int *op, int *src, int *dst,
int *alpha_op, int *alpha_src, int *alpha_dst)
{
thread_local_state *tls;
ALLEGRO_BLENDER *b;
if ((tls = tls_get()) == NULL)
return;
b = &tls->current_blender;
if (op)
*op = b->blend_op;
if (src)
*src = b->blend_source;
if (dst)
*dst = b->blend_dest;
if (alpha_op)
*alpha_op = b->blend_alpha_op;
if (alpha_src)
*alpha_src = b->blend_alpha_source;
if (alpha_dst)
*alpha_dst = b->blend_alpha_dest;
}
/* Function: al_set_new_bitmap_format
*/
void al_set_new_bitmap_format(int format)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
tls->new_bitmap_format = format;
}
/* Function: al_set_new_bitmap_flags
*/
void al_set_new_bitmap_flags(int flags)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
tls->new_bitmap_flags = flags;
}
/* Function: al_add_new_bitmap_flag
*/
void al_add_new_bitmap_flag(int flag)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
tls->new_bitmap_flags |= flag;
}
/* Function: al_get_new_bitmap_format
*/
int al_get_new_bitmap_format(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return 0;
return tls->new_bitmap_format;
}
/* Function: al_get_new_bitmap_flags
*/
int al_get_new_bitmap_flags(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return 0;
return tls->new_bitmap_flags;
}
/* Function: al_store_state
*/
void al_store_state(ALLEGRO_STATE *state, int flags)
{
thread_local_state *tls;
INTERNAL_STATE *stored;
if ((tls = tls_get()) == NULL)
return;
stored = (void *)state;
stored->flags = flags;
#define _STORE(x) (stored->tls.x = tls->x)
if (flags & ALLEGRO_STATE_NEW_DISPLAY_PARAMETERS) {
_STORE(new_display_flags);
_STORE(new_display_refresh_rate);
_STORE(new_display_adapter);
_STORE(new_window_x);
_STORE(new_window_y);
_STORE(new_display_settings);
_al_sane_strncpy(stored->tls.new_window_title, tls->new_window_title,
strlen(tls->new_window_title));
}
if (flags & ALLEGRO_STATE_NEW_BITMAP_PARAMETERS) {
_STORE(new_bitmap_format);
_STORE(new_bitmap_flags);
}
if (flags & ALLEGRO_STATE_DISPLAY) {
_STORE(current_display);
}
if (flags & ALLEGRO_STATE_TARGET_BITMAP) {
_STORE(target_bitmap);
}
if (flags & ALLEGRO_STATE_BLENDER) {
stored->stored_blender = tls->current_blender;
}
if (flags & ALLEGRO_STATE_NEW_FILE_INTERFACE) {
_STORE(new_file_interface);
_STORE(fs_interface);
}
if (flags & ALLEGRO_STATE_TRANSFORM) {
ALLEGRO_BITMAP *target = al_get_target_bitmap();
if (!target)
al_identity_transform(&stored->stored_transform);
else
stored->stored_transform = target->transform;
}
if (flags & ALLEGRO_STATE_PROJECTION_TRANSFORM) {
ALLEGRO_BITMAP *target = al_get_target_bitmap();
if (target) {
stored->stored_projection_transform = target->proj_transform;
}
}
#undef _STORE
}
/* Function: al_restore_state
*/
void al_restore_state(ALLEGRO_STATE const *state)
{
thread_local_state *tls;
INTERNAL_STATE *stored;
int flags;
if ((tls = tls_get()) == NULL)
return;
stored = (void *)state;
flags = stored->flags;
#define _RESTORE(x) (tls->x = stored->tls.x)
if (flags & ALLEGRO_STATE_NEW_DISPLAY_PARAMETERS) {
_RESTORE(new_display_flags);
_RESTORE(new_display_refresh_rate);
_RESTORE(new_display_adapter);
_RESTORE(new_window_x);
_RESTORE(new_window_y);
_RESTORE(new_display_settings);
_al_sane_strncpy(tls->new_window_title, stored->tls.new_window_title,
strlen(tls->new_window_title));
}
if (flags & ALLEGRO_STATE_NEW_BITMAP_PARAMETERS) {
_RESTORE(new_bitmap_format);
_RESTORE(new_bitmap_flags);
}
if (flags & ALLEGRO_STATE_DISPLAY) {
if (tls->current_display != stored->tls.current_display) {
_al_set_current_display_only(stored->tls.current_display);
ASSERT(tls->current_display == stored->tls.current_display);
}
}
if (flags & ALLEGRO_STATE_TARGET_BITMAP) {
if (tls->target_bitmap != stored->tls.target_bitmap) {
al_set_target_bitmap(stored->tls.target_bitmap);
ASSERT(tls->target_bitmap == stored->tls.target_bitmap);
}
}
if (flags & ALLEGRO_STATE_BLENDER) {
tls->current_blender = stored->stored_blender;
}
if (flags & ALLEGRO_STATE_NEW_FILE_INTERFACE) {
_RESTORE(new_file_interface);
_RESTORE(fs_interface);
}
if (flags & ALLEGRO_STATE_TRANSFORM) {
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
if (bitmap)
al_use_transform(&stored->stored_transform);
}
if (flags & ALLEGRO_STATE_PROJECTION_TRANSFORM) {
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
if (bitmap)
al_use_projection_transform(&stored->stored_projection_transform);
}
#undef _RESTORE
}
/* Function: al_get_new_file_interface
* FIXME: added a work-around for the situation where TLS has not yet been
* initialised when this function is called. This may happen if Allegro
* tries to load a configuration file before the system has been
* initialised. Should probably rethink the logic here...
*/
const ALLEGRO_FILE_INTERFACE *al_get_new_file_interface(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return &_al_file_interface_stdio;
/* FIXME: this situation should never arise because tls_ has the stdio
* interface set as a default, but it arises on OS X if
* pthread_getspecific() is called before pthreads_thread_init()...
*/
if (tls->new_file_interface)
return tls->new_file_interface;
else
return &_al_file_interface_stdio;
}
/* Function: al_set_new_file_interface
*/
void al_set_new_file_interface(const ALLEGRO_FILE_INTERFACE *file_interface)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
tls->new_file_interface = file_interface;
}
/* Function: al_get_fs_interface
*/
const ALLEGRO_FS_INTERFACE *al_get_fs_interface(void)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return &_al_fs_interface_stdio;
if (tls->fs_interface)
return tls->fs_interface;
else
return &_al_fs_interface_stdio;
}
/* Function: al_set_fs_interface
*/
void al_set_fs_interface(const ALLEGRO_FS_INTERFACE *fs_interface)
{
thread_local_state *tls;
if ((tls = tls_get()) == NULL)
return;
tls->fs_interface = fs_interface;
}
/* Function: al_set_standard_fs_interface
*/
void al_set_standard_fs_interface(void)
{
al_set_fs_interface(&_al_fs_interface_stdio);
}
#define SETTER(name, value) \
{ \
thread_local_state *tls; \
if ((tls = tls_get()) == NULL) \
return; \
tls->name = value; \
}
#define GETTER(name, value) \
{ \
thread_local_state *tls; \
if ((tls = tls_get()) == NULL) \
return value; \
return tls->name; \
}
/* Function: al_get_errno
*/
int al_get_errno(void)
GETTER(allegro_errno, 0)
/* Function: al_set_errno
*/
void al_set_errno(int errnum)
SETTER(allegro_errno, errnum)
/* Function: al_get_new_bitmap_depth
*/
int al_get_new_bitmap_depth(void)
GETTER(new_bitmap_depth, 0)
/* Function: al_set_new_bitmap_depth
*/
void al_set_new_bitmap_depth(int depth)
SETTER(new_bitmap_depth, depth)
/* Function: al_get_new_bitmap_samples
*/
int al_get_new_bitmap_samples(void)
GETTER(new_bitmap_samples, 0)
/* Function: al_set_new_bitmap_samples
*/
void al_set_new_bitmap_samples(int samples)
SETTER(new_bitmap_samples, samples)
#ifdef ALLEGRO_ANDROID
JNIEnv *_al_android_get_jnienv(void)
GETTER(jnienv, 0)
void _al_android_set_jnienv(JNIEnv *jnienv)
SETTER(jnienv, jnienv)
#endif
int *_al_tls_get_dtor_owner_count(void)
{
thread_local_state *tls;
tls = tls_get();
return &tls->dtor_owner_count;
}
/* vim: set sts=3 sw=3 et: */