8d14376ff95c5650950f32023e1b7c605b68c0f8
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 1
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 free(screen->buf);
224 free(screen->findbuf);
225 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 = 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 = 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);
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 doupdate();
412 }
414 void
415 screen_update(mpd_client_t *c)
416 {
417 static int repeat = -1;
418 static int random = -1;
420 if( !screen->painted )
421 return screen_paint(c);
423 if( repeat<0 )
424 {
425 repeat = c->status->repeat;
426 random = c->status->random;
427 }
428 if( repeat != c->status->repeat )
429 screen_status_printf("Repeat is %s",
430 c->status->repeat ? "On" : "Off");
431 if( random != c->status->random )
432 screen_status_printf("Random is %s",
433 c->status->random ? "On" : "Off");
435 repeat = c->status->repeat;
436 random = c->status->random;
438 switch(screen->mode)
439 {
440 case SCREEN_PLAY_WINDOW:
441 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 0);
442 play_update(screen, c);
443 break;
444 case SCREEN_FILE_WINDOW:
445 paint_top_window(file_get_header(c), c->status->volume, 0);
446 file_update(screen, c);
447 break;
448 case SCREEN_SEARCH_WINDOW:
449 paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 0);
450 search_update(screen, c);
451 break;
452 case SCREEN_HELP_WINDOW:
453 paint_top_window(TOP_HEADER_HELP, c->status->volume, 0);
454 help_update(screen, c);
455 break;
456 }
457 paint_progress_window(c);
458 paint_status_window(c);
459 doupdate();
460 }
462 void
463 screen_cmd(mpd_client_t *c, command_t cmd)
464 {
465 int n;
466 screen_mode_t new_mode = screen->mode;
468 switch(screen->mode)
469 {
470 case SCREEN_PLAY_WINDOW:
471 if( play_cmd(screen, c, cmd) )
472 return;
473 break;
474 case SCREEN_FILE_WINDOW:
475 if( file_cmd(screen, c, cmd) )
476 return;
477 break;
478 case SCREEN_SEARCH_WINDOW:
479 if( search_cmd(screen, c, cmd) )
480 return;
481 break;
482 case SCREEN_HELP_WINDOW:
483 if( help_cmd(screen, c, cmd) )
484 return;
485 break;
486 }
488 switch(cmd)
489 {
490 case CMD_PLAY:
491 mpd_sendPlayCommand(c->connection, screen->playlist->selected);
492 mpd_finishCommand(c->connection);
493 break;
494 case CMD_PAUSE:
495 mpd_sendPauseCommand(c->connection);
496 mpd_finishCommand(c->connection);
497 break;
498 case CMD_STOP:
499 mpd_sendStopCommand(c->connection);
500 mpd_finishCommand(c->connection);
501 break;
502 case CMD_TRACK_NEXT:
503 if( IS_PLAYING(c->status->state) )
504 {
505 mpd_sendNextCommand(c->connection);
506 mpd_finishCommand(c->connection);
507 }
508 break;
509 case CMD_TRACK_PREVIOUS:
510 if( IS_PLAYING(c->status->state) )
511 {
512 mpd_sendPrevCommand(c->connection);
513 mpd_finishCommand(c->connection);
514 }
515 break;
516 case CMD_SHUFFLE:
517 mpd_sendShuffleCommand(c->connection);
518 mpd_finishCommand(c->connection);
519 screen_status_message("Shuffled playlist!");
520 break;
521 case CMD_CLEAR:
522 mpd_sendClearCommand(c->connection);
523 mpd_finishCommand(c->connection);
524 file_clear_highlights(c);
525 screen_status_message("Cleared playlist!");
526 break;
527 case CMD_REPEAT:
528 n = !c->status->repeat;
529 mpd_sendRepeatCommand(c->connection, n);
530 mpd_finishCommand(c->connection);
531 break;
532 case CMD_RANDOM:
533 n = !c->status->random;
534 mpd_sendRandomCommand(c->connection, n);
535 mpd_finishCommand(c->connection);
536 break;
537 case CMD_VOLUME_UP:
538 if( c->status->volume!=MPD_STATUS_NO_VOLUME && c->status->volume<100 )
539 {
540 c->status->volume=c->status->volume+1;
541 mpd_sendSetvolCommand(c->connection, c->status->volume );
542 mpd_finishCommand(c->connection);
543 screen_status_printf("Volume %d%%", c->status->volume);
544 }
545 break;
546 case CMD_VOLUME_DOWN:
547 if( c->status->volume!=MPD_STATUS_NO_VOLUME && c->status->volume>0 )
548 {
549 c->status->volume=c->status->volume-1;
550 mpd_sendSetvolCommand(c->connection, c->status->volume );
551 mpd_finishCommand(c->connection);
552 screen_status_printf("Volume %d%%", c->status->volume);
553 }
554 break;
555 case CMD_TOGGLE_FIND_WRAP:
556 options.find_wrap = !options.find_wrap;
557 screen_status_printf("Find mode: %s",
558 options.find_wrap ? "Wrapped" : "Normal");
559 break;
560 case CMD_SCREEN_PREVIOUS:
561 if( screen->mode > SCREEN_PLAY_WINDOW )
562 new_mode = screen->mode - 1;
563 else
564 new_mode = SCREEN_HELP_WINDOW-1;
565 switch_screen_mode(new_mode, c);
566 break;
567 case CMD_SCREEN_NEXT:
568 new_mode = screen->mode + 1;
569 if( new_mode >= SCREEN_HELP_WINDOW )
570 new_mode = SCREEN_PLAY_WINDOW;
571 switch_screen_mode(new_mode, c);
572 break;
573 case CMD_SCREEN_PLAY:
574 switch_screen_mode(SCREEN_PLAY_WINDOW, c);
575 break;
576 case CMD_SCREEN_FILE:
577 switch_screen_mode(SCREEN_FILE_WINDOW, c);
578 break;
579 case CMD_SCREEN_SEARCH:
580 switch_screen_mode(SCREEN_SEARCH_WINDOW, c);
581 break;
582 case CMD_SCREEN_HELP:
583 switch_screen_mode(SCREEN_HELP_WINDOW, c);
584 break;
585 case CMD_QUIT:
586 exit(EXIT_SUCCESS);
587 default:
588 break;
589 }
591 }