1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2009 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_message.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"
43 #include <mpd/client.h>
52 /** welcome message time [s] */
53 static const GTime SCREEN_WELCOME_TIME
= 10;
56 /* minimum window size */
57 static const int SCREEN_MIN_COLS
= 14;
58 static const int SCREEN_MIN_ROWS
= 5;
63 static const struct screen_functions
*mode_fn
= &screen_queue
;
64 static const struct screen_functions
*mode_fn_prev
= &screen_queue
;
67 screen_is_visible(const struct screen_functions
*sf
)
73 screen_switch(const struct screen_functions
*sf
, struct mpdclient
*c
)
80 mode_fn_prev
= mode_fn
;
82 /* close the old mode */
83 if (mode_fn
->close
!= NULL
)
86 /* get functions for the new mode */
89 /* open the new mode */
90 if (mode_fn
->open
!= NULL
)
97 screen_swap(struct mpdclient
*c
, const struct mpd_song
*song
)
102 { /* just a hack to make the ifdefs less ugly */ }
103 #ifdef ENABLE_SONG_SCREEN
104 if (mode_fn_prev
== &screen_song
)
105 screen_song_switch(c
, song
);
107 #ifdef ENABLE_LYRICS_SCREEN
108 else if (mode_fn_prev
== &screen_lyrics
)
109 screen_lyrics_switch(c
, song
, true);
112 screen_switch(mode_fn_prev
, c
);
115 screen_switch(mode_fn_prev
, c
);
119 find_configured_screen(const char *name
)
123 for (i
= 0; options
.screen_list
[i
] != NULL
; ++i
)
124 if (strcmp(options
.screen_list
[i
], name
) == 0)
131 screen_next_mode(struct mpdclient
*c
, int offset
)
133 int max
= g_strv_length(options
.screen_list
);
135 const struct screen_functions
*sf
;
137 /* find current screen */
138 current
= find_configured_screen(screen_get_name(mode_fn
));
139 next
= current
+ offset
;
145 sf
= 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
)
159 unsigned elapsed
, duration
;
161 if (c
->song
!= NULL
&& seek_id
== (int)mpd_song_get_id(c
->song
))
162 elapsed
= seek_target_time
;
163 else if (c
->status
!= NULL
)
164 elapsed
= mpd_status_get_elapsed_time(c
->status
);
168 duration
= c
->status
!= NULL
&&
169 (mpd_status_get_state(c
->status
) == MPD_STATE_PLAY
||
170 mpd_status_get_state(c
->status
) == MPD_STATE_PAUSE
)
171 ? mpd_status_get_total_time(c
->status
)
174 if (progress_bar_set(&screen
.progress_bar
, elapsed
, duration
) ||
176 progress_bar_paint(&screen
.progress_bar
);
182 if (mode_fn
->close
!= NULL
)
187 string_list_free(screen
.find_history
);
189 g_free(screen
.findbuf
);
191 title_bar_deinit(&screen
.title_bar
);
192 delwin(screen
.main_window
.w
);
193 progress_bar_deinit(&screen
.progress_bar
);
194 status_bar_deinit(&screen
.status_bar
);
197 if (screen
.welcome_source_id
!= 0)
198 g_source_remove(screen
.welcome_source_id
);
203 screen_resize(struct mpdclient
*c
)
205 if (COLS
<SCREEN_MIN_COLS
|| LINES
<SCREEN_MIN_ROWS
) {
207 fprintf(stderr
, "%s", _("Error: Screen too small"));
211 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 // leaveok(screen.main_window.w, TRUE); temporary disabled
296 keypad(screen
.main_window
.w
, TRUE
);
298 /* create progress window */
299 progress_bar_init(&screen
.progress_bar
, screen
.cols
,
301 progress_bar_paint(&screen
.progress_bar
);
303 /* create status window */
304 status_bar_init(&screen
.status_bar
, screen
.cols
, screen
.rows
- 1, 0);
305 status_bar_paint(&screen
.status_bar
, c
->status
, c
->song
);
308 if (options
.enable_colors
) {
309 /* set background attributes */
310 wbkgd(stdscr
, COLOR_PAIR(COLOR_LIST
));
311 wbkgd(screen
.main_window
.w
, COLOR_PAIR(COLOR_LIST
));
312 wbkgd(screen
.title_bar
.window
.w
, COLOR_PAIR(COLOR_TITLE
));
313 wbkgd(screen
.progress_bar
.window
.w
,
314 COLOR_PAIR(COLOR_PROGRESSBAR
));
315 wbkgd(screen
.status_bar
.window
.w
, COLOR_PAIR(COLOR_STATUS
));
316 colors_use(screen
.progress_bar
.window
.w
, COLOR_PROGRESSBAR
);
322 /* initialize screens */
323 screen_list_init(screen
.main_window
.w
,
324 screen
.main_window
.cols
, screen
.main_window
.rows
);
326 if (mode_fn
->open
!= NULL
)
331 screen_paint(struct mpdclient
*c
)
333 const char *title
= NULL
;
335 if (mode_fn
->get_title
!= NULL
)
336 title
= mode_fn
->get_title(screen
.buf
, screen
.buf_size
);
338 /* paint the title/header window */
340 paint_top_window(title
, c
);
342 paint_top_window("", c
);
344 /* paint the bottom window */
346 update_progress_window(c
, true);
347 status_bar_paint(&screen
.status_bar
, c
->status
, c
->song
);
349 /* paint the main window */
351 wclear(screen
.main_window
.w
);
352 if (mode_fn
->paint
!= NULL
)
355 /* move the cursor to the origin */
357 if (!options
.hardware_cursor
)
358 wmove(screen
.main_window
.w
, 0, 0);
360 wnoutrefresh(screen
.main_window
.w
);
362 /* tell curses to update */
367 screen_update(struct mpdclient
*c
)
370 static bool was_connected
;
371 static bool initialized
= false;
373 static bool random_enabled
;
376 static unsigned crossfade
;
378 /* print a message if mpd status has changed */
379 if ((c
->events
& MPD_IDLE_OPTIONS
) && c
->status
!= NULL
) {
381 repeat
= mpd_status_get_repeat(c
->status
);
382 random_enabled
= mpd_status_get_random(c
->status
);
383 single
= mpd_status_get_single(c
->status
);
384 consume
= mpd_status_get_consume(c
->status
);
385 crossfade
= mpd_status_get_crossfade(c
->status
);
389 if (repeat
!= mpd_status_get_repeat(c
->status
))
390 screen_status_printf(mpd_status_get_repeat(c
->status
) ?
391 _("Repeat mode is on") :
392 _("Repeat mode is off"));
394 if (random_enabled
!= mpd_status_get_random(c
->status
))
395 screen_status_printf(mpd_status_get_random(c
->status
) ?
396 _("Random mode is on") :
397 _("Random mode is off"));
399 if (single
!= mpd_status_get_single(c
->status
))
400 screen_status_printf(mpd_status_get_single(c
->status
) ?
401 /* "single" mode means
406 _("Single mode is on") :
407 _("Single mode is off"));
409 if (consume
!= mpd_status_get_consume(c
->status
))
410 screen_status_printf(mpd_status_get_consume(c
->status
) ?
411 /* "consume" mode means
412 that MPD removes each
415 _("Consume mode is on") :
416 _("Consume mode is off"));
418 if (crossfade
!= mpd_status_get_crossfade(c
->status
))
419 screen_status_printf(_("Crossfade %d seconds"),
420 mpd_status_get_crossfade(c
->status
));
422 repeat
= mpd_status_get_repeat(c
->status
);
423 random_enabled
= mpd_status_get_random(c
->status
);
424 single
= mpd_status_get_single(c
->status
);
425 consume
= mpd_status_get_consume(c
->status
);
426 crossfade
= mpd_status_get_crossfade(c
->status
);
429 if ((c
->events
& MPD_IDLE_DATABASE
) != 0 && was_connected
&&
430 mpdclient_is_connected(c
))
431 screen_status_printf(_("Database updated"));
432 was_connected
= mpdclient_is_connected(c
);
434 /* update title/header window */
435 if (screen
.welcome_source_id
!= 0)
436 paint_top_window("", c
);
439 if (mode_fn
->get_title
!= NULL
) {
440 paint_top_window(mode_fn
->get_title(screen
.buf
,screen
.buf_size
), c
);
442 paint_top_window("", c
);
444 /* update progress window */
445 update_progress_window(c
, false);
447 /* update status window */
448 status_bar_paint(&screen
.status_bar
, c
->status
, c
->song
);
450 /* update the main window */
451 if (mode_fn
->update
!= NULL
)
454 /* move the cursor to the origin */
456 if (!options
.hardware_cursor
)
457 wmove(screen
.main_window
.w
, 0, 0);
459 wnoutrefresh(screen
.main_window
.w
);
461 /* tell curses to update */
467 screen_get_mouse_event(struct mpdclient
*c
, unsigned long *bstate
, int *row
)
471 /* retrieve the mouse event from ncurses */
473 /* calculate the selected row in the list window */
474 *row
= event
.y
- screen
.title_bar
.window
.rows
;
475 /* copy button state bits */
476 *bstate
= event
.bstate
;
477 /* if button 2 was pressed switch screen */
478 if (event
.bstate
& BUTTON2_CLICKED
) {
479 screen_cmd(c
, CMD_SCREEN_NEXT
);
488 screen_cmd(struct mpdclient
*c
, command_t cmd
)
491 if (screen
.welcome_source_id
!= 0) {
492 g_source_remove(screen
.welcome_source_id
);
493 screen
.welcome_source_id
= 0;
497 if (mode_fn
->cmd
!= NULL
&& mode_fn
->cmd(c
, cmd
))
500 if (handle_player_command(c
, cmd
))
504 case CMD_TOGGLE_FIND_WRAP
:
505 options
.find_wrap
= !options
.find_wrap
;
506 screen_status_printf(options
.find_wrap
?
507 _("Find mode: Wrapped") :
508 _("Find mode: Normal"));
510 case CMD_TOGGLE_AUTOCENTER
:
511 options
.auto_center
= !options
.auto_center
;
512 screen_status_printf(options
.auto_center
?
513 _("Auto center mode: On") :
514 _("Auto center mode: Off"));
516 case CMD_SCREEN_UPDATE
:
519 case CMD_SCREEN_PREVIOUS
:
520 screen_next_mode(c
, -1);
522 case CMD_SCREEN_NEXT
:
523 screen_next_mode(c
, 1);
525 case CMD_SCREEN_PLAY
:
526 screen_switch(&screen_queue
, c
);
528 case CMD_SCREEN_FILE
:
529 screen_switch(&screen_browse
, c
);
531 #ifdef ENABLE_HELP_SCREEN
532 case CMD_SCREEN_HELP
:
533 screen_switch(&screen_help
, c
);
536 #ifdef ENABLE_SEARCH_SCREEN
537 case CMD_SCREEN_SEARCH
:
538 screen_switch(&screen_search
, c
);
541 #ifdef ENABLE_ARTIST_SCREEN
542 case CMD_SCREEN_ARTIST
:
543 screen_switch(&screen_artist
, c
);
546 #ifdef ENABLE_SONG_SCREEN
547 case CMD_SCREEN_SONG
:
548 screen_switch(&screen_song
, c
);
551 #ifdef ENABLE_KEYDEF_SCREEN
552 case CMD_SCREEN_KEYDEF
:
553 screen_switch(&screen_keydef
, c
);
556 #ifdef ENABLE_LYRICS_SCREEN
557 case CMD_SCREEN_LYRICS
:
558 screen_switch(&screen_lyrics
, c
);
561 #ifdef ENABLE_OUTPUTS_SCREEN
562 case CMD_SCREEN_OUTPUTS
:
563 screen_switch(&screen_outputs
, c
);
565 case CMD_SCREEN_SWAP
:
566 screen_swap(c
, NULL
);