summaryrefslogtreecommitdiff
path: root/src/bin/exactness/recorder.c
diff options
context:
space:
mode:
authorDaniel Zaoui <daniel.zaoui@yahoo.com>2018-12-24 22:51:52 +0200
committerStefan Schmidt <s.schmidt@samsung.com>2020-02-07 11:31:13 +0100
commit50dbfcf31de264366cc6212f5af73084fa8a7f0e (patch)
tree0a72cb4bbf3de4b12576c2d0e7ea5aedff45dd75 /src/bin/exactness/recorder.c
parent78ad088dd73a18fb4cfcfedcc18a6bf03e28080b (diff)
exactness: import code from external repo into efl.git
Exactness has been developed in a separate git repo for many years. This finally moves it over into efl. Having it in tree allows us for easier testing with our current main target elementary_test and integration into our CI system (patches for this are work in progress already). We are only importing the lib and binary for test execution, not the full set of test data. This is would be over 500MB and thus it will stay in a different repo and only made available during the actual testing. [The original patch was made by Daniel Zaoui. Over the course of review and testing it got extended with build fixes for API changes and mingw compilation support from Stefan Schmidt and Michael Blumenkrantz] Reviewed-by: Mike Blumenkrantz <michael.blumenkrantz@gmail.com> Differential Revision: https://phab.enlightenment.org/D11285
Diffstat (limited to '')
-rw-r--r--src/bin/exactness/recorder.c531
1 files changed, 531 insertions, 0 deletions
diff --git a/src/bin/exactness/recorder.c b/src/bin/exactness/recorder.c
new file mode 100644
index 0000000000..11a2087d08
--- /dev/null
+++ b/src/bin/exactness/recorder.c
@@ -0,0 +1,531 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <getopt.h>
8#include <unistd.h>
9
10#include <sys/types.h>
11#ifdef HAVE_SYS_SYSINFO_H
12# include <sys/sysinfo.h>
13#endif
14
15#ifndef EFL_EO_API_SUPPORT
16#define EFL_EO_API_SUPPORT
17#endif
18#include <Eina.h>
19#include <Eo.h>
20#include <Evas.h>
21#include <Ecore.h>
22#include <Ecore_File.h>
23#include <Ecore_Getopt.h>
24#include <Ecore_Con.h>
25#include <Elementary.h>
26#include <Exactness.h>
27
28#include <exactness_private.h>
29
30#define MAX_PATH 1024
31#define STABILIZE_KEY_STR "F1"
32#define SHOT_KEY_STR "F2"
33#define SAVE_KEY_STR "F3"
34
35static Evas *(*_evas_new)(void) = NULL;
36static const char *_out_filename = NULL;
37static const char *_test_name = NULL;
38static int _verbose = 0;
39
40static Eina_List *_evas_list = NULL;
41static unsigned int _last_evas_id = 0;
42
43static Exactness_Unit *_unit = NULL;
44
45static char *_shot_key = NULL;
46static unsigned int _last_timestamp = 0.0;
47
48static void
49_printf(int verbose, const char *fmt, ...)
50{
51 va_list ap;
52 if (!_verbose || verbose > _verbose) return;
53
54 va_start(ap, fmt);
55 vprintf(fmt, ap);
56 va_end(ap);
57}
58
59static Exactness_Action_Type
60_event_pointer_type_get(Efl_Pointer_Action t)
61{
62 switch(t)
63 {
64 case EFL_POINTER_ACTION_IN: return EXACTNESS_ACTION_MOUSE_IN;
65 case EFL_POINTER_ACTION_OUT: return EXACTNESS_ACTION_MOUSE_OUT;
66 case EFL_POINTER_ACTION_DOWN: return EXACTNESS_ACTION_MULTI_DOWN;
67 case EFL_POINTER_ACTION_UP: return EXACTNESS_ACTION_MULTI_UP;
68 case EFL_POINTER_ACTION_MOVE: return EXACTNESS_ACTION_MULTI_MOVE;
69 case EFL_POINTER_ACTION_WHEEL: return EXACTNESS_ACTION_MOUSE_WHEEL;
70 default: return EXACTNESS_ACTION_UNKNOWN;
71 }
72}
73
74static void
75_output_write()
76{
77 if (_unit) exactness_unit_file_write(_unit, _out_filename);
78}
79
80static void
81_add_to_list(Exactness_Action_Type type, unsigned int n_evas, unsigned int timestamp, void *data, int len)
82{
83 if (_unit)
84 {
85 const Exactness_Action *prev_v = eina_list_last_data_get(_unit->actions);
86 if (prev_v)
87 {
88 if (prev_v->type == type &&
89 timestamp == _last_timestamp &&
90 prev_v->n_evas == n_evas &&
91 (!len || !memcmp(prev_v->data, data, len))) return;
92 }
93 _printf(1, "Recording %s\n", _exactness_action_type_to_string_get(type));
94 Exactness_Action *act = malloc(sizeof(*act));
95 act->type = type;
96 act->n_evas = n_evas;
97 act->delay_ms = timestamp - _last_timestamp;
98 _last_timestamp = timestamp;
99 if (len)
100 {
101 act->data = malloc(len);
102 memcpy(act->data, data, len);
103 }
104 _unit->actions = eina_list_append(_unit->actions, act);
105 }
106}
107
108static int
109_evas_id_get(Evas *e)
110{
111 return (intptr_t)efl_key_data_get(e, "__evas_id");
112}
113
114static void
115_event_pointer_cb(void *data, const Efl_Event *event)
116{
117 Eo *eo_e = data;
118 Eo *evp = event->info;
119 if (!evp) return;
120
121 int timestamp = efl_input_timestamp_get(evp);
122 int n_evas = _evas_id_get(eo_e);
123 Efl_Pointer_Action action = efl_input_pointer_action_get(evp);
124 Exactness_Action_Type evt = _event_pointer_type_get(action);
125
126 if (!timestamp) return;
127
128 _printf(2, "Calling \"%s\" timestamp=<%u>\n", _exactness_action_type_to_string_get(evt), timestamp);
129
130 switch (action)
131 {
132 case EFL_POINTER_ACTION_MOVE:
133 {
134 double rad = 0, radx = 0, rady = 0, pres = 0, ang = 0, fx = 0, fy = 0;
135 int tool = efl_input_pointer_touch_id_get(evp);
136 Eina_Position2D pos = efl_input_pointer_position_get(evp);
137 Exactness_Action_Multi_Move t = { tool, pos.x, pos.y, rad, radx, rady, pres, ang, fx, fy };
138 if (n_evas >= 0) _add_to_list(evt, n_evas, timestamp, &t, sizeof(t));
139 break;
140 }
141 case EFL_POINTER_ACTION_DOWN: case EFL_POINTER_ACTION_UP:
142 {
143 double rad = 0, radx = 0, rady = 0, pres = 0, ang = 0, fx = 0, fy = 0;
144 int b = efl_input_pointer_button_get(evp);
145 int tool = efl_input_pointer_touch_id_get(evp);
146 Eina_Position2D pos = efl_input_pointer_position_get(evp);
147 Efl_Pointer_Flags flags = efl_input_pointer_button_flags_get(evp);
148 Exactness_Action_Multi_Event t = { tool, b, pos.x, pos.y, rad, radx, rady, pres, ang,
149 fx, fy, flags };
150 if (n_evas >= 0) _add_to_list(evt, n_evas, timestamp, &t, sizeof(t));
151 break;
152 }
153 case EFL_POINTER_ACTION_IN: case EFL_POINTER_ACTION_OUT:
154 {
155 if (n_evas >= 0) _add_to_list(evt, n_evas, timestamp, NULL, 0);
156 break;
157 }
158 case EFL_POINTER_ACTION_WHEEL:
159 {
160 Eina_Bool horiz = efl_input_pointer_wheel_horizontal_get(evp);
161 int z = efl_input_pointer_wheel_delta_get(evp);
162 Exactness_Action_Mouse_Wheel t = { horiz, z };
163 if (n_evas >= 0) _add_to_list(evt, n_evas, timestamp, &t, sizeof(t));
164 break;
165 }
166 default:
167 break;
168 }
169}
170
171static void
172_event_key_cb(void *data, const Efl_Event *event)
173{
174 Efl_Input_Key *evk = event->info;
175 Eo *eo_e = data;
176 if (!evk) return;
177 const char *key = efl_input_key_name_get(evk);
178 int timestamp = efl_input_timestamp_get(evk);
179 unsigned int n_evas = _evas_id_get(eo_e);
180 Exactness_Action_Type evt = EXACTNESS_ACTION_KEY_UP;
181
182 if (efl_input_key_pressed_get(evk))
183 {
184 if (!strcmp(key, _shot_key))
185 {
186 _printf(2, "Take Screenshot: %s timestamp=<%u>\n", __func__, timestamp);
187 _add_to_list(EXACTNESS_ACTION_TAKE_SHOT, n_evas, timestamp, NULL, 0);
188 return;
189 }
190 if (!strcmp(key, STABILIZE_KEY_STR))
191 {
192 _printf(2, "Stabilize: %s timestamp=<%u>\n", __func__, timestamp);
193 _add_to_list(EXACTNESS_ACTION_STABILIZE, n_evas, timestamp, NULL, 0);
194 return;
195 }
196 if (!strcmp(key, SAVE_KEY_STR))
197 {
198 _output_write();
199 _printf(2, "Save events: %s timestamp=<%u>\n", __func__, timestamp);
200 return;
201 }
202 evt = EXACTNESS_ACTION_KEY_DOWN;
203 }
204 else
205 {
206 if (!strcmp(key, _shot_key) || !strcmp(key, SAVE_KEY_STR) || !strcmp(key, STABILIZE_KEY_STR)) return;
207 }
208 if (_unit)
209 { /* Construct duplicate strings, free them when list if freed */
210 Exactness_Action_Key_Down_Up t;
211 t.keyname = eina_stringshare_add(key);
212 t.key = eina_stringshare_add(efl_input_key_sym_get(evk));
213 t.string = eina_stringshare_add(efl_input_key_string_get(evk));
214 t.compose = eina_stringshare_add(efl_input_key_compose_string_get(evk));
215 t.keycode = efl_input_key_code_get(evk);
216 _add_to_list(evt, n_evas, timestamp, &t, sizeof(t));
217 }
218}
219
220// note: "hold" event comes from above (elm), not below (ecore)
221EFL_CALLBACKS_ARRAY_DEFINE(_event_pointer_callbacks,
222 { EFL_EVENT_POINTER_MOVE, _event_pointer_cb },
223 { EFL_EVENT_POINTER_DOWN, _event_pointer_cb },
224 { EFL_EVENT_POINTER_UP, _event_pointer_cb },
225 { EFL_EVENT_POINTER_IN, _event_pointer_cb },
226 { EFL_EVENT_POINTER_OUT, _event_pointer_cb },
227 { EFL_EVENT_POINTER_WHEEL, _event_pointer_cb },
228 { EFL_EVENT_FINGER_MOVE, _event_pointer_cb },
229 { EFL_EVENT_FINGER_DOWN, _event_pointer_cb },
230 { EFL_EVENT_FINGER_UP, _event_pointer_cb },
231 { EFL_EVENT_KEY_DOWN, _event_key_cb },
232 { EFL_EVENT_KEY_UP, _event_key_cb }
233 )
234
235static Evas *
236_my_evas_new(int w EINA_UNUSED, int h EINA_UNUSED)
237{
238 Evas *e;
239 if (!_evas_new) return NULL;
240 e = _evas_new();
241 if (e)
242 {
243 _printf(1, "New Evas\n");
244 _evas_list = eina_list_append(_evas_list, e);
245 efl_key_data_set(e, "__evas_id", (void *)(intptr_t)_last_evas_id++);
246 efl_event_callback_array_add(e, _event_pointer_callbacks(), e);
247 }
248 return e;
249}
250
251static int
252_prg_invoke(const char *full_path, int argc, char **argv)
253{
254 Eina_Value *ret__;
255 int real__;
256
257 void (*efl_main)(void *data, const Efl_Event *ev);
258 int (*elm_main)(int argc, char **argv);
259 int (*c_main)(int argc, char **argv);
260 Eina_Module *h = eina_module_new(full_path);
261 if (!h || !eina_module_load(h))
262 {
263 fprintf(stderr, "Failed loading %s.\n", full_path);
264 if (h) eina_module_free(h);
265 return EINA_FALSE;
266 }
267 efl_main = eina_module_symbol_get(h, "efl_main");
268 elm_main = eina_module_symbol_get(h, "elm_main");
269 c_main = eina_module_symbol_get(h, "main");
270 _evas_new = eina_module_symbol_get(h, "evas_new");
271 if (!_evas_new)
272 {
273 fprintf(stderr, "Failed loading symbol 'evas_new' from %s.\n", full_path);
274 eina_module_free(h);
275 return 1;
276 }
277 if (efl_main)
278 {
279 elm_init(argc, argv);
280 efl_event_callback_add(efl_main_loop_get(), EFL_LOOP_EVENT_ARGUMENTS, efl_main, NULL);
281 ret__ = efl_loop_begin(efl_main_loop_get());
282 real__ = efl_loop_exit_code_process(ret__);
283 elm_shutdown();
284 }
285 else if (elm_main)
286 {
287 elm_init(argc, argv);
288 real__ = elm_main(argc, argv);
289 elm_shutdown();
290 }
291 else if (c_main)
292 {
293 real__ = c_main(argc, argv);
294 }
295 else
296 {
297 fprintf(stderr, "Failed loading symbol 'efl_main', 'elm_main' or 'main' from %s.\n", full_path);
298 eina_module_free(h);
299 real__ = 1;
300 }
301 return real__;
302}
303
304static Eina_Stringshare *
305_prg_full_path_guess(const char *prg)
306{
307 char full_path[MAX_PATH];
308 if (strchr(prg, '/')) return eina_stringshare_add(prg);
309 char *paths = strdup(getenv("PATH"));
310 Eina_Stringshare *ret = NULL;
311 while (paths && *paths && !ret)
312 {
313 char *real_path;
314 char *colon = strchr(paths, ':');
315 if (colon) *colon = '\0';
316
317 sprintf(full_path, "%s/%s", paths, prg);
318 real_path = ecore_file_realpath(full_path);
319 if (*real_path)
320 {
321 ret = eina_stringshare_add(real_path);
322 // check if executable
323 }
324 free(real_path);
325
326 paths += strlen(paths);
327 if (colon) paths++;
328 }
329 return ret;
330}
331
332static Eina_Bool
333_mkdir(const char *dir)
334{
335 if (!ecore_file_exists(dir))
336 {
337 const char *cur = dir + 1;
338 do
339 {
340 char *slash = strchr(cur, '/');
341 if (slash) *slash = '\0';
342 if (!ecore_file_exists(dir) && !ecore_file_mkdir(dir)) return EINA_FALSE;
343 if (slash) *slash = '/';
344 if (slash) cur = slash + 1;
345 else cur = NULL;
346 }
347 while (cur);
348 }
349 return EINA_TRUE;
350}
351
352static const Ecore_Getopt optdesc = {
353 "exactness_record",
354 "%prog [options] <-v|-t|-h> command",
355 PACKAGE_VERSION,
356 "(C) 2017 Enlightenment",
357 "BSD",
358 "A scenario recorder for EFL based applications.\n"
359 "\tF1 - Request stabilization\n"
360 "\tF2 - Request shot\n"
361 "\tF3 - Request to save the scenario\n",
362 1,
363 {
364 ECORE_GETOPT_STORE_STR('t', "test", "Name of the filename where to store the test."),
365 ECORE_GETOPT_STORE_STR('f', "fonts-dir", "Specify a directory of the fonts that should be used."),
366 ECORE_GETOPT_COUNT('v', "verbose", "Turn verbose messages on."),
367
368 ECORE_GETOPT_LICENSE('L', "license"),
369 ECORE_GETOPT_COPYRIGHT('C', "copyright"),
370 ECORE_GETOPT_VERSION('V', "version"),
371 ECORE_GETOPT_HELP('h', "help"),
372 ECORE_GETOPT_SENTINEL
373 }
374};
375
376int main(int argc, char **argv)
377{
378 char *dest = NULL, *eq;
379 char *fonts_dir = NULL;
380 int pret = 1, opt_args = 0;
381 Eina_Bool want_quit = EINA_FALSE;
382
383 Ecore_Getopt_Value values[] = {
384 ECORE_GETOPT_VALUE_STR(dest),
385 ECORE_GETOPT_VALUE_STR(fonts_dir),
386 ECORE_GETOPT_VALUE_INT(_verbose),
387
388 ECORE_GETOPT_VALUE_BOOL(want_quit),
389 ECORE_GETOPT_VALUE_BOOL(want_quit),
390 ECORE_GETOPT_VALUE_BOOL(want_quit),
391 ECORE_GETOPT_VALUE_BOOL(want_quit),
392 ECORE_GETOPT_VALUE_NONE
393 };
394
395 eina_init();
396 ecore_init();
397
398 opt_args = ecore_getopt_parse(&optdesc, values, argc, argv);
399 if (opt_args < 0)
400 {
401 fprintf(stderr, "Failed parsing arguments.\n");
402 goto end;
403 }
404 if (want_quit) goto end;
405
406 /* Check for a sentinel */
407 if (argv[opt_args] && !strcmp(argv[opt_args], "--")) opt_args++;
408
409 /* Check for env variables */
410 do
411 {
412 eq = argv[opt_args] ? strchr(argv[opt_args], '=') : NULL;
413 if (eq)
414 {
415 char *var = malloc(eq - argv[opt_args] + 1);
416 memcpy(var, argv[opt_args], eq - argv[opt_args]);
417 var[eq - argv[opt_args]] = '\0';
418 setenv(var, eq + 1, 1);
419 opt_args++;
420 }
421 } while (eq);
422 _out_filename = eina_stringshare_add(dest);
423
424 if (!_out_filename)
425 {
426 fprintf(stderr, "no test file specified\n");
427 goto end;
428 }
429 else
430 {
431 char *slash = strrchr(_out_filename, '/');
432 if (slash) _test_name = strdup(slash + 1);
433 else _test_name = strdup(_out_filename);
434 char *dot = strrchr(_test_name, '.');
435 if (dot) *dot = '\0';
436 if (slash)
437 {
438 *slash = '\0';
439 if (!_mkdir(_out_filename))
440 {
441 fprintf(stderr, "Can't create %s\n", _out_filename);
442 goto end;
443 }
444 *slash = '/';
445 }
446 }
447 if (strcmp(_out_filename + strlen(_out_filename) - 4,".exu"))
448 {
449 fprintf(stderr, "A file with a exu extension is required - %s invalid\n", _out_filename);
450 goto end;
451 }
452
453 if (strcmp(_out_filename + strlen(_out_filename) - 4,".exu"))
454 {
455 fprintf(stderr, "A file with a exu extension is required - %s invalid\n", _out_filename);
456 goto end;
457 }
458
459 if (!argv[opt_args])
460 {
461 fprintf(stderr, "no program specified\nUse -h for more information\n");
462 goto end;
463 }
464
465 efl_object_init();
466 evas_init();
467
468 if (!_unit)
469 {
470 _unit = calloc(1, sizeof(*_unit));
471 }
472
473 if (fonts_dir)
474 {
475 Eina_Tmpstr *fonts_conf_name = NULL;
476 if (!ecore_file_exists(fonts_dir))
477 {
478 fprintf(stderr, "Unable to find fonts directory %s\n", fonts_dir);
479 goto end;
480 }
481 Eina_List *dated_fonts = ecore_file_ls(fonts_dir);
482 char *date_dir;
483 _unit->fonts_path = strdup(eina_list_last_data_get(dated_fonts));
484 EINA_LIST_FREE(dated_fonts, date_dir) free(date_dir);
485 if (_unit->fonts_path)
486 {
487 int tmp_fd = eina_file_mkstemp("/tmp/fonts_XXXXXX.conf", &fonts_conf_name);
488 FILE *tmp_f = fdopen(tmp_fd, "wb");
489 fprintf(tmp_f,
490 "<?xml version=\"1.0\"?>\n<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n<fontconfig>\n"
491 "<dir prefix=\"default\">%s/%s</dir>\n</fontconfig>\n",
492 fonts_dir, _unit->fonts_path);
493 fclose(tmp_f);
494 close(tmp_fd);
495
496 setenv("FONTCONFIG_FILE", fonts_conf_name, 1);
497 }
498 }
499
500 /* Replace the current command line to hide the Exactness part */
501 int len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[opt_args];
502 memcpy(argv[0], argv[opt_args], len);
503 memset(argv[0] + len, 0, MAX_PATH - len);
504
505 int i;
506 for (i = opt_args; i < argc; i++)
507 {
508 if (i != opt_args)
509 {
510 argv[i - opt_args] = argv[0] + (argv[i] - argv[opt_args]);
511 }
512 _printf(1, "%s ", argv[i - opt_args]);
513 }
514 _printf(1, "\n");
515
516 if (!_shot_key) _shot_key = getenv("SHOT_KEY");
517 if (!_shot_key) _shot_key = SHOT_KEY_STR;
518
519 ecore_evas_callback_new_set(_my_evas_new);
520 _last_timestamp = ecore_time_get() * 1000;
521 pret = _prg_invoke(_prg_full_path_guess(argv[0]), argc - opt_args, argv);
522
523 _output_write();
524 //free_events(_events_list, EINA_TRUE);
525 //_events_list = NULL;
526
527 pret = 0;
528end:
529 eina_shutdown();
530 return pret;
531}