Code

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