diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h deleted file mode 100644 index 2b8f128a..00000000 --- a/src/frontend/FrontendUtil.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - Copyright 2016-2023 melonDS team - - This file is part of melonDS. - - melonDS 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 3 of the License, or (at your option) - any later version. - - melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/. -*/ - -#ifndef FRONTENDUTIL_H -#define FRONTENDUTIL_H - -#include "types.h" - -#include -#include - -namespace melonDS -{ -class NDS; -} -namespace Frontend -{ -using namespace melonDS; - -enum ScreenLayout -{ - screenLayout_Natural, // top screen above bottom screen always - screenLayout_Horizontal, - screenLayout_Vertical, - screenLayout_Hybrid, - screenLayout_MAX, -}; - -enum ScreenRotation -{ - screenRot_0Deg, - screenRot_90Deg, - screenRot_180Deg, - screenRot_270Deg, - screenRot_MAX, -}; - -enum ScreenSizing -{ - screenSizing_Even, // both screens get same size - screenSizing_EmphTop, // make top screen as big as possible, fit bottom screen in remaining space - screenSizing_EmphBot, - screenSizing_Auto, // not applied in SetupScreenLayout - screenSizing_TopOnly, - screenSizing_BotOnly, - screenSizing_MAX, -}; - -// setup the display layout based on the provided display size and parameters -// * screenWidth/screenHeight: size of the host display -// * screenLayout: how the DS screens are laid out -// * rotation: angle at which the DS screens are presented -// * sizing: how the display size is shared between the two screens -// * screenGap: size of the gap between the two screens in pixels -// * integerScale: force screens to be scaled up at integer scaling factors -// * screenSwap: whether to swap the position of both screens -// * topAspect/botAspect: ratio by which to scale the top and bottom screen respectively -void SetupScreenLayout(int screenWidth, int screenHeight, - ScreenLayout screenLayout, - ScreenRotation rotation, - ScreenSizing sizing, - int screenGap, - bool integerScale, - bool swapScreens, - float topAspect, float botAspect); - -const int MaxScreenTransforms = 3; - -// get a 2x3 transform matrix for each screen and whether it's a top or bottom screen -// note: the transform assumes an origin point at the top left of the display, -// X going right and Y going down -// for each screen the source coordinates should be (0,0) and (256,192) -// 'out' should point to an array of 6*MaxScreenTransforms floats -// 'kind' should point to an array of MaxScreenTransforms ints -// (0 = indicates top screen, 1 = bottom screen) -// returns the amount of screens -int GetScreenTransforms(float* out, int* kind); - -// de-transform the provided host display coordinates to get coordinates -// on the bottom screen -bool GetTouchCoords(int& x, int& y, bool clamp); - -} - -#endif // FRONTENDUTIL_H diff --git a/src/frontend/Util_Video.cpp b/src/frontend/ScreenLayout.cpp similarity index 93% rename from src/frontend/Util_Video.cpp rename to src/frontend/ScreenLayout.cpp index e772be9a..d144f982 100644 --- a/src/frontend/Util_Video.cpp +++ b/src/frontend/ScreenLayout.cpp @@ -1,553 +1,548 @@ -/* - Copyright 2016-2023 melonDS team - - This file is part of melonDS. - - melonDS 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 3 of the License, or (at your option) - any later version. - - melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/. -*/ - -#include -#include -#include -#include -#include - -#include "FrontendUtil.h" - - -namespace Frontend -{ - -float TopScreenMtx[6]; -float BotScreenMtx[6]; -float HybScreenMtx[6]; -float TouchMtx[6]; -float HybTouchMtx[6]; -bool TopEnable; -bool BotEnable; -bool HybEnable; -int HybScreen; -int HybPrevTouchScreen; // 0:unknown, 1:buttom screen, 2:hybrid screen - -void M23_Identity(float* m) -{ - m[0] = 1; m[1] = 0; - m[2] = 0; m[3] = 1; - m[4] = 0; m[5] = 0; -} - -void M23_Scale(float* m, float s) -{ - m[0] *= s; m[1] *= s; - m[2] *= s; m[3] *= s; - m[4] *= s; m[5] *= s; -} - -void M23_Scale(float* m, float x, float y) -{ - m[0] *= x; m[1] *= y; - m[2] *= x; m[3] *= y; - m[4] *= x; m[5] *= y; -} - -void M23_RotateFast(float* m, int angle) -{ - if (angle == 0) return; - - float temp[4]; memcpy(temp, m, sizeof(float)*4); - - switch (angle) - { - case 1: // 90 - m[0] = temp[2]; - m[1] = temp[3]; - m[2] = -temp[0]; - m[3] = -temp[1]; - break; - - case 2: // 180 - m[0] = -temp[0]; - m[1] = -temp[1]; - m[2] = -temp[2]; - m[3] = -temp[3]; - break; - - case 3: // 270 - m[0] = -temp[2]; - m[1] = -temp[3]; - m[2] = temp[0]; - m[3] = temp[1]; - break; - } -} - -void M23_Translate(float* m, float tx, float ty) -{ - m[4] += tx; - m[5] += ty; -} - -void M23_Multiply(float* m, float* _a, float* _b) -{ - float a[6]; memcpy(a, _a, 6*sizeof(float)); - float b[6]; memcpy(b, _b, 6*sizeof(float)); - - m[0] = (a[0] * b[0]) + (a[2] * b[1]); - m[1] = (a[1] * b[0]) + (a[3] * b[1]); - - m[2] = (a[0] * b[2]) + (a[2] * b[3]); - m[3] = (a[1] * b[2]) + (a[3] * b[3]); - - m[4] = (a[0] * b[4]) + (a[2] * b[5]) + a[4]; - m[5] = (a[1] * b[4]) + (a[3] * b[5]) + a[5]; -} - -void M23_Transform(float* m, float& x, float& y) -{ - float vx = x; - float vy = y; - - x = (vx * m[0]) + (vy * m[2]) + m[4]; - y = (vx * m[1]) + (vy * m[3]) + m[5]; -} - - -void SetupScreenLayout(int screenWidth, int screenHeight, - ScreenLayout screenLayout, - ScreenRotation rotation, - ScreenSizing sizing, - int screenGap, - bool integerScale, - bool swapScreens, - float topAspect, float botAspect) -{ - HybEnable = screenLayout == 3; - if (HybEnable) - { - screenLayout = screenLayout_Natural; - sizing = screenSizing_Even; - HybScreen = swapScreens ? 1 : 0; - swapScreens = false; - topAspect = botAspect = 1; - HybPrevTouchScreen = 0; - } - - float refpoints[6][2] = - { - {0, 0}, {256, 192}, - {0, 0}, {256, 192}, - {0, 0}, {256, 192} - }; - - int layout = screenLayout == screenLayout_Natural - ? rotation % 2 - : screenLayout - 1; - - float botScale = 1; - float hybScale = 1; - float botTrans[4] = {0}; - float hybTrans[2] = {0}; - - M23_Identity(TopScreenMtx); - M23_Identity(BotScreenMtx); - M23_Identity(HybScreenMtx); - - M23_Translate(TopScreenMtx, -256/2, -192/2); - M23_Translate(BotScreenMtx, -256/2, -192/2); - - M23_Scale(TopScreenMtx, topAspect, 1); - M23_Scale(BotScreenMtx, botAspect, 1); - - // rotation - { - float rotmtx[6]; - M23_Identity(rotmtx); - - M23_RotateFast(rotmtx, rotation); - M23_Multiply(TopScreenMtx, rotmtx, TopScreenMtx); - M23_Multiply(BotScreenMtx, rotmtx, BotScreenMtx); - M23_Multiply(HybScreenMtx, rotmtx, HybScreenMtx); - - M23_Transform(TopScreenMtx, refpoints[0][0], refpoints[0][1]); - M23_Transform(TopScreenMtx, refpoints[1][0], refpoints[1][1]); - M23_Transform(BotScreenMtx, refpoints[2][0], refpoints[2][1]); - M23_Transform(BotScreenMtx, refpoints[3][0], refpoints[3][1]); - } - - int posRefPointOffset = 0; - int posRefPointCount = HybEnable ? 6 : 4; - - if (sizing == screenSizing_TopOnly || sizing == screenSizing_BotOnly) - { - float* mtx = sizing == screenSizing_TopOnly ? TopScreenMtx : BotScreenMtx; - int primOffset = sizing == screenSizing_TopOnly ? 0 : 2; - int secOffset = sizing == screenSizing_BotOnly ? 2 : 0; - - float hSize = fabsf(refpoints[primOffset][0] - refpoints[primOffset+1][0]); - float vSize = fabsf(refpoints[primOffset][1] - refpoints[primOffset+1][1]); - - float scale = std::min(screenWidth / hSize, screenHeight / vSize); - if (integerScale) - scale = floorf(scale); - - TopEnable = sizing == screenSizing_TopOnly; - BotEnable = sizing == screenSizing_BotOnly; - botScale = scale; - - M23_Scale(mtx, scale); - refpoints[primOffset][0] *= scale; - refpoints[primOffset][1] *= scale; - refpoints[primOffset+1][0] *= scale; - refpoints[primOffset+1][1] *= scale; - - posRefPointOffset = primOffset; - posRefPointCount = 2; - } - else - { - TopEnable = BotEnable = true; - - // move screens apart - { - int idx = layout == 0 ? 1 : 0; - - bool moveV = rotation % 2 == layout; - - float offsetBot = (moveV ? 192.0 : 256.0 * botAspect) / 2.0 + screenGap / 2.0; - float offsetTop = -((moveV ? 192.0 : 256.0 * topAspect) / 2.0 + screenGap / 2.0); - - if ((rotation == 1 || rotation == 2) ^ swapScreens) - { - offsetTop *= -1; - offsetBot *= -1; - } - - M23_Translate(TopScreenMtx, (idx==0)?offsetTop:0, (idx==1)?offsetTop:0); - M23_Translate(BotScreenMtx, (idx==0)?offsetBot:0, (idx==1)?offsetBot:0); - - refpoints[0][idx] += offsetTop; - refpoints[1][idx] += offsetTop; - refpoints[2][idx] += offsetBot; - refpoints[3][idx] += offsetBot; - - botTrans[idx] = offsetBot; - } - - // scale - { - if (sizing == screenSizing_Even) - { - float minX = refpoints[0][0], maxX = minX; - float minY = refpoints[0][1], maxY = minY; - - for (int i = 1; i < 4; i++) - { - minX = std::min(minX, refpoints[i][0]); - minY = std::min(minY, refpoints[i][1]); - maxX = std::max(maxX, refpoints[i][0]); - maxY = std::max(maxY, refpoints[i][1]); - } - - float hSize = maxX - minX; - float vSize = maxY - minY; - - if (HybEnable) - { - hybScale = layout == 0 - ? (4 * vSize) / (3 * hSize) - : (4 * hSize) / (3 * vSize); - if (layout == 0) - hSize += (vSize * 4) / 3; - else - vSize += (hSize * 4) / 3; - } - - // scale evenly - float scale = std::min(screenWidth / hSize, screenHeight / vSize); - - if (integerScale) - scale = floor(scale); - - hybScale *= scale; - - M23_Scale(TopScreenMtx, scale); - M23_Scale(BotScreenMtx, scale); - M23_Scale(HybScreenMtx, hybScale); - - for (int i = 0; i < 4; i++) - { - refpoints[i][0] *= scale; - refpoints[i][1] *= scale; - } - - botScale = scale; - - // move screens aside - if (HybEnable) - { - float hybWidth = layout == 0 - ? (scale * vSize * 4) / 3 - : (scale * hSize * 4) / 3; - - if (rotation > screenRot_90Deg) - hybWidth *= -1; - - M23_Translate(TopScreenMtx, (layout==0)?hybWidth:0, (layout==1)?hybWidth:0); - M23_Translate(BotScreenMtx, (layout==0)?hybWidth:0, (layout==1)?hybWidth:0); - refpoints[0][layout] += hybWidth; - refpoints[1][layout] += hybWidth; - refpoints[2][layout] += hybWidth; - refpoints[3][layout] += hybWidth; - - botTrans[2+layout] += hybWidth; - - hybTrans[0] = scale * (rotation == screenRot_0Deg || rotation == screenRot_270Deg ? minX : maxX); - hybTrans[1] = scale * (rotation == screenRot_0Deg || rotation == screenRot_90Deg ? minY : maxY); - M23_Translate(HybScreenMtx, hybTrans[0], hybTrans[1]); - - M23_Transform(HybScreenMtx, refpoints[4][0], refpoints[4][1]); - M23_Transform(HybScreenMtx, refpoints[5][0], refpoints[5][1]); - } - } - else - { - int primOffset = (sizing == screenSizing_EmphTop) ? 0 : 2; - int secOffset = (sizing == screenSizing_EmphTop) ? 2 : 0; - float* primMtx = (sizing == screenSizing_EmphTop) ? TopScreenMtx : BotScreenMtx; - float* secMtx = (sizing == screenSizing_EmphTop) ? BotScreenMtx : TopScreenMtx; - - float primMinX = refpoints[primOffset][0], primMaxX = primMinX; - float primMinY = refpoints[primOffset][1], primMaxY = primMinY; - float secMinX = refpoints[secOffset][0], secMaxX = secMinX; - float secMinY = refpoints[secOffset][1], secMaxY = secMinY; - - primMinX = std::min(primMinX, refpoints[primOffset+1][0]); - primMinY = std::min(primMinY, refpoints[primOffset+1][1]); - primMaxX = std::max(primMaxX, refpoints[primOffset+1][0]); - primMaxY = std::max(primMaxY, refpoints[primOffset+1][1]); - - secMinX = std::min(secMinX, refpoints[secOffset+1][0]); - secMinY = std::min(secMinY, refpoints[secOffset+1][1]); - secMaxX = std::max(secMaxX, refpoints[secOffset+1][0]); - secMaxY = std::max(secMaxY, refpoints[secOffset+1][1]); - - float primHSize = layout == 1 ? std::max(primMaxX, -primMinX) : primMaxX - primMinX; - float primVSize = layout == 0 ? std::max(primMaxY, -primMinY) : primMaxY - primMinY; - - float secHSize = layout == 1 ? std::max(secMaxX, -secMinX) : secMaxX - secMinX; - float secVSize = layout == 0 ? std::max(secMaxY, -secMinY) : secMaxY - secMinY; - - float primScale = std::min(screenWidth / primHSize, screenHeight / primVSize); - float secScale = 1.f; - - if (integerScale) - primScale = floorf(primScale); - - if (layout == 0) - { - if (screenHeight - primVSize * primScale < secVSize) - primScale = std::min(screenWidth / primHSize, (screenHeight - secVSize) / primVSize); - else - secScale = std::min((screenHeight - primVSize * primScale) / secVSize, screenWidth / secHSize); - } - else - { - if (screenWidth - primHSize * primScale < secHSize) - primScale = std::min((screenWidth - secHSize) / primHSize, screenHeight / primVSize); - else - secScale = std::min((screenWidth - primHSize * primScale) / secHSize, screenHeight / secVSize); - } - - if (integerScale) - { - primScale = floorf(primScale); - secScale = floorf(secScale); - } - - M23_Scale(primMtx, primScale); - M23_Scale(secMtx, secScale); - - refpoints[primOffset+0][0] *= primScale; - refpoints[primOffset+0][1] *= primScale; - refpoints[primOffset+1][0] *= primScale; - refpoints[primOffset+1][1] *= primScale; - refpoints[secOffset+0][0] *= secScale; - refpoints[secOffset+0][1] *= secScale; - refpoints[secOffset+1][0] *= secScale; - refpoints[secOffset+1][1] *= secScale; - - botScale = (sizing == screenSizing_EmphTop) ? secScale : primScale; - } - } - } - - // position - { - float minX = refpoints[posRefPointOffset][0], maxX = minX; - float minY = refpoints[posRefPointOffset][1], maxY = minY; - - for (int i = posRefPointOffset + 1; i < posRefPointOffset + posRefPointCount; i++) - { - minX = std::min(minX, refpoints[i][0]); - minY = std::min(minY, refpoints[i][1]); - maxX = std::max(maxX, refpoints[i][0]); - maxY = std::max(maxY, refpoints[i][1]); - } - - float width = maxX - minX; - float height = maxY - minY; - - float tx = (screenWidth/2) - (width/2) - minX; - float ty = (screenHeight/2) - (height/2) - minY; - - M23_Translate(TopScreenMtx, tx, ty); - M23_Translate(BotScreenMtx, tx, ty); - M23_Translate(HybScreenMtx, tx, ty); - - botTrans[2] += tx; botTrans[3] += ty; - hybTrans[0] += tx; hybTrans[1] += ty; - } - - // prepare a 'reverse' matrix for the touchscreen - // this matrix undoes the transforms applied to the bottom screen - // and can be used to calculate touchscreen coords from host screen coords - if (BotEnable) - { - M23_Identity(TouchMtx); - - M23_Translate(TouchMtx, -botTrans[2], -botTrans[3]); - M23_Scale(TouchMtx, 1.f / botScale); - M23_Translate(TouchMtx, -botTrans[0], -botTrans[1]); - - float rotmtx[6]; - M23_Identity(rotmtx); - M23_RotateFast(rotmtx, (4-rotation) & 3); - M23_Multiply(TouchMtx, rotmtx, TouchMtx); - - M23_Scale(TouchMtx, 1.f/botAspect, 1); - M23_Translate(TouchMtx, 256/2, 192/2); - - if (HybEnable && HybScreen == 1) - { - M23_Identity(HybTouchMtx); - - M23_Translate(HybTouchMtx, -hybTrans[0], -hybTrans[1]); - M23_Scale(HybTouchMtx, 1.f/hybScale); - M23_Multiply(HybTouchMtx, rotmtx, HybTouchMtx); - } - } -} - -int GetScreenTransforms(float* out, int* kind) -{ - int num = 0; - if (TopEnable) - { - memcpy(out + 6*num, TopScreenMtx, sizeof(TopScreenMtx)); - kind[num++] = 0; - } - if (BotEnable) - { - memcpy(out + 6*num, BotScreenMtx, sizeof(BotScreenMtx)); - kind[num++] = 1; - } - if (HybEnable) - { - memcpy(out + 6*num, HybScreenMtx, sizeof(HybScreenMtx)); - kind[num++] = HybScreen; - } - return num; -} - -bool GetTouchCoords(int& x, int& y, bool clamp) -{ - if (HybEnable && HybScreen == 1) - { - float vx = x; - float vy = y; - float hvx = x; - float hvy = y; - - M23_Transform(TouchMtx, vx, vy); - M23_Transform(HybTouchMtx, hvx, hvy); - - if (clamp) - { - if (HybPrevTouchScreen == 1) - { - x = std::clamp((int)vx, 0, 255); - y = std::clamp((int)vy, 0, 191); - - return true; - } - if (HybPrevTouchScreen == 2) - { - x = std::clamp((int)hvx, 0, 255); - y = std::clamp((int)hvy, 0, 191); - - return true; - } - } - else - { - if (vx >= 0 && vx < 256 && vy >= 0 && vy < 192) - { - HybPrevTouchScreen = 1; - - x = (int)vx; - y = (int)vy; - - return true; - } - if (hvx >= 0 && hvx < 256 && hvy >= 0 && hvy < 192) - { - HybPrevTouchScreen = 2; - - x = (int)hvx; - y = (int)hvy; - - return true; - } - } - } - else if (BotEnable) - { - float vx = x; - float vy = y; - - M23_Transform(TouchMtx, vx, vy); - - if (clamp) - { - x = std::clamp((int)vx, 0, 255); - y = std::clamp((int)vy, 0, 191); - - return true; - } - else - { - if (vx >= 0 && vx < 256 && vy >= 0 && vy < 192) - { - x = (int)vx; - y = (int)vy; - - return true; - } - } - } - - return false; -} - -} - +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS 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 3 of the License, or (at your option) + any later version. + + melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include + +#include "ScreenLayout.h" + + +void M23_Identity(float* m) +{ + m[0] = 1; m[1] = 0; + m[2] = 0; m[3] = 1; + m[4] = 0; m[5] = 0; +} + +void M23_Scale(float* m, float s) +{ + m[0] *= s; m[1] *= s; + m[2] *= s; m[3] *= s; + m[4] *= s; m[5] *= s; +} + +void M23_Scale(float* m, float x, float y) +{ + m[0] *= x; m[1] *= y; + m[2] *= x; m[3] *= y; + m[4] *= x; m[5] *= y; +} + +void M23_RotateFast(float* m, int angle) +{ + if (angle == 0) return; + + float temp[4]; memcpy(temp, m, sizeof(float)*4); + + switch (angle) + { + case 1: // 90 + m[0] = temp[2]; + m[1] = temp[3]; + m[2] = -temp[0]; + m[3] = -temp[1]; + break; + + case 2: // 180 + m[0] = -temp[0]; + m[1] = -temp[1]; + m[2] = -temp[2]; + m[3] = -temp[3]; + break; + + case 3: // 270 + m[0] = -temp[2]; + m[1] = -temp[3]; + m[2] = temp[0]; + m[3] = temp[1]; + break; + } +} + +void M23_Translate(float* m, float tx, float ty) +{ + m[4] += tx; + m[5] += ty; +} + +void M23_Multiply(float* m, float* _a, float* _b) +{ + float a[6]; memcpy(a, _a, 6*sizeof(float)); + float b[6]; memcpy(b, _b, 6*sizeof(float)); + + m[0] = (a[0] * b[0]) + (a[2] * b[1]); + m[1] = (a[1] * b[0]) + (a[3] * b[1]); + + m[2] = (a[0] * b[2]) + (a[2] * b[3]); + m[3] = (a[1] * b[2]) + (a[3] * b[3]); + + m[4] = (a[0] * b[4]) + (a[2] * b[5]) + a[4]; + m[5] = (a[1] * b[4]) + (a[3] * b[5]) + a[5]; +} + +void M23_Transform(float* m, float& x, float& y) +{ + float vx = x; + float vy = y; + + x = (vx * m[0]) + (vy * m[2]) + m[4]; + y = (vx * m[1]) + (vy * m[3]) + m[5]; +} + + +ScreenLayout::ScreenLayout() +{ + M23_Identity(TopScreenMtx); + M23_Identity(BotScreenMtx); + M23_Identity(HybScreenMtx); + M23_Identity(TouchMtx); + M23_Identity(HybTouchMtx); + TopEnable = true; + BotEnable = true; + HybEnable = false; + HybScreen = 0; + HybPrevTouchScreen = 0; +} + +void ScreenLayout::Setup(int screenWidth, int screenHeight, + ScreenLayoutType screenLayout, + ScreenRotation rotation, + ScreenSizing sizing, + int screenGap, + bool integerScale, + bool swapScreens, + float topAspect, float botAspect) +{ + HybEnable = screenLayout == 3; + if (HybEnable) + { + screenLayout = screenLayout_Natural; + sizing = screenSizing_Even; + HybScreen = swapScreens ? 1 : 0; + swapScreens = false; + topAspect = botAspect = 1; + HybPrevTouchScreen = 0; + } + + float refpoints[6][2] = + { + {0, 0}, {256, 192}, + {0, 0}, {256, 192}, + {0, 0}, {256, 192} + }; + + int layout = screenLayout == screenLayout_Natural + ? rotation % 2 + : screenLayout - 1; + + float botScale = 1; + float hybScale = 1; + float botTrans[4] = {0}; + float hybTrans[2] = {0}; + + M23_Identity(TopScreenMtx); + M23_Identity(BotScreenMtx); + M23_Identity(HybScreenMtx); + + M23_Translate(TopScreenMtx, -256/2, -192/2); + M23_Translate(BotScreenMtx, -256/2, -192/2); + + M23_Scale(TopScreenMtx, topAspect, 1); + M23_Scale(BotScreenMtx, botAspect, 1); + + // rotation + { + float rotmtx[6]; + M23_Identity(rotmtx); + + M23_RotateFast(rotmtx, rotation); + M23_Multiply(TopScreenMtx, rotmtx, TopScreenMtx); + M23_Multiply(BotScreenMtx, rotmtx, BotScreenMtx); + M23_Multiply(HybScreenMtx, rotmtx, HybScreenMtx); + + M23_Transform(TopScreenMtx, refpoints[0][0], refpoints[0][1]); + M23_Transform(TopScreenMtx, refpoints[1][0], refpoints[1][1]); + M23_Transform(BotScreenMtx, refpoints[2][0], refpoints[2][1]); + M23_Transform(BotScreenMtx, refpoints[3][0], refpoints[3][1]); + } + + int posRefPointOffset = 0; + int posRefPointCount = HybEnable ? 6 : 4; + + if (sizing == screenSizing_TopOnly || sizing == screenSizing_BotOnly) + { + float* mtx = sizing == screenSizing_TopOnly ? TopScreenMtx : BotScreenMtx; + int primOffset = sizing == screenSizing_TopOnly ? 0 : 2; + int secOffset = sizing == screenSizing_BotOnly ? 2 : 0; + + float hSize = fabsf(refpoints[primOffset][0] - refpoints[primOffset+1][0]); + float vSize = fabsf(refpoints[primOffset][1] - refpoints[primOffset+1][1]); + + float scale = std::min(screenWidth / hSize, screenHeight / vSize); + if (integerScale) + scale = floorf(scale); + + TopEnable = sizing == screenSizing_TopOnly; + BotEnable = sizing == screenSizing_BotOnly; + botScale = scale; + + M23_Scale(mtx, scale); + refpoints[primOffset][0] *= scale; + refpoints[primOffset][1] *= scale; + refpoints[primOffset+1][0] *= scale; + refpoints[primOffset+1][1] *= scale; + + posRefPointOffset = primOffset; + posRefPointCount = 2; + } + else + { + TopEnable = BotEnable = true; + + // move screens apart + { + int idx = layout == 0 ? 1 : 0; + + bool moveV = rotation % 2 == layout; + + float offsetBot = (moveV ? 192.0 : 256.0 * botAspect) / 2.0 + screenGap / 2.0; + float offsetTop = -((moveV ? 192.0 : 256.0 * topAspect) / 2.0 + screenGap / 2.0); + + if ((rotation == 1 || rotation == 2) ^ swapScreens) + { + offsetTop *= -1; + offsetBot *= -1; + } + + M23_Translate(TopScreenMtx, (idx==0)?offsetTop:0, (idx==1)?offsetTop:0); + M23_Translate(BotScreenMtx, (idx==0)?offsetBot:0, (idx==1)?offsetBot:0); + + refpoints[0][idx] += offsetTop; + refpoints[1][idx] += offsetTop; + refpoints[2][idx] += offsetBot; + refpoints[3][idx] += offsetBot; + + botTrans[idx] = offsetBot; + } + + // scale + { + if (sizing == screenSizing_Even) + { + float minX = refpoints[0][0], maxX = minX; + float minY = refpoints[0][1], maxY = minY; + + for (int i = 1; i < 4; i++) + { + minX = std::min(minX, refpoints[i][0]); + minY = std::min(minY, refpoints[i][1]); + maxX = std::max(maxX, refpoints[i][0]); + maxY = std::max(maxY, refpoints[i][1]); + } + + float hSize = maxX - minX; + float vSize = maxY - minY; + + if (HybEnable) + { + hybScale = layout == 0 + ? (4 * vSize) / (3 * hSize) + : (4 * hSize) / (3 * vSize); + if (layout == 0) + hSize += (vSize * 4) / 3; + else + vSize += (hSize * 4) / 3; + } + + // scale evenly + float scale = std::min(screenWidth / hSize, screenHeight / vSize); + + if (integerScale) + scale = floor(scale); + + hybScale *= scale; + + M23_Scale(TopScreenMtx, scale); + M23_Scale(BotScreenMtx, scale); + M23_Scale(HybScreenMtx, hybScale); + + for (int i = 0; i < 4; i++) + { + refpoints[i][0] *= scale; + refpoints[i][1] *= scale; + } + + botScale = scale; + + // move screens aside + if (HybEnable) + { + float hybWidth = layout == 0 + ? (scale * vSize * 4) / 3 + : (scale * hSize * 4) / 3; + + if (rotation > screenRot_90Deg) + hybWidth *= -1; + + M23_Translate(TopScreenMtx, (layout==0)?hybWidth:0, (layout==1)?hybWidth:0); + M23_Translate(BotScreenMtx, (layout==0)?hybWidth:0, (layout==1)?hybWidth:0); + refpoints[0][layout] += hybWidth; + refpoints[1][layout] += hybWidth; + refpoints[2][layout] += hybWidth; + refpoints[3][layout] += hybWidth; + + botTrans[2+layout] += hybWidth; + + hybTrans[0] = scale * (rotation == screenRot_0Deg || rotation == screenRot_270Deg ? minX : maxX); + hybTrans[1] = scale * (rotation == screenRot_0Deg || rotation == screenRot_90Deg ? minY : maxY); + M23_Translate(HybScreenMtx, hybTrans[0], hybTrans[1]); + + M23_Transform(HybScreenMtx, refpoints[4][0], refpoints[4][1]); + M23_Transform(HybScreenMtx, refpoints[5][0], refpoints[5][1]); + } + } + else + { + int primOffset = (sizing == screenSizing_EmphTop) ? 0 : 2; + int secOffset = (sizing == screenSizing_EmphTop) ? 2 : 0; + float* primMtx = (sizing == screenSizing_EmphTop) ? TopScreenMtx : BotScreenMtx; + float* secMtx = (sizing == screenSizing_EmphTop) ? BotScreenMtx : TopScreenMtx; + + float primMinX = refpoints[primOffset][0], primMaxX = primMinX; + float primMinY = refpoints[primOffset][1], primMaxY = primMinY; + float secMinX = refpoints[secOffset][0], secMaxX = secMinX; + float secMinY = refpoints[secOffset][1], secMaxY = secMinY; + + primMinX = std::min(primMinX, refpoints[primOffset+1][0]); + primMinY = std::min(primMinY, refpoints[primOffset+1][1]); + primMaxX = std::max(primMaxX, refpoints[primOffset+1][0]); + primMaxY = std::max(primMaxY, refpoints[primOffset+1][1]); + + secMinX = std::min(secMinX, refpoints[secOffset+1][0]); + secMinY = std::min(secMinY, refpoints[secOffset+1][1]); + secMaxX = std::max(secMaxX, refpoints[secOffset+1][0]); + secMaxY = std::max(secMaxY, refpoints[secOffset+1][1]); + + float primHSize = layout == 1 ? std::max(primMaxX, -primMinX) : primMaxX - primMinX; + float primVSize = layout == 0 ? std::max(primMaxY, -primMinY) : primMaxY - primMinY; + + float secHSize = layout == 1 ? std::max(secMaxX, -secMinX) : secMaxX - secMinX; + float secVSize = layout == 0 ? std::max(secMaxY, -secMinY) : secMaxY - secMinY; + + float primScale = std::min(screenWidth / primHSize, screenHeight / primVSize); + float secScale = 1.f; + + if (integerScale) + primScale = floorf(primScale); + + if (layout == 0) + { + if (screenHeight - primVSize * primScale < secVSize) + primScale = std::min(screenWidth / primHSize, (screenHeight - secVSize) / primVSize); + else + secScale = std::min((screenHeight - primVSize * primScale) / secVSize, screenWidth / secHSize); + } + else + { + if (screenWidth - primHSize * primScale < secHSize) + primScale = std::min((screenWidth - secHSize) / primHSize, screenHeight / primVSize); + else + secScale = std::min((screenWidth - primHSize * primScale) / secHSize, screenHeight / secVSize); + } + + if (integerScale) + { + primScale = floorf(primScale); + secScale = floorf(secScale); + } + + M23_Scale(primMtx, primScale); + M23_Scale(secMtx, secScale); + + refpoints[primOffset+0][0] *= primScale; + refpoints[primOffset+0][1] *= primScale; + refpoints[primOffset+1][0] *= primScale; + refpoints[primOffset+1][1] *= primScale; + refpoints[secOffset+0][0] *= secScale; + refpoints[secOffset+0][1] *= secScale; + refpoints[secOffset+1][0] *= secScale; + refpoints[secOffset+1][1] *= secScale; + + botScale = (sizing == screenSizing_EmphTop) ? secScale : primScale; + } + } + } + + // position + { + float minX = refpoints[posRefPointOffset][0], maxX = minX; + float minY = refpoints[posRefPointOffset][1], maxY = minY; + + for (int i = posRefPointOffset + 1; i < posRefPointOffset + posRefPointCount; i++) + { + minX = std::min(minX, refpoints[i][0]); + minY = std::min(minY, refpoints[i][1]); + maxX = std::max(maxX, refpoints[i][0]); + maxY = std::max(maxY, refpoints[i][1]); + } + + float width = maxX - minX; + float height = maxY - minY; + + float tx = (screenWidth/2) - (width/2) - minX; + float ty = (screenHeight/2) - (height/2) - minY; + + M23_Translate(TopScreenMtx, tx, ty); + M23_Translate(BotScreenMtx, tx, ty); + M23_Translate(HybScreenMtx, tx, ty); + + botTrans[2] += tx; botTrans[3] += ty; + hybTrans[0] += tx; hybTrans[1] += ty; + } + + // prepare a 'reverse' matrix for the touchscreen + // this matrix undoes the transforms applied to the bottom screen + // and can be used to calculate touchscreen coords from host screen coords + if (BotEnable) + { + M23_Identity(TouchMtx); + + M23_Translate(TouchMtx, -botTrans[2], -botTrans[3]); + M23_Scale(TouchMtx, 1.f / botScale); + M23_Translate(TouchMtx, -botTrans[0], -botTrans[1]); + + float rotmtx[6]; + M23_Identity(rotmtx); + M23_RotateFast(rotmtx, (4-rotation) & 3); + M23_Multiply(TouchMtx, rotmtx, TouchMtx); + + M23_Scale(TouchMtx, 1.f/botAspect, 1); + M23_Translate(TouchMtx, 256/2, 192/2); + + if (HybEnable && HybScreen == 1) + { + M23_Identity(HybTouchMtx); + + M23_Translate(HybTouchMtx, -hybTrans[0], -hybTrans[1]); + M23_Scale(HybTouchMtx, 1.f/hybScale); + M23_Multiply(HybTouchMtx, rotmtx, HybTouchMtx); + } + } +} + +int ScreenLayout::GetScreenTransforms(float* out, int* kind) +{ + int num = 0; + if (TopEnable) + { + memcpy(out + 6*num, TopScreenMtx, sizeof(TopScreenMtx)); + kind[num++] = 0; + } + if (BotEnable) + { + memcpy(out + 6*num, BotScreenMtx, sizeof(BotScreenMtx)); + kind[num++] = 1; + } + if (HybEnable) + { + memcpy(out + 6*num, HybScreenMtx, sizeof(HybScreenMtx)); + kind[num++] = HybScreen; + } + return num; +} + +bool ScreenLayout::GetTouchCoords(int& x, int& y, bool clamp) +{ + if (HybEnable && HybScreen == 1) + { + float vx = x; + float vy = y; + float hvx = x; + float hvy = y; + + M23_Transform(TouchMtx, vx, vy); + M23_Transform(HybTouchMtx, hvx, hvy); + + if (clamp) + { + if (HybPrevTouchScreen == 1) + { + x = std::clamp((int)vx, 0, 255); + y = std::clamp((int)vy, 0, 191); + + return true; + } + if (HybPrevTouchScreen == 2) + { + x = std::clamp((int)hvx, 0, 255); + y = std::clamp((int)hvy, 0, 191); + + return true; + } + } + else + { + if (vx >= 0 && vx < 256 && vy >= 0 && vy < 192) + { + HybPrevTouchScreen = 1; + + x = (int)vx; + y = (int)vy; + + return true; + } + if (hvx >= 0 && hvx < 256 && hvy >= 0 && hvy < 192) + { + HybPrevTouchScreen = 2; + + x = (int)hvx; + y = (int)hvy; + + return true; + } + } + } + else if (BotEnable) + { + float vx = x; + float vy = y; + + M23_Transform(TouchMtx, vx, vy); + + if (clamp) + { + x = std::clamp((int)vx, 0, 255); + y = std::clamp((int)vy, 0, 191); + + return true; + } + else + { + if (vx >= 0 && vx < 256 && vy >= 0 && vy < 192) + { + x = (int)vx; + y = (int)vy; + + return true; + } + } + } + + return false; +} diff --git a/src/frontend/ScreenLayout.h b/src/frontend/ScreenLayout.h new file mode 100644 index 00000000..7c40526c --- /dev/null +++ b/src/frontend/ScreenLayout.h @@ -0,0 +1,104 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS 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 3 of the License, or (at your option) + any later version. + + melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef SCREENLAYOUT_H +#define SCREENLAYOUT_H + +enum ScreenLayoutType +{ + screenLayout_Natural, // top screen above bottom screen always + screenLayout_Horizontal, + screenLayout_Vertical, + screenLayout_Hybrid, + screenLayout_MAX, +}; + +enum ScreenRotation +{ + screenRot_0Deg, + screenRot_90Deg, + screenRot_180Deg, + screenRot_270Deg, + screenRot_MAX, +}; + +enum ScreenSizing +{ + screenSizing_Even, // both screens get same size + screenSizing_EmphTop, // make top screen as big as possible, fit bottom screen in remaining space + screenSizing_EmphBot, + screenSizing_Auto, // not applied in SetupScreenLayout + screenSizing_TopOnly, + screenSizing_BotOnly, + screenSizing_MAX, +}; + +const int kMaxScreenTransforms = 3; + +class ScreenLayout +{ +public: + ScreenLayout(); + ~ScreenLayout() {} + + // setup the display layout based on the provided display size and parameters + // * screenWidth/screenHeight: size of the host display + // * screenLayout: how the DS screens are laid out + // * rotation: angle at which the DS screens are presented + // * sizing: how the display size is shared between the two screens + // * screenGap: size of the gap between the two screens in pixels + // * integerScale: force screens to be scaled up at integer scaling factors + // * screenSwap: whether to swap the position of both screens + // * topAspect/botAspect: ratio by which to scale the top and bottom screen respectively + void Setup(int screenWidth, int screenHeight, + ScreenLayoutType screenLayout, + ScreenRotation rotation, + ScreenSizing sizing, + int screenGap, + bool integerScale, + bool swapScreens, + float topAspect, float botAspect); + + // get a 2x3 transform matrix for each screen and whether it's a top or bottom screen + // note: the transform assumes an origin point at the top left of the display, + // X going right and Y going down + // for each screen the source coordinates should be (0,0) and (256,192) + // 'out' should point to an array of 6*MaxScreenTransforms floats + // 'kind' should point to an array of MaxScreenTransforms ints + // (0 = indicates top screen, 1 = bottom screen) + // returns the amount of screens + int GetScreenTransforms(float* out, int* kind); + + // de-transform the provided host display coordinates to get coordinates + // on the bottom screen + bool GetTouchCoords(int& x, int& y, bool clamp); + +private: + float TopScreenMtx[6]; + float BotScreenMtx[6]; + float HybScreenMtx[6]; + float TouchMtx[6]; + float HybTouchMtx[6]; + bool TopEnable; + bool BotEnable; + bool HybEnable; + int HybScreen; + int HybPrevTouchScreen; // 0:unknown, 1:buttom screen, 2:hybrid screen +}; + +#endif // SCREENLAYOUT_H diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 83e3e3d4..039c0091 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -44,8 +44,7 @@ set(SOURCES_QT_SDL ArchiveUtil.h ArchiveUtil.cpp - ../Util_Video.cpp - ../FrontendUtil.h + ../ScreenLayout.cpp ../mic_blow.h ../glad/glad.c diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index a607c2a9..e680d273 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -27,7 +27,7 @@ #include "Platform.h" #include "Config.h" -#include "FrontendUtil.h" +#include "ScreenLayout.h" #include "main.h" using namespace std::string_literals; @@ -37,16 +37,6 @@ namespace Config { using namespace melonDS; -int ScreenRotation; -int ScreenGap; -int ScreenLayout; -bool ScreenSwap; -int ScreenSizing; -bool IntegerScaling; -int ScreenAspectTop; -int ScreenAspectBot; -bool ScreenFilter; - bool ScreenUseGL; bool ScreenVSync; int ScreenVSyncInterval; @@ -108,10 +98,10 @@ RangeList IntRanges = {"Audio.Interpolation", {0, 3}}, {"Instance*.Audio.Volume", {0, 256}}, {"Mic.InputType", {0, micInputType_MAX-1}}, - {"Instance*.Window*.ScreenRotation", {0, Frontend::screenRot_MAX-1}}, + {"Instance*.Window*.ScreenRotation", {0, screenRot_MAX-1}}, {"Instance*.Window*.ScreenGap", {0, 500}}, - {"Instance*.Window*.ScreenLayout", {0, Frontend::screenLayout_MAX-1}}, - {"Instance*.Window*.ScreenSizing", {0, Frontend::screenSizing_MAX-1}}, + {"Instance*.Window*.ScreenLayout", {0, screenLayout_MAX-1}}, + {"Instance*.Window*.ScreenSizing", {0, screenSizing_MAX-1}}, {"Instance*.Window*.ScreenAspectTop", {0, AspectRatiosNum-1}}, {"Instance*.Window*.ScreenAspectBot", {0, AspectRatiosNum-1}}, {"MP.AudioMode", {0, 2}}, diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 54ed63b9..4a18b63f 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -151,16 +151,6 @@ private: }; -extern int ScreenRotation; -extern int ScreenGap; -extern int ScreenLayout; -extern bool ScreenSwap; -extern int ScreenSizing; -extern int ScreenAspectTop; -extern int ScreenAspectBot; -extern bool IntegerScaling; -extern bool ScreenFilter; - extern bool ScreenUseGL; extern bool ScreenVSync; extern int ScreenVSyncInterval; diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 66129a41..38fc29cf 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -33,7 +33,7 @@ #include "types.h" #include "version.h" -#include "FrontendUtil.h" +#include "ScreenLayout.h" #include "Args.h" #include "NDS.h" @@ -79,19 +79,6 @@ EmuThread::EmuThread(EmuInstance* inst, QObject* parent) : QThread(parent) EmuRunning = emuStatus_Paused; EmuPauseStack = EmuPauseStackRunning; RunningSomething = false; - - /*connect(this, SIGNAL(windowUpdate()), mainWindow->panel, SLOT(repaint())); - connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); - connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); - connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); - connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); - connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); - connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger())); - connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger())); - connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged())); - connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled())); - connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); - connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled()));*/ } void EmuThread::attachWindow(MainWindow* window) @@ -108,7 +95,7 @@ void EmuThread::attachWindow(MainWindow* window) connect(this, SIGNAL(windowEmuReset()), window->actReset, SLOT(trigger())); connect(this, SIGNAL(windowEmuFrameStep()), window->actFrameStep, SLOT(trigger())); connect(this, SIGNAL(windowLimitFPSChange()), window->actLimitFramerate, SLOT(trigger())); - connect(this, SIGNAL(screenLayoutChange()), window->panel, SLOT(onScreenLayoutChanged())); + connect(this, SIGNAL(autoScreenSizingChange(int)), window->panel, SLOT(onAutoScreenSizingChanged(int))); connect(this, SIGNAL(windowFullscreenToggle()), window, SLOT(onFullscreenToggled())); connect(this, SIGNAL(swapScreensToggle()), window->actScreenSwap, SLOT(trigger())); connect(this, SIGNAL(screenEmphasisToggle()), window, SLOT(onScreenEmphasisToggled())); @@ -124,7 +111,7 @@ void EmuThread::detachWindow(MainWindow* window) disconnect(this, SIGNAL(windowEmuReset()), window->actReset, SLOT(trigger())); disconnect(this, SIGNAL(windowEmuFrameStep()), window->actFrameStep, SLOT(trigger())); disconnect(this, SIGNAL(windowLimitFPSChange()), window->actLimitFramerate, SLOT(trigger())); - disconnect(this, SIGNAL(screenLayoutChange()), window->panel, SLOT(onScreenLayoutChanged())); + disconnect(this, SIGNAL(autoScreenSizingChange(int)), window->panel, SLOT(onAutoScreenSizingChanged(int))); disconnect(this, SIGNAL(windowFullscreenToggle()), window, SLOT(onFullscreenToggled())); disconnect(this, SIGNAL(swapScreensToggle()), window->actScreenSwap, SLOT(trigger())); disconnect(this, SIGNAL(screenEmphasisToggle()), window, SLOT(onScreenEmphasisToggled())); @@ -307,7 +294,6 @@ void EmuThread::run() emuInstance->micProcess(); // auto screen layout - if (Config::ScreenSizing == Frontend::screenSizing_Auto) { mainScreenPos[2] = mainScreenPos[1]; mainScreenPos[1] = mainScreenPos[0]; @@ -319,20 +305,20 @@ void EmuThread::run() { // constant flickering, likely displaying 3D on both screens // TODO: when both screens are used for 2D only...??? - guess = Frontend::screenSizing_Even; + guess = screenSizing_Even; } else { if (mainScreenPos[0] == 1) - guess = Frontend::screenSizing_EmphTop; + guess = screenSizing_EmphTop; else - guess = Frontend::screenSizing_EmphBot; + guess = screenSizing_EmphBot; } if (guess != autoScreenSizing) { autoScreenSizing = guess; - emit screenLayoutChange(); + emit autoScreenSizingChange(autoScreenSizing); } } diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index 6f38b600..eb00ca07 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -83,7 +83,7 @@ signals: void windowLimitFPSChange(); - void screenLayoutChange(); + void autoScreenSizingChange(int sizing); void windowFullscreenToggle(); diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index f7564a8f..a86a6e44 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -59,7 +59,6 @@ using namespace melonDS; // TEMP extern bool RunningSomething; -extern int autoScreenSizing; extern int videoRenderer; extern bool videoSettingsDirty; @@ -91,6 +90,9 @@ ScreenPanel::ScreenPanel(QWidget* parent) : QWidget(parent) osdEnabled = false; osdID = 1; + + loadConfig(); + setFilter(mainWindow->getWindowConfig().GetBool("ScreenFilter")); } ScreenPanel::~ScreenPanel() @@ -99,6 +101,25 @@ ScreenPanel::~ScreenPanel() delete mouseTimer; } +void ScreenPanel::loadConfig() +{ + auto& cfg = mainWindow->getWindowConfig(); + + screenRotation = cfg.GetInt("ScreenRotation"); + screenGap = cfg.GetInt("ScreenGap"); + screenLayout = cfg.GetInt("ScreenLayout"); + screenSwap = cfg.GetBool("ScreenSwap"); + screenSizing = cfg.GetInt("ScreenSizing"); + integerScaling = cfg.GetBool("IntegerScaling"); + screenAspectTop = cfg.GetInt("ScreenAspectTop"); + screenAspectBot = cfg.GetInt("ScreenAspectBot"); +} + +void ScreenPanel::setFilter(bool filter) +{ + this->filter = filter; +} + void ScreenPanel::setMouseHide(bool enable, int delay) { mouseHide = enable; @@ -112,16 +133,16 @@ void ScreenPanel::setupScreenLayout() int w = width(); int h = height(); - int sizing = Config::ScreenSizing; - if (sizing == 3) sizing = autoScreenSizing; + int sizing = screenSizing; + if (sizing == screenSizing_Auto) sizing = autoScreenSizing; float aspectTop, aspectBot; for (auto ratio : aspectRatios) { - if (ratio.id == Config::ScreenAspectTop) + if (ratio.id == screenAspectTop) aspectTop = ratio.ratio; - if (ratio.id == Config::ScreenAspectBot) + if (ratio.id == screenAspectBot) aspectBot = ratio.ratio; } @@ -131,49 +152,49 @@ void ScreenPanel::setupScreenLayout() if (aspectBot == 0) aspectBot = ((float) w / h) / (4.f / 3.f); - Frontend::SetupScreenLayout(w, h, - static_cast(Config::ScreenLayout), - static_cast(Config::ScreenRotation), - static_cast(sizing), - Config::ScreenGap, - Config::IntegerScaling != 0, - Config::ScreenSwap != 0, - aspectTop, - aspectBot); + layout.Setup(w, h, + static_cast(screenLayout), + static_cast(screenRotation), + static_cast(sizing), + screenGap, + integerScaling != 0, + screenSwap != 0, + aspectTop, + aspectBot); - numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind); + numScreens = layout.GetScreenTransforms(screenMatrix[0], screenKind); } QSize ScreenPanel::screenGetMinSize(int factor = 1) { - bool isHori = (Config::ScreenRotation == Frontend::screenRot_90Deg - || Config::ScreenRotation == Frontend::screenRot_270Deg); - int gap = Config::ScreenGap * factor; + bool isHori = (screenRotation == screenRot_90Deg + || screenRotation == screenRot_270Deg); + int gap = screenGap * factor; int w = 256 * factor; int h = 192 * factor; - if (Config::ScreenSizing == Frontend::screenSizing_TopOnly - || Config::ScreenSizing == Frontend::screenSizing_BotOnly) + if (screenSizing == screenSizing_TopOnly + || screenSizing == screenSizing_BotOnly) { return QSize(w, h); } - if (Config::ScreenLayout == Frontend::screenLayout_Natural) + if (screenLayout == screenLayout_Natural) { if (isHori) return QSize(h+gap+h, w); else return QSize(w, h+gap+h); } - else if (Config::ScreenLayout == Frontend::screenLayout_Vertical) + else if (screenLayout == screenLayout_Vertical) { if (isHori) return QSize(h, w+gap+w); else return QSize(w, h+gap+h); } - else if (Config::ScreenLayout == Frontend::screenLayout_Horizontal) + else if (screenLayout == screenLayout_Horizontal) { if (isHori) return QSize(h+gap+h, w); @@ -191,10 +212,20 @@ QSize ScreenPanel::screenGetMinSize(int factor = 1) void ScreenPanel::onScreenLayoutChanged() { + loadConfig(); + setMinimumSize(screenGetMinSize()); setupScreenLayout(); } +void ScreenPanel::onAutoScreenSizingChanged(int sizing) +{ + autoScreenSizing = sizing; + if (screenSizing != screenSizing_Auto) return; + + setupScreenLayout(); +} + void ScreenPanel::resizeEvent(QResizeEvent* event) { setupScreenLayout(); @@ -209,7 +240,7 @@ void ScreenPanel::mousePressEvent(QMouseEvent* event) int x = event->pos().x(); int y = event->pos().y(); - if (Frontend::GetTouchCoords(x, y, false)) + if (layout.GetTouchCoords(x, y, false)) { touching = true; assert(emuInstance->getNDS() != nullptr); @@ -242,7 +273,7 @@ void ScreenPanel::mouseMoveEvent(QMouseEvent* event) int x = event->pos().x(); int y = event->pos().y(); - if (Frontend::GetTouchCoords(x, y, true)) + if (layout.GetTouchCoords(x, y, true)) { assert(emuInstance->getNDS() != nullptr); emuInstance->getNDS()->TouchScreen(x, y); @@ -261,7 +292,7 @@ void ScreenPanel::tabletEvent(QTabletEvent* event) int x = event->x(); int y = event->y(); - if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TabletMove)) + if (layout.GetTouchCoords(x, y, event->type()==QEvent::TabletMove)) { touching = true; assert(emuInstance->getNDS() != nullptr); @@ -296,7 +327,7 @@ void ScreenPanel::touchEvent(QTouchEvent* event) int x = (int)lastPosition.x(); int y = (int)lastPosition.y(); - if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate)) + if (layout.GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate)) { touching = true; assert(emuInstance->getNDS() != nullptr); @@ -1115,7 +1146,6 @@ void ScreenPanelGL::transferLayout() lastScreenHeight = windowInfo->surface_height; } - this->filter = Config::ScreenFilter; this->windowInfo = *windowInfo; screenSettingsLock.unlock(); diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h index 53b55341..83117849 100644 --- a/src/frontend/qt_sdl/Screen.h +++ b/src/frontend/qt_sdl/Screen.h @@ -31,7 +31,7 @@ #include #include "glad/glad.h" -#include "FrontendUtil.h" +#include "ScreenLayout.h" #include "duckstation/gl/context.h" @@ -58,6 +58,8 @@ public: explicit ScreenPanel(QWidget* parent); virtual ~ScreenPanel(); + void setFilter(bool filter); + void setMouseHide(bool enable, int delay); QTimer* setupMouseTimer(); @@ -70,11 +72,31 @@ public: private slots: void onScreenLayoutChanged(); + void onAutoScreenSizingChanged(int sizing); protected: MainWindow* mainWindow; EmuInstance* emuInstance; + bool filter; + + int screenRotation; + int screenGap; + int screenLayout; + bool screenSwap; + int screenSizing; + bool integerScaling; + int screenAspectTop, screenAspectBot; + + int autoScreenSizing; + + ScreenLayout layout; + float screenMatrix[kMaxScreenTransforms][6]; + int screenKind[kMaxScreenTransforms]; + int numScreens; + + bool touching = false; + bool mouseHide; int mouseHideDelay; @@ -95,6 +117,8 @@ protected: unsigned int osdID; std::deque osdItems; + void loadConfig(); + virtual void setupScreenLayout(); void resizeEvent(QResizeEvent* event) override; @@ -107,12 +131,6 @@ protected: void touchEvent(QTouchEvent* event); bool event(QEvent* event) override; - float screenMatrix[Frontend::MaxScreenTransforms][6]; - int screenKind[Frontend::MaxScreenTransforms]; - int numScreens; - - bool touching = false; - void showCursor(); int osdFindBreakPoint(const char* text, int i); @@ -141,7 +159,7 @@ private: void setupScreenLayout() override; QImage screen[2]; - QTransform screenTrans[Frontend::MaxScreenTransforms]; + QTransform screenTrans[kMaxScreenTransforms]; }; @@ -187,7 +205,6 @@ private: QMutex screenSettingsLock; WindowInfo windowInfo; - bool filter; int lastScreenWidth = -1, lastScreenHeight = -1; diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 0433b6ac..33cdb46f 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -462,7 +462,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : QMenu* submenu = menu->addMenu("Screen rotation"); grpScreenRotation = new QActionGroup(submenu); - for (int i = 0; i < Frontend::screenRot_MAX; i++) + for (int i = 0; i < screenRot_MAX; i++) { int data = i*90; actScreenRotation[i] = submenu->addAction(QString("%1°").arg(data)); @@ -496,7 +496,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : const char* screenlayout[] = {"Natural", "Vertical", "Horizontal", "Hybrid"}; - for (int i = 0; i < Frontend::screenLayout_MAX; i++) + for (int i = 0; i < screenLayout_MAX; i++) { actScreenLayout[i] = submenu->addAction(QString(screenlayout[i])); actScreenLayout[i]->setActionGroup(grpScreenLayout); @@ -518,7 +518,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto", "Top only", "Bottom only"}; - for (int i = 0; i < Frontend::screenSizing_MAX; i++) + for (int i = 0; i < screenSizing_MAX; i++) { actScreenSizing[i] = submenu->addAction(QString(screensizing[i])); actScreenSizing[i]->setActionGroup(grpScreenSizing); @@ -594,8 +594,6 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : } setMenuBar(menubar); - //resize(curW, curH); - if (localCfg.GetString("Firmware.Username") == "Arisotura") actMPNewInstance->setText("Fart"); @@ -606,10 +604,6 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : move(frameGeo.topLeft()); #endif - /*if (oldMax) - showMaximized(); - else - show();*/ std::string geom = windowCfg.GetString("Geometry"); if (!geom.empty()) { @@ -658,32 +652,35 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : actSavestateSRAMReloc->setChecked(globalCfg.GetBool("Savestate.RelocSRAM")); - actScreenRotation[Config::ScreenRotation]->setChecked(true); + actScreenRotation[windowCfg.GetInt("ScreenRotation")]->setChecked(true); + int screenGap = windowCfg.GetInt("ScreenGap"); for (int i = 0; i < 6; i++) { - if (actScreenGap[i]->data().toInt() == Config::ScreenGap) + if (actScreenGap[i]->data().toInt() == screenGap) { actScreenGap[i]->setChecked(true); break; } } - actScreenLayout[Config::ScreenLayout]->setChecked(true); - actScreenSizing[Config::ScreenSizing]->setChecked(true); - actIntegerScaling->setChecked(Config::IntegerScaling); + actScreenLayout[windowCfg.GetInt("ScreenLayout")]->setChecked(true); + actScreenSizing[windowCfg.GetInt("ScreenSizing")]->setChecked(true); + actIntegerScaling->setChecked(windowCfg.GetBool("IntegerScaling")); - actScreenSwap->setChecked(Config::ScreenSwap); + actScreenSwap->setChecked(windowCfg.GetBool("ScreenSwap")); + int aspectTop = windowCfg.GetInt("ScreenAspectTop"); + int aspectBot = windowCfg.GetInt("ScreenAspectBot"); for (int i = 0; i < AspectRatiosNum; i++) { - if (Config::ScreenAspectTop == aspectRatios[i].id) + if (aspectTop == aspectRatios[i].id) actScreenAspectTop[i]->setChecked(true); - if (Config::ScreenAspectBot == aspectRatios[i].id) + if (aspectBot == aspectRatios[i].id) actScreenAspectBot[i]->setChecked(true); } - actScreenFiltering->setChecked(Config::ScreenFilter); + actScreenFiltering->setChecked(windowCfg.GetBool("ScreenFilter")); actShowOSD->setChecked(showOSD); actLimitFramerate->setChecked(emuInstance->doLimitFPS); @@ -1886,7 +1883,7 @@ void MainWindow::onChangeScreenSize() void MainWindow::onChangeScreenRotation(QAction* act) { int rot = act->data().toInt(); - Config::ScreenRotation = rot; + windowCfg.SetInt("ScreenRotation", rot); emit screenLayoutChange(); } @@ -1894,7 +1891,7 @@ void MainWindow::onChangeScreenRotation(QAction* act) void MainWindow::onChangeScreenGap(QAction* act) { int gap = act->data().toInt(); - Config::ScreenGap = gap; + windowCfg.SetInt("ScreenGap", gap); emit screenLayoutChange(); } @@ -1902,30 +1899,32 @@ void MainWindow::onChangeScreenGap(QAction* act) void MainWindow::onChangeScreenLayout(QAction* act) { int layout = act->data().toInt(); - Config::ScreenLayout = layout; + windowCfg.SetInt("ScreenLayout", layout); emit screenLayoutChange(); } void MainWindow::onChangeScreenSwap(bool checked) { - Config::ScreenSwap = checked?1:0; + windowCfg.SetBool("ScreenSwap", checked); // Swap between top and bottom screen when displaying one screen. - if (Config::ScreenSizing == Frontend::screenSizing_TopOnly) + int sizing = windowCfg.GetInt("ScreenSizing"); + if (sizing == screenSizing_TopOnly) { // Bottom Screen. - Config::ScreenSizing = Frontend::screenSizing_BotOnly; - actScreenSizing[Frontend::screenSizing_TopOnly]->setChecked(false); - actScreenSizing[Config::ScreenSizing]->setChecked(true); + sizing = screenSizing_BotOnly; + actScreenSizing[screenSizing_TopOnly]->setChecked(false); + actScreenSizing[sizing]->setChecked(true); } - else if (Config::ScreenSizing == Frontend::screenSizing_BotOnly) + else if (sizing == screenSizing_BotOnly) { // Top Screen. - Config::ScreenSizing = Frontend::screenSizing_TopOnly; - actScreenSizing[Frontend::screenSizing_BotOnly]->setChecked(false); - actScreenSizing[Config::ScreenSizing]->setChecked(true); + sizing = screenSizing_TopOnly; + actScreenSizing[screenSizing_BotOnly]->setChecked(false); + actScreenSizing[sizing]->setChecked(true); } + windowCfg.SetInt("ScreenSizing", sizing); emit screenLayoutChange(); } @@ -1933,7 +1932,7 @@ void MainWindow::onChangeScreenSwap(bool checked) void MainWindow::onChangeScreenSizing(QAction* act) { int sizing = act->data().toInt(); - Config::ScreenSizing = sizing; + windowCfg.SetInt("ScreenSizing", sizing); emit screenLayoutChange(); } @@ -1945,11 +1944,11 @@ void MainWindow::onChangeScreenAspect(QAction* act) if (group == grpScreenAspectTop) { - Config::ScreenAspectTop = aspect; + windowCfg.SetInt("ScreenAspectTop", aspect); } else { - Config::ScreenAspectBot = aspect; + windowCfg.SetInt("ScreenAspectBot", aspect); } emit screenLayoutChange(); @@ -1957,16 +1956,17 @@ void MainWindow::onChangeScreenAspect(QAction* act) void MainWindow::onChangeIntegerScaling(bool checked) { - Config::IntegerScaling = checked?1:0; + windowCfg.SetBool("IntegerScaling", checked); emit screenLayoutChange(); } void MainWindow::onChangeScreenFiltering(bool checked) { - Config::ScreenFilter = checked?1:0; + windowCfg.SetBool("ScreenFilter", checked); - emit screenLayoutChange(); + //emit screenLayoutChange(); + panel->setFilter(checked); } void MainWindow::onChangeShowOSD(bool checked) @@ -2016,15 +2016,16 @@ void MainWindow::onFullscreenToggled() void MainWindow::onScreenEmphasisToggled() { - int currentSizing = Config::ScreenSizing; - if (currentSizing == Frontend::screenSizing_EmphTop) + int currentSizing = windowCfg.GetInt("ScreenSizing"); + if (currentSizing == screenSizing_EmphTop) { - Config::ScreenSizing = Frontend::screenSizing_EmphBot; + currentSizing = screenSizing_EmphBot; } - else if (currentSizing == Frontend::screenSizing_EmphBot) + else if (currentSizing == screenSizing_EmphBot) { - Config::ScreenSizing = Frontend::screenSizing_EmphTop; + currentSizing = screenSizing_EmphTop; } + windowCfg.SetInt("ScreenSizing", currentSizing); emit screenLayoutChange(); } diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index a8d96893..2346240c 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -20,7 +20,7 @@ #define WINDOW_H #include "glad/glad.h" -#include "FrontendUtil.h" +#include "ScreenLayout.h" #include "duckstation/gl/context.h" #include @@ -108,6 +108,7 @@ public: ~MainWindow(); EmuInstance* getEmuInstance() { return emuInstance; } + Config::Table& getWindowConfig() { return windowCfg; } void attachEmuThread(EmuThread* thread); @@ -296,14 +297,14 @@ public: QAction* actSavestateSRAMReloc; QAction* actScreenSize[4]; QActionGroup* grpScreenRotation; - QAction* actScreenRotation[Frontend::screenRot_MAX]; + QAction* actScreenRotation[screenRot_MAX]; QActionGroup* grpScreenGap; QAction* actScreenGap[6]; QActionGroup* grpScreenLayout; - QAction* actScreenLayout[Frontend::screenLayout_MAX]; + QAction* actScreenLayout[screenLayout_MAX]; QAction* actScreenSwap; QActionGroup* grpScreenSizing; - QAction* actScreenSizing[Frontend::screenSizing_MAX]; + QAction* actScreenSizing[screenSizing_MAX]; QAction* actIntegerScaling; QActionGroup* grpScreenAspectTop; QAction** actScreenAspectTop; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index a077c554..c545f73a 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -318,12 +318,6 @@ int main(int argc, char** argv) #endif SANITIZE(Config::ScreenVSyncInterval, 1, 20); SANITIZE(Config::GL_ScaleFactor, 1, 16); - SANITIZE(Config::ScreenRotation, 0, (int)Frontend::screenRot_MAX); - SANITIZE(Config::ScreenGap, 0, 500); - SANITIZE(Config::ScreenLayout, 0, (int)Frontend::screenLayout_MAX); - SANITIZE(Config::ScreenSizing, 0, (int)Frontend::screenSizing_MAX); - SANITIZE(Config::ScreenAspectTop, 0, AspectRatiosNum); - SANITIZE(Config::ScreenAspectBot, 0, AspectRatiosNum); #undef SANITIZE camStarted[0] = false; diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index e112afe4..c91dc64b 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -34,7 +34,7 @@ #include "EmuInstance.h" #include "Window.h" #include "EmuThread.h" -#include "FrontendUtil.h" +#include "ScreenLayout.h" class MelonApplication : public QApplication {