diff options
Diffstat (limited to 'src/windowing/android/WinEventsAndroid.cpp')
-rw-r--r-- | src/windowing/android/WinEventsAndroid.cpp | 347 |
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; + } + } +} |