tizen 2.3 release
[framework/connectivity/bluez.git] / tools / bluetooth-player.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2012  Intel Corporation. All rights reserved.
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <stdbool.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <signal.h>
34 #include <sys/signalfd.h>
35
36 #include <readline/readline.h>
37 #include <readline/history.h>
38 #include <glib.h>
39 #include <gdbus.h>
40
41 #include <client/display.h>
42
43 /* String display constants */
44 #define COLORED_NEW     COLOR_GREEN "NEW" COLOR_OFF
45 #define COLORED_CHG     COLOR_YELLOW "CHG" COLOR_OFF
46 #define COLORED_DEL     COLOR_RED "DEL" COLOR_OFF
47
48 #define PROMPT_ON       COLOR_BLUE "[bluetooth]" COLOR_OFF "# "
49 #define PROMPT_OFF      "[bluetooth]# "
50
51 #define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
52 #define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
53 #define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
54
55 static GMainLoop *main_loop;
56 static DBusConnection *dbus_conn;
57 static GDBusProxy *default_player;
58 static GSList *players = NULL;
59 static GSList *folders = NULL;
60 static GSList *items = NULL;
61
62 static void connect_handler(DBusConnection *connection, void *user_data)
63 {
64         rl_set_prompt(PROMPT_ON);
65         printf("\r");
66         rl_on_new_line();
67         rl_redisplay();
68 }
69
70 static void disconnect_handler(DBusConnection *connection, void *user_data)
71 {
72         rl_set_prompt(PROMPT_OFF);
73         printf("\r");
74         rl_on_new_line();
75         rl_redisplay();
76 }
77
78 static void cmd_quit(int argc, char *argv[])
79 {
80         g_main_loop_quit(main_loop);
81 }
82
83 static bool check_default_player(void)
84 {
85         if (!default_player) {
86                 rl_printf("No default player available\n");
87                 return FALSE;
88         }
89
90         return TRUE;
91 }
92
93 static void play_reply(DBusMessage *message, void *user_data)
94 {
95         DBusError error;
96
97         dbus_error_init(&error);
98
99         if (dbus_set_error_from_message(&error, message) == TRUE) {
100                 rl_printf("Failed to play: %s\n", error.name);
101                 dbus_error_free(&error);
102                 return;
103         }
104
105         rl_printf("Play successful\n");
106 }
107
108 static GDBusProxy *find_item(const char *path)
109 {
110         GSList *l;
111
112         for (l = items; l; l = g_slist_next(l)) {
113                 GDBusProxy *proxy = l->data;
114
115                 if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
116                         return proxy;
117         }
118
119         return NULL;
120 }
121
122 static void cmd_play_item(int argc, char *argv[])
123 {
124         GDBusProxy *proxy;
125
126         proxy = find_item(argv[1]);
127         if (proxy == NULL) {
128                 rl_printf("Item %s not available\n", argv[1]);
129                 return;
130         }
131
132         if (g_dbus_proxy_method_call(proxy, "Play", NULL, play_reply,
133                                                         NULL, NULL) == FALSE) {
134                 rl_printf("Failed to play\n");
135                 return;
136         }
137
138         rl_printf("Attempting to play %s\n", argv[1]);
139 }
140
141 static void cmd_play(int argc, char *argv[])
142 {
143         if (argc > 1)
144                 return cmd_play_item(argc, argv);
145
146         if (!check_default_player())
147                 return;
148
149         if (g_dbus_proxy_method_call(default_player, "Play", NULL, play_reply,
150                                                         NULL, NULL) == FALSE) {
151                 rl_printf("Failed to play\n");
152                 return;
153         }
154
155         rl_printf("Attempting to play\n");
156 }
157
158 static void pause_reply(DBusMessage *message, void *user_data)
159 {
160         DBusError error;
161
162         dbus_error_init(&error);
163
164         if (dbus_set_error_from_message(&error, message) == TRUE) {
165                 rl_printf("Failed to pause: %s\n", error.name);
166                 dbus_error_free(&error);
167                 return;
168         }
169
170         rl_printf("Pause successful\n");
171 }
172
173 static void cmd_pause(int argc, char *argv[])
174 {
175         if (!check_default_player())
176                 return;
177
178         if (g_dbus_proxy_method_call(default_player, "Pause", NULL,
179                                         pause_reply, NULL, NULL) == FALSE) {
180                 rl_printf("Failed to play\n");
181                 return;
182         }
183
184         rl_printf("Attempting to pause\n");
185 }
186
187 static void stop_reply(DBusMessage *message, void *user_data)
188 {
189         DBusError error;
190
191         dbus_error_init(&error);
192
193         if (dbus_set_error_from_message(&error, message) == TRUE) {
194                 rl_printf("Failed to stop: %s\n", error.name);
195                 dbus_error_free(&error);
196                 return;
197         }
198
199         rl_printf("Stop successful\n");
200 }
201
202 static void cmd_stop(int argc, char *argv[])
203 {
204         if (!check_default_player())
205                 return;
206
207         if (g_dbus_proxy_method_call(default_player, "Stop", NULL, stop_reply,
208                                                         NULL, NULL) == FALSE) {
209                 rl_printf("Failed to stop\n");
210                 return;
211         }
212
213         rl_printf("Attempting to stop\n");
214 }
215
216 static void next_reply(DBusMessage *message, void *user_data)
217 {
218         DBusError error;
219
220         dbus_error_init(&error);
221
222         if (dbus_set_error_from_message(&error, message) == TRUE) {
223                 rl_printf("Failed to jump to next: %s\n", error.name);
224                 dbus_error_free(&error);
225                 return;
226         }
227
228         rl_printf("Next successful\n");
229 }
230
231 static void cmd_next(int argc, char *argv[])
232 {
233         if (!check_default_player())
234                 return;
235
236         if (g_dbus_proxy_method_call(default_player, "Next", NULL, next_reply,
237                                                         NULL, NULL) == FALSE) {
238                 rl_printf("Failed to jump to next\n");
239                 return;
240         }
241
242         rl_printf("Attempting to jump to next\n");
243 }
244
245 static void previous_reply(DBusMessage *message, void *user_data)
246 {
247         DBusError error;
248
249         dbus_error_init(&error);
250
251         if (dbus_set_error_from_message(&error, message) == TRUE) {
252                 rl_printf("Failed to jump to previous: %s\n", error.name);
253                 dbus_error_free(&error);
254                 return;
255         }
256
257         rl_printf("Previous successful\n");
258 }
259
260 static void cmd_previous(int argc, char *argv[])
261 {
262         if (!check_default_player())
263                 return;
264
265         if (g_dbus_proxy_method_call(default_player, "Previous", NULL,
266                                         previous_reply, NULL, NULL) == FALSE) {
267                 rl_printf("Failed to jump to previous\n");
268                 return;
269         }
270
271         rl_printf("Attempting to jump to previous\n");
272 }
273
274 static void fast_forward_reply(DBusMessage *message, void *user_data)
275 {
276         DBusError error;
277
278         dbus_error_init(&error);
279
280         if (dbus_set_error_from_message(&error, message) == TRUE) {
281                 rl_printf("Failed to fast forward: %s\n", error.name);
282                 dbus_error_free(&error);
283                 return;
284         }
285
286         rl_printf("FastForward successful\n");
287 }
288
289 static void cmd_fast_forward(int argc, char *argv[])
290 {
291         if (!check_default_player())
292                 return;
293
294         if (g_dbus_proxy_method_call(default_player, "FastForward", NULL,
295                                 fast_forward_reply, NULL, NULL) == FALSE) {
296                 rl_printf("Failed to jump to previous\n");
297                 return;
298         }
299
300         rl_printf("Fast forward playback\n");
301 }
302
303 static void rewind_reply(DBusMessage *message, void *user_data)
304 {
305         DBusError error;
306
307         dbus_error_init(&error);
308
309         if (dbus_set_error_from_message(&error, message) == TRUE) {
310                 rl_printf("Failed to rewind: %s\n", error.name);
311                 dbus_error_free(&error);
312                 return;
313         }
314
315         rl_printf("Rewind successful\n");
316 }
317
318 static void cmd_rewind(int argc, char *argv[])
319 {
320         if (!check_default_player())
321                 return;
322
323         if (g_dbus_proxy_method_call(default_player, "Rewind", NULL,
324                                         rewind_reply, NULL, NULL) == FALSE) {
325                 rl_printf("Failed to rewind\n");
326                 return;
327         }
328
329         rl_printf("Rewind playback\n");
330 }
331
332 static void generic_callback(const DBusError *error, void *user_data)
333 {
334         char *str = user_data;
335
336         if (dbus_error_is_set(error))
337                 rl_printf("Failed to set %s: %s\n", str, error->name);
338         else
339                 rl_printf("Changing %s succeeded\n", str);
340 }
341
342 static void cmd_equalizer(int argc, char *argv[])
343 {
344         char *value;
345         DBusMessageIter iter;
346
347         if (!check_default_player())
348                 return;
349
350         if (argc < 2) {
351                 rl_printf("Missing on/off argument\n");
352                 return;
353         }
354
355         if (!g_dbus_proxy_get_property(default_player, "Equalizer", &iter)) {
356                 rl_printf("Operation not supported\n");
357                 return;
358         }
359
360         value = g_strdup(argv[1]);
361
362         if (g_dbus_proxy_set_property_basic(default_player, "Equalizer",
363                                                 DBUS_TYPE_STRING, &value,
364                                                 generic_callback, value,
365                                                 g_free) == FALSE) {
366                 rl_printf("Failed to setting equalizer\n");
367                 g_free(value);
368                 return;
369         }
370
371         rl_printf("Attempting to set equalizer\n");
372 }
373
374 static void cmd_repeat(int argc, char *argv[])
375 {
376         char *value;
377         DBusMessageIter iter;
378
379         if (!check_default_player())
380                 return;
381
382         if (argc < 2) {
383                 rl_printf("Missing mode argument\n");
384                 return;
385         }
386
387         if (!g_dbus_proxy_get_property(default_player, "Repeat", &iter)) {
388                 rl_printf("Operation not supported\n");
389                 return;
390         }
391
392         value = g_strdup(argv[1]);
393
394         if (g_dbus_proxy_set_property_basic(default_player, "Repeat",
395                                                 DBUS_TYPE_STRING, &value,
396                                                 generic_callback, value,
397                                                 g_free) == FALSE) {
398                 rl_printf("Failed to set repeat\n");
399                 g_free(value);
400                 return;
401         }
402
403         rl_printf("Attempting to set repeat\n");
404 }
405
406 static void cmd_shuffle(int argc, char *argv[])
407 {
408         char *value;
409         DBusMessageIter iter;
410
411         if (!check_default_player())
412                 return;
413
414         if (argc < 2) {
415                 rl_printf("Missing mode argument\n");
416                 return;
417         }
418
419         if (!g_dbus_proxy_get_property(default_player, "Shuffle", &iter)) {
420                 rl_printf("Operation not supported\n");
421                 return;
422         }
423
424         value = g_strdup(argv[1]);
425
426         if (g_dbus_proxy_set_property_basic(default_player, "Shuffle",
427                                                 DBUS_TYPE_STRING, &value,
428                                                 generic_callback, value,
429                                                 g_free) == FALSE) {
430                 rl_printf("Failed to set shuffle\n");
431                 g_free(value);
432                 return;
433         }
434
435         rl_printf("Attempting to set shuffle\n");
436 }
437
438 static void cmd_scan(int argc, char *argv[])
439 {
440         char *value;
441         DBusMessageIter iter;
442
443         if (!check_default_player())
444                 return;
445
446         if (argc < 2) {
447                 rl_printf("Missing mode argument\n");
448                 return;
449         }
450
451         if (!g_dbus_proxy_get_property(default_player, "Shuffle", &iter)) {
452                 rl_printf("Operation not supported\n");
453                 return;
454         }
455
456         value = g_strdup(argv[1]);
457
458         if (g_dbus_proxy_set_property_basic(default_player, "Shuffle",
459                                                 DBUS_TYPE_STRING, &value,
460                                                 generic_callback, value,
461                                                 g_free) == FALSE) {
462                 rl_printf("Failed to set scan\n");
463                 g_free(value);
464                 return;
465         }
466
467         rl_printf("Attempting to set scan\n");
468 }
469
470 static char *proxy_description(GDBusProxy *proxy, const char *title,
471                                                 const char *description)
472 {
473         const char *path;
474
475         path = g_dbus_proxy_get_path(proxy);
476
477         return g_strdup_printf("%s%s%s%s %s ",
478                                         description ? "[" : "",
479                                         description ? : "",
480                                         description ? "] " : "",
481                                         title, path);
482 }
483
484 static void print_player(GDBusProxy *proxy, const char *description)
485 {
486         char *str;
487
488         str = proxy_description(proxy, "Player", description);
489
490         rl_printf("%s%s\n", str, default_player == proxy ? "[default]" : "");
491
492         g_free(str);
493 }
494
495 static void cmd_list(int argc, char *arg[])
496 {
497         GSList *l;
498
499         for (l = players; l; l = g_slist_next(l)) {
500                 GDBusProxy *proxy = l->data;
501                 print_player(proxy, NULL);
502         }
503 }
504
505 static GDBusProxy *find_player(const char *path)
506 {
507         GSList *l;
508
509         for (l = players; l; l = g_slist_next(l)) {
510                 GDBusProxy *proxy = l->data;
511
512                 if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
513                         return proxy;
514         }
515
516         return NULL;
517 }
518
519 static void print_iter(const char *label, const char *name,
520                                                 DBusMessageIter *iter)
521 {
522         dbus_bool_t valbool;
523         dbus_uint32_t valu32;
524         dbus_uint16_t valu16;
525         dbus_int16_t vals16;
526         const char *valstr;
527         DBusMessageIter subiter;
528
529         if (iter == NULL) {
530                 rl_printf("%s%s is nil\n", label, name);
531                 return;
532         }
533
534         switch (dbus_message_iter_get_arg_type(iter)) {
535         case DBUS_TYPE_INVALID:
536                 rl_printf("%s%s is invalid\n", label, name);
537                 break;
538         case DBUS_TYPE_STRING:
539         case DBUS_TYPE_OBJECT_PATH:
540                 dbus_message_iter_get_basic(iter, &valstr);
541                 rl_printf("%s%s: %s\n", label, name, valstr);
542                 break;
543         case DBUS_TYPE_BOOLEAN:
544                 dbus_message_iter_get_basic(iter, &valbool);
545                 rl_printf("%s%s: %s\n", label, name,
546                                         valbool == TRUE ? "yes" : "no");
547                 break;
548         case DBUS_TYPE_UINT32:
549                 dbus_message_iter_get_basic(iter, &valu32);
550                 rl_printf("%s%s: 0x%06x\n", label, name, valu32);
551                 break;
552         case DBUS_TYPE_UINT16:
553                 dbus_message_iter_get_basic(iter, &valu16);
554                 rl_printf("%s%s: 0x%04x\n", label, name, valu16);
555                 break;
556         case DBUS_TYPE_INT16:
557                 dbus_message_iter_get_basic(iter, &vals16);
558                 rl_printf("%s%s: %d\n", label, name, vals16);
559                 break;
560         case DBUS_TYPE_VARIANT:
561                 dbus_message_iter_recurse(iter, &subiter);
562                 print_iter(label, name, &subiter);
563                 break;
564         case DBUS_TYPE_ARRAY:
565                 dbus_message_iter_recurse(iter, &subiter);
566                 while (dbus_message_iter_get_arg_type(&subiter) !=
567                                                         DBUS_TYPE_INVALID) {
568                         print_iter(label, name, &subiter);
569                         dbus_message_iter_next(&subiter);
570                 }
571                 break;
572         case DBUS_TYPE_DICT_ENTRY:
573                 dbus_message_iter_recurse(iter, &subiter);
574                 dbus_message_iter_get_basic(&subiter, &valstr);
575                 dbus_message_iter_next(&subiter);
576                 print_iter(label, valstr, &subiter);
577                 break;
578         default:
579                 rl_printf("%s%s has unsupported type\n", label, name);
580                 break;
581         }
582 }
583
584 static void print_property(GDBusProxy *proxy, const char *name)
585 {
586         DBusMessageIter iter;
587
588         if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
589                 return;
590
591         print_iter("\t", name, &iter);
592 }
593
594 static GDBusProxy *find_folder(const char *path)
595 {
596         GSList *l;
597
598         for (l = folders; l; l = g_slist_next(l)) {
599                 GDBusProxy *proxy = l->data;
600
601                 if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
602                         return proxy;
603         }
604
605         return NULL;
606 }
607
608 static void cmd_show_item(int argc, char *argv[])
609 {
610         GDBusProxy *proxy;
611
612         if (argc < 2) {
613                 rl_printf("Missing item address argument\n");
614                 return;
615         }
616
617         proxy = find_item(argv[1]);
618         if (!proxy) {
619                 rl_printf("Item %s not available\n", argv[1]);
620                 return;
621         }
622
623         rl_printf("Item %s\n", g_dbus_proxy_get_path(proxy));
624
625         print_property(proxy, "Player");
626         print_property(proxy, "Name");
627         print_property(proxy, "Type");
628         print_property(proxy, "FolderType");
629         print_property(proxy, "Playable");
630         print_property(proxy, "Metadata");
631 }
632
633 static void cmd_show(int argc, char *argv[])
634 {
635         GDBusProxy *proxy;
636         GDBusProxy *folder;
637         GDBusProxy *item;
638         DBusMessageIter iter;
639         const char *path;
640
641         if (argc < 2) {
642                 if (check_default_player() == FALSE)
643                         return;
644
645                 proxy = default_player;
646         } else {
647                 proxy = find_player(argv[1]);
648                 if (!proxy) {
649                         rl_printf("Player %s not available\n", argv[1]);
650                         return;
651                 }
652         }
653
654         rl_printf("Player %s\n", g_dbus_proxy_get_path(proxy));
655
656         print_property(proxy, "Name");
657         print_property(proxy, "Repeat");
658         print_property(proxy, "Equalizer");
659         print_property(proxy, "Shuffle");
660         print_property(proxy, "Scan");
661         print_property(proxy, "Status");
662         print_property(proxy, "Position");
663         print_property(proxy, "Track");
664
665         folder = find_folder(g_dbus_proxy_get_path(proxy));
666         if (folder == NULL)
667                 return;
668
669         rl_printf("Folder %s\n", g_dbus_proxy_get_path(proxy));
670
671         print_property(folder, "Name");
672         print_property(folder, "NumberOfItems");
673
674         if (!g_dbus_proxy_get_property(proxy, "Playlist", &iter))
675                 return;
676
677         dbus_message_iter_get_basic(&iter, &path);
678
679         item = find_item(path);
680         if (item == NULL)
681                 return;
682
683         rl_printf("Playlist %s\n", path);
684
685         print_property(item, "Name");
686 }
687
688 static void cmd_select(int argc, char *argv[])
689 {
690         GDBusProxy *proxy;
691
692         if (argc < 2) {
693                 rl_printf("Missing player address argument\n");
694                 return;
695         }
696
697         proxy = find_player(argv[1]);
698         if (proxy == NULL) {
699                 rl_printf("Player %s not available\n", argv[1]);
700                 return;
701         }
702
703         if (default_player == proxy)
704                 return;
705
706         default_player = proxy,
707         print_player(proxy, NULL);
708 }
709
710 static void change_folder_reply(DBusMessage *message, void *user_data)
711 {
712         DBusError error;
713
714         dbus_error_init(&error);
715
716         if (dbus_set_error_from_message(&error, message) == TRUE) {
717                 rl_printf("Failed to change folder: %s\n", error.name);
718                 dbus_error_free(&error);
719                 return;
720         }
721
722         rl_printf("ChangeFolder successful\n");
723 }
724
725 static void change_folder_setup(DBusMessageIter *iter, void *user_data)
726 {
727         const char *path = user_data;
728
729         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
730 }
731
732 static void cmd_change_folder(int argc, char *argv[])
733 {
734         GDBusProxy *proxy;
735
736         if (argc < 2) {
737                 rl_printf("Missing item argument\n");
738                 return;
739         }
740
741         if (dbus_validate_path(argv[1], NULL) == FALSE) {
742                 rl_printf("Not a valid path\n");
743                 return;
744         }
745
746         if (check_default_player() == FALSE)
747                 return;
748
749         proxy = find_folder(g_dbus_proxy_get_path(default_player));
750         if (proxy == NULL) {
751                 rl_printf("Operation not supported\n");
752                 return;
753         }
754
755         if (g_dbus_proxy_method_call(proxy, "ChangeFolder", change_folder_setup,
756                                 change_folder_reply, argv[1], NULL) == FALSE) {
757                 rl_printf("Failed to change current folder\n");
758                 return;
759         }
760
761         rl_printf("Attempting to change folder\n");
762 }
763
764 static void append_variant(DBusMessageIter *iter, int type, void *val)
765 {
766         DBusMessageIter value;
767         char sig[2] = { type, '\0' };
768
769         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
770
771         dbus_message_iter_append_basic(&value, type, val);
772
773         dbus_message_iter_close_container(iter, &value);
774 }
775
776 static void dict_append_entry(DBusMessageIter *dict,
777                         const char *key, int type, void *val)
778 {
779         DBusMessageIter entry;
780
781         if (type == DBUS_TYPE_STRING) {
782                 const char *str = *((const char **) val);
783                 if (str == NULL)
784                         return;
785         }
786
787         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
788                                                         NULL, &entry);
789
790         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
791
792         append_variant(&entry, type, val);
793
794         dbus_message_iter_close_container(dict, &entry);
795 }
796
797 struct list_items_args {
798         int start;
799         int end;
800 };
801
802 static void list_items_setup(DBusMessageIter *iter, void *user_data)
803 {
804         struct list_items_args *args = user_data;
805         DBusMessageIter dict;
806
807         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
808                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
809                                         DBUS_TYPE_STRING_AS_STRING
810                                         DBUS_TYPE_VARIANT_AS_STRING
811                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
812                                         &dict);
813
814         if (args->start < 0)
815                 goto done;
816
817         dict_append_entry(&dict, "Start", DBUS_TYPE_UINT32, &args->start);
818
819         if (args->end < 0)
820                 goto done;
821
822         dict_append_entry(&dict, "End", DBUS_TYPE_UINT32, &args->end);
823
824 done:
825         dbus_message_iter_close_container(iter, &dict);
826 }
827
828 static void list_items_reply(DBusMessage *message, void *user_data)
829 {
830         DBusError error;
831
832         dbus_error_init(&error);
833
834         if (dbus_set_error_from_message(&error, message) == TRUE) {
835                 rl_printf("Failed to list items: %s\n", error.name);
836                 dbus_error_free(&error);
837                 return;
838         }
839
840         rl_printf("ListItems successful\n");
841 }
842
843 static void cmd_list_items(int argc, char *argv[])
844 {
845         GDBusProxy *proxy;
846         struct list_items_args *args;
847
848         if (check_default_player() == FALSE)
849                 return;
850
851         proxy = find_folder(g_dbus_proxy_get_path(default_player));
852         if (proxy == NULL) {
853                 rl_printf("Operation not supported\n");
854                 return;
855         }
856
857         args = g_new0(struct list_items_args, 1);
858         args->start = -1;
859         args->end = -1;
860
861         if (argc < 2)
862                 goto done;
863
864         errno = 0;
865         args->start = strtol(argv[1], NULL, 10);
866         if (errno != 0) {
867                 rl_printf("%s(%d)\n", strerror(errno), errno);
868                 g_free(args);
869                 return;
870         }
871
872         if (argc < 3)
873                 goto done;
874
875         errno = 0;
876         args->end = strtol(argv[2], NULL, 10);
877         if (errno != 0) {
878                 rl_printf("%s(%d)\n", strerror(errno), errno);
879                 g_free(args);
880                 return;
881         }
882
883 done:
884         if (g_dbus_proxy_method_call(proxy, "ListItems", list_items_setup,
885                                 list_items_reply, args, g_free) == FALSE) {
886                 rl_printf("Failed to change current folder\n");
887                 g_free(args);
888                 return;
889         }
890
891         rl_printf("Attempting to list items\n");
892 }
893
894 static void search_setup(DBusMessageIter *iter, void *user_data)
895 {
896         char *string = user_data;
897         DBusMessageIter dict;
898
899         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string);
900
901         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
902                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
903                                         DBUS_TYPE_STRING_AS_STRING
904                                         DBUS_TYPE_VARIANT_AS_STRING
905                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
906                                         &dict);
907
908         dbus_message_iter_close_container(iter, &dict);
909 }
910
911 static void search_reply(DBusMessage *message, void *user_data)
912 {
913         DBusError error;
914
915         dbus_error_init(&error);
916
917         if (dbus_set_error_from_message(&error, message) == TRUE) {
918                 rl_printf("Failed to search: %s\n", error.name);
919                 dbus_error_free(&error);
920                 return;
921         }
922
923         rl_printf("Search successful\n");
924 }
925
926 static void cmd_search(int argc, char *argv[])
927 {
928         GDBusProxy *proxy;
929         char *string;
930
931         if (argc < 2) {
932                 rl_printf("Missing string argument\n");
933                 return;
934         }
935
936         if (check_default_player() == FALSE)
937                 return;
938
939         proxy = find_folder(g_dbus_proxy_get_path(default_player));
940         if (proxy == NULL) {
941                 rl_printf("Operation not supported\n");
942                 return;
943         }
944
945         string = g_strdup(argv[1]);
946
947         if (g_dbus_proxy_method_call(proxy, "Search", search_setup,
948                                 search_reply, string, g_free) == FALSE) {
949                 rl_printf("Failed to search\n");
950                 g_free(string);
951                 return;
952         }
953
954         rl_printf("Attempting to search\n");
955 }
956
957 static void add_to_nowplaying_reply(DBusMessage *message, void *user_data)
958 {
959         DBusError error;
960
961         dbus_error_init(&error);
962
963         if (dbus_set_error_from_message(&error, message) == TRUE) {
964                 rl_printf("Failed to queue: %s\n", error.name);
965                 dbus_error_free(&error);
966                 return;
967         }
968
969         rl_printf("AddToNowPlaying successful\n");
970 }
971
972 static void cmd_queue(int argc, char *argv[])
973 {
974         GDBusProxy *proxy;
975
976         if (argc < 2) {
977                 rl_printf("Missing item address argument\n");
978                 return;
979         }
980
981         proxy = find_item(argv[1]);
982         if (proxy == NULL) {
983                 rl_printf("Item %s not available\n", argv[1]);
984                 return;
985         }
986
987         if (g_dbus_proxy_method_call(proxy, "AddtoNowPlaying", NULL,
988                                         add_to_nowplaying_reply, NULL,
989                                         NULL) == FALSE) {
990                 rl_printf("Failed to play\n");
991                 return;
992         }
993
994         rl_printf("Attempting to queue %s\n", argv[1]);
995 }
996
997 static const struct {
998         const char *cmd;
999         const char *arg;
1000         void (*func) (int argc, char *argv[]);
1001         const char *desc;
1002 } cmd_table[] = {
1003         { "list",         NULL,       cmd_list, "List available players" },
1004         { "show",         "[player]", cmd_show, "Player information" },
1005         { "select",       "<player>", cmd_select, "Select default player" },
1006         { "play",         "[item]",   cmd_play, "Start playback" },
1007         { "pause",        NULL,       cmd_pause, "Pause playback" },
1008         { "stop",         NULL,       cmd_stop, "Stop playback" },
1009         { "next",         NULL,       cmd_next, "Jump to next item" },
1010         { "previous",     NULL,       cmd_previous, "Jump to previous item" },
1011         { "fast-forward", NULL,       cmd_fast_forward,
1012                                                 "Fast forward playback" },
1013         { "rewind",       NULL,       cmd_rewind, "Rewind playback" },
1014         { "equalizer",    "<on/off>", cmd_equalizer,
1015                                                 "Enable/Disable equalizer"},
1016         { "repeat",       "<singletrack/alltrack/group/off>", cmd_repeat,
1017                                                 "Set repeat mode"},
1018         { "shuffle",      "<alltracks/group/off>", cmd_shuffle,
1019                                                 "Set shuffle mode"},
1020         { "scan",         "<alltracks/group/off>", cmd_scan,
1021                                                 "Set scan mode"},
1022         { "change-folder", "<item>",  cmd_change_folder,
1023                                                 "Change current folder" },
1024         { "list-items", "[start] [end]",  cmd_list_items,
1025                                         "List items of current folder" },
1026         { "search",     "string",     cmd_search,
1027                                         "Search items containing string" },
1028         { "queue",       "<item>",    cmd_queue, "Add item to playlist queue" },
1029         { "show-item",   "<item>",    cmd_show_item, "Show item information" },
1030         { "quit",         NULL,       cmd_quit, "Quit program" },
1031         { "exit",         NULL,       cmd_quit },
1032         { "help" },
1033         {}
1034 };
1035
1036 static char *cmd_generator(const char *text, int state)
1037 {
1038         static int index, len;
1039         const char *cmd;
1040
1041         if (!state) {
1042                 index = 0;
1043                 len = strlen(text);
1044         }
1045
1046         while ((cmd = cmd_table[index].cmd)) {
1047                 index++;
1048
1049                 if (!strncmp(cmd, text, len))
1050                         return strdup(cmd);
1051         }
1052
1053         return NULL;
1054 }
1055
1056 static char **cmd_completion(const char *text, int start, int end)
1057 {
1058         char **matches = NULL;
1059
1060         if (start == 0) {
1061                 rl_completion_display_matches_hook = NULL;
1062                 matches = rl_completion_matches(text, cmd_generator);
1063         }
1064
1065         if (!matches)
1066                 rl_attempted_completion_over = 1;
1067
1068         return matches;
1069 }
1070
1071 static void rl_handler(char *input)
1072 {
1073         int argc;
1074         char **argv = NULL;
1075         int i;
1076
1077         if (!input) {
1078                 rl_insert_text("quit");
1079                 rl_redisplay();
1080                 rl_crlf();
1081                 g_main_loop_quit(main_loop);
1082                 return;
1083         }
1084
1085         if (!strlen(input))
1086                 goto done;
1087
1088         g_strstrip(input);
1089         add_history(input);
1090
1091         argv = g_strsplit(input, " ", -1);
1092         if (argv == NULL)
1093                 goto done;
1094
1095         for (argc = 0; argv[argc];)
1096                 argc++;
1097
1098         if (argc == 0)
1099                 goto done;
1100
1101         for (i = 0; cmd_table[i].cmd; i++) {
1102                 if (strcmp(argv[0], cmd_table[i].cmd))
1103                         continue;
1104
1105                 if (cmd_table[i].func) {
1106                         cmd_table[i].func(argc, argv);
1107                         goto done;
1108                 }
1109         }
1110
1111         if (strcmp(argv[0], "help")) {
1112                 printf("Invalid command\n");
1113                 goto done;
1114         }
1115
1116         printf("Available commands:\n");
1117
1118         for (i = 0; cmd_table[i].cmd; i++) {
1119                 if (cmd_table[i].desc)
1120                         printf("\t%s %s\t%s\n", cmd_table[i].cmd,
1121                                                 cmd_table[i].arg ? : "    ",
1122                                                 cmd_table[i].desc);
1123         }
1124
1125 done:
1126         g_strfreev(argv);
1127         free(input);
1128 }
1129
1130 static gboolean option_version = FALSE;
1131
1132 static GOptionEntry options[] = {
1133         { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
1134                                 "Show version information and exit" },
1135         { NULL },
1136 };
1137
1138 static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
1139                                                         gpointer user_data)
1140 {
1141         static unsigned int __terminated = 0;
1142         struct signalfd_siginfo si;
1143         ssize_t result;
1144         int fd;
1145
1146         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
1147                 g_main_loop_quit(main_loop);
1148                 return FALSE;
1149         }
1150
1151         fd = g_io_channel_unix_get_fd(channel);
1152
1153         result = read(fd, &si, sizeof(si));
1154         if (result != sizeof(si))
1155                 return FALSE;
1156
1157         switch (si.ssi_signo) {
1158         case SIGINT:
1159                 rl_replace_line("", 0);
1160                 rl_crlf();
1161                 rl_on_new_line();
1162                 rl_redisplay();
1163                 break;
1164         case SIGTERM:
1165                 if (__terminated == 0) {
1166                         rl_replace_line("", 0);
1167                         rl_crlf();
1168                         g_main_loop_quit(main_loop);
1169                 }
1170
1171                 __terminated = 1;
1172                 break;
1173         }
1174
1175         return TRUE;
1176 }
1177
1178 static guint setup_signalfd(void)
1179 {
1180         GIOChannel *channel;
1181         guint source;
1182         sigset_t mask;
1183         int fd;
1184
1185         sigemptyset(&mask);
1186         sigaddset(&mask, SIGINT);
1187         sigaddset(&mask, SIGTERM);
1188
1189         if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
1190                 perror("Failed to set signal mask");
1191                 return 0;
1192         }
1193
1194         fd = signalfd(-1, &mask, 0);
1195         if (fd < 0) {
1196                 perror("Failed to create signal descriptor");
1197                 return 0;
1198         }
1199
1200         channel = g_io_channel_unix_new(fd);
1201
1202         g_io_channel_set_close_on_unref(channel, TRUE);
1203         g_io_channel_set_encoding(channel, NULL, NULL);
1204         g_io_channel_set_buffered(channel, FALSE);
1205
1206         source = g_io_add_watch(channel,
1207                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
1208                                 signal_handler, NULL);
1209
1210         g_io_channel_unref(channel);
1211
1212         return source;
1213 }
1214
1215 static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
1216                                                         gpointer user_data)
1217 {
1218         if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
1219                 g_main_loop_quit(main_loop);
1220                 return FALSE;
1221         }
1222
1223         rl_callback_read_char();
1224         return TRUE;
1225 }
1226
1227 static guint setup_standard_input(void)
1228 {
1229         GIOChannel *channel;
1230         guint source;
1231
1232         channel = g_io_channel_unix_new(fileno(stdin));
1233
1234         source = g_io_add_watch(channel,
1235                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
1236                                 input_handler, NULL);
1237
1238         g_io_channel_unref(channel);
1239
1240         return source;
1241 }
1242
1243 static void player_added(GDBusProxy *proxy)
1244 {
1245         players = g_slist_append(players, proxy);
1246
1247         if (default_player == NULL)
1248                 default_player = proxy;
1249
1250         print_player(proxy, COLORED_NEW);
1251 }
1252
1253 static void print_folder(GDBusProxy *proxy, const char *description)
1254 {
1255         const char *path;
1256
1257         path = g_dbus_proxy_get_path(proxy);
1258
1259         rl_printf("%s%s%sFolder %s\n", description ? "[" : "",
1260                                         description ? : "",
1261                                         description ? "] " : "",
1262                                         path);
1263 }
1264
1265 static void folder_added(GDBusProxy *proxy)
1266 {
1267         folders = g_slist_append(folders, proxy);
1268
1269         print_folder(proxy, COLORED_NEW);
1270 }
1271
1272 static void print_item(GDBusProxy *proxy, const char *description)
1273 {
1274         const char *path, *name;
1275         DBusMessageIter iter;
1276
1277         path = g_dbus_proxy_get_path(proxy);
1278
1279         if (g_dbus_proxy_get_property(proxy, "Name", &iter))
1280                 dbus_message_iter_get_basic(&iter, &name);
1281         else
1282                 name = "<unknown>";
1283
1284         rl_printf("%s%s%sItem %s %s\n", description ? "[" : "",
1285                                         description ? : "",
1286                                         description ? "] " : "",
1287                                         path, name);
1288 }
1289
1290 static void item_added(GDBusProxy *proxy)
1291 {
1292         items = g_slist_append(items, proxy);
1293
1294         print_item(proxy, COLORED_NEW);
1295 }
1296
1297 static void proxy_added(GDBusProxy *proxy, void *user_data)
1298 {
1299         const char *interface;
1300
1301         interface = g_dbus_proxy_get_interface(proxy);
1302
1303         if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
1304                 player_added(proxy);
1305         else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
1306                 folder_added(proxy);
1307         else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
1308                 item_added(proxy);
1309 }
1310
1311 static void player_removed(GDBusProxy *proxy)
1312 {
1313         print_player(proxy, COLORED_DEL);
1314
1315         if (default_player == proxy)
1316                 default_player = NULL;
1317
1318         players = g_slist_remove(players, proxy);
1319 }
1320
1321 static void folder_removed(GDBusProxy *proxy)
1322 {
1323         folders = g_slist_remove(folders, proxy);
1324
1325         print_folder(proxy, COLORED_DEL);
1326 }
1327
1328 static void item_removed(GDBusProxy *proxy)
1329 {
1330         items = g_slist_remove(items, proxy);
1331
1332         print_item(proxy, COLORED_DEL);
1333 }
1334
1335 static void proxy_removed(GDBusProxy *proxy, void *user_data)
1336 {
1337         const char *interface;
1338
1339         interface = g_dbus_proxy_get_interface(proxy);
1340
1341         if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
1342                 player_removed(proxy);
1343         if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
1344                 folder_removed(proxy);
1345         if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
1346                 item_removed(proxy);
1347 }
1348
1349 static void player_property_changed(GDBusProxy *proxy, const char *name,
1350                                                 DBusMessageIter *iter)
1351 {
1352         char *str;
1353
1354         str = proxy_description(proxy, "Player", COLORED_CHG);
1355         print_iter(str, name, iter);
1356         g_free(str);
1357 }
1358
1359 static void folder_property_changed(GDBusProxy *proxy, const char *name,
1360                                                 DBusMessageIter *iter)
1361 {
1362         char *str;
1363
1364         str = proxy_description(proxy, "Folder", COLORED_CHG);
1365         print_iter(str, name, iter);
1366         g_free(str);
1367 }
1368
1369 static void item_property_changed(GDBusProxy *proxy, const char *name,
1370                                                 DBusMessageIter *iter)
1371 {
1372         char *str;
1373
1374         str = proxy_description(proxy, "Item", COLORED_CHG);
1375         print_iter(str, name, iter);
1376         g_free(str);
1377 }
1378
1379 static void property_changed(GDBusProxy *proxy, const char *name,
1380                                         DBusMessageIter *iter, void *user_data)
1381 {
1382         const char *interface;
1383
1384         interface = g_dbus_proxy_get_interface(proxy);
1385
1386         if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
1387                 player_property_changed(proxy, name, iter);
1388         else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
1389                 folder_property_changed(proxy, name, iter);
1390         else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
1391                 item_property_changed(proxy, name, iter);
1392 }
1393
1394 int main(int argc, char *argv[])
1395 {
1396         GOptionContext *context;
1397         GError *error = NULL;
1398         GDBusClient *client;
1399         guint signal, input;
1400
1401         context = g_option_context_new(NULL);
1402         g_option_context_add_main_entries(context, options, NULL);
1403
1404         if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
1405                 if (error != NULL) {
1406                         g_printerr("%s\n", error->message);
1407                         g_error_free(error);
1408                 } else
1409                         g_printerr("An unknown error occurred\n");
1410                 exit(1);
1411         }
1412
1413         g_option_context_free(context);
1414
1415         if (option_version == TRUE) {
1416                 printf("%s\n", VERSION);
1417                 exit(0);
1418         }
1419
1420         main_loop = g_main_loop_new(NULL, FALSE);
1421         dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
1422
1423         rl_attempted_completion_function = cmd_completion;
1424
1425         rl_erase_empty_line = 1;
1426         rl_callback_handler_install(NULL, rl_handler);
1427
1428         rl_set_prompt(PROMPT_OFF);
1429         rl_redisplay();
1430
1431         input = setup_standard_input();
1432         signal = setup_signalfd();
1433         client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
1434
1435         g_dbus_client_set_connect_watch(client, connect_handler, NULL);
1436         g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
1437
1438         g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
1439                                                         property_changed, NULL);
1440
1441         g_main_loop_run(main_loop);
1442
1443         g_dbus_client_unref(client);
1444         g_source_remove(signal);
1445         g_source_remove(input);
1446
1447         rl_message("");
1448         rl_callback_handler_remove();
1449
1450         dbus_connection_unref(dbus_conn);
1451         g_main_loop_unref(main_loop);
1452
1453         return 0;
1454 }