LCOV - code coverage report
Current view: top level - engine/interface - input.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 4.7 % 233 11
Test Date: 2026-01-01 07:45:46 Functions: 18.2 % 11 2

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

Generated by: LCOV version 2.0-1