86269512527dc1ec8a5a37010e56e220d0917619
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 WINDOW *w = screen->top_window.w;
73 if(clear)
74 {
75 wclear(w);
76 }
78 if(prev_volume!=volume || clear)
79 {
80 char buf[12];
82 wattron(w, A_BOLD);
83 mvwaddstr(w, 0, 0, header);
84 wattroff(w, A_BOLD);
85 if( volume==MPD_STATUS_NO_VOLUME )
86 {
87 snprintf(buf, 12, "Volume n/a ");
88 }
89 else
90 {
91 snprintf(buf, 12, "Volume %3d%%", volume);
92 }
93 mvwaddstr(w, 0, screen->top_window.cols-12, buf);
95 if( options.enable_colors )
96 wattron(w, LINE_COLORS);
98 mvwhline(w, 1, 0, ACS_HLINE, screen->top_window.cols);
100 if( options.enable_colors )
101 wattroff(w, LINE_COLORS);
103 wrefresh(w);
104 }
105 }
107 static void
108 paint_progress_window(mpd_client_t *c)
109 {
110 double p;
111 int width;
113 if( c->status==NULL || !IS_PLAYING(c->status->state) )
114 {
115 mvwhline(screen->progress_window.w, 0, 0, ACS_HLINE,
116 screen->progress_window.cols);
117 wrefresh(screen->progress_window.w);
118 return;
119 }
121 p = ((double) c->status->elapsedTime) / ((double) c->status->totalTime);
123 width = (int) (p * (double) screen->progress_window.cols);
124 mvwhline(screen->progress_window.w,
125 0, 0,
126 ACS_HLINE,
127 screen->progress_window.cols);
128 whline(screen->progress_window.w, '=', width-1);
129 mvwaddch(screen->progress_window.w, 0, width-1, 'O');
130 wrefresh(screen->progress_window.w);
131 }
133 static void
134 paint_status_window(mpd_client_t *c)
135 {
136 WINDOW *w = screen->status_window.w;
137 mpd_Status *status = c->status;
138 mpd_Song *song = c->song;
139 int x = 0;
141 if( time(NULL) - screen->status_timestamp <= STATUS_MESSAGE_TIMEOUT )
142 return;
144 wmove(w, 0, 0);
145 wclrtoeol(w);
147 switch(status->state)
148 {
149 case MPD_STATUS_STATE_STOP:
150 wattron(w, A_BOLD);
151 waddstr(w, "Stopped! ");
152 wattroff(w, A_BOLD);
153 break;
154 case MPD_STATUS_STATE_PLAY:
155 wattron(w, A_BOLD);
156 waddstr(w, "Playing:");
157 wattroff(w, A_BOLD);
158 break;
159 case MPD_STATUS_STATE_PAUSE:
160 wattron(w, A_BOLD);
161 waddstr(w, "[Paused]");
162 wattroff(w, A_BOLD);
163 break;
164 default:
165 my_waddstr(w,
166 "Warning: Music Player Daemon in unknown state!",
167 ALERT_COLORS);
168 break;
169 }
170 x += 10;
172 if( (IS_PLAYING(status->state) || IS_PAUSED(status->state)) && song )
173 {
174 // my_mvwaddstr(w, 0, x, mpc_get_song_name(song), COLOR_PAIR(2));
175 mvwaddstr(w, 0, x, mpc_get_song_name(song));
176 }
179 /* time */
180 if( IS_PLAYING(status->state) || IS_PAUSED(status->state) )
181 {
182 x = screen->status_window.cols - strlen(screen->buf);
184 snprintf(screen->buf, screen->buf_size,
185 " [%i:%02i/%i:%02i] ",
186 status->elapsedTime/60, status->elapsedTime%60,
187 status->totalTime/60, status->totalTime%60 );
188 //my_mvwaddstr(w, 0, x, screen->buf, COLOR_PAIR(1));
189 mvwaddstr(w, 0, x, screen->buf);
191 }
192 #if 1
193 else if( c->status->state == MPD_STATUS_STATE_STOP )
194 {
195 time_t timep;
196 x = screen->status_window.cols - strlen(screen->buf);
198 time(&timep);
199 //strftime(screen->buf, screen->buf_size, "%X ", localtime(&timep));
200 strftime(screen->buf, screen->buf_size, "%R ", localtime(&timep));
201 mvwaddstr(w, 0, x, screen->buf);
202 }
203 #endif
206 wrefresh(w);
207 }
211 int
212 screen_exit(void)
213 {
214 endwin();
215 if( screen )
216 {
217 screen->playlist = list_window_free(screen->playlist);
218 screen->filelist = list_window_free(screen->filelist);
219 screen->helplist = list_window_free(screen->helplist);
220 free(screen->buf);
221 free(screen->findbuf);
222 free(screen);
223 screen = NULL;
224 }
225 return 0;
226 }
228 void
229 screen_resized(int sig)
230 {
231 screen_exit();
232 if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
233 {
234 fprintf(stderr, "Error: Screen to small!\n");
235 exit(EXIT_FAILURE);
236 }
237 screen_init();
238 }
240 void
241 screen_status_message(char *msg)
242 {
243 WINDOW *w = screen->status_window.w;
245 wmove(w, 0, 0);
246 wclrtoeol(w);
247 wattron(w, A_BOLD);
248 my_waddstr(w, msg, ALERT_COLORS);
249 wattroff(w, A_BOLD);
250 wrefresh(w);
251 screen->status_timestamp = time(NULL);
252 }
254 void
255 screen_status_printf(char *format, ...)
256 {
257 char buffer[STATUS_LINE_MAX_SIZE];
258 va_list ap;
260 va_start(ap,format);
261 vsnprintf(buffer,sizeof(buffer),format,ap);
262 va_end(ap);
263 screen_status_message(buffer);
264 }
266 int
267 screen_init(void)
268 {
269 /* initialize the curses library */
270 initscr();
271 start_color();
272 if( options.enable_colors )
273 {
274 init_pair(1, options.title_color, options.bg_color);
275 init_pair(2, options.line_color, options.bg_color);
276 init_pair(3, options.list_color, options.bg_color);
277 init_pair(4, options.progress_color, options.bg_color);
278 init_pair(5, options.status_color, options.bg_color);
279 init_pair(6, options.alert_color, options.bg_color);
280 }
281 else
282 use_default_colors();
284 /* tell curses not to do NL->CR/NL on output */
285 nonl();
286 /* take input chars one at a time, no wait for \n */
287 cbreak();
288 /* don't echo input */
289 noecho();
290 /* set cursor invisible */
291 curs_set(0);
292 /* return from getch() without blocking */
293 // nodelay(stdscr, TRUE);
294 keypad(stdscr, TRUE);
295 timeout(100); /*void wtimeout(WINDOW *win, int delay);*/
297 if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
298 {
299 fprintf(stderr, "Error: Screen to small!\n");
300 exit(EXIT_FAILURE);
301 }
303 screen = malloc(sizeof(screen_t));
304 memset(screen, 0, sizeof(screen_t));
305 screen->mode = SCREEN_PLAY_WINDOW;
306 screen->cols = COLS;
307 screen->rows = LINES;
308 screen->buf = malloc(screen->cols);
309 screen->buf_size = screen->cols;
310 screen->findbuf = NULL;
311 screen->painted = 0;
313 /* create top window */
314 screen->top_window.rows = 2;
315 screen->top_window.cols = screen->cols;
316 screen->top_window.w = newwin(screen->top_window.rows,
317 screen->top_window.cols,
318 0, 0);
319 leaveok(screen->top_window.w, TRUE);
320 keypad(screen->top_window.w, TRUE);
322 /* create main window */
323 screen->main_window.rows = screen->rows-4;
324 screen->main_window.cols = screen->cols;
325 screen->main_window.w = newwin(screen->main_window.rows,
326 screen->main_window.cols,
327 2,
328 0);
329 screen->playlist = list_window_init( screen->main_window.w,
330 screen->main_window.cols,
331 screen->main_window.rows );
332 screen->filelist = list_window_init( screen->main_window.w,
333 screen->main_window.cols,
334 screen->main_window.rows );
335 screen->helplist = list_window_init( screen->main_window.w,
336 screen->main_window.cols,
337 screen->main_window.rows );
338 leaveok(screen->main_window.w, TRUE);
339 keypad(screen->main_window.w, TRUE);
341 /* create progress window */
342 screen->progress_window.rows = 1;
343 screen->progress_window.cols = screen->cols;
344 screen->progress_window.w = newwin(screen->progress_window.rows,
345 screen->progress_window.cols,
346 screen->rows-2,
347 0);
348 leaveok(screen->progress_window.w, TRUE);
350 /* create status window */
351 screen->status_window.rows = 1;
352 screen->status_window.cols = screen->cols;
353 screen->status_window.w = newwin(screen->status_window.rows,
354 screen->status_window.cols,
355 screen->rows-1,
356 0);
358 leaveok(screen->status_window.w, FALSE);
359 keypad(screen->status_window.w, TRUE);
361 if( options.enable_colors )
362 {
363 /* set background attributes */
364 wbkgd(screen->main_window.w, LIST_COLORS);
365 wbkgd(screen->top_window.w, TITLE_COLORS);
366 wbkgd(screen->playlist->w, LIST_COLORS);
367 wbkgd(screen->filelist->w, LIST_COLORS);
368 wbkgd(screen->helplist->w, LIST_COLORS);
369 wbkgd(screen->progress_window.w, PROGRESS_COLORS);
370 wbkgd(screen->status_window.w, STATUS_COLORS);
371 }
373 return 0;
374 }
376 void
377 screen_paint(mpd_client_t *c)
378 {
379 switch(screen->mode)
380 {
381 case SCREEN_PLAY_WINDOW:
382 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1);
383 play_paint(screen, c);
384 break;
385 case SCREEN_FILE_WINDOW:
386 paint_top_window(file_get_header(c), c->status->volume, 1);
387 file_paint(screen, c);
388 break;
389 case SCREEN_SEARCH_WINDOW:
390 paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 1);
391 search_paint(screen, c);
392 break;
393 case SCREEN_HELP_WINDOW:
394 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1);
395 help_paint(screen, c);
396 break;
397 }
399 paint_progress_window(c);
400 paint_status_window(c);
401 screen->painted = 1;
402 }
404 void
405 screen_update(mpd_client_t *c)
406 {
407 if( !screen->painted )
408 return screen_paint(c);
410 switch(screen->mode)
411 {
412 case SCREEN_PLAY_WINDOW:
413 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 0);
414 play_update(screen, c);
415 break;
416 case SCREEN_FILE_WINDOW:
417 paint_top_window(file_get_header(c), c->status->volume, 0);
418 file_update(screen, c);
419 break;
420 case SCREEN_SEARCH_WINDOW:
421 paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 0);
422 search_update(screen, c);
423 break;
424 case SCREEN_HELP_WINDOW:
425 paint_top_window(TOP_HEADER_HELP, c->status->volume, 0);
426 help_update(screen, c);
427 break;
428 }
429 paint_progress_window(c);
430 paint_status_window(c);
431 }
433 void
434 screen_cmd(mpd_client_t *c, command_t cmd)
435 {
436 int n;
437 // char buf[256];
438 screen_mode_t new_mode = screen->mode;
440 switch(screen->mode)
441 {
442 case SCREEN_PLAY_WINDOW:
443 if( play_cmd(screen, c, cmd) )
444 return;
445 break;
446 case SCREEN_FILE_WINDOW:
447 if( file_cmd(screen, c, cmd) )
448 return;
449 break;
450 case SCREEN_SEARCH_WINDOW:
451 if( search_cmd(screen, c, cmd) )
452 return;
453 break;
454 case SCREEN_HELP_WINDOW:
455 if( help_cmd(screen, c, cmd) )
456 return;
457 break;
458 }
460 switch(cmd)
461 {
462 case CMD_PLAY:
463 mpd_sendPlayCommand(c->connection, screen->playlist->selected);
464 mpd_finishCommand(c->connection);
465 break;
466 case CMD_PAUSE:
467 mpd_sendPauseCommand(c->connection);
468 mpd_finishCommand(c->connection);
469 break;
470 case CMD_STOP:
471 mpd_sendStopCommand(c->connection);
472 mpd_finishCommand(c->connection);
473 break;
474 case CMD_TRACK_NEXT:
475 if( IS_PLAYING(c->status->state) )
476 {
477 mpd_sendNextCommand(c->connection);
478 mpd_finishCommand(c->connection);
479 }
480 break;
481 case CMD_TRACK_PREVIOUS:
482 if( IS_PLAYING(c->status->state) )
483 {
484 mpd_sendPrevCommand(c->connection);
485 mpd_finishCommand(c->connection);
486 }
487 break;
488 case CMD_SHUFFLE:
489 mpd_sendShuffleCommand(c->connection);
490 mpd_finishCommand(c->connection);
491 screen_status_message("Shuffled playlist!");
492 break;
493 case CMD_CLEAR:
494 mpd_sendClearCommand(c->connection);
495 mpd_finishCommand(c->connection);
496 file_clear_highlights(c);
497 screen_status_message("Cleared playlist!");
498 break;
499 case CMD_REPEAT:
500 n = !c->status->repeat;
501 mpd_sendRepeatCommand(c->connection, n);
502 mpd_finishCommand(c->connection);
503 screen_status_printf("Repeat is %s", n ? "On" : "Off");
504 break;
505 case CMD_RANDOM:
506 n = !c->status->random;
507 mpd_sendRandomCommand(c->connection, n);
508 mpd_finishCommand(c->connection);
509 screen_status_printf("Random is %s", n ? "On" : "Off");
510 break;
511 case CMD_VOLUME_UP:
512 if( c->status->volume!=MPD_STATUS_NO_VOLUME && c->status->volume<100 )
513 {
514 c->status->volume=c->status->volume+1;
515 mpd_sendSetvolCommand(c->connection, c->status->volume );
516 mpd_finishCommand(c->connection);
517 screen_status_printf("Volume %d%%", c->status->volume);
518 }
519 break;
520 case CMD_VOLUME_DOWN:
521 if( c->status->volume!=MPD_STATUS_NO_VOLUME && c->status->volume>0 )
522 {
523 c->status->volume=c->status->volume-1;
524 mpd_sendSetvolCommand(c->connection, c->status->volume );
525 mpd_finishCommand(c->connection);
526 screen_status_printf("Volume %d%%", c->status->volume);
527 }
528 break;
529 case CMD_SCREEN_PREVIOUS:
530 if( screen->mode > SCREEN_PLAY_WINDOW )
531 new_mode = screen->mode - 1;
532 else
533 new_mode = SCREEN_HELP_WINDOW-1;
534 switch_screen_mode(new_mode, c);
535 break;
536 case CMD_SCREEN_NEXT:
537 new_mode = screen->mode + 1;
538 if( new_mode >= SCREEN_HELP_WINDOW )
539 new_mode = SCREEN_PLAY_WINDOW;
540 switch_screen_mode(new_mode, c);
541 break;
542 case CMD_SCREEN_PLAY:
543 switch_screen_mode(SCREEN_PLAY_WINDOW, c);
544 break;
545 case CMD_SCREEN_FILE:
546 switch_screen_mode(SCREEN_FILE_WINDOW, c);
547 break;
548 case CMD_SCREEN_SEARCH:
549 switch_screen_mode(SCREEN_SEARCH_WINDOW, c);
550 break;
551 case CMD_SCREEN_HELP:
552 switch_screen_mode(SCREEN_HELP_WINDOW, c);
553 break;
554 case CMD_QUIT:
555 exit(EXIT_SUCCESS);
556 case CMD_LIST_FIND:
557 case CMD_LIST_FIND_NEXT:
558 case CMD_NONE:
559 case CMD_DELETE:
560 case CMD_SELECT:
561 case CMD_LIST_PREVIOUS:
562 case CMD_LIST_NEXT:
563 case CMD_LIST_FIRST:
564 case CMD_LIST_LAST:
565 case CMD_LIST_NEXT_PAGE:
566 case CMD_LIST_PREVIOUS_PAGE:
567 break;
568 }
570 }
573 ;