volume.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /* See LICENSE file for copyright and license details. */
  2. #include <fcntl.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <sys/ioctl.h>
  6. #include <unistd.h>
  7. #include "../slstatus.h"
  8. #include "../util.h"
  9. #if defined(__OpenBSD__) | defined(__FreeBSD__)
  10. #include <poll.h>
  11. #include <sndio.h>
  12. #include <stdlib.h>
  13. #include <sys/queue.h>
  14. struct control {
  15. LIST_ENTRY(control) next;
  16. unsigned int addr;
  17. #define CTRL_NONE 0
  18. #define CTRL_LEVEL 1
  19. #define CTRL_MUTE 2
  20. unsigned int type;
  21. unsigned int maxval;
  22. unsigned int val;
  23. };
  24. static LIST_HEAD(, control) controls = LIST_HEAD_INITIALIZER(controls);
  25. static struct pollfd *pfds;
  26. static struct sioctl_hdl *hdl;
  27. static int initialized;
  28. /*
  29. * Call-back to obtain the description of all audio controls.
  30. */
  31. static void
  32. ondesc(void *unused, struct sioctl_desc *desc, int val)
  33. {
  34. struct control *c, *ctmp;
  35. unsigned int type = CTRL_NONE;
  36. if (desc == NULL)
  37. return;
  38. /* Delete existing audio control with the same address. */
  39. LIST_FOREACH_SAFE(c, &controls, next, ctmp) {
  40. if (desc->addr == c->addr) {
  41. LIST_REMOVE(c, next);
  42. free(c);
  43. break;
  44. }
  45. }
  46. /* Only match output.level and output.mute audio controls. */
  47. if (desc->group[0] != 0 ||
  48. strcmp(desc->node0.name, "output") != 0)
  49. return;
  50. if (desc->type == SIOCTL_NUM &&
  51. strcmp(desc->func, "level") == 0)
  52. type = CTRL_LEVEL;
  53. else if (desc->type == SIOCTL_SW &&
  54. strcmp(desc->func, "mute") == 0)
  55. type = CTRL_MUTE;
  56. else
  57. return;
  58. c = malloc(sizeof(struct control));
  59. if (c == NULL) {
  60. warn("sndio: failed to allocate audio control\n");
  61. return;
  62. }
  63. c->addr = desc->addr;
  64. c->type = type;
  65. c->maxval = desc->maxval;
  66. c->val = val;
  67. LIST_INSERT_HEAD(&controls, c, next);
  68. }
  69. /*
  70. * Call-back invoked whenever an audio control changes.
  71. */
  72. static void
  73. onval(void *unused, unsigned int addr, unsigned int val)
  74. {
  75. struct control *c;
  76. LIST_FOREACH(c, &controls, next) {
  77. if (c->addr == addr)
  78. break;
  79. }
  80. c->val = val;
  81. }
  82. static void
  83. cleanup(void)
  84. {
  85. struct control *c;
  86. if (hdl) {
  87. sioctl_close(hdl);
  88. hdl = NULL;
  89. }
  90. free(pfds);
  91. pfds = NULL;
  92. while (!LIST_EMPTY(&controls)) {
  93. c = LIST_FIRST(&controls);
  94. LIST_REMOVE(c, next);
  95. free(c);
  96. }
  97. }
  98. static int
  99. init(void)
  100. {
  101. hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
  102. if (hdl == NULL) {
  103. warn("sndio: cannot open device");
  104. goto failed;
  105. }
  106. if (!sioctl_ondesc(hdl, ondesc, NULL)) {
  107. warn("sndio: cannot set control description call-back");
  108. goto failed;
  109. }
  110. if (!sioctl_onval(hdl, onval, NULL)) {
  111. warn("sndio: cannot set control values call-back");
  112. goto failed;
  113. }
  114. pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd));
  115. if (pfds == NULL) {
  116. warn("sndio: cannot allocate pollfd structures");
  117. goto failed;
  118. }
  119. return 1;
  120. failed:
  121. cleanup();
  122. return 0;
  123. }
  124. const char *
  125. vol_perc(const char *unused)
  126. {
  127. struct control *c;
  128. int n, v, value;
  129. if (!initialized)
  130. initialized = init();
  131. if (hdl == NULL)
  132. return NULL;
  133. n = sioctl_pollfd(hdl, pfds, POLLIN);
  134. if (n > 0) {
  135. n = poll(pfds, n, 0);
  136. if (n > 0) {
  137. if (sioctl_revents(hdl, pfds) & POLLHUP) {
  138. warn("sndio: disconnected");
  139. cleanup();
  140. initialized = 0;
  141. return NULL;
  142. }
  143. }
  144. }
  145. value = 100;
  146. LIST_FOREACH(c, &controls, next) {
  147. if (c->type == CTRL_MUTE && c->val == 1)
  148. value = 0;
  149. else if (c->type == CTRL_LEVEL) {
  150. v = (c->val * 100 + c->maxval / 2) / c->maxval;
  151. /* For multiple channels return the minimum. */
  152. if (v < value)
  153. value = v;
  154. }
  155. }
  156. return bprintf("%d", value);
  157. }
  158. #else
  159. #include <sys/soundcard.h>
  160. const char *
  161. vol_perc(const char *card)
  162. {
  163. size_t i;
  164. int v, afd, devmask;
  165. char *vnames[] = SOUND_DEVICE_NAMES;
  166. if ((afd = open(card, O_RDONLY | O_NONBLOCK)) < 0) {
  167. warn("open '%s':", card);
  168. return NULL;
  169. }
  170. if (ioctl(afd, (int)SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
  171. warn("ioctl 'SOUND_MIXER_READ_DEVMASK':");
  172. close(afd);
  173. return NULL;
  174. }
  175. for (i = 0; i < LEN(vnames); i++) {
  176. if (devmask & (1 << i) && !strcmp("vol", vnames[i])) {
  177. if (ioctl(afd, MIXER_READ(i), &v) < 0) {
  178. warn("ioctl 'MIXER_READ(%ld)':", i);
  179. close(afd);
  180. return NULL;
  181. }
  182. }
  183. }
  184. close(afd);
  185. return bprintf("%d", v & 0xff);
  186. }
  187. #endif