Hi all,
I'm a new dwm user and i've written a simple dwmstatusbar app that
show volume and time.
The code relative to volume use alsalib and maybe someone can find it useful.
Critics and suggestion are welcome.
/*
 * Made by armaoin <arma...@gmail.com> 2012-12-30 (yes the Mayans were wrong)
 * based on amixer.c from alsa-utils and profil-dwmstatus-1.0.c by profil.
 *
 * Compile with:
 * gcc -Wall -pedantic -std=c99 -lX11 -lasound dwmstatusbar.c
 * */
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <locale.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <alsa/asoundlib.h>

#define STATUS_MAX_DIMENSION 90
#define DATE_MAX_DIMENSION 30
#define TIMEFORMAT "%a %d %b %H:%M"
#define UPDATE_INTERVAL 1
#define CARD_NAME "default"
#define CHANNEL SND_MIXER_SCHN_MONO

static size_t gettime(char *s, size_t max, const char *format);
static void setstatus(Display *dpy, char *name);
static int getvolume(const char *, snd_mixer_selem_channel_id_t, int *, int *);

int main(int argc, char **argv) {
	char statusbar[STATUS_MAX_DIMENSION];
	char date[DATE_MAX_DIMENSION]; /* current time in strftime format */
	Display *dpy;
	int vol;
	int is_active;

	/* Open display */
	if (!(dpy = XOpenDisplay(NULL))) {
		fprintf(stderr, "Cannot open display.\n");
		exit(EXIT_FAILURE);
	}


	while(1) {
		if (!gettime(date, sizeof(date), TIMEFORMAT)) {
			/* 0 byte in date there is error*/
			fprintf(stderr, "Error on gettime()\n");
			exit(EXIT_FAILURE);
		}

		if (getvolume(CARD_NAME, CHANNEL, &vol, &is_active) < 0) {
			fprintf(stderr, "Error on getvolume()\n");
			exit(EXIT_FAILURE);
		}

		sprintf(statusbar, "VOL:%d%%%s :: %s", vol, is_active ? "[on]" : "[off]", date);
		setstatus(dpy, statusbar);

		sleep(UPDATE_INTERVAL);
	}

	XCloseDisplay(dpy);
	exit(EXIT_SUCCESS);
}

/* A wrapper to strftime that return a string that rapresent
 * current time in system locale in according to format string.
 * return the number of byte written in s.
 * */
static size_t gettime(char *s, size_t max, const char *format) {
	time_t t;
	struct tm *tm;

	/* Get system locale */
	setlocale(LC_TIME, "");

	if ((t = time(NULL)) == -1) {
		fprintf(stderr, "Error on time()");
		return 0;
	}

	if (!(tm = localtime(&t))) {
		fprintf(stderr, "Error on localtime(t)");
		return 0;
	}

	return strftime(s, max, format, tm);
}

/* change the name of the DefaultRootWindow on Display dpy
 * to name */
static void setstatus(Display *dpy, char *name) {
	XStoreName(dpy, DefaultRootWindow(dpy), name);
	XSync(dpy, False);
}

/*
 * This function put in *volume the volume (in %) associated with the first
 * simple control for the card indicated by the string *card (usally the card
 * name is "dafault").
 * The first control of the card default is the first control that show
 * amixer if launched without any options. (This code is based on the source 
 * of amixer).
 * is_active will be 0 if the scontrol is mute (off) or 1 if the scontrol is
 * unmute (on).
 * channel is used to select the corret channel if scontrol has multiple 
 * multiple channels.
 * channel is an enum and can assume the following value:
 * SND_MIXER_SCHN_UNKNOWN 	Unknown
 * SND_MIXER_SCHN_FRONT_LEFT 	Front left
 * SND_MIXER_SCHN_FRONT_RIGHT 	Front right
 * SND_MIXER_SCHN_REAR_LEFT 	Rear left
 * SND_MIXER_SCHN_REAR_RIGHT 	Rear right
 * SND_MIXER_SCHN_FRONT_CENTER 	Front center
 * SND_MIXER_SCHN_WOOFER 	Woofer
 * SND_MIXER_SCHN_SIDE_LEFT 	Side Left
 * SND_MIXER_SCHN_SIDE_RIGHT 	Side Right
 * SND_MIXER_SCHN_REAR_CENTER 	Rear Center
 * SND_MIXER_SCHN_MONO 	Mono (Front left alias)
 *
 * The returned value is 0 on success and a negative on failure
 * */
static int getvolume(const char *card, snd_mixer_selem_channel_id_t channel, int *volume, int *is_active) {
	int err;
	snd_mixer_t *mhandle;	/* mixer handle */
	snd_mixer_elem_t *elem;	/* simple mixer handle */
	long int vol;		/* current playback volume */
	long int vmin;		/* min playback volume */
	long int vmax;		/* max playback volume */

	/* create a new empty mixex and link to card */
	if ((err = snd_mixer_open(&mhandle,0)) < 0 ) {
		fprintf(stderr, "Cannot open mixer\n");
		snd_mixer_close(mhandle);
		return err;
	}
	if ((err = snd_mixer_attach(mhandle, card)) < 0) {
		fprintf(stderr, "Cannot attach mixer to card:%s\n", card);
		snd_mixer_close(mhandle);
		return err;
	}

	/* Register loading and association */
	if ((err =  snd_mixer_selem_register(mhandle, NULL, NULL)) < 0) {
		fprintf(stderr, "Cannot register simple mixer\n");
		snd_mixer_close(mhandle);
		return err;
	}
	if ((err = snd_mixer_load(mhandle)) < 0) {
		fprintf(stderr, "Error on loading mixer\n");
		snd_mixer_close(mhandle);
		return err;
	}
	if (!(elem = snd_mixer_first_elem(mhandle))) {
		fprintf(stderr, "First element is null\n");
		snd_mixer_close(mhandle);
		return -1;
	}

	/* Getting volumes */
	if ((err = snd_mixer_selem_get_playback_volume(elem, channel, &vol)) < 0) {
		fprintf(stderr, "Cannot get volume\n");
		snd_mixer_close(mhandle);
		return err;
	}
	if ((err = snd_mixer_selem_get_playback_volume_range(elem, &vmin, &vmax)) < 0) {
		fprintf(stderr, "Cannot get volume range\n");
		snd_mixer_close(mhandle);
		return err;
	}
	if ((err = snd_mixer_selem_get_playback_switch(elem, channel, is_active)) < 0) {
		fprintf(stderr, "Cannot get switch\n");
		snd_mixer_close(mhandle);
		return err;
	}

	if (vmax != vmin) {
		*volume = (int)((float)vol / (vmax - vmin) * 100);
	}
	else {
		fprintf(stderr, "Division by zero: vmax == vmin\n");
		snd_mixer_close(mhandle);
		return -1;
	}

	snd_mixer_close(mhandle);
	return 0;
}

Reply via email to