/* 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. * */ #define FORBIDDEN_SYMBOL_ALLOW_ALL #include "backends/timer/default/default-timer.h" #include "backends/platform/3ds/gui.h" #include "osystem.h" static Common::Mutex *eventMutex; static InputMode inputMode = MODE_DRAG; static aptHookCookie cookie; static void pushEventQueue(Common::Queue* queue, Common::Event& event) { Common::StackLock lock(*eventMutex); queue->push(event); } static void eventThreadFunc(void* arg) { OSystem_3DS* osys = (OSystem_3DS*) g_system; auto eventQueue = (Common::Queue*) arg; uint32 touchStartTime = osys->getMillis(); touchPosition lastTouch = {0,0}; bool isRightClick = false; Common::Event event; while(!osys->exiting) { do { osys->delayMillis(10); } while (osys->sleeping && !osys->exiting); hidScanInput(); touchPosition touch; u32 held = hidKeysHeld(); u32 keysPressed = hidKeysDown(); u32 keysReleased = hidKeysUp(); if (held & KEY_TOUCH) { hidTouchRead(&touch); osys->transformPoint(touch); osys->warpMouse(touch.px, touch.py); event.mouse.x = touch.px; event.mouse.y = touch.py; if (keysPressed & KEY_TOUCH) { touchStartTime = osys->getMillis(); isRightClick = (held & KEY_X || held & KEY_DUP); if (inputMode == MODE_DRAG) { event.type = isRightClick ? Common::EVENT_RBUTTONDOWN : Common::EVENT_LBUTTONDOWN; pushEventQueue(eventQueue, event); } } else if (touch.px != lastTouch.px || touch.py != lastTouch.py) { event.type = Common::EVENT_MOUSEMOVE; pushEventQueue(eventQueue, event); } lastTouch = touch; } else if (keysReleased & KEY_TOUCH) { event.mouse.x = lastTouch.px; event.mouse.y = lastTouch.py; printf("clicked %u, %u\n", lastTouch.px, lastTouch.py); if (inputMode == MODE_DRAG) { event.type = isRightClick ? Common::EVENT_RBUTTONUP : Common::EVENT_LBUTTONUP; pushEventQueue(eventQueue, event); } else if (osys->getMillis() - touchStartTime < 200) { // Process click in MODE_HOVER event.type = Common::EVENT_MOUSEMOVE; pushEventQueue(eventQueue, event); event.type = isRightClick ? Common::EVENT_RBUTTONDOWN : Common::EVENT_LBUTTONDOWN; pushEventQueue(eventQueue, event); event.type = isRightClick ? Common::EVENT_RBUTTONUP : Common::EVENT_LBUTTONUP; pushEventQueue(eventQueue, event); } } if (keysPressed & KEY_R) { if (inputMode == MODE_DRAG) { inputMode = MODE_HOVER; osys->displayMessageOnOSD("Hover Mode"); } else { inputMode = MODE_DRAG; osys->displayMessageOnOSD("Drag Mode"); } } if (keysPressed & KEY_A || keysPressed & KEY_DLEFT) { // SIMULATE LEFT CLICK event.mouse.x = lastTouch.px; event.mouse.y = lastTouch.py; event.type = Common::EVENT_LBUTTONDOWN; pushEventQueue(eventQueue, event); event.type = Common::EVENT_LBUTTONUP; pushEventQueue(eventQueue, event); } if (keysPressed & KEY_X || keysPressed & KEY_DUP) { // SIMULATE RIGHT CLICK event.mouse.x = lastTouch.px; event.mouse.y = lastTouch.py; event.type = Common::EVENT_RBUTTONDOWN; pushEventQueue(eventQueue, event); event.type = Common::EVENT_RBUTTONUP; pushEventQueue(eventQueue, event); } if (keysPressed & KEY_L) { event.type = Common::EVENT_VIRTUAL_KEYBOARD; pushEventQueue(eventQueue, event); } if (keysPressed & KEY_START) { event.type = Common::EVENT_MAINMENU; pushEventQueue(eventQueue, event); } if (keysPressed & KEY_SELECT) { event.type = Common::EVENT_RTL; pushEventQueue(eventQueue, event); } if (keysPressed & KEY_B || keysReleased & KEY_B || keysPressed & KEY_DDOWN || keysReleased & KEY_DDOWN) { if (keysPressed & KEY_B || keysPressed & KEY_DDOWN) event.type = Common::EVENT_KEYDOWN; else event.type = Common::EVENT_KEYUP; event.kbd.keycode = Common::KEYCODE_ESCAPE; event.kbd.ascii = Common::ASCII_ESCAPE; event.kbd.flags = 0; pushEventQueue(eventQueue, event); } // TODO: EVENT_PREDICTIVE_DIALOG // EVENT_SCREEN_CHANGED } } static void aptHookFunc(APT_HookType hookType, void* param) { auto eventQueue = (Common::Queue*) param; OSystem_3DS* osys = (OSystem_3DS*) g_system; Common::Event event; switch (hookType) { case APTHOOK_ONSUSPEND: case APTHOOK_ONSLEEP: event.type = Common::EVENT_MAINMENU; pushEventQueue(eventQueue, event); osys->sleeping = true; break; case APTHOOK_ONRESTORE: case APTHOOK_ONWAKEUP: osys->sleeping = false; break; default: event.type = Common::EVENT_QUIT; pushEventQueue(eventQueue, event); break; } } static void timerThreadFunc(void *arg) { OSystem_3DS* osys = (OSystem_3DS*) arg; DefaultTimerManager *tm = (DefaultTimerManager *) osys->getTimerManager(); while (!osys->exiting) { tm->handler(); g_system->delayMillis(10); aptMainLoop(); // Call apt hook when necessary } } void OSystem_3DS::initEvents() { eventMutex = new Common::Mutex(); s32 prio = 0; svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); _timerThread = threadCreate(&timerThreadFunc, this, 32*1024, prio-1, -2, false); _eventThread = threadCreate(&eventThreadFunc, &_eventQueue, 32*1024, prio-1, -2, false); aptHook(&cookie, aptHookFunc, &_eventQueue); } void OSystem_3DS::destroyEvents() { threadJoin(_timerThread, U64_MAX); threadFree(_timerThread); threadJoin(_eventThread, U64_MAX); threadFree(_eventThread); delete eventMutex; } void OSystem_3DS::transformPoint(touchPosition &point) { if (!_overlayVisible) { point.px = static_cast(point.px) / _gameTexture.getScaleX() - _gameX; point.py = static_cast(point.py) / _gameTexture.getScaleY() - _gameY; } } void OSystem_3DS::displayMessageOnOSD(const char *msg) { _messageOSD = msg; _showMessageOSD = true; } bool OSystem_3DS::pollEvent(Common::Event &event) { if (_showMessageOSD) { _showMessageOSD = false; StatusMessageDialog dialog(_messageOSD, 800); dialog.runModal(); } Common::StackLock lock(*eventMutex); if (_eventQueue.empty()) return false; event = _eventQueue.pop(); return true; }