1 /*
2  * ============================================================================
3  *  Title:    Event Handling Driver for both SDL1 and SDL2
4  *  Author:   J. Zbiciak
5  * ============================================================================
6  *  This is the platform-specific driver for SDL 1.2.x and SDL 2.x
7  *  The SDL1 and SDL2 specific pieces are in event_sdl1.c and event_sdl2.c.
8  * ============================================================================
9  */
10 
11 #include "config.h"
12 #include "sdl_jzintv.h"
13 #include "event/event_tbl.h"
14 #include "event/event_plat.h"
15 #include "event/event_sdl_pvt.h"
16 #include "joy/joy.h"
17 #include "joy/joy_sdl.h"
18 #include "mouse/mouse.h"
19 
20 /* TODO:  Migrate Enscripten support to SDL2. */
21 #if defined(__EMSCRIPTEN__)
22 # define SDL_EventState(x,y) ((void)(x), (void)(y))
23 #endif
24 
25 /* ======================================================================== */
26 /*  Some very minor platform-specific tweaks.                               */
27 /* ======================================================================== */
28 #ifdef WII
29 # define ENABLE_JOY_EVENTS SDL_IGNORE
30 #else
31 # define ENABLE_JOY_EVENTS SDL_ENABLE
32 #endif
33 
34 #ifdef N900
35 # define ENABLE_SYSWM_EVENTS SDL_IGNORE
36 #else
37 # define ENABLE_SYSWM_EVENTS SDL_ENABLE
38 #endif
39 
40 #ifdef USE_SDL2
41 # define WINDOW_EVENT_CATEGORY SDL_WINDOWEVENT
42 #else
43 # define WINDOW_EVENT_CATEGORY SDL_ACTIVEEVENT
44 #endif
45 
46 typedef struct event_sdl_pvt
47 {
48     bool mouse_enabled;
49 } event_sdl_pvt_t;
50 
51 /* ======================================================================== */
52 /*  EVENT_PLAT_INIT  -- Initializes the SDL1 Event subsystem.               */
53 /* ======================================================================== */
event_plat_init(const bool enable_mouse,evt_pvt_t * const evt_pvt,void ** const ptr_plat_pvt)54 int event_plat_init(
55     const bool       enable_mouse,  /*  Enable mouse events?                */
56     evt_pvt_t *const evt_pvt,       /*  Event core private ptr, if needed.  */
57     void     **const ptr_plat_pvt   /*  Our allocated private struct.       */
58 )
59 {
60     SDL_Event dummy;
61 
62     UNUSED(evt_pvt);
63 
64     /* -------------------------------------------------------------------- */
65     /*  Set up our "private" structure.                                     */
66     /* -------------------------------------------------------------------- */
67     event_sdl_pvt_t *plat_pvt = CALLOC(event_sdl_pvt_t, 1);
68 
69     if (!plat_pvt)
70     {
71         fprintf(stderr, "event_sdl1: Unable to allocate private state.\n");
72         return -1;
73     }
74 
75     *ptr_plat_pvt = plat_pvt;
76 
77     /* -------------------------------------------------------------------- */
78     /*  Set up SDL to filter out the events we're NOT interested in...      */
79     /* -------------------------------------------------------------------- */
80     if (!enable_mouse)
81     {
82         SDL_EventState(SDL_MOUSEMOTION,         SDL_IGNORE);
83         SDL_EventState(SDL_MOUSEBUTTONDOWN,     SDL_IGNORE);
84         SDL_EventState(SDL_MOUSEBUTTONUP,       SDL_IGNORE);
85     } else
86     {
87         plat_pvt->mouse_enabled = true;
88         SDL_EventState(SDL_MOUSEMOTION,         SDL_ENABLE);
89         SDL_EventState(SDL_MOUSEBUTTONDOWN,     SDL_ENABLE);
90         SDL_EventState(SDL_MOUSEBUTTONUP,       SDL_ENABLE);
91     }
92     SDL_EventState(SDL_SYSWMEVENT,          ENABLE_SYSWM_EVENTS);
93 
94     /* -------------------------------------------------------------------- */
95     /*  ...and leave us only with the events we ARE interested in.          */
96     /* -------------------------------------------------------------------- */
97     SDL_EventState(WINDOW_EVENT_CATEGORY,   SDL_ENABLE);
98     SDL_EventState(SDL_KEYDOWN,             SDL_ENABLE);
99     SDL_EventState(SDL_KEYUP,               SDL_ENABLE);
100     SDL_EventState(SDL_QUIT,                SDL_ENABLE);
101     SDL_EventState(SDL_JOYAXISMOTION,       ENABLE_JOY_EVENTS);
102     SDL_EventState(SDL_JOYHATMOTION,        ENABLE_JOY_EVENTS);
103     SDL_EventState(SDL_JOYBUTTONDOWN,       ENABLE_JOY_EVENTS);
104     SDL_EventState(SDL_JOYBUTTONUP,         ENABLE_JOY_EVENTS);
105     SDL_EventState(SDL_JOYBALLMOTION,       ENABLE_JOY_EVENTS);
106     SDL_JoystickEventState(ENABLE_JOY_EVENTS);
107 
108     /* -------------------------------------------------------------------- */
109     /*  Drain the event queue right now to clear any initial events.        */
110     /* -------------------------------------------------------------------- */
111     while (SDL_PollEvent(&dummy))
112         ;
113 
114     /* -------------------------------------------------------------------- */
115     /*  Done!                                                               */
116     /* -------------------------------------------------------------------- */
117     return 0;
118 }
119 
120 /* ======================================================================== */
121 /*  EVENT_PLAT_DTOR  -- Tear down the event engine.                         */
122 /* ======================================================================== */
event_plat_dtor(void * const plat_pvt)123 void event_plat_dtor(void *const plat_pvt)
124 {
125     if (plat_pvt) free(plat_pvt);
126     joy_dtor();
127 }
128 
129 /* ======================================================================== */
130 /*  EVENT_PLAT_PUMP  -- Pump the event engine before the full tick starts.  */
131 /* ======================================================================== */
event_plat_pump(evt_pvt_t * const evt_pvt,void * const void_plat_pvt)132 void event_plat_pump(evt_pvt_t *const evt_pvt, void *const void_plat_pvt)
133 {
134     UNUSED(evt_pvt);
135     UNUSED(void_plat_pvt);
136 
137     SDL_PumpEvents();
138 }
139 
140 /* ======================================================================== */
141 /*  EVENT_PLAT_TICK  -- Performs the bulk of the tick cycle.                */
142 /*                                                                          */
143 /*  Returns true if we need to cork events, typically due to a combo.       */
144 /* ======================================================================== */
event_plat_tick(evt_pvt_t * const evt_pvt,void * const void_plat_pvt)145 bool event_plat_tick(evt_pvt_t *const evt_pvt, void *const void_plat_pvt)
146 {
147     const event_sdl_pvt_t *const plat_pvt = (event_sdl_pvt_t*)void_plat_pvt;
148     SDL_Event    event;
149 
150     /* -------------------------------------------------------------------- */
151     /*  Now, process all pending events.                                    */
152     /* -------------------------------------------------------------------- */
153 #ifdef WII
154     getWiiJoyEvents();
155 #endif
156     while (event_queue_has_room(evt_pvt, 4) && SDL_PollEvent(&event))
157     {
158         switch (event.type)
159         {
160             /* ------------------------------------------------------------ */
161             /*  Handle keypresses and releases by sending the decoded       */
162             /*  keysym value as event #.                                    */
163             /* ------------------------------------------------------------ */
164             case SDL_KEYDOWN:
165             case SDL_KEYUP:
166             {
167                 const event_updn_t event_updn =
168                     event.type == SDL_KEYUP ? EV_UP : EV_DOWN;
169 
170                 const event_num_t event_num = event_sdl_translate_key(&event);
171 
172                 if (event_enqueue_check_combo(evt_pvt, event_updn, event_num))
173                     return true;
174 
175                 break;
176             }
177 
178             /* ------------------------------------------------------------ */
179             /*  Outsource all the joystick event decoding...                */
180             /* ------------------------------------------------------------ */
181             case SDL_JOYAXISMOTION:
182             case SDL_JOYHATMOTION:
183             case SDL_JOYBUTTONDOWN:
184             case SDL_JOYBUTTONUP:
185             {
186                 event_updn_t event_updn[2] = { EV_DOWN, EV_DOWN };
187                 event_num_t  event_num[2] = { EVENT_IGNORE, EVENT_IGNORE };
188 
189                 const bool may_combo =
190                     joy_decode_event(&event, event_updn, event_num);
191 
192                 assert(!may_combo || event_num[1] == EVENT_IGNORE);
193                 if (may_combo)
194                 {
195                     if (event_enqueue_check_combo(
196                             evt_pvt, event_updn[0], event_num[0]))
197                         return true;
198                 } else
199                 {
200                     event_enqueue(evt_pvt, event_updn[0], event_num[0]);
201                     event_enqueue(evt_pvt, event_updn[1], event_num[1]);
202                 }
203                 break;
204             }
205 
206             /* ------------------------------------------------------------ */
207             /*  Outsource all mouse event decoding...                       */
208             /* ------------------------------------------------------------ */
209             case SDL_MOUSEMOTION:
210             case SDL_MOUSEBUTTONDOWN:
211             case SDL_MOUSEBUTTONUP:
212             {
213                 if (plat_pvt->mouse_enabled)
214                 {
215                     event_updn_t event_updn[2] = { EV_DOWN, EV_DOWN };
216                     event_num_t  event_num[2] = { EVENT_IGNORE, EVENT_IGNORE };
217 
218                     mouse_decode_event(&event, event_updn, event_num);
219 
220                     event_enqueue(evt_pvt, event_updn[0], event_num[0]);
221                     event_enqueue(evt_pvt, event_updn[1], event_num[1]);
222                 }
223                 break;
224             }
225 
226             /* ------------------------------------------------------------ */
227             /*  And finally, handle the QUIT event.                         */
228             /* ------------------------------------------------------------ */
229             case SDL_QUIT:
230             {
231                 event_enqueue(evt_pvt, EV_DOWN, EVENT_QUIT);
232                 break;
233             }
234 
235             /* ------------------------------------------------------------ */
236             /*  If it's unhandled, pass down to the SDL-version specific    */
237             /*  handlers to see if they understand it.                      */
238             /* ------------------------------------------------------------ */
239             default:
240             {
241                 /* If this returns 'true', that means it queued a combo. */
242                 if (event_sdl_unhandled_event(evt_pvt, &event))
243                     return true;
244                 break;
245             }
246         }
247     }
248 
249     return false;  /* did not need to cork. */
250 }
251 
252 /* ======================================================================== */
253 /*  EVENT_PLAT_TICK_LATE -- Performs deferred tick cycle tasks, after we    */
254 /*                          have drained our internal event queue.          */
255 /*                                                                          */
256 /*  Currently this is only used by SDL's experimental mouse processing.     */
257 /*  Not sure if this is really necessary.                                   */
258 /* ======================================================================== */
event_plat_tick_late(evt_pvt_t * const evt_pvt,void * const void_plat_pvt)259 void event_plat_tick_late(evt_pvt_t *const evt_pvt, void *const void_plat_pvt)
260 {
261     const event_sdl_pvt_t *const plat_pvt = (event_sdl_pvt_t*)void_plat_pvt;
262 
263     /* -------------------------------------------------------------------- */
264     /*  If the mouse is enabled, see if we need to do any delayed events.   */
265     /* -------------------------------------------------------------------- */
266     if (plat_pvt->mouse_enabled)
267     {
268         event_updn_t event_updn = EV_DOWN;
269         event_num_t  event_num = EVENT_IGNORE;
270 
271         mouse_pump(&event_updn, &event_num);
272         event_enqueue(evt_pvt, event_updn, event_num);
273     }
274 }
275 
276 /* ======================================================================== */
277 /*  This program is free software; you can redistribute it and/or modify    */
278 /*  it under the terms of the GNU General Public License as published by    */
279 /*  the Free Software Foundation; either version 2 of the License, or       */
280 /*  (at your option) any later version.                                     */
281 /*                                                                          */
282 /*  This program is distributed in the hope that it will be useful,         */
283 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
284 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
285 /*  General Public License for more details.                                */
286 /*                                                                          */
287 /*  You should have received a copy of the GNU General Public License along */
288 /*  with this program; if not, write to the Free Software Foundation, Inc., */
289 /*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.             */
290 /* ======================================================================== */
291 /*                 Copyright (c) 1998-2020, Joseph Zbiciak                  */
292 /* ======================================================================== */
293