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
;
250 screen
.welcome_source_id
= 0;
252 paint_top_window(mode_fn
->get_title
!= NULL
253 ? mode_fn
->get_title(screen
.buf
, screen
.buf_size
)
263 screen_init(struct mpdclient
*c
)
265 if (COLS
< SCREEN_MIN_COLS
|| LINES
< SCREEN_MIN_ROWS
) {
266 fprintf(stderr
, "%s\n", _("Error: Screen too small"));
273 screen
.buf
= g_malloc(screen
.cols
);
274 screen
.buf_size
= screen
.cols
;
275 screen
.findbuf
= NULL
;
278 if (options
.welcome_screen_list
)
279 screen
.welcome_source_id
=
280 g_timeout_add(SCREEN_WELCOME_TIME
* 1000,
281 welcome_timer_callback
, c
);
284 /* create top window */
285 title_bar_init(&screen
.title_bar
, screen
.cols
, 0, 0);
287 /* create main window */
288 window_init(&screen
.main_window
, screen
.rows
- 4, screen
.cols
, 2, 0);
290 if (!options
.hardware_cursor
)
291 leaveok(screen
.main_window
.w
, TRUE
);
293 keypad(screen
.main_window
.w
, TRUE
);
295 /* create progress window */
296 progress_bar_init(&screen
.progress_bar
, screen
.cols
,
299 /* create status window */
300 status_bar_init(&screen
.status_bar
, screen
.cols
, screen
.rows
- 1, 0);
303 if (options
.enable_colors
) {
304 /* set background attributes */
305 wbkgd(stdscr
, COLOR_PAIR(COLOR_LIST
));
306 wbkgd(screen
.main_window
.w
, COLOR_PAIR(COLOR_LIST
));
307 wbkgd(screen
.title_bar
.window
.w
, COLOR_PAIR(COLOR_TITLE
));
308 wbkgd(screen
.progress_bar
.window
.w
,
309 COLOR_PAIR(COLOR_PROGRESSBAR
));
310 wbkgd(screen
.status_bar
.window
.w
, COLOR_PAIR(COLOR_STATUS
));
311 colors_use(screen
.progress_bar
.window
.w
, COLOR_PROGRESSBAR
);
315 /* initialize screens */
316 screen_list_init(screen
.main_window
.w
,
317 screen
.main_window
.cols
, screen
.main_window
.rows
);
319 if (mode_fn
->open
!= NULL
)
324 screen_refresh(struct mpdclient
*c
, bool main_dirty
)
326 /* update title/header window */
329 screen
.welcome_source_id
== 0 &&
331 mode_fn
->get_title
!= NULL
332 ? mode_fn
->get_title(screen
.buf
, screen
.buf_size
)
334 assert(title
!= NULL
);
335 paint_top_window(title
, c
);
337 /* paint the bottom window */
339 update_progress_window(c
, true);
340 status_bar_paint(&screen
.status_bar
, c
->status
, c
->song
);
342 /* paint the main window */
345 wclear(screen
.main_window
.w
);
346 if (mode_fn
->paint
!= NULL
)
350 /* move the cursor to the origin */
352 if (!options
.hardware_cursor
)
353 wmove(screen
.main_window
.w
, 0, 0);
355 wnoutrefresh(screen
.main_window
.w
);
357 /* tell curses to update */
362 screen_paint(struct mpdclient
*c
)
364 screen_refresh(c
, true);
368 screen_update(struct mpdclient
*c
)
371 static bool was_connected
;
372 static bool initialized
= false;
374 static bool random_enabled
;
377 static unsigned crossfade
;
379 /* print a message if mpd status has changed */
380 if ((c
->events
& MPD_IDLE_OPTIONS
) && c
->status
!= NULL
) {
382 repeat
= mpd_status_get_repeat(c
->status
);
383 random_enabled
= mpd_status_get_random(c
->status
);
384 single
= mpd_status_get_single(c
->status
);
385 consume
= mpd_status_get_consume(c
->status
);
386 crossfade
= mpd_status_get_crossfade(c
->status
);
390 if (repeat
!= mpd_status_get_repeat(c
->status
))
391 screen_status_printf(mpd_status_get_repeat(c
->status
) ?
392 _("Repeat mode is on") :
393 _("Repeat mode is off"));
395 if (random_enabled
!= mpd_status_get_random(c
->status
))
396 screen_status_printf(mpd_status_get_random(c
->status
) ?
397 _("Random mode is on") :
398 _("Random mode is off"));
400 if (single
!= mpd_status_get_single(c
->status
))
401 screen_status_printf(mpd_status_get_single(c
->status
) ?
402 /* "single" mode means
407 _("Single mode is on") :
408 _("Single mode is off"));
410 if (consume
!= mpd_status_get_consume(c
->status
))
411 screen_status_printf(mpd_status_get_consume(c
->status
) ?
412 /* "consume" mode means
413 that MPD removes each
416 _("Consume mode is on") :
417 _("Consume mode is off"));
419 if (crossfade
!= mpd_status_get_crossfade(c
->status
))
420 screen_status_printf(_("Crossfade %d seconds"),
421 mpd_status_get_crossfade(c
->status
));
423 repeat
= mpd_status_get_repeat(c
->status
);
424 random_enabled
= mpd_status_get_random(c
->status
);
425 single
= mpd_status_get_single(c
->status
);
426 consume
= mpd_status_get_consume(c
->status
);
427 crossfade
= mpd_status_get_crossfade(c
->status
);
430 if ((c
->events
& MPD_IDLE_DATABASE
) != 0 && was_connected
&&
431 mpdclient_is_connected(c
))
432 screen_status_printf(_("Database updated"));
433 was_connected
= mpdclient_is_connected(c
);
436 /* update the main window */
437 if (mode_fn
->update
!= NULL
)
440 screen_refresh(c
, false);
445 screen_get_mouse_event(struct mpdclient
*c
, unsigned long *bstate
, int *row
)
449 /* retrieve the mouse event from curses */
455 /* calculate the selected row in the list window */
456 *row
= event
.y
- screen
.title_bar
.window
.rows
;
457 /* copy button state bits */
458 *bstate
= event
.bstate
;
459 /* if button 2 was pressed switch screen */
460 if (event
.bstate
& BUTTON2_CLICKED
) {
461 screen_cmd(c
, CMD_SCREEN_NEXT
);
470 screen_cmd(struct mpdclient
*c
, command_t cmd
)
473 if (screen
.welcome_source_id
!= 0) {
474 g_source_remove(screen
.welcome_source_id
);
475 screen
.welcome_source_id
= 0;
479 if (mode_fn
->cmd
!= NULL
&& mode_fn
->cmd(c
, cmd
))
482 if (handle_player_command(c
, cmd
))
486 case CMD_TOGGLE_FIND_WRAP
:
487 options
.find_wrap
= !options
.find_wrap
;
488 screen_status_printf(options
.find_wrap
?
489 _("Find mode: Wrapped") :
490 _("Find mode: Normal"));
492 case CMD_TOGGLE_AUTOCENTER
:
493 options
.auto_center
= !options
.auto_center
;
494 screen_status_printf(options
.auto_center
?
495 _("Auto center mode: On") :
496 _("Auto center mode: Off"));
498 case CMD_SCREEN_UPDATE
:
501 case CMD_SCREEN_PREVIOUS
:
502 screen_next_mode(c
, -1);
504 case CMD_SCREEN_NEXT
:
505 screen_next_mode(c
, 1);
507 case CMD_SCREEN_PLAY
:
508 screen_switch(&screen_queue
, c
);
510 case CMD_SCREEN_FILE
:
511 screen_switch(&screen_browse
, c
);
513 #ifdef ENABLE_HELP_SCREEN
514 case CMD_SCREEN_HELP
:
515 screen_switch(&screen_help
, c
);
518 #ifdef ENABLE_SEARCH_SCREEN
519 case CMD_SCREEN_SEARCH
:
520 screen_switch(&screen_search
, c
);
523 #ifdef ENABLE_ARTIST_SCREEN
524 case CMD_SCREEN_ARTIST
:
525 screen_switch(&screen_artist
, c
);
528 #ifdef ENABLE_SONG_SCREEN
529 case CMD_SCREEN_SONG
:
530 screen_switch(&screen_song
, c
);
533 #ifdef ENABLE_KEYDEF_SCREEN
534 case CMD_SCREEN_KEYDEF
:
535 screen_switch(&screen_keydef
, c
);
538 #ifdef ENABLE_LYRICS_SCREEN
539 case CMD_SCREEN_LYRICS
:
540 screen_switch(&screen_lyrics
, c
);
543 #ifdef ENABLE_OUTPUTS_SCREEN
544 case CMD_SCREEN_OUTPUTS
:
545 screen_switch(&screen_outputs
, c
);
548 #ifdef ENABLE_CHAT_SCREEN
549 case CMD_SCREEN_CHAT
:
550 screen_switch(&screen_chat
, c
);
553 case CMD_SCREEN_SWAP
:
554 screen_swap(c
, NULL
);