From 9a8a6bf9b8414470952b008c84515b9516ddc7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 21 Dec 2017 23:32:18 +0100 Subject: Add sdl_cursor along with all its related functions --- README.md | 3 - c_src/esdl2.h | 15 +++ c_src/esdl2_cursors.c | 96 ++++++++++++++++++ c_src/esdl2_renderers.c | 4 +- c_src/esdl2_windows.c | 4 +- c_src/sdl_cursor.c | 262 ++++++++++++++++++++++++++++++++++++++++++++++++ c_src/sdl_mouse.c | 16 --- c_src/sdl_renderer.c | 2 +- ebin/esdl2.app | 2 +- src/esdl2.erl | 32 ++++++ src/sdl_cursor.erl | 80 +++++++++++++++ 11 files changed, 489 insertions(+), 27 deletions(-) create mode 100644 c_src/esdl2_cursors.c create mode 100644 c_src/sdl_cursor.c create mode 100644 src/sdl_cursor.erl diff --git a/README.md b/README.md index bb12b65..35e931a 100644 --- a/README.md +++ b/README.md @@ -228,9 +228,6 @@ The following tasks remain to be done: * http://hg.libsdl.org/SDL/file/default/include/SDL_scancode.h * We probably should make it easy to identify keycodes and scancodes. -* http://hg.libsdl.org/SDL/file/default/include/SDL_mouse.h - * We need to implement everything. - * http://hg.libsdl.org/SDL/file/default/include/SDL_joystick.h * We need to implement everything. diff --git a/c_src/esdl2.h b/c_src/esdl2.h index ca9a424..c265110 100644 --- a/c_src/esdl2.h +++ b/c_src/esdl2.h @@ -139,6 +139,7 @@ #define NIF_RES_TYPE(r) SDL_ ## r #define NIF_RESOURCES(R) \ + R(Cursor) \ R(GLContext) \ R(Renderer) \ R(Surface) \ @@ -176,6 +177,14 @@ F(has_sse3, 0) \ F(has_sse41, 0) \ F(has_sse42, 0) \ + /* sdl_cursor */ \ + F(create_cursor, 6) \ + F(create_color_cursor, 3) \ + F(create_system_cursor, 1) \ + F(get_cursor, 0) \ + F(get_default_cursor, 0) \ + F(set_cursor, 1) \ + F(show_cursor, 1) \ /* sdl_events */ \ F(poll_event, 0) \ /* sdl_filesystem */ \ @@ -302,6 +311,12 @@ ERL_NIF_TERM esdl2_renderers_find(ErlNifEnv*, SDL_Renderer*); void esdl2_renderers_remove(SDL_Renderer*); void esdl2_renderers_free(void); +void esdl2_cursors_init(void); +void esdl2_cursors_insert(SDL_Cursor*, obj_Cursor*); +ERL_NIF_TERM esdl2_cursors_find(ErlNifEnv*, SDL_Cursor*); +void esdl2_cursors_remove(SDL_Cursor*); +void esdl2_cursors_free(void); + ErlNifPid* get_callback_process(void); #define sdl_error_tuple(env) \ diff --git a/c_src/esdl2_cursors.c b/c_src/esdl2_cursors.c new file mode 100644 index 0000000..8ce576c --- /dev/null +++ b/c_src/esdl2_cursors.c @@ -0,0 +1,96 @@ +// Copyright (c) 2017, Loïc Hoguin +// +// 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 "esdl2.h" +#include + +struct esdl2_cursor { + LIST_ENTRY(esdl2_cursor) entries; + + SDL_Cursor* cursor; + obj_Cursor* res; +}; + +static LIST_HEAD(esdl2_cursor_list, esdl2_cursor) cursors; + +void esdl2_cursors_init() +{ + LIST_INIT(&cursors); +} + +void esdl2_cursors_insert(SDL_Cursor* cursor, obj_Cursor* res) +{ + struct esdl2_cursor* item; + + item = malloc(sizeof(struct esdl2_cursor)); + item->cursor = cursor; + item->res = res; + + LIST_INSERT_HEAD(&cursors, item, entries); +} + +static struct esdl2_cursor* esdl2_cursors_find_entry(SDL_Cursor* cursor) +{ + struct esdl2_cursor* head; + + head = LIST_FIRST(&cursors); + while (head != NULL) { + if (head->cursor == cursor) + return head; + + head = LIST_NEXT(head, entries); + } + + return NULL; +} + +ERL_NIF_TERM esdl2_cursors_find(ErlNifEnv* env, SDL_Cursor* cursor) +{ + struct esdl2_cursor* entry; + ERL_NIF_TERM term; + + entry = esdl2_cursors_find_entry(cursor); + + if (entry == NULL) + return atom_undefined; + + term = enif_make_resource(env, entry->res); + + return term; +} + +void esdl2_cursors_remove(SDL_Cursor* cursor) +{ + struct esdl2_cursor* entry; + + entry = esdl2_cursors_find_entry(cursor); + + if (entry == NULL) + return; + + LIST_REMOVE(entry, entries); +} + +void esdl2_cursors_free() +{ + struct esdl2_cursor *head, *next; + + head = LIST_FIRST(&cursors); + while (head != NULL) { + next = LIST_NEXT(head, entries); + free(head); + head = next; + } +} + diff --git a/c_src/esdl2_renderers.c b/c_src/esdl2_renderers.c index 281079c..70ef30d 100644 --- a/c_src/esdl2_renderers.c +++ b/c_src/esdl2_renderers.c @@ -44,9 +44,7 @@ void esdl2_renderers_insert(SDL_Renderer* renderer, obj_Renderer* res, obj_Windo LIST_INSERT_HEAD(&renderers, item, entries); } -struct esdl2_renderer* esdl2_renderers_find_entry(SDL_Renderer* renderer); - -struct esdl2_renderer* esdl2_renderers_find_entry(SDL_Renderer* renderer) +static struct esdl2_renderer* esdl2_renderers_find_entry(SDL_Renderer* renderer) { struct esdl2_renderer* head; diff --git a/c_src/esdl2_windows.c b/c_src/esdl2_windows.c index 8855987..bff9948 100644 --- a/c_src/esdl2_windows.c +++ b/c_src/esdl2_windows.c @@ -40,9 +40,7 @@ void esdl2_windows_insert(SDL_Window* window, obj_Window* res) LIST_INSERT_HEAD(&windows, item, entries); } -struct esdl2_window* esdl2_windows_find_entry(SDL_Window* window); - -struct esdl2_window* esdl2_windows_find_entry(SDL_Window* window) +static struct esdl2_window* esdl2_windows_find_entry(SDL_Window* window) { struct esdl2_window* head; diff --git a/c_src/sdl_cursor.c b/c_src/sdl_cursor.c new file mode 100644 index 0000000..59ad923 --- /dev/null +++ b/c_src/sdl_cursor.c @@ -0,0 +1,262 @@ +// Copyright (c) 2017, Loïc Hoguin +// +// 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 "esdl2.h" + +#define SYSTEM_CURSOR_ENUM(E) \ + E(arrow, SDL_SYSTEM_CURSOR_ARROW) \ + E(ibeam, SDL_SYSTEM_CURSOR_IBEAM) \ + E(wait, SDL_SYSTEM_CURSOR_WAIT) \ + E(crosshair, SDL_SYSTEM_CURSOR_CROSSHAIR) \ + E(wait_arrow, SDL_SYSTEM_CURSOR_WAITARROW) \ + E(size_nwse, SDL_SYSTEM_CURSOR_SIZENWSE) \ + E(size_nesw, SDL_SYSTEM_CURSOR_SIZENESW) \ + E(size_we, SDL_SYSTEM_CURSOR_SIZEWE) \ + E(size_ns, SDL_SYSTEM_CURSOR_SIZENS) \ + E(size_all, SDL_SYSTEM_CURSOR_SIZEALL) \ + E(no, SDL_SYSTEM_CURSOR_NO) \ + E(hand, SDL_SYSTEM_CURSOR_HAND) + +static NIF_ATOM_TO_ENUM_FUNCTION(atom_to_system_cursor, int, SYSTEM_CURSOR_ENUM) + +void dtor_Cursor(ErlNifEnv* env, void* obj) +{ + SDL_Cursor* cursor = NIF_RES_GET(Cursor, obj); + + SDL_FreeCursor(cursor); + esdl2_cursors_remove(cursor); +} + +// create_cursor + +NIF_CALL_HANDLER(thread_create_cursor) +{ + SDL_Cursor* cursor; + obj_Cursor* res; + ERL_NIF_TERM term; + ErlNifBinary data, mask; + + enif_inspect_binary(args[0], (long)args[1], &data); + enif_inspect_binary(args[0], (long)args[2], &mask); + + cursor = SDL_CreateCursor(data.data, mask.data, + (long)args[3], (long)args[4], (long)args[5], (long)args[6]); + + enif_free_env(args[0]); + + if (!cursor) + return sdl_error_tuple(env); + + NIF_RES_TO_PTR_AND_TERM(Cursor, cursor, res, term); + + esdl2_cursors_insert(cursor, res); + + return enif_make_tuple2(env, + atom_ok, + term + ); +} + +NIF_FUNCTION(create_cursor) +{ + ErlNifEnv* bin_env; + ERL_NIF_TERM data, mask; + int w, h, hot_x, hot_y; + + BADARG_IF(!enif_is_binary(env, argv[0])); + BADARG_IF(!enif_is_binary(env, argv[1])); + BADARG_IF(!enif_get_int(env, argv[2], &w)); + BADARG_IF(!enif_get_int(env, argv[3], &h)); + BADARG_IF(!enif_get_int(env, argv[4], &hot_x)); + BADARG_IF(!enif_get_int(env, argv[5], &hot_y)); + + // We copy the binaries to avoid copying their content. + bin_env = enif_alloc_env(); + data = enif_make_copy(bin_env, argv[0]); + mask = enif_make_copy(bin_env, argv[1]); + + return nif_thread_call(env, thread_create_cursor, 7, + bin_env, data, mask, w, h, hot_x, hot_y); +} + +// create_color_cursor + +NIF_CALL_HANDLER(thread_create_color_cursor) +{ + SDL_Cursor* cursor; + obj_Cursor* res; + ERL_NIF_TERM term; + + cursor = SDL_CreateColorCursor(args[0], (long)args[1], (long)args[2]); + + if (!cursor) + return sdl_error_tuple(env); + + NIF_RES_TO_PTR_AND_TERM(Cursor, cursor, res, term); + + esdl2_cursors_insert(cursor, res); + + return enif_make_tuple2(env, + atom_ok, + term + ); +} + +NIF_FUNCTION(create_color_cursor) +{ + void* surface_res; + int hot_x, hot_y; + + BADARG_IF(!enif_get_resource(env, argv[0], res_Surface, &surface_res)); + BADARG_IF(!enif_get_int(env, argv[1], &hot_x)); + BADARG_IF(!enif_get_int(env, argv[2], &hot_y)); + + return nif_thread_call(env, thread_create_color_cursor, 3, + NIF_RES_GET(Surface, surface_res), hot_x, hot_y); +} + +// create_system_cursor + +NIF_CALL_HANDLER(thread_create_system_cursor) +{ + SDL_Cursor* cursor; + obj_Cursor* res; + ERL_NIF_TERM term; + + cursor = SDL_CreateSystemCursor((long)args[0]); + + if (!cursor) + return sdl_error_tuple(env); + + NIF_RES_TO_PTR_AND_TERM(Cursor, cursor, res, term); + + esdl2_cursors_insert(cursor, res); + + return enif_make_tuple2(env, + atom_ok, + term + ); +} + +NIF_FUNCTION(create_system_cursor) +{ + int id; + + BADARG_IF(!atom_to_system_cursor(env, argv[0], &id)); + + return nif_thread_call(env, thread_create_system_cursor, 1, id); +} + +// get_cursor + +NIF_CALL_HANDLER(thread_get_cursor) +{ + SDL_Cursor* cursor; + obj_Cursor* res; + ERL_NIF_TERM term; + + cursor = SDL_GetCursor(); + + // There is no mouse. + if (!cursor) + return atom_undefined; + + term = esdl2_cursors_find(env, cursor); + + if (!enif_is_identical(term, atom_undefined)) + return term; + + // We don't know this cursor. It's probably a system cursor. + // We should insert it in the list to have the same reference + // for it while it's active. + + NIF_RES_TO_PTR_AND_TERM(Cursor, cursor, res, term); + + esdl2_cursors_insert(cursor, res); + + return term; +} + +NIF_FUNCTION(get_cursor) +{ + return nif_thread_call(env, thread_get_cursor, 0); +} + +// get_default_cursor + +NIF_CALL_HANDLER(thread_get_default_cursor) +{ + SDL_Cursor* cursor; + obj_Cursor* res; + ERL_NIF_TERM term; + + cursor = SDL_GetDefaultCursor(); + + // There is no mouse. + if (!cursor) + return atom_undefined; + + term = esdl2_cursors_find(env, cursor); + + if (!enif_is_identical(term, atom_undefined)) + return term; + + // We don't know this cursor. It's probably a system cursor. + // We should insert it in the list to have the same reference + // for it while it's active. + + NIF_RES_TO_PTR_AND_TERM(Cursor, cursor, res, term); + + esdl2_cursors_insert(cursor, res); + + return term; +} + +NIF_FUNCTION(get_default_cursor) +{ + return nif_thread_call(env, thread_get_default_cursor, 0); +} + +// set_cursor + +NIF_CAST_HANDLER(thread_set_cursor) +{ + SDL_SetCursor(args[0]); +} + +NIF_FUNCTION(set_cursor) +{ + void* cursor_res; + + BADARG_IF(!enif_get_resource(env, argv[0], res_Cursor, &cursor_res)); + + return nif_thread_cast(env, thread_set_cursor, 1, + NIF_RES_GET(Cursor, cursor_res)); +} + +// show_cursor + +NIF_CALL_HANDLER(thread_show_cursor) +{ + return enif_make_int(env, SDL_ShowCursor((long)args[0])); +} + +NIF_FUNCTION(show_cursor) +{ + int toggle; + + BADARG_IF(!enif_get_int(env, argv[0], &toggle)); + + return nif_thread_call(env, thread_show_cursor, 1, toggle); +} diff --git a/c_src/sdl_mouse.c b/c_src/sdl_mouse.c index b74d088..8b36131 100644 --- a/c_src/sdl_mouse.c +++ b/c_src/sdl_mouse.c @@ -14,22 +14,6 @@ #include "esdl2.h" -#define SYSTEM_CURSOR_ENUM(E) \ - E(arrow, SDL_SYSTEM_CURSOR_ARROW) \ - E(ibeam, SDL_SYSTEM_CURSOR_IBEAM) \ - E(wait, SDL_SYSTEM_CURSOR_WAIT) \ - E(crosshair, SDL_SYSTEM_CURSOR_CROSSHAIR) \ - E(wait_arrow, SDL_SYSTEM_CURSOR_WAITARROW) \ - E(size_nwse, SDL_SYSTEM_CURSOR_SIZENWSE) \ - E(size_nesw, SDL_SYSTEM_CURSOR_SIZENESW) \ - E(size_we, SDL_SYSTEM_CURSOR_SIZEWE) \ - E(size_ns, SDL_SYSTEM_CURSOR_SIZENS) \ - E(size_all, SDL_SYSTEM_CURSOR_SIZEALL) \ - E(no, SDL_SYSTEM_CURSOR_NO) \ - E(hand, SDL_SYSTEM_CURSOR_HAND) - -static NIF_ATOM_TO_ENUM_FUNCTION(atom_to_system_cursor, int, SYSTEM_CURSOR_ENUM) - #define MOUSEWHEEL_DIRECTION_ENUM(E) \ E(normal, SDL_MOUSEWHEEL_NORMAL) \ E(flipped, SDL_MOUSEWHEEL_FLIPPED) diff --git a/c_src/sdl_renderer.c b/c_src/sdl_renderer.c index 81e60c4..23b41ca 100644 --- a/c_src/sdl_renderer.c +++ b/c_src/sdl_renderer.c @@ -880,7 +880,7 @@ NIF_FUNCTION(render_set_viewport) NIF_RES_GET(Renderer, renderer_res), x, y, w, h); } -// get_window_grab +// render_target_supported NIF_CALL_HANDLER(thread_render_target_supported) { diff --git a/ebin/esdl2.app b/ebin/esdl2.app index 7439c29..92ddcc9 100644 --- a/ebin/esdl2.app +++ b/ebin/esdl2.app @@ -1,7 +1,7 @@ {application, 'esdl2', [ {description, "SDL2 Erlang NIF."}, {vsn, "0.1.0"}, - {modules, ['esdl2','esdl2_app','esdl2_callbacks','esdl2_sup','sdl','sdl_clipboard','sdl_cpu_info','sdl_events','sdl_filesystem','sdl_gl','sdl_hints','sdl_keyboard','sdl_mouse','sdl_power','sdl_renderer','sdl_surface','sdl_texture','sdl_version','sdl_window']}, + {modules, ['esdl2','esdl2_app','esdl2_callbacks','esdl2_sup','sdl','sdl_clipboard','sdl_cpu_info','sdl_cursor','sdl_events','sdl_filesystem','sdl_gl','sdl_hints','sdl_keyboard','sdl_mouse','sdl_power','sdl_renderer','sdl_surface','sdl_texture','sdl_version','sdl_window']}, {registered, [esdl2_sup]}, {applications, [kernel,stdlib]}, {mod, {esdl2_app, []}}, diff --git a/src/esdl2.erl b/src/esdl2.erl index 0b1b456..e18a7fc 100644 --- a/src/esdl2.erl +++ b/src/esdl2.erl @@ -45,6 +45,15 @@ -export([has_sse41/0]). -export([has_sse42/0]). +%% sdl_cursor +-export([create_cursor/6]). +-export([create_color_cursor/3]). +-export([create_system_cursor/1]). +-export([get_cursor/0]). +-export([get_default_cursor/0]). +-export([set_cursor/1]). +-export([show_cursor/1]). + %% sdl_events -export([poll_event/0]). @@ -243,6 +252,29 @@ has_sse41() -> has_sse42() -> erlang:nif_error({not_loaded, ?MODULE}). +%% sdl_cursor + +create_cursor(_, _, _, _, _, _) -> + erlang:nif_error({not_loaded, ?MODULE}). + +create_color_cursor(_, _, _) -> + erlang:nif_error({not_loaded, ?MODULE}). + +create_system_cursor(_) -> + erlang:nif_error({not_loaded, ?MODULE}). + +get_cursor() -> + erlang:nif_error({not_loaded, ?MODULE}). + +get_default_cursor() -> + erlang:nif_error({not_loaded, ?MODULE}). + +set_cursor(_) -> + erlang:nif_error({not_loaded, ?MODULE}). + +show_cursor(_) -> + erlang:nif_error({not_loaded, ?MODULE}). + %% sdl_events poll_event() -> diff --git a/src/sdl_cursor.erl b/src/sdl_cursor.erl new file mode 100644 index 0000000..373838d --- /dev/null +++ b/src/sdl_cursor.erl @@ -0,0 +1,80 @@ +%% Copyright (c) 2017, Loïc Hoguin +%% +%% 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. + +-module(sdl_cursor). + +-export([create/1]). +-export([create/3]). +-export([create/6]). +-export([get_current/0]). +-export([get_default/0]). +-export([hide/0]). +-export([is_visible/0]). +-export([set/1]). +-export([show/0]). + +-opaque cursor() :: <<>>. +-export_type([cursor/0]). + +-type system_cursor() :: arrow | ibeam | wait | crosshair | wait_arrow + | size_nwse | size_nesw | size_we | size_ns | size_all | no | hand. + +-spec create(system_cursor()) -> {ok, cursor()} | sdl:error(). +create(ID) -> + esdl2:create_system_cursor(ID), + receive {'_nif_thread_ret_', Ret} -> Ret end. + +-spec create(sdl_surface:surface(), integer(), integer()) + -> {ok, cursor()} | sdl:error(). +create(Surface, HotX, HotY) -> + esdl2:create_color_cursor(Surface, HotX, HotY), + receive {'_nif_thread_ret_', Ret} -> Ret end. + +-spec create(binary(), binary(), integer(), integer(), integer(), integer()) + -> {ok, cursor()} | sdl:error(). +create(Data, Mask, W, H, HotX, HotY) -> + esdl2:create_cursor(Data, Mask, W, H, HotX, HotY), + receive {'_nif_thread_ret_', Ret} -> Ret end. + +-spec get_current() -> cursor(). +get_current() -> + esdl2:get_cursor(), + receive {'_nif_thread_ret_', Ret} -> Ret end. + +-spec get_default() -> cursor(). +get_default() -> + esdl2:get_default_cursor(), + receive {'_nif_thread_ret_', Ret} -> Ret end. + +-spec hide() -> ok. +hide() -> + esdl2:show_cursor(0), + receive {'_nif_thread_ret_', _} -> ok end. + +-spec is_visible() -> ok. +is_visible() -> + esdl2:show_cursor(-1), + receive + {'_nif_thread_ret_', 0} -> false; + {'_nif_thread_ret_', 1} -> true + end. + +-spec set(cursor()) -> ok. +set(Cursor) -> + esdl2:set_cursor(Cursor). + +-spec show() -> ok. +show() -> + esdl2:show_cursor(1), + receive {'_nif_thread_ret_', _} -> ok end. -- cgit v1.2.3