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 | } |