--- /dev/null
+<!-- This file contains the mappings for a Gamestick controller to actions within XBMC -->
+<!-- The <global> section is a fall through - they will only be used if the button is not -->
+<!-- used in the current window's section. Note that there is only handling -->
+<!-- for a single action per button at this stage. -->
+
+<!-- The format of a mapping is: -->
+<!-- <device name="name"> -->
+<!-- <button id="x">action</button> -->
+<!-- <axis id="x" limit="y">action</axis> -->
+<!-- </device> -->
+
+<!-- Note that the action can be a built-in function. -->
+<!-- eg <button id="x">XBMC.ActivateWindow(Home)</button> -->
+<!-- would automatically go to Home on the press of button 'x'. -->
+
+<!-- Joystick Name: Gamestick -->
+
+<!-- Button Mappings in Android: -->
+<!-- see JoyButtonMap in AndroidKey.cpp -->
+<!-- -->
+<!-- ID Button -->
+<!-- -->
+<!-- 1 A -->
+<!-- 2 B -->
+<!-- 4 X -->
+<!-- 5 Y -->
+<!-- 7 Left Shoulder -->
+<!-- 8 Right Shoulder -->
+<!-- 11 Left Stick Button -->
+<!-- 12 Right Stick Button -->
+<!-- 13 Start -->
+
+<!-- Axis Mappings: -->
+<!-- -->
+<!-- ID Button -->
+<!-- -->
+<!-- 1 Left Stick L/R -->
+<!-- 2 Left Stick U/D -->
+<!-- 3 Right Stick L/R -->
+<!-- 4 Right Stick U/D -->
+<!-- 5 D-Pad U/D -->
+<!-- 6 D-Pad L/R -->
+
+<keymap>
+ <global>
+ <joystick name="GameStick Controller">
+ <altname>GameStick Controller 1</altname>
+ <altname>GameStick Controller 2</altname>
+ <!-- A selects. B goes back. X gets context menu. Y goes fullscreen and back. -->
+ <button id="1">Select</button>
+ <button id="2">Back</button>
+ <button id="4">ContextMenu</button>
+ <button id="5">FullScreen</button>
+ <!--Left Shoulder Queues videos. Right shoulder displays the current queue. -->
+ <button id="7">Queue</button>
+ <button id="8">Playlist</button>
+ <!--left/right stick buttons -->
+ <button id="11">Enter</button>
+ <button id="12">Enter</button>
+ <!-- Push up on the left stick for volueme up. Push down for volume down. -->
+ <axis id="1" limit="-1">Up</axis>
+ <axis id="1" limit="+1">Down</axis>
+ <axis id="2" limit="-1">Left</axis>
+ <axis id="2" limit="+1">Right</axis>
+ <!-- Push up on the right stick for volueme up. Push down for volume down. -->
+ <axis id="3" limit="-1">VolumeDown</axis>
+ <axis id="3" limit="+1">VolumeUp</axis>
+ <axis id="4" limit="-1">VolumeDown</axis>
+ <axis id="4" limit="+1">VolumeUp</axis>
+ <!-- Analog DPad. -->
+ <axis id="5" limit="-1">Up</axis>
+ <axis id="5" limit="+1">Down</axis>
+ <axis id="6" limit="-1">Left</axis>
+ <axis id="6" limit="+1">Right</axis>
+ </joystick>
+ </global>
+ <FullscreenVideo>
+ <joystick name="GameStick Controller">
+ <altname>GameStick Controller 1</altname>
+ <altname>GameStick Controller 2</altname>
+ <!--
+ A pauses and starts the video.
+ B stops the video.
+ X opens the onscreen display.
+ Y switches in and out of full screen
+ -->
+ <button id="1">Pause</button>
+ <button id="2">Stop</button>
+ <button id="4">OSD</button>
+ <!--
+ Left shoulder changes aspect ratio.
+ Right shoulder changes subtitles.
+ Right stick changes Audio Language.
+ Start button displays info.
+ -->
+ <button id="7">Info</button>
+ <button id="8">AudioNextLanguage</button>
+ <!-- Analog DPad. -->
+ <axis id="5" limit="-1">BigStepForward</axis>
+ <axis id="5" limit="+1">BigStepBack</axis>
+ <axis id="6" limit="-1">StepBack</axis>
+ <axis id="6" limit="+1">StepForward</axis>
+ </joystick>
+ </FullscreenVideo>
+ <FullscreenInfo>
+ <joystick name="GameStick Controller">
+ <altname>GameStick Controller 1</altname>
+ <altname>GameStick Controller 2</altname>
+ <button id="2">Close</button>
+ <button id="4">OSD</button>
+ </joystick>
+ </FullscreenInfo>
+ <VideoOSD>
+ <joystick name="GameStick Controller">
+ <altname>GameStick Controller 1</altname>
+ <altname>GameStick Controller 2</altname>
+ <button id="4">CodecInfo</button>
+ </joystick>
+ </VideoOSD>
+ <PlayerControls>
+ <joystick name="GameStick Controller">
+ <altname>GameStick Controller 1</altname>
+ <altname>GameStick Controller 2</altname>
+ <button id="4">Close</button>
+ </joystick>
+ </PlayerControls>
+</keymap>
--- /dev/null
+<!-- This file contains the mappings for a NVIDIA Shiled Controller to actions within XBMC -->
+<!-- The <global> section is a fall through - they will only be used if the button is not -->
+<!-- used in the current window's section. Note that there is only handling -->
+<!-- for a single action per button at this stage. -->
+
+<!-- The format of a mapping is: -->
+<!-- <device name="name"> -->
+<!-- <button id="x">action</button> -->
+<!-- <axis id="x" limit="y">action</axis> -->
+<!-- </device> -->
+
+<!-- Note that the action can be a built-in function. -->
+<!-- eg <button id="x">XBMC.ActivateWindow(Home)</button> -->
+<!-- would automatically go to Home on the press of button 'x'. -->
+
+<!-- Joystick Name: NVIDIA Corporation NVIDIA Controller -->
+
+<!-- Button Mappings in Android: -->
+<!-- see JoyButtonMap in AndroidKey.cpp -->
+<!-- -->
+<!-- ID Button -->
+<!-- -->
+<!-- 1 A -->
+<!-- 2 B -->
+<!-- 4 X -->
+<!-- 5 Y -->
+<!-- 7 Left Shoulder -->
+<!-- 8 Right Shoulder -->
+<!-- 11 Left Stick Button -->
+<!-- 12 Right Stick Button -->
+<!-- 13 Start -->
+
+<!-- Axis Mappings: -->
+<!-- -->
+<!-- ID Button -->
+<!-- -->
+<!-- 1 Left Stick L/R -->
+<!-- 2 Left Stick U/D -->
+<!-- 3 Right Stick L/R -->
+<!-- 4 Right Stick U/D -->
+<!-- 5 D-Pad U/D -->
+<!-- 6 D-Pad L/R -->
+
+<keymap>
+ <global>
+ <joystick name="NVIDIA Corporation NVIDIA Controller">
+ <altname>NVIDIA Corporation NVIDIA Controller v01.01</altname>
+ <!-- A selects. B goes back. X gets context menu. Y goes fullscreen and back. -->
+ <button id="1">Select</button>
+ <button id="2">Back</button>
+ <button id="4">ContextMenu</button>
+ <button id="5">FullScreen</button>
+ <!--Left Shoulder Queues videos. Right shoulder displays the current queue. -->
+ <button id="7">Queue</button>
+ <button id="8">Playlist</button>
+ <!--left/right stick buttons -->
+ <button id="11">Enter</button>
+ <button id="12">Enter</button>
+ <!-- Push up on the left stick for volueme up. Push down for volume down. -->
+ <axis id="1" limit="-1">Up</axis>
+ <axis id="1" limit="+1">Down</axis>
+ <axis id="2" limit="-1">Left</axis>
+ <axis id="2" limit="+1">Right</axis>
+ <!-- Push up on the right stick for volueme up. Push down for volume down. -->
+ <axis id="3" limit="-1">VolumeDown</axis>
+ <axis id="3" limit="+1">VolumeUp</axis>
+ <axis id="4" limit="-1">VolumeDown</axis>
+ <axis id="4" limit="+1">VolumeUp</axis>
+ <!-- Analog DPad. -->
+ <axis id="5" limit="-1">Up</axis>
+ <axis id="5" limit="+1">Down</axis>
+ <axis id="6" limit="-1">Left</axis>
+ <axis id="6" limit="+1">Right</axis>
+ </joystick>
+ </global>
+ <FullscreenVideo>
+ <joystick name="NVIDIA Corporation NVIDIA Controller">
+ <altname>NVIDIA Corporation NVIDIA Controller v01.01</altname>
+ <!--
+ A pauses and starts the video.
+ B stops the video.
+ X opens the onscreen display.
+ Y switches in and out of full screen
+ -->
+ <button id="1">Pause</button>
+ <button id="2">Stop</button>
+ <button id="4">OSD</button>
+ <!--
+ Left shoulder changes aspect ratio.
+ Right shoulder changes subtitles.
+ Right stick changes Audio Language.
+ Start button displays info.
+ -->
+ <button id="7">Info</button>
+ <button id="8">AudioNextLanguage</button>
+ <!-- Analog DPad. -->
+ <axis id="5" limit="-1">BigStepForward</axis>
+ <axis id="5" limit="+1">BigStepBack</axis>
+ <axis id="6" limit="-1">StepBack</axis>
+ <axis id="6" limit="+1">StepForward</axis>
+ </joystick>
+ </FullscreenVideo>
+ <FullscreenInfo>
+ <joystick name="NVIDIA Corporation NVIDIA Controller">
+ <altname>NVIDIA Corporation NVIDIA Controller v01.01</altname>
+ <button id="2">Close</button>
+ <button id="4">OSD</button>
+ </joystick>
+ </FullscreenInfo>
+ <VideoOSD>
+ <joystick name="NVIDIA Corporation NVIDIA Controller">
+ <altname>NVIDIA Corporation NVIDIA Controller v01.01</altname>
+ <button id="4">CodecInfo</button>
+ </joystick>
+ </VideoOSD>
+ <PlayerControls>
+ <joystick name="NVIDIA Corporation NVIDIA Controller">
+ <altname>NVIDIA Corporation NVIDIA Controller v01.01</altname>
+ <button id="4">Close</button>
+ </joystick>
+ </PlayerControls>
+</keymap>
--- /dev/null
+<!-- This file contains the mappings for a OUYA Game Controller to actions within XBMC -->
+<!-- The <global> section is a fall through - they will only be used if the button is not -->
+<!-- used in the current window's section. Note that there is only handling -->
+<!-- for a single action per button at this stage. -->
+
+<!-- The format of a mapping is: -->
+<!-- <device name="name"> -->
+<!-- <button id="x">action</button> -->
+<!-- <axis id="x" limit="y">action</axis> -->
+<!-- </device> -->
+
+<!-- Note that the action can be a built-in function. -->
+<!-- eg <button id="x">XBMC.ActivateWindow(Home)</button> -->
+<!-- would automatically go to Home on the press of button 'x'. -->
+
+<!-- Joystick Name: OUYA Game Controller -->
+
+<!-- Button Mappings in Android: -->
+<!-- see JoyButtonMap in AndroidKey.cpp -->
+<!-- -->
+<!-- ID Button -->
+<!-- -->
+<!-- 1 A -->
+<!-- 2 B -->
+<!-- 4 X -->
+<!-- 5 Y -->
+<!-- 7 Left Shoulder -->
+<!-- 8 Right Shoulder -->
+<!-- 11 Left Stick Button -->
+<!-- 12 Right Stick Button -->
+<!-- 13 Start -->
+
+<!-- Axis Mappings: -->
+<!-- -->
+<!-- ID Button -->
+<!-- -->
+<!-- 1 Left Stick L/R -->
+<!-- 2 Left Stick U/D -->
+<!-- 3 Right Stick L/R -->
+<!-- 4 Right Stick U/D -->
+<!-- 5 D-Pad U/D -->
+<!-- 6 D-Pad L/R -->
+
+<keymap>
+ <global>
+ <joystick name="OUYA Game Controller">
+ <altname>OUYA Game Controller 1</altname>
+ <altname>OUYA Game Controller 2</altname>
+ <!-- A selects. B goes back. X gets context menu. Y goes fullscreen and back. -->
+ <button id="1">Select</button>
+ <button id="2">Back</button>
+ <button id="4">ContextMenu</button>
+ <button id="5">FullScreen</button>
+ <!--Left Shoulder Queues videos. Right shoulder displays the current queue. -->
+ <button id="7">Queue</button>
+ <button id="8">Playlist</button>
+ <!--left/right stick buttons -->
+ <button id="11">Enter</button>
+ <button id="12">Enter</button>
+ <!-- Push up on the left stick for volueme up. Push down for volume down. -->
+ <axis id="1" limit="-1">Up</axis>
+ <axis id="1" limit="+1">Down</axis>
+ <axis id="2" limit="-1">Left</axis>
+ <axis id="2" limit="+1">Right</axis>
+ <!-- Push up on the right stick for volueme up. Push down for volume down. -->
+ <axis id="3" limit="-1">VolumeDown</axis>
+ <axis id="3" limit="+1">VolumeUp</axis>
+ <axis id="4" limit="-1">VolumeDown</axis>
+ <axis id="4" limit="+1">VolumeUp</axis>
+ <!-- Analog DPad. -->
+ <axis id="5" limit="-1">Up</axis>
+ <axis id="5" limit="+1">Down</axis>
+ <axis id="6" limit="-1">Left</axis>
+ <axis id="6" limit="+1">Right</axis>
+ </joystick>
+ </global>
+ <FullscreenVideo>
+ <joystick name="OUYA Game Controller">
+ <altname>OUYA Game Controller 1</altname>
+ <altname>OUYA Game Controller 2</altname>
+ <!--
+ A pauses and starts the video.
+ B stops the video.
+ X opens the onscreen display.
+ Y switches in and out of full screen
+ -->
+ <button id="1">Pause</button>
+ <button id="2">Stop</button>
+ <button id="4">OSD</button>
+ <!--
+ Left shoulder changes aspect ratio.
+ Right shoulder changes subtitles.
+ Right stick changes Audio Language.
+ Start button displays info.
+ -->
+ <button id="7">Info</button>
+ <button id="8">AudioNextLanguage</button>
+ <!-- Analog DPad. -->
+ <axis id="5" limit="-1">BigStepForward</axis>
+ <axis id="5" limit="+1">BigStepBack</axis>
+ <axis id="6" limit="-1">StepBack</axis>
+ <axis id="6" limit="+1">StepForward</axis>
+ </joystick>
+ </FullscreenVideo>
+ <FullscreenInfo>
+ <joystick name="OUYA Game Controller">
+ <altname>OUYA Game Controller 1</altname>
+ <altname>OUYA Game Controller 2</altname>
+ <button id="2">Close</button>
+ <button id="4">OSD</button>
+ </joystick>
+ </FullscreenInfo>
+ <VideoOSD>
+ <joystick name="OUYA Game Controller">
+ <altname>OUYA Game Controller 1</altname>
+ <altname>OUYA Game Controller 2</altname>
+ <button id="4">CodecInfo</button>
+ </joystick>
+ </VideoOSD>
+ <PlayerControls>
+ <joystick name="OUYA Game Controller">
+ <altname>OUYA Game Controller 1</altname>
+ <altname>OUYA Game Controller 2</altname>
+ <button id="4">Close</button>
+ </joystick>
+ </PlayerControls>
+</keymap>
#if defined(TARGET_DARWIN_IOS)
friend class CWinEventsIOS;
#endif
+#if defined(TARGET_ANDROID)
+ friend class CWinEventsAndroid;
+#endif
// screensaver
bool m_bScreenSave;
ADDON::AddonPtr m_screenSaver;
#define AKEYCODE_FORWARD 125
#define AKEYCODE_MEDIA_PLAY 126
#define AKEYCODE_MEDIA_EJECT 129
+
+#define AINPUT_SOURCE_CLASS_JOYSTICK 0x00000010
+
+#define AINPUT_SOURCE_GAMEPAD (0x00000400 | AINPUT_SOURCE_CLASS_BUTTON)
+#define AINPUT_SOURCE_JOYSTICK (0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK)
+
+// 1st stick X, Y
+#define AMOTION_EVENT_AXIS_X 0
+#define AMOTION_EVENT_AXIS_Y 1
+// 2nd stick X, Y
+#define AMOTION_EVENT_AXIS_Z 11
+#define AMOTION_EVENT_AXIS_RZ 14
+// d-pad X, Y
+#define AMOTION_EVENT_AXIS_HAT_X 15
+#define AMOTION_EVENT_AXIS_HAT_Y 16
+// trigger left, right
+#define AMOTION_EVENT_AXIS_LTRIGGER 17
+#define AMOTION_EVENT_AXIS_RTRIGGER 18
--- /dev/null
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "AndroidJoyStick.h"
+#include "AndroidExtra.h"
+#include "XBMCApp.h"
+#include "android/jni/View.h"
+#include "android/activity/AndroidFeatures.h"
+#include "utils/log.h"
+#include "windowing/WinEvents.h"
+#include "windowing/XBMC_events.h"
+#include "utils/TimeUtils.h"
+
+#include <android/input.h>
+
+
+#include <math.h>
+#include <dlfcn.h>
+
+// mapping to axis IDs codes in keymaps.xmls
+enum {
+ AXIS_LEFT_STICK_L_R = 1,
+ AXIS_LEFT_STICK_U_D = 2,
+ AXIS_RIGHT_STICK_L_R = 3,
+ AXIS_RIGHT_STICK_U_D = 4,
+ AXIS_DPAD_U_D = 5,
+ AXIS_DPAD_L_R = 6,
+};
+
+// mapping to button codes in keymaps.xmls
+typedef struct {
+ int32_t nativeKey;
+ int16_t xbmcID;
+} KeyMap;
+
+static const KeyMap GamePadMap[] = {
+ { AKEYCODE_BUTTON_A , 1 },
+ { AKEYCODE_BUTTON_B , 2 },
+ { AKEYCODE_BUTTON_C , 3 },
+ { AKEYCODE_BUTTON_X , 4 },
+ { AKEYCODE_BUTTON_Y , 5 },
+ { AKEYCODE_BUTTON_Z , 6 },
+ { AKEYCODE_BUTTON_L1 , 7 },
+ { AKEYCODE_BUTTON_R1 , 8 },
+ { AKEYCODE_BUTTON_L2 , 9 },
+ { AKEYCODE_BUTTON_R2 , 10 },
+ { AKEYCODE_BUTTON_THUMBL , 11 },
+ { AKEYCODE_BUTTON_THUMBR , 12 },
+ { AKEYCODE_BUTTON_START , 13 }
+};
+
+// missing in early NDKs, should be present in r9+
+extern float AMotionEvent_getAxisValue(const AInputEvent* motion_event, int32_t axis, size_t pointer_index);
+static typeof(AMotionEvent_getAxisValue) *p_AMotionEvent_getAxisValue;
+#define AMotionEvent_getAxisValue (*p_AMotionEvent_getAxisValue)
+
+/************************************************************************/
+/************************************************************************/
+static float AxisClampAsButton(const APP_InputDeviceAxis &axis, float value)
+{
+ // Clamp Axis so it acts like a D-Pad, return -1, 0 or +1
+ if (fabs(value) < axis.buttonclamp)
+ return 0.0;
+ else
+ return value < 0.0 ? -1.0:1.0;
+}
+
+static void LogAxisValues(int axis_id, const APP_InputDeviceAxis &axis)
+{
+ CLog::Log(LOGDEBUG, "LogAxisValues: "
+ "axis(%d) Enabled(%d) Max(%f) Min(%f) Range(%f) Flat(%f) Fuzz(%f)",
+ axis_id, axis.enabled, axis.max, axis.min, axis.range, axis.flat, axis.fuzz);
+}
+
+static void SetAxisFromValues(const float min, const float max,
+ const float flat, const float fuzz, const float range, APP_InputDeviceAxis &axis)
+{
+ axis.min = min;
+ axis.max = max;
+ axis.flat = flat;
+ axis.fuzz = fuzz;
+ axis.range= range;
+ // precalc some internals
+ axis.deadzone= axis.flat + axis.fuzz;
+ if (axis.deadzone < 0.1f)
+ axis.deadzone = 0.1f;
+ axis.buttonclamp = axis.range / 4.0f;
+}
+
+static void SetupAxis(const CJNIViewInputDevice &input_device, APP_InputDeviceAxis &axis, int axis_id, int source)
+{
+ CJNIViewInputDeviceMotionRange range = input_device.getMotionRange(axis_id, source);
+
+ SetAxisFromValues(range.getMin(), range.getMax(), range.getFlat(), range.getFuzz(), range.getRange(), axis);
+ axis.enabled = true;
+}
+
+static void SetupJoySticks(APP_InputDeviceAxes *axes, int device)
+{
+ axes->id = device;
+ memset(&axes->x_hat, 0x00, sizeof(APP_InputDeviceAxis));
+ memset(&axes->y_hat, 0x00, sizeof(APP_InputDeviceAxis));
+ memset(&axes->x_axis, 0x00, sizeof(APP_InputDeviceAxis));
+ memset(&axes->y_axis, 0x00, sizeof(APP_InputDeviceAxis));
+ memset(&axes->z_axis, 0x00, sizeof(APP_InputDeviceAxis));
+ memset(&axes->rz_axis,0x00, sizeof(APP_InputDeviceAxis));
+
+ CJNIViewInputDevice input_device = CJNIViewInputDevice::getDevice(axes->id);
+ int device_sources = input_device.getSources();
+ std::string device_name = input_device.getName();
+
+ CLog::Log(LOGDEBUG, "SetupJoySticks:caching id(%d), sources(%d), device(%s)",
+ axes->id, device_sources, device_name.c_str());
+
+ CJNIList<CJNIViewInputDeviceMotionRange> device_ranges = input_device.getMotionRanges();
+ for (int i = 0; i < device_ranges.size(); i++)
+ {
+ int axis = device_ranges.get(i).getAxis();
+ int source = device_ranges.get(i).getSource();
+ CLog::Log(LOGDEBUG, "SetupJoySticks:range(%d), axis(%d), source(%d)", i, axis, source);
+
+ // ignore anything we do not understand
+ if (source != AINPUT_SOURCE_JOYSTICK)
+ continue;
+
+ // match axis/source to our handlers
+ // anything that is not present, will be disabled
+ switch(axis)
+ {
+ // Left joystick
+ case AMOTION_EVENT_AXIS_X:
+ SetupAxis(input_device, axes->x_axis, axis, source);
+ break;
+ break;
+ case AMOTION_EVENT_AXIS_Y:
+ SetupAxis(input_device, axes->y_axis, axis, source);
+ break;
+
+ // Right joystick
+ case AMOTION_EVENT_AXIS_Z:
+ SetupAxis(input_device, axes->z_axis, axis, source);
+ break;
+ case AMOTION_EVENT_AXIS_RZ:
+ SetupAxis(input_device, axes->rz_axis, axis, source);
+ break;
+
+ // D-Pad
+ case AMOTION_EVENT_AXIS_HAT_X:
+ SetupAxis(input_device, axes->x_hat, axis, source);
+ break;
+ case AMOTION_EVENT_AXIS_HAT_Y:
+ SetupAxis(input_device, axes->y_hat, axis, source);
+ break;
+ }
+ }
+
+ if (device_name.find("GameStick Controller") != std::string::npos)
+ {
+ // Right joystick seems to have a range of -0.5 to 0.5, fix the range
+ // Production GameStick Controllers should not have this problem
+ // and this quirk can vanish once verified.
+ SetAxisFromValues(-0.5f, 0.5f, 0.1f, 0.0f, 1.0f, axes->z_axis);
+ SetAxisFromValues(-0.5f, 0.5f, 0.1f, 0.0f, 1.0f, axes->rz_axis);
+ }
+
+#if 1
+ LogAxisValues(AMOTION_EVENT_AXIS_X, axes->x_axis);
+ LogAxisValues(AMOTION_EVENT_AXIS_Y, axes->y_axis);
+ LogAxisValues(AMOTION_EVENT_AXIS_Z, axes->z_axis);
+ LogAxisValues(AMOTION_EVENT_AXIS_RZ, axes->rz_axis);
+ LogAxisValues(AMOTION_EVENT_AXIS_HAT_X, axes->x_hat);
+ LogAxisValues(AMOTION_EVENT_AXIS_HAT_Y, axes->y_hat);
+#endif
+}
+
+/************************************************************************/
+/************************************************************************/
+CAndroidJoyStick::CAndroidJoyStick()
+ : m_prev_device(0)
+ , m_prev_button(0)
+ , m_prev_holdtime(0)
+{
+ p_AMotionEvent_getAxisValue = (typeof(AMotionEvent_getAxisValue)*) dlsym(RTLD_DEFAULT, "AMotionEvent_getAxisValue");
+ CXBMCApp::android_printf("CAndroidJoystick: AMotionEvent_getAxisValue: %p", p_AMotionEvent_getAxisValue);
+}
+
+CAndroidJoyStick::~CAndroidJoyStick()
+{
+ while (!m_input_devices.empty())
+ {
+ APP_InputDeviceAxes *device_axes = m_input_devices.back();
+ delete device_axes;
+ m_input_devices.pop_back();
+ }
+}
+
+bool CAndroidJoyStick::onJoyStickKeyEvent(AInputEvent *event)
+{
+ if (event == NULL)
+ return false;
+
+ int32_t keycode = AKeyEvent_getKeyCode(event);
+ // watch this check, others might be different.
+ // AML IR Controller is AINPUT_SOURCE_GAMEPAD | AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD
+ // Gamestick Controller == AINPUT_SOURCE_GAMEPAD | AINPUT_SOURCE_KEYBOARD
+ // NVidiaShield Controller == AINPUT_SOURCE_GAMEPAD | AINPUT_SOURCE_KEYBOARD
+ // we want to reject AML IR Controller.
+ if (AInputEvent_getSource(event) == (AINPUT_SOURCE_GAMEPAD | AINPUT_SOURCE_KEYBOARD))
+ {
+ // GamePad events are AINPUT_EVENT_TYPE_KEY events,
+ // trap them here and revector valid ones as JoyButtons
+ // so we get keymap handling.
+ for (size_t i = 0; i < sizeof(GamePadMap) / sizeof(KeyMap); i++)
+ {
+ if (keycode == GamePadMap[i].nativeKey)
+ {
+ uint32_t holdtime = 0;
+ uint8_t button = GamePadMap[i].xbmcID;
+ int32_t action = AKeyEvent_getAction(event);
+ int32_t device = AInputEvent_getDeviceId(event);
+
+ if ((action == AKEY_EVENT_ACTION_UP))
+ {
+ // ProcessJoystickEvent does not understand up, ignore it.
+ m_prev_holdtime = m_prev_device = m_prev_button = 0;
+ return false;
+ }
+ else
+ {
+ if (m_prev_holdtime && device == m_prev_device && button == m_prev_button)
+ {
+ holdtime = CTimeUtils::GetFrameTime() - m_prev_holdtime;
+ }
+ else
+ {
+ m_prev_holdtime = CTimeUtils::GetFrameTime();
+ m_prev_device = device;
+ m_prev_button = button;
+ }
+ }
+
+ XBMC_JoyButton(device, button, holdtime, action == AKEY_EVENT_ACTION_UP);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool CAndroidJoyStick::onJoyStickMotionEvent(AInputEvent *event)
+{
+ if (event == NULL)
+ return false;
+
+ // match this device to a created device struct,
+ // create it if we do not find it.
+ APP_InputDeviceAxes *device_axes = NULL;
+ int32_t device = AInputEvent_getDeviceId(event);
+ // look for device name in our inputdevice cache.
+ for (size_t i = 0; i < m_input_devices.size(); i++)
+ {
+ if (m_input_devices[i]->id == device)
+ device_axes = m_input_devices[i];
+ }
+ if (!device_axes)
+ {
+ // as we see each axis, create a device axes and cache it.
+ device_axes = new APP_InputDeviceAxes;
+ SetupJoySticks(device_axes, device);
+ m_input_devices.push_back(device_axes);
+ }
+
+ // handle queued motion events, we
+ // ingnore history as it only relates to touch.
+ for (size_t p = 0; p < AMotionEvent_getPointerCount(event); p++)
+ ProcessMotionEvents(event, p, device, device_axes);
+
+ return true;
+}
+
+void CAndroidJoyStick::ProcessMotionEvents(AInputEvent *event,
+ size_t pointer_index, int32_t device, APP_InputDeviceAxes *axes)
+{
+ // Left joystick
+ if (axes->y_axis.enabled)
+ ProcessAxis(event, pointer_index, axes->y_axis, device, AXIS_LEFT_STICK_L_R, AMOTION_EVENT_AXIS_Y);
+ if (axes->x_axis.enabled)
+ ProcessAxis(event, pointer_index, axes->x_axis, device, AXIS_LEFT_STICK_U_D, AMOTION_EVENT_AXIS_X);
+
+ // Right joystick
+ if (axes->z_axis.enabled)
+ ProcessAxis(event, pointer_index, axes->z_axis, device, AXIS_RIGHT_STICK_L_R, AMOTION_EVENT_AXIS_Z);
+ if (axes->rz_axis.enabled)
+ ProcessAxis(event, pointer_index, axes->rz_axis,device, AXIS_RIGHT_STICK_U_D, AMOTION_EVENT_AXIS_RZ);
+
+ // Dpad
+ if (axes->y_hat.enabled)
+ ProcessHat(event, pointer_index, axes->y_hat, device, AXIS_DPAD_U_D, AMOTION_EVENT_AXIS_HAT_Y);
+ if (axes->x_hat.enabled)
+ ProcessHat(event, pointer_index, axes->x_hat, device, AXIS_DPAD_L_R, AMOTION_EVENT_AXIS_HAT_X);
+
+#if 0
+ CLog::Log(LOGDEBUG, "joystick event. x(%f), y(%f)", axes->x_axis.value, axes->y_axis.value);
+ CLog::Log(LOGDEBUG, "joystick event. z(%f), rz(%f)", axes->z_axis.value, axes->rz_axis.value);
+ CLog::Log(LOGDEBUG, "joystick event. xhat(%f), yhat(%f)", axes->x_hat.value, axes->y_hat.value);
+#endif
+}
+
+bool CAndroidJoyStick::ProcessHat(AInputEvent *event, size_t pointer_index,
+ APP_InputDeviceAxis &hat, int device, int keymap_axis, int android_axis)
+{
+ bool rtn = false;
+ // Dpad (quantized to -1.0, 0.0 and 1.0)
+ float value = AMotionEvent_getAxisValue(event, android_axis, pointer_index);
+ if (value != hat.value)
+ {
+ XBMC_JoyAxis(device, keymap_axis, value);
+ rtn = true;
+ }
+ hat.value = value;
+
+ return rtn;
+}
+
+bool CAndroidJoyStick::ProcessAxis(AInputEvent *event, size_t pointer_index,
+ APP_InputDeviceAxis &axis, int device, int keymap_axis, int android_axis)
+{
+ bool rtn = false;
+
+ float value = AMotionEvent_getAxisValue(event, android_axis, pointer_index);
+ //CLog::Log(LOGDEBUG, "ProcessAxis: keymap_axis(%d), value(%f)", keymap_axis, value);
+
+ value = AxisClampAsButton(axis, value);
+ if (value != axis.value)
+ {
+ XBMC_JoyAxis(device, keymap_axis, value);
+ rtn = true;
+ }
+ axis.value = value;
+
+ return rtn;
+}
+
+void CAndroidJoyStick::XBMC_JoyAxis(uint8_t device, uint8_t axis, float value)
+{
+ XBMC_Event newEvent = {};
+
+ newEvent.type = XBMC_JOYAXISMOTION;
+ newEvent.jaxis.type = XBMC_JOYAXISMOTION;
+ newEvent.jaxis.which = device;
+ newEvent.jaxis.axis = axis;
+ newEvent.jaxis.fvalue = value;
+
+ //CLog::Log(LOGDEBUG, "XBMC_Axis(%u, %u, %u, %f)", type, device, axis, value);
+ CWinEvents::MessagePush(&newEvent);
+}
+
+void CAndroidJoyStick::XBMC_JoyButton(uint8_t device, uint8_t button, uint32_t holdtime, bool up)
+{
+ XBMC_Event newEvent = {};
+
+ unsigned char type = up ? XBMC_JOYBUTTONUP : XBMC_JOYBUTTONDOWN;
+ newEvent.type = type;
+ newEvent.jbutton.type = type;
+ newEvent.jbutton.which = device;
+ newEvent.jbutton.button = button;
+ newEvent.jbutton.holdTime = holdtime;
+
+ //CXBMCApp::android_printf("CAndroidJoyStick::XBMC_JoyButton(%u, %u, %u, %d)",
+ // newEvent.jbutton.type, newEvent.jbutton.which, newEvent.jbutton.button, newEvent.jbutton.holdTime);
+
+ CWinEvents::MessagePush(&newEvent);
+}
--- /dev/null
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <vector>
+
+struct AInputEvent;
+
+typedef struct {
+ float flat;
+ float fuzz;
+ float min;
+ float max;
+ float range;
+ float value;
+ // internal helper values
+ bool enabled;
+ float deadzone;
+ float buttonclamp;
+} APP_InputDeviceAxis;
+
+typedef struct {
+ int32_t id;
+ APP_InputDeviceAxis x_hat;
+ APP_InputDeviceAxis y_hat;
+ APP_InputDeviceAxis x_axis;
+ APP_InputDeviceAxis y_axis;
+ APP_InputDeviceAxis z_axis;
+ APP_InputDeviceAxis rz_axis;
+} APP_InputDeviceAxes;
+
+class CAndroidJoyStick
+{
+public:
+ CAndroidJoyStick();
+ ~CAndroidJoyStick();
+
+ bool onJoyStickKeyEvent(AInputEvent* event);
+ bool onJoyStickMotionEvent(AInputEvent* event);
+
+private:
+ void ProcessMotionEvents(AInputEvent *event, size_t pointer_index,
+ int32_t device, APP_InputDeviceAxes *axes);
+ bool ProcessHat( AInputEvent *event, size_t pointer_index,
+ APP_InputDeviceAxis &hat, int device, int keymap_axis, int android_axis);
+ bool ProcessAxis(AInputEvent *event, size_t pointer_index,
+ APP_InputDeviceAxis &axis, int device, int keymap_axis, int android_axis);
+
+ void XBMC_JoyAxis(uint8_t device, uint8_t axis, float value);
+ void XBMC_JoyButton(uint8_t device, uint8_t button, uint32_t holdtime, bool up);
+
+ uint8_t m_prev_device;
+ uint8_t m_prev_button;
+ uint32_t m_prev_holdtime;
+ std::vector<APP_InputDeviceAxes*> m_input_devices;
+};
*/
#include "AndroidKey.h"
+#include "AndroidExtra.h"
#include "XBMCApp.h"
#include "guilib/Key.h"
#include "windowing/WinEvents.h"
-#include "AndroidExtra.h"
+
+typedef struct {
+ int32_t nativeKey;
+ int16_t xbmcKey;
+} KeyMap;
static KeyMap keyMap[] = {
{ AKEYCODE_UNKNOWN , XBMCK_LAST },
{ AKEYCODE_MEDIA_EJECT , XBMCK_EJECT },
};
-bool CAndroidKey::onKeyboardEvent(AInputEvent* event)
+bool CAndroidKey::onKeyboardEvent(AInputEvent *event)
{
- CXBMCApp::android_printf("%s", __PRETTY_FUNCTION__);
if (event == NULL)
return false;
+ int32_t flags = AKeyEvent_getFlags(event);
+ int32_t state = AKeyEvent_getMetaState(event);
+ int32_t action = AKeyEvent_getAction(event);
+ int32_t repeat = AKeyEvent_getRepeatCount(event);
int32_t keycode = AKeyEvent_getKeyCode(event);
- int32_t flags = AKeyEvent_getFlags(event);
- int32_t state = AKeyEvent_getMetaState(event);
- int32_t repeatCount = AKeyEvent_getRepeatCount(event);
// Check if we got some special key
uint16_t sym = XBMCK_UNKNOWN;
break;
}
}
-
+
// check if this is a key we don't want to handle
if (sym == XBMCK_LAST || sym == XBMCK_UNKNOWN)
return false;
if (state & AMETA_SYM_ON)
modifiers |= 0x000?;*/
- switch (AKeyEvent_getAction(event))
+ switch (action)
{
case AKEY_EVENT_ACTION_DOWN:
- CXBMCApp::android_printf("CXBMCApp: key down (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)",
- keycode, repeatCount, flags,
- (state & AMETA_ALT_ON) ? "yes" : "no",
- (state & AMETA_SHIFT_ON) ? "yes" : "no",
- (state & AMETA_SYM_ON) ? "yes" : "no");
+#if 1
+ CXBMCApp::android_printf("CAndroidKey: key down (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)",
+ keycode, repeat, flags,
+ (state & AMETA_ALT_ON) ? "yes" : "no",
+ (state & AMETA_SHIFT_ON) ? "yes" : "no",
+ (state & AMETA_SYM_ON) ? "yes" : "no");
+#endif
XBMC_Key((uint8_t)keycode, sym, modifiers, false);
return true;
case AKEY_EVENT_ACTION_UP:
- CXBMCApp::android_printf("CXBMCApp: key up (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)",
- keycode, repeatCount, flags,
- (state & AMETA_ALT_ON) ? "yes" : "no",
- (state & AMETA_SHIFT_ON) ? "yes" : "no",
- (state & AMETA_SYM_ON) ? "yes" : "no");
+#if 1
+ CXBMCApp::android_printf("CAndroidKey: key up (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)",
+ keycode, repeat, flags,
+ (state & AMETA_ALT_ON) ? "yes" : "no",
+ (state & AMETA_SHIFT_ON) ? "yes" : "no",
+ (state & AMETA_SYM_ON) ? "yes" : "no");
XBMC_Key((uint8_t)keycode, sym, modifiers, true);
+#endif
return true;
case AKEY_EVENT_ACTION_MULTIPLE:
- CXBMCApp::android_printf("CXBMCApp: key multiple (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)",
- keycode, repeatCount, flags,
- (state & AMETA_ALT_ON) ? "yes" : "no",
- (state & AMETA_SHIFT_ON) ? "yes" : "no",
- (state & AMETA_SYM_ON) ? "yes" : "no");
+#if 1
+ CXBMCApp::android_printf("CAndroidKey: key multiple (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)",
+ keycode, repeat, flags,
+ (state & AMETA_ALT_ON) ? "yes" : "no",
+ (state & AMETA_SHIFT_ON) ? "yes" : "no",
+ (state & AMETA_SYM_ON) ? "yes" : "no");
+#endif
break;
default:
- CXBMCApp::android_printf("CXBMCApp: unknown key (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)",
- keycode, repeatCount, flags,
- (state & AMETA_ALT_ON) ? "yes" : "no",
- (state & AMETA_SHIFT_ON) ? "yes" : "no",
- (state & AMETA_SYM_ON) ? "yes" : "no");
+#if 1
+ CXBMCApp::android_printf("CAndroidKey: unknown key (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)",
+ keycode, repeat, flags,
+ (state & AMETA_ALT_ON) ? "yes" : "no",
+ (state & AMETA_SHIFT_ON) ? "yes" : "no",
+ (state & AMETA_SYM_ON) ? "yes" : "no");
+#endif
break;
}
newEvent.key.keysym.unicode = key;
newEvent.key.keysym.mod = (XBMCMod)modifiers;
- CXBMCApp::android_printf("XBMC_Key(%u, %u, 0x%04X, %d)", code, key, modifiers, up);
+ //CXBMCApp::android_printf("XBMC_Key(%u, %u, 0x%04X, %d)", code, key, modifiers, up);
CWinEvents::MessagePush(&newEvent);
}
*
*/
-#include <stdint.h>
#include <android/input.h>
-typedef struct {
- int32_t nativeKey;
- int16_t xbmcKey;
-} KeyMap;
+#include <stdint.h>
+#include <string>
+#include <vector>
class CAndroidKey
{
public:
- CAndroidKey(){};
- ~CAndroidKey(){};
+ CAndroidKey() {};
+ ~CAndroidKey() {};
+
+ bool onKeyboardEvent(AInputEvent *event);
void XBMC_Key(uint8_t code, uint16_t key, uint16_t modifiers, bool up);
- bool onKeyboardEvent(AInputEvent* event);
-};
\ No newline at end of file
+ void XBMC_JoyButton(uint8_t id, uint8_t button, bool up);
+};
* <http://www.gnu.org/licenses/>.
*
*/
+
#include <android/input.h>
class CAndroidMouse
* <http://www.gnu.org/licenses/>.
*
*/
+
#include <android/input.h>
#include <math.h>
#include "EventLoop.h"
#include "XBMCApp.h"
+#include "AndroidExtra.h"
CEventLoop::CEventLoop(android_app* application)
: m_enabled(false),
int32_t CEventLoop::processInput(AInputEvent* event)
{
- int32_t type = AInputEvent_getType(event);
- switch (type)
+ int32_t rtn = 0;
+ int32_t type = AInputEvent_getType(event);
+ int32_t source = AInputEvent_getSource(event);
+ int32_t repeat = AKeyEvent_getRepeatCount(event);
+ int32_t keycod = AKeyEvent_getKeyCode(event);
+
+ switch(type)
{
+ case AINPUT_EVENT_TYPE_KEY:
+ if (source & AINPUT_SOURCE_GAMEPAD || source & AINPUT_SOURCE_JOYSTICK)
+ {
+ if (m_inputHandler->onJoyStickKeyEvent(event))
+ return true;
+ }
+ if (source & AINPUT_SOURCE_CLASS_BUTTON)
+ rtn = m_inputHandler->onKeyboardEvent(event);
+ break;
case AINPUT_EVENT_TYPE_MOTION:
- switch (AInputEvent_getSource(event))
+ switch(source)
{
case AINPUT_SOURCE_TOUCHSCREEN:
- return m_inputHandler->onTouchEvent(event);
+ rtn = m_inputHandler->onTouchEvent(event);
+ break;
case AINPUT_SOURCE_MOUSE:
- return m_inputHandler->onMouseEvent(event);
+ rtn = m_inputHandler->onMouseEvent(event);
+ break;
+ case AINPUT_SOURCE_GAMEPAD:
+ case AINPUT_SOURCE_JOYSTICK:
+ rtn = m_inputHandler->onJoyStickMotionEvent(event);
+ break;
}
break;
-
- case AINPUT_EVENT_TYPE_KEY:
- return m_inputHandler->onKeyboardEvent(event);
}
- return 0;
+ return rtn;
}
void CEventLoop::activityCallback(android_app* application, int32_t command)
int32_t CEventLoop::inputCallback(android_app* application, AInputEvent* event)
{
- if (application == NULL || application->userData == NULL ||
- event == NULL)
+ if (application == NULL || application->userData == NULL || event == NULL)
return 0;
CEventLoop& eventLoop = *((CEventLoop*)application->userData);
+
return eventLoop.processInput(event);
}
#include "AndroidTouch.h"
#include "AndroidKey.h"
#include "AndroidMouse.h"
+#include "AndroidJoyStick.h"
-class IInputHandler : public CAndroidTouch, public CAndroidKey, public CAndroidMouse
+class IInputHandler
+: public CAndroidKey
+, public CAndroidMouse
+, public CAndroidTouch
+, public CAndroidJoyStick
{
public:
- IInputHandler() : CAndroidTouch(), CAndroidKey(), CAndroidMouse() {}
+ IInputHandler()
+ : CAndroidKey()
+ , CAndroidMouse()
+ , CAndroidTouch()
+ , CAndroidJoyStick()
+ {}
virtual void setDPI(uint32_t dpi) { CAndroidTouch::setDPI(dpi); }
};
SRCS += AndroidKey.cpp
SRCS += AndroidTouch.cpp
SRCS += AndroidMouse.cpp
+SRCS += AndroidJoyStick.cpp
SRCS += GraphicBuffer.cpp
SRCS += EventLoop.cpp
SRCS += XBMCApp.cpp
unsigned char which; /* The joystick device index */
unsigned char axis; /* The joystick axis index */
int16_t value; /* The axis value (range: -32768 to 32767) */
+ float fvalue; /* The axis value (range: -1.0 to 1.0) */
} XBMC_JoyAxisEvent;
/* Joystick trackball motion event structure */
/*
-* Copyright (C) 2010-2013 Team XBMC
+ * Copyright (C) 2010-2013 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
*/
#include "system.h"
-#include <list>
+
#include "WinEventsAndroid.h"
+
#include "Application.h"
-#include "threads/CriticalSection.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/XBMC_vkeys.h"
+#include "utils/log.h"
+#include "windowing/WindowingFactory.h"
+
+#include "android/jni/View.h"
+
+#define DEBUG_MESSAGEPUMP 0
+
+#define ALMOST_ZERO 0.125f
+enum {
+ EVENT_STATE_TEST,
+ EVENT_STATE_HOLD,
+ EVENT_STATE_REPEAT
+};
+
+/************************************************************************/
+/************************************************************************/
+static bool different_event(XBMC_Event &curEvent, XBMC_Event &newEvent)
+{
+ // different axis
+ if (curEvent.jaxis.axis != newEvent.jaxis.axis)
+ return true;
+
+ // different axis direction (handles -1 vs 1)
+ if (signbit(curEvent.jaxis.fvalue) != signbit(newEvent.jaxis.fvalue))
+ return true;
-static CCriticalSection g_inputCond;
+ // different axis value (handles 0 vs 1 or -1)
+ if ((fabs(curEvent.jaxis.fvalue) < ALMOST_ZERO) != (fabs(newEvent.jaxis.fvalue) < ALMOST_ZERO))
+ return true;
-static std::list<XBMC_Event> events;
+ return false;
+}
-void CWinEventsAndroid::DeInit()
+/************************************************************************/
+/************************************************************************/
+CWinEventsAndroid::CWinEventsAndroid()
+: CThread("CWinEventsAndroid")
{
+ CLog::Log(LOGDEBUG, "CWinEventsAndroid::CWinEventsAndroid");
+ Create();
}
-void CWinEventsAndroid::Init()
+CWinEventsAndroid::~CWinEventsAndroid()
{
+ m_bStop = true;
+ StopThread(true);
}
void CWinEventsAndroid::MessagePush(XBMC_Event *newEvent)
{
- CSingleLock lock(g_inputCond);
- events.push_back(*newEvent);
+ CSingleLock lock(m_eventsCond);
+
+ m_events.push_back(*newEvent);
+ #if DEBUG_MESSAGEPUMP
+ CLog::Log(LOGDEBUG, "push event, size(%d), fvalue(%f)",
+ m_events.size(), newEvent->jaxis.fvalue);
+ #endif
+}
+
+void CWinEventsAndroid::MessagePushRepeat(XBMC_Event *repeatEvent)
+{
+ CSingleLock lock(m_eventsCond);
+
+ std::list<XBMC_Event>::iterator itt;
+ for (itt = m_events.begin(); itt != m_events.end(); ++itt)
+ {
+ // we have events pending, if we we just
+ // repush, we might push the repeat event
+ // in back of a canceling non-active event.
+ // do not repush if pending are different event.
+ if (different_event(*itt, *repeatEvent))
+ {
+ #if DEBUG_MESSAGEPUMP
+ CLog::Log(LOGDEBUG, "repush skip, size(%d), fvalue(%f)",
+ m_events.size(), repeatEvent->jaxis.fvalue);
+ #endif
+ return;
+ }
+ }
+ // is a repeat, push it
+ m_events.push_back(*repeatEvent);
+ #if DEBUG_MESSAGEPUMP
+ CLog::Log(LOGDEBUG, "repush event, size(%d), fvalue(%f)",
+ m_events.size(), repeatEvent->jaxis.fvalue);
+ #endif
}
bool CWinEventsAndroid::MessagePump()
{
bool ret = false;
- // Do not always loop, only pump the initial queued count events. else if ui keep pushing
- // events the loop won't finish then it will block xbmc main message loop.
- for (size_t pumpEventCount = GetQueueSize(); pumpEventCount > 0; --pumpEventCount)
+ // Do not always loop, only pump the initial queued count events. else if ui keep pushing
+ // events the loop won't finish then it will block xbmc main message loop.
+ for (size_t pumpEventCount = GetQueueSize(); pumpEventCount > 0; --pumpEventCount)
{
-
- // Pop up only one event per time since in App::OnEvent it may init modal dialog which init
- // deeper message loop and call the deeper MessagePump from there.
- XBMC_Event pumpEvent;
- {
- CSingleLock lock(g_inputCond);
- if (events.size() == 0)
- return ret;
- pumpEvent = events.front();
- events.pop_front();
- }
-
- ret |= g_application.OnEvent(pumpEvent);
+ // Pop up only one event per time since in App::OnEvent it may init modal dialog which init
+ // deeper message loop and call the deeper MessagePump from there.
+ XBMC_Event pumpEvent;
+ {
+ CSingleLock lock(m_eventsCond);
+ if (m_events.empty())
+ return ret;
+ pumpEvent = m_events.front();
+ m_events.pop_front();
+ #if DEBUG_MESSAGEPUMP
+ CLog::Log(LOGDEBUG, " pop event, size(%d), fvalue(%f)",
+ m_events.size(), pumpEvent.jaxis.fvalue);
+ #endif
+ }
+
+ if ((pumpEvent.type == XBMC_JOYBUTTONUP) ||
+ (pumpEvent.type == XBMC_JOYBUTTONDOWN) ||
+ (pumpEvent.type == XBMC_JOYAXISMOTION))
+ {
+ int item;
+ int type;
+ uint32_t holdTime;
+ APP_InputDevice input_device;
+ float amount = 1.0f;
+
+ type = pumpEvent.type;
+ if (type == XBMC_JOYAXISMOTION)
+ {
+ // The typical joystick keymap xml has the following where 'id' is the axis
+ // and 'limit' is which action to choose (ie. Up or Down).
+ // <axis id="5" limit="-1">Up</axis>
+ // <axis id="5" limit="+1">Down</axis>
+ // One would think that limits is in reference to fvalue but
+ // it is really in reference to id :) The sign of item passed
+ // into ProcessJoystickEvent indicates the action mapping.
+ item = pumpEvent.jaxis.axis;
+ if (fabs(pumpEvent.jaxis.fvalue) < ALMOST_ZERO)
+ amount = 0.0f;
+ else if (pumpEvent.jaxis.fvalue < 0.0f)
+ item = -item;
+ holdTime = 0;
+ input_device.id = pumpEvent.jaxis.which;
+ }
+ else
+ {
+ item = pumpEvent.jbutton.button;
+ holdTime = pumpEvent.jbutton.holdTime;
+ input_device.id = pumpEvent.jbutton.which;
+ }
+
+ // look for device name in our inputdevice cache
+ // so we can lookup and match the right joystick.xxx.xml
+ for (size_t i = 0; i < m_input_devices.size(); i++)
+ {
+ if (m_input_devices[i].id == input_device.id)
+ input_device.name = m_input_devices[i].name;
+ }
+ if (input_device.name.empty())
+ {
+ // not in inputdevice cache, fetch and cache it.
+ CJNIViewInputDevice view_input_device = CJNIViewInputDevice::getDevice(input_device.id);
+ input_device.name = view_input_device.getName();
+ CLog::Log(LOGDEBUG, "CWinEventsAndroid::MessagePump:caching id(%d), device(%s)",
+ input_device.id, input_device.name.c_str());
+ m_input_devices.push_back(input_device);
+ }
+
+ if (type == XBMC_JOYAXISMOTION)
+ {
+ // Joystick autorepeat -> only handle axis
+ CSingleLock lock(m_lasteventCond);
+ m_lastevent.push(pumpEvent);
+ }
+
+ if (fabs(amount) >= ALMOST_ZERO)
+ {
+ ret |= g_application.ProcessJoystickEvent(input_device.name,
+ item, type == XBMC_JOYAXISMOTION, amount, holdTime);
+ }
+ }
+ else
+ {
+ ret |= g_application.OnEvent(pumpEvent);
+ }
+
+ if (pumpEvent.type == XBMC_MOUSEBUTTONUP)
+ g_windowManager.SendMessage(GUI_MSG_UNFOCUS_ALL, 0, 0, 0, 0);
}
return ret;
size_t CWinEventsAndroid::GetQueueSize()
{
- CSingleLock lock(g_inputCond);
- return events.size();
+ CSingleLock lock(m_eventsCond);
+ return m_events.size();
+}
+
+void CWinEventsAndroid::Process()
+{
+ uint32_t timeout = 10;
+ uint32_t holdTimeout = 500;
+ uint32_t repeatTimeout = 100;
+ uint32_t repeatDuration = 0;
+
+ XBMC_Event cur_event;
+ int state = EVENT_STATE_TEST;
+ while (!m_bStop)
+ {
+ // run a 10ms (timeout) wait cycle
+ Sleep(timeout);
+
+ CSingleLock lock(m_lasteventCond);
+
+ switch(state)
+ {
+ default:
+ case EVENT_STATE_TEST:
+ // check for axis action events
+ if (!m_lastevent.empty())
+ {
+ if (fabs(m_lastevent.front().jaxis.fvalue) >= ALMOST_ZERO)
+ {
+ // new active event
+ cur_event = m_lastevent.front();
+ #if DEBUG_MESSAGEPUMP
+ CLog::Log(LOGDEBUG, "test -> hold, size(%d), fvalue(%f)",
+ m_lastevent.size(), m_lastevent.front().jaxis.fvalue);
+ #endif
+ m_lastevent.pop();
+ repeatDuration = 0;
+ state = EVENT_STATE_HOLD;
+ break;
+ }
+ #if DEBUG_MESSAGEPUMP
+ CLog::Log(LOGDEBUG, "munch test, size(%d), fvalue(%f)",
+ m_lastevent.size(), m_lastevent.front().jaxis.fvalue);
+ #endif
+ // non-active event, eat it
+ m_lastevent.pop();
+ }
+ break;
+
+ case EVENT_STATE_HOLD:
+ repeatDuration += timeout;
+ if (!m_lastevent.empty())
+ {
+ if (different_event(cur_event, m_lastevent.front()))
+ {
+ // different axis event, cycle back to test
+ state = EVENT_STATE_TEST;
+ break;
+ }
+ #if DEBUG_MESSAGEPUMP
+ CLog::Log(LOGDEBUG, "munch hold, size(%d), fvalue(%f)",
+ m_lastevent.size(), m_lastevent.front().jaxis.fvalue);
+ #endif
+ // same axis event, eat it
+ m_lastevent.pop();
+ }
+ if (repeatDuration >= holdTimeout)
+ {
+ CLog::Log(LOGDEBUG, "hold ->repeat, size(%d), repeatDuration(%d)", m_lastevent.size(), repeatDuration);
+ state = EVENT_STATE_REPEAT;
+ }
+ break;
+
+ case EVENT_STATE_REPEAT:
+ repeatDuration += timeout;
+ if (!m_lastevent.empty())
+ {
+ if (different_event(cur_event, m_lastevent.front()))
+ {
+ // different axis event, cycle back to test
+ state = EVENT_STATE_TEST;
+ #if DEBUG_MESSAGEPUMP
+ CLog::Log(LOGDEBUG, "repeat-> test, size(%d), fvalue(%f)",
+ m_lastevent.size(), m_lastevent.front().jaxis.fvalue);
+ #endif
+ break;
+ }
+ #if DEBUG_MESSAGEPUMP
+ CLog::Log(LOGDEBUG, "munch repeat, size(%d), fvalue(%f)",
+ m_lastevent.size(), m_lastevent.front().jaxis.fvalue);
+ #endif
+ // same axis event, eat it
+ m_lastevent.pop();
+ }
+ if (repeatDuration >= holdTimeout)
+ {
+ // this is a repeat, push it
+ MessagePushRepeat(&cur_event);
+ // assuming holdTimeout > repeatTimeout,
+ // just subtract the repeatTimeout
+ // to get the next cycle time
+ repeatDuration -= repeatTimeout;
+ }
+ break;
+ }
+ }
}
/*
-* Copyright (C) 2010-2013 Team XBMC
+ * Copyright (C) 2010-2013 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
#ifndef WINDOW_EVENTS_ANDROID_H
#define WINDOW_EVENTS_ANDROID_H
+#include <list>
+#include <queue>
+#include <vector>
+#include <string>
+
+#include "threads/Event.h"
+#include "threads/Thread.h"
+#include "threads/CriticalSection.h"
#include "windowing/WinEvents.h"
-class CWinEventsAndroid : public IWinEvents
+typedef struct {
+ int32_t id;
+ std::string name;
+} APP_InputDevice;
+
+class CWinEventsAndroid : public IWinEvents, public CThread
{
public:
- static void Init();
- static void DeInit();
- void MessagePush(XBMC_Event *newEvent);
- bool MessagePump();
+ CWinEventsAndroid();
+ ~CWinEventsAndroid();
+
+ void MessagePush(XBMC_Event *newEvent);
+ void MessagePushRepeat(XBMC_Event *repeatEvent);
+ bool MessagePump();
virtual size_t GetQueueSize();
+
+private:
+ // for CThread
+ virtual void Process();
+
+ CCriticalSection m_eventsCond;
+ std::list<XBMC_Event> m_events;
+
+ CCriticalSection m_lasteventCond;
+ std::queue<XBMC_Event> m_lastevent;
+
+ std::vector<APP_InputDevice> m_input_devices;
};
#endif // WINDOW_EVENTS_ANDROID_H