Code

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