Code

86269512527dc1ec8a5a37010e56e220d0917619
[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   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     }
107 static void
108 paint_progress_window(mpd_client_t *c)
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);
122   
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);
133 static void 
134 paint_status_window(mpd_client_t *c)
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;
143   
144   wmove(w, 0, 0);
145   wclrtoeol(w);
146   
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     }
177   
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);
190         
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);
197       
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
204   
206   wrefresh(w);
211 int
212 screen_exit(void)
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;
228 void
229 screen_resized(int sig)
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();
240 void 
241 screen_status_message(char *msg)
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);
254 void 
255 screen_status_printf(char *format, ...)
257   char buffer[STATUS_LINE_MAX_SIZE];
258   va_list ap;
259   
260   va_start(ap,format);
261   vsnprintf(buffer,sizeof(buffer),format,ap);
262   va_end(ap);
263   screen_status_message(buffer);
266 int
267 screen_init(void)
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);
349   
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;
376 void 
377 screen_paint(mpd_client_t *c)
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;
404 void 
405 screen_update(mpd_client_t *c)
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);
433 void 
434 screen_cmd(mpd_client_t *c, command_t cmd)
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     }