1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2017 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
);
223 /* progress window */
224 progress_bar_resize(&screen
.progress_bar
, screen
.cols
,
228 status_bar_resize(&screen
.status_bar
, screen
.cols
, screen
.rows
- 1, 0);
230 screen
.buf_size
= screen
.cols
;
232 screen
.buf
= g_malloc(screen
.cols
);
234 /* resize all screens */
235 screen_list_resize(screen
.main_window
.cols
, screen
.main_window
.rows
);
237 /* ? - without this the cursor becomes visible with aterm & Eterm */
246 welcome_timer_callback(gpointer data
)
248 struct mpdclient
*c
= data
;
251 screen
.welcome_source_id
= 0;
254 paint_top_window(mode_fn
->get_title
!= NULL
255 ? mode_fn
->get_title(screen
.buf
, screen
.buf_size
)
265 screen_init(struct mpdclient
*c
)
267 if (COLS
< SCREEN_MIN_COLS
|| LINES
< SCREEN_MIN_ROWS
) {
268 fprintf(stderr
, "%s\n", _("Error: Screen too small"));
275 screen
.buf
= g_malloc(screen
.cols
);
276 screen
.buf_size
= screen
.cols
;
277 screen
.findbuf
= NULL
;
280 if (options
.welcome_screen_list
)
281 screen
.welcome_source_id
=
282 g_timeout_add(SCREEN_WELCOME_TIME
* 1000,
283 welcome_timer_callback
, c
);
286 /* create top window */
287 title_bar_init(&screen
.title_bar
, screen
.cols
, 0, 0);
289 /* create main window */
290 window_init(&screen
.main_window
, screen
.rows
- 4, screen
.cols
, 2, 0);
292 if (!options
.hardware_cursor
)
293 leaveok(screen
.main_window
.w
, TRUE
);
295 keypad(screen
.main_window
.w
, TRUE
);
297 /* create progress window */
298 progress_bar_init(&screen
.progress_bar
, screen
.cols
,
301 /* create status window */
302 status_bar_init(&screen
.status_bar
, screen
.cols
, screen
.rows
- 1, 0);
305 if (options
.enable_colors
) {
306 /* set background attributes */
307 wbkgd(stdscr
, COLOR_PAIR(COLOR_LIST
));
308 wbkgd(screen
.main_window
.w
, COLOR_PAIR(COLOR_LIST
));
309 wbkgd(screen
.title_bar
.window
.w
, COLOR_PAIR(COLOR_TITLE
));
310 wbkgd(screen
.progress_bar
.window
.w
,
311 COLOR_PAIR(COLOR_PROGRESSBAR
));
312 wbkgd(screen
.status_bar
.window
.w
, COLOR_PAIR(COLOR_STATUS
));
313 colors_use(screen
.progress_bar
.window
.w
, COLOR_PROGRESSBAR
);
317 /* initialize screens */
318 screen_list_init(screen
.main_window
.w
,
319 screen
.main_window
.cols
, screen
.main_window
.rows
);
321 if (mode_fn
->open
!= NULL
)
326 screen_refresh(struct mpdclient
*c
, bool main_dirty
)
328 /* update title/header window */
331 screen
.welcome_source_id
== 0 &&
333 mode_fn
->get_title
!= NULL
334 ? mode_fn
->get_title(screen
.buf
, screen
.buf_size
)
336 assert(title
!= NULL
);
337 paint_top_window(title
, c
);
339 /* paint the bottom window */
341 update_progress_window(c
, true);
342 status_bar_paint(&screen
.status_bar
, c
->status
, c
->song
);
344 /* paint the main window */
347 wclear(screen
.main_window
.w
);
348 if (mode_fn
->paint
!= NULL
)
352 /* move the cursor to the origin */
354 if (!options
.hardware_cursor
)
355 wmove(screen
.main_window
.w
, 0, 0);
357 wnoutrefresh(screen
.main_window
.w
);
359 /* tell curses to update */
364 screen_paint(struct mpdclient
*c
)
366 screen_refresh(c
, true);
370 screen_update(struct mpdclient
*c
)
373 static bool was_connected
;
374 static bool initialized
= false;
376 static bool random_enabled
;
379 static unsigned crossfade
;
381 /* print a message if mpd status has changed */
382 if ((c
->events
& MPD_IDLE_OPTIONS
) && c
->status
!= NULL
) {
384 repeat
= mpd_status_get_repeat(c
->status
);
385 random_enabled
= mpd_status_get_random(c
->status
);
386 single
= mpd_status_get_single(c
->status
);
387 consume
= mpd_status_get_consume(c
->status
);
388 crossfade
= mpd_status_get_crossfade(c
->status
);
392 if (repeat
!= mpd_status_get_repeat(c
->status
))
393 screen_status_printf(mpd_status_get_repeat(c
->status
) ?
394 _("Repeat mode is on") :
395 _("Repeat mode is off"));
397 if (random_enabled
!= mpd_status_get_random(c
->status
))
398 screen_status_printf(mpd_status_get_random(c
->status
) ?
399 _("Random mode is on") :
400 _("Random mode is off"));
402 if (single
!= mpd_status_get_single(c
->status
))
403 screen_status_printf(mpd_status_get_single(c
->status
) ?
404 /* "single" mode means
409 _("Single mode is on") :
410 _("Single mode is off"));
412 if (consume
!= mpd_status_get_consume(c
->status
))
413 screen_status_printf(mpd_status_get_consume(c
->status
) ?
414 /* "consume" mode means
415 that MPD removes each
418 _("Consume mode is on") :
419 _("Consume mode is off"));
421 if (crossfade
!= mpd_status_get_crossfade(c
->status
))
422 screen_status_printf(_("Crossfade %d seconds"),
423 mpd_status_get_crossfade(c
->status
));
425 repeat
= mpd_status_get_repeat(c
->status
);
426 random_enabled
= mpd_status_get_random(c
->status
);
427 single
= mpd_status_get_single(c
->status
);
428 consume
= mpd_status_get_consume(c
->status
);
429 crossfade
= mpd_status_get_crossfade(c
->status
);
432 if ((c
->events
& MPD_IDLE_DATABASE
) != 0 && was_connected
&&
433 mpdclient_is_connected(c
))
434 screen_status_printf(_("Database updated"));
435 was_connected
= mpdclient_is_connected(c
);
438 /* update the main window */
439 if (mode_fn
->update
!= NULL
)
442 screen_refresh(c
, false);
447 screen_get_mouse_event(struct mpdclient
*c
, unsigned long *bstate
, int *row
)
451 /* retrieve the mouse event from curses */
457 /* calculate the selected row in the list window */
458 *row
= event
.y
- screen
.title_bar
.window
.rows
;
459 /* copy button state bits */
460 *bstate
= event
.bstate
;
461 /* if button 2 was pressed switch screen */
462 if (event
.bstate
& BUTTON2_CLICKED
) {
463 screen_cmd(c
, CMD_SCREEN_NEXT
);
472 screen_cmd(struct mpdclient
*c
, command_t cmd
)
475 if (screen
.welcome_source_id
!= 0) {
476 g_source_remove(screen
.welcome_source_id
);
477 screen
.welcome_source_id
= 0;
481 if (mode_fn
->cmd
!= NULL
&& mode_fn
->cmd(c
, cmd
))
484 if (handle_player_command(c
, cmd
))
488 case CMD_TOGGLE_FIND_WRAP
:
489 options
.find_wrap
= !options
.find_wrap
;
490 screen_status_printf(options
.find_wrap
?
491 _("Find mode: Wrapped") :
492 _("Find mode: Normal"));
494 case CMD_TOGGLE_AUTOCENTER
:
495 options
.auto_center
= !options
.auto_center
;
496 screen_status_printf(options
.auto_center
?
497 _("Auto center mode: On") :
498 _("Auto center mode: Off"));
500 case CMD_SCREEN_UPDATE
:
503 case CMD_SCREEN_PREVIOUS
:
504 screen_next_mode(c
, -1);
506 case CMD_SCREEN_NEXT
:
507 screen_next_mode(c
, 1);
509 case CMD_SCREEN_PLAY
:
510 screen_switch(&screen_queue
, c
);
512 case CMD_SCREEN_FILE
:
513 screen_switch(&screen_browse
, c
);
515 #ifdef ENABLE_HELP_SCREEN
516 case CMD_SCREEN_HELP
:
517 screen_switch(&screen_help
, c
);
520 #ifdef ENABLE_SEARCH_SCREEN
521 case CMD_SCREEN_SEARCH
:
522 screen_switch(&screen_search
, c
);
525 #ifdef ENABLE_ARTIST_SCREEN
526 case CMD_SCREEN_ARTIST
:
527 screen_switch(&screen_artist
, c
);
530 #ifdef ENABLE_SONG_SCREEN
531 case CMD_SCREEN_SONG
:
532 screen_switch(&screen_song
, c
);
535 #ifdef ENABLE_KEYDEF_SCREEN
536 case CMD_SCREEN_KEYDEF
:
537 screen_switch(&screen_keydef
, c
);
540 #ifdef ENABLE_LYRICS_SCREEN
541 case CMD_SCREEN_LYRICS
:
542 screen_switch(&screen_lyrics
, c
);
545 #ifdef ENABLE_OUTPUTS_SCREEN
546 case CMD_SCREEN_OUTPUTS
:
547 screen_switch(&screen_outputs
, c
);
550 #ifdef ENABLE_CHAT_SCREEN
551 case CMD_SCREEN_CHAT
:
552 screen_switch(&screen_chat
, c
);
555 case CMD_SCREEN_SWAP
:
556 screen_swap(c
, NULL
);