1 /*****************************************************************************
2 * ncurses.c : NCurses interface for vlc
3 *****************************************************************************
4 * Copyright © 2001-2010 the VideoLAN team
7 * Authors: Sam Hocevar <sam@zoy.org>
8 * Laurent Aimar <fenrir@via.ecp.fr>
9 * Yoann Peronneau <yoann@videolan.org>
10 * Derk-Jan Hartman <hartman at videolan dot org>
11 * Rafaël Carré <funman@videolanorg>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
29 * Note that when we use wide characters (and link with libncursesw),
30 * we assume that an UTF8 locale is used (or compatible, such as ASCII).
31 * Other characters encodings are not supported.
34 /*****************************************************************************
36 *****************************************************************************/
41 #include <vlc_common.h>
42 #include <vlc_plugin.h>
45 # define _XOPEN_SOURCE_EXTENDED 1
51 #include <vlc_interface.h>
54 #include <vlc_charset.h>
55 #include <vlc_input.h>
57 #include <vlc_playlist.h>
63 #ifdef HAVE_SYS_STAT_H
64 # include <sys/stat.h>
67 #define SEARCH_CHAIN_SIZE 20
68 #define OPEN_CHAIN_SIZE 50
70 /*****************************************************************************
72 *****************************************************************************/
73 static int Open (vlc_object_t *);
74 static void Close (vlc_object_t *);
76 /*****************************************************************************
78 *****************************************************************************/
80 #define BROWSE_TEXT N_("Filebrowser starting point")
81 #define BROWSE_LONGTEXT N_(\
82 "This option allows you to specify the directory the ncurses filebrowser " \
83 "will show you initially.")
86 set_shortname("Ncurses")
87 set_description(N_("Ncurses interface"))
88 set_capability("interface", 10)
89 set_category(CAT_INTERFACE)
90 set_subcategory(SUBCAT_INTERFACE_MAIN)
91 set_callbacks(Open, Close)
92 add_shortcut("curses")
93 add_directory("browse-dir", NULL, BROWSE_TEXT, BROWSE_LONGTEXT, false)
96 /*****************************************************************************
97 * intf_sys_t: description and status of ncurses interface
98 *****************************************************************************/
141 playlist_item_t *p_item;
146 input_thread_t *p_input;
147 playlist_t *p_playlist;
150 bool b_color_started;
160 int i_box_lines_total;
163 int i_box_plidx; /* Playlist index */
164 int b_box_plidx_follow;
165 int i_box_bidx; /* browser index */
167 playlist_item_t *p_node; /* current node */
171 msg_subscription_t* p_sub; /* message bank subscription */
173 char *psz_search_chain; /* for playlist searching */
174 char *psz_old_search; /* for searching next */
177 char *psz_open_chain;
178 #ifndef HAVE_NCURSESW
179 char psz_partial_keys[7];
182 char *psz_current_dir;
184 struct dir_entry_t **pp_dir_entries;
185 bool b_show_hidden_files;
187 int i_current_view; /* playlist view */
188 struct pl_item_t **pp_plist;
190 bool b_need_update; /* for playlist view */
193 /*****************************************************************************
195 *****************************************************************************/
197 static void DirsDestroy(intf_sys_t *p_sys)
199 while (p_sys->i_dir_entries)
201 struct dir_entry_t *p_dir_entry;
202 p_dir_entry = p_sys->pp_dir_entries[--p_sys->i_dir_entries];
203 free(p_dir_entry->psz_path);
206 free(p_sys->pp_dir_entries);
207 p_sys->pp_dir_entries = NULL;
210 static int comp_dir_entries(const void *pp_dir_entry1, const void *pp_dir_entry2)
212 struct dir_entry_t *p_dir_entry1 = *(struct dir_entry_t**)pp_dir_entry1;
213 struct dir_entry_t *p_dir_entry2 = *(struct dir_entry_t**)pp_dir_entry2;
215 if (p_dir_entry1->b_file == p_dir_entry2->b_file)
216 return strcasecmp(p_dir_entry1->psz_path, p_dir_entry2->psz_path);
218 return p_dir_entry1->b_file ? 1 : -1;
221 static void ReadDir(intf_thread_t *p_intf)
223 intf_sys_t *p_sys = p_intf->p_sys;
226 if (!p_sys->psz_current_dir || !*p_sys->psz_current_dir)
228 msg_Dbg(p_intf, "no current dir set");
235 p_current_dir = vlc_opendir(p_sys->psz_current_dir);
239 /* something went bad, get out of here ! */
240 msg_Warn(p_intf, "cannot open directory `%s' (%m)",
241 p_sys->psz_current_dir);
245 /* Clean the old shit */
248 /* while we still have entries in the directory */
249 while ((psz_entry = vlc_readdir(p_current_dir)))
252 struct stat stat_data;
254 struct dir_entry_t *p_dir_entry;
255 char *psz_uri = NULL;
257 if (!p_sys->b_show_hidden_files)
258 if (*psz_entry == '.' && strcmp(psz_entry, ".."))
261 if (asprintf(&psz_uri, "%s/%s", p_sys->psz_current_dir, psz_entry) == -1)
267 if (!(p_dir_entry = malloc(sizeof *p_dir_entry)))
270 p_dir_entry->b_file =
272 vlc_stat(psz_uri, &stat_data) || !S_ISDIR(stat_data.st_mode)
273 /*#elif defined(DT_DIR)
274 !(p_dir_content->d_type & DT_DIR)*/
280 p_dir_entry->psz_path = strdup(psz_entry);
281 INSERT_ELEM(p_sys->pp_dir_entries, p_sys->i_dir_entries,
282 p_sys->i_dir_entries, p_dir_entry);
290 qsort(p_sys->pp_dir_entries, p_sys->i_dir_entries,
291 sizeof(struct dir_entry_t*), &comp_dir_entries);
293 closedir(p_current_dir);
296 /*****************************************************************************
298 *****************************************************************************/
300 static void PlaylistDestroy(intf_sys_t *p_sys)
302 while (p_sys->i_plist_entries)
304 struct pl_item_t *p_pl_item = p_sys->pp_plist[--p_sys->i_plist_entries];
305 free(p_pl_item->psz_display);
308 free(p_sys->pp_plist);
309 p_sys->pp_plist = NULL;
312 static void PlaylistAddNode(intf_thread_t *p_intf, playlist_item_t *p_node,
313 int i, const char *c)
315 intf_sys_t *p_sys = p_intf->p_sys;
316 playlist_item_t *p_child;
319 for(k = 0; k < p_node->i_children; k++)
322 p_child = p_node->pp_children[k];
323 char *psz_name = input_item_GetTitleFbName(p_child->p_input);
327 if (asprintf(&psz_display, "%s%c-%s", c,
328 k == p_node->i_children - 1 ? '`' : '|', psz_name) == -1)
331 else if (asprintf(&psz_display, " %s", psz_name) == -1)
335 struct pl_item_t *p_pl_item = malloc(sizeof(struct pl_item_t));
338 p_pl_item->psz_display = psz_display;
339 p_pl_item->p_item = p_child;
340 INSERT_ELEM(p_sys->pp_plist, p_sys->i_plist_entries,
341 p_sys->i_plist_entries, p_pl_item);
344 if (p_child->i_children > 0)
347 if (asprintf(&psz_tmp, "%s%c ", c,
348 k == p_node->i_children - 1 ? ' ' : '|') == -1)
350 PlaylistAddNode(p_intf, p_child, i,
351 strlen(c) ? psz_tmp : " ");
357 static playlist_item_t *PlaylistGetRoot(intf_thread_t *p_intf)
359 intf_sys_t *p_sys = p_intf->p_sys;
360 playlist_t *p_playlist = pl_Get(p_intf);
361 playlist_item_t *p_item;
363 switch(p_sys->i_current_view)
366 p_item = p_playlist->p_root_category;
369 p_item = p_playlist->p_root_onelevel;
374 static void PlaylistRebuild(intf_thread_t *p_intf)
376 intf_sys_t *p_sys = p_intf->p_sys;
377 playlist_t *p_playlist = pl_Get(p_intf);
381 /* First clear the old one */
382 PlaylistDestroy(p_sys);
384 /* Build the new one */
385 PlaylistAddNode(p_intf, PlaylistGetRoot(p_intf), 0, "");
387 p_sys->b_need_update = false;
392 static int PlaylistChanged(vlc_object_t *p_this, const char *psz_variable,
393 vlc_value_t oval, vlc_value_t nval, void *param)
395 VLC_UNUSED(p_this); VLC_UNUSED(psz_variable);
396 VLC_UNUSED(oval); VLC_UNUSED(nval);
397 intf_thread_t *p_intf = (intf_thread_t *)param;
398 playlist_t *p_playlist = pl_Get(p_intf);
399 p_intf->p_sys->b_need_update = true;
400 p_intf->p_sys->p_node = playlist_CurrentPlayingItem(p_playlist) ? playlist_CurrentPlayingItem(p_playlist)->p_parent : NULL;
405 /* This function have to be called with the playlist locked */
406 static inline bool PlaylistIsPlaying(playlist_t *p_playlist,
407 playlist_item_t *p_item)
409 playlist_item_t *p_played_item = playlist_CurrentPlayingItem(p_playlist);
410 return(p_item && p_played_item && p_item->p_input && p_played_item->p_input
411 && p_item->p_input->i_id == p_played_item->p_input->i_id);
414 static int SubSearchPlaylist(intf_thread_t *p_intf, char *psz_searchstring,
415 int i_start, int i_stop)
417 intf_sys_t *p_sys = p_intf->p_sys;
420 for(i = i_start + 1; i < i_stop; i++)
421 if (strcasestr(p_sys->pp_plist[i]->psz_display, psz_searchstring))
430 static void SearchPlaylist(intf_thread_t *p_intf, char *psz_searchstring)
435 intf_sys_t *p_sys = p_intf->p_sys;
437 if (p_sys->i_before_search >= 0)
438 i_first = p_sys->i_before_search;
440 if ((! psz_searchstring) || strlen(psz_searchstring) <= 0)
442 p_sys->i_box_plidx = p_sys->i_before_search;
446 i_max = p_sys->i_plist_entries;
448 i_item = SubSearchPlaylist(p_intf, psz_searchstring, i_first + 1, i_max);
450 i_item = SubSearchPlaylist(p_intf, psz_searchstring, 0, i_first);
452 if (i_item < 0 || i_item >= i_max) return;
454 p_sys->i_box_plidx = i_item;
457 static void FindIndex(intf_thread_t *p_intf, playlist_t *p_playlist)
459 intf_sys_t *p_sys = p_intf->p_sys;
462 if (p_sys->i_box_plidx < p_sys->i_plist_entries && p_sys->i_box_plidx >= 0)
464 playlist_item_t *p_item = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
466 if ((p_item->i_children == 0 && p_item == p_sys->p_node) ||
467 PlaylistIsPlaying(p_playlist, p_item))
474 for(i = 0; i < p_sys->i_plist_entries; i++)
476 playlist_item_t *p_item = p_sys->pp_plist[i]->p_item;
477 if ((p_item->i_children == 0 && p_item == p_sys->p_node) ||
478 PlaylistIsPlaying(p_playlist, p_item))
480 p_sys->i_box_plidx = i;
487 static void start_color_and_pairs(intf_thread_t *p_intf)
489 assert(p_intf->p_sys->b_color && !p_intf->p_sys->b_color_started);
493 p_intf->p_sys->b_color = false;
494 msg_Warn(p_intf, "Terminal doesn't support colors");
500 /* Available colors: BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE */
502 /* untested, in all my terminals, !can_change_color() --funman */
503 if (can_change_color())
504 init_color(COLOR_YELLOW, 960, 500, 0); /* YELLOW -> ORANGE */
507 init_pair(C_TITLE, COLOR_YELLOW, COLOR_BLACK);
509 /* jamaican playlist */
510 init_pair(C_PLAYLIST_1, COLOR_GREEN, COLOR_BLACK);
511 init_pair(C_PLAYLIST_2, COLOR_YELLOW, COLOR_BLACK);
512 init_pair(C_PLAYLIST_3, COLOR_RED, COLOR_BLACK);
514 /* used in DrawBox() */
515 init_pair(C_BOX, COLOR_CYAN, COLOR_BLACK);
516 /* Source, State, Position, Volume, Chapters, etc...*/
517 init_pair(C_STATUS, COLOR_BLUE, COLOR_BLACK);
519 /* VLC messages, keep the order from highest priority to lowest */
522 init_pair(C_INFO, COLOR_BLACK, COLOR_WHITE);
524 init_pair(C_ERROR, COLOR_RED, COLOR_BLACK);
526 init_pair(C_WARNING, COLOR_YELLOW, COLOR_BLACK);
528 init_pair(C_DEBUG, COLOR_WHITE, COLOR_BLACK);
530 /* Category title (help, info, metadata) */
531 init_pair(C_CATEGORY, COLOR_MAGENTA, COLOR_BLACK);
533 /* Folder (BOX_BROWSE) */
534 init_pair(C_FOLDER, COLOR_RED, COLOR_BLACK);
536 p_intf->p_sys->b_color_started = true;
539 /****************************************************************************
541 ****************************************************************************/
543 static void DrawBox(WINDOW *win, int y, int x, int h, int w, const char *title, bool b_color)
551 wcolor_set(win, C_BOX, NULL);
552 if (!title) title = "";
553 i_len = strlen(title);
555 if (i_len > w - 2) i_len = w - 2;
557 mvwaddch(win, y, x, ACS_ULCORNER);
558 mvwhline(win, y, x+1, ACS_HLINE, (w-i_len-2)/2);
559 mvwprintw(win,y, x+1+(w-i_len-2)/2, "%s", title);
560 mvwhline(win, y, x+(w-i_len)/2+i_len, ACS_HLINE, w - 1 - ((w-i_len)/2+i_len));
561 mvwaddch(win, y, x+w-1,ACS_URCORNER);
563 for(i = 0; i < h-2; i++)
565 mvwaddch(win, y+i+1, x, ACS_VLINE);
566 mvwaddch(win, y+i+1, x+w-1, ACS_VLINE);
569 mvwaddch(win, y+h-1, x, ACS_LLCORNER);
570 mvwhline(win, y+h-1, x+1, ACS_HLINE, w - 2);
571 mvwaddch(win, y+h-1, x+w-1, ACS_LRCORNER);
573 wcolor_set(win, C_DEFAULT, NULL);
577 static void DrawEmptyLine(WINDOW *win, int y, int x, int w)
581 mvwhline(win, y, x, ' ', w);
584 static void DrawLine(WINDOW *win, int y, int x, int w)
589 mvwhline(win, y, x, ' ', w);
593 static void mvnprintw(int y, int x, int w, const char *p_fmt, ...)
602 va_start(vl_args, p_fmt);
603 if (vasprintf(&p_buf, p_fmt, vl_args) == -1)
607 i_len = strlen(p_buf);
610 wchar_t psz_wide[i_len + 1];
613 size_t i_char_len = mbstowcs(psz_wide, p_buf, i_len);
615 size_t i_width; /* number of columns */
617 if (i_char_len == (size_t)-1)
618 /* an invalid character was encountered */
625 i_width = wcswidth(psz_wide, i_char_len);
626 if (i_width == (size_t)-1)
628 /* a non printable character was encountered */
632 for(i = 0 ; i < i_char_len ; i++)
634 i_cwidth = wcwidth(psz_wide[i]);
640 if (i_width > (size_t)w)
642 int i_total_width = 0;
644 while (i_total_width < w)
646 i_total_width += wcwidth(psz_wide[i]);
647 if (w > 7 && i_total_width >= w/2)
651 i_total_width -= wcwidth(psz_wide[i]) - 2;
654 /* we require this check only if at least one character
655 * 4 or more columns wide exists (which i doubt) */
657 i_total_width -= wcwidth(psz_wide[i-1]) - 1;
660 /* find the widest string */
661 int j, i_2nd_width = 0;
662 for(j = i_char_len - 1; i_2nd_width < w - i_total_width; j--)
663 i_2nd_width += wcwidth(psz_wide[j]);
665 /* we already have i_total_width columns filled, and we can't
666 * have more than w columns */
667 if (i_2nd_width > w - i_total_width)
670 wmemmove(&psz_wide[i+2], &psz_wide[j+1], i_char_len - j - 1);
671 psz_wide[i + 2 + i_char_len - j - 1] = '\0';
676 if (w <= 7) /* we don't add the '...' else we lose too much chars */
679 size_t i_wlen = wcslen(psz_wide) * 6 + 1; /* worst case */
680 char psz_ellipsized[i_wlen];
681 wcstombs(psz_ellipsized, psz_wide, i_wlen);
682 mvprintw(y, x, "%s", psz_ellipsized);
686 mvprintw(y, x, "%s", p_buf);
687 mvhline(y, x + i_width, ' ', w - i_width);
693 int i_cut = i_len - w;
694 int x1 = i_len/2 - i_cut/2;
698 memmove(&p_buf[x1], &p_buf[x2], i_len - x2);
707 char *psz_local = ToLocale(p_buf);
708 mvprintw(y, x, "%s", psz_local);
713 char *psz_local = ToLocale(p_buf);
714 mvprintw(y, x, "%s", psz_local);
716 mvhline(y, x + i_len, ' ', w - i_len);
722 static void MainBoxWrite(intf_thread_t *p_intf, int l, int x, const char *p_fmt, ...)
724 intf_sys_t *p_sys = p_intf->p_sys;
729 if (l < p_sys->i_box_start || l - p_sys->i_box_start >= p_sys->i_box_lines)
732 va_start(vl_args, p_fmt);
733 if (vasprintf(&p_buf, p_fmt, vl_args) == -1)
737 mvnprintw(p_sys->i_box_y + l - p_sys->i_box_start, x, COLS - x - 1, "%s", p_buf);
741 static void DumpObject(intf_thread_t *p_intf, int *l, vlc_object_t *p_obj, int i_level)
743 char *psz_name = vlc_object_get_name(p_obj);
746 MainBoxWrite(p_intf, (*l)++, 1 + 2 * i_level, "%s \"%s\" (%p)",
747 p_obj->psz_object_type, psz_name, p_obj);
751 MainBoxWrite(p_intf, (*l)++, 1 + 2 * i_level, "%s (%o)",
752 p_obj->psz_object_type, p_obj);
754 vlc_list_t *list = vlc_list_children(p_obj);
755 for(int i = 0; i < list->i_count ; i++)
757 MainBoxWrite(p_intf, *l, 1 + 2 * i_level,
758 i == list->i_count - 1 ? "`-" : "|-");
759 DumpObject(p_intf, l, list->p_values[i].p_object, i_level + 1);
761 vlc_list_release(list);
764 static void Redraw(intf_thread_t *p_intf, time_t *t_last_refresh)
766 intf_sys_t *p_sys = p_intf->p_sys;
767 input_thread_t *p_input = p_sys->p_input;
768 playlist_t *p_playlist = pl_Get(p_intf);
775 int i_len = strlen("VLC media player "PACKAGE_VERSION);
776 int mid = (COLS - i_len) / 2;
779 int i_size = (COLS > i_len + 1) ? COLS : i_len + 1;
780 char psz_title[i_size];
781 memset(psz_title, ' ', mid);
783 wcolor_set(p_sys->w, C_TITLE, NULL);
784 strlcpy(&psz_title[mid], "VLC media player "PACKAGE_VERSION, i_size);
785 mvnprintw(y, 0, COLS, "%s", psz_title);
790 wcolor_set(p_sys->w, C_STATUS, NULL);
794 if (asprintf(&psz_state, "%s%s%s",
795 var_GetBool(p_playlist, "repeat") ? _("[Repeat] ") : "",
796 var_GetBool(p_playlist, "random") ? _("[Random] ") : "",
797 var_GetBool(p_playlist, "loop") ? _("[Loop]") : "") == -1)
800 if (p_input && !p_input->b_dead)
802 char buf1[MSTRTIME_MAX_SIZE];
803 char buf2[MSTRTIME_MAX_SIZE];
807 char *psz_uri = input_item_GetURI(input_GetItem(p_input));
808 mvnprintw(y++, 0, COLS, _(" Source : %s"), psz_uri);
812 var_Get(p_input, "state", &val);
813 if (val.i_int == PLAYING_S)
814 mvnprintw(y++, 0, COLS, _(" State : Playing %s"), psz_state);
815 else if (val.i_int == OPENING_S)
816 mvnprintw(y++, 0, COLS, _(" State : Opening/Connecting %s"), psz_state);
817 else if (val.i_int == PAUSE_S)
818 mvnprintw(y++, 0, COLS, _(" State : Paused %s"), psz_state);
820 if (val.i_int != INIT_S && val.i_int != END_S)
822 audio_volume_t i_volume;
825 var_Get(p_input, "time", &val);
826 secstotimestr(buf1, val.i_time / CLOCK_FREQ);
828 var_Get(p_input, "length", &val);
829 secstotimestr(buf2, val.i_time / CLOCK_FREQ);
831 mvnprintw(y++, 0, COLS, _(" Position : %s/%s (%.2f%%)"), buf1, buf2, p_sys->f_slider);
834 aout_VolumeGet(p_playlist, &i_volume);
835 mvnprintw(y++, 0, COLS, _(" Volume : %i%%"), i_volume*200/AOUT_VOLUME_MAX);
838 if (!var_Get(p_input, "title", &val))
840 int i_title_count = var_CountChoices(p_input, "title");
841 if (i_title_count > 0)
842 mvnprintw(y++, 0, COLS, _(" Title : %"PRId64"/%d"),
843 val.i_int, i_title_count);
847 if (!var_Get(p_input, "chapter", &val))
849 int i_chapter_count = var_CountChoices(p_input, "chapter");
850 if (i_chapter_count > 0)
851 mvnprintw(y++, 0, COLS, _(" Chapter : %"PRId64"/%d"),
852 val.i_int, i_chapter_count);
862 mvnprintw(y++, 0, COLS, _(" Source: <no current item> %s"), psz_state);
863 DrawEmptyLine(p_sys->w, y++, 0, COLS);
864 mvnprintw(y++, 0, COLS, _(" [ h for help ]"));
865 DrawEmptyLine(p_sys->w, y++, 0, COLS);
869 wcolor_set(p_sys->w, C_DEFAULT, NULL);
871 DrawBox(p_sys->w, y, 0, 3, COLS, "", p_sys->b_color);
872 DrawEmptyLine(p_sys->w, y+1, 1, COLS-2);
873 DrawLine(p_sys->w, y+1, 1, (int)(p_intf->p_sys->f_slider/100.0 * (COLS -2)));
876 p_sys->i_box_y = y + 1;
877 p_sys->i_box_lines = LINES - y - 2;
882 if (p_sys->i_box_type == BOX_HELP)
886 DrawBox(p_sys->w, y++, 0, h, COLS, _(" Help "), p_sys->b_color);
889 wcolor_set(p_sys->w, C_CATEGORY, NULL);
890 MainBoxWrite(p_intf, l++, 1, _("[Display]"));
892 wcolor_set(p_sys->w, C_DEFAULT, NULL);
893 MainBoxWrite(p_intf, l++, 1, _(" h,H Show/Hide help box"));
894 MainBoxWrite(p_intf, l++, 1, _(" i Show/Hide info box"));
895 MainBoxWrite(p_intf, l++, 1, _(" m Show/Hide metadata box"));
896 MainBoxWrite(p_intf, l++, 1, _(" L Show/Hide messages box"));
897 MainBoxWrite(p_intf, l++, 1, _(" P Show/Hide playlist box"));
898 MainBoxWrite(p_intf, l++, 1, _(" B Show/Hide filebrowser"));
899 MainBoxWrite(p_intf, l++, 1, _(" x Show/Hide objects box"));
900 MainBoxWrite(p_intf, l++, 1, _(" S Show/Hide statistics box"));
901 MainBoxWrite(p_intf, l++, 1, _(" c Switch color on/off"));
902 MainBoxWrite(p_intf, l++, 1, _(" Esc Close Add/Search entry"));
903 MainBoxWrite(p_intf, l++, 1, "");
906 wcolor_set(p_sys->w, C_CATEGORY, NULL);
907 MainBoxWrite(p_intf, l++, 1, _("[Global]"));
909 wcolor_set(p_sys->w, C_DEFAULT, NULL);
910 MainBoxWrite(p_intf, l++, 1, _(" q, Q, Esc Quit"));
911 MainBoxWrite(p_intf, l++, 1, _(" s Stop"));
912 MainBoxWrite(p_intf, l++, 1, _(" <space> Pause/Play"));
913 MainBoxWrite(p_intf, l++, 1, _(" f Toggle Fullscreen"));
914 MainBoxWrite(p_intf, l++, 1, _(" n, p Next/Previous playlist item"));
915 MainBoxWrite(p_intf, l++, 1, _(" [, ] Next/Previous title"));
916 MainBoxWrite(p_intf, l++, 1, _(" <, > Next/Previous chapter"));
917 MainBoxWrite(p_intf, l++, 1, _(" <right> Seek +1%%"));
918 MainBoxWrite(p_intf, l++, 1, _(" <left> Seek -1%%"));
919 MainBoxWrite(p_intf, l++, 1, _(" a Volume Up"));
920 MainBoxWrite(p_intf, l++, 1, _(" z Volume Down"));
921 MainBoxWrite(p_intf, l++, 1, "");
924 wcolor_set(p_sys->w, C_CATEGORY, NULL);
925 MainBoxWrite(p_intf, l++, 1, _("[Playlist]"));
927 wcolor_set(p_sys->w, C_DEFAULT, NULL);
928 MainBoxWrite(p_intf, l++, 1, _(" r Toggle Random playing"));
929 MainBoxWrite(p_intf, l++, 1, _(" l Toggle Loop Playlist"));
930 MainBoxWrite(p_intf, l++, 1, _(" R Toggle Repeat item"));
931 MainBoxWrite(p_intf, l++, 1, _(" o Order Playlist by title"));
932 MainBoxWrite(p_intf, l++, 1, _(" O Reverse order Playlist by title"));
933 MainBoxWrite(p_intf, l++, 1, _(" g Go to the current playing item"));
934 MainBoxWrite(p_intf, l++, 1, _(" / Look for an item"));
935 MainBoxWrite(p_intf, l++, 1, _(" A Add an entry"));
936 MainBoxWrite(p_intf, l++, 1, _(" D, <del> Delete an entry"));
937 MainBoxWrite(p_intf, l++, 1, _(" <backspace> Delete an entry"));
938 MainBoxWrite(p_intf, l++, 1, _(" e Eject (if stopped)"));
939 MainBoxWrite(p_intf, l++, 1, "");
942 wcolor_set(p_sys->w, C_CATEGORY, NULL);
943 MainBoxWrite(p_intf, l++, 1, _("[Filebrowser]"));
945 wcolor_set(p_sys->w, C_DEFAULT, NULL);
946 MainBoxWrite(p_intf, l++, 1, _(" <enter> Add the selected file to the playlist"));
947 MainBoxWrite(p_intf, l++, 1, _(" <space> Add the selected directory to the playlist"));
948 MainBoxWrite(p_intf, l++, 1, _(" . Show/Hide hidden files"));
949 MainBoxWrite(p_intf, l++, 1, "");
952 wcolor_set(p_sys->w, C_CATEGORY, NULL);
953 MainBoxWrite(p_intf, l++, 1, _("[Boxes]"));
955 wcolor_set(p_sys->w, C_DEFAULT, NULL);
956 MainBoxWrite(p_intf, l++, 1, _(" <up>,<down> Navigate through the box line by line"));
957 MainBoxWrite(p_intf, l++, 1, _(" <pgup>,<pgdown> Navigate through the box page by page"));
958 MainBoxWrite(p_intf, l++, 1, "");
961 wcolor_set(p_sys->w, C_CATEGORY, NULL);
962 MainBoxWrite(p_intf, l++, 1, _("[Player]"));
964 wcolor_set(p_sys->w, C_DEFAULT, NULL);
965 MainBoxWrite(p_intf, l++, 1, _(" <up>,<down> Seek +/-5%%"));
966 MainBoxWrite(p_intf, l++, 1, "");
969 wcolor_set(p_sys->w, C_CATEGORY, NULL);
970 MainBoxWrite(p_intf, l++, 1, _("[Miscellaneous]"));
972 wcolor_set(p_sys->w, C_DEFAULT, NULL);
973 MainBoxWrite(p_intf, l++, 1, _(" Ctrl-l Refresh the screen"));
975 p_sys->i_box_lines_total = l;
976 if (p_sys->i_box_start >= p_sys->i_box_lines_total)
977 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
979 if (l - p_sys->i_box_start < p_sys->i_box_lines)
980 y += l - p_sys->i_box_start;
982 y += p_sys->i_box_lines;
984 else if (p_sys->i_box_type == BOX_INFO)
988 DrawBox(p_sys->w, y++, 0, h, COLS, _(" Information "), p_sys->b_color);
993 vlc_mutex_lock(&input_GetItem(p_input)->lock);
994 for(i = 0; i < input_GetItem(p_input)->i_categories; i++)
996 info_category_t *p_category = input_GetItem(p_input)->pp_categories[i];
997 if (y >= y_end) break;
999 wcolor_set(p_sys->w, C_CATEGORY, NULL);
1000 MainBoxWrite(p_intf, l++, 1, _(" [%s]"), p_category->psz_name);
1002 wcolor_set(p_sys->w, C_DEFAULT, NULL);
1003 for(j = 0; j < p_category->i_infos; j++)
1005 info_t *p_info = p_category->pp_infos[j];
1006 if (y >= y_end) break;
1007 MainBoxWrite(p_intf, l++, 1, _(" %s: %s"), p_info->psz_name, p_info->psz_value);
1010 vlc_mutex_unlock(&input_GetItem(p_input)->lock);
1013 MainBoxWrite(p_intf, l++, 1, _("No item currently playing"));
1015 p_sys->i_box_lines_total = l;
1016 if (p_sys->i_box_start >= p_sys->i_box_lines_total)
1017 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1019 if (l - p_sys->i_box_start < p_sys->i_box_lines)
1020 y += l - p_sys->i_box_start;
1022 y += p_sys->i_box_lines;
1024 else if (p_sys->i_box_type == BOX_META)
1029 DrawBox(p_sys->w, y++, 0, h, COLS, _("Meta-information"),
1035 input_item_t *p_item = input_GetItem(p_input);
1036 vlc_mutex_lock(&p_item->lock);
1037 for(i=0; i<VLC_META_TYPE_COUNT; i++)
1039 if (y >= y_end) break;
1040 const char *psz_meta = vlc_meta_Get(p_item->p_meta, i);
1041 if (psz_meta && *psz_meta)
1043 const char *psz_meta_title;
1047 psz_meta_title = VLC_META_TITLE; break;
1049 psz_meta_title = VLC_META_ARTIST; break;
1051 psz_meta_title = VLC_META_GENRE ; break;
1053 psz_meta_title = VLC_META_COPYRIGHT; break;
1055 psz_meta_title = VLC_META_ALBUM; break;
1057 psz_meta_title = VLC_META_TRACK_NUMBER; break;
1059 psz_meta_title = VLC_META_DESCRIPTION; break;
1061 psz_meta_title = VLC_META_RATING; break;
1063 psz_meta_title = VLC_META_DATE; break;
1065 psz_meta_title = VLC_META_SETTING; break;
1067 psz_meta_title = VLC_META_URL; break;
1069 psz_meta_title = VLC_META_LANGUAGE; break;
1071 psz_meta_title = VLC_META_NOW_PLAYING; break;
1073 psz_meta_title = VLC_META_PUBLISHER; break;
1075 psz_meta_title = VLC_META_ENCODED_BY; break;
1077 psz_meta_title = VLC_META_ART_URL; break;
1079 psz_meta_title = VLC_META_TRACKID; break;
1081 psz_meta_title = ""; break;
1084 wcolor_set(p_sys->w, C_CATEGORY, NULL);
1085 MainBoxWrite(p_intf, l++, 1, " [%s]", psz_meta_title);
1087 wcolor_set(p_sys->w, C_DEFAULT, NULL);
1088 MainBoxWrite(p_intf, l++, 1, " %s", psz_meta);
1091 vlc_mutex_unlock(&p_item->lock);
1094 MainBoxWrite(p_intf, l++, 1, _("No item currently playing"));
1096 p_sys->i_box_lines_total = l;
1097 if (p_sys->i_box_start >= p_sys->i_box_lines_total)
1098 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1100 if (l - p_sys->i_box_start < p_sys->i_box_lines)
1101 y += l - p_sys->i_box_start;
1103 y += p_sys->i_box_lines;
1105 else if (p_sys->i_box_type == BOX_LOG)
1107 #warning Deprecated API
1113 DrawBox(p_sys->w, y++, 0, h, COLS, _(" Logs "), p_sys->b_color);
1116 i_start = p_intf->p_sys->p_sub->i_start;
1118 vlc_mutex_lock(p_intf->p_sys->p_sub->p_lock);
1119 i_stop = *p_intf->p_sys->p_sub->pi_stop;
1120 vlc_mutex_unlock(p_intf->p_sys->p_sub->p_lock);
1124 static const char *ppsz_type[4] = { "", "error", "warning", "debug" };
1125 if (i_line >= h - 2)
1131 if (i_stop < 0) i_stop += VLC_MSG_QSIZE;
1132 if (i_stop == i_start)
1137 wcolor_set(p_sys->w,
1138 p_sys->p_sub->p_msg[i_stop].i_type + C_INFO,
1140 mvnprintw(y + h-2-i_line, 1, COLS - 2, " [%s] %s",
1141 ppsz_type[p_sys->p_sub->p_msg[i_stop].i_type],
1142 p_sys->p_sub->p_msg[i_stop].psz_msg);
1144 wcolor_set(p_sys->w, C_DEFAULT, NULL);
1147 vlc_mutex_lock(p_intf->p_sys->p_sub->p_lock);
1148 p_intf->p_sys->p_sub->i_start = i_stop;
1149 vlc_mutex_unlock(p_intf->p_sys->p_sub->p_lock);
1153 else if (p_sys->i_box_type == BOX_BROWSE)
1155 /* Filebrowser box */
1156 int i_start, i_stop;
1158 DrawBox(p_sys->w, y++, 0, h, COLS, _(" Browse "), p_sys->b_color);
1160 if (p_sys->i_box_bidx >= p_sys->i_dir_entries) p_sys->i_box_plidx = p_sys->i_dir_entries - 1;
1161 if (p_sys->i_box_bidx < 0) p_sys->i_box_bidx = 0;
1163 if (p_sys->i_box_bidx < (h - 2)/2)
1168 else if (p_sys->i_dir_entries - p_sys->i_box_bidx > (h - 2)/2)
1170 i_start = p_sys->i_box_bidx - (h - 2)/2;
1171 i_stop = i_start + h - 2;
1175 i_stop = p_sys->i_dir_entries;
1176 i_start = p_sys->i_dir_entries - (h - 2);
1180 if (i_stop > p_sys->i_dir_entries)
1181 i_stop = p_sys->i_dir_entries;
1183 for(i_item = i_start; i_item < i_stop; i_item++)
1185 bool b_selected = (p_sys->i_box_bidx == i_item);
1187 if (y >= y_end) break;
1190 if (p_sys->b_color && !p_sys->pp_dir_entries[i_item]->b_file)
1191 wcolor_set(p_sys->w, C_FOLDER, NULL);
1192 mvnprintw(y++, 1, COLS - 2, " %c %s", p_sys->pp_dir_entries[i_item]->b_file == true ? ' ' : '+',
1193 p_sys->pp_dir_entries[i_item]->psz_path);
1194 if (p_sys->b_color && !p_sys->pp_dir_entries[i_item]->b_file)
1195 wcolor_set(p_sys->w, C_DEFAULT, NULL);
1202 else if (p_sys->i_box_type == BOX_OBJECTS)
1205 DrawBox(p_sys->w, y++, 0, h, COLS, _(" Objects "), p_sys->b_color);
1206 DumpObject(p_intf, &l, VLC_OBJECT(p_intf->p_libvlc), 0);
1208 p_sys->i_box_lines_total = l;
1209 if (p_sys->i_box_start >= p_sys->i_box_lines_total)
1210 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1212 if (l - p_sys->i_box_start < p_sys->i_box_lines)
1213 y += l - p_sys->i_box_start;
1215 y += p_sys->i_box_lines;
1217 else if (p_sys->i_box_type == BOX_STATS)
1219 DrawBox(p_sys->w, y++, 0, h, COLS, _(" Stats "), p_sys->b_color);
1223 input_item_t *p_item = input_GetItem(p_input);
1225 vlc_mutex_lock(&p_item->lock);
1226 vlc_mutex_lock(&p_item->p_stats->lock);
1233 i_video = i_audio = 1;
1235 for(i = 0; i < p_item->i_es ; i++)
1237 i_audio += (p_item->es[i]->i_cat == AUDIO_ES);
1238 i_video += (p_item->es[i]->i_cat == VIDEO_ES);
1243 #define SHOW_ACS(x,c) \
1244 if (l >= p_sys->i_box_start && l - p_sys->i_box_start < p_sys->i_box_lines) \
1245 mvaddch(p_sys->i_box_y - p_sys->i_box_start + l, x, c)
1248 if (p_sys->b_color) wcolor_set(p_sys->w, C_CATEGORY, NULL);
1249 MainBoxWrite(p_intf, l, 1, _("+-[Incoming]"));
1250 SHOW_ACS(1, ACS_ULCORNER); SHOW_ACS(2, ACS_HLINE); l++;
1251 if (p_sys->b_color) wcolor_set(p_sys->w, C_DEFAULT, NULL);
1252 MainBoxWrite(p_intf, l, 1, _("| input bytes read : %8.0f KiB"),
1253 (float)(p_item->p_stats->i_read_bytes)/1024);
1254 SHOW_ACS(1, ACS_VLINE); l++;
1255 MainBoxWrite(p_intf, l, 1, _("| input bitrate : %6.0f kb/s"),
1256 (float)(p_item->p_stats->f_input_bitrate)*8000);
1257 MainBoxWrite(p_intf, l, 1, _("| demux bytes read : %8.0f KiB"),
1258 (float)(p_item->p_stats->i_demux_read_bytes)/1024);
1259 SHOW_ACS(1, ACS_VLINE); l++;
1260 MainBoxWrite(p_intf, l, 1, _("| demux bitrate : %6.0f kb/s"),
1261 (float)(p_item->p_stats->f_demux_bitrate)*8000);
1262 SHOW_ACS(1, ACS_VLINE); l++;
1263 DrawEmptyLine(p_sys->w, p_sys->i_box_y + l - p_sys->i_box_start, 1, COLS - 2);
1264 SHOW_ACS(1, ACS_VLINE); l++;
1269 if (p_sys->b_color) wcolor_set(p_sys->w, C_CATEGORY, NULL);
1270 MainBoxWrite(p_intf, l, 1, _("+-[Video Decoding]"));
1271 SHOW_ACS(1, ACS_LTEE); SHOW_ACS(2, ACS_HLINE); l++;
1272 if (p_sys->b_color) wcolor_set(p_sys->w, C_DEFAULT, NULL);
1273 MainBoxWrite(p_intf, l, 1, _("| video decoded : %"PRId64),
1274 p_item->p_stats->i_decoded_video);
1275 SHOW_ACS(1, ACS_VLINE); l++;
1276 MainBoxWrite(p_intf, l, 1, _("| frames displayed : %"PRId64),
1277 p_item->p_stats->i_displayed_pictures);
1278 SHOW_ACS(1, ACS_VLINE); l++;
1279 MainBoxWrite(p_intf, l, 1, _("| frames lost : %"PRId64),
1280 p_item->p_stats->i_lost_pictures);
1281 SHOW_ACS(1, ACS_VLINE); l++;
1282 DrawEmptyLine(p_sys->w, p_sys->i_box_y + l - p_sys->i_box_start, 1, COLS - 2);
1283 SHOW_ACS(1, ACS_VLINE); l++;
1288 if (p_sys->b_color) wcolor_set(p_sys->w, C_CATEGORY, NULL);
1289 MainBoxWrite(p_intf, l, 1, _("+-[Audio Decoding]"));
1290 SHOW_ACS(1, ACS_LTEE); SHOW_ACS(2, ACS_HLINE); l++;
1291 if (p_sys->b_color) wcolor_set(p_sys->w, C_DEFAULT, NULL);
1292 MainBoxWrite(p_intf, l, 1, _("| audio decoded : %"PRId64),
1293 p_item->p_stats->i_decoded_audio);
1294 SHOW_ACS(1, ACS_VLINE); l++;
1295 MainBoxWrite(p_intf, l, 1, _("| buffers played : %"PRId64),
1296 p_item->p_stats->i_played_abuffers);
1297 SHOW_ACS(1, ACS_VLINE); l++;
1298 MainBoxWrite(p_intf, l, 1, _("| buffers lost : %"PRId64),
1299 p_item->p_stats->i_lost_abuffers);
1300 SHOW_ACS(1, ACS_VLINE); l++;
1301 DrawEmptyLine(p_sys->w, p_sys->i_box_y + l - p_sys->i_box_start, 1, COLS - 2);
1302 SHOW_ACS(1, ACS_VLINE); l++;
1305 if (p_sys->b_color) wcolor_set(p_sys->w, C_CATEGORY, NULL);
1306 MainBoxWrite(p_intf, l, 1, _("+-[Streaming]"));
1307 SHOW_ACS(1, ACS_LTEE); SHOW_ACS(2, ACS_HLINE); l++;
1308 if (p_sys->b_color) wcolor_set(p_sys->w, C_DEFAULT, NULL);
1309 MainBoxWrite(p_intf, l, 1, _("| packets sent : %5i"), p_item->p_stats->i_sent_packets);
1310 SHOW_ACS(1, ACS_VLINE); l++;
1311 MainBoxWrite(p_intf, l, 1, _("| bytes sent : %8.0f KiB"),
1312 (float)(p_item->p_stats->i_sent_bytes)/1024);
1313 SHOW_ACS(1, ACS_VLINE); l++;
1314 MainBoxWrite(p_intf, l, 1, _("\\ sending bitrate : %6.0f kb/s"),
1315 (float)(p_item->p_stats->f_send_bitrate*8)*1000);
1316 SHOW_ACS(1, ACS_LLCORNER); l++;
1317 if (p_sys->b_color) wcolor_set(p_sys->w, C_DEFAULT, NULL);
1321 p_sys->i_box_lines_total = l;
1322 if (p_sys->i_box_start >= p_sys->i_box_lines_total)
1323 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1325 if (l - p_sys->i_box_start < p_sys->i_box_lines)
1326 y += l - p_sys->i_box_start;
1328 y += p_sys->i_box_lines;
1330 vlc_mutex_unlock(&p_item->p_stats->lock);
1331 vlc_mutex_unlock(&p_item->lock);
1335 else if (p_sys->i_box_type == BOX_PLAYLIST ||
1336 p_sys->i_box_type == BOX_SEARCH ||
1337 p_sys->i_box_type == BOX_OPEN )
1340 int i_start, i_stop, i_max = p_sys->i_plist_entries;
1344 switch(p_sys->i_current_view)
1347 psz_title = strdup(_(" Playlist (All, one level) "));
1350 psz_title = strdup(_(" Playlist (By category) "));
1353 psz_title = strdup(_(" Playlist (Manually added) "));
1356 DrawBox(p_sys->w, y++, 0, h, COLS, psz_title, p_sys->b_color);
1359 if (p_sys->b_need_update || !p_sys->pp_plist)
1360 PlaylistRebuild(p_intf);
1361 if (p_sys->b_box_plidx_follow)
1362 FindIndex(p_intf, p_playlist);
1364 if (p_sys->i_box_plidx < 0) p_sys->i_box_plidx = 0;
1365 if (p_sys->i_box_plidx < 0) p_sys->i_box_plidx = 0;
1366 if (p_sys->i_box_plidx >= i_max) p_sys->i_box_plidx = i_max - 1;
1368 if (p_sys->i_box_plidx < (h - 2)/2)
1373 else if (i_max - p_sys->i_box_plidx > (h - 2)/2)
1375 i_start = p_sys->i_box_plidx - (h - 2)/2;
1376 i_stop = i_start + h - 2;
1381 i_start = i_max - (h - 2);
1388 for(i_item = i_start; i_item < i_stop; i_item++)
1390 bool b_selected = (p_sys->i_box_plidx == i_item);
1391 playlist_item_t *p_item = p_sys->pp_plist[i_item]->p_item;
1392 playlist_item_t *p_node = p_sys->p_node;
1394 input_thread_t *p_input2 = playlist_CurrentInput(p_playlist);
1398 playlist_item_t *p_current_playing_item = playlist_CurrentPlayingItem(p_playlist);
1399 if ((p_node && p_item->p_input == p_node->p_input) ||
1400 (!p_node && p_input2 && p_current_playing_item &&
1401 p_item->p_input == p_current_playing_item->p_input))
1403 else if (p_item == p_node || (p_item != p_node &&
1404 PlaylistIsPlaying(p_playlist, p_item)))
1409 vlc_object_release(p_input2);
1411 if (y >= y_end) break;
1415 wcolor_set(p_sys->w, i_item % 3 + C_PLAYLIST_1, NULL);
1416 mvnprintw(y++, 1, COLS - 2, "%c%s", c,
1417 p_sys->pp_plist[i_item]->psz_display);
1419 wcolor_set(p_sys->w, C_DEFAULT, NULL);
1428 if (p_sys->i_box_type == BOX_SEARCH)
1430 DrawEmptyLine(p_sys->w, 7, 1, COLS-2);
1431 if (p_sys->psz_search_chain)
1433 if (*p_sys->psz_search_chain == '\0' && p_sys->psz_old_search)
1434 /* Searching next entry */
1435 mvnprintw(7, 1, COLS-2, _("Find: %s"), p_sys->psz_old_search);
1437 mvnprintw(7, 1, COLS-2, _("Find: %s"), p_sys->psz_search_chain);
1440 if (p_sys->i_box_type == BOX_OPEN && p_sys->psz_open_chain)
1442 DrawEmptyLine(p_sys->w, 7, 1, COLS-2);
1443 mvnprintw(7, 1, COLS-2, _("Open: %s"), p_sys->psz_open_chain);
1447 DrawEmptyLine(p_sys->w, y++, 1, COLS - 2);
1451 *t_last_refresh = time(0);
1454 static void ManageSlider(intf_thread_t *p_intf)
1456 intf_sys_t *p_sys = p_intf->p_sys;
1457 input_thread_t *p_input = p_sys->p_input;
1462 var_Get(p_input, "state", &val);
1463 if (val.i_int != PLAYING_S)
1466 var_Get(p_input, "position", &val);
1467 if (p_sys->f_slider == p_sys->f_slider_old)
1468 p_sys->f_slider = p_sys->f_slider_old = 100 * val.f_float;
1471 p_sys->f_slider_old = p_sys->f_slider;
1473 val.f_float = p_sys->f_slider / 100.0;
1474 var_Set(p_input, "position", val);
1479 /* following functions are local */
1480 #ifndef HAVE_NCURSESW
1481 static char *KeyToUTF8(int i_key, char *psz_part)
1484 int len = strlen(psz_part);
1487 /* overflow error - should not happen */
1488 memset(psz_part, 0, 6);
1492 psz_part[len] = (char)i_key;
1494 psz_utf8 = FromLocaleDup(psz_part);
1496 /* Ugly check for incomplete bytes sequences
1497 * (in case of non-UTF8 multibyte local encoding) */
1499 for(psz = psz_utf8; *psz; psz++)
1500 if ((*psz == '?') && (*psz_utf8 != '?'))
1502 /* incomplete bytes sequence detected
1503 * (VLC core inserted dummy question marks) */
1508 /* Check for incomplete UTF8 bytes sequence */
1509 if (!EnsureUTF8(psz_utf8))
1515 memset(psz_part, 0, 6);
1520 static inline int RemoveLastUTF8Entity(char *psz, int len)
1522 while (len && ((psz[--len] & 0xc0) == 0x80));
1523 /* UTF8 continuation byte */
1529 static void Eject(intf_thread_t *p_intf)
1531 char *psz_device = NULL, *psz_parser, *psz_name;
1534 * Get the active input
1535 * Determine whether we can eject a media, ie it's a DVD, VCD or CD-DA
1536 * If it's neither of these, then return
1539 playlist_t * p_playlist = pl_Get(p_intf);
1542 if (!playlist_CurrentPlayingItem(p_playlist))
1548 psz_name = playlist_CurrentPlayingItem(p_playlist)->p_input->psz_name;
1552 if (!strncmp(psz_name, "dvd://", 6))
1558 psz_device = config_GetPsz(p_intf, "dvd");
1561 /* Omit the first MRL-selector characters */
1562 psz_device = strdup(psz_name + strlen("dvd://"));
1566 else if (!strncmp(psz_name, "vcd://", 6))
1572 psz_device = config_GetPsz(p_intf, "vcd");
1575 /* Omit the beginning MRL-selector characters */
1576 psz_device = strdup(psz_name + 6);
1580 else if (!strncmp(psz_name, "cdda://", 7))
1586 psz_device = config_GetPsz(p_intf, "cd-audio");
1589 /* Omit the beginning MRL-selector characters */
1590 psz_device = strdup(psz_name + 7);
1595 psz_device = strdup(psz_name);
1603 /* Remove what we have after @ */
1604 for(psz_parser = psz_device ; *psz_parser ; psz_parser++)
1605 if (*psz_parser == '@')
1611 /* If there's a stream playing, we aren't allowed to eject ! */
1612 if (!p_intf->p_sys->p_input)
1614 msg_Dbg(p_intf, "ejecting %s", psz_device);
1616 intf_Eject(p_intf, psz_device);
1622 static void PlayPause(intf_thread_t *p_intf)
1624 input_thread_t *p_input = p_intf->p_sys->p_input;
1625 playlist_t *p_playlist = pl_Get(p_intf);
1630 var_Get(p_input, "state", &val);
1631 if (val.i_int != PAUSE_S)
1632 val.i_int = PAUSE_S;
1634 val.i_int = PLAYING_S;
1635 var_Set(p_input, "state", val);
1638 playlist_Play(p_playlist);
1641 static int HandleKey(intf_thread_t *p_intf, int i_key)
1643 intf_sys_t *p_sys = p_intf->p_sys;
1646 playlist_t *p_playlist = pl_Get(p_intf);
1648 if (p_sys->i_box_type == BOX_PLAYLIST)
1651 bool b_box_plidx_follow = false;
1655 /* Playlist Settings */
1657 var_ToggleBool(p_playlist, "random");
1660 var_ToggleBool(p_playlist, "loop");
1663 var_ToggleBool(p_playlist, "repeat");
1668 playlist_RecursiveNodeSort(p_playlist,
1669 PlaylistGetRoot(p_intf),
1670 SORT_TITLE_NODES_FIRST, ORDER_NORMAL);
1671 p_sys->b_need_update = true;
1674 playlist_RecursiveNodeSort(p_playlist,
1675 PlaylistGetRoot(p_intf),
1676 SORT_TITLE_NODES_FIRST, ORDER_REVERSE);
1677 p_sys->b_need_update = true;
1682 switch(p_sys->i_current_view)
1685 p_sys->i_current_view = VIEW_ONELEVEL;
1688 p_sys->i_current_view = VIEW_CATEGORY;
1690 PlaylistRebuild(p_intf);
1693 /* Playlist navigation */
1695 FindIndex(p_intf, p_playlist);
1698 p_sys->i_box_plidx = 0;
1701 /* workaround for FreeBSD + xterm:
1702 * see http://www.nabble.com/curses-vs.-xterm-key-mismatch-t3574377.html */
1706 p_sys->i_box_plidx = p_playlist->items.i_size - 1;
1709 p_sys->i_box_plidx--;
1712 p_sys->i_box_plidx++;
1715 p_sys->i_box_plidx -= p_sys->i_box_lines;
1718 p_sys->i_box_plidx += p_sys->i_box_lines;
1725 playlist_item_t *p_item;
1728 p_item = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
1729 if (p_item->i_children == -1)
1730 playlist_DeleteFromInput(p_playlist,
1731 p_item->p_input, pl_Locked);
1733 playlist_NodeDelete(p_playlist, p_item, true , false);
1735 PlaylistRebuild(p_intf);
1742 if (!p_sys->pp_plist[p_sys->i_box_plidx])
1747 if (p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
1750 playlist_item_t *p_item, *p_parent;
1752 p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
1755 p_parent = p_playlist->p_root_onelevel;
1756 while (p_parent->p_parent)
1757 p_parent = p_parent->p_parent;
1758 playlist_Control(p_playlist, PLAYLIST_VIEWPLAY,
1759 pl_Unlocked, p_parent, p_item);
1761 else if (p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
1763 { /* We only want to set the current node */
1764 playlist_Stop(p_playlist);
1765 p_sys->p_node = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
1769 p_sys->p_node = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
1770 playlist_Control(p_playlist, PLAYLIST_VIEWPLAY, pl_Unlocked,
1771 p_sys->pp_plist[p_sys->i_box_plidx]->p_item, NULL);
1773 b_box_plidx_follow = true;
1782 int i_max = p_sys->i_plist_entries;
1783 if (p_sys->i_box_plidx >= i_max) p_sys->i_box_plidx = i_max - 1;
1784 if (p_sys->i_box_plidx < 0) p_sys->i_box_plidx = 0;
1787 if (PlaylistIsPlaying(p_playlist,
1788 p_sys->pp_plist[p_sys->i_box_plidx]->p_item))
1789 b_box_plidx_follow = true;
1791 p_sys->b_box_plidx_follow = b_box_plidx_follow;
1795 if (p_sys->i_box_type == BOX_BROWSE)
1798 /* Browser navigation */
1802 p_sys->i_box_bidx = 0;
1808 p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
1811 p_sys->i_box_bidx--;
1814 p_sys->i_box_bidx++;
1817 p_sys->i_box_bidx -= p_sys->i_box_lines;
1820 p_sys->i_box_bidx += p_sys->i_box_lines;
1822 case '.': /* Toggle show hidden files */
1823 p_sys->b_show_hidden_files = (p_sys->b_show_hidden_files ==
1824 true ? false : true);
1832 if (p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file || i_key == ' ')
1835 if (asprintf(&psz_uri, "%s://%s/%s",
1836 p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file ?
1837 "file" : "directory",
1838 p_sys->psz_current_dir,
1839 p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path
1845 playlist_item_t *p_parent = p_sys->p_node;
1849 p_parent = playlist_CurrentPlayingItem(p_playlist) ? playlist_CurrentPlayingItem(p_playlist)->p_parent : NULL;
1852 p_parent = p_playlist->p_local_onelevel;
1855 while (p_parent->p_parent && p_parent->p_parent->p_parent)
1856 p_parent = p_parent->p_parent;
1858 playlist_Add(p_playlist, psz_uri, NULL, PLAYLIST_APPEND,
1860 p_parent->p_input ==
1861 p_playlist->p_local_onelevel->p_input
1864 p_sys->i_box_type = BOX_PLAYLIST;
1869 if (asprintf(&(p_sys->psz_current_dir), "%s/%s", p_sys->psz_current_dir,
1870 p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path) != -1)
1880 if (p_sys->i_box_bidx >= p_sys->i_dir_entries) p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
1881 if (p_sys->i_box_bidx < 0) p_sys->i_box_bidx = 0;
1885 else if (p_sys->i_box_type == BOX_HELP || p_sys->i_box_type == BOX_INFO ||
1886 p_sys->i_box_type == BOX_META || p_sys->i_box_type == BOX_STATS ||
1887 p_sys->i_box_type == BOX_OBJECTS)
1892 p_sys->i_box_start = 0;
1898 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1901 if (p_sys->i_box_start > 0) p_sys->i_box_start--;
1904 if (p_sys->i_box_start < p_sys->i_box_lines_total - 1)
1905 p_sys->i_box_start++;
1908 p_sys->i_box_start -= p_sys->i_box_lines;
1909 if (p_sys->i_box_start < 0) p_sys->i_box_start = 0;
1912 p_sys->i_box_start += p_sys->i_box_lines;
1913 if (p_sys->i_box_start >= p_sys->i_box_lines_total)
1914 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1920 else if (p_sys->i_box_type == BOX_NONE)
1925 p_sys->f_slider = 0;
1926 ManageSlider(p_intf);
1932 p_sys->f_slider = 99.9;
1933 ManageSlider(p_intf);
1936 p_sys->f_slider += 5.0;
1937 if (p_sys->f_slider >= 99.0) p_sys->f_slider = 99.0;
1938 ManageSlider(p_intf);
1941 p_sys->f_slider -= 5.0;
1942 if (p_sys->f_slider < 0.0) p_sys->f_slider = 0.0;
1943 ManageSlider(p_intf);
1950 else if (p_sys->i_box_type == BOX_SEARCH && p_sys->psz_search_chain)
1952 int i_chain_len = strlen(p_sys->psz_search_chain);
1962 if (i_chain_len > 0)
1963 p_sys->psz_old_search = strdup(p_sys->psz_search_chain);
1964 else if (p_sys->psz_old_search)
1965 SearchPlaylist(p_intf, p_sys->psz_old_search);
1966 p_sys->i_box_type = BOX_PLAYLIST;
1968 case 0x1b: /* ESC */
1969 /* Alt+key combinations return 2 keys in the terminal keyboard:
1970 * ESC, and the 2nd key.
1971 * If some other key is available immediately (where immediately
1972 * means after wgetch() 1 second delay), that means that the
1973 * ESC key was not pressed.
1975 * man 3X curs_getch says:
1977 * Use of the escape key by a programmer for a single
1978 * character function is discouraged, as it will cause a delay
1979 * of up to one second while the keypad code looks for a
1980 * following function-key sequence.
1983 if (wgetch(p_sys->w) != ERR)
1988 p_sys->i_box_plidx = p_sys->i_before_search;
1989 p_sys->i_box_type = BOX_PLAYLIST;
1993 RemoveLastUTF8Entity(p_sys->psz_search_chain, i_chain_len);
1997 #ifdef HAVE_NCURSESW
1998 if (i_chain_len + 1 < SEARCH_CHAIN_SIZE)
2000 p_sys->psz_search_chain[i_chain_len] = (char) i_key;
2001 p_sys->psz_search_chain[i_chain_len + 1] = '\0';
2004 char *psz_utf8 = KeyToUTF8(i_key, p_sys->psz_partial_keys);
2008 if (i_chain_len + strlen(psz_utf8) < SEARCH_CHAIN_SIZE)
2009 strcpy(p_sys->psz_search_chain + i_chain_len, psz_utf8);
2016 free(p_sys->psz_old_search);
2017 p_sys->psz_old_search = NULL;
2018 SearchPlaylist(p_intf, p_sys->psz_search_chain);
2021 else if (p_sys->i_box_type == BOX_OPEN && p_sys->psz_open_chain)
2023 int i_chain_len = strlen(p_sys->psz_open_chain);
2034 if (i_chain_len > 0)
2036 playlist_item_t *p_parent = p_sys->p_node;
2040 p_parent = playlist_CurrentPlayingItem(p_playlist) ? playlist_CurrentPlayingItem(p_playlist)->p_parent : NULL;
2042 p_parent = p_playlist->p_local_onelevel;
2044 while (p_parent->p_parent && p_parent->p_parent->p_parent)
2045 p_parent = p_parent->p_parent;
2048 playlist_Add(p_playlist, p_sys->psz_open_chain, NULL,
2049 PLAYLIST_APPEND|PLAYLIST_GO, PLAYLIST_END,
2050 p_parent->p_input ==
2051 p_playlist->p_local_onelevel->p_input
2054 p_sys->b_box_plidx_follow = true;
2056 p_sys->i_box_type = BOX_PLAYLIST;
2058 case 0x1b: /* ESC */
2059 if (wgetch(p_sys->w) != ERR)
2064 p_sys->i_box_type = BOX_PLAYLIST;
2068 RemoveLastUTF8Entity(p_sys->psz_open_chain, i_chain_len);
2072 #ifdef HAVE_NCURSESW
2073 if (i_chain_len + 1 < OPEN_CHAIN_SIZE)
2075 p_sys->psz_open_chain[i_chain_len] = (char) i_key;
2076 p_sys->psz_open_chain[i_chain_len + 1] = '\0';
2079 char *psz_utf8 = KeyToUTF8(i_key, p_sys->psz_partial_keys);
2083 if (i_chain_len + strlen(psz_utf8) < OPEN_CHAIN_SIZE)
2084 strcpy(p_sys->psz_open_chain + i_chain_len, psz_utf8);
2097 case 0x1b: /* ESC */
2098 if (wgetch(p_sys->w) != ERR)
2106 libvlc_Quit(p_intf->p_libvlc);
2112 if (p_sys->i_box_type == BOX_INFO)
2113 p_sys->i_box_type = BOX_NONE;
2115 p_sys->i_box_type = BOX_INFO;
2116 p_sys->i_box_lines_total = 0;
2119 if (p_sys->i_box_type == BOX_META)
2120 p_sys->i_box_type = BOX_NONE;
2122 p_sys->i_box_type = BOX_META;
2123 p_sys->i_box_lines_total = 0;
2126 if (p_sys->i_box_type == BOX_LOG)
2127 p_sys->i_box_type = BOX_NONE;
2129 p_sys->i_box_type = BOX_LOG;
2132 if (p_sys->i_box_type == BOX_PLAYLIST)
2133 p_sys->i_box_type = BOX_NONE;
2135 p_sys->i_box_type = BOX_PLAYLIST;
2138 if (p_sys->i_box_type == BOX_BROWSE)
2139 p_sys->i_box_type = BOX_NONE;
2141 p_sys->i_box_type = BOX_BROWSE;
2144 if (p_sys->i_box_type == BOX_OBJECTS)
2145 p_sys->i_box_type = BOX_NONE;
2147 p_sys->i_box_type = BOX_OBJECTS;
2150 if (p_sys->i_box_type == BOX_STATS)
2151 p_sys->i_box_type = BOX_NONE;
2153 p_sys->i_box_type = BOX_STATS;
2156 p_sys->b_color = !p_sys->b_color;
2157 if (p_sys->b_color && !p_sys->b_color_started)
2158 start_color_and_pairs(p_intf);
2162 if (p_sys->i_box_type == BOX_HELP)
2163 p_sys->i_box_type = BOX_NONE;
2165 p_sys->i_box_type = BOX_HELP;
2166 p_sys->i_box_lines_total = 0;
2169 if (p_sys->i_box_type != BOX_SEARCH && p_sys->psz_search_chain)
2171 p_sys->psz_search_chain[0] = '\0';
2172 p_sys->b_box_plidx_follow = false;
2173 p_sys->i_before_search = p_sys->i_box_plidx;
2174 p_sys->i_box_type = BOX_SEARCH;
2177 case 'A': /* Open */
2178 if (p_sys->i_box_type != BOX_OPEN && p_sys->psz_open_chain)
2180 p_sys->psz_open_chain[0] = '\0';
2181 p_sys->i_box_type = BOX_OPEN;
2187 p_sys->f_slider += 1.0;
2188 if (p_sys->f_slider > 99.9) p_sys->f_slider = 99.9;
2189 ManageSlider(p_intf);
2193 p_sys->f_slider -= 1.0;
2194 if (p_sys->f_slider < 0.0) p_sys->f_slider = 0.0;
2195 ManageSlider(p_intf);
2198 /* Common control */
2201 bool fs = var_ToggleBool(p_playlist, "fullscreen");
2202 if (p_intf->p_sys->p_input)
2204 vout_thread_t *p_vout = input_GetVout(p_intf->p_sys->p_input);
2207 var_SetBool(p_vout, "fullscreen", fs);
2208 vlc_object_release(p_vout);
2220 playlist_Stop(p_playlist);
2229 var_TriggerCallback(p_sys->p_input, "prev-title");
2234 var_TriggerCallback(p_sys->p_input, "next-title");
2239 var_TriggerCallback(p_sys->p_input, "prev-chapter");
2244 var_TriggerCallback(p_sys->p_input, "next-chapter");
2248 playlist_Prev(p_playlist);
2253 playlist_Next(p_playlist);
2258 aout_VolumeUp(p_playlist, 1, NULL);
2263 aout_VolumeDown(p_playlist, 1, NULL);
2268 * ^l should clear and redraw the screen
2283 /*****************************************************************************
2284 * Run: ncurses thread
2285 *****************************************************************************/
2286 static void Run(intf_thread_t *p_intf)
2288 intf_sys_t *p_sys = p_intf->p_sys;
2289 playlist_t *p_playlist = pl_Get(p_intf);
2290 p_sys->p_playlist = p_playlist;
2293 time_t t_last_refresh;
2294 int canc = vlc_savecancel();
2297 * force drawing the interface for the first time
2299 t_last_refresh = (time(0) - 1);
2301 * force building of the playlist array
2303 PlaylistRebuild(p_intf);
2304 var_AddCallback(p_playlist, "intf-change", PlaylistChanged, p_intf);
2305 var_AddCallback(p_playlist, "playlist-item-append", PlaylistChanged, p_intf);
2307 while (vlc_object_alive(p_intf))
2309 msleep(INTF_IDLE_SLEEP);
2311 /* Update the input */
2312 if (!p_sys->p_input)
2313 p_sys->p_input = playlist_CurrentInput(p_playlist);
2314 else if (p_sys->p_input->b_dead)
2316 vlc_object_release(p_sys->p_input);
2317 p_sys->p_input = NULL;
2318 p_sys->f_slider = p_sys->f_slider_old = 0.0;
2319 p_sys->b_box_cleared = false;
2323 if (p_sys->b_box_plidx_follow && playlist_CurrentPlayingItem(p_playlist))
2326 FindIndex(p_intf, p_playlist);
2331 while ((i_key = wgetch(p_sys->w)) != -1)
2332 if (HandleKey(p_intf, i_key))
2333 Redraw(p_intf, &t_last_refresh);
2336 if (p_sys->f_slider > 0.0001 && !p_sys->b_box_cleared)
2339 Redraw(p_intf, &t_last_refresh);
2340 p_sys->b_box_cleared = true;
2344 * redraw the screen every second
2346 if ((time(0) - t_last_refresh) >= 1)
2348 ManageSlider(p_intf);
2349 Redraw(p_intf, &t_last_refresh);
2352 var_DelCallback(p_playlist, "intf-change", PlaylistChanged, p_intf);
2353 var_DelCallback(p_playlist, "playlist-item-append", PlaylistChanged, p_intf);
2354 vlc_restorecancel(canc);
2357 /*****************************************************************************
2358 * Open: initialize and create window
2359 *****************************************************************************/
2360 static int Open(vlc_object_t *p_this)
2362 intf_thread_t *p_intf = (intf_thread_t *)p_this;
2365 /* Allocate instance and initialize some members */
2366 p_sys = p_intf->p_sys = malloc(sizeof(intf_sys_t));
2369 p_sys->p_node = NULL;
2370 p_sys->p_input = NULL;
2371 p_sys->f_slider = 0.0;
2372 p_sys->f_slider_old = 0.0;
2373 p_sys->i_box_type = BOX_PLAYLIST;
2374 p_sys->i_box_lines = 0;
2375 p_sys->i_box_start= 0;
2376 p_sys->i_box_lines_total = 0;
2377 p_sys->b_box_plidx_follow = true;
2378 p_sys->b_box_cleared = false;
2379 p_sys->i_box_plidx = 0;
2380 p_sys->i_box_bidx = 0;
2381 // FIXME p_sys->p_sub = msg_Subscribe(p_intf);
2382 p_sys->b_color = var_CreateGetBool(p_intf, "color");
2383 p_sys->b_color_started = false;
2385 #ifndef HAVE_NCURSESW
2386 memset(p_sys->psz_partial_keys, 0, sizeof(p_sys->psz_partial_keys));
2389 /* Initialize the curses library */
2390 p_sys->w = initscr();
2393 start_color_and_pairs(p_intf);
2395 keypad(p_sys->w, TRUE);
2396 /* Don't do NL -> CR/NL */
2398 /* Take input chars one at a time */
2402 /* Invisible cursor */
2404 /* Non blocking wgetch() */
2405 wtimeout(p_sys->w, 0);
2409 /* exported function */
2410 p_intf->pf_run = Run;
2412 /* Stop printing errors to the console */
2413 freopen("/dev/null", "wb", stderr);
2415 /* Set defaul playlist view */
2416 p_sys->i_current_view = VIEW_CATEGORY;
2417 p_sys->pp_plist = NULL;
2418 p_sys->i_plist_entries = 0;
2419 p_sys->b_need_update = false;
2421 /* Initialize search chain */
2422 p_sys->psz_search_chain = malloc(SEARCH_CHAIN_SIZE + 1);
2423 p_sys->psz_old_search = NULL;
2424 p_sys->i_before_search = 0;
2426 /* Initialize open chain */
2427 p_sys->psz_open_chain = malloc(OPEN_CHAIN_SIZE + 1);
2429 /* Initialize browser options */
2430 char* psz_tmp = var_CreateGetString(p_intf, "browse-dir");
2431 if (psz_tmp && *psz_tmp)
2432 p_sys->psz_current_dir = psz_tmp;
2435 p_sys->psz_current_dir = config_GetUserDir(VLC_HOME_DIR);
2439 p_sys->i_dir_entries = 0;
2440 p_sys->pp_dir_entries = NULL;
2441 p_sys->b_show_hidden_files = false;
2447 /*****************************************************************************
2448 * Close: destroy interface window
2449 *****************************************************************************/
2450 static void Close(vlc_object_t *p_this)
2452 intf_thread_t *p_intf = (intf_thread_t *)p_this;
2453 intf_sys_t *p_sys = p_intf->p_sys;
2455 PlaylistDestroy(p_sys);
2458 free(p_sys->psz_current_dir);
2459 free(p_sys->psz_search_chain);
2460 free(p_sys->psz_old_search);
2461 free(p_sys->psz_open_chain);
2464 vlc_object_release(p_sys->p_input);
2466 /* Close the ncurses interface */
2469 // FIXME msg_Unsubscribe(p_intf, p_sys->p_sub);
2471 /* Destroy structure */