Line data Source code
1 : /**
2 : * @file SDL input handling
3 : *
4 : * SDL handles low-level window manager and key inputs for libprimis
5 : * this file determines how the game parses the SDL information it is given
6 : *
7 : * includes handling for when the window manager tells the game to change state
8 : * and handling for how the game should react to key inputs (though obviously this
9 : * is rebindable by the client)
10 : *
11 : */
12 :
13 : #include "../libprimis-headers/cube.h"
14 : #include "../../shared/stream.h"
15 :
16 : #include "console.h"
17 : #include "control.h"
18 : #include "input.h"
19 : #include "ui.h"
20 :
21 : #include "render/rendergl.h"
22 : #include "render/renderwindow.h"
23 :
24 : //sets SDL relative mouse mode to enabled if 1, 0 otherwise
25 : static VARNP(relativemouse, userelativemouse, 0, 1, 1);
26 :
27 : bool grabinput = false,
28 : minimized = false;
29 : static bool shouldgrab = false,
30 : canrelativemouse = true,
31 : relativemouse = false;
32 : int keyrepeatmask = 0,
33 : textinputmask = 0;
34 : Uint32 textinputtime = 0;
35 :
36 : static VAR(textinputfilter, 0, 5, 1000); //delay in ms between text input events
37 :
38 2 : void keyrepeat(bool on, int mask)
39 : {
40 2 : if(on)
41 : {
42 2 : keyrepeatmask |= mask;
43 : }
44 : else
45 : {
46 0 : keyrepeatmask &= ~mask;
47 : }
48 2 : }
49 :
50 2 : void textinput(bool on, int mask)
51 : {
52 2 : if(on)
53 : {
54 2 : if(!textinputmask)
55 : {
56 1 : SDL_StartTextInput();
57 1 : textinputtime = SDL_GetTicks();
58 : }
59 2 : textinputmask |= mask;
60 : }
61 : else
62 : {
63 0 : textinputmask &= ~mask;
64 0 : if(!textinputmask)
65 : {
66 0 : SDL_StopTextInput();
67 : }
68 : }
69 2 : }
70 :
71 0 : void inputgrab(bool on)
72 : {
73 0 : if(on)
74 : {
75 0 : SDL_ShowCursor(SDL_FALSE);
76 0 : if(canrelativemouse && userelativemouse)
77 : {
78 0 : if(SDL_SetRelativeMouseMode(SDL_TRUE) >= 0)
79 : {
80 0 : SDL_SetWindowGrab(screen, SDL_TRUE);
81 0 : relativemouse = true;
82 : }
83 : else
84 : {
85 0 : SDL_SetWindowGrab(screen, SDL_FALSE);
86 0 : canrelativemouse = false;
87 0 : relativemouse = false;
88 : }
89 : }
90 : }
91 : else
92 : {
93 0 : SDL_ShowCursor(SDL_TRUE);
94 0 : if(relativemouse)
95 : {
96 0 : SDL_SetRelativeMouseMode(SDL_FALSE);
97 0 : SDL_SetWindowGrab(screen, SDL_FALSE);
98 0 : relativemouse = false;
99 : }
100 : }
101 0 : shouldgrab = false;
102 0 : }
103 :
104 : std::vector<SDL_Event> events;
105 :
106 0 : void pushevent(const SDL_Event &e)
107 : {
108 0 : events.push_back(e);
109 0 : }
110 :
111 0 : static bool filterevent(const SDL_Event &event)
112 : {
113 0 : switch(event.type)
114 : {
115 0 : case SDL_MOUSEMOTION:
116 0 : if(grabinput && !relativemouse && !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
117 : {
118 0 : if(event.motion.x == hudw() / 2 && event.motion.y == hudh() / 2)
119 : {
120 0 : return false; // ignore any motion events generated by SDL_WarpMouse
121 : }
122 : }
123 0 : break;
124 : }
125 0 : return true;
126 : }
127 :
128 0 : static bool pollevent(SDL_Event &event)
129 : {
130 0 : while(SDL_PollEvent(&event))
131 : {
132 0 : if(filterevent(event))
133 : {
134 0 : return true;
135 : }
136 : }
137 0 : return false;
138 : }
139 :
140 0 : bool interceptkey(int sym)
141 : {
142 : static int lastintercept = SDLK_UNKNOWN;
143 0 : int len = lastintercept == sym ? static_cast<int>(events.size()) : 0;
144 : SDL_Event event;
145 0 : while(pollevent(event))
146 : {
147 0 : switch(event.type)
148 : {
149 0 : case SDL_MOUSEMOTION:
150 : {
151 0 : break;
152 : }
153 0 : default:
154 : {
155 0 : pushevent(event);
156 0 : break;
157 : }
158 : }
159 : }
160 0 : lastintercept = sym;
161 0 : if(sym != SDLK_UNKNOWN)
162 : {
163 0 : for(size_t i = len; i < events.size(); i++)
164 : {
165 0 : if(events[i].type == SDL_KEYDOWN && events[i].key.keysym.sym == sym)
166 : {
167 0 : events.erase(events.begin() + i);
168 0 : return true;
169 : }
170 : }
171 : }
172 0 : return false;
173 : }
174 :
175 0 : void ignoremousemotion()
176 : {
177 : SDL_Event e;
178 0 : SDL_PumpEvents();
179 : //go through each event and do nothing
180 0 : while(SDL_PeepEvents(&e, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION))
181 : {
182 : //(empty body)
183 : }
184 0 : }
185 :
186 : /**
187 : * @brief Resets the cursor to the middle of the screen
188 : *
189 : * Moves cursor to the center of the screen, regardless of where it is. This corresponds
190 : * to the position at half of (hudw(), hudh()).
191 : */
192 0 : static void resetmousemotion()
193 : {
194 0 : if(grabinput && !relativemouse && !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
195 : {
196 0 : SDL_WarpMouseInWindow(screen, hudw() / 2, hudh() / 2); //move to middle of screen
197 : }
198 0 : }
199 :
200 : /**
201 : * @brief Gets cursor information from SDL2
202 : *
203 : * gets the information from SDL2 about the amount by which the cursor has moved, and
204 : * and changes the passed dx, dy values by that amount
205 : *
206 : * @param dx reference to the mouse x delta
207 : * @param dy reference to the mouse y delta
208 : */
209 0 : static void checkmousemotion(int &dx, int &dy)
210 : {
211 0 : for(size_t i = 0; i < events.size(); i++)
212 : {
213 0 : SDL_Event &event = events[i];
214 0 : if(event.type != SDL_MOUSEMOTION)
215 : {
216 0 : if(i > 0)
217 : {
218 0 : events.erase(events.begin(), events.begin() + i);
219 : }
220 0 : return;
221 : }
222 0 : dx += event.motion.xrel;
223 0 : dy += event.motion.yrel;
224 : }
225 0 : events.clear();
226 : SDL_Event event;
227 0 : while(pollevent(event))
228 : {
229 0 : if(event.type != SDL_MOUSEMOTION)
230 : {
231 0 : events.push_back(event);
232 0 : return;
233 : }
234 0 : dx += event.motion.xrel;
235 0 : dy += event.motion.yrel;
236 : }
237 : }
238 :
239 :
240 : //handle different input types
241 : // map: which keymap to map the pressed key to
242 0 : void checkinput(int map)
243 : {
244 0 : constexpr uint minthreshhold = 5000; // minimum value to register inputs
245 0 : constexpr uint maxthreshhold = 27000; // maximum value to register inputs to triggers
246 0 : constexpr int strafethreshhold = 16384; //value when to assign movement in strafe pad, signed to allow unary negation
247 0 : constexpr uint inverseindex = 16;
248 :
249 0 : constexpr uint mousemovescale = 25000; //how much to divide mouse movement by from joystick input
250 : //carry over joystick states
251 0 : static vec2 lpad;
252 0 : static vec2 rpad;
253 0 : static vec2 triggers; //x = left, y = right
254 : SDL_Event event;
255 0 : bool mousemoved = false;
256 0 : while(events.size() || pollevent(event))
257 : {
258 0 : if(events.size())
259 : {
260 0 : event = events[0];
261 0 : events.erase(events.begin());
262 : }
263 0 : switch(event.type)
264 : {
265 0 : case SDL_QUIT:
266 : {
267 0 : exit(EXIT_SUCCESS);
268 : return;
269 : }
270 0 : case SDL_TEXTINPUT:
271 : {
272 0 : if(textinputmask && static_cast<int>(event.text.timestamp-textinputtime) >= textinputfilter)
273 : {
274 0 : size_t len = std::strlen(event.text.text);
275 0 : if(len > 0)
276 : {
277 0 : processtextinput(reinterpret_cast<const char *>(event.text.text), len);
278 : }
279 : }
280 0 : break;
281 : }
282 0 : case SDL_KEYDOWN:
283 : case SDL_KEYUP:
284 : {
285 0 : if(keyrepeatmask || !event.key.repeat)
286 : {
287 0 : processkey(event.key.keysym.sym, event.key.state==SDL_PRESSED, map);
288 : }
289 0 : break;
290 : }
291 0 : case SDL_WINDOWEVENT:
292 : {
293 0 : switch(event.window.event)
294 : {
295 0 : case SDL_WINDOWEVENT_CLOSE:
296 : {
297 0 : exit(EXIT_SUCCESS);
298 : break;
299 : }
300 0 : case SDL_WINDOWEVENT_FOCUS_GAINED:
301 : {
302 0 : shouldgrab = true;
303 0 : break;
304 : }
305 0 : case SDL_WINDOWEVENT_ENTER:
306 : {
307 0 : inputgrab(grabinput = true);
308 0 : break;
309 : }
310 0 : case SDL_WINDOWEVENT_LEAVE:
311 : case SDL_WINDOWEVENT_FOCUS_LOST:
312 : {
313 0 : inputgrab(grabinput = false);
314 0 : break;
315 : }
316 0 : case SDL_WINDOWEVENT_MINIMIZED:
317 : {
318 0 : minimized = true;
319 0 : break;
320 : }
321 0 : case SDL_WINDOWEVENT_MAXIMIZED:
322 : case SDL_WINDOWEVENT_RESTORED:
323 : {
324 0 : minimized = false;
325 0 : break;
326 : }
327 0 : case SDL_WINDOWEVENT_RESIZED:
328 : {
329 0 : break;
330 : }
331 0 : case SDL_WINDOWEVENT_SIZE_CHANGED:
332 : {
333 0 : SDL_GetWindowSize(screen, &screenw, &screenh);
334 0 : if(!(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
335 : {
336 : //need to cast enums to ints for std's clamp implementation
337 0 : scr_w = std::clamp(hudw(), static_cast<int>(SCR_MINW), static_cast<int>(SCR_MAXW));
338 0 : scr_h = std::clamp(hudh(), static_cast<int>(SCR_MINH), static_cast<int>(SCR_MAXH));
339 : }
340 0 : gl_resize();
341 0 : break;
342 : }
343 : }
344 0 : break;
345 : }
346 0 : case SDL_MOUSEMOTION:
347 : {
348 0 : if(grabinput)
349 : {
350 0 : int dx = event.motion.xrel,
351 0 : dy = event.motion.yrel;
352 0 : checkmousemotion(dx, dy);
353 0 : if(!UI::movecursor(dx, dy))
354 : {
355 0 : mousemove(dx, dy);
356 : }
357 0 : mousemoved = true;
358 : }
359 0 : else if(shouldgrab)
360 : {
361 0 : inputgrab(grabinput = true);
362 : }
363 0 : break;
364 : }
365 0 : case SDL_MOUSEBUTTONDOWN:
366 : case SDL_MOUSEBUTTONUP:
367 : {
368 0 : switch(event.button.button)
369 : {
370 0 : case SDL_BUTTON_LEFT:
371 : {
372 0 : processkey(Key_Left, event.button.state==SDL_PRESSED, map);
373 0 : break;
374 : }
375 0 : case SDL_BUTTON_MIDDLE:
376 : {
377 0 : processkey(Key_Middle, event.button.state==SDL_PRESSED, map);
378 0 : break;
379 : }
380 0 : case SDL_BUTTON_RIGHT:
381 : {
382 0 : processkey(Key_Right, event.button.state==SDL_PRESSED, map);
383 0 : break;
384 : }
385 0 : case SDL_BUTTON_X1:
386 : {
387 0 : processkey(Key_X1, event.button.state==SDL_PRESSED, map);
388 0 : break;
389 : }
390 0 : case SDL_BUTTON_X2:
391 : {
392 0 : processkey(Key_X2, event.button.state==SDL_PRESSED, map);
393 0 : break;
394 : }
395 : }
396 0 : break;
397 : }
398 0 : case SDL_MOUSEWHEEL:
399 : {
400 : //up
401 0 : if(event.wheel.y > 0)
402 : {
403 0 : processkey(Key_ScrollUp, true, map);
404 0 : processkey(Key_ScrollUp, false, map);
405 : }
406 : //down
407 0 : else if(event.wheel.y < 0)
408 : {
409 0 : processkey(Key_ScrollDown, true, map);
410 0 : processkey(Key_ScrollDown, false, map);
411 : }
412 0 : break;
413 : }
414 0 : case SDL_CONTROLLERBUTTONDOWN:
415 : case SDL_CONTROLLERBUTTONUP:
416 : {
417 0 : processkey(event.cbutton.button + (1<<29), event.cbutton.state == SDL_PRESSED, map); //1<<30 is used for non-char keybinds, 1<<29 is an unused part of the namespace
418 0 : break;
419 : }
420 0 : case SDL_CONTROLLERAXISMOTION:
421 : {
422 0 : uint axis = event.caxis.axis;
423 0 : uint value = event.caxis.value;
424 0 : switch(axis)
425 : {
426 0 : case 0: //left x axis
427 : //these processkeys will push back 1<<28 to keep out of the way of other binds
428 0 : if(lpad.x >= strafethreshhold)
429 : {
430 0 : processkey(axis + (1<<28), true, map); //strafe in +x direction
431 : }
432 0 : else if(lpad.x <= -strafethreshhold)
433 : {
434 0 : processkey(axis + (1<<28) + inverseindex, true, map); //-x dir
435 : }
436 : else //no input
437 : {
438 0 : processkey(axis + (1<<28), false, map);
439 0 : processkey(axis + (1<<28) + inverseindex, false, map);
440 : }
441 0 : lpad.x = value;
442 0 : break;
443 0 : case 1: //left y axis
444 : //these processkeys will push back 1<<28 to keep out of the way of other binds
445 0 : if(lpad.y >= strafethreshhold)
446 : {
447 0 : processkey(axis + (1<<28), true, map);
448 : }
449 0 : else if(lpad.y <= -strafethreshhold)
450 : {
451 0 : processkey(axis + (1<<28) + inverseindex, true, map);
452 : }
453 : else
454 : {
455 0 : processkey(axis + (1<<28), false, map);
456 0 : processkey(axis + (1<<28) + inverseindex, false, map);
457 : }
458 0 : lpad.y = value;
459 0 : break;
460 0 : case 2: //right x axis
461 0 : rpad.x = value;
462 0 : break;
463 0 : case 3: //right y axis
464 0 : rpad.y = value;
465 0 : break;
466 0 : case 4: //left trigger
467 : //these processkeys will push back 1<<28 to keep out of the way of other binds
468 0 : if(triggers.x >= minthreshhold && value < minthreshhold)
469 : {
470 0 : processkey(axis + (1<<28), false, map); //left trigger has been unpressed
471 : }
472 0 : else if(triggers.x <= maxthreshhold && value > maxthreshhold)
473 : {
474 0 : processkey(axis + (1<<28), true, map); //left trigger has been pressed
475 : }
476 0 : triggers.x = value;
477 0 : break;
478 0 : case 5: //right trigger
479 0 : if(triggers.y >= minthreshhold && value < minthreshhold)
480 : {
481 0 : processkey(axis + (1<<28), false, map); //right trigger has been unpressed
482 : }
483 0 : else if(triggers.y <= maxthreshhold && value > maxthreshhold)
484 : {
485 0 : processkey(axis + (1<<28), true, map); //right trigger has been pressed
486 : }
487 0 : triggers.y = value;
488 : }
489 0 : break;
490 : }
491 : }
492 : }
493 : { //scoping brakets
494 : static int oldmillis;
495 0 : int delta = lastmillis-oldmillis;
496 0 : if(std::abs(rpad.x) > minthreshhold || std::abs(rpad.y) > minthreshhold)
497 : {
498 0 : mousemove(delta*rpad.x/mousemovescale, delta*rpad.y/mousemovescale);
499 : }
500 0 : oldmillis = lastmillis;
501 : }
502 0 : if(mousemoved)
503 : {
504 0 : resetmousemotion();
505 : }
506 0 : }
|