From cfbf3f4a3fe1e3d1cbf78248b5a6567907da79af Mon Sep 17 00:00:00 2001 From: Joshua Granick Date: Wed, 18 Mar 2015 01:42:56 -0700 Subject: [PATCH] Initial Lime Gamepad API --- lime/_backend/native/NativeApplication.hx | 78 ++++++++++++++++++++ lime/app/Application.hx | 5 ++ lime/app/IModule.hx | 7 ++ lime/app/Module.hx | 7 ++ lime/ui/Gamepad.hx | 33 +++++++++ lime/ui/Window.hx | 5 ++ project/Build.xml | 2 + project/include/ui/Gamepad.h | 20 ++++++ project/include/ui/GamepadEvent.h | 45 ++++++++++++ project/src/ExternalInterface.cpp | 20 ++++++ project/src/backend/sdl/SDLApplication.cpp | 82 +++++++++++++++++++++- project/src/backend/sdl/SDLApplication.h | 3 + project/src/backend/sdl/SDLGamepad.cpp | 67 ++++++++++++++++++ project/src/backend/sdl/SDLGamepad.h | 27 +++++++ project/src/ui/GamepadEvent.cpp | 60 ++++++++++++++++ 15 files changed, 460 insertions(+), 1 deletion(-) create mode 100644 lime/ui/Gamepad.hx create mode 100644 project/include/ui/Gamepad.h create mode 100644 project/include/ui/GamepadEvent.h create mode 100644 project/src/backend/sdl/SDLGamepad.cpp create mode 100644 project/src/backend/sdl/SDLGamepad.h create mode 100644 project/src/ui/GamepadEvent.cpp diff --git a/lime/_backend/native/NativeApplication.hx b/lime/_backend/native/NativeApplication.hx index 3466c8998..fef10001e 100644 --- a/lime/_backend/native/NativeApplication.hx +++ b/lime/_backend/native/NativeApplication.hx @@ -18,6 +18,7 @@ import lime.ui.Window; class NativeApplication { + private var gamepadEventInfo = new GamepadEventInfo (); private var keyEventInfo = new KeyEventInfo (); private var mouseEventInfo = new MouseEventInfo (); private var renderEventInfo = new RenderEventInfo (RENDER); @@ -60,6 +61,7 @@ class NativeApplication { public function exec ():Int { + lime_gamepad_event_manager_register (handleGamepadEvent, gamepadEventInfo); lime_key_event_manager_register (handleKeyEvent, keyEventInfo); lime_mouse_event_manager_register (handleMouseEvent, mouseEventInfo); lime_render_event_manager_register (handleRenderEvent, renderEventInfo); @@ -105,6 +107,39 @@ class NativeApplication { } + private function handleGamepadEvent ():Void { + + if (parent.window != null) { + + switch (gamepadEventInfo.type) { + + case AXIS_MOVE: + + parent.window.onGamepadAxisMove.dispatch (gamepadEventInfo.id, gamepadEventInfo.axis, gamepadEventInfo.value); + + case BUTTON_DOWN: + + parent.window.onGamepadButtonDown.dispatch (gamepadEventInfo.id, gamepadEventInfo.button); + + case BUTTON_UP: + + parent.window.onGamepadButtonUp.dispatch (gamepadEventInfo.id, gamepadEventInfo.button); + + case CONNECT: + + parent.window.onGamepadConnect.dispatch (gamepadEventInfo.id); + + case DISCONNECT: + + parent.window.onGamepadDisconnect.dispatch (gamepadEventInfo.id); + + } + + } + + } + + private function handleKeyEvent ():Void { if (parent.window != null) { @@ -291,6 +326,7 @@ class NativeApplication { private static var lime_application_update = System.load ("lime", "lime_application_update", 1); private static var lime_application_quit = System.load ("lime", "lime_application_quit", 1); private static var lime_application_get_ticks = System.load ("lime", "lime_application_get_ticks", 0); + private static var lime_gamepad_event_manager_register = System.load ("lime", "lime_gamepad_event_manager_register", 2); private static var lime_key_event_manager_register = System.load ("lime", "lime_key_event_manager_register", 2); private static var lime_mouse_event_manager_register = System.load ("lime", "lime_mouse_event_manager_register", 2); private static var lime_render_event_manager_register = System.load ("lime", "lime_render_event_manager_register", 2); @@ -302,6 +338,48 @@ class NativeApplication { } +private class GamepadEventInfo { + + + public var axis:Int; + public var button:Int; + public var id:Int; + public var type:GamepadEventType; + public var value:Float; + + + public function new (type:GamepadEventType = null, id:Int = 0, button:Int = 0, axis:Int = 0, value:Float = 0) { + + this.type = type; + this.id = id; + this.button = button; + this.axis = axis; + this.value = value; + + } + + + public function clone ():GamepadEventInfo { + + return new GamepadEventInfo (type, id, button, axis, value); + + } + + +} + + +@:enum private abstract GamepadEventType(Int) { + + var AXIS_MOVE = 0; + var BUTTON_DOWN = 1; + var BUTTON_UP = 2; + var CONNECT = 3; + var DISCONNECT = 4; + +} + + private class KeyEventInfo { diff --git a/lime/app/Application.hx b/lime/app/Application.hx index 440943f29..6ac33cb49 100644 --- a/lime/app/Application.hx +++ b/lime/app/Application.hx @@ -98,6 +98,11 @@ class Application extends Module { windows.push (window); + window.onGamepadAxisMove.add (onGamepadAxisMove); + window.onGamepadButtonDown.add (onGamepadButtonDown); + window.onGamepadButtonUp.add (onGamepadButtonUp); + window.onGamepadConnect.add (onGamepadConnect); + window.onGamepadDisconnect.add (onGamepadDisconnect); window.onKeyDown.add (onKeyDown); window.onKeyUp.add (onKeyUp); window.onMouseDown.add (onMouseDown); diff --git a/lime/app/IModule.hx b/lime/app/IModule.hx index e6eaedf02..9ca0660f6 100644 --- a/lime/app/IModule.hx +++ b/lime/app/IModule.hx @@ -18,6 +18,13 @@ interface IModule { public function init (context:RenderContext):Void; + public function onGamepadAxisMove (id:Int, axis:Int, value:Float):Void; + public function onGamepadButtonDown(id:Int, button:Int):Void; + public function onGamepadButtonUp (id:Int, button:Int):Void; + public function onGamepadConnect (id:Int):Void; + public function onGamepadDisconnect (id:Int):Void; + + /** * Called when a key down event is fired * @param keyCode The code of the key that was pressed diff --git a/lime/app/Module.hx b/lime/app/Module.hx index d32a09fcf..7b2ba538e 100644 --- a/lime/app/Module.hx +++ b/lime/app/Module.hx @@ -25,6 +25,13 @@ class Module implements IModule { public function init (context:RenderContext):Void { } + public function onGamepadAxisMove (id:Int, axis:Int, value:Float):Void { } + public function onGamepadButtonDown(id:Int, button:Int):Void { } + public function onGamepadButtonUp (id:Int, button:Int):Void { } + public function onGamepadConnect (id:Int):Void { } + public function onGamepadDisconnect (id:Int):Void { } + + /** * Called when a key down event is fired * @param keyCode The code of the key that was pressed diff --git a/lime/ui/Gamepad.hx b/lime/ui/Gamepad.hx new file mode 100644 index 000000000..b64c15f4f --- /dev/null +++ b/lime/ui/Gamepad.hx @@ -0,0 +1,33 @@ +package lime.ui; + + +import lime.system.System; + + +class Gamepad { + + + public static function getDeviceName (id:Int):String { + + #if (cpp || neko || nodejs) + return lime_gamepad_get_device_name (id); + #else + return null; + #end + + } + + + + + // Native Methods + + + + + #if (cpp || neko || nodejs) + private static var lime_gamepad_get_device_name = System.load ("lime", "lime_gamepad_get_device_name", 1); + #end + + +} \ No newline at end of file diff --git a/lime/ui/Window.hx b/lime/ui/Window.hx index bf9cbb3fc..7e0acf07b 100644 --- a/lime/ui/Window.hx +++ b/lime/ui/Window.hx @@ -15,6 +15,11 @@ class Window { public var config:Config; public var fullscreen:Bool; public var height:Int; + public var onGamepadAxisMove = new EventInt->Float->Void> (); + public var onGamepadButtonDown = new EventInt->Void> (); + public var onGamepadButtonUp = new EventInt->Void> (); + public var onGamepadConnect = new EventVoid> (); + public var onGamepadDisconnect = new EventVoid> (); public var onKeyDown = new EventKeyModifier->Void> (); public var onKeyUp = new EventKeyModifier->Void> (); public var onMouseDown = new EventFloat->Int->Void> (); diff --git a/project/Build.xml b/project/Build.xml index 7be2f259d..8617be66a 100644 --- a/project/Build.xml +++ b/project/Build.xml @@ -145,6 +145,7 @@ + @@ -164,6 +165,7 @@ + diff --git a/project/include/ui/Gamepad.h b/project/include/ui/Gamepad.h new file mode 100644 index 000000000..b9acfcf61 --- /dev/null +++ b/project/include/ui/Gamepad.h @@ -0,0 +1,20 @@ +#ifndef LIME_UI_GAMEPAD_H +#define LIME_UI_GAMEPAD_H + + +namespace lime { + + + class Gamepad { + + public: + + static const char* GetDeviceName (int id); + + }; + + +} + + +#endif \ No newline at end of file diff --git a/project/include/ui/GamepadEvent.h b/project/include/ui/GamepadEvent.h new file mode 100644 index 000000000..a363eae40 --- /dev/null +++ b/project/include/ui/GamepadEvent.h @@ -0,0 +1,45 @@ +#ifndef LIME_UI_GAMEPAD_EVENT_H +#define LIME_UI_GAMEPAD_EVENT_H + + +#include + + +namespace lime { + + + enum GamepadEventType { + + AXIS_MOVE, + BUTTON_DOWN, + BUTTON_UP, + CONNECT, + DISCONNECT + + }; + + + class GamepadEvent { + + public: + + static AutoGCRoot* callback; + static AutoGCRoot* eventObject; + + GamepadEvent (); + + static void Dispatch (GamepadEvent* event); + + int axis; + double axisValue; + int button; + int id; + GamepadEventType type; + + }; + + +} + + +#endif \ No newline at end of file diff --git a/project/src/ExternalInterface.cpp b/project/src/ExternalInterface.cpp index 36b0ff17c..4f0d242d7 100644 --- a/project/src/ExternalInterface.cpp +++ b/project/src/ExternalInterface.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -342,6 +344,22 @@ namespace lime { } + value lime_gamepad_event_manager_register (value callback, value eventObject) { + + GamepadEvent::callback = new AutoGCRoot (callback); + GamepadEvent::eventObject = new AutoGCRoot (eventObject); + return alloc_null (); + + } + + + value lime_gamepad_get_device_name (value id) { + + return alloc_string (Gamepad::GetDeviceName (val_int (id))); + + } + + value lime_image_encode (value buffer, value type, value quality) { ImageBuffer imageBuffer = ImageBuffer (buffer); @@ -715,6 +733,8 @@ namespace lime { DEFINE_PRIM (lime_font_render_glyph, 3); DEFINE_PRIM (lime_font_render_glyphs, 3); DEFINE_PRIM (lime_font_set_size, 2); + DEFINE_PRIM (lime_gamepad_event_manager_register, 2); + DEFINE_PRIM (lime_gamepad_get_device_name, 1); DEFINE_PRIM (lime_image_encode, 3); DEFINE_PRIM (lime_image_load, 1); DEFINE_PRIM (lime_jni_getenv, 0); diff --git a/project/src/backend/sdl/SDLApplication.cpp b/project/src/backend/sdl/SDLApplication.cpp index 2087008b5..f7642219c 100644 --- a/project/src/backend/sdl/SDLApplication.cpp +++ b/project/src/backend/sdl/SDLApplication.cpp @@ -1,4 +1,5 @@ #include "SDLApplication.h" +#include "SDLGamepad.h" #ifdef HX_MACOS #include @@ -24,7 +25,7 @@ namespace lime { SDLApplication::SDLApplication () { - SDL_Init (SDL_INIT_VIDEO | SDL_INIT_TIMER); + SDL_Init (SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_TIMER); #ifdef EMSCRIPTEN currentApplication = this; @@ -38,6 +39,7 @@ namespace lime { lastUpdate = 0; nextUpdate = 0; + GamepadEvent gamepadEvent; KeyEvent keyEvent; MouseEvent mouseEvent; RenderEvent renderEvent; @@ -111,6 +113,15 @@ namespace lime { RenderEvent::Dispatch (&renderEvent); break; + case SDL_CONTROLLERAXISMOTION: + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + case SDL_CONTROLLERDEVICEADDED: + case SDL_CONTROLLERDEVICEREMOVED: + + ProcessGamepadEvent (event); + break; + case SDL_JOYAXISMOTION: case SDL_JOYBALLMOTION: case SDL_JOYBUTTONDOWN: @@ -191,6 +202,75 @@ namespace lime { } + void SDLApplication::ProcessGamepadEvent (SDL_Event* event) { + + if (GamepadEvent::callback) { + + switch (event->type) { + + case SDL_CONTROLLERAXISMOTION: + + gamepadEvent.type = BUTTON_UP; + gamepadEvent.axis = event->caxis.axis; + gamepadEvent.id = event->caxis.which; + gamepadEvent.axisValue = event->caxis.value / 32768.0; + + GamepadEvent::Dispatch (&gamepadEvent); + break; + + case SDL_CONTROLLERBUTTONDOWN: + + gamepadEvent.type = BUTTON_DOWN; + gamepadEvent.button = event->cbutton.button; + gamepadEvent.id = event->cbutton.which; + + GamepadEvent::Dispatch (&gamepadEvent); + break; + + case SDL_CONTROLLERBUTTONUP: + + gamepadEvent.type = BUTTON_UP; + gamepadEvent.button = event->cbutton.button; + gamepadEvent.id = event->cbutton.which; + + GamepadEvent::Dispatch (&gamepadEvent); + break; + + case SDL_CONTROLLERDEVICEADDED: + + if (SDLGamepad::Connect (event->cdevice.which)) { + + gamepadEvent.type = CONNECT; + gamepadEvent.id = SDLGamepad::GetInstanceID (event->cdevice.which); + + GamepadEvent::Dispatch (&gamepadEvent); + + } + + break; + + case SDL_CONTROLLERDEVICEREMOVED: { + + if (SDLGamepad::Disconnect (event->cdevice.which)) { + + gamepadEvent.type = DISCONNECT; + gamepadEvent.id = event->cdevice.which; + + GamepadEvent::Dispatch (&gamepadEvent); + + } + + break; + + } + + } + + } + + } + + void SDLApplication::ProcessKeyEvent (SDL_Event* event) { if (KeyEvent::callback) { diff --git a/project/src/backend/sdl/SDLApplication.h b/project/src/backend/sdl/SDLApplication.h index 8e2c69d4a..caf79ce68 100644 --- a/project/src/backend/sdl/SDLApplication.h +++ b/project/src/backend/sdl/SDLApplication.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ namespace lime { private: void HandleEvent (SDL_Event* event); + void ProcessGamepadEvent (SDL_Event* event); void ProcessKeyEvent (SDL_Event* event); void ProcessMouseEvent (SDL_Event* event); void ProcessTouchEvent (SDL_Event* event); @@ -43,6 +45,7 @@ namespace lime { bool active; Uint32 currentUpdate; double framePeriod; + GamepadEvent gamepadEvent; KeyEvent keyEvent; Uint32 lastUpdate; MouseEvent mouseEvent; diff --git a/project/src/backend/sdl/SDLGamepad.cpp b/project/src/backend/sdl/SDLGamepad.cpp new file mode 100644 index 000000000..fd4ad886f --- /dev/null +++ b/project/src/backend/sdl/SDLGamepad.cpp @@ -0,0 +1,67 @@ +#include "SDLGamepad.h" + + +namespace lime { + + + std::map gameControllers = std::map (); + std::map gameControllerIDs = std::map (); + + + bool SDLGamepad::Connect (int deviceID) { + + if (SDL_IsGameController (deviceID)) { + + SDL_GameController *gameController = SDL_GameControllerOpen (deviceID); + + if (gameController) { + + SDL_Joystick *joystick = SDL_GameControllerGetJoystick (gameController); + int id = SDL_JoystickInstanceID (joystick); + + gameControllers[id] = gameController; + gameControllerIDs[deviceID] = id; + + return true; + + } + + } + + return false; + + } + + + bool SDLGamepad::Disconnect (int id) { + + if (gameControllers.find (id) != gameControllers.end ()) { + + SDL_GameController *gameController = gameControllers[id]; + SDL_GameControllerClose (gameController); + gameControllers.erase (id); + + return true; + + } + + return false; + + } + + + const char* Gamepad::GetDeviceName (int id) { + + return SDL_GameControllerName (gameControllers[id]); + + } + + + int SDLGamepad::GetInstanceID (int deviceID) { + + return gameControllerIDs[deviceID]; + + } + + +} \ No newline at end of file diff --git a/project/src/backend/sdl/SDLGamepad.h b/project/src/backend/sdl/SDLGamepad.h new file mode 100644 index 000000000..0c9f5e421 --- /dev/null +++ b/project/src/backend/sdl/SDLGamepad.h @@ -0,0 +1,27 @@ +#ifndef LIME_SDL_GAMEPAD_H +#define LIME_SDL_GAMEPAD_H + + +#include +#include +#include + + +namespace lime { + + + class SDLGamepad { + + public: + + static bool Connect (int deviceID); + static int GetInstanceID (int deviceID); + static bool Disconnect (int id); + + }; + + +} + + +#endif \ No newline at end of file diff --git a/project/src/ui/GamepadEvent.cpp b/project/src/ui/GamepadEvent.cpp new file mode 100644 index 000000000..ccd67b932 --- /dev/null +++ b/project/src/ui/GamepadEvent.cpp @@ -0,0 +1,60 @@ +#include +#include + + +namespace lime { + + + AutoGCRoot* GamepadEvent::callback = 0; + AutoGCRoot* GamepadEvent::eventObject = 0; + + static double id_axis; + static int id_button; + static int id_id; + static int id_type; + static int id_value; + static bool init = false; + + + GamepadEvent::GamepadEvent () { + + axis = 0; + axisValue = 0; + button = 0; + id = 0; + type = AXIS_MOVE; + + } + + + void GamepadEvent::Dispatch (GamepadEvent* event) { + + if (GamepadEvent::callback) { + + if (!init) { + + id_axis = val_id ("axis"); + id_button = val_id ("button"); + id_id = val_id ("id"); + id_type = val_id ("type"); + id_value = val_id ("value"); + init = true; + + } + + value object = (GamepadEvent::eventObject ? GamepadEvent::eventObject->get () : alloc_empty_object ()); + + alloc_field (object, id_axis, alloc_float (event->axis)); + alloc_field (object, id_button, alloc_int (event->button)); + alloc_field (object, id_id, alloc_int (event->id)); + alloc_field (object, id_type, alloc_int (event->type)); + alloc_field (object, id_value, alloc_float (event->axisValue)); + + val_call0 (GamepadEvent::callback->get ()); + + } + + } + + +} \ No newline at end of file