Code

efd93d1fec9e76f903b3229626bb3aa9a9c700f7
[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       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]");
116         
117       if( options.enable_colors )
118         wattroff(w, LINE_COLORS);
120 #endif
122       wnoutrefresh(w);
123     }
126 static void
127 paint_progress_window(mpd_client_t *c)
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);
141   
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);
152 static void 
153 paint_status_window(mpd_client_t *c)
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;
162   
163   wmove(w, 0, 0);
164   wclrtoeol(w);
165   
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     }
195   
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);
207         
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);
214       
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
221   
223   wnoutrefresh(w);
228 int
229 screen_exit(void)
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;
245 void
246 screen_resized(int sig)
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();
257 void 
258 screen_status_message(char *msg)
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);
271 void 
272 screen_status_printf(char *format, ...)
274   char buffer[STATUS_LINE_MAX_SIZE];
275   va_list ap;
276   
277   va_start(ap,format);
278   vsnprintf(buffer,sizeof(buffer),format,ap);
279   va_end(ap);
280   screen_status_message(buffer);
283 int
284 screen_init(void)
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);
376   
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;
400 void 
401 screen_paint(mpd_client_t *c)
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();
430 void 
431 screen_update(mpd_client_t *c)
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");
451   
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();
484 void 
485 screen_cmd(mpd_client_t *c, command_t cmd)
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     }