diff options
author | dhewg | 2011-03-15 23:30:17 +0100 |
---|---|---|
committer | dhewg | 2011-03-16 20:54:49 +0100 |
commit | adef4c3f4256a690b374b9801279952c39ccf7a4 (patch) | |
tree | 691f0ac0f5e42708ec1620629592703acff7f7ae /backends/platform/android/events.cpp | |
parent | e71c2cf850388b2a25200d544d2fb422525b4c88 (diff) | |
download | scummvm-rg350-adef4c3f4256a690b374b9801279952c39ccf7a4.tar.gz scummvm-rg350-adef4c3f4256a690b374b9801279952c39ccf7a4.tar.bz2 scummvm-rg350-adef4c3f4256a690b374b9801279952c39ccf7a4.zip |
ANDROID: Input system overhaul
Rewritten input system with many new feature.
Fixed related bugs and shortcomings on the way.
Diffstat (limited to 'backends/platform/android/events.cpp')
-rw-r--r-- | backends/platform/android/events.cpp | 661 |
1 files changed, 661 insertions, 0 deletions
diff --git a/backends/platform/android/events.cpp b/backends/platform/android/events.cpp new file mode 100644 index 0000000000..1715e6a8a7 --- /dev/null +++ b/backends/platform/android/events.cpp @@ -0,0 +1,661 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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 + * of the License, 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(__ANDROID__) + +#include "common/events.h" + +#include "backends/platform/android/android.h" +#include "backends/platform/android/jni.h" + +// $ANDROID_NDK/platforms/android-9/arch-arm/usr/include/android/keycodes.h +// http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=libs/ui/Input.cpp +// http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/java/android/view/KeyEvent.java + +// event type +enum { + JE_SYS_KEY = 0, + JE_KEY = 1, + JE_DOWN = 2, + JE_SCROLL = 3, + JE_TAP = 4, + JE_DOUBLE_TAP = 5, + JE_BALL = 6, + JE_QUIT = 0x1000 +}; + +// action type +enum { + JACTION_DOWN = 0, + JACTION_UP = 1, + JACTION_MULTIPLE = 2 +}; + +// system keys +enum { + JKEYCODE_SOFT_RIGHT = 2, + JKEYCODE_HOME = 3, + JKEYCODE_BACK = 4, + JKEYCODE_CALL = 5, + JKEYCODE_ENDCALL = 6, + JKEYCODE_VOLUME_UP = 24, + JKEYCODE_VOLUME_DOWN = 25, + JKEYCODE_POWER = 26, + JKEYCODE_CAMERA = 27, + JKEYCODE_HEADSETHOOK = 79, + JKEYCODE_FOCUS = 80, + JKEYCODE_MENU = 82, + JKEYCODE_SEARCH = 84, + JKEYCODE_MUTE = 91, + JKEYCODE_MEDIA_PLAY_PAUSE = 85, + JKEYCODE_MEDIA_STOP = 86, + JKEYCODE_MEDIA_NEXT = 87, + JKEYCODE_MEDIA_PREVIOUS = 88, + JKEYCODE_MEDIA_REWIND = 89, + JKEYCODE_MEDIA_FAST_FORWARD = 90 +}; + +// five-way navigation control +enum { + JKEYCODE_DPAD_UP = 19, + JKEYCODE_DPAD_DOWN = 20, + JKEYCODE_DPAD_LEFT = 21, + JKEYCODE_DPAD_RIGHT = 22, + JKEYCODE_DPAD_CENTER = 23 +}; + +// meta modifier +enum { + JMETA_SHIFT = 0x01, + JMETA_ALT = 0x02, + JMETA_SYM = 0x04, + JMETA_CTRL = 0x1000 +}; + +// map android key codes to our kbd codes +static const Common::KeyCode jkeymap[] = { + Common::KEYCODE_INVALID, // KEYCODE_UNKNOWN + Common::KEYCODE_INVALID, // KEYCODE_SOFT_LEFT + Common::KEYCODE_INVALID, // KEYCODE_SOFT_RIGHT + Common::KEYCODE_INVALID, // KEYCODE_HOME + Common::KEYCODE_INVALID, // KEYCODE_BACK + Common::KEYCODE_INVALID, // KEYCODE_CALL + Common::KEYCODE_INVALID, // KEYCODE_ENDCALL + Common::KEYCODE_0, // KEYCODE_0 + Common::KEYCODE_1, // KEYCODE_1 + Common::KEYCODE_2, // KEYCODE_2 + Common::KEYCODE_3, // KEYCODE_3 + Common::KEYCODE_4, // KEYCODE_4 + Common::KEYCODE_5, // KEYCODE_5 + Common::KEYCODE_6, // KEYCODE_6 + Common::KEYCODE_7, // KEYCODE_7 + Common::KEYCODE_8, // KEYCODE_8 + Common::KEYCODE_9, // KEYCODE_9 + Common::KEYCODE_ASTERISK, // KEYCODE_STAR + Common::KEYCODE_HASH, // KEYCODE_POUND + Common::KEYCODE_INVALID, // KEYCODE_DPAD_UP + Common::KEYCODE_INVALID, // KEYCODE_DPAD_DOWN + Common::KEYCODE_INVALID, // KEYCODE_DPAD_LEFT + Common::KEYCODE_INVALID, // KEYCODE_DPAD_RIGHT + Common::KEYCODE_INVALID, // KEYCODE_DPAD_CENTER + Common::KEYCODE_INVALID, // KEYCODE_VOLUME_UP + Common::KEYCODE_INVALID, // KEYCODE_VOLUME_DOWN + Common::KEYCODE_INVALID, // KEYCODE_POWER + Common::KEYCODE_INVALID, // KEYCODE_CAMERA + Common::KEYCODE_INVALID, // KEYCODE_CLEAR + Common::KEYCODE_a, // KEYCODE_A + Common::KEYCODE_b, // KEYCODE_B + Common::KEYCODE_c, // KEYCODE_C + Common::KEYCODE_d, // KEYCODE_D + Common::KEYCODE_e, // KEYCODE_E + Common::KEYCODE_f, // KEYCODE_F + Common::KEYCODE_g, // KEYCODE_G + Common::KEYCODE_h, // KEYCODE_H + Common::KEYCODE_i, // KEYCODE_I + Common::KEYCODE_j, // KEYCODE_J + Common::KEYCODE_k, // KEYCODE_K + Common::KEYCODE_l, // KEYCODE_L + Common::KEYCODE_m, // KEYCODE_M + Common::KEYCODE_n, // KEYCODE_N + Common::KEYCODE_o, // KEYCODE_O + Common::KEYCODE_p, // KEYCODE_P + Common::KEYCODE_q, // KEYCODE_Q + Common::KEYCODE_r, // KEYCODE_R + Common::KEYCODE_s, // KEYCODE_S + Common::KEYCODE_t, // KEYCODE_T + Common::KEYCODE_u, // KEYCODE_U + Common::KEYCODE_v, // KEYCODE_V + Common::KEYCODE_w, // KEYCODE_W + Common::KEYCODE_x, // KEYCODE_X + Common::KEYCODE_y, // KEYCODE_Y + Common::KEYCODE_z, // KEYCODE_Z + Common::KEYCODE_COMMA, // KEYCODE_COMMA + Common::KEYCODE_PERIOD, // KEYCODE_PERIOD + Common::KEYCODE_LALT, // KEYCODE_ALT_LEFT + Common::KEYCODE_RALT, // KEYCODE_ALT_RIGHT + Common::KEYCODE_LSHIFT, // KEYCODE_SHIFT_LEFT + Common::KEYCODE_RSHIFT, // KEYCODE_SHIFT_RIGHT + Common::KEYCODE_TAB, // KEYCODE_TAB + Common::KEYCODE_SPACE, // KEYCODE_SPACE + Common::KEYCODE_LCTRL, // KEYCODE_SYM + Common::KEYCODE_INVALID, // KEYCODE_EXPLORER + Common::KEYCODE_INVALID, // KEYCODE_ENVELOPE + Common::KEYCODE_RETURN, // KEYCODE_ENTER + Common::KEYCODE_BACKSPACE, // KEYCODE_DEL + Common::KEYCODE_BACKQUOTE, // KEYCODE_GRAVE + Common::KEYCODE_MINUS, // KEYCODE_MINUS + Common::KEYCODE_EQUALS, // KEYCODE_EQUALS + Common::KEYCODE_LEFTPAREN, // KEYCODE_LEFT_BRACKET + Common::KEYCODE_RIGHTPAREN, // KEYCODE_RIGHT_BRACKET + Common::KEYCODE_BACKSLASH, // KEYCODE_BACKSLASH + Common::KEYCODE_SEMICOLON, // KEYCODE_SEMICOLON + Common::KEYCODE_QUOTE, // KEYCODE_APOSTROPHE + Common::KEYCODE_SLASH, // KEYCODE_SLASH + Common::KEYCODE_AT, // KEYCODE_AT + Common::KEYCODE_INVALID, // KEYCODE_NUM + Common::KEYCODE_INVALID, // KEYCODE_HEADSETHOOK + Common::KEYCODE_INVALID, // KEYCODE_FOCUS + Common::KEYCODE_PLUS, // KEYCODE_PLUS + Common::KEYCODE_INVALID, // KEYCODE_MENU + Common::KEYCODE_INVALID, // KEYCODE_NOTIFICATION + Common::KEYCODE_INVALID, // KEYCODE_SEARCH + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_PLAY_PAUSE + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_STOP + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_NEXT + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_PREVIOUS + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_REWIND + Common::KEYCODE_INVALID, // KEYCODE_MEDIA_FAST_FORWARD + Common::KEYCODE_INVALID, // KEYCODE_MUTE + Common::KEYCODE_PAGEUP, // KEYCODE_PAGE_UP + Common::KEYCODE_PAGEDOWN // KEYCODE_PAGE_DOWN +}; + +// floating point. use sparingly +template <class T> +static inline T scalef(T in, float numerator, float denominator) { + return static_cast<float>(in) * numerator / denominator; +} + +void OSystem_Android::setupKeymapper() { +#ifdef ENABLE_KEYMAPPER + using namespace Common; + + Keymapper *mapper = getEventManager()->getKeymapper(); + + HardwareKeySet *keySet = new HardwareKeySet(); + + keySet->addHardwareKey( + new HardwareKey("n", KeyState(KEYCODE_n), "n (vk)", + kTriggerLeftKeyType, + kVirtualKeyboardActionType)); + + mapper->registerHardwareKeySet(keySet); + + Keymap *globalMap = new Keymap("global"); + Action *act; + + act = new Action(globalMap, "VIRT", "Display keyboard", + kVirtualKeyboardActionType); + act->addKeyEvent(KeyState(KEYCODE_F7, ASCII_F7, 0)); + + mapper->addGlobalKeymap(globalMap); + + mapper->pushKeymap("global"); +#endif +} + +void OSystem_Android::warpMouse(int x, int y) { + ENTER("%d, %d", x, y); + + Common::Event e; + + e.type = Common::EVENT_MOUSEMOVE; + e.mouse.x = x; + e.mouse.y = y; + + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); +} + +void OSystem_Android::clipMouse(Common::Point &p) { + const GLESBaseTexture *tex; + + if (_show_overlay) + tex = _overlay_texture; + else + tex = _game_texture; + + p.x = CLIP(p.x, int16(0), int16(tex->width())); + p.y = CLIP(p.y, int16(0), int16(tex->height())); +} + +void OSystem_Android::scaleMouse(Common::Point &p, int x, int y, + bool deductDrawRect) { + const GLESBaseTexture *tex; + + if (_show_overlay) + tex = _overlay_texture; + else + tex = _game_texture; + + const Common::Rect &r = tex->getDrawRect(); + + if (_touchpad_mode) { + x = x * 100 / _touchpad_scale; + y = y * 100 / _touchpad_scale; + } + + if (deductDrawRect) { + x -= r.left; + y -= r.top; + } + + p.x = scalef(x, tex->width(), r.width()); + p.y = scalef(y, tex->height(), r.height()); +} + +void OSystem_Android::updateEventScale() { + const GLESBaseTexture *tex; + + if (_show_overlay) + tex = _overlay_texture; + else + tex = _game_texture; + + _eventScaleY = 100 * 480 / tex->height(); + _eventScaleX = 100 * 640 / tex->width(); +} + +void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3, + int arg4, int arg5) { + Common::Event e; + + switch (type) { + case JE_SYS_KEY: + switch (arg1) { + case JACTION_DOWN: + e.type = Common::EVENT_KEYDOWN; + break; + case JACTION_UP: + e.type = Common::EVENT_KEYUP; + break; + default: + LOGE("unhandled jaction on system key: %d", arg1); + return; + } + + switch (arg2) { + case JKEYCODE_BACK: + e.kbd.keycode = Common::KEYCODE_ESCAPE; + e.kbd.ascii = Common::ASCII_ESCAPE; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + // special case. we'll only get it's up event + case JKEYCODE_MENU: + e.type = Common::EVENT_MAINMENU; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + default: + LOGW("unmapped system key: %d", arg2); + return; + } + + break; + + case JE_KEY: + // five-way first + switch (arg2) { + case JKEYCODE_DPAD_UP: + case JKEYCODE_DPAD_DOWN: + case JKEYCODE_DPAD_LEFT: + case JKEYCODE_DPAD_RIGHT: + { + if (arg1 != JACTION_DOWN) + return; + + e.type = Common::EVENT_MOUSEMOVE; + e.synthetic = true; + + e.mouse = getEventManager()->getMousePos(); + + int16 *c; + int s; + + if (arg2 == JKEYCODE_DPAD_UP || arg2 == JKEYCODE_DPAD_DOWN) { + c = &e.mouse.y; + s = _eventScaleY; + } else { + c = &e.mouse.x; + s = _eventScaleX; + } + + // the longer the button held, the faster the pointer is + // TODO put these values in some option dlg? + int f = CLIP(arg5, 1, 8) * _dpad_scale * 100 / s; + + *c += ((arg2 == JKEYCODE_DPAD_UP || + arg2 == JKEYCODE_DPAD_LEFT) ? -1 : 1) * f; + + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + } + + return; + + case JKEYCODE_DPAD_CENTER: + switch (arg1) { + case JACTION_DOWN: + e.type = Common::EVENT_LBUTTONDOWN; + break; + case JACTION_UP: + e.type = Common::EVENT_LBUTTONUP; + break; + default: + LOGE("unhandled jaction on dpad key: %d", arg1); + return; + } + + { + const Common::Point &m = getEventManager()->getMousePos(); + + e.mouse = m; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + } + + return; + } + + switch (arg1) { + case JACTION_DOWN: + e.type = Common::EVENT_KEYDOWN; + break; + case JACTION_UP: + e.type = Common::EVENT_KEYUP; + break; + default: + LOGE("unhandled jaction on key: %d", arg1); + return; + } + + if (arg2 < 1 || arg2 > ARRAYSIZE(jkeymap)) { + LOGE("received invalid keycode: %d", arg2); + return; + } + + if (arg5 > 0) + e.synthetic = true; + + e.kbd.keycode = jkeymap[arg2]; + e.kbd.ascii = arg3; + + if (arg4 & JMETA_SHIFT) + e.kbd.flags |= Common::KBD_SHIFT; + if (arg4 & JMETA_ALT) + e.kbd.flags |= Common::KBD_ALT; + if (arg4 & (JMETA_SYM | JMETA_CTRL)) + e.kbd.flags |= Common::KBD_CTRL; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + case JE_DOWN: + _touch_pt_down = getEventManager()->getMousePos(); + break; + + case JE_SCROLL: + e.type = Common::EVENT_MOUSEMOVE; + + if (_touchpad_mode) { + scaleMouse(e.mouse, arg3 - arg1, arg4 - arg2, false); + e.mouse += _touch_pt_down; + clipMouse(e.mouse); + } else { + scaleMouse(e.mouse, arg3, arg4); + clipMouse(e.mouse); + } + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + case JE_TAP: + e.type = Common::EVENT_MOUSEMOVE; + + if (_touchpad_mode) { + e.mouse = getEventManager()->getMousePos(); + } else { + scaleMouse(e.mouse, arg1, arg2); + clipMouse(e.mouse); + } + + { + Common::EventType down, up; + + // TODO put these values in some option dlg? + if (arg3 > 1000) { + down = Common::EVENT_MBUTTONDOWN; + up = Common::EVENT_MBUTTONUP; + } else if (arg3 > 500) { + down = Common::EVENT_RBUTTONDOWN; + up = Common::EVENT_RBUTTONUP; + } else { + down = Common::EVENT_LBUTTONDOWN; + up = Common::EVENT_LBUTTONUP; + } + + lockMutex(_event_queue_lock); + + if (!_touchpad_mode) + _event_queue.push(e); + + e.type = down; + _event_queue.push(e); + e.type = up; + _event_queue.push(e); + + unlockMutex(_event_queue_lock); + } + + return; + + case JE_DOUBLE_TAP: + e.type = Common::EVENT_MOUSEMOVE; + + if (_touchpad_mode) { + e.mouse = getEventManager()->getMousePos(); + } else { + scaleMouse(e.mouse, arg1, arg2); + clipMouse(e.mouse); + } + + { + Common::EventType dptype = Common::EVENT_INVALID; + + switch (arg3) { + case JACTION_DOWN: + dptype = Common::EVENT_LBUTTONDOWN; + _touch_pt_dt.x = arg1; + _touch_pt_dt.y = arg2; + break; + case JACTION_UP: + dptype = Common::EVENT_LBUTTONUP; + break; + case JACTION_MULTIPLE: + // held and moved + dptype = Common::EVENT_MOUSEMOVE; + + if (_touchpad_mode) { + scaleMouse(e.mouse, arg1 - _touch_pt_dt.x, + arg2 - _touch_pt_dt.y, false); + e.mouse += _touch_pt_down; + + clipMouse(e.mouse); + } + + break; + default: + LOGE("unhandled jaction on double tap: %d", arg3); + return; + } + + lockMutex(_event_queue_lock); + _event_queue.push(e); + e.type = dptype; + _event_queue.push(e); + unlockMutex(_event_queue_lock); + } + + return; + + case JE_BALL: + e.type = Common::EVENT_MOUSEMOVE; + + e.mouse = getEventManager()->getMousePos(); + + // already multiplied by 100 + e.mouse.x += arg1 * _trackball_scale / _eventScaleX; + e.mouse.y += arg2 * _trackball_scale / _eventScaleY; + + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + case JE_QUIT: + e.type = Common::EVENT_QUIT; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + default: + LOGE("unknown jevent type: %d", type); + + break; + } +} + +bool OSystem_Android::pollEvent(Common::Event &event) { + //ENTER(); + + if (pthread_self() == _main_thread) { + if (_screen_changeid != JNI::surface_changeid) { + if (JNI::egl_surface_width > 0 && JNI::egl_surface_height > 0) { + if (_egl_surface_width > 0 && _egl_surface_height > 0) { + // surface still alive but changed + _screen_changeid = JNI::surface_changeid; + _egl_surface_width = JNI::egl_surface_width; + _egl_surface_height = JNI::egl_surface_height; + + initViewport(); + updateScreenRect(); + updateEventScale(); + + // double buffered, flip twice + clearScreen(kClearUpdate, 2); + + event.type = Common::EVENT_SCREEN_CHANGED; + + return true; + } else { + // new surface + initSurface(); + updateScreenRect(); + updateEventScale(); + + // double buffered, flip twice + clearScreen(kClearUpdate, 2); + + event.type = Common::EVENT_SCREEN_CHANGED; + + return true; + } + } else { + // surface lost + deinitSurface(); + } + } + + if (JNI::pause) { + deinitSurface(); + + LOGD("main thread going to sleep"); + sem_wait(&JNI::pause_sem); + LOGD("main thread woke up"); + } + } + + lockMutex(_event_queue_lock); + + if (_event_queue.empty()) { + unlockMutex(_event_queue_lock); + return false; + } + + event = _event_queue.pop(); + + unlockMutex(_event_queue_lock); + + if (event.type == Common::EVENT_MOUSEMOVE) { + const Common::Point &m = getEventManager()->getMousePos(); + + if (m != event.mouse) + _force_redraw = true; + } + + return true; +} + +#endif + |