summaryrefslogtreecommitdiffstats
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c1384
1 files changed, 13 insertions, 1371 deletions
diff --git a/src/main.c b/src/main.c
index 7989615..1984617 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,203 +1,27 @@
-#define _XOPEN_SOURCE 600
-#define _DEFAULT_SOURCE
-
-#include "util.h"
+#include "data.h"
+#include "history.h"
#include "list.h"
+#include "listnav.h"
#include "log.h"
#include "mpris.h"
-#include "history.h"
-#include "tag.h"
#include "pane.h"
#include "player.h"
-#include "listnav.h"
#include "ref.h"
+#include "style.h"
+#include "tag.h"
+#include "tui.h"
+#include "util.h"
-#include "mpd/player.h"
-#include "curses.h"
-
-#include <dirent.h>
-#include <fcntl.h>
#include <locale.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <time.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#undef KEY_ENTER
-#define KEY_ENTER '\n'
-#define KEY_SPACE ' '
-#define KEY_ESC '\x1b'
-#define KEY_TAB '\t'
-#define KEY_CTRL(c) ((c) & ~0x60)
-
-#define ATTR_ON(win, attr) wattr_on(win, attr, NULL)
-#define ATTR_OFF(win, attr) wattr_off(win, attr, NULL)
-
-#define CMD_ERROR(...) do { \
- free(cmd_status); \
- cmd_status = awprintf(__VA_ARGS__); \
- return false; \
- } while (0)
-
-enum {
- STYLE_DEFAULT,
- STYLE_TITLE,
- STYLE_PANE_SEP,
- STYLE_ITEM_SEL,
- STYLE_ITEM_HOVER,
- STYLE_ITEM_HOVER_SEL,
- STYLE_PREV,
- STYLE_ERROR,
- STYLE_COUNT
-};
-
-enum {
- IMODE_EXECUTE,
- IMODE_TRACK_SEARCH,
- IMODE_TAG_SEARCH,
- IMODE_COUNT
-};
-
-typedef wchar_t *(*completion_gen)(const wchar_t *text, int fwd, int state);
-typedef int (*cmd_handler)(const wchar_t *args);
-
-struct cmd {
- const wchar_t *name;
- cmd_handler func;
-};
-
-static const char player_state_chars[] = {
- [PLAYER_STATE_PAUSED] = '|',
- [PLAYER_STATE_PLAYING] = '>',
- [PLAYER_STATE_STOPPED] = '#'
-};
-
-static int style_attrs[STYLE_COUNT];
-
-static const char *datadir;
-static int scrw, scrh;
-static int quit;
-
-static struct pane *pane_sel, *pane_top_sel;
-static struct pane pane_left, pane_right, pane_bot;
-static struct pane *const panes[] = {
- &pane_left,
- &pane_right,
- &pane_bot
-};
-
-/* 'tracks' holds all *unique* tracks */
-static struct list tracks;
-static struct list tags;
-static struct list tags_sel;
-static struct list *tracks_vis;
-static int track_show_playlist;
-
-/* bottom 'cmd' pane for search / exec */
-static struct pane *cmd_pane;
-static struct history command_history;
-static struct history track_search_history;
-static struct history tag_search_history;
-static struct history *history;
-static int cmd_show, cmd_mode;
-static struct inputln completion_query;
-static int completion_reset;
-static completion_gen completion;
-static wchar_t *cmd_status;
-
-/* left pane for tags */
-static struct pane *tag_pane;
-static struct listnav tag_nav;
-
-/* right pane for tracks */
-static struct pane *track_pane;
-static struct listnav track_nav;
static void init(void);
static void cleanup(int code, void *arg);
-static void tui_init(void);
-static void tui_ncurses_init(void);
-static void tui_resize(void);
-static void tui_end(void);
-
-static void data_load(void);
-static void data_save(void);
-static void data_free(void);
-
-static int get_fid(const char *path);
-static void index_update(struct tag *tag);
-static void tracks_load(struct tag *tag);
-static void tracks_save(struct tag *tag);
-
-static void copy_file(const char *dst, const char *src);
-static void move_file(const char *dst, const char *src);
-
-static struct tag *tag_find(const wchar_t *query);
-static void pane_title(struct pane *pane, const char *title, int highlight);
-
-static void style_init(int style, int fg, int bg, int attr);
-static void style_on(WINDOW *win, int style);
-static void style_off(WINDOW *win, int style);
-
-static wchar_t *command_name_gen(const wchar_t *text, int fwd, int state);
-static wchar_t *track_name_gen(const wchar_t *text, int fwd, int state);
-
-static void toggle_current_tag(void);
-static int tag_pane_input(wint_t c);
-static void tag_pane_vis(struct pane *pane, int sel);
-
-static int track_pane_input(wint_t c);
-static void track_pane_vis(struct pane *pane, int sel);
-
-static int play_track(const wchar_t *name);
-static int select_tag(const wchar_t *name);
-static int cmd_pane_input(wint_t c);
-static void cmd_pane_vis(struct pane *pane, int sel);
-
-static void queue_hover(void);
-static void update_track_playlist(void);
-static void main_input(wint_t c);
-static void main_vis(void);
-
-static int cmd_save(const wchar_t *args);
-static int cmd_move(const wchar_t *args);
-static int cmd_add(const wchar_t *args);
-
-const char imode_prefix[IMODE_COUNT] = {
- [IMODE_EXECUTE] = ':',
- [IMODE_TRACK_SEARCH] = '/',
- [IMODE_TAG_SEARCH] = '?',
-};
-
-const struct cmd cmds[] = {
- { L"save", cmd_save },
- { L"move", cmd_move },
- { L"add", cmd_move },
-};
-
void
init(void)
{
setlocale(LC_ALL, "");
srand(time(NULL));
- quit = 0;
-
- inputln_init(&completion_query);
- completion_reset = 1;
-
- history_init(&track_search_history);
- history_init(&tag_search_history);
- history_init(&command_history);
- history = &command_history;
log_init();
@@ -209,14 +33,6 @@ init(void)
dbus_init();
- listnav_init(&tag_nav);
- listnav_init(&track_nav);
-
- track_show_playlist = 0;
- update_track_playlist();
-
- cmd_status = NULL;
-
on_exit(cleanup, NULL);
signal(SIGINT, exit);
}
@@ -224,1202 +40,28 @@ init(void)
void
cleanup(int exitcode, void* arg)
{
- if (!exitcode) {
- tui_end();
- } else {
- curs_set(1);
- }
+ tui_restore();
data_save();
data_free();
- player_free();
-
- dbus_end();
-
- log_end();
-
- history_free(&track_search_history);
- history_free(&tag_search_history);
- history_free(&command_history);
-}
-
-void
-tui_init(void)
-{
- tui_ncurses_init();
-
- memset(style_attrs, 0, sizeof(style_attrs));
- style_init(STYLE_DEFAULT, COLOR_WHITE, COLOR_BLACK, 0);
- style_init(STYLE_TITLE, COLOR_WHITE, COLOR_BLUE, A_BOLD);
- style_init(STYLE_PANE_SEP, COLOR_BLUE, COLOR_BLACK, 0);
- style_init(STYLE_ITEM_SEL, COLOR_YELLOW, COLOR_BLACK, A_BOLD);
- style_init(STYLE_ITEM_HOVER, COLOR_WHITE, COLOR_BLUE, 0);
- style_init(STYLE_ITEM_HOVER_SEL, COLOR_YELLOW, COLOR_BLUE, A_BOLD);
- style_init(STYLE_ERROR, COLOR_RED, COLOR_BLACK, 0);
- style_init(STYLE_PREV, COLOR_WHITE, COLOR_BLACK, A_DIM);
-
- pane_init((tag_pane = &pane_left), tag_pane_input, tag_pane_vis);
- pane_init((track_pane = &pane_right), track_pane_input, track_pane_vis);
- pane_init((cmd_pane = &pane_bot), cmd_pane_input, cmd_pane_vis);
-
- pane_sel = &pane_left;
- pane_top_sel = pane_sel;
-}
-
-void
-tui_ncurses_init(void)
-{
- initscr();
-
- /* do most of the handling ourselves,
- * enable special keys */
- raw();
- noecho();
- keypad(stdscr, TRUE);
-
- /* update screen occasionally for things like
- * time even when no input was received */
- halfdelay(1);
-
- /* inits COLOR and COLOR_PAIRS used by styles */
- start_color();
-
- /* dont show cursor */
- curs_set(0);
-
- /* we use ESC deselecting the current pane
- * and not for escape sequences, so dont wait */
- ESCDELAY = 0;
-}
-
-void
-tui_resize(void)
-{
- struct link *iter;
- struct tag *tag;
- int i, leftw;
-
- getmaxyx(stdscr, scrh, scrw);
-
- /* guarantee a minimum terminal size */
- while (scrw < 10 || scrh < 4) {
- clear();
- refresh();
- usleep(10000);
- getmaxyx(stdscr, scrh, scrw);
- }
-
- /* adjust tag pane width to name lengths */
- leftw = 0;
- for (LIST_ITER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
- leftw = MAX(leftw, wcslen(tag->name));
- }
- leftw = MAX(leftw + 1, 0.2f * scrw);
-
- pane_resize(&pane_left, 0, 0, leftw, scrh - 3);
- pane_resize(&pane_right, pane_left.ex + 1, 0, scrw, scrh - 3);
- pane_resize(&pane_bot, 0, scrh - 3, scrw, scrh);
-}
-
-void
-tui_end(void)
-{
- pane_free(&pane_left);
- pane_free(&pane_right);
- pane_free(&pane_bot);
-
- if (!isendwin()) endwin();
-}
-
-void
-data_load(void)
-{
- struct dirent *ent;
- struct tag *tag;
- struct stat st;
- char *path;
- DIR *dir;
-
- list_init(&tracks);
- list_init(&tags);
- list_init(&tags_sel);
-
- datadir = getenv("TMUS_DATA");
- ASSERT(datadir != NULL);
-
- dir = opendir(datadir);
- ASSERT(dir != NULL);
- while ((ent = readdir(dir))) {
- if (!strcmp(ent->d_name, "."))
- continue;
- if (!strcmp(ent->d_name, ".."))
- continue;
-
- path = aprintf("%s/%s", datadir, ent->d_name);
- ASSERT(path != NULL);
-
- if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
- tag = tag_init(datadir, ent->d_name);
- tracks_load(tag);
- list_push_back(&tags, LINK(tag));
- }
-
- free(path);
- }
- closedir(dir);
-
- ASSERT(!list_empty(&tags));
-}
-
-void
-data_save(void)
-{
- struct link *iter;
- struct tag *tag;
-
- for (LIST_ITER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
- tracks_save(tag);
- }
-}
-
-void
-data_free(void)
-{
- struct link *track_link;
- struct link *tag_link;
- struct tag *tag;
-
- log_info("MAIN: data_free()\n");
-
- refs_free(&tracks);
- refs_free(&tags_sel);
-
- while (!list_empty(&tags)) {
- tag_link = list_pop_front(&tags);
-
- tag = UPCAST(tag_link, struct tag);
- while (!list_empty(&tag->tracks)) {
- track_link = list_pop_front(&tag->tracks);
- track_free(UPCAST(track_link, struct ref)->data);
- ref_free(UPCAST(track_link, struct ref));
- }
- tag_free(tag);
- }
-}
-
-int
-get_fid(const char *path)
-{
- struct stat st;
- return stat(path, &st) ? -1 : st.st_ino;
-}
-
-void
-index_update(struct tag *tag)
-{
- struct track *track, *track_iter;
- struct dirent *ent;
- struct link *iter;
- struct ref *ref;
- struct stat st;
- char *path;
- FILE *file;
- DIR *dir;
- int fid;
-
- path = aprintf("%s/index", tag->fpath);
- ASSERT(path != NULL);
-
- file = fopen(path, "w+");
- ASSERT(file != NULL);
- free(path);
-
- dir = opendir(tag->fpath);
- ASSERT(dir != NULL);
-
- while ((ent = readdir(dir))) {
- if (!strcmp(ent->d_name, "."))
- continue;
- if (!strcmp(ent->d_name, ".."))
- continue;
- if (!strcmp(ent->d_name, "index"))
- continue;
-
- /* skip files without extension */
- if (!strchr(ent->d_name + 1, '.'))
- continue;
-
- path = aprintf("%s/%s", tag->fpath, ent->d_name);
- ASSERT(path != NULL);
- fid = get_fid(path);
- free(path);
-
- fprintf(file, "%i:%s\n", fid, ent->d_name);
- }
-
- closedir(dir);
- fclose(file);
-}
-
-void
-tracks_load(struct tag *tag)
-{
- char linebuf[1024];
- struct link *link;
- struct track *track;
- struct track *track2;
- struct ref *ref;
- char *index_path;
- char *track_name, *sep;
- int track_fid;
- bool new_track;
- FILE *file;
-
- printf("Loading files from %s", tag->fpath);
- fflush(stdout);
-
- index_path = aprintf("%s/index", tag->fpath);
- ASSERT(index_path != NULL);
-
- file = fopen(index_path, "r");
- if (file == NULL) {
- index_update(tag);
- file = fopen(index_path, "r");
- ASSERT(file != NULL);
- }
-
- while (fgets(linebuf, sizeof(linebuf), file)) {
- sep = strchr(linebuf, '\n');
- if (sep) *sep = '\0';
-
- sep = strchr(linebuf, ':');
- ASSERT(sep != NULL);
- *sep = '\0';
-
- track_fid = atoi(linebuf);
- track_name = sep + 1;
- track = track_init(tag->fpath, track_name, track_fid);
-
- ref = ref_init(tag);
- ASSERT(ref != NULL);
- list_push_back(&track->tags, LINK(ref));
-
- ref = ref_init(track);
- ASSERT(ref != NULL);
- list_push_back(&tag->tracks, LINK(ref));
-
- new_track = true;
- for (LIST_ITER(&tracks, link)) {
- track2 = UPCAST(link, struct ref)->data;
- if (track->fid > 0 && track->fid == track2->fid)
- new_track = false;
- }
-
- if (new_track) {
- ref = ref_init(track);
- ASSERT(ref != NULL);
- list_push_back(&tracks, LINK(ref));
- }
- }
-
- /* clear line and reset cursor */
- printf("\x1b[0K\r");
-
- fclose(file);
- free(index_path);
-}
-
-void
-tracks_save(struct tag *tag)
-{
- struct track *track;
- struct link *link;
- char *index_path;
- FILE *file;
-
- /* write playlist back to index file */
-
- printf("Saving tracks to %s", tag->fpath);
-
- index_path = aprintf("%s/index", tag->fpath);
- ASSERT(index != NULL);
-
- file = fopen(index_path, "w+");
- ASSERT(file != NULL);
-
- for (LIST_ITER(&tag->tracks, link)) {
- track = UPCAST(link, struct ref)->data;
- fprintf(file, "%i:%s\n", track->fid, track->fname);
- }
-
- /* clear line and reset cursor */
- printf("\x1b[0K\r");
-
- fclose(file);
- free(index_path);
-}
-
-void
-rm_file(const char *path)
-{
- ASSERT(unlink(path) == 0);
-}
-
-void
-copy_file(const char *src, const char *dst)
-{
- FILE *in, *out;
- char buf[4096];
- int len, nread;
-
- in = fopen(src, "r");
- if (in == NULL)
- PANIC("Failed to open file %s", src);
-
- out = fopen(dst, "w+");
- if (out == NULL)
- PANIC("Failed to open file %s", dst);
-
- while ((nread = fread(buf, 1, sizeof(buf), in)) > 0) {
- fwrite(buf, 1, nread, out);
- }
-
- if (nread < 0)
- PANIC("Copy failed!", src, dst);
-
- fclose(in);
- fclose(out);
-}
-
-void
-move_file(const char *src, const char *dst)
-{
- copy_file(src, dst);
- rm_file(src);
-}
-
-struct tag *
-tag_find(const wchar_t *query)
-{
- struct link *iter;
- struct tag *tag;
-
- for (LIST_ITER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
- if (!wcscmp(tag->name, query)) {
- return tag;
- }
- }
-
- return NULL;
-}
-
-void
-pane_title(struct pane *pane, const char *title, int highlight)
-{
- wmove(pane->win, 0, 0);
-
- style_on(pane->win, STYLE_TITLE);
- if (highlight) ATTR_ON(pane->win, A_STANDOUT);
-
- wprintw(pane->win, " %-*.*s", pane->w - 1, pane->w - 1, title);
+ player_deinit();
- if (highlight) ATTR_OFF(pane->win, A_STANDOUT);
- style_off(pane->win, STYLE_TITLE);
-}
-
-void
-style_init(int style, int fg, int bg, int attr)
-{
- style_attrs[style] = attr;
- init_pair(style, fg, bg);
-}
-
-void
-style_on(WINDOW *win, int style)
-{
- ATTR_ON(win, COLOR_PAIR(style) | style_attrs[style]);
-}
-
-void
-style_off(WINDOW *win, int style)
-{
- ATTR_OFF(win, COLOR_PAIR(style) | style_attrs[style]);
-}
-
-wchar_t *
-command_name_gen(const wchar_t *text, int fwd, int reset)
-{
- static int index, len;
- int dir;
-
- dir = fwd ? 1 : -1;
-
- if (reset) {
- index = 0;
- len = wcslen(text);
- } else if (index >= -1 && index <= ARRLEN(cmds)) {
- index += dir;
- }
-
- while (index >= 0 && index < ARRLEN(cmds)) {
- if (!wcsncmp(cmds[index].name, text, len))
- return wcsdup(cmds[index].name);
- index += dir;
- }
-
- return NULL;
-}
-
-wchar_t *
-track_name_gen(const wchar_t *text, int fwd, int reset)
-{
- static struct link *cur;
- struct link *iter;
- struct track *track;
-
- if (reset) {
- cur = tracks.head.next;
- iter = cur;
- } else {
- iter = fwd ? cur->next : cur->prev;
- }
-
- while (iter && LIST_INNER(&tracks, iter)) {
- track = UPCAST(iter, struct ref)->data;
- if (wcsstr(track->name, text)) {
- cur = iter;
- return wcsdup(track->name);
- }
- iter = fwd ? iter->next : iter->prev;
- }
-
- return NULL;
-}
-
-wchar_t *
-tag_name_gen(const wchar_t *text, int fwd, int reset)
-{
- static struct link *cur;
- struct link *iter;
- struct tag *tag;
-
- if (reset) {
- cur = tags.head.next;
- iter = cur;
- } else {
- iter = fwd ? cur->next : cur->prev;
- }
-
- while (iter && LIST_INNER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
- if (wcsstr(tag->name, text)) {
- cur = iter;
- return wcsdup(tag->name);
- }
- iter = fwd ? iter->next : iter->prev;
- }
-
- return NULL;
-}
-
-void
-toggle_current_tag(void)
-{
- struct link *link, *iter;
- struct track *track;
- struct tag *tag;
- struct ref *ref;
- int in_tags, in_playlist;
-
- if (list_empty(&tags)) return;
-
- link = list_at(&tags, tag_nav.sel);
- ASSERT(link != NULL);
- tag = UPCAST(link, struct tag);
-
- /* toggle tag in tags_sel */
- if (refs_incl(&tags_sel, tag)) {
- refs_rm(&tags_sel, tag);
- } else {
- ref = ref_init(tag);
- list_push_back(&tags_sel, LINK(ref));
- }
-
- /* rebuild the full playlist */
- refs_free(&player->playlist);
- for (LIST_ITER(&tags_sel, link)) {
- tag = UPCAST(link, struct ref)->data;
- for (LIST_ITER(&tag->tracks, iter)) {
- ref = ref_init(UPCAST(iter, struct ref)->data);
- ASSERT(ref != NULL);
- list_push_back(&player->playlist, LINK(ref));
- }
- }
-}
-
-int
-tag_pane_input(wint_t c)
-{
- switch (c) {
- case KEY_UP:
- listnav_update_sel(&tag_nav, tag_nav.sel - 1);
- return 1;
- case KEY_DOWN:
- listnav_update_sel(&tag_nav, tag_nav.sel + 1);
- return 1;
- case KEY_SPACE:
- toggle_current_tag();
- return 1;
- case KEY_ENTER:
- refs_free(&tags_sel);
- toggle_current_tag();
- return 1;
- case KEY_PPAGE:
- listnav_update_sel(&tag_nav, tag_nav.sel - tag_nav.wlen / 2);
- return 1;
- case KEY_NPAGE:
- listnav_update_sel(&tag_nav, tag_nav.sel + tag_nav.wlen / 2);
- return 1;
- }
+ dbus_deinit();
- return 0;
-}
-
-void
-tag_pane_vis(struct pane *pane, int sel)
-{
- struct tag *tag;
- struct link *iter;
- int index, tagsel;
-
- werase(pane->win);
- pane_title(pane, "Tags", sel);
-
- listnav_update_bounds(&tag_nav, 0, list_len(&tags));
- listnav_update_wlen(&tag_nav, pane->h - 1);
+ tui_deinit();
- index = -1;
- for (LIST_ITER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
- tagsel = refs_incl(&tags_sel, tag);
-
- index += 1;
- if (index < tag_nav.wmin) continue;
- if (index >= tag_nav.wmax) break;
-
- if (sel && tagsel && index == tag_nav.sel)
- style_on(pane->win, STYLE_ITEM_HOVER_SEL);
- else if (sel && index == tag_nav.sel)
- style_on(pane->win, STYLE_ITEM_HOVER);
- else if (tagsel)
- style_on(pane->win, STYLE_ITEM_SEL);
- else if (index == tag_nav.sel)
- style_on(pane->win, STYLE_PREV);
-
- wmove(pane->win, 1 + index - tag_nav.wmin, 0);
- wprintw(pane->win, "%-*.*ls", pane->w, pane->w, tag->name);
-
- if (sel && tagsel && index == tag_nav.sel)
- style_off(pane->win, STYLE_ITEM_HOVER_SEL);
- else if (sel && index == tag_nav.sel)
- style_off(pane->win, STYLE_ITEM_HOVER);
- else if (tagsel)
- style_off(pane->win, STYLE_ITEM_SEL);
- else if (index == tag_nav.sel)
- style_off(pane->win, STYLE_PREV);
- }
-}
-
-int
-track_pane_input(wint_t c)
-{
- struct link *link;
- struct track *track;
-
- switch (c) {
- case KEY_UP:
- listnav_update_sel(&track_nav, track_nav.sel - 1);
- return 1;
- case KEY_DOWN:
- listnav_update_sel(&track_nav, track_nav.sel + 1);
- return 1;
- case KEY_ENTER:
- link = list_at(tracks_vis, track_nav.sel);
- if (!link) return 1;
- track = UPCAST(link, struct ref)->data;
- player_play_track(track);
- return 1;
- case KEY_PPAGE:
- listnav_update_sel(&track_nav,
- track_nav.sel - track_nav.wlen / 2);
- return 1;
- case KEY_NPAGE:
- listnav_update_sel(&track_nav,
- track_nav.sel + track_nav.wlen / 2);
- return 1;
- }
-
- return 0;
-}
-
-void
-track_pane_vis(struct pane *pane, int sel)
-{
- struct track *track;
- struct link *iter;
- struct tag *tag;
- int index;
-
- werase(pane->win);
- pane_title(pane, "Tracks", sel);
-
- listnav_update_bounds(&track_nav, 0, list_len(tracks_vis));
- listnav_update_wlen(&track_nav, pane->h - 1);
-
- index = -1;
- for (LIST_ITER(tracks_vis, iter)) {
- track = UPCAST(iter, struct ref)->data;
-
- index += 1;
- if (index < track_nav.wmin) continue;
- if (index >= track_nav.wmax) break;
-
- if (sel && index == track_nav.sel && track == player->track)
- style_on(pane->win, STYLE_ITEM_HOVER_SEL);
- else if (sel && index == track_nav.sel)
- style_on(pane->win, STYLE_ITEM_HOVER);
- else if (track == player->track)
- style_on(pane->win, STYLE_ITEM_SEL);
- else if (index == track_nav.sel)
- style_on(pane->win, STYLE_PREV);
-
- wmove(pane->win, 1 + index - track_nav.wmin, 0);
- wprintw(pane->win, "%-*.*ls", pane->w, pane->w, track->name);
-
- if (sel && index == track_nav.sel && track == player->track)
- style_off(pane->win, STYLE_ITEM_HOVER_SEL);
- else if (sel && index == track_nav.sel)
- style_off(pane->win, STYLE_ITEM_HOVER);
- else if (track == player->track)
- style_off(pane->win, STYLE_ITEM_SEL);
- else if (index == track_nav.sel)
- style_off(pane->win, STYLE_PREV);
- }
-}
-
-int
-run_cmd(const wchar_t *query)
-{
- const wchar_t *sep;
- int i, cmdlen;
-
- sep = wcschr(query, L' ');
- cmdlen = sep ? sep - query : wcslen(query);
- for (i = 0; i < ARRLEN(cmds); i++) {
- if (!wcsncmp(cmds[i].name, query, cmdlen)) {
- if (!cmds[i].func(sep ? sep + 1 : NULL)) {
- free(cmd_status);
- cmd_status = wcsdup(L"Command Failed!\n");
- ASSERT(cmd_status != NULL);
- }
- return 1;
- }
- }
-
- return 0;
-}
-
-int
-play_track(const wchar_t *query)
-{
- struct track *track;
- struct link *iter;
-
- for (LIST_ITER(&tracks, iter)) {
- track = UPCAST(iter, struct ref)->data;
- if (wcsstr(track->name, query)) {
- player_play_track(track);
- return 1;
- }
- }
-
- return 0;
-}
-
-int
-select_tag(const wchar_t *query)
-{
- struct tag *tag;
- struct link *iter;
- int index;
-
- index = -1;
- for (LIST_ITER(&tags, iter)) {
- index += 1;
- tag = UPCAST(iter, struct tag);
- if (wcsstr(tag->name, query)) {
- listnav_update_sel(&tag_nav, index);
- return 1;
- }
- }
-
- return 0;
-}
-
-int
-cmd_pane_input(wint_t c)
-{
- wchar_t *res;
- int match;
-
- switch (c) {
- case KEY_ESC:
- match = wcscmp(completion_query.buf, history->input->buf);
- if (!completion_reset && match)
- inputln_copy(history->input, &completion_query);
- else if (history->sel == history->input)
- pane_sel = pane_top_sel;
- else
- history->sel = history->input;
- break;
- case KEY_LEFT:
- inputln_left(history->sel);
- break;
- case KEY_RIGHT:
- inputln_right(history->sel);
- break;
- case KEY_CTRL('w'):
- inputln_del(history->sel, history->sel->cur);
- break;
- case KEY_UP:
- history_next(history);
- break;
- case KEY_DOWN:
- history_prev(history);
- break;
- case KEY_ENTER:
- if (!*history->sel->buf) {
- pane_sel = pane_top_sel;
- break;
- }
-
- if (cmd_mode == IMODE_EXECUTE) {
- run_cmd(history->sel->buf);
- } else if (cmd_mode == IMODE_TRACK_SEARCH) {
- play_track(history->sel->buf);
- } else if (cmd_mode == IMODE_TAG_SEARCH) {
- select_tag(history->sel->buf);
- }
-
- history_submit(history);
- pane_sel = pane_top_sel;
- break;
- case KEY_TAB:
- case KEY_BTAB:
- if (history->sel != history->input) {
- inputln_copy(history->input, history->sel);
- history->sel = history->input;
- }
-
- if (completion_reset)
- inputln_copy(&completion_query, history->input);
-
- res = completion(completion_query.buf,
- c == KEY_TAB, completion_reset);
- if (res) inputln_replace(history->input, res);
- free(res);
-
- completion_reset = 0;
- break;
- case KEY_BACKSPACE:
- if (history->sel->cur == 0) {
- pane_sel = pane_top_sel;
- break;
- }
- inputln_del(history->sel, 1);
- completion_reset = 1;
- break;
- default:
- if (!iswprint(c)) return 0;
- inputln_addch(history->sel, c);
- completion_reset = 1;
- break;
- }
-
- return 1; /* grab everything */
-}
-
-void
-cmd_pane_vis(struct pane *pane, int sel)
-{
- static wchar_t *linebuf = NULL;
- static int linecap = 0;
- struct inputln *cmd;
- struct link *iter;
- wchar_t *line, *end;
- int index, offset;
-
- werase(pane->win);
-
- /* TODO add clear_line func to fill with spaces instead of
- * filling buffer */
-
- /* static line buffer for perf */
- if (pane->w > linecap) {
- linecap = MAX(linecap, pane->w);
- linebuf = realloc(linebuf, linecap * sizeof(wchar_t));
- }
- end = linebuf + linecap;
-
- /* track name */
- style_on(pane->win, STYLE_TITLE);
- pane_clearln(pane, 0);
- if (player->track) {
- swprintf(linebuf, linecap, L"%ls", player->track->name);
- mvwaddwstr(pane->win, 0, 1, linebuf);
- }
- style_off(pane->win, STYLE_TITLE);
-
- if (player->loaded) {
- /* status line */
- line = linebuf;
- line += swprintf(line, end - line, L"%c ",
- player_state_chars[player->state]);
- line += swprintf(line, end - line, L"%s / ",
- timestr(player->time_pos));
- line += swprintf(line, end - line, L"%s",
- timestr(player->time_end));
-
- if (player->volume >= 0) {
- line += swprintf(line, end - line, L" - vol: %u%%",
- player->volume);
- }
-
- if (player->msg) {
- line += swprintf(line, end - line, L" | [PLAYER] %s",
- player->msg);
- }
-
- if (!list_empty(&player->queue)) {
- line += swprintf(line, end - line,
- L" | [QUEUE] %i tracks",
- list_len(&player->queue));
- }
-
- ATTR_ON(pane->win, A_REVERSE);
- pane_clearln(pane, 1);
- mvwaddwstr(pane->win, 1, 0, linebuf);
- ATTR_OFF(pane->win, A_REVERSE);
- } else if (player->msg) {
- /* player message */
- line = linebuf;
- line += swprintf(line, linecap, L"[PLAYER] %s", player->msg);
- line += swprintf(line, end - line, L"%*.*s",
- pane->w, pane->w, L" ");
-
- pane_clearln(pane, 1);
- mvwaddwstr(pane->win, 1, 0, linebuf);
- }
-
- /* status bits on right of status line */
- if (player->loaded) ATTR_ON(pane->win, A_REVERSE);
- mvwaddstr(pane->win, 1, pane->w - 5, "[ ]");
- if (track_show_playlist) mvwaddstr(pane->win, 1, pane->w - 4, "P");
- if (player->autoplay) mvwaddstr(pane->win, 1, pane->w - 3, "A");
- if (player->shuffle) mvwaddstr(pane->win, 1, pane->w - 2, "S");
- if (player->loaded) ATTR_OFF(pane->win, A_REVERSE);
-
- if (sel || cmd_show) {
- /* cmd and search input */
- line = linebuf;
-
- free(cmd_status);
- cmd_status = NULL;
-
- cmd = history->sel;
- if (cmd != history->input) {
- index = 0;
- for (LIST_ITER(&history->list, iter)) {
- if (UPCAST(iter, struct inputln) == cmd)
- break;
- }
- line += swprintf(line, end - line, L"[%i] ",
- iter ? index : -1);
- } else {
- line += swprintf(line, end - line, L"%c",
- imode_prefix[cmd_mode]);
- }
- offset = wcslen(linebuf);
-
- line += swprintf(line, end - line, L"%ls", cmd->buf);
-
- pane_clearln(pane, 2);
- mvwaddwstr(pane->win, 2, 0, linebuf);
-
- if (sel) { /* show cursor in text */
- ATTR_ON(pane->win, A_REVERSE);
- wmove(pane->win, 2, offset + cmd->cur);
- waddch(pane->win, cmd->cur < cmd->len
- ? cmd->buf[cmd->cur] : L' ');
- ATTR_OFF(pane->win, A_REVERSE);
- }
- } else if (cmd_status) {
- pane_clearln(pane, 2);
- style_on(pane->win, STYLE_ERROR);
- mvwaddwstr(pane->win, 2, 1, cmd_status);
- style_off(pane->win, STYLE_ERROR);
- }
-}
-
-void
-queue_hover(void)
-{
- struct link *link;
-
- link = list_at(&player->playlist, track_nav.sel);
- if (!link) return;
- player_queue_append(UPCAST(link, struct ref)->data);
-}
-
-void
-update_track_playlist(void)
-{
- struct link *link;
- struct tag *tag;
-
- if (track_show_playlist) {
- tracks_vis = &player->playlist;
- } else {
- link = list_at(&tags, tag_nav.sel);
- if (!link) return;
- tag = UPCAST(link, struct tag);
- tracks_vis = &tag->tracks;
- }
-}
-
-void
-main_input(wint_t c)
-{
- switch (c) {
- case KEY_CTRL('r'):
- redrawwin(stdscr);
- break;
- case KEY_TAB:
- pane_sel = pane_sel == &pane_left
- ? &pane_right : &pane_left;
- pane_top_sel = pane_sel;
- break;
- case KEY_ESC:
- pane_sel = pane_top_sel;
- break;
- case KEY_LEFT:
- if (!player->loaded) break;
- player_seek(MAX(player->time_pos - 10, 0));
- break;
- case KEY_RIGHT:
- if (!player->loaded) break;
- player_seek(MIN(player->time_pos + 10, player->time_end));
- break;
- case L'y':
- queue_hover();
- break;
- case L'o':
- player_queue_clear();
- break;
- case L'c':
- player_toggle_pause();
- break;
- case L'n':
- case L'>':
- player_next();
- break;
- case L'p':
- case L'<':
- player_prev();
- break;
- case L'P':
- if (track_show_playlist) {
- pane_sel = tag_pane;
- track_show_playlist = 0;
- } else {
- pane_sel = track_pane;
- track_show_playlist = 1;
- }
- break;
- case L'A':
- player->autoplay ^= 1;
- break;
- case L'S':
- player->shuffle ^= 1;
- break;
- case L'b':
- player_seek(0);
- break;
- case L'x':
- if (player->state == PLAYER_STATE_PLAYING) {
- player_stop();
- } else {
- player_play();
- }
- break;
- case L':':
- cmd_mode = IMODE_EXECUTE;
- pane_sel = &pane_bot;
- completion_reset = 1;
- history = &command_history;
- completion = command_name_gen;
- break;
- case L'/':
- cmd_mode = IMODE_TRACK_SEARCH;
- pane_sel = &pane_bot;
- completion_reset = 1;
- history = &track_search_history;
- completion = track_name_gen;
- break;
- case L'?':
- cmd_mode = IMODE_TAG_SEARCH;
- pane_sel = &pane_bot;
- completion_reset = 1;
- history = &tag_search_history;
- completion = tag_name_gen;
- break;
- case L'+':
- player_set_volume(MIN(100, player->volume + 5));
- break;
- case L'-':
- player_set_volume(MAX(0, player->volume - 5));
- break;
- case L'q':
- quit = 1;
- break;
- }
-}
-
-void
-main_vis(void)
-{
- int i;
-
- /* add missing title tile at the top */
- style_on(stdscr, STYLE_TITLE);
- move(0, pane_left.ex);
- addch(' ');
- style_off(stdscr, STYLE_TITLE);
-
- /* draw left-right separator line */
- style_on(stdscr, STYLE_PANE_SEP);
- for (i = pane_left.sy + 1; i < pane_left.ey; i++) {
- move(i, pane_left.ex);
- addch(ACS_VLINE);
- }
- style_off(stdscr, STYLE_PANE_SEP);
-}
-
-int
-cmd_save(const wchar_t *args)
-{
- data_save();
- return 0;
-}
-
-int
-cmd_move(const wchar_t *name)
-{
- struct link *link;
- struct track *track;
- struct tag *tag;
- char *newpath;
-
- tag = tag_find(name);
- if (!tag) CMD_ERROR(L"Tag not found");
-
- link = list_at(tracks_vis, track_nav.sel);
- if (!link) CMD_ERROR(L"No track selected");
- track = UPCAST(link, struct ref)->data;
-
- newpath = aprintf("%s/%s", tag->fpath, track->fname);
- ASSERT(newpath != NULL);
- move_file(track->fpath, newpath);
- free(track->fpath);
- track->fpath = newpath;
-
- link_pop(link);
- list_push_back(&tag->tracks, link);
-
- return 1;
-}
-
-int
-cmd_add(const wchar_t *name)
-{
- struct link *link;
- struct track *track;
- struct ref *ref;
- struct tag *tag;
- char *newpath;
-
- tag = tag_find(name);
- if (!tag) return 0;
-
- link = list_at(&player->playlist, track_nav.sel);
- if (!link) return 0;
- track = UPCAST(link, struct ref)->data;
-
- newpath = aprintf("%s/%s", tag->fpath, track->fname);
- ASSERT(newpath != NULL);
- copy_file(track->fpath, newpath);
- track->fpath = newpath;
-
- track = track_init(tag->fpath, track->fname, get_fid(tag->fpath));
- ref = ref_init(track);
- list_push_back(&tag->tracks, &ref->link);
-
- return 1;
+ log_deinit();
}
int
main(int argc, const char **argv)
{
- int i, handled;
- wint_t c;
-
init();
- c = KEY_RESIZE;
do {
dbus_update();
player_update();
-
- if (c == KEY_RESIZE) {
- tui_resize();
- } else if (c != ERR) {
- handled = 0;
- if (pane_sel && pane_sel->active)
- handled = pane_sel->handle(c);
-
- /* fallback if char not handled by pane */
- if (!handled) main_input(c);
- }
-
- update_track_playlist();
-
- refresh();
- for (i = 0; i < ARRLEN(panes); i++) {
- /* only update ui for panes that are visible */
- if (!panes[i]->active) continue;
-
- panes[i]->update(panes[i], pane_sel == panes[i]);
- wnoutrefresh(panes[i]->win);
- }
-
- main_vis();
- doupdate();
-
- get_wch(&c);
- } while (!quit);
+ } while (tui_update());
}