Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .github/workflows/manual_generate_apk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,7 @@ jobs:
# uses: nttld/setup-ndk@v1
# with:
# ndk-version: r21e

- name: Test androidGitVersion
run: |
echo "count=${{steps.valid-tags.outputs.count}}"
gradle --quiet androidGitVersion


- name: Assemble APK
run: ./gradlew assemble${{ github.event.inputs.buildVariant }} --stacktrace

Expand Down
60 changes: 60 additions & 0 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,9 @@ static const ConfigSetting controlSettings[] = {

ConfigSetting("SystemControls", SETTING(g_Config, bSystemControls), true, CfgFlag::DEFAULT),
ConfigSetting("RapidFileInterval", SETTING(g_Config, iRapidFireInterval), 5, CfgFlag::DEFAULT),

// Touch layout selection for swap layout feature (persisted value)
ConfigSetting("TouchLayoutSelection", SETTING(g_Config, iTouchLayoutSelectionSaved), 1, CfgFlag::PER_GAME),
};

static const ConfigSetting networkSettings[] = {
Expand Down Expand Up @@ -1154,6 +1157,8 @@ static const ConfigSectionMeta g_sectionMeta[] = {
{ &g_Config.displayLayoutPortrait, displayLayoutSettings, ARRAY_SIZE(displayLayoutSettings), "DisplayLayout.Portrait"}, // These we don't want to read from the old settings, since for most people, those settings will be bad.
{ &g_Config.touchControlsLandscape, touchControlSettings, ARRAY_SIZE(touchControlSettings), "TouchControls.Landscape", "Control" }, // We read the old settings from [Control], since most people played in landscape before.
{ &g_Config.touchControlsPortrait, touchControlSettings, ARRAY_SIZE(touchControlSettings), "TouchControls.Portrait"}, // These we don't want to read from the old settings, since for most people, those settings will be bad.
{ &g_Config.touchControlsLandscapeLayout2, touchControlSettings, ARRAY_SIZE(touchControlSettings), "TouchControls.LandscapeLayout2" },
{ &g_Config.touchControlsPortraitLayout2, touchControlSettings, ARRAY_SIZE(touchControlSettings), "TouchControls.PortraitLayout2" },
{ &g_Config.gestureControls[0], gestureControlSettings, ARRAY_SIZE(gestureControlSettings), "GestureControls.Left", "General"}, // We read the old settings from [General], since most of them used to be there (except the analog stuff).
{ &g_Config.gestureControls[1], gestureControlSettings, ARRAY_SIZE(gestureControlSettings), "GestureControls.Right", "General"}, // We read the old settings from [General], since most of them used to be there (except the analog stuff).
};
Expand Down Expand Up @@ -1405,8 +1410,14 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
// but these configs shouldn't contain older versions anyhow
_dbg_assert_(!IsGameSpecific());

// Ensure layout2 defaults are initialized from layout1 so swapping is visible
EnsureSecondaryLayoutsInitialized();

PostLoadCleanup();

// Apply persisted layout selection to runtime selection.
g_Config.iTouchLayoutSelection = g_Config.iTouchLayoutSelectionSaved;

INFO_LOG(Log::Loader, "Config loaded: '%s' (%0.1f ms)", iniFilename_.c_str(), (time_now_d() - startTime) * 1000.0);
}

Expand Down Expand Up @@ -1924,6 +1935,9 @@ bool Config::LoadGameConfig(const std::string &gameId) {
PostLoadCleanup();

DEBUG_LOG(Log::Loader, "Game-specific config loaded: %s", gameId_.c_str());

// Apply persisted layout selection to runtime selection for game-specific mode.
g_Config.iTouchLayoutSelection = g_Config.iTouchLayoutSelectionSaved;
return true;
}

Expand Down Expand Up @@ -2101,3 +2115,49 @@ int MultiplierToVolume100(float multiplier) {
float UIScaleFactorToMultiplier(int factor) {
return powf(2.0f, (float)factor / 8.0f);
}
void Config::SwapTouchControlsLayouts() {
// Instead of swapping the large layout structs themselves,
// just toggle the selection between layout 1 and 2.
// This is much safer: avoids invalidating any references/pointers
// to the layout structs, and is atomic.
if (iTouchLayoutSelection == 1) {
iTouchLayoutSelection = 2;
} else {
iTouchLayoutSelection = 1;
}
// Note: do NOT auto-save here. Caller should persist if desired.
}

void Config::EnsureSecondaryLayoutsInitialized() {
auto IsConfigured = [](const TouchControlConfig &c) {
// If any position has been set (x >= 0), or any custom button is visible,
// consider the layout configured.
auto posSet = [](const ConfigTouchPos &p) { return p.x >= 0.0f || p.y >= 0.0f; };

if (posSet(c.touchActionButtonCenter) || posSet(c.touchDpad) || posSet(c.touchStartKey) ||
posSet(c.touchSelectKey) || posSet(c.touchFastForwardKey) || posSet(c.touchLKey) ||
posSet(c.touchRKey) || posSet(c.touchAnalogStick) || posSet(c.touchRightAnalogStick) ||
posSet(c.touchPauseKey)) {
return true;
}

for (size_t i = 0; i < TouchControlConfig::CUSTOM_BUTTON_COUNT; ++i) {
if (c.touchCustom[i].show)
return true;
if (posSet(c.touchCustom[i]))
return true;
}

return false;
};

if (!IsConfigured(touchControlsPortraitLayout2)) {
// Use preset defaults for the secondary layout instead of copying
// the primary layout. This avoids unintentionally duplicating
// user-customized primary layouts into layout 2.
touchControlsPortraitLayout2.ResetToDefault("TouchControls.PortraitLayout2");
}
if (!IsConfigured(touchControlsLandscapeLayout2)) {
touchControlsLandscapeLayout2.ResetToDefault("TouchControls.LandscapeLayout2");
}
}
37 changes: 37 additions & 0 deletions Core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,20 @@ struct Config : public ConfigBlock {
TouchControlConfig touchControlsLandscape;
TouchControlConfig touchControlsPortrait;

// Alternative layout (for swap layout feature)
TouchControlConfig touchControlsLandscapeLayout2;
TouchControlConfig touchControlsPortraitLayout2;

// Current layout selection (1 for primary layout, 2 for secondary layout)
// Runtime selection used by UI/runtime. This value reflects the currently
// active layout during runtime but is not written directly to the ini when
// changed in-game via button bindings.
int iTouchLayoutSelection = 1;

// Persisted selection stored in ini. Editor changes should update this
// value so the user's saved preference is preserved across restarts.
int iTouchLayoutSelectionSaved = 1;

// These are shared between portrait and landscape, just the positions aren't.
ConfigCustomButton CustomButton[TouchControlConfig::CUSTOM_BUTTON_COUNT];

Expand Down Expand Up @@ -734,7 +748,30 @@ struct Config : public ConfigBlock {
return orientation == DeviceOrientation::Portrait ? touchControlsPortrait : touchControlsLandscape;
}

// Get the touched control config based on current layout selection
const TouchControlConfig &GetCurrentTouchControlsConfig(DeviceOrientation orientation) const {
if (iTouchLayoutSelection == 2) {
return orientation == DeviceOrientation::Portrait ? touchControlsPortraitLayout2 : touchControlsLandscapeLayout2;
}
return orientation == DeviceOrientation::Portrait ? touchControlsPortrait : touchControlsLandscape;
}
TouchControlConfig &GetCurrentTouchControlsConfig(DeviceOrientation orientation) {
if (iTouchLayoutSelection == 2) {
return orientation == DeviceOrientation::Portrait ? touchControlsPortraitLayout2 : touchControlsLandscapeLayout2;
}
return orientation == DeviceOrientation::Portrait ? touchControlsPortrait : touchControlsLandscape;
}

// Exchange layout selections between mode 1 and 2
void SwapTouchControlsLayouts();

// Ensure layout2 is initialized as a copy of layout1 if empty
void EnsureSecondaryLayoutsInitialized();

static int GetDefaultValueInt(int *configSetting);
// Initialize Layout 2 as copy of Layout 1 if not already configured
void InitializeLayout2IfNeeded();


void DoNotSaveSetting(void *configSetting) {
settingsNotToSave_.push_back(configSetting);
Expand Down
1 change: 1 addition & 0 deletions Core/KeyMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ const KeyMap_IntStrPair psp_button_names[] = {
#endif
{VIRTKEY_TOGGLE_DEBUGGER, "Toggle Debugger"},
{VIRTKEY_TOGGLE_TILT, "Toggle tilt control"},
{VIRTKEY_SWAP_LAYOUT, "Swap layout"},

{VIRTKEY_OPENCHAT, "OpenChat" },

Expand Down
3 changes: 2 additions & 1 deletion Core/KeyMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ enum VirtKey : u32 {
VIRTKEY_TOGGLE_DEBUGGER = 0x40000033,
VIRTKEY_PAUSE_NO_MENU = 0x40000034,
VIRTKEY_TOGGLE_TILT = 0x40000035,
VIRTKEY_AXIS_SWAP_HOLD = 0x40000036,
VIRTKEY_SWAP_LAYOUT = 0x40000036,
VIRTKEY_AXIS_SWAP_HOLD = 0x40000037,
VIRTKEY_LAST,
VIRTKEY_COUNT = VIRTKEY_LAST - VIRTKEY_FIRST
};
Expand Down
2 changes: 1 addition & 1 deletion UI/CustomButtonMappingScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ void CustomButtonMappingScreen::CreateDialogViews(UI::ViewGroup *parent) {
LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(120, FILL_PARENT));
auto di = GetI18NCategory(I18NCat::DIALOG);

TouchControlConfig &touch = g_Config.GetTouchControlsConfig(deviceOrientation_);
TouchControlConfig &touch = g_Config.GetCurrentTouchControlsConfig(deviceOrientation_);

ConfigCustomButton *cfg = nullptr;
bool *show = nullptr;
Expand Down
13 changes: 12 additions & 1 deletion UI/EmuScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,17 @@ void EmuScreen::OnVKey(VirtKey virtualKeyCode, bool down) {
case VIRTKEY_RAPID_FIRE:
__CtrlSetRapidFire(down, g_Config.iRapidFireInterval);
break;

case VIRTKEY_SWAP_LAYOUT:
if (down) {
// Only perform minimal operations during emulation:
// Toggle layout selection between 1 and 2.
// This is safer than swapping struct contents, avoids invalidating references.
g_Config.SwapTouchControlsLayouts();
System_PostUIMessage(UIMessage::RECREATE_VIEWS);
}
break;

default:
// To make sure we're not in an async context.
if (down) {
Expand Down Expand Up @@ -1262,7 +1273,7 @@ void EmuScreen::CreateViews() {

const DeviceOrientation deviceOrientation = GetDeviceOrientation();

TouchControlConfig &touch = g_Config.GetTouchControlsConfig(deviceOrientation);
TouchControlConfig &touch = g_Config.GetCurrentTouchControlsConfig(deviceOrientation);

const Bounds &bounds = screenManager()->getUIContext()->GetLayoutBounds();
InitPadLayout(&touch, deviceOrientation, bounds.w, bounds.h);
Expand Down
2 changes: 1 addition & 1 deletion UI/GameSettingsScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ void GameSettingsScreen::CreateControlsSettings(UI::ViewGroup *controlsSettings)
CheckBox *touchGliding = controlsSettings->Add(new CheckBox(&g_Config.bTouchGliding, co->T("Keep first touched button pressed when dragging")));
touchGliding->SetEnabledPtr(&g_Config.bShowTouchControls);

TouchControlConfig &touch = g_Config.GetTouchControlsConfig(GetDeviceOrientation());
TouchControlConfig &touch = g_Config.GetCurrentTouchControlsConfig(GetDeviceOrientation());

// Hide stick background, useful when increasing the size
CheckBox *hideStickBackground = controlsSettings->Add(new CheckBox(&touch.bHideStickBackground, co->T("Hide touch analog stick background circle")));
Expand Down
8 changes: 4 additions & 4 deletions UI/GamepadEmu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ void PSPStick::Draw(UIContext &dc) {
__CtrlPeekAnalog(stick_, &dx, &dy);
rotateTouchHelper(dx, dy);

const TouchControlConfig &config = g_Config.GetTouchControlsConfig(g_display.GetDeviceOrientation());
const TouchControlConfig &config = g_Config.GetCurrentTouchControlsConfig(g_display.GetDeviceOrientation());

if (!config.bHideStickBackground)
dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER);
Expand All @@ -551,7 +551,7 @@ bool PSPStick::Touch(const TouchInput &input) {
return retval;
}
if (input.flags & TouchInputFlags::DOWN) {
const TouchControlConfig &config = g_Config.GetTouchControlsConfig(g_display.GetDeviceOrientation());
const TouchControlConfig &config = g_Config.GetCurrentTouchControlsConfig(g_display.GetDeviceOrientation());
float fac = 0.5f * (stick_ ? config.fRightStickHeadScale : config.fLeftStickHeadScale)-0.5f;
if (dragPointerId_ == -1 && bounds_.Expand(bounds_.w*fac, bounds_.h*fac).Contains(input.x, input.y)) {
if (g_Config.bAutoCenterTouchAnalog) {
Expand Down Expand Up @@ -642,7 +642,7 @@ void PSPCustomStick::Draw(UIContext &dc) {
dx = posX_;
dy = -posY_;

const TouchControlConfig &config = g_Config.GetTouchControlsConfig(g_display.GetDeviceOrientation());
const TouchControlConfig &config = g_Config.GetCurrentTouchControlsConfig(g_display.GetDeviceOrientation());
const float headScale = config.fRightStickHeadScale;
if (!config.bHideStickBackground)
dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER);
Expand All @@ -664,7 +664,7 @@ bool PSPCustomStick::Touch(const TouchInput &input) {
return false;
}
if (input.flags & TouchInputFlags::DOWN) {
const TouchControlConfig &config = g_Config.GetTouchControlsConfig(g_display.GetDeviceOrientation());
const TouchControlConfig &config = g_Config.GetCurrentTouchControlsConfig(g_display.GetDeviceOrientation());
float fac = 0.5f * config.fRightStickHeadScale - 0.5f;
if (dragPointerId_ == -1 && bounds_.Expand(bounds_.w*fac, bounds_.h*fac).Contains(input.x, input.y)) {
if (g_Config.bAutoCenterTouchAnalog) {
Expand Down
1 change: 1 addition & 0 deletions UI/GamepadEmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ namespace CustomKeyData {
{ ImageID::invalid(), VIRTKEY_TOGGLE_DEBUGGER },
{ ImageID::invalid(), VIRTKEY_PAUSE_NO_MENU },
{ ImageID::invalid(), VIRTKEY_TOGGLE_TILT },
{ ImageID::invalid(), VIRTKEY_SWAP_LAYOUT },
// IMPORTANT: Only add at the end!
};
static_assert(ARRAY_SIZE(g_customKeyList) <= 64, "Too many key for a uint64_t bit mask");
Expand Down
31 changes: 28 additions & 3 deletions UI/TouchControlLayoutScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ void ControlLayoutView::CreateViews() {
}

// Create all the subviews.
TouchControlConfig &touch = g_Config.GetTouchControlsConfig(deviceOrientation_);
TouchControlConfig &touch = g_Config.GetCurrentTouchControlsConfig(deviceOrientation_);

if (touch.bShowTouchCircle || touch.bShowTouchCross || touch.bShowTouchTriangle || touch.bShowTouchSquare) {
PSPActionButtons *actionButtons = new PSPActionButtons(touch.touchActionButtonCenter, "Action buttons", touch.fActionButtonSpacing, bounds);
Expand Down Expand Up @@ -592,7 +592,7 @@ void TouchControlLayoutScreen::OnReset(UI::EventParams &e) {

const Bounds &bounds = screenManager()->getUIContext()->GetBounds();
const DeviceOrientation orientation = GetDeviceOrientation();
TouchControlConfig &touch = g_Config.GetTouchControlsConfig(orientation);
TouchControlConfig &touch = g_Config.GetCurrentTouchControlsConfig(orientation);
touch.ResetLayout();
InitPadLayout(&touch, orientation, bounds.w, bounds.h);
RecreateViews();
Expand All @@ -609,6 +609,21 @@ void TouchControlLayoutScreen::OnMode(UI::EventParams &e) {
}
}

void TouchControlLayoutScreen::OnLayoutSelection(UI::EventParams &e) {
const DeviceOrientation orientation = GetDeviceOrientation();
int selection = e.a;

int newSelection = selection + 1; // Convert from 0-based to 1-based
// Update both runtime and persisted selection when changed through the editor.
g_Config.iTouchLayoutSelection = newSelection;
g_Config.iTouchLayoutSelectionSaved = newSelection;

// Reload the layout for the newly selected layout
const Bounds &bounds = screenManager()->getUIContext()->GetBounds();
InitPadLayout(&g_Config.GetCurrentTouchControlsConfig(orientation), orientation, bounds.w, bounds.h);
RecreateViews();
}

void TouchControlLayoutScreen::update() {
UIBaseDialogScreen::update();

Expand Down Expand Up @@ -637,7 +652,7 @@ void TouchControlLayoutScreen::CreateViews() {
// setup g_Config for button layout
const Bounds &bounds = screenManager()->getUIContext()->GetBounds();
const DeviceOrientation orientation = GetDeviceOrientation();
InitPadLayout(&g_Config.GetTouchControlsConfig(orientation), orientation, bounds.w, bounds.h);
InitPadLayout(&g_Config.GetCurrentTouchControlsConfig(orientation), orientation, bounds.w, bounds.h);

// const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait;

Expand All @@ -661,6 +676,13 @@ void TouchControlLayoutScreen::CreateViews() {
mode_->SetSelection(0, false);
mode_->OnChoice.Handle(this, &TouchControlLayoutScreen::OnMode);

// Layout selection for swap layout feature
auto layoutSelectionStrip = new ChoiceStrip(ORIENT_VERTICAL);
layoutSelectionStrip->AddChoice("Layout 1");
layoutSelectionStrip->AddChoice("Layout 2");
layoutSelectionStrip->SetSelection(g_Config.iTouchLayoutSelection - 1, false);
layoutSelectionStrip->OnChoice.Handle(this, &TouchControlLayoutScreen::OnLayoutSelection);

CheckBox *snap = new CheckBox(&g_Config.bTouchSnapToGrid, di->T("Snap"));
PopupSliderChoice *gridSize = new PopupSliderChoice(&g_Config.iTouchSnapGridSize, 2, 256, 64, di->T("Grid"), screenManager(), "");
gridSize->SetEnabledPtr(&g_Config.bTouchSnapToGrid);
Expand All @@ -669,6 +691,9 @@ void TouchControlLayoutScreen::CreateViews() {
leftColumn->Add(new Choice(co->T("Customize")))->OnClick.Add([this](UI::EventParams &e) {
screenManager()->push(new TouchControlVisibilityScreen(gamePath_));
});
leftColumn->Add(new Spacer(8.0f));
leftColumn->Add(new TextView(di->T("Layout:")))->SetTextSize(TextSize::Small);
leftColumn->Add(layoutSelectionStrip);
leftColumn->Add(snap);
leftColumn->Add(gridSize);
leftColumn->Add(new Choice(di->T("Reset")))->OnClick.Handle(this, &TouchControlLayoutScreen::OnReset);
Expand Down
2 changes: 2 additions & 0 deletions UI/TouchControlLayoutScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class TouchControlLayoutScreen : public UIBaseDialogScreen {
protected:
void OnReset(UI::EventParams &e);
void OnMode(UI::EventParams &e);
void OnLayoutSelection(UI::EventParams &e);


private:
UI::ChoiceStrip *mode_ = nullptr;
Expand Down
28 changes: 26 additions & 2 deletions UI/TouchControlVisibilityScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ void TouchControlVisibilityScreen::CreateDialogViews(UI::ViewGroup *parent) {
gridsettings.fillCells = true;
GridLayout *grid = parent->Add(new GridLayoutList(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));

TouchControlConfig &touch = g_Config.GetTouchControlsConfig(GetDeviceOrientation());
TouchControlConfig &touch = g_Config.GetCurrentTouchControlsConfig(GetDeviceOrientation());

toggles_.clear();
toggles_.push_back({ "Circle", &touch.bShowTouchCircle, ImageID("I_CIRCLE"), nullptr });
Expand Down Expand Up @@ -146,6 +146,30 @@ void TouchControlVisibilityScreen::CreateDialogViews(UI::ViewGroup *parent) {
}

void TouchControlVisibilityScreen::onFinish(DialogResult result) {
// Refresh current layout pointer and sync visibility settings
TouchControlConfig &touch = g_Config.GetCurrentTouchControlsConfig(GetDeviceOrientation());
for (size_t i = 0; i < toggles_.size(); i++) {
if (toggles_[i].show) {
bool val = *toggles_[i].show;
if (i == 0) touch.bShowTouchCircle = val;
else if (i == 1) touch.bShowTouchCross = val;
else if (i == 2) touch.bShowTouchSquare = val;
else if (i == 3) touch.bShowTouchTriangle = val;
else if (i == 4) touch.touchLKey.show = val;
else if (i == 5) touch.touchRKey.show = val;
else if (i == 6) touch.touchStartKey.show = val;
else if (i == 7) touch.touchSelectKey.show = val;
else if (i == 8) touch.touchDpad.show = val;
else if (i == 9) touch.touchAnalogStick.show = val;
else if (i == 10) touch.touchRightAnalogStick.show = val;
else if (i == 11) touch.touchFastForwardKey.show = val;
else if (i == 12) touch.touchPauseKey.show = val;
else if (i >= 13 && i < (size_t)(13 + TouchControlConfig::CUSTOM_BUTTON_COUNT)) {
int idx = i - 13;
touch.touchCustom[idx].show = val;
}
}
}
g_Config.Save("TouchControlVisibilityScreen::onFinish");
}

Expand All @@ -161,7 +185,7 @@ void RightAnalogMappingScreen::CreateDialogViews(UI::ViewGroup *parent) {
auto co = GetI18NCategory(I18NCat::CONTROLS);
auto mc = GetI18NCategory(I18NCat::MAPPABLECONTROLS);

TouchControlConfig &touch = g_Config.GetTouchControlsConfig(GetDeviceOrientation());
TouchControlConfig &touch = g_Config.GetCurrentTouchControlsConfig(GetDeviceOrientation());

static const char *rightAnalogButton[] = {"None", "L", "R", "Square", "Triangle", "Circle", "Cross", "D-pad up", "D-pad down", "D-pad left", "D-pad right", "Start", "Select", "RightAn.Up", "RightAn.Down", "RightAn.Left", "RightAn.Right", "An.Up", "An.Down", "An.Left", "An.Right"};

Expand Down