1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2010 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "screen_interface.h"
22 #include "screen_list.h"
23 #include "screen_utils.h"
24 #include "screen_status.h"
28 #include "mpdclient.h"
32 #include "player_command.h"
33 #include "screen_help.h"
34 #include "screen_queue.h"
35 #include "screen_file.h"
36 #include "screen_artist.h"
37 #include "screen_search.h"
38 #include "screen_song.h"
39 #include "screen_keydef.h"
40 #include "screen_lyrics.h"
41 #include "screen_outputs.h"
42 #include "screen_chat.h"
44 #include <mpd/client.h>
53 /** welcome message time [s] */
54 static const GTime SCREEN_WELCOME_TIME
= 10;
57 /* minimum window size */
58 static const int SCREEN_MIN_COLS
= 14;
59 static const int SCREEN_MIN_ROWS
= 5;
64 static const struct screen_functions
*mode_fn
= &screen_queue
;
65 static const struct screen_functions
*mode_fn_prev
= &screen_queue
;
68 screen_is_visible(const struct screen_functions
*sf
)
74 screen_switch(const struct screen_functions
*sf
, struct mpdclient
*c
)
81 mode_fn_prev
= mode_fn
;
83 /* close the old mode */
84 if (mode_fn
->close
!= NULL
)
87 /* get functions for the new mode */
90 /* open the new mode */
91 if (mode_fn
->open
!= NULL
)
98 screen_swap(struct mpdclient
*c
, const struct mpd_song
*song
)
103 { /* just a hack to make the ifdefs less ugly */ }
104 #ifdef ENABLE_SONG_SCREEN
105 if (mode_fn_prev
== &screen_song
)
106 screen_song_switch(c
, song
);
108 #ifdef ENABLE_LYRICS_SCREEN
109 else if (mode_fn_prev
== &screen_lyrics
)
110 screen_lyrics_switch(c
, song
, true);
113 screen_switch(mode_fn_prev
, c
);
116 screen_switch(mode_fn_prev
, c
);
120 find_configured_screen(const char *name
)
124 for (i
= 0; options
.screen_list
[i
] != NULL
; ++i
)
125 if (strcmp(options
.screen_list
[i
], name
) == 0)
132 screen_next_mode(struct mpdclient
*c
, int offset
)
134 int max
= g_strv_length(options
.screen_list
);
136 /* find current screen */
137 int current
= find_configured_screen(screen_get_name(mode_fn
));
138 int next
= current
+ offset
;
144 const struct screen_functions
*sf
=
145 screen_lookup_name(options
.screen_list
[next
]);
147 screen_switch(sf
, c
);
151 paint_top_window(const char *header
, const struct mpdclient
*c
)
153 title_bar_paint(&screen
.title_bar
, header
, c
->status
);
157 update_progress_window(struct mpdclient
*c
, bool repaint
)
160 if (c
->status
== NULL
)
162 else if (seek_id
>= 0 && seek_id
== mpd_status_get_song_id(c
->status
))
163 elapsed
= seek_target_time
;
165 elapsed
= mpd_status_get_elapsed_time(c
->status
);
167 unsigned duration
= mpdclient_is_playing(c
)
168 ? mpd_status_get_total_time(c
->status
)
171 if (progress_bar_set(&screen
.progress_bar
, elapsed
, duration
) ||
173 progress_bar_paint(&screen
.progress_bar
);
179 if (mode_fn
->close
!= NULL
)
184 string_list_free(screen
.find_history
);
186 g_free(screen
.findbuf
);
188 title_bar_deinit(&screen
.title_bar
);
189 delwin(screen
.main_window
.w
);
190 progress_bar_deinit(&screen
.progress_bar
);
191 status_bar_deinit(&screen
.status_bar
);
194 if (screen
.welcome_source_id
!= 0)
195 g_source_remove(screen
.welcome_source_id
);
200 screen_resize(struct mpdclient
*c
)
202 if (COLS
<SCREEN_MIN_COLS
|| LINES
<SCREEN_MIN_ROWS
) {
204 fprintf(stderr
, "%s\n", _("Error: Screen too small"));
208 resize_term(LINES
, COLS
);
210 resizeterm(LINES
, COLS
);
216 title_bar_resize(&screen
.title_bar
, screen
.cols
);
219 screen
.main_window
.cols
= screen
.cols
;
220 screen
.main_window
.rows
= screen
.rows
-4;
221 wresize(screen
.main_window
.w
, screen
.main_window
.rows
, screen
.cols
);
222 wclear(screen
.main_window
.w
);
224 /* progress window */
225 progress_bar_resize(&screen
.progress_bar
, screen
.cols
,
227 progress_bar_paint(&screen
.progress_bar
);
230 status_bar_resize(&screen
.status_bar
, screen
.cols
, screen
.rows
- 1, 0);
231 status_bar_paint(&screen
.status_bar
, c
->status
, c
->song
);
233 screen
.buf_size
= screen
.cols
;
235 screen
.buf
= g_malloc(screen
.cols
);
237 /* resize all screens */
238 screen_list_resize(screen
.main_window
.cols
, screen
.main_window
.rows
);
240 /* ? - without this the cursor becomes visible with aterm & Eterm */
249 welcome_timer_callback(gpointer data
)
251 struct mpdclient
*c
= data
;
254 screen
.welcome_source_id
= 0;
257 paint_top_window(mode_fn
->get_title
!= NULL
258 ? mode_fn
->get_title(screen
.buf
, screen
.buf_size
)
268 screen_init(struct mpdclient
*c
)
270 if (COLS
< SCREEN_MIN_COLS
|| LINES
< SCREEN_MIN_ROWS
) {
271 fprintf(stderr
, "%s\n", _("Error: Screen too small"));
278 screen
.buf
= g_malloc(screen
.cols
);
279 screen
.buf_size
= screen
.cols
;
280 screen
.findbuf
= NULL
;
283 if (options
.welcome_screen_list
)
284 screen
.welcome_source_id
=
285 g_timeout_add(SCREEN_WELCOME_TIME
* 1000,
286 welcome_timer_callback
, c
);
289 /* create top window */
290 title_bar_init(&screen
.title_bar
, screen
.cols
, 0, 0);
292 /* create main window */
293 window_init(&screen
.main_window
, screen
.rows
- 4, screen
.cols
, 2, 0);
295 if (!options
.hardware_cursor
)
296 leaveok(screen
.main_window
.w
, TRUE
);
298 keypad(screen
.main_window
.w
, TRUE
);
300 /* create progress window */
301 progress_bar_init(&screen
.progress_bar
, screen
.cols
,
303 progress_bar_paint(&screen
.progress_bar
);
305 /* create status window */
306 status_bar_init(&screen
.status_bar
, screen
.cols
, screen
.rows
- 1, 0);
307 status_bar_paint(&screen
.status_bar
, c
->status
, c
->song
);
310 if (options
.enable_colors
) {
311 /* set background attributes */
312 wbkgd(stdscr
, COLOR_PAIR(COLOR_LIST
));
313 wbkgd(screen
.main_window
.w
, COLOR_PAIR(COLOR_LIST
));
314 wbkgd(screen
.title_bar
.window
.w
, COLOR_PAIR(COLOR_TITLE
));
315 wbkgd(screen
.progress_bar
.window
.w
,
316 COLOR_PAIR(COLOR_PROGRESSBAR
));
317 wbkgd(screen
.status_bar
.window
.w
, COLOR_PAIR(COLOR_STATUS
));
318 colors_use(screen
.progress_bar
.window
.w
, COLOR_PROGRESSBAR
);
324 /* initialize screens */
325 screen_list_init(screen
.main_window
.w
,
326 screen
.main_window
.cols
, screen
.main_window
.rows
);
328 if (mode_fn
->open
!= NULL
)
333 screen_paint(struct mpdclient
*c
)
335 const char *title
= NULL
;
337 if (mode_fn
->get_title
!= NULL
)
338 title
= mode_fn
->get_title(screen
.buf
, screen
.buf_size
);
340 /* paint the title/header window */
342 paint_top_window(title
, c
);
344 paint_top_window("", c
);
346 /* paint the bottom window */
348 update_progress_window(c
, true);
349 status_bar_paint(&screen
.status_bar
, c
->status
, c
->song
);
351 /* paint the main window */
353 wclear(screen
.main_window
.w
);
354 if (mode_fn
->paint
!= NULL
)
357 /* move the cursor to the origin */
359 if (!options
.hardware_cursor
)
360 wmove(screen
.main_window
.w
, 0, 0);
362 wnoutrefresh(screen
.main_window
.w
);
364 /* tell curses to update */
369 screen_update(struct mpdclient
*c
)
372 static bool was_connected
;
373 static bool initialized
= false;
375 static bool random_enabled
;
378 static unsigned crossfade
;
380 /* print a message if mpd status has changed */
381 if ((c
->events
& MPD_IDLE_OPTIONS
) && c
->status
!= NULL
) {
383 repeat
= mpd_status_get_repeat(c
->status
);
384 random_enabled
= mpd_status_get_random(c
->status
);
385 single
= mpd_status_get_single(c
->status
);
386 consume
= mpd_status_get_consume(c
->status
);
387 crossfade
= mpd_status_get_crossfade(c
->status
);
391 if (repeat
!= mpd_status_get_repeat(c
->status
))
392 screen_status_printf(mpd_status_get_repeat(c
->status
) ?
393 _("Repeat mode is on") :
394 _("Repeat mode is off"));
396 if (random_enabled
!= mpd_status_get_random(c
->status
))
397 screen_status_printf(mpd_status_get_random(c
->status
) ?
398 _("Random mode is on") :
399 _("Random mode is off"));
401 if (single
!= mpd_status_get_single(c
->status
))
402 screen_status_printf(mpd_status_get_single(c
->status
) ?
403 /* "single" mode means
408 _("Single mode is on") :
409 _("Single mode is off"));
411 if (consume
!= mpd_status_get_consume(c
->status
))
412 screen_status_printf(mpd_status_get_consume(c
->status
) ?
413 /* "consume" mode means
414 that MPD removes each
417 _("Consume mode is on") :
418 _("Consume mode is off"));
420 if (crossfade
!= mpd_status_get_crossfade(c
->status
))
421 screen_status_printf(_("Crossfade %d seconds"),
422 mpd_status_get_crossfade(c
->status
));
424 repeat
= mpd_status_get_repeat(c
->status
);
425 random_enabled
= mpd_status_get_random(c
->status
);
426 single
= mpd_status_get_single(c
->status
);
427 consume
= mpd_status_get_consume(c
->status
);
428 crossfade
= mpd_status_get_crossfade(c
->status
);
431 if ((c
->events
& MPD_IDLE_DATABASE
) != 0 && was_connected
&&
432 mpdclient_is_connected(c
))
433 screen_status_printf(_("Database updated"));
434 was_connected
= mpdclient_is_connected(c
);
436 /* update title/header window */
437 if (screen
.welcome_source_id
!= 0)
438 paint_top_window("", c
);
441 if (mode_fn
->get_title
!= NULL
) {
442 paint_top_window(mode_fn
->get_title(screen
.buf
,screen
.buf_size
), c
);
444 paint_top_window("", c
);
446 /* update progress window */
447 update_progress_window(c
, false);
449 /* update status window */
450 status_bar_paint(&screen
.status_bar
, c
->status
, c
->song
);
452 /* update the main window */
453 if (mode_fn
->update
!= NULL
)
456 /* move the cursor to the origin */
458 if (!options
.hardware_cursor
)
459 wmove(screen
.main_window
.w
, 0, 0);
461 wnoutrefresh(screen
.main_window
.w
);
463 /* tell curses to update */
469 screen_get_mouse_event(struct mpdclient
*c
, unsigned long *bstate
, int *row
)
473 /* retrieve the mouse event from curses */
479 /* calculate the selected row in the list window */
480 *row
= event
.y
- screen
.title_bar
.window
.rows
;
481 /* copy button state bits */
482 *bstate
= event
.bstate
;
483 /* if button 2 was pressed switch screen */
484 if (event
.bstate
& BUTTON2_CLICKED
) {
485 screen_cmd(c
, CMD_SCREEN_NEXT
);
494 screen_cmd(struct mpdclient
*c
, command_t cmd
)
497 if (screen
.welcome_source_id
!= 0) {
498 g_source_remove(screen
.welcome_source_id
);
499 screen
.welcome_source_id
= 0;
503 if (mode_fn
->cmd
!= NULL
&& mode_fn
->cmd(c
, cmd
))
506 if (handle_player_command(c
, cmd
))
510 case CMD_TOGGLE_FIND_WRAP
:
511 options
.find_wrap
= !options
.find_wrap
;
512 screen_status_printf(options
.find_wrap
?
513 _("Find mode: Wrapped") :
514 _("Find mode: Normal"));
516 case CMD_TOGGLE_AUTOCENTER
:
517 options
.auto_center
= !options
.auto_center
;
518 screen_status_printf(options
.auto_center
?
519 _("Auto center mode: On") :
520 _("Auto center mode: Off"));
522 case CMD_SCREEN_UPDATE
:
525 case CMD_SCREEN_PREVIOUS
:
526 screen_next_mode(c
, -1);
528 case CMD_SCREEN_NEXT
:
529 screen_next_mode(c
, 1);
531 case CMD_SCREEN_PLAY
:
532 screen_switch(&screen_queue
, c
);
534 case CMD_SCREEN_FILE
:
535 screen_switch(&screen_browse
, c
);
537 #ifdef ENABLE_HELP_SCREEN
538 case CMD_SCREEN_HELP
:
539 screen_switch(&screen_help
, c
);
542 #ifdef ENABLE_SEARCH_SCREEN
543 case CMD_SCREEN_SEARCH
:
544 screen_switch(&screen_search
, c
);
547 #ifdef ENABLE_ARTIST_SCREEN
548 case CMD_SCREEN_ARTIST
:
549 screen_switch(&screen_artist
, c
);
552 #ifdef ENABLE_SONG_SCREEN
553 case CMD_SCREEN_SONG
:
554 screen_switch(&screen_song
, c
);
557 #ifdef ENABLE_KEYDEF_SCREEN
558 case CMD_SCREEN_KEYDEF
:
559 screen_switch(&screen_keydef
, c
);
562 #ifdef ENABLE_LYRICS_SCREEN
563 case CMD_SCREEN_LYRICS
:
564 screen_switch(&screen_lyrics
, c
);
567 #ifdef ENABLE_OUTPUTS_SCREEN
568 case CMD_SCREEN_OUTPUTS
:
569 screen_switch(&screen_outputs
, c
);
572 #ifdef ENABLE_CHAT_SCREEN
573 case CMD_SCREEN_CHAT
:
574 screen_switch(&screen_chat
, c
);
577 case CMD_SCREEN_SWAP
:
578 screen_swap(c
, NULL
);