pulse-listener.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. #include "pulse-listener.h"
  2. #include <pulse/pulseaudio.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <errno.h>
  6. #include <signal.h>
  7. #include <limits.h>
  8. #define ERROR(...) fprintf(stderr, "error: " __VA_ARGS__)
  9. #ifdef __OpenBSD__
  10. #define SIGMAX (SIGUSR2 - 1)
  11. #define SIGMIN (SIGUSR1 - 1)
  12. #else
  13. #define SIGMAX SIGRTMAX
  14. #define SIGMIN SIGRTMIN
  15. #endif
  16. static void sink_info_callback(pa_context *ctx, const pa_sink_info *info,
  17. int eol, pthread_t *main_thread) {
  18. pthread_kill(*main_thread, SIGMIN + 1);
  19. }
  20. static void server_info_callback(pa_context *ctx, const pa_server_info *info,
  21. pthread_t *main_thread) {
  22. pa_operation *op = pa_context_get_sink_info_by_name(
  23. ctx,
  24. info->default_sink_name,
  25. (pa_sink_info_cb_t) sink_info_callback,
  26. main_thread);
  27. pa_operation_unref(op);
  28. }
  29. static void subscribe_callback(pa_context *ctx,
  30. pa_subscription_event_type_t type, uint32_t idx,
  31. pthread_t *main_thread) {
  32. if ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
  33. pa_operation *op = pa_context_get_sink_info_by_index(ctx,
  34. idx,
  35. (pa_sink_info_cb_t) sink_info_callback,
  36. main_thread);
  37. pa_operation_unref(op);
  38. }
  39. }
  40. static void state_callback(pa_context *ctx, pthread_t *main_thread) {
  41. pa_operation *op;
  42. switch (pa_context_get_state(ctx)) {
  43. case PA_CONTEXT_READY:
  44. op = pa_context_get_server_info(ctx,
  45. (pa_server_info_cb_t) server_info_callback,
  46. main_thread);
  47. pa_operation_unref(op);
  48. pa_context_set_subscribe_callback(ctx,
  49. (pa_context_subscribe_cb_t) subscribe_callback,
  50. main_thread);
  51. op = pa_context_subscribe(ctx, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL);
  52. pa_operation_unref(op);
  53. break;
  54. case PA_CONTEXT_FAILED:
  55. pthread_kill(*main_thread, SIGMIN + 1);
  56. break;
  57. default:
  58. break;
  59. }
  60. }
  61. static void cleanup(void *data_to_free[2]) {
  62. pa_context_unref(data_to_free[1]);
  63. pa_mainloop_free(data_to_free[0]);
  64. }
  65. void pulse_listener_main(pthread_t *main_thread) {
  66. pthread_setcanceltype(PTHREAD_CANCEL_DISABLE, NULL);
  67. pa_mainloop *mainloop = pa_mainloop_new();
  68. if (!mainloop) {
  69. ERROR("could not create pulse mainloop\n");
  70. return;
  71. }
  72. pa_mainloop_api *api = pa_mainloop_get_api(mainloop);
  73. if (!api) {
  74. ERROR("could not create pulse mainloop api\n");
  75. return;
  76. }
  77. pa_context *context = pa_context_new(api, "dwmblocks-listener");
  78. if (!context) {
  79. ERROR("could not create pulse context\n");
  80. return;
  81. }
  82. pa_context_set_state_callback(context,
  83. (pa_context_notify_cb_t)&state_callback,
  84. main_thread);
  85. if (pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) {
  86. ERROR("could not connect to pulse\n");
  87. return;
  88. }
  89. void *data_to_free[2] = { mainloop, context };
  90. pthread_cleanup_push((void(*)(void *)) cleanup, data_to_free);
  91. pthread_setcanceltype(PTHREAD_CANCEL_ENABLE, NULL);
  92. pa_mainloop_run(mainloop, NULL);
  93. pthread_cleanup_pop(1);
  94. }