diff options
Diffstat (limited to 'c_src')
-rw-r--r-- | c_src/esdl2.c | 4 | ||||
-rw-r--r-- | c_src/esdl2.h | 2 | ||||
-rw-r--r-- | c_src/nif_helpers.c | 186 | ||||
-rw-r--r-- | c_src/nif_helpers.h | 11 | ||||
-rw-r--r-- | c_src/sdl_events.c | 9 | ||||
-rw-r--r-- | c_src/sdl_renderer.c | 107 | ||||
-rw-r--r-- | c_src/sdl_texture.c | 23 | ||||
-rw-r--r-- | c_src/sdl_window.c | 34 |
8 files changed, 337 insertions, 39 deletions
diff --git a/c_src/esdl2.c b/c_src/esdl2.c index 460526e..13d18d0 100644 --- a/c_src/esdl2.c +++ b/c_src/esdl2.c @@ -13,6 +13,7 @@ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "esdl2.h" +#include <sys/queue.h> NIF_ATOMS(NIF_ATOM_DECL) NIF_RESOURCES(NIF_RES_DECL) @@ -22,11 +23,14 @@ int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) NIF_ATOMS(NIF_ATOM_INIT) NIF_RESOURCES(NIF_RES_INIT) + *priv_data = nif_create_main_thread("esdl2"); + return 0; } void unload(ErlNifEnv* env, void* priv_data) { + nif_destroy_main_thread(priv_data); } static ErlNifFunc nif_funcs[] = { diff --git a/c_src/esdl2.h b/c_src/esdl2.h index d3c57c4..455c42d 100644 --- a/c_src/esdl2.h +++ b/c_src/esdl2.h @@ -82,6 +82,7 @@ A(xrel) \ A(y) \ A(yrel) \ + A(_nif_thread_ret_) // List of resources used by this NIF. @@ -110,6 +111,7 @@ F(render_clear, 1) \ F(render_copy, 4) \ F(render_present, 1) \ + F(render_set_logical_size, 3) \ F(set_render_draw_color, 5) \ /* sdl_surface */ \ F(img_load, 1) \ diff --git a/c_src/nif_helpers.c b/c_src/nif_helpers.c new file mode 100644 index 0000000..820fef8 --- /dev/null +++ b/c_src/nif_helpers.c @@ -0,0 +1,186 @@ +// Copyright (c) 2014, Loïc Hoguin <[email protected]> +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#include "nif_helpers.h" +#include <sys/queue.h> +#include <stdarg.h> + +extern atom_ok; +extern atom__nif_thread_ret_; + +typedef struct nif_thread_message { + TAILQ_ENTRY(nif_thread_message) next_entry; + + ErlNifPid* from_pid; + void* function; + nif_thread_arg* args; +} nif_thread_message; + +typedef TAILQ_HEAD(nif_thread_mailbox, nif_thread_message) nif_thread_mailbox; + +typedef struct { + ErlNifTid tid; + ErlNifMutex* lock; + ErlNifCond* cond; + nif_thread_mailbox* mailbox; +} nif_thread_state; + +// Message. + +nif_thread_message* nif_thread_message_alloc(void* f, nif_thread_arg* args, ErlNifPid* pid) +{ + nif_thread_message* msg = (nif_thread_message*)enif_alloc(sizeof(nif_thread_message)); + + msg->from_pid = pid; + msg->function = f; + msg->args = args; + + return msg; +} + +void nif_thread_message_free(nif_thread_message* msg) +{ + enif_free(msg->from_pid); + enif_free(msg->args); + enif_free(msg); +} + +// Calls and casts. + +ERL_NIF_TERM nif_thread_send(nif_thread_state* st, nif_thread_message* msg) +{ + enif_mutex_lock(st->lock); + + TAILQ_INSERT_TAIL(st->mailbox, msg, next_entry); + + enif_cond_signal(st->cond); + enif_mutex_unlock(st->lock); + + return atom_ok; +} + +ERL_NIF_TERM nif_thread_cast(ErlNifEnv* env, void (*f)(nif_thread_arg*), int a, ...) +{ + va_list ap; + int i; + + nif_thread_arg* args = (nif_thread_arg*)enif_alloc(a * sizeof(nif_thread_arg)); + + va_start(ap, a); + for (i = 0; i < a; i++) + args[i] = va_arg(ap, void*); + va_end(ap); + + nif_thread_message* msg = nif_thread_message_alloc(f, args, NULL); + + return nif_thread_send((nif_thread_state*)enif_priv_data(env), msg); +} + +ERL_NIF_TERM nif_thread_call(ErlNifEnv* env, ERL_NIF_TERM (*f)(ErlNifEnv*, nif_thread_arg*), int a, ...) +{ + va_list ap; + int i; + + nif_thread_arg* args = (nif_thread_arg*)enif_alloc(a * sizeof(nif_thread_arg)); + + va_start(ap, a); + for (i = 0; i < a; i++) + args[i] = va_arg(ap, void*); + va_end(ap); + + ErlNifPid* pid = (ErlNifPid*)enif_alloc(sizeof(ErlNifPid)); + nif_thread_message* msg = nif_thread_message_alloc((void*)f, args, enif_self(env, pid)); + + return nif_thread_send((nif_thread_state*)enif_priv_data(env), msg); +} + +// Main thread loop. + +int nif_thread_receive(nif_thread_state* st, nif_thread_message** msg) +{ + enif_mutex_lock(st->lock); + + while (TAILQ_EMPTY(st->mailbox)) + enif_cond_wait(st->cond, st->lock); + + *msg = TAILQ_FIRST(st->mailbox); + TAILQ_REMOVE(st->mailbox, TAILQ_FIRST(st->mailbox), next_entry); + + enif_mutex_unlock(st->lock); + + if ((*msg)->function == NULL) + return 0; + + return 1; +} + +void nif_thread_handle(ErlNifEnv* env, nif_thread_state* st, nif_thread_message* msg) +{ + if (msg->from_pid == NULL) { + void (*cast)(nif_thread_arg*) = msg->function; + cast(msg->args); + } else { + ERL_NIF_TERM (*call)(ErlNifEnv*, nif_thread_arg*) = msg->function; + ERL_NIF_TERM ret = call(env, msg->args); + + enif_send(NULL, msg->from_pid, env, + enif_make_tuple2(env, atom__nif_thread_ret_, ret)); + + enif_clear_env(env); + } + + nif_thread_message_free(msg); +} + +void* nif_main_thread(void* obj) +{ + ErlNifEnv* env = enif_alloc_env(); + nif_thread_state* st = (nif_thread_state*)obj; + nif_thread_message* msg; + + while (nif_thread_receive(st, &msg)) + nif_thread_handle(env, st, msg); + + return NULL; +} + +// Main thread creation/destruction. + +void* nif_create_main_thread(char* name) +{ + nif_thread_state* st = (nif_thread_state*)enif_alloc(sizeof(nif_thread_state)); + + st->lock = enif_mutex_create("esdl2_lock"); + st->cond = enif_cond_create("esdl2_cond"); + st->mailbox = (nif_thread_mailbox*)enif_alloc(sizeof(nif_thread_mailbox)); + TAILQ_INIT(st->mailbox); + + enif_thread_create(name, &(st->tid), nif_main_thread, st, NULL); + + return (void*)st; +} + +void nif_destroy_main_thread(void* void_st) +{ + nif_thread_state* st = (nif_thread_state*)void_st; + nif_thread_message* msg = nif_thread_message_alloc(NULL, NULL, NULL); + + nif_thread_send(st, msg); + enif_thread_join(st->tid, NULL); + + enif_cond_destroy(st->cond); + enif_mutex_destroy(st->lock); + enif_free(st->mailbox); + enif_free(st); +} diff --git a/c_src/nif_helpers.h b/c_src/nif_helpers.h index 06eb787..d359f50 100644 --- a/c_src/nif_helpers.h +++ b/c_src/nif_helpers.h @@ -106,4 +106,15 @@ return atom_undefined; \ } +// Threaded NIFs. + +typedef void* nif_thread_arg; + +void* nif_create_main_thread(char*); +ERL_NIF_TERM nif_thread_cast(ErlNifEnv*, void (*f)(nif_thread_arg*), int a, ...); +ERL_NIF_TERM nif_thread_call(ErlNifEnv*, ERL_NIF_TERM (*f)(ErlNifEnv*, nif_thread_arg*), int a, ...); + +#define NIF_CAST_HANDLER(f) void f(nif_thread_arg* args) +#define NIF_CALL_HANDLER(f) ERL_NIF_TERM f(ErlNifEnv* env, nif_thread_arg* args) + #endif diff --git a/c_src/sdl_events.c b/c_src/sdl_events.c index 4593a09..6952275 100644 --- a/c_src/sdl_events.c +++ b/c_src/sdl_events.c @@ -68,7 +68,9 @@ NIF_FLAGS_TO_LIST_FUNCTION(keymod_flags_to_list, Uint16, KEYMOD_FLAGS) NIF_ENUM_TO_ATOM_FUNCTION(button_to_atom, Uint8, BUTTON_ENUM) -NIF_FUNCTION(poll_event) +// poll_event + +NIF_CALL_HANDLER(thread_poll_event) { SDL_Event event; ERL_NIF_TERM map; @@ -177,3 +179,8 @@ NIF_FUNCTION(poll_event) return map; } + +NIF_FUNCTION(poll_event) +{ + return nif_thread_call(env, thread_poll_event, 0); +} diff --git a/c_src/sdl_renderer.c b/c_src/sdl_renderer.c index ae65436..a45b287 100644 --- a/c_src/sdl_renderer.c +++ b/c_src/sdl_renderer.c @@ -52,19 +52,14 @@ int map_to_rect(ErlNifEnv* env, ERL_NIF_TERM map, SDL_Rect* rect) return 1; } -NIF_FUNCTION(create_renderer) +// create_renderer + +NIF_CALL_HANDLER(thread_create_renderer) { - void* window_res; - int index; - Uint32 flags = 0; SDL_Renderer* renderer; ERL_NIF_TERM term; - BADARG_IF(!enif_get_resource(env, argv[0], res_Window, &window_res)); - BADARG_IF(!enif_get_int(env, argv[1], &index)); - BADARG_IF(!list_to_renderer_flags(env, argv[2], &flags)); - - renderer = SDL_CreateRenderer(NIF_RES_GET(Window, window_res), index, flags); + renderer = SDL_CreateRenderer(args[0], (long)args[1], (long)args[2]); if (!renderer) return sdl_error_tuple(env); @@ -76,13 +71,50 @@ NIF_FUNCTION(create_renderer) ); } +NIF_FUNCTION(create_renderer) +{ + void* window_res; + int index; + Uint32 flags = 0; + + BADARG_IF(!enif_get_resource(env, argv[0], res_Window, &window_res)); + BADARG_IF(!enif_get_int(env, argv[1], &index)); + BADARG_IF(!list_to_renderer_flags(env, argv[2], &flags)); + + return nif_thread_call(env, thread_create_renderer, 3, + NIF_RES_GET(Window, window_res), index, flags); +} + +// render_clear + +NIF_CALL_HANDLER(thread_render_clear) +{ + if (SDL_RenderClear(args[0])) + return sdl_error_tuple(env); + + return atom_ok; +} + NIF_FUNCTION(render_clear) { void* renderer_res; BADARG_IF(!enif_get_resource(env, argv[0], res_Renderer, &renderer_res)); - if (SDL_RenderClear(NIF_RES_GET(Renderer, renderer_res))) + return nif_thread_call(env, thread_render_clear, 1, + NIF_RES_GET(Renderer, renderer_res)); +} + +// render_copy + +NIF_CALL_HANDLER(thread_render_copy) +{ + int ret = SDL_RenderCopy(args[0], args[1], args[2], args[3]); + + enif_free(args[2]); + enif_free(args[3]); + + if (ret) return sdl_error_tuple(env); return atom_ok; @@ -92,7 +124,7 @@ NIF_FUNCTION(render_copy) { void* renderer_res; void* texture_res; - SDL_Rect src, *srcPtr, dst, *dstPtr; + SDL_Rect *srcPtr, *dstPtr; BADARG_IF(!enif_get_resource(env, argv[0], res_Renderer, &renderer_res)); BADARG_IF(!enif_get_resource(env, argv[1], res_Texture, &texture_res)); @@ -102,7 +134,7 @@ NIF_FUNCTION(render_copy) else { BADARG_IF(!enif_is_map(env, argv[2])); - srcPtr = &src; + srcPtr = (SDL_Rect*)enif_alloc(sizeof(SDL_Rect)); if (!map_to_rect(env, argv[2], srcPtr)) return enif_make_badarg(env); } @@ -112,24 +144,61 @@ NIF_FUNCTION(render_copy) else { BADARG_IF(!enif_is_map(env, argv[3])); - dstPtr = &dst; + dstPtr = (SDL_Rect*)enif_alloc(sizeof(SDL_Rect)); if (!map_to_rect(env, argv[3], dstPtr)) return enif_make_badarg(env); } - if (SDL_RenderCopy(NIF_RES_GET(Renderer, renderer_res), NIF_RES_GET(Texture, texture_res), srcPtr, dstPtr)) + return nif_thread_call(env, thread_render_copy, 4, + NIF_RES_GET(Renderer, renderer_res), NIF_RES_GET(Texture, texture_res), srcPtr, dstPtr); +} + +// render_present + +NIF_CAST_HANDLER(thread_render_present) +{ + SDL_RenderPresent(args[0]); +} + +NIF_FUNCTION(render_present) +{ + void* renderer_res; + + BADARG_IF(!enif_get_resource(env, argv[0], res_Renderer, &renderer_res)); + + return nif_thread_cast(env, thread_render_present, 1, + NIF_RES_GET(Renderer, renderer_res)); +} + +// render_set_logical_size + +NIF_CALL_HANDLER(thread_render_set_logical_size) +{ + if (SDL_RenderSetLogicalSize(args[0], (long)args[1], (long)args[2])) return sdl_error_tuple(env); return atom_ok; } -NIF_FUNCTION(render_present) +NIF_FUNCTION(render_set_logical_size) { void* renderer_res; + int w, h; BADARG_IF(!enif_get_resource(env, argv[0], res_Renderer, &renderer_res)); + BADARG_IF(!enif_get_int(env, argv[1], &w)); + BADARG_IF(!enif_get_int(env, argv[2], &h)); + + return nif_thread_call(env, thread_render_set_logical_size, 3, + NIF_RES_GET(Renderer, renderer_res), w, h); +} + +// set_render_draw_color - SDL_RenderPresent(NIF_RES_GET(Renderer, renderer_res)); +NIF_CALL_HANDLER(thread_set_render_draw_color) +{ + if (SDL_SetRenderDrawColor(args[0], (long)args[1], (long)args[2], (long)args[3], (long)args[4])) + return sdl_error_tuple(env); return atom_ok; } @@ -147,8 +216,6 @@ NIF_FUNCTION(set_render_draw_color) BADARG_IF(r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255); - if (SDL_SetRenderDrawColor(NIF_RES_GET(Renderer, renderer_res), r, g, b ,a)) - return sdl_error_tuple(env); - - return atom_ok; + return nif_thread_call(env, thread_set_render_draw_color, 5, + NIF_RES_GET(Renderer, renderer_res), r, g, b, a); } diff --git a/c_src/sdl_texture.c b/c_src/sdl_texture.c index daf5b93..4741bb6 100644 --- a/c_src/sdl_texture.c +++ b/c_src/sdl_texture.c @@ -19,17 +19,14 @@ void dtor_Texture(ErlNifEnv* env, void* obj) SDL_DestroyTexture(NIF_RES_GET(Texture, obj)); } -NIF_FUNCTION(create_texture_from_surface) +// create_texture_from_surface + +NIF_CALL_HANDLER(thread_create_texture_from_surface) { - void* renderer_res; - void* surface_res; SDL_Texture* texture; ERL_NIF_TERM term; - BADARG_IF(!enif_get_resource(env, argv[0], res_Renderer, &renderer_res)); - BADARG_IF(!enif_get_resource(env, argv[1], res_Surface, &surface_res)); - - texture = SDL_CreateTextureFromSurface(NIF_RES_GET(Renderer, renderer_res), NIF_RES_GET(Surface, surface_res)); + texture = SDL_CreateTextureFromSurface(args[0], args[1]); if (!texture) return sdl_error_tuple(env); @@ -40,3 +37,15 @@ NIF_FUNCTION(create_texture_from_surface) term ); } + +NIF_FUNCTION(create_texture_from_surface) +{ + void* renderer_res; + void* surface_res; + + BADARG_IF(!enif_get_resource(env, argv[0], res_Renderer, &renderer_res)); + BADARG_IF(!enif_get_resource(env, argv[1], res_Surface, &surface_res)); + + return nif_thread_call(env, thread_create_texture_from_surface, 2, + NIF_RES_GET(Renderer, renderer_res), NIF_RES_GET(Surface, surface_res)); +} diff --git a/c_src/sdl_window.c b/c_src/sdl_window.c index 0a130f3..9319270 100644 --- a/c_src/sdl_window.c +++ b/c_src/sdl_window.c @@ -37,22 +37,17 @@ void dtor_Window(ErlNifEnv* env, void* obj) NIF_LIST_TO_FLAGS_FUNCTION(list_to_window_flags, Uint32, WINDOW_FLAGS) -NIF_FUNCTION(create_window) +// create_window + +NIF_CALL_HANDLER(thread_create_window) { - char title[255]; - int x, y, w, h; - Uint32 flags = 0; SDL_Window* window; ERL_NIF_TERM term; - BADARG_IF(!enif_get_string(env, argv[0], title, 255, ERL_NIF_LATIN1)); - BADARG_IF(!enif_get_int(env, argv[1], &x)); - BADARG_IF(!enif_get_int(env, argv[2], &y)); - BADARG_IF(!enif_get_int(env, argv[3], &w)); - BADARG_IF(!enif_get_int(env, argv[4], &h)); - BADARG_IF(!list_to_window_flags(env, argv[5], &flags)); + window = SDL_CreateWindow(args[0], (long)args[1], (long)args[2], (long)args[3], (long)args[4], (long)args[5]); + + enif_free(args[0]); - window = SDL_CreateWindow(title, x, y, w, h, flags); if (!window) return sdl_error_tuple(env); @@ -63,3 +58,20 @@ NIF_FUNCTION(create_window) term ); } + +NIF_FUNCTION(create_window) +{ + char* title = (char*)enif_alloc(255); + int x, y, w, h; + Uint32 flags = 0; + + BADARG_IF(!enif_get_string(env, argv[0], title, 255, ERL_NIF_LATIN1)); + BADARG_IF(!enif_get_int(env, argv[1], &x)); + BADARG_IF(!enif_get_int(env, argv[2], &y)); + BADARG_IF(!enif_get_int(env, argv[3], &w)); + BADARG_IF(!enif_get_int(env, argv[4], &h)); + BADARG_IF(!list_to_window_flags(env, argv[5], &flags)); + + return nif_thread_call(env, thread_create_window, 6, + title, x, y, w, h, flags); +} |