Skip to content

Commit

Permalink
Game API v1.0.36: Controller topology (hub support)
Browse files Browse the repository at this point in the history
  • Loading branch information
garbear committed Feb 14, 2018
1 parent 583431a commit 1362c5d
Show file tree
Hide file tree
Showing 41 changed files with 2,350 additions and 237 deletions.
1 change: 1 addition & 0 deletions cmake/treedata/common/games.txt
Expand Up @@ -6,6 +6,7 @@ xbmc/games/addons/savestates games/addons/savestates
xbmc/games/controllers games/controllers
xbmc/games/controllers/dialogs games/controllers/dialogs
xbmc/games/controllers/guicontrols games/controllers/guicontrols
xbmc/games/controllers/types games/controllers/types
xbmc/games/controllers/windows games/controllers/windows
xbmc/games/dialogs games/dialogs
xbmc/games/dialogs/osd games/dialogs/osd
Expand Down
77 changes: 65 additions & 12 deletions xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_dll.h
Expand Up @@ -130,17 +130,6 @@ GAME_ERROR HwContextDestroy(void);

// --- Input operations --------------------------------------------------------

/*!
* \brief Notify the add-on of a status change on an open port
*
* Ports can be opened using the OpenPort() callback
*
* \param port Non-negative for a joystick port, or GAME_INPUT_PORT value otherwise
* \param collected True if a controller was connected, false if disconnected
* \param controller The connected controller
*/
void UpdatePort(int port, bool connected, const game_controller* controller);

/*!
* \brief Check if input is accepted for a feature on the controller
*
Expand All @@ -155,6 +144,26 @@ void UpdatePort(int port, bool connected, const game_controller* controller);
*/
bool HasFeature(const char* controller_id, const char* feature_name);

/*!
* \brief Get the input topolgy that specifies which controllers can be connected
*
* \return The input topology, or null to use the default
*
* If this returns non-null, topology must be freed using FreeTopology().
*
* If this returns null, the topology will default to a single port that can
* accept all controllers imported by addon.xml. The port ID is set to
* the DEFAULT_PORT_ID constant.
*/
game_input_topology* GetTopology();

/*!
* \brief Free the topology's resources
*
* \param topology The topology returned by GetTopology()
*/
void FreeTopology(game_input_topology* topology);

/*!
* \brief Enable/disable keyboard input using the specified controller
*
Expand All @@ -175,6 +184,48 @@ bool EnableKeyboard(bool enable, const game_controller* controller);
*/
bool EnableMouse(bool enable, const game_controller* controller);

/*!
* \brief Connect/disconnect a controller to a port on the virtual game console
*
* \param connect True to connect a controller, false to disconnect
* \param address The address of the port
* \param controller The controller info if connecting, or unused if disconnecting
*
* The address is a string that allows traversal of the controller topology.
* It is formed by alternating port IDs and controller IDs separated by "/".
*
* For example, assume that the topology represented in XML for Snes9x is:
*
* <logicaltopology>
* <port type="controller" id="1">
* <accepts controller="game.controller.snes"/>
* <accepts controller="game.controller.snes.multitap">
* <port type="controller" id="1">
* <accepts controller="game.controller.snes"/>
* </port>
* <port type="controller" id="2">
* <accepts controller="game.controller.snes"/>
* </port>
* ...
* </accepts>
* </port>
* </logicaltopology>
*
* To connect a multitap to the console's first port, the multitap's controller
* info is set using the port address:
*
* 1
*
* To connect a SNES controller to the second port of the multitap, the
* controller info is next set using the address:
*
* 1/game.controller.multitap/2
*
* Any attempts to connect a controller to a port on a disconnected multitap
* will return false.
*/
bool ConnectController(bool connect, const char* port_address, const game_controller* controller);

/*!
* \brief Notify the add-on of an input event
*
Expand Down Expand Up @@ -267,10 +318,12 @@ void __declspec(dllexport) get_addon(void* ptr)
pClient->toAddon.Reset = Reset;
pClient->toAddon.HwContextReset = HwContextReset;
pClient->toAddon.HwContextDestroy = HwContextDestroy;
pClient->toAddon.UpdatePort = UpdatePort;
pClient->toAddon.HasFeature = HasFeature;
pClient->toAddon.GetTopology = GetTopology;
pClient->toAddon.FreeTopology = FreeTopology;
pClient->toAddon.EnableKeyboard = EnableKeyboard;
pClient->toAddon.EnableMouse = EnableMouse;
pClient->toAddon.ConnectController = ConnectController;
pClient->toAddon.InputEvent = InputEvent;
pClient->toAddon.SerializeSize = SerializeSize;
pClient->toAddon.Serialize = Serialize;
Expand Down
73 changes: 63 additions & 10 deletions xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h
Expand Up @@ -57,6 +57,9 @@
#include "input/XBMC_vkeys.h"
#endif

/*! Port ID used when topology is unknown */
#define DEFAULT_PORT_ID "1"

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -159,11 +162,6 @@ typedef enum GAME_HW_CONTEXT_TYPE
GAME_HW_CONTEXT_OPENGLES3, // GLES 3.0
} GAME_HW_CONTEXT_TYPE;

typedef enum GAME_INPUT_PORT
{
GAME_INPUT_PORT_JOYSTICK_START = 0, // Non-negative values are for joystick ports
} GAME_INPUT_PORT;

typedef enum GAME_INPUT_EVENT_SOURCE
{
GAME_INPUT_EVENT_DIGITAL_BUTTON,
Expand Down Expand Up @@ -275,9 +273,21 @@ typedef enum GAME_ROTATION
GAME_ROTATION_270_CW,
} GAME_ROTATION;

/*!
* \brief Type of port on the virtual game console
*/
typedef enum GAME_PORT_TYPE
{
GAME_PORT_UNKNOWN,
GAME_PORT_KEYBOARD,
GAME_PORT_MOUSE,
GAME_PORT_CONTROLLER,
} GAME_PORT_TYPE;

typedef struct game_controller
{
const char* controller_id;
bool provides_input; // False for multitaps
unsigned int digital_button_count;
unsigned int analog_button_count;
unsigned int analog_stick_count;
Expand All @@ -288,6 +298,48 @@ typedef struct game_controller
unsigned int motor_count;
} ATTRIBUTE_PACKED game_controller;

struct game_input_port;

/*!
* \brief Device that can provide input
*/
typedef struct game_input_device
{
const char* controller_id; // ID used in the Kodi controller API
const char* port_address;
game_input_port* available_ports;
unsigned int port_count;
} ATTRIBUTE_PACKED game_input_device;

/*!
* \brief Port that can provide input
*
* Ports can accept multiple devices and devices can have multiple ports, so
* the topology of possible configurations is a tree structure of alternating
* port and device nodes.
*/
typedef struct game_input_port
{
GAME_PORT_TYPE type;
const char* port_id; // Required for GAME_PORT_CONTROLLER type
game_input_device* accepted_devices;
unsigned int device_count;
} ATTRIBUTE_PACKED game_input_port;

/*!
* \brief The input topology is the possible ways to connect input devices
*
* This represents the logical topology, which is the possible connections that
* the game client's logic can handle. It is strictly a subset of the physical
* topology. Loops are not allowed.
*/
typedef struct game_input_topology
{
game_input_port *ports; //! The list of ports on the virtual game console
unsigned int port_count; //! The number of ports
int player_limit; //! A limit on the number of input-providing devices, or -1 for no limit
} ATTRIBUTE_PACKED game_input_topology;

typedef struct game_digital_button_event
{
bool pressed;
Expand Down Expand Up @@ -351,8 +403,9 @@ typedef struct game_motor_event
typedef struct game_input_event
{
GAME_INPUT_EVENT_SOURCE type;
int port;
const char* controller_id;
GAME_PORT_TYPE port_type;
const char* port_address;
const char* feature_name;
union
{
Expand Down Expand Up @@ -479,8 +532,6 @@ typedef struct AddonToKodiFuncTable_Game
uintptr_t (*HwGetCurrentFramebuffer)(void* kodiInstance);
game_proc_address_t (*HwGetProcAddress)(void* kodiInstance, const char* symbol);
void (*RenderFrame)(void* kodiInstance);
bool (*OpenPort)(void* kodiInstance, unsigned int port);
void (*ClosePort)(void* kodiInstance, unsigned int port);
bool (*InputEvent)(void* kodiInstance, const game_input_event* event);

} AddonToKodiFuncTable_Game;
Expand All @@ -498,10 +549,12 @@ typedef struct KodiToAddonFuncTable_Game
GAME_ERROR (__cdecl* Reset)(void);
GAME_ERROR (__cdecl* HwContextReset)(void);
GAME_ERROR (__cdecl* HwContextDestroy)(void);
void (__cdecl* UpdatePort)(int, bool, const game_controller*);
bool (__cdecl* HasFeature)(const char* controller_id, const char* feature_name);
bool (__cdecl* HasFeature)(const char*, const char*);
game_input_topology* (__cdecl* GetTopology)();
void (__cdecl* FreeTopology)(game_input_topology*);
bool (__cdecl* EnableKeyboard)(bool, const game_controller*);
bool (__cdecl* EnableMouse)(bool, const game_controller*);
bool (__cdecl* ConnectController)(bool, const char*, const game_controller*);
bool (__cdecl* InputEvent)(const game_input_event*);
size_t (__cdecl* SerializeSize)(void);
GAME_ERROR (__cdecl* Serialize)(uint8_t*, size_t);
Expand Down
22 changes: 0 additions & 22 deletions xbmc/addons/kodi-addon-dev-kit/include/kodi/libKODI_game.h
Expand Up @@ -187,28 +187,6 @@ class CHelper_libKODI_game

// --- Input callbacks -------------------------------------------------------

/*!
* \brief Begin reporting events for the specified joystick port
*
* \param port The zero-indexed port number
*
* \return true if the port was opened, false otherwise
*/
bool OpenPort(unsigned int port)
{
return m_callbacks->toKodi.OpenPort(m_callbacks->toKodi.kodiInstance, port);
}

/*!
* \brief End reporting events for the specified port
*
* \param port The port number passed to OpenPort()
*/
void ClosePort(unsigned int port)
{
return m_callbacks->toKodi.ClosePort(m_callbacks->toKodi.kodiInstance, port);
}

/*!
* \brief Notify the port of an input event
*
Expand Down
4 changes: 2 additions & 2 deletions xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h
Expand Up @@ -91,8 +91,8 @@
#define ADDON_INSTANCE_VERSION_AUDIOENCODER_XML_ID "kodi.binary.instance.audioencoder"
#define ADDON_INSTANCE_VERSION_AUDIOENCODER_DEPENDS "addon-instance/AudioEncoder.h"

#define ADDON_INSTANCE_VERSION_GAME "1.0.35"
#define ADDON_INSTANCE_VERSION_GAME_MIN "1.0.35"
#define ADDON_INSTANCE_VERSION_GAME "1.0.36"
#define ADDON_INSTANCE_VERSION_GAME_MIN "1.0.36"
#define ADDON_INSTANCE_VERSION_GAME_XML_ID "kodi.binary.instance.game"
#define ADDON_INSTANCE_VERSION_GAME_DEPENDS "kodi_game_dll.h" \
"kodi_game_types.h" \
Expand Down
3 changes: 2 additions & 1 deletion xbmc/cores/RetroPlayer/RetroPlayer.cpp
Expand Up @@ -30,6 +30,7 @@
#include "cores/RetroPlayer/rendering/RPRenderManager.h"
#include "dialogs/GUIDialogYesNo.h"
#include "filesystem/File.h"
#include "games/addons/input/GameClientInput.h"
#include "games/addons/playback/IGameClientPlayback.h"
#include "games/addons/savestates/Savestate.h"
#include "games/addons/savestates/SavestateUtils.h"
Expand Down Expand Up @@ -403,7 +404,7 @@ bool CRetroPlayer::OnAction(const CAction &action)
m_gameClient->GetPlayback()->SetSpeed(0.0);

CLog::Log(LOGDEBUG, "RetroPlayer[PLAYER]: Sending reset command via ACTION_PLAYER_RESET");
//m_gameServices.PortManager().HardwareReset();
m_gameClient->Input().HardwareReset();

// If rewinding or paused, begin playback
if (speed <= 0.0f)
Expand Down
8 changes: 8 additions & 0 deletions xbmc/games/GameTypes.h
Expand Up @@ -31,5 +31,13 @@ namespace GAME
using GameClientPtr = std::shared_ptr<CGameClient>;
using GameClientVector = std::vector<GameClientPtr>;

class CGameClientPort;
using GameClientPortPtr = std::unique_ptr<CGameClientPort>;
using GameClientPortVec = std::vector<GameClientPortPtr>;

class CGameClientDevice;
using GameClientDevicePtr = std::unique_ptr<CGameClientDevice>;
using GameClientDeviceVec = std::vector<GameClientDevicePtr>;

}
}

0 comments on commit 1362c5d

Please sign in to comment.