Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3DS: NEW FEATURE: Magnify Mode #1909

Closed
wants to merge 11 commits into from
Closed
29 changes: 28 additions & 1 deletion backends/platform/3ds/README.md
Expand Up @@ -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

Expand Down Expand Up @@ -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 |
Expand All @@ -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:
Expand Down
72 changes: 59 additions & 13 deletions backends/platform/3ds/osystem-events.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand All @@ -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);
}
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 15 additions & 0 deletions backends/platform/3ds/osystem-graphics.cpp
Expand Up @@ -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());
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();
}
Expand Down
5 changes: 5 additions & 0 deletions backends/platform/3ds/osystem.cpp
Expand Up @@ -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)
{
Expand Down
12 changes: 12 additions & 0 deletions backends/platform/3ds/osystem.h
Expand Up @@ -44,6 +44,11 @@ enum {
GFX_NEAREST = 1
};

enum MagnifyMode {
MODE_MAGON,
MODE_MAGOFF,
};

enum InputMode {
MODE_HOVER,
MODE_DRAG,
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand Down Expand Up @@ -218,6 +229,7 @@ class OSystem_3DS : public EventsBaseBackend, public PaletteManager {
float _cursorDeltaX, _cursorDeltaY;
int _cursorHotspotX, _cursorHotspotY;
uint32 _cursorKeyColor;
MagnifyMode _magnifyMode;
};

} // namespace _3DS
Expand Down
11 changes: 9 additions & 2 deletions backends/platform/3ds/sprite.cpp
Expand Up @@ -42,6 +42,8 @@ Sprite::Sprite()
, actualHeight(0)
, posX(0)
, posY(0)
, offsetX(0)
, offsetY(0)
, scaleX(1.f)
, scaleY(1.f)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
5 changes: 5 additions & 0 deletions backends/platform/3ds/sprite.h
Expand Up @@ -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;
Expand All @@ -68,6 +71,8 @@ class Sprite : public Graphics::Surface {
vertex* vertices;
int posX;
int posY;
uint16 offsetX;
uint16 offsetY;
float scaleX;
float scaleY;
};
Expand Down