+static void update_group_from_button(int i, int on)
+{
+ if (!on)
+ return;
+ if (i >= NUM_GROUPS)
+ return;
+ struct group_config *gc = &group_config[i];
+ struct group_state *gs = &group_state[i];
+ if (!gc->enabled)
+ return;
+
+ calc_groups();
+ if (!gs->have_muted[0] && !gs->have_muted[1])
+ return;
+ uns mute = !gs->have_muted[1];
+
+ CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list)
+ {
+ if (s->noct_group_idx == i)
+ {
+ DBG("@@ Client #%d, sink input #%d: setting mute=%u", s->client_idx, s->idx, mute);
+ pulse_sink_input_set_mute(s->idx, mute);
+ }
+ }
+}
+
+#if 0 // Not used at the moment
+
+static int find_touched_client(void)
+{
+ int touched = -1;
+
+ for (uns i=0; i < NUM_GROUPS; i++)
+ if (group_config[i].enabled && noct_rotary_touched[i])
+ {
+ if (touched >= 0)
+ return -1;
+ touched = i;
+ }
+ return touched;
+}
+
+#endif
+
+/*** Default sink controls ***/
+
+#if 0 // Not mapped to any button at the moment
+
+static const char *get_client_sink(int i)
+{
+ const char *sink = NULL;
+
+ CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list)
+ if (s->noct_group_idx == i)
+ {
+ struct pulse_sink *sk = (s->sink_idx >= 0) ? pulse_sink_by_idx(s->sink_idx) : NULL;
+ const char *ss = sk ? sk->name : NULL;
+ if (!sink)
+ sink = ss;
+ else if (strcmp(sink, ss))
+ sink = "?";
+ }
+ return sink ? : "?";
+}
+
+static void update_default_sink(void)
+{
+ int i = find_touched_client();
+ const char *sink;
+ if (i >= 0)
+ sink = get_client_sink(i);
+ else
+ sink = pulse_default_sink_name ? : "?";
+
+ if (!strcmp(sink, "ursarium"))
+ {
+ noct_set_button(8, 1);
+ noct_set_button(9, 0);
+ noct_set_button(10, 0);
+ }
+ else if (!strcmp(sink, "catarium"))
+ {
+ noct_set_button(8, 0);
+ noct_set_button(9, 1);
+ noct_set_button(10, 0);
+ }
+ else if (!strcmp(sink, "compress"))
+ {
+ noct_set_button(8, 0);
+ noct_set_button(9, 0);
+ noct_set_button(10, 1);
+ }
+ else
+ {
+ noct_set_button(8, 0);
+ noct_set_button(9, 0);
+ noct_set_button(10, 0);
+ }
+}
+
+static void update_default_sink_from_button(int button, int on)
+{
+ if (!on)
+ return;
+
+ int i = find_touched_client();
+ const char *sink;
+ if (i >= 0)
+ sink = get_client_sink(i);
+ else
+ sink = pulse_default_sink_name ? : "?";
+
+ const char *switch_to = NULL;
+ if (button == 8)
+ {
+ if (!strcmp(sink, "ursarium"))
+ switch_to = "burrow";
+ else
+ switch_to = "ursarium";
+ }
+ else if (button == 9)
+ {
+ if (!strcmp(sink, "catarium"))
+ switch_to = "burrow";
+ else
+ switch_to = "catarium";
+ }
+ else if (button == 10)
+ {
+ if (!strcmp(sink, "compress"))
+ switch_to = "burrow";
+ else
+ switch_to = "compress";
+ }
+
+ if (!switch_to)
+ return;
+
+ if (i >= 0)
+ {
+ struct pulse_sink *sk = pulse_sink_by_name(switch_to);
+ if (!sk)
+ return;
+
+ CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list)
+ if (s->noct_group_idx == i)
+ {
+ DBG("Moving input #%d to sink #%d", s->idx, sk->idx);
+ pulse_sink_input_move(s->idx, sk->idx);
+ }
+ }
+ else
+ {
+ DBG("Switching default sink to %s", switch_to);
+ pulse_server_set_default_sink(switch_to);
+ }
+}
+
+#endif
+
+/*** MPD controls ***/
+
+static bool mpd_flash_state;
+
+static void mpd_flash_timeout(struct main_timer *t)
+{
+ mpd_flash_state ^= 1;
+ noct_set_button(12, mpd_flash_state);
+ timer_add_rel(t, 500);
+}
+
+static struct main_timer mpd_flash_timer = {
+ .handler = mpd_flash_timeout,
+};
+
+static void update_mpd(void)
+{
+ const char *state = mpd_get_player_state();
+ if (!strcmp(state, "play"))
+ {
+ noct_set_button(12, 1);
+ timer_del(&mpd_flash_timer);
+ }
+ else if (!strcmp(state, "pause"))
+ {
+ if (!timer_is_active(&mpd_flash_timer))
+ {
+ mpd_flash_state = 1;
+ mpd_flash_timeout(&mpd_flash_timer);
+ }
+ }
+ else
+ {
+ noct_set_button(12, 0);
+ timer_del(&mpd_flash_timer);
+ }
+}
+
+static void mpd_button_timeout(struct main_timer *t)
+{
+ DBG("MPD stop");
+ timer_del(t);
+ mpd_stop();
+}
+
+static void update_mpd_from_button(int button UNUSED, int on)
+{
+ static struct main_timer mpd_button_timer = {
+ .handler = mpd_button_timeout,
+ };
+
+ const char *state = mpd_get_player_state();
+
+ if (!on)
+ {
+ if (timer_is_active(&mpd_button_timer))
+ {
+ timer_del(&mpd_button_timer);
+ if (!strcmp(state, "play"))
+ {
+ DBG("MPD pause");
+ mpd_pause(1);
+ }
+ else if (!strcmp(state, "pause"))
+ {
+ DBG("MPD resume");
+ mpd_pause(0);
+ }
+ }
+ return;
+ }
+
+ if (!strcmp(state, "stop"))
+ {
+ DBG("MPD play");
+ mpd_play();
+ }
+ else
+ {
+ DBG("MPD starting button timer");
+ timer_add_rel(&mpd_button_timer, 1000);
+ }
+}
+
+/*** Main update routines ***/
+
+static struct main_timer update_timer;
+static timestamp_t last_touch_time;
+
+enum update_state {
+ US_OFFLINE,
+ US_ONLINE,
+ US_SLEEPING,
+ US_PULSE_DEAD,
+};
+
+static enum update_state update_state;
+
+static bool want_sleep_p(void)
+{
+ CLIST_FOR_EACH(struct pulse_sink *, s, pulse_sink_list)
+ if (!s->suspended)
+ return 0;
+ return 1;
+}
+