Compare commits

...

2 Commits

Author SHA1 Message Date
cc78fe00af
Ensure pulse gets cleaned up 2023-09-09 00:31:15 -07:00
26b304eeb3
Make pulse listener built in 2023-09-08 23:39:55 -07:00
4 changed files with 88 additions and 68 deletions

View File

@ -3,13 +3,18 @@ CC := cc
CFLAGS := -pedantic -Wall -Wno-deprecated-declarations -Os CFLAGS := -pedantic -Wall -Wno-deprecated-declarations -Os
LDFLAGS := -lX11 LDFLAGS := -lX11
PULSEFLAGS := -lpulse PULSEFLAGS := -lpulse
NO_X := 0
ifeq ($(NO_X), 1)
CFLAGS := $(CFLAGS) -DNO_X
endif
# FreeBSD (uncomment) # FreeBSD (uncomment)
#LDFLAGS += -L/usr/local/lib -I/usr/local/include #LDFLAGS += -L/usr/local/lib -I/usr/local/include
# # OpenBSD (uncomment) # # OpenBSD (uncomment)
#LDFLAGS += -L/usr/X11R6/lib -I/usr/X11R6/include #LDFLAGS += -L/usr/X11R6/lib -I/usr/X11R6/include
all: options dwmblocks dwmblocks-pulse-listener all: options dwmblocks
options: options:
@echo dwmblocks build options: @echo dwmblocks build options:
@ -18,28 +23,23 @@ options:
@echo "CC = ${CC}" @echo "CC = ${CC}"
@echo "PULSEFLAGS = ${PULSEFLAGS}" @echo "PULSEFLAGS = ${PULSEFLAGS}"
dwmblocks: dwmblocks.c blocks.def.h blocks.h dwmblocks: dwmblocks.c pulse-listener.c blocks.def.h blocks.h
${CC} -o dwmblocks dwmblocks.c ${CFLAGS} ${LDFLAGS} ${CC} -o dwmblocks dwmblocks.c pulse-listener.c ${CFLAGS} ${PULSEFLAGS} ${LDFLAGS}
dwmblocks-pulse-listener: dwmblocks-pulse-listener.c
${CC} -o dwmblocks-pulse-listener dwmblocks-pulse-listener.c ${CFLAGS} ${PULSEFLAGS}
blocks.h: blocks.h:
cp blocks.def.h $@ cp blocks.def.h $@
clean: clean:
rm -f *.o *.gch dwmblocks dwmblocks-pulse-listener rm -f *.o *.gch dwmblocks
install: dwmblocks install: dwmblocks
mkdir -p ${DESTDIR}${PREFIX}/bin mkdir -p ${DESTDIR}${PREFIX}/bin
cp -f dwmblocks ${DESTDIR}${PREFIX}/bin cp -f dwmblocks ${DESTDIR}${PREFIX}/bin
cp -f dwmblocks-pulse-listener ${DESTDIR}${PREFIX}/bin
chmod 755 ${DESTDIR}${PREFIX}/bin/dwmblocks chmod 755 ${DESTDIR}${PREFIX}/bin/dwmblocks
install -m0755 scripts/* ${DESTDIR}${PREFIX}/bin install -m0755 scripts/* ${DESTDIR}${PREFIX}/bin
uninstall: uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/dwmblocks \ rm -f ${DESTDIR}${PREFIX}/bin/dwmblocks \
${DESTDIR}${PREFIX}/bin/dwmblocks-pulse-listener \
${DESTDIR}${PREFIX}/bin/dwmblocks-battery \ ${DESTDIR}${PREFIX}/bin/dwmblocks-battery \
${DESTDIR}${PREFIX}/bin/dwmblocks-network \ ${DESTDIR}${PREFIX}/bin/dwmblocks-network \
${DESTDIR}${PREFIX}/bin/dwmblocks-volume \ ${DESTDIR}${PREFIX}/bin/dwmblocks-volume \

View File

@ -5,6 +5,7 @@
#include<signal.h> #include<signal.h>
#include <time.h> #include <time.h>
#include <math.h> #include <math.h>
#include <pthread.h>
#ifndef NO_X #ifndef NO_X
#include<X11/Xlib.h> #include<X11/Xlib.h>
#endif #endif
@ -27,6 +28,7 @@ typedef struct {
unsigned int interval; unsigned int interval;
unsigned int signal; unsigned int signal;
} Block; } Block;
typedef void(*ThreadCallback)(pthread_t *);
void getcmd(const Block *block, char *output); void getcmd(const Block *block, char *output);
void getcmds(int time); void getcmds(int time);
void getsigcmds(unsigned int signal); void getsigcmds(unsigned int signal);
@ -38,7 +40,9 @@ void setupsignals(void);
int getstatus(char *str, char *last); int getstatus(char *str, char *last);
void pstdout(void); void pstdout(void);
int gcd(int n1, int n2); int gcd(int n1, int n2);
void startthreads(void);
void statusloop(void); void statusloop(void);
void stopthreads(void);
void termhandler(void); void termhandler(void);
#ifndef NO_X #ifndef NO_X
void setroot(void); void setroot(void);
@ -57,6 +61,10 @@ static char statusbar[LENGTH(blocks)][CMDLENGTH] = {0};
static char statusstr[2][STATUSLENGTH]; static char statusstr[2][STATUSLENGTH];
static int statuscontinue = 1; static int statuscontinue = 1;
static pthread_t main_thread;
static pthread_t threads[LENGTH(thread_callbacks)];
static int initdone = 0;
//opens process *cmd and stores output in *output //opens process *cmd and stores output in *output
void getcmd(const Block *block, char *output) { void getcmd(const Block *block, char *output) {
strcpy(output, block->icon); strcpy(output, block->icon);
@ -141,13 +149,21 @@ int getstatus(char *str, char *last) {
} }
void pstdout() { void pstdout() {
if (!getstatus(statusstr[0], statusstr[1])) { //Only write out if text has changed. if (!initdone || !getstatus(statusstr[0], statusstr[1])) { //Only write out if text has changed.
return; return;
} }
printf("%s\n", statusstr[0]); printf("%s\n", statusstr[0]);
fflush(stdout); fflush(stdout);
} }
void somebar_pstdout() {
if (!initdone || !getstatus(statusstr[0], statusstr[1])) { //Only write out if text has changed.
return;
}
printf("status %s\n", statusstr[0]);
fflush(stdout);
}
int gcd(int n1, int n2) { int gcd(int n1, int n2) {
int temp; int temp;
while (n2 > 0){ while (n2 > 0){
@ -158,6 +174,16 @@ int gcd(int n1, int n2) {
return n1; return n1;
} }
void startthreads() {
main_thread = pthread_self();
for (int i = 0; i < LENGTH(thread_callbacks); ++i) {
pthread_create(&threads[i],
NULL,
(void *(*)(void *)) thread_callbacks[i],
&main_thread);
}
}
void statusloop() { void statusloop() {
setupsignals(); setupsignals();
unsigned int interval = -1; unsigned int interval = -1;
@ -166,6 +192,7 @@ void statusloop() {
interval = gcd(blocks[i].interval, interval); interval = gcd(blocks[i].interval, interval);
} }
} }
startthreads();
int i = 0; int i = 0;
getcmds(-1); getcmds(-1);
long nsec_part = interval * 1000000; long nsec_part = interval * 1000000;
@ -174,6 +201,7 @@ void statusloop() {
const struct timespec sleeptime = { sec_part, nsec_part }; const struct timespec sleeptime = { sec_part, nsec_part };
struct timespec tosleep = sleeptime; struct timespec tosleep = sleeptime;
while (statuscontinue) { while (statuscontinue) {
initdone = 1;
if (nanosleep(&tosleep, &tosleep) < 0) { if (nanosleep(&tosleep, &tosleep) < 0) {
continue; continue;
} }
@ -183,13 +211,20 @@ void statusloop() {
} }
} }
void stopthreads() {
for (int i = 0; i < LENGTH(thread_callbacks); ++i) {
pthread_cancel(threads[i]);
pthread_join(threads[i], NULL);
}
}
void termhandler() { void termhandler() {
statuscontinue = 0; statuscontinue = 0;
} }
#ifndef NO_X #ifndef NO_X
void setroot() { void setroot() {
if (!getstatus(statusstr[0], statusstr[1])) { //Only set root if text has changed. if (!initdone || !getstatus(statusstr[0], statusstr[1])) { //Only set root if text has changed.
return; return;
} }
XStoreName(dpy, root, statusstr[0]); XStoreName(dpy, root, statusstr[0]);
@ -214,6 +249,8 @@ int main(int argc, char** argv) {
strncpy(delim, argv[++i], delimLen); strncpy(delim, argv[++i], delimLen);
} else if (strcmp("-p", argv[i]) == 0) { } else if (strcmp("-p", argv[i]) == 0) {
writestatus = pstdout; writestatus = pstdout;
} else if (strcmp("-s", argv[i]) == 0) {
writestatus = somebar_pstdout;
} }
} }
#ifndef NO_X #ifndef NO_X
@ -229,5 +266,6 @@ int main(int argc, char** argv) {
#ifndef NO_X #ifndef NO_X
XCloseDisplay(dpy); XCloseDisplay(dpy);
#endif #endif
stopthreads();
return 0; return 0;
} }

View File

@ -1,8 +1,10 @@
#include "pulse-listener.h"
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <sys/signal.h> #include <signal.h>
#include <limits.h> #include <limits.h>
#define ERROR(...) fprintf(stderr, "error: " __VA_ARGS__) #define ERROR(...) fprintf(stderr, "error: " __VA_ARGS__)
@ -15,113 +17,85 @@
#define SIGMIN SIGRTMIN #define SIGMIN SIGRTMIN
#endif #endif
typedef struct {
size_t pid;
unsigned int signum;
} TargetData;
static void sink_info_callback(pa_context *ctx, const pa_sink_info *info, static void sink_info_callback(pa_context *ctx, const pa_sink_info *info,
int eol, TargetData *target) { int eol, pthread_t *main_thread) {
if (info && kill(target->pid, target->signum) < 0) { pthread_kill(*main_thread, SIGMIN + 1);
ERROR("terget process died\n");
exit(1);
}
} }
static void server_info_callback(pa_context *ctx, const pa_server_info *info, static void server_info_callback(pa_context *ctx, const pa_server_info *info,
TargetData *target) { pthread_t *main_thread) {
pa_operation *op = pa_context_get_sink_info_by_name( pa_operation *op = pa_context_get_sink_info_by_name(
ctx, ctx,
info->default_sink_name, info->default_sink_name,
(pa_sink_info_cb_t) sink_info_callback, (pa_sink_info_cb_t) sink_info_callback,
target); main_thread);
pa_operation_unref(op); pa_operation_unref(op);
} }
static void subscribe_callback(pa_context *ctx, static void subscribe_callback(pa_context *ctx,
pa_subscription_event_type_t type, uint32_t idx, pa_subscription_event_type_t type, uint32_t idx,
TargetData *target) { pthread_t *main_thread) {
if ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { if ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
pa_operation *op = pa_context_get_sink_info_by_index(ctx, pa_operation *op = pa_context_get_sink_info_by_index(ctx,
idx, idx,
(pa_sink_info_cb_t) sink_info_callback, (pa_sink_info_cb_t) sink_info_callback,
target); main_thread);
pa_operation_unref(op); pa_operation_unref(op);
} }
} }
static void state_callback(pa_context *ctx, TargetData *target) { static void state_callback(pa_context *ctx, pthread_t *main_thread) {
pa_operation *op; pa_operation *op;
switch (pa_context_get_state(ctx)) { switch (pa_context_get_state(ctx)) {
case PA_CONTEXT_READY: case PA_CONTEXT_READY:
op = pa_context_get_server_info(ctx, op = pa_context_get_server_info(ctx,
(pa_server_info_cb_t) server_info_callback, (pa_server_info_cb_t) server_info_callback,
target); main_thread);
pa_operation_unref(op); pa_operation_unref(op);
pa_context_set_subscribe_callback(ctx, pa_context_set_subscribe_callback(ctx,
(pa_context_subscribe_cb_t) subscribe_callback, (pa_context_subscribe_cb_t) subscribe_callback,
target); main_thread);
op = pa_context_subscribe(ctx, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL); op = pa_context_subscribe(ctx, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL);
pa_operation_unref(op); pa_operation_unref(op);
break; break;
case PA_CONTEXT_FAILED: case PA_CONTEXT_FAILED:
if (kill(target->pid, target->signum) < 0) { pthread_kill(*main_thread, SIGMIN + 1);
ERROR("terget process died\n");
exit(1);
}
break; break;
default: default:
break; break;
} }
} }
int main(int argc, const char **argv) { static void cleanup(void *data_to_free[2]) {
if (argc < 2) { pa_context_unref(data_to_free[1]);
ERROR("no pid\n"); pa_mainloop_free(data_to_free[0]);
return 1;
}
char *endptr = NULL;
unsigned int signum = SIGMIN + 1;
if (argc >= 3 && argv[1][0] == '-') {
const char *signumstr = argv[1] + 1;
unsigned long num = strtoul(signumstr, &endptr, 10);
if (num > SIGMAX - SIGMIN) {
ERROR("signal out of range: %lu\n", num);
return 1;
}
signum = SIGMIN + num;
++argv;
}
endptr = NULL;
errno = 0;
unsigned long pid = strtoul(argv[1], &endptr, 10);
if (argv[1][0] == '-' || errno == ERANGE) {
ERROR("pid out of range: \"%s\"\n", argv[1]);
return 1;
} }
void pulse_listener_main(pthread_t *main_thread) {
pa_mainloop *mainloop = pa_mainloop_new(); pa_mainloop *mainloop = pa_mainloop_new();
if (!mainloop) { if (!mainloop) {
ERROR("could not create pulse mainloop\n"); ERROR("could not create pulse mainloop\n");
return 1; return;
} }
pa_mainloop_api *api = pa_mainloop_get_api(mainloop); pa_mainloop_api *api = pa_mainloop_get_api(mainloop);
if (!api) { if (!api) {
ERROR("could not create pulse mainloop api\n"); ERROR("could not create pulse mainloop api\n");
return 1; return;
} }
pa_context *context = pa_context_new(api, "dwmblocks-listener"); pa_context *context = pa_context_new(api, "dwmblocks-listener");
if (!context) { if (!context) {
ERROR("could not create pulse context\n"); ERROR("could not create pulse context\n");
return 1; return;
} }
TargetData target = { pid, signum };
pa_context_set_state_callback(context, pa_context_set_state_callback(context,
(pa_context_notify_cb_t)&state_callback, (pa_context_notify_cb_t)&state_callback,
&target); main_thread);
if (pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { if (pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) {
ERROR("could not connect to pulse"); ERROR("could not connect to pulse");
return 1; return;
} }
void *data_to_free[2] = { mainloop, context };
pthread_cleanup_push((void(*)(void *)) cleanup, data_to_free);
pa_mainloop_run(mainloop, NULL); pa_mainloop_run(mainloop, NULL);
return 0; pthread_cleanup_pop(1);
} }

8
pulse-listener.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef INCLUDED_PULSE_LISTENER_H
#define INCLUDED_PULSE_LISTENER_H
#include <pthread.h>
void pulse_listener_main(pthread_t *main_thread);
#endif