LCOV - code coverage report
Current view: top level - engine/interface - input.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 11 232 4.7 %
Date: 2024-11-22 05:07:59 Functions: 2 11 18.2 %

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

Generated by: LCOV version 1.14