]> git.sur5r.net Git - i3/i3status/blob - src/print_volume.c
Added different format string for volume in case it is muted.
[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 #if defined(__FreeBSD__) || defined(__DragonFly__)
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <sys/soundcard.h>
18 #endif
19
20 #ifdef __OpenBSD__
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <soundcard.h>
24 #endif
25
26 #include "i3status.h"
27 #include "queue.h"
28
29 void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *fmt_muted, const char *device, const char *mixer, int mixer_idx) {
30         char *outwalk = buffer;
31         int pbval = 1;
32
33         /* Printing volume only works with ALSA at the moment */
34         if (output_format == O_I3BAR) {
35                 char *instance;
36                 asprintf(&instance, "%s.%s.%d", device, mixer, mixer_idx);
37                 INSTANCE(instance);
38                 free(instance);
39         }
40 #ifdef LINUX
41         int err;
42         snd_mixer_t *m;
43         snd_mixer_selem_id_t *sid;
44         snd_mixer_elem_t *elem;
45         long min, max, val;
46         int avg;
47
48         if ((err = snd_mixer_open(&m, 0)) < 0) {
49                 fprintf(stderr, "i3status: ALSA: Cannot open mixer: %s\n", snd_strerror(err));
50                 goto out;
51         }
52
53         /* Attach this mixer handle to the given device */
54         if ((err = snd_mixer_attach(m, device)) < 0) {
55                 fprintf(stderr, "i3status: ALSA: Cannot attach mixer to device: %s\n", snd_strerror(err));
56                 snd_mixer_close(m);
57                 goto out;
58         }
59
60         /* Register this mixer */
61         if ((err = snd_mixer_selem_register(m, NULL, NULL)) < 0) {
62                 fprintf(stderr, "i3status: ALSA: snd_mixer_selem_register: %s\n", snd_strerror(err));
63                 snd_mixer_close(m);
64                 goto out;
65         }
66
67         if ((err = snd_mixer_load(m)) < 0) {
68                 fprintf(stderr, "i3status: ALSA: snd_mixer_load: %s\n", snd_strerror(err));
69                 snd_mixer_close(m);
70                 goto out;
71         }
72
73         snd_mixer_selem_id_malloc(&sid);
74         if (sid == NULL) {
75                 snd_mixer_close(m);
76                 goto out;
77         }
78
79         /* Find the given mixer */
80         snd_mixer_selem_id_set_index(sid, mixer_idx);
81         snd_mixer_selem_id_set_name(sid, mixer);
82         if (!(elem = snd_mixer_find_selem(m, sid))) {
83                 fprintf(stderr, "i3status: ALSA: Cannot find mixer %s (index %i)\n",
84                         snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
85                 snd_mixer_close(m);
86                 snd_mixer_selem_id_free(sid);
87                 goto out;
88         }
89
90         /* Get the volume range to convert the volume later */
91         snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
92
93         snd_mixer_handle_events (m);
94         snd_mixer_selem_get_playback_volume (elem, 0, &val);
95         if (max != 100) {
96                 float avgf = ((float)val / max) * 100;
97                 avg = (int)avgf;
98                 avg = (avgf - avg < 0.5 ? avg : (avg+1));
99         } else avg = (int)val;
100
101         /* Check for mute */
102         if (snd_mixer_selem_has_playback_switch(elem)) {
103                 if ((err = snd_mixer_selem_get_playback_switch(elem, 0, &pbval)) < 0)
104                         fprintf (stderr, "i3status: ALSA: playback_switch: %s\n", snd_strerror(err));
105                 if (!pbval)  {
106                         START_COLOR("color_degraded");
107                         fmt = fmt_muted;
108                 }
109         }
110
111         snd_mixer_close(m);
112         snd_mixer_selem_id_free(sid);
113
114         const char *walk = fmt;
115         for (; *walk != '\0'; walk++) {
116                 if (*walk != '%') {
117                         *(outwalk++) = *walk;
118                         continue;
119                 }
120                 if (BEGINS_WITH(walk+1, "%")) {
121                         outwalk += sprintf(outwalk, "%%");
122                         walk += strlen("%");
123                 }
124                 if (BEGINS_WITH(walk+1, "volume")) {
125                         outwalk += sprintf(outwalk, "%d%%", avg);
126                         walk += strlen("volume");
127                 }
128         }
129 #endif
130 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
131         char *mixerpath;
132         char defaultmixer[] = "/dev/mixer";
133         int mixfd, vol, devmask = 0;
134         pbval = 1;
135
136         if (mixer_idx > 0)
137                 asprintf(&mixerpath, "/dev/mixer%d", mixer_idx);
138         else
139                 mixerpath = defaultmixer;
140
141         if ((mixfd = open(mixerpath, O_RDWR)) < 0)
142                 return;
143
144         if (mixer_idx > 0)
145                 free(mixerpath);
146
147         if (ioctl(mixfd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
148                 return;
149         if (ioctl(mixfd, MIXER_READ(0),&vol) == -1)
150                 return;
151
152         if (((vol & 0x7f) == 0) && (((vol >> 8) & 0x7f) == 0)) {
153                 START_COLOR("color_degraded");
154                 pbval = 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, "%")) {
164                         outwalk += sprintf(outwalk, "%%");
165                         walk += strlen("%");
166                 }
167                 if (BEGINS_WITH(walk+1, "volume")) {
168                         outwalk += sprintf(outwalk, "%d%%", vol & 0x7f);
169                         walk += strlen("volume");
170                 }
171         }
172         close(mixfd);
173 #endif
174
175 out:
176         *outwalk = '\0';
177         OUTPUT_FULL_TEXT(buffer);
178
179         if (!pbval)
180                 END_COLOR;
181 }