Command line options!
Made update interval, scroll length and scroll step settable at runtime. Renamed meta to state, changed its structure. Optimizations to print_status: - Calculate full_label and padded_label (and their lengths) only when the song changes, rather than doing multiple sprintf's and strlen's each call - Only allocate space for the printed label when doing scrolling, otherwise just update the label pointer directly to the full_label Other minor changes.
This commit is contained in:
parent
aa27ea7c53
commit
2424a92e4c
173
mpd_control.c
173
mpd_control.c
@ -8,14 +8,70 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#include <argp.h>
|
||||||
|
|
||||||
struct worker_meta {
|
const char *argp_program_version = "mpd_control 0.3.0";
|
||||||
long long update_interval;
|
const char *argp_program_bug_address = "<matt-low@zusmail.xyz>";
|
||||||
bool stop;
|
static char doc[] = "MPD blocket for i3blocks.";
|
||||||
int scroll_length;
|
static char args_doc[] = "[OPTION]...";
|
||||||
int scroll_step;
|
static struct argp_option options[] = {
|
||||||
|
{ "interval", 'i', "interval", 0, "How frequently to update the status line (in seconds). Default: 1"},
|
||||||
|
{ "length", 'l', "length", 0, "The length of the shown song label (in characters). Default: 25"},
|
||||||
|
{ "step", 's', "step", 0, "How many characters to step the label while scrolling. Default: 3"},
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct arguments {
|
||||||
|
float interval;
|
||||||
|
int length;
|
||||||
|
int step;
|
||||||
|
};
|
||||||
|
static struct arguments arguments;
|
||||||
|
|
||||||
|
|
||||||
|
static error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||||
|
struct arguments *arguments = state->input;
|
||||||
|
switch (key) {
|
||||||
|
case 'i':
|
||||||
|
if (!arg) break;
|
||||||
|
float i = strtof(arg, NULL);
|
||||||
|
if (!i || i < 0) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
arguments->interval = i;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (!arg) break;
|
||||||
|
long l = strtol(arg, NULL, 10);
|
||||||
|
if (!l || l < 0) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
arguments->length = l;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if (!arg) break;
|
||||||
|
long s = strtol(arg, NULL, 10);
|
||||||
|
if (!s || s < 0) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
arguments->step = s; break;
|
||||||
|
case ARGP_KEY_ARG: return 0;
|
||||||
|
default: return ARGP_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };
|
||||||
|
|
||||||
|
struct worker_state {
|
||||||
int scroll_index;
|
int scroll_index;
|
||||||
char* label;
|
char* artist;
|
||||||
|
char* album;
|
||||||
|
char* title;
|
||||||
|
char* full_label;
|
||||||
|
size_t full_label_length;
|
||||||
|
char* padded_label;
|
||||||
|
size_t padded_label_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
static long long
|
static long long
|
||||||
@ -74,17 +130,15 @@ get_tag(struct mpd_song *song, char *tag, enum mpd_tag_type type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
print_status(struct mpd_connection *conn, struct worker_meta *meta)
|
print_status(struct mpd_connection *conn, struct worker_state *state)
|
||||||
{
|
{
|
||||||
struct mpd_status *status = mpd_run_status(conn);
|
struct mpd_status *status = mpd_run_status(conn);
|
||||||
|
|
||||||
if (status == NULL)
|
if (status == NULL)
|
||||||
return handle_error(conn, false);
|
return handle_error(conn, false);
|
||||||
|
|
||||||
const enum mpd_state state = mpd_status_get_state(status);
|
|
||||||
|
|
||||||
char *play_icon = NULL;
|
char *play_icon = NULL;
|
||||||
switch (state) {
|
switch (mpd_status_get_state(status)) {
|
||||||
case MPD_STATE_PLAY:
|
case MPD_STATE_PLAY:
|
||||||
play_icon = "";
|
play_icon = "";
|
||||||
break;
|
break;
|
||||||
@ -92,7 +146,7 @@ print_status(struct mpd_connection *conn, struct worker_meta *meta)
|
|||||||
play_icon = "";
|
play_icon = "";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
meta->scroll_index = 0;
|
state->scroll_index = 0;
|
||||||
printf("\n");
|
printf("\n");
|
||||||
mpd_status_free(status);
|
mpd_status_free(status);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@ -133,37 +187,42 @@ print_status(struct mpd_connection *conn, struct worker_meta *meta)
|
|||||||
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS)
|
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS)
|
||||||
return handle_error(conn, false);
|
return handle_error(conn, false);
|
||||||
|
|
||||||
// the full song label. would be cool if this was customizable at runtime
|
if (strcmp(state->title, title) != 0
|
||||||
char full_label[strlen(artist) + strlen(title) + 4];
|
|| strcmp(state->album, album) != 0
|
||||||
sprintf(full_label, "%s - %s", title, artist);
|
|| strcmp(state->artist, artist) != 0) {
|
||||||
|
strcpy(state->title, title);
|
||||||
|
strcpy(state->album, album);
|
||||||
|
strcpy(state->artist, artist);
|
||||||
|
|
||||||
// compare current label against previous update's label
|
// The song changed, so let's update the full label...
|
||||||
if (strcmp(meta->label, full_label) != 0) {
|
sprintf(state->full_label, "%s - %s", title, artist);
|
||||||
// The song changed, reset scroll and update meta
|
state->full_label_length = g_utf8_strlen(state->full_label, -1);
|
||||||
meta->scroll_index = 0;
|
|
||||||
strcpy(meta->label, full_label);
|
// ... and the padded label (if the full label is long enough) ...
|
||||||
|
if (state->full_label_length > arguments.length) {
|
||||||
|
sprintf(state->padded_label, "%s | ", state->full_label);
|
||||||
|
state->padded_label_length = g_utf8_strlen(state->padded_label, -1);
|
||||||
|
}
|
||||||
|
// ... and reset the scroll index
|
||||||
|
state->scroll_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The label we'll actually print
|
// The label we'll actually print
|
||||||
// Allocate scroll_length * 4 chars(bytes) since each UTF-8 character may
|
char *label = NULL;
|
||||||
// consume up to 4 bytes.
|
|
||||||
char label[(meta->scroll_length*4)+1];
|
|
||||||
strncpy(label, "", sizeof(label));
|
|
||||||
|
|
||||||
if (g_utf8_strlen(full_label, -1) > meta->scroll_length) {
|
if (state->full_label_length > arguments.length) {
|
||||||
// If length of full label > meta->scroll_length, we'll scroll it
|
// If length of full label > scroll length, we'll scroll it
|
||||||
|
|
||||||
// Pad the text with a separator
|
// Allocate enough space for the buffer
|
||||||
char padded_text[strlen(full_label) + 3 + 1];
|
// Up to 4 bytes per character because utf8.
|
||||||
padded_text[sizeof(padded_text)-1] = '\0';
|
label = alloca((arguments.length*4)+1);
|
||||||
|
label[0] = '\0';
|
||||||
|
|
||||||
sprintf(padded_text, "%s | ", full_label);
|
scroll_text(state->padded_label, state->padded_label_length, label,
|
||||||
|
&state->scroll_index, arguments.length);
|
||||||
scroll_text(padded_text, g_utf8_strlen(padded_text, -1), label,
|
|
||||||
&meta->scroll_index, meta->scroll_length);
|
|
||||||
} else {
|
} else {
|
||||||
// Else we'll just use the full label
|
// Else we'll just point label directly to the full label
|
||||||
strcpy(label, full_label);
|
label = state->full_label;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s%s %s (-%u:%02u) [%u/%u]\n",
|
printf("%s%s %s (-%u:%02u) [%u/%u]\n",
|
||||||
@ -176,23 +235,26 @@ print_status(struct mpd_connection *conn, struct worker_meta *meta)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
long long max(int a, int b) {
|
||||||
|
return a >= b? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
void*
|
void*
|
||||||
status_loop(void* worker_meta)
|
status_loop(void* worker_state)
|
||||||
{
|
{
|
||||||
struct worker_meta *meta = (struct worker_meta*)worker_meta;
|
struct worker_state *state = (struct worker_state*)worker_state;
|
||||||
struct mpd_connection *conn = mpd_connection_new(NULL, 0, 0);
|
struct mpd_connection *conn = mpd_connection_new(NULL, 0, 0);
|
||||||
|
|
||||||
while (!meta->stop) {
|
for (;;) {
|
||||||
long long start = current_timestamp();
|
long long start = current_timestamp();
|
||||||
|
|
||||||
if (handle_error(conn, true) == 0 && print_status(conn, meta) == 0) {
|
if (handle_error(conn, true) == 0 && print_status(conn, state) == 0) {
|
||||||
meta->scroll_index += meta->scroll_step;
|
state->scroll_index += arguments.step;
|
||||||
}
|
}
|
||||||
|
|
||||||
long long elapsed = current_timestamp() - start;
|
long long elapsed = current_timestamp() - start;
|
||||||
if (meta->update_interval - elapsed >= 0) {
|
usleep(max(0, ((arguments.interval*1000) - elapsed) * 1000));
|
||||||
usleep((meta->update_interval - elapsed) * 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mpd_connection_free(conn);
|
mpd_connection_free(conn);
|
||||||
@ -204,7 +266,7 @@ enum click_command {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
mpd_run_command(struct worker_meta *meta, enum click_command command) {
|
mpd_run_command(struct worker_state *state, enum click_command command) {
|
||||||
struct mpd_connection *conn = mpd_connection_new(NULL, 0, 0);
|
struct mpd_connection *conn = mpd_connection_new(NULL, 0, 0);
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case TOGGLE_PAUSE:
|
case TOGGLE_PAUSE:
|
||||||
@ -222,15 +284,20 @@ mpd_run_command(struct worker_meta *meta, enum click_command command) {
|
|||||||
case SEEK_BCKWD:
|
case SEEK_BCKWD:
|
||||||
mpd_run_seek_current(conn, -3, true);
|
mpd_run_seek_current(conn, -3, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
print_status(conn, meta);
|
print_status(conn, state);
|
||||||
mpd_connection_free(conn);
|
mpd_connection_free(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
arguments.interval = 1.0f;
|
||||||
|
arguments.length = 25;
|
||||||
|
arguments.step = 3;
|
||||||
|
|
||||||
int main(void) {
|
argp_parse(&argp, argc, argv, 0, 0, &arguments);
|
||||||
struct worker_meta meta = {950, false, 25, 3, 0, malloc(1024)};
|
|
||||||
|
struct worker_state state = {0, malloc(256), malloc(256), malloc(256),
|
||||||
|
malloc(1024), 0, malloc(1024), 0};
|
||||||
|
|
||||||
struct sigaction new_actn, old_actn;
|
struct sigaction new_actn, old_actn;
|
||||||
new_actn.sa_handler = SIG_IGN;
|
new_actn.sa_handler = SIG_IGN;
|
||||||
@ -239,7 +306,7 @@ int main(void) {
|
|||||||
sigaction(SIGPIPE, &new_actn, &old_actn);
|
sigaction(SIGPIPE, &new_actn, &old_actn);
|
||||||
|
|
||||||
pthread_t update_thread;
|
pthread_t update_thread;
|
||||||
if(pthread_create(&update_thread, NULL, status_loop, &meta)) {
|
if(pthread_create(&update_thread, NULL, status_loop, &state)) {
|
||||||
fprintf(stderr, "Error creating thread\n");
|
fprintf(stderr, "Error creating thread\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -250,15 +317,15 @@ int main(void) {
|
|||||||
|
|
||||||
if (getline(&line, &size, stdin) != -1) {
|
if (getline(&line, &size, stdin) != -1) {
|
||||||
if (strcmp(line, "1\n") == 0) {
|
if (strcmp(line, "1\n") == 0) {
|
||||||
mpd_run_command(&meta, PREVIOUS);
|
mpd_run_command(&state, PREVIOUS);
|
||||||
} else if (strcmp(line, "2\n") == 0) {
|
} else if (strcmp(line, "2\n") == 0) {
|
||||||
mpd_run_command(&meta, TOGGLE_PAUSE);
|
mpd_run_command(&state, TOGGLE_PAUSE);
|
||||||
} else if (strcmp(line, "3\n") == 0) {
|
} else if (strcmp(line, "3\n") == 0) {
|
||||||
mpd_run_command(&meta, NEXT);
|
mpd_run_command(&state, NEXT);
|
||||||
} else if (strcmp(line, "4\n") == 0) {
|
} else if (strcmp(line, "4\n") == 0) {
|
||||||
mpd_run_command(&meta, SEEK_FWD);
|
mpd_run_command(&state, SEEK_FWD);
|
||||||
} else if (strcmp(line, "5\n") == 0) {
|
} else if (strcmp(line, "5\n") == 0) {
|
||||||
mpd_run_command(&meta, SEEK_BCKWD);
|
mpd_run_command(&state, SEEK_BCKWD);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(line);
|
free(line);
|
||||||
|
Loading…
Reference in New Issue
Block a user