Subversion Repositories Games.Descent

Rev

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
}