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-06-04 05:03:08 Functions: 18.2 % 11 2

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

Generated by: LCOV version 2.0-1