Code

8d14376ff95c5650950f32023e1b7c605b68c0f8
[ncmpc.git] / screen.c
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     }
112 static void
113 paint_progress_window(mpd_client_t *c)
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);
127   
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);
138 static void 
139 paint_status_window(mpd_client_t *c)
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;
148   
149   wmove(w, 0, 0);
150   wclrtoeol(w);
151   
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     }
181   
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);
193         
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);
200       
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
207   
209   wnoutrefresh(w);
214 int
215 screen_exit(void)
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;
231 void
232 screen_resized(int sig)
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();
243 void 
244 screen_status_message(char *msg)
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);
257 void 
258 screen_status_printf(char *format, ...)
260   char buffer[STATUS_LINE_MAX_SIZE];
261   va_list ap;
262   
263   va_start(ap,format);
264   vsnprintf(buffer,sizeof(buffer),format,ap);
265   va_end(ap);
266   screen_status_message(buffer);
269 int
270 screen_init(void)
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);
361   
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;
385 void 
386 screen_paint(mpd_client_t *c)
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();
414 void 
415 screen_update(mpd_client_t *c)
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");
434   
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();
462 void 
463 screen_cmd(mpd_client_t *c, command_t cmd)
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     }