]> git.sur5r.net Git - i3/i3status/blob - src/print_volume.c
0eca0c0a2bbae34d432a2c2737bbceff201a7ba6
[i3/i3status] / src / print_volume.c
1 // vim:ts=8:expandtab
2 #include <time.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6
7 #ifdef LINUX
8 #include <alsa/asoundlib.h>
9 #include <alloca.h>
10 #endif
11
12 #include "i3status.h"
13 #include "queue.h"
14
15 #ifdef LINUX
16 struct mixer_hdl {
17         char *device;
18         char *mixer;
19         int mixer_idx;
20         snd_mixer_selem_id_t *sid;
21         snd_mixer_t *m;
22         snd_mixer_elem_t *elem;
23         long min;
24         long max;
25
26         TAILQ_ENTRY(mixer_hdl) handles;
27 };
28
29 TAILQ_HEAD(handles_head, mixer_hdl) cached = TAILQ_HEAD_INITIALIZER(cached);
30 #endif
31
32 static void free_hdl(struct mixer_hdl *hdl) {
33         free(hdl->device);
34         free(hdl->mixer);
35         free(hdl);
36 }
37
38 void print_volume(const char *fmt, const char *device, const char *mixer, int mixer_idx) {
39 /* Printing volume only works with ALSA at the moment */
40 #ifndef LINUX
41         return;
42 #endif
43         /* Check if we already opened the mixer and get the handle
44          * from cache if so */
45         bool found = false;
46         int err;
47         struct mixer_hdl *hdl;
48         TAILQ_FOREACH(hdl, &cached, handles) {
49                 if (strcmp(hdl->device, device) != 0 ||
50                     strcmp(hdl->mixer, mixer) != 0 ||
51                     hdl->mixer_idx != mixer_idx)
52                         continue;
53                 found = true;
54                 break;
55         }
56
57         if (!found) {
58                 if ((hdl = calloc(sizeof(struct mixer_hdl), 1)) == NULL)
59                         return;
60
61                 if ((hdl->device = strdup(device)) == NULL) {
62                         free(hdl);
63                         return;
64                 }
65
66                 if ((hdl->mixer = strdup(mixer)) == NULL) {
67                         free(hdl->device);
68                         free(hdl);
69                         return;
70                 }
71
72                 hdl->mixer_idx = mixer_idx;
73                 snd_mixer_selem_id_malloc(&(hdl->sid));
74                 if (hdl->sid == NULL) {
75                         free_hdl(hdl);
76                         return;
77                 }
78
79                 if ((err = snd_mixer_open(&(hdl->m), 0)) < 0) {
80                         fprintf(stderr, "ALSA: Cannot open mixer: %s\n", snd_strerror(err));
81                         free_hdl(hdl);
82                         return;
83                 }
84
85                 /* Attach this mixer handle to the given device */
86                 if ((err = snd_mixer_attach(hdl->m, device)) < 0) {
87                         fprintf(stderr, "ALSA: Cannot attach mixer to device: %s\n", snd_strerror(err));
88                         snd_mixer_close(hdl->m);
89                         free_hdl(hdl);
90                         return;
91                 }
92
93                 /* Register this mixer */
94                 if ((err = snd_mixer_selem_register(hdl->m, NULL, NULL)) < 0) {
95                         fprintf(stderr, "ALSA: snd_mixer_selem_register: %s\n", snd_strerror(err));
96                         snd_mixer_close(hdl->m);
97                         free_hdl(hdl);
98                         return;
99                 }
100
101                 if ((err = snd_mixer_load(hdl->m)) < 0) {
102                         fprintf(stderr, "ALSA: snd_mixer_load: %s\n", snd_strerror(err));
103                         snd_mixer_close(hdl->m);
104                         free_hdl(hdl);
105                         return;
106                 }
107
108                 /* Find the given mixer */
109                 snd_mixer_selem_id_set_index(hdl->sid, mixer_idx);
110                 snd_mixer_selem_id_set_name(hdl->sid, mixer);
111                 if (!(hdl->elem = snd_mixer_find_selem(hdl->m, hdl->sid))) {
112                         fprintf(stderr, "ALSA: Cannot find mixer %s (index %i)\n",
113                                 snd_mixer_selem_id_get_name(hdl->sid), snd_mixer_selem_id_get_index(hdl->sid));
114                         snd_mixer_close(hdl->m);
115                         free_hdl(hdl);
116                         return;
117                 }
118
119                 /* Get the volume range to convert the volume later */
120                 snd_mixer_selem_get_playback_volume_range(hdl->elem, &(hdl->min), &(hdl->max));
121                 TAILQ_INSERT_TAIL(&cached, hdl, handles);
122         }
123
124         long val;
125         snd_mixer_handle_events (hdl->m);
126         snd_mixer_selem_get_playback_volume (hdl->elem, 0, &val);
127         int avg;
128         if (hdl->max != 100) {
129                 float avgf = ((float)val / hdl->max) * 100;
130                 avg = (int)avgf;
131                 avg = (avgf - avg < 0.5 ? avg : (avg+1));
132         } else avg = (int)val;
133
134         /* Check for mute */
135         if (snd_mixer_selem_has_playback_switch(hdl->elem)) {
136                 int pbval;
137                 if ((err = snd_mixer_selem_get_playback_switch(hdl->elem, 0, &pbval)) < 0)
138                         fprintf (stderr, "ALSA: playback_switch: %s\n", snd_strerror(err));
139                 if (!pbval)
140                         avg = 0;
141         }
142
143         const char *walk = fmt;
144         for (; *walk != '\0'; walk++) {
145                 if (*walk != '%') {
146                         putchar(*walk);
147                         continue;
148                 }
149                 if (BEGINS_WITH(walk+1, "volume")) {
150                         printf("%d%%", avg);
151                         walk += strlen("volume");
152                 }
153         }
154 }