diff --git a/backends/platform/3ds/README.md b/backends/platform/3ds/README.md index a5e24ee91dfd..2ee0f712135a 100644 --- a/backends/platform/3ds/README.md +++ b/backends/platform/3ds/README.md @@ -12,6 +12,7 @@ Table of Contents: * 2.1 Default key mappings * 2.2 Hover mode * 2.3 Drag mode + * 2.4 Magnify mode 3.0) Supported Games @@ -64,7 +65,8 @@ depending on if you're right or left-handed. | A / D-left | Left-click | | X / D-up | Right-click | | B / D-down | ESC (skips cutscenes and such) | -| L | Use virtual keyboard | +| Y / D-right| Use virtual keyboard | +| L | Toggle magnify mode on/off | | R | Toggle hover/drag modes | | Start | Open game menu | | Select | Open 3DS config menu | @@ -81,6 +83,31 @@ mouse button without using buttons mapped to right/left-click. Every time you touch and release the touchscreen, you are simulating the click and release of the mouse buttons. At the moment, this is only a left-click. +2.4) Magnify mode +----------------- +Due to the low resolutions of the 3DS's two screens (400x240 for the top, and 320x240 +for the bottom), games that run at a higher resolution will inevitably lose some visual +detail from being scaled down. This can result in situations where essential information +is undiscernable, such as text. Magnify mode increases the scale factor of the top screen +back to 1; the bottom screen remains unchanged. The touchscreen can then be used to change +which part of the game display is being magnified. This can all be done even in situations +where the cursor is disabled, such as during full-motion video (FMV) segments. + +When activating magnify mode, touchscreen controls are automatically switched to hover +mode; this is to reduce the risk of the user accidentally inputting a click when changing +the magnified area via dragging the stylus. Clicking can still be done at will as in normal +hover mode. Turning off magnify mode will revert controls back to what was being used +previously (ex: if drag mode was in use prior to activating magnify mode, drag mode will +be reactivated upon exiting magnify mode), as well as restore the top screen's previous +scale factor. + +Currently magnify mode can only be used when the following conditions are met: + - In the 3DS config menu, "Use Screen" is set to "Both" + - A game is currently being played + - The horizontal and/or vertical resolution in-game is greater than that of the top screen + +Magnify mode cannot be used in the Launcher menu. + 3.0) Supported Games -------------------- The full game engine compatibility list can be found here: diff --git a/backends/platform/3ds/osystem-events.cpp b/backends/platform/3ds/osystem-events.cpp index de0f58426c53..0e635f0e1fe3 100644 --- a/backends/platform/3ds/osystem-events.cpp +++ b/backends/platform/3ds/osystem-events.cpp @@ -33,6 +33,7 @@ namespace _3DS { static Common::Mutex *eventMutex; static InputMode inputMode = MODE_DRAG; +static InputMode savedInputMode = MODE_DRAG; static aptHookCookie cookie; static bool optionMenuOpening = false; static Common::String messageOSD; @@ -146,13 +147,41 @@ static void eventThreadFunc(void *arg) { } // Button events + if (keysPressed & KEY_L) { + if (osys->getWidth() >= 400 || osys->getHeight() >= 240) { + if (osys->getMagnifyMode() == MODE_MAGOFF) { + osys->setMagnifyMode(MODE_MAGON); + if (inputMode == MODE_DRAG) { + inputMode = MODE_HOVER; + osys->displayMessageOnOSD("Magnify Mode On. Switching to Hover Mode..."); + } else + osys->displayMessageOnOSD("Magnify Mode On"); + } else { + osys->setMagnifyMode(MODE_MAGOFF); + osys->updateSize(); + if (savedInputMode == MODE_DRAG) { + inputMode = savedInputMode; + osys->displayMessageOnOSD("Magnify Mode Off. Reactivating Drag Mode..."); + } else + osys->displayMessageOnOSD("Magnify Mode Off"); + } + } else { + if (osys->getWidth() == 0 || osys->getHeight() == 0) { + osys->displayMessageOnOSD("Magnify Mode cannot be activated in Launcher."); + } else + osys->displayMessageOnOSD("In-game resolution too small to magnify."); + } + } if (keysPressed & KEY_R) { if (inputMode == MODE_DRAG) { - inputMode = MODE_HOVER; + inputMode = savedInputMode = MODE_HOVER; osys->displayMessageOnOSD("Hover Mode"); } else { - inputMode = MODE_DRAG; - osys->displayMessageOnOSD("Drag Mode"); + if (osys->getMagnifyMode() == MODE_MAGOFF) { + inputMode = savedInputMode = MODE_DRAG; + osys->displayMessageOnOSD("Drag Mode"); + } else + osys->displayMessageOnOSD("Cannot Switch to Drag Mode while Magnify Mode is On"); } } if (keysPressed & KEY_A || keysPressed & KEY_DLEFT || keysReleased & KEY_A || keysReleased & KEY_DLEFT) { @@ -165,6 +194,16 @@ static void eventThreadFunc(void *arg) { event.type = Common::EVENT_LBUTTONUP; 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); + } if (keysPressed & KEY_X || keysPressed & KEY_DUP || keysReleased & KEY_X || keysReleased & KEY_DUP) { // SIMULATE RIGHT CLICK event.mouse.x = lastTouch.px; @@ -175,7 +214,7 @@ static void eventThreadFunc(void *arg) { event.type = Common::EVENT_RBUTTONUP; pushEventQueue(eventQueue, event); } - if (keysPressed & KEY_L) { + if (keysPressed & KEY_Y || keysPressed & KEY_DRIGHT) { event.type = Common::EVENT_VIRTUAL_KEYBOARD; pushEventQueue(eventQueue, event); } @@ -187,15 +226,18 @@ static void eventThreadFunc(void *arg) { if (!optionMenuOpened) optionMenuOpening = true; } - 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); + + // If magnify mode is on when returning to Launcher, turn it off + if (g_system->getEventManager()->shouldRTL()) { + if (osys->getMagnifyMode() == MODE_MAGON) { + osys->setMagnifyMode(MODE_MAGOFF); + osys->updateSize(); + if (savedInputMode == MODE_DRAG) { + inputMode = savedInputMode; + osys->displayMessageOnOSD("Magnify Mode Off. Reactivating Drag Mode.\nReturning to Launcher..."); + } else + osys->displayMessageOnOSD("Magnify Mode Off. Returning to Launcher..."); + } } // TODO: EVENT_PREDICTIVE_DIALOG @@ -268,6 +310,10 @@ void OSystem_3DS::transformPoint(touchPosition &point) { } } +void OSystem_3DS::setMagnifyMode(MagnifyMode mode) { + _magnifyMode = mode; +} + void OSystem_3DS::displayMessageOnOSD(const char *msg) { messageOSD = msg; showMessageOSD = true; diff --git a/backends/platform/3ds/osystem-graphics.cpp b/backends/platform/3ds/osystem-graphics.cpp index 1a86be440211..7204a9fb05ce 100644 --- a/backends/platform/3ds/osystem-graphics.cpp +++ b/backends/platform/3ds/osystem-graphics.cpp @@ -151,6 +151,8 @@ void OSystem_3DS::initSize(uint width, uint height, _gameHeight = height; _gameTopTexture.create(width, height, _pfGameTexture); _overlay.create(getOverlayWidth(), getOverlayHeight(), _pfGameTexture); + _topHalfWidth = _topWidth / 2; + _topHalfHeight = _topHeight / 2; if (format) { debug("pixelformat: %d %d %d %d %d", format->bytesPerPixel, format->rBits(), format->gBits(), format->bBits(), format->aBits()); @@ -198,6 +200,8 @@ void OSystem_3DS::updateSize() { } _gameTopTexture.setPosition(_gameTopX, _gameTopY); _gameBottomTexture.setPosition(_gameBottomX, _gameBottomY); + _gameTopTexture.setOffset(0, 0); + _gameBottomTexture.setOffset(0, 0); if (_overlayVisible) _cursorTexture.setScale(1.f, 1.f); else if (config.screen == kScreenTop) @@ -275,6 +279,17 @@ void OSystem_3DS::updateScreen() { C3D_FrameBegin(0); _gameTopTexture.transfer(); + if (_magnifyMode == MODE_MAGON) { + _topX = (_cursorX < _topHalfWidth) ? + 0 : ((_cursorX < (_gameWidth - _topHalfWidth)) ? + _cursorX - _topHalfWidth : _gameWidth - _topWidth); + _topY = (_cursorY < _topHalfHeight) ? + 0 : ((_cursorY < _gameHeight - _topHalfHeight) ? + _cursorY - _topHalfHeight : _gameHeight - _topHeight); + _gameTopTexture.setScale(1.f,1.f); + _gameTopTexture.setPosition(0,0); + _gameTopTexture.setOffset(_topX, _topY); + } if (_overlayVisible) { _overlay.transfer(); } diff --git a/backends/platform/3ds/osystem.cpp b/backends/platform/3ds/osystem.cpp index e1186e4bfb07..aa0b3a75037c 100644 --- a/backends/platform/3ds/osystem.cpp +++ b/backends/platform/3ds/osystem.cpp @@ -72,7 +72,12 @@ OSystem_3DS::OSystem_3DS(): _gameBottomY(0), _gameWidth(320), _gameHeight(240), + _topX(0), + _topY(0), + _topWidth(400), + _topHeight(240), _overlayVisible(false), + _magnifyMode(MODE_MAGOFF), exiting(false), sleeping(false) { diff --git a/backends/platform/3ds/osystem.h b/backends/platform/3ds/osystem.h index 89271e127b6a..544c9001024e 100644 --- a/backends/platform/3ds/osystem.h +++ b/backends/platform/3ds/osystem.h @@ -44,6 +44,11 @@ enum { GFX_NEAREST = 1 }; +enum MagnifyMode { + MODE_MAGON, + MODE_MAGOFF, +}; + enum InputMode { MODE_HOVER, MODE_DRAG, @@ -143,6 +148,9 @@ class OSystem_3DS : public EventsBaseBackend, public PaletteManager { void updateFocus(); void updateConfig(); void updateSize(); + void setMagnifyMode(MagnifyMode mode); + MagnifyMode getMagnifyMode(){ return _magnifyMode; } + private: void initGraphics(); @@ -162,6 +170,9 @@ class OSystem_3DS : public EventsBaseBackend, public PaletteManager { u16 _gameWidth, _gameHeight; u16 _gameTopX, _gameTopY; u16 _gameBottomX, _gameBottomY; + u16 _topWidth, _topHeight; + u16 _topHalfWidth, _topHalfHeight; + u16 _topX, _topY; // Audio Thread audioThread; @@ -218,6 +229,7 @@ class OSystem_3DS : public EventsBaseBackend, public PaletteManager { float _cursorDeltaX, _cursorDeltaY; int _cursorHotspotX, _cursorHotspotY; uint32 _cursorKeyColor; + MagnifyMode _magnifyMode; }; } // namespace _3DS diff --git a/backends/platform/3ds/sprite.cpp b/backends/platform/3ds/sprite.cpp index 1f2c72e2d32d..bbccf94fe602 100644 --- a/backends/platform/3ds/sprite.cpp +++ b/backends/platform/3ds/sprite.cpp @@ -42,6 +42,8 @@ Sprite::Sprite() , actualHeight(0) , posX(0) , posY(0) + , offsetX(0) + , offsetY(0) , scaleX(1.f) , scaleY(1.f) { @@ -85,7 +87,6 @@ void Sprite::create(uint16 width, uint16 height, const Graphics::PixelFormat &f) memcpy(vertices, tmp, sizeof(vertex) * 4); } - void Sprite::free() { linearFree(vertices); linearFree(pixels); @@ -138,12 +139,18 @@ void Sprite::setPosition(int x, int y) { } } +void Sprite::setOffset(uint16 x, uint16 y) { + offsetX = x; + offsetY = y; + dirtyMatrix = true; +} + C3D_Mtx* Sprite::getMatrix() { if (dirtyMatrix) { dirtyMatrix = false; Mtx_Identity(&modelview); Mtx_Scale(&modelview, scaleX, scaleY, 1.f); - Mtx_Translate(&modelview, posX, posY, 0, true); + Mtx_Translate(&modelview, posX - offsetX, posY - offsetY, 0, true); } return &modelview; } diff --git a/backends/platform/3ds/sprite.h b/backends/platform/3ds/sprite.h index 1e9c0b4c0122..a7d8b778426d 100644 --- a/backends/platform/3ds/sprite.h +++ b/backends/platform/3ds/sprite.h @@ -52,9 +52,12 @@ class Sprite : public Graphics::Surface { void markDirty(){ dirtyPixels = true; } void setPosition(int x, int y); + void setOffset(uint16 x, uint16 y); void setScale(float x, float y); float getScaleX(){ return scaleX; } float getScaleY(){ return scaleY; } + int getPosX(){ return posX; } + int getPosY(){ return posY; } C3D_Mtx* getMatrix(); uint16 actualWidth; @@ -68,6 +71,8 @@ class Sprite : public Graphics::Surface { vertex* vertices; int posX; int posY; + uint16 offsetX; + uint16 offsetY; float scaleX; float scaleY; };