tool: change copy length
[platform/core/uifw/libtdm.git] / tools / tdm_test_server.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 <signal.h>
40 #include <poll.h>
41 #include <errno.h>
42 #include <time.h>
43 #include <stdint.h>
44
45 #include <tbm_surface.h>
46 #include <tbm_surface_internal.h>
47
48 #include "tdm.h"
49 #include "tdm_log.h"
50 #include "tdm_list.h"
51 #include "tdm_helper.h"
52 #include "tdm_backend.h"
53
54 #include "tdm_macro.h"
55 #include "tdm_private.h"
56 #include "buffers.h"
57
58 ////////////////////////////////////////////////////////////////////////////////
59 struct typestrings {
60         int type;
61         const char *string;
62         const char *desc;
63 };
64
65 struct optstrings {
66         int  type;
67         const char *opt;
68         const char *desc;
69         const char *arg;
70         const char *ex;
71 };
72
73 enum {
74         OPT_QRY,
75         OPT_TST,
76         OPT_GEN,
77 };
78
79 static struct typestrings typestrs[] = {
80         {OPT_QRY, "Query",          NULL},
81         {OPT_TST, "Test",           NULL},
82         {OPT_GEN, "General",        NULL},
83 };
84
85 static struct optstrings optstrs[] = {
86         {
87                 OPT_QRY, "q", "show tdm output, layer information",
88                 NULL, NULL
89         },
90         {
91                 OPT_TST, "a", "set all layer objects for all connected outputs",
92                 NULL, NULL
93         },
94         {
95                 OPT_TST, "o", "set a mode for a output object",
96                 "<output_idx>@<mode>[&<refresh>]", "0@1920x1080"
97         },
98         {
99                 OPT_TST, "l", "set a layer object",
100                 "<layer_idx>[:<w>x<h>[+<x>+<y>][,<h>x<v>][@<format>]]~<w>x<h>[+<x>+<y>][*<transform>]", NULL
101         },
102         {
103                 OPT_TST, "p", "set a PP object.\n\t\t'-l' is used to show the result on screen.",
104                 "<w>x<h>[+<x>+<y>][,<h>x<v>][@<format>]~<w>x<h>[+<x>+<y>][,<h>x<v>][@<format>][*<transform>][&<fps>]", NULL
105         },
106         {
107                 OPT_TST, "c", "catpure a output object or a layer object.\n\t\t'-l' is used to show the result on screen.",
108                 "<output_idx>[,<layer_idx>]~<w>x<h>[+<x>+<y>][,<h>x<v>][@<format>][*<transform>][^stream]", NULL
109         },
110         {
111                 OPT_TST, "V", "virtual output test.\n\t\t'-l' is used to show the result on screen.",
112                 NULL, NULL
113         },
114         {
115                 OPT_GEN, "w", "set the property of a object",
116                 "<prop_name>:<value>", NULL
117         },
118         {
119                 OPT_GEN, "b", "set the fill(smtpe,tiles,plain) and framebuffer type(scanout,noncachable,wc)",
120                 "<fill>[:<buf_flag>[,<buf_flag2>[,...]]]", NULL
121         },
122         {
123                 OPT_GEN, "v", "update layers every vblank",
124                 NULL, NULL
125         },
126 };
127
128 struct usagestring {
129         const char *string;
130         const char *desc;
131 };
132
133 static struct usagestring usages[] = {
134         {
135                 "-q",
136                 NULL
137         },
138         {
139                 "-a -b plain",
140                 "test all outputs, layers with plain buffers"
141         },
142         {
143                 "-o 0@1920x1080",
144                 "Set the \"1920x1080\" mode to the output 0. And show a buffer via a primary layer of the output 0"
145         },
146         {
147                 "-o 0@1920x1080 -l 1~640x480+50+100",
148                 "Create the 640x480 buffer and show it in the (50,100) pos of screen via the layer 1"
149         },
150         {
151                 "-p 320x240@NV12~480x360+80+40,640x480@AR24 -l 1~640x480+50+100",
152                 "Convert the 320x240@NV12 buffer to the 640x480@AR24 buffer(480x360+80+40) and show the result via the layer 1"
153         },
154 };
155
156 static void
157 usage(char *app_name)
158 {
159         int type_size = sizeof(typestrs) / sizeof(struct typestrings);
160         int opt_size = sizeof(optstrs) / sizeof(struct optstrings);
161         int usages_size = sizeof(usages) / sizeof(struct usagestring);
162         int t;
163
164         printf("usage: %s \n\n", app_name);
165
166         for (t = 0; t < type_size; t++) {
167                 int o, f = 1;
168
169                 for (o = 0; o < opt_size; o++)
170                         if (optstrs[o].type == typestrs[t].type) {
171                                 if (f == 1)
172                                         printf(" %s options: %s\n\n", typestrs[t].string, (typestrs[t].desc) ? : "");
173                                 printf("\t-%s\t%s\n", optstrs[o].opt, optstrs[o].desc);
174                                 if (optstrs[o].arg)
175                                         printf("\t\t  %s\n", optstrs[o].arg);
176                                 if (optstrs[o].ex)
177                                         printf("\t\t  ex) %s\n", optstrs[o].ex);
178                                 f = 0;
179                         }
180                 printf("\n");
181         }
182
183         printf(" For example)\n\n");
184
185         for (t = 0; t < usages_size; t++) {
186                 printf("    $ %s %s\n", app_name, usages[t].string);
187                 printf("\t%s\n", usages[t].desc);
188         }
189         printf("\n");
190 }
191
192 ////////////////////////////////////////////////////////////////////////////////
193
194 static const char *tdm_buf_flag_names[] = {
195         "scanout",
196         "noncachable",
197         "wc",
198 };
199 TDM_BIT_NAME_FB(buf_flag)
200
201 #define DEFAULT_FORMAT  TBM_FORMAT_ARGB8888
202
203 #define print_size(s) \
204         printf("%dx%d", (s)->h, (s)->v)
205 #define print_pos(p) \
206         printf("%dx%d+%d+%d", (p)->w, (p)->h, (p)->x, (p)->y)
207 #define print_format(f) \
208         if (f) printf("%c%c%c%c", FOURCC_STR(f)); \
209         else printf("NONE")
210 #define print_config(c) \
211         do { \
212                 print_size(&(c)->size); \
213                 printf(" "); \
214                 print_pos(&(c)->pos); \
215                 printf(" "); \
216                 print_format((c)->format); \
217         } while (0)
218 #define print_prop(w) \
219         printf("%s(%d)", (w)->name, ((w)->value).u32)
220
221 typedef struct _tdm_test_server tdm_test_server;
222 typedef struct _tdm_test_server_layer tdm_test_server_layer;
223 typedef struct _tdm_test_server_capture tdm_test_server_capture;
224 typedef struct _tdm_test_server_voutput tdm_test_server_voutput;
225
226 typedef struct _tdm_test_server_prop {
227         /* args */
228         char name[TDM_NAME_LEN];
229         tdm_value value;
230
231         /* variables for test */
232         struct list_head link;
233 } tdm_test_server_prop;
234
235 typedef struct _tdm_test_server_buffer {
236         /* variables for test */
237         tbm_surface_h b;
238         int in_use;
239         tdm_test_server_layer *l;
240
241         tdm_test_server_capture *c;
242         tdm_buffer_release_handler done;
243 } tdm_test_server_buffer;
244
245 typedef struct _tdm_test_server_output {
246         /* args */
247         int idx;
248         char mode[TDM_NAME_LEN];
249         int refresh;
250
251         /* variables for test */
252         struct list_head link;
253         struct list_head prop_list;
254         struct list_head layer_list;
255         tdm_test_server *data;
256         tdm_output *output;
257
258         int fill_primary_layer;
259 } tdm_test_server_output;
260
261 typedef struct _tdm_test_server_pp {
262         /* args */
263         tdm_info_pp info;
264         int fps;
265
266         /* variables for test */
267         struct list_head link;
268         tdm_test_server *data;
269         tdm_test_server_layer *l;
270         tdm_pp *pp;
271         tbm_surface_h bufs[6];
272         int buf_idx;
273
274         tdm_event_loop_source *timer_source;
275 } tdm_test_server_pp;
276
277 struct _tdm_test_server_capture {
278         /* args */
279         int output_idx;
280         int layer_idx;
281         tdm_info_capture info;
282
283         /* variables for test */
284         struct list_head link;
285         tdm_test_server *data;
286         tdm_test_server_layer *l;
287         tdm_capture *capture;
288 };
289
290 struct _tdm_test_server_layer {
291         /* args */
292         int idx;
293         tdm_info_layer info;
294
295         /* variables for test */
296         struct list_head link;
297         struct list_head prop_list;
298         tdm_test_server *data;
299         tdm_test_server_output *o;
300         tdm_layer *layer;
301         int is_primary;
302         tdm_test_server_pp *owner_p;
303         tdm_test_server_capture *owner_c;
304         tbm_surface_h bufs[3];
305         int buf_idx;
306 };
307
308 struct _tdm_test_server_voutput {
309         struct list_head link;
310         tdm_test_server *data;
311         tdm_output *output;
312         tdm_layer *layer;
313         tbm_surface_h bufs[3];
314         int buf_idx;
315         int need_mode_change;
316         int mode_index;
317         tdm_event_loop_source *timer;
318         const tdm_output_mode *current_mode;
319 };
320
321 struct _tdm_test_server {
322         /* args */
323         int do_query;
324         int do_all;
325         int do_vblank;
326         int do_voutput;
327         int bflags;
328         int b_fill;
329
330         /* variables for test */
331         struct list_head output_list;
332         struct list_head pp_list;
333         struct list_head capture_list;
334         struct list_head voutput_list;
335         tdm_display *display;
336 };
337
338 static void destroy(tdm_test_server *data);
339 static void run_test(tdm_test_server *data);
340 static void output_setup(tdm_test_server_output *o);
341 static void layer_show_buffer(tdm_test_server_layer *l, tbm_surface_h b);
342 static void capture_attach(tdm_test_server_capture *c, tbm_surface_h b);
343 static void _vlayer_show_buffer(tdm_test_server_voutput *voutput);
344 static void _voutput_layer_init(tdm_test_server_voutput *voutput);
345
346 static char*
347 parse_size(tdm_size *size, char *arg)
348 {
349         char *end;
350         size->h = strtol(arg, &end, 10);
351         TDM_EXIT_IF_FAIL(*end == 'x');
352         arg = end + 1;
353         size->v = strtol(arg, &end, 10);
354         return end;
355 }
356
357 static char*
358 parse_pos(tdm_pos *pos, char *arg)
359 {
360         char *end;
361         pos->w = strtol(arg, &end, 10);
362         TDM_EXIT_IF_FAIL(*end == 'x');
363         arg = end + 1;
364         pos->h = strtol(arg, &end, 10);
365         if (*end == '+') {
366                 arg = end + 1;
367                 pos->x = strtol(arg, &end, 10);
368                 TDM_EXIT_IF_FAIL(*end == '+');
369                 arg = end + 1;
370                 pos->y = strtol(arg, &end, 10);
371         }
372         return end;
373 }
374
375 static char*
376 parse_config(tdm_info_config *config, char *arg)
377 {
378         char *end;
379         end = parse_pos(&config->pos, arg);
380         if (*end == ',') {
381                 arg = end + 1;
382                 end = parse_size(&config->size, arg);
383         }
384         if (*end == '@') {
385                 char temp[32];
386                 arg = end + 1;
387                 end = strtostr(temp, 32, arg, TDM_DELIM);
388                 config->format = FOURCC_ID(temp);
389         }
390         return end;
391 }
392
393 static void
394 parse_arg_o(tdm_test_server_output *o, char *arg)
395 {
396         char *end;
397         TDM_EXIT_IF_FAIL(arg != NULL);
398         o->idx = strtol(arg, &end, 10);
399         TDM_EXIT_IF_FAIL(*end == '@');
400         arg = end + 1;
401         end = strtostr(o->mode, TDM_NAME_LEN, arg, TDM_DELIM);
402         if (*end == ',') {
403                 arg = end + 1;
404                 o->refresh = strtol(arg, &end, 10);
405         }
406 }
407
408 static void
409 parse_arg_p(tdm_test_server_pp *p, char *arg)
410 {
411         tdm_info_pp *pp_info = &p->info;
412         char *end;
413         TDM_EXIT_IF_FAIL(arg != NULL);
414         end = parse_config(&pp_info->src_config, arg);
415         TDM_EXIT_IF_FAIL(*end == '~');
416         arg = end + 1;
417         end = parse_config(&pp_info->dst_config, arg);
418         if (*end == '*') {
419                 arg = end + 1;
420                 pp_info->transform = strtol(arg, &end, 10);
421         }
422         if (*end == '&') {
423                 arg = end + 1;
424                 p->fps = strtol(arg, &end, 10);
425         }
426 }
427
428 static void
429 parse_arg_c(tdm_test_server_capture *c, char *arg)
430 {
431         tdm_info_capture *capture_info = &c->info;
432         char *end;
433         TDM_EXIT_IF_FAIL(arg != NULL);
434         c->output_idx = strtol(arg, &end, 10);
435         if (*end == ',') {
436                 arg = end + 1;
437                 c->layer_idx = strtol(arg, &end, 10);
438         }
439         TDM_EXIT_IF_FAIL(*end == '~');
440         arg = end + 1;
441         end = parse_config(&capture_info->dst_config, arg);
442         if (*end == '*') {
443                 arg = end + 1;
444                 capture_info->transform = strtol(arg, &end, 10);
445         }
446         if (*end == '^') {
447                 arg = end + 1;
448                 if (strtol(arg, &end, 10) > 0)
449                         capture_info->type = TDM_CAPTURE_TYPE_STREAM;
450                 else
451                         capture_info->type = TDM_CAPTURE_TYPE_ONESHOT;
452         }
453 }
454
455 static void
456 parse_arg_l(tdm_test_server_layer *l, char *arg)
457 {
458         tdm_info_layer *layer_info = &l->info;
459         char *end;
460         TDM_EXIT_IF_FAIL(arg != NULL);
461         l->idx = strtol(arg, &end, 10);
462         if (*end == ':') {
463                 arg = end + 1;
464                 end = parse_config(&layer_info->src_config, arg);
465         }
466         TDM_EXIT_IF_FAIL(*end == '~');
467         arg = end + 1;
468         end = parse_pos(&layer_info->dst_pos, arg);
469         if (*end == '*') {
470                 arg = end + 1;
471                 layer_info->transform = strtol(arg, &end, 10);
472         }
473 }
474
475 static void
476 parse_arg_w(tdm_test_server_prop *w, char *arg)
477 {
478         char *end;
479         TDM_EXIT_IF_FAIL(arg != NULL);
480         end = strtostr(w->name, TDM_NAME_LEN, arg, TDM_DELIM);
481         TDM_EXIT_IF_FAIL(*end == ':');
482         arg = end + 1;
483         w->value.u32 = strtol(arg, &end, 10);
484 }
485
486 static void
487 parse_arg_b(tdm_test_server *data, char *arg)
488 {
489         char *end = arg;
490         char temp[TDM_NAME_LEN] = {0,};
491         TDM_EXIT_IF_FAIL(arg != NULL);
492
493         end = strtostr(temp, 32, arg, TDM_DELIM);
494         if (!strncmp(temp, "smpte", 5))
495                 data->b_fill = PATTERN_SMPTE;
496         else if (!strncmp(temp, "tiles", 5))
497                 data->b_fill = PATTERN_TILES;
498         else if (!strncmp(temp, "plain", 5))
499                 data->b_fill = PATTERN_PLAIN;
500         else {
501                 printf("'%s': unknown flag\n", temp);
502                 exit(0);
503         }
504
505         if (*arg == ':') {
506                 data->bflags = 0;
507                 arg = end + 1;
508                 snprintf(temp, TDM_NAME_LEN, "%s", arg);
509                 arg = strtok_r(temp, ",", &end);
510                 while (arg) {
511                         if (!strncmp(arg, "default", 7))
512                                 printf("Ignore '%s' flag\n", arg);
513                         else if (!strncmp(arg, "scanout", 7))
514                                 data->bflags |= TBM_BO_SCANOUT;
515                         else if (!strncmp(arg, "noncachable", 11))
516                                 data->bflags |= TBM_BO_NONCACHABLE;
517                         else if (!strncmp(arg, "wc", 2))
518                                 data->bflags |= TBM_BO_WC;
519                         else {
520                                 printf("'%s': unknown flag\n", arg);
521                                 exit(0);
522                         }
523                         arg = strtok_r(NULL, ",", &end);
524                 }
525         }
526 }
527
528 static void
529 parse_args(tdm_test_server *data, int argc, char *argv[])
530 {
531         tdm_test_server_output *o = NULL;
532         tdm_test_server_layer *l = NULL;
533         tdm_test_server_pp *p = NULL;
534         tdm_test_server_capture *c = NULL;
535         tdm_test_server_prop *w = NULL;
536         void *last_option = NULL;
537         void *last_object = NULL;
538         int i;
539
540         if (argc < 2) {
541                 usage(argv[0]);
542                 destroy(data);
543                 exit(0);
544         }
545
546         for (i = 1; i < argc; i++) {
547                 if (!strncmp(argv[i] + 1, "q", 1)) {
548                         data->do_query = 1;
549                         return;
550                 } else if (!strncmp(argv[i] + 1, "a", 1)) {
551                         data->do_all = 1;
552                 } else if (!strncmp(argv[i] + 1, "o", 1)) {
553                         TDM_GOTO_IF_FAIL(data->do_all == 0, all);
554                         o = calloc(1, sizeof * o);
555                         TDM_EXIT_IF_FAIL(o != NULL);
556                         o->data = data;
557                         LIST_INITHEAD(&o->layer_list);
558                         LIST_INITHEAD(&o->prop_list);
559                         LIST_ADDTAIL(&o->link, &data->output_list);
560                         parse_arg_o(o, argv[++i]);
561                         last_object = o;
562                 } else if (!strncmp(argv[i] + 1, "p", 1)) {
563                         TDM_GOTO_IF_FAIL(data->do_all == 0, all);
564                         p = calloc(1, sizeof * p);
565                         TDM_EXIT_IF_FAIL(p != NULL);
566                         p->data = data;
567                         p->fps = 30;   /* default 30 fps */
568                         LIST_ADDTAIL(&p->link, &data->pp_list);
569                         parse_arg_p(p, argv[++i]);
570                         last_option = p;
571                 } else if (!strncmp(argv[i] + 1, "c", 1)) {
572                         TDM_GOTO_IF_FAIL(data->do_all == 0, all);
573                         c = calloc(1, sizeof * c);
574                         TDM_EXIT_IF_FAIL(c != NULL);
575                         c->data = data;
576                         c->output_idx = -1;
577                         c->layer_idx = -1;
578                         LIST_ADDTAIL(&c->link, &data->capture_list);
579                         parse_arg_c(c, argv[++i]);
580                         last_option = c;
581                 } else if (!strncmp(argv[i] + 1, "l", 1)) {
582                         TDM_GOTO_IF_FAIL(data->do_all == 0, all);
583                         if (!o) {
584                                 o = calloc(1, sizeof * o);
585                                 TDM_EXIT_IF_FAIL(o != NULL);
586                                 o->data = data;
587                                 LIST_INITHEAD(&o->layer_list);
588                                 LIST_INITHEAD(&o->prop_list);
589                                 LIST_ADDTAIL(&o->link, &data->output_list);
590                         }
591                         l = calloc(1, sizeof * l);
592                         TDM_EXIT_IF_FAIL(l != NULL);
593                         LIST_INITHEAD(&l->prop_list);
594                         LIST_ADDTAIL(&l->link, &o->layer_list);
595                         l->data = data;
596                         l->o = o;
597                         parse_arg_l(l, argv[++i]);
598                         if (p && last_option == p) {
599                                 p->l = l;
600                                 l->owner_p = p;
601                         } else if (c && last_option == c) {
602                                 c->l = l;
603                                 l->owner_c = c;
604                         }
605                         last_object = l;
606                 } else if (!strncmp(argv[i] + 1, "w", 1)) {
607                         TDM_GOTO_IF_FAIL(data->do_all == 0, all);
608                         if (!last_object)
609                                 goto no_object;
610                         w = calloc(1, sizeof * w);
611                         TDM_EXIT_IF_FAIL(w != NULL);
612                         if (o && last_object == o)
613                                 LIST_ADDTAIL(&w->link, &o->prop_list);
614                         else if (l && last_object == l)
615                                 LIST_ADDTAIL(&w->link, &l->prop_list);
616                         parse_arg_w(w, argv[++i]);
617                 } else if (!strncmp(argv[i] + 1, "b", 1)) {
618                         parse_arg_b(data, argv[++i]);
619                 } else if (!strncmp(argv[i] + 1, "v", 1)) {
620                         data->do_vblank = 1;
621                 } else if (!strncmp(argv[i] + 1, "V", 1)) {
622                         data->do_voutput = 1;
623                 } else {
624                         usage(argv[0]);
625                         destroy(data);
626                         exit(0);
627                 }
628         }
629
630         LIST_FOR_EACH_ENTRY(p, &data->pp_list, link) {
631                 if (!p->l)
632                         goto no_layer;
633         }
634         LIST_FOR_EACH_ENTRY(c, &data->capture_list, link) {
635                 if (!c->l)
636                         goto no_layer;
637         }
638
639         return;
640 no_layer:
641         printf("Use '-l' to set a layer for '-p' or '-c'.\n");
642         exit(0);
643 no_object:
644         printf("Use '-o' or '-l' to set a object first.\n");
645         exit(0);
646 all:
647         printf("Can't use '-%s' with '-a'.\n", argv[i] + 1);
648         exit(0);
649 }
650
651 static void
652 interpret_args(tdm_test_server *data)
653 {
654         tdm_test_server_output *o = NULL;
655         tdm_test_server_layer *l = NULL;
656         tdm_error ret;
657
658         /* create the objects of outputs */
659         if (data->do_all) {
660                 int i, output_count;
661
662                 ret = tdm_display_get_output_count(data->display, &output_count);
663                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
664
665                 for (i = 0; i < output_count; i++) {
666                         tdm_output *output = tdm_display_get_output(data->display, i, NULL);
667                         tdm_output_conn_status status;
668                         ret = tdm_output_get_conn_status(output, &status);
669                         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
670
671                         if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
672                                 continue;
673                         o = calloc(1, sizeof * o);
674                         TDM_EXIT_IF_FAIL(o != NULL);
675                         o->data = data;
676                         LIST_INITHEAD(&o->layer_list);
677                         LIST_INITHEAD(&o->prop_list);
678                         LIST_ADDTAIL(&o->link, &data->output_list);
679                         o->idx = i;
680                         o->fill_primary_layer = 1;
681                 }
682         }
683
684         /* check if the primary layer object exists */
685         LIST_FOR_EACH_ENTRY(o, &data->output_list, link) {
686                 tdm_output *output;
687                 const tdm_output_mode *mode;
688                 int j, layer_count, primary_index = 0;
689                 tdm_test_server_layer *primary_l = NULL;
690
691                 output_setup(o);
692
693                 output = tdm_display_get_output(data->display, o->idx, &ret);
694                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
695                 ret = tdm_output_get_mode(output, &mode);
696                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
697                 ret = tdm_output_get_layer_count(output, &layer_count);
698                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
699                 ret = tdm_output_get_primary_index(output, &primary_index);
700                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
701
702                 if (LIST_IS_EMPTY(&o->layer_list))
703                         o->fill_primary_layer = 1;
704                 else {
705                         LIST_FOR_EACH_ENTRY(l, &o->layer_list, link) {
706                                 if (l->idx == primary_index) {
707                                         l->is_primary = 1;
708                                         o->fill_primary_layer = 1;
709                                         primary_l = l;
710                                         break;
711                                 }
712                         }
713                 }
714
715                 if (!primary_l || data->do_all) {
716                         for (j = 0; j < layer_count; j++) {
717                                 if (j != primary_index && !data->do_all)
718                                         continue;
719                                 l = calloc(1, sizeof * l);
720                                 TDM_EXIT_IF_FAIL(l != NULL);
721                                 LIST_INITHEAD(&l->prop_list);
722                                 LIST_ADDTAIL(&l->link, &o->layer_list);
723                                 l->data = data;
724                                 l->o = o;
725                                 l->idx = j;
726                                 if (j == primary_index) {
727                                         l->is_primary = 1;
728                                         l->info.dst_pos.w = mode->hdisplay;
729                                         l->info.dst_pos.h = mode->vdisplay;
730                                         primary_l = l;
731                                 } else {
732                                         l->info.dst_pos.w = TDM_ALIGN(mode->hdisplay / 3, 2);
733                                         l->info.dst_pos.h = TDM_ALIGN(mode->vdisplay / 3, 2);
734                                         l->info.dst_pos.x = TDM_ALIGN(((mode->hdisplay / 3) / layer_count) * j, 2);
735                                         l->info.dst_pos.y = TDM_ALIGN(((mode->vdisplay / 3) / layer_count) * j, 2);
736                                 }
737                         }
738                 }
739
740                 TDM_EXIT_IF_FAIL(primary_l != NULL);
741                 LIST_DEL(&primary_l->link);
742                 LIST_ADD(&primary_l->link, &o->layer_list);
743         }
744
745         /* fill the empty information of layers */
746         LIST_FOR_EACH_ENTRY(o, &data->output_list, link) {
747                 tdm_output *output;
748                 int minw, minh, maxw, maxh;
749
750                 output = tdm_display_get_output(data->display, o->idx, &ret);
751                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
752                 ret = tdm_output_get_available_size(output, &minw, &minh, &maxw, &maxh, NULL);
753                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
754
755                 /* l->info.src_config.size will be decided when a buffer shows really */
756                 LIST_FOR_EACH_ENTRY(l, &o->layer_list, link) {
757                         if (minw > 0 && minh > 0) {
758                                 TDM_EXIT_IF_FAIL(l->info.dst_pos.w >= minw);
759                                 TDM_EXIT_IF_FAIL(l->info.dst_pos.h >= minh);
760                         }
761                         if (maxw > 0 && maxh > 0) {
762                                 TDM_EXIT_IF_FAIL(l->info.dst_pos.w <= maxw);
763                                 TDM_EXIT_IF_FAIL(l->info.dst_pos.h <= maxh);
764                         }
765
766                         if (l->owner_p) {
767                                 l->info.src_config.format = l->owner_p->info.dst_config.format;
768                                 if (l->info.src_config.pos.w == 0) {
769                                         TDM_EXIT_IF_FAIL(l->owner_p->info.dst_config.size.h > 0);
770                                         l->info.src_config.pos.w = l->owner_p->info.dst_config.size.h;
771                                         l->info.src_config.pos.h = l->owner_p->info.dst_config.size.v;
772                                 }
773                         } else if (l->owner_c) {
774                                 l->info.src_config.format = l->owner_c->info.dst_config.format;
775                                 if (l->info.src_config.pos.w == 0) {
776                                         TDM_EXIT_IF_FAIL(l->owner_c->info.dst_config.size.h > 0);
777                                         l->info.src_config.pos.w = l->owner_c->info.dst_config.size.h;
778                                         l->info.src_config.pos.h = l->owner_c->info.dst_config.size.v;
779                                 }
780                         } else {
781                                 if (l->info.src_config.pos.w == 0) {
782                                         TDM_EXIT_IF_FAIL(l->info.dst_pos.w > 0);
783                                         l->info.src_config.pos.w = l->info.dst_pos.w;
784                                         l->info.src_config.pos.h = l->info.dst_pos.h;
785                                 }
786                         }
787                 }
788         }
789 }
790
791 static tdm_test_server tts_data;
792 static int tts_buffer_key;
793 #define TTS_BUFFER_KEY ((unsigned long)&tts_buffer_key)
794
795 static tbm_surface_h
796 buffer_allocate(int width, int height, int format, int flags)
797 {
798         tdm_test_server_buffer *tb = calloc(1, sizeof *tb);
799         TDM_EXIT_IF_FAIL(tb != NULL);
800         tb->b = tbm_surface_internal_create_with_flags(width, height, format, flags);
801         TDM_EXIT_IF_FAIL(tb->b != NULL);
802         tdm_helper_clear_buffer(tb->b);
803         tbm_surface_internal_add_user_data(tb->b, TTS_BUFFER_KEY, free);
804         tbm_surface_internal_set_user_data(tb->b, TTS_BUFFER_KEY, tb);
805         return tb->b;
806 }
807
808 static tdm_test_server_buffer*
809 get_tts_buffer(tbm_surface_h b)
810 {
811         tdm_test_server_buffer *tb = NULL;
812         tbm_surface_internal_get_user_data(b, TTS_BUFFER_KEY, (void **)&tb);
813         return tb;
814 }
815
816 static void
817 _voutput_buff_deinit(tdm_test_server_voutput *voutput)
818 {
819         int i;
820
821         for (i = 0; i < 3; i++) {
822                 if (!voutput->bufs[i]) continue;
823
824                 tbm_surface_destroy(voutput->bufs[i]);
825                 voutput->bufs[i] = NULL;
826         }
827 }
828
829 static void
830 _voutput_buff_init(tdm_test_server_voutput *voutput)
831 {
832         tdm_output *output = voutput->output;
833         const tdm_output_mode *mode;
834         tdm_error ret = TDM_ERROR_NONE;
835         int i;
836
837         ret = tdm_output_get_mode(output, &mode);
838         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
839
840         for (i = 0; i < 3; i++) {
841                 tbm_surface_h b = tbm_surface_internal_create_with_flags(mode->hdisplay, mode->vdisplay, DEFAULT_FORMAT, 0);
842                 TDM_EXIT_IF_FAIL(b != NULL);
843                 tdm_test_buffer_fill(b, i);
844                 voutput->bufs[i] = b;
845         }
846 }
847 static void
848 _vlayer_cb_commit(tdm_layer *layer, unsigned int sequence,
849                                 unsigned int tv_sec, unsigned int tv_usec, void *user_data)
850 {
851         tdm_test_server_voutput *voutput = (tdm_test_server_voutput *)user_data;
852         TDM_EXIT_IF_FAIL(voutput != NULL);
853         tdm_test_server *data;
854         tdm_output_conn_status status;
855         tdm_error ret;
856
857         printf("voutput cb:\t %d: l(%p) b(%p)\n", voutput->buf_idx, voutput->layer, voutput->bufs[voutput->buf_idx]);
858
859         ret = tdm_output_get_conn_status(voutput->output, &status);
860         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
861
862         if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) return;
863
864         if (voutput->need_mode_change) {
865                 const tdm_output_mode *modes;
866                 int count = 0;
867
868                 tdm_output_get_available_modes(voutput->output, &modes, &count);
869                 if (count > voutput->mode_index) {
870                         const tdm_output_mode *mode;
871
872                         mode = &modes[voutput->mode_index];
873                         data = voutput->data;
874
875                         printf("mode change to %d (%dx%d, %d)\n",
876                                 voutput->mode_index, mode->hdisplay, mode->vdisplay, mode->vrefresh);
877                         _voutput_buff_deinit(voutput);
878                         _voutput_buff_init(voutput);
879
880                         ret = tdm_output_set_mode(voutput->output, mode);
881                         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
882
883                         voutput->current_mode = mode;
884
885                         _voutput_layer_init(voutput);
886                         voutput->buf_idx = 0;
887
888                         tdm_display_lock(data->display);
889                         ret = tdm_event_loop_source_timer_update(voutput->timer, 10);
890                         tdm_display_unlock(data->display);
891                         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
892                 }
893
894                 voutput->need_mode_change = 0;
895                 return;
896         }
897
898         voutput->buf_idx++;
899         _vlayer_show_buffer(voutput);
900 }
901
902 static void
903 _vlayer_show_buffer(tdm_test_server_voutput *voutput)
904 {
905         tdm_error ret;
906         int index;
907         if (voutput->buf_idx == 3)
908                 voutput->buf_idx = 0;
909
910         index = voutput->buf_idx;
911
912         ret = tdm_layer_set_buffer(voutput->layer, voutput->bufs[index]);
913         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
914
915         ret = tdm_layer_commit(voutput->layer, _vlayer_cb_commit, voutput);
916         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
917
918         printf("voutput show:\t %d: l(%p) b(%p)\n", index, voutput->layer, voutput->bufs[index]);
919 }
920
921 static void
922 _voutput_layer_init(tdm_test_server_voutput *voutput)
923 {
924         tdm_output *output = voutput->output;
925         const tdm_output_mode *mode;
926         tdm_info_layer layer_info;
927         tbm_surface_info_s info;
928         tdm_error ret = TDM_ERROR_NONE;
929
930         ret = tdm_output_get_mode(output, &mode);
931         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
932
933         TDM_ERR("modeinfo : %dx%d %d", mode->hdisplay, mode->vdisplay, mode->vrefresh);
934
935         voutput->current_mode = mode;
936
937         voutput->layer = tdm_output_get_layer(output, 0, &ret);
938         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
939
940         _voutput_buff_init(voutput);
941
942         tbm_surface_get_info(voutput->bufs[0], &info);
943         memset(&layer_info, 0x0, sizeof(tdm_info_layer));
944         if (IS_RGB(info.format)) {
945                 layer_info.src_config.size.h = info.planes[0].stride >> 2;
946                 layer_info.src_config.size.v  = info.height;
947         } else {
948                 layer_info.src_config.size.h = info.planes[0].stride;
949                 layer_info.src_config.size.v  = info.height;
950         }
951         layer_info.src_config.format = info.format;
952
953         layer_info.src_config.size.h = mode->hdisplay;
954         layer_info.src_config.size.v = mode->vdisplay;
955         layer_info.src_config.pos.x = 0;
956         layer_info.src_config.pos.y = 0;
957         layer_info.src_config.pos.w = mode->hdisplay;
958         layer_info.src_config.pos.h = mode->vdisplay;
959         layer_info.dst_pos.x = 0;
960         layer_info.dst_pos.y = 0;
961         layer_info.dst_pos.w = mode->hdisplay;
962         layer_info.dst_pos.h = mode->vdisplay;
963         layer_info.transform = TDM_TRANSFORM_NORMAL;
964
965         ret = tdm_layer_set_info(voutput->layer, &layer_info);
966         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
967 }
968
969 static void
970 _voutput_connect(tdm_test_server_voutput *voutput)
971 {
972         tdm_output *output;
973         const tdm_output_mode *modes, *found = NULL, *best = NULL, *prefer = NULL;
974         int i, count;
975         tdm_error ret;
976
977         output = voutput->output;
978
979         ret = tdm_output_get_available_modes(output, &modes, &count);
980         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
981
982         for (i = 0; i < count; i++) {
983                 if (!best)
984                         best = &modes[i];
985                 if (modes[i].type & TDM_OUTPUT_MODE_TYPE_PREFERRED)
986                         prefer = &modes[i];
987         }
988         if (prefer) {
989                 found = prefer;
990                 printf("found prefer mode: %dx%d %d\n", found->hdisplay, found->vdisplay, found->vrefresh);
991         }
992         if (!found && best) {
993                 found = best;
994                 printf("found best mode: %dx%d %d\n", found->hdisplay, found->vdisplay, found->vrefresh);
995         }
996         if (!found) {
997                 printf("couldn't find any mode\n");
998                 exit(0);
999         }
1000
1001         ret = tdm_output_set_mode(output, found);
1002         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1003
1004         printf("output: %s %d\n", found->name, found->vrefresh);
1005
1006         ret = tdm_output_set_dpms(output, TDM_OUTPUT_DPMS_ON);
1007         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1008 }
1009
1010 static void
1011 _voutput_disconnect(tdm_test_server_voutput *voutput)
1012 {
1013         tdm_output *output;
1014         tdm_error ret;
1015
1016         output = voutput->output;
1017
1018         printf("output: %p disconnect\n", voutput);
1019
1020         ret = tdm_output_set_dpms(output, TDM_OUTPUT_DPMS_OFF);
1021         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1022 }
1023
1024 static tdm_error
1025 _voutput_buffer_send(void *user_data)
1026 {
1027         tdm_test_server_voutput *voutput = user_data;
1028         tdm_test_server *data = voutput->data;
1029
1030         tdm_display_unlock(data->display);
1031
1032         _vlayer_show_buffer(voutput);
1033
1034         tdm_display_lock(data->display);
1035
1036         return TDM_ERROR_NONE;
1037 }
1038
1039 static void
1040 _tdm_test_server_cb_output_change(tdm_output *output, tdm_output_change_type type, tdm_value value, void *user_data)
1041 {
1042         tdm_test_server_voutput *voutput = NULL;
1043         tdm_test_server *data = NULL;
1044         const tdm_output_mode *mode;
1045         tdm_output_conn_status status;
1046         tdm_error ret = TDM_ERROR_NONE;
1047
1048         voutput = (tdm_test_server_voutput *)user_data;
1049         TDM_EXIT_IF_FAIL(voutput != NULL);
1050
1051         data = voutput->data;
1052
1053         switch (type) {
1054         case TDM_OUTPUT_CHANGE_CONNECTION:
1055                 status = (tdm_output_conn_status)value.u32;
1056                 if (status == TDM_OUTPUT_CONN_STATUS_CONNECTED) {
1057                         _voutput_connect(voutput);
1058                         _voutput_layer_init(voutput);
1059
1060                         tdm_display_lock(data->display);
1061                         ret = tdm_event_loop_source_timer_update(voutput->timer, 10);
1062                         tdm_display_unlock(data->display);
1063                         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1064                 } else if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
1065                         _voutput_disconnect(voutput);
1066                 } else if (status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) {
1067                         ret = tdm_output_get_mode(output, &mode);
1068                         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1069
1070                         printf("mode setted (%dx%d, %d)\n", mode->hdisplay, mode->vdisplay, mode->vrefresh);
1071                 }
1072
1073                 break;
1074         default:
1075                 break;
1076         }
1077 }
1078
1079 static void
1080 _tdm_test_server_cb_output_mode_change(tdm_output *output, unsigned int index, void *user_data)
1081 {
1082         tdm_test_server_voutput *voutput = NULL;
1083         const tdm_output_mode *modes, *mode;
1084         int count = 0;
1085         tdm_error ret;
1086
1087         voutput = (tdm_test_server_voutput *)user_data;
1088         TDM_EXIT_IF_FAIL(voutput != NULL);
1089
1090         ret = tdm_output_get_available_modes(output, &modes, &count);
1091         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
1092         TDM_RETURN_IF_FAIL(index < count);
1093
1094
1095         mode = &modes[index];
1096
1097         if (mode == voutput->current_mode) return;
1098
1099         voutput->need_mode_change = 1;
1100         voutput->mode_index = index;
1101
1102         printf("request mode change to %d (%dx%d, %d) from (%dx%d, %d)\n",
1103                 index, mode->hdisplay, mode->vdisplay, mode->vrefresh,
1104                 voutput->current_mode->hdisplay, voutput->current_mode->vdisplay, voutput->current_mode->vrefresh);
1105 }
1106
1107 static void
1108 _tdm_output_cb_destroy_handler(tdm_output *output, void *user_data)
1109 {
1110         tdm_test_server_voutput *voutput = NULL;
1111
1112         voutput = (tdm_test_server_voutput *)user_data;
1113         TDM_EXIT_IF_FAIL(voutput != NULL);
1114
1115         tdm_output_remove_mode_change_request_handler(output, _tdm_test_server_cb_output_mode_change, voutput);
1116         tdm_output_remove_change_handler(output, _tdm_test_server_cb_output_change, voutput);
1117         tdm_output_remove_destroy_handler(output, _tdm_output_cb_destroy_handler, voutput);
1118
1119         LIST_DEL(&voutput->link);
1120
1121         _voutput_buff_deinit(voutput);
1122
1123         printf("voutput: %p destroy\n", voutput);
1124
1125         free(voutput);
1126 }
1127
1128 static void
1129 _tdm_output_cb_create_handler(tdm_display *dpy, tdm_output *output, void *user_data)
1130 {
1131         tdm_test_server *data;
1132         tdm_test_server_voutput *voutput = NULL;
1133         tdm_error ret = TDM_ERROR_NONE;
1134
1135         printf("voutput create call\n");
1136
1137         data = (tdm_test_server *)user_data;
1138         TDM_EXIT_IF_FAIL(data != NULL);
1139
1140         voutput = calloc(1, sizeof *voutput);
1141         TDM_EXIT_IF_FAIL(voutput != NULL);
1142
1143         ret = tdm_output_add_change_handler(output, _tdm_test_server_cb_output_change, voutput);
1144         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1145
1146         ret = tdm_output_add_mode_change_request_handler(output, _tdm_test_server_cb_output_mode_change, voutput);
1147         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1148
1149         ret = tdm_output_add_destroy_handler(output, _tdm_output_cb_destroy_handler, voutput);
1150         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1151
1152         voutput->output = output;
1153         voutput->data = data;
1154
1155         tdm_display_lock(data->display);
1156         voutput->timer = tdm_event_loop_add_timer_handler(data->display, _voutput_buffer_send, voutput, &ret);
1157         tdm_display_unlock(data->display);
1158         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1159
1160         printf("voutput %p create done\n", voutput);
1161
1162         LIST_ADDTAIL(&voutput->link, &data->voutput_list);
1163 }
1164
1165 static void
1166 destroy(tdm_test_server *data)
1167 {
1168         tdm_test_server_output *o = NULL, *oo = NULL;
1169         tdm_test_server_layer *l = NULL, *ll = NULL;
1170         tdm_test_server_pp *p = NULL, *pp = NULL;
1171         tdm_test_server_capture *c = NULL, *cc = NULL;
1172         tdm_test_server_prop *w = NULL, *ww = NULL;
1173         tdm_test_server_voutput *v = NULL, *vv = NULL;
1174         tdm_error ret;
1175         int i;
1176
1177         LIST_FOR_EACH_ENTRY_SAFE(c, cc, &data->capture_list, link) {
1178                 LIST_DEL(&c->link);
1179                 tdm_capture_destroy(c->capture);
1180                 free(c);
1181         }
1182
1183         LIST_FOR_EACH_ENTRY_SAFE(p, pp, &data->pp_list, link) {
1184                 tdm_display_lock(data->display);
1185                 tdm_event_loop_source_remove(p->timer_source);
1186                 tdm_display_unlock(data->display);
1187
1188                 tdm_pp_destroy(p->pp);
1189         }
1190
1191         LIST_FOR_EACH_ENTRY_SAFE(o, oo, &data->output_list, link) {
1192                 LIST_FOR_EACH_ENTRY_SAFE(l, ll, &o->layer_list, link) {
1193                         ret = tdm_layer_unset_buffer(l->layer);
1194                         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
1195                 }
1196                 ret = tdm_layer_commit(l->layer, NULL, NULL);
1197                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1198         }
1199
1200         LIST_FOR_EACH_ENTRY_SAFE(p, pp, &data->pp_list, link) {
1201                 LIST_DEL(&p->link);
1202                 for (i = 0; i < TDM_ARRAY_SIZE(p->bufs); i++)
1203                         tbm_surface_destroy(p->bufs[i]);
1204                 free(p);
1205         }
1206
1207         LIST_FOR_EACH_ENTRY_SAFE(o, oo, &data->output_list, link) {
1208                 LIST_DEL(&o->link);
1209                 LIST_FOR_EACH_ENTRY_SAFE(l, ll, &o->layer_list, link) {
1210                         LIST_DEL(&l->link);
1211                         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &l->prop_list, link) {
1212                                 LIST_DEL(&w->link);
1213                                 free(w);
1214                         }
1215                         for (i = 0; i < TDM_ARRAY_SIZE(l->bufs); i++)
1216                                 tbm_surface_destroy(l->bufs[i]);
1217                         free(l);
1218                 }
1219                 LIST_FOR_EACH_ENTRY_SAFE(w, ww, &o->prop_list, link) {
1220                         LIST_DEL(&w->link);
1221                         free(w);
1222                 }
1223
1224                 free(o);
1225         }
1226
1227         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &data->voutput_list, link) {
1228                 _voutput_buff_deinit(v);
1229                 LIST_DEL(&v->link);
1230                 free(v);
1231         }
1232
1233         if (data->do_voutput)
1234                 tdm_display_remove_output_create_handler(data->display, _tdm_output_cb_create_handler, data);
1235
1236         if (data->display)
1237                 tdm_display_deinit(data->display);
1238
1239         exit(0);
1240 }
1241
1242 int
1243 main(int argc, char *argv[])
1244 {
1245         tdm_test_server *data = &tts_data;
1246         char temp[TDM_SERVER_REPLY_MSG_LEN];
1247         int len = sizeof temp;
1248         tdm_error ret;
1249
1250 #if 1 /* for testing */
1251         const char *s = (const char*)getenv("XDG_RUNTIME_DIR");
1252         if (!s) {
1253                 char buf[32];
1254                 snprintf(buf, sizeof(buf), "/run");
1255                 int ret = setenv("XDG_RUNTIME_DIR", (const char*)buf, 1);
1256                 if (ret != 0)
1257                         exit(0);
1258         }
1259
1260         s = (const char*)getenv("TBM_DISPLAY_SERVER");
1261         if (!s) {
1262                 char buf[32];
1263                 snprintf(buf, sizeof(buf), "1");
1264                 int ret = setenv("TBM_DISPLAY_SERVER", (const char*)buf, 1);
1265                 if (ret != 0)
1266                         exit(0);
1267         }
1268 #endif
1269
1270         memset(data, 0, sizeof * data);
1271         LIST_INITHEAD(&data->output_list);
1272         LIST_INITHEAD(&data->pp_list);
1273         LIST_INITHEAD(&data->capture_list);
1274         LIST_INITHEAD(&data->voutput_list);
1275
1276         /* init value */
1277         data->bflags = TBM_BO_SCANOUT;
1278         data->b_fill = PATTERN_SMPTE;
1279
1280         data->display = tdm_display_init(&ret);
1281         TDM_EXIT_IF_FAIL(data->display != NULL);
1282
1283         parse_args(data, argc, argv);
1284         interpret_args(data);
1285
1286         if (data->do_query) {
1287                 tdm_helper_get_display_information(data->display, temp, &len);
1288                 printf("%s", temp);
1289                 goto done;
1290         }
1291
1292         if (data->do_voutput) {
1293                 printf("support virtual output - server\n");
1294                 ret = tdm_display_add_output_create_handler(data->display, _tdm_output_cb_create_handler, data);
1295                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1296         }
1297
1298         run_test(data);
1299
1300 done:
1301         tdm_display_deinit(data->display);
1302
1303         return 0;
1304 }
1305
1306 static void
1307 output_setup(tdm_test_server_output *o)
1308 {
1309         tdm_test_server *data = o->data;
1310         const tdm_output_mode *modes, *found = NULL, *best = NULL, *prefer = NULL;
1311         tdm_test_server_prop *w = NULL;
1312         const tdm_prop *props;
1313         int i, count;
1314         tdm_error ret;
1315
1316         o->output = tdm_display_get_output(data->display, o->idx, &ret);
1317         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1318
1319         ret = tdm_output_get_available_modes(o->output, &modes, &count);
1320         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1321
1322         for (i = 0; i < count; i++) {
1323                 if (!strncmp(o->mode, modes[i].name, TDM_NAME_LEN) && o->refresh == modes[i].vrefresh) {
1324                         found = &modes[i];
1325                         printf("found mode: %dx%d %d\n", found->hdisplay, found->vdisplay, found->vrefresh);
1326                         break;
1327                 }
1328                 if (!best)
1329                         best = &modes[i];
1330                 if (modes[i].type & TDM_OUTPUT_MODE_TYPE_PREFERRED)
1331                         prefer = &modes[i];
1332         }
1333         if (!found && prefer) {
1334                 found = prefer;
1335                 printf("found prefer mode: %dx%d %d\n", found->hdisplay, found->vdisplay, found->vrefresh);
1336         }
1337         if (!found && best) {
1338                 found = best;
1339                 printf("found best mode: %dx%d %d\n", found->hdisplay, found->vdisplay, found->vrefresh);
1340         }
1341
1342         if (!found) {
1343                 printf("couldn't find any mode\n");
1344                 exit(0);
1345         }
1346
1347         ret = tdm_output_set_mode(o->output, found);
1348         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1349
1350         printf("output %d: %s %d\n", o->idx, found->name, found->vrefresh);
1351
1352         ret = tdm_output_get_available_properties(o->output, &props, &count);
1353         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1354
1355         LIST_FOR_EACH_ENTRY(w, &o->prop_list, link) {
1356                 for (i = 0; i < count; i++) {
1357                         if (strncmp(w->name, props[i].name, TDM_NAME_LEN))
1358                                 continue;
1359                         ret = tdm_output_set_property(o->output, props[i].id, w->value);
1360                         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1361                         printf("\tprop '%s': %d\n", props[i].name, w->value.u32);
1362                         break;
1363                 }
1364         }
1365
1366         /* DPMS on forcely at the first time. */
1367         ret = tdm_output_set_dpms(o->output, TDM_OUTPUT_DPMS_ON);
1368         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1369 }
1370
1371 static tbm_surface_h
1372 layer_get_buffer(tdm_test_server_layer *l)
1373 {
1374         int i, size = TDM_ARRAY_SIZE(l->bufs);
1375         if (!l->bufs[0]) {
1376                 for (i = 0; i < size; i++) {
1377                         int width = (l->info.src_config.size.h) ? : l->info.src_config.pos.w;
1378                         int height = (l->info.src_config.size.v) ? : l->info.src_config.pos.h;
1379                         unsigned int format = (l->info.src_config.format) ? : DEFAULT_FORMAT;
1380                         int flags = l->o->data->bflags;
1381                         tbm_surface_h b = buffer_allocate(width, height, format, flags);
1382                         TDM_EXIT_IF_FAIL(b != NULL);
1383                         l->bufs[i] = b;
1384                 }
1385         }
1386         for (i = 0; i < size; i++) {
1387                 tdm_test_server_buffer *tb = get_tts_buffer(l->bufs[i]);
1388                 TDM_EXIT_IF_FAIL(tb != NULL);
1389                 if (!tb->in_use)
1390                         return l->bufs[i];
1391         }
1392         printf("no available layer buffer.\n");
1393         exit(0);
1394 }
1395
1396 static void
1397 layer_cb_commit(tdm_layer *layer, unsigned int sequence,
1398                                 unsigned int tv_sec, unsigned int tv_usec,
1399                                 void *user_data)
1400 {
1401         tdm_test_server_layer *l = user_data;
1402         tbm_surface_h b = layer_get_buffer(l);
1403         TDM_EXIT_IF_FAIL(b != NULL);
1404
1405         if (!l->is_primary || l->o->fill_primary_layer)
1406                 tdm_test_buffer_fill(b, l->data->b_fill);
1407
1408         if (!l->is_primary || l->o->fill_primary_layer)
1409                 layer_show_buffer(l, b);
1410 }
1411
1412 static void
1413 layer_cb_buffer_release(tbm_surface_h b, void *user_data)
1414 {
1415         tdm_test_server_buffer *tb = get_tts_buffer(b);
1416         TDM_EXIT_IF_FAIL(tb != NULL);
1417
1418         tb->in_use = 0;
1419         tdm_buffer_remove_release_handler(b, layer_cb_buffer_release, NULL);
1420         if (tb->done)
1421                 tb->done(b, user_data);
1422 }
1423
1424 static void
1425 layer_show_buffer(tdm_test_server_layer *l, tbm_surface_h b)
1426 {
1427         tdm_test_server *data = l->o->data;
1428         tdm_test_server_buffer *tb;
1429         tdm_error ret;
1430
1431         ret = tdm_layer_set_buffer(l->layer, b);
1432         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1433
1434         tb = get_tts_buffer(b);
1435         TDM_EXIT_IF_FAIL(tb != NULL);
1436
1437         tb->in_use = 1;
1438         ret = tdm_buffer_add_release_handler(b, layer_cb_buffer_release, NULL);
1439         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1440
1441         if (data->do_vblank)
1442                 ret = tdm_layer_commit(l->layer, layer_cb_commit, l);
1443         else
1444                 ret = tdm_layer_commit(l->layer, NULL, NULL);
1445         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1446
1447         printf("show:\tl(%p) b(%p)\n", l, b);
1448 }
1449
1450 static void
1451 layer_setup(tdm_test_server_layer *l, tbm_surface_h b)
1452 {
1453         tdm_test_server_prop *w = NULL;
1454         const tdm_prop *props;
1455         int i, count;
1456         tdm_error ret;
1457         tbm_surface_info_s info;
1458
1459         l->layer = tdm_output_get_layer(l->o->output, l->idx, &ret);
1460         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1461
1462         /* The size and format information should be same with buffer's */
1463         tbm_surface_get_info(b, &info);
1464         if (IS_RGB(info.format)) {
1465                 l->info.src_config.size.h = info.planes[0].stride >> 2;
1466                 l->info.src_config.size.v = info.height;
1467         } else {
1468                 l->info.src_config.size.h = info.planes[0].stride;
1469                 l->info.src_config.size.v = info.height;
1470         }
1471         l->info.src_config.format = info.format;
1472
1473         ret = tdm_layer_set_info(l->layer, &l->info);
1474         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1475
1476         printf("layer %d: output(%d) ", l->idx, l->o->idx);
1477         print_config(&l->info.src_config);
1478         printf(" ! ");
1479         print_pos(&l->info.dst_pos);
1480         printf(" transform(%s)\n", tdm_transform_str(l->info.transform));
1481
1482         ret = tdm_layer_get_available_properties(l->layer, &props, &count);
1483         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1484
1485         LIST_FOR_EACH_ENTRY(w, &l->prop_list, link) {
1486                 for (i = 0; i < count; i++) {
1487                         if (strncmp(w->name, props[i].name, TDM_NAME_LEN))
1488                                 continue;
1489                         ret = tdm_layer_set_property(l->layer, props[i].id, w->value);
1490                         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1491                         printf("\tprop '%s': %d\n", props[i].name, w->value.u32);
1492                         break;
1493                 }
1494         }
1495 }
1496
1497 static tbm_surface_h
1498 pp_get_buffer(tdm_test_server_pp *p)
1499 {
1500         int i, size = TDM_ARRAY_SIZE(p->bufs);
1501         if (!p->bufs[0]) {
1502                 for (i = 0; i < size; i++) {
1503                         int width = (p->info.src_config.size.h) ? : p->info.src_config.pos.w;
1504                         int height = (p->info.src_config.size.v) ? : p->info.src_config.pos.h;
1505                         unsigned int format = (p->info.src_config.format) ? : DEFAULT_FORMAT;
1506                         tbm_surface_h b = buffer_allocate(width, height, format, 0);
1507                         TDM_EXIT_IF_FAIL(b != NULL);
1508                         p->bufs[i] = b;
1509                 }
1510         }
1511         for (i = 0; i < size; i++) {
1512                 tdm_test_server_buffer *tb = get_tts_buffer(p->bufs[i]);
1513
1514                 if (tb == NULL)
1515                         continue;
1516
1517                 if (!tb->in_use) {
1518                         tdm_test_buffer_fill(p->bufs[i], p->data->b_fill);
1519                         return p->bufs[i];
1520                 }
1521         }
1522         printf("no available pp buffer.\n");
1523         exit(0);
1524 }
1525
1526 static void
1527 pp_cb_done(tdm_pp *pp, tbm_surface_h sb, tbm_surface_h db, void *user_data)
1528 {
1529         tdm_test_server_buffer *stb, *dtb;
1530
1531         stb = get_tts_buffer(sb);
1532         TDM_EXIT_IF_FAIL(stb != NULL);
1533
1534         dtb = get_tts_buffer(db);
1535         TDM_EXIT_IF_FAIL(dtb != NULL);
1536
1537         stb->in_use = dtb->in_use = 0;
1538
1539         layer_show_buffer(dtb->l, db);
1540 }
1541
1542 static void
1543 pp_convert_buffer(tdm_test_server_pp *p, tbm_surface_h sb, tbm_surface_h db)
1544 {
1545         tdm_test_server_buffer *stb, *dtb;
1546         tdm_error ret;
1547
1548         stb = get_tts_buffer(sb);
1549         TDM_EXIT_IF_FAIL(stb != NULL);
1550
1551         dtb = get_tts_buffer(db);
1552         TDM_EXIT_IF_FAIL(dtb != NULL);
1553
1554         stb->in_use = dtb->in_use = 1;
1555         dtb->l = p->l;
1556
1557         ret = tdm_pp_attach(p->pp, sb, db);
1558         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1559
1560         ret = tdm_pp_commit(p->pp);
1561         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1562
1563         printf("convt:\tp(%p) sb(%p) db(%p)\n", p, sb, db);
1564 }
1565
1566 /* tdm_event_loop_xxx() function is not for the display server. It's for TDM
1567  * backend module. I use them only for simulating the video play. When we call
1568  * tdm_event_loop_xxx() outside of TDM backend module, we have to lock/unlock
1569  * the TDM display. And when the callback function of tdm_event_loop_xxx() is
1570  * called, the display has been already locked. So in this test application,
1571  * we have to unlock/lock the display for testing.
1572  */
1573 static tdm_error
1574 pp_cb_timeout(void *user_data)
1575 {
1576         tdm_test_server_pp *p = user_data;
1577         tdm_test_server *data = p->l->o->data;
1578         tbm_surface_h sb, db;
1579         tdm_error ret;
1580
1581         tdm_display_unlock(data->display);
1582
1583         sb = pp_get_buffer(p);
1584         TDM_EXIT_IF_FAIL(sb != NULL);
1585         db = layer_get_buffer(p->l);
1586         TDM_EXIT_IF_FAIL(db != NULL);
1587
1588         pp_convert_buffer(p, sb, db);
1589
1590         tdm_display_lock(data->display);
1591         ret = tdm_event_loop_source_timer_update(p->timer_source, 1000 / p->fps);
1592         tdm_display_unlock(data->display);
1593         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1594
1595         tdm_display_lock(data->display);
1596
1597         return TDM_ERROR_NONE;
1598 }
1599
1600 static void
1601 pp_setup(tdm_test_server_pp *p, tbm_surface_h sb, tbm_surface_h db)
1602 {
1603         tdm_test_server *data;
1604         tbm_surface_info_s info;
1605         tdm_error ret;
1606
1607         if (!p || !p->l || !p->l->o || !p->l->o->data) {
1608                 TDM_ERR("invalid parameter failed");
1609                 exit(0);
1610         }
1611         data = p->l->o->data;
1612
1613         p->pp = tdm_display_create_pp(data->display, &ret);
1614         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1615
1616         /* The size and format information should be same with buffer's */
1617         tbm_surface_get_info(sb, &info);
1618         if (IS_RGB(info.format)) {
1619                 p->info.src_config.size.h = info.planes[0].stride >> 2;
1620                 p->info.src_config.size.v = info.height;
1621         } else {
1622                 p->info.src_config.size.h = info.planes[0].stride;
1623                 p->info.src_config.size.v = info.height;
1624         }
1625         p->info.src_config.format = info.format;
1626
1627         /* The size and format information should be same with buffer's */
1628         tbm_surface_get_info(db, &info);
1629         if (IS_RGB(info.format)) {
1630                 p->info.dst_config.size.h = info.planes[0].stride >> 2;
1631                 p->info.dst_config.size.v = info.height;
1632         } else {
1633                 p->info.dst_config.size.h = info.planes[0].stride;
1634                 p->info.dst_config.size.v = info.height;
1635         }
1636         p->info.dst_config.format = info.format;
1637
1638         ret = tdm_pp_set_info(p->pp, &p->info);
1639         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1640
1641         ret = tdm_pp_set_done_handler(p->pp, pp_cb_done, NULL);
1642         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1643
1644         printf("pp: ");
1645         print_config(&p->info.src_config);
1646         printf(" ! ");
1647         print_config(&p->info.dst_config);
1648         printf(" fps(%d) transform(%s)\n", p->fps, tdm_transform_str(p->info.transform));
1649         printf("\toutput_idx(%d) layer_idx(%d)\n", p->l->o->idx, p->l->idx);
1650
1651         layer_setup(p->l, db);
1652
1653         /* tdm_event_loop_xxx() function is not for the display server. It's for TDM
1654          * backend module. I use them only for simulating the video play. When we call
1655          * tdm_event_loop_xxx() outside of TDM backend module, we have to lock/unlock
1656          * the TDM display. And when the callback function of tdm_event_loop_xxx() is
1657          * called, the display has been already locked. So in this test application,
1658          * we have to unlock/lock the display for testing.
1659          */
1660         tdm_display_lock(data->display);
1661         p->timer_source = tdm_event_loop_add_timer_handler(data->display, pp_cb_timeout, p, &ret);
1662         tdm_display_unlock(data->display);
1663         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1664
1665         tdm_display_lock(data->display);
1666         ret = tdm_event_loop_source_timer_update(p->timer_source, 1000 / p->fps);
1667         tdm_display_unlock(data->display);
1668         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1669 }
1670
1671 static void
1672 capture_cb_buffer_done(tbm_surface_h b, void *user_data)
1673 {
1674         tdm_test_server_buffer *tb;
1675
1676         tb = get_tts_buffer(b);
1677         TDM_EXIT_IF_FAIL(tb != NULL);
1678
1679         capture_attach(tb->c, b);
1680 }
1681
1682 static void
1683 capture_cb_done(tdm_capture *capture, tbm_surface_h b, void *user_data)
1684 {
1685         tdm_test_server_buffer *tb;
1686
1687         tb = get_tts_buffer(b);
1688         TDM_EXIT_IF_FAIL(tb != NULL);
1689
1690         tb->in_use = 0;
1691
1692         tb->done = capture_cb_buffer_done;
1693         layer_show_buffer(tb->l, b);
1694 }
1695
1696 static void
1697 capture_attach(tdm_test_server_capture *c, tbm_surface_h b)
1698 {
1699         tdm_error ret;
1700         tdm_test_server_buffer *tb;
1701
1702         tb = get_tts_buffer(b);
1703         TDM_EXIT_IF_FAIL(tb != NULL);
1704
1705         tb->in_use = 1;
1706         tb->l = c->l;
1707         tb->c = c;
1708
1709         ret = tdm_capture_attach(c->capture, b);
1710         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1711
1712         printf("capture:\tc(%p) b(%p)\n", c, b);
1713
1714         ret = tdm_capture_commit(c->capture);
1715         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1716 }
1717
1718 static void
1719 capture_setup(tdm_test_server_capture *c, tbm_surface_h b)
1720 {
1721         tdm_test_server *data;
1722         tdm_output *output;
1723         tdm_layer *layer;
1724         tbm_surface_info_s info;
1725         tdm_error ret;
1726
1727         if (!c || !c->l || !c->l->o || !c->l->o->data) {
1728                 TDM_ERR("invalid parameter failed");
1729                 exit(0);
1730         }
1731         data = c->l->o->data;
1732
1733         if (c->output_idx != -1 && c->layer_idx == -1) {
1734                 output = tdm_display_get_output(data->display, c->output_idx, &ret);
1735                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1736                 c->capture = tdm_output_create_capture(output, &ret);
1737                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1738         } else if (c->output_idx != -1 && c->layer_idx != -1) {
1739                 output = tdm_display_get_output(data->display, c->output_idx, &ret);
1740                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1741                 layer = tdm_output_get_layer(output, c->layer_idx, &ret);
1742                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1743                 c->capture = tdm_layer_create_capture(layer, &ret);
1744                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1745         }
1746
1747         TDM_EXIT_IF_FAIL(c->capture != NULL);
1748
1749         /* The size and format information should be same with buffer's */
1750         tbm_surface_get_info(b, &info);
1751         if (IS_RGB(info.format)) {
1752                 c->info.dst_config.size.h = info.planes[0].stride >> 2;
1753                 c->info.dst_config.size.v = info.height;
1754         } else {
1755                 c->info.dst_config.size.h = info.planes[0].stride;
1756                 c->info.dst_config.size.v = info.height;
1757         }
1758         c->info.dst_config.format = info.format;
1759         c->info.type = TDM_CAPTURE_TYPE_ONESHOT;
1760
1761         ret = tdm_capture_set_info(c->capture, &c->info);
1762         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1763
1764         ret = tdm_capture_set_done_handler(c->capture, capture_cb_done, NULL);
1765         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1766
1767         printf("capture: o(%d) l(%d) ", c->output_idx, c->layer_idx);
1768         print_config(&c->info.dst_config);
1769         printf(" transform(%s)\n", tdm_transform_str(c->info.transform));
1770         printf("\toutput_idx(%d) layer_idx(%d)\n", c->l->o->idx, c->l->idx);
1771
1772         layer_setup(c->l, b);
1773 }
1774
1775 static void
1776 run_test(tdm_test_server *data)
1777 {
1778         tdm_test_server_output *o = NULL;
1779         tdm_test_server_layer *l = NULL;
1780         tdm_test_server_pp *p = NULL;
1781         tdm_test_server_capture *c = NULL;
1782         tdm_display_capability caps;
1783         tdm_error ret;
1784
1785         ret = tdm_display_get_capabilities(data->display, &caps);
1786         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1787
1788         LIST_FOR_EACH_ENTRY(o, &data->output_list, link) {
1789                 LIST_FOR_EACH_ENTRY(l, &o->layer_list, link) {
1790                         if (!l->owner_p && !l->owner_c) {
1791                                 tbm_surface_h b;
1792                                 b = layer_get_buffer(l);
1793                                 if (!l->is_primary || l->o->fill_primary_layer)
1794                                         tdm_test_buffer_fill(b, data->b_fill);
1795                                 layer_setup(l, b);
1796                                 layer_show_buffer(l, b);
1797                         }
1798                 }
1799         }
1800
1801         LIST_FOR_EACH_ENTRY(p, &data->pp_list, link) {
1802                 tbm_surface_h sb, db;
1803                 TDM_GOTO_IF_FAIL(caps & TDM_DISPLAY_CAPABILITY_PP, no_pp);
1804                 sb = pp_get_buffer(p);
1805                 TDM_EXIT_IF_FAIL(sb != NULL);
1806                 db = layer_get_buffer(p->l);
1807                 TDM_EXIT_IF_FAIL(db != NULL);
1808                 pp_setup(p, sb, db);
1809                 pp_convert_buffer(p, sb, db);
1810         }
1811
1812         LIST_FOR_EACH_ENTRY(c, &data->capture_list, link) {
1813                 TDM_GOTO_IF_FAIL(caps & TDM_DISPLAY_CAPABILITY_CAPTURE, no_capture);
1814                 tbm_surface_h b;
1815                 b = layer_get_buffer(c->l);
1816                 capture_setup(c, b);
1817                 capture_attach(c, b);
1818                 b = layer_get_buffer(c->l);
1819                 capture_attach(c, b);
1820                 b = layer_get_buffer(c->l);
1821                 capture_attach(c, b);
1822         }
1823
1824         printf("enter test loop\n");
1825
1826         while (1) {
1827                 ret = tdm_display_handle_events(data->display);
1828                 TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
1829         }
1830
1831         destroy(data);
1832
1833         return;
1834 no_pp:
1835         printf("no PP capability\n");
1836         exit(0);
1837 no_capture:
1838         printf("no Capture capability\n");
1839         exit(0);
1840 }