aboutsummaryrefslogtreecommitdiff
path: root/src/windowing/android/WinEventsAndroid.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/windowing/android/WinEventsAndroid.cpp')
-rw-r--r--src/windowing/android/WinEventsAndroid.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/src/windowing/android/WinEventsAndroid.cpp b/src/windowing/android/WinEventsAndroid.cpp
new file mode 100644
index 0000000000..f1fbf11e98
--- /dev/null
+++ b/src/windowing/android/WinEventsAndroid.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2010-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 "system.h"
+
+#include "WinEventsAndroid.h"
+
+#include "Application.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/XBMC_vkeys.h"
+#include "input/SDLJoystick.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 type
+ if (curEvent.type != newEvent.type)
+ return true;
+
+ // Hat
+ if(newEvent.type == XBMC_JOYHATMOTION)
+ return (curEvent.jhat.value != newEvent.jhat.value);
+
+ // 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;
+
+ // different axis value (handles 0 vs 1 or -1)
+ if ((fabs(curEvent.jaxis.fvalue) < ALMOST_ZERO) != (fabs(newEvent.jaxis.fvalue) < ALMOST_ZERO))
+ return true;
+
+ return false;
+}
+
+/************************************************************************/
+/************************************************************************/
+CWinEventsAndroid::CWinEventsAndroid()
+: CThread("CWinEventsAndroid")
+{
+ CLog::Log(LOGDEBUG, "CWinEventsAndroid::CWinEventsAndroid");
+ Create();
+}
+
+CWinEventsAndroid::~CWinEventsAndroid()
+{
+ m_bStop = true;
+ StopThread(true);
+}
+
+void CWinEventsAndroid::MessagePush(XBMC_Event *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)
+ {
+ // 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) ||
+ (pumpEvent.type == XBMC_JOYHATMOTION))
+ {
+ int item;
+ int type;
+ uint32_t holdTime;
+ APP_InputDevice input_device;
+ float amount = 1.0f;
+ short input_type;
+
+ type = pumpEvent.type;
+ switch (type)
+ {
+ case 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;
+ input_type = JACTIVE_AXIS;
+ break;
+
+ case XBMC_JOYHATMOTION:
+ item = pumpEvent.jhat.hat | 0xFFF00000 | (pumpEvent.jhat.value<<16);
+ holdTime = 0;
+ input_device.id = pumpEvent.jhat.which;
+ input_type = JACTIVE_HAT;
+ break;
+
+ case XBMC_JOYBUTTONUP:
+ case XBMC_JOYBUTTONDOWN:
+ item = pumpEvent.jbutton.button;
+ holdTime = pumpEvent.jbutton.holdTime;
+ input_device.id = pumpEvent.jbutton.which;
+ input_type = JACTIVE_BUTTON;
+ break;
+ }
+
+ // 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 || type == XBMC_JOYHATMOTION)
+ {
+ // 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, input_type, 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(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 ((m_lastevent.front().type == XBMC_JOYAXISMOTION && fabs(m_lastevent.front().jaxis.fvalue) >= ALMOST_ZERO)
+ || (m_lastevent.front().type == XBMC_JOYHATMOTION && m_lastevent.front().jhat.value > XBMC_HAT_CENTERED))
+ {
+ // 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;
+ }
+ }
+}