Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 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 | } |