exactness/src/bin/recorder.c

494 lines
15 KiB
C
Raw Normal View History

#define _GNU_SOURCE 1
#define EFL_EO_API_SUPPORT
#define EFL_BETA_API_SUPPORT
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sysinfo.h>
#include <dlfcn.h>
#include <Eina.h>
#include <Eo.h>
#include <Evas.h>
#include <Ecore.h>
#include <Ecore_File.h>
#include <Ecore_Con.h>
#include <Elementary.h>
#include "tsuite_file_data.h"
#include "exactness_private.h"
#define MAX_PATH 1024
#define SHOT_KEY_STR "F2"
#define SAVE_KEY_STR "F3"
static Evas *(*_evas_new)(void) = NULL;
static const char *_out_filename = NULL;
static const char *_test_name = NULL;
static int _verbose = 0;
static Eina_List *_evas_list = NULL;
static int _last_evas_id = 0;
static Lists_st *_events_list = NULL;
static char *_shot_key = NULL;
static void
_printf(int verbose, const char *fmt, ...)
{
va_list ap;
if (!_verbose || verbose > _verbose) return;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
static Tsuite_Event_Type
_event_pointer_type_get(Efl_Pointer_Action t)
{
switch(t)
{
case EFL_POINTER_ACTION_IN: return TSUITE_EVENT_MOUSE_IN;
case EFL_POINTER_ACTION_OUT: return TSUITE_EVENT_MOUSE_OUT;
case EFL_POINTER_ACTION_DOWN: return TSUITE_EVENT_MULTI_DOWN;
case EFL_POINTER_ACTION_UP: return TSUITE_EVENT_MULTI_UP;
case EFL_POINTER_ACTION_MOVE: return TSUITE_EVENT_MULTI_MOVE;
case EFL_POINTER_ACTION_WHEEL: return TSUITE_EVENT_MOUSE_WHEEL;
default:
return TSUITE_EVENT_NOT_SUPPORTED;
}
}
static const char *
_event_name_get(Tsuite_Event_Type type)
{
switch(type)
{
case TSUITE_EVENT_MOUSE_IN: return "Mouse In";
case TSUITE_EVENT_MOUSE_OUT: return "Mouse Out";
case TSUITE_EVENT_MOUSE_DOWN: return "Mouse Down";
case TSUITE_EVENT_MOUSE_UP: return "Mouse Up";
case TSUITE_EVENT_MOUSE_MOVE: return "Mouse Move";
case TSUITE_EVENT_MOUSE_WHEEL: return "Mouse Wheel";
case TSUITE_EVENT_MULTI_DOWN: return "Multi Down";
case TSUITE_EVENT_MULTI_UP: return "Multi Up";
case TSUITE_EVENT_MULTI_MOVE: return "Multi Move";
case TSUITE_EVENT_KEY_DOWN: return "Key Down";
case TSUITE_EVENT_KEY_UP: return "Key Up";
case TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE: return "Key Down with Keycode";
case TSUITE_EVENT_KEY_UP_WITH_KEYCODE: return "Key Up with Keycode";
case TSUITE_EVENT_TAKE_SHOT: return "Take shot";
default: return NULL;
}
}
static Eina_Bool
_is_hook_duplicate(const Variant_st *v, Tsuite_Event_Type ev_type, const void *info, int len)
{
if (v->t.type == tsuite_event_mapping_type_str_get(ev_type) &&
!memcmp(v->data, info, len)) return EINA_TRUE;
return EINA_FALSE;
}
2018-01-25 11:36:49 -08:00
static void
_output_write()
{
if (!strcmp(_out_filename + strlen(_out_filename) - 4,".exu"))
{
Exactness_Unit unit;
unit.imgs = NULL;
unit.events = _events_list->variant_list;
Eet_Data_Descriptor *unit_edd = unit_desc_make();
Eet_File *file = eet_open(_out_filename, EET_FILE_MODE_WRITE);
eet_data_write(file, unit_edd, CACHE_FILE_ENTRY, &unit, EINA_TRUE);
eet_close(file);
}
else if (!strcmp(_out_filename + strlen(_out_filename) - 4,".rec"))
{
write_events(_out_filename, _events_list);
}
}
/* Adding variant to list, this list is later written to EET file */
#define ADD_TO_LIST(EVT_TYPE, INFO) \
do { /* This macro will add event to EET data list */ \
if (_events_list) \
{ \
const Variant_st *prev_v = eina_list_last_data_get(_events_list->variant_list); \
if (!prev_v || !_is_hook_duplicate(prev_v, EVT_TYPE, &INFO, sizeof(INFO))) \
{ \
_printf(1, "Recording %s\n", tsuite_event_mapping_type_str_get(EVT_TYPE)); \
Variant_st *v = malloc(sizeof(Variant_st)); \
v->data = malloc(sizeof(INFO)); \
_variant_type_set(tsuite_event_mapping_type_str_get(EVT_TYPE), \
&v->t, EINA_FALSE); \
memcpy(v->data, &INFO, sizeof(INFO)); \
_events_list->variant_list = eina_list_append(_events_list->variant_list, v); \
} \
} \
} while (0)
static int
_evas_id_get(Evas *e)
{
return (intptr_t)efl_key_data_get(e, "__evas_id");
}
static void
_event_pointer_cb(void *data, const Efl_Event *event)
{
Eo *eo_e = data;
Eo *evp = event->info;
if (!evp) return;
int timestamp = efl_input_timestamp_get(evp);
int n_evas = _evas_id_get(eo_e);
Efl_Pointer_Action action = efl_input_pointer_action_get(evp);
Tsuite_Event_Type evt = _event_pointer_type_get(action);
if (!timestamp) return;
_printf(2, "Calling \"%s\" timestamp=<%u>\n", _event_name_get(evt), timestamp);
switch (action)
{
case EFL_POINTER_ACTION_MOVE:
{
double rad = 0, radx = 0, rady = 0, pres = 0, ang = 0, fx = 0, fy = 0;
int tool = efl_input_pointer_tool_get(evp);
Eina_Position2D pos = efl_input_pointer_position_get(evp);
multi_move t = { tool, pos.x, pos.y, rad, radx, rady, pres, ang, fx, fy, timestamp, n_evas };
if (t.n_evas >= 0) ADD_TO_LIST(evt, t);
break;
}
case EFL_POINTER_ACTION_DOWN: case EFL_POINTER_ACTION_UP:
{
double rad = 0, radx = 0, rady = 0, pres = 0, ang = 0, fx = 0, fy = 0;
int b = efl_input_pointer_button_get(evp);
int tool = efl_input_pointer_tool_get(evp);
Eina_Position2D pos = efl_input_pointer_position_get(evp);
Efl_Pointer_Flags flags = efl_input_pointer_button_flags_get(evp);
multi_event t = { tool, b, pos.x, pos.y, rad, radx, rady, pres, ang,
fx, fy, flags, timestamp, n_evas };
if (t.n_evas >= 0) ADD_TO_LIST(evt, t);
break;
}
case EFL_POINTER_ACTION_IN: case EFL_POINTER_ACTION_OUT:
{
mouse_in_mouse_out t = { timestamp, n_evas };
if (t.n_evas >= 0) ADD_TO_LIST(evt, t);
break;
}
case EFL_POINTER_ACTION_WHEEL:
{
Eina_Bool horiz = efl_input_pointer_wheel_horizontal_get(evp);
int z = efl_input_pointer_wheel_delta_get(evp);
mouse_wheel t = { horiz, z, timestamp, n_evas };
if (t.n_evas >= 0) ADD_TO_LIST(evt, t);
break;
}
default:
break;
}
}
static void
_event_key_cb(void *data, const Efl_Event *event)
{
Efl_Input_Key *evk = event->info;
Eo *eo_e = data;
if (!evk) return;
const char *key = efl_input_key_name_get(evk);
int timestamp = efl_input_timestamp_get(evk);
unsigned int n_evas = _evas_id_get(eo_e);
Tsuite_Event_Type evt = TSUITE_EVENT_KEY_UP_WITH_KEYCODE;
if (efl_input_key_pressed_get(evk))
{
if (!strcmp(key, _shot_key))
{
_printf(2, "Take Screenshot: %s timestamp=<%u>\n", __func__, timestamp);
take_screenshot t = { timestamp, n_evas };
if (t.n_evas >= 0) ADD_TO_LIST(TSUITE_EVENT_TAKE_SHOT, t);
return;
}
if (!strcmp(key, SAVE_KEY_STR))
{
if (_events_list)
2018-01-25 11:36:49 -08:00
_output_write();
_printf(2, "Save events: %s timestamp=<%u>\n", __func__, timestamp);
return;
}
evt = TSUITE_EVENT_KEY_DOWN_WITH_KEYCODE;
}
else
{
if (!strcmp(key, _shot_key) || !strcmp(key, SAVE_KEY_STR)) return;
}
if (_events_list)
{ /* Construct duplicate strings, free them when list if freed */
key_down_key_up_with_keycode t;
t.timestamp = timestamp;
t.keyname = eina_stringshare_add(key);
t.key = eina_stringshare_add(efl_input_key_get(evk));
t.string = eina_stringshare_add(efl_input_key_string_get(evk));
t.compose = eina_stringshare_add(efl_input_key_compose_get(evk));
t.keycode = efl_input_key_code_get(evk);
t.n_evas = n_evas;
if (t.n_evas >= 0) ADD_TO_LIST(evt, t);
}
}
// note: "hold" event comes from above (elm), not below (ecore)
EFL_CALLBACKS_ARRAY_DEFINE(_event_pointer_callbacks,
{ EFL_EVENT_POINTER_MOVE, _event_pointer_cb },
{ EFL_EVENT_POINTER_DOWN, _event_pointer_cb },
{ EFL_EVENT_POINTER_UP, _event_pointer_cb },
{ EFL_EVENT_POINTER_IN, _event_pointer_cb },
{ EFL_EVENT_POINTER_OUT, _event_pointer_cb },
{ EFL_EVENT_POINTER_WHEEL, _event_pointer_cb },
{ EFL_EVENT_FINGER_MOVE, _event_pointer_cb },
{ EFL_EVENT_FINGER_DOWN, _event_pointer_cb },
{ EFL_EVENT_FINGER_UP, _event_pointer_cb },
{ EFL_EVENT_KEY_DOWN, _event_key_cb },
{ EFL_EVENT_KEY_UP, _event_key_cb }
)
static Evas *
_my_evas_new(int w EINA_UNUSED, int h EINA_UNUSED)
{
Evas *e;
if (!_evas_new) return NULL;
e = _evas_new();
if (e)
{
_printf(1, "New Evas\n");
_evas_list = eina_list_append(_evas_list, e);
efl_key_data_set(e, "__evas_id", (void *)(intptr_t)_last_evas_id++);
efl_event_callback_array_add(e, _event_pointer_callbacks(), e);
}
return e;
}
static Eina_Bool
_prg_invoke(const char *full_path, int argc, char **argv)
{
Eina_Value *ret__;
int real__;
void (*efl_main)(void *data, const Efl_Event *ev);
int (*elm_main)(int argc, char **argv);
int (*c_main)(int argc, char **argv);
Eina_Module *h = eina_module_new(full_path);
if (!h || !eina_module_load(h))
{
fprintf(stderr, "Failed loading %s.\n", full_path);
if (h) eina_module_free(h);
return EINA_FALSE;
}
efl_main = eina_module_symbol_get(h, "efl_main");
elm_main = eina_module_symbol_get(h, "elm_main");
c_main = eina_module_symbol_get(h, "main");
_evas_new = eina_module_symbol_get(h, "evas_new");
if (!_evas_new)
{
fprintf(stderr, "Failed loading symbol 'evas_new' from %s.\n", full_path);
eina_module_free(h);
return 1;
}
if (efl_main)
{
elm_init(argc, argv);
efl_event_callback_add(efl_main_loop_get(), EFL_LOOP_EVENT_ARGUMENTS, efl_main, NULL);
ret__ = efl_loop_begin(efl_main_loop_get());
real__ = efl_loop_exit_code_process(ret__);
elm_shutdown();
}
else if (elm_main)
{
elm_init(argc, argv);
real__ = elm_main(argc, argv);
elm_shutdown();
}
else if (c_main)
{
real__ = c_main(argc, argv);
}
else
{
fprintf(stderr, "Failed loading symbol 'efl_main', 'elm_main' or 'main' from %s.\n", full_path);
eina_module_free(h);
real__ = 1;
}
return real__;
}
static Eina_Stringshare *
_prg_full_path_guess(const char *prg)
{
char full_path[MAX_PATH];
if (strchr(prg, '/')) return eina_stringshare_add(prg);
char *paths = strdup(getenv("PATH"));
Eina_Stringshare *ret = NULL;
while (paths && *paths && !ret)
{
char *real_path;
char *colon = strchr(paths, ':');
if (colon) *colon = '\0';
sprintf(full_path, "%s/%s", paths, prg);
real_path = ecore_file_realpath(full_path);
if (*real_path)
{
ret = eina_stringshare_add(real_path);
// check if executable
}
free(real_path);
paths += strlen(paths);
if (colon) paths++;
}
return ret;
}
static void
_print_usage(const char *progn, FILE *outf)
{
fprintf(outf, "Usage: %s [options] [program]\n", progn);
fprintf(outf, "Options:\n"
2018-01-25 11:36:49 -08:00
" -t file.rec|exu Name of the filename where to store the test\n"
" -v Verbose mode\n"
" -h Print this message and exit\n"
"\n");
}
static Eina_Bool
_mkdir(const char *dir)
{
if (!ecore_file_exists(dir))
{
const char *cur = dir + 1;
do
{
char *slash = strchr(cur, '/');
if (slash) *slash = '\0';
if (!ecore_file_exists(dir) && !ecore_file_mkdir(dir)) return EINA_FALSE;
if (slash) *slash = '/';
if (slash) cur = slash + 1;
else cur = NULL;
}
while (cur);
}
return EINA_TRUE;
}
int main(int argc, char **argv)
{
int pret = 1;
eina_init();
opterr = 0;
for (int opt; (opt = getopt(argc, argv, "+vt:h")) != -1;)
switch (opt)
{
case 0:
break;
case 't':
{
2018-01-25 11:36:49 -08:00
_out_filename = eina_stringshare_add(optarg);
break;
}
case 'v':
{
_verbose++;
break;
}
case 'h':
{
_print_usage(argv[0], stdout);
pret = 0;
goto end;
}
default:
{
_print_usage(argv[0], stderr);
goto end;
}
}
if (!argv[optind])
{
fprintf(stderr, "no program specified\nUse -h for more information\n");
goto end;
}
2018-01-25 11:36:49 -08:00
if (!_out_filename)
{
fprintf(stderr, "no test file specified\n");
goto end;
}
else
{
2018-01-25 11:36:49 -08:00
char *slash = strrchr(_out_filename, '/');
if (slash) _test_name = strdup(slash + 1);
2018-01-25 11:36:49 -08:00
else _test_name = strdup(_out_filename);
char *dot = strrchr(_test_name, '.');
if (dot) *dot = '\0';
if (slash)
{
*slash = '\0';
2018-01-25 11:36:49 -08:00
if (!_mkdir(_out_filename))
{
2018-01-25 11:36:49 -08:00
fprintf(stderr, "Can't create %s\n", _out_filename);
goto end;
}
*slash = '/';
}
}
efl_object_init();
evas_init();
/* Replace the current command line to hide the Exactness part */
int len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[optind];
memcpy(argv[0], argv[optind], len);
memset(argv[0] + len, 0, _POSIX_PATH_MAX - len);
for (int i = optind; i < argc; i++)
{
if (i != optind)
{
argv[i - optind] = argv[0] + (argv[i] - argv[optind]);
}
_printf(1, "%s ", argv[i - optind]);
}
_printf(1, "\n");
if (!_shot_key) _shot_key = getenv("TSUITE_SHOT_KEY");
if (!_shot_key) _shot_key = SHOT_KEY_STR;
if (!_events_list)
{
struct sysinfo s_info;
sysinfo(&s_info);
_events_list = calloc(1, sizeof(*_events_list));
_events_list->first_timestamp = s_info.uptime * 1000;
_printf(2, "Uptime=<%u>\n", _events_list->first_timestamp);
}
ecore_evas_callback_new_set(_my_evas_new);
_prg_invoke(_prg_full_path_guess(argv[0]), argc - optind, argv);
2018-01-25 11:36:49 -08:00
_output_write();
free_events(_events_list, EINA_TRUE);
_events_list = NULL;
end:
eina_shutdown();
return pret;
}