aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2018-01-17 18:11:24 +0100
committerLoïc Hoguin <[email protected]>2018-01-17 18:12:23 +0100
commitd6de0fce0b46be3b382ba3c30fb7c9559f3fcb8e (patch)
tree9171aab855f013fd4cabe9e6838e76f92af8c8cf
parent13d8ea6917d584e44888d5202dd28ac5602b2836 (diff)
downloadesdl2-d6de0fce0b46be3b382ba3c30fb7c9559f3fcb8e.tar.gz
esdl2-d6de0fce0b46be3b382ba3c30fb7c9559f3fcb8e.tar.bz2
esdl2-d6de0fce0b46be3b382ba3c30fb7c9559f3fcb8e.zip
Add the SDL_SetWindowHitTest callback
-rw-r--r--README.asciidoc1
-rw-r--r--c_src/esdl2.h12
-rw-r--r--c_src/internal.c2
-rw-r--r--c_src/sdl_rect.c16
-rw-r--r--c_src/sdl_window.c223
-rw-r--r--src/esdl2.erl12
-rw-r--r--src/esdl2_callbacks.erl14
-rw-r--r--src/sdl_window.erl12
8 files changed, 287 insertions, 5 deletions
diff --git a/README.asciidoc b/README.asciidoc
index 64a9289..1ab6055 100644
--- a/README.asciidoc
+++ b/README.asciidoc
@@ -92,7 +92,6 @@ corresponding to the public headers.
** `SDL_GetWindowSurface` (window)
** `SDL_UpdateWindowSurface` (window)
** `SDL_UpdateWindowSurfaceRects` (window)
-** `SDL_SetWindowHitTest` and the related callback `SDL_HitTestResult` (unclear if we need it and whether we can make it efficient enough)
** `SDL_IsScreenSaverEnabled`
** `SDL_EnableScreenSaver`
** `SDL_DisableScreenSaver`
diff --git a/c_src/esdl2.h b/c_src/esdl2.h
index 8ab4e32..8c107f8 100644
--- a/c_src/esdl2.h
+++ b/c_src/esdl2.h
@@ -55,6 +55,8 @@
A(blend) \
A(borderless) \
A(bottom) \
+ A(bottom_left) \
+ A(bottom_right) \
A(button) \
A(callback) \
A(caps) \
@@ -77,6 +79,7 @@
A(direction) \
A(dollar_gesture) \
A(dollar_record) \
+ A(draggable) \
A(drop_begin) \
A(drop_complete) \
A(drop_file) \
@@ -202,6 +205,7 @@
A(right_gui) \
A(right_shift) \
A(scancode) \
+ A(set_window_hit_test_result) \
A(shown) \
A(size_all) \
A(size_changed) \
@@ -226,6 +230,8 @@
A(timestamp) \
A(tooltip) \
A(top) \
+ A(top_left) \
+ A(top_right) \
A(touch) \
A(true) \
A(type) \
@@ -450,6 +456,9 @@
F(set_window_fullscreen, 2) \
F(set_window_gamma_ramp, 4) \
F(set_window_grab, 2) \
+ F(set_window_hit_test, 3) \
+ F(set_window_hit_test_remove, 1) \
+ F(set_window_hit_test_result, 2) \
F(set_window_icon, 2) \
F(set_window_input_focus, 1) \
F(set_window_maximum_size, 3) \
@@ -485,8 +494,9 @@ NIF_LIST_TO_FLAGS_FUNCTION_DECL(keymod_list_to_flags, Uint16)
NIF_FLAGS_TO_LIST_FUNCTION_DECL(keymod_flags_to_list, Uint16)
int map_to_point(ErlNifEnv*, ERL_NIF_TERM, SDL_Point*);
+ERL_NIF_TERM point_to_map(ErlNifEnv*, const SDL_Point*);
int map_to_rect(ErlNifEnv*, ERL_NIF_TERM, SDL_Rect*);
-ERL_NIF_TERM rect_to_map(ErlNifEnv*, SDL_Rect*);
+ERL_NIF_TERM rect_to_map(ErlNifEnv*, const SDL_Rect*);
ERL_NIF_TERM display_mode_to_map(ErlNifEnv*, SDL_DisplayMode*);
ERL_NIF_TERM mouse_state_to_list(ErlNifEnv*, Uint32);
diff --git a/c_src/internal.c b/c_src/internal.c
index 462484f..10ca5a1 100644
--- a/c_src/internal.c
+++ b/c_src/internal.c
@@ -18,7 +18,7 @@
// register_callback_process
-ErlNifPid callback_process;
+static ErlNifPid callback_process;
NIF_FUNCTION(register_callback_process)
{
diff --git a/c_src/sdl_rect.c b/c_src/sdl_rect.c
index 737c920..472cf01 100644
--- a/c_src/sdl_rect.c
+++ b/c_src/sdl_rect.c
@@ -31,6 +31,20 @@ int map_to_point(ErlNifEnv* env, ERL_NIF_TERM map, SDL_Point* point)
return 1;
}
+ERL_NIF_TERM point_to_map(ErlNifEnv* env, const SDL_Point* point)
+{
+ ERL_NIF_TERM map;
+
+ map = enif_make_new_map(env);
+
+ enif_make_map_put(env, map, atom_x,
+ enif_make_int(env, point->x), &map);
+ enif_make_map_put(env, map, atom_y,
+ enif_make_int(env, point->y), &map);
+
+ return map;
+}
+
int map_to_rect(ErlNifEnv* env, ERL_NIF_TERM map, SDL_Rect* rect)
{
ERL_NIF_TERM x, y, w, h;
@@ -56,7 +70,7 @@ int map_to_rect(ErlNifEnv* env, ERL_NIF_TERM map, SDL_Rect* rect)
return 1;
}
-ERL_NIF_TERM rect_to_map(ErlNifEnv* env, SDL_Rect* rect)
+ERL_NIF_TERM rect_to_map(ErlNifEnv* env, const SDL_Rect* rect)
{
ERL_NIF_TERM map;
diff --git a/c_src/sdl_window.c b/c_src/sdl_window.c
index 8122479..6f910ed 100644
--- a/c_src/sdl_window.c
+++ b/c_src/sdl_window.c
@@ -82,6 +82,20 @@ NIF_ENUM_TO_ATOM_FUNCTION(window_event_to_atom, Uint8, WINDOW_EVENT_ENUM)
static NIF_ATOM_TO_ENUM_FUNCTION(atom_to_window_fullscreen, Uint32, WINDOW_FULLSCREEN_ENUM)
+#define HIT_TEST_ENUM(E) \
+ E(normal, SDL_HITTEST_NORMAL) \
+ E(draggable, SDL_HITTEST_DRAGGABLE) \
+ E(top_left, SDL_HITTEST_RESIZE_TOPLEFT) \
+ E(top, SDL_HITTEST_RESIZE_TOP) \
+ E(top_right, SDL_HITTEST_RESIZE_TOPRIGHT) \
+ E(right, SDL_HITTEST_RESIZE_RIGHT) \
+ E(bottom_right, SDL_HITTEST_RESIZE_BOTTOMRIGHT) \
+ E(bottom, SDL_HITTEST_RESIZE_BOTTOM) \
+ E(bottom_left, SDL_HITTEST_RESIZE_BOTTOMLEFT) \
+ E(left, SDL_HITTEST_RESIZE_LEFT)
+
+static NIF_ATOM_TO_ENUM_FUNCTION(atom_to_hit_test_result, SDL_HitTestResult, HIT_TEST_ENUM)
+
// create_window
NIF_CALL_HANDLER(thread_create_window)
@@ -835,6 +849,215 @@ NIF_FUNCTION(set_window_grab)
NIF_RES_GET(Window, window_res), b);
}
+// set_window_hit_test
+
+#define WAIT_FOR_RESULT -1
+#define NO_RESULT -2
+
+typedef struct hit_test_callback_data {
+ char module[256];
+ char function[256];
+ ErlNifMutex* lock;
+ ErlNifCond* cond;
+ int result; /* SDL_HitTestResult | WAIT_FOR_RESULT | NO_RESULT */
+} hit_test_callback_data;
+
+static SDL_HitTestResult hit_test_callback(SDL_Window* window, const SDL_Point* area, void* data)
+{
+ ERL_NIF_TERM module, function, window_term;
+ int result;
+ ErlNifEnv* env = enif_alloc_env();
+ hit_test_callback_data* callback = SDL_GetWindowData(window, "hit_test_callback");
+
+ enif_mutex_lock(callback->lock);
+
+ module = enif_make_atom(env, callback->module);
+ function = enif_make_atom(env, callback->function);
+
+ callback->result = WAIT_FOR_RESULT;
+
+ window_term = esdl2_windows_find(env, window);
+
+ // Tell our Erlang process to execute the callback.
+
+ enif_send(NULL, get_callback_process(), env,
+ enif_make_tuple6(env, atom_callback,
+ module,
+ function,
+ enif_make_list2(env, window_term, point_to_map(env, area)),
+ atom_set_window_hit_test_result,
+ enif_make_list1(env, window_term)
+ ));
+
+ enif_free_env(env);
+
+ // Then wait for the result.
+
+ while (callback->result == WAIT_FOR_RESULT)
+ enif_cond_wait(callback->cond, callback->lock);
+
+ result = callback->result;
+ callback->result = NO_RESULT;
+
+ enif_mutex_unlock(callback->lock);
+
+ return result;
+}
+
+NIF_CALL_HANDLER(thread_set_window_hit_test)
+{
+ hit_test_callback_data* callback = SDL_GetWindowData(args[0], "hit_test_callback");
+
+ // We need to copy the module/function because atoms are theoretically
+ // dependent on an environment. This is not the case today but might change.
+
+ if (callback) {
+ // We already have an SDL2 window hit test callback.
+ // Just update the Erlang module/function we need to call.
+
+ enif_mutex_lock(callback->lock);
+
+ strcpy(callback->module, args[1]),
+ strcpy(callback->function, args[2]),
+
+ enif_mutex_unlock(callback->lock);
+ } else {
+ // This is the first time this function is called. We need
+ // to create the initial callback along with its lock/cond.
+
+ callback = (hit_test_callback_data*)SDL_malloc(sizeof(hit_test_callback_data));
+
+ strcpy(callback->module, args[1]),
+ strcpy(callback->function, args[2]),
+
+ callback->lock = enif_mutex_create("hit_test_callback_lock");
+ callback->cond = enif_cond_create("hit_test_callback_cond");
+ callback->result = NO_RESULT;
+
+ SDL_SetWindowData(args[0], "hit_test_callback", callback);
+ }
+
+ enif_free(args[1]);
+ enif_free(args[2]);
+
+ if (SDL_SetWindowHitTest(args[0], &hit_test_callback, NULL)) {
+ SDL_SetWindowData(args[0], "hit_test_callback", NULL);
+ SDL_free(callback);
+
+ return sdl_error_tuple(env);
+ }
+
+ return atom_ok;
+}
+
+NIF_FUNCTION(set_window_hit_test)
+{
+ void* window_res;
+ unsigned int module_len, function_len;
+ char *module = NULL, *function = NULL;
+
+ BADARG_IF(!enif_get_resource(env, argv[0], res_Window, &window_res));
+ BADARG_IF(!enif_get_atom_length(env, argv[1], &module_len, ERL_NIF_LATIN1));
+ BADARG_IF(!enif_get_atom_length(env, argv[2], &function_len, ERL_NIF_LATIN1));
+
+ module = (char*)enif_alloc(module_len + 1);
+ if (!enif_get_atom(env, argv[1], module, module_len + 1, ERL_NIF_LATIN1))
+ goto set_window_hit_test_badarg;
+
+ function = (char*)enif_alloc(function_len + 1);
+ if (!enif_get_atom(env, argv[2], function, function_len + 1, ERL_NIF_LATIN1))
+ goto set_window_hit_test_badarg;
+
+ return nif_thread_call(env, thread_set_window_hit_test, 3,
+ NIF_RES_GET(Window, window_res), module, function);
+
+set_window_hit_test_badarg:
+ enif_free(module);
+ enif_free(function);
+
+ return enif_make_badarg(env);
+}
+
+// set_window_hit_test_remove
+
+NIF_CALL_HANDLER(thread_set_window_hit_test_remove)
+{
+ hit_test_callback_data* callback;
+
+ // We execute the function unconditionally even if we know
+ // there is no callback set in order to best reproduce the
+ // behavior when the feature is unsupported.
+
+ if (SDL_SetWindowHitTest(args[0], NULL, NULL))
+ return sdl_error_tuple(env);
+
+ callback = SDL_GetWindowData(args[0], "hit_test_callback");
+
+ if (!callback)
+ return atom_ok;
+
+ // The callback we removed but just in case we check that
+ // there is no ongoing callback running before freeing memory.
+
+ enif_mutex_lock(callback->lock);
+
+ while (callback->result != NO_RESULT)
+ enif_cond_wait(callback->cond, callback->lock);
+
+ enif_mutex_unlock(callback->lock);
+
+ SDL_SetWindowData(args[0], "hit_test_callback", NULL);
+ SDL_free(callback);
+
+ return atom_ok;
+}
+
+NIF_FUNCTION(set_window_hit_test_remove)
+{
+ void* window_res;
+
+ BADARG_IF(!enif_get_resource(env, argv[0], res_Window, &window_res));
+
+ return nif_thread_call(env, thread_set_window_hit_test_remove, 1,
+ NIF_RES_GET(Window, window_res));
+}
+
+// set_window_hit_test_result
+
+NIF_FUNCTION(set_window_hit_test_result)
+{
+ void* window_res;
+ hit_test_callback_data* callback;
+ SDL_HitTestResult result;
+
+ BADARG_IF(!enif_get_resource(env, argv[0], res_Window, &window_res));
+
+ callback = SDL_GetWindowData(NIF_RES_GET(Window, window_res), "hit_test_callback");
+
+ enif_mutex_lock(callback->lock);
+
+ if (!atom_to_hit_test_result(env, argv[1], &result)) {
+ // An exception occurred inside the callback function or
+ // the user returned an invalid value. Not much we can do.
+ // Act as if the callback was disabled and make the callback
+ // process crash loudly.
+
+ callback->result = SDL_HITTEST_NORMAL;
+
+ enif_cond_signal(callback->cond);
+ enif_mutex_unlock(callback->lock);
+
+ return enif_make_badarg(env);
+ }
+
+ callback->result = result;
+
+ enif_cond_signal(callback->cond);
+ enif_mutex_unlock(callback->lock);
+
+ return atom_ok;
+}
+
// set_window_icon
//
// We use a call here because we need the surface to exist until this call
diff --git a/src/esdl2.erl b/src/esdl2.erl
index 69885c6..edbb3b1 100644
--- a/src/esdl2.erl
+++ b/src/esdl2.erl
@@ -213,6 +213,9 @@
-export([set_window_fullscreen/2]).
-export([set_window_gamma_ramp/4]).
-export([set_window_grab/2]).
+-export([set_window_hit_test/3]).
+-export([set_window_hit_test_remove/1]).
+-export([set_window_hit_test_result/2]).
-export([set_window_icon/2]).
-export([set_window_input_focus/1]).
-export([set_window_maximum_size/3]).
@@ -757,6 +760,15 @@ set_window_gamma_ramp(_, _, _, _) ->
set_window_grab(_, _) ->
erlang:nif_error({not_loaded, ?MODULE}).
+set_window_hit_test(_, _, _) ->
+ erlang:nif_error({not_loaded, ?MODULE}).
+
+set_window_hit_test_remove(_) ->
+ erlang:nif_error({not_loaded, ?MODULE}).
+
+set_window_hit_test_result(_, _) ->
+ erlang:nif_error({not_loaded, ?MODULE}).
+
set_window_icon(_, _) ->
erlang:nif_error({not_loaded, ?MODULE}).
diff --git a/src/esdl2_callbacks.erl b/src/esdl2_callbacks.erl
index ccbe858..ecbc4a2 100644
--- a/src/esdl2_callbacks.erl
+++ b/src/esdl2_callbacks.erl
@@ -51,7 +51,19 @@ handle_info({callback, M, F, A}, State) ->
try
apply(M, F, A)
catch Class:Reason ->
- error_logger:error_msg("Exception ~p:~p with callback:~n{~p,~p,~p}~n", [Class, Reason, M, F, A])
+ error_logger:error_msg("Exception ~p:~p with callback:~n{~p,~p,~p}~n",
+ [Class, Reason, M, F, A])
+ end,
+ {noreply, State};
+handle_info({callback, M, F, A, ResF, ResA}, State) ->
+ try apply(M, F, A) of
+ Res ->
+ apply(esdl2, ResF, ResA ++ [Res])
+ catch Class:Reason ->
+ %% We need to inform the NIF that an error occurred.
+ apply(esdl2, ResF, ResA ++ [error]),
+ error_logger:error_msg("Exception ~p:~p with callback:~n{~p,~p,~p}~n",
+ [Class, Reason, M, F, A])
end,
{noreply, State};
handle_info(_Info, State) ->
diff --git a/src/sdl_window.erl b/src/sdl_window.erl
index 802e20c..24a4622 100644
--- a/src/sdl_window.erl
+++ b/src/sdl_window.erl
@@ -45,6 +45,7 @@
-export([set_display_mode/2]).
-export([set_fullscreen/2]).
-export([set_gamma_ramp/4]).
+-export([set_hit_test_callback/3]).
-export([set_icon/2]).
-export([set_max_size/3]).
-export([set_min_size/3]).
@@ -55,6 +56,7 @@
-export([set_size/3]).
-export([set_title/2]).
-export([show/1]).
+-export([unset_hit_test_callback/1]).
-opaque window() :: <<>>.
-export_type([window/0]).
@@ -237,6 +239,11 @@ set_gamma_ramp(Window, Red, Green, Blue) ->
esdl2:set_window_gamma_ramp(Window, Red, Green, Blue),
receive {'_nif_thread_ret_', Ret} -> Ret end.
+-spec set_hit_test_callback(window(), module(), atom()) -> ok | sdl:error().
+set_hit_test_callback(Window, Module, Function) ->
+ esdl2:set_window_hit_test(Window, Module, Function),
+ receive {'_nif_thread_ret_', Ret} -> Ret end.
+
-spec set_icon(window(), sdl_surface:surface()) -> ok.
set_icon(Window, Surface) ->
esdl2:set_window_icon(Window, Surface),
@@ -279,3 +286,8 @@ set_title(Window, Title) ->
-spec show(window()) -> ok.
show(Window) ->
esdl2:show_window(Window).
+
+-spec unset_hit_test_callback(window()) -> ok | sdl:error().
+unset_hit_test_callback(Window) ->
+ esdl2:set_window_hit_test_remove(Window),
+ receive {'_nif_thread_ret_', Ret} -> Ret end.