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