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;
258 print(c, "You need to unregister first to modify commands.");
264 for (i = 0; i < ntoken; i++) {
267 if (!strncmp(t + 1, "timeout:", o=8)) {
268 timeout = strtol(t + 1 + o, &e, 10);
270 print(c, "Invalid timeout: %s.", t + 1 + o);
274 else if (!strncmp(t + 1, "events", o=6)) {
277 else if (!strncmp(t + 1, "voice:", o=6)) {
282 n = snprintf(p, l, "%s%s", sep, t);
283 if ((size_t)n >= l) {
284 print(c, "TTS message too long.");
294 print(c, "message: '%s'", msg);
296 request_render_voice(c, msg, voice, timeout, events);
300 static void cancel_tts(client_t *c, int ntoken, char **tokens)
308 request_cancel_voice(c, c->vreq);
310 print(c, "No outstanding TTS request.");
313 for (i = 0; i < ntoken; i++) {
314 vreq = strtoul(tokens[i], &end, 10);
317 request_cancel_voice(c, vreq);
319 print(c, "TTS request id '%s' is invalid.", tokens[i]);
325 static void set_client_defaults(client_t *c, const char *argv0)
329 c->dbus_address = "session";
330 c->app_class = "player";
331 c->app_name = strrchr(argv0, '/');
333 if (c->app_name != NULL)
338 c->commands = mrp_allocz(sizeof(default_commands));
339 c->ncommand = MRP_ARRAY_SIZE(default_commands);
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.");
351 static void destroy_client(client_t *c)
354 mrp_debug("destroying client");
357 mrp_mainloop_destroy(c->ml);
360 pa_mainloop_free(c->pa);
367 static client_t *create_client(const char *argv0)
369 client_t *c = mrp_allocz(sizeof(*c));
372 set_client_defaults(c, argv0);
374 c->pa = pa_mainloop_new();
375 c->ml = mrp_mainloop_pulse_get(pa_mainloop_get_api(c->pa));
377 if (c->pa != NULL && c->ml != NULL)
387 static int focus_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
389 client_t *c = (client_t *)user_data;
395 if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &focus))
396 print(c, "Voice focus is now: %s", focus);
398 print(c, "Failed to parse voice focus notification.");
405 static int voice_command_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
408 client_t *c = (client_t *)user_data;
414 if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &command))
415 print(c, "Received voice command: %s", command);
417 print(c, "Failed to parse voice command notification.");
424 static int voice_render_notify(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
427 client_t *c = (client_t *)user_data;
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.");
442 if (!strcmp(event, "progress")) {
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,
452 print(c, "Rendering <%u> progress: failed to parse message", id);
455 print(c, "Rendering <%u>: %s", id, event);
462 static void server_name_change(mrp_dbus_t *dbus, const char *name, int running,
463 const char *owner, void *user_data)
465 client_t *c = (client_t *)user_data;
469 c->server_up = running;
472 set_prompt(c, "server up");
473 print(c, "Server (%s) is now up (as %s).", name, owner);
479 set_prompt(c, "server down");
480 print(c, "Server (%s) is now down.", name);
481 c->registered = FALSE;
486 static void setup_dbus(client_t *c)
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;
495 c->dbus = mrp_dbus_get(c->ml, c->dbus_address, NULL);
497 if (c->dbus == NULL) {
498 print(c, "Failed to connect to D-BUS (%s).", c->dbus_address);
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))
511 print(c, "Failed to set up server D-BUS name tracking.");
516 static void cleanup_dbus(client_t *c)
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;
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);
538 static void run_mainloop(client_t *c)
540 pa_mainloop_run(c->pa, &c->exit_status);
544 static void quit_mainloop(client_t *c, int exit_status)
547 pa_mainloop_quit(c->pa, exit_status);
553 static void sighandler(mrp_sighandler_t *h, int signum, void *user_data)
555 client_t *c = (client_t *)user_data;
561 printf("Received SIGINT, exiting...");
566 printf("Received SIGTERM, exiting...");
573 static void setup_signals(client_t *c)
575 mrp_add_sighandler(c->ml, SIGINT , sighandler, c);
576 mrp_add_sighandler(c->ml, SIGTERM, sighandler, c);
582 static int split_input(char *input, int narg, char **args)
591 while (*p == ' ' || *p == '\t')
601 while (*p && *p != ' ' && *p != '\t')
612 static void process_input(brl_t *brl, const char *input, void *user_data)
614 client_t *c = (client_t *)user_data;
615 int len = input ? strlen(input) + 1 : 0;
616 char buf[len], *args[64];
620 brl_add_history(brl, input);
624 narg = split_input(buf, MRP_ARRAY_SIZE(args), args);
626 execute_user_command(c, narg, &args[0]);
628 printf("failed to parse input '%s'\n", input);
635 static void setup_input(client_t *c)
640 c->brl = brl_create_with_murphy(fd, "starting", c->ml, process_input, c);
643 brl_show_prompt(c->brl);
645 fprintf(stderr, "Failed to initialize breedline for console input.");
651 static void cleanup_input(client_t *c)
653 if (c != NULL && c->brl != NULL) {
660 static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
671 exe = strrchr(argv0, '/');
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);
693 static void parse_cmdline(client_t *c, int argc, char **argv)
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' },
709 while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
712 c->app_name = optarg;
716 c->app_class = optarg;
720 c->dbus_address = optarg;
724 mrp_debug_set_config(optarg);
725 mrp_debug_enable(TRUE);
729 c->autoregister = TRUE;
733 c->autofocus = optarg ? optarg : "shared";
737 print_usage(argv[0], -1, "");
742 print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
748 static void register_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
751 client_t *c = (client_t *)user_data;
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.");
759 request_focus(c, c->autofocus);
762 set_prompt(c, "failed");
763 print(c, "Failed to register to server.");
768 static void register_client(client_t *c)
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;
778 print(c, "Server is currently down.");
783 if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
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.");
793 static void unregister_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
796 client_t *c = (client_t *)user_data;
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.");
805 print(c, "Failed to unregister from server.");
809 static void unregister_client(client_t *c)
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;
817 print(c, "Server is currently down.");
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.");
827 static void focus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
829 client_t *c = (client_t *)user_data;
833 if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
834 print(c, "Focus request sent to server.");
836 print(c, "Focus request failed on server.");
840 static void request_focus(client_t *c, const char *focus)
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;
848 print(c, "Server is currently down.");
852 if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
854 MRP_DBUS_TYPE_STRING, focus, DBUS_TYPE_INVALID))
855 print(c, "Failed to send focus request to server.");
859 static void render_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
861 client_t *c = (client_t *)user_data;
865 if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
866 print(c, "TTS render request succeeded.");
868 print(c, "TTS render request failed on server.");
872 static void request_render_voice(client_t *c, const char *msg, const char *vid,
873 int timeout, int subscribe)
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;
881 char *full[] = { "started", "progress", "completed", "timeout",
887 print(c, "Server is currently down.");
893 nevent = MRP_ARRAY_SIZE(full);
900 if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
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.");
914 static void cancel_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl, void *user_data)
916 client_t *c = (client_t *)user_data;
920 if (mrp_dbus_msg_type(rpl) == MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN)
921 print(c, "TTS cancel request succeeded.");
923 print(c, "TTS cancel request failed on server.");
927 static void request_cancel_voice(client_t *c, uint32_t id)
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;
935 print(c, "Server is currently down.");
939 if (!mrp_dbus_call(c->dbus, dest, path, iface, method, -1,
941 MRP_DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID))
942 print(c, "Failed to send voice cancel request to server.");
946 static void voice_query_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *rpl,
949 client_t *c = (client_t *)user_data;
951 char **voices, **lang, **dialect, **gender, **description;
956 if (mrp_dbus_msg_type(rpl) != MRP_DBUS_MESSAGE_TYPE_METHOD_RETURN) {
957 print(c, "Voice query failed.");
961 if (!mrp_dbus_msg_read_basic(rpl, MRP_DBUS_TYPE_UINT32, &nvoice)) {
962 print(c, "Failed to parse voice query reply.");
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.");
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]);
993 static void query_voices(client_t *c, const char *language)
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;
1000 if (!c->server_up) {
1001 print(c, "Server is currently down.");
1005 if (language == NULL)
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.");
1018 static void execute_user_command(client_t *c, int narg, char **args)
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 "
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");
1051 print(c, "Unknown command '%s'.", cmd);
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]);
1063 request_focus(c, args[0]);
1065 else if (!strcmp(cmd, "reset") && !strcmp(args[0], "commands"))
1067 else if (!strcmp(cmd, "list" ) && !strcmp(args[0], "commands"))
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);
1074 print(c, "Invalid command.");
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);
1084 print(c, "Invalid command.");
1086 else if (!strcmp(args[0], "tts")) {
1087 if (!strcmp(cmd, "render"))
1088 request_tts(c, narg-1, args+1);
1090 print(c, "Invalid TTS command.");
1093 print(c, "Invalid command.");
1099 int main(int argc, char *argv[])
1103 c = create_client(argv[0]);
1106 fprintf(stderr, "Failed to create client.");
1111 parse_cmdline(c, argc, &argv[0]);