commit fb0a1cabe8d61d3305d9d14acc4754a1b6151b8c
parent fee76b1e3d8da5152e44d67293165ae0c5651d25
Author: Louis Burda <quent.burda@gmail.com>
Date: Tue, 28 Dec 2021 22:56:43 +0100
Refactor for readability, use static line buffer for statusline etc
Diffstat:
M | src/main.c | | | 446 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
A | src/pane.c | | | 35 | +++++++++++++++++++++++++++++++++++ |
A | src/pane.h | | | 25 | +++++++++++++++++++++++++ |
M | src/player.h | | | 5 | ++++- |
4 files changed, 307 insertions(+), 204 deletions(-)
diff --git a/src/main.c b/src/main.c
@@ -6,6 +6,7 @@
#include "history.h"
#include "tag.h"
#include "track.h"
+#include "pane.h"
#include "player.h"
#include "listnav.h"
#include "ref.h"
@@ -51,23 +52,8 @@ enum {
IMODE_SEARCH
};
-struct pane;
-
-typedef int (*pane_handler)(wint_t c);
-typedef void (*pane_updater)(struct pane *pane, int sel);
-typedef wchar_t *(*completion_generator)(const wchar_t *text,
- int fwd, int state);
-typedef int (*cmd_handler)(const char *args);
-
-struct pane {
- WINDOW *win;
- int sx, sy, ex, ey;
- int w, h;
- int active;
-
- pane_handler handle;
- pane_updater update;
-};
+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;
@@ -94,57 +80,59 @@ static struct pane *const panes[] = {
&pane_bot
};
-static struct inputln completion_query = { 0 };
-static int completion_reset = 1;
-static completion_generator completion;
-
+/* bottom 'cmd' pane for search / exec */
static struct pane *cmd_pane;
static struct history search_history, command_history;
static struct history *history;
static int cmd_show, cmd_mode;
+static struct inputln completion_query = { 0 };
+static int completion_reset = 1;
+static completion_gen completion;
-static int autoplay;
-static int shuffle;
-
+/* left pane for tags */
static struct pane *tag_pane;
static struct listnav tag_nav;
static struct link tags;
static struct link tags_sel;
+/* right pane for tracks */
static struct pane *track_pane;
static struct listnav track_nav;
static struct link playlist;
static struct link tracks;
+static int autoplay;
+static int shuffle;
static void init(void);
-static void cleanup(void *arg, int code);
+static void cleanup(int code, void *arg);
-static void data_load(void);
-static void tracks_load(struct tag *tag);
+static void tui_init(void);
+static void tui_resize(void);
+static void tui_end(void);
+
+static void ncurses_init(void);
+static void data_load(void);
static void data_save(void);
-static void tracks_save(struct tag *tag);
-static void resize(void);
-static void pane_resize(struct pane *pane,
- int sx, int sy, int ex, int ey);
+static void tracks_load(struct tag *tag);
+static void tracks_save(struct tag *tag);
-static void pane_init(struct pane *p, pane_handler handle, pane_updater update);
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_generator(const wchar_t *text, int fwd, int state);
-static wchar_t *track_name_generator(const wchar_t *text, int fwd, int state);
+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_input(wint_t c);
-static void tag_vis(struct pane *pane, int sel);
+static int tag_pane_input(wint_t c);
+static void tag_pane_vis(struct pane *pane, int sel);
-static int track_input(wint_t c);
-static void track_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 cmd_input(wint_t c);
static void cmd_vis(struct pane *pane, int sel);
@@ -153,7 +141,7 @@ static void queue_hover(void);
static void main_input(wint_t c);
static void main_vis(void);
-static int usercmd_save(const char *args);
+static int usercmd_save(const wchar_t *args);
static void update_player(void);
@@ -164,32 +152,44 @@ const struct cmd cmds[] = {
void
init(void)
{
- quit = 0;
-
setlocale(LC_ALL, "");
+ quit = 0;
- /* TODO handle as character intead of signal */
signal(SIGINT, exit);
- atexit((void*) cleanup); /* this works */
+ on_exit(cleanup, NULL);
- history = &command_history;
history_init(&search_history);
history_init(&command_history);
+ history = &command_history;
data_load();
player_init();
- /* ncurses init */
- initscr();
- raw();
- noecho();
- halfdelay(1);
- intrflush(stdscr, FALSE);
- keypad(stdscr, TRUE);
- start_color();
- curs_set(0);
- ESCDELAY = 0;
+ tui_init();
+
+ listnav_init(&tag_nav);
+ listnav_init(&track_nav);
+}
+
+void
+cleanup(int code, void* arg)
+{
+ tui_end();
+
+ if (code == EXIT_SUCCESS)
+ data_save();
+
+ player_free();
+
+ history_free(&search_history);
+ history_free(&command_history);
+}
+
+void
+tui_init(void)
+{
+ ncurses_init();
memset(style_attrs, 0, sizeof(style_attrs));
style_init(STYLE_DEFAULT, COLOR_WHITE, COLOR_BLACK, 0);
@@ -199,39 +199,75 @@ init(void)
style_init(STYLE_ITEM_HOVER, COLOR_WHITE, COLOR_BLUE, 0);
style_init(STYLE_ITEM_HOVER_SEL, COLOR_YELLOW, COLOR_BLUE, A_BOLD);
- pane_init((tag_pane = &pane_left), tag_input, tag_vis);
- pane_init((track_pane = &pane_right), track_input, track_vis);
+ 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_input, cmd_vis);
pane_sel = &pane_left;
pane_top_sel = pane_sel;
+}
- playlist = LIST_HEAD;
- tags_sel = LIST_HEAD;
+void
+tui_resize(void)
+{
+ struct link *iter;
+ int i, leftw;
- autoplay = 0;
- shuffle = 0;
+ getmaxyx(stdscr, scrh, scrw);
- listnav_init(&tag_nav);
- listnav_init(&track_nav);
+ /* guarantee a minimum terminal size */
+ if (scrw < 10 || scrh < 4) {
+ clear();
+ printw("...");
+ refresh();
+ usleep(10000);
+ }
+
+ /* adjust tag pane width to name lengths */
+ leftw = 0;
+ for (iter = tags.next; iter; iter = iter->next)
+ leftw = MAX(leftw, strlen(UPCAST(iter, struct 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
-cleanup(void* arg, int code)
+tui_end(void)
{
- if (code) return;
+ pane_free(&pane_left);
+ pane_free(&pane_right);
+ pane_free(&pane_bot);
- delwin(pane_left.win);
- delwin(pane_right.win);
- delwin(pane_bot.win);
- endwin();
+ if (!isendwin()) endwin();
+}
- data_save();
+void
+ncurses_init(void)
+{
+ initscr();
- player_free();
+ /* do most of the handling ourselves,
+ * enable special keys */
+ raw();
+ noecho();
+ keypad(stdscr, TRUE);
- history_free(&search_history);
- history_free(&command_history);
+ /* 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
@@ -241,8 +277,12 @@ data_load(void)
struct tag *tag;
DIR *dir;
+ tracks = LIST_HEAD;
tags = LIST_HEAD;
+ playlist = LIST_HEAD;
+ tags_sel = LIST_HEAD;
+
datadir = getenv("TMUS_DATA");
ASSERT(datadir != NULL);
@@ -270,6 +310,15 @@ data_load(void)
tracks_load(tag);
}
closedir(dir);
+
+ autoplay = 0;
+ shuffle = 0;
+}
+
+void
+data_save(void)
+{
+
}
void
@@ -299,65 +348,12 @@ tracks_load(struct tag *tag)
}
void
-data_save(void)
-{
-
-}
-
-void
tracks_save(struct tag *tag)
{
}
void
-resize(void)
-{
- int i, leftw;
-
- getmaxyx(stdscr, scrh, scrw);
-
- if (scrw < 10 || scrh < 4) {
- clear();
- printw("Term too small..");
- refresh();
- usleep(10000);
- }
-
- leftw = MIN(40, 0.3f * 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
-pane_resize(struct pane *pane, int sx, int sy, int ex, int ey)
-{
- pane->sx = sx;
- pane->sy = sy;
- pane->ex = ex;
- pane->ey = ey;
- pane->w = pane->ex - pane->sx;
- pane->h = pane->ey - pane->sy;
-
- pane->active = (pane->w > 0 && pane->h > 0);
- if (pane->active) {
- wresize(pane->win, pane->h, pane->w);
- mvwin(pane->win, pane->sy, pane->sx);
- redrawwin(pane->win);
- }
-}
-
-void
-pane_init(struct pane *pane, pane_handler handle, pane_updater update)
-{
- pane->win = newwin(1, 1, 0, 0);
- ASSERT(pane->win != NULL);
- pane->handle = handle;
- pane->update = update;
-}
-
-void
pane_title(struct pane *pane, const char *title, int highlight)
{
wmove(pane->win, 0, 0);
@@ -391,7 +387,7 @@ style_off(WINDOW *win, int style)
}
wchar_t *
-command_name_generator(const wchar_t *text, int fwd, int reset)
+command_name_gen(const wchar_t *text, int fwd, int reset)
{
static int index, len;
int dir;
@@ -415,7 +411,7 @@ command_name_generator(const wchar_t *text, int fwd, int reset)
}
wchar_t *
-track_name_generator(const wchar_t *text, int fwd, int reset)
+track_name_gen(const wchar_t *text, int fwd, int reset)
{
static struct link *cur;
struct track *track;
@@ -447,12 +443,16 @@ toggle_current_tag(void)
link = link_iter(tags.next, 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(&playlist);
for (link = tags_sel.next; link; link = link->next) {
tag = UPCAST(link, struct ref)->data;
@@ -468,7 +468,7 @@ toggle_current_tag(void)
}
int
-tag_input(wint_t c)
+tag_pane_input(wint_t c)
{
switch (c) {
case KEY_UP:
@@ -485,13 +485,12 @@ tag_input(wint_t c)
player->next = NULL;
refs_free(&tags_sel);
toggle_current_tag();
- case KEY_NPAGE:
- listnav_update_sel(&track_nav,
- track_nav.sel - track_nav.wlen / 2);
return 1;
case KEY_PPAGE:
- listnav_update_sel(&track_nav,
- track_nav.sel + track_nav.wlen / 2);
+ 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;
}
@@ -499,10 +498,10 @@ tag_input(wint_t c)
}
void
-tag_vis(struct pane *pane, int sel)
+tag_pane_vis(struct pane *pane, int sel)
{
- struct tag *tag, *tag2;
- struct link *iter, *iter2;
+ struct tag *tag;
+ struct link *iter;
int index, tsel;
werase(pane->win);
@@ -512,10 +511,13 @@ tag_vis(struct pane *pane, int sel)
listnav_update_wlen(&tag_nav, pane->h - 1);
index = 0;
- for (iter = tags.next; iter; iter = iter->next) {
+ for (iter = tags.next; iter; iter = iter->next, index++) {
tag = UPCAST(iter, struct tag);
tsel = refs_incl(&tags_sel, tag);
+ if (index < tag_nav.wmin) continue;
+ if (index >= tag_nav.wmax) break;
+
if (sel && index == tag_nav.sel && tsel)
style_on(pane->win, STYLE_ITEM_HOVER_SEL);
else if (sel && index == tag_nav.sel)
@@ -523,7 +525,7 @@ tag_vis(struct pane *pane, int sel)
else if (tsel)
style_on(pane->win, STYLE_ITEM_SEL);
- wmove(pane->win, 1 + index, 0);
+ wmove(pane->win, 1 + index - tag_nav.wmin, 0);
wprintw(pane->win, "%*.*s", pane->w, pane->w, tag->name);
if (index == tag_nav.sel && tsel)
@@ -532,12 +534,11 @@ tag_vis(struct pane *pane, int sel)
style_off(pane->win, STYLE_ITEM_HOVER);
else if (tsel)
style_off(pane->win, STYLE_ITEM_SEL);
- index++;
}
}
int
-track_input(wint_t c)
+track_pane_input(wint_t c)
{
struct link *link;
struct track *track;
@@ -570,7 +571,7 @@ track_input(wint_t c)
}
void
-track_vis(struct pane *pane, int sel)
+track_pane_vis(struct pane *pane, int sel)
{
struct track *track;
struct link *iter;
@@ -608,13 +609,25 @@ track_vis(struct pane *pane, int sel)
}
}
-void
+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)) {
+ cmds[i].func(sep ? sep + 1 : NULL);
+ return 1;
+ }
+ }
+ return 0;
}
-void
+int
play_track(const wchar_t *query)
{
struct track *track;
@@ -622,11 +635,13 @@ play_track(const wchar_t *query)
for (iter = tracks.next; iter; iter = iter->next) {
track = UPCAST(iter, struct track);
- if (wcsstr(track->name, history->cmd->buf)) {
+ if (wcsstr(track->name, query)) {
player_play_track(track);
- break;
+ return 1;
}
}
+
+ return 0;
}
int
@@ -636,19 +651,18 @@ cmd_input(wint_t c)
if (cmd_mode == IMODE_EXECUTE) {
history = &command_history;
- completion = command_name_generator;
+ completion = command_name_gen;
} else if (cmd_mode == IMODE_SEARCH) {
history = &search_history;
- completion = track_name_generator;
+ completion = track_name_gen;
}
switch (c) {
case KEY_ESC:
- if (history->cmd == history->query) {
+ if (history->cmd == history->query)
pane_sel = pane_top_sel;
- } else {
+ else
history->cmd = history->query;
- }
break;
case KEY_LEFT:
inputln_left(history->cmd);
@@ -687,12 +701,11 @@ cmd_input(wint_t c)
history->cmd = history->query;
}
- if (completion_reset) {
+ if (completion_reset)
inputln_copy(&completion_query, history->query);
- }
res = completion(completion_query.buf,
- c == KEY_TAB, completion_reset);
+ c == KEY_TAB, completion_reset);
if (res) inputln_replace(history->query, res);
free(res);
@@ -712,19 +725,29 @@ cmd_input(wint_t c)
completion_reset = 1;
break;
}
- return 1;
+
+ return 1; /* grab everything */
}
void
cmd_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;
- char *line;
werase(pane->win);
+ /* static line buffer for perf */
+ if (pane->w + 1 > linecap) {
+ linecap = MAX(linecap, pane->w + 1);
+ linebuf = realloc(linebuf, linecap * sizeof(wchar_t));
+ }
+ end = linebuf + linecap;
+
wmove(pane->win, 0, 0);
style_on(pane->win, STYLE_TITLE);
wprintw(pane->win, " %-*.*ls\n", pane->w - 1, pane->w - 1,
@@ -732,42 +755,53 @@ cmd_vis(struct pane *pane, int sel)
style_off(pane->win, STYLE_TITLE);
if (player->loaded) {
- line = appendstrf(NULL, "%c ", player_state_chars[player->state]);
- line = appendstrf(line, "%s / ", timestr(player->time_pos));
- line = appendstrf(line, "%s", timestr(player->time_end));
+ 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->volume >= 0)
- line = appendstrf(line, " - vol: %u%%", player->volume);
+ if (player->msg) {
+ line += swprintf(line, end - line, L" | [PLAYER] %s",
+ player->msg);
+ }
- if (player->msg)
- line = appendstrf(line, " | [PLAYER] %s", player->msg);
+ if (!list_empty(&player->queue)) {
+ line += swprintf(line, end - line,
+ L" | [QUEUE] %i tracks",
+ list_len(&player->queue));
+ }
- if (!list_empty(&player->queue))
- line = appendstrf(line, " | [QUEUE] %i tracks",
- list_len(&player->queue));
+ if (autoplay) {
+ line += swprintf(line, end - line, L" | AUTOPLAY");
+ }
- if (autoplay)
- line = appendstrf(line, " | AUTOPLAY");
+ if (shuffle) {
+ line += swprintf(line, end - line, L" | SHUFFLE");
+ }
- if (shuffle)
- line = appendstrf(line, " | SHUFFLE");
+ line += swprintf(line, end - line, L"%*s", pane->w, L" ");
- wmove(pane->win, 1, 0);
ATTR_ON(pane->win, A_REVERSE);
- wprintw(pane->win, "%-*.*s\n", pane->w, pane->w, line);
+ mvwaddwstr(pane->win, 1, 0, linebuf);
ATTR_OFF(pane->win, A_REVERSE);
-
- free(line);
- } else {
- if (player->msg) {
- wmove(pane->win, 1, 0);
- line = aprintf("[PLAYER] %s", player->msg);
- wprintw(pane->win, "%-*.*s\n", pane->w, pane->w, line);
- free(line);
- }
+ } else if (player->msg) {
+ line = linebuf;
+ line += swprintf(line, linecap, L"[PLAYER] %s", player->msg);
+ line += swprintf(line, end - line, L"%*s", pane->w, L" ");
+ mvwaddwstr(pane->win, 1, 0, linebuf);
}
if (sel || cmd_show) {
+ line = linebuf;
+
cmd = history->cmd;
if (cmd != history->query) {
index = 0;
@@ -775,20 +809,24 @@ cmd_vis(struct pane *pane, int sel)
for (; iter; iter = iter->next, index++)
if (UPCAST(iter, struct inputln) == cmd)
break;
- line = appendstrf(NULL, "[%i] ", iter ? index : -1);
+ line += swprintf(line, end - line, L"[%i] ",
+ iter ? index : -1);
} else {
- line = appendstrf(NULL, "%c",
+ line += swprintf(line, end - line, L"%c",
cmd_mode == IMODE_SEARCH ? '/' : ':');
}
- offset = strlen(line);
- line = appendstrf(line, "%ls", cmd->buf);
- wprintw(pane->win, "%-*.*s", pane->w, pane->w, line);
- free(line);
- if (sel) { /* cursor */
+ offset = wcslen(linebuf);
+
+ line += swprintf(line, end - line, L"%ls", cmd->buf);
+ line += swprintf(line, end - line, L"%*s", pane->w, L" ");
+
+ 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] : ' ');
+ ? cmd->buf[cmd->cur] : L' ');
ATTR_OFF(pane->win, A_REVERSE);
}
}
@@ -809,26 +847,23 @@ main_input(wint_t c)
{
switch (c) {
case KEY_TAB:
- if (pane_sel == &pane_left)
- pane_sel = &pane_right;
- else
- pane_sel = &pane_left;
+ 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->track)
- player_seek(MAX(player->time_pos - 10, 0));
+ if (!player->loaded) break;
+ player_seek(MAX(player->time_pos - 10, 0));
break;
case KEY_RIGHT:
- if (player->track) {
- if (player->time_end > player->time_pos + 10) {
- player_seek(player->time_pos + 10);
- } else {
- player_next();
- }
+ if (!player->loaded) break;
+ if (player->time_end > player->time_pos + 10) {
+ player_seek(player->time_pos + 10);
+ } else {
+ player_next();
}
break;
case L'y':
@@ -892,11 +927,13 @@ 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);
@@ -906,10 +943,10 @@ main_vis(void)
}
int
-usercmd_save(const char *args)
+usercmd_save(const wchar_t *args)
{
data_save();
- return 1;
+ return 0;
}
void
@@ -962,18 +999,21 @@ main(int argc, const char **argv)
update_player();
if (c == KEY_RESIZE) {
- 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);
}
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);
}
diff --git a/src/pane.c b/src/pane.c
@@ -0,0 +1,35 @@
+#include "pane.h"
+#include "util.h"
+
+void
+pane_init(struct pane *pane, pane_handler handle, pane_updater update)
+{
+ pane->win = newwin(1, 1, 0, 0);
+ ASSERT(pane->win != NULL);
+ pane->handle = handle;
+ pane->update = update;
+}
+
+void
+pane_resize(struct pane *pane, int sx, int sy, int ex, int ey)
+{
+ pane->sx = sx;
+ pane->sy = sy;
+ pane->ex = ex;
+ pane->ey = ey;
+ pane->w = pane->ex - pane->sx;
+ pane->h = pane->ey - pane->sy;
+
+ pane->active = (pane->w > 0 && pane->h > 0);
+ if (pane->active) {
+ wresize(pane->win, pane->h, pane->w);
+ mvwin(pane->win, pane->sy, pane->sx);
+ redrawwin(pane->win);
+ }
+}
+
+void
+pane_free(struct pane *pane)
+{
+ delwin(pane->win);
+}
diff --git a/src/pane.h b/src/pane.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "ncurses.h"
+
+#include <wchar.h>
+
+struct pane;
+
+typedef int (*pane_handler)(wint_t c);
+typedef void (*pane_updater)(struct pane *pane, int sel);
+
+struct pane {
+ WINDOW *win;
+ int sx, sy, ex, ey;
+ int w, h;
+ int active;
+
+ pane_handler handle;
+ pane_updater update;
+};
+
+void pane_init(struct pane *pane, pane_handler handle, pane_updater update);
+void pane_resize(struct pane *pane, int sx, int sy, int ex, int ey);
+void pane_free(struct pane *p);
+
diff --git a/src/player.h b/src/player.h
@@ -39,8 +39,11 @@ struct player {
struct link queue;
struct link history;
- /* current loaded track */
+ /* last player track */
struct track *track;
+
+ /* player has a track loaded,
+ * not necessarily player->track */
int loaded;
/* stopped, paused or playing */