build: fix build warnings.
[profile/ivi/speech-recognition.git] / src / plugins / client-api / dbus / test-client.c
1 /*
2  * Copyright (c) 2012, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *   * Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *   * Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *   * Neither the name of Intel Corporation nor the names of its contributors
14  *     may be used to endorse or promote products derived from this software
15  *     without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <getopt.h>
34 #include <stdarg.h>
35
36 #include <murphy/common/macros.h>
37 #include <murphy/common/mm.h>
38 #include <murphy/common/debug.h>
39 #include <murphy/common/mainloop.h>
40 #include <murphy/common/dbus-libdbus.h>
41 #include <murphy/common/pulse-glue.h>
42
43 #include <breedline/breedline-murphy.h>
44
45 #include "dbus-config.h"
46
47
48 static const char *default_commands[] = {
49     "hal open the pod bay doors",
50     "hal play music",
51     "hal stop music",
52     "hal exit",
53     "hal dial __push_dict__(digits) *",
54     "hal play artist __push_dict__(artists) *"
55 };
56
57
58 typedef struct {
59     pa_mainloop    *pa;
60     mrp_mainloop_t *ml;
61     mrp_dbus_t     *dbus;
62     brl_t          *brl;
63     const char     *app_class;
64     const char     *app_name;
65     const char     *dbus_address;
66     int             exit_status;
67     int             server_up : 1;
68     int             registered : 1;
69     const char     *focus;
70     char          **commands;
71     int             ncommand;
72     int             autoregister : 1;
73     const char     *autofocus;
74     uint32_t        vreq;
75 } client_t;
76
77
78 static void register_client(client_t *c);
79 static void request_focus(client_t *c, const char *focus);
80 static void execute_user_command(client_t *c, int narg, char **args);
81 static void request_render_voice(client_t *c, const char *msg, const char *vid,
82                                  int timeout, int subscribe);
83 static void request_cancel_voice(client_t *c, uint32_t id);
84 static void query_voices(client_t *c, const char *language);
85
86 static void set_prompt(client_t *c, const char *prompt)
87 {
88     brl_set_prompt(c->brl, prompt);
89 }
90
91
92 static void show_prompt(client_t *c)
93 {
94     if (c->brl != NULL)
95         brl_show_prompt(c->brl);
96 }
97
98
99 static void hide_prompt(client_t *c)
100 {
101     if (c->brl != NULL)
102         brl_hide_prompt(c->brl);
103 }
104
105
106 static void print(client_t *c, const char *format, ...)
107 {
108     va_list ap;
109
110     hide_prompt(c);
111
112     va_start(ap, format);
113     vfprintf(stdout, format, ap);
114     fputc('\n', stdout);
115     fflush(stdout);
116     va_end(ap);
117
118     show_prompt(c);
119 }
120
121
122 static char *concat_tokens(char *buf, int size, int ntoken, char **tokens)
123 {
124     char   *p, *t;
125     int     l, n;
126
127     p = buf;
128     t = "";
129     l = size - 1;
130
131     while (ntoken > 0) {
132         n  = snprintf(p, l, "%s%s", t, tokens[0]);
133
134         if (n >= l)
135             return NULL;
136
137         p += n;
138         l -= n;
139         t = " ";
140         ntoken--;
141         tokens++;
142     }
143
144     buf[size - 1] = '\0';
145     return buf;
146 }
147
148
149 static void add_command(client_t *c, int ntoken, char **tokens)
150 {
151     char   command[1024];
152     size_t osize, nsize;
153
154     if (c->registered) {
155         print(c, "You need to unregister first to modify commands.");
156         return;
157     }
158
159     if (concat_tokens(command, sizeof(command), ntoken, tokens) == NULL) {
160         print(c, "Command too long.");
161         return;
162     }
163
164     osize = sizeof(*c->commands) *  c->ncommand;
165     nsize = sizeof(*c->commands) * (c->ncommand + 1);
166
167     if (!mrp_reallocz(c->commands, osize, nsize)) {
168         print(c, "Failed to add new command.");
169         return;
170     }
171
172     c->commands[c->ncommand] = mrp_strdup(command);
173
174     if (c->commands[c->ncommand] != NULL) {
175         c->ncommand++;
176         print(c, "Command '%s' added to command set.", command);
177     }
178     else
179         print(c, "Failed to register new command.");
180 }
181
182
183 static void del_command(client_t *c, int ntoken, char **tokens)
184 {
185     char command[1024];
186     int  i;
187
188     if (c->registered) {
189         print(c, "You need to unregister first to modify commands.");
190         return;
191     }
192
193     if (concat_tokens(command, sizeof(command), ntoken, tokens) == NULL) {
194         print(c, "Command too long.");
195         return;
196     }
197
198     for (i = 0; i < c->ncommand; i++) {
199         if (!strcmp(c->commands[i], command)) {
200             if (i < c->ncommand - 1)
201                 memmove(c->commands + i + 1, c->commands + i,
202                         (c->ncommand - 1 - i) * sizeof(*c->commands));
203
204             c->ncommand--;
205             mrp_realloc(c->commands, sizeof(*c->commands) * c->ncommand);
206
207             print(c, "Command '%s' deleted.", command);
208         }
209     }
210 }
211
212
213 static void reset_commands(client_t *c)
214 {
215     int i;
216
217     if (c->registered){
218         print(c, "You need to unregister first to modify commands.");
219         return;
220     }
221
222     for (i = 0; i < c->ncommand; i++)
223         mrp_free(c->commands[i]);
224     mrp_free(c->commands);
225
226     c->commands = NULL;
227     c->ncommand = 0;
228
229     print(c, "Commands resetted, no current commands.");
230 }
231
232
233 static void list_commands(client_t *c)
234 {
235     int i;
236
237     if (c->ncommand > 0) {
238         print(c, "Command set:");
239         for (i = 0; i < c->ncommand; i++)
240             print(c, "  %s", c->commands[i]);
241     }
242     else
243         print(c, "No commands defined.");
244 }
245
246
247 static void request_tts(client_t *c, int ntoken, char **tokens)
248 {
249     const char *sep     = "";
250     const char *voice   = "english";
251     int         timeout = 5000;
252     int         events  = FALSE;
253     char        msg[1024], *t, *e, *p;
254     int         i, o, n;
255     size_t      l;
256
257     if (c->registered) {
258         print(c, "You need to unregister first to modify commands.");
259         return;
260     }
261
262     p = msg;
263     l = sizeof(msg);
264     for (i = 0; i < ntoken; i++) {
265         t = tokens[i];
266         if (*t == '-') {
267             if (!strncmp(t + 1, "timeout:", o=8)) {
268                 timeout = strtol(t + 1 + o, &e, 10);
269                 if (*e != '\0') {
270                     print(c, "Invalid timeout: %s.", t + 1 + o);
271                     return;
272                 }
273             }
274             else if (!strncmp(t + 1, "events", o=6)) {
275                 events = TRUE;
276             }
277             else if (!strncmp(t + 1, "voice:", o=6)) {
278                 voice = t + 1 + o;
279             }
280         }
281         else {
282             n = snprintf(p, l, "%s%s", sep, t);
283             if ((size_t)n >= l) {
284                 print(c, "TTS message too long.");
285                 return;
286             }
287
288             p += n;
289             l -= n;
290             sep = " ";
291         }
292     }
293
294     print(c, "message: '%s'", msg);
295
296     request_render_voice(c, msg, voice, timeout, events);
297 }
298
299
300 static void cancel_tts(client_t *c, int ntoken, char **tokens)
301 {
302     int       i;
303     uint32_t  vreq;
304     char     *end;
305
306     if (ntoken == 0) {
307         if (c->vreq)
308             request_cancel_voice(c, c->vreq);
309         else
310             print(c, "No outstanding TTS request.");
311     }
312     else {
313         for (i = 0; i < ntoken; i++) {
314             vreq = strtoul(tokens[i], &end, 10);
315
316             if (end && !*end)
317                 request_cancel_voice(c, vreq);
318             else
319                 print(c, "TTS request id '%s' is invalid.", tokens[i]);
320         }
321     }
322 }
323
324
325 static void set_client_defaults(client_t *c, const char *argv0)
326 {
327     int i;
328
329     c->dbus_address = "session";
330     c->app_class    = "player";
331     c->app_name     = strrchr(argv0, '/');
332
333     if (c->app_name != NULL)
334         c->app_name++;
335     else
336         c->app_name = argv0;
337
338     c->commands = mrp_allocz(sizeof(default_commands));
339     c->ncommand = MRP_ARRAY_SIZE(default_commands);
340
341     for (i = 0; i < c->ncommand; i++) {
342         c->commands[i] = mrp_strdup(default_commands[i]);
343         if (c->commands[i] == NULL) {
344             print(c, "Failed to initialize default command set.");
345             exit(1);
346         }
347     }
348 }
349
350
351 static void destroy_client(client_t *c)
352 {
353     if (c != NULL) {
354         mrp_debug("destroying client");
355
356         if (c->ml != NULL)
357             mrp_mainloop_destroy(c->ml);
358
359         if (c->pa != NULL)
360             pa_mainloop_free(c->pa);
361
362         mrp_free(c);
363     }
364 }
365
366
367 static client_t *create_client(const char *argv0)
368 {
369     client_t *c = mrp_allocz(sizeof(*c));
370
371     if (c != NULL) {
372         set_client_defaults(c, argv0);
373
374         c->pa = pa_mainloop_new();
375         c->ml = mrp_mainloop_pulse_get(pa_mainloop_get_api(c->pa));
376
377         if (c->pa != NULL && c->ml != NULL)
378             return c;
379         else
380             destroy_client(c);
381     }
382
383     return NULL;
384 }
385
386
387 static int focus_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
388 {
389     client_t   *c = (client_t *)user_data;
390     const char *focus;
391
392     MRP_UNUSED(dbus);
393
394     hide_prompt(c);
395     if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &focus))
396         print(c, "Voice focus is now: %s", focus);
397     else
398         print(c, "Failed to parse voice focus notification.");
399     show_prompt(c);
400
401     return TRUE;
402 }
403
404
405 static int voice_command_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
406                                 void *user_data)
407 {
408     client_t   *c = (client_t *)user_data;
409     const char *command;
410
411     MRP_UNUSED(dbus);
412
413     hide_prompt(c);
414     if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &command))
415         print(c, "Received voice command: %s", command);
416     else
417         print(c, "Failed to parse voice command notification.");
418     show_prompt(c);
419
420     return TRUE;
421 }
422
423
424 static int voice_render_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
425                                void *user_data)
426 {
427     client_t   *c = (client_t *)user_data;
428     uint32_t    id;
429     const char *event;
430     double      pcnt;
431     uint32_t    msec;
432
433     MRP_UNUSED(dbus);
434
435     hide_prompt(c);
436     if (!mrp_dbus_msg_read_basic(msg, DBUS_TYPE_UINT32, &id) ||
437         !mrp_dbus_msg_read_basic(msg, DBUS_TYPE_STRING, &event)) {
438         print(c, "Failed to parse voice render event notification.");
439         return TRUE;
440     }
441
442     if (!strcmp(event, "progress")) {
443         pcnt = -1.0;
444         msec = (uint32_t)-1;
445         if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &id) &&
446             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &event) &&
447             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_DOUBLE, &pcnt) &&
448             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &msec))
449             print(c, "Rendering <%u> progress: %f %% (%u msecs)", id,
450                   pcnt, msec);
451         else
452             print(c, "Rendering <%u> progress: failed to parse message", id);
453     }
454     else
455         print(c, "Rendering <%u>: %s", id, event);
456     show_prompt(c);
457
458     return TRUE;
459 }
460
461
462 static void server_name_change(mrp_dbus_t *dbus, const char *name, int running,
463                                const char *owner, void *user_data)
464 {
465     client_t *c = (client_t *)user_data;
466
467     MRP_UNUSED(dbus);
468
469     c->server_up = running;
470
471     if (running) {
472         set_prompt(c, "server up");
473         print(c, "Server (%s) is now up (as %s).", name, owner);
474
475         if (c->autoregister)
476             register_client(c);
477     }
478     else {
479         set_prompt(c, "server down");
480         print(c, "Server (%s) is now down.", name);
481         c->registered = FALSE;
482     }
483 }
484
485
486 static void setup_dbus(client_t *c)
487 {
488     const char *name      = SRS_CLIENT_SERVICE;
489     const char *path      = SRS_CLIENT_PATH;
490     const char *interface = SRS_CLIENT_INTERFACE;
491     const char *focus     = SRS_CLIENT_NOTIFY_FOCUS;
492     const char *command   = SRS_CLIENT_NOTIFY_COMMAND;
493     const char *voice     = SRS_CLIENT_NOTIFY_VOICE;
494
495     c->dbus = mrp_dbus_get(c->ml, c->dbus_address, NULL);
496
497     if (c->dbus == NULL) {
498         print(c, "Failed to connect to D-BUS (%s).", c->dbus_address);
499         exit(1);
500     }
501
502     if (mrp_dbus_follow_name(c->dbus, name, server_name_change, c) &&
503         mrp_dbus_subscribe_signal(c->dbus, focus_notify, c,
504                                   NULL, path, interface, focus, NULL) &&
505         mrp_dbus_subscribe_signal(c->dbus, voice_command_notify, c,
506                                   NULL, path, interface, command, NULL) &&
507         mrp_dbus_subscribe_signal(c->dbus, voice_render_notify, c,
508                                   NULL, path, interface, voice, NULL))
509         return;
510
511     print(c, "Failed to set up server D-BUS name tracking.");
512     exit(1);
513 }
514
515
516 static void cleanup_dbus(client_t *c)
517 {
518     const char *name      = SRS_CLIENT_SERVICE;
519     const char *path      = SRS_CLIENT_PATH;
520     const char *interface = SRS_CLIENT_INTERFACE;
521     const char *focus     = SRS_CLIENT_NOTIFY_FOCUS;
522     const char *command   = SRS_CLIENT_NOTIFY_COMMAND;
523     const char *voice     = SRS_CLIENT_NOTIFY_VOICE;
524
525     if (c != NULL) {
526         mrp_dbus_forget_name(c->dbus, name, server_name_change, c);
527         mrp_dbus_unsubscribe_signal(c->dbus, focus_notify, c,
528                                     NULL, path, interface, focus, NULL);
529         mrp_dbus_unsubscribe_signal(c->dbus, voice_command_notify, c,
530                                     NULL, path, interface, command, NULL);
531         mrp_dbus_unsubscribe_signal(c->dbus, voice_render_notify, c,
532                                     NULL, path, interface, voice, NULL);
533         mrp_dbus_unref(c->dbus);
534     }
535 }
536
537
538 static void run_mainloop(client_t *c)
539 {
540     pa_mainloop_run(c->pa, &c->exit_status);
541 }
542
543
544 static void quit_mainloop(client_t *c, int exit_status)
545 {
546     if (c != NULL)
547         pa_mainloop_quit(c->pa, exit_status);
548     else
549         exit(exit_status);
550 }
551
552
553 static void sighandler(mrp_sighandler_t *h, int signum, void *user_data)
554 {
555     client_t *c = (client_t *)user_data;
556
557     MRP_UNUSED(h);
558
559     switch (signum) {
560     case SIGINT:
561         printf("Received SIGINT, exiting...");
562         quit_mainloop(c, 0);
563         break;
564
565     case SIGTERM:
566         printf("Received SIGTERM, exiting...");
567         quit_mainloop(c, 0);
568         break;
569     }
570 }
571
572
573 static void setup_signals(client_t *c)
574 {
575     mrp_add_sighandler(c->ml, SIGINT , sighandler, c);
576     mrp_add_sighandler(c->ml, SIGTERM, sighandler, c);
577 }
578
579
580
581
582 static int split_input(char *input, int narg, char **args)
583 {
584     int   n;
585     char *p;
586
587     n = 0;
588     p = input;
589
590     while (*p) {
591         while (*p == ' ' || *p == '\t')
592             p++;
593
594         args[n++] = p;
595
596         if (n >= narg) {
597             errno = EOVERFLOW;
598             return -1;
599         }
600
601         while (*p && *p != ' ' && *p != '\t')
602             p++;
603
604         if (*p)
605             *p++ = '\0';
606     }
607
608     return n;
609 }
610
611
612 static void process_input(brl_t *brl, const char *input, void *user_data)
613 {
614     client_t *c   = (client_t *)user_data;
615     int       len = input ? strlen(input) + 1 : 0;
616     char      buf[len], *args[64];
617     int       narg;
618
619     if (len > 1) {
620         brl_add_history(brl, input);
621         hide_prompt(c);
622
623         strcpy(buf, input);
624         narg = split_input(buf, MRP_ARRAY_SIZE(args), args);
625         if (narg > 0)
626             execute_user_command(c, narg, &args[0]);
627         else
628             printf("failed to parse input '%s'\n", input);
629
630         show_prompt(c);
631     }
632 }
633
634
635 static void setup_input(client_t *c)
636 {
637     int fd;
638
639     fd     = fileno(stdin);
640     c->brl = brl_create_with_murphy(fd, "starting", c->ml, process_input, c);
641
642     if (c->brl != NULL)
643         brl_show_prompt(c->brl);
644     else {
645         fprintf(stderr, "Failed to initialize breedline for console input.");
646         exit(1);
647     }
648 }
649
650
651 static void cleanup_input(client_t *c)
652 {
653     if (c != NULL && c->brl != NULL) {
654         brl_destroy(c->brl);
655         c->brl = NULL;
656     }
657 }
658
659
660 static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
661 {
662     va_list     ap;
663     const char *exe;
664
665     if (fmt && *fmt) {
666         va_start(ap, fmt);
667         vprintf(fmt, ap);
668         va_end(ap);
669     }
670
671     exe = strrchr(argv0, '/');
672
673     printf("usage: %s [options]\n\n"
674            "The possible options are:\n"
675            "  -N, --name=APPNAME             application name to use\n"
676            "  -C, --class=APPCLASS           application class to use\n"
677            "  -D, --dbus=DBUS                D-BUS to use\n"
678            "      DBUS is 'session', 'system', or a DBUS daemon address.\n"
679            "  -v, --verbose                  increase logging verbosity\n"
680            "  -d, --debug                    enable debug messages\n"
681            "  -R, --register                 automatically register to server\n"
682            "  -F, --focus[=TYPE]             automatically request focus\n"
683            "  -h, --help                     show help on usage\n", exe);
684     printf("\n");
685
686     if (exit_code < 0)
687         return;
688     else
689         exit(exit_code);
690 }
691
692
693 static void parse_cmdline(client_t *c, int argc, char **argv)
694 {
695 #   define OPTIONS "N:C:D:d:RFh"
696     struct option options[] = {
697         { "name"      , required_argument, NULL, 'N' },
698         { "class"     , required_argument, NULL, 'C' },
699         { "dbus"      , required_argument, NULL, 'D' },
700         { "debug"     , required_argument, NULL, 'd' },
701         { "register"  , no_argument      , NULL, 'R' },
702         { "focus"     , optional_argument, NULL, 'F' },
703         { "help"      , no_argument      , NULL, 'h' },
704         { NULL, 0, NULL, 0 }
705     };
706
707     int opt;
708
709     while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
710         switch (opt) {
711         case 'N':
712             c->app_name = optarg;
713             break;
714
715         case 'C':
716             c->app_class = optarg;
717             break;
718
719         case 'D':
720             c->dbus_address = optarg;
721             break;
722
723         case 'd':
724             mrp_debug_set_config(optarg);
725             mrp_debug_enable(TRUE);
726             break;
727
728         case 'R':
729             c->autoregister = TRUE;
730             break;
731
732         case 'F':
733             c->autofocus = optarg ? optarg : "shared";
734             break;
735
736         case 'h':
737             print_usage(argv[0], -1, "");
738             exit(0);
739             break;
740
741         default:
742             print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
743         }
744     }
745 }
746
747
748 static void register_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
749                            void *user_data)
750 {
751     client_t *c = (client_t *)user_data;
752
753     MRP_UNUSED(dbus);
754
755     if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN) {
756         set_prompt(c, c->app_name);
757         print(c, "Successfully registered to server.");
758         if (c->autofocus)
759             request_focus(c, c->autofocus);
760     }
761     else {
762         set_prompt(c, "failed");
763         print(c, "Failed to register to server.");
764     }
765 }
766
767
768 static void register_client(client_t *c)
769 {
770     const char **cmds   = (const char **)c->commands;
771     int          ncmd   = c->ncommand;
772     const char  *dest   = SRS_CLIENT_SERVICE;
773     const char  *path   = SRS_CLIENT_PATH;
774     const char  *iface  = SRS_CLIENT_INTERFACE;
775     const char  *method = SRS_CLIENT_REGISTER;
776
777     if (!c->server_up) {
778         print(c, "Server is currently down.");
779
780         return;
781     }
782
783     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
784                        register_reply, c,
785                        MRP_DBUS_TYPE_STRING, c->app_name,
786                        MRP_DBUS_TYPE_STRING, c->app_class,
787                        MRP_DBUS_TYPE_ARRAY , MRP_DBUS_TYPE_STRING, cmds, ncmd,
788                        MRP_DBUS_TYPE_INVALID))
789         print(c, "Failed to send register message to server.");
790 }
791
792
793 static void unregister_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
794                              void *user_data)
795 {
796     client_t *c = (client_t *)user_data;
797
798     MRP_UNUSED(dbus);
799
800     if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN) {
801         set_prompt(c, "unregistered");
802         print(c, "Successfully unregistered from server.");
803     }
804     else
805         print(c, "Failed to unregister from server.");
806 }
807
808
809 static void unregister_client(client_t *c)
810 {
811     const char  *dest   = SRS_CLIENT_SERVICE;
812     const char  *path   = SRS_CLIENT_PATH;
813     const char  *iface  = SRS_CLIENT_INTERFACE;
814     const char  *method = SRS_CLIENT_UNREGISTER;
815
816     if (!c->server_up) {
817         print(c, "Server is currently down.");
818         return;
819     }
820
821     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
822                        unregister_reply, c, MRP_DBUS_TYPE_INVALID))
823         print(c, "Failed to send unregister message to server.");
824 }
825
826
827 static void focus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
828 {
829     client_t *c = (client_t *)user_data;
830
831     MRP_UNUSED(dbus);
832
833     if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
834         print(c, "Focus request sent to server.");
835     else
836         print(c, "Focus request failed on server.");
837 }
838
839
840 static void request_focus(client_t *c, const char *focus)
841 {
842     const char  *dest   = SRS_CLIENT_SERVICE;
843     const char  *path   = SRS_CLIENT_PATH;
844     const char  *iface  = SRS_CLIENT_INTERFACE;
845     const char  *method = SRS_CLIENT_REQUEST_FOCUS;
846
847     if (!c->server_up) {
848         print(c, "Server is currently down.");
849         return;
850     }
851
852     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
853                        focus_reply, c,
854                        MRP_DBUS_TYPE_STRING, focus, DBUS_TYPE_INVALID))
855         print(c, "Failed to send focus request to server.");
856 }
857
858
859 static void render_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
860 {
861     client_t *c = (client_t *)user_data;
862
863     MRP_UNUSED(dbus);
864
865     if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
866         print(c, "TTS render request succeeded.");
867     else
868         print(c, "TTS render request failed on server.");
869 }
870
871
872 static void request_render_voice(client_t *c, const char *msg, const char *vid,
873                                  int timeout, int subscribe)
874 {
875     const char  *dest   = SRS_CLIENT_SERVICE;
876     const char  *path   = SRS_CLIENT_PATH;
877     const char  *iface  = SRS_CLIENT_INTERFACE;
878     const char  *method = SRS_CLIENT_RENDER_VOICE;
879     int32_t      to     = (int32_t)timeout;
880     char        *none[] = { };
881     char        *full[] = { "started", "progress", "completed", "timeout",
882                             "aborted" };
883     char       **events;
884     int          nevent;
885
886     if (!c->server_up) {
887         print(c, "Server is currently down.");
888         return;
889     }
890
891     if (subscribe) {
892         events = full;
893         nevent = MRP_ARRAY_SIZE(full);
894     }
895     else {
896         events = none;
897         nevent = 0;
898     }
899
900     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
901                        render_reply, c,
902                        MRP_DBUS_TYPE_STRING, msg,
903                        MRP_DBUS_TYPE_STRING, vid,
904                        MRP_DBUS_TYPE_INT32 , &to,
905                        MRP_DBUS_TYPE_ARRAY ,
906                        MRP_DBUS_TYPE_STRING, events, nevent,
907                        MRP_DBUS_TYPE_INVALID))
908         print(c, "Failed to send voice cancel request to server.");
909
910     return;
911 }
912
913
914 static void cancel_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
915 {
916     client_t *c = (client_t *)user_data;
917
918     MRP_UNUSED(dbus);
919
920     if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
921         print(c, "TTS cancel request succeeded.");
922     else
923         print(c, "TTS cancel request failed on server.");
924 }
925
926
927 static void request_cancel_voice(client_t *c, uint32_t id)
928 {
929     const char  *dest   = SRS_CLIENT_SERVICE;
930     const char  *path   = SRS_CLIENT_PATH;
931     const char  *iface  = SRS_CLIENT_INTERFACE;
932     const char  *method = SRS_CLIENT_CANCEL_VOICE;
933
934     if (!c->server_up) {
935         print(c, "Server is currently down.");
936         return;
937     }
938
939     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
940                        cancel_reply, c,
941                        MRP_DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID))
942         print(c, "Failed to send voice cancel request to server.");
943 }
944
945
946 static void voice_query_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
947                               void *user_data)
948 {
949     client_t  *c = (client_t *)user_data;
950     uint32_t   nvoice, i;
951     char     **voices, **lang, **dialect, **gender, **description;
952     size_t     dummy;
953
954     MRP_UNUSED(dbus);
955
956     if (mrp_dbus_msg_type(rpl) != MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN) {
957         print(c, "Voice query failed.");
958         return;
959     }
960
961     if (!mrp_dbus_msg_read_basic(rpl, MRP_DBUS_TYPE_UINT32, &nvoice)) {
962         print(c, "Failed to parse voice query reply.");
963         return;
964     }
965
966     if (!mrp_dbus_msg_read_basic(rpl, MRP_DBUS_TYPE_UINT32, &nvoice) ||
967         !mrp_dbus_msg_read_array(rpl, MRP_DBUS_TYPE_STRING,
968                                  (void **)&voices, &dummy) ||
969         !mrp_dbus_msg_read_array(rpl, MRP_DBUS_TYPE_STRING,
970                                  (void **)&lang, &dummy) ||
971         !mrp_dbus_msg_read_array(rpl, MRP_DBUS_TYPE_STRING,
972                                  (void **)&dialect, &dummy) ||
973         !mrp_dbus_msg_read_array(rpl, MRP_DBUS_TYPE_STRING,
974                                  (void **)&gender, &dummy) ||
975         !mrp_dbus_msg_read_array(rpl, MRP_DBUS_TYPE_STRING,
976                                  (void **)&description, &dummy)) {
977         print(c, "Failed to parse voice query reply.");
978         return;
979     }
980
981     print(c, "Server has %d voice%s loaded.", nvoice,
982           nvoice == 1 ? "" : "s");
983     for (i = 0; i < nvoice; i++) {
984         print(c, "#%d: %s", i + 1, voices[i]);
985         print(c, "    language: %s", lang[i]);
986         print(c, "    dialect: %s", dialect[i] ? dialect[i] : "<none>");
987         print(c, "    gender: %s", gender[i]);
988         print(c, "    description: %s", description[i]);
989     }
990 }
991
992
993 static void query_voices(client_t *c, const char *language)
994 {
995     const char  *dest   = SRS_CLIENT_SERVICE;
996     const char  *path   = SRS_CLIENT_PATH;
997     const char  *iface  = SRS_CLIENT_INTERFACE;
998     const char  *method = SRS_CLIENT_QUERY_VOICES;
999
1000     if (!c->server_up) {
1001         print(c, "Server is currently down.");
1002         return;
1003     }
1004
1005     if (language == NULL)
1006         language = "";
1007
1008     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
1009                        voice_query_reply, c,
1010                        MRP_DBUS_TYPE_STRING, language,
1011                        MRP_DBUS_TYPE_INVALID))
1012         print(c, "Failed to send voice query request to server.");
1013
1014     return;
1015 }
1016
1017
1018 static void execute_user_command(client_t *c, int narg, char **args)
1019 {
1020     const char *cmd;
1021
1022     cmd = args[0];
1023     narg--;
1024     args++;
1025
1026     switch (narg) {
1027     case 0:
1028         if      (!strcmp(cmd, "register"))   register_client(c);
1029         else if (!strcmp(cmd, "unregister")) unregister_client(c);
1030         else if (!strcmp(cmd, "exit"))       quit_mainloop(c, 0);
1031         else if (!strcmp(cmd, "quit"))       quit_mainloop(c, 0);
1032         else if (!strcmp(cmd, "help")) {
1033             print(c, "Available commands:");
1034             print(c, "  register                     - register to server");
1035             print(c, "  unregister                   - unregister from server");
1036             print(c, "  focus none|shared|exclusive  - request voice focus");
1037             print(c, "  add command <command>        - add new command");
1038             print(c, "  del command <command>        - delete a command");
1039             print(c, "  render tts '<msg>' \\        - request TTS of <msg>");
1040             print(c, "    [-voice:<voice>] \\");
1041             print(c, "    [-timeout:<timeout>]\\");
1042             print(c, "    [-events]");
1043             print(c, "  cancel tts '<id>'            - cancel given TTS "
1044                   "request");
1045             print(c, "  list commands                - list commands set");
1046             print(c, "  list voices                  - list available voices");
1047             print(c, "  help                         - show this help");
1048             print(c, "  exit                         - exit from client");
1049         }
1050         else
1051             print(c, "Unknown command '%s'.", cmd);
1052         break;
1053
1054     case 1:
1055         if (!strcmp(cmd, "focus")) {
1056             if (strcmp(args[0], "none") &&
1057                 strcmp(args[0], "shared") &&
1058                 strcmp(args[0], "exclusive")) {
1059                 print(c, "Invalid focus '%s', valid foci are: "
1060                       "none, shared, and exclusive.", args[0]);
1061             }
1062             else
1063                 request_focus(c, args[0]);
1064         }
1065         else if (!strcmp(cmd, "reset") && !strcmp(args[0], "commands"))
1066             reset_commands(c);
1067         else if (!strcmp(cmd, "list" ) && !strcmp(args[0], "commands"))
1068             list_commands(c);
1069         else if (!strcmp(cmd, "list" ) && !strcmp(args[0], "voices"))
1070             query_voices(c, NULL);
1071         else if (!strcmp(cmd, "cancel"))
1072             cancel_tts(c, narg-1, args+1);
1073         else
1074             print(c, "Invalid command.");
1075         break;
1076
1077     default:
1078         if (!strcmp(args[0], "command")) {
1079             if (!strcmp(cmd, "add" ))
1080                 add_command(c, narg-1, args+1);
1081             else if (!strcmp(cmd, "del" ) || !strcmp(cmd, "delete"))
1082                 del_command(c, narg-1, args+1);
1083             else
1084                 print(c, "Invalid command.");
1085         }
1086         else if (!strcmp(args[0], "tts")) {
1087             if (!strcmp(cmd, "render"))
1088                 request_tts(c, narg-1, args+1);
1089             else
1090                 print(c, "Invalid TTS command.");
1091         }
1092         else
1093             print(c, "Invalid command.");
1094         break;
1095     }
1096 }
1097
1098
1099 int main(int argc, char *argv[])
1100 {
1101     client_t *c;
1102
1103     c = create_client(argv[0]);
1104
1105     if (c == NULL) {
1106         fprintf(stderr, "Failed to create client.");
1107         exit(1);
1108     }
1109
1110     setup_signals(c);
1111     parse_cmdline(c, argc, &argv[0]);
1112     setup_dbus(c);
1113     setup_input(c);
1114     run_mainloop(c);
1115     cleanup_input(c);
1116     cleanup_dbus(c);
1117     destroy_client(c);
1118
1119     return 0;
1120 }