diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 6895bb704..9dca82b10 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -6,13 +6,24 @@
-
-
-
-
+
+
+ android:required="false"/>
+
+
+
+
+
@@ -20,7 +31,6 @@
-
-
+
@@ -72,7 +82,7 @@
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java
index 90d774f6c..5899d62a6 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java
@@ -38,6 +38,8 @@ import java.util.Objects;
import static android.Manifest.permission.CAMERA;
import static android.Manifest.permission.RECORD_AUDIO;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+
/**
* Class which contains methods that interact
* with the native side of the Citra code.
@@ -245,7 +247,7 @@ public final class NativeLibrary {
final String title = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("title"));
final String message = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("message"));
- return new AlertDialog.Builder(emulationActivity)
+ return new MaterialAlertDialogBuilder(emulationActivity)
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.continue_button, (dialog, which) -> {
@@ -345,7 +347,7 @@ public final class NativeLibrary {
} else {
// Create object used for waiting.
final Object lock = new Object();
- AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
+ MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity)
.setTitle(caption)
.setMessage(text);
@@ -427,7 +429,7 @@ public final class NativeLibrary {
return alertPromptResult;
}
- public static AlertDialog.Builder displayAlertPromptImpl(String caption, String text, int buttonConfig) {
+ public static MaterialAlertDialogBuilder displayAlertPromptImpl(String caption, String text, int buttonConfig) {
final EmulationActivity emulationActivity = sEmulationActivity.get();
alertPromptResult = "";
alertPromptButton = 0;
@@ -444,7 +446,7 @@ public final class NativeLibrary {
FrameLayout container = new FrameLayout(emulationActivity);
container.addView(alertPromptEditText);
- AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
+ MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity)
.setTitle(caption)
.setView(container)
.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
@@ -506,7 +508,7 @@ public final class NativeLibrary {
captionId = R.string.loader_error_encrypted;
}
- AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
+ MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity)
.setTitle(captionId)
.setMessage(Html.fromHtml("Please follow the guides to redump your game cartidges or installed titles.", Html.FROM_HTML_MODE_LEGACY))
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> emulationActivity.finish())
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.java b/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.java
index b5e547266..28659589e 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.java
@@ -18,13 +18,11 @@ import android.view.MotionEvent;
import android.view.SubMenu;
import android.view.View;
import android.widget.CheckBox;
-import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.PopupMenu;
import androidx.core.app.NotificationManagerCompat;
@@ -45,6 +43,7 @@ import org.citra.citra_emu.utils.EmulationMenuSettings;
import org.citra.citra_emu.utils.FileBrowserHelper;
import org.citra.citra_emu.utils.FileUtil;
import org.citra.citra_emu.utils.ForegroundService;
+import org.citra.citra_emu.utils.ThemeUtil;
import java.io.File;
import java.io.IOException;
@@ -56,6 +55,9 @@ import static android.Manifest.permission.CAMERA;
import static android.Manifest.permission.RECORD_AUDIO;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+import com.google.android.material.slider.Slider;
+
public final class EmulationActivity extends AppCompatActivity {
public static final String EXTRA_SELECTED_GAME = "SelectedGame";
public static final String EXTRA_SELECTED_TITLE = "SelectedTitle";
@@ -151,6 +153,8 @@ public final class EmulationActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
+ ThemeUtil.applyTheme(this);
+
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
@@ -179,8 +183,6 @@ public final class EmulationActivity extends AppCompatActivity {
// Set these options now so that the SurfaceView the game renders into is the right size.
enableFullscreenImmersive();
- setTheme(R.style.CitraEmulationBase);
-
setContentView(R.layout.activity_emulation);
// Find or create the EmulationFragment
@@ -243,7 +245,7 @@ public final class EmulationActivity extends AppCompatActivity {
case NativeLibrary.REQUEST_CODE_NATIVE_CAMERA:
if (grantResults[0] != PackageManager.PERMISSION_GRANTED &&
shouldShowRequestPermissionRationale(CAMERA)) {
- new AlertDialog.Builder(this)
+ new MaterialAlertDialogBuilder(this)
.setTitle(R.string.camera)
.setMessage(R.string.camera_permission_needed)
.setPositiveButton(android.R.string.ok, null)
@@ -254,7 +256,7 @@ public final class EmulationActivity extends AppCompatActivity {
case NativeLibrary.REQUEST_CODE_NATIVE_MIC:
if (grantResults[0] != PackageManager.PERMISSION_GRANTED &&
shouldShowRequestPermissionRationale(RECORD_AUDIO)) {
- new AlertDialog.Builder(this)
+ new MaterialAlertDialogBuilder(this)
.setTitle(R.string.microphone)
.setMessage(R.string.microphone_permission_needed)
.setPositiveButton(android.R.string.ok, null)
@@ -324,7 +326,7 @@ public final class EmulationActivity extends AppCompatActivity {
View view = inflater.inflate(R.layout.dialog_checkbox, null);
CheckBox checkBox = view.findViewById(R.id.checkBox);
- new AlertDialog.Builder(this)
+ new MaterialAlertDialogBuilder(this)
.setTitle(R.string.savestate_warning_title)
.setMessage(R.string.savestate_warning_message)
.setView(view)
@@ -464,8 +466,8 @@ public final class EmulationActivity extends AppCompatActivity {
case MENU_ACTION_LOAD_AMIIBO:
FileBrowserHelper.openFilePicker(this, REQUEST_SELECT_AMIIBO,
- R.string.select_amiibo,
- Collections.singletonList("bin"), false);
+ R.string.select_amiibo,
+ Collections.singletonList("bin"), false);
break;
case MENU_ACTION_REMOVE_AMIIBO:
@@ -490,7 +492,7 @@ public final class EmulationActivity extends AppCompatActivity {
case MENU_ACTION_CLOSE_GAME:
NativeLibrary.PauseEmulation();
- new AlertDialog.Builder(this)
+ new MaterialAlertDialogBuilder(this)
.setTitle(R.string.emulation_close_game)
.setMessage(R.string.emulation_close_game_message)
.setPositiveButton(android.R.string.yes, (dialogInterface, i) ->
@@ -498,11 +500,8 @@ public final class EmulationActivity extends AppCompatActivity {
mEmulationFragment.stopEmulation();
finish();
})
- .setNegativeButton(android.R.string.cancel, (dialogInterface, i) ->
- NativeLibrary.UnPauseEmulation())
- .setOnCancelListener(dialogInterface ->
- NativeLibrary.UnPauseEmulation())
- .create()
+ .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> NativeLibrary.UnPauseEmulation())
+ .setOnCancelListener(dialogInterface -> NativeLibrary.UnPauseEmulation())
.show();
break;
}
@@ -589,11 +588,10 @@ public final class EmulationActivity extends AppCompatActivity {
}
if (!success) {
- new AlertDialog.Builder(this)
+ new MaterialAlertDialogBuilder(this)
.setTitle(R.string.amiibo_load_error)
.setMessage(R.string.amiibo_load_error_message)
.setPositiveButton(android.R.string.ok, null)
- .create()
.show();
}
}
@@ -605,8 +603,6 @@ public final class EmulationActivity extends AppCompatActivity {
private void toggleControls() {
final SharedPreferences.Editor editor = mPreferences.edit();
boolean[] enabledButtons = new boolean[14];
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.emulation_toggle_controls);
for (int i = 0; i < enabledButtons.length; i++) {
// Buttons that are disabled by default
@@ -621,63 +617,47 @@ public final class EmulationActivity extends AppCompatActivity {
enabledButtons[i] = mPreferences.getBoolean("buttonToggle" + i, defaultValue);
}
- builder.setMultiChoiceItems(R.array.n3dsButtons, enabledButtons,
- (dialog, indexSelected, isChecked) -> editor
- .putBoolean("buttonToggle" + indexSelected, isChecked));
- builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
- {
- editor.apply();
- mEmulationFragment.refreshInputOverlay();
- });
-
- AlertDialog alertDialog = builder.create();
- alertDialog.show();
+ new MaterialAlertDialogBuilder(this)
+ .setTitle(R.string.emulation_toggle_controls)
+ .setMultiChoiceItems(R.array.n3dsButtons, enabledButtons,
+ (dialog, indexSelected, isChecked) -> editor
+ .putBoolean("buttonToggle" + indexSelected, isChecked))
+ .setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
+ {
+ editor.apply();
+ mEmulationFragment.refreshInputOverlay();
+ })
+ .show();
}
private void adjustScale() {
LayoutInflater inflater = LayoutInflater.from(this);
- View view = inflater.inflate(R.layout.dialog_seekbar, null);
+ View view = inflater.inflate(R.layout.dialog_slider, null);
- final SeekBar seekbar = view.findViewById(R.id.seekbar);
- final TextView value = view.findViewById(R.id.text_value);
+ final Slider slider = view.findViewById(R.id.slider);
+ final TextView textValue = view.findViewById(R.id.text_value);
final TextView units = view.findViewById(R.id.text_units);
- seekbar.setMax(150);
- seekbar.setProgress(mPreferences.getInt("controlScale", 50));
- seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- value.setText(String.valueOf(progress + 50));
- }
-
- public void onStopTrackingTouch(SeekBar seekBar) {
- setControlScale(seekbar.getProgress());
- }
+ slider.setValueTo(150);
+ slider.setValue(mPreferences.getInt("controlScale", 50));
+ slider.addOnChangeListener((slider1, progress, fromUser) -> {
+ textValue.setText(String.valueOf((int) progress + 50));
+ setControlScale((int) slider1.getValue());
});
- value.setText(String.valueOf(seekbar.getProgress() + 50));
+ textValue.setText(String.valueOf((int) slider.getValue() + 50));
units.setText("%");
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.emulation_control_scale);
- builder.setView(view);
- final int previousProgress = seekbar.getProgress();
- builder.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {
- setControlScale(previousProgress);
- });
- builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
- {
- setControlScale(seekbar.getProgress());
- });
- builder.setNeutralButton(R.string.slider_default, (dialogInterface, i) -> {
- setControlScale(50);
- });
+ final int previousProgress = (int) slider.getValue();
- AlertDialog alertDialog = builder.create();
- alertDialog.show();
+ new MaterialAlertDialogBuilder(this)
+ .setTitle(R.string.emulation_control_scale)
+ .setView(view)
+ .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> setControlScale(previousProgress))
+ .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> setControlScale((int) slider.getValue()))
+ .setNeutralButton(R.string.slider_default, (dialogInterface, i) -> setControlScale(50))
+ .show();
}
private void setControlScale(int scale) {
@@ -688,12 +668,10 @@ public final class EmulationActivity extends AppCompatActivity {
}
private void resetOverlay() {
- new AlertDialog.Builder(this)
+ new MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.emulation_touch_overlay_reset))
.setPositiveButton(android.R.string.yes, (dialogInterface, i) -> mEmulationFragment.resetInputOverlay())
- .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {
- })
- .create()
+ .setNegativeButton(android.R.string.cancel, null)
.show();
}
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java b/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java
index bc791638a..ca66c3b9b 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java
@@ -2,8 +2,6 @@ package org.citra.citra_emu.adapters;
import android.database.Cursor;
import android.database.DataSetObserver;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.SystemClock;
import android.view.LayoutInflater;
@@ -12,14 +10,14 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
-import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.RecyclerView;
+import com.google.android.material.color.MaterialColors;
+
import org.citra.citra_emu.R;
import org.citra.citra_emu.activities.EmulationActivity;
import org.citra.citra_emu.model.GameDatabase;
-import org.citra.citra_emu.ui.DividerItemDecoration;
import org.citra.citra_emu.utils.Log;
import org.citra.citra_emu.utils.PicassoUtils;
import org.citra.citra_emu.viewholders.GameViewHolder;
@@ -99,9 +97,9 @@ public final class GameAdapter extends RecyclerView.Adapter impl
holder.regions = mCursor.getString(GameDatabase.GAME_COLUMN_REGIONS);
holder.company = mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY);
- final int backgroundColorId = isValidGame(holder.path) ? R.color.card_view_background : R.color.card_view_disabled;
+ final int backgroundColorId = isValidGame(holder.path) ? R.attr.colorSurface : R.attr.colorErrorContainer;
View itemView = holder.getItemView();
- itemView.setBackgroundColor(ContextCompat.getColor(itemView.getContext(), backgroundColorId));
+ itemView.setBackgroundColor(MaterialColors.getColor(itemView, backgroundColorId));
} else {
Log.error("[GameAdapter] Can't bind view; Cursor is not valid.");
}
@@ -204,24 +202,6 @@ public final class GameAdapter extends RecyclerView.Adapter impl
EmulationActivity.launch((FragmentActivity) view.getContext(), holder.path, holder.title);
}
- public static class SpacesItemDecoration extends DividerItemDecoration {
- private int space;
-
- public SpacesItemDecoration(Drawable divider, int space) {
- super(divider);
- this.space = space;
- }
-
- @Override
- public void getItemOffsets(Rect outRect, @NonNull View view, @NonNull RecyclerView parent,
- @NonNull RecyclerView.State state) {
- outRect.left = 0;
- outRect.right = 0;
- outRect.bottom = space;
- outRect.top = 0;
- }
- }
-
private boolean isValidGame(String path) {
return Stream.of(
".rar", ".zip", ".7z", ".torrent", ".tar", ".gz").noneMatch(suffix -> path.toLowerCase().endsWith(suffix));
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/applets/MiiSelector.java b/src/android/app/src/main/java/org/citra/citra_emu/applets/MiiSelector.java
index 85b55b00d..d6fce6a30 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/applets/MiiSelector.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/applets/MiiSelector.java
@@ -22,6 +22,8 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+
public final class MiiSelector {
public static class MiiSelectorConfig implements java.io.Serializable {
public boolean enable_cancel_button;
@@ -69,8 +71,8 @@ public final class MiiSelector {
? (int) config.initially_selected_mii_index
: 0;
data.index = initialIndex;
- AlertDialog.Builder builder =
- new AlertDialog.Builder(emulationActivity)
+ MaterialAlertDialogBuilder builder =
+ new MaterialAlertDialogBuilder(emulationActivity)
.setTitle(config.title.isEmpty()
? emulationActivity.getString(R.string.mii_selector)
: config.title)
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/applets/SoftwareKeyboard.java b/src/android/app/src/main/java/org/citra/citra_emu/applets/SoftwareKeyboard.java
index 7be5f6d97..800ecaf86 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/applets/SoftwareKeyboard.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/applets/SoftwareKeyboard.java
@@ -19,6 +19,8 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+
import org.citra.citra_emu.CitraApplication;
import org.citra.citra_emu.NativeLibrary;
import org.citra.citra_emu.R;
@@ -124,7 +126,7 @@ public final class SoftwareKeyboard {
FrameLayout container = new FrameLayout(emulationActivity);
container.addView(editText);
- AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
+ MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity)
.setTitle(R.string.software_keyboard)
.setView(container);
setCancelable(false);
@@ -227,7 +229,7 @@ public final class SoftwareKeyboard {
break;
}
- new AlertDialog.Builder(emulationActivity)
+ new MaterialAlertDialogBuilder(emulationActivity)
.setTitle(R.string.software_keyboard)
.setMessage(message)
.setPositiveButton(android.R.string.ok, null)
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress.java b/src/android/app/src/main/java/org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress.java
index e71c2cfc1..1e3111739 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress.java
@@ -8,7 +8,6 @@ import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
-import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ProgressBar;
@@ -18,6 +17,8 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+
import org.citra.citra_emu.NativeLibrary;
import org.citra.citra_emu.R;
import org.citra.citra_emu.activities.EmulationActivity;
@@ -70,22 +71,15 @@ public class DiskShaderCacheProgress {
setCancelable(false);
setRetainInstance(true);
- AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity);
- builder.setTitle(title);
- builder.setMessage(message);
- builder.setView(view);
- builder.setNegativeButton(android.R.string.cancel, null);
-
- dialog = builder.create();
- dialog.create();
-
- dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener((v) -> emulationActivity.onBackPressed());
-
synchronized (finishLock) {
finishLock.notifyAll();
}
- return dialog;
+ return new MaterialAlertDialogBuilder(emulationActivity)
+ .setTitle(title)
+ .setMessage(message)
+ .setNegativeButton(android.R.string.cancel, (dialog, which) -> emulationActivity.onBackPressed())
+ .create();
}
private void onUpdateProgress(String msg, int progress, int max) {
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java
index 762cdb80e..f4833c0c5 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java
@@ -15,6 +15,8 @@ import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+
import org.citra.citra_emu.R;
import org.citra.citra_emu.features.cheats.model.Cheat;
import org.citra.citra_emu.features.cheats.model.CheatsViewModel;
@@ -80,12 +82,12 @@ public class CheatDetailsFragment extends Fragment {
private void onDeleteClicked(View view) {
String name = mEditName.getText().toString();
- AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
- builder.setMessage(getString(R.string.cheats_delete_confirmation, name));
- builder.setPositiveButton(android.R.string.yes,
- (dialog, i) -> mViewModel.deleteSelectedCheat());
- builder.setNegativeButton(android.R.string.no, null);
- builder.show();
+ new MaterialAlertDialogBuilder(requireContext())
+ .setMessage(getString(R.string.cheats_delete_confirmation, name))
+ .setPositiveButton(android.R.string.yes,
+ (dialog, i) -> mViewModel.deleteSelectedCheat())
+ .setNegativeButton(android.R.string.no, null)
+ .show();
}
private void onEditClicked(View view) {
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java
index a36bf427c..a6ab89429 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java
@@ -14,10 +14,13 @@ import androidx.core.view.ViewCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.slidingpanelayout.widget.SlidingPaneLayout;
+import com.google.android.material.appbar.MaterialToolbar;
+
import org.citra.citra_emu.R;
import org.citra.citra_emu.features.cheats.model.Cheat;
import org.citra.citra_emu.features.cheats.model.CheatsViewModel;
import org.citra.citra_emu.ui.TwoPaneOnBackPressedCallback;
+import org.citra.citra_emu.utils.ThemeUtil;
public class CheatsActivity extends AppCompatActivity
implements SlidingPaneLayout.PanelSlideListener {
@@ -37,6 +40,8 @@ public class CheatsActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
+ ThemeUtil.applyTheme(this);
+
super.onCreate(savedInstanceState);
mViewModel = new ViewModelProvider(this).get(CheatsViewModel.class);
@@ -63,6 +68,8 @@ public class CheatsActivity extends AppCompatActivity
mViewModel.getOpenDetailsViewEvent().observe(this, this::openDetailsView);
// Show "Up" button in the action bar for navigation
+ MaterialToolbar toolbar = findViewById(R.id.toolbar_cheats);
+ setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java
index 23c3c4c9e..cfbcf5099 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java
@@ -15,11 +15,14 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentTransaction;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import com.google.android.material.appbar.MaterialToolbar;
+
import org.citra.citra_emu.NativeLibrary;
import org.citra.citra_emu.R;
import org.citra.citra_emu.utils.DirectoryInitialization;
import org.citra.citra_emu.utils.DirectoryStateReceiver;
import org.citra.citra_emu.utils.EmulationMenuSettings;
+import org.citra.citra_emu.utils.ThemeUtil;
public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView {
private static final String ARG_MENU_TAG = "menu_tag";
@@ -38,6 +41,8 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
@Override
protected void onCreate(Bundle savedInstanceState) {
+ ThemeUtil.applyTheme(this);
+
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
@@ -49,6 +54,8 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
mPresenter.onCreate(savedInstanceState, menuTag, gameID);
// Show "Back" button in the action bar for navigation
+ MaterialToolbar toolbar = findViewById(R.id.toolbar_settings);
+ setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@@ -107,10 +114,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
if (addToStack) {
if (areSystemAnimationsEnabled()) {
transaction.setCustomAnimations(
- R.animator.settings_enter,
- R.animator.settings_exit,
- R.animator.settings_pop_enter,
- R.animator.setttings_pop_exit);
+ R.anim.anim_settings_fragment_in,
+ R.anim.anim_settings_fragment_out,
+ 0,
+ R.anim.anim_pop_settings_fragment_out);
}
transaction.addToBackStack(null);
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.java
index 0d63873bb..e288bf934 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.java
@@ -4,6 +4,8 @@ import android.content.IntentFilter;
import android.os.Bundle;
import android.text.TextUtils;
+import androidx.appcompat.app.AppCompatActivity;
+
import org.citra.citra_emu.NativeLibrary;
import org.citra.citra_emu.features.settings.model.Settings;
import org.citra.citra_emu.features.settings.utils.SettingsFile;
@@ -109,8 +111,6 @@ public final class SettingsActivityPresenter {
mSettings.saveSettings(mView);
}
- ThemeUtil.applyTheme();
-
NativeLibrary.ReloadSettings();
}
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.java
index bfd7c71a9..59c37394e 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.java
@@ -6,13 +6,16 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.DatePicker;
-import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.TimePicker;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.RecyclerView;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+import com.google.android.material.slider.Slider;
+
import org.citra.citra_emu.R;
import org.citra.citra_emu.dialogs.MotionAlertDialog;
import org.citra.citra_emu.features.settings.model.FloatSetting;
@@ -41,15 +44,14 @@ import org.citra.citra_emu.utils.Log;
import java.util.ArrayList;
-public final class SettingsAdapter extends RecyclerView.Adapter
- implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener {
+public final class SettingsAdapter extends RecyclerView.Adapter implements DialogInterface.OnClickListener, Slider.OnChangeListener {
private SettingsFragmentView mView;
private Context mContext;
private ArrayList mSettings;
private SettingsItem mClickedItem;
private int mClickedPosition;
- private int mSeekbarProgress;
+ private int mSliderProgress;
private AlertDialog mDialog;
private TextView mTextSliderValue;
@@ -149,11 +151,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter {
- seekbar.setProgress(item.getDefaultValue());
- onClick(dialog, which);
- });
+ MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(mView.getActivity())
+ .setTitle(item.getNameId())
+ .setView(view)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, defaultCancelListener)
+ .setNeutralButton(R.string.slider_default, (DialogInterface dialog, int which) -> {
+ slider.setValue(item.getDefaultValue());
+ onClick(dialog, which);
+ });
mDialog = builder.show();
mTextSliderValue = view.findViewById(R.id.text_value);
- mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
+ mTextSliderValue.setText(String.valueOf(mSliderProgress));
TextView units = view.findViewById(R.id.text_units);
units.setText(item.getUnits());
- seekbar.setMin(item.getMin());
- seekbar.setMax(item.getMax());
- seekbar.setProgress(mSeekbarProgress);
+ slider.setValueFrom(item.getMin());
+ slider.setValueTo(item.getMax());
+ slider.setValue(mSliderProgress);
- seekbar.setOnSeekBarChangeListener(this);
+ slider.addOnChangeListener(this);
}
public void onSubmenuClick(SubmenuSetting item) {
@@ -375,19 +370,19 @@ public final class SettingsAdapter extends RecyclerView.Adapter
+
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml b/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml
new file mode 100644
index 000000000..82fd719db
--- /dev/null
+++ b/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml b/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml
new file mode 100644
index 000000000..5892128f1
--- /dev/null
+++ b/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml b/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml
new file mode 100644
index 000000000..98e0cf8bd
--- /dev/null
+++ b/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml b/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml
new file mode 100644
index 000000000..77a40a4d1
--- /dev/null
+++ b/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/animator/settings_enter.xml b/src/android/app/src/main/res/animator/settings_enter.xml
deleted file mode 100644
index 3c216a054..000000000
--- a/src/android/app/src/main/res/animator/settings_enter.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/android/app/src/main/res/animator/settings_exit.xml b/src/android/app/src/main/res/animator/settings_exit.xml
deleted file mode 100644
index a233b6757..000000000
--- a/src/android/app/src/main/res/animator/settings_exit.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/android/app/src/main/res/animator/settings_pop_enter.xml b/src/android/app/src/main/res/animator/settings_pop_enter.xml
deleted file mode 100644
index 080bc27c4..000000000
--- a/src/android/app/src/main/res/animator/settings_pop_enter.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/android/app/src/main/res/animator/setttings_pop_exit.xml b/src/android/app/src/main/res/animator/setttings_pop_exit.xml
deleted file mode 100644
index 4fccbcca2..000000000
--- a/src/android/app/src/main/res/animator/setttings_pop_exit.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable-hdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-hdpi/ic_cia_install.png
deleted file mode 100644
index 8c00d8c34..000000000
Binary files a/src/android/app/src/main/res/drawable-hdpi/ic_cia_install.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-hdpi/ic_folder.png b/src/android/app/src/main/res/drawable-hdpi/ic_folder.png
deleted file mode 100644
index 90085252b..000000000
Binary files a/src/android/app/src/main/res/drawable-hdpi/ic_folder.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-hdpi/ic_premium.png b/src/android/app/src/main/res/drawable-hdpi/ic_premium.png
deleted file mode 100644
index 7dd45a405..000000000
Binary files a/src/android/app/src/main/res/drawable-hdpi/ic_premium.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-hdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-hdpi/ic_settings_core.png
deleted file mode 100644
index 2e7837020..000000000
Binary files a/src/android/app/src/main/res/drawable-hdpi/ic_settings_core.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-mdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-mdpi/ic_cia_install.png
deleted file mode 100644
index c6dc232b4..000000000
Binary files a/src/android/app/src/main/res/drawable-mdpi/ic_cia_install.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-mdpi/ic_folder.png b/src/android/app/src/main/res/drawable-mdpi/ic_folder.png
deleted file mode 100644
index 1e428dfe3..000000000
Binary files a/src/android/app/src/main/res/drawable-mdpi/ic_folder.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-mdpi/ic_premium.png b/src/android/app/src/main/res/drawable-mdpi/ic_premium.png
deleted file mode 100644
index 4dfb62596..000000000
Binary files a/src/android/app/src/main/res/drawable-mdpi/ic_premium.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-hdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-hdpi/ic_cia_install.png
deleted file mode 100644
index cc986c8ac..000000000
Binary files a/src/android/app/src/main/res/drawable-night-hdpi/ic_cia_install.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-hdpi/ic_folder.png b/src/android/app/src/main/res/drawable-night-hdpi/ic_folder.png
deleted file mode 100644
index ee688b09f..000000000
Binary files a/src/android/app/src/main/res/drawable-night-hdpi/ic_folder.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-hdpi/ic_premium.png b/src/android/app/src/main/res/drawable-night-hdpi/ic_premium.png
deleted file mode 100644
index 6b678d22c..000000000
Binary files a/src/android/app/src/main/res/drawable-night-hdpi/ic_premium.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-hdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-night-hdpi/ic_settings_core.png
deleted file mode 100644
index bc9dc0beb..000000000
Binary files a/src/android/app/src/main/res/drawable-night-hdpi/ic_settings_core.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-mdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-mdpi/ic_cia_install.png
deleted file mode 100644
index f61d84961..000000000
Binary files a/src/android/app/src/main/res/drawable-night-mdpi/ic_cia_install.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-mdpi/ic_folder.png b/src/android/app/src/main/res/drawable-night-mdpi/ic_folder.png
deleted file mode 100644
index 05847c34b..000000000
Binary files a/src/android/app/src/main/res/drawable-night-mdpi/ic_folder.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-mdpi/ic_premium.png b/src/android/app/src/main/res/drawable-night-mdpi/ic_premium.png
deleted file mode 100644
index 87bac27df..000000000
Binary files a/src/android/app/src/main/res/drawable-night-mdpi/ic_premium.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-xhdpi/ic_cia_install.png
deleted file mode 100644
index 1eccbe68d..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xhdpi/ic_cia_install.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xhdpi/ic_folder.png b/src/android/app/src/main/res/drawable-night-xhdpi/ic_folder.png
deleted file mode 100644
index ffa1d200e..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xhdpi/ic_folder.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-night-xhdpi/ic_premium.png
deleted file mode 100644
index 23a5cec51..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xhdpi/ic_premium.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xhdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-night-xhdpi/ic_settings_core.png
deleted file mode 100644
index 9ca7975bb..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xhdpi/ic_settings_core.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-xxhdpi/ic_cia_install.png
deleted file mode 100644
index fc3c434b0..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_cia_install.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_folder.png b/src/android/app/src/main/res/drawable-night-xxhdpi/ic_folder.png
deleted file mode 100644
index 013600d1f..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_folder.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-night-xxhdpi/ic_premium.png
deleted file mode 100644
index 2a0f1568f..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_premium.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-night-xxhdpi/ic_settings_core.png
deleted file mode 100644
index 23706188b..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_settings_core.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_cia_install.png
deleted file mode 100644
index b4d1b92b7..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_cia_install.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_folder.png b/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_folder.png
deleted file mode 100644
index 166bd052d..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_folder.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_premium.png
deleted file mode 100644
index 8d357b228..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_premium.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_settings_core.png
deleted file mode 100644
index 32eb4faff..000000000
Binary files a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_settings_core.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-xhdpi/ic_cia_install.png
deleted file mode 100644
index 839869401..000000000
Binary files a/src/android/app/src/main/res/drawable-xhdpi/ic_cia_install.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xhdpi/ic_folder.png b/src/android/app/src/main/res/drawable-xhdpi/ic_folder.png
deleted file mode 100644
index 02bc3d75a..000000000
Binary files a/src/android/app/src/main/res/drawable-xhdpi/ic_folder.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-xhdpi/ic_premium.png
deleted file mode 100644
index ac4b19ff4..000000000
Binary files a/src/android/app/src/main/res/drawable-xhdpi/ic_premium.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xhdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-xhdpi/ic_settings_core.png
deleted file mode 100644
index 8cff45f84..000000000
Binary files a/src/android/app/src/main/res/drawable-xhdpi/ic_settings_core.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-xxhdpi/ic_cia_install.png
deleted file mode 100644
index e6812f0d4..000000000
Binary files a/src/android/app/src/main/res/drawable-xxhdpi/ic_cia_install.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xxhdpi/ic_folder.png b/src/android/app/src/main/res/drawable-xxhdpi/ic_folder.png
deleted file mode 100644
index 05f429614..000000000
Binary files a/src/android/app/src/main/res/drawable-xxhdpi/ic_folder.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xxhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-xxhdpi/ic_premium.png
deleted file mode 100644
index 63f162e52..000000000
Binary files a/src/android/app/src/main/res/drawable-xxhdpi/ic_premium.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xxhdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-xxhdpi/ic_settings_core.png
deleted file mode 100644
index 0b9049f46..000000000
Binary files a/src/android/app/src/main/res/drawable-xxhdpi/ic_settings_core.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xxxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-xxxhdpi/ic_cia_install.png
deleted file mode 100644
index 69ae32dc3..000000000
Binary files a/src/android/app/src/main/res/drawable-xxxhdpi/ic_cia_install.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xxxhdpi/ic_folder.png b/src/android/app/src/main/res/drawable-xxxhdpi/ic_folder.png
deleted file mode 100644
index c85074c60..000000000
Binary files a/src/android/app/src/main/res/drawable-xxxhdpi/ic_folder.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xxxhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-xxxhdpi/ic_premium.png
deleted file mode 100644
index 6f1550a10..000000000
Binary files a/src/android/app/src/main/res/drawable-xxxhdpi/ic_premium.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable-xxxhdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-xxxhdpi/ic_settings_core.png
deleted file mode 100644
index 2827a1777..000000000
Binary files a/src/android/app/src/main/res/drawable-xxxhdpi/ic_settings_core.png and /dev/null differ
diff --git a/src/android/app/src/main/res/drawable/gamelist_divider.xml b/src/android/app/src/main/res/drawable/gamelist_divider.xml
index 7da9dccce..a1cec8e41 100644
--- a/src/android/app/src/main/res/drawable/gamelist_divider.xml
+++ b/src/android/app/src/main/res/drawable/gamelist_divider.xml
@@ -6,6 +6,6 @@
android:width="1dp"
android:height="1dp" />
-
+
diff --git a/src/android/app/src/main/res/drawable/ic_add.xml b/src/android/app/src/main/res/drawable/ic_add.xml
index bdd99f48d..64360e8d9 100644
--- a/src/android/app/src/main/res/drawable/ic_add.xml
+++ b/src/android/app/src/main/res/drawable/ic_add.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/src/android/app/src/main/res/drawable/ic_back.xml b/src/android/app/src/main/res/drawable/ic_back.xml
new file mode 100644
index 000000000..f99fea719
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_back.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/android/app/src/main/res/drawable/ic_folder.xml b/src/android/app/src/main/res/drawable/ic_folder.xml
new file mode 100644
index 000000000..17d6c7927
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_folder.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/android/app/src/main/res/drawable/ic_heart.xml b/src/android/app/src/main/res/drawable/ic_heart.xml
new file mode 100644
index 000000000..6845a40e7
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_heart.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/android/app/src/main/res/drawable/ic_install.xml b/src/android/app/src/main/res/drawable/ic_install.xml
new file mode 100644
index 000000000..fe58acf84
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_install.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/android/app/src/main/res/drawable/ic_settings.xml b/src/android/app/src/main/res/drawable/ic_settings.xml
new file mode 100644
index 000000000..12ce6e231
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_settings.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/android/app/src/main/res/drawable/popup_background.xml b/src/android/app/src/main/res/drawable/popup_background.xml
new file mode 100644
index 000000000..057f81c83
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/popup_background.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/android/app/src/main/res/layout-ldrtl/list_item_cheat.xml b/src/android/app/src/main/res/layout-ldrtl/list_item_cheat.xml
index 9bcf883e1..ec9942cc5 100644
--- a/src/android/app/src/main/res/layout-ldrtl/list_item_cheat.xml
+++ b/src/android/app/src/main/res/layout-ldrtl/list_item_cheat.xml
@@ -13,7 +13,6 @@
android:id="@+id/text_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:textColor="@color/header_text"
android:textSize="16sp"
android:layout_margin="@dimen/spacing_large"
style="@style/TextAppearance.AppCompat.Headline"
diff --git a/src/android/app/src/main/res/layout/activity_cheats.xml b/src/android/app/src/main/res/layout/activity_cheats.xml
index b9414ab6d..0b6204b6d 100644
--- a/src/android/app/src/main/res/layout/activity_cheats.xml
+++ b/src/android/app/src/main/res/layout/activity_cheats.xml
@@ -1,22 +1,58 @@
-
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/layout/activity_main.xml b/src/android/app/src/main/res/layout/activity_main.xml
index cea0922a7..1f737c321 100644
--- a/src/android/app/src/main/res/layout/activity_main.xml
+++ b/src/android/app/src/main/res/layout/activity_main.xml
@@ -1,27 +1,30 @@
-
+
+
+
+
+
+
-
-
-
-
-
-
diff --git a/src/android/app/src/main/res/layout/activity_settings.xml b/src/android/app/src/main/res/layout/activity_settings.xml
index 11b91c45f..9da84faea 100644
--- a/src/android/app/src/main/res/layout/activity_settings.xml
+++ b/src/android/app/src/main/res/layout/activity_settings.xml
@@ -1,5 +1,27 @@
-
-
+ android:layout_height="match_parent">
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/layout/card_game.xml b/src/android/app/src/main/res/layout/card_game.xml
index 6e87490f9..84ca54abb 100644
--- a/src/android/app/src/main/res/layout/card_game.xml
+++ b/src/android/app/src/main/res/layout/card_game.xml
@@ -36,15 +36,13 @@
android:layout_marginStart="8dp"
android:baselineAligned="false"
android:ellipsize="end"
- android:gravity="center_vertical"
android:lines="1"
android:maxLines="1"
android:textAlignment="viewStart"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/image_game_screen"
app:layout_constraintTop_toTopOf="parent"
- tools:text="The Legend of Zelda\nOcarina of Time 3D"
- android:textColor="@color/header_text" />
+ tools:text="The Legend of Zelda\nOcarina of Time 3D" />
+ tools:text="Nintendo" />
+ tools:text="Pilotwings_Resort.cxi" />
-
diff --git a/src/android/app/src/main/res/layout/dialog_seekbar.xml b/src/android/app/src/main/res/layout/dialog_slider.xml
similarity index 93%
rename from src/android/app/src/main/res/layout/dialog_seekbar.xml
rename to src/android/app/src/main/res/layout/dialog_slider.xml
index 35abecfcb..59719e013 100644
--- a/src/android/app/src/main/res/layout/dialog_seekbar.xml
+++ b/src/android/app/src/main/res/layout/dialog_slider.xml
@@ -5,8 +5,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
-
\ No newline at end of file
+
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml
index d6e47e1e4..bd64d5d16 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -40,7 +40,6 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="@dimen/spacing_small"
- android:background="@color/citra_orange"
android:text="@string/emulation_done"
android:visibility="gone" />
diff --git a/src/android/app/src/main/res/layout/fragment_grid.xml b/src/android/app/src/main/res/layout/fragment_grid.xml
index f5b6c2e19..5978bf998 100644
--- a/src/android/app/src/main/res/layout/fragment_grid.xml
+++ b/src/android/app/src/main/res/layout/fragment_grid.xml
@@ -17,17 +17,19 @@
android:id="@+id/gamelist_empty_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:gravity="center"
android:text="@string/empty_gamelist"
- android:visibility="gone"
android:textSize="18sp"
- android:gravity="center" />
+ android:visibility="gone" />
+
-
\ No newline at end of file
+
+
diff --git a/src/android/app/src/main/res/layout/fragment_settings.xml b/src/android/app/src/main/res/layout/fragment_settings.xml
index 4c5d597c1..f8724552c 100644
--- a/src/android/app/src/main/res/layout/fragment_settings.xml
+++ b/src/android/app/src/main/res/layout/fragment_settings.xml
@@ -1,12 +1,13 @@
-
+ android:layout_height="match_parent"
+ android:background="?attr/colorSurface">
+ android:layout_height="match_parent" />
-
\ No newline at end of file
+
diff --git a/src/android/app/src/main/res/layout/list_item_cheat.xml b/src/android/app/src/main/res/layout/list_item_cheat.xml
index c0b5f982f..d31ae63f9 100644
--- a/src/android/app/src/main/res/layout/list_item_cheat.xml
+++ b/src/android/app/src/main/res/layout/list_item_cheat.xml
@@ -13,7 +13,6 @@
android:id="@+id/text_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:textColor="@color/header_text"
android:textSize="16sp"
android:layout_margin="@dimen/spacing_large"
style="@style/TextAppearance.AppCompat.Headline"
diff --git a/src/android/app/src/main/res/layout/list_item_setting.xml b/src/android/app/src/main/res/layout/list_item_setting.xml
index df83684f7..a2a07b381 100644
--- a/src/android/app/src/main/res/layout/list_item_setting.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting.xml
@@ -21,7 +21,6 @@
android:layout_alignParentEnd="true"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginEnd="@dimen/spacing_large"
- android:textColor="@color/header_text"
android:textSize="16sp"
tools:text="Setting Name" />
@@ -37,7 +36,6 @@
android:layout_marginTop="@dimen/spacing_small"
android:layout_marginEnd="@dimen/spacing_large"
android:visibility="visible"
- tools:text="@string/app_disclaimer"
- android:textColor="@color/header_subtext" />
+ tools:text="@string/app_disclaimer" />
\ No newline at end of file
diff --git a/src/android/app/src/main/res/layout/list_item_setting_checkbox.xml b/src/android/app/src/main/res/layout/list_item_setting_checkbox.xml
index 86ba83f11..332b2e08f 100644
--- a/src/android/app/src/main/res/layout/list_item_setting_checkbox.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting_checkbox.xml
@@ -19,7 +19,6 @@
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginTop="@dimen/spacing_large"
android:layout_toStartOf="@+id/checkbox"
- android:textColor="@color/header_text"
android:textSize="16sp"
tools:text="@string/frame_limit_enable" />
@@ -36,7 +35,6 @@
android:layout_marginTop="@dimen/spacing_small"
android:layout_toStartOf="@+id/checkbox"
android:textAlignment="textStart"
- android:textColor="@color/header_subtext"
tools:text="@string/frame_limit_enable_description" />
-
\ No newline at end of file
+
diff --git a/src/android/app/src/main/res/layout/premium_item_setting.xml b/src/android/app/src/main/res/layout/premium_item_setting.xml
index 17d5a13b2..1a606a458 100644
--- a/src/android/app/src/main/res/layout/premium_item_setting.xml
+++ b/src/android/app/src/main/res/layout/premium_item_setting.xml
@@ -21,7 +21,7 @@
android:layout_alignParentEnd="true"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginEnd="@dimen/spacing_large"
- android:textColor="?android:colorAccent"
+ android:textColor="?attr/colorPrimary"
android:textStyle="bold"
tools:text="Setting Name" />
@@ -37,7 +37,6 @@
android:layout_marginTop="@dimen/spacing_small"
android:layout_marginEnd="@dimen/spacing_large"
android:visibility="visible"
- tools:text="@string/app_disclaimer"
- android:textColor="@color/header_subtext" />
+ tools:text="@string/app_disclaimer" />
\ No newline at end of file
diff --git a/src/android/app/src/main/res/menu/menu_game_grid.xml b/src/android/app/src/main/res/menu/menu_game_grid.xml
index 9cdcc7f08..1a4bee6df 100644
--- a/src/android/app/src/main/res/menu/menu_game_grid.xml
+++ b/src/android/app/src/main/res/menu/menu_game_grid.xml
@@ -4,7 +4,7 @@
diff --git a/src/android/app/src/main/res/values-night/citra_colors.xml b/src/android/app/src/main/res/values-night/citra_colors.xml
new file mode 100644
index 000000000..54e6a913a
--- /dev/null
+++ b/src/android/app/src/main/res/values-night/citra_colors.xml
@@ -0,0 +1,33 @@
+
+
+
+ #F8BE00
+ #3F2E00
+ #451C00
+ #FFDF9A
+ #D7C4A0
+ #3A2F15
+ #52452A
+ #F4E0BB
+ #FFB2BC
+ #5F1126
+ #7D293B
+ #FFD9DD
+ #FFB4AB
+ #93000A
+ #690005
+ #FFDAD6
+ #1E1B16
+ #E9E1D9
+ #1E1B16
+ #E9E1D9
+ #4D4639
+ #D0C5B4
+ #999080
+ #1E1B16
+ #E9E1D9
+ #785A00
+ #F8BE00
+ #4D4639
+
+
diff --git a/src/android/app/src/main/res/values-night/colors.xml b/src/android/app/src/main/res/values-night/colors.xml
deleted file mode 100644
index 43b948021..000000000
--- a/src/android/app/src/main/res/values-night/colors.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- #272727
- #121212
- #FEC303
-
- #121212
- #3D3D3D
-
- #404040
-
- #E0E0E0
- #A0A0A0
-
- @color/citra_accent
-
diff --git a/src/android/app/src/main/res/values/citra_colors.xml b/src/android/app/src/main/res/values/citra_colors.xml
new file mode 100644
index 000000000..f0cfd3780
--- /dev/null
+++ b/src/android/app/src/main/res/values/citra_colors.xml
@@ -0,0 +1,35 @@
+
+
+
+ #FFAB03
+ #FFFFFF
+ #FFDF9A
+ #251A00
+ #6B5D3F
+ #FFFFFF
+ #F4E0BB
+ #241A04
+ #9B4052
+ #FFFFFF
+ #FFD9DD
+ #400013
+ #BA1A1A
+ #FFDAD6
+ #FFFFFF
+ #410002
+ #FFFBFF
+ #1E1B16
+ #FFFBFF
+ #1E1B16
+ #EDE1CF
+ #4D4639
+ #7F7667
+ #F7F0E7
+ #33302A
+ #F8BE00
+ #000000
+ #783E00
+ #D0C5B4
+ #000000
+
+
diff --git a/src/android/app/src/main/res/values/colors.xml b/src/android/app/src/main/res/values/colors.xml
deleted file mode 100644
index 6668288a7..000000000
--- a/src/android/app/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- #FFC303
- #FF8D03
- #CC7102
-
- #ffffff
- #D5D5D5
-
- #ffffff
-
- #1C1424
- #5C5661
-
- @color/header_text
-
diff --git a/src/android/app/src/main/res/values/styles.xml b/src/android/app/src/main/res/values/styles.xml
index 47fe6f6ea..b1d36498e 100644
--- a/src/android/app/src/main/res/values/styles.xml
+++ b/src/android/app/src/main/res/values/styles.xml
@@ -1,65 +1,37 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/values/themes.xml b/src/android/app/src/main/res/values/themes.xml
new file mode 100644
index 000000000..edff173fe
--- /dev/null
+++ b/src/android/app/src/main/res/values/themes.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/audio_core/hle/hle.cpp b/src/audio_core/hle/hle.cpp
index cba17c5ca..652e06bf3 100644
--- a/src/audio_core/hle/hle.cpp
+++ b/src/audio_core/hle/hle.cpp
@@ -272,6 +272,7 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, const std::vector& buffer)
case StateChange::Sleep:
LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware");
UNIMPLEMENTED();
+ AudioPipeWriteStructAddresses();
dsp_state = DspState::Sleeping;
break;
default:
@@ -438,7 +439,7 @@ bool DspHle::Impl::Tick() {
parent.OutputFrame(std::move(current_frame));
- return true;
+ return GetDspState() == DspState::On;
}
void DspHle::Impl::AudioTickCallback(s64 cycles_late) {
diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp
index 62be40208..b13013c1a 100644
--- a/src/citra_qt/configuration/configure_input.cpp
+++ b/src/citra_qt/configuration/configure_input.cpp
@@ -423,11 +423,6 @@ void ConfigureInput::OnHotkeysChanged(QList new_key_list) {
QList ConfigureInput::GetUsedKeyboardKeys() {
QList list;
for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
- // TODO(adityaruplaha): Add home button to list when we finally emulate it
- if (button == Settings::NativeButton::Home) {
- continue;
- }
-
const auto& button_param = buttons_param[button];
if (button_param.Get("engine", "") == "keyboard") {
list << QKeySequence(button_param.Get("code", 0));
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index a4085c83c..c4b981f30 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -74,6 +74,7 @@
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/archive_source_sd_savedata.h"
#include "core/frontend/applets/default_applets.h"
+#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/loader/loader.h"
@@ -313,6 +314,8 @@ void GMainWindow::InitializeWidgets() {
updater = new Updater(this);
UISettings::values.updater_found = updater->HasUpdater();
+ UpdateBootHomeMenuState();
+
// Create status bar
message_label = new QLabel();
// Configured separately for left alignment
@@ -754,6 +757,7 @@ void GMainWindow::ConnectMenuEvents() {
// File
connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile);
connect_menu(ui->action_Install_CIA, &GMainWindow::OnMenuInstallCIA);
+ connect_menu(ui->action_Boot_Home_Menu, &GMainWindow::OnMenuBootHomeMenu);
connect_menu(ui->action_Exit, &QMainWindow::close);
connect_menu(ui->action_Load_Amiibo, &GMainWindow::OnLoadAmiibo);
connect_menu(ui->action_Remove_Amiibo, &GMainWindow::OnRemoveAmiibo);
@@ -1597,6 +1601,20 @@ void GMainWindow::OnMenuInstallCIA() {
InstallCIA(filepaths);
}
+static std::string GetHomeMenuPath() {
+ static const std::array home_menu_tids = {
+ 0x0004003000008202, 0x0004003000008F02, 0x0004003000009802, 0x0004003000009802,
+ 0x000400300000A102, 0x000400300000A902, 0x000400300000B102};
+
+ Service::CFG::Module cfg{};
+ return Service::AM::GetTitleContentPath(Service::FS::MediaType::NAND,
+ home_menu_tids[cfg.GetRegionValue()]);
+}
+
+void GMainWindow::OnMenuBootHomeMenu() {
+ BootGame(QString::fromStdString(GetHomeMenuPath()));
+}
+
void GMainWindow::InstallCIA(QStringList filepaths) {
ui->action_Install_CIA->setEnabled(false);
game_list->SetDirectoryWatcherEnabled(false);
@@ -1948,6 +1966,7 @@ void GMainWindow::OnConfigure() {
}
UpdateSecondaryWindowVisibility();
UpdateAPIIndicator(false);
+ UpdateBootHomeMenuState();
} else {
Settings::values.input_profiles = old_input_profiles;
Settings::values.touch_from_button_maps = old_touch_from_button_maps;
@@ -2241,6 +2260,12 @@ void GMainWindow::UpdateStatusBar() {
emu_frametime_label->setVisible(true);
}
+void GMainWindow::UpdateBootHomeMenuState() {
+ const std::string home_menu_path = GetHomeMenuPath();
+ ui->action_Boot_Home_Menu->setEnabled(!home_menu_path.empty() &&
+ FileUtil::Exists(GetHomeMenuPath()));
+}
+
void GMainWindow::HideMouseCursor() {
if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) {
mouse_hide_timer.stop();
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index a056c2aae..26798fa59 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -193,6 +193,7 @@ private slots:
void OnConfigurePerGame();
void OnMenuLoadFile();
void OnMenuInstallCIA();
+ void OnMenuBootHomeMenu();
void OnUpdateProgress(std::size_t written, std::size_t total);
void OnCIAInstallReport(Service::AM::InstallStatus status, QString filepath);
void OnCIAInstallFinished();
@@ -239,6 +240,7 @@ private slots:
private:
Q_INVOKABLE void OnMoviePlaybackCompleted();
void UpdateStatusBar();
+ void UpdateBootHomeMenuState();
void LoadTranslation();
void UpdateWindowTitle();
void UpdateUISettings();
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index 4c88252eb..38fff8b71 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -66,6 +66,7 @@
+
@@ -209,6 +210,11 @@
Install CIA...
+
+
+ Boot Home Menu
+
+
E&xit
diff --git a/src/citra_qt/multiplayer/direct_connect.cpp b/src/citra_qt/multiplayer/direct_connect.cpp
index feaea69c9..6cef55845 100644
--- a/src/citra_qt/multiplayer/direct_connect.cpp
+++ b/src/citra_qt/multiplayer/direct_connect.cpp
@@ -70,20 +70,13 @@ void DirectConnectWindow::Connect() {
}
}
}
- switch (static_cast(ui->connection_type->currentIndex())) {
- case ConnectionType::TraversalServer:
- break;
- case ConnectionType::IP:
- if (!ui->ip->hasAcceptableInput()) {
- NetworkMessage::ErrorManager::ShowError(
- NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID);
- return;
- }
- if (!ui->port->hasAcceptableInput()) {
- NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID);
- return;
- }
- break;
+ if (!ui->ip->hasAcceptableInput()) {
+ NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID);
+ return;
+ }
+ if (!ui->port->hasAcceptableInput()) {
+ NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID);
+ return;
}
// Store settings
diff --git a/src/citra_qt/multiplayer/direct_connect.ui b/src/citra_qt/multiplayer/direct_connect.ui
index 681b6bf69..4a328189a 100644
--- a/src/citra_qt/multiplayer/direct_connect.ui
+++ b/src/citra_qt/multiplayer/direct_connect.ui
@@ -26,20 +26,11 @@
0
- -
-
-
-
-
- IP Address
-
-
-
-
-
- 5
+ 0
0
@@ -53,17 +44,17 @@
-
- IP
+ Server Address
-
- <html><head/><body><p>IPv4 address of the host</p></body></html>
+ <html><head/><body><p>Server address of the host</p></body></html>
- 16
+ 253
@@ -85,6 +76,12 @@
24872
+
+
+ 65
+ 50
+
+
diff --git a/src/citra_qt/multiplayer/lobby.cpp b/src/citra_qt/multiplayer/lobby.cpp
index d74bbf3ab..72f11b9db 100644
--- a/src/citra_qt/multiplayer/lobby.cpp
+++ b/src/citra_qt/multiplayer/lobby.cpp
@@ -66,6 +66,7 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
// UI Buttons
connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
+ connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
@@ -282,12 +283,22 @@ bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
return true;
}
+ // filter by empty rooms
+ if (filter_empty) {
+ QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
+ const int player_count =
+ sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size();
+ if (player_count == 0) {
+ return false;
+ }
+ }
+
// filter by filled rooms
if (filter_full) {
QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
- int player_count =
+ const int player_count =
sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size();
- int max_players =
+ const int max_players =
sourceModel()->data(member_list, LobbyItemMemberList::MaxPlayerRole).toInt();
if (player_count >= max_players) {
return false;
@@ -352,6 +363,11 @@ void LobbyFilterProxyModel::SetFilterOwned(bool filter) {
invalidate();
}
+void LobbyFilterProxyModel::SetFilterEmpty(bool filter) {
+ filter_empty = filter;
+ invalidate();
+}
+
void LobbyFilterProxyModel::SetFilterFull(bool filter) {
filter_full = filter;
invalidate();
diff --git a/src/citra_qt/multiplayer/lobby.h b/src/citra_qt/multiplayer/lobby.h
index 985b82ba7..129b25497 100644
--- a/src/citra_qt/multiplayer/lobby.h
+++ b/src/citra_qt/multiplayer/lobby.h
@@ -116,12 +116,14 @@ public:
public slots:
void SetFilterOwned(bool);
+ void SetFilterEmpty(bool);
void SetFilterFull(bool);
void SetFilterSearch(const QString&);
private:
QStandardItemModel* game_list;
bool filter_owned = false;
+ bool filter_empty = false;
bool filter_full = false;
QString filter_search;
};
diff --git a/src/citra_qt/multiplayer/lobby.ui b/src/citra_qt/multiplayer/lobby.ui
index 4c9901c9a..0ef0ef762 100644
--- a/src/citra_qt/multiplayer/lobby.ui
+++ b/src/citra_qt/multiplayer/lobby.ui
@@ -77,6 +77,13 @@
+ -
+
+
+ Hide Empty Rooms
+
+
+
-
diff --git a/src/citra_qt/multiplayer/validation.h b/src/citra_qt/multiplayer/validation.h
index 1c215a190..ecf3069d9 100644
--- a/src/citra_qt/multiplayer/validation.h
+++ b/src/citra_qt/multiplayer/validation.h
@@ -37,11 +37,28 @@ private:
QRegExp nickname_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$"));
QRegExpValidator nickname;
- /// ipv4 address only
- // TODO remove this when we support hostnames in direct connect
+ /// ipv4 / ipv6 / hostnames
QRegExp ip_regex = QRegExp(QStringLiteral(
- "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|"
- "2[0-4][0-9]|25[0-5])"));
+ // IPv4 regex
+ "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|"
+ // IPv6 regex
+ "^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|"
+ "(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-"
+ "5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|"
+ "(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)"
+ "(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|"
+ "(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]"
+ "\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
+ "(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2["
+ "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
+ "(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2["
+ "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
+ "(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2["
+ "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
+ "(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?"
+ "\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?$|"
+ // Hostname regex
+ "^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}$"));
QRegExpValidator ip;
/// port must be between 0 and 65535
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 11248bd7a..9a88b2ebf 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -67,6 +67,11 @@ void Apply() {
hid->ReloadInputDevices();
}
+ auto apt = Service::APT::GetModule(system);
+ if (apt) {
+ apt->GetAppletManager()->ReloadInputDevices();
+ }
+
auto sm = system.ServiceManager();
auto ir_user = sm.GetService("ir:USER");
if (ir_user)
diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp
index ab96cc2e2..8be1a0e7e 100644
--- a/src/core/hle/applets/applet.cpp
+++ b/src/core/hle/applets/applet.cpp
@@ -70,7 +70,7 @@ ResultCode Applet::Create(Service::APT::AppletId id, Service::APT::AppletId pare
}
Service::APT::AppletAttributes attributes;
- attributes.applet_pos.Assign(static_cast(Service::APT::AppletPos::AutoLibrary));
+ attributes.applet_pos.Assign(Service::APT::AppletPos::AutoLibrary);
attributes.is_home_menu.Assign(false);
const auto lock_handle_data = manager->GetLockHandle(attributes);
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index ca47d591f..b807bc261 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -133,6 +133,12 @@ public:
std::shared_ptr CreateProcess(std::shared_ptr code_set);
+ /**
+ * Removes a process from the kernel process list
+ * @param process Process to remove
+ */
+ void RemoveProcess(std::shared_ptr process);
+
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param name The friendly name desired for the thread
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 6e8e25bea..89b3636c5 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -79,6 +79,10 @@ std::shared_ptr KernelSystem::CreateProcess(std::shared_ptr co
return process;
}
+void KernelSystem::RemoveProcess(std::shared_ptr process) {
+ std::erase(process_list, process);
+}
+
void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
for (std::size_t i = 0; i < len; ++i) {
u32 descriptor = kernel_caps[i];
@@ -208,9 +212,6 @@ void Process::Exit() {
if (plgldr) {
plgldr->OnProcessExit(*this, kernel);
}
-
- // Clear the process's open handles.
- handle_table.Clear();
}
VAddr Process::GetLinearHeapAreaAddress() const {
@@ -474,6 +475,8 @@ Kernel::Process::Process(KernelSystem& kernel)
kernel.memory.RegisterPageTable(vm_manager.page_table);
}
Kernel::Process::~Process() {
+ LOG_INFO(Kernel, "Cleaning up process {}", process_id);
+
// Release all objects this process owns first so that their potential destructor can do clean
// up with this process before further destruction.
// TODO(wwylele): explicitly destroy or invalidate objects this process owns (threads, shared
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 6af90edbd..666fdecd1 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -565,6 +565,9 @@ void SVC::ExitProcess() {
// Kill the current thread
kernel.GetCurrentThreadManager().GetCurrentThread()->Stop();
+ // Remove kernel reference to process so it can be cleaned up.
+ kernel.RemoveProcess(current_process);
+
system.PrepareReschedule();
}
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index af4532b75..dd00b71c4 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1140,6 +1140,7 @@ void Module::Interface::CheckContentRights(Kernel::HLERequestContext& ctx) {
// TODO(shinyquagsire23): Read tickets for this instead?
bool has_rights =
+ FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::NAND, tid, content_index)) ||
FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index));
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp
index 0bcbd2ecf..b0755bfdb 100644
--- a/src/core/hle/service/apt/applet_manager.cpp
+++ b/src/core/hle/service/apt/applet_manager.cpp
@@ -4,17 +4,23 @@
#include "common/settings.h"
#include "core/core.h"
+#include "core/frontend/input.h"
#include "core/hle/applets/applet.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/apt/applet_manager.h"
#include "core/hle/service/apt/errors.h"
#include "core/hle/service/apt/ns.h"
#include "core/hle/service/cfg/cfg.h"
+#include "core/hle/service/gsp/gsp_gpu.h"
+#include "video_core/utils.h"
SERVICE_CONSTRUCT_IMPL(Service::APT::AppletManager)
namespace Service::APT {
+/// The interval at which the home button update callback will be called, 16.6ms
+static constexpr u64 home_button_update_interval_us = 16666;
+
struct AppletTitleData {
// There are two possible applet ids for each applet.
std::array applet_ids;
@@ -108,6 +114,14 @@ static u64 GetTitleIdForApplet(AppletId id, u32 region_value) {
return itr->title_ids[region_value];
}
+static bool IsSystemAppletId(AppletId applet_id) {
+ return (static_cast(applet_id) & static_cast(AppletId::AnySystemApplet)) != 0;
+}
+
+static bool IsApplicationAppletId(AppletId applet_id) {
+ return (static_cast(applet_id) & static_cast(AppletId::Application)) != 0;
+}
+
AppletManager::AppletSlot AppletManager::GetAppletSlotFromId(AppletId id) {
if (id == AppletId::Application) {
if (GetAppletSlot(AppletSlot::Application)->applet_id != AppletId::None)
@@ -133,7 +147,7 @@ AppletManager::AppletSlot AppletManager::GetAppletSlotFromId(AppletId id) {
if (slot_data->applet_id == AppletId::None)
return AppletSlot::Error;
- auto applet_pos = static_cast(slot_data->attributes.applet_pos.Value());
+ auto applet_pos = slot_data->attributes.applet_pos.Value();
if ((id == AppletId::AnyLibraryApplet && applet_pos == AppletPos::Library) ||
(id == AppletId::AnySysLibraryApplet && applet_pos == AppletPos::SysLibrary))
return AppletSlot::LibraryApplet;
@@ -163,11 +177,11 @@ AppletManager::AppletSlot AppletManager::GetAppletSlotFromAttributes(AppletAttri
AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet,
AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet};
- auto applet_pos = attributes.applet_pos;
- if (applet_pos >= applet_position_slots.size())
+ auto applet_pos_value = static_cast(attributes.applet_pos.Value());
+ if (applet_pos_value >= applet_position_slots.size())
return AppletSlot::Error;
- auto slot = applet_position_slots[applet_pos];
+ auto slot = applet_position_slots[applet_pos_value];
if (slot == AppletSlot::Error)
return AppletSlot::Error;
@@ -212,13 +226,35 @@ void AppletManager::CancelAndSendParameter(const MessageParameter& parameter) {
// Otherwise, send the parameter the LLE way.
next_parameter = parameter;
+ if (parameter.signal == SignalType::RequestForSysApplet) {
+ // APT handles RequestForSysApplet messages itself.
+ LOG_DEBUG(Service_APT, "Replying to RequestForSysApplet from {:03X}",
+ parameter.sender_id);
+
+ if (parameter.buffer.size() >= sizeof(CaptureBufferInfo)) {
+ SendCaptureBufferInfo(parameter.buffer);
+ CaptureFrameBuffers();
+ }
+
+ next_parameter->sender_id = parameter.destination_id;
+ next_parameter->destination_id = parameter.sender_id;
+ next_parameter->signal = SignalType::Response;
+ next_parameter->buffer.clear();
+ next_parameter->object = nullptr;
+ } else if (IsSystemAppletId(parameter.sender_id) &&
+ IsApplicationAppletId(parameter.destination_id) && parameter.object) {
+ // When a message is sent from a system applet to an application, APT
+ // replaces its object with the zero handle.
+ next_parameter->object = nullptr;
+ }
+
// Signal the event to let the receiver know that a new parameter is ready to be read
- auto slot = GetAppletSlotFromId(parameter.destination_id);
+ auto slot = GetAppletSlotFromId(next_parameter->destination_id);
if (slot != AppletSlot::Error) {
GetAppletSlot(slot)->parameter_event->Signal();
} else {
LOG_DEBUG(Service_APT, "No applet was registered with ID {:03X}",
- parameter.destination_id);
+ next_parameter->destination_id);
}
}
}
@@ -261,6 +297,10 @@ ResultVal AppletManager::GlanceParameter(AppletId app_id) {
ResultVal AppletManager::ReceiveParameter(AppletId app_id) {
auto result = GlanceParameter(app_id);
if (result.Succeeded()) {
+ LOG_DEBUG(Service_APT,
+ "Received parameter from {:03X} to {:03X} with signal {:08X} and size {:08X}",
+ result->sender_id, result->destination_id, result->signal, result->buffer.size());
+
// Clear the parameter
next_parameter = {};
}
@@ -282,13 +322,13 @@ bool AppletManager::CancelParameter(bool check_sender, AppletId sender_appid, bo
ResultVal AppletManager::GetLockHandle(
AppletAttributes attributes) {
auto corrected_attributes = attributes;
- if (attributes.applet_pos == static_cast(AppletPos::Library) ||
- attributes.applet_pos == static_cast(AppletPos::SysLibrary) ||
- attributes.applet_pos == static_cast(AppletPos::AutoLibrary)) {
+ if (attributes.applet_pos == AppletPos::Library ||
+ attributes.applet_pos == AppletPos::SysLibrary ||
+ attributes.applet_pos == AppletPos::AutoLibrary) {
auto corrected_pos = last_library_launcher_slot == AppletSlot::Application
? AppletPos::Library
: AppletPos::SysLibrary;
- corrected_attributes.applet_pos.Assign(static_cast(corrected_pos));
+ corrected_attributes.applet_pos.Assign(corrected_pos);
LOG_DEBUG(Service_APT, "Corrected applet attributes from {:08X} to {:08X}", attributes.raw,
corrected_attributes.raw);
}
@@ -350,6 +390,13 @@ ResultCode AppletManager::Enable(AppletAttributes attributes) {
auto slot_data = GetAppletSlot(slot);
slot_data->registered = true;
+ if (slot_data->attributes.applet_pos == AppletPos::System &&
+ slot_data->attributes.is_home_menu) {
+ slot_data->attributes.raw |= attributes.raw;
+ LOG_DEBUG(Service_APT, "Updated home menu attributes to {:08X}.",
+ slot_data->attributes.raw);
+ }
+
// Send any outstanding parameters to the newly-registered application
if (delayed_parameter && delayed_parameter->destination_id == slot_data->applet_id) {
// TODO: Real APT would loop trying to send the parameter until it succeeds,
@@ -366,6 +413,35 @@ bool AppletManager::IsRegistered(AppletId app_id) {
return slot != AppletSlot::Error && GetAppletSlot(slot)->registered;
}
+ResultVal AppletManager::InquireNotification(AppletId app_id) {
+ auto slot = GetAppletSlotFromId(app_id);
+ if (slot != AppletSlot::Error) {
+ auto slot_data = GetAppletSlot(slot);
+ if (slot_data->registered) {
+ auto notification = slot_data->notification;
+ slot_data->notification = Notification::None;
+ return MakeResult(notification);
+ }
+ }
+
+ return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
+ ErrorLevel::Status);
+}
+
+ResultCode AppletManager::SendNotification(Notification notification) {
+ if (active_slot != AppletSlot::Error) {
+ const auto slot_data = GetAppletSlot(active_slot);
+ if (slot_data->registered) {
+ slot_data->notification = notification;
+ slot_data->notification_event->Signal();
+ return RESULT_SUCCESS;
+ }
+ }
+
+ return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
+ ErrorLevel::Status};
+}
+
ResultCode AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) {
// The real APT service returns an error if there's a pending APT parameter when this function
// is called.
@@ -589,18 +665,281 @@ ResultCode AppletManager::CloseSystemApplet(std::shared_ptr obje
"Attempting to close a system applet from a non-system applet.");
auto slot = GetAppletSlot(active_slot);
+ auto closed_applet_id = slot->applet_id;
active_slot = last_system_launcher_slot;
-
- // TODO: Send a parameter to the application only if the application ordered the applet to
- // close.
-
- // TODO: Terminate the running applet title
slot->Reset();
+ if (ordered_to_close_sys_applet) {
+ ordered_to_close_sys_applet = false;
+
+ active_slot = AppletSlot::Application;
+ CancelAndSendParameter({
+ .sender_id = closed_applet_id,
+ .destination_id = AppletId::Application,
+ .signal = SignalType::WakeupByExit,
+ .object = std::move(object),
+ .buffer = buffer,
+ });
+ }
+
+ // TODO: Terminate the running applet title
return RESULT_SUCCESS;
}
+ResultCode AppletManager::OrderToCloseSystemApplet() {
+ if (active_slot == AppletSlot::Error) {
+ return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ auto active_slot_data = GetAppletSlot(active_slot);
+ if (active_slot_data->applet_id == AppletId::None ||
+ active_slot_data->attributes.applet_pos != AppletPos::Application) {
+ return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ auto system_slot = GetAppletSlotFromPos(AppletPos::System);
+ if (system_slot == AppletSlot::Error) {
+ return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
+ ErrorLevel::Status};
+ }
+
+ auto system_slot_data = GetAppletSlot(system_slot);
+ if (!system_slot_data->registered) {
+ return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
+ ErrorLevel::Status};
+ }
+
+ ordered_to_close_sys_applet = true;
+ active_slot = system_slot;
+
+ SendParameter({
+ .sender_id = AppletId::Application,
+ .destination_id = system_slot_data->applet_id,
+ .signal = SignalType::WakeupByCancel,
+ });
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode AppletManager::PrepareToJumpToHomeMenu() {
+ if (next_parameter) {
+ return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ last_jump_to_home_slot = active_slot;
+ if (last_jump_to_home_slot == AppletSlot::Application) {
+ EnsureHomeMenuLoaded();
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode AppletManager::JumpToHomeMenu(std::shared_ptr object,
+ const std::vector& buffer) {
+ if (last_jump_to_home_slot != AppletSlot::Error) {
+ auto slot_data = GetAppletSlot(last_jump_to_home_slot);
+ if (slot_data->applet_id != AppletId::None) {
+ MessageParameter param;
+ param.object = std::move(object);
+ param.buffer = buffer;
+
+ switch (slot_data->attributes.applet_pos) {
+ case AppletPos::Application:
+ active_slot = AppletSlot::HomeMenu;
+
+ param.destination_id = AppletId::HomeMenu;
+ param.sender_id = AppletId::Application;
+ param.signal = SignalType::WakeupByPause;
+ SendParameter(param);
+ break;
+ case AppletPos::Library:
+ param.destination_id = slot_data->applet_id;
+ param.sender_id = slot_data->applet_id;
+ param.signal = SignalType::WakeupByCancel;
+ SendParameter(param);
+ break;
+ case AppletPos::System:
+ if (slot_data->attributes.is_home_menu) {
+ param.destination_id = slot_data->applet_id;
+ param.sender_id = slot_data->applet_id;
+ param.signal = SignalType::WakeupToJumpHome;
+ SendParameter(param);
+ }
+ break;
+ case AppletPos::SysLibrary: {
+ const auto system_slot_data = GetAppletSlot(AppletSlot::SystemApplet);
+ param.destination_id = slot_data->applet_id;
+ param.sender_id = slot_data->applet_id;
+ param.signal = system_slot_data->registered ? SignalType::WakeupByCancel
+ : SignalType::WakeupToJumpHome;
+ SendParameter(param);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode AppletManager::PrepareToLeaveHomeMenu() {
+ if (!GetAppletSlot(AppletSlot::Application)->registered) {
+ return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ if (next_parameter) {
+ return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode AppletManager::LeaveHomeMenu(std::shared_ptr object,
+ const std::vector& buffer) {
+ active_slot = AppletSlot::Application;
+
+ SendParameter({
+ .sender_id = AppletId::HomeMenu,
+ .destination_id = AppletId::Application,
+ .signal = SignalType::WakeupByPause,
+ .object = std::move(object),
+ .buffer = buffer,
+ });
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode AppletManager::OrderToCloseApplication() {
+ if (active_slot == AppletSlot::Error) {
+ return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ auto active_slot_data = GetAppletSlot(active_slot);
+ if (active_slot_data->applet_id == AppletId::None ||
+ active_slot_data->attributes.applet_pos != AppletPos::System) {
+ return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ ordered_to_close_application = true;
+ active_slot = AppletSlot::Application;
+
+ SendParameter({
+ .sender_id = AppletId::HomeMenu,
+ .destination_id = AppletId::Application,
+ .signal = SignalType::WakeupByCancel,
+ });
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode AppletManager::PrepareToCloseApplication(bool return_to_sys) {
+ if (active_slot == AppletSlot::Error) {
+ return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ auto active_slot_data = GetAppletSlot(active_slot);
+ if (active_slot_data->applet_id == AppletId::None ||
+ active_slot_data->attributes.applet_pos != AppletPos::Application) {
+ return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ auto system_slot_data = GetAppletSlot(AppletSlot::SystemApplet);
+ auto home_menu_slot_data = GetAppletSlot(AppletSlot::HomeMenu);
+
+ if (!application_cancelled && return_to_sys) {
+ // TODO: Left side of the OR also includes "&& !power_button_clicked", but this isn't
+ // implemented yet.
+ if (!ordered_to_close_application || !system_slot_data->registered) {
+ application_close_target = AppletSlot::HomeMenu;
+ } else {
+ application_close_target = AppletSlot::SystemApplet;
+ }
+ } else {
+ application_close_target = AppletSlot::Error;
+ }
+
+ if (application_close_target != AppletSlot::HomeMenu && !system_slot_data->registered &&
+ !home_menu_slot_data->registered) {
+ return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ if (next_parameter) {
+ return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ if (application_close_target == AppletSlot::HomeMenu) {
+ EnsureHomeMenuLoaded();
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode AppletManager::CloseApplication(std::shared_ptr object,
+ const std::vector& buffer) {
+ ordered_to_close_application = false;
+ application_cancelled = false;
+
+ GetAppletSlot(AppletSlot::Application)->Reset();
+
+ if (application_close_target != AppletSlot::Error) {
+ active_slot = application_close_target;
+
+ CancelAndSendParameter({
+ .sender_id = AppletId::Application,
+ .destination_id = GetAppletSlot(application_close_target)->applet_id,
+ .signal = SignalType::WakeupByExit,
+ .object = std::move(object),
+ .buffer = buffer,
+ });
+ }
+
+ // TODO: Terminate the application process.
+ return RESULT_SUCCESS;
+}
+
+ResultVal AppletManager::GetAppletManInfo(
+ AppletPos requested_applet_pos) {
+ auto active_applet_pos = AppletPos::Invalid;
+ auto active_applet_id = AppletId::None;
+ if (active_slot != AppletSlot::Error) {
+ auto active_slot_data = GetAppletSlot(active_slot);
+ if (active_slot_data->applet_id != AppletId::None) {
+ active_applet_pos = active_slot_data->attributes.applet_pos;
+ active_applet_id = active_slot_data->applet_id;
+ }
+ }
+
+ auto requested_applet_id = AppletId::None;
+ auto requested_slot = GetAppletSlotFromPos(requested_applet_pos);
+ if (requested_slot != AppletSlot::Error) {
+ auto requested_slot_data = GetAppletSlot(requested_slot);
+ if (requested_slot_data->registered) {
+ requested_applet_id = requested_slot_data->applet_id;
+ }
+ }
+
+ return MakeResult({
+ .active_applet_pos = active_applet_pos,
+ .requested_applet_id = requested_applet_id,
+ .home_menu_applet_id = AppletId::HomeMenu,
+ .active_applet_id = active_applet_id,
+ });
+}
+
ResultVal AppletManager::GetAppletInfo(AppletId app_id) {
auto slot = GetAppletSlotFromId(app_id);
if (slot == AppletSlot::Error) {
@@ -709,7 +1048,7 @@ ResultCode AppletManager::DoApplicationJump(const DeliverArg& arg) {
ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType media_type) {
if (active_slot == AppletSlot::Error ||
- GetAppletSlot(active_slot)->attributes.applet_pos != static_cast(AppletPos::System)) {
+ GetAppletSlot(active_slot)->attributes.applet_pos != AppletPos::System) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
ErrorLevel::Status};
}
@@ -771,9 +1110,30 @@ ResultCode AppletManager::WakeupApplication() {
// Send a Wakeup signal via the apt parameter to the application once it registers itself.
// The real APT service does this by spin waiting on another thread until the application is
// registered.
- SendApplicationParameterAfterRegistration({.sender_id = AppletId::HomeMenu,
- .destination_id = AppletId::Application,
- .signal = SignalType::Wakeup});
+ SendApplicationParameterAfterRegistration({
+ .sender_id = AppletId::HomeMenu,
+ .destination_id = AppletId::Application,
+ .signal = SignalType::Wakeup,
+ });
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode AppletManager::CancelApplication() {
+ auto application_slot_data = GetAppletSlot(AppletSlot::Application);
+ if (application_slot_data->applet_id == AppletId::None) {
+ return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ application_cancelled = true;
+
+ SendApplicationParameterAfterRegistration({
+ .sender_id = active_slot != AppletSlot::Error ? GetAppletSlot(active_slot)->applet_id
+ : AppletId::None,
+ .destination_id = AppletId::Application,
+ .signal = SignalType::WakeupByCancel,
+ });
return RESULT_SUCCESS;
}
@@ -813,6 +1173,78 @@ void AppletManager::EnsureHomeMenuLoaded() {
}
}
+static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr src, u32 height,
+ u32 format) {
+ static constexpr auto screen_capture_base_vaddr = static_cast(0x1F500000);
+ static constexpr auto screen_width = 240;
+ static constexpr auto screen_width_pow2 = 256;
+ const auto bpp = format < 2 ? 3 : 2;
+
+ Memory::RasterizerFlushVirtualRegion(src, screen_width * height * bpp,
+ Memory::FlushMode::Flush);
+
+ auto dst_vaddr = screen_capture_base_vaddr + capture_offset;
+ auto dst_ptr = system.Memory().GetPointer(dst_vaddr);
+ const auto src_ptr = system.Memory().GetPointer(src);
+ for (auto y = 0; y < height; y++) {
+ for (auto x = 0; x < screen_width; x++) {
+ auto dst_offset =
+ VideoCore::GetMortonOffset(x, y, bpp) + (y & ~7) * screen_width_pow2 * bpp;
+ auto src_offset = bpp * (screen_width * y + x);
+ std::memcpy(dst_ptr + dst_offset, src_ptr + src_offset, bpp);
+ }
+ }
+
+ Memory::RasterizerFlushVirtualRegion(dst_vaddr, screen_width_pow2 * height * bpp,
+ Memory::FlushMode::Invalidate);
+}
+
+void AppletManager::CaptureFrameBuffers() {
+ auto gsp =
+ Core::System::GetInstance().ServiceManager().GetService("gsp::Gpu");
+ auto active_thread_id = gsp->GetActiveThreadId();
+ auto top_screen = gsp->GetFrameBufferInfo(active_thread_id, 0);
+ auto bottom_screen = gsp->GetFrameBufferInfo(active_thread_id, 1);
+
+ auto top_fb = top_screen->framebuffer_info[top_screen->index];
+ auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index];
+
+ CaptureFrameBuffer(system, capture_info->bottom_screen_left_offset, bottom_fb.address_left, 320,
+ capture_info->bottom_screen_format);
+ CaptureFrameBuffer(system, capture_info->top_screen_left_offset, top_fb.address_left, 400,
+ capture_info->top_screen_format);
+ if (capture_info->is_3d) {
+ CaptureFrameBuffer(system, capture_info->top_screen_right_offset, top_fb.address_right, 400,
+ capture_info->top_screen_format);
+ }
+}
+
+void AppletManager::LoadInputDevices() {
+ home_button = Input::CreateDevice(
+ Settings::values.current_input_profile.buttons[Settings::NativeButton::Home]);
+}
+
+void AppletManager::HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late) {
+ if (is_device_reload_pending.exchange(false)) {
+ LoadInputDevices();
+ }
+
+ const bool state = home_button->GetStatus();
+ // NOTE: We technically do support loading and jumping to home menu even if it isn't
+ // initially registered. However since the home menu suspend is not bug-free, we don't
+ // want normal users who didn't launch the home menu accidentally pressing the home
+ // button binding and freezing their game, so for now, gate it to only environments
+ // where the home menu was already loaded by the user (last condition).
+ if (state && !last_home_button_state && GetAppletSlot(AppletSlot::HomeMenu)->registered) {
+ SendNotification(Notification::HomeButtonSingle);
+ }
+ last_home_button_state = state;
+
+ // Reschedule recurrent event
+ Core::System::GetInstance().CoreTiming().ScheduleEvent(
+ usToCycles(home_button_update_interval_us) - cycles_late, home_button_update_event);
+}
+
AppletManager::AppletManager(Core::System& system) : system(system) {
lock = system.Kernel().CreateMutex(false, "APT_U:Lock");
for (std::size_t slot = 0; slot < applet_slots.size(); ++slot) {
@@ -828,10 +1260,20 @@ AppletManager::AppletManager(Core::System& system) : system(system) {
system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "APT:Parameter");
}
HLE::Applets::Init();
+ home_button_update_event = Core::System::GetInstance().CoreTiming().RegisterEvent(
+ "Home Button Update Event", [this](std::uintptr_t user_data, s64 cycles_late) {
+ HomeButtonUpdateEvent(user_data, cycles_late);
+ });
+ Core::System::GetInstance().CoreTiming().ScheduleEvent(
+ usToCycles(home_button_update_interval_us), home_button_update_event);
}
AppletManager::~AppletManager() {
HLE::Applets::Shutdown();
}
+void AppletManager::ReloadInputDevices() {
+ is_device_reload_pending.store(true);
+}
+
} // namespace Service::APT
diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h
index b7a11500a..1f7b041de 100644
--- a/src/core/hle/service/apt/applet_manager.h
+++ b/src/core/hle/service/apt/applet_manager.h
@@ -13,6 +13,7 @@
#include
#include
#include
+#include "core/frontend/input.h"
#include "core/global.h"
#include "core/hle/kernel/event.h"
#include "core/hle/result.h"
@@ -46,6 +47,21 @@ enum class SignalType : u32 {
WakeupToLaunchApplication = 0x11,
};
+enum class Notification : u32 {
+ None = 0,
+ HomeButtonSingle = 1,
+ HomeButtonDouble = 2,
+ SleepQuery = 3,
+ SleepCancelledByOpen = 4,
+ SleepAccepted = 5,
+ SleepAwake = 6,
+ Shutdown = 7,
+ PowerButtonClick = 8,
+ PowerButtonClear = 9,
+ TrySleep = 10,
+ OrderToClose = 11,
+};
+
/// App Id's used by APT functions
enum class AppletId : u32 {
None = 0,
@@ -103,19 +119,20 @@ private:
friend class boost::serialization::access;
};
-enum class AppletPos {
+enum class AppletPos : u32 {
Application = 0,
Library = 1,
System = 2,
SysLibrary = 3,
Resident = 4,
- AutoLibrary = 5
+ AutoLibrary = 5,
+ Invalid = 0xFF,
};
union AppletAttributes {
u32 raw;
- BitField<0, 3, u32> applet_pos;
+ BitField<0, 3, AppletPos> applet_pos;
BitField<29, 1, u32> is_home_menu;
AppletAttributes() : raw(0) {}
@@ -178,11 +195,41 @@ private:
friend class boost::serialization::access;
};
+/// Used by the application to pass information about the current framebuffer to applets.
+struct CaptureBufferInfo {
+ u32_le size;
+ u8 is_3d;
+ INSERT_PADDING_BYTES(0x3); // Padding for alignment
+ u32_le top_screen_left_offset;
+ u32_le top_screen_right_offset;
+ u32_le top_screen_format;
+ u32_le bottom_screen_left_offset;
+ u32_le bottom_screen_right_offset;
+ u32_le bottom_screen_format;
+
+private:
+ template
+ void serialize(Archive& ar, const unsigned int) {
+ ar& size;
+ ar& is_3d;
+ ar& top_screen_left_offset;
+ ar& top_screen_right_offset;
+ ar& top_screen_format;
+ ar& bottom_screen_left_offset;
+ ar& bottom_screen_right_offset;
+ ar& bottom_screen_format;
+ }
+ friend class boost::serialization::access;
+};
+static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
+
class AppletManager : public std::enable_shared_from_this {
public:
explicit AppletManager(Core::System& system);
~AppletManager();
+ void ReloadInputDevices();
+
/**
* Clears any existing parameter and places a new one. This function is currently only used by
* HLE Applets and should be likely removed in the future
@@ -211,6 +258,9 @@ public:
ResultCode Enable(AppletAttributes attributes);
bool IsRegistered(AppletId app_id);
+ ResultVal InquireNotification(AppletId app_id);
+ ResultCode SendNotification(Notification notification);
+
ResultCode PrepareToStartLibraryApplet(AppletId applet_id);
ResultCode PreloadLibraryApplet(AppletId applet_id);
ResultCode FinishPreloadingLibraryApplet(AppletId applet_id);
@@ -227,6 +277,18 @@ public:
ResultCode PrepareToCloseSystemApplet();
ResultCode CloseSystemApplet(std::shared_ptr object,
const std::vector& buffer);
+ ResultCode OrderToCloseSystemApplet();
+
+ ResultCode PrepareToJumpToHomeMenu();
+ ResultCode JumpToHomeMenu(std::shared_ptr object,
+ const std::vector& buffer);
+ ResultCode PrepareToLeaveHomeMenu();
+ ResultCode LeaveHomeMenu(std::shared_ptr object, const std::vector& buffer);
+
+ ResultCode OrderToCloseApplication();
+ ResultCode PrepareToCloseApplication(bool return_to_sys);
+ ResultCode CloseApplication(std::shared_ptr object,
+ const std::vector& buffer);
ResultCode PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type,
ApplicationJumpFlags flags);
@@ -239,10 +301,40 @@ public:
deliver_arg = std::move(arg);
}
+ std::vector GetCaptureInfo() {
+ std::vector buffer;
+ if (capture_info) {
+ buffer.resize(sizeof(CaptureBufferInfo));
+ std::memcpy(buffer.data(), &capture_info.get(), sizeof(CaptureBufferInfo));
+ }
+ return buffer;
+ }
+ std::vector ReceiveCaptureBufferInfo() {
+ std::vector buffer = GetCaptureInfo();
+ capture_info.reset();
+ return buffer;
+ }
+ void SendCaptureBufferInfo(std::vector buffer) {
+ ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small.");
+
+ capture_info.emplace();
+ std::memcpy(&capture_info.get(), buffer.data(), sizeof(CaptureBufferInfo));
+ }
+
ResultCode PrepareToStartApplication(u64 title_id, FS::MediaType media_type);
ResultCode StartApplication(const std::vector& parameter, const std::vector& hmac,
bool paused);
ResultCode WakeupApplication();
+ ResultCode CancelApplication();
+
+ struct AppletManInfo {
+ AppletPos active_applet_pos;
+ AppletId requested_applet_id;
+ AppletId home_menu_applet_id;
+ AppletId active_applet_id;
+ };
+
+ ResultVal GetAppletManInfo(AppletPos requested_applet_pos);
struct AppletInfo {
u64 title_id;
@@ -273,6 +365,8 @@ private:
boost::optional app_start_parameters{};
boost::optional deliver_arg{};
+ boost::optional capture_info;
+
static constexpr std::size_t NumAppletSlot = 4;
enum class AppletSlot : u8 {
@@ -292,6 +386,7 @@ private:
bool registered;
bool loaded;
AppletAttributes attributes;
+ Notification notification;
std::shared_ptr notification_event;
std::shared_ptr parameter_event;
@@ -311,6 +406,7 @@ private:
ar& registered;
ar& loaded;
ar& attributes.raw;
+ ar& notification;
ar& notification_event;
ar& parameter_event;
}
@@ -325,6 +421,16 @@ private:
SignalType library_applet_closing_command = SignalType::None;
AppletId last_prepared_library_applet = AppletId::None;
AppletSlot last_system_launcher_slot = AppletSlot::Error;
+ AppletSlot last_jump_to_home_slot = AppletSlot::Error;
+ bool ordered_to_close_sys_applet = false;
+ bool ordered_to_close_application = false;
+ bool application_cancelled = false;
+ AppletSlot application_close_target = AppletSlot::Error;
+
+ Core::TimingEventType* home_button_update_event;
+ std::atomic is_device_reload_pending{true};
+ std::unique_ptr home_button;
+ bool last_home_button_state = false;
Core::System& system;
@@ -346,6 +452,11 @@ private:
void EnsureHomeMenuLoaded();
+ void CaptureFrameBuffers();
+
+ void LoadInputDevices();
+ void HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late);
+
template
void serialize(Archive& ar, const unsigned int file_version) {
ar& next_parameter;
@@ -358,10 +469,20 @@ private:
ar& last_library_launcher_slot;
ar& last_prepared_library_applet;
ar& last_system_launcher_slot;
+ ar& last_jump_to_home_slot;
+ ar& ordered_to_close_sys_applet;
+ ar& ordered_to_close_application;
+ ar& application_cancelled;
+ ar& application_close_target;
ar& lock;
+ ar& capture_info;
}
ar& applet_slots;
ar& library_applet_closing_command;
+
+ if (Archive::is_loading::value) {
+ LoadInputDevices();
+ }
}
friend class boost::serialization::access;
};
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index bb0d19f7a..45051963e 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -41,7 +41,6 @@ void Module::serialize(Archive& ar, const unsigned int file_version) {
ar& shared_font_relocated;
ar& cpu_percent;
ar& unknown_ns_state_field;
- ar& screen_capture_buffer;
ar& screen_capture_post_permission;
ar& applet_manager;
if (file_version > 0) {
@@ -73,6 +72,47 @@ void Module::NSInterface::SetWirelessRebootInfo(Kernel::HLERequestContext& ctx)
LOG_WARNING(Service_APT, "called size={}", size);
}
+void Module::NSInterface::ShutdownAsync(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0xE, 0, 0); // 0xE0000
+
+ LOG_INFO(Service_APT, "called");
+
+ apt->system.RequestShutdown();
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Module::NSInterface::RebootSystem(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x10, 6, 0); // 0x100180
+ const auto launch_title = rp.Pop() != 0;
+ const auto title_id = rp.Pop();
+ const auto media_type = static_cast(rp.Pop());
+ rp.Skip(1, false); // Skip padding
+ const auto mem_type = rp.Pop();
+
+ LOG_WARNING(Service_APT,
+ "called launch_title={}, title_id={:016X}, media_type={:02X}, mem_type={:02X}",
+ launch_title, title_id, media_type, mem_type);
+
+ // TODO: Implement loading a specific title.
+ apt->system.RequestReset();
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Module::NSInterface::RebootSystemClean(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x16, 0, 0); // 0x160000
+
+ LOG_INFO(Service_APT, "called");
+
+ apt->system.RequestReset();
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS);
+}
+
void Module::APTInterface::Initialize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x2, 2, 0); // 0x20080
const auto app_id = rp.PopEnum();
@@ -332,16 +372,22 @@ void Module::APTInterface::Enable(Kernel::HLERequestContext& ctx) {
void Module::APTInterface::GetAppletManInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x5, 1, 0); // 0x50040
- const auto unk = rp.Pop();
+ auto applet_pos = rp.PopEnum();
- IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
- rb.Push(RESULT_SUCCESS); // No error
- rb.Push(0);
- rb.Push(0);
- rb.Push(static_cast(AppletId::HomeMenu)); // Home menu AppID
- rb.Push(static_cast(AppletId::Application)); // TODO(purpasmart96): Do this correctly
+ LOG_DEBUG(Service_APT, "called, applet_pos={:08X}", applet_pos);
- LOG_WARNING(Service_APT, "(STUBBED) called unk={:#010X}", unk);
+ auto info = apt->applet_manager->GetAppletManInfo(applet_pos);
+ if (info.Failed()) {
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(info.Code());
+ } else {
+ IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(info->active_applet_pos);
+ rb.PushEnum(info->requested_applet_id);
+ rb.PushEnum(info->home_menu_applet_id);
+ rb.PushEnum(info->active_applet_id);
+ }
}
void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) {
@@ -357,13 +403,19 @@ void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) {
void Module::APTInterface::InquireNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0xB, 1, 0); // 0xB0040
- const auto app_id = rp.Pop();
+ const auto app_id = rp.PopEnum();
- IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
- rb.Push(RESULT_SUCCESS); // No error
- rb.Push(static_cast(SignalType::None)); // Signal type
+ LOG_DEBUG(Service_APT, "called app_id={:#010X}", app_id);
- LOG_WARNING(Service_APT, "(STUBBED) called app_id={:#010X}", app_id);
+ auto notification = apt->applet_manager->InquireNotification(app_id);
+ if (notification.Failed()) {
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(notification.Code());
+ } else {
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(static_cast(notification.Unwrap()));
+ }
}
void Module::APTInterface::SendParameter(Kernel::HLERequestContext& ctx) {
@@ -561,6 +613,15 @@ void Module::APTInterface::WakeupApplication(Kernel::HLERequestContext& ctx) {
rb.Push(apt->applet_manager->WakeupApplication());
}
+void Module::APTInterface::CancelApplication(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x1D, 0, 0); // 0x001D0000
+
+ LOG_DEBUG(Service_APT, "called");
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(apt->applet_manager->CancelApplication());
+}
+
void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x4B, 3, 2); // 0x004B00C2
@@ -574,8 +635,18 @@ void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) {
"(STUBBED) called command={:#010X}, input_size={:#010X}, output_size={:#010X}",
utility_command, input_size, output_size);
- IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ std::vector out(output_size);
+ if (utility_command == 0x6 && output_size > 0) {
+ // Command 0x6 (TryLockTransition) expects a boolean return value indicating
+ // whether the attempt succeeded. Since we don't implement any of the transition
+ // locking stuff yet, fake a success result to avoid app crashes.
+ out[0] = true;
+ }
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS); // No error
+ rb.Push(RESULT_SUCCESS); // Utility function result
+ rb.PushStaticBuffer(out, 0);
}
void Module::APTInterface::SetAppCpuTimeLimit(Kernel::HLERequestContext& ctx) {
@@ -688,18 +759,35 @@ void Module::APTInterface::StartSystemApplet(Kernel::HLERequestContext& ctx) {
rb.Push(apt->applet_manager->StartSystemApplet(applet_id, object, buffer));
}
-void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp(ctx, 0x27, 1, 4);
- [[maybe_unused]] const auto parameters_size = rp.Pop();
- [[maybe_unused]] const auto object = rp.PopGenericObject();
- [[maybe_unused]] const auto buffer = rp.PopStaticBuffer();
+void Module::APTInterface::OrderToCloseApplication(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x21, 0, 0);
LOG_DEBUG(Service_APT, "called");
- apt->system.RequestShutdown();
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(apt->applet_manager->OrderToCloseApplication());
+}
+
+void Module::APTInterface::PrepareToCloseApplication(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x22, 1, 0);
+ const auto return_to_sys = rp.Pop() != 0;
+
+ LOG_DEBUG(Service_APT, "called return_to_sys={}", return_to_sys);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
- rb.Push(RESULT_SUCCESS);
+ rb.Push(apt->applet_manager->PrepareToCloseApplication(return_to_sys));
+}
+
+void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x27, 1, 4);
+ const auto parameter_size = rp.Pop();
+ const auto object = rp.PopGenericObject();
+ const auto buffer = rp.PopStaticBuffer();
+
+ LOG_DEBUG(Service_APT, "called size={}", parameter_size);
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(apt->applet_manager->CloseApplication(object, buffer));
}
void Module::APTInterface::CancelLibraryApplet(Kernel::HLERequestContext& ctx) {
@@ -758,6 +846,57 @@ void Module::APTInterface::CloseSystemApplet(Kernel::HLERequestContext& ctx) {
rb.Push(apt->applet_manager->CloseSystemApplet(object, buffer));
}
+void Module::APTInterface::OrderToCloseSystemApplet(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x2A, 0, 0); // 0x2A0000
+
+ LOG_DEBUG(Service_APT, "called");
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(apt->applet_manager->OrderToCloseSystemApplet());
+}
+
+void Module::APTInterface::PrepareToJumpToHomeMenu(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x2B, 0, 0); // 0x2B0000
+
+ LOG_DEBUG(Service_APT, "called");
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(apt->applet_manager->PrepareToJumpToHomeMenu());
+}
+
+void Module::APTInterface::JumpToHomeMenu(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x2C, 1, 4); // 0x2C0044
+ const auto parameter_size = rp.Pop();
+ const auto object = rp.PopGenericObject();
+ const auto buffer = rp.PopStaticBuffer();
+
+ LOG_DEBUG(Service_APT, "called size={}", parameter_size);
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(apt->applet_manager->JumpToHomeMenu(object, buffer));
+}
+
+void Module::APTInterface::PrepareToLeaveHomeMenu(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x2D, 0, 0); // 0x2D0000
+
+ LOG_DEBUG(Service_APT, "called");
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(apt->applet_manager->PrepareToLeaveHomeMenu());
+}
+
+void Module::APTInterface::LeaveHomeMenu(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x2E, 1, 4); // 0x2E0044
+ const auto parameter_size = rp.Pop();
+ const auto object = rp.PopGenericObject();
+ const auto buffer = rp.PopStaticBuffer();
+
+ LOG_DEBUG(Service_APT, "called size={}", parameter_size);
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(apt->applet_manager->LeaveHomeMenu(object, buffer));
+}
+
void Module::APTInterface::LoadSysMenuArg(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x36, 1, 0); // 0x00360040
const auto size = std::min(std::size_t{rp.Pop()}, SysMenuArgSize);
@@ -794,8 +933,7 @@ void Module::APTInterface::SendCaptureBufferInfo(Kernel::HLERequestContext& ctx)
LOG_DEBUG(Service_APT, "called");
- ASSERT(size == 0x20);
- apt->screen_capture_buffer = buffer;
+ apt->applet_manager->SendCaptureBufferInfo(buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
@@ -807,12 +945,14 @@ void Module::APTInterface::ReceiveCaptureBufferInfo(Kernel::HLERequestContext& c
LOG_DEBUG(Service_APT, "called");
- ASSERT(size == 0x20);
+ auto screen_capture_buffer = apt->applet_manager->ReceiveCaptureBufferInfo();
+ auto real_size = std::min(static_cast(screen_capture_buffer.size()), size);
+ screen_capture_buffer.resize(size);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS);
- rb.Push(static_cast(apt->screen_capture_buffer.size()));
- rb.PushStaticBuffer(std::move(apt->screen_capture_buffer), 0);
+ rb.Push(real_size);
+ rb.PushStaticBuffer(std::move(screen_capture_buffer), 0);
}
void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) {
@@ -820,13 +960,15 @@ void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) {
const auto size = rp.Pop();
LOG_DEBUG(Service_APT, "called");
- ASSERT(size == 0x20);
+
+ auto screen_capture_buffer = apt->applet_manager->GetCaptureInfo();
+ auto real_size = std::min(static_cast(screen_capture_buffer.size()), size);
+ screen_capture_buffer.resize(size);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS);
- rb.Push(static_cast(apt->screen_capture_buffer.size()));
- // This service function does not clear the capture buffer.
- rb.PushStaticBuffer(apt->screen_capture_buffer, 0);
+ rb.Push(real_size);
+ rb.PushStaticBuffer(std::move(screen_capture_buffer), 0);
}
void Module::APTInterface::SetScreenCapPostPermission(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index e831a7d77..90688ff85 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -31,20 +31,6 @@ class AppletManager;
/// Each APT service can only have up to 2 sessions connected at the same time.
static const u32 MaxAPTSessions = 2;
-/// Used by the application to pass information about the current framebuffer to applets.
-struct CaptureBufferInfo {
- u32_le size;
- u8 is_3d;
- INSERT_PADDING_BYTES(0x3); // Padding for alignment
- u32_le top_screen_left_offset;
- u32_le top_screen_right_offset;
- u32_le top_screen_format;
- u32_le bottom_screen_left_offset;
- u32_le bottom_screen_right_offset;
- u32_le bottom_screen_format;
-};
-static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
-
constexpr std::size_t SysMenuArgSize = 0x40;
enum class StartupArgumentType : u32 {
@@ -87,6 +73,37 @@ public:
* 0 : Result of function, 0 on success, otherwise error code
*/
void SetWirelessRebootInfo(Kernel::HLERequestContext& ctx);
+
+ /**
+ * NS::ShutdownAsync service function.
+ * Inputs:
+ * 1 : None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+ void ShutdownAsync(Kernel::HLERequestContext& ctx);
+
+ /**
+ * NS::RebootSystem service function.
+ * Inputs:
+ * 1 : Boolean indicating whether to launch a title.
+ * 2-3 : Title ID
+ * 4 : Media Type
+ * 5 : Padding
+ * 6 : Launch memory type
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+ void RebootSystem(Kernel::HLERequestContext& ctx);
+
+ /**
+ * NS::RebootSystemClean service function.
+ * Inputs:
+ * 1 : None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+ void RebootSystemClean(Kernel::HLERequestContext& ctx);
};
class APTInterface : public ServiceFramework {
@@ -371,6 +388,16 @@ public:
*/
void WakeupApplication(Kernel::HLERequestContext& ctx);
+ /**
+ * APT::CancelApplication service function.
+ * Inputs:
+ * 0 : Command header [0x001D0000]
+ * Outputs:
+ * 0 : Return Header
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+ void CancelApplication(Kernel::HLERequestContext& ctx);
+
/**
* APT::AppletUtility service function
* Inputs:
@@ -491,6 +518,27 @@ public:
*/
void StartSystemApplet(Kernel::HLERequestContext& ctx);
+ /**
+ * APT::OrderToCloseApplication service function
+ * Inputs:
+ * 0 : Command header [0x00210000]
+ * Outputs:
+ * 0 : Header code
+ * 1 : Result code
+ */
+ void OrderToCloseApplication(Kernel::HLERequestContext& ctx);
+
+ /**
+ * APT::PrepareToCloseApplication service function
+ * Inputs:
+ * 0 : Command header [0x00220040]
+ * 1 : Boolean indicating whether to cancel applet preloads.
+ * Outputs:
+ * 0 : Header code
+ * 1 : Result code
+ */
+ void PrepareToCloseApplication(Kernel::HLERequestContext& ctx);
+
/**
* APT::CloseApplication service function
* Inputs:
@@ -629,6 +677,66 @@ public:
*/
void CloseSystemApplet(Kernel::HLERequestContext& ctx);
+ /**
+ * APT::OrderToCloseSystemApplet service function
+ * Inputs:
+ * 0 : Command header [0x002A0000]
+ * Outputs:
+ * 0 : Header code
+ * 1 : Result code
+ */
+ void OrderToCloseSystemApplet(Kernel::HLERequestContext& ctx);
+
+ /**
+ * APT::PrepareToJumpToHomeMenu service function
+ * Inputs:
+ * 0 : Command header [0x002B0000]
+ * Outputs:
+ * 0 : Header code
+ * 1 : Result code
+ */
+ void PrepareToJumpToHomeMenu(Kernel::HLERequestContext& ctx);
+
+ /**
+ * APT::JumpToHomeMenu service function
+ * Inputs:
+ * 0 : Command header [0x002C0044]
+ * 1 : Buffer size
+ * 2 : 0x0
+ * 3 : Object handle
+ * 4 : (Size << 14) | 2
+ * 5 : Input buffer virtual address
+ * Outputs:
+ * 0 : Header code
+ * 1 : Result code
+ */
+ void JumpToHomeMenu(Kernel::HLERequestContext& ctx);
+
+ /**
+ * APT::PrepareToLeaveHomeMenu service function
+ * Inputs:
+ * 0 : Command header [0x002B0000]
+ * Outputs:
+ * 0 : Header code
+ * 1 : Result code
+ */
+ void PrepareToLeaveHomeMenu(Kernel::HLERequestContext& ctx);
+
+ /**
+ * APT::LeaveHomeMenu service function
+ * Inputs:
+ * 0 : Command header [0x002C0044]
+ * 1 : Buffer size
+ * 2 : 0x0
+ * 3 : Object handle
+ * 4 : (Size << 14) | 2
+ * 5 : Input buffer virtual address
+ * Outputs:
+ * 0 : Header code
+ * 1 : Result code
+ */
+ void LeaveHomeMenu(Kernel::HLERequestContext& ctx);
+
/**
* APT::LoadSysMenuArg service function
* Inputs:
@@ -801,7 +909,6 @@ private:
// APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode
u8 unknown_ns_state_field = 0;
- std::vector screen_capture_buffer;
std::array sys_menu_arg_buffer;
ScreencapPostPermission screen_capture_post_permission =
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
index 091ccaf0e..824e2b176 100644
--- a/src/core/hle/service/apt/apt_a.cpp
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -42,8 +42,8 @@ APT_A::APT_A(std::shared_ptr apt)
{0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"},
{0x001F0084, &APT_A::StartSystemApplet, "StartSystemApplet"},
{0x00200044, nullptr, "StartNewestHomeMenu"},
- {0x00210000, nullptr, "OrderToCloseApplication"},
- {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00210000, &APT_A::OrderToCloseApplication, "OrderToCloseApplication"},
+ {0x00220040, &APT_A::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x00230040, nullptr, "PrepareToJumpToApplication"},
{0x00240044, nullptr, "JumpToApplication"},
{0x002500C0, &APT_A::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
@@ -51,11 +51,11 @@ APT_A::APT_A(std::shared_ptr apt)
{0x00270044, &APT_A::CloseApplication, "CloseApplication"},
{0x00280044, &APT_A::CloseLibraryApplet, "CloseLibraryApplet"},
{0x00290044, &APT_A::CloseSystemApplet, "CloseSystemApplet"},
- {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
- {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
- {0x002C0044, nullptr, "JumpToHomeMenu"},
- {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
- {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002A0000, &APT_A::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"},
+ {0x002B0000, &APT_A::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, &APT_A::JumpToHomeMenu, "JumpToHomeMenu"},
+ {0x002D0000, &APT_A::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, &APT_A::LeaveHomeMenu, "LeaveHomeMenu"},
{0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
{0x00300044, nullptr, "LeaveResidentApplet"},
{0x00310100, &APT_A::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index 98ee8cb61..639c0d934 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -42,8 +42,8 @@ APT_S::APT_S(std::shared_ptr apt)
{0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"},
{0x001F0084, &APT_S::StartSystemApplet, "StartSystemApplet"},
{0x00200044, nullptr, "StartNewestHomeMenu"},
- {0x00210000, nullptr, "OrderToCloseApplication"},
- {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00210000, &APT_S::OrderToCloseApplication, "OrderToCloseApplication"},
+ {0x00220040, &APT_S::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x00230040, nullptr, "PrepareToJumpToApplication"},
{0x00240044, nullptr, "JumpToApplication"},
{0x002500C0, &APT_S::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
@@ -51,11 +51,11 @@ APT_S::APT_S(std::shared_ptr apt)
{0x00270044, &APT_S::CloseApplication, "CloseApplication"},
{0x00280044, &APT_S::CloseLibraryApplet, "CloseLibraryApplet"},
{0x00290044, &APT_S::CloseSystemApplet, "CloseSystemApplet"},
- {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
- {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
- {0x002C0044, nullptr, "JumpToHomeMenu"},
- {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
- {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002A0000, &APT_S::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"},
+ {0x002B0000, &APT_S::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, &APT_S::JumpToHomeMenu, "JumpToHomeMenu"},
+ {0x002D0000, &APT_S::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, &APT_S::LeaveHomeMenu, "LeaveHomeMenu"},
{0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
{0x00300044, nullptr, "LeaveResidentApplet"},
{0x00310100, &APT_S::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
index 329187153..34e9bc491 100644
--- a/src/core/hle/service/apt/apt_u.cpp
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -42,8 +42,8 @@ APT_U::APT_U(std::shared_ptr apt)
{0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"},
{0x001F0084, &APT_U::StartSystemApplet, "StartSystemApplet"},
{0x00200044, nullptr, "StartNewestHomeMenu"},
- {0x00210000, nullptr, "OrderToCloseApplication"},
- {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00210000, &APT_U::OrderToCloseApplication, "OrderToCloseApplication"},
+ {0x00220040, &APT_U::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x00230040, nullptr, "PrepareToJumpToApplication"},
{0x00240044, nullptr, "JumpToApplication"},
{0x002500C0, &APT_U::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
@@ -51,11 +51,11 @@ APT_U::APT_U(std::shared_ptr apt)
{0x00270044, &APT_U::CloseApplication, "CloseApplication"},
{0x00280044, &APT_U::CloseLibraryApplet, "CloseLibraryApplet"},
{0x00290044, &APT_U::CloseSystemApplet, "CloseSystemApplet"},
- {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
- {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
- {0x002C0044, nullptr, "JumpToHomeMenu"},
- {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
- {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002A0000, &APT_U::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"},
+ {0x002B0000, &APT_U::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, &APT_U::JumpToHomeMenu, "JumpToHomeMenu"},
+ {0x002D0000, &APT_U::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, &APT_U::LeaveHomeMenu, "LeaveHomeMenu"},
{0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
{0x00300044, nullptr, "LeaveResidentApplet"},
{0x00310100, &APT_U::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},
diff --git a/src/core/hle/service/apt/ns_s.cpp b/src/core/hle/service/apt/ns_s.cpp
index 2988ea5d8..63666dcae 100644
--- a/src/core/hle/service/apt/ns_s.cpp
+++ b/src/core/hle/service/apt/ns_s.cpp
@@ -19,12 +19,12 @@ NS_S::NS_S(std::shared_ptr apt)
{0x00070042, nullptr, "CardUpdateInitialize"},
{0x00080000, nullptr, "CardUpdateShutdown"},
{0x000D0140, nullptr, "SetTWLBannerHMAC"},
- {0x000E0000, nullptr, "ShutdownAsync"},
- {0x00100180, nullptr, "RebootSystem"},
+ {0x000E0000, &NS_S::ShutdownAsync, "ShutdownAsync"},
+ {0x00100180, &NS_S::RebootSystem, "RebootSystem"},
{0x00110100, nullptr, "TerminateTitle"},
{0x001200C0, nullptr, "SetApplicationCpuTimeLimit"},
{0x00150140, nullptr, "LaunchApplication"},
- {0x00160000, nullptr, "RebootSystemClean"},
+ {0x00160000, &NS_S::RebootSystemClean, "RebootSystemClean"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 68ba0b64f..86ba6b45a 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -639,7 +639,8 @@ ResultCode Module::FormatConfig() {
// 0x00110000 - The low u16 indicates whether the system setup is required, such as when the
// system is booted for the first time or after doing a System Format: 0 = setup required,
// non-zero = no setup required
- res = CreateConfigInfoBlk(SystemSetupRequiredBlockID, 0x4, 0xC, zero_buffer);
+ u32 system_setup_flag = 1;
+ res = CreateConfigInfoBlk(SystemSetupRequiredBlockID, 0x4, 0xC, &system_setup_flag);
if (!res.IsSuccess())
return res;
diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp
index f1ae11615..144339e79 100644
--- a/src/core/hle/service/gsp/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp/gsp_gpu.cpp
@@ -663,14 +663,17 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) {
void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x18, 0, 0);
- // TODO(Subv): We're always returning the framebuffer structures for thread_id = 0,
- // because we only support a single running application at a time.
- // This should always return the framebuffer data that is currently displayed on the screen.
+ if (active_thread_id == UINT32_MAX) {
+ LOG_WARNING(Service_GSP, "Called without an active thread.");
- u32 thread_id = 0;
+ // TODO: Find the right error code.
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(-1);
+ return;
+ }
- FrameBufferUpdate* top_screen = GetFrameBufferInfo(thread_id, 0);
- FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(thread_id, 1);
+ FrameBufferUpdate* top_screen = GetFrameBufferInfo(active_thread_id, 0);
+ FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
struct CaptureInfoEntry {
u32_le address_left;
@@ -700,6 +703,39 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_GSP, "called");
}
+void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x19, 0, 0);
+
+ LOG_INFO(Service_GSP, "called");
+
+ // TODO: This should also DMA framebuffers into VRAM and save LCD register state.
+ Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
+ Memory::FlushMode::Flush);
+ auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
+ saved_vram.emplace(std::vector(Memory::VRAM_SIZE));
+ std::memcpy(saved_vram.get().data(), vram, Memory::VRAM_SIZE);
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS);
+}
+
+void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx, 0x1A, 0, 0);
+
+ LOG_INFO(Service_GSP, "called");
+
+ if (saved_vram) {
+ // TODO: This should also restore LCD register state.
+ auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
+ std::memcpy(vram, saved_vram.get().data(), Memory::VRAM_SIZE);
+ Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
+ Memory::FlushMode::Invalidate);
+ }
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS);
+}
+
void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x16, 1, 2);
@@ -808,8 +844,8 @@ GSP_GPU::GSP_GPU(Core::System& system) : ServiceFramework("gsp::Gpu", 2), system
{0x00160042, &GSP_GPU::AcquireRight, "AcquireRight"},
{0x00170000, &GSP_GPU::ReleaseRight, "ReleaseRight"},
{0x00180000, &GSP_GPU::ImportDisplayCaptureInfo, "ImportDisplayCaptureInfo"},
- {0x00190000, nullptr, "SaveVramSysArea"},
- {0x001A0000, nullptr, "RestoreVramSysArea"},
+ {0x00190000, &GSP_GPU::SaveVramSysArea, "SaveVramSysArea"},
+ {0x001A0000, &GSP_GPU::RestoreVramSysArea, "RestoreVramSysArea"},
{0x001B0000, nullptr, "ResetGpuCore"},
{0x001C0040, &GSP_GPU::SetLedForceOff, "SetLedForceOff"},
{0x001D0040, nullptr, "SetTestCommand"},
diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h
index 0a8517c8c..cf976654e 100644
--- a/src/core/hle/service/gsp/gsp_gpu.h
+++ b/src/core/hle/service/gsp/gsp_gpu.h
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
#include
#include "common/bit_field.h"
#include "common/common_types.h"
@@ -238,6 +239,13 @@ public:
*/
FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index);
+ /**
+ * Retreives the ID of the thread with GPU rights.
+ */
+ u32 GetActiveThreadId() {
+ return active_thread_id;
+ }
+
private:
/**
* Signals that the specified interrupt type has occurred to userland code for the specified GSP
@@ -402,6 +410,32 @@ private:
*/
void ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx);
+ /**
+ * GSP_GPU::SaveVramSysArea service function
+ *
+ * Returns information about the current framebuffer state
+ *
+ * Inputs:
+ * 0: Header 0x00190000
+ * Outputs:
+ * 0: Header Code[0x00190040]
+ * 1: Result code
+ */
+ void SaveVramSysArea(Kernel::HLERequestContext& ctx);
+
+ /**
+ * GSP_GPU::RestoreVramSysArea service function
+ *
+ * Returns information about the current framebuffer state
+ *
+ * Inputs:
+ * 0: Header 0x001A0000
+ * Outputs:
+ * 0: Header Code[0x001A0040]
+ * 1: Result code
+ */
+ void RestoreVramSysArea(Kernel::HLERequestContext& ctx);
+
/**
* GSP_GPU::StoreDataCache service function
*
@@ -438,6 +472,9 @@ private:
bool first_initialization = true;
+ /// VRAM copy saved using SaveVramSysArea.
+ boost::optional> saved_vram;
+
/// Maximum number of threads that can be registered at the same time in the GSP module.
static constexpr u32 MaxGSPThreads = 4;
@@ -453,6 +490,7 @@ private:
ar& active_thread_id;
ar& first_initialization;
ar& used_thread_ids;
+ ar& saved_vram;
}
friend class boost::serialization::access;