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: 2025-02-21 06:59:27 Functions: 18.2 % 11 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            0 :                 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 2.0-1