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 <[email protected]> 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;
}