I'm adding support for the fbneo emulator to the retrobat emulator frontend. I'm running into a really tricky problem right now.
The method for the retrobat front end to exit the game is to use SDL2 to monitor the key event of the controller. If the hotkey is activated, the frontend sends an event to the game window and exits the game.
Within tens of seconds after the front end starts the game, the exit is normal. But after this time point, there will be problems with the front-end detection of the keys, and the hotkey keys will often not be triggered. Checking the retrobat’s code and logs, the monitoring mechanism of SDL seems to conflict with the fbneo emulator. It can't get the key event of the controller, or gets an incorrect value.
I don't know how to fix this, I'm not very familiar with both SDL2 and fbneo.
public void DoWork()
{
Joysticks joysticks = new Joysticks(_inputList);
SDL.SDL_SetHint(SDL.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
SDL.SDL_Init(SDL.SDL_INIT_JOYSTICK | SDL.SDL_INIT_VIDEO); // Since SDL 2.0.16, SDL_INIT_VIDEO is required or joystick events are not received....
SDL.SDL_InitSubSystem(SDL.SDL_INIT_JOYSTICK);
SDL.SDL_JoystickEventState(SDL.SDL_ENABLE);
int numJoysticks = SDL.SDL_NumJoysticks();
for (int i = 0; i < numJoysticks; i++)
joysticks.AddJoystick(i);
while (true)
{
if (_waitHandle.WaitOne(1))
break;
try
{
SDL.SDL_Event evt;
if (SDL.SDL_PollEvent(out evt) != 0)
{
if (evt.type == SDL.SDL_EventType.SDL_QUIT)
break;
switch (evt.type)
{
case SDL.SDL_EventType.SDL_JOYAXISMOTION:
{
const int DEADZONE = 500;
int initialValue = 0;
int normValue = 0;
if (Math.Abs(evt.jaxis.axisValue - initialValue) > DEADZONE)
{
if (evt.jaxis.axisValue - initialValue > 0)
normValue = 1;
else
normValue = -1;
}
var joy = joysticks[evt.jaxis.which];
if (joy != null)
{
var axis = joy.GetInput("axis", evt.jaxis.axis);
if (axis != null)
{
var axisName = axis.Name;
var revertedAxis = RevertedAxis(axisName);
int value = evt.jaxis.axisValue;
if (value != 0 && (Math.Abs(value) / value) == -axis.Value)
{
if (revertedAxis != axisName)
{
axisName = revertedAxis;
revertedAxis = axis.Name;
value = -value;
}
else
{
normValue = 0;
value = 0;
}
}
if (normValue != 0)
{
joy.State.Remove(revertedAxis);
joy.State.Add(axisName, value, true);
}
else
{
joy.State.Remove(revertedAxis);
joy.State.Remove(axisName);
}
}
}
}
break;
case SDL.SDL_EventType.SDL_JOYBUTTONDOWN:
case SDL.SDL_EventType.SDL_JOYBUTTONUP:
{
var joy = joysticks[evt.jbutton.which];
if (joy != null)
{
foreach (var conf in joy.GetButtons(evt.jbutton.button))
{
if (evt.jbutton.state == SDL.SDL_PRESSED)
joy.State.Add(conf.Name);
else
joy.State.Remove(conf.Name);
}
}
}
break;
case SDL.SDL_EventType.SDL_JOYHATMOTION:
{
var joy = joysticks[evt.jhat.which];
if (joy != null)
{
var up = joy.GetInput("hat", evt.jhat.hat, (int) SDL.SDL_HAT_UP);
if (up != null)
{
if ((evt.jhat.hatValue & SDL.SDL_HAT_UP) == SDL.SDL_HAT_UP)
joy.State.Add(up.Name);
else
joy.State.Remove(up.Name);
}
var right = joy.GetInput("hat", evt.jhat.hat, (int)SDL.SDL_HAT_RIGHT);
if (right != null)
{
if ((evt.jhat.hatValue & SDL.SDL_HAT_RIGHT) == SDL.SDL_HAT_RIGHT)
joy.State.Add(right.Name);
else
joy.State.Remove(right.Name);
}
var down = joy.GetInput("hat", evt.jhat.hat, (int)SDL.SDL_HAT_DOWN);
if (down != null)
{
if ((evt.jhat.hatValue & SDL.SDL_HAT_DOWN) == SDL.SDL_HAT_DOWN)
joy.State.Add(down.Name);
else
joy.State.Remove(down.Name);
}
var left = joy.GetInput("hat", evt.jhat.hat, (int)SDL.SDL_HAT_LEFT);
if (left != null)
{
if ((evt.jhat.hatValue & SDL.SDL_HAT_LEFT) == SDL.SDL_HAT_LEFT)
joy.State.Add(left.Name);
else
joy.State.Remove(left.Name);
}
}
}
break;
case SDL.SDL_EventType.SDL_JOYDEVICEREMOVED:
joysticks.RemoveJoystick(evt.jdevice.which);
break;
case SDL.SDL_EventType.SDL_JOYDEVICEADDED:
joysticks.AddJoystick(evt.jdevice.which);
break;
}
foreach (var joy in joysticks)
{
if (joy.State == joy.OldState)
continue;
ProcessJoystickState(joy.Controller, joy.State, joy.OldState);
joy.OldState = joy.State.Clone();
}
Thread.Sleep(1);
}
}
catch { }
}
foreach(var joy in joysticks)
joy.Close();
SDL.SDL_QuitSubSystem(SDL.SDL_INIT_JOYSTICK);
SDL.SDL_Quit();
}
I tried a few things like setting SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS to a higher priority; starting more systems and subsystems. Neither has any effect.
The keystroke log looks like this:
Hotkey a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b
The following log shows that it cannot respond to hotkey