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