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 Event related stuff
  10.  *
  11.  *
  12.  */
  13.  
  14. #include <SDL.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include "event.h"
  18. #include "key.h"
  19. #include "mouse.h"
  20. #include "window.h"
  21. #include "timer.h"
  22. #include "config.h"
  23. #include "inferno.h"
  24.  
  25. #include "joy.h"
  26. #include "args.h"
  27. #include "partial_range.h"
  28.  
  29. namespace dcx {
  30.  
  31. namespace {
  32.  
  33. struct event_poll_state
  34. {
  35.         uint8_t clean_uniframe = 1;
  36.         const window *const front_window = window_get_front();
  37.         window_event_result highest_result = window_event_result::ignored;
  38.         void process_event_batch(partial_range_t<const SDL_Event *>);
  39. };
  40.  
  41. }
  42.  
  43. #if SDL_MAJOR_VERSION == 2
  44. extern SDL_Window *g_pRebirthSDLMainWindow;
  45.  
  46. static void windowevent_handler(const SDL_WindowEvent &windowevent)
  47. {
  48.         switch (windowevent.event)
  49.         {
  50.                 case SDL_WINDOWEVENT_SIZE_CHANGED:
  51.                         {
  52.                                 const d_window_size_event e{windowevent.data1, windowevent.data2};
  53.                                 event_send(e);
  54.                                 break;
  55.                         }
  56.         }
  57. }
  58. #endif
  59.  
  60. static void event_notify_begin_loop()
  61. {
  62.         const d_event_begin_loop event;
  63.         event_send(event);
  64. }
  65.  
  66. window_event_result event_poll()
  67. {
  68.         event_poll_state state;
  69.         event_notify_begin_loop();
  70.  
  71.         for (;;)
  72.         {
  73.         // If the front window changes, exit this loop, otherwise unintended behavior can occur
  74.         // like pressing 'Return' really fast at 'Difficulty Level' causing multiple games to be started
  75.                 if (state.front_window != window_get_front())
  76.                         break;
  77.                 std::array<SDL_Event, 128> events;
  78.  
  79.                 SDL_PumpEvents();
  80. #if SDL_MAJOR_VERSION == 1
  81.                 const auto peep = SDL_PeepEvents(events.data(), events.size(), SDL_GETEVENT, SDL_ALLEVENTS);
  82. #elif SDL_MAJOR_VERSION == 2
  83.                 const auto peep = SDL_PeepEvents(events.data(), events.size(), SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
  84. #endif
  85.                 if (peep <= 0)
  86.                         break;
  87.                 state.process_event_batch(unchecked_partial_range(events.data(), static_cast<unsigned>(peep)));
  88.                 if (state.highest_result == window_event_result::deleted)
  89.                         break;
  90.         }
  91.         // Send the idle event if there were no other events (or they were ignored)
  92.         if (state.highest_result == window_event_result::ignored)
  93.         {
  94.                 const d_event ievent{EVENT_IDLE};
  95.                 state.highest_result = std::max(event_send(ievent), state.highest_result);
  96.         }
  97.         else
  98.         {
  99. #if DXX_USE_EDITOR
  100.                 event_reset_idle_seconds();
  101. #endif
  102.         }
  103.         mouse_cursor_autohide();
  104.         return state.highest_result;
  105. }
  106.  
  107. void event_poll_state::process_event_batch(partial_range_t<const SDL_Event *> events)
  108. {
  109.         for (auto &&event : events)
  110.         {
  111.                 window_event_result result;
  112.                 switch(event.type) {
  113. #if SDL_MAJOR_VERSION == 2
  114.                         case SDL_WINDOWEVENT:
  115.                                 windowevent_handler(event.window);
  116.                                 continue;
  117. #endif
  118.                         case SDL_KEYDOWN:
  119.                         case SDL_KEYUP:
  120.                                 if (clean_uniframe)
  121.                                 {
  122.                                         clean_uniframe=0;
  123.                                         unicode_frame_buffer = {};
  124.                                 }
  125.                                 result = key_handler(&event.key);
  126.                                 break;
  127.                         case SDL_MOUSEBUTTONDOWN:
  128.                         case SDL_MOUSEBUTTONUP:
  129.                                 if (CGameArg.CtlNoMouse)
  130.                                         continue;
  131.                                 result = mouse_button_handler(&event.button);
  132.                                 break;
  133.                         case SDL_MOUSEMOTION:
  134.                                 if (CGameArg.CtlNoMouse)
  135.                                         continue;
  136.                                 result = mouse_motion_handler(&event.motion);
  137.                                 break;
  138.                         case SDL_JOYBUTTONDOWN:
  139.                         case SDL_JOYBUTTONUP:
  140.                                 if (CGameArg.CtlNoJoystick)
  141.                                         continue;
  142.                                 result = joy_button_handler(&event.jbutton);
  143.                                 break;
  144.                         case SDL_JOYAXISMOTION:
  145.                                 if (CGameArg.CtlNoJoystick)
  146.                                         continue;
  147. #if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK
  148.                                 highest_result = std::max(joy_axisbutton_handler(&event.jaxis), highest_result);
  149. #endif
  150.                                 result = joy_axis_handler(&event.jaxis);
  151.                                 break;
  152.                         case SDL_JOYHATMOTION:
  153.                                 if (CGameArg.CtlNoJoystick)
  154.                                         continue;
  155.                                 result = joy_hat_handler(&event.jhat);
  156.                                 break;
  157.                         case SDL_JOYBALLMOTION:
  158.                                 continue;
  159.                         case SDL_QUIT: {
  160.                                 d_event qevent = { EVENT_QUIT };
  161.                                 result = call_default_handler(qevent);
  162.                                 break;
  163.                         }
  164.                         default:
  165.                                 continue;
  166.                 }
  167.                 highest_result = std::max(result, highest_result);
  168.         }
  169. }
  170.  
  171. void event_flush()
  172. {
  173.         std::array<SDL_Event, 128> events;
  174.         for (;;)
  175.         {
  176.                 SDL_PumpEvents();
  177. #if SDL_MAJOR_VERSION == 1
  178.                 const auto peep = SDL_PeepEvents(events.data(), events.size(), SDL_GETEVENT, SDL_ALLEVENTS);
  179. #elif SDL_MAJOR_VERSION == 2
  180.                 const auto peep = SDL_PeepEvents(events.data(), events.size(), SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
  181. #endif
  182.                 if (peep != events.size())
  183.                         break;
  184.         }
  185. }
  186.  
  187. int event_init()
  188. {
  189.         // We should now be active and responding to events.
  190.         return 0;
  191. }
  192.  
  193. window_event_result call_default_handler(const d_event &event)
  194. {
  195.         return standard_handler(event);
  196. }
  197.  
  198. window_event_result event_send(const d_event &event)
  199. {
  200.         window *wind;
  201.         window_event_result handled = window_event_result::ignored;
  202.  
  203.         for (wind = window_get_front(); wind && handled == window_event_result::ignored; wind = window_get_prev(*wind))
  204.                 if (window_is_visible(wind))
  205.                 {
  206.                         handled = window_send_event(*wind, event);
  207.  
  208.                         if (handled == window_event_result::deleted) // break away if necessary: window_send_event() could have closed wind by now
  209.                                 break;
  210.                         if (window_is_modal(*wind))
  211.                                 break;
  212.                 }
  213.        
  214.         if (handled == window_event_result::ignored)
  215.                 return call_default_handler(event);
  216.  
  217.         return handled;
  218. }
  219.  
  220. // Process the first event in queue, sending to the appropriate handler
  221. // This is the new object-oriented system
  222. // Uses the old system for now, but this may change
  223. window_event_result event_process(void)
  224. {
  225.         window *wind = window_get_front();
  226.         window_event_result highest_result;
  227.  
  228.         timer_update();
  229.  
  230.         highest_result = event_poll();  // send input events first
  231.  
  232.         cmd_queue_process();
  233.  
  234.         // Doing this prevents problems when a draw event can create a newmenu,
  235.         // such as some network menus when they report a problem
  236.         // Also checking for window_event_result::deleted in case a window was created
  237.         // with the same pointer value as the deleted one
  238.         if ((highest_result == window_event_result::deleted) || (window_get_front() != wind))
  239.                 return highest_result;
  240.  
  241.         const d_event event{EVENT_WINDOW_DRAW}; // then draw all visible windows
  242.         for (wind = window_get_first(); wind != nullptr;)
  243.         {
  244.                 if (window_is_visible(wind))
  245.                 {
  246.                         auto prev = window_get_prev(*wind);
  247.                         auto result = window_send_event(*wind, event);
  248.                         highest_result = std::max(result, highest_result);
  249.                         if (result == window_event_result::deleted)
  250.                         {
  251.                                 if (!prev)
  252.                                 {
  253.                                         wind = window_get_first();
  254.                                         continue;
  255.                                 }
  256.                                 wind = prev;    // take the previous window and get the next one from that (if prev isn't nullptr)
  257.                         }
  258.                 }
  259.                 wind = window_get_next(*wind);
  260.         }
  261.  
  262.         gr_flip();
  263.  
  264.         return highest_result;
  265. }
  266.  
  267. template <bool activate_focus>
  268. static void event_change_focus()
  269. {
  270.         const auto enable_grab = activate_focus && CGameCfg.Grabinput && likely(!CGameArg.DbgForbidConsoleGrab);
  271. #if SDL_MAJOR_VERSION == 1
  272.         SDL_WM_GrabInput(enable_grab ? SDL_GRAB_ON : SDL_GRAB_OFF);
  273. #elif SDL_MAJOR_VERSION == 2
  274.         SDL_SetWindowGrab(g_pRebirthSDLMainWindow, enable_grab ? SDL_TRUE : SDL_FALSE);
  275.         SDL_SetRelativeMouseMode(enable_grab ? SDL_TRUE : SDL_FALSE);
  276. #endif
  277.         if (activate_focus)
  278.                 mouse_disable_cursor();
  279.         else
  280.                 mouse_enable_cursor();
  281. }
  282.  
  283. void event_enable_focus()
  284. {
  285.         event_change_focus<true>();
  286. }
  287.  
  288. void event_disable_focus()
  289. {
  290.         event_change_focus<false>();
  291. }
  292.  
  293. #if DXX_USE_EDITOR
  294. static fix64 last_event = 0;
  295.  
  296. void event_reset_idle_seconds()
  297. {
  298.         last_event = timer_query();
  299. }
  300.  
  301. fix event_get_idle_seconds()
  302. {
  303.         return (timer_query() - last_event)/F1_0;
  304. }
  305. #endif
  306.  
  307. }
  308.