// Copyright (c) 2014-2018, 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 "esdl2.h"
#define EVENT_TYPE_ENUM(E) \
E(first, SDL_FIRSTEVENT) \
E(quit, SDL_QUIT) \
E(app_terminating, SDL_APP_TERMINATING) \
E(app_low_memory, SDL_APP_LOWMEMORY) \
E(app_will_enter_background, SDL_APP_WILLENTERBACKGROUND) \
E(app_did_enter_background, SDL_APP_DIDENTERBACKGROUND) \
E(app_will_enter_foreground, SDL_APP_WILLENTERFOREGROUND) \
E(app_did_enter_foreground, SDL_APP_DIDENTERFOREGROUND) \
E(window, SDL_WINDOWEVENT) \
E(syswm, SDL_SYSWMEVENT) \
E(key_down, SDL_KEYDOWN) \
E(key_up, SDL_KEYUP) \
E(text_editing, SDL_TEXTEDITING) \
E(text_input, SDL_TEXTINPUT) \
E(keymap_changed, SDL_KEYMAPCHANGED) \
E(mouse_motion, SDL_MOUSEMOTION) \
E(mouse_down, SDL_MOUSEBUTTONDOWN) \
E(mouse_up, SDL_MOUSEBUTTONUP) \
E(mouse_wheel, SDL_MOUSEWHEEL) \
E(joy_axis_motion, SDL_JOYAXISMOTION) \
E(joy_ball_motion, SDL_JOYBALLMOTION) \
E(joy_hat_motion, SDL_JOYHATMOTION) \
E(joy_button_down, SDL_JOYBUTTONDOWN) \
E(joy_button_up, SDL_JOYBUTTONUP) \
E(joy_device_added, SDL_JOYDEVICEADDED) \
E(joy_device_removed, SDL_JOYDEVICEREMOVED) \
E(controller_axis_motion, SDL_CONTROLLERAXISMOTION) \
E(controller_button_down, SDL_CONTROLLERBUTTONDOWN) \
E(controller_button_up, SDL_CONTROLLERBUTTONUP) \
E(controller_device_added, SDL_CONTROLLERDEVICEADDED) \
E(controller_device_removed, SDL_CONTROLLERDEVICEREMOVED) \
E(controller_device_remapped, SDL_CONTROLLERDEVICEREMAPPED) \
E(finger_down, SDL_FINGERDOWN) \
E(finger_up, SDL_FINGERUP) \
E(finger_motion, SDL_FINGERMOTION) \
E(dollar_gesture, SDL_DOLLARGESTURE) \
E(dollar_record, SDL_DOLLARRECORD) \
E(multi_gesture, SDL_MULTIGESTURE) \
E(clipboard_update, SDL_CLIPBOARDUPDATE) \
E(drop_file, SDL_DROPFILE) \
E(drop_text, SDL_DROPTEXT) \
E(drop_begin, SDL_DROPBEGIN) \
E(drop_complete, SDL_DROPCOMPLETE) \
E(audio_device_added, SDL_AUDIODEVICEADDED) \
E(audio_device_removed, SDL_AUDIODEVICEREMOVED) \
E(render_targets_reset, SDL_RENDER_TARGETS_RESET) \
E(render_device_reset, SDL_RENDER_DEVICE_RESET) \
E(last, SDL_LASTEVENT)
static NIF_ENUM_TO_ATOM_FUNCTION(event_type_to_atom, Uint32, EVENT_TYPE_ENUM)
static NIF_ATOM_TO_ENUM_FUNCTION(atom_to_event_type, Uint32, EVENT_TYPE_ENUM)
#define EVENT_ACTION_ENUM(E) \
E(add, SDL_ADDEVENT) \
E(peek, SDL_PEEKEVENT) \
E(get, SDL_GETEVENT)
static NIF_ATOM_TO_ENUM_FUNCTION(atom_to_event_action, SDL_eventaction, EVENT_ACTION_ENUM)
// Event conversion functions.
static ERL_NIF_TERM window_event_to_map(ErlNifEnv* env, SDL_Event* event, ERL_NIF_TERM map)
{
enif_make_map_put(env, map, atom_window_id,
enif_make_uint(env, event->window.windowID), &map);
enif_make_map_put(env, map, atom_event,
window_event_to_atom(event->window.event), &map);
enif_make_map_put(env, map, atom_data1,
enif_make_int(env, event->window.data1), &map);
enif_make_map_put(env, map, atom_data2,
enif_make_int(env, event->window.data2), &map);
return map;
}
static ERL_NIF_TERM keyboard_event_to_map(ErlNifEnv* env, SDL_Event* event, ERL_NIF_TERM map)
{
enif_make_map_put(env, map, atom_window_id,
enif_make_uint(env, event->key.windowID), &map);
enif_make_map_put(env, map, atom_state,
event->key.state == SDL_RELEASED ? atom_released : atom_pressed, &map);
enif_make_map_put(env, map, atom_repeat,
event->key.repeat == 0 ? atom_false : atom_true, &map);
enif_make_map_put(env, map, atom_scancode,
enif_make_uint(env, event->key.keysym.scancode), &map);
enif_make_map_put(env, map, atom_sym,
enif_make_int(env, event->key.keysym.sym), &map);
enif_make_map_put(env, map, atom_mod,
keymod_flags_to_list(env, event->key.keysym.mod), &map);
return map;
}
static ERL_NIF_TERM mouse_motion_event_to_map(ErlNifEnv* env, SDL_Event* event, ERL_NIF_TERM map)
{
enif_make_map_put(env, map, atom_window_id,
enif_make_uint(env, event->motion.windowID), &map);
enif_make_map_put(env, map, atom_which,
(event->motion.which == SDL_TOUCH_MOUSEID)
? atom_touch
: enif_make_uint(env, event->motion.which),
&map);
enif_make_map_put(env, map, atom_state,
mouse_state_to_list(env, event->motion.state), &map);
enif_make_map_put(env, map, atom_x,
enif_make_int(env, event->motion.x), &map);
enif_make_map_put(env, map, atom_y,
enif_make_int(env, event->motion.y), &map);
enif_make_map_put(env, map, atom_xrel,
enif_make_int(env, event->motion.xrel), &map);
enif_make_map_put(env, map, atom_yrel,
enif_make_int(env, event->motion.yrel), &map);
return map;
}
static ERL_NIF_TERM mouse_button_event_to_map(ErlNifEnv* env, SDL_Event* event, ERL_NIF_TERM map)
{
enif_make_map_put(env, map, atom_window_id,
enif_make_uint(env, event->button.windowID), &map);
enif_make_map_put(env, map, atom_which,
(event->button.which == SDL_TOUCH_MOUSEID)
? atom_touch
: enif_make_uint(env, event->button.which),
&map);
enif_make_map_put(env, map, atom_button,
button_to_atom(event->button.button), &map);
enif_make_map_put(env, map, atom_state,
event->button.state == SDL_RELEASED ? atom_released : atom_pressed, &map);
enif_make_map_put(env, map, atom_clicks,
enif_make_uint(env, event->button.clicks), &map);
enif_make_map_put(env, map, atom_x,
enif_make_int(env, event->button.x), &map);
enif_make_map_put(env, map, atom_y,
enif_make_int(env, event->button.y), &map);
return map;
}
static ERL_NIF_TERM mouse_wheel_event_to_map(ErlNifEnv* env, SDL_Event* event, ERL_NIF_TERM map)
{
enif_make_map_put(env, map, atom_window_id,
enif_make_uint(env, event->wheel.windowID), &map);
enif_make_map_put(env, map, atom_which,
(event->wheel.which == SDL_TOUCH_MOUSEID)
? atom_touch
: enif_make_uint(env, event->wheel.which),
&map);
enif_make_map_put(env, map, atom_x,
enif_make_int(env, event->wheel.x), &map);
enif_make_map_put(env, map, atom_y,
enif_make_int(env, event->wheel.y), &map);
enif_make_map_put(env, map, atom_direction,
mousewheel_direction_to_atom(event->wheel.direction), &map);
return map;
}
static ERL_NIF_TERM event_to_map(ErlNifEnv* env, SDL_Event* event)
{
ERL_NIF_TERM map;
map = enif_make_new_map(env);
// All events have a type and a timestamp.
enif_make_map_put(env, map, atom_type,
event_type_to_atom(event->type), &map);
enif_make_map_put(env, map, atom_timestamp,
enif_make_uint(env, event->common.timestamp), &map);
// The following event types have no additional fields:
//
// - SDL_QUIT
// - SDL_APP_TERMINATING
// - SDL_APP_LOWMEMORY
// - SDL_APP_WILLENTERBACKGROUND
// - SDL_APP_DIDENTERBACKGROUND
// - SDL_APP_WILLENTERFOREGROUND
// - SDL_APP_DIDENTERFOREGROUND
// - SDL_KEYMAPCHANGED
// - SDL_CLIPBOARDUPDATE
// - SDL_RENDER_TARGETS_RESET
// - SDL_RENDER_DEVICE_RESET
switch (event->type) {
case SDL_WINDOWEVENT:
return window_event_to_map(env, event, map);
// @todo SDL_SYSWMEVENT
case SDL_KEYDOWN:
case SDL_KEYUP:
return keyboard_event_to_map(env, event, map);
// @todo SDL_TEXTEDITING
// @todo SDL_TEXTINPUT
case SDL_MOUSEMOTION:
return mouse_motion_event_to_map(env, event, map);
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
return mouse_button_event_to_map(env, event, map);
case SDL_MOUSEWHEEL:
return mouse_wheel_event_to_map(env, event, map);
// @todo SDL_JOYAXISMOTION
// @todo SDL_JOYBALLMOTION
// @todo SDL_JOYHATMOTION
// @todo SDL_JOYBUTTONDOWN
// @todo SDL_JOYBUTTONUP
// @todo SDL_JOYDEVICEADDED
// @todo SDL_JOYDEVICEREMOVED
// @todo SDL_CONTROLLERAXISMOTION
// @todo SDL_CONTROLLERBUTTONDOWN
// @todo SDL_CONTROLLERBUTTONUP
// @todo SDL_CONTROLLERDEVICEADDED
// @todo SDL_CONTROLLERDEVICEREMOVED
// @todo SDL_CONTROLLERDEVICEREMAPPED
// @todo SDL_FINGERDOWN
// @todo SDL_FINGERUP
// @todo SDL_FINGERMOTION
// @todo SDL_DOLLARGESTURE
// @todo SDL_DOLLARRECORD
// @todo SDL_MULTIGESTURE
// @todo SDL_DROPFILE
// @todo SDL_DROPTEXT
// @todo SDL_DROPBEGIN
// @todo SDL_DROPCOMPLETE
// @todo SDL_AUDIODEVICEADDED
// @todo SDL_AUDIODEVICEREMOVED
}
return map;
}
// flush_event
NIF_CAST_HANDLER(thread_flush_event)
{
SDL_FlushEvent((long)args[0]);
}
NIF_FUNCTION(flush_event)
{
Uint32 type;
BADARG_IF(!atom_to_event_type(env, argv[0], &type));
return nif_thread_cast(env, thread_flush_event, 1, type);
}
// flush_events
NIF_CAST_HANDLER(thread_flush_events)
{
SDL_FlushEvents((long)args[0], (long)args[1]);
}
NIF_FUNCTION(flush_events)
{
Uint32 minType, maxType;
BADARG_IF(!atom_to_event_type(env, argv[0], &minType));
BADARG_IF(!atom_to_event_type(env, argv[1], &maxType));
return nif_thread_cast(env, thread_flush_events, 2, minType, maxType);
}
// has_event
NIF_CALL_HANDLER(thread_has_event)
{
if (SDL_HasEvent((long)args[0]))
return atom_true;
return atom_false;
}
NIF_FUNCTION(has_event)
{
Uint32 type;
BADARG_IF(!atom_to_event_type(env, argv[0], &type));
return nif_thread_call(env, thread_has_event, 1, type);
}
// has_events
NIF_CALL_HANDLER(thread_has_events)
{
if (SDL_HasEvents((long)args[0], (long)args[1]))
return atom_true;
return atom_false;
}
NIF_FUNCTION(has_events)
{
Uint32 minType, maxType;
BADARG_IF(!atom_to_event_type(env, argv[0], &minType));
BADARG_IF(!atom_to_event_type(env, argv[1], &maxType));
return nif_thread_call(env, thread_has_events, 2, minType, maxType);
}
// peep_events
//
// @todo It is not currently possible to add events at the back of the queue.
NIF_CALL_HANDLER(thread_peep_events)
{
SDL_Event* events;
int i, numEvents;
ERL_NIF_TERM list;
events = enif_alloc(sizeof(SDL_Event) * (long)args[1]);
numEvents = SDL_PeepEvents(events, (long)args[1],
(long)args[0], (long)args[2], (long)args[3]);
if (numEvents < 0)
return sdl_error_tuple(env);
list = enif_make_list(env, 0);
for (i = 0; i < numEvents; i++)
list = enif_make_list_cell(env, event_to_map(env, &events[i]), list);
enif_free(events);
return enif_make_tuple2(env, atom_ok, list);
}
NIF_FUNCTION(peep_events)
{
SDL_eventaction action;
int numEvents;
Uint32 minType, maxType;
BADARG_IF(enif_is_identical(atom_add, argv[0]));
BADARG_IF(!atom_to_event_action(env, argv[0], &action));
BADARG_IF(!enif_get_int(env, argv[1], &numEvents));
BADARG_IF(!atom_to_event_type(env, argv[2], &minType));
BADARG_IF(!atom_to_event_type(env, argv[3], &maxType));
return nif_thread_call(env, thread_peep_events, 4,
action, numEvents, minType, maxType);
}
// poll_event
NIF_CALL_HANDLER(thread_poll_event)
{
SDL_Event event;
if (SDL_PollEvent(&event) == 0)
return atom_false;
return event_to_map(env, &event);
}
NIF_FUNCTION(poll_event)
{
return nif_thread_call(env, thread_poll_event, 0);
}
// pump_events
NIF_CAST_HANDLER(thread_pump_events)
{
SDL_PumpEvents();
}
NIF_FUNCTION(pump_events)
{
return nif_thread_cast(env, thread_pump_events, 0);
}