summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvisarhoxha <vhoxha08@gmail.com>2026-09-24 00:30:54 +0200
committervisarhoxha <vhoxha08@gmail.com>2026-10-19 12:22:32 +0200
commitf0ef80016182ef0a79fd8b100b35ed906f954044 (patch)
tree7f2f1cca208245bc270bd4abfb72e84ca0ca1b3e
parent301182532074dae57a4b23ff508308485c122ea8 (diff)
Android: Avoid constant hotkey polling to save battery
Only poll for hotkeys while EmulationActivity is resumed and at least one supported hotkey is mapped. Otherwise the scheduler thread waits on an event instead of polling.
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt8
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt5
-rw-r--r--Source/Android/jni/HotkeyScheduler.cpp84
-rw-r--r--Source/Android/jni/HotkeyScheduler.h11
4 files changed, 90 insertions, 18 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt
index 23ffe96cd6..0678c97724 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt
@@ -42,6 +42,7 @@ import org.dolphinemu.dolphinemu.features.infinitybase.ui.FigureSlot
import org.dolphinemu.dolphinemu.features.infinitybase.ui.FigureSlotAdapter
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface
import org.dolphinemu.dolphinemu.features.input.model.DolphinSensorEventListener
+import org.dolphinemu.dolphinemu.features.input.model.Hotkeys
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting
import org.dolphinemu.dolphinemu.utils.ContentHandler
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting
@@ -319,6 +320,13 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
}
DolphinSensorEventListener.setDeviceRotation(windowManager.defaultDisplay.rotation)
+
+ Hotkeys.setEnabled(true)
+ }
+
+ override fun onPause() {
+ Hotkeys.setEnabled(false)
+ super.onPause()
}
override fun onStop() {
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt
index 6a99d1365c..19c08b9a05 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt
@@ -27,7 +27,6 @@ import org.dolphinemu.dolphinemu.NativeLibrary
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.databinding.ActivitySettingsBinding
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface
-import org.dolphinemu.dolphinemu.features.input.model.Hotkeys
import org.dolphinemu.dolphinemu.features.settings.model.Settings
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsFragment.Companion.newInstance
import org.dolphinemu.dolphinemu.ui.main.MainPresenter
@@ -181,8 +180,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
override fun onStart() {
super.onStart()
presenter!!.onStart()
-
- Hotkeys.setEnabled(false)
}
override fun onResume() {
@@ -202,8 +199,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
override fun onStop() {
super.onStop()
presenter!!.onStop(isFinishing)
-
- Hotkeys.setEnabled(true)
}
override fun onDestroy() {
diff --git a/Source/Android/jni/HotkeyScheduler.cpp b/Source/Android/jni/HotkeyScheduler.cpp
index bb360d1e2a..85d981f755 100644
--- a/Source/Android/jni/HotkeyScheduler.cpp
+++ b/Source/Android/jni/HotkeyScheduler.cpp
@@ -3,6 +3,7 @@
#include "jni/HotkeyScheduler.h"
+#include <chrono>
#include <cmath>
#include <jni.h>
#include <thread>
@@ -13,6 +14,7 @@
#include "AudioCommon/AudioCommon.h"
#include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
+#include "Common/Event.h"
#include "Common/Flag.h"
#include "Common/Thread.h"
#include "Core/Config/GraphicsSettings.h"
@@ -22,6 +24,8 @@
#include "Core/State.h"
#include "Core/System.h"
#include "InputCommon/ControlReference/ControlReference.h"
+#include "InputCommon/ControllerEmu/Control/Control.h"
+#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoConfig.h"
@@ -34,6 +38,8 @@ constexpr int POLL_INTERVAL_MS = 5;
std::thread s_thread;
Common::Flag s_stop_requested;
+Common::Flag s_any_hotkey_set;
+Common::Event s_wakeup;
int s_state_slot = 1;
@@ -42,6 +48,43 @@ bool IsHotkey(int id, bool held = false)
return HotkeyManagerEmu::IsPressed(id, held);
}
+bool IsAnyHotkeySet()
+{
+ InputConfig* const config = HotkeyManagerEmu::GetConfig();
+ if (config == nullptr || config->ControllersNeedToBeCreated())
+ return false;
+
+ auto* const hotkeys = static_cast<HotkeyManager*>(config->GetController(0));
+
+ for (const int hotkey : GetSupportedHotkeys())
+ {
+ const int group = hotkeys->FindGroupByID(hotkey);
+ const int index = hotkeys->GetIndexForGroup(group, hotkey);
+ const auto* const control_group =
+ HotkeyManagerEmu::GetHotkeyGroup(static_cast<HotkeyGroup>(group));
+
+ if (!control_group->controls[index]->control_ref->GetExpression().empty())
+ return true;
+ }
+
+ return false;
+}
+
+bool ShouldPoll()
+{
+ if (!s_any_hotkey_set.IsSet() || !HotkeyManagerEmu::IsEnabled())
+ return false;
+
+ return Core::GetState(Core::System::GetInstance()) != Core::State::Stopping;
+}
+
+// Buttons already held when polling resumes must not count as new presses
+void ConsumePendingPresses()
+{
+ for (const int hotkey : GetSupportedHotkeys())
+ IsHotkey(hotkey);
+}
+
void ShowVolumeOSD()
{
OSD::AddMessage(
@@ -196,19 +239,24 @@ void Run()
{
Common::SetCurrentThreadName("HotkeyScheduler");
+ bool was_polling = false;
+
while (!s_stop_requested.IsSet())
{
- Common::SleepCurrentThread(POLL_INTERVAL_MS);
+ if (!ShouldPoll())
+ {
+ was_polling = false;
+ s_wakeup.Wait();
+ continue;
+ }
- ControllerInterface::SetCurrentInputChannel(ciface::InputChannel::Host);
- g_controller_interface.UpdateInput();
+ s_wakeup.WaitFor(std::chrono::milliseconds(POLL_INTERVAL_MS));
- if (!HotkeyManagerEmu::IsEnabled())
- continue;
+ if (s_stop_requested.IsSet())
+ break;
- Core::System& system = Core::System::GetInstance();
- if (Core::GetState(system) == Core::State::Stopping)
- continue;
+ ControllerInterface::SetCurrentInputChannel(ciface::InputChannel::Host);
+ g_controller_interface.UpdateInput();
ControlReference::SetInputGate(true);
@@ -217,6 +265,15 @@ void Run()
HotkeyManagerEmu::GetStatus(false);
HotkeyManagerEmu::GetStatus(true);
+ if (!was_polling)
+ {
+ ConsumePendingPresses();
+ was_polling = true;
+ continue;
+ }
+
+ Core::System& system = Core::System::GetInstance();
+
HandleGeneralHotkeys();
HandleEmulationSpeedHotkeys();
HandleVolumeHotkeys(system);
@@ -231,9 +288,10 @@ void Start()
if (s_thread.joinable())
return;
- HotkeyManagerEmu::Enable(true);
s_state_slot = 1;
+ s_any_hotkey_set.Set(IsAnyHotkeySet());
s_stop_requested.Clear();
+ s_wakeup.Reset();
s_thread = std::thread(Run);
}
@@ -243,11 +301,18 @@ void Stop()
return;
s_stop_requested.Set();
+ s_wakeup.Set();
s_thread.join();
Core::SetIsThrottlerTempDisabled(false);
}
+void RefreshActiveHotkeys()
+{
+ s_any_hotkey_set.Set(IsAnyHotkeySet());
+ s_wakeup.Set();
+}
+
const std::vector<int>& GetSupportedHotkeys()
{
static const std::vector<int> supported = [] {
@@ -294,5 +359,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_Hotke
JNIEnv*, jclass, jboolean enabled)
{
HotkeyManagerEmu::Enable(enabled == JNI_TRUE);
+ HotkeyScheduler::RefreshActiveHotkeys();
}
}
diff --git a/Source/Android/jni/HotkeyScheduler.h b/Source/Android/jni/HotkeyScheduler.h
index c904139d6b..d98d637991 100644
--- a/Source/Android/jni/HotkeyScheduler.h
+++ b/Source/Android/jni/HotkeyScheduler.h
@@ -5,10 +5,13 @@
#include <vector>
-namespace HotkeyScheduler {
- void Start();
+namespace HotkeyScheduler
+{
+void Start();
- void Stop();
+void Stop();
- const std::vector<int> &GetSupportedHotkeys();
+void RefreshActiveHotkeys();
+
+const std::vector<int>& GetSupportedHotkeys();
} // namespace HotkeyScheduler