2 * Copyright (c) 2012, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
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>
43 #include <breedline/breedline-murphy.h>
45 #include "dbus-config.h"
48 static const char *default_commands[] = {
49 "hal open the pod bay doors",
53 "hal dial __push_dict__(digits) *",
54 "hal play artist __push_dict__(artists) *"
63 const char *app_class;
65 const char *dbus_address;
73 const char *autofocus;
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);
86 static void set_prompt(client_t *c, const char *prompt)
88 brl_set_prompt(c->brl, prompt);
92 static void show_prompt(client_t *c)
95 brl_show_prompt(c->brl);
99 static void hide_prompt(client_t *c)
102 brl_hide_prompt(c->brl);
106 static void print(client_t *c, const char *format, ...)
112 va_start(ap, format);
113 vfprintf(stdout, format, ap);
122 static char *concat_tokens(char *buf, int size, int ntoken, char **tokens)
132 n = snprintf(p, l, "%s%s", t, tokens[0]);
144 buf[size - 1] = '\0';
149 static void add_command(client_t *c, int ntoken, char **tokens)
155 print(c, "You need to unregister first to modify commands.");
159 if (concat_tokens(command, sizeof(command), ntoken, tokens) == NULL) {
160 print(c, "Command too long.");
164 osize = sizeof(*c->commands) * c->ncommand;
165 nsize = sizeof(*c->commands) * (c->ncommand + 1);
167 if (!mrp_reallocz(c->commands, osize, nsize)) {
168 print(c, "Failed to add new command.");
172 c->commands[c->ncommand] = mrp_strdup(command);
174 if (c->commands[c->ncommand] != NULL) {
176 print(c, "Command '%s' added to command set.", command);
179 print(c, "Failed to register new command.");
183 static void del_command(client_t *c, int ntoken, char **tokens)
189 print(c, "You need to unregister first to modify commands.");
193 if (concat_tokens(command, sizeof(command), ntoken, tokens) == NULL) {
194 print(c, "Command too long.");
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));
205 mrp_realloc(c->commands, sizeof(*c->commands) * c->ncommand);
207 print(c, "Command '%s' deleted.", command);
213 static void reset_commands(client_t *c)
218 print(c, "You need to unregister first to modify commands.");
222 for (i = 0; i < c->ncommand; i++)
223 mrp_free(c->commands[i]);
224 mrp_free(c->commands);
229 print(c, "Commands resetted, no current commands.");
233 static void list_commands(client_t *c)
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]);
243 print(c, "No commands defined.");
247 static void request_tts(client_t *c, int ntoken, char **tokens)
249 const char *sep = "";
250 const char *voice = "english";
253 char msg[1024], *t, *e, *p;
259 print(c, "You need to unregister first to modify commands.");
265 for (i = 0; i < ntoken; i++) {
268 if (!strncmp(t + 1, "timeout:", o=8)) {
269 timeout = strtol(t + 1 + o, &e, 10);
271 print(c, "Invalid timeout: %s.", t + 1 + o);
275 else if (!strncmp(t + 1, "events", o=6)) {
278 else if (!strncmp(t + 1, "voice:", o=6)) {
283 n = snprintf(p, l, "%s%s", sep, t);
285 print(c, "TTS message too long.");
295 print(c, "message: '%s'", msg);
297 request_render_voice(c, msg, voice, timeout, events);
301 static void cancel_tts(client_t *c, int ntoken, char **tokens)
309 request_cancel_voice(c, c->vreq);
311 print(c, "No outstanding TTS request.");
314 for (i = 0; i < ntoken; i++) {
315 vreq = strtoul(tokens[i], &end, 10);
318 request_cancel_voice(c, vreq);
320 print(c, "TTS request id '%s' is invalid.", tokens[i]);
326 static void set_client_defaults(client_t *c, const char *argv0)
330 c->dbus_address = "session";
331 c->app_class = "player";
332 c->app_name = strrchr(argv0, '/');
334 if (c->app_name != NULL)
339 c->commands = mrp_allocz(sizeof(default_commands));
340 c->ncommand = MRP_ARRAY_SIZE(default_commands);
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.");
352 static void destroy_client(client_t *c)
355 mrp_debug("destroying client");
358 mrp_mainloop_destroy(c->ml);
361 pa_mainloop_free(c->pa);
368 static client_t *create_client(const char *argv0)
370 client_t *c = mrp_allocz(sizeof(*c));
373 set_client_defaults(c, argv0);
375 c->pa = pa_mainloop_new();
376 c->ml = mrp_mainloop_pulse_get(pa_mainloop_get_api(c->pa));
378 if (c->pa != NULL && c->ml != NULL)
388 static int focus_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
390 client_t *c = (client_t *)user_data;
396 if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &focus))
397 print(c, "Voice focus is now: %s", focus);
399 print(c, "Failed to parse voice focus notification.");
406 static int voice_command_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
409 client_t *c = (client_t *)user_data;
415 if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &command))
416 print(c, "Received voice command: %s", command);
418 print(c, "Failed to parse voice command notification.");
425 static int voice_render_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
428 client_t *c = (client_t *)user_data;
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.");
443 if (!strcmp(event, "progress")) {
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,
453 print(c, "Rendering <%u> progress: failed to parse message", id);
456 print(c, "Rendering <%u>: %s", id, event);
463 static void server_name_change(mrp_dbus_t *dbus, const char *name, int running,
464 const char *owner, void *user_data)
466 client_t *c = (client_t *)user_data;
470 c->server_up = running;
473 set_prompt(c, "server up");
474 print(c, "Server (%s) is now up (as %s).", name, owner);
480 set_prompt(c, "server down");
481 print(c, "Server (%s) is now down.", name);
482 c->registered = FALSE;
487 static void setup_dbus(client_t *c)
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;
496 c->dbus = mrp_dbus_get(c->ml, c->dbus_address, NULL);
498 if (c->dbus == NULL) {
499 print(c, "Failed to connect to D-BUS (%s).", c->dbus_address);
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))
512 print(c, "Failed to set up server D-BUS name tracking.");
517 static void cleanup_dbus(client_t *c)
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;
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);
539 static void run_mainloop(client_t *c)
541 pa_mainloop_run(c->pa, &c->exit_status);
545 static void quit_mainloop(client_t *c, int exit_status)
548 pa_mainloop_quit(c->pa, exit_status);
554 static void sighandler(mrp_sighandler_t *h, int signum, void *user_data)
556 client_t *c = (client_t *)user_data;
562 printf("Received SIGINT, exiting...");
567 printf("Received SIGTERM, exiting...");
574 static void setup_signals(client_t *c)
576 mrp_add_sighandler(c->ml, SIGINT , sighandler, c);
577 mrp_add_sighandler(c->ml, SIGTERM, sighandler, c);
583 static int split_input(char *input, int narg, char **args)
592 while (*p == ' ' || *p == '\t')
602 while (*p && *p != ' ' && *p != '\t')
613 static void process_input(brl_t *brl, const char *input, void *user_data)
615 client_t *c = (client_t *)user_data;
616 int len = input ? strlen(input) + 1 : 0;
617 char buf[len], *args[64];
621 brl_add_history(brl, input);
625 narg = split_input(buf, MRP_ARRAY_SIZE(args), args);
627 execute_user_command(c, narg, &args[0]);
629 printf("failed to parse input '%s'\n", input);
636 static void setup_input(client_t *c)
641 c->brl = brl_create_with_murphy(fd, "starting", c->ml, process_input, c);
644 brl_show_prompt(c->brl);
646 fprintf(stderr, "Failed to initialize breedline for console input.");
652 static void cleanup_input(client_t *c)
654 if (c != NULL && c->brl != NULL) {
661 static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
672 exe = strrchr(argv0, '/');
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);
694 static void parse_cmdline(client_t *c, int argc, char **argv)
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' },
710 while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
713 c->app_name = optarg;
717 c->app_class = optarg;
721 c->dbus_address = optarg;
725 mrp_debug_set_config(optarg);
726 mrp_debug_enable(TRUE);
730 c->autoregister = TRUE;
734 c->autofocus = optarg ? optarg : "shared";
738 print_usage(argv[0], -1, "");
743 print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
749 static void register_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
752 client_t *c = (client_t *)user_data;
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.");
760 request_focus(c, c->autofocus);
763 set_prompt(c, "failed");
764 print(c, "Failed to register to server.");
769 static void register_client(client_t *c)
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;
779 print(c, "Server is currently down.");
784 if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
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.");
794 static void unregister_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
797 client_t *c = (client_t *)user_data;
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.");
806 print(c, "Failed to unregister from server.");
810 static void unregister_client(client_t *c)
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;
818 print(c, "Server is currently down.");
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.");
828 static void focus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
830 client_t *c = (client_t *)user_data;
834 if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
835 print(c, "Focus request sent to server.");
837 print(c, "Focus request failed on server.");
841 static void request_focus(client_t *c, const char *focus)
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;
849 print(c, "Server is currently down.");
853 if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
855 MRP_DBUS_TYPE_STRING, focus, DBUS_TYPE_INVALID))
856 print(c, "Failed to send focus request to server.");
860 static void render_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
862 client_t *c = (client_t *)user_data;
866 if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
867 print(c, "TTS render request succeeded.");
869 print(c, "TTS render request failed on server.");
873 static void request_render_voice(client_t *c, const char *msg, const char *vid,
874 int timeout, int subscribe)
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;
882 char *full[] = { "started", "progress", "completed", "timeout",
888 print(c, "Server is currently down.");
894 nevent = MRP_ARRAY_SIZE(full);
901 if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
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.");
915 static void cancel_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
917 client_t *c = (client_t *)user_data;
921 if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
922 print(c, "TTS cancel request succeeded.");
924 print(c, "TTS cancel request failed on server.");
928 static void request_cancel_voice(client_t *c, uint32_t id)
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;
936 print(c, "Server is currently down.");
940 if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
942 MRP_DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID))
943 print(c, "Failed to send voice cancel request to server.");
947 static void voice_query_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
950 client_t *c = (client_t *)user_data;
952 char **voices, **lang, **dialect, **gender, **description;
958 if (mrp_dbus_msg_type(rpl) != MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN) {
959 print(c, "Voice query failed.");
963 if (!mrp_dbus_msg_read_basic(rpl, MRP_DBUS_TYPE_UINT32, &nvoice)) {
964 print(c, "Failed to parse voice query reply.");
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.");
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]);
995 static void query_voices(client_t *c, const char *language)
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;
1002 if (!c->server_up) {
1003 print(c, "Server is currently down.");
1007 if (language == NULL)
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.");
1020 static void execute_user_command(client_t *c, int narg, char **args)
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 "
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");
1053 print(c, "Unknown command '%s'.", cmd);
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]);
1065 request_focus(c, args[0]);
1067 else if (!strcmp(cmd, "reset") && !strcmp(args[0], "commands"))
1069 else if (!strcmp(cmd, "list" ) && !strcmp(args[0], "commands"))
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);
1076 print(c, "Invalid command.");
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);
1086 print(c, "Invalid command.");
1088 else if (!strcmp(args[0], "tts")) {
1089 if (!strcmp(cmd, "render"))
1090 request_tts(c, narg-1, args+1);
1092 print(c, "Invalid TTS command.");
1095 print(c, "Invalid command.");
1101 int main(int argc, char *argv[])
1105 c = create_client(argv[0]);
1108 fprintf(stderr, "Failed to create client.");
1113 parse_cmdline(c, argc, &argv[0]);