tmus

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

commit 96e9e044e779b301b0608aea0ee4449124a8d0bd
parent f5ccba488250d09df9c4a0d5f50a1e7666e7cac3
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sun, 13 Feb 2022 16:43:00 +0100

Added command status and move and copy cmds

Diffstat:
MMakefile | 5++++-
Msrc/main.c | 215+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/util.c | 23+++++++++++++++++++++++
Msrc/util.h | 2++
4 files changed, 223 insertions(+), 22 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,4 +1,4 @@ -CFLAGS = -I src -g -pg $(shell pkg-config --cflags glib-2.0 dbus-1) +CFLAGS = -O2 -I src -g $(shell pkg-config --cflags glib-2.0 dbus-1) LDLIBS = -lcurses -lmpdclient $(shell pkg-config --libs glib-2.0 dbus-1) DEPFLAGS = -MT $@ -MMD -MP -MF build/$*.d @@ -26,6 +26,9 @@ include $(DEPS) tmus: $(OBJS) $(CC) -o $@ $^ $(CFLAGS) $(LDLIBS) +tmus.prof: $(OBJS) + $(CC) -o $@ $^ -pg $(CFLAGS) $(LDLIBS) + install: install -m 755 tmus /usr/bin diff --git a/src/main.c b/src/main.c @@ -16,13 +16,15 @@ #include "curses.h" #include <dirent.h> +#include <fcntl.h> #include <locale.h> #include <signal.h> -#include <sys/stat.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> @@ -39,6 +41,12 @@ #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, @@ -47,6 +55,7 @@ enum { STYLE_ITEM_HOVER, STYLE_ITEM_HOVER_SEL, STYLE_PREV, + STYLE_ERROR, STYLE_COUNT }; @@ -102,6 +111,7 @@ 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; @@ -123,10 +133,15 @@ 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); @@ -153,7 +168,9 @@ static void update_track_playlist(void); static void main_input(wint_t c); static void main_vis(void); -static int usercmd_save(const wchar_t *args); +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] = ':', @@ -162,7 +179,9 @@ const char imode_prefix[IMODE_COUNT] = { }; const struct cmd cmds[] = { - { L"save", usercmd_save }, + { L"save", cmd_save }, + { L"move", cmd_move }, + { L"add", cmd_move }, }; void @@ -196,6 +215,8 @@ init(void) track_show_playlist = 0; update_track_playlist(); + cmd_status = NULL; + on_exit(cleanup, NULL); signal(SIGINT, exit); } @@ -203,9 +224,13 @@ init(void) void cleanup(int exitcode, void* arg) { - if (!exitcode) tui_end(); + if (!exitcode) { + tui_end(); + } else { + curs_set(1); + } - if (!exitcode) data_save(); + data_save(); data_free(); player_free(); @@ -231,6 +256,7 @@ tui_init(void) 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); @@ -350,7 +376,13 @@ data_load(void) void data_save(void) { - + struct link *iter; + struct tag *tag; + + for (LIST_ITER(&tags, iter)) { + tag = UPCAST(iter, struct tag); + tracks_save(tag); + } } void @@ -378,6 +410,13 @@ data_free(void) } } +int +get_fid(const char *path) +{ + struct stat st; + return stat(path, &st) ? -1 : st.st_ino; +} + void index_update(struct tag *tag) { @@ -410,12 +449,12 @@ index_update(struct tag *tag) continue; /* skip files without extension */ - if (!strchr(ent->d_name, '.')) + if (!strchr(ent->d_name + 1, '.')) continue; path = aprintf("%s/%s", tag->fpath, ent->d_name); ASSERT(path != NULL); - fid = stat(path, &st) ? -1 : st.st_ino; + fid = get_fid(path); free(path); fprintf(file, "%i:%s\n", fid, ent->d_name); @@ -436,8 +475,12 @@ tracks_load(struct tag *tag) 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); @@ -468,19 +511,23 @@ tracks_load(struct tag *tag) 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 == track2->fid) - break; + if (track->fid > 0 && track->fid == track2->fid) + new_track = false; } - if (!link) { + 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); } @@ -495,22 +542,82 @@ tracks_save(struct tag *tag) /* 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(&tracks, link)) { + 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); @@ -741,9 +848,8 @@ track_pane_input(wint_t c) listnav_update_sel(&track_nav, track_nav.sel + 1); return 1; case KEY_ENTER: - if (list_empty(tracks_vis)) return 1; link = list_at(tracks_vis, track_nav.sel); - ASSERT(link != NULL); + if (!link) return 1; track = UPCAST(link, struct ref)->data; player_play_track(track); return 1; @@ -815,7 +921,11 @@ run_cmd(const wchar_t *query) cmdlen = sep ? sep - query : wcslen(query); for (i = 0; i < ARRLEN(cmds); i++) { if (!wcsncmp(cmds[i].name, query, cmdlen)) { - cmds[i].func(sep ? sep + 1 : NULL); + if (!cmds[i].func(sep ? sep + 1 : NULL)) { + free(cmd_status); + cmd_status = wcsdup(L"Command Failed!\n"); + ASSERT(cmd_status != NULL); + } return 1; } } @@ -1027,6 +1137,9 @@ cmd_pane_vis(struct pane *pane, int sel) /* cmd and search input */ line = linebuf; + free(cmd_status); + cmd_status = NULL; + cmd = history->sel; if (cmd != history->input) { index = 0; @@ -1054,6 +1167,11 @@ cmd_pane_vis(struct pane *pane, int sel) ? 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); } } @@ -1063,22 +1181,22 @@ queue_hover(void) struct link *link; link = list_at(&player->playlist, track_nav.sel); - ASSERT(link != NULL); + if (!link) return; player_queue_append(UPCAST(link, struct ref)->data); } void update_track_playlist(void) { - struct link *iter; + struct link *link; struct tag *tag; if (track_show_playlist) { tracks_vis = &player->playlist; } else { - iter = list_at(&tags, tag_nav.sel); - ASSERT(iter != NULL); - tag = UPCAST(iter, struct tag); + link = list_at(&tags, tag_nav.sel); + if (!link) return; + tag = UPCAST(link, struct tag); tracks_vis = &tag->tracks; } } @@ -1202,13 +1320,68 @@ main_vis(void) } int -usercmd_save(const wchar_t *args) +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; +} + +int main(int argc, const char **argv) { int i, handled; diff --git a/src/util.c b/src/util.c @@ -65,6 +65,29 @@ assert(int cond, const char *file, int line, const char *condstr) exit(1); } +wchar_t * +awprintf(const wchar_t *fmtstr, ...) +{ + va_list ap, cpy; + size_t size; + wchar_t *str; + + va_copy(cpy, ap); + + va_start(ap, fmtstr); + size = swprintf(NULL, 0, fmtstr, ap); + va_end(ap); + + str = malloc((size + 1) * sizeof(wchar_t)); + ASSERT(str != NULL); + + va_start(cpy, fmtstr); + swprintf(str, size + 1, fmtstr, cpy); + va_end(cpy); + + return str; +} + char * aprintf(const char *fmtstr, ...) { diff --git a/src/util.h b/src/util.h @@ -1,6 +1,7 @@ #pragma once #include <stdio.h> +#include <wchar.h> #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) > (b) ? (b) : (a)) @@ -15,6 +16,7 @@ void panic(const char *file, int line, const char *msg, ...); void assert(int cond, const char *file, int line, const char *condstr); char *aprintf(const char *fmtstr, ...); +wchar_t *awprintf(const wchar_t *fmtstr, ...); char *appendstrf(char *alloc, const char *fmtstr, ...); char *sanitized(const char *instr);