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-05-09 04:28:55 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              : static std::vector<SDL_Event> events;
     105              : 
     106            0 : static 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              : //used in iengine
     141            0 : bool interceptkey(int sym)
     142              : {
     143              :     static int lastintercept = SDLK_UNKNOWN;
     144            0 :     int len = lastintercept == sym ? static_cast<int>(events.size()) : 0;
     145              :     SDL_Event event;
     146            0 :     while(pollevent(event))
     147              :     {
     148            0 :         switch(event.type)
     149              :         {
     150            0 :             case SDL_MOUSEMOTION:
     151              :             {
     152            0 :                 break;
     153              :             }
     154            0 :             default:
     155              :             {
     156            0 :                 pushevent(event);
     157            0 :                 break;
     158              :             }
     159              :         }
     160              :     }
     161            0 :     lastintercept = sym;
     162            0 :     if(sym != SDLK_UNKNOWN)
     163              :     {
     164            0 :         for(size_t i = len; i < events.size(); i++)
     165              :         {
     166            0 :             if(events[i].type == SDL_KEYDOWN && events[i].key.keysym.sym == sym)
     167              :             {
     168            0 :                 events.erase(events.begin() + i);
     169            0 :                 return true;
     170              :             }
     171              :         }
     172              :     }
     173            0 :     return false;
     174              : }
     175              : 
     176            0 : void ignoremousemotion()
     177              : {
     178              :     SDL_Event e;
     179            0 :     SDL_PumpEvents();
     180              :     //go through each event and do nothing
     181            0 :     while(SDL_PeepEvents(&e, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION))
     182              :     {
     183              :         //(empty body)
     184              :     }
     185            0 : }
     186              : 
     187              : /**
     188              :  * @brief Resets the cursor to the middle of the screen
     189              :  *
     190              :  * Moves cursor to the center of the screen, regardless of where it is. This corresponds
     191              :  * to the position at half of (hudw(), hudh()).
     192              :  */
     193            0 : static void resetmousemotion()
     194              : {
     195            0 :     if(grabinput && !relativemouse && !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
     196              :     {
     197            0 :         SDL_WarpMouseInWindow(screen, hudw() / 2, hudh() / 2); //move to middle of screen
     198              :     }
     199            0 : }
     200              : 
     201              : /**
     202              :  * @brief Gets cursor information from SDL2
     203              :  *
     204              :  * gets the information from SDL2 about the amount by which the cursor has moved, and
     205              :  * and changes the passed dx, dy values by that amount
     206              :  *
     207              :  * @param dx reference to the mouse x delta
     208              :  * @param dy reference to the mouse y delta
     209              :  */
     210            0 : static void checkmousemotion(int &dx, int &dy)
     211              : {
     212            0 :     for(size_t i = 0; i < events.size(); i++)
     213              :     {
     214            0 :         SDL_Event &event = events[i];
     215            0 :         if(event.type != SDL_MOUSEMOTION)
     216              :         {
     217            0 :             if(i > 0)
     218              :             {
     219            0 :                 events.erase(events.begin(), events.begin() + i);
     220              :             }
     221            0 :             return;
     222              :         }
     223            0 :         dx += event.motion.xrel;
     224            0 :         dy += event.motion.yrel;
     225              :     }
     226            0 :     events.clear();
     227              :     SDL_Event event;
     228            0 :     while(pollevent(event))
     229              :     {
     230            0 :         if(event.type != SDL_MOUSEMOTION)
     231              :         {
     232            0 :             events.push_back(event);
     233            0 :             return;
     234              :         }
     235            0 :         dx += event.motion.xrel;
     236            0 :         dy += event.motion.yrel;
     237              :     }
     238              : }
     239              : 
     240              : 
     241              : //handle different input types
     242              : // map: which keymap to map the pressed key to
     243            0 : void checkinput(int map)
     244              : {
     245            0 :     constexpr uint minthreshhold = 5000, // minimum value to register inputs
     246            0 :                    maxthreshhold = 27000, // maximum value to register inputs to triggers
     247            0 :                    inverseindex = 16,
     248            0 :                    mousemovescale = 25000; //how much to divide mouse movement by from joystick input
     249              : 
     250            0 :     constexpr int strafethreshhold = 16384; //value when to assign movement in strafe pad, signed to allow unary negation
     251              : 
     252              :     //carry over joystick states
     253            0 :     static vec2 lpad,
     254            0 :                 rpad,
     255            0 :                 triggers; //x = left, y = right
     256              :     SDL_Event event;
     257            0 :     bool mousemoved = false;
     258            0 :     while(events.size() || pollevent(event))
     259              :     {
     260            0 :         if(events.size())
     261              :         {
     262            0 :             event = events[0];
     263            0 :             events.erase(events.begin());
     264              :         }
     265            0 :         switch(event.type)
     266              :         {
     267            0 :             case SDL_QUIT:
     268              :             {
     269            0 :                 exit(EXIT_SUCCESS);
     270              :                 return;
     271              :             }
     272            0 :             case SDL_TEXTINPUT:
     273              :             {
     274            0 :                 if(textinputmask && static_cast<int>(event.text.timestamp-textinputtime) >= textinputfilter)
     275              :                 {
     276            0 :                     size_t len = std::strlen(event.text.text);
     277            0 :                     if(len > 0)
     278              :                     {
     279            0 :                         processtextinput(reinterpret_cast<const char *>(event.text.text), len);
     280              :                     }
     281              :                 }
     282            0 :                 break;
     283              :             }
     284            0 :             case SDL_KEYDOWN:
     285              :             case SDL_KEYUP:
     286              :             {
     287            0 :                 if(keyrepeatmask || !event.key.repeat)
     288              :                 {
     289            0 :                     processkey(event.key.keysym.sym, event.key.state==SDL_PRESSED, map);
     290              :                 }
     291            0 :                 break;
     292              :             }
     293            0 :             case SDL_WINDOWEVENT:
     294              :             {
     295            0 :                 switch(event.window.event)
     296              :                 {
     297            0 :                     case SDL_WINDOWEVENT_CLOSE:
     298              :                     {
     299            0 :                         exit(EXIT_SUCCESS);
     300              :                         break;
     301              :                     }
     302            0 :                     case SDL_WINDOWEVENT_FOCUS_GAINED:
     303              :                     {
     304            0 :                         shouldgrab = true;
     305            0 :                         break;
     306              :                     }
     307            0 :                     case SDL_WINDOWEVENT_ENTER:
     308              :                     {
     309            0 :                         inputgrab(grabinput = true);
     310            0 :                         break;
     311              :                     }
     312            0 :                     case SDL_WINDOWEVENT_LEAVE:
     313              :                     case SDL_WINDOWEVENT_FOCUS_LOST:
     314              :                     {
     315            0 :                         inputgrab(grabinput = false);
     316            0 :                         break;
     317              :                     }
     318            0 :                     case SDL_WINDOWEVENT_MINIMIZED:
     319              :                     {
     320            0 :                         minimized = true;
     321            0 :                         break;
     322              :                     }
     323            0 :                     case SDL_WINDOWEVENT_MAXIMIZED:
     324              :                     case SDL_WINDOWEVENT_RESTORED:
     325              :                     {
     326            0 :                         minimized = false;
     327            0 :                         break;
     328              :                     }
     329            0 :                     case SDL_WINDOWEVENT_RESIZED:
     330              :                     {
     331            0 :                         break;
     332              :                     }
     333            0 :                     case SDL_WINDOWEVENT_SIZE_CHANGED:
     334              :                     {
     335            0 :                         SDL_GetWindowSize(screen, &screenw, &screenh);
     336            0 :                         if(!(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
     337              :                         {
     338              :                             //need to cast enums to ints for std's clamp implementation
     339            0 :                             scr_w = std::clamp(hudw(), static_cast<int>(SCR_MINW), static_cast<int>(SCR_MAXW));
     340            0 :                             scr_h = std::clamp(hudh(), static_cast<int>(SCR_MINH), static_cast<int>(SCR_MAXH));
     341              :                         }
     342            0 :                         gl_resize();
     343            0 :                         break;
     344              :                     }
     345              :                 }
     346            0 :                 break;
     347              :             }
     348            0 :             case SDL_MOUSEMOTION:
     349              :             {
     350            0 :                 if(grabinput)
     351              :                 {
     352            0 :                     int dx = event.motion.xrel,
     353            0 :                         dy = event.motion.yrel;
     354            0 :                     checkmousemotion(dx, dy);
     355            0 :                     if(!UI::movecursor(dx, dy))
     356              :                     {
     357            0 :                         mousemove(dx, dy);
     358              :                     }
     359            0 :                     mousemoved = true;
     360              :                 }
     361            0 :                 else if(shouldgrab)
     362              :                 {
     363            0 :                     inputgrab(grabinput = true);
     364              :                 }
     365            0 :                 break;
     366              :             }
     367            0 :             case SDL_MOUSEBUTTONDOWN:
     368              :             case SDL_MOUSEBUTTONUP:
     369              :             {
     370            0 :                 switch(event.button.button)
     371              :                 {
     372            0 :                     case SDL_BUTTON_LEFT:
     373              :                     {
     374            0 :                         processkey(Key_Left, event.button.state==SDL_PRESSED, map);
     375            0 :                         break;
     376              :                     }
     377            0 :                     case SDL_BUTTON_MIDDLE:
     378              :                     {
     379            0 :                         processkey(Key_Middle, event.button.state==SDL_PRESSED, map);
     380            0 :                         break;
     381              :                     }
     382            0 :                     case SDL_BUTTON_RIGHT:
     383              :                     {
     384            0 :                         processkey(Key_Right, event.button.state==SDL_PRESSED, map);
     385            0 :                         break;
     386              :                     }
     387            0 :                     case SDL_BUTTON_X1:
     388              :                     {
     389            0 :                         processkey(Key_X1, event.button.state==SDL_PRESSED, map);
     390            0 :                         break;
     391              :                     }
     392            0 :                     case SDL_BUTTON_X2:
     393              :                     {
     394            0 :                         processkey(Key_X2, event.button.state==SDL_PRESSED, map);
     395            0 :                         break;
     396              :                     }
     397              :                 }
     398            0 :                 break;
     399              :             }
     400            0 :             case SDL_MOUSEWHEEL:
     401              :             {
     402              :                 //up
     403            0 :                 if(event.wheel.y > 0)
     404              :                 {
     405            0 :                     processkey(Key_ScrollUp, true, map);
     406            0 :                     processkey(Key_ScrollUp, false, map);
     407              :                 }
     408              :                 //down
     409            0 :                 else if(event.wheel.y < 0)
     410              :                 {
     411            0 :                     processkey(Key_ScrollDown, true, map);
     412            0 :                     processkey(Key_ScrollDown, false, map);
     413              :                 }
     414            0 :                 break;
     415              :             }
     416            0 :             case SDL_CONTROLLERBUTTONDOWN:
     417              :             case SDL_CONTROLLERBUTTONUP:
     418              :             {
     419            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
     420            0 :                 break;
     421              :             }
     422            0 :             case SDL_CONTROLLERAXISMOTION:
     423              :             {
     424            0 :                 uint axis = event.caxis.axis,
     425            0 :                      value = event.caxis.value;
     426            0 :                 switch(axis)
     427              :                 {
     428            0 :                     case 0: //left x axis
     429              :                         //these processkeys will push back 1<<28 to keep out of the way of other binds
     430            0 :                         if(lpad.x >= strafethreshhold)
     431              :                         {
     432            0 :                             processkey(axis + (1<<28), true, map); //strafe in +x direction
     433              :                         }
     434            0 :                         else if(lpad.x <= -strafethreshhold)
     435              :                         {
     436            0 :                             processkey(axis + (1<<28) + inverseindex, true, map); //-x dir
     437              :                         }
     438              :                         else //no input
     439              :                         {
     440            0 :                             processkey(axis + (1<<28), false, map);
     441            0 :                             processkey(axis + (1<<28) + inverseindex, false, map);
     442              :                         }
     443            0 :                         lpad.x = value;
     444            0 :                         break;
     445            0 :                     case 1: //left y axis
     446              :                         //these processkeys will push back 1<<28 to keep out of the way of other binds
     447            0 :                         if(lpad.y >= strafethreshhold)
     448              :                         {
     449            0 :                             processkey(axis + (1<<28), true, map);
     450              :                         }
     451            0 :                         else if(lpad.y <= -strafethreshhold)
     452              :                         {
     453            0 :                             processkey(axis + (1<<28) + inverseindex, true, map);
     454              :                         }
     455              :                         else
     456              :                         {
     457            0 :                             processkey(axis + (1<<28), false, map);
     458            0 :                             processkey(axis + (1<<28) + inverseindex, false, map);
     459              :                         }
     460            0 :                         lpad.y = value;
     461            0 :                         break;
     462            0 :                     case 2: //right x axis
     463            0 :                         rpad.x = value;
     464            0 :                         break;
     465            0 :                     case 3: //right y axis
     466            0 :                         rpad.y = value;
     467            0 :                         break;
     468            0 :                     case 4: //left trigger
     469              :                         //these processkeys will push back 1<<28 to keep out of the way of other binds
     470            0 :                         if(triggers.x >= minthreshhold && value < minthreshhold)
     471              :                         {
     472            0 :                             processkey(axis + (1<<28), false, map); //left trigger has been unpressed
     473              :                         }
     474            0 :                         else if(triggers.x <= maxthreshhold && value > maxthreshhold)
     475              :                         {
     476            0 :                             processkey(axis + (1<<28), true, map); //left trigger has been pressed
     477              :                         }
     478            0 :                         triggers.x = value;
     479            0 :                         break;
     480            0 :                     case 5: //right trigger
     481            0 :                         if(triggers.y >= minthreshhold && value < minthreshhold)
     482              :                         {
     483            0 :                             processkey(axis + (1<<28), false, map); //right trigger has been unpressed
     484              :                         }
     485            0 :                         else if(triggers.y <= maxthreshhold && value > maxthreshhold)
     486              :                         {
     487            0 :                             processkey(axis + (1<<28), true, map); //right trigger has been pressed
     488              :                         }
     489            0 :                         triggers.y = value;
     490              :                 }
     491            0 :                 break;
     492              :             }
     493              :         }
     494              :     }
     495              :     { //scoping brakets
     496              :         static int oldmillis;
     497            0 :         int delta = lastmillis-oldmillis;
     498            0 :         if(std::abs(rpad.x) > minthreshhold || std::abs(rpad.y) > minthreshhold)
     499              :         {
     500            0 :             mousemove(delta*rpad.x/mousemovescale, delta*rpad.y/mousemovescale);
     501              :         }
     502            0 :         oldmillis = lastmillis;
     503              :     }
     504            0 :     if(mousemoved)
     505              :     {
     506            0 :         resetmousemotion();
     507              :     }
     508            0 : }
        

Generated by: LCOV version 2.0-1