]> git.sur5r.net Git - i3/i3status/blob - src/print_volume.c
add yajl compat code
[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 #include <yajl/yajl_gen.h>
7 #include <yajl/yajl_version.h>
8
9 #ifdef LINUX
10 #include <alsa/asoundlib.h>
11 #include <alloca.h>
12 #endif
13
14 #ifdef __FreeBSD__
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <sys/soundcard.h>
18 #endif
19
20 #include "i3status.h"
21 #include "queue.h"
22
23 #ifdef LINUX
24 struct mixer_hdl {
25         char *device;
26         char *mixer;
27         int mixer_idx;
28         snd_mixer_selem_id_t *sid;
29         snd_mixer_t *m;
30         snd_mixer_elem_t *elem;
31         long min;
32         long max;
33
34         TAILQ_ENTRY(mixer_hdl) handles;
35 };
36
37 TAILQ_HEAD(handles_head, mixer_hdl) cached = TAILQ_HEAD_INITIALIZER(cached);
38
39 static void free_hdl(struct mixer_hdl *hdl) {
40         free(hdl->device);
41         free(hdl->mixer);
42         free(hdl);
43 }
44 #endif
45
46 void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *device, const char *mixer, int mixer_idx) {
47         char *outwalk = buffer;
48
49         /* Printing volume only works with ALSA at the moment */
50         if (output_format == O_I3BAR) {
51                 char *instance;
52                 asprintf(&instance, "%s.%s.%d", device, mixer, mixer_idx);
53                 INSTANCE(instance);
54                 free(instance);
55         }
56 #ifdef LINUX
57         /* Check if we already opened the mixer and get the handle
58          * from cache if so */
59         bool found = false;
60         int err;
61         struct mixer_hdl *hdl;
62         TAILQ_FOREACH(hdl, &cached, handles) {
63                 if (strcmp(hdl->device, device) != 0 ||
64                     strcmp(hdl->mixer, mixer) != 0 ||
65                     hdl->mixer_idx != mixer_idx)
66                         continue;
67                 found = true;
68                 break;
69         }
70
71         if (!found) {
72                 if ((hdl = calloc(sizeof(struct mixer_hdl), 1)) == NULL)
73                         return;
74
75                 if ((hdl->device = strdup(device)) == NULL) {
76                         free(hdl);
77                         return;
78                 }
79
80                 if ((hdl->mixer = strdup(mixer)) == NULL) {
81                         free(hdl->device);
82                         free(hdl);
83                         return;
84                 }
85
86                 hdl->mixer_idx = mixer_idx;
87                 snd_mixer_selem_id_malloc(&(hdl->sid));
88                 if (hdl->sid == NULL) {
89                         free_hdl(hdl);
90                         return;
91                 }
92
93                 if ((err = snd_mixer_open(&(hdl->m), 0)) < 0) {
94                         fprintf(stderr, "ALSA: Cannot open mixer: %s\n", snd_strerror(err));
95                         free_hdl(hdl);
96                         return;
97                 }
98
99                 /* Attach this mixer handle to the given device */
100                 if ((err = snd_mixer_attach(hdl->m, device)) < 0) {
101                         fprintf(stderr, "ALSA: Cannot attach mixer to device: %s\n", snd_strerror(err));
102                         snd_mixer_close(hdl->m);
103                         free_hdl(hdl);
104                         return;
105                 }
106
107                 /* Register this mixer */
108                 if ((err = snd_mixer_selem_register(hdl->m, NULL, NULL)) < 0) {
109                         fprintf(stderr, "ALSA: snd_mixer_selem_register: %s\n", snd_strerror(err));
110                         snd_mixer_close(hdl->m);
111                         free_hdl(hdl);
112                         return;
113                 }
114
115                 if ((err = snd_mixer_load(hdl->m)) < 0) {
116                         fprintf(stderr, "ALSA: snd_mixer_load: %s\n", snd_strerror(err));
117                         snd_mixer_close(hdl->m);
118                         free_hdl(hdl);
119                         return;
120                 }
121
122                 /* Find the given mixer */
123                 snd_mixer_selem_id_set_index(hdl->sid, mixer_idx);
124                 snd_mixer_selem_id_set_name(hdl->sid, mixer);
125                 if (!(hdl->elem = snd_mixer_find_selem(hdl->m, hdl->sid))) {
126                         fprintf(stderr, "ALSA: Cannot find mixer %s (index %i)\n",
127                                 snd_mixer_selem_id_get_name(hdl->sid), snd_mixer_selem_id_get_index(hdl->sid));
128                         snd_mixer_close(hdl->m);
129                         free_hdl(hdl);
130                         return;
131                 }
132
133                 /* Get the volume range to convert the volume later */
134                 snd_mixer_selem_get_playback_volume_range(hdl->elem, &(hdl->min), &(hdl->max));
135                 TAILQ_INSERT_TAIL(&cached, hdl, handles);
136         }
137
138         long val;
139         snd_mixer_handle_events (hdl->m);
140         snd_mixer_selem_get_playback_volume (hdl->elem, 0, &val);
141         int avg;
142         if (hdl->max != 100) {
143                 float avgf = ((float)val / hdl->max) * 100;
144                 avg = (int)avgf;
145                 avg = (avgf - avg < 0.5 ? avg : (avg+1));
146         } else avg = (int)val;
147
148         /* Check for mute */
149         if (snd_mixer_selem_has_playback_switch(hdl->elem)) {
150                 int pbval;
151                 if ((err = snd_mixer_selem_get_playback_switch(hdl->elem, 0, &pbval)) < 0)
152                         fprintf (stderr, "ALSA: playback_switch: %s\n", snd_strerror(err));
153                 if (!pbval)
154                         avg = 0;
155         }
156
157         const char *walk = fmt;
158         for (; *walk != '\0'; walk++) {
159                 if (*walk != '%') {
160                         *(outwalk++) = *walk;
161                         continue;
162                 }
163                 if (BEGINS_WITH(walk+1, "volume")) {
164                         outwalk += sprintf(outwalk, "%d%%", avg);
165                         walk += strlen("volume");
166                 }
167         }
168 #endif
169 #ifdef __FreeBSD__
170         char mixerpath[] = "/dev/mixer";
171         int mixfd, vol, devmask = 0;
172
173         if ((mixfd = open(mixerpath, O_RDWR)) < 0)
174                 return;
175         if (ioctl(mixfd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
176                 return;
177         if (ioctl(mixfd, MIXER_READ(0),&vol) == -1)
178                 return;
179
180         const char *walk = fmt;
181         for (; *walk != '\0'; walk++) {
182                 if (*walk != '%') {
183                         *(outwalk++) = *walk;
184                         continue;
185                 }
186                 if (BEGINS_WITH(walk+1, "volume")) {
187                         outwalk += sprintf(outwalk, "%d%%", vol & 0x7f);
188                         walk += strlen("volume");
189                 }
190         }
191         close(mixfd);
192 #endif
193
194         *outwalk = '\0';
195         OUTPUT_FULL_TEXT(buffer);
196 }