Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
  3.  * It is copyright by its individual contributors, as recorded in the
  4.  * project's Git history.  See COPYING.txt at the top level for license
  5.  * terms and a link to the Git history.
  6.  */
  7. /*
  8.  *
  9.  * SDL joystick support
  10.  *
  11.  */
  12.  
  13. #include <memory>
  14. #include <vector>
  15. #include <tuple>
  16. #include <type_traits>
  17. #include "joy.h"
  18. #include "key.h"
  19. #include "dxxerror.h"
  20. #include "timer.h"
  21. #include "console.h"
  22. #include "event.h"
  23. #include "u_mem.h"
  24. #include "playsave.h"
  25. #include "kconfig.h"
  26. #include "compiler-range_for.h"
  27.  
  28. #if DXX_MAX_JOYSTICKS
  29. #include "compiler-cf_assert.h"
  30. #include "d_enumerate.h"
  31. #include "d_range.h"
  32. #include "partial_range.h"
  33. #include <utility>
  34.  
  35. namespace dcx {
  36.  
  37. namespace {
  38.  
  39. int num_joysticks = 0;
  40.  
  41. std::vector<unsigned> joy_key_map;
  42.  
  43. /* This struct is a "virtual" joystick, which includes all the axes
  44.  * and buttons of every joystick found.
  45.  */
  46. static struct joyinfo {
  47. #if DXX_MAX_BUTTONS_PER_JOYSTICK
  48.         std::array<uint8_t, JOY_MAX_BUTTONS> button_state;
  49. #endif
  50. } Joystick;
  51.  
  52. struct d_event_joystickbutton : d_event
  53. {
  54.         const unsigned button;
  55.         constexpr d_event_joystickbutton(const event_type t, const unsigned b) :
  56.                 d_event(t), button(b)
  57.         {
  58.         }
  59. };
  60.  
  61. struct d_event_joystick_moved : d_event, d_event_joystick_axis_value
  62. {
  63.         DXX_INHERIT_CONSTRUCTORS(d_event_joystick_moved, d_event);
  64. };
  65.  
  66. class SDL_Joystick_deleter
  67. {
  68. public:
  69.         void operator()(SDL_Joystick *j) const
  70.         {
  71.                 SDL_JoystickClose(j);
  72.         }
  73. };
  74.  
  75. #ifndef DXX_USE_SIZE_SORTED_TUPLE
  76. #define DXX_USE_SIZE_SORTED_TUPLE       (__cplusplus > 201103L)
  77. #endif
  78.  
  79. #if DXX_USE_SIZE_SORTED_TUPLE
  80. template <typename T, std::size_t... Is, std::size_t... Js>
  81. auto d_split_tuple(T &&t, std::index_sequence<Is...>, std::index_sequence<Js...>) ->
  82.         std::pair<
  83.                 std::tuple<typename std::tuple_element<Is, T>::type...>,
  84.                 std::tuple<typename std::tuple_element<sizeof...(Is) + Js, T>::type...>
  85.         >;
  86.  
  87. template <typename>
  88. class d_size_sorted;
  89.  
  90. /* Given an input tuple T, define a public member `type` with the same
  91.  * members as T, but sorted such that sizeof(Ti) <= sizeof(Tj) for all i
  92.  * <= j.
  93.  */
  94. template <typename... Ts>
  95. class d_size_sorted<std::tuple<Ts...>>
  96. {
  97.         using split_tuple = decltype(d_split_tuple(
  98.                 std::declval<std::tuple<Ts...>>(),
  99.                 std::make_index_sequence<sizeof...(Ts) / 2>(),
  100.                 std::make_index_sequence<(1 + sizeof...(Ts)) / 2>()
  101.         ));
  102.         using first_type = typename split_tuple::first_type;
  103.         using second_type = typename split_tuple::second_type;
  104. public:
  105.         using type = typename std::conditional<(sizeof(first_type) < sizeof(second_type)),
  106.                 decltype(std::tuple_cat(std::declval<first_type>(), std::declval<second_type>())),
  107.                 decltype(std::tuple_cat(std::declval<second_type>(), std::declval<first_type>()))
  108.         >::type;
  109. };
  110.  
  111. template <typename T1>
  112. class d_size_sorted<std::tuple<T1>>
  113. {
  114. public:
  115.         using type = std::tuple<T1>;
  116. };
  117. #endif
  118.  
  119. template <typename>
  120. class ignore_empty {};
  121.  
  122. template <typename T>
  123. using maybe_empty_array = typename std::conditional<T().empty(), ignore_empty<T>, T>::type;
  124.  
  125. /* This struct is an array, with one entry for each physical joystick
  126.  * found.
  127.  */
  128. class d_physical_joystick
  129. {
  130. #define for_each_tuple_item(HANDLE_VERB,VERB)   \
  131.         HANDLE_VERB(handle)     \
  132.         VERB(hat_map)   \
  133.         VERB(button_map)        \
  134.         VERB(axis_map)  \
  135.         VERB(axis_value)        \
  136.         VERB(axis_button_map)   \
  137.  
  138. #if DXX_USE_SIZE_SORTED_TUPLE
  139.         template <typename... Ts>
  140.                 using tuple_type = typename d_size_sorted<std::tuple<Ts...>>::type;
  141. #define define_handle_getter(N) \
  142.         define_getter(N, tuple_member_type_##N)
  143. #define define_array_getter(N)  \
  144.         define_getter(N, maybe_empty_array<tuple_member_type_##N>)
  145. #else
  146.         template <typename... Ts>
  147.                 using tuple_type = std::tuple<Ts...>;
  148.         enum
  149.         {
  150. #define define_enum(V)  tuple_item_##V,
  151.                 for_each_tuple_item(define_enum, define_enum)
  152. #undef define_enum
  153.         };
  154.         /* std::get<index> does not handle the maybe_empty_array case, so
  155.          * reuse the same getter for both handle and array.
  156.          */
  157. #define define_handle_getter(N) \
  158.         define_getter(N, tuple_item_##N)
  159. #define define_array_getter     define_handle_getter
  160. #endif
  161. #define define_getter(N,V)      \
  162.         auto &N()       \
  163.         {       \
  164.                 return std::get<V>(t);  \
  165.         }
  166.         using tuple_member_type_handle = std::unique_ptr<SDL_Joystick, SDL_Joystick_deleter>;
  167.         //Note: Descent expects hats to be buttons, so these are indices into Joystick.buttons
  168.         struct tuple_member_type_hat_map : std::array<unsigned, DXX_MAX_HATS_PER_JOYSTICK> {};
  169.         struct tuple_member_type_button_map : std::array<unsigned, DXX_MAX_BUTTONS_PER_JOYSTICK> {};
  170.         struct tuple_member_type_axis_map : std::array<unsigned, DXX_MAX_AXES_PER_JOYSTICK> {};
  171.         struct tuple_member_type_axis_value : std::array<int, DXX_MAX_AXES_PER_JOYSTICK> {};
  172.         struct tuple_member_type_axis_button_map : std::array<unsigned, DXX_MAX_AXES_PER_JOYSTICK> {};
  173.         tuple_type<
  174.                 tuple_member_type_handle,
  175.                 maybe_empty_array<tuple_member_type_hat_map>,
  176.                 maybe_empty_array<tuple_member_type_button_map>,
  177.                 maybe_empty_array<tuple_member_type_axis_map>,
  178.                 maybe_empty_array<tuple_member_type_axis_value>,
  179.                 maybe_empty_array<tuple_member_type_axis_button_map>
  180.         > t;
  181. public:
  182.         for_each_tuple_item(define_handle_getter, define_array_getter);
  183. #undef define_handle_getter
  184. #undef define_array_getter
  185. #undef define_getter
  186. #undef for_each_tuple_item
  187. };
  188.  
  189. }
  190.  
  191. static std::array<d_physical_joystick, DXX_MAX_JOYSTICKS> SDL_Joysticks;
  192.  
  193. #if DXX_MAX_BUTTONS_PER_JOYSTICK
  194. window_event_result joy_button_handler(const SDL_JoyButtonEvent *const jbe)
  195. {
  196.         const unsigned button = SDL_Joysticks[jbe->which].button_map()[jbe->button];
  197.  
  198.         Joystick.button_state[button] = jbe->state;
  199.  
  200.         const d_event_joystickbutton event{
  201.                 (jbe->type == SDL_JOYBUTTONDOWN) ? EVENT_JOYSTICK_BUTTON_DOWN : EVENT_JOYSTICK_BUTTON_UP,
  202.                 button
  203.         };
  204.         con_printf(CON_DEBUG, "Sending event %s, button %d", (jbe->type == SDL_JOYBUTTONDOWN) ? "EVENT_JOYSTICK_BUTTON_DOWN" : "EVENT_JOYSTICK_JOYSTICK_UP", event.button);
  205.         return event_send(event);
  206. }
  207. #endif
  208.  
  209. #if DXX_MAX_HATS_PER_JOYSTICK
  210. window_event_result joy_hat_handler(const SDL_JoyHatEvent *const jhe)
  211. {
  212.         int hat = SDL_Joysticks[jhe->which].hat_map()[jhe->hat];
  213.         window_event_result highest_result(window_event_result::ignored);
  214.         //Save last state of the hat-button
  215.  
  216.         //get current state of the hat-button
  217.         const auto jhe_value = jhe->value;
  218.         /* Every value must have exactly one bit set, and the union must
  219.          * cover the lower four bits.  If any of these assertions fail, the
  220.          * loop will not work right.
  221.          */
  222. #define assert_hat_one_bit(M)   \
  223.         static_assert(!((SDL_HAT_##M) & ((SDL_HAT_##M) - 1)), "unexpected " #M " mask");
  224.         assert_hat_one_bit(UP);
  225.         assert_hat_one_bit(RIGHT);
  226.         assert_hat_one_bit(DOWN);
  227.         assert_hat_one_bit(LEFT);
  228. #undef assert_hat_one_bit
  229.         static_assert((SDL_HAT_UP | SDL_HAT_RIGHT | SDL_HAT_DOWN | SDL_HAT_LEFT) == 0xf, "unexpected hat mask");
  230.  
  231.         //determine if a hat-button up or down event based on state and last_state
  232.         range_for (const unsigned i, xrange(4u))
  233.         {
  234.                 const auto current_button_state = !!(jhe_value & (1 << i));
  235.                 auto &saved_button_state = Joystick.button_state[hat + i];
  236.                 if (saved_button_state == current_button_state)
  237.                         // Same state as before
  238.                         continue;
  239.                 saved_button_state = current_button_state;
  240.                 const d_event_joystickbutton event{current_button_state ? EVENT_JOYSTICK_BUTTON_DOWN : EVENT_JOYSTICK_BUTTON_UP, hat + i};
  241.                 if (current_button_state) //last_state up, current state down
  242.                 {
  243.                         con_printf(CON_DEBUG, "Sending event EVENT_JOYSTICK_BUTTON_DOWN, button %d", event.button);
  244.                 }
  245.                 else    //last_state down, current state up
  246.                 {
  247.                         con_printf(CON_DEBUG, "Sending event EVENT_JOYSTICK_BUTTON_UP, button %d", event.button);
  248.                 }
  249.                 highest_result = std::max(event_send(event), highest_result);
  250.         }
  251.  
  252.         return highest_result;
  253. }
  254. #endif
  255.  
  256. #if DXX_MAX_AXES_PER_JOYSTICK
  257. #if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK
  258. static window_event_result send_axis_button_event(unsigned button, event_type e)
  259. {
  260.         Joystick.button_state[button] = (e == EVENT_JOYSTICK_BUTTON_UP) ? 0 : 1;
  261.         const d_event_joystickbutton event{ e, button };
  262.         con_printf(CON_DEBUG, "Sending event %s, button %d", (e == EVENT_JOYSTICK_BUTTON_UP) ? "EVENT_JOYSTICK_BUTTON_UP" : "EVENT_JOYSTICK_BUTTON_DOWN", event.button);
  263.         return event_send(event);
  264. }
  265.  
  266. window_event_result joy_axisbutton_handler(const SDL_JoyAxisEvent *const jae)
  267. {
  268.         auto &js = SDL_Joysticks[jae->which];
  269.         auto axis_value = js.axis_value()[jae->axis];
  270.         auto button = js.axis_button_map()[jae->axis];
  271.         window_event_result highest_result(window_event_result::ignored);
  272.  
  273.         // We have to hardcode a deadzone here. It's not mapped into the settings.
  274.         // We could add another deadzone slider called "axis button deadzone".
  275.         // I think it's safe to assume a 30% deadzone on analog button presses for now.
  276.         const decltype(axis_value) deadzone = 38;
  277.         auto prev_value = apply_deadzone(axis_value, deadzone);
  278.         auto new_value = apply_deadzone(jae->value/256, deadzone);
  279.  
  280.         if (prev_value <= 0 && new_value >= 0) // positive pressed
  281.         {
  282.                 if (prev_value < 0) // Do previous direction release first if the case
  283.                         highest_result = std::max(send_axis_button_event(button + 1, EVENT_JOYSTICK_BUTTON_UP), highest_result);
  284.                 if (new_value > 0)
  285.                         highest_result = std::max(send_axis_button_event(button, EVENT_JOYSTICK_BUTTON_DOWN), highest_result);
  286.         }
  287.         else if (prev_value >= 0 && new_value <= 0) // negative pressed
  288.         {
  289.                 if (prev_value > 0) // Do previous direction release first if the case
  290.                         highest_result = std::max(send_axis_button_event(button, EVENT_JOYSTICK_BUTTON_UP), highest_result);
  291.                 if (new_value < 0)
  292.                         highest_result = std::max(send_axis_button_event(button + 1, EVENT_JOYSTICK_BUTTON_DOWN), highest_result);
  293.         }
  294.  
  295.         return highest_result;
  296. }
  297. #endif
  298.  
  299. window_event_result joy_axis_handler(const SDL_JoyAxisEvent *const jae)
  300. {
  301.         auto &js = SDL_Joysticks[jae->which];
  302.         const auto axis = js.axis_map()[jae->axis];
  303.         auto &axis_value = js.axis_value()[jae->axis];
  304.         // inaccurate stick is inaccurate. SDL might send SDL_JoyAxisEvent even if the value is the same as before.
  305.         if (axis_value == jae->value/256)
  306.                 return window_event_result::ignored;
  307.  
  308.         d_event_joystick_moved event{EVENT_JOYSTICK_MOVED};
  309.         event.value = axis_value = jae->value/256;
  310.         event.axis = axis;
  311.         con_printf(CON_DEBUG, "Sending event EVENT_JOYSTICK_MOVED, axis: %d, value: %d",event.axis, event.value);
  312.  
  313.         return event_send(event);
  314. }
  315. #endif
  316.  
  317.  
  318. /* ----------------------------------------------- */
  319.  
  320. static unsigned check_warn_joy_support_limit(const unsigned n, const char *const desc, const unsigned MAX)
  321. {
  322.         if (n <= MAX)
  323.         {
  324.                 con_printf(CON_NORMAL, "sdl-joystick: %d %ss", n, desc);
  325.                 return n;
  326.         }
  327.         Warning("sdl-joystick: found %d %ss, only %d supported.\n", n, desc, MAX);
  328.         return MAX;
  329. }
  330.  
  331. void joy_init()
  332. {
  333.         if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
  334.                 con_printf(CON_NORMAL, "sdl-joystick: initialisation failed: %s.",SDL_GetError());
  335.                 return;
  336.         }
  337.  
  338.         Joystick = {};
  339. #if DXX_MAX_AXES_PER_JOYSTICK
  340.         joyaxis_text.clear();
  341. #endif
  342.         joybutton_text.clear();
  343.         joy_key_map.clear();
  344.  
  345.         const auto n = check_warn_joy_support_limit(SDL_NumJoysticks(), "joystick", DXX_MAX_JOYSTICKS);
  346.         cf_assert(n <= DXX_MAX_JOYSTICKS);
  347.         unsigned joystick_n_buttons = 0, joystick_n_axes = 0;
  348.         range_for (const unsigned i, xrange(n))
  349.         {
  350.                 auto &joystick = SDL_Joysticks[num_joysticks];
  351.                 const auto handle = SDL_JoystickOpen(i);
  352.                 joystick.handle().reset(handle);
  353. #if SDL_MAJOR_VERSION == 1
  354.                 con_printf(CON_NORMAL, "sdl-joystick %d: %s", i, SDL_JoystickName(i));
  355. #else
  356.                 con_printf(CON_NORMAL, "sdl-joystick %d: %s", i, SDL_JoystickName(handle));
  357. #endif
  358.                 if (handle)
  359.                 {
  360. #if DXX_MAX_AXES_PER_JOYSTICK
  361.                         const auto n_axes = check_warn_joy_support_limit(SDL_JoystickNumAxes(handle), "axe", DXX_MAX_AXES_PER_JOYSTICK);
  362.  
  363.                         joyaxis_text.resize(joyaxis_text.size() + n_axes);
  364.                         range_for (auto &&e, enumerate(partial_range(joystick.axis_map(), n_axes), 1))
  365.                         {
  366.                                 cf_assert(e.idx <= DXX_MAX_AXES_PER_JOYSTICK);
  367.                                 auto &text = joyaxis_text[joystick_n_axes];
  368.                                 e.value = joystick_n_axes++;
  369.                                 snprintf(&text[0], sizeof(text), "J%d A%u", i + 1, e.idx);
  370.                         }
  371. #else
  372.             const auto n_axes = 0;
  373. #endif
  374.  
  375.                         const auto n_buttons = check_warn_joy_support_limit(SDL_JoystickNumButtons(handle), "button", DXX_MAX_BUTTONS_PER_JOYSTICK);
  376.                         const auto n_hats = check_warn_joy_support_limit(SDL_JoystickNumHats(handle), "hat", DXX_MAX_HATS_PER_JOYSTICK);
  377.  
  378.                         const auto n_virtual_buttons = n_buttons + (4 * n_hats) + (2 * n_axes);
  379.                         joybutton_text.resize(joybutton_text.size() + n_virtual_buttons);
  380.                         joy_key_map.resize(joy_key_map.size() + n_virtual_buttons, 0);
  381. #if DXX_MAX_BUTTONS_PER_JOYSTICK
  382.                         range_for (auto &&e, enumerate(partial_range(joystick.button_map(), n_buttons), 1))
  383.                         {
  384.                                 switch (e.idx) {
  385.                                         case 1: joy_key_map[joystick_n_buttons] = KEY_ENTER; break;
  386.                                         case 2: joy_key_map[joystick_n_buttons] = KEY_ESC; break;
  387.                                         case 3: joy_key_map[joystick_n_buttons] = KEY_SPACEBAR; break;
  388.                                         case 4: joy_key_map[joystick_n_buttons] = KEY_DELETE; break;
  389.                                         default: break;
  390.                                 }
  391.                                 auto &text = joybutton_text[joystick_n_buttons];
  392.                                 e.value = joystick_n_buttons++;
  393.                                 cf_assert(e.idx <= DXX_MAX_BUTTONS_PER_JOYSTICK);
  394.                                 snprintf(&text[0], sizeof(text), "J%u B%u", i + 1, e.idx);
  395.                         }
  396. #endif
  397. #if DXX_MAX_HATS_PER_JOYSTICK
  398.                         range_for (auto &&e, enumerate(partial_range(joystick.hat_map(), n_hats), 1))
  399.                         {
  400.                                 e.value = joystick_n_buttons;
  401.                                 cf_assert(e.idx <= DXX_MAX_HATS_PER_JOYSTICK);
  402.                                 //a hat counts as four buttons
  403.                                 joy_key_map[joystick_n_buttons] = KEY_UP;
  404.                                 snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0202);
  405.                                 joy_key_map[joystick_n_buttons] = KEY_RIGHT;
  406.                                 snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0177);
  407.                                 joy_key_map[joystick_n_buttons] = KEY_DOWN;
  408.                                 snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0200);
  409.                                 joy_key_map[joystick_n_buttons] = KEY_LEFT;
  410.                                 snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0201);
  411.                         }
  412. #endif
  413. #if DXX_MAX_AXES_PER_JOYSTICK
  414.                         range_for (auto &&e, enumerate(partial_range(joystick.axis_button_map(), n_axes), 1))
  415.                         {
  416.                                 e.value = joystick_n_buttons;
  417.                                 cf_assert(e.idx <= DXX_MAX_AXES_PER_JOYSTICK);
  418.                                 //an axis count as 2 buttons. negative - and positive +
  419.                                 joy_key_map[joystick_n_buttons] = (e.idx == 1) ? KEY_RIGHT : (e.idx == 2) ? KEY_DOWN : 0;
  420.                                 snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u -A%u", i + 1, e.idx);
  421.                                 joy_key_map[joystick_n_buttons] = (e.idx == 1) ? KEY_LEFT : (e.idx == 2) ? KEY_UP : 0;
  422.                                 snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u +A%u", i + 1, e.idx);
  423.                         }
  424. #endif
  425.  
  426.                         num_joysticks++;
  427.                 }
  428.                 else
  429.                         con_puts(CON_NORMAL, "sdl-joystick: initialization failed!");
  430.  
  431.                 con_printf(CON_NORMAL, "sdl-joystick: %d axes (total)", joystick_n_axes);
  432.                 con_printf(CON_NORMAL, "sdl-joystick: %d buttons (total)", joystick_n_buttons);
  433.         }
  434. }
  435.  
  436. void joy_close()
  437. {
  438.         range_for (auto &j, SDL_Joysticks)
  439.                 j.handle().reset();
  440. #if DXX_MAX_AXES_PER_JOYSTICK
  441.         joyaxis_text.clear();
  442. #endif
  443.         joybutton_text.clear();
  444. }
  445.  
  446. const d_event_joystick_axis_value &event_joystick_get_axis(const d_event &event)
  447. {
  448.         auto &e = static_cast<const d_event_joystick_moved &>(event);
  449.         Assert(e.type == EVENT_JOYSTICK_MOVED);
  450.         return e;
  451. }
  452.  
  453. void joy_flush()
  454. {
  455.         if (!num_joysticks)
  456.                 return;
  457.  
  458.         static_assert(SDL_RELEASED == uint8_t(), "SDL_RELEASED not 0.");
  459. #if DXX_MAX_BUTTONS_PER_JOYSTICK
  460.         Joystick.button_state = {};
  461. #endif
  462. #if DXX_MAX_AXES_PER_JOYSTICK
  463.         range_for (auto &j, SDL_Joysticks)
  464.                 j.axis_value() = {};
  465. #endif
  466. }
  467.  
  468. int event_joystick_get_button(const d_event &event)
  469. {
  470.         auto &e = static_cast<const d_event_joystickbutton &>(event);
  471.         Assert(e.type == EVENT_JOYSTICK_BUTTON_DOWN || e.type == EVENT_JOYSTICK_BUTTON_UP);
  472.         return e.button;
  473. }
  474.  
  475. int apply_deadzone(int value, int deadzone)
  476. {
  477.         if (value > deadzone)
  478.                 return ((value - deadzone) * 128) / (128 - deadzone);
  479.         else if (value < -deadzone)
  480.                 return ((value + deadzone) * 128) / (128 - deadzone);
  481.         else
  482.                 return 0;
  483. }
  484.  
  485. bool joy_translate_menu_key(const d_event &event) {
  486.         if (event.type != EVENT_JOYSTICK_BUTTON_DOWN)
  487.                 return false;
  488. #if DXX_MAX_JOYSTICKS
  489.         auto &e = static_cast<const d_event_joystickbutton &>(event);
  490.         assert(e.button < joy_key_map.size());
  491.         auto key = joy_key_map[e.button];
  492.         if (key)
  493.         {
  494.                 event_keycommand_send(key);
  495.                 return true;
  496.         }
  497.         return false;
  498. #endif
  499. }
  500.  
  501. }
  502. #endif
  503.