Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[games] Mouse support #67

Merged
merged 1 commit into from
Nov 5, 2016

Conversation

fetzerch
Copy link

@fetzerch fetzerch commented Oct 28, 2016

Adds mouse support for Emulators like ScummVM or Dosbox. Fixes #6

Related changes:

Known issues:

static int m_mouseStateY;
for (std::vector<MOUSE::IMouseHandler*>::iterator it = m_mouseHandlers.begin(); it != m_mouseHandlers.end(); ++it)
{
if ((*it)->OnMouseMotion(newEvent.motion.x - m_mouseStateX, newEvent.motion.y - m_mouseStateY))
Copy link
Owner

@garbear garbear Oct 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XBMC_Event struct has fields for relative coordinates, why don't we use those? If we are given relative coords, and libretro requires relative coords, there shouldn't be any need to ever touch absolute positions. It should be possible to transfer dx and dy to libretro without any addition or subtraction.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find anthing that stores relative coordinates. there's xrel/yrel, but that's a different thing. From what I can see x/y are the absolute coordinate inside kodi window, xrel/yrel are the absolute coordinates relative to the screen 0,0 position. If kodi is running in fullscreen mode then they are the same. There are no other values set in https://github.com/xbmc/xbmc/blob/master/xbmc/windowing/WinEventsX11.cpp#L546. I could only find the relative position being calculated in MouseStat: https://github.com/xbmc/xbmc/blob/master/xbmc/input/MouseStat.cpp#L52.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, not a problem. Add a second mouse motion to IMouseHandler interface for absolute positions. Then create a IMouseHandler to wrap CGameClientMouse that accepts absolute coordinates and translates them to relative coordinates. The handler should be placed in xbmc/input/mouse/generic because this is a class that describes generic behavior of a mouse. Our goal is to isolate all mouse handling logic so that it doesn't become entangled with any other dependencies.

handled = true;
}
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you refactor the above code to only use 1 for loop?

game_input_event event;

event.type = GAME_INPUT_EVENT_DIGITAL_BUTTON;
event.port = -1;
Copy link
Author

@fetzerch fetzerch Oct 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here i'm not sure if that's the correct event type / port. Because the code in https://github.com/kodi-game/game.libretro/pull/7/files#diff-97dc480240fc0f2c40a1732397d7adecR148 feels like a hack now. (checking for -1)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup. total hack and something we should avoid

@garbear
Copy link
Owner

garbear commented Oct 29, 2016

I've rethought our approach. Let's create mice controller profiles that can be mapped in the controller dialog.

Then we add OpenMouse() callback to the Game API similar to OpenPort(). That way, DOS can capture both the mouse and 2 controller ports. We can add #defines for GAME_PORT_MOUSE and GAME_PORT_KEYBOARD to identify if it is mouse, joystick or keyboard input.

@garbear
Copy link
Owner

garbear commented Oct 29, 2016

Don't add OpenMouse() to the game API, i realized that mouse support is only specified in addon.xml so we should open the mouse in CGameClient like we're doing now. But add the port #defines to detect mouse or joystick. Then you can use the port as a key in CLibretroInputDevice.

@garbear
Copy link
Owner

garbear commented Oct 29, 2016

screen shot 2016-10-29 at 11 57 03 am

It's a little weird having a mouse in controller configuration, we should probably modify the controller dialog to be able to show only controllers or only the mouse

@fetzerch
Copy link
Author

Mouse movement is working again (with https://github.com/fetzerch/game.libretro.scummvm/tree/mouse, and https://github.com/fetzerch/game.libretro/tree/mouse). Couldn't yet get the buttons to work. They seem to be registered in game.libretro LibretroDeviceInput and also buttons are queried by the core but they don't trigger any action it seems.

I'll do the refactoring of xbmc/input/InputManager.cpp once it's working again.

One thing I noticed also is that only the first matching controller mapping is taken into account, hence I think that the joypad is no longer functioning. Not sure if there's a quick workaround.

Btw: In case you also want to try this, just load scummvm in standalone mode. It has the ingame menu where you can use the mouse already without any games registered.

@garbear
Copy link
Owner

garbear commented Oct 31, 2016

@fetzerch I sent you a fix that refactors some stuff out of CInputManager

}

void CInputManager::UnregisterMouseHandler(MOUSE::IMouseInputHandler* handler)
{
auto it = m_mouseHandlers.find(handler);
auto it = std::find_if(m_mouseHandlers.begin(), m_mouseHandlers.end(),
[handler](const std::pair<MOUSE::IMouseInputHandler*, std::unique_ptr<MOUSE::IMouseDriverHandler>>& element) { return element.first == handler; });
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you break this up into multiple lines?

auto it = std::find_if(m_mouseHandlers.begin(), m_mouseHandlers.end(),
  [handler](const std::pair<MOUSE::IMouseInputHandler*, std::unique_ptr<MOUSE::IMouseDriverHandler>>& element)
  {
    return element.first == handler;
  });

if (it == m_mouseHandlers.end())
m_mouseHandlers[handler].reset(new MOUSE::CMouseInputHandling(handler, m_mouseButtonMap.get()));
m_mouseHandlers.emplace_back(std::make_pair(handler, std::unique_ptr<MOUSE::IMouseDriverHandler>(new MOUSE::CMouseInputHandling(handler, m_mouseButtonMap.get()))));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want mouse handlers to be processed in the same order as RegisterMouseHandler() is called, or should priority be given to the most-recently registered mouse handler?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd find it easier to understand if they are called in the registered order. If there's a reason for doing it the other way, we can change it.

@@ -284,7 +284,7 @@ class CInputManager : public ISettingCallback
CCriticalSection m_actionMutex;

std::vector<KEYBOARD::IKeyboardHandler*> m_keyboardHandlers;
std::map<MOUSE::IMouseInputHandler*, std::unique_ptr<MOUSE::IMouseDriverHandler>> m_mouseHandlers;
std::vector<std::pair<MOUSE::IMouseInputHandler*, std::unique_ptr<MOUSE::IMouseDriverHandler>>> m_mouseHandlers;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest:

typedef std::pair<MOUSE::IMouseInputHandler*, std::unique_ptr<MOUSE::IMouseDriverHandler>> MouseHandlerHandle;
std::vector<MouseHandlerHandle> m_mouseHandlers;

@@ -965,10 +965,12 @@ void CGameClient::CloseKeyboard(void)
void CGameClient::OpenMouse(void)
{
m_mouse.reset(new CGameClientMouse(this, m_pStruct));
OpenPort(GAME_INPUT_PORT_MOUSE);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This opens a joystick handler on the mouse port

@garbear
Copy link
Owner

garbear commented Nov 1, 2016

Can you link your PR description to all the other open PRs that have to do with mouse support? xbmc, game.libretro, game.libretro.scummvm, kodi-game-controllers... I think we're up to 4 repos now.

@fetzerch
Copy link
Author

fetzerch commented Nov 1, 2016

sure, Updated PR description


if (controller->LoadLayout())
{
CloseMouse();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mouse port is guaranteed to be open by now due to the check at the beginning of this function, so this line isn't needed

@garbear
Copy link
Owner

garbear commented Nov 1, 2016

Mouse button mapping doen't work (clicks are ignored in mapping dialog)

We can map mouse buttons the same way we map keyboard buttons. We need a layer similar to CJoystickEmulation in xbmc/input/mouse/generic. Then we have the mouse emulate a joystick, which will allow it to be mapped via peripheral.joystick.

We can add support for mapping mouse buttons by having CGUIConfigurationWizard::InstallHooks() register itself as a mouse handler. We could even add support for inverting the x and y axis or rotating the coordinate system.

@Hedda
Copy link

Hedda commented Nov 2, 2016

Guess this can also be copied to or used by the Trackball in the (Xgaming) X-Arcade Tankstick.

https://shop.xgaming.com/products/x-arcade-tankstick-trackball-usb-included

Trackball inside the X-Arcade Tankstick should really work like any PS2 or USB HID mouse.

https://github.com/kodi-game/peripheral.xarcade

Know that @garbear have a X-Arcade Tankstic but not sure if he tested the Trackball?

https://en.wikipedia.org/wiki/List_of_trackball_arcade_games

Tip is above link which lists classic trackball games, most of which works in MAME emulator.

@garbear
Copy link
Owner

garbear commented Nov 2, 2016

Thanks for the links, the trackball works like a mouse so it's usable with Kodi, but the resolution is really high so it goes really slow. for it to be useful in games we might need a setting for speed gain

@@ -21,6 +21,8 @@

#include "input/mouse/IMouseInputHandler.h"

#define MOUSE_CONTROLLER_ID "game.controller.mouse"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way we can avoid defining this here and use the value from here? https://github.com/garbear/xbmc/blob/retroplayer-mouse/xbmc/input/mouse/MouseWindowingButtonMap.cpp#L28

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of like how DEFAULT_CONTROLLER_ID is done, but it'd be nice if we could keep this #define inside MouseWindowingButtonMap.cpp and access it through its ControllerID() function

@@ -114,7 +112,7 @@ void CGameClientMouse::OnButtonRelease(const std::string& button)

event.type = GAME_INPUT_EVENT_DIGITAL_BUTTON;
event.port = GAME_INPUT_PORT_MOUSE;
event.controller_id = CONTROLLER_NAME;
event.controller_id = MOUSE_CONTROLLER_ID;
event.feature_name = button.c_str();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, button is the name of a feature of game.controller.default, so game.controller.default serves as the "context" for this feature name. e.g. "left" is useless unless we know it's "left" for game.controller.mouse.

As I mentioned in my comment below, it'd be nice if the controller ID could come from the same source as the button name (CMouseWindowingButtonMap)

Copy link
Author

@fetzerch fetzerch Nov 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a static MouseControllerID() function in CMouseWindowingButtonMap, or have a default constructed instance of CMouseWindowingButtonMap in CGameClientMouse?

Copy link
Owner

@garbear garbear Nov 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both are undesirable. we need to pass the controller context to this class somehow. See CGameClientInput::ControllerID() and notice how the context comes from CGameClient. CGameClientInput::ControllerID() is then used to transfer the context to the port manager, which opens a port using that controller ID.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding a return value to CInputManager::RegisterMouseHandler and let InputManager return m_mouseButtonMap->ControllerID(). That way we can easily keep CGameClientMouse independent from the button map. A getter would also do, but since we call register anyway, that's much cleaner.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could work. Give it a try, and I'll see if it leads to any glaring problems.

@Hedda
Copy link

Hedda commented Nov 3, 2016

@garbear is it the same for mouse speed at different resolutions in all emulators or?

So on a 2160p (4K) display it might even be slower then.

Would then be nice if could set different default speed for mouse on different resolutions.

If detect a 240p display then it runs mouse at 1x speed by default.
If detect a 320p display then it runs mouse at 1,5x speed by default.
If detect a 480p/576p display then it runs mouse at 2x speed by default.
If detect a 720p display then it runs mouse at 3x speed by default.
If detect a 1080p display then it runs mouse at 4x speed by default.
If detect a 2160p display then it runs mouse at 8x speed by default.

@garbear
Copy link
Owner

garbear commented Nov 3, 2016

@fetzerch what's left before we merge? do you want to add support for mapping the mouse and emulating the mouse with a joystick, or merge this first and add that later?

@fetzerch
Copy link
Author

fetzerch commented Nov 3, 2016

Wanted to ask you ;) For this PR, just the MOUSE_CONTROLLER_ID. Haven't yet found a nice way of doing it. If we pass in the controller when creating creating the gameclientmouse, we could access it from there. But we need to access it also when loading the addon and have to get it from buttonmap (which is created in inputmanager).

Mouse mapping, I'd say we do separately. (Is there an option to disable it in the dialog for the moment?)

For game.libretro, we have open the thing with the port number and the logging of the libretro key.

Then for the addons it's just removing the game.controller.default. I think we don't need that explicitly now that we load the mouse controller explicitly.

@garbear
Copy link
Owner

garbear commented Nov 3, 2016

For this PR, just the MOUSE_CONTROLLER_ID

I guess for now we should just copy the default controller thing, define the mouse ID in MouseWindowingButtonMap.h, add this line to GameClient.cpp:

#include "input/mouse/MouseWindowingButtonMap.h" // for MOUSE_CONTROLLER_ID

then pass the ID to CGameClientMouse.

Is there an option to disable it in the dialog for the moment?

For now we can simply not ship game.controller.mouse. Then it won't show up in the GUI, but it'll still be usable because the Kodi -> mouse translations are hardcoded in MouseWindowingButtonMap.cpp.

@fetzerch
Copy link
Author

fetzerch commented Nov 3, 2016

Updated. If we don't ship the addon, it'll not work because in OpenMouse we need to call UpdatePort so that game.libretro gets informed.

@fetzerch
Copy link
Author

fetzerch commented Nov 3, 2016

Let me know if you want me to squash the changes

/*! \brief Registers a handler to be called on mouse input (e.g a game client).
*
* \param handler The handler to call on mouse input.
* \return[in] The controller ID of the button map used to translate from windowing
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say \return The controller ID that serves as a context for incoming events

@garbear
Copy link
Owner

garbear commented Nov 3, 2016

Yes, squash the changes and i'll do a review of the full patch

@garbear
Copy link
Owner

garbear commented Nov 4, 2016

The changes here look good. I sent you a PR for build system fixes for xcode and autotools.

Game API v1.0.28:
- Define keyboard and mouse ports
- Use XBMCVKey for keyboard keys
- Add XBMC_vkeys.h to addon bindings
@garbear
Copy link
Owner

garbear commented Nov 5, 2016

Is that it? What's left for this PR?

@fetzerch
Copy link
Author

fetzerch commented Nov 5, 2016

I'll test why buttons are not working anymore, then i'll squash both PRs and push another version.

@fetzerch
Copy link
Author

fetzerch commented Nov 5, 2016

PRs updated. dosbox and scummvm work with mouse and joystick.

Still haven't fully figured out how game.controller.kodi is supposed to come into play. but maybe we can do this independently.

@garbear
Copy link
Owner

garbear commented Nov 5, 2016

you mean game.controller.mouse? it's needed for button mapping, but it's ok that it's missing now because the feature names are hardcoded in MouseWindowingButtonMap.cpp

Ready for merge across the PRs?

@fetzerch
Copy link
Author

fetzerch commented Nov 5, 2016

from my side, yes. everything tested and pushed

for (unsigned int i = 0; i < m_ports.size(); i++)
ClosePort(i);
for (const auto &it : m_ports)
ClosePort(it.first);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClosePort() modifies m_ports, which i think invalidates the iterator. I'll fix once i merge this

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should do it: e762427

@garbear garbear merged commit 4aef902 into garbear:retroplayer-17beta4 Nov 5, 2016
@fetzerch fetzerch deleted the retroplayer-mouse branch November 6, 2016 13:31
garbear pushed a commit that referenced this pull request Jan 4, 2017
[addons] add initial base add-on C++ support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants