efd93d1fec9e76f903b3229626bb3aa9a9c700f7
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 wmove(w, 0, 0);
83 wclrtoeol(w);
84 }
86 if(prev_volume!=volume || clear)
87 {
88 char buf[12];
90 wattron(w, A_BOLD);
91 mvwaddstr(w, 0, 0, header);
92 wattroff(w, A_BOLD);
93 if( volume==MPD_STATUS_NO_VOLUME )
94 {
95 snprintf(buf, 12, "Volume n/a ");
96 }
97 else
98 {
99 snprintf(buf, 12, "Volume %3d%%", volume);
100 }
101 mvwaddstr(w, 0, screen->top_window.cols-12, buf);
103 #if 1
104 if( options.enable_colors )
105 wattron(w, LINE_COLORS);
106 mvwhline(w, 1, 0, ACS_HLINE, screen->top_window.cols);
107 if( options.enable_colors )
108 wattroff(w, LINE_COLORS);
109 #else
110 if( options.enable_colors )
111 wattron(w, LINE_COLORS);
113 mvwhline(w, 1, 0, ACS_HLINE, screen->top_window.cols);
114 wmove(w,1,screen->top_window.cols-6);
115 waddstr(w, "[rzx]");
117 if( options.enable_colors )
118 wattroff(w, LINE_COLORS);
120 #endif
122 wnoutrefresh(w);
123 }
124 }
126 static void
127 paint_progress_window(mpd_client_t *c)
128 {
129 double p;
130 int width;
132 if( c->status==NULL || IS_STOPPED(c->status->state) )
133 {
134 mvwhline(screen->progress_window.w, 0, 0, ACS_HLINE,
135 screen->progress_window.cols);
136 wnoutrefresh(screen->progress_window.w);
137 return;
138 }
140 p = ((double) c->status->elapsedTime) / ((double) c->status->totalTime);
142 width = (int) (p * (double) screen->progress_window.cols);
143 mvwhline(screen->progress_window.w,
144 0, 0,
145 ACS_HLINE,
146 screen->progress_window.cols);
147 whline(screen->progress_window.w, '=', width-1);
148 mvwaddch(screen->progress_window.w, 0, width-1, 'O');
149 wnoutrefresh(screen->progress_window.w);
150 }
152 static void
153 paint_status_window(mpd_client_t *c)
154 {
155 WINDOW *w = screen->status_window.w;
156 mpd_Status *status = c->status;
157 mpd_Song *song = c->song;
158 int x = 0;
160 if( time(NULL) - screen->status_timestamp <= STATUS_MESSAGE_TIMEOUT )
161 return;
163 wmove(w, 0, 0);
164 wclrtoeol(w);
166 switch(status->state)
167 {
168 case MPD_STATUS_STATE_STOP:
169 wattron(w, A_BOLD);
170 waddstr(w, "Stopped! ");
171 wattroff(w, A_BOLD);
172 break;
173 case MPD_STATUS_STATE_PLAY:
174 wattron(w, A_BOLD);
175 waddstr(w, "Playing:");
176 wattroff(w, A_BOLD);
177 break;
178 case MPD_STATUS_STATE_PAUSE:
179 wattron(w, A_BOLD);
180 waddstr(w, "[Paused]");
181 wattroff(w, A_BOLD);
182 break;
183 default:
184 my_waddstr(w,
185 "Warning: Music Player Daemon in unknown state!",
186 ALERT_COLORS);
187 break;
188 }
189 x += 10;
191 if( (IS_PLAYING(status->state) || IS_PAUSED(status->state)) && song )
192 {
193 mvwaddstr(w, 0, x, mpc_get_song_name(song));
194 }
197 /* time */
198 if( IS_PLAYING(status->state) || IS_PAUSED(status->state) )
199 {
200 x = screen->status_window.cols - strlen(screen->buf);
202 snprintf(screen->buf, screen->buf_size,
203 " [%i:%02i/%i:%02i] ",
204 status->elapsedTime/60, status->elapsedTime%60,
205 status->totalTime/60, status->totalTime%60 );
206 mvwaddstr(w, 0, x, screen->buf);
208 }
209 #if 0
210 else if( c->status->state == MPD_STATUS_STATE_STOP )
211 {
212 time_t timep;
213 x = screen->status_window.cols - strlen(screen->buf);
215 time(&timep);
216 //strftime(screen->buf, screen->buf_size, "%X ", localtime(&timep));
217 strftime(screen->buf, screen->buf_size, "%R ", localtime(&timep));
218 mvwaddstr(w, 0, x, screen->buf);
219 }
220 #endif
223 wnoutrefresh(w);
224 }
228 int
229 screen_exit(void)
230 {
231 endwin();
232 if( screen )
233 {
234 screen->playlist = list_window_free(screen->playlist);
235 screen->filelist = list_window_free(screen->filelist);
236 screen->helplist = list_window_free(screen->helplist);
237 g_free(screen->buf);
238 g_free(screen->findbuf);
239 g_free(screen);
240 screen = NULL;
241 }
242 return 0;
243 }
245 void
246 screen_resized(int sig)
247 {
248 screen_exit();
249 if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
250 {
251 fprintf(stderr, "Error: Screen to small!\n");
252 exit(EXIT_FAILURE);
253 }
254 screen_init();
255 }
257 void
258 screen_status_message(char *msg)
259 {
260 WINDOW *w = screen->status_window.w;
262 wmove(w, 0, 0);
263 wclrtoeol(w);
264 wattron(w, A_BOLD);
265 my_waddstr(w, msg, ALERT_COLORS);
266 wattroff(w, A_BOLD);
267 wnoutrefresh(w);
268 screen->status_timestamp = time(NULL);
269 }
271 void
272 screen_status_printf(char *format, ...)
273 {
274 char buffer[STATUS_LINE_MAX_SIZE];
275 va_list ap;
277 va_start(ap,format);
278 vsnprintf(buffer,sizeof(buffer),format,ap);
279 va_end(ap);
280 screen_status_message(buffer);
281 }
283 int
284 screen_init(void)
285 {
286 /* initialize the curses library */
287 initscr();
288 if( has_colors() )
289 {
290 start_color();
291 if( options.enable_colors )
292 {
293 init_pair(1, options.title_color, options.bg_color);
294 init_pair(2, options.line_color, options.bg_color);
295 init_pair(3, options.list_color, options.bg_color);
296 init_pair(4, options.progress_color, options.bg_color);
297 init_pair(5, options.status_color, options.bg_color);
298 init_pair(6, options.alert_color, options.bg_color);
299 }
300 else
301 use_default_colors();
302 }
303 else if( options.enable_colors )
304 {
305 fprintf(stderr, "Terminal lacks color capabilities.\n");
306 options.enable_colors = 0;
307 }
309 /* tell curses not to do NL->CR/NL on output */
310 nonl();
311 /* take input chars one at a time, no wait for \n */
312 cbreak();
313 /* don't echo input */
314 noecho();
315 /* set cursor invisible */
316 curs_set(0);
317 /* return from getch() without blocking */
318 // nodelay(stdscr, TRUE);
319 keypad(stdscr, TRUE);
320 timeout(SCREEN_TIMEOUT);
322 if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
323 {
324 fprintf(stderr, "Error: Screen to small!\n");
325 exit(EXIT_FAILURE);
326 }
328 screen = g_malloc(sizeof(screen_t));
329 memset(screen, 0, sizeof(screen_t));
330 screen->mode = SCREEN_PLAY_WINDOW;
331 screen->cols = COLS;
332 screen->rows = LINES;
333 screen->buf = g_malloc(screen->cols);
334 screen->buf_size = screen->cols;
335 screen->findbuf = NULL;
336 screen->painted = 0;
337 screen->input_timestamp = time(NULL);
339 /* create top window */
340 screen->top_window.rows = 2;
341 screen->top_window.cols = screen->cols;
342 screen->top_window.w = newwin(screen->top_window.rows,
343 screen->top_window.cols,
344 0, 0);
345 leaveok(screen->top_window.w, TRUE);
346 keypad(screen->top_window.w, TRUE);
348 /* create main window */
349 screen->main_window.rows = screen->rows-4;
350 screen->main_window.cols = screen->cols;
351 screen->main_window.w = newwin(screen->main_window.rows,
352 screen->main_window.cols,
353 2,
354 0);
355 screen->playlist = list_window_init( screen->main_window.w,
356 screen->main_window.cols,
357 screen->main_window.rows );
358 screen->filelist = list_window_init( screen->main_window.w,
359 screen->main_window.cols,
360 screen->main_window.rows );
361 screen->helplist = list_window_init( screen->main_window.w,
362 screen->main_window.cols,
363 screen->main_window.rows );
365 // leaveok(screen->main_window.w, TRUE); temporary disabled
366 keypad(screen->main_window.w, TRUE);
368 /* create progress window */
369 screen->progress_window.rows = 1;
370 screen->progress_window.cols = screen->cols;
371 screen->progress_window.w = newwin(screen->progress_window.rows,
372 screen->progress_window.cols,
373 screen->rows-2,
374 0);
375 leaveok(screen->progress_window.w, TRUE);
377 /* create status window */
378 screen->status_window.rows = 1;
379 screen->status_window.cols = screen->cols;
380 screen->status_window.w = newwin(screen->status_window.rows,
381 screen->status_window.cols,
382 screen->rows-1,
383 0);
385 leaveok(screen->status_window.w, FALSE);
386 keypad(screen->status_window.w, TRUE);
388 if( options.enable_colors )
389 {
390 /* set background attributes */
391 wbkgd(screen->main_window.w, LIST_COLORS);
392 wbkgd(screen->top_window.w, TITLE_COLORS);
393 wbkgd(screen->progress_window.w, PROGRESS_COLORS);
394 wbkgd(screen->status_window.w, STATUS_COLORS);
395 }
397 return 0;
398 }
400 void
401 screen_paint(mpd_client_t *c)
402 {
403 switch(screen->mode)
404 {
405 case SCREEN_PLAY_WINDOW:
406 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1);
407 play_paint(screen, c);
408 break;
409 case SCREEN_FILE_WINDOW:
410 paint_top_window(file_get_header(c), c->status->volume, 1);
411 file_paint(screen, c);
412 break;
413 case SCREEN_SEARCH_WINDOW:
414 paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 1);
415 search_paint(screen, c);
416 break;
417 case SCREEN_HELP_WINDOW:
418 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1);
419 help_paint(screen, c);
420 break;
421 }
423 paint_progress_window(c);
424 paint_status_window(c);
425 screen->painted = 1;
426 wmove(screen->main_window.w, 0, 0); wnoutrefresh(screen->main_window.w);
427 doupdate();
428 }
430 void
431 screen_update(mpd_client_t *c)
432 {
433 static int repeat = -1;
434 static int random = -1;
435 list_window_t *lw = NULL;
437 if( !screen->painted )
438 return screen_paint(c);
440 if( repeat<0 )
441 {
442 repeat = c->status->repeat;
443 random = c->status->random;
444 }
445 if( repeat != c->status->repeat )
446 screen_status_printf("Repeat is %s",
447 c->status->repeat ? "On" : "Off");
448 if( random != c->status->random )
449 screen_status_printf("Random is %s",
450 c->status->random ? "On" : "Off");
452 repeat = c->status->repeat;
453 random = c->status->random;
455 switch(screen->mode)
456 {
457 case SCREEN_PLAY_WINDOW:
458 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 0);
459 play_update(screen, c);
460 lw = screen->playlist;
461 break;
462 case SCREEN_FILE_WINDOW:
463 paint_top_window(file_get_header(c), c->status->volume, 0);
464 file_update(screen, c);
465 lw = screen->filelist;
466 break;
467 case SCREEN_SEARCH_WINDOW:
468 paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 0);
469 search_update(screen, c);
470 break;
471 case SCREEN_HELP_WINDOW:
472 paint_top_window(TOP_HEADER_HELP, c->status->volume, 0);
473 help_update(screen, c);
474 lw = screen->helplist;
475 break;
476 }
477 paint_progress_window(c);
478 paint_status_window(c);
479 wmove(screen->main_window.w, LW_ROW(lw), 0);
480 wnoutrefresh(screen->main_window.w);
481 doupdate();
482 }
484 void
485 screen_cmd(mpd_client_t *c, command_t cmd)
486 {
487 int n;
488 screen_mode_t new_mode = screen->mode;
490 screen->input_timestamp = time(NULL);
491 switch(screen->mode)
492 {
493 case SCREEN_PLAY_WINDOW:
494 if( play_cmd(screen, c, cmd) )
495 return;
496 break;
497 case SCREEN_FILE_WINDOW:
498 if( file_cmd(screen, c, cmd) )
499 return;
500 break;
501 case SCREEN_SEARCH_WINDOW:
502 if( search_cmd(screen, c, cmd) )
503 return;
504 break;
505 case SCREEN_HELP_WINDOW:
506 if( help_cmd(screen, c, cmd) )
507 return;
508 break;
509 }
511 switch(cmd)
512 {
513 case CMD_PLAY:
514 mpd_sendPlayCommand(c->connection, screen->playlist->selected);
515 mpd_finishCommand(c->connection);
516 break;
517 case CMD_PAUSE:
518 mpd_sendPauseCommand(c->connection);
519 mpd_finishCommand(c->connection);
520 break;
521 case CMD_STOP:
522 mpd_sendStopCommand(c->connection);
523 mpd_finishCommand(c->connection);
524 break;
525 case CMD_TRACK_NEXT:
526 if( IS_PLAYING(c->status->state) )
527 {
528 mpd_sendNextCommand(c->connection);
529 mpd_finishCommand(c->connection);
530 }
531 break;
532 case CMD_TRACK_PREVIOUS:
533 if( IS_PLAYING(c->status->state) )
534 {
535 mpd_sendPrevCommand(c->connection);
536 mpd_finishCommand(c->connection);
537 }
538 break;
539 case CMD_SHUFFLE:
540 mpd_sendShuffleCommand(c->connection);
541 mpd_finishCommand(c->connection);
542 screen_status_message("Shuffled playlist!");
543 break;
544 case CMD_CLEAR:
545 mpd_sendClearCommand(c->connection);
546 mpd_finishCommand(c->connection);
547 file_clear_highlights(c);
548 screen_status_message("Cleared playlist!");
549 break;
550 case CMD_REPEAT:
551 n = !c->status->repeat;
552 mpd_sendRepeatCommand(c->connection, n);
553 mpd_finishCommand(c->connection);
554 break;
555 case CMD_RANDOM:
556 n = !c->status->random;
557 mpd_sendRandomCommand(c->connection, n);
558 mpd_finishCommand(c->connection);
559 break;
560 case CMD_VOLUME_UP:
561 if( c->status->volume!=MPD_STATUS_NO_VOLUME && c->status->volume<100 )
562 {
563 c->status->volume=c->status->volume+1;
564 mpd_sendSetvolCommand(c->connection, c->status->volume );
565 mpd_finishCommand(c->connection);
566 screen_status_printf("Volume %d%%", c->status->volume);
567 }
568 break;
569 case CMD_VOLUME_DOWN:
570 if( c->status->volume!=MPD_STATUS_NO_VOLUME && c->status->volume>0 )
571 {
572 c->status->volume=c->status->volume-1;
573 mpd_sendSetvolCommand(c->connection, c->status->volume );
574 mpd_finishCommand(c->connection);
575 screen_status_printf("Volume %d%%", c->status->volume);
576 }
577 break;
578 case CMD_TOGGLE_FIND_WRAP:
579 options.find_wrap = !options.find_wrap;
580 screen_status_printf("Find mode: %s",
581 options.find_wrap ? "Wrapped" : "Normal");
582 break;
583 case CMD_TOGGLE_AUTOCENTER:
584 options.auto_center = !options.auto_center;
585 screen_status_printf("Auto center mode: %s",
586 options.auto_center ? "On" : "Off");
587 break;
588 case CMD_SCREEN_PREVIOUS:
589 if( screen->mode > SCREEN_PLAY_WINDOW )
590 new_mode = screen->mode - 1;
591 else
592 new_mode = SCREEN_HELP_WINDOW-1;
593 switch_screen_mode(new_mode, c);
594 break;
595 case CMD_SCREEN_NEXT:
596 new_mode = screen->mode + 1;
597 if( new_mode >= SCREEN_HELP_WINDOW )
598 new_mode = SCREEN_PLAY_WINDOW;
599 switch_screen_mode(new_mode, c);
600 break;
601 case CMD_SCREEN_PLAY:
602 switch_screen_mode(SCREEN_PLAY_WINDOW, c);
603 break;
604 case CMD_SCREEN_FILE:
605 switch_screen_mode(SCREEN_FILE_WINDOW, c);
606 break;
607 case CMD_SCREEN_SEARCH:
608 switch_screen_mode(SCREEN_SEARCH_WINDOW, c);
609 break;
610 case CMD_SCREEN_HELP:
611 switch_screen_mode(SCREEN_HELP_WINDOW, c);
612 break;
613 case CMD_QUIT:
614 exit(EXIT_SUCCESS);
615 default:
616 break;
617 }
619 }