1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <stdarg.h>
4 #include <string.h>
5 #include <time.h>
6 #include <glib.h>
7 #include <ncurses.h>
9 #include "libmpdclient.h"
10 #include "mpc.h"
11 #include "command.h"
12 #include "options.h"
13 #include "screen.h"
14 #include "screen_play.h"
15 #include "screen_file.h"
16 #include "screen_help.h"
17 #include "screen_search.h"
18 #include "screen_utils.h"
20 #define STATUS_MESSAGE_TIMEOUT 3
21 #define STATUS_LINE_MAX_SIZE 512
23 static screen_t *screen = NULL;
25 static void
26 switch_screen_mode(screen_mode_t new_mode, mpd_client_t *c)
27 {
28 if( new_mode == screen->mode )
29 return;
31 switch(screen->mode)
32 {
33 case SCREEN_PLAY_WINDOW:
34 play_close(screen, c);
35 break;
36 case SCREEN_FILE_WINDOW:
37 file_close(screen, c);
38 break;
39 case SCREEN_SEARCH_WINDOW:
40 search_close(screen, c);
41 break;
42 case SCREEN_HELP_WINDOW:
43 help_close(screen, c);
44 break;
45 }
47 screen->mode = new_mode;
48 screen->painted = 0;
50 switch(screen->mode)
51 {
52 case SCREEN_PLAY_WINDOW:
53 play_open(screen, c);
54 break;
55 case SCREEN_FILE_WINDOW:
56 file_open(screen, c);
57 break;
58 case SCREEN_SEARCH_WINDOW:
59 search_open(screen, c);
60 break;
61 case SCREEN_HELP_WINDOW:
62 help_open(screen, c);
63 break;
64 }
65 }
67 static void
68 paint_top_window(char *header, int volume, int clear)
69 {
70 static int prev_volume = -1;
71 static int prev_header_len = -1;
72 WINDOW *w = screen->top_window.w;
74 if(prev_header_len!=strlen(header))
75 {
76 prev_header_len = strlen(header);
77 clear = 1;
78 }
80 if(clear)
81 {
82 wclear(w);
83 }
85 if(prev_volume!=volume || clear)
86 {
87 char buf[12];
89 wattron(w, A_BOLD);
90 mvwaddstr(w, 0, 0, header);
91 wattroff(w, A_BOLD);
92 if( volume==MPD_STATUS_NO_VOLUME )
93 {
94 snprintf(buf, 12, "Volume n/a ");
95 }
96 else
97 {
98 snprintf(buf, 12, "Volume %3d%%", volume);
99 }
100 mvwaddstr(w, 0, screen->top_window.cols-12, buf);
102 if( options.enable_colors )
103 wattron(w, LINE_COLORS);
104 mvwhline(w, 1, 0, ACS_HLINE, screen->top_window.cols);
105 if( options.enable_colors )
106 wattroff(w, LINE_COLORS);
108 wnoutrefresh(w);
109 }
110 }
112 static void
113 paint_progress_window(mpd_client_t *c)
114 {
115 double p;
116 int width;
118 if( c->status==NULL || !IS_PLAYING(c->status->state) )
119 {
120 mvwhline(screen->progress_window.w, 0, 0, ACS_HLINE,
121 screen->progress_window.cols);
122 wnoutrefresh(screen->progress_window.w);
123 return;
124 }
126 p = ((double) c->status->elapsedTime) / ((double) c->status->totalTime);
128 width = (int) (p * (double) screen->progress_window.cols);
129 mvwhline(screen->progress_window.w,
130 0, 0,
131 ACS_HLINE,
132 screen->progress_window.cols);
133 whline(screen->progress_window.w, '=', width-1);
134 mvwaddch(screen->progress_window.w, 0, width-1, 'O');
135 wnoutrefresh(screen->progress_window.w);
136 }
138 static void
139 paint_status_window(mpd_client_t *c)
140 {
141 WINDOW *w = screen->status_window.w;
142 mpd_Status *status = c->status;
143 mpd_Song *song = c->song;
144 int x = 0;
146 if( time(NULL) - screen->status_timestamp <= STATUS_MESSAGE_TIMEOUT )
147 return;
149 wmove(w, 0, 0);
150 wclrtoeol(w);
152 switch(status->state)
153 {
154 case MPD_STATUS_STATE_STOP:
155 wattron(w, A_BOLD);
156 waddstr(w, "Stopped! ");
157 wattroff(w, A_BOLD);
158 break;
159 case MPD_STATUS_STATE_PLAY:
160 wattron(w, A_BOLD);
161 waddstr(w, "Playing:");
162 wattroff(w, A_BOLD);
163 break;
164 case MPD_STATUS_STATE_PAUSE:
165 wattron(w, A_BOLD);
166 waddstr(w, "[Paused]");
167 wattroff(w, A_BOLD);
168 break;
169 default:
170 my_waddstr(w,
171 "Warning: Music Player Daemon in unknown state!",
172 ALERT_COLORS);
173 break;
174 }
175 x += 10;
177 if( (IS_PLAYING(status->state) || IS_PAUSED(status->state)) && song )
178 {
179 mvwaddstr(w, 0, x, mpc_get_song_name(song));
180 }
183 /* time */
184 if( IS_PLAYING(status->state) || IS_PAUSED(status->state) )
185 {
186 x = screen->status_window.cols - strlen(screen->buf);
188 snprintf(screen->buf, screen->buf_size,
189 " [%i:%02i/%i:%02i] ",
190 status->elapsedTime/60, status->elapsedTime%60,
191 status->totalTime/60, status->totalTime%60 );
192 mvwaddstr(w, 0, x, screen->buf);
194 }
195 #if 0
196 else if( c->status->state == MPD_STATUS_STATE_STOP )
197 {
198 time_t timep;
199 x = screen->status_window.cols - strlen(screen->buf);
201 time(&timep);
202 //strftime(screen->buf, screen->buf_size, "%X ", localtime(&timep));
203 strftime(screen->buf, screen->buf_size, "%R ", localtime(&timep));
204 mvwaddstr(w, 0, x, screen->buf);
205 }
206 #endif
209 wnoutrefresh(w);
210 }
214 int
215 screen_exit(void)
216 {
217 endwin();
218 if( screen )
219 {
220 screen->playlist = list_window_free(screen->playlist);
221 screen->filelist = list_window_free(screen->filelist);
222 screen->helplist = list_window_free(screen->helplist);
223 g_free(screen->buf);
224 g_free(screen->findbuf);
225 g_free(screen);
226 screen = NULL;
227 }
228 return 0;
229 }
231 void
232 screen_resized(int sig)
233 {
234 screen_exit();
235 if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
236 {
237 fprintf(stderr, "Error: Screen to small!\n");
238 exit(EXIT_FAILURE);
239 }
240 screen_init();
241 }
243 void
244 screen_status_message(char *msg)
245 {
246 WINDOW *w = screen->status_window.w;
248 wmove(w, 0, 0);
249 wclrtoeol(w);
250 wattron(w, A_BOLD);
251 my_waddstr(w, msg, ALERT_COLORS);
252 wattroff(w, A_BOLD);
253 wnoutrefresh(w);
254 screen->status_timestamp = time(NULL);
255 }
257 void
258 screen_status_printf(char *format, ...)
259 {
260 char buffer[STATUS_LINE_MAX_SIZE];
261 va_list ap;
263 va_start(ap,format);
264 vsnprintf(buffer,sizeof(buffer),format,ap);
265 va_end(ap);
266 screen_status_message(buffer);
267 }
269 int
270 screen_init(void)
271 {
272 /* initialize the curses library */
273 initscr();
274 if( has_colors() )
275 {
276 start_color();
277 if( options.enable_colors )
278 {
279 init_pair(1, options.title_color, options.bg_color);
280 init_pair(2, options.line_color, options.bg_color);
281 init_pair(3, options.list_color, options.bg_color);
282 init_pair(4, options.progress_color, options.bg_color);
283 init_pair(5, options.status_color, options.bg_color);
284 init_pair(6, options.alert_color, options.bg_color);
285 }
286 else
287 use_default_colors();
288 }
289 else if( options.enable_colors )
290 {
291 fprintf(stderr, "Terminal lacks color capabilities.\n");
292 options.enable_colors = 0;
293 }
295 /* tell curses not to do NL->CR/NL on output */
296 nonl();
297 /* take input chars one at a time, no wait for \n */
298 cbreak();
299 /* don't echo input */
300 noecho();
301 /* set cursor invisible */
302 curs_set(0);
303 /* return from getch() without blocking */
304 // nodelay(stdscr, TRUE);
305 keypad(stdscr, TRUE);
306 timeout(SCREEN_TIMEOUT);
308 if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
309 {
310 fprintf(stderr, "Error: Screen to small!\n");
311 exit(EXIT_FAILURE);
312 }
314 screen = g_malloc(sizeof(screen_t));
315 memset(screen, 0, sizeof(screen_t));
316 screen->mode = SCREEN_PLAY_WINDOW;
317 screen->cols = COLS;
318 screen->rows = LINES;
319 screen->buf = g_malloc(screen->cols);
320 screen->buf_size = screen->cols;
321 screen->findbuf = NULL;
322 screen->painted = 0;
324 /* create top window */
325 screen->top_window.rows = 2;
326 screen->top_window.cols = screen->cols;
327 screen->top_window.w = newwin(screen->top_window.rows,
328 screen->top_window.cols,
329 0, 0);
330 leaveok(screen->top_window.w, TRUE);
331 keypad(screen->top_window.w, TRUE);
333 /* create main window */
334 screen->main_window.rows = screen->rows-4;
335 screen->main_window.cols = screen->cols;
336 screen->main_window.w = newwin(screen->main_window.rows,
337 screen->main_window.cols,
338 2,
339 0);
340 screen->playlist = list_window_init( screen->main_window.w,
341 screen->main_window.cols,
342 screen->main_window.rows );
343 screen->filelist = list_window_init( screen->main_window.w,
344 screen->main_window.cols,
345 screen->main_window.rows );
346 screen->helplist = list_window_init( screen->main_window.w,
347 screen->main_window.cols,
348 screen->main_window.rows );
350 // leaveok(screen->main_window.w, TRUE); temporary disabled
351 keypad(screen->main_window.w, TRUE);
353 /* create progress window */
354 screen->progress_window.rows = 1;
355 screen->progress_window.cols = screen->cols;
356 screen->progress_window.w = newwin(screen->progress_window.rows,
357 screen->progress_window.cols,
358 screen->rows-2,
359 0);
360 leaveok(screen->progress_window.w, TRUE);
362 /* create status window */
363 screen->status_window.rows = 1;
364 screen->status_window.cols = screen->cols;
365 screen->status_window.w = newwin(screen->status_window.rows,
366 screen->status_window.cols,
367 screen->rows-1,
368 0);
370 leaveok(screen->status_window.w, FALSE);
371 keypad(screen->status_window.w, TRUE);
373 if( options.enable_colors )
374 {
375 /* set background attributes */
376 wbkgd(screen->main_window.w, LIST_COLORS);
377 wbkgd(screen->top_window.w, TITLE_COLORS);
378 wbkgd(screen->progress_window.w, PROGRESS_COLORS);
379 wbkgd(screen->status_window.w, STATUS_COLORS);
380 }
382 return 0;
383 }
385 void
386 screen_paint(mpd_client_t *c)
387 {
388 switch(screen->mode)
389 {
390 case SCREEN_PLAY_WINDOW:
391 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1);
392 play_paint(screen, c);
393 break;
394 case SCREEN_FILE_WINDOW:
395 paint_top_window(file_get_header(c), c->status->volume, 1);
396 file_paint(screen, c);
397 break;
398 case SCREEN_SEARCH_WINDOW:
399 paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 1);
400 search_paint(screen, c);
401 break;
402 case SCREEN_HELP_WINDOW:
403 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1);
404 help_paint(screen, c);
405 break;
406 }
408 paint_progress_window(c);
409 paint_status_window(c);
410 screen->painted = 1;
411 wmove(screen->main_window.w, 0, 0); wnoutrefresh(screen->main_window.w);
412 doupdate();
413 }
415 void
416 screen_update(mpd_client_t *c)
417 {
418 static int repeat = -1;
419 static int random = -1;
420 list_window_t *lw = NULL;
422 if( !screen->painted )
423 return screen_paint(c);
425 if( repeat<0 )
426 {
427 repeat = c->status->repeat;
428 random = c->status->random;
429 }
430 if( repeat != c->status->repeat )
431 screen_status_printf("Repeat is %s",
432 c->status->repeat ? "On" : "Off");
433 if( random != c->status->random )
434 screen_status_printf("Random is %s",
435 c->status->random ? "On" : "Off");
437 repeat = c->status->repeat;
438 random = c->status->random;
440 switch(screen->mode)
441 {
442 case SCREEN_PLAY_WINDOW:
443 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 0);
444 play_update(screen, c);
445 lw = screen->playlist;
446 break;
447 case SCREEN_FILE_WINDOW:
448 paint_top_window(file_get_header(c), c->status->volume, 0);
449 file_update(screen, c);
450 lw = screen->filelist;
451 break;
452 case SCREEN_SEARCH_WINDOW:
453 paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 0);
454 search_update(screen, c);
455 break;
456 case SCREEN_HELP_WINDOW:
457 paint_top_window(TOP_HEADER_HELP, c->status->volume, 0);
458 help_update(screen, c);
459 lw = screen->helplist;
460 break;
461 }
462 paint_progress_window(c);
463 paint_status_window(c);
464 wmove(screen->main_window.w, LW_ROW(lw), 0);
465 wnoutrefresh(screen->main_window.w);
466 doupdate();
467 }
469 void
470 screen_cmd(mpd_client_t *c, command_t cmd)
471 {
472 int n;
473 screen_mode_t new_mode = screen->mode;
475 switch(screen->mode)
476 {
477 case SCREEN_PLAY_WINDOW:
478 if( play_cmd(screen, c, cmd) )
479 return;
480 break;
481 case SCREEN_FILE_WINDOW:
482 if( file_cmd(screen, c, cmd) )
483 return;
484 break;
485 case SCREEN_SEARCH_WINDOW:
486 if( search_cmd(screen, c, cmd) )
487 return;
488 break;
489 case SCREEN_HELP_WINDOW:
490 if( help_cmd(screen, c, cmd) )
491 return;
492 break;
493 }
495 switch(cmd)
496 {
497 case CMD_PLAY:
498 mpd_sendPlayCommand(c->connection, screen->playlist->selected);
499 mpd_finishCommand(c->connection);
500 break;
501 case CMD_PAUSE:
502 mpd_sendPauseCommand(c->connection);
503 mpd_finishCommand(c->connection);
504 break;
505 case CMD_STOP:
506 mpd_sendStopCommand(c->connection);
507 mpd_finishCommand(c->connection);
508 break;
509 case CMD_TRACK_NEXT:
510 if( IS_PLAYING(c->status->state) )
511 {
512 mpd_sendNextCommand(c->connection);
513 mpd_finishCommand(c->connection);
514 }
515 break;
516 case CMD_TRACK_PREVIOUS:
517 if( IS_PLAYING(c->status->state) )
518 {
519 mpd_sendPrevCommand(c->connection);
520 mpd_finishCommand(c->connection);
521 }
522 break;
523 case CMD_SHUFFLE:
524 mpd_sendShuffleCommand(c->connection);
525 mpd_finishCommand(c->connection);
526 screen_status_message("Shuffled playlist!");
527 break;
528 case CMD_CLEAR:
529 mpd_sendClearCommand(c->connection);
530 mpd_finishCommand(c->connection);
531 file_clear_highlights(c);
532 screen_status_message("Cleared playlist!");
533 break;
534 case CMD_REPEAT:
535 n = !c->status->repeat;
536 mpd_sendRepeatCommand(c->connection, n);
537 mpd_finishCommand(c->connection);
538 break;
539 case CMD_RANDOM:
540 n = !c->status->random;
541 mpd_sendRandomCommand(c->connection, n);
542 mpd_finishCommand(c->connection);
543 break;
544 case CMD_VOLUME_UP:
545 if( c->status->volume!=MPD_STATUS_NO_VOLUME && c->status->volume<100 )
546 {
547 c->status->volume=c->status->volume+1;
548 mpd_sendSetvolCommand(c->connection, c->status->volume );
549 mpd_finishCommand(c->connection);
550 screen_status_printf("Volume %d%%", c->status->volume);
551 }
552 break;
553 case CMD_VOLUME_DOWN:
554 if( c->status->volume!=MPD_STATUS_NO_VOLUME && c->status->volume>0 )
555 {
556 c->status->volume=c->status->volume-1;
557 mpd_sendSetvolCommand(c->connection, c->status->volume );
558 mpd_finishCommand(c->connection);
559 screen_status_printf("Volume %d%%", c->status->volume);
560 }
561 break;
562 case CMD_TOGGLE_FIND_WRAP:
563 options.find_wrap = !options.find_wrap;
564 screen_status_printf("Find mode: %s",
565 options.find_wrap ? "Wrapped" : "Normal");
566 break;
567 case CMD_SCREEN_PREVIOUS:
568 if( screen->mode > SCREEN_PLAY_WINDOW )
569 new_mode = screen->mode - 1;
570 else
571 new_mode = SCREEN_HELP_WINDOW-1;
572 switch_screen_mode(new_mode, c);
573 break;
574 case CMD_SCREEN_NEXT:
575 new_mode = screen->mode + 1;
576 if( new_mode >= SCREEN_HELP_WINDOW )
577 new_mode = SCREEN_PLAY_WINDOW;
578 switch_screen_mode(new_mode, c);
579 break;
580 case CMD_SCREEN_PLAY:
581 switch_screen_mode(SCREEN_PLAY_WINDOW, c);
582 break;
583 case CMD_SCREEN_FILE:
584 switch_screen_mode(SCREEN_FILE_WINDOW, c);
585 break;
586 case CMD_SCREEN_SEARCH:
587 switch_screen_mode(SCREEN_SEARCH_WINDOW, c);
588 break;
589 case CMD_SCREEN_HELP:
590 switch_screen_mode(SCREEN_HELP_WINDOW, c);
591 break;
592 case CMD_QUIT:
593 exit(EXIT_SUCCESS);
594 default:
595 break;
596 }
598 }