dbus-client: test client cancel and help fixes.
[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;
255     size_t      l;
256     ssize_t     n;
257
258     if (c->registered) {
259         print(c, "You need to unregister first to modify commands.");
260         return;
261     }
262
263     p = msg;
264     l = sizeof(msg);
265     for (i = 0; i < ntoken; i++) {
266         t = tokens[i];
267         if (*t == '-') {
268             if (!strncmp(t + 1, "timeout:", o=8)) {
269                 timeout = strtol(t + 1 + o, &e, 10);
270                 if (*e != '\0') {
271                     print(c, "Invalid timeout: %s.", t + 1 + o);
272                     return;
273                 }
274             }
275             else if (!strncmp(t + 1, "events", o=6)) {
276                 events = TRUE;
277             }
278             else if (!strncmp(t + 1, "voice:", o=6)) {
279                 voice = t + 1 + o;
280             }
281         }
282         else {
283             n = snprintf(p, l, "%s%s", sep, t);
284             if (n >= l) {
285                 print(c, "TTS message too long.");
286                 return;
287             }
288
289             p += n;
290             l -= n;
291             sep = " ";
292         }
293     }
294
295     print(c, "message: '%s'", msg);
296
297     request_render_voice(c, msg, voice, timeout, events);
298 }
299
300
301 static void cancel_tts(client_t *c, int ntoken, char **tokens)
302 {
303     int       i;
304     uint32_t  vreq;
305     char     *end;
306
307     if (ntoken == 0) {
308         if (c->vreq)
309             request_cancel_voice(c, c->vreq);
310         else
311             print(c, "No outstanding TTS request.");
312     }
313     else {
314         for (i = 0; i < ntoken; i++) {
315             vreq = strtoul(tokens[i], &end, 10);
316
317             if (end && !*end)
318                 request_cancel_voice(c, vreq);
319             else
320                 print(c, "TTS request id '%s' is invalid.", tokens[i]);
321         }
322     }
323 }
324
325
326 static void set_client_defaults(client_t *c, const char *argv0)
327 {
328     int i;
329
330     c->dbus_address = "session";
331     c->app_class    = "player";
332     c->app_name     = strrchr(argv0, '/');
333
334     if (c->app_name != NULL)
335         c->app_name++;
336     else
337         c->app_name = argv0;
338
339     c->commands = mrp_allocz(sizeof(default_commands));
340     c->ncommand = MRP_ARRAY_SIZE(default_commands);
341
342     for (i = 0; i < c->ncommand; i++) {
343         c->commands[i] = mrp_strdup(default_commands[i]);
344         if (c->commands[i] == NULL) {
345             print(c, "Failed to initialize default command set.");
346             exit(1);
347         }
348     }
349 }
350
351
352 static void destroy_client(client_t *c)
353 {
354     if (c != NULL) {
355         mrp_debug("destroying client");
356
357         if (c->ml != NULL)
358             mrp_mainloop_destroy(c->ml);
359
360         if (c->pa != NULL)
361             pa_mainloop_free(c->pa);
362
363         mrp_free(c);
364     }
365 }
366
367
368 static client_t *create_client(const char *argv0)
369 {
370     client_t *c = mrp_allocz(sizeof(*c));
371
372     if (c != NULL) {
373         set_client_defaults(c, argv0);
374
375         c->pa = pa_mainloop_new();
376         c->ml = mrp_mainloop_pulse_get(pa_mainloop_get_api(c->pa));
377
378         if (c->pa != NULL && c->ml != NULL)
379             return c;
380         else
381             destroy_client(c);
382     }
383
384     return NULL;
385 }
386
387
388 static int focus_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
389 {
390     client_t   *c = (client_t *)user_data;
391     const char *focus;
392
393     MRP_UNUSED(dbus);
394
395     hide_prompt(c);
396     if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &focus))
397         print(c, "Voice focus is now: %s", focus);
398     else
399         print(c, "Failed to parse voice focus notification.");
400     show_prompt(c);
401
402     return TRUE;
403 }
404
405
406 static int voice_command_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
407                                 void *user_data)
408 {
409     client_t   *c = (client_t *)user_data;
410     const char *command;
411
412     MRP_UNUSED(dbus);
413
414     hide_prompt(c);
415     if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &command))
416         print(c, "Received voice command: %s", command);
417     else
418         print(c, "Failed to parse voice command notification.");
419     show_prompt(c);
420
421     return TRUE;
422 }
423
424
425 static int voice_render_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
426                                void *user_data)
427 {
428     client_t   *c = (client_t *)user_data;
429     uint32_t    id;
430     const char *event;
431     double      pcnt;
432     uint32_t    msec;
433
434     MRP_UNUSED(dbus);
435
436     hide_prompt(c);
437     if (!mrp_dbus_msg_read_basic(msg, DBUS_TYPE_UINT32, &id) ||
438         !mrp_dbus_msg_read_basic(msg, DBUS_TYPE_STRING, &event)) {
439         print(c, "Failed to parse voice render event notification.");
440         return TRUE;
441     }
442
443     if (!strcmp(event, "progress")) {
444         pcnt = -1.0;
445         msec = (uint32_t)-1;
446         if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &id) &&
447             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &event) &&
448             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_DOUBLE, &pcnt) &&
449             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &msec))
450             print(c, "Rendering <%u> progress: %f %% (%u msecs)", id,
451                   pcnt, msec);
452         else
453             print(c, "Rendering <%u> progress: failed to parse message", id);
454     }
455     else
456         print(c, "Rendering <%u>: %s", id, event);
457     show_prompt(c);
458
459     return TRUE;
460 }
461
462
463 static void server_name_change(mrp_dbus_t *dbus, const char *name, int running,
464                                const char *owner, void *user_data)
465 {
466     client_t *c = (client_t *)user_data;
467
468     MRP_UNUSED(dbus);
469
470     c->server_up = running;
471
472     if (running) {
473         set_prompt(c, "server up");
474         print(c, "Server (%s) is now up (as %s).", name, owner);
475
476         if (c->autoregister)
477             register_client(c);
478     }
479     else {
480         set_prompt(c, "server down");
481         print(c, "Server (%s) is now down.", name);
482         c->registered = FALSE;
483     }
484 }
485
486
487 static void setup_dbus(client_t *c)
488 {
489     const char *name      = SRS_CLIENT_SERVICE;
490     const char *path      = SRS_CLIENT_PATH;
491     const char *interface = SRS_CLIENT_INTERFACE;
492     const char *focus     = SRS_CLIENT_NOTIFY_FOCUS;
493     const char *command   = SRS_CLIENT_NOTIFY_COMMAND;
494     const char *voice     = SRS_CLIENT_NOTIFY_VOICE;
495
496     c->dbus = mrp_dbus_get(c->ml, c->dbus_address, NULL);
497
498     if (c->dbus == NULL) {
499         print(c, "Failed to connect to D-BUS (%s).", c->dbus_address);
500         exit(1);
501     }
502
503     if (mrp_dbus_follow_name(c->dbus, name, server_name_change, c) &&
504         mrp_dbus_subscribe_signal(c->dbus, focus_notify, c,
505                                   NULL, path, interface, focus, NULL) &&
506         mrp_dbus_subscribe_signal(c->dbus, voice_command_notify, c,
507                                   NULL, path, interface, command, NULL) &&
508         mrp_dbus_subscribe_signal(c->dbus, voice_render_notify, c,
509                                   NULL, path, interface, voice, NULL))
510         return;
511
512     print(c, "Failed to set up server D-BUS name tracking.");
513     exit(1);
514 }
515
516
517 static void cleanup_dbus(client_t *c)
518 {
519     const char *name      = SRS_CLIENT_SERVICE;
520     const char *path      = SRS_CLIENT_PATH;
521     const char *interface = SRS_CLIENT_INTERFACE;
522     const char *focus     = SRS_CLIENT_NOTIFY_FOCUS;
523     const char *command   = SRS_CLIENT_NOTIFY_COMMAND;
524     const char *voice     = SRS_CLIENT_NOTIFY_VOICE;
525
526     if (c != NULL) {
527         mrp_dbus_forget_name(c->dbus, name, server_name_change, c);
528         mrp_dbus_unsubscribe_signal(c->dbus, focus_notify, c,
529                                     NULL, path, interface, focus, NULL);
530         mrp_dbus_unsubscribe_signal(c->dbus, voice_command_notify, c,
531                                     NULL, path, interface, command, NULL);
532         mrp_dbus_unsubscribe_signal(c->dbus, voice_render_notify, c,
533                                     NULL, path, interface, voice, NULL);
534         mrp_dbus_unref(c->dbus);
535     }
536 }
537
538
539 static void run_mainloop(client_t *c)
540 {
541     pa_mainloop_run(c->pa, &c->exit_status);
542 }
543
544
545 static void quit_mainloop(client_t *c, int exit_status)
546 {
547     if (c != NULL)
548         pa_mainloop_quit(c->pa, exit_status);
549     else
550         exit(exit_status);
551 }
552
553
554 static void sighandler(mrp_sighandler_t *h, int signum, void *user_data)
555 {
556     client_t *c = (client_t *)user_data;
557
558     MRP_UNUSED(h);
559
560     switch (signum) {
561     case SIGINT:
562         printf("Received SIGINT, exiting...");
563         quit_mainloop(c, 0);
564         break;
565
566     case SIGTERM:
567         printf("Received SIGTERM, exiting...");
568         quit_mainloop(c, 0);
569         break;
570     }
571 }
572
573
574 static void setup_signals(client_t *c)
575 {
576     mrp_add_sighandler(c->ml, SIGINT , sighandler, c);
577     mrp_add_sighandler(c->ml, SIGTERM, sighandler, c);
578 }
579
580
581
582
583 static int split_input(char *input, int narg, char **args)
584 {
585     int   n;
586     char *p;
587
588     n = 0;
589     p = input;
590
591     while (*p) {
592         while (*p == ' ' || *p == '\t')
593             p++;
594
595         args[n++] = p;
596
597         if (n >= narg) {
598             errno = EOVERFLOW;
599             return -1;
600         }
601
602         while (*p && *p != ' ' && *p != '\t')
603             p++;
604
605         if (*p)
606             *p++ = '\0';
607     }
608
609     return n;
610 }
611
612
613 static void process_input(brl_t *brl, const char *input, void *user_data)
614 {
615     client_t *c   = (client_t *)user_data;
616     int       len = input ? strlen(input) + 1 : 0;
617     char      buf[len], *args[64];
618     int       narg;
619
620     if (len > 1) {
621         brl_add_history(brl, input);
622         hide_prompt(c);
623
624         strcpy(buf, input);
625         narg = split_input(buf, MRP_ARRAY_SIZE(args), args);
626         if (narg > 0)
627             execute_user_command(c, narg, &args[0]);
628         else
629             printf("failed to parse input '%s'\n", input);
630
631         show_prompt(c);
632     }
633 }
634
635
636 static void setup_input(client_t *c)
637 {
638     int fd;
639
640     fd     = fileno(stdin);
641     c->brl = brl_create_with_murphy(fd, "starting", c->ml, process_input, c);
642
643     if (c->brl != NULL)
644         brl_show_prompt(c->brl);
645     else {
646         fprintf(stderr, "Failed to initialize breedline for console input.");
647         exit(1);
648     }
649 }
650
651
652 static void cleanup_input(client_t *c)
653 {
654     if (c != NULL && c->brl != NULL) {
655         brl_destroy(c->brl);
656         c->brl = NULL;
657     }
658 }
659
660
661 static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
662 {
663     va_list     ap;
664     const char *exe;
665
666     if (fmt && *fmt) {
667         va_start(ap, fmt);
668         vprintf(fmt, ap);
669         va_end(ap);
670     }
671
672     exe = strrchr(argv0, '/');
673
674     printf("usage: %s [options]\n\n"
675            "The possible options are:\n"
676            "  -N, --name=APPNAME             application name to use\n"
677            "  -C, --class=APPCLASS           application class to use\n"
678            "  -D, --dbus=DBUS                D-BUS to use\n"
679            "      DBUS is 'session', 'system', or a DBUS daemon address.\n"
680            "  -v, --verbose                  increase logging verbosity\n"
681            "  -d, --debug                    enable debug messages\n"
682            "  -R, --register                 automatically register to server\n"
683            "  -F, --focus[=TYPE]             automatically request focus\n"
684            "  -h, --help                     show help on usage\n", exe);
685     printf("\n");
686
687     if (exit_code < 0)
688         return;
689     else
690         exit(exit_code);
691 }
692
693
694 static void parse_cmdline(client_t *c, int argc, char **argv)
695 {
696 #   define OPTIONS "N:C:D:d:RFh"
697     struct option options[] = {
698         { "name"      , required_argument, NULL, 'N' },
699         { "class"     , required_argument, NULL, 'C' },
700         { "dbus"      , required_argument, NULL, 'D' },
701         { "debug"     , required_argument, NULL, 'd' },
702         { "register"  , no_argument      , NULL, 'R' },
703         { "focus"     , optional_argument, NULL, 'F' },
704         { "help"      , no_argument      , NULL, 'h' },
705         { NULL, 0, NULL, 0 }
706     };
707
708     int opt;
709
710     while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
711         switch (opt) {
712         case 'N':
713             c->app_name = optarg;
714             break;
715
716         case 'C':
717             c->app_class = optarg;
718             break;
719
720         case 'D':
721             c->dbus_address = optarg;
722             break;
723
724         case 'd':
725             mrp_debug_set_config(optarg);
726             mrp_debug_enable(TRUE);
727             break;
728
729         case 'R':
730             c->autoregister = TRUE;
731             break;
732
733         case 'F':
734             c->autofocus = optarg ? optarg : "shared";
735             break;
736
737         case 'h':
738             print_usage(argv[0], -1, "");
739             exit(0);
740             break;
741
742         default:
743             print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
744         }
745     }
746 }
747
748
749 static void register_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
750                            void *user_data)
751 {
752     client_t *c = (client_t *)user_data;
753
754     MRP_UNUSED(dbus);
755
756     if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN) {
757         set_prompt(c, c->app_name);
758         print(c, "Successfully registered to server.");
759         if (c->autofocus)
760             request_focus(c, c->autofocus);
761     }
762     else {
763         set_prompt(c, "failed");
764         print(c, "Failed to register to server.");
765     }
766 }
767
768
769 static void register_client(client_t *c)
770 {
771     const char **cmds   = (const char **)c->commands;
772     int          ncmd   = c->ncommand;
773     const char  *dest   = SRS_CLIENT_SERVICE;
774     const char  *path   = SRS_CLIENT_PATH;
775     const char  *iface  = SRS_CLIENT_INTERFACE;
776     const char  *method = SRS_CLIENT_REGISTER;
777
778     if (!c->server_up) {
779         print(c, "Server is currently down.");
780
781         return;
782     }
783
784     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
785                        register_reply, c,
786                        MRP_DBUS_TYPE_STRING, c->app_name,
787                        MRP_DBUS_TYPE_STRING, c->app_class,
788                        MRP_DBUS_TYPE_ARRAY , MRP_DBUS_TYPE_STRING, cmds, ncmd,
789                        MRP_DBUS_TYPE_INVALID))
790         print(c, "Failed to send register message to server.");
791 }
792
793
794 static void unregister_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
795                              void *user_data)
796 {
797     client_t *c = (client_t *)user_data;
798
799     MRP_UNUSED(dbus);
800
801     if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN) {
802         set_prompt(c, "unregistered");
803         print(c, "Successfully unregistered from server.");
804     }
805     else
806         print(c, "Failed to unregister from server.");
807 }
808
809
810 static void unregister_client(client_t *c)
811 {
812     const char  *dest   = SRS_CLIENT_SERVICE;
813     const char  *path   = SRS_CLIENT_PATH;
814     const char  *iface  = SRS_CLIENT_INTERFACE;
815     const char  *method = SRS_CLIENT_UNREGISTER;
816
817     if (!c->server_up) {
818         print(c, "Server is currently down.");
819         return;
820     }
821
822     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
823                        unregister_reply, c, MRP_DBUS_TYPE_INVALID))
824         print(c, "Failed to send unregister message to server.");
825 }
826
827
828 static void focus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
829 {
830     client_t *c = (client_t *)user_data;
831
832     MRP_UNUSED(dbus);
833
834     if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
835         print(c, "Focus request sent to server.");
836     else
837         print(c, "Focus request failed on server.");
838 }
839
840
841 static void request_focus(client_t *c, const char *focus)
842 {
843     const char  *dest   = SRS_CLIENT_SERVICE;
844     const char  *path   = SRS_CLIENT_PATH;
845     const char  *iface  = SRS_CLIENT_INTERFACE;
846     const char  *method = SRS_CLIENT_REQUEST_FOCUS;
847
848     if (!c->server_up) {
849         print(c, "Server is currently down.");
850         return;
851     }
852
853     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
854                        focus_reply, c,
855                        MRP_DBUS_TYPE_STRING, focus, DBUS_TYPE_INVALID))
856         print(c, "Failed to send focus request to server.");
857 }
858
859
860 static void render_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
861 {
862     client_t *c = (client_t *)user_data;
863
864     MRP_UNUSED(dbus);
865
866     if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
867         print(c, "TTS render request succeeded.");
868     else
869         print(c, "TTS render request failed on server.");
870 }
871
872
873 static void request_render_voice(client_t *c, const char *msg, const char *vid,
874                                  int timeout, int subscribe)
875 {
876     const char  *dest   = SRS_CLIENT_SERVICE;
877     const char  *path   = SRS_CLIENT_PATH;
878     const char  *iface  = SRS_CLIENT_INTERFACE;
879     const char  *method = SRS_CLIENT_RENDER_VOICE;
880     int32_t      to     = (int32_t)timeout;
881     char        *none[] = { };
882     char        *full[] = { "started", "progress", "completed", "timeout",
883                             "aborted" };
884     char       **events;
885     int          nevent;
886
887     if (!c->server_up) {
888         print(c, "Server is currently down.");
889         return;
890     }
891
892     if (subscribe) {
893         events = full;
894         nevent = MRP_ARRAY_SIZE(full);
895     }
896     else {
897         events = none;
898         nevent = 0;
899     }
900
901     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
902                        render_reply, c,
903                        MRP_DBUS_TYPE_STRING, msg,
904                        MRP_DBUS_TYPE_STRING, vid,
905                        MRP_DBUS_TYPE_INT32 , &to,
906                        MRP_DBUS_TYPE_ARRAY ,
907                        MRP_DBUS_TYPE_STRING, events, nevent,
908                        MRP_DBUS_TYPE_INVALID))
909         print(c, "Failed to send voice cancel request to server.");
910
911     return;
912 }
913
914
915 static void cancel_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
916 {
917     client_t *c = (client_t *)user_data;
918
919     MRP_UNUSED(dbus);
920
921     if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
922         print(c, "TTS cancel request succeeded.");
923     else
924         print(c, "TTS cancel request failed on server.");
925 }
926
927
928 static void request_cancel_voice(client_t *c, uint32_t id)
929 {
930     const char  *dest   = SRS_CLIENT_SERVICE;
931     const char  *path   = SRS_CLIENT_PATH;
932     const char  *iface  = SRS_CLIENT_INTERFACE;
933     const char  *method = SRS_CLIENT_CANCEL_VOICE;
934
935     if (!c->server_up) {
936         print(c, "Server is currently down.");
937         return;
938     }
939
940     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
941                        cancel_reply, c,
942                        MRP_DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID))
943         print(c, "Failed to send voice cancel request to server.");
944 }
945
946
947 static void voice_query_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
948                               void *user_data)
949 {
950     client_t  *c = (client_t *)user_data;
951     uint32_t   nvoice;
952     char     **voices, **lang, **dialect, **gender, **description;
953     size_t     dummy;
954     int        i, n;
955
956     MRP_UNUSED(dbus);
957
958     if (mrp_dbus_msg_type(rpl) != MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN) {
959         print(c, "Voice query failed.");
960         return;
961     }
962
963     if (!mrp_dbus_msg_read_basic(rpl, MRP_DBUS_TYPE_UINT32, &nvoice)) {
964         print(c, "Failed to parse voice query reply.");
965         return;
966     }
967
968     if (!mrp_dbus_msg_read_basic(rpl, MRP_DBUS_TYPE_UINT32, &nvoice) ||
969         !mrp_dbus_msg_read_array(rpl, MRP_DBUS_TYPE_STRING,
970                                  (void **)&voices, &dummy) ||
971         !mrp_dbus_msg_read_array(rpl, MRP_DBUS_TYPE_STRING,
972                                  (void **)&lang, &dummy) ||
973         !mrp_dbus_msg_read_array(rpl, MRP_DBUS_TYPE_STRING,
974                                  (void **)&dialect, &dummy) ||
975         !mrp_dbus_msg_read_array(rpl, MRP_DBUS_TYPE_STRING,
976                                  (void **)&gender, &dummy) ||
977         !mrp_dbus_msg_read_array(rpl, MRP_DBUS_TYPE_STRING,
978                                  (void **)&description, &dummy)) {
979         print(c, "Failed to parse voice query reply.");
980         return;
981     }
982
983     print(c, "Server has %d voice%s loaded.", nvoice,
984           nvoice == 1 ? "" : "s");
985     for (i = 0; i < nvoice; i++) {
986         print(c, "#%d: %s", i + 1, voices[i]);
987         print(c, "    language: %s", lang[i]);
988         print(c, "    dialect: %s", dialect[i] ? dialect[i] : "<none>");
989         print(c, "    gender: %s", gender[i]);
990         print(c, "    description: %s", description[i]);
991     }
992 }
993
994
995 static void query_voices(client_t *c, const char *language)
996 {
997     const char  *dest   = SRS_CLIENT_SERVICE;
998     const char  *path   = SRS_CLIENT_PATH;
999     const char  *iface  = SRS_CLIENT_INTERFACE;
1000     const char  *method = SRS_CLIENT_QUERY_VOICES;
1001
1002     if (!c->server_up) {
1003         print(c, "Server is currently down.");
1004         return;
1005     }
1006
1007     if (language == NULL)
1008         language = "";
1009
1010     if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
1011                        voice_query_reply, c,
1012                        MRP_DBUS_TYPE_STRING, language,
1013                        MRP_DBUS_TYPE_INVALID))
1014         print(c, "Failed to send voice query request to server.");
1015
1016     return;
1017 }
1018
1019
1020 static void execute_user_command(client_t *c, int narg, char **args)
1021 {
1022     const char *cmd;
1023
1024     cmd = args[0];
1025     narg--;
1026     args++;
1027
1028     switch (narg) {
1029     case 0:
1030         if      (!strcmp(cmd, "register"))   register_client(c);
1031         else if (!strcmp(cmd, "unregister")) unregister_client(c);
1032         else if (!strcmp(cmd, "exit"))       quit_mainloop(c, 0);
1033         else if (!strcmp(cmd, "quit"))       quit_mainloop(c, 0);
1034         else if (!strcmp(cmd, "help")) {
1035             print(c, "Available commands:");
1036             print(c, "  register                     - register to server");
1037             print(c, "  unregister                   - unregister from server");
1038             print(c, "  focus none|shared|exclusive  - request voice focus");
1039             print(c, "  add command <command>        - add new command");
1040             print(c, "  del command <command>        - delete a command");
1041             print(c, "  render tts '<msg>' \\        - request TTS of <msg>");
1042             print(c, "    [-voice:<voice>] \\");
1043             print(c, "    [-timeout:<timeout>]\\");
1044             print(c, "    [-events]");
1045             print(c, "  cancel tts '<id>'            - cancel given TTS "
1046                   "request");
1047             print(c, "  list commands                - list commands set");
1048             print(c, "  list voices                  - list available voices");
1049             print(c, "  help                         - show this help");
1050             print(c, "  exit                         - exit from client");
1051         }
1052         else
1053             print(c, "Unknown command '%s'.", cmd);
1054         break;
1055
1056     case 1:
1057         if (!strcmp(cmd, "focus")) {
1058             if (strcmp(args[0], "none") &&
1059                 strcmp(args[0], "shared") &&
1060                 strcmp(args[0], "exclusive")) {
1061                 print(c, "Invalid focus '%s', valid foci are: "
1062                       "none, shared, and exclusive.", args[0]);
1063             }
1064             else
1065                 request_focus(c, args[0]);
1066         }
1067         else if (!strcmp(cmd, "reset") && !strcmp(args[0], "commands"))
1068             reset_commands(c);
1069         else if (!strcmp(cmd, "list" ) && !strcmp(args[0], "commands"))
1070             list_commands(c);
1071         else if (!strcmp(cmd, "list" ) && !strcmp(args[0], "voices"))
1072             query_voices(c, NULL);
1073         else if (!strcmp(cmd, "cancel"))
1074             cancel_tts(c, narg-1, args+1);
1075         else
1076             print(c, "Invalid command.");
1077         break;
1078
1079     default:
1080         if (!strcmp(args[0], "command")) {
1081             if (!strcmp(cmd, "add" ))
1082                 add_command(c, narg-1, args+1);
1083             else if (!strcmp(cmd, "del" ) || !strcmp(cmd, "delete"))
1084                 del_command(c, narg-1, args+1);
1085             else
1086                 print(c, "Invalid command.");
1087         }
1088         else if (!strcmp(args[0], "tts")) {
1089             if (!strcmp(cmd, "render"))
1090                 request_tts(c, narg-1, args+1);
1091             else
1092                 print(c, "Invalid TTS command.");
1093         }
1094         else
1095             print(c, "Invalid command.");
1096         break;
1097     }
1098 }
1099
1100
1101 int main(int argc, char *argv[])
1102 {
1103     client_t *c;
1104
1105     c = create_client(argv[0]);
1106
1107     if (c == NULL) {
1108         fprintf(stderr, "Failed to create client.");
1109         exit(1);
1110     }
1111
1112     setup_signals(c);
1113     parse_cmdline(c, argc, &argv[0]);
1114     setup_dbus(c);
1115     setup_input(c);
1116     run_mainloop(c);
1117     cleanup_input(c);
1118     cleanup_dbus(c);
1119     destroy_client(c);
1120
1121     return 0;
1122 }