summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2026-05-30 01:35:57 +0300
committerAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2026-06-04 01:31:21 +0300
commit1832026b5896968bac100e67235ad242a40f2157 (patch)
treecd674130e91206bc1c9d5e031d31b63e742cf0a0
parent0797d76b7aec1e1b8fe45866cdb61147b3f531e5 (diff)
#5856 An option to prevent hibernation
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llagent.cpp9
-rw-r--r--indra/newview/llappviewer.cpp48
-rw-r--r--indra/newview/llappviewer.h15
-rw-r--r--indra/newview/llappviewerlinux.cpp302
-rw-r--r--indra/newview/llappviewerlinux.h10
-rw-r--r--indra/newview/llappviewermacosx-objc.h1
-rw-r--r--indra/newview/llappviewermacosx-objc.mm66
-rw-r--r--indra/newview/llappviewermacosx.cpp7
-rw-r--r--indra/newview/llappviewermacosx.h1
-rw-r--r--indra/newview/llappviewerwin32.cpp50
-rw-r--r--indra/newview/llappviewerwin32.h1
-rw-r--r--indra/newview/llviewerwindow.cpp30
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_setup.xml36
14 files changed, 578 insertions, 9 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 8086afb865..b1e161182e 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -14429,6 +14429,17 @@
<key>Value</key>
<integer>180</integer>
</map>
+ <key>OSHibernationMode</key>
+ <map>
+ <key>Comment</key>
+ <string>Whether to prevent OS from hibernating. 0 - can hibernate; 1 - can't hibernate, can turn screen off; 2 - can't hibernate, can't turn screen off </string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>HeightUnits</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 31814d8f4f..35d60e6595 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -1552,6 +1552,8 @@ void LLAgent::setAFK()
setControlFlags(AGENT_CONTROL_AWAY | AGENT_CONTROL_STOP);
gAwayTimer.start();
}
+
+ LLAppViewer::instance()->setPermitOSHibernation(true);
}
//-----------------------------------------------------------------------------
@@ -1570,6 +1572,13 @@ void LLAgent::clearAFK()
sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_STOP);
clearControlFlags(AGENT_CONTROL_AWAY);
}
+
+ if (isAgentAvatarValid())
+ {
+ // Only set this if agent is inworld, login screen
+ // shouldn't prevent hibernation.
+ LLAppViewer::instance()->setPermitOSHibernation(false);
+ }
}
//-----------------------------------------------------------------------------
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 6b5c0ea6b0..4a6739bb40 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -633,6 +633,12 @@ bool LLAppViewer::sendURLToOtherInstance(const std::string& url)
return false;
}
+//virtual
+void LLAppViewer::setOSHibernationMode(eHibernationMode mode)
+{
+ // See OS specific files
+}
+
//----------------------------------------------------------------------------
// LLAppViewer definition
@@ -4474,9 +4480,12 @@ void LLAppViewer::abortQuit()
mClosingFloaters = false;
}
-void LLAppViewer::sendViewerStatistics()
+void LLAppViewer::sendViewerStatistics(bool include_preferences)
{
- send_viewer_stats(false);
+ if (!gDisconnected)
+ {
+ send_viewer_stats(include_preferences);
+ }
}
void LLAppViewer::migrateCacheDirectory()
@@ -5857,6 +5866,29 @@ void LLAppViewer::outOfMemorySoftQuit()
}
}
+void LLAppViewer::setPermitOSHibernation(bool permit)
+{
+ if (permit)
+ {
+ if (mCurrentHibernationMode != LL_HIBERNATE_MODE_DEFAULT)
+ {
+ // Will call OS specific code to let OS hibernate when idle
+ setOSHibernationMode(LL_HIBERNATE_MODE_DEFAULT);
+ mCurrentHibernationMode = LL_HIBERNATE_MODE_DEFAULT;
+ }
+ }
+ else
+ {
+ static LLCachedControl<S32> os_hibernation_mode(gSavedSettings, "OSHibernationMode", 0);
+ eHibernationMode mode = static_cast<eHibernationMode>(os_hibernation_mode());
+ if (mode != LL_HIBERNATE_MODE_DEFAULT && mCurrentHibernationMode != mode)
+ {
+ setOSHibernationMode(mode);
+ mCurrentHibernationMode = mode;
+ }
+ }
+}
+
void LLAppViewer::idleNameCache()
{
// Neither old nor new name cache can function before agent has a region
@@ -6064,6 +6096,9 @@ void LLAppViewer::disconnectViewer()
// Pass the connection state to LLUrlEntryParcel not to attempt
// parcel info requests while disconnected.
LLUrlEntryParcel::setDisconnected(gDisconnected);
+
+ // Restore default OS hibernation mode
+ setPermitOSHibernation(true);
}
void LLAppViewer::forceErrorLLError()
@@ -6329,6 +6364,15 @@ void LLAppViewer::handleLoginComplete()
// we logged in successfully, so save settings on logout
LL_INFOS() << "Login successful, per account settings will be saved on log out." << LL_ENDL;
mSavePerAccountSettings=true;
+
+ // Don't allow hibernation while we're running
+ setPermitOSHibernation(false);
+ // Track 'hibernation' mode changes
+ mOSHibernationModeChangeConnection = gSavedSettings.getControl("OSHibernationMode")->getSignal()->connect([](LLControlVariable* control, const LLSD& new_val, const LLSD& old_val)
+ {
+ // setPermitOSHibernation will sort itself out based on new mode.
+ LLAppViewer::instance()->setPermitOSHibernation(false);
+ });
}
//virtual
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index cde58b0850..6ecafae036 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -114,7 +114,7 @@ public:
const LLSD& substitutions = LLSD()); // Display an error dialog and forcibly quit.
void earlyExitNoNotify(); // Do not display error dialog then forcibly quit.
void abortQuit(); // Called to abort a quit request.
- void sendViewerStatistics();
+ void sendViewerStatistics(bool include_preferences);
bool quitRequested() { return mQuitRequested; }
bool logoutRequestSent() { return mLogoutRequestSent; }
@@ -269,6 +269,8 @@ public:
// Note: mQuitRequested can be aborted by user.
void outOfMemorySoftQuit();
+ virtual void setPermitOSHibernation(bool permit);
+
#ifdef LL_DISCORD
static void initDiscordSocial();
static void updateDiscordActivity();
@@ -285,6 +287,14 @@ protected:
virtual bool initSLURLHandler();
virtual bool sendURLToOtherInstance(const std::string& url);
+ typedef enum
+ {
+ LL_HIBERNATE_MODE_DEFAULT = 0, // Use the platform's default behavior.
+ LL_HIBERNATE_MODE_PREVENT = 1,
+ LL_HIBERNATE_MODE_PREVENT_SCREEN = 2,
+ } eHibernationMode;
+ virtual void setOSHibernationMode(eHibernationMode mode);
+
virtual bool initParseCommandLine(LLCommandLineParser& clp)
{ return true; } // Allow platforms to specify the command line args.
@@ -391,6 +401,9 @@ private:
LLAppCoreHttp mAppCoreHttp;
bool mIsFirstRun;
+
+ eHibernationMode mCurrentHibernationMode = LL_HIBERNATE_MODE_DEFAULT;
+ boost::signals2::scoped_connection mOSHibernationModeChangeConnection;
};
// Globals with external linkage. From viewer.h
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp
index 89d19d180b..fe0d6f6e7d 100644
--- a/indra/newview/llappviewerlinux.cpp
+++ b/indra/newview/llappviewerlinux.cpp
@@ -58,6 +58,10 @@ namespace
void (*gOldTerminateHandler)() = NULL;
}
+// Initialize static members
+guint32 LLAppViewerLinux::sPowerInhibitCookie = 0;
+bool LLAppViewerLinux::sPowerInhibitActive = false;
+
static void exceptionTerminateHandler()
{
@@ -117,6 +121,11 @@ LLAppViewerLinux::LLAppViewerLinux()
LLAppViewerLinux::~LLAppViewerLinux()
{
+ // Clean up any power management inhibition on exit
+ if (sPowerInhibitActive)
+ {
+ uninhibitPowerManagement();
+ }
}
bool LLAppViewerLinux::init()
@@ -329,6 +338,299 @@ bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url)
}
#endif // LL_DBUS_ENABLED
+
+void LLAppViewerLinux::setOSHibernationMode(eHibernationMode mode)
+{
+ if (mode == LL_HIBERNATE_MODE_DEFAULT)
+ {
+ // Allow OS to sleep/hibernate - remove any inhibition
+ if (sPowerInhibitActive)
+ {
+ uninhibitPowerManagement();
+ LL_INFOS("OS") << "Permitted OS hibernation/sleep" << LL_ENDL;
+ }
+ }
+ else if (mode == LL_HIBERNATE_MODE_PREVENT)
+ {
+ // Prevent system sleep, but allow display to turn off
+ // Release any existing inhibition first to allow mode switching
+ if (sPowerInhibitActive)
+ {
+ uninhibitPowerManagement();
+ }
+
+ if (inhibitPowerManagement(false))
+ {
+ LL_INFOS("OS") << "Prevented OS hibernation/sleep, display sleep allowed" << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("OS") << "Failed to prevent OS hibernation/sleep" << LL_ENDL;
+ }
+ }
+ else if (mode == LL_HIBERNATE_MODE_PREVENT_SCREEN)
+ {
+ // Prevent both system and display sleep
+ // Release any existing inhibition first to allow mode switching
+ if (sPowerInhibitActive)
+ {
+ uninhibitPowerManagement();
+ }
+
+ if (inhibitPowerManagement(true))
+ {
+ LL_INFOS("OS") << "Prevented OS hibernation/sleep and display sleep" << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("OS") << "Failed to prevent OS hibernation/sleep and display sleep" << LL_ENDL;
+ }
+ }
+}
+
+// TODO: This is AI Generated!!!, needs review and testing.
+bool LLAppViewerLinux::inhibitPowerManagement(bool inhibit_display)
+{
+#if LL_DBUS_ENABLED
+ // Try to use D-Bus to inhibit power management via various desktop environment APIs
+ // This works with GNOME, KDE, XFCE, and most modern Linux desktop environments
+
+ if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME))
+ {
+ LL_WARNS("OS") << "Failed to load D-Bus symbols for power management" << LL_ENDL;
+ return false;
+ }
+
+ GError* error = nullptr;
+ DBusGConnection* bus = lldbus_g_bus_get(DBUS_BUS_SESSION, &error);
+
+ if (!bus)
+ {
+ LL_WARNS("OS") << "Failed to connect to D-Bus session bus: "
+ << (error ? error->message : "unknown error") << LL_ENDL;
+ if (error)
+ g_error_free(error);
+ return false;
+ }
+
+ // Try multiple power management services in order of preference
+ // 1. org.freedesktop.PowerManagement (older standard)
+ // 2. org.gnome.SessionManager (GNOME)
+ // 3. org.kde.Solid.PowerManagement (KDE)
+
+ const char* services[] = {
+ "org.freedesktop.PowerManagement",
+ "org.gnome.SessionManager",
+ "org.kde.Solid.PowerManagement"
+ };
+
+ const char* paths[] = {
+ "/org/freedesktop/PowerManagement/Inhibit",
+ "/org/gnome/SessionManager",
+ "/org/kde/Solid/PowerManagement"
+ };
+
+ const char* interfaces[] = {
+ "org.freedesktop.PowerManagement.Inhibit",
+ "org.gnome.SessionManager",
+ "org.kde.Solid.PowerManagement"
+ };
+
+ const char* methods[] = {
+ "Inhibit",
+ "Inhibit",
+ "inhibit"
+ };
+
+ bool success = false;
+
+ for (int i = 0; i < 3 && !success; ++i)
+ {
+ DBusGProxy* proxy = lldbus_g_proxy_new_for_name(
+ bus,
+ services[i],
+ paths[i],
+ interfaces[i]
+ );
+
+ if (!proxy)
+ continue;
+
+ error = nullptr;
+ guint32 cookie = 0;
+
+ if (i == 0) // freedesktop.PowerManagement
+ {
+ // Inhibit(application_name: s, reason: s) -> cookie: u
+ success = lldbus_g_proxy_call(
+ proxy,
+ methods[i],
+ &error,
+ G_TYPE_STRING, "Second Life Viewer",
+ G_TYPE_STRING, inhibit_display ?
+ "Viewer active - preventing system and display sleep" :
+ "Viewer active - preventing system sleep",
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &cookie,
+ G_TYPE_INVALID
+ );
+ }
+ else if (i == 1) // GNOME SessionManager
+ {
+ // Inhibit(app_id: s, toplevel_xid: u, reason: s, flags: u) -> cookie: u
+ // flags: 4 = suspend, 8 = idle (display), 12 = both
+ guint32 flags = inhibit_display ? 12 : 4;
+ success = lldbus_g_proxy_call(
+ proxy,
+ methods[i],
+ &error,
+ G_TYPE_STRING, "SecondLifeViewer",
+ G_TYPE_UINT, 0, // toplevel_xid (0 = none)
+ G_TYPE_STRING, inhibit_display ?
+ "Viewer active - preventing system and display sleep" :
+ "Viewer active - preventing system sleep",
+ G_TYPE_UINT, flags,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &cookie,
+ G_TYPE_INVALID
+ );
+ }
+ else if (i == 2) // KDE Solid
+ {
+ // Different method signature for KDE
+ success = lldbus_g_proxy_call(
+ proxy,
+ methods[i],
+ &error,
+ G_TYPE_INVALID,
+ G_TYPE_INT, &cookie,
+ G_TYPE_INVALID
+ );
+ }
+
+ if (success)
+ {
+ sPowerInhibitCookie = cookie;
+ sPowerInhibitActive = true;
+ LL_INFOS("OS") << "Successfully inhibited power management using "
+ << services[i] << LL_ENDL;
+ }
+ else if (error)
+ {
+ LL_DEBUGS("OS") << "Failed to inhibit via " << services[i]
+ << ": " << error->message << LL_ENDL;
+ g_error_free(error);
+ error = nullptr;
+ }
+
+ g_object_unref(proxy);
+ }
+
+ return success;
+
+#else // !LL_DBUS_ENABLED
+ LL_WARNS("OS") << "Power management control not available - D-Bus support not enabled" << LL_ENDL;
+ return false;
+#endif
+}
+
+void LLAppViewerLinux::uninhibitPowerManagement()
+{
+#if LL_DBUS_ENABLED
+ if (!sPowerInhibitActive || sPowerInhibitCookie == 0)
+ {
+ return;
+ }
+
+ if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME))
+ {
+ LL_WARNS("OS") << "Failed to load D-Bus symbols for power management uninhibit" << LL_ENDL;
+ return;
+ }
+
+ GError* error = nullptr;
+ DBusGConnection* bus = lldbus_g_bus_get(DBUS_BUS_SESSION, &error);
+
+ if (!bus)
+ {
+ if (error)
+ g_error_free(error);
+ return;
+ }
+
+ // Try to uninhibit using all services that might have been used
+ const char* services[] = {
+ "org.freedesktop.PowerManagement",
+ "org.gnome.SessionManager",
+ "org.kde.Solid.PowerManagement"
+ };
+
+ const char* paths[] = {
+ "/org/freedesktop/PowerManagement/Inhibit",
+ "/org/gnome/SessionManager",
+ "/org/kde/Solid/PowerManagement"
+ };
+
+ const char* interfaces[] = {
+ "org.freedesktop.PowerManagement.Inhibit",
+ "org.gnome.SessionManager",
+ "org.kde.Solid.PowerManagement"
+ };
+
+ const char* methods[] = {
+ "UnInhibit",
+ "Uninhibit",
+ "uninhibit"
+ };
+
+ bool success = false;
+
+ for (int i = 0; i < 3; ++i)
+ {
+ DBusGProxy* proxy = lldbus_g_proxy_new_for_name(
+ bus,
+ services[i],
+ paths[i],
+ interfaces[i]
+ );
+
+ if (!proxy)
+ continue;
+
+ error = nullptr;
+
+ if (lldbus_g_proxy_call(
+ proxy,
+ methods[i],
+ &error,
+ G_TYPE_UINT, sPowerInhibitCookie,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID))
+ {
+ success = true;
+ LL_INFOS("OS") << "Successfully uninhibited power management using "
+ << services[i] << LL_ENDL;
+ }
+ else if (error)
+ {
+ LL_DEBUGS("OS") << "Failed to uninhibit via " << services[i]
+ << ": " << error->message << LL_ENDL;
+ g_error_free(error);
+ error = nullptr;
+ }
+
+ g_object_unref(proxy);
+
+ if (success)
+ break;
+ }
+
+ sPowerInhibitCookie = 0;
+ sPowerInhibitActive = false;
+
+#endif // LL_DBUS_ENABLED
+}
+
void LLAppViewerLinux::initCrashReporting(bool reportFreeze)
{
std::string cmd =gDirUtilp->getExecutableDir();
diff --git a/indra/newview/llappviewerlinux.h b/indra/newview/llappviewerlinux.h
index dde223878d..6ab3682515 100644
--- a/indra/newview/llappviewerlinux.h
+++ b/indra/newview/llappviewerlinux.h
@@ -68,6 +68,16 @@ protected:
virtual bool initSLURLHandler();
virtual bool sendURLToOtherInstance(const std::string& url);
+ virtual void setOSHibernationMode(eHibernationMode mode);
+
+private:
+ // Power management state tracking
+ static guint32 sPowerInhibitCookie;
+ static bool sPowerInhibitActive;
+
+ // Helper methods for power management
+ bool inhibitPowerManagement(bool inhibit_display);
+ void uninhibitPowerManagement();
};
#if LL_DBUS_ENABLED
diff --git a/indra/newview/llappviewermacosx-objc.h b/indra/newview/llappviewermacosx-objc.h
index bfbb48dadb..13b8056193 100644
--- a/indra/newview/llappviewermacosx-objc.h
+++ b/indra/newview/llappviewermacosx-objc.h
@@ -32,5 +32,6 @@
void force_ns_sxeption();
void register_url_schemes();
+void set_os_hibernation_mode(int mode);
#endif // LL_LLAPPVIEWERMACOSX_OBJC_H
diff --git a/indra/newview/llappviewermacosx-objc.mm b/indra/newview/llappviewermacosx-objc.mm
index 75d6b56e3e..ab4ae428b9 100644
--- a/indra/newview/llappviewermacosx-objc.mm
+++ b/indra/newview/llappviewermacosx-objc.mm
@@ -29,6 +29,7 @@
#endif
#import <Cocoa/Cocoa.h>
+#import <IOKit/pwr_mgt/IOPMLib.h>
#include <iostream>
#include "llappviewermacosx-objc.h"
@@ -63,3 +64,68 @@ void register_url_schemes()
}
}
}
+
+// Add these as static variables at file scope
+static IOPMAssertionID gPowerAssertionID = kIOPMNullAssertionID;
+
+void set_os_hibernation_mode(int mode)
+{
+ // Release existing assertion
+ if (gPowerAssertionID != kIOPMNullAssertionID)
+ {
+ IOReturn result = IOPMAssertionRelease(gPowerAssertionID);
+ if (result == kIOReturnSuccess)
+ {
+ gPowerAssertionID = kIOPMNullAssertionID;
+ NSLog(@"Permitted OS hibernation/sleep");
+ }
+ else
+ {
+ NSLog(@"Failed to release power assertion: %d", result);
+ }
+ }
+
+ if (mode == 1)
+ {
+ // Prevent OS from sleeping/hibernating
+ CFStringRef assertionName = CFSTR("Second Life Viewer");
+ // kIOPMAssertionTypeNoIdleSleep prevents idle sleep
+ IOReturn result = IOPMAssertionCreateWithName(
+ kIOPMAssertionTypeNoIdleSleep,
+ kIOPMAssertionLevelOn,
+ assertionName,
+ &gPowerAssertionID
+ );
+
+ if (result == kIOReturnSuccess)
+ {
+ NSLog(@"Prevented OS hibernation/sleep, allow display sleep");
+ }
+ else
+ {
+ NSLog(@"Failed to create power assertion: %d", result);
+ }
+ }
+ else if (mode == 2)
+ {
+ // Prevent OS from sleeping/hibernating, prevent screen from going off
+ CFStringRef assertionName = CFSTR("Second Life Viewer");
+ // kIOPMAssertionTypeNoIdleSleep prevents idle sleep
+ // kIOPMAssertionTypeNoDisplaySleep prevents display sleep
+ IOReturn result = IOPMAssertionCreateWithName(
+ kIOPMAssertionTypeNoDisplaySleep,
+ kIOPMAssertionLevelOn,
+ assertionName,
+ &gPowerAssertionID
+ );
+
+ if (result == kIOReturnSuccess)
+ {
+ NSLog(@"Prevented OS hibernation/sleep or screen from turning off");
+ }
+ else
+ {
+ NSLog(@"Failed to create power assertion: %d", result);
+ }
+ }
+}
diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp
index 830e2d4473..1c01d06852 100644
--- a/indra/newview/llappviewermacosx.cpp
+++ b/indra/newview/llappviewermacosx.cpp
@@ -125,6 +125,7 @@ bool pumpMainLoop()
void cleanupViewer()
{
+ set_os_hibernation_mode(0); // restore default OS hibernation behavior
if(!LLApp::isError())
{
if (gViewerAppPtr)
@@ -427,6 +428,12 @@ bool LLAppViewerMacOSX::initSLURLHandler()
return true;
}
+void LLAppViewerMacOSX::setOSHibernationMode(eHibernationMode mode)
+{
+ // pass to objective-c++
+ set_os_hibernation_mode((int)mode);
+}
+
std::string LLAppViewerMacOSX::generateSerialNumber()
{
char serial_md5[MD5HEX_STR_SIZE]; // Flawfinder: ignore
diff --git a/indra/newview/llappviewermacosx.h b/indra/newview/llappviewermacosx.h
index 35fc99dbd9..1153bd4191 100644
--- a/indra/newview/llappviewermacosx.h
+++ b/indra/newview/llappviewermacosx.h
@@ -47,6 +47,7 @@ public:
protected:
virtual bool restoreErrorTrap();
virtual bool initSLURLHandler();
+ virtual void setOSHibernationMode(eHibernationMode mode);
std::string generateSerialNumber();
virtual bool initParseCommandLine(LLCommandLineParser& clp);
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 5184b0f025..25aeb2a26e 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -1020,6 +1020,56 @@ bool LLAppViewerWin32::sendURLToOtherInstance(const std::string& url)
return false;
}
+void LLAppViewerWin32::setOSHibernationMode(eHibernationMode mode)
+{
+ // ES_CONTINUOUS tells Windows to reset the idle timer
+ // and restore normal operation
+ // ES_SYSTEM_REQUIRED prevents system sleep/hibernation
+ // ES_DISPLAY_REQUIRED prevents display sleep
+
+ if (mode == LL_HIBERNATE_MODE_DEFAULT)
+ {
+ // Allow OS to hibernate - clear the previous execution state flags
+ // ES_CONTINUOUS without other flags allows the system to idle normally
+ SetThreadExecutionState(ES_CONTINUOUS);
+ LL_INFOS("OS") << "Permitted OS hibernation/sleep" << LL_ENDL;
+ }
+ else if (mode == LL_HIBERNATE_MODE_PREVENT)
+ {
+ // Prevent OS from hibernating while viewer is running
+ // ES_CONTINUOUS | ES_SYSTEM_REQUIRED keeps the system awake
+ EXECUTION_STATE result = SetThreadExecutionState(
+ ES_CONTINUOUS | ES_SYSTEM_REQUIRED
+ );
+ if (result == NULL)
+ {
+ LL_WARNS("OS") << "Failed to prevent OS hibernation, error: " << GetLastError() << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("OS") << "Prevented OS hibernation, but allowed display sleep" << LL_ENDL;
+ }
+ }
+ else if (mode == LL_HIBERNATE_MODE_PREVENT_SCREEN)
+ {
+ // Prevent OS from hibernating or turning screen off while viewer is running
+ // ES_CONTINUOUS | ES_SYSTEM_REQUIRED keeps the system awake
+ // ES_DISPLAY_REQUIRED keeps the display on
+ EXECUTION_STATE result = SetThreadExecutionState(
+ ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED
+ );
+
+ if (result == NULL)
+ {
+ LL_WARNS("OS") << "Failed to prevent OS hibernation and display sleep, error: " << GetLastError() << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("OS") << "Prevented OS hibernation/sleep" << LL_ENDL;
+ }
+ }
+}
+
bool LLAppViewerWin32::sendShutdownToOtherInstances(const std::wstring& install_dir)
{
// Velopack installs viewer like this:
diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h
index 971907694a..7c3c7a475a 100644
--- a/indra/newview/llappviewerwin32.h
+++ b/indra/newview/llappviewerwin32.h
@@ -62,6 +62,7 @@ protected:
bool restoreErrorTrap() override;
bool sendURLToOtherInstance(const std::string& url) override;
+ void setOSHibernationMode(eHibernationMode mode) override;
std::string generateSerialNumber();
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index abd7096f50..4229ffcfb5 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1487,12 +1487,30 @@ void LLViewerWindow::handleCloseRequestCanceled()
void LLViewerWindow::handleSuspendRequest()
{
- LLAppViewer::instance()->sendViewerStatistics();
- // Todo: this should send a disconnect request as viewer
- // can't keep heartbeat up while suspended and will get
- // disconnected within a minute.
- // Add a disconnect here once 'prevent OS from sleeping'
- // feature is ready.
+ static LLCachedControl<S32> os_hibernation_mode(gSavedSettings, "OSHibernationMode", 0);
+ if (os_hibernation_mode == 0)
+ {
+ LL_INFOS() << "Got a 'suspend' event from OS" << LL_ENDL;
+ // Viewer doesn't handle hibernation.
+ // Just send statistics.
+ LLAppViewer::instance()->sendViewerStatistics(false);
+ }
+ else
+ {
+ LL_INFOS() << "Got a 'suspend' event from OS, disconnecting" << LL_ENDL;
+ // Viewer is set to prevent hibernation if agent isn't away.
+ // If we got here, likely Agent 'went' away then viewer got
+ // a hibernation message.
+ // We have a limited timeframe. Sends stats then disconnect.
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ LLAppViewer::instance()->sendViewerStatistics(true);
+ LLAppViewer::instance()->metricsSend(!gDisconnected);
+ // Make sure to show a message.
+ LLAppViewer::instance()->forceDisconnect(LLTrans::getString("YouHaveBeenDisconnected"));
+ }
+ }
}
bool LLViewerWindow::handleCloseRequest(LLWindow *window, bool from_user)
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
index 2036ed75ca..d5f2f69df7 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
@@ -246,5 +246,41 @@
<button.commit_callback
function="Pref.Proxy" />
</button>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="30"
+ name="os_hibernation_mode"
+ mouse_opaque="false"
+ top_pad="8"
+ width="300">
+ Prevent OS from hibernating if not Away:
+ </text>
+ <combo_box
+ control_name="OSHibernationMode"
+ follows="left|top"
+ height="23"
+ layout="topleft"
+ left_delta="50"
+ top_pad="10"
+ name="os_hibernation_combobox"
+ width="300"
+ tool_tip="Only prevents OS from sleeping if user is not away">
+ <combo_box.item
+ label="Can hibernate"
+ name="can_hibernate"
+ value="0" />
+ <combo_box.item
+ label="Can't hibernate, can turn screen off"
+ name="cant_hibernate"
+ value="1" />
+ <combo_box.item
+ label="Can't hibernate, can't turn screen off"
+ name="cant_hibernate_screen"
+ value="2" />
+ </combo_box>
</panel>