tmus

TUI Music Player
git clone https://git.sinitax.com/sinitax/tmus
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

commit 66dd37c0018bd958ea1a644e3552bce66b13f658
parent 5d8e3d19823a3c193b0ad4e8d8fd3ccdf8f77c80
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sun, 27 Feb 2022 13:59:28 +0100

Switch from mostly ref based tracking to multi-link architecture

Diffstat:
MMakefile | 2+-
Msrc/cmd.c | 110+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/data.c | 628++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/data.h | 52++++++++++++++++++++++++++++++++++++++--------------
Msrc/history.c | 16++++++++--------
Msrc/main.c | 5++---
Msrc/player.h | 17+++++++----------
Msrc/player_mpd.c | 174+++++++++++++++++++++++++++++++++++--------------------------------------------
Msrc/ref.c | 33+++++++++++++++++----------------
Msrc/ref.h | 3+++
Dsrc/tag.c | 38--------------------------------------
Dsrc/tag.h | 14--------------
Dsrc/track.c | 37-------------------------------------
Dsrc/track.h | 13-------------
Msrc/tui.c | 290+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/util.h | 3+--
16 files changed, 745 insertions(+), 690 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ CFLAGS = -I src -g $(shell pkg-config --cflags glib-2.0 dbus-1) -CFLAGS += -I lib/liblist/include +CFLAGS += -I lib/liblist/include -Wunused-variable LDLIBS = -lcurses -lmpdclient $(shell pkg-config --libs glib-2.0 dbus-1) DEPFLAGS = -MT $@ -MMD -MP -MF build/$*.d diff --git a/src/cmd.c b/src/cmd.c @@ -4,8 +4,6 @@ #include "list.h" #include "player.h" #include "ref.h" -#include "tag.h" -#include "track.h" #include "tui.h" #include "util.h" @@ -49,7 +47,7 @@ bool cmd_move(const char *name) { struct link *link; - struct track *track; + struct track *track, *new; struct tag *tag; char *newpath; @@ -58,49 +56,56 @@ cmd_move(const char *name) link = list_at(tracks_vis, track_nav.sel); if (!link) CMD_ERROR("No track selected"); - track = UPCAST(link, struct ref)->data; + track = tracks_vis_track(link); newpath = aprintf("%s/%s", tag->fpath, track->name); OOM_CHECK(newpath); + if (!dup_file(track->fpath, newpath)) { + free(newpath); + CMD_ERROR("Failed to move track"); + } + free(newpath); - if (!move_file(track->fpath, newpath)) - CMD_ERROR("Failed to move file"); - free(track->fpath); - track->fpath = newpath; - - link_pop(link); - list_push_back(&tag->tracks, link); + new = track_add(tag, track->name); + if (!new) { + rm_file(track->fpath); + ERROR("Failed to move track"); + } - playlist_update(); + if (!track_rm(track, true)) + ERROR("Failed to move track"); - return 1; + return true; } bool cmd_copy(const char *name) { struct link *link; - struct track *track; - struct ref *ref; + struct track *track, *new; 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; + link = list_at(tracks_vis, track_nav.sel); + if (!link) CMD_ERROR("No track selected"); + track = tracks_vis_track(link); newpath = aprintf("%s/%s", tag->fpath, track->name); OOM_CHECK(newpath); + if (!dup_file(track->fpath, newpath)) { + free(newpath); + ERROR("Failed to copy track"); + } + free(newpath); - copy_file(track->fpath, newpath); - track->fpath = newpath; - - track = track_alloc(tag->fpath, track->name, get_fid(tag->fpath)); - ref = ref_alloc(track); - list_push_back(&tag->tracks, &ref->link); + new = track_add(tag, track->name); + if (!new) { + rm_file(track->fpath); + CMD_ERROR("Failed to copy track"); + } return 1; } @@ -110,6 +115,7 @@ cmd_reindex(const char *name) { struct link *link; struct tag *tag; + struct ref *ref; struct list matches; list_init(&matches); @@ -117,18 +123,21 @@ cmd_reindex(const char *name) if (!*name) { link = list_at(&tags, tag_nav.sel); if (!link) return false; - tag = UPCAST(link, struct tag); - list_push_back(&matches, LINK(ref_alloc(tag))); + tag = UPCAST(link, struct tag, link); + ref = ref_alloc(tag); + list_push_back(&matches, &ref->link); } else if (!strcmp(name, "*")) { for (LIST_ITER(&tags, link)) { - tag = UPCAST(link, struct tag); - list_push_back(&matches, LINK(ref_alloc(tag))); + tag = UPCAST(link, struct tag, link); + ref = ref_alloc(tag); + list_push_back(&matches, &ref->link); } } else { for (LIST_ITER(&tags, link)) { - tag = UPCAST(link, struct tag); + tag = UPCAST(link, struct tag, link); if (!strcmp(tag->name, name)) { - list_push_back(&matches, LINK(ref_alloc(tag))); + ref = ref_alloc(tag); + list_push_back(&matches, &ref->link); break; } } @@ -137,8 +146,8 @@ cmd_reindex(const char *name) if (list_empty(&matches)) return false; for (LIST_ITER(&matches, link)) { - tag = UPCAST(link, struct ref)->data; - if (!tracks_update(tag)) + ref = UPCAST(link, struct ref, link); + if (!tracks_update(ref->data)) return false; } @@ -152,35 +161,27 @@ cmd_add_tag(const char *name) { struct link *link; struct tag *tag; - char *fname, *fpath; + char *fpath; for (LIST_ITER(&tags, link)) { - tag = UPCAST(link, struct tag); + tag = UPCAST(link, struct tag, link); if (!strcmp(tag->name, name)) CMD_ERROR("Tag already exists"); } - fname = aprintf("%s", name); - OOM_CHECK(fname); - - fpath = aprintf("%s/%s", datadir, fname); + fpath = aprintf("%s/%s", datadir, name); OOM_CHECK(fpath); if (!make_dir(fpath)) { - CMD_SET_STATUS("Failed to create dir"); - free(fname); free(fpath); - return false; + CMD_ERROR("Failed to create dir"); } - tag = tag_alloc(datadir, fname); - OOM_CHECK(tag); - - list_push_back(&tags, LINK(tag)); - - free(fname); free(fpath); + tag = tag_add(name); + if (!tag) CMD_ERROR("Failed to add tag"); + return true; } @@ -189,15 +190,14 @@ cmd_rm_tag(const char *name) { struct link *link; struct tag *tag; - char *fname, *fpath; if (!*name) { link = list_at(&tags, tag_nav.sel); if (!link) return false; - tag = UPCAST(link, struct tag); + tag = UPCAST(link, struct tag, link); } else { for (LIST_ITER(&tags, link)) { - tag = UPCAST(link, struct tag); + tag = UPCAST(link, struct tag, link); if (!strcmp(tag->name, name)) break; } @@ -206,13 +206,8 @@ cmd_rm_tag(const char *name) CMD_ERROR("No such tag"); } - if (!rm_dir(tag->fpath, true)) { - tracks_update(tag); /* in case some deleted, some not */ - CMD_ERROR("Failed to remove dir"); - } - - link_pop(LINK(tag)); - tag_free(tag); + if (!tag_rm(tag, true)) + CMD_ERROR("Failed to remove tag"); return true; } @@ -235,7 +230,6 @@ cmd_run(const char *query, bool *found) { const char *sep, *args; int i, cmdlen; - bool success; *found = false; sep = strchr(query, ' '); diff --git a/src/data.c b/src/data.c @@ -3,9 +3,6 @@ #include "player.h" #include "list.h" #include "log.h" -#include "ref.h" -#include "track.h" -#include "tag.h" #include "tui.h" #include <fts.h> @@ -18,88 +15,21 @@ const char *datadir; -struct list tracks; -struct list tags; -struct list tags_sel; +struct list tracks; /* struct track (link) */ +struct list tags; /* struct track (link) */ +struct list tags_sel; /* struct tag (link_sel) */ -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"); - if (!datadir) ERROR("TMUS_DATA not set!\n"); - - dir = opendir(datadir); - if (!dir) ERROR("Failed to access dir: %s\n", datadir); - - while ((ent = readdir(dir))) { - if (!strcmp(ent->d_name, ".")) - continue; - if (!strcmp(ent->d_name, "..")) - continue; - - path = aprintf("%s/%s", datadir, ent->d_name); - OOM_CHECK(path); - - if (!stat(path, &st) && S_ISDIR(st.st_mode)) { - tag = tag_alloc(datadir, ent->d_name); - OOM_CHECK(tag); - tracks_load(tag); - list_push_back(&tags, LINK(tag)); - } - - free(path); - } - closedir(dir); - - list_sort(&tracks, track_fid_compare); -} - -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"); +static int get_fid(const char *path); - refs_free(&tracks); - refs_free(&tags_sel); +static struct tag *tag_alloc(const char *path, const char *fname); +static void tag_free(struct tag *tag); - while (!list_empty(&tags)) { - tag_link = list_pop_front(&tags); +static struct track *track_alloc(const char *path, const char *fname); +static void track_free(struct track *t); +static int track_name_compare(struct link *a, struct link *b); - 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); - } -} +static void tracks_load(struct tag *tag); +static void tracks_save(struct tag *tag); int get_fid(const char *path) @@ -108,168 +38,86 @@ get_fid(const char *path) return stat(path, &st) ? -1 : st.st_ino; } -int -track_fid_compare(struct link *a, struct link *b) -{ - struct track *ta, *tb; - - ta = UPCAST(a, struct ref)->data; - tb = UPCAST(b, struct ref)->data; - - return ta->fid - tb->fid; -} - -void -index_update(struct tag *tag) +struct tag * +tag_alloc(const char *path, const char *fname) { - struct dirent *ent; - struct stat st; - char *path; - FILE *file; - DIR *dir; - int fid; + struct tag *tag; - path = aprintf("%s/index", tag->fpath); - OOM_CHECK(path); + tag = malloc(sizeof(struct tag)); + ASSERT(tag != NULL); - dir = opendir(tag->fpath); - if (!dir) ERROR("Failed to access dir: %s\n", tag->fpath); + tag->fpath = aprintf("%s/%s", path, fname); + ASSERT(tag->fpath != NULL); - file = fopen(path, "w+"); - if (!file) ERROR("Failed to create file: %s\n", path); - free(path); + tag->name = strdup(fname); + ASSERT(tag->name != NULL); - while ((ent = readdir(dir))) { - if (!strcmp(ent->d_name, ".")) - continue; - if (!strcmp(ent->d_name, "..")) - continue; - if (!strcmp(ent->d_name, "index")) - continue; + tag->link = LINK_EMPTY; + tag->link_sel = LINK_EMPTY; - /* skip files without extension */ - if (!strchr(ent->d_name + 1, '.')) - continue; + list_init(&tag->tracks); - path = aprintf("%s/%s", tag->fpath, ent->d_name); - OOM_CHECK(path); - fid = get_fid(path); - free(path); - - fprintf(file, "%i:%s\n", fid, ent->d_name); - } + return tag; +} - closedir(dir); - fclose(file); +void +tag_free(struct tag *tag) +{ + free(tag->fpath); + free(tag->name); + list_clear(&tag->tracks); + free(tag); } -bool -tracks_update(struct tag *tag) +struct track * +track_alloc(const char *dir, const char *fname) { - struct dirent *ent; - struct stat st; - struct link *link; - struct ref *ref; struct track *track; - char *path; - DIR *dir; - int fid; - - dir = opendir(tag->fpath); - if (!dir) return false; - while (!list_empty(&tag->tracks)) { - link = list_pop_front(&tag->tracks); - ref = UPCAST(link, struct ref); - track = ref->data; - ref_free(ref); - for (LIST_ITER(&tracks, link)) { - ref = UPCAST(link, struct ref); - if (ref->data == track) { - link = link_pop(link); - ref_free(ref); - break; - } - } - if (player.track == track) - player.track = NULL; - track_free(track); - } + track = malloc(sizeof(struct track)); + ASSERT(track != NULL); - while ((ent = readdir(dir))) { - if (!strcmp(ent->d_name, ".")) - continue; - if (!strcmp(ent->d_name, "..")) - continue; - if (!strcmp(ent->d_name, "index")) - continue; + track->fpath = aprintf("%s/%s", dir, fname); + ASSERT(track->fpath != NULL); - /* skip files without extension */ - if (!strchr(ent->d_name + 1, '.')) - continue; + track->name = strdup(fname); + ASSERT(track->name != NULL); - path = aprintf("%s/%s", tag->fpath, ent->d_name); - OOM_CHECK(path); + track->tag = NULL; - fid = get_fid(path); + track->link = LINK_EMPTY; + track->link_pl = LINK_EMPTY; + track->link_tt = LINK_EMPTY; + track->link_pq = LINK_EMPTY; + track->link_hs = LINK_EMPTY; - track = track_alloc(tag->fpath, ent->d_name, fid); - OOM_CHECK(track); - - ref = ref_alloc(tag); - OOM_CHECK(ref); - list_push_back(&track->tags, LINK(ref)); - - ref = ref_alloc(track); - OOM_CHECK(ref); - list_push_back(&tag->tracks, LINK(ref)); - - ref = ref_alloc(track); - OOM_CHECK(ref); - list_push_back(&tracks, LINK(ref)); - - free(path); - } - - list_sort(&tracks, track_fid_compare); - - playlist_update(); - - closedir(dir); - return true; + return track; } void -playlist_update(void) +track_free(struct track *t) { - struct link *link, *link2; - struct track *track; - struct ref *ref; - struct tag *tag; + free(t->fpath); + free(t->name); + free(t); +} - refs_free(&player.playlist); - for (LIST_ITER(&tags_sel, link)) { - tag = UPCAST(link, struct ref)->data; - for (LIST_ITER(&tag->tracks, link2)) { - track = UPCAST(link2, struct ref)->data; - ref = ref_alloc(track); - ASSERT(ref != NULL); - list_push_back(&player.playlist, LINK(ref)); - } - } +int +track_name_compare(struct link *a, struct link *b) +{ + struct track *ta, *tb; + + ta = UPCAST(a, struct track, link); + tb = UPCAST(b, struct track, link); + + return strcmp(ta->name, tb->name); } 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; FILE *file; index_path = aprintf("%s/index", tag->fpath); @@ -283,28 +131,10 @@ tracks_load(struct tag *tag) } while (fgets(linebuf, sizeof(linebuf), file)) { - sep = strchr(linebuf, '\n'); - if (sep) *sep = '\0'; - - sep = strchr(linebuf, ':'); - if (!sep) ERROR("Syntax error in index file: %s\n", index_path); - *sep = '\0'; - - track_fid = atoi(linebuf); - track_name = sep + 1; - track = track_alloc(tag->fpath, track_name, track_fid); - - ref = ref_alloc(tag); - OOM_CHECK(ref); - list_push_back(&track->tags, LINK(ref)); - - ref = ref_alloc(track); - OOM_CHECK(ref); - list_push_back(&tag->tracks, LINK(ref)); - - ref = ref_alloc(track); - OOM_CHECK(ref); - list_push_back(&tracks, LINK(ref)); + if (!*linebuf) continue; + if (linebuf[strlen(linebuf) - 1] == '\n') + linebuf[strlen(linebuf) - 1] = '\0'; + track_add(tag, linebuf); } fclose(file); @@ -333,8 +163,8 @@ tracks_save(struct tag *tag) } for (LIST_ITER(&tag->tracks, link)) { - track = UPCAST(link, struct ref)->data; - fprintf(file, "%i:%s\n", track->fid, track->name); + track = UPCAST(link, struct track, link_tt); + fprintf(file, "%s\n", track->name); } fclose(file); @@ -342,36 +172,6 @@ tracks_save(struct tag *tag) } bool -track_rm(struct track *track) -{ - struct link *link; - struct ref *ref; - struct tag *tag; - - if (!rm_file(track->fpath)) { - CMD_SET_STATUS("Failed to remove track"); - return false; - } - - refs_rm(&tracks, track); - - for (LIST_ITER(&track->tags, link)) { - ref = UPCAST(link, struct ref); - tag = ref->data; - refs_rm(&tag->tracks, track); - } - - refs_rm(&player.playlist, track); - - if (player.track == track) - player.track = NULL; - - track_free(track); - - return true; -} - -bool make_dir(const char *path) { return mkdir(path, S_IRWXU | S_IRWXG) == 0; @@ -428,7 +228,7 @@ copy_file(const char *src, const char *dst) { FILE *in, *out; char buf[4096]; - int len, nread; + int nread; bool ok; ok = false; @@ -457,25 +257,305 @@ cleanup: } bool +dup_file(const char *src, const char *dst) +{ + /* TODO add option for hard link dup if possible */ + return copy_file(src, dst); +} + +bool move_file(const char *src, const char *dst) { return rename(src, dst) == 0; } +void +index_update(struct tag *tag) +{ + struct dirent *ent; + char *index_path; + FILE *file; + DIR *dir; + + dir = opendir(tag->fpath); + if (!dir) ERROR("Failed to access dir: %s\n", tag->fpath); + + index_path = aprintf("%s/index", tag->fpath); + OOM_CHECK(index_path); + + file = fopen(index_path, "w+"); + if (!file) ERROR("Failed to create index file in dir %s\n", tag->name); + free(index_path); + + while ((ent = readdir(dir))) { + if (!strcmp(ent->d_name, ".")) + continue; + if (!strcmp(ent->d_name, "..")) + continue; + + /* skip files without extension */ + if (!strchr(ent->d_name + 1, '.')) + continue; + + fprintf(file, "%s\n", ent->d_name); + } + + closedir(dir); + fclose(file); +} + +bool +tracks_update(struct tag *tag) +{ + struct link *link; + struct track *track; + struct dirent *ent; + DIR *dir; + + dir = opendir(tag->fpath); + if (!dir) return false; + + while (!list_empty(&tag->tracks)) { + link = list_pop_front(&tag->tracks); + track = UPCAST(link, struct track, link_tt); + track_rm(track, false); + } + + while ((ent = readdir(dir))) { + if (!strcmp(ent->d_name, ".")) + continue; + if (!strcmp(ent->d_name, "..")) + continue; + + /* skip files without extension */ + if (!strchr(ent->d_name + 1, '.')) + continue; + + track_add(tag, ent->d_name); + } + + closedir(dir); + + return true; +} + +struct track * +tracks_vis_track(struct link *link) +{ + if (tracks_vis == &player.playlist) { + return UPCAST(link, struct track, link_pl); + } else { + return UPCAST(link, struct track, link_tt); + } +} + +void +playlist_clear(void) +{ + while (!list_empty(&player.playlist)) + list_pop_front(&player.playlist); +} + +void +playlist_update(bool exec) +{ + static bool update = false; + struct link *link, *link2; + struct track *track; + struct tag *tag; + + if (!exec) { + update = true; + return; + } + + if (!update) return; + update = false; + + playlist_clear(); + + for (LIST_ITER(&tags_sel, link)) { + tag = UPCAST(link, struct tag, link_sel); + for (LIST_ITER(&tag->tracks, link2)) { + track = UPCAST(link2, struct track, link_tt); + link_pop(&track->link_pl); + list_push_back(&player.playlist, &track->link_pl); + } + } +} + struct tag * -tag_find(const char *query) +tag_add(const char *fname) { - struct link *iter; struct tag *tag; - for (LIST_ITER(&tags, iter)) { - tag = UPCAST(iter, struct tag); - if (!strcmp(tag->name, query)) { + /* add to tags list */ + tag = tag_alloc(datadir, fname); + OOM_CHECK(tag); + list_push_back(&tags, &tag->link); + + return tag; +} + +struct tag * +tag_find(const char *name) +{ + struct link *link; + struct tag *tag; + + for (LIST_ITER(&tags, link)) { + tag = UPCAST(link, struct tag, link); + if (!strcmp(tag->name, name)) return tag; - } } return NULL; } +bool +tag_rm(struct tag *tag, bool sync_fs) +{ + struct link *link; + struct track *track; + + /* remove contained tracks */ + while (!list_empty(&tag->tracks)) { + link = list_pop_front(&tag->tracks); + track = UPCAST(link, struct track, link_tt); + if (!track_rm(track, sync_fs)) + return false; + } + + /* delete dir and remaining non-track files */ + if (sync_fs && !rm_dir(tag->fpath, true)) + return false; + + /* remove from selected */ + link_pop(&tag->link_sel); + + /* remove from tags list */ + link_pop(&tag->link); + + tag_free(tag); + + return true; +} + +struct track * +track_add(struct tag *tag, const char *fname) +{ + struct track *track; + + track = track_alloc(tag->fpath, fname); + OOM_CHECK(track); + + track->tag = tag; + + /* insert track into sorted tracks list */ + list_push_back(&tracks, &track->link); + + /* add to tag's tracks list */ + list_push_back(&tag->tracks, &track->link_tt); + + /* if track's tag is selected, update playlist */ + if (link_inuse(&tag->link_sel)) + playlist_update(false); + + return track; +} + +bool +track_rm(struct track *track, bool sync_fs) +{ + if (sync_fs && !rm_file(track->fpath)) + return false; + + /* remove from tracks list */ + link_pop(&track->link); + + /* remove from tag's track list */ + link_pop(&track->link_tt); + + /* remove from playlist */ + link_pop(&track->link_pl); + + /* remove from player queue */ + link_pop(&track->link_pq); + + /* remove the reference as last used track */ + if (player.track == track) + player.track = NULL; + + track_free(track); + + return true; +} + +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"); + if (!datadir) ERROR("TMUS_DATA not set!\n"); + + dir = opendir(datadir); + if (!dir) ERROR("Failed to access dir: %s\n", datadir); + + while ((ent = readdir(dir))) { + if (!strcmp(ent->d_name, ".")) + continue; + if (!strcmp(ent->d_name, "..")) + continue; + + path = aprintf("%s/%s", datadir, ent->d_name); + OOM_CHECK(path); + + if (!stat(path, &st) && S_ISDIR(st.st_mode)) { + tag = tag_add(ent->d_name); + tracks_load(tag); + } + + free(path); + } + + closedir(dir); +} + +void +data_save(void) +{ + struct link *link; + struct tag *tag; + + for (LIST_ITER(&tags, link)) { + tag = UPCAST(link, struct tag, link); + tracks_save(tag); + } +} + +void +data_free(void) +{ + struct link *link; + struct tag *tag; + + list_clear(&player.playlist); + list_clear(&player.queue); + list_clear(&player.history); + + while (!list_empty(&tags)) { + link = list_pop_front(&tags); + tag = UPCAST(link, struct tag, link); + tag_rm(tag, false); + } +} diff --git a/src/data.h b/src/data.h @@ -1,33 +1,57 @@ #pragma once -#include "tag.h" -#include "track.h" +#include "list.h" -void data_load(void); -void data_save(void); -void data_free(void); +#include <stdbool.h> -int get_fid(const char *path); -int track_fid_compare(struct link *a, struct link *b); -void index_update(struct tag *tag); -bool tracks_update(struct tag *tag); -void playlist_update(void); +struct tag { + char *name, *fpath; + struct list tracks; -void tracks_load(struct tag *tag); -void tracks_save(struct tag *tag); + struct link link; /* tags list */ + struct link link_sel; /* selected tags list */ +}; -bool track_rm(struct track *track); +struct track { + char *name, *fpath; + struct tag *tag; + + struct link link; /* tracks list */ + struct link link_pl; /* player playlist */ + struct link link_tt; /* tag tracks list */ + struct link link_pq; /* player queue */ + struct link link_hs; /* player history */ +}; bool make_dir(const char *path); bool rm_dir(const char *path, bool recursive); bool rm_file(const char *path); bool copy_file(const char *dst, const char *src); +bool dup_file(const char *dst, const char *src); bool move_file(const char *dst, const char *src); -struct tag *tag_find(const char *query); +void index_update(struct tag *tag); +bool tracks_update(struct tag *tag); + +struct track *tracks_vis_track(struct link *link); + +void playlist_clear(void); +void playlist_update(bool exec); + +struct tag *tag_add(const char *fname); +struct tag *tag_find(const char *name); +bool tag_rm(struct tag *tag, bool sync_fs); + +struct track *track_add(struct tag *tag, const char *fname); +bool track_rm(struct track *track, bool sync_fs); + +void data_load(void); +void data_save(void); +void data_free(void); extern const char *datadir; extern struct list tracks; /* struct ref */ extern struct list tags; /* struct tag */ extern struct list tags_sel; /* struct ref */ + diff --git a/src/history.c b/src/history.c @@ -18,7 +18,7 @@ history_list_prev(struct inputln *cur, const char *search) struct inputln *ln; for (iter = cur->link.prev; iter && iter->prev; iter = iter->prev) { - ln = UPCAST(iter, struct inputln); + ln = UPCAST(iter, struct inputln, link); if (!search || !*search || strcasestr(ln->buf, search)) return ln; } @@ -34,7 +34,7 @@ history_list_next(struct inputln *cur, const char *search) iter = cur->link.next; while (LIST_INNER(iter)) { - ln = UPCAST(iter, struct inputln); + ln = UPCAST(iter, struct inputln, link); if (!search || !*search || strcasestr(ln->buf, search)) return ln; iter = iter->next; @@ -57,8 +57,8 @@ history_deinit(struct history *history) struct link *link; struct inputln *ln; - link = link_pop(LINK(history->input)); - ln = UPCAST(link, struct inputln); + link = link_pop(&history->input->link); + ln = UPCAST(link, struct inputln, link); inputln_free(ln); history->input = NULL; @@ -73,12 +73,12 @@ history_submit(struct history *history) { /* if chose from history free input */ if (history->sel != history->input) { - link_pop(LINK(history->input)); + link_pop(&history->input->link); inputln_free(history->input); } /* pop first in case already in history */ - link_pop(LINK(history->sel)); + link_pop(&history->sel->link); history_add(history, history->sel); /* create new input buf and add to hist */ @@ -108,11 +108,11 @@ history_add(struct history *history, struct inputln *line) if (list_len(&history->list) == HISTORY_MAX) { /* pop last item to make space */ back = list_pop_back(&history->list); - ln = UPCAST(back, struct inputln); + ln = UPCAST(back, struct inputln, link); inputln_free(ln); } - list_push_front(&history->list, LINK(line)); + list_push_front(&history->list, &line->link); } void diff --git a/src/main.c b/src/main.c @@ -8,7 +8,6 @@ #include "player.h" #include "ref.h" #include "style.h" -#include "tag.h" #include "tui.h" #include "util.h" @@ -42,11 +41,11 @@ cleanup(int exitcode, void* arg) { tui_restore(); + player_deinit(); + data_save(); data_free(); - player_deinit(); - dbus_deinit(); tui_deinit(); diff --git a/src/player.h b/src/player.h @@ -1,6 +1,5 @@ #pragma once -#include "track.h" #include "list.h" #include "util.h" @@ -29,21 +28,19 @@ enum { }; struct player { + /* list of tracks to choose from on prev / next */ + struct list playlist; /* struct track (link_pl) */ + /* played track history */ - struct list history; /* struct ref -> struct track */ - struct link *history_sel; /* position in history */ + struct list history; /* struct track (link_hs) */ /* queued tracks */ - struct list queue; /* struct ref -> struct track */ + struct list queue; /* struct track (link_pq) */ - /* selected track, not (yet) part of history or queue */ + /* last used track */ struct track *track; - /* list of tracks to choose from on prev / next */ - struct list playlist; /* struct ref -> struct track */ - struct link *playlist_sel; /* position in playlist */ - - /* a track is loaded, not necessarily player.track */ + /* player has a track is loaded (not necessarily player.track) */ bool loaded; /* automatically select new tracks when queue empty */ diff --git a/src/player_mpd.c b/src/player_mpd.c @@ -1,9 +1,9 @@ #include "player.h" -#include "ref.h" -#include "portaudio.h" -#include "sndfile.h" +#include "data.h" +#include "list.h" #include "util.h" +#include "log.h" #include <mpd/client.h> #include <mpd/song.h> @@ -81,15 +81,15 @@ mpd_handle_status(int status) } bool -history_contains(struct track *track, int depth) +history_contains(struct track *cmp, int depth) { struct link *link; - struct ref *ref; + struct track *track; link = list_back(&player.history); while (LIST_INNER(link) && depth-- > 0) { - ref = UPCAST(link, struct ref); - if (track == ref->data) + track = UPCAST(link, struct track, link_hs); + if (track == cmp) return true; link = link->prev; } @@ -102,7 +102,6 @@ playlist_track_lru(int skip) { struct track *track; struct link *link; - struct ref *ref; int len; track = NULL; @@ -110,8 +109,7 @@ playlist_track_lru(int skip) len = list_len(&player.playlist); link = list_front(&player.playlist); while (skip >= 0 && LIST_INNER(link)) { - ref = UPCAST(link, struct ref); - track = ref->data; + track = UPCAST(link, struct track, link_pl); if (!history_contains(track, len - 1)) skip -= 1; @@ -123,102 +121,101 @@ playlist_track_lru(int skip) link = list_front(&player.playlist); } - PLAYER_STATUS(INFO, "%i, %i", len - 1, skip); - return track; } +struct track * +player_next_from_playlist(void) +{ + struct link *link; + int index; + + if (list_empty(&player.playlist)) + return NULL; + + if (player.track) + player_add_history(player.track); + + if (player.shuffle) { + index = rand() % list_len(&player.playlist); + return playlist_track_lru(index); + } else { + if (player.track && link_inuse(&player.track->link_pl)) { + link = player.track->link_pl.next; + if (!link) list_front(&player.playlist); + } else { + link = list_front(&player.playlist); + } + return UPCAST(link, struct track, link_pl); + } + + return NULL; +} + void player_play_prev(void) { - struct link *link, *next; + struct link *next; struct track *track; - struct ref *ref; if (list_empty(&player.history)) return; - if (!player.history_sel) { + if (!player.track || !link_inuse(&player.track->link_hs)) { next = list_back(&player.history); - } else if (LIST_INNER(player.history_sel->prev)) { - next = player.history_sel->prev; + } else if (LIST_INNER(player.track->link_hs.prev)) { + next = player.track->link_hs.prev; } else { return; } - ref = UPCAST(next, struct ref); - player_play_track(ref->data); - - player.history_sel = next; + track = UPCAST(next, struct track, link_hs); + player_play_track(track); } void player_play_next(void) { + struct track *next_track; struct link *link; - struct track *track, *next_track; - struct ref *ref; - int index, len; - int status; next_track = NULL; - link = player.history_sel; - if (link && LIST_INNER(link->next)) { - player.history_sel = link->next; - ref = UPCAST(link->next, struct ref); - next_track = ref->data; + if (player.track && link_inuse(&player.track->link_hs) + && LIST_INNER(player.track->link_hs.next)) { + next_track = UPCAST(player.track->link_hs.next, + struct track, link_hs); + } else if (!list_empty(&player.queue)) { + link = list_pop_front(&player.queue); + next_track = UPCAST(link, struct track, link_pq); } else { - if (!list_empty(&player.queue)) { - link = list_pop_front(&player.queue); - ref = UPCAST(link, struct ref); - next_track = ref->data; - ref_free(ref); - } else { - if (list_empty(&player.playlist)) - return; - - if (!player.history_sel) { - player_add_history(player.track); - player.track = NULL; - } - player.history_sel = NULL; - - if (player.shuffle) { - index = rand() % list_len(&player.playlist); - next_track = playlist_track_lru(index + 1); - ASSERT(next_track != NULL); - } else { - link = player.playlist_sel; - if (link && LIST_INNER(link->next)) { - ref = UPCAST(link->next, struct ref); - next_track = ref->data; - } else { - status = mpd_run_clear(mpd.conn); - mpd_handle_status(status); - return; - } - } - } + next_track = player_next_from_playlist(); } + if (!next_track) goto clear; + player_play_track(next_track); + return; + +clear: + player.track = NULL; + mpd_run_clear(mpd.conn); } void -player_add_history(struct track *track) +player_add_history(struct track *new) { struct link *link; - struct ref *ref; + struct track *track; link = list_back(&player.history); if (link) { - ref = UPCAST(link, struct ref); - if (ref->data == track) return; + track = UPCAST(link, struct track, link_hs); + if (track == new) return; } - ref = ref_alloc(track); - list_push_back(&player.history, LINK(ref)); + link_pop(&new->link_hs); + list_push_back(&player.history, &new->link_hs); } void @@ -228,19 +225,22 @@ player_init(void) mpd.action = PLAYER_ACTION_NONE; mpd.seek_delay = 0; + list_init(&player.playlist); list_init(&player.history); - player.history_sel = NULL; list_init(&player.queue); + player.track = NULL; - list_init(&player.playlist); - player.playlist_sel = NULL; + player.loaded = 0; player.autoplay = true; player.shuffle = true; + player.state = PLAYER_STATE_PAUSED; player.volume = 0; + player.time_pos = 0; player.time_end = 0; + player.status = NULL; player.status_lvl = PLAYER_STATUS_MSG_INFO; } @@ -248,13 +248,9 @@ player_init(void) void player_deinit(void) { - struct link *iter; - - if (!mpd.conn) return; - - refs_free(&player.queue); - refs_free(&player.history); - refs_free(&player.playlist); + list_clear(&player.playlist); + list_clear(&player.queue); + list_clear(&player.history); if (player.status) free(player.status); if (mpd.conn) mpd_connection_free(mpd.conn); @@ -265,7 +261,6 @@ player_update(void) { struct mpd_status *status; struct mpd_song *current_song; - struct ref *ref; bool queue_empty; if (!mpd.conn) { @@ -283,10 +278,8 @@ player_update(void) current_song = mpd_run_current_song(mpd.conn); if (!current_song) { - if (player.track && !player.history_sel) { + if (player.track) player_add_history(player.track); - player.track = NULL; - } queue_empty = list_empty(&player.queue); if (player.loaded && player.autoplay || !queue_empty) @@ -359,8 +352,6 @@ player_update(void) int player_play_track(struct track *track) { - struct link *link; - struct ref *ref; int status; status = mpd_run_clear(mpd.conn); @@ -374,22 +365,9 @@ player_play_track(struct track *track) if (mpd_handle_status(status)) return PLAYER_STATUS_ERR; - if (player.track && !player.history_sel) { + /* add last track to history */ + if (player.track && !link_inuse(&player.track->link_hs)) player_add_history(player.track); - player.track = NULL; - } - - player.history_sel = NULL; - /* re-assigning history_sel done in calling code */ - - player.playlist_sel = NULL; - for (LIST_ITER(&player.playlist, link)) { - ref = UPCAST(link, struct ref); - if (ref->data == track) { - player.playlist_sel = link; - break; - } - } player.track = track; diff --git a/src/ref.c b/src/ref.c @@ -27,37 +27,37 @@ refs_free(struct list *list) list_free(list, ref_free, LINK_OFFSET(struct ref, link)); } -static struct link * -refs_ffind(struct list *list, void *data) +int +refs_index(struct list *list, void *data) { struct link *iter; struct ref *ref; + int index; + index = 0; for (LIST_ITER(list, iter)) { - ref = UPCAST(iter, struct ref); + ref = UPCAST(iter, struct ref, link); if (ref->data == data) - return iter; + return index; + index++; } - return NULL; + return -1; } -int -refs_index(struct list *list, void *data) +struct link * +refs_find(struct list *list, void *data) { struct link *iter; struct ref *ref; - int index; - index = 0; for (LIST_ITER(list, iter)) { - ref = UPCAST(iter, struct ref); + ref = UPCAST(iter, struct ref, link); if (ref->data == data) - return index; - index++; + return iter; } - return -1; + return NULL; } int @@ -65,7 +65,8 @@ refs_incl(struct list *list, void *data) { struct link *ref; - ref = refs_ffind(list, data); + ref = refs_find(list, data); + return ref != NULL; } @@ -75,10 +76,10 @@ refs_rm(struct list *list, void *data) struct link *ref; struct ref *dataref; - ref = refs_ffind(list, data); + ref = refs_find(list, data); if (!ref) return; - dataref = UPCAST(ref, struct ref); + dataref = UPCAST(ref, struct ref, link); link_pop(ref); free(dataref); } diff --git a/src/ref.h b/src/ref.h @@ -13,6 +13,9 @@ void ref_free(void *ref); void refs_free(struct list *list); int refs_index(struct list *list, void *data); + +struct link *refs_find(struct list *list, void *data); int refs_incl(struct list *list, void *data); + void refs_rm(struct list *list, void *data); diff --git a/src/tag.c b/src/tag.c @@ -1,38 +0,0 @@ -#include "tag.h" - -#include "link.h" -#include "ref.h" -#include "util.h" - -#include <string.h> - -struct tag * -tag_alloc(const char *path, const char *fname) -{ - struct tag *tag; - int len; - - tag = malloc(sizeof(struct tag)); - ASSERT(tag != NULL); - - tag->fpath = aprintf("%s/%s", path, fname); - ASSERT(tag->fpath != NULL); - - tag->name = strdup(fname); - ASSERT(tag->name != NULL); - - tag->link = LINK_EMPTY; - - list_init(&tag->tracks); - - return tag; -} - -void -tag_free(struct tag *tag) -{ - free(tag->fpath); - free(tag->name); - refs_free(&tag->tracks); - free(tag); -} diff --git a/src/tag.h b/src/tag.h @@ -1,14 +0,0 @@ -#pragma once - -#include "list.h" - -struct tag { - char *name, *fpath; - - struct list tracks; - - struct link link; -}; - -struct tag *tag_alloc(const char *path, const char *fname); -void tag_free(struct tag *tag); diff --git a/src/track.c b/src/track.c @@ -1,37 +0,0 @@ -#include "ref.h" -#include "track.h" - -#include <wchar.h> -#include <string.h> -#include <sys/stat.h> - -struct track * -track_alloc(const char *dir, const char *fname, int fid) -{ - struct track *track; - int len; - - track = malloc(sizeof(struct track)); - ASSERT(track != NULL); - - track->fpath = aprintf("%s/%s", dir, fname); - ASSERT(track->fpath != NULL); - - track->name = strdup(fname); - ASSERT(track->name != NULL); - - track->fid = fid; - - list_init(&track->tags); - - return track; -} - -void -track_free(struct track *t) -{ - free(t->fpath); - free(t->name); - refs_free(&t->tags); - free(t); -} diff --git a/src/track.h b/src/track.h @@ -1,13 +0,0 @@ -#pragma once - -#include "list.h" -#include "util.h" - -struct track { - char *name, *fpath; - struct list tags; - int fid; -}; - -struct track *track_alloc(const char *dir, const char *file, int fid); -void track_free(struct track *t); diff --git a/src/tui.c b/src/tui.c @@ -10,7 +10,6 @@ #include "player.h" #include "list.h" #include "listnav.h" -#include "ref.h" #include "style.h" #include "strbuf.h" #include "util.h" @@ -45,10 +44,15 @@ static char *track_name_gen(const char *text, int fwd, int state); static char *tag_name_gen(const char *text, int fwd, int state); static void toggle_current_tag(void); +static void select_only_current_tag(void); +static void seek_next_selected_tag(void); +static void delete_selected_tag(void); static bool tag_pane_input(wint_t c); static void tag_pane_vis(struct pane *pane, int sel); +static void delete_selected_track(void); + static bool track_pane_input(wint_t c); static void track_pane_vis(struct pane *pane, int sel); @@ -61,7 +65,7 @@ static bool 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 update_tracks_vis(void); static void main_input(wint_t c); static void main_vis(void); @@ -159,26 +163,26 @@ char * track_name_gen(const char *text, int fwd, int reset) { static struct link *cur; - struct link *iter; + struct link *link; struct track *track; char *dup; if (reset) { cur = tracks.head.next; - iter = cur; + link = cur; } else { - iter = fwd ? cur->next : cur->prev; + link = fwd ? cur->next : cur->prev; } - while (LIST_INNER(iter)) { - track = UPCAST(iter, struct ref)->data; + while (LIST_INNER(link)) { + track = UPCAST(link, struct track, link); if (strcasestr(track->name, text)) { - cur = iter; + cur = link; dup = strdup(track->name); OOM_CHECK(dup); return dup; } - iter = fwd ? iter->next : iter->prev; + link = fwd ? link->next : link->prev; } return NULL; @@ -188,26 +192,26 @@ char * tag_name_gen(const char *text, int fwd, int reset) { static struct link *cur; - struct link *iter; + struct link *link; struct tag *tag; char *dup; if (reset) { cur = tags.head.next; - iter = cur; + link = cur; } else { - iter = fwd ? cur->next : cur->prev; + link = fwd ? cur->next : cur->prev; } - while (LIST_INNER(iter)) { - tag = UPCAST(iter, struct tag); + while (LIST_INNER(link)) { + tag = UPCAST(link, struct tag, link); if (strcasestr(tag->name, text)) { - cur = iter; + cur = link; dup = strdup(tag->name); OOM_CHECK(dup); return dup; } - iter = fwd ? iter->next : iter->prev; + link = fwd ? link->next : link->prev; } return NULL; @@ -216,59 +220,111 @@ tag_name_gen(const char *text, int fwd, int reset) void toggle_current_tag(void) { - struct link *link, *iter; - struct track *track; + struct link *link; 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); + tag = UPCAST(link, struct tag, link); /* toggle tag in tags_sel */ - if (refs_incl(&tags_sel, tag)) { - refs_rm(&tags_sel, tag); + if (link_inuse(&tag->link_sel)) { + link_pop(&tag->link_sel); } else { - ref = ref_alloc(tag); - list_push_back(&tags_sel, LINK(ref)); + list_push_back(&tags_sel, &tag->link_sel); } +} - /* rebuild the full playlist */ - playlist_update(); +void +select_only_current_tag(void) +{ + list_clear(&tags_sel); + + toggle_current_tag(); +} + +void +seek_next_selected_tag(void) +{ + struct link *link; + struct tag *tag; + int index; + + if (list_empty(&tags_sel)) + return; + + if (list_empty(&tags)) + return; + + link = list_at(&tags, track_nav.sel); + if (!link) return; + + index = track_nav.sel; + tag = UPCAST(link, struct tag, link); + do { + index += 1; + link = tag->link.next; + if (!LIST_INNER(link)) { + link = list_at(&tags, 0); + index = 0; + } + tag = UPCAST(link, struct tag, link); + } while (!link_inuse(&tag->link_sel)); + + listnav_update_sel(&track_nav, index); +} + +void +delete_selected_tag(void) +{ + struct link *link; + struct tag *tag; + + link = list_at(&tags, track_nav.sel); + if (!link) return; + tag = UPCAST(link, struct tag, link); + tag_rm(tag, true); } bool tag_pane_input(wint_t c) { switch (c) { - case KEY_UP: + case KEY_UP: /* nav up */ listnav_update_sel(&tag_nav, tag_nav.sel - 1); return true; - case KEY_DOWN: + case KEY_DOWN: /* nav down */ listnav_update_sel(&tag_nav, tag_nav.sel + 1); return true; - case KEY_SPACE: + case KEY_SPACE: /* toggle tag */ toggle_current_tag(); + playlist_update(false); return true; - case KEY_ENTER: - refs_free(&tags_sel); - toggle_current_tag(); + case KEY_ENTER: /* select only current tag */ + select_only_current_tag(); + playlist_update(false); return true; - case KEY_PPAGE: + case KEY_PPAGE: /* seek half a page up */ listnav_update_sel(&tag_nav, tag_nav.sel - tag_nav.wlen / 2); return true; - case KEY_NPAGE: + case KEY_NPAGE: /* seek half a page down */ listnav_update_sel(&tag_nav, tag_nav.sel + tag_nav.wlen / 2); return true; - case 'g': + case 'g': /* seek start of list */ listnav_update_sel(&tag_nav, 0); - break; - case 'G': + return true; + case 'G': /* seek end of list */ listnav_update_sel(&tag_nav, tag_nav.max - 1); - break; + return true; + case 'n': /* nav through selected tags */ + seek_next_selected_tag(); + return true; + case 'D': /* delete tag */ + delete_selected_tag(); + playlist_update(false); + return true; } return false; @@ -278,7 +334,7 @@ void tag_pane_vis(struct pane *pane, int sel) { struct tag *tag; - struct link *iter; + struct link *link; int index, tagsel; werase(pane->win); @@ -288,9 +344,9 @@ tag_pane_vis(struct pane *pane, int sel) listnav_update_wlen(&tag_nav, pane->h - 1); index = -1; - for (LIST_ITER(&tags, iter)) { - tag = UPCAST(iter, struct tag); - tagsel = refs_incl(&tags_sel, tag); + for (LIST_ITER(&tags, link)) { + tag = UPCAST(link, struct tag, link); + tagsel = link_inuse(&tag->link_sel); index += 1; if (index < tag_nav.wmin) continue; @@ -318,56 +374,80 @@ tag_pane_vis(struct pane *pane, int sel) } } -bool -track_pane_input(wint_t c) +void +play_selected_track(void) +{ + struct link *link; + struct track *track; + + link = list_at(tracks_vis, track_nav.sel); + if (!link) return; + track = tracks_vis_track(link); + player_play_track(track); +} + +void +seek_playing_track(void) { struct link *link; struct track *track; int index; + index = 0; + for (LIST_ITER(tracks_vis, link)) { + track = tracks_vis_track(link); + if (track == player.track) { + listnav_update_sel(&track_nav, index); + break; + } + index += 1; + } +} + +void +delete_selected_track(void) +{ + struct link *link; + struct track *track; + + link = list_at(tracks_vis, track_nav.sel); + ASSERT(link != NULL); + track = tracks_vis_track(link); + track_rm(track, true); +} + +bool +track_pane_input(wint_t c) +{ switch (c) { - case KEY_UP: + case KEY_UP: /* nav up */ listnav_update_sel(&track_nav, track_nav.sel - 1); return true; - case KEY_DOWN: + case KEY_DOWN: /* nav down */ listnav_update_sel(&track_nav, track_nav.sel + 1); return true; - case KEY_ENTER: - link = list_at(tracks_vis, track_nav.sel); - if (!link) return true; - track = UPCAST(link, struct ref)->data; - player_play_track(track); + case KEY_ENTER: /* play track */ + play_selected_track(); return true; - case KEY_PPAGE: + case KEY_PPAGE: /* seek half page up */ listnav_update_sel(&track_nav, track_nav.sel - track_nav.wlen / 2); return true; - case KEY_NPAGE: + case KEY_NPAGE: /* seek half page down */ listnav_update_sel(&track_nav, track_nav.sel + track_nav.wlen / 2); return true; - case 'g': + case 'g': /* seek start of list */ listnav_update_sel(&track_nav, 0); break; - case 'G': + case 'G': /* seek end of list */ listnav_update_sel(&track_nav, track_nav.max - 1); break; - case 'n': - index = 0; - for (LIST_ITER(tracks_vis, link)) { - track = UPCAST(link, struct ref)->data; - if (track == player.track) { - listnav_update_sel(&track_nav, index); - break; - } - index += 1; - } + case 'n': /* seek playing */ + seek_playing_track(); break; - case 'D': - link = list_at(tracks_vis, track_nav.sel); - ASSERT(link != NULL); - track = UPCAST(link, struct ref)->data; - track_rm(track); + case 'D': /* delete track */ + delete_selected_track(); break; } @@ -378,8 +458,7 @@ void track_pane_vis(struct pane *pane, int sel) { struct track *track; - struct link *iter; - struct tag *tag; + struct link *link; int index; werase(pane->win); @@ -389,8 +468,8 @@ track_pane_vis(struct pane *pane, int sel) listnav_update_wlen(&track_nav, pane->h - 1); index = -1; - for (LIST_ITER(tracks_vis, iter)) { - track = UPCAST(iter, struct ref)->data; + for (LIST_ITER(tracks_vis, link)) { + track = tracks_vis_track(link); index += 1; if (index < track_nav.wmin) continue; @@ -436,20 +515,21 @@ bool play_track(const char *query) { struct track *track; - struct link *iter; - int fid; + struct link *link; + const char *prevname; - fid = -1; - for (LIST_ITER(&tracks, iter)) { - track = UPCAST(iter, struct ref)->data; - if (fid == track->fid) continue; + prevname = NULL; + for (LIST_ITER(&tracks, link)) { + track = UPCAST(link, struct track, link); + if (prevname && !strcmp(track->name, prevname)) + continue; if (!strcmp(track->name, query)) { player_play_track(track); return true; } - fid = track->fid; + prevname = track->name; } return false; @@ -464,10 +544,11 @@ select_track(const char *query) index = 0; for (LIST_ITER(tracks_vis, link)) { - track = UPCAST(link, struct ref)->data; + track = tracks_vis_track(link); if (!strcmp(track->name, query)) { listnav_update_sel(&track_nav, index); pane_after_cmd = track_pane; + playlist_update(false); return true; } index += 1; @@ -480,13 +561,13 @@ bool select_tag(const char *query) { struct tag *tag; - struct link *iter; + struct link *link; int index; index = -1; - for (LIST_ITER(&tags, iter)) { + for (LIST_ITER(&tags, link)) { index += 1; - tag = UPCAST(iter, struct tag); + tag = UPCAST(link, struct tag, link); if (!strcmp(tag->name, query)) { listnav_update_sel(&tag_nav, index); pane_after_cmd = tag_pane; @@ -594,7 +675,7 @@ cmd_pane_vis(struct pane *pane, int sel) { static struct strbuf line = { 0 }; struct inputln *cmd; - struct link *iter; + struct link *link; int index, offset; werase(pane->win); @@ -654,12 +735,12 @@ cmd_pane_vis(struct pane *pane, int sel) cmd = history->sel; if (cmd != history->input) { index = 0; - for (LIST_ITER(&history->list, iter)) { - if (UPCAST(iter, struct inputln) == cmd) + for (LIST_ITER(&history->list, link)) { + if (UPCAST(link, struct inputln, link) == cmd) break; index += 1; } - strbuf_append(&line, "[%i] ", iter ? index : -1); + strbuf_append(&line, "[%i] ", link ? index : -1); } else { strbuf_append(&line, "%c", imode_prefix[cmd_input_mode]); } @@ -687,17 +768,17 @@ void queue_hover(void) { struct link *link; - struct ref *ref; + struct track *track; - link = list_at(&player.playlist, track_nav.sel); + link = list_at(tracks_vis, track_nav.sel); if (!link) return; - ref = UPCAST(link, struct ref); - list_push_back(&player.queue, ref->data); + track = tracks_vis_track(link); + list_push_back(&player.queue, &track->link_pq); } void -update_track_playlist(void) +update_tracks_vis(void) { struct link *link; struct tag *tag; @@ -707,7 +788,7 @@ update_track_playlist(void) } else { link = list_at(&tags, tag_nav.sel); if (!link) return; - tag = UPCAST(link, struct tag); + tag = UPCAST(link, struct tag, link); tracks_vis = &tag->tracks; } } @@ -739,10 +820,10 @@ main_input(wint_t c) queue_hover(); break; case L'o': - refs_free(&player.queue); + list_clear(&player.queue); break; case L'h': - refs_free(&player.history); + list_clear(&player.history); break; case L'c': player_toggle_pause(); @@ -874,9 +955,9 @@ tui_curses_init(void) void tui_resize(void) { - struct link *iter; + struct link *link; struct tag *tag; - int i, leftw; + int leftw; getmaxyx(stdscr, scrh, scrw); @@ -890,8 +971,8 @@ tui_resize(void) /* adjust tag pane width to name lengths */ leftw = 0; - for (LIST_ITER(&tags, iter)) { - tag = UPCAST(iter, struct tag); + for (LIST_ITER(&tags, link)) { + tag = UPCAST(link, struct tag, link); leftw = MAX(leftw, strlen(tag->name)); } leftw = MAX(leftw + 1, 0.2f * scrw); @@ -933,7 +1014,7 @@ tui_init(void) listnav_init(&track_nav); track_show_playlist = 0; - update_track_playlist(); + update_tracks_vis(); tui_resize(); } @@ -977,7 +1058,8 @@ tui_update(void) if (!handled) main_input(c); } - update_track_playlist(); + playlist_update(true); + update_tracks_vis(); refresh(); for (i = 0; i < ARRLEN(panes); i++) { diff --git a/src/util.h b/src/util.h @@ -11,8 +11,7 @@ #define OOM_CHECK(x) assert((x) != NULL, __FILE__, __LINE__, "Out of Memory!") #define ERROR(...) error("" __VA_ARGS__) -#define LINK(p) (&(p)->link) -#define UPCAST(iter, type) LINK_UPCAST(iter, type, link) +#define UPCAST(iter, type, link) LINK_UPCAST(iter, type, link) void panic(const char *file, int line, const char *msg, ...); void assert(int cond, const char *file, int line, const char *condstr);