1 /*
2 * MOC - music on console
3 * Copyright (C) 2004 - 2006 Damian Pietras <daper@daper.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 */
11
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #include <stdarg.h>
17 #include <locale.h>
18 #include <unistd.h>
19 #include <assert.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <time.h>
25 #include <signal.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/time.h>
30 #include <sys/wait.h>
31 #ifdef HAVE_SYS_SELECT_H
32 # include <sys/select.h>
33 #endif
34
35 /* Include dirent for various systems */
36 #ifdef HAVE_DIRENT_H
37 # include <dirent.h>
38 #else
39 # define dirent direct
40 # if HAVE_SYS_NDIR_H
41 # include <sys/ndir.h>
42 # endif
43 #endif
44
45 #define DEBUG
46
47 #include "common.h"
48 #include "log.h"
49 #include "interface_elements.h"
50 #include "interface.h"
51 #include "lists.h"
52 #include "playlist.h"
53 #include "playlist_file.h"
54 #include "protocol.h"
55 #include "keys.h"
56 #include "options.h"
57 #include "files.h"
58 #include "decoder.h"
59 #include "themes.h"
60 #include "softmixer.h"
61 #include "utf8.h"
62
63 #define INTERFACE_LOG "mocp_client_log"
64 #define PLAYLIST_FILE "playlist.m3u"
65
66 #define QUEUE_CLEAR_THRESH 128
67
68 /* Socket of the server connection. */
69 static int srv_sock = -1;
70
71 static struct plist *playlist = NULL; /* our playlist */
72 static struct plist *queue = NULL; /* our queue */
73 static struct plist *dir_plist = NULL; /* contents of the current directory */
74
75 /* Queue for events coming from the server. */
76 static struct event_queue events;
77
78 /* Current working directory (the directory we show). */
79 static char cwd[PATH_MAX] = "";
80
81 /* If the user presses quit, or we receive a termination signal. */
82 static volatile enum want_quit want_quit = NO_QUIT;
83
84 /* If user presses CTRL-C, set this to 1. This should interrupt long
85 * operations which block the interface. */
86 static volatile int wants_interrupt = 0;
87
88 #ifdef SIGWINCH
89 /* If we get SIGWINCH. */
90 static volatile int want_resize = 0;
91 #endif
92
93 /* Are we waiting for the playlist we have loaded and sent to the clients? */
94 static int waiting_for_plist_load = 0;
95
96 /* Information about the currently played file. */
97 static struct file_info curr_file;
98
99 /* Silent seeking - where we are in seconds. -1 - no seeking. */
100 static int silent_seek_pos = -1;
101 static time_t silent_seek_key_last = (time_t)0; /* when the silent seek key was
102 last used */
103
104 /* When the menu was last moved (arrow keys, page up, etc.) */
105 static time_t last_menu_move_time = (time_t)0;
106
sig_quit(int sig ATTR_UNUSED)107 static void sig_quit (int sig ATTR_UNUSED)
108 {
109 log_signal (sig);
110 want_quit = QUIT_CLIENT;
111 }
112
sig_interrupt(int sig ATTR_UNUSED)113 static void sig_interrupt (int sig ATTR_UNUSED)
114 {
115 log_signal (sig);
116 wants_interrupt = 1;
117 }
118
119 #ifdef SIGWINCH
sig_winch(int sig ATTR_UNUSED)120 static void sig_winch (int sig ATTR_UNUSED)
121 {
122 log_signal (sig);
123 want_resize = 1;
124 }
125 #endif
126
user_wants_interrupt()127 int user_wants_interrupt ()
128 {
129 return wants_interrupt;
130 }
131
clear_interrupt()132 static void clear_interrupt ()
133 {
134 wants_interrupt = 0;
135 }
136
send_int_to_srv(const int num)137 static void send_int_to_srv (const int num)
138 {
139 if (!send_int(srv_sock, num))
140 fatal ("Can't send() int to the server!");
141 }
142
send_str_to_srv(const char * str)143 static void send_str_to_srv (const char *str)
144 {
145 if (!send_str(srv_sock, str))
146 fatal ("Can't send() string to the server!");
147 }
148
send_item_to_srv(const struct plist_item * item)149 static void send_item_to_srv (const struct plist_item *item)
150 {
151 if (!send_item(srv_sock, item))
152 fatal ("Can't send() item to the server!");
153 }
154
get_int_from_srv()155 static int get_int_from_srv ()
156 {
157 int num;
158
159 if (!get_int(srv_sock, &num))
160 fatal ("Can't receive value from the server!");
161
162 return num;
163 }
164
165 /* Returned memory is malloc()ed. */
get_str_from_srv()166 static char *get_str_from_srv ()
167 {
168 char *str = get_str (srv_sock);
169
170 if (!str)
171 fatal ("Can't receive string from the server!");
172
173 return str;
174 }
175
recv_tags_from_srv()176 static struct file_tags *recv_tags_from_srv ()
177 {
178 struct file_tags *tags = recv_tags (srv_sock);
179
180 if (!tags)
181 fatal ("Can't receive tags from the server!");
182
183 return tags;
184 }
185
186 /* Noblocking version of get_int_from_srv(): return 0 if there are no data. */
get_int_from_srv_noblock(int * num)187 static int get_int_from_srv_noblock (int *num)
188 {
189 enum noblock_io_status st;
190
191 if ((st = get_int_noblock(srv_sock, num)) == NB_IO_ERR)
192 fatal ("Can't receive value from the server!");
193
194 return st == NB_IO_OK ? 1 : 0;
195 }
196
recv_item_from_srv()197 static struct plist_item *recv_item_from_srv ()
198 {
199 struct plist_item *item;
200
201 if (!(item = recv_item(srv_sock)))
202 fatal ("Can't receive item from the server!");
203
204 return item;
205 }
206
recv_tags_data_from_srv()207 static struct tag_ev_response *recv_tags_data_from_srv ()
208 {
209 struct tag_ev_response *r;
210
211 r = (struct tag_ev_response *)xmalloc (sizeof(struct tag_ev_response));
212
213 r->file = get_str_from_srv ();
214 if (!(r->tags = recv_tags(srv_sock)))
215 fatal ("Can't receive tags event's data from the server!");
216
217 return r;
218 }
219
recv_move_ev_data_from_srv()220 static struct move_ev_data *recv_move_ev_data_from_srv ()
221 {
222 struct move_ev_data *d;
223
224 if (!(d = recv_move_ev_data(srv_sock)))
225 fatal ("Can't receive move data from the server!");
226
227 return d;
228 }
229
230 /* Receive data for the given type of event and return them. Return NULL if
231 * there is no data for the event. */
get_event_data(const int type)232 static void *get_event_data (const int type)
233 {
234 switch (type) {
235 case EV_PLIST_ADD:
236 case EV_QUEUE_ADD:
237 return recv_item_from_srv ();
238 case EV_PLIST_DEL:
239 case EV_QUEUE_DEL:
240 case EV_STATUS_MSG:
241 case EV_SRV_ERROR:
242 return get_str_from_srv ();
243 case EV_FILE_TAGS:
244 return recv_tags_data_from_srv ();
245 case EV_PLIST_MOVE:
246 case EV_QUEUE_MOVE:
247 return recv_move_ev_data_from_srv ();
248 }
249
250 return NULL;
251 }
252
253 /* Wait for EV_DATA handling other events. */
wait_for_data()254 static void wait_for_data ()
255 {
256 int event;
257
258 do {
259 event = get_int_from_srv ();
260 if (event == EV_EXIT)
261 interface_fatal ("The server exited!");
262 if (event != EV_DATA)
263 event_push (&events, event, get_event_data(event));
264 } while (event != EV_DATA);
265 }
266
267 /* Get an integer value from the server that will arrive after EV_DATA. */
get_data_int()268 static int get_data_int ()
269 {
270 wait_for_data ();
271 return get_int_from_srv ();
272 }
273
274 /* Get a string value from the server that will arrive after EV_DATA. */
get_data_str()275 static char *get_data_str ()
276 {
277 wait_for_data ();
278 return get_str_from_srv ();
279 }
280
get_data_tags()281 static struct file_tags *get_data_tags ()
282 {
283 wait_for_data ();
284 return recv_tags_from_srv ();
285 }
286
send_tags_request(const char * file,const int tags_sel)287 static int send_tags_request (const char *file, const int tags_sel)
288 {
289 assert (file != NULL);
290 assert (tags_sel != 0);
291
292 if (file_type(file) == F_SOUND) {
293 send_int_to_srv (CMD_GET_FILE_TAGS);
294 send_str_to_srv (file);
295 send_int_to_srv (tags_sel);
296 debug ("Asking for tags for %s", file);
297
298 return 1;
299 }
300 else {
301 debug ("Not sending tags request for URL (%s)", file);
302 return 0;
303 }
304 }
305
306 /* Send all items from this playlist to other clients. */
send_items_to_clients(const struct plist * plist)307 static void send_items_to_clients (const struct plist *plist)
308 {
309 int i;
310
311 for (i = 0; i < plist->num; i++)
312 if (!plist_deleted(plist, i)) {
313 send_int_to_srv (CMD_CLI_PLIST_ADD);
314 send_item_to_srv (&plist->items[i]);
315 }
316 }
317
init_playlists()318 static void init_playlists ()
319 {
320 dir_plist = (struct plist *)xmalloc (sizeof(struct plist));
321 plist_init (dir_plist);
322 playlist = (struct plist *)xmalloc (sizeof(struct plist));
323 plist_init (playlist);
324 queue = (struct plist *)xmalloc (sizeof(struct plist));
325 plist_init (queue);
326
327 /* set serial numbers for the playlist */
328 send_int_to_srv (CMD_GET_SERIAL);
329 plist_set_serial (playlist, get_data_int());
330 }
331
file_info_reset(struct file_info * f)332 static void file_info_reset (struct file_info *f)
333 {
334 f->file = NULL;
335 f->tags = NULL;
336 f->title = NULL;
337 f->bitrate = -1;
338 f->rate = -1;
339 f->curr_time = -1;
340 f->total_time = -1;
341 f->channels = 1;
342 f->state = STATE_STOP;
343 }
344
file_info_cleanup(struct file_info * f)345 static void file_info_cleanup (struct file_info *f)
346 {
347 if (f->tags)
348 tags_free (f->tags);
349 if (f->file)
350 free (f->file);
351 if (f->title)
352 free (f->title);
353
354 f->file = NULL;
355 f->tags = NULL;
356 f->title = NULL;
357 }
358
359 /* Initialise the block marker information. */
file_info_block_init(struct file_info * f)360 static void file_info_block_init (struct file_info *f)
361 {
362 f->block_file = NULL;
363 }
364
365 /* Reset the block start and end markers. */
file_info_block_reset(struct file_info * f)366 static void file_info_block_reset (struct file_info *f)
367 {
368 if (f->block_file)
369 free (f->block_file);
370 f->block_file = NULL;
371 }
372
373 /* Enter the current time into a block start or end marker. */
file_info_block_mark(int * marker)374 static void file_info_block_mark (int *marker)
375 {
376 if (curr_file.state == STATE_STOP) {
377 error ("Cannot make block marks while stopped.");
378 } else if (file_type (curr_file.file) == F_URL) {
379 error ("Cannot make block marks in URLs.");
380 } else if (file_type (curr_file.file) != F_SOUND) {
381 error ("Cannot make block marks in non-audio files.");
382 } else if (!curr_file.block_file) {
383 error ("Cannot make block marks in files of unknown duration.");
384 } else {
385 *marker = curr_file.curr_time;
386 iface_set_block (curr_file.block_start, curr_file.block_end);
387 }
388 }
389
390 /* Get an integer option from the server (like shuffle) and set it. */
sync_int_option(const char * name)391 static void sync_int_option (const char *name)
392 {
393 int value;
394
395 send_int_to_srv (CMD_GET_OPTION);
396 send_str_to_srv (name);
397 value = get_data_int ();
398 options_set_int (name, value);
399 iface_set_option_state (name, value);
400 }
401
402 /* Get the server options and set our options like them. */
get_server_options()403 static void get_server_options ()
404 {
405 sync_int_option ("Shuffle");
406 sync_int_option ("Repeat");
407 sync_int_option ("AutoNext");
408 }
409
get_server_plist_serial()410 static int get_server_plist_serial ()
411 {
412 send_int_to_srv (CMD_PLIST_GET_SERIAL);
413 return get_data_int ();
414 }
415
get_mixer_value()416 static int get_mixer_value ()
417 {
418 send_int_to_srv (CMD_GET_MIXER);
419 return get_data_int ();
420 }
421
get_state()422 static int get_state ()
423 {
424 send_int_to_srv (CMD_GET_STATE);
425 return get_data_int ();
426 }
427
get_channels()428 static int get_channels ()
429 {
430 send_int_to_srv (CMD_GET_CHANNELS);
431 return get_data_int ();
432 }
433
get_rate()434 static int get_rate ()
435 {
436 send_int_to_srv (CMD_GET_RATE);
437 return get_data_int ();
438 }
439
get_bitrate()440 static int get_bitrate ()
441 {
442 send_int_to_srv (CMD_GET_BITRATE);
443 return get_data_int ();
444 }
445
get_avg_bitrate()446 static int get_avg_bitrate ()
447 {
448 send_int_to_srv (CMD_GET_AVG_BITRATE);
449 return get_data_int ();
450 }
451
get_curr_time()452 static int get_curr_time ()
453 {
454 send_int_to_srv (CMD_GET_CTIME);
455 return get_data_int ();
456 }
457
get_curr_file()458 static char *get_curr_file ()
459 {
460 send_int_to_srv (CMD_GET_SNAME);
461 return get_data_str ();
462 }
463
update_mixer_value()464 static void update_mixer_value ()
465 {
466 int val;
467
468 val = get_mixer_value ();
469 iface_set_mixer_value (MAX(val, 0));
470 }
471
update_mixer_name()472 static void update_mixer_name ()
473 {
474 char *name;
475
476 send_int_to_srv (CMD_GET_MIXER_CHANNEL_NAME);
477 name = get_data_str();
478 debug ("Mixer name: %s", name);
479
480 iface_set_mixer_name (name);
481
482 free (name);
483
484 update_mixer_value ();
485 }
486
487 /* Make new cwd path from CWD and this path. */
set_cwd(const char * path)488 static void set_cwd (const char *path)
489 {
490 if (path[0] == '/')
491 strcpy (cwd, "/"); /* for absolute path */
492 else if (!cwd[0]) {
493 if (!getcwd(cwd, sizeof(cwd)))
494 fatal ("Can't get CWD: %s", strerror(errno));
495 }
496
497 resolve_path (cwd, sizeof(cwd), path);
498 }
499
500 /* Try to find the directory we can start and set cwd to it. */
set_start_dir()501 static void set_start_dir ()
502 {
503 if (!getcwd(cwd, sizeof (cwd))) {
504 if (errno == ERANGE)
505 fatal ("CWD is larger than PATH_MAX!");
506 strncpy (cwd, get_home (), sizeof (cwd));
507 if (cwd[sizeof (cwd) - 1])
508 fatal ("Home directory path is longer than PATH_MAX!");
509 }
510 }
511
512 /* Set cwd to last directory written to a file, return 1 on success. */
read_last_dir()513 static int read_last_dir ()
514 {
515 FILE *dir_file;
516 int res = 1;
517 int read;
518
519 if (!(dir_file = fopen(create_file_name("last_directory"), "r")))
520 return 0;
521
522 if ((read = fread(cwd, sizeof(char), sizeof(cwd)-1, dir_file)) == 0)
523 res = 0;
524 else
525 cwd[read] = 0;
526
527 fclose (dir_file);
528 return res;
529 }
530
531 /* Check if dir2 is in dir1. */
is_subdir(const char * dir1,const char * dir2)532 static int is_subdir (const char *dir1, const char *dir2)
533 {
534 return !strncmp(dir1, dir2, strlen(dir1)) ? 1 : 0;
535 }
536
sort_strcmp_func(const void * a,const void * b)537 static int sort_strcmp_func (const void *a, const void *b)
538 {
539 return strcoll (*(char **)a, *(char **)b);
540 }
541
sort_dirs_func(const void * a,const void * b)542 static int sort_dirs_func (const void *a, const void *b)
543 {
544 char *sa = *(char **)a;
545 char *sb = *(char **)b;
546
547 /* '../' is always first */
548 if (!strcmp(sa, "../"))
549 return -1;
550 if (!strcmp(sb, "../"))
551 return 1;
552
553 return strcoll (sa, sb);
554 }
555
get_tags_setting()556 static int get_tags_setting ()
557 {
558 int needed_tags = 0;
559
560 if (options_get_int("ReadTags"))
561 needed_tags |= TAGS_COMMENTS;
562 if (!strcasecmp(options_get_str("ShowTime"), "yes"))
563 needed_tags |= TAGS_TIME;
564
565 return needed_tags;
566 }
567
568 /* For each file in the playlist, send a request for all the given tags if
569 * the file is missing any of those tags. Return the number of requests. */
ask_for_tags(const struct plist * plist,const int tags_sel)570 static int ask_for_tags (const struct plist *plist, const int tags_sel)
571 {
572 int i;
573 int req = 0;
574
575 assert (plist != NULL);
576
577 if (tags_sel != 0) {
578 for (i = 0; i < plist->num; i++) {
579 if (!plist_deleted(plist, i) &&
580 (!plist->items[i].tags ||
581 ~plist->items[i].tags->filled & tags_sel)) {
582 char *file;
583
584 file = plist_get_file (plist, i);
585 req += send_tags_request (file, tags_sel);
586 free (file);
587 }
588 }
589 }
590
591 return req;
592 }
593
interface_message(const char * format,...)594 static void interface_message (const char *format, ...)
595 {
596 va_list va;
597 char *msg;
598
599 va_start (va, format);
600 msg = format_msg_va (format, va);
601 va_end (va);
602
603 iface_message (msg);
604
605 free (msg);
606 }
607
608 /* Update tags (and titles) for the given item on the playlist with new tags. */
update_item_tags(struct plist * plist,const int num,const struct file_tags * tags)609 static void update_item_tags (struct plist *plist, const int num,
610 const struct file_tags *tags)
611 {
612 struct file_tags *old_tags = plist_get_tags (plist, num);
613
614 plist_set_tags (plist, num, tags);
615
616 /* Get the time from the old tags if it's not present in the new tags.
617 * FIXME: There is risk, that the file was modified and the time
618 * from the old tags is not valid. */
619 if (!(tags->filled & TAGS_TIME) && old_tags && old_tags->time != -1)
620 plist_set_item_time (plist, num, old_tags->time);
621
622 if (plist->items[num].title_tags) {
623 free (plist->items[num].title_tags);
624 plist->items[num].title_tags = NULL;
625 }
626
627 make_tags_title (plist, num);
628
629 if (options_get_int ("ReadTags") && !plist->items[num].title_tags) {
630 if (!plist->items[num].title_file)
631 make_file_title (plist, num, options_get_int ("HideFileExtension"));
632 }
633
634 if (old_tags)
635 tags_free (old_tags);
636 }
637
638 /* Truncate string at screen-upsetting whitespace. */
sanitise_string(char * str)639 static void sanitise_string (char *str)
640 {
641 if (!str)
642 return;
643
644 while (*str) {
645 if (*str != ' ' && isspace (*str)) {
646 *str = 0x00;
647 break;
648 }
649 str++;
650 }
651 }
652
653 /* Handle EV_FILE_TAGS. */
ev_file_tags(const struct tag_ev_response * data)654 static void ev_file_tags (const struct tag_ev_response *data)
655 {
656 int n;
657
658 assert (data != NULL);
659 assert (data->file != NULL);
660 assert (data->tags != NULL);
661
662 debug ("Received tags for %s", data->file);
663
664 sanitise_string (data->tags->title);
665 sanitise_string (data->tags->artist);
666 sanitise_string (data->tags->album);
667
668 if ((n = plist_find_fname(dir_plist, data->file)) != -1) {
669 update_item_tags (dir_plist, n, data->tags);
670 iface_update_item (IFACE_MENU_DIR, dir_plist, n);
671 }
672
673 if ((n = plist_find_fname(playlist, data->file)) != -1) {
674 update_item_tags (playlist, n, data->tags);
675 iface_update_item (IFACE_MENU_PLIST, playlist, n);
676 }
677
678 if (curr_file.file && !strcmp(data->file, curr_file.file)) {
679
680 debug ("Tags apply to the currently played file.");
681
682 if (data->tags->time != -1) {
683 curr_file.total_time = data->tags->time;
684 iface_set_total_time (curr_file.total_time);
685 if (file_type (curr_file.file) == F_SOUND) {
686 if (!curr_file.block_file) {
687 curr_file.block_file = xstrdup(curr_file.file);
688 curr_file.block_start = 0;
689 curr_file.block_end = curr_file.total_time;
690 }
691 iface_set_block (curr_file.block_start, curr_file.block_end);
692 }
693 }
694 else
695 debug ("No time information");
696
697 if (data->tags->title) {
698 if (curr_file.title)
699 free (curr_file.title);
700 curr_file.title = build_title (data->tags);
701 iface_set_played_file_title (curr_file.title);
702 }
703
704 if (curr_file.tags)
705 tags_free (curr_file.tags);
706 curr_file.tags = tags_dup (data->tags);
707 }
708 }
709
710 /* Update the current time. */
update_ctime()711 static void update_ctime ()
712 {
713 curr_file.curr_time = get_curr_time ();
714 if (silent_seek_pos == -1)
715 iface_set_curr_time (curr_file.curr_time);
716 }
717
718 /* Use new tags for current file title (for Internet streams). */
update_curr_tags()719 static void update_curr_tags ()
720 {
721 if (curr_file.file && is_url(curr_file.file)) {
722 if (curr_file.tags)
723 tags_free (curr_file.tags);
724 send_int_to_srv (CMD_GET_TAGS);
725 curr_file.tags = get_data_tags ();
726
727 if (curr_file.tags->title) {
728 if (curr_file.title)
729 free (curr_file.title);
730 curr_file.title = build_title (curr_file.tags);
731 iface_set_played_file_title (curr_file.title);
732 }
733 }
734 }
735
736 /* Make sure that the currently played file is visible if it is in one of our
737 * menus. */
follow_curr_file()738 static void follow_curr_file ()
739 {
740 if (curr_file.file && file_type(curr_file.file) == F_SOUND
741 && last_menu_move_time <= time(NULL) - 2) {
742 int server_plist_serial = get_server_plist_serial();
743
744 if (server_plist_serial == plist_get_serial(playlist))
745 iface_make_visible (IFACE_MENU_PLIST, curr_file.file);
746 else if (server_plist_serial == plist_get_serial(dir_plist))
747 iface_make_visible (IFACE_MENU_DIR, curr_file.file);
748 else
749 logit ("Not my playlist.");
750 }
751 }
752
update_curr_file()753 static void update_curr_file ()
754 {
755 char *file;
756
757 file = get_curr_file ();
758
759 if (!file[0] || curr_file.state == STATE_STOP) {
760
761 /* Nothing is played/paused. */
762
763 file_info_cleanup (&curr_file);
764 file_info_reset (&curr_file);
765 iface_set_played_file (NULL);
766 iface_load_lyrics (NULL);
767 free (file);
768 }
769 else if (file[0] &&
770 (!curr_file.file || strcmp(file, curr_file.file))) {
771
772 /* played file has changed */
773
774 file_info_cleanup (&curr_file);
775 if (curr_file.block_file && strcmp(file, curr_file.block_file))
776 file_info_block_reset (&curr_file);
777
778 /* The total time could not get reset. */
779 iface_set_total_time (-1);
780
781 iface_set_played_file (file);
782 send_tags_request (file, TAGS_COMMENTS | TAGS_TIME);
783 curr_file.file = file;
784
785 /* make a title that will be used until we get tags */
786 if (file_type(file) == F_URL || !strchr(file, '/')) {
787 curr_file.title = xstrdup (file);
788 update_curr_tags ();
789 }
790 else
791 {
792 if (options_get_int ("FileNamesIconv"))
793 {
794 curr_file.title = files_iconv_str (
795 strrchr(file, '/') + 1);
796 }
797 else
798 {
799 curr_file.title = xstrdup (
800 strrchr(file, '/') + 1);
801 }
802 }
803
804 iface_set_played_file (file);
805 iface_set_played_file_title (curr_file.title);
806 /* Try to load the lyrics of the new file. */
807 iface_load_lyrics (file);
808 /* Silent seeking makes no sense if the playing file has changed. */
809 silent_seek_pos = -1;
810 iface_set_curr_time (curr_file.curr_time);
811
812 if (options_get_int("FollowPlayedFile"))
813 follow_curr_file ();
814 }
815 else
816 free (file);
817 }
818
update_rate()819 static void update_rate ()
820 {
821 curr_file.rate = get_rate ();
822 iface_set_rate (curr_file.rate);
823 }
824
update_channels()825 static void update_channels ()
826 {
827 curr_file.channels = get_channels () == 2 ? 2 : 1;
828 iface_set_channels (curr_file.channels);
829 }
830
update_bitrate()831 static void update_bitrate ()
832 {
833 curr_file.bitrate = get_bitrate ();
834 iface_set_bitrate (curr_file.bitrate);
835 }
836
837 /* Get and show the server state. */
update_state()838 static void update_state ()
839 {
840 int old_state = curr_file.state;
841
842 /* play | stop | pause */
843 curr_file.state = get_state ();
844 iface_set_state (curr_file.state);
845
846 /* Silent seeking makes no sense if the state has changed. */
847 if (old_state != curr_file.state)
848 silent_seek_pos = -1;
849
850 update_curr_file ();
851
852 update_channels ();
853 update_bitrate ();
854 update_rate ();
855 update_ctime ();
856 }
857
858 /* Handle EV_PLIST_ADD. */
event_plist_add(const struct plist_item * item)859 static void event_plist_add (const struct plist_item *item)
860 {
861 if (plist_find_fname(playlist, item->file) == -1) {
862 int item_num = plist_add_from_item (playlist, item);
863 int needed_tags = 0;
864 int i;
865
866 if (options_get_int("ReadTags")
867 && (!item->tags || !item->tags->title))
868 needed_tags |= TAGS_COMMENTS;
869 if (!strcasecmp(options_get_str("ShowTime"), "yes")
870 && (!item->tags || item->tags->time == -1))
871 needed_tags |= TAGS_TIME;
872
873 if (needed_tags)
874 send_tags_request (item->file, needed_tags);
875
876 if (options_get_int ("ReadTags"))
877 make_tags_title (playlist, item_num);
878 else
879 make_file_title (playlist, item_num,
880 options_get_int ("HideFileExtension"));
881
882 /* Just calling iface_update_queue_positions (queue, playlist,
883 * NULL, NULL) is too slow in cases when we receive a large
884 * number of items from server (e.g., loading playlist w/
885 * SyncPlaylist on). Since we know the filename in question,
886 * we try to find it in queue and eventually update the value.
887 */
888 if ((i = plist_find_fname(queue, item->file)) != -1) {
889 playlist->items[item_num].queue_pos
890 = plist_get_position (queue, i);
891 }
892
893 iface_add_to_plist (playlist, item_num);
894
895 if (waiting_for_plist_load) {
896 if (iface_in_dir_menu())
897 iface_switch_to_plist ();
898 waiting_for_plist_load = 0;
899 }
900 }
901 }
902
903 /* Handle EV_QUEUE_ADD. */
event_queue_add(const struct plist_item * item)904 static void event_queue_add (const struct plist_item *item)
905 {
906 if (plist_find_fname(queue, item->file) == -1) {
907 plist_add_from_item (queue, item);
908 iface_set_files_in_queue (plist_count(queue));
909 iface_update_queue_position_last (queue, playlist, dir_plist);
910 logit ("Adding %s to queue", item->file);
911 }
912 else
913 logit ("Adding file already present in queue");
914 }
915
916 /* Get error message from the server and show it. */
update_error(char * err)917 static void update_error (char *err)
918 {
919 error ("%s", err);
920 }
921
922 /* Send the playlist to the server to be forwarded to another client. */
forward_playlist()923 static void forward_playlist ()
924 {
925 int i;
926
927 debug ("Forwarding the playlist...");
928
929 send_int_to_srv (CMD_SEND_PLIST);
930 send_int_to_srv (plist_get_serial(playlist));
931
932 for (i = 0; i < playlist->num; i++)
933 if (!plist_deleted(playlist, i))
934 send_item_to_srv (&playlist->items[i]);
935
936 send_item_to_srv (NULL);
937 }
938
recv_server_plist(struct plist * plist)939 static int recv_server_plist (struct plist *plist)
940 {
941 int end_of_list = 0;
942 struct plist_item *item;
943
944 logit ("Asking server for the playlist from other client.");
945 send_int_to_srv (CMD_GET_PLIST);
946 logit ("Waiting for response");
947 wait_for_data ();
948
949 if (!get_int_from_srv()) {
950 debug ("There is no playlist");
951 return 0; /* there are no other clients with a playlist */
952 }
953
954 logit ("There is a playlist, getting...");
955 wait_for_data ();
956
957 logit ("Transfer...");
958
959 plist_set_serial (plist, get_int_from_srv());
960
961 do {
962 item = recv_item_from_srv ();
963 if (item->file[0])
964 plist_add_from_item (plist, item);
965 else
966 end_of_list = 1;
967 plist_free_item_fields (item);
968 free (item);
969 } while (!end_of_list);
970
971 return 1;
972 }
973
recv_server_queue(struct plist * queue)974 static void recv_server_queue (struct plist *queue)
975 {
976 int end_of_list = 0;
977 struct plist_item *item;
978
979 logit ("Asking server for the queue.");
980 send_int_to_srv (CMD_GET_QUEUE);
981 logit ("Waiting for response");
982 wait_for_data (); /* There must always be (possibly empty) queue. */
983
984 do {
985 item = recv_item_from_srv ();
986 if (item->file[0])
987 plist_add_from_item (queue, item);
988 else
989 end_of_list = 1;
990 plist_free_item_fields (item);
991 free (item);
992 } while (!end_of_list);
993 }
994
995 /* Clear the playlist locally. */
clear_playlist()996 static void clear_playlist ()
997 {
998 if (iface_in_plist_menu())
999 iface_switch_to_dir ();
1000 plist_clear (playlist);
1001 iface_clear_plist ();
1002
1003 if (!waiting_for_plist_load)
1004 interface_message ("The playlist was cleared.");
1005 iface_set_status ("");
1006 }
1007
clear_queue()1008 static void clear_queue ()
1009 {
1010 iface_clear_queue_positions (queue, playlist, dir_plist);
1011
1012 plist_clear (queue);
1013 iface_set_files_in_queue (0);
1014
1015 interface_message ("The queue was cleared.");
1016 }
1017
1018 /* Handle EV_PLIST_DEL. */
event_plist_del(char * file)1019 static void event_plist_del (char *file)
1020 {
1021 int item = plist_find_fname (playlist, file);
1022
1023 if (item != -1) {
1024 char *file;
1025 int have_all_times;
1026 int playlist_total_time;
1027
1028 file = plist_get_file (playlist, item);
1029 plist_delete (playlist, item);
1030
1031 iface_del_plist_item (file);
1032 playlist_total_time = plist_total_time (playlist,
1033 &have_all_times);
1034 iface_plist_set_total_time (playlist_total_time,
1035 have_all_times);
1036 free (file);
1037
1038 if (plist_count(playlist) == 0)
1039 clear_playlist ();
1040 }
1041 else
1042 logit ("Server requested deleting an item not present on the"
1043 " playlist.");
1044 }
1045
1046 /* Handle EV_QUEUE_DEL. */
event_queue_del(char * file)1047 static void event_queue_del (char *file)
1048 {
1049 int item = plist_find_fname (queue, file);
1050
1051 if (item != -1) {
1052 plist_delete (queue, item);
1053
1054 /* Free the deleted items occasionally.
1055 * QUEUE_CLEAR_THRESH is chosen to be two times
1056 * the initial size of the playlist. */
1057 if (plist_count(queue) == 0
1058 && queue->num >= QUEUE_CLEAR_THRESH)
1059 plist_clear (queue);
1060
1061 iface_set_files_in_queue (plist_count(queue));
1062 iface_update_queue_positions (queue, playlist, dir_plist, file);
1063 logit ("Deleting %s from queue", file);
1064 }
1065 else
1066 logit ("Deleting an item not present in the queue");
1067
1068 }
1069
1070 /* Swap 2 file on the playlist. */
swap_playlist_items(const char * file1,const char * file2)1071 static void swap_playlist_items (const char *file1, const char *file2)
1072 {
1073 assert (file1 != NULL);
1074 assert (file2 != NULL);
1075
1076 plist_swap_files (playlist, file1, file2);
1077 iface_swap_plist_items (file1, file2);
1078 }
1079
1080 /* Handle EV_PLIST_MOVE. */
event_plist_move(const struct move_ev_data * d)1081 static void event_plist_move (const struct move_ev_data *d)
1082 {
1083 assert (d != NULL);
1084 assert (d->from != NULL);
1085 assert (d->to != NULL);
1086
1087 swap_playlist_items (d->from, d->to);
1088 }
1089
1090 /* Handle EV_QUEUE_MOVE. */
event_queue_move(const struct move_ev_data * d)1091 static void event_queue_move (const struct move_ev_data *d)
1092 {
1093 assert (d != NULL);
1094 assert (d->from != NULL);
1095 assert (d->to != NULL);
1096
1097 plist_swap_files (queue, d->from, d->to);
1098 }
1099
1100 /* Handle server event. */
server_event(const int event,void * data)1101 static void server_event (const int event, void *data)
1102 {
1103 logit ("EVENT: 0x%02x", event);
1104
1105 switch (event) {
1106 case EV_BUSY:
1107 interface_fatal ("The server is busy; "
1108 "another client is connected!");
1109 break;
1110 case EV_CTIME:
1111 update_ctime ();
1112 break;
1113 case EV_STATE:
1114 update_state ();
1115 break;
1116 case EV_EXIT:
1117 interface_fatal ("The server exited!");
1118 break;
1119 case EV_BITRATE:
1120 update_bitrate ();
1121 break;
1122 case EV_RATE:
1123 update_rate ();
1124 break;
1125 case EV_CHANNELS:
1126 update_channels ();
1127 break;
1128 case EV_SRV_ERROR:
1129 update_error ((char *)data);
1130 break;
1131 case EV_OPTIONS:
1132 get_server_options ();
1133 break;
1134 case EV_SEND_PLIST:
1135 forward_playlist ();
1136 break;
1137 case EV_PLIST_ADD:
1138 if (options_get_int("SyncPlaylist"))
1139 event_plist_add ((struct plist_item *)data);
1140 break;
1141 case EV_PLIST_CLEAR:
1142 if (options_get_int("SyncPlaylist"))
1143 clear_playlist ();
1144 break;
1145 case EV_PLIST_DEL:
1146 if (options_get_int("SyncPlaylist"))
1147 event_plist_del ((char *)data);
1148 break;
1149 case EV_PLIST_MOVE:
1150 if (options_get_int("SyncPlaylist"))
1151 event_plist_move ((struct move_ev_data *)data);
1152 break;
1153 case EV_TAGS:
1154 update_curr_tags ();
1155 break;
1156 case EV_STATUS_MSG:
1157 iface_set_status ((char *)data);
1158 break;
1159 case EV_MIXER_CHANGE:
1160 update_mixer_name ();
1161 break;
1162 case EV_FILE_TAGS:
1163 ev_file_tags ((struct tag_ev_response *)data);
1164 break;
1165 case EV_AVG_BITRATE:
1166 curr_file.avg_bitrate = get_avg_bitrate ();
1167 break;
1168 case EV_QUEUE_ADD:
1169 event_queue_add ((struct plist_item *)data);
1170 break;
1171 case EV_QUEUE_DEL:
1172 event_queue_del ((char *)data);
1173 break;
1174 case EV_QUEUE_CLEAR:
1175 clear_queue ();
1176 break;
1177 case EV_QUEUE_MOVE:
1178 event_queue_move ((struct move_ev_data *)data);
1179 break;
1180 case EV_AUDIO_START:
1181 break;
1182 case EV_AUDIO_STOP:
1183 break;
1184 default:
1185 interface_fatal ("Unknown event: 0x%02x!", event);
1186 }
1187
1188 free_event_data (event, data);
1189 }
1190
1191 /* Send requests for the given tags for every file on the playlist and wait
1192 * for all responses. If no_iface has non-zero value, it will not access the
1193 * interface. */
fill_tags(struct plist * plist,const int tags_sel,const int no_iface)1194 static void fill_tags (struct plist *plist, const int tags_sel,
1195 const int no_iface)
1196 {
1197 int files;
1198
1199 assert (plist != NULL);
1200 assert (tags_sel != 0);
1201
1202 iface_set_status ("Reading tags...");
1203 files = ask_for_tags (plist, tags_sel);
1204
1205 /* Process events until we have all tags. */
1206 while (files && !user_wants_interrupt()) {
1207 int type;
1208 void *data;
1209
1210 /* Event queue is not initialized if there is no interface. */
1211 if (!no_iface && !event_queue_empty (&events)) {
1212 struct event e = *event_get_first (&events);
1213
1214 type = e.type;
1215 data = e.data;
1216
1217 event_pop (&events);
1218
1219 }
1220 else {
1221 type = get_int_from_srv ();
1222 data = get_event_data (type);
1223 }
1224
1225 if (type == EV_FILE_TAGS) {
1226 struct tag_ev_response *ev
1227 = (struct tag_ev_response *)data;
1228 int n;
1229
1230 if ((n = plist_find_fname(plist, ev->file)) != -1) {
1231 if ((ev->tags->filled & tags_sel))
1232 files--;
1233 update_item_tags (plist, n, ev->tags);
1234 }
1235 }
1236 else if (no_iface)
1237 abort (); /* can't handle other events without the interface */
1238
1239 if (!no_iface)
1240 server_event (type, data);
1241 else
1242 free_event_data (type, data);
1243 }
1244
1245 iface_set_status ("");
1246 }
1247
1248 /* Load the directory content into dir_plist and switch the menu to it.
1249 * If dir is NULL, go to the cwd. If reload is not zero, we are reloading
1250 * the current directory, so use iface_update_dir_content().
1251 * Return 1 on success, 0 on error. */
go_to_dir(const char * dir,const int reload)1252 static int go_to_dir (const char *dir, const int reload)
1253 {
1254 struct plist *old_dir_plist;
1255 char last_dir[PATH_MAX];
1256 const char *new_dir = dir ? dir : cwd;
1257 int going_up = 0;
1258 lists_t_strs *dirs, *playlists;
1259
1260 iface_set_status ("Reading directory...");
1261
1262 if (dir && is_subdir(dir, cwd)) {
1263 strcpy (last_dir, strrchr(cwd, '/') + 1);
1264 strcat (last_dir, "/");
1265 going_up = 1;
1266 }
1267
1268 old_dir_plist = dir_plist;
1269 dir_plist = (struct plist *)xmalloc (sizeof(struct plist));
1270 plist_init (dir_plist);
1271 dirs = lists_strs_new (FILES_LIST_INIT_SIZE);
1272 playlists = lists_strs_new (FILES_LIST_INIT_SIZE);
1273
1274 if (!read_directory(new_dir, dirs, playlists, dir_plist)) {
1275 iface_set_status ("");
1276 plist_free (dir_plist);
1277 lists_strs_free (dirs);
1278 lists_strs_free (playlists);
1279 free (dir_plist);
1280 dir_plist = old_dir_plist;
1281 return 0;
1282 }
1283
1284 /* TODO: use CMD_ABORT_TAGS_REQUESTS (what if we requested tags for the
1285 playlist?) */
1286
1287 plist_free (old_dir_plist);
1288 free (old_dir_plist);
1289
1290 if (dir) /* if dir is NULL, we went to cwd */
1291 strcpy (cwd, dir);
1292
1293 switch_titles_file (dir_plist);
1294
1295 plist_sort_fname (dir_plist);
1296 lists_strs_sort (dirs, sort_dirs_func);
1297 lists_strs_sort (playlists, sort_strcmp_func);
1298
1299 ask_for_tags (dir_plist, get_tags_setting());
1300
1301 if (reload)
1302 iface_update_dir_content (IFACE_MENU_DIR, dir_plist, dirs, playlists);
1303 else
1304 iface_set_dir_content (IFACE_MENU_DIR, dir_plist, dirs, playlists);
1305 lists_strs_free (dirs);
1306 lists_strs_free (playlists);
1307 if (going_up)
1308 iface_set_curr_item_title (last_dir);
1309
1310 iface_set_title (IFACE_MENU_DIR, cwd);
1311 iface_update_queue_positions (queue, NULL, dir_plist, NULL);
1312
1313 if (iface_in_plist_menu())
1314 iface_switch_to_dir ();
1315
1316 return 1;
1317 }
1318
1319 /* Make sure that the server's playlist has different serial from ours. */
change_srv_plist_serial()1320 static void change_srv_plist_serial ()
1321 {
1322 int serial;
1323
1324 do {
1325 send_int_to_srv (CMD_GET_SERIAL);
1326 serial = get_data_int ();
1327 } while (serial == plist_get_serial(playlist) ||
1328 serial == plist_get_serial(dir_plist));
1329
1330 send_int_to_srv (CMD_PLIST_SET_SERIAL);
1331 send_int_to_srv (serial);
1332 }
1333
1334 static void enter_first_dir ();
1335
1336 /* Switch between the directory view and the playlist. */
toggle_menu()1337 static void toggle_menu ()
1338 {
1339 int num;
1340
1341 if (iface_in_plist_menu()) {
1342 if (!cwd[0])
1343 /* we were at the playlist from the startup */
1344 enter_first_dir ();
1345 else
1346 iface_switch_to_dir ();
1347 }
1348 else if ((num = plist_count(playlist)))
1349 iface_switch_to_plist ();
1350 else
1351 error ("The playlist is empty.");
1352 }
1353
1354 /* Load the playlist file and switch the menu to it. Return 1 on success. */
go_to_playlist(const char * file,const int load_serial,bool default_playlist)1355 static int go_to_playlist (const char *file, const int load_serial,
1356 bool default_playlist)
1357 {
1358 if (plist_count(playlist)) {
1359 error ("Please clear the playlist, because "
1360 "I'm not sure you want to do this.");
1361 return 0;
1362 }
1363
1364 plist_clear (playlist);
1365
1366 iface_set_status ("Loading playlist...");
1367 if (plist_load(playlist, file, cwd, load_serial)) {
1368
1369 if (options_get_int("SyncPlaylist")) {
1370 send_int_to_srv (CMD_LOCK);
1371 if (!load_serial)
1372 change_srv_plist_serial ();
1373 send_int_to_srv (CMD_CLI_PLIST_CLEAR);
1374 iface_set_status ("Notifying clients...");
1375 send_items_to_clients (playlist);
1376 iface_set_status ("");
1377 waiting_for_plist_load = 1;
1378 send_int_to_srv (CMD_UNLOCK);
1379
1380 /* We'll use the playlist received from the
1381 * server to be synchronized with other clients.
1382 */
1383 plist_clear (playlist);
1384 }
1385 else {
1386 if (!default_playlist)
1387 toggle_menu ();
1388 iface_set_dir_content (IFACE_MENU_PLIST, playlist, NULL, NULL);
1389 iface_update_queue_positions (queue, playlist, NULL, NULL);
1390 }
1391
1392 interface_message ("Playlist loaded.");
1393 }
1394 else {
1395 interface_message ("The playlist is empty");
1396 iface_set_status ("");
1397 return 0;
1398 }
1399
1400 return 1;
1401 }
1402
1403 /* Enter to the initial directory or toggle to the initial playlist (only
1404 * if the function has not been called yet). */
enter_first_dir()1405 static void enter_first_dir ()
1406 {
1407 static int first_run = 1;
1408
1409 if (options_get_int("StartInMusicDir")) {
1410 char *music_dir;
1411
1412 if ((music_dir = options_get_str("MusicDir"))) {
1413 set_cwd (music_dir);
1414 if (first_run && file_type(music_dir) == F_PLAYLIST
1415 && plist_count(playlist) == 0
1416 && go_to_playlist(music_dir, 0, false)) {
1417 cwd[0] = 0;
1418 first_run = 0;
1419 }
1420 else if (file_type(cwd) == F_DIR
1421 && go_to_dir(NULL, 0)) {
1422 first_run = 0;
1423 return;
1424 }
1425 }
1426 else
1427 error ("MusicDir is not set");
1428 }
1429
1430 if (!(read_last_dir() && go_to_dir(NULL, 0))) {
1431 set_start_dir ();
1432 if (!go_to_dir(NULL, 0))
1433 interface_fatal ("Can't enter any directory!");
1434 }
1435
1436 first_run = 0;
1437 }
1438
1439 /* Request the playlist from the server (given by another client). Make
1440 * the titles. Return 0 if such a list doesn't exist. */
get_server_playlist(struct plist * plist)1441 static int get_server_playlist (struct plist *plist)
1442 {
1443 iface_set_status ("Getting the playlist...");
1444 debug ("Getting the playlist...");
1445 if (recv_server_plist(plist)) {
1446 ask_for_tags (plist, get_tags_setting());
1447 if (options_get_bool ("ReadTags"))
1448 switch_titles_tags (plist);
1449 else
1450 switch_titles_file (plist);
1451 iface_set_status ("");
1452 return 1;
1453 }
1454
1455 iface_set_status ("");
1456
1457 return 0;
1458 }
1459
1460 /* Get the playlist from another client and use it as our playlist.
1461 * Return 0 if there is no client with a playlist. */
use_server_playlist()1462 static int use_server_playlist ()
1463 {
1464 if (get_server_playlist(playlist)) {
1465 iface_set_dir_content (IFACE_MENU_PLIST, playlist, NULL, NULL);
1466 iface_update_queue_positions (queue, playlist, NULL, NULL);
1467 return 1;
1468 }
1469
1470 return 0;
1471 }
1472
use_server_queue()1473 static void use_server_queue ()
1474 {
1475 iface_set_status ("Getting the queue...");
1476 debug ("Getting the queue...");
1477
1478 recv_server_queue(queue);
1479 iface_set_files_in_queue (plist_count(queue));
1480 iface_update_queue_positions (queue, playlist, dir_plist, NULL);
1481 iface_set_status ("");
1482 }
1483
1484 /* Process a single directory argument. */
process_dir_arg(const char * dir)1485 static void process_dir_arg (const char *dir)
1486 {
1487 set_cwd (dir);
1488 if (!go_to_dir (NULL, 0))
1489 enter_first_dir ();
1490 }
1491
1492 /* Process a single playlist argument. */
process_plist_arg(const char * file)1493 static void process_plist_arg (const char *file)
1494 {
1495 char path[PATH_MAX + 1]; /* the playlist's directory */
1496 char *slash;
1497
1498 if (file[0] == '/')
1499 strcpy (path, "/");
1500 else if (!getcwd (path, sizeof (path)))
1501 interface_fatal ("Can't get CWD: %s", strerror (errno));
1502
1503 resolve_path (path, sizeof (path), file);
1504 slash = strrchr (path, '/');
1505 assert (slash != NULL);
1506 *slash = 0;
1507
1508 iface_set_status ("Loading playlist...");
1509 plist_load (playlist, file, path, 0);
1510 iface_set_status ("");
1511 }
1512
1513 /* Process a list of arguments. */
process_multiple_args(lists_t_strs * args)1514 static void process_multiple_args (lists_t_strs *args)
1515 {
1516 int size, ix;
1517 const char *arg;
1518 char this_cwd[PATH_MAX];
1519
1520 if (!getcwd (this_cwd, sizeof (cwd)))
1521 interface_fatal ("Can't get CWD: %s", strerror (errno));
1522
1523 size = lists_strs_size (args);
1524
1525 for (ix = 0; ix < size; ix += 1) {
1526 int dir;
1527 char path[2 * PATH_MAX];
1528
1529 arg = lists_strs_at (args, ix);
1530 dir = is_dir (arg);
1531
1532 if (is_url (arg)) {
1533 strncpy (path, arg, sizeof (path));
1534 path[sizeof(path) - 1] = 0;
1535 }
1536 else {
1537 if (arg[0] == '/')
1538 strcpy (path, "/");
1539 else
1540 strcpy (path, this_cwd);
1541 resolve_path (path, sizeof (path), arg);
1542 }
1543
1544 if (dir == 1)
1545 read_directory_recurr (path, playlist);
1546 else if (!dir && (is_sound_file (path) || is_url (path))) {
1547 if (plist_find_fname (playlist, path) == -1)
1548 plist_add (playlist, path);
1549 }
1550 else if (is_plist_file (path)) {
1551 char *plist_dir, *slash;
1552
1553 /* Here we've chosen to resolve the playlist's relative paths
1554 * with respect to the directory of the playlist (or of the
1555 * symlink being used to reference it). If some other base is
1556 * desired, then we probably need to introduce a new option. */
1557
1558 plist_dir = xstrdup (path);
1559 slash = strrchr (plist_dir, '/');
1560 assert (slash != NULL);
1561 *slash = 0;
1562
1563 plist_load (playlist, path, plist_dir, 0);
1564
1565 free (plist_dir);
1566 }
1567 }
1568 }
1569
1570 /* Process file names passed as arguments. */
process_args(lists_t_strs * args)1571 static void process_args (lists_t_strs *args)
1572 {
1573 int size;
1574 const char *arg;
1575
1576 size = lists_strs_size (args);
1577 arg = lists_strs_at (args, 0);
1578
1579 if (size == 1 && is_dir (arg) == 1) {
1580 process_dir_arg (arg);
1581 return;
1582 }
1583
1584 if (size == 1 && is_plist_file (arg))
1585 process_plist_arg (arg);
1586 else
1587 process_multiple_args (args);
1588
1589 if (plist_count (playlist) && !options_get_int ("SyncPlaylist")) {
1590 switch_titles_file (playlist);
1591 ask_for_tags (playlist, get_tags_setting ());
1592 iface_set_dir_content (IFACE_MENU_PLIST, playlist, NULL, NULL);
1593 iface_update_queue_positions (queue, playlist, NULL, NULL);
1594 iface_switch_to_plist ();
1595 }
1596 else
1597 enter_first_dir ();
1598 }
1599
1600 /* Load the playlist from .moc directory. */
load_playlist()1601 static void load_playlist ()
1602 {
1603 char *plist_file = create_file_name (PLAYLIST_FILE);
1604
1605 if (file_type(plist_file) == F_PLAYLIST) {
1606 go_to_playlist (plist_file, 1, true);
1607
1608 /* We don't want to switch to the playlist after loading. */
1609 waiting_for_plist_load = 0;
1610 }
1611 }
1612
1613 #ifdef SIGWINCH
1614 /* Handle resizing xterm. */
do_resize()1615 static void do_resize ()
1616 {
1617 iface_resize ();
1618 logit ("resize");
1619 want_resize = 0;
1620 }
1621 #endif
1622
1623 /* Strip the last directory from the path. Returned memory is mallod()ed. */
dir_up(const char * path)1624 static char *dir_up (const char *path)
1625 {
1626 char *slash;
1627 char *dir;
1628
1629 assert (path != NULL);
1630
1631 dir = xstrdup (path);
1632 slash = strrchr (dir, '/');
1633 assert (slash != NULL);
1634 if (slash == dir)
1635 *(slash + 1) = 0;
1636 else
1637 *slash = 0;
1638
1639 return dir;
1640 }
1641
go_dir_up()1642 static void go_dir_up ()
1643 {
1644 char *dir;
1645
1646 dir = dir_up (cwd);
1647 go_to_dir (dir, 0);
1648 free (dir);
1649 }
1650
1651 /* Return a generated playlist serial from the server and make sure
1652 * it's not the same as our playlist's serial. */
get_safe_serial()1653 static int get_safe_serial ()
1654 {
1655 int serial;
1656
1657 do {
1658 send_int_to_srv (CMD_GET_SERIAL);
1659 serial = get_data_int ();
1660 } while (playlist &&
1661 serial == plist_get_serial(playlist)); /* check only the
1662 playlist, because dir_plist has serial
1663 -1 */
1664
1665 return serial;
1666 }
1667
1668 /* Send the playlist to the server. If clear != 0, clear the server's playlist
1669 * before sending. */
send_playlist(struct plist * plist,const int clear)1670 static void send_playlist (struct plist *plist, const int clear)
1671 {
1672 int i;
1673
1674 if (clear)
1675 send_int_to_srv (CMD_LIST_CLEAR);
1676
1677 for (i = 0; i < plist->num; i++) {
1678 if (!plist_deleted(plist, i)) {
1679 send_int_to_srv (CMD_LIST_ADD);
1680 send_str_to_srv (plist->items[i].file);
1681 }
1682 }
1683 }
1684
1685 /* Send the playlist to the server if necessary and request playing this
1686 * item. */
play_it(const char * file)1687 static void play_it (const char *file)
1688 {
1689 struct plist *curr_plist;
1690
1691 assert (file != NULL);
1692
1693 if (iface_in_dir_menu())
1694 curr_plist = dir_plist;
1695 else
1696 curr_plist = playlist;
1697
1698 send_int_to_srv (CMD_LOCK);
1699
1700 if (plist_get_serial(curr_plist) == -1 || get_server_plist_serial()
1701 != plist_get_serial(curr_plist)) {
1702 int serial;
1703
1704 logit ("The server has different playlist");
1705
1706 serial = get_safe_serial();
1707 plist_set_serial (curr_plist, serial);
1708 send_int_to_srv (CMD_PLIST_SET_SERIAL);
1709 send_int_to_srv (serial);
1710
1711 send_playlist (curr_plist, 1);
1712 }
1713 else
1714 logit ("The server already has my playlist");
1715 send_int_to_srv (CMD_PLAY);
1716 send_str_to_srv (file);
1717
1718 send_int_to_srv (CMD_UNLOCK);
1719 }
1720
1721 /* Action when the user selected a file. */
go_file()1722 static void go_file ()
1723 {
1724 enum file_type type = iface_curritem_get_type ();
1725 char *file = iface_get_curr_file ();
1726
1727 if (!file)
1728 return;
1729
1730 if (type == F_SOUND || type == F_URL)
1731 play_it (file);
1732 else if (type == F_DIR && iface_in_dir_menu()) {
1733 if (!strcmp(file, ".."))
1734 go_dir_up ();
1735 else
1736 go_to_dir (file, 0);
1737 }
1738 else if (type == F_PLAYLIST)
1739 go_to_playlist (file, 0, false);
1740
1741 free (file);
1742 }
1743
1744 /* pause/unpause */
switch_pause()1745 static void switch_pause ()
1746 {
1747 switch (curr_file.state) {
1748 case STATE_PLAY:
1749 send_int_to_srv (CMD_PAUSE);
1750 break;
1751 case STATE_PAUSE:
1752 send_int_to_srv (CMD_UNPAUSE);
1753 break;
1754 default:
1755 logit ("User pressed pause when not playing.");
1756 }
1757 }
1758
set_mixer(int val)1759 static void set_mixer (int val)
1760 {
1761 val = CLAMP(0, val, 100);
1762 send_int_to_srv (CMD_SET_MIXER);
1763 send_int_to_srv (val);
1764 }
1765
adjust_mixer(const int diff)1766 static void adjust_mixer (const int diff)
1767 {
1768 set_mixer (get_mixer_value() + diff);
1769 }
1770
1771 /* Recursively add the content of a directory to the playlist. */
add_dir_plist()1772 static void add_dir_plist ()
1773 {
1774 struct plist plist;
1775 char *file;
1776 enum file_type type;
1777
1778 if (iface_in_plist_menu()) {
1779 error ("Can't add to the playlist a file from the "
1780 "playlist.");
1781 return;
1782 }
1783
1784 file = iface_get_curr_file ();
1785
1786 if (!file)
1787 return;
1788
1789 type = iface_curritem_get_type ();
1790 if (type != F_DIR && type != F_PLAYLIST) {
1791 error ("This is not a directory or a playlist.");
1792 free (file);
1793 return;
1794 }
1795
1796 if (!strcmp(file, "..")) {
1797 error ("Can't add '..'.");
1798 free (file);
1799 return;
1800 }
1801
1802 iface_set_status ("Reading directories...");
1803 plist_init (&plist);
1804
1805 if (type == F_DIR) {
1806 read_directory_recurr (file, &plist);
1807 plist_sort_fname (&plist);
1808 }
1809 else
1810 plist_load (&plist, file, cwd, 0);
1811
1812 send_int_to_srv (CMD_LOCK);
1813
1814 plist_remove_common_items (&plist, playlist);
1815
1816 /* Add the new files to the server's playlist if the server has our
1817 * playlist. */
1818 if (get_server_plist_serial() == plist_get_serial(playlist))
1819 send_playlist (&plist, 0);
1820
1821 if (options_get_int("SyncPlaylist")) {
1822 iface_set_status ("Notifying clients...");
1823 send_items_to_clients (&plist);
1824 iface_set_status ("");
1825 }
1826 else {
1827 int i;
1828
1829 switch_titles_file (&plist);
1830 ask_for_tags (&plist, get_tags_setting());
1831
1832 for (i = 0; i < plist.num; i++)
1833 if (!plist_deleted(&plist, i))
1834 iface_add_to_plist (&plist, i);
1835 plist_cat (playlist, &plist);
1836 }
1837
1838 send_int_to_srv (CMD_UNLOCK);
1839
1840 plist_free (&plist);
1841 free (file);
1842 }
1843
1844 /* To avoid lots of locks and unlocks, this assumes a lock is sent before
1845 * the first call and an unlock after the last.
1846 *
1847 * It's also assumed to be in the menu.
1848 */
remove_file_from_playlist(const char * file)1849 static void remove_file_from_playlist (const char *file)
1850 {
1851 assert (file != NULL);
1852 assert (plist_count(playlist) > 0);
1853
1854 if (options_get_int("SyncPlaylist")) {
1855 send_int_to_srv (CMD_CLI_PLIST_DEL);
1856 send_str_to_srv (file);
1857 }
1858 else {
1859 int n = plist_find_fname (playlist, file);
1860
1861 assert (n != -1);
1862
1863 plist_delete (playlist, n);
1864 iface_del_plist_item (file);
1865
1866 if (plist_count(playlist) == 0)
1867 clear_playlist ();
1868 }
1869
1870 /* Delete this item from the server's playlist if it has our
1871 * playlist. */
1872 if (get_server_plist_serial() == plist_get_serial(playlist)) {
1873 send_int_to_srv (CMD_DELETE);
1874 send_str_to_srv (file);
1875 }
1876 }
1877
1878 /* Remove all dead entries (point to non-existent or unreadable). */
remove_dead_entries_plist()1879 static void remove_dead_entries_plist ()
1880 {
1881 const char *file = NULL;
1882 int i;
1883
1884 if (! iface_in_plist_menu()) {
1885 error ("Can't prune when not in the playlist.");
1886 return;
1887 }
1888
1889 send_int_to_srv (CMD_LOCK);
1890 for (i = 0, file = plist_get_next_dead_entry(playlist, &i);
1891 file != NULL;
1892 file = plist_get_next_dead_entry(playlist, &i)) {
1893 remove_file_from_playlist (file);
1894 }
1895 send_int_to_srv (CMD_UNLOCK);
1896 }
1897
1898 /* Add the currently selected file to the playlist. */
add_file_plist()1899 static void add_file_plist ()
1900 {
1901 char *file;
1902
1903 if (iface_in_plist_menu()) {
1904 error ("Can't add to the playlist a file from the playlist.");
1905 return;
1906 }
1907
1908 if (iface_curritem_get_type() == F_DIR) {
1909 add_dir_plist();
1910 return;
1911 }
1912
1913 file = iface_get_curr_file ();
1914
1915 if (!file)
1916 return;
1917
1918 if (iface_curritem_get_type() != F_SOUND) {
1919 error ("You can only add a file using this command.");
1920 free (file);
1921 return;
1922 }
1923
1924 if (plist_find_fname(playlist, file) == -1) {
1925 struct plist_item *item = &dir_plist->items[
1926 plist_find_fname(dir_plist, file)];
1927
1928 send_int_to_srv (CMD_LOCK);
1929
1930 if (options_get_int("SyncPlaylist")) {
1931 send_int_to_srv (CMD_CLI_PLIST_ADD);
1932 send_item_to_srv (item);
1933 }
1934 else {
1935 int added;
1936
1937 added = plist_add_from_item (playlist, item);
1938 iface_add_to_plist (playlist, added);
1939 }
1940
1941 /* Add to the server's playlist if the server has our
1942 * playlist. */
1943 if (get_server_plist_serial() == plist_get_serial(playlist)) {
1944 send_int_to_srv (CMD_LIST_ADD);
1945 send_str_to_srv (file);
1946 }
1947 send_int_to_srv (CMD_UNLOCK);
1948 }
1949 else
1950 error ("The file is already on the playlist.");
1951
1952 iface_menu_key (KEY_CMD_MENU_DOWN);
1953
1954 free (file);
1955 }
1956
queue_toggle_file()1957 static void queue_toggle_file ()
1958 {
1959 char *file;
1960
1961 file = iface_get_curr_file ();
1962
1963 if (!file)
1964 return;
1965
1966 if (iface_curritem_get_type() != F_SOUND
1967 && iface_curritem_get_type() != F_URL) {
1968 error ("You can only add a file or URL using this command.");
1969 free (file);
1970 return;
1971 }
1972
1973 /* Check if the file is already in the queue; if it isn't, add it,
1974 * otherwise, remove it. */
1975
1976 if (plist_find_fname(queue, file) == -1) {
1977 /* Add item to the server's queue. */
1978 send_int_to_srv (CMD_QUEUE_ADD);
1979 send_str_to_srv (file);
1980
1981 logit ("Added to queue: %s", file);
1982 }
1983 else {
1984 /* Delete this item from the server's queue. */
1985 send_int_to_srv (CMD_QUEUE_DEL);
1986 send_str_to_srv (file);
1987
1988 logit ("Removed from queue: %s", file);
1989 }
1990
1991 iface_menu_key (KEY_CMD_MENU_DOWN);
1992
1993 free (file);
1994 }
1995
toggle_option(const char * name)1996 static void toggle_option (const char *name)
1997 {
1998 send_int_to_srv (CMD_SET_OPTION);
1999 send_str_to_srv (name);
2000 send_int_to_srv (!options_get_int(name));
2001 sync_int_option (name);
2002 }
2003
toggle_show_time()2004 static void toggle_show_time ()
2005 {
2006 if (!strcasecmp (options_get_str ("ShowTime"), "yes")) {
2007 options_set_str ("ShowTime", "IfAvailable");
2008 iface_set_status ("ShowTime: IfAvailable");
2009 }
2010 else if (!strcasecmp (options_get_str ("ShowTime"), "no")) {
2011 options_set_str ("ShowTime", "yes");
2012 iface_update_show_time ();
2013 ask_for_tags (dir_plist, TAGS_TIME);
2014 ask_for_tags (playlist, TAGS_TIME);
2015 iface_set_status ("ShowTime: yes");
2016
2017 }
2018 else { /* IfAvailable */
2019 options_set_str ("ShowTime", "no");
2020 iface_update_show_time ();
2021 iface_set_status ("ShowTime: no");
2022 }
2023 }
2024
toggle_show_format()2025 static void toggle_show_format ()
2026 {
2027 int show_format = !options_get_int("ShowFormat");
2028
2029 options_set_int ("ShowFormat", show_format);
2030 if (show_format)
2031 iface_set_status ("ShowFormat: yes");
2032 else
2033 iface_set_status ("ShowFormat: no");
2034
2035 iface_update_show_format ();
2036 }
2037
2038 /* Reread the directory. */
reread_dir()2039 static void reread_dir ()
2040 {
2041 go_to_dir (NULL, 1);
2042 }
2043
2044 /* Clear the playlist on user request. */
cmd_clear_playlist()2045 static void cmd_clear_playlist ()
2046 {
2047 if (options_get_int("SyncPlaylist")) {
2048 send_int_to_srv (CMD_LOCK);
2049 send_int_to_srv (CMD_CLI_PLIST_CLEAR);
2050 change_srv_plist_serial ();
2051 send_int_to_srv (CMD_UNLOCK);
2052 }
2053 else
2054 clear_playlist ();
2055 }
2056
cmd_clear_queue()2057 static void cmd_clear_queue ()
2058 {
2059 send_int_to_srv (CMD_QUEUE_CLEAR);
2060 }
2061
go_to_music_dir()2062 static void go_to_music_dir ()
2063 {
2064 const char *musicdir_optn;
2065 char music_dir[PATH_MAX] = "/";
2066
2067 musicdir_optn = options_get_str ("MusicDir");
2068
2069 if (!musicdir_optn) {
2070 error ("MusicDir not defined");
2071 return;
2072 }
2073
2074 resolve_path (music_dir, sizeof(music_dir), musicdir_optn);
2075
2076 switch (file_type (music_dir)) {
2077 case F_DIR:
2078 go_to_dir (music_dir, 0);
2079 break;
2080 case F_PLAYLIST:
2081 go_to_playlist (music_dir, 0, false);
2082 break;
2083 default:
2084 error ("MusicDir is neither a directory nor a playlist!");
2085 }
2086 }
2087
2088 /* Make a directory from the string resolving ~, './' and '..'.
2089 * Return the directory, the memory is malloc()ed.
2090 * Return NULL on error. */
make_dir(const char * str)2091 static char *make_dir (const char *str)
2092 {
2093 char *dir;
2094 int add_slash = 0;
2095
2096 dir = (char *)xmalloc (sizeof(char) * (PATH_MAX + 1));
2097
2098 dir[PATH_MAX] = 0;
2099
2100 /* If the string ends with a slash and is not just '/', add this
2101 * slash. */
2102 if (strlen(str) > 1 && str[strlen(str)-1] == '/')
2103 add_slash = 1;
2104
2105 if (str[0] == '~') {
2106 strncpy (dir, get_home (), PATH_MAX);
2107
2108 if (dir[PATH_MAX]) {
2109 logit ("Path too long!");
2110 return NULL;
2111 }
2112
2113 if (!strcmp(str, "~"))
2114 add_slash = 1;
2115
2116 str++;
2117 }
2118 else if (str[0] != '/')
2119 strcpy (dir, cwd);
2120 else
2121 strcpy (dir, "/");
2122
2123 resolve_path (dir, PATH_MAX, str);
2124
2125 if (add_slash && strlen(dir) < PATH_MAX)
2126 strcat (dir, "/");
2127
2128 return dir;
2129 }
2130
entry_key_go_dir(const struct iface_key * k)2131 static void entry_key_go_dir (const struct iface_key *k)
2132 {
2133 if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\t') {
2134 char *dir;
2135 char *complete_dir;
2136 char buf[PATH_MAX+1];
2137 char *entry_text;
2138
2139 entry_text = iface_entry_get_text ();
2140 if (!(dir = make_dir(entry_text))) {
2141 free (entry_text);
2142 return;
2143 }
2144 free (entry_text);
2145
2146 complete_dir = find_match_dir (dir);
2147
2148 strncpy (buf, complete_dir ? complete_dir : dir, sizeof(buf));
2149 if (complete_dir)
2150 free (complete_dir);
2151
2152 iface_entry_set_text (buf);
2153 free (dir);
2154 }
2155 else if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2156 char *entry_text = iface_entry_get_text ();
2157
2158 if (entry_text[0]) {
2159 char *dir = make_dir (entry_text);
2160
2161 iface_entry_history_add ();
2162
2163 if (dir) {
2164 /* strip trailing slash */
2165 if (dir[strlen(dir)-1] == '/'
2166 && strcmp(dir, "/"))
2167 dir[strlen(dir)-1] = 0;
2168 go_to_dir (dir, 0);
2169 free (dir);
2170 }
2171 }
2172
2173 iface_entry_disable ();
2174 free (entry_text);
2175 }
2176 else
2177 iface_entry_handle_key (k);
2178 }
2179
2180 /* Request playing from the specified URL. */
play_from_url(const char * url)2181 static void play_from_url (const char *url)
2182 {
2183 send_int_to_srv (CMD_LOCK);
2184
2185 change_srv_plist_serial ();
2186 send_int_to_srv (CMD_LIST_CLEAR);
2187 send_int_to_srv (CMD_LIST_ADD);
2188 send_str_to_srv (url);
2189
2190 send_int_to_srv (CMD_PLAY);
2191 send_str_to_srv ("");
2192
2193 send_int_to_srv (CMD_UNLOCK);
2194 }
2195
2196 /* Return malloc()ed string that is a copy of str without leading and trailing
2197 * white spaces. */
strip_white_spaces(const char * str)2198 static char *strip_white_spaces (const char *str)
2199 {
2200 char *clean;
2201 int n;
2202
2203 assert (str != NULL);
2204
2205 n = strlen (str);
2206
2207 /* Strip trailing. */
2208 while (n > 0 && isblank(str[n-1]))
2209 n--;
2210
2211 /* Strip leading whitespace. */
2212 while (*str && isblank(*str)) {
2213 str++;
2214 n--;
2215 }
2216
2217 if (n > 0) {
2218 clean = (char *)xmalloc ((n + 1) * sizeof(char));
2219 strncpy (clean, str, n);
2220 clean[n] = 0;
2221 }
2222 else
2223 clean = xstrdup ("");
2224
2225 return clean;
2226 }
2227
entry_key_go_url(const struct iface_key * k)2228 static void entry_key_go_url (const struct iface_key *k)
2229 {
2230 if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2231 char *entry_text = iface_entry_get_text ();
2232
2233 if (entry_text[0]) {
2234 char *clean_url = strip_white_spaces (entry_text);
2235
2236 iface_entry_history_add ();
2237
2238 if (is_url(clean_url))
2239 play_from_url (clean_url);
2240 else
2241 error ("Not a valid URL.");
2242
2243 free (clean_url);
2244 }
2245
2246 free (entry_text);
2247 iface_entry_disable ();
2248 }
2249 else
2250 iface_entry_handle_key (k);
2251 }
2252
add_url_to_plist(const char * url)2253 static void add_url_to_plist (const char *url)
2254 {
2255 assert (url != NULL);
2256
2257 if (plist_find_fname(playlist, url) == -1) {
2258 send_int_to_srv (CMD_LOCK);
2259
2260 if (options_get_int("SyncPlaylist")) {
2261 struct plist_item *item = plist_new_item ();
2262
2263 item->file = xstrdup (url);
2264 item->title_file = xstrdup (url);
2265
2266 send_int_to_srv (CMD_CLI_PLIST_ADD);
2267 send_item_to_srv (item);
2268
2269 plist_free_item_fields (item);
2270 free (item);
2271 }
2272 else {
2273 int added;
2274
2275 added = plist_add (playlist, url);
2276 make_file_title (playlist, added, 0);
2277 iface_add_to_plist (playlist, added);
2278 }
2279
2280 /* Add to the server's playlist if the server has our
2281 * playlist. */
2282 if (get_server_plist_serial() == plist_get_serial(playlist)) {
2283 send_int_to_srv (CMD_LIST_ADD);
2284 send_str_to_srv (url);
2285 }
2286 send_int_to_srv (CMD_UNLOCK);
2287 }
2288 else
2289 error ("URL already on the playlist");
2290 }
2291
entry_key_add_url(const struct iface_key * k)2292 static void entry_key_add_url (const struct iface_key *k)
2293 {
2294 if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2295 char *entry_text = iface_entry_get_text ();
2296
2297 if (entry_text[0]) {
2298 char *clean_url = strip_white_spaces (entry_text);
2299
2300 iface_entry_history_add ();
2301
2302 if (is_url(clean_url))
2303 add_url_to_plist (clean_url);
2304 else
2305 error ("Not a valid URL.");
2306
2307 free (clean_url);
2308 }
2309
2310 free (entry_text);
2311 iface_entry_disable ();
2312 }
2313 else
2314 iface_entry_handle_key (k);
2315 }
2316
entry_key_search(const struct iface_key * k)2317 static void entry_key_search (const struct iface_key *k)
2318 {
2319 if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2320 char *file = iface_get_curr_file ();
2321 char *text = iface_entry_get_text ();
2322
2323 iface_entry_disable ();
2324
2325 if (text[0]) {
2326
2327 if (!strcmp(file, "..")) {
2328 free (file);
2329 file = dir_up (cwd);
2330 }
2331
2332 if (is_url(file))
2333 play_from_url (file);
2334 else if (file_type(file) == F_DIR)
2335 go_to_dir (file, 0);
2336 else if (file_type(file) == F_PLAYLIST)
2337 go_to_playlist (file, 0, false);
2338 else
2339 play_it (file);
2340 }
2341
2342 free (text);
2343 free (file);
2344 }
2345 else
2346 iface_entry_handle_key (k);
2347 }
2348
save_playlist(const char * file,const char * cwd,const int save_serial)2349 static void save_playlist (const char *file, const char *cwd,
2350 const int save_serial)
2351 {
2352 iface_set_status ("Saving the playlist...");
2353 fill_tags (playlist, TAGS_COMMENTS | TAGS_TIME, 0);
2354 if (!user_wants_interrupt()) {
2355 if (plist_save(playlist, file, cwd, save_serial))
2356 interface_message ("Playlist saved");
2357 }
2358 else
2359 iface_set_status ("Aborted");
2360 iface_set_status ("");
2361 }
2362
entry_key_plist_save(const struct iface_key * k)2363 static void entry_key_plist_save (const struct iface_key *k)
2364 {
2365 if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2366 char *text = iface_entry_get_text ();
2367
2368 iface_entry_disable ();
2369
2370 if (text[0]) {
2371 char *ext = ext_pos (text);
2372 char *file;
2373
2374 /* add extension if necessary */
2375 if (!ext || strcmp(ext, "m3u")) {
2376 char *tmp = (char *)xmalloc((strlen(text) + 5) *
2377 sizeof(char));
2378
2379 sprintf (tmp, "%s.m3u", text);
2380 free (text);
2381 text = tmp;
2382 }
2383
2384 file = make_dir (text);
2385
2386 if (file_exists(file)) {
2387 iface_make_entry (ENTRY_PLIST_OVERWRITE);
2388 iface_entry_set_file (file);
2389 }
2390 else {
2391 save_playlist (file, strchr(text, '/')
2392 ? NULL : cwd, 0);
2393
2394 if (iface_in_dir_menu())
2395 reread_dir ();
2396 }
2397
2398 free (file);
2399 }
2400
2401 free (text);
2402 }
2403 else
2404 iface_entry_handle_key (k);
2405 }
2406
entry_key_plist_overwrite(const struct iface_key * k)2407 static void entry_key_plist_overwrite (const struct iface_key *k)
2408 {
2409 if (k->type == IFACE_KEY_CHAR && toupper(k->key.ucs) == 'Y') {
2410 char *file = iface_entry_get_file ();
2411
2412 assert (file != NULL);
2413
2414 iface_entry_disable ();
2415
2416 save_playlist (file, NULL, 0); /* FIXME: not always NULL! */
2417 if (iface_in_dir_menu())
2418 reread_dir ();
2419
2420 free (file);
2421 }
2422 else if (k->type == IFACE_KEY_CHAR && toupper(k->key.ucs) == 'N') {
2423 iface_entry_disable ();
2424 iface_message ("Not overwriting.");
2425 }
2426 }
2427
entry_key_user_query(const struct iface_key * k)2428 static void entry_key_user_query (const struct iface_key *k)
2429 {
2430 if (k->type == IFACE_KEY_CHAR && k->key.ucs == '\n') {
2431 char *entry_text = iface_entry_get_text ();
2432 iface_entry_disable ();
2433 iface_user_reply (entry_text);
2434 free (entry_text);
2435 }
2436 else
2437 iface_entry_handle_key (k);
2438 }
2439
2440 /* Handle keys while in an entry. */
entry_key(const struct iface_key * k)2441 static void entry_key (const struct iface_key *k)
2442 {
2443 switch (iface_get_entry_type()) {
2444 case ENTRY_GO_DIR:
2445 entry_key_go_dir (k);
2446 break;
2447 case ENTRY_GO_URL:
2448 entry_key_go_url (k);
2449 break;
2450 case ENTRY_ADD_URL:
2451 entry_key_add_url (k);
2452 break;
2453 case ENTRY_SEARCH:
2454 entry_key_search (k);
2455 break;
2456 case ENTRY_PLIST_SAVE:
2457 entry_key_plist_save (k);
2458 break;
2459 case ENTRY_PLIST_OVERWRITE:
2460 entry_key_plist_overwrite (k);
2461 break;
2462 case ENTRY_USER_QUERY:
2463 entry_key_user_query (k);
2464 break;
2465 default:
2466 abort (); /* BUG */
2467 }
2468 }
2469
2470 /* Update items in the menu for all items on the playlist. */
update_iface_menu(const enum iface_menu menu,const struct plist * plist)2471 static void update_iface_menu (const enum iface_menu menu,
2472 const struct plist *plist)
2473 {
2474 int i;
2475
2476 assert (plist != NULL);
2477
2478 for (i = 0; i < plist->num; i++)
2479 if (!plist_deleted(plist, i))
2480 iface_update_item (menu, plist, i);
2481 }
2482
2483 /* Switch ReadTags options and update the menu. */
switch_read_tags()2484 static void switch_read_tags ()
2485 {
2486 if (options_get_int ("ReadTags")) {
2487 options_set_int ("ReadTags", 0);
2488 switch_titles_file (dir_plist);
2489 switch_titles_file (playlist);
2490 iface_set_status ("ReadTags: no");
2491 }
2492 else {
2493 options_set_int ("ReadTags", 1);
2494 ask_for_tags (dir_plist, TAGS_COMMENTS);
2495 ask_for_tags (playlist, TAGS_COMMENTS);
2496 switch_titles_tags (dir_plist);
2497 switch_titles_tags (playlist);
2498 iface_set_status ("ReadTags: yes");
2499 }
2500
2501 update_iface_menu (IFACE_MENU_DIR, dir_plist);
2502 update_iface_menu (IFACE_MENU_PLIST, playlist);
2503 }
2504
seek(const int sec)2505 static void seek (const int sec)
2506 {
2507 send_int_to_srv (CMD_SEEK);
2508 send_int_to_srv (sec);
2509 }
2510
jump_to(const int sec)2511 static void jump_to (const int sec)
2512 {
2513 send_int_to_srv (CMD_JUMP_TO);
2514 send_int_to_srv (sec);
2515 }
2516
delete_item()2517 static void delete_item ()
2518 {
2519 char *file;
2520
2521 if (!iface_in_plist_menu()) {
2522 error ("You can only delete an item from the playlist.");
2523 return;
2524 }
2525
2526 assert (plist_count(playlist) > 0);
2527
2528 file = iface_get_curr_file ();
2529
2530 send_int_to_srv (CMD_LOCK);
2531 remove_file_from_playlist (file);
2532 send_int_to_srv (CMD_UNLOCK);
2533
2534 free (file);
2535 }
2536
2537 /* Select the file that is currently played. */
go_to_playing_file()2538 static void go_to_playing_file ()
2539 {
2540 if (curr_file.file && file_type(curr_file.file) == F_SOUND) {
2541 if (plist_find_fname(playlist, curr_file.file) != -1)
2542 iface_switch_to_plist ();
2543 else if (plist_find_fname(dir_plist, curr_file.file) != -1)
2544 iface_switch_to_dir ();
2545 else {
2546 char *slash;
2547 char *file = xstrdup (curr_file.file);
2548
2549 slash = strrchr (file, '/');
2550 assert (slash != NULL);
2551 *slash = 0;
2552
2553 if (file[0])
2554 go_to_dir (file, 0);
2555 else
2556 go_to_dir ("/", 0);
2557
2558 iface_switch_to_dir ();
2559 free (file);
2560 }
2561
2562 iface_select_file (curr_file.file);
2563 }
2564 }
2565
2566 /* Return the time like the standard time() function, but rounded i.e. if we
2567 * have 11.8 seconds, return 12 seconds. */
rounded_time()2568 static time_t rounded_time ()
2569 {
2570 struct timeval exact_time;
2571 time_t curr_time;
2572
2573 if (gettimeofday(&exact_time, NULL) == -1)
2574 interface_fatal ("gettimeofday() failed: %s", strerror(errno));
2575
2576 curr_time = exact_time.tv_sec;
2577 if (exact_time.tv_usec > 500000)
2578 curr_time++;
2579
2580 return curr_time;
2581 }
2582
2583 /* Handle silent seek key. */
seek_silent(const int sec)2584 static void seek_silent (const int sec)
2585 {
2586 if (curr_file.state == STATE_PLAY && curr_file.file
2587 && !is_url(curr_file.file)) {
2588 if (silent_seek_pos == -1) {
2589 silent_seek_pos = curr_file.curr_time + sec;
2590 }
2591 else
2592 silent_seek_pos += sec;
2593
2594 silent_seek_pos = CLAMP(0, silent_seek_pos, curr_file.total_time);
2595
2596 silent_seek_key_last = rounded_time ();
2597 iface_set_curr_time (silent_seek_pos);
2598 }
2599 }
2600
2601 /* Move the current playlist item (direction: 1 - up, -1 - down). */
move_item(const int direction)2602 static void move_item (const int direction)
2603 {
2604 char *file;
2605 int second;
2606 char *second_file;
2607
2608 if (!iface_in_plist_menu()) {
2609 error ("You can move only playlist items.");
2610 return;
2611 }
2612
2613 if (!(file = iface_get_curr_file()))
2614 return;
2615
2616 second = plist_find_fname (playlist, file);
2617 assert (second != -1);
2618
2619 if (direction == -1)
2620 second = plist_next (playlist, second);
2621 else if (direction == 1)
2622 second = plist_prev (playlist, second);
2623 else
2624 abort (); /* BUG */
2625
2626 if (second == -1) {
2627 free (file);
2628 return;
2629 }
2630
2631 second_file = plist_get_file (playlist, second);
2632
2633 send_int_to_srv (CMD_LOCK);
2634
2635 if (options_get_int("SyncPlaylist")) {
2636 send_int_to_srv (CMD_CLI_PLIST_MOVE);
2637 send_str_to_srv (file);
2638 send_str_to_srv (second_file);
2639 }
2640 else
2641 swap_playlist_items (file, second_file);
2642
2643 /* update the server's playlist */
2644 if (get_server_plist_serial() == plist_get_serial(playlist)) {
2645 send_int_to_srv (CMD_LIST_MOVE);
2646 send_str_to_srv (file);
2647 send_str_to_srv (second_file);
2648 }
2649
2650 send_int_to_srv (CMD_UNLOCK);
2651
2652 free (second_file);
2653 free (file);
2654 }
2655
2656 /* Handle releasing silent seek key. */
do_silent_seek()2657 static void do_silent_seek ()
2658 {
2659 time_t curr_time = time(NULL);
2660
2661 if (silent_seek_pos != -1 && silent_seek_key_last < curr_time) {
2662 seek (silent_seek_pos - curr_file.curr_time - 1);
2663 silent_seek_pos = -1;
2664 iface_set_curr_time (curr_file.curr_time);
2665 }
2666 }
2667
2668 /* Handle the 'next' command. */
cmd_next()2669 static void cmd_next ()
2670 {
2671 if (curr_file.state != STATE_STOP)
2672 send_int_to_srv (CMD_NEXT);
2673 else if (plist_count(playlist)) {
2674 if (plist_get_serial(playlist) != -1
2675 || get_server_plist_serial()
2676 != plist_get_serial(playlist)) {
2677 int serial;
2678
2679 send_int_to_srv (CMD_LOCK);
2680
2681 send_playlist (playlist, 1);
2682 serial = get_safe_serial();
2683 plist_set_serial (playlist, serial);
2684 send_int_to_srv (CMD_PLIST_SET_SERIAL);
2685 send_int_to_srv (plist_get_serial(playlist));
2686
2687 send_int_to_srv (CMD_UNLOCK);
2688 }
2689
2690 send_int_to_srv (CMD_PLAY);
2691 send_str_to_srv ("");
2692 }
2693 }
2694
2695 /* Add themes found in the directory to the list of theme files. */
add_themes_to_list(lists_t_strs * themes,const char * themes_dir)2696 static void add_themes_to_list (lists_t_strs *themes, const char *themes_dir)
2697 {
2698 DIR *dir;
2699 struct dirent *entry;
2700
2701 assert (themes);
2702 assert (themes_dir);
2703
2704 if (!(dir = opendir(themes_dir))) {
2705 logit ("Can't open themes directory %s: %s", themes_dir,
2706 strerror(errno));
2707 return;
2708 }
2709
2710 while ((entry = readdir(dir))) {
2711 char file[PATH_MAX];
2712
2713 if (entry->d_name[0] == '.')
2714 continue;
2715
2716 /* Filter out backup files (*~). */
2717 if (entry->d_name[strlen(entry->d_name)-1] == '~')
2718 continue;
2719
2720 if (snprintf(file, sizeof(file), "%s/%s", themes_dir,
2721 entry->d_name) >= (int)sizeof(file))
2722 continue;
2723
2724 lists_strs_append (themes, file);
2725 }
2726
2727 closedir (dir);
2728 }
2729
2730 /* Compare two pathnames based on filename. */
themes_cmp(const void * a,const void * b)2731 static int themes_cmp (const void *a, const void *b)
2732 {
2733 int result;
2734 char *sa = *(char **)a;
2735 char *sb = *(char **)b;
2736
2737 result = strcoll (strrchr (sa, '/') + 1, strrchr (sb, '/') + 1);
2738 if (result == 0)
2739 result = strcoll (sa, sb);
2740
2741 return result;
2742 }
2743
2744 /* Add themes found in the directories to the theme selection menu.
2745 * Return the number of items added. */
add_themes_to_menu(const char * user_themes,const char * system_themes)2746 static int add_themes_to_menu (const char *user_themes,
2747 const char *system_themes)
2748 {
2749 int ix;
2750 lists_t_strs *themes;
2751
2752 assert (user_themes);
2753 assert (system_themes);
2754
2755 themes = lists_strs_new (16);
2756 add_themes_to_list (themes, user_themes);
2757 add_themes_to_list (themes, system_themes);
2758 lists_strs_sort (themes, themes_cmp);
2759
2760 for (ix = 0; ix < lists_strs_size (themes); ix += 1) {
2761 char *file;
2762
2763 file = lists_strs_at (themes, ix);
2764 iface_add_file (file, strrchr (file, '/') + 1, F_THEME);
2765 }
2766
2767 lists_strs_free (themes);
2768
2769 return ix;
2770 }
2771
make_theme_menu()2772 static void make_theme_menu ()
2773 {
2774 iface_switch_to_theme_menu ();
2775
2776 if (add_themes_to_menu (create_file_name ("themes"),
2777 SYSTEM_THEMES_DIR) == 0) {
2778 if (!cwd[0])
2779 enter_first_dir (); /* we were at the playlist from the startup */
2780 else
2781 iface_switch_to_dir ();
2782
2783 error ("No themes found.");
2784 }
2785
2786 iface_update_theme_selection (get_current_theme ());
2787 iface_refresh ();
2788 }
2789
2790 /* Use theme from the currently selected file. */
use_theme()2791 static void use_theme ()
2792 {
2793 char *file = iface_get_curr_file ();
2794
2795 assert (iface_curritem_get_type() == F_THEME);
2796
2797 if (!file)
2798 return;
2799
2800 themes_switch_theme (file);
2801 iface_update_attrs ();
2802 iface_refresh ();
2803
2804 free (file);
2805 }
2806
2807 /* Handle keys while in the theme menu. */
theme_menu_key(const struct iface_key * k)2808 static void theme_menu_key (const struct iface_key *k)
2809 {
2810 if (!iface_key_is_resize(k)) {
2811 enum key_cmd cmd = get_key_cmd (CON_MENU, k);
2812
2813 switch (cmd) {
2814 case KEY_CMD_GO:
2815 use_theme ();
2816 break;
2817 case KEY_CMD_MENU_DOWN:
2818 case KEY_CMD_MENU_UP:
2819 case KEY_CMD_MENU_NPAGE:
2820 case KEY_CMD_MENU_PPAGE:
2821 case KEY_CMD_MENU_FIRST:
2822 case KEY_CMD_MENU_LAST:
2823 iface_menu_key (cmd);
2824 break;
2825 default:
2826 iface_switch_to_dir ();
2827 logit ("Bad key");
2828 }
2829 }
2830 }
2831
2832 /* Make sure that we have tags and a title for this file which is in a menu. */
make_sure_tags_exist(const char * file)2833 static void make_sure_tags_exist (const char *file)
2834 {
2835 struct plist *plist;
2836 int item_num;
2837
2838 if (file_type(file) != F_SOUND)
2839 return;
2840
2841 if ((item_num = plist_find_fname(dir_plist, file)) != -1)
2842 plist = dir_plist;
2843 else if ((item_num = plist_find_fname(playlist, file)) != -1)
2844 plist = playlist;
2845 else
2846 return;
2847
2848 if (!plist->items[item_num].tags
2849 || plist->items[item_num].tags->filled
2850 != (TAGS_COMMENTS | TAGS_TIME)) {
2851 int got_it = 0;
2852
2853 send_tags_request (file, TAGS_COMMENTS | TAGS_TIME);
2854
2855 while (!got_it) {
2856 int type = get_int_from_srv ();
2857 void *data = get_event_data (type);
2858
2859 if (type == EV_FILE_TAGS) {
2860 struct tag_ev_response *ev
2861 = (struct tag_ev_response *)data;
2862
2863 if (!strcmp(ev->file, file))
2864 got_it = 1;
2865 }
2866
2867 server_event (type, data);
2868 }
2869 }
2870 }
2871
2872 /* Request tags from the server for a file in the playlist or the directory
2873 * menu, wait until they arrive and return them (malloc()ed). */
get_tags(const char * file)2874 static struct file_tags *get_tags (const char *file)
2875 {
2876 struct plist *plist;
2877 int item_num;
2878
2879 make_sure_tags_exist (file);
2880
2881 if ((item_num = plist_find_fname(dir_plist, file)) != -1)
2882 plist = dir_plist;
2883 else if ((item_num = plist_find_fname(playlist, file)) != -1)
2884 plist = playlist;
2885 else
2886 return tags_new ();
2887
2888 if (file_type(file) == F_SOUND)
2889 return tags_dup (plist->items[item_num].tags);
2890
2891 return tags_new ();
2892 }
2893
2894 /* Get the title of a file (malloc()ed) that is present in a menu. */
get_title(const char * file)2895 static char *get_title (const char *file)
2896 {
2897 struct plist *plist;
2898 int item_num;
2899
2900 make_sure_tags_exist (file);
2901
2902 if ((item_num = plist_find_fname(dir_plist, file)) != -1)
2903 plist = dir_plist;
2904 else if ((item_num = plist_find_fname(playlist, file)) != -1)
2905 plist = playlist;
2906 else
2907 return NULL;
2908
2909 return xstrdup (plist->items[item_num].title_tags
2910 ? plist->items[item_num].title_tags
2911 : plist->items[item_num].title_file);
2912 }
2913
2914 /* Substitute arguments for custom command that begin with '%'.
2915 * The new value is returned. */
custom_cmd_substitute(const char * arg)2916 static char *custom_cmd_substitute (const char *arg)
2917 {
2918 char *result = NULL;
2919 char *file = NULL;
2920 struct file_tags *tags = NULL;
2921
2922 if (strlen (arg) == 2 && arg[0] == '%') {
2923 switch (arg[1]) {
2924 case 'i':
2925 file = iface_get_curr_file ();
2926 result = get_title (file);
2927 break;
2928 case 't':
2929 file = iface_get_curr_file ();
2930 tags = get_tags (file);
2931 result = xstrdup (tags->title);
2932 break;
2933 case 'a':
2934 file = iface_get_curr_file ();
2935 tags = get_tags (file);
2936 result = xstrdup (tags->album);
2937 break;
2938 case 'r':
2939 file = iface_get_curr_file ();
2940 tags = get_tags (file);
2941 result = xstrdup (tags->artist);
2942 break;
2943 case 'n':
2944 file = iface_get_curr_file ();
2945 tags = get_tags (file);
2946 result = (char *) xmalloc (sizeof (char) * 10);
2947 snprintf (result, 10, "%d", tags->track);
2948 break;
2949 case 'm':
2950 file = iface_get_curr_file ();
2951 tags = get_tags (file);
2952 result = (char *) xmalloc (sizeof (char) * 10);
2953 snprintf (result, 10, "%d", tags->time);
2954 break;
2955 case 'f':
2956 result = iface_get_curr_file ();
2957 break;
2958 case 'I':
2959 result = xstrdup (curr_file.title);
2960 break;
2961 case 'T':
2962 if (curr_file.tags && curr_file.tags->title)
2963 result = xstrdup (curr_file.tags->title);
2964 break;
2965 case 'A':
2966 if (curr_file.tags && curr_file.tags->album)
2967 result = xstrdup (curr_file.tags->album);
2968 break;
2969 case 'R':
2970 if (curr_file.tags && curr_file.tags->artist)
2971 result = xstrdup (curr_file.tags->artist);
2972 break;
2973 case 'N':
2974 if (curr_file.tags && curr_file.tags->track != -1) {
2975 result = (char *) xmalloc (sizeof (char) * 10);
2976 snprintf (result, 10, "%d", curr_file.tags->track);
2977 }
2978 break;
2979 case 'M':
2980 if (curr_file.tags && curr_file.tags->time != -1) {
2981 result = (char *) xmalloc (sizeof (char) * 10);
2982 snprintf (result, 10, "%d", curr_file.tags->time);
2983 }
2984 break;
2985 case 'F':
2986 if (curr_file.file)
2987 result = xstrdup (curr_file.file);
2988 break;
2989 case 'S':
2990 if (curr_file.file && curr_file.block_file) {
2991 result = (char *) xmalloc (sizeof (char) * 10);
2992 snprintf (result, 10, "%d", curr_file.block_start);
2993 }
2994 break;
2995 case 'E':
2996 if (curr_file.file && curr_file.block_file) {
2997 result = (char *) xmalloc (sizeof (char) * 10);
2998 snprintf (result, 10, "%d", curr_file.block_end);
2999 }
3000 break;
3001 default:
3002 result = xstrdup (arg);
3003 }
3004 }
3005 else
3006 result = xstrdup (arg);
3007
3008 /* Replace nonexisting data with an empty string. */
3009 if (!result)
3010 result = xstrdup ("");
3011
3012 free (file);
3013 if (tags)
3014 tags_free (tags);
3015
3016 return result;
3017 }
3018
run_external_cmd(char ** args,const int arg_num ATTR_UNUSED)3019 static void run_external_cmd (char **args, const int arg_num ATTR_UNUSED)
3020 {
3021 pid_t child;
3022
3023 assert (args != NULL);
3024 assert (arg_num >= 1);
3025 assert (args[0] != NULL);
3026 assert (args[arg_num] == NULL);
3027
3028 iface_temporary_exit ();
3029
3030 child = fork();
3031 if (child == -1)
3032 error ("fork() failed: %s", strerror (errno));
3033 else {
3034 int status;
3035
3036 if (child == 0) { /* I'm a child. */
3037
3038 putchar ('\n');
3039 execvp (args[0], args);
3040
3041 /* We have an error. */
3042 fprintf (stderr, "\nError executing %s: %s\n", args[0],
3043 strerror(errno));
3044 sleep (2);
3045 exit (EXIT_FAILURE);
3046 }
3047
3048 /* parent */
3049 waitpid (child, &status, 0);
3050 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
3051 fprintf (stderr, "\nCommand exited with error (status %d).\n",
3052 WEXITSTATUS(status));
3053 sleep (2);
3054 }
3055 iface_restore ();
3056 }
3057 }
3058
3059 /* Exec (execvp()) a custom command (ExecCommand[1-10] options). */
exec_custom_command(const char * option)3060 static void exec_custom_command (const char *option)
3061 {
3062 char *cmd, *arg;
3063 char **args;
3064 int ix, arg_num;
3065 lists_t_strs *cmd_list, *arg_list;
3066
3067 assert (option != NULL);
3068
3069 cmd = options_get_str (option);
3070 if (!cmd || !cmd[0]) {
3071 error ("%s is not set", option);
3072 return;
3073 }
3074
3075 /* Split into arguments. */
3076 cmd_list = lists_strs_new (4);
3077 arg_num = lists_strs_tokenise (cmd_list, cmd);
3078 if (arg_num == 0) {
3079 error ("Malformed %s option", option);
3080 lists_strs_free (cmd_list);
3081 return;
3082 }
3083
3084 arg_list = lists_strs_new (lists_strs_size (cmd_list));
3085 for (ix = 0; ix < arg_num; ix += 1) {
3086 arg = custom_cmd_substitute (lists_strs_at (cmd_list, ix));
3087 lists_strs_push (arg_list, arg);
3088 }
3089 lists_strs_free (cmd_list);
3090
3091 cmd = lists_strs_fmt (arg_list, " %s");
3092 logit ("Running command:%s", cmd);
3093 free (cmd);
3094
3095 args = lists_strs_save (arg_list);
3096 lists_strs_free (arg_list);
3097 run_external_cmd (args, arg_num);
3098 free (args);
3099
3100 if (iface_in_dir_menu())
3101 reread_dir ();
3102 }
3103
go_to_fast_dir(const int num)3104 static void go_to_fast_dir (const int num)
3105 {
3106 char option_name[20];
3107
3108 assert (RANGE(1, num, 10));
3109
3110 sprintf (option_name, "FastDir%d", num);
3111
3112 if (options_get_str(option_name)) {
3113 char dir[PATH_MAX] = "/";
3114
3115 resolve_path (dir, sizeof(dir), options_get_str(option_name));
3116 go_to_dir (dir, 0);
3117 }
3118 else
3119 error ("%s is not defined", option_name);
3120 }
3121
toggle_playlist_full_paths(void)3122 static void toggle_playlist_full_paths (void)
3123 {
3124 int new_val = !options_get_int ("PlaylistFullPaths");
3125
3126 options_set_int ("PlaylistFullPaths", new_val);
3127
3128 if (new_val)
3129 iface_set_status ("PlaylistFullPaths: on");
3130 else
3131 iface_set_status ("PlaylistFullPaths: off");
3132
3133 update_iface_menu (IFACE_MENU_PLIST, playlist);
3134 }
3135
3136 /* Handle key. */
menu_key(const struct iface_key * k)3137 static void menu_key (const struct iface_key *k)
3138 {
3139 if (iface_in_help ())
3140 iface_handle_help_key (k);
3141 else if (iface_in_lyrics ())
3142 iface_handle_lyrics_key (k);
3143 else if (iface_in_entry ())
3144 entry_key (k);
3145 else if (iface_in_theme_menu ())
3146 theme_menu_key (k);
3147 else if (!iface_key_is_resize (k)) {
3148 enum key_cmd cmd = get_key_cmd (CON_MENU, k);
3149
3150 switch (cmd) {
3151 case KEY_CMD_QUIT_CLIENT:
3152 want_quit = QUIT_CLIENT;
3153 break;
3154 case KEY_CMD_GO:
3155 go_file ();
3156 break;
3157 case KEY_CMD_MENU_DOWN:
3158 case KEY_CMD_MENU_UP:
3159 case KEY_CMD_MENU_NPAGE:
3160 case KEY_CMD_MENU_PPAGE:
3161 case KEY_CMD_MENU_FIRST:
3162 case KEY_CMD_MENU_LAST:
3163 iface_menu_key (cmd);
3164 last_menu_move_time = time (NULL);
3165 break;
3166 case KEY_CMD_QUIT:
3167 want_quit = QUIT_SERVER;
3168 break;
3169 case KEY_CMD_STOP:
3170 send_int_to_srv (CMD_STOP);
3171 break;
3172 case KEY_CMD_NEXT:
3173 cmd_next ();
3174 break;
3175 case KEY_CMD_PREVIOUS:
3176 send_int_to_srv (CMD_PREV);
3177 break;
3178 case KEY_CMD_PAUSE:
3179 switch_pause ();
3180 break;
3181 case KEY_CMD_TOGGLE_READ_TAGS:
3182 switch_read_tags ();
3183 break;
3184 case KEY_CMD_TOGGLE_SHUFFLE:
3185 toggle_option ("Shuffle");
3186 break;
3187 case KEY_CMD_TOGGLE_REPEAT:
3188 toggle_option ("Repeat");
3189 break;
3190 case KEY_CMD_TOGGLE_AUTO_NEXT:
3191 toggle_option ("AutoNext");
3192 break;
3193 case KEY_CMD_TOGGLE_MENU:
3194 toggle_menu ();
3195 break;
3196 case KEY_CMD_TOGGLE_PLAYLIST_FULL_PATHS:
3197 toggle_playlist_full_paths ();
3198 break;
3199 case KEY_CMD_PLIST_ADD_FILE:
3200 add_file_plist ();
3201 break;
3202 case KEY_CMD_PLIST_CLEAR:
3203 cmd_clear_playlist ();
3204 break;
3205 case KEY_CMD_PLIST_ADD_DIR:
3206 add_dir_plist ();
3207 break;
3208 case KEY_CMD_PLIST_REMOVE_DEAD_ENTRIES:
3209 remove_dead_entries_plist ();
3210 break;
3211 case KEY_CMD_MIXER_DEC_1:
3212 adjust_mixer (-1);
3213 break;
3214 case KEY_CMD_MIXER_DEC_5:
3215 adjust_mixer (-5);
3216 break;
3217 case KEY_CMD_MIXER_INC_5:
3218 adjust_mixer (+5);
3219 break;
3220 case KEY_CMD_MIXER_INC_1:
3221 adjust_mixer (+1);
3222 break;
3223 case KEY_CMD_SEEK_BACKWARD:
3224 seek (-options_get_int ("SeekTime"));
3225 break;
3226 case KEY_CMD_SEEK_FORWARD:
3227 seek (options_get_int ("SeekTime"));
3228 break;
3229 case KEY_CMD_HELP:
3230 iface_switch_to_help ();
3231 break;
3232 case KEY_CMD_LYRICS:
3233 iface_switch_to_lyrics ();
3234 break;
3235 case KEY_CMD_HIDE_MESSAGE:
3236 iface_disable_message ();
3237 break;
3238 case KEY_CMD_REFRESH:
3239 iface_refresh ();
3240 break;
3241 case KEY_CMD_RELOAD:
3242 if (iface_in_dir_menu ())
3243 reread_dir ();
3244 break;
3245 case KEY_CMD_TOGGLE_SHOW_HIDDEN_FILES:
3246 options_set_int ("ShowHiddenFiles",
3247 !options_get_int ("ShowHiddenFiles"));
3248 if (iface_in_dir_menu ())
3249 reread_dir ();
3250 break;
3251 case KEY_CMD_GO_MUSIC_DIR:
3252 go_to_music_dir ();
3253 break;
3254 case KEY_CMD_PLIST_DEL:
3255 delete_item ();
3256 break;
3257 case KEY_CMD_MENU_SEARCH:
3258 iface_make_entry (ENTRY_SEARCH);
3259 break;
3260 case KEY_CMD_PLIST_SAVE:
3261 if (plist_count (playlist))
3262 iface_make_entry (ENTRY_PLIST_SAVE);
3263 else
3264 error ("The playlist is empty.");
3265 break;
3266 case KEY_CMD_TOGGLE_SHOW_TIME:
3267 toggle_show_time ();
3268 break;
3269 case KEY_CMD_TOGGLE_SHOW_FORMAT:
3270 toggle_show_format ();
3271 break;
3272 case KEY_CMD_GO_TO_PLAYING_FILE:
3273 go_to_playing_file ();
3274 break;
3275 case KEY_CMD_GO_DIR:
3276 iface_make_entry (ENTRY_GO_DIR);
3277 break;
3278 case KEY_CMD_GO_URL:
3279 iface_make_entry (ENTRY_GO_URL);
3280 break;
3281 case KEY_CMD_GO_DIR_UP:
3282 go_dir_up ();
3283 break;
3284 case KEY_CMD_WRONG:
3285 error ("Bad command");
3286 break;
3287 case KEY_CMD_SEEK_FORWARD_5:
3288 seek_silent (options_get_int ("SilentSeekTime"));
3289 break;
3290 case KEY_CMD_SEEK_BACKWARD_5:
3291 seek_silent (-options_get_int ("SilentSeekTime"));
3292 break;
3293 case KEY_CMD_VOLUME_10:
3294 set_mixer (10);
3295 break;
3296 case KEY_CMD_VOLUME_20:
3297 set_mixer (20);
3298 break;
3299 case KEY_CMD_VOLUME_30:
3300 set_mixer (30);
3301 break;
3302 case KEY_CMD_VOLUME_40:
3303 set_mixer (40);
3304 break;
3305 case KEY_CMD_VOLUME_50:
3306 set_mixer (50);
3307 break;
3308 case KEY_CMD_VOLUME_60:
3309 set_mixer (60);
3310 break;
3311 case KEY_CMD_VOLUME_70:
3312 set_mixer (70);
3313 break;
3314 case KEY_CMD_VOLUME_80:
3315 set_mixer (80);
3316 break;
3317 case KEY_CMD_VOLUME_90:
3318 set_mixer (90);
3319 break;
3320 case KEY_CMD_MARK_START:
3321 file_info_block_mark (&curr_file.block_start);
3322 break;
3323 case KEY_CMD_MARK_END:
3324 file_info_block_mark (&curr_file.block_end);
3325 break;
3326 case KEY_CMD_FAST_DIR_1:
3327 go_to_fast_dir (1);
3328 break;
3329 case KEY_CMD_FAST_DIR_2:
3330 go_to_fast_dir (2);
3331 break;
3332 case KEY_CMD_FAST_DIR_3:
3333 go_to_fast_dir (3);
3334 break;
3335 case KEY_CMD_FAST_DIR_4:
3336 go_to_fast_dir (4);
3337 break;
3338 case KEY_CMD_FAST_DIR_5:
3339 go_to_fast_dir (5);
3340 break;
3341 case KEY_CMD_FAST_DIR_6:
3342 go_to_fast_dir (6);
3343 break;
3344 case KEY_CMD_FAST_DIR_7:
3345 go_to_fast_dir (7);
3346 break;
3347 case KEY_CMD_FAST_DIR_8:
3348 go_to_fast_dir (8);
3349 break;
3350 case KEY_CMD_FAST_DIR_9:
3351 go_to_fast_dir (9);
3352 break;
3353 case KEY_CMD_FAST_DIR_10:
3354 go_to_fast_dir (10);
3355 break;
3356 case KEY_CMD_TOGGLE_MIXER:
3357 debug ("Toggle mixer.");
3358 send_int_to_srv (CMD_TOGGLE_MIXER_CHANNEL);
3359 break;
3360 case KEY_CMD_TOGGLE_SOFTMIXER:
3361 debug ("Toggle softmixer.");
3362 send_int_to_srv (CMD_TOGGLE_SOFTMIXER);
3363 break;
3364 case KEY_CMD_TOGGLE_EQUALIZER:
3365 debug ("Toggle equalizer.");
3366 send_int_to_srv (CMD_TOGGLE_EQUALIZER);
3367 break;
3368 case KEY_CMD_EQUALIZER_REFRESH:
3369 debug ("Equalizer Refresh.");
3370 send_int_to_srv (CMD_EQUALIZER_REFRESH);
3371 break;
3372 case KEY_CMD_EQUALIZER_PREV:
3373 debug ("Equalizer Prev.");
3374 send_int_to_srv (CMD_EQUALIZER_PREV);
3375 break;
3376 case KEY_CMD_EQUALIZER_NEXT:
3377 debug ("Equalizer Next.");
3378 send_int_to_srv (CMD_EQUALIZER_NEXT);
3379 break;
3380 case KEY_CMD_TOGGLE_MAKE_MONO:
3381 debug ("Toggle Mono-Mixing.");
3382 send_int_to_srv (CMD_TOGGLE_MAKE_MONO);
3383 break;
3384 case KEY_CMD_TOGGLE_LAYOUT:
3385 iface_toggle_layout ();
3386 break;
3387 case KEY_CMD_TOGGLE_PERCENT:
3388 iface_toggle_percent ();
3389 break;
3390 case KEY_CMD_PLIST_MOVE_UP:
3391 move_item (1);
3392 break;
3393 case KEY_CMD_PLIST_MOVE_DOWN:
3394 move_item (-1);
3395 break;
3396 case KEY_CMD_ADD_STREAM:
3397 iface_make_entry (ENTRY_ADD_URL);
3398 break;
3399 case KEY_CMD_THEME_MENU:
3400 make_theme_menu ();
3401 break;
3402 case KEY_CMD_EXEC1:
3403 exec_custom_command ("ExecCommand1");
3404 break;
3405 case KEY_CMD_EXEC2:
3406 exec_custom_command ("ExecCommand2");
3407 break;
3408 case KEY_CMD_EXEC3:
3409 exec_custom_command ("ExecCommand3");
3410 break;
3411 case KEY_CMD_EXEC4:
3412 exec_custom_command ("ExecCommand4");
3413 break;
3414 case KEY_CMD_EXEC5:
3415 exec_custom_command ("ExecCommand5");
3416 break;
3417 case KEY_CMD_EXEC6:
3418 exec_custom_command ("ExecCommand6");
3419 break;
3420 case KEY_CMD_EXEC7:
3421 exec_custom_command ("ExecCommand7");
3422 break;
3423 case KEY_CMD_EXEC8:
3424 exec_custom_command ("ExecCommand8");
3425 break;
3426 case KEY_CMD_EXEC9:
3427 exec_custom_command ("ExecCommand9");
3428 break;
3429 case KEY_CMD_EXEC10:
3430 exec_custom_command ("ExecCommand10");
3431 break;
3432 case KEY_CMD_QUEUE_TOGGLE_FILE:
3433 queue_toggle_file ();
3434 break;
3435 case KEY_CMD_QUEUE_CLEAR:
3436 cmd_clear_queue ();
3437 break;
3438 default:
3439 abort ();
3440 }
3441 }
3442 }
3443
3444 /* Get event from the server and handle it. */
get_and_handle_event()3445 static void get_and_handle_event ()
3446 {
3447 int type;
3448
3449 if (!get_int_from_srv_noblock(&type)) {
3450 debug ("Getting event would block.");
3451 return;
3452 }
3453
3454 server_event (type, get_event_data(type));
3455 }
3456
3457 /* Handle events from the queue. */
dequeue_events()3458 static void dequeue_events ()
3459 {
3460 struct event *e;
3461
3462 debug ("Dequeuing events...");
3463
3464 while ((e = event_get_first(&events))) {
3465 server_event (e->type, e->data);
3466 event_pop (&events);
3467 }
3468
3469 debug ("done");
3470 }
3471
3472 /* Action after CTRL-C was pressed. */
handle_interrupt()3473 static void handle_interrupt ()
3474 {
3475 if (iface_in_entry())
3476 iface_entry_disable ();
3477 }
3478
init_interface(const int sock,const int logging,lists_t_strs * args)3479 void init_interface (const int sock, const int logging, lists_t_strs *args)
3480 {
3481 FILE *logfp;
3482
3483 logit ("Starting MOC Interface");
3484
3485 logfp = NULL;
3486 if (logging) {
3487 logfp = fopen (INTERFACE_LOG, "a");
3488 if (!logfp)
3489 fatal ("Can't open client log file: %s", strerror (errno));
3490 }
3491 log_init_stream (logfp, INTERFACE_LOG);
3492
3493 /* Set locale according to the environment variables. */
3494 if (!setlocale(LC_CTYPE, ""))
3495 logit ("Could not set locale!");
3496
3497 srv_sock = sock;
3498
3499 file_info_reset (&curr_file);
3500 file_info_block_init (&curr_file);
3501 init_playlists ();
3502 event_queue_init (&events);
3503 keys_init ();
3504 windows_init ();
3505 get_server_options ();
3506 update_mixer_name ();
3507
3508 signal (SIGQUIT, sig_quit);
3509 signal (SIGTERM, sig_quit);
3510 signal (SIGHUP, sig_quit);
3511 signal (SIGINT, sig_interrupt);
3512
3513 #ifdef SIGWINCH
3514 signal (SIGWINCH, sig_winch);
3515 #endif
3516
3517 if (!lists_strs_empty (args)) {
3518 process_args (args);
3519
3520 if (plist_count(playlist) == 0) {
3521 if (!options_get_int("SyncPlaylist") || !use_server_playlist())
3522 load_playlist ();
3523 send_int_to_srv (CMD_SEND_PLIST_EVENTS);
3524 }
3525 else if (options_get_int("SyncPlaylist")) {
3526 struct plist tmp_plist;
3527
3528 /* We have made the playlist from command line. */
3529
3530 /* The playlist should be now clear, but this will give
3531 * us the serial number of the playlist used by other
3532 * clients. */
3533 plist_init (&tmp_plist);
3534 get_server_playlist (&tmp_plist);
3535
3536 send_int_to_srv (CMD_SEND_PLIST_EVENTS);
3537
3538 send_int_to_srv (CMD_LOCK);
3539 send_int_to_srv (CMD_CLI_PLIST_CLEAR);
3540
3541 plist_set_serial (playlist,
3542 plist_get_serial(&tmp_plist));
3543 plist_free (&tmp_plist);
3544
3545 change_srv_plist_serial ();
3546
3547 iface_set_status ("Notifying clients...");
3548 send_items_to_clients (playlist);
3549 iface_set_status ("");
3550 plist_clear (playlist);
3551 waiting_for_plist_load = 1;
3552 send_int_to_srv (CMD_UNLOCK);
3553
3554 /* Now enter_first_dir() should not go to the music
3555 * directory. */
3556 options_set_int ("StartInMusicDir", 0);
3557 }
3558 }
3559 else {
3560 send_int_to_srv (CMD_SEND_PLIST_EVENTS);
3561 if (!options_get_int("SyncPlaylist") || !use_server_playlist())
3562 load_playlist ();
3563 enter_first_dir ();
3564 }
3565
3566 /* Ask the server for queue. */
3567 use_server_queue ();
3568
3569 if (options_get_int("SyncPlaylist"))
3570 send_int_to_srv (CMD_CAN_SEND_PLIST);
3571
3572 update_state ();
3573
3574 if (options_get_int("CanStartInPlaylist")
3575 && curr_file.file
3576 && plist_find_fname(playlist, curr_file.file) != -1)
3577 iface_switch_to_plist ();
3578 }
3579
interface_loop()3580 void interface_loop ()
3581 {
3582 while (want_quit == NO_QUIT) {
3583 fd_set fds;
3584 int ret;
3585 struct timeval timeout = { 1, 0 };
3586
3587 FD_ZERO (&fds);
3588 FD_SET (srv_sock, &fds);
3589 FD_SET (STDIN_FILENO, &fds);
3590
3591 dequeue_events ();
3592 ret = select (srv_sock + 1, &fds, NULL, NULL, &timeout);
3593 if (ret == -1 && !want_quit && errno != EINTR)
3594 interface_fatal ("select() failed: %s", strerror(errno));
3595
3596 iface_tick ();
3597
3598 if (ret == 0)
3599 do_silent_seek ();
3600
3601 #ifdef SIGWINCH
3602 if (want_resize)
3603 do_resize ();
3604 #endif
3605
3606 if (ret > 0) {
3607 if (FD_ISSET(STDIN_FILENO, &fds)) {
3608 struct iface_key k;
3609
3610 iface_get_key (&k);
3611
3612 clear_interrupt ();
3613 menu_key (&k);
3614 }
3615
3616 if (!want_quit) {
3617 if (FD_ISSET(srv_sock, &fds))
3618 get_and_handle_event ();
3619 do_silent_seek ();
3620 }
3621 }
3622 else if (user_wants_interrupt())
3623 handle_interrupt ();
3624
3625 if (!want_quit)
3626 update_mixer_value ();
3627 }
3628 }
3629
3630 /* Save the current directory path to a file. */
save_curr_dir()3631 static void save_curr_dir ()
3632 {
3633 FILE *dir_file;
3634
3635 if (!(dir_file = fopen(create_file_name("last_directory"), "w"))) {
3636 error ("Can't save current directory: %s", strerror(errno));
3637 return;
3638 }
3639
3640 fprintf (dir_file, "%s", cwd);
3641 fclose (dir_file);
3642 }
3643
3644 /* Save the playlist in .moc directory or remove the old playist if the
3645 * playlist is empty. */
save_playlist_in_moc()3646 static void save_playlist_in_moc ()
3647 {
3648 char *plist_file = create_file_name (PLAYLIST_FILE);
3649
3650 if (plist_count(playlist) && options_get_int("SavePlaylist"))
3651 save_playlist (plist_file, NULL, 1);
3652 else
3653 unlink (plist_file);
3654 }
3655
interface_end()3656 void interface_end ()
3657 {
3658 save_curr_dir ();
3659 save_playlist_in_moc ();
3660 if (want_quit == QUIT_SERVER)
3661 send_int_to_srv (CMD_QUIT);
3662 else
3663 send_int_to_srv (CMD_DISCONNECT);
3664 close (srv_sock);
3665 srv_sock = -1;
3666
3667 windows_end ();
3668 keys_cleanup ();
3669
3670 plist_free (dir_plist);
3671 plist_free (playlist);
3672 plist_free (queue);
3673 free (dir_plist);
3674 free (playlist);
3675 free (queue);
3676
3677 event_queue_free (&events);
3678
3679 logit ("Interface exited");
3680
3681 log_close ();
3682 }
3683
interface_fatal(const char * format,...)3684 void interface_fatal (const char *format, ...)
3685 {
3686 char *msg;
3687 va_list va;
3688
3689 va_start (va, format);
3690 msg = format_msg_va (format, va);
3691 va_end (va);
3692
3693 windows_end ();
3694 fatal ("%s", msg);
3695 }
3696
interface_error(const char * msg)3697 void interface_error (const char *msg)
3698 {
3699 iface_error (msg);
3700 }
3701
interface_cmdline_clear_plist(int server_sock)3702 void interface_cmdline_clear_plist (int server_sock)
3703 {
3704 struct plist plist;
3705 int serial;
3706 srv_sock = server_sock; /* the interface is not initialized, so set it
3707 here */
3708
3709 plist_init (&plist);
3710
3711 if (options_get_int("SyncPlaylist"))
3712 send_int_to_srv (CMD_CLI_PLIST_CLEAR);
3713
3714 if (recv_server_plist(&plist) && plist_get_serial(&plist)
3715 == get_server_plist_serial()) {
3716 send_int_to_srv (CMD_LOCK);
3717 send_int_to_srv (CMD_GET_SERIAL);
3718 serial = get_data_int ();
3719 send_int_to_srv (CMD_PLIST_SET_SERIAL);
3720 send_int_to_srv (serial);
3721 send_int_to_srv (CMD_LIST_CLEAR);
3722 send_int_to_srv (CMD_UNLOCK);
3723 }
3724
3725 unlink (create_file_name (PLAYLIST_FILE));
3726
3727 plist_free (&plist);
3728 }
3729
add_recursively(struct plist * plist,lists_t_strs * args)3730 static void add_recursively (struct plist *plist, lists_t_strs *args)
3731 {
3732 int ix;
3733
3734 for (ix = 0; ix < lists_strs_size (args); ix += 1) {
3735 int dir;
3736 char path[PATH_MAX + 1];
3737 const char *arg;
3738
3739 arg = lists_strs_at (args, ix);
3740
3741 if (!is_url (arg) && arg[0] != '/') {
3742 if (arg[0] == '/')
3743 strcpy (path, "/");
3744 else {
3745 strncpy (path, cwd, sizeof (path));
3746 path[sizeof (path) - 1] = 0;
3747 }
3748 resolve_path (path, sizeof (path), arg);
3749 }
3750 else {
3751 strncpy (path, arg, sizeof (path));
3752 path[sizeof (path) - 1] = 0;
3753
3754 if (!is_url (arg))
3755 resolve_path (path, sizeof (path), "");
3756 }
3757
3758 dir = is_dir (path);
3759
3760 if (dir == 1)
3761 read_directory_recurr (path, plist);
3762 else if (is_plist_file (arg))
3763 plist_load (plist, arg, cwd, 0);
3764 else if ((is_url (path) || is_sound_file (path))
3765 && plist_find_fname (plist, path) == -1) {
3766 int added = plist_add (plist, path);
3767
3768 if (is_url (path))
3769 make_file_title (plist, added, 0);
3770 }
3771 }
3772 }
3773
interface_cmdline_append(int server_sock,lists_t_strs * args)3774 void interface_cmdline_append (int server_sock, lists_t_strs *args)
3775 {
3776 srv_sock = server_sock; /* the interface is not initialized, so set it
3777 here */
3778
3779 if (options_get_int("SyncPlaylist")) {
3780 struct plist clients_plist;
3781 struct plist new;
3782
3783 plist_init (&clients_plist);
3784 plist_init (&new);
3785
3786 if (!getcwd(cwd, sizeof(cwd)))
3787 fatal ("Can't get CWD: %s", strerror(errno));
3788
3789 if (recv_server_plist(&clients_plist)) {
3790 add_recursively (&new, args);
3791 plist_sort_fname (&new);
3792
3793 send_int_to_srv (CMD_LOCK);
3794
3795 plist_remove_common_items (&new, &clients_plist);
3796 send_items_to_clients (&new);
3797
3798 if (get_server_plist_serial()
3799 == plist_get_serial(&clients_plist))
3800 send_playlist (&new, 0);
3801 send_int_to_srv (CMD_UNLOCK);
3802 }
3803 else {
3804 struct plist saved_plist;
3805
3806 plist_init (&saved_plist);
3807
3808 /* this checks if the file exists */
3809 if (file_type (create_file_name (PLAYLIST_FILE))
3810 == F_PLAYLIST)
3811 plist_load (&saved_plist,
3812 create_file_name (PLAYLIST_FILE),
3813 cwd, 1);
3814 add_recursively (&new, args);
3815 plist_sort_fname (&new);
3816
3817 send_int_to_srv (CMD_LOCK);
3818 plist_remove_common_items (&new, &saved_plist);
3819 if (plist_get_serial(&saved_plist))
3820 plist_set_serial(&saved_plist,
3821 get_safe_serial());
3822 plist_set_serial(&new, plist_get_serial(&saved_plist));
3823 send_playlist (&new, 0);
3824 send_int_to_srv (CMD_UNLOCK);
3825
3826 plist_cat (&saved_plist, &new);
3827 if (options_get_int("SavePlaylist")) {
3828 fill_tags (&saved_plist, TAGS_COMMENTS
3829 | TAGS_TIME, 1);
3830 plist_save (&saved_plist,
3831 create_file_name (PLAYLIST_FILE),
3832 NULL, 1);
3833 }
3834
3835 plist_free (&saved_plist);
3836 }
3837
3838 plist_free (&clients_plist);
3839 plist_free (&new);
3840 }
3841 }
3842
interface_cmdline_play_first(int server_sock)3843 void interface_cmdline_play_first (int server_sock)
3844 {
3845 struct plist plist;
3846
3847 srv_sock = server_sock; /* the interface is not initialized, so set it
3848 here */
3849
3850 if (!getcwd(cwd, sizeof(cwd)))
3851 fatal ("Can't get CWD: %s", strerror(errno));
3852 plist_init (&plist);
3853
3854 send_int_to_srv (CMD_GET_SERIAL);
3855 plist_set_serial (&plist, get_data_int());
3856
3857 /* the second condition will checks if the file exists */
3858 if (!recv_server_plist(&plist)
3859 && file_type (create_file_name (PLAYLIST_FILE))
3860 == F_PLAYLIST)
3861 plist_load (&plist, create_file_name (PLAYLIST_FILE), cwd, 1);
3862
3863 send_int_to_srv (CMD_LOCK);
3864 if (get_server_plist_serial() != plist_get_serial(&plist)) {
3865 send_playlist (&plist, 1);
3866 send_int_to_srv (CMD_PLIST_SET_SERIAL);
3867 send_int_to_srv (plist_get_serial(&plist));
3868 }
3869
3870 send_int_to_srv (CMD_PLAY);
3871 send_str_to_srv ("");
3872
3873 plist_free (&plist);
3874 }
3875
3876 /* Request tags from the server, wait until they arrive and return them
3877 * (malloc()ed). This function assumes that the interface is not initialized. */
get_tags_no_iface(const char * file,const int tags_sel)3878 static struct file_tags *get_tags_no_iface (const char *file,
3879 const int tags_sel)
3880 {
3881 struct file_tags *tags = NULL;
3882
3883 assert (file_type(file) == F_SOUND);
3884
3885 send_tags_request (file, tags_sel);
3886
3887 while (!tags) {
3888 int type = get_int_from_srv ();
3889 void *data = get_event_data (type);
3890
3891 if (type == EV_FILE_TAGS) {
3892 struct tag_ev_response *ev
3893 = (struct tag_ev_response *)data;
3894
3895 if (!strcmp(ev->file, file))
3896 tags = tags_dup (ev->tags);
3897
3898 free_tag_ev_data (ev);
3899 }
3900 else {
3901 /* We can't handle other events, since this function
3902 * is to be invoked without the interface. */
3903 logit ("Server sent an event which I didn't expect!");
3904 abort ();
3905 }
3906 }
3907
3908 return tags;
3909 }
3910
interface_cmdline_file_info(const int server_sock)3911 void interface_cmdline_file_info (const int server_sock)
3912 {
3913 srv_sock = server_sock; /* the interface is not initialized, so set it
3914 here */
3915 init_playlists ();
3916 file_info_reset (&curr_file);
3917 file_info_block_init (&curr_file);
3918
3919 curr_file.state = get_state ();
3920
3921 if (curr_file.state == STATE_STOP)
3922 puts ("State: STOP");
3923 else {
3924 int left;
3925 char curr_time_str[6];
3926 char time_left_str[6];
3927 char time_str[6];
3928 char *title;
3929
3930 if (curr_file.state == STATE_PLAY)
3931 puts ("State: PLAY");
3932 else if (curr_file.state == STATE_PAUSE)
3933 puts ("State: PAUSE");
3934
3935 curr_file.file = get_curr_file ();
3936
3937 if (curr_file.file[0]) {
3938
3939 /* get tags */
3940 if (file_type(curr_file.file) == F_URL) {
3941 send_int_to_srv (CMD_GET_TAGS);
3942 curr_file.tags = get_data_tags ();
3943 }
3944 else
3945 curr_file.tags = get_tags_no_iface (
3946 curr_file.file,
3947 TAGS_COMMENTS | TAGS_TIME);
3948
3949 /* get the title */
3950 if (curr_file.tags->title)
3951 title = build_title (curr_file.tags);
3952 else
3953 title = xstrdup ("");
3954 }
3955 else
3956 title = xstrdup ("");
3957
3958 curr_file.channels = get_channels ();
3959 curr_file.rate = get_rate ();
3960 curr_file.bitrate = get_bitrate ();
3961 curr_file.curr_time = get_curr_time ();
3962 curr_file.avg_bitrate = get_avg_bitrate ();
3963
3964 if (curr_file.tags->time != -1)
3965 sec_to_min (time_str, curr_file.tags->time);
3966 else
3967 time_str[0] = 0;
3968
3969 if (curr_file.curr_time != -1) {
3970 sec_to_min (curr_time_str, curr_file.curr_time);
3971
3972 if (curr_file.tags->time != -1) {
3973 sec_to_min (curr_time_str, curr_file.curr_time);
3974 left = curr_file.tags->time -
3975 curr_file.curr_time;
3976 sec_to_min (time_left_str, MAX(left, 0));
3977 }
3978 }
3979 else {
3980 strcpy (curr_time_str, "00:00");
3981 time_left_str[0] = 0;
3982 }
3983
3984 printf ("File: %s\n", curr_file.file);
3985 printf ("Title: %s\n", title);
3986
3987 if (curr_file.tags) {
3988 printf ("Artist: %s\n",
3989 curr_file.tags->artist
3990 ? curr_file.tags->artist : "");
3991 printf ("SongTitle: %s\n",
3992 curr_file.tags->title
3993 ? curr_file.tags->title : "");
3994 printf ("Album: %s\n",
3995 curr_file.tags->album
3996 ? curr_file.tags->album : "");
3997 }
3998
3999 if (curr_file.tags->time != -1) {
4000 printf ("TotalTime: %s\n", time_str);
4001 printf ("TimeLeft: %s\n", time_left_str);
4002 printf ("TotalSec: %d\n", curr_file.tags->time);
4003 }
4004
4005 printf ("CurrentTime: %s\n", curr_time_str);
4006 printf ("CurrentSec: %d\n", curr_file.curr_time);
4007
4008 printf ("Bitrate: %dkbps\n", MAX(curr_file.bitrate, 0));
4009 printf ("AvgBitrate: %dkbps\n", MAX(curr_file.avg_bitrate, 0));
4010 printf ("Rate: %dkHz\n", curr_file.rate);
4011
4012 file_info_cleanup (&curr_file);
4013 free (title);
4014 }
4015
4016 plist_free (dir_plist);
4017 plist_free (playlist);
4018 plist_free (queue);
4019 }
4020
interface_cmdline_enqueue(int server_sock,lists_t_strs * args)4021 void interface_cmdline_enqueue (int server_sock, lists_t_strs *args)
4022 {
4023 int ix;
4024
4025 /* the interface is not initialized, so set it here */
4026 srv_sock = server_sock;
4027
4028 if (!getcwd (cwd, sizeof (cwd)))
4029 fatal ("Can't get CWD: %s", strerror(errno));
4030
4031 for (ix = 0; ix < lists_strs_size (args); ix += 1) {
4032 const char *arg;
4033
4034 arg = lists_strs_at (args, ix);
4035 if (is_sound_file (arg) || is_url (arg)) {
4036 char *path = absolute_path (arg, cwd);
4037 send_int_to_srv (CMD_QUEUE_ADD);
4038 send_str_to_srv (path);
4039 free (path);
4040 }
4041 }
4042 }
4043
interface_cmdline_playit(int server_sock,lists_t_strs * args)4044 void interface_cmdline_playit (int server_sock, lists_t_strs *args)
4045 {
4046 struct plist plist;
4047 int ix;
4048
4049 srv_sock = server_sock; /* the interface is not initialized, so set it
4050 here */
4051
4052 if (!getcwd(cwd, sizeof(cwd)))
4053 fatal ("Can't get CWD: %s", strerror(errno));
4054
4055 plist_init (&plist);
4056
4057 for (ix = 0; ix < lists_strs_size (args); ix += 1) {
4058 const char *arg;
4059
4060 arg = lists_strs_at (args, ix);
4061 if (is_url(arg) || is_sound_file(arg)) {
4062 char *path = absolute_path (arg, cwd);
4063 plist_add (&plist, path);
4064 free (path);
4065 }
4066 }
4067
4068 if (plist_count(&plist)) {
4069 int serial;
4070
4071 send_int_to_srv (CMD_LOCK);
4072
4073 send_playlist (&plist, 1);
4074
4075 send_int_to_srv (CMD_GET_SERIAL);
4076 serial = get_data_int ();
4077 send_int_to_srv (CMD_PLIST_SET_SERIAL);
4078 send_int_to_srv (serial);
4079
4080 send_int_to_srv (CMD_UNLOCK);
4081
4082 send_int_to_srv (CMD_PLAY);
4083 send_str_to_srv ("");
4084 }
4085 else
4086 fatal ("No files added - no sound files on command line!");
4087
4088 plist_free (&plist);
4089 }
4090
interface_cmdline_seek_by(int server_sock,const int seek_by)4091 void interface_cmdline_seek_by (int server_sock, const int seek_by)
4092 {
4093 srv_sock = server_sock; /* the interface is not initialized, so set it
4094 here */
4095 seek (seek_by);
4096 }
4097
interface_cmdline_jump_to(int server_sock,const int pos)4098 void interface_cmdline_jump_to (int server_sock, const int pos)
4099 {
4100 srv_sock = server_sock; /* the interface is not initialized, so set it here */
4101 jump_to (pos);
4102 }
4103
interface_cmdline_jump_to_percent(int server_sock,const int percent)4104 void interface_cmdline_jump_to_percent (int server_sock, const int percent)
4105 {
4106 srv_sock = server_sock; /* the interface is not initialized, so set it here */
4107 curr_file.file = get_curr_file ();
4108 int new_pos;
4109
4110 if (percent >= 100) {
4111 fprintf (stderr, "Can't jump beyond the end of file.\n");
4112 return;
4113 }
4114
4115 if (!curr_file.file[0]) {
4116 fprintf (stderr, "Nothing is played.\n");
4117 return;
4118 }
4119
4120 if (file_type(curr_file.file) == F_URL) {
4121 fprintf (stderr, "Can't seek in network stream.\n");
4122 return;
4123 }
4124
4125 curr_file.tags = get_tags_no_iface (curr_file.file,TAGS_TIME);
4126 new_pos = (percent*curr_file.tags->time)/100;
4127 printf("Jumping to: %ds. Total time is: %ds\n", new_pos, curr_file.tags->time);
4128 jump_to (new_pos);
4129 }
4130
interface_cmdline_adj_volume(int server_sock,const char * arg)4131 void interface_cmdline_adj_volume (int server_sock, const char *arg)
4132 {
4133 srv_sock = server_sock;
4134
4135 if(arg[0] == '+')
4136 adjust_mixer(atoi(arg + 1));
4137 else if(arg[0] == '-')
4138 adjust_mixer(atoi(arg)); /* atoi can handle - */
4139 else if (arg[0] != 0)
4140 set_mixer(atoi(arg));
4141 }
4142
interface_cmdline_set(int server_sock,char * arg,const int val)4143 void interface_cmdline_set (int server_sock, char *arg, const int val)
4144 {
4145 srv_sock = server_sock;
4146 char *last = NULL;
4147 char *tok;
4148
4149 tok = strtok_r(arg, ",", &last);
4150
4151 while(tok) {
4152
4153 if(!strncmp (tok, "shuffle", 8) || !strncmp (tok,"s",2))
4154 tok = "Shuffle";
4155 else if(!strncmp (tok, "autonext", 9) || !strncmp (tok, "n",2))
4156 tok = "AutoNext";
4157 else if(!strncmp (tok, "repeat", 7) || !strncmp (tok, "r", 2))
4158 tok = "Repeat";
4159 else {
4160 fprintf (stderr, "Unknown option '%s'\n", tok);
4161 break;
4162 }
4163
4164 if(val == 2) {
4165 send_int_to_srv (CMD_GET_OPTION);
4166 send_str_to_srv (tok);
4167 options_set_int (tok, get_data_int());
4168 }
4169
4170 send_int_to_srv (CMD_SET_OPTION);
4171 send_str_to_srv (tok);
4172
4173 if(val == 2)
4174 send_int_to_srv (!options_get_int(tok));
4175 else
4176 send_int_to_srv (val);
4177
4178 tok = strtok_r (NULL, ",", &last);
4179 }
4180 }
4181
4182 /* Print formatted info
4183 State %state
4184 File %file
4185 Title %title
4186 Artist %artist
4187 SongTitle %song
4188 Album %album
4189 TotalTime %tt
4190 TimeLeft %tl
4191 TotalSec %ts
4192 CurrentTime %ct
4193 CurrentSec %cs
4194 Bitrate %b
4195 Rate %r
4196 */
interface_cmdline_formatted_info(const int server_sock,const char * format_str)4197 void interface_cmdline_formatted_info (const int server_sock,
4198 const char *format_str)
4199 {
4200 typedef struct {
4201 char *state;
4202 char *file;
4203 char *title;
4204 char *artist;
4205 char *song;
4206 char *album;
4207 char *totaltime;
4208 char *timeleft;
4209 char *totalsec;
4210 char *currenttime;
4211 char *currentsec;
4212 char *bitrate;
4213 char *rate;
4214 } info_t;
4215
4216 char curr_time_str[6];
4217 char time_left_str[6];
4218 char time_str[6];
4219 char time_sec_str[5];
4220 char curr_time_sec_str[5];
4221 char file_bitrate_str[4];
4222 char file_rate_str[3];
4223
4224 char *fmt, *str;
4225 info_t str_info;
4226
4227 srv_sock = server_sock; /* the interface is not initialized, so set it
4228 here */
4229 init_playlists ();
4230 file_info_reset (&curr_file);
4231 file_info_block_init (&curr_file);
4232
4233 curr_file.state = get_state ();
4234
4235 /* extra paranoid about struct data */
4236 memset(&str_info, 0, sizeof(str_info));
4237 curr_time_str[0] = time_left_str[0] = time_str[0] =
4238 time_sec_str[0] = curr_time_sec_str[0] =
4239 file_bitrate_str[0] = file_rate_str[0] = '\0';
4240
4241 str_info.currenttime = curr_time_str;
4242 str_info.timeleft = time_left_str;
4243 str_info.totaltime = time_str;
4244 str_info.totalsec = time_sec_str;
4245 str_info.currentsec = curr_time_sec_str;
4246 str_info.bitrate = file_bitrate_str;
4247 str_info.rate = file_rate_str;
4248
4249 if (curr_file.state == STATE_STOP)
4250 str_info.state = "STOP";
4251 else {
4252 int left;
4253
4254 if (curr_file.state == STATE_PLAY)
4255 str_info.state = "PLAY";
4256 else if (curr_file.state == STATE_PAUSE)
4257 str_info.state = "PAUSE";
4258
4259 curr_file.file = get_curr_file ();
4260
4261 if (curr_file.file[0]) {
4262
4263 /* get tags */
4264 if (file_type(curr_file.file) == F_URL) {
4265 send_int_to_srv (CMD_GET_TAGS);
4266 curr_file.tags = get_data_tags ();
4267 }
4268 else
4269 curr_file.tags = get_tags_no_iface (
4270 curr_file.file,
4271 TAGS_COMMENTS | TAGS_TIME);
4272
4273 /* get the title */
4274 if (curr_file.tags->title)
4275 str_info.title = build_title (curr_file.tags);
4276 else
4277 str_info.title = xstrdup ("");
4278 }
4279 else
4280 str_info.title = xstrdup ("");
4281
4282 curr_file.channels = get_channels ();
4283 curr_file.rate = get_rate ();
4284 curr_file.bitrate = get_bitrate ();
4285 curr_file.curr_time = get_curr_time ();
4286
4287 if (curr_file.tags->time != -1)
4288 sec_to_min (time_str, curr_file.tags->time);
4289 else
4290 time_str[0] = 0;
4291
4292 if (curr_file.curr_time != -1) {
4293 sec_to_min (curr_time_str, curr_file.curr_time);
4294
4295 if (curr_file.tags->time != -1) {
4296 sec_to_min (curr_time_str, curr_file.curr_time);
4297 left = curr_file.tags->time -
4298 curr_file.curr_time;
4299 sec_to_min (time_left_str, MAX(left, 0));
4300 }
4301 }
4302 else {
4303 strcpy (curr_time_str, "00:00");
4304 time_left_str[0] = 0;
4305 }
4306
4307 str_info.file = curr_file.file;
4308
4309 str_info.artist =
4310 curr_file.tags->artist ? curr_file.tags->artist : NULL;
4311 str_info.song = curr_file.tags->title ? curr_file.tags->title : NULL;
4312 str_info.album = curr_file.tags->album ? curr_file.tags->album : NULL;
4313
4314 if (curr_file.tags->time != -1)
4315 snprintf(time_sec_str, 5, "%d", curr_file.tags->time);
4316
4317 snprintf(curr_time_sec_str, 5, "%d", curr_file.curr_time);
4318 snprintf(file_bitrate_str, 4, "%d", MAX(curr_file.bitrate, 0));
4319 snprintf(file_rate_str, 3, "%d", curr_file.rate);
4320 }
4321
4322 /* string with formatting tags */
4323 fmt = xstrdup(format_str);
4324
4325 fmt = str_repl(fmt, "%state", str_info.state);
4326 fmt = str_repl(fmt, "%file",
4327 str_info.file ? str_info.file : "");
4328 fmt = str_repl(fmt, "%title",
4329 str_info.title ? str_info.title : "");
4330 fmt = str_repl(fmt, "%artist",
4331 str_info.artist ? str_info.artist : "");
4332 fmt = str_repl(fmt, "%song",
4333 str_info.song ? str_info.song : "");
4334 fmt = str_repl(fmt, "%album",
4335 str_info.album ? str_info.album : "");
4336 fmt = str_repl(fmt, "%tt",
4337 str_info.totaltime ? str_info.totaltime : "");
4338 fmt = str_repl(fmt, "%tl",
4339 str_info.timeleft ? str_info.timeleft : "");
4340 fmt = str_repl(fmt, "%ts",
4341 str_info.totalsec ? str_info.totalsec : "");
4342 fmt = str_repl(fmt, "%ct",
4343 str_info.currenttime ? str_info.currenttime : "");
4344 fmt = str_repl(fmt, "%cs",
4345 str_info.currentsec ? str_info.currentsec : "");
4346 fmt = str_repl(fmt, "%b",
4347 str_info.bitrate ? str_info.bitrate : "");
4348 fmt = str_repl(fmt, "%r",
4349 str_info.rate ? str_info.rate : "");
4350 fmt = str_repl(fmt, "\\n", "\n");
4351
4352 str = build_title_with_format (curr_file.tags, fmt);
4353
4354 printf("%s\n", str);
4355 free(str);
4356 free(fmt);
4357
4358 if (str_info.title)
4359 free(str_info.title);
4360
4361 if (curr_file.state != STATE_STOP)
4362 file_info_cleanup (&curr_file);
4363
4364 plist_free (dir_plist);
4365 plist_free (playlist);
4366 plist_free (queue);
4367
4368 }
4369