コード例 #1
0
ファイル: keymap.c プロジェクト: UnderSampled/qmk_firmware
void matrix_scan_keymap(void) {  // runs frequently to update info
  uint8_t modifiers = get_mods();
  uint8_t led_usb_state = host_keyboard_leds();
  uint8_t one_shot = get_oneshot_mods();

  if (!skip_leds) {
    ergodox_board_led_off();
    ergodox_right_led_1_off();
    ergodox_right_led_2_off();
    ergodox_right_led_3_off();

    // Since we're not using the LEDs here for layer indication anymore,
    // then lets use them for modifier indicators.  Shame we don't have 4...
    // Also, no "else", since we want to know each, independently.
    if ( ( modifiers | one_shot ) & MOD_MASK_SHIFT || led_usb_state & (1<<USB_LED_CAPS_LOCK) ) {
      ergodox_right_led_2_on();
      ergodox_right_led_2_set( 50 );
    }
    if ( ( modifiers | one_shot ) & MOD_MASK_CTRL) {
      ergodox_right_led_1_on();
      ergodox_right_led_1_set( 10 );
    }
    if ( ( modifiers | one_shot ) & MOD_MASK_ALT) {
      ergodox_right_led_3_on();
      ergodox_right_led_3_set( 10 );
    }

  }

};
コード例 #2
0
bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
  uint16_t idx = keycode - QK_TAP_DANCE;
  qk_tap_dance_action_t *action;

  if (last_td && last_td != keycode) {
    (&tap_dance_actions[last_td - QK_TAP_DANCE])->state.interrupted = true;
  }

  switch(keycode) {
  case QK_TAP_DANCE ... QK_TAP_DANCE_MAX:
    if ((int16_t)idx > highest_td)
      highest_td = idx;
    action = &tap_dance_actions[idx];

    action->state.pressed = record->event.pressed;
    if (record->event.pressed) {
      action->state.keycode = keycode;
      action->state.count++;
      action->state.timer = timer_read();
      action->state.oneshot_mods = get_oneshot_mods();
      action->state.weak_mods = get_mods();
      action->state.weak_mods |= get_weak_mods();
      process_tap_dance_action_on_each_tap (action);

      if (last_td && last_td != keycode) {
        qk_tap_dance_action_t *paction = &tap_dance_actions[last_td - QK_TAP_DANCE];
        paction->state.interrupted = true;
        process_tap_dance_action_on_dance_finished (paction);
        reset_tap_dance (&paction->state);
      }

      last_td = keycode;
    }

    break;

  default:
    if (!record->event.pressed)
      return true;

    if (highest_td == -1)
      return true;

    for (int i = 0; i <= highest_td; i++) {
      action = &tap_dance_actions[i];
      if (action->state.count == 0)
        continue;
      action->state.interrupted = true;
      process_tap_dance_action_on_dance_finished (action);
      reset_tap_dance (&action->state);
    }
    break;
  }

  return true;
}
コード例 #3
0
ファイル: rgb_stuff.c プロジェクト: bellwood4486/qmk_firmware
void matrix_scan_indicator(void) {
  current_mod = get_mods();
  current_led = host_keyboard_leds();
  current_osm = get_oneshot_mods();

  set_rgb_indicators(current_mod, current_led, current_osm);

  last_mod = current_mod;
  last_led = current_led;
  last_osm = current_osm;

}
コード例 #4
0
ファイル: keymap.c プロジェクト: GrandpaGhostTim/qmk_firmware
// Runs constantly in the background, in a loop.
void matrix_scan_user(void) {
  uint8_t layer = biton32(layer_state);

  ergodox_led_all_off();
  ergodox_led_all_set(LED_BRIGHTNESS_LO);

  switch (layer) {
  case BASE:
    current_layer = BASE;
    break;
  case KEYPAD:
    current_layer = KEYPAD;
    break;
  default:
    // none
    break;
  }

  // layer leds
  if (current_layer == KEYPAD) {
    ergodox_right_led_3_on();
  }

  // capslock
  if (host_keyboard_leds() & (3<<USB_LED_CAPS_LOCK)) {
    ergodox_right_led_1_on();
  }

  // Temporary leds

  // The function layer takes over other layers and we need to reflect that on the leds.
  // If the current layer is the BASE, we simply turn on the FN led, but if the current
  // layer is the KEYPAD, than we must turn it off before turning on the FN led.
  if (layer == FN && !has_oneshot_layer_timed_out()) {
    ergodox_right_led_3_off();
    ergodox_right_led_2_on();
  }

  // if the shifted is pressed I show the case led in a brighter color. This is nice to
  // differenciate the shift from the capslock.
  // Notice that I make sure that we're not using the shift on a chord shortcut (pressing
  // shift togather with other modifiers).
  if((keyboard_report->mods & (MOD_BIT(KC_LSFT) | MOD_BIT(KC_RSFT)) &&                                 // is shift pressed and there is no other
      !(keyboard_report->mods & (~MOD_BIT(KC_LSFT) & ~MOD_BIT(KC_RSFT)))) ||                           //    modifier being pressed as well
     (get_oneshot_mods() & (MOD_BIT(KC_LSFT) | MOD_BIT(KC_RSFT)) && !has_oneshot_mods_timed_out())) {  // or the one shot shift didn't timed out
    ergodox_right_led_1_set(LED_BRIGHTNESS_HI);
    ergodox_right_led_1_on();
  }
};
コード例 #5
0
ファイル: keymap.c プロジェクト: 20lives/qmk_firmware
uint32_t layer_state_set_keymap (uint32_t state) {
  uint8_t modifiders = get_mods();
  uint8_t led_usb_state = host_keyboard_leds();
  uint8_t one_shot = get_oneshot_mods();


  if (modifiders & MODS_SHIFT_MASK || led_usb_state & (1<<USB_LED_CAPS_LOCK) || one_shot & MODS_SHIFT_MASK) {
    rgblight_sethsv_at(0, 255, 255, 5);
    rgblight_sethsv_at(0, 255, 255, 10);
  }
  if (modifiders & MODS_CTRL_MASK || one_shot & MODS_CTRL_MASK) {
    rgblight_sethsv_at(51, 255, 255, 6);
    rgblight_sethsv_at(51, 255, 255, 9);
  }
  if (modifiders & MODS_ALT_MASK || one_shot & MODS_ALT_MASK) {
    rgblight_sethsv_at(120, 255, 255, 7);
    rgblight_sethsv_at(120, 255, 255, 8);
  }

  return state;
}
コード例 #6
0
ファイル: keymap.c プロジェクト: 20lives/qmk_firmware
void matrix_scan_keymap (void) {
  static uint8_t current_mods;
  static uint8_t current_host_leds;
  static uint8_t current_oneshot_mods;
  static bool has_status_changed = true;

  if ( current_mods != get_mods() || current_host_leds != host_keyboard_leds() || current_oneshot_mods != get_oneshot_mods()) {
    has_status_changed = true;
    current_mods = get_mods();
    current_host_leds = host_keyboard_leds();
    current_oneshot_mods = get_oneshot_mods();
  }
  if (has_status_changed) {
    has_status_changed = false;

    if (current_mods & MODS_SHIFT_MASK || current_host_leds & (1<<USB_LED_CAPS_LOCK) || current_oneshot_mods & MODS_SHIFT_MASK) {
      rgblight_sethsv_at(0, 255, 255, 5);
      rgblight_sethsv_at(0, 255, 255, 10);
    } else {
      rgblight_sethsv_default_helper(5);
      rgblight_sethsv_default_helper(10);
    }
    if (current_mods & MODS_CTRL_MASK || current_oneshot_mods & MODS_CTRL_MASK) {
      rgblight_sethsv_at(51, 255, 255, 6);
      rgblight_sethsv_at(51, 255, 255, 9);
    } else {
      rgblight_sethsv_default_helper(6);
      rgblight_sethsv_default_helper(9);
    }
    if (current_mods & MODS_GUI_MASK || current_oneshot_mods & MODS_GUI_MASK) {
      rgblight_sethsv_at(120, 255, 255, 7);
      rgblight_sethsv_at(120, 255, 255, 8);
    } else {
      rgblight_sethsv_default_helper(7);
      rgblight_sethsv_default_helper(8);

    }
  }
}
コード例 #7
0
ファイル: rgb_stuff.c プロジェクト: bellwood4486/qmk_firmware
void matrix_init_rgb(void) {
#ifdef INDICATOR_LIGHTS
  current_mod = last_mod = get_mods();
  current_led = last_led = host_keyboard_leds();
  current_osm = last_osm = get_oneshot_mods();
#endif

  if (userspace_config.rgb_layer_change) {
    rgblight_enable_noeeprom();
    switch (biton32(eeconfig_read_default_layer())) {
      case _COLEMAK:
        rgblight_sethsv_noeeprom_magenta(); break;
      case _DVORAK:
        rgblight_sethsv_noeeprom_springgreen(); break;
      case _WORKMAN:
        rgblight_sethsv_noeeprom_goldenrod(); break;
      default:
        rgblight_sethsv_noeeprom_cyan(); break;
    }
    rgblight_mode_noeeprom(1);
  }
}
コード例 #8
0
ファイル: keymap.c プロジェクト: berfarah/qmk_firmware
void matrix_scan_user(void)
{

  uint8_t layer = biton32(layer_state);

  if (keyboard_report->mods & MOD_BIT(KC_LSFT) ||
      ((get_oneshot_mods() & MOD_BIT(KC_LSFT)) &&
       !has_oneshot_mods_timed_out()))
  {
    ergodox_right_led_1_set(LED_BRIGHTNESS_HI);
    ergodox_right_led_1_on();
  }
  else if (layer == RAISE || layer == AUX)
  {
    ergodox_right_led_1_set(LED_BRIGHTNESS_LO);
    ergodox_right_led_1_on();
  }
  else
  {
    ergodox_right_led_1_off();
  }

  if (keyboard_report->mods & MOD_BIT(KC_LCTL) ||
      ((get_oneshot_mods() & MOD_BIT(KC_LCTL)) &&
       !has_oneshot_mods_timed_out()))
  {
    ergodox_right_led_2_set(LED_BRIGHTNESS_HI);
    ergodox_right_led_2_on();
  }
  else if (layer == LOWER || layer == AUX)
  {
    ergodox_right_led_2_set(LED_BRIGHTNESS_LO);
    ergodox_right_led_2_on();
  }
  else
  {
    ergodox_right_led_2_off();
  }

  if (keyboard_report->mods & MOD_BIT(KC_LALT) ||
      ((get_oneshot_mods() & MOD_BIT(KC_LALT)) &&
       !has_oneshot_mods_timed_out()))
  {
    ergodox_right_led_3_set(LED_BRIGHTNESS_HI);
    ergodox_right_led_3_on();
  }
  else if (layer == COLE || layer == AUX)
  {
    ergodox_right_led_3_set(LED_BRIGHTNESS_LO);
    ergodox_right_led_3_on();
  }
  else
  {
    ergodox_right_led_3_off();
  }

  LEADER_DICTIONARY()
  {
    leading = false;
    leader_end();

    SEQ_THREE_KEYS(KC_W, KC_I, KC_N) { os_type = OS_WIN; };
    SEQ_THREE_KEYS(KC_O, KC_S, KC_X) { os_type = OS_OSX; };
    SEQ_THREE_KEYS(KC_L, KC_I, KC_N) { os_type = OS_LIN; };

    SEQ_ONE_KEY(KC_A)
    {
      switch (os_type)
      {
      case OS_WIN:
        tap(KC_NLCK);
        register_code(KC_RALT);
        tap(KC_KP_0);
        tap(KC_KP_2);
        tap(KC_KP_2);
        tap(KC_KP_8);
        unregister_code(KC_RALT);
        tap(KC_NLCK);
        break;
      case OS_OSX:
        register_code(KC_RALT);
        register_code(KC_RSFT);
        register_code(KC_SCLN);
        unregister_code(KC_SCLN);
        unregister_code(KC_RSFT);
        unregister_code(KC_RALT);
        tap(KC_A);
        break;
      case OS_LIN:
        tap(KC_RALT);
        tap(KC_DQT);
        tap(KC_A);
        break;
      }
    }
    SEQ_TWO_KEYS(KC_A, KC_A)
    {
      switch (os_type)
      {
      case OS_WIN:
        tap(KC_NLCK);
        register_code(KC_RALT);
        tap(KC_KP_0);
        tap(KC_KP_1);
        tap(KC_KP_9);
        tap(KC_KP_6);
        unregister_code(KC_RALT);
        tap(KC_NLCK);
        break;
      case OS_OSX:
        register_code(KC_RALT);
        register_code(KC_RSFT);
        register_code(KC_SCLN);
        unregister_code(KC_SCLN);
        unregister_code(KC_RSFT);
        unregister_code(KC_RALT);
        register_code(KC_LSFT);
        register_code(KC_A);
        unregister_code(KC_A);
        unregister_code(KC_LSFT);
        break;
      case OS_LIN:
        tap(KC_RALT);
        tap(KC_DQT);
        register_code(KC_LSFT);
        register_code(KC_A);
        unregister_code(KC_A);
        unregister_code(KC_LSFT);
        break;
      }
    }
    SEQ_ONE_KEY(KC_O)
    {
      switch (os_type)
      {
      case OS_WIN:
        tap(KC_NLCK);
        register_code(KC_RALT);
        tap(KC_KP_0);
        tap(KC_KP_2);
        tap(KC_KP_4);
        tap(KC_KP_6);
        unregister_code(KC_RALT);
        tap(KC_NLCK);
        break;
      case OS_OSX:
        register_code(KC_RALT);
        register_code(KC_RSFT);
        register_code(KC_SCLN);
        unregister_code(KC_SCLN);
        unregister_code(KC_RSFT);
        unregister_code(KC_RALT);
        tap(KC_O);
        break;
      case OS_LIN:
        tap(KC_RALT);
        tap(KC_DQT);
        tap(KC_O);
        break;
      }
    }
    SEQ_TWO_KEYS(KC_O, KC_O)
    {
      switch (os_type)
      {
      case OS_WIN:
        tap(KC_NLCK);
        register_code(KC_RALT);
        tap(KC_KP_0);
        tap(KC_KP_2);
        tap(KC_KP_1);
        tap(KC_KP_4);
        unregister_code(KC_RALT);
        tap(KC_NLCK);
        break;
      case OS_OSX:
        register_code(KC_RALT);
        register_code(KC_RSFT);
        register_code(KC_SCLN);
        unregister_code(KC_SCLN);
        unregister_code(KC_RSFT);
        unregister_code(KC_RALT);
        tap(LSFT(KC_O));
        break;
      case OS_LIN:
        tap(KC_RALT);
        tap(KC_DQT);
        register_code(KC_LSFT);
        register_code(KC_O);
        unregister_code(KC_O);
        unregister_code(KC_LSFT);
        break;
      }
    }
    SEQ_ONE_KEY(KC_U)
    {
      switch (os_type)
      {
      case OS_WIN:
        tap(KC_NLCK);
        register_code(KC_RALT);
        tap(KC_KP_0);
        tap(KC_KP_2);
        tap(KC_KP_5);
        tap(KC_KP_2);
        unregister_code(KC_RALT);
        tap(KC_NLCK);
        break;
      case OS_OSX:
        register_code(KC_RALT);
        register_code(KC_RSFT);
        register_code(KC_SCLN);
        unregister_code(KC_SCLN);
        unregister_code(KC_RSFT);
        unregister_code(KC_RALT);
        tap(KC_U);
        break;
      case OS_LIN:
        tap(KC_RALT);
        tap(KC_DQT);
        tap(KC_U);
        break;
      }
    }
    SEQ_TWO_KEYS(KC_U, KC_U)
    {
      switch (os_type)
      {
      case OS_WIN:
        tap(KC_NLCK);
        register_code(KC_RALT);
        tap(KC_KP_0);
        tap(KC_KP_2);
        tap(KC_KP_2);
        tap(KC_KP_0);
        unregister_code(KC_RALT);
        tap(KC_NLCK);
        break;
      case OS_OSX:
        register_code(KC_RALT);
        register_code(KC_RSFT);
        register_code(KC_SCLN);
        unregister_code(KC_SCLN);
        unregister_code(KC_RSFT);
        unregister_code(KC_RALT);
        tap(LSFT(KC_U));
        break;
      case OS_LIN:
        tap(KC_RALT);
        tap(KC_DQT);
        register_code(KC_LSFT);
        register_code(KC_U);
        unregister_code(KC_U);
        unregister_code(KC_LSFT);
        break;
      }
    }
    SEQ_ONE_KEY(KC_S)
    {
      switch (os_type)
      {
      case OS_WIN:
        tap(KC_NLCK);
        register_code(KC_RALT);
        tap(KC_KP_0);
        tap(KC_KP_2);
        tap(KC_KP_2);
        tap(KC_KP_3);
        unregister_code(KC_RALT);
        tap(KC_NLCK);
        break;
      case OS_OSX:
        register_code(KC_RALT);
        tap(KC_S);
        unregister_code(KC_RALT);
        break;
      case OS_LIN:
        tap(KC_RALT);
        tap(KC_S);
        tap(KC_S);
        break;
      }
    }
  }
}
コード例 #9
0
ファイル: rgb_stuff.c プロジェクト: UnderSampled/qmk_firmware
void matrix_scan_indicator(void) {
  if (has_initialized) {
    set_rgb_indicators(get_mods(), host_keyboard_leds(), get_oneshot_mods());
  }
}
コード例 #10
0
ファイル: keymap.c プロジェクト: muzfuz/qmk_firmware
void render_status(void) {

    oled_write_P(PSTR("Layer"), false);
    switch (biton32(layer_state)) {
        case 0:
            oled_write_P(PSTR("Base "), false);
            break;
        case _RAISE:
            oled_write_P(PSTR("Raise"), false);
            break;
        case _LOWER:
            oled_write_P(PSTR("Lower"), false);
            break;
        case _ADJUST:
            oled_write_P(PSTR("Adjst"), false);
            break;
        default:
            oled_write_P(PSTR("Unkn "), false);
            break;
    }
    oled_write_P(PSTR("Lyout"), false);
    switch (biton32(default_layer_state)) {
        case _QWERTY:
            oled_write_P(PSTR("QWRTY"), false);
            break;
        case _COLEMAK:
            oled_write_P(PSTR("COLMK"), false);
            break;
        case _DVORAK:
            oled_write_P(PSTR("DVRAK"), false);
            break;
        case _WORKMAN:
            oled_write_P(PSTR("WRKMN"), false);
            break;
        case _NORMAN:
            oled_write_P(PSTR("NORMN"), false);
            break;
        case _MALTRON:
            oled_write_P(PSTR("MLTRN"), false);
            break;
        case _EUCALYN:
            oled_write_P(PSTR("ECLYN"), false);
            break;
        case _CARPLAX:
            oled_write_P(PSTR("CRPLX"), false);
            break;
    }

    uint8_t modifiers = get_mods();
    uint8_t one_shot = get_oneshot_mods();

    oled_write_P(PSTR("Mods:"), false);
    oled_write_P( (modifiers & MOD_MASK_SHIFT || one_shot & MOD_MASK_SHIFT) ? PSTR(" SFT ") : PSTR("     "), false);
    oled_write_P( (modifiers & MOD_MASK_CTRL  || one_shot & MOD_MASK_CTRL ) ? PSTR(" CTL ") : PSTR("     "), false);
    oled_write_P( (modifiers & MOD_MASK_ALT   || one_shot & MOD_MASK_ALT  ) ? PSTR(" ALT ") : PSTR("     "), false);
    oled_write_P( (modifiers & MOD_MASK_GUI   || one_shot & MOD_MASK_GUI  ) ? PSTR(" GUI ") : PSTR("     "), false);


    oled_write_P(PSTR("BTMGK"), false);

    if (keymap_config.swap_lalt_lgui) {
        oled_write_P(PSTR(" Mac "), false);
    } else {
        oled_write_P(PSTR(" Win "), false);
    }

    uint8_t led_usb_state = host_keyboard_leds();
    oled_write_P(PSTR("Lock:"), false);
    oled_write_P(led_usb_state & (1<<USB_LED_NUM_LOCK)    ? PSTR(" NUM ") : PSTR("     "), false);
    oled_write_P(led_usb_state & (1<<USB_LED_CAPS_LOCK)   ? PSTR(" CAPS") : PSTR("     "), false);
    oled_write_P(led_usb_state & (1<<USB_LED_SCROLL_LOCK) ? PSTR(" SCRL") : PSTR("     "), false);

    oled_write(keylog_str, false);
}
コード例 #11
0
ファイル: visualizer.c プロジェクト: Ictinike/qmk_firmware
static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
    (void)arg;

    GListener event_listener;
    geventListenerInit(&event_listener);
    geventAttachSource(&event_listener, (GSourceHandle)&current_status, 0);

    visualizer_keyboard_status_t initial_status = {
        .default_layer = 0xFFFFFFFF,
        .layer = 0xFFFFFFFF,
        .mods = 0xFF,
        .leds = 0xFFFFFFFF,
        .suspended = false,
    };

    visualizer_state_t state = {
        .status = initial_status,
        .current_lcd_color = 0,
#ifdef LCD_ENABLE
        .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
        .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
#endif
    };
    initialize_user_visualizer(&state);
    state.prev_lcd_color = state.current_lcd_color;

#ifdef LCD_BACKLIGHT_ENABLE
    lcd_backlight_color(
            LCD_HUE(state.current_lcd_color),
            LCD_SAT(state.current_lcd_color),
            LCD_INT(state.current_lcd_color));
#endif

    systemticks_t sleep_time = TIME_INFINITE;
    systemticks_t current_time = gfxSystemTicks();

    while(true) {
        systemticks_t new_time = gfxSystemTicks();
        systemticks_t delta = new_time - current_time;
        current_time = new_time;
        bool enabled = visualizer_enabled;
        if (!same_status(&state.status, &current_status)) {
            if (visualizer_enabled) {
                if (current_status.suspended) {
                    stop_all_keyframe_animations();
                    visualizer_enabled = false;
                    state.status = current_status;
                    user_visualizer_suspend(&state);
                }
                else {
                    state.status = current_status;
                    update_user_visualizer_state(&state);
                }
                state.prev_lcd_color = state.current_lcd_color;
            }
        }
        if (!enabled && state.status.suspended && current_status.suspended == false) {
            // Setting the status to the initial status will force an update
            // when the visualizer is enabled again
            state.status = initial_status;
            state.status.suspended = false;
            stop_all_keyframe_animations();
            user_visualizer_resume(&state);
            state.prev_lcd_color = state.current_lcd_color;
        }
        sleep_time = TIME_INFINITE;
        for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
            if (animations[i]) {
                update_keyframe_animation(animations[i], &state, delta, &sleep_time);
            }
        }
#ifdef LED_ENABLE
        gdispGFlush(LED_DISPLAY);
#endif

#ifdef EMULATOR
        draw_emulator();
#endif
        // The animation can enable the visualizer
        // And we might need to update the state when that happens
        // so don't sleep
        if (enabled != visualizer_enabled) {
            sleep_time = 0;
        }

        systemticks_t after_update = gfxSystemTicks();
        unsigned update_delta = after_update - current_time;
        if (sleep_time != TIME_INFINITE) {
            if (sleep_time > update_delta) {
                sleep_time -= update_delta;
            }
            else {
                sleep_time = 0;
            }
        }
        dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
#ifdef PROTOCOL_CHIBIOS
        // The gEventWait function really takes milliseconds, even if the documentation says ticks.
        // Unfortunately there's no generic ugfx conversion from system time to milliseconds,
        // so let's do it in a platform dependent way.

        // On windows the system ticks is the same as milliseconds anyway
        if (sleep_time != TIME_INFINITE) {
            sleep_time = ST2MS(sleep_time);
        }
#endif
        geventEventWait(&event_listener, sleep_time);
    }
#ifdef LCD_ENABLE
    gdispCloseFont(state.font_fixed5x8);
    gdispCloseFont(state.font_dejavusansbold12);
#endif

    return 0;
}

void visualizer_init(void) {
    gfxInit();

#ifdef LCD_BACKLIGHT_ENABLE
    lcd_backlight_init();
#endif

#ifdef SERIAL_LINK_ENABLE
    add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) );
#endif

#ifdef LCD_ENABLE
    LCD_DISPLAY = get_lcd_display();
#endif
#ifdef LED_ENABLE
    LED_DISPLAY = get_led_display();
#endif

    // We are using a low priority thread, the idea is to have it run only
    // when the main thread is sleeping during the matrix scanning
    gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack),
                              VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL);
}

void update_status(bool changed) {
    if (changed) {
        GSourceListener* listener = geventGetSourceListener((GSourceHandle)&current_status, NULL);
        if (listener) {
            geventSendEvent(listener);
        }
    }
#ifdef SERIAL_LINK_ENABLE
    static systime_t last_update = 0;
    systime_t current_update = chVTGetSystemTimeX();
    systime_t delta = current_update - last_update;
    if (changed || delta > MS2ST(10)) {
        last_update = current_update;
        visualizer_keyboard_status_t* r = begin_write_current_status();
        *r = current_status;
        end_write_current_status();
    }
#endif
}

uint8_t visualizer_get_mods() {
  uint8_t mods = get_mods();

#ifndef NO_ACTION_ONESHOT
  if (!has_oneshot_mods_timed_out()) {
    mods |= get_oneshot_mods();
  }
#endif  
  return mods;
}

void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds) {
    // Note that there's a small race condition here, the thread could read
    // a state where one of these are set but not the other. But this should
    // not really matter as it will be fixed during the next loop step.
    // Alternatively a mutex could be used instead of the volatile variables

    bool changed = false;
#ifdef SERIAL_LINK_ENABLE
    if (is_serial_link_connected ()) {
        visualizer_keyboard_status_t* new_status = read_current_status();
        if (new_status) {
            if (!same_status(&current_status, new_status)) {
                changed = true;
                current_status = *new_status;
            }
        }
    }
    else {
#else
   {
#endif
        visualizer_keyboard_status_t new_status = {
            .layer = state,
            .default_layer = default_state,
            .mods = mods,
            .leds = leds,
            .suspended = current_status.suspended,
        };
        if (!same_status(&current_status, &new_status)) {
            changed = true;
            current_status = new_status;
        }
    }
    update_status(changed);
}

void visualizer_suspend(void) {
    current_status.suspended = true;
    update_status(true);
}

void visualizer_resume(void) {
    current_status.suspended = false;
    update_status(true);
}