Fix sign extension error
[platform/core/uifw/libtdm.git] / tools / tdm_test_client.c
1 /**************************************************************************
2  *
3  * libtdm
4  *
5  * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
6  *
7  * Contact: Eunchul Kim <chulspro.kim@samsung.com>,
8  *          JinYoung Jeon <jy0.jeon@samsung.com>,
9  *          Taeheon Kim <th908.kim@samsung.com>,
10  *          YoungJun Cho <yj44.cho@samsung.com>,
11  *          SooChan Lim <sc1.lim@samsung.com>,
12  *          Boram Park <boram1288.park@samsung.com>
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the
16  * "Software"), to deal in the Software without restriction, including
17  * without limitation the rights to use, copy, modify, merge, publish,
18  * distribute, sub license, and/or sell copies of the Software, and to
19  * permit persons to whom the Software is furnished to do so, subject to
20  * the following conditions:
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
29  * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
30  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33  *
34 **************************************************************************/
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <poll.h>
40 #include <errno.h>
41 #include <time.h>
42 #include <stdint.h>
43
44 #include "tdm_client.h"
45 #include "tdm_macro.h"
46 #include "buffers.h"
47
48 #define CHECK_V_STEP 0
49
50 typedef struct _tdm_test_client_arg {
51         char *output_name;
52         int fps;
53         int sync;
54         int interval;
55         int offset;
56         int enable_fake;
57         int pid;
58         char *vblank_name;
59 } tdm_test_client_arg;
60
61 typedef struct _tdm_test_client {
62         tdm_test_client_arg args;
63
64         int do_query;
65         int do_vblank;
66         int do_voutput;
67         int waiting;
68
69         tdm_client *client;
70         tdm_client_voutput *voutput;
71         tdm_client_output *output;
72 } tdm_test_client;
73
74 struct typestrings {
75         int type;
76         const char *string;
77 };
78
79 struct optstrings {
80         int  type;
81         const char *opt;
82         const char *desc;
83         const char *arg;
84         const char *ex;
85 };
86
87 enum {
88         OPT_QRY,
89         OPT_TST,
90         OPT_GNR,
91 };
92
93 static struct typestrings typestrs[] = {
94         {OPT_QRY, "Query"},
95         {OPT_TST, "Test"},
96         {OPT_GNR, "General"},
97 };
98
99 static struct optstrings optstrs[] = {
100         {OPT_QRY, "qo", "output objects info", "<output_name>", "primary"},
101         {OPT_TST, "v", "vblank test", "<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake][^vblank_name]", "primary,0@60~1+0*1^test"},
102         {OPT_TST, "V", "virtual output test", NULL, NULL},
103 };
104
105 static void
106 usage(char *app_name)
107 {
108         int type_size = sizeof(typestrs) / sizeof(struct typestrings);
109         int opt_size = sizeof(optstrs) / sizeof(struct optstrings);
110         int t;
111
112         printf("usage: %s \n\n", app_name);
113
114         for (t = 0; t < type_size; t++) {
115                 int o, f = 1;
116
117                 for (o = 0; o < opt_size; o++)
118                         if (optstrs[o].type == typestrs[t].type) {
119                                 if (f == 1)
120                                         printf(" %s options:\n\n", typestrs[t].string);
121                                 printf("\t-%s\t%s\n", optstrs[o].opt, optstrs[o].desc);
122                                 if (optstrs[o].arg)
123                                         printf("\t\t  %s\n", optstrs[o].arg);
124                                 if (optstrs[o].ex)
125                                         printf("\t\t  ex) %s\n", optstrs[o].ex);
126                                 f = 0;
127                         }
128                 printf("\n");
129         }
130
131         exit(0);
132 }
133
134 //"<output_name>"
135 static void
136 parse_arg_qo(tdm_test_client *data, char *arg)
137 {
138         char name[TDM_NAME_LEN];
139         strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
140         data->args.output_name = strndup(name, TDM_NAME_LEN);
141 }
142
143 //"<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake]"
144 static void
145 parse_arg_v(tdm_test_client *data, char *arg)
146 {
147         char *end = arg;
148         char name[TDM_NAME_LEN];
149
150         end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
151         data->args.output_name = strndup(name, TDM_NAME_LEN);
152
153         if (*end == ',') {
154                 arg = end + 1;
155                 data->args.sync = strtol(arg, &end, 10);
156         }
157
158         if (*end == '@') {
159                 arg = end + 1;
160                 data->args.fps = strtol(arg, &end, 10);
161         }
162
163         if (*end == '~') {
164                 arg = end + 1;
165                 data->args.interval = strtol(arg, &end, 10);
166         }
167
168         if (*end == '+' || *end == '-') {
169                 arg = end;
170                 data->args.offset = strtol(arg, &end, 10);
171         }
172
173         if (*end == '*') {
174                 arg = end + 1;
175                 data->args.enable_fake = strtol(arg, &end, 10);
176         }
177
178         if (*end == '^') {
179                 char name[TDM_NAME_LEN];
180                 arg = end + 1;
181                 end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
182                 data->args.vblank_name = strndup(name, TDM_NAME_LEN);
183         }
184 }
185
186 static void
187 parse_args(tdm_test_client *data, int argc, char *argv[])
188 {
189         int i;
190
191         if (argc < 2) {
192                 usage(argv[0]);
193                 exit(0);
194         }
195
196         memset(data, 0, sizeof *data);
197         data->args.interval = 1;
198
199         for (i = 1; i < argc; i++) {
200                 if (!strncmp(argv[i] + 1, "qo", 2)) {
201                         data->do_query = 1;
202                         parse_arg_qo(data, argv[++i]);
203                 } else if (!strncmp(argv[i] + 1, "v", 1)) {
204                         data->do_vblank = 1;
205                         parse_arg_v(data, argv[++i]);
206                 } else if (!strncmp(argv[i] + 1, "V", 1)) {
207                         data->do_voutput = 1;
208                 } else {
209                         usage(argv[0]);
210                         exit(0);
211                 }
212         }
213 }
214
215 static double
216 get_time(void)
217 {
218         struct timespec tp;
219
220         if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
221                 return (double)tp.tv_sec + ((double)tp.tv_nsec) / 1000000000.0;
222
223         return 0;
224 }
225
226 static void
227 _client_vblank_handler(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence,
228                                            unsigned int tv_sec, unsigned int tv_usec, void *user_data)
229 {
230         tdm_test_client *data = user_data;
231         double cur, vbl;
232         static double p_vbl = 0;
233
234         data->waiting = 0;
235
236         if (error == TDM_ERROR_DPMS_OFF) {
237                 printf("exit: dpms off\n");
238                 exit(0);
239         }
240
241         if (error != TDM_ERROR_NONE) {
242                 printf("exit: error(%d)\n", error);
243                 exit(0);
244         }
245
246         cur = get_time();
247         vbl = (double)tv_sec + ((double)tv_usec) / 1000000.0;
248
249         printf("vblank              : %.6f us vbl(%.6f)\n", vbl - p_vbl, vbl);
250
251         if (cur - vbl > 0.002) /* 2ms */
252                 printf("kernel -> tdm-client: %.0f us\n", (cur - vbl) * 1000000.0);
253
254         p_vbl = vbl;
255 }
256
257 static char *conn_str[3] = {"disconnected", "connected", "mode_setted"};
258 static char *dpms_str[4] = {"on", "standy", "suspend", "off"};
259
260 static void
261 _client_output_handler(tdm_client_output *output, tdm_output_change_type type,
262                                            tdm_value value, void *user_data)
263 {
264         if (type == TDM_OUTPUT_CHANGE_CONNECTION)
265                 printf("output %s.\n", conn_str[value.u32]);
266         else if (type == TDM_OUTPUT_CHANGE_DPMS)
267                 printf("dpms %s.\n", dpms_str[value.u32]);
268 }
269
270 static void
271 do_query(tdm_test_client *data)
272 {
273         tdm_client_output *output;
274         tdm_output_conn_status status;
275         tdm_output_dpms dpms;
276         unsigned int refresh;
277         tdm_error error;
278
279         output = tdm_client_get_output(data->client, NULL, &error);
280         if (error != TDM_ERROR_NONE) {
281                 printf("tdm_client_get_output failed\n");
282                 return;
283         }
284
285         error = tdm_client_output_get_conn_status(output, &status);
286         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
287         error = tdm_client_output_get_dpms(output, &dpms);
288         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
289         error = tdm_client_output_get_refresh_rate(output, &refresh);
290         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
291
292         printf("tdm_output \"%s\"\n", data->args.output_name);
293         printf("\tstatus : %s\n", conn_str[status]);
294         printf("\tdpms : %s\n", dpms_str[dpms]);
295         printf("\trefresh : %d\n", refresh);
296 }
297
298 static void
299 do_vblank(tdm_test_client *data)
300 {
301         tdm_client_output *output;
302         tdm_client_vblank *vblank = NULL;
303         tdm_error error;
304         int fd = -1;
305         struct pollfd fds;
306
307         output = tdm_client_get_output(data->client, data->args.output_name, &error);
308         if (error != TDM_ERROR_NONE) {
309                 printf("tdm_client_get_output failed\n");
310                 return;
311         }
312
313         error = tdm_client_output_add_change_handler(output, _client_output_handler, NULL);
314         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
315
316         vblank = tdm_client_output_create_vblank(output, &error);
317         if (error != TDM_ERROR_NONE) {
318                 printf("tdm_client_output_create_vblank failed\n");
319                 return;
320         }
321
322         error = tdm_client_vblank_set_name(vblank, data->args.vblank_name);
323         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
324         error = tdm_client_vblank_set_enable_fake(vblank, data->args.enable_fake);
325         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
326         error = tdm_client_vblank_set_sync(vblank, data->args.sync);
327         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
328         if (data->args.fps > 0) {
329                 error = tdm_client_vblank_set_fps(vblank, data->args.fps);
330                 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
331         }
332         error = tdm_client_vblank_set_offset(vblank, data->args.offset);
333         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
334
335         error = tdm_client_get_fd(data->client, &fd);
336         if (error != TDM_ERROR_NONE || fd < 0) {
337                 printf("tdm_client_get_fd failed\n");
338                 goto done;
339         }
340
341         fds.events = POLLIN;
342         fds.fd = fd;
343         fds.revents = 0;
344
345         while (1) {
346                 int ret;
347
348                 if (!data->waiting) {
349                         error = tdm_client_vblank_wait(vblank, data->args.interval,
350                                                                                    _client_vblank_handler, data);
351                         if (error == TDM_ERROR_DPMS_OFF) {
352                                 printf("tdm_client_vblank_wait failed (dpms off)\n");
353                                 goto done;
354                         }
355                         if (error != TDM_ERROR_NONE) {
356                                 printf("tdm_client_vblank_wait failed (error: %d)\n", error);
357                                 goto done;
358                         }
359                         data->waiting = 1;
360                 }
361
362                 if (!data->args.sync) {
363                         ret = poll(&fds, 1, -1);
364                         if (ret < 0) {
365                                 if (errno == EINTR || errno == EAGAIN)  /* normal case */
366                                         continue;
367                                 else {
368                                         printf("poll failed: %m\n");
369                                         goto done;
370                                 }
371                         }
372
373                         error = tdm_client_handle_events(data->client);
374                         if (error != TDM_ERROR_NONE) {
375                                 printf("tdm_client_handle_events failed\n");
376                                 goto done;
377                         }
378                 }
379         }
380
381 done:
382         if (vblank)
383                 tdm_client_vblank_destroy(vblank);
384 }
385
386 static void
387 _dump_buffer(tbm_surface_h buffer, int count)
388 {
389         char temp[TDM_PATH_LEN] = {0,};
390         tbm_format tformat;
391         const char *ext, *file_exts[2] = {"png", "yuv"};
392
393         tformat = tbm_surface_get_format(buffer);
394
395         if (IS_RGB(tformat))
396                 ext = file_exts[0];
397         else
398                 ext = file_exts[1];
399
400         snprintf(temp, TDM_PATH_LEN, "%c%c%c%c_%dx%d_%d",
401                 FOURCC_STR(tbm_surface_get_format(buffer)),
402                 tbm_surface_get_width(buffer),
403                 tbm_surface_get_height(buffer),
404                 count);
405         tbm_surface_internal_capture_buffer(buffer, "/tmp", temp, ext);
406 }
407
408 static void
409 _voutput_commit(tdm_client_voutput *voutput, tbm_surface_h buffer, void *user_data)
410 {
411         tdm_test_client *data = (tdm_test_client *)user_data;
412         static int count = 0;
413
414         TDM_EXIT_IF_FAIL(data != NULL);
415         TDM_EXIT_IF_FAIL(buffer != NULL);
416
417         if ((count < 10) || (count >= 31 && count <= 40))
418                 _dump_buffer(buffer, count);
419         count++;
420
421         if (count == 30) {
422                 printf("client: %d commited(%p), mode change request to index 1\n", count, buffer);
423                 tdm_client_voutput_set_mode(data->voutput, 1);
424         } else if (count == 50) {
425                 printf("client: %d commited(%p), disconnect\n", count, buffer);
426                 tdm_client_voutput_disconnect(data->voutput);
427         } else {
428                 printf("client: %d commited(%p)\n", count, buffer);
429         }
430
431         tdm_client_voutput_commit_done(voutput);
432 }
433
434 static void
435 _voutput_output_handler(tdm_client_output *output, tdm_output_change_type type,
436                                            tdm_value value, void *user_data)
437 {
438         tdm_client_voutput *voutput = NULL;
439         tdm_output_conn_status status;
440         tdm_test_client *data;
441         unsigned int width, height;
442
443         data = (tdm_test_client *) user_data;
444         TDM_RETURN_IF_FAIL(data != NULL);
445         voutput = data->voutput;
446         TDM_RETURN_IF_FAIL(voutput != NULL);
447
448         if (type == TDM_OUTPUT_CHANGE_CONNECTION) {
449                 status = (tdm_output_conn_status)value.u32;
450                 if (value.u32 < 3)
451                         printf("output %s.\n", conn_str[value.u32]);
452                 else
453                         printf("output unkown.\n");
454
455                 if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
456                         printf("client: disconnected, destroy voutput\n");
457                         tdm_client_output_remove_change_handler(output, _voutput_output_handler, data);
458 #if CHECK_V_STEP
459                         printf("press enter to continuet\n");
460                         getchar();
461 #endif
462                         tdm_client_voutput_destroy(voutput);
463                 } else if (status == TDM_OUTPUT_CONN_STATUS_CONNECTED) {
464                         printf("client: connected\n");
465                 } else if (status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) {
466                         tdm_client_output_get_mode(output, &width, &height);
467                         printf("client: mode setted(%dx%d)\n", width, height);
468 #if CHECK_V_STEP
469                         printf("press enter to continuet\n");
470                         getchar();
471 #endif
472                 }
473         } else if (type == TDM_OUTPUT_CHANGE_DPMS) {
474                 printf("output %s.\n", dpms_str[value.u32]);
475         }
476 }
477
478 static void
479 _voutput_make_available_mode(tdm_client_output_mode *modes, int count)
480 {
481         int i;
482         for (i = 0 ; i < count; i++) {
483                 modes[i].clock = 25200;
484                 modes[i].hdisplay = 640 * (count - i);
485                 modes[i].hsync_start = 656;
486                 modes[i].hsync_end = 752;
487                 modes[i].htotal = 800;
488                 modes[i].hskew = 0;
489                 modes[i].vdisplay = 480 * (count - i);
490                 modes[i].vsync_start = 490;
491                 modes[i].vsync_end = 492;
492                 modes[i].vtotal = 525;
493                 modes[i].vscan = 0;
494                 modes[i].vrefresh = 30;
495                 modes[i].flags = 0;
496                 modes[i].type = 0;
497                 snprintf(modes[i].name, TDM_NAME_LEN, "%dx%d_%d", modes[i].hdisplay, modes[i].vdisplay, i);
498         }
499 }
500
501 static void
502 do_voutput(tdm_test_client *data)
503 {
504         tdm_client_voutput *voutput = NULL;
505         tdm_client_output *output = NULL;
506         tdm_client_output_mode modes[2];
507         tdm_error ret = TDM_ERROR_NONE;
508
509         printf("virtual output test - client\n");
510
511         voutput = tdm_client_create_voutput(data->client, "virtual-test", &ret);
512         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
513
514         ret = tdm_client_voutput_add_commit_handler(voutput, _voutput_commit, data);
515         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
516
517         output = tdm_client_voutput_get_client_output(voutput, &ret);
518         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
519
520         ret = tdm_client_output_add_change_handler(output, _voutput_output_handler, data);
521         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
522
523         ret = tdm_client_voutput_set_physical_size(voutput, 300, 200);
524         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
525
526         _voutput_make_available_mode(modes, 2);
527         ret = tdm_client_voutput_set_available_modes(voutput, modes, 2);
528         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
529 #if CHECK_V_STEP
530         printf("virtual output test - press enter to connect\n");
531         getchar();
532 #endif
533         ret = tdm_client_voutput_connect(voutput);
534         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
535
536         data->voutput = voutput;
537         data->output = output;
538
539         while (1) {
540                 tdm_client_handle_events_timeout(data->client, 1000);
541         }
542
543 done:
544         if (voutput)
545                 tdm_client_voutput_destroy(voutput);
546 }
547
548 static tdm_test_client ttc_data;
549
550 int
551 main(int argc, char *argv[])
552 {
553         tdm_test_client *data = &ttc_data;
554         tdm_error error;
555
556         /* for testing */
557         const char *xdg = (const char*)getenv("XDG_RUNTIME_DIR");
558         if (!xdg) {
559                 char buf[32];
560                 snprintf(buf, sizeof(buf), "/run");
561                 int ret = setenv("XDG_RUNTIME_DIR", (const char*)buf, 1);
562                 if (ret != 0)
563                         exit(0);
564         }
565
566         /* for tbm_bufmgr_init */
567         const char *s  = (const char*)getenv("TBM_DISPLAY_SERVER");
568         if (!s) {
569                 char buf[32];
570                 snprintf(buf, sizeof(buf), "1");
571                 int ret = setenv("TBM_DISPLAY_SERVER", (const char*)buf, 1);
572                 if (ret != 0)
573                         exit(0);
574         }
575
576         parse_args(data, argc, argv);
577
578         printf("sync(%d) fps(%d) interval(%d) offset(%d) enable_fake(%d) pid(%d)\n",
579                    data->args.sync, data->args.fps, data->args.interval,
580                    data->args.offset, data->args.enable_fake, data->args.pid);
581
582         data->client = tdm_client_create(&error);
583         if (error != TDM_ERROR_NONE) {
584                 printf("tdm_client_create failed\n");
585                 goto done;
586         }
587
588         if (data->do_query)
589                 do_query(data);
590         if (data->do_vblank)
591                 do_vblank(data);
592         if (data->do_voutput)
593                 do_voutput(data);
594
595 done:
596         if (data->args.output_name)
597                 free(data->args.output_name);
598         if (data->args.vblank_name)
599                 free(data->args.vblank_name);
600         if (data->client)
601                 tdm_client_destroy(data->client);
602
603         return 0;
604 }