1 /*
2 * $Id: screen.c,v 1.10 2004/03/17 14:50:12 kalle Exp $
3 *
4 */
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <string.h>
9 #include <time.h>
10 #include <glib.h>
11 #include <ncurses.h>
13 #include "libmpdclient.h"
14 #include "mpc.h"
15 #include "command.h"
16 #include "screen.h"
17 #include "screen_play.h"
18 #include "screen_file.h"
19 #include "screen_help.h"
20 #include "screen_search.h"
22 #define STATUS_MESSAGE_TIMEOUT 3
24 static screen_t *screen = NULL;
26 static void
27 switch_screen_mode(screen_mode_t new_mode, mpd_client_t *c)
28 {
29 if( new_mode == screen->mode )
30 return;
32 switch(screen->mode)
33 {
34 case SCREEN_PLAY_WINDOW:
35 play_close(screen, c);
36 break;
37 case SCREEN_FILE_WINDOW:
38 file_close(screen, c);
39 break;
40 case SCREEN_SEARCH_WINDOW:
41 search_close(screen, c);
42 break;
43 case SCREEN_HELP_WINDOW:
44 help_close(screen, c);
45 break;
46 }
48 screen->mode = new_mode;
49 screen->painted = 0;
51 switch(screen->mode)
52 {
53 case SCREEN_PLAY_WINDOW:
54 play_open(screen, c);
55 break;
56 case SCREEN_FILE_WINDOW:
57 file_open(screen, c);
58 break;
59 case SCREEN_SEARCH_WINDOW:
60 search_open(screen, c);
61 break;
62 case SCREEN_HELP_WINDOW:
63 help_open(screen, c);
64 break;
65 }
66 }
68 static void
69 paint_top_window(char *header, int volume, int clear)
70 {
71 static int prev_volume = -1;
72 WINDOW *w = screen->top_window.w;
74 if(clear)
75 {
76 wclear(w);
77 }
79 if(prev_volume!=volume || clear)
80 {
81 char buf[12];
83 wattron(w, A_BOLD);
84 mvwaddstr(w, 0, 0, header );
85 wattroff(w, A_BOLD);
86 if( volume==MPD_STATUS_NO_VOLUME )
87 {
88 snprintf(buf, 12, "Volume n/a ");
89 }
90 else
91 {
92 snprintf(buf, 12, "Volume %3d%%", volume);
93 }
94 mvwaddstr(w, 0, screen->top_window.cols-12, buf);
96 mvwhline(w, 1, 0, ACS_HLINE, screen->top_window.cols);
98 wrefresh(w);
99 }
100 }
102 static void
103 paint_progress_window(mpd_client_t *c)
104 {
105 double p;
106 int width;
108 if( c->status==NULL || !IS_PLAYING(c->status->state) )
109 {
110 mvwhline(screen->progress_window.w, 0, 0, ACS_HLINE,
111 screen->progress_window.cols);
112 wrefresh(screen->progress_window.w);
113 return;
114 }
116 p = ((double) c->status->elapsedTime) / ((double) c->status->totalTime);
118 width = (int) (p * (double) screen->progress_window.cols);
120 mvwhline(screen->progress_window.w,
121 0, 0,
122 ACS_HLINE,
123 screen->progress_window.cols);
125 whline(screen->progress_window.w, '=', width-1);
127 mvwaddch(screen->progress_window.w, 0, width-1, 'O');
128 wrefresh(screen->progress_window.w);
129 }
131 static void
132 paint_status_window(mpd_client_t *c)
133 {
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;
142 wmove(w, 0, 0);
143 wclrtoeol(w);
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 waddstr(w, "Playing:");
154 break;
155 case MPD_STATUS_STATE_PAUSE:
156 wattron(w, A_BOLD);
157 waddstr(w, "Paused:");
158 wattroff(w, A_BOLD);
159 break;
160 default:
161 waddstr(w, "Warning: Music Player Daemon in unknown state!");
162 break;
163 }
164 x += 10;
166 if( IS_PLAYING(status->state) && song )
167 {
168 mvwaddstr(w, 0, x, mpc_get_song_name(song));
169 }
172 /* time */
173 if( IS_PLAYING(status->state) )
174 {
175 x = screen->status_window.cols - strlen(screen->buf);
177 if( c->status->repeat )
178 mvwaddstr(w, 0, x-3, "<R>");
179 else
180 mvwaddstr(w, 0, x-3, " ");
182 snprintf(screen->buf, screen->buf_size,
183 " [%i:%02i/%i:%02i] ",
184 status->elapsedTime/60, status->elapsedTime%60,
185 status->totalTime/60, status->totalTime%60 );
186 mvwaddstr(w, 0, x, screen->buf);
188 }
191 wrefresh(w);
192 }
196 int
197 screen_exit(void)
198 {
199 endwin();
200 if( screen )
201 {
202 screen->playlist = list_window_free(screen->playlist);
203 screen->filelist = list_window_free(screen->filelist);
204 screen->helplist = list_window_free(screen->helplist);
205 free(screen->buf);
206 free(screen);
207 screen = NULL;
208 }
209 return 0;
210 }
212 void
213 screen_resized(int sig)
214 {
215 screen_exit();
216 if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
217 {
218 fprintf(stderr, "Error: Screen to small!\n");
219 exit(EXIT_FAILURE);
220 }
221 screen_init();
222 }
224 void
225 screen_status_message(mpd_client_t *c, char *msg)
226 {
227 WINDOW *w = screen->status_window.w;
229 wmove(w, 0, 0);
230 wclrtoeol(w);
231 wattron(w, A_BOLD);
232 waddstr(w, msg);
233 wattroff(w, A_BOLD);
234 wrefresh(w);
235 screen->status_timestamp = time(NULL);
236 }
238 int
239 screen_init(void)
240 {
241 /* initialize the curses library */
242 initscr();
243 start_color();
244 use_default_colors();
245 /* tell curses not to do NL->CR/NL on output */
246 nonl();
247 /* take input chars one at a time, no wait for \n */
248 cbreak();
249 /* don't echo input */
250 noecho();
251 /* set cursor invisible */
252 curs_set(0);
253 /* return from getch() without blocking */
254 // nodelay(stdscr, TRUE);
255 keypad(stdscr, TRUE);
256 timeout(100); /*void wtimeout(WINDOW *win, int delay);*/
259 if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
260 {
261 fprintf(stderr, "Error: Screen to small!\n");
262 exit(EXIT_FAILURE);
263 }
265 screen = malloc(sizeof(screen_t));
266 memset(screen, 0, sizeof(screen_t));
267 screen->mode = SCREEN_PLAY_WINDOW;
268 screen->cols = COLS;
269 screen->rows = LINES;
270 screen->buf = malloc(screen->cols);
271 screen->buf_size = screen->cols;
272 screen->painted = 0;
274 /* create top window */
275 screen->top_window.rows = 2;
276 screen->top_window.cols = screen->cols;
277 screen->top_window.w = newwin(screen->top_window.rows,
278 screen->top_window.cols,
279 0, 0);
280 leaveok(screen->top_window.w, TRUE);
281 keypad(screen->top_window.w, TRUE);
283 /* create main window */
284 screen->main_window.rows = screen->rows-4;
285 screen->main_window.cols = screen->cols;
286 screen->main_window.w = newwin(screen->main_window.rows,
287 screen->main_window.cols,
288 2,
289 0);
290 screen->playlist = list_window_init( screen->main_window.w,
291 screen->main_window.cols,
292 screen->main_window.rows );
293 screen->filelist = list_window_init( screen->main_window.w,
294 screen->main_window.cols,
295 screen->main_window.rows );
296 screen->helplist = list_window_init( screen->main_window.w,
297 screen->main_window.cols,
298 screen->main_window.rows );
299 leaveok(screen->main_window.w, TRUE);
300 keypad(screen->main_window.w, TRUE);
302 /* create progress window */
303 screen->progress_window.rows = 1;
304 screen->progress_window.cols = screen->cols;
305 screen->progress_window.w = newwin(screen->progress_window.rows,
306 screen->progress_window.cols,
307 screen->rows-2,
308 0);
309 leaveok(screen->progress_window.w, TRUE);
311 /* create status window */
312 screen->status_window.rows = 1;
313 screen->status_window.cols = screen->cols;
314 screen->status_window.w = newwin(screen->status_window.rows,
315 screen->status_window.cols,
316 screen->rows-1,
317 0);
318 leaveok(screen->status_window.w, FALSE);
319 keypad(screen->status_window.w, TRUE);
321 return 0;
322 }
324 void
325 screen_paint(mpd_client_t *c)
326 {
327 switch(screen->mode)
328 {
329 case SCREEN_PLAY_WINDOW:
330 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1);
331 play_paint(screen, c);
332 break;
333 case SCREEN_FILE_WINDOW:
334 paint_top_window(file_get_header(c), c->status->volume, 1);
335 file_paint(screen, c);
336 break;
337 case SCREEN_SEARCH_WINDOW:
338 paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 1);
339 search_paint(screen, c);
340 break;
341 case SCREEN_HELP_WINDOW:
342 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1);
343 help_paint(screen, c);
344 break;
345 }
347 paint_progress_window(c);
348 paint_status_window(c);
349 screen->painted = 1;
350 }
352 void
353 screen_update(mpd_client_t *c)
354 {
355 if( !screen->painted )
356 return screen_paint(c);
358 switch(screen->mode)
359 {
360 case SCREEN_PLAY_WINDOW:
361 paint_top_window(TOP_HEADER_PLAY, c->status->volume, 0);
362 play_update(screen, c);
363 break;
364 case SCREEN_FILE_WINDOW:
365 paint_top_window(file_get_header(c), c->status->volume, 0);
366 file_update(screen, c);
367 break;
368 case SCREEN_SEARCH_WINDOW:
369 paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 0);
370 search_update(screen, c);
371 break;
372 case SCREEN_HELP_WINDOW:
373 paint_top_window(TOP_HEADER_HELP, c->status->volume, 0);
374 help_update(screen, c);
375 break;
376 }
377 paint_progress_window(c);
378 paint_status_window(c);
379 }
381 void
382 screen_cmd(mpd_client_t *c, command_t cmd)
383 {
384 int n;
385 char buf[256];
386 screen_mode_t new_mode = screen->mode;
388 switch(screen->mode)
389 {
390 case SCREEN_PLAY_WINDOW:
391 if( play_cmd(screen, c, cmd) )
392 return;
393 break;
394 case SCREEN_FILE_WINDOW:
395 if( file_cmd(screen, c, cmd) )
396 return;
397 break;
398 case SCREEN_SEARCH_WINDOW:
399 if( search_cmd(screen, c, cmd) )
400 return;
401 break;
402 case SCREEN_HELP_WINDOW:
403 if( help_cmd(screen, c, cmd) )
404 return;
405 break;
406 }
408 switch(cmd)
409 {
410 case CMD_PLAY:
411 mpd_sendPlayCommand(c->connection, screen->playlist->selected);
412 mpd_finishCommand(c->connection);
413 break;
414 case CMD_PAUSE:
415 mpd_sendPauseCommand(c->connection);
416 mpd_finishCommand(c->connection);
417 break;
418 case CMD_STOP:
419 mpd_sendStopCommand(c->connection);
420 mpd_finishCommand(c->connection);
421 break;
422 case CMD_TRACK_NEXT:
423 if( IS_PLAYING(c->status->state) )
424 {
425 mpd_sendNextCommand(c->connection);
426 mpd_finishCommand(c->connection);
427 }
428 break;
429 case CMD_TRACK_PREVIOUS:
430 if( IS_PLAYING(c->status->state) )
431 {
432 mpd_sendPrevCommand(c->connection);
433 mpd_finishCommand(c->connection);
434 }
435 break;
436 case CMD_SHUFFLE:
437 mpd_sendShuffleCommand(c->connection);
438 mpd_finishCommand(c->connection);
439 screen_status_message(c, "Shuffled playlist!");
440 break;
441 case CMD_CLEAR:
442 mpd_sendClearCommand(c->connection);
443 mpd_finishCommand(c->connection);
444 file_clear_highlights(c);
445 screen_status_message(c, "Cleared playlist!");
446 break;
447 case CMD_REPEAT:
448 n = !c->status->repeat;
449 mpd_sendRepeatCommand(c->connection, n);
450 mpd_finishCommand(c->connection);
451 snprintf(buf, 256, "Repeat is %s", n ? "On" : "Off");
452 screen_status_message(c, buf);
453 break;
454 case CMD_RANDOM:
455 n = !c->status->random;
456 mpd_sendRandomCommand(c->connection, n);
457 mpd_finishCommand(c->connection);
458 snprintf(buf, 256, "Random is %s", n ? "On" : "Off");
459 screen_status_message(c, buf);
460 break;
461 case CMD_VOLUME_UP:
462 mpd_sendVolumeCommand(c->connection, 1);
463 mpd_finishCommand(c->connection);
464 if( c->status->volume!=MPD_STATUS_NO_VOLUME )
465 {
466 snprintf(buf, 256, "Volume %d%%", c->status->volume+1);
467 screen_status_message(c, buf);
468 }
469 break;
470 case CMD_VOLUME_DOWN:
471 mpd_sendVolumeCommand(c->connection, -1);
472 mpd_finishCommand(c->connection);
473 if( c->status->volume!=MPD_STATUS_NO_VOLUME )
474 {
475 snprintf(buf, 256, "Volume %d%%", c->status->volume-1);
476 screen_status_message(c, buf);
477 }
478 break;
479 case CMD_SCREEN_PREVIOUS:
480 if( screen->mode > SCREEN_PLAY_WINDOW )
481 new_mode = screen->mode - 1;
482 else
483 new_mode = SCREEN_HELP_WINDOW-1;
484 switch_screen_mode(new_mode, c);
485 break;
486 case CMD_SCREEN_NEXT:
487 new_mode = screen->mode + 1;
488 if( new_mode >= SCREEN_HELP_WINDOW )
489 new_mode = SCREEN_PLAY_WINDOW;
490 switch_screen_mode(new_mode, c);
491 break;
492 case CMD_SCREEN_PLAY:
493 switch_screen_mode(SCREEN_PLAY_WINDOW, c);
494 break;
495 case CMD_SCREEN_FILE:
496 switch_screen_mode(SCREEN_FILE_WINDOW, c);
497 break;
498 case CMD_SCREEN_SEARCH:
499 switch_screen_mode(SCREEN_SEARCH_WINDOW, c);
500 break;
501 case CMD_SCREEN_HELP:
502 switch_screen_mode(SCREEN_HELP_WINDOW, c);
503 break;
504 case CMD_QUIT:
505 exit(EXIT_SUCCESS);
506 case CMD_NONE:
507 case CMD_DELETE:
508 case CMD_SELECT:
509 case CMD_LIST_PREVIOUS:
510 case CMD_LIST_NEXT:
511 case CMD_LIST_FIRST:
512 case CMD_LIST_LAST:
513 case CMD_LIST_NEXT_PAGE:
514 case CMD_LIST_PREVIOUS_PAGE:
515 break;
516 }
518 }