+static uint16_t
+ao_lco_drag_beep_check(uint16_t now, uint16_t delay)
+{
+ PRINTD("beep check count %d delta %d\n",
+ ao_lco_drag_beep_count,
+ (int16_t) (now - ao_lco_drag_beep_time));
+ if (ao_lco_drag_beep_count) {
+ if ((int16_t) (now - ao_lco_drag_beep_time) >= 0) {
+ if (ao_lco_drag_beep_on)
+ ao_lco_drag_beep_stop();
+ else
+ ao_lco_drag_beep_start();
+ }
+ }
+
+ if (ao_lco_drag_beep_count) {
+ if (delay > AO_LCO_DRAG_BEEP_TIME)
+ delay = AO_LCO_DRAG_BEEP_TIME;
+ }
+ return delay;
+}
+
+static void
+ao_lco_drag_enable(void)
+{
+ PRINTD("Drag enable\n");
+ ao_lco_drag_race = 1;
+ memset(ao_lco_selected, 0, sizeof (ao_lco_selected));
+ ao_lco_drag_beep(5);
+ ao_lco_set_display();
+ ao_lco_fire_down = 0;
+}
+
+static void
+ao_lco_drag_disable(void)
+{
+ PRINTD("Drag disable\n");
+ ao_lco_drag_race = 0;
+ memset(ao_lco_selected, 0, sizeof (ao_lco_selected));
+ ao_lco_drag_beep(2);
+ ao_lco_set_display();
+ ao_lco_fire_down = 0;
+}
+
+static uint16_t
+ao_lco_drag_button_check(uint16_t now, uint16_t delay)
+{
+ uint16_t button_delay = ~0;
+
+ /*
+ * Check to see if the button has been held down long enough
+ * to switch in/out of drag race mode
+ */
+ if (ao_lco_fire_down) {
+ if (ao_lco_drag_race) {
+ if ((int16_t) (now - ao_lco_fire_tick) >= AO_LCO_DRAG_RACE_STOP_TIME)
+ ao_lco_drag_disable();
+ else
+ button_delay = ao_lco_fire_tick + AO_LCO_DRAG_RACE_STOP_TIME - now;
+ } else {
+ if ((int16_t) (now - ao_lco_fire_tick) >= AO_LCO_DRAG_RACE_START_TIME)
+ ao_lco_drag_enable();
+ else
+ button_delay = ao_lco_fire_tick + AO_LCO_DRAG_RACE_START_TIME - now;
+ }
+ if (delay > button_delay)
+ delay = button_delay;
+ }
+ return delay;
+}
+
+static uint16_t
+ao_lco_drag_warn_check(uint16_t now, uint16_t delay)
+{
+ uint16_t warn_delay = ~0;
+
+ if (ao_lco_drag_race) {
+ if ((int16_t) (now - ao_lco_drag_warn_time) >= 0) {
+ ao_lco_drag_beep(1);
+ ao_lco_drag_warn_time = now + AO_LCO_DRAG_WARN_TIME;
+ }
+ warn_delay = ao_lco_drag_warn_time - now;
+ }
+ if (delay > warn_delay)
+ delay = warn_delay;
+ return delay;
+}
+
+static void
+ao_lco_drag_monitor(void)
+{
+ uint16_t delay = ~0;
+ uint16_t now;
+
+ for (;;) {
+ PRINTD("Drag monitor active %d delay %d\n", ao_lco_drag_active, delay);
+ if (delay == (uint16_t) ~0)
+ ao_sleep(&ao_lco_drag_active);
+ else
+ ao_sleep_for(&ao_lco_drag_active, delay);
+
+ delay = ~0;
+ if (!ao_lco_drag_active)
+ continue;
+
+ now = ao_time();
+ delay = ao_lco_drag_button_check(now, delay);
+ delay = ao_lco_drag_warn_check(now, delay);
+ delay = ao_lco_drag_beep_check(now, delay);
+
+ /* check to see if there's anything left to do here */
+ if (!ao_lco_fire_down && !ao_lco_drag_race && !ao_lco_drag_beep_count) {
+ delay = ~0;
+ ao_lco_drag_active = 0;
+ }
+ }
+}
+
+static void
+ao_lco_input(void)
+{
+ static struct ao_event event;
+ int8_t dir, new_pad;
+ int16_t new_box;
+
+ ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+ for (;;) {
+ ao_event_get(&event);
+ PRINTD("event type %d unit %d value %d\n",
+ event.type, event.unit, event.value);
+ switch (event.type) {
+ case AO_EVENT_QUADRATURE:
+ switch (event.unit) {
+ case AO_QUADRATURE_PAD:
+ if (!ao_lco_armed) {
+ dir = (int8_t) event.value;
+ new_pad = ao_lco_pad;
+ do {
+ new_pad += dir;
+ if (new_pad > AO_PAD_MAX_CHANNELS)
+ new_pad = 0;
+ if (new_pad < 0)
+ new_pad = AO_PAD_MAX_CHANNELS;
+ if (new_pad == ao_lco_pad)
+ break;
+ } while (!ao_lco_pad_present(ao_lco_box, new_pad));
+ if (new_pad != ao_lco_pad) {
+ ao_lco_pad = new_pad;
+ ao_lco_set_display();
+ }
+ }