/* * Made by armaoin 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 #include #include #include #include #include #include #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; }