1 /**************************************************************************
5 * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
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>
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:
22 * The above copyright notice and this permission notice (including the
23 * next paragraph) shall be included in all copies or substantial portions
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.
34 **************************************************************************/
45 #include "tdm_client.h"
46 #include "tdm_macro.h"
49 #define CHECK_V_STEP 0
51 typedef struct _tdm_test_client_arg {
60 } tdm_test_client_arg;
62 typedef struct _tdm_test_client {
63 tdm_test_client_arg args;
71 tdm_client_voutput *voutput;
72 tdm_client_output *output;
94 static struct typestrings typestrs[] = {
100 static struct optstrings optstrs[] = {
101 {OPT_QRY, "qo", "output objects info", "<output_name>", "primary"},
102 {OPT_TST, "v", "vblank test", "<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake][^vblank_name]", "primary,0@60~1+0*1^test"},
103 {OPT_TST, "V", "virtual output test", NULL, NULL},
107 usage(char *app_name)
109 int type_size = sizeof(typestrs) / sizeof(struct typestrings);
110 int opt_size = sizeof(optstrs) / sizeof(struct optstrings);
113 printf("usage: %s \n\n", app_name);
115 for (t = 0; t < type_size; t++) {
118 for (o = 0; o < opt_size; o++)
119 if (optstrs[o].type == typestrs[t].type) {
121 printf(" %s options:\n\n", typestrs[t].string);
122 printf("\t-%s\t%s\n", optstrs[o].opt, optstrs[o].desc);
124 printf("\t\t %s\n", optstrs[o].arg);
126 printf("\t\t ex) %s\n", optstrs[o].ex);
137 parse_arg_qo(tdm_test_client *data, char *arg)
139 char name[TDM_NAME_LEN];
140 strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
141 data->args.output_name = strndup(name, TDM_NAME_LEN);
144 //"<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake]"
146 parse_arg_v(tdm_test_client *data, char *arg)
149 char name[TDM_NAME_LEN];
151 end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
152 data->args.output_name = strndup(name, TDM_NAME_LEN);
156 data->args.sync = strtol(arg, &end, 10);
161 data->args.fps = strtol(arg, &end, 10);
166 data->args.interval = strtol(arg, &end, 10);
169 if (*end == '+' || *end == '-') {
171 data->args.offset = strtol(arg, &end, 10);
176 data->args.enable_fake = strtol(arg, &end, 10);
180 char name[TDM_NAME_LEN];
182 end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
183 data->args.vblank_name = strndup(name, TDM_NAME_LEN);
188 parse_args(tdm_test_client *data, int argc, char *argv[])
197 memset(data, 0, sizeof *data);
198 data->args.interval = 1;
200 for (i = 1; i < argc; i++) {
201 if (!strncmp(argv[i] + 1, "qo", 2)) {
203 parse_arg_qo(data, argv[++i]);
204 } else if (!strncmp(argv[i] + 1, "v", 1)) {
206 parse_arg_v(data, argv[++i]);
207 } else if (!strncmp(argv[i] + 1, "V", 1)) {
208 data->do_voutput = 1;
221 if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
222 return (double)tp.tv_sec + ((double)tp.tv_nsec) / 1000000000.0;
228 _client_vblank_handler(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence,
229 unsigned int tv_sec, unsigned int tv_usec, void *user_data)
231 tdm_test_client *data = user_data;
233 static double p_vbl = 0;
237 if (error == TDM_ERROR_DPMS_OFF) {
238 printf("exit: dpms off\n");
242 if (error != TDM_ERROR_NONE) {
243 printf("exit: error(%d)\n", error);
248 vbl = (double)tv_sec + ((double)tv_usec) / 1000000.0;
250 printf("vblank : %.6f us vbl(%.6f)\n", vbl - p_vbl, vbl);
252 if (cur - vbl > 0.002) /* 2ms */
253 printf("kernel -> tdm-client: %.0f us\n", (cur - vbl) * 1000000.0);
258 static char *conn_str[3] = {"disconnected", "connected", "mode_setted"};
259 static char *dpms_str[4] = {"on", "standy", "suspend", "off"};
262 _client_output_handler(tdm_client_output *output, tdm_output_change_type type,
263 tdm_value value, void *user_data)
265 if (type == TDM_OUTPUT_CHANGE_CONNECTION)
266 printf("output %s.\n", conn_str[value.u32]);
267 else if (type == TDM_OUTPUT_CHANGE_DPMS)
268 printf("dpms %s.\n", dpms_str[value.u32]);
272 do_query(tdm_test_client *data)
274 tdm_client_output *output;
275 tdm_output_conn_status status;
276 tdm_output_dpms dpms;
277 unsigned int refresh;
280 output = tdm_client_get_output(data->client, NULL, &error);
281 if (error != TDM_ERROR_NONE) {
282 printf("tdm_client_get_output failed\n");
286 error = tdm_client_output_get_conn_status(output, &status);
287 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
288 error = tdm_client_output_get_dpms(output, &dpms);
289 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
290 error = tdm_client_output_get_refresh_rate(output, &refresh);
291 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
293 printf("tdm_output \"%s\"\n", data->args.output_name);
294 printf("\tstatus : %s\n", conn_str[status]);
295 printf("\tdpms : %s\n", dpms_str[dpms]);
296 printf("\trefresh : %d\n", refresh);
300 do_vblank(tdm_test_client *data)
302 tdm_client_output *output;
303 tdm_client_vblank *vblank = NULL;
308 output = tdm_client_get_output(data->client, data->args.output_name, &error);
309 if (error != TDM_ERROR_NONE) {
310 printf("tdm_client_get_output failed\n");
314 error = tdm_client_output_add_change_handler(output, _client_output_handler, NULL);
315 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
317 vblank = tdm_client_output_create_vblank(output, &error);
318 if (error != TDM_ERROR_NONE) {
319 printf("tdm_client_output_create_vblank failed\n");
323 error = tdm_client_vblank_set_name(vblank, data->args.vblank_name);
324 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
325 error = tdm_client_vblank_set_enable_fake(vblank, data->args.enable_fake);
326 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
327 error = tdm_client_vblank_set_sync(vblank, data->args.sync);
328 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
329 if (data->args.fps > 0) {
330 error = tdm_client_vblank_set_fps(vblank, data->args.fps);
331 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
333 error = tdm_client_vblank_set_offset(vblank, data->args.offset);
334 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
336 error = tdm_client_get_fd(data->client, &fd);
337 if (error != TDM_ERROR_NONE || fd < 0) {
338 printf("tdm_client_get_fd failed\n");
349 if (!data->waiting) {
350 error = tdm_client_vblank_wait(vblank, data->args.interval,
351 _client_vblank_handler, data);
352 if (error == TDM_ERROR_DPMS_OFF) {
353 printf("tdm_client_vblank_wait failed (dpms off)\n");
356 if (error != TDM_ERROR_NONE) {
357 printf("tdm_client_vblank_wait failed (error: %d)\n", error);
363 if (!data->args.sync) {
364 ret = poll(&fds, 1, -1);
366 if (errno == EINTR || errno == EAGAIN) /* normal case */
369 printf("poll failed: %m\n");
374 error = tdm_client_handle_events(data->client);
375 if (error != TDM_ERROR_NONE) {
376 printf("tdm_client_handle_events failed\n");
384 tdm_client_vblank_destroy(vblank);
390 _tdm_client_get_buffer_full_size(tbm_surface_h buffer, int *buffer_w, int *buffer_h)
392 tbm_surface_info_s info;
395 TDM_RETURN_IF_FAIL(buffer != NULL);
397 ret = tbm_surface_get_info(buffer, &info);
398 TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE);
401 if (IS_RGB(info.format))
402 *buffer_w = info.planes[0].stride >> 2;
404 *buffer_w = info.planes[0].stride;
408 *buffer_h = info.planes[0].size / info.planes[0].stride;
412 _tdm_client_dump_png(const char *file, const void *data, int width,
417 fp = fopen(file, "wb");
418 TDM_RETURN_IF_FAIL(fp != NULL);
420 png_structp pPngStruct =
421 png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
427 png_infop pPngInfo = png_create_info_struct(pPngStruct);
429 png_destroy_write_struct(&pPngStruct, NULL);
434 png_init_io(pPngStruct, fp);
435 png_set_IHDR(pPngStruct,
442 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
444 png_set_bgr(pPngStruct);
445 png_write_info(pPngStruct, pPngInfo);
447 const int pixel_size = 4; // RGBA
448 png_bytep *row_pointers =
449 png_malloc(pPngStruct, height * sizeof(png_byte *));
451 png_destroy_write_struct(&pPngStruct, &pPngInfo);
456 unsigned int *blocks = (unsigned int *)data;
460 for (; y < height; ++y) {
462 png_malloc(pPngStruct, sizeof(png_byte) * width * pixel_size);
464 for (x = 0; x < y; x++)
465 png_free(pPngStruct, row_pointers[x]);
466 png_free(pPngStruct, row_pointers);
467 png_destroy_write_struct(&pPngStruct, &pPngInfo);
472 row_pointers[y] = (png_bytep)row;
473 for (x = 0; x < width; ++x) {
474 unsigned int curBlock = blocks[y * width + x];
475 row[x * pixel_size] = (curBlock & 0xFF);
476 row[1 + x * pixel_size] = (curBlock >> 8) & 0xFF;
477 row[2 + x * pixel_size] = (curBlock >> 16) & 0xFF;
478 row[3 + x * pixel_size] = (curBlock >> 24) & 0xFF;
482 png_write_image(pPngStruct, row_pointers);
483 png_write_end(pPngStruct, pPngInfo);
485 for (y = 0; y < height; y++)
486 png_free(pPngStruct, row_pointers[y]);
487 png_free(pPngStruct, row_pointers);
489 png_destroy_write_struct(&pPngStruct, &pPngInfo);
495 _tdm_client_dump_buffer(tbm_surface_h buffer, const char *file)
497 char temp[TDM_PATH_LEN] = {0,};
498 tbm_surface_info_s info;
503 char *dot, *p = temp;
504 const char *file_exts[2] = {"png", "raw"};
506 TDM_RETURN_IF_FAIL(buffer != NULL);
507 TDM_RETURN_IF_FAIL(file != NULL);
509 ret = tbm_surface_map(buffer, TBM_OPTION_READ, &info);
510 TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE);
512 if (IS_RGB(info.format))
517 dot = strrchr(file, '.');
518 if (!dot || strlen(dot + 1) != 3 || strncmp(dot + 1, ext, 3)) {
519 len = strnlen(file, TDM_PATH_LEN - 5);
520 strncat(p, file, len);
527 len = strnlen(file, TDM_PATH_LEN - 1);
528 strncat(p, file, len);
533 _tdm_client_get_buffer_full_size(buffer, &bw, &bh);
535 bo_cnt = tbm_surface_internal_get_num_bos(buffer);
536 TDM_DBG("buffer: bo_cnt(%d) %dx%d(%dx%d) %c%c%c%c, plane: (%p+%d, %d,%d) (%p+%d, %d,%d) (%p+%d, %d,%d)",
537 bo_cnt, bw, bh, info.width, info.height, FOURCC_STR(info.format),
538 info.planes[0].ptr, info.planes[0].offset, info.planes[0].stride, info.planes[0].size,
539 info.planes[1].ptr, info.planes[1].offset, info.planes[1].stride, info.planes[1].size,
540 info.planes[2].ptr, info.planes[2].offset, info.planes[2].stride, info.planes[2].size);
542 _tdm_client_dump_png(temp, info.planes[0].ptr, bw, bh);
544 tbm_surface_unmap(buffer);
546 printf("dump %s\n", temp);
550 _dump_buffer(tbm_surface_h buffer, int count)
552 char temp[TDM_PATH_LEN] = {0,};
554 snprintf(temp, TDM_PATH_LEN, "/tmp/%c%c%c%c_%dx%d_%d",
555 FOURCC_STR(tbm_surface_get_format(buffer)),
556 tbm_surface_get_width(buffer),
557 tbm_surface_get_height(buffer),
559 _tdm_client_dump_buffer(buffer, temp);
563 _voutput_commit(tdm_client_voutput *voutput, tbm_surface_h buffer, void *user_data)
565 tdm_test_client *data = (tdm_test_client *)user_data;
566 static int count = 0;
568 TDM_EXIT_IF_FAIL(data != NULL);
569 TDM_EXIT_IF_FAIL(buffer != NULL);
571 if ((count < 10) || (count >= 31 && count <= 40))
572 _dump_buffer(buffer, count);
576 printf("client: %d commited(%p), mode change request to index 1\n", count, buffer);
577 tdm_client_voutput_set_mode(data->voutput, 1);
578 } else if (count == 50) {
579 printf("client: %d commited(%p), disconnect\n", count, buffer);
580 tdm_client_voutput_disconnect(data->voutput);
582 printf("client: %d commited(%p)\n", count, buffer);
585 tdm_client_voutput_commit_done(voutput);
589 _voutput_output_handler(tdm_client_output *output, tdm_output_change_type type,
590 tdm_value value, void *user_data)
592 tdm_client_voutput *voutput = NULL;
593 tdm_output_conn_status status;
594 tdm_test_client *data;
595 unsigned int width, height;
597 data = (tdm_test_client *) user_data;
598 TDM_RETURN_IF_FAIL(data != NULL);
599 voutput = data->voutput;
600 TDM_RETURN_IF_FAIL(voutput != NULL);
602 if (type == TDM_OUTPUT_CHANGE_CONNECTION) {
603 status = (tdm_output_conn_status)value.u32;
604 printf("output %s.\n", conn_str[value.u32]);
606 if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
607 printf("client: disconnected, destroy voutput\n");
608 tdm_client_output_remove_change_handler(output, _voutput_output_handler, data);
610 printf("press enter to continuet\n");
613 tdm_client_voutput_destroy(voutput);
614 } else if (status == TDM_OUTPUT_CONN_STATUS_CONNECTED) {
615 printf("client: connected\n");
616 } else if (status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) {
617 tdm_client_output_get_mode(output, &width, &height);
618 printf("client: mode setted(%dx%d)\n", width, height);
620 printf("press enter to continuet\n");
624 } else if (type == TDM_OUTPUT_CHANGE_DPMS) {
625 printf("output %s.\n", dpms_str[value.u32]);
630 _voutput_make_available_mode(tdm_client_output_mode *modes, int count)
633 for (i = 0 ; i < count; i++) {
634 modes[i].clock = 25200;
635 modes[i].hdisplay = 640 * (count - i);
636 modes[i].hsync_start = 656;
637 modes[i].hsync_end = 752;
638 modes[i].htotal = 800;
640 modes[i].vdisplay = 480 * (count - i);
641 modes[i].vsync_start = 490;
642 modes[i].vsync_end = 492;
643 modes[i].vtotal = 525;
645 modes[i].vrefresh = 30;
648 snprintf(modes[i].name, TDM_NAME_LEN, "%dx%d_%d", modes[i].hdisplay, modes[i].vdisplay, i);
653 do_voutput(tdm_test_client *data)
655 tdm_client_voutput *voutput = NULL;
656 tdm_client_output *output = NULL;
657 tdm_client_output_mode modes[2];
658 tdm_error ret = TDM_ERROR_NONE;
660 printf("virtual output test - client\n");
662 voutput = tdm_client_create_voutput(data->client, "virtual-test", &ret);
663 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
665 ret = tdm_client_voutput_add_commit_handler(voutput, _voutput_commit, data);
666 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
668 output = tdm_client_voutput_get_client_output(voutput, &ret);
669 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
671 ret = tdm_client_output_add_change_handler(output, _voutput_output_handler, data);
672 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
674 ret = tdm_client_voutput_set_physical_size(voutput, 300, 200);
675 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
677 _voutput_make_available_mode(modes, 2);
678 ret = tdm_client_voutput_set_available_modes(voutput, modes, 2);
679 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
681 printf("virtual output test - press enter to connect\n");
684 ret = tdm_client_voutput_connect(voutput);
685 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
687 data->voutput = voutput;
688 data->output = output;
691 tdm_client_handle_events_timeout(data->client, 1000);
696 tdm_client_voutput_destroy(voutput);
699 static tdm_test_client ttc_data;
702 main(int argc, char *argv[])
704 tdm_test_client *data = &ttc_data;
708 const char *xdg = (const char*)getenv("XDG_RUNTIME_DIR");
711 snprintf(buf, sizeof(buf), "/run");
712 int ret = setenv("XDG_RUNTIME_DIR", (const char*)buf, 1);
717 /* for tbm_bufmgr_init */
718 const char *s = (const char*)getenv("TBM_DISPLAY_SERVER");
721 snprintf(buf, sizeof(buf), "1");
722 int ret = setenv("TBM_DISPLAY_SERVER", (const char*)buf, 1);
727 parse_args(data, argc, argv);
729 printf("sync(%d) fps(%d) interval(%d) offset(%d) enable_fake(%d) pid(%d)\n",
730 data->args.sync, data->args.fps, data->args.interval,
731 data->args.offset, data->args.enable_fake, data->args.pid);
733 data->client = tdm_client_create(&error);
734 if (error != TDM_ERROR_NONE) {
735 printf("tdm_client_create failed\n");
743 if (data->do_voutput)
747 if (data->args.output_name)
748 free(data->args.output_name);
749 if (data->args.vblank_name)
750 free(data->args.vblank_name);
752 tdm_client_destroy(data->client);