• Advertisement
Sign in to follow this  

SDL2 - Unexpected Behavior When Hot Swapping Gamepads If One Or More Is Asleep

Recommended Posts

I created a small test application where I handle the SDL_CONTROLLERDEVICEADDED and SDL_CONTROLLERDEVICEREMOVED events, and output information about the controller being added/removed, based on the information in this blog.

I have two controllers connected to my PC, an Xbox 360 controller and a Logitech controller. What I found is that if they are both active and awake it produces the expected output, but if either one or both of them are asleep, it outputs the wrong name under certain conditions. I don't want to be overly verbose and go over every configuration I tested, but one example is if I start the application with both controllers in sleep mode, it fires the SDL_CONTROLLERDEVICEADDED for the Logitech controller but not the Xbox controller at application startup (The event for the Xbox control only happens when it is awakened), and then when I unplug/plug in the Logitech controller's USB receiver, it shows the device name as "X360" every time, until I wake them both up, at which point it outputs the correct name again.

Perhaps I'm worrying too much about this, as most people probably don't mix and match controllers and likely only use one, but I just found it curious. I'm wondering if it might be a bug in SDL2, or if maybe there is some insight someone might have related to xbox controllers and how they behave in sleep mode.

 

Here is the relevant code if anyone's interested:

struct SdlGamepad
{
	SDL_GameController*	controller;
	SDL_Joystick*		joystick;
	SDL_JoystickID		joystickId;
	std::string		deviceName;
	int			deviceId;
};



std::map<SDL_JoystickID, SdlGamepad> joystickIdToGamepadMap;

SDL_Event event;

bool hasQuit = false;

while (hasQuit == false)
{
	while (SDL_PollEvent(&event))
	{
		switch (event.type)
		{
			case SDL_CONTROLLERDEVICEADDED:
			{
				std::cout << "Controller device added." << std::endl;

				if (SDL_IsGameController(event.cdevice.which))
				{
					SdlGamepad gamepad;

					gamepad.controller = SDL_GameControllerOpen(event.cdevice.which);
					gamepad.joystick = SDL_GameControllerGetJoystick(gamepad.controller);
					gamepad.joystickId = SDL_JoystickInstanceID(gamepad.joystick);

					std::string deviceName = SDL_GameControllerName(gamepad.controller);

					gamepad.deviceName = deviceName;
					gamepad.deviceId = event.cdevice.which;

					joystickIdToGamepadMap[gamepad.joystickId] = gamepad;

					std::cout 
						<< "Controller device name = " << gamepad.deviceName << std::endl
						<< "Controller device ID = " << gamepad.deviceId << std::endl
						<< "Joystick device ID = " << gamepad.joystickId << std::endl << std::endl;

				}

				break;
			}
			case SDL_CONTROLLERDEVICEREMOVED:
			{
				std::cout << "Controller device removed." << std::endl;

				SdlGamepad gamepad = joystickIdToGamepadMap[event.cdevice.which];

				std::cout
					<< "Controller device name = " << gamepad.deviceName << std::endl
					<< "Controller device ID = " << gamepad.deviceId << std::endl
					<< "Joystick device ID = " << gamepad.joystickId << std::endl << std::endl;
  
				SDL_GameControllerClose(gamepad.controller);

				joystickIdToGamepadMap.erase(event.cdevice.which);
  
				break;
			}
			case SDL_QUIT:
			{
				hasQuit = true;
				break;
			}
		}
	}
}

 

Share this post


Link to post
Share on other sites
Advertisement

Perhaps I'm misunderstanding. I'm using the joystick ID as the map key, obtained as the above link says, by calling SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(myController)), albeit factored out into multiple lines. Is that not correct?

Share this post


Link to post
Share on other sites

Your code looks right. The code that is relevant for this particular can be condensed to:

SDL_Event event;
while (SDL_PollEvent(&event))
{
  switch (event.type)
  {
    case SDL_CONTROLLERDEVICEADDED:
    {
      std::cout << "Controller device added." << std::endl;
      if (SDL_IsGameController(event.cdevice.which))
      {
        auto controller = SDL_GameControllerOpen(event.cdevice.which);
        std::cout << "Controller device name = " << SDL_GameControllerName(gamepad.controller) << std::endl;
      }
      break;
    }
  }
}

This should still reproduce the same problem. If it does, I suggest that you report this as a bug in SDL.

Edited by Ansou

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By smit patil
      I am at the first year of diploma and I am planing to make a nice small 2D or 3D First Person Shooter game . As I mentioned I am just at first year in IT ,I Dont know much about game developing but I do know lots of more things that normal 1st and even 3rd year students . 
      I  Know Java,C,C++ and little bit of python and GTK+ framework . I am currently trying to learn SDL and Qt with C++. I will also happily learn new technology if required to make games.
      Now my main Question's are:-
      How much time will it take to make SMALL 2D game as a single person in SDL while LEARNING SDL ?
      How much time will it take to make SMALL 3D game as a single person in SDL while LEARNING SDL ?
      Will learning Qt help me make games faster or with High performance ?
      My main platform is Linux and Android.I don't care if It runs on windows or Mac but it should run fine on Linux .
       
      Thanks in advance
       
       
    • By starfruit64
      Hello,
      I'm programming a simple rougelike video game for fun. I'm running into a very difficult problem with my room generation code, specifically the part that deals with the maze-like hallway to connect the randomly placed rooms inside a dungeon, using the depth-first search method. The code I've written is kinda long and probably hard to read, but I seriously cannot figure out what is causing the issue so try to bear with me please.
      Below is the header file for the Floor class, which generates the floor of the dungeon.
      #ifndef FLOOR_H_ #define FLOOR_H_ #include <iostream> #include <vector> #include <algorithm> #include "Cell.h" class Floor { private: int attempts; //Amount of attempts it tries to place a room before giving up. More attempts = more cramped. int maxdoor; //Maximum amount of doors on each room int maxroom; //Maximum number of rooms. 0 for 999. int maxheight, minheight; int maxwidth, minwidth; int maxbigroom; std::vector<std::vector<Cell> > region; //A collection of regions. A region is a collection of cells (rooms & hallway) std::vector<Cell> temp_region; //Used to fill the region. std::vector<Cell> temp_maze; std::vector<Cell> maze; public: Floor(); void init(int attempts, int maxdoor, int maxroom, int maxwidth, int minwidth, int maxheight, int minheight, int maxbigroom); void gen(Cell cell[120][60], SDL_Texture* tex); bool check_walls(Cell cell[120][60], int* x, int* y); }; #endif The main thing that is important in this header is the temp_maze vector, which is used to keep track of the current section of the maze that has been carved out, so that when the maze hits a dead end it can back track. is the Cell is a class that represents a cell in the 2D grid that the player can move on. For the issue of this post, just think of each Cell as having an X integer, a Y integer, and a Bool to keep track of whether or not the cell is opened up. 
      Below is the part of the gen function that deals with the hallway generation.
      //Hallway Generation int cur_x, cur_y; //Current cell being tested bool time_to_break; for (int i = 1; i < 119; i++) { for (int j = 1; j < 59; j++) { if (cell[i][j].return_blockade() == true && cell[i + 1][j].return_blockade() == true && cell[i][j + 1].return_blockade() == true && cell[i - 1][j].return_blockade() == true && cell[i][j - 1].return_blockade() == true) { //Start of a path cur_x = i; cur_y = j; while (true) { cell[cur_x][cur_y].set_bloc(false); //Opens up cell cell[cur_x][cur_y].set_tex(tex); cell[cur_x][cur_y].set_default_tex(tex); temp_maze.push_back(cell[cur_x][cur_y]); if (check_walls(cell, &cur_x, &cur_y) == false) { std::cout << "Entering nospace" << std::endl; std::cout << "Size of k: " << temp_maze.size() << std::endl; for (int k = temp_maze.size() - 1; k > -1; k--) { int xte = temp_maze[k].return_rect().x / 15; int yte = temp_maze[k].return_rect().y / 15; std::cout << "k: " << k << std::endl; std::cout << "rect_x: "<< temp_maze[k].return_rect().x<< " rect_y : "<< temp_maze[k].return_rect().y<< std::endl; std::cout << "x_te: " << xte << " y_te: " << yte << std::endl; if (check_walls(cell, &xte, &yte) == true) { cur_x = xte; cur_y = yte; std::cout << "Exiting nospace via newblock" << std::endl; break; } if (k == 0) { time_to_break = true; std::cout << "Exiting nospace via noviableblock" << std::endl; break; } } } if (time_to_break == true) { time_to_break = false; temp_maze.clear(); break; } } } } } The broad idea is that, starting from the top-left cell, we scroll through them until we find a cell that is not blocked, nor has any neighbors that are blocked. Than we open that cell up, add it to the temp_maze vector, and check whether it has any viable neighbors. If it does, it moves cur_y and cur_x to that new cell and repeats. If it doesn't have any viable neighbors, than it goes through the temp_maze vector until either it finds a cell that has viable neighbors, or it reaches the end of the vector. This is accomplished with the check_walls function.
      bool Floor::check_walls(Cell cell[120][60], int* x, int* y) { int ori_x = *x; int ori_y = *y; std::cout << "Entering checkwalls with x: " << ori_x << " y: " << ori_y << std::endl; if (cell[ori_x][ori_y].return_blockade() == true) { std::cout << "Checkwalls returns false due to being blocked" << std::endl; return false; } while (true) { *x = ori_x; *y = ori_y; bool upblock, rightblock, downblock, leftblock; int sidecount = 0; int dir = rand() % 4; if (dir == 0) { *y = (*y) - 1; } else if (dir == 1) { *x = (*x) - 1; } else if (dir == 2) { *y = (*y) + 1; } else if (dir == 3) { *x = (*x) + 1; } if (*x <= 0 || *y <= 0) { sidecount = 8; dir = 5; *x = ori_x; *y = ori_y; } if (cell[*x + 1][*y].return_blockade() == false) { sidecount++; } if (cell[*x - 1][*y].return_blockade() == false) { sidecount++; } if (cell[*x][*y + 1].return_blockade() == false) { sidecount++; } if (cell[*x][*y - 1].return_blockade() == false) { sidecount++; } if (sidecount <= 1) { std::cout << "Checkwalls returns true" << std::endl; return true; } else { if (dir == 0) { upblock = true; } else if (dir == 1) { leftblock = true; } else if (dir == 2) { downblock = true; } else if (dir == 3) { rightblock = true; } } if (upblock == true && leftblock == true && downblock == true && rightblock == true) { *x = ori_x; *y = ori_y; std::cout << "Checkwalls returns false" << std::endl; return false; } } } The basic idea here is that if one of the neighbors are available, than it moves x & y to that cell and return true, and if there are none, than return false.
       
      The problem is that when I run this the temp_maze  function seems to get filled with the same cell, and time_to_break never gets set to true, as the vector seems to never end. Once again I understand this is kind of a lot but I have looked at this for a couple days now and have no idea what i am doing wrong, so any help would be very much appreciated.
    • By reenigne
      For those that don't know me. I am the individual who's two videos are listed here under setup for https://wiki.libsdl.org/Tutorials
      I also run grhmedia.com where I host the projects and code for the tutorials I have online.
      Recently, I received a notice from youtube they will be implementing their new policy in protecting video content as of which I won't be monetized till I meat there required number of viewers and views each month.

      Frankly, I'm pretty sick of youtube. I put up a video and someone else learns from it and puts up another video and because of the way youtube does their placement they end up with more views.
      Even guys that clearly post false information such as one individual who said GLEW 2.0 was broken because he didn't know how to compile it. He in short didn't know how to modify the script he used because he didn't understand make files and how the requirements of the compiler and library changes needed some different flags.

      At the end of the month when they implement this I will take down the content and host on my own server purely and it will be a paid system and or patreon. 

      I get my videos may be a bit dry, I generally figure people are there to learn how to do something and I rather not waste their time. 
      I used to also help people for free even those coming from the other videos. That won't be the case any more. I used to just take anyone emails and work with them my email is posted on the site.

      I don't expect to get the required number of subscribers in that time or increased views. Even if I did well it wouldn't take care of each reoccurring month.
      I figure this is simpler and I don't plan on putting some sort of exorbitant fee for a monthly subscription or the like.
      I was thinking on the lines of a few dollars 1,2, and 3 and the larger subscription gets you assistance with the content in the tutorials if needed that month.
      Maybe another fee if it is related but not directly in the content. 
      The fees would serve to cut down on the number of people who ask for help and maybe encourage some of the people to actually pay attention to what is said rather than do their own thing. That actually turns out to be 90% of the issues. I spent 6 hours helping one individual last week I must have asked him 20 times did you do exactly like I said in the video even pointed directly to the section. When he finally sent me a copy of the what he entered I knew then and there he had not. I circled it and I pointed out that wasn't what I said to do in the video. I didn't tell him what was wrong and how I knew that way he would go back and actually follow what it said to do. He then reported it worked. Yea, no kidding following directions works. But hey isn't alone and well its part of the learning process.

      So the point of this isn't to be a gripe session. I'm just looking for a bit of feed back. Do you think the fees are unreasonable?
      Should I keep the youtube channel and do just the fees with patreon or do you think locking the content to my site and require a subscription is an idea.

      I'm just looking at the fact it is unrealistic to think youtube/google will actually get stuff right or that youtube viewers will actually bother to start looking for more accurate videos. 
    • By Jeff de Anda
      Question about SDL_Rect scope.
      Say I have a member function like so.
      void drawRect(int x, int y, int w, int y) { SDL_Rect r = {x, y, w, h}; SDL_RenderDrawRect(m_renderer, &r); } Will it cause problems if SDL_Rect goes out of scope before calling SDL_RenderPresent?
      I cannot tell if the values are copied from SDL_Rect when calling SDL_RenderDrawRect or if internally SDL is storing the pointer until SDL_RenderPresent is called.
      Thanks!
    • By alex1997
      Hey, I've a minor problem that prevents me from moving forward with development and looking to find a way that could solve it. Overall, I'm having a sf::VertexArray object and looking to reander a shader inside its area. The problem is that the shader takes the window as canvas and only becomes visible in the object range which is not what I'm looking for.. 
      Here's a stackoverflow links that shows the expected behaviour image. Any tips or help is really appreciated. I would have accepted that answer, but currently it does not work with #version 330 ...
  • Advertisement