|
37 | 37 | using ArchiSteamFarm.Localization; |
38 | 38 | using ArchiSteamFarm.Storage; |
39 | 39 | using ArchiSteamFarm.Web; |
| 40 | +using Microsoft.Win32.SafeHandles; |
| 41 | +using Tmds.DBus.Protocol; |
40 | 42 |
|
41 | 43 | namespace ArchiSteamFarm.Core; |
42 | 44 |
|
@@ -69,13 +71,20 @@ internal static string Version { |
69 | 71 | } |
70 | 72 | } |
71 | 73 |
|
| 74 | + private static SafeHandle? InhibitLock; |
72 | 75 | private static Mutex? SingleInstance; |
73 | 76 |
|
74 | | - internal static void CoreInit(bool minimized, bool systemRequired) { |
| 77 | + internal static async Task CoreInit(bool minimized, bool systemRequired) { |
75 | 78 | if (minimized) { |
76 | 79 | MinimizeConsoleWindow(); |
77 | 80 | } |
78 | 81 |
|
| 82 | + if (OperatingSystem.IsLinux()) { |
| 83 | + if (systemRequired) { |
| 84 | + await LinuxKeepSystemActive().ConfigureAwait(false); |
| 85 | + } |
| 86 | + } |
| 87 | + |
79 | 88 | if (OperatingSystem.IsWindows()) { |
80 | 89 | if (systemRequired) { |
81 | 90 | WindowsKeepSystemActive(); |
@@ -181,6 +190,12 @@ internal static void UnregisterProcess() { |
181 | 190 | // Instead, we'll dispose the mutex which should automatically release it by the CLR |
182 | 191 | SingleInstance.Dispose(); |
183 | 192 | SingleInstance = null; |
| 193 | + |
| 194 | + // Release the inhibit lock as well, if needed |
| 195 | + if (InhibitLock != null) { |
| 196 | + InhibitLock.Dispose(); |
| 197 | + InhibitLock = null; |
| 198 | + } |
184 | 199 | } |
185 | 200 |
|
186 | 201 | internal static bool VerifyEnvironment() { |
@@ -261,6 +276,79 @@ internal static void WindowsStopFlashingConsoleWindow() { |
261 | 276 | NativeMethods.FlashWindowEx(ref flashInfo); |
262 | 277 | } |
263 | 278 |
|
| 279 | + [SupportedOSPlatform("Linux")] |
| 280 | + private static async Task LinuxKeepSystemActive() { |
| 281 | + if (!OperatingSystem.IsLinux()) { |
| 282 | + throw new PlatformNotSupportedException(); |
| 283 | + } |
| 284 | + |
| 285 | + // Docs: https://systemd.io/INHIBITOR_LOCKS |
| 286 | + string? systemAddress = Address.System; |
| 287 | + |
| 288 | + if (string.IsNullOrEmpty(systemAddress)) { |
| 289 | + ASF.ArchiLogger.LogGenericError(Strings.FormatWarningFailedWithError(nameof(systemAddress))); |
| 290 | + |
| 291 | + return; |
| 292 | + } |
| 293 | + |
| 294 | + using Connection connection = new(systemAddress); |
| 295 | + |
| 296 | + try { |
| 297 | + await connection.ConnectAsync().ConfigureAwait(false); |
| 298 | + } catch (ConnectException e) { |
| 299 | + // Possible if no DBus is available at all |
| 300 | + ASF.ArchiLogger.LogGenericDebuggingException(e); |
| 301 | + ASF.ArchiLogger.LogGenericError(Strings.FormatWarningFailedWithError(nameof(connection))); |
| 302 | + |
| 303 | + return; |
| 304 | + } |
| 305 | + |
| 306 | + MessageWriter writer = connection.GetMessageWriter(); |
| 307 | + |
| 308 | + writer.WriteMethodCallHeader( |
| 309 | + "org.freedesktop.login1", |
| 310 | + "/org/freedesktop/login1", |
| 311 | + "org.freedesktop.login1.Manager", |
| 312 | + "Inhibit", |
| 313 | + "ssss" |
| 314 | + ); |
| 315 | + |
| 316 | + // Colon-separated list of lock types |
| 317 | + writer.WriteString("idle"); |
| 318 | + |
| 319 | + // Human-readable, descriptive string of who is taking the lock |
| 320 | + writer.WriteString(SharedInfo.PublicIdentifier); |
| 321 | + |
| 322 | + // Human-readable, descriptive string of why the lock is taken |
| 323 | + writer.WriteString("--system-required"); |
| 324 | + |
| 325 | + // Mode |
| 326 | + writer.WriteString("block"); |
| 327 | + |
| 328 | + MessageBuffer message = writer.CreateMessage(); |
| 329 | + |
| 330 | + try { |
| 331 | + // Inhibit() returns a single value, a file descriptor that encapsulates the lock |
| 332 | + InhibitLock = await connection.CallMethodAsync( |
| 333 | + message, static (response, _) => { |
| 334 | + Reader reader = response.GetBodyReader(); |
| 335 | + |
| 336 | + return reader.ReadHandle<SafeFileHandle>(); |
| 337 | + } |
| 338 | + ).ConfigureAwait(false); |
| 339 | + } catch (DBusException e) { |
| 340 | + // Possible if login manager does not support inhibit, although that should be super rare |
| 341 | + ASF.ArchiLogger.LogGenericDebuggingException(e); |
| 342 | + ASF.ArchiLogger.LogGenericError(Strings.FormatWarningFailedWithError(nameof(connection))); |
| 343 | + |
| 344 | + return; |
| 345 | + } |
| 346 | + |
| 347 | + if (InhibitLock == null) { |
| 348 | + ASF.ArchiLogger.LogGenericError(Strings.FormatWarningFailedWithError(nameof(InhibitLock))); |
| 349 | + } |
| 350 | + } |
| 351 | + |
264 | 352 | private static void MinimizeConsoleWindow() { |
265 | 353 | (_, int top) = Console.GetCursorPosition(); |
266 | 354 |
|
|
0 commit comments